Calculates solar radiation on tilted surfaces according to Perez.

within AixLib.BoundaryConditions.WeatherData.Old.WeatherTRY.RadiationOnTiltedSurface;
model RadOnTiltedSurf_Perez
  "Calculates solar radiation on tilted surfaces according to Perez"
  extends RadiationOnTiltedSurface.BaseClasses.PartialRadOnTiltedSurf;

  import Modelica.Units.Conversions.to_deg;
  import Modelica.Units.Conversions.from_deg;
  import Modelica.Math.sin;
  import Modelica.Math.acos;
  import Modelica.Math.cos;

//parameter

  parameter Boolean GroundReflexApprox =  false
    "Shall the GroundReflection be approximated?"
  parameter Modelica.Units.SI.Height h=0
    "height of the tilted surfaces centre in metre. if unknown it is 0";

//constants
protected
  constant Real a_rho=0.45
    "estimated on measured Albedo from NREL USA (Latitude=39.74 deg)";
  constant Real b_rho=0.013 "estimated on measured Albedo from NREL USA";
  constant Real c_rho=0.2 "estimated on measured Albedo from NREL USA";
  constant Real rho_avg=1/0.27055
    "factor for setting the average of rho to the value of GroundReflection";
  constant Real SolarConstant=1367;
  constant Real A[ 8,6]= [-0.0083117, 0.5877285, -0.0620636, -0.0596012, 0.0721249, -0.0220216;
                          0.1299457, 0.6825954, -0.1513752, -0.0189325, 0.0659650, -0.0288748;
                          0.3296958, 0.4868735, -0.2210958, 0.0554140, -0.0639588, -0.0260542;
                          0.5682053, 0.1874525, -0.2951290, 0.1088631, -0.1519229, 0.0139754;
                          0.8730280, -0.3920403, -0.3616149, 0.2255647, -0.4620442, 0.0012448;
                          1.1326077, -1.2367284, -0.4118494, 0.2877813, -0.8230357, 0.0558651;
                          1.0601591, -1.5999137, -0.3589221, 0.2642124, -1.1272340, 0.1310694;
                          0.6777470, -0.3272588, -0.2504286, 0.1561313, -1.3765031, 0.2506212]
    "Brightness Coefficient Matrix by [Perez 1990] * Engineering reference EnergyPlus, page 143";

//variables

public
    Real InBeamRadHor "beam irradiance on the horizontal surface";
    Real InDiffRadHor "diffuse irradiance on the horizontal surface";
    Real BeamRadTilt "Beam irradiance on a tilted surface";
    Real DiffRadTilt "Diffuse irradiance on a tilted surface";
    Real RadGroundRefl "Ground-reflected irradiance";
    //the following variables are necessary for the use of the Perez anisotropic sky-model for determining the diffuse radiation on tilted surfaces [Perez et al., Solar Energy, 44, 271, (1990). "Modeling Daylight Availibility and Irradiance Components from Direct and Global Irradiance];
    Real DiffRadTiltCS "circumsolar diffuse irradiance";
    Real DiffRadTiltHZ "horizontal diffuse irradiance";
    Real DiffRadTiltDOM "sky dome diffuse irradiance";

protected
    Real rho "groundreflection";
    Real cos_theta_help;
    Real cos_theta;
    Real cos_theta_z_help;
    Real cos_theta_z;
    Real theta_z_pos;
    Real theta_z;
    Real R "geometric beam radiation factor";
    Real Airmass;
    Real ExtraterRadHor;
    Real a "used for determining R";
    Real b "used for determining R";
    Real c "used instead of InDiffRadHor for determining epsilon";
    Real F1 "brightness coefficient 1";
    Real F2 "brightness coefficient 2";
    Real epsilon "sky-clearness parameter";
    Real delta "brightness parameter";

    //Fij factors
    Real f11;
    Real f12;
    Real f13;
    Real f21;
    Real f22;
    Real f23;

  //input

  //output

public
    Modelica.Blocks.Interfaces.RealOutput rho_out "groundreflection"
    Modelica.Blocks.Interfaces.RealOutput theta_out
    "[degree] angle of incidence"
    Modelica.Blocks.Interfaces.RealOutput theta_z_out
    "degree value; solar zenith angle"
    Modelica.Blocks.Interfaces.RealOutput BeamRadTiltOut
    "[W/m2] Beam irradiance on a tilted surface"
    Modelica.Blocks.Interfaces.RealOutput DiffRadTiltCSOut
    "[W/m2] circumsolar diffuse irradiance"
    Modelica.Blocks.Interfaces.RealOutput DiffRadTiltHZOut
    "[W/m2] Diffuse irradiance on a tilted surface"
    Modelica.Blocks.Interfaces.RealOutput DiffRadTiltDOMOut
    "[W/m2] Diffuse irradiance on a tilted surface"
    Modelica.Blocks.Interfaces.RealOutput brightness_out
    "dimensionless factor, which says how cloudy the sky is (see: delta)"

  Modelica.Blocks.Tables.CombiTable1Ds FijFactors(
    smoothness=Modelica.Blocks.Types.Smoothness.ConstantSegments,
    tableOnFile=false,
    table=[1.000,-0.0083117,0.5877285,-0.0620636,-0.0596012,0.0721249,-0.0220216;
        1.065,0.1299457,0.6825954,-0.1513752,-0.0189325,0.0659650,-0.0288748;
        1.230,0.3296958,0.4868735,-0.2210958,0.0554140,-0.0639588,-0.0260542;
        1.500,0.5682053,0.1874525,-0.2951290,0.1088631,-0.1519229,-0.0139754;
        1.950,0.8730280,-0.3920403,-0.3616149,0.2255647,-0.4620442,0.0012448;
        2.800,1.1326077,-1.2367284,-0.4118494,0.2877813,-0.8230357,0.0558651;
        4.500,1.0601591,-1.5999137,-0.3589221,0.2642124,-1.1272340,0.1310694;
        6.200,0.6777470,-0.3272588,-0.2504286,0.1561313,-1.3765031,0.2506212;
        100.00,0.6777470,-0.3272588,-0.2504286,0.1561313,-1.3765031,0.2506212])
    "Fij Factors as a Function of Sky Clearness Range."

equation
    if GroundReflexApprox then
      rho = GroundReflection*rho_avg*max(a_rho*cos(from_deg(InDayAngleSun)), b_rho*(from_deg(InDayAngleSun-180))^2 + c_rho);
      //rho = 1.25*GroundReflection + 0.25*GroundReflection*cos(from_deg(InDayAngleSun)) - 0.5*GroundReflection*(sin(from_deg(InDayAngleSun)))^2 "another approximation"
    else
      rho = GroundReflection;
    end if;
    rho_out=rho;

    // calculation of cos_theta_z [Duffie/Beckman, p.15], cos_theta_z is manually cut at 0 (no neg. values);
    cos_theta_z_help =  sin(from_deg(InDeclinationSun))*sin(from_deg(Latitude))
                      + cos(from_deg(InDeclinationSun))*cos(from_deg(Latitude))*cos(from_deg(InHourAngleSun));

    theta_z = to_deg(acos(cos_theta_z_help));
    cos_theta_z = (cos_theta_z_help + abs(cos_theta_z_help))/2;
    theta_z_pos = to_deg(acos(cos_theta_z));
    theta_z_out = theta_z_pos;

    //calculation of cos_theta [Duffie/Beckman, p.15], cos_theta is manually cut at 0 (no neg. values);
    cos_theta_help =  sin(from_deg(InDeclinationSun))*sin(from_deg(Latitude))*cos(from_deg(Tilt))
                    - sin(from_deg(InDeclinationSun))*cos(from_deg(Latitude))*sin(from_deg(Tilt))*cos(from_deg(Azimut))
                    + cos(from_deg(InDeclinationSun))*cos(from_deg(Latitude))*cos(from_deg(Tilt))*cos(from_deg(InHourAngleSun))
                    + cos(from_deg(InDeclinationSun))*sin(from_deg(Latitude))*sin(from_deg(Tilt))*cos(from_deg(Azimut))*cos(from_deg(InHourAngleSun))
                    + cos(from_deg(InDeclinationSun))*sin(from_deg(Tilt))*sin(from_deg(Azimut))*sin(from_deg(InHourAngleSun));

    cos_theta = (cos_theta_help + abs(cos_theta_help))/2;
    theta_out = to_deg(acos(cos_theta));

    // calculation of R factor [Duffie/Beckman, p.25], but in order not to divide by zero it is determined like a/b in the Model of Perez [Duffie/Beckman, p.94] where the minimum b is set to cos(85 deg);
            // R is manually set to 0 for theta_z_pos >= 80 degrees (-> 90 degrees means sunset)__old solution for the numerical problems of dividing by zero;
            //if noEvent(cos_theta_z <= 0.08715574274) then
            //  R_help = cos_theta_z*cos_theta;
            //else
            //  R_help = cos_theta/cos_theta_z;
            //end if;
            //R = R_help;

      a = max(0,cos_theta)
    "in the Perez Model it is max(0,cos(theta)), but cos_theta is already positiv";
    //b = max(cos(from_deg(85)), cos_theta_z);
      b = max(0.087, cos_theta_z);
      R = a / b;

  // conversion of direct and diffuse horizontal radiation
  if not WeatherFormat then // TRY
    InBeamRadHor = solarInput1;
    InDiffRadHor = solarInput2;
  else  // WeatherFormat == true , TMY then
    InBeamRadHor = solarInput1 * cos_theta_z;
    InDiffRadHor = max(solarInput2-InBeamRadHor, 0);
  end if;

  // calculation of the Beam Irradiation on a tilted surface;
  BeamRadTilt = R*InBeamRadHor;

  //calculation of the Groundreflected Irradiation on a tilted surface;
  RadGroundRefl = rho*(InBeamRadHor + InDiffRadHor)*((1 - cos(from_deg(Tilt)))/2);

  // calculating of the diffuse irradiation with the Method of [Perez 1990];

      //getting brightness coefficients from brightness coefficient vector;

    //Calculation of the extraterrestrial Radiation on a horizontal Surface [Duffie/Beckman 2006, p.9];
      ExtraterRadHor = SolarConstant * (1.000110 + 0.034221 * cos(from_deg(InDayAngleSun))
                      + 0.001280 * sin(from_deg(InDayAngleSun)) + 0.000719 * cos(2*from_deg(InDayAngleSun))
                      + 0.000077 * sin(2*from_deg(InDayAngleSun)));

    //Calculation of the Airmass [Duffie/Beckman 2006, p.10];
      Airmass = exp(-0.0001184*h) / (cos_theta_z + 0.5057 * (96.080 - theta_z_pos)^(-1.634));

/*    //Calculation of epsilon like in the Perez model [Duffie/Beckman 2006, p.94]. In order not to divide by zero, the minimum of c(the diffuse irradiation on a horizontal surface) is set 0.1 W/m2;
      c = max(0.1, InDiffRadHor);
      epsilon = (((InBeamRadHor/b + c) / c) + 5.535E-6 * theta_z_pos^3) / (1 + 5.535E-6 * theta_z_pos^3);
      */
      //Calculation of epsilon like in the EngineeringReference(EnergyPlus) page 143 In order not to divide by zero, the minimum of c(the diffuse irradiation on a horizontal surface) is set 0.1 W/m2;
      c = max(0.1, InDiffRadHor);
      epsilon = (((InBeamRadHor/b + c) / c) + 1.041 * (from_deg(theta_z_out))^3) / (1 + 1.041 * (from_deg(theta_z_out))^3);

      ////get the right coefficients for determining the brigthness coefficient;
      FijFactors.u = epsilon;
      f11=FijFactors.y[1];
      f12=FijFactors.y[2];
      f13=FijFactors.y[3];
      f21=FijFactors.y[4];
      f22=FijFactors.y[5];
      f23=FijFactors.y[6];

    //Calculation of the brightness parameter [Duffie/Beckman 2006, p.94];
      delta = Airmass * InDiffRadHor /  ExtraterRadHor;
      brightness_out=delta;

    //Calculation of the brightness coefficients [Duffie/Beckman 2006, p.94]
      F1 = max(0, f11 + f12 * delta + from_deg(theta_z_out) * f13);
      F2 = f21 + f22 * delta + from_deg(theta_z_out) * f23;

    DiffRadTiltHZ = InDiffRadHor * F2 * sin(from_deg(Tilt))
    "diffuse irradiance from the sky horizon on the tilted Surface";

    DiffRadTiltDOM = InDiffRadHor * (1 - F1)*((1 + cos(from_deg(Tilt)))/2)
    "diffuse irradiation from sky dome on a tilted Surface";

    DiffRadTiltCS = InDiffRadHor * F1 * R
    "diffuse irradiation from circumsolar region on a tilted Surface";

    DiffRadTilt = DiffRadTiltHZ + DiffRadTiltDOM + DiffRadTiltCS
    "total diffuse irradiation on a tilted Surface";

    OutTotalRadTilted.I = max(0, BeamRadTilt + DiffRadTilt + RadGroundRefl)
    "output connector for the total Irradiation on a tilted Surface";

  // calculation of direct. diffuse and ground reflection radiation on tilted surface
  OutTotalRadTilted.I_dir = BeamRadTilt;
  OutTotalRadTilted.I_diff = DiffRadTilt;
  OutTotalRadTilted.I_gr = RadGroundRefl;
  OutTotalRadTilted.AOI = Modelica.Math.acos(cos_theta); // in rad

//Output
  BeamRadTilt=BeamRadTiltOut;
  DiffRadTiltCS=DiffRadTiltCSOut;
  DiffRadTiltHZ=DiffRadTiltHZOut;
  DiffRadTiltDOM=DiffRadTiltDOMOut;
end RadOnTiltedSurf_Perez;


Format weather file: If checked TMY, else TRY.

within AixLib.BoundaryConditions.WeatherData.Old.WeatherTRY.RadiationOnTiltedSurface.BaseClasses;
partial model PartialRadOnTiltedSurf
  parameter Boolean WeatherFormat = false "Format weather file: If checked TMY, else TRY"
  parameter Modelica.Units.NonSI.Angle_deg Latitude=49.5 "latitude of location"
  parameter Real GroundReflection=0.2 "ground reflection coefficient"

  parameter Modelica.Units.NonSI.Angle_deg Azimut=13.400
    "azimut of tilted surface, e.g. 0=south, 90=west, 180=north, -90=east"
  parameter Modelica.Units.NonSI.Angle_deg Tilt=90
    "tilt of surface, e.g. 0=horizontal surface, 90=vertical surface"

    Modelica.Blocks.Interfaces.RealInput InHourAngleSun
    Modelica.Blocks.Interfaces.RealInput InDeclinationSun
  Modelica.Blocks.Interfaces.RealInput solarInput1
    "beam horizontal for TRY; beam for TMY"
  Modelica.Blocks.Interfaces.RealInput solarInput2
    "diffuse horizontal for TRY; ground horizontal for TMY"
  Utilities.Interfaces.SolarRad_out   OutTotalRadTilted
public
    Modelica.Blocks.Interfaces.RealInput InDayAngleSun
end PartialRadOnTiltedSurf;


Validation model for a data reader that has data spanning only some hours in December to January. This is a validation case for a data file that has a few incomplete days in December and goes into
a few days into January. The data does not start and does not end at midnight.

within AixLib.BoundaryConditions.WeatherData.Validation;
model DecemberToJanuary
  "Validation model for a data reader that has data spanning only some hours in December to January"
  extends Modelica.Icons.Example;
  AixLib.BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    computeWetBulbTemperature=false,
    TDewPoiSou=AixLib.BoundaryConditions.Types.DataSource.Parameter,
    HInfHorSou=AixLib.BoundaryConditions.Types.DataSource.Parameter,
    HInfHor=100,
    calTSky=AixLib.BoundaryConditions.Types.SkyTemperatureCalculation.HorizontalRadiation,
    filNam=Modelica.Utilities.Files.loadResource(
        "modelica://AixLib/Resources/Data/BoundaryConditions/WeatherData/Validation/DecemberToJanuary.mos"))
    "Weather data reader with data file going from December to January"
end DecemberToJanuary;


Validation model for a simulation extending with two months over one year but using data for only one year. This is a validation case for a simulation extending with two months over one year,
but using data for only one year.

within AixLib.BoundaryConditions.WeatherData.Validation;
model OverAYear_usingOneYearData
  "Validation model for a simulation extending with two months over one year but using data for only one year"
  extends Modelica.Icons.Example;
  AixLib.BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    computeWetBulbTemperature=false,
    TDewPoiSou=AixLib.BoundaryConditions.Types.DataSource.Parameter,
    HInfHorSou=AixLib.BoundaryConditions.Types.DataSource.Parameter,
    HInfHor=100,
    calTSky=AixLib.BoundaryConditions.Types.SkyTemperatureCalculation.HorizontalRadiation,
    filNam=Modelica.Utilities.Files.loadResource(
        "modelica://AixLib/Resources/weatherdata/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos"))
    "Weather data reader with data file for one year, hourly data"
end OverAYear_usingOneYearData;


Collection of validation models

within AixLib.BoundaryConditions.WeatherData;
package Validation "Collection of validation models"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


Test model for calculating the direct normal radiation. This model tests the calculation of the direct normal radiation.
The instance weaDatInpCon obtains the global horizontal and
the diffuse horizontal solar radiation from its inputs connectors.

within AixLib.BoundaryConditions.WeatherData.Validation;
model ReaderTMY3HDirNor
  "Test model for calculating the direct normal radiation"
  extends Modelica.Icons.Example;
  AixLib.BoundaryConditions.WeatherData.ReaderTMY3 weaDatInpCon(filNam=
        Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
      HSou=AixLib.BoundaryConditions.Types.RadiationDataSource.Input_HGloHor_HDifHor)
    "Weather data reader with radiation data obtained from the inputs' connectors"
protected
  Modelica.Blocks.Sources.Sine HGloHor1(
    f=1/86400,
    startTime=25200,
    offset=0,
    amplitude=100) "Horizontal global radiation"

  Modelica.Blocks.Sources.Sine HGloHor(
    f=1/86400,
    startTime=68428,
    offset=0,
    amplitude=100) "Horizontal global radiation"
  Modelica.Blocks.Math.Add add
  Modelica.Blocks.Math.Gain gaiHDifHor(k=0.5)
    "Gain for diffuse solar radiation"
equation
  connect(HGloHor.y, add.u1)
  connect(HGloHor1.y, add.u2)
  connect(add.y, weaDatInpCon.HGloHor_in)
  connect(add.y, gaiHDifHor.u)
  connect(gaiHDifHor.y, weaDatInpCon.HDifHor_in)
end ReaderTMY3HDirNor;


Validation model for a simulation spanning three years but using only two years of data. This is a validation case for a simulation extending over three years,
but using only two years of data with equidistant monthly values.

within AixLib.BoundaryConditions.WeatherData.Validation;
model ThreeYears_usingTwoYearData
  "Validation model for a simulation spanning three years but using only two years of data"
  extends Modelica.Icons.Example;
  AixLib.BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    computeWetBulbTemperature=false,
    TDewPoiSou=AixLib.BoundaryConditions.Types.DataSource.Parameter,
    HInfHorSou=AixLib.BoundaryConditions.Types.DataSource.Parameter,
    HInfHor=100,
    calTSky=AixLib.BoundaryConditions.Types.SkyTemperatureCalculation.HorizontalRadiation,
    filNam=Modelica.Utilities.Files.loadResource(
        "modelica://AixLib/Resources/Data/BoundaryConditions/WeatherData/Validation/TwoYears_DataOnceAMonth_TMY3.mos"))
    "Weather data reader with data for two years, only monthly values"
end ThreeYears_usingTwoYearData;


Validation model for a simulation spanning three years, starting at a negative time and using only two years of data. This is a validation case for a simulation extending over six years
starting at a negative start date,
but using only two years of data with equidistant monthly values.

within AixLib.BoundaryConditions.WeatherData.Validation;
model ThreeYears_usingTwoYearDataNegativeStart
  "Validation model for a simulation spanning three years, starting at a negative time and using only two years of data"
  extends
    AixLib.BoundaryConditions.WeatherData.Validation.ThreeYears_usingTwoYearData;
end ThreeYears_usingTwoYearDataNegativeStart;


Validation model for a simulation spanning three years, starting at a positive time and using only two years of data. This is a validation case for a simulation extending over six years
starting at a positive start date,
but using only two years of data with equidistant monthly values.

within AixLib.BoundaryConditions.WeatherData.Validation;
model ThreeYears_usingTwoYearDataPositiveStart
  "Validation model for a simulation spanning three years, starting at a positive time and using only two years of data"
  extends
    AixLib.BoundaryConditions.WeatherData.Validation.ThreeYears_usingTwoYearData;
end ThreeYears_usingTwoYearDataPositiveStart;


Package with models for controls

within AixLib;
package Controls "Package with models for controls"
  extends Modelica.Icons.Package;
end Controls;


Types, constants to define menu choices

within AixLib.Controls;
package Types "Types, constants to define menu choices"
  extends Modelica.Icons.TypesPackage;

  type heatPumpMode = enumeration(
      heatPump
      "Heat pump is used as heat pump",
      chiller
      "Heat pump is used as chiller")
    "Enumeration to define mode of heat pump"
  type ControlVariable = enumeration(
      pEva
      "Pressure of evaporation",
      pCon
      "Pressure of condensation",
      TEva
      "Temperature of evaporation",
      TCon
      "Temperature of condensation",
      TSupHea
      "Degree of superheating at evaporator's outlet",
      TSupCol
      "Degree of supercooling at condenser's outlet",
      TCol
      "Temperature at source-sided evaporator's outlet (Cooling temperature)",
      THea
      "Temperature at sink-sided condenser's outlet (Heating temperature)",
      QFloCol
      "Cooling capacity if system works as chiller",
      QFloHea
      "Heating capacity if system works as heat pump")
    "Enumeration to control variables of expansion valves"
end Types;


Rule-based controller of a facade ventilation unit.

within AixLib.Controls.AirHandling;
model FVUController "Rule-based controller of a facade ventilation unit"

  parameter Modelica.Units.SI.Temperature minimumSupTemp=273.15 + 17
    "Minimum supply air temperature";

  parameter Real co2SetConcentration(min=0) = 600
    "Set point for CO2 concentration in ppm";

  parameter Real maxSupFanPower(min=0, max=1) = 1
    "Maximum relative supply air fan power (0..1)";

  parameter Real maxExFanPower(min=0, max=1) = 1
    "Maximum relative exhaust air fan power (0..1)";

  parameter Modelica.Units.SI.TemperatureDifference deltaTemp=1
    "Added to the set temperature in cooling mode";

  Modelica.Blocks.Logical.OnOffController roomToBeCooled(bandwidth=2)
    "Detects cooling demand"
  Modelica.Blocks.Logical.OnOffController roomToBeHeated(bandwidth=2)
    "Detects heating demand"
  Modelica.Blocks.Logical.OnOffController roomToBeVentilated(bandwidth=200)
    "Detects ventilation demand"
  Modelica.Blocks.Logical.OnOffController freeCoolingPossible(bandwidth=2)
    "Detects if free cooling is possible"
  Modelica.Blocks.Logical.OnOffController coldRecoveryPossible(bandwidth=2)
    "Detects if cold recovery is possible"
  Modelica.Blocks.Logical.OnOffController heatRecoveryPossible(bandwidth=2)
    "Detects if heat recovery is possible"
  Modelica.Blocks.Sources.Constant co2SeetConcentrationSource(k=
        co2SetConcentration)
  Modelica.Blocks.Math.Add add(k1=-1)
  Modelica.Blocks.Math.Add add1(k1=-1)
  Modelica.Blocks.Sources.Constant setDeviationFreeCooling(k=-4)
  Modelica.Blocks.Sources.Constant setDeviationRecovery(k=0)
  Modelica.Blocks.Logical.Switch useCoolingValve
  Modelica.Blocks.Logical.And and1
  Modelica.Blocks.Logical.And and2
  Modelica.Blocks.Logical.Or climatizationNeeded
  Modelica.Blocks.Logical.And and3
  Modelica.Blocks.Logical.Switch useHeatingValve
  Modelica.Blocks.Sources.Constant one(k=1)
  Modelica.Blocks.Sources.Constant zero(k=0)
  Modelica.Blocks.Logical.Or useExFan
  Modelica.Blocks.Logical.Switch switch3
  Modelica.Blocks.Sources.Constant exhaustFan(k=maxExFanPower)
  Modelica.Blocks.Logical.Switch switch4
  Modelica.Blocks.Sources.Constant twoHours(k=2*3600)
  Modelica.Blocks.Logical.Timer timer
  Modelica.Blocks.Logical.Greater timePassed
  Modelica.Blocks.Logical.Not not1
  Modelica.Blocks.Sources.Constant twentyMinutes(k=20*60)
  Modelica.Blocks.Logical.Greater timePassed1
  Modelica.Blocks.Logical.Timer timer1
  Modelica.Blocks.Logical.RSFlipFlop rSFlipFlop
  Modelica.Blocks.Logical.Switch switch5
  Modelica.Blocks.Logical.Switch switch1
  Modelica.Blocks.Logical.Switch switch2
  Modelica.Blocks.Sources.Constant supplyFan(k=maxSupFanPower)
  Modelica.Blocks.Logical.Greater PexaLargerPsupa
  Modelica.Blocks.Sources.BooleanExpression booleanExpression(y=if
        fVUControlBus.fanExhaustAirPower > fVUControlBus.fanSupplyAirPower +
        0.001 or fVUControlBus.fanExhaustAirPower < fVUControlBus.fanSupplyAirPower
         - 0.001 then false else true)
  Modelica.Blocks.Logical.Switch switch6
  Modelica.Blocks.Logical.Or or3
  Modelica.Blocks.Logical.Switch useHRC
  Modelica.Blocks.Logical.Pre pre1
  Modelica.Blocks.Logical.GreaterThreshold greaterThreshold(threshold=0.05)
  Modelica.Blocks.Logical.Switch switch7
  Modelica.Blocks.Sources.Constant delta(k=deltaTemp)
  Modelica.Blocks.Math.Add add2
  Interfaces.FVUControlBus fVUControlBus
    "Bus connector containing all inputs and outputs of the controller"
equation
  connect(add1.y, heatRecoveryPossible.u)
  connect(add.y, coldRecoveryPossible.u)
  connect(setDeviationFreeCooling.y, freeCoolingPossible.reference)
  connect(setDeviationRecovery.y, coldRecoveryPossible.reference)
  connect(setDeviationRecovery.y, heatRecoveryPossible.reference)
  connect(roomToBeCooled.y, and1.u1)
  connect(coldRecoveryPossible.y, and1.u2)
  connect(roomToBeCooled.y, useCoolingValve.u2)
  connect(roomToBeCooled.y, and2.u1)
  connect(freeCoolingPossible.y, and2.u2)
  connect(roomToBeCooled.y, climatizationNeeded.u1)
  connect(roomToBeHeated.y, climatizationNeeded.u2)
  connect(heatRecoveryPossible.y, and3.u2)
  connect(roomToBeHeated.y, and3.u1)
  connect(roomToBeHeated.y, useHeatingValve.u2)
  connect(one.y, useHeatingValve.u1)
  connect(zero.y, useHeatingValve.u3)
  connect(roomToBeVentilated.y, useExFan.u2)
  connect(useExFan.y, switch3.u2)
  connect(exhaustFan.y, switch3.u1)
  connect(zero.y, switch3.u3)
  connect(useExFan.y, not1.u)
  connect(timePassed.y, timer1.u)
  connect(not1.y, rSFlipFlop.S)
  connect(rSFlipFlop.Q, timer.u)
  connect(zero.y, useCoolingValve.u3)
  connect(freeCoolingPossible.y, switch5.u2)
  connect(zero.y, switch5.u1)
  connect(one.y, switch5.u3)
  connect(switch5.y, useCoolingValve.u1)
  connect(co2SeetConcentrationSource.y, roomToBeVentilated.u)
  connect(climatizationNeeded.y, switch2.u2)
  connect(supplyFan.y, switch2.u1)
  connect(zero.y, switch2.u3)
  connect(switch2.y, switch1.u3)
  connect(PexaLargerPsupa.y, switch1.u2)
  connect(switch4.y, switch1.u1)
  connect(booleanExpression.y, switch6.u2)
  connect(and1.y, or3.u1)
  connect(and3.y, or3.u2)
  connect(or3.y, useHRC.u2)
  connect(one.y, useHRC.u1)
  connect(zero.y, useHRC.u3)
  connect(add1.y, freeCoolingPossible.u)
  connect(timePassed1.y, pre1.u)
  connect(pre1.y, rSFlipFlop.R)
  connect(switch4.y, PexaLargerPsupa.u1)
  connect(switch2.y, PexaLargerPsupa.u2)
  connect(timer.y, timePassed.u1)
  connect(twoHours.y, timePassed.u2)
  connect(timer1.y, timePassed1.u1)
  connect(twentyMinutes.y, timePassed1.u2)
  connect(exhaustFan.y, switch4.u1)
  connect(timePassed.y, switch4.u2)
  connect(switch3.y, switch4.u3)
  connect(and2.y, useExFan.u1)
  connect(greaterThreshold.y, switch7.u2)
  connect(switch4.y, greaterThreshold.u)
  connect(one.y, switch6.u3)
  connect(zero.y, switch6.u1)
  connect(one.y, switch7.u1)
  connect(zero.y, switch7.u3)
  connect(delta.y, add2.u2)
  connect(add2.y, roomToBeCooled.u)
  connect(useCoolingValve.y, fVUControlBus.coolingValveOpening)
  connect(useHeatingValve.y, fVUControlBus.heatingValveOpening)
  connect(switch4.y, fVUControlBus.fanExhaustAirPower)
  connect(switch1.y, fVUControlBus.fanSupplyAirPower)
  connect(switch6.y, fVUControlBus.circulationDamperOpening)
  connect(useHRC.y, fVUControlBus.hRCDamperOpening)
  connect(switch7.y, fVUControlBus.freshAirDamperOpening)
  connect(roomToBeCooled.reference, fVUControlBus.roomTemperature)
  connect(roomToBeHeated.u, fVUControlBus.roomTemperature)
  connect(add1.u1, fVUControlBus.roomTemperature)
  connect(add.u2, fVUControlBus.roomTemperature)
  connect(add1.u2, fVUControlBus.outdoorTemperature)
  connect(add.u1, fVUControlBus.outdoorTemperature)
  connect(add2.u1, fVUControlBus.roomSetTemperature)
  connect(roomToBeHeated.reference, fVUControlBus.roomSetTemperature)
  connect(roomToBeVentilated.reference, fVUControlBus.co2Concentration)
end FVUController;


within AixLib.Controls;
package AirHandling "Controllers of (decentralized) air handling units"
extends Modelica.Icons.VariantsPackage;
end AirHandling;


P, PI, PD, and PID controller with limited output, anti-windup compensation and setpoint weighting. PID controller in the standard form

within AixLib.Controls.Continuous;
block LimPID
  "P, PI, PD, and PID controller with limited output, anti-windup compensation and setpoint weighting"

  extends Modelica.Blocks.Interfaces.SVcontrol;
  output Real controlError = u_s - u_m
    "Control error (set point - measurement)";

  parameter Modelica.Blocks.Types.SimpleController controllerType=
         Modelica.Blocks.Types.SimpleController.PI "Type of controller";
  parameter Real k(min=0) = 1 "Gain of controller";
  parameter Modelica.Units.SI.Time Ti(min=Modelica.Constants.small) = 0.5
    "Time constant of Integrator block"
  parameter Modelica.Units.SI.Time Td(min=0) = 0.1
    "Time constant of Derivative block"
  parameter Real yMax(start=1)=1 "Upper limit of output";
  parameter Real yMin=0 "Lower limit of output";
  parameter Real wp(min=0) = 1 "Set-point weight for Proportional block (0..1)";
  parameter Real wd(min=0) = 0 "Set-point weight for Derivative block (0..1)"
  parameter Real Ni(min=100*Modelica.Constants.eps) = 0.9
    "Ni*Ti is time constant of anti-windup compensation"
  parameter Real Nd(min=100*Modelica.Constants.eps) = 10
    "The higher Nd, the more ideal the derivative block"
  parameter Modelica.Blocks.Types.Init initType=Modelica.Blocks.Types.Init.InitialState
    "Type of initialization (1: no init, 2: steady state, 3: initial state, 4: initial output)"
      // Removed as the Limiter block no longer uses this parameter.
      // parameter Boolean limitsAtInit = true
      //  "= false, if limits are ignored during initialization"
      //
  parameter Real xi_start=0
    "Initial or guess value value for integrator output (= integrator state)"
  parameter Real xd_start=0
    "Initial or guess value for state of derivative block"
  parameter Real y_start=0 "Initial value of output"
  parameter Boolean strict=true "= true, if strict limits with noEvent(..)"

  parameter Boolean reverseActing = true
    "Set to true for reverse acting, or false for direct acting control action";

  parameter AixLib.Types.Reset reset = AixLib.Types.Reset.Disabled
    "Type of controller output reset"

  parameter Real y_reset=xi_start
    "Value to which the controller output is reset if the boolean trigger has a rising edge, used if reset == AixLib.Types.Reset.Parameter"

  Modelica.Blocks.Interfaces.BooleanInput trigger
    if reset <> AixLib.Types.Reset.Disabled
    "Resets the controller output when trigger becomes true"

  Modelica.Blocks.Interfaces.RealInput y_reset_in
    if reset == AixLib.Types.Reset.Input
    "Input signal for state to which integrator is reset, enabled if reset = AixLib.Types.Reset.Input"

  Modelica.Blocks.Math.Add addP(k1=revAct*wp, k2=-revAct) "Adder for P gain"
  Modelica.Blocks.Math.Add addD(k1=revAct*wd, k2=-revAct) if with_D
    "Adder for D gain"
  Modelica.Blocks.Math.Gain P(k=1) "Proportional term"
  Utilities.Math.IntegratorWithReset I(
    final reset=if reset == AixLib.Types.Reset.Disabled then reset else AixLib.Types.Reset.Input,

    final y_reset=y_reset,
    final k=unitTime/Ti,
    final y_start=xi_start,
    final initType=if initType == Modelica.Blocks.Types.Init.SteadyState then
        Modelica.Blocks.Types.Init.SteadyState else if initType == Modelica.Blocks.Types.Init.InitialState
         or initType == Modelica.Blocks.Types.Init.InitialState then Modelica.Blocks.Types.Init.InitialState
         else Modelica.Blocks.Types.Init.NoInit) if with_I "Integral term"

  Modelica.Blocks.Continuous.Derivative D(
    final k=Td/unitTime,
    final T=max([Td/Nd,1.e-14]),
    final x_start=xd_start,
    final initType=if initType == Modelica.Blocks.Types.Init.SteadyState or
        initType == Modelica.Blocks.Types.Init.InitialOutput then Modelica.Blocks.Types.Init.SteadyState
         else if initType == Modelica.Blocks.Types.Init.InitialState then
        Modelica.Blocks.Types.Init.InitialState else Modelica.Blocks.Types.Init.NoInit)
    if with_D "Derivative term"

  Modelica.Blocks.Math.Add3 addPID(
    final k1=1,
    final k2=1,
    final k3=1) "Adder for the gains"

protected
  constant Modelica.Units.SI.Time unitTime=1

  final parameter Real revAct = if reverseActing then 1 else -1
    "Switch for sign for reverse or direct acting controller";

  parameter Boolean with_I = controllerType==Modelica.Blocks.Types.SimpleController.PI or
                             controllerType==Modelica.Blocks.Types.SimpleController.PID
    "Boolean flag to enable integral action"
  parameter Boolean with_D = controllerType==Modelica.Blocks.Types.SimpleController.PD or
                             controllerType==Modelica.Blocks.Types.SimpleController.PID
    "Boolean flag to enable derivative action"

  Modelica.Blocks.Sources.Constant Dzero(final k=0) if not with_D
    "Zero input signal"

  Modelica.Blocks.Sources.Constant Izero(final k=0) if not with_I
    "Zero input signal"

  Modelica.Blocks.Interfaces.RealInput y_reset_internal
   "Internal connector for controller output reset"

  Modelica.Blocks.Math.Add3 addI(
    final k1=revAct,
    final k2=-revAct) if with_I
    "Adder for I gain"

  Modelica.Blocks.Math.Add addSat(
    final k1=+1,
    final k2=-1) if with_I
    "Adder for integrator feedback"

  Modelica.Blocks.Math.Gain gainPID(final k=k) "Multiplier for control gain"

  Modelica.Blocks.Math.Gain gainTrack(k=1/(k*Ni)) if with_I
    "Gain for anti-windup compensation"

  Limiter limiter(
    final uMax=yMax,
    final uMin=yMin,
    final strict=strict)
    "Output limiter"

  Modelica.Blocks.Sources.RealExpression intRes(
    final y=y_reset_internal/k - addPID.u1 - addPID.u2)
    if reset <> AixLib.Types.Reset.Disabled
    "Signal source for integrator reset"

  // The block Limiter below has been implemented as it is introduced in MSL 3.2.3, but
  // not all tools include MSL 3.2.3.
  // See https://github.com/ibpsa/modelica-ibpsa/pull/1222#issuecomment-554114617
block Limiter "Limit the range of a signal"
  parameter Real uMax(start=1) "Upper limits of input signals";
  parameter Real uMin= -uMax "Lower limits of input signals";
  parameter Boolean strict=false "= true, if strict limits with noEvent(..)"
  parameter Boolean limitsAtInit=true
    "Has no longer an effect and is only kept for backwards compatibility (the implementation uses now the homotopy operator)"
  extends Modelica.Blocks.Interfaces.SISO;

equation
  assert(uMax >= uMin, "Limiter: Limits must be consistent. However, uMax (=" + String(uMax) +
                       ") < uMin (=" + String(uMin) + ")");

  if strict then
    y = smooth(0, noEvent(if u > uMax then uMax else if u < uMin then uMin else u));
  else
    y = smooth(0,if u > uMax then uMax else if u < uMin then uMin else u);
  end if;
end Limiter;

initial equation
  if initType == Modelica.Blocks.Types.Init.InitialOutput then
     gainPID.y = y_start;
  end if;

equation
  assert(yMax >= yMin, "LimPID: Limits must be consistent. However, yMax (=" + String(yMax) +
                       ") < yMin (=" + String(yMin) + ")");
  if initType == Modelica.Blocks.Types.Init.InitialOutput and (y_start < yMin
       or y_start > yMax) then
      Modelica.Utilities.Streams.error("LimPID: Start value y_start (=" + String(y_start) +
         ") is outside of the limits of yMin (=" + String(yMin) +") and yMax (=" + String(yMax) + ")");
  end if;

  // Equations for conditional connectors
  connect(y_reset_in, y_reset_internal);

  if reset <> AixLib.Types.Reset.Input then
    y_reset_internal = y_reset;
  end if;

  connect(u_s, addP.u1)
  connect(u_s, addD.u1)
  connect(u_s, addI.u1)
  connect(addP.y, P.u)
  connect(addD.y, D.u)
  connect(addI.y, I.u)
  connect(P.y, addPID.u1)
  connect(D.y, addPID.u2)
  connect(I.y, addPID.u3)
  connect(addPID.y, gainPID.u)
  connect(gainPID.y, addSat.u2)
  connect(gainPID.y, limiter.u)
  connect(limiter.y, addSat.u1)
  connect(limiter.y, y)
  connect(addSat.y, gainTrack.u)
  connect(gainTrack.y, addI.u3)
  connect(u_m, addP.u2)
  connect(u_m, addD.u2)
  connect(u_m, addI.u2)
  connect(Dzero.y, addPID.u2)
  connect(Izero.y, addPID.u3)
  connect(trigger, I.trigger)
  connect(intRes.y, I.y_reset_in)
end LimPID;


Outputs the number of signals that are above/below a certain threshold. Block that outputs the number of inputs that exceed a threshold.
The parameter kind is used to determine the kind of the
inequality. The table below shows the allowed settings.

within AixLib.Controls.Continuous;
block NumberOfRequests
  "Outputs the number of signals that are above/below a certain threshold"
   extends Modelica.Blocks.Icons.Block;

  parameter Integer nin "Number of inputs";
  parameter Real threShold = 0 "Threshold";
  parameter Integer kind
    "Set to 0 for u>threShold, to 1 for >=, to 2 for <= or to 3 for <";
  Modelica.Blocks.Interfaces.IntegerOutput y
    "Number of input signals that violate the threshold"
  Modelica.Blocks.Interfaces.RealInput u[nin] "Input signals"
algorithm
  y := 0;
  for i in 1:nin loop
    if kind == 0 then
      if u[i] > threShold then
        y := y+1;
      end if;
    end if;
    if kind == 1 then
      if u[i] >= threShold then
        y := y+1;
      end if;
    end if;
    if kind == 2 then
      if u[i] <= threShold then
        y := y+1;
      end if;
    end if;
    if kind == 3 then
      if u[i] < threShold then
        y := y+1;
      end if;
    end if;
  end for;
end NumberOfRequests;


Records the time since the input changed to false. Timer that starts at the initial time with a value of 0,
and gets reset each time the input signal switches to false.

within AixLib.Controls.Continuous;
model OffTimer "Records the time since the input changed to false"
  extends Modelica.Blocks.Icons.PartialBooleanBlock;

  Modelica.Blocks.Interfaces.BooleanInput u "Connector of Boolean input signal"
  Modelica.Blocks.Interfaces.RealOutput y "Connector of Real output signal"

protected
  discrete Modelica.Units.SI.Time entryTime "Time instant when u became true";
initial equation
  pre(entryTime) = time;
equation
  when (not u) then
    entryTime = time;
  end when;
  y = time - entryTime;
end OffTimer;


within AixLib.Controls;
package Continuous "Package with models for discrete time controls"
  extends Modelica.Icons.Package;
end Continuous;


PI controller that can switch the output range of the controller.

within AixLib.Controls.Continuous;
model PITemp "PI controller that can switch the output range of the controller"

  Modelica.Blocks.Interfaces.RealInput setPoint
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort
  parameter Real h = 1 "upper limit controller output"
  parameter Real l = 0 "lower limit of controller output"
  parameter Real KR = 1 "Gain"
  parameter Modelica.Units.SI.Time TN=1 "Time Constant (T>0 required)"
  Modelica.Blocks.Interfaces.RealOutput y
  parameter Boolean rangeSwitch = false "Switch controller output range";
  Modelica.Blocks.Interfaces.BooleanInput onOff "Switches Controler on and off"
  Modelica.Blocks.Logical.Switch switch2
  Modelica.Blocks.Logical.TriggeredTrapezoid triggeredTrapezoid(rising = 0, falling = 60)
  Modelica.Blocks.Math.Product product
  Modelica.Blocks.Continuous.LimPID PI(k = KR, yMax = if rangeSwitch then -l else h, yMin = if rangeSwitch then -h else l, controllerType = Modelica.Blocks.Types.SimpleController.PI, Ti = TN, Td = 0.1)
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temperatureSensor
  Modelica.Blocks.Logical.Switch switch1
equation
  connect(switch2.y, y)
  connect(onOff, switch2.u2)
  connect(onOff, triggeredTrapezoid.u)
  connect(triggeredTrapezoid.y, product.u1)
  connect(product.y, switch2.u3)
  connect(PI.y, switch2.u1)
  connect(PI.y, product.u2)
  connect(setPoint, PI.u_s)
  connect(temperatureSensor.port, heatPort)
  connect(onOff, switch1.u2)
  connect(switch1.y, PI.u_m)
  connect(temperatureSensor.T, switch1.u1)
  connect(setPoint, switch1.u3)
end PITemp;


Ranks output signals such that y[i] >= y[i+1]. Block that sorts the input signal u[:] such that the output
signal satisfies y[i] >= y[i+1] for all i=1, ..., nin-1.

within AixLib.Controls.Continuous;
block SignalRanker "Ranks output signals such that y[i] >= y[i+1]"
   extends Modelica.Blocks.Interfaces.MIMO(final nout=nin);
equation
  y = Modelica.Math.Vectors.sort(u, ascending=false);
end SignalRanker;


Test model for PID controller with optional reverse action. This model tests the implementation of the
PID controller with optional reverse action.
The model limPIDOri is the original
implementation of the controller from the Modelica
Standard Library. The models limPID
and limPIDRev are the implementations
from the AixLib library. The model
limPIDRev is parameterized to have
reverse action.
The assertion blocks test whether the results
of all three controllers are identical.

within AixLib.Controls.Continuous.Examples;
model LimPID "Test model for PID controller with optional reverse action"
  extends Modelica.Icons.Example;

  Modelica.Blocks.Sources.Pulse pulse(period=0.25)
  AixLib.Controls.Continuous.LimPID limPID(
    controllerType=Modelica.Blocks.Types.SimpleController.PID,
    Ti=1,
    Td=1,
    yMax=1,
    yMin=-1,
    initType=Modelica.Blocks.Types.Init.InitialState)
  AixLib.Controls.Continuous.LimPID limPIDRev(
    controllerType=Modelica.Blocks.Types.SimpleController.PID,
    reverseActing=false,
    Ti=1,
    Td=1,
    yMax=1,
    yMin=-1,
    initType=Modelica.Blocks.Types.Init.InitialState)
    "Controller with reverse action"
  Modelica.Blocks.Sources.Constant const(k=0.5)
  Modelica.Blocks.Math.Gain gain(k=-1)
  AixLib.Utilities.Diagnostics.AssertEquality assertEquality(
    threShold=1e-3)
  Modelica.Blocks.Continuous.LimPID limPIDOri(
    controllerType=Modelica.Blocks.Types.SimpleController.PID,
    Ti=1,
    Td=1,
    yMax=1,
    yMin=-1,
    initType=Modelica.Blocks.Types.Init.InitialState)
  AixLib.Utilities.Diagnostics.AssertEquality assertEquality1(
    threShold=1e-3)
equation
  connect(pulse.y, limPID.u_s)
  connect(pulse.y, limPIDRev.u_s)
  connect(const.y, limPID.u_m)
  connect(const.y, limPIDRev.u_m)
  connect(limPIDRev.y, gain.u)
  connect(gain.y, assertEquality.u2)
  connect(limPID.y, assertEquality.u1)
  connect(pulse.y, limPIDOri.u_s)
  connect(const.y, limPIDOri.u_m)
  connect(assertEquality1.u1, limPIDOri.y)
  connect(assertEquality1.u2, limPID.y)
end LimPID;


Example that demonstrates the controller output reset. Plant model for

AixLib.Controls.Continuous.Examples.LimPIDWithReset.
consisting of a simple heat transfer model.

within AixLib.Controls.Continuous.Examples;
model LimPIDWithReset
  "Example that demonstrates the controller output reset"
  extends Modelica.Icons.Example;

  Plant plaWitRes "Plant connected to controller with reset"
  Controller conWitRes(reset=AixLib.Types.Reset.Parameter)
    "Controller with reset"
  Plant plaNoRes "Plant connected to controller without reset"
  Controller conNoRes(reset=AixLib.Types.Reset.Disabled)
    "Controller without reset"
  Modelica.Blocks.Sources.Pulse TSet(
    amplitude=20,
    width=50,
    offset=293.15,
    y(unit="K"),
    period=180)    "Temperature set point"

protected
  model Plant
    "Plant model"
    extends Modelica.Blocks.Icons.Block;

    Modelica.Blocks.Interfaces.RealInput Q_flow(unit="W")
      "Heat flow rate added to system"
    Modelica.Blocks.Interfaces.RealOutput T(unit="K")
      "Controlled temperature"

    Modelica.Thermal.HeatTransfer.Components.HeatCapacitor cap(
      C=10,
      T(fixed=true,
        start=293.15)) "Heat capacitor"
    Modelica.Thermal.HeatTransfer.Components.HeatCapacitor cap1(
      C=10,
      T(fixed=true,
          start=293.15)) "Heat capacitor"

    Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSen
      "Temperature sensor"
    Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea
      "Prescribed heat flow rate"
    Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=5)
      "Thermal conductor"
    Modelica.Thermal.HeatTransfer.Sources.FixedTemperature TBou(T=293.15)
      "Boundary condition"
    Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon1(G=1)
      "Thermal conductor"

  equation
    connect(Q_flow, preHea.Q_flow)
    connect(T, temSen.T)
    connect(TBou.port, theCon.port_a)
    connect(cap.port, theCon1.port_a)
    connect(theCon1.port_b, cap1.port)
    connect(cap1.port, temSen.port)
    connect(theCon.port_b, cap.port)
    connect(preHea.port, cap.port)
  end Plant;

  model Controller "PID controller with optional output reset"
    extends Modelica.Blocks.Icons.Block;

    parameter AixLib.Types.Reset reset=AixLib.Types.Reset.Disabled
      "Type of controller output reset";

    Modelica.Blocks.Interfaces.RealInput TSet(unit="K") "Temperature set point"

    Modelica.Blocks.Interfaces.RealInput u_m(unit="K")
      "Measured temperature"

    Modelica.Blocks.Interfaces.RealOutput y
      "Control signal"

    AixLib.Controls.Continuous.LimPID conPID(
      final reset=reset,
      yMax=1,
      yMin=0,
      Ti=1,
      k=10) "PI controller"
    Modelica.Blocks.Math.Gain gain(k=5000)
                                          "Gain for heat flow rate"
    Modelica.Blocks.Logical.GreaterThreshold trigger(threshold=303.15)
      "Trigger input for controller reset"

  equation
    connect(conPID.y,gain. u)
    connect(trigger.y,conPID. trigger)
    connect(u_m, conPID.u_m)
    connect(conPID.u_s, TSet)
    connect(trigger.u, TSet)
    connect(gain.y, y)
  end Controller;

equation
  connect(plaWitRes.T, conWitRes.u_m)
  connect(conWitRes.y, plaWitRes.Q_flow)

  connect(plaNoRes.T, conNoRes.u_m)
  connect(conNoRes.y, plaNoRes.Q_flow)
  connect(TSet.y, conWitRes.TSet)
  connect(TSet.y, conNoRes.TSet)
end LimPIDWithReset;


Example model for block that outputs the number of requests. Example that demonstrates the use of the block

AixLib.Controls.Continuous.NumberOfRequests.
The parameters of the block are such that the output is incremented
for each input signal that is strictly larger than 0.
The figure below shows the inputs and the output of the block.

within AixLib.Controls.Continuous.Examples;
model NumberOfRequests
  "Example model for block that outputs the number of requests"
  extends Modelica.Icons.Example;
  AixLib.Controls.Continuous.NumberOfRequests numReq(
    nin=2,
    threShold=0,
    kind=0)
  Modelica.Blocks.Sources.Sine sine(f=2)
  Modelica.Blocks.Sources.Pulse pulse(
    amplitude=1-pulse.offset,
    period=0.35,
    offset=-0.01)
equation
  connect(sine.y, numReq.u[1])
  connect(pulse.y, numReq.u[2])
end NumberOfRequests;


Example model for off timer. Example that demonstrates the use of the model

AixLib.Controls.Continuous.OffTimer.
The input to the two timers are alternating boolean values.
Whenever the input becomes false(=0), the timer is reset.
The figures below show the input and output of the blocks.

within AixLib.Controls.Continuous.Examples;
model OffTimer "Example model for off timer"
  extends Modelica.Icons.Example;

  Modelica.Blocks.Sources.BooleanPulse booleanPulse(period=0.2)
  AixLib.Controls.Continuous.OffTimer offTim1
  AixLib.Controls.Continuous.OffTimer offTim2
  Modelica.Blocks.Logical.Not not1
equation
  connect(booleanPulse.y, offTim1.u)
  connect(booleanPulse.y, not1.u)
  connect(offTim2.u, not1.y)
end OffTimer;


within AixLib.Controls.Continuous;
package Examples "Collection of models that illustrate model use and test models"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


Example model for signal ranker. Example that demonstrates the use of the signal ranker model.
The figure below shows the input and output signals of the block.
Note that
sigRan.y[1] ≥ sigRan.y[2] ≥ sigRan.y[3].

within AixLib.Controls.Continuous.Examples;
model SignalRanker "Example model for signal ranker"
  extends Modelica.Icons.Example;
  Modelica.Blocks.Sources.Sine sine(f=2, startTime=0.025) "Sine source"
  Modelica.Blocks.Sources.Pulse pulse(period=0.25)
    "Pulse source"
  AixLib.Controls.Continuous.SignalRanker sigRan(nin=3)
    "Signal ranker"
  Modelica.Blocks.Sources.ExpSine expSine(f=10, damping=1)
    "Exponential sine source"
equation
  connect(sine.y, sigRan.u[1])
  connect(pulse.y, sigRan.u[2])
  connect(expSine.y, sigRan.u[3])
end SignalRanker;


Test model for PID controller with optional intgerator reset. This model tests the implementation the
AixLib.Controls.Continuous.LimPID
with integrator reset.

within AixLib.Controls.Continuous.Validation;
model LimPIDReset
  "Test model for PID controller with optional intgerator reset"
  extends Modelica.Icons.Example;

  Modelica.Blocks.Sources.Sine setPoi(f=1) "Set point signal"
  AixLib.Controls.Continuous.LimPID limPIDPar(
    yMax=1,
    yMin=-1,
    reset=AixLib.Types.Reset.Parameter,
    y_reset=0.2,
    Ti=20,
    Td=10,
    k=0.2,
    controllerType=Modelica.Blocks.Types.SimpleController.PID,
    initType=Modelica.Blocks.Types.Init.InitialOutput,
    y_start=0.3) "PId controller with integrator reset to a parameter value"
  Modelica.Blocks.Sources.Constant mea(k=0.5) "Measured signal"
  AixLib.Controls.Continuous.LimPID limPIDDef(
    initType=Modelica.Blocks.Types.Init.InitialState,
    Td=10,
    k=1,
    Ti=1,
    yMax=100,
    yMin=-100,
    controllerType=Modelica.Blocks.Types.SimpleController.PID,
    xi_start=0.2,
    xd_start=0.1) "PID controller without integrator reset"
  Modelica.Blocks.Sources.BooleanPulse trigger(
    width=50,
    startTime=0.1,
    period=0.2) "Boolean pulse to reset integrator"
  AixLib.Controls.Continuous.LimPID limPIDInp(
    y_reset=0.2,
    Td=10,
    reset=AixLib.Types.Reset.Input,
    yMax=1,
    yMin=-1,
    k=0.2,
    Ti=20,
    controllerType=Modelica.Blocks.Types.SimpleController.PID,
    initType=Modelica.Blocks.Types.Init.InitialOutput,
    y_start=0.3) "PId controller with integrator reset to an input value"
  Modelica.Blocks.Sources.Constant conRes(k=0.9)
    "Signal to which integrator will be reset to"
  Modelica.Blocks.Continuous.LimPID limPIDOri(
    initType=Modelica.Blocks.Types.Init.InitialState,
    Td=10,
    k=1,
    Ti=1,
    yMax=100,
    yMin=-100,
    controllerType=Modelica.Blocks.Types.SimpleController.PID,
    xi_start=0.2,
    xd_start=0.1) "PID controller from Standard Modelica Library"
  AixLib.Utilities.Diagnostics.AssertEquality assEqu
equation
  connect(setPoi.y, limPIDPar.u_s)
  connect(mea.y, limPIDPar.u_m)
  connect(setPoi.y, limPIDDef.u_s)
  connect(mea.y, limPIDDef.u_m)
  connect(trigger.y, limPIDPar.trigger)
  connect(setPoi.y, limPIDInp.u_s)
  connect(mea.y, limPIDInp.u_m)
  connect(trigger.y, limPIDInp.trigger)
  connect(conRes.y, limPIDInp.y_reset_in)
  connect(setPoi.y, limPIDOri.u_s)
  connect(mea.y, limPIDOri.u_m)
  connect(assEqu.u1, limPIDOri.y)
  connect(limPIDDef.y, assEqu.u2)
end LimPIDReset;


Test model for off timer with negative start time. This model tests the implementation the
AixLib.Controls.Continuous.OffTimer
for negative start time.

within AixLib.Controls.Continuous.Validation;
model OffTimerNonZeroStart
  "Test model for off timer with negative start time"
  extends AixLib.Controls.Continuous.Examples.OffTimer(
    booleanPulse(startTime=-1));
end OffTimerNonZeroStart;


Collection of validation models

within AixLib.Controls.Continuous;
package Validation "Collection of validation models"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


Zero order hold for boolean variable. Block that delays the boolean input signal by
one sampling interval.
For example,
if u denotes the input,
y denotes the output, and
ti and ti+1
denote subsequent sampling
instants, then the model outputs

within AixLib.Controls.Discrete;
block BooleanDelay "Zero order hold for boolean variable"
  extends Modelica.Blocks.Interfaces.BooleanSISO;
  extends Modelica.Blocks.Interfaces.DiscreteBlock(
    firstTrigger(fixed=true,
                 start=firstTrigger_start));
  parameter Boolean firstTrigger_start=false
    "Start value for rising edge signals first sample instant"
protected
  Boolean ySample;
algorithm
  when {sampleTrigger, initial()} then
    y := ySample;
    ySample := u;
  end when;
end BooleanDelay;


within AixLib.Controls;
package Discrete "Package with models for discrete time controls"
  extends Modelica.Icons.Package;
end Discrete;


Example model for boolean delay. Example that demonstrates the use of the boolean delay block.
The output signal of the block is delayed by one sampling interval.

within AixLib.Controls.Discrete.Examples;
model BooleanDelay "Example model for boolean delay"
  extends Modelica.Icons.Example;
  AixLib.Controls.Discrete.BooleanDelay del(samplePeriod=0.05)
  Modelica.Blocks.Sources.BooleanPulse booleanPulse(
    period=0.1,
    width=50,
    startTime=0.01)
equation
  connect(booleanPulse.y, del.u)
end BooleanDelay;


within AixLib.Controls.Discrete;
package Examples "Collection of models that illustrate model use and test models"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


Control to avoid Legionella in the DHW. This model represents the anti legionella control of a real heat
  pump. Based on a daily or weekly approach, the given supply
  temperature is raised above the minimal temperature required for the
  thermal desinfection (at least 60 °C) for a given duration
  minTimeAntLeg.

within AixLib.Controls.HeatPump;
model AntiLegionella "Control to avoid Legionella in the DHW"

  parameter Modelica.Units.SI.ThermodynamicTemperature TLegMin=333.15
    "Temperature at which the legionella in DWH dies";

  parameter Modelica.Units.SI.Time minTimeAntLeg
    "Minimal duration of antilegionella control";
  parameter Boolean weekly=true
    "Switch between a daily or weekly trigger approach"
  parameter Integer trigWeekDay "Day of the week at which control is triggered"
  parameter Integer trigHour "Hour of the day at which control is triggered";
  parameter AixLib.Utilities.Time.Types.ZeroTime zerTim
    "Enumeration for choosing how reference time (time = 0) should be defined";
  parameter Integer yearRef=2016 "Year when time = 0, used if zerTim=Custom";
  Modelica.Blocks.Logical.GreaterEqual
                               TConLessTLegMin
    "Compare if current TCon is smaller than the minimal TLeg"
  AixLib.Utilities.Logical.SmoothSwitch switchTLeg
    "Switch to Legionalla control if needed"
  Modelica.Blocks.Interfaces.RealOutput TSet_out
    "Set value for the condenser outlet temperature"

  Modelica.Blocks.Sources.Constant constTLegMin(final k=TLegMin)
    "Temperature at which the legionella in DWH dies"
  Modelica.Blocks.Logical.Timer timeAntiLeg "Time in which legionella will die"
  Modelica.Blocks.Logical.GreaterThreshold
                               greaterThreshold(final threshold=minTimeAntLeg)
  Modelica.Blocks.Interfaces.RealInput TSet_in "Input of TSet"
  Modelica.Blocks.Logical.Pre pre1
  AixLib.Utilities.Time.DaytimeSwitch daytimeSwitch(
    final hourDay=trigHour,
    final zerTim=zerTim,
    final yearRef=yearRef,
    final weekDay=trigWeekDay)
    "If given day and hour match the current daytime, output will be true"
  Modelica.Blocks.MathInteger.TriggeredAdd triggeredAdd(use_reset=true, use_set=
       false,
    y_start=0)
              "See info of model for description"
  Modelica.Blocks.Sources.IntegerConstant intConPluOne(final k=1)
    "Value for counting"
  Modelica.Blocks.Logical.LessThreshold    lessThreshold(final threshold=1)
    "Checks if value is less than one"
  Modelica.Blocks.Math.IntegerToReal intToReal "Converts Integer to Real"
  Modelica.Blocks.Interfaces.RealInput TSupAct
    "Input of actual supply temperature"
equation
  connect(constTLegMin.y, TConLessTLegMin.u2)
  connect(switchTLeg.y, TSet_out)
  connect(TSet_in, switchTLeg.u1)
  connect(switchTLeg.u3, constTLegMin.y)
  connect(timeAntiLeg.u, pre1.y)
  connect(TConLessTLegMin.y, pre1.u)
  connect(lessThreshold.y, switchTLeg.u2)
  connect(intToReal.y, lessThreshold.u)
  connect(intConPluOne.y, triggeredAdd.u)
  connect(intToReal.u, triggeredAdd.y)
  connect(greaterThreshold.y, triggeredAdd.reset)
  connect(TSupAct, TConLessTLegMin.u1)
  connect(daytimeSwitch.isDaytime, triggeredAdd.trigger)
  connect(timeAntiLeg.y, greaterThreshold.u)
end AntiLegionella;


Control block to ensure no frost limits heat flow at the evaporator. Basic model for a defrost control. The icing factor is calculated in
  the heat pump based on functions or other models.

within AixLib.Controls.HeatPump;
model DefrostControl
  "Control block to ensure no frost limits heat flow at the evaporator"
  parameter Real minIceFac "Minimal value above which no defrost is necessary";

  parameter Real deltaIceFac = 0.1 "Bandwitdth for hystereses. If the icing factor is based on the duration of defrost, this value is necessary to avoid state-events.";
  Modelica.Blocks.Interfaces.BooleanInput hea
  Modelica.Blocks.Interfaces.RealInput ySet
    "Set value relative speed of compressor. Analog from 0 to 1"
  Utilities.Logical.SmoothSwitch swiErr
    "If an error occurs, the value of the conZero block will be used(0)"
  Modelica.Blocks.Sources.Constant conOne(final k=1)
    "If Defrost is enabled, HP runs at full power"
  Modelica.Blocks.Interfaces.RealOutput yOut
    "Relative speed of compressor. From 0 to 1"
  Modelica.Blocks.Interfaces.BooleanOutput heaOut
  Fluid.HeatPumps.ModularReversible.BaseClasses.RefrigerantMachineControlBus sigBus
  Modelica.Blocks.Logical.Hysteresis            iceFacGreMinChi(
    final uLow=minIceFac,
    final uHigh=minIceFac + deltaIceFac,
    final pre_y_start=true)
    "Check if icing factor is greater than a boundary"
  Modelica.Blocks.Logical.LogicalSwitch logicalSwitch
    "If a chiller is used to defrost, mode will be false"
  Modelica.Blocks.Sources.BooleanConstant conTrueUseChi(final k=false)
  "Set mode to false to simulate the defrost cycle"
equation
  connect(conOne.y, swiErr.u3)
  connect(swiErr.y,yOut)
  connect(ySet, swiErr.u1)

  connect(sigBus.iceFacMea, iceFacGreMinChi.u)
  connect(iceFacGreMinChi.y, swiErr.u2)
  connect(logicalSwitch.y, heaOut)
  connect(hea, logicalSwitch.u1)
  connect(iceFacGreMinChi.y, logicalSwitch.u2)
  connect(conTrueUseChi.y, logicalSwitch.u3)
end DefrostControl;


Control block which makes sure the desired temperature is supplied by the HP. Model of a heat pump controller, setting a compressor signal and heat
  pump mode based on a given input temperarute. A heating curve is used
  to determine the supply temperature. This TSet is later converted to
  a signal nSet based on the acutal temperature in the system.
  Futhermore, a legionella protection for DHW-Simulation is
  implemented.

within AixLib.Controls.HeatPump;
model HPControl
  "Control block which makes sure the desired temperature is supplied by the HP"
  //General
  replaceable model TSetToNSet =
      AixLib.Controls.HeatPump.BaseClasses.PartialTSetToNSet constrainedby
    AixLib.Controls.HeatPump.BaseClasses.PartialTSetToNSet(
    final use_secHeaGen=use_secHeaGen)

  parameter Boolean use_secHeaGen=false "True to choose a bivalent system"
//Heating Curve
  parameter Boolean use_tableData=true
    "Choose between tables or function to calculate TSet"
  replaceable function HeatingCurveFunction =
      SetPoints.Functions.HeatingCurveFunction constrainedby
    SetPoints.Functions.PartialBaseFct
  parameter
    AixLib.DataBase.Boiler.DayNightMode.HeatingCurvesDayNightBaseDataDefinition
    heatingCurveRecord=
      AixLib.DataBase.Boiler.DayNightMode.HeatingCurves_Vitotronic_Day25_Night10()
    "Record with information about heating curve data"
  parameter Real declination=2 "Declination of curve"
  parameter Real day_hour=6 "Hour of day at which day mode is enabled"
  parameter Real night_hour=22 "Hour of day at which night mode is enabled"
  parameter AixLib.Utilities.Time.Types.ZeroTime zerTim=AixLib.Utilities.Time.Types.ZeroTime.NY2017
    "Enumeration for choosing how reference time (time = 0) should be defined. Used for heating curve and antilegionella"
//Anti Legionella
  parameter Boolean use_antLeg "True if Legionella Control is of relevance"
  parameter Modelica.Units.SI.ThermodynamicTemperature TLegMin=333.15
    "Temperature at which the legionella in DWH dies"
  parameter Modelica.Units.SI.Time minTimeAntLeg
    "Minimal duration of antilegionella control"
  parameter Boolean weekly=true
    "Switch between a daily or weekly trigger approach"
  parameter Integer trigWeekDay "Day of the week at which control is triggered"
  parameter Integer trigHour=3 "Hour of the day at which control is triggered"

  AixLib.Controls.HeatPump.AntiLegionella antiLegionella(
    final TLegMin=TLegMin,
    minTimeAntLeg=minTimeAntLeg,
    trigWeekDay=trigWeekDay,
    trigHour=trigHour,
    yearRef=2017,
    final zerTim=zerTim)
                    if use_antLeg
  AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.RefrigerantMachineControlBus sigBusHP
  Modelica.Blocks.Interfaces.RealOutput yOut "Relative compressor speed"
  Modelica.Blocks.Interfaces.RealInput T_oda "Outdoor air temperature"
  Modelica.Blocks.Interfaces.RealOutput ySecHeaGen if use_secHeaGen
                                                   "Relative power of second heat generator, from 0 to 1"
  Modelica.Blocks.Interfaces.RealOutput y_sou
  Modelica.Blocks.Interfaces.RealOutput y_sin
  Modelica.Blocks.Sources.Constant        constHeating1(final k=1)
    "If you want to include chilling, please insert control blocks first"

  AixLib.Controls.SetPoints.HeatingCurve heatCurve(
    final TOffset=0,
    final use_dynTRoom=false,
    final zerTim=zerTim,
    final day_hour=day_hour,
    final night_hour=night_hour,
    final heatingCurveRecord=heatingCurveRecord,
    final declination=declination,
    redeclare function HeatingCurveFunction = HeatingCurveFunction,
    use_tableData=use_tableData,
    final TRoom_nominal=293.15)

  TSetToNSet ConvTSetToNSet
  Modelica.Blocks.Routing.RealPassThrough realPasThrAntLeg if not use_antLeg
                                                           "No Anti Legionella"
  Modelica.Blocks.Sources.BooleanConstant constHeating(final k=true)
    "If you want to include chilling, please insert control blocks first"
  Modelica.Blocks.Interfaces.BooleanOutput hea "Heating mode"
  Modelica.Blocks.Interfaces.RealInput TSup "Supply temperature"

equation

  connect(T_oda, sigBusHP.T_oda)
  connect(T_oda, heatCurve.T_oda)
  connect(heatCurve.TSet, antiLegionella.TSet_in)

  connect(antiLegionella.TSet_out,ConvTSetToNSet. TSet)

  connect(ConvTSetToNSet.nOut,yOut)

  connect(sigBusHP,ConvTSetToNSet. sigBusHP)
  connect(heatCurve.TSet, realPasThrAntLeg.u)

  connect(realPasThrAntLeg.y,ConvTSetToNSet. TSet)
  connect(hea, constHeating.y)
  connect(TSup, antiLegionella.TSupAct)

  connect(TSup,ConvTSetToNSet.TMea)

  connect(ConvTSetToNSet.ySecHeaGen, ySecHeaGen)
  connect(ySecHeaGen, ySecHeaGen)
  connect(constHeating1.y, y_sou)
  connect(constHeating1.y, y_sin)
end HPControl;


Converter model for an inverter / speed controlled HP modulating between 0 and 1.

within AixLib.Controls.HeatPump;
model InverterControlledHP
  "Converter model for an inverter / speed controlled HP modulating between 0 and 1"
  extends BaseClasses.PartialTSetToNSet;
  parameter Real bandwidth "Bandwith of hysteresis of controller";
  parameter Real k     "Gain of controller"
  parameter Modelica.Units.SI.Time Ti    "Time constant of Integrator block"
  parameter Real yMin=0.2 "Minimal relative compressor speed"

  Modelica.Blocks.Logical.OnOffController onOffController(bandwidth=bandwidth,
      pre_y_start=false)                                                                    "Hysteresis controller for set temperature"
  Modelica.Blocks.Continuous.LimPID InverterControl(
    final controllerType=Modelica.Blocks.Types.SimpleController.PI,
    final k=k,
    final Ti=Ti,
    final yMax=1,
    final yMin=yMin)
    "PI-Control for a inverter controlled HP"

equation
  connect(TSet, onOffController.reference)
  connect(InverterControl.y, swiNullHP.u1)
  connect(TSet, InverterControl.u_s)
  connect(TMea, onOffController.u)
  connect(TMea, InverterControl.u_m)
  connect(onOffController.y, andHeaLim.u1)
end InverterControlledHP;


within AixLib.Controls;
package HeatPump "Contains typical controllers for heat pumps"
extends Modelica.Icons.VariantsPackage;
end HeatPump;


Controller gives full speed or stop signal depending on temperature hysteresis.

within AixLib.Controls.HeatPump;
model TwoPointControlledHP
  "Controller gives full speed or stop signal depending on temperature hysteresis"
  extends AixLib.Controls.HeatPump.BaseClasses.PartialTSetToNSet;
  parameter Real bandwidth "Bandwith of hysteresis of controller";
  Modelica.Blocks.Logical.OnOffController onOffController(final bandwidth=bandwidth,
      final pre_y_start=false)                                                              "Hysteresis controller for set temperature"
  Modelica.Blocks.Sources.Constant conOne(final k=1)
                                               "Constant one for on off heat pump"

equation
  connect(TSet, onOffController.reference)
  connect(conOne.y, swiNullHP.u1)
  connect(TMea, onOffController.u)
  connect(onOffController.y, andHeaLim.u1)
end TwoPointControlledHP;


within AixLib.Controls.HeatPump;
package BaseClasses "Package with base classes for the HeatPump controller"
end BaseClasses;


Base class of a heat pump controller with bus interface. Base class for heat pump controllers that contains the AixLib.Controls.Interfaces.HeatPumpControlBus.

within AixLib.Controls.HeatPump.BaseClasses;
partial model PartialHPController
  "Base class of a heat pump controller with bus interface"
  AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.RefrigerantMachineControlBus heatPumpControlBus
end PartialHPController;


Partial model to convert set temperature to compressor speed of heat pump.

within AixLib.Controls.HeatPump.BaseClasses;
partial model PartialTSetToNSet
  "Partial model to convert set temperature to compressor speed of heat pump"
  parameter Boolean use_secHeaGen=false
                                       "True to choose a bivalent system"

  // Heating limit temperature
  parameter Boolean use_heaLim=true "=false to disable moving average heating limit"
  parameter Modelica.Units.SI.Temperature T_heaLim=293.15
    "Heating limit temperature. If the filtered outdoor air temperature surpasses this threshold, the device will be shut down"
  parameter Modelica.Units.SI.Time movAveTime=300
    "Time span for building the average of the outdoor air temperature. Used for heating limit temperature"

  Modelica.Blocks.Logical.Switch        swiNullHP "If HP is off, zero is passed"
  Modelica.Blocks.Sources.Constant conZer(final k=0)
                                               "If an error occurs, the compressor speed is set to zero"
  Modelica.Blocks.Interfaces.RealInput TSet(
      final quantity="ThermodynamicTemperature",
      final unit="K",
      final displayUnit="degC") "Set temperature"
  Modelica.Blocks.Interfaces.RealOutput nOut "Relative speed of compressor. From 0 to 1"
  AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.RefrigerantMachineControlBus sigBusHP
  Modelica.Blocks.Interfaces.RealOutput ySecHeaGen if use_secHeaGen
                                                   "Relative power of second heat generator, from 0 to 1"
  Modelica.Blocks.Interfaces.RealInput TMea(
      final quantity="ThermodynamicTemperature",
      final unit="K",
      final displayUnit="degC") "Actual temperature, control variable"
  AixLib.Utilities.Logical.SmoothSwitch swiNullsecHeaGen if use_secHeaGen
    "If second heater is off, zero is passed"
  AixLib.Utilities.Math.MovingAverage movAve(final aveTime=movAveTime, final u_start=0)
    if use_heaLim
    "Moving average to account for fluctuations in the outdoor air temperature"
  Modelica.Blocks.Logical.And andHeaLim
    "Check if control and heating limit temperature yield true to turn the device on"
  Modelica.Blocks.Logical.LessThreshold      lessThreshold(final threshold=
        T_heaLim) if use_heaLim

  Modelica.Blocks.Sources.BooleanConstant    booleanConstant(final k=true)
 if not use_heaLim
equation
  connect(conZer.y, swiNullHP.u3)
  connect(swiNullHP.y, nOut)
  connect(swiNullsecHeaGen.y, ySecHeaGen)
  connect(conZer.y, swiNullsecHeaGen.u3)
  connect(sigBusHP.T_oda, movAve.u)
  connect(movAve.y, lessThreshold.u)
  connect(lessThreshold.y, andHeaLim.u2)
  connect(andHeaLim.y, swiNullHP.u2)
  connect(booleanConstant.y, andHeaLim.u2)
end PartialTSetToNSet;


<html><p>
  Example for an inverter controlled heat pump. Play around with the PI
  settings to see how they influence nSet depending on TSet and TMea.
</p>
</html>. Example for an inverter controlled heat pump. Play around with the PI
  settings to see how they influence nSet depending on TSet and TMea.

within AixLib.Controls.HeatPump.Examples;
model InverterControlledHeatPump
  extends Modelica.Icons.Example;
  extends PartialHeatPumpController(redeclare InverterControlledHP hPController(
      bandwidth=2,
      k=0.05,
      Ti=100));
end InverterControlledHeatPump;


within AixLib.Controls.HeatPump;
package Examples "Examples for controller and interface usage"
extends Modelica.Icons.ExamplesPackage;
end Examples;


Example for usage of heat pump controller. Partial example model for heat pump controls.

within AixLib.Controls.HeatPump.Examples;
partial model PartialHeatPumpController
  "Example for usage of heat pump controller"

  AixLib.Fluid.HeatPumps.ModularReversible.BaseClasses.RefrigerantMachineControlBus heatPumpControlBus
    "Required to make the signals on the bus accessible"
  Modelica.Blocks.Sources.Sine T_meas(
    f=1/3600,
    amplitude=6,
    offset=310) "Generates the measured temperature"
  Modelica.Blocks.Sources.Constant T_set(k=310)
    "Provides the temperature set point"
  Modelica.Blocks.Interfaces.RealOutput TMea(
    final quantity="ThermodynamicTemperature",
    final unit="K",
    displayUnit="degC",
    min=0) "Outputs the temperature measurement"
  replaceable BaseClasses.PartialTSetToNSet hPController constrainedby
    BaseClasses.PartialTSetToNSet(
    final use_secHeaGen=false,
    use_heaLim=false,
    T_heaLim=293.15,
    movAveTime=300)
  Modelica.Blocks.Interfaces.RealOutput TSet(
    final quantity="ThermodynamicTemperature",
    final unit="K",
    displayUnit="degC",
    min=0) "Outputs the temperature measurement"
  Modelica.Blocks.Interfaces.RealOutput nSet
    "Outputs the temperature measurement"
  Modelica.Blocks.Sources.Constant T_oda(k=273.15)
    "Provides the temperature set point"
equation
  connect(T_meas.y, TMea)
  connect(hPController.TSet, T_set.y)
  connect(hPController.TMea, T_meas.y)
  connect(nSet, hPController.nOut)
  connect(T_oda.y, heatPumpControlBus.TOdaMea)
  connect(heatPumpControlBus, hPController.sigBusHP)
  connect(T_set.y, TSet)
end PartialHeatPumpController;


<html><p>
  Example for an two-point hysteresis controlled heat pump. Play around
  with the bandwith settings to see how it influence nSet depending on
  TSet and TMea.
</p>
</html>. Example for an two-point hysteresis controlled heat pump. Play around
  with the bandwith settings to see how it influence nSet depending on
  TSet and TMea.

within AixLib.Controls.HeatPump.Examples;
model TwoPointControlledHeatPump
  extends Modelica.Icons.Example;
  extends PartialHeatPumpController(redeclare TwoPointControlledHP hPController(
        bandwidth=2));
end TwoPointControlledHeatPump;


Model of an internal controller for modular compressors. This a controller block used for modular compressor models as
  presented in AixLib.Fluid.Movers.Compressors.ModularCompressors.
  The controller block consists of simple PID controllers for each
  compressor and no further control strategy is implemented yet.
  However, these PID controller are only initialised a nd activated if
  no external controller is provided (i.g. if useExt =
  false).

within AixLib.Controls.HeatPump.ModularHeatPumps;
model ModularCompressorController
  "Model of an internal controller for modular compressors"
  extends BaseClasses.PartialModularController(
    dataBus(final nCom=nCom));

  // Definition of dummy signals for controllers' rest inputs
  //
  Modelica.Blocks.Sources.BooleanConstant trigger[nCom](each k=false)
    "Boolean block to provide dummy signal to controllers' trigger signals"
  Modelica.Blocks.Sources.Constant triggerVal[nCom](each k=0)
    "Real block to provide dummy signal to controllers' rest signals"

equation
  // Connect internal controller with inputs and outputs
  //
  connect(intCon.u_m, dataBus.comBus.meaConVarCom);
  connect(intCon.u_s, dataBus.comBus.intSetPoiCom);
  connect(intCon.y, manVarThr);

  if useExt then
    connect(manVarThr, dataBus.comBus.extManVarCom);
  end if;
  manVar = manVarThr;

  /*The output block 'manVarThr' is mandantory since the internal controller is
    conditional. Therefore, the connection 'connect(internalController.y,
    manVarThr)' is required to prevent errors that would occur otherwise if the
    internal controller is removed conditionally.
  */

  // Connect dummy signals to controllers' rest inputs
  //
  connect(trigger.y, intCon.trigger)
  connect(triggerVal.y, intCon.y_reset_in)

  // Connect output signals
  //
  connect(useExtBlo, dataBus.comBus.extConCom)
  connect(curManVar, dataBus.comBus.curManVarVal)
end ModularCompressorController;


Model of an internal controller for modular expansion valves. This a controller block used for modular expansion valve models as
  presented in
  AixLib.Fluid.Actuators.Valves.ExpansionValves.ModularExpansionValves.
  The controller block consists of simple PID controllers for each
  expansion valve and no further control strategy is implemented.
  However, these PID controller are only initialised and activated if
  no external controller is provided (i.g. if useExt =
  false).

within AixLib.Controls.HeatPump.ModularHeatPumps;
model ModularExpansionValveController
  "Model of an internal controller for modular expansion valves"
  extends BaseClasses.PartialModularController(
    dataBus(final nVal=nCom));

  // Definition of dummy signals for controllers' rest inputs
  //
  Modelica.Blocks.Sources.BooleanConstant tri[nCom](each k=false)
    "Boolean block to provide dummy signal to controllers' trigger signals"
  Modelica.Blocks.Sources.Constant triVal[nCom](each k=0)
    "Real block to provide dummy signal to controllers' rest signals"

equation
  // Connect internal controller with inputs and outputs
  //
  connect(intCon.u_m, dataBus.expValBus.meaConVarVal);
  connect(intCon.u_s, dataBus.expValBus.intSetPoiVal);
  connect(intCon.y, manVarThr);

  if useExt then
    connect(manVar, dataBus.expValBus.extManVarVal);
  end if;
  manVar = manVarThr;

  /*The output block 'manVarThr' is mandantory since the internal controller is
    conditional. Therefore, the connection 'connect(internalController.y,
    manVarThr)' is required to prevent errors that would occur otherwise if the
    internal controller is removed conditionally.
  */

  // Connect dummy signals to controllers' rest inputs
  //
  connect(tri.y, intCon.trigger)
  connect(triVal.y, intCon.y_reset_in)

  // Connect parameters describing if internal or external signal is used
  //
  connect(useExtBlo, dataBus.expValBus.extConVal)

  // Connect output signals
  //
  connect(curManVar, dataBus.expValBus.curManVarVal)
end ModularExpansionValveController;


within AixLib.Controls.HeatPump;
package ModularHeatPumps "Package that contains controllers used for modular heat pumps"
  extends Modelica.Icons.VariantsPackage;
end ModularHeatPumps;


within AixLib.Controls.HeatPump.ModularHeatPumps;
package BaseClasses "Package that contains base models used for controllers of modular heat pumps"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


Base model used for all modular controllers. This is a base model used by all modular controller models. It
  defines basic inputs, outputs, parameters and sub-models. The main
  inputs and outputs are the modular heat pump control bus AixLib.Controls.Interfaces.ModularHeatPumpControlBus,
  the manipulated signals passed to the controlled components and the
  current manipulated signals. The main sub-model is a PID controller
  presented in AixLib.Controls.Continuous.LimPID
  that is initialised if no external controller is used. Furthermore,
  all parameters with respect to the PID controller are propagated
  because the modular controller shall be used in an architectural
  modeling approach.

within AixLib.Controls.HeatPump.ModularHeatPumps.BaseClasses;
partial model PartialModularController
  "Base model used for all modular controllers"

  // Definition of parameters describing modular approach
  //
  parameter Integer nCom = 1
    "Number of components"
  parameter Boolean useExt = true
    "= true, if external signal is used instead of internal controllers"

  // Definition of parameters describing the controller
  //
  parameter Modelica.Blocks.Types.SimpleController controllerType[nCom]=
    fill(Modelica.Blocks.Types.SimpleController.PID,nCom)
    "Type of controller"
  parameter Boolean reverseAction[nCom] = fill(false,nCom)
    "= true, if medium flow rate is throttled through cooling coil controller"
  parameter AixLib.Types.Reset reset[nCom]=
    fill(AixLib.Types.Reset.Disabled,nCom)
    "Type of controller output reset"

  parameter Real k[nCom] = fill(1,nCom)
    "Gain of controller"

  parameter Modelica.Units.SI.Time Ti[nCom]=fill(0.5, nCom)
    "Time constant of integrator block"
  parameter Real Ni[nCom] = fill(0.9,nCom)
    "Ni*Ti is time constant of anti-windup compensation"

  parameter Modelica.Units.SI.Time Td[nCom]=fill(0.1, nCom)
    "Time constant of derivative block"
  parameter Real Nd[nCom] = fill(10,nCom)
    "The higher Nd, the more ideal the derivative block"

  parameter Real wp[nCom] = fill(1,nCom)
    "Set-point weight for Proportional block (0..1)"
  parameter Real wd[nCom] = fill(0,nCom)
    "Set-point weight for Derivative block (0..1)"

  parameter Real yMax[nCom] = fill(1,nCom)
    "Upper limit of output"
  parameter Real yMin[nCom] = fill(0,nCom)
    "Lower limit of output"

  parameter Modelica.Blocks.Types.Init initType[nCom]=fill(Modelica.Blocks.Types.Init.InitialState,
      nCom)
    "Init: (1: no init, 2: steady state, 3: initial state, 4: initial output)"
  parameter Real xi_start[nCom] = fill(0,nCom)
    "Initial or guess value value for integrator output (= integrator state)"
  parameter Real xd_start[nCom] = fill(0,nCom)
    "Initial or guess value for state of derivative block"
  parameter Real y_start[nCom] = fill(0,nCom)
    "Initial value of output"

  // Definition of models
  //
  Continuous.LimPID intCon[nCom](
    final controllerType=controllerType,
    final k=k,
    final Ti=Ti,
    final Ni=Ni,
    final Td=Td,
    final Nd=Nd,
    final wp=wp,
    final wd=wd,
    final yMax=yMax,
    final yMin=yMin,
    final initType=initType,
    final xi_start=xi_start,
    final xd_start=xd_start,
    final y_start=y_start,
    final reverseActing=reverseAction,
    final reset=reset) if not useExt
    "Internal controller if internal controller is required"

  // Definition of connectors and inputs and outputs
  //
  Modelica.Blocks.Interfaces.BooleanOutput useExtBlo
    "Boolean block used to set data bus' useExt-signal"
  Modelica.Blocks.Interfaces.RealOutput manVarThr[nCom]
    "Input connector to pass controller's manipualted signals through if activated"

  Modelica.Blocks.Interfaces.RealInput curManVar[nCom]
    "Array of signals with current manipulated variables of controlled components"
  Modelica.Blocks.Interfaces.RealOutput manVar[nCom]
    "Array of manipulated variables for controlled components"

  Interfaces.ModularHeatPumpControlBus dataBus "Data bus with control signals"
end PartialModularController;


within AixLib.Controls;
package HVACAgentBasedControl "This package contains a library for agent-based control of HVAC systems"
extends Modelica.Icons.VariantsPackage;
end HVACAgentBasedControl;