Mathematical functions

within PowerSystems.Utilities;
package Math "Mathematical functions"
    extends Modelica.Icons.Package;

    function atanVarCut "arc-tangens with variable cut"
      extends Modelica.Icons.Function;

      input Real[2] x "2-dimensional vector";
      input SI.Angle alpha "angle";
      output SI.Angle phi
      "arc(x) with range (alpha-pi) < phi <= (alpha+pi)";
  protected
      Real c;
      Real s;
      import Modelica.Math.atan2;

    algorithm
      c := cos(-alpha);
      s := sin(-alpha);
      phi := atan2({s, c}*x, {c, -s}*x) + alpha;
    end atanVarCut;

    function angVelocity "Angular velocity of 2dim vector"
      extends Modelica.Icons.Function;

      input Real[2] x "2-dimensional vector";
      input Real[2] x_dot "time-derivative of x";
      output Real omega "angular velocity of x";
      import Modelica.Constants.eps;

    algorithm
      omega :=(x[1]*x_dot[2] - x_dot[1]*x[2])/(x*x + eps);
    end angVelocity;

    function mod2sign "Modulo-two sign"
      extends Modelica.Icons.Function;

      input Integer[:] n "integer vector";
      output Integer[size(n, 1)] sign_n "(-1)^n_k, k=1:size(n)";

    algorithm
      for k in 1:size(n, 1) loop
        if n[k] == 2*integer(n[k]/2) then
          sign_n[k] := 1;
      else
          sign_n[k] := -1;
        end if;
      end for;
    end mod2sign;

    function interpolateTable
    "Interpolation of tables with equidistant arguments"
      extends Modelica.Icons.Function;

      input Real x "table argument";
      input Real[:, :] xy_tab "table, [argument, values]";
      output Real[size(xy_tab, 2) - 1] y "interpolated table values";
  protected
      Integer N1=size(xy_tab, 1);
      Integer N2=size(xy_tab, 2);
      Real x0=xy_tab[1, 1];
      Real del_x=xy_tab[2, 1] - xy_tab[1, 1];
      Real x_rel;
      Integer nx;
      Integer n;

    algorithm
      x_rel := (x - x0)/del_x;
      nx := max(min(integer(x_rel), N1 - 2), 0);
      n := nx + 1;
      y := xy_tab[n, 2:N2] + (x_rel - nx)*(xy_tab[n + 1, 2:N2] - xy_tab[n, 2:N2]);
    end interpolateTable;

    function polyCoefReal "Coefficients of a polynomial from real roots"
      extends Modelica.Icons.Function;

      input Real[:] r "root vector";
      output Real[size(r,1)+1] c "coefficient vector";
  protected
      parameter Integer n=size(r,1);

    algorithm
      c := cat(1, zeros(n), {1});
      for k in n:-1:1 loop
      c[n:-1:k] := c[n:-1:k] - r[k]*c[n+1:-1:k+1];
      end for;

The resulting n+1 = 4 coefficients are:

  c = {-6, 11, -6, 1};

See also
polyCoef, polyRoots

"));
    end polyCoefReal;

    function polyCoef "Coefficients of a polynomial from roots"
      extends Modelica.Icons.Function;

      input Real[:,2] r "root vector, 2nd index=1:2, real and imaginary part";
      output Real[size(r,1)+1,2] c
      "coefficient vector, 2nd index=1:2, real and imaginary part";
  protected
      parameter Integer n=size(r,1);

    algorithm
      c := zeros(n+1,2);
      c[n+1,1] := 1;
      for k in n:-1:1 loop
      c[n:-1:k,:] := c[n:-1:k,:] - cat(2, r[k,1]*c[n+1:-1:k+1,1:1] - r[k,2]*c[n+1:-1:k+1,2:2], r[k,1]*c[n+1:-1:k+1,2:2] + r[k,2]*c[n+1:-1:k+1,1:1]);
      end for;

The resulting n+1 = 4 coefficients are:

  c = [-6, 0; 11, 0; -6, 0; 1, 0];

See also
polyCoefReal, polyRoots

"));
    end polyCoef;

    function polyRoots "Roots of a polynomial"
      extends Modelica.Icons.Function;

      input Real[:] c "coefficient vector";
      output Real[size(c,1)-1,2] r
      "root vector, 2nd index=1:2, real and imaginary part";
      output Integer N0
      "true deg of polynomial = number of valid roots (r[1:N0,:])";
  protected
      parameter Integer N=size(c,1)-1 "formal degree of polynome";
      Integer n;
      Integer n0;
      Real[N, N] A;
      Real[N+1] C;
      import Modelica.Math.Matrices.eigenValues;

    algorithm
      N0 := N "determine true degree of polymomial";
    //  while c[N0+1] == 0 and N0 > 0 loop
      while abs(c[N0+1])/max(abs(c)) < Modelica.Constants.eps and N0 > 0 loop
        N0 := N0 - 1;
      end while;
      if N0 == 0 then
        r := zeros(N,2);
    else
        n0 := 0;
        while c[n0+1] == 0 loop
          n0 := n0 + 1;
        end while;
        n := N0-n0;
        for k in 1:n+1 loop
          C[k] := c[n0+k];
        end for;
        A[1, 1:n] := -C[n:-1:1]/C[n+1];
        A[2:n,1:n-1] := diagonal(ones(n-1));
        A[2:n,n] := zeros(n-1);
        r[1:n0,:] := zeros(n0,2);
        r[n0+1:n0+n,:] := eigenValues(A[1:n, 1:n]);
      end if;

The resulting n = 2 roots are:

  r[1,:] = {-0.333333 +0.471405};
  r[2,:] = {-0.333333 -0.471405};

See also
polyCoefReal, polyCoef, eigenValues

"));
    end polyRoots;

    function fminSearch
    "Determines minimum of a scalar function with vector-argument x"
      extends Modelica.Icons.Function;

      input Real[:] x0 "start value, fcn(x0) is approximate min";
      input Real[:] x_opt "optional further arguments of function fcn";
      output Real[size(x0,1)] x "argument where function value is minimal";
      output Real y "value of function at x";
  protected
      replaceable function fcn = PowerSystems.Utilities.Precalculation.i_field
      "function to be minimised around x0";
      Integer n = size(x,1);
      Integer max_fun = 200*n;
      Integer max_iter = 200*n;
      constant Real tol_x = 1e-4;
      constant Real tol_f = 1e-4;
      constant Real rho = 1;
      constant Real chi = 2;
      constant Real psi = 0.5;
      constant Real sigma = 0.5;
      constant Real delta=0.05; //for non-zero terms
      constant Real delta0=0.00025; //for zero elements of x
      Integer funcount;
      Integer itercount;
      Integer ifv[n+1];
      Real[n,n+1] v;
      Real fv[n+1];
      Real[n] x1;
      Real[n] xbar;
      Real[n] xr;
      Real fxr;
      Real[n] xe;
      Real fxe;
      Real[n] xc;
      Real fxc;
      Real[n] xcc;
      Real fxcc;
      Boolean shrink;
      String msg;

    algorithm
    // Set up a simplex near the initial guess.
      v := zeros(n,n+1);
      fv := zeros(n+1);
      v[:,1] := x0; //Place input guess in the simplex
      fv[1] := fcn(x0, x_opt);
      funcount := 1;
      itercount := 0;

    // Following improvement suggested by L.Pfeffer at Stanford
      for j in 1:n loop
        x1 := x0;
        x1[j] := if abs(x1[j]) > Modelica.Constants.small then (1 + delta)*x1[j] else delta0;
        v[:,j+1] := x1;
        fv[j+1] := fcn(x1, x_opt);
      end for;

    // Sort so v(1,:) has the lowest function value
      (fv, ifv) := sortUp(fv);
      v := v[:, ifv];
      itercount := itercount + 1;
      funcount := n + 1;

    // Main algorithm
      while (funcount < max_fun and itercount < max_iter) and
        (max(abs(fv[ones(n)] - fv[2:n+1])) > tol_f or
         max(abs(v[:,ones(n)] - v[:,2:n+1])) > tol_x) loop

    // Compute the reflection point
        xbar := sum(v[:,k] for k in 1:n)/n; //average of the n (NOT n+1) best points
        xr := (1 + rho)*xbar - rho*v[:,end];
        fxr := fcn(xr, x_opt);
        funcount := funcount+1;

        if fxr < fv[1] then // Calculate the expansion point
          xe := (1 + rho*chi)*xbar - rho*chi*v[:,end];
          fxe := fcn(xe, x_opt);
          funcount := funcount + 1;
          if fxe < fxr then
            v[:,end] := xe;
            fv[end] := fxe;
        else  v[:,end] := xr;
              fv[end] := fxr;
          end if;
      else
          if fxr < fv[n] then
              v[:,end] := xr;
              fv[end] := fxr;
        else //perform contraction
            if fxr < fv[end] then //outside contraction
              xc := (1 + psi*rho)*xbar - psi*rho*v[:,end];
              fxc := fcn(xc, x_opt);
              funcount := funcount + 1;
              if fxc <= fxr then
                v[:,end] := xc;
                fv[end] := fxc;
                shrink := false;
            else //perform a shrink
                shrink := true;
              end if;
          else //inside contraction
              xcc := (1-psi)*xbar + psi*v[:,end];
              fxcc := fcn(xcc, x_opt);
              funcount := funcount + 1;
              if fxcc < fv[end] then
                v[:,end] := xcc;
                fv[end] := fxcc;
                shrink := false;
            else //perform a shrink
                shrink := true;
              end if;
            end if;
            if shrink then
              for j in 2:n+1 loop
                v[:,j] := v[:,1] + sigma*(v[:,j] - v[:,1]);
                fv[j] := fcn(v[:,j], x_opt);
              end for;
            funcount := funcount + n;
            end if;
          end if;
        end if;

        (fv, ifv) := sortUp(fv);
        v := v[:, ifv];
        itercount := itercount + 1;
      end while;

      x := v[:,1];
    //if isPresent(y) then
      y := fv[1];
    //end if;

      if funcount >= max_fun then
        msg := "fminSearch: max number of function evaluations EXCEEDED";
    elseif itercount >= max_iter then
        msg := "fminSearch: max number of iterations EXCEEDED";
    else
        msg := "fminSearch: terminated successfully";
      end if;
    end fminSearch;

    function sortUp "Sorts components of x in increasing order"
      extends Modelica.Icons.Function;

      input Real[:] x "x unsorted";
      output Real[size(x,1)] y "x sorted increasing";
      output Integer[size(x,1)] i "indizes of sorted x";
  protected
      Integer n = size(x,1);
      Integer itemp;
      Real ytemp;

    algorithm
      y := x;
      i := 1:n;
      for j in 1:n-1 loop
        for k in j+1:n loop
          if y[j] > y[k] then
            ytemp := y[j];
            y[j] := y[k];
            y[k] := ytemp;
            itemp := i[j];
            i[j] := i[k];
            i[k] := itemp;
          end if;
        end for;
      end for;
    end sortUp;

    function sortDown "Sorts components of x in decreasing order"
      extends Modelica.Icons.Function;

      input Real[:] x "x unsorted";
      output Real[size(x,1)] y "x sorted decreasing";
      output Integer[size(x,1)] i "indizes of sorted x";
  protected
      Integer n = size(x,1);
      Integer itemp;
      Real ytemp;

    algorithm
      y := x;
      i := 1:n;
      for j in 1:n-1 loop
        for k in j+1:n loop
          if y[j] < y[k] then
            ytemp := y[j];
            y[j] := y[k];
            y[k] := ytemp;
            itemp := i[j];
            i[j] := i[k];
            i[k] := itemp;
          end if;
        end for;
      end for;
    end sortDown;

    function relaxation "Exponential relaxation function"
      extends Modelica.Icons.Function;

      input Real t "relative time";
      input Real t_char "characteristic time";
      input Real beta(min=2) "power of exponent";
      output Real[2] y "relaxation function {decreasing, increasing}";
  protected
      final parameter Real gamma=exp(-0.5);
      Real dt=1-t/t_char;

    algorithm
      y[2] := if t < 0 then 1 else if t < t_char then (exp(-0.5*dt^beta) - gamma)/(1 - gamma) else 1;
      y[1] := 1 - y[2];
    end relaxation;

    function taylor "Taylor series"
      extends Modelica.Icons.Function;

      input Real x "argument";
      input Real[:] c "coefficients";
      output Real y "sum(c[n]*x^n)";
  protected
      Real x_k;

    algorithm
      y :=1;
      x_k := 1;
      for k in 1:size(c, 1) loop
        x_k := x*x_k;
        y := y + c[k]*x_k;
      end for;
    end taylor;

    function sign_gtlt "Characteristic function abs(x)>b"
      extends Modelica.Icons.Function;

      input Real[:] x "argument";
      input Real b(min=0) "threshold value";
      output Real[size(x,1)] y "characteristic function of abs(x) > b";

    algorithm
      for k in 1:size(x,1) loop
        y[k] := if x[k] > b then 1 else if x[k] < -b then -1 else 0;
      end for;
    end sign_gtlt;

    function sign_gt "Sign function x>b"
      extends Modelica.Icons.Function;

      input Real[:] x "argument";
      input Real b(min=0) "threshold value";
      output Real[size(x,1)] y "characteristic function of x > b";

    algorithm
      for k in 1:size(x,1) loop
        y[k] := if x[k] > b then 1 else 0;
      end for;
    end sign_gt;

    function sign_lt "Sign function x<b"
      extends Modelica.Icons.Function;

      input Real[:] x "argument";
      input Real b(min=0) "threshold value";
      output Real[size(x,1)] y "characteristic function of x < b";

    algorithm
      for k in 1:size(x,1) loop
        y[k] := if x[k] < b then -1 else 0;
      end for;
    end sign_lt;
end Math;


Basic utilities

within PowerSystems;
package Utilities "Basic utilities"
  extends Modelica.Icons.UtilitiesPackage;

end Utilities;


Precalculation functions

within PowerSystems.Utilities;
package Precalculation "Precalculation functions"
  extends Modelica.Icons.Package;

  function baseV "Base voltage"
    extends Modelica.Icons.Function;

    input Boolean puUnits "= true if pu else SI units";
    input SI.Voltage V_nom "nom voltage";
    output SI.Voltage V_base "base voltage";

  algorithm
    if puUnits then
      V_base := V_nom;
    else
      V_base := 1;
    end if;
  end baseV;

  function baseI "Base current"
    extends Modelica.Icons.Function;

    input Boolean puUnits "= true if pu else SI units";
    input SI.Voltage V_nom "nom voltage";
    input SI.ApparentPower S_nom "apparent power";
    output SI.Current I_base "base current";

  algorithm
    if puUnits then
      I_base := S_nom/V_nom;
    else
      I_base := 1;
    end if;
  end baseI;

  function baseS "Base power"
    extends Modelica.Icons.Function;

    input Boolean puUnits "= true if pu else SI units";
    input SI.ApparentPower S_nom "apparent power";
    output SI.ApparentPower S_base "base power";

  algorithm
    if puUnits then
      S_base := S_nom;
    else
      S_base := 1;
    end if;
  end baseS;

  function baseR "Base resistance"
    extends Modelica.Icons.Function;

    input Boolean puUnits "= true if pu else SI units";
    input SI.Voltage V_nom "nom voltage";
    input SI.ApparentPower S_nom "apparent power";
    input Integer scale=1 "scaling factor topology (Y:1, Delta:3)";
    output SI.Resistance R_base "base resistance";

  algorithm
    if puUnits then
      R_base := scale*V_nom*V_nom/S_nom;
    else
      R_base := scale;
    end if;
  end baseR;

  function baseL "Base inductance"
    extends Modelica.Icons.Function;

    input Boolean puUnits "= true if pu else SI units";
    input SI.Voltage V_nom "nom voltage";
    input SI.ApparentPower S_nom "apparent power";
    input SI.AngularFrequency omega_nom "angular frequency";
    output SI.Inductance L_base "base inductance";

  algorithm
    if puUnits then
      L_base := V_nom*V_nom/(S_nom*omega_nom);
    else
      L_base := 1;
    end if;
  end baseL;

  function baseRL "Base resistance and inductance"
    extends Modelica.Icons.Function;

    input Boolean puUnits "= true if pu else SI units";
    input SI.Voltage V_nom "nom voltage";
    input SI.ApparentPower S_nom "apparent power";
    input SI.AngularFrequency omega_nom "angular frequency";
    input Integer scale=1 "scaling factor topology (Y:1, Delta:3)";
    output Real[2] RL_base "base {resistance, inductance}";

  algorithm
    if puUnits then
      RL_base := scale*(V_nom*V_nom/S_nom)*{1, 1/omega_nom};
    else
      RL_base := scale*({1, 1/omega_nom});
    end if;
  end baseRL;

  function baseGC "Base conductance and capacitance"
    extends Modelica.Icons.Function;

    input Boolean puUnits "= true if pu else SI units";
    input SI.Voltage V_nom "nom voltage";
    input SI.ApparentPower S_nom "apparent power";
    input SI.AngularFrequency omega_nom "angular frequency";
    input Integer scale=1 "scaling factor topology (Y:1, Delta:3)";
    output Real[2] GC_base "base {conductance, capacitance}";

  algorithm
    if puUnits then
      GC_base := (S_nom/(V_nom*V_nom))*{1, 1/omega_nom}/scale;
    else
      GC_base := {1, 1/omega_nom}/scale;
    end if;
  end baseGC;

  function baseTrafoV "Base voltage transformers"
    extends Modelica.Icons.Function;

    input Boolean puUnits "= true if pu else SI units";
    input SI.Voltage[:] V_nom "nom voltage {prim, sec} or {prim, sec1, sec2}";
    output SI.Voltage[size(V_nom,1)] V_base
      "base voltage {prim,sec} or {prim, sec1, sec2}";

  algorithm
    if puUnits then
      V_base := V_nom;
    else
      V_base := ones(size(V_nom,1));
    end if;
  end baseTrafoV;

  function baseTrafoRL "Base resistance and inductance transformers"
    extends Modelica.Icons.Function;

    input Boolean puUnits "= true if pu else SI units";
    input SI.Voltage[:] V_nom "nom voltage {prim, sec} or {prim, sec1, sec2}";
    input SI.ApparentPower S_nom "apparent power";
    input SI.AngularFrequency omega_nom "angular frequency";
    output Real[size(V_nom,1), 2] RL_base "base [prim res, prim ind; sec res, sec ind] or
   [prim res, prim ind; sec1 res, sec1 ind; sec2 res, sec2 ind]";

  algorithm
    if puUnits then
      RL_base := fill(V_nom[1]^2/S_nom, size(V_nom,1), 1)*[1, 1/omega_nom];
    else
      RL_base := [(fill(V_nom[1],size(V_nom,1))./ V_nom).^2]*[1, 1/omega_nom];
    end if;
  end baseTrafoRL;

  function machineDCser "Calculates coefficients of DC-machine series excited"
    extends Modelica.Icons.Function;

    input AC1ph_DC.Machines.Parameters.DCser p "parameters DC machine series";
    output AC1ph_DC.Machines.Coefficients.DCser c
      "coefficients DC machine series";
  protected
    final parameter SI.AngularVelocity w_el_nom=p.pp*p.w_nom;
    final parameter SI.Resistance R_base=Utilities.Precalculation.baseR(
                                               p.puUnits, p.V_nom, p.S_nom)
      "base resistance";
    final parameter SI.Inductance L_base=Utilities.Precalculation.baseL(
                                               p.puUnits, p.V_nom, p.S_nom, w_el_nom)
      "base resistance";

  algorithm
    c.L := (p.l_fd + p.l_q)*L_base;
    c.R := {p.r_fd, p.r_q}*R_base;
    c.L_md := (p.V_nom^2/p.S_nom - sum(c.R))/w_el_nom;
  end machineDCser;

  function machineDCpar
    "Calculates coefficients of DC-machine parallel excited"
    extends Modelica.Icons.Function;

    input AC1ph_DC.Machines.Parameters.DCpar p "parameters DC machine parallel";
    output AC1ph_DC.Machines.Coefficients.DCpar c
      "coefficients DC machine parallel";
  protected
    final parameter SI.AngularVelocity w_el_nom=p.pp*p.w_nom;
    final parameter SI.Resistance R_base=Utilities.Precalculation.baseR(
                                               p.puUnits, p.V_nom, p.S_nom)
      "base resistance";
    final parameter SI.Inductance L_base=Utilities.Precalculation.baseL(
                                               p.puUnits, p.V_nom, p.S_nom, w_el_nom)
      "base resistance";
    SI.AngularFrequency w_el_lim;

  algorithm
    c.L := {p.l_fd, p.l_q}*L_base;
    c.R := {p.r_fd, p.r_q}*R_base;
    w_el_lim := w_el_nom/(1 - c.R[2]*p.S_nom/p.V_nom^2);
    c.L_md := (c.R[1]/w_el_lim)*(p.V_nom/p.Vf_nom);
  end machineDCpar;

  function machineDCpm "Calculates coefficients of DC-machine permanent magnet"
    extends Modelica.Icons.Function;

    input AC1ph_DC.Machines.Parameters.DCpm p "parameters DC machine pm";
    output AC1ph_DC.Machines.Coefficients.DCpm c "coefficients DC machine pm";
  protected
    final parameter SI.AngularVelocity w_el_nom=p.pp*p.w_nom;
    final parameter SI.Resistance R_base=Utilities.Precalculation.baseR(
                                               p.puUnits, p.V_nom, p.S_nom)
      "base resistance";
    final parameter SI.Inductance L_base=Utilities.Precalculation.baseL(
                                               p.puUnits, p.V_nom, p.S_nom, w_el_nom)
      "base resistance";

  algorithm
    c.L := p.l_aq*L_base;
    c.R := p.r_aq*R_base;
    c.Psi_pm := (p.V_nom - c.R*p.S_nom/p.V_nom)/w_el_nom;
  end machineDCpm;

  function machineAsyn
    "Calculates coefficient matrices of asynchronous machine"
    extends Modelica.Icons.Function;

    input AC3ph.Machines.Parameters.Asynchron p
      "parameters asynchronous machine";
    input Integer scale=1 "scaling factor topology (Y:1, Delta:3)";
    output AC3ph.Machines.Coefficients.Asynchron c(n_r=p.n_r)
      "coefficient matrices asynchronous machine";
  protected
    final parameter Integer n_r=p.n_r "number of rotor circuits";
    final parameter SI.AngularFrequency omega_nom=2*pi*p.f_nom;
    final parameter Real[2] RL_base=Utilities.Precalculation.baseRL(
                                           p.puUnits, p.V_nom, p.S_nom, omega_nom, scale)
      "base resistance inductance";

    SI.Angle[n_r] Tc "time constant closed-loop";
    SI.Angle[n_r] To "time constant open-loop";
    SI.Resistance[n_r+1] zr;
    SI.Reactance[n_r+1,n_r+1] zx;
    import Modelica.Math.Matrices.solve;

  algorithm
    if p.transDat then
      if p.use_xtr then
        assert(size(p.tc,1) == size(p.xtr,1), "size of tc and xtr must be equal!");
    else
        assert(size(p.tc,1) == size(p.to,1), "size of tc and to must be equal!");
      end if;
      Tc := omega_nom*p.tc;
      To := if p.use_xtr then T_open(p.x, p.xtr, Tc) else omega_nom*p.to;
      (zr, zx) := z_fromTransDat(n_r, Tc, To, p.x, p.xsig_s, p.r_s, 0, 0, 0, false);
    else
      (zr, zx) := z_fromEqCirc(n_r, p.x, p.xsig_s, p.r_s, zeros(n_r-1), p.xsig_r, p.r_r);
    end if;

  //  c.L_s := {p.x, p.x, p.x_0}*RL_base[2];
    c.L_s := {zx[n_r+1,n_r+1], zx[n_r+1,n_r+1], p.x_0}*RL_base[2];
    c.L_r := zx[1:n_r,1:n_r]*RL_base[2];
    c.L_m := zx[n_r+1,1:n_r]*RL_base[2];
    c.R_s := p.r_s*RL_base[1];
    c.R_r := zr[1:end-1]*RL_base[1];
    c.R_n := p.r_n*RL_base[1];
    if n_r == 1 then
      c.R_m := c.R_r[1] / c.L_r[1,1] * c.L_m;
    else
      c.R_m := c.R_r .* solve(c.L_r, c.L_m);
    end if;
  end machineAsyn;

  function machineSyn3rd
    "Calculates coefficient matrices of synchronous machine, 3rd order"
    extends Modelica.Icons.Function;

    input AC3ph.Machines.Parameters.Synchron3rd p
      "parameters synchronous machine 3rd order";
    input Integer scale=1 "scaling factor topology (Y:1, Delta:3)";
    output AC3ph.Machines.Coefficients.Synchron3rd c
      "coefficient matrices synchronous machine 3rd order";
  protected
    final parameter SI.AngularFrequency omega_nom=2*pi*p.f_nom;
    final parameter Real[2] RL_base=Utilities.Precalculation.baseRL(
                                           p.puUnits, p.V_nom, p.S_nom, omega_nom, scale)
      "base resistance inductance";

  algorithm
    c.L_s := {p.x_d, p.x_q, p.x_0}*RL_base[2];
    c.R_s := p.r_s*RL_base[1];
    c.R_n := p.r_n*RL_base[1];
    c.Psi_pm := p.psi_pm*(p.V_nom/omega_nom);
    c.omega_nom := omega_nom;
  end machineSyn3rd;

  function machineSyn "Calculates coefficient matrices of synchronous machine"
    extends Modelica.Icons.Function;

    input AC3ph.Machines.Parameters.Synchron p "parameters synchronous machine";
    input Integer scale=1 "scaling factor topology (Y:1, Delta:3)";
    output AC3ph.Machines.Coefficients.Synchron c(n_d=p.n_d, n_q=p.n_q)
      "coefficient matrices synchronous machine";

  protected
    final parameter Integer n_d=p.n_d "number of rotor circuits d-axis";
    final parameter Integer n_q=p.n_q "number of rotor circuits q-axis";
    final parameter SI.AngularFrequency omega_nom=2*pi*p.f_nom;
    final parameter Real[2] RL_base=Utilities.Precalculation.baseRL(
                                           p.puUnits, p.V_nom, p.S_nom, omega_nom, scale)
      "base resistance inductance";
    final parameter SI.Current If_base=(p.x_d - p.xsig_s)*p.If_nom;
    final parameter Real if0_pu= if p.puUnits then p.if0 else p.if0/If_base;
    final parameter SI.Angle alpha_if0=(p.alpha_if0 + pi)
      "mathematical sign convention";

    SI.Angle[n_d] Tc_d "time constant closed-loop d-axis";
    SI.Angle[n_q] Tc_q "time constant closed-loop q-axis";
    SI.Angle[n_d] To_d "time constant open-loop d-axis";
    SI.Angle[n_q] To_q "time constant open-loop q-axis";
    SI.Resistance[n_d+1] zr_d;
    SI.Resistance[n_q+1] zr_q;
    SI.Reactance[n_d+1,n_d+1] zx_d;
    SI.Reactance[n_q+1,n_q+1] zx_q;

  algorithm
    c.Psi_pm := p.psi_pm*(p.V_nom/omega_nom);
    if p.transDat then
      if p.use_xtr then
        assert(size(p.tc_d,1) == size(p.xtr_d,1), "size of tc_d and xtr_d must be equal!");
        assert(size(p.tc_q,1) == size(p.xtr_q,1), "size of tc_q and xtr_q must be equal!");
    else
        assert(size(p.tc_d,1) == size(p.to_d,1), "size of tc_d and to_d must be equal!");
        assert(size(p.tc_q,1) == size(p.to_q,1), "size of tc_q and to_q must be equal!");
      end if;
      Tc_d := omega_nom*p.tc_d;
      Tc_q := omega_nom*p.tc_q;
      To_d := if p.use_xtr then T_open(p.x_d, p.xtr_d, Tc_d) else omega_nom*p.to_d;
      To_q := if p.use_xtr then T_open(p.x_q, p.xtr_q, Tc_q) else omega_nom*p.to_q;
      if p.use_if0 and p.excite==1 then
        (zr_d, zx_d) := z_fromTransDat(n_d, Tc_d, To_d, p.x_d, p.xsig_s, p.r_s, if0_pu, alpha_if0, p.tol, true);
    else
        (zr_d, zx_d) := z_fromTransDat(n_d, Tc_d, To_d, p.x_d, p.xsig_s, p.r_s, 0, 0, p.tol, false);
      end if;
        (zr_q, zx_q) := z_fromTransDat(n_q, Tc_q, To_q, p.x_q, p.xsig_s, p.r_s, 0, 0, p.tol, false);
    else
      if p.use_if0 and p.excite==1 then
        (zr_d, zx_d) := z_fromEqCirc(n_d, p.x_d, p.xsig_s, p.r_s, p.xm_d, p.xsig_rd, p.r_rd);
    else
        (zr_d, zx_d) := z_fromEqCirc(n_d, p.x_d, p.xsig_s, p.r_s, zeros(n_d-1), p.xsig_rd, p.r_rd);
      end if;
        (zr_q, zx_q) := z_fromEqCirc(n_q, p.x_q, p.xsig_s, p.r_s, zeros(n_q-1), p.xsig_rq, p.r_rq);
    end if;

  //  c.L_s := {p.x_d, p.x_q, p.x_0}*RL_base[2];
    c.L_s := {zx_d[n_d+1,n_d+1], zx_q[n_q+1,n_q+1], p.x_0}*RL_base[2];
    c.L_rd := zx_d[1:n_d,1:n_d]*RL_base[2];
    c.L_rq := zx_q[1:n_q,1:n_q]*RL_base[2];
    c.L_md := zx_d[n_d+1,1:n_d]*RL_base[2];
    c.L_mq := zx_q[n_q+1,1:n_q]*RL_base[2];
    c.R_s := p.r_s*RL_base[1];
    c.R_rd := zr_d[1:end-1]*RL_base[1];
    c.R_rq := zr_q[1:end-1]*RL_base[1];
    c.R_n := p.r_n*RL_base[1];
    c.wf := omega_nom*c.L_md[1]*p.If_nom/p.V_nom;
    c.Vf_nom := if p.excite==1 then (c.R_rd[1]/(c.wf*c.wf))*p.If_nom else 0;
    c.omega_nom := omega_nom;
  end machineSyn;

  function polyCoef "Calculates polynome coefficients from time constants"
    extends Modelica.Icons.Function;
    input SI.Angle[:] T "time constant";
    output Real[size(T,1)] a "coefficients of polynome";
  protected
    parameter Integer n=size(T,1);

  algorithm
    a := fill(0, n);
    for k in 1:n loop
      a[1:k] := cat(1, {a[1] + T[k]}, a[2:k] + a[1:k-1]*T[k]);
    end for;
  end polyCoef;

  function polyTime "Calculates time constants from polynome coefficients"

    extends Modelica.Icons.Function;

    input Real[:] a "coefficients of polynome";
    output SI.Angle[size(a,1)] T "time constant";
    output Boolean Tisreal "true if all time constants real";
    import Modelica.Constants.eps;
  protected
    parameter Integer n=size(a,1);
    Real[n, n] A;
    Real[n,2] lam "2nd index=1:2, real and imaginary part";
    import Modelica.Math.Matrices.eigenValues;
    import PowerSystems.Utilities.Math.sortDown;

  algorithm
    A[1, 1:n] := -cat(1, a[n-1:-1:1], {1})/a[n];
    A[2:n,1:n-1] := diagonal(ones(n-1));
    A[2:n,n] := zeros(n-1);
    lam := eigenValues(A);
    Tisreal := max(abs(lam[:, 2])) < eps;
    T := -ones(n)./lam[n:-1:1,1];
    T := sortDown(T);
  end polyTime;

  function x_transient
    "Calculates x_transient from x, T_closed, and T_open, forward transform"
    extends Modelica.Icons.Function;

    input Real x(unit="1") "total or syn reactance";
    input SI.Angle[:] Tc "time constant closed-loop";
    input SI.Angle[size(Tc,1)] To "time constant open-loop";
    output SIpu.Reactance[size(Tc,1)] xtr(each unit="1") "transient reactance";
  protected
    parameter Integer n=size(Tc,1);
    Real[n] y;
    Real[n-1] Tc_p;

  algorithm
    if n==0 then
      xtr := fill(0, n);
    elseif n==1 then
      y[1] := -(Tc[1] - To[1])/Tc[1];
      xtr[1] := x/(1 + y[1]);
    else
      for j in 1:n loop
        Tc_p := fill(Tc[j], n - 1) - cat(1, Tc[1:j - 1], Tc[j + 1:n]);
        y[j] := -product(fill(Tc[j], n) - To)/(Tc[j]*product(Tc_p));
      end for;
      xtr[1] := x/(1 + y[1]);
    end if;
    for j in 2:n loop
      xtr[j] := x*xtr[j - 1]/(x + xtr[j - 1]*y[j]);
    end for;
  end x_transient;

  function T_closed
    "Calculates T_closed from x, x_transient, and T_open, backward transform"
    extends Modelica.Icons.Function;

    input SIpu.Reactance x(unit="1") "total or syn reactance";
    input SIpu.Reactance[:] xtr(each unit="1") "transient reactance";
    input SI.Angle[size(xtr,1)] To "time constant open-loop";
    output SI.Angle[size(xtr,1)] Tc "time constant closed-loop";
  protected
    parameter Integer n=size(xtr,1);
    Real[n] y;
    Real[n] ac;
    Real[n-1,n] A;

  algorithm
    // not implemented
  end T_closed;

  function T_open
    "Calculates T_open from x, x_transient, and T_closed, backward transform"
    extends Modelica.Icons.Function;

    input SIpu.Reactance x(unit="1") "total or syn reactance";
    input SIpu.Reactance[:] xtr(each unit="1") "transient reactance";
    input SI.Angle[size(xtr,1)] Tc "time constant closed-loop";
    output SI.Angle[size(xtr,1)] To "time constant open-loop";
  protected
    parameter Integer n=size(xtr,1);
    Real[n] y;
    Real[n] ac;
    Real[n-1,n] A;
    Boolean Treal;

  algorithm
    if n == 0 then
      To := fill(0, n);
    else
    y := x./xtr;
    y := y - cat(1, {1}, y[1:end-1]);
    ac := polyCoef(Tc);
    for j in 1:n loop
      A[:,j] := polyCoef(cat(1, Tc[1:j-1], Tc[j+1:n]));
    end for;
    (To, Treal) := polyTime(ac*x/xtr[n] - cat(1, A*y, {0}));
    end if;
  end T_open;

  function i_field "Calculates complex field current"
    extends Modelica.Icons.Function;
    import PowerSystems.Utilities.Complex.ComplexType;
    import PowerSystems.Utilities.Complex.re;
    import PowerSystems.Utilities.Complex.im;

    input SIpu.Reactance[:] xm2_n(each unit="1") "approximate value of xm[2:n]";
    input Real[:] x_opt "additional input arguments";
    output Real di_f2 "i_f*conjugate(i_f)";
    output Boolean result "true if Tsig real and convergence tol-ok";
  protected
    Integer n=size(xm2_n,1) + 1;
  //x_opt collects the following 9 variables:
    Real[n] ac = x_opt[1:n];
    Real[n] ao = x_opt[n+1:2*n];
    Real[n] Tc = x_opt[2*n+1:3*n];
    Real[n] To = x_opt[3*n+1:4*n];
    Real xsig_s = x_opt[4*n+1];
    Real xm_s = x_opt[4*n+2];
    Real r_s = x_opt[4*n+3];
    Real[2] i_f0 = x_opt[4*n+4:4*n+5];
    Real tol = x_opt[4*n+6];

    Real qs;
    Real Tsig_s;
    Real[n] Tsig;
    Real[n] xsig;

    ComplexType[n] Qsig;
    ComplexType[n] Qo;
    ComplexType[n] Qc;
    ComplexType di_f;

  algorithm
    qs :=xsig_s/(xm_s);
    Tsig_s := xsig_s/r_s;
    (Tsig, xsig, result) := Tsig_xsig(n, ac, ao, xsig_s, cat(1, {0}, xm2_n, {xm_s}), true, tol);

    for k in 1:n loop
      Qsig[k,:,:] := re + im*Tsig[k];
      Qc[k,:,:] := re + im*Tc[k];
      Qo[k,:,:] := re + im*To[k];
    end for;

    di_f := -im*Tsig_s*Tsig[1]*Complex.prodC(Qsig[2:n,:,:])*
           Complex.invC(xsig[1]*(qs*Complex.prodC(Qo) + (1 + qs)*im*Tsig_s*Complex.prodC(Qc))) -
           (re*i_f0[1] + im*i_f0[2]);
    di_f2 := Complex.detC(di_f);
  end i_field;

  function Tsig_xsig "Calculates Tsig and xsig"
    extends Modelica.Icons.Function;

    input Integer n "transient order";
    input Real[n] ac "polynome coefficient closed loop";
    input Real[n] ao "polynome coefficient open loop";
    input SIpu.Reactance xsig_s(unit="1") "leakage reactance stator";
    input SIpu.Reactance[n+1] xm(each unit="1") "coupling reactance";
    input Boolean field "field winding yes/no";
    input Real tol "tolerance, iterative solution";
    output SI.Angle[n] Tsig "sig-time constants";
    output SIpu.Reactance[n] xsig(each unit="1") "leakage reactance rotor";
    output Boolean result "true if Tsig real and convergence tol-ok";
  protected
    final parameter Integer n1=n-1;
    constant Integer maxiter=10;
    Integer iter;
    Real qs;
    Real qm;
    Real qsm;
    Real[n] asig;
    Real[n] asig0;
    Real[n] bsig;
    Real[n] gsig;
    Real[n,n] A;
    Real[n,n] B;
    Real[n,n] C;
    Real[n] asig_prx;
    Real[n] gsig_prx;
    Real dasig;
    Real dgsig;
    import Modelica.Math.Matrices.inv;

  algorithm
    iter := 0;
    qs := xsig_s/xm[n+1];
    qm := xm[n]/xm[n+1];
    qsm :=(xsig_s + xm[n+1])/(xm[n+1]*xm[n+1]);

    bsig := qsm*(ao - ac);
    if not field then
      asig := ac - qs*(ao - ac);
      (Tsig, result) := polyTime(asig);
      for j in 1:n loop
        B[j,1] := Tsig[j];
        B[j,2:n] := polyCoef(cat(1, Tsig[1:j-1], Tsig[j+1:n]))*Tsig[j];
      end for;
      gsig := bsig*inv(B);
    elseif field then
      asig := ac - (qs + qm + qs*qm)*(ao - ac);
      (Tsig, result) :=polyTime(asig);
      if n < 3 then
        for j in 1:n loop
          B[j,1] := Tsig[j];
          B[j,2:n] := polyCoef(cat(1, Tsig[1:j-1], Tsig[j+1:n]))*Tsig[j];
        end for;
        gsig := bsig*inv(B);
    else
        dgsig := 1 + tol;
        dasig := 1 + tol;
        asig0 := asig;
        gsig :=zeros(n);
        while (dgsig > tol or dasig > tol) and iter < maxiter and result loop
          iter := iter + 1;
          gsig_prx := gsig;
          asig_prx := asig;
          for j in 1:n loop
            B[j,1] := Tsig[j];
            B[j,2:n] := polyCoef(cat(1, Tsig[1:j-1], Tsig[j+1:n]))*Tsig[j];
          end for;
          for j in 1:n1 loop
            C[j,1:2] := Tsig[j]*Tsig[n]*{0, 1};
            C[j,3:n] := polyCoef(cat(1, Tsig[1:j-1], Tsig[j+1:n1]))*Tsig[j]*Tsig[n];
          end for;
          C[n,1:n] :=zeros(n);
          A := B + xm[n1]*gsig[n]*C;
          A[n,:] := A[n, :] + xm[n1]*gsig*C;
          gsig :=(bsig + xm[n1]*gsig[n]*gsig*C)*inv(A);
          asig := asig0 - xm[n1]*gsig[1:n1]*B[1:n1,:];
          (Tsig, result) := polyTime(asig);
          dgsig := sum(abs(gsig - gsig_prx))/sum(abs(gsig));
          dasig := sum(abs(asig - asig_prx))/sum(abs(asig));
        end while;
        if iter == maxiter then
          result := false;
        end if;
      end if;
    end if;
    xsig := ones(n)./gsig;
  end Tsig_xsig;

  function z_fromTransDat "Calculates impedance matrix z from transient data"
    extends Modelica.Icons.Function;

    input Integer n "transient order";
    input SI.Angle[n] Tc "time constant closed-loop";
    input SI.Angle[n] To "time constant open-loop";
    input SIpu.Reactance x(unit="1") "total or syn reactance";
    input SIpu.Reactance xsig_s(unit="1") "leakage reactance stator";
    input SIpu.Resistance r_s "resistance stator";
    input SIpu.Current if0(unit="1") "field current";
    input SI.Angle alpha_if0
      "angle field current (sign: mathematical convention)";
    input Real tol "tolerance, iterative solution";
    input Boolean field "field winding yes/no";
    output SIpu.Resistance[n+1] zr(each unit="1") "impedance matrix resistive";
    output SIpu.Reactance[n+1,n+1] zx(each unit="1")
      "impedance matrix reactive";
  protected
    Real[n] ac;
    Real[n] ao;
    Real[n+1] xm;
    Real[n] Tsig;
    Real[n] xsig;
    Boolean result;
    import PowerSystems.Utilities.Math.fminSearch;

  algorithm
    if n==0 then
      zr := {r_s};
      zx := [x];
    else
      ac := polyCoef(Tc);
      ao := polyCoef(To);
      xm := cat(1, zeros(n), {x - xsig_s});
      if field and n>1 then //minimises deviation from desired i_f
  ///// possible start-values:
        if n==2 then
          xm[n] := 0.06;
      elseif n>2 then
          xm[n-1:n] := {0.02,0.04};
        end if;
  ///// may be eliminated after modification of fminSearch!
        (xm[2:n],) := fminSearch(xm[2:n],
        cat(1, ac, ao, Tc, To, {xsig_s}, {xm[n+1]}, {r_s}, if0*{cos(alpha_if0),sin(alpha_if0)}, {tol}));
      end if;

      (Tsig, xsig, result) := Tsig_xsig(n, ac, ao, xsig_s, xm, field, tol);
      assert(result, "Tsig and xsig may be false, no convergence, or complex time-constants");

      zr := cat(1, xsig./Tsig, {r_s});
      zx := diagonal(cat(1, xsig, {xsig_s}));

      for k in 2:n+1 loop
        zx[1:k,1:k] := zx[1:k,1:k] + fill(xm[k], k, k);
      end for;
    end if;
  end z_fromTransDat;

  function z_fromEqCirc "Calculates impedance matrix z from equivalent circuit"
    extends Modelica.Icons.Function;

    input Integer n "transient order";
    input SIpu.Reactance x(unit="1") "total or syn reactance";
    input SIpu.Reactance xsig_s(unit="1") "leakage reactance stator";
    input SIpu.Resistance r_s(unit="1") "resistance stator";
    input SIpu.Reactance[n-1] xm2_n(each unit="1") "coupling reactance";
    input SIpu.Reactance[n] xsig_r(each unit="1") "leakage reactance rotor";
    input SIpu.Resistance[n] r_r(each unit="1") "resistance rotor";
    output SIpu.Resistance[n+1] zr(each unit="1") "impedance matrix resistive";
    output SIpu.Reactance[n+1,n+1] zx(each unit="1")
      "impedance matrix reactive";

  algorithm
    zr := cat(1, r_r, {r_s});
    zx := diagonal(cat(1, xsig_r, {xsig_s}));
      for k in 2:n loop
        zx[1:k,1:k] := zx[1:k,1:k] + fill(xm2_n[k-1], k, k);
      end for;
    zx := zx + fill(x - xsig_s, n+1, n+1);
  end z_fromEqCirc;

  function equiCircuit "Calculates equivalent circuit from transient data"
    extends Modelica.Icons.Function;

    input Integer n "transient order";
    input SI.Angle[n] Tc "time constant closed-loop";
    input SI.Angle[n] To "time constant open-loop";
    input SIpu.Reactance x(unit="1") "total or syn reactance";
    input SIpu.Reactance xsig_s(unit="1") "leakage reactance stator";
    input SIpu.Resistance r_s(unit="1") "resistance stator";
    input SIpu.Current if0(unit="1") "field current";
    input SI.Angle alpha_if0
      "angle field current (sign: mathematical convention)";
    input Real tol "tolerance, iterative solution";
    input Boolean field "field winding yes/no";
    output SIpu.Resistance[n] r_r(each unit="1") "resistance rotor";
    output SIpu.Reactance[n] xsig_r(each unit="1") "leakage reactance rotor";
    output SIpu.Reactance[n+1] xm(each unit="1") "coupling reactance";
  protected
    Real[n] ac;
    Real[n] ao;
    Real[n] Tsig;
    Real dif;
    Boolean result;
    import PowerSystems.Utilities.Math.fminSearch;

  algorithm
    xm := cat(1, zeros(n), {x - xsig_s});
    if n==0 then
      r_r := fill(0, n);
      xsig_r := fill(0, n);
    else
      ac := polyCoef(Tc);
      ao := polyCoef(To);
      if field and n>1 then //minimises deviation from desired i_f
  ///// possible start-values:
        if n==2 then
          xm[n] := 0.06;
      elseif n>2 then
          xm[n-1:n] := {0.02,0.04};
        end if;
  ///// may be eliminated after modification of fminSearch!
        (xm[2:n], dif) := fminSearch(xm[2:n],
        cat(1, ac, ao, Tc, To, {xsig_s}, {xm[n+1]}, {r_s}, if0*{cos(alpha_if0),sin(alpha_if0)}, {tol}));
      end if;

      (Tsig, xsig_r, result) := Tsig_xsig(n, ac, ao, xsig_s, xm, field, tol);
      assert(result, "Tsig and xsig may be false, no convergence, or complex time-constants");

      for k in 1:n loop
        r_r[k] := xsig_r[k]/Tsig[k];
      end for;
    end if;
  end equiCircuit;

  function transientData "Calculates transient data from equivalent circuit"
    extends Modelica.Icons.Function;

    input Integer n "transient order";
    input SIpu.Reactance x(unit="1") "total or syn reactance";
    input SIpu.Reactance xsig_s(unit="1") "leakage reactance stator";
    input SIpu.Resistance r_s(unit="1") "resistance stator";
    input SIpu.Reactance[n-1] xm2_n(each unit="1") "coupling reactance";
    input SIpu.Reactance[n] xsig_r(each unit="1") "leakage reactance rotor";
    input SIpu.Resistance[n] r_r(each unit="1") "resistance rotor";
    output SI.Angle[n] Tc "time constant closed-loop";
    output SI.Angle[n] To "time constant open-loop";
    output SIpu.Reactance[n] xtr(each unit="1") "transient reactance";
    import Modelica.Constants.eps;
  protected
    Real[n+1] xm;
    Real[n+1,n+1] X;
    Real[n,n] X11;
    Real[n,n] X11ac;
    Real[n] sRinv;
    Real[n,2] lam "2nd index=1:2, real and imaginary part";
    import Modelica.Math.Matrices.eigenValues;
    import PowerSystems.Utilities.Math.sortDown;

  algorithm
    xm := cat(1, {0}, xm2_n, {x - xsig_s});

    X := diagonal(cat(1, xsig_r, {xsig_s}));
    for k in 2:n+1 loop
      X[1:k,1:k] := X[1:k,1:k] + xm[k]*ones(k,k);
    end for;

    X11 := diagonal(xsig_r);
    for k in 2:n loop
      X11[1:k,1:k] := X11[1:k,1:k] + xm[k]*ones(k,k);
    end for;
    X11 := X11 + xm[n+1]*ones(n,n);

    X11ac := transpose(X[n+1:n+1, 1:n])*transpose(X[1:n, n+1:n+1])/X[n + 1, n + 1];
    sRinv := ones(n)./sqrt(r_r);

    lam := eigenValues(diagonal(sRinv)*X11*diagonal(sRinv));
    To := sortDown(lam[:,1]);
    assert(max(abs(lam[:,2])) < eps, "spectrum open-loop is not real!");
    lam := eigenValues(diagonal(sRinv)*(X11 - X11ac)*diagonal(sRinv));
    Tc := sortDown(lam[:,1]);
    assert(max(abs(lam[:,2])) < eps, "spectrum closed-loop is not real!");
    xtr := x_transient(x, Tc, To);
  end transientData;
end Precalculation;


Transform functions

within PowerSystems.Utilities;
package Transforms "Transform functions"
  extends Modelica.Icons.Package;

  constant Real[3, 3] Park0=[[2, -1, -1]/sqrt(6); [0, 1, -1]/sqrt(2); [1, 1, 1]/sqrt(3)]
    "Orthogonal transform = Park(theta=0)";
  constant Real[3, 3] J_abc=[0,-1,1; 1,0,-1; -1,1,0]/sqrt(3)
    "Rotation (pi/2) around {1,1,1} and projection on orth plane";
  //constant Real[3, 3] J_abc=skew(fill(sqrt(1/3), 3))/ "alternative";
  //J_abc = P0'*J_dq0*P0 = Park'*J_dq0*Park
  constant Real[3, 3] J_dq0=[0,-1,0; 1,0,0; 0,0,0]
    "Rotation (pi/2) around {0,0,1} and projection on orth plane";
  //J_dq0 = P0*J_abc*P0' = Park*J_abc*Park'

  function j_abc
    "Rotation(pi/2) of vector around {1,1,1} and projection on orth plane"
      extends Modelica.Icons.Function;

      input Real[3] u "vector (voltage, current)";
      output Real[3] y "rotated vector (voltage, current)";
  protected
      constant Real s13=sqrt(1/3);

  algorithm
      y := s13*{u[3]-u[2], u[1]-u[3], u[2]-u[1]};
  end j_abc;

    function jj_abc
    "Rotation(pi/2) of vector around {1,1,1} and projection on orth plane"
      extends Modelica.Icons.Function;

      input Real[3,:] u "array of 3-vectors (voltage, current)";
      output Real[3,size(u,2)] y "array of rotated vectors (voltage, current)";
  protected
      constant Real s13=sqrt(1/3);

    algorithm
      y := s13*{u[3,:]-u[2,:], u[1,:]-u[3,:], u[2,:]-u[1,:]};
    end jj_abc;

    function j_dq0
    "Rotation(pi/2) of vector around {0,0,1} and projection on orth plane"
      extends Modelica.Icons.Function;

      input Real[:] u "vector (voltage, current)";
      output Real[size(u,1)] y "rotated vector (voltage, current)";

    algorithm
      y := cat(1, {-u[2], u[1]}, zeros(size(u,1)-2));
    end j_dq0;

    function jj_dq0
    "Rotation(pi/2) of vector around {0,0,1} and projection on orth plane"
      extends Modelica.Icons.Function;

      input Real[:,:] u "array of 3- (or 2-) vectors (voltage, current)";
      output Real[size(u,1),size(u,2)] y
      "array of rotated vectors (voltage, current)";

    algorithm
      y := cat(1, {-u[2,:], u[1,:]}, zeros(size(u,1)-2, size(u,2))); // Dymola implementation now ok?
    //  y := cat(1, {-u[2,1:size(u,2)], u[1,1:size(u,2)]}, zeros(size(u,1)-2, size(u,2))); // preliminary until bug removed
    end jj_dq0;

    function park "Park transform"
      extends Modelica.Icons.Function;

      input SI.Angle theta "transformation angle";
      output Real[3,3] P "Park transformation matrix";
  protected
      constant Real s13=sqrt(1/3);
      constant Real s23=sqrt(2/3);
      constant Real dph_b=2*Modelica.Constants.pi/3;
      constant Real dph_c=4*Modelica.Constants.pi/3;
      Real[3] c;
      Real[3] s;

    algorithm
      c := cos({theta, theta - dph_b, theta - dph_c});
      s := sin({theta, theta - dph_b, theta - dph_c});
      P := transpose([s23*c, -s23*s, {s13, s13, s13}]);
    end park;

    function der_park "Derivative of Park transform"
      extends Modelica.Icons.Function;

      input SI.Angle theta "transformation angle";
      input SI.AngularFrequency omega "d/dt theta";
      output Real[3, 3] der_P "d/dt park";
  protected
      constant Real s23=sqrt(2/3);
      constant Real dph_b=2*Modelica.Constants.pi/3;
      constant Real dph_c=4*Modelica.Constants.pi/3;
      Real[3] c;
      Real[3] s;
      Real s23omega;

    algorithm
      s23omega := s23*omega;
      c := cos({theta, theta - dph_b, theta - dph_c});
      s := sin({theta, theta - dph_b, theta - dph_c});
      der_P := transpose([-s23omega*s, -s23omega*c, {0, 0, 0}]);
    end der_park;

    function der2_park "2nd derivative of Park transform"
      extends Modelica.Icons.Function;

      input SI.Angle theta "transformation angle";
      input SI.AngularFrequency omega "d/dt theta";
      input SI.AngularAcceleration omega_dot "d/dt omega";
      output Real[3, 3] der2_P "d2/dt2 park";
  protected
      constant Real s23=sqrt(2/3);
      constant Real dph_b=2*Modelica.Constants.pi/3;
      constant Real dph_c=4*Modelica.Constants.pi/3;
      Real[3] c;
      Real[3] s;
      Real s23omega_dot;
      Real s23omega2;

    algorithm
      s23omega_dot := s23*omega_dot;
      s23omega2 := s23*omega*omega;
      c := cos({theta, theta - dph_b, theta - dph_c});
      s := sin({theta, theta - dph_b, theta - dph_c});
      der2_P := transpose([-s23omega_dot*s - s23omega2*c, -s23omega_dot*c + s23omega2*s, {0, 0, 0}]);
    end der2_park;

    function rotation_dq "Rotation matrix dq"
      extends Modelica.Icons.Function;

      input SI.Angle theta "rotation angle";
      output Real[2, 2] R_dq "rotation matrix";
  protected
      Real c;
      Real s;

    algorithm
      c :=  cos(theta);
      s :=  sin(theta);
      R_dq :=  [c, -s; s, c];
    end rotation_dq;

    function der_rotation_dq "Derivative of rotation matrix dq"
      extends Modelica.Icons.Function;

      input SI.Angle theta;
      input SI.AngularFrequency omega "d/dt theta";
      output Real[2, 2] der_R_dq "d/dt rotation_dq";
  protected
      Real dc;
      Real ds;

    algorithm
      dc :=  -omega*sin(theta);
      ds :=   omega*cos(theta);
      der_R_dq :=  [dc, -ds; ds, dc];
    end der_rotation_dq;

    function der2_rotation_dq "2nd derivative of rotation matrix dq"
      extends Modelica.Icons.Function;

      input SI.Angle theta;
      input SI.AngularFrequency omega "d/dt theta";
      input SI.AngularAcceleration omega_dot "d/dt omega";
      output Real[2, 2] der2_R_dq "d/2dt2 rotation_dq";
  protected
      Real c;
      Real s;
      Real d2c;
      Real d2s;
      Real omega2=omega*omega;

    algorithm
      c := cos(theta);
      s := sin(theta);
      d2c := -omega_dot*s - omega2*c;
      d2s :=  omega_dot*c - omega2*s;
      der2_R_dq := [d2c, -d2s; d2s, d2c];
    end der2_rotation_dq;

    function rotation_abc "Rotation matrix abc"
      extends Modelica.Icons.Function;

      input SI.Angle theta "rotation angle";
      output Real[3,3] R_abc "rotation matrix";
  protected
      constant Real q13=1/3;
      constant Real s13=1/sqrt(3);
      Real c;
      Real ac;
      Real bs;
      Real[3] g;

    algorithm
      c := cos(theta);
      ac := q13*(1 - c);
      bs := s13*sin(theta);

      g := {ac + c, ac + bs, ac - bs};
      R_abc := [g[{1,2,3}], g[{3,1,2}], g[{2,3,1}]];
    end rotation_abc;

    function der_rotation_abc "Derivative of rotation matrix abc"
      extends Modelica.Icons.Function;

      input SI.Angle theta;
      input SI.AngularFrequency omega "d/dt theta";
      output Real[3, 3] der_R_abc "d/dt rotation_abc";
  protected
      constant Real q13=1/3;
      constant Real s13=1/sqrt(3);
      Real s;
      Real as;
      Real bc;
      Real[3] dg;

    algorithm
      s := sin(theta);
      as := q13*s;
      bc := s13*cos(theta);

      dg := omega*{as - s, as + bc, as - bc};
      der_R_abc := [dg[{1,2,3}], dg[{3,1,2}], dg[{2,3,1}]];
    end der_rotation_abc;

    function der2_rotation_abc "2nd derivative of rotation matrix abc"
      extends Modelica.Icons.Function;

      input SI.Angle theta;
      input SI.AngularFrequency omega "d/dt theta";
      input SI.AngularAcceleration omega_dot "d/dt omega";
      output Real[3, 3] der2_R_abc "d2/dt2 rotation_abc";
  protected
      constant Real q13=1/3;
      constant Real s13=1/sqrt(3);
      Real c;
      Real s;
      Real ac;
      Real as;
      Real bc;
      Real bs;
      Real[3] d2g;

    algorithm
      c := cos(theta);
      s := sin(theta);
      ac := q13*c;
      as := q13*s;
      bc := s13*cos(theta);
      bs := s13*sin(theta);

      d2g := omega*omega*{ac - c, ac - bs, ac + bs} + omega_dot*{as - s, as + bc, as - bc};
      der2_R_abc := [d2g[{1,2,3}], d2g[{3,1,2}], d2g[{2,3,1}]];
    end der2_rotation_abc;

    function permutation "Permutation of vector components"
      extends Modelica.Icons.Function;

      input Integer s(min=-1,max=1) "(-1, 0, 1), numbers permutation";
      input Real[3] u "vector";
      output Real[3] v "permuted vector";

    algorithm
      if s == 1 then
        v := u[{2,3,1}];
      elseif s == -1 then
        v := u[{3,1,2}];
      else
        v := u;
      end if;
    end permutation;

    function der_permutation "Derivative of permutation of vector components"
      extends Modelica.Icons.Function;

      input Integer s(min=-1,max=1) "(-1, 0, 1), numbers permutation";
      input Real[3] u "vector";
      input Real[3] der_u "d/dt u";
      output Real[3] der_v "d/dt permutation";

    algorithm
      if s == 1 then
        der_v := der_u[{2,3,1}];
      elseif s == -1 then
        der_v := der_u[{3,1,2}];
      else
        der_v := der_u;
      end if;
    end der_permutation;

    function der2_permutation
    "2nd derivative of permutation of vector components"
      extends Modelica.Icons.Function;

      input Integer s(min=-1,max=1) "(-1, 0, 1), numbers permutation";
      input Real[3] u "vector";
      input Real[3] der_u "d/dt u";
      input Real[3] der2_u "d2/dt2 u";
      output Real[3] der2_v "d2/dt2 permutation";

    algorithm
      if s == 1 then
        der2_v := der2_u[{2,3,1}];
      elseif s == -1 then
        der2_v := der2_u[{3,1,2}];
      else
        der2_v := der2_u;
      end if;
    end der2_permutation;
end Transforms;


Components

within PhotoVoltaics;
package Components "Components"
  extends Modelica.Icons.Package;
end Components;


Sampling maximum power tracker. This is a simple model of sampling maximum power tracker. The initial output of this tracker is maximum power voltage
VmpRef. After that, the sensed input power in sampled by a fixed sampling period. The output voltage is decreased by
a voltage step of VmpRef/n. If then then actual power is greater than the previous power, the sign of of voltage change
does not change. If the actual power is less than the previous power, the direction of the voltage change is swiched. This way
the sampling maximum power tracker is permanently searching for a local maximum.

within PhotoVoltaics.Components.Blocks;
block MPTrackerSample "Sampling maximum power tracker"
  extends Modelica.Blocks.Icons.Block;
  parameter Modelica.Units.SI.Time startTime=0 "Start time";
  parameter Modelica.Units.SI.Time samplePeriod=1 "Sample period";
  parameter Modelica.Units.SI.Voltage VmpRef
    "Reference maximum power power of plant";
  parameter Modelica.Units.SI.Current ImpRef
    "Reference maximum power current of plant";
  parameter Integer n = 100 "Number of voltage and power discretizations";
  final parameter Modelica.Units.SI.Voltage dv=VmpRef/n
    "Voltage change and maximum deviation";
  final parameter Modelica.Units.SI.Power dpower=VmpRef*ImpRef/n
    "Power change and maximum deviation";
  Boolean firstTrigger(start = false, fixed = true) "First boolean sample trigger signal";
  // Boolean secondTrigger "Second boolean sample trigger signal";
  Boolean sampleTrigger "Boolean sample trigger signal";
  discrete Integer counter(final start = 0, fixed = true) "Sample counter";
  discrete Real signv(final start = -1, fixed = true) "Sign of voltage change";
  Modelica.Blocks.Interfaces.RealInput power(final unit = "W") "Power"
  Modelica.Blocks.Interfaces.RealOutput vRef(final unit = "V", final start = VmpRef, fixed = true) "Reference DC voltage"
algorithm
  sampleTrigger := sample(startTime, samplePeriod);
  when sampleTrigger then
    counter := pre(counter) + 1;
    firstTrigger := time <= startTime + samplePeriod / 2;
    vRef := pre(vRef) + signv * dv;
    if not firstTrigger and power < pre(power) then
      signv := -pre(signv);
    end if;
    if vRef <= 3 * dv then
      signv := 1;
    end if;
  end when;
  // Change direction of voltage derivative
  // Do not allow negative voltages
end MPTrackerSample;


Blocks

within PhotoVoltaics.Components;
package Blocks "Blocks"
  extends Modelica.Icons.Package;
end Blocks;


Calculates to the power of 10.

within PhotoVoltaics.Components.Blocks;
model Power10 "Calculates to the power of 10"
  extends Modelica.Blocks.Interfaces.SISO;
  parameter Real k "Gain";
equation
  y = k * 10 ^ u;
end Power10;


DC controlled single phase DC/AC converter. This is an ideal DC/DC converter.

within PhotoVoltaics.Components.Converters;
model DCConverter "DC controlled single phase DC/AC converter"
  extends Modelica.Electrical.PowerConverters.Interfaces.DCDC.DCtwoPin1;
  extends Modelica.Electrical.PowerConverters.Interfaces.DCDC.DCtwoPin2;
  extends .PhotoVoltaics.Icons.Converter;

  parameter Modelica.Units.SI.Voltage VRef=48
    "Reference DC source voltage";
  parameter Modelica.Units.SI.Time Ti=1E-6
    "Internal integration time constant";
  Modelica.Blocks.Interfaces.RealInput vDCRef(final unit = "V") "DC voltage"
  Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage
  Modelica.Electrical.Analog.Sensors.CurrentSensor currentSensor
  Modelica.Blocks.Math.Product product
  Modelica.Blocks.Math.Feedback feedback
  Modelica.Electrical.Analog.Sources.SignalCurrent            variableCurrentSource
  Modelica.Electrical.Analog.Sensors.PowerSensor                      powerSensor
  Modelica.Blocks.Continuous.Integrator integrator(k=1/VRef/Ti)
  Modelica.Blocks.Math.Gain gain(final k = -1)

equation
  connect(currentSensor.n, signalVoltage.p)
  connect(signalVoltage.v, vDCRef)
  connect(currentSensor.i, product.u1)
  connect(vDCRef, product.u2)
  connect(product.y, feedback.u1)
  connect(feedback.y, integrator.u)
  connect(gain.y, feedback.u2)
  connect(gain.u, powerSensor.power)
  connect(powerSensor.nc, variableCurrentSource.n)
  connect(integrator.y, variableCurrentSource.i)
  connect(powerSensor.pv, powerSensor.pc)
  connect(currentSensor.p, dc_p1)
  connect(signalVoltage.n, dc_n1)
  connect(powerSensor.pc, dc_p2)
  connect(variableCurrentSource.p, dc_n2)
  connect(dc_n2, powerSensor.nv)
end DCConverter;


Converters

within PhotoVoltaics.Components;
package Converters "Converters"
  extends Modelica.Icons.Package;
end Converters;


Ideal polyphase DC/AC converter. This is an ideal DC/AC converter.

The polyphase converter is characterized by:

within PhotoVoltaics.Components.Converters;
model PolyphaseConverter "Ideal polyphase DC/AC converter"

  import Modelica.Constants.pi;
  extends Modelica.Electrical.PowerConverters.Interfaces.DCAC.DCtwoPin;
  extends .PhotoVoltaics.Icons.Converter;

  Modelica.Electrical.Polyphase.Interfaces.PositivePlug ac "AC output"
  Modelica.Blocks.Interfaces.RealInput vDCRef(final unit = "V")
    "DC voltage"

  parameter Modelica.Units.SI.Frequency f=50 "Frequency";
  parameter Modelica.Units.SI.Voltage VRef=400
    "Reference line to line voltage";
  parameter Modelica.Units.SI.Time Ti=1E-6
    "Internal integration time constant";
  Modelica.Units.SI.Power powerDC=vDC*iDC "Power of DC side";

  Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage
  Modelica.Electrical.Analog.Sensors.CurrentSensor currentSensor
  Modelica.Blocks.Math.Product product
  Modelica.Blocks.Math.Feedback feedback
  Modelica.Electrical.Machines.SpacePhasors.Blocks.FromPolar fromPolar
  Modelica.Blocks.Continuous.Integrator integrator(k=sqrt(3)/VRef/Ti)
  Modelica.Blocks.Math.Gain gain(final k=1)
  Modelica.Electrical.Machines.SpacePhasors.Blocks.FromSpacePhasor fromSpacePhasor(m=3)
  Modelica.Blocks.Sources.Constant zeroConst(final k=0)
  Modelica.Electrical.Polyphase.Sources.SignalCurrent signalCurrent
  Modelica.Electrical.Polyphase.Basic.Star star
  Modelica.Electrical.Analog.Basic.Ground ground
  Modelica.Blocks.Sources.Constant constOmega(final k=2*pi*f)
  Modelica.Blocks.Math.RectangularToPolar rectangularToPolar

  Modelica.Electrical.Polyphase.Sensors.VoltageSensor voltageSensorAC(
      final m=3)
  Modelica.Blocks.Math.Product productPower[3]
  Modelica.Blocks.Math.Add add
  Modelica.Blocks.Interfaces.RealInput phi "Phase angle of current with respect to voltage"

  Modelica.Blocks.Continuous.Integrator integratorOmega(final k=1)
  Modelica.Blocks.Continuous.FirstOrder firstOrder[3](k=ones(3), T=fill(Ti, 3),
    initType=fill(Modelica.Blocks.Types.Init.InitialState, 3),
    y_start=fill(0, 3))
  Modelica.Electrical.Machines.SpacePhasors.Blocks.Rotator rotatorOut
  Modelica.Blocks.Math.Gain neg(final k=-1)
  Modelica.Electrical.Machines.SpacePhasors.Blocks.ToSpacePhasor toSpacePhasor
  Modelica.Electrical.Machines.SpacePhasors.Blocks.Rotator rotatorIn
  Modelica.Blocks.Math.Sum sum3(nin=3)
equation
  connect(currentSensor.n,signalVoltage. p)
  connect(signalVoltage.v,vDCRef)
  connect(dc_p, currentSensor.p)
  connect(dc_n, signalVoltage.n)
  connect(product.y,feedback. u1)
  connect(feedback.y,integrator. u)
  connect(gain.y,feedback. u2)
  connect(product.u1, currentSensor.i)
  connect(product.u2, vDCRef)
  connect(star.plug_p, signalCurrent.plug_p)
  connect(ground.p, star.pin_n)
  connect(fromSpacePhasor.zero, zeroConst.y)
  connect(integrator.y, fromPolar.u[1])
  connect(voltageSensorAC.plug_n, star.plug_p)
  connect(voltageSensorAC.plug_p, ac)
  connect(rectangularToPolar.y_arg, add.u1)
  connect(add.y, fromPolar.u[2])
  connect(constOmega.y, integratorOmega.u)
  connect(fromSpacePhasor.y, firstOrder.u)
  connect(firstOrder.y, signalCurrent.i)
  connect(fromPolar.y, rotatorOut.u)
  connect(neg.y, rotatorOut.angle)
  connect(rotatorIn.u, toSpacePhasor.y)
  connect(rotatorIn.angle, integratorOmega.y)
  connect(neg.u, integratorOmega.y)
  connect(rotatorOut.y, fromSpacePhasor.u)
  connect(rectangularToPolar.u_re, rotatorIn.y[1])
  connect(rectangularToPolar.u_im, rotatorIn.y[2])
  connect(sum3.y, gain.u)
  connect(productPower.u2, voltageSensorAC.v)
  connect(sum3.u, productPower.y)
  connect(firstOrder.y, productPower.u1)
  connect(signalCurrent.plug_n, voltageSensorAC.plug_p)
  connect(voltageSensorAC.v, toSpacePhasor.u)
  connect(add.u2, phi)
end PolyphaseConverter;


Ideal quasi stastic polyphase DC/AC converter. This is an ideal DC/AC converter.

within PhotoVoltaics.Components.Converters;
model QuasiStaticPolyphaseConverter
  "Ideal quasi stastic polyphase DC/AC converter"
  extends Modelica.Electrical.PowerConverters.Interfaces.DCAC.DCtwoPin;
  extends .PhotoVoltaics.Interfaces.QuasiStatic.ACplug;
  extends .PhotoVoltaics.Icons.Converter;
  parameter Modelica.Units.SI.Voltage VRef=400
    "Reference line to line voltage";
  parameter Modelica.Units.SI.Time Ti=1E-6
    "Internal integration time constant";
  Modelica.Units.SI.Power powerDC=vDC*iDC "Power of DC side";
  Modelica.Units.SI.Power powerAC=Modelica.ComplexMath.real(vAC*
      Modelica.ComplexMath.conj(iAC)) "Complex apparent power of AC side";
  Modelica.Blocks.Interfaces.RealInput vDCRef(final unit = "V") "DC voltage"
  Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage
  Modelica.Electrical.Analog.Sensors.CurrentSensor currentSensor
  Modelica.Blocks.Math.Product product
  Modelica.Blocks.Math.Feedback feedback
  Sources.Electrical.VariableUnrootedPolyphaseCurrentSource
    variableCurrentSource(final m=m)
  Modelica.Electrical.QuasiStatic.Polyphase.Basic.Star star(final m=m)
  Modelica.Electrical.QuasiStatic.Polyphase.Sensors.PowerSensor
    powerSensor(final m=m)
  Modelica.Electrical.QuasiStatic.Polyphase.Sensors.PotentialSensor
    potentialSensor(final m=m)
  Modelica.Electrical.QuasiStatic.Polyphase.Blocks.ToSpacePhasor
    toSpacePhasor(final m=m)
  Modelica.ComplexBlocks.ComplexMath.ComplexToReal complexToReal
  Modelica.Electrical.QuasiStatic.Polyphase.Blocks.FromSpacePhasor
    fromSpacePhasor(final m=m)
  Modelica.Electrical.Machines.SpacePhasors.Blocks.FromPolar fromPolar
  Modelica.Electrical.Machines.SpacePhasors.Blocks.ToPolar toPolar
  Modelica.Electrical.QuasiStatic.SinglePhase.Basic.Ground ground
  Modelica.Blocks.Continuous.Integrator integrator(k=sqrt(3)/VRef/Ti)
  Modelica.Blocks.Math.Gain gain(final k = -1)
equation
  connect(signalVoltage.n, dc_n)
  connect(currentSensor.p, dc_p)
  connect(currentSensor.n, signalVoltage.p)
  connect(signalVoltage.v, vDCRef)
  connect(currentSensor.i, product.u1)
  connect(vDCRef, product.u2)
  connect(product.y, feedback.u1)
  connect(powerSensor.currentP, powerSensor.voltageP)
  connect(powerSensor.voltageN, star.plug_p)
  connect(potentialSensor.v, toSpacePhasor.u)
  connect(complexToReal.u, powerSensor.apparentPower)
  connect(fromSpacePhasor.y, variableCurrentSource.I)
  connect(fromSpacePhasor.u, fromPolar.y)
  connect(integrator.y, fromPolar.u[1])
  connect(toSpacePhasor.y, toPolar.u)
  connect(toPolar.y[2], fromPolar.u[2])
  connect(star.pin_n, ground.pin)
  connect(powerSensor.currentP, ac)
  connect(potentialSensor.plug_p, ac)
  connect(feedback.y, integrator.u)
  connect(powerSensor.currentN, variableCurrentSource.plug_n)
  connect(variableCurrentSource.plug_p, star.plug_p)
  connect(gain.u, complexToReal.re)
  connect(gain.y, feedback.u2)
end QuasiStaticPolyphaseConverter;


Ideal quasi static single phase DC/AC converter. This is an ideal DC/AC converter.

within PhotoVoltaics.Components.Converters;
model QuasiStaticSinglePhaseConverter "Ideal quasi static single phase DC/AC converter"
  extends Modelica.Electrical.PowerConverters.Interfaces.DCAC.DCtwoPin;
  extends .PhotoVoltaics.Interfaces.QuasiStatic.ACpins;
  extends .PhotoVoltaics.Icons.Converter;
  parameter Modelica.Units.SI.Voltage VRef=400/sqrt(3)
    "Reference voltage";
  parameter Modelica.Units.SI.Time Ti=1E-6
    "Internal integration time constant";
  Modelica.Units.SI.Power powerDC=vDC*iDC "Power of DC side";
  Modelica.Units.SI.Power powerAC=Modelica.ComplexMath.real(vAC*
      Modelica.ComplexMath.conj(iAC)) "Complex apparent power of AC side";
  Modelica.Blocks.Interfaces.RealInput vDCRef(final unit = "V") "DC voltage"
  Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage
  Modelica.Electrical.Analog.Sensors.CurrentSensor currentSensor
  Modelica.Blocks.Math.Product product
  Modelica.Blocks.Math.Feedback feedback
  Sources.Electrical.VariableUnrootedSinglePhaseCurrentSource variableCurrentSource
  Modelica.Electrical.QuasiStatic.SinglePhase.Sensors.PowerSensor
    powerSensor
  Modelica.Electrical.QuasiStatic.SinglePhase.Sensors.VoltageSensor
    potentialSensor
  Modelica.ComplexBlocks.ComplexMath.ComplexToReal complexToReal
  Modelica.Blocks.Continuous.Integrator integrator(k=1/VRef/Ti)
  Modelica.ComplexBlocks.ComplexMath.ComplexToPolar complexToPolar
  Modelica.ComplexBlocks.ComplexMath.PolarToComplex polarToComplex
  Modelica.Blocks.Math.Gain gain(final k = -1)
equation
  connect(signalVoltage.n, dc_n)
  connect(currentSensor.p, dc_p)
  connect(currentSensor.n, signalVoltage.p)
  connect(signalVoltage.v, vDCRef)
  connect(currentSensor.i, product.u1)
  connect(vDCRef, product.u2)
  connect(product.y, feedback.u1)
  connect(powerSensor.currentP, powerSensor.voltageP)
  connect(complexToReal.u, powerSensor.apparentPower)
  connect(feedback.y, integrator.u)
  connect(powerSensor.currentP, ac_p)
  connect(potentialSensor.pin_p, ac_p)
  connect(potentialSensor.pin_n, ac_n)
  connect(powerSensor.voltageN, ac_n)
  connect(complexToPolar.u,potentialSensor.v)
  connect(polarToComplex.y, variableCurrentSource.I)
  connect(integrator.y, polarToComplex.len)
  connect(polarToComplex.phi, complexToPolar.phi)
  connect(gain.u, complexToReal.re)
  connect(gain.y, feedback.u2)
  connect(powerSensor.currentN, variableCurrentSource.pin_n)
  connect(variableCurrentSource.pin_p, ac_n)
end QuasiStaticSinglePhaseConverter;


DC converter with sampled maximum power tracking. This is DC/DC converter
   with integrated power measurement and sampled
   maxmimum power tracking.

within PhotoVoltaics.Components.Converters.ConvertersWithSampleMPTracker;
model DCConverter "DC converter with sampled maximum power tracking"
  import PhotoVoltaics;
  extends Modelica.Electrical.PowerConverters.Interfaces.DCDC.DCtwoPin1;
  extends Modelica.Electrical.PowerConverters.Interfaces.DCDC.DCtwoPin2;
  extends PhotoVoltaics.Icons.Converter;
  extends PhotoVoltaics.Icons.MPTrackerSmall;

  parameter Modelica.Units.SI.Voltage VRef=48
    "Reference DC source voltage"
  parameter Modelica.Units.SI.Time Ti=1E-6
    "Internal integration time constant"

  parameter Modelica.Units.SI.Time startTime=0 "Start time"
  parameter Modelica.Units.SI.Time samplePeriod=1 "Sample period"
  parameter Modelica.Units.SI.Voltage VmpRef
    "Reference maximum power power of plant"
  parameter Modelica.Units.SI.Current ImpRef
    "Reference maximum power current of plant"
  parameter Integer n = 100 "Number of voltage and power discretizations"

  PhotoVoltaics.Components.Converters.DCConverter converter(final Ti=Ti, final VRef=VRef)
  Modelica.Electrical.Analog.Sensors.PowerSensor powerSensor
  PhotoVoltaics.Components.Blocks.MPTrackerSample mpTracker(
    final startTime=startTime,
    final samplePeriod=samplePeriod,
    final VmpRef=VmpRef,
    final ImpRef=ImpRef,
    final n=n)
equation
  connect(dc_p1, powerSensor.pc)
  connect(powerSensor.nc, converter.dc_p1)
  connect(converter.dc_p2, dc_p2)
  connect(dc_n1, converter.dc_n1)
  connect(converter.dc_n2, dc_n2)
  connect(powerSensor.pc, powerSensor.pv)
  connect(powerSensor.nv, dc_n1)
  connect(mpTracker.vRef, converter.vDCRef)
  connect(mpTracker.power, powerSensor.power)
end DCConverter;


within PhotoVoltaics.Components.Converters;
package ConvertersWithSampleMPTracker "Converter models with integrated sampling MP tracker"
  extends Modelica.Icons.Package;
end ConvertersWithSampleMPTracker;


Diode with one exponential function.

within PhotoVoltaics.Components.Diodes;
model Diode "Diode with one exponential function"
  extends .PhotoVoltaics.Interfaces.PartialDiode;
equation
  i = Ids * (exp(v / m / Vt) - 1) + v / R;
end Diode;


Diode with two superimposed exponential functions. The simple model of a diode is derived from
ZDiode.
It consists of the diode with exponental forward and backward breakthrough characteristic, including parallel ohmic resistance R.
The diode formula is:

within PhotoVoltaics.Components.Diodes;
model Diode2exp "Diode with two superimposed exponential functions"
  extends .PhotoVoltaics.Interfaces.PartialDiode;
  parameter Modelica.Units.SI.Voltage Bv=5.1 "Breakthrough voltage";
  parameter Modelica.Units.SI.Current Ibv=0.7 "Breakthrough knee current";
  parameter Real Nbv = 0.74 "Breakthrough emission coefficient";
equation
  i = Ids * (exp(v / m / Vt) - 1) - Ibv * exp(-(v + Bv) / (Nbv * m * Vt)) + v / R;
end Diode2exp;


Diode model with four different sections including breakthrough. This model consists of four different regions:

Forward direction: exponential function, see Diode2exp

Backwards direction: linear, before reaching backwards breakthrough region

Breaktrough: exponential function, see Diode2exp

Beyond breakthrough: linear region in order to limit magnitude of exponential breakthrough

One particular feature of this scalable model is that this diode can be used to model cells, symmetric modules and symmetric plants,
as

within PhotoVoltaics.Components.Diodes;
model Diode2Module "Diode model with four different sections including breakthrough"
  extends .PhotoVoltaics.Interfaces.PartialDiode;
  parameter Modelica.Units.SI.Voltage Bv=5.1 "Breakthrough voltage";
  parameter Modelica.Units.SI.Current Ibv=0.7 "Breakthrough knee current";
  parameter Real Nbv = 0.74 "Breakthrough emission coefficient";
  parameter Integer ns = 1 "Number of series connected cells per module";
  parameter Integer nsModule(final min = 1) = 1 "Number of series connected modules";
  parameter Integer npModule(final min = 1) = 1 "Number of parallel connected modules";
  final parameter Modelica.Units.SI.Voltage VtRef=Modelica.Constants.k*
      TRef/Q "Reference voltage equivalent of temperature";
  final parameter Modelica.Units.SI.Voltage VBv=(-m*Nbv*log(IdsRef*Nbv/
      Ibv)*VtRef) - Bv "Voltage limit of approximation of breakthrough";
  final parameter Modelica.Units.SI.Current IdsRef=IRef/(exp(VRef/m/VtRef)
       - 1) "Reference saturation current";
  final parameter Modelica.Units.SI.Voltage VNegLin=(-VRef/m/VtRef*(Nbv*m*
      VtRef)) - Bv "Limit of linear range left of breakthrough";
  Modelica.Units.SI.Voltage VNeg "Limit of linear negative voltage range";
  Modelica.Units.SI.Voltage vCell=v/ns/nsModule "Cell voltage";
  Modelica.Units.SI.Voltage vModule=v/nsModule "Module voltage";
  Modelica.Units.SI.Current iModule=i/npModule "Module current";
equation
  // Voltage limit of negative range
  VNeg = m * Vt * log(Vt / VtRef);
  // Current approximation
  i / npModule = smooth(1, if v / ns / nsModule > VNeg then Ids * (exp(v / ns / nsModule / m / Vt) - 1) + v / ns / nsModule / R elseif v / ns / nsModule > VBv then Ids * v / ns / nsModule / m / VtRef + v / ns / nsModule / R
   elseif v / ns / nsModule > VNegLin then (-Ibv * exp(-(v / ns / nsModule + Bv) / (Nbv * m * Vt))) + Ids * VBv / m / VtRef + v / ns / nsModule / R else Ids * v / ns / nsModule / m / Vt - Ibv * exp(VRef / m / VtRef) * (1 - (v / ns / nsModule + Bv) / (Nbv * m * Vt) - VRef / m / VtRef) + v / ns / nsModule / R);
end Diode2Module;


Diodes

within PhotoVoltaics.Components;
package Diodes "Diodes"
  extends Modelica.Icons.Package;
end Diodes;


Photovoltaic cells, modules and plants

within PhotoVoltaics.Components;
package SimplePhotoVoltaics "Photovoltaic cells, modules and plants"
  extends Modelica.Icons.Package;
end SimplePhotoVoltaics;


Simple cell model. The basic simple cell model consists of a scalable
diode model and a
signal current source with temperature
dependency in order to consider the temperature coefficient of the short circuit of PV cell.

within PhotoVoltaics.Components.SimplePhotoVoltaics;
model SimpleCell "Simple cell model"
  extends .PhotoVoltaics.Interfaces.PartialCell(signalCurrent(
      final irradianceRef=moduleData.irradianceRef,
      final alphaRef=moduleData.alphaIsc,
      final IRef=IphRef), diode(
      final Bv=moduleData.BvCell,
      final Ibv=moduleData.Ibv,
      final Nbv=moduleData.Nbv,
      final VRef=moduleData.VocCellRef,
      final IRef=moduleData.IscRef,
      final alphaI=moduleData.alphaIsc,
      final alphaV=moduleData.alphaVoc,
      final R=1E8,
      final m=m,
      final ns=1,
      final nsModule=1,
      final npModule=1));
  final parameter Real m(start = 2, fixed = false) "Ideality factor of diode";
  final parameter Modelica.Units.SI.Current IsdRef(start=1E-4, fixed=false)
    "Reference saturation current of cell";
  final parameter Modelica.Units.SI.Current IphRef=moduleData.IscRef
    "Reference photo current of cell";
initial equation
  IphRef = IsdRef * (exp(moduleData.VocCellRef / m / moduleData.VtCellRef) - 1);
  IphRef = IsdRef * (exp(moduleData.VmpCellRef / m / moduleData.VtCellRef) - 1) + moduleData.ImpRef;
  (2)  IphRef = IsdRef * (exp(moduleData.VmpCellRef / m / moduleData.VtCellRef) - 1) + moduleData.ImpRef;

These two equations are evaluated in the initial equation section of this model.

The solar irradiance of the model can eithe be constant or provided by signal input. The current of current source
is determined linearely dependent of irradiance.

"));
end SimpleCell;


Simple module consisting of series connected cells.

within PhotoVoltaics.Components.SimplePhotoVoltaics;
model SimpleModule "Simple module consisting of series connected cells"
  extends .PhotoVoltaics.Interfaces.PartialComponent;
  parameter Real shadow[moduleData.ns] = zeros(moduleData.ns) "Shadow vector based on: 0 = full sun, 1 = full shadow";
  SimpleCell cell[moduleData.ns](
    final useHeatPort=fill(useHeatPort, moduleData.ns),
    final T=fill(T, moduleData.ns),
    final constantIrradiance=fill(constantIrradiance, moduleData.ns),
    final moduleData=fill(moduleData, moduleData.ns),
    final useConstantIrradiance=fill(false, moduleData.ns),
    final shadow=shadow)
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector collectorModule(final m = moduleData.ns) if useHeatPort
  Diodes.Diode diode[moduleData.nb](final useHeatPort = fill(useHeatPort, moduleData.nb), final T = fill(T, moduleData.nb), final m = fill(1, moduleData.nb), final R = fill(1E8, moduleData.nb), final TRef = fill(moduleData.TRef, moduleData.nb), final IRef = fill(moduleData.IscRef, moduleData.nb), final alphaI = fill(0, moduleData.nb), final alphaV = fill(0, moduleData.nb), final VRef = fill(0.5, moduleData.nb))
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector collectorByPass(final m = moduleData.nb) if useHeatPort
  Modelica.Blocks.Routing.Replicator replicator(final nout=moduleData.ns)

equation
  assert(mod(moduleData.ns, moduleData.nb) == 0, "Simple: number of bypassed cells cannot be determined unambiguously");
  // Connect cells to module
  connect(p, cell[1].p)
  for k in 1:moduleData.ns - 1 loop
    connect(cell[k].n, cell[k + 1].p);
  end for;
  // Inter-module connections
  connect(cell[moduleData.ns].n, n)
  connect(cell.heatPort, collectorModule.port_a)
  connect(collectorModule.port_b, heatPort)
  // Connect bypass diodes
  for k in 1:moduleData.nb loop
    connect(diode[k].n, cell[(k - 1) * div(moduleData.ns, moduleData.nb) + 1].p)
    connect(diode[k].p, cell[k * div(moduleData.ns, moduleData.nb)].n)
  end for;
  connect(collectorByPass.port_a, diode.heatPort)
  connect(collectorByPass.port_b, heatPort)
  connect(irradiance, replicator.u)
  connect(replicator.y, cell.variableIrradiance)
end SimpleModule;


Simple module consisting of symmetric series connected cells.

within PhotoVoltaics.Components.SimplePhotoVoltaics;
model SimpleModuleSymmetric "Simple module consisting of symmetric series connected cells"
  extends .PhotoVoltaics.Interfaces.PartialCell(diode(
      final m=m,
      final R=1E8,
      final Bv=moduleData.BvCell,
      final Ibv=moduleData.Ibv,
      final Nbv=moduleData.Nbv,
      final IRef=moduleData.IscRef,
      final alphaI=moduleData.alphaIsc,
      final alphaV=moduleData.alphaVoc,
      final ns=moduleData.ns,
      final VRef=moduleData.VocCellRef,
      final nsModule=1,
      final npModule=1), signalCurrent(
      final IRef=IphRef,
      final irradianceRef=moduleData.irradianceRef,
      final alphaRef=moduleData.alphaIsc));

  final parameter Real m(start = 2, fixed = false) "Ideality factor of diode";
  final parameter Modelica.Units.SI.Current IsdRef(start=1E-4, fixed=false)
    "Reference saturation current of cell";
  final parameter Modelica.Units.SI.Current IphRef=moduleData.IscRef
    "Reference photo current of cell";
  Modelica.Units.SI.Voltage vCell=v/moduleData.ns "Cell voltage";
  Modelica.Units.SI.Current iCell=i "Cell current";
  Modelica.Units.SI.Current iCellGenerating=-iCell
    "Negative cell current (generating)";
  Modelica.Units.SI.Power powerCell=vCell*iCell "Cell power";
  Modelica.Units.SI.Power powerCellGenerating=vCell*iCellGenerating
    "Negative power consumption (generating)";
initial equation
  IphRef = IsdRef * (exp(moduleData.VocCellRef / m / moduleData.VtCellRef) - 1);
  IphRef = IsdRef * (exp(moduleData.VmpCellRef / m / moduleData.VtCellRef) - 1) + moduleData.ImpRef;
end SimpleModuleSymmetric;


Simple plant consisting of symmetric series and parallel connected modules.

within PhotoVoltaics.Components.SimplePhotoVoltaics;
model SimplePlantSymmetric "Simple plant consisting of symmetric series and parallel connected modules"
  extends .PhotoVoltaics.Interfaces.PartialCell(diode(
      final m=m,
      final R=1E8,
      final Bv=moduleData.BvCell,
      final Ibv=moduleData.Ibv,
      final Nbv=moduleData.Nbv,
      final IRef=moduleData.IscRef,
      final alphaI=moduleData.alphaIsc,
      final alphaV=moduleData.alphaVoc,
      final ns=moduleData.ns,
      final VRef=moduleData.VocCellRef,
      final nsModule=nsModule,
      final npModule=npModule), signalCurrent(
      final irradianceRef=moduleData.irradianceRef,
      final alphaRef=moduleData.alphaIsc,
      final IRef=npModule*IphRef));
  parameter Integer nsModule(final min = 1) = 1 "Number of series connected modules";
  parameter Integer npModule(final min = 1) = 1 "Number of parallel connected modules";
  final parameter Real m(start = 2, fixed = false) "Ideality factor of diode";
  final parameter Modelica.Units.SI.Current IsdRef(start=1E-4, fixed=false)
    "Reference saturation current of cell";
  final parameter Modelica.Units.SI.Current IphRef=moduleData.IscRef
    "Reference photo current of cell";
  Modelica.Units.SI.Voltage vCell=v/moduleData.ns/nsModule "Cell voltage";
  Modelica.Units.SI.Current iCell=i/npModule "Cell current";
  Modelica.Units.SI.Current iCellGenerating=-iCell
    "Negative cell current (generating)";
  Modelica.Units.SI.Power powerCell=vCell*iCell "Cell power";
  Modelica.Units.SI.Power powerCellGenerating=vCell*iCellGenerating
    "Negative power consumption (generating)";
  Modelica.Units.SI.Voltage vModule=v/nsModule "Module voltage";
  Modelica.Units.SI.Current iModule=i/npModule "Modile current";
  Modelica.Units.SI.Current iModuleGenerating=-iModule
    "Negative module current (generating)";
  Modelica.Units.SI.Power powerModule=vModule*iModule "Module power";
  Modelica.Units.SI.Power powerModuleGenerating=vModule*iModuleGenerating
    "Negative module power (generating)";
initial equation
  IphRef = IsdRef * (exp(moduleData.VocCellRef / m / moduleData.VtCellRef) - 1);
  IphRef = IsdRef * (exp(moduleData.VmpCellRef / m / moduleData.VtCellRef) - 1) + moduleData.ImpRef;
end SimplePlantSymmetric;


Solar pyramid with DC/DC converters. Model of a symmetric solar pyramid as used for a
Mars rover
prototype. This model consists of four symmetrical photovoltaic panels.
The center position of can be moved vertically, such that the four panels are moved altogether.
The angle of inclination of each panel, gamma, is an input to this model.
The second input is the azimuth of one of the fore panels. The remaining three panels are
displaced by 90 degrees, each.

within PhotoVoltaics.Components.SimplePhotoVoltaics;
model SolarPyramidDCConverter "Solar pyramid with DC/DC converters"
  import Modelica.Constants.pi;
  extends
    Modelica.Thermal.HeatTransfer.Interfaces.PartialConditionalHeatPort(      T = 298.15);
  extends Modelica.Electrical.PowerConverters.Interfaces.ACDC.DCtwoPin;
  parameter Records.ModuleData moduleData "Module parameters"
  parameter Modelica.Units.SI.Angle sunAzimuth=0 "Azimuth of sun"
  parameter Modelica.Units.SI.Angle sunHeight=1.0471975511965976
    "Height of sun"
  parameter Modelica.Units.SI.Irradiance irradiance=1000 "Irradiance"

  parameter Modelica.Units.SI.Voltage VRef=48
    "Reference DC source voltage"
  parameter Modelica.Units.SI.Time Ti=1E-6
    "Internal integration time constant"

  parameter Modelica.Units.SI.Time startTime=0 "Start time"
  parameter Modelica.Units.SI.Time samplePeriod=1 "Sample period"
  parameter Modelica.Units.SI.Voltage VmpRef
    "Reference maximum power power of plant"
  parameter Modelica.Units.SI.Current ImpRef
    "Reference maximum power current of plant"
  parameter Integer n = 100 "Number of voltage and power discretizations"

  parameter Real shadow1(final min=0, final max=1)=0 "Shadow of module 1";
  parameter Real shadow2(final min=0, final max=1)=0 "Shadow of module 2";
  parameter Real shadow3(final min=0, final max=1)=0 "Shadow of module 3";
  parameter Real shadow4(final min=0, final max=1)=0 "Shadow of module 4";

  Modelica.Blocks.Sources.Constant const(k=Modelica.Constants.pi/2)
  Modelica.Blocks.Math.Add add1
  Modelica.Blocks.Math.Add add2
  Modelica.Blocks.Math.Add add3
  SimpleModuleSymmetric module1(
    final useConstantIrradiance=false,
    final moduleData=moduleData,
    final useHeatPort=useHeatPort,
    final T=T,
    final shadow=shadow1)
  SimpleModuleSymmetric module2(
    final useConstantIrradiance=false,
    final moduleData=moduleData,
    final useHeatPort=useHeatPort,
    final T=T,
    final shadow=shadow2)
  SimpleModuleSymmetric module3(
    final useConstantIrradiance=false,
    final moduleData=moduleData,
    final useHeatPort=useHeatPort,
    final T=T,
    final shadow=shadow3)
  SimpleModuleSymmetric module4(
    final useConstantIrradiance=false,
    final moduleData=moduleData,
    final useHeatPort=useHeatPort,
    final T=T,
    final shadow=shadow4)
  Sources.Irradiance.IrradianceFixedSun irradianceParameter1(
    final irradianceRef=irradiance,
    final sunHeight = sunHeight,
    final sunAzimuth = sunAzimuth)
  Sources.Irradiance.IrradianceFixedSun irradianceParameter2(
    final irradianceRef=irradiance,
    final sunHeight = sunHeight,
    final sunAzimuth = sunAzimuth)
  Sources.Irradiance.IrradianceFixedSun irradianceParameter3(
    final irradianceRef=irradiance,
    final sunHeight = sunHeight,
    final sunAzimuth = sunAzimuth)
  Sources.Irradiance.IrradianceFixedSun irradianceParameter4(
    final irradianceRef=irradiance,
    final sunHeight = sunHeight,
    final sunAzimuth = sunAzimuth)
  Modelica.Electrical.Analog.Interfaces.NegativePin nModule "Common negative pin of all four PV modules (needs ground)"
  Modelica.Blocks.Interfaces.RealInput azimuth "Azimuth of first solar panel"
  Modelica.Blocks.Interfaces.RealInput gamma "Angle of inclination of each solar panel"
  // Voltages
  Modelica.Units.SI.Voltage v1=module1.v "Voltage of module 1";
  Modelica.Units.SI.Voltage v2=module2.v "Voltage of module 2";
  Modelica.Units.SI.Voltage v3=module3.v "Voltage of module 3";
  Modelica.Units.SI.Voltage v4=module4.v "Voltage of module 4";
  // Currents
  Modelica.Units.SI.Current i1(start=0) = module1.i "Current of module 1";
  Modelica.Units.SI.Current i2(start=0) = module2.i "Current of module 2";
  Modelica.Units.SI.Current i3(start=0) = module3.i "Current of module 3";
  Modelica.Units.SI.Current i4(start=0) = module4.i "Current of module 4";
  Modelica.Units.SI.Current iGenerating1=module1.iGenerating
    "Negativ current of module 1 (generating)";
  Modelica.Units.SI.Current iGenerating2=module2.iGenerating
    "Negativ current of module 2 (generating)";
  Modelica.Units.SI.Current iGenerating3=module3.iGenerating
    "Negativ current of module 3 (generating)";
  Modelica.Units.SI.Current iGenerating4=module4.iGenerating
    "Negativ current of module 4 (generating)";
  // Power
  Modelica.Units.SI.Power power1=module1.power "Power of module 1";
  Modelica.Units.SI.Power power2=module2.power "Power of module 2";
  Modelica.Units.SI.Power power3=module3.power "Power of module 3";
  Modelica.Units.SI.Power power4=module4.power "Power of module 4";
  Modelica.Units.SI.Power power=power1 + power2 + power3 + power4
    "Total power of all four modules";

  Modelica.Units.SI.Power powerGenerating1=module1.powerGenerating
    "Negative power consumption (generating) of module 1";
  Modelica.Units.SI.Power powerGenerating2=module2.powerGenerating
    "Negative power consumption (generating) of module 2";
  Modelica.Units.SI.Power powerGenerating3=module3.powerGenerating
    "Negative power consumption (generating) of module 3";
  Modelica.Units.SI.Power powerGenerating4=module4.powerGenerating
    "Negative power consumption (generating) of module 4";
  Modelica.Units.SI.Power powerGenerating=powerGenerating1 +
      powerGenerating2 + powerGenerating3 + powerGenerating4
    "Total negative power consumption (generating) of all four modules";

  Converters.ConvertersWithSampleMPTracker.DCConverter dcConverter1(
    final VRef=VRef,
    final Ti=Ti,
    final startTime=startTime,
    final samplePeriod=samplePeriod,
    final VmpRef=VmpRef,
    final ImpRef=ImpRef,
    final n=n)
  Converters.ConvertersWithSampleMPTracker.DCConverter dcConverter2(
    final VRef=VRef,
    final Ti=Ti,
    final startTime=startTime,
    final samplePeriod=samplePeriod,
    final VmpRef=VmpRef,
    final ImpRef=ImpRef,
    final n=n)
  Converters.ConvertersWithSampleMPTracker.DCConverter dcConverter3(
    final VRef=VRef,
    final Ti=Ti,
    final startTime=startTime,
    final samplePeriod=samplePeriod,
    final VmpRef=VmpRef,
    final ImpRef=ImpRef,
    final n=n)
  Converters.ConvertersWithSampleMPTracker.DCConverter dcConverter4(
    final VRef=VRef,
    final Ti=Ti,
    final startTime=startTime,
    final samplePeriod=samplePeriod,
    final VmpRef=VmpRef,
    final ImpRef=ImpRef,
    final n=n)
equation
  connect(const.y,add1. u2)
  connect(const.y,add2. u2)
  connect(const.y,add3. u2)
  connect(irradianceParameter3.gamma,irradianceParameter4. gamma)
  connect(irradianceParameter2.gamma,irradianceParameter3. gamma)
  connect(irradianceParameter1.gamma,irradianceParameter2. gamma)
  connect(add3.y,irradianceParameter4. azimuth)
  connect(add2.y,irradianceParameter3. azimuth)
  connect(add1.y,irradianceParameter2. azimuth)
  connect(irradianceParameter4.irradiance,module4. variableIrradiance)
  connect(irradianceParameter3.irradiance,module3. variableIrradiance)
  connect(irradianceParameter2.irradiance,module2. variableIrradiance)
  connect(irradianceParameter1.irradiance, module1.variableIrradiance)
  connect(add3.u1,add2. y)
  connect(add2.u1,add1. y)
  connect(module4.n, nModule)
  connect(irradianceParameter1.azimuth, azimuth)
  connect(azimuth, add1.u1)
  connect(irradianceParameter1.gamma, gamma)
  connect(module3.n, nModule)
  connect(module2.n, nModule)
  connect(module1.n, nModule)
  connect(module3.heatPort, module4.heatPort)
  connect(module2.heatPort, module4.heatPort)
  connect(module1.heatPort, module4.heatPort)
  connect(module1.n, dcConverter1.dc_n1)
  connect(module1.p, dcConverter1.dc_p1)
  connect(internalHeatPort, module4.heatPort)
  connect(module2.p, dcConverter2.dc_p1)
  connect(module2.n, dcConverter2.dc_n1)
  connect(module3.p, dcConverter3.dc_p1)
  connect(module3.n, dcConverter3.dc_n1)
  connect(module4.p, dcConverter4.dc_p1)
  connect(module4.n, dcConverter4.dc_n1)
  connect(dcConverter1.dc_p2, dc_p)
  connect(dcConverter1.dc_n2, dc_n)
  connect(dcConverter2.dc_p2, dc_p)
  connect(dcConverter3.dc_p2, dc_p)
  connect(dcConverter4.dc_p2, dc_p)
  connect(dcConverter2.dc_n2, dc_n)
  connect(dcConverter3.dc_n2, dc_n)
  connect(dcConverter4.dc_n2, dc_n)
end SolarPyramidDCConverter;


Compare different diode models.

within PhotoVoltaics.ComponentTesting;
model DiodeCompare "Compare different diode models"
  extends Modelica.Icons.Example;
  parameter Integer ns = 10 "Number of series connected cells";
  parameter Integer nsModule = 2 "Number of series connected modules";
  parameter Integer npModule = 2 "Number of parallel connected modules";
  parameter Modelica.Units.SI.Voltage Vmin=-5.30 "Minimum voltage range";
  parameter Modelica.Units.SI.Voltage Vmax=+0.75 "Maximum voltage range";
  Modelica.Electrical.Analog.Semiconductors.ZDiode zDiode(useHeatPort = true, R = 1E8, Maxexp = 0.6292 / 0.04, Ids = 1.26092E-6, T = 298.15)
  Modelica.Electrical.Analog.Sources.RampVoltage rampVoltage(duration = 1, startTime = 0, V = Vmax - Vmin, offset = Vmin)
  PhotoVoltaics.Components.Diodes.Diode2exp diode(m = 40 / 25.69, useHeatPort = true)
  parameter PhotoVoltaics.Records.ModuleData moduleData
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature fixedTemperature(T = 318.15)
  Modelica.Electrical.Analog.Sources.RampVoltage rampVoltagem(duration = 1, startTime = 0, V = nsModule * ns * (Vmax - Vmin), offset = nsModule * ns * Vmin)
  Modelica.Electrical.Analog.Basic.Ground groundm
  PhotoVoltaics.Components.Diodes.Diode2Module diodexm(m = 40 / 25.69, useHeatPort = true, nsModule = nsModule, npModule = npModule, ns = ns)
  Modelica.Electrical.Analog.Basic.Ground ground
equation
  connect(rampVoltage.p, zDiode.p)
  connect(zDiode.n, rampVoltage.n)
  connect(rampVoltage.p, diode.p)
  connect(rampVoltage.n, diode.n)
  connect(fixedTemperature.port, diode.heatPort)
  connect(fixedTemperature.port, zDiode.heatPort)
  connect(rampVoltagem.n, groundm.p)
  connect(diodexm.p, rampVoltagem.p)
  connect(diodexm.heatPort, fixedTemperature.port)
  connect(diodexm.n, groundm.p)
  connect(ground.p, rampVoltage.n)
end DiodeCompare;


Testing of single components

within PhotoVoltaics;
package ComponentTesting "Testing of single components"
  extends Modelica.Icons.ExamplesPackage;
end ComponentTesting;


Test of voltage current controlled converter.

within PhotoVoltaics.ComponentTesting;
model PolyphaseVoltageControlledConverter
  "Test of voltage current controlled converter"
  extends Modelica.Icons.Example;
  PhotoVoltaics.Components.Converters.QuasiStaticPolyphaseConverter
    converter
  Modelica.Electrical.Analog.Sources.ConstantCurrent constantCurrent(I = 1)
  Modelica.Electrical.Analog.Basic.Ground groundDC
  Modelica.Electrical.QuasiStatic.Polyphase.Sources.VoltageSource
    voltageSource(f=50, V=fill(400/sqrt(3), 3))
  Modelica.Electrical.QuasiStatic.SinglePhase.Basic.Ground groundAC
  Modelica.Blocks.Sources.Ramp ramp(duration = 0.6, startTime = 0.2, height = 200, offset = -100)
  Modelica.Electrical.QuasiStatic.Polyphase.Basic.Star star
equation
  connect(constantCurrent.n, converter.dc_p)
  connect(constantCurrent.p, converter.dc_n)
  connect(groundDC.p, constantCurrent.p)
  connect(ramp.y, converter.vDCRef)
  connect(star.pin_n, groundAC.pin)
  connect(star.plug_p, voltageSource.plug_n)
  connect(converter.ac, voltageSource.plug_p)
end PolyphaseVoltageControlledConverter;


within PhotoVoltaics.ComponentTesting;
model SinglePhaseVoltageControlledConverter "Test of voltage current controlled converter"
  extends Modelica.Icons.Example;
  PhotoVoltaics.Components.Converters.QuasiStaticSinglePhaseConverter converter
  Modelica.Electrical.Analog.Sources.ConstantCurrent constantCurrent(I = 1)
  Modelica.Electrical.Analog.Basic.Ground groundDC
  Modelica.Electrical.QuasiStatic.SinglePhase.Sources.VoltageSource
    voltageSource(
    f=50,
    V=100,
    phi=0)
  Modelica.Electrical.QuasiStatic.SinglePhase.Basic.Ground groundAC
  Modelica.Blocks.Sources.Ramp ramp(duration = 0.6, startTime = 0.2, height = 200, offset = -100)
equation
  connect(constantCurrent.n, converter.dc_p)
  connect(constantCurrent.p, converter.dc_n)
  connect(groundDC.p, constantCurrent.p)
  connect(converter.ac_p, voltageSource.pin_p)
  connect(converter.ac_n, voltageSource.pin_n)
  connect(groundAC.pin, voltageSource.pin_n)
  connect(ramp.y, converter.vDCRef)
end SinglePhaseVoltageControlledConverter;


Testing irradiance model.

within PhotoVoltaics.ComponentTesting;
model TestIrradiance "Testing irradiance model"
  extends Modelica.Icons.Example;
  PhotoVoltaics.Sources.Irradiance.Irradiance irradiance(startMonth = 12, startDay = 30)
  Modelica.Blocks.Continuous.Integrator integrator(y(unit = "J/m2"))
equation
  connect(irradiance.irradiance, integrator.u)
end TestIrradiance;


Examples

within PhotoVoltaics;
package Examples "Examples"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


Simple cell with load resistor.

within PhotoVoltaics.Examples;
model SimpleCell "Simple cell with load resistor"
  extends Modelica.Icons.Example;
  Modelica.Electrical.Analog.Basic.Ground ground
  Modelica.Electrical.Analog.Basic.VariableResistor variableResistor
  PhotoVoltaics.Sources.Blocks.PowerRamp powerRamp(duration = 0.6, height = 8, offset = -4, ref = moduleData.VmpCellRef / moduleData.ImpRef, startTime = 0.2)
  PhotoVoltaics.Components.SimplePhotoVoltaics.SimpleCell cell(moduleData=moduleData)
  parameter PhotoVoltaics.Records.SHARP_NU_S5_E3E moduleData
equation
  connect(cell.p, variableResistor.p)
  connect(ground.p, cell.n)
  connect(ground.p, variableResistor.n)
  connect(variableResistor.R, powerRamp.y)
end SimpleCell;


Voltage current characteristic for different irradiances.

within PhotoVoltaics.Examples;
model SimpleCellIrradianceCharacteristic "Voltage current characteristic for different irradiances"
  extends Modelica.Icons.Example;
  Modelica.Electrical.Analog.Basic.Ground ground
  Modelica.Electrical.Analog.Basic.VariableResistor variableResistor
  PhotoVoltaics.Components.SimplePhotoVoltaics.SimpleCell cell(useConstantIrradiance=false, moduleData=moduleData)
  PhotoVoltaics.Sources.Blocks.TriangleAndStep triangleAndStep(T = 1, stepHeight = 1000, triangleHeight = 8, triangleOffset = -4)
  PhotoVoltaics.Components.Blocks.Power10 power10(k = moduleData.VocCellRef / moduleData.IscRef)
  parameter PhotoVoltaics.Records.SHARP_NU_S5_E3E moduleData
equation
  connect(variableResistor.n, ground.p)
  connect(cell.p, variableResistor.p)
  connect(ground.p, cell.n)
  connect(triangleAndStep.step, cell.variableIrradiance)
  connect(triangleAndStep.triangle, power10.u)
  connect(variableResistor.R, power10.y)
end SimpleCellIrradianceCharacteristic;


Voltage current characteristic for different temperatures.

within PhotoVoltaics.Examples;
model SimpleCellTemperatureCharacteristic "Voltage current characteristic for different temperatures"
  extends Modelica.Icons.Example;
  Modelica.Electrical.Analog.Basic.Ground ground
  Modelica.Electrical.Analog.Basic.VariableResistor variableResistor
  PhotoVoltaics.Components.SimplePhotoVoltaics.SimpleCell cell(
    moduleData=moduleData,
    useHeatPort=true,
    useConstantIrradiance=true)
  PhotoVoltaics.Sources.Blocks.TriangleAndStep triangleAndStep(T = 1, triangleHeight = 8, triangleOffset = -4, stepNumber = 5, stepOffset = 293.15 - 40, stepHeight = 60)
  PhotoVoltaics.Components.Blocks.Power10 power10(k = moduleData.VocCellRef / moduleData.IscRef)
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature prescribedTemperature
  parameter PhotoVoltaics.Records.SHARP_NU_S5_E3E moduleData
equation
  connect(variableResistor.n, ground.p)
  connect(cell.p, variableResistor.p)
  connect(ground.p, cell.n)
  connect(triangleAndStep.triangle, power10.u)
  connect(variableResistor.R, power10.y)
  connect(prescribedTemperature.port, cell.heatPort)
  connect(triangleAndStep.step, prescribedTemperature.T)
end SimpleCellTemperatureCharacteristic;


Simple cell with DC voltage.

within PhotoVoltaics.Examples;
model SimpleCellVoltageSource "Simple cell with DC voltage"
  extends Modelica.Icons.Example;
  Modelica.Electrical.Analog.Basic.Ground ground
  Modelica.Blocks.Sources.Ramp rampCurrent(duration = 0.6, height = moduleData.VocCellRef + moduleData.BvCell, offset = -moduleData.BvCell, startTime = 0.2)
  PhotoVoltaics.Components.SimplePhotoVoltaics.SimpleCell cell(moduleData=moduleData)
  Modelica.Electrical.Analog.Sources.SignalVoltage signalVoltage
  parameter PhotoVoltaics.Records.SHARP_NU_S5_E3E moduleData
equation
  connect(signalVoltage.n, ground.p)
  connect(signalVoltage.p, cell.p)
  connect(ground.p, cell.n)
  connect(signalVoltage.v, rampCurrent.y)
end SimpleCellVoltageSource;


Simple module with load resistor.

within PhotoVoltaics.Examples;
model SimpleModule "Simple module with load resistor"
  extends Modelica.Icons.Example;
  Modelica.Electrical.Analog.Basic.Ground ground
  PhotoVoltaics.Components.SimplePhotoVoltaics.SimpleModule module(
    T=298.15,
    moduleData=moduleData,
    cell(v(start=zeros(moduleData.ns))))
  PhotoVoltaics.Sources.Blocks.PowerRamp powerRamp(duration = 0.6, height = 8, offset = -4, ref = moduleData.VmpCellRef / moduleData.ImpRef, startTime = 0.2)
  Modelica.Electrical.Analog.Basic.VariableResistor variableResistor
  parameter PhotoVoltaics.Records.SHARP_NU_S5_E3E moduleData
equation
  connect(module.p, variableResistor.p)
  connect(ground.p, module.n)
  connect(variableResistor.n, ground.p)
  connect(variableResistor.R, powerRamp.y)
end SimpleModule;


Simple module supplies quasi-static single-phase AC grid with maximum power tracker.

within PhotoVoltaics.Examples;
model SimpleModuleMP "Simple module supplies quasi-static single-phase AC grid with maximum power tracker"
  extends Modelica.Icons.Example;
  Modelica.Electrical.Analog.Basic.Ground groundDC
  PhotoVoltaics.Components.SimplePhotoVoltaics.SimpleModuleSymmetric module(
    moduleData=moduleData,
    T=298.15,
    useConstantIrradiance=false)
  Modelica.Blocks.Sources.Ramp ramp(duration = 100, startTime = 100, height = 800, offset = 200)
  PhotoVoltaics.Components.Converters.QuasiStaticSinglePhaseConverter converter
  PhotoVoltaics.Components.Blocks.MPTrackerSample mpTracker(VmpRef = moduleData.VmpRef, ImpRef = moduleData.ImpRef)
  Modelica.Electrical.QuasiStatic.SinglePhase.Basic.Ground groundAC
  Modelica.Electrical.QuasiStatic.SinglePhase.Sources.VoltageSource
    voltageSource(
    f=50,
    V=230,
    phi=0,
    gamma(fixed=true, start=0))
  Modelica.Electrical.Analog.Sensors.PowerSensor powerSensor
  parameter PhotoVoltaics.Records.SHARP_NU_S5_E3E moduleData
equation
  connect(groundDC.p, module.n)
  connect(ramp.y, module.variableIrradiance)
  connect(groundDC.p, converter.dc_n)
  connect(mpTracker.vRef, converter.vDCRef)
  connect(converter.ac_n, groundAC.pin)
  connect(groundAC.pin, voltageSource.pin_n)
  connect(module.p, powerSensor.pc)
  connect(powerSensor.nc, converter.dc_p)
  connect(mpTracker.power, powerSensor.power)
  connect(powerSensor.pc, powerSensor.pv)
  connect(powerSensor.nv, groundDC.p)
  connect(converter.ac_p, voltageSource.pin_p)
end SimpleModuleMP;


Simple module supplies quasi-static three-phase AC grid with maximum power tracker.

within PhotoVoltaics.Examples;
model SimpleModuleMP3 "Simple module supplies quasi-static three-phase AC grid with maximum power tracker"
  extends Modelica.Icons.Example;
  Modelica.Electrical.Analog.Basic.Ground groundDC
  PhotoVoltaics.Components.SimplePhotoVoltaics.SimpleModuleSymmetric module(
    moduleData=moduleData,
    T=298.15,
    useConstantIrradiance=false)
  Modelica.Blocks.Sources.Ramp ramp(duration = 100, startTime = 100, height = 800, offset = 200)
  PhotoVoltaics.Components.Converters.QuasiStaticPolyphaseConverter
    converter
  PhotoVoltaics.Components.Blocks.MPTrackerSample mpTracker(VmpRef = moduleData.VmpRef, ImpRef = moduleData.ImpRef)
  Modelica.Electrical.QuasiStatic.SinglePhase.Basic.Ground groundAC
  Modelica.Electrical.QuasiStatic.Polyphase.Sources.VoltageSource voltageSource(
    f=50,
    V=fill(400/sqrt(3), 3),
    gamma(fixed=true, start=0))
  Modelica.Electrical.Analog.Sensors.PowerSensor powerSensor
  Modelica.Electrical.QuasiStatic.Polyphase.Basic.Star star
  parameter PhotoVoltaics.Records.SHARP_NU_S5_E3E moduleData
equation
  connect(groundDC.p, module.n)
  connect(ramp.y, module.variableIrradiance)
  connect(groundDC.p, converter.dc_n)
  connect(mpTracker.vRef, converter.vDCRef)
  connect(module.p, powerSensor.pc)
  connect(powerSensor.nc, converter.dc_p)
  connect(mpTracker.power, powerSensor.power)
  connect(powerSensor.pc, powerSensor.pv)
  connect(powerSensor.nv, groundDC.p)
  connect(star.plug_p, voltageSource.plug_n)
  connect(star.pin_n, groundAC.pin)
  connect(converter.ac, voltageSource.plug_p)
end SimpleModuleMP3;