Block that selects the solar radiation source and outputs the solar radiation quantities. Block that outputs the direct normal, diffuse horizontal and diffuse global
solar irradiation.
This block computes these output quantities based on conditionally provided
input signals.

within AixLib.BoundaryConditions.WeatherData.BaseClasses;
block SourceSelectorRadiation
  "Block that selects the solar radiation source and outputs the solar radiation quantities"
  extends Modelica.Blocks.Icons.Block;
  parameter AixLib.BoundaryConditions.Types.RadiationDataSource datSou "Data source"

  Modelica.Blocks.Interfaces.RealInput HDirNorFil(
    final quantity="RadiantEnergyFluenceRate",
    final unit="W/m2")
   if datSou == AixLib.BoundaryConditions.Types.RadiationDataSource.File
    "Direct normal solar irradiation from weather data file"

  Modelica.Blocks.Interfaces.RealInput HDirNorIn(
    final quantity="RadiantEnergyFluenceRate",
    final unit="W/m2")
   if datSou == AixLib.BoundaryConditions.Types.RadiationDataSource.Input_HDirNor_HDifHor or
      datSou == AixLib.BoundaryConditions.Types.RadiationDataSource.Input_HDirNor_HGloHor
    "Direct normal solar irradiation from input connector"

  Modelica.Blocks.Interfaces.RealInput HDifHorFil(
    final quantity="RadiantEnergyFluenceRate",
    final unit="W/m2")
  if datSou == AixLib.BoundaryConditions.Types.RadiationDataSource.File
    "Diffuse horizontal solar irradiation from weather data file"

  Modelica.Blocks.Interfaces.RealInput HDifHorIn(
    final quantity="RadiantEnergyFluenceRate",
    final unit="W/m2")
  if datSou == AixLib.BoundaryConditions.Types.RadiationDataSource.Input_HGloHor_HDifHor or
     datSou == AixLib.BoundaryConditions.Types.RadiationDataSource.Input_HDirNor_HDifHor
    "Diffuse horizontal solar irradiation from input connector"

  Modelica.Blocks.Interfaces.RealInput HGloHorFil(
    final quantity="RadiantEnergyFluenceRate",
    final unit="W/m2")
   if datSou == AixLib.BoundaryConditions.Types.RadiationDataSource.File
    "Global horizontal solar irradiation from weather data file"

  Modelica.Blocks.Interfaces.RealInput HGloHorIn(
    final quantity="RadiantEnergyFluenceRate",
    final unit="W/m2")
   if datSou == AixLib.BoundaryConditions.Types.RadiationDataSource.Input_HGloHor_HDifHor or
      datSou == AixLib.BoundaryConditions.Types.RadiationDataSource.Input_HDirNor_HGloHor
    "Global horizontal solar irradiation from input connector"

  Modelica.Blocks.Interfaces.RealInput zen(
    final quantity="Angle",
    final unit="rad")
    "Zenith angle"

  Modelica.Blocks.Interfaces.RealOutput HDirNor(
    final quantity="RadiantEnergyFluenceRate",
    final unit="W/m2")
    "Direct normal solar irradiation"
  Modelica.Blocks.Interfaces.RealOutput HDifHor(
    final quantity="RadiantEnergyFluenceRate",
    final unit="W/m2")
    "Diffuse horizontal solar irradiation"
  Modelica.Blocks.Interfaces.RealOutput HGloHor(
    final quantity="RadiantEnergyFluenceRate",
    final unit="W/m2")
    "Global horizontal solar irradiation"

protected
  constant Real epsCos = 1e-6 "Small value to avoid division by 0";
  constant Modelica.Units.SI.HeatFlux solCon=1367.7 "Solar constant";

  // Conditional connectors
  Modelica.Blocks.Interfaces.RealInput HGloHor_in_internal(
    final quantity="RadiantEnergyFluenceRate",
    final unit="W/m2") "Needed to connect to conditional connector";
  Modelica.Blocks.Interfaces.RealInput HDifHor_in_internal(
    final quantity="RadiantEnergyFluenceRate",
    final unit="W/m2") "Needed to connect to conditional connector";
  Modelica.Blocks.Interfaces.RealInput HDirNor_in_internal(
    final quantity="RadiantEnergyFluenceRate",
    final unit="W/m2") "Needed to connect to conditional connector";

equation
  // Conditional connect statements
  connect(HGloHor_in_internal, HGloHorFil);
  connect(HDifHor_in_internal, HDifHorFil);
  connect(HDirNor_in_internal, HDirNorFil);
  connect(HGloHor_in_internal, HGloHorIn);
  connect(HDifHor_in_internal, HDifHorIn);
  connect(HDirNor_in_internal, HDirNorIn);

  //---------------------------------------------------------------------------
  // Select global horizontal radiation connector
  if datSou == AixLib.BoundaryConditions.Types.RadiationDataSource.Input_HDirNor_HDifHor then
    HGloHor = max(0, HDirNor_in_internal*cos(zen)+HDifHor_in_internal)
      "Calculate the HGloHor using HDirNor and HDifHor according to (A.4.14) and (A.4.15)";
    HGloHor_in_internal = 0;
  else
    HGloHor = max(0, HGloHor_in_internal)
      "Get HGloHor using weather data file or input connector of weather data reader";
  end if;

  //---------------------------------------------------------------------------
  // Select diffuse horizontal radiation connector
  if datSou == AixLib.BoundaryConditions.Types.RadiationDataSource.Input_HDirNor_HGloHor then
    HDifHor = max(0, HGloHor_in_internal - HDirNor_in_internal*cos(zen))
      "Calculate the HGloHor using HDirNor and HDifHor according to (A.4.14) and (A.4.15)";
    HDifHor_in_internal = 0;
  else
    HDifHor = max(0, HDifHor_in_internal)
      "Get HDifHor using weather data file or input connector of weather data reader";
  end if;

  //---------------------------------------------------------------------------
  // Select direct normal radiation connector
  if datSou == AixLib.BoundaryConditions.Types.RadiationDataSource.Input_HGloHor_HDifHor then
      HDirNor = max(0, min(
        solCon,
        (HGloHor_in_internal -HDifHor_in_internal)*
          AixLib.Utilities.Math.Functions.spliceFunction(
            x=cos(zen),
            pos=AixLib.Utilities.Math.Functions.inverseXRegularized(cos(zen), epsCos),
            neg=0,
            deltax=epsCos)))
      "Calculate the HDirNor using HGloHor and HDifHor according to (A.4.14) and (A.4.15)";
    HDirNor_in_internal = 0;
  else
    HDirNor = max(0, HDirNor_in_internal)
      "Get HDirNor using weather data file or input connector of weather data reader";
  end if;
end SourceSelectorRadiation;


Test model for CheckBlackBodySkyTemperature. This example tests the model that checks the black-body sky temperature.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model CheckBlackBodySkyTemperature
  "Test model for CheckBlackBodySkyTemperature"
  extends Modelica.Icons.Example;
  AixLib.BoundaryConditions.WeatherData.BaseClasses.CheckBlackBodySkyTemperature
    cheSkyBlaBodTem "Check for the black body sky temperature"
  Modelica.Blocks.Sources.Ramp TBlaBod(
    height=140-0.02,
    duration=1,
    offset=273.15 - 69.99) "Black body sky temperature"
equation
  connect(TBlaBod.y, cheSkyBlaBodTem.TIn)
end CheckBlackBodySkyTemperature;


Test model for ceiling height check. This example tests the model that constrains the ceiling height.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model CheckCeilingHeight "Test model for ceiling height check"
  extends Modelica.Icons.Example;
  AixLib.Utilities.Time.ModelTime modTim
  AixLib.BoundaryConditions.WeatherData.BaseClasses.CheckCeilingHeight
    cheCeiHei "Block that constrains the ceiling height"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertTime conTim(
    weaDatStaTim=0,
    weaDatEndTim=31536000)
    "Block that converts time"
protected
  Modelica.Blocks.Tables.CombiTable1Ds datRea(
    tableOnFile=true,
    tableName="tab1",
    fileName=Modelica.Utilities.Files.loadResource(
       Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos")),
    columns=2:30,
    smoothness=Modelica.Blocks.Types.Smoothness.ContinuousDerivative)
    "Data reader"
equation
  connect(datRea.y[20], cheCeiHei.ceiHeiIn)
  connect(modTim.y, conTim.modTim)
  connect(conTim.calTim, datRea.u)
end CheckCeilingHeight;


Test model for pressure check. This example tests the model that asserts that the pressure is within acceptable bounds.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model CheckPressure "Test model for pressure check"
  extends Modelica.Icons.Example;

  AixLib.BoundaryConditions.WeatherData.BaseClasses.CheckPressure chePre
    "Block that checks the pressure"
  AixLib.Utilities.Time.ModelTime modTim
    "Block that outputs simulation time"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertTime conTim(
    weaDatStaTim=0,
    weaDatEndTim=31536000)
    "Block that converts time"
protected
  Modelica.Blocks.Tables.CombiTable1Ds datRea(
    tableOnFile=true,
    tableName="tab1",
    fileName=Modelica.Utilities.Files.loadResource(
       Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos")),
    columns=2:30,
    smoothness=Modelica.Blocks.Types.Smoothness.ContinuousDerivative)
    "Data reader"
equation
  connect(datRea.y[4], chePre.PIn)
  connect(modTim.y, conTim.modTim)
  connect(conTim.calTim, datRea.u)
end CheckPressure;


Test model for CheckRadiation. This example tests the model that constrains the radiation.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model CheckRadiation "Test model for CheckRadiation"
  extends AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples.ConvertRadiation;
  AixLib.BoundaryConditions.WeatherData.BaseClasses.CheckRadiation cheGloRad
    "Check global horizontal radiation"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.CheckRadiation cheDifRad
    "Check diffuse horizontal radiation"
equation

  connect(conGloRad.HOut, cheGloRad.HIn)
  connect(conDifRad.HOut, cheDifRad.HIn)
end CheckRadiation;


Test model for CheckRelativeHumidity. This example tests the model that constrains the relative humidity.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model CheckRelativeHumidity "Test model for CheckRelativeHumidity"
  extends AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples.ConvertRelativeHumidity;

  AixLib.BoundaryConditions.WeatherData.BaseClasses.CheckRelativeHumidity cheRelHum
    "Block that constrains the relative humidity"
equation
  connect(conRelHum.relHumOut, cheRelHum.relHumIn)
end CheckRelativeHumidity;


Test model for checking sky cover. This example tests the model that constrains the sky cover.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model CheckSkyCover "Test model for checking sky cover"
  extends Modelica.Icons.Example;
  AixLib.BoundaryConditions.WeatherData.BaseClasses.CheckSkyCover
    cheTotSkyCov "Check total sky cover"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.CheckSkyCover
    cheOpaSkyCov "Check opaque sky cover"
protected
  AixLib.Utilities.Time.ModelTime modTim "Generate simulation time"

  Modelica.Blocks.Tables.CombiTable1Ds datRea(
    tableOnFile=true,
    tableName="tab1",
    fileName=Modelica.Utilities.Files.loadResource(
       Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos")),
    columns=2:30,
    smoothness=Modelica.Blocks.Types.Smoothness.ContinuousDerivative)
    "Data reader"

  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertTime conTim(
    weaDatStaTim=0,
    weaDatEndTim=31536000)

  Modelica.Blocks.Math.Gain conTotSkyCov(final k=0.1)
    "Convert sky cover from [0...10] to [0...1]"
  Modelica.Blocks.Math.Gain conOpaSkyCov1(final k=0.1)
    "Convert sky cover from [0...10] to [0...1]"
equation
  connect(modTim.y, conTim.modTim)
  connect(conTim.calTim, datRea.u)
  connect(datRea.y[17], conTotSkyCov.u)
  connect(datRea.y[18], conOpaSkyCov1.u)
  connect(conTotSkyCov.y, cheTotSkyCov.nIn)
  connect(conOpaSkyCov1.y, cheOpaSkyCov.nIn)
end CheckSkyCover;


Test model for CheckTemperature. This example tests the model that checks the temperature.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model CheckTemperature "Test model for CheckTemperature"
  extends Modelica.Icons.Example;
  AixLib.BoundaryConditions.WeatherData.BaseClasses.CheckDryBulbTemperature
    cheTemDryBul "Check dry bulb temperature "
  AixLib.BoundaryConditions.WeatherData.BaseClasses.CheckDewPointTemperature
    cheTemDewPoi "Check dew point temperature"
  AixLib.Utilities.Time.ModelTime modTim
    "Block that outputs the model time"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertTime conTim(
    weaDatStaTim=0,
    weaDatEndTim=31536000)
    "Block that converts time"
  Modelica.Blocks.Math.UnitConversions.From_degC from_degC
    "Block that converts temperature"
  Modelica.Blocks.Math.UnitConversions.From_degC from_degC1
    "Block that converts temperature"
protected
  Modelica.Blocks.Tables.CombiTable1Ds datRea(
    tableOnFile=true,
    tableName="tab1",
    fileName=Modelica.Utilities.Files.loadResource(
       Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos")),
    columns=2:30,
    smoothness=Modelica.Blocks.Types.Smoothness.ContinuousDerivative)
    "Data reader"
equation
  connect(modTim.y, conTim.modTim)
  connect(conTim.calTim, datRea.u)
  connect(datRea.y[1], from_degC.u)
  connect(from_degC.y, cheTemDryBul.TIn)
  connect(datRea.y[2], from_degC1.u)
  connect(from_degC1.y, cheTemDewPoi.TIn)
end CheckTemperature;


Test model for wind direction check. This example tests the model that constrains the wind direction.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model CheckWindDirection "Test model for wind direction check"
  extends Modelica.Icons.Example;
  AixLib.Utilities.Time.ModelTime modTim
  AixLib.BoundaryConditions.WeatherData.BaseClasses.CheckWindDirection
    cheWinDir "Block that constrains the wind direction"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertTime conTim(
    weaDatStaTim=0,
    weaDatEndTim=31536000)
    "Block that converts time"
  Modelica.Blocks.Math.UnitConversions.From_deg from_deg
    "Block that converts temperature"
protected
  Modelica.Blocks.Tables.CombiTable1Ds datRea(
    tableOnFile=true,
    tableName="tab1",
    fileName=Modelica.Utilities.Files.loadResource(
       Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos")),
    columns=2:30,
    smoothness=Modelica.Blocks.Types.Smoothness.ContinuousDerivative)
    "Data reader"
equation
  connect(modTim.y, conTim.modTim)
  connect(conTim.calTim, datRea.u)
  connect(datRea.y[15], from_deg.u)
  connect(from_deg.y, cheWinDir.nIn)
end CheckWindDirection;


Test model for wind speed check. This example tests the model that constrains the wind speed.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model CheckWindSpeed "Test model for wind speed check"
  extends Modelica.Icons.Example;
  AixLib.Utilities.Time.ModelTime modTim
    "Block that outputs simulation time"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.CheckWindSpeed cheWinSpe
    "Block that constrains the wind speed"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertTime conTim(
    weaDatStaTim=0,
    weaDatEndTim=31536000)
    "Block that converts time"
protected
  Modelica.Blocks.Tables.CombiTable1Ds datRea(
    tableOnFile=true,
    tableName="tab1",
    fileName=Modelica.Utilities.Files.loadResource(
       Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos")),
    columns=2:30,
    smoothness=Modelica.Blocks.Types.Smoothness.ContinuousDerivative)
    "Data reader"
equation
  connect(datRea.y[16], cheWinSpe.winSpeIn)
  connect(modTim.y, conTim.modTim)
  connect(conTim.calTim, datRea.u)
end CheckWindSpeed;


Test model for ConvertRadiation. This example tests the model that converts radiation.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model ConvertRadiation "Test model for ConvertRadiation"
  extends Modelica.Icons.Example;
  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertRadiation
    conGloRad "Convert units for global horizontal radiation"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertRadiation
    conDifRad "Convert units for diffuse horizontal radiation"
  AixLib.Utilities.Time.ModelTime modTim
    "Block that outputs simulation time"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertTime timCon(
    weaDatStaTim=0,
    weaDatEndTim=31536000)
    "Convert simmulation time to calendar time"
protected
  Modelica.Blocks.Tables.CombiTable1Ds datRea(
    tableOnFile=true,
    tableName="tab1",
    fileName=Modelica.Utilities.Files.loadResource(
       Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos")),
    columns=2:30,
    smoothness=Modelica.Blocks.Types.Smoothness.ContinuousDerivative)
    "Data reader"

equation
  connect(timCon.calTim, datRea.u)
  connect(datRea.y[8], conGloRad.HIn)
  connect(datRea.y[10], conDifRad.HIn)
  connect(modTim.y, timCon.modTim)
end ConvertRadiation;


Test model for converting relative humidity and checking its validity. This example tests the model that converts relative humidity.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model ConvertRelativeHumidity
  "Test model for converting relative humidity and checking its validity"
  extends Modelica.Icons.Example;
  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertRelativeHumidity conRelHum
    "Block that converts relative humidity"
  AixLib.Utilities.Time.ModelTime modTim
    "Block that outputs simulation time"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertTime conTim(
    weaDatStaTim=0,
    weaDatEndTim=31536000)
    "Block that converts time"
protected
  Modelica.Blocks.Tables.CombiTable1Ds datRea(
    tableOnFile=true,
    tableName="tab1",
    fileName=Modelica.Utilities.Files.loadResource(
       Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos")),
    columns=2:30,
    smoothness=Modelica.Blocks.Types.Smoothness.ContinuousDerivative)
    "Data reader"

equation
  connect(datRea.y[3], conRelHum.relHumIn)
  connect(modTim.y, conTim.modTim)
  connect(conTim.calTim, datRea.u)
end ConvertRelativeHumidity;


Test model for converting time. This example tests the model that converts time.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model ConvertTime "Test model for converting time"
  extends Modelica.Icons.Example;
  AixLib.Utilities.Time.ModelTime modTim
    "Block that outputs simulation time"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertTime conTim(
    weaDatStaTim=0,
    weaDatEndTim=31536000)
    "Block that converts time"
equation
  connect(modTim.y, conTim.modTim)
end ConvertTime;


Validation of time conversion for negative start time. This example tests the model that converts time for negative start time.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model ConvertTimeNegativeStart
  "Validation of time conversion for negative start time"
  extends AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples.ConvertTime;
end ConvertTimeNegativeStart;


Validation of time conversion for positive start time. This example tests the model that converts time for positive start time.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model ConvertTimePositiveStart
  "Validation of time conversion for positive start time"
  extends AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples.ConvertTime;
end ConvertTimePositiveStart;


Test model for equation of time. This example tests the model that computes the equation of time.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model EquationOfTime "Test model for equation of time"
  extends Modelica.Icons.Example;
  Utilities.Time.ModelTime modTim "Block that outputs simulation time"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.EquationOfTime eqnTim
    "Block that computes the equation of time"
equation
  connect(modTim.y, eqnTim.nDay)
end EquationOfTime;


Test model to get the absolute path of a URI. This model tests the function that gets the absolute path of a URI.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model GetAbsolutePath "Test model to get the absolute path of a URI"
  extends Modelica.Icons.Example;
  parameter String f = Modelica.Utilities.Files.loadResource("modelica://AixLib/package.mo")
   "Name of a file that exists";
  parameter String fAbs=
    AixLib.BoundaryConditions.WeatherData.BaseClasses.getAbsolutePath(uri=f)
    "Absolute path of f";
  final parameter Integer dummy = 1
    "Dummy variable, used to have a result as needed for the unit tests";
initial algorithm
  Modelica.Utilities.Streams.print("Absolute path = " + fAbs);
end GetAbsolutePath;


Test model to get Altitude of TMY3. This example tests getting the location altitude of a TMY3 weather data file.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model GetAltitudeTMY3 "Test model to get Altitude of TMY3"
  extends Modelica.Icons.Example;
  parameter String filNam = Modelica.Utilities.Files.loadResource(
  "modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos")
    "Name of weather data file";
  parameter Modelica.Units.SI.Length alt = AixLib.BoundaryConditions.WeatherData.BaseClasses.getAltitudeLocationTMY3(
  filNam) "Altitude of TMY3 location";
end GetAltitudeTMY3;


Test model to get header element. This example tests getting the header of the TMY3 weather data file.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model GetHeaderElement "Test model to get header element"
  extends Modelica.Icons.Example;
  parameter Modelica.Units.SI.Angle longitude(fixed=false, displayUnit="deg")
    "Longitude";
  parameter Modelica.Units.SI.Angle latitude(fixed=false, displayUnit="deg")
    "Latitude";
  parameter Modelica.Units.SI.Time timeZone(fixed=false, displayUnit="h")
    "Time zone";

  parameter String filNam = Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos")
    "Name of weather data file";

  final parameter String absFilNam = AixLib.BoundaryConditions.WeatherData.BaseClasses.getAbsolutePath(filNam)
    "Absolute path of the file";
initial equation
  longitude = AixLib.BoundaryConditions.WeatherData.BaseClasses.getLongitudeTMY3(
    filNam=absFilNam);
  latitude = AixLib.BoundaryConditions.WeatherData.BaseClasses.getLatitudeTMY3(
    filNam=absFilNam);
  timeZone = AixLib.BoundaryConditions.WeatherData.BaseClasses.getTimeZoneTMY3(
    filNam=absFilNam);
  assert(abs(longitude*180/Modelica.Constants.pi+87.92) < 1,
      "Error when parsing longitude, longitude = " + String(longitude));
  assert(abs(latitude*180/Modelica.Constants.pi-41.98) < 1,
      "Error when parsing latitude, latitude = " + String(latitude));
  assert(abs(timeZone+6*3600) < 1, "Error when parsing time zone, timeZone = "
    + String(timeZone));
end GetHeaderElement;


Test model to get the time span of a weather file. This example tests getting the time span of a TMY3 weather data file.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model GetTimeSpanTMY3 "Test model to get the time span of a weather file"
  extends Modelica.Icons.Example;

  parameter String filNam = Modelica.Utilities.Files.loadResource(
  "modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos")
    "Name of weather data file";
  parameter String tabNam = "tab1" "Name of table on weather file";

  parameter Modelica.Units.SI.Time[2] timeSpan(each fixed=false)
    "Start time, end time of weather data";

protected
  constant Modelica.Units.SI.Time endTim=365*24*3600.;
  constant Modelica.Units.SI.Time staTim=0.;

initial equation
  timeSpan = AixLib.BoundaryConditions.WeatherData.BaseClasses.getTimeSpanTMY3(
  filNam, tabNam);

  assert(abs(timeSpan[2]-endTim) < 1E-5  and abs(timeSpan[1]-staTim) < 1E-5,
      "Error in weather file, start time " + String(timeSpan[1]) +
      " and end time " + String(timeSpan[2]) +
      ", but expected " + String(staTim) + " and " + String(endTim) + ".");
end GetTimeSpanTMY3;


Test model to get the time span of a weather file with a long header line. This example tests getting the time span of a TMY3 weather data file with a long line in the
header of the weather file.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model GetTimeSpanTMY3LongHeader
  "Test model to get the time span of a weather file with a long header line"
  extends Modelica.Icons.Example;

  parameter String filNam=Modelica.Utilities.Files.loadResource(
  "modelica://AixLib/Resources/Data/BoundaryConditions/WeatherData/BaseClasses/Examples/weatherWithLongHeader.mos")
   "Name of weather data file";

  final parameter Modelica.Units.SI.Time[2] timeSpan=
      AixLib.BoundaryConditions.WeatherData.BaseClasses.getTimeSpanTMY3(filNam,
      "tab1") "Start time, end time of weather data";

initial equation
  assert(abs(timeSpan[2]-14400) < 0.1, "Error in getting time span.");
end GetTimeSpanTMY3LongHeader;


Test model to get time span of a weather file, start time is non zero. This example tests getting time span of a TMY3 weather data file that
starts at a non-zero time.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model GetTimeSpanTMY3_NonzeroStart
  "Test model to get time span of a weather file, start time is non zero"
  extends Modelica.Icons.Example;
  extends AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples.GetTimeSpanTMY3(
    filNam = Modelica.Utilities.Files.loadResource(
 "modelica://AixLib/Resources/Data/BoundaryConditions/WeatherData/Validation/DecemberToJanuary.mos"),
    staTim = 30992400,
    endTim = 31863600);
end GetTimeSpanTMY3_NonzeroStart;


Test model for ceiling height check. This example tests the model that constrains the ceiling height.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model LimitMin "Test model for ceiling height check"
  extends Modelica.Icons.Example;
  AixLib.Utilities.Time.ModelTime modTim
  AixLib.BoundaryConditions.WeatherData.BaseClasses.LimiterCeilingHeight limCeiHei
    "Block that constrains the ceiling height"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.ConvertTime conTim(
    weaDatStaTim=0,
    weaDatEndTim=31536000)
    "Block that converts time"
protected
  Modelica.Blocks.Tables.CombiTable1Ds datRea(
    tableOnFile=true,
    tableName="tab1",
    fileName=Modelica.Utilities.Files.loadResource(
       Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos")),
    columns=2:30,
    smoothness=Modelica.Blocks.Types.Smoothness.ContinuousDerivative)
    "Data reader"
equation
  connect(datRea.y[20],limCeiHei.u)
  connect(modTim.y, conTim.modTim)
  connect(conTim.calTim, datRea.u)
end LimitMin;


Test model for calculate local civil time. This example tests the model that computes the local civil time.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model LocalCivilTime "Test model for calculate local civil time"
  extends Modelica.Icons.Example;
  AixLib.Utilities.Time.ModelTime modTim
    "Block that outputs the model time"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.LocalCivilTime locTim(
      timZon=-21600, lon=-1.5293932423067)
equation
  connect(modTim.y, locTim.cloTim)
end LocalCivilTime;


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


Test model for solar time. This example tests the model that computes the solar time.

within AixLib.BoundaryConditions.WeatherData.BaseClasses.Examples;
model SolarTime "Test model for solar time"
  extends Modelica.Icons.Example;
  Utilities.Time.ModelTime modTim "Block that outputs simulation time"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.LocalCivilTime locTim(
      timZon=-21600,
      lon=-1.7039261675061) "Block that computes the local civil time"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.SolarTime solTim
    "Block that computes the solar time"
  AixLib.BoundaryConditions.WeatherData.BaseClasses.EquationOfTime eqnTim
    "Block that computes the equation of time"
equation
  connect(modTim.y, locTim.cloTim)
  connect(locTim.locTim, solTim.locTim)
  connect(modTim.y, eqnTim.nDay)
  connect(eqnTim.eqnTim, solTim.equTim)
end SolarTime;


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


Test model for reading weather data. This model tests the TMY3 data reader.
The instance weaDat obtains all weather data from the weather file,
whereas the instance weaDatInpCon obtains the global horizontal and
the diffuse horizontal solar radiation from its input connectors.

within AixLib.BoundaryConditions.WeatherData.Examples;
model ReaderTMY3 "Test model for reading weather data"
  extends Modelica.Icons.Example;
  AixLib.BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    filNam=Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"))
    "Weather data reader"
  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 input connector"
  Modelica.Blocks.Sources.Constant HDifHor(k=0) "Diffuse horizontal radiation"
  Modelica.Blocks.Sources.Constant HGloHor(k=0) "Horizontal global radiation"
equation
  connect(HGloHor.y, weaDatInpCon.HGloHor_in)
  connect(HDifHor.y, weaDatInpCon.HDifHor_in)
end ReaderTMY3;


within AixLib.BoundaryConditions.WeatherData;
package Old "Old weather data reader of AixLib for TRY data"
end Old;


Complex weather model.

within AixLib.BoundaryConditions.WeatherData.Old.WeatherTRY;
model Weather "Complex weather model"
  parameter Modelica.Units.NonSI.Angle_deg Latitude=49.5 "latitude of location"
  parameter Modelica.Units.NonSI.Angle_deg Longitude=8.5
    "longitude of location"
  parameter Modelica.Units.NonSI.Time_hour DiffWeatherDataTime=1
    "difference between weather data time and UTC, e.g. +1 for CET"
  parameter Real GroundReflection = 0.2 "ground reflection coefficient"
  parameter String tableName = "wetter"
    "table name on file or in function usertab"
  parameter String fileName = Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/TRY2010_12_Jahr_Modelica-Library.txt")
    "file where matrix is stored";Matlab files (*.mat)", caption = "Open file in which table is present")));
  parameter String formatTRY="TRY 2010/2011" "File format of test reference year (TRY)"
  parameter Real offset[:] = {0} "offsets of output signals"
  parameter Modelica.Blocks.Types.Smoothness smoothness = Modelica.Blocks.Types.Smoothness.LinearSegments
    "Smoothness of table interpolation"
  parameter Modelica.Blocks.Types.Extrapolation extrapolation = Modelica.Blocks.Types.Extrapolation.Periodic
    "Extrapolation of data outside the definition range"
  parameter Real startTime[1] = {0}
    "output = offset for time < startTime (same value for all columns)"

  replaceable model RadOnTiltedSurface =
      AixLib.BoundaryConditions.WeatherData.Old.WeatherTRY.RadiationOnTiltedSurface.RadOnTiltedSurf_Liu
    constrainedby
    AixLib.BoundaryConditions.WeatherData.Old.WeatherTRY.RadiationOnTiltedSurface.BaseClasses.PartialRadOnTiltedSurf
    "Model for calculating radiation on tilted surfaces"

  parameter
    DataBase.Weather.SurfaceOrientation.SurfaceOrientationBaseDataDefinition         SOD = DataBase.Weather.SurfaceOrientation.SurfaceOrientationData_N_E_S_W_Hor()
    "Surface orientation data"
  Utilities.Interfaces.SolarRad_out SolarRadiation_OrientedSurfaces[SOD.nSurfaces]
  parameter Boolean outOpt = true "Output individual vectors instead of one vector [-]"
  parameter Boolean Cloud_cover = false "Cloud cover [-] (TRY col 7)"
  parameter Boolean Wind_dir = false "Wind direction [deg] (TRY col 8)"
  parameter Boolean Wind_speed = false "Wind speed [m/s]  (TRY col 9)"
  parameter Boolean Air_temp = false "Air temperature [K] (TRY col 10)"
  parameter Boolean Air_press = false "Air pressure [Pa] (TRY col 11)"
  parameter Boolean Mass_frac = false
    "Mass fraction of water in dry air [kg/kg] (TRY col 12)"
  parameter Boolean Rel_hum = false "Realtive humidity of air [-] (TRY col 13)"
  parameter Boolean Sky_rad = false
    "Longwave sky radiation on horizontal [W/m2] (TRY col 18)"
  parameter Boolean Ter_rad = false
    "Longwave terrestric radiation from horizontal [W/m2] (TRY col 19)"
  BaseClasses.Sun Sun(
    Longitude=Longitude,
    Latitude=Latitude,
    DiffWeatherDataTime=DiffWeatherDataTime)
  RadOnTiltedSurface RadOnTiltedSurf[SOD.nSurfaces](each Latitude = Latitude, each GroundReflection = GroundReflection, Azimut = SOD.Azimut, Tilt = SOD.Tilt, each WeatherFormat=false)
  Modelica.Blocks.Sources.CombiTimeTable WeatherData(fileName = Modelica.Utilities.Files.loadResource(fileName), columns = columns, offset = offset, table = [0, 0; 1, 1], startTime = scalar(startTime), tableName = tableName, tableOnFile = tableName <> "NoName", smoothness = smoothness, extrapolation = extrapolation)
  Modelica.Blocks.Routing.DeMultiplex3 deMultiplex(n3 = 9)
  Modelica.Blocks.Interfaces.RealOutput WeatherDataVector[m] if outOpt == false and (Cloud_cover or Wind_dir or Wind_speed or Air_temp or Air_press or Mass_frac or Rel_hum or Sky_rad or Ter_rad)
  Modelica.Blocks.Interfaces.RealOutput CloudCover if Cloud_cover and outOpt "[0..8]"
  Modelica.Blocks.Interfaces.RealOutput WindDirection(unit = "deg") if Wind_dir and outOpt
    "in deg [0...360]"
  Modelica.Blocks.Interfaces.RealOutput WindSpeed(unit = "m/s") if Wind_speed and outOpt "in m/s"
  Modelica.Blocks.Interfaces.RealOutput AirTemp(unit = "K") if Air_temp and outOpt
    "in Kelvin"
  Modelica.Blocks.Interfaces.RealOutput AirPressure(unit = "Pa") if Air_press and outOpt "in Pa"
  Modelica.Blocks.Interfaces.RealOutput WaterInAir if Mass_frac and outOpt
    "in kg/kg"
  Modelica.Blocks.Interfaces.RealOutput RelHumidity if Rel_hum and outOpt
    "in percent"
  Modelica.Blocks.Interfaces.RealOutput SkyRadiation(unit = "W/m2") if Sky_rad and outOpt "in W/m2"
  Modelica.Blocks.Interfaces.RealOutput TerrestrialRadiation(unit = "W/m2") if Ter_rad and outOpt "in W/m2"
  Modelica.Blocks.Math.Gain hPa_to_Pa(k = 100) if Air_press
  Modelica.Blocks.Math.Gain percent_to_unit(k = 0.01) if Rel_hum
  Modelica.Blocks.Math.Gain g_to_kg(k = 0.001) if Mass_frac
  Modelica.Blocks.Math.UnitConversions.From_degC from_degC if Air_temp
protected
  parameter Integer m = BaseClasses.CalculateNrOfOutputs(Cloud_cover, Wind_dir, Wind_speed, Air_temp, Air_press, Mass_frac, Rel_hum, Sky_rad, Ter_rad)
    "Number of chosen output variables";
  parameter Integer[9] PosWV = BaseClasses.DeterminePositionsInWeatherVector(Cloud_cover, Wind_dir, Wind_speed, Air_temp, Air_press, Mass_frac, Rel_hum, Sky_rad, Ter_rad)
    "Positions Weather Vector";
  /*
  | 1: year / Modelica time | 2         | 3         | 4         | 5         | 6         | 7         | 8         | 9         | 10        | 11        | 12        | 13         | 14        | 15        | 16        | 17        | 18        | 19       | 20       |
  | 2010/2011               | RG        | IS        | MM        | DD        | HH        | N         | WR        | WG        | t         | p         | x         | RF         | W         | B         | D         | IK        | A         | E        | IL       |
  | 2015/2017               | RW        | HW        | MM        | DD        | HH        | t         | p         | WR        | WG        | N         | x         | RF         | B         | D         | A         | E         | IL        |          |          |
  */
  parameter Integer columns[:] = if formatTRY=="TRY 2015/2017" then {15, 14, 11, 9, 10, 7, 8, 12, 13, 16, 17} elseif formatTRY=="TRY 2010/2011" then {16, 15, 7, 8, 9, 10, 11, 12, 13, 18, 19} else fill(0, 11);
initial equation
  assert(SOD.nSurfaces == size(SOD.name, 1), "name has to have the nSurfaces Elements (see Surface orientation data in the Weather Model)");
  assert(SOD.nSurfaces == size(SOD.Azimut, 1), "Azimut has to have the nSurfaces Elements (see Surface orientation data in the Weather Model)");
  assert(SOD.nSurfaces == size(SOD.Tilt, 1), "Tilt has to have the nSurfaces Elements (see Surface orientation data in the Weather Model)");
equation
  // cloud cover
  if Cloud_cover then
    if outOpt == false then
      connect(WeatherDataVector[PosWV[1]], deMultiplex.y3[1]);
    else
      connect(CloudCover, deMultiplex.y3[1]);
    end if;
  end if;
  // wind direction
  if Wind_dir then
    if not outOpt then
      connect(WeatherDataVector[PosWV[2]], deMultiplex.y3[2]);
    else
      connect(WindDirection, deMultiplex.y3[2]);
    end if;
  end if;
  // wind speed
  if Wind_speed then
    if not outOpt then
      connect(WeatherDataVector[PosWV[3]], deMultiplex.y3[3]);
    else
      connect(WindSpeed, deMultiplex.y3[3]);
    end if;
  end if;
  // air temperature
  if Air_temp then
    if not outOpt then
      connect(deMultiplex.y3[4], from_degC.u);
      connect(WeatherDataVector[PosWV[4]], from_degC.y);
    else
      connect(deMultiplex.y3[4], from_degC.u);
      connect(AirTemp, from_degC.y);
    end if;
  end if;
  // air pressure, conversion from hPa to Pa
  if Air_press then
    if not outOpt then
      connect(deMultiplex.y3[5], hPa_to_Pa.u);
      connect(WeatherDataVector[PosWV[5]], hPa_to_Pa.y);
    else
      connect(deMultiplex.y3[5], hPa_to_Pa.u);
      connect(AirPressure, hPa_to_Pa.y);
    end if;
  end if;
  // mass fraction water in dry air, conversion from g/kg to kg/kg
  if Mass_frac then
    if not outOpt then
      connect(deMultiplex.y3[6], g_to_kg.u);
      connect(WeatherDataVector[PosWV[6]], g_to_kg.y);
    else
      connect(deMultiplex.y3[6], g_to_kg.u);
      connect(WaterInAir, g_to_kg.y);
    end if;
  end if;
  // rel. humidity, conversion from % to 0..1
  if Rel_hum then
    if not outOpt then
      connect(deMultiplex.y3[7], percent_to_unit.u);
      connect(WeatherDataVector[PosWV[7]], percent_to_unit.y);
    else
      connect(deMultiplex.y3[7], percent_to_unit.u);
      connect(RelHumidity, percent_to_unit.y);
    end if;
  end if;
  // longwave sky radiation
  if Sky_rad then
    if not outOpt then
      connect(WeatherDataVector[PosWV[8]], deMultiplex.y3[8]);
    else
      connect(SkyRadiation, deMultiplex.y3[8]);
    end if;
  end if;
  // longwave terrestric radiation
  if Ter_rad then
    if not outOpt then
      connect(WeatherDataVector[PosWV[9]], deMultiplex.y3[9]);
    else
      connect(TerrestrialRadiation, deMultiplex.y3[9]);
    end if;
  end if;
  connect(WeatherData.y, deMultiplex.u)
 // Connecting n RadOnTiltedSurf
  for i in 1:SOD.nSurfaces loop
    connect(Sun.OutDayAngleSun, RadOnTiltedSurf[i].InDayAngleSun);
    connect(Sun.OutHourAngleSun, RadOnTiltedSurf[i].InHourAngleSun);
    connect(Sun.OutDeclinationSun, RadOnTiltedSurf[i].InDeclinationSun);
    connect(deMultiplex.y1[1], RadOnTiltedSurf[i].solarInput2);
    connect(deMultiplex.y2[1], RadOnTiltedSurf[i].solarInput1);
  end for;

  connect(RadOnTiltedSurf.OutTotalRadTilted, SolarRadiation_OrientedSurfaces)
end Weather;


Computes the sun's altitude of the current site.

within AixLib.BoundaryConditions.WeatherData.Old.WeatherTRY.BaseClasses;
model Sun "Computes the sun's altitude of the current site"

import Modelica.Units.Conversions.from_deg;
import Modelica.Units.Conversions.to_deg;
  parameter Real TimeCorrection = 0.5 "for TRY = 0.5, for TMY = 0";
  parameter Modelica.Units.NonSI.Angle_deg Latitude "latitude of location";
  parameter Modelica.Units.NonSI.Angle_deg Longitude "longitude of location in";
  parameter Modelica.Units.NonSI.Time_hour DiffWeatherDataTime
    "difference between local time and UTC, e.g. +1 for MET";
  parameter Real Diff_localStandardTime_WeatherDataTime = 0
    "difference between weather data time and the time of the local time zone";

  Real NumberOfDay;
  Real SolarTime;
  Real TimeEquation;
  Modelica.Blocks.Interfaces.RealOutput OutHourAngleSun
  Modelica.Blocks.Interfaces.RealOutput OutDeclinationSun
  Modelica.Blocks.Interfaces.RealOutput OutDayAngleSun

  Real DeclinationSun;
  Real HourAngleSun;
  Real DayAngleSun;
  Real StandardTime "the time of the standard time zone of the WeatherData";
equation
//calculation of the SolarTime (Duffie2006 chap. 1.5)

      // number of day: 1 = Jan 1st
      NumberOfDay = time/86400 + 1
    "NumberOfDay is calculated as float because then the variables that use NumberoOfDay in their calculation don't have to be interpolated between the time-steps. To get the integer value it has to be calculated with: NumberOfDay =floor(time/86400) + 1";

      // day angle of sun
      DayAngleSun = 360/365.25*(NumberOfDay - 1)
    "360 is an angle in degree and 365 is the number of days in one year. The earth rotation in one year is 360 degrees";

      OutDayAngleSun = DayAngleSun;

      // equation of time in hours - used to convert local time in solar time
      TimeEquation = 229.2*(0.000075+0.001868*cos(from_deg(DayAngleSun))-0.032077*sin(from_deg(DayAngleSun))-0.014615*cos(2*from_deg(DayAngleSun))-0.04089*sin(2*from_deg(DayAngleSun)))/60;
      StandardTime=mod(time/3600, 24);
      SolarTime =  StandardTime - TimeCorrection - (DiffWeatherDataTime + Diff_localStandardTime_WeatherDataTime) + 4 * (Longitude)/60 + TimeEquation
    "the difference between the UTC and the time standard is given by DiffWeatherDataTime and Diff_lokalStandardTime Longitude";

// hour angle of sun, first term calculates local time of day from continuous time signal
      HourAngleSun = (SolarTime-12) * 360/24 "HourAngleSun=0 deg means sun peak";
      if (HourAngleSun > 180) then
        OutHourAngleSun = HourAngleSun - 360;
      elseif (HourAngleSun < -180) then
        OutHourAngleSun = HourAngleSun + 360;
      else
        OutHourAngleSun = HourAngleSun;
      end if;

// declination of sun
      DeclinationSun = (to_deg(0.006918 - 0.399912 * cos(from_deg(DayAngleSun)) + 0.070257 * sin(from_deg(DayAngleSun)) - 0.006758 * cos(2*from_deg(DayAngleSun)) + 0.000907 * sin(2*from_deg(DayAngleSun)) - 0.002679 * cos(3*from_deg(DayAngleSun)) + 0.00148 * sin(3*from_deg(DayAngleSun))));
      OutDeclinationSun=DeclinationSun;
end Sun;


Lsodar.

within AixLib.BoundaryConditions.WeatherData.Old.WeatherTRY.Examples;
model WeatherModels
  extends Modelica.Icons.Example;
  BoundaryConditions.WeatherData.Old.WeatherTRY.Weather weather(
    Cloud_cover=true,
    Wind_dir=true,
    Wind_speed=true,
    Air_temp=true,
    Air_press=true,
    Mass_frac=true,
    Rel_hum=true,
    Sky_rad=true,
    Ter_rad=true)
equation
end WeatherModels;


Calculates solar radiation on tilted surfaces according to Liu.

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

import Modelica.Units.Conversions.from_deg;

  Real InBeamRadHor "beam irradiance on the horizontal surface";
  Real InDiffRadHor "diffuse irradiance on the horizontal surface";

  Real cos_theta;
  Real cos_theta_help;
  Real cos_theta_z;
  Real cos_theta_z_help;
  Real R;
  Real R_help;
  Real term;

equation
  // 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));
  cos_theta_z = (cos_theta_z_help + abs(cos_theta_z_help))/2;

  // calculation of cos_theta [Duffie/Beckman, p.15], cos_theta is manually cut at 0 (no neg. values)
  term = cos(from_deg(InDeclinationSun))*sin(from_deg(Tilt))*sin(from_deg(
    Azimut))*sin(from_deg(InHourAngleSun));
  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)) + term;
  cos_theta = (cos_theta_help + abs(cos_theta_help))/2;

  // calculation of R factor [Duffie/Beckman, p.25], due to numerical problems (cos_theta_z in denominator)
  // R is manually set to 0 for theta_z >= 80 deg (-> 90 deg means sunset)
  if noEvent(cos_theta_z <= 0.17365) then
    R_help = cos_theta_z*cos_theta;

  else
    R_help = cos_theta/cos_theta_z;

  end if;

  R = R_help;

  // 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 total radiation on tilted surface according to model of Liu and Jordan
  // according to [Dissertation Nytsch-Geusen, p.98]
  OutTotalRadTilted.I = max(0, R*InBeamRadHor + 0.5*(1 + cos(from_deg(
    Tilt)))*InDiffRadHor + GroundReflection*(InBeamRadHor + InDiffRadHor)
    *((1 - cos(from_deg(Tilt)))/2));

  // Setting the outputs of direct. diffuse and ground reflection radiation on tilted surface and the angle of incidence
  OutTotalRadTilted.I_dir = R*InBeamRadHor;
  OutTotalRadTilted.I_diff = 0.5*(1 + cos(from_deg(Tilt)))*InDiffRadHor;
  OutTotalRadTilted.I_gr = GroundReflection*(InBeamRadHor + InDiffRadHor)*((1 - cos(from_deg(Tilt)))/2);

  OutTotalRadTilted.AOI = Modelica.Math.acos(cos_theta); // in rad
end RadOnTiltedSurf_Liu;


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;