Ideal sensor for flow velocity. This model outputs the flow velocity flowing from
port_a to port_b.

within AixLib.Fluid.Sensors;
model Velocity "Ideal sensor for flow velocity"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor;
  extends Modelica.Icons.RoundSensor;
  parameter Medium.Density
    d_start=Medium.density(Medium.setState_pTX(p_start, T_start, X_start))
    "Initial or guess value of density"
  parameter Modelica.Units.SI.Temperature T_start=Medium.T_default
    "Temperature used to compute d_start"
  parameter Modelica.Units.SI.Pressure p_start=Medium.p_default
    "Pressure used to compute d_start"
  parameter Modelica.Units.SI.MassFraction X_start[Medium.nX]=Medium.X_default
    "Mass fraction used to compute d_start"

  parameter Modelica.Units.SI.Area A "Cross sectional area of flow channel";
  Modelica.Units.SI.VolumeFlowRate V_flow
    "Volume flow rate from port_a to port_b";
  Modelica.Blocks.Interfaces.RealOutput v(final quantity="Velocity",
                                          final unit="m/s")
    "Flow velocity from port_a to port_b"
protected
  Medium.Density dMed(start=d_start)
    "Medium temperature to which the sensor is exposed";

  Medium.Density d_a_inflow(start=d_start)
    "Density of inflowing fluid at port_a";
  Medium.Density d_b_inflow(start=d_start)
    "Density of inflowing fluid at port_b, or rho_a_inflow if uni-directional flow";
  Medium.Density d(start=d_start) "Density of the passing fluid";
initial equation
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(d) = 0;
     elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      d = d_start;
    end if;
  end if;
equation
  if allowFlowReversal then
     d_a_inflow = Medium.density(state=
                    Medium.setState_phX(p=port_b.p, h=port_b.h_outflow, X=port_b.Xi_outflow));
     d_b_inflow = Medium.density(state=
                    Medium.setState_phX(p=port_a.p, h=port_a.h_outflow, X=port_a.Xi_outflow));
     dMed = Modelica.Fluid.Utilities.regStep(
              x=port_a.m_flow,
              y1=d_a_inflow,
              y2=d_b_inflow,
              x_small=m_flow_small);
  else
     dMed = Medium.density(state=Medium.setState_phX(
              p=port_b.p,
              h=port_b.h_outflow,
              X=port_b.Xi_outflow));
     d_a_inflow = dMed;
     d_b_inflow = dMed;
  end if;
  // Output signal of density sensor that is used to compute
  // the volume flow rate
  if dynamic then
    der(d) = (dMed-d)*k*tauInv;
  else
    d = dMed;
  end if;
  // Volume flow rate
  V_flow = port_a.m_flow/d;
  // Flow velocity
  v = V_flow / A;
end Velocity;


Ideal sensor for volume flow rate. This model outputs the volume flow rate flowing from
port_a to port_b.
The sensor is ideal, i.e., it does not influence the fluid.
If the parameter tau is non-zero, then the measured
density that is used to convert the mass flow rate into
volumetric flow rate is computed using a first order differential equation.
Setting tau=0 is not recommend. See

AixLib.Fluid.Sensors.UsersGuide for an explanation.

within AixLib.Fluid.Sensors;
model VolumeFlowRate "Ideal sensor for volume flow rate"
  extends AixLib.Fluid.Sensors.BaseClasses.PartialDynamicFlowSensor(
    tau=0);
  extends Modelica.Icons.RoundSensor;
  parameter Medium.Density
    d_start=Medium.density(Medium.setState_pTX(p_start, T_start, X_start))
    "Initial or guess value of density"
  parameter Modelica.Units.SI.Temperature T_start=Medium.T_default
    "Temperature used to compute d_start"
  parameter Modelica.Units.SI.Pressure p_start=Medium.p_default
    "Pressure used to compute d_start"
  parameter Modelica.Units.SI.MassFraction X_start[Medium.nX]=Medium.X_default
    "Mass fraction used to compute d_start"
  Modelica.Blocks.Interfaces.RealOutput V_flow(final quantity="VolumeFlowRate",
                                               final unit="m3/s")
    "Volume flow rate from port_a to port_b"
protected
  Medium.Density dMed(start=d_start)
    "Medium density to which the sensor is exposed";

  Medium.Density d_a_inflow(start=d_start)
    "Density of inflowing fluid at port_a";
  Medium.Density d_b_inflow(start=d_start)
    "Density of inflowing fluid at port_b, or rho_a_inflow if uni-directional flow";
  Medium.Density d(start=d_start) "Density of the passing fluid";
initial equation
  if dynamic then
    if initType == Modelica.Blocks.Types.Init.SteadyState then
      der(d) = 0;
     elseif initType == Modelica.Blocks.Types.Init.InitialState or
           initType == Modelica.Blocks.Types.Init.InitialOutput then
      d = d_start;
    end if;
  end if;
equation
  if allowFlowReversal then
     d_a_inflow = Medium.density(state=
                    Medium.setState_phX(p=port_b.p, h=port_b.h_outflow, X=port_b.Xi_outflow));
     d_b_inflow = Medium.density(state=
                    Medium.setState_phX(p=port_a.p, h=port_a.h_outflow, X=port_a.Xi_outflow));
     dMed = Modelica.Fluid.Utilities.regStep(
              x=port_a.m_flow,
              y1=d_a_inflow,
              y2=d_b_inflow,
              x_small=m_flow_small);
  else
     dMed = Medium.density(state=Medium.setState_phX(
              p=port_b.p,
              h=port_b.h_outflow,
              X=port_b.Xi_outflow));
     d_a_inflow = dMed;
     d_b_inflow = dMed;
  end if;
  // Output signal of density sensor that is used to compute
  // the volume flow rate
  if dynamic then
    der(d) = (dMed-d)*k*tauInv;
  else
    d = dMed;
  end if;
  // Volume flow rate
  V_flow = port_a.m_flow/d;
end VolumeFlowRate;


within AixLib.Fluid.Sensors;
package BaseClasses "Package with base classes for AixLib.Fluid.Sensors"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


within AixLib.Fluid.Sensors.BaseClasses;
partial model PartialAbsoluteSensor
  "Partial component to model a sensor that measures a potential variable"

  replaceable package Medium=Modelica.Media.Interfaces.PartialMedium
    "Medium in the sensor"

  parameter Boolean warnAboutOnePortConnection = true
   "Set to false to suppress warning about potential numerical issues, see AixLib.Fluid.Sensors.UsersGuide for more information"
  Modelica.Fluid.Interfaces.FluidPort_a port(redeclare package Medium=Medium, m_flow(min=0))

protected
  parameter String instanceName = getInstanceName() "Name of the instance";
initial equation
  assert(not warnAboutOnePortConnection,
  "Sensor " + instanceName + " can lead to numerical problems if connected to a scalar fluid port.
  Only connect it to a vectorized fluid port, such as used in 'AixLib.Fluid.MixingVolumes`.
  See AixLib.Fluid.Sensors.UsersGuide for more information.
  To disable this warning, set 'warnAboutOnePortConnection = false' in " + instanceName + ".",
  level=AssertionLevel.warning);
equation
  port.m_flow = 0;
  port.h_outflow = 0;
  port.Xi_outflow = zeros(Medium.nXi);
  port.C_outflow = zeros(Medium.nC);
end PartialAbsoluteSensor;


Partial component to model sensors that measure flow properties using a dynamic model. Partial component to model a sensor that measures any intensive properties
of a flow, e.g., to get temperature or density in the flow
between fluid connectors.

within AixLib.Fluid.Sensors.BaseClasses;
partial model PartialDynamicFlowSensor
  "Partial component to model sensors that measure flow properties using a dynamic model"
  extends PartialFlowSensor;

  parameter Modelica.Units.SI.Time tau(min=0) = 1
    "Time constant at nominal flow rate (use tau=0 for steady-state sensor, but see user guide for potential problems)";
  parameter Modelica.Blocks.Types.Init initType = Modelica.Blocks.Types.Init.InitialState
    "Type of initialization (InitialState and InitialOutput are identical)"
protected
  Real k(start=1)
    "Gain to take flow rate into account for sensor time constant";
  final parameter Boolean dynamic = tau > 1E-10 or tau < -1E-10
    "Flag, true if the sensor is a dynamic sensor"
  Real mNor_flow "Normalized mass flow rate";
  final parameter Real tauInv(final unit="s-1")= if dynamic then 1/tau else 0
    "Inverse of tau";
equation
  if dynamic then
    mNor_flow = port_a.m_flow/m_flow_nominal;
    k = Modelica.Fluid.Utilities.regStep(x=port_a.m_flow,
                                         y1= mNor_flow,
                                         y2=-mNor_flow,
                                         x_small=m_flow_small);
  else
    mNor_flow = 1;
    k = 1;
  end if;
end PartialDynamicFlowSensor;


Partial component to model sensors that measure flow properties. Partial component to model a sensor.
The sensor is ideal. It does not influence mass, energy,
species or substance balance, and it has no flow friction.

within AixLib.Fluid.Sensors.BaseClasses;
partial model PartialFlowSensor
  "Partial component to model sensors that measure flow properties"
  extends AixLib.Fluid.Interfaces.PartialTwoPort;
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal(min=0)
    "Nominal mass flow rate, used for regularization near zero flow"
  parameter Modelica.Units.SI.MassFlowRate m_flow_small(min=0) = 1E-4*
    m_flow_nominal
    "For bi-directional flow, temperature is regularized in the region |m_flow| < m_flow_small (m_flow_small > 0 required)"
equation
  // mass balance
  port_b.m_flow = -port_a.m_flow;
  // momentum equation (no pressure loss)
  port_a.p = port_b.p;
  // isenthalpic state transformation (no storage and no loss of energy)
  port_a.h_outflow = if allowFlowReversal then inStream(port_b.h_outflow) else Medium.h_default;
  port_b.h_outflow = inStream(port_a.h_outflow);
  port_a.Xi_outflow = if allowFlowReversal then inStream(port_b.Xi_outflow) else Medium.X_default[1:Medium.nXi];
  port_b.Xi_outflow = inStream(port_a.Xi_outflow);
  port_a.C_outflow = if allowFlowReversal then inStream(port_b.C_outflow) else zeros(Medium.nC);
  port_b.C_outflow = inStream(port_a.C_outflow);
end PartialFlowSensor;


within AixLib.Fluid.Sensors;
package Conversions "Package with conversions for sensor models"
  extends Modelica.Icons.Package;
end Conversions;


Conversion from mass fraction to volume fraction. This component converts mass fraction to volume fraction for an ideal gas.
The default value for the parameter MMBul
assumes that the bulk medium is air.
The model assumes that the concentration
of the measured substance is small enough to be neglected in the molar mass
of the mixture.

within AixLib.Fluid.Sensors.Conversions;
model To_VolumeFraction "Conversion from mass fraction to volume fraction"

  parameter Modelica.Units.SI.MolarMass MMMea
    "Molar mass of measured substance";
  parameter Modelica.Units.SI.MolarMass MMBul=Modelica.Media.IdealGases.Common.SingleGasesData.Air.MM
    "Molar mass of bulk medium";

  Modelica.Blocks.Interfaces.RealInput m "Mass fraction"
  Modelica.Blocks.Interfaces.RealOutput V "Volume fraction"

protected
 parameter Real con = MMBul/MMMea
    "Conversion coefficient from mass to volume fraction";
equation
  V = con * m;
end To_VolumeFraction;


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


Example problem for conversion model

within AixLib.Fluid.Sensors.Conversions.Examples;
model To_VolumeFraction "Example problem for conversion model"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air(extraPropertiesNames={"CO2"});

  AixLib.Fluid.Sensors.Conversions.To_VolumeFraction conMasVolFra(MMMea=
        Modelica.Media.IdealGases.Common.SingleGasesData.CO2.MM)
    "Conversion from mass fraction CO2 to volume fraction CO2"
  Modelica.Blocks.Sources.Constant volFra(k=1000E-6)
    "Set point for volume fraction of 700PPM CO2"
  AixLib.Controls.Continuous.LimPID limPID(
    reverseActing=false,
    Ti=600,
    k=2,
    Td=1)
  AixLib.Fluid.Sensors.TraceSubstances senCO2(
    redeclare package Medium = Medium,
    substanceName="CO2",
    warnAboutOnePortConnection = false) "CO2 sensor"
  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    nPorts=4,
    redeclare package Medium = Medium,
    V=4*4*2.7,
    C_start={300E-6}*44.009544/28.9651159,
    m_flow_nominal=0.1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial) "Volume of air"
  AixLib.Fluid.Sources.TraceSubstancesFlowSource souCO2(
    use_m_flow_in=true,
    redeclare package Medium = Medium,
    nPorts=1) "CO2 source"
  Modelica.Blocks.Math.Gain CO2Per(k=15/1000/3600*1.977)
    "CO2 emission per person"
  AixLib.Fluid.Sources.MassFlowSource_T sou(
    use_m_flow_in=true,
    redeclare package Medium = Medium,
    C={300E-6}*44.009544/28.9651159,
    nPorts=1) "Source of fresh air with 300 PPM CO2"
  Modelica.Blocks.Math.Gain gai(k=50/3600) "Gain for mass flow rate"
  Modelica.Blocks.Sources.Constant nPeo(k=1) "Number of people"
  AixLib.Fluid.Sensors.VolumeFlowRate senVolFlo(
    redeclare package Medium = Medium,
    m_flow_nominal=0.1)
  Modelica.Blocks.Math.Gain norSet(k=1/1000E-6)
    "Normalization for set point (to scale control input)"
  Modelica.Blocks.Math.Gain norMea(k=1/1000E-6)
    "Normalization for measured concentration (to scale control input)"
  Modelica.Blocks.Math.Gain conVolFlo(k=3600, y(unit="m3/h"))
    "Conversion from m3/s to m3/h"
  AixLib.Fluid.Sensors.TraceSubstancesTwoPort senTraSubPeo(m_flow_nominal=0.1,
    redeclare package Medium = Medium,
    C_start=0,
    initType=Modelica.Blocks.Types.Init.InitialState)
    "CO2 concentration in absorptance from people"
  AixLib.Fluid.Sensors.TraceSubstancesTwoPort senTraSubFre(m_flow_nominal=0.1,
    redeclare package Medium = Medium,
    C_start=0,
    initType=Modelica.Blocks.Types.Init.InitialState)
    "CO2 concentration in fresh air supply"
  AixLib.Fluid.FixedResistances.PressureDrop res(
    redeclare package Medium = Medium,
    dp_nominal=10,
    m_flow_nominal=50/3600)
    "Pressure drop to decouple the state of the volume from the state of the boundary condition"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    C={300E-6}*44.009544/28.9651159,
    p=100000,
    nPorts=1) "Sink for exhaust air"

equation
  connect(souCO2.m_flow_in, CO2Per.y)
  connect(gai.y, sou.m_flow_in)
  connect(limPID.y, gai.u)
  connect(nPeo.y, CO2Per.u)
  connect(sou.ports[1], senVolFlo.port_a)
  connect(senCO2.C, conMasVolFra.m)
  connect(conMasVolFra.V, norMea.u)
  connect(norMea.y, limPID.u_m)
  connect(volFra.y, norSet.u)
  connect(norSet.y, limPID.u_s)
  connect(conVolFlo.u, senVolFlo.V_flow)
  connect(souCO2.ports[1], senTraSubPeo.port_a)
  connect(senTraSubPeo.port_b, vol.ports[1])
  connect(senVolFlo.port_b, senTraSubFre.port_a)
  connect(senTraSubFre.port_b, vol.ports[2])
  connect(vol.ports[3], senCO2.port)
  connect(vol.ports[4], res.port_a)
  connect(sin.ports[1], res.port_b)
end To_VolumeFraction;


Test model for the density sensor

within AixLib.Fluid.Sensors.Examples;
model Density "Test model for the density sensor"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air
    "Medium model";

  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    T=293.15,
    nPorts=1) "Flow boundary condition"
  AixLib.Fluid.Sources.MassFlowSource_T masFloRat(
    redeclare package Medium = Medium,
    use_T_in=false,
    X={0.02,0.98},
    nPorts=1,
    use_m_flow_in=true) "Flow boundary condition"

  AixLib.Fluid.Sensors.Density senDenVol(
    redeclare package Medium = Medium, warnAboutOnePortConnection=false)
                                       "Density sensor for the volume"
  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    redeclare package Medium = Medium,
    V=1,
    nPorts=3,
    m_flow_nominal=10,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial) "Volume"
  AixLib.Fluid.FixedResistances.PressureDrop dp(
    redeclare package Medium = Medium,
    m_flow_nominal=10,
    dp_nominal=200) "Flow resistance"
  AixLib.Fluid.Sensors.DensityTwoPort senDenFlo(
    redeclare package Medium = Medium,
    m_flow_nominal=10) "Density sensor for the flowing medium"
  Modelica.Blocks.Sources.Ramp ramp(
    height=-20,
    offset=10,
    duration=60)
equation
  connect(masFloRat.ports[1], vol.ports[1])
  connect(vol.ports[2], dp.port_a)
  connect(vol.ports[3], senDenVol.port)
  connect(dp.port_b, senDenFlo.port_a)
  connect(senDenFlo.port_b, sin.ports[1])
  connect(ramp.y, masFloRat.m_flow_in)
end Density;


within AixLib.Fluid.Sensors.Examples;
model EnthalpyFlowRate "Test model for the enthalpy flow rate sensors"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air "Medium model";

  AixLib.Fluid.Sensors.EnthalpyFlowRate senH_flow(
    redeclare package Medium = Medium,
    m_flow_nominal=2) "Enthalpy flow rate sensor"
  AixLib.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    nPorts=1,
    T=293.15) "Flow boundary condition"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=1,
    T=313.15) "Flow boundary condition"
  Modelica.Blocks.Sources.Ramp ramp(
    height=-2,
    offset=1,
    duration=60) "Input signal for mass flow rate"

  AixLib.Fluid.Sensors.SpecificEnthalpyTwoPort senH(
    redeclare package Medium = Medium,
    m_flow_nominal=2,
    tau=0)            "Specific enthalpy sensor"
  AixLib.Fluid.Sensors.MassFlowRate senM_flow(
    redeclare package Medium = Medium) "Mass flow rate sensor"
  Modelica.Blocks.Math.Add cheEqu(k2=-1)
    "Check for equality of the enthalpy flow rate computations"
  Modelica.Blocks.Math.Product pro "Computes the enthalphy flow rate"
equation
  connect(ramp.y, sou.m_flow_in)
  connect(sou.ports[1], senH_flow.port_a)
  connect(senH_flow.port_b, senH.port_a)
  connect(senH.port_b, senM_flow.port_a)
  connect(senM_flow.port_b, sin.ports[1])
  connect(senH_flow.H_flow,cheEqu. u1)
  connect(senH.h_out, pro.u1)
  connect(senM_flow.m_flow, pro.u2)
  connect(pro.y,cheEqu. u2)
end EnthalpyFlowRate;


within AixLib.Fluid.Sensors.Examples;
model EntropyFlowRate "Test model for the entropy flow rate sensors"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air "Medium model";

  AixLib.Fluid.Sensors.EntropyFlowRate  senS_flow(
    redeclare package Medium = Medium,
    m_flow_nominal=2) "Entropy flow rate sensor"
  AixLib.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    nPorts=1,
    T=293.15) "Flow boundary condition"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=1,
    T=313.15) "Flow boundary condition"
  Modelica.Blocks.Sources.Ramp ramp(
    height=-2,
    offset=1,
    duration=60) "Input signal for mass flow rate"

  AixLib.Fluid.Sensors.SpecificEntropyTwoPort  senS(
    redeclare package Medium = Medium,
    m_flow_nominal=2,
    tau=0)            "Specific entropy sensor"
  AixLib.Fluid.Sensors.MassFlowRate senM_flow(
    redeclare package Medium = Medium) "Mass flow rate sensor"
  AixLib.Utilities.Diagnostics.AssertEquality assEqu
    "Asserts the equality of the entropy flow rate computations"
  Modelica.Blocks.Math.Product pro "Computes the enthalphy flow rate"
equation
  connect(ramp.y, sou.m_flow_in)
  connect(sou.ports[1],senS_flow. port_a)
  connect(senS_flow.port_b,senS. port_a)
  connect(senS.port_b, senM_flow.port_a)
  connect(senM_flow.port_b, sin.ports[1])
  connect(senM_flow.m_flow, pro.u2)
  connect(pro.y, assEqu.u2)
  connect(senS_flow.S_flow, assEqu.u1)
  connect(senS.s, pro.u1)
end EntropyFlowRate;


Medium model

within AixLib.Fluid.Sensors.Examples;
model HeatMeter
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Water
    "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal = 10 "Nominal mass flow rate";

  Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    m_flow=m_flow_nominal,
    nPorts=1) "Mass flow source"
  Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=1)
    "Sink"
  Modelica.Thermal.HeatTransfer.Sources.FixedHeatFlow heaFloRat(Q_flow=
        m_flow_nominal*10*4200) "Heat flow rate"
  MixingVolumes.MixingVolume vol(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=m_flow_nominal,
    V=0.1,
    nPorts=2)
    "Mixing volume"
  TemperatureTwoPort senT(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal)
  AixLib.Fluid.Sensors.HeatMeter senHeaFlo(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal)
equation
  connect(sou.ports[1], senT.port_a)
  connect(senT.port_b, vol.ports[1])
  connect(vol.ports[2], senHeaFlo.port_a)
  connect(senHeaFlo.port_b, sin.ports[1])
  connect(senT.T, senHeaFlo.TExt)
  connect(heaFloRat.port, vol.heatPort)
end HeatMeter;


Test model for the mass fraction sensor

within AixLib.Fluid.Sensors.Examples;
model MassFraction "Test model for the mass fraction sensor"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air
    "Medium model";

  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=1,
    T=293.15) "Flow boundary condition"
  AixLib.Fluid.Sources.MassFlowSource_T masFloRat(
    redeclare package Medium = Medium,
    use_m_flow_in=false,
    use_T_in=false,
    X={0.02,0.98},
    m_flow=10,
    nPorts=1) "Flow boundary condition"

  AixLib.Fluid.Sensors.MassFraction senMasFra2(
    redeclare package Medium = Medium, warnAboutOnePortConnection=false)
                                       "Mass fraction sensor for the volume"
  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    redeclare package Medium = Medium,
    V=1,
    nPorts=3,
    m_flow_nominal=10,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial) "Volume"
  AixLib.Fluid.FixedResistances.PressureDrop dp(
    redeclare package Medium = Medium,
    m_flow_nominal=10,
    dp_nominal=200) "Flow resistance"
  AixLib.Fluid.Sensors.MassFractionTwoPort senMasFra1(
    redeclare package Medium = Medium, m_flow_nominal=10)
    "Mass fraction sensor for the flowing medium"
equation
  connect(dp.port_b, sin.ports[1])
  connect(masFloRat.ports[1], senMasFra1.port_a)
  connect(senMasFra1.port_b, vol.ports[1])
  connect(vol.ports[2], dp.port_a)
  connect(vol.ports[3], senMasFra2.port)
end MassFraction;


within AixLib.Fluid.Sensors.Examples;
model MoistAirEnthalpyFlowRate
  "Test model for the sensible and latent enthalpy flow rate sensors"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air;

  AixLib.Fluid.Sensors.EnthalpyFlowRate senH_flow(
    redeclare package Medium = Medium,
    m_flow_nominal=1) "Sensor for enthalpy flow rate"
  AixLib.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    nPorts=1,
    T=293.15) "Flow boundary condition"
  AixLib.Fluid.Sources.Boundary_pT sin(
     redeclare package Medium = Medium,
    nPorts=1,
    X={0.02,0.98},
    T=313.15) "Flow boundary condition"
  Modelica.Blocks.Sources.Ramp ramp(
    height=-2,
    offset=1,
    duration=60)

  AixLib.Fluid.Sensors.SpecificEnthalpyTwoPort senH(
    redeclare package Medium = Medium,
    m_flow_nominal=1) "Specific enthalpy sensor"
  AixLib.Fluid.Sensors.MassFlowRate senM_flow(
    redeclare package Medium = Medium) "Mass flow rate sensor"
  Modelica.Blocks.Math.Product pro "Product to compute enthalpy flow rate"
  AixLib.Fluid.Sensors.LatentEnthalpyFlowRate senHLat_flow(
    redeclare package Medium = Medium,
    m_flow_nominal=1) "Latent enthalpy flow rate sensor"
  AixLib.Fluid.Sensors.SensibleEnthalpyFlowRate senHSen_flow(
    redeclare package Medium = Medium, m_flow_nominal=1)
    "Sensible enthalpy flow rate sensor"
  Modelica.Blocks.Math.Add add
    "Outputs the sensible plus latent enthalpy flow rate"
equation
  connect(ramp.y, sou.m_flow_in)
  connect(sou.ports[1], senH_flow.port_a)
  connect(senH_flow.port_b, senH.port_a)
  connect(senH.port_b, senM_flow.port_a)
  connect(senH.h_out, pro.u1)
  connect(senM_flow.m_flow, pro.u2)
  connect(senHLat_flow.H_flow, add.u1)
  connect(senHSen_flow.H_flow, add.u2)
  connect(senM_flow.port_b, senHLat_flow.port_a)
  connect(senHLat_flow.port_b, senHSen_flow.port_a)
  connect(senHSen_flow.port_b, sin.ports[1])
end MoistAirEnthalpyFlowRate;


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


within AixLib.Fluid.Sensors.Examples;
model PPM "Test model for the extra property sensor outputting PPM"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air(extraPropertiesNames={"CO2"})
    "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=
    volDyn.V*senPPMTwoPort.tau*3*rho_default
    "Mass flow rate into and out of the volume";

  AixLib.Fluid.MixingVolumes.MixingVolume volDyn(
    redeclare package Medium = Medium,
    nPorts=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    V=1,
    use_C_flow=true,
    m_flow_nominal=m_flow_nominal) "Mixing volume with dynamics"
  AixLib.Fluid.Sources.MassFlowSource_T mSou(
    redeclare package Medium = Medium,
    nPorts=2,
    m_flow=m_flow_nominal) "Fresh air supply"

  AixLib.Fluid.Sensors.PPM senPPMVol(
    redeclare package Medium = Medium,
    warnAboutOnePortConnection=false)
    "PPM sensor for mixing volume"
  Modelica.Blocks.Sources.Constant CO2In(k=m_flow_nominal/1000)
    "CO2 mass flow rate entering mixing volume"
  AixLib.Fluid.Sensors.PPMTwoPort senPPMTwoPort(
    redeclare package Medium = Medium,
    allowFlowReversal=true,
    m_flow_nominal=m_flow_nominal) "PPM sensor"
  AixLib.Fluid.Sensors.PPM senPPMIn(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
    "PPM sensor for inlet"
  AixLib.Fluid.Sensors.PPMTwoPort senPPMNoRev(
    redeclare package Medium = Medium,
    allowFlowReversal=false,
    m_flow_nominal=m_flow_nominal) "PPM sensor without flow reversal disabled"
  AixLib.Fluid.Sensors.PPMTwoPort senPPMRev(
    redeclare package Medium = Medium,
    allowFlowReversal=true,
    m_flow_nominal=m_flow_nominal) "PPM sensor with flow in reverse direction"
  AixLib.Fluid.Sensors.PPMTwoPort senPPMSta(
    redeclare package Medium = Medium,
    allowFlowReversal=true,
    tau=0,
    m_flow_nominal=m_flow_nominal) "Static PPM sensor"
  AixLib.Fluid.MixingVolumes.MixingVolume volSte(
    redeclare package Medium = Medium,
    nPorts=3,
    V=1,
    use_C_flow=true,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Mixing volume without dynamics"
  AixLib.Fluid.Sensors.PPM senPPMVol2( redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
    "PPM sensor for mixing volume"
  AixLib.Fluid.Sources.MassFlowSource_T mSouSta(
    redeclare package Medium = Medium,
    nPorts=1,
    m_flow=m_flow_nominal) "Fresh air supply for steady state volume"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    nPorts=2) "Exhaust air"

  AixLib.Fluid.FixedResistances.PressureDrop dp(
     redeclare package Medium = Medium,
     dp_nominal = 200,
     m_flow_nominal = m_flow_nominal)
     "Pressure drop to decouple volume pressure from boundary pressure"

protected
  final parameter Medium.ThermodynamicState state_default = Medium.setState_pTX(
      T=Medium.T_default,
      p=Medium.p_default,
      X=Medium.X_default[1:Medium.nXi]) "Medium state at default values"
  // Density at medium default values, used to compute the size of control volumes
  final parameter Modelica.Units.SI.Density rho_default=Medium.density(
    state=state_default)
    "Density, used to compute fluid mass"

equation
  connect(mSou.ports[1], volDyn.ports[1])
  connect(CO2In.y, volDyn.C_flow[1])
  connect(senPPMVol.port, volDyn.ports[2])
  connect(senPPMIn.port, mSou.ports[2])
  connect(senPPMTwoPort.port_a, volDyn.ports[3])
  connect(senPPMNoRev.port_a, senPPMTwoPort.port_b)
  connect(senPPMRev.port_b, senPPMNoRev.port_b)
  connect(senPPMSta.port_a, senPPMRev.port_a)
  connect(CO2In.y,volSte. C_flow[1])
  connect(volSte.ports[1], senPPMVol2.port)
  connect(mSouSta.ports[1], volSte.ports[2])
  connect(sin.ports[1], volSte.ports[3])
  connect(dp.port_b, senPPMSta.port_b)
  connect(dp.port_a, sin.ports[2])
end PPM;


Test model for the pressure sensor

within AixLib.Fluid.Sensors.Examples;
model Pressure "Test model for the pressure sensor"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air
    "Medium model";

  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    T=293.15,
    nPorts=1) "Flow boundary condition"
  AixLib.Fluid.Sources.MassFlowSource_T masFloRat(
    redeclare package Medium = Medium,
    use_T_in=false,
    X={0.02,0.98},
    use_m_flow_in=true,
    nPorts=1) "Flow boundary condition"

  AixLib.Fluid.FixedResistances.PressureDrop dp(
    redeclare package Medium = Medium,
    m_flow_nominal=10,
    dp_nominal=200) "Flow resistance"
  Modelica.Blocks.Sources.Ramp ramp(
    height=-20,
    offset=10,
    duration=1)
  AixLib.Fluid.Sensors.Pressure senPre_a(redeclare package Medium = Medium)
    "Pressure sensor at resistance port a"
  AixLib.Fluid.Sensors.Pressure senPre_b(redeclare package Medium = Medium)
    "Pressure sensor at resistance port b"
  AixLib.Fluid.Sensors.RelativePressure senRelPre(
    redeclare package Medium = Medium) "Pressure difference across resistance"
equation
  connect(ramp.y, masFloRat.m_flow_in)
  connect(masFloRat.ports[1], dp.port_a)
  connect(dp.port_b, sin.ports[1])

  connect(senPre_a.port, dp.port_a)
  connect(senPre_b.port, dp.port_b)
  connect(senRelPre.port_a, dp.port_a)
  connect(senRelPre.port_b, dp.port_b)
end Pressure;


Test model for relative humidity sensor

within AixLib.Fluid.Sensors.Examples;
model RelativeHumidity "Test model for relative humidity sensor"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air "Medium model"

  AixLib.Fluid.Sources.Boundary_pT sin(
   redeclare package Medium = Medium,
    use_p_in=false,
    T=293.15,
    nPorts=1)
  AixLib.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    m_flow=1,
    use_T_in=true,
    use_m_flow_in=true,
    nPorts=2,
    use_Xi_in=true)
              "Flow boundary condition"
  Modelica.Blocks.Sources.Ramp TDryBul(
    height=10,
    offset=273.15 + 30,
    duration=120) "Dry bulb temperature"
  Modelica.Blocks.Sources.Ramp XHum(
    duration=1,
    height=(0.0133 - 0.0175),
    offset=0.0175) "Humidity concentration"

  AixLib.Fluid.Sensors.RelativeHumidity senRelHum(
    redeclare package Medium = Medium, warnAboutOnePortConnection=false)
    "Relative humidity of the flow source if the medium were outflowing"
    Modelica.Blocks.Sources.Ramp m_flow(
    height=-2,
    offset=1,
    duration=500) "Mass flow rate"
  AixLib.Fluid.Sensors.RelativeHumidityTwoPort relHum(
    redeclare package Medium = Medium, m_flow_nominal=1,
    initType=Modelica.Blocks.Types.Init.InitialState)
    "Relative humidity of the passing fluid"
equation
  connect(TDryBul.y, sou.T_in)
  connect(m_flow.y, sou.m_flow_in)
  connect(relHum.port_b, sin.ports[1])
  connect(senRelHum.port, sou.ports[1])
  connect(sou.ports[2], relHum.port_a)
  connect(sou.Xi_in[1], XHum.y)
end RelativeHumidity;


within AixLib.Fluid.Sensors.Examples;
model SpecificEnthalpy "Test model for the enthalpy flow rate sensors"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air "Medium model";

  AixLib.Fluid.Sources.MassFlowSource_h sou(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    use_h_in=false,
    nPorts=2) "Flow boundary condition"
  AixLib.Fluid.Sources.Boundary_ph sin(
    redeclare package Medium = Medium,
    use_h_in=false,
    h=20,
    nPorts=1) "Flow boundary condition"
  Modelica.Blocks.Sources.Ramp ramp(
    height=-2,
    offset=1,
    duration=60)

  AixLib.Fluid.Sensors.SpecificEnthalpy senFloSou(
    redeclare package Medium = Medium, warnAboutOnePortConnection=false)
                                       "Sensor at the flow source"
  AixLib.Fluid.Sensors.SpecificEnthalpyTwoPort senStr(
    redeclare package Medium = Medium,
    m_flow_nominal=2) "Sensor in the fluid stream"
equation
  connect(ramp.y, sou.m_flow_in)
  connect(sou.ports[1], senFloSou.port)
  connect(sou.ports[2], senStr.port_a)
  connect(senStr.port_b, sin.ports[1])
end SpecificEnthalpy;


within AixLib.Fluid.Sensors.Examples;
model SpecificEntropy "Test model for the entropy flow rate sensors"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air "Medium model";

  AixLib.Fluid.Sources.MassFlowSource_h sou(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    use_h_in=false,
    nPorts=2) "Flow boundary condition"
  AixLib.Fluid.Sources.Boundary_ph sin(
    redeclare package Medium = Medium,
    use_h_in=false,
    h=20,
    nPorts=1) "Flow boundary condition"
  Modelica.Blocks.Sources.Ramp ramp(
    height=-2,
    offset=1,
    duration=60)

  AixLib.Fluid.Sensors.SpecificEntropy senFloSou(
    redeclare package Medium = Medium, warnAboutOnePortConnection=false)
                                       "Sensor at the flow source"
  AixLib.Fluid.Sensors.SpecificEntropyTwoPort senStr(
    redeclare package Medium = Medium,
    m_flow_nominal=2) "Sensor in the fluid stream"
equation
  connect(ramp.y, sou.m_flow_in)
  connect(sou.ports[1], senFloSou.port)
  connect(sou.ports[2], senStr.port_a)
  connect(senStr.port_b, sin.ports[1])
end SpecificEntropy;


within AixLib.Fluid.Sensors.Examples;
model TemperatureDryBulb "Test model for the dry bulb temperature sensor"
  extends Modelica.Icons.Example;

 package Medium = AixLib.Media.Air "Medium model";
  AixLib.Fluid.Sources.Boundary_pT amb(
    redeclare package Medium = Medium,
    T=298.15,
    nPorts=1)
    "Ambient conditions, used to test the relative temperature sensor"
  AixLib.Fluid.Sources.MassFlowSource_T masFloRat(
    redeclare package Medium = Medium,
    use_T_in=true,
    use_m_flow_in=true,
    nPorts=1) "Flow boundary condition"
  Modelica.Blocks.Sources.Ramp TDryBul(
    height=10,
    duration=1,
    offset=273.15 + 30) "Dry bulb temperature"
  AixLib.Fluid.Sensors.TemperatureTwoPort temSteSta(
   redeclare package Medium = Medium,
   m_flow_nominal=2,
   tau=0) "Steady state temperature sensor"

    Modelica.Blocks.Sources.Pulse m_flow1(
    period=30,
    offset=0,
    amplitude=-1) "Mass flow rate"
  AixLib.Fluid.Sensors.TemperatureTwoPort temDyn(
    redeclare package Medium = Medium,
    m_flow_nominal=2,
    initType=Modelica.Blocks.Types.Init.InitialState,
    T_start=293.15) "Dynamic temperature sensor"
  RelativeTemperature senRelTem(redeclare package Medium = Medium)
    "Temperature difference sensor"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    T=293.15,
    nPorts=2) "Flow boundary condition"
  AixLib.Fluid.Sensors.TemperatureTwoPort temDynLoss(
    redeclare package Medium = Medium,
    m_flow_nominal=2,
    initType=Modelica.Blocks.Types.Init.InitialState,
    transferHeat=true,
    T_start=293.15,
    TAmb=293.15,
    tauHeaTra=30) "Dynamic temperature sensor with heat transfer"
  Modelica.Blocks.Math.Add add_m_flow
    "Add two pulse functions for mass flow rate"
    Modelica.Blocks.Sources.Pulse m_flow2(
    amplitude=1,
    offset=0,
    period=45) "Mass flow rate"
equation
  connect(TDryBul.y, masFloRat.T_in)
  connect(masFloRat.ports[1], temSteSta.port_a)
  connect(temSteSta.port_b, temDyn.port_a)
  connect(amb.ports[1], senRelTem.port_a)
  connect(temDyn.port_b, temDynLoss.port_a)
  connect(masFloRat.m_flow_in, add_m_flow.y)
  connect(add_m_flow.u1, m_flow1.y)
  connect(m_flow2.y, add_m_flow.u2)
  connect(sin.ports[1], temDynLoss.port_b)
  connect(sin.ports[2], senRelTem.port_b)
end TemperatureDryBulb;


within AixLib.Fluid.Sensors.Examples;
model TemperatureWetBulb "Test model for the wet bulb temperature sensor"
  extends Modelica.Icons.Example;
 package Medium = AixLib.Media.Air "Medium model"
    Modelica.Blocks.Sources.Ramp p(
    duration=1,
    offset=101325,
    height=250) "Pressure boundary condition"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium=Medium,
    use_p_in=true,
    nPorts=1,
    T=293.15) "Flow boundary condition"
  AixLib.Fluid.Sensors.TemperatureWetBulbTwoPort senWetBul(
    redeclare package Medium=Medium,
    m_flow_nominal=1,
    tau=0) "Wet bulb temperature sensor"
  AixLib.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    m_flow=1,
    use_T_in=true,
    nPorts=1,
    use_Xi_in=true)
              "Flow boundary condition"
  Modelica.Blocks.Sources.Ramp TDryBul(
    height=10,
    offset=273.15 + 30,
    duration=50) "Dry bulb temperature"
  Modelica.Blocks.Sources.Ramp XHum(
    height=(0.0133 - 0.0175),
    offset=0.0175,
    duration=50) "Humidity concentration"

equation
  connect(TDryBul.y, sou.T_in)
  connect(p.y, sin.p_in)
  connect(sou.ports[1], senWetBul.port_a)
  connect(senWetBul.port_b, sin.ports[1])
  connect(XHum.y, sou.Xi_in[1])
end TemperatureWetBulb;


within AixLib.Fluid.Sensors.Examples;
model TraceSubstances "Test model for the extra property sensor"
  extends Modelica.Icons.Example;
 package Medium = AixLib.Media.Air(extraPropertiesNames={"CO2"})
    "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=15*1.2/3600
    "Mass flow rate into and out of the volume";

  MixingVolumes.MixingVolume vol(
    redeclare package Medium = Medium,
    V=2*3*3,
    m_flow_nominal=1E-6,
    nPorts=5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial) "Mixing volume"
  Sources.TraceSubstancesFlowSource sou(
    redeclare package Medium = Medium,
    nPorts=2,
    use_m_flow_in=true) "CO2 mass flow source"
  Modelica.Blocks.Sources.Constant step(k=8.18E-6) "CO2 mass flow rate"
  AixLib.Fluid.Sensors.TraceSubstances senVol(
    redeclare package Medium = Medium, warnAboutOnePortConnection=false)
                                       "Sensor at volume"
  AixLib.Fluid.Sensors.TraceSubstances senSou(
    redeclare package Medium = Medium,
    warnAboutOnePortConnection=false,
    substanceName="CO2") "Sensor at source"
  Modelica.Blocks.Sources.Constant m_flow(k=m_flow_nominal)
    "Fresh air mass flow rate"
  AixLib.Fluid.Sources.MassFlowSource_T mSou(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    nPorts=1) "Fresh air supply"
  AixLib.Fluid.Sources.Boundary_pT mSin(
    redeclare package Medium = Medium, nPorts=1) "Exhaust air"
  AixLib.Fluid.Sensors.Conversions.To_VolumeFraction masFraSou(
    MMMea=Modelica.Media.IdealGases.Common.SingleGasesData.CO2.MM)
    "Conversion of mass ratio to volume ratio"
  AixLib.Fluid.Sensors.Conversions.To_VolumeFraction masFraVol(
    MMMea=Modelica.Media.IdealGases.Common.SingleGasesData.CO2.MM)
    "Conversion of mass ratio to volume ratio"
  AixLib.Fluid.Sensors.TraceSubstancesTwoPort senTraSub(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    tau=0) "Sensor at exhaust air"
  FixedResistances.PressureDrop res(
    redeclare package Medium = Medium,
    dp_nominal=10,
    m_flow_nominal=0.005,
    linearized=true)
  AixLib.Fluid.Sensors.TraceSubstancesTwoPort senTraSubNoFlorRev(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    allowFlowReversal=false,
    tau=0) "Sensor at exhaust air, configured to not allow flow reversal"
  AixLib.Fluid.Sensors.PPM senPPM(redeclare package Medium = Medium,
      warnAboutOnePortConnection=false)
    "PPM sensor"

equation
  connect(m_flow.y, mSou.m_flow_in)
  connect(senSou.C, masFraSou.m)
  connect(senVol.C, masFraVol.m)
  connect(sou.ports[1], senSou.port)
  connect(step.y, sou.m_flow_in)
  connect(sou.ports[2], vol.ports[1])
  connect(mSou.ports[1], vol.ports[2])
  connect(res.port_a, senTraSub.port_a)
  connect(res.port_b, vol.ports[3])
  connect(senVol.port, vol.ports[4])
  connect(senTraSubNoFlorRev.port_a, senTraSub.port_b)
  connect(senTraSubNoFlorRev.port_b, mSin.ports[1])
  connect(senPPM.port, vol.ports[5])
end TraceSubstances;


Test model for the flow velocity sensor

within AixLib.Fluid.Sensors.Examples;
model Velocity "Test model for the flow velocity sensor"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air "Medium model";

  parameter Modelica.Units.SI.Area A=0.1*0.1/4*Modelica.Constants.pi
    "Cross sectional area of flow channel";

  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    T=293.15,
    nPorts=1) "Flow boundary condition"
  AixLib.Fluid.Sources.MassFlowSource_T masFloRat(
    redeclare package Medium = Medium,
    use_T_in=false,
    X={0.02,0.98},
    use_m_flow_in=true,
    nPorts=1) "Flow boundary condition"

  Modelica.Blocks.Sources.Ramp ramp(
    duration=60,
    height=-0.2,
    offset=0.1) "Signal for mass flow rate"
  AixLib.Fluid.Sensors.Velocity senDyn(
    redeclare package Medium = Medium,
    m_flow_nominal=10,
    A=A) "Sensor configured to use a dynamic model for the density"
  AixLib.Fluid.Sensors.Velocity senSteSta(
    redeclare package Medium = Medium,
    m_flow_nominal=10,
    tau=0,
    A=A) "Sensor configured to use a steady-state model for the density"
equation
  connect(ramp.y, masFloRat.m_flow_in)
  connect(masFloRat.ports[1], senDyn.port_a)
  connect(senDyn.port_b, senSteSta.port_a)
  connect(senSteSta.port_b, sin.ports[1])
end Velocity;


within AixLib.Fluid.Sensors.Examples;
model VolumeFlowRate "Test model for the volume flow rate sensor"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air
    "Medium model";

  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    T=293.15,
    nPorts=1) "Flow boundary condition"
  AixLib.Fluid.Sources.MassFlowSource_T masFloRat(
    redeclare package Medium = Medium,
    use_T_in=false,
    X={0.02,0.98},
    use_m_flow_in=true,
    nPorts=1) "Flow boundary condition"

  Modelica.Blocks.Sources.Ramp ramp(
    height=-20,
    offset=10,
    duration=60)
  AixLib.Fluid.Sensors.VolumeFlowRate senDyn(
    redeclare package Medium = Medium,
    m_flow_nominal=10,
    tau=1)
    "Sensor configured to use a dynamic model for the density"
  AixLib.Fluid.Sensors.VolumeFlowRate senSteSta(
    redeclare package Medium = Medium,
    m_flow_nominal=10,
    tau=0) "Sensor configured to use a steady-state model for the density"
equation
  connect(ramp.y, masFloRat.m_flow_in)
  connect(masFloRat.ports[1], senDyn.port_a)
  connect(senDyn.port_b, senSteSta.port_a)
  connect(senSteSta.port_b, sin.ports[1])
end VolumeFlowRate;


Package with solar thermal models

within AixLib.Fluid.Solar;
package Thermal "Package with solar thermal models"
  extends Modelica.Icons.VariantsPackage;

end Thermal;


Model of a solar thermal panel. Overview

within AixLib.Fluid.Solar.Thermal;
model SolarThermal "Model of a solar thermal panel"
  extends AixLib.Fluid.BoilerCHP.BaseClasses.PartialHeatGenerator(
    vol(final V=volPip),
    a=pressureDropCoeff,
    dp_start=pressureDropCoeff*(m_flow_start /
      Medium.density(Medium.setState_pTX(
                       p_start,
                       T_start,
                       Medium.reference_X)))^2);

  parameter Modelica.Units.SI.Area A=2 "Area of solar thermal collector"
  parameter Modelica.Units.SI.Volume volPip "Water volume of piping"
  parameter Real pressureDropCoeff(unit="(Pa.s2)/m6") = 2500/(A*2.5e-5)^2
    "Pressure drop coefficient, delta_p[Pa] = PD * Q_flow[m^3/s]^2";
  parameter AixLib.DataBase.SolarThermal.SolarThermalBaseDataDefinition
    Collector = AixLib.DataBase.SolarThermal.SimpleAbsorber()
    "Properties of Solar Thermal Collector"
  Modelica.Blocks.Interfaces.RealInput T_air(
    quantity="ThermodynamicTemperature",
    unit="K") "Outdoor air temperature in K"
  Modelica.Blocks.Interfaces.RealInput Irradiation(
    quantity="Irradiance",
    unit="W/m2") "Solar irradiation on a horizontal plane in W/m2"
  AixLib.Fluid.Solar.Thermal.BaseClasses.SolarThermalEfficiency
    solarThermalEfficiency(Collector=Collector)
  Modelica.Blocks.Math.Gain convertRelHeatFlow2absHeatFlow(final k=A)
  Modelica.Blocks.Math.Add calcTempMean(k1=0.5, k2=0.5)
equation
  connect(T_air, solarThermalEfficiency.T_air)
  connect(solarThermalEfficiency.G, Irradiation)
  connect(convertRelHeatFlow2absHeatFlow.y, heater.Q_flow)
  connect(senTCold.T, calcTempMean.u1)
  connect(senTHot.T, calcTempMean.u2)
  connect(calcTempMean.y, solarThermalEfficiency.T_col)
  connect(solarThermalEfficiency.Q_flow, convertRelHeatFlow2absHeatFlow.u)
end SolarThermal;


within AixLib.Fluid.Solar.Thermal;
package BaseClasses "Package with base classes for solar thermal models"
  extends Modelica.Icons.BasesPackage;

end BaseClasses;


Calculates the efficiency of a solar thermal collector.

within AixLib.Fluid.Solar.Thermal.BaseClasses;
model SolarThermalEfficiency
  "Calculates the efficiency of a solar thermal collector"
  parameter AixLib.DataBase.SolarThermal.SolarThermalBaseDataDefinition
    Collector=AixLib.DataBase.SolarThermal.SimpleAbsorber()
    "Properties of Solar Thermal Collector"
  Modelica.Blocks.Interfaces.RealInput T_air(
    quantity="ThermodynamicTemperature",
    unit="K",
    displayUnit="degC") "Air temperature"
  Modelica.Blocks.Interfaces.RealInput G(quantity="Irradiance", unit="W/m2")
    "Global solar irradiation in W/m2"
  Modelica.Blocks.Interfaces.RealInput T_col(
    quantity="ThermodynamicTemperature",
    unit="K",
    displayUnit="degC") "Collector temperature"
  Modelica.Blocks.Interfaces.RealOutput Q_flow(quantity="HeatFlux", unit="W/m2")
    "Useful heat flow from solar collector in W/m2"
protected
  Modelica.Units.SI.Efficiency eta(max=Collector.eta_zero)
    "Efficiency of solar thermal collector";
  Modelica.Units.SI.TemperatureDifference dT
    "Temperature difference between collector and air in K";
equation
  dT = T_col - T_air;
  eta = max(0,
          min(Collector.eta_zero,
              Collector.eta_zero - Collector.c1*dT/max(G,
                Modelica.Constants.eps) -
              Collector.c2*dT*dT/max(G, Modelica.Constants.eps)));
  Q_flow = G*eta;
end SolarThermalEfficiency;


within AixLib.Fluid.Solar.Thermal;
package Examples "Package with examples of solar thermal models"
  extends Modelica.Icons.ExamplesPackage;

end Examples;


within AixLib.Fluid.Solar.Thermal.Examples;
model SolarThermalCollector
  "Example to demonstrate the function of the solar thermal collector model"
  extends Modelica.Icons.Example;

  replaceable package Medium = AixLib.Media.Water constrainedby Modelica.Media.Interfaces.PartialMedium
                                            "Medium model";

  Sources.Boundary_pT                source(
    nPorts=1,
    redeclare package Medium = Medium,
    p=system.p_ambient + solarThermal.pressureDropCoeff*(solarThermal.m_flow_nominal
        /995)^2+pipe.dp_nominal)
  Sources.Boundary_pT                sink(nPorts=1, redeclare package Medium =
        Medium,
    p=system.p_ambient)
  AixLib.Fluid.Sensors.MassFlowRate massFlowSensor(redeclare package Medium =
        Medium)
  AixLib.Fluid.Sensors.TemperatureTwoPort T1(redeclare package Medium = Medium,
      m_flow_nominal=system.m_flow_nominal)
  AixLib.Fluid.Solar.Thermal.SolarThermal solarThermal(
    A=2,
    redeclare package Medium = Medium,
    m_flow_nominal=system.m_flow_nominal,
    volPip=0.05,
    T_start=system.T_start,
    p_start=source.p,
    Collector=DataBase.SolarThermal.FlatCollector())
  AixLib.Fluid.FixedResistances.PressureDrop pipe(
    redeclare package Medium = Medium,
    m_flow_nominal=system.m_flow_nominal,
    dp_nominal=200)
  AixLib.Fluid.Sensors.TemperatureTwoPort T2(redeclare package Medium = Medium,
      m_flow_nominal=system.m_flow_nominal)
  Modelica.Blocks.Sources.CombiTimeTable hotSummerDay(
    extrapolation=Modelica.Blocks.Types.Extrapolation.Periodic,
    table=[0,21,0; 3600,20.6,0; 7200,20.5,0; 10800,20.4,0; 14400,20,6; 18000,20.5,
        106; 21600,22.4,251; 25200,24.1,402; 28800,26.3,540; 32400,28.4,657; 36000,
        30,739; 39600,31.5,777; 43200,31.5,778; 46800,32.5,737; 50400,32.5,657;
        54000,32.5,544; 57600,32.5,407; 61200,32.5,257; 64800,31.6,60; 68400,30.8,
        5; 72000,22.9,0; 75600,21.2,0; 79200,20.6,0; 82800,20.3,0],
    offset={273.15,0.01})
  inner Modelica.Fluid.System system(
    m_flow_start=system.m_flow_nominal,
    use_eps_Re=true,
    m_flow_nominal=1.5*solarThermal.A/60*995/1000,
    p_ambient=300000)
equation
  connect(massFlowSensor.port_b, T1.port_a)
  connect(T1.port_b, solarThermal.port_a)
  connect(solarThermal.port_b, T2.port_a)
  connect(T2.port_b, pipe.port_a)
  connect(source.ports[1], massFlowSensor.port_a)
  connect(pipe.port_b, sink.ports[1])
  connect(hotSummerDay.y[1], solarThermal.T_air)
  connect(hotSummerDay.y[2], solarThermal.Irradiation)
end SolarThermalCollector;


within AixLib.Fluid.SolarCollectors;
model ASHRAE93 "Model of a solar thermal collector according to the ASHRAE Standard 93"
  extends AixLib.Fluid.SolarCollectors.BaseClasses.PartialSolarCollector(
    redeclare AixLib.Fluid.SolarCollectors.Data.GenericASHRAE93 per);

  BaseClasses.ASHRAESolarGain solGai(
    redeclare package Medium = Medium,
    final nSeg=nSeg,
    final til=til,
    final incAngDat=per.incAngDat,
    final incAngModDat=per.incAngModDat,
    final y_intercept=per.y_intercept,
    final use_shaCoe_in=use_shaCoe_in,
    final shaCoe=shaCoe,
    final A_c=ATot_internal)
    "Identifies heat gained from the sun using the ASHRAE Standard 93 calculations"

  BaseClasses.ASHRAEHeatLoss heaLos(
    redeclare package Medium = Medium,
    final nSeg=nSeg,
    final slope=per.slope,
    final A_c=ATot_internal)
    "Calculates the heat lost to the surroundings using the ASHRAE Standard 93 calculations"

equation
  // Make sure the model is only used with the ASHRAE ratings data, and slope < 0
  assert(per.slope < 0,
    "In " + getInstanceName() + ": The heat loss coefficient from the ASHRAE ratings data must be strictly negative. Obtained slope = " + String(per.slope));

  connect(weaBus.TDryBul, heaLos.TEnv)
  connect(HDirTil.inc, solGai.incAng)
  connect(HDirTil.H, solGai.HDirTil)
  connect(HDifTilIso.HGroDifTil, solGai.HGroDifTil)
  connect(HDifTilIso.HSkyDifTil, solGai.HSkyDifTil)
  connect(shaCoe_in, solGai.shaCoe_in)
  connect(solGai.QSol_flow, QGai.Q_flow)
  connect(temSen.T, heaLos.TFlu)
  connect(temSen.T, solGai.TFlu)
  connect(heaLos.QLos_flow, QLos.Q_flow)
end ASHRAE93;


within AixLib.Fluid.SolarCollectors;
model EN12975 "Model of a solar thermal collector according to the ASHRAE Standard 93"
  extends AixLib.Fluid.SolarCollectors.BaseClasses.PartialSolarCollector(
    redeclare AixLib.Fluid.SolarCollectors.Data.GenericEN12975 per);

  AixLib.Fluid.SolarCollectors.BaseClasses.EN12975SolarGain solGai(
    redeclare package Medium = Medium,
    final nSeg=nSeg,
    final incAngDat=per.incAngDat,
    final incAngModDat=per.incAngModDat,
    final iamDiff=per.IAMDiff,
    final eta0=per.eta0,
    final use_shaCoe_in=use_shaCoe_in,
    final shaCoe=shaCoe,
    final A_c=ATot_internal)
    "Identifies heat gained from the sun using the EN12975 standard calculations"
  AixLib.Fluid.SolarCollectors.BaseClasses.EN12975HeatLoss heaLos(
    redeclare package Medium = Medium,
    final nSeg=nSeg,
    final a1=per.a1,
    final a2=per.a2,
    final A_c=ATot_internal)
    "Calculates the heat lost to the surroundings using the EN12975 standard calculations"

equation
  // Make sure the model is only used with the EN ratings data, and hence a1 > 0
  assert(per.a1 > 0,
    "In " + getInstanceName() + ": The heat loss coefficient from the EN 12975 ratings data must be strictly positive. Obtained a1 = " + String(per.a1));
  connect(shaCoe_internal, solGai.shaCoe_in);

  connect(weaBus.TDryBul, heaLos.TEnv)
  connect(HDirTil.inc, solGai.incAng)
  connect(HDifTilIso.H, solGai.HSkyDifTil)
  connect(HDirTil.H, solGai.HDirTil)
  connect(shaCoe_in, solGai.shaCoe_in)
  connect(heaLos.TFlu, temSen.T)
  connect(heaLos.QLos_flow, QLos.Q_flow)
  connect(solGai.QSol_flow, QGai.Q_flow)
  connect(temSen.T, solGai.TFlu)
end EN12975;


within AixLib.Fluid;
package SolarCollectors "Package with models for solar collectors"
  extends Modelica.Icons.Package;
end SolarCollectors;


User's Guide

within AixLib.Fluid.SolarCollectors;
package UsersGuide "User's Guide"
  extends Modelica.Icons.Information;

end UsersGuide;


Calculate the heat loss of a solar collector per ASHRAE standard 93. This component computes the heat loss from the solar thermal collector to the
environment.
It is designed for use with ratings data collected in accordance with
ASHRAE Standard 93.
A negative heat loss indicates that heat is being lost to the environment.

within AixLib.Fluid.SolarCollectors.BaseClasses;
block ASHRAEHeatLoss
  "Calculate the heat loss of a solar collector per ASHRAE standard 93"
  extends AixLib.Fluid.SolarCollectors.BaseClasses.PartialHeatLoss(
    QLos_internal = -slope * A_c/nSeg * {dT[i] for i in 1:nSeg});

  parameter Modelica.Units.SI.CoefficientOfHeatTransfer slope(final max=0)
    "Slope from ratings data";
end ASHRAEHeatLoss;


within AixLib.Fluid.SolarCollectors.BaseClasses;
block ASHRAESolarGain
  "Calculate the solar heat gain of a solar collector per ASHRAE Standard 93"
  extends Modelica.Blocks.Icons.Block;
  extends SolarCollectors.BaseClasses.PartialParameters;

  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
    "Medium in the system";

  parameter Real y_intercept(final unit="1") "y intercept(maximum efficiency)";
  parameter Modelica.Units.SI.Angle[:] incAngDat "Incidence angle modifier spline derivative coefficients";
  parameter Real[size(incAngDat,1)] incAngModDat(
    each final unit="1") "Incidence angle modifier spline derivative coefficients";
  parameter Boolean use_shaCoe_in = false "Enable input connector for shaCoe"

  parameter Real shaCoe(
    min=0.0,
    max=1.0) = 0 "Shading coefficient 0.0: no shading, 1.0: full shading"

  parameter Modelica.Units.SI.Angle til "Surface tilt";

  Modelica.Blocks.Interfaces.RealInput shaCoe_in if use_shaCoe_in
    "Shading coefficient"
   Modelica.Blocks.Interfaces.RealInput TFlu[nSeg](
   each unit = "K",
   each displayUnit="degC",
   each quantity="ThermodynamicTemperature")
   Modelica.Blocks.Interfaces.RealInput HSkyDifTil(
     unit="W/m2", quantity="RadiantEnergyFluenceRate")
    "Diffuse solar irradiation on a tilted surfce from the sky"
  Modelica.Blocks.Interfaces.RealInput HGroDifTil(
    unit="W/m2", quantity="RadiantEnergyFluenceRate")
    "Diffuse solar irradiation on a tilted surfce from the ground"
  Modelica.Blocks.Interfaces.RealInput incAng(
    quantity="Angle",
    unit="rad",
    displayUnit="deg") "Incidence angle of the sun beam on a tilted surface"
  Modelica.Blocks.Interfaces.RealInput HDirTil(
    unit="W/m2", quantity="RadiantEnergyFluenceRate")
    "Direct solar irradiation on a tilted surfce"
  Modelica.Blocks.Interfaces.RealOutput QSol_flow[nSeg](each final unit="W")
    "Solar heat gain"

protected
  constant Modelica.Units.SI.TemperatureDifference dTMax=1
    "Safety temperature difference to prevent TFlu > Medium.T_max";
  final parameter Modelica.Units.SI.Temperature TMedMax=Medium.T_max - dTMax
    "Fluid temperature above which there will be no heat gain computed to prevent TFlu > Medium.T_max";
  final parameter Modelica.Units.SI.Temperature TMedMax2=TMedMax - dTMax
    "Fluid temperature above which there will be no heat gain computed to prevent TFlu > Medium.T_max";

  final parameter Real iamSky(fixed=false)
    "Incident angle modifier for diffuse solar radiation from the sky";
  final parameter Real iamGro(fixed=false)
    "Incident angle modifier for diffuse solar radiation from the ground";
  final parameter Modelica.Units.SI.Angle incAngSky(fixed=false)
    "Incident angle of diffuse radiation from the sky";
  final parameter Modelica.Units.SI.Angle incAngGro(fixed=false)
    "Incident angle of diffuse radiation from the ground";
  final parameter Real tilDeg(
    unit = "deg") = Modelica.Units.Conversions.to_deg(til)
    "Surface tilt angle in degrees";
  final parameter Modelica.Units.SI.HeatFlux HTotMin=1
    "Minimum HTot to avoid div/0";
  final parameter Real HMinDel = 0.001
    "Delta of the smoothing function for HTot";

  Real iamBea "Incident angle modifier for direct solar radiation";
  Real iam "Weighted incident angle modifier";

  Modelica.Blocks.Interfaces.RealInput shaCoe_internal
    "Internally used shading coefficient";

  parameter Real[size(incAngDat, 1)] dMonotone(each fixed=false) "Derivatives";

initial algorithm
  dMonotone := AixLib.Utilities.Math.Functions.splineDerivatives(
    x=incAngDat,
    y=incAngModDat,
    ensureMonotonicity=false);

initial equation
  // EnergyPlus 23.2.0 Engineering Reference Eq 18.300
  incAngSky =Modelica.Units.Conversions.from_deg(59.68 - 0.1388*(tilDeg) +
    0.001497*(tilDeg)^2);
  // Diffuse radiation from the sky
  // EnergyPlus 23.2.0 Engineering Reference Eq 18.298
  iamSky = SolarCollectors.BaseClasses.IAM(incAngSky, incAngDat, incAngModDat, dMonotone);
  // EnergyPlus 23.2.0 Engineering Reference Eq 18.301
  incAngGro =Modelica.Units.Conversions.from_deg(90 - 0.5788*(tilDeg) +
    0.002693*(tilDeg)^2);
  // Diffuse radiation from the ground
  // EnergyPlus 23.2.0 Engineering Reference Eq 18.298
  iamGro = SolarCollectors.BaseClasses.IAM(incAngGro, incAngDat, incAngModDat, dMonotone);

equation

  connect(shaCoe_internal, shaCoe_in);

  if not use_shaCoe_in then
    shaCoe_internal = shaCoe;
  end if;

  // EnergyPlus 23.2.0 Engineering Reference Eq 18.298
  iamBea = SolarCollectors.BaseClasses.IAM(incAng, incAngDat, incAngModDat, dMonotone);
  // EnergyPlus 23.2.0 Engineering Reference Eq 18.299
  iam = (HDirTil*iamBea + HSkyDifTil*iamSky + HGroDifTil*iamGro)/
      AixLib.Utilities.Math.Functions.smoothMax((
        HDirTil + HSkyDifTil + HGroDifTil), HTotMin, HMinDel);
  // Modified from EnergyPlus 23.2.0 Engineering Reference Eq 18.302
  // by applying shade effect for direct solar radiation
  // Only solar heat gain is considered here
  for i in 1 : nSeg loop
    QSol_flow[i] = A_c/nSeg*(y_intercept*iam*
      (HDirTil*(1.0 - shaCoe_internal) + HSkyDifTil + HGroDifTil))*
      smooth(1, if TFlu[i] < TMedMax2
        then 1
        else AixLib.Utilities.Math.Functions.smoothHeaviside(TMedMax-TFlu[i], dTMax));
  end for;
end ASHRAESolarGain;


Calculate the heat loss of a solar collector per EN12975. This component computes the heat loss from the solar thermal collector
to the environment. It is designed anticipating ratings data collected in
accordance with EN12975. A negative heat loss indicates that heat
is being lost to the environment.

within AixLib.Fluid.SolarCollectors.BaseClasses;
block EN12975HeatLoss "Calculate the heat loss of a solar collector per EN12975"
  extends AixLib.Fluid.SolarCollectors.BaseClasses.PartialHeatLoss(
    QLos_internal = A_c/nSeg * {dT[i] * (a1 - a2 * dT[i]) for i in 1:nSeg});

  parameter Modelica.Units.SI.CoefficientOfHeatTransfer a1(final min=0)
    "a1 from ratings data";

  parameter Real a2(final unit = "W/(m2.K2)", final min=0)
    "a2 from ratings data";
end EN12975HeatLoss;


within AixLib.Fluid.SolarCollectors.BaseClasses;
model EN12975SolarGain "Model calculating solar gains per the EN12975 standard"
  extends Modelica.Blocks.Icons.Block;
  extends SolarCollectors.BaseClasses.PartialParameters;

  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
    "Medium in the system";

  parameter Real eta0(final unit="1") "Optical efficiency (maximum efficiency)";
  parameter Modelica.Units.SI.Angle[:] incAngDat "Incidence angle modifier spline derivative coefficients";
  parameter Real[size(incAngDat,1)] incAngModDat(each final unit="1") "Incidence angle modifier spline derivative coefficients";
  parameter Boolean use_shaCoe_in = false
    "Enables an input connector for shaCoe"
  parameter Real shaCoe(
    min=0.0,
    max=1.0) = 0 "Shading coefficient 0.0: no shading, 1.0: full shading"

  parameter Real iamDiff "Incidence angle modifier for diffuse radiation";

  Modelica.Blocks.Interfaces.RealInput shaCoe_in if use_shaCoe_in
    "Time varying input for the shading coefficient"

  Modelica.Blocks.Interfaces.RealInput HSkyDifTil(
    unit="W/m2",
    quantity="RadiantEnergyFluenceRate")
    "Diffuse solar irradiation on a tilted surfce from the sky"
  Modelica.Blocks.Interfaces.RealInput incAng(
    quantity="Angle",
    unit="rad",
    displayUnit="deg") "Incidence angle of the sun beam on a tilted surface"
  Modelica.Blocks.Interfaces.RealInput HDirTil(
    unit="W/m2",
    quantity="RadiantEnergyFluenceRate")
    "Direct solar irradiation on a tilted surfce"
  Modelica.Blocks.Interfaces.RealOutput QSol_flow[nSeg](each final unit="W")
    "Solar heat gain"
  Modelica.Blocks.Interfaces.RealInput TFlu[nSeg](
     each final unit="K",
     each displayUnit="degC",
     each final quantity="ThermodynamicTemperature")

protected
  constant Modelica.Units.SI.TemperatureDifference dTMax=1
    "Safety temperature difference to prevent TFlu > Medium.T_max";
  final parameter Modelica.Units.SI.Temperature TMedMax=Medium.T_max - dTMax
    "Fluid temperature above which there will be no heat gain computed to prevent TFlu > Medium.T_max";
  final parameter Modelica.Units.SI.Temperature TMedMax2=TMedMax - dTMax
    "Fluid temperature above which there will be no heat gain computed to prevent TFlu > Medium.T_max";
  Real iamBea "Incidence angle modifier for director solar radiation";
  Modelica.Blocks.Interfaces.RealInput shaCoe_internal "Internally used shaCoe";

  parameter Real[size(incAngDat, 1)] dMonotone(each fixed=false) "Derivatives";

initial algorithm
  dMonotone := AixLib.Utilities.Math.Functions.splineDerivatives(
    x=incAngDat,
    y=incAngModDat,
    ensureMonotonicity=false);

equation
  connect(shaCoe_internal, shaCoe_in);

  if not use_shaCoe_in then
    shaCoe_internal = shaCoe;
  end if;

  // EnergyPlus 23.2.0 Engineering Reference Eq 18.298
  iamBea = SolarCollectors.BaseClasses.IAM(incAng, incAngDat, incAngModDat, dMonotone);
  // Modified from EnergyPlus 23.2.0 Engineering Reference Eq 18.302
  // by applying shade effect for direct solar radiation
  // Only solar heat gain is considered here
  for i in 1 : nSeg loop
  QSol_flow[i] = A_c/nSeg*(eta0*(iamBea*HDirTil*(1.0 - shaCoe_internal) + iamDiff *
  HSkyDifTil))*
      smooth(1, if TFlu[i] < TMedMax2
        then 1
        else AixLib.Utilities.Math.Functions.smoothHeaviside(TMedMax-TFlu[i], dTMax));
  end for;
end EN12975SolarGain;


within AixLib.Fluid.SolarCollectors;
package BaseClasses "Package with base classes for AixLib.Fluid.SolarCollectors"
extends Modelica.Icons.BasesPackage;
end BaseClasses;


within AixLib.Fluid.SolarCollectors.BaseClasses;
block PartialHeatLoss
  "Partial heat loss model on which ASHRAEHeatLoss and EN12975HeatLoss are based"
  extends Modelica.Blocks.Icons.Block;
  extends SolarCollectors.BaseClasses.PartialParameters;

  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
    "Medium in the component";

  Modelica.Blocks.Interfaces.RealInput TEnv(
    quantity="ThermodynamicTemperature",
    unit="K",
    displayUnit="degC") "Temperature of surrounding environment"

  Modelica.Blocks.Interfaces.RealInput TFlu[nSeg](
    each quantity="ThermodynamicTemperature",
    each unit = "K",
    each displayUnit="degC") "Temperature of the heat transfer fluid"
  Modelica.Blocks.Interfaces.RealOutput QLos_flow[nSeg](
    each quantity="HeatFlowRate",
    each unit="W",
    each displayUnit="W") = {QLos_internal[i]*smooth(1, if TFlu[i] > TMedMin2
     then 1 else AixLib.Utilities.Math.Functions.smoothHeaviside(TFlu[i] -
    TMedMin, dTMin)) for i in 1:nSeg}
    "Limited heat loss rate at current conditions"

protected
  constant Modelica.Units.SI.Temperature dTMin=1
    "Safety temperature difference to prevent TFlu < Medium.T_min";
  final parameter Modelica.Units.SI.Temperature TMedMin=Medium.T_min + dTMin
    "Fluid temperature below which there will be no heat loss computed to prevent TFlu < Medium.T_min";
  final parameter Modelica.Units.SI.Temperature TMedMin2=TMedMin + dTMin
    "Fluid temperature below which there will be no heat loss computed to prevent TFlu < Medium.T_min";

  input Modelica.Units.SI.HeatFlowRate QLos_internal[nSeg]
    "Heat loss rate at current conditions for each segment";

  Modelica.Units.SI.TemperatureDifference dT[nSeg]={TEnv - TFlu[i] for i in 1:
      nSeg} "Environment minus collector fluid temperature";
end PartialHeatLoss;


Partial model for parameters. Partial parameters used in all solar collector models

within AixLib.Fluid.SolarCollectors.BaseClasses;
block PartialParameters "Partial model for parameters"

  parameter Modelica.Units.SI.Area A_c "Area of the collector";
  parameter Integer nSeg=3 "Number of segments";
end PartialParameters;


Partial model for solar collectors

within AixLib.Fluid.SolarCollectors.BaseClasses;
partial model PartialSolarCollector "Partial model for solar collectors"
 extends AixLib.Fluid.Interfaces.LumpedVolumeDeclarations;
  extends AixLib.Fluid.Interfaces.TwoPortFlowResistanceParameters(
    final dp_nominal = dp_nominal_final,
    final computeFlowResistance=(abs(dp_nominal) > Modelica.Constants.eps));
  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface(
    final m_flow_nominal=m_flow_nominal_final);

  constant Boolean homotopyInitialization = true "= true, use homotopy method"

  parameter Integer nSeg = 3
    "Number of segments used to discretize the collector model";

  parameter Modelica.Units.SI.Angle azi(displayUnit="deg")
    "Surface azimuth (0 for south-facing; -90 degree for east-facing; +90 degree for west facing";
  parameter Modelica.Units.SI.Angle til(displayUnit="deg")
    "Surface tilt (0 for horizontally mounted collector)";
  parameter Real rho(
    final min=0,
    final max=1,
    final unit = "1") "Ground reflectance";

  parameter Modelica.Units.SI.HeatCapacity CTot=
    if per.CTyp==AixLib.Fluid.SolarCollectors.Types.HeatCapacity.TotalCapacity then
      per.C
    elseif per.CTyp==AixLib.Fluid.SolarCollectors.Types.HeatCapacity.DryCapacity then
      per.C+rho_default*per.V*cp_default
    else
      385*per.mDry+rho_default*per.V*cp_default
    "Heat capacity of solar collector with fluid";

  parameter Boolean use_shaCoe_in = false
    "Enables an input connector for shaCoe"
  parameter Real shaCoe(
    min=0.0,
    max=1.0) = 0 "Shading coefficient. 0.0: no shading, 1.0: full shading"

  parameter AixLib.Fluid.SolarCollectors.Types.NumberSelection nColType=
  AixLib.Fluid.SolarCollectors.Types.NumberSelection.Number
    "Selection of area specification format"
  parameter Integer nPanels= 0 "Desired number of panels in the simulation"

  parameter Modelica.Units.SI.Area totalArea=0
    "Total area of panels in the simulation"

  parameter AixLib.Fluid.SolarCollectors.Types.SystemConfiguration sysConfig=
  AixLib.Fluid.SolarCollectors.Types.SystemConfiguration.Series
    "Selection of system configuration"
  parameter Integer nPanelsSer=0 "Number of array panels in series"
  parameter Integer nPanelsPar=0 "Number of array panels in parallel"

  Modelica.Blocks.Interfaces.RealInput shaCoe_in if use_shaCoe_in
    "Shading coefficient"

  AixLib.BoundaryConditions.WeatherData.Bus weaBus "Weather data bus"
  AixLib.BoundaryConditions.SolarIrradiation.DiffusePerez HDifTilIso(
    final outSkyCon=true,
    final outGroCon=true,
    final til=til,
    final azi=azi,
    final rho=rho) "Diffuse solar irradiation on a tilted surface"

  AixLib.BoundaryConditions.SolarIrradiation.DirectTiltedSurface HDirTil(
    final til=til,
    final azi=azi) "Direct solar irradiation on a tilted surface"

  AixLib.Fluid.Sensors.MassFlowRate senMasFlo(redeclare package Medium =
    Medium, allowFlowReversal=allowFlowReversal) "Mass flow rate sensor"
  AixLib.Fluid.FixedResistances.PressureDrop res(
    redeclare final package Medium = Medium,
    final from_dp=from_dp,
    final show_T=show_T,
    final m_flow_nominal=m_flow_nominal,
    final allowFlowReversal=allowFlowReversal,
    final linearized=linearizeFlowResistance,
    final homotopyInitialization=homotopyInitialization,
    deltaM=deltaM,
    final dp_nominal=dp_nominal_final) "Flow resistance"

  // The size of the liquid volume has been increased to also add
  // the heat capacity of the metal.
  AixLib.Fluid.MixingVolumes.MixingVolume vol[nSeg](
    each nPorts=2,
    redeclare package Medium = Medium,
    each final m_flow_nominal=m_flow_nominal,
    each final energyDynamics=energyDynamics,
    each final p_start=p_start,
    each final T_start=T_start,
    each final m_flow_small=m_flow_small,
    each final V=CTot/cp_default/rho_default*nPanels_internal/nSeg,
    each final massDynamics=massDynamics,
    each final X_start=X_start,
    each final C_start=C_start,
    each final C_nominal=C_nominal,
    each final mSenFac=mSenFac,
    each final allowFlowReversal=allowFlowReversal,
    each final prescribedHeatFlowRate=false)
    "Volume of fluid in one segment of the solar collector"

  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSen[nSeg]
    "Temperature sensor"

  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow QGai[nSeg]
    "Rate of solar heat gain"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow QLos[nSeg]
    "Rate of heat loss"
  replaceable parameter AixLib.Fluid.SolarCollectors.Data.GenericASHRAE93 per
    constrainedby AixLib.Fluid.SolarCollectors.Data.BaseClasses.Generic
    "Performance data"

protected
  Modelica.Blocks.Interfaces.RealInput shaCoe_internal
    "Internally used shading coefficient";

  final parameter Modelica.Units.SI.MassFlowRate m_flow_nominal_final(
      displayUnit="kg/s") = nPanelsPar_internal*per.mperA_flow_nominal*per.A
    "Nominal mass flow rate through the system of collectors";

  final parameter Modelica.Units.SI.PressureDifference dp_nominal_final(
      displayUnit="Pa") = nPanelsSer_internal*per.dp_nominal
    "Nominal pressure loss across the system of collectors";

  parameter Modelica.Units.SI.Area ATot_internal=nPanels_internal*per.A
    "Area used in the simulation";

  parameter Real nPanels_internal=
    if nColType == AixLib.Fluid.SolarCollectors.Types.NumberSelection.Number then
      nPanels
    else
      totalArea/per.A "Number of panels used in the simulation";
  parameter Real nPanelsSer_internal=
    if sysConfig == AixLib.Fluid.SolarCollectors.Types.SystemConfiguration.Series then
      nPanels
    else if sysConfig == AixLib.Fluid.SolarCollectors.Types.SystemConfiguration.Parallel then
      1
    else
      nPanelsSer "Number of panels in series";
  parameter Real nPanelsPar_internal=
    if sysConfig == AixLib.Fluid.SolarCollectors.Types.SystemConfiguration.Parallel then
      nPanels
    else if sysConfig == AixLib.Fluid.SolarCollectors.Types.SystemConfiguration.Series then
      1
    else
      nPanelsPar "Number of panels in parallel";

  parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX(
    T=Medium.T_default,
    p=Medium.p_default,
    X=Medium.X_default[1:Medium.nXi]) "Medium state at default properties";
  parameter Modelica.Units.SI.SpecificHeatCapacity cp_default=
      Medium.specificHeatCapacityCp(sta_default)
    "Specific heat capacity of the fluid";
  parameter Modelica.Units.SI.Density rho_default=Medium.density(sta_default)
    "Density, used to compute fluid mass";

initial equation
  assert(homotopyInitialization, "In " + getInstanceName() +
    ": The constant homotopyInitialization has been modified from its default value. This constant will be removed in future releases.",
    level = AssertionLevel.warning);

  if sysConfig==AixLib.Fluid.SolarCollectors.Types.SystemConfiguration.Array then
    assert(abs(nPanelsPar_internal*nPanelsSer_internal-nPanels_internal) < 1E-6,
      "In " + getInstanceName() +
      ": The product of the number of panels in series and parallel is not equal to the total number of panels in the array.",
      level = AssertionLevel.error);
  end if;

equation
  connect(shaCoe_internal,shaCoe_in);

  if not use_shaCoe_in then
    shaCoe_internal=shaCoe;
  end if;

  connect(weaBus, HDifTilIso.weaBus)
  connect(weaBus, HDirTil.weaBus)
  connect(port_a, senMasFlo.port_a)
  connect(senMasFlo.port_b, res.port_a)
  connect(vol[nSeg].ports[2], port_b)
  connect(vol[1].ports[1], res.port_b)
      for i in 1:(nSeg - 1) loop
    connect(vol[i].ports[2], vol[i + 1].ports[1]);
  end for;
  connect(vol.heatPort, temSen.port)
  connect(QGai.port, vol.heatPort)
  connect(QLos.port, vol.heatPort)
end PartialSolarCollector;


within AixLib.Fluid.SolarCollectors.BaseClasses.Examples;
model ASHRAEHeatLoss "Example showing the use of ASHRAEHeatLoss"
  extends Modelica.Icons.Example;
  parameter AixLib.Fluid.SolarCollectors.Data.GenericASHRAE93 per=
    AixLib.Fluid.SolarCollectors.Data.GlazedFlatPlate.FP_SolahartKf()
    "Performance data"
  Modelica.Blocks.Sources.Sine TEnv(
    f=0.01,
    offset=273.15 + 10,
    amplitude=7.5) "Temperature of the surrounding environment"
  Modelica.Blocks.Sources.Sine T1(
    amplitude=15,
    f=0.1,
    offset=273.15 + 10) "Temperature of the first segment"
  Modelica.Blocks.Sources.Sine T2(
    f=0.1,
    amplitude=15,
    offset=273.15 + 15) "Temperature of the second segment"
  Modelica.Blocks.Sources.Sine T3(
    f=0.1,
    amplitude=15,
    offset=273.15 + 20) "Temperature of the third segment"
  AixLib.Fluid.SolarCollectors.BaseClasses.ASHRAEHeatLoss  heaLos(
    nSeg=3,
    redeclare package Medium = AixLib.Media.Water,
    A_c=per.A,
    slope=per.slope) "Heat loss model using EN12975 calculations"
equation
  connect(TEnv.y, heaLos.TEnv)
  connect(T3.y, heaLos.TFlu[3])
  connect(T2.y, heaLos.TFlu[2])
  connect(T1.y, heaLos.TFlu[1])
end ASHRAEHeatLoss;


within AixLib.Fluid.SolarCollectors.BaseClasses.Examples;
model ASHRAESolarGain "Example showing the use of ASHRAESolarGain"
  extends Modelica.Icons.Example;
  parameter AixLib.Fluid.SolarCollectors.Data.GenericASHRAE93 per=
    AixLib.Fluid.SolarCollectors.Data.GlazedFlatPlate.FP_ThermaLiteHS20()
    "Performance data"
  AixLib.Fluid.SolarCollectors.BaseClasses.ASHRAESolarGain solGai(
    nSeg=3,
    incAngDat=per.incAngDat,
    incAngModDat=per.incAngModDat,
    shaCoe=0,
    use_shaCoe_in=true,
    A_c=per.A,
    y_intercept=per.y_intercept,
    redeclare package Medium = AixLib.Media.Water,
    til=0.78539816339745) "Solar heat gain model using ASHRAE 93 calculations"
  Modelica.Blocks.Sources.Sine HGroDifTil(
    amplitude=50,
    f=4/86400,
    offset=100) "Diffuse radiation from the ground, tilted surface"
  Modelica.Blocks.Sources.Ramp incAng(duration=86400, height=60*(2*Modelica.Constants.pi
        /360)) "Incidence angle"
  Modelica.Blocks.Sources.Sine HDirTil(
    offset=400,
    amplitude=300,
    f=2/86400) "Direct beam radiation, tilted surface"
  Modelica.Blocks.Sources.Sine HSkyDifTil(
    f=1/86400,
    amplitude=100,
    offset=100) "Diffuse radiation, tilted surface"
  Modelica.Blocks.Sources.Ramp shaCoe(
    height=-1,
    duration=86400,
    offset=1) "Shading coefficient"
  Modelica.Blocks.Sources.Sine T3(
    f=2/86400,
    amplitude=50,
    offset=273.15 + 110)
  Modelica.Blocks.Sources.Sine T2(
    f=2/86400,
    amplitude=50,
    offset=273.15 + 100)
  Modelica.Blocks.Sources.Sine T1(
    f=2/86400,
    amplitude=50,
    offset=273.15 + 90)
equation
  connect(HGroDifTil.y,solGai. HGroDifTil)
  connect(shaCoe.y,solGai. shaCoe_in)
  connect(incAng.y,solGai. incAng)
  connect(HDirTil.y,solGai. HDirTil)
  connect(HSkyDifTil.y,solGai. HSkyDifTil)
  connect(T3.y,solGai. TFlu[3])
  connect(T2.y,solGai. TFlu[2])
  connect(T1.y,solGai. TFlu[1])
end ASHRAESolarGain;


within AixLib.Fluid.SolarCollectors.BaseClasses.Examples;
model EN12975HeatLoss "Example showing the use of EN12975HeatLoss"
  extends Modelica.Icons.Example;
  parameter AixLib.Fluid.SolarCollectors.Data.GenericEN12975 per=
    AixLib.Fluid.SolarCollectors.Data.GlazedFlatPlate.FP_VerificationModel()
    "Performance data"
  Modelica.Blocks.Sources.Sine TEnv(
    f=0.01,
    offset=273.15 + 10,
    amplitude=15) "Temperature of the surrounding environment"
  Modelica.Blocks.Sources.Sine T1(
    amplitude=15,
    f=0.1,
    offset=273.15 + 10) "Temperature of the first segment"
  Modelica.Blocks.Sources.Sine T2(
    f=0.1,
    amplitude=15,
    offset=273.15 + 15) "Temperature of the second segment"
  Modelica.Blocks.Sources.Sine T3(
    f=0.1,
    amplitude=15,
    offset=273.15 + 20) "Temperature of the third segment"
  AixLib.Fluid.SolarCollectors.BaseClasses.EN12975HeatLoss heaLos(
    nSeg=3,
    redeclare package Medium = AixLib.Media.Water,
    a1=per.a1,
    a2=per.a2,
    A_c=per.A)       "Heat loss model using EN12975 calculations"
equation
  connect(TEnv.y, heaLos.TEnv)
  connect(T3.y, heaLos.TFlu[3])
  connect(T2.y, heaLos.TFlu[2])
  connect(T1.y, heaLos.TFlu[1])
end EN12975HeatLoss;


within AixLib.Fluid.SolarCollectors.BaseClasses.Examples;
model EN12975SolarGain "Example showing the use of EN12975SolarGain"
  extends Modelica.Icons.Example;
  parameter AixLib.Fluid.SolarCollectors.Data.GenericEN12975 per=
    AixLib.Fluid.SolarCollectors.Data.GlazedFlatPlate.FP_VerificationModel()
    "Performance data"
  AixLib.Fluid.SolarCollectors.BaseClasses.EN12975SolarGain solGai(
    eta0=per.eta0,
    incAngDat=per.incAngDat,
    incAngModDat=per.incAngModDat,
    nSeg=3,
    A_c=per.A,
    iamDiff=per.IAMDiff,
    shaCoe=0,
    use_shaCoe_in=true,
    redeclare package Medium = AixLib.Media.Water)
    "Solar heat gain model using EN12975 calculations"
  Modelica.Blocks.Sources.Ramp incAng(duration=86400,
    height=60*(2*Modelica.Constants.pi/360)) "Incidence angle"
  Modelica.Blocks.Sources.Sine HDirTil(
    offset=400,
    amplitude=300,
    f=2/86400) "Direct beam radiation, tilted surface"
  Modelica.Blocks.Sources.Sine HDifTil(
    amplitude=200,
    f=1/86400,
    offset=300) "Diffuse radiation, tilted surface"
  Modelica.Blocks.Sources.Ramp shaCoe(
    duration=86400,
    offset=1,
    height=-1) "Shading coefficient"
  Modelica.Blocks.Sources.Sine T3(
    f=2/86400,
    amplitude=50,
    offset=273.15 + 110)
  Modelica.Blocks.Sources.Sine T2(
    f=2/86400,
    amplitude=50,
    offset=273.15 + 100)
  Modelica.Blocks.Sources.Sine T1(
    f=2/86400,
    amplitude=50,
    offset=273.15 + 90)
equation
  connect(incAng.y, solGai.incAng)
  connect(shaCoe.y, solGai.shaCoe_in)
  connect(HDirTil.y, solGai.HDirTil)
  connect(HDifTil.y, solGai.HSkyDifTil)
  connect(T3.y, solGai.TFlu[3])
  connect(T2.y, solGai.TFlu[2])
  connect(T1.y, solGai.TFlu[1])
end EN12975SolarGain;


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


Controller which activates a circulation pump when solar radiation is above a critical level. Pump on/off controller for a solar thermal system.

within AixLib.Fluid.SolarCollectors.Controls;
model CollectorPump
  "Controller which activates a circulation pump when solar radiation is above a critical level"
  extends Modelica.Blocks.Icons.Block;

  parameter Modelica.Units.SI.HeatFlowRate delQ_flow(min=1)=10
    "Required estimated heat gain per unit area of collector to switch system on";
  parameter Modelica.Units.SI.Angle azi(displayUnit="deg")
    "Surface azimuth (0 for south-facing; -90 degree for east-facing; +90 degree for west facing";
  parameter Modelica.Units.SI.Angle til(displayUnit="deg")
    "Surface tilt (0 for horizontally mounted collector)";
  parameter Real rho(
    final min=0,
    final max=1,
    final unit = "1") = 0.2 "Ground reflectance";

  parameter AixLib.Fluid.SolarCollectors.Data.GenericASHRAE93 per
    "Performance data"

  Modelica.Blocks.Interfaces.RealInput TIn(
    final unit = "K",
    final displayUnit = "degC",
    quantity = "ThermodynamicTemperature")
    "Fluid temperature entering the collector"

  Modelica.Blocks.Interfaces.BooleanOutput on
    "Ouputs true if collector pump should be on"

  AixLib.BoundaryConditions.WeatherData.Bus weaBus "Weather data input"

  AixLib.Fluid.SolarCollectors.Controls.BaseClasses.GCritCalc criSol(
    final slope=per.slope,
    final y_intercept=per.y_intercept)
    "Calculates the critical insolation based on collector design and current weather conditions"
  BoundaryConditions.SolarIrradiation.DiffusePerez HDifTilIso(
    final til=til,
    final azi=azi,
    final rho=rho) "Diffuse solar irradiation on a tilted surface"
  BoundaryConditions.SolarIrradiation.DirectTiltedSurface HDirTil(
    final til=til,
    final azi=azi)
      "Direct solar irradiation on a tilted surface"
  Modelica.Blocks.Math.Add HTotTil(
    u1(final unit="W/m2"),
    u2(final unit="W/m2"),
    y(final unit="W/m2"))
    "Total irradiation on tilted surface"

  Modelica.Blocks.Logical.Hysteresis hys(
    final uLow=0,
    final uHigh=delQ_flow)
protected
  Modelica.Blocks.Math.Add sub(
    final k2=-1)
    "Difference between incident solar and required incident to compensate thermal losses"
equation
  connect(TIn, criSol.TIn)
  connect(weaBus.TDryBul, criSol.TEnv)
  connect(HDirTil.weaBus, weaBus)
  connect(HDifTilIso.weaBus, weaBus)
  connect(HDifTilIso.H, HTotTil.u1)
  connect(HDirTil.H, HTotTil.u2)
  connect(HTotTil.y, sub.u1)
  connect(criSol.G_TC, sub.u2)
  connect(hys.u, sub.y)
  connect(on, hys.y)
end CollectorPump;


within AixLib.Fluid.SolarCollectors;
package Controls "Package for solar thermal collector controllers"
  extends Modelica.Icons.Package;
end Controls;


Model calculating the critical insolation level. This component calculates the solar radiation necessary for the fluid
in the collector to gain heat. It is used in the model

AixLib.Fluid.SolarCollectors.Controls.CollectorPump.

within AixLib.Fluid.SolarCollectors.Controls.BaseClasses;
model GCritCalc "Model calculating the critical insolation level"

  extends Modelica.Blocks.Icons.Block;

  parameter Modelica.Units.SI.CoefficientOfHeatTransfer slope
    "Slope from ratings data";
  parameter Real y_intercept(final unit="1") "y_intercept from ratings data";

  Modelica.Blocks.Interfaces.RealInput TIn(
    final unit="K",
    displayUnit = "degC",
    quantity = "ThermodynamicTemperature")
    "Temperature of water entering the collector"
  Modelica.Blocks.Interfaces.RealOutput G_TC(
    final unit="W/m2",
    quantity = "RadiantEnergyFluenceRate") "Critical radiation level"
  Modelica.Blocks.Interfaces.RealInput TEnv(
    final unit="K",
    displayUnit = "degC",
    quantity = "ThermodynamicTemperature")
    "Ambient temperature at the collector"

equation
  G_TC = -slope * (TIn - TEnv) / y_intercept;
end GCritCalc;


within AixLib.Fluid.SolarCollectors.Controls;
package BaseClasses "Package of BaseClass models used to create the models in the Controls package"
extends Modelica.Icons.Package;
end BaseClasses;


Example showing the use of GCritCalc. This model provides an example of how to use the

AixLib.Fluid.SolarCollectors.Controls.BaseClasses.GCritCalc model.

within AixLib.Fluid.SolarCollectors.Controls.BaseClasses.Examples;
model GCritCalc "Example showing the use of GCritCalc"
  extends Modelica.Icons.Example;
  AixLib.Fluid.SolarCollectors.Controls.BaseClasses.GCritCalc criSol(slope=-3.764,
      y_intercept=0.602)
    "Calculates the critical insolation based on collector design and current weather conditions"
  Modelica.Blocks.Sources.Sine TEnv(
    amplitude=10,
    f=0.1,
    offset=10)
  Modelica.Blocks.Sources.Sine TIn(
    amplitude=10,
    f=0.01,
    offset=30)
equation
  connect(TEnv.y, criSol.TEnv)
  connect(TIn.y, criSol.TIn)
end GCritCalc;


within AixLib.Fluid.SolarCollectors.Controls.BaseClasses;
package Examples "Package containing examples demonstrating the use of Controls.BaseClasses models"

  extends Modelica.Icons.ExamplesPackage;
end Examples;


within AixLib.Fluid.SolarCollectors.Controls;
package Examples "Examples for solar thermal collector controller models"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


Example for the solar pump controller. This model illustrates the use of the

AixLib.Fluid.SolarCollectors.Controls.CollectorPump model.
Based on weather data and inlet temperature, the controller switches the pump on
and off.

within AixLib.Fluid.SolarCollectors.Controls.Examples;
model SolarPumpController "Example for the solar pump controller"
  extends Modelica.Icons.Example;
  AixLib.Fluid.SolarCollectors.Controls.CollectorPump pumCon(
    azi=0,
    til=0.78539816339745,
    per=AixLib.Fluid.SolarCollectors.Data.GlazedFlatPlate.FP_ThermaLiteHS20())
    "Model controlling the on/off status of the pump"
  AixLib.BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
     filNam=Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos"))
    "Weather data input file"
  Modelica.Blocks.Sources.Sine sine(
    amplitude=20,
    f=0.0001,
    offset=273.15 + 40) "Water inlet temperature"
equation
  connect(weaDat.weaBus, pumCon.weaBus)
  connect(sine.y, pumCon.TIn)
end SolarPumpController;


Data for solar thermal collectors

within AixLib.Fluid.SolarCollectors;
package Data "Data for solar thermal collectors"
extends Modelica.Icons.MaterialPropertiesPackage;
end Data;


Generic data record providing common inputs for ASHRAE93 and EN12975 solar collector models. Partial record containing common performance parameters for ASHRAE93 and EN12975
solar collector models.

within AixLib.Fluid.SolarCollectors.Data.BaseClasses;
record Generic
  "Generic data record providing common inputs for ASHRAE93 and EN12975 solar collector models"
  extends Modelica.Icons.Record;

  parameter Modelica.Units.SI.Area A "Area";
  parameter AixLib.Fluid.SolarCollectors.Types.HeatCapacity CTyp
    "Total thermal capacity or fluid volume and 'dry' thermal capacity or mass";
  parameter Modelica.Units.SI.HeatCapacity C
    "Dry or total thermal capacity of the solar thermal collector";
  parameter Modelica.Units.SI.Volume V "Fluid volume ";
  parameter Modelica.Units.SI.Mass mDry "Dry mass";
  parameter Real mperA_flow_nominal(final unit="kg/(s.m2)")
    "Nominal mass flow rate per unit area of collector";
  parameter Modelica.Units.SI.PressureDifference dp_nominal(displayUnit="Pa")
    "Nominal pressure drop";
  parameter Modelica.Units.SI.Angle[:] incAngDat=Modelica.Units.Conversions.from_deg({0,10,20,30,40,50,60,70,80,90})
    "Incident angle data";
  parameter Real[size(incAngDat,1)] incAngModDat(
    each final min=0,
    each final unit="1")
    "Incident angle modifier data";

  final parameter Boolean validated = validateAngles(
    incAngDat=incAngDat,
    incAngModDat=incAngModDat)
    "True if data are valid, otherwise an assertion is issued";
end Generic;


within AixLib.Fluid.SolarCollectors.Data;
package BaseClasses "Package with base classes for data records of solar thermal collectors"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


FP - All inputs necessary for verification of EN12975 models.

within AixLib.Fluid.SolarCollectors.Data.GlazedFlatPlate;
record FP_VerificationModel =
  AixLib.Fluid.SolarCollectors.Data.GenericEN12975 (
    final A=4.302,
    final CTyp=AixLib.Fluid.SolarCollectors.Types.HeatCapacity.DryMass,
    final C=0,
    final V=4.4/1000,
    final dp_nominal = 100,
    final mperA_flow_nominal=0.0241,
    final eta0=0.720,
    final IAMDiff=0.133,
    final a1=2.8312,
    final a2=0.00119,
    final incAngDat=Modelica.Units.Conversions.from_deg({0,10,20,30,40,50,60,70,80,90}),
    final incAngModDat={1.0,0.9967,0.9862,0.9671,0.9360,0.8868,0.8065,0.6686,0.4906,0.0},
    final mDry=484)
  "FP - All inputs necessary for verification of EN12975 models"


within AixLib.Fluid.SolarCollectors.Data;
package GlazedFlatPlate "Package with SRCC rating information for glazed flat-plate solar thermal collectors"
extends Modelica.Icons.MaterialPropertiesPackage;
end GlazedFlatPlate;


within AixLib.Fluid.SolarCollectors.Data;
package Tubular "Package with SRCC rating information for tubular solar thermal collectors"
extends Modelica.Icons.MaterialPropertiesPackage;
end Tubular;


Test model for FlatPlate

within AixLib.Fluid.SolarCollectors.Examples;
model FlatPlate "Test model for FlatPlate"
  extends Modelica.Icons.Example;
  replaceable package Medium = Modelica.Media.Incompressible.Examples.Glycol47
    "Medium in the system";

  AixLib.Fluid.SolarCollectors.ASHRAE93 solCol(
    redeclare package Medium = Medium,
    shaCoe=0,
    rho=0.2,
    nColType=AixLib.Fluid.SolarCollectors.Types.NumberSelection.Number,
    sysConfig=AixLib.Fluid.SolarCollectors.Types.SystemConfiguration.Array,
    nPanelsSer=5,
    nPanelsPar=5,
    per=AixLib.Fluid.SolarCollectors.Data.GlazedFlatPlate.FP_GuangdongFSPTY95(),
    nPanels=25,
    nSeg=9,
    azi=0.3,
    til=0.5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    massDynamics=Modelica.Fluid.Types.Dynamics.SteadyState)
    "Flat plate solar collector model"

  AixLib.BoundaryConditions.WeatherData.ReaderTMY3 weaDat(filNam=
    Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos"))
    "Weather data input file"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    p(displayUnit="bar") = 100000,
    nPorts=1) "Outlet for water flow"
  AixLib.Fluid.Sensors.TemperatureTwoPort TOut(
    redeclare package Medium = Medium,
    T_start(displayUnit="K"),
    m_flow_nominal=solCol.m_flow_nominal) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort TIn(redeclare package Medium =
    Medium, m_flow_nominal=solCol.m_flow_nominal) "Temperature sensor"
  Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    T=273.15 + 10,
    nPorts=1,
    use_p_in=true) "Inlet for water flow"
  Modelica.Blocks.Sources.Sine sine(
    f=3/86400,
    amplitude=-solCol.dp_nominal,
    offset=1E5) "Pressure source"
equation
  connect(solCol.port_b, TOut.port_a)
  connect(sou.ports[1], TIn.port_a)
  connect(weaDat.weaBus, solCol.weaBus)
  connect(TIn.port_b, solCol.port_a)
  connect(TOut.port_b, sin.ports[1])
  connect(sine.y, sou.p_in)
end FlatPlate;


within AixLib.Fluid.SolarCollectors.Examples;
model FlatPlateShaCoeTrue "Test model for FlatPlate with use_shaCoe_in = true"
  extends Modelica.Icons.Example;
  replaceable package Medium = AixLib.Media.Water "Medium in the system";
  AixLib.Fluid.SolarCollectors.ASHRAE93          solCol(
    redeclare package Medium = Medium,
    shaCoe=0,
    from_dp=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    use_shaCoe_in=true,
    per=AixLib.Fluid.SolarCollectors.Data.GlazedFlatPlate.FP_GuangdongFSPTY95(),
    rho=0.2,
    azi=0,
    nColType=AixLib.Fluid.SolarCollectors.Types.NumberSelection.Number,
    nPanels=5,
    sysConfig=AixLib.Fluid.SolarCollectors.Types.SystemConfiguration.Series,
    nSeg=9,
    til=0.5235987755983) "Flat plate solar collector with 3 segments"

  AixLib.BoundaryConditions.WeatherData.ReaderTMY3 weaDat(filNam=
    Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos"))
    "Weather data input file"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    use_p_in=false,
    p(displayUnit="Pa") = 101325,
    nPorts=1) "Outlet for water flow"
  AixLib.Fluid.Sensors.TemperatureTwoPort TOut(
    redeclare package Medium = Medium,
    T_start(displayUnit="K"),
    m_flow_nominal=solCol.m_flow_nominal) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort TIn(redeclare package Medium =
    Medium, m_flow_nominal=solCol.m_flow_nominal) "Temperature sensor"
  AixLib.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    use_p_in=false,
    nPorts=1,
    T=273.15 + 10,
    p(displayUnit="Pa") = 101325 + 5*solCol.per.dp_nominal)
    "Inlet for water flow"
  Modelica.Blocks.Sources.Ramp shaCoe(
    startTime=34040,
    height=1,
    duration=24193) "Varying shading coefficient"
equation
  connect(solCol.port_b, TOut.port_a)
  connect(TOut.port_b, sin.ports[1])
  connect(TIn.port_b, solCol.port_a)
  connect(sou.ports[1], TIn.port_a)
  connect(shaCoe.y, solCol.shaCoe_in)
  connect(weaDat.weaBus, solCol.weaBus)
end FlatPlateShaCoeTrue;


within AixLib.Fluid.SolarCollectors.Examples;
model FlatPlateTotalArea "Example showing the use of TotalArea and nSeg"
  extends Modelica.Icons.Example;
  replaceable package Medium = AixLib.Media.Water "Medium in the system";

  AixLib.Fluid.SolarCollectors.ASHRAE93          solCol(
    redeclare package Medium = Medium,
    shaCoe=0,
    from_dp=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    rho=0.2,
    sysConfig=AixLib.Fluid.SolarCollectors.Types.SystemConfiguration.Series,
    per=AixLib.Fluid.SolarCollectors.Data.GlazedFlatPlate.FP_SolahartKf(),
    nColType=AixLib.Fluid.SolarCollectors.Types.NumberSelection.Number,
    nPanels=10,
    nSeg=9,
    azi=0.3,
    til=0.5) "Flat plate solar collector model"

  AixLib.BoundaryConditions.WeatherData.ReaderTMY3 weaDat(filNam=
    Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos"))
    "Weather data input file"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    use_p_in=false,
    p(displayUnit="Pa") = 101325,
    nPorts=2) "Outlet for water flow"
  AixLib.Fluid.Sensors.TemperatureTwoPort TOut(
    redeclare package Medium = Medium,
    T_start(displayUnit="K"),
    m_flow_nominal=solCol.m_flow_nominal) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort TIn(redeclare package Medium =
    Medium, m_flow_nominal=solCol.m_flow_nominal) "Temperature sensor"
  AixLib.Fluid.Sources.Boundary_pT sou(
    redeclare package Medium = Medium,
    T=273.15 + 10,
    use_p_in=false,
    nPorts=2,
    p(displayUnit="Pa") = 101325 + solCol.dp_nominal) "Inlet for water flow"

  AixLib.Fluid.SolarCollectors.ASHRAE93 solCol1(
    redeclare package Medium = Medium,
    shaCoe=0,
    from_dp=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    rho=0.2,
    sysConfig=AixLib.Fluid.SolarCollectors.Types.SystemConfiguration.Series,
    per=AixLib.Fluid.SolarCollectors.Data.GlazedFlatPlate.FP_SolahartKf(),
    nColType=AixLib.Fluid.SolarCollectors.Types.NumberSelection.Number,
    nPanels=10,
    nSeg=27,
    azi=0.3,
    til=0.5) "Flat plate solar collector model"
  AixLib.Fluid.Sensors.TemperatureTwoPort TOut1(
    redeclare package Medium = Medium,
    T_start(displayUnit="K"),
    m_flow_nominal=solCol.m_flow_nominal) "Temperature sensor"
  AixLib.Fluid.Sensors.TemperatureTwoPort TIn1(
    redeclare package Medium =
    Medium, m_flow_nominal=solCol.m_flow_nominal) "Temperature sensor"
equation
  connect(solCol.port_b, TOut.port_a)
  connect(TOut.port_b, sin.ports[1])
  connect(TIn.port_b, solCol.port_a)
  connect(sou.ports[1], TIn.port_a)
  connect(weaDat.weaBus, solCol.weaBus)
  connect(solCol1.port_b, TOut1.port_a)
  connect(TIn1.port_b, solCol1.port_a)
  connect(weaDat.weaBus, solCol1.weaBus)
  connect(sou.ports[2], TIn1.port_a)
  connect(TOut1.port_b, sin.ports[2])
end FlatPlateTotalArea;