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;


Multiplexer and AD converter

within PowerSystems.Blocks;
package Multiplex "Multiplexer and AD converter"
  extends Modelica.Icons.VariantsPackage;

  block R2mux "Real multiplexer 2-fold"
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealInput u1[n[1]] "vector of dimension n[1]"
    Modelica.Blocks.Interfaces.RealInput u2[n[2]] "vector of dimension n[2]"
    Modelica.Blocks.Interfaces.RealOutput y[sum(n)]
      "vector of dimension sum(n)"
    parameter Integer n[2]={1,1} "dim of input signals"

  equation
    y = cat(1, u1, u2);
  end R2mux;

  block R3mux "Real multiplexer 3-fold"
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealInput u1[n[1]] "vector of dimension n[1]"
    Modelica.Blocks.Interfaces.RealInput u2[n[2]] "vector of dimension n[2]"
    Modelica.Blocks.Interfaces.RealInput u3[n[3]] "vector of dimension n[3]"
    Modelica.Blocks.Interfaces.RealOutput y[sum(n)]
      "vector of dimension sum(n)"
    parameter Integer n[3]={1,1,1} "dim of input signals"

  equation
    y = cat(1,u1, u2, u2);
  end R3mux;

  block R4mux "Real multiplexer 4-fold"
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealInput u1[n[1]] "vector of dimension n[1]"
    Modelica.Blocks.Interfaces.RealInput u2[n[2]] "vector of dimension n[2]"
    Modelica.Blocks.Interfaces.RealInput u3[n[3]] "vector of dimension n[3]"
    Modelica.Blocks.Interfaces.RealInput u4[n[4]] "vector of dimension n[4]"
    Modelica.Blocks.Interfaces.RealOutput y[sum(n)]
      "vector of dimension sum(n)"
    parameter Integer n[4]={1,1,1,1} "dim of input signals"

  equation
    y = cat(1, u1[1:n[1]], u2[1:n[2]], u3[1:n[3]], u4[1:n[4]]);
  end R4mux;

  block R2demux "Real demultiplexer 2-fold"
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealInput u[sum(n)] "vector of dimension sum(n)"
    Modelica.Blocks.Interfaces.RealOutput y1[n[1]] "vector of dimension n[1]"
    Modelica.Blocks.Interfaces.RealOutput y2[n[2]] "vector of dimension n[2]"
    parameter Integer n[2]={1,1} "dim of output signals"

  equation
    u = cat(1, y1, y2);
  end R2demux;

  block R3demux "Real demultiplexer 3-fold"
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealInput u[sum(n)] "vector of dimension sum(n)"
    Modelica.Blocks.Interfaces.RealOutput y1[n[1]] "vector of dimension n[1]"
    Modelica.Blocks.Interfaces.RealOutput y2[n[2]] "vector of dimension n[2]"
    Modelica.Blocks.Interfaces.RealOutput y3[n[3]] "vector of dimension n[3]"
    parameter Integer n[3]={1,1,1} "dim of output signals"

  equation
    u = cat(1, y1, y2, y3);
  end R3demux;

  block R4demux "Real demultiplexer 4-fold"
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealInput u[sum(n)] "vector of dimension sum(n)"
    Modelica.Blocks.Interfaces.RealOutput y1[n[1]] "vector of dimension n[1]"
    Modelica.Blocks.Interfaces.RealOutput y2[n[2]] "vector of dimension n[2]"
    Modelica.Blocks.Interfaces.RealOutput y3[n[3]] "vector of dimension n[3]"
    Modelica.Blocks.Interfaces.RealOutput y4[n[4]] "vector of dimension n[4]"
    parameter Integer n[4]={1,1,1,1} "dim of output signals"

  equation
    u = cat(1, y1, y2, y3, y4);
  end R4demux;

  block B2mux "Boolean multiplexer 2-fold "
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.BooleanInput u1[n[1]] "vector of dimension n[1]"
    Modelica.Blocks.Interfaces.BooleanInput u2[n[2]] "vector of dimension n[2]"
    Modelica.Blocks.Interfaces.BooleanOutput y[sum(n)]
      "vector of dimension sum(n)"
    parameter Integer n[2]={1,1} "dim of input signals"

  equation
    y = cat(1, u1, u2);
  end B2mux;

  block B2demux "Boolean demultiplexer 2-fold "
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.BooleanInput u[sum(n)]
      "vector of dimension sum(n)"
    Modelica.Blocks.Interfaces.BooleanOutput y1[n[1]]
      "vector of dimension n[1]"
    Modelica.Blocks.Interfaces.BooleanOutput y2[n[2]]
      "vector of dimension n[2]"
    parameter Integer n[2]={1,1} "dim of output signals"

  equation
    u = cat(1, y1, y2);
  end B2demux;

  block Gate2demux "Boolean demultiplexer 2-fold"
    extends PowerSystems.Icons.Block;

    Modelica.Blocks.Interfaces.BooleanInput gates[2*n]
      "vector of dimension 2*n"
    Modelica.Blocks.Interfaces.BooleanOutput gates_1[n] "vector of dimension n"
    Modelica.Blocks.Interfaces.BooleanOutput gates_2[n] "vector of dimension n"
    parameter Integer n=1 "number of pairs out"

  equation
    gates_1[1:n] = gates[1:n];
    gates_2[1:n] = gates[(n + 1):(n + n)];
  end Gate2demux;

  block Gate3demux "Boolean demultiplexer 3-fold "
    extends PowerSystems.Icons.Block;

    Modelica.Blocks.Interfaces.BooleanInput gates[3*n]
      "vector of dimension 3*n"
    Modelica.Blocks.Interfaces.BooleanOutput gates_a[n] "vector of dimension n"
    Modelica.Blocks.Interfaces.BooleanOutput gates_b[n] "vector of dimension n"
    Modelica.Blocks.Interfaces.BooleanOutput gates_c[n] "vector of dimension n"
    parameter Integer n=1 "number of triples out"

  equation
    gates_a[1:n] = gates[1:n];
    gates_b[1:n] = gates[(n + 1):2*n];
    gates_c[1:n] = gates[(2*n + 1):3*n];
  end Gate3demux;

  block AD1ph "Analog-Digital converter 1-phase"
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealInput p "scalar analog p-signal"
    Modelica.Blocks.Interfaces.RealInput n "scalar analog n-signal"
    Modelica.Blocks.Interfaces.BooleanOutput gates[2] "boolean {p, n}-gate"

  equation
    gates[1] = if p > 0.5 then true else false;
    gates[2] = if n > 0.5 then true else false;
  end AD1ph;

  block DA1ph "Digital-Analog converter 1-phase"
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.BooleanInput gates[2] "boolean {p, n}-gate"
    Modelica.Blocks.Interfaces.RealOutput p "scalar analog p-signal"
    Modelica.Blocks.Interfaces.RealOutput n "scalar analog n-signal"

  equation
    p = if gates[1] then 1 else 0;
    n = if gates[2] then 1 else 0;
  end DA1ph;

  block AD3ph "Analog-Digital converter 3-phase"
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealInput a_p "scalar analog p-signal phase a"
    Modelica.Blocks.Interfaces.RealInput a_n "scalar analog n-signal phase a"
    Modelica.Blocks.Interfaces.RealInput b_p "scalar analog p-signal phase b"
    Modelica.Blocks.Interfaces.RealInput b_n "scalar analog n-signal phase b"
    Modelica.Blocks.Interfaces.RealInput c_p "scalar analog p-signal phase c"
    Modelica.Blocks.Interfaces.RealInput c_n "scalar analog n-signal phase c"
    Modelica.Blocks.Interfaces.BooleanOutput gates[6]
      "boolean {a_p,a_n, b_p,b_n, c_p,c_n,}-gate"

  equation
    gates[1] = if a_p > 0.5 then true else false;
    gates[2] = if a_n > 0.5 then true else false;
    gates[3] = if b_p > 0.5 then true else false;
    gates[4] = if b_n > 0.5 then true else false;
    gates[5] = if c_p > 0.5 then true else false;
    gates[6] = if c_n > 0.5 then true else false;
  end AD3ph;

  block DA3ph "Digital-Analog converter 3-phase"
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.BooleanInput gates[6]
      "boolean {a_p,a_n, b_p,b_n, c_p,c_n,}-gate"
    Modelica.Blocks.Interfaces.RealOutput a_p "scalar analog p-signal phase a"
    Modelica.Blocks.Interfaces.RealOutput a_n "scalar analog n-signal phase a"
    Modelica.Blocks.Interfaces.RealOutput b_p "scalar analog p-signal phase b"
    Modelica.Blocks.Interfaces.RealOutput b_n "scalar analog n-signal phase b"
    Modelica.Blocks.Interfaces.RealOutput c_p "scalar analog p-signal phase c"
    Modelica.Blocks.Interfaces.RealOutput c_n "scalar analog n-signal phase c"

  equation
    a_p = if gates[1] then 1 else 0;
    a_n = if gates[2] then 1 else 0;
    b_p = if gates[3] then 1 else 0;
    b_n = if gates[4] then 1 else 0;
    c_p = if gates[5] then 1 else 0;
    c_n = if gates[6] then 1 else 0;
  end DA3ph;
end Multiplex;


Blocks

within PowerSystems;
package Blocks "Blocks"
  extends Modelica.Icons.Package;
end Blocks;


Partial models

within PowerSystems.Blocks;
package Partials "Partial models"
  extends Modelica.Icons.BasesPackage;

  partial block SO
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealOutput y "output signal"
  end SO;

  partial block MO
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealOutput[n] y "output signal-vector"
    parameter Integer n=1 "dim of output signal-vector";
  end MO;

  partial block SISO
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealInput u "input signal"
    Modelica.Blocks.Interfaces.RealOutput y "output signal"
  end SISO;

  partial block MISO
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealInput[n] u "input signal-vector"
    Modelica.Blocks.Interfaces.RealOutput y "output signal"
    parameter Integer n=1 "dim of input signal-vector";
  end MISO;

  partial block MIMO
    extends PowerSystems.Icons.Block0;

    Modelica.Blocks.Interfaces.RealInput u[nin] "input signal-vector"
    Modelica.Blocks.Interfaces.RealOutput y[nout] "output signal vector"
    parameter Integer nin=1 "dim of input signal-vector";
    parameter Integer nout=1 "dim of output signal-vector";
  end MIMO;
end Partials;


Special signals

within PowerSystems.Blocks;
package Signals "Special signals"
  extends Modelica.Icons.VariantsPackage;

  block Constant "Constant vector"
    extends Partials.SO;

    parameter Real c=1 "constant";

  equation
    y = c;
  end Constant;

  block Transient "Transient vector"
    extends Partials.SO;

    parameter SI.Time t_change=0.5 "time when change";
    parameter SI.Time t_duration=1 "transition duration";
    parameter Real s_start=1 "initial value";
    parameter Real s_end=1 "final value";
  protected
    final parameter SI.Frequency coef=2*exp(1)/t_duration;

  equation
    y = 0.5*((s_end + s_start) + (s_end - s_start)*tanh(coef*(time - t_change)));
  end Transient;

  block ConstantPhasor "Constant {norm, phase} of vector"
    extends Partials.MO(final n=2);

    parameter Real a=1 "norm |y|";
    parameter SI.Angle ph=0 "phase (y)";

  equation
    y= {a, ph};
  end ConstantPhasor;

  block TransientPhasor "Transient {norm, phase} of vector"
    extends Partials.MO(final n=2);

    parameter SI.Time t_change=0.5 "time when change";
    parameter SI.Time t_duration=1 "transition duration";
    parameter Real a_start=1 "initial norm |y|";
    parameter Real a_end=1 "final norm |y|";
    parameter SI.Angle ph_start=0 "initial phase (y)";
    parameter SI.Angle ph_end=0 "final phase (y)";
  protected
    final parameter SI.Frequency coef=2*exp(1)/t_duration;

  equation
    y = 0.5*({a_end+a_start, ph_end+ph_start} + {a_end-a_start, ph_end-ph_start}*tanh(coef*(time - t_change)));
  end TransientPhasor;

  block ConstantFreq "Constant frequency"
    extends Constant(final c=2*pi*f);

    parameter SI.Frequency f=system.f "frequency";
  protected
    outer System system;
  end ConstantFreq;

  block TransientFreq "Transient frequency"
    extends Transient(final s_start=omega_start, final s_end=omega_end);

    parameter SI.Frequency f_start=system.f "initial frequency";
    parameter SI.Frequency f_end=system.f "final frequency";
  protected
    outer System system;
    final parameter SI.AngularFrequency omega_start=2*pi*f_start;
    final parameter SI.AngularFrequency omega_end=2*pi*f_end;
  end TransientFreq;

  block Sin "Sine of n phases"
    extends Partials.MO;

    parameter Real a=1 "signal amplitude";
    parameter SI.Frequency f=50 "frequency";
    parameter SI.Angle phComm=0 "common phase angle";
    parameter SI.Angle[n] phShift=(0:n-1)*2*pi/n "phase shift";
  protected
    final parameter SI.AngularFrequency omega=2*pi*f;
    final parameter SI.Angle[n] ph=fill(phComm, n) - phShift;

  equation
    y = a*sin(omega*time*ones(n) + ph);
  end Sin;

  block VarSin "Sine of n phases with variable amplitude and frequency"
    extends Partials.MO;

    parameter SI.Angle phComm=0 "common phase angle";
    parameter SI.Angle[n] phShift=(0:n-1)*2*pi/n "phase shift";
    Modelica.Blocks.Interfaces.RealInput amplitude "signal amplitude"
    Modelica.Blocks.Interfaces.RealInput omega "angular frequency"
  protected
    final parameter SI.Angle[n] ph=fill(phComm, n) - phShift;
    SI.Angle theta(start=0, fixed=true, stateSelect=StateSelect.always);

  equation
    der(theta) = omega;
    y = amplitude*sin(fill(theta, n) + ph);
  end VarSin;

  block VarSinSpec
    "Sine + harmonics of n phases with variable amplitude and frequency"
    extends Partials.MO;

    Modelica.Blocks.Interfaces.RealInput amplitude "common signal amplitude"
    Modelica.Blocks.Interfaces.RealInput omega "common angular frequency"
    parameter SI.Angle phComm=0 "common phase angle";
    parameter SI.Angle phShift[n]=(0:n-1)*2*pi/n "phase shift";
    parameter Integer N=3 "1 + number of harmonics";
    parameter Integer[N] h={1,3,5} "{1, ...} which harmonics?";
    parameter Real[N] a_rel={1,0.3,0.1} "rel amplitudes";
    parameter SI.Angle[N] ph_h={0,0,0} "rel phase angles harmonics";
  protected
    final parameter SI.Angle[n] ph=fill(phComm, n) - phShift;
    SI.Angle theta(start=0, fixed=true, stateSelect=StateSelect.always);

  equation
    der(theta) = omega;
    for k in 1:n loop
      y[k] = amplitude*(a_rel*sin((theta + ph[k])*h + ph_h));
    end for;
  end VarSinSpec;
end Signals;


Auxiliary blocks

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

  block Park "Park-transform of input signal-vector"
    extends Partials.MIMO(final nin=3, final nout=3);

    Modelica.Blocks.Interfaces.RealInput theta "transformation angle"
  protected
    constant Real s13=sqrt(1/3);
    constant Real s23=sqrt(2/3);
    constant Real dph_b=2*pi/3;
    constant Real dph_c=4*pi/3;
    Real[3] c;
    Real[3] s;

  equation
    c =  cos({theta, theta - dph_b, theta - dph_c});
    s =  sin({theta, theta - dph_b, theta - dph_c});
    y = transpose([s23*c, -s23*s, {s13, s13, s13}])*u;
  end Park;

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

    Modelica.Blocks.Interfaces.RealInput theta "rotation angle"
  protected
    Real c;
    Real s;

  equation
    c =   cos(theta);
    s =   sin(theta);
    y = [c, -s; s, c]*u;
  end Rotation_dq;

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

    Modelica.Blocks.Interfaces.RealInput theta "rotation angle"
  protected
    constant Real q13=1/3;
    constant Real q23=2/3;
    constant Real dph_b=2*pi/3;
    constant Real dph_c=4*pi/3;
    Real[3] g;

  equation
    g =  {q13, q13, q13} + q23*cos({theta, theta - dph_b, theta - dph_c});
    y =  [g[{1,2,3}], g[{3,1,2}], g[{2,3,1}]]*u;
  end Rotation_abc;

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

    Modelica.Blocks.Interfaces.RealInput theta "rotation angle"

  equation
    y = {u[1], u[2] + theta};
  end RotationPhasor;

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

    Modelica.Blocks.Interfaces.RealInput theta "rotation angle"
  protected
    constant Real s23=sqrt(2/3);

  equation
    y = sqrt(2/3)*u[1]*{cos(u[2] + theta), sin(u[2] + theta)};
  end PhasorToAlphaBeta;
end Transforms;


Iron saturation properties


Units and nominal values

within PowerSystems.Common;
package Nominal "Units and nominal values"
  extends Modelica.Icons.BasesPackage;

partial model Nominal "Units and nominal values"

  parameter Boolean puUnits = true
      "= true, if scaled with nom. values (pu), else scaled with 1 (SI)"

  parameter SI.Voltage V_nom(final min=0)=1 "nominal Voltage (= base for pu)"

  parameter SI.ApparentPower S_nom(final min=0)=1
      "nominal Power (= base for pu)"
end Nominal;

partial model NominalAC "Units and nominal values AC"
  extends Nominal;

  parameter SI.Frequency f_nom=system.f_nom "nominal frequency"
  protected
  outer PowerSystems.System system;
end NominalAC;

partial model NominalDC "Units and nominal values DC"
    import PowerSystems;
  extends Nominal;

  parameter SI.AngularVelocity w_nom=system.w_nom
    "nominal r.p.m."
  protected
  outer PowerSystems.System system;
end NominalDC;

partial model NominalVI "Nominal values"

  parameter SI.Voltage V_nom(final min=0)=1 "nom Voltage"
  parameter SI.Current I_nom(final min=0)=1 "nom Current"
end NominalVI;

record NominalData "Units and nominal data"
  extends Modelica.Icons.Record;

  Boolean puUnits = true
      "= true, if scaled with nom. values (pu), else scaled with 1 (SI)"
  SI.Voltage V_nom(final min=0)=1 "nominal Voltage (= base for pu)"
  SI.ApparentPower S_nom(final min=0)=1
      "nominal Power (= base for pu)"
end NominalData;

record NominalDataAC "Units and nominal data AC"
  extends NominalData;

  SI.Frequency f_nom=50 "nominal frequency"
end NominalDataAC;

record NominalDataDC "Units and nominal data DC"
    import PowerSystems;
  extends NominalData;

  SI.AngularVelocity w_nom=314.159265358979323846
    "nominal r.p.m."
end NominalDataDC;

record NominalDataVI "Units and nominal data"
  extends Modelica.Icons.Record;

  SI.Voltage V_nom(final min=0)=1 "nom Voltage"
  SI.Current I_nom(final min=0)=1 "nom Current"
end NominalDataVI;

record NominalDataTrafo "Units and nominal data transformer"
  extends Modelica.Icons.Record;

  Boolean puUnits = true
      "= true, if scaled with nom. values (pu), else scaled with 1 (SI)"
  SI.Voltage[:] V_nom(each final min=0)={1,1}
      "{prim,sec} nom Voltage (= base of pu)"
  SI.ApparentPower S_nom(final min=0)=1
      "nominal Power (= base of pu)"
  SI.Frequency f_nom=50 "nominal frequency"
end NominalDataTrafo;
end Nominal;


Common components

within PowerSystems;
package Common "Common components"
  extends Modelica.Icons.Package;
end Common;


Plasma arcs

within PowerSystems.Common;
package Plasma "Plasma arcs"
  extends Modelica.Icons.Package;

  model ArcBreaker "Arc voltage for breakers"
    extends Partials.ArcBase;

    parameter SI.ElectricFieldStrength E "av electric field arc";
    parameter Real r(unit="1/A") "= R0/(d*Earc), R0 small signal resistance";
    input SI.Distance d "contact distance";

  equation
    v = d*E*tanh(r*i);
  end ArcBreaker;

  model ArcFault "Arc voltage for faults"
    extends Partials.ArcBase;

    parameter SI.Voltage V "arc voltage";
    parameter Real r(unit="1/A") "= R0/V, R0 small signal resistance";

  equation
    v = V*tanh(r*i);
  end ArcFault;

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

    partial model ArcBase "Arc voltage base"

    /*
  SI.Voltage v;
  SI.Current i;
    InputVoltage v;
    InputCurrent i;
*/
        // Local connector definition, used for equation balancing check
        connector InputVoltage = input SI.Voltage;
      InputVoltage v;
      SI.Current i;

    end ArcBase;
  end Partials;
end Plasma;


Common switching components

within PowerSystems.Common;
package Switching "Common switching components"
  extends Modelica.Icons.Package;

model Switch "Switch kernel, no terminals"
  extends Partials.SwitchBase;

initial equation
  pre(open) = not closed;

equation
  when {open and i < 0, open and i > 0, closed} then
    arc = edge(open);
  end when;
  {v,i} = if closed or arc then {epsR*s,s} else {s,epsG*s};
end Switch;

model Breaker "Breaker kernel, no terminals"
  extends Partials.SwitchBase;

  parameter SI.Distance D=50e-3 "contact distance open";
  parameter SI.Time t_opening=20e-3 "opening duration";
  parameter SI.ElectricFieldStrength Earc=50e3 "electric field arc";
  parameter SI.Resistance R0=1 "small signal resistance arc";
  replaceable Plasma.ArcBreaker arcBreaker(E=Earc, r=R0/(D*Earc), d=d)
  protected
  SI.Voltage v_arc;
  SI.Current i_arc;
  SI.Time t0(start=Modelica.Constants.inf, fixed=true) "start opening";
  SI.Distance d "contact distance";
  Boolean opening(start=false, fixed=true);

initial equation
  pre(open) = not closed;

equation
  arcBreaker.v=v_arc;
  arcBreaker.i=i_arc;

  when {open and i < 0, open and i > 0, closed} then
    arc = edge(open) or opening;
  end when;
  when pre(arc) then
    t0 = time;
  end when;
  opening = t0 < time and time < t0 + t_opening;
  d = if opening then ((time - t0)/t_opening)^2*D else D
      "d not needed if closed (d=0)";
  i_arc = if arc then s else 0;
  {v,i} = if closed then {epsR*s,s} else if arc then {v_arc,i_arc} else {s,epsG*s};
end Breaker;

model Short "Short kernel optionally with exponential relaxation, no terminals"
  extends Partials.FaultBase;

  parameter Boolean relax=false "use relaxation fct"
  parameter SI.Time t_c=10e-3 "relaxation time constant"
  parameter Real beta(min=2)=4 "power of exponent"
  SI.Time t0(start=-Modelica.Constants.inf, fixed=true);
  Real[2] r;
  function relaxation=Utilities.Math.relaxation;

equation
  when edge(on) then
    t0 = time;
  end when;
  if relax then
    r = relaxation(time - t0, t_c, beta);
    {v,i} = if on then {(r[1] + r[2]*epsR)*s,s} else {s,(r[1]*epsG + r[2])*s};
  else
    r = {0,0};
    {v,i} = if on then {epsR*s,s} else {s,epsG*s};
  end if;
end Short;

model Fault "Line fault kernel, no terminals"
  extends Partials.FaultBase;

  parameter SI.Voltage Varc=100 "arc voltage";
  parameter SI.Resistance R0=1 "small-signal resistance";
  parameter SI.Current Iclear=10 "self clearing current";
  parameter SI.Time t_c=1e-3 "cooling time constant";
  Boolean arc(start=false, fixed=true);
  protected
  final parameter Real r=(R0/Varc);
  final parameter Real Qclear=Iclear*Varc*t_c;
  final parameter Real Qini=100*Qclear;
  SI.Voltage v_arc;
  SI.Current i_arc;
  Real Q(start=0, fixed=true);
  Boolean cleared(start=true, fixed=true);
  replaceable Plasma.ArcFault arcFault(
    V=Varc,
    r=R0/Varc)

equation
  arcFault.v = v_arc;
  arcFault.i = i_arc;

  when edge(on) then
    reinit(Q, Qini);
  end when;
  when {on, cleared} then
    arc = edge(on);
  end when;
  if arc then
    i_arc = s;
    {v,i} = {v_arc,i_arc};
    der(Q) = v_arc*i_arc - Q/t_c;
  else
    i_arc = 0;
    {v,i} = {s,epsG*s};
    der(Q) = 0;
  end if;
  cleared = Q < Qclear;
end Fault;

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

partial model SwitchBase "Switch base kernel, no terminals"

  parameter SI.Resistance epsR=1e-5 "resistance 'closed'";
  parameter SI.Conductance epsG=1e-5 "conductance 'open'";
  connector InputVoltage = input SI.Voltage;
  InputVoltage v;
  SI.Current i;
  Boolean arc(start=false, fixed=true) "arc on";
  Boolean open(start=true)=not closed;
  Modelica.Blocks.Interfaces.BooleanInput closed(start=false)
        "true:closed, false:open"
    protected
  Real s(start = 0.5);

end SwitchBase;

partial model FaultBase "Fault kernel base"

  parameter SI.Resistance epsR=1e-5 "resistance 'short'";
  parameter SI.Conductance epsG=1e-5 "conductance 'open'";
  connector InputVoltage = input SI.Voltage;
  InputVoltage v;
  SI.Current i;
  input Boolean on(start=false, fixed=true);
    protected
  Real s(start = 0.5);

end FaultBase;
end Partials;
end Switching;


Thermal boundary and adaptors

within PowerSystems.Common;
package Thermal "Thermal boundary and adaptors"
  extends Modelica.Icons.VariantsPackage;

  model BdCond "Default (Neumann) boundary condition, scalar port"
    extends Partials.BdCondBase;

    Interfaces.Thermal_p heat "heat port"

  equation
    heat.T = T_amb;
  end BdCond;

  model BdCondV "Default (Neumann) boundary condition, vector port"

    parameter Integer m(final min=1)=1 "dimension of heat port";
    extends Partials.BdCondBase;
    PowerSystems.Interfaces.ThermalV_p heat(
                                    final m=m) "vector heat port"

  equation
    heat.ports.T = fill(T_amb, heat.m);
  end BdCondV;

  model Boundary "Boundary model, scalar port"
    extends Partials.BoundaryBase;

    output SI.HeatFlowRate Q_flow;
    output SI.HeatFlowRate Qav_flow=q if av;
    PowerSystems.Interfaces.Thermal_p heat "heat port"
  protected
    SI.HeatFlowRate q;

  equation
    heat.T = T;
    Q_flow = heat.Q_flow;

    if ideal then
      T = T_amb;
    else
      C*der(T) = Q_flow - G*(T - T_amb);
    end if;

    if av then
      der(q) = (Q_flow - q)/tcst;
    else
      q = 0;
    end if;
  end Boundary;

  model BoundaryV "Boundary model, vector port"

    parameter Boolean add_up=true "add up Q_flow at equal T";
    parameter Integer m(final min=1)=1 "dimension of heat port";
    extends Partials.BoundaryBase;
    output SI.HeatFlowRate[if add_up then 1 else m] Q_flow;
    output SI.HeatFlowRate[if add_up then 1 else m] Qav_flow=q if av;
    PowerSystems.Interfaces.ThermalV_p heat(
                                    final m=m) "vector heat port"
  protected
    SI.HeatFlowRate[if add_up then 1 else m] q;

  equation
    heat.ports.T = fill(T, heat.m);

    if add_up then
       Q_flow = {sum(heat.ports.Q_flow)};
    else
       Q_flow[1:m] = heat.ports.Q_flow;
    end if;

    if ideal then
      T = T_amb;
    else
      C*der(T) = sum(Q_flow) - G*(T - T_amb);
    end if;

    if av then
      der(q) = (Q_flow - q)/tcst;
    else
      q = zeros(if add_up then 1 else m);
    end if;
  end BoundaryV;

  model Heat_a_b_ab "Adaptor 2 x Thermal (scalar) to ThermalV (vector)"

    PowerSystems.Interfaces.Thermal_p port_a "scalar port a"
    PowerSystems.Interfaces.Thermal_p port_b "scalar port b"
    PowerSystems.Interfaces.ThermalV_n port_ab(
                                       final m=2) "vector port {a,b}"

  equation
    {port_a.T, port_b.T} = port_ab.ports.T;
    {port_a.Q_flow, port_b.Q_flow} + port_ab.ports.Q_flow = zeros(2);
  end Heat_a_b_ab;

  model Heat_a_b_c_abc "Adaptor 3 x Thermal (scalar) to ThermalV (vector)"

    PowerSystems.Interfaces.Thermal_p port_a "scalar port a"
    PowerSystems.Interfaces.Thermal_p port_b "scalar port b"
    PowerSystems.Interfaces.Thermal_p port_c "scalar port c"
    PowerSystems.Interfaces.ThermalV_n port_abc(
                                        final m=3) "vector port {a,b,c}"

  equation
    {port_a.T, port_b.T, port_c.T} = port_abc.ports.T;
    {port_a.Q_flow, port_b.Q_flow, port_c.Q_flow} + port_abc.ports.Q_flow = zeros(3);
  end Heat_a_b_c_abc;

  model HeatV_a_b_ab "Adaptor 2 x ThermalV (vector) to ThermalV (vector)"

    parameter Integer[2] m={1,1} "dimension {port_a, port_b}";
    PowerSystems.Interfaces.ThermalV_p port_a(
                                      final m=m[1]) "vector port a"
    PowerSystems.Interfaces.ThermalV_p port_b(
                                      final m=m[2]) "vector port b"
    PowerSystems.Interfaces.ThermalV_n port_ab(
                                       final m=sum(m)) "vector port {a,b}"

  equation
    cat(1, port_a.ports.T, port_b.ports.T) = port_ab.ports.T;
    cat(1, port_a.ports.Q_flow, port_b.ports.Q_flow) + port_ab.ports.Q_flow = zeros(sum(m));
  end HeatV_a_b_ab;

  model HeatV_a_b_c_abc "Adaptor 3 x Thermal (scalar) to ThermalV (vector)"

    parameter Integer[3] m={1,1,1} "dimension {port_a, port_b, port_c}";
    PowerSystems.Interfaces.ThermalV_p port_a(
                                      final m=m[1]) "scalar port a"
    PowerSystems.Interfaces.ThermalV_p port_b(
                                      final m=m[2]) "scalar port b"
    PowerSystems.Interfaces.ThermalV_p port_c(
                                      final m=m[3]) "scalar port c"
    PowerSystems.Interfaces.ThermalV_n port_abc(
                                        final m=sum(m)) "vector port {a,b,c}"

  equation
    cat(1, port_a.ports.T, port_b.ports.T, port_c.ports.T) = port_abc.ports.T;
    cat(1, port_a.ports.Q_flow, port_b.ports.Q_flow, port_c.ports.Q_flow) + port_abc.ports.Q_flow = zeros(sum(m));
  end HeatV_a_b_c_abc;

  model HeatV_S "Collector ThermalV (vector) to Thermal (scalar)"

    parameter Integer m(final min=1) = 1 "dimension of port_a";
    PowerSystems.Interfaces.ThermalV_p port_a(
                                      final m=m) "vector port"
    PowerSystems.Interfaces.Thermal_n port_b "scalar port"

  equation
    port_a.ports.T = fill( port_b.T, port_a.m);
    sum(port_a.ports.Q_flow) + port_b.Q_flow = 0;
  end HeatV_S;

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

model BdCondBase "Default (Neumann) boundary condition base"

  parameter SI.Temperature T_amb=300 "ambient temperature";

end BdCondBase;

partial model BoundaryBase "Boundary model base"

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

  parameter Boolean av=false "time average heat-flow"
  parameter SI.Time tcst(min=1e-9)=1 "average time-constant"
  parameter Boolean ideal=true "ideal cooling";
  parameter SI.Temperature T_amb=300 "ambient temperature";
  parameter SI.HeatCapacity C=1 "heat capacity cp*m"
  parameter SI.ThermalConductance G=1 "thermal conductance to ambient"
  SI.Temperature T(start=300) "temperature";

    protected
  outer System system;

initial equation
  if not ideal and dynType == Types.Dynamics.SteadyInitial then
    der(T) = 0;
  end if;
end BoundaryBase;
end Partials;
end Thermal;


Generator Exciters

within PowerSystems.Control;
package Exciters "Generator Exciters "
  extends Modelica.Icons.VariantsPackage;

  block ExciterSimple "Simple exciter for constant field voltage"

    parameter Boolean par=true "v_f parameter or initialised?"
    parameter SIpu.Voltage v_f(unit="1", fixed=par)=1 "exciter voltage"
    Modelica.Blocks.Interfaces.RealInput termVoltage[3](each final unit="1")
      "terminal voltage pu"
    Modelica.Blocks.Interfaces.RealOutput fieldVoltage(final unit="1")
      "field voltage pu"

  equation
    fieldVoltage = v_f;
  end ExciterSimple;

  block ExciterConst "Exciter for constant field voltage"
    extends Partials.ExciterBase;

  equation
    connect(setptVoltage, limiter.u)
  end ExciterConst;

  block Exciter1st "Exciter first order"
    extends Partials.ExciterBase;

    parameter Types.Dynamics dynType=system.dynType "transient or steady-state model"
    parameter Real k=50 "gain";
    parameter SI.Time t=0.005 "time constant voltage regulator";
  protected
    outer System system;
    final parameter Modelica.Blocks.Types.Init initType=
      if dynType == Types.Dynamics.SteadyInitial then
           Modelica.Blocks.Types.Init.SteadyState else
           Modelica.Blocks.Types.Init.NoInit;
    Blocks.Math.Norm norm(final n=3, n_eval=3)
    Modelica.Blocks.Math.Add delta_voltage(k1=+1, k2=-1)
    Modelica.Blocks.Continuous.TransferFunction voltageReg(
      initType=initType,
      a={t,1},
      b={k})

  equation
    connect(setptVoltage, delta_voltage.u1)
    connect(termVoltage, norm.u)
    connect(norm.y, delta_voltage.u2)
    connect(delta_voltage.y, voltageReg.u)
    connect(voltageReg.y, limiter.u)
  end Exciter1st;

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

  partial block ExciterBase "Exciter base"
    extends PowerSystems.Icons.Block1;

    parameter SIpu.Voltage[2] v_f_minmax(each unit="1")={0, 3}
        "{min,max} exciter voltage";
    Modelica.Blocks.Interfaces.RealInput setptVoltage(
                             final unit="1")
        "setpoint norm of terminal voltage pu"
    Modelica.Blocks.Interfaces.RealInput termVoltage[3](
                             each final unit="1") "terminal voltage pu"
    Modelica.Blocks.Interfaces.RealOutput fieldVoltage(
                             final unit="1") "field voltage pu"
    Modelica.Blocks.Nonlinear.Limiter limiter(uMin=v_f_minmax[1],uMax=v_f_minmax[2])

  equation
    connect(limiter.y, fieldVoltage)
  end ExciterBase;
  end Partials;
end Exciters;


Turbine Governors

within PowerSystems.Control;
package Governors "Turbine Governors "
  extends Modelica.Icons.VariantsPackage;

  block GovernorSimple "Simple governor for constant turbine power"

    parameter Boolean par=true "p parameter or initialised?"
    parameter SIpu.Power p(unit="1", fixed=par)=1 "turbine power"
    Modelica.Blocks.Interfaces.RealInput speed(
                                     final unit="1") "speed of turbine pu"
    Modelica.Blocks.Interfaces.RealOutput power(
                           final unit="1") "power of turbine pu"

  equation
    power = p;
  end GovernorSimple;

  block GovernorConst "Governor for constant turbine power"
    extends Partials.GovernorBase;

  initial equation
    setptSpeed = speed;

  equation
    connect(setptPower, limiter.u)
  end GovernorConst;

  block Governor1st "Governor first order"
    extends Partials.GovernorBase;

    parameter Types.Dynamics dynType=system.dynType "transient or steady-state model"
    parameter Real k=20 "Gain";
    parameter Real t=5 "time constant speed regulator";
  protected
    outer System system;
    final parameter Modelica.Blocks.Types.Init initType=
      if dynType == Types.Dynamics.SteadyInitial then
           Modelica.Blocks.Types.Init.SteadyState else
           Modelica.Blocks.Types.Init.NoInit;
    Modelica.Blocks.Math.Add delta_speed(k2=-1)
    Modelica.Blocks.Continuous.TransferFunction speedReg(
      initType=initType,
      a={t,1},
      b={k})
    Modelica.Blocks.Math.Add delta_power

  equation
    connect(setptSpeed, delta_speed.u1)
    connect(speed, delta_speed.u2)
    connect(delta_speed.y, speedReg.u)
    connect(speedReg.y, delta_power.u1)
    connect(setptPower, delta_power.u2)
    connect(delta_power.y, limiter.u)
  end Governor1st;

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

  partial block GovernorBase "Governor base"
    extends PowerSystems.Icons.Block1;

    parameter SIpu.Power[2] p_minmax(each unit="1")={0,2}
        "{min,max} turbine power";
    Modelica.Blocks.Interfaces.RealInput setptPower(
                           final unit="1") "setpoint power pu"
    Modelica.Blocks.Interfaces.RealInput setptSpeed(
                                     final unit="1") "setpoint speed pu"
    Modelica.Blocks.Interfaces.RealInput speed(
                                     final unit="1") "turbine speed pu"
    Modelica.Blocks.Interfaces.RealOutput power(
                           final unit="1") "turbine power pu"
    Modelica.Blocks.Nonlinear.Limiter limiter(uMin=p_minmax[1], uMax=p_minmax[2])

  equation
    connect(limiter.y, power)
  end GovernorBase;
  end Partials;
end Governors;


Ignition tables for inverters


Pulse width modulation

within PowerSystems.Control;
package Modulation "Pulse width modulation"
  extends Modelica.Icons.VariantsPackage;

block PWMasyn "Sine PWM asynchronous mode, 3phase"
  extends Partials.PWMasynBase(final m=3);

  protected
  constant SI.Angle[3] phShift=(0:2)*2*pi/3;
  Real[3] v_abc;
  discrete SI.Time[3] sigdel_t;
  discrete SI.Time[3] t0;

initial algorithm
  for k in 1:3 loop
    if v_abc[k] > -1 then
      gates[{pgt[k], ngt[k]}] := {true,false};
      sigdel_t[k] := del_t;
    else
      gates[{pgt[k], ngt[k]}] := {false,true};
      sigdel_t[k] := -del_t;
    end if;
    t0[k] := del_t;
  end for;

equation
  v_abc = vPhasor [1]*cos(fill(theta +vPhasor [2], 3) - phShift);
/*
  for k in 1:3 loop  // assumes |v_abc| < 1:
    when time > pre(t0[k]) + pre(sigdel_t[k])*v_abc[k] then
      gates[{pgt[k], ngt[k]}] = pre(gates[{ngt[k], pgt[k]}]);
      sigdel_t[k] = -pre(sigdel_t[k]);
      t0[k] = pre(t0[k]) + 2*del_t;
    end when;
  end for;
*/
  for k in 1:3 loop  // allows |v_abc| >= 1:
    when time > pre(t0[k]) + pre(sigdel_t[k])*v_abc[k] then
      if noEvent(time < pre(t0[k]) + delp_t) then
        gates[{pgt[k], ngt[k]}] = pre(gates[{ngt[k],pgt[k]}]);
        sigdel_t[k] = -pre(sigdel_t[k]);
        t0[k] = pre(t0[k]) + 2*del_t;
      else
        gates[{pgt[k], ngt[k]}] = pre(gates[{pgt[k],ngt[k]}]);
        sigdel_t[k] = pre(sigdel_t[k]);
        t0[k] = pre(t0[k]) + 4*del_t;
      end if;
    end when;
  end for;
end PWMasyn;

block PWMsyn "Sine PWM synchronous mode, 3phase"
  extends Partials.PWMsynBase(final m=3);

  protected
  constant SI.Angle[3] phShift=(0:2)*2*pi/3;
  final parameter SI.Angle[m2] phi0=(1:2:2*m2 - 1)*del_phi;
  Integer[3] n;
  SI.Angle[3] phi;
  Real[3] v_abc;
  discrete Real[3] sigdel_phi;
  function mod2sign = Utilities.Math.mod2sign;

initial algorithm
  n := {1,1,1} + integer(phi/(2*del_phi));
  for k in 1:3 loop
    if -scalar(mod2sign({n[k]})) > 0 then
      if phi[k] <= phi0[n[k]] + del_phi*v_abc[k] then
        gates[{pgt[k], ngt[k]}] := {true,false};
        sigdel_phi[k] := del_phi;
      else
        gates[{pgt[k], ngt[k]}] := {false,true};
        sigdel_phi[k] := -del_phi;
        n[k] := if n[k] < m2 then n[k] + 1 else 1;
      end if;
    else
      if phi[k] <= phi0[n[k]] - del_phi*v_abc[k] then
        gates[{pgt[k], ngt[k]}] := {false,true};
        sigdel_phi[k] := -del_phi;
      else
        gates[{pgt[k], ngt[k]}] := {true,false};
        sigdel_phi[k] := del_phi;
        n[k] := if n[k] < m2 then n[k] + 1 else 1;
      end if;
    end if;
  end for;

equation
  phi = mod(fill(theta +vPhasor [2], 3) - phShift, 2*pi);
  v_abc =vPhasor [1]*cos(phi);
/*
  for k in 1:3 loop  // assumes |v_abc| < 1:
    when phi[k] > phi0[(pre(n[k]))] + pre(sigdel_phi[k])*v_abc[k] then
      gates[{pgt[k], ngt[k]}] = pre(gates[{ngt[k], pgt[k]}]);
      sigdel_phi[k] = -pre(sigdel_phi[k]);
      n[k] = if pre(n[k]) < m2 then pre(n[k]) + 1 else 1;
    end when;
  end for;
*/
  for k in 1:3 loop  // allows |v_abc| >= 1:
    when phi[k] > phi0[pre(n[k])] + pre(sigdel_phi[k])*v_abc[k] then
      if noEvent(phi[k] < phi0[pre(n[k])] + delp_phi) then
        gates[{pgt[k], ngt[k]}] = pre(gates[{ngt[k],pgt[k]}]);
        sigdel_phi[k] = -pre(sigdel_phi[k]);
        n[k] = if pre(n[k]) < m2 then pre(n[k]) + 1 else 1;
      else
        gates[{pgt[k], ngt[k]}] = pre(gates[{pgt[k],ngt[k]}]);
        sigdel_phi[k] = pre(sigdel_phi[k]);
        n[k] = if pre(n[k]) < m2 - 1 then pre(n[k]) + 2 else pre(n[k]) + 2 - m2;
      end if;
    end when;
  end for;
end PWMsyn;

block PWMtab "PWM tabulated, synchronous mode, 3phase"
  extends Partials.PWMsynBase(final m=3, final mult=integer((size(table.phiIgn, 2)+1)/4));

  replaceable PowerSystems.Control.IgnitionTables.Table_2 table
      "table(multiplicity)"
  protected
  constant SI.Angle[3] phShift=(0:2)*2*pi/3;
  discrete SI.Angle[m2] phiIgn1;
  discrete SI.Angle[m2] phiIgn2;
  discrete SI.Angle[m2] phiIgn3;
//  discrete SI.Angle[3,m2] phiIgn; // desired version
  Integer[3] n;
  SI.Angle[3] phi;
  function intpol = Utilities.Math.interpolateTable;

initial algorithm
  n := {1,1,1};
  phiIgn1 := intpol(vPhasor[1], table.phiIgn);
  gates[{pgt[1], ngt[1]}] := {true, false};
  while phiIgn1[n[1]] < phi[1] and phi[1] < phiIgn1[end] loop
    n[1] := n[1] + 1;
    gates[{pgt[1], ngt[1]}] := gates[{ngt[1], pgt[1]}];
  end while;
  phiIgn2 := intpol(vPhasor[1], table.phiIgn);
  gates[{pgt[2], ngt[2]}] := {true, false};
  while phiIgn2[n[2]] < phi[2] and phi[2] < phiIgn2[end] loop
    n[2] := n[2] + 1;
    gates[{pgt[2], ngt[2]}] := gates[{ngt[2], pgt[2]}];
  end while;
  phiIgn3 := intpol(vPhasor[1], table.phiIgn);
  gates[{pgt[3], ngt[3]}] := {true, false};
  while phiIgn3[n[3]] < phi[3] and phi[3] < phiIgn3[end] loop
    n[3] := n[3] + 1;
    gates[{pgt[3], ngt[3]}] := gates[{ngt[3], pgt[3]}];
  end while;

equation
  phi = mod(fill(theta +vPhasor [2], 3) - phShift, 2*pi);

  when phi[1] > pre(phiIgn1[pre(n[1])]) then
    gates[{pgt[1], ngt[1]}] = pre(gates[{ngt[1], pgt[1]}]);
    n[1] = if pre(n[1]) < m2 then pre(n[1]) + 1 else 1;
  end when;
  when phi[2] > pre(phiIgn2[pre(n[2])]) then
    gates[{pgt[2], ngt[2]}] = pre(gates[{ngt[2], pgt[2]}]);
    n[2] = if pre(n[2]) < m2 then pre(n[2]) + 1 else 1;
  end when;
  when phi[3] > pre(phiIgn3[pre(n[3])]) then
    gates[{pgt[3], ngt[3]}] = pre(gates[{ngt[3], pgt[3]}]);
    n[3] = if pre(n[3]) < m2 then pre(n[3]) + 1 else 1;
  end when;
  when n[1]==1 then
    phiIgn1 = intpol(vPhasor[1], table.phiIgn);
  end when;
  when n[2]==1 then
    phiIgn2 = intpol(vPhasor[1], table.phiIgn);
  end when;
  when n[3]==1 then
    phiIgn3 = intpol(vPhasor[1], table.phiIgn);
  end when;

/* desired version:
initial algorithm
  n := {1,1,1};
  for k in 1:3 loop
    phiIgn[k,:] := intpol(vPhasor[1], table.phiIgn);
    gates[{pgt[k], ngt[k]}] := {true, false};
    while phiIgn[k, n[k]] < phi[k] and phi[k] < phiIgn[k, end] loop
      n[k] := n[k] + 1;
      gates[{pgt[k], ngt[k]}] := gates[{ngt[k], pgt[k]}];
    end while;
  end for;
equation
  for k in 1:3 loop
    when phi[k] > pre(phiIgn[k, pre(n[k])]) then
      gates[{pgt[k], ngt[k]}] := pre(gates[{ngt[k],pgt[k]}]);
      n[k] := if pre(n[k]) < m2 then pre(n[k]) + 1 else 1;
    end when;
    when n[k]==1 then
      phiIgn[k,:] := intpol(vPhasor[1], table.phiIgn);
    end when;
  end for;*/
end PWMtab;

block SVPWMasyn "Space vector PWM asynchronous mode"
  extends Partials.SVPWMsynBase;

  protected
  Real u;
  SI.Angle phi;
  discrete SI.Angle phi_s;
  discrete SI.Angle[m2] phiIgn;
  Integer i;
  Integer k;
  Integer n;
  Integer s;

initial algorithm
  s := integer(3*phi/pi) + 1;
  phi_s := (s - 1)*pi/3;
  phiIgn := Phi + u*Dphi;
  i := 1;
  n := 1;
  while phiIgn[n] < phi - phi_s loop
    n := n + 1;
    i := if i < imax then i + 1 else 1;
  end while;
  k := K[s, i];
  gates[{1,3,5}] := state[k,:];
  gates[{2,4,6}] := {not state[k,1], not state[k,2], not state[k,3]};

equation
  u = min(vPhasor[1], 0.85); // preliminary (no overmodulation)
  phi = mod(theta +vPhasor [2], 2*pi);

  when phi > pre(phi_s) + pre(phiIgn[pre(n)]) then
    if pre(n) < m2 then
      i = if pre(i) < imax then pre(i) + 1 else 1;
      n = pre(n) + 1;
      s = pre(s);
      phi_s = pre(phi_s);
      phiIgn = pre(phiIgn);
    else
      i = 1;
      n = 1;
      if pre(s) < 6 then
        s = pre(s) + 1;
        phi_s = pre(phi_s) + pi/3;
        phiIgn = pre(phiIgn);
      else
        s = 1;
        phi_s = 0;
        phiIgn =  Phi + u*Dphi;
      end if;
    end if;
    k = K[s, i];
    gates[{1,3,5}] = state[k,:];
    gates[{2,4,6}] = {not state[k,1], not state[k,2], not state[k,3]};
  end when;
end SVPWMasyn;

block SVPWMsyn "Space vector PWM synchronous mode"
  extends Partials.SVPWMsynBase;

  protected
  Real u;
  SI.Angle phi;
  discrete SI.Angle phi_s;
  discrete SI.Angle[m2] phiIgn;
  Integer i;
  Integer k;
  Integer n;
  Integer s;

initial algorithm
  s := integer(3*phi/pi) + 1;
  phi_s := (s - 1)*pi/3;
  phiIgn := Phi + u*Dphi;
  i := 1;
  n := 1;
  while phiIgn[n] < phi - phi_s loop
    n := n + 1;
    i := if i < imax then i + 1 else 1;
  end while;
  k := K[s, i];
  gates[{1,3,5}] := state[k,:];
  gates[{2,4,6}] := {not state[k,1], not state[k,2], not state[k,3]};

equation
  u = min(vPhasor[1], 0.85); // preliminary (no overmodulation)
  phi = mod(theta +vPhasor [2], 2*pi);

  when phi > pre(phi_s) + pre(phiIgn[pre(n)]) then
    if pre(n) < m2 then
      i = if pre(i) < imax then pre(i) + 1 else 1;
      n = pre(n) + 1;
      s = pre(s);
      phi_s = pre(phi_s);
      phiIgn = pre(phiIgn);
    else
      i = 1;
      n = 1;
      if pre(s) < 6 then
        s = pre(s) + 1;
        phi_s = pre(phi_s) + pi/3;
        phiIgn = pre(phiIgn);
      else
        s = 1;
        phi_s = 0;
        phiIgn =  Phi + u*Dphi;
      end if;
    end if;
    k = K[s, i];
    gates[{1,3,5}] = state[k,:];
    gates[{2,4,6}] = {not state[k,1], not state[k,2], not state[k,3]};
  end when;
end SVPWMsyn;

block SVPWM "Space vector PWM"
  extends Partials.SVPWMbase;

  parameter SI.Frequency f_carr=1e3 "carrier frequency";
  Blocks.Transforms.PhasorToAlphaBeta phToAlphaBeta
  SpaceVector.SVMlogic logic(T_pwm=1/f_carr)
  SpaceVector.SVMpwm pwm(T_pwm=1/f_carr)
  Modelica.Blocks.Math.Add phaseCorr
equation
  phaseCorr.u2 = der(theta)/(2*f_carr); //d_phi = pi*f/f_carr.

  connect(vPhasor, phToAlphaBeta.u)
  connect(phToAlphaBeta.y, logic.u_alpha_beta)
  connect(logic.trigger, pwm.trigger)
  connect(pwm.switch, gates)
  connect(logic.dutyRatio, pwm.dutyRatio)
    connect(theta, phaseCorr.u1)
    connect(phaseCorr.y, phToAlphaBeta.theta)
end SVPWM;

block BlockM "Block modulation, 3phase"
  extends Partials.BlockBase(final m=3);

  protected
  constant SI.Angle[3] phShift=(0:2)*2*pi/3;
  SI.Angle alpha;
  Real a;
  Real[3] v_abc;

equation
  alpha = max(min((1 - width)*pi/2, pi/2 - dphimin), 0);
  a = sin(alpha);
  v_abc = cos(fill(theta +vPhasor [2], 3) - phShift);

  for k in 1:3 loop
    gates[{pgt[k], ngt[k]}] = {v_abc[k] > a, v_abc[k] < -a};
  end for;
end BlockM;

 block PWMasyn1ph "Sine PWM asynchronous mode, 1-phase"
   extends Partials.PWMasynBase(final m=2);

  protected
   Real u;
   discrete SI.Time sigdel_t;
   discrete SI.Time t0;

 initial algorithm
   if u > -1 then
     gates[{pgt[1], ngt[1]}] := {true,false};
     gates[{pgt[2], ngt[2]}] := {false,true};
     sigdel_t := del_t;
   else
     gates[{pgt[1], ngt[1]}] := {false,true};
     gates[{pgt[2], ngt[2]}] := {true,false};
     sigdel_t := -del_t;
   end if;
   t0 := del_t;

 equation
   u =vPhasor [1]*cos(theta +vPhasor [2]);
 /*
//assumes |u| < 1:
  when time > pre(t0) + pre(sigdel_t)*u then
    gates[{pgt[1], ngt[1]}] = pre(gates[{ngt[1], pgt[1]}]);
    gates[{pgt[2], ngt[2]}] = pre(gates[{ngt[2], pgt[2]}]);
    sigdel_t = -pre(sigdel_t);
    t0 = pre(t0) + 2*del_t;
  end when;
*/
 //allows |u| >= 1:
   when time > pre(t0) + pre(sigdel_t)*u then
     if noEvent(time < pre(t0) + delp_t) then
       gates[{pgt[1], ngt[1]}] = pre(gates[{ngt[1], pgt[1]}]);
       gates[{pgt[2], ngt[2]}] = pre(gates[{ngt[2], pgt[2]}]);
       sigdel_t = -pre(sigdel_t);
       t0 = pre(t0) + 2*del_t;
     else
       gates = pre(gates);
       sigdel_t = pre(sigdel_t);
       t0 = pre(t0) + 4*del_t;
     end if;
   end when;
 end PWMasyn1ph;

block PWMsyn1ph "Sine PWM synchronous mode, 1-phase"
  extends Partials.PWMsynBase(final m=2);

  protected
  final parameter SI.Angle[m2] phi0=(1:2:2*m2 - 1)*del_phi;
  SI.Angle phi;
  Real u;
  Integer n;
  Integer sigma;
  discrete Real sigdel_phi;
  function mod2sign = Utilities.Math.mod2sign;

initial algorithm
  n := 1 + integer(phi/(2*del_phi));
  sigma := -scalar(mod2sign({n}));
  if sigma > 0 then
    if phi <= phi0[n] + del_phi*u then
      gates[{pgt[1], ngt[1]}] := {true,false};
      gates[{pgt[2], ngt[2]}] := {false,true};
      sigdel_phi := del_phi;
    else
      gates[{pgt[1], ngt[1]}] := {false,true};
      gates[{pgt[2], ngt[2]}] := {true,false};
      sigdel_phi := -del_phi;
      n := if n < m2 then n + 1 else 1;
    end if;
  else
    if phi <= phi0[n] - del_phi*u then
      gates[{pgt[1], ngt[1]}] := {false,true};
      gates[{pgt[2], ngt[2]}] := {true,false};
      sigdel_phi := -del_phi;
    else
      gates[{pgt[1], ngt[1]}] := {true,false};
      gates[{pgt[2], ngt[2]}] := {false,true};
      sigdel_phi := del_phi;
      n := if n < m2 then n + 1 else 1;
    end if;
  end if;

equation
  sigma = pre(sigma);
  phi = mod(theta +vPhasor [2], 2*pi);
  u =vPhasor [1]*cos(phi);

/*
// assumes |v_abc| < 1:
  when phi > phi0[(pre(n))] + pre(sigdel_phi)*u then
    gates[{pgt[1], ngt[1]}] = pre(gates[{ngt[1], pgt[1]}]);
    gates[{pgt[2], ngt[2]}] = pre(gates[{ngt[2], pgt[2]}]);
    sigdel_phi = -pre(sigdel_phi);
    n = if pre(n) < m2 then pre(n) + 1 else 1;
  end when;
*/
// allows |v_abc| >= 1:
  when phi > phi0[pre(n)] + pre(sigdel_phi)*u then
    if noEvent(phi < phi0[pre(n)] + delp_phi) then
      gates[{pgt[1], ngt[1]}] = pre(gates[{ngt[1], pgt[1]}]);
      gates[{pgt[2], ngt[2]}] = pre(gates[{ngt[2], pgt[2]}]);
      sigdel_phi = -pre(sigdel_phi);
      n = if pre(n) < m2 then pre(n) + 1 else 1;
    else
      gates = pre(gates);
      sigdel_phi = pre(sigdel_phi);
      n =  if pre(n) < m2 - 1 then pre(n) + 2 else pre(n) + 2 -
        m2;
    end if;
  end when;
end PWMsyn1ph;

block PWMtab1ph "PWM tabulated, synchronous mode, 1-phase"
  extends Partials.PWMsynBase(final m=2, final mult=integer((size(table.phiIgn, 2)+1)/4));

  replaceable PowerSystems.Control.IgnitionTables.Table_2 table
      "table(multiplicity)"
  protected
  discrete SI.Angle[m2] phiIgn;
  SI.Angle phi;
  Integer n;
  function intpol = Utilities.Math.interpolateTable;

initial algorithm
  n := 1;
  phiIgn := intpol(vPhasor[1], table.phiIgn);
  gates[{pgt[1], ngt[1]}] := {true, false};
  gates[{pgt[2], ngt[2]}] := {false, true};
  while phiIgn[n] < phi and phi < phiIgn[end] loop
    n := n + 1;
    gates[{pgt[1], ngt[1]}] := gates[{ngt[1], pgt[1]}];
    gates[{pgt[2], ngt[2]}] := gates[{ngt[2], pgt[2]}];
  end while;

equation
  phi = mod(theta +vPhasor [2], 2*pi);

  when phi > pre(phiIgn[pre(n)]) then
    gates[{pgt[1], ngt[1]}] = pre(gates[{ngt[1], pgt[1]}]);
    gates[{pgt[2], ngt[2]}] = pre(gates[{ngt[2], pgt[2]}]);
    n = if pre(n) < m2 then pre(n) + 1 else 1;
  end when;
  when n==1 then
    phiIgn = intpol(vPhasor[1], table.phiIgn);
  end when;
end PWMtab1ph;

block BlockM1ph "Block modulation, 1phase"
  extends Partials.BlockBase(final m=2);

  protected
  SI.Angle alpha;
  Real a;
  Real u;

equation
  alpha = max(min((1 - width)*pi/2, pi/2 - dphimin), 0);
  a = sin(alpha);
  u = cos(theta +vPhasor [2]);

  gates[{pgt[1], ngt[1]}] = {u > a, u < -a};
  gates[{pgt[2], ngt[2]}] = {u < -a, u > a};
end BlockM1ph;

block ChopperPWM "Chopper PWM (voltage)"
  extends PowerSystems.Icons.BlockS;

  parameter SI.Frequency f_carr=500 "carrier frequency";
  Modelica.Blocks.Interfaces.RealInput vDC "desired average voltage, pu"
  Modelica.Blocks.Interfaces.BooleanOutput gate(start=false) "gate"
  protected
  final parameter SI.Time del_t=1/f_carr;
  discrete SI.Time t0;
  discrete SI.Time t1;

equation
  when sample(0, del_t) then
    t0 = time;
    t1 = time +vDC *del_t;
  end when;

  gate = t0 <= time and time < t1;
end ChopperPWM;

block ChopperPWM_I "Chopper PWM (current)"
  extends PowerSystems.Icons.BlockS;

  parameter SI.Current iRipple=1 "max ripple current";
  Modelica.Blocks.Interfaces.BooleanOutput gate(start=false) "gate"
  Modelica.Blocks.Interfaces.RealInput iSet "desired current"
  Modelica.Blocks.Interfaces.RealInput iMeas "measured current"
  protected
  SI.Current di;

equation
  di = (iMeas - iSet);

  if di > iRipple then
    gate = false;
  elseif di < -iRipple then
    gate = true;
  else
    gate = pre(gate);
  end if;
end ChopperPWM_I;

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

  partial block ModulatorBase "Modulator base"
    extends PowerSystems.Icons.BlockS;

    parameter Integer m(min=2,max=3)=3 "3 for 3-phase, 2 for 1-phase";
    Modelica.Blocks.Interfaces.RealInput theta "reference angle"
    Modelica.Blocks.Interfaces.RealInput[2] vPhasor
        "voltage demand pu {norm(v), phase(v)} (see info)"
    Modelica.Blocks.Interfaces.BooleanOutput[2*m] gates(each start=false) "gates"
    protected
    final parameter Integer[m] pgt=1:2:(2*m-1) "positive gates"
    final parameter Integer[m] ngt=2:2:(2*m) "negative gates"
  end ModulatorBase;

  partial block SVPWMbase "Space vector PWM base"
    extends ModulatorBase(final m=3);

    parameter Boolean sym=true "'symmetric' or 'antisymmetric'"
    protected
    Boolean[8,3] state=
      [true,false,false;
       true,true,false;
       false,true,false;
       false,true,true;
       false,false,true;
       true,false,true;
       false,false,false;
       true,true,true];
  end SVPWMbase;

  partial block SVPWMasynBase "Space vector PWM asynchronous base"
    extends SVPWMbase;
  /* asynchronous:
  parameter SI.Frequency f_carr=1e3 "carrier frequency" annotation 0;
protected
  final parameter SI.Time del_t=1/(2*f_carr) annotation 1;
  constant Integer mult=10 "number of stored phase-values";
  constant SI.Angle del_phi=pi/(3*mult);
//constant SI.Angle[1+mult] dt=(del_t/sqrt(3))*sin((0:mult)*del_phi);
  constant SI.Angle[1+mult] dt_sum=(del_t/sqrt(3))*(sin((mult:-1:0)*del_phi)+sin((0:mult)*del_phi));
  constant SI.Angle[1+mult] dt_dif=(del_t/sqrt(3))*(sin((mult:-1:0)*del_phi)-sin((0:mult)*del_phi));
  SI.Angle Phi;
  SI.Angle[3] Dt;
  Integer[6,:] K=if sym then
    [7,1,2,8,2,1;
     7,3,2,8,2,3;
     7,3,4,8,4,3;
     7,5,4,8,4,5;
     7,5,6,8,6,5;
     7,1,6,8,6,1] else
    [7,1,2,8;
     7,3,2,8;
     7,3,4,8;
     7,5,4,8;
     7,5,6,8;
     7,1,6,8];
  Integer imax = size(K,2);
*/
    parameter Integer mult(min=1)=1 "multiplicity (6m-1 pulses/period)"
    protected
    final parameter Integer m1=if sym then 2*mult else mult
    final parameter Integer m2=if sym then 3*m1-1 else 4*m1-1
    final parameter SI.Angle del_phi=pi/(3*m1)
    final parameter SI.Angle[m1] dphi=(del_phi/sqrt(3))*sin((1:m1)*del_phi)
    SI.Angle[m2] Phi;
    SI.Angle[m2] Dphi;
    Integer[6,:] K=if sym then
      [1,7,1,2,8,2;
       2,8,2,3,7,3;
       3,7,3,4,8,4;
       4,8,4,5,7,5;
       5,7,5,6,8,6;
       6,8,6,1,7,1] else
      [1,7,8,2,0,0;
       2,8,7,3,0,0;
       3,7,8,4,0,0;
       4,8,7,5,0,0;
       5,7,8,6,0,0;
       6,8,7,1,0,0];
    Integer imax = if sym then 6 else 4; // size(K,2);
  equation
    if sym then
      Phi[1] = 0;
      Dphi[1] = dphi[m1];
      for m in 1:m1-1 loop
        Phi[3*m-1:3*m+1] = fill(m*del_phi, 3);
        Dphi[3*m-1:3*m+1] = {-dphi[m]-dphi[m1-m], (-1)^m*(dphi[m]-dphi[m1-m]), dphi[m]+dphi[m1-m]};
      end for;
      Phi[m2] = pi/3;
      Dphi[m2] = -dphi[m1];
    else
      Phi[1:2] = {0, del_phi/2};
      Dphi[1:2] = {dphi[m1], 0};
      for m in 1:m1-1 loop
        Phi[4*m-1:4*m+1] = fill(m*del_phi, 3);
        Dphi[4*m-1:4*m+1] = {-dphi[m]-dphi[m1-m], (dphi[m]-dphi[m1-m]), dphi[m]+dphi[m1-m]};
        Phi[4*m+2] = (0.5 + m)*del_phi;
        Dphi[4*m+2] = 0;
      end for;
      Phi[m2] = pi/3;
      Dphi[m2] = -dphi[m1];
    end if;
  end SVPWMasynBase;

  partial block SVPWMsynBase "Space vector PWM synchronous base"
    extends SVPWMbase;

    parameter Integer mult(min=1)=1 "multiplicity (6*mult-1 pulses/period)"
    protected
    parameter Integer m1=if sym then 2*mult else mult
    parameter Integer m2=if sym then 3*m1-1 else 4*m1-1
    parameter SI.Angle del_phi=pi/(3*m1)
    parameter SI.Angle[m1] dphi=(del_phi/sqrt(3))*sin((1:m1)*del_phi)
    SI.Angle[m2] Phi;
    SI.Angle[m2] Dphi;
    Integer[6,:] K=if sym then
      [1,7,1,2,8,2;
       2,8,2,3,7,3;
       3,7,3,4,8,4;
       4,8,4,5,7,5;
       5,7,5,6,8,6;
       6,8,6,1,7,1] else
      [1,7,8,2,0,0;
       2,8,7,3,0,0;
       3,7,8,4,0,0;
       4,8,7,5,0,0;
       5,7,8,6,0,0;
       6,8,7,1,0,0];
    Integer imax = if sym then 6 else 4; // size(K,2);

  equation
    if sym then
      Phi[1] = 0;
      Dphi[1] = dphi[m1];
      for m in 1:m1-1 loop
        Phi[3*m-1:3*m+1] = fill(m*del_phi, 3);
        Dphi[3*m-1:3*m+1] = {-dphi[m]-dphi[m1-m], (-1)^m*(dphi[m]-dphi[m1-m]), dphi[m]+dphi[m1-m]};
      end for;
      Phi[m2] = pi/3;
      Dphi[m2] = -dphi[m1];
    else
      Phi[1:2] = {0, del_phi/2};
      Dphi[1:2] = {dphi[m1], 0};
      for m in 1:m1-1 loop
        Phi[4*m-1:4*m+1] = fill(m*del_phi, 3);
        Dphi[4*m-1:4*m+1] = {-dphi[m]-dphi[m1-m], (dphi[m]-dphi[m1-m]), dphi[m]+dphi[m1-m]};
        Phi[4*m+2] = (0.5 + m)*del_phi;
        Dphi[4*m+2] = 0;
      end for;
      Phi[m2] = pi/3;
      Dphi[m2] = -dphi[m1];
    end if;
  end SVPWMsynBase;

  partial block PWMasynBase "Sine PWM asynchronous base"
    extends ModulatorBase;

    parameter SI.Frequency f_carr=1e3 "carrier frequency"
    parameter SI.Time dt_min(min=Modelica.Constants.eps,max=0.1*del_t)=1e-6
        "minimal time between on/off"
    protected
    final parameter SI.Time del_t=1/(4*f_carr)
    final parameter SI.Time delp_t=del_t - dt_min/2

  end PWMasynBase;

  partial block PWMsynBase "Sine PWM synchronous base"
    extends ModulatorBase;

    parameter Integer mult(min=1)=3 "multiplicity (2*mult-1 pulses/period)"
    parameter SI.Angle dphi_min(min=Modelica.Constants.eps,max=0.1*del_phi)=1e-6
        "minimal time between on/off"
    protected
    final parameter Integer m2=2*(2*mult-1)
    final parameter SI.Angle del_phi=pi/m2
    final parameter SI.Angle delp_phi=del_phi-dphi_min/2

  end PWMsynBase;

  partial block BlockBase "Block modulator base"
    extends PowerSystems.Icons.BlockS;

    parameter Integer m(min=2,max=3)=3 "3 for 3-phase, 2 for 1-phase";
    parameter SI.Angle width=2/3 "relative width (0 - 1)";
    parameter SI.Angle dphimin(min=Modelica.Constants.eps, max=pi)=1e-3
        "minimal phase angle on"
    Modelica.Blocks.Interfaces.RealInput theta "reference angle"
    Modelica.Blocks.Interfaces.RealInput[2] vPhasor
        "voltage demand pu {norm(v), phase(v)} (see info)"
    Modelica.Blocks.Interfaces.BooleanOutput[2*m] gates(each start=false) "gates"
    protected
    final parameter Integer[m] pgt=1:2:(2*m-1) "positive gates"
    final parameter Integer[m] ngt=2:2:(2*m) "negative gates"

  end BlockBase;

end Partials;

package SpaceVector "Space vector logic and control"
  extends Modelica.Icons.VariantsPackage;

  model SVMlogic "Logical part for SVM"

    parameter SI.Time T_pwm "cycle time pwm";
    inner Modelica.StateGraph.StateGraphRoot stateGraphRoot
    Modelica.StateGraph.Alternative Alternative1
    Modelica.Blocks.Interfaces.RealInput u_alpha_beta[2]
    Modelica.Blocks.Math.Product norm1
    Modelica.Blocks.Sources.Constant n1(k=sqrt(3/2))
    Modelica.StateGraph.StepWithSignal sector2(nIn = 1, nOut = 1)
    Modelica.StateGraph.TransitionWithSignal t1
    Modelica.Blocks.Logical.GreaterEqual GreaterEqual1
    Modelica.Blocks.Math.Gain Gain1(k=1/sqrt(3))
    Modelica.Blocks.Logical.GreaterEqualThreshold GreaterEqualThreshold1
    Modelica.Blocks.Logical.Not Not1
    Modelica.Blocks.Math.Product norm2
    Modelica.Blocks.Logical.GreaterEqual GreaterEqual2
    Modelica.Blocks.Math.Gain Gain3(k=-1/sqrt(3))
    Modelica.StateGraph.TransitionWithSignal t2
    Modelica.StateGraph.Alternative Alternative2
    Modelica.StateGraph.Step Step1(nIn = 1, nOut = 1)
    Modelica.StateGraph.Step Step2(nIn = 1, nOut = 1)
    Modelica.StateGraph.Alternative Alternative3
    Modelica.StateGraph.Alternative Alternative5
    Modelica.StateGraph.Alternative Alternative7
    Modelica.StateGraph.TransitionWithSignal t3
    Modelica.StateGraph.TransitionWithSignal t4
    Modelica.StateGraph.Step Step3(nIn = 1, nOut = 1)
    Modelica.StateGraph.Step Step4(nIn = 1, nOut = 1)
    Modelica.StateGraph.TransitionWithSignal t5
    Modelica.StateGraph.TransitionWithSignal t6
    Modelica.Blocks.Logical.Not Not2
    Modelica.Blocks.Logical.Not Not3
    Modelica.StateGraph.TransitionWithSignal t7
    Modelica.StateGraph.TransitionWithSignal t8
    Modelica.StateGraph.TransitionWithSignal t9
    Modelica.StateGraph.TransitionWithSignal t10
    Modelica.StateGraph.StepWithSignal sector3(nIn = 1, nOut = 1)
    Modelica.StateGraph.StepWithSignal sector1(nIn = 1, nOut = 1)
    Modelica.StateGraph.StepWithSignal sector6(nIn = 1, nOut = 1)
    Modelica.StateGraph.StepWithSignal sector5(nIn = 1, nOut = 1)
    Modelica.StateGraph.StepWithSignal sector4(nIn = 1, nOut = 1)
    Modelica.StateGraph.Transition t11(enableTimer=true, waitTime=T_pwm)
    Modelica.StateGraph.Transition t12(enableTimer=true, waitTime=T_pwm)
    Modelica.StateGraph.Transition t13(enableTimer=true, waitTime=T_pwm)
    Modelica.StateGraph.Transition t16(enableTimer=true, waitTime=T_pwm)
    Modelica.StateGraph.Transition t15(enableTimer=true, waitTime=T_pwm)
    Modelica.StateGraph.Transition t14(enableTimer=true, waitTime=T_pwm)
    Modelica.Blocks.Logical.Or s1_4
    Modelica.Blocks.Logical.Or s2_5
    Modelica.Blocks.Logical.Or s3_6
    Modelica.Blocks.Sources.Constant zero[3](each k=0)
    SVMsector1p4 sector1p4
    SVMsector2p5 sector2p5
    SVMsector3p6 sector3p6
    Modelica.Blocks.Nonlinear.Limiter Limiter[3]
    Modelica.Blocks.Logical.Switch Switch1[3]
    Modelica.Blocks.Logical.Switch Switch2[3]
    Modelica.Blocks.Logical.Switch Switch3[3]
    Modelica.Blocks.Interfaces.RealOutput dutyRatio[3]
    Modelica.Blocks.Routing.DeMultiplex2 alpha_beta
    Modelica.Blocks.Interfaces.BooleanOutput trigger
    Modelica.StateGraph.InitialStep InitialStep1(nIn = 1, nOut = 1)
    Modelica.Blocks.Logical.Or Or1
    Modelica.Blocks.Logical.Or Or2
    TriggeredPulse TriggeredPulse1(T_pulse=1/10*T_pwm)

  equation
    connect(Gain1.y, GreaterEqual1.u2)
    connect(norm2.y, GreaterEqualThreshold1.u)
    connect(Gain3.y, GreaterEqual2.u2)
    connect(norm1.y, GreaterEqual1.u1)
    connect(GreaterEqual2.u1, norm1.y)
    connect(norm2.y, Gain3.u)
    connect(GreaterEqualThreshold1.y, Not1.u)
    connect(GreaterEqualThreshold1.y, t1.condition)
    connect(t1.outPort, Step1.inPort[1])
    connect(Step1.outPort[1], Alternative2.inPort)
    connect(t2.outPort, Step2.inPort[1])
    connect(Step2.outPort[1], Alternative3.inPort)
    connect(Step3.outPort[1], Alternative5.inPort)
    connect(t4.outPort,Step3. inPort[1])
    connect(t5.outPort,Step4. inPort[1])
    connect(Step4.outPort[1], Alternative7.inPort)
    connect(GreaterEqual1.y, t3.condition)
    connect(Not1.y, t2.condition)
    connect(GreaterEqual1.y, Not2.u)
    connect(GreaterEqual2.y, Not3.u)
    connect(Not2.y, t4.condition)
    connect(GreaterEqual2.y, t6.condition)
    connect(Not3.y,t5. condition)
    connect(GreaterEqual2.y, t7.condition)
    connect(Not3.y, t8.condition)
    connect(GreaterEqual1.y, t10.condition)
    connect(Not2.y, t9.condition)
    connect(t2.inPort, Alternative1.split[2])
    connect(t1.inPort, Alternative1.split[1])
    connect(t3.inPort, Alternative2.split[1])
    connect(t4.inPort, Alternative2.split[2])
    connect(t7.inPort, Alternative5.split[1])
    connect(t8.inPort, Alternative5.split[2])
    connect(t7.outPort, sector2.inPort[1])
    connect(sector4.inPort[1], t9.outPort)
    connect(sector5.inPort[1], t10.outPort)
    connect(t3.outPort, sector1.inPort[1])
    connect(t6.outPort, sector6.inPort[1])
    connect(t8.outPort, sector3.inPort[1])
    connect(Alternative5.outPort, Alternative2.join[2])
    connect(Alternative3.outPort, Alternative1.join[2])
    connect(Alternative2.outPort, Alternative1.join[1])
    connect(sector1.outPort[1], t11.inPort)
    connect(sector5.outPort[1], t15.inPort)
    connect(sector4.outPort[1],t14. inPort)
    connect(sector6.outPort[1], t16.inPort)
    connect(sector3.outPort[1], t13.inPort)
    connect(sector2.outPort[1], t12.inPort)
    connect(t12.outPort, Alternative5.join[1])
    connect(t13.outPort, Alternative5.join[2])
    connect(t11.outPort, Alternative2.join[1])
    connect(sector1.active, s1_4.u1)
    connect(sector2.active, s2_5.u1)
    connect(sector3.active, s3_6.u1)
      connect(sector1p4.u_alpha, sector2p5.u_alpha)
      connect(sector2p5.u_alpha, sector3p6.u_alpha)
      connect(sector3p6.u_beta, sector2p5.u_beta)
      connect(sector2p5.u_beta, sector1p4.u_beta)
    connect(Switch3.y, Switch2.u3)
    connect(Switch2.y, Switch1.u3)
    connect(Switch1.y, Limiter.u)
    connect(Limiter.y, dutyRatio)
    connect(s1_4.y, Switch1[1].u2)
    connect(s1_4.y, Switch1[2].u2)
    connect(s1_4.y, Switch1[3].u2)
    connect(s2_5.y, Switch2[1].u2)
    connect(s2_5.y, Switch2[2].u2)
    connect(s2_5.y, Switch2[3].u2)
    connect(s3_6.y, Switch3[1].u2)
    connect(s3_6.y, Switch3[2].u2)
    connect(s3_6.y, Switch3[3].u2)
    connect(zero.y, Switch3.u3)
    connect(alpha_beta.y1[1], norm1.u2)
      connect(alpha_beta.u, u_alpha_beta)
    connect(alpha_beta.y2[1], norm2.u2)
    connect(norm2.y, Gain1.u)
    connect(InitialStep1.outPort[1], Alternative1.inPort)
    connect(InitialStep1.inPort[1], Alternative1.outPort)
    connect(s1_4.y, Or1.u1)
    connect(s2_5.y, Or1.u2)
    connect(Or1.y, Or2.u1)
    connect(s3_6.y, Or2.u2)
      connect(norm1.y, sector1p4.u_alpha)
      connect(norm2.y, sector1p4.u_beta)
    connect(Alternative3.split[2], t6.inPort)
    connect(Alternative3.split[1], t5.inPort)
    connect(Alternative7.split[1], t9.inPort)
    connect(Alternative7.split[2], t10.inPort)
    connect(t14.outPort, Alternative7.join[1])
    connect(t15.outPort, Alternative7.join[2])
    connect(t16.outPort, Alternative3.join[2])
    connect(Alternative7.outPort, Alternative3.join[1])
    connect(sector4.active, s1_4.u2)
    connect(sector5.active, s2_5.u2)
    connect(sector6.active, s3_6.u2)
      connect(sector3p6.abc, Switch3.u1)
      connect(sector2p5.abc, Switch2.u1)
      connect(sector1p4.abc, Switch1.u1)
    connect(Or2.y, TriggeredPulse1.u)
    connect(trigger, TriggeredPulse1.y)
    connect(n1.y, norm1.u1)
    connect(n1.y, norm2.u1)
  end SVMlogic;

model SVMpwm "Block generating the pwm pulses for SVM"

  parameter SI.Time T_pwm;
  inner Modelica.StateGraph.StateGraphRoot stateGraphRoot
  Modelica.Blocks.Interfaces.BooleanInput trigger
  Modelica.Blocks.Interfaces.RealInput dutyRatio[3]
  Modelica.Blocks.Interfaces.BooleanOutput switch[6]
  Modelica.Blocks.Routing.DeMultiplex3 DeMultiplex3_1
  Modelica.Blocks.Sources.Constant one
  Modelica.Blocks.Math.Feedback t1
  Modelica.Blocks.Math.Gain start_100(k=1/2)
  Modelica.Blocks.Logical.GreaterEqual GreaterEqual1
  Modelica.Blocks.Math.Gain start_010(k=1/2)
  Modelica.Blocks.Math.Feedback t2
  Modelica.Blocks.Math.Gain start_001(k=1/2)
  Modelica.Blocks.Math.Feedback t3
  Modelica.Blocks.Logical.GreaterEqual GreaterEqual2
  Modelica.Blocks.Logical.GreaterEqual GreaterEqual3
  Modelica.Blocks.Math.Add stop_100
  Modelica.Blocks.Math.Add stop_010
  Modelica.Blocks.Math.Add stop_001
  Modelica.Blocks.Logical.GreaterEqual GreaterEqual4
  Modelica.Blocks.Logical.GreaterEqual GreaterEqual5
  Modelica.Blocks.Logical.GreaterEqual GreaterEqual6
  Modelica.Blocks.Discrete.TriggeredSampler load_data[3]
  Modelica.Blocks.Logical.Timer Timer
  Modelica.Blocks.Math.Gain time_scaling(k=1/T_pwm)
  Modelica.Blocks.Logical.Edge Edge
  Modelica.Blocks.Logical.Not Not
  DutyRatioToCycle dutyRtoC
  Modelica.Blocks.Logical.And And1
  Modelica.Blocks.Logical.Not Not1
  Modelica.Blocks.Logical.Not Not2
  Modelica.Blocks.Logical.And And2
  Modelica.Blocks.Logical.Not Not3
  Modelica.Blocks.Logical.And And3
  Modelica.Blocks.Logical.Not Not4
  Modelica.Blocks.Logical.Not Not5
  Modelica.Blocks.Logical.Not Not6

equation
  connect(one.y, t1.u1)
  connect(t1.y, start_100.u)
  connect(start_100.y, GreaterEqual1.u2)
  connect(t2.y, start_010.u)
  connect(t3.y, start_001.u)
  connect(one.y, t2.u1)
  connect(one.y, t3.u1)
  connect(DeMultiplex3_1.y2[1], t2.u2)
  connect(DeMultiplex3_1.y3[1], t3.u2)
  connect(start_010.y, GreaterEqual2.u2)
  connect(start_001.y, GreaterEqual3.u2)
  connect(DeMultiplex3_1.y1[1], t1.u2)
  connect(start_100.y, stop_100.u1)
  connect(start_010.y, stop_010.u1)
  connect(stop_010.u2, DeMultiplex3_1.y2[1])
  connect(start_001.y, stop_001.u1)
  connect(stop_001.u2, DeMultiplex3_1.y3[1])
  connect(stop_010.y, GreaterEqual5.u2)
  connect(dutyRatio, load_data.u)
  connect(stop_001.y, GreaterEqual6.u2)
  connect(stop_100.y, GreaterEqual4.u2)
  connect(Timer.y, time_scaling.u)
  connect(Not.y, Timer.u)
  connect(trigger, load_data[1].trigger)
  connect(trigger, load_data[2].trigger)
  connect(trigger, load_data[3].trigger)
  connect(GreaterEqual1.u1, GreaterEqual2.u1)
  connect(GreaterEqual2.u1, GreaterEqual3.u1)
  connect(GreaterEqual3.u1, GreaterEqual6.u1)
  connect(GreaterEqual6.u1, GreaterEqual5.u1)
  connect(GreaterEqual5.u1, GreaterEqual4.u1)
  connect(Edge.y, Not.u)
  connect(stop_100.u2, DeMultiplex3_1.y1[1])
  connect(Edge.u, trigger)
  connect(time_scaling.y, GreaterEqual1.u1)
  connect(GreaterEqual4.y, Not1.u)
  connect(GreaterEqual1.y, And1.u1)
  connect(Not1.y, And1.u2)
  connect(Not2.y, And2.u2)
  connect(Not3.y, And3.u2)
  connect(GreaterEqual6.y, Not3.u)
  connect(GreaterEqual3.y, And3.u1)
  connect(GreaterEqual2.y, And2.u1)
  connect(GreaterEqual5.y, Not2.u)
  connect(And1.y, Not4.u)
  connect(And2.y, Not5.u)
  connect(And3.y, Not6.u)
  connect(And1.y, switch[1])
  connect(Not4.y, switch[2])
  connect(And2.y, switch[3])
  connect(Not5.y, switch[4])
  connect(And3.y, switch[5])
  connect(Not6.y, switch[6])
  connect(load_data.y, dutyRtoC.dutyRatio)
  connect(dutyRtoC.dutyCycle, DeMultiplex3_1.u)
end SVMpwm;

model SVMsector1p4 "Sector 1-4"

  Modelica.Blocks.Interfaces.RealInput u_alpha
  Modelica.Blocks.Interfaces.RealInput u_beta
  Modelica.Blocks.Interfaces.RealOutput abc[3]
  Modelica.Blocks.Math.Add Add1
  Modelica.Blocks.Math.Gain Gain1(k=1/(sqrt(3)))
  Modelica.Blocks.Math.Gain Gain2(k=-1)
  Modelica.Blocks.Math.Gain Gain3(k=-1)
  Modelica.Blocks.Math.Add Add2
  Modelica.Blocks.Math.Gain Gain4(k=3/(sqrt(3)))

equation
  connect(u_alpha, Add1.u1)
  connect(u_beta, Gain1.u)
  connect(Gain1.y, Add1.u2)
  connect(Add1.y, Gain2.u)
  connect(u_alpha, Gain3.u)
  connect(Gain3.y, Add2.u1)
  connect(u_beta, Gain4.u)
  connect(Gain4.y, Add2.u2)
  connect(Add1.y, abc[1])
  connect(Add2.y, abc[2])
  connect(Gain2.y, abc[3])
end SVMsector1p4;

model SVMsector2p5 "Sector 2-5"

  Modelica.Blocks.Interfaces.RealInput u_alpha
  Modelica.Blocks.Interfaces.RealInput u_beta
  Modelica.Blocks.Interfaces.RealOutput abc[3]
  Modelica.Blocks.Math.Gain Gain1(k=2)
  Modelica.Blocks.Math.Gain Gain2(k=-1)
  Modelica.Blocks.Math.Gain Gain4(k=2/(sqrt(3)))

equation
  connect(u_beta, Gain4.u)
  connect(u_alpha, Gain1.u)
  connect(Gain4.y, Gain2.u)
  connect(Gain1.y, abc[1])
  connect(Gain4.y, abc[2])
  connect(Gain2.y, abc[3])
end SVMsector2p5;

model SVMsector3p6 "Sector 3-6"

  Modelica.Blocks.Interfaces.RealInput u_alpha
  Modelica.Blocks.Interfaces.RealInput u_beta
  Modelica.Blocks.Interfaces.RealOutput abc[3]
  Modelica.Blocks.Math.Add Add1
  Modelica.Blocks.Math.Gain Gain1(k=-1/(sqrt(3)))
  Modelica.Blocks.Math.Gain Gain2(k=-1)
  Modelica.Blocks.Math.Gain Gain3(k=-1)
  Modelica.Blocks.Math.Add Add2
  Modelica.Blocks.Math.Gain Gain4(k=-3/(sqrt(3)))

equation
  connect(u_alpha, Add1.u1)
  connect(u_beta, Gain1.u)
  connect(Gain1.y, Add1.u2)
  connect(u_alpha, Gain3.u)
  connect(Gain3.y, Add2.u1)
  connect(u_beta, Gain4.u)
  connect(Gain4.y, Add2.u2)
  connect(Add1.y, Gain2.u)
  connect(Add1.y, abc[1])
  connect(Gain2.y, abc[2])
  connect(Add2.y, abc[3])
end SVMsector3p6;

model TriggeredPulse "Edge triggered pulse"

  parameter SI.Time T_pulse=1e-3 "width of triggered pulse";
  Modelica.Blocks.Logical.Timer Timer1(y(start=0))
  Modelica.Blocks.Logical.Edge Edge1
  Modelica.Blocks.Logical.Or Or1
  Modelica.Blocks.Logical.And And1
  Modelica.Blocks.Interfaces.BooleanInput u
  Modelica.Blocks.Interfaces.BooleanOutput y
  Modelica.Blocks.Logical.LessThreshold LessThreshold1(threshold=T_pulse)
  Modelica.Blocks.Logical.Pre Pre1

equation
  connect(u, Edge1.u)
  connect(Timer1.y, LessThreshold1.u)
  connect(Edge1.y, Or1.u1)
  connect(Or1.y, Timer1.u)
  connect(LessThreshold1.y, And1.u1)
  connect(Or1.y, y)
  connect(Or1.u2, Pre1.y)
  connect(Pre1.u, And1.y)
  connect(Or1.y, And1.u2)
end TriggeredPulse;

model DutyRatioToCycle "Duty ratio to duty cycle transform"

  Modelica.Blocks.Interfaces.RealInput dutyRatio[3]
  Modelica.Blocks.Interfaces.RealOutput dutyCycle[3]
  Modelica.Blocks.Math.Add Add[3]
  Modelica.Blocks.Sources.Constant One[3](each k=1)
  Modelica.Blocks.Math.Gain Gain[3](each k=1/2)
  Modelica.Blocks.Math.Gain per_cent[3](each k=1)

equation
      connect(dutyRatio, Add.u2)
  connect(One.y, Add.u1)
      connect(per_cent.y, dutyCycle)
  connect(Gain.y, per_cent.u)
  connect(Add.y, Gain.u)
end DutyRatioToCycle;
end SpaceVector;
end Modulation;


Control blocks

within PowerSystems;
package Control "Control blocks"
  extends Modelica.Icons.Package;
end Control;


Relays

within PowerSystems.Control;
package Relays "Relays"
  extends Modelica.Icons.VariantsPackage;

  block SwitchRelay "Relay for sequential switching "
    extends PowerSystems.Icons.Block0;

    parameter Integer n(min=1)=3 "number of signals";
    parameter Integer switched[:]=1:n "switched signals";
    parameter Boolean ini_state=true "initial state (closed true, open false)"
    parameter SI.Time t_switch[:]={1} "switching time vector";
    Modelica.Blocks.Interfaces.BooleanOutput[n] y(start=fill(ini_state, n), each fixed=true)
      "boolean state of switch (closed:true, open:false)"
  protected
    Integer cnt(start=1,fixed=true);

  algorithm
    when time > t_switch[cnt] then
      cnt := min(cnt + 1, size(t_switch, 1));
      for k in switched loop
        y[k] := not y[k];
      end for;
    end when;
  end SwitchRelay;

  block TapChangerRelay "Relay for setting tap-changer "
    extends PowerSystems.Icons.Block0;

    parameter Integer preset_1[:]={1}
      "1: positions tap changer";
    parameter Integer preset_2[:]={1}
      "2: positions tap changer";
    parameter SI.Time t_switch_1[:]={0.5} "1: switching times";
    parameter SI.Time t_switch_2[:]={0.5} "2: switching times";
    Modelica.Blocks.Interfaces.IntegerOutput tap_1(start = preset_1[1], fixed = true)
      "positions of tap changer 1"
    Modelica.Blocks.Interfaces.IntegerOutput tap_2(start = preset_2[1], fixed = true)
      "positions of tap changer 2"
  protected
    Integer cnt_1(start=1,fixed=true);
    Integer cnt_2(start=1,fixed=true);

  algorithm
    when time > t_switch_1[min(cnt_1, size(t_switch_1, 1))] then
      cnt_1 := cnt_1 + 1;
      tap_1 := preset_1[min(cnt_1, size(preset_1, 1))];
    end when;
    when time > t_switch_2[min(cnt_2, size(t_switch_2, 1))] then
      cnt_2 := cnt_2 + 1;
      tap_2 := preset_2[min(cnt_2, size(preset_2, 1))];
    end when;
  end TapChangerRelay;

  block TapChanger3Relay "Relay for setting tap-changer 3-winding transformer"
    extends PowerSystems.Icons.Block0;

    parameter Integer preset_1[:]={1}
      "1: positions tap changer";
    parameter Integer preset_2a[:]={1}
      "2a: positions tap changer";
    parameter Integer preset_2b[:]={1}
      "2b: positions tap changer";
    parameter SI.Time t_switch_1[:]={0.5} "1: switching times";
    parameter SI.Time t_switch_2a[:]={0.5} "2a: switching times";
    parameter SI.Time t_switch_2b[:]={0.5} "2b: switching times";
    Modelica.Blocks.Interfaces.IntegerOutput tap_1(start = preset_1[1], fixed = true)
      "positions of tap changer 1"
    Modelica.Blocks.Interfaces.IntegerOutput[2] tap_2(
      start = {preset_2a[1], preset_2b[1]}, each fixed = true)
      "positions of tap changers {2a,2b}"
  protected
    Integer cnt_1(start=1,fixed=true);
    Integer cnt_2a(start=1,fixed=true);
    Integer cnt_2b(start=1,fixed=true);

  algorithm
    when time > t_switch_1[min(cnt_1, size(t_switch_1, 1))] then
      cnt_1 := cnt_1 + 1;
      tap_1 := preset_1[min(cnt_1, size(preset_1, 1))];
    end when;
    when time > t_switch_2a[min(cnt_2a, size(t_switch_2a, 1))] then
      cnt_2a := cnt_2a + 1;
      tap_2[1] := preset_2a[min(cnt_2a, size(preset_2a, 1))];
    end when;
    when time > t_switch_2b[min(cnt_2b, size(t_switch_2b, 1))] then
      cnt_2b := cnt_2b + 1;
      tap_2[2] := preset_2b[min(cnt_2b, size(preset_2b, 1))];
    end when;
  end TapChanger3Relay;

  block Y_DeltaControl "Relay for Y-Delta topology switching "
    extends PowerSystems.Icons.Block0;

    parameter Boolean ini_state=true "initial state (Y true, D false)"
    parameter SI.Time t_switch[:]={1} "switching time vector";
    Modelica.Blocks.Interfaces.BooleanOutput y(start=ini_state, fixed=true)
      "boolean state (Y-top: true, Delta-top: false)"
  protected
    Integer cnt(start=1,fixed=true);

  algorithm
    when time > t_switch[cnt] then
      cnt := min(cnt + 1, size(t_switch, 1));
      y := not y;
    end when;
  end Y_DeltaControl;
end Relays;


Setpoints of generators

within PowerSystems.Control;
package Setpoints "Setpoints of generators"
  extends Modelica.Icons.VariantsPackage;

block Set_w_p_v "Constant setpoints w, p, v for generation"

  Modelica.Blocks.Interfaces.RealOutput[3] setpts
      "setpoints {speed, power, voltage} pu"
  protected
  parameter SIpu.AngularVelocity w_set(unit="1", fixed=false, start=1)
      "setpt turbine speed pu";
  parameter SIpu.Power p_set(unit="1", fixed=false, start=1)
      "setpt turbine power pu";
  parameter SIpu.Voltage v_set(unit="1", fixed=false, start=1)
      "setpt exciter voltage pu";

equation
  setpts[1] = w_set;
  setpts[2] = p_set;
  setpts[3] = v_set;
end Set_w_p_v;

block SetVariable_w_p_v "Variable setpoints w, p, v for generation"

  Modelica.Blocks.Interfaces.RealInput setpt_w "setpoint turbine speed pu"
  Modelica.Blocks.Interfaces.RealInput setpt_p "setpoint turbine power pu"
  Modelica.Blocks.Interfaces.RealInput setpt_v "setpoint exciter voltage pu"
  Modelica.Blocks.Interfaces.RealOutput[3] setpts
      "setpoints {speed, power, voltage} pu"
  protected
  parameter SIpu.AngularVelocity w_set(unit="1", fixed=false, start=1)
      "setpt turbine speed pu";
  parameter SIpu.Power p_set(unit="1", fixed=false, start=1)
      "setpt turbine power pu";
  parameter SIpu.Voltage v_set(unit="1", fixed=false, start=1)
      "setpt exciter voltage pu";

initial equation
  setpts[1] = w_set;
  setpts[2] = p_set;
  setpts[3] = v_set;

equation
  setpts[1] = setpt_w;
  setpts[2] = setpt_p;
  setpts[3] = setpt_v;
end SetVariable_w_p_v;

block Set_w_p "Set-points for generation"

  parameter SIpu.AngularVelocity w_set(unit="1", fixed=true)=1
      "setpoint turbine speed pu";
  Modelica.Blocks.Interfaces.RealInput setpt_p "setpoint turbine power pu"
  Modelica.Blocks.Interfaces.RealOutput[2] setpts "setpoints {speed, power} pu"

equation
  setpts[1] = w_set;
  setpts[2] = setpt_p;
end Set_w_p;
end Setpoints;


Tap changers

within PowerSystems.Control;
package TapChange "Tap changers"
  extends Modelica.Icons.VariantsPackage;

end TapChange;