AC machines, electric part

within PowerSystems.AC3ph;
package Machines "AC machines, electric part "
  extends Modelica.Icons.VariantsPackage;

  model Asynchron "Asynchronous machine, cage-rotor, 3-phase dq0"
    extends Partials.AsynchronBase(redeclare replaceable record Data =
      PowerSystems.AC3ph.Machines.Parameters.Asynchron_cage);

  initial equation
    phi_el = phi_el_start;
  end Asynchron;

  model AsynchronY_D "Asynchronous machine Y-Delta, cage-rotor, 3-phase dq0"
    extends Partials.AsynchronBase(redeclare model Topology_p =
      PowerSystems.AC3ph.Ports.Topology.Y_Delta(control=YDcontrol) "Y-Delta",
      redeclare replaceable record Data =
          PowerSystems.AC3ph.Machines.Parameters.Asynchron_cage);
   Modelica.Blocks.Interfaces.BooleanInput YDcontrol "true:Y, false:Delta"

  initial equation
    phi_el = phi_el_start;

  equation
  /*
  connect(YDcontrol, top.control)
*/
  end AsynchronY_D;

  model Asynchron_ctrl
    "Asynchronous machine, cage-rotor, for field-oriented control, 3-phase dq0"
    extends Partials.AsynchronBase(redeclare replaceable record Data =
      PowerSystems.AC3ph.Machines.Parameters.Asynchron_cage);

    parameter PS.Current[n_r] i_d_start = zeros(n_r)
      "start value of current d_axis"
    parameter PS.Current[n_r] i_q_start = zeros(n_r)
      "start value of current q_axis"

    Modelica.Blocks.Interfaces.RealOutput[2] i_meas(each final unit="1")
      "measured current {i_d, i_q} pu"
    Modelica.Blocks.Interfaces.RealInput[2] i_act(each final unit="1")
      "actuated current {i_d, i_q} pu"
    Modelica.Blocks.Interfaces.RealOutput phiRotorflux "rotor-flux angle"
    Modelica.Blocks.Interfaces.RealOutput[2] vPhasor
      "desired {abs(v), phase(v)}"
  protected
    constant Real eps=Modelica.Constants.eps;
    final parameter PS.Current I_nom=par.S_nom/par.V_nom;
    SI.Angle alpha_i;
    SI.Angle alpha_psi;
    PS.Voltage[2] v_dq "stator voltage demand in rotor flux-system";
    PS.Current[2] i_dq "stator current demand in rotor flux-system";
    PS.Current[n_r] i_d(start = i_d_start);
    PS.Current[n_r] i_q(start = i_q_start);
    function acos=Modelica.Math.acos;

  initial equation
    phi_el = phi_el_start;
    phiRotorflux = 0;

  equation
    der(phiRotorflux) = w_el - (-diagonal(R_r)*i_rd*psi_rq + diagonal(R_r)*i_rq*psi_rd)/(psi_rd*psi_rd + psi_rq*psi_rq + eps);
    alpha_i = atan2(i[2], i[1]);
    alpha_psi = atan2(R_m*psi_rq, R_m*psi_rd);
    i_meas = {cos(alpha_i - alpha_psi),sin(alpha_i - alpha_psi)}*sqrt(i[1:2]*i[1:2])/I_nom;
    i_dq =  i_act*I_nom;
    (sum(omega) - w_el)*(-L_m*i_dq[2] - L_r*i_q) + diagonal(R_r)*i_d = zeros(n_r);
    (sum(omega) - w_el)*(L_m*i_dq[1] + L_r*i_d) + diagonal(R_r)*i_q = zeros(n_r);
    v_dq = sum(omega)*{-(c.L_s[2]*i_dq[2] + L_m*i_q), c.L_s[1]*i_dq[1] + L_m*i_d} + c.R_s*i_dq;
    vPhasor = {sqrt(v_dq*v_dq)/par.V_nom, atan2(v_dq[2], v_dq[1]) + atan2(sin(alpha_psi - term.theta[1]), cos(alpha_psi - term.theta[1]))};
  end Asynchron_ctrl;

  model Asynchron_dfig "Doubly fed induction generator, 3-phase dq0"
    extends AC3ph.Machines.Partials.AsynchronBase(v_rd = term_r.v[1]*ones(n_r), v_rq = term_r.v[2]*ones(n_r));
    Modelica.Blocks.Interfaces.RealOutput phiRotor "rotor angle"

    AC3ph.Ports.ACdq0_p term_r "positive terminal"
  initial equation
    phi_el = phi_el_start;
    phiRotor = 0;

  equation
    der(phiRotor) = w_el;
    term_r.i = {sum(i_rd), sum(i_rq), 0};
  end Asynchron_dfig;

  model Synchron3rd_ee
    "Synchronous machine electrically excited, 3rd order model, 3-phase dq0"
    extends Partials.Synchron3rdBase(final phi_el_start=-pi/2+system.alpha0,
      redeclare replaceable record Data =
        PowerSystems.AC3ph.Machines.Parameters.Synchron3rd_ee);

    final parameter PS.Voltage Vf_nom=par.V_nom; // to be accessible from 'excitation'.
    output SI.Angle powerAngle(start=0, stateSelect=StateSelect.never)
      "power angle";
    AC1ph_DC.Ports.TwoPin_p field "field winding"

  initial equation
    w_el = sum(omega);

  equation
    if par.excite==1 then
      psi_e = (field.v[1] - field.v[2])/c.omega_nom;
      field.i = zeros(2);
    else
      assert(false, "machine-parameter must be excite = 1 (el)");
    end if;
    powerAngle = noEvent(mod(phi_el - term.theta[2] - atan2(-v[1], v[2]) + pi, 2*pi)) - pi;
  end Synchron3rd_ee;

  model Synchron_ee "Synchronous machine electrically excited, 3-phase dq0"
    extends Partials.SynchronBase(final phi_el_start=-pi/2+system.alpha0,
      redeclare replaceable record Data =
        PowerSystems.AC3ph.Machines.Parameters.Synchron_ee);
    final parameter PS.Voltage Vf_nom=c.Vf_nom; // to be accessible from 'excitation'.
    output SI.Angle powerAngle(start=0, stateSelect=StateSelect.never)
      "power angle";
    AC1ph_DC.Ports.TwoPin_p field "field winding"

  initial equation
    w_el = sum(omega);

  equation
    if par.excite==1 then
      psi_e = 0;
      v_rd[1] = (field.v[1] - field.v[2])*c.wf;
      field.i = zeros(2);
    else
      assert(false, "machine-parameter must be excite = 1 (el)");
    end if;
    powerAngle = noEvent(mod(phi_el - term.theta[2] - atan2(-v[1], v[2]) + pi, 2*pi)) - pi;
  end Synchron_ee;

  model Synchron3rd_pm "Synchronous machine pm, 3rd order model, 3-phase dq0"
    extends Partials.Synchron3rdBase(redeclare replaceable record Data =
        PowerSystems.AC3ph.Machines.Parameters.Synchron3rd_pm);

    Modelica.Blocks.Interfaces.RealOutput phiRotor=
                      phi_el "rotor angle el"

  initial equation
    phi_el = phi_el_start;

  equation
    if par.excite==1 then
      assert(false, "machine-parameter must be excite = 2 (pm) or 3 (reluctance)");
    elseif par.excite==2 then
      psi_e = c.Psi_pm; // = par.psi_pm*(par.V_nom/c.omega_nom)
    elseif par.excite==3 then
      psi_e = 0;
    end if;
  end Synchron3rd_pm;

  model Synchron_pm "Synchronous machine pm, 3-phase dq0"
    extends Partials.SynchronBase(redeclare replaceable record Data =
        PowerSystems.AC3ph.Machines.Parameters.Synchron_pm);

    Modelica.Blocks.Interfaces.RealOutput phiRotor=
                      phi_el "rotor angle el"

  initial equation
    phi_el = phi_el_start;

  equation
    if par.excite==1 then
      assert(false, "machine-parameter must be excite = 2 (pm) or 3 (reluctance)");
    elseif par.excite==2 then
      psi_e = c.Psi_pm;  // = par.psi_pm*(par.V_nom/c.omega_nom)
      v_rd[1] = 0;
    elseif par.excite==3 then
      psi_e = 0;
      v_rd[1] = 0;
    end if;
  end Synchron_pm;

  model Synchron3rd_pm_ctrl
    "Synchronous machine, for field-oriented control, 3rd order model, 3-phase dq0"
    extends Partials.Synchron3rdBase(redeclare replaceable record Data =
        PowerSystems.AC3ph.Machines.Parameters.Synchron3rd_pm);

    Modelica.Blocks.Interfaces.RealOutput[2] i_meas(each final unit="1")
      "measured current {i_d, i_q} pu"
    Modelica.Blocks.Interfaces.RealInput[2] i_act(each final unit="1")
      "actuated current {i_d, i_q} pu"
    Modelica.Blocks.Interfaces.RealOutput phiRotor=phi_el "rotor angle el"
    Modelica.Blocks.Interfaces.RealOutput[2] vPhasor
      "desired {abs(u), phase(u)}"
  protected
    final parameter PS.Current I_nom=par.S_nom/par.V_nom;
    PS.Voltage[2] v_dq "voltage demand {v_d, v_q} pu";
    PS.Current[2] i_dq "current demand {i_d, i_q} pu";
  initial equation
    phi_el = phi_el_start;

  equation
    if par.excite==1 then
      assert(false, "machine-parameter must be excite = 2 (pm) or 3 (reluctance)");
    elseif par.excite==2 then
      psi_e = c.Psi_pm; // = par.psi_pm*(par.V_nom/c.omega_nom)
    elseif par.excite==3 then
      psi_e = 0;
    end if;

    i_meas = i_s[1:2]/I_nom;
    i_dq = i_act*I_nom;
    v_dq = w_el*{-(c.L_s[2]*i_dq[2]),c.L_s[1]*i_dq[1] + psi_e} + c.R_s*i_dq;
    vPhasor = {sqrt(v_dq*v_dq)/par.V_nom,atan2(v_dq[2], v_dq[1])};
  end Synchron3rd_pm_ctrl;

  model Synchron_pm_ctrl
    "Synchronous machine, for field-oriented control, 3-phase dq0"
    extends Partials.SynchronBase(redeclare replaceable record Data =
        PowerSystems.AC3ph.Machines.Parameters.Synchron_pm);

    Modelica.Blocks.Interfaces.RealOutput[2] i_meas(
                             each final unit="1") "measured current {i_d, i_q} pu"
    Modelica.Blocks.Interfaces.RealInput[2] i_act(
                             each final unit="1") "actuated current {i_d, i_q} pu"
    Modelica.Blocks.Interfaces.RealOutput phiRotor=
                      phi_el "rotor angle el"
    Modelica.Blocks.Interfaces.RealOutput[2] vPhasor
      "desired {abs(u), phase(u)}"
  protected
    final parameter PS.Current I_nom=par.S_nom/par.V_nom;
    PS.Voltage[2] v_dq "voltage demand {v_d, v_q} pu";
    PS.Current[2] i_dq "current demand {i_d, i_q} pu";
  initial equation
    phi_el = phi_el_start;

  equation
    if par.excite==1 then
      assert(false, "machine-parameter must be excite = 2 (pm) or 3 (reluctance)");
    elseif par.excite==2 then
      psi_e = c.Psi_pm;  // = par.psi_pm*(par.V_nom/c.omega_nom)
      v_rd[1] = 0;
    elseif par.excite==3 then
      psi_e = 0;
      v_rd[1] = 0;
    end if;

    i_meas = i_s[1:2]/I_nom;
    i_dq = i_act*I_nom;
    v_dq = w_el*{-c.L_s[2]*i_dq[2],c.L_s[1]*i_dq[1] + c.L_md[1]*i_rd[1] + psi_e}
       + c.R_s*i_dq;
    vPhasor = {sqrt(v_dq*v_dq)/par.V_nom,atan2(v_dq[2], v_dq[1])};
  end Synchron_pm_ctrl;

  package Partials "Partial models"
    extends Modelica.Icons.BasesPackage;

    partial model ACmachine "AC machine base, 3-phase dq0"
      extends Ports.YDport_p(i(start = i_start));

      parameter Types.Dynamics dynType=system.dynType "transient or steady-state model"
      parameter PS.Current[3] i_start = zeros(3)
        "start value of current conductor"
      parameter SI.Angle phi_el_start=0 "initial rotor angle electric"
      parameter Integer pp=1 "pole-pair number";
      SI.Angle phi_el(stateSelect=StateSelect.prefer, start=phi_el_start)
        "rotor angle electric (syn: +pi/2)";
      SI.AngularVelocity w_el "rotor angular velocity el";
      SI.Torque tau_el "electromagnetic torque";
      Interfaces.Rotation_n airgap "electro-mechanical connection"
      Interfaces.ThermalV_n heat(m=2) "heat source port {stator, rotor}"
    protected
      outer System system;
      SI.AngularFrequency[2] omega;
      function atan2 = Modelica.Math.atan2;

    equation
      omega = der(term.theta);
      pp*airgap.phi = phi_el;
      airgap.tau = -pp*tau_el;
      w_el = der(phi_el);
    end ACmachine;

    partial model AsynchronBase "Asynchronous machine base, 3-phase dq0"
      extends ACmachine(final pp=par.pp, v(start={cos(system.alpha0),sin(system.alpha0),0}*par.V_nom));

      output Real slip "<0: motor, >0: generator";
      replaceable record Data =
        PowerSystems.AC3ph.Machines.Parameters.Asynchron(f_nom=system.f_nom)
        "machine parameters"
      final parameter Data par "machine parameters"
    protected
      final parameter Integer n_r = par.n_r
        "number of rotor circuits d- and q-axis";
      final parameter Coefficients.Asynchron c = Utilities.Precalculation.machineAsyn(
                                                                                par, top.scale);
      parameter SI.Inductance L_m[n_r] = c.L_m;
      parameter SI.Resistance R_r[n_r] = c.R_r;
      parameter SI.Resistance R_m[n_r] = c.R_m;
      parameter SI.Inductance L_r[n_r,n_r] = c.L_r;

      parameter PS.Current[n_r] i_rd_start = zeros(n_r)
        "start value of rotor current d_axis"
      parameter PS.Current[n_r] i_rq_start = zeros(n_r)
        "start value of rotor current q_axis"

      PS.Voltage[n_r] v_rd=zeros(n_r) "rotor voltage d_axis, cage-rotor = 0";
      PS.Voltage[n_r] v_rq=zeros(n_r) "rotor voltage q_axis, cage-rotor = 0";
      PS.Current[n_r] i_rd(start = i_rd_start) "rotor current d_axis";
      PS.Current[n_r] i_rq(start = i_rq_start) "rotor current q_axis";
      SI.MagneticFlux[2] psi_s "magnetic flux stator dq";
      SI.MagneticFlux[n_r] psi_rd "magnetic flux rotor d";
      SI.MagneticFlux[n_r] psi_rq "magnetic fluxrotor q";

    initial equation
      if dynType == Types.Dynamics.SteadyInitial then
        der(psi_s) = omega[1]*{-psi_s[2], psi_s[1]};
        der(i[3]) = 0;
        der(psi_rd) = omega[1]*(-psi_rq);
        der(psi_rq) = omega[1]*psi_rd;
      elseif dynType <> Types.Dynamics.SteadyState then
        der(psi_rd) = omega[1]*(-psi_rq);
        der(psi_rq) = omega[1]*psi_rd;
      end if;

    equation
      psi_s = diagonal(c.L_s[1:2])*i[1:2] + {L_m*i_rd, L_m*i_rq};
      psi_rd = L_m*i[1] + L_r*i_rd;
      psi_rq = L_m*i[2] + L_r*i_rq;

      if dynType <> Types.Dynamics.SteadyState then
        der(psi_s) + omega[2]*{-psi_s[2], psi_s[1]} + c.R_s*i[1:2] = v[1:2];
        c.L_s[3]*der(i[3]) + c.R_s*i[3] = v[3];
        der(psi_rd) + (omega[2] - w_el)*(-psi_rq) + diagonal(R_r)*i_rd = v_rd;
        der(psi_rq) + (omega[2] - w_el)*psi_rd + diagonal(R_r)*i_rq = v_rq;
      else
        omega[2]*{-psi_s[2], psi_s[1]} + c.R_s*i[1:2] = v[1:2];
        c.R_s*i[3] = v[3];
        (omega[2] - w_el)*(-psi_rq) + diagonal(R_r)*i_rd = v_rd;
        (omega[2] - w_el)*psi_rd + diagonal(R_r)*i_rq = v_rq;
      end if;

      if par.neu_iso then
        i_n = zeros(top.n_n);
      else
        v_n = c.R_n*i_n "equation neutral to ground (relevant if Y-topology)";
      end if;

      slip = (w_el/sum(omega) - 1);
      tau_el = i[1:2]*{-psi_s[2], psi_s[1]};
      heat.ports.Q_flow = -{c.R_s*i*i, diagonal(R_r)*i_rd*i_rd + diagonal(R_r)*i_rq*i_rq};
    end AsynchronBase;

    partial model SynTransform "Rotation transform dq"
      extends ACmachine;
      import Modelica.Math.Matrices.inv;

      PS.Voltage[3] v_s "stator voltage dq0 in rotor-system";
      PS.Current[3] i_s(each stateSelect=StateSelect.prefer, start = i_s_start)
        "stator current dq0 in rotor-system";

    protected
      parameter PS.Current[3] i_s_start = cat(1, inv(Utilities.Transforms.rotation_dq(
                                                                                phi_el_start))*i_start[1:2], {i_start[3]})
        "start value of stator current dq0 in rotor-system";
      SI.MagneticFlux psi_e "excitation flux";
      Real[2,2] Rot_dq "Rotation reference-dq0 to rotor-dq0 system";

    initial equation
      if dynType == Types.Dynamics.FixedInitial then
        // initialize i_s from i_start
        i_start = cat(1, Rot_dq*i_s[1:2], {i_s[3]});
      end if;

    equation
      Rot_dq =Utilities.Transforms.rotation_dq(phi_el - term.theta[2]);
      v_s = cat(1, transpose(Rot_dq)*v[1:2], {v[3]});
      i = cat(1, Rot_dq*i_s[1:2], {i_s[3]});
    end SynTransform;

    partial model Synchron3rdBase "Synchronous machine 3rd base, 3-phase dq0"
      extends SynTransform(final pp=par.pp,  v(start={cos(system.alpha0),sin(system.alpha0),0}*par.V_nom));

      replaceable record Data =
        PowerSystems.AC3ph.Machines.Parameters.Synchron3rd(f_nom=system.f_nom)
        "machine parameters"
      final parameter Data par "machine parameters"
    protected
      final parameter Coefficients.Synchron3rd c = Utilities.Precalculation.machineSyn3rd(
                                                                                par,top.scale);
      SI.MagneticFlux[2] psi_s "magnetic flux stator dq";

    initial equation
      if dynType == Types.Dynamics.SteadyInitial then
        der(psi_s) = zeros(2);
        der(c.L_s[3]*i_s[3]) = 0;
      end if;

    equation
      psi_s = {c.L_s[1]*i_s[1] + psi_e, c.L_s[2]*i_s[2]};

      if dynType <> Types.Dynamics.SteadyState then
        der(psi_s) + w_el*{-psi_s[2], psi_s[1]} + c.R_s*i_s[1:2] = v_s[1:2];
        c.L_s[3]*der(i_s[3]) + c.R_s*i_s[3] = v_s[3];
      else
        w_el*{-psi_s[2], psi_s[1]} + c.R_s*i_s[1:2] = v_s[1:2];
        c.R_s*i_s[3] = v_s[3];
      end if;

      if par.neu_iso then
        i_n = zeros(top.n_n);
      else
        v_n = c.R_n*i_n "equation neutral to ground (relevant if Y-topology)";
      end if;

      tau_el = i_s[1:2]*{-psi_s[2], psi_s[1]};
      heat.ports.Q_flow = -{c.R_s*i_s*i_s, 0};
    end Synchron3rdBase;

    partial model SynchronBase "Synchronous machine base, 3-phase dq0"
      extends SynTransform(final pp=par.pp,v(start={cos(system.alpha0),sin(system.alpha0),0}*par.V_nom));

      replaceable record Data =
        PowerSystems.AC3ph.Machines.Parameters.Synchron(f_nom=system.f_nom)
        "machine parameters"
      final parameter Data par "machine parameters"
    protected
      final parameter Integer n_d = par.n_d "number of rotor circuits d-axis";
      final parameter Integer n_q = par.n_q "number of rotor circuits q-axis";
      final parameter Coefficients.Synchron c = Utilities.Precalculation.machineSyn(
                                                                                par, top.scale);
      parameter SI.Inductance[n_d, n_d] L_rd = c.L_rd "L matrix rotor";
      parameter SI.Inductance[n_q, n_q] L_rq = c.L_rq "L matrix rotor";
      parameter SI.Inductance[n_d] L_md = c.L_md "L matrix mutual d-axis";
      parameter SI.Inductance[n_q] L_mq = c.L_mq "L matrix mutual q-axis";
      parameter SI.Resistance[n_d] R_rd = c.R_rd "R matrix rotor";
      parameter SI.Resistance[n_q] R_rq = c.R_rq "R matrix rotor";

      PS.Voltage[n_d] v_rd(start=ones(n_d))
        "rotor voltage d-axis in rotor-system";
      PS.Voltage[n_q] v_rq "rotor voltage q-axis in rotor-system";
      PS.Current[n_d] i_rd(each stateSelect=StateSelect.prefer)
        "rotor current d-axis in rotor-system";
      PS.Current[n_q] i_rq(each stateSelect=StateSelect.prefer)
        "rotor current q-axis in rotor-system";
      SI.MagneticFlux[2] psi_s "magnetic flux stator dq";
      SI.MagneticFlux[n_d] psi_rd "magnetic flux rotor d";
      SI.MagneticFlux[n_q] psi_rq "magnetic fluxrotor q";
      PS.Voltage v_f "field voltage (not scaled to stator units)";
      PS.Current i_f "field current (not scaled to stator units)";

    initial equation
      if dynType == Types.Dynamics.SteadyInitial then
        der(psi_s) = zeros(2);
        der(c.L_s[3]*i_s[3]) = 0;
        der(psi_rd) = zeros(n_d);
        der(psi_rq) = zeros(n_q);
      elseif dynType <> Types.Dynamics.SteadyState then
        der(psi_rd) = zeros(n_d);
        der(psi_rq) = zeros(n_q);
      end if;

    equation
      v_rd[2:end] = zeros(n_d - 1);
      v_rq = zeros(n_q);
      psi_s = diagonal(c.L_s[1:2])*i_s[1:2] + {L_md*i_rd + psi_e, L_mq*i_rq};
      psi_rd = L_md*i_s[1] + L_rd*i_rd;
      psi_rq = L_mq*i_s[2] + L_rq*i_rq;
      v_f = v_rd[1]/c.wf;
      i_f = i_rd[1]*c.wf;

      if dynType <> Types.Dynamics.SteadyState then
        der(psi_s) + w_el*{-psi_s[2], psi_s[1]} + c.R_s*i_s[1:2] = v_s[1:2];
        c.L_s[3]*der(i_s[3]) + c.R_s*i_s[3] = v_s[3];
        der(psi_rd) + diagonal(R_rd)*i_rd = v_rd;
        der(psi_rq) + diagonal(R_rq)*i_rq = v_rq;
      else
        w_el*{-psi_s[2], psi_s[1]} + c.R_s*i_s[1:2] = v_s[1:2];
        c.R_s*i_s[3] = v_s[3];
        diagonal(R_rd)*i_rd = v_rd;
        diagonal(R_rq)*i_rq = v_rq;
      end if;

      if par.neu_iso then
        i_n = zeros(top.n_n);
      else
        v_n = c.R_n*i_n "equation neutral to ground (relevant if Y-topology)";
      end if;

      tau_el = i_s[1:2]*{-psi_s[2], psi_s[1]};
      heat.ports.Q_flow = -{c.R_s*i_s*i_s, diagonal(R_rd)*i_rd*i_rd + diagonal(R_rq)*i_rq*i_rq};
    end SynchronBase;

  end Partials;

  package Control
    extends Modelica.Icons.Package;

  model Excitation "Excitation (electric part)"
    extends Ports.Port_p;

    parameter PS.Voltage V_nom=1 "nom voltage armature"
    parameter PS.Voltage Vf_nom=1 "nom voltage field-winding"

    Modelica.Blocks.Interfaces.RealOutput[3] termVoltage(each final unit="1")
        "terminal voltage pu to exciter control"
    Modelica.Blocks.Interfaces.RealInput fieldVoltage(final unit="1")
        "field voltage pu from exciter control"
    AC1ph_DC.Ports.TwoPin_n field "to generator field-winding"

  equation
    term.i = zeros(3);
    termVoltage = term.v/V_nom;
    field.v = {fieldVoltage*Vf_nom, 0};
  end Excitation;

  model PowerAngle "Direct determination of generator power angle"
    extends Ports.Port_p;
    outer System system;

    parameter Integer pp=1 "pole-pair number";
    parameter SI.Angle delta=0 "power angle";
    parameter SI.AngularFrequency gamma=1 "inverse time constant";
    SI.Angle phi_el(start=delta-pi/2+system.alpha0) "rotor angle";
    Interfaces.Rotation_n airgap(     phi(start=(delta-pi/2+system.alpha0)/pp))
        "to airgap of generator"
    protected
    function atan2 = Modelica.Math.atan2;

  equation
    term.i = zeros(3);
    pp*airgap.phi = phi_el;

    phi_el = delta + atan2(-term.v[1], term.v[2]) + term.theta[2]; // steady state!
  //  der(phi_el) = gamma*(delta + atan2(-term.v[1], term.v[2]) + term.theta[2] - phi_el);
  end PowerAngle;
  end Control;

package Parameters "Parameter data for interactive use"

  extends Modelica.Icons.MaterialPropertiesPackage;

  record Asynchron3rd "Asynchronous machine 3rd order parameters"
    extends PowerSystems.Common.Nominal.NominalDataAC;

    Boolean neu_iso "isolated neutral if Y"
    Integer pp "pole-pair number"

    SIpu.Reactance x "total reactance d- and q-axis"
    SIpu.Reactance x_0 "reactance 0-axis"
    SIpu.Resistance r_s "resistance stator"
    SIpu.Resistance r_n "resistance neutral to grd (if Y)"
  end Asynchron3rd;

  record Asynchron "Asynchronous machine parameters"
    extends Asynchron3rd;

    parameter Integer n_r(min=0) "# rotor circuits d- and q-axis";
    Boolean transDat "use transient data"
    Boolean use_xtr "use x_transient and t_closed"
    SIpu.Reactance[n_r] xtr "transient reactance {xtr', xtr'', ..}"
    SI.Time[n_r] tc "time constant closed-loop {tc', tc'', ..}"
    SI.Time[n_r] to "time constant open-loop {to', to'', ..}"

    SIpu.Reactance xsig_s "leakage reactance stator"
    SIpu.Reactance[n_r] xsig_r "leakage reactance rotor"
    SIpu.Resistance[n_r] r_r "resistance rotor"
  end Asynchron;

  record Asynchron3rd_cage "Asynchronous machine 3rd order parameters"
    extends Asynchron3rd(
      neu_iso=false,
      pp=1,
      x=4,
      x_0=0.1,
      r_s=0.04,
      r_n=1);
  end Asynchron3rd_cage;

  record Asynchron_cage "Asynchronous machine parameters"
    extends Asynchron(
      neu_iso=false,
      pp=1,
      x=4,
      x_0=0.1,
      r_s=0.04,
      r_n=1,
      n_r=1,
      transDat=true,
      use_xtr=true,
      xtr={0.4844},
      tc={0.03212},
      to={0.2653},
      xsig_s=0.25,
      xsig_r={0.25},
      r_r={0.04});
  end Asynchron_cage;

  record Synchron3rd "Synchronous machine 3rd order parameters"
    extends PowerSystems.Common.Nominal.NominalDataAC;

    Boolean neu_iso "isolated neutral if Y"
    Integer pp "pole-pair number"
    Integer excite(min=0,max=3) "excitation (1:el, 2:pm, 3:reluctance)"
    SIpu.MagneticFlux psi_pm
        "magnetisation (V/V_nom at open term at omega_nom)"

    SIpu.Reactance x_d "syn reactance d-axis"
    SIpu.Reactance x_q "syn reactance q-axis"
    SIpu.Reactance x_0 "reactance 0-axis"
    SIpu.Resistance r_s "resistance armature"
    SIpu.Resistance r_n "resistance neutral to grd (if Y)"
  end Synchron3rd;

  record Synchron "Synchronous machine parameters"
    extends Synchron3rd;

    parameter Integer n_d(min=1) "# rotor circuits d-axis";
    parameter Integer n_q(min=0) "# rotor circuits q-axis";

    Boolean transDat "use transient data?"
    Boolean use_xtr "use x_transient and t_closed?"
    SIpu.Reactance[n_d] xtr_d "trans reactance d-axis {xtr_d', xtr_d'', ..}"
    SIpu.Reactance[n_q] xtr_q "trans reactance q-axis {xtr_q', xtr_q'', ..}"
    SI.Time[n_d] tc_d "time constant closed-loop d-axis {tc_d', tc_d'', ..}"
    SI.Time[n_q] tc_q "time constant closed-loop q-axis {tc_q', tc_q'', ..}"
    SI.Time[n_d] to_d "time constant open-loop d-axis {to_d', to_d'', ..}"
    SI.Time[n_q] to_q "time constant open-loop q-axis {to_q', to_q'', ..}"
    Boolean use_if0 "induced field current and phase available?"
    SIpu.Current if0 "induced field current at v_s=Vnom/0deg"
    SI.Angle alpha_if0 "angle(if0) at v_s=Vnom/0deg (sign: i_f behind v_s)"
    Real tol "tolerance precalculation"

    SIpu.Reactance xsig_s "leakage reactance armature"
    SIpu.Reactance[n_d] xsig_rd "leakage reactance rotor d-axis {f, D, ..}"
    SIpu.Reactance[n_q] xsig_rq "leakage reactance rotor q-axis {Q1, ..}"
    SIpu.Reactance[n_d-1] xm_d "coupling-reactance d-axis {xm1, ..}"
    SIpu.Resistance[n_d] r_rd "resistance rotor d-axis {f, D, ..}"
    SIpu.Resistance[n_q] r_rq "resistance rotor q-axis {Q1, ..}"

    SI.Current If_nom "nom field current (V=V_nom at open term)"
  end Synchron;

  record Synchron3rd_ee "Synchronous machine 3rd order parameters"
    extends Synchron3rd(
      neu_iso=false,
      pp=1,
      final excite=1,
      final psi_pm=0,
      x_d=1.9,
      x_q=1.77,
      x_0=0.1,
      r_s=0.005,
      r_n=1);
  end Synchron3rd_ee;

  record Synchron_ee "Synchronous machine parameters"
    extends Synchron(
      neu_iso=false,
      pp=1,
      final excite=1,
      final psi_pm=0,
      x_d=1.9,
      x_q=1.77,
      x_0=0.1,
      r_s=0.005,
      r_n=1,
      n_d=2,
      n_q=2,
      transDat=true,
      use_xtr=true,
      xtr_d={0.33,0.25},
      xtr_q={0.44,0.27},
      tc_d={0.86, 0.025},
      tc_q={0.25, 0.04},
      to_d={4.9898, 0.032747},
      to_q={1.0867, 0.060327},
      use_if0=true,
      if0=0.85,
      alpha_if0=-1.7453292519943,
      tol=1e-6,
      xsig_s=0.17,
      xsig_rd={0.135194,0.0365214},
      xsig_rq={0.407386,0.144502},
      xm_d={0.0555125},
      r_rd={1.32139e-3, 14.376e-3},
      r_rq={7.38411e-3, 19.7148e-3},
      If_nom=1);

  //  example: V_nom=20e3, S_nom=500e6, If_nom=1500.
  end Synchron_ee;

  record Synchron3rd_pm "Synchronous machine pm 3rd order parameters"
    extends Synchron3rd(
      neu_iso=false,
      pp=2,
      final excite=2,
      psi_pm=1.2,
      x_d=0.4,
      x_q=0.4,
      x_0=0.1,
      r_s=0.05,
      r_n=1);
  end Synchron3rd_pm;

  record Synchron_pm "Synchronous machine pm parameters"
    extends Synchron(
      neu_iso=false,
      pp=2,
      final excite=2,
      psi_pm=1.2,
      x_d=0.4,
      x_q=0.4,
      x_0=0.1,
      r_s=0.05,
      r_n=1,
      n_d=1,
      n_q=1,
      transDat=true,
      use_xtr=true,
      xtr_d={0.142857},
      xtr_q={0.142857},
      tc_d={0.00994718},
      tc_q={0.00994718},
      to_d={0.0278521},
      to_q={0.0278521},
      use_if0=false,
      if0=0,
      alpha_if0=0,
      tol=1e-6,
      xsig_s=0.1,
      xsig_rd={0.05},
      xsig_rq={0.05},
      xm_d=fill(0, 0),
      r_rd={0.04},
      r_rq={0.04},
      If_nom=0);
  end Synchron_pm;

  record Synchron3rd_reluctance "Synchronous machine pm 3rd order parameters"
    extends Synchron3rd(
      neu_iso=false,
      pp=2,
      final excite=3,
      final psi_pm=0,
      x_d=2.0,
      x_q=0.6,
      x_0=0.1,
      r_s=0.05,
      r_n=1);
  end Synchron3rd_reluctance;
end Parameters;

package Coefficients "Coefficient matrices of machine equations"
  extends Modelica.Icons.MaterialPropertiesPackage;

record Asynchron "Coefficient matrices of asynchronous machine"
  extends Modelica.Icons.Record;

  parameter Integer n_r "number of rotor circuits";
  SI.Inductance[3] L_s "L matrix stator dq0, d=q";
  SI.Inductance[n_r, n_r] L_r "L matrix rotor";
  SI.Inductance[n_r] L_m "L matrix mutual";
  SI.Resistance R_s "R matrix stator";
  SI.Resistance[n_r] R_r "R matrix rotor";
  SI.Resistance R_n "resistance neutral to grd (if Y)";
  SI.Resistance[n_r] R_m "= diagonal(R_r)*inv(L_r)*L_m";
end Asynchron;

record Synchron3rd "Coefficient matrices of synchronous machine, 3rd order"
  extends Modelica.Icons.Record;

  SI.Inductance[3] L_s "L matrix stator dq0";
  SI.Resistance R_s "R stator (armature)";
  SI.Resistance R_n "resistance neutral to grd (if Y)";
  SI.MagneticFlux Psi_pm "flux permanent magnet";
  SI.AngularFrequency omega_nom;
end Synchron3rd;

record Synchron "Coefficient matrices of synchronous machine"
  extends Modelica.Icons.Record;

  parameter Integer n_d "number of rotor circuits d-axis";
  parameter Integer n_q "number of rotor circuits q-axis";
  SI.Inductance[3] L_s "L matrix stator dq0";
  SI.Inductance[n_d, n_d] L_rd "L matrix rotor";
  SI.Inductance[n_q, n_q] L_rq "L matrix rotor";
  SI.Inductance[n_d] L_md "L matrix mutual d-axis";
  SI.Inductance[n_q] L_mq "L matrix mutual q-axis";
  SI.Resistance R_s "R stator (armature)";
  SI.Resistance[n_d] R_rd "R matrix rotor";
  SI.Resistance[n_q] R_rq "R matrix rotor";
  SI.Resistance R_n "resistance neutral to grd (if Y)";
  SI.MagneticFlux Psi_pm "flux permanent magnet";
  Real wf "ratio field winding";
  SI.Voltage Vf_nom "nom voltage field winding";
  SI.AngularFrequency omega_nom;
end Synchron;
end Coefficients;
end Machines;


Nodes and adaptors

within PowerSystems.AC3ph;
package Nodes "Nodes and adaptors"
  extends Modelica.Icons.VariantsPackage;

  model Ground "AC Ground, 3-phase dq0"
    extends Ports.Port_p;

  equation
    term.v = zeros(PS.n);
  end Ground;

  model GroundOne "Ground, one conductor"

    Interfaces.Electric_p term "positive scalar terminal"

  equation
    term.v = 0;
  end GroundOne;

  model BusBar "Busbar, 3-phase dq0"
    extends Ports.PortBase;

    output PS.Voltage v_norm(stateSelect=StateSelect.never);
    output SI.Angle alpha_v(stateSelect=StateSelect.never);
    Ports.ACdq0_p term "bus bar"
  protected
    Real[2,2] R=Utilities.Transforms.rotation_dq(term.theta[1]);
    function atan2 = Modelica.Math.atan2;

  equation
    term.i = zeros(PS.n);
    v_norm = sqrt(term.v*term.v);
    alpha_v = atan2(R[:, 2]*term.v[1:2], R[:, 1]*term.v[1:2]);
  end BusBar;

/*
  model Y_Delta "Y_Delta switch, 3-phase dq0"
    extends Ports.PortBase;

    Ports.ACdq0_p term_p
      "connect to non source side of windings"
    Ports.ACdq0_n term_n
      "connect to source side of windings"
    Interfaces.ElectricV_n switchY_D(     final m=3)
      "connect to switch 'commute' position"
    Interfaces.ElectricV_p switchD(     final m=3)
      "connect to switch 'Delta' position"
    Interfaces.ElectricV_p switchY(     final m=3)
      "connect to switch 'Y' position"
    Interfaces.Electric_n neutral "neutral Y"
  protected
    Real[3,3] Park = Basic.Transforms.park(
                                          term_p.theta[2]);

  equation
    switchY_D.pin.v = transpose(Park)*term_p.v;
    term_p.i + Park*switchY_D.pin.i = zeros(3);
    switchY.pin.v = fill(neutral.v, 3);
    sum(switchY.pin.i) + neutral.i = 0;
    switchD.pin[{3,1,2}].v = transpose(Park)*term_n.v;
    term_n.i + Park*switchD.pin[{3,1,2}].i = zeros(3);
  end Y_Delta;
*/

  model Ynode "Y-node with neutral-access, 3-phase dq0"
    extends Ports.Port_p;

    Interfaces.Electric_n neutral "neutral Y"

  equation
    term.v[3] = sqrt(3)*neutral.v;
    term.i[1:2] = {0, 0};
    neutral.i + sqrt(3)*term.i[3] = 0;
  end Ynode;

  model ResistiveGround "Y-node with neutral-access, 3-phase dq0"
    extends Ports.Yport_p;
    extends Common.Nominal.Nominal;

    parameter SIpu.Resistance r_n=0 "resistance neutral to grd";
  protected
    final parameter Real R_base=Utilities.Precalculation.baseR(
          puUnits,
          V_nom,
          S_nom);
    final parameter SI.Resistance R_n=r_n*R_base;

  equation
    v = zeros(3);
    R_n*i_n = v_n "equation neutral to ground";
  end ResistiveGround;

  model InductiveGround "Y-node with neutral-access, 3-phase dq0"
    extends Ports.Yport_p;
    extends Common.Nominal.NominalAC;

    parameter Types.Dynamics dynType=system.dynType "transient or steady-state model"

    parameter SIpu.Reactance x_n=1 "reactance neutral to grd";
    parameter SIpu.Resistance r_n=0 "resistance neutral to grd";
  protected
    final parameter Real R_base=Utilities.Precalculation.baseR(
          puUnits,
          V_nom,
          S_nom);
    final parameter SI.Inductance L_n=x_n*R_base/(2*pi*f_nom);
    final parameter SI.Resistance R_n=r_n*R_base;

  initial equation
    if dynType == Types.Dynamics.SteadyInitial then
      der(i_n) = 0;
    end if;

  equation
    v = zeros(3);
    if dynType <> Types.Dynamics.SteadyState then
      L_n*der(i_n) + R_n*i_n = v_n;
    else
      R_n*i_n = v_n;
    end if;
  end InductiveGround;

  model CapacitiveGround "Y-node with neutral-access, 3-phase dq0"
    extends Ports.Yport_p;
    extends Common.Nominal.NominalAC;

    parameter Types.Dynamics dynType=system.dynType "transient or steady-state model"

    parameter SIpu.Susceptance b_n=1 "susceptance neutral to grd";
    parameter SIpu.Conductance g_n=0 "conductance neutral to grd";
  protected
    final parameter Real R_base=Utilities.Precalculation.baseR(
          puUnits,
          V_nom,
          S_nom);
    final parameter SI.Capacitance C_n=b_n/(R_base*2*pi*f_nom);
    final parameter SI.Conductance G_n=g_n/R_base;

  initial equation
    if dynType == Types.Dynamics.SteadyInitial then
      der(i_n) = 0;
    end if;

  equation
    v = zeros(3);
    if dynType <> Types.Dynamics.SteadyState then
      C_n*der(v_n) + G_n*v_n = i_n;
    else
      G_n*v_n = i_n;
    end if;
  end CapacitiveGround;

model Y_OnePhase "Terminator, ACdq0 to an, bn, cn"
  extends Ports.Port_p;
  extends Icons.Adaptor_dq0;

  Interfaces.Electric_p neutral "neutral Y"
  AC1ph_DC.Ports.TwoPin_n plug_a "phase a and neutral"
  AC1ph_DC.Ports.TwoPin_n plug_b "phase b and neutral"
  AC1ph_DC.Ports.TwoPin_n plug_c "phase c and neutral"
  protected
    Real[3,3] P=Utilities.Transforms.park(term.theta[2]);

equation
  plug_a.v = cat(1, transpose(P[:, 1:1])*term.v, {neutral.v});
  plug_b.v = cat(1, transpose(P[:, 2:2])*term.v, {neutral.v});
  plug_c.v = cat(1, transpose(P[:, 3:3])*term.v, {neutral.v});
  term.i + P*{plug_a.i[1], plug_b.i[1], plug_c.i[1]} = zeros(3);
end Y_OnePhase;

model ACdq0_a_b_c "Adaptor ACdq0 to pins a, b, c"
  extends Ports.Port_p;
  extends PowerSystems.Icons.Adaptor_dq0;

  Interfaces.Electric_n term_a "phase a"
  Interfaces.Electric_n term_b "phase b"
  Interfaces.Electric_n term_c "phase c"
  protected
    Real[3,3] P=Utilities.Transforms.park(term.theta[2]);

equation
  {term_a.v,term_b.v,term_c.v} = transpose(P)*term.v;
  term.i + P*{term_a.i,term_b.i,term_c.i} = zeros(3);
end ACdq0_a_b_c;

/*
model ACdq0_abc "Adaptor ACdq0 to 3-vector abc"
  extends Ports.Port_p;
  extends Basic.Icons.Adaptor_dq0;

  Interfaces.ElectricV_n term_abc(     final m=3) "phase abc vector-pin"
  protected
  Real[3,3] P = Basic.Transforms.park(
                                     term.theta[2]);

equation
  term_abc.pin.v = transpose(P)*term.v;
  term.i + P*term_abc.pin.i = zeros(3);
end ACdq0_abc;
*/

  model DefReference "Defines reference frame, 3phase dq0"
    extends Ports.PortBase;
    outer System system;

    Ports.ACdq0_p term "bus bar"
    Modelica.Blocks.Interfaces.RealInput theta "absolute angle"

  equation
    term.i = zeros(PS.n);
    Connections.potentialRoot(term.theta);
    if Connections.isRoot(term.theta) then
      term.theta = if system.synRef then {0, theta} else {theta, 0};
    end if;
  end DefReference;

  model Break "Breaks transmission of term.theta, 3phase dq0"
    extends Ports.PortBase;

    Ports.ACdq0_p term_p "positive terminal"
    Ports.ACdq0_n term_n "negative terminal"

  equation
    term_p.v = term_n.v;
    term_p.i + term_n.i = zeros(PS.n);
  end Break;
end Nodes;


within PowerSystems;
package AC3ph "AC three phase components from Spot ACdq0"
  extends Modelica.Icons.VariantsPackage;

  replaceable package PackagePhaseSystem = PhaseSystems.ThreePhase_dq0
    "Default phase system for AC3ph"

end AC3ph;


AC three-phase ports dq0 representation

within PowerSystems.AC3ph;
package Ports "AC three-phase ports dq0 representation"
  extends Modelica.Icons.InterfacesPackage;

partial model PortBase "base model adapting Spot to PowerSystems"
  package PS = PackagePhaseSystem;
  function j = PS.j
  function jj = PS.jj
  function j_dq0 = PhaseSystems.ThreePhase_dq0.j
  function jj_dq0 = PhaseSystems.ThreePhase_dq0.jj
end PortBase;

connector ACdq0_p "AC terminal, 3-phase dq0 ('positive')"
  extends Interfaces.Terminal(redeclare package PhaseSystem =
          PackagePhaseSystem);
end ACdq0_p;

connector ACdq0_n "AC terminal, 3-phase dq0 ('negative')"
  extends Interfaces.Terminal(redeclare package PhaseSystem =
          PackagePhaseSystem);
end ACdq0_n;

partial model Port_p "AC one port 'positive', 3-phase"
  extends PortBase;

  Ports.ACdq0_p term "positive terminal"
end Port_p;

partial model Port_n "AC one port 'negative', 3-phase"
  extends PortBase;

  Ports.ACdq0_n term "negative terminal"
end Port_n;

partial model Port_f "AC one port 'fault', 3-phase"
  extends PortBase;

  Ports.ACdq0_p term "fault terminal"
end Port_f;

partial model Port_p_n "AC two port, 3-phase"
  extends PortBase;

  Ports.ACdq0_p term_p "positive terminal"
  Ports.ACdq0_n term_n "negative terminal"
equation
  Connections.branch(term_p.theta, term_n.theta);
  term_n.theta = term_p.theta;
end Port_p_n;

partial model Port_pn "AC two port 'current_in = current_out', 3-phase"
  extends Port_p_n;

equation
  term_p.i + term_n.i = zeros(PS.n);
end Port_pn;

partial model Port_p_n_f "AC three port, 3-phase"
  extends Port_p_n;

  Ports.ACdq0_n term_f "fault terminal"

equation
  Connections.branch(term_p.theta, term_f.theta);
  term_f.theta = term_p.theta;
end Port_p_n_f;

partial model Yport_p "AC one port Y topology 'positive'"
  extends Port_p;

  PS.Voltage[PS.n] v "voltage terminal to neutral";
  PS.Current[PS.n] i "current terminal in";
  PS.Voltage v_n(final start=0) "voltage neutral";
  PS.Current i_n(final start=0) "current neutral";

equation
  v = term.v - PS.map({0, 0, sqrt(3)*v_n});
  term.i = i;
  if PS.n > 2 then
    i_n = sqrt(3)*term.i[3];
  else
    i_n = 0;
  end if;
end Yport_p;

partial model Yport_n "AC one port Y topology 'negative'"
  extends Port_n;

  PS.Voltage[PS.n] v "voltage terminal to neutral";
  PS.Current[PS.n] i "current terminal in";
  PS.Voltage v_n(final start=0) "voltage neutral";
  PS.Current i_n(final start=0) "current neutral";

equation
  v = term.v - PS.map({0, 0, sqrt(3)*v_n});
  term.i = i;
  if PS.n > 2 then
    i_n = sqrt(3)*term.i[3];
  else
    i_n = 0;
  end if;
end Yport_n;

partial model YDport_p "AC one port Y or Delta topology 'positive'"
  extends Port_p;

/*
  replaceable Topology.Y top "Y or Delta topology"
*/

  replaceable model Topology_p = Topology.Y constrainedby Topology.TopologyBase
      "Y or Delta topology"
  Topology_p top(v_cond=v, i_cond=i, v_n=v_n)

  PS.Voltage[PS.n] v "voltage conductor";
  PS.Current[PS.n] i "current conductor";
  PS.Voltage[n_n] v_n "voltage neutral";
  PS.Current[n_n] i_n=top.i_n "current neutral to ground";
  protected
  final parameter Integer n_n=top.n_n

equation
  term.v = top.v_term;
  term.i = top.i_term;
end YDport_p;

partial model YDport_n "AC one port Y or Delta topology 'positive'"
  extends Port_n;

  replaceable model Topology_n = Topology.Y constrainedby Topology.TopologyBase
      "Y or Delta topology"
  Topology_n top(v_cond=v, i_cond=i, v_n=v_n) "Y or Delta topology"
  PS.Voltage[PS.n] v "voltage conductor";
  PS.Current[PS.n] i "current conductor";
  PS.Voltage[n_n] v_n "voltage neutral";
  PS.Current[n_n] i_n=top.i_n "current neutral to ground";
  protected
  final parameter Integer n_n=top.n_n

equation
  term.v = top.v_term;
  term.i = top.i_term;
end YDport_n;

partial model Y_Dport_p "AC two port, switcheable Y-Delta topology"
  extends Port_p;

  replaceable model Topology_p = Topology.Y_Delta constrainedby
      Topology.TopologyBase "Y-Delta switcheable"
  Topology_p top(v_cond=v, i_cond=i, control=YDcontrol) "Y-Delta switcheable"
  PS.Voltage[PS.n] v "voltage conductor";
  PS.Current[PS.n] i "current conductor";
  Modelica.Blocks.Interfaces.BooleanInput YDcontrol "true:Y, false:Delta"

equation
  term.v = top.v_term;
  term.i = top.i_term;
/*
  connect(YDcontrol, top.control)
*/
end Y_Dport_p;

partial model YDportTrafo_p_n
    "AC two port with Y or Delta topology for transformers"
  extends Port_p_n;

  replaceable model Topology_p = Topology.Y constrainedby Topology.TopologyBase
      "p: Y, Delta or PAR topology"
  Topology_p top_p(v_cond=v1, i_cond=i1, v_n=v_n1, w=w1)
      "p: Y, Delta or PAR topology"

  replaceable model Topology_n = Topology.Y constrainedby Topology.TopologyBase
      "n: Y, Delta or PAR topology"
  Topology_n top_n(v_cond=v2*w2_nom, i_cond=i2/w2_nom, v_n=v_n2, w=w2)
      "n: Y, Delta or PAR topology"

  PS.Voltage[PS.n] v1 "voltage conductor";
  PS.Current[PS.n] i1 "current conductor";
  PS.Voltage[n_n1] v_n1 "voltage neutral";
  PS.Current[n_n1] i_n1=top_p.i_n "current neutral to ground";

  PS.Voltage[PS.n] v2 "voltage conductor";
  PS.Current[PS.n] i2 "current conductor";
  PS.Voltage[n_n2] v_n2 "voltage neutral";
  PS.Current[n_n2] i_n2=top_n.i_n "current neutral to ground";

  protected
  constant Integer[2] scale={top_p.scale, top_n.scale};
  final parameter Integer n_n1=top_p.n_n
  final parameter Integer n_n2=top_n.n_n
  Real w1 "1: voltage ratio to nominal for topology";
  Real w2 "2: voltage ratio to nominal for topology";
  Real w2_nom "2: nominal turns ratio";

equation
  term_p.v = top_p.v_term;
  term_p.i = top_p.i_term;
  term_n.v = top_n.v_term;
  term_n.i = top_n.i_term;
end YDportTrafo_p_n;

partial model YDportTrafo_p_n_n
    "AC three port with Y or Delta topology for 3-winding transformers"
  extends PortBase;

  Ports.ACdq0_p term_p "positive terminal"
  Ports.ACdq0_n term_na "negative terminal a"
  Ports.ACdq0_n term_nb "negative terminal b"

  replaceable model Topology_p = Topology.Y constrainedby Topology.TopologyBase
      "p: Y or Delta topology"
  Topology_p top_p(v_cond=v1, i_cond=i1, v_n=v_n1, w=w1)
      "p: Y or Delta topology"

  replaceable model Topology_na = Topology.Y constrainedby
      Topology.TopologyBase "na: Y or Delta topology"
  Topology_na top_na(v_cond=v2a*w2a_nom, i_cond=i2a/w2a_nom, v_n=v_n2a, w=w2a)
      "na: Y or Delta topology"

  replaceable model Topology_nb = Topology.Y constrainedby
      Topology.TopologyBase "nb: Y or Delta topology"
  Topology_nb top_nb(v_cond=v2b*w2b_nom, i_cond=i2b/w2b_nom, v_n=v_n2b, w=w2b)
      "nb: Y or Delta topology"

  PS.Voltage[PS.n] v1 "voltage conductor";
  PS.Current[PS.n] i1 "current conductor";
  PS.Voltage[n_n1] v_n1 "voltage neutral";
  PS.Current[n_n1] i_n1=top_p.i_n "current neutral to ground";

  PS.Voltage[PS.n] v2a "voltage conductor";
  PS.Current[PS.n] i2a "current conductor";
  PS.Voltage[n_n2a] v_n2a "voltage neutral";
  PS.Current[n_n2a] i_n2a=top_na.i_n "current neutral to ground";

  PS.Voltage[PS.n] v2b "voltage conductor";
  PS.Current[PS.n] i2b "current conductor";
  PS.Voltage[n_n2b] v_n2b "voltage neutral";
  PS.Current[n_n2b] i_n2b=top_nb.i_n "current neutral to ground";

  PS.Voltage[PS.n] v0;
  protected
  constant Integer[3] scale={top_p.scale, top_na.scale, top_nb.scale};
  final parameter Integer n_n1=top_p.n_n
  final parameter Integer n_n2a=top_na.n_n
  final parameter Integer n_n2b=top_nb.n_n
  Real w1 "1: voltage ratio to nominal for topology";
  Real w2a "2a: voltage ratio to nominal for topology";
  Real w2b "2b: voltage ratio to nominal for topology";
  Real w2a_nom "2a: nominal turns ratio";
  Real w2b_nom "2b: nominal turns ratio";

equation
  Connections.branch(term_p.theta, term_na.theta);
  Connections.branch(term_p.theta, term_nb.theta);
  term_na.theta = term_p.theta;
  term_nb.theta = term_p.theta;

  term_p.v = top_p.v_term;
  term_p.i = top_p.i_term;
  term_na.v = top_na.v_term;
  term_na.i = top_na.i_term;
  term_nb.v = top_nb.v_term;
  term_nb.i = top_nb.i_term;
end YDportTrafo_p_n_n;

package Topology "Topology transforms "
  extends Modelica.Icons.BasesPackage;

  partial model TopologyBase "Topology transform base"
    extends PortBase;
    constant Integer scale "for scaling of impedance values";
    parameter Integer n_n(min=0,max=1)=1 "1 for Y, 0 for Delta";
    PS.Voltage[PS.n] v_term "terminal voltage";
    PS.Current[PS.n] i_term "terminal current";
    input PS.Voltage[PS.n] v_cond "conductor voltage";
    input PS.Current[PS.n] i_cond "conductor current";
    input PS.Voltage[n_n] v_n(start=fill(0,n_n)) "voltage neutral";
    input Real w = 1 "voltage ratio to nominal";
    PS.Current[n_n] i_n(start=fill(0,n_n)) "current neutral to ground";
    protected
    constant Real s3=sqrt(3);
  end TopologyBase;

  model Y "Y transform"
    extends TopologyBase(final scale=1, final n_n=1);

  equation
    w*v_cond = v_term - PS.map({0, 0, s3*v_n[1]});
    i_term = i_cond/w;
    if PS.n > 2 then
      i_n[1] = s3*i_term[3];
    else
      i_n[1] = 0;
    end if;
  end Y;

  model Delta "Delta transform"
    extends TopologyBase(final scale=3, final n_n=0);
    parameter Integer shift(min=0, max=11) = 1 "(0, 1 .. 11)*30deg phase shift"

    protected
      Real[2,2] Rot = Utilities.Transforms.rotation_dq(shift*pi/6)*s3/w;

  equation
    v_cond[1:2] = Rot*v_term[1:2];
    i_term[1:2] = i_cond[1:2]*Rot;
    if PS.n > 2 then
      v_cond[3] = 0;
      i_term[3] = 0;
    end if;
  end Delta;

  model Y_Delta "Y Delta switcheble transform"
    extends TopologyBase(final scale=1, final n_n=1);

  /*
  Modelica.Blocks.Interfaces.BooleanInput control "true:Y, false:Delta"
*/
    input Boolean control "true:Y, false:Delta";

  // in switcheable topology impedance defined as WINDING-impedance (i.e. Y-topology)
    protected
    parameter SI.Conductance epsG=1e-5;
      constant Real[2,2] Rot=Utilities.Transforms.rotation_dq(pi/6);

  equation
    if control then
      w*v_cond = v_term - {0, 0, s3*v_n[1]};
      i_term = i_cond/w;
      i_n[1] = s3*i_term[3];
    else
      w*v_cond[1:2] = s3*Rot*v_term[1:2];
      v_cond[3] = 0;
      i_term[1:2] = s3*transpose(Rot)*i_cond[1:2]/w;
      i_term[3] = 0;
      i_n[1] = 0;
    end if;
  //i_n[1] = epsG*v_n[1]; // neutral point isolated
  end Y_Delta;

  model Y_DeltaRegular "Y Delta switcheble transform, eps-regularised"
    extends TopologyBase(final scale=1, final n_n=1);

  /*
  Modelica.Blocks.Interfaces.BooleanInput control "true:Y, false:Delta"
*/
    input Boolean control "true:Y, false:Delta";

  // in switcheable topology impedance defined as WINDING-impedance (i.e. Y-topology)
    protected
    parameter SI.Conductance epsG=1e-5;
    parameter SI.Resistance epsR=1e-5;
      constant Real[2,2] Rot=Utilities.Transforms.rotation_dq(pi/6);
    SI.Current[3] i_neu;
    SI.Current[3] i_del;

  equation
    i_term[1:2] = i_cond[1:2]/w - Rot*{-i_del[2], i_del[1]};
    i_term[3] = i_cond[3]/w - i_del[3];
    i_cond/w = i_neu + i_del;
    i_n[1] = s3*i_neu[3];
    if control then
      w*v_cond = v_term - {0, 0, s3*v_n[1]} - epsR*i_neu;
      i_del[1:2] = epsG*(s3*Rot*v_term[1:2] - w*v_cond[1:2]);
      i_del[3] = -epsG*w*v_cond[3];
    else
      w*v_cond[1:2] = s3*Rot*v_term[1:2] - epsR*i_del[1:2];
      w*v_cond[3] = -epsR*i_del[3];
      i_neu = epsG*(v_term - w*v_cond - {0, 0, s3*v_n[1]});
    end if;
  //i_n[1] = epsG*v_n[1]; // neutral point isolated
  end Y_DeltaRegular;

  model PAR "Phase angle regulating (quadrature booster)"
    extends PowerSystems.AC3ph.Ports.Topology.TopologyBase(
      final scale=1, final n_n=1);

    SI.Angle alpha = atan(w - 1) "phase shift";

    protected
    Real[2, 2] Rot = [1, -w+1; w-1, 1] "Transforms.rotation_dq(alpha)/cos(alpha)";

  equation
    v_term[1:2] = Rot*v_cond[1:2];
    i_cond[1:2] = i_term[1:2]*Rot;
    if PS.n > 2 then
      v_term[3] = s3*v_n[1];
      i_cond[3] = i_term[3];
      i_n[1] = s3*i_term[3];
    else
      i_n[1] = 0;
    end if;
  end PAR;
end Topology;
end Ports;


Sensors and meters 3-phase

within PowerSystems.AC3ph;
package Sensors "Sensors and meters 3-phase"
  extends Modelica.Icons.SensorsPackage;

  model VnormSensor "Voltage-norm sensor, 3-phase dq0"
    extends Partials.Sensor1Base(final S_nom=1);

    parameter Integer n_eval(
      min=2,
      max=3) = 2 "dq- or dq0-norm"
    Modelica.Blocks.Interfaces.RealOutput v "voltage norm, phase-to-ground"

  protected
    final parameter PS.Voltage V_base=Utilities.Precalculation.baseV(
      puUnits, V_nom);

  equation
    v = sqrt(term.v[1:n_eval]*term.v[1:n_eval])/V_base;
  end VnormSensor;

  model InormSensor "Current-norm sensor, 3-phase dq0"
    extends Partials.Sensor2Base;

    parameter Integer n_eval(
      min=2,
      max=3) = 2 "dq- or dq0-norm"
    Modelica.Blocks.Interfaces.RealOutput i "current norm, term_p to term_n"

  protected
    final parameter PS.Current I_base=Utilities.Precalculation.baseI(
      puUnits, V_nom, S_nom);

  equation
    i = sqrt(term_p.i[1:n_eval]*term_p.i[1:n_eval])/I_base;
  end InormSensor;

  model Vsensor "Voltage sensor, 3-phase dq0"
    extends Partials.Sensor1Base(final S_nom=1);

    parameter Integer signalTrsf=0 "signal in which reference frame?"

    Modelica.Blocks.Interfaces.RealOutput[3] v "voltage, phase-to-ground"

  protected
    final parameter PS.Voltage V_base=Utilities.Precalculation.baseV(
      puUnits, V_nom);

  equation
    if signalTrsf == 0 then
      v = term.v/V_base; // actual
    elseif signalTrsf == 1 then
      v = cat(1, transpose(rot_dq(term.theta[1]))*term.v[1:2], term.v[3:3])/V_base; // dq0
    elseif signalTrsf == 2 then
      v = cat(1, rot_dq(term.theta[2])*term.v[1:2], term.v[3:3])/V_base; // alpha-beta_o
    elseif signalTrsf == 3 then
      v = transpose(park(term.theta[2]))*term.v/V_base; // abc
    end if;
  end Vsensor;

  model Isensor "Current sensor, 3-phase dq0"
    extends Partials.Sensor2Base;

    parameter Integer signalTrsf=0 "signal in which reference frame?"

    Modelica.Blocks.Interfaces.RealOutput[3] i "current, term_p to term_n"

  protected
    final parameter PS.Current I_base=Utilities.Precalculation.baseI(
      puUnits, V_nom, S_nom);

  equation
    if signalTrsf == 0 then
      i = term_p.i/I_base;
    elseif signalTrsf == 1 then // actual
      i = cat(1, transpose(rot_dq(term_p.theta[1]))*term_p.i[1:2], term_p.i[3:3])/I_base; // dq0
    elseif signalTrsf == 2 then
      i = cat(1, rot_dq(term_p.theta[2])*term_p.i[1:2], term_p.i[3:3])/I_base; // alpha-beta_o
    elseif signalTrsf == 3 then
      i = transpose(park(term_p.theta[2]))*term_p.i/I_base; // abc
    end if;
  end Isensor;

  model Psensor "Power sensor, 3-phase dq0"
    extends Partials.Sensor2Base(final V_nom=1);

    Modelica.Blocks.Interfaces.RealOutput[PS.n] p
      "{active, reactive, DC} power, term_p to term_n"

  protected
    final parameter SI.ApparentPower S_base=Utilities.Precalculation.baseS(
      puUnits, S_nom);

  equation
    p = PS.phasePowers_vi(term_p.v, term_p.i)/S_base;
  end Psensor;

  model Vmeter "Voltage meter, 3-phase dq0"
    extends Partials.Meter1Base(final S_nom=1);

    output SIpu.Voltage[PS.n] v(each stateSelect=StateSelect.never);
    output SIpu.Voltage[2] vpp(each stateSelect=StateSelect.never);

    output SIpu.Voltage[3] v_abc(each stateSelect=StateSelect.never)=transpose(Park)*v if abc;
    output SIpu.Voltage[3] vpp_abc(each stateSelect=StateSelect.never)=
      {v_abc[2],v_abc[3],v_abc[1]} - {v_abc[3],v_abc[1],v_abc[2]} if abc;

    output SIpu.Voltage v_norm(stateSelect=StateSelect.never);
    output SI.Angle alpha_v(stateSelect=StateSelect.never);
  protected
    final parameter PS.Voltage V_base=Utilities.Precalculation.baseV(
      puUnits, V_nom);

  equation
    v = term.v/V_base;
    vpp = sqrt(3)*{v[2],-v[1]};
    v_norm = sqrt(v*v);
    alpha_v = atan2(Rot_dq[:,2]*v[1:2], Rot_dq[:,1]*v[1:2]);
  end Vmeter;

  model Imeter "Current meter, 3-phase dq0"
    extends Partials.Meter2Base;

    output SIpu.Current[PS.n] i(each stateSelect=StateSelect.never);

    output SIpu.Current[3] i_abc(each stateSelect=StateSelect.never)=transpose(Park)*i if abc;

    output SIpu.Current i_norm(stateSelect=StateSelect.never);
    output SI.Angle alpha_i(stateSelect=StateSelect.never);
  protected
    final parameter PS.Current I_base=Utilities.Precalculation.baseI(
      puUnits, V_nom, S_nom);

  equation
    i = term_p.i/I_base;
    i_norm = sqrt(i*i);
    alpha_i = atan2(Rot_dq[:,2]*i[1:2], Rot_dq[:,1]*i[1:2]);
  end Imeter;

  model Pmeter "Power meter, 3-phase dq0"
    extends Partials.Meter2Base(final V_nom=1, final abc=false);

    parameter Boolean av=false "time average power"
    parameter SI.Time tcst(min=1e-9)=1 "average time-constant"
    output SIpu.Power[PS.n] p(each stateSelect=StateSelect.never);
    output SIpu.Power[PS.n] p_av=pav if av;
  protected
    outer System system;
    final parameter SI.ApparentPower S_base=Utilities.Precalculation.baseS(
      puUnits, S_nom);
    SIpu.Power[PS.n] pav;

  initial equation
    if av then
      pav = p;
    end if;

  equation
    p = PS.phasePowers_vi(term_p.v, term_p.i)/S_base;
    if av then
      der(pav) = (p - pav)/tcst;
    else
      pav = zeros(PS.n);
    end if;
  end Pmeter;

  model PVImeter "Power-voltage-current meter, 3-phase dq0"
    extends Partials.Meter2Base;

    parameter Boolean av=false "time average power"
    parameter SI.Time tcst(min=1e-9)=1 "average time-constant"

    function v2vpp_abc
      input SIpu.Voltage[3] v_abc;
      output SIpu.Voltage[3] vpp_abc;
    algorithm
      vpp_abc := {v_abc[2],v_abc[3],v_abc[1]} - {v_abc[3],v_abc[1],v_abc[2]};
    end v2vpp_abc;

    output SIpu.Power[PS.n] p(each stateSelect=StateSelect.never);
    output SIpu.Power[PS.n] p_av=pav if av;
    output SIpu.Voltage[PS.n] v(each stateSelect=StateSelect.never);
    output SIpu.Voltage[2] vpp(each stateSelect=StateSelect.never);
    output SIpu.Current[PS.n] i(each stateSelect=StateSelect.never);

    output SIpu.Voltage[3] v_abc(each stateSelect=StateSelect.never)=transpose(Park)*v if abc;
    output SIpu.Voltage[3] vpp_abc(each stateSelect=StateSelect.never)=v2vpp_abc(transpose(Park)*v) if abc;
    output SIpu.Current[3] i_abc(each stateSelect=StateSelect.never)=transpose(Park)*i if abc;

    output SIpu.Voltage v_norm(stateSelect=StateSelect.never);
    output SI.Angle alpha_v(stateSelect=StateSelect.never);
    output SIpu.Current i_norm(stateSelect=StateSelect.never);
    output SI.Angle alpha_i(stateSelect=StateSelect.never);
    output Real cos_phi(stateSelect=StateSelect.never);

  protected
    outer System system;
    final parameter PS.Voltage V_base=Utilities.Precalculation.baseV(
      puUnits, V_nom);
    final parameter PS.Current I_base=Utilities.Precalculation.baseI(
      puUnits, V_nom, S_nom);
    SIpu.Power[PS.n] pav;

  initial equation
    if av then
      pav = p;
    end if;

  equation
    v = term_p.v/V_base;
    vpp = sqrt(3)*{v[2],-v[1]};
    i = term_p.i/I_base;
    p = PS.phasePowers_vi(v, i);
    if av then
      der(pav) = (p - pav)/tcst;
    else
      pav = zeros(PS.n);
    end if;
    v_norm = sqrt(v*v);
    alpha_v = atan2(Rot_dq[:,2]*v[1:2], Rot_dq[:,1]*v[1:2]);
    i_norm = sqrt(i*i);
    alpha_i = atan2(Rot_dq[:,2]*i[1:2], Rot_dq[:,1]*i[1:2]);
    cos_phi = cos(alpha_v - alpha_i);
  end PVImeter;

  model Efficiency "Power sensor, 3-phase dq0"
    extends Partials.Sensor2Base;

    Interfaces.ThermalV_p heat(     m=m) "vector heat port"
    parameter Boolean dir_in=true "direction"
    parameter Integer m(final min=1)=1 "dimension of heat port";
    parameter Boolean av=false "time average efficiency"
    parameter SI.Time tcst(min=1e-9)=1 "average time-constant"
    parameter SI.Temperature T_amb=300 "ambient temperature";
    output Real eta "efficiency";
  protected
    SI.Power p "total el power, term_p to term_n";
    SI.HeatFlowRate q "total heat flow 'in'";
    SI.Power pav;
    SI.HeatFlowRate qav;

  initial equation
    if av then
      pav = p;
      qav = q;
    end if;

  equation
    heat.ports.T = fill(T_amb, heat.m);
    p = term_p.v*term_p.i;
    q = sum(heat.ports.Q_flow);

    if av then
      der(pav) = (p - pav)/tcst;
      der(qav) = (q - qav)/tcst;
    else
      pav = p;
      qav = q;
    end if;

    if qav < abs(pav) then
      if dir_in then
        eta = if pav > 0 then 100*(pav - qav)/pav else -100*pav/(pav - qav);
      else
        eta = if pav > 0 then 100*pav/(pav + qav) else -100*(pav + qav)/pav;
      end if;
    else
      eta = 0;
    end if;
  end Efficiency;

  model Phasor "Visualiser of voltage and current phasor, 3-phase dq0"
    extends Partials.PhasorBase;

    Types.Color color_p;
    Types.Color color_n;
    extends Icons.LeftBar(colorL={0,127,127}, xL=x_norm*abs(p[1])/V_base/I_base);
    extends Icons.RightBar(colorR={127,0,127}, xR=x_norm*abs(p[2])/V_base/I_base);
    extends Icons.DoubleNeedle(
      color1={255,0,0},
      color2={0,0,255},
      x1=r_norm*v_dq[1]/V_base,
      y1=r_norm*v_dq[2]/V_base,
      x2=r_norm*i_dq[1]/I_base,
      y2=r_norm*i_dq[2]/I_base);

  equation
    color_p = if p[1]>0 then {0,127,127} else {215,215,215};
    color_n = if p[1]<0 then {0,127,127} else {215,215,215};
  end Phasor;

  package Partials "Partial models"
    extends Modelica.Icons.BasesPackage;

    partial model Sensor1Base "Sensor 1 terminal base, 3-phase dq0"
      extends Ports.Port_p;
      extends Common.Nominal.Nominal;

    protected
      function park = Utilities.Transforms.park;
      function rot_dq = Utilities.Transforms.rotation_dq;

    equation
      term.i = zeros(PS.n);
    end Sensor1Base;

    partial model Sensor2Base "Sensor 2 terminal base, 3-phase dq0"
      extends Ports.Port_pn;
      extends Common.Nominal.Nominal;

    protected
      function park = Utilities.Transforms.park;
      function rot_dq = Utilities.Transforms.rotation_dq;

    equation
      term_p.v = term_n.v;
    end Sensor2Base;

    partial model Meter1Base "Meter 1 terminal base, 3-phase dq0"
      extends Sensor1Base;

      parameter Boolean abc=false "abc inertial"
    protected
      Real[3,3] Park;
      Real[2,2] Rot_dq;
      function atan2 = Modelica.Math.atan2;
    equation
      if abc then
        Park = park(term.theta[2]);
      else
        Park = zeros(3,3);
      end if;
      Rot_dq = rot_dq(term.theta[1]);
    end Meter1Base;

    partial model Meter2Base "Meter 2 terminal base, 3-phase dq0"
      extends Sensor2Base;

      parameter Boolean abc=false "abc inertial"
    protected
      Real[3,3] Park;
      Real[2,2] Rot_dq;
      function atan2 = Modelica.Math.atan2;
    equation
      if abc then
        Park = park(term_p.theta[2]);
      else
        Park = zeros(3,3);
      end if;
      Rot_dq = rot_dq(term_p.theta[1]);
    end Meter2Base;

  partial model PhasorBase "Phasor base, 3-phase dq0"
    extends Ports.Port_pn;
    extends Common.Nominal.Nominal(final puUnits=true);

    PS.Voltage[2] v_dq;
    PS.Current[2] i_dq;
    SI.Power[2] p;
    protected
    constant Real r_norm(unit="1")=0.8 "norm radius phasor";
    constant Real x_norm(unit="1")=0.8 "norm amplitude power";
    final parameter PS.Voltage V_base=Utilities.Precalculation.baseV(
                                                                 puUnits, V_nom);
    final parameter PS.Current I_base=Utilities.Precalculation.baseI(
                                                                 puUnits, V_nom, S_nom);
      Real[2,2] Rot_dq=Utilities.Transforms.rotation_dq(term_p.theta[1]);

  equation
    term_p.v = term_n.v;
    v_dq = transpose(Rot_dq)*term_p.v[1:2];
    i_dq = transpose(Rot_dq)*term_p.i[1:2];
    p = {v_dq*i_dq, -j_dq0(v_dq)*i_dq};
  end PhasorBase;

  end Partials;
end Sensors;


Reactive and capacitive shunts

within PowerSystems.AC3ph;
package Shunts "Reactive and capacitive shunts"
  extends Modelica.Icons.VariantsPackage;

  model ReactiveShunt "Shunt reactor with parallel conductor, 3-phase dq0"
    extends Partials.ShuntBase;

    parameter SIpu.Conductance g=0 "conductance (parallel)";
    parameter SIpu.Resistance r=0 "resistance (serial)";
    parameter SIpu.Reactance x_s=1 "self reactance";
    parameter SIpu.Reactance x_m=0 "mutual reactance, -x_s/2 < x_m < x_s";
  protected
    final parameter SI.Resistance[2] RL_base=Utilities.Precalculation.baseRL(
        puUnits,
        V_nom,
        S_nom,
        2*pi*f_nom);
    SI.Conductance G=g/RL_base[1];
    SI.Resistance R=r*RL_base[1];
    SI.Inductance L=(x_s-x_m)*RL_base[2];
    SI.Inductance L0=(x_s+2*x_m)*RL_base[2];
    PS.Current[PS.n] i_x;

  initial equation
    if dynType == Types.Dynamics.SteadyInitial then
      der(i_x) = omega[1]*j(i_x);
    end if;

  equation
    i_x = i - G*v;
    if dynType <> Types.Dynamics.SteadyState then
      PS.map({L,L,L0}).*der(i_x) + omega[2]*L*j(i_x) + R*i_x = v;
    else
      omega[2]*L*j(i_x) + R*i_x = v;
    end if;
  end ReactiveShunt;

  model CapacitiveShunt
    "Shunt capacitor with parallel conductor, 3-phase dq0, pp pg"
    extends Partials.ShuntBase;

    parameter SIpu.Conductance g_pg=0 "conductance ph-grd";
    parameter SIpu.Conductance g_pp=0 "conductance ph_ph";
    parameter SIpu.Susceptance b_pg=1 "susceptance ph-grd";
    parameter SIpu.Susceptance b_pp=1/3 "susceptance ph-ph";
  protected
    final parameter SI.Resistance[2] GC_base=Utilities.Precalculation.baseGC(
        puUnits,
        V_nom,
        S_nom,
        2*pi*f_nom);
    SI.Conductance G=(g_pg+3*g_pp)*GC_base[1];
    SI.Conductance G0=g_pg*GC_base[1];
    SI.Capacitance C=(b_pg+3*b_pp)*GC_base[2];
    SI.Capacitance C0=b_pg*GC_base[2];

  initial equation
    if dynType == Types.Dynamics.SteadyInitial then
      der(v) = omega[1]*j(v);
    end if;

  equation
    if dynType <> Types.Dynamics.SteadyState then
      PS.map({C,C,C0}).*der(v) + omega[2]*C*j(v) + PS.map({G,G,G0}).*v = i;
    else
      omega[2]*C*j(v) + PS.map({G,G,G0}).*v = i;
    end if;
  end CapacitiveShunt;

  model ReactiveShuntNonSym
    "Shunt reactor with parallel conductor non symmetric, 3-phase dq0"
    extends Partials.ShuntBaseNonSym;

    parameter SIpu.Conductance[3] g={0,0,0} "conductance abc (parallel)";
    parameter SIpu.Resistance[3] r={0,0,0} "resistance abc (serial)";
    parameter SIpu.Reactance[3, 3] x=[1, 0, 0; 0, 1, 0; 0, 0, 1] "reactance abc";
    SI.MagneticFlux[3] psi_x(each stateSelect=StateSelect.prefer) "magnetic flux";
  protected
    final parameter SI.Resistance[2] RL_base=Utilities.Precalculation.baseRL(
        puUnits,
        V_nom,
        S_nom,
        2*pi*f_nom);
    final parameter SI.Conductance[3] G_abc=g/RL_base[1];
    final parameter SI.Reactance[3] R_abc=r*RL_base[1];
    final parameter SI.Inductance[3, 3] L_abc=x*RL_base[2];
    SI.Conductance[3, 3] G;
    SI.Resistance[3, 3] R;
    SI.Inductance[3, 3] L;
    PS.Current[3] i_x;

  initial equation
    if dynType == Types.Dynamics.SteadyInitial then
      der(psi_x[1:2]) = omega[1]*j_dq0(psi_x[1:2]);
      psi_x[3] = 0;
    end if;

  equation
    L = Park*L_abc*transpose(Park);
    R = Park*diagonal(R_abc)*transpose(Park);
    G = Park*diagonal(G_abc)*transpose(Park);

    i_x = i - G*v;
    psi_x = L*(i - G*v);
    if dynType <> Types.Dynamics.SteadyState then
      der(psi_x) + omega[2]*j_dq0(psi_x) + R*i_x = v;
    else
      omega[2]*j_dq0(psi_x) + R*i_x = v;
    end if;
  end ReactiveShuntNonSym;

  model CapacitiveShuntNonSym
    "Shunt capacitor with parallel conductor non symmetric, 3-phase dq0, pp pg"
    extends Partials.ShuntBaseNonSym;

    parameter SIpu.Conductance[3] g_pg={0,0,0} "conductance ph-grd abc";
    parameter SIpu.Conductance[3] g_pp={0,0,0} "conductance ph_ph abc";
    parameter SIpu.Susceptance[3] b_pg={1,1,1} "susceptance ph-grd abc";
    parameter SIpu.Susceptance[3] b_pp={1,1,1}/3 "susceptance ph-ph abc";
    SI.ElectricCharge[3] q(each stateSelect=StateSelect.prefer) "electric charge";
  protected
    final parameter SI.Resistance[2] GC_base=Utilities.Precalculation.baseGC(
        puUnits,
        V_nom,
        S_nom,
        2*pi*f_nom);
    final parameter SI.Conductance[3,3] G_abc=(diagonal(g_pg)+
      [g_pp[2]+g_pp[3],-g_pp[3],-g_pp[2];-g_pp[3],g_pp[3]+g_pp[1],-g_pp[1];-g_pp[2],-g_pp[1],g_pp[1]+g_pp[2]])
      *GC_base[1];
    final parameter SI.Capacitance[3,3] C_abc=(diagonal(b_pg)+
      [b_pp[2]+b_pp[3],-b_pp[3],-b_pp[2];-b_pp[3],b_pp[3]+b_pp[1],-b_pp[1];-b_pp[2],-b_pp[1],b_pp[1]+b_pp[2]])
      *GC_base[2];
    SI.Conductance[3, 3] G;
    SI.Capacitance[3, 3] C;

  initial equation
    if dynType == Types.Dynamics.SteadyInitial then
      der(q[1:2]) = omega[1]*j_dq0(q[1:2]);
      q[3] = 0;
    end if;

  equation
    C = Park*C_abc*transpose(Park);
    G = Park*G_abc*transpose(Park);

    q = C*v;
    if dynType <> Types.Dynamics.SteadyState then
      der(q) + omega[2]*j_dq0(q) + G*v = i;
    else
      omega[2]*j_dq0(q) + G*v = i;
    end if;
  end CapacitiveShuntNonSym;

  package Partials "Partial models"
    extends Modelica.Icons.BasesPackage;

    partial model ShuntBase "Shunt base, 3-phase dq0"
      extends Ports.Port_p;
      extends Common.Nominal.NominalAC;

      parameter Types.Dynamics dynType=system.dynType "transient or steady-state model"

      PS.Voltage[PS.n] v;
      PS.Current[PS.n] i;
    protected
      SI.AngularFrequency[2] omega;

    equation
      omega = der(term.theta);
      v = term.v;
      i = term.i;
    end ShuntBase;

    partial model ShuntBaseNonSym "Shunt base non symmetric, 3-phase dq0"
      extends ShuntBase;

    protected
      Real[3,3] Park=Utilities.Transforms.park(term.theta[2]);
    end ShuntBaseNonSym;

  end Partials;
end Shunts;


Voltage and Power Sources

within PowerSystems.AC3ph;
package Sources "Voltage and Power Sources"
  extends Modelica.Icons.SourcesPackage;

  model Voltage "Ideal voltage, 3-phase dq0"
    extends Partials.VoltageBase;

    parameter SIpu.Voltage v0=1 "fixed voltage"
    parameter SI.Angle alpha0=0 "fixed phase angle"
  protected
    PS.Voltage V;
    SI.Angle alpha;
    SI.Angle phi;

  equation
    if not use_vPhasor_in then
      vPhasor_internal = {v0, alpha0};
    end if;
    V = vPhasor_internal[1]*V_base;
    alpha = vPhasor_internal[2];
    phi = term.theta[1] + alpha + system.alpha0;
    term.v = PS.map({V*cos(phi), V*sin(phi), sqrt(3)*neutral.v});
  end Voltage;

  model Vspectrum "Ideal voltage spectrum, 3-phase dq0"
    extends Partials.VoltageBase;

    constant Real s2=sqrt(2);
    parameter Integer[:] h={1,3,5} "{1, ...}, which harmonics?";
    parameter SIpu.Voltage[N] v0={1,0.3,0.1} "voltages";
    parameter SI.Angle[N] alpha0=zeros(N) "phase angles";
  protected
    final parameter Integer N=size(h, 1) "nb of harmonics";
    PS.Voltage V;
    SI.Angle alpha;
    SI.Angle phi;
    Integer[N] h_mod3;
    Real[PS.n, N] H;

  equation
    if not use_vPhasor_in then
      vPhasor_internal = {1, 0};
    end if;
    V = vPhasor_internal[1]*V_base;
    alpha = vPhasor_internal[2];

  algorithm
    h_mod3 := mod(h, 3);
    for n in 1:N loop
      if h_mod3[n] == 1 then
        phi := h[n]*(theta + alpha + system.alpha0 + alpha0[n]) - term.theta[2];
        H[:, n] := PS.map({cos(phi), sin(phi), 0});
      elseif h_mod3[n] == 2 then
        phi := h[n]*(theta + alpha + system.alpha0 + alpha0[n]) + term.theta[2];
        H[:, n] := PS.map({cos(phi), -sin(phi), 0});
      else
        phi := h[n]*(theta + alpha + system.alpha0 + alpha0[n]);
        H[:, n] := PS.map({0, 0, s2*cos(phi)});
      end if;
    end for;
    term.v := V*(H*v0);
  end Vspectrum;

  model InfBus "Infinite slack bus, 3-phase dq0"
    extends Partials.PowerBase(final S_nom=1);

    Modelica.Blocks.Interfaces.RealInput[2] vPhasor_in if use_vPhasor_in
      "{abs(voltage), phase}"
    parameter Boolean use_vPhasor_in = false
      "= true to use input signal vPhasor_in, otherwise use {v0, alpha0}"
    parameter SIpu.Voltage v0=1 "fixed voltage"
    parameter SI.Angle alpha0=0 "fixed phase angle"

  protected
    PS.Voltage V;
    SI.Angle alpha;
    SI.Angle phi;
    Modelica.Blocks.Interfaces.RealInput[2] vPhasor_internal
      "Needed to connect to conditional connector";
  equation
    connect(vPhasor_in, vPhasor_internal);

    if not use_vPhasor_in then
      vPhasor_internal = {v0, alpha0};
    end if;
    V = vPhasor_internal[1]*V_base;
    alpha = vPhasor_internal[2];
    phi = term.theta[1] + alpha + system.alpha0;
    term.v = PS.map({V*cos(phi), V*sin(phi), sqrt(3)*neutral.v});
  end InfBus;

  model VsourceRX "Voltage behind reactance source, 3-phase dq0"
    extends Partials.PowerBase;

    parameter Types.Dynamics dynType=system.dynType "transient or steady-state model"

    parameter PowerSystems.Types.Init initType=PowerSystems.Types.Init.v_alpha
      "initialisation type"
    parameter SIpu.Voltage v_start=1 "initial terminal voltage"
    parameter SI.Angle alpha_start=0 "initial terminal phase angle"
    parameter SIpu.ApparentPower p_start=1 "initial terminal active power"
    parameter SIpu.ApparentPower q_start=1 "initial terminal reactive power"
    parameter SIpu.ApparentPower pq_start[2]={1,0}
      "initial terminal {active, reactive} power"
    parameter SIpu.Resistance r=0.01 "resistance";
    parameter SIpu.Reactance x=1 "reactance d- and q-axis";
    parameter SIpu.Reactance x0=0.1 "reactance 0-axis";
  protected
    final parameter SIpu.Voltage v0(final fixed=false, start=1)
      "voltage behind reactance";
    final parameter SI.Angle alpha0(final fixed=false, start=0)
      "phase angle of voltage b r";
    final parameter PS.Voltage V(start=V_base)=v0*V_base;
    final parameter Real[2] RL_base=Utilities.Precalculation.baseRL(
          puUnits,
          V_nom,
          S_nom,
          2*pi*system.f_nom);
    final parameter SI.Resistance R=r*RL_base[1];
    final parameter SI.Inductance L = x*RL_base[2];
    final parameter SI.Inductance L0 = x0*RL_base[2];
    PS.Voltage[PS.n] v(start=PS.map({cos(system.alpha0),sin(system.alpha0),0})*V_base);
    PS.Current[PS.n] i(start=zeros(PS.n));
    SI.AngularFrequency[2] omega;
    SI.Angle phi(start=alpha0+system.alpha0);
    function atan2 = Modelica.Math.atan2;

  initial equation
    if initType == PowerSystems.Types.Init.v_alpha then
      sqrt(v[1:2]*v[1:2]) = v_start*V_base;
      atan2(v[2], v[1]) = alpha_start + system.alpha0;
    elseif initType == PowerSystems.Types.Init.v_p then
      sqrt(v[1:2]*v[1:2]) = v_start*V_base;
      v[1:2]*i[1:2] = p_start*S_base;
    elseif initType == PowerSystems.Types.Init.v_q then
      sqrt(v[1:2]*v[1:2]) = v_start*V_base;
      {v[2],-v[1]}*i[1:2] = q_start*S_base;
    elseif initType == PowerSystems.Types.Init.p_q then
      {v[1:2]*i[1:2], {v[2],-v[1]}*i[1:2]} = pq_start*S_base;
    end if;
    if dynType == Types.Dynamics.SteadyInitial then
      der(i) = omega[1]*j(i);
    end if;

  equation
    omega = der(term.theta);
    v = term.v;
    i = -term.i;
    phi = term.theta[1] + alpha0 + system.alpha0;

    if dynType <> Types.Dynamics.SteadyState then
      PS.map({L,L,L0}).*der(i) + omega[2]*L*j(i) + R*i = PS.map({V*cos(phi), V*sin(phi), sqrt(3)*neutral.v}) - v;
    else
      omega[2]*L*j(i) + R*i = PS.map({V*cos(phi), V*sin(phi), sqrt(3)*neutral.v}) - v;
    end if;
  end VsourceRX;

  model PVsource "Power-voltage source, 3-phase dq0"
    extends Partials.PowerBase;

    Modelica.Blocks.Interfaces.RealInput[2] pv_in if use_pv_in
      "{active power, abs(voltage)}"
    parameter Boolean use_pv_in = false
      "= true to use input signal pv_in, otherwise use {p0, v0}"
    parameter SIpu.ApparentPower p0=1 "fixed active power"
    parameter SIpu.Voltage v0=1 "fixed voltage"
    parameter PS.Voltage V_start=V_nom "start value terminal voltage"

  protected
    SI.Power P;
    PS.Voltage V;
    PS.Voltage[2] v(start={cos(system.alpha0),sin(system.alpha0)}*V_start);
    PS.Current[2] i(start={0,0});
    Modelica.Blocks.Interfaces.RealInput[2] pv_internal
      "Needed to connect to conditional connector";
  equation
    connect(pv_in, pv_internal);

    i = -term.i[1:2];
    if not use_pv_in then
      pv_internal={p0, v0};
    end if;
    P = pv_internal[1]*S_base;
    V = pv_internal[2]*V_base;
    v*v = V*V;
    v*i = P;
    term.v = PS.map(cat(1, v, {sqrt(3)*neutral.v}));
  end PVsource;

  model PQsource "Power source, 3-phase dq0"
    extends Partials.PowerBase;

    Modelica.Blocks.Interfaces.RealInput[2] pq_in(each final unit="1") if  use_pq_in
      "{active, reactive} power"
    parameter Boolean use_pq_in = false
      "= true to use input signal pq_in, otherwise use pq0"

    parameter SIpu.ApparentPower pq0[2]={1,0} "fixed {active, reactive} power"
    parameter PS.Voltage V_start=V_nom "start value terminal voltage"

  protected
    SI.Power[2] P;
    PS.Voltage[2] v(start={cos(system.alpha0),sin(system.alpha0)}*V_start);
    PS.Current[2] i(start={0,0});
    Modelica.Blocks.Interfaces.RealInput[2] pq_internal
      "Needed to connect to conditional connector";
  equation
    connect(pq_in, pq_internal);

    i = -term.i[1:2];
    if not use_pq_in then
      pq_internal = pq0;
    end if;
    P = pq_internal*S_base;
    {v*i, {v[2],-v[1]}*i} = P;
    term.v = PS.map(cat(1, v, {sqrt(3)*neutral.v}));
  end PQsource;

  package Partials "Partial models"
    extends Modelica.Icons.BasesPackage;

    partial model SourceBase "Voltage base, 3-phase dq0"
      extends Ports.Port_n;
      extends Common.Nominal.Nominal;

      Interfaces.Electric_p neutral "(use for grounding)"
    protected
      outer System system;
      final parameter PS.Voltage V_base=Utilities.Precalculation.baseV(
                                                                   puUnits, V_nom);
      SI.Angle theta(stateSelect=StateSelect.prefer) "absolute angle";

    equation
      Connections.potentialRoot(term.theta);
      if Connections.isRoot(term.theta) then
        term.theta = {system.thetaRel, system.thetaRef};
      end if;

      if PS.n > 2 then
        sqrt(3)*term.i[3] + neutral.i = 0;
      else
        neutral.i = 0;
      end if;
    end SourceBase;

    partial model VoltageBase "Voltage base, 3-phase dq0"
      extends SourceBase(final S_nom=1);

      parameter Types.SourceFrequency fType=PowerSystems.Types.SourceFrequency.System
        "frequency type"
      parameter SI.Frequency f=system.f "frequency if type is parameter"

      parameter Boolean use_vPhasor_in = false
        "= true to use input signal vPhasor_in, otherwise use fixed values"

      Modelica.Blocks.Interfaces.RealInput omega_in(final unit="rad/s") if fType
         == PowerSystems.Types.SourceFrequency.Signal
        "angular frequency"
      Modelica.Blocks.Interfaces.RealInput[2] vPhasor_in if use_vPhasor_in
        "{abs(voltage), phase}"
    protected
      Modelica.Blocks.Interfaces.RealInput omega_internal
        "Needed to connect to conditional connector";
      Modelica.Blocks.Interfaces.RealInput[2] vPhasor_internal
        "Needed to connect to conditional connector";

    initial equation
      if fType == Types.SourceFrequency.Signal then
        theta = 0;
      end if;

    equation
      connect(omega_in, omega_internal);
      connect(vPhasor_in, vPhasor_internal);
      if fType <> Types.SourceFrequency.Signal then
         omega_internal = 0.0;
      end if;

      if fType == Types.SourceFrequency.System then
        theta = system.theta;
      elseif fType == Types.SourceFrequency.Parameter then
        theta = 2*pi*f*(time - system.initime);
      elseif fType == Types.SourceFrequency.Signal then
        der(theta) = omega_internal;
      end if;
    end VoltageBase;

    partial model PowerBase "Power source base, 3-phase dq0"
      extends SourceBase;

    protected
      final parameter Real S_base=Utilities.Precalculation.baseS(puUnits, S_nom);

    equation
      theta=system.theta;
    end PowerBase;

  end Partials;
end Sources;


Transformers 3-phase

within PowerSystems.AC3ph;
package Transformers "Transformers 3-phase"
  extends Modelica.Icons.VariantsPackage;

  model TrafoIdeal "Ideal transformer, 3-phase dq0"
    parameter Boolean D_D=false "set true if Delta-Delta topology!";
    extends Partials.TrafoIdealBase(final dynType=Types.Dynamics.SteadyState);

  equation
    i1 + i2 = zeros(PS.n);
    if D_D then
      v1[1:2] = v2[1:2];
      i1[3]=0;
    else
      v1 = v2;
    end if;
  end TrafoIdeal;

  model TrafoStray "Ideal magnetic coupling transformer, 3-phase dq0"
    extends Partials.TrafoStrayBase;

  initial equation
    if dynType == Types.Dynamics.SteadyInitial then
      der(i1) = omega[1]*j(i1);
    end if;

  equation
    i1 + i2 = zeros(PS.n);
    if dynType <> Types.Dynamics.SteadyState then
      PS.map({sum(L),sum(L),sum(L0)}).*der(i1) + omega[2]*sum(L)*j(i1) + sum(R)*i1 = v1 - v2;
    else
      omega[2]*sum(L)*j(i1) + sum(R)*i1 = v1 - v2;
    end if;
  end TrafoStray;

  model TrafoMag "Magnetic coupling transformer, 3-phase dq0"
    extends Partials.TrafoMagBase;

    PS.Voltage[PS.n] v0;
    PS.Current[PS.n] imag;
    PS.Current[PS.n] iedc;

  initial equation
    if dynType == Types.Dynamics.SteadyInitial then
      der(i1) = omega[1]*j(i1);
      der(i2) = omega[1]*j(i2);
      der(imag) = omega[1]*j(imag);
    elseif dynType <> Types.Dynamics.SteadyState then
      der(imag) = omega[1]*j(imag);
    end if;

  equation
    i1 + i2 = imag + iedc;
    Redc*iedc = v0;
    if dynType <> Types.Dynamics.SteadyState then
      PS.map({L[1],L[1],L0[1]}).*der(i1) + omega[2]*L[1]*j(i1) + R[1]*i1 = v1 - v0;
      PS.map({L[2],L[2],L0[2]}).*der(i2) + omega[2]*L[2]*j(i2) + R[2]*i2 = v2 - v0;
      Lm*der(imag) + omega[2]*Lm*j(imag) = v0;
    else
      omega[2]*L[1]*j(i1) + R[1]*i1 = v1 - v0;
      omega[2]*L[2]*j(i2) + R[2]*i2 = v2 - v0;
      omega[2]*Lm*j(imag) = v0;
    end if;
  end TrafoMag;

  model TrafoSatEff "Averaged saturation transformer, 3-phase dq0"
    extends Partials.TrafoSatBase;

    PS.Voltage[PS.n] v0;
    PS.Current[PS.n] imag;
    PS.Current[PS.n] iedc;
  protected
    Real[PS.n] psi0(each stateSelect=StateSelect.prefer) "unsaturated flux";
    Real gp;
    function der_sat = Common.IronSaturation.der_saturationAnalytic;

  initial equation
    if dynType == Types.Dynamics.SteadyInitial then
      der(i1) = omega[1]*j(i1);
      der(i2) = omega[1]*j(i2);
      der(psi0) = omega[1]*j(psi0);
    elseif dynType <> Types.Dynamics.SteadyState then
      der(psi0) = omega[1]*j(psi0);
    end if;

  equation
    i1 + i2 = imag + iedc;
    Redc*iedc = v0;
    psi0 = Lm*imag;
    gp = scalar(der_sat({0.66*sqrt(psi0*psi0)/psi_nom}, c_sat));

    if dynType <> Types.Dynamics.SteadyState then
      PS.map({L[1],L[1],L0[1]}).*der(i1) + omega[2]*L[1]*j(i1) + R[1]*i1 = v1 - v0;
      PS.map({L[2],L[2],L0[2]}).*der(i2) + omega[2]*L[2]*j(i2) + R[2]*i2 = v2 - v0;
      gp*(der(psi0) + omega[2]*j(psi0)) = v0;
    else
      omega[2]*L[1]*j(i1) + R[1]*i1 = v1 - v0;
      omega[2]*L[2]*j(i2) + R[2]*i2 = v2 - v0;
      gp*(omega[2]*j(psi0)) = v0;
    end if;
  end TrafoSatEff;

  model TrafoSat "Saturation transformer, 3-phase dq0"
    extends Partials.TrafoSatBase;

    PS.Voltage[3] v0;
    PS.Current[3] imag;
    PS.Current[3] iedc;
  protected
    Real[3] psi0(each stateSelect=StateSelect.prefer) "unsaturated flux";
    Real[3] gp;
    Real[3,3] Park=Utilities.Transforms.park(term_p.theta[2]);
    function der_sat = Common.IronSaturation.der_saturationAnalytic;

  initial equation
    if dynType == Types.Dynamics.SteadyInitial then
      der(i1) = omega[1]*j_dq0(i1);
      der(i2) = omega[1]*j_dq0(i2);
      der(psi0) = omega[1]*j_dq0(psi0);
    elseif dynType <> Types.Dynamics.SteadyState then
      der(psi0) = omega[1]*j_dq0(psi0);
    end if;

  equation
    i1 + i2 = imag + iedc;
    Redc*iedc = v0;
    psi0 = Lm*imag;
    gp = der_sat(transpose(Park)*psi0/psi_nom, c_sat);

    diagonal({L[1],L[1],L0[1]})*der(i1) + omega[2]*L[1]*j_dq0(i1) + R[1]*i1 = v1 - v0;
    diagonal({L[2],L[2],L0[2]})*der(i2) + omega[2]*L[2]*j_dq0(i2) + R[2]*i2 = v2 - v0;
    Park*diagonal(gp)*transpose(Park)*(der(psi0) + omega[2]*j_dq0(psi0)) = v0;
  end TrafoSat;

  model Trafo3Stray
    extends Partials.Trafo3StrayBase;

  initial equation
    if dynType == Types.Dynamics.SteadyInitial then
      der(i2a) = omega[1]*j(i2a);
      der(i2b) = omega[1]*j(i2b);
    end if;

  equation
    i1 + i2a + i2b = zeros(PS.n);
    if dynType <> Types.Dynamics.SteadyState then
      PS.map({L[1],L[1],L0[1]}).*der(i1) + omega[2]*L[1]*j(i1) + R[1]*i1 = v1 - v0;
      PS.map({L[2],L[2],L0[2]}).*der(i2a) + omega[2]*L[2]*j(i2a) + R[2]*i2a = v2a - v0;
      PS.map({L[3],L[3],L0[3]}).*der(i2b) + omega[2]*L[3]*j(i2b) + R[3]*i2b = v2b - v0;
    else
      omega[2]*L[1]*j(i1) + R[1]*i1 = v1 - v0;
      omega[2]*L[2]*j(i2a) + R[2]*i2a = v2a - v0;
      omega[2]*L[3]*j(i2b) + R[3]*i2b = v2b - v0;
    end if;
  end Trafo3Stray;

  package Partials "Partial models"
    extends Modelica.Icons.BasesPackage;

      partial model TrafoIdealBase "Base for ideal transformer, 3-phase dq0"

        extends Ports.YDportTrafo_p_n(
          final term_p(v(start=PS.map({cos(system.alpha0),sin(system.alpha0),0}*par.V_nom[1]))),
          final term_n(v(start=PS.map({cos(system.alpha0),sin(system.alpha0),0}*par.V_nom[2]))),
          i1(start=i1_start), i2(start=i2_start));

        parameter Types.Dynamics dynType=system.dynType "transient or steady-state model"
        parameter PS.Current[PS.n] i1_start = zeros(PS.n)
        "start value of primary current"
        parameter PS.Current[PS.n] i2_start = i1_start
        "start value of secondary current"

        parameter Boolean use_tap_1_in = false "= true to enable input tap_1_in"
        parameter Integer tap_1 = par.tap_neutral[1] "fixed tap_1 position"
        parameter Boolean use_tap_2_in = false "= true to enable input tap_2_in"
        parameter Integer tap_2 = par.tap_neutral[2] "fixed tap_2 position"
        parameter Boolean dynTC=false "enable dynamic tap-changing"

        Modelica.Blocks.Interfaces.IntegerInput tap_1_in if use_tap_1_in
        "1: index of voltage level"
        Modelica.Blocks.Interfaces.IntegerInput tap_2_in if use_tap_2_in
        "2: index of voltage level"

        replaceable record Data =
          PowerSystems.AC3ph.Transformers.Parameters.TrafoIdeal
          constrainedby PowerSystems.AC3ph.Transformers.Parameters.TrafoIdeal
        "trafo parameters"
        final parameter Data par
    protected
        Modelica.Blocks.Interfaces.IntegerInput tap_1_internal
        "Needed to connect to conditional connector";
        Modelica.Blocks.Interfaces.IntegerInput tap_2_internal
        "Needed to connect to conditional connector";

        outer System system;
        constant Real tc=0.01 "time constant tap-chg switching";
        final parameter PS.Voltage[2] V_base=Utilities.Precalculation.baseTrafoV(
                                                                             par.puUnits, par.V_nom);
      final parameter SI.Resistance[2,2] RL_base=
          Utilities.Precalculation.baseTrafoRL(
                par.puUnits,
                par.V_nom,
                par.S_nom,
                2*pi*par.f_nom);
        final parameter SI.Resistance R_n1=par.r_n[1]*RL_base[1,1];
        final parameter SI.Resistance R_n2=par.r_n[2]*RL_base[2,1];
        SI.AngularFrequency[2] omega;
        Real[2] dv_tap_pu = par.dv_tap .* V_base ./ par.V_nom;
        Real w1_set = (1 + (tap_1_internal - par.tap_neutral[1]) * dv_tap_pu[1]) * sqrt(scale[1])
          "1: set voltage ratio to nominal primary";
        Real w2_set = (1 + (tap_2_internal - par.tap_neutral[2])*dv_tap_pu[2]) * sqrt(scale[2])
          "2: set voltage ratio to nominal secondary";

      initial equation
        if dynTC then
          w1 = w1_set;
          w2 = w2_set;
        end if;

      equation
        connect(tap_1_in, tap_1_internal);
        connect(tap_2_in, tap_2_internal);
        if not use_tap_1_in then
           tap_1_internal = tap_1;
        end if;
        if not use_tap_2_in then
           tap_2_internal = tap_2;
        end if;

        omega = der(term_p.theta);
        if dynType <> Types.Dynamics.SteadyState and dynTC then
          der(w1) + (w1 - w1_set)/tc = 0;
          der(w2) + (w2 - w2_set)/tc = 0;
        else
          w1 = w1_set;
          w2 = w2_set;
        end if;
        w2_nom = par.V_nom[2]/par.V_nom[1];

        v_n1 = R_n1*i_n1
        "1: equation neutral to ground (relevant if Y-topology)";
        v_n2 = R_n2*i_n2
        "2: equation neutral to ground (relevant if Y-topology)";
      end TrafoIdealBase;

    partial model TrafoStrayBase
      "Base for ideal magnetic coupling transformer, 3-phase dq0"
      extends TrafoIdealBase(redeclare replaceable record Data =
          PowerSystems.AC3ph.Transformers.Parameters.TrafoStray
          constrainedby PowerSystems.AC3ph.Transformers.Parameters.TrafoStray);
    protected
      SI.Resistance[2] R=par.r.*RL_base[:, 1];
      SI.Inductance[2] L=par.x.*RL_base[:, 2];
      SI.Inductance[2] L0=par.x0.*RL_base[:, 2];
    end TrafoStrayBase;

    partial model TrafoMagBase
      "Base for magnetic coupling transformer, 3-phase dq0"
      extends TrafoStrayBase(redeclare replaceable record Data =
          PowerSystems.AC3ph.Transformers.Parameters.TrafoMag
          constrainedby PowerSystems.AC3ph.Transformers.Parameters.TrafoMag);
    protected
      final parameter SI.Resistance[2] RL12_base = sqrt((RL_base[1,:].*RL_base[2,:])/product(scale));
      SI.Resistance Redc=par.redc*RL12_base[1];
      SI.Inductance Lm=par.xm*RL12_base[2];
      SI.Inductance Lm0=par.xm0*RL12_base[2];
    end TrafoMagBase;

    partial model TrafoSatBase "Base for saturation transformer, 3-phase dq0"
      extends TrafoMagBase(redeclare replaceable record Data =
          PowerSystems.AC3ph.Transformers.Parameters.TrafoSat
          constrainedby PowerSystems.AC3ph.Transformers.Parameters.TrafoSat);
    protected
      Real xratio=par.xm_sat/par.xm;
      Real[3] c_sat={1-xratio,(1-xratio)/(par.psi_sat-xratio),xratio};
      final parameter SI.MagneticFlux psi_nom=sqrt(scale[1])*par.V_nom[1]/(2*pi*par.f_nom)
        "amplitude!";

    end TrafoSatBase;

  partial model Trafo3IdealBase
      "Base for ideal 3-winding transformer, 3-phase dq0"

    extends Ports.YDportTrafo_p_n_n(
      final term_p(v(start=PS.map({cos(system.alpha0),sin(system.alpha0),0}*par.V_nom[1]))),
      final term_na(v(start=PS.map({cos(system.alpha0),sin(system.alpha0),0}*par.V_nom[2]))),
      final term_nb(v(start=PS.map({cos(system.alpha0),sin(system.alpha0),0}*par.V_nom[3]))));

    parameter Types.Dynamics dynType=system.dynType "transient or steady-state model"

    parameter Boolean use_tap_1_in = false "= true to enable input tap_1_in"
    parameter Integer tap_1 = par.tap_neutral[1] "fixed tap_1 position"
    parameter Boolean use_tap_2_in = false "= true to enable input tap_2_in"
    parameter Integer[2] tap_2 = par.tap_neutral[2:3] "fixed tap_2 position"
    parameter Boolean dynTC=false "enable dynamic tap-changing"

    Modelica.Blocks.Interfaces.IntegerInput tap_1_in if  use_tap_1_in
        "1: index of voltage level"
    Modelica.Blocks.Interfaces.IntegerInput[2] tap_2_in if  use_tap_2_in
        "2: indices of voltage levels"

    replaceable record Data =
      PowerSystems.AC3ph.Transformers.Parameters.Trafo3Ideal
      constrainedby PowerSystems.AC3ph.Transformers.Parameters.Trafo3Ideal
        "trafo parameters"
    final parameter Data par "trafo parameter record"
    protected
    Modelica.Blocks.Interfaces.IntegerInput tap_1_internal
        "Needed to connect to conditional connector";
    Modelica.Blocks.Interfaces.IntegerInput[2] tap_2_internal
        "Needed to connect to conditional connector";

    outer System system;
    constant Real tc=0.01 "time constant tap-chg switching";
    final parameter PS.Voltage[3] V_base=Utilities.Precalculation.baseTrafoV(
                                                                         par.puUnits, par.V_nom);
      final parameter SI.Resistance[3,2] RL_base=
          Utilities.Precalculation.baseTrafoRL(
            par.puUnits,
            par.V_nom,
            par.S_nom,
            2*pi*par.f_nom);
    final parameter SI.Resistance R_n1=par.r_n[1]*RL_base[1,1];
    final parameter SI.Resistance R_n2a=par.r_n[2]*RL_base[2,1];
    final parameter SI.Resistance R_n2b=par.r_n[3]*RL_base[3,1];
    SI.AngularFrequency omega[2];
    Real[3] dv_tap_pu = par.dv_tap .* V_base ./ par.V_nom;
    Real w1_set=(1 + (tap_1_internal - par.tap_neutral[1]) * dv_tap_pu[1]) * sqrt(scale[1])
        "1: set voltage ratio to nominal primary";
    Real w2a_set = (1 + (tap_2_internal[1] - par.tap_neutral[2]) * dv_tap_pu[2]) * sqrt(scale[2])
        "2a: set voltage ratio to nominal secondary";
    Real w2b_set = (1 + (tap_2_internal[2] - par.tap_neutral[3]) * dv_tap_pu[3]) * sqrt(scale[3])
        "2b: set voltage ratio to nominal secondary";

  initial equation
    if dynTC then
      w1 = w1_set;
      w2a = w2a_set;
      w2b = w2b_set;
    end if;

  equation
    connect(tap_1_in, tap_1_internal);
    connect(tap_2_in, tap_2_internal);
    if not use_tap_1_in then
       tap_1_internal = tap_1;
    end if;
    if not use_tap_2_in then
       tap_2_internal = tap_2;
    end if;

    omega = der(term_p.theta);
    if dynType <> Types.Dynamics.SteadyState and dynTC then
      der(w1) + (w1 - w1_set)/tc = 0;
      der(w2a) + (w2a - w2a_set)/tc = 0;
      der(w2b) + (w2b - w2b_set)/tc = 0;
    else
      w1 = w1_set;
      w2a = w2a_set;
      w2b = w2b_set;
    end if;
    w2a_nom = par.V_nom[2]/par.V_nom[1];
    w2b_nom = par.V_nom[3]/par.V_nom[1];

    v_n1 = R_n1*i_n1 "1: equation neutral to ground (relevant if Y-topology)";
    v_n2a = R_n2a*i_n2a
        "2a: equation neutral to ground (relevant if Y-topology)";
    v_n2b = R_n2b*i_n2b
        "2b: equation neutral to ground (relevant if Y-topology)";
  end Trafo3IdealBase;

  partial model Trafo3StrayBase
      "Base for ideal magnetic coupling 3-winding transformer, 3-phase dq0"
    extends Trafo3IdealBase(redeclare replaceable record Data =
        PowerSystems.AC3ph.Transformers.Parameters.Trafo3Stray
        constrainedby PowerSystems.AC3ph.Transformers.Parameters.Trafo3Stray);
    protected
    SI.Resistance[3] R=par.r.*RL_base[:, 1];
    SI.Inductance[3] L=par.x.*RL_base[:, 2];
    SI.Inductance[3] L0=par.x0.*RL_base[:, 2];
  end Trafo3StrayBase;

  end Partials;

package Parameters "Parameter data for interactive use"
  extends Modelica.Icons.MaterialPropertiesPackage;

record TrafoIdeal "Parameters for ideal transformer, 3-phase"
  extends Common.Nominal.NominalDataTrafo;

  Integer[2] tap_neutral={0, 0} "{1,2}: neutral tap position"
  SIpu.Voltage[2] dv_tap={0, 0} "{1,2}: delta-v per tap change"
  SIpu.Resistance[2] r_n={1, 1} "{1,2}: resistance neutral to grd (if Y)"
end TrafoIdeal;

record TrafoStray "Parameters for ideal magnetic coupling transformer, 3-phase"
  extends TrafoIdeal;

  SIpu.Resistance[2] r={0.05,0.05} "{1,2}: resistance"
  SIpu.Reactance[2] x={0.05,0.05} "{1,2}: stray reactance"
  SIpu.Reactance[2] x0={x[1],x[2]} "{1,2}: stray reactance zero-comp"
end TrafoStray;

record TrafoMag "Parameters for magnetic coupling transformer, 3-phase"
  extends TrafoStray;

  SIpu.Resistance redc=500 "resistance eddy current"
  SIpu.Reactance xm=500 "mutual reactance"
  SIpu.Reactance xm0=1 "mutual reactance zero"
end TrafoMag;

record TrafoSat "Parameters for saturation transformer, 3-phase"
  extends TrafoMag;

  Real psi_sat(unit="1")=1.5 "saturation flux"
  SIpu.Reactance xm_sat=1 "mutual reactance saturated"
end TrafoSat;

record Trafo3Ideal "Parameters for ideal 3-winding transformer, 3-phase"
  extends Common.Nominal.NominalDataTrafo(
                                         V_nom={1,1,1}
          "{prim,sec_a,sec_b} nom Voltage (= base if pu)");

  Integer[3] tap_neutral={0, 0, 0} "{1,2a,2b}: neutral tap position"
  SIpu.Voltage[3] dv_tap={0, 0, 0} "{1,2a,2b}: delta-v per tap change"
  SIpu.Resistance[3] r_n={1, 1, 1} "{1,2a,2b}: resistance neutral to grd (if Y)"
end Trafo3Ideal;

record Trafo3Stray
      "Parameters for ideal magnetic coupling 3-winding transformer, 3-phase"
  extends Trafo3Ideal;

  SIpu.Resistance[3] r={0.05,0.05,0.05} "{1,2a,2b}: resistance"
  SIpu.Reactance[3] x={0.05,0.05,0.05} "{1,2a,2b}: stray reactance"
  SIpu.Reactance[3] x0={x[1],x[2],x[3]} "{1,2a,2b}: stray reactance zero-comp"
end Trafo3Stray;
end Parameters;
end Transformers;


Auxiliary blocks

within PowerSystems.Blocks;
package Math "Auxiliary blocks"
  extends Modelica.Icons.VariantsPackage;

block Integrator "Integral of input-signal"
  extends Partials.SISO(y(start=y_start, fixed=true));

  parameter Real y_start=0 "initial value";

equation
  der(y) = u;
end Integrator;

  block TimeAverage "Time average of input signal"
    extends Partials.MIMO(final nin=n, final nout=n);

    parameter Integer n=1 "dim of input/output signal";
    parameter SI.Time tcst(min=Modelica.Constants.eps)=1 "memory time constant";

  initial equation
    y = u;

  equation
    der(y) = (u - y)/tcst;
  end TimeAverage;

  block TimeAvInterval "Time average over interval of input signal"
    extends Partials.MIMO(final nin=n, final nout=n);

    parameter Integer n=1 "dim of input/output signal";
    parameter SI.Time tcst(min=1e-9)=1 "average time";
  protected
    Real[n] U(start=zeros(n), fixed=true, each stateSelect=StateSelect.always);

  equation
    der(U) = u;
    y = (U - delay(U, tcst))/tcst;
  end TimeAvInterval;

  block ComponentAverage "Component average of input signal"
    extends Partials.MISO;

  algorithm
    y := sum(u)/n;
  end ComponentAverage;

  block Norm "Norm of input signal"
    extends Partials.MISO(n=3);

    parameter Integer n_eval(
      min=2,
      max=3) = 2 "dim of evaluated input signal u[1:n_eval]";

  equation
    y = sqrt(u[1:n_eval]*u[1:n_eval]);
  end Norm;

  block ToPolar "Rotation of input signal-vector"
    extends Partials.MIMO(final nin=2, final nout=2);

    parameter Integer sig(min=-1, max=1)=1 "+u or -u as input"
  protected
    function atan2=Modelica.Math.atan2;

  equation
    y[1] = sqrt(u*u);
    y[2] = atan2(sig*u[2], sig*u[1]);
  end ToPolar;

  block ToPolarR "Rotation of input signal-vector"
    extends Partials.MIMO(final nin=2, final nout=2);

    parameter Integer sig(min=-1, max=1)=1 "+u or -u as input"
  protected
    SI.Angle alpha(stateSelect=StateSelect.always);
    SI.AngularVelocity omega;
    function atan2=Modelica.Math.atan2;
    function atanV=Utilities.Math.atanVarCut;
    function angVelocity=Utilities.Math.angVelocity;

  initial equation
    alpha =  atan2(sig*u[2], sig*u[1]); // leads to troubles with GNU-compiler.

  equation
    omega =  angVelocity(u, der(u));
    der(alpha) = omega;
    y[1] = sqrt(u*u);
    y[2] = atanV(sig*u, alpha);
  end ToPolarR;

  block FromPolar "Rotation of input signal-vector"
    extends Partials.MIMO(final nin=2, final nout=2);

  equation
    y = u[1]*{cos(u[2]), sin(u[2])};
  end FromPolar;
end Math;