Common base class for tap-changer/phase shifter transformers.

within PowerGrids.Electrical.Branches.BaseClasses;
partial model TapChangerPhaseShifterCommon "Common base class for tap-changer/phase shifter transformers"
  extends BaseClasses.PiNetwork;
  extends Icons.Transformer;

  final constant Real NotUsed = Modelica.Constants.inf "Generic default value for not-used parameters";
  encapsulated type MonitoredQuantitySelection = enumeration(currentMagnitude "phase shifter monitors the port_b current magnitude",
                                                             activePower "phase shifter monitors the port_b active power");

  parameter Types.Resistance R = 0 "Series resistance";
  parameter Types.Reactance X = 0 "Series reactance";
  parameter Types.Conductance G = 0 "Shunt conductance";
  parameter Types.Susceptance B = 0 "Shunt susceptance";

equation
  Y = 1/Complex(R, X);
  YA = Complex(0);
  YB = Complex(G, B);
end TapChangerPhaseShifterCommon;


Default value for counters.

within PowerGrids.Electrical.Branches.BaseClasses;
partial model TapChangerPhaseShifterLogicCommon
  final constant Types.Time timeInfinity = Modelica.Constants.inf "Default value for counters";

  encapsulated type State = enumeration (moveDownN "1: tap-changer/phase-shifter has decreased the next tap",
                                         moveDown1 "2: tap-changer/phase-shifter has decreased the first tap",
                                         waitingToMoveDown "3: tap-changer/phase-shifter is waiting to decrease the first tap",
                                         standard "4:tap-changer/phase-shifter is in standard state with UThresholdDown <= UMonitored <= UThresholdUp",
                                         waitingToMoveUp "5: tap-changer/phase-shifter is waiting to increase the first tap",
                                         moveUp1 "6: tap-changer/phase-shifter has increased the first tap",
                                         moveUpN "7: tap-changer/phase-shifter has increased the next tap",
                                         locked "8: tap-changer/phase-shifter locked");
  State state(start = stateStart, fixed = true);

  encapsulated type ActionType = enumeration (direct "Whether increasing the tap will increase the monitored value",
                                              reverse "Whether increasing the tap will decrease the monitored value");

  parameter Types.Time t1st(min = 0) "Time lag before changing the first tap";
  parameter Types.Time tNext(min = 0) "Time lag before changing subsequent taps";
  parameter Integer Ntap "Number of tap positions";
  parameter Integer tapMax = Ntap "Max tap position";
  parameter Integer tapMin = 1    "Min tap position";
  parameter ActionType actionSel = ActionType.direct "Tap direct or reverse action selector";
  parameter Boolean lockedStart = false "Whether the tap-changer/phase-shifter is initially locked"
  parameter Boolean runningStart = true "Whether the tap-changer/phase-shifter is initially running"
  parameter Integer tapStart "Initial tap"
  parameter State stateStart = State.standard "Initial state"
  final parameter Boolean directAction = (actionSel == ActionType.direct) "= true, when direct action is selected";
  final parameter Boolean reverseAction = (actionSel == ActionType.reverse) "= true, when reverse action is selected";

  Boolean locked (start = lockedStart) "whether the tap-changer/phase-shifter is locked";
  Boolean running (start = runningStart) "whether the tap-changer/phase-shifter is running";
  Integer tap(start = tapStart, fixed = true) "Current tap";

protected
  Boolean lookingToIncreaseTap "true if the tap-changer/phase-shifter wants to increase tap";
  Boolean lookingToDecreaseTap "true if the tap-changer/phase-shifter wants to decrease tap";
  Types.Time tTapMoved(start = timeInfinity, fixed = true) "Time when the tap has been moved";

initial equation
  assert (tapMax <= Ntap,  "Max tap value supposed to be equal or below Ntap");
  assert (tapMin >= 1,     "Min tap value supposed to be equal or higher than 1");
  assert (tapMax > tapMin, "Max tap value supposed to be higher than Min tap value");

equation
  assert (tap <= tapMax, "Tap value supposed to be below maximum tap");
  assert (tap >= tapMin, "Tap value supposed to be above minimum tap");
end TapChangerPhaseShifterLogicCommon;


time lag before transition to standard state, in order to avoid chattering.

within PowerGrids.Electrical.Branches.BaseClasses;
partial model TapChangerPhaseShifterLogicInterval
  extends TapChangerPhaseShifterLogicCommon;
  parameter Types.Time tToStandardState = 0.1 "time lag before transition to standard state, in order to avoid chattering";

protected
    Boolean valueUnderMin(start = false) "true if the monitored signal is under the minimum limit";
    Boolean valueAboveMax(start = false) "true if the monitored signal is above the maximum limit";
    Boolean valueInRange = not(pre(valueUnderMin)) and not(pre(valueAboveMax)) "true if the monitored signal is into the [valueMin ; valueMax] range";
    Types.Time tValueUnderMinWhileRunning(start = timeInfinity) "time when the monitored signal went under the minimum limit and the tap-changer/phase-shifter is running";
    Types.Time tValueAboveMaxWhileRunning(start = timeInfinity) "time when the monitored signal went above the maximum limit and the tap-changer/phase-shifter is running";
    Types.Time tValueInRangeWhileRunning(start = timeInfinity) "time when the monitored signal comes back into the [valueMin ; valueMax] range and the tap-changer/phase-shifter is running";

equation
  lookingToDecreaseTap = (pre(running) and pre(valueAboveMax) and directAction) or (pre(running) and pre(valueUnderMin) and reverseAction);
  lookingToIncreaseTap = (pre(running) and pre(valueUnderMin) and directAction) or (pre(running) and pre(valueAboveMax) and reverseAction);

  when pre(running) and not(pre(locked)) and pre(valueUnderMin) then
    tValueUnderMinWhileRunning = time;
    tValueAboveMaxWhileRunning = pre(tValueAboveMaxWhileRunning);
    tValueInRangeWhileRunning = pre(tValueInRangeWhileRunning);
  elsewhen pre(running) and not(pre(locked)) and not(pre(valueUnderMin)) then
    tValueUnderMinWhileRunning = pre(tValueUnderMinWhileRunning);
    tValueAboveMaxWhileRunning = pre(tValueAboveMaxWhileRunning);
    tValueInRangeWhileRunning = time;
  elsewhen pre(running) and not(pre(locked)) and pre(valueAboveMax) then
    tValueUnderMinWhileRunning = pre(tValueUnderMinWhileRunning);
    tValueAboveMaxWhileRunning = time;
    tValueInRangeWhileRunning = pre(tValueInRangeWhileRunning);
  elsewhen pre(running) and not(pre(locked)) and not(pre(valueAboveMax)) then
    tValueUnderMinWhileRunning = pre(tValueUnderMinWhileRunning);
    tValueAboveMaxWhileRunning = pre(tValueAboveMaxWhileRunning);
    tValueInRangeWhileRunning = time;
  elsewhen pre(locked) then
    tValueUnderMinWhileRunning = timeInfinity;
    tValueAboveMaxWhileRunning = timeInfinity;
    tValueInRangeWhileRunning = timeInfinity;
  end when;

  //Transition to "locked"
  when not(pre(running)) or pre(locked) then
    state = State.locked;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "locked" to "standard"
  elsewhen pre(running) and pre(state) == State.locked and not(pre(locked))
                   and valueInRange
                   and time - tValueInRangeWhileRunning >= tToStandardState then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "locked" to "waitingToMoveDown"
  elsewhen pre(running) and pre(state) == State.locked
                   and not(pre(locked))
                   and lookingToDecreaseTap then
    state = State.waitingToMoveDown;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "locked" to "waitingToMoveUp"
  elsewhen pre(running) and pre(state) == State.locked
                   and not(pre(locked))
                   and lookingToIncreaseTap then
    state = State.waitingToMoveUp;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "standard" to "waitingToMoveUp"
  elsewhen pre(running) and pre(state) == State.standard
                   and lookingToIncreaseTap then
    state = State.waitingToMoveUp;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "standard" to "waitingToMoveDown"
  elsewhen pre(running) and pre(state) == State.standard
                   and lookingToDecreaseTap then
    state = State.waitingToMoveDown;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "waitingToMoveUp" to "standard"
  elsewhen pre(running) and pre(state) == State.waitingToMoveUp
                   and valueInRange
                   and time - tValueInRangeWhileRunning >= tToStandardState then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "waitingToMoveUp" to "waitingToMoveDown"
  elsewhen pre(running) and pre(state) == State.waitingToMoveUp
                   and lookingToDecreaseTap then
    state = State.waitingToMoveDown;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "waitingToMoveUp" to "moveUp1"
  elsewhen pre(running) and pre(state) == State.waitingToMoveUp
                   and time - (if directAction then tValueUnderMinWhileRunning else tValueAboveMaxWhileRunning) >= t1st
                   and pre(tap) < tapMax then
    state = State.moveUp1;
    tap = pre(tap) + 1;
    tTapMoved = time;

  //Transition from "waitingToMoveDown" to "standard"
  elsewhen pre(running) and pre(state) == State.waitingToMoveDown
                   and valueInRange
                   and time - tValueInRangeWhileRunning >= tToStandardState then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "waitingToMoveDown" to "waitingToMoveUp"
  elsewhen pre(running) and pre(state) == State.waitingToMoveDown
                   and lookingToIncreaseTap then
    state = State.waitingToMoveUp;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "waitingToMoveDown" to "moveDown1"
  elsewhen pre(running) and pre(state) == State.waitingToMoveDown
                   and time - (if directAction then tValueAboveMaxWhileRunning else tValueUnderMinWhileRunning) >= t1st
                   and pre(tap) > tapMin then
    state = State.moveDown1;
    tap = pre(tap) - 1;
    tTapMoved = time; //Transition from "moveUp1" to "standard"
  elsewhen pre(running) and pre(state) == State.moveUp1
                   and valueInRange
                   and time - tValueInRangeWhileRunning >= tToStandardState then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "moveUp1" to "waitingToMoveDown"
  elsewhen pre(running) and pre(state) == State.moveUp1
                   and lookingToDecreaseTap then
    state = State.waitingToMoveDown;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "moveUp1" to "moveUpN"
  elsewhen pre(running) and pre(state) == State.moveUp1
                   and time - pre(tTapMoved) >= tNext
                   and pre(tap) < tapMax then
    state = State.moveUpN;
    tap = pre(tap) + 1;
    tTapMoved = time;

  //Transition from "moveUpN" to "standard"
  elsewhen pre(running) and pre(state) == State.moveUpN
                   and valueInRange
                   and time - tValueInRangeWhileRunning >= tToStandardState then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "moveUpN" to "waitingToMoveDown"
  elsewhen pre(running) and pre(state) == State.moveUpN
                   and lookingToDecreaseTap then
    state = State.waitingToMoveDown;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "moveUpN" to "moveUpN"
  elsewhen pre(running) and pre(state) == State.moveUpN
                   and time - pre(tTapMoved) >= tNext
                   and pre(tap) < tapMax then
    state = State.moveUpN;
    tap = pre(tap) + 1;
    tTapMoved = time;

  //Transition from "moveDown1" to "standard"
  elsewhen pre(running) and pre(state) == State.moveDown1
                   and valueInRange
                   and time - tValueInRangeWhileRunning >= tToStandardState then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "moveDown1" to "waitingToMoveUp"
  elsewhen pre(running) and pre(state) == State.moveDown1
                   and lookingToIncreaseTap then
    state = State.waitingToMoveUp;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "moveDown1" to "moveDownN"
  elsewhen pre(running) and pre(state) == State.moveDown1
                   and time - pre(tTapMoved) >= tNext
                   and pre(tap) > tapMin then
    state = State.moveDownN;
    tap = pre(tap) - 1;
    tTapMoved = time;

  //Transition from "moveDownN" to "standard"
  elsewhen pre(running) and pre(state) == State.moveDownN
                   and valueInRange
                   and time - tValueInRangeWhileRunning >= tToStandardState then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "moveDownN" to "waitingToMoveUp"
  elsewhen pre(running) and pre(state) == State.moveDownN
                   and lookingToIncreaseTap then
    state = State.waitingToMoveUp;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "moveDownN" to "moveDownN"
  elsewhen pre(running) and pre(state) == State.moveDownN
                   and time - pre(tTapMoved) >= tNext
                   and pre(tap) > tapMin then
    state = State.moveDownN;
    tap = pre(tap) - 1;
    tTapMoved = time;

  end when;
end TapChangerPhaseShifterLogicInterval;


whether the monitored signal is under the stop limit.

within PowerGrids.Electrical.Branches.BaseClasses;
partial model TapChangerPhaseShifterLogicMax
  extends TapChangerPhaseShifterLogicCommon;

protected
  Boolean valueUnderStop(start = false) "whether the monitored signal is under the stop limit";
  Boolean valueAboveMax(start = false) "wheter the monitored signal is above the maximum limit";
  Types.Time tValueAboveMaxWhileRunning(start = timeInfinity, fixed = true) "time when the monitored signal went above the maximum limit and the tap-changer/phase-shifter is running";

equation
  lookingToDecreaseTap = pre(running) and pre(valueAboveMax) and directAction;
  lookingToIncreaseTap = pre(running) and pre(valueAboveMax) and reverseAction;

  when pre(running) and not(pre(locked)) and pre(valueAboveMax) then
    tValueAboveMaxWhileRunning = time;
  elsewhen pre(running) and not(pre(locked)) and pre(valueUnderStop) then
    tValueAboveMaxWhileRunning = timeInfinity;
  elsewhen pre(locked) then
    tValueAboveMaxWhileRunning = timeInfinity;
  end when;

  //Transition to "locked"
  when (not pre(running)) or pre(locked) then
    state = State.locked;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "locked" to "standard"
  elsewhen pre(running) and pre(state) == State.locked
                   and not(pre(locked))
                   and not(pre(valueAboveMax)) then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "locked" to "waitingToMoveDown"
  elsewhen pre(running) and pre(state) == State.locked
                   and not(pre(locked))
                   and lookingToDecreaseTap then
    state = State.waitingToMoveDown;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "locked" to "waitingToMoveUp"
  elsewhen pre(running) and pre(state) == State.locked
                   and not(pre(locked))
                   and lookingToIncreaseTap then
    state = State.waitingToMoveUp;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "standard" to "waitingToMoveUp"
  elsewhen pre(running) and pre(state) == State.standard
                   and lookingToIncreaseTap then
    state = State.waitingToMoveUp;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "standard" to "waitingToMoveDown"
  elsewhen pre(running) and pre(state) == State.standard
                   and lookingToDecreaseTap then
    state = State.waitingToMoveDown;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "waitingToMoveUp" to "standard"
  elsewhen pre(running) and pre(state) == State.waitingToMoveUp
                   and pre(valueUnderStop) then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "waitingToMoveUp" to "moveUp1"
  elsewhen pre(running) and pre(state) == State.waitingToMoveUp
                   and time - tValueAboveMaxWhileRunning >= t1st
                   and pre(tap) < tapMax then
    state = State.moveUp1;
    tap = pre(tap) + 1;
    tTapMoved = time;

  //Transition from "moveUp1" to "standard"
  elsewhen pre(running) and pre(state) == State.moveUp1
                   and pre(valueUnderStop) then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "moveUp1" to "moveUpN"
  elsewhen pre(running) and pre(state) == State.moveUp1
                   and time - pre(tTapMoved) >= tNext
                   and pre(tap) < tapMax then
    state = State.moveUpN;
    tap = pre(tap) + 1;
    tTapMoved = time;

  //Transition from "moveUpN" to "standard"
  elsewhen pre(running) and pre(state) == State.moveUpN
                   and pre(valueUnderStop) then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "moveUpN" to "moveUpN"
  elsewhen pre(running) and pre(state) == State.moveUpN
                   and time - pre(tTapMoved) >= tNext
                   and pre(tap) < tapMax then
    state = State.moveUpN;
    tap = pre(tap) + 1;
    tTapMoved = time;

  //Transition from "waitingToMoveDown" to "standard"
  elsewhen pre(running) and pre(state) == State.waitingToMoveDown
                   and pre(valueUnderStop) then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "waitingToMoveDown" to "moveDown1"
  elsewhen pre(running) and pre(state) == State.waitingToMoveDown
                   and time - tValueAboveMaxWhileRunning >= t1st
                   and pre(tap) > tapMin then
    state = State.moveDown1;
    tap = pre(tap) - 1;
    tTapMoved = time;

  //Transition from "moveDown1" to "standard"
  elsewhen pre(running) and pre(state) == State.moveDown1
                   and pre(valueUnderStop) then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "moveDown1" to "moveDownN"
  elsewhen pre(running) and pre(state) == State.moveDown1
                   and time - pre(tTapMoved) >= tNext
                   and pre(tap) > tapMin then
    state = State.moveDownN;
    tap = pre(tap) - 1;
    tTapMoved = time;

  //Transition from "moveDownN" to "standard"
  elsewhen pre(running) and pre(state) == State.moveDownN
                   and pre(valueUnderStop) then
    state = State.standard;
    tap = pre(tap);
    tTapMoved = timeInfinity;

  //Transition from "moveDownN" to "moveDownN"
  elsewhen pre(running) and pre(state) == State.moveDownN
                   and time - pre(tTapMoved) >= tNext
                   and pre(tap) > tapMin then
    state = State.moveDownN;
    tap = pre(tap) - 1;
    tTapMoved = time;

  end when;
end TapChangerPhaseShifterLogicMax;


within PowerGrids.Electrical.Buses;

package BaseClasses
  extends Modelica.Icons.BasesPackage;

  model BusBase
    extends PowerGrids.Electrical.BaseClasses.OnePortAC;
    input Types.ComplexVoltage e(re(nominal = port.VBase), im(nominal = port.VBase)) "Voltage of ideal generator, phase-to-ground";
    input Types.ComplexImpedance Z "Internal impedance";
    Types.ComplexVoltage v(re(nominal = port.VBase), im(nominal = port.VBase)) = port.v "Port voltage, phase-to-ground";
    Types.ComplexCurrent i(re(nominal = port.IBase), im(nominal = port.IBase)) = port.i "Port current";
  equation
    v = e + Z*i;
  end BusBase;
end BaseClasses;


Bus model.

within PowerGrids.Electrical.Buses;

model Bus "Bus model"
  extends PowerGrids.Electrical.BaseClasses.OnePortAC(
    final PStart = 0,
    final QStart = 0);
  extends Icons.Bus;

equation
  port.i = Complex(0);
end Bus;


Series resistance to ground during fault.

within PowerGrids.Electrical.Buses;
model BusFault
  extends Icons.Bus;
  extends Electrical.BaseClasses.OnePortAC;
  parameter Types.Resistance R = 0 "Series resistance to ground during fault"
  parameter Types.Reactance X = 0 "Series reactance to ground during fault"
  parameter SI.Time startTime "Start time of the fault"
  parameter SI.Time stopTime "End time of the fault"

  Types.ComplexVoltage v(re(nominal = port.VBase), im(nominal = port.VBase)) = port.v "Port voltage, phase-to-ground";
  Types.ComplexCurrent i(re(nominal = port.IBase), im(nominal = port.IBase)) = port.i "Port current";

  discrete Types.ComplexAdmittance Y(re(start = 0, fixed = true), im(start = 0, fixed = true)) "Shunt admittance";
  Boolean fault(start = false, fixed = true) "The fault is active";

// State machine to compute the fault variable
algorithm
   when time >= startTime then
     fault := true;
   end when;

   when time >= stopTime then
     fault := false;
   end when;

// State machine to compute the shunt admittance
algorithm
   when pre(fault) then
     Y := 1/Complex(R, X);
   end when;

   when not pre(fault) then
     Y := Complex(0);
   end when;
equation
   i = Y*v;
end BusFault;


Equivalent grid model characterized by short circuit capacity.

within PowerGrids.Electrical.Buses;

model EquivalentGrid "Equivalent grid model characterized by short circuit capacity"
  extends PowerGrids.Electrical.Buses.BaseClasses.BusBase(
    e = eSource,
    Z = ZGrid,
    UStart = URef, UPhaseStart = 0);
  extends Icons.Grid;
  import Modelica.ComplexMath;

  parameter Types.Voltage URef = UNom "Reference phase-to-phase voltage at connection terminal";
  parameter Types.Angle UPhaseRef = 0 "Voltage phase angle at connection terminal";
  parameter Types.ApparentPower SSC "Short-circuit capacity";
  parameter Types.PerUnit c = 1.1 "c-factor for calculation of short-circuit capacity";
  parameter Types.PerUnit R_X = 1/10 "Ratio of resistance to reactance of the grid";

  final parameter Types.ComplexPower Ssc = ComplexMath.fromPolar(SSC/c, atan(1/R_X))  "Short circuit active power";
  final parameter Types.ComplexImpedance ZGrid = 1/ComplexMath.conj(Ssc/UNom ^ 2) "Grid impedance";
  final parameter Types.ComplexVoltage eStart = ComplexMath.fromPolar(URef,UPhaseStart) "Tentative value of voltage source";
  final parameter Types.ComplexVoltage eSource(
    re(fixed = false, start = eStart.re),
    im(fixed = false, start = eStart.im)) "Constant value of voltage source";
initial equation
  // Initial voltage at port fixed at reference values
  port. u= ComplexMath.fromPolar(URef, UPhaseRef);
end EquivalentGrid;


within PowerGrids.Electrical.Buses;

model Ground
  extends Icons.Ground;
  PowerGrids.Interfaces.TerminalAC terminal
equation
  terminal.v = Complex(0);
end Ground;


Infinite bus with internal impedance.

within PowerGrids.Electrical.Buses;
model InfiniteBus "Infinite bus with internal impedance"
  extends BaseClasses.BusBase(
      e = CM.fromPolar(ERef, theta),
      Z = Complex(R, X));
  extends Icons.Bus;
  parameter Types.Resistance R = 0 "Series resistance";
  parameter Types.Reactance X = 0 "Series reactance";
  parameter Types.Angle theta = 0 "Voltage phase angle of ideal voltage generator";
  parameter Types.Voltage URef = UNom "Phase-to-phase voltage of ideal voltage generator";
  final parameter Types.Voltage ERef = URef/sqrt(3) "Phase-to-ground voltage of ideal voltage generator";
end InfiniteBus;


Fixed source voltage modulus, phase-to-phase.

within PowerGrids.Electrical.Buses;
model InfiniteBusVariableVoltage
  extends BaseClasses.BusBase(
    e = CM.fromPolar(UAux/sqrt(3), thetaAux),
    Z = Complex(R, X));
  extends Icons.Bus;
  parameter Types.Voltage UFixed = UNom "Fixed source voltage modulus, phase-to-phase"
  parameter Types.Angle thetaFixed = 0 "Fixed angle of source voltage"
  parameter Boolean useUIn = false "Use external input for source voltage magnitude"
  parameter Boolean useThetaIn = false "Use external input for source voltage angle"
  parameter Types.Resistance R = 0 "Series resistance";
  parameter Types.Reactance X = 0 "Series reactance";

  Modelica.Blocks.Interfaces.RealInput UIn(unit = "V", displayUnit = "kV") if useUIn "Source voltage modulus input, phase-to-phase, V"
  Modelica.Blocks.Interfaces.RealInput thetaIn(unit = "rad", displayUnit = "deg") if useThetaIn "Source voltage phase angle input, rad"

protected
  // Auxiliary hidden connectors to manage the conditional connectors
  Modelica.Blocks.Interfaces.RealInput UAux "Source voltage modulus, phase-to-phase";
  Modelica.Blocks.Interfaces.RealInput thetaAux "Source voltage phase angle";

equation
// Conditional connectors
   connect(UIn, UAux);
   if not useUIn then
     UAux = UFixed;
   end if;

   connect(thetaIn, thetaAux);
   if not useThetaIn then
     thetaAux = thetaFixed;
   end if;

   assert(UAux >= 0, "Magnitude must be positive");
end InfiniteBusVariableVoltage;


Bus models

within PowerGrids.Electrical;
package Buses "Bus models"
  extends Modelica.Icons.Package;
end Buses;


Reference bus for an isolated grid.

within PowerGrids.Electrical.Buses;

model ReferenceBus "Reference bus for an isolated grid"
  extends PowerGrids.Electrical.BaseClasses.OnePortAC;
  extends Icons.Bus;
  import PowerGrids.Types.Choices.InitializationOption;
  parameter Boolean setPhaseOnly = false "= true if only the initial voltage phase is to be set";
  parameter InitializationOption initOpt = systemPowerGrids.initOpt "Initialization option";
  final parameter Types.ComplexPerUnit nStart = CM.fromPolar(1, UPhaseStart) "Unit phasor with angle UPhaseStart";
  final parameter Types.ActivePower PSlack(fixed = false) "Constant slack active power leaving system through bus";
  final parameter Types.ReactivePower QSlack(fixed = false) "Constant slack reactive power leaving system through bus";
initial equation
  if not setPhaseOnly then
    port.u = CM.fromPolar(UStart, UPhaseStart) "Set initial bus voltage, phase-to-phase";
  else
    port.u.re*nStart.im = port.u.im*nStart.re "port.u has the same phase as nStart";
    QSlack = 0 "No reactive power leaving system through bus";
  end if;
  assert(abs(PSlack)/SNom < 0.01, "The active power flowing into or out of the reference bus is above 0.01 pu.\n"+
    "You probably need a better balancing of the active power generators in the system.\n" +
    "Please check the documentation of ReferenceBus for further reference", AssertionLevel.warning);
  assert(abs(QSlack)/SNom < 0.01, "The reactive power flowing into or out of the reference bus is above 0.01 pu.\n"+
    "You probably need a better setup of excitation voltages, or you can set setPhaseOnly = true.\n" +
    "Please check the documentation of ReferenceBus for further reference", AssertionLevel.warning);
equation
  port.P = PSlack;
  port.Q = QSlack;
end ReferenceBus;


Power systems controllers

within PowerGrids.Electrical;
package Controls "Power systems controllers"
  extends Modelica.Icons.Package;
end Controls;


Base framework for all controllers such as AVR, PSS, etc..

within PowerGrids.Electrical.Controls.BaseClasses;

block BaseControllerFramework "Base framework for all controllers such as AVR, PSS, etc."
  final parameter Boolean fixInitialControlledVariableDefault =
    if (systemPowerGrids.initOpt == Types.Choices.InitializationOption.globalSteadyStateFixedPowerFlow or
        systemPowerGrids.initOpt == Types.Choices.InitializationOption.localSteadyStateFixedPowerFlow)
    then true else false
    "= true adds extra initial equation to fix controlled variable"
  parameter Boolean fixInitialControlledVariable = fixInitialControlledVariableDefault
    "= true adds extra initial equation to fix controlled variable"

  outer System systemPowerGrids "Reference to system object for system-level defaults";
end BaseControllerFramework;


Base classes for controllers

within PowerGrids.Electrical.Controls;

package BaseClasses "Base classes for controllers"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


Static excitation system - IEEE type AC4A.

within PowerGrids.Electrical.Controls.ExcitationSystems;
block IEEE_AC4A "Static excitation system - IEEE type AC4A"
  extends Controls.BaseClasses.BaseControllerFramework;

  parameter SI.PerUnit ViMax = 100 "Input voltage max limit in p.u.";
  parameter SI.PerUnit ViMin = -100 "Input voltage min limit in p.u.";
  parameter SI.Time Tc = 3 "Lead time constant";
  parameter SI.Time Tb = 10 "Lag delay time constant";
  parameter SI.PerUnit Ka(min = 0) = 200 "Overall equivalent gain";
  parameter SI.Time Ta = 0.05 "Overall time constant";
  parameter SI.PerUnit VrMax = 4 "Output voltage max limit in p.u.";
  parameter SI.PerUnit VrMin = 0 "Output voltage min limit in p.u.";
  parameter SI.PerUnit VcPuStart = 1 "Required start value of VcPu when fixInitialControlledVariable = true"
  parameter SI.PerUnit oversaturationPu = 0.1 "abs(u-usat)/(Vmax-Vmin) in case of saturated initial condition"
  final parameter Real delta = (inputLimiter.uMax-inputLimiter.uMin)*oversaturationPu "Actuator saturation margin";

  Modelica.Blocks.Interfaces.RealInput VsPu "PSS output p.u"
  Modelica.Blocks.Interfaces.RealInput VcPu "Machine terminal voltage p.u."
  Modelica.Blocks.Interfaces.RealInput VrefPu "Voltage reference p.u."
  Modelica.Blocks.Interfaces.RealInput VuelPu "Underexcitation limit p.u."
  Modelica.Blocks.Interfaces.RealOutput efdPu "Exciter output voltage p.u."
  Modelica.Blocks.Math.Add3 addIng(k1 = +1, k2 = -1, k3 = +1)
  Modelica.Blocks.Nonlinear.Limiter inputLimiter(limitsAtInit = true, uMax = ViMax, uMin = ViMin)
  PowerGrids.Controls.LeadLag leadLag(T1 = Tc, T2 = Tb, initType = Modelica.Blocks.Types.Init.SteadyState, k = 1, yStart = 1 / Ka)
  PowerGrids.Controls.FirstOrderWithNonWindupLimiter firstOrderLim(T = Ta, initType = Modelica.Blocks.Types.Init.SteadyState, k = Ka, yMax = VrMax, yMin = VrMin, yStart = 1)
  Modelica.Blocks.Nonlinear.VariableLimiter variableLimiter
  Modelica.Blocks.Sources.Constant largeReal(k = 1e9)
initial equation
  /* The following equation could be written as

     if fixInitialControlledVariable then
       if inputLimiter.u > inputLimiter.uMax or firstOrderLim.u > firstOrderLim.yMax/Ka then
         inputLimiter.u = inputLimiter.uMax + delta;
       elseif inputLimiter.u < inputLimiter.uMin or firstOrderLim.u < firstOrderLim.yMin/Ka then
         inputLimiter.u = inputLimiter.uMin - delta;
       else
         VcPu = VcPuStart;
       end if;
     end if;

     However, we need to use homotopy with a simplified linear model, to
     avoid the need of explicitly initializing all the internal controller
     variables. This is only possible by writing those equations in implicit form
  */
  if fixInitialControlledVariable then
    0 = homotopy(
      actual = if inputLimiter.u > inputLimiter.uMax then inputLimiter.u - (inputLimiter.uMax + delta)
               else if inputLimiter.u < inputLimiter.uMin then inputLimiter.u - (inputLimiter.uMin - delta)
               else VcPu - VcPuStart,
      simplified = VcPu - VcPuStart);
  end if;
equation
  connect(firstOrderLim.y, efdPu)
  connect(inputLimiter.y, leadLag.u)
  connect(addIng.y, inputLimiter.u)
  connect(VrefPu, addIng.u3)
  connect(VsPu, addIng.u1)
  connect(VcPu, addIng.u2)
  connect(variableLimiter.y, firstOrderLim.u)
  connect(leadLag.y, variableLimiter.u)
  connect(largeReal.y, variableLimiter.limit1)
  connect(VuelPu, variableLimiter.limit2)
end IEEE_AC4A;


Static excitation system - IEEE type ST4B .

within PowerGrids.Electrical.Controls.ExcitationSystems;

model IEEE_ST4B "Static excitation system - IEEE type ST4B "
  parameter SI.PerUnit kpr = 10.75 "Voltage regulator proportional gain";
  parameter Real kir(unit = "1/s") = 10.75 "Voltage regulator integral gain";
  parameter SI.Time Ta = 0.02 "Voltage regulator time constant";
  parameter SI.PerUnit vrmax = 1 "Maximum voltage regulator output";
  parameter SI.PerUnit vrmin = -0.87 "Minimum voltage regulator output";
  parameter SI.PerUnit kpm = 1 "Voltage regulator proportional gain output";
  parameter Real kim(unit = "1/s") = 0 "Voltage regulator integral gain output";
  parameter SI.PerUnit vmmax = 99 "Maximum inner loop output";
  parameter SI.PerUnit vmmin = -99 "Minimum inner loop output";
  parameter SI.PerUnit kg = 0 "Feedbak gain of the inner loop field regulator";
  parameter SI.PerUnit kc = 0.113 "Rectifier loading factor proportional to commutating reactance";
  parameter SI.PerUnit vbmax = 11.63 "Maximum excitation voltage";
  Modelica.Blocks.Interfaces.RealInput VsPu "PSS output p.u"
  Modelica.Blocks.Interfaces.RealInput VcPu "Machine terminal voltage p.u."
  Modelica.Blocks.Interfaces.RealInput VePu "Exciter voltage back of commutating reactance p.u."
  Modelica.Blocks.Interfaces.RealInput IfdPu "Machine field current p.u."
  Modelica.Blocks.Interfaces.RealInput VrefPu "Voltage reference p.u."
  Modelica.Blocks.Interfaces.RealInput VuelPu "Underexcitation limit p.u."
  Modelica.Blocks.Interfaces.RealInput VoelPu "Overexcitation limit p.u."
  Modelica.Blocks.Interfaces.RealOutput EfdPu "Exciter output voltage p.u."
  Modelica.Blocks.Math.MultiSum sum1(k = {+1, +1, -1, +1}, nu = 4)
  PowerGrids.Controls.PIWithNonWindupLimiter voltageRegulatorPI(Ki = kir, Kp = kpr, initType = Modelica.Blocks.Types.Init.SteadyState, yMax = vrmax, yMin = vrmin, yStart = 1)
  PowerGrids.Controls.FirstOrder voltageRegulatorTa(T = Ta, initType = Modelica.Blocks.Types.Init.SteadyState, y_start = 1)
  Modelica.Blocks.Math.Feedback feedback
  PowerGrids.Controls.PIWithNonWindupLimiter voltageRegulatorOutput(Ki = kim, Kp = kpm, initType = Modelica.Blocks.Types.Init.SteadyState, yMax = vmmax, yMin = vmmin, yStart = 1)
  Modelica.Blocks.Math.Min lvGate
  Modelica.Blocks.Math.Gain gainKg(k = kg)
  Modelica.Blocks.Math.Product prod1
  Modelica.Blocks.Math.Division div1
  Modelica.Blocks.Math.Gain gainKc(k = kc)
  BaseClasses.ExciterRectifierRegulationCharacteristicEquationsIEEE exciterRectifierRegulationCharacteristicEquationsIEEE
  Modelica.Blocks.Math.Product prod2
  Modelica.Blocks.Nonlinear.Limiter limiterVbmax(limitsAtInit = true, uMax = vbmax, uMin = -Modelica.Constants.inf)
equation
  connect(limiterVbmax.y, prod1.u2)
  connect(prod2.y, limiterVbmax.u)
  connect(VePu, prod2.u2)
  connect(exciterRectifierRegulationCharacteristicEquationsIEEE.y, prod2.u1)
  connect(gainKc.y, exciterRectifierRegulationCharacteristicEquationsIEEE.u)
  connect(div1.y, gainKc.u)
  connect(VePu, div1.u2)
  connect(IfdPu, div1.u1)
  connect(gainKg.y, feedback.u2)
  connect(prod1.y, gainKg.u)
  connect(prod1.y, EfdPu)
  connect(lvGate.y, prod1.u1)
  connect(lvGate.u1, VoelPu)
  connect(voltageRegulatorOutput.y, lvGate.u2)
  connect(feedback.y, voltageRegulatorOutput.u)
  connect(voltageRegulatorTa.y, feedback.u1)
  connect(voltageRegulatorPI.y, voltageRegulatorTa.u)
  connect(sum1.y, voltageRegulatorPI.u)
  connect(VrefPu, sum1.u[4])
  connect(VcPu, sum1.u[3])
  connect(VuelPu, sum1.u[2])
  connect(VsPu, sum1.u[1])
end IEEE_ST4B;


Overall equivalent gain.

within PowerGrids.Electrical.Controls.ExcitationSystems;
block VRProportional
  extends Controls.BaseClasses.BaseControllerFramework;

  parameter SI.PerUnit Ka(min = 0) "Overall equivalent gain";
  parameter SI.PerUnit VrMax "Output voltage max limit in p.u.";
  parameter SI.PerUnit VrMin "Output voltage min limit in p.u.";
  parameter Types.Time LagMax = 0 "Time lag before taking action when u going above uMax";
  parameter Types.Time LagMin = 0 "Time lag before taking action when u going below uMin";
  parameter PowerGrids.Controls.LimiterWithLag.State stateStart = PowerGrids.Controls.LimiterWithLag.State.notSat "Saturation initial state"
  parameter SI.PerUnit VcPuStart = 1 "Required start value of VcPu when fixInitialControlledVariable = true"
  parameter SI.PerUnit oversaturationPu = 0.1 "abs(u-usat)/(Vmax-Vmin) in case of saturated initial condition"
  final parameter Real delta = (limiterWithLag.uMax-limiterWithLag.uMin)*oversaturationPu "Actuator saturation margin";

  outer PowerGrids.Electrical.System systemPowerGrids "Reference to system object";

  Modelica.Blocks.Interfaces.RealInput VcPu "Machine terminal voltage p.u."
  Modelica.Blocks.Interfaces.RealInput VrefPu "Voltage reference p.u."
  Modelica.Blocks.Interfaces.RealOutput efdPu "Exciter output voltage p.u."
  Modelica.Blocks.Math.Feedback Verr
  PowerGrids.Controls.LimiterWithLag limiterWithLag(LagMax = LagMax, LagMin = LagMin, stateStart = stateStart,uMax = VrMax, uMin = VrMin)
  Modelica.Blocks.Math.Gain gain(k = Ka)
initial equation
  /* The following equation could be written as

     if fixInitialControlledVariable then
       if limiterWithLag.u > limiterWithLag.uMax then
         limiterWithLag.u = limiterWithLag.uMax + delta;
       elseif limiterWithLag.u < limiterWithLag.uMin then
         limiterWithLag.u = limiterWithLag.uMin - delta;
       else
         VcPu = VcPuStart;
       end if;
     end if;

     However, we need to use homotopy with a simplified linear model, to
     avoid the need of explicitly initializing all the internal controller
     variables. This is only possible by writing those equations in implicit form
  */
  if fixInitialControlledVariable then
    0 = homotopy(
      actual = if limiterWithLag.u > limiterWithLag.uMax then limiterWithLag.u - (limiterWithLag.uMax + delta)
               else if limiterWithLag.u < limiterWithLag.uMin then limiterWithLag.u - (limiterWithLag.uMin - delta)
               else VcPu - VcPuStart,
      simplified = VcPu - VcPuStart);
  end if;
equation
  connect(VrefPu, Verr.u1)
  connect(VcPu, Verr.u2)
  connect(limiterWithLag.y, efdPu)
  connect(Verr.y, gain.u)
  connect(gain.y, limiterWithLag.u)
end VRProportional;


Exciter rectifier regulation characteristic IEEE.

within PowerGrids.Electrical.Controls.ExcitationSystems.BaseClasses;
model ExciterRectifierRegulationCharacteristicEquationsIEEE "Exciter rectifier regulation characteristic IEEE"
  extends Modelica.Blocks.Interfaces.SISO;

algorithm
  if u < 0.433 then
    y := 1 - 0.577*u;
  elseif u < 0.75 then
    y := sqrt(0.75 - u^2);
  elseif u < 1 then
    y := 1.732*(1-u);
  else
    y := 0;
  end if;
end ExciterRectifierRegulationCharacteristicEquationsIEEE;


Power System Stabilizer IEEE type PSS2B.

within PowerGrids.Electrical.Controls.PowerSystemStabilizers;
model IEEE_PSS2A "Power System Stabilizer IEEE type PSS2B"
  parameter SI.Time Tw1 = 2 "First washout on frequency signal time constant";
  parameter SI.Time Tw2 = 2 "Second washout on frequency signal time constant";
  parameter SI.Time Tw3 = 2 "First washout on power signal time constant";
  parameter SI.Time Tw4 = 2 "Second washout on power signal time constant";
  parameter SI.Time T1 = 0.12  "Lead time constant - leadlag1";
  parameter SI.Time T2 = 0.02 "Lag time constant - leadlag1";
  parameter SI.Time T3 = 0.3 "Lead time constant - leadlag2";
  parameter SI.Time T4 = 0.02 "Lag time constant - leadlag2";
  parameter SI.Time T6 = 0 "Time constant on frequency signal";
  parameter SI.Time T7 = 2 "Time constant on power signal";
  parameter SI.Time T8 = 0.2 "Lead of ramp tracking filter";
  parameter SI.Time T9 = 0.1 "Lag of ramp tracking filter";
  parameter SI.PerUnit Ks1 = 12 "Stabilizer gain";
  parameter SI.PerUnit Ks2 = 0.2 "Gain on power signal";
  parameter SI.PerUnit Ks3 = 1 "Gain on power signal before ramp-tracking filter";
  parameter Integer N = 1 "Order of ramp tracking filter";
  parameter Integer M = 5 "Denominator order of ramp tracking filter";
  parameter SI.PerUnit VstMax = 0.1 "Stabilizer output max limit in p.u.";
  parameter SI.PerUnit VstMin = -0.1 "Stabilizer output min limit in p.u.";
  Modelica.Blocks.Interfaces.RealInput Vsi1Pu "Frequency input p.u."
  Modelica.Blocks.Interfaces.RealInput Vsi2Pu "Power input p.u."
  Modelica.Blocks.Interfaces.RealOutput VstPu "Stabilizer output"
  PowerGrids.Controls.DerivativeLag firstWashoutVsi1( T1 = Tw1,Td = Tw1, initType = Modelica.Blocks.Types.Init.SteadyState, yStart = 0)
  PowerGrids.Controls.DerivativeLag secondWashoutVsi1( T1 = Tw2,Td = Tw2, initType = Modelica.Blocks.Types.Init.SteadyState, yStart = 0)
  PowerGrids.Controls.DerivativeLag firstWashoutVsi2( T1 = Tw3,Td = Tw3, initType = Modelica.Blocks.Types.Init.SteadyState, yStart = 0)
  PowerGrids.Controls.DerivativeLag secondWashoutVsi2( T1 = Tw4,Td = Tw4, initType = Modelica.Blocks.Types.Init.SteadyState, yStart = 0)
  Modelica.Blocks.Math.Add add1
  PowerGrids.Controls.RampTrackingFilter rampTrackingFilter(M = M, N = N, T1 = T8, T2 = T9, initType = Modelica.Blocks.Types.Init.SteadyState, k = 1, yStart = 0)
  PowerGrids.Controls.FirstOrder firstOrderVsi1(T = T6, initType = Modelica.Blocks.Types.Init.SteadyState, k = 1, y_start = 0)
  Modelica.Blocks.Math.Add add2(k2 = -1)
  PowerGrids.Controls.LeadLag leadLag1(T1 = T1, T2 = T2, initType = Modelica.Blocks.Types.Init.SteadyState, k = 1, yStart = 0)
  PowerGrids.Controls.LeadLag leadLag2(T1 = T3, T2 = T4, initType = Modelica.Blocks.Types.Init.SteadyState, k = 1, yStart = 0)
  Modelica.Blocks.Nonlinear.Limiter limiterVst(limitsAtInit = true, uMax = VstMax, uMin = VstMin)
  PowerGrids.Controls.FirstOrder firstOrderVsi2(T = T7, initType = Modelica.Blocks.Types.Init.SteadyState, k = Ks2, y_start = 0)
  Modelica.Blocks.Math.Gain gainKs1(k = Ks1)
  Modelica.Blocks.Math.Gain gainKs3(k = Ks3)
  Modelica.Blocks.Math.Feedback deltaPu
  Modelica.Blocks.Sources.Constant refPu(k = 1)
equation
  connect(refPu.y, deltaPu.u2)
  connect(deltaPu.y, firstWashoutVsi1.u)
  connect(Vsi1Pu, deltaPu.u1)
  connect(firstOrderVsi2.y, add2.u2)
  connect(rampTrackingFilter.y, add2.u1)
  connect(add2.y, gainKs1.u)
  connect(Vsi2Pu, firstWashoutVsi2.u)
  connect(limiterVst.y, VstPu)
  connect(leadLag2.y, limiterVst.u)
  connect(leadLag1.y, leadLag2.u)
  connect(gainKs1.y, leadLag1.u)
  connect(add1.y, rampTrackingFilter.u)
  connect(firstOrderVsi1.y, add1.u1)
  connect(secondWashoutVsi1.y, firstOrderVsi1.u)
  connect(firstWashoutVsi1.y, secondWashoutVsi1.u)
  connect(gainKs3.y, add1.u2)
  connect(firstOrderVsi2.y, gainKs3.u)
  connect(secondWashoutVsi2.y, firstOrderVsi2.u)
  connect(firstWashoutVsi2.y, secondWashoutVsi2.u)
end IEEE_PSS2A;


within PowerGrids.Electrical.Controls.PowerSystemStabilizers;
model IEEE_PSS2B "Power System Stabilizer IEEE type PSS2B"
  parameter SI.PerUnit Vsi1Max = 2 "Input frequency signal max limit in p.u.";
  parameter SI.PerUnit Vsi1Min = -2 "Input frequency signal min limit in p.u.";
  parameter SI.Time Tw1 = 2 "First washout on frequency signal time constant";
  parameter SI.Time Tw2 = 2 "Second washout on frequency signal time constant";
  parameter SI.PerUnit Vsi2Max = 2 "Input power signal max limit in p.u.";
  parameter SI.PerUnit Vsi2Min = -2 "Input power signal min limit in p.u.";
  parameter SI.Time Tw3 = 2 "First washout on power signal time constant";
  parameter SI.Time Tw4 = 2 "Second washout on power signal time constant";
  parameter SI.Time T1 = 0.12  "Lead time constant - leadlag1";
  parameter SI.Time T2 = 0.02 "Lag time constant - leadlag1";
  parameter SI.Time T3 = 0.3 "Lead time constant - leadlag2";
  parameter SI.Time T4 = 0.02 "Lag time constant - leadlag2";
  parameter SI.Time T6 = 0 "Time constant on frequency signal";
  parameter SI.Time T7 = 2 "Time constant on power signal";
  parameter SI.Time T8 = 0.2 "Lead of ramp tracking filter";
  parameter SI.Time T9 = 0.1 "Lag of ramp tracking filter";
  parameter SI.Time T10 = 0 "Lead time constant";
  parameter SI.Time T11 = 0 "Lag time constant";
  parameter SI.PerUnit Ks1 = 12 "Stabilizer gain";
  parameter SI.PerUnit Ks2 = 0.2 "Gain on power signal";
  parameter SI.PerUnit Ks3 = 1 "Gain on power signal before ramp-tracking filter";
  parameter Integer N = 1 "Order of ramp tracking filter";
  parameter Integer M = 5 "Denominator order of ramp tracking filter";
  parameter SI.PerUnit VstMax = 0.1 "Stabilizer output max limit in p.u.";
  parameter SI.PerUnit VstMin = -0.1 "Stabilozer output min limit in p.u.";
  Modelica.Blocks.Interfaces.RealInput Vsi1Pu
  Modelica.Blocks.Interfaces.RealInput Vsi2Pu
  Modelica.Blocks.Interfaces.RealOutput VstPu
  Modelica.Blocks.Nonlinear.Limiter limiterVsi1(limitsAtInit = true, uMax = Vsi1Max, uMin = Vsi1Min)
  Modelica.Blocks.Nonlinear.Limiter limiterVsi2(limitsAtInit = true, uMax = Vsi2Max, uMin = Vsi2Min)
  PowerGrids.Controls.DerivativeLag firstWashoutVsi1( T1 = Tw1,Td = Tw1, initType = Modelica.Blocks.Types.Init.SteadyState, yStart = 0)
  PowerGrids.Controls.DerivativeLag secondWashoutVsi1( T1 = Tw2,Td = Tw2, initType = Modelica.Blocks.Types.Init.SteadyState, yStart = 0)
  PowerGrids.Controls.DerivativeLag firstWashoutVsi2( T1 = Tw3,Td = Tw3, initType = Modelica.Blocks.Types.Init.SteadyState, yStart = 0)
  PowerGrids.Controls.DerivativeLag secondWashoutVsi2( T1 = Tw4,Td = Tw4, initType = Modelica.Blocks.Types.Init.SteadyState, yStart = 0)
  Modelica.Blocks.Math.Add add1
  PowerGrids.Controls.RampTrackingFilter rampTrackingFilter(M = M, N = N, T1 = T8, T2 = T9, initType = Modelica.Blocks.Types.Init.SteadyState, k = 1, yStart = 0)
  PowerGrids.Controls.FirstOrder firstOrderVsi1(T = T6, initType = Modelica.Blocks.Types.Init.SteadyState, k = 1, y_start = 0)
  Modelica.Blocks.Math.Add add2(k2 = -1)
  PowerGrids.Controls.LeadLag leadLag1(T1 = T1, T2 = T2, initType = Modelica.Blocks.Types.Init.SteadyState, k = 1, yStart = 0)
  PowerGrids.Controls.LeadLag leadLag2(T1 = T3, T2 = T4, initType = Modelica.Blocks.Types.Init.SteadyState, k = 1, yStart = 0)
  PowerGrids.Controls.LeadLag leadLag3(T1 = T10, T2 = T11, initType = Modelica.Blocks.Types.Init.SteadyState, k = 1, yStart = 0)
  Modelica.Blocks.Nonlinear.Limiter limiterVst(limitsAtInit = true, uMax = VstMax, uMin = VstMin)
  PowerGrids.Controls.FirstOrder firstOrderVsi2(T = T7, initType = Modelica.Blocks.Types.Init.SteadyState, k = Ks2, y_start = 0)
  Modelica.Blocks.Math.Gain gainKs1(k = Ks1)
  Modelica.Blocks.Math.Gain gainKs3(k = Ks3)
  Modelica.Blocks.Math.Feedback deltaPu
  Modelica.Blocks.Sources.Constant refPu(k = 1)
equation
  connect(deltaPu.u2, refPu.y)
  connect(Vsi1Pu, deltaPu.u1)
  connect(deltaPu.y, limiterVsi1.u)
  connect(limiterVst.y, VstPu)
  connect(leadLag3.y, limiterVst.u)
  connect(leadLag2.y, leadLag3.u)
  connect(leadLag1.y, leadLag2.u)
  connect(gainKs1.y, leadLag1.u)
  connect(add2.y, gainKs1.u)
  connect(rampTrackingFilter.y, add2.u1)
  connect(add1.y, rampTrackingFilter.u)
  connect(firstOrderVsi1.y, add1.u1)
  connect(secondWashoutVsi1.y, firstOrderVsi1.u)
  connect(firstWashoutVsi1.y, secondWashoutVsi1.u)
  connect(limiterVsi1.y, firstWashoutVsi1.u)
  connect(gainKs3.y, add1.u2)
  connect(firstOrderVsi2.y, add2.u2)
  connect(firstOrderVsi2.y, gainKs3.u)
  connect(secondWashoutVsi2.y, firstOrderVsi2.u)
  connect(firstWashoutVsi2.y, secondWashoutVsi2.u)
  connect(limiterVsi2.y, firstWashoutVsi2.u)
  connect(Vsi2Pu, limiterVsi2.u)
end IEEE_PSS2B;


Simple proportional governor.

within PowerGrids.Electrical.Controls.TurbineGovernors;

block GoverProportional "Simple proportional governor"
  extends Controls.BaseClasses.BaseControllerFramework;

  parameter SI.PerUnit KGover "Mechanical power sensitivity to frequency";
  parameter SI.PerUnit PMaxPu = 1 "Maximum mechanical power p.u.";
  parameter SI.PerUnit PMinPu = 0 "Minimum mechanical power p.u.";
  parameter SI.PerUnit PPuStart = 1 "Required start value of PPu when fixInitialControlledVariable = true"
  parameter SI.PerUnit oversaturationPu = 0.1 "abs(u-usat)/(Vmax-Vmin) in case of saturated initial condition"
  final parameter Real delta = (limiter.uMax-limiter.uMin)*oversaturationPu "Actuator saturation margin";

  outer PowerGrids.Electrical.System systemPowerGrids "Reference to system object";

  Modelica.Blocks.Interfaces.RealInput PmRefPu "Reference frequency/load input [pu]"
  Modelica.Blocks.Interfaces.RealInput omegaPu "Frequency [pu]"
  Modelica.Blocks.Interfaces.RealOutput PMechPu "Mechanical turbine power [pu]"

  Modelica.Blocks.Math.Feedback errPu
  Modelica.Blocks.Math.Feedback deltaOmegaPu
  Modelica.Blocks.Sources.Constant omegaRefPu(k = 1)
  Modelica.Blocks.Math.Gain gain(k = KGover)
  Modelica.Blocks.Nonlinear.Limiter limiter(limitsAtInit = true, uMax = PMaxPu, uMin = PMinPu)
  Modelica.Blocks.Interfaces.RealInput PPu "Generator Active Power p.u."
initial equation
  /* The following equation could be written as

     if fixInitialControlledVariable then
       if limiter.u > limiter.uMax then
         limiter.u = limiter.uMax + delta;
       elseif limiter.u < limiter.uMin then
         limiter.u = limiter.uMin - delta;
       else
         PPu = PPuStart;
      end if;

      However, we need to use homotopy with a simplified linear model, to
      avoid the need of explicitly initializing all the internal controller
      variables. This is only possible by writing those equations in implicit form
  */
  if fixInitialControlledVariable then
    0 = homotopy(
      actual = if limiter.u > limiter.uMax then limiter.u - (limiter.uMax + delta)
               else if limiter.u < limiter.uMin then limiter.u - (limiter.uMin - delta)
               else PPu - PPuStart,
      simplified = PPu - PPuStart);
  end if;
equation
  connect(omegaRefPu.y, deltaOmegaPu.u2)
  connect(PmRefPu, errPu.u1)
  connect(omegaPu, deltaOmegaPu.u1)
  connect(deltaOmegaPu.y, gain.u)
  connect(gain.y, errPu.u2)
  connect(errPu.y, limiter.u)
  connect(limiter.y, PMechPu)
end GoverProportional;


Simple general-purpose governor for hydraulic turbines.

within PowerGrids.Electrical.Controls.TurbineGovernors;
model GovHydro4GeneralPurpose "Simple general-purpose governor for hydraulic turbines"
  extends BaseClasses.GovHydro4Base;
equation
  connect(deadBandDb2.y, div1.u1)
end GovHydro4GeneralPurpose;


Pelton.

within PowerGrids.Electrical.Controls.TurbineGovernors;

model GovHydro4Kaplan
  extends BaseClasses.GovHydro4Base;
  Modelica.Blocks.Tables.CombiTable1Ds tableGvPgv(smoothness = Modelica.Blocks.Types.Smoothness.MonotoneContinuousDerivative1, table = [0.1, 0; 0.4, 0.35; 0.5, 0.468; 0.7, 0.796; 0.8, 0.917; 0.9, 0.99; 1, 1])
equation
  connect(deadBandDb2.y, tableGvPgv.u)
  connect(div1.u1, tableGvPgv.y[1])
end GovHydro4Kaplan;


Pelton.

within PowerGrids.Electrical.Controls.TurbineGovernors;

model GovHydro4PeltonFrancis
  extends BaseClasses.GovHydro4Base;
  Modelica.Blocks.Tables.CombiTable1Ds tableGvPgv(smoothness = Modelica.Blocks.Types.Smoothness.MonotoneContinuousDerivative1, table = [0.1, 0; 0.4, 0.42; 0.5, 0.56; 0.7, 0.8; 0.8, 0.9; 0.9, 0.97; 1, 1])
equation
  connect(deadBandDb2.y, tableGvPgv.u)
  connect(div1.u1, tableGvPgv.y[1])
end GovHydro4PeltonFrancis;


Simple Steam Turbine Governor - IEEE type TGOV1.

within PowerGrids.Electrical.Controls.TurbineGovernors;
block IEEE_TGOV1 "Simple Steam Turbine Governor - IEEE type TGOV1"
  extends Controls.BaseClasses.BaseControllerFramework;

  parameter SI.PerUnit VMax = 1 "Maximum gate limit in p.u.";
  parameter SI.PerUnit VMin = 0 "Minimum gate limit in p.u.";
  parameter SI.PerUnit R = 0.05 "Controller Droop";
  parameter SI.PerUnit Dt = 0 "Frictional losses factor";
  parameter SI.Time T1 = 0.5 "Governor time constant";
  parameter SI.Time T2 = 3 "Turbine derivative time constant";
  parameter SI.Time T3 = 10 "Turbine delay time constant";
  parameter SI.PerUnit PMechPuStart = 1 "Required start value of PmechPu when fixInitialControlledVariable = true"
  parameter SI.PerUnit oversaturationPu = 0.1 "abs(u-usat)/(Vmax-Vmin) in case of saturated initial condition"
  final parameter Real delta = (firstOrderLim.yMax - firstOrderLim.yMin)*oversaturationPu "Actuator saturation margin";

  Modelica.Blocks.Interfaces.RealInput RefLPu "Reference frequency/load input [pu]"
  Modelica.Blocks.Interfaces.RealInput omegaPu "Frequency [pu]"
  Modelica.Blocks.Interfaces.RealOutput PMechPu "Mechanical turbine power [pu]"
  Modelica.Blocks.Math.Feedback errPu
  Modelica.Blocks.Math.Feedback deltaOmegaPu
  Modelica.Blocks.Sources.Constant omegaRefPu(k = 1)
  Modelica.Blocks.Math.Gain gainDt(k = Dt)
  Modelica.Blocks.Math.Gain gainDivR(k = 1 / R)
  PowerGrids.Controls.FirstOrderWithNonWindupLimiter firstOrderLim(T = T1, initType = Modelica.Blocks.Types.Init.SteadyState, k = 1, yMax = VMax, yMin = VMin)
  PowerGrids.Controls.LeadLag leadLag(T1 = T2, T2 = T3, initType = Modelica.Blocks.Types.Init.SteadyState, k = 1)
  Modelica.Blocks.Math.Feedback sumPMechPu
initial equation
  /* The following equation could be written as

    if fixInitialControlledVariable then
      if firstOrderLim.u > firstOrderLim.yMax then
        firstOrderLim.u = firstOrderLim.yMax + delta;
      elseif firstOrderLim.u < firstOrderLim.yMin then
        firstOrderLim.u = firstOrderLim.yMin - delta;
      else
        PMechPu = PMechPuStart;
      end if;
    end if;

      However, we need to use homotopy with a simplified linear model, to
      avoid the need of explicitly initializing all the internal controller
      variables. This is only possible by writing those equations in implicit form
  */
  if fixInitialControlledVariable then
    0 = homotopy(
      actual = if firstOrderLim.u > firstOrderLim.yMax then firstOrderLim.u - (firstOrderLim.yMax + delta)
               else if firstOrderLim.u < firstOrderLim.yMin then firstOrderLim.u - (firstOrderLim.yMin - delta)
               else PMechPu - PMechPuStart,
      simplified = PMechPu - PMechPuStart);
  end if;
equation
  connect(omegaRefPu.y, deltaOmegaPu.u2)
  connect(gainDt.y, sumPMechPu.u2)
  connect(deltaOmegaPu.y, gainDt.u)
  connect(deltaOmegaPu.y, errPu.u2)
  connect(sumPMechPu.y, PMechPu)
  connect(leadLag.y, sumPMechPu.u1)
  connect(firstOrderLim.y, leadLag.u)
  connect(gainDivR.y, firstOrderLim.u)
  connect(errPu.y, gainDivR.u)
  connect(RefLPu, errPu.u1)
  connect(omegaPu, deltaOmegaPu.u1)
end IEEE_TGOV1;


Base class for Governors of hydraulic turbines.

within PowerGrids.Electrical.Controls.TurbineGovernors.BaseClasses;

partial model GovHydro4Base "Base class for Governors of hydraulic turbines"
  parameter Modelica.SIunits.PerUnit db1 = 0 "Intentional dead-band width"
  parameter Modelica.SIunits.PerUnit db2 = 0 "Unintentional dead-band width"
  parameter Modelica.SIunits.Time Tp = 0.1 "Pilot servo time constant"
  parameter Modelica.SIunits.Time Tg = 0.5 "Gate servo time constant"
  parameter Modelica.SIunits.Time Tr = 5 "Dashpot time constant"
  parameter Modelica.SIunits.PerUnit uo = 0.2 "Max gate opening velocity"
  parameter Modelica.SIunits.PerUnit uc = -0.2 "Max gate closing velocity"
  parameter Modelica.SIunits.PerUnit gMax = 1 "Maximum gate opening"
  parameter Modelica.SIunits.PerUnit gMin = 0 "Minimum gate opening"
  parameter Modelica.SIunits.PerUnit rPerm = 0.05 "Permanent droop"
  parameter Modelica.SIunits.PerUnit rTemp = 0.3 "Temporary droop"
  parameter Real GvPgvTable[:, :] = [0, 0; 1, 1] "Points to define the curve Pgv = f(Gv)"
  parameter Modelica.Blocks.Types.Smoothness GvPgvSmoothness = Modelica.Blocks.Types.Smoothness.LinearSegments "Smoothness of curve Pgv = f(Gv)"
  parameter Modelica.SIunits.PerUnit at = 1.2 "Turbine gain"
  parameter Modelica.SIunits.PerUnit qnl = 0.08 "No-load flow at nominal head"
  parameter Modelica.SIunits.Time Tw = 1 "Water inertia time constant"
  parameter Modelica.SIunits.PerUnit hDam = 1 "Head available at dam"
  parameter Modelica.SIunits.PerUnit dTurb = 0.5 "Turbine damping factor"
  parameter Modelica.SIunits.PerUnit qStart "Turbine flow Start value"
  Modelica.Blocks.Interfaces.RealInput deltaOmegaPu
  Modelica.Blocks.Interfaces.RealInput PrefPu
  Modelica.Blocks.Interfaces.RealOutput PmPu
  PowerGrids.Controls.DiscontinuousDeadBand deadbandDb1(uMax = db1 / 2)
  Modelica.Blocks.Math.Add3 add1(k2 = -1, k3 = -1)
  PowerGrids.Controls.FirstOrder lagTp(T = Tp, initType = Modelica.Blocks.Types.Init.SteadyState, y_start = 1)
  Modelica.Blocks.Nonlinear.Limiter limU(limitsAtInit = true, uMax = uo, uMin = uc)
  PowerGrids.Controls.IntegratorWithNonWindupLimiter limIntG(initType = Modelica.Blocks.Types.Init.SteadyState, yMax = gMax, yMin = gMin, yStart = 1)
  Modelica.Blocks.Math.Add add2
  Modelica.Blocks.Math.Gain gainRperm(k = rPerm)
  PowerGrids.Controls.DerivativeLag derlagTrRtemp(Td = Tr*rTemp, T1 = Tr, initType = Modelica.Blocks.Types.Init.SteadyState, yStart = 1)
  Modelica.Blocks.Math.Division div1
  Modelica.Blocks.Math.Product square1
  Modelica.Blocks.Sources.Constant constHdam(k = hDam)
  Modelica.Blocks.Sources.Constant constQnl(k = qnl)
  Modelica.Blocks.Math.Product prod1
  Modelica.Blocks.Math.Gain gainAt(k = at)
  Modelica.Blocks.Math.Add add3(k1 = -1)
  Modelica.Blocks.Math.Add add4(k2 = -1)
  Modelica.Blocks.Math.Add add5(k1 = -1)
  Modelica.Blocks.Math.Gain gainDturb(k = dTurb)
  Modelica.Blocks.Math.Product product1
  Modelica.Blocks.Math.Gain gainTg(k = 1 / Tg)
  Modelica.Blocks.Math.Gain gainRtemp(k = rTemp)
  Modelica.Blocks.Continuous.Integrator intTw(initType = Modelica.Blocks.Types.Init.InitialOutput, k = 1 / Tw, y_start = qStart)
  PowerGrids.Controls.DeadBand deadBandDb2(uMax = db2 / 2)
equation
  connect(add5.y, PmPu)
  connect(intTw.y, div1.u2)
  connect(deadBandDb2.y, gainRtemp.u)
  connect(deadBandDb2.y, gainRperm.u)
  connect(deadBandDb2.y, product1.u2)
  connect(limIntG.y, deadBandDb2.u)
  connect(derlagTrRtemp.y, add2.u2)
  connect(constQnl.y, add4.u2)
  connect(gainDturb.y, add5.u1)
  connect(product1.y, gainDturb.u)
  connect(gainRperm.y, add2.u1)
  connect(intTw.y, add4.u1)
  connect(add4.y, prod1.u2)
  connect(add3.y, intTw.u)
  connect(gainRtemp.y, derlagTrRtemp.u)
  connect(gainTg.y, limU.u)
  connect(lagTp.y, gainTg.u)
  connect(deltaOmegaPu, product1.u1)
  connect(constHdam.y, add3.u2)
  connect(deltaOmegaPu, deadbandDb1.u)
  connect(gainAt.y, add5.u2)
  connect(prod1.y, gainAt.u)
  connect(square1.y, prod1.u1)
  connect(square1.y, add3.u1)
  connect(div1.y, square1.u2)
  connect(div1.y, square1.u1)
  connect(PrefPu, add1.u1)
  connect(add2.y, add1.u3)
  connect(limU.y, limIntG.u)
  connect(add1.y, lagTp.u)
  connect(deadbandDb1.y, add1.u2)
end GovHydro4Base;


Load model with impedance specified by PRef and QRef.

within PowerGrids.Electrical.Loads;

model LoadImpedancePQ "Load model with impedance specified by PRef and QRef"
  extends PowerGrids.Electrical.Buses.BaseClasses.BusBase(
    e = Complex(0),
    Z = 1/ComplexMath.conj(Complex(PRef,QRef)/URef^2),
    PStart = PRefConst,
    QStart = QRefConst);
  extends Icons.Load;
  import Modelica.ComplexMath;
  parameter Types.ActivePower PRefConst = 0 "Active power consumption at reference voltage";
  parameter Types.ReactivePower QRefConst = 0 "Reactive power consumption at reference voltage";
  parameter Types.Voltage URef = UNom "Reference value of phase-to-phase voltage";
  Types.ActivePower PRef(nominal = SNom) = PRefConst "Active power consumption at reference voltage, the default binding can be changed when instantiating";
  Types.ActivePower QRef(nominal = SNom) = QRefConst "Reactive power consumption at reference voltage, the default binding can be changed when instantiating";
end LoadImpedancePQ;


Load model with impedance specified by PRefIn and QRefIn inputs.

within PowerGrids.Electrical.Loads;

model LoadImpedancePQInputs "Load model with impedance specified by PRefIn and QRefIn inputs"
  extends LoadImpedancePQ(
    final PRef = PRefIn,
    final QRef = QRefIn,
    final PRefConst = 0,
    final QRefConst = 0);
  Modelica.Blocks.Interfaces.RealInput PRefIn(unit="W", displayUnit="MW") "Active reference power at VPu = 1, W"
  Modelica.Blocks.Interfaces.RealInput QRefIn(unit="var", displayUnit = "MVA") "Reactive reference power at VPu = 1, var"
end LoadImpedancePQInputs;


Load model with voltage dependent P and Q.

within PowerGrids.Electrical.Loads;

model LoadPQVoltageDependence "Load model with voltage dependent P and Q"
  extends PowerGrids.Electrical.BaseClasses.OnePortAC(
    final portVariablesPu=true,
    PStart = PRefConst,
    QStart = QRefConst);
  extends Icons.Load;

  parameter Types.PerUnit alpha = 0 "Exponential of voltage ratio for actual P calculation";
  parameter Types.PerUnit beta = 0 "Exponential of voltage ratio for actual Q calculation";

  parameter Types.ActivePower PRefConst = 0 "Constant active power entering the load at reference voltage";
  parameter Types.ReactivePower QRefConst = 0 "Constant reactive power entering the load at reference voltage";
  parameter Types.Voltage URef = UNom "Reference value of phase-to-phase voltage";

  Types.ActivePower PRef(nominal = SNom) =  PRefConst "Active power at reference voltage, the default binding can be changed when instantiating";
  Types.ActivePower QRef(nominal = SNom) =  QRefConst "Reactive power at reference voltage, the default binding can be changed when instantiating";
    Types.PerUnit U_URef(start = UStart/UNom) "Ratio between voltage and reference voltage";
equation
  U_URef = port.U / URef;
  port.P = PRef*U_URef^alpha;
  port.Q = QRef*U_URef^ beta;

in which case, the constant reference value PRefConst is ignored. Alternatively, one can use the LoadPQVoltageDependenceInputs model that has input connectors for PRef and QRef.

By default alpha = beta = 0 so there is no voltage dependence.

"));
end LoadPQVoltageDependence;


Load model with voltage dependent P and Q specified by inputs.

within PowerGrids.Electrical.Loads;

model LoadPQVoltageDependenceInputs "Load model with voltage dependent P and Q specified by inputs"
  extends LoadPQVoltageDependence(
    final PRef = PRefIn,
    final QRef = QRefIn,
    final PRefConst = 0,
    final QRefConst = 0,
    PStart = 0,
    QStart = 0);
  Modelica.Blocks.Interfaces.RealInput PRefIn(unit="W", displayUnit="MW") "Active reference power at VPu = 1, W"
  Modelica.Blocks.Interfaces.RealInput QRefIn(unit="var", displayUnit = "MVA") "Reactive reference power at VPu = 1, var"
end LoadPQVoltageDependenceInputs;


Load models

within PowerGrids.Electrical;
package Loads "Load models"
  extends Modelica.Icons.Package;
end Loads;


Synchronous machine models

within PowerGrids.Electrical;

package Machines "Synchronous machine models"
  extends Modelica.Icons.Package;
end Machines;


Synchronous machine with four windings - external parameters.

within PowerGrids.Electrical.Machines;

model SynchronousMachine4Windings "Synchronous machine with four windings - external parameters"
  // General-purpose start values are provided, to avoid division by zero errors.
  // These values don't need to be modified for specific machine instancces
  extends SynchronousMachine4WindingsInternalParameters(
    final LdPu(fixed = false, min = 1e-3, start = 0.1),
    final MdPu(fixed = false, min = 1e-3, start = 1),
    final rfPu(fixed = false, min = 1e-5, start = 0.001),
    final rDPu(fixed = false, min = 1e-4, start = 0.01),
    final LfPu(fixed = false, min = 1e-3, start = 0.1),
    final LDPu(fixed = false, min = 1e-3, start = 0.2),
    final mrcPu(fixed = false),
    final LqPu(fixed = false,  min = 1e-3, start = 0.1),
    final MqPu(fixed = false,  min = 1e-3, start = 1),
    final rQ1Pu(fixed = false, min = 1e-5, start = 0.01),
    final rQ2Pu(fixed = false, min = 1e-5, start = 0.001),
    final LQ1Pu(fixed = false, min = 1e-3, start = 0.5),
    final LQ2Pu(fixed = false, min = 1e-3, start = 0.1),
    final thetaStart =
      UPhaseStart +
      atan2(xqPu*IStartPu*cosPhiStart - raPu*IStartPu*sinPhiStart,
            VStartPu + raPu*IStartPu*cosPhiStart + xqPu*IStartPu*sinPhiStart));
  // External parameters
  parameter Types.PerUnit xlPu "Stator leakage in p.u.";
  parameter Types.PerUnit xdPu "Direct axis reactance in p.u.";
  parameter Types.PerUnit xpdPu "Direct axis transient reactance in p.u.";
  parameter Types.PerUnit xppdPu "Direct axis sub-transient reactance p.u.";
  parameter Types.Time Tpd0 "Direct axis, open circuit transient time constant";
  parameter Types.Time Tppd0 "Direct axis, open circuit sub-transient time constant";
  parameter Types.PerUnit xqPu "Quadrature axis reactance in p.u.";
  parameter Types.PerUnit xpqPu "Quadrature axis transient reactance in p.u.";
  parameter Types.PerUnit xppqPu "Quadrature axis sub-transient reactance in p.u.";
  parameter Types.Time Tpq0 "Open circuit quadrature axis transient time constant";
  parameter Types.Time Tppq0 "Open circuit quadrature axis sub-transient time constant";
  parameter Types.Choices.TimeConstantsApproximation timeConstApprox =
    PowerGrids.Types.Choices.TimeConstantsApproximation.classicalDefinition
    "Approximation of time constants for internal parameter computation";

  // Auxiliary parameters, direct and quadrature axis
  // Start values are good enough to achieve convergence for all realistic
  // values of the constants
  final parameter Types.PerUnit T1dPu(fixed = false, min = 0, start = 1000);
  final parameter Types.PerUnit T2dPu(fixed = false, min = 0, start = 100);
  final parameter Types.PerUnit T3dPu(fixed = false, min = 0, start = 10);
  final parameter Types.PerUnit T4dPu(fixed = false, min = 0, start = 1000);
  final parameter Types.PerUnit T5dPu(fixed = false, min = 0, start = 100);
  final parameter Types.PerUnit T6dPu(fixed = false, min = 0, start = 10);

  final parameter Types.PerUnit T1qPu(fixed = false, min = 0, start = 1000);
  final parameter Types.PerUnit T2qPu(fixed = false, min = 0, start = 100);
  final parameter Types.PerUnit T3qPu(fixed = false, min = 0, start = 10);
  final parameter Types.PerUnit T4qPu(fixed = false, min = 0, start = 100);
  final parameter Types.PerUnit T5qPu(fixed = false, min = 0, start = 100);
  final parameter Types.PerUnit T6qPu(fixed = false, min = 0, start = 10);

  final parameter Types.Time Tpd(fixed = false, min = 0, start = 10);
  final parameter Types.Time Tppd(fixed = false, min = 0,start = 0.1);
  final parameter Types.Time Tpq(fixed = false, min = 0, start = 1);
  final parameter Types.Time Tppq(fixed = false, min = 0, start = 0.1);

  // Start values
  final parameter Types.PerUnit cosPhiStart = abs(PStart)/sqrt(PStart^2+QStart^2+1e-9*SNom)
    "Start value of power factor cos(phi)";
  final parameter Types.PerUnit sinPhiStart = sqrt(1 - cosPhiStart^2)
    "Start value of sin(phi)";
  final parameter Types.PerUnit VStartPu = port.VStart/port.VBase "Start value of voltage modulus in p.u.";
  final parameter Types.PerUnit IStartPu = port.IStart/port.IBase "Start value of current modulus in p.u.";
initial equation
  // Initial equations to determine the internal parameters as a function
  // of the external ones
  //
  // These equations, found in the Kundur textbook, would allow to compute the external parameters
  // explicitly, given the internal parameters. The Modelica tool figures out automatically
  // how to solve them backwards, using symbolic and numerical techniques

  // Modelling assumption taken by Kundur
  LqPu = xlPu;
  LdPu = xlPu;

  // Direct axis
  MdPu + LdPu = xdPu;
  T1dPu = (MdPu + LfPu)/rfPu;
  T2dPu = (MdPu + LDPu)/rDPu;
  T3dPu = (LDPu + MdPu * LfPu / (MdPu + LfPu)) / rDPu;
  T4dPu = (LfPu + MdPu * LdPu / (MdPu + LdPu)) / rfPu;
  T5dPu = (LDPu + MdPu * LdPu / (MdPu + LdPu)) / rDPu;
  T6dPu = (LDPu + MdPu * LdPu * LfPu / (MdPu * LdPu + MdPu * LfPu + LdPu * LfPu)) / rDPu;

  // For the accurate and exact case, the equations are first solved by the Modelica tool
  // using the approximate values, which are given by the second argument of the
  // homotopy() operator. Then, the solution to the accurate or exact equations is
  // computed by a continuation method, whereby the approximated expression is smoothly
  // transformed into the actual one
  if timeConstApprox == Types.Choices.TimeConstantsApproximation.classicalDefinition then
    Tpd0  * omegaBase = T1dPu;
    Tppd0 * omegaBase = T3dPu;
    Tpd   * omegaBase = T4dPu;
    Tppd  * omegaBase = T6dPu;
  elseif timeConstApprox == Types.Choices.TimeConstantsApproximation.accurateEstimation then
    Tpd0  * omegaBase = homotopy(T1dPu + T2dPu,               T1dPu);
    Tppd0 * omegaBase = homotopy(T1dPu*T3dPu/(T1dPu + T2dPu), T3dPu);
    Tpd   * omegaBase = homotopy(T4dPu + T5dPu,               T4dPu);
    Tppd  * omegaBase = homotopy(T4dPu*T6dPu/(T4dPu + T5dPu), T6dPu);
  else
    Tpd0  * omegaBase = homotopy( Functions.largerTimeConstant(T1dPu + T2dPu, T1dPu*T3dPu), T1dPu);
    Tppd0 * omegaBase = homotopy(Functions.smallerTimeConstant(T1dPu + T2dPu, T1dPu*T3dPu), T3dPu);
    Tpd   * omegaBase = homotopy( Functions.largerTimeConstant(T4dPu + T5dPu, T4dPu*T6dPu), T4dPu);
    Tppd  * omegaBase = homotopy(Functions.smallerTimeConstant(T4dPu + T5dPu, T4dPu*T6dPu), T6dPu);
  end if;
  xpdPu   = xdPu *Tpd /Tpd0;
  xppdPu  = xpdPu*Tppd/Tppd0;

  // Quadrature axis
  MqPu + LqPu = xqPu;
  T1qPu = (MqPu + LQ1Pu)/rQ1Pu;
  T2qPu = (MqPu + LQ2Pu)/rQ2Pu;
  T3qPu = (LQ2Pu + MqPu * LQ1Pu / (MqPu + LQ1Pu))/ rQ2Pu;
  T4qPu = (LQ1Pu + MqPu * LqPu  / (MqPu + LqPu)) / rQ1Pu;
  T5qPu = (LQ2Pu + MqPu * LqPu  / (MqPu + LqPu)) / rQ2Pu;
  T6qPu = (LQ2Pu + MqPu * LqPu * LQ1Pu / (MqPu * LqPu + MqPu * LQ1Pu + LqPu * LQ1Pu)) / rQ2Pu;
  if timeConstApprox == Types.Choices.TimeConstantsApproximation.classicalDefinition then
    Tpq0 *omegaBase = T1qPu;
    Tpq  *omegaBase = T4qPu;
    Tppq0*omegaBase = T3qPu;
    Tppq *omegaBase = T6qPu;
  elseif timeConstApprox == Types.Choices.TimeConstantsApproximation.accurateEstimation then
    Tpq0  * omegaBase = homotopy(T1qPu + T2qPu,               T1qPu);
    Tppq0 * omegaBase = homotopy(T1qPu*T3qPu/(T1qPu + T2qPu), T3qPu);
    Tpq   * omegaBase = homotopy(T4qPu + T5qPu,               T4qPu);
    Tppq  * omegaBase = homotopy(T4qPu*T6qPu/(T4qPu + T5qPu), T6qPu);
  else
    Tpq0  * omegaBase = homotopy( Functions.largerTimeConstant(T1qPu + T2qPu, T1qPu*T3qPu), T1qPu);
    Tppq0 * omegaBase = homotopy(Functions.smallerTimeConstant(T1qPu + T2qPu, T1qPu*T3qPu), T3qPu);
    Tpq   * omegaBase = homotopy( Functions.largerTimeConstant(T4qPu + T5qPu, T4qPu*T6qPu), T4qPu);
    Tppq  * omegaBase = homotopy(Functions.smallerTimeConstant(T4qPu + T5qPu, T4qPu*T6qPu), T6qPu);
  end if;
  xpqPu   = xqPu *Tpq /Tpq0;
  xppqPu  = xpqPu*Tppq/Tppq0;

  mrcPu = 0;
end SynchronousMachine4Windings;


Synchronous machine with 4 windings - internal parameters.

within PowerGrids.Electrical.Machines;

model SynchronousMachine4WindingsInternalParameters "Synchronous machine with 4 windings - internal parameters"
  extends BaseClasses.OnePortACdqPU(
    final generatorConvention = true,
    localInit = if initOpt == InitializationOption.localSteadyStateFixedPowerFlow
                then LocalInitializationOption.PV
                else LocalInitializationOption.none);
  extends Icons.Machine;
  import PowerGrids.Types.Choices.InitializationOption;
  import PowerGrids.Types.Choices.LocalInitializationOption;

  parameter Types.ActivePower PNom = SNom "Nominal active (turbine) power";
  parameter Types.PerUnit raPu "Armature resistance in p.u.";
  parameter Types.PerUnit LdPu "Direct axis stator leakage in p.u.";
  parameter Types.PerUnit MdPu "Direct axis mutual inductance in p.u.";
  parameter Types.PerUnit mrcPu "Canay's mutual inductance in p.u.";
  parameter Types.PerUnit LDPu "Direct axis damper leakage in p.u.";
  parameter Types.PerUnit rDPu "Resistance of the direct axis damper in p.u.";
  parameter Types.PerUnit LfPu "Excitation winding leakage in p.u.";
  parameter Types.PerUnit rfPu "Resistance of the excitation windings in p.u.";
  parameter Types.PerUnit LqPu "Quadrature axis stator leakage in p.u.";
  parameter Types.PerUnit MqPu "Quadrature axis mutual inductance in p.u.";
  parameter Types.PerUnit LQ1Pu "Quadrature axis 1st damper leakage in p.u.";
  parameter Types.PerUnit rQ1Pu "Quadrature axis 2nd damper leakage in p.u.";
  parameter Types.PerUnit LQ2Pu "Leakage of quadrature axis 2nd damper in p.u.";
  parameter Types.PerUnit rQ2Pu "Resistance of quadrature axis 2nd damper in p.u.";
  parameter Types.PerUnit DPu = 0 "Damping coefficient of the swing equation in p.u.";
  parameter Modelica.SIunits.Time H "Kinetic constant = kinetic energy / rated power";
  parameter Types.Choices.ExcitationPuType excitationPuType =
    PowerGrids.Types.Choices.ExcitationPuType.nominalStatorVoltageNoLoad "Choice of excitation base voltage";
  parameter Boolean neglectTransformerTerms = true "Neglect the transformer terms in the Park equations";
  parameter Boolean referenceGenerator = false "True if reference generator in an isolated synchronous system";
  parameter Types.Choices.InitializationOption initOpt = systemPowerGrids.initOpt "Initialization option";
  final parameter SI.AngularVelocity omegaBase = systemPowerGrids.omegaNom "Base angular frequency value";
  final parameter Types.PerUnit kuf(fixed = false) "Scaling factor for excitation p.u. voltage";
  constant Types.PerUnit omegaNomPu = 1 "Nominal frequency in p.u.";

  final parameter Types.PerUnit lambdadPuStart(fixed = false) "Start value of lambdadPu";
  final parameter Types.PerUnit lambdaqPuStart(fixed = false) "Start value of lambdaqPu";
  final parameter Types.PerUnit ufPuStart(fixed = false) "Start value of exciter voltage in p.u. (Kundur base)";
  final parameter Types.PerUnit ufPuInStart(fixed = false) "Start value of input exciter voltage in p.u. (user-selcted base";
  final parameter Types.PerUnit ifPuStart(fixed = false) "Start value of ifPu";
  // Input variables
  Modelica.Blocks.Interfaces.RealInput PmPu(unit = "1") "Input mechanical power in p.u. (base PNom)"
  Modelica.Blocks.Interfaces.RealInput ufPuIn(unit = "1", start = ufPuInStart) "Input voltage of exciter winding in p.u. (user-selected base voltage)"
  // Output variables
  Modelica.Blocks.Interfaces.RealOutput omega(unit = "rad/s") "Angular frequency in rad/s for system object"
// Other per-unit variables
  Modelica.Blocks.Interfaces.RealOutput omegaPu(start = 1) "Angular frequency in p.u."
  Types.PerUnit iDPu(start = 0) "Current of direct axis damper in p.u";
  Types.PerUnit ifPu(start = ifPuStart) "Current of excitation winding in p.u.";
  Types.PerUnit iQ1Pu(start = 0) "Current of quadrature axis 1st damper in p.u.";
  Types.PerUnit iQ2Pu(start = 0) "Current of quadrature axis 2nd damper in p.u.";
  Types.PerUnit ufPu(start = ufPuStart) "Voltage of exciter winding in p.u. (base voltage as per Kundur)";
  Types.PerUnit lambdadPu(start = lambdadPuStart) "Flux of direct axis in p.u.";
  Types.PerUnit lambdaqPu(start = lambdaqPuStart) "Flux of quadrature axis in p.u.";
  Types.PerUnit lambdaDPu "Flux of direct axis damper in p.u";
  Types.PerUnit lambdafPu "Flux of excitation winding in p.u.";
  Types.PerUnit lambdaQ1Pu "Flux of quadrature axis 1st damper in p.u.";
  Types.PerUnit lambdaQ2Pu "Flux of quadrature axis 1st damper in p.u.";
  Types.PerUnit omegaRefPu = systemPowerGrids.omegaRef/systemPowerGrids.omegaNom "Reference frequency in p.u.";
  Types.PerUnit CmPu(start = -PStart/SNom) "Mechanical torque in p.u. (base PNom/omegaBase)";
  Types.PerUnit CePu(start = -PStart/SNom) "Electrical torque in p.u. (base SNom/omegaBase)";
  Types.PerUnit PePu(start = -PStart/SNom) "Electrical power in p.u. (base SNom)";
  Types.PerUnit PePuPNom = PePu*SNom/PNom "Electrical active power in p.u. (base PNom)";

  // SI-unit variables
  Types.Frequency f = systemPowerGrids.fNom*omegaPu "Frequency of rotation of d-q axes";
  Modelica.Blocks.Interfaces.RealOutput VPu "Port voltage magnitude [pu]"
  Modelica.Blocks.Interfaces.RealOutput PPu "Active Power Production [pu base PNom]"
initial equation
  // Scaling factor for excitation p.u. voltage
  if excitationPuType == Types.Choices.ExcitationPuType.Kundur then
    kuf = 1 "Choice of per-unit as per Kundur textbook";
  else
    kuf = rfPu / MdPu "Base voltage gives 1 p.u. air-gap stator voltage at no load";
  end if;
  // Equations to compute start values
  // these equations are the same that involve the actual variables in the equation section
  // except that they involve the start values instead, and they do not contain terms which
  // are zero at steady state (e.g., all the damping winding currents)
  // the Modelica tool figures out automatically how to solve them for the unknown parameters
  udPuStart = raPu*idPuStart - omegaNomPu*lambdaqPuStart;
  uqPuStart = raPu*iqPuStart + omegaNomPu*lambdadPuStart;
  lambdadPuStart = (MdPu + LdPu)*idPuStart + MdPu*ifPuStart;
  ufPuStart =  rfPu*ifPuStart;
  ufPuInStart = ufPuStart/kuf;

  // Equations to determine the initial state values
  if initOpt == InitializationOption.noInit then
    // No initial equations
  else
    omegaPu = omegaNomPu;
    der(omegaPu) = 0;
    if not neglectTransformerTerms then
      der(lambdadPu) = 0;
      der(lambdaqPu) = 0;
    end if;
    der(lambdafPu) = 0;
    der(lambdaDPu) = 0;
    der(lambdaQ1Pu) = 0;
    der(lambdaQ2Pu) = 0;
  end if;
equation
  // Flux linkages
  lambdadPu = (MdPu + LdPu) * idPu + MdPu * ifPu + MdPu * iDPu;
  lambdafPu =     MdPu      *idPu + (MdPu + LfPu + mrcPu)*ifPu +    (MdPu + mrcPu)    *iDPu;
  lambdaDPu =     MdPu      *idPu +     (MdPu + mrcPu)   *ifPu + (MdPu + LDPu + mrcPu)*iDPu;
  lambdaqPu = (MqPu + LqPu) *iqPu +         MqPu         *iQ1Pu +        MqPu         *iQ2Pu;
  lambdaQ1Pu =     MqPu     *iqPu +     (MqPu + LQ1Pu )  *iQ1Pu +        MqPu         *iQ2Pu;
  lambdaQ2Pu =     MqPu     *iqPu +         MqPu         *iQ1Pu +    (MqPu + LQ2Pu)   *iQ2Pu;

  // Equivalent circuit equations in Park's coordinates
  if neglectTransformerTerms then
    udPu = raPu * idPu - omegaPu * lambdaqPu;
    uqPu = raPu * iqPu + omegaPu * lambdadPu;
  else
    udPu = raPu * idPu - omegaPu * lambdaqPu + der(lambdadPu) / omegaBase;
    uqPu = raPu * iqPu + omegaPu * lambdadPu + der(lambdaqPu) / omegaBase;
  end if;
  ufPu =  rfPu *ifPu  + der(lambdafPu)/omegaBase;
  0    =  rDPu *iDPu  + der(lambdaDPu)/omegaBase;
  0    =  rQ1Pu*iQ1Pu + der(lambdaQ1Pu)/omegaBase;
  0    =  rQ2Pu*iQ2Pu + der(lambdaQ2Pu)/omegaBase;

  // Mechanical equations
  der(theta) = (omegaPu - omegaRefPu) * omegaBase;
  2*H*der(omegaPu) = (CmPu*PNom/SNom - CePu) - DPu*(omegaPu - omegaRefPu);
  CePu = lambdaqPu*idPu - lambdadPu*iqPu;
  PePu = CePu*omegaPu;
  PmPu = CmPu*omegaPu;
  omega = omegaPu*omegaBase;

  // Excitation voltage p.u. conversion
  ufPu = ufPuIn * kuf;

  // Output signal equations
  VPu = port.VPu;
  PPu = -port.P/PNom;
end SynchronousMachine4WindingsInternalParameters;


Components for power flow calculations

within PowerGrids.Electrical;

package PowerFlow "Components for power flow calculations"
  extends Modelica.Icons.Package;
end PowerFlow;


PQ bus.

within PowerGrids.Electrical.PowerFlow;

model PQBus "PQ bus"
  extends BaseClasses.OnePortAC(portVariablesPhases = true);
  extends Icons.Load;
  parameter Types.ActivePower P = SNom "Active power entering the bus";
  parameter Types.ReactivePower Q = 0 "Reactive power entering the bus";
equation
  port.P = P;
  port.Q = Q;

end PQBus;


PV bus.

within PowerGrids.Electrical.PowerFlow;

model PVBus "PV bus"
  extends BaseClasses.OnePortAC(portVariablesPhases = true);
  extends Icons.Machine;
  parameter Types.ActivePower P = SNom "Active power entering the bus";
  parameter Types.Voltage U = UNom "Absolute value of phase-to-phase voltage";
equation
  port.P = P;
  port.U = U;

end PVBus;


Slack Bus.

within PowerGrids.Electrical.PowerFlow;

model SlackBus "Slack Bus"
  extends BaseClasses.OnePortAC;
  extends Icons.Bus;
  parameter Types.Voltage U = UNom "Voltage magnitude, phase-to-phase";
  parameter Types.Angle theta = 0 "Phase of voltage phasor";
equation
  port.u = CM.fromPolar(U, theta) "Voltage of ideal generator, phase-to-ground";

end SlackBus;


Load with constant impedance.

within PowerGrids.Electrical.PowerFlow;

model ZLoad "Load with constant impedance"
  extends BaseClasses.OnePortAC;
  extends Icons.Load;
  parameter Types.ActivePower PNom = SNom "Nominal active power";
  parameter Types.ActivePower QNom = 0 "Nominal reactive power";

  parameter Types.ComplexImpedance Z = UNom^2/Complex(PNom, -QNom) "Constant impedance of the load";
equation
  port.v = Z*port.i;
end ZLoad;


Simple test case with one slack bus connected to a PQ bus..

within PowerGrids.Electrical.PowerFlow.Test;

model OneSlackOneLoad
  extends Modelica.Icons.Example;
  inner PowerGrids.Electrical.System systemPowerGrids
  PowerGrids.Electrical.PowerFlow.SlackBus slack(SNom = 1e+08, UNom = 400000)
  PowerGrids.Electrical.PowerFlow.PQBus load(P = 3e+07, Q = 0, SNom = 3e+07, UNom = 400000)
equation
  connect(load.terminal, slack.terminal)end OneSlackOneLoad;


Simple test case with one slack bus and one PQ bus connected through a <a href=\.

within PowerGrids.Electrical.PowerFlow.Test;

model OneSlackOneLoadWithOneLine
  extends Modelica.Icons.Example;
  inner PowerGrids.Electrical.System systemPowerGrids
  PowerGrids.Electrical.PowerFlow.SlackBus slack(SNom = 1e+08, UNom = 400000)
  PowerGrids.Electrical.PowerFlow.PQBus load(P = 3e+07, Q = 0, SNom = 3e+07, UNom = 400000)
  PowerGrids.Electrical.Branches.LineConstantImpedance line(R = 200, SNom = 3e+07, UNom = 400000, X = 2000)
equation
  connect(line.terminalB, load.terminal)
  connect(line.terminalA, slack.terminal)end OneSlackOneLoadWithOneLine;


Simple test case, similar to <a href=\.

within PowerGrids.Electrical.PowerFlow.Test;

model OneSlackOnePVWithOneLine
  extends Modelica.Icons.Example;
  inner PowerGrids.Electrical.System systemPowerGrids
  PowerGrids.Electrical.PowerFlow.SlackBus slack(SNom = 1e+08, UNom = 400000)
  PowerGrids.Electrical.Branches.LineConstantImpedance line(R = 200, SNom = 3e+07, UNom = 400000, X = 2000)
  PowerGrids.Electrical.PowerFlow.PVBus node1(P = 3e+07, SNom = 3e+07, UNom = 400000)
equation
  connect(line.terminalB, node1.terminal)
  connect(slack.terminal, line.terminalA)
end OneSlackOnePVWithOneLine;


Simple test system with one slack node and one <a href=\.

within PowerGrids.Electrical.PowerFlow.Test;
model OneSlackOneZLoad
  extends Modelica.Icons.Example;
  inner PowerGrids.Electrical.System systemPowerGrids
  PowerGrids.Electrical.PowerFlow.SlackBus slack(SNom = 1e+08, UNom = 400000)
  PowerGrids.Electrical.PowerFlow.ZLoad load(PNom = 2e+07, QNom = 1e+07, SNom = 3e+07, UNom = 400000)
equation
  connect(load.terminal, slack.terminal)end OneSlackOneZLoad;


Small system with a few PQ buses, one PV bus and one slack bus connected through constant impedant lines..

within PowerGrids.Electrical.PowerFlow.Test;

model SmallSystem
  extends Modelica.Icons.Example;
  inner PowerGrids.Electrical.System systemPowerGrids
  PowerGrids.Electrical.PowerFlow.PVBus bus2(P = -1e+08,SNom = 1e+08, UNom = 400000, portVariablesPu = true)
  PowerGrids.Electrical.Branches.LineConstantImpedance line1(R = 100, SNom = 1e+08, UNom = 400000, X = 1000, portVariablesPu = true)
  PowerGrids.Electrical.Branches.LineConstantImpedance line2(R = 100, SNom = 1e+08, UNom = 400000, X = 1000, portVariablesPu = true)
  PowerGrids.Electrical.Branches.LineConstantImpedance line3(R = 100, SNom = 1e+08, UNom = 400000, X = 1000, portVariablesPu = true)
  PowerGrids.Electrical.PowerFlow.PQBus load1(P = 3e+07, SNom = 3e+07, UNom = 400000, portVariablesPu = true)
  PowerGrids.Electrical.PowerFlow.PQBus load2(P = 2e+07, SNom = 4e+07, UNom = 400000, portVariablesPu = true)
  PowerGrids.Electrical.PowerFlow.PQBus load3(P = 2e+07, SNom = 2e+07, UNom = 400000, portVariablesPu = true)
  PowerGrids.Electrical.PowerFlow.PQBus load4(P = 3e+07, SNom = 3e+07, UNom = 400000, portVariablesPu = true)
  PowerGrids.Electrical.PowerFlow.SlackBus slack(SNom = 1e+08, UNom = 400000, portVariablesPu = true)
  Buses.Bus bus(SNom = 2e+07, UNom = 400000)
equation
  connect(load1.terminal, line1.terminalA)
  connect(line3.terminalB, load4.terminal)
  connect(line2.terminalB, load2.terminal)
  connect(load1.terminal, slack.terminal)
  connect(line2.terminalB, bus2.terminal)
  connect(line1.terminalB, bus.terminal)
  connect(line2.terminalA, bus.terminal)
  connect(line3.terminalA, bus.terminal)
  connect(load3.terminal, bus.terminal)end SmallSystem;


info

within PowerGrids.Electrical.Test;

package Info
  extends Modelica.Icons.Info;
end Info;


Test case for LoadImpedancePQ.

within PowerGrids.Electrical.Test;

model LoadImpedancePQ "Test case for LoadImpedancePQ"
  extends Modelica.Icons.Example;
  inner System systemPowerGrids
  PowerGrids.Electrical.Loads.LoadImpedancePQ load1(PRef = 8e+07, PRefConst = 40e6, QRef = 4e+07, QRefConst = 20e6, SNom = 1e+08, UNom = 380000, URef = 400000, portVariablesPhases = true, portVariablesPu = true)
  PowerGrids.Electrical.Loads.LoadImpedancePQ load2(PRef = 8e+07 + (if time >= 1 then 1e7 else 0), PRefConst = 4e+07, QRef = 4e+7 + (if time >= 2 then 5e6 else 0), QRefConst = 2e+07, SNom = 1e+08, UNom = 380000, URef = 400000, portVariablesPhases = true, portVariablesPu = true)
  PowerGrids.Electrical.Loads.LoadImpedancePQInputs load3(SNom = 5e+08, UNom = 380000, URef = 400000, portVariablesPhases = true, portVariablesPu = true)
  PowerGrids.Electrical.Buses.InfiniteBus infiniteBus1(SNom = 1e+08, UNom = 380000, URef = 400000, portVariablesPhases = true, portVariablesPu = true)
  PowerGrids.Electrical.Buses.InfiniteBus infiniteBus2(SNom = 1e+08, UNom = 380000, URef = 400000, portVariablesPhases = true, portVariablesPu = true)
  PowerGrids.Electrical.Buses.InfiniteBus infiniteBus3(SNom = 1e+08, UNom = 380000, URef = 400000, portVariablesPhases = true, portVariablesPu = true)
  Modelica.Blocks.Sources.Step PRef(height = 10e6, offset = 80e6, startTime = 1)
  Modelica.Blocks.Sources.Step QRef(height = 10e6, offset = 40e6, startTime = 2)
equation
  connect(QRef.y, load3.QRefIn)
  connect(PRef.y, load3.PRefIn)
  connect(infiniteBus3.terminal, load3.terminal)
  connect(infiniteBus2.terminal, load2.terminal)
  connect(infiniteBus1.terminal, load1.terminal)
end LoadImpedancePQ;


This model is similar to <a href=\.

within PowerGrids.Electrical.Test;

model OneBusImpedanceOneLoad
  extends OneBusOneLoad(infiniteBus.R = 0.3, infiniteBus.X = 1.5, infiniteBus.portVariablesPhases = true, infiniteBus.portVariablesPu = true, infiniteBus.generatorConvention = true);
end OneBusImpedanceOneLoad;


This model demonstrates both ways of implementing a time-varying load.

The model is similar to <a href=\.

within PowerGrids.Electrical.Test;

model OneBusImpedanceOneVariableLoad
  extends Modelica.Icons.Example;
  PowerGrids.Electrical.Buses.InfiniteBus bus1(PStart = -1e+07,R = 0.04,SNom = 1e+08, UNom = 10000, X = 0.4, generatorConvention = true, portVariablesPhases = true, portVariablesPu = true)
  PowerGrids.Electrical.Loads.LoadPQVoltageDependence load1(
    PRef = if time < 1 then 10e6 else 20e6, PStart = 1e+07,
    QRef = if time < 2 then 0 else 30e6, QStart = 0, SNom = 1e+08, UNom = 10000, portVariablesPhases = true)
  inner System systemPowerGrids
  PowerGrids.Electrical.Buses.InfiniteBus bus2(PStart = -1e+07,R = 0.04, SNom = 1e+08, UNom = 10000, X = 0.4, generatorConvention = true, portVariablesPhases = true, portVariablesPu = true)
  PowerGrids.Electrical.Loads.LoadPQVoltageDependenceInputs load2(PStart = 1e+07, SNom = 1e+08, UNom = 10000, portVariablesPhases = true)
  Modelica.Blocks.Sources.Step PSignal(height = 10e6, offset = 10e6, startTime = 1)
  Modelica.Blocks.Sources.Step QSignal(height = 30e6, startTime = 2)
equation
  connect(PSignal.y, load2.PRefIn)
  connect(QSignal.y, load2.QRefIn)
  connect(bus2.terminal, load2.terminal)
  connect(bus1.terminal, load1.terminal)
end OneBusImpedanceOneVariableLoad;


This model demonstrates the usage of a variable-voltage infinite bus.

The model is similar to <a href=\.

within PowerGrids.Electrical.Test;

model OneBusImpedanceVariableVoltageOneLoad
  extends Modelica.Icons.Example;
  PowerGrids.Electrical.Buses.InfiniteBusVariableVoltage bus1(PStart = -1e+07, R = 0.04, SNom = 1e+08, UNom = 10000, X = 0.4, generatorConvention = true, portVariablesPhases = true, portVariablesPu = true, useThetaIn = true, useUIn = true)
  PowerGrids.Electrical.Loads.LoadPQVoltageDependence load1(PRefConst = 1e+07,PStart = 1e+07, QRefConst = 0, QStart = 0, SNom = 1e+08, UNom = 10000, portVariablesPhases = true)
  inner System systemPowerGrids
  Modelica.Blocks.Sources.Step UMod(height = -2e3, offset = 10e3, startTime = 1)
  Modelica.Blocks.Sources.Step UPhase(height = 30 / 180 * Modelica.Constants.pi, startTime = 2)
equation
  connect(UPhase.y, bus1.thetaIn)
  connect(UMod.y, bus1.UIn)
  connect(bus1.terminal, load1.terminal)
end OneBusImpedanceVariableVoltageOneLoad;


An infinite bus with zero impedance, U_abs = 10 kV, theta = 30 deg, is connected to a PQ load with P = 10 MW and Q = 0 MW.

As the nominal power is set to 10 MW and the nominal voltage to 10 kV on both components, all p.u. absolute values are one. Since the load is purely resistive, all phasors are rotated by 30 deg.

For simplicity, the Start values are set assuming theta = 0; the nonlinear solver is able to converge on the correct solution from this initial guess.

This model demonstrates the correct behaviour of the <a href=\.

within PowerGrids.Electrical.Test;

model OneBusOneLoad
  extends Modelica.Icons.Example;
  PowerGrids.Electrical.Buses.InfiniteBus infiniteBus(SNom = 1e+07, UNom = 10000, portVariablesPu = true, theta = 0.523599)
  PowerGrids.Electrical.Loads.LoadPQVoltageDependence loadPQ(PRefConst = 1e+07, QRefConst = 0, SNom = 1e+07, UNom = 10000, portVariablesPhases = true)
  inner PowerGrids.Electrical.System systemPowerGrids
equation
  connect(infiniteBus.terminal, loadPQ.terminal)
end OneBusOneLoad;


Same behaviour as <a href=\.

within PowerGrids.Electrical.Test;

model OneBusTransmissionLineOneLoad
  extends Modelica.Icons.Example;
  PowerGrids.Electrical.Buses.InfiniteBus infiniteBus(SNom = 1e+07, UNom = 10000, portVariablesPu = true, theta = 0.523599)
  PowerGrids.Electrical.Loads.LoadPQVoltageDependence loadPQ(PRefConst = 1e+07, QRefConst = 0, SNom = 1e+07, UNom = 10000, alpha = 0, beta = 0, portVariablesPhases = true)
  inner PowerGrids.Electrical.System systemPowerGrids
  PowerGrids.Electrical.Branches.LineConstantImpedance transmissionLine(R = 0.3, SNom = 1e+07, UNom = 10000, X = 1.5, portVariablesPu = true)
equation
  connect(infiniteBus.terminal, transmissionLine.terminalA)
  connect(transmissionLine.terminalB, loadPQ.terminal)
end OneBusTransmissionLineOneLoad;


This model is similar to<a href=\.

within PowerGrids.Electrical.Test;

model OneBusTransmissionLineShuntOneLoad
  extends OneBusTransmissionLineOneLoad(transmissionLine.B = 0.02, transmissionLine.G = 0.01, transmissionLine.portVariablesPu = true);
end OneBusTransmissionLineShuntOneLoad;


Component test models

within PowerGrids.Electrical;
package Test "Component test models"
  extends Modelica.Icons.ExamplesPackage;
end Test;


Test case for the conversion of external parameter to internal parameters taken from Kundur, Power System Stability and Control, example 4.1. Note that the system frequency for this example is 60 Hz.

The machine is directly connected to an ideal bus with UNom = 24 kV and phase = 0 and initialized in steady-state with PmPu = 0.9, ufPu = 0.000939, using the Kundur base excitation voltage.

 Parameter	Meaning	Value	Unit

 SNom	Rated power	550	MVA

 UNom	Rated phase-to-phase voltage	24	kV

 xlPu	Stator Leakage	0.15	pu

 xdPu	Direct axis reactance	1.81	pu

 xpdPu	Direct axis transient reactance	0.3	pu

 xppdPu	Direct axis sub-transient reactance	0.23	pu

 Tpd0	Direct axis open circuit transient time constant	8	s

 Tppd0	Direct axis open circuit sub-transient time constant	0.03	s

 xqPu	Quadrature axis reactance	1.76	pu

 xpqPu	Quadrature axis transient reactance	0.65	pu

 xppqPu	Quadrature axis sub-transient reactance	0.25	pu

 Tpq0	Quadrature axis open circuit transient time constant	1	s

 Tppq0	Quadrature axis open circuit sub-transient time constant	0.07	s

 raPu	Armature resistance	0.003	pu

 mrcPu	Canay's mutual inductance	0	pu

The following internal parameter values are reported from Kundur and match the ones computed by the model with timeConstantApproximation=classicalDefinitions.

 PowerGrids	Kundur	Meaning	Value	Unit

LdPu	Ll	Direct axis stator leakage	0.15	pu

 MdPu	Lad	Direct axis mutual inductance	1.66	pu

 LDPu	L1d	Direct axis damper leakage	0.1713	pu

 rDPu	R1d	Direct axis damper resistance	0.0284	pu

 LfPu	Lfd	Excitation winding leakage	0.165	pu

 rfPu	Rfd	Excitation windings resistance	0.000605	pu

 LqPu	Ll	Quadrature axis stator leakage 	0.15	pu

 MqPu	Laq	Quadrature axis mutual inductance	1.61	pu

 LQ1Pu	L1q	Quadrature axis 1st damper leakage	0.7252	pu

 rQ1Pu	R1q	Quadrature axis 1st damper resistance	0.0062	pu

 LQ2Pu	L2q	Quadrature axis 2nd damper leakage	0.125	pu

 rQ2Pu	R2q	Quadrature axis 2nd damper resistance	0.02368	pu

The following table reports the steady-state solution computed by Kundur in Example 3.2 and by the PowerGrids model. Note that the Kundur example unfortunately includes a the effect of an 83.5% magnetic saturation, which is not accounted for in the PowerGrids model, so the two sets of values do not match exactly, particularly as regards the reactive power and the quadrature current. However, the difference is compatible with the effect of the reduced inductances. Also note that the sign convention for id, iq is opposite to Kundur's.

 Variable	Name	Value Kundur	Value PowerGrids	Unit

 PGenPu	Generated active power	0.9	0.89667	pu

 QGenPu	Generated reactive power	0.436	0.55206	pu

 theta	Machine angle	39.1	38.61	deg

 udPu	Direct axis voltage	0.631	0.6240	pu

 uqPu	Quadrature axis voltage	0.776	0.7814	pu

 idPu	Direct axis current	0.906	-0.9909	pu

 iqPu	Quadrature axis current	0.423	-0.3562	pu

.

within PowerGrids.Electrical.Test;

model SynchronousMachine4Windings
  extends Modelica.Icons.Example;
  PowerGrids.Electrical.Buses.InfiniteBus bus(SNom = 5.5e+08, UNom = 24000, UStart = 24000, X = 0, portVariablesPhases = true, portVariablesPu = true, theta = 0)
  PowerGrids.Electrical.Machines.SynchronousMachine4Windings machine(H = 6, PStart = -5.52e+08, QStart = -2.52e+08, SNom = 5.5e+08, Tpd0 = 8, Tppd0 = 0.03, Tppq0 = 0.07, Tpq0 = 1, UNom = 24000, UStart = 24000, excitationPuType = PowerGrids.Types.Choices.ExcitationPuType.Kundur, portVariablesPhases = true, raPu = 0.003, timeConstApprox = PowerGrids.Types.Choices.TimeConstantsApproximation.classicalDefinition, xdPu = 1.81, xlPu = 0.15, xpdPu = 0.3, xppdPu = 0.23, xppqPu = 0.25, xpqPu = 0.65, xqPu = 1.76)
  inner PowerGrids.Electrical.System systemPowerGrids(fNom = 60)
  Modelica.Blocks.Sources.Step PmPu(height = 0.0, offset = 0.9, startTime = 50)
  Modelica.Blocks.Sources.Step ufPu(height = 0, offset = 0.000939)
equation
  connect(bus.terminal, machine.terminal)
  connect(ufPu.y, machine.ufPuIn)
  connect(PmPu.y, machine.PmPu)
end SynchronousMachine4Windings;


within PowerGrids.Electrical.Test;

model SynchronousMachine4WindingsAccurate
  extends Modelica.Icons.Example;
  PowerGrids.Electrical.Buses.InfiniteBus bus(SNom = 5.5e+08, UNom = 24000, UStart = 24000, X = 0, portVariablesPhases = true, portVariablesPu = true, theta = 0)
  PowerGrids.Electrical.Machines.SynchronousMachine4Windings machine(H = 6, PStart = -5.52e+08, QStart = -2.52e+08, SNom = 5.5e+08, Tpd0 = 8, Tppd0 = 0.03, Tppq0 = 0.07, Tpq0 = 1, UNom = 24000, UStart = 24000, excitationPuType = PowerGrids.Types.Choices.ExcitationPuType.Kundur, portVariablesPhases = true, raPu = 0.003, timeConstApprox = PowerGrids.Types.Choices.TimeConstantsApproximation.accurateEstimation, xdPu = 1.81, xlPu = 0.15, xpdPu = 0.3, xppdPu = 0.23, xppqPu = 0.25, xpqPu = 0.65, xqPu = 1.76)
  inner PowerGrids.Electrical.System systemPowerGrids(fNom = 60)
  Modelica.Blocks.Sources.Step PmPu(height = 0.0, offset = 0.9, startTime = 50)
  Modelica.Blocks.Sources.Step ufPu(height = 0, offset = 0.000939)
equation
  connect(bus.terminal, machine.terminal)
  connect(ufPu.y, machine.ufPuIn)
  connect(PmPu.y, machine.PmPu)
end SynchronousMachine4WindingsAccurate;


within PowerGrids.Electrical.Test;

model SynchronousMachine4WindingsExact
  extends Modelica.Icons.Example;
  PowerGrids.Electrical.Buses.InfiniteBus bus(SNom = 5.5e+08, UNom = 24000, UStart = 24000, X = 0, portVariablesPhases = true, portVariablesPu = true, theta = 0)
  PowerGrids.Electrical.Machines.SynchronousMachine4Windings machine(H = 6, PStart = -5.52e+08, QStart = -2.52e+08, SNom = 5.5e+08, Tpd0 = 8, Tppd0 = 0.03, Tppq0 = 0.07, Tpq0 = 1, UNom = 24000, UStart = 24000, excitationPuType = PowerGrids.Types.Choices.ExcitationPuType.Kundur, portVariablesPhases = true, raPu = 0.003, timeConstApprox = PowerGrids.Types.Choices.TimeConstantsApproximation.exactComputation, xdPu = 1.81, xlPu = 0.15, xpdPu = 0.3, xppdPu = 0.23, xppqPu = 0.25, xpqPu = 0.65, xqPu = 1.76)
  inner PowerGrids.Electrical.System systemPowerGrids(fNom = 60)
  Modelica.Blocks.Sources.Step PmPu(height = 0.0, offset = 0.9, startTime = 50)
  Modelica.Blocks.Sources.Step ufPu(height = 0, offset = 0.000939)
equation
  connect(bus.terminal, machine.terminal)
  connect(ufPu.y, machine.ufPuIn)
  connect(PmPu.y, machine.PmPu)
end SynchronousMachine4WindingsExact;


This test case validates the NoLoad choice of base excitation voltage of the SynchronousMachine4Windings model.

By setting ufPuIn = 1 and PmPu = 0, one obtains machine.port.VPu = 1 and machine.port.IPu = 0, as  required by the base voltage definition.

The extermal machine parameters are the same as in the  <a href=\.

within PowerGrids.Electrical.Test;

model SynchronousMachine4WindingsNoLoad
  extends Modelica.Icons.Example;
  PowerGrids.Electrical.Buses.InfiniteBus bus(PStart = 0,SNom = 5.5e+08, UNom = 24000, UStart = 24000, X = 0, portVariablesPhases = true, portVariablesPu = true, theta = 0)
  PowerGrids.Electrical.Machines.SynchronousMachine4Windings machine(H = 6, PStart = 0, QStart = 0, SNom = 5.5e+08, Tpd0 = 8, Tppd0 = 0.03, Tppq0 = 0.07, Tpq0 = 1, UNom = 24000, UStart = 24000, excitationPuType = PowerGrids.Types.Choices.ExcitationPuType.nominalStatorVoltageNoLoad, portVariablesPhases = true, raPu = 0.003, timeConstApprox = PowerGrids.Types.Choices.TimeConstantsApproximation.classicalDefinition, xdPu = 1.81, xlPu = 0.15, xpdPu = 0.3, xppdPu = 0.23, xppqPu = 0.25, xpqPu = 0.65, xqPu = 1.76)
  inner PowerGrids.Electrical.System systemPowerGrids(fNom = 50)
  Modelica.Blocks.Sources.Step PmPu(height = 0.0, offset = 0.0, startTime = 50)
  Modelica.Blocks.Sources.Step ufPu(height = 0, offset = 1)
equation
  connect(bus.terminal, machine.terminal)
  connect(ufPu.y, machine.ufPuIn)
  connect(PmPu.y, machine.PmPu)
end SynchronousMachine4WindingsNoLoad;