## Modelica_LinearSystems2

Modelica_LinearSystems2 - Analysis, Synthesis and Modeling of Continuous and Discrete Linear Systems

### Information

Library Modelica_LinearSystems2 is a Modelica package providing different representations of linear, time invariant differential and difference equation systems. For example, record StateSpace defines a linear time invariant differential equation system in state space form:

        der(x) = A * x + B * u
            y  = C * x + D * u

Operators are overloaded to work conveniently with these system descriptions in an interactive environment, e.g., to multiply transfer functions or to operate on complex numbers. About 180 functions are provided to operate on these data structures, e.g., to compute eigen values, zeros, step responses, to design pole-placement and LQG controllers, to plot step responses, frequency responses, eigen values, to convert between different description forms, or to generate a linear system description by linearization of a Modelica model.

Furthermore, in sublibrary Controller about 20 input/output blocks of linear systems are provided that are based on the different representation forms, e.g., PID, StateSpace, Filter blocks. A unique feature of these blocks is that it is very convenient to quickly switch between a continuous and a discrete block representation. Also, templates are provide to quickly built-up standard controller structures.

For an introduction, have especially a look at:

- Getting started provides an overview of the Library inside the Users Guide.
- Release Notes summarizes the changes of new versions of this package.
- Contact gives the contact information for this library.
- In an interactive environment, it is useful to run first the script "_abbreviations.mos" in directory "Modelica_LinearSystems2\Extras\Scripts" in order to set useful abbreviations: ss, tf, zp, poly, Complex, Plot, s, p, j.

It is planned to include this library in a future version of the Modelica Standard Library. Note, the library is not backwards compatible to the previous beta version 0.95, called "Modelica_LinearSystems", which was shipped with previous versions of Dymola. Since the differences are too large, no conversion scripts are provided, but different library names are used.

Licensed by DLR under the Modelica License 2
Copyright © 2005-2009, DLR Institute of Robotics and Mechatronics

This Modelica package is free software and the use is completely at your own risk; it can be redistributed and/or modified under the terms of the Modelica license 2, see the license conditions (including the disclaimer of warranty) here or at http://www.Modelica.org/licenses/ModelicaLicense2.

### Package Content

Name | Description
--- | ---
UsersGuide | Users Guide
DataDir=classDirectory() + "Extras/Data/" | Absolute path to directory containing utilitiy files for this package
Examples | Examples demonstrating the usage of this package
StateSpace | Continuous state space description of a linear, time invariant differential equation system (data + operations)
TransferFunction | Continuous transfer function description of a single input, single output system (data + operations)
ZerosAndPoles | Continuous zeros and poles description of a single input, single output system (data + operations)
DiscreteStateSpace | Discrete state space description of a linear, time invariant difference equation system (data + operations)
Controller | Continuous and discrete input/output blocks. Easy to switch from continuous to discrete representation.
Math | Additional functions for Modelica.Math
Utilities | Functions that shall be included in Modelica.Utilities
Types | Constants and types with choices, especially to build menus
Internal | Internal library of library LinearSystems (should not be directly used by user)
WorkInProgress | Package for developments and internal models

### Types and constants

    constant String DataDir=classDirectory() + "Extras/Data/"
      "Absolute path to directory containing utilitiy files for this package";

## Modelica_LinearSystems2.StateSpace

Continuous state space description of a linear, time invariant differential equation system (data + operations)

### Information

This record defines a linear time invariant differential equation system in state space form:

        der(x) = A * x + B * u
            y  = C * x + D * u

with

- u - the input vector
- y - the output vector
- x - the state vector
- A,B,C,D - matrices of appropriate dimensions

Extends from Modelica.Icons.Record (Icon for a record).

### Parameters

Type | Name | Default | Description
--- | --- | --- | ---
der(x) = A*x + B*u; y = C*x + D*u
Real | A[:, size(A, 1)] |  |
Real | B[size(A, 1), :] |  |
Real | C[:, size(A, 1)] |  |
Real | D[size(C, 1), size(B, 2)] |  |
Signal names
String | yNames[size(C, 1)] | fill("", size(C, 1)) | Names of the output signals
String | xNames[size(A, 1)] | fill("", size(A, 1)) | Names of the states
String | uNames[size(B, 2)] | fill("", size(B, 2)) | Names of the input signals

### Modelica definition

    record StateSpace
      "Continuous state space description of a linear, time invariant differential equation system (data + operations)"

      extends Modelica.Icons.Record;

      Real A[:,size(A, 1)];
      Real B[size(A, 1),:];
      Real C[:,size(A, 1)];
      Real D[size(C, 1),size(B, 2)];

       String yNames[size(C, 1)]=fill("", size(C, 1)) "Names of the output signals";
       String xNames[size(A, 1)]=fill("", size(A, 1)) "Names of the states";
       String uNames[size(B, 2)]=fill("", size(B, 2)) "Names of the input signals";

    encapsulated operator 'constructor'
        "Default constructors for a StateSpace record"
        import Modelica_LinearSystems2;

      function fromABCDMatrices
          "Default constructor for a StateSpace record"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;

        input Real A[:,size(A, 1)];
        input Real B[size(A, 1),:];
        input Real C[:,size(A, 1)];
        input Real D[size(C, 1),size(B, 2)];

        input String uNames[size(B, 2)]=fill("", size(B, 2));
        input String yNames[size(C, 1)]=fill("", size(C, 1));
        input String xNames[size(A, 2)]=fill("", size(A, 2));

         output StateSpace result(
          redeclare Real A[size(A, 1),size(A, 2)],
          redeclare Real B[size(B, 1),size(B, 2)],
          redeclare Real C[size(C, 1),size(C, 2)],
          redeclare Real D[size(D, 1),size(D, 2)],
          redeclare String uNames[size(B, 2)],
          redeclare String yNames[size(C, 1)],
          redeclare String xNames[size(A, 2)]);

      algorithm
        result.A := A;
        result.B := B;
        result.C := C;
        result.D := D;
         result.uNames := uNames;
         result.yNames := yNames;
         result.xNames := xNames;

      end fromABCDMatrices;

      function fromReal
          "Generate a StateSpace data record from a Real value"

          import Modelica;
          import Modelica_LinearSystems2.StateSpace;

        input Real r "Value of Real variable";
        output StateSpace ss(
          redeclare Real A[0,0],
          redeclare Real B[0,1],
          redeclare Real C[1,0],
          redeclare Real D[1,1]) "= r";

      algorithm
        ss.D[1, 1] := r;
      end fromReal;

      function fromTransferFunction =
          Modelica_LinearSystems2.TransferFunction.Conversion.toStateSpace;
      function fromZerosAndPoles =
          Modelica_LinearSystems2.ZerosAndPoles.Conversion.toStateSpace;

    end 'constructor';

    encapsulated operator '-'
        "Contains operators for subtraction of state space systems"

      function subtract
          "Subtraction of two state space systems connected in parallel (= inputs are the same, outputs of the two systems are subtracted)"

          import Modelica;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss1 "State space system 1";
        input StateSpace ss2 "State Space system 2 is subtracted from system 1";
        output StateSpace result(
          redeclare Real A[size(ss1.A, 1) + size(ss2.A, 1),size(ss1.A, 2) + size(
            ss2.A, 2)],
          redeclare Real B[size(ss1.B, 1) + size(ss2.B, 1),size(ss1.B, 2)],
          redeclare Real C[size(ss1.C, 1),size(ss1.C, 2) + size(ss2.C, 2)],
          redeclare Real D[size(ss1.D, 1),size(ss1.D, 2)]) "= ss1 - ss2";
        protected
        Integer nx1=size(ss1.A, 1);
        Integer nx2=size(ss2.A, 1);
      algorithm
        result.A := [ss1.A,zeros(nx1, nx2); zeros(nx2, nx1),ss2.A];
        result.B := [ss1.B; ss2.B];
        result.C := [ss1.C,-ss2.C];
        result.D := ss1.D - ss2.D;
      end subtract;

      function negate
          "Unary minus (state space system where the output is multiplied by a gain of -1)"
          import Modelica;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss;
        output StateSpace result(
          redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
          redeclare Real B[size(ss.B, 1),size(ss.B, 2)],
          redeclare Real C[size(ss.C, 1),size(ss.C, 2)],
          redeclare Real D[size(ss.D, 1),size(ss.D, 2)]) "= -ss";
      algorithm
        result.A := ss.A;
        result.B := ss.B;
        result.C := -ss.C;
        result.D := -ss.D;
      end negate;
    end '-';

    encapsulated operator function '+'
        "Parallel connection of two state space systems (= inputs are the same, outputs of the two systems are added)"
        import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss1 "System 1";
        input StateSpace ss2 "System 2 is added in parallel to system 1";
        output StateSpace result(
          redeclare Real A[size(ss1.A, 1) + size(ss2.A, 1),size(ss1.A, 2) + size(
            ss2.A, 2)],
          redeclare Real B[size(ss1.B, 1) + size(ss2.B, 1),size(ss1.B, 2)],
          redeclare Real C[size(ss1.C, 1),size(ss1.C, 2) + size(ss2.C, 2)],
          redeclare Real D[size(ss1.D, 1),size(ss1.D, 2)]) "= ss1 + ss2";
      protected
        Integer nx1=size(ss1.A, 1);
        Integer nx2=size(ss2.A, 1);
    algorithm
        result.A := [ss1.A,zeros(nx1, nx2); zeros(nx2, nx1),ss2.A];
        result.B := [ss1.B; ss2.B];
        result.C := [ss1.C,ss2.C];
        result.D := ss1.D + ss2.D;

    end '+';

    encapsulated operator function '*'
        "Series connection of two state space systems"
        import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss1 "System 1";
        input StateSpace ss2 "System 2";
        output StateSpace result(
          redeclare Real A[size(ss1.A, 1) + size(ss2.A, 1),size(ss1.A, 2) + size(
            ss2.A, 2)],
          redeclare Real B[size(ss1.B, 1) + size(ss2.B, 1),size(ss2.B, 2)],
          redeclare Real C[size(ss1.C, 1),size(ss1.C, 2) + size(ss2.C, 2)],
          redeclare Real D[size(ss1.D, 1),size(ss2.D, 2)])
          "y = G(s)*u = G(ss1)*G(ss2)*u";
      protected
        Integer nx1=size(ss1.A, 1);
        Integer nx2=size(ss2.A, 1);
    algorithm
        result.A := [ss1.A,ss1.B*ss2.C; zeros(nx2, nx1),ss2.A];
        result.B := [ss1.B*ss2.D; ss2.B];
        result.C := [ss1.C,ss1.D*ss2.C];
        result.D := ss1.D*ss2.D;

    end '*';

    encapsulated operator function '=='
        "Check whether two linear systems have identical matrices"
        import Modelica.Math.Matrices.isEqual;
        import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss1 "System 1";
        input StateSpace ss2 "System 2";
        input Real eps(min=0) = 0
          "Two elements e1 and e2 of the two systems are identical if abs(e1-e2) <= eps";
        output Boolean same "=true, if the two systems are identical";
    algorithm
        same := isEqual(
              ss1.A,
              ss2.A,
              eps) and isEqual(
              ss1.B,
              ss2.B,
              eps) and isEqual(
              ss1.C,
              ss2.C,
              eps) and isEqual(
              ss1.D,
              ss2.D,
              eps);
    end '==';

    encapsulated operator function 'String'
        "Transform state space into a String representation"
       import Modelica;
       import Modelica_LinearSystems2.StateSpace;
       import Modelica.Utilities.Strings;

       input StateSpace ss
          "State space system to be transformed in a String representation";
       input Integer significantDigits=12
          "Number of significant digits that are shown";
       input String name="ss" "Independent variable name used for printing";
       output String s="";

      protected
        String space=Strings.repeat(5);
        String space2=Strings.repeat(3);
        String space3=Strings.repeat(8);
        Integer nx=size(ss.A, 1);
        Integer nu=size(ss.B, 2);
        Integer ny=size(ss.C, 1);
        Integer sizeD=size(ss.D, 2);
        Integer stringMaxLength;
        Boolean xNamesExist=false;
        Boolean uNamesExist=false;
        Boolean yNamesExist=false;

    algorithm
      //Checking if name arrays are empty
        for i in 1:nx loop
          xNamesExist := xNamesExist or (ss.xNames[i] <> "");
        end for;

        for i in 1:ny loop
          yNamesExist := yNamesExist or (ss.yNames[i] <> "");
        end for;

        for i in 1:nu loop
          uNamesExist := uNamesExist or (ss.uNames[i] <> "");
        end for;

        if xNamesExist then
          Modelica.Utilities.Streams.print("xNamesExist == true");
        else
          Modelica.Utilities.Streams.print("xNamesExist == false");
        end if;

        stringMaxLength := max(size(ss.xNames, 1), min(size(ss.yNames, 1),
          11));

        if nx == 0 and sizeD == 0 then
          s := name + ".A = []\n  " + name + ".B = []\n   " + name + ".C = [] \n   " + name + ".D = []";
        else
          s := "\n" + name + ".A = \n";

      //Horizontal
      // Two alternatives when printing state names
          if xNamesExist == false then
            s := s + Strings.repeat(stringMaxLength + significantDigits - 1) +
              "x1 ";
          else
            s := s + Strings.repeat(11 + significantDigits - min(Strings.length(
              ss.xNames[1]), 11)) + Strings.repeat(min(Strings.length(ss.xNames[
              1]), 11)) + " " + Strings.substring(
                  ss.xNames[1],
                  1,
                  min(Strings.length(ss.xNames[1]), 11));
          end if;

          for i in 2:nx loop

      //Two alternatives when printing state names

            if xNamesExist == false then
              s := s + Strings.repeat(significantDigits + 11 - Strings.length("x"
                 + String(i - 1))) + "x" + String(i) + " ";
            else
              s := s + " " + Strings.repeat(significantDigits + 11 - min(
                Strings.length(ss.xNames[i - 1]), 11)) + Strings.substring(
                    ss.xNames[i],
                    1,
                    min(Strings.length(ss.xNames[i]), 11));

            end if;

      //s := s + Strings.repeat(6) + "x" + String(i);
          end for;
          s := s + "\n";

          for i in 1:nx loop
      //Vertical
      //Two alternatives when printing state names
            if xNamesExist == false then
              s := s + space + "x" + String(i) + " ";
            else
              s := s + Strings.repeat(significantDigits + 11 - min(Strings.length(
                ss.xNames[i]), 11)) + Strings.substring(
                    ss.xNames[i],
                    1,
                    min(Strings.length(ss.xNames[i]), 11)) + " ";
            end if;

            for j in 1:nx loop
              if ss.A[i, j] >= 0 then
                s := s + " ";
              end if;
              s := s + String(ss.A[i, j], significantDigits=significantDigits) +
                Strings.repeat(significantDigits + 11 - Strings.length(String(abs(
                ss.A[i, j]), significantDigits=significantDigits)));
           end for;
            s := s + "\n";
          end for;
      //--------------------------------------------------------------------------------------------------------------------------------------------------
          s := s + "\n" + name + ".B = \n";
       //Horizontal
      // Two alternatives when printing state names
          if uNamesExist == false then
            s := s + Strings.repeat(stringMaxLength + significantDigits - 1) +
              "u1 ";
          else
            s := s + Strings.repeat(11 + significantDigits - min(Strings.length(
              ss.uNames[1]), 11)) + Strings.repeat(min(Strings.length(ss.uNames[
              1]), 11)) + " " + Strings.substring(
                  ss.uNames[1],
                  1,
                  min(Strings.length(ss.uNames[1]), 11));
          end if;

          for i in 2:nu loop
      //Two alternatives when printing state names
            if uNamesExist == false then
              s := s + Strings.repeat(significantDigits + 11 - Strings.length("u"
                 + String(i - 1))) + "u" + String(i) + " ";
            else
              s := s + " " + Strings.repeat(significantDigits + 11 - min(
                Strings.length(ss.uNames[i - 1]), 11)) + Strings.substring(
                    ss.uNames[i],
                    1,
                    min(Strings.length(ss.uNames[i]), 11));
            end if;
          end for;
          s := s + "\n";
      //s := s + Strings.repeat(6) + "x" + String(i);
          for i in 1:nx loop

      //Vertical
      //Two alternatives when printing state names
            if xNamesExist == false then
              s := s + space + "x" + String(i) + " ";
            else

              s := s + Strings.repeat(significantDigits + 11 - min(Strings.length(
                ss.xNames[i]), 11)) + Strings.substring(
                    ss.xNames[i],
                    1,
                    min(Strings.length(ss.xNames[i]), 11)) + " ";
            end if;

            for j in 1:nu loop
              if ss.B[i, j] >= 0 then
                s := s + " ";
              end if;
              s := s + String(ss.B[i, j], significantDigits=significantDigits) +
                Strings.repeat(significantDigits + 11 - Strings.length(String(abs(
                ss.B[i, j]), significantDigits=significantDigits)));
            end for;
            s := s + "\n";
          end for;

      //--------------------------------------------------------------------------------------------------------------------------------------------------
          s := s + "\n" + name + ".C = \n";
       //Horizontal
      // Two alternatives when printing state names
          if xNamesExist == false then
            s := s + Strings.repeat(stringMaxLength + significantDigits - 1) +
              "x1 ";
          else
            s := s + Strings.repeat(11 + significantDigits - min(Strings.length(
              ss.xNames[1]), 11)) + Strings.repeat(min(Strings.length(ss.xNames[
              1]), 11)) + " " + Strings.substring(
                  ss.xNames[1],
                  1,
                  min(Strings.length(ss.xNames[1]), 11));
          end if;

          for i in 2:nx loop
      //Two alternatives when printing state names
            if xNamesExist == false then
              s := s + Strings.repeat(significantDigits + 11 - Strings.length("x"
                 + String(i - 1))) + "x" + String(i) + " ";
            else
              s := s + " " + Strings.repeat(significantDigits + 11 - min(
                Strings.length(ss.xNames[i - 1]), 11)) + Strings.substring(
                    ss.xNames[i],
                    1,
                    min(Strings.length(ss.xNames[i]), 11));
            end if;
          end for;
          s := s + "\n";
      //s := s + Strings.repeat(6) + "x" + String(i);

          for i in 1:ny loop
      //Vertical
      //Two alternatives when printing state names
            if yNamesExist == false then
              s := s + space + "y" + String(i) + " ";
            else
              s := s + Strings.repeat(significantDigits + 11 - min(Strings.length(
                ss.yNames[i]), 11)) + Strings.substring(
                    ss.yNames[i],
                    1,
                    min(Strings.length(ss.yNames[i]), 11)) + " ";

            end if;

            for j in 1:nx loop
              if ss.C[i, j] >= 0 then
                s := s + " ";
              end if;
              s := s + String(ss.C[i, j], significantDigits=significantDigits) +
                Strings.repeat(significantDigits + 11 - Strings.length(String(abs(
                ss.C[i, j]), significantDigits=significantDigits)));
            end for;
            s := s + "\n";
          end for;
      //--------------------------------------------------------------------------------------------------------------------------------------------------
          s := s + "\n" + name + ".D = \n";
       //Horizontal
      // Two alternatives when printing state names
          if uNamesExist == false then
            s := s + Strings.repeat(stringMaxLength + significantDigits - 1) +
              "u1 ";
          else
            s := s + Strings.repeat(11 + significantDigits - min(Strings.length(
              ss.uNames[1]), 11)) + Strings.repeat(min(Strings.length(ss.uNames[
              1]), 11)) + " " + Strings.substring(
                  ss.uNames[1],
                  1,
                  min(Strings.length(ss.uNames[1]), 11));
          end if;

          for i in 2:nu loop
      //Two alternatives when printing state names
            if uNamesExist == false then
              s := s + Strings.repeat(significantDigits + 11 - Strings.length("u"
                 + String(i - 1))) + "u" + String(i) + " ";
            else
              s := s + " " + Strings.repeat(significantDigits + 11 - min(
                Strings.length(ss.uNames[i - 1]), 11)) + Strings.substring(
                    ss.uNames[i],
                    1,
                    min(Strings.length(ss.uNames[i]), 11));
            end if;
          end for;
          s := s + "\n";
          for i in 1:ny loop
      //Vertical
      //Two alternatives when printing state names
            if yNamesExist == false then
              s := s + space + "y" + String(i) + " ";
            else
              s := s + Strings.repeat(significantDigits + 11 - min(Strings.length(
                ss.yNames[i]), 11)) + Strings.substring(
                    ss.yNames[i],
                    1,
                    min(Strings.length(ss.yNames[i]), 11)) + " ";
            end if;

            for j in 1:nu loop
              if ss.D[i, j] >= 0 then
                s := s + " ";
              end if;
              s := s + String(ss.D[i, j], significantDigits=significantDigits) +
                Strings.repeat(significantDigits + 11 - Strings.length(String(abs(
                ss.D[i, j]), significantDigits=significantDigits)));
            end for;
            s := s + "\n";
          end for;

        end if;

    end 'String';

    encapsulated package Analysis
        "Functions to analyse state space systems represented by a StateSpace record"

      function analysis
          "Perform a system analysis based on the poles and zeros of the system"

          import Modelica;
          import Modelica.Utilities.Strings;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Internal.Eigenvalue;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Internal;
          import Modelica.Utilities.Streams.print;

          import Modelica_LinearSystems2.Utilities.Plot;

        input StateSpace ss;

        input Internal.AnalyseOptions analyseOptions=
            Modelica_LinearSystems2.Internal.AnalyseOptions(
            plotEigenValues=true,
            plotInvariantZeros=true,
            plotStepResponse=true,
            plotFrequencyResponse=true,
            printEigenValues=true,
            printEigenValueProperties=true,
            printInvariantZeros=true,
            printControllability=true,
            printObservability=true,
            headingEigenValues="Eigenvalues",
            headingInvariantzeros="Invariant zeros",
            headingStepResponse="Step response",
            headingFrequencyResponse="Frequency response");
        input String fileName="eigenvalues.html"
            "Name of html-file that contains eigenvalue table";
        input String systemName="" "Name of system (used as heading in html file)";
        input String description="" "Description of system (used in html file)";
        protected
        input Boolean printStateSpaceSystem=true;
        String dummyFileName="dummy" + fileName;
        public
        extends Modelica_LinearSystems2.Internal.PartialPlotFunction(defaultDiagram=
              Modelica_LinearSystems2.Internal.DefaultDiagramPolesAndZeros());

        protected
        input Complex j=Modelica_LinearSystems2.Math.Complex.j();
        Eigenvalue ev[size(ss.A, 1)];
        Integer nx=size(ss.A, 1);
        Integer window=0;
        Real eval[nx,2];
        Real revec[nx,nx];
        Real levec[nx,nx];
        Complex cev[size(ss.A, 1)];
        Complex systemZeros[:]=
        Modelica_LinearSystems2.StateSpace.Analysis.invariantZeros(ss);
        Boolean isStable;
        Boolean isControllable;
        Boolean isStabilizable;
        Boolean isObservable;
        Boolean isDetectable;

        Real abs_evec[nx];
        String xNames2[nx];
        String heading="Eigenvalues" "Eigen values of system";
        Eigenvalue evSorted[size(ss.A, 1)];
        Integer evIndex[size(ss.A, 1)];
        Complex zerosSorted[:];
        Integer zerosIndex[size(systemZeros, 1)];
        Integer nReal;

        Integer i;
        Integer k;
        Complex evecComplex[size(ss.A, 1),size(ss.A, 1)];
        Plot.Records.Curve curves[2];
        Plot.Records.Diagram diagram2;
        Boolean instableZeros=false;

      algorithm
        (eval,levec,revec) := Modelica_LinearSystems2.Math.Matrices.eigenValues(ss.A);

        for i in 1:nx loop
          cev[i].re := eval[i, 1];
          cev[i].im := eval[i, 2];
          ev[i].ev := cev[i];
        end for;

        (evSorted,evIndex) := Modelica_LinearSystems2.Internal.sortEigenvalue(ev);

       // Build x names
        if size(ss.xNames, 1) <> nx then
          for i in 1:nx loop
            xNames2[i] := "x[" + String(i) + "]";
          end for;
        else
          xNames2 := ss.xNames;
        end if;

      // ###### whole system checks ######
      // stability check
        isStable := true;
        for i in 1:nx loop
          isStable := isStable and ev[i].ev.re < 0;
        end for;

      //controllability check, stabilizability check
        isControllable := StateSpace.Analysis.isControllable(ss);
        isStabilizable := StateSpace.Analysis.isStabilizable(ss);

      // observability check, detectability check
        isObservable := StateSpace.Analysis.isObservable(ss);
        isDetectable := StateSpace.Analysis.isDetectable(ss);

      // analysis of single eingenvalues
        ev := StateSpace.Internal.characterizeEigenvalue(ss, ev);

      // Sort eigen values according to smallest imaginary value and restore the original order
        evSorted := Modelica_LinearSystems2.Internal.sortEigenvalue(ev);

      // analysis file

        Modelica.Utilities.Files.removeFile(fileName);
        Modelica.Utilities.Files.removeFile(dummyFileName);
        if printStateSpaceSystem then
          printSystem(
            ss,
            fileName,
            systemName,
            description);
          printSystem(
            ss,
            dummyFileName,
            systemName,
            description);
        end if;

        printHead1(
          ss,
          isStable,
          isControllable,
          isStabilizable,
          isObservable,
          isDetectable,
          fileName,
          analyseOptions=analyseOptions);
        printHead1(
          ss,
          isStable,
          isControllable,
          isStabilizable,
          isObservable,
          isDetectable,
          dummyFileName,
          analyseOptions=analyseOptions);

        Modelica.Utilities.Streams.readFile(dummyFileName);

      // Plot step response
        if analyseOptions.plotStepResponse then
          Modelica.Utilities.Files.removeFile(dummyFileName);
          print("

\n
\n

\nStep responses\n

    ",
            dummyFileName);
          Modelica.Utilities.Streams.readFile(dummyFileName);
          StateSpace.Plot.step(ss=ss);
        end if;

      // Plot Bode plots
        if analyseOptions.plotFrequencyResponse then
          Modelica.Utilities.Files.removeFile(dummyFileName);
          print("

\n
\n

\nBode plots\n

    ", dummyFileName);
          Modelica.Utilities.Streams.readFile(dummyFileName);
          StateSpace.Plot.bodeMIMO(ss=ss);
        end if;

       // calculate the number of real eigenvalues
        nReal := Modelica_LinearSystems2.Internal.numberOfRealZeros(cev);

        // Construct complex eigenvector matrix
        i := 1;
        while i <= nx loop
          if eval[i, 2] == 0.0 then

            for jj in 1:nx loop
              evecComplex[jj, i] := revec[jj, i] + 0*j;
            end for;
            i := i + 1;
          else
            for jj in 1:nx loop
              evecComplex[jj, i] := revec[jj, i] + revec[jj, i + 1]*j;
              evecComplex[jj, i + 1] := revec[jj, i] - revec[jj, i + 1]*j;
            end for;
            i := i + 2;
          end if;
        end while;

        if analyseOptions.printEigenValues then
          if nReal > 0 then
            printHead2a(fileName, analyseOptions=analyseOptions);
            printTab1(
              evSorted,
              evIndex,
              revec,
              levec,
              nReal,
              xNames2,
              fileName,
              analyseOptions=analyseOptions);

            Modelica.Utilities.Files.removeFile(dummyFileName);
            printHead2a(dummyFileName, analyseOptions=analyseOptions);
            printTab1(
              evSorted,
              evIndex,
              revec,
              levec,
              nReal,
              xNames2,
              dummyFileName,
              analyseOptions=analyseOptions);
            Modelica.Utilities.Streams.readFile(dummyFileName);
          else
            print("<b>The system has no real eigenvalues</b><br><br>", fileName);
            Modelica.Utilities.Files.removeFile(dummyFileName);
            print("

The system has no real eigenvalues

    ",
              dummyFileName);
            Modelica.Utilities.Streams.readFile(dummyFileName);

          end if;

          if nReal < nx then
            printHead2b(fileName, analyseOptions=analyseOptions);
            printTab2(
              evSorted,
              evIndex,
              revec,
              levec,
              nReal,
              xNames2,
              fileName,
              analyseOptions=analyseOptions);

            Modelica.Utilities.Files.removeFile(dummyFileName);
            printHead2b(dummyFileName, analyseOptions=analyseOptions);
            printTab2(
              evSorted,
              evIndex,
              revec,
              levec,
              nReal,
              xNames2,
              dummyFileName,
              analyseOptions=analyseOptions);
            Modelica.Utilities.Streams.readFile(dummyFileName);

        // Plot eigen values
            i := 0;
            if analyseOptions.plotEigenValues then
              i := i + 1;
              curves[i] := Plot.Records.Curve(
                x=eval[:, 1],
                y=eval[:, 2],
                legend="poles",
                autoLine=false,
                linePattern=Plot.Types.LinePattern.None,
                lineSymbol=Plot.Types.PointSymbol.Cross);
            end if;
       // Plot invariant zeros
            if size(systemZeros, 1) > 0 and analyseOptions.plotInvariantZeros then
              i := i + 1;
              curves[i] := Plot.Records.Curve(
                x=systemZeros[:].re,
                y=systemZeros[:].im,
                legend="zeros",
                autoLine=false,
                linePattern=Plot.Types.LinePattern.None,
                lineSymbol=Plot.Types.PointSymbol.Circle);
            end if;

            diagram2 := defaultDiagram;
            diagram2.curve := curves[1:i];
            Plot.diagram(diagram2, device);
          else
            print("<b>The system has no conjugated complex eigenvalues</b><br><br>", fileName);
            Modelica.Utilities.Files.removeFile(dummyFileName);
            print("

No conjugated complex eigenvalues

    ",
              dummyFileName);
            Modelica.Utilities.Streams.readFile(dummyFileName);
          end if;

          if analyseOptions.printEigenValueProperties then
            printHead3(fileName);
            printTab3(
              evSorted,
              evecComplex,
              evIndex,
              cev,
              nReal,
              xNames2,
              fileName);

            Modelica.Utilities.Files.removeFile(dummyFileName);
            printHead3(dummyFileName);
            printTab3(
              evSorted,
              evecComplex,
              evIndex,
              cev,
              nReal,
              xNames2,
              dummyFileName);
            Modelica.Utilities.Streams.readFile(dummyFileName);

          end if;
        end if;

      // ZEROS
        (zerosSorted,zerosIndex) :=
          Modelica_LinearSystems2.Math.Complex.Vectors.sortComplex(systemZeros);
        nReal := Modelica_LinearSystems2.Internal.numberOfRealZeros(zerosSorted);

        if analyseOptions.printInvariantZeros then
          if size(systemZeros, 1) > 0 then
            printHead4(fileName);
            Modelica_LinearSystems2.StateSpace.Analysis.analysis.printTab4(
              zerosSorted,
              zerosIndex,
              nReal,
              fileName);

            Modelica.Utilities.Files.removeFile(dummyFileName);
            printHead4(dummyFileName);
            printTab4(
              zerosSorted,
              zerosIndex,
              nReal,
              dummyFileName);
          else
            print("The system has no invariant zeros<br><br>", fileName);
            Modelica.Utilities.Files.removeFile(dummyFileName);
            print("

Invariant zeros
The system has no invariant zeros

    ",
              dummyFileName);
          end if;
          k := 0;
          for i in 1:size(systemZeros, 1) loop
            if systemZeros[i].re > 0 then
              k := k + 1;
            end if;
          end for;
          if k > 0 then
            print("<b>Note, that the system has " + String(k) + " zeros in the right complex half-plane</b>",
              fileName);
            print("

Note, that the system has "Error:Found no end-tag in HTML-documentation

     + String(k) + " zeros in the right complex half-plane</b></body></html>",
              dummyFileName);
          end if;

        end if;
        print("</body></html>", fileName);
        print("</body></html>", dummyFileName);
        Modelica.Utilities.Streams.readFile(dummyFileName);
        Modelica.Utilities.Files.removeFile(dummyFileName);

        print("\n\nAnalysis results have been written to file \"" +
          Modelica.Utilities.Files.fullPathName(fileName) + "\"");

      // SUB FUNCTIONS

      equation

        public
        encapsulated function printSystem
            "Print the state space system in html format on file"
            import Modelica;
            import Modelica.Utilities.Streams.print;
            import Modelica_LinearSystems2.StateSpace;
            import Modelica_LinearSystems2;

          input StateSpace ss "state space system to analyze";
          input String fileName="systemAnalysis.html"
              "File on which the state space is written in html format";
          input String systemName="State Space System"
              "name of the state space system";
          input String description="" "Description of system (used in html file)";
          input String format=".3g" "Format of numbers (e.g. \"20.8e\")";
          protected
          Integer nx=size(ss.A, 1);
          Integer nu=size(ss.B, 2);
          Integer ny=size(ss.C, 1);
          Integer c1=integer(ceil(nx/2) - 1);
          Integer c2=integer(ceil(ny/2) - 1);
          Integer dist=8;

        algorithm
          Modelica.Utilities.Files.removeFile(fileName);
          print("

System report

"Error:Found no end-tag in HTML-documentation

    , fileName);
          print("<p><br> The system <b>" + systemName + "</b><br></p>", fileName);
          print("<table style=\"font-size:10pt; font-family:Arial; border-collapse:collapse; text-align:right\" "
             + "cellpadding=\"3\" border=\"0\"> ", fileName);
          print("<tr><td>der(x) </td> <td>=</td> <td> Ax</td> <td> +</td><td> Bu</td></tr>
             <tr><td> y </td>     <td>=</td> <td> Cx</td> <td> + </td><td>Du</td></tr></table> <br><br>is defined by<br>",
            fileName);

        // print A and B
          print("<table style=\"font-size:10pt; font-family:Arial; border-collapse:collapse; text-align:right\" "
             + "cellpadding=\"3\" border=\"0\"> ", fileName);
          print("<tr><td><br></td><td><br></td><td><br></td>", fileName);
          for i1 in 1:nx loop
            print("<td style=\"text-align:center\" valign=\"top\"> " + ss.xNames[i1] + " </td>",
              fileName);
          end for;
          for i1 in 1:dist loop
            print("<td><br></td>", fileName);
          end for;
          for i1 in 1:nu loop
            print("<td>" + ss.uNames[i1] + "</td>", fileName);
          end for;

          print("</tr>", fileName);

        //print upper parts of A and B
          for i1 in 1:c1 loop
            print("<tr> <td><br></td><td><br></td><td> " + ss.xNames[i1] + " </td>",
              fileName);
            for i2 in 1:nx loop
              print("<td> " + String(ss.A[i1, i2], format=format) + " </td>", fileName);
            end for;

            for i2 in 1:dist - 1 loop
              print("<td><br></td>", fileName);
            end for;

            if nu > 0 then
              print("<td>" + ss.xNames[i1] + " </td>", fileName);
              for i2 in 1:nu loop
                print("<td> " + String(ss.B[i1, i2], format=format) + " </td>",
                  fileName);
              end for;
            end if;
               //nu>0
            print("</tr>", fileName);
          end for;

        //print middle part of A and B
          print("<tr><td>A</td><td>=</td><td>" + ss.xNames[c1 + 1] + " </td>", fileName);
          for i2 in 1:nx loop
            print("<td> " + String(ss.A[c1 + 1, i2], format=format) + " </td>",
              fileName);
          end for;
          for i2 in 1:dist - 3 loop
            print("<td><br></td>", fileName);
          end for;

          if nu > 0 then
            print("<td>B</td><td>=</td><td>" + ss.xNames[c1 + 1] + " </td>", fileName);

            for i2 in 1:nu loop
              print("<td> " + String(ss.B[c1 + 1, i2], format=format) + " </td>",
                fileName);
            end for;
          else
            print("<td>B</td><td>=</td><td>[ ],  empty matrix: " +String(nx)+" by "+String(nu) + " </td>", fileName);

          end if;
               //nu>0

        //print lower parts of A and B
          for i1 in c1 + 2:nx loop
            print("<tr><td><br></td><td><br></td><td> " + ss.xNames[i1] + " </td>",
              fileName);
            for i2 in 1:nx loop
              print("<td> " + String(ss.A[i1, i2], format=format) + " </td>", fileName);
            end for;

            for i2 in 1:dist - 1 loop
              print("<td><br></td>", fileName);
            end for;
            if nu > 0 then
              print("<td>" + ss.xNames[i1] + " </td>", fileName);
              for i2 in 1:nu loop
                print("<td> " + String(ss.B[i1, i2], format=format) + " </td>",
                  fileName);
              end for;
            end if;
               //nu>0
            print("</tr>", fileName);
          end for;

          print("</table>\n", fileName);

          print("<br><br>", fileName);

        // print C and D
          print("<table style=\"font-size:10pt; font-family:Arial; border-collapse:collapse; text-align:right\" "
             + "cellpadding=\"3\" border=\"0\">", fileName);
          print("<tr><td><br></td><td><br></td><td><br></td>", fileName);

          if ny > 0 then
            for i1 in 1:nx loop
              print("<td style=\"text-align:center\" valign=\"top\"> " + ss.xNames[i1] + " </td>",
                fileName);
            end for;
            for i1 in 1:dist loop
              print("<td><br></td>", fileName);
            end for;
            for i1 in 1:nu loop
              print("<td>" + ss.uNames[i1] + "</td>", fileName);
            end for;
          end if;
               //ny>0
          print("</tr>", fileName);

          if ny > 0 then
         //print upper parts of C and D
            for i1 in 1:c2 loop
              print("<tr> <td><br></td><td><br></td><td> " + ss.yNames[i1] + " </td>",
                fileName);
              for i2 in 1:nx loop
                print("<td> " + String(ss.C[i1, i2], format=format) + " </td>",
                  fileName);
              end for;

              for i2 in 1:dist - 1 loop
                print("<td><br></td>", fileName);
              end for;
              print("<td>" + ss.yNames[i1] + " </td>", fileName);
              for i2 in 1:nu loop
                print("<td> " + String(ss.D[i1, i2], format=format) + " </td>",
                  fileName);
              end for;

              print("</tr>", fileName);
            end for;
          end if;
               //ny>0

         //print middle part of C and D
          if ny > 0 then
            print("<tr><td>C</td><td>=</td><td>" + ss.yNames[c2 + 1] + " </td>",
              fileName);
            for i2 in 1:nx loop
              print("<td> " + String(ss.C[c2 + 1, i2], format=format) + " </td>",
                fileName);
            end for;
            for i2 in 1:dist - 3 loop
              print("<td><br></td>", fileName);
            end for;
            print("<td>D</td><td>=</td><td>" + (if nu>0 then ss.yNames[min(c2 + 1, ny)] else "[ ],  empty matrix: " +String(ny)+" by "+String(nu)) + " </td>",
              fileName);

            for i2 in 1:nu loop
              print("<td> " + String(ss.D[c2 + 1, i2], format=format) + " </td>",
                fileName);
            end for;
          else
            print("<tr><td>C</td><td>=</td><td>[ ],  empty matrix: " +String(ny)+" by "+String(nx) + " </td>", fileName);
            for i2 in 1:dist - 3 loop
              print("<td><br></td>", fileName);
            end for;
            print("<td>D</td><td>=</td><td>[ ],  empty matrix: " +String(ny)+" by "+String(nu) + " </td>", fileName);
          end if;
               //ny>0

         //print lower parts of C and D
          if ny > 0 then
            for i1 in c2 + 2:ny loop
              print("<tr><td><br></td><td><br></td><td> " + ss.yNames[i1] + " </td>",
                fileName);
              for i2 in 1:nx loop
                print("<td> " + String(ss.C[i1, i2], format=format) + " </td>",
                  fileName);
              end for;

              for i2 in 1:dist - 1 loop
                print("<td><br></td>", fileName);
              end for;
              print("<td>" + ss.yNames[i1] + " </td>", fileName);
              for i2 in 1:nu loop
                print("<td> " + String(ss.D[i1, i2], format=format) + " </td>",
                  fileName);
              end for;
              print("</tr>", fileName);
            end for;
          end if;
               // ny>0

          if description == "" then
            print("</table>\n", fileName);
          else
            print("</table>", fileName);
            print("<p>\n<b>Description</b>\n</p>", fileName);
            print(description, fileName);
          end if;

          if ny==0 and nu==0 then
            print("<p>\n<b>Note</b>, that matrices <b>B</b> and <b>C</b> are empty matrices, i.e. the system has neither inputs nor outputs!\n</p>", fileName);
          elseif ny==0 then
             print("<p>\n<b>Note</b>, that atrix <b>C</b> is empty matrix, i.e. the system has no outputs!\n</p>", fileName);
          elseif nu==0 then
             print("<p>\n<b>Note</b>, that matrix <b>B</b> is empty matrix, i.e. the system has no inputs!\n</p>", fileName);
          end if;

          print("</body></html>", fileName);

        end printSystem;

        encapsulated function printHead1
            import Modelica;
            import Modelica.Utilities.Strings;
            import Modelica_LinearSystems2;
            import Modelica.Utilities.Streams.print;
            import Modelica_LinearSystems2.StateSpace;

          input StateSpace ss;
          input Boolean isStable;
          input Boolean isControllable;
          input Boolean isStabilizable;
          input Boolean isObservable;
          input Boolean isDetectable;
          input String fileName;

          input Modelica_LinearSystems2.Internal.AnalyseOptions analyseOptions=
              Modelica_LinearSystems2.Internal.AnalyseOptions(
                plotEigenValues=true,
                plotInvariantZeros=true,
                plotStepResponse=true,
                plotFrequencyResponse=true,
                printEigenValues=true,
                printEigenValueProperties=true,
                printInvariantZeros=true,
                printControllability=true,
                printObservability=true,
                headingEigenValues="Eigenvalues",
                headingInvariantzeros="Invariant zeros",
                headingStepResponse="Step response",
                headingFrequencyResponse="Frequency response");

        algorithm
          print("

\n
\n

\nCharacteristics\n

The system\n

"Error:Found no end-tag in HTML-documentation

     + "</p> is ",
            fileName);
          if analyseOptions.printControllability and analyseOptions.printObservability then
            print((if isStable then " " else "not ") + "stable" + "\n<br>" + (if
              isStable then if isControllable then "and it is " else "but it is not " else
                    if isControllable then "but it is " else "and it is not ") + "controllable"
               + (if isStable then "" else "\n<br>" + (if isControllable then " and therefore it is " else
                    if isStabilizable then " but it is " else "and is not ") + "stabilizable")
               + "\n<br> The system is " + (if isObservable then " " else "not ") + "observable"
               + (if isStable then "" else "\n<br>" + (if isObservable then " and therefore it is " else
                    if isDetectable then " but it is " else "and is not ") + "detectable")
               + "\n<br></br>", fileName);
          elseif not analyseOptions.printObservability and analyseOptions.printControllability then
            print((if isStable then " " else "not ") + "stable" + "\n<br>" + (if
              isStable then if isControllable then "and it is " else "but it is not " else
                    if isControllable then "but it is " else "and it is not ") + "controllable"
               + (if isStable then "" else "\n<br>" + (if isControllable then " and therefore it is " else
                    if isStabilizable then " but it is " else "and is not ") + "stabilizable")
               + "\n<br></br>", fileName);
          elseif not analyseOptions.printControllability and analyseOptions.printObservability then
            print((if isStable then " " else "not ") + "stable" + "\n<br> The system is "
               + (if isObservable then " " else "not ") + "observable" + (if isStable then
                    "" else "\n<br>" + (if isObservable then " and therefore it is " else
                    if isDetectable then " but it is " else "and is not ") + "detectable")
               + "\n<br></br>", fileName);
          else
            print((if isStable then " " else "not ") + "stable" + "\n<br></br>",
              fileName);
          end if;
          print("</body></html>", fileName);

        end printHead1;

        public
        encapsulated function printHead2a
            import Modelica;
            import Modelica.Utilities.Strings;
            import Modelica_LinearSystems2;
            import Modelica.Utilities.Streams.print;

          input String fileName;
          input Modelica_LinearSystems2.Internal.AnalyseOptions analyseOptions=
              Modelica_LinearSystems2.Internal.AnalyseOptions(
                plotEigenValues=true,
                plotInvariantZeros=true,
                plotStepResponse=true,
                plotFrequencyResponse=true,
                printEigenValues=true,
                printEigenValueProperties=true,
                printInvariantZeros=true,
                printControllability=true,
                printObservability=true,
                headingEigenValues="Eigenvalues",
                headingInvariantzeros="Invariant zeros",
                headingStepResponse="Step response",
                headingFrequencyResponse="Frequency response");

        algorithm
          if analyseOptions.printEigenValueProperties then
            print("

\n
\nEigenvalues analysis
Real eigenvalues\n
"Error:Found no end-tag in HTML-documentation

               + "<table style=\"font-size:10pt; font-family:Arial; border-collapse:collapse; text-align:right\" "
               + "cellpadding=\"3\" border=\"1\">\n" + "<tr style=\"background-color:rgb(230, 230, 230); text-align:center;\">"
               + "<td> number </td><td> eigenvalue </td> <td> T [s] </td>  <td> characteristics </td><td> contribution to states</td></tr>",
              fileName);
          else
            print("

\n
\nEigenvalues analysis
Real eigenvalues\n
"Error:Found no end-tag in HTML-documentation

               + "<table style=\"font-size:10pt; font-family:Arial; border-collapse:collapse; text-align:right\" "
               + "cellpadding=\"3\" border=\"1\">\n" + "<tr style=\"background-color:rgb(230, 230, 230); text-align:center;\">"
               + "<td> number </td><td> eigenvalue </td> <td> T [s] </td>  <td> characteristics </td></tr>",
              fileName);
          end if;
        end printHead2a;

        public
        encapsulated function printHead2b
            import Modelica;
            import Modelica.Utilities.Strings;
            import Modelica_LinearSystems2;
            import Modelica.Utilities.Streams.print;

          input String fileName;
          input Modelica_LinearSystems2.Internal.AnalyseOptions analyseOptions=
              Modelica_LinearSystems2.Internal.AnalyseOptions(
                plotEigenValues=true,
                plotInvariantZeros=true,
                plotStepResponse=true,
                plotFrequencyResponse=true,
                printEigenValues=true,
                printEigenValueProperties=true,
                printInvariantZeros=true,
                printControllability=true,
                printObservability=true,
                headingEigenValues="Eigenvalues",
                headingInvariantzeros="Invariant zeros",
                headingStepResponse="Step response",
                headingFrequencyResponse="Frequency response");

        algorithm
          print("

\n
"Error:Found no end-tag in HTML-documentation

    , fileName);
          if analyseOptions.printEigenValueProperties then
            print("<b>Conjugated complex pairs of eigenvalues</b>\n<br>" + "<table style=\"font-size:10pt; font-family:Arial; border-collapse:collapse; text-align:right\" "
               + "cellpadding=\"3\" border=\"1\">\n" + "<tr style=\"background-color:rgb(230, 230, 230); text-align:center;\">"
               + "<td> number </td> <td> eigenvalue </td><td> freq. [Hz] </td> <td> damping </td><td> characteristics </td>  <td> contribution to states</td></tr>",
              fileName);
          else
            print("<b>Conjugated complex pairs of eigenvalues</b>\n<br>" + "<table style=\"font-size:10pt; font-family:Arial; border-collapse:collapse; text-align:right\" "
               + "cellpadding=\"3\" border=\"1\">\n" + "<tr style=\"background-color:rgb(230, 230, 230); text-align:center;\">"
               + "<td> number </td> <td> eigenvalue </td><td> freq. [Hz] </td> <td> damping </td><td> characteristics </td> </tr>",
              fileName);
          end if;
        end printHead2b;

        encapsulated function printHead3
            import Modelica;
            import Modelica.Utilities.Strings;
            import Modelica_LinearSystems2;
            import Modelica.Utilities.Streams.print;

          input String fileName;
          input Modelica_LinearSystems2.Internal.AnalyseOptions analyseOptions=
              Modelica_LinearSystems2.Internal.AnalyseOptions(
                plotEigenValues=true,
                plotInvariantZeros=true,
                plotStepResponse=true,
                plotFrequencyResponse=true,
                printEigenValues=true,
                printEigenValueProperties=true,
                printInvariantZeros=true,
                printControllability=true,
                printObservability=true,
                headingEigenValues="Eigenvalues",
                headingInvariantzeros="Invariant zeros",
                headingStepResponse="Step response",
                headingFrequencyResponse="Frequency response");

        algorithm
          print("

"Error:Found no end-tag in HTML-documentation

    , fileName);
          print("<p> In the tables above, the column <b>contribution to states</b> lists for each eigenvalue the states to which the"
             + "corresponding modal state contributes most. This information is based on the "
             + "two largest absolute values of the corresponding right eigenvector (if the second large value "
             + "is less than 5 % of the largest contribution, it is not shown).<br>" + "</p><p>"
             + "In the next table, for each state in the column <b>correlation to modal states</b>, the modal  "
             + "states which contribute most to the coresponding state are summarized, i.e. the state is mostly composed of these modal states "
             + "This information is based on the two largest absolute values of row i of the "
             + "eigenvector matrix that is associated with eigenvalue i (if the second large value  "
             + "is less than 5 % of the largest contribution, it is not shown). This only holds "
             + "if the modal states are in the same order of magnitude. Otherwise, the modal states "
             + "listed in the last column might be not the most relevant one. </p><br><br> "
             + "<table style=\"font-size:10pt; font-family:Arial; border-collapse:collapse; text-align:right\" "
             + "cellpadding=\"3\" border=\"1\"> " + "<tr style=\"background-color:rgb(230, 230, 230); text-align:center;\">"
             + "<td> state </td> <td> composition </td> <td> eigenvalue #</td> <td> freq. [Hz] </td> <td> damping </td>  </td> <td> T [s] </td></tr>",
            fileName);

        end printHead3;

        encapsulated function printHead4
            import Modelica;
            import Modelica.Utilities.Strings;
            import Modelica_LinearSystems2;
            import Modelica.Utilities.Streams.print;

          input String fileName;
          input Modelica_LinearSystems2.Internal.AnalyseOptions analyseOptions=
              Modelica_LinearSystems2.Internal.AnalyseOptions(
                plotEigenValues=true,
                plotInvariantZeros=true,
                plotStepResponse=true,
                plotFrequencyResponse=true,
                printEigenValues=true,
                printEigenValueProperties=true,
                printInvariantZeros=true,
                printControllability=true,
                printObservability=true,
                headingEigenValues="Eigenvalues",
                headingInvariantzeros="Invariant zeros",
                headingStepResponse="Step response",
                headingFrequencyResponse="Frequency response");

        algorithm
          print("

"Error:Found no end-tag in HTML-documentation

    , fileName);
          print("<p><br><br><b>Invariant zeros</b><br>" + "<table style=\"font-size:10pt; font-family:Arial; border-collapse:collapse; text-align:right\" "
             + "cellpadding=\"3\" border=\"1\">" + "<tr style=\"background-color:rgb(230, 230, 230); text-align:center;\">"
             + "<td> number </td> <td> invariant zero </td><td> Time constant [s] </td> <td> freq. [Hz] </td> <td> damping </td></tr>",
            fileName);
        end printHead4;

        encapsulated function printTab1
            import Modelica;
            import Modelica.Utilities.Strings;
            import Modelica_LinearSystems2;
            import Modelica.Utilities.Streams.print;
            import Modelica_LinearSystems2.Internal.Eigenvalue;
            import Modelica_LinearSystems2.Math.Complex;

          input Eigenvalue evSorted[:];
          input Integer evIndex[size(evSorted, 1)];
          input Real r_evec[size(evSorted, 1),size(evSorted, 1)];
          input Real l_evec[size(evSorted, 1),size(evSorted, 1)];
          input Integer nReal;
          input String xNames2[size(evSorted, 1)];
          input String fileName;
          input Modelica_LinearSystems2.Internal.AnalyseOptions analyseOptions=
              Modelica_LinearSystems2.Internal.AnalyseOptions(
                plotEigenValues=true,
                plotInvariantZeros=true,
                plotStepResponse=true,
                plotFrequencyResponse=true,
                printEigenValues=true,
                printEigenValueProperties=true,
                printInvariantZeros=true,
                printControllability=true,
                printObservability=true,
                headingEigenValues="Eigenvalues",
                headingInvariantzeros="Invariant zeros",
                headingStepResponse="Step response",
                headingFrequencyResponse="Frequency response");

          protected
          Integer nx=size(evSorted, 1);
          Real w;
          Real d;

          Integer i;
          Integer j;
          Integer k;
          String number;

          Real r_abs_evec[nx];
          Real l_abs_evec[nx];
          Integer r_maxIndex1;
          Integer l_maxIndex1;
          Integer r_maxIndex2;
          Integer l_maxIndex2;
        //  Complex v_normalized[size(evSorted,1)];
          Real r_abs_v_normalized;
          Real l_abs_v_normalized;
          Real r_v;
          Real l_v;
          Real r_absMax1;
          Real l_absMax1;
          Real r_absMax2;
          Real l_absMax2;
          Boolean r_two;
          Boolean l_two;
          Boolean r_first;
          Boolean l_first;

        algorithm
          i := 1;
          j := i;
          while i <= nReal loop
           // Build eigenvalue number

            number := String(
                i,
                minimumLength=7,
                leftJustified=false);
            j := j + 1;

           // Determine largest value in eigenvector
            k := evIndex[i] "Index with respect to unsorted eigen values";
            r_abs_evec := abs(r_evec[:, k]);
            l_abs_evec := abs(l_evec[:, k]);

            r_first := true;
            r_two := false;
            r_absMax1 := 0;
            r_maxIndex1 := 0;
            r_absMax2 := 0;
            r_maxIndex2 := 0;
            r_abs_v_normalized := Modelica.Math.Vectors.norm(r_abs_evec, 1);

            l_first := true;
            l_two := false;
            l_absMax1 := 0;
            l_maxIndex1 := 0;
            l_absMax2 := 0;
            l_maxIndex2 := 0;
            l_abs_v_normalized := Modelica.Math.Vectors.norm(l_abs_evec, 1);
            for j in 1:nx loop
              r_v := r_abs_evec[j];
              l_v := l_abs_evec[j];

              if r_first then
                r_first := false;
                r_absMax1 := r_v;
                r_maxIndex1 := j;
              elseif not r_two then
                r_two := true;
                if r_v < r_absMax1 then
                  r_absMax2 := r_v;
                  r_maxIndex2 := j;
                else
                  r_absMax2 := r_absMax1;
                  r_maxIndex2 := r_maxIndex1;
                  r_absMax1 := r_v;
                  r_maxIndex1 := j;
                end if;
              elseif r_v > r_absMax1 then
                r_absMax2 := r_absMax1;
                r_maxIndex2 := r_maxIndex1;
                r_absMax1 := r_v;
                r_maxIndex1 := j;
              elseif r_v > r_absMax2 then
                r_absMax2 := r_v;
                r_maxIndex2 := j;
              end if;

              if l_first then
                l_first := false;
                l_absMax1 := l_v;
                l_maxIndex1 := j;
              elseif not l_two then
                l_two := true;
                if l_v < l_absMax1 then
                  l_absMax2 := l_v;
                  l_maxIndex2 := j;
                else
                  l_absMax2 := l_absMax1;
                  l_maxIndex2 := l_maxIndex1;
                  l_absMax1 := l_v;
                  l_maxIndex1 := j;
                end if;
              elseif l_v > l_absMax1 then
                l_absMax2 := l_absMax1;
                l_maxIndex2 := l_maxIndex1;
                l_absMax1 := l_v;
                l_maxIndex1 := j;
              elseif l_v > l_absMax2 then
                l_absMax2 := l_v;
                l_maxIndex2 := j;
              end if;

            end for;

            r_absMax1 := 100*r_absMax1/r_abs_v_normalized;
            r_absMax2 := 100*r_absMax2/r_abs_v_normalized;

            if r_absMax2 < 0.05*r_absMax1 then
              r_two := false;
            end if;

            l_absMax1 := 100*l_absMax1/l_abs_v_normalized;
            l_absMax2 := 100*l_absMax2/l_abs_v_normalized;

            if l_absMax2 < 0.05*l_absMax1 then
              l_two := false;
            end if;

           // Print data for one eigen value
            if analyseOptions.printEigenValueProperties then
              print("<tr>\n <td style=\"text-align:center\"> " + number + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + String(evSorted[i].ev.re, format="14.4e") + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + String(evSorted[i].timeConstant, format="9.4f") + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + (if evSorted[i].isStable then "" else "not ") + "stable, " + (if
                evSorted[i].isStable then (if evSorted[i].isControllable then "" else
                      "not ") + "controllable, " else (if evSorted[i].isStabilizable then
                      "" else "not ") + "stabilizable, ") + (if evSorted[i].isStable then
                      (if evSorted[i].isObservable then "" else "not ") + "observable " else
                      (if evSorted[i].isDetectable then "" else "not ") + "detectable ")
                 + " </td> <td style=\"text-align:left\"> &nbsp; " + " z[" + String(i)
                 + "]" + " contributes to " + xNames2[r_maxIndex1] + " with " +
                String(r_absMax1, format=".3g") + " %<br>" + (if r_two then "&nbsp; " + " z["
                 + String(i) + "]" + " contributes to " + xNames2[r_maxIndex2] + " with "
                 + String(r_absMax2, format=".3g") + " %" else "") + " </td> </tr> ",
                fileName);

            else
              print("<tr>\n <td style=\"text-align:center\"> " + number + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + String(evSorted[i].ev.re, format="14.4e") + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + String(evSorted[i].timeConstant, format="9.4f") + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + (if evSorted[i].isStable then "" else "not ") + "stable, " + (if
                evSorted[i].isStable then (if evSorted[i].isControllable then "" else
                      "not ") + "controllable, " else (if evSorted[i].isStabilizable then
                      "" else "not ") + "stabilizable, ") + (if evSorted[i].isStable then
                      (if evSorted[i].isObservable then "" else "not ") + "observable " else
                      (if evSorted[i].isDetectable then "" else "not ") + "detectable ")
                 + " </td> </tr> ", fileName);
            end if;
            i := j;
          end while;

          print("</table><br><br>\n\n</body></html>", fileName);
        end printTab1;

        encapsulated function printTab2
            import Modelica;
            import Modelica.Utilities.Strings;
            import Modelica_LinearSystems2;
            import Modelica.Utilities.Streams.print;
            import Modelica_LinearSystems2.Internal.Eigenvalue;
            import Modelica_LinearSystems2.Math.Complex;

          input Eigenvalue evSorted[:];
          input Integer evIndex[size(evSorted, 1)];
          input Real r_evec[size(evSorted, 1),size(evSorted, 1)];
          input Real l_evec[size(evSorted, 1),size(evSorted, 1)];
          input Integer nReal;
          input String xNames2[size(evSorted, 1)];
          input String fileName;
          input Modelica_LinearSystems2.Internal.AnalyseOptions analyseOptions=
              Modelica_LinearSystems2.Internal.AnalyseOptions(
                plotEigenValues=true,
                plotInvariantZeros=true,
                plotStepResponse=true,
                plotFrequencyResponse=true,
                printEigenValues=true,
                printEigenValueProperties=true,
                printInvariantZeros=true,
                printControllability=true,
                printObservability=true,
                headingEigenValues="Eigenvalues",
                headingInvariantzeros="Invariant zeros",
                headingStepResponse="Step response",
                headingFrequencyResponse="Frequency response");

          protected
          Integer nx=size(evSorted, 1);

          Integer i;
          Integer k;
          String number;
          Integer j;

          Real r_abs_evec[nx];
          Real l_abs_evec[nx];
          Integer r_maxIndex1;
          Integer r_maxIndex2;
          Integer l_maxIndex1;
          Integer l_maxIndex2;
          Real r_abs_v_normalized;
          Real l_abs_v_normalized;
          Real r_v;
          Real l_v;
          Real r_absMax1;
          Real r_absMax2;
          Real l_absMax1;
          Real l_absMax2;
          Boolean r_two;
          Boolean l_two;
          Boolean r_first;
          Boolean l_first;

        algorithm
          i := nReal + 1;
          j := i;
          while i <= nx loop
           // Build eigenvalue number
            number := String(i) + "/" + String(i + 1);
            number := Strings.repeat(max(0, 7 - Strings.length(number))) + number;
            j := j + 2;

           // Determine largest value in eigenvector
            k := evIndex[i] "Index with respect to unsorted eigen values";

            for i2 in 1:nx loop
              r_abs_evec[i2] := sqrt(r_evec[i2, k]^2 + r_evec[i2, k + 1]^2);
              l_abs_evec[i2] := sqrt(l_evec[i2, k]^2 + l_evec[i2, k + 1]^2);
            end for;

            r_first := true;
            r_two := false;
            r_absMax1 := 0;
            r_maxIndex1 := 0;
            r_absMax2 := 0;
            r_maxIndex2 := 0;
            r_abs_v_normalized := Modelica.Math.Vectors.norm(r_abs_evec, 1);
            l_first := true;
            l_two := false;
            l_absMax1 := 0;
            l_maxIndex1 := 0;
            l_absMax2 := 0;
            l_maxIndex2 := 0;
            l_abs_v_normalized := Modelica.Math.Vectors.norm(l_abs_evec, 1);

            for j in 1:nx loop
              r_v := r_abs_evec[j];
              l_v := l_abs_evec[j];

              if r_first then
                r_first := false;
                r_absMax1 := r_v;
                r_maxIndex1 := j;
              elseif not r_two then
                r_two := true;
                if r_v < r_absMax1 then
                  r_absMax2 := r_v;
                  r_maxIndex2 := j;
                else
                  r_absMax2 := r_absMax1;
                  r_maxIndex2 := r_maxIndex1;
                  r_absMax1 := r_v;
                  r_maxIndex1 := j;
                end if;
              elseif r_v > r_absMax1 then
                r_absMax2 := r_absMax1;
                r_maxIndex2 := r_maxIndex1;
                r_absMax1 := r_v;
                r_maxIndex1 := j;
              elseif r_v > r_absMax2 then
                r_absMax2 := r_v;
                r_maxIndex2 := j;
              end if;

              if l_first then
                l_first := false;
                l_absMax1 := l_v;
                l_maxIndex1 := j;
              elseif not l_two then
                l_two := true;
                if l_v < l_absMax1 then
                  l_absMax2 := l_v;
                  l_maxIndex2 := j;
                else
                  l_absMax2 := l_absMax1;
                  l_maxIndex2 := l_maxIndex1;
                  l_absMax1 := l_v;
                  l_maxIndex1 := j;
                end if;
              elseif l_v > l_absMax1 then
                l_absMax2 := l_absMax1;
                l_maxIndex2 := l_maxIndex1;
                l_absMax1 := l_v;
                l_maxIndex1 := j;
              elseif l_v > l_absMax2 then
                l_absMax2 := l_v;
                l_maxIndex2 := j;
              end if;

            end for;
            r_absMax1 := 100*r_absMax1/r_abs_v_normalized;
            r_absMax2 := 100*r_absMax2/r_abs_v_normalized;
            if r_absMax2 < 0.05*r_absMax1 then
              r_two := false;
            end if;

            l_absMax1 := 100*l_absMax1/l_abs_v_normalized;
            l_absMax2 := 100*l_absMax2/l_abs_v_normalized;
            if l_absMax2 < 0.05*l_absMax1 then
              l_two := false;
            end if;

           // Print data for one eigen value
            if analyseOptions.printEigenValueProperties then
              print("<tr>\n <td style=\"text-align:left\"> " + number + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + String(evSorted[i].ev.re, format="14.4e") + " &plusmn; " + String(
                evSorted[i].ev.im, format="12.4e") + "j" + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + String(evSorted[i].frequency, format="9.4f") + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + String(evSorted[i].damping, format="9.4f") + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + (if evSorted[i].isStable then "" else "not ") + "stable, " + (if
                evSorted[i].isStable then (if evSorted[i].isControllable then "" else
                      "not ") + "controllable, " else (if evSorted[i].isStabilizable then
                      "" else "not ") + "stabilizable, ") + (if evSorted[i].isStable then
                      (if evSorted[i].isObservable then "" else "not ") + "observable " else
                      (if evSorted[i].isDetectable then "" else "not ") + "detectable ")
                 + " </td> <td style=\"text-align:left\"> &nbsp; " + " z[" + number + "]"
                 + " contribute to " + xNames2[r_maxIndex1] + " with " + String(
                r_absMax1, format=".3g") + " %<br>" + (if r_two then "&nbsp; " + " z["
                 + number + "]" + " contribute to " + xNames2[r_maxIndex2] + " with " +
                String(r_absMax2, format=".3g") + " %" else "") + " </td> </tr> ",
                fileName);
            else
              print("<tr>\n <td style=\"text-align:left\"> " + number + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + String(evSorted[i].ev.re, format="14.4e") + " &plusmn; " + String(
                evSorted[i].ev.im, format="12.4e") + "j" + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + String(evSorted[i].frequency, format="9.4f") + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + String(evSorted[i].damping, format="9.4f") + " </td> <td style=\"text-align:left\"> &nbsp; "
                 + (if evSorted[i].isStable then "" else "not ") + "stable, " + (if
                evSorted[i].isStable then (if evSorted[i].isControllable then "" else
                      "not ") + "controllable, " else (if evSorted[i].isStabilizable then
                      "" else "not ") + "stabilizable, ") + (if evSorted[i].isStable then
                      (if evSorted[i].isObservable then "" else "not ") + "observable " else
                      (if evSorted[i].isDetectable then "" else "not ") + "detectable ")
                 + " </td> </tr> ", fileName);
            end if;
            i := j;
          end while;

          print("</table>", fileName);
          print("</table><br><br>\n\n</body></html>", fileName);
        end printTab2;

        encapsulated function printTab3
            import Modelica;
            import Modelica.Utilities.Strings;
            import Modelica_LinearSystems2;
            import Modelica.Utilities.Streams.print;
            import Modelica_LinearSystems2.Internal.Eigenvalue;
            import Modelica_LinearSystems2.Math.Complex;

          input Eigenvalue evSorted[:];
          input Complex evecComplex[:,:];
          input Integer evIndex[size(evecComplex, 1)];
          input Complex cev[size(evecComplex, 1)];
          input Integer nReal;
          input String xNames2[size(evecComplex, 1)];
          input String fileName;
          input Modelica_LinearSystems2.Internal.AnalyseOptions analyseOptions=
              Modelica_LinearSystems2.Internal.AnalyseOptions(
                plotEigenValues=true,
                plotInvariantZeros=true,
                plotStepResponse=true,
                plotFrequencyResponse=true,
                printEigenValues=true,
                printEigenValueProperties=true,
                printInvariantZeros=true,
                printControllability=true,
                printObservability=true,
                headingEigenValues="Eigenvalues",
                headingInvariantzeros="Invariant zeros",
                headingStepResponse="Step response",
                headingFrequencyResponse="Frequency response");

          protected
          Integer nx=size(evecComplex, 1);

          Integer maxIndex1;
          Integer maxIndex2;

          Complex v_normalized[size(evecComplex, 1)];
          Real abs_v_normalized;
          Real v;
          Real absMax1;
          Real absMax2;
          Boolean two;
          Boolean first;
          Integer j;
          Integer k;
          Integer iw1;
          Integer iw2;
          String number1;
          String number2;
          Real w1;
          Real w2;
          Real d1;
          Real d2;

        algorithm
          for i in 1:nx loop
             // Normalize i-th row of complex eigenvector matrix and determine two largest elements
            v_normalized := Complex.Vectors.normalize(evecComplex[i, :]);
            first := true;
            two := false;
            absMax1 := 0;
            maxIndex1 := 0;
            absMax2 := 0;
            maxIndex2 := 0;
            j := 1;
            abs_v_normalized := Complex.Vectors.norm(v_normalized, 1);
            while j <= nx loop
              if cev[j].im == 0 then
                v := abs(v_normalized[j].re);
                k := j;
                j := j + 1;
              else
                v := 2*Complex.'abs'(v_normalized[j]);
                k := j;
                j := j + 2;
              end if;

              if first then
                first := false;
                absMax1 := v;
                maxIndex1 := k;
              elseif not two then
                two := true;
                if v < absMax1 then
                  absMax2 := v;
                  maxIndex2 := k;
                else
                  absMax2 := absMax1;
                  maxIndex2 := maxIndex1;
                  absMax1 := v;
                  maxIndex1 := k;
                end if;
              elseif v > absMax1 then
                absMax2 := absMax1;
                maxIndex2 := maxIndex1;
                absMax1 := v;
                maxIndex1 := k;
              elseif v > absMax2 then
                absMax2 := v;
                maxIndex2 := k;
              end if;
            end while;
            absMax1 := absMax1/abs_v_normalized;
            absMax2 := absMax2/abs_v_normalized;

            if absMax2 < 0.05*absMax1 then
              two := false;
            end if;

             // Determine frequency and number of corresponding eigenvalue
            (w1,d1) := Complex.frequency(cev[maxIndex1]);
            iw1 := Modelica_LinearSystems2.Math.Vectors.find(maxIndex1, evIndex);
            if iw1 <= nReal then
              number1 := String(iw1);
            else
              number1 := String(iw1) + "/" + String(iw1 + 1);
            end if;

            if two then
              (w2,d2) := Complex.frequency(cev[maxIndex2]);
              iw2 := Modelica_LinearSystems2.Math.Vectors.find(maxIndex2, evIndex);
              if iw2 <= nReal then
                number2 := String(iw2);
              else
                number2 := String(iw2) + "/" + String(iw2 + 1);
              end if;
            end if;

            print("<tr>\n <td style=\"text-align:left\"> &nbsp; " + xNames2[i] + " </td> <td style=\"text-align:left\"> &nbsp; "
               + " is composed of " + String(100*absMax1, format="5.1f") + "% by z[" +
              number1 + "]" + (if two then " <br>" + " &nbsp; " + " is composed of " +
              String(100*absMax2, format="5.1f") + "% by z[" + number2 + "]" else "") + " </td> <td style=\"text-align:center\"> &nbsp; "
               + number1 + (if two then "<br> &nbsp; " + number2 else Strings.repeat(9))
               + " </td> <td style=\"text-align:center\"> &nbsp; " + (if iw1 <= nReal then
                    "---" else String(w1, format="9.4f")) + (if two then "<br> &nbsp; "
               + (if iw2 <= nReal then "---" else String(w2, format="9.4f")) else
              Strings.repeat(9)) + " </td> <td style=\"text-align:center\"> &nbsp; " +
              (if iw1 <= nReal then "---" else String(d1, format="9.4f")) + (if two then
                    "<br> &nbsp; " + (if iw2 <= nReal then "---" else String(d2,
              format="9.4f")) else "") + " </td> <td style=\"text-align:center\"> &nbsp; "
               + (if (iw1 <= nReal) then String(evSorted[i].timeConstant, format="9.4f") else
                    "---") + (if two then "<br> &nbsp; " + (if (iw2 <= nReal and abs(
              cev[maxIndex2].re) > 1e-10) then String(1/abs(cev[maxIndex2].re),
              format="9.4f") else "---") else "") + " </td> </tr> ", fileName);

          end for;
          print("</table></body></html>", fileName);

        end printTab3;

        encapsulated function printTab4
            import Modelica;
            import Modelica.Utilities.Strings;
            import Modelica_LinearSystems2;
            import Modelica.Utilities.Streams.print;
            import Modelica_LinearSystems2.Internal.Eigenvalue;
            import Modelica_LinearSystems2.Math.Complex;

          input Complex systemZeros[:];
          input Integer evIndex[size(systemZeros, 1)];
          input Integer nReal;
          input String fileName;
          input Modelica_LinearSystems2.Internal.AnalyseOptions analyseOptions=
              Modelica_LinearSystems2.Internal.AnalyseOptions(
                plotEigenValues=true,
                plotInvariantZeros=true,
                plotStepResponse=true,
                plotFrequencyResponse=true,
                printEigenValues=true,
                printEigenValueProperties=true,
                printInvariantZeros=true,
                printControllability=true,
                printObservability=true,
                headingEigenValues="Eigenvalues",
                headingInvariantzeros="Invariant zeros",
                headingStepResponse="Step response",
                headingFrequencyResponse="Frequency response");

          protected
          Integer nz=size(systemZeros, 1);

          String number;
          Real timeConstant;
          Real freq;
          Real damp;

        algorithm
          for i in 1:nReal loop
          // Build eigenvalue number

            number := String(
                i,
                minimumLength=7,
                leftJustified=false);
            timeConstant := if abs(systemZeros[i].re) > 10*Modelica.Constants.eps then
                    1/abs(systemZeros[i].re) else 1/(10*Modelica.Constants.eps);

            print("<tr>\n <td style=\"text-align:left\"> &nbsp; " + number + " </td> <td> &nbsp; "
               + String(systemZeros[i].re, format="14.4e") + " </td> <td> &nbsp; " +
              String(timeConstant, format="9.4f") + " </td> <td style=\"text-align:center\"> &nbsp; "
               + "---" + " </td> <td style=\"text-align:center\"> &nbsp; " + "---" + " </td> </tr> ",
              fileName);

          end for;

          for i in nReal + 1:2:nz loop
            number := String(i) + "/" + String(i + 1);
            number := Strings.repeat(max(0, 7 - Strings.length(number))) + number;

           // Determine frequency and number of corresponding zero
            (freq,damp) := Complex.frequency(systemZeros[i]);

            print("<tr>\n <td style=\"text-align:left\"> &nbsp; " + number + " </td> <td style=\"text-align:left\"> &nbsp; "
               + String(systemZeros[i].re, format="14.4e") + " &plusmn; " + String(
              systemZeros[i].im, format="12.4e") + "j" + " </td> <td style=\"text-align:center\"> &nbsp; "
               + "---" + " </td> <td style=\"text-align:left\"> &nbsp; " + String(
              freq, format="9.4f") + " </td> <td style=\"text-align:left\"> &nbsp; " +
              String(damp, format="9.4f") + " </td> </tr> ", fileName);

          end for;

          print("</table><br><br><br>", fileName);
        end printTab4;
      end analysis;

     encapsulated function timeResponse
          "Calculate the time response of a state space system"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Types.TimeResponse;
     input Modelica_LinearSystems2.Types.TimeResponse response=Modelica_LinearSystems2.Types.TimeResponse.Step;
      extends Modelica_LinearSystems2.Internal.timeResponseMask2(redeclare Real y[:,size(sc.C,1),if response == TimeResponse.Initial then 1 else size(sc.B,2)], redeclare
              Real x_continuous[
                             :,size(sc.A,1),if response == TimeResponse.Initial then 1 else size(sc.B,2)]);     // Input/Output declarations of time response functions

      input Real x0[size(sc.A, 1)]=zeros(size(sc.A, 1)) "Initial state vector";

        protected
      Real dtVar;
      Real tSpanVar;
      Integer samples;
      Real u[:,size(sc.B, 2)];
      Real new_x[size(sc.A, 1),1];
      Real x[size(sc.A, 1),1]=zeros(size(sc.A, 1), 1);
      Modelica_LinearSystems2.DiscreteStateSpace sd(
        redeclare Real A[size(sc.A, 1),size(sc.A, 2)],
        redeclare Real B[size(sc.B, 1),size(sc.B, 2)],
        redeclare Real C[size(sc.C, 1),size(sc.C, 2)],
        redeclare Real D[size(sc.D, 1),size(sc.D, 2)],
        redeclare Real B2[size(sc.B, 1),size(sc.B, 2)]);
      Real i1;
      Real i2;

     algorithm
          // set sample time and simulation time span
      if (dt == 0 and tSpan == 0) then
        (dtVar,tSpanVar) := Modelica_LinearSystems2.Internal.timeResponseSamples(
          sc);
      elseif (dt == 0 and tSpan <> 0) then
        dtVar := Modelica_LinearSystems2.Internal.timeResponseSamples(sc);
        tSpanVar := tSpan;
      elseif (dt <> 0 and tSpan == 0) then
        (,tSpanVar) := Modelica_LinearSystems2.Internal.timeResponseSamples(sc);
        dtVar := dt;
      else
        dtVar := dt;
        tSpanVar := tSpan;
      end if;

      samples := integer(tSpanVar/dtVar + 1);
      t := 0:dtVar:tSpanVar;
      u := zeros(samples, size(sc.B, 2));
      y := if response == TimeResponse.Initial then zeros(samples, size(sc.C, 1),1) else zeros(samples, size(sc.C, 1),size(sc.B, 2));
      x_continuous :=  if response == TimeResponse.Initial then zeros(samples, size(sc.A, 1),  1) else zeros(samples, size(sc.A, 1),  size(sc.B, 2));

     if response == TimeResponse.Initial then
          sd := Modelica_LinearSystems2.DiscreteStateSpace(
              sc,
              dtVar,
              Modelica_LinearSystems2.Types.Method.Trapezoidal);
        (y[:, :, 1],x_continuous[:, :, 1]) :=
          Modelica_LinearSystems2.DiscreteStateSpace.initialResponse(sd, x0, samples);
        else

      for i1 in 1:size(sc.B, 2) loop
            // Loop over inputs

            // time response to plot
        if response == TimeResponse.Impulse then
          u[1, :] := zeros(size(sc.B, 2));
          u[1, i1] := 1;
          sd := Modelica_LinearSystems2.DiscreteStateSpace(
              sc,
              dtVar,
              Modelica_LinearSystems2.Types.Method.ImpulseExact);
        elseif response == TimeResponse.Step then
          u[:, :] := zeros(samples, size(sc.B, 2));
          u[:, i1] := ones(samples);
          sd := Modelica_LinearSystems2.DiscreteStateSpace(
              sc,
              dtVar,
              Modelica_LinearSystems2.Types.Method.StepExact);
        elseif response == TimeResponse.Ramp then
          u[:, :] := zeros(samples, size(sc.B, 2));
          u[:, i1] := 0:dtVar:tSpanVar;
          sd := Modelica_LinearSystems2.DiscreteStateSpace(
              sc,
              dtVar,
              Modelica_LinearSystems2.Types.Method.RampExact);
     //    elseif response == TimeResponse.Initial then
     //      u[:, :] := zeros(samples, size(sc.B, 2));
     //      sd := Modelica_LinearSystems2.DiscreteStateSpace(
     //          sc,
     //          dtVar,
     //          Modelica_LinearSystems2.Types.Method.Trapezoidal);
        else
          assert(false, "Argument response (= " + String(response) +
            ") of \"Time response to plot\" is wrong.");
        end if;
        (y[:, :, i1],x_continuous[:, :, i1]) :=
          Modelica_LinearSystems2.DiscreteStateSpace.timeResponse(
            sd,
            u,
            x0);

      end for;
     end if;

     end timeResponse;

    encapsulated function impulseResponse
          "Calculate the impulse time response of a state space system"

          import Modelica;
          import Modelica_LinearSystems2;

        // Input/Output declarations of time response functions:
      extends Modelica_LinearSystems2.Internal.timeResponseMask2;

    algorithm
      (y,t,x_continuous) := Modelica_LinearSystems2.StateSpace.Analysis.timeResponse(
          sc=sc,
          dt=dt,
          tSpan=tSpan,
          response=Modelica_LinearSystems2.Types.TimeResponse.Impulse,
          x0=zeros(size(sc.A, 1)));

    end impulseResponse;

    encapsulated function stepResponse
          "Calculate the step time response of a state space system"

          import Modelica;
          import Modelica_LinearSystems2;

        // Input/Output declarations of time response functions:
      extends Modelica_LinearSystems2.Internal.timeResponseMask2;

    algorithm
      (y,t,x_continuous) := Modelica_LinearSystems2.StateSpace.Analysis.timeResponse(
          sc=sc,
          dt=dt,
          tSpan=tSpan,
          response=Modelica_LinearSystems2.Types.TimeResponse.Step,
          x0=zeros(size(sc.A, 1)));

    end stepResponse;

    encapsulated function rampResponse
          "Calculate the ramp time response of a state space system"

          import Modelica;
          import Modelica_LinearSystems2;

        // Input/Output declarations of time response functions:
      extends Modelica_LinearSystems2.Internal.timeResponseMask2;

    algorithm
      (y,t,x_continuous) := Modelica_LinearSystems2.StateSpace.Analysis.timeResponse(
          sc=sc,
          dt=dt,
          tSpan=tSpan,
          response=Modelica_LinearSystems2.Types.TimeResponse.Ramp,
          x0=zeros(size(sc.A, 1)));

    end rampResponse;

    encapsulated function initialResponse
          "Calculate the time response of a state space system for given initial condition and zero inputs"

          import Modelica;
          import Modelica_LinearSystems2;

      input Real x0[:]=fill(0,0) "Initial state vector";

        // Input/Output declarations of time response functions:
      extends Modelica_LinearSystems2.Internal.timeResponseMask2(redeclare Real y[:,size(sc.C,1),1], redeclare
              Real x_continuous[
                            :,size(sc.A,1),1]);

    algorithm
      (y,t,x_continuous) := Modelica_LinearSystems2.StateSpace.Analysis.timeResponse(
          sc=sc,
          dt=dt,
          tSpan=tSpan,
          response=Modelica_LinearSystems2.Types.TimeResponse.Initial,
          x0=x0);

    end initialResponse;

      encapsulated function numeratorDegree
          "Return numerator degree of the corresponding transfer function"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;

          input Modelica_LinearSystems2.StateSpace ss;
          output Integer result;

        protected
          Modelica_LinearSystems2.TransferFunction tf= if StateSpace.Internal.isSISO(ss) then StateSpace.Conversion.toTransferFunction(ss) else TransferFunction(1);

      algorithm
        assert(StateSpace.Internal.isSISO(ss),"System must be SISO but is "+ String(size(ss.B,2)) +"-by-" +String(size(ss.C,1)) +" system");
       result := size(tf.n,1)-1;

      end numeratorDegree;

      encapsulated function denominatorDegree
          "Return denominator degree of the corresponding transfer function"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;

        input Modelica_LinearSystems2.StateSpace ss;
        output Integer result;
        protected
        Modelica_LinearSystems2.TransferFunction tf= if StateSpace.Internal.isSISO(ss) then StateSpace.Conversion.toTransferFunction(ss) else TransferFunction(1);

      algorithm
        assert(StateSpace.Internal.isSISO(ss),"System must be SISO but is "+ String(size(ss.B,2)) +"-by-" +String(size(ss.C,1)) +" system");
      result := size(tf.d,1)-1;

      end denominatorDegree;

      encapsulated function evaluate
          "Evaluate a the corresponding transfer function at a given (complex) value of s"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss;
        input Complex s "Value of s where tf shall be evaluated";
        input Real den_min=0 "|denominator(s)| is limited by den_min";
        output Complex result "= tf(s)";

        protected
        Boolean issiso=StateSpace.Internal.isSISO(ss);
        TransferFunction tf= if issiso then StateSpace.Conversion.toTransferFunction(ss) else TransferFunction(1);
        Complex j=Modelica_LinearSystems2.Math.Complex.j();
        Complex den=Polynomial.evaluateComplex(Polynomial(tf.d), s);
        Real abs_den=Complex.'abs'(den);
      algorithm
        assert(issiso,"System must be SISO but is "+ String(size(ss.B,2)) +"-by-" +String(size(ss.C,1)) +" system");
        den := if abs_den >= den_min then den else -abs_den + 0*j;
        result := Polynomial.evaluateComplex(Polynomial(tf.n), s)/den;

      end evaluate;

      encapsulated function zerosAndPoles
          "Calculate zeros and poles of the TransferFunction corresponding to a state space representation"
          import Modelica;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss;

        output Complex z[:] "Zeros (Complex vector of numerator zeros)";
        output Complex p[:] "Poles (Complex vector of denominator zeros)";
        output Real k
            "Constant multiplied with transfer function that is factorized with zeros and poles";

        protected
        TransferFunction tf=StateSpace.Conversion.toTransferFunction(ss);
        Polynomial pn;
        Polynomial pd;
        TransferFunction tf2;
        Real r;
        Complex s;
        Complex y1;
        Complex y2;
      algorithm

        z:=Polynomial.roots(Polynomial(tf.n));
        p:=Polynomial.roots(Polynomial(tf.d));
        pn:=Polynomial(z);
        pd:=Polynomial(p);
        tf2:=TransferFunction(pn, pd);
        // Determine an s-value that is neither a zero nor a pole
        r := 1.0;
        for i in 1:size(z, 1) loop
          r := max(r, abs(z[i].re));
        end for;
        for i in 1:size(p, 1) loop
          r := max(r, abs(p[i].re));
        end for;
        r := 2*r;
        s := Complex(r, 0);

        // Evaluate both tf and tf2 and determine k from the quotient
        y1 := TransferFunction.Analysis.evaluate(tf, s);
        y2 := TransferFunction.Analysis.evaluate(tf2, s);
        k := y1.re/y2.re;
      end zerosAndPoles;

      encapsulated function eigenValues
          "Calculate the eigenvalues of a linear state space system and write them in a complex vector"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Complex;

        input StateSpace ss "state space system";
        output Complex eigvalues[size(ss.A, 1)]=Complex.eigenValues(ss.A)
            "eigen values of the system";
      algorithm

      end eigenValues;

      encapsulated function eigenVectors
          "Calculate the rigth eigenvectors of a linear state space system and write them columnwise in a matrix. Optionally, the eigenvalues are computed"
          import Modelica;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica.Math.Matrices.LAPACK;
          import Modelica_LinearSystems2.Math.Complex;

        input StateSpace ss "state space system";
        input Boolean onlyEigenvectors=true;
        output Real eigvec[size(ss.A, 1),size(ss.A, 2)]
            "eigen values of the system";
        output Complex eigval[size(ss.A, 1)]=fill(Complex(0), size(ss.A, 1))
            "eigen values of the system";
        protected
        Integer info;
        Real eigvalRe[size(ss.A, 1)]=fill(0, size(ss.A, 1));
        Real eigvalIm[size(ss.A, 1)]=fill(0, size(ss.A, 1));

      algorithm
        if size(ss.A, 1) > 0 then
          if onlyEigenvectors then
            (,,eigvec,info) := LAPACK.dgeev(ss.A);
          else
            (eigvalRe,eigvalIm,eigvec,info) := LAPACK.dgeev(ss.A);
            for i in 1:size(ss.A, 1) loop
              eigval[i].re := eigvalRe[i];
              eigval[i].im := eigvalIm[i];
            end for;
          end if;

          assert(info == 0, "Calculating the eigen values with function
    \"StateSpace.Analysis.eigenVectors\" is not possible, since the
    numerical algorithm does not converge.");
        end if;

      end eigenVectors;

      encapsulated function invariantZeros
          "Compute invariant zeros of linear state space system"

          import Modelica;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Math.Matrices;
          import Modelica_LinearSystems2.Math.Matrices.LAPACK;

        input StateSpace ss "Linear system in state space form";

        output Complex Zeros[:]
            "Finite, invariant zeros of ss; size(Zeros,1) <= size(ss.A,1)";

        protected
        Integer n=10;
        Integer m;
        Integer p;
        Real Ar[:,:];
        Real Br[:,:];
        Real Cr[:,:];
        Real Dr[:,:];

        Real Af[:,:];
        Real Bf[:,:];
        Real AfBf[:,:];

        Real V2[:,:];
        Real Vf[:,:];
        Real R[:,:];

        Integer na;
       Real alphaReal[:];
        Real alphaImag[:];
        Real beta[:];
        Integer info;
        Real beta_small=100*Modelica.Constants.eps;
        Real normB=max(Modelica.Math.Matrices.norm(ss.B, p=1),beta_small);
        Real normA=max(Modelica.Math.Matrices.norm(ss.A, p=1),beta_small);

      algorithm
        if min(size(ss.B)) == 0 or min(size(ss.C)) == 0 then
          Zeros := fill(Complex(0), 0);
        else
          (Ar,Br,Cr,Dr,n,m,p) := StateSpace.Internal.reduceRosenbrock(ss.A, ss.B, ss.C, ss.D);
          if n > 0 then
            (Ar,Br,Cr,Dr,n,m,p) := StateSpace.Internal.reduceRosenbrock(transpose(Ar), transpose(Cr), transpose(Br), transpose(Dr));
          end if;
          if n == 0 then
            Zeros := fill(Complex(0), 0);
          else

            (,R,,V2) := Matrices.QR(Matrices.fliplr(transpose([Cr,Dr])));
            Vf := Matrices.fliplr(V2);
            AfBf := [Ar,Br]*Vf;
            Af := AfBf[:, 1:size(Ar, 2)];
            Bf := Vf[1:size(Ar, 1), 1:size(Ar, 2)];

            (alphaReal,alphaImag,beta,,,info) := LAPACK.dggev(Af, Bf, n);
            assert(info == 0, "Failed to compute invariant zeros with function invariantZeros(..)");

            Zeros := fill(Complex(0), size(beta, 1));

      // If beta[i] is zero, then zero i is infinite.
            for i in 1:size(beta, 1) loop
              if beta[i] >= normB*1e-6 then
           // finite eigenvalue
                Zeros[i].re := if abs(alphaReal[i]) >= normB*1e-12 then alphaReal[i]/
                  beta[i] else 0;
                Zeros[i].im := if abs(alphaImag[i]) >= normB*1e-12 then alphaImag[i]/
                  beta[i] else 0;
              end if;
            end for;

          end if;
        end if;
      end invariantZeros;

      encapsulated function isControllable
          "Check controllability of a state space system"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.StateSpace.Internal;

        input StateSpace ss;
        input Modelica_LinearSystems2.Types.Method method=Modelica_LinearSystems2.Types.StaircaseMethod.SVD;

        output Boolean controllable;
      algorithm

        controllable := if StateSpace.Internal.isSISO(ss) then
          StateSpace.Internal.isControllableSISO(ss) else StateSpace.Internal.isControllableMIMO(ss,method);

      end isControllable;

      encapsulated function isObservable
          "Check observability of a state space system"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss;
        input Modelica_LinearSystems2.Types.StaircaseMethod method=
            Modelica_LinearSystems2.Types.StaircaseMethod.SVD;

        output Boolean observable;
      algorithm

        observable := if StateSpace.Internal.isSISO(ss) then
          StateSpace.Internal.isObservableSISO(ss) else
          StateSpace.Internal.isObservableMIMO(ss, method);

      end isObservable;

      encapsulated function isStabilizable
          "Check stabilizability of a state space system"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss;

        output Boolean stabilizable;

      algorithm
        if StateSpace.Internal.isSISO(ss) then
          stabilizable := StateSpace.Internal.isStabilizableSISO(ss);
        else
          stabilizable := StateSpace.Internal.isStabilizableMIMO(ss);
        end if;

      end isStabilizable;

      encapsulated function isDetectable
          "Check detectability of a state space system"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss;

        output Boolean detectable;

      algorithm
       if StateSpace.Internal.isSISO(ss) then
        detectable := StateSpace.Internal.isDetectableSISO(ss);
        else
         detectable := StateSpace.Internal.isDetectableMIMO(ss);
         end if;

      end isDetectable;

      encapsulated function controllabilityMatrix
          "Compute the controllability matrix [B, A*B, ..., A^(n-1)*B] of a state space system"

          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss;
        output Real cm[size(ss.B, 1),size(ss.A, 2)*size(ss.B, 2)];

      algorithm
        if size(ss.A, 2) == 0 then
          cm := fill(
              0,
              size(ss.B, 1),
              0);
        else
          cm[:, 1:size(ss.B, 2)] := ss.B;

          for i in 2:size(ss.A, 1) loop
            cm[:, ((i - 1)*size(ss.B, 2) + 1):(i*size(ss.B, 2))] := ss.A*cm[:, ((i
               - 2)*size(ss.B, 2) + 1):((i - 1)*size(ss.B, 2))];
          end for;
        end if;

      end controllabilityMatrix;

      encapsulated function observabilityMatrix
          "Compute the observability matrix of a state space system"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss;
        output Real om[size(ss.A, 1)*size(ss.C, 1),size(ss.C, 2)];

      algorithm
        if size(ss.A, 1) == 0 then
          om := fill(
              0,
              0,
              size(ss.C, 2));
        else
          om[1:size(ss.C, 1), :] := ss.C;

          for i in 2:size(ss.A, 1) loop
            om[((i - 1)*size(ss.C, 1) + 1):(i*size(ss.C, 1)), :] := om[((i - 2)*
              size(ss.C, 1) + 1):((i - 1)*size(ss.C, 1)), :]*ss.A;
          end for;
        end if;

      end observabilityMatrix;

    end Analysis;

      encapsulated package Design
        "Functions for state space controller design"
        encapsulated function assignPolesSI
          "Pole placement for single input systems using Ackermann's formula."

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;

          input StateSpace ss;
          input Modelica_LinearSystems2.Math.Complex p[size(ss.A, 1)]
            "Vector of desired poles";
          output Real k[size(ss.A, 1)] "Feedback gain matrix";

        protected
          Real cm[size(ss.B, 1),size(ss.A, 1)*size(ss.B, 2)];
          Modelica_LinearSystems2.Math.Polynomial poly;
          Real Y[size(ss.A, 1),size(ss.A, 2)];
          Real X[:,:];
          Modelica_LinearSystems2.Math.Complex p_actual[size(p, 1)];
          Modelica_LinearSystems2.Math.Complex p_sorted[size(p, 1)];
          Real poleError;
          Modelica_LinearSystems2.Math.Complex smaller;
        algorithm
          assert(size(ss.B,2)==1,"System must be SI but has "+ String(size(ss.B,2)) +" inputs");
          cm := StateSpace.Analysis.controllabilityMatrix(ss);
          assert(Modelica.Math.Matrices.rank(cm) == size(cm, 1) or
            Modelica.Math.Matrices.rank(cm) == size(cm, 2),
            "Controllability matrix has not full rank. System is not controllable!");
          poly := Modelica_LinearSystems2.Math.Polynomial(p);
          Y := Modelica_LinearSystems2.Math.Polynomial.evaluateMatrix(poly, ss.A);
          X := zeros(size(cm, 2), size(Y, 2));
          for j in 1:size(Y, 2) loop
            X[:, j] := Modelica.Math.Matrices.leastSquares(cm, vector(Y[:, j]));
          end for;
          k := X[size(ss.A, 1), :];

          // Check results
          // sort p
          p_sorted := p;
          for i1 in 1:size(p_sorted, 1) loop
            for i2 in (1 + i1):size(p_sorted, 1) loop
              if Modelica_LinearSystems2.Math.Complex.'abs'(p_sorted[i1]) >
                  Modelica_LinearSystems2.Math.Complex.'abs'(p_sorted[i2]) then
                smaller := p_sorted[i2];
                p_sorted[i2] := p_sorted[i1];
                p_sorted[i1] := smaller;
              end if;
            end for;
          end for;

          p_actual := Modelica_LinearSystems2.Math.Complex.eigenValues(ss.A - ss.B*
            transpose(matrix(k)));
           // sort actual eigenvalues
          for i1 in 1:size(p_actual, 1) loop
            for i2 in (1 + i1):size(p_actual, 1) loop
              if Modelica_LinearSystems2.Math.Complex.'abs'(p_actual[i1]) >
                  Modelica_LinearSystems2.Math.Complex.'abs'(p_actual[i2]) then
                smaller := p_actual[i2];
                p_actual[i2] := p_actual[i1];
                p_actual[i1] := smaller;
              end if;
            end for;
          end for;

          // check for poles that have an error of more than 10%
          for i in 1:size(p_sorted, 1) loop
            if (Modelica_LinearSystems2.Math.Complex.'abs'(p_sorted[i]) <> 0) then
               poleError := Modelica_LinearSystems2.Math.Complex.'abs'((p_sorted[i] -
                p_actual[i]))/Modelica_LinearSystems2.Math.Complex.'abs'(p_sorted[i]);

              if poleError > 0.1 then
                  Modelica.Utilities.Streams.print("Warning: Pole location of pole " +
                  String(p_sorted[i]) + " has an error of " + String(100*poleError)
                   + "%. (Is " + String(p_actual[i]) + ")");

              end if;
            end if;
          end for;

        end assignPolesSI;

        encapsulated function assignPolesMI
          "Pole assigment design algorithm for multi input systems"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica;
          import Modelica.Utilities.Streams.print;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Math.Matrices;

          input StateSpace ss "state space system";

          input Complex gamma[:]=fill(Complex(0), 0) "Designed Poles";
        //  input Integer np=size(gamma, 1) "number of given eigenvalues to assign";
          input Real alpha=-1e10
            "maximum admissible value for real parts(continuous) or for moduli (discrete) of the eigenvalues of A which will not be modified by the eigenvalue assignment algorithm";
          input Real tolerance=Modelica.Math.Matrices.norm(ss.A, 1)*1e-12
            "The tolerance to be used in determining the controllability of (A,B)";
          input Boolean calculateEigenvectors=false
            "Calculate the eigenvectors X of the closed loop system when true";

          output Real K[size(ss.B, 2),size(ss.A, 1)]
            "State feedback matrix assigning the desired poles";
          output Real S[:,:] "Closed loop System matrix";
          output Complex po[size(ss.A, 1)] "poles of the closed loop system";
          output Integer nfp
            "number of eigenvalues that are not modified with respect to alpha";
          output Integer nap "number of assigned eigenvalues";
          output Integer nup "number of uncontrollable eigenvalues";
          output Complex X[size(ss.A, 1),size(ss.A, 1)]
            "eigenvectors of the closed loop system";

        protected
          Real A_rsf[size(ss.A, 1),size(ss.A, 2)];
          Real B_rsf[size(ss.B, 1),size(ss.B, 2)];
          Real Q[size(ss.A, 1),size(ss.A, 1)];
          Real Ks1[:,:];
          Real Ks2[:,:];
          Real Q2[:,:];
          Real A_rsf_1[:,:];
          Real Q1[:,:];
          Boolean select[:];
          Boolean rselectA[:];
          Real Z[:,:] "orthogonal transformation matrix";
          Real ZT[:,:] "orthogonal transformation matrix";
          Complex pf[:];
          Complex gammaReordered[:]=gamma;
          Integer info;
          Real wr[size(gamma, 1)];
          Real wi[size(gamma, 1)];
          Boolean imag=false;
          Integer i;
          Integer ii;
          Integer iii;
          Integer counter;
          Integer counter2;
          Integer n=size(ss.A, 1);
          Integer nccA "number of conjugated complex pole pairs of openloop system";
          Integer nccg "number of conjugated complex pole pairs of gamma";
          Integer rpg "number of real poles in gamma";
          Integer rpA "number of real poles of open loop system";
          Integer ncc "Min(nccA, nccg)";
          Integer rp "Min(rpg, rpA)";
          Integer ng=size(gamma,1);
          Integer nr "Differenz between rpA and rpg; Sign(rpA-rpg)*(rpA-rpg)";

          Real alphaReal[size(ss.A, 1)]
            "Real part of eigenvalue=alphaReal+i*alphaImag";
          Real alphaImag[size(ss.A, 1)]
            "Imaginary part of eigenvalue=(alphaReal+i*alphaImag";

          Complex SS[:,:];
          Complex Xj[:,:];
          Complex h;

          Real dist;
          Real evImag;

        algorithm
          assert(size(gamma, 1) <= size(ss.A, 1),
            "At most n (order of ss) eigenvalues can be assigned");

         /* Extraction of Poles (Variable conversation) and pole sequence check */
          for i in 1:size(gamma, 1) loop
            wr[i] := gamma[i].re;
            wi[i] := gamma[i].im;
            if imag then
              assert(wi[i - 1] == -wi[i] and wr[i - 1] == wr[i],
                "Poles are in wrong sequence");
              imag := false;
            elseif wi[i] <> 0 then
              imag := true;
            end if;
          end for;

          // put matrix ss.A to real Schur form A <- QAQ' and compute B <- QB
          (A_rsf,Z,alphaReal,alphaImag) := Matrices.rsf2(ss.A);
          ZT := transpose(Z);

          // reorder real Schur form according to alpha
          (A_rsf,Z,alphaReal,alphaImag) := Matrices.Internal.reorderRSF2(
              A_rsf,
              identity(size(A_rsf, 1)),
              alphaReal,
              alphaImag,
              alpha);
          ZT := transpose(Z)*ZT;
          B_rsf := ZT*ss.B;

          // determine number of poles not to be assigned according to alpha
          nfp := 0;
          for i in 1:n loop
            if alphaReal[i] < alpha then
              nfp := nfp + 1;
            end if;
          end for;
          nap := n - nfp;

          assert(size(gamma, 1) >= nap, String(nap) +
            " poles should be modified, therefore gamma should contain at at least "
             + String(nap) + " assigned eigenvalues");

          // second reorder (reorderRSF3) according to conjugated complex pairs in A and p
          // count numbre of conjugated complex pole pairs = max(number_ccpp(eig(A), number_ccpp(gamma))
          nccA := 0;
          //mark the real poles of original system
          rselectA := fill(true, nap);
          ii := 1;
          for i in nfp + 1:n loop
            if abs(alphaImag[i]) > 0 then
              nccA := nccA + 1;
            else
              rselectA[ii] := false;

            end if;
            ii := ii + 1;
            end for;
            rpA := n-nccA;
            nccA := div(nccA, 2);

          // reorder gamma and A_rsf
          (gammaReordered,rpg) := Modelica_LinearSystems2.Internal.reorderZeros(gamma);
          gammaReordered := Complex.Vectors.reverse(gammaReordered);
          nccg := div(size(gammaReordered, 1) - rpg, 2);
          ncc := min(nccA, nccg);
          rp := min(rpA, rpg);
          if nccA > 0 then
            (A_rsf[nfp + 1:n, nfp + 1:n],Q2) := Matrices.LAPACK.dtrsen(
                "E",
                "V",
                rselectA,
                A_rsf[nfp + 1:n, nfp + 1:n],
                identity(n - nfp));//The Schur vector matrix is identity, since A_rsf already has Schur form

            A_rsf[1:nfp, nfp + 1:n] := A_rsf[1:nfp, nfp + 1:n]*Q2;
            B_rsf[nfp + 1:n, :] := transpose(Q2)*B_rsf[nfp + 1:n, :];
            ZT[nfp + 1:n, :] := transpose(Q2)*ZT[nfp + 1:n, :];
          end if;

          // main algorithm
          K := zeros(size(ss.B, 2), size(ss.A, 1));
          counter := nfp + 1;
          counter2 := 1;

            for i in 1:rp loop // 1x1 blocks; real system pole and real assigned poles; take the next eigenvalue in the
                               // diagonal of the Schur form and search the nearest pole in the set of the real poles to assign
              dist := Modelica.Constants.inf;
              for ii in i:rpg loop // looking for nearest pole and reorder gamma
                if abs(A_rsf[n, n] - gammaReordered[ng - ii + 1].re) < dist then
                  iii := ng - ii + 1;
                  dist := abs(A_rsf[n, n] - gammaReordered[ng - ii + 1].re);
                end if;
              end for;
              h := gammaReordered[ng - i + 1];
              gammaReordered[ng - i + 1] := gammaReordered[iii];
              gammaReordered[iii] := h;

              Ks1 := StateSpace.Internal.assignOneOrTwoPoles(
                matrix(A_rsf[n, n]),
                transpose(matrix(B_rsf[n, :])),
                {gammaReordered[ng - i + 1]},
                tolerance);
              K := K + [zeros(size(Ks1, 1), size(K, 2) - 1),Ks1]*ZT;
              A_rsf := A_rsf - B_rsf*[zeros(size(Ks1, 1), size(K, 2) - 1),Ks1];
              select := fill(false, n - counter + 1);
              select[n - counter + 1] := true;

              (A_rsf[counter:n, counter:n],Q1) := Matrices.LAPACK.dtrsen(
                "E",
                "V",
                select,
                A_rsf[counter:n, counter:n],
                identity(n - counter + 1));//The Schur vector matrix is identity, since A_rsf already has Schur form

              A_rsf[1:counter - 1, counter:n] := A_rsf[1:counter - 1, counter:n]*Q1;
              B_rsf[counter:n, :] := transpose(Q1)*B_rsf[counter:n, :];
              ZT[counter:n, :] := transpose(Q1)*ZT[counter:n, :];
              counter := counter + 1;
              counter2 := counter2 + 1;
            end for;

            if counter2<rpg and counter2>rpA then //System has less real eigenvalues than real assigned poles
            for i in 1:div(rpg - rpA, 2) loop // 2x2 blocks; complex pair of system poles and 2 real assigned poles; take the next complex pair
                                              // (Schur bump) in the diagonal of the Schur form and search the two nearest poles in the set of the
                                              // remaining real assigned poles
              dist := Modelica.Constants.inf;
              evImag := sqrt(-A_rsf[n - 1, n]*A_rsf[n, n - 1]);//positive imaginary part of the complex system pole pair
              for ii in 2*(i - 1) + 1:2:rpg - rpA loop
                if abs(A_rsf[n, n] - gammaReordered[ng - rp - ii + 1].re) + evImag < dist then
                  iii := ng - rp - ii + 1;
                  dist := abs(A_rsf[n, n] - gammaReordered[ng - rp - ii + 1].re) + evImag;
                end if;
              end for;
              h := gammaReordered[ng - rp - 2*(i - 1)];
              gammaReordered[ng - rp - 2*(i - 1)] := gammaReordered[iii];
              gammaReordered[iii] := h;
              dist := Modelica.Constants.inf;
              for ii in 2*(i - 1) + 1:2:rpg - rpA loop
                if abs(A_rsf[n, n] - gammaReordered[ng - rp - ii + 1].re) + evImag < dist then
                  iii := ng - rp - ii + 1;
                  dist := abs(A_rsf[n, n] - gammaReordered[ng - rp - ii + 1].re) + evImag;
                end if;
              end for;
              h := gammaReordered[ng - rp - 2*i + 1];
              gammaReordered[ng - rp - 2*i + 1] := gammaReordered[iii];
              gammaReordered[iii] := h;

              Ks2 := StateSpace.Internal.assignOneOrTwoPoles(
                A_rsf[n - 1:n, n - 1:n],
                matrix(B_rsf[n - 1:n, :]),
                gammaReordered[ng - rp - 2*i + 1:ng - rp - 2*(i - 1)],
                tolerance);

              K := K + [zeros(size(Ks2, 1), size(K, 2) - 2),Ks2]*ZT;
              A_rsf := A_rsf - B_rsf*[zeros(size(Ks2, 1), size(K, 2) - 2),Ks2];
              select := fill(false, n - counter + 1);
              select[n - counter:n - counter + 1] := {true,true};

              (A_rsf[counter:n, counter:n],Q2) := Matrices.LAPACK.dtrsen(
                "E",
                "V",
                select,
                A_rsf[counter:n, counter:n],
                identity(n - counter + 1)); //The Schur vector matrix is identity, since A_rsf already has Schur form

              A_rsf[1:counter - 1, counter:n] := A_rsf[1:counter - 1, counter:n]*Q2;
              B_rsf[counter:n, :] := transpose(Q2)*B_rsf[counter:n, :];
              ZT[counter:n, :] := transpose(Q2)*ZT[counter:n, :];
              counter := counter + 2;
              counter2 := counter2 + 2;
            end for;
          end if;

          if counter2>rpg and counter2<rpA then//System has more real eigenvalues than real assigned poles
            for i in 1:div(rpA - rpg, 2) loop// 2x2 blocks; 2 real system poles and a pair of complex assigned poles; take the next two real
                                              // eigenvalues in the diagonal of the Schur form and search the complex pole pair of the assigned poles
                                              // which is nearest to the two real poles
              dist := Modelica.Constants.inf;
              for ii in 2*(i - 1)+1:2:rpA - rpg loop
                if abs(A_rsf[n, n] - gammaReordered[ng - rp - ii + 1].re) + abs(gammaReordered[ng - rp - ii + 1].im) + abs(A_rsf[n - 1, n - 1] -
                  gammaReordered[ng - rp - ii + 1].re) + abs(gammaReordered[ng - rp - ii + 1].re) < dist then
                  iii := ng - rp - ii + 1;
                  dist := abs(A_rsf[n, n] - gammaReordered[ng - rp - ii + 1].re)
                     + abs(gammaReordered[ng - rp - ii + 1].im) + abs(A_rsf[
                    n - 1, n - 1] - gammaReordered[ng - rp - ii + 1].re) +
                    abs(gammaReordered[ng - rp - ii + 1].re);
                end if;
              end for;
              h := gammaReordered[ng - rp - 2*(i - 1)];
              gammaReordered[ng - rp - 2*(i - 1)] := gammaReordered[iii];
              gammaReordered[iii] := h;
              h := gammaReordered[ng - rp - 2*i + 1];
              gammaReordered[ng - rp - 2*i + 1] := gammaReordered[iii - 1];
              gammaReordered[iii - 1] := h;

              Ks2 := StateSpace.Internal.assignOneOrTwoPoles(
                A_rsf[n - 1:n, n - 1:n],
                matrix(B_rsf[n - 1:n, :]),
                gammaReordered[ng - rp - 2*i + 1:ng - rp - 2*(i - 1)],
                tolerance);

              K := K + [zeros(size(Ks2, 1), size(K, 2) - 2),Ks2]*ZT;
              A_rsf := A_rsf - B_rsf*[zeros(size(Ks2, 1), size(K, 2) - 2),Ks2];
              select := fill(false, n - counter + 1);
              select[n - counter:n - counter + 1] := {true,true};

              (A_rsf[counter:n, counter:n],Q2) := Matrices.LAPACK.dtrsen(
                "E",
                "V",
                select,
                A_rsf[counter:n, counter:n],
                identity(n - counter + 1)); //The Schur vector matrix is identity, since A_rsf already has Schur form

              A_rsf[1:counter - 1, counter:n] := A_rsf[1:counter - 1, counter:n]*Q2;
              B_rsf[counter:n, :] := transpose(Q2)*B_rsf[counter:n, :];
              ZT[counter:n, :] := transpose(Q2)*ZT[counter:n, :];
              counter := counter + 2;
              counter2 := counter2 + 2;
              Modelica.Utilities.Streams.print("counter2Case3 = " + String(counter2));
            end for;
          end if;
        //      else

          for i in 1:ncc loop // 2x2 blocks; 2 complex system poles and two complex assigned poles; take the next complex
                              // system pole pair (next Schur bump) in the diagonal of the Schur form and search the complex
                              //  assigned pole pair which is nearest
            dist := Modelica.Constants.inf;
            evImag := sqrt(-A_rsf[n - 1, n]*A_rsf[n, n - 1]);//positive imaginary part of the complex system pole pair
            for ii in 2*(i - 1) + 1:2:2*ncc loop
              if abs(A_rsf[n, n] - gammaReordered[2*ncc - ii + 1].re) + abs(evImag -
                  abs(gammaReordered[2*ncc - ii + 1].im)) < dist then
                iii := 2*ncc - ii + 1;
                dist := abs(A_rsf[n, n] - gammaReordered[2*ncc - ii + 1].re) + abs(
                  evImag - abs(gammaReordered[2*ncc - ii + 1].im));
              end if;
            end for;
            h := gammaReordered[2*ncc - 2*(i - 1)];
            gammaReordered[2*ncc - 2*(i - 1)] := gammaReordered[iii];
            gammaReordered[iii] := h;
            h := gammaReordered[2*ncc - 2*i + 1];
            gammaReordered[2*ncc - 2*i + 1] := gammaReordered[iii - 1];
            gammaReordered[iii - 1] := h;
            Ks2 := StateSpace.Internal.assignOneOrTwoPoles(
              A_rsf[n - 1:n, n - 1:n],
              matrix(B_rsf[n - 1:n, :]),
              gammaReordered[2*ncc - 2*i + 1:2*ncc - 2*(i - 1)],
              tolerance);
            K := K + [zeros(size(Ks2, 1), size(K, 2) - 2),Ks2]*ZT;
            A_rsf := A_rsf - B_rsf*[zeros(size(Ks2, 1), size(K, 2) - 2),Ks2];
            select := fill(false, n - counter + 1);
            select[n - counter:n - counter + 1] := {true,true};

            (A_rsf[counter:n, counter:n],Q2) := Matrices.LAPACK.dtrsen(
              "E",
              "V",
              select,
              A_rsf[counter:n, counter:n],
              identity(n - counter + 1));   //The Schur vector matrix is identity, since A_rsf already has Schur form

            A_rsf[1:counter - 1, counter:n] := A_rsf[1:counter - 1, counter:n]*Q2;
            B_rsf[counter:n, :] := transpose(Q2)*B_rsf[counter:n, :];
            ZT[counter:n, :] := transpose(Q2)*ZT[counter:n, :];
            counter := counter + 2;
            counter2 := counter2 + 2;
          end for;

          S := ss.A - ss.B*K;
          po := Complex.eigenValues(S);

          if calculateEigenvectors then
        //     X := fill(Complex(0), n, n);
        //     for i in 1:n loop
        //       SS := Complex(1)*S;
        //       for ii in 1:n loop
        //         SS[ii, ii] := SS[ii, ii] - po[i];
        //       end for;
        //       Xj := Modelica_LinearSystems2.WorkInProgress.Math.Matrices.C_nullspace(
        //                                  SS);
        //       for ii in 1:n loop
        //         X[ii, i] := Xj[ii, 1];
        //       end for;
        //     end for;
        //      Modelica_LinearSystems2.Math.Complex.Matrices.print(X,6,"X1");
            X := Complex.eigenVectors(S);
        //      Modelica_LinearSystems2.Math.Complex.Matrices.print(X,6,"X2");

          end if;

        end assignPolesMI;

      function kalmanFilter "Design of a Kalman estimator matrix"
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss "Time-continuous system in state space form";
        input Real Q[size(ss.A, 1),size(ss.A, 1)]
            "Covariance Matrix of state noise (n x n), n number of states";
        input Real R[size(ss.B, 2),size(ss.B, 2)]
            "Covariance Matrix of input noise (m x m), m number of inputs";

        public
        output Real L[:,:] "Kalman filter matrix";
        output StateSpace kss(
          redeclare Real A[size(ss.A, 1),size(ss.A, 1)],
          redeclare Real B[size(ss.B, 1),size(ss.B, 2) + size(ss.C, 1)],
          redeclare Real C[size(ss.A, 1),size(ss.A, 2)],
          redeclare Real D[size(ss.A, 1),size(ss.B, 2) + size(ss.C, 1)])
            "kalman system";

        protected
        Real AR[:,:]=transpose(ss.A);
        Real BR[:,:]=transpose(ss.C);
        Real CR[:,:]=zeros(1, size(AR, 1));
        Real DR[:,:]=zeros(1, size(BR, 2));
        StateSpace rss=StateSpace(AR, BR, CR, DR)
            "System to calculate the Kalman estimator with lqr algorithm";

        Real AK[size(ss.A, 1),size(ss.A, 1)];
        Real BK[size(ss.B, 1),size(ss.B, 2) + size(ss.C, 1)];
        Real CK[size(ss.A, 1),size(ss.A, 2)];
        Real DK[size(ss.A, 1),size(ss.B, 2) + size(ss.C, 1)];// matrices of the kalman system kss

      algorithm
        (L) := StateSpace.Design.lqr(rss, Q, R, true);
        L := transpose(L);

        AK := ss.A - L*ss.C;
        BK[:, 1:size(ss.B, 2)] := ss.B - L*ss.D;
        BK[:, size(ss.B, 2) + 1:size(BK, 2)] := L;
        CK := identity(size(ss.A, 1));
        DK := zeros(size(ss.A, 1),size(ss.B, 2) + size(ss.C, 1));

        kss := StateSpace(AK, BK, CK, DK);

      end kalmanFilter;

        encapsulated function lqr "LQR design algorithm"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math;

          input StateSpace ss "open loop system";
          input Real Q[size(ss.A, 1),size(ss.A, 2)]=identity(size(ss.A, 1))
            " state weighting matrix";
          input Real R[size(ss.B, 2),size(ss.B, 2)]=identity(size(ss.B, 2))
            " input weighting matrix";
        protected
          input Boolean iscontinuousSystem=true;
        public
          output Real K[size(ss.B, 2),size(ss.A, 1)] "Feedback gain matrix";
          output StateSpace sslqr(
            redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
            redeclare Real B[size(ss.B, 1),size(ss.B, 2)],
            redeclare Real C[size(ss.C, 1),size(ss.C, 2)],
            redeclare Real D[size(ss.D, 1),size(ss.D, 2)]) "closed loop system";

          output Real S[size(ss.A, 1),size(ss.A, 1)]
            "solution of the Riccati equation";

          output Math.Complex ev[:];

        algorithm
          if min(size(ss.A,1),size(ss.A,2))>0 then
          assert(StateSpace.Analysis.isControllable(ss),"System in function \"Modelica_LinearSystems2.StateSpace.Design.lqr\" has to be controllable");
          if iscontinuousSystem then
            (S,ev) := Math.Matrices.care(
                ss.A,
                ss.B,
                R,
                Q);
            K := Math.Matrices.solve2(R, transpose(ss.B)*S);
          else
            (S,ev) := Math.Matrices.dare(
                ss.A,
                ss.B,
                R,
                Q);
            K := Math.Matrices.solve2(R + transpose(ss.B)*S*ss.B, transpose(ss.B)*S*
              ss.A);
          end if;

          sslqr.A := ss.A - ss.B*K;
          sslqr.B := ss.B;
          sslqr.C := ss.C - ss.D*K;
          sslqr.D := ss.D;

          else
            K := fill(
              0,
              size(ss.B, 2),
              size(ss.A, 1));
            ev := fill(Modelica_LinearSystems2.Math.Complex(0), 0);
          end if;

        end lqr;

        encapsulated function lqg "LQG design algorithm"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math;

          input StateSpace ss "open loop system";
          input Real Q[size(ss.A, 1),size(ss.A, 2)]=identity(size(ss.A, 1))
            " state weighting matrix";
          input Real R[size(ss.B, 2),size(ss.B, 2)]=identity(size(ss.B, 2))
            " input weighting matrix";
          input Real V[size(ss.C, 1),size(ss.C, 1)]=identity(size(ss.C, 1))
            " covariance output noise matrix";
          input Real W[size(ss.A, 1),size(ss.A, 1)]=identity(size(ss.A, 1))
            " covariance input noise matrix";

          input Boolean iscontinuousSystem=true;

          output Real Kc[size(ss.B, 2),size(ss.A, 1)]
            "Controller feedback gain matrix";
          output Real Kf[size(ss.A, 1),size(ss.C, 1)] "Kalman feedback gain matrix";

          output StateSpace sslqg(
            redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
            redeclare Real B[size(ss.B, 1),size(ss.B, 2)],
            redeclare Real C[size(ss.C, 1),size(ss.C, 2)],
            redeclare Real D[size(ss.D, 1),size(ss.D, 2)]) "closed loop system";

        protected
         Real AR[:,:]=transpose(ss.A);
         Real BR[:,:]=transpose(ss.C);
         Real CR[:,:]=zeros(1, size(AR, 1));
         Real DR[:,:]=zeros(1, size(BR, 2));  // System for lqr
         StateSpace rss=StateSpace(
             AR,
             BR,
             CR,
             DR);
          Real Sc[size(ss.A, 1),size(ss.A, 1)]
            "solution of the Riccati equation, controller";
          Real Sf[size(ss.A, 1),size(ss.A, 1)]
            "solution of the Riccati equation, filter";

        algorithm
          if min(size(ss.A,1),size(ss.A,2))>0 then
          assert(StateSpace.Analysis.isControllable(ss),"System in function \"Modelica_LinearSystems2.StateSpace.Design.lqg\" has to be controllable");

          if iscontinuousSystem then
            (Sc,) := Math.Matrices.care(
                ss.A,
                ss.B,
                R,
                Q);
            Kc := Math.Matrices.solve2(R, transpose(ss.B)*Sc);
          else
            (Sc,) := Math.Matrices.dare(
                ss.A,
                ss.B,
                R,
                Q);
            Kc := Math.Matrices.solve2(R + transpose(ss.B)*Sc*ss.B, transpose(ss.B)*Sc*
              ss.A);
          end if;

          assert(StateSpace.Analysis.isObservable(ss),"System in function \"Modelica_LinearSystems2.StateSpace.Design.lqg\" has to be observable");
         if iscontinuousSystem then
            (Sf,) := Math.Matrices.care(
                rss.A,
                rss.B,
                V,
                W);
            Kf := transpose(Math.Matrices.solve2(V, ss.C*Sf));
          else
            (Sf,) := Math.Matrices.dare(
                rss.A,
                rss.B,
                V,
                W);
            Kf := transpose(Math.Matrices.solve2(V + rss.C*Sf*rss.B, rss.C*Sf*rss.A));
          end if;

          else
            Kc := fill(
              0,
              size(ss.B, 2),
              size(ss.A, 1));
            Kf := fill(
              0,
              size(ss.C, 1),
              size(ss.A, 1));

          end if;

          sslqg.A := ss.A - Kf*ss.C - ss.B*Kc + Kf*ss.D*Kc;
          sslqg.B := Kf;
          sslqg.C := ss.C - ss.D*Kc;
          sslqg.D := zeros(size(ss.D,1),size(ss.D,2));

        end lqg;

      end Design;

    encapsulated package Plot
        "Functions to plot state space system responses"

        encapsulated function polesAndZeros
          "Plot poles (i.e. eigenvalues) and/or invariant zeros of a state space system"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Utilities.Plot;

          input StateSpace ss "Linear system in state space form";
          input Boolean poles=true
            "= true, to plot the poles (i.e. the eigenvalues) of ss";
          input Boolean zeros=true "= true, to plot the (invariant) zeros of ss ";

          extends Modelica_LinearSystems2.Internal.PartialPlotFunction(
             defaultDiagram = Modelica_LinearSystems2.Internal.DefaultDiagramPolesAndZeros());

        protected
          Integer nx=size(ss.A, 1);
          Real eval[nx,2];
          Real invZerosRe[:];
          Real invZerosIm[:];
          Complex invZeros[:];
          Plot.Records.Curve curves[2];
          Integer i;
          Plot.Records.Diagram diagram2;
        algorithm
          // Determine eigen values
          if poles then
            eval := Modelica.Math.Matrices.eigenValues(ss.A);
          end if;

          if zeros then
            invZeros := StateSpace.Analysis.invariantZeros(ss);
            invZerosRe := fill(0,size(invZeros,1));
            invZerosIm := fill(0,size(invZeros,1));
            for i in 1:size(invZeros, 1) loop
              invZerosRe[i] := invZeros[i].re;
              invZerosIm[i] := invZeros[i].im;
            end for;
          end if;

          i :=0;
          if poles then
             i :=i + 1;
             curves[i] :=Plot.Records.Curve(
                                x=eval[:, 1],
                                y=eval[:, 2],
                                legend="poles",
                                autoLine=false,
                                linePattern=Plot.Types.LinePattern.None,
                                lineSymbol=Plot.Types.PointSymbol.Cross);
          end if;

          if zeros then
             i :=i + 1;
             curves[i] :=Plot.Records.Curve(
                                x=invZerosRe,
                                y=invZerosIm,
                                legend="zeros",
                                autoLine=false,
                                linePattern=Plot.Types.LinePattern.None,
                                lineSymbol=Plot.Types.PointSymbol.Circle);
          end if;

             diagram2 :=defaultDiagram;
             diagram2.curve :=curves[1:i];
             Plot.diagram(diagram2,device);

        end polesAndZeros;

        encapsulated function bodeSISO
          "Plot bode plot of the corresponding transfer function"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input StateSpace ss "state space system";
          input Integer iu=1 "index of input";
          input Integer iy=1 "index of output";
          input Integer nPoints(min=2) = 200 "Number of points";
          input Boolean autoRange=true
            "= true, if abszissa range is automatically determined";
          input Modelica.SIunits.Frequency f_min=0.1
            "Minimum frequency value, if autoRange = false";
          input Modelica.SIunits.Frequency f_max=10
            "Maximum frequency value, if autoRange = false";

          input Boolean magnitude=true "= true, to plot the magnitude of tf";
          input Boolean phase=true "= true, to plot the pase of tf";

          extends Modelica_LinearSystems2.Internal.PartialPlotFunction(defaultDiagram=
                Modelica_LinearSystems2.Internal.DefaultDiagramBodePlot());

        protected
          ZerosAndPoles zp "ZP-Transfer functions to be plotted";
          StateSpace ss_siso(
            redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
            redeclare Real B[size(ss.B, 1),1],
            redeclare Real C[1,size(ss.C, 2)],
            redeclare Real D[1,1]);

        algorithm
          assert(iu <= size(ss.B, 2) and iu > 0, "index for input is " + String(iu) + " which is not in [1, "
             + String(size(ss.B, 2)) + "].");
          assert(iy <= size(ss.C, 1) and iy > 0, "index for output is " + String(iy) + " which is not in [1, "
             + String(size(ss.C, 1)) + "].");
          ss_siso := StateSpace(
            A=ss.A,
            B=matrix(ss.B[:, iu]),
            C=transpose(matrix(ss.C[iy, :])),
            D=matrix(ss.D[iy, iu]));
          zp := StateSpace.Conversion.toZerosAndPoles(ss_siso);

          ZerosAndPoles.Plot.bode(
            zp,
            nPoints,
            autoRange,
            f_min,
            f_max,
            magnitude,
            phase,
            defaultDiagram=defaultDiagram,
            device=device);

        end bodeSISO;

        encapsulated function bodeMIMO
          "Plot bode plot of all transfer functions, corresponding to the state space system"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Utilities.Plot;

          input StateSpace ss;
          input Integer nPoints(min=2) = 200 "Number of points";
          input Boolean autoRange[:,:]=fill(
              true,
              size(ss.C, 1),
              size(ss.B, 2))
            "= true, if abszissa range is automatically determined";
          input Modelica.SIunits.Frequency f_min[:,:]=fill(
              0.1,
              size(ss.C, 1),
              size(ss.B, 2)) "Minimum frequency value, if autoRange = false";
          input Modelica.SIunits.Frequency f_max[:,:]=fill(
              10,
              size(ss.C, 1),
              size(ss.B, 2)) "Maximum frequency value, if autoRange = false";
          input Boolean magnitude=true "= true, to plot the magnitude of tf";
          input Boolean phase=true "= true, to plot the pase of tf";

          extends Modelica_LinearSystems2.Internal.PartialPlotFunction(defaultDiagram=
                Modelica_LinearSystems2.Internal.DefaultDiagramBodePlot());

        protected
          TransferFunction tf[size(ss.C, 1),size(ss.B, 2)]
            "Transfer functions to be plotted";
          Plot.Records.Diagram diagram2=defaultDiagram;
          String yNames[size(ss.C, 1)];
          String uNames[size(ss.B, 2)];

        algorithm
         // generate headings
          for i1 in 1:size(ss.B, 2) loop
            uNames[i1] := if ss.uNames[i1] == "" then "u" + String(i1) else ss.uNames[i1];
          end for;
          for i1 in 1:size(ss.C, 1) loop
            yNames[i1] := if ss.yNames[i1] == "" then "y" + String(i1) else ss.yNames[i1];
          end for;

          tf := StateSpace.Conversion.toTransferFunctionMIMO(ss);

          for i1 in 1:size(ss.C, 1) loop
            for i2 in 1:size(ss.B, 2) loop
              diagram2.heading := defaultDiagram.heading +"  "+ uNames[i2] + " -> " + yNames[i1];
              TransferFunction.Plot.bode(
                tf[i1, i2],
                nPoints,
                autoRange[i1, i2],
                f_min[i1, i2],
                f_max[i1, i2],
                magnitude,
                phase,
                defaultDiagram=diagram2,
                device=device);
            end for;
          end for;

        end bodeMIMO;

        encapsulated function timeResponse
          "Plot the time response of the system. The response type is selectable"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

          input StateSpace ss;
          input Real dt=0 "Sample time [s]";
          input Real tSpan=0 "Simulation time span [s]";

          input Modelica_LinearSystems2.Types.TimeResponse response=
              Modelica_LinearSystems2.Types.TimeResponse.Step;

          input Real x0[size(ss.A, 1)]=zeros(size(ss.A, 1)) "Initial state vector";

          input Boolean subPlots=true
            "true if all subsystem time responses are plotted in one window with subplots";

          extends Modelica_LinearSystems2.Internal.PartialPlotFunctionMIMO(defaultDiagram=Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(
                heading="Time response"));

        protected
          Plot.Records.Curve curve;
          Integer i1;
          Integer i2;
          Plot.Records.Diagram diagram2[size(ss.C, 1)];

          Real y[:,size(ss.C, 1),if response == TimeResponse.Initial then 1 else size(ss.B,2)]
            "Output response: (number of samples) x (number of outputs) x (number of inputs)";
          Real t[:] "Time vector: (number of samples)";
          Real x[:,size(ss.A, 1),if response == TimeResponse.Initial then 1 else size(ss.B,2)]
            "State trajectories: (number of samples) x (number of states) x (number of inputs)";
          String yNames[size(ss.C, 1)];
          String uNames[size(ss.B, 2)];
          Integer loops=if response == TimeResponse.Initial then 1 else size(ss.B,2);

        algorithm
          (y,t,x) := Modelica_LinearSystems2.StateSpace.Analysis.timeResponse(
            sc=ss,
            dt=dt,
            tSpan=tSpan,
            response=response,
            x0=x0);

        // generate headings
          for i1 in 1:size(ss.B, 2) loop
            uNames[i1] := if ss.uNames[i1] == "" then "u" + String(i1) else ss.uNames[
              i1];
          end for;
          for i1 in 1:size(ss.C, 1) loop
            yNames[i1] := if ss.yNames[i1] == "" then "y" + String(i1) else ss.yNames[
              i1];
          end for;

          for i2 in 1:loops loop
            for i1 in 1:size(ss.C, 1) loop
              curve := Plot.Records.Curve(
                x=t,
                y=y[:, i1, i2],
                autoLine=true);

              diagram2[i1] := defaultDiagram;
              diagram2[i1].curve := {curve};
              diagram2[i1].heading := if response == TimeResponse.Initial then defaultDiagram.heading +" "+ yNames[i1] else defaultDiagram.heading + "  " + uNames[i2] + " -> " + yNames[i1];
              diagram2[i1].yLabel := yNames[i1];

            end for;

            if subPlots then
              Plot.diagramVector(diagram2, device);
            else
              for i1 in 1:size(ss.C, 1) loop
                Plot.diagram(diagram2[i1], device);
              end for;
            end if;
          end for;

        end timeResponse;

        encapsulated function impulse "Impulse response plot"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

          input StateSpace ss;
          input Real dt=0 "Sample time [s]";
          input Real tSpan=0 "Simulation time span [s]";
          input Real x0[size(ss.A, 1)]=zeros(size(ss.A, 1)) "Initial state vector";

          input Boolean subPlots=true
            "true if all subsystem time responses are plotted in one window with subplots";

          extends Modelica_LinearSystems2.Internal.PartialPlotFunctionMIMO(defaultDiagram=Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(heading="Impulse response"));

        protected
          input Modelica_LinearSystems2.Types.TimeResponse response=Modelica_LinearSystems2.Types.TimeResponse.Impulse
            "type of time response";
        algorithm

          Modelica_LinearSystems2.StateSpace.Plot.timeResponse(
            ss=ss,
            dt=dt,
            tSpan=tSpan,
            response=response,
            x0=x0,
            defaultDiagram=defaultDiagram,
            device=device);

        end impulse;

        encapsulated function step "Step response plot"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

          input StateSpace ss;
          input Real dt=0 "Sample time [s]";
          input Real tSpan=0 "Simulation time span [s]";
          input Real x0[size(ss.A, 1)]=zeros(size(ss.A, 1)) "Initial state vector";

          input Boolean subPlots=true
            "true if all subsystem time responses are plotted in one window with subplots";

          extends Modelica_LinearSystems2.Internal.PartialPlotFunctionMIMO(defaultDiagram=Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(heading="Step response"));

          input Modelica_LinearSystems2.Types.TimeResponse response=
              Modelica_LinearSystems2.Types.TimeResponse.Step
            "type of time response";

        algorithm
          Modelica_LinearSystems2.StateSpace.Plot.timeResponse(
            ss=ss,
            dt=dt,
            tSpan=tSpan,
            response=response,
            x0=x0,
            defaultDiagram=defaultDiagram,
            device=device);

        end step;

        encapsulated function ramp "Ramp response plot"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

        input StateSpace ss;
        input Real dt=0 "Sample time [s]";
        input Real tSpan=0 "Simulation time span [s]";
        input Real x0[size(ss.A, 1)]=zeros(size(ss.A, 1)) "Initial state vector";

        input Boolean subPlots=true
            "true if all subsystem time responses are plotted in one window with subplots";

        extends Modelica_LinearSystems2.Internal.PartialPlotFunctionMIMO(defaultDiagram=Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(
              heading="Ramp response"));

        input Modelica_LinearSystems2.Types.TimeResponse response=
            Modelica_LinearSystems2.Types.TimeResponse.Ramp "type of time response";

        algorithm
        Modelica_LinearSystems2.StateSpace.Plot.timeResponse(
              ss=ss,
              dt=dt,
              tSpan=tSpan,
              response=response,
              x0=x0,
              defaultDiagram=defaultDiagram,
              device=device);

        end ramp;

        encapsulated function initialResponse
          "Initial condition response plot"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

          input StateSpace ss;
          input Real dt=0 "Sample time [s]";
          input Real tSpan=0 "Simulation time span [s]";
          input Real x0[size(ss.A, 1)]=zeros(size(ss.A, 1)) "Initial state vector";

          input Boolean subPlots=true
            "true if all subsystem time responses are plotted in one window with subplots";

          extends Modelica_LinearSystems2.Internal.PartialPlotFunctionMIMO(defaultDiagram=Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(
                heading="Initial response"));

          input Modelica_LinearSystems2.Types.TimeResponse response=
              Modelica_LinearSystems2.Types.TimeResponse.Initial
            "type of time response";
        algorithm

          Modelica_LinearSystems2.StateSpace.Plot.timeResponse(
            ss=ss,
            dt=dt,
            tSpan=tSpan,
            response=response,
            x0=x0,
            defaultDiagram=defaultDiagram,
            device=device);

        end initialResponse;

    end Plot;

    encapsulated package Conversion
        "Conversion functions from StateSpace into TransferFunction or ZerosAndPoles representations"

      encapsulated function toZerosAndPoles
          "Generate a zeros-and-poles representation from a SISO state space representation"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss "StateSpace object";
        protected
        input Boolean cancel=true "false to hinder cancellation";
        public
        output ZerosAndPoles zp;

        protected
        StateSpace ssm=StateSpace.Transformation.toIrreducibleForm(ss);
        Complex poles[:];
        Complex zeros[:];

        Real gain;
        Complex frequency;
        Complex Gs;
        Real As[:,:];
        Real pk;
        Integer i;
        Integer k;
        Boolean h;

      algorithm
        if Modelica.Math.Vectors.length(ssm.B[:, 1]) > 0 and
            Modelica.Math.Vectors.length(ssm.C[1, :]) > 0 then

          poles := Complex.Internal.eigenValues_dhseqr(ssm.A);//ssm.A is of upper Hessenberg form
          zeros := if cancel then StateSpace.Internal.invariantZeros2(ssm) else
            StateSpace.Analysis.zerosAndPoles(ss);

          if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
            assert(size(ss.B, 2) == 1, " function fromStateSpaceSISO expects a SISO-system as input\n but the number of inputs is "
               + String(size(ss.B, 2)) + " instead of 1");
            assert(size(ss.C, 1) == 1, " function fromStateSpaceSISO expects a SISO-system as input\n but the number of outputs is "
               + String(size(ss.C, 1)) + " instead of 1");
          end if;

          zp := ZerosAndPoles(
              z=zeros,
              p=poles,
              k=1);
      // set frequency to a complex value which is whether pole nor zero
          frequency := Complex(2*abs(max(cat(
              1,
              zeros[:].re,
              poles[:].re)) + 1));
          Gs := ZerosAndPoles.Analysis.evaluate(zp, frequency);

          As := -ssm.A;
          for i in 1:size(As, 1) loop
            As[i, i] := As[i, i] + frequency.re;
          end for;

          pk := StateSpace.Internal.partialGain(As, ssm.B[:, 1]);
          gain := (ssm.C[1, size(As, 1)]*pk + ss.D[1, 1])/Gs.re;

          zp := ZerosAndPoles(
              z=zeros,
              p=poles,
              k=gain);

        else
          zp := ZerosAndPoles(
              z=fill(Complex(0), 0),
              p=fill(Complex(0), 0),
              k=0);

        end if;
        zp.uName := ss.uNames[1];
        zp.yName := ss.yNames[1];

      end toZerosAndPoles;

      function toTransferFunction
          "Generate a transfer function from a SISO state space representation"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;

        input StateSpace ss "StateSpace object";

        output TransferFunction tf;

        protected
        ZerosAndPoles zp;

      algorithm
        zp := toZerosAndPoles(ss);
        tf := ZerosAndPoles.Conversion.toTransferFunction(zp);

      end toTransferFunction;

    encapsulated function toZerosAndPolesMIMO
          "Generate a zeros-and-poles representation from a MIMO state space representation"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.StateSpace;

      input StateSpace ss "StateSpace object";

      output ZerosAndPoles zp[size(ss.C, 1),size(ss.B, 2)];

        protected
      StateSpace ss_siso(
        redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
        redeclare Real B[size(ss.B, 1),1],
        redeclare Real C[1,size(ss.C, 2)],
        redeclare Real D[1,1]);

      Integer ny=size(ss.C, 1);
      Integer nu=size(ss.B, 2);

    algorithm
      for ic in 1:ny loop
        for ib in 1:nu loop
          ss_siso := StateSpace(
              A=ss.A,
              B=matrix(ss.B[:, ib]),
              C=transpose(matrix(ss.C[ic, :])),
              D=matrix(ss.D[ic, ib]));
              zp[ic, ib] := StateSpace.Conversion.toZerosAndPoles(ss_siso);
         end for;
      end for;
    end toZerosAndPolesMIMO;

    function toTransferFunctionMIMO
          "Generate a transfer function of a MIMO system from state space representation"
          import Modelica_LinearSystems2;

          import Modelica;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;

      input StateSpace ss "StateSpace object";

      output TransferFunction tf[size(ss.C, 1),size(ss.B, 2)]
            "Matrix of transfer function objects";

        protected
      ZerosAndPoles zp[:,:];
      parameter Integer m=size(ss.B, 2);
      parameter Integer p=size(ss.C, 1);

    algorithm
      zp := Modelica_LinearSystems2.StateSpace.Conversion.toZerosAndPolesMIMO(ss);
      for i1 in 1:m loop
        for i2 in 1:p loop
          tf[i2, i1] := ZerosAndPoles.Conversion.toTransferFunction(zp[i2, i1]);
        end for;
      end for;

    end toTransferFunctionMIMO;

    end Conversion;

      encapsulated package Transformation
        "State Space similarity transformations"

          encapsulated function toSimilarForm
          "Perform the similarity transformation z = Tx (or x = inv(T)z) which leads to Az=T*A*inv(T), Bz=T*B, Cz=C*inv(T), Dz=D (or Az=inv(T)*A*T, Bz=inv(T)B, Cz=C*T, Dz=D)"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica.Math.Matrices;
          import Modelica_LinearSystems2.Math.Matrices.LAPACK;

            input StateSpace ss "state space system";
            input Real T[size(ss.A, 2),size(ss.A, 1)] = identity(size(ss.A,1))
            "Transformation matrix";
            input Boolean inverted=false
            "false (default) for transformation z = Tx, true for x = Tz";

            output StateSpace tss(
              redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
              redeclare Real B[size(ss.B, 1),size(ss.B, 2)],
              redeclare Real C[size(ss.C, 1),size(ss.C, 2)],
              redeclare Real D[size(ss.D, 1),size(ss.D, 2)]);

          algorithm
            if inverted then
              tss.A := Matrices.solve2(T, ss.A*T);
              tss.B := Matrices.solve2(T, ss.B);
              tss.C := ss.C*T;
              tss.D := ss.D;
            else
              tss.A := transpose(LAPACK.dgesvx(T, transpose(T*ss.A)));
              tss.B := T*ss.B;
              tss.C := transpose(LAPACK.dgesvx(T, transpose(ss.C)));
              tss.D := ss.D;
            end if;
          end toSimilarForm;

          encapsulated function toObservabilityForm
          "Perform the similarity transformation to the obervabillity canonical form"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Internal.Streams;
          import Modelica_LinearSystems2.Math.Matrices.LAPACK;

            input StateSpace ss "state space system";
            output StateSpace tss(
              redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
              redeclare Real B[size(ss.B, 1),size(ss.B, 2)],
              redeclare Real C[size(ss.C, 1),size(ss.C, 2)],
              redeclare Real D[size(ss.D, 1),size(ss.D, 2)]);

        protected
            Real V[size(ss.A, 1),size(ss.A, 1)]
            "Matrix of the right eigenvectors of the matrix ss.A";

            Integer nx=size(ss.A, 1);

          algorithm
            assert(size(ss.C, 1) == 1 and size(ss.B, 2) == 1,
              "Calculation of controllable form fails for systems with more than 1 inputs or outputs");
            assert(Modelica_LinearSystems2.StateSpace.Analysis.isObservable(ss),
              "transformation ist not realizable since the system ist not obersvable");

            V[:, 1] := Modelica.Math.Matrices.solve(StateSpace.Analysis.observabilityMatrix(ss), vector([
              fill(
                0,
                1,
                nx - 1),1]));

            for i in 2:nx loop
              V[:, i] := ss.A*V[:, i - 1];
            end for;

            tss := StateSpace.Transformation.toSimilarForm(ss, V,inverted=true);

          end toObservabilityForm;

          encapsulated function toControllabilityForm
          "Perform the similarity transformation to the controllability canonical form"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Internal.Streams;
          import Modelica_LinearSystems2.Math.Matrices.LAPACK;

            input StateSpace ss "state space system";
            output StateSpace tss(
              redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
              redeclare Real B[size(ss.B, 1),size(ss.B, 2)],
              redeclare Real C[size(ss.C, 1),size(ss.C, 2)],
              redeclare Real D[size(ss.D, 1),size(ss.D, 2)]);

        protected
            Real V[size(ss.A, 1),size(ss.A, 1)]
            "Matrix of the right eigenvectors of the matrix ss.A";

            Integer nx=size(ss.A, 1);

          algorithm
            assert(size(ss.C, 1) == 1 and size(ss.B, 2) == 1,
              "Calculation of controllable form fails for systems with more than 1 inputs or outputs");
            assert(Modelica_LinearSystems2.StateSpace.Analysis.isControllable(ss),
              "transformation ist not realizable since the system ist not controllable");

            V[1, :] := Modelica.Math.Matrices.solve(transpose(StateSpace.Analysis.controllabilityMatrix(ss)),
              vector([fill(
                0,
                nx - 1,
                1); 1]));

            for i in 2:nx loop
              V[i, :] := V[i - 1, :]*ss.A;
            end for;

            tss := StateSpace.Transformation.toSimilarForm(
                                    ss, V);

          end toControllabilityForm;

          encapsulated function toDiagonalForm
          "Perform the similarity transformation with the (real) inverse right eigenvector matrix of the system, that lead to the Jordan canonical form for single eigenvalues"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Matrices.LAPACK;

            input StateSpace ss "state space system";
            output StateSpace tss(
              redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
              redeclare Real B[size(ss.B, 1),size(ss.B, 2)],
              redeclare Real C[size(ss.C, 1),size(ss.C, 2)],
              redeclare Real D[size(ss.D, 1),size(ss.D, 2)]);

        protected
            Real V[size(ss.A, 1),size(ss.A, 1)]
            "Matrix of the right eigenvectors of the matrix ss.A";

          algorithm
            (,,,V,) := LAPACK.dgeev(ss.A);

            tss := StateSpace.Transformation.toSimilarForm(ss, V, inverted=true);

          end toDiagonalForm;

          encapsulated function toIrreducibleForm
          "Calculate a minimal controllable and observable block Hessenberg realization of a given SISO state-space representation "

           // test of SISO has to be added
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica;

            input StateSpace ss "State space system";

        protected
            Modelica_LinearSystems2.Internal.StateSpaceR ssm1=
                StateSpace.Internal.reducedCtrSystem(ss);
            Integer nx=size(ss.A, 1);
            Integer rankQ=ssm1.r;
            StateSpace ss2=StateSpace(
                  A=transpose(ssm1.A[nx - rankQ + 1:nx, nx - rankQ + 1:nx]),
                  B=transpose(ssm1.C[:, nx - rankQ + 1:nx]),
                  C=transpose(ssm1.B[nx - rankQ + 1:nx, :]),
                  D=ssm1.D);
            Integer nx2=ssm1.r;
            Modelica_LinearSystems2.Internal.StateSpaceR ssm2=
                StateSpace.Internal.reducedCtrSystem(ss2);
            Integer rankQ2=ssm2.r;
        public
            output StateSpace ssm3(
              redeclare Real A[rankQ2,rankQ2],
              redeclare Real B[rankQ2,size(ss.B, 2)],
              redeclare Real C[size(ss.C, 1),rankQ2],
              redeclare Real D[size(ss.D, 1),size(ss.D, 2)]);
          algorithm
            ssm3 := StateSpace(
                A=transpose(ssm2.A[nx2 - rankQ2 + 1:nx2, nx2 - rankQ2 + 1:nx2]),
                B=transpose(ssm2.C[:, nx2 - rankQ2 + 1:nx2]),
                C=transpose(ssm2.B[nx2 - rankQ2 + 1:nx2, :]),
                D=(ssm2.D));

          end toIrreducibleForm;

      encapsulated function extract
          "Extract input/output related subsystems from state space system record"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss "State space system";
        input Integer outputIndex[:]={1} "Vector of subsystem outputs indices";
        input Integer inputIndex[:]={1} "Vector of subsystem inputs indices";
        output StateSpace subSc(
          redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
          redeclare Real B[size(ss.B, 1),size(inputIndex, 1)],
          redeclare Real C[size(outputIndex, 1),size(ss.C, 2)],
          redeclare Real D[size(outputIndex, 1),size(inputIndex, 1)])
            "Subsystem state space record";

      algorithm
        subSc.A := ss.A;
        subSc.B := ss.B[:, inputIndex];
        subSc.C := ss.C[outputIndex, :];
        subSc.D := ss.D[outputIndex, inputIndex];

      end extract;

      end Transformation;

    encapsulated package Import
        "Utilitiy functions to import StaeSpace representations"

      encapsulated function fromFile
          "Read a StateSpace data record from mat-file"

          import Modelica;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace.Internal;
        input String fileName="dslin.mat"
            "Name of the state space system data file";
        input String matrixName="ABCD" "Name of the state space system matrix";
        protected
        input Integer xuy[3]=Internal.readSystemDimension(fileName, matrixName);
        input Integer nx=xuy[1];
        input Integer nu=xuy[2];
        input Integer ny=xuy[3];

        public
        output StateSpace result(
          redeclare Real A[nx,nx],
          redeclare Real B[nx,nu],
          redeclare Real C[ny,nx],
          redeclare Real D[ny,nu]) "= model linearized at initial point";

        protected
        Real ABCD[nx + ny,nx + nu]=Modelica_LinearSystems2.Internal.Streams.readMatrixInternal(
              fileName,
              matrixName,
              nx + ny,
              nx + nu);

      algorithm
        result.A := ABCD[1:nx, 1:nx];
        result.B := ABCD[1:nx, nx + 1:nx + nu];
        result.C := ABCD[nx + 1:nx + ny, 1:nx];
        result.D := ABCD[nx + 1:nx + ny, nx + 1:nx + nu];
        Modelica.Utilities.Streams.print("StateSpace record loaded from file: \""
           + fileName + "\"");

      end fromFile;

      function fromModel
          "Generate a StateSpace data record by linearization of a model"

          import Modelica;
          import Modelica_LinearSystems2.StateSpace;

        input String modelName "Name of the Modelica model";
        input Real T_linearize=0
            "point in time of simulation to linearize the model";
        input String fileName="dslin" "Name of the result file";
        protected
        String fileName2=fileName + ".mat";
        Boolean OK1 = simulateModel(problem=modelName, startTime=0, stopTime=T_linearize);
        Boolean OK2 = importInitial("dsfinal.txt");
        Boolean OK3 = linearizeModel(problem=modelName, resultFile=fileName, startTime=T_linearize, stopTime=T_linearize+1);

        Real nxMat[1,1]=readMatrix(fileName2, "nx", 1, 1);
        Integer ABCDsizes[2]=readMatrixSize(fileName2, "ABCD");
        Integer nx=integer(nxMat[1, 1]);
        Integer nu=ABCDsizes[2] - nx;
        Integer ny=ABCDsizes[1] - nx;
        Real ABCD[nx + ny,nx + nu]=readMatrix(fileName2, "ABCD", nx + ny, nx + nu);
        String xuyName[nx + nu + ny]=readStringMatrix(fileName2, "xuyName", nx + nu + ny);
        public
        output StateSpace result(
          redeclare Real A[nx,nx],
          redeclare Real B[nx,nu],
          redeclare Real C[ny,nx],
          redeclare Real D[ny,nu]) "= model linearized at initial point";

      algorithm
        result.A := ABCD[1:nx, 1:nx];
        result.B := ABCD[1:nx, nx + 1:nx + nu];
        result.C := ABCD[nx + 1:nx + ny, 1:nx];
        result.D := ABCD[nx + 1:nx + ny, nx + 1:nx + nu];
        result.uNames := xuyName[nx + 1:nx + nu];
        result.yNames := xuyName[nx + nu + 1:nx + nu + ny];
        result.xNames := xuyName[1:nx];

      end fromModel;

    end Import;

    encapsulated package Internal
        "Internal library of record StateSpace (should not be directly used by user)"
        import Modelica;
        import Modelica_LinearSystems2;
      extends Modelica.Icons.Library;

      encapsulated function isSISO
          "To check a state space system to be SISO (or not)"

          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss;

        output Boolean isSISO;
      algorithm
        isSISO := size(ss.B, 2) == 1 and size(ss.C, 1) == 1;
      end isSISO;

    encapsulated function invariantZeros2
          "Compute invariant zeros of linear SISO state space system with a generalized system matrix [A, B, C, D] which is of upper Hessenberg form"
          import Modelica;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Math.Matrices;
          import Modelica_LinearSystems2.Math.Matrices.LAPACK;

      input StateSpace ss "Linear system in state space form";
      output Complex Zeros[:]
            "Finite, invariant zeros of ss; size(Zeros,1) <= size(ss.A,1)";

        protected
      Integer nx=size(ss.A, 1) "Number of states";
      Integer nu=size(ss.B, 2) "Number of inputs";
      Integer ny=size(ss.C, 1) "Number of outputs";
      Integer na=nx + nu;
      Real A[nx + ny,nx + nu]=[ss.A,ss.B; ss.C,ss.D];
      Real B[nx + ny,nx + nu]=[identity(nx),zeros(nx, nu); zeros(ny, nx + nu)];
      Real alphaReal[na];
      Real alphaImag[na];
      Real beta[na];
      Integer info;
      Real beta_small=100*Modelica.Constants.eps;
      Integer nZeros;
      Complex z[size(ss.A, 1)];
      Integer j;
      Real normB=max(beta_small,Modelica.Math.Matrices.norm(ss.B, p=1));
    algorithm
      assert(nu == ny, "Function invariantZeros requires currently that the number of
    inputs (= "
              + String(nu) + ") = number of outputs (= " + String(ny) + ")
    This condition is however not fulfilled");

    // Compute zeros

      (alphaReal,alphaImag,beta,info) :=
        Matrices.generalizedEigenvaluesTriangular(A, B);

      assert(info == 0,
        "Failed to compute invariant zeros with function invariantZeros(..)");

    // If beta[i] is zero, then zero i is infinite.
      j := 1;
      for i in 1:na loop

       if beta[i] >= normB*1e-6 then
         // finite eigenvalue
          z[j].re := if abs(alphaReal[i]) >= normB*1e-12 then alphaReal[i]/beta[i] else
                  0;
          z[j].im := if abs(alphaImag[i]) >= normB*1e-12 then alphaImag[i]/beta[i] else
                  0;
          j := j + 1;
        end if;
      end for;
      nZeros := j - 1;
      Zeros := z[1:nZeros];
    end invariantZeros2;

    function characterizeEigenvalue
          "Check stability, stabilizability, controllability, observability nad detectability of the single poles"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Internal.Eigenvalue;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Internal;

      input StateSpace ss=Modelica_LinearSystems2.StateSpace(
          A=fill(
            0,
            0,
            0),
          B=fill(
            0,
            0,
            0),
          C=fill(
            0,
            0,
            0),
          D=fill(
            0,
            0,
            0));
      input Eigenvalue evin[:];
      output Eigenvalue ev[size(ss.A, 1)];

        protected
      Real cPoles[:,2] "controllable poles";
      Real ncPoles[:,2] "uncontrollable poles";
      Real poles[size(ss.A, 1),2] "controllable and uncontrollable poles";
      Integer n_c;
      Integer nx=size(ss.A, 1);

      Eigenvalue cdummy[size(ss.A, 1)];
      Eigenvalue odummy[size(ss.A, 1)];
      StateSpace sst=StateSpace(
          A=transpose(ss.A),
          B=transpose(ss.C),
          C=transpose(ss.B),
          D=transpose(ss.D));
      Boolean equal;
      Integer ii;

      Real eps=1e-16*Modelica.Math.Matrices.norm(A=ss.A, p=1);
      Real factor_eps=1;
      Real absVector[nx];
      Integer indices[:];
      Integer indexMin;
      Real indexVector[:];
      Integer vv[:];
      Complex j = Modelica_LinearSystems2.Math.Complex.j();

    algorithm
      for i in 1:nx loop
        ev[i].ev := evin[i].ev;
      end for;

      (cPoles,ncPoles,poles) := StateSpace.Internal.controllablePoles(ss);
      n_c := size(cPoles, 1);
      for i in 1:n_c loop
        cdummy[i].ev := cPoles[i, 1]+cPoles[i, 2]*j;
        cdummy[i].isControllable := true;
      end for;
      for i in 1:size(ncPoles, 1) loop
        cdummy[n_c + i].ev := ncPoles[i, 1]+ncPoles[i, 2]*j;
        cdummy[n_c + i].isControllable := false;
      end for;

    // controllable poles of the trnasposed system are the observable poles
      (cPoles,ncPoles,poles) := StateSpace.Internal.controllablePoles(sst);
      n_c := size(cPoles, 1);
      for i in 1:n_c loop
        odummy[i].ev := cPoles[i, 1]+cPoles[i, 2]*j;
        odummy[i].isObservable := true;
      end for;
      for i in 1:size(ncPoles, 1) loop
        odummy[n_c + i].ev := ncPoles[i, 1]+ncPoles[i, 2]*j;
        odummy[n_c + i].isObservable := false;
      end for;

    // using ev.imag as an flag to mark the eigenvalues
      for i in 1:nx loop
        absVector := fill(1e50, nx);
        for ii in 1:nx loop
          if not odummy[ii].imag then
            absVector[ii] := Complex.'abs'(ev[i].ev - odummy[ii].ev);
          end if;
        end for;
        (absVector,indices) := Modelica.Math.Vectors.sort(absVector);
        indexMin := indices[1];
        ev[i].isObservable := odummy[indexMin].isObservable;
        odummy[indexMin].imag := true;
      end for;

      for i in 1:nx loop
        absVector := fill(1e50, nx);
        for ii in 1:nx loop
          if not cdummy[ii].imag then
            absVector[ii] := Complex.'abs'(ev[i].ev - cdummy[ii].ev);
          end if;
        end for;
        (absVector,indices) := Modelica.Math.Vectors.sort(absVector);
        indexMin := indices[1];
        ev[i].isControllable := cdummy[indexMin].isControllable;
        cdummy[indexMin].imag := true;
      end for;

      for i in 1:nx loop
        ev[i] := Eigenvalue(
          ev[i].ev,
          ev[i].isControllable,
          ev[i].isObservable);
      end for;

    end characterizeEigenvalue;

      encapsulated function isStabilizableSISO
          "To check wether a SISO system is stabliziable"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Complex;

        input StateSpace ss;

        protected
        input Modelica_LinearSystems2.Internal.StateSpaceR ssm=
            StateSpace.Internal.cntrHessenberg(ss);
        public
        output Boolean stabilizable;

        protected
        Complex evd[:]=fill(Complex(0), size(ss.A, 1) - ssm.r);

      algorithm
        if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
          assert(size(ss.B, 2) == 1,
            "A SISO-system is expected as input\n but the number of inputs is " +
            String(size(ss.B, 2)) + " instead of 1");
          assert(size(ss.C, 1) == 1,
            " A SISO-system is expected as input\n but the number of outputs is " +
            String(size(ss.C, 1)) + " instead of 1");
        end if;
        evd := Complex.eigenValues(ssm.A[ssm.r + 1:size(ss.A, 1), ssm.r + 1:size(ss.A,
          1)]);
        stabilizable := true;

        if size(ss.A, 1) > ssm.r then
           for i1 in 1:size(evd, 1) loop
            stabilizable := stabilizable and evd[i1].re < 0;
          end for;
        end if;

      end isStabilizableSISO;

      encapsulated function isStabilizableMIMO
          "To check wether a MIMO system is stabliziable"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Complex;

          input StateSpace ss;

          output Boolean stabilizable;

        protected
          Complex evnc[:] "complex vector of uncontrollable poles";
          Real cPoles[:,2] "controllable poles";
          Real ncPoles[:,2] "uncontrollable poles";
          Real poles[size(ss.A, 1),2] "controllable and uncontrollable poles";
          Complex j=Modelica_LinearSystems2.Math.Complex.j();

      algorithm
          (cPoles,ncPoles,poles) := StateSpace.Internal.controllablePoles(ss);
          evnc := fill(Complex(0), size(ncPoles, 1));
          for i1 in 1:size(ncPoles, 1) loop
            evnc[i1] := ncPoles[i1, 1] + j*ncPoles[i1, 2];
          end for;

          stabilizable := true;

          if size(ss.A, 1) > size(cPoles, 1) then
            for i1 in 1:size(ncPoles, 1) loop
              stabilizable := stabilizable and ncPoles[i1, 1] < 0;
            end for;
          end if;

      end isStabilizableMIMO;

      encapsulated function isDetectableSISO
          "To check wether a SISO system is detectable"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Complex;

        input StateSpace ss;

        protected
        input Modelica_LinearSystems2.Internal.StateSpaceR ssm=
            StateSpace.Internal.cntrHessenberg(
            StateSpace.Internal.transposeStateSpace(ss));
        public
        output Boolean detectable;
        protected
        Complex evd[:]=fill(Complex(0), size(ss.A, 1) - ssm.r);

      algorithm
        if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
          assert(size(ss.B, 2) == 1,
            "A SISO-system is expected as input\n but the number of inputs is " +
            String(size(ss.B, 2)) + " instead of 1");
          assert(size(ss.C, 1) == 1,
            " A SISO-system is expected as input\n but the number of outputs is " +
            String(size(ss.C, 1)) + " instead of 1");
        end if;
        evd := Complex.eigenValues(ssm.A[ssm.r + 1:size(ss.A, 1), ssm.r + 1:size(ss.A,1)]);

        if size(ss.A, 1) == ssm.r then
          detectable := true;

        else
          detectable := true;
          for i1 in 1:size(evd, 1) loop
            detectable := detectable and evd[i1].re < 0;
          end for;
        end if;

      end isDetectableSISO;

      encapsulated function isDetectableMIMO
          "To check wether a MIMO system is detectable"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Complex;

          input StateSpace ss;

          output Boolean detectable;

        protected
          StateSpace sst=StateSpace.Internal.transposeStateSpace(ss);

          Complex evnd[:] "complex vector of uncontrollable poles";
          Real dPoles[:,2] "controllable poles";
          Real ndPoles[:,2] "uncontrollable poles";
          Real poles[size(ss.A, 1),2] "controllable and uncontrollable poles";
          Complex j=Modelica_LinearSystems2.Math.Complex.j();

      algorithm
          (dPoles,ndPoles,poles) := StateSpace.Internal.controllablePoles(sst);
          evnd := fill(Complex(0), size(ndPoles, 1));
          for i1 in 1:size(ndPoles, 1) loop
            evnd[i1] := ndPoles[i1, 1] + j*ndPoles[i1, 2];
          end for;

          detectable := true;

          if size(sst.A, 1) == size(dPoles, 1) then
            detectable := true;
          else
            for i1 in 1:size(ndPoles, 1) loop
              detectable := detectable and ndPoles[i1, 1] < 0;
            end for;
          end if;

      end isDetectableMIMO;

      encapsulated function isObservableSISO
          "To check wether a SISO system is observable"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;

         input StateSpace ss;

        protected
         StateSpace ss2=StateSpace.Internal.transposeStateSpace(ss);

         Modelica_LinearSystems2.Internal.StateSpaceR ssm2=
             StateSpace.Internal.reducedCtrSystem(ss2);
        public
         output Boolean observable;
      algorithm
       if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
             assert(size(ss.B, 2) == 1,
               "A SISO-system is expected as input\n but the number of inputs is "
                + String(size(ss.B, 2)) + " instead of 1");
             assert(size(ss.C, 1) == 1,
               " A SISO-system is expected as input\n but the number of outputs is "
                + String(size(ss.C, 1)) + " instead of 1");
           end if;
         observable := size(ss.A, 1) == ssm2.r;

      end isObservableSISO;

      encapsulated function isControllableSISO
          "To check a SISO system wether it is controllable"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss;

        protected
        Modelica_LinearSystems2.Internal.StateSpaceR ssm=
            StateSpace.Internal.reducedCtrSystem(ss);

        public
        output Boolean controllable;
      algorithm
        if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
          assert(size(ss.B, 2) == 1,
            "A SISO-system is expected as input\n but the number of inputs is "
             + String(size(ss.B, 2)) + " instead of 1");
          assert(size(ss.C, 1) == 1,
            " A SISO-system is expected as input\n but the number of outputs is "
             + String(size(ss.C, 1)) + " instead of 1");
        end if;
        controllable := size(ss.A, 1) == ssm.r;

      end isControllableSISO;

      encapsulated function isControllableMIMO
          "To check a MIMO system wether it is controllable"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;

          input StateSpace ss;
          input Modelica_LinearSystems2.Types.StaircaseMethod method=
              Modelica_LinearSystems2.Types.StaircaseMethod.SVD;

          output Boolean controllable;
      algorithm
          assert(method == Modelica_LinearSystems2.Types.StaircaseMethod.SVD or
            method == Modelica_LinearSystems2.Types.StaircaseMethod.QR, "\nMethods for staircase algorithm are QR factorization or singular value decomposition. Therefore,
    the variable \"method\" in \"Modelica_LinearSystems2.StateSpace.Internal.isControllableMIMO\" has to be qr or svd but is method = "
             + String(method));
          if min(size(ss.B)) == 0 then
            controllable := false;
          else
            if method == Modelica_LinearSystems2.Types.StaircaseMethod.QR then
              controllable := StateSpace.Internal.staircaseQR(ss);
            else
              controllable := StateSpace.Internal.staircaseSVD(ss);
            end if;
          end if;
      end isControllableMIMO;

      encapsulated function isObservableMIMO
          "To check a MIMO system wether it is observable"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;

          input StateSpace ss;
          input Modelica_LinearSystems2.Types.StaircaseMethod method=
              Modelica_LinearSystems2.Types.StaircaseMethod.SVD;

        protected
          StateSpace ss2=StateSpace.Internal.transposeStateSpace(ss);

        public
          output Boolean observable;
      algorithm
          assert(method == Modelica_LinearSystems2.Types.StaircaseMethod.SVD or
            method == Modelica_LinearSystems2.Types.StaircaseMethod.QR, "\nMethods for staircase algorithm are QR factorization or singular value decomposition. Therefore,
    the variable \"method\" in \"Modelica_LinearSystems2.StateSpace.Internal.isControllableMIMO\" has to be qr or svd but is method = "
             + String(method));
          if min(size(ss.C)) == 0 then
            observable := false;
          else
            if method == Modelica_LinearSystems2.Types.StaircaseMethod.QR then
              observable := StateSpace.Internal.staircaseQR(ss2);
            else
              observable := StateSpace.Internal.staircaseSVD(ss2);
            end if;

          end if;

      end isObservableMIMO;

     encapsulated function isControllableAndObservableSISO
          "To check whether a SISO system is controllable and observable"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss;

        protected
        Modelica_LinearSystems2.Internal.StateSpaceR ssm1=
            StateSpace.Internal.reducedCtrSystem(ss);
        Integer nx=size(ss.A, 1);
        Integer rankQ=ssm1.r;
        StateSpace ss2=StateSpace(
              A=transpose(ssm1.A[nx - rankQ + 1:nx, nx - rankQ + 1:nx]),
              B=transpose(ssm1.C[:, nx - rankQ + 1:nx]),
              C=transpose(ssm1.B[nx - rankQ + 1:nx, :]),
              D=ssm1.D);
        Integer nx2=ssm1.r;
        Modelica_LinearSystems2.Internal.StateSpaceR ssm2=
            StateSpace.Internal.reducedCtrSystem(ss2);
        public
        output Boolean controllableAndObservable;
     algorithm
        if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
          assert(size(ss.B, 2) == 1,
            "A SISO-system is expected as input\n but the number of inputs is " +
            String(size(ss.B, 2)) + " instead of 1");
          assert(size(ss.C, 1) == 1,
            " A SISO-system is expected as input\n but the number of outputs is "
             + String(size(ss.C, 1)) + " instead of 1");
        end if;
        controllableAndObservable := size(ss.A, 1) == ssm2.r;
     equation

     end isControllableAndObservableSISO;

      encapsulated function readLength_nx
          "Read the order nx of a state space system from a file"

        input String fileName="ss_siso.mat"
            "Name of the state space system data file";
        output Integer nx;
        protected
        Real nxMat[1,1]=readMatrix(
                fileName,
                "nx",
                1,
                1);
      algorithm
        nx := integer(nxMat[1, 1]);
      end readLength_nx;

      encapsulated function staircaseQR
          "Staircase algorithm to put a state space system to controller Hessenberg form"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Matrices;
          import Modelica_LinearSystems2.Math.Vectors;

        input StateSpace ss;

        output Boolean isControllable;
        output Modelica_LinearSystems2.Internal.StateSpaceR ssm1(
          redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
          redeclare Real B[size(ss.B, 1),size(ss.B, 2)],
          redeclare Real C[size(ss.C, 1),size(ss.C, 2)],
          redeclare Real D[size(ss.D, 1),size(ss.D, 2)])
            "controllable state space system";
        output Real PP[:,:];
        protected
        Real A[:,:];
        Real B[size(ss.B, 1),size(ss.B, 2)];
        Real C[:,:];
        Real Q[:,:];
        Real Q2[:,:];
        Real R[:,:];

        Real P[:,:];
        Real tau[:];

        Integer nx=size(ss.A, 1);
        Integer nu=size(ss.B, 2);
        Integer n1;
        Boolean stop;
        Real normA=Modelica.Math.Matrices.norm(A=ss.A, p=1);
        Real eps=normA*1e-10;
        Integer stairStepinSys;
        Integer info;
        Integer nn;
        Integer stairStep;
        Integer rankR;

      algorithm
        if nu > 0 then
        if nx > 1 then

           //#####  first step of staircase
             // transform b->Q'b = {*,0,...,0} and c->cQ, A->Q'AQ

          (Q,R,tau,Q2) := Matrices.QR( ss.B);
          B := [R; zeros(nx - nu, nu)];  // should be the same as transopse(Q2)*ss.B

          A := transpose(Q2)*ss.A;
          A := A*Q2;
          C := ss.C*Q2;
          PP := transpose(Q2);

          stairStep := 0;
          rankR := 0;
           // for i in 1:size(R, 1) loop
           //   if abs(R[i, i]) > eps then
           //     rankR := rankR + 1;
           //   end if;
           // end for;

      //  !!!! rank has to be determined. In the case of ill conditioned systems svd should be used
          for i in 1:min(size(R, 1), size(R, 2)) loop
            if abs(R[i, size(R, 2) - min(size(R, 1), size(R, 2)) + i]) > eps then
              rankR := rankR + 1;
            end if;
          end for;
          stairStep := stairStep + rankR;
          n1 := nx - stairStep;
          stop := false;

           // #######  buildig rest of staircase
          while not stop loop

            (Q,R,tau,Q2) := Matrices.QR( A[stairStep + 1:nx, stairStep - rankR +
              1:stairStep]);
            P := [identity(nx - n1),zeros(nx - n1, n1); zeros(n1, nx - n1),Q2];
            PP := transpose(P)*PP;
            A := [A[1:stairStep, 1:stairStep],A[1:stairStep, stairStep + 1:nx]*Q2;
              transpose(Q2)*A[stairStep + 1:nx, 1:stairStep],transpose(Q2)*A[
              stairStep + 1:nx, stairStep + 1:nx]*Q2];
             //=transpose(P)*A*P = [A11, A12*Q2; transpose(Q2)*A21, transpose(Q2)*A22*Q2]
            C[:, nx - n1 + 1:nx] := C[:, nx - n1 + 1:nx]*Q2;

            rankR := 0;
             // for i in 1:size(R, 1) loop
             //   if abs(R[i, i]) > eps then
             //     rankR := rankR + 1;
             //   end if;
             // end for;

      //  !!!! rank has to be determined. In the case of ill conditioned systems svd should be used
            for i in 1:min(size(R, 1), size(R, 2)) loop
              if abs(R[i, size(R, 2) - min(size(R, 1), size(R, 2)) + i]) > eps then
                rankR := rankR + 1;
              end if;
            end for;
            stairStep := stairStep + rankR;
            n1 := if rankR < 1 then -1 else n1 - rankR;
            stop := n1 <= 0;

          end while;
        else
          stairStep := if Modelica.Math.Matrices.isEqual(ss.B, zeros(size(ss.B, 1), size(ss.B, 2))) then 0 else
                  1;
          A := ss.A;
          B := ss.B;
          C := ss.C;
        end if;

        ssm1 := Modelica_LinearSystems2.Internal.StateSpaceR(
              A=A,
              B=B,
              C=C,
              D=ss.D,
              r=stairStep);

        isControllable := stairStep == nx;
        else // no inputs, nu==0
        isControllable := false;
        ssm1 :=  Modelica_LinearSystems2.Internal.StateSpaceR(
              A=ss.A,
              B=ss.B,
              C=ss.C,
              D=ss.D,
              r=0);
        P := identity(nu);
        end if;

      end staircaseQR;

      encapsulated function staircaseSVD
          "Staircase algorithm based on singular value decomposition"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Vectors;
          import Modelica_LinearSystems2.Math.Complex;

        input StateSpace ss;

        output Boolean isControllable;
        output Modelica_LinearSystems2.Internal.StateSpaceR ssm1(
          redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
          redeclare Real B[size(ss.B, 1),size(ss.B, 2)],
          redeclare Real C[size(ss.C, 1),size(ss.C, 2)],
          redeclare Real D[size(ss.D, 1),size(ss.D, 2)])
            "upper block Hessenberg form state space system";
        output Real P[:,:];

        protected
        Real A[:,:];
        Real B[size(ss.B, 1),size(ss.B, 2)];
        Real C[:,:];
        Real U[:,:];
        Real VT[:,:];

        Real Q[:,:];
        Real Pi[:,:];
        Real tau[:];
        Real sigma[:];

        Integer nx=size(ss.A, 1);
        Integer nu=size(ss.B, 2);
        Integer ni;
        Boolean stop;
        Real normA=Modelica.Math.Matrices.norm(A=ss.A, p=1);
        Real eps=normA*1e-10;
        Integer stairStepinSys;
        Integer info;
        Integer nn;
        Integer stairStep;
        Integer rankS;
        Integer maxA=max(size(ss.A));
      //   Real evc[:,2];
      //   Real evnc[:,2];

      algorithm
        if nu > 0 then
        if nx > 1 then
      //#####  first step of staircase
              // transform b->Q'b = {*,0,...,0} and c->cQ, A->Q'AQ
          (sigma,U,VT) := Modelica.Math.Matrices.singularValues(ss.B);

          rankS := 0;
          for i in 1:size(sigma, 1) loop
            if sigma[i] > maxA*sigma[1]*Modelica.Constants.eps then
              rankS := rankS + 1;
            end if;
          end for;

          B := [diagonal(sigma[1:rankS]),zeros(rankS, nu - rankS); zeros(nx - rankS, nu)];
          P := transpose(U);
          Q := transpose(VT);
          A := transpose(U)*ss.A*U;
          C := ss.C*U;
          (sigma,U,VT) := Modelica.Math.Matrices.singularValues(A[rankS + 1:nx, 1:rankS]);

          stairStep := rankS;
          rankS := 0;
          if size(sigma, 1) > 1 then
            for i in 1:size(sigma, 1) loop
              if sigma[i] > maxA*sigma[1]*Modelica.Constants.eps then
                rankS := rankS + 1;
              end if;
            end for;
          else
            rankS := if size(sigma, 1) > 0 then if sigma[1] > maxA*Modelica.Constants.eps then 1 else 0 else 0;
          end if;

          stairStep := stairStep + rankS;
          Pi := [VT,zeros(size(VT, 1), size(U, 1)); zeros(size(U, 2), size(VT, 2)),transpose(U)];
          B := Pi*B;
          P := Pi*P;

       // P could be ambigious according to the sign
          if transpose(P)*B[:,1]*ss.B[:,1]<0 then
            Pi:=-Pi;
            P:=-P;
          end if;

          A := Pi*A*transpose(Pi);

         // should be made better because of many zeros in B

          while stairStep < nx and rankS > 0 and not
              Modelica.Math.Matrices.isEqual(
                  A[stairStep + 1:nx, stairStep - rankS + 1:stairStep],
                  zeros(stairStep - rankS, rankS),
                  eps) loop

            (sigma,U,VT) := Modelica.Math.Matrices.singularValues(A[stairStep + 1:nx, stairStep
               - rankS + 1:stairStep]);

            Pi := [identity(stairStep - rankS),zeros(stairStep - rankS, nx -
              stairStep + rankS); zeros(nx - stairStep + rankS, stairStep - rankS),
              [VT,zeros(rankS, nx - stairStep); zeros(nx - stairStep, rankS),
              transpose(U)]];
            P := Pi*P;
            A := Pi*A*transpose(Pi);

      //new implenmentation necessary because of many zeros in Pi
            C := C*transpose(Pi);
            rankS := 0;
            if size(sigma, 1) > 1 then
              for i in 1:size(sigma, 1) loop
                if sigma[i] > maxA*sigma[1]*Modelica.Constants.eps then
                  rankS := rankS + 1;
                end if;
              end for;
            else
              rankS := if size(sigma, 1) > 0 then if sigma[1] > maxA*Modelica.Constants.eps then
                      1 else 0 else 0;
            end if;
            stairStep := stairStep + rankS;
          end while;

        else
          stairStep := if Modelica.Math.Matrices.isEqual(ss.B, zeros(size(ss.B, 1),
            size(ss.B, 2))) then 0 else 1;
          A := ss.A;
          B := ss.B;
          C := ss.C;
        end if;

        ssm1 := Modelica_LinearSystems2.Internal.StateSpaceR(
              A=A,
              B=B,
              C=C,
              D=ss.D,
              r=stairStep);

      //   evc := Modelica.Math.Matrices.eigenValues(A[1:stairStep, 1:stairStep]);
      //   evnc := Modelica.Math.Matrices.eigenValues(A[stairStep + 1:nx, stairStep + 1:nx]);

        isControllable := stairStep == nx;

        else // no inputs, nu==0
        isControllable := false;
        ssm1 :=  Modelica_LinearSystems2.Internal.StateSpaceR(
              A=ss.A,
              B=ss.B,
              C=ss.C,
              D=ss.D,
              r=0);
        P := identity(nu);
        end if;

      end staircaseSVD;

      encapsulated function partialGain
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.StateSpace.Internal;

        input Real H[:,size(H, 1)];
        input Real b[size(H, 1)];
        output Real result;
        protected
        Real Hh[:,:]=H;
        Real bh[:]=b;
        Integer q=size(H, 1);
      algorithm

        (Hh,bh) := Internal.trianUpperHess(Hh, bh);
        result := bh[q]/Hh[q, q];

      end partialGain;

      encapsulated function assignOneOrTwoPoles_alpha
          "Algorithm to assign p (p = 1 or 2) eigenvalues"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Math.Vectors;

        input Real F[:,size(F, 1)] "system matrix of order p=1 or p=2";
        input Real G[size(F, 1),:] "control input matrix p rows";
        input Complex gamma[size(F, 1)];
        input Real tolerance=Modelica.Constants.eps;
        output Real K[:,size(F, 1)] "feedback matrix p columns";

        protected
        Real Gamma[:,:];
        Integer rankGs;
        Real Fs[size(F, 1),size(F, 2)];
        Real Gs[size(G, 1),size(G, 2)];
        Real Gst[:,:]=transpose(G);
        Real Ks[:,size(F, 1)];
        Real c;
        Real s;
        Real r;

        Real V1[size(G, 2),size(G, 2)];
        Real V2[size(G, 2),size(G, 2)];
        Real V[size(G, 2),size(G, 2)];
        Real U[size(F, 1),size(F, 2)];

        Real u1[:];
        Real u2[:];

      algorithm
        assert(size(F, 1) >= size(gamma, 1),
          "\n In function StateSpace.Internal.assignOneOrTwoPoles() matrix F is of size ["
           + String(size(F, 1)) + "," + String(size(F, 1)) + "] and " + String(
          size(F, 1)) + " demanded assigned poles are expected. However, " +
          String(size(gamma, 1)) + " poles are given");
      //assert(not Modelica.Math.Matrices.isEqual(G,zeros(size(G,1),size(G,2)),tolerance),"A subsystem (F, G) in StateSpace.Internal.assignOneOrTwoPoles() is not controllable, since G is equal to zero matrix ");
        if size(gamma, 1) == 1 then
          assert(gamma[1].im == 0, "\n In function StateSpace.Internal.assignOneOrTwoPoles() matrix F has size [" + String(size(F, 1)) + "," + String(size(F, 1)) +
            "], therefore, the demanded assigned pole must be real. However, the imaginary part is "
             + String(gamma[1].im));
        elseif abs(gamma[1].im) > 0 or abs(gamma[2].im) > 0 then
          assert(gamma[1].re == gamma[2].re and gamma[1].im == -gamma[2].im,
            "\nThe assigned pole pair given in function StateSpace.Internal.assignOneOrTwoPoles() must be conjungated complex. However, the poles are\npole1 = "
             + String(gamma[1]) + "\npole2 = " + String(gamma[2]) +
            ". \nTry\npole1 = " + String(gamma[1]) + "\npole2 = " + String(
            Complex.conj(gamma[1])) + "\ninstead");
        end if;

        if not Modelica.Math.Matrices.isEqual(
                G,
                zeros(size(G, 1), size(G, 2)),
                tolerance) then
          if size(G, 2) == 1 then
            V := [1];
            if size(G, 1) == 1 then
              U := [1];
            else
               // Givens
              r := sqrt(G[1, 1]^2 + G[2, 1]^2);
              c := G[1, 1]/r;
              s := G[2, 1]/r;
              U := [c,s; -s,c];
            end if;
            Gs := U*G;

            rankGs := if abs(Gs[1, 1]) > tolerance then 1 else 0;
          else
           // size(G, 2)>1

            if size(G, 1) == 1 then // U=I, compute V by just one Householder transformation
              U := [1];
              u1 := cat(1, Vectors.householderVector(Gst[:, 1], cat(1, {1}, zeros(size(G, 2) - 1))));
                                       // Householder vector
              Gst := Modelica_LinearSystems2.Math.Matrices.householderReflexion(
                Gst, u1);

              V := identity(size(G, 2)) - 2*matrix(u1)*transpose(matrix(u1))/(u1*u1);
              Gs := transpose(Gst);
              rankGs := if abs(Gs[1, 1]) > tolerance then 1 else 0;

            else
              //2xHH + Givens
              u1 := cat(1, Vectors.householderVector(Gst[:, 1], cat(
                    1,
                    {1},
                    zeros(size(G, 2) - 1))));
                                       // Householder vector1
              Gst := Modelica_LinearSystems2.Math.Matrices.householderReflexion(
                Gst, u1);
              V1 := identity(size(G, 2)) - 2*matrix(u1)*transpose(matrix(u1))/(u1
                *u1);

      // if rank of G of a multi input system is equal to 1
              if Modelica.Math.Vectors.isEqual(Gst[:, 2], zeros(size(G, 2)), tolerance) or
                Modelica.Math.Matrices.isEqual(Gst[2:size(Gst, 1), :], zeros(size(Gst, 1) - 1, size(Gst, 2)), tolerance) then
                V := V1;
                rankGs := if abs(Gs[1, 1]) > tolerance then 1 else 0;
              else

                u2 := cat(
                      1,
                      zeros(1),
                      Vectors.householderVector(Gst[2:size(G, 2), 2], cat(
                        1,
                        {1},
                        zeros(size(G, 2) - 2))));
                                         // Householder vector2
                Gst := Modelica_LinearSystems2.Math.Matrices.householderReflexion(
                  Gst, u2);

                V2 := identity(size(G, 2)) - 2*matrix(u2)*transpose(matrix(u2))/(
                  u1*u1);
                V := V2*V1;

              end if;

              Gs := transpose(Gst);

              rankGs := 0;
              for i in 1:2 loop
                if abs(Gs[i, i]) > tolerance then
                  rankGs := rankGs + 1;
                end if;
              end for;
           // Givens rotation to transfotm Gs[1:2,1:2] to right upper triangle
              r := sqrt(Gs[1, 1]^2 + Gs[2, 1]^2);
              c := Gs[1, 1]/r;
              s := Gs[2, 1]/r;
              U := [c,s; -s,c];
              Gs := U*Gs;

            end if;
                end if;

      // check controllability
          assert(not Modelica.Math.Matrices.isEqual(
                Gs,
                zeros(size(Gs, 1), size(Gs, 2)),
                tolerance),
            "A subsystem in StateSpace.Internal.assignOneOrTwoPoles() is not controllable");

          Ks := fill(
                0,
                rankGs,
                size(F, 1));
          Fs := U*F*transpose(U);

          if size(F, 1) == 1 then
            Ks := matrix((Fs[1, 1] - gamma[1].re)/Gs[1, 1]);
          else
            if rankGs == size(F, 1) then

              // Gamma:= if size(F,1)==1 then [gamma[1].re] else [gamma[1].re, -(gamma[1].im)^2;1, gamma[2].re];
              //  Ks :=  Modelica_LinearSystems2.Math.Matrices.solve2(Gs, Fs - Gamma);
              Ks := [(Fs[1, 1] - gamma[1].re)/Gs[1, 1] - Gs[1, 2]*(Fs[2, 1] - 1)/Gs[1, 1]/Gs[2,
                2],(Fs[1, 2] + (gamma[1].im)^2)/Gs[1, 1] - Gs[1, 2]*(Fs[2, 2] -
                gamma[2].re)/Gs[1, 1]/Gs[2, 2]; (Fs[2, 1] - 1)/Gs[2, 2],(Fs[2, 2]
                 - gamma[2].re)/Gs[2, 2]];
            else

              Ks[1, 1] := (gamma[1].re + gamma[2].re - Fs[1, 1] - Fs[2, 2])/Gs[1,
                1];
              Ks[1, 2] := Ks[1, 1]*Fs[2, 2]/Fs[2, 1] + (Fs[1, 1]*Fs[2, 2] - Fs[1,
                2]*Fs[2, 1] - (gamma[1].re*gamma[2].re - gamma[1].im*gamma[2].im))
                /Fs[2, 1]/Gs[1, 1];
              Ks := -Ks;
            end if;
          end if;

      //    K := transpose(V)*[Ks; zeros(size(G, 2) - rankGs, size(Ks, 2))]*(U);
          K := transpose(V[1:size(Ks, 1), :])*Ks*U;

        else
          Modelica.Utilities.Streams.print(
            "\n A subsystem (F, G) in StateSpace.Internal.assignOneOrTwoPoles() is not controllable, since G is equal to zero matrix. Therefore, K is set to zero matrix and the eigenvalues are retained");
          K := zeros(size(G, 2), size(F, 1));
        end if;

      end assignOneOrTwoPoles_alpha;

      encapsulated function assignOneOrTwoPoles
          "Algorithm to assign p (p = 1 or 2) eigenvalues"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Math.Vectors;

        input Real F[:,size(F, 1)] "system matrix of order p=1 or p=2";
        input Real G[size(F, 1),:] "control input matrix p rows";
        input Complex gamma[size(F, 1)];
        input Real tolerance=Modelica.Constants.eps;
        output Real K[:,size(F, 1)] "feedback matrix p columns";

        protected
        Real Gamma[:,:];
        Integer rankGs;
        Real Fs[size(F, 1),size(F, 2)];
        Real Gs[size(G, 1),size(G, 2)];
        Real Gst[:,:]=transpose(G);
        Real Ks[:,size(F, 1)];
        Real c;
        Real s;
        Real r;
        Integer p=size(F,1);
        Real sigmaG[:];

        Real V[size(G, 2),size(G, 2)];
        Real U[size(F, 1),size(F, 2)];

        Real u1[:];
        Real u2[:];
        Integer i;
        Complex system_ev[:];

      algorithm
        assert(size(F, 1) >= size(gamma, 1),
          "\n In function StateSpace.Internal.assignOneOrTwoPoles() matrix F is of size ["
           + String(size(F, 1)) + "," + String(size(F, 1)) + "] and " + String(
          size(F, 1)) + " demanded assigned poles are expected. However, " +
          String(size(gamma, 1)) + " poles are given");
      //assert(not Modelica.Math.Matrices.isEqual(G,zeros(size(G,1),size(G,2)),tolerance),"A subsystem (F, G) in StateSpace.Internal.assignOneOrTwoPoles() is not controllable, since G is equal to zero matrix ");
        if size(gamma, 1) == 1 then
          assert(gamma[1].im == 0, "\n In function StateSpace.Internal.assignOneOrTwoPoles() matrix F has size [" + String(size(F, 1)) + "," + String(size(F, 1)) +
            "], therefore, the demanded assigned pole must be real. However, the imaginary part is "
             + String(gamma[1].im));
        elseif abs(gamma[1].im) > 0 or abs(gamma[2].im) > 0 then
          assert(gamma[1].re == gamma[2].re and gamma[1].im == -gamma[2].im,
            "\nThe assigned pole pair given in function StateSpace.Internal.assignOneOrTwoPoles() must be conjungated complex. However, the poles are\npole1 = "
             + String(gamma[1]) + "\npole2 = " + String(gamma[2]) +
            ". \nTry\npole1 = " + String(gamma[1]) + "\npole2 = " + String(
            Complex.conj(gamma[1])) + "\ninstead");
        end if;

        if not Modelica.Math.Matrices.isEqual(
            G,
            zeros(size(G, 1), size(G, 2)),
            tolerance) then
          if size(G, 2) == 1 then
            V := [1];
            if size(G, 1) == 1 then
              U := [1];
            else
               // Givens
              r := sqrt(G[1, 1]^2 + G[2, 1]^2);
              c := G[1, 1]/r;
              s := G[2, 1]/r;
              U := [c,s; -s,c];
            end if;
            Gs := U*G;

            rankGs := if abs(Gs[1, 1]) > tolerance then 1 else 0;
          else
           // size(G, 2)>1

            if size(G, 1) == 1 then // U=I, compute V by just one Householder transformation
              U := [1];
              u1 := cat(1, Vectors.householderVector(Gst[:, 1],
                           cat(1, {1}, zeros(size(G, 2) - 1))));// Householder vector
              Gst := Modelica_LinearSystems2.Math.Matrices.householderReflexion(Gst, u1);

              V := identity(size(G, 2)) - 2*matrix(u1)*transpose(matrix(u1))/(u1*u1);
              Gs := transpose(Gst);
              rankGs := if abs(Gs[1, 1]) > tolerance then 1 else 0;

            else
      // systems with p==2 and m>1 are transformed by svd
              (sigmaG,U,V) := Modelica.Math.Matrices.singularValues(G);
              rankGs := 0;
              i := size(sigmaG, 1);
              while i > 0 loop
                if sigmaG[i] > 1e-10 then
                  rankGs := i;
                  i := 0;
                end if;
                i := i - 1;
              end while;
              Gs := zeros(p, size(G, 2));
              for i in 1:rankGs loop
                Gs[i, i] := sigmaG[i];
              end for;

            end if;
            V := transpose(V);
          end if;

      // check controllability
          assert(not Modelica.Math.Matrices.isEqual(
            Gs,
            zeros(size(Gs, 1), size(Gs, 2)),
            tolerance), "A subsystem in StateSpace.Internal.assignOneOrTwoPoles() is not controllable");

          Ks := fill(
            0,
            rankGs,
            size(F, 1));
          Fs := U*F*transpose(U);

          if size(F, 1) == 1 then
            Ks := matrix((Fs[1, 1] - gamma[1].re)/Gs[1, 1]);
          else
            if rankGs == size(F, 1) then

              // Gamma:= if size(F,1)==1 then [gamma[1].re] else [gamma[1].re, -(gamma[1].im)^2;1, gamma[2].re];
              //  Ks :=  Modelica_LinearSystems2.Math.Matrices.solve2(Gs, Fs - Gamma);
              Ks := [(Fs[1, 1] - gamma[1].re)/Gs[1, 1] - Gs[1, 2]*(Fs[2, 1] - 1)/Gs[1, 1]/Gs[2,2],
              (Fs[1, 2] + (gamma[1].im)^2)/Gs[1, 1] - Gs[1, 2]*(Fs[2, 2] - gamma[2].re)/Gs[1, 1]/Gs[2, 2];
              (Fs[2, 1] - 1)/Gs[2, 2],(Fs[2, 2] - gamma[2].re)/Gs[2, 2]];
            else

              Ks[1, 1] := (gamma[1].re + gamma[2].re - Fs[1, 1] - Fs[2, 2])/Gs[1, 1];
              Ks[1, 2] := Ks[1, 1]*Fs[2, 2]/Fs[2, 1] + (Fs[1, 1]*Fs[2, 2] - Fs[1, 2]*
                Fs[2, 1] - (gamma[1].re*gamma[2].re - gamma[1].im*gamma[2].im))/Fs[2,1]/Gs[1, 1];
              Ks := -Ks;
            end if;
          end if;

          K := V[:, 1:size(Ks, 1)]*Ks*U;

        else
          if p == 1 then
            Modelica.Utilities.Streams.print("\n A subsystem (F, G) in StateSpace.Internal.assignOneOrTwoPoles() is not controllable, since G is equal to zero matrix. Therefore, K is set to zero matrix and the eigenvalues are retained.\n
          That is, "   + String(F[1, 1]) + " remains and " + String(gamma[1].re) + " cannot be realized");
          else
            system_ev := Complex.eigenValues(F);
            Modelica.Utilities.Streams.print("\n A subsystem (F, G) in StateSpace.Internal.assignOneOrTwoPoles() is not controllable, since G is equal to zero matrix. Therefore, K is set to zero matrix and the eigenvalues are retained.\n
          That is, "   + String(system_ev[1].re) + (if abs(system_ev[1].im) > 0 then " + " else
                    " - ") + String(system_ev[1].im) + "j and " + String(system_ev[2].re)
               + (if abs(system_ev[2].im) > 0 then " + " else " - ") + String(
              system_ev[2].im) + "j remain and " + String(gamma[1].re) + (if abs(
              gamma[1].im) > 0 then (if gamma[1].im > 0 then " + " else " - " +
              String(gamma[1].im) + "j") else "" + " and ") + String(gamma[2].re) + (
              if abs(gamma[2].im) > 0 then (if gamma[2].im > 0 then " + " else " - " +
              String(gamma[2].im) + "j") else "") + " cannot be realized");
          end if;
          K := zeros(size(G, 2), size(F, 1));
        end if;

      end assignOneOrTwoPoles;

      encapsulated function readSystemDimension
          "Read the order nx of state matrix and the numbers nu and ny of inputs and outputs"
          import Modelica_LinearSystems2;
        input String fileName="stateSpace.mat";
        input String matrixName="ABCD"
            "Name of the generalized state space system matrix";
        output Integer xuy[3];

        protected
        Real sizeA[1,1]=readMatrix(
                fileName,
                "nx",
                1,
                1);

        Integer ABCDsizes[2]=readMatrixSize(fileName, matrixName);

      algorithm
        xuy[1] := integer(sizeA[1, 1]);
        xuy[2] := ABCDsizes[2] - xuy[1];
        xuy[3] := ABCDsizes[1] - xuy[1];

      end readSystemDimension;

      encapsulated function readLength_nu
          "Read the number of inputs nu of a state space system from a file"

        input String fileName="ss_siso.mat"
            "Name of the state space system data file";
        input String matrixName="ABCD" "Name of the state space system matrix";

        output Integer nu;
        protected
        Real nxMat[1,1]=readMatrix(
                fileName,
                "nx",
                1,
                1);
        Integer ABCDsizes[2]=readMatrixSize(fileName, matrixName);
        Integer nx=integer(nxMat[1, 1]);

      algorithm
        nu := ABCDsizes[2] - nx;
      end readLength_nu;

      encapsulated function readLength_ny
          "Read the number of outputs ny of a state space system from a file"

        input String fileName="ss_siso.mat"
            "Name of the state space system data file";
        input String matrixName="ABCD" "Name of the state space system matrix";

        output Integer ny;
        protected
        Real nxMat[1,1]=readMatrix(
                fileName,
                "nx",
                1,
                1);
        Integer ABCDsizes[2]=readMatrixSize(fileName, matrixName);
        Integer nx=integer(nxMat[1, 1]);

      algorithm
        ny := ABCDsizes[1] - nx;
      end readLength_ny;

      encapsulated function dgreeOfRedSys
          "Calculate the controllable and observable part of a state space system"

          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2;

        input StateSpace ss;
        output Integer degree_rs;

        protected
        StateSpace ssm1=
            Modelica_LinearSystems2.StateSpace.Internal.reducedCtrSystem(ss);

        StateSpace ss2=StateSpace(
                A=transpose(ssm1.A),
                B=transpose(ssm1.C),
                C=transpose(ssm1.B),
                D=ssm1.D);

        StateSpace ssm2=
            Modelica_LinearSystems2.StateSpace.Internal.reducedCtrSystem(ss2);

      algorithm
        degree_rs := size(ssm2.A, 1);

      end dgreeOfRedSys;

      encapsulated function householder
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Matrices;
          import Modelica_LinearSystems2.Math.Vectors;

        input StateSpace ss;
        input Real v[size(ss.A, 1)];

        output StateSpace ssh;

        protected
        Real Ah[size(ss.A, 1),size(ss.A, 2)];
        Real Bh[size(ss.B, 1),size(ss.B, 2)];
        Real Ch[size(ss.C, 1),size(ss.C, 2)];
      algorithm
        Ah := Matrices.householderSimilarityTransformation(ss.A, v);
        Bh := Matrices.householderReflexion(ss.B, v);
        Ch := transpose(Matrices.householderReflexion(transpose(ss.C), v));

        ssh := StateSpace(
              A=Ah,
              B=Bh,
              C=Ch,
              D=ss.D);

      end householder;

      encapsulated function numberOfZeros
          "Calculate the number of zeros of the related transfer function"

          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss "StateSpace object";

        output Integer numberOfZeros;

        protected
        Integer nop;
        Integer noz;

      algorithm
        (nop,noz) := StateSpace.Internal.numberOfPolesAndZeros(ss);
        numberOfZeros := noz;
      end numberOfZeros;

      encapsulated function complexZeros
          "Calculate the zeros of the related transfer function"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss "StateSpace object";
        output Complex zeros2[:]=fill(Complex(0, 0),
            StateSpace.Internal.numberOfZeros(ss));

        protected
        Integer nx=size(ss.A, 2);

        Complex zeros[:];
        Complex poles[:];
        Real eval[nx,2];
        Real evec[nx,nx];

        Integer index[:]=fill(0, nx) "indices of zeros which are equal to poles";
        Integer i;
        Integer j;
        Integer k;
        Boolean h;
        Integer nzero;

      algorithm
        if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
          assert(size(ss.B, 2) == 1,
            " function fromStateSpaceSISO expects a SISO-system as input\n but the number of inputs is "
             + String(size(ss.B, 2)) + " instead of 1");
          assert(size(ss.C, 1) == 1,
            " function fromStateSpaceSISO expects a SISO-system as input\n but the number of outputs is "
             + String(size(ss.C, 1)) + " instead of 1");
        end if;

        zeros := StateSpace.Analysis.invariantZeros( ss);

        poles := Modelica_LinearSystems2.Math.Complex.eigenValues(ss.A);

        for i in 1:size(zeros, 1) loop
          for j in 1:size(poles, 1) loop
             if zeros[i] == poles[j] then
              h := false;
              k := 1;
              while ((k < i) and (not h)) loop
                h := if (j == index[k]) then true else false;
                k := k + 1;
              end while;
              index[i] := if h then 0 else j;
            end if;
          end for;
        end for;

        j := 0;
        for i in 1:size(zeros, 1) loop
          if index[i] == 0 then
            j := j + 1;
            zeros2[j] := zeros[i];
          end if;
        end for;

      end complexZeros;

      encapsulated function numberOfPoles
          "Calculate the number of poles of the related transfer function"

          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss "StateSpace object";

        output Integer numberOfPoles=StateSpace.Internal.numberOfPolesAndZeros(ss);

      algorithm
      end numberOfPoles;

      encapsulated function numberOfPolesAndZeros
          "Calculate the number poles and of zeros of the related transfer function"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Matrices.LAPACK;

        input StateSpace ss "StateSpace object";
        output Integer numberOfPoles;
        output Integer numberOfZeros;

        protected
        Integer nx=size(ss.A, 2);
        Complex zeros[:];
        Complex zeros2[:]
            "Finite, invariant zeros of ss; size(Zeros,1) <= size(ss.A,1)";
        Complex poles[:];
        Complex poles2[:] "eigenvalues of ss";
        Real eval[nx,2];
        Real evec[nx,nx];

        Integer index[:]=fill(0, nx) "indices of zeros which are equal to poles";
        Integer i;
        Integer j;
        Integer k;
        Boolean h;
        Integer nzero;

      algorithm
        if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
          assert(size(ss.B, 2) == 1,
            " function fromStateSpaceSISO expects a SISO-system as input\n but the number of inputs is "
             + String(size(ss.B, 2)) + " instead of 1");
          assert(size(ss.C, 1) == 1,
            " function fromStateSpaceSISO expects a SISO-system as input\n but the number of outputs is "
             + String(size(ss.C, 1)) + " instead of 1");
        end if;

        zeros := StateSpace.Analysis.invariantZeros(ss);
        zeros2 := zeros;

        poles := Modelica_LinearSystems2.Math.Complex.eigenValues(ss.A);
        poles2 := poles;

      //Reduce terms which are in nominator as well as in denominator
        for i in 1:size(zeros, 1) loop
          for j in 1:size(poles, 1) loop
            if zeros[i] == poles[j] then
              h := false;
              k := 1;
              while ((k < i) and (not h)) loop
                h := if (j == index[k]) then true else false;
                k := k + 1;
              end while;
              index[i] := if h then 0 else j;
            end if;
          end for;
        end for;

        j := 0;
        for i in 1:size(zeros, 1) loop
          if index[i] == 0 then
            j := j + 1;
            zeros2[j] := zeros[i];
          end if;
        end for;
        nzero := j;
        j := 0;
        for i in 1:size(poles, 1) loop
          h := false;
          k := 1;
          while (k <= size(zeros, 1) and (not h)) loop
            h := if i == index[k] then true else false;
            k := k + 1;
          end while;
          if not h then
            j := j + 1;
            poles2[j] := poles[i];

          end if;
        end for;

        numberOfPoles := nx - size(zeros, 1) + nzero;
        numberOfZeros := nzero;

      end numberOfPolesAndZeros;

      encapsulated function complexPoles
          "Generate a zeros-and-poles representation from state space representation"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss "StateSpace object";

        output Complex poles2[:]=fill(Complex(0, 0),
            StateSpace.Internal.numberOfPoles(ss));

        protected
        Integer nx=size(ss.A, 2);

        Complex zeros[:]
            "Finite, invariant zeros of ss; size(Zeros,1) <= size(ss.A,1)";
        Complex zeros2[:];
        Complex poles[:] "eigenvalues of ss";
        Real eval[nx,2];
        Real evec[nx,nx];

        Integer index[:]=fill(0, nx) "indices of zeros which are equal to poles";
        Integer i;
        Integer j;
        Integer k;
        Boolean h;
        Integer nzero;

      algorithm
        if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
          assert(size(ss.B, 2) == 1,
            " function fromStateSpaceSISO expects a SISO-system as input\n but the number of inputs is "
             + String(size(ss.B, 2)) + " instead of 1");
          assert(size(ss.C, 1) == 1,
            " function fromStateSpaceSISO expects a SISO-system as input\n but the number of outputs is "
             + String(size(ss.C, 1)) + " instead of 1");
        end if;

        h := false;
        zeros := StateSpace.Analysis.invariantZeros( ss);
        zeros2 := zeros;

        poles := Modelica_LinearSystems2.Math.Complex.eigenValues(ss.A);

        for i in 1:size(zeros, 1) loop
          for j in 1:size(poles, 1) loop
            if zeros[i] == poles[j] then
              h := false;
              k := 1;
              while ((k < i) and (not h)) loop
                h := if (j == index[k]) then true else false;
                k := k + 1;
              end while;
              index[i] := if h then 0 else j;
            end if;
          end for;
        end for;

        j := 0;
        for i in 1:size(zeros, 1) loop
          if index[i] == 0 then
            j := j + 1;
            zeros2[j] := zeros[i];
          end if;
        end for;
        nzero := j;
        j := 0;
        for i in 1:size(poles, 1) loop
          h := false;
          k := 1;
          while (k <= size(zeros, 1) and (not h)) loop
            h := if i == index[k] then true else false;
            k := k + 1;
          end while;
          if not h then
            j := j + 1;
            poles2[j] := poles[i];

          end if;
        end for;

      end complexPoles;

      encapsulated function trianUpperHess
          "Triangulize an upper Hessenberg matrix by repeatedly applicated householder reflexion"
          import Modelica;
          import Modelica_LinearSystems2.Math.Matrices;
          import Modelica_LinearSystems2.Math.Vectors;

        input Real H[:,:];
        input Real b[size(H, 1)];

        output Real Ht[size(H, 1),size(H, 2)];
        output Real bt[size(b, 1)];

        protected
        Integer q=size(H, 1);
        Real u[:] "householder vector";
        Integer ll;

      algorithm
        Ht := H;
        bt := b;

        for ll in 1:q - 1 loop
          u := cat(
                1,
                zeros(ll - 1),
                cat(
                  1,
                  Vectors.householderVector(vector(Ht[ll:ll + 1, ll]), {1,0}),
                  zeros(q - ll - 1)));
          Ht := Matrices.householderReflexion(Ht, u);
          bt := Vectors.householderReflexion(bt, u);
        end for;

      end trianUpperHess;

      encapsulated function reducedCtrSystem2
          "calculate the controllable part of a SISO system"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Matrices;
          import Modelica_LinearSystems2.Math.Vectors;

        input StateSpace ss;
        input Real eps=0;

        protected
        StateSpace sst(
          redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
          redeclare Real B[size(ss.B, 1),size(ss.B, 2)],
          redeclare Real C[size(ss.C, 1),size(ss.C, 2)],
          redeclare Real D[size(ss.D, 1),size(ss.D, 2)])
            "tranformed state space system";

        Integer nx=size(ss.A, 1);
        Real Ah1[size(ss.A, 1),size(ss.A, 2)];
        Real bh1[size(ss.A, 1)];
        Real ch1[size(ss.A, 1)];

        Real u[:] "householder vector";
        Real cpoles[:,2]=Modelica_LinearSystems2.StateSpace.Internal.controllablePoles(ss);
        Integer rankQc=size(cpoles,1);

        Integer rankQc2;
        Real Qc2[nx,nx];
        Real sigma[:];
        Real eps2;

        Real Ah2[rankQc,rankQc];
        Real bh2[rankQc];
        Real ch2[rankQc];

        Integer ll;
        Integer r;

        Boolean h;

        public
        output StateSpace ssm1(
          redeclare Real A[rankQc,rankQc],
          redeclare Real B[rankQc,1],
          redeclare Real C[1,rankQc],
          redeclare Real D[size(ss.D, 1),size(ss.D, 2)])
            "controllable state space system";

      algorithm
        if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
          assert(size(ss.B, 2) == 1,
            "A SISO-system is expected as input\n but the number of inputs is "
             + String(size(ss.B, 2)) + " instead of 1");
          assert(size(ss.C, 1) == 1,
            " A SISO-system is expected as input\n but the number of outputs is "
             + String(size(ss.C, 1)) + " instead of 1");
        end if;

        Ah1 := ss.A;
        bh1 := ss.B[:, 1];
        ch1 := ss.C[1, :];

        if nx > 1 then

          u := Vectors.householderVector(bh1, cat(
                1,
                fill(0, nx - 1),
                {1}));

          Ah1 := Matrices.householderSimilarityTransformation(Ah1, u);

          bh1 := Vectors.householderReflexion(bh1, u);
          ch1 := Vectors.householderReflexion(ch1, u);

          ll := nx;

          h := true;
          r := 1;

          while r <= nx - 2 and h loop
            if max(Ah1[1:ll - 1, ll]) <= 1e-8 then

              u := cat(
                    1,
                    Vectors.householderVector(Ah1[1:ll - 1, ll], cat(
                      1,
                      fill(0, ll - 2),
                      {1})),
                    fill(0, nx - ll + 1));
              Ah1 := Matrices.householderSimilarityTransformation(Ah1, u);
              ch1 := Vectors.householderReflexion(ch1, u);

              Ah1[1:ll - 2, ll] := fill(0, ll - 2);

              ll := ll - 1;

            else
              h := false;

            end if;
            r := r + 1;
          end while;
        end if;

        Qc2 := cat(
              2,
              Ah1[:, 2:nx],
              matrix(bh1));
        sigma := Modelica.Math.Matrices.singularValues(Qc2);
        eps2 := if eps > 0 then eps else 1000*sigma[1]*Modelica.Constants.eps;
        rankQc2 := 0;

        for i in 1:nx loop
          Modelica.Utilities.Streams.print(" s[" + String(i) + "] = " + String(
            sigma[i]));
          if sigma[i] > eps2 then
            rankQc2 := rankQc2 + 1;
          end if;
        end for;

        Modelica.Utilities.Streams.print("rankQc = " + String(rankQc) +
          "     rankQc2 = " + String(rankQc2) + "     eps2 = " + String(eps2));

        sst := StateSpace(
              A=Ah1,
              B=matrix(bh1),
              C=transpose(matrix(ch1)),
              D=ss.D);

        Ah2 := Ah1[nx - rankQc2 + 1:nx, nx - rankQc2 + 1:nx];
        bh2 := bh1[nx - rankQc2 + 1:nx];
        ch2 := ch1[nx - rankQc2 + 1:nx];
        ssm1 := StateSpace(
              A=Ah2,
              B=matrix(bh2),
              C=transpose(matrix(ch2)),
              D=ss.D);

      end reducedCtrSystem2;

      encapsulated function controllablePoles
          "Compute the controllable and uncontrollable poles of a state space system"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Internal;

        input StateSpace ss=StateSpace(
            A=[-1],
            B=[1],
            C=[0],
            D=[0]);

        output Real cPoles[:,2] "controllable poles";
        output Real ncPoles[:,2] "uncontrollable poles";
        output Real poles[size(ss.A, 1),2] "controllable and uncontrollable poles";
        protected
        Modelica_LinearSystems2.Internal.StateSpaceR ssch(
          redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
          redeclare Real B[size(ss.B, 1),size(ss.B, 2)],
          redeclare Real C[size(ss.C, 1),size(ss.C, 2)],
          redeclare Real D[size(ss.D, 1),size(ss.D, 2)])
            "upper block controller Hessenberg form state space system";
        Boolean isControllable;

      algorithm
        if size(ss.B, 2) == 0 then
          poles := Modelica.Math.Matrices.eigenValues(ss.A);
          ncPoles := poles;
          cPoles := fill(0, 0, 2);
        else
      // build upper Hessenberg staircase to decomposite controllable/uncontrollable subspaces
      // The controllable part of A is in A[1:ssch.r, 1:ssch.r]
          (isControllable,ssch) := StateSpace.Internal.staircaseSVD(ss);
          if isControllable then
            poles := Modelica.Math.Matrices.eigenValues(ss.A);
            cPoles := poles;
            ncPoles := fill(0, 0, 2);
          else
            cPoles := Modelica.Math.Matrices.eigenValues(ssch.A[1:ssch.r, 1:ssch.r]);
            ncPoles := Modelica.Math.Matrices.eigenValues(ssch.A[ssch.r + 1:size(ss.A,
              1), ssch.r + 1:size(ss.A, 1)]);
            poles := [cPoles; ncPoles];
          end if;
        end if;

      end controllablePoles;

      encapsulated function polesAndZeros
          "Generate poles and zeros from state space representation"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Internal.PolesAndZeros;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Internal;

        input StateSpace ss "StateSpace object";
        input StateSpace ssm=Modelica_LinearSystems2.StateSpace.Transformation.toIrreducibleForm(
                                                                             ss);
        output Internal.PolesAndZeros pz(
          redeclare Real p_real[size(ssm.A, 1)],
          redeclare Real p_im[size(ssm.A, 1)],
          redeclare Real z_real[size(StateSpace.Analysis.invariantZeros(ssm), 1)],
          redeclare Real z_im[size(StateSpace.Analysis.invariantZeros(ssm), 1)]);
        protected
        Complex poles[:]=Complex.eigenValues(ssm.A);
        Complex zeros[:]=StateSpace.Analysis.invariantZeros( ssm);

      algorithm
        pz.p_real := poles[:].re;
        pz.p_im := poles[:].im;
        pz.z_real := zeros[:].re;
        pz.z_im := zeros[:].im;
        pz.norz_p := Internal.numberOfRealZeros(poles);
        pz.norz_z := Internal.numberOfRealZeros(zeros);

      end polesAndZeros;

      encapsulated function scaleFactor1
          "Return scale factor for first order block"
          import Modelica;
        input Real n "(s+n)/(s+d)";
        input Real d "(s+n)/(s+d)";
        input Real small=100*Modelica.Constants.eps;
        output Real k "= d/n, if d,n are not zero, otherwise special cases";
      algorithm
        k := (if abs(d) > small then abs(d) else 1)/(if abs(n) > small then abs(n) else
                1);
      end scaleFactor1;

      function scaleFactor2 "Return scale factor for second order block"
          import Modelica;
        input Real n1 "(s^2 + n1*s + n2)/(s^2 + d1*s + d2)";
        input Real n2 "(s^2 + n1*s + n2)/(s^2 + d1*s + d2)";
        input Real d1 "(s^2 + n1*s + n2)/(s^2 + d1*s + d2)";
        input Real d2 "(s^2 + n1*s + n2)/(s^2 + d1*s + d2)";
        input Real small=100*Modelica.Constants.eps;
        output Real k "= d2/n2, if d2,n2 are not zero, otherwise special cases";
      algorithm
        k := (if abs(d2) > small then abs(d2) else (if abs(d1) > small then abs(
          d1) else 1))/(if abs(n2) > small then abs(n2) else (if abs(n1) > small then
                abs(n1) else 1));
      end scaleFactor2;

      encapsulated function invariantZerosHessenberg
          "Fast version to calculate the system zeros of a SISO system with D=[0] and A has upper Hessenberg form, delivered by StateSpace.reduceSystem"
          import Modelica;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Math.Matrices.LAPACK;

        input StateSpace ss "Linear system in state space form";
        output Complex Zeros[:]
            "Finite, invariant zeros of ss; size(Zeros,1) <= size(ss.A,1)";

        protected
        Integer nx=size(ss.A, 1) "Number of states";
        Integer nu=size(ss.B, 2) "Number of inputs";
        Integer ny=size(ss.C, 1) "Number of outputs";
        Real Ah[nx,nx]=ss.A;
        Real eps=100*Modelica.Constants.eps;
        Integer k;
        Boolean h;

      algorithm
        assert(nu == 1, "Function invariantZeros2 requires currently a SISO-input system.\n
    This condition is however not fulfilled because the number of inputs is nu = "
           + String(nu));
        assert(ny == 1, "Function invariantZeros2 requires currently a SISO-input system.\n
    This condition is however not fulfilled because the number of outputs is ny = "
           + String(ny));

        h := true;
        k := nx + 1;

        if size(ss.B, 2) > 0 then
          while k >= 1 and h loop
            k := k - 1;
            if abs(ss.B[k, 1]) >= eps then

              h := false;
            end if;

          end while;

          Zeros := fill(Complex(0), k - 1);

          if k > 1 then
            Ah[:, k - 1] := ss.A[:, k - 1] - ss.A[k, k - 1]/ss.B[k, 1]*ss.B[:, 1];

      //    Zeros := Complex.eigenValues(Ah[1:k - 1, 1:k - 1]);
            Zeros := Complex.Internal.eigenValues_dhseqr(Ah[1:k - 1, 1:k - 1]);

            for i in 1:k - 1 loop
              if Complex.'abs'(Zeros[i]) < Modelica.Math.Matrices.norm(Ah[1:k - 1, 1:
                  k - 1], p=1)*1e-12 then
                Zeros[i] := Complex(0);
              end if;

            end for;

          end if;
        else
          Zeros := fill(Complex(0, 0), 0);
        end if;

      end invariantZerosHessenberg;

      encapsulated function cntrHessenberg
          "calculate the controllable part of a SISO system"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Matrices;
          import Modelica_LinearSystems2.Math.Vectors;
          import Modelica_LinearSystems2.Math.Complex;

        input StateSpace ss;

        output Modelica_LinearSystems2.Internal.StateSpaceR ssm1(
          redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
          redeclare Real B[size(ss.B, 1),1],
          redeclare Real C[1,size(ss.C, 2)],
          redeclare Real D[size(ss.D, 1),size(ss.D, 2)])
            "controllable state space system";

        protected
        Integer nx=size(ss.A, 1);
        Real Ah1[nx,nx];
        Real bh1[nx];
        Real ch1[nx];
        Real u[:] "householder vector";
        Real Q[nx,nx];
        Real V[size(ss.A, 1),size(ss.A, 2)];
        Real tau[nx - 1];
        Real Qc[:,:];
        Real svd[:];
        Real normA=Modelica.Math.Matrices.norm(A=ss.A, p=1);
        Integer rankMinSys;
        Boolean isZero=false;

      algorithm
        if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
          assert(size(ss.B, 2) == 1,
            "A SISO-system is expected as input\n but the number of inputs is "
             + String(size(ss.B, 2)) + " instead of 1");
          assert(size(ss.C, 1) == 1,
            " A SISO-system is expected as input\n but the number of outputs is "
             + String(size(ss.C, 1)) + " instead of 1");
        end if;

        Ah1 := ss.A;
        bh1 := ss.B[:, 1];
        ch1 := ss.C[1, :];

        if Modelica.Math.Vectors.length(bh1) > 0 then

          if nx > 1 then

              // transform b->Qb = {*,0,...,0} and c->cQ, A->QAQ
            u := Vectors.householderVector(bh1, cat(
                  1,
                  {1},
                  fill(0, nx - 1)));  //householder vector to compute a housholder reflector S = I - 2*u*u'/u'*u
            Ah1 := Matrices.householderSimilarityTransformation(Ah1, u);
            bh1 := Vectors.householderReflexion_e1(bh1, u);
            ch1 := Vectors.householderReflexion(ch1, u);

            (Ah1,V,tau) := Matrices.toUpperHessenberg(
                  Ah1,
                  1,
                  nx);
            Q := Matrices.orthogonalQ(
                  V,
                  tau,
                  1,
                  nx);
            ch1 := ch1*Q;

          end if;

          rankMinSys := 1;
          while rankMinSys < nx and not isZero loop
            isZero := abs(Ah1[rankMinSys + 1, rankMinSys]) < normA*1e-10;
            rankMinSys := rankMinSys + 1;
          end while;

          ssm1 := Modelica_LinearSystems2.Internal.StateSpaceR(
                A=Ah1,
                B=matrix(bh1),
                C=transpose(matrix(ch1)),
                D=ss.D,
                r=if isZero then rankMinSys - 1 else rankMinSys);

        end if;

      //equation

      //algorithm
      end cntrHessenberg;

      encapsulated function transposeStateSpace
          "Return the transposed state space system"

          import Modelica_LinearSystems2.StateSpace;

        input StateSpace ss;

        output StateSpace sst=StateSpace(
                A=transpose(ss.A),
                B=transpose(ss.C),
                C=transpose(ss.B),
                D=transpose(ss.D),
                uNames=ss.yNames,
                yNames=ss.uNames);
      algorithm

      end transposeStateSpace;

      encapsulated function reduceRosenbrock
          "Algorithm to compress the generalized system matrix [A, B; C, D] to calculate the invariant zeros of a system"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Matrices;
          import Modelica_LinearSystems2.Math.Vectors;

          import Modelica.Utilities.Streams.print;

        input Real A[:,:];
        input Real B[:,:];
        input Real C[:,:];
        input Real D[:,:];

        output Real Ar[:,:];
        output Real Br[:,:];
        output Real Cr[:,:];
        output Real Dr[:,:];
        output Integer n;
        output Integer m;
        output Integer p;

        protected
        Real A2[:,:];
        Real B2[:,:];
        Real C2[:,:];
        Real CC[:,:];
        Real Co[:,:];
        Real Cu[:,:];
        Real D2[:,:];
        Real DD[:,:];
        Real Mr[:,:];
        Real Vf[:,:];
        Real V[:,:];
        Real V2[:,:];
        Real R[:,:];
        Real tau[:];

        Integer nx=size(A, 1);
        Integer nu=size(B, 2);
        Integer ny=size(C, 1);

        Integer nue;
        Integer delta;
        Integer rho;
        Integer mue;
        Integer sigma;
        Integer j;
        Boolean stop;
        Boolean stop1 "reduction finished";
        Boolean stop2 "system has no zeros";
        Integer rankR;
        Real normA=Modelica.Math.Matrices.norm(A=A, p=1);
        Real eps=normA*1e-10;

      algorithm
        if nx > 0 then

          A2 := A;
          B2 := B;
          C2 := C;
          D2 := D;
          stop := false;
          stop1 := false;
          stop2 := false;
          nue := nx;
          delta := 0;
          mue := ny;
          sigma := ny;
          j := 1;

          while not stop loop
            (V,R,tau,V2) := Matrices.QR( D2);

            rankR := 0;
      //  !!!! rank has to be determined. In the case of ill conditioned systems svd should be used
            for i in 1:min(size(R, 1), size(R, 2)) loop
              if abs(R[i, size(R, 2) - min(size(R, 1), size(R, 2)) + i]) > eps then
                rankR := rankR + 1;
              end if;
            end for;

      //rankR:=Modelica.Math.Matrices.rank(R);

            DD := R[1:rankR, :];

            CC := transpose(V2)*C2;

            sigma := rankR;
            stop1 := size(CC,1) == rankR;

            if not stop1 then
              Cu := CC[sigma + 1:end, :];
              Co := CC[1:sigma, :];

              (V,R,tau,V2) := Matrices.QR( Matrices.fliplr(transpose(Cu)));
               Vf:=Matrices.fliplr(V2);

               rankR := 0;
      //  !!!! rank determination
              for i in 1:min(size(R, 1), size(R, 2)) loop
                if abs(R[i, size(R, 2) - min(size(R, 1), size(R, 2)) + i]) > eps then
                  rankR := rankR + 1;
                end if;
              end for;
      //rankR:=Modelica.Math.Matrices.rank(R);

              rho := rankR;
              stop1 := rho == 0;
              stop2 := size(Cu,2) == rankR;

              if not stop1 and not stop2 then
                nue := size(Cu, 2) - rankR;
                mue := rho + sigma;
                delta := delta + rho;

                if sigma == 0 then
                  Mr := [transpose(Vf)*A2*Vf,transpose(Vf)*B2];
                else
                  Mr := [transpose(Vf)*A2*Vf,transpose(Vf)*B2; Co*Vf,DD];
                end if;

                A2 := Mr[1:nue, 1:nue];
                B2 := Mr[1:nue, nue + rho + 1:nue + rho + nu];
                C2 := Mr[nue + 1:nue + mue,1:nue];
                D2 := Mr[nue + 1:nue + mue,nue + rho + 1:nue + rho + nu];

                j := j + 1;
              end if;
             //not stop1 or not stop2

            end if;
             //if not stop1

            stop := stop1 or stop2 or j>3*nx;

          end while;

          if stop1 then
            Ar := A2;
            Br := B2;
            Cr := C2;
            Dr := D2;
            n := nue;
            p := sigma;
            m := nu;
          else
            n := 0;
            p := 0;
            m := 0;
            Ar := fill(0,0,0);
            Br := fill(0,0,0);
            Cr := fill(0,0,0);
            Dr := fill(0,0,0);
          end if;

        else
          n := 0;
          p := 0;
          m := 0;
          A2 := A;
          B2 := B;
          C2 := C;
          D2 := D;
        end if;

      end reduceRosenbrock;

      encapsulated function reducedCtrSystem
          "calculate the controllable part of a SISO system"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Matrices;
          import Modelica_LinearSystems2.Math.Vectors;
          import Modelica_LinearSystems2.Math.Complex;

        input StateSpace ss;

        output Modelica_LinearSystems2.Internal.StateSpaceR ssm1(
          redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
          redeclare Real B[size(ss.B, 1),1],
          redeclare Real C[1,size(ss.C, 2)],
          redeclare Real D[size(ss.D, 1),size(ss.D, 2)])
            "controllable state space system";

        protected
        Integer nx=size(ss.A, 1);
        Real Ah1[nx,size(ss.A, 2)];
        Real bh1[nx];
        Real ch1[nx];
        Real u[:] "householder vector";
        Integer ll;
        Integer r=1;
        Real maxa;
        Real normA=Modelica.Math.Matrices.norm(A=ss.A, p=1);

      algorithm
        if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
          assert(size(ss.B, 2) == 1,
            "A SISO-system is expected as input\n but the number of inputs is "
             + String(size(ss.B, 2)) + " instead of 1");
          assert(size(ss.C, 1) == 1,
            " A SISO-system is expected as input\n but the number of outputs is "
             + String(size(ss.C, 1)) + " instead of 1");
        end if;

        Ah1 := ss.A;
        bh1 := ss.B[:, 1];
        ch1 := ss.C[1, :];

        if Modelica.Math.Vectors.length(bh1) > 0 then
          r := 1;
          if nx > 1 then

            u := Vectors.householderVector(bh1, cat(
                  1,
                  fill(0, nx - 1),
                  {1}));

            Ah1 := Matrices.householderSimilarityTransformation(Ah1, u);
            bh1 := Vectors.householderReflexion_en(bh1, u);
            ch1 := Vectors.householderReflexion(ch1, u);
            bh1[1:nx - 1] := fill(0, nx - 1);

            ll := nx;
            maxa := max(abs(Ah1[1:ll - 1, ll]));

            while r <= nx - 1 and maxa > nx*normA*1e-10 loop
              u := cat(
                    1,
                    Vectors.householderVector(Ah1[1:ll - 1, ll], cat(
                      1,
                      fill(0, ll - 2),
                      {1})),
                    fill(0, nx - ll + 1));

              Ah1 := Matrices.Internal.hohoTrafoLowerHess(
                    Ah1,
                    u,
                    r);

              ch1 := Vectors.householderReflexion(ch1, u);
              ll := ll - 1;
              maxa := max(abs(Ah1[1:ll - 1, ll]));

              r := r + 1;
            end while;

          end if;

          ssm1 := Modelica_LinearSystems2.Internal.StateSpaceR(
                A=Ah1,
                B=matrix(bh1),
                C=transpose(matrix(ch1)),
                D=ss.D,
                r=r);
          ssm1.r := r;
        end if;
      end reducedCtrSystem;

      encapsulated function reducedCtrSystemX
          "calculate the controllable part of a SISO system"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Matrices;
          import Modelica_LinearSystems2.Math.Vectors;
          import Modelica_LinearSystems2.Math.Complex;

        input StateSpace ss;

        output Modelica_LinearSystems2.Internal.StateSpaceR ssm1(
          redeclare Real A[size(ss.A, 1),size(ss.A, 2)],
          redeclare Real B[size(ss.B, 1),1],
          redeclare Real C[1,size(ss.C, 2)],
          redeclare Real D[size(ss.D, 1),size(ss.D, 2)])
            "controllable state space system";

        protected
        Integer nx=size(ss.A, 1);
        Real Ah1[nx,size(ss.A, 2)];
        Real bh1[nx];
        Real ch1[nx];
        Real u[:] "householder vector";
        Integer ll;
        Integer r=1;
        Real maxa;
        Real normA=Modelica.Math.Matrices.norm(A=ss.A, p=1);

      algorithm
        if size(ss.C, 1) <> 1 or size(ss.B, 2) <> 1 then
          assert(size(ss.B, 2) == 1,
            "A SISO-system is expected as input\n but the number of inputs is "
             + String(size(ss.B, 2)) + " instead of 1");
          assert(size(ss.C, 1) == 1,
            " A SISO-system is expected as input\n but the number of outputs is "
             + String(size(ss.C, 1)) + " instead of 1");
        end if;

        Ah1 := ss.A;
        bh1 := ss.B[:, 1];
        ch1 := ss.C[1, :];

        if Modelica.Math.Vectors.length(bh1) > 0 then
          r := 1;
          if nx > 1 then

            u := Vectors.householderVector(bh1, cat(
                  1,
                  fill(0, nx - 1),
                  {1}));

            Ah1 := Matrices.householderSimilarityTransformation(Ah1, u);
            bh1 := Vectors.householderReflexion_en(bh1, u);
            ch1 := Vectors.householderReflexion(ch1, u);
            bh1[1:nx - 1] := fill(0, nx - 1);

            ll := nx;
            maxa := max(abs(Ah1[1:ll - 1, ll]));

            while r <= nx - 1 and maxa > nx*normA*1e-5 loop
              u := cat(
                    1,
                    Vectors.householderVector(Ah1[1:ll - 1, ll], cat(
                      1,
                      fill(0, ll - 2),
                      {1})),
                    fill(0, nx - ll + 1));

              Ah1 := Matrices.Internal.hohoTrafoLowerHess(
                    Ah1,
                    u,
                    r);

              ch1 := Vectors.householderReflexion(ch1, u);
              ll := ll - 1;
              maxa := max(abs(Ah1[1:ll - 1, ll]));

              r := r + 1;
            end while;

          end if;

          ssm1 := Modelica_LinearSystems2.Internal.StateSpaceR(
                A=Ah1,
                B=matrix(bh1),
                C=transpose(matrix(ch1)),
                D=ss.D,
                r=r);
          ssm1.r := r;
        end if;
      end reducedCtrSystemX;

    encapsulated function assignPolesMI2
          "Pole assigment design algorithm for multi input systems"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica;
          import Modelica.Utilities.Streams.print;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Math.Matrices;

      input StateSpace ss "state space system";

      input Complex gamma[:]=fill(Complex(0), size(ss.A,1)) "Designed Poles";
      input Integer np=size(gamma, 1) "number of given eigenvalues to assign";
      input Boolean giveEigenvalues=false
            "Boolean parameter to display the order of the eigenvalues";
      input Real alpha=-1e10
            "maximum admissible value for real parts(continuous) or for moduli (discrete) of the eigenvalues of A which will not be modified by the eigenvalue assignment algorithm";
      input Real tolerance=Modelica.Math.Matrices.norm(ss.A, 1)*1e-12
            "The tolerance to be used in determining the controllability of (A,B)";
      output Real K[size(ss.B, 2),size(ss.A, 1)]
            "State feedback matrix assigning the desired poles";
      output Real S[:,:] "Closed loop System matrix";
      output Complex po[size(ss.A, 1)] "poles of the closed loop system";
      output Integer nfp
            "number of eigenvalues that are not modified with respect to alpha";
      output Integer nap "number of assigned eigenvalues";
      output Integer nup "number of uncontrollable eigenvalues";
      output Complex X[size(ss.A, 1),size(ss.A, 1)]
            "eigenvectors of the closed loop system";

        protected
      Real A_rsf[size(ss.A, 1),size(ss.A, 2)];
      Real B_rsf[size(ss.B, 1),size(ss.B, 2)];
      Real Q[size(ss.A, 1),size(ss.A, 1)];
      Real Ks1[:,:];
      Real Ks2[:,:];
      Real Q2[:,:];
      Real A_rsf_1[:,:];
      Real Q1[:,:];
      Boolean select[:];
      Boolean rselectA[:];
      Real Z[:,:] "orthogonal transformation matrix";
      Real ZT[:,:] "orthogonal transformation matrix";
      Complex pf[:];
      Complex gammaReordered[:]=gamma;
      Integer info;
      Real wr[size(gamma, 1)];
      Real wi[size(gamma, 1)];
      Boolean imag=false;
      Integer i;
      Integer ii;
      Integer counter;
      Integer n=size(ss.A, 1);
      Integer ipf;
      Integer nccA "number of conjugated complex pole pairs of unmodified system";
      Integer nccg "number of conjugated complex pole pairs of gamma";
      Integer ncc;

      Complex h;
      Integer h2[2];
      Real alphaReal[size(ss.A, 1)] "Real part of eigenvalue=alphaReal+i*alphaImag";
      Real alphaImag[size(ss.A, 1)]
            "Imaginary part of eigenvalue=(alphaReal+i*alphaImag";

      Complex pi[:]=Complex.eigenValues(ss.A);

      Boolean complex_assignedPoles=false
            "true if there is at least one conjugated comples pole pair in the set of the assigned poles";
      Boolean complex_originalPoles=false
            "true if there is at least one conjugated comples pole pair in the set of unmodified system poles";
      Boolean consistency;

      Complex SS[:,:];
      Complex Xj[:,:];

      Integer markA[n]=fill(1,n);
      Integer markg[n]=fill(1,n);

      Complex ev[:];

    algorithm
      if giveEigenvalues then
        (,,alphaReal,alphaImag,info) :=
          Modelica_LinearSystems2.Math.Matrices.LAPACK.dgees(ss.A);
        assert(info == 0, "The output info of LAPACK.dgees should be zero, else if\n
         info < 0:  if info = -i, the i-th argument of dgees had an illegal value\n
         info > 0:  if INFO = i, and i is
                   <= N: the QR algorithm failed to compute all the
                         eigenvalues; elements 1:ILO-1 and i+1:N of WR and WI
                         contain those eigenvalues which have converged; if
                         JOBVS = 'V', VS contains the matrix which reduces A
                         to its partially converged Schur form.\n");
        for i in 1:n loop
          po[i].re := alphaReal[i];
          po[i].im := alphaImag[i];
        end for;
        Complex.Vectors.print("The eigenvalues of the open loop system are sorted to\n eigenvalues", pi);
      else
        assert(size(gamma, 1) <= size(ss.A, 1), "At most n (order of ss) eigenvalues can be assigned");

     /* Extraction of Poles (Variable conversation) and pole sequence check */
      for i in 1:size(gamma, 1) loop
        wr[i] := gamma[i].re;
        wi[i] := gamma[i].im;
        if imag then
          assert(wi[i - 1] == -wi[i] and wr[i - 1] == wr[i],
            "Poles are in wrong sequence");
          imag := false;
        elseif wi[i] <> 0 then
          imag := true;
        end if;
      end for;

      // put matrix ss.A to real Schur form A <- QAQ' and compute B <- QB
      (A_rsf,Z,alphaReal,alphaImag) := Matrices.rsf2(ss.A);
      ZT := transpose(Z);

      // determine number of poles not to be assigned according to alpha
         nfp := 0;
         nccA := 0;
         nccg := 0;
         for i in 1:n loop
           if alphaReal[i] < alpha then
             nfp := nfp + 1;
             markA[i] := 0;
             markg[i] := 0;
           end if;
           if abs(alphaImag[i]) > 0 then
             markA[i] := 2;
             nccA := nccA + 1;
           end if;
           if abs(gammaReordered[i].im) > 0 then
             markg[i] := 2;
             nccg := nccg + 1;
           end if;
         end for;
         nap := n - nfp;
         nccA := div(nccA, 2);
         nccg := div(nccg, 2);
         ncc := max(nccA, nccg);

       // reorder real Schur form according to alpha
       (A_rsf,Z,alphaReal,alphaImag) := Matrices.Internal.reorderRSF2(
           A_rsf,
           identity(size(A_rsf, 1)),
           alphaReal,
           alphaImag,
           alpha);
       ZT := transpose(Z)*ZT;
       B_rsf := ZT*ss.B;

        Modelica_LinearSystems2.Math.Vectors.printVector(alphaReal,6,"alphaReal");
        Modelica_LinearSystems2.Math.Vectors.printVector(alphaImag,6,"alphaImag");
       Modelica_LinearSystems2.Math.Complex.Vectors.print("gammaReordered1",gammaReordered);

      // Reorder gammaReordered according to alpha
        ii := 1;
        for i in 1:n loop
          if markg[i]==0 then
            h := gammaReordered[ii];
            gammaReordered[ii] := gammaReordered[i];
            gammaReordered[i] := h;
            ii := ii+1;
          end if;
        end for;

       // check consistency of poles assignment, i.e. complex pole pairs in gammaReordered and alphaReal and alphaImag may not be separated
        consistency:=true;
        i:=1;
        while i<n loop
          if markA[i]==2 then
            consistency :=markA[i + 1] == 2 and ((markg[i] == 2 and markg[i + 1] == 2)
              or (markg[i] == 1 and markg[i + 1] == 1));
            i := i+2;
          elseif markg[i]==2 then
            consistency :=markg[i + 1] == 2 and (markA[i] == 1 and markA[i + 1] == 1);
            i := i+2;
          else
            i := i+1;
          end if;
        end while;
       assert(consistency,"System poles and assigned poles have to be assigned consistently, i.e. complex pole pairs may not be separated");

      // main algorithm
      K := zeros(size(ss.B, 2), size(ss.A, 1));
      counter := nfp + 1;
       while counter<=n loop
        if markA[n + nfp + 1 - counter] == 2 or markg[n + nfp + 1 - counter] == 2 then

    //#############################
    Modelica_LinearSystems2.Math.Complex.Vectors.print("g2",gammaReordered[n + nfp - counter:n + nfp + 1 - counter]);
    Modelica_LinearSystems2.Math.Vectors.printVector(alphaReal[n + nfp - counter:n + nfp + 1 - counter],6,"ar2");
    ev:=Modelica_LinearSystems2.Math.Complex.eigenValues( A_rsf[n - 1:n, n - 1:n]);
    Modelica_LinearSystems2.Math.Complex.Vectors.print("ev2",ev);
    //#############################

          Ks2 := StateSpace.Internal.assignOneOrTwoPoles(
            A_rsf[n - 1:n, n - 1:n],
            matrix(B_rsf[n - 1:n, :]),
            gammaReordered[n + nfp - counter:n + nfp +
            1 - counter],
            tolerance);
          K := K + [zeros(size(Ks2, 1), size(K, 2) - 2),Ks2]*ZT;
          A_rsf := A_rsf - B_rsf*[zeros(size(Ks2, 1), size(K, 2) - 2),Ks2];
          select := fill(false, n - counter + 1);
          select[n - counter:n - counter + 1] := {true,true};

          (A_rsf[counter:n, counter:n],Q2) := Matrices.LAPACK.dtrsen(
            "E",
            "V",
            select,
            A_rsf[counter:n, counter:n],
            identity(n - counter + 1));  //The Schur vector matrix is identity, since A_rsf already has Schur form

          A_rsf[1:counter - 1, counter:n] := A_rsf[1:counter - 1, counter:n]*Q2;
          B_rsf[counter:n, :] := transpose(Q2)*B_rsf[counter:n, :];
          ZT[counter:n, :] := transpose(Q2)*ZT[counter:n, :];
    //       h2 := markA[counter:counter + 1];
    //       markA[counter:counter + 1] := markA[n - 1:n];
    //       markA[n - 1:n] := h2;
    //       h2 := markg[counter:counter + 1];
    //       markg[counter:counter + 1] := markg[n - 1:n];
    //       markg[n - 1:n] := h2;
    //       h := gammaReordered[n - 1];
    //       gammaReordered[n - 1] := gammaReordered[counter];
    //       gammaReordered[counter] := gammaReordered[n - 1];
    //       h := gammaReordered[n];
    //       gammaReordered[n] := gammaReordered[counter + 1];
    //       gammaReordered[counter + 1] := gammaReordered[n];
          counter := counter + 2;
        else
    //#############################
    print("g1 = "+String(gammaReordered[n + nfp + 1 - counter]));
    print("ar1 = "+String(alphaReal[n + nfp + 1 - counter]));
    print("ev1 = "+String(A_rsf[n,n])+"\n");
    //#############################

          Ks1 := StateSpace.Internal.assignOneOrTwoPoles(
            matrix(A_rsf[n, n]),
            transpose(matrix(B_rsf[n, :])),
            {gammaReordered[n + nfp + 1 - counter]},
            tolerance);

          K := K + [zeros(size(Ks1, 1), size(K, 2) - 1),Ks1]*ZT;
          A_rsf := A_rsf - B_rsf*[zeros(size(Ks1, 1), size(K, 2) - 1),Ks1];
          select := fill(false, n - counter + 1);
          select[n - counter + 1] := true;

          (A_rsf[counter:n, counter:n],Q1) := Matrices.LAPACK.dtrsen(
            "E",
            "V",
            select,
            A_rsf[counter:n, counter:n],
            identity(n - counter + 1)); //The Schur vector matrix is identity, since A_rsf already has Schur form

          A_rsf[1:counter - 1, counter:n] := A_rsf[1:counter - 1, counter:n]*Q1;
          B_rsf[counter:n, :] := transpose(Q1)*B_rsf[counter:n, :];
          ZT[counter:n, :] := transpose(Q1)*ZT[counter:n, :];
    //       h2[1] := markA[counter];
    //       markA[counter] := markA[n];
    //       markA[n] := h2[1];
    //       h2[1] := markg[counter];
    //       markg[counter] := markg[n];
    //       markg[n] := h2[1];
    //       h := gammaReordered[n];
    //       gammaReordered[n] := gammaReordered[counter];
    //       gammaReordered[counter] := gammaReordered[n];
          counter := counter + 1;
        end if;

        end while;

      S := ss.A - ss.B*K;
      po := Complex.eigenValues(S);
       X := Complex.eigenVectors(S);

    //    X := fill(Complex(0),n,n);
    //    for i in 1:n loop
    //      SS:=Complex(1)*S;
    //      for ii in 1:n loop
    //        SS[ii,ii] := SS[ii,ii]-po[i];
    //      end for;
    //      Xj := Modelica_LinearSystems2.WorkInProgress.Math.Matrices.C_nullspace(
    //                                 SS);
    //      // Matrices.printMatrix(Complex.real(Xj),6,"ReXj");
    //      // Matrices.printMatrix(Complex.imag(Xj),6,"ImXj");
    //      for ii in 1:n loop
    //        X[ii,i] := Xj[ii,1];
    //      end for;
    //    end for;

    end if;

    end assignPolesMI2;

    end Internal;

    end StateSpace;

## Modelica_LinearSystems2.TransferFunction

Continuous transfer function description of a single input, single output system (data + operations)

### Information

This record defines the transfer function between the input signal u and the output signal y by the coefficients of the numerator and denominator polynomials n(s) and d(s) respectively:

            n(s)
       y = ------ * u
            d(s)

The order of the numerator polynomial can be larger as the order of the denominator polynomial (in such a case, the transfer function can not be transformed to a StateSpace system, but other operations are possible).

Example: The transfer function

                 2*s+3
       y = ----------------- * u
            4*s^2 + 5*s + 6

is transformed in the following way in a TransferFunction record:

       import Modelica_LinearSystems2.TransferFunction;
       import Modelica.Utilities.Streams;
       TransferFunction tf(n={2,3}, d={4,5,6});
       print("y = " + TransferFunction.'String'(tf) + " * u");
       // prints the following string to the output window:
       //   y = (2*s + 3) / (4*s^2 + 5*s + 6) * u

Extends from Modelica.Icons.Record (Icon for a record).

### Parameters

Type | Name | Default | Description
--- | --- | --- | ---
y = n*{s^m, ... , s, 1} / (d*{s^r, ... , s, 1}) * u
Real | n[:] |  | Coefficients of numerator polynomial (in descending order)
Real | d[:] |  | Coefficients of denominator polynomial (in descending order)
Signal names
String | uName | "u" | Name of input signal
String | yName | "y" | Name of output signal

### Modelica definition

    record TransferFunction
      "Continuous transfer function description of a single input, single output system (data + operations)"

      extends Modelica.Icons.Record;
      import Modelica_LinearSystems2.Math.Polynomial;

      Real n[:] "Coefficients of numerator polynomial (in descending order)";
      Real d[:] "Coefficients of denominator polynomial (in descending order)";

      String uName="u" "Name of input signal";
      String yName="y" "Name of output signal";

    /* If the numerator polynomial has no coefficients, the transfer function
       is zero. The denominator polynomial must always have at
       least one coefficient, such as {1}
    */

      encapsulated operator 'constructor'
        "Default constructor for a transfer function"
        import Modelica;
        import Modelica_LinearSystems2.TransferFunction;

        function fromReal
          "Generate a TransferFunction data record from a Real value"
          import Modelica;
          import Modelica_LinearSystems2.TransferFunction;

          input Real r "Value of Real variable";
          input String uName="" "input name";
          input String yName="" "output name";
          output TransferFunction tf(n={r}, d={1});

        algorithm
          tf.uName := uName;
          tf.yName := yName;
        end fromReal;

      encapsulated function fromZerosAndPoles
          "Generate a transfer function from a set of zeros and poles"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.Math.Complex;

        input Complex z[:]=fill(Modelica_LinearSystems2.Math.Complex(0), 0)
            "Zeros (Complex vector of numerator zeros)";
        input Complex p[:]=fill(Modelica_LinearSystems2.Math.Complex(0), 0)
            "Poles (Complex vector of denominator zeros)";
        input Real k=1.0 "Constant multiplied with transfer function";
        input String uName="" "input name";
        input String yName="" "output name";
        output TransferFunction tf(redeclare Real n[size(z, 1)+1], redeclare Real d[size(p, 1)+1])
            "TransferFunction built by ZerosAndPoles object";

        protected
        Polynomial pn=k*Polynomial(z);
        Polynomial pd=Polynomial(p);
      algorithm

      tf.n := pn.c;
        tf.d := pd.c;
        tf.uName := uName;
        tf.yName := yName;

      end fromZerosAndPoles;

        encapsulated function fromArrays
          "Generate a TransferFunction data record from numerator and denominator array"
          import Modelica;
          import Modelica_LinearSystems2.TransferFunction;

               input Real n[:] "Coefficients of numerator polynomial";
               input Real d[:] "Coefficients of denominator polynomial";
               input String uName = "" "input name";
               input String yName = "" "output name";

               output TransferFunction tf(redeclare Real n[size(n, 1)], redeclare Real
              d[                                                                         size(d, 1)])
            "Transfer function";

        algorithm
                     //this is the constructor algorithm
               assert(size(d, 1) > 0, "Input denominator d must have at least one element, however\n"
                  + "d is an empty vector");
               tf.n := n;
               tf.d := d;
               tf.uName := uName;
               tf.yName := yName;
        end fromArrays;

        function fromPolynomials
          "Generate a TransferFunction data record from a numerator and denominator polynomial"
          import Modelica;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Math.Polynomial;

          input Polynomial n "Numerator polynomial";
          input Polynomial d "Denominator polynomial";
          input String uName="" "input name";
          input String yName="" "output name";
          output TransferFunction tf(n=n.c, d=d.c,uName=uName, yName=yName);

        algorithm

        end fromPolynomials;

      end 'constructor';

      encapsulated operator '-'
        function subtract "Subtract two TransferFunctions (tf1 - tf2)"
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.TransferFunction;

          input TransferFunction tf1;
          input TransferFunction tf2;

          output TransferFunction result = (Polynomial(tf1.n)*Polynomial(tf2.d) - Polynomial(tf2.n)*Polynomial(tf1.d))/(Polynomial(tf1.d)*Polynomial(tf2.d));

        algorithm
        end subtract;

        function negate "Unary minus (multiply transfer function by -1)"
          import Modelica_LinearSystems2.TransferFunction;

          input TransferFunction tf;
          output TransferFunction result(n=-tf.n, d=tf.d) "= -tf";
        algorithm
        end negate;
      end '-';

      encapsulated operator function '+'
        "Parallel connection of two transfer functions (= inputs are the same, outputs of the two systems are added)"
         import Modelica_LinearSystems2.Math.Polynomial;
         import Modelica_LinearSystems2.TransferFunction;

         input TransferFunction tf1 "Transfer function system 1";
         input TransferFunction tf2 "Transfer function system 2";
         output TransferFunction result;
      algorithm
          result := (Polynomial(tf1.n)*Polynomial(tf2.d) + Polynomial(tf2.n)*Polynomial(tf1.d))/(Polynomial(tf1.d)*Polynomial(tf2.d));
      end '+';

      encapsulated operator function '*'
        "Multiply two TransferFunctions (tf1 * tf2)"
         import Modelica_LinearSystems2.Math.Polynomial;
         import Modelica_LinearSystems2.TransferFunction;

         input TransferFunction tf1 "Transfer function system 1";
         input TransferFunction tf2 "Transfer function system 1";
         output TransferFunction result;
      algorithm
         result := (Polynomial(tf1.n)*Polynomial(tf2.n))/(Polynomial(tf1.d)*Polynomial(tf2.d));
      end '*';

      encapsulated operator function '/'
        "Divide two transfer functions (tf1 / tf2)"
         import Modelica_LinearSystems2.Math.Polynomial;
         import Modelica_LinearSystems2.TransferFunction;

         input TransferFunction tf1 "Transfer function system 1";
         input TransferFunction tf2 "Transfer function system 1";
         output TransferFunction result;
      algorithm
        result := (Polynomial(tf1.n)*Polynomial(tf2.d))/(Polynomial(tf1.d)*
          Polynomial(tf2.n));
      end '/';

      encapsulated operator function '^'
        "Integer power of TransferFunction (tf1^k)"
         import Modelica_LinearSystems2.Math.Polynomial;
         import Modelica_LinearSystems2.TransferFunction;

         input TransferFunction tf "Transfer function";
         input Integer k(min=0) = 1 "Integer exponent";
         output TransferFunction result;
      algorithm
        result := (Polynomial(tf.n)^k)/(Polynomial(tf.d)^k);
      end '^';

      encapsulated operator function '=='
        "Check whether two transfer functions are identical"
         import Modelica_LinearSystems2.Math.Polynomial;
         import Modelica_LinearSystems2.TransferFunction;

         input TransferFunction tf1 "Transfer function system 1";
         input TransferFunction tf2 "Transfer function system 1";
         input Real eps(min=0) = 0
          "Two coefficients c1 and c2 of the two transfer functions are identical if abs(c1-c2) <= eps";

         output Boolean result "= tf1 == tf2";
      algorithm
        result := (Polynomial(tf1.n) == Polynomial(tf2.n)) and (Polynomial(tf1.d) == Polynomial(tf2.d));
      end '==';

      encapsulated operator function 'String'
        "Transform TransferFunction into a String representation"
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.TransferFunction;

          input TransferFunction tf
          "Transfer function to be transformed in a String representation";
          input Integer significantDigits=6
          "Number of significant digits that are shown";
          input String name="s" "Independent variable name used for printing";
          output String s="";
      protected
          Integer n_num=size(tf.n, 1) - 1;
          Integer n_den=size(tf.d, 1) - 1;
          Boolean numParenthesis;
      algorithm
          if n_num == -1 then
            s := "0";
          else
            numParenthesis := n_num > 0 and not (n_den == 0 and tf.d[1] == 1);
            if numParenthesis then
              s := "(";
            end if;
             s := s + String(
                  Polynomial(tf.n),
                  significantDigits,
                  name);

            if numParenthesis then
              s := s + ")";
            end if;
          if n_den > 0 or tf.d[1] <> 1 then
              if n_den > 0 then
                s := s + "/(";
              else
                s := s + "/";
              end if;

              s := s + String(
                    Polynomial(tf.d),
                    significantDigits,
                    name);

              if n_den > 0 then
                s := s + ")";
              end if;
            end if;
          end if;
      end 'String';

      encapsulated function s "Generate the transfer function s"
        import Modelica_LinearSystems2.Math.Polynomial;
        import Modelica_LinearSystems2.TransferFunction;

        output TransferFunction tf(n={1,0}, d={1}) "s";
      algorithm

      end s;

      encapsulated package Analysis
        function analysis
          "Make a system analysis based on the poles and zeros of the system"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Internal.AnalyseOptions;
          import Modelica_LinearSystems2.Internal.AnalyseOptions2;
          import Modelica_LinearSystems2.Internal.Eigenvalue;

          input TransferFunction tf(uName="u", yName="y")
            "transfer function of a system";

          input AnalyseOptions2 analyseOptions2=
              Modelica_LinearSystems2.Internal.AnalyseOptions2(printControllability=
               false, printObservability=false);

          input String fileName="eigenvalues.html"
            "Name of html-file that contains eigenvalue table";

          input String systemName="tf"
            "Name of system (used as heading in html file)";
          input String description="" "Description of system (used in html file)";

        protected
          input Boolean printStateSpaceSystem=true;
          String dummyFileName = "dummy" + fileName;

          input StateSpace ss=StateSpace(tf);

          Modelica_LinearSystems2.Internal.AnalyseOptions analyseOptions=
              AnalyseOptions(
                  plotEigenValues=analyseOptions2.plotEigenValues,
                  plotInvariantZeros=analyseOptions2.plotInvariantZeros,
                  plotStepResponse=analyseOptions2.plotStepResponse,
                  plotFrequencyResponse=analyseOptions2.plotFrequencyResponse,
                  printEigenValues=analyseOptions2.printEigenValues,
                  printEigenValueProperties=analyseOptions2.printEigenValueProperties,
                  printInvariantZeros=analyseOptions2.printInvariantZeros,
                  printControllability=analyseOptions2.printControllability,
                  printObservability=analyseOptions2.printObservability,
                  headingEigenValues=analyseOptions2.headingEigenValues,
                  headingInvariantzeros=analyseOptions2.headingInvariantzeros,
                  headingStepResponse=analyseOptions2.headingStepResponse,
                  headingFrequencyResponse=analyseOptions2.headingFrequencyResponse);

        algorithm
          assert(TransferFunction.Analysis.denominatorDegree(tf) >
            TransferFunction.Analysis.numeratorDegree(tf),
            " Denominator polynominal of transfer function in function\"TransferFunction.Analysis.analysis\"has to be of higher order than numerator polynomial");

         Modelica.Utilities.Files.removeFile(fileName);
         Modelica.Utilities.Files.removeFile(dummyFileName);
        if printStateSpaceSystem then
        Modelica_LinearSystems2.TransferFunction.Analysis.analysis.printSystem(
                tf,
                fileName,
                systemName,
                description);
        Modelica_LinearSystems2.TransferFunction.Analysis.analysis.printSystem(
                tf,
                dummyFileName,
                systemName,
                description);
        end if;
          Modelica.Utilities.Streams.readFile(dummyFileName);

          StateSpace.Analysis.analysis(
                ss=ss,
                analyseOptions=analyseOptions,
                fileName=fileName,
                systemName=systemName,
                description=description,
                printStateSpaceSystem=false);
        equation

        public
          encapsulated function printSystem
            "Print the state space system in html format on file"
            import Modelica;
            import Modelica.Utilities.Streams.print;
            import Modelica_LinearSystems2.TransferFunction;
            import Modelica_LinearSystems2;

            input TransferFunction tf "transfer function to analyze";
            input String fileName="systemAnalysis.html"
              "File on which the transfer fucntion is written in html format";
            input String systemName="Transfer Function" "name of the system";
            input String description="" "Description of system (used in html file)";
            input String format=".3g" "Format of numbers (e.g. \"20.8e\")";
          protected
            String st=String(tf);

          algorithm
            Modelica.Utilities.Files.removeFile(fileName);
            print("

System report

"Error:Found no end-tag in HTML-documentation

    , fileName);
            print("<p> The system " + systemName + " is defined by</p>", fileName);
            print("G(s) = " + st, fileName);
             if description == "" then
              print("</table> ", fileName);
            else
              print("</table>", fileName);
              print("<p><b>Description</b></p>", fileName);
              print(description, fileName);
            end if;
            print("<br></body></html>",fileName);

          end printSystem;

        end analysis;
        import Modelica;
        import Modelica_LinearSystems2;
        import Modelica_LinearSystems2.StateSpace;
        import Modelica_LinearSystems2.TransferFunction;
        import Modelica_LinearSystems2.Types.TimeResponse;

       encapsulated function timeResponse
          "Calculate the time response of a transfer function"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;

        extends Modelica_LinearSystems2.Internal.timeResponseMask2_tf;     // Input/Output declarations of time response functions
        input Modelica_LinearSystems2.Types.TimeResponse response=Modelica_LinearSystems2.Types.TimeResponse.Step;

        input Real x0[TransferFunction.Analysis.denominatorDegree(tf)]=zeros(TransferFunction.Analysis.denominatorDegree(tf))
            "Initial state vector";

        protected
         StateSpace ss=StateSpace(tf);

       algorithm
         (y,t,x_continuous) := StateSpace.Analysis.timeResponse(sc=ss, dt=dt, tSpan=tSpan, response=response, x0=x0);

       end timeResponse;

      encapsulated function impulseResponse
          "Calculate the impulse time response"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;

          // Input/Output declarations of time response functions:
        extends Modelica_LinearSystems2.Internal.timeResponseMask2_tf;

      algorithm
        (y,t,x_continuous) := Modelica_LinearSystems2.TransferFunction.Analysis.timeResponse(
            tf=tf,
            dt=dt,
            tSpan=tSpan,
            response=Modelica_LinearSystems2.Types.TimeResponse.Impulse,
            x0=zeros(Modelica_LinearSystems2.TransferFunction.Analysis.denominatorDegree(tf)));

      end impulseResponse;

      encapsulated function stepResponse
          "Calculate the step time response"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;
          // Input/Output declarations of time response functions:
        extends Modelica_LinearSystems2.Internal.timeResponseMask2_tf;

      algorithm
        (y,t,x_continuous) := Modelica_LinearSystems2.TransferFunction.Analysis.timeResponse(
            tf=tf,
            dt=dt,
            tSpan=tSpan,
            response=Modelica_LinearSystems2.Types.TimeResponse.Step,
            x0=zeros(Modelica_LinearSystems2.TransferFunction.Analysis.denominatorDegree(tf)));

      end stepResponse;

      encapsulated function rampResponse
          "Calculate the ramp time response"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;

          // Input/Output declarations of time response functions:
        extends Modelica_LinearSystems2.Internal.timeResponseMask2_tf;

      algorithm
        (y,t,x_continuous) := Modelica_LinearSystems2.TransferFunction.Analysis.timeResponse(
            tf=tf,
            dt=dt,
            tSpan=tSpan,
            response=Modelica_LinearSystems2.Types.TimeResponse.Ramp,
            x0=zeros(Modelica_LinearSystems2.TransferFunction.Analysis.denominatorDegree(tf)));

      end rampResponse;

      encapsulated function initialResponse
          "Calculate the time response for given initial condition and zero inputs"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;

        input Real x0[:]=fill(0,0) "Initial state vector";

          // Input/Output declarations of time response functions:
        extends Modelica_LinearSystems2.Internal.timeResponseMask2_tf;

      algorithm
        (y,t,x_continuous) := Modelica_LinearSystems2.TransferFunction.Analysis.timeResponse(
            tf=tf,
            dt=dt,
            tSpan=tSpan,
            response=Modelica_LinearSystems2.Types.TimeResponse.Initial,
            x0=x0);

      end initialResponse;

        encapsulated function numeratorDegree "Return numerator degree"
          import Modelica;
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.TransferFunction;

          input TransferFunction tf "transfer function of a system";
          output Integer result;

        algorithm
          result := size(tf.n,1)-1;
        end numeratorDegree;

        encapsulated function denominatorDegree
          "Return denominator degree"
          import Modelica;
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.TransferFunction;

          input TransferFunction tf "transfer function of a system";
          output Integer result;

        algorithm
          result := size(tf.d,1)-1;
        end denominatorDegree;

        encapsulated function evaluate
          "Evaluate a transfer function for a given (Complex) value of s"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.TransferFunction;

          input TransferFunction tf "transfer function of a system";
          input Complex s "Value of s where tf shall be evaluated";
          input Real den_min=0 "|denominator(s)| is limited by den_min";
          output Complex result "= tf(s)";

        protected
          Complex j = Modelica_LinearSystems2.Math.Complex.j();
         Complex den=Polynomial.evaluateComplex(Polynomial(tf.d), s);
          Real abs_den=Complex.'abs'(den);
        algorithm
          den := if abs_den >= den_min then den else -abs_den+0*j;
          result := Polynomial.evaluateComplex(Polynomial(tf.n), s)/den;
        end evaluate;

        encapsulated function zerosAndPoles
          "Calculate zeros and poles of a transfer function"
          import Modelica;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.TransferFunction;

          input TransferFunction tf "transfer function of a system";

          output Complex z[:]=Polynomial.roots(Polynomial(tf.n))
            "Zeros (Complex vector of numerator zeros)";
          output Complex p[:]=Polynomial.roots(Polynomial(tf.d))
            "Poles (Complex vector of denominator zeros)";
          output Real k
            "Constant multiplied with transfer function that is factorized with zeros and poles";

        protected
          TransferFunction tf2=TransferFunction(z, p);
          Real r;
          Complex s;
          Complex y1;
          Complex y2;
        algorithm
          // Determine an s-value that is neither a zero nor a pole
          r := 1.0;
          for i in 1:size(z, 1) loop
            r := max(r, abs(z[i].re));
          end for;
          for i in 1:size(p, 1) loop
            r := max(r, abs(p[i].re));
          end for;
          r := 2*r;
          s := Complex(r, 0);

          // Evaluate both tf and tf2 and determine k from the quotient
          y1 := TransferFunction.Analysis.evaluate(tf, s);
          y2 := TransferFunction.Analysis.evaluate(tf2, s);
          k := y1.re/y2.re;
        end zerosAndPoles;

        function eigenValues
          "Calculate the eigenvalues of a linear transfer function system and write them in a complex vector"
        //encapsulated function eigenValues
          import Modelica;
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.Math.Complex;

          input TransferFunction tf "transfer function of a system";
          output Complex eigval[:] "eigen values of the system";

        protected
          StateSpace ss=StateSpace(tf);

        algorithm
          assert(TransferFunction.Analysis.denominatorDegree(tf) >
            TransferFunction.Analysis.numeratorDegree(tf),
            " Denominator polynominal of transfer function in function\"TransferFunction.Analysis.eigenValues\"has to be of higher order than numerator polynomial");

          eigval := StateSpace.Analysis.eigenValues(ss);

        end eigenValues;

        encapsulated function eigenVectors
          "Calculate the right eigenvectors of the state space system corresponding to a transfer function and write them columnwise in a matrix. Optionally, the eigenvalues are computed"
          import Modelica;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica.Math.Matrices.LAPACK;
          import Modelica_LinearSystems2.Math.Complex;

          input TransferFunction tf "transfer function of a system";
          input Boolean onlyEigenvectors=true;
          output Real eigvec[:,:] "eigen values of the system";
          output Complex eigval[:] "eigen values of the system";
        protected
          StateSpace ss=StateSpace(tf);

        algorithm
          assert(TransferFunction.Analysis.denominatorDegree(tf) >
            TransferFunction.Analysis.numeratorDegree(tf),
            " Denominator polynominal of transfer function in function\"TransferFunction.Analysis.eigenVectors\"has to be of higher order than numerator polynomial");
          (eigvec,eigval) := StateSpace.Analysis.eigenVectors(ss=ss,
            onlyEigenvectors=onlyEigenvectors);

        end eigenVectors;

        encapsulated function invariantZeros
          "Compute invariant zeros of linear transfer function"

          import Modelica;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.StateSpace;

          input TransferFunction tf "transfer function of a system";

          output Complex Zeros[:] "invariant zeros";

        protected
          StateSpace ss=StateSpace(tf);

        algorithm
          Zeros := StateSpace.Analysis.invariantZeros(ss);

        end invariantZeros;

        encapsulated function observabilityMatrix
          "Calculate the observability matrix of a transfer function"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;

          input TransferFunction tf "transfer function of a system";
          output Real om[:,:];

        protected
          StateSpace ss=StateSpace(tf);

        algorithm
          om := StateSpace.Analysis.observabilityMatrix(ss=ss);

        end observabilityMatrix;

        encapsulated function controllabilityMatrix
          "Calculate the controllability matrix [B, A*B, ..., A^(n-1)*B] of a transfer function"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;

          input TransferFunction tf "transfer function of a system";
          output Real om[:,:];

        protected
          StateSpace ss=StateSpace(tf);

        algorithm
          om := StateSpace.Analysis.controllabilityMatrix(ss=ss);

        end controllabilityMatrix;

        encapsulated function isObservable
          "Check oberservability of a transfer function"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;

            input TransferFunction tf "transfer function of a system";
            input Modelica_LinearSystems2.Types.StaircaseMethod method=
                Modelica_LinearSystems2.Types.StaircaseMethod.SVD;
            output Boolean observable;
        protected
            StateSpace ss=StateSpace(tf);

        algorithm
            observable := StateSpace.Analysis.isObservable(ss=ss, method=method);

        end isObservable;

        encapsulated function isControllable
          "Check controllability of a transfer function"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;

            input TransferFunction tf "transfer function of a system";
            input Modelica_LinearSystems2.Types.Method method=Modelica_LinearSystems2.Types.StaircaseMethod.SVD;

            output Boolean controllable;
        protected
            StateSpace ss=StateSpace(tf);

        algorithm
            controllable := StateSpace.Analysis.isControllable(ss=ss, method=method);

        end isControllable;

        encapsulated function isStabilizable
          "Check stabilizability of a transfer function"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;

          input TransferFunction tf "transfer function of a system";

          output Boolean stabilizable;
        protected
          StateSpace ss=StateSpace(tf);

        algorithm
          stabilizable := StateSpace.Analysis.isStabilizable(ss=ss);

        end isStabilizable;

        encapsulated function isDetectable
          "Check detectability of a transfer function"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;

          input TransferFunction tf "transfer function of a system";

          output Boolean detectable;

        protected
          StateSpace ss=StateSpace(tf);

        algorithm
          detectable := StateSpace.Analysis.isDetectable(ss=ss);

        end isDetectable;

      end Analysis;

      encapsulated package Design
        encapsulated function filter
          "Generate the data record of a ZerosAndPoles transfer function from a filter description"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input Modelica_LinearSystems2.Types.AnalogFilter analogFilter=Modelica_LinearSystems2.Types.AnalogFilter.CriticalDamping
            "Analog filter characteristics (CriticalDamping/Bessel/Butterworth/Chebyshev)";
          input Modelica_LinearSystems2.Types.FilterType filterType=Modelica_LinearSystems2.Types.FilterType.LowPass
            "Type of filter (LowPass/HighPass)";
          input Integer order(min=1) = 2 "Order of filter";
          input Modelica.SIunits.Frequency f_cut=1/(2*Modelica.Constants.pi)
            "Cut-off frequency (default is w_cut = 1 rad/s)";
          input Real gain=1.0
            "Gain (= amplitude of frequency response at zero frequency)";
          input Real A_ripple(unit="dB") = 0.5
            "Pass band ripple for Chebyshev filter (otherwise not used)";
          input Boolean normalized=true
            "= true, if amplitude of low pass filter at f_cut where the magnitude of the filter sagged for 3dB, otherwise unmodified filter";

          output TransferFunction filter "Filter transfer function";

        protected
          ZerosAndPoles zpFilter;
        algorithm
          zpFilter := ZerosAndPoles.Design.filter(analogFilter, filterType, order, f_cut, gain, A_ripple, normalized);
          filter := ZerosAndPoles.Conversion.toTransferFunction(zpFilter);

        end filter;

      end Design;

      encapsulated package Plot
        import Modelica;
        import Modelica_LinearSystems2;
        import Modelica_LinearSystems2.TransferFunction;
        import Modelica_LinearSystems2.ZerosAndPoles;

      encapsulated function polesAndZeros
          "Plot poles and/or the zeros of a transfer function"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;

          import Modelica_LinearSystems2.Utilities.Plot;

        input TransferFunction tf "Linear system in transfer function form";
        input Boolean poles=true "= true, to plot the poles of tf";
        input Boolean zeros=true "= true, to plot the zeros of tf";

        extends Modelica_LinearSystems2.Internal.PartialPlotFunction(
           defaultDiagram = Modelica_LinearSystems2.Internal.DefaultDiagramPolesAndZeros());

      algorithm
         StateSpace.Plot.polesAndZeros(StateSpace(tf), poles, zeros, defaultDiagram, device);
      end polesAndZeros;

        encapsulated function bode "Plot transfer function as bode plot"
          import Modelica;
          import Modelica.Utilities.Strings;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Internal;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Math.Complex;

          import Modelica_LinearSystems2.Utilities.Plot;

          import SI = Modelica.SIunits;

          input TransferFunction tf "Transfer function to be plotted";
          input Integer nPoints(min=2) = 200 "Number of points";
          input Boolean autoRange=true
            "= true, if abszissa range is automatically determined";
          input Modelica.SIunits.Frequency f_min(min=0) = 0.1
            "Minimum frequency value, if autoRange = false";
          input Modelica.SIunits.Frequency f_max(min=0) = 10
            "Maximum frequency value, if autoRange = false";

          input Boolean magnitude=true "= true, to plot the magnitude of tf";
          input Boolean phase=true "= true, to plot the pase of tf";

          extends Modelica_LinearSystems2.Internal.PartialPlotFunction(defaultDiagram=
                Modelica_LinearSystems2.Internal.DefaultDiagramBodePlot(heading="Bode plot of  tf = "
                 + String(tf)));

        protected
          SI.AngularVelocity w[nPoints];
          SI.Frequency f[nPoints];
          Modelica.SIunits.Conversions.NonSIunits.Angle_deg phi[nPoints];
          Real A[nPoints];
          Boolean OK;
          Complex c;
          Modelica.SIunits.Angle phi_old;
          Complex numZeros[:];
          Complex denZeros[:];

          Plot.Records.Curve curves[2];
          Integer i;
          Plot.Records.Diagram diagram2[2];

        algorithm
                // Determine frequency vector f
          if autoRange then
            (numZeros,denZeros) := TransferFunction.Analysis.zerosAndPoles(tf);
          else
            numZeros := fill(Complex(0), 0);
            denZeros := fill(Complex(0), 0);
          end if;
          f := Internal.frequencyVector(
                nPoints,
                autoRange,
                f_min,
                f_max,
                numZeros,
                denZeros);

          // Compute magnitude/phase at the frequency points
          phi_old := 0.0;
          for i in 1:nPoints loop
            w[i] := Modelica.SIunits.Conversions.from_Hz(f[i]);
            c := TransferFunction.Analysis.evaluate(
                  tf,
                  Complex(0, w[i]),
                  1e-10);
            A[i] := Complex.'abs'(c);
            phi_old := Complex.arg(c, phi_old);
            phi[i] := Modelica.SIunits.Conversions.to_deg(phi_old);

          end for;

         // Plot computed frequency response
         diagram2 := fill(defaultDiagram, 2);
          i := 0;
          if magnitude then
            i := i + 1;
            curves[i] := Plot.Records.Curve(
                  x=f,
                  y=A,
                  autoLine=true);
            diagram2[i].curve := {curves[i]};
            diagram2[i].yLabel := "magnitude";
            if phase then
               diagram2[i].xLabel:="";
            end if;
          end if;

          if phase then
            i := i + 1;
            curves[i] := Plot.Records.Curve(
                  x=f,
                  y=phi,
                  autoLine=true);
            diagram2[i].curve := {curves[i]};
            diagram2[i].yLabel := "phase [deg]";
            diagram2[i].logY := false;
            if magnitude then
              diagram2[i].heading:="";
           end if;
          end if;

            if magnitude and phase then
              Plot.diagramVector(diagram2, device);
            else
              Plot.diagram(diagram2[1], device);
            end if;

        end bode;

      encapsulated function timeResponse
          "Plot the time response of a system represented by a transfer function. The response type is selectable"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

          input Modelica_LinearSystems2.TransferFunction tf;
          input Real dt=0 "Sample time [s]";
          input Real tSpan=0 "Simulation time span [s]";

          input Modelica_LinearSystems2.Types.TimeResponse response=
              Modelica_LinearSystems2.Types.TimeResponse.Step
            "type of time response";
          input Real x0[TransferFunction.Analysis.denominatorDegree(tf)]=zeros(
              TransferFunction.Analysis.denominatorDegree(tf))
            "Initial state vector";

          extends Modelica_LinearSystems2.Internal.PartialPlotFunction(
              defaultDiagram=
                Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(heading=
                 "time response of  tf = " + String(tf)));

        protected
          Plot.Records.Curve curve;
          Plot.Records.Diagram diagram2;
          Real y[:,1,1] "Output response";
          Real t[:] "Time vector: (number of samples)";

      algorithm
          (y,t) := TransferFunction.Analysis.timeResponse(
              tf,
              dt,
              tSpan,
              response,
              x0);

          curve := Plot.Records.Curve(
              x=t,
              y=y[:, 1, 1],
              legend="y",
              autoLine=true);
          diagram2 := defaultDiagram;
          diagram2.curve := {curve};

          Plot.diagram(diagram2, device);

      end timeResponse;

      encapsulated function impulse "Impulse response plot"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

          input Modelica_LinearSystems2.TransferFunction tf "transfer function";
          input Real dt=0 "Sample time [s]";
          input Real tSpan=0 "Simulation time span [s]";

          input Real x0[TransferFunction.Analysis.denominatorDegree(tf)]=zeros(
              TransferFunction.Analysis.denominatorDegree(tf))
            "Initial state vector";

          extends Modelica_LinearSystems2.Internal.PartialPlotFunction(
              defaultDiagram=
                Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(heading="Impulse response of  tf = "
                 + String(tf)));

        protected
          input Modelica_LinearSystems2.Types.TimeResponse response=
              Modelica_LinearSystems2.Types.TimeResponse.Impulse
            "type of time response";
      algorithm
          Modelica_LinearSystems2.TransferFunction.Plot.timeResponse(
              tf=tf,
              dt=dt,
              tSpan=tSpan,
              response=response,
              x0=x0,
              defaultDiagram=defaultDiagram,
              device=device);

      end impulse;

      encapsulated function step "Step response plot"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

        input Modelica_LinearSystems2.TransferFunction tf;
        input Real dt=0 "Sample time [s]";
        input Real tSpan=0 "Simulation time span [s]";

        input Modelica_LinearSystems2.Types.TimeResponse response=
            Modelica_LinearSystems2.Types.TimeResponse.Step "type of time response";
        input Real x0[TransferFunction.Analysis.denominatorDegree(tf)]=zeros(
            TransferFunction.Analysis.denominatorDegree(tf)) "Initial state vector";

        extends Modelica_LinearSystems2.Internal.PartialPlotFunction(defaultDiagram=
              Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(heading="Step response of  tf = "
               + String(tf)));

      algorithm
       Modelica_LinearSystems2.TransferFunction.Plot.timeResponse(
          tf=tf,
          dt=dt,
          tSpan=tSpan,
          response=response,
          x0=x0,
          defaultDiagram=defaultDiagram,
          device=device);

      end step;

      encapsulated function ramp "Ramp response plot"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

        input Modelica_LinearSystems2.TransferFunction tf;
        input Real dt=0 "Sample time [s]";
        input Real tSpan=0 "Simulation time span [s]";

        input Modelica_LinearSystems2.Types.TimeResponse response=
            Modelica_LinearSystems2.Types.TimeResponse.Ramp "type of time response";
        input Real x0[TransferFunction.Analysis.denominatorDegree(tf)]=zeros(
            TransferFunction.Analysis.denominatorDegree(tf)) "Initial state vector";

        extends Modelica_LinearSystems2.Internal.PartialPlotFunction(defaultDiagram=
              Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(heading="Ramp response of  tf = "
               + String(tf)));

      algorithm
       Modelica_LinearSystems2.TransferFunction.Plot.timeResponse(
          tf=tf,
          dt=dt,
          tSpan=tSpan,
          response=response,
          x0=x0,
          defaultDiagram=defaultDiagram,
          device=device);

      equation

      end ramp;

      encapsulated function initialResponse
          "Initial condition response plot"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

          input Modelica_LinearSystems2.TransferFunction tf;
          input Real dt=0 "Sample time [s]";
          input Real tSpan=0 "Simulation time span [s]";

          input Modelica_LinearSystems2.Types.TimeResponse response=
              Modelica_LinearSystems2.Types.TimeResponse.Initial
            "type of time response";
          input Real y0 "Initial output (for initial condition plot)";

          extends Modelica_LinearSystems2.Internal.PartialPlotFunction(
              defaultDiagram=
                Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(heading=
                 "Initial response of  tf = " + String(tf) + "  with y0 = " + String(
                y0)));

        protected
          Modelica_LinearSystems2.StateSpace ss=Modelica_LinearSystems2.StateSpace(
              tf);
          Integer nx=TransferFunction.Analysis.denominatorDegree(tf);
          Real x0[nx]=Modelica.Math.Matrices.equalityLeastSquares(
                ss.A,
                fill(0, nx),
                ss.C,
                vector(y0)) "Initial state vector (for initial condition plot)";
      algorithm

          Modelica_LinearSystems2.TransferFunction.Plot.timeResponse(
              tf=tf,
              dt=dt,
              tSpan=tSpan,
              response=response,
              x0=x0,
              defaultDiagram=defaultDiagram,
              device=device);

      end initialResponse;

      end Plot;

      encapsulated package Conversion
        encapsulated function toZerosAndPoles
          "Generate a ZerosAndPoles object from a TransferFunction object"
          import Modelica;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.ZerosAndPoles.Internal;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Math.Complex;

          input TransferFunction tf "transfer function of a system";
          output ZerosAndPoles zp(
            redeclare Real n1[Internal.numberOfRealZeros2(tf)],
            redeclare Real n2[integer((size(tf.n, 1) - 1 -
              Internal.numberOfRealZeros2(tf))/2),2],
            redeclare Real d1[Internal.numberOfRealPoles(tf)],
            redeclare Real d2[integer((size(tf.d, 1) - 1 -
              Internal.numberOfRealPoles(tf))/2),2]);
        protected
          Complex z[:];
          Complex p[:];
          Real k;
        algorithm
          (z,p,k) := TransferFunction.Analysis.zerosAndPoles(tf);
          zp := ZerosAndPoles(
              z,
              p,
              k);
        end toZerosAndPoles;

        function toStateSpace
          "Convert a TransferFunction into a StateSpace representation"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica.Math.Vectors;

         input Modelica_LinearSystems2.TransferFunction tf
            "transfer function of a system";
              output Modelica_LinearSystems2.StateSpace ss(
                redeclare Real A[TransferFunction.Analysis.denominatorDegree(tf),TransferFunction.Analysis.denominatorDegree(tf)],
                redeclare Real B[TransferFunction.Analysis.denominatorDegree(tf),1],
                redeclare Real C[1,TransferFunction.Analysis.denominatorDegree(tf)],
                redeclare Real D[1,1]) "Transfer function in StateSpace form";

        protected
         Integer na=TransferFunction.Analysis.denominatorDegree(tf) + 1;
         Integer nb=TransferFunction.Analysis.numeratorDegree(tf) + 1;
         Integer nx=na - 1;
         Real a[na]=Vectors.reverse(tf.d) "Reverse element order of tf.a";
         Real b[na]=vector([Vectors.reverse(tf.n); zeros(na - nb, 1)]);
         Real d=b[na]/a[na];
        algorithm
         if nx == 0 then
           ss.A := fill(
               0,
               0,
               nx);
           ss.B := fill(
               0,
               0,
               1);
           ss.C := fill(
               0,
               1,
               0);
         else
           ss.A[1:nx - 1, 1:nx] := [zeros(nx - 1, 1),identity(nx - 1)];
           ss.A[nx, 1:nx] := -a[1:na - 1]/a[na];
           ss.B := [zeros(nx - 1, 1); 1/a[na]];
           ss.C := {b[1:nx] - d*a[1:nx]};

        end if;

          ss.D := [d];
        end toStateSpace;

      encapsulated function toZerosAndPolesMIMO
          "Generate a zeros-and-poles transfer function matrix from a numerator-denominator-polynomial transfer function matrix representation"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.TransferFunction;

        input TransferFunction tf[:,:] "transfer function of a system";

        output ZerosAndPoles zp[size(tf, 1),size(tf, 2)];

        protected
        Integer ny=size(tf, 1);
        Integer nu=size(tf, 2);

      algorithm
        for iy in 1:ny loop
          for iu in 1:nu loop
            zp[iy, iu] := ZerosAndPoles(tf[iy, iu]);
          end for;
        end for;
      end toZerosAndPolesMIMO;

        function toMatrices
          "Convert a TransferFunction into the matrices A, B, C of a StateSpace"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica.Math.Vectors;

         input Modelica_LinearSystems2.TransferFunction tf
            "transfer function of a system";

              output Real ABCD[size(tf.d,1),size(tf.d,1)];

        protected
         Integer na=TransferFunction.Analysis.denominatorDegree(tf) + 1;
         Integer nb=TransferFunction.Analysis.numeratorDegree(tf) + 1;
         Integer nx=na - 1;
         output Real A[nx,nx];
         output Real B[nx,1];
         output Real C[1,nx];
         output Real D[1,1];
         Real a[na]=Vectors.reverse(tf.d) "Reverse element order of tf.a";
         Real b[na]=vector([Vectors.reverse(tf.n); zeros(na - nb, 1)]);
         Real d=b[na]/a[na];
        algorithm
         if nx == 0 then
           A := fill(
               0,
               0,
               nx);
           B := fill(
               0,
               0,
               1);
           C := fill(
               0,
               1,
               0);
         else
           A[1:nx - 1, 1:nx] := [zeros(nx - 1, 1),identity(nx - 1)];
           A[nx, 1:nx] := -a[1:na - 1]/a[na];
           B := [zeros(nx - 1, 1); 1/a[na]];
           C := {b[1:nx] - d*a[1:nx]};
        end if;
          D := [d];
          ABCD := [A,B;C,D];
        end toMatrices;
      end Conversion;

      encapsulated package Import

      function fromModel
          "Generate a TransferFunction record array from a state space representation resulted from linearization of a model"

          import Modelica;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;

        input String modelName "Name of the Modelica model";
        input Real T_linearize=0
            "point in time of simulation to linearize the model";
        input String fileName="dslin" "Name of the result file";

        protected
        String fileName2=fileName + ".mat";
        Boolean OK1 = simulateModel(problem=modelName, startTime=0, stopTime=T_linearize);
        Boolean OK2 = importInitial("dsfinal.txt");
        Boolean OK3 = linearizeModel(problem=modelName, resultFile=fileName, startTime=T_linearize, stopTime=T_linearize+1);
        Real nxMat[1,1]=readMatrix(fileName2, "nx", 1, 1);
        Integer ABCDsizes[2]=readMatrixSize(fileName2, "ABCD");
        Integer nx=integer(nxMat[1, 1]);
        Integer nu=ABCDsizes[2] - nx;
        Integer ny=ABCDsizes[1] - nx;
        Real ABCD[nx + ny,nx + nu]=readMatrix(fileName2, "ABCD", nx + ny, nx + nu);
        String xuyName[nx + nu + ny]=readStringMatrix(fileName2, "xuyName", nx + nu + ny);

      StateSpace result(
          redeclare Real A[nx,nx],
          redeclare Real B[nx,nu],
          redeclare Real C[ny,nx],
          redeclare Real D[ny,nu]) "= model linearized at initial point";
        public
        output TransferFunction tf[:,:];

      algorithm
        result.A := ABCD[1:nx, 1:nx];
        result.B := ABCD[1:nx, nx + 1:nx + nu];
        result.C := ABCD[nx + 1:nx + ny, 1:nx];
        result.D := ABCD[nx + 1:nx + ny, nx + 1:nx + nu];
        result.uNames := xuyName[nx + 1:nx + nu];
        result.yNames := xuyName[nx + nu + 1:nx + nu + ny];
        result.xNames := xuyName[1:nx];

        tf := Modelica_LinearSystems2.StateSpace.Conversion.toTransferFunctionMIMO(result);

      end fromModel;

      encapsulated function fromFile
          "Generate a transfer function data record by reading numenator coefficients and denominator coefficients from a file (default file name is tf.mat)"

          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Math.Polynomial;
        input String fileName="tf.mat" "Name of the transfer function data file";
        input String numName="n" "Name of the numenator of the transfer function";
        input String denName="d" "Name of the denominator of the transfer function";

        protected
        input Integer numSize[2]=readMatrixSize(fileName, numName);
        input Integer denSize[2]=readMatrixSize(fileName, denName);

        Real num[numSize[1],numSize[2]]=readMatrix(
              fileName,
              numName,
              numSize[1],
              numSize[2]) "numenator coefficients";
        Real den[denSize[1],denSize[2]]=readMatrix(
              fileName,
              denName,
              denSize[1],
              denSize[2]) "denominator coefficients";
        input Integer ns2=numSize[2];
        input Integer ds2=denSize[2];
        public
       output TransferFunction tf(n=fill(0,ns2),d=fill(0,ds2)) "tranfer function";

      algorithm
        tf.n := vector(num);
        tf.d := vector(den);
        tf.uName := numName;
        tf.yName := denName;

      end fromFile;

      end Import;

      encapsulated package Internal
        "Internal library of record transferFunction (should not be directly used by user)"
        import Modelica;
        extends Modelica.Icons.Library;

        encapsulated function readLength
          "Read the number n of coefficients written in a [n,1]-matrix"
          input String fileName="tf.mat" "Name of the transfer function data file";
          input String polyName="n"
            "Name of the polynominal (numenator or denominator) coefficients of the transfer function";
          output Integer result;
        protected
          Integer polySize[2]=readMatrixSize(fileName, polyName);

        algorithm
          result := polySize[2];
        end readLength;

        encapsulated function isControllableAndObservableSISO
          "To check whether a SISO system is controllable and observable"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.TransferFunction;

        input TransferFunction tf;

          output Boolean controllableAndObservable;
        protected
          StateSpace ss=StateSpace(tf);

        algorithm
          controllableAndObservable := StateSpace.Internal.isControllableAndObservableSISO(ss=ss);

        end isControllableAndObservableSISO;

      end Internal;

    end TransferFunction;

## Modelica_LinearSystems2.ZerosAndPoles

Continuous zeros and poles description of a single input, single output system (data + operations)

### Information

This record defines a transfer function by its zeros, poles and a gain:

             product(p - z[i])
      y = k*------------------- * u
             product(p - n[i])

where z[:] is a Complex vector of zeros, n[:] is a Complex vector of poles and k is an additional multiplicative factor. The elements of the two Complex vectors must either be real numbers or conjugate complex pairs (in order that their product results in a polynomial with Real coefficients).

In the record, the zeros and poles are transformed into a product of first and second order polynomials. The data structure is especially useful in applications where first and second order polynomials are naturally occuring, e.g., as for filters. In fact, via function ZerosAndPoles.Design.filter, a ZeroAndPole transfer function is generated from low and high pass analog filters (CriticalDamping, Bessel, Butterworth, Chebyshev). The filters are available in normalized (default) and non-normalized form. In the normalized form, the amplitude of the filter transfer function at the cutoff frequency is 3 dB.

A ZeroAndPole transfer function is internally stored by the coefficients of first and second order polynomials, and by an additional multiplicative factor k:

             product(s + n1[i]) * product(p^2 + n2[i,1]*p + n2[i,2])
      y = k*---------------------------------------------------------
             product(p + d1[i]) * product(p^2 + d2[i,1]*p + d2[i,2])

Note, the degrees of the numerator and denominator polynomials are given as:

       degree of numerator   = size(n1,1) + 2*size(n2,1);
       degree of denominator = size(d1,1) + 2*size(d2,1);

Example:

                              (p+1)
      zp = 4* -------------------------------------
               (p - 1)*(p - (2+j*3))*(p - (2-j*3))

with j=sqrt(-1), is defined as

       import Modelica_LinearSystems2.Math.Complex;
       import Modelica_LinearSystems2.ZerosAndPoles;

       zp = ZerosAndPoles(z = {Complex(-1,0)},
                          p = {Complex(1,0),
                               Complex(2,3),
                               Complex(2,-3)},
                               k=4);

Extends from Modelica.Icons.Record (Icon for a record).

### Parameters

Type | Name | Default | Description
--- | --- | --- | ---
y = k*(product(p+n1[i]) * product(p^2+n2[i,1]*p+n2[i,2])) / (product(p+d1[i])*product(p^2+d2[i,1]*p+d2[i,2])) *u
Real | k | 1.0 | Multiplicative factor of transfer function
Real | n1[:] |  | [p^0] coefficients of 1st order numerator polynomials
Real | n2[:, 2] |  | [p,p^0] coefficients of 2nd order numerator polynomials
Real | d1[:] |  | [p^0] coefficients of 1st order denominator polynomials
Real | d2[:, 2] |  | [p,p^0] coefficients of 2nd order denominator polynomials
Signal names
String | uName | "u" | Name of input signal
String | yName | "y" | Name of output signal

### Modelica definition

    record ZerosAndPoles
      "Continuous zeros and poles description of a single input, single output system (data + operations)"
      extends Modelica.Icons.Record;

      Real k=1.0 "Multiplicative factor of transfer function";
      Real n1[:] "[p^0] coefficients of 1st order numerator polynomials";
      Real n2[:,2] "[p,p^0] coefficients of 2nd order numerator polynomials";
      Real d1[:] "[p^0] coefficients of 1st order denominator polynomials";
      Real d2[:,2] "[p,p^0] coefficients of 2nd order denominator polynomials";
      String uName="u" "Name of input signal";
      String yName="y" "Name of output signal";

      encapsulated operator 'constructor'
        "Generate a ZerosAndPoles object"
        import Modelica_LinearSystems2;

        encapsulated function fromReal
          "Generate a ZerosAndPoles transfer function from a Real value"
          import Modelica;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input Real r "Value of Real variable";
          input String uName="" "input name";
          input String yName="" "output name";
          output ZerosAndPoles zp(
            redeclare Real n1[0],
            redeclare Real n2[0,2],
            redeclare Real d1[0],
            redeclare Real d2[0,2]) "= r";

        algorithm
          zp.k := r;
          zp.uName := uName;
          zp.yName := yName;

        end fromReal;

      encapsulated function fromZerosAndPoles
          "Generate a ZerosAndPoles transfer function from a set of zeros and poles"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Internal;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica.Utilities.Streams.print;

        input Complex z[:]=fill(Modelica_LinearSystems2.Math.Complex(0), 0)
            "Zeros (Complex vector of numerator zeros)";
        input Complex p[:]=fill(Modelica_LinearSystems2.Math.Complex(0), 0)
            "Poles (Complex vector of denominator zeros)";
        input Real k=1.0 "Constant multiplied with transfer function";
        input String uName="" "input name";
        input String yName="" "output name";
        output ZerosAndPoles zp(
          redeclare Real n1[Internal.numberOfRealZeros(z)],
          redeclare Real n2[integer((size(z, 1) - Internal.numberOfRealZeros(z))/2),
            2],
          redeclare Real d1[Internal.numberOfRealZeros(p)],
          redeclare Real d2[integer((size(p, 1) - Internal.numberOfRealZeros(p))/2),
            2]) "ZerosAndPoles transfer functions of the zeros, poles and k";

        protected
        Integer n_n1=size(zp.n1, 1);
        Integer n_d1=size(zp.d1, 1);
        Integer n_n2=size(zp.n2, 1);
        Integer n_d2=size(zp.d2, 1);
        Integer i;
        Integer j;
        Complex z_reordered[size(z, 1)] "Reordered zeros";
        Complex p_reordered[size(p, 1)] "Reordered poles";
        Integer nz_real "Number of real zeros";
        Integer np_real "Number of real poles";
      algorithm
        //Extra input (string) added
        (z_reordered,nz_real) := Internal.reorderZeros(z, "");
        (p_reordered,np_real) := Internal.reorderZeros(p, "");

        // Numerator
        for i in 1:n_n1 loop
          zp.n1[i] := -z_reordered[i].re;
        end for;

        j := 1;
        for i in n_n1 + 1:2:size(z, 1) loop
          zp.n2[j, :] := {-2*z_reordered[i].re,z_reordered[i].re^2 + z_reordered[i].im
            ^2};
          j := j + 1;
        end for;

        // Denominator
        for i in 1:n_d1 loop
          zp.d1[i] := -p_reordered[i].re;
        end for;

        j := 1;
        for i in n_d1 + 1:2:size(p, 1) loop
          zp.d2[j, :] := {-2*p_reordered[i].re,p_reordered[i].re^2 + p_reordered[i].im
            ^2};
          j := j + 1;
        end for;

        zp.k := k;
        zp.uName := uName;
        zp.yName := yName;

      end fromZerosAndPoles;

        function fromTransferFunction =
            Modelica_LinearSystems2.TransferFunction.Conversion.toZerosAndPoles;
        encapsulated function fromFactorization
          "Generate a ZerosAndPoles object from first and second order polynomials"
          import Modelica;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input Real n1[:]=fill(0, 0)
            "[p^0] coefficients of 1st order numerator polynomials";
          input Real n2[:,2]=fill(
                  0,
                  0,
                  2) "[p,p^0] coefficients of 2nd order numerator polynomials";
          input Real d1[:]=fill(0, 0)
            "[p^0] coefficients of 1st order denominator polynomials";
          input Real d2[:,2]=fill(
                  0,
                  0,
                  2) "[p,p^0] coefficients of 2nd order denominator polynomials";
          input Real k=1.0 "Multiplicative factor of transfer function";
          input String uName="" "input name";
          input String yName="" "output name";
          output ZerosAndPoles zp(
            redeclare Real n1[size(n1, 1)],
            redeclare Real n2[size(n2, 1),2],
            redeclare Real d1[size(d1, 1)],
            redeclare Real d2[size(d2, 1),2]) "ZerosAndPoles transfer function";
        algorithm
          zp.n1 := n1;
          zp.n2 := n2;
          zp.d1 := d1;
          zp.d2 := d2;
          zp.k := k;
          zp.uName := uName;
          zp.yName := yName;
        end fromFactorization;

      end 'constructor';

    encapsulated operator '-'
      function subtract "Subtract two TransferFunctions (zp1 - zp2)"
          import Modelica;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.Math.Complex;

        input ZerosAndPoles zp1;
        input ZerosAndPoles zp2;

        protected
        Integer size_z1n1=size(zp1.n1, 1);
        Integer size_z1d1=size(zp1.d1, 1);
        Integer size_z1n2=size(zp1.n2, 1);
        Integer size_z1d2=size(zp1.d2, 1);
        Integer size_z2n1=size(zp2.n1, 1);
        Integer size_z2d1=size(zp2.d1, 1);
        Integer size_z2n2=size(zp2.n2, 1);
        Integer size_z2d2=size(zp2.d2, 1);
        Polynomial p1;
        Polynomial p2;
        Polynomial p3;
        Complex numZeros[:];
        Complex dummy[:]=fill(Complex(1), size_z1d1 + size_z2d1 + 2*(size_z1d2 +
            size_z2d2));
        Real k;

        output ZerosAndPoles result "= zp1/zp2";

      algorithm
        if zp1 == zp2 then
          result := ZerosAndPoles(0);
        else
          p1 := Polynomial(1);
          p2 := Polynomial(1);

          for i in 1:size_z1n1 loop
            p1 := p1*Polynomial({1,zp1.n1[i]});
          end for;
          for i in 1:size_z1n2 loop
            p1 := p1*Polynomial(cat(
              1,
              {1},
              zp1.n2[i, :]));
          end for;
          for i in 1:size_z2d1 loop
            p1 := p1*Polynomial({1,zp2.d1[i]});
          end for;
          for i in 1:size_z2d2 loop
            p1 := p1*Polynomial(cat(
              1,
              {1},
              zp2.d2[i, :]));
          end for;

          for i in 1:size_z2n1 loop
            p2 := p2*Polynomial({1,zp2.n1[i]});
          end for;
          for i in 1:size_z2n2 loop
            p2 := p2*Polynomial(cat(
              1,
              {1},
              zp2.n2[i, :]));
          end for;
          for i in 1:size_z1d1 loop
            p2 := p2*Polynomial({1,zp1.d1[i]});
          end for;
          for i in 1:size_z1d2 loop
            p2 := p2*Polynomial(cat(
              1,
              {1},
              zp1.d2[i, :]));
          end for;

          p3 := zp1.k*p1 - zp2.k*p2;
          k := 0;
          for i in size(p3.c, 1):-1:1 loop
            if abs(p3.c[i]) > Modelica.Constants.eps then
              k := p3.c[i];
            end if;
          end for;
          numZeros := Polynomial.roots(p3);
          result := ZerosAndPoles(
            numZeros,
            dummy,
            k);

          result.d1 := cat(
            1,
            zp1.d1,
            zp2.d1);
          result.d2 := cat(
            1,
            zp1.d2,
            zp2.d2);

        end if;
      end subtract;

      function negate "Unary minus (multiply transfer function by -1)"
          import Modelica_LinearSystems2.ZerosAndPoles;

         input ZerosAndPoles zp;
         output ZerosAndPoles result(n1=zp.n1, n2=zp.n2, d1=zp.d1, d2=zp.d2, k=-zp.k) "= -zp";
      algorithm
      end negate;
    end '-';

      encapsulated operator function '+'
        "Addition of to tarnsfwer functions zp1 + zp2, i.e. parallel connection of two transfer functions (= inputs are the same, outputs of the two systems are added)"

        import Modelica;
        import Modelica_LinearSystems2.ZerosAndPoles;
        import Modelica_LinearSystems2.Math.Polynomial;
        import Modelica_LinearSystems2.Math.Complex;

        input ZerosAndPoles zp1;
        input ZerosAndPoles zp2;

      protected
        Integer size_z1n1=size(zp1.n1, 1);
        Integer size_z1d1=size(zp1.d1, 1);
        Integer size_z1n2=size(zp1.n2, 1);
        Integer size_z1d2=size(zp1.d2, 1);
        Integer size_z2n1=size(zp2.n1, 1);
        Integer size_z2d1=size(zp2.d1, 1);
        Integer size_z2n2=size(zp2.n2, 1);
        Integer size_z2d2=size(zp2.d2, 1);
        Polynomial p1;
        Polynomial p2;
        Polynomial p3;
        Complex numZeros[:];
        Complex dummy[:]=fill(Complex(1), size_z1d1 + size_z2d1 + 2*(size_z1d2 +
            size_z2d2));
        Real k;

        output ZerosAndPoles result "= zp1+zp2";

      algorithm
        if zp1 == -zp2 then
          result := ZerosAndPoles(0);
        else

          p1 := Polynomial(1);
          p2 := Polynomial(1);

          for i in 1:size_z1n1 loop
            p1 := p1*Polynomial({1,zp1.n1[i]});
          end for;
          for i in 1:size_z1n2 loop
            p1 := p1*Polynomial(cat(
              1,
              {1},
              zp1.n2[i, :]));
          end for;
          for i in 1:size_z2d1 loop
            p1 := p1*Polynomial({1,zp2.d1[i]});
          end for;
          for i in 1:size_z2d2 loop
            p1 := p1*Polynomial(cat(
              1,
              {1},
              zp2.d2[i, :]));
          end for;

          for i in 1:size_z2n1 loop
            p2 := p2*Polynomial({1,zp2.n1[i]});
          end for;
          for i in 1:size_z2n2 loop
            p2 := p2*Polynomial(cat(
              1,
              {1},
              zp2.n2[i, :]));
          end for;
          for i in 1:size_z1d1 loop
            p2 := p2*Polynomial({1,zp1.d1[i]});
          end for;
          for i in 1:size_z1d2 loop
            p2 := p2*Polynomial(cat(
              1,
              {1},
              zp1.d2[i, :]));
          end for;

          p3 := zp1.k*p1 + zp2.k*p2;
          k := p3.c[1];
          numZeros := Polynomial.roots(p3);
          result := ZerosAndPoles(
            numZeros,
            dummy,
            k);

          result.d1 := cat(
            1,
            zp1.d1,
            zp2.d1);
          result.d2 := cat(
            1,
            zp1.d2,
            zp2.d2);
        end if;

      end '+';

      encapsulated operator function '*'
        "Multiply two ZerosAndPoles transfer functions (zp1 * zp2)"

        import Modelica;
        import Modelica_LinearSystems2.ZerosAndPoles;

        input ZerosAndPoles zp1;
        input ZerosAndPoles zp2;

        output ZerosAndPoles result "= zp1 * zp2";
      algorithm
        if zp1 == ZerosAndPoles(0) or zp2 == ZerosAndPoles(0) then
          result := ZerosAndPoles(0);
        else
          result.n1 := cat(
            1,
            zp1.n1,
            zp2.n1);
          result.n2 := cat(
            1,
            zp1.n2,
            zp2.n2);
          result.d1 := cat(
            1,
            zp1.d1,
            zp2.d1);
          result.d2 := cat(
            1,
            zp1.d2,
            zp2.d2);
          result.k := zp1.k*zp2.k;
        end if;

      end '*';

      encapsulated operator function '/'
        "Divide two transfer functions (zp1 / zp2)"

        import Modelica;
        import Modelica_LinearSystems2.ZerosAndPoles;

        input ZerosAndPoles zp1;
        input ZerosAndPoles zp2;

        output ZerosAndPoles result "= zp1/zp2";

      algorithm
        assert(abs(zp2.k)>100*Modelica.Constants.small,"zp2 in operator \"Modelica_LinearSystems2.TransferFunction.'/'()\" may not be zero");
        if zp1==ZerosAndPoles(0) then
          result := ZerosAndPoles(0);
        else
          result.n1 := cat(1,zp1.n1, zp2.d1);
          result.n2 := cat(1,zp1.n2, zp2.d2);
          result.d1 := cat(1,zp1.d1, zp2.n1);
          result.d2 := cat(1,zp1.d2, zp2.n2);
          result.k := zp1.k/zp2.k;
      end if;

      end '/';

      encapsulated operator function '^'
        "Integer power of TransferFunction (zp^k)"

        import Modelica;
        import Modelica_LinearSystems2.ZerosAndPoles;

        input ZerosAndPoles zp;
        input Integer k;

        output ZerosAndPoles result(
          redeclare Real n1[k*size(zp.n1, 1)],
          redeclare Real n2[k*size(zp.n2, 1),2],
          redeclare Real d1[k*size(zp.d1, 1)],
          redeclare Real d2[k*size(zp.d2, 1),2]) "= zp^k";
      protected
        Integer size_n1=size(zp.n1, 1);
        Integer size_d1=size(zp.d1, 1);
        Integer size_n2=size(zp.n2, 1);
        Integer size_d2=size(zp.d2, 1);
      algorithm
        for i in 1:k loop
          result.n1[(i - 1)*size_n1 + 1:i*size_n1] := zp.n1;
          result.d1[(i - 1)*size_d1 + 1:i*size_d1] := zp.d1;
          result.n2[(i - 1)*size_n2 + 1:i*size_n2, :] := zp.n2;
          result.d2[(i - 1)*size_d2 + 1:i*size_d2, :] := zp.d2;
        end for;
        result.k := zp.k^k;

      end '^';

    encapsulated operator function '=='
        "Check whether two transfer functions are identical"
            import Modelica;
            import Modelica.Math;
            import Modelica_LinearSystems2.Math.Polynomial;
            import Modelica_LinearSystems2.ZerosAndPoles;

        input ZerosAndPoles zp1;
        input ZerosAndPoles zp2;
        input Real eps(min=0) = 0
          "Two numbers n1 and n2 are identical if abs(n1-n2) <= eps";

        output Boolean result "= zp1 == zp2";
    algorithm
        result := Math.Vectors.isEqual(zp1.n1,zp2.n1,eps) and Math.Vectors.isEqual(zp1.d1,zp2.d1,eps) and Math.Matrices.isEqual(zp1.n2,zp2.n2,eps) and Math.Matrices.isEqual(zp1.d2,zp2.d2,eps) and (zp1.k==zp2.k);

    end '==';

      encapsulated operator function 'String'
        "Transform ZerosAndPoles transfer function into a String representation"
              import Modelica;
              import Modelica_LinearSystems2.ZerosAndPoles;
              import Modelica_LinearSystems2.ZerosAndPoles.Internal;

          input ZerosAndPoles zp
          "ZerosAndPoles transfer function to be transformed in a String representation";
          input Integer significantDigits=6
          "Number of significant digits that are shown";
          input String name="p" "Independent variable name used for printing";
          output String s="";
      protected
          Integer num_order=size(zp.n1, 1) + 2*size(zp.n2, 1);
          Integer den_order=size(zp.d1, 1) + 2*size(zp.d2, 1);
      algorithm
          if num_order == 0 and den_order == 0 then
            s := String(zp.k);

          else
             // construct string for multiplicative factor
            if zp.k <> 1.0 or zp.k == 1.0 and num_order == 0 then
              s := String(zp.k);
              if num_order <> 0 then
                s := s + "*";
              end if;
            end if;

            if num_order <> 0 then
                // construct numerator string
              s := s + Internal.firstOrderToString(
                    zp.n1,
                    significantDigits,
                    name);
              if size(zp.n2, 1) <> 0 then
                s := if size(zp.n1, 1) > 0 then s + "*" +
                  Internal.secondOrderToString(
                      zp.n2,
                      significantDigits,
                      name) else s + Internal.secondOrderToString(
                      zp.n2,
                      significantDigits,
                      name);
              end if;
            end if;

            if den_order <> 0 then
                // construct denominator string
              s := s + "/";
              if den_order > 1 then
                s := s + "( ";
              end if;
              s := s + Internal.firstOrderToString(
                    zp.d1,
                    significantDigits,
                    name);
              if size(zp.d2, 1) <> 0 then
                if size(zp.d1, 1) > 0 then
                  s := s + "*";
                end if;
                s := s + Internal.secondOrderToString(
                      zp.d2,
                      significantDigits,
                      name);
              end if;
              if den_order > 1 then
                s := s + " )";
              end if;
            end if;
          end if;
        //    end toString;
      end 'String';

      encapsulated function p "Generate the transfer function p"
        import Modelica;
        import Modelica_LinearSystems2.Math.Polynomial;
        import Modelica_LinearSystems2.ZerosAndPoles;

        output ZerosAndPoles zp(
          redeclare Real n1[1],
          redeclare Real n2[0,2],
          redeclare Real d1[0],
          redeclare Real d2[0,2]) "= tf1 * tf2";
      algorithm
        zp.n1[1] := 0;
      end p;

      encapsulated package Analysis
        function analysis
          "Make a system analysis based on the poles and zeros of the system"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Internal.AnalyseOptions;
          import Modelica_LinearSystems2.Internal.AnalyseOptions2;
          import Modelica_LinearSystems2.Internal.Eigenvalue;

          input ZerosAndPoles zp(uName="u", yName="y")
            "transfer function of a system";

          input AnalyseOptions2 analyseOptions2 = Modelica_LinearSystems2.Internal.AnalyseOptions2(
                   printControllability=false,
                   printObservability=false);

          input String fileName="eigenvalues.html"
            "Name of html-file that contains eigenvalue table";

          input String systemName = ""
            "Name of system (used as heading in html file)";
          input String description = "" "Description of system (used in html file)";

        protected
        input Boolean printStateSpaceSystem=true;
          String dummyFileName = "dummy" + fileName;
          input StateSpace ss=StateSpace(zp);

        AnalyseOptions analyseOptions=AnalyseOptions(
                   plotEigenValues=analyseOptions2.plotEigenValues,
                   plotInvariantZeros=analyseOptions2.plotInvariantZeros,
                   plotStepResponse=analyseOptions2.plotStepResponse,
                   plotFrequencyResponse=analyseOptions2.plotFrequencyResponse,
                   printEigenValues=analyseOptions2.printEigenValues,
                   printEigenValueProperties=analyseOptions2.printEigenValueProperties,
                   printInvariantZeros=analyseOptions2.printInvariantZeros,
                   printControllability=analyseOptions2.printControllability,
                   printObservability=analyseOptions2.printObservability,
                   headingEigenValues=analyseOptions2.headingEigenValues,
                   headingInvariantzeros=analyseOptions2.headingInvariantzeros,
                   headingStepResponse=analyseOptions2.headingStepResponse,
                   headingFrequencyResponse=analyseOptions2.headingFrequencyResponse);

        algorithm
          assert(ZerosAndPoles.Analysis.denominatorDegree(zp) >
            ZerosAndPoles.Analysis.numeratorDegree(zp),
            " Denominator polynominal of transfer function in function\"TransferFunction.Analysis.analysis\"has to be of higher order than numerator polynomial");
        Modelica.Utilities.Files.removeFile(fileName);
         Modelica.Utilities.Files.removeFile(dummyFileName);
        if printStateSpaceSystem then
        printSystem(
                zp,
                fileName,
                systemName,
                description);
        printSystem(
                zp,
                dummyFileName,
                systemName,
                description);
        end if;
          Modelica.Utilities.Streams.readFile(dummyFileName);
          StateSpace.Analysis.analysis(
                ss=ss,
                analyseOptions=analyseOptions,
                fileName=fileName,
                systemName=systemName,
                description=description,
                printStateSpaceSystem=false);
        equation

        public
         encapsulated function printSystem
            "Print the state space system in html format on file"
            import Modelica;
            import Modelica.Utilities.Streams.print;
            import Modelica_LinearSystems2.ZerosAndPoles;
            import Modelica_LinearSystems2;

          input ZerosAndPoles zp "transfer function to analyze";
          input String fileName="systemAnalysis.html"
              "File on which the zeros-and-poles transfer function is written in html format";
          input String systemName="ZerosAndPoles Transfer Function"
              "name of the system";
          input String description = "" "Description of system (used in html file)";
          input String format=".3g" "Format of numbers (e.g. \"20.8e\")";
          protected
          String st=String(zp);

         algorithm
          Modelica.Utilities.Files.removeFile(fileName);
          print("

\nSystem report\n

"Error:Found no end-tag in HTML-documentation

    ,fileName);
          print("<body><p><br> The system <b>" + systemName + "</b> is defined by</p>",fileName);
           print("G(p) = "+st, fileName);

             if description=="" then
           print("</table>", fileName);
          else
           print("</table>", fileName);
           print("<body><p><b>Description</b></p>",fileName);
           print(description, fileName);
           end if;

         end printSystem;
        end analysis;

       encapsulated function timeResponse
          "Calculate the time response of a zeros-and-poles transfer function"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Types.TimeResponse;

        extends Modelica_LinearSystems2.Internal.timeResponseMask2_zp;     // Input/Output declarations of time response functions
        input Modelica_LinearSystems2.Types.TimeResponse response=Modelica_LinearSystems2.Types.TimeResponse.Step;

        input Real x0[Modelica_LinearSystems2.ZerosAndPoles.Analysis.denominatorDegree(zp)]=zeros(Modelica_LinearSystems2.ZerosAndPoles.Analysis.denominatorDegree(zp))
            "Initial state vector";

        protected
         StateSpace ss=StateSpace(zp);

       algorithm
         (y,t,x_continuous) := StateSpace.Analysis.timeResponse(sc=ss, dt=dt, tSpan=tSpan, response=response, x0=x0);

       end timeResponse;

      encapsulated function impulseResponse
          "Calculate the impulse time response"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;

          // Input/Output declarations of time response functions:
        extends Modelica_LinearSystems2.Internal.timeResponseMask2_zp;

      algorithm
        (y,t,x_continuous) := Modelica_LinearSystems2.ZerosAndPoles.Analysis.timeResponse(
            zp=zp,
            dt=dt,
            tSpan=tSpan,
            response=Modelica_LinearSystems2.Types.TimeResponse.Impulse,
            x0=zeros(Modelica_LinearSystems2.ZerosAndPoles.Analysis.denominatorDegree(zp)));

      end impulseResponse;

      encapsulated function stepResponse
          "Calculate the step time response"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;
          // Input/Output declarations of time response functions:
        extends Modelica_LinearSystems2.Internal.timeResponseMask2_zp;

      algorithm
        (y,t,x_continuous) := Modelica_LinearSystems2.ZerosAndPoles.Analysis.timeResponse(
            zp=zp,
            dt=dt,
            tSpan=tSpan,
            response=Modelica_LinearSystems2.Types.TimeResponse.Step,
            x0=zeros(Modelica_LinearSystems2.ZerosAndPoles.Analysis.denominatorDegree(zp)));

      end stepResponse;

      encapsulated function rampResponse
          "Calculate the ramp time response"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.TransferFunction;

          // Input/Output declarations of time response functions:
        extends Modelica_LinearSystems2.Internal.timeResponseMask2_zp;

      algorithm
        (y,t,x_continuous) := Modelica_LinearSystems2.ZerosAndPoles.Analysis.timeResponse(
            zp=zp,
            dt=dt,
            tSpan=tSpan,
            response=Modelica_LinearSystems2.Types.TimeResponse.Ramp,
            x0=zeros(Modelica_LinearSystems2.ZerosAndPoles.Analysis.denominatorDegree(zp)));

      end rampResponse;

      encapsulated function initialResponse
          "Calculate the time response for given initial condition and zero inputs"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;

        input Real x0[:]=fill(0,0) "Initial state vector";

          // Input/Output declarations of time response functions:
        extends Modelica_LinearSystems2.Internal.timeResponseMask2_zp;

      algorithm
        (y,t,x_continuous) := Modelica_LinearSystems2.ZerosAndPoles.Analysis.timeResponse(
            zp=zp,
            dt=dt,
            tSpan=tSpan,
            response=Modelica_LinearSystems2.Types.TimeResponse.Initial,
            x0=x0);

      end initialResponse;

        encapsulated function numeratorDegree "Return numerator degree"
          import Modelica;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
          output Integer result;
        algorithm
          result := size(zp.n1, 1) + 2*size(zp.n2, 1);
        end numeratorDegree;

        encapsulated function denominatorDegree
          "Return denominator degree"
          import Modelica;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
          output Integer result;
        algorithm
          result := size(zp.d1, 1) + 2*size(zp.d2, 1);
        end denominatorDegree;

        encapsulated function evaluate
          "Evaluate a ZerosAndPoles transfer function at a given value of p"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.ZerosAndPoles.Internal;
          import Modelica_LinearSystems2.Math.Complex;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
          input Complex p=Complex(0) "Complex value p where zp is evaluated";
          input Real den_min=0 "|denominator(p)| is limited by den_min";
          output Complex y "= zp(p)";
        protected
          Complex j = Modelica_LinearSystems2.Math.Complex.j();
          Complex num;
          Complex den;
          Real abs_den;
        algorithm
          // Build numerator
          num := zp.k+0*j;
          for i in 1:size(zp.n1, 1) loop
             num := num*Internal.'p+a'(p, zp.n1[i]);
          end for;
          for i in 1:size(zp.n2, 1) loop
             num := num*Internal.'p^2+k[1]*p+k[2]'(p, zp.n2[i, :]);
          end for;

          // Build denominator
          den := 1+0*j;
          for i in 1:size(zp.d1, 1) loop
            den := den*Internal.'p+a'(p, zp.d1[i]);
          end for;
          for i in 1:size(zp.d2, 1) loop
            den := den*Internal.'p^2+k[1]*p+k[2]'(p, zp.d2[i, :]);
          end for;

          // Build value of transfer function
          abs_den := Complex.'abs'(den);
          den := if abs_den >= den_min then den else -abs_den+0*j;
          y := num/den;
        end evaluate;

        encapsulated function zerosAndPoles
          "Calculate zeros and poles of a ZerosAndPoles transfer function"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.ZerosAndPoles.Internal;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
          output Complex z[:]=fill(Complex(0, 0), size(zp.n1, 1) + 2*size(zp.n2, 1))
            "Zeros (Complex vector of numerator zeros)";
          output Complex p[:]=fill(Complex(0, 0), size(zp.d1, 1) + 2*size(zp.d2, 1))
            "Poles (Complex vector of denominator zeros)";
          output Real k "Constant multiplied with transfer function";

        protected
          Integer n_num1=size(zp.n1, 1);
          Integer n_num2=size(zp.n2, 1);
          Integer n_den1=size(zp.d1, 1);
          Integer n_den2=size(zp.d2, 1);
          Integer n_num=n_num1 + 2*n_num2;
          Integer n_den=n_den1 + 2*n_den2;
          Real re;
          Real im;
          Integer nz_real=Internal.numberOfRealZeros(zp.n1, zp.n2)
            "z[1:nz_real] are the real zeros";
          Integer np_real=Internal.numberOfRealZeros(zp.d1, zp.d2)
            "p[1:np_real] are the real poles";
          Real num_zeros1[nz_real];
          Real den_zeros1[np_real];
          Complex num_zeros2[:]=fill(Complex(0, 0), integer((n_num - nz_real)/2));
          Complex den_zeros2[:]=fill(Complex(0, 0), integer((n_den - np_real)/2));
          Integer n;
          Integer jj;
          Complex j=Modelica_LinearSystems2.Math.Complex.j();

        algorithm
          (num_zeros1,num_zeros2) := Internal.roots(
              zp.n1,
              zp.n2,
              nz_real);
          (den_zeros1,den_zeros2) := Internal.roots(
              zp.d1,
              zp.d2,
              np_real);

          n := size(num_zeros1, 1);
          for i in 1:n loop
            z[i] := Complex(num_zeros1[i], 0);
          end for;

          jj := 1;
          for i in 1:size(num_zeros2, 1) loop
            z[n + jj] := num_zeros2[i];
            z[n + jj + 1] := num_zeros2[i].re-num_zeros2[i].im*j;
            jj := jj + 2;
          end for;

          n := size(den_zeros1, 1);
          for i in 1:n loop
            p[i] := den_zeros1[i]+0*j;
          end for;

          jj := 1;
          for i in 1:size(den_zeros2, 1) loop
            p[n + jj] := den_zeros2[i];
            p[n + jj + 1] := den_zeros2[i].re-den_zeros2[i].im*j;
            jj := jj + 2;
          end for;

          k := zp.k;
        end zerosAndPoles;

        function eigenValues
          "Calculate the eigen values of a linear zeros-and-poles transfer function and write them in a complex vector"
          import Modelica;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Math.Complex;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
          output Complex eigval[:] "eigen values of the system";
        protected
          StateSpace ss=StateSpace(zp);

        algorithm
          assert(ZerosAndPoles.Analysis.denominatorDegree(zp) >
            ZerosAndPoles.Analysis.numeratorDegree(zp),
            " Denominator polynominal of transfer function in function\"ZerosAndPoles.Analysis.eigenValues\"has to be of higher order than numerator polynomial");
          eigval := Complex.eigenValues(ss.A);
        end eigenValues;

        encapsulated function eigenVectors
          "Calculate the rigth eigenvectors of the corresponding state space system of a zeros-and-poles transfer function and write them columnwise in a matrix"
          import Modelica;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Math.Complex;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
          input Boolean onlyEigenvectors=true;
          output Real eigvec[:,:] "eigen values of the system";
          output Complex eigval[:] "eigen values of the system";
        protected
          StateSpace ss=StateSpace(zp);

        algorithm
          assert(ZerosAndPoles.Analysis.denominatorDegree(zp) >
            ZerosAndPoles.Analysis.numeratorDegree(zp),
            " Denominator polynominal of transfer function in function\"ZerosAndPoles.Analysis.eigenVectors\"has to be of higher order than numerator polynomial");
          (eigvec,eigval) := StateSpace.Analysis.eigenVectors(ss=ss,
            onlyEigenvectors=onlyEigenvectors);

        end eigenVectors;

        encapsulated function invariantZeros
          "Compute invariant zeros of zeros-and-poles transfer function"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Math.Complex;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";

          output Complex Zeros[:]= ZerosAndPoles.Analysis.zerosAndPoles(zp);

        algorithm

        end invariantZeros;

        encapsulated function observabilityMatrix
          "Calculate the observability matrix of zp-transfer-function"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
          output Real om[:,:];

        protected
          StateSpace ss=StateSpace(zp);

        algorithm
          om := StateSpace.Analysis.observabilityMatrix(ss=ss);

        end observabilityMatrix;

        encapsulated function controllabilityMatrix
          "Calculate the controllability matrix [B, A*B, ..., A^(n-1)*B] of a zp-transfer-function"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
          output Real om[:,:];

        protected
          StateSpace ss=StateSpace(zp);

        algorithm
          om := StateSpace.Analysis.controllabilityMatrix(ss=ss);

        end controllabilityMatrix;

        encapsulated function isObservable
          "Check observability of a zp-transfer-function"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;

            input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
            input Modelica_LinearSystems2.Types.StaircaseMethod method=
                Modelica_LinearSystems2.Types.StaircaseMethod.SVD;

            output Boolean observable;
        protected
            StateSpace ss=StateSpace(zp);

        algorithm
            observable := StateSpace.Analysis.isObservable(ss=ss, method=method);

        end isObservable;

        encapsulated function isControllable
          "Check controllability of a zp-transfer-function"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
          input Modelica_LinearSystems2.Types.Method method=Modelica_LinearSystems2.Types.StaircaseMethod.SVD;

          output Boolean controllable;
        protected
          StateSpace ss=StateSpace(zp);

        algorithm
          controllable := StateSpace.Analysis.isControllable(ss=ss, method=method);

        end isControllable;

        encapsulated function isStabilizable
          "Check stabilizability of a zp-transfer-function"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";

          output Boolean stabilizable;
        protected
          StateSpace ss=StateSpace(zp);

        algorithm
          stabilizable := StateSpace.Analysis.isStabilizable(ss=ss);

        end isStabilizable;

        encapsulated function isDetectable
          "Check detectability of a zp-transfer-function"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";

          output Boolean detectable;

        protected
          StateSpace ss=StateSpace(zp);

        algorithm
          detectable := StateSpace.Analysis.isDetectable(ss=ss);

        end isDetectable;

      end Analysis;

      encapsulated package Design

      encapsulated function filter
          "Generate the data record of a ZerosAndPoles transfer function from a filter description"

          import Modelica;
          import Modelica.Utilities.Streams;
          import Modelica.Utilities.Streams.print;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Types;
          import Modelica_LinearSystems2.ZerosAndPoles;

          import Modelica_LinearSystems2.ZerosAndPoles.Internal;
          import Modelica.Math.*;
          import Modelica_LinearSystems2.Math.*;

        input Modelica_LinearSystems2.Types.AnalogFilter analogFilter=Types.AnalogFilter.CriticalDamping
            "Analog filter characteristics (CriticalDamping/Bessel/Butterworth/Chebyshev)";
        input Modelica_LinearSystems2.Types.FilterType filterType=Types.FilterType.LowPass
            "Type of filter (LowPass/HighPass)";
        input Integer order(min=1) = 2 "Order of filter";
        input Modelica.SIunits.Frequency f_cut=1/(2*Modelica.Constants.pi)
            "Cut-off frequency (default is w_cut = 1 rad/s)";
        input Real gain=1.0
            "Gain (= amplitude of frequency response at zero frequency)";
        input Real A_ripple(unit="dB") = 0.5
            "Pass band ripple for Chebyshev filter (otherwise not used)";
        input Boolean normalized=true
            "= true, if amplitude of low pass filter at f_cut where the magnitude of the filter sagged for 3dB, otherwise unmodified filter";
        output ZerosAndPoles filter(
          redeclare Real n1[if filterType == Types.FilterType.LowPass then 0 else (
            if analogFilter == Types.AnalogFilter.CriticalDamping then order else
            mod(order, 2))],
          redeclare Real n2[if filterType == Types.FilterType.LowPass then 0 else (
            if analogFilter == Types.AnalogFilter.CriticalDamping then 0 else
            integer((order + 1)/2)),2],
          redeclare Real d1[if analogFilter == Types.AnalogFilter.CriticalDamping then
                  order else mod(order, 2)],
          redeclare Real d2[if analogFilter == Types.AnalogFilter.CriticalDamping then
                  0 else integer(order/2),2]) "Filter transfer function";

        protected
        Integer n_num1=size(filter.n1, 1);
        Integer n_num2=size(filter.n2, 1);
        Integer n_den1=size(filter.d1, 1);
        Integer n_den2=size(filter.d2, 1);
        Integer n_num=n_num1 + 2*n_num2;
        Integer n_den=n_den1 + 2*n_den2;
        Real pi=Modelica.Constants.pi;
        Boolean evenOrder=mod(order, 2) == 0
            "= true, if even filter order (otherwise uneven)";
        Modelica.SIunits.AngularVelocity w_cut=2*pi*f_cut
            "Cut-off angular frequency";
        Real w_cut2 "= w_cut*w_cut";
        Real alpha=1.0 "Frequency correction factor";
        Real alpha2 "= alpha*alpha";

        Real alphax;

        Real poleReal "Real part of pole";
        Real poleImag "Imaginary part of pole";
        Real epsilon "Ripple size";
        Real fac "arsinh(epsilon)";
        Real A2 "poleReal^2 + poleImag^2";
        Real A "Amplitude at w_cut";
        Real num1[n_num1]
            "[p] coefficients of numerator first order polynomials (a*p + 1)";
        Real num2[n_num2,2]
            "[p^2, p] coefficients of numerator second order polynomials (b*p^2 + a*p + 1I)";
        Real den1[n_den1]
            "[p] coefficients of denominator first order polynomials (a*p + 1)";
        Real den2[n_den2,2]
            "[p^2, p] coefficients of denominator second order polynomials (b*p^2 + a*p + 1I)";
        Real aux;
        Real k;
      algorithm
        // Set properties that are common for all filters
        filter.k := gain;

        /* Compute filter coefficients of prototype low pass filter. If another filter
         characteristics is desired (e.g. high pass filter), it is derived
         from the low pass filter coefficients below
      */
        if analogFilter == Types.AnalogFilter.CriticalDamping then
          if normalized then
            alpha := sqrt(2^(1/order) - 1);
      //alpha := sqrt(10^(3/10/order)-1)
          else
            alpha := 1;
          end if;
          for i in 1:n_den1 loop
            den1[i] := alpha;
          end for;

        elseif analogFilter == Types.AnalogFilter.Bessel then
          (den1,den2,alpha) := Internal.BesselCoefficients(order);
          if not normalized then
            alpha2 := alpha*alpha;
            for i in 1:n_den2 loop
              den2[i, 1] := den2[i, 1]*alpha2;
              den2[i, 2] := den2[i, 2]*alpha;
            end for;
            if not evenOrder then
              den1[1] := den1[1]*alpha;
            end if;
          end if;

        elseif analogFilter == Types.AnalogFilter.Butterworth then
           // Original filter is already normalized
          for i in 1:n_den2 loop
            den2[i, 1] := 1.0;
            den2[i, 2] := -2*cos(pi*(0.5 + (i - 0.5)/order));
          end for;
          if not evenOrder then
            den1[1] := 1.0;
          end if;

        elseif analogFilter == Types.AnalogFilter.Chebyshev then
          epsilon := sqrt(10^(A_ripple/10) - 1);
          fac := asinh(1/epsilon)/order;

          for i in 1:n_den2 loop
             // poles of prototype lowpass
            poleReal := cos(pi/2 + pi*(2*i - 1)/(2*order))*sinh(fac);
            poleImag := -sin(pi/2 + pi*(2*i - 1)/(2*order))*cosh(fac);

             // second order filter coefficients
            A2 := poleReal^2 + poleImag^2;
            den2[i, 1] := 1/A2;
            den2[i, 2] := -2*poleReal/A2;
          end for;
          if not evenOrder then
            den1[1] := -1.0/(cos(pi/2 + pi*(2*n_den - 1)/(2*order))*sinh(fac));
          end if;

           /* Transformation of filter transfer function with "new(p) = alpha*p"
            in order that the filter transfer function has an amplitude of
            3 db at the cutoff frequency
         */
          if normalized then
            alpha := Internal.normalizationFactor(den1, den2);
            alpha2 := alpha*alpha;
            for i in 1:n_den2 loop
              den2[i, 1] := den2[i, 1]*alpha2;
              den2[i, 2] := den2[i, 2]*alpha;
            end for;
            if not evenOrder then
              den1[1] := den1[1]*alpha;
            end if;
          end if;

        else
          Streams.error("analogFilter (= " + String(analogFilter) +
            ") is not supported");
        end if;

        // Determine normalized denominator polynomials with highest power of p equal to one
        filter.n1 := zeros(n_num1);
        filter.n2 := zeros(n_num2, 2);
        (filter.d1,filter.d2,k) := Internal.filterToNormalized(den1, den2);
        filter.k := filter.k/k;

        // Compute desired filter characteristics from low pass filter coefficients
        if filterType == Types.FilterType.HighPass then
           /* The high pass filter is derived from the low pass filter by
            the transformation new(p) = 1/p
            1/(p^2 + a*p + b) -> 1/((1/p)^2 + a*(1/p) + b) = (1/b)*p^2 / (p^2 + (a/b)*p + 1/b)
            1/(p + a)         -> 1/((1/p) + a) = (1/a)*p / (p + (1/a))
         */
          assert(n_num1 == n_den1 and n_num2 == n_den2,
            "Internal error 1, should not occur");
          filter.n1 := zeros(n_num1);
          filter.n2 := zeros(n_num2, 2);
          for i in 1:n_num1 loop
            filter.k := filter.k/filter.d1[i];
            filter.d1[i] := 1/filter.d1[i];
          end for;
          for i in 1:n_num2 loop
            filter.k := filter.k/filter.d2[i, 2];
            filter.d2[i, 1] := filter.d2[i, 1]/filter.d2[i, 2];
            filter.d2[i, 2] := 1/filter.d2[i, 2];
          end for;
        end if;

        /* Change filter coefficients according to transformation new(p) = p/w_cut
         Numerator  :     (p/w)^2 + a*(p/w) + b = (1/w^2)*(p^2 + (a*w)*p + b*w^2)
                                      (p/w) + a = (1/w)*(p + w*a)
         Denominator: 1/((p/w)^2 + a*(p/w) + b) = w^2/(p^2 + (a*w)*p + w^2/b)
                                  1/((p/w) + a) = w/(p + w*a)
      */
        w_cut2 := w_cut*w_cut;
        filter.k := filter.k*w_cut^(n_den1 + 2*n_den2 - n_num1 - 2*n_num2);
        filter.n1 := w_cut*filter.n1;
        filter.d1 := w_cut*filter.d1;
        filter.n2 := [w_cut*filter.n2[:, 1],w_cut2*filter.n2[:, 2]];
        filter.d2 := [w_cut*filter.d2[:, 1],w_cut2*filter.d2[:, 2]];

      end filter;
      end Design;

      encapsulated package Plot

      encapsulated function polesAndZeros
          "Plot eigenvalues and or the zeros of a zeros-and-poles transfer function"

          import Modelica;
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Math.Complex;

          import Modelica_LinearSystems2.Utilities.Plot;

        input ZerosAndPoles zp "Linear system in ZerosAndPoles form";
        input Boolean poles=true "= true, to plot the poles of zp";
        input Boolean zeros=true "= true, to plot the zeros of zp";

        extends Modelica_LinearSystems2.Internal.PartialPlotFunction(
           defaultDiagram = Modelica_LinearSystems2.Internal.DefaultDiagramPolesAndZeros());

        protected
        Real eval[ZerosAndPoles.Analysis.denominatorDegree(zp),2];
        Real invZerosRe[ZerosAndPoles.Analysis.numeratorDegree(zp)];
        Real invZerosIm[ZerosAndPoles.Analysis.numeratorDegree(zp)];

        Complex invZeros[:];
        Complex poles2[:];

        Plot.Records.Curve curves[2];
        Integer i;
        Plot.Records.Diagram diagram2;
      algorithm
        (invZeros, poles2) := ZerosAndPoles.Analysis.zerosAndPoles(zp);

        for i in 1:size(invZeros, 1) loop
           invZerosRe[i] := invZeros[i].re;
           invZerosIm[i] := invZeros[i].im;
        end for;

        for i in 1:size(poles2, 1) loop
           eval[i,1] := poles2[i].re;
           eval[i,2] := poles2[i].im;
        end for;

        i :=0;
        if poles then
           i :=i + 1;
           curves[i] :=Plot.Records.Curve(
                              x=eval[:, 1],
                              y=eval[:, 2],
                              legend="poles",
                              autoLine=false,
                              linePattern=Plot.Types.LinePattern.None,
                              lineSymbol=Plot.Types.PointSymbol.Cross);
        end if;

        if zeros then
           i :=i + 1;
           curves[i] :=Plot.Records.Curve(
                              x=invZerosRe,
                              y=invZerosIm,
                              legend="zeros",
                              autoLine=false,
                              linePattern=Plot.Types.LinePattern.None,
                              lineSymbol=Plot.Types.PointSymbol.Circle);
        end if;

           diagram2 :=defaultDiagram;
           diagram2.curve :=curves[1:i];
           Plot.diagram(diagram2,device);

      end polesAndZeros;

      encapsulated function bode
          "Plot ZerosAndPoles transfer function as bode plot"
          import Modelica;
          import Modelica.Utilities.Streams.print;
          import Modelica.Utilities.Strings;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.Internal;
          import SI = Modelica.SIunits;

          import Modelica_LinearSystems2.Utilities.Plot;

        input ZerosAndPoles zp "ZerosAndPoles transfer function to be plotted";
        input Integer nPoints(min=2) = 200 "Number of points";
        input Boolean autoRange=true
            "= true, if abszissa range is automatically determined";
        input Modelica.SIunits.Frequency f_min(min=0) = 0.1
            "Minimum frequency value, if autoRange = false";
        input Modelica.SIunits.Frequency f_max(min=0) = 10
            "Maximum frequency value, if autoRange = false";

        input Boolean magnitude=true "= true, to plot the magnitude of tf";
        input Boolean phase=true "= true, to plot the pase of tf";

        extends Modelica_LinearSystems2.Internal.PartialPlotFunction(defaultDiagram=
              Modelica_LinearSystems2.Internal.DefaultDiagramBodePlot(heading="Bode plot of  zp = "
               + String(zp)));

        protected
        SI.AngularVelocity w[nPoints];
        SI.Frequency f[nPoints];
        Modelica.SIunits.Conversions.NonSIunits.Angle_deg phi[nPoints];
        Real A[nPoints];
        Boolean OK;
        Complex c;
        Integer window=0;
        Modelica.SIunits.Angle phi_old;
        Complex numZeros[:];
        Complex denZeros[:];
        Plot.Records.Curve curves[2];
        Integer i;
        Plot.Records.Diagram diagram2[2];
      algorithm
             // Determine frequency vector f
        if autoRange then
          (numZeros,denZeros) := ZerosAndPoles.Analysis.zerosAndPoles(zp);
        else
          numZeros := fill(Complex(0), 0);
          denZeros := fill(Complex(0), 0);
        end if;
        f := Internal.frequencyVector(
          nPoints,
          autoRange,
          f_min,
          f_max,
          numZeros,
          denZeros);

             // Compute magnitude/phase at the frequency points

        phi_old := 0.0;
        for i in 1:nPoints loop
          w[i] := Modelica.SIunits.Conversions.from_Hz(f[i]);
          c := ZerosAndPoles.Analysis.evaluate(
            zp,
            Complex(0, w[i]),
            1e-10);
          A[i] := Complex.'abs'(c);
          phi_old := Complex.arg(c, phi_old);
          phi[i] := Modelica.SIunits.Conversions.to_deg(phi_old);

        end for;

        // Plot computed frequency response
        diagram2 := fill(defaultDiagram, 2);
        i := 0;
        if magnitude then
          i := i + 1;
          curves[i] := Plot.Records.Curve(
            x=f,
            y=A,
            autoLine=true);
          diagram2[i].curve := {curves[i]};
          diagram2[i].yLabel := "magnitude";
          if phase then
             diagram2[i].xLabel:="";
          end if;
        end if;

        if phase then
          i := i + 1;
          curves[i] := Plot.Records.Curve(
            x=f,
            y=phi,
            autoLine=true);
          diagram2[i].curve := {curves[i]};
          diagram2[i].yLabel := "phase [deg]";
          diagram2[i].logY := false;
          if magnitude then
            diagram2[i].heading:="";
         end if;
        end if;

        if magnitude and phase then
          Plot.diagramVector(diagram2, device);
        else
          Plot.diagram(diagram2[1], device);
        end if;

      end bode;

      encapsulated function timeResponse
          "Plot the time response of a system represented by a transfer function. The response type is selectable"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

        input Modelica_LinearSystems2.ZerosAndPoles zp;
        input Real dt=0 "Sample time [s]";
        input Real tSpan=0 "Simulation time span [s]";

        input Modelica_LinearSystems2.Types.TimeResponse response=
            Modelica_LinearSystems2.Types.TimeResponse.Step "type of time response";
        input Real x0[ZerosAndPoles.Analysis.denominatorDegree(zp)]=zeros(
            ZerosAndPoles.Analysis.denominatorDegree(zp)) "Initial state vector";

        extends Modelica_LinearSystems2.Internal.PartialPlotFunction(defaultDiagram=
              Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(heading="time response of  zp = "
               + String(zp)));

        protected
        Plot.Records.Curve curve;
        Plot.Records.Diagram diagram2;
        Real y[:,1,1] "Output response";
        Real t[:] "Time vector: (number of samples)";

      algorithm
        (y,t) := ZerosAndPoles.Analysis.timeResponse(
          zp,
          dt,
          tSpan,
          response,
          x0);

        curve := Plot.Records.Curve(
          x=t,
          y=y[:, 1, 1],
          legend="y",
          autoLine=true);
        diagram2 := defaultDiagram;
        diagram2.curve := {curve};

        Plot.diagram(diagram2, device);
      end timeResponse;

      encapsulated function impulse "Impulse response plot"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;

          import Modelica_LinearSystems2.Utilities.Plot;

        input Modelica_LinearSystems2.ZerosAndPoles zp
            "zeros-and-poles transfer function";
        input Real dt=0 "Sample time [s]";
        input Real tSpan=0 "Simulation time span [s]";

        input Real x0[ZerosAndPoles.Analysis.denominatorDegree(zp)]=zeros(ZerosAndPoles.Analysis.denominatorDegree(zp))
            "Initial state vector";

        extends Modelica_LinearSystems2.Internal.PartialPlotFunction(defaultDiagram=
              Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(heading="Impulse response of  zp = "
               + String(zp)));

        protected
        input Modelica_LinearSystems2.Types.TimeResponse response=
            Modelica_LinearSystems2.Types.TimeResponse.Impulse
            "type of time response";
      algorithm
        Modelica_LinearSystems2.ZerosAndPoles.Plot.timeResponse(
          zp=zp,
          dt=dt,
          tSpan=tSpan,
          response=response,
          x0=x0,
          defaultDiagram=defaultDiagram,
          device=device);

      end impulse;

      encapsulated function step "Step response plot"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

        input Modelica_LinearSystems2.ZerosAndPoles zp;
        input Real dt=0 "Sample time [s]";
        input Real tSpan=0 "Simulation time span [s]";

        input Modelica_LinearSystems2.Types.TimeResponse response=
            Modelica_LinearSystems2.Types.TimeResponse.Step "type of time response";
        input Real x0[ZerosAndPoles.Analysis.denominatorDegree(zp)]=zeros(
            ZerosAndPoles.Analysis.denominatorDegree(zp)) "Initial state vector";

        extends Modelica_LinearSystems2.Internal.PartialPlotFunction(defaultDiagram=
              Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(heading="Step response of  zp = "
               + String(zp)));

      algorithm
       Modelica_LinearSystems2.ZerosAndPoles.Plot.timeResponse(
          zp=zp,
          dt=dt,
          tSpan=tSpan,
          response=response,
          x0=x0,
          defaultDiagram=defaultDiagram,
          device=device);

      equation

      end step;

      encapsulated function ramp "Ramp response plot"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

        input Modelica_LinearSystems2.ZerosAndPoles zp;
        input Real dt=0 "Sample time [s]";
        input Real tSpan=0 "Simulation time span [s]";

        input Modelica_LinearSystems2.Types.TimeResponse response=
            Modelica_LinearSystems2.Types.TimeResponse.Ramp "type of time response";
        input Real x0[ZerosAndPoles.Analysis.denominatorDegree(zp)]=zeros(
            ZerosAndPoles.Analysis.denominatorDegree(zp)) "Initial state vector";

        extends Modelica_LinearSystems2.Internal.PartialPlotFunction(defaultDiagram=
              Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(heading="Ramp response of  zp = "
               + String(zp)));

      algorithm
       Modelica_LinearSystems2.ZerosAndPoles.Plot.timeResponse(
          zp=zp,
          dt=dt,
          tSpan=tSpan,
          response=response,
          x0=x0,
          defaultDiagram=defaultDiagram,
          device=device);

      end ramp;

      encapsulated function initialResponse
          "Initial condition response plot"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Types.TimeResponse;

          import Modelica_LinearSystems2.Utilities.Plot;

        input Modelica_LinearSystems2.ZerosAndPoles zp;
        input Real dt=0 "Sample time [s]";
        input Real tSpan=0 "Simulation time span [s]";

        input Modelica_LinearSystems2.Types.TimeResponse response=
            Modelica_LinearSystems2.Types.TimeResponse.Initial
            "type of time response";
        input Real y0 "Initial output (for initial condition plot)";

        extends Modelica_LinearSystems2.Internal.PartialPlotFunction(defaultDiagram=
              Modelica_LinearSystems2.Internal.DefaultDiagramTimeResponse(heading="Initial response of  zp = "
               + String(zp) + "  with y0 = " + String(y0)));

        protected
        Modelica_LinearSystems2.StateSpace ss=Modelica_LinearSystems2.StateSpace(zp);
        Real x0[ZerosAndPoles.Analysis.denominatorDegree(zp)]=
            Modelica.Math.Matrices.equalityLeastSquares(
            ss.A,
            fill(0, size(ss.B, 1)),
            ss.C,
            vector(y0)) "Initial state vector (for initial condition plot)";
      algorithm

        Modelica_LinearSystems2.ZerosAndPoles.Plot.timeResponse(
              zp=zp,
              dt=dt,
              tSpan=tSpan,
              response=response,
              x0=x0,
              defaultDiagram=defaultDiagram,
              device=device);

      end initialResponse;

      end Plot;

      encapsulated package Conversion

        function toTransferFunction
          "Generate a TransferFunction object from a ZerosAndPoles object"
          //encapsulated function fromZerosAndPoles
          import Modelica;
          import Modelica_LinearSystems2.Math.Polynomial;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Internal;
          import Modelica_LinearSystems2.Math.Complex;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
          output TransferFunction tf;

        protected
          Real k;
          Complex z[:];
          Complex p[:];
          Polynomial pn;
          Polynomial pd;
        algorithm
          (z,p,k) := ZerosAndPoles.Analysis.zerosAndPoles(zp);
          pn := Polynomial(z)*Polynomial(k);
          pd := Polynomial(p);
          tf.n := pn.c;
          tf.d := pd.c;
          tf.uName := zp.uName;
          tf.yName := zp.yName;
        end toTransferFunction;

      encapsulated function toTransferFunctionMIMO
          "Generate a transfer function matrix  from zeros-and-poles transfer function matrix"

          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.TransferFunction;

        input ZerosAndPoles zp[:,:] "ZerosAndPoles transfer function of a system";

        output TransferFunction tf[size(zp, 1),size(zp, 2)];

        protected
        Integer ny=size(zp, 1);
        Integer nu=size(zp, 2);

      algorithm
        for iy in 1:ny loop
          for iu in 1:nu loop
            tf[iy, iu] := ZerosAndPoles.Conversion.toTransferFunction(zp[iy, iu]);
          end for;
        end for;
      end toTransferFunctionMIMO;

        function toStateSpace
          "Transform a ZerosAndPoles object into a StateSpace object"
         //encapsulated function fromZerosAndPoles
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Math.Vectors;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.StateSpace.Internal;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
          output StateSpace ss(
            redeclare Real A[ZerosAndPoles.Analysis.denominatorDegree(zp),
              ZerosAndPoles.Analysis.denominatorDegree(zp)],
            redeclare Real B[ZerosAndPoles.Analysis.denominatorDegree(zp),1],
            redeclare Real C[1,ZerosAndPoles.Analysis.denominatorDegree(zp)],
            redeclare Real D[1,1]) "Transfer function in StateSpace SISO form";

        protected
          Real A[2,2] "system matrix of partial 2nd order system";
          Real B[2,1] "input matrix of partial 2nd order system";
          Real C[1,2] "output matrix of partial 2nd order system";
          Real D[1,1] "feedthrough matrix of partial 2nd order system";
          Real a "system 'matrix' of partial 1st order system";
          Real b "input 'matrix' of partial 1st order system";
          Real c "output 'matrix' of partial 1st order system";
          Real d "feedthrough 'matrix' of partial 1st order system";
          Integer nx=ZerosAndPoles.Analysis.denominatorDegree(zp);
          Integer n_num1=size(zp.n1, 1);
          Integer n_num2=size(zp.n2, 1);
          Integer n_den1=size(zp.d1, 1);
          Integer n_den2=size(zp.d2, 1);
          Integer n_num=n_num1 + 2*n_num2;
          Integer n_den=n_den1 + 2*n_den2;

          Integer i_d=if n_num2 > n_den2 then 2*(n_num2 - n_den2) + 1 else 1;
          Integer i_k=if n_num2 > n_den2 then n_den2 - (n_num2 - n_den2) else n_den2;
          Integer i;
          Integer ili;
          Real num[nx,2]=[zp.n2; [zp.n1,zeros(n_num1)]; zeros(nx - n_num2 - n_num1, 2)]
            "Numerator matrix, in order that indices are defined in all situations in all if clauses";
          Real den[nx,2]=[zp.d2; [zp.d1,zeros(n_den1)]; zeros(nx - n_den2 - n_den1, 2)]
            "Denominator matrix, in order that indices are defined in all situations in all if clauses";
          Real k[i_k + n_den1](each fixed=false)
            "Additional factors of the first and second order blocks, in order that the gain of the blocks is 1";
          Real k_total;

          Boolean dZero=true;

         //ZerosAndPoles zp2;

        algorithm
          assert(n_num <= n_den,
            "ZerosAndPoles transfer function is not proper as required from StateSpace system:\n"
             + "  numerator degree (= " + String(n_num) +
            ") <= denominator degree (= " + String(n_den) + ") required.");

          if n_den > 0 then

            for i in 1:max(n_den2, n_num2) loop
              // State space systems of order 2
              if i <= n_den2 then
                if i <= n_num2 then
                    // State space system in form (1)
                  k[i] := Internal.scaleFactor2(
                      num[i, 1],
                      num[i, 2],
                      den[i, 1],
                      den[i, 2]);
                elseif i - n_num2 + 1 <= n_num1 then
                    // State space system in form (1) with 2 first order numerator polynomials
                  k[i] := Internal.scaleFactor2(
                      num[i, 1] + num[i + 1, 1],
                      num[i, 1]*num[i + 1, 1],
                      den[i, 1],
                      den[i, 2]);
                elseif i - n_num2 == n_num1 then
                    // State space system in form (2) with 1 first order numerator polynomial
                  k[i] := Internal.scaleFactor2(
                      0,
                      num[i, 1],
                      den[i, 1],
                      den[i, 2]);
                else
                    // State space system in form (3)
                  k[i] := Internal.scaleFactor2(
                      0,
                      0,
                      den[i, 1],
                      den[i, 2]);
                end if;
              else
                 // State space system in form (1) with 2 first order denominator polynomials
                k[i] := Internal.scaleFactor2(
                    num[i, 1],
                    num[i, 2],
                    den[i, 1] + den[i + 1, 1],
                    den[i, 1]*den[i + 1, 1]);
              end if;
            end for;

            for i in i_d:n_den1 loop
              // State space systems of order 1
              if n_num2 <= n_den2 and 2*(n_den2 - n_num2) + i <= n_num1 then
                 // State space system in form (4)
                k[i_k + i] := Internal.scaleFactor1(num[max(1, n_num2 + 2*(n_den2 -
                  n_num2) + i), 1], den[n_den2 + i, 1]);
              elseif n_num2 > n_den2 and i - i_d + 1 <= n_num1 then
                 // State space system in form (4)
                k[i_k + i] := Internal.scaleFactor1(num[max(1, n_num2 + i - i_d + 1),
                  1], den[n_den2 + i, 1]);
              else
                 // State space system in form (5)
                k[i_k + i] := Internal.scaleFactor1(0, den[n_den2 + i, 1]);
              end if;
            end for;

            k_total := zp.k/product(k);

            ss.A := zeros(nx, nx);
            ss.B := zeros(nx, 1);
            ss.C := zeros(1, nx);
            ss.D := zeros(1, 1);

         // Calculation of matrices A, B, C, D
         //first elements of A, B, C and D

            if max(n_den2, n_num2) > 0 then
              ili := i_d;
              A[1, :] := {0,1};
              B[1, 1] := 0;
                  // Construct state space systems of order 2
              if 1 <= n_den2 then
                A[2, :] := {-den[1, 2],-den[1, 1]};
                B[2, 1] := den[1, 2];
                if 1 <= n_num2 then
                       // State space system in form (1)
                  C := k[1]*[num[1, 2] - den[1, 2],num[1, 1] - den[1, 1]]/den[1, 2];
                  D := [k[1]];
                  dZero := false;

                elseif 1 - n_num2 + 1 <= n_num1 then
                       // State space system in form (1) with 2 first order numerator polynomials

                  C := k[1]*[num[1, 1]*num[2, 1] - den[1, 2],num[1, 1] + num[2, 1] - den[1, 1]]/den[1, 2];
                  D := [k[1]];
                  dZero := false;
                elseif 1 - n_num2 == n_num1 then
                       // State space system in form (2) with 1 first order numerator polynomial

                  C := k[1]*[num[1, 1],1]/den[1, 2];
                  D := [0];
                  dZero := dZero and true;
                else
                       // State space system in form (3)

                  C := k[1]*[1,0]/den[1, 2];
                  D := [0];
                  dZero := dZero and true;
                end if;
              else
                    // State space system in form (1) with 2 first order denominator polynomials

                A[2, :] := {-(den[1, 1]*den[2, 1]),-(den[1, 1] + den[2, 1])};
                B[2, 1] := den[1, 1]*den[1, 1];

                C := k[1]*[num[1, 2] - (den[1, 1]*den[2, 1]),num[1, 1] - (den[1, 1] + den[2, 1])]/den[1, 1]
                  /den[1, 1];
                D := [k[1]];
                dZero := false;
              end if;
              ss.A[1:2, 1:2] := A;
              ss.B[1:2, 1] := vector(B);
              ss.C[1, 1:2] := vector(C);
              ss.D := D;

            else
              ili := max(2, i_d);
           // Construct state space systems of order 1
              a := -den[1, 1];
              b := den[1, 1];

              if 1 <= n_num1 then
                    // State space system in form (4)
                c := k[1]*(num[1, 1] - den[1, 1])/den[1, 1];
                d := k[1];
                dZero := false;
              else
                    // State space system in form (5)
                c := k[1]/den[1, 1];
                d := 0;
                dZero := dZero and true;
              end if;
              ss.A[1, 1] := a;
              ss.B[1, 1] := b;
              ss.C[1, 1] := c;
              ss.D[1, 1] := d;

            end if;

         /// for i=2 to degree(system)
            A[1, :] := {0,1};
            B[1, 1] := 0;

            for i in 2:max(n_den2, n_num2) loop
                 // Construct state space systems of order 2
              if i <= n_den2 then
                A[2, :] := {-den[i, 2],-den[i, 1]};
                B[2, 1] := den[i, 2];

                if i <= n_num2 then
                       // State space system in form (1)

                  C := k[i]*[num[i, 2] - den[i, 2],num[i, 1] - den[i, 1]]/den[i, 2];
                  D := [k[i]];
                  dZero := false;

                elseif i - n_num2 + 1 <= n_num1 then
                       // State space system in form (1) with 2 first order numerator polynomials
                  C := k[i]*[num[i, 1]*num[i + 1, 1] - den[i, 2],num[i, 1] + num[i + 1, 1] - den[i, 1]]/den[
                    i, 2];
                  D := [k[i]];
                  dZero := false;

                elseif i - n_num2 == n_num1 then
                       // State space system in form (2) with 1 first order numerator polynomial
                  C := k[i]*[num[i, 1],1]/den[i, 2];
                  D := [0];
                  dZero := dZero and true;

                else
                       // State space system in form (3)
                  C := k[i]*[1,0]/den[i, 2];
                  D := [0];
                  dZero := dZero and true;

                end if;

              else
                    // State space system in form (1) with 2 first order denominator polynomials
                A[2, :] := {-(den[i, 1]*den[i + 1, 1]),-(den[i, 1] + den[i + 1, 1])};
                B[2, 1] := den[i, 1]*den[i, 1];

                C := k[i]*[num[i, 2] - (den[i, 1]*den[i + 1, 1]),num[i, 1] - (den[i, 1] + den[i + 1, 1])]/
                  den[i, 1]/den[i, 1];
                D := [k[i]];
                dZero := false;
              end if;

              ss.A[2*i, 1:2*i - 2] := B[2, 1]*ss.C[1, 1:2*i - 2];
              ss.A[2*i - 1:2*i, 2*i - 1:2*i] := A;
              ss.B[2*i, 1] := if dZero then 0 else B[2, 1]*ss.D[1, 1];
              ss.C[1, 1:2*i - 2] := if dZero then fill(0, 2*i - 2) else D[1, 1]*ss.C[
                1, 1:2*i - 2];
              ss.C[1, 2*i - 1:2*i] := vector(C);
              ss.D := D*ss.D;
            end for;

         //  for i in max(2,i_d):n_den1 loop

            for i in ili:n_den1 loop
                 // Construct state space systems of order 1
              a := if abs(den[n_den2 + i, 1])>Modelica.Constants.eps then -den[n_den2 + i, 1] else 0.0;
              b := if abs(den[n_den2 + i, 1])>Modelica.Constants.eps then den[n_den2 + i, 1] else 1.0;
              if n_num2 <= n_den2 and 2*(n_den2 - n_num2) + i <= n_num1 then
                    // State space system in form (4)

                c := if abs(den[n_den2 + i, 1])>Modelica.Constants.eps then k[i_k + i]*(num[max(1, n_num2 + 2*(n_den2 - n_num2) + i), 1] -  den[n_den2 + i, 1])/den[n_den2 + i, 1] else 1.0;

                d := k[i_k + i];

                dZero := false;
              elseif n_num2 > n_den2 and i - i_d + 1 <= n_num1 then
                    // State space system in form (4)

                c := if abs(den[n_den2 + i, 1])>Modelica.Constants.eps then k[i_k + i]*(num[max(1, n_num2 + i - i_d + 1), 1] - den[n_den2 + i, 1])/den[n_den2 + i, 1] else 1.0;
                d := k[i_k + i];
                dZero := false;
              else

                    // State space system in form (5)

                c := k[i_k + i]/den[n_den2 + i, 1];
                d := 0;
                dZero := dZero and true;
              end if;

              ss.A[2*n_den2 + i, 1:2*n_den2 + i - 1] := b*ss.C[1, 1:2*n_den2 + i - 1];
              ss.A[2*n_den2 + i, 2*n_den2 + i] := a;
              ss.B[2*n_den2 + i, 1] := if dZero then 0 else b*ss.D[1, 1];
              ss.C[1, 1:2*n_den2 + i - 1] := if dZero then fill(0, 2*n_den2 + i - 1) else
                      d*ss.C[1, 1:2*n_den2 + i - 1];
              ss.C[1, 2*n_den2 + i] := c;
              ss.D := if dZero then [0] else d*ss.D;

            end for;
            ss.C := k_total*ss.C;
            ss.D := k_total*ss.D;
          else
            ss := Modelica_LinearSystems2.StateSpace(zp.k);
          end if;

        end toStateSpace;

        function toMatrices
          "Convert a ZerosAndPoles object into the matrices A, B, C of a StateSpace"
         //encapsulated function fromZerosAndPoles
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Math.Vectors;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.StateSpace.Internal;

          input ZerosAndPoles zp "ZerosAndPoles transfer function of a system";
          output Real ABCD[ZerosAndPoles.Analysis.denominatorDegree(zp)+1,
              ZerosAndPoles.Analysis.denominatorDegree(zp)+1];

        protected
          Real ssA[ZerosAndPoles.Analysis.denominatorDegree(zp),ZerosAndPoles.Analysis.denominatorDegree(zp)]
            "system matrix of partial 2nd order system";
          Real ssB[ZerosAndPoles.Analysis.denominatorDegree(zp),1]
            "input matrix of partial 2nd order system";
          Real ssC[1,ZerosAndPoles.Analysis.denominatorDegree(zp)]
            "output matrix of partial 2nd order system";
          Real ssD[1,1] "feedthrough matrix of partial 2nd order system";

          Real A[2,2] "system matrix of partial 2nd order system";
          Real B[2,1] "input matrix of partial 2nd order system";
          Real C[1,2] "output matrix of partial 2nd order system";
          Real D[1,1] "feedthrough matrix of partial 2nd order system";
          Real a "system 'matrix' of partial 1st order system";
          Real b "input 'matrix' of partial 1st order system";
          Real c "output 'matrix' of partial 1st order system";
          Real d "feedthrough 'matrix' of partial 1st order system";
          Integer nx=ZerosAndPoles.Analysis.denominatorDegree(zp);
          Integer n_num1=size(zp.n1, 1);
          Integer n_num2=size(zp.n2, 1);
          Integer n_den1=size(zp.d1, 1);
          Integer n_den2=size(zp.d2, 1);
          Integer n_num=n_num1 + 2*n_num2;
          Integer n_den=n_den1 + 2*n_den2;

          Integer i_d=if n_num2 > n_den2 then 2*(n_num2 - n_den2) + 1 else 1;
          Integer i_k=if n_num2 > n_den2 then n_den2 - (n_num2 - n_den2) else n_den2;
          Integer i;
          Integer ili;
          Real num[nx,2]=[zp.n2; [zp.n1,zeros(n_num1)]; zeros(nx - n_num2 - n_num1, 2)]
            "Numerator matrix, in order that indices are defined in all situations in all if clauses";
          Real den[nx,2]=[zp.d2; [zp.d1,zeros(n_den1)]; zeros(nx - n_den2 - n_den1, 2)]
            "Denominator matrix, in order that indices are defined in all situations in all if clauses";
          Real k[i_k + n_den1](each fixed=false)
            "Additional factors of the first and second order blocks, in order that the gain of the blocks is 1";
          Real k_total;

          Boolean dZero=true;

         //ZerosAndPoles zp2;

        algorithm
          assert(n_num <= n_den,
            "ZerosAndPoles transfer function is not proper as required from StateSpace system:\n"
             + "  numerator degree (= " + String(n_num) +
            ") <= denominator degree (= " + String(n_den) + ") required.");

          if n_den > 0 then

            for i in 1:max(n_den2, n_num2) loop
              // State space systems of order 2
              if i <= n_den2 then
                if i <= n_num2 then
                    // State space system in form (1)
                  k[i] := Internal.scaleFactor2(
                      num[i, 1],
                      num[i, 2],
                      den[i, 1],
                      den[i, 2]);
                elseif i - n_num2 + 1 <= n_num1 then
                    // State space system in form (1) with 2 first order numerator polynomials
                  k[i] := Internal.scaleFactor2(
                      num[i, 1] + num[i + 1, 1],
                      num[i, 1]*num[i + 1, 1],
                      den[i, 1],
                      den[i, 2]);
                elseif i - n_num2 == n_num1 then
                    // State space system in form (2) with 1 first order numerator polynomial
                  k[i] := Internal.scaleFactor2(
                      0,
                      num[i, 1],
                      den[i, 1],
                      den[i, 2]);
                else
                    // State space system in form (3)
                  k[i] := Internal.scaleFactor2(
                      0,
                      0,
                      den[i, 1],
                      den[i, 2]);
                end if;
              else
                 // State space system in form (1) with 2 first order denominator polynomials
                k[i] := Internal.scaleFactor2(
                    num[i, 1],
                    num[i, 2],
                    den[i, 1] + den[i + 1, 1],
                    den[i, 1]*den[i + 1, 1]);
              end if;
            end for;

            for i in i_d:n_den1 loop
              // State space systems of order 1
              if n_num2 <= n_den2 and 2*(n_den2 - n_num2) + i <= n_num1 then
                 // State space system in form (4)
                k[i_k + i] := Internal.scaleFactor1(num[max(1, n_num2 + 2*(n_den2 -
                  n_num2) + i), 1], den[n_den2 + i, 1]);
              elseif n_num2 > n_den2 and i - i_d + 1 <= n_num1 then
                 // State space system in form (4)
                k[i_k + i] := Internal.scaleFactor1(num[max(1, n_num2 + i - i_d + 1),
                  1], den[n_den2 + i, 1]);
              else
                 // State space system in form (5)
                k[i_k + i] := Internal.scaleFactor1(0, den[n_den2 + i, 1]);
              end if;
            end for;

            k_total := zp.k/product(k);

            ssA := zeros(nx, nx);
            ssB := zeros(nx, 1);
            ssC := zeros(1, nx);
            ssD := zeros(1, 1);

         // Calculation of matrices A, B, C, D
         //first elements of A, B, C and D

            if max(n_den2, n_num2) > 0 then
              ili := i_d;
              A[1, :] := {0,1};
              B[1, 1] := 0;
                  // Construct state space systems of order 2
              if 1 <= n_den2 then
                A[2, :] := {-den[1, 2],-den[1, 1]};
                B[2, 1] := den[1, 2];
                if 1 <= n_num2 then
                       // State space system in form (1)
                  C := k[1]*[num[1, 2] - den[1, 2],num[1, 1] - den[1, 1]]/den[1, 2];
                  D := [k[1]];
                  dZero := false;

                elseif 1 - n_num2 + 1 <= n_num1 then
                       // State space system in form (1) with 2 first order numerator polynomials

                  C := k[1]*[num[1, 1]*num[2, 1] - den[1, 2],num[1, 1] + num[2, 1] - den[1, 1]]/den[1, 2];
                  D := [k[1]];
                  dZero := false;
                elseif 1 - n_num2 == n_num1 then
                       // State space system in form (2) with 1 first order numerator polynomial

                  C := k[1]*[num[1, 1],1]/den[1, 2];
                  D := [0];
                  dZero := dZero and true;
                else
                       // State space system in form (3)

                  C := k[1]*[1,0]/den[1, 2];
                  D := [0];
                  dZero := dZero and true;
                end if;
              else
                    // State space system in form (1) with 2 first order denominator polynomials

                A[2, :] := {-(den[1, 1]*den[2, 1]),-(den[1, 1] + den[2, 1])};
                B[2, 1] := den[1, 1]*den[1, 1];

                C := k[1]*[num[1, 2] - (den[1, 1]*den[2, 1]),num[1, 1] - (den[1, 1] + den[2, 1])]/den[1, 1]
                  /den[1, 1];
                D := [k[1]];
                dZero := false;
              end if;
              ssA[1:2, 1:2] := A;
              ssB[1:2, 1] := vector(B);
              ssC[1, 1:2] := vector(C);
              ssD := D;

            else
              ili := max(2, i_d);
           // Construct state space systems of order 1
              a := -den[1, 1];
              b := den[1, 1];

              if 1 <= n_num1 then
                    // State space system in form (4)
                c := k[1]*(num[1, 1] - den[1, 1])/den[1, 1];
                d := k[1];
                dZero := false;
              else
                    // State space system in form (5)
                c := k[1]/den[1, 1];
                d := 0;
                dZero := dZero and true;
              end if;
              ssA[1, 1] := a;
              ssB[1, 1] := b;
              ssC[1, 1] := c;
              ssD[1, 1] := d;

            end if;

         /// for i=2 to degree(system)
            A[1, :] := {0,1};
            B[1, 1] := 0;

            for i in 2:max(n_den2, n_num2) loop
                 // Construct state space systems of order 2
              if i <= n_den2 then
                A[2, :] := {-den[i, 2],-den[i, 1]};
                B[2, 1] := den[i, 2];

                if i <= n_num2 then
                       // State space system in form (1)

                  C := k[i]*[num[i, 2] - den[i, 2],num[i, 1] - den[i, 1]]/den[i, 2];
                  D := [k[i]];
                  dZero := false;

                elseif i - n_num2 + 1 <= n_num1 then
                       // State space system in form (1) with 2 first order numerator polynomials
                  C := k[i]*[num[i, 1]*num[i + 1, 1] - den[i, 2],num[i, 1] + num[i + 1, 1] - den[i, 1]]/den[
                    i, 2];
                  D := [k[i]];
                  dZero := false;

                elseif i - n_num2 == n_num1 then
                       // State space system in form (2) with 1 first order numerator polynomial
                  C := k[i]*[num[i, 1],1]/den[i, 2];
                  D := [0];
                  dZero := dZero and true;

                else
                       // State space system in form (3)
                  C := k[i]*[1,0]/den[i, 2];
                  D := [0];
                  dZero := dZero and true;

                end if;

              else
                    // State space system in form (1) with 2 first order denominator polynomials
                A[2, :] := {-(den[i, 1]*den[i + 1, 1]),-(den[i, 1] + den[i + 1, 1])};
                B[2, 1] := den[i, 1]*den[i, 1];

                C := k[i]*[num[i, 2] - (den[i, 1]*den[i + 1, 1]),num[i, 1] - (den[i, 1] + den[i + 1, 1])]/
                  den[i, 1]/den[i, 1];
                D := [k[i]];
                dZero := false;
              end if;

              ssA[2*i, 1:2*i - 2] := B[2, 1]*ssC[1, 1:2*i - 2];
              ssA[2*i - 1:2*i, 2*i - 1:2*i] := A;
              ssB[2*i, 1] := if dZero then 0 else B[2, 1]*ssD[1, 1];
              ssC[1, 1:2*i - 2] := if dZero then fill(0, 2*i - 2) else D[1, 1]*ssC[
                1, 1:2*i - 2];
              ssC[1, 2*i - 1:2*i] := vector(C);
              ssD := D*ssD;
            end for;

         //  for i in max(2,i_d):n_den1 loop

            for i in ili:n_den1 loop
                 // Construct state space systems of order 1
              a := if abs(den[n_den2 + i, 1])>Modelica.Constants.eps then -den[n_den2 + i, 1] else 0.0;
              b := if abs(den[n_den2 + i, 1])>Modelica.Constants.eps then den[n_den2 + i, 1] else 1.0;
              if n_num2 <= n_den2 and 2*(n_den2 - n_num2) + i <= n_num1 then
                    // State space system in form (4)

                c := if abs(den[n_den2 + i, 1])>Modelica.Constants.eps then k[i_k + i]*(num[max(1, n_num2 + 2*(n_den2 - n_num2) + i), 1] -  den[n_den2 + i, 1])/den[n_den2 + i, 1] else 1.0;

                d := k[i_k + i];

                dZero := false;
              elseif n_num2 > n_den2 and i - i_d + 1 <= n_num1 then
                    // State space system in form (4)

                c := if abs(den[n_den2 + i, 1])>Modelica.Constants.eps then k[i_k + i]*(num[max(1, n_num2 + i - i_d + 1), 1] - den[n_den2 + i, 1])/den[n_den2 + i, 1] else 1.0;
                d := k[i_k + i];
                dZero := false;
              else

                    // State space system in form (5)

                c := k[i_k + i]/den[n_den2 + i, 1];
                d := 0;
                dZero := dZero and true;
              end if;

              ssA[2*n_den2 + i, 1:2*n_den2 + i - 1] := b*ssC[1, 1:2*n_den2 + i - 1];
              ssA[2*n_den2 + i, 2*n_den2 + i] := a;
              ssB[2*n_den2 + i, 1] := if dZero then 0 else b*ssD[1, 1];
              ssC[1, 1:2*n_den2 + i - 1] := if dZero then fill(0, 2*n_den2 + i - 1) else
                      d*ssC[1, 1:2*n_den2 + i - 1];
              ssC[1, 2*n_den2 + i] := c;
              ssD := if dZero then [0] else d*ssD;

            end for;
            ssC := k_total*ssC;
            ssD := k_total*ssD;
          else
            ABCD := [zp.k];
          end if;

          ABCD := [ssA,ssB; ssC,ssD];

        end toMatrices;
      end Conversion;

      encapsulated package Import

      function fromModel
          "Generate a ZerosAndPoles record array from a state space representation resulted from linearization of a model"

          import Modelica;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input String modelName "Name of the Modelica model";
          input Real T_linearize=0
            "point in time of simulation to linearize the model";
          input String fileName="dslin" "Name of the result file";

        protected
          String fileName2=fileName + ".mat";
          Boolean OK1=simulateModel(
                problem=modelName,
                startTime=0,
                stopTime=T_linearize);
          Boolean OK2=importInitial("dsfinal.txt");
          Boolean OK3=linearizeModel(
                problem=modelName,
                resultFile=fileName,
                startTime=T_linearize,
                stopTime=T_linearize + 1);
          Real nxMat[1,1]=readMatrix(
                fileName2,
                "nx",
                1,
                1);
          Integer ABCDsizes[2]=readMatrixSize(fileName2, "ABCD");
          Integer nx=integer(nxMat[1, 1]);
          Integer nu=ABCDsizes[2] - nx;
          Integer ny=ABCDsizes[1] - nx;
          Real ABCD[nx + ny,nx + nu]=readMatrix(
                fileName2,
                "ABCD",
                nx + ny,
                nx + nu);
          String xuyName[nx + nu + ny]=readStringMatrix(
                fileName2,
                "xuyName",
                nx + nu + ny);

          StateSpace result(
            redeclare Real A[nx,nx],
            redeclare Real B[nx,nu],
            redeclare Real C[ny,nx],
            redeclare Real D[ny,nu]) "= model linearized at initial point";
        public
          output ZerosAndPoles zp[:,:];
      algorithm
          result.A := ABCD[1:nx, 1:nx];
          result.B := ABCD[1:nx, nx + 1:nx + nu];
          result.C := ABCD[nx + 1:nx + ny, 1:nx];
          result.D := ABCD[nx + 1:nx + ny, nx + 1:nx + nu];
          result.uNames := xuyName[nx + 1:nx + nu];
          result.yNames := xuyName[nx + nu + 1:nx + nu + ny];
          result.xNames := xuyName[1:nx];

          zp := StateSpace.Conversion.toZerosAndPolesMIMO(result);

      end fromModel;

        encapsulated function fromFile
          "Generate a ZerosAndPoles record by reading the polynomial coefficients or zeros and poles from a file"
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica;
          import Modelica_LinearSystems2.DataDir;

          input String fileName=DataDir + "/zp.mat"
            "Name of the zeros and poles data file";

        protected
          input Integer n1n2d1d2[4]=if ZerosAndPoles.Internal.checkRepresentation(
              fileName) then ZerosAndPoles.Internal.numberOfRealZerosAndPoles_zp(
              fileName) else ZerosAndPoles.Internal.numberOfRealZerosAndPoles_pc(
              fileName);
          input Integer n1=n1n2d1d2[1];
          input Integer n2=n1n2d1d2[2];
          input Integer d1=n1n2d1d2[3];
          input Integer d2=n1n2d1d2[4];
          input Integer zSize=n1n2d1d2[1] + 2*n1n2d1d2[2];
          input Integer pSize=n1n2d1d2[3] + 2*n1n2d1d2[4];
        public
          output ZerosAndPoles zp(
            n1=fill(0, n1),
            n2=fill(
                  0,
                  n2,
                  2),
            d1=fill(0, d1),
            d2=fill(
                  0,
                  d2,
                  2));
        algorithm
        //Whenever this function becomes operational the code must be rewritten if fromFile_pc2 and fromFile_zp2 are in the 'constructor'

          zp := if ZerosAndPoles.Internal.checkRepresentation(fileName) then ZerosAndPoles.Internal.fromFile_zp( fileName) else ZerosAndPoles.Internal.fromFile_pc(
            fileName);

        end fromFile;

      end Import;

      encapsulated package Internal
        "Internal library of record Filter (should not be directly used by user)"

        import Modelica;
        import Modelica_LinearSystems2;
        extends Modelica.Icons.Library;

      record ZerosAndPoles
          "Continuous zeros and poles description of a single input, single output system (data + operations)"
        extends Modelica.Icons.Record;

        Real k=1.0 "Multiplicative factor of transfer function";
        Real n1[:] "[p^0] coefficients of 1st order numerator polynomials";
        Real n2[:,2] "[p,p^0] coefficients of 2nd order numerator polynomials";
        Real d1[:] "[p^0] coefficients of 1st order denominator polynomials";
        Real d2[:,2] "[p,p^0] coefficients of 2nd order denominator polynomials";

      end ZerosAndPoles;

      encapsulated function filter
          "Generate the data record of a ZerosAndPoles transfer function from a filter description"

          import Modelica;
          import Modelica.Utilities.Streams;
          import Modelica.Utilities.Streams.print;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Types;
          import Modelica_LinearSystems2.ZerosAndPoles;

          import Modelica_LinearSystems2.ZerosAndPoles.Internal;
          import Modelica.Math.*;
          import Modelica_LinearSystems2.Math.*;

        input Modelica_LinearSystems2.Types.AnalogFilter analogFilter=Types.AnalogFilter.CriticalDamping
            "Analog filter characteristics (CriticalDamping/Bessel/Butterworth/Chebyshev)";
        input Modelica_LinearSystems2.Types.FilterType filterType=Types.FilterType.LowPass
            "Type of filter (LowPass/HighPass)";
        input Integer order(min=1) = 2 "Order of filter";
        input Modelica.SIunits.Frequency f_cut=1/(2*Modelica.Constants.pi)
            "Cut-off frequency (default is w_cut = 1 rad/s)";
        input Real gain=1.0
            "Gain (= amplitude of frequency response at zero frequency)";
        input Real A_ripple(unit="dB") = 0.5
            "Pass band ripple for Chebyshev filter (otherwise not used)";
        input Boolean normalized=true
            "= true, if amplitude of low pass filter at f_cut where the magnitude of the filter sagged for 3dB, otherwise unmodified filter";
        output Internal.ZerosAndPoles filter(
          redeclare Real n1[if filterType == Types.FilterType.LowPass then 0 else (
            if analogFilter == Types.AnalogFilter.CriticalDamping then order else
            mod(order, 2))],
          redeclare Real n2[if filterType == Types.FilterType.LowPass then 0 else (
            if analogFilter == Types.AnalogFilter.CriticalDamping then 0 else
            integer((order + 1)/2)),2],
          redeclare Real d1[if analogFilter == Types.AnalogFilter.CriticalDamping then
                  order else mod(order, 2)],
          redeclare Real d2[if analogFilter == Types.AnalogFilter.CriticalDamping then
                  0 else integer(order/2),2]) "Filter transfer function";

        protected
        Integer n_num1=size(filter.n1, 1);
        Integer n_num2=size(filter.n2, 1);
        Integer n_den1=size(filter.d1, 1);
        Integer n_den2=size(filter.d2, 1);
        Integer n_num=n_num1 + 2*n_num2;
        Integer n_den=n_den1 + 2*n_den2;
        Real pi=Modelica.Constants.pi;
        Boolean evenOrder=mod(order, 2) == 0
            "= true, if even filter order (otherwise uneven)";
        Modelica.SIunits.AngularVelocity w_cut=2*pi*f_cut
            "Cut-off angular frequency";
        Real w_cut2 "= w_cut*w_cut";
        Real alpha=1.0 "Frequency correction factor";
        Real alpha2 "= alpha*alpha";

        Real alphax;

        Real poleReal "Real part of pole";
        Real poleImag "Imaginary part of pole";
        Real epsilon "Ripple size";
        Real fac "arsinh(epsilon)";
        Real A2 "poleReal^2 + poleImag^2";
        Real A "Amplitude at w_cut";
        Real num1[n_num1]
            "[p] coefficients of numerator first order polynomials (a*p + 1)";
        Real num2[n_num2,2]
            "[p^2, p] coefficients of numerator second order polynomials (b*p^2 + a*p + 1I)";
        Real den1[n_den1]
            "[p] coefficients of denominator first order polynomials (a*p + 1)";
        Real den2[n_den2,2]
            "[p^2, p] coefficients of denominator second order polynomials (b*p^2 + a*p + 1I)";
        Real aux;
        Real k;
      algorithm
        // Set properties that are common for all filters
        filter.k := gain;

        /* Compute filter coefficients of prototype low pass filter. If another filter
         characteristics is desired (e.g. high pass filter), it is derived
         from the low pass filter coefficients below
      */
        if analogFilter == Types.AnalogFilter.CriticalDamping then
          if normalized then
            alpha := sqrt(2^(1/order) - 1);
      //alpha := sqrt(10^(3/10/order)-1)
          else
            alpha := 1;
          end if;
          for i in 1:n_den1 loop
            den1[i] := alpha;
          end for;

        elseif analogFilter == Types.AnalogFilter.Bessel then
          (den1,den2,alpha) := Internal.BesselCoefficients(order);
          if not normalized then
            alpha2 := alpha*alpha;
            for i in 1:n_den2 loop
              den2[i, 1] := den2[i, 1]*alpha2;
              den2[i, 2] := den2[i, 2]*alpha;
            end for;
            if not evenOrder then
              den1[1] := den1[1]*alpha;
            end if;
          end if;

        elseif analogFilter == Types.AnalogFilter.Butterworth then
           // Original filter is already normalized
          for i in 1:n_den2 loop
            den2[i, 1] := 1.0;
            den2[i, 2] := -2*cos(pi*(0.5 + (i - 0.5)/order));
          end for;
          if not evenOrder then
            den1[1] := 1.0;
          end if;

        elseif analogFilter == Types.AnalogFilter.Chebyshev then
          epsilon := sqrt(10^(A_ripple/10) - 1);
          fac := asinh(1/epsilon)/order;

          for i in 1:n_den2 loop
             // poles of prototype lowpass
            poleReal := cos(pi/2 + pi*(2*i - 1)/(2*order))*sinh(fac);
            poleImag := -sin(pi/2 + pi*(2*i - 1)/(2*order))*cosh(fac);

             // second order filter coefficients
            A2 := poleReal^2 + poleImag^2;
            den2[i, 1] := 1/A2;
            den2[i, 2] := -2*poleReal/A2;
          end for;
          if not evenOrder then
            den1[1] := -1.0/(cos(pi/2 + pi*(2*n_den - 1)/(2*order))*sinh(fac));
          end if;

           /* Transformation of filter transfer function with "new(p) = alpha*p"
            in order that the filter transfer function has an amplitude of
            1/sqrt(2) at the cutoff frequency
         */
          if normalized then
            alpha := Internal.normalizationFactor(den1, den2);
            alpha2 := alpha*alpha;
            for i in 1:n_den2 loop
              den2[i, 1] := den2[i, 1]*alpha2;
              den2[i, 2] := den2[i, 2]*alpha;
            end for;
            if not evenOrder then
              den1[1] := den1[1]*alpha;
            end if;
          end if;

        else
          Streams.error("analogFilter (= " + String(analogFilter) +
            ") is not supported");
        end if;

        // Compute amplitude at w=1
      /*
      A := 1.0;
      for i in 1:n_den2 loop
         A := A*(1 + den2[i,2]^2 - 2*den2[i,1]
                   + den2[i,1]^2);
      end for;
      for i in 1:n_den1 loop
         A := A*(1 + den1[i]^2);
      end for;
      A := 1/sqrt(A);
      print("A = " + String(A));
    */

        // Determine normalized denominator polynomials with highest power of p equal to one
        filter.n1 := zeros(n_num1);
        filter.n2 := zeros(n_num2, 2);
        (filter.d1,filter.d2,k) := Internal.filterToNormalized(den1, den2);
        filter.k := filter.k/k;

        // Compute desired filter characteristics from low pass filter coefficients
        if filterType == Types.FilterType.HighPass then
           /* The high pass filter is derived from the low pass filter by
            the transformation new(p) = 1/p
            1/(p^2 + a*p + b) -> 1/((1/p)^2 + a*(1/p) + b) = (1/b)*p^2 / (p^2 + (a/b)*p + 1/b)
            1/(p + a)         -> 1/((1/p) + a) = (1/a)*p / (p + (1/a))
         */
          assert(n_num1 == n_den1 and n_num2 == n_den2,
            "Internal error 1, should not occur");
          filter.n1 := zeros(n_num1);
          filter.n2 := zeros(n_num2, 2);
          for i in 1:n_num1 loop
            filter.k := filter.k/filter.d1[i];
            filter.d1[i] := 1/filter.d1[i];
          end for;
          for i in 1:n_num2 loop
            filter.k := filter.k/filter.d2[i, 2];
            filter.d2[i, 1] := filter.d2[i, 1]/filter.d2[i, 2];
            filter.d2[i, 2] := 1/filter.d2[i, 2];
          end for;
        end if;

        /* Change filter coefficients according to transformation new(p) = p/w_cut
         Numerator  :     (p/w)^2 + a*(p/w) + b = (1/w^2)*(p^2 + (a*w)*p + b*w^2)
                                      (p/w) + a = (1/w)*(p + w*a)
         Denominator: 1/((p/w)^2 + a*(p/w) + b) = w^2/(p^2 + (a*w)*p + w^2/b)
                                  1/((p/w) + a) = w/(p + w*a)
      */
        w_cut2 := w_cut*w_cut;
        filter.k := filter.k*w_cut^(n_den1 + 2*n_den2 - n_num1 - 2*n_num2);
        filter.n1 := w_cut*filter.n1;
        filter.d1 := w_cut*filter.d1;
        filter.n2 := [w_cut*filter.n2[:, 1],w_cut2*filter.n2[:, 2]];
        filter.d2 := [w_cut*filter.d2[:, 1],w_cut2*filter.d2[:, 2]];

      end filter;

        function findMatrixName
          input String filename;
          input String matName="z";

          output Integer m;

        external "C" m = findMatrixName(
                filename,
                matName,
                "NoClass");

        end findMatrixName;

      encapsulated function fromFile_pc
          "Generate a zeros and poles data record by reading the polynomial coefficients from a file (default file name is pc.mat)"
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica;

        input String fileName="pc.mat" "Name of the zeros and poles data file";

        protected
        input Integer n1n2d1d2[4]=
            ZerosAndPoles.Internal.numberOfRealZerosAndPoles_pc(fileName);
        input Integer n1=n1n2d1d2[1];
        input Integer n2=n1n2d1d2[2];
        input Integer d1=n1n2d1d2[3];
        input Integer d2=n1n2d1d2[4];
        input Integer zSize=n1n2d1d2[1] + 2*n1n2d1d2[2];
        input Integer pSize=n1n2d1d2[3] + 2*n1n2d1d2[4];
        public
        output ZerosAndPoles zp(
          n1=fill(0, n1),
          n2=fill(
                0,
                n2,
                2),
          d1=fill(0, d1),
          d2=fill(
                0,
                d2,
                2));

        protected
        Integer n1_2=if n1 > 0 then 1 else 0 "second dimension of n1-matrix";
        Integer n2_2=if n2 > 0 then 2 else 0 "second dimension of n2-matrix";
        Integer d1_2=if d1 > 0 then 1 else 0 "second dimension of d1-matrix";
        Integer d2_2=if d2 > 0 then 2 else 0 "second dimension of d2-matrix";

        Real k=scalar(readMatrix(
              fileName,
              "k",
              1,
              1));
        Real n1Vector[n1]=vector(readMatrix(
              fileName,
              "n1",
              n1,
              n1_2)) "coefficients of first order numenator polynomials";
        Real n2Matrix[n2,n2_2]=readMatrix(
              fileName,
              "n2",
              n2,
              n2_2) "coefficients of second order denominator polynomials";
        Real d1Vector[d1]=vector(readMatrix(
              fileName,
              "d1",
              d2,
              d1_2)) "coefficients of first order denominator polynomials";
        Real d2Matrix[d2,d2_2]=readMatrix(
              fileName,
              "d2",
              d2,
              d2_2) "coefficients of second order numenator polynomials";

      algorithm
        zp.k := k;
        zp.n1 := if n1 > 0 then n1Vector else fill(0, 0);
        zp.n2 := if n2 > 0 then n2Matrix else fill(
            0,
            0,
            2);
        zp.d1 := if d1 > 0 then d1Vector else fill(0, 0);
        zp.d2 := if d2 > 0 then d2Matrix else fill(
            0,
            0,
            2);

      end fromFile_pc;

      encapsulated function fromFile_zp
          "Generate a zeros and poles data record by reading poles and zeros from a file (default file name is zp.mat)"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Math.Complex;

        input String fileName="zp.mat" "Name of the zeros and poles data file";
        protected
        input Integer n1n2d1d2[4]=
            ZerosAndPoles.Internal.numberOfRealZerosAndPoles_zp(fileName);
        input Integer n1=n1n2d1d2[1];
        input Integer n2=n1n2d1d2[2];
        input Integer d1=n1n2d1d2[3];
        input Integer d2=n1n2d1d2[4];
        input Integer zSize=n1n2d1d2[1] + 2*n1n2d1d2[2];
        input Integer pSize=n1n2d1d2[3] + 2*n1n2d1d2[4];
        public
        output ZerosAndPoles zp(
          n1=fill(0, n1),
          n2=fill(
                0,
                n2,
                2),
          d1=fill(0, d1),
          d2=fill(
                0,
                d2,
                2));

        protected
        Integer z_2=if zSize > 0 then 2 else 0 "second dimension of zeros-matrix";
        Integer p_2=if pSize > 0 then 2 else 0 "second dimension of poles-matrix";

        Real k=scalar(readMatrix(
              fileName,
              "k",
              1,
              1));
        Real zerosMatrix[zSize,z_2]=readMatrix(
              fileName,
              "z",
              zSize,
              z_2) "zeros in rows of real parts and imaginary parts";
        Real polesMatrix[pSize,p_2]=readMatrix(
              fileName,
              "p",
              pSize,
              p_2) "poles in rows of real parts and imaginary parts";
        Complex zeros[:]=if zSize > 0 then ZerosAndPoles.Internal.fromRealAndImag(
            zerosMatrix[:, 1], zerosMatrix[:, z_2]) else fill(Complex(0), 0);
        Complex poles[:]=if pSize > 0 then ZerosAndPoles.Internal.fromRealAndImag(
            polesMatrix[:, 1], polesMatrix[:, p_2]) else fill(Complex(0), 0);

      algorithm
        zp := ZerosAndPoles(
            k=k,
            z=zeros,
            p=poles);
      end fromFile_zp;

        function checkRepresentation
          "checks whether the system on file is represented by zeros and poles (z, p) or first and second order polynomials (n1, n2, d1, d2)"
          import Modelica_LinearSystems2.ZerosAndPoles.Internal;
          import Modelica_LinearSystems2;
          input String fileName="zp.mat" "Name of the zeros and poles data file";
          output Boolean iszp=true;
        protected
          Integer m=0;

        algorithm
          m := Internal.findMatrixName(fileName, "z");
          m := m + Internal.findMatrixName(fileName, "p");
          iszp := m == 2;

        end checkRepresentation;

        function sameVectorElements
          "Determine identical elements of a vector"

          input Real v[:] "Vector";
          input Integer startIndex=1 "Start index";
          output Integer endIndex=startIndex
            "startIndex:endIndex are identical elements in v";
        protected
          Integer nv=size(v, 1);
          Integer i=startIndex + 1;
          Real v0=v[startIndex];
        algorithm
          while i <= nv loop
            if v[i] == v0 then
              endIndex := i;
              i := i + 1;
            else
              i := nv + 1;
            end if;
          end while;
        end sameVectorElements;

        function fromRealAndImag
          "Generate a complex vector from a real part vector and imaginary part vector "

          import Modelica_LinearSystems2.Math.Complex;

          input Real real[:];
          input Real imag[size(real, 1)];
          output Complex result[size(real, 1)] "Number of real zeros";
        algorithm
          for i in 1:size(real, 1) loop
            result[i].re := real[i];
            result[i].im := imag[i];
          end for;
        end fromRealAndImag;

        function sameMatrixRows
          "Determine identical rows of a [:,2] matrix"

          input Real M[:,2] "Matrix";
          input Integer startIndex=1 "Start index";
          output Integer endIndex=startIndex
            "startIndex:endIndex are identical rows in M";
        protected
          Integer n=size(M, 1);
          Integer i=startIndex + 1;
          Real v0[size(M, 2)]=M[startIndex, :];
        algorithm
          while i <= n loop
            if M[i, 1] == v0[1] and M[i, 2] == v0[2] then
              endIndex := i;
              i := i + 1;
            else
              i := n + 1;
            end if;
          end while;
        end sameMatrixRows;

        function firstOrderToString
          "Transform vector of coefficients of normalized first order polynomials to a string representation"
          import Modelica_LinearSystems2.Math.Vectors;

          input Real c[:] "p^0 coefficients of normalized first order polynomials";
          input Integer significantDigits=6
            "Number of significant digits that are shown";
          input String name="p" "Independent variable name used for printing";
          output String s="";
        protected
          Integer nc=size(c, 1);
          Real cs[nc]=Modelica.Math.Vectors.sort(c);
          Real cc[nc];
          Integer i;
          Integer j;
          Integer nj;
          Integer k;
          Boolean found=false;
        algorithm
          // Move coefficients with zero to the beginning
          i := 1;
          while i <= nc loop
            if cs[i] == 0 then
              found := true;
              k := i;
              i := nc + 1;
            else
              i := i + 1;
            end if;
          end while;

          if found then
            j := sameVectorElements(cs, k);
            cc := cat(
                  1,
                  cs[k:j],
                  cs[1:k - 1],
                  cs[j + 1:end]);
          else
            cc := cs;
          end if;

          // Transform coefficients to string
          i := 1;
          while i <= nc loop
            if i <> 1 then
              s := s + "*";
            end if;
            if cc[i] == 0 then
              s := s + name;
            else
              if cc[i] > 0 then
                s := s + "(" + name + " + ";
              else
                s := s + "(" + name;
              end if;
              s := s + String(cc[i], significantDigits=significantDigits) + ")";
            end if;
            j := sameVectorElements(cc, i);
            nj := j - i + 1;
            if nj > 1 then
              s := s + "^" + String(nj);
            end if;
            i := j + 1;
          end while;
        end firstOrderToString;

        function secondOrderToString
          "Transform vector of coefficients of normalized second order polynomials to a string representation"
          import Modelica_LinearSystems2.Math.Matrices;

          input Real c[:,2]
            "[p,p^0] coefficients of normalized second order polynomials";
          input Integer significantDigits=6
            "Number of significant digits that are shown";
          input String name="p" "Independent variable name used for printing";
          output String s="";
        protected
          Integer nc=size(c, 1);
          Real cc[nc,2]=Modelica.Math.Matrices.sort(c);
          Integer i=1;
          Integer j;
          Integer nj;
        algorithm
          while i <= nc loop
            if i <> 1 then
              s := s + "*";
            end if;
            j := sameMatrixRows(cc, i);
            nj := j - i + 1;
            if cc[i, 1] == 0 and cc[i, 2] == 0 then
                // case p^2
              s := s + name + "^" + String(2*nj);
            else
              s := s + "(" + name + "^2";

                // b*p term
              if cc[i, 1] == 1 then
                s := s + "+" + name;
              elseif cc[i, 1] == -1 then
                s := s + " - " + name;
              elseif cc[i, 1] <> 0 then
                if cc[i, 1] > 0 then
                  s := s + " + ";
                end if;
                s := s + String(cc[i, 1], significantDigits=significantDigits) +
                  "*" + name;
              end if;

                // a*p^0 term
              if cc[i, 2] > 0 then
                s := s + " + ";
              end if;
              s := s + String(cc[i, 2], significantDigits=significantDigits) + ")";
              if nj > 1 then
                s := s + "^" + String(nj);
              end if;
            end if;
            i := j + 1;
          end while;
        end secondOrderToString;

        function normalizationFactor
          "Compute correction factor of low pass filter such that amplitude at cut-off frequency is 1/sqrt(2) (= 3db)"
          import Modelica.Utilities.Streams;

          input Real c1[:] "[p] coefficients of denominator polynomials (a*p + 1)";
          input Real c2[:,2]
            "[p^2, p] coefficients of denominator polynomials (b*p^2 + a*p + 1)";
          output Real alpha "Correction factor (replace p by alpha*p)";
        protected
          Real oldAlpha=1.0;
          Integer counter=0;
          Integer counterMax=100;
          Boolean iterating=true;
          Real residue;
          Real der_residue;

          encapsulated function normalizationFactorResidues
            "Residue and residue derivative for Newton iteration of correction factor computation"
            input Real c1[:]
              "[p] coefficients of denominator polynomials (a*p + 1)";
            input Real c2[:,2]
              "[p^2, p] coefficients of denominator polynomials (b*p^2 + a*p + 1)";
            input Real alpha;
            output Real residue;
            output Real der_residue;
          protected
            Real beta=2;  //beta = 10^(3/10);
            Real cc1;
            Real cc2;
            Real p;
            Real pd;
            Real alpha2=alpha*alpha;
            Real alpha4=alpha2*alpha2;
            Real A2=1.0;
            Real fac=0.0;
          algorithm
            if size(c1, 1) == 1 then
              cc1 := c1[1]*c1[1];
              p := 1 + cc1*alpha2;
              pd := cc1;
              fac := fac + pd/p;
              A2 := A2*p;
            end if;
            for i in 1:size(c2, 1) loop
              cc1 := c2[i, 2]*c2[i, 2] - 2*c2[i, 1];
              cc2 := c2[i, 1]*c2[i, 1];
              p := 1 + cc1*alpha2 + cc2*alpha4;
              pd := cc1 + 2*cc2*alpha2;
              fac := fac + pd/p;
              A2 := A2*p;
            end for;
            residue := A2 - beta;
            der_residue := 2*alpha*A2*fac;
          end normalizationFactorResidues;
        algorithm
           // Simple Newton iteration
          while iterating loop
            counter := counter + 1;
            (residue,der_residue) := normalizationFactorResidues(
                  c1,
                  c2,
                  oldAlpha);
            alpha := oldAlpha - residue/der_residue;
            if abs(alpha - oldAlpha) <= 100*Modelica.Constants.eps then
              iterating := false;
            elseif counter > counterMax then
              Streams.error("Numerical problems when computing cutoff frequency\n"
                 + "correction factor of filter (use option normalized=false)");
            else
              oldAlpha := alpha;
            end if;
          end while;
          assert(alpha > 0.0,
            "Numerical problems when computing cutoff frequency\n" +
            "correction factor of filter (alpha = " + String(alpha) + ")\n" +
            "(use option normalized=false)");
        end normalizationFactor;

        function BesselCoefficients
          "Return coefficients of normalized low pass Bessel filter (= gain at cut-off frequency 1 rad/s is decreased 3dB"

          import Modelica.Utilities.Streams;
          import Modelica_LinearSystems2;
          input Integer order "Order of filter in the range 1..41";
          output Real c1[mod(order, 2)]
            "[p] coefficients of Bessel denominator polynomials (a*p + 1)";
          output Real c2[integer(order/2),2]
            "[p^2, p] coefficients of Bessel denominator polynomials (b2*p^2 + b1*p + 1)";
          output Real alpha "Normalization factor";
        algorithm
          if order == 1 then
            alpha := 1.002377293007601;
            c1[1] := 0.9976283451109835;
          elseif order == 2 then
            alpha := 0.7356641785819585;
            c2[1, 1] := 0.6159132201783791;
            c2[1, 2] := 1.359315879600889;
          elseif order == 3 then
            alpha := 0.5704770156982642;
            c1[1] := 0.7548574865985343;
            c2[1, 1] := 0.4756958028827457;
            c2[1, 2] := 0.9980615136104388;
          elseif order == 4 then
            alpha := 0.4737978580281427;
            c2[1, 1] := 0.4873729247240677;
            c2[1, 2] := 1.337564170455762;
            c2[2, 1] := 0.3877724315741958;
            c2[2, 2] := 0.7730405590839861;
          elseif order == 5 then
            alpha := 0.4126226974763408;
            c1[1] := 0.6645723262620757;
            c2[1, 1] := 0.4115231900614016;
            c2[1, 2] := 1.138349926728708;
            c2[2, 1] := 0.3234938702877912;
            c2[2, 2] := 0.6205992985771313;
          elseif order == 6 then
            alpha := 0.3705098000736233;
            c2[1, 1] := 0.3874508649098960;
            c2[1, 2] := 1.219740879520741;
            c2[2, 1] := 0.3493298843155746;
            c2[2, 2] := 0.9670265529381365;
            c2[3, 1] := 0.2747419229514599;
            c2[3, 2] := 0.5122165075105700;
          elseif order == 7 then
            alpha := 0.3393452623586350;
            c1[1] := 0.5927147125821412;
            c2[1, 1] := 0.3383379423919174;
            c2[1, 2] := 1.092630816438030;
            c2[2, 1] := 0.3001025788696046;
            c2[2, 2] := 0.8289928256598656;
            c2[3, 1] := 0.2372867471539579;
            c2[3, 2] := 0.4325128641920154;
          elseif order == 8 then
            alpha := 0.3150267393795002;
            c2[1, 1] := 0.3151115975207653;
            c2[1, 2] := 1.109403015460190;
            c2[2, 1] := 0.2969344839572762;
            c2[2, 2] := 0.9737455812222699;
            c2[3, 1] := 0.2612545921889538;
            c2[3, 2] := 0.7190394712068573;
            c2[4, 1] := 0.2080523342974281;
            c2[4, 2] := 0.3721456473047434;
          elseif order == 9 then
            alpha := 0.2953310177184124;
            c1[1] := 0.5377196679501422;
            c2[1, 1] := 0.2824689124281034;
            c2[1, 2] := 1.022646191567475;
            c2[2, 1] := 0.2626824161383468;
            c2[2, 2] := 0.8695626454762596;
            c2[3, 1] := 0.2302781917677917;
            c2[3, 2] := 0.6309047553448520;
            c2[4, 1] := 0.1847991729757028;
            c2[4, 2] := 0.3251978031287202;
          elseif order == 10 then
            alpha := 0.2789426890619463;
            c2[1, 1] := 0.2640769908255582;
            c2[1, 2] := 1.019788132875305;
            c2[2, 1] := 0.2540802639216947;
            c2[2, 2] := 0.9377020417760623;
            c2[3, 1] := 0.2343577229427963;
            c2[3, 2] := 0.7802229808216112;
            c2[4, 1] := 0.2052193139338624;
            c2[4, 2] := 0.5594176813008133;
            c2[5, 1] := 0.1659546953748916;
            c2[5, 2] := 0.2878349616233292;
          elseif order == 11 then
            alpha := 0.2650227766037203;
            c1[1] := 0.4950265498954191;
            c2[1, 1] := 0.2411858478546218;
            c2[1, 2] := 0.9567800996387417;
            c2[2, 1] := 0.2296849355380925;
            c2[2, 2] := 0.8592523717113126;
            c2[3, 1] := 0.2107851705677406;
            c2[3, 2] := 0.7040216048898129;
            c2[4, 1] := 0.1846461385164021;
            c2[4, 2] := 0.5006729207276717;
            c2[5, 1] := 0.1504217970817433;
            c2[5, 2] := 0.2575070491320295;
          elseif order == 12 then
            alpha := 0.2530051198547209;
            c2[1, 1] := 0.2268294941204543;
            c2[1, 2] := 0.9473116570034053;
            c2[2, 1] := 0.2207657387793729;
            c2[2, 2] := 0.8933728946287606;
            c2[3, 1] := 0.2087600700376653;
            c2[3, 2] := 0.7886236252756229;
            c2[4, 1] := 0.1909959101492760;
            c2[4, 2] := 0.6389263649257017;
            c2[5, 1] := 0.1675208146048472;
            c2[5, 2] := 0.4517847275162215;
            c2[6, 1] := 0.1374257286372761;
            c2[6, 2] := 0.2324699157474680;
          elseif order == 13 then
            alpha := 0.2424910397561007;
            c1[1] := 0.4608848369928040;
            c2[1, 1] := 0.2099813050274780;
            c2[1, 2] := 0.8992478823790660;
            c2[2, 1] := 0.2027250423101359;
            c2[2, 2] := 0.8328117484224146;
            c2[3, 1] := 0.1907635894058731;
            c2[3, 2] := 0.7257379204691213;
            c2[4, 1] := 0.1742280397887686;
            c2[4, 2] := 0.5830640944868014;
            c2[5, 1] := 0.1530858190490478;
            c2[5, 2] := 0.4106192089751885;
            c2[6, 1] := 0.1264090712880446;
            c2[6, 2] := 0.2114980230156001;
          elseif order == 14 then
            alpha := 0.2331902368695848;
            c2[1, 1] := 0.1986162311411235;
            c2[1, 2] := 0.8876961808055535;
            c2[2, 1] := 0.1946683341271615;
            c2[2, 2] := 0.8500754229171967;
            c2[3, 1] := 0.1868331332895056;
            c2[3, 2] := 0.7764629313723603;
            c2[4, 1] := 0.1752118757862992;
            c2[4, 2] := 0.6699720402924552;
            c2[5, 1] := 0.1598906457908402;
            c2[5, 2] := 0.5348446712848934;
            c2[6, 1] := 0.1407810153019944;
            c2[6, 2] := 0.3755841316563539;
            c2[7, 1] := 0.1169627966707339;
            c2[7, 2] := 0.1937088226304455;
          elseif order == 15 then
            alpha := 0.2248854870552422;
            c1[1] := 0.4328492272335646;
            c2[1, 1] := 0.1857292591004588;
            c2[1, 2] := 0.8496337061962563;
            c2[2, 1] := 0.1808644178280136;
            c2[2, 2] := 0.8020517898136011;
            c2[3, 1] := 0.1728264404199081;
            c2[3, 2] := 0.7247449729331105;
            c2[4, 1] := 0.1616970125901954;
            c2[4, 2] := 0.6205369315943097;
            c2[5, 1] := 0.1475257264578426;
            c2[5, 2] := 0.4929612162355906;
            c2[6, 1] := 0.1301861023357119;
            c2[6, 2] := 0.3454770708040735;
            c2[7, 1] := 0.1087810777120188;
            c2[7, 2] := 0.1784526655428406;
          elseif order == 16 then
            alpha := 0.2174105053474761;
            c2[1, 1] := 0.1765637967473151;
            c2[1, 2] := 0.8377453068635511;
            c2[2, 1] := 0.1738525357503125;
            c2[2, 2] := 0.8102988957433199;
            c2[3, 1] := 0.1684627004613343;
            c2[3, 2] := 0.7563265923413258;
            c2[4, 1] := 0.1604519074815815;
            c2[4, 2] := 0.6776082294687619;
            c2[5, 1] := 0.1498828607802206;
            c2[5, 2] := 0.5766417034027680;
            c2[6, 1] := 0.1367764717792823;
            c2[6, 2] := 0.4563528264410489;
            c2[7, 1] := 0.1209810465419295;
            c2[7, 2] := 0.3193782657322374;
            c2[8, 1] := 0.1016312648007554;
            c2[8, 2] := 0.1652419227369036;
          elseif order == 17 then
            alpha := 0.2106355148193306;
            c1[1] := 0.4093223608497299;
            c2[1, 1] := 0.1664014345826274;
            c2[1, 2] := 0.8067173752345952;
            c2[2, 1] := 0.1629839591538256;
            c2[2, 2] := 0.7712924931447541;
            c2[3, 1] := 0.1573277802512491;
            c2[3, 2] := 0.7134213666303411;
            c2[4, 1] := 0.1494828185148637;
            c2[4, 2] := 0.6347841731714884;
            c2[5, 1] := 0.1394948812681826;
            c2[5, 2] := 0.5375594414619047;
            c2[6, 1] := 0.1273627583380806;
            c2[6, 2] := 0.4241608926375478;
            c2[7, 1] := 0.1129187258461290;
            c2[7, 2] := 0.2965752009703245;
            c2[8, 1] := 0.9533357359908857e-1;
            c2[8, 2] := 0.1537041700889585;
          elseif order == 18 then
            alpha := 0.2044575288651841;
            c2[1, 1] := 0.1588768571976356;
            c2[1, 2] := 0.7951914263212913;
            c2[2, 1] := 0.1569357024981854;
            c2[2, 2] := 0.7744529690772538;
            c2[3, 1] := 0.1530722206358810;
            c2[3, 2] := 0.7335304425992080;
            c2[4, 1] := 0.1473206710524167;
            c2[4, 2] := 0.6735038935387268;
            c2[5, 1] := 0.1397225420331520;
            c2[5, 2] := 0.5959151542621590;
            c2[6, 1] := 0.1303092459809849;
            c2[6, 2] := 0.5026483447894845;
            c2[7, 1] := 0.1190627367060072;
            c2[7, 2] := 0.3956893824587150;
            c2[8, 1] := 0.1058058030798994;
            c2[8, 2] := 0.2765091830730650;
            c2[9, 1] := 0.8974708108800873e-1;
            c2[9, 2] := 0.1435505288284833;
          elseif order == 19 then
            alpha := 0.1987936248083529;
            c1[1] := 0.3892259966869526;
            c2[1, 1] := 0.1506640012172225;
            c2[1, 2] := 0.7693121733774260;
            c2[2, 1] := 0.1481728062796673;
            c2[2, 2] := 0.7421133586741549;
            c2[3, 1] := 0.1440444668388838;
            c2[3, 2] := 0.6975075386214800;
            c2[4, 1] := 0.1383101628540374;
            c2[4, 2] := 0.6365464378910025;
            c2[5, 1] := 0.1310032283190998;
            c2[5, 2] := 0.5606211948462122;
            c2[6, 1] := 0.1221431166405330;
            c2[6, 2] := 0.4713530424221445;
            c2[7, 1] := 0.1116991161103884;
            c2[7, 2] := 0.3703717538617073;
            c2[8, 1] := 0.9948917351196349e-1;
            c2[8, 2] := 0.2587371155559744;
            c2[9, 1] := 0.8475989238107367e-1;
            c2[9, 2] := 0.1345537894555993;
          elseif order == 20 then
            alpha := 0.1935761760416219;
            c2[1, 1] := 0.1443871348337404;
            c2[1, 2] := 0.7584165598446141;
            c2[2, 1] := 0.1429501891353184;
            c2[2, 2] := 0.7423000962318863;
            c2[3, 1] := 0.1400877384920004;
            c2[3, 2] := 0.7104185332215555;
            c2[4, 1] := 0.1358210369491446;
            c2[4, 2] := 0.6634599783272630;
            c2[5, 1] := 0.1301773703034290;
            c2[5, 2] := 0.6024175491895959;
            c2[6, 1] := 0.1231826501439148;
            c2[6, 2] := 0.5285332736326852;
            c2[7, 1] := 0.1148465498575254;
            c2[7, 2] := 0.4431977385498628;
            c2[8, 1] := 0.1051289462376788;
            c2[8, 2] := 0.3477444062821162;
            c2[9, 1] := 0.9384622797485121e-1;
            c2[9, 2] := 0.2429038300327729;
            c2[10, 1] := 0.8028211612831444e-1;
            c2[10, 2] := 0.1265329974009533;
          elseif order == 21 then
            alpha := 0.1887494014766075;
            c1[1] := 0.3718070668941645;
            c2[1, 1] := 0.1376151928386445;
            c2[1, 2] := 0.7364290859445481;
            c2[2, 1] := 0.1357438914390695;
            c2[2, 2] := 0.7150167318935022;
            c2[3, 1] := 0.1326398453462415;
            c2[3, 2] := 0.6798001808470175;
            c2[4, 1] := 0.1283231214897678;
            c2[4, 2] := 0.6314663440439816;
            c2[5, 1] := 0.1228169159777534;
            c2[5, 2] := 0.5709353626166905;
            c2[6, 1] := 0.1161406100773184;
            c2[6, 2] := 0.4993087153571335;
            c2[7, 1] := 0.1082959649233524;
            c2[7, 2] := 0.4177766148584385;
            c2[8, 1] := 0.9923596957485723e-1;
            c2[8, 2] := 0.3274257287232124;
            c2[9, 1] := 0.8877776108724853e-1;
            c2[9, 2] := 0.2287218166767916;
            c2[10, 1] := 0.7624076527736326e-1;
            c2[10, 2] := 0.1193423971506988;
          elseif order == 22 then
            alpha := 0.1842668221199706;
            c2[1, 1] := 0.1323053462701543;
            c2[1, 2] := 0.7262446126765204;
            c2[2, 1] := 0.1312121721769772;
            c2[2, 2] := 0.7134286088450949;
            c2[3, 1] := 0.1290330911166814;
            c2[3, 2] := 0.6880287870435514;
            c2[4, 1] := 0.1257817990372067;
            c2[4, 2] := 0.6505015800059301;
            c2[5, 1] := 0.1214765261983008;
            c2[5, 2] := 0.6015107185211451;
            c2[6, 1] := 0.1161365140967959;
            c2[6, 2] := 0.5418983553698413;
            c2[7, 1] := 0.1097755171533100;
            c2[7, 2] := 0.4726370779831614;
            c2[8, 1] := 0.1023889478519956;
            c2[8, 2] := 0.3947439506537486;
            c2[9, 1] := 0.9392485861253800e-1;
            c2[9, 2] := 0.3090996703083202;
            c2[10, 1] := 0.8420273775456455e-1;
            c2[10, 2] := 0.2159561978556017;
            c2[11, 1] := 0.7257600023938262e-1;
            c2[11, 2] := 0.1128633732721116;
          elseif order == 23 then
            alpha := 0.1800893554453722;
            c1[1] := 0.3565232673929280;
            c2[1, 1] := 0.1266275171652706;
            c2[1, 2] := 0.7072778066734162;
            c2[2, 1] := 0.1251865227648538;
            c2[2, 2] := 0.6900676345785905;
            c2[3, 1] := 0.1227944815236645;
            c2[3, 2] := 0.6617011100576023;
            c2[4, 1] := 0.1194647013077667;
            c2[4, 2] := 0.6226432315773119;
            c2[5, 1] := 0.1152132989252356;
            c2[5, 2] := 0.5735222810625359;
            c2[6, 1] := 0.1100558598478487;
            c2[6, 2] := 0.5151027978024605;
            c2[7, 1] := 0.1040013558214886;
            c2[7, 2] := 0.4482410942032739;
            c2[8, 1] := 0.9704014176512626e-1;
            c2[8, 2] := 0.3738049984631116;
            c2[9, 1] := 0.8911683905758054e-1;
            c2[9, 2] := 0.2925028692588410;
            c2[10, 1] := 0.8005438265072295e-1;
            c2[10, 2] := 0.2044134600278901;
            c2[11, 1] := 0.6923832296800832e-1;
            c2[11, 2] := 0.1069984887283394;
          elseif order == 24 then
            alpha := 0.1761838665838427;
            c2[1, 1] := 0.1220804912720132;
            c2[1, 2] := 0.6978026874156063;
            c2[2, 1] := 0.1212296762358897;
            c2[2, 2] := 0.6874139794926736;
            c2[3, 1] := 0.1195328372961027;
            c2[3, 2] := 0.6667954259551859;
            c2[4, 1] := 0.1169990987333593;
            c2[4, 2] := 0.6362602049901176;
            c2[5, 1] := 0.1136409040480130;
            c2[5, 2] := 0.5962662188435553;
            c2[6, 1] := 0.1094722001757955;
            c2[6, 2] := 0.5474001634109253;
            c2[7, 1] := 0.1045052832229087;
            c2[7, 2] := 0.4903523180249535;
            c2[8, 1] := 0.9874509806025907e-1;
            c2[8, 2] := 0.4258751523524645;
            c2[9, 1] := 0.9217799943472177e-1;
            c2[9, 2] := 0.3547079765396403;
            c2[10, 1] := 0.8474633796250476e-1;
            c2[10, 2] := 0.2774145482392767;
            c2[11, 1] := 0.7627722381240495e-1;
            c2[11, 2] := 0.1939329108084139;
            c2[12, 1] := 0.6618645465422745e-1;
            c2[12, 2] := 0.1016670147947242;
          elseif order == 25 then
            alpha := 0.1725220521949266;
            c1[1] := 0.3429735385896000;
            c2[1, 1] := 0.1172525033170618;
            c2[1, 2] := 0.6812327932576614;
            c2[2, 1] := 0.1161194585333535;
            c2[2, 2] := 0.6671566071153211;
            c2[3, 1] := 0.1142375145794466;
            c2[3, 2] := 0.6439167855053158;
            c2[4, 1] := 0.1116157454252308;
            c2[4, 2] := 0.6118378416180135;
            c2[5, 1] := 0.1082654809459177;
            c2[5, 2] := 0.5713609763370088;
            c2[6, 1] := 0.1041985674230918;
            c2[6, 2] := 0.5230289949762722;
            c2[7, 1] := 0.9942439308123559e-1;
            c2[7, 2] := 0.4674627926041906;
            c2[8, 1] := 0.9394453593830893e-1;
            c2[8, 2] := 0.4053226688298811;
            c2[9, 1] := 0.8774221237222533e-1;
            c2[9, 2] := 0.3372372276379071;
            c2[10, 1] := 0.8075839512216483e-1;
            c2[10, 2] := 0.2636485508005428;
            c2[11, 1] := 0.7282483286646764e-1;
            c2[11, 2] := 0.1843801345273085;
            c2[12, 1] := 0.6338571166846652e-1;
            c2[12, 2] := 0.9680153764737715e-1;
          elseif order == 26 then
            alpha := 0.1690795702796737;
            c2[1, 1] := 0.1133168695796030;
            c2[1, 2] := 0.6724297955493932;
            c2[2, 1] := 0.1126417845769961;
            c2[2, 2] := 0.6638709519790540;
            c2[3, 1] := 0.1112948749545606;
            c2[3, 2] := 0.6468652038763624;
            c2[4, 1] := 0.1092823986944244;
            c2[4, 2] := 0.6216337070799265;
            c2[5, 1] := 0.1066130386697976;
            c2[5, 2] := 0.5885011413992190;
            c2[6, 1] := 0.1032969057045413;
            c2[6, 2] := 0.5478864278297548;
            c2[7, 1] := 0.9934388184210715e-1;
            c2[7, 2] := 0.5002885306054287;
            c2[8, 1] := 0.9476081523436283e-1;
            c2[8, 2] := 0.4462644847551711;
            c2[9, 1] := 0.8954648464575577e-1;
            c2[9, 2] := 0.3863930785049522;
            c2[10, 1] := 0.8368166847159917e-1;
            c2[10, 2] := 0.3212074592527143;
            c2[11, 1] := 0.7710664731701103e-1;
            c2[11, 2] := 0.2510470347119383;
            c2[12, 1] := 0.6965807988411425e-1;
            c2[12, 2] := 0.1756419294111342;
            c2[13, 1] := 0.6080674930548766e-1;
            c2[13, 2] := 0.9234535279274277e-1;
          elseif order == 27 then
            alpha := 0.1658353543067995;
            c1[1] := 0.3308543720638957;
            c2[1, 1] := 0.1091618578712746;
            c2[1, 2] := 0.6577977071169651;
            c2[2, 1] := 0.1082549561495043;
            c2[2, 2] := 0.6461121666520275;
            c2[3, 1] := 0.1067479247890451;
            c2[3, 2] := 0.6267937760991321;
            c2[4, 1] := 0.1046471079537577;
            c2[4, 2] := 0.6000750116745808;
            c2[5, 1] := 0.1019605976654259;
            c2[5, 2] := 0.5662734183049320;
            c2[6, 1] := 0.9869726954433709e-1;
            c2[6, 2] := 0.5257827234948534;
            c2[7, 1] := 0.9486520934132483e-1;
            c2[7, 2] := 0.4790595019077763;
            c2[8, 1] := 0.9046906518775348e-1;
            c2[8, 2] := 0.4266025862147336;
            c2[9, 1] := 0.8550529998276152e-1;
            c2[9, 2] := 0.3689188223512328;
            c2[10, 1] := 0.7995282239306020e-1;
            c2[10, 2] := 0.3064589322702932;
            c2[11, 1] := 0.7375174596252882e-1;
            c2[11, 2] := 0.2394754504667310;
            c2[12, 1] := 0.6674377263329041e-1;
            c2[12, 2] := 0.1676223546666024;
            c2[13, 1] := 0.5842458027529246e-1;
            c2[13, 2] := 0.8825044329219431e-1;
          elseif order == 28 then
            alpha := 0.1627710671942929;
            c2[1, 1] := 0.1057232656113488;
            c2[1, 2] := 0.6496161226860832;
            c2[2, 1] := 0.1051786825724864;
            c2[2, 2] := 0.6424661279909941;
            c2[3, 1] := 0.1040917964935006;
            c2[3, 2] := 0.6282470268918791;
            c2[4, 1] := 0.1024670101953951;
            c2[4, 2] := 0.6071189030701136;
            c2[5, 1] := 0.1003105109519892;
            c2[5, 2] := 0.5793175191747016;
            c2[6, 1] := 0.9762969425430802e-1;
            c2[6, 2] := 0.5451486608855443;
            c2[7, 1] := 0.9443223803058400e-1;
            c2[7, 2] := 0.5049796971628137;
            c2[8, 1] := 0.9072460982036488e-1;
            c2[8, 2] := 0.4592270546572523;
            c2[9, 1] := 0.8650956423253280e-1;
            c2[9, 2] := 0.4083368605952977;
            c2[10, 1] := 0.8178165740374893e-1;
            c2[10, 2] := 0.3527525188880655;
            c2[11, 1] := 0.7651838885868020e-1;
            c2[11, 2] := 0.2928534570013572;
            c2[12, 1] := 0.7066010532447490e-1;
            c2[12, 2] := 0.2288185204390681;
            c2[13, 1] := 0.6405358596145789e-1;
            c2[13, 2] := 0.1602396172588190;
            c2[14, 1] := 0.5621780070227172e-1;
            c2[14, 2] := 0.8447589564915071e-1;
          elseif order == 29 then
            alpha := 0.1598706626277596;
            c1[1] := 0.3199314513011623;
            c2[1, 1] := 0.1021101032532951;
            c2[1, 2] := 0.6365758882240111;
            c2[2, 1] := 0.1013729819392774;
            c2[2, 2] := 0.6267495975736321;
            c2[3, 1] := 0.1001476175660628;
            c2[3, 2] := 0.6104876178266819;
            c2[4, 1] := 0.9843854640428316e-1;
            c2[4, 2] := 0.5879603139195113;
            c2[5, 1] := 0.9625164534591696e-1;
            c2[5, 2] := 0.5594012291050210;
            c2[6, 1] := 0.9359356960417668e-1;
            c2[6, 2] := 0.5251016150410664;
            c2[7, 1] := 0.9047086748649986e-1;
            c2[7, 2] := 0.4854024475590397;
            c2[8, 1] := 0.8688856407189167e-1;
            c2[8, 2] := 0.4406826457109709;
            c2[9, 1] := 0.8284779224069856e-1;
            c2[9, 2] := 0.3913408089298914;
            c2[10, 1] := 0.7834154620997181e-1;
            c2[10, 2] := 0.3377643999400627;
            c2[11, 1] := 0.7334628941928766e-1;
            c2[11, 2] := 0.2802710651919946;
            c2[12, 1] := 0.6780290487362146e-1;
            c2[12, 2] := 0.2189770008083379;
            c2[13, 1] := 0.6156321231528423e-1;
            c2[13, 2] := 0.1534235999306070;
            c2[14, 1] := 0.5416797446761512e-1;
            c2[14, 2] := 0.8098664736760292e-1;
          elseif order == 30 then
            alpha := 0.1571200296252450;
            c2[1, 1] := 0.9908074847842124e-1;
            c2[1, 2] := 0.6289618807831557;
            c2[2, 1] := 0.9863509708328196e-1;
            c2[2, 2] := 0.6229164525571278;
            c2[3, 1] := 0.9774542692037148e-1;
            c2[3, 2] := 0.6108853364240036;
            c2[4, 1] := 0.9641490581986484e-1;
            c2[4, 2] := 0.5929869253412513;
            c2[5, 1] := 0.9464802912225441e-1;
            c2[5, 2] := 0.5693960175547550;
            c2[6, 1] := 0.9245027206218041e-1;
            c2[6, 2] := 0.5403402396359503;
            c2[7, 1] := 0.8982754584112941e-1;
            c2[7, 2] := 0.5060948065875106;
            c2[8, 1] := 0.8678535291732599e-1;
            c2[8, 2] := 0.4669749797983789;
            c2[9, 1] := 0.8332744242052199e-1;
            c2[9, 2] := 0.4233249626334694;
            c2[10, 1] := 0.7945356393775309e-1;
            c2[10, 2] := 0.3755006094498054;
            c2[11, 1] := 0.7515543969833788e-1;
            c2[11, 2] := 0.3238400339292700;
            c2[12, 1] := 0.7040879901685638e-1;
            c2[12, 2] := 0.2686072427439079;
            c2[13, 1] := 0.6515528854010540e-1;
            c2[13, 2] := 0.2098650589782619;
            c2[14, 1] := 0.5925168237177876e-1;
            c2[14, 2] := 0.1471138832654873;
            c2[15, 1] := 0.5225913954211672e-1;
            c2[15, 2] := 0.7775248839507864e-1;
          elseif order == 31 then
            alpha := 0.1545067022920929;
            c1[1] := 0.3100206996451866;
            c2[1, 1] := 0.9591020358831668e-1;
            c2[1, 2] := 0.6172474793293396;
            c2[2, 1] := 0.9530301275601203e-1;
            c2[2, 2] := 0.6088916323460413;
            c2[3, 1] := 0.9429332655402368e-1;
            c2[3, 2] := 0.5950511595503025;
            c2[4, 1] := 0.9288445429894548e-1;
            c2[4, 2] := 0.5758534119053522;
            c2[5, 1] := 0.9108073420087422e-1;
            c2[5, 2] := 0.5514734636081183;
            c2[6, 1] := 0.8888719137536870e-1;
            c2[6, 2] := 0.5221306199481831;
            c2[7, 1] := 0.8630901440239650e-1;
            c2[7, 2] := 0.4880834248148061;
            c2[8, 1] := 0.8335074993373294e-1;
            c2[8, 2] := 0.4496225358496770;
            c2[9, 1] := 0.8001502494376102e-1;
            c2[9, 2] := 0.4070602306679052;
            c2[10, 1] := 0.7630041338037624e-1;
            c2[10, 2] := 0.3607139804818122;
            c2[11, 1] := 0.7219760885744920e-1;
            c2[11, 2] := 0.3108783301229550;
            c2[12, 1] := 0.6768185077153345e-1;
            c2[12, 2] := 0.2577706252514497;
            c2[13, 1] := 0.6269571766328638e-1;
            c2[13, 2] := 0.2014081375889921;
            c2[14, 1] := 0.5710081766945065e-1;
            c2[14, 2] := 0.1412581515841926;
            c2[15, 1] := 0.5047740914807019e-1;
            c2[15, 2] := 0.7474725873250158e-1;
          elseif order == 32 then
            alpha := 0.1520196210848210;
            c2[1, 1] := 0.9322163554339406e-1;
            c2[1, 2] := 0.6101488690506050;
            c2[2, 1] := 0.9285233997694042e-1;
            c2[2, 2] := 0.6049832320721264;
            c2[3, 1] := 0.9211494244473163e-1;
            c2[3, 2] := 0.5946969295569034;
            c2[4, 1] := 0.9101176786042449e-1;
            c2[4, 2] := 0.5793791854364477;
            c2[5, 1] := 0.8954614071360517e-1;
            c2[5, 2] := 0.5591619969234026;
            c2[6, 1] := 0.8772216763680164e-1;
            c2[6, 2] := 0.5342177994699602;
            c2[7, 1] := 0.8554440426912734e-1;
            c2[7, 2] := 0.5047560942986598;
            c2[8, 1] := 0.8301735302045588e-1;
            c2[8, 2] := 0.4710187048140929;
            c2[9, 1] := 0.8014469519188161e-1;
            c2[9, 2] := 0.4332730387207936;
            c2[10, 1] := 0.7692807528893225e-1;
            c2[10, 2] := 0.3918021436411035;
            c2[11, 1] := 0.7336507157284898e-1;
            c2[11, 2] := 0.3468890521471250;
            c2[12, 1] := 0.6944555312763458e-1;
            c2[12, 2] := 0.2987898029050460;
            c2[13, 1] := 0.6514446669420571e-1;
            c2[13, 2] := 0.2476810747407199;
            c2[14, 1] := 0.6040544477732702e-1;
            c2[14, 2] := 0.1935412053397663;
            c2[15, 1] := 0.5509478650672775e-1;
            c2[15, 2] := 0.1358108994174911;
            c2[16, 1] := 0.4881064725720192e-1;
            c2[16, 2] := 0.7194819894416505e-1;
          elseif order == 33 then
            alpha := 0.1496489351138032;
            c1[1] := 0.3009752799176432;
            c2[1, 1] := 0.9041725460994505e-1;
            c2[1, 2] := 0.5995521047364046;
            c2[2, 1] := 0.8991117804113002e-1;
            c2[2, 2] := 0.5923764112099496;
            c2[3, 1] := 0.8906941547422532e-1;
            c2[3, 2] := 0.5804822013853129;
            c2[4, 1] := 0.8789442491445575e-1;
            c2[4, 2] := 0.5639663528946501;
            c2[5, 1] := 0.8638945831033775e-1;
            c2[5, 2] := 0.5429623519607796;
            c2[6, 1] := 0.8455834602616358e-1;
            c2[6, 2] := 0.5176379938389326;
            c2[7, 1] := 0.8240517431382334e-1;
            c2[7, 2] := 0.4881921474066189;
            c2[8, 1] := 0.7993380417355076e-1;
            c2[8, 2] := 0.4548502528082586;
            c2[9, 1] := 0.7714713890732801e-1;
            c2[9, 2] := 0.4178579388038483;
            c2[10, 1] := 0.7404596598181127e-1;
            c2[10, 2] := 0.3774715722484659;
            c2[11, 1] := 0.7062702339160462e-1;
            c2[11, 2] := 0.3339432938810453;
            c2[12, 1] := 0.6687952672391507e-1;
            c2[12, 2] := 0.2874950693388235;
            c2[13, 1] := 0.6277828912909767e-1;
            c2[13, 2] := 0.2382680702894708;
            c2[14, 1] := 0.5826808305383988e-1;
            c2[14, 2] := 0.1862073169968455;
            c2[15, 1] := 0.5321974125363517e-1;
            c2[15, 2] := 0.1307323751236313;
            c2[16, 1] := 0.4724820282032780e-1;
            c2[16, 2] := 0.6933542082177094e-1;
          elseif order == 34 then
            alpha := 0.1473858373968463;
            c2[1, 1] := 0.8801537152275983e-1;
            c2[1, 2] := 0.5929204288972172;
            c2[2, 1] := 0.8770594341007476e-1;
            c2[2, 2] := 0.5884653382247518;
            c2[3, 1] := 0.8708797598072095e-1;
            c2[3, 2] := 0.5795895850253119;
            c2[4, 1] := 0.8616320590689187e-1;
            c2[4, 2] := 0.5663615383647170;
            c2[5, 1] := 0.8493413175570858e-1;
            c2[5, 2] := 0.5488825092350877;
            c2[6, 1] := 0.8340387368687513e-1;
            c2[6, 2] := 0.5272851839324592;
            c2[7, 1] := 0.8157596213131521e-1;
            c2[7, 2] := 0.5017313864372913;
            c2[8, 1] := 0.7945402670834270e-1;
            c2[8, 2] := 0.4724089864574216;
            c2[9, 1] := 0.7704133559556429e-1;
            c2[9, 2] := 0.4395276256463053;
            c2[10, 1] := 0.7434009635219704e-1;
            c2[10, 2] := 0.4033126590648964;
            c2[11, 1] := 0.7135035113853376e-1;
            c2[11, 2] := 0.3639961488919042;
            c2[12, 1] := 0.6806813160738834e-1;
            c2[12, 2] := 0.3218025212900124;
            c2[13, 1] := 0.6448214312000864e-1;
            c2[13, 2] := 0.2769235521088158;
            c2[14, 1] := 0.6056719318430530e-1;
            c2[14, 2] := 0.2294693573271038;
            c2[15, 1] := 0.5626925196925040e-1;
            c2[15, 2] := 0.1793564218840015;
            c2[16, 1] := 0.5146352031547277e-1;
            c2[16, 2] := 0.1259877129326412;
            c2[17, 1] := 0.4578069074410591e-1;
            c2[17, 2] := 0.6689147319568768e-1;
          elseif order == 35 then
            alpha := 0.1452224267615486;
            c1[1] := 0.2926764667564367;
            c2[1, 1] := 0.8551731299267280e-1;
            c2[1, 2] := 0.5832758214629523;
            c2[2, 1] := 0.8509109732853060e-1;
            c2[2, 2] := 0.5770596582643844;
            c2[3, 1] := 0.8438201446671953e-1;
            c2[3, 2] := 0.5667497616665494;
            c2[4, 1] := 0.8339191981579831e-1;
            c2[4, 2] := 0.5524209816238369;
            c2[5, 1] := 0.8212328610083385e-1;
            c2[5, 2] := 0.5341766459916322;
            c2[6, 1] := 0.8057906332198853e-1;
            c2[6, 2] := 0.5121470053512750;
            c2[7, 1] := 0.7876247299954955e-1;
            c2[7, 2] := 0.4864870722254752;
            c2[8, 1] := 0.7667670879950268e-1;
            c2[8, 2] := 0.4573736721705665;
            c2[9, 1] := 0.7432449556218945e-1;
            c2[9, 2] := 0.4250013835198991;
            c2[10, 1] := 0.7170742126011575e-1;
            c2[10, 2] := 0.3895767735915445;
            c2[11, 1] := 0.6882488171701314e-1;
            c2[11, 2] := 0.3513097926737368;
            c2[12, 1] := 0.6567231746957568e-1;
            c2[12, 2] := 0.3103999917596611;
            c2[13, 1] := 0.6223804362223595e-1;
            c2[13, 2] := 0.2670123611280899;
            c2[14, 1] := 0.5849696460782910e-1;
            c2[14, 2] := 0.2212298104867592;
            c2[15, 1] := 0.5439628409499822e-1;
            c2[15, 2] := 0.1729443731341637;
            c2[16, 1] := 0.4981540179136920e-1;
            c2[16, 2] := 0.1215462157134930;
            c2[17, 1] := 0.4439981033536435e-1;
            c2[17, 2] := 0.6460098363520967e-1;
          elseif order == 36 then
            alpha := 0.1431515914458580;
            c2[1, 1] := 0.8335881847130301e-1;
            c2[1, 2] := 0.5770670512160201;
            c2[2, 1] := 0.8309698922852212e-1;
            c2[2, 2] := 0.5731929100172432;
            c2[3, 1] := 0.8257400347039723e-1;
            c2[3, 2] := 0.5654713811993058;
            c2[4, 1] := 0.8179117911600136e-1;
            c2[4, 2] := 0.5539556343603020;
            c2[5, 1] := 0.8075042173126963e-1;
            c2[5, 2] := 0.5387245649546684;
            c2[6, 1] := 0.7945413151258206e-1;
            c2[6, 2] := 0.5198817177723069;
            c2[7, 1] := 0.7790506514288866e-1;
            c2[7, 2] := 0.4975537629595409;
            c2[8, 1] := 0.7610613635339480e-1;
            c2[8, 2] := 0.4718884193866789;
            c2[9, 1] := 0.7406012816626425e-1;
            c2[9, 2] := 0.4430516443136726;
            c2[10, 1] := 0.7176927060205631e-1;
            c2[10, 2] := 0.4112237708115829;
            c2[11, 1] := 0.6923460172504251e-1;
            c2[11, 2] := 0.3765940116389730;
            c2[12, 1] := 0.6645495833489556e-1;
            c2[12, 2] := 0.3393522147815403;
            c2[13, 1] := 0.6342528888937094e-1;
            c2[13, 2] := 0.2996755899575573;
            c2[14, 1] := 0.6013361864949449e-1;
            c2[14, 2] := 0.2577053294053830;
            c2[15, 1] := 0.5655503081322404e-1;
            c2[15, 2] := 0.2135004731531631;
            c2[16, 1] := 0.5263798119559069e-1;
            c2[16, 2] := 0.1669320999865636;
            c2[17, 1] := 0.4826589873626196e-1;
            c2[17, 2] := 0.1173807590715484;
            c2[18, 1] := 0.4309819397289806e-1;
            c2[18, 2] := 0.6245036108880222e-1;
          elseif order == 37 then
            alpha := 0.1411669104782917;
            c1[1] := 0.2850271036215707;
            c2[1, 1] := 0.8111958235023328e-1;
            c2[1, 2] := 0.5682412610563970;
            c2[2, 1] := 0.8075727567979578e-1;
            c2[2, 2] := 0.5628142923227016;
            c2[3, 1] := 0.8015440554413301e-1;
            c2[3, 2] := 0.5538087696879930;
            c2[4, 1] := 0.7931239302677386e-1;
            c2[4, 2] := 0.5412833323304460;
            c2[5, 1] := 0.7823314328639347e-1;
            c2[5, 2] := 0.5253190555393968;
            c2[6, 1] := 0.7691895211595101e-1;
            c2[6, 2] := 0.5060183741977191;
            c2[7, 1] := 0.7537237072011853e-1;
            c2[7, 2] := 0.4835036020049034;
            c2[8, 1] := 0.7359601294804538e-1;
            c2[8, 2] := 0.4579149413954837;
            c2[9, 1] := 0.7159227884849299e-1;
            c2[9, 2] := 0.4294078049978829;
            c2[10, 1] := 0.6936295002846032e-1;
            c2[10, 2] := 0.3981491350382047;
            c2[11, 1] := 0.6690857785828917e-1;
            c2[11, 2] := 0.3643121502867948;
            c2[12, 1] := 0.6422751692085542e-1;
            c2[12, 2] := 0.3280684291406284;
            c2[13, 1] := 0.6131430866206096e-1;
            c2[13, 2] := 0.2895750997170303;
            c2[14, 1] := 0.5815677249570920e-1;
            c2[14, 2] := 0.2489521814805720;
            c2[15, 1] := 0.5473023527947980e-1;
            c2[15, 2] := 0.2062377435955363;
            c2[16, 1] := 0.5098441033167034e-1;
            c2[16, 2] := 0.1612849131645336;
            c2[17, 1] := 0.4680658811093562e-1;
            c2[17, 2] := 0.1134672937045305;
            c2[18, 1] := 0.4186928031694695e-1;
            c2[18, 2] := 0.6042754777339966e-1;
          elseif order == 38 then
            alpha := 0.1392625697140030;
            c2[1, 1] := 0.7916943373658329e-1;
            c2[1, 2] := 0.5624158631591745;
            c2[2, 1] := 0.7894592250257840e-1;
            c2[2, 2] := 0.5590219398777304;
            c2[3, 1] := 0.7849941672384930e-1;
            c2[3, 2] := 0.5522551628416841;
            c2[4, 1] := 0.7783093084875645e-1;
            c2[4, 2] := 0.5421574325808380;
            c2[5, 1] := 0.7694193770482690e-1;
            c2[5, 2] := 0.5287909941093643;
            c2[6, 1] := 0.7583430534712885e-1;
            c2[6, 2] := 0.5122376814029880;
            c2[7, 1] := 0.7451020436122948e-1;
            c2[7, 2] := 0.4925978555548549;
            c2[8, 1] := 0.7297197617673508e-1;
            c2[8, 2] := 0.4699889739625235;
            c2[9, 1] := 0.7122194706992953e-1;
            c2[9, 2] := 0.4445436860615774;
            c2[10, 1] := 0.6926216260386816e-1;
            c2[10, 2] := 0.4164072786327193;
            c2[11, 1] := 0.6709399961255503e-1;
            c2[11, 2] := 0.3857341621868851;
            c2[12, 1] := 0.6471757977022456e-1;
            c2[12, 2] := 0.3526828388476838;
            c2[13, 1] := 0.6213084287116965e-1;
            c2[13, 2] := 0.3174082831364342;
            c2[14, 1] := 0.5932799638550641e-1;
            c2[14, 2] := 0.2800495563550299;
            c2[15, 1] := 0.5629672408524944e-1;
            c2[15, 2] := 0.2407078154782509;
            c2[16, 1] := 0.5301264751544952e-1;
            c2[16, 2] := 0.1994026830553859;
            c2[17, 1] := 0.4942673259817896e-1;
            c2[17, 2] := 0.1559719194038917;
            c2[18, 1] := 0.4542996716979947e-1;
            c2[18, 2] := 0.1097844277878470;
            c2[19, 1] := 0.4070720755433961e-1;
            c2[19, 2] := 0.5852181110523043e-1;
          elseif order == 39 then
            alpha := 0.1374332900196804;
            c1[1] := 0.2779468246419593;
            c2[1, 1] := 0.7715084161825772e-1;
            c2[1, 2] := 0.5543001331300056;
            c2[2, 1] := 0.7684028301163326e-1;
            c2[2, 2] := 0.5495289890712267;
            c2[3, 1] := 0.7632343924866024e-1;
            c2[3, 2] := 0.5416083298429741;
            c2[4, 1] := 0.7560141319808483e-1;
            c2[4, 2] := 0.5305846713929198;
            c2[5, 1] := 0.7467569064745969e-1;
            c2[5, 2] := 0.5165224112570647;
            c2[6, 1] := 0.7354807648551346e-1;
            c2[6, 2] := 0.4995030679271456;
            c2[7, 1] := 0.7222060351121389e-1;
            c2[7, 2] := 0.4796242430956156;
            c2[8, 1] := 0.7069540462458585e-1;
            c2[8, 2] := 0.4569982440368368;
            c2[9, 1] := 0.6897453353492381e-1;
            c2[9, 2] := 0.4317502624832354;
            c2[10, 1] := 0.6705970959388781e-1;
            c2[10, 2] := 0.4040159353969854;
            c2[11, 1] := 0.6495194541066725e-1;
            c2[11, 2] := 0.3739379843169939;
            c2[12, 1] := 0.6265098412417610e-1;
            c2[12, 2] := 0.3416613843816217;
            c2[13, 1] := 0.6015440984955930e-1;
            c2[13, 2] := 0.3073260166338746;
            c2[14, 1] := 0.5745615876877304e-1;
            c2[14, 2] := 0.2710546723961181;
            c2[15, 1] := 0.5454383762391338e-1;
            c2[15, 2] := 0.2329316824061170;
            c2[16, 1] := 0.5139340231935751e-1;
            c2[16, 2] := 0.1929604256043231;
            c2[17, 1] := 0.4795705862458131e-1;
            c2[17, 2] := 0.1509655259246037;
            c2[18, 1] := 0.4412933231935506e-1;
            c2[18, 2] := 0.1063130748962878;
            c2[19, 1] := 0.3960672309405603e-1;
            c2[19, 2] := 0.5672356837211527e-1;
          elseif order == 40 then
            alpha := 0.1356742655825434;
            c2[1, 1] := 0.7538038374294594e-1;
            c2[1, 2] := 0.5488228264329617;
            c2[2, 1] := 0.7518806529402738e-1;
            c2[2, 2] := 0.5458297722483311;
            c2[3, 1] := 0.7480383050347119e-1;
            c2[3, 2] := 0.5398604576730540;
            c2[4, 1] := 0.7422847031965465e-1;
            c2[4, 2] := 0.5309482987446206;
            c2[5, 1] := 0.7346313704205006e-1;
            c2[5, 2] := 0.5191429845322307;
            c2[6, 1] := 0.7250930053201402e-1;
            c2[6, 2] := 0.5045099368431007;
            c2[7, 1] := 0.7136868456879621e-1;
            c2[7, 2] := 0.4871295553902607;
            c2[8, 1] := 0.7004317764946634e-1;
            c2[8, 2] := 0.4670962098860498;
            c2[9, 1] := 0.6853470921527828e-1;
            c2[9, 2] := 0.4445169164956202;
            c2[10, 1] := 0.6684507689945471e-1;
            c2[10, 2] := 0.4195095960479698;
            c2[11, 1] := 0.6497570123412630e-1;
            c2[11, 2] := 0.3922007419030645;
            c2[12, 1] := 0.6292726794917847e-1;
            c2[12, 2] := 0.3627221993494397;
            c2[13, 1] := 0.6069918741663154e-1;
            c2[13, 2] := 0.3312065181294388;
            c2[14, 1] := 0.5828873983769410e-1;
            c2[14, 2] := 0.2977798532686911;
            c2[15, 1] := 0.5568964389813015e-1;
            c2[15, 2] := 0.2625503293999835;
            c2[16, 1] := 0.5288947816690705e-1;
            c2[16, 2] := 0.2255872486520188;
            c2[17, 1] := 0.4986456327645859e-1;
            c2[17, 2] := 0.1868796731919594;
            c2[18, 1] := 0.4656832613054458e-1;
            c2[18, 2] := 0.1462410193532463;
            c2[19, 1] := 0.4289867647614935e-1;
            c2[19, 2] := 0.1030361558710747;
            c2[20, 1] := 0.3856310684054106e-1;
            c2[20, 2] := 0.5502423832293889e-1;
          elseif order == 41 then
            alpha := 0.1339811106984253;
            c1[1] := 0.2713685065531391;
            c2[1, 1] := 0.7355140275160984e-1;
            c2[1, 2] := 0.5413274778282860;
            c2[2, 1] := 0.7328319082267173e-1;
            c2[2, 2] := 0.5371064088294270;
            c2[3, 1] := 0.7283676160772547e-1;
            c2[3, 2] := 0.5300963437270770;
            c2[4, 1] := 0.7221298133014343e-1;
            c2[4, 2] := 0.5203345998371490;
            c2[5, 1] := 0.7141302173623395e-1;
            c2[5, 2] := 0.5078728971879841;
            c2[6, 1] := 0.7043831559982149e-1;
            c2[6, 2] := 0.4927768111819803;
            c2[7, 1] := 0.6929049381827268e-1;
            c2[7, 2] := 0.4751250308594139;
            c2[8, 1] := 0.6797129849758392e-1;
            c2[8, 2] := 0.4550083840638406;
            c2[9, 1] := 0.6648246325101609e-1;
            c2[9, 2] := 0.4325285673076087;
            c2[10, 1] := 0.6482554675958526e-1;
            c2[10, 2] := 0.4077964789091151;
            c2[11, 1] := 0.6300169683004558e-1;
            c2[11, 2] := 0.3809299858742483;
            c2[12, 1] := 0.6101130648543355e-1;
            c2[12, 2] := 0.3520508315700898;
            c2[13, 1] := 0.5885349417435808e-1;
            c2[13, 2] := 0.3212801560701271;
            c2[14, 1] := 0.5652528148656809e-1;
            c2[14, 2] := 0.2887316252774887;
            c2[15, 1] := 0.5402021575818373e-1;
            c2[15, 2] := 0.2545001287790888;
            c2[16, 1] := 0.5132588802608274e-1;
            c2[16, 2] := 0.2186415296842951;
            c2[17, 1] := 0.4841900639702602e-1;
            c2[17, 2] := 0.1811322622296060;
            c2[18, 1] := 0.4525419574485134e-1;
            c2[18, 2] := 0.1417762065404688;
            c2[19, 1] := 0.4173260173087802e-1;
            c2[19, 2] := 0.9993834530966510e-1;
            c2[20, 1] := 0.3757210572966463e-1;
            c2[20, 2] := 0.5341611499960143e-1;
          else
            Streams.error("Input argument order (= " + String(order) +
              ") of Bessel filter is not in the range 1..41");
          end if;

        end BesselCoefficients;

        function 'p+a'
          import Modelica;
          import Modelica_LinearSystems2.Math.Complex;

          input Complex p;
          input Real a;
          output Complex c;
        protected
          Complex j = Modelica_LinearSystems2.Math.Complex.j();
        algorithm
          c := p.re + p.im*j +a;
        end 'p+a';

        function 'p^2+k[1]*p+k[2]'
          import Modelica;
          import Modelica.Utilities.Streams.print;
          import Modelica_LinearSystems2.Math.Complex;

          input Complex p;
          input Real k[2];
          output Complex c;
        protected
          Complex c1;
          Complex c2;
          Complex c3;
          Complex c4;
          Complex j = Modelica_LinearSystems2.Math.Complex.j();
        algorithm
          c := p.re^2 - p.im^2 + k[1]*p.re + k[2]+p.im*(2*p.re + k[1])*j;

        end 'p^2+k[1]*p+k[2]';

        function roots "Determine zeros of factorized polynomial"
          import Modelica;
          import Modelica_LinearSystems2.Math.Complex;

          input Real poly1[:] "[p^0] coefficients of first order polynomials";
          input Real poly2[:,2] "[p, p^0] coefficients of second order polynomials";
          input Integer n_real
            "Number of real zeros computed with Internal.numberOfRealZeros";
          output Real realZeros[n_real] "All real zeros of poly1 and poly2";
          output Complex complexZeros[:]=fill(Complex(0, 0), integer((size(poly1, 1)
               + 2*size(poly2, 1) - n_real)/2))
            "All complex zeros of poly1 and poly2; for a complex conjugate pair, only one zero is stored";
        protected
          Integer np1=size(poly1, 1);
          Integer np2=size(poly2, 1);
          Real D;
          Real D2;
          Real b;
          Integer j1;
          Integer j2;
          Complex j = Modelica_LinearSystems2.Math.Complex.j();

        algorithm
          assert(np1 <= n_real, "Size of poly1 = " + String(np1) + " > n_real " +
            " (= " + String(n_real) + ").");
          for i in 1:np1 loop
            realZeros[i] := -poly1[i];
          end for;

          j1 := np1 + 1;
          j2 := 1;
          for i in 1:np2 loop
            b := poly2[i, 1]/2;
            D := b*b - poly2[i, 2];
            D2 := sqrt(abs(D));
            if D >= 0 then
              realZeros[j1] := -b + D2;
              realZeros[j1 + 1] := -b - D2;
              j1 := j1 + 2;
            else
              complexZeros[j2] := -b+D2*j;
              j2 := j2 + 1;
            end if;
          end for;
        end roots;

        function frequencyRange
          "Determine min. and max. resonance frequencies"
          import Modelica;
          import Modelica_LinearSystems2.Math.Complex;

          input Real poly1[:];
          input Real poly2[:,2];
          output Boolean w_found=false;
          output Modelica.SIunits.AngularVelocity w_min;
          output Modelica.SIunits.AngularVelocity w_max;
        protected
          Integer order=size(poly1, 1) + 2*size(poly2, 1);
          Integer n_real=numberOfRealZeros(poly1, poly2);
          Real zeros1[n_real];
          Complex zeros2[:]=fill(Complex(0, 0), integer((order - n_real)/2));
          Real w;
        algorithm
          // Compute zeros
          (zeros1,zeros2) := roots(
                poly1,
                poly2,
                n_real);

          // Compute resonance frequencies
          w_min := Modelica.Constants.inf;
          w_max := -Modelica.Constants.inf;
          for i in 1:size(zeros1, 1) loop
            if zeros1[i] <> 0 then
              w := abs(zeros1[i]);
              w_min := min(w_min, w);
              w_max := max(w_max, w);
              w_found := true;
            end if;
          end for;

          for i in 1:size(zeros2, 1) loop
            w := Complex.'abs'(zeros2[i]);
            w_min := min(w_min, w);
            w_max := max(w_max, w);
            w_found := true;
          end for;
        end frequencyRange;

        function frequencyRangeBode
          "Determine min. and max. frequencies for Bode plot"
          import Modelica;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input ZerosAndPoles tf "ZerosAndPoles transfer function";
          output Modelica.SIunits.AngularVelocity w_min;
          output Modelica.SIunits.AngularVelocity w_max;
        protected
          Real phi_min=Modelica.SIunits.Conversions.from_deg(3);
          Real real_min=1.0e-4;
          Real pi=Modelica.Constants.pi;
          Complex numZeros[:];
          Complex denZeros[:];
          Integer n_num;
          Integer n_den;
          Real w_min1;
          Real w_min2;
          Real w_max1;
          Real w_max2;
        algorithm
          // Compute zeros and poles
          (numZeros,denZeros) := ZerosAndPoles.Analysis.zerosAndPoles(tf);

          // Compute frequencies for numerator
          n_num := size(numZeros, 1);
          if n_num > 0 then
            (w_min1,w_max1) := Modelica_LinearSystems2.Internal.frequencyRangeZeros(
                  numZeros,
                  phi_min,
                  real_min);
          end if;

          // Compute frequencies for denominator
          n_den := size(denZeros, 1);
          if n_den > 0 then
            (w_min2,w_max2) := Modelica_LinearSystems2.Internal.frequencyRangeZeros(
                  denZeros,
                  phi_min,
                  real_min);
          end if;

          // Use largest range
          if n_num == 0 and n_den == 0 then
            w_min := 0.1;
            w_max := 10;
          elseif n_num == 0 then
            w_min := w_min2;
            w_max := w_max2;
          elseif n_den == 0 then
            w_min := w_min1;
            w_max := w_max1;
          else
            w_min := min(w_min1, w_min2);
            w_max := max(w_max1, w_max2);
          end if;
        end frequencyRangeBode;

        function filterToNormalized
          "Given [p^2,p] and [p] coefficients, transform to normalized form with highest power of p equal 1"

          input Real c1[:] "[p] coefficients of polynomials (a*p + 1)";
          input Real c2[:,2]
            "[p^2, p] coefficients of polynomials (b*p^2 + a*p + 1)";
          output Real n1[size(c1, 1)] "[p^0] coefficients of polynomials a*(p+1/a)";
          output Real n2[size(c2, 1),2]
            "[p, p^0] coefficients of polynomials b*(p^2 + (a/b)*p + (1/b))";
          output Real k "Gain (product(1/a)*(1/b)";
        algorithm
          k := 1.0;
          for i in 1:size(c1, 1) loop
            k := k*c1[i];
            n1[i] := 1/c1[i];
          end for;

          for i in 1:size(c2, 1) loop
            k := k*c2[i, 1];
            n2[i, 1] := c2[i, 2]/c2[i, 1];
            n2[i, 2] := 1/c2[i, 1];
          end for;
        end filterToNormalized;

        encapsulated function numberOfRealZeros
          "Calculate number of real zeros"
          import Modelica;
          import Modelica_LinearSystems2.ZerosAndPoles;

          input Real poly1[:];
          input Real poly2[:,2];
          output Integer result "Number of real zeros";
        protected
          Real D;
        algorithm
          result := size(poly1, 1);
          for i in 1:size(poly2, 1) loop
            D := (poly2[i, 1]/2)^2 - poly2[i, 2];
            if D >= 0 then
                // two real zeros
              result := result + 2;
            end if;
          end for;
        end numberOfRealZeros;

        function numberOfRealZeros2 "Calculate number of real zeros"
          import Modelica;
          import Modelica_LinearSystems2.Internal;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Math.Polynomial;

          input TransferFunction tf "TransferFunction";
          output Integer result=Internal.numberOfRealZeros(Polynomial.roots(Polynomial(tf.n)));
        algorithm
        end numberOfRealZeros2;

        function numberOfRealPoles "Calculate number of real poles"
          import Modelica;
          import Modelica_LinearSystems2.Internal;
          import Modelica_LinearSystems2.TransferFunction;
          import Modelica_LinearSystems2.Math.Polynomial;

          input TransferFunction tf "TransferFunction";
        output Integer result=Internal.numberOfRealZeros(Polynomial.roots(Polynomial(tf.d)));
        algorithm
        end numberOfRealPoles;

        function isRoot
          "check if frequency is an element of the complex vector zeros"

          import Modelica_LinearSystems2.Math.Complex;
          //import Modelica_LinearSystems2;

          input Complex zeros[:];
          input Complex p;
        //Never used
          input Real eps(min=0) = 0;
          output Boolean result;

        protected
          Integer sz=size(zeros, 1);
          Integer i;

        algorithm
          i := 1;
          result := false;
          while i <= sz and not result loop

        //The implementation of Complex.'==' does not take a third parameter.
            result := p == zeros[i];
            i := i + 1;
          end while;

        end isRoot;

        function numberOfRealZerosAndPoles_zp
          "Get the number of first oder polynomials (n1, d1) and second order polynomials (n2, d2) of zeros and poles from zeros and poles written in a MAT-file"

          import Modelica_LinearSystems2.DataDir;
          import Modelica_LinearSystems2.Internal;

          input String fileName=DataDir + "/zp.mat"
            "Name of the zeros and poles data file";
          input Real eps=Modelica.Constants.eps;

          output Integer n1n2d1d2[4];

        protected
          Integer n1;
          Integer d1;

          Integer zSize[2]=readMatrixSize(fileName, "z");
          Integer pSize[2]=readMatrixSize(fileName, "p");

          Real zerosMatrix[zSize[1],zSize[2]]=readMatrix(
                  fileName,
                  "z",
                  zSize[1],
                  zSize[2]) "zeros in rows of real parts and imaginary parts";
          Real polesMatrix[pSize[1],pSize[2]]=readMatrix(
                  fileName,
                  "p",
                  pSize[1],
                  pSize[2]) "poles in rows of real parts and imaginary parts";

        algorithm
          n1 := zSize[1];
          d1 := pSize[1];
          for i in 1:zSize[1] loop
            if abs(zerosMatrix[i, 2]) >= eps then
              n1 := n1 - 1;
            end if;
          end for;
          for i in 1:pSize[1] loop
            if abs(polesMatrix[i, 2]) >= eps then
              d1 := d1 - 1;
            end if;
          end for;
          n1n2d1d2[1] := n1;
          n1n2d1d2[2] := div((zSize[1] - n1), 2);
          n1n2d1d2[3] := d1;
          n1n2d1d2[4] := div((pSize[1] - d1), 2);

        end numberOfRealZerosAndPoles_zp;

        function numberOfRealZerosAndPoles_pc
          "Generate a zeros and poles data record by reading the polynomial coefficients from a file (default file name is zp.mat)"
          import Modelica_LinearSystems2.ZerosAndPoles;
          import Modelica_LinearSystems2.Math.Complex;
          import Modelica;

          input String fileName="pc.mat" "Name of the zeros and poles data file";
          output Integer n1n2d1d2[4];

        protected
          Integer n1Size[2]=readMatrixSize(fileName, "n1");
          Integer n2Size[2]=readMatrixSize(fileName, "n2");
          Integer d1Size[2]=readMatrixSize(fileName, "d1");
          Integer d2Size[2]=readMatrixSize(fileName, "d2");

        algorithm
          n1n2d1d2[1] := n1Size[1];
          n1n2d1d2[2] := n2Size[1];
          n1n2d1d2[3] := d1Size[1];
          n1n2d1d2[4] := d2Size[1];

        end numberOfRealZerosAndPoles_pc;

        encapsulated function isControllableAndObservableSISO
          "To check whether a SISO system is controllable and observable"

          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.StateSpace;
          import Modelica_LinearSystems2.ZerosAndPoles;

        input ZerosAndPoles zp;

          output Boolean controllableAndObservable;
        protected
          StateSpace ss=StateSpace(zp);

        algorithm
          controllableAndObservable := StateSpace.Internal.isControllableAndObservableSISO(ss=ss);

        end isControllableAndObservableSISO;

      end Internal;

    end ZerosAndPoles;

## Modelica_LinearSystems2.DiscreteStateSpace

Discrete state space description of a linear, time invariant difference equation system (data + operations)

### Information

This record defines a linear time invariant difference equation system in state space form:

         x(Ts*(k+1)) = A * x(Ts*k) + B * u(Ts*k)
         y(Ts*k)     = C * x(Ts*k) + D * u(Ts*k)
         x_continuous(Ts*k) = x(Ts*k) + B2 * u(Ts*k)

with

- Ts - the sample time
- k - the index of the actual sample instance (k=0,1,2,3,...)
- t - the time
- u(t) - the input vector,
- y(t) - the output vector,
- x(t) - the discrete state vector (x(t=Ts*0) is the initial state),
- x_continuous(t) - the state vector of the continuous system from which the discrete block has been derived (details see below),
- A,B,C,D,B2 - matrices of appropriate dimensions.

A discrete system is usually derived by discretization from a continuous block, e.g., by function LinearSystems.DiscreteStateSpace.fromStateSpace. If the discretization method, e.g., the trapezoidal method, accesses actual and past values of the input u (e.g. u(Ts*k), u(Ts*(k-1), u(Ts*(k-2))), a state transformation is needed to get the difference equation above where only the actual value u(Ts*k) is accessed.

If the original continuous state vector should be computed from the sampled data system above, the matrices of this transformation have to be known. For simplicity and efficiency, here only the specific transformation used by function LinearSystems.DiscreteStateSpace.fromStateSpace is stored in the data record of the discrete system via matrix B2. Therefore, the state vector of the underlying continuous system can be calculated by adding the term B2*u to the state vector of the discretized system.

In Modelica notation, the difference equation above can be implemented as:

         when {initial(), sample(Ts,Ts)} then
            new_x = A * x + B * u;
                y = C * x + D * u;
                x = pre(new_x);
            x_continuous = x + B2 * u;
         end when;

Since no "next value" operator is available in Modelica, an auxiliary variable new_x stores the value of x for the next sampling instant. The relationship between new_x and x is defined via equation "x = pre(new_x)".

The body of the when-clause is active during initialization and at the next sample instant t=Ts. Note, the when-equation is not active after the initialization at t=0 (due to sample(Ts,Ts)), since the state x of the initialization has to be used also at t=0.

In library Blocks.Controller additional equations are added for the initialization to uniquely compute the initial vector x:

      initial equation
         if init == InitialState then
            x = x_start;
         elseif init == SteadyState then
            x = new_x;
         end if;

Optionally, x is set to a given start vector x_start. As default initialization, the equation "x = new_x" is added that defines steady state initialization for the discrete system. As a consequence, the output y(Ts*k), k=0,1,2,.., remains constant after this initialization, provided the input vector u(Ts*k) remains constant.

Extends from Modelica.Icons.Record (Icon for a record).

### Parameters

Type | Name | Default | Description
--- | --- | --- | ---
new_x = A*x + B*u; y = C*x + D*u; x_cont = x + B2*u
Real | A[:, size(A, 1)] |  |
Real | B[size(A, 1), :] |  |
Real | C[:, size(A, 1)] |  |
Real | D[size(C, 1), size(B, 2)] |  |
Data used to construct discrete from continuous system
Time | Ts | 1 | Sample time [s]
Real | B2[size(B, 1), size(B, 2)] | fill(0, size(B, 1), size(B, ... | Reconstruct continuous state
Method | method | Modelica_LinearSystems2.Type... | Discretization method

### Modelica definition

    record DiscreteStateSpace
      "Discrete state space description of a linear, time invariant difference equation system (data + operations)"
      extends Modelica.Icons.Record;

      Real A[:,size(A, 1)];
      Real B[size(A, 1),:];
      Real C[:,size(A, 1)];
      Real D[size(C, 1),size(B, 2)];

      Modelica.SIunits.Time Ts=1 "Sample time";
      Real B2[size(B, 1),size(B, 2)]=fill(0,size(B,1),size(B,2))
        "Reconstruct continuous state";
      Modelica_LinearSystems2.Types.Method method=Modelica_LinearSystems2.Types.Method.Trapezoidal
        "Discretization method";

      encapsulated operator 'constructor'
        function fromMatrices
          "Default constructor for a DiscreteStateSpace record"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.DiscreteStateSpace;

          input Real A[:,size(A, 1)];
          input Real B[size(A, 1),:];
          input Real C[:,size(A, 1)];
          input Real D[size(C, 1),size(B, 2)];

          input Modelica.SIunits.Time Ts=1 "Sample time";
          input Real B2[:,:]=zeros(size(B, 1), size(B, 2));
          input Modelica_LinearSystems2.Types.Method method=Modelica_LinearSystems2.Types.Method.Trapezoidal
            "Discretization methodDiscretization method";
          output DiscreteStateSpace result(
            redeclare Real A[size(A, 1),size(A, 2)],
            redeclare Real B[size(B, 1),size(B, 2)],
            redeclare Real C[size(C, 1),size(C, 2)],
            redeclare Real D[size(D, 1),size(D, 2)],
            redeclare Real B2[size(B2, 1),size(B2, 2)]);
        algorithm
          result.A := A;
          result.B := B;
          result.C := C;
          result.D := D;

          result.B2 := B2;
          result.Ts := Ts;
          result.method := method;
        end fromMatrices;

        function fromStateSpace
          "Transform a continuous into a discrete linear state space system"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Types.Method;
          import Modelica_LinearSystems2.Math.Matrices.LU_solve2;

          input Modelica_LinearSystems2.StateSpace sc
            "Continuous linear state space system";
          input Modelica.SIunits.Time Ts "Sample time";
          input Modelica_LinearSystems2.Types.Method method=Modelica_LinearSystems2.Types.Method.Trapezoidal
            "Discretization method";
          output Modelica_LinearSystems2.DiscreteStateSpace sd(
            redeclare Real A[size(sc.A, 1),size(sc.A, 2)],
            redeclare Real B[size(sc.B, 1),size(sc.B, 2)],
            redeclare Real C[size(sc.C, 1),size(sc.C, 2)],
            redeclare Real D[size(sc.D, 1),size(sc.D, 2)],
            redeclare Real B2[size(sc.B, 1),size(sc.B, 2)])
            "Discrete state space system";

        protected
          Integer nx=size(sc.A, 1) "Number of states";
          Integer nu=size(sc.B, 2) "Number of input signals";
          Real LU[nx,nx] "LU decomposition";
          Integer pivots[nx] "Pivots of LU decomposition";
        algorithm
          sd.Ts := Ts;
          sd.method := method;

          if method == Method.ExplicitEuler then
                /*  der_x = A*x + B*u
                 x = pre(x) + Ts*pre(der_x)
         */
            sd.A := identity(nx) + Ts*sc.A;
            sd.B := Ts*sc.B;
            sd.C := sc.C;
            sd.D := sc.D;
            sd.B2 := zeros(nx, nu);

          elseif method == Method.ImplicitEuler then
                /*  der_x = A*x + B*u
                 x = pre(x) + Ts*der_x
         */
            (LU,pivots) := Modelica_LinearSystems2.Math.Matrices.LU(identity(nx) -
              Ts*sc.A);
            sd.B2 := LU_solve2(
                  LU,
                  pivots,
                  Ts*sc.B);
            sd.A := LU_solve2(
                  LU,
                  pivots,
                  identity(nx));
            sd.B := sd.A*sd.B2;
            sd.C := sc.C;
            sd.D := sd.C*sd.B2 + sc.D;

          elseif method == Method.Trapezoidal then
                /*  der_x = A*x + B*u
                 x = pre_x + (Ts/2)*(pre_der_x + der_x);
         */
            (LU,pivots) := Modelica_LinearSystems2.Math.Matrices.LU(identity(nx) -
              (Ts/2)*sc.A);
            sd.B2 := LU_solve2(
                  LU,
                  pivots,
                  (Ts/2)*sc.B);
            sd.A := LU_solve2(
                  LU,
                  pivots,
                  identity(nx) + (Ts/2)*sc.A);
            sd.B := sd.A*sd.B2 + sd.B2;
            sd.C := sc.C;
            sd.D := sd.C*sd.B2 + sc.D;

          elseif method == Method.StepExact then
               /* x = phi*pre(x) + gamma*pre(u);
           y = C*x + D*u
        */
            (sd.A,sd.B) := Modelica.Math.Matrices.integralExp(
                  sc.A,
                  sc.B,
                  Ts);
            sd.C := sc.C;
            sd.D := sc.D;
            sd.B2 := zeros(nx, nu);

          elseif method == Method.RampExact then
               /* x = phi*pre(x) + gamma*pre(u) + gamma1/Ts*(u - pre_u);
            -> x = phi*pre(x) + (gamma - gamma1/Ts)*pre(u) + gamma1/Ts*u;
      z = x - gamma1/Ts*u
          leads to
            z = phi*pre(z) + phi*gamma1/Ts*pre(u) +gamma*pre(u) - gamma1/Ts*pre(u)
           y = C*x + D*u
          -> y = C*z + (D + C*gamma1/Ts)*u
          x = z + gamma1/Ts*u

        */
            (sd.A,sd.B,sd.B2) := Modelica.Math.Matrices.integralExpT(
                  sc.A,
                  sc.B,
                  Ts);
            sd.B2 := sd.B2/Ts;
            sd.B := sd.A*sd.B2 + sd.B - sd.B2;
            sd.C := sc.C;
            sd.D := sd.C*sd.B2 + sc.D;

          elseif method == Method.ImpulseExact then
               /* x = phi*pre(x) + phi*B*u;
            y = C*x
            (u = [1,0,1,1,0..,0])
          Limitations: The infinit impulses at t = kT is ignored in the mapping
        */

            sd.A := Modelica.Math.Matrices.exp(sc.A, Ts);
            sd.B := sd.A*sc.B;
            sd.C := sc.C;
            sd.D := sc.C*sc.B;
            sd.B2 := sc.B;

          else
            assert(false, "Argument method (= " + String(method) +
              ") of makeDiscrete is wrong.");
          end if;
        end fromStateSpace;

        encapsulated function fromMatrices2
          "Transform a continuous into a discrete linear state space system"
          import Modelica;
          import Modelica_LinearSystems2;
          import Modelica_LinearSystems2.Types.Method;
          import Modelica_LinearSystems2.Math.Matrices.LU_solve2;

          input Real A[:,size(A, 1)];
          input Real B[size(A, 1),:];
          input Real C[:,size(A, 1)];
          input Real D[size(C, 1),size(B, 2)];
          input Modelica.SIunits.Time Ts "Sample time";
          input Modelica_LinearSystems2.Types.Method method=Modelica_LinearSystems2.Types.Method.Trapezoidal
            "Discretization method";
        //  input Modelica_LinearSystems2.Types method=Modelica_LinearSystems2.Types.Method.Trapezoidal
          output Modelica_LinearSystems2.DiscreteStateSpace sd(
            redeclare Real A[size(A, 1),size(A, 2)],
            redeclare Real B[size(B, 1),size(B, 2)],
            redeclare Real C[size(C, 1),size(C, 2)],
            redeclare Real D[size(D, 1),size(D, 2)],
            redeclare Real B2[size(B, 1),size(B, 2)]) "Discrete state space system";

        protected
          Integer nx=size(A, 1) "Number of states";
          Integer nu=size(B, 2) "Number of input signals";
          Real LU[nx,nx] "LU decomposition";
          Integer pivots[nx] "Pivots of LU decomposition";
        algorithm
          sd.Ts := Ts;
          sd.method := method;

          if method == Method.ExplicitEuler then
                /*  der_x = A*x + B*u
                 x = pre(x) + Ts*pre(der_x)
         */
            sd.A := identity(nx) + Ts*A;
            sd.B := Ts*B;
            sd.C := C;
            sd.D := D;
            sd.B2 := zeros(nx, nu);

          elseif method == Method.ImplicitEuler then
                /*  der_x = A*x + B*u
                 x = pre(x) + Ts*der_x
         */
            (LU,pivots) := Modelica_LinearSystems2.Math.Matrices.LU(identity(nx) -
              Ts*A);
            sd.B2 := LU_solve2(
                  LU,
                  pivots,
                  Ts*B);
            sd.A := LU_solve2(
                  LU,
                  pivots,
                  identity(nx));
            sd.B := sd.A*sd.B2;
            sd.C := C;
            sd.D := sd.C*sd.B2 + D;

          elseif method == Method.Trapezoidal then
                /*  der_x = A*x + B*u
                 x = pre_x + (Ts/2)*(pre_der_x + der_x);
         */
            (LU,pivots) := Modelica_LinearSystems2.Math.Matrices.LU(identity(nx) -
              (Ts/2)*A);
            sd.B2 := LU_solve2(
                  LU,
                  pivots,
                  (Ts/2)*B);
            sd.A := LU_solve2(
                  LU,
                  pivots,
                  identity(nx) + (Ts/2)*A);
            sd.B := sd.A*sd.B2 + sd.B2;
            sd.C := C;
            sd.D := sd.C*sd.B2 + D;

          elseif method == Method.StepExact then
               /* x = phi*pre(x) + gamma*pre(u);
           y = C*x + D*u
        */
            (sd.A,sd.B) := Modelica.Math.Matrices.integralExp(
                  A,
                  B,
                  Ts);
            sd.C := C;
            sd.D := D;
            sd.B2 := zeros(nx, nu);

          elseif method == Method.RampExact then
               /* x = phi*pre(x) + gamma*pre(u) + gamma1/Ts*(u - pre_u);
            -> x = phi*pre(x) + (gamma - gamma1/Ts)*pre(u) + gamma1/Ts*u;
      z = x - gamma1/Ts*u
          leads to
            z = phi*pre(z) + phi*gamma1/Ts*pre(u) +gamma*pre(u) - gamma1/Ts*pre(u)
           y = C*x + D*u
          -> y = C*z + (D + C*gamma1/Ts)*u
          x = z + gamma1/Ts*u

        */
            (sd.A,sd.B,sd.B2) := Modelica.Math.Matrices.integralExpT(
                  A,
                  B,
                  Ts);
            sd.B2 := sd.B2/Ts;
            sd.B := sd.A*sd.B2 + sd.B - sd.B2;
            sd.C := C;
            sd.D := sd.C*sd.B2 + D;

          elseif method == Method.ImpulseExact then
               /* x = phi*pre(x) + phi*B*u;
            y = C*x
            (u = [1,0,1,1,0..,0])
          Limitations: The infinit impulses at t = kT is ignored in the mapping
        */

            sd.A := Modelica.Math.Matrices.exp(A, Ts);
            sd.B := sd.A*B;
            sd.C := C;
            sd.D := C*B;
            sd.B2 := B;

          else
            assert(false, "Argument method (= " + String(method) +
              ") of makeDiscrete is wrong.");
          end if;
        end fromMatrices2;
      end 'constructor';

      encapsulated function timeResponse
        "Compute time response of DiscreteStateSpace system"
        import Modelica;
        import Modelica_LinearSystems2;
        import Modelica_LinearSystems2.DiscreteStateSpace;

        input DiscreteStateSpace sd "Linear system in discrete state space form";
        input Real u[:,size(sd.B, 2)]=ones(3, size(sd.B, 2))
          "System input (dimension: (input samples) x (number of inputs))";
        input Real x0[size(sd.A, 1)]=zeros(size(sd.A, 1)) "Initial system state";
        output Real y[size(u, 1),size(sd.C, 1)]
          "System response (dimension: (input samples) x (number of outputs))";
        output Real x_continuous[size(u, 1),size(sd.A, 1)]
          "State trajectories (dimension: (input samples) x (number of states)";

      protected
        Integer samples=size(u, 1);
        Integer i;
        Real new_x[size(sd.A, 1)];
        Real x[size(sd.A, 1)]=x0;

      algorithm
        for i in 1:samples loop
          new_x := sd.A*x + sd.B*u[i, :];
          y[i, :] := sd.C*x + sd.D*u[i, :];
          x_continuous[i, :] := x + sd.B2*u[i, :];
          x := new_x;
        end for;
      end timeResponse;

      encapsulated function initialResponse
        "Compute initial response of DiscreteStateSpace system"
        import Modelica;
        import Modelica_LinearSystems2;
        import Modelica_LinearSystems2.DiscreteStateSpace;

        input DiscreteStateSpace sd "Linear system in discrete state space form";
        input Real x0[size(sd.A, 1)]=zeros(size(sd.A, 1)) "Initial system state";
        input Integer samples "Number of samples";
        output Real y[samples,size(sd.C, 1)]
          "System response (dimension: (input samples) x (number of outputs))";
        output Real x_continuous[samples,size(sd.A, 1)]
          "State trajectories (dimension: (input samples) x (number of states)";

      protected
        Integer i;
        Real new_x[size(sd.A, 1)];
        Real x[size(sd.A, 1)]=x0;

      algorithm
        for i in 1:samples loop
          new_x := sd.A*x;
          y[i, :] := sd.C*x;
          x_continuous[i, :] := x;
          x := new_x;
        end for;
      end initialResponse;

    end DiscreteStateSpace;