within AixLib.Fluid.FMI.Conversion;
block AirToOutlet
  "Conversion from real signals for a fluid to a AixLib.Fluid.FMI.Interfaces.Outlet connector"
  extends Modelica.Blocks.Icons.Block;

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

  parameter Boolean allowFlowReversal = true
    "= true to allow flow reversal, false restricts to design direction (inlet -> outlet)"

  Modelica.Blocks.Interfaces.RealInput m_flow(
    final unit="kg/s") "Mass flow rate"

  Modelica.Blocks.Interfaces.RealInput h(final unit="J/kg")
    "Specific enthalpy"

  Modelica.Blocks.Interfaces.RealInput Xi[Medium.nXi](
    each final unit="kg/kg")
    "Water vapor concentration in kg/kg total air"

  Modelica.Blocks.Interfaces.RealInput C[Medium.nC](
    final quantity=Medium.extraPropertiesNames)
    "Prescribed boundary trace substances"

  AixLib.Fluid.FMI.Interfaces.Outlet outlet(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_p_in=false) "Fluid outlet"

  Modelica.Blocks.Interfaces.RealOutput TAirZon(
    final unit="K",
    displayUnit="degC")
    if allowFlowReversal
    "Temperature of the backward flowing medium in the connector outlet"
  Modelica.Blocks.Interfaces.RealOutput X_wZon(
    final unit="kg/kg")
    if Medium.nXi > 0 and allowFlowReversal
    "Water mass fraction per total air mass of the backward flowing medium in the connector outlet"
  Modelica.Blocks.Interfaces.RealOutput CZon[Medium.nC](
    final quantity=Medium.extraPropertiesNames)
    if allowFlowReversal
    "Trace substances of the backward flowing medium in the connector outlet"

protected
  Modelica.Blocks.Interfaces.RealInput Xi_internal[Medium.nXi](
    each final unit = "kg/kg")
    "Internal connector for water vapor concentration in kg/kg total air";

  Modelica.Blocks.Interfaces.RealInput X_w_internal(
    final unit = "kg/kg")
    "Internal connector for water vapor concentration in kg/kg total air";

   // Conditional connectors for the backward flowing medium
  output AixLib.Fluid.FMI.Interfaces.FluidProperties bacPro_internal(
    redeclare final package Medium = Medium)
    "Internal connector for fluid properties for back flow";

  Modelica.Blocks.Interfaces.RealOutput TAirZon_internal(
    final unit="K",
    displayUnit="degC")
    "Conditinal connector for zone air temperature";

  Modelica.Blocks.Interfaces.RealOutput X_wZon_internal(
    final unit="kg/kg")
    "Internal connector for zone water vapor mass fraction";

  Modelica.Blocks.Interfaces.RealOutput X_wZon_internal2(
    final unit="kg/kg") = 0
    if Medium.nXi == 0 or not allowFlowReversal
    "Internal connector for zone water vapor mass fraction, required if X_wZon is removed";
  Modelica.Blocks.Interfaces.RealOutput CZon_internal[Medium.nC]
    "Internal connector for trace substances";
equation
  // Conditional connectors
  connect(Xi_internal, Xi);
  if Medium.nXi == 0 then
    Xi_internal = zeros(Medium.nXi);
  end if;

  outlet.m_flow = m_flow;
  // If m_flow <= 0, output default properties.
  // This avoids that changes in state variables of the return
  // air are propagated to the room model which may trigger an
  // evaluation of the room ODE, even though Q=max(0, m_flow) c_p (TSup-TZon).
  connect(outlet.forward.X_w,  X_w_internal);

  if m_flow >= 0 then
    outlet.forward.T = Medium.temperature_phX(
    p=Medium.p_default,
    h=h,
    X=cat(1, Xi_internal, {1-sum(Xi_internal)}));
    // Xi internal has 1 or zero components, hence we can use the sum.
    X_w_internal = sum(Xi_internal);
    outlet.forward.C = C;
  else
    outlet.forward.T = Medium.T_default;
    X_w_internal = Medium.X_default[1];
    outlet.forward.C  = zeros(Medium.nC);
  end if;

  // Connectors for backward flow
  connect(TAirZon_internal, TAirZon);
  bacPro_internal.T = TAirZon_internal;

  connect(bacPro_internal.X_w, X_wZon_internal);

  connect(CZon_internal, CZon);
  bacPro_internal.C = CZon_internal;

  connect(X_wZon_internal, X_wZon);
  connect(X_wZon_internal, X_wZon_internal2);
  if not allowFlowReversal then
    TAirZon_internal = Medium.T_default;
    CZon_internal = zeros(Medium.nC);
  end if;

  connect(outlet.backward, bacPro_internal);
end AirToOutlet;


within AixLib.Fluid.FMI.Conversion;
block InletToAir
  "Conversion from real signals for a fluid to a AixLib.Fluid.FMI.Interfaces.Inlet connector"
  extends Modelica.Blocks.Icons.Block;

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

  parameter Boolean allowFlowReversal = true
    "= true to allow flow reversal, false restricts to design direction (inlet -> outlet)"

  AixLib.Fluid.FMI.Interfaces.Inlet inlet(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_p_in=false) "Fluid outlet"

  Modelica.Blocks.Interfaces.RealInput TAirZon(
    final unit="K",
    displayUnit="degC")
    if allowFlowReversal
    "Zone air temperature"
  Modelica.Blocks.Interfaces.RealInput X_wZon(
    final unit="kg/kg")
    if Medium.nXi > 0 and allowFlowReversal
    "Zone air water mass fraction per total air mass"
  Modelica.Blocks.Interfaces.RealInput CZon[Medium.nC](
    final quantity=Medium.extraPropertiesNames)
    if allowFlowReversal
    "Zone air trace substances"

  Modelica.Blocks.Interfaces.RealOutput m_flow(
    final unit="kg/s") "Mass flow rate of the inlet"

  Modelica.Blocks.Interfaces.RealOutput T(final unit="K") "Temperature of the inlet"

  Modelica.Blocks.Interfaces.RealOutput X_w(final unit="kg/kg")
    if Medium.nXi > 0
    "Water mass fraction per total air mass of the inlet"

  Modelica.Blocks.Interfaces.RealOutput C[Medium.nC](
    final quantity=Medium.extraPropertiesNames)
    "Trace substances of the inlet"

protected
  input AixLib.Fluid.FMI.Interfaces.FluidProperties bacPro_internal(
    redeclare final package Medium = Medium)
    "Internal connector for fluid properties for back flow";

  Modelica.Blocks.Interfaces.RealInput TAirZon_internal(
    final unit="K",
    displayUnit="degC")
    "Conditinal connector for zone air temperature";

  Modelica.Blocks.Interfaces.RealInput X_wZon_internal(
    final unit="kg/kg")
    "Internal connector for zone water vapor mass fraction";

  Modelica.Blocks.Interfaces.RealInput X_wZon_internal2(
    final unit="kg/kg") = 0
    if Medium.nXi == 0 or not allowFlowReversal
    "Internal connector for zone water vapor mass fraction, required if X_wZon is removed";
  Modelica.Blocks.Interfaces.RealInput CZon_internal[Medium.nC]
    "Internal connector for trace substances";
equation
  // Conditional connectors
  connect(TAirZon_internal, TAirZon);
  bacPro_internal.T = TAirZon_internal;

  connect(bacPro_internal.X_w, X_wZon_internal);
  connect(CZon_internal, CZon);

  bacPro_internal.C = CZon_internal;

  connect(X_wZon_internal, X_wZon);
  connect(X_wZon_internal, X_wZon_internal2);
  if not allowFlowReversal then
    TAirZon_internal = Medium.T_default;
    CZon_internal = zeros(Medium.nC);
  end if;

  connect(inlet.backward, bacPro_internal);

  // Mass flow rate
  m_flow = inlet.m_flow;

  // Temperature
  T = inlet.forward.T;

  // Vapor concentration
 // X_w_internal = inlet.forward.X_w;
  connect(inlet.forward.X_w, X_w);

  // Species concentration
  C = inlet.forward.C;
end InletToAir;


within AixLib.Fluid.FMI;
package Conversion "Package with blocks that convert between different connectors"
  extends Modelica.Icons.Package;
end Conversion;


within AixLib.Fluid.FMI.Conversion.Validation;
model AirToOutlet "Validation model for air to outlet conversion"
  extends Modelica.Icons.Example;

  parameter Boolean allowFlowReversal = false
    "= true to allow flow reversal, false restricts to design direction (inlet -> outlet)"

  Modelica.Blocks.Sources.Constant m_flow(k=0.2) "Mass flow rate"
  Modelica.Blocks.Sources.Constant h(k=3E5) "Specific enthalpy"
  Modelica.Blocks.Sources.Constant Xi[1](k={0.01}) "Water vapor concentration"
  Modelica.Blocks.Sources.Constant C[1](k={1E-5}) "Trace substances"

  AixLib.Fluid.FMI.Conversion.AirToOutlet conAirNoC(
    redeclare package Medium = AixLib.Media.Air,
    final allowFlowReversal=allowFlowReversal)
    "Converter for air without trace substances"

  AixLib.Fluid.FMI.Conversion.AirToOutlet conAirWithC(
    redeclare package Medium = AixLib.Media.Air(extraPropertiesNames={"CO2"}),
    final allowFlowReversal=allowFlowReversal)
    "Converter for air with trace substances"
  AixLib.Fluid.FMI.Conversion.AirToOutlet conDryAirNoC(
    redeclare package Medium = Modelica.Media.Air.SimpleAir,
    final allowFlowReversal=allowFlowReversal)
    "Converter for dry air without trace substances"
  AixLib.Fluid.FMI.Conversion.AirToOutlet conDryAirWithC(
    redeclare package Medium = Modelica.Media.Air.SimpleAir(extraPropertiesNames={"CO2"}),
    final allowFlowReversal=allowFlowReversal)
    "Converter for dry air with trace substances"
equation
  connect(m_flow.y, conAirNoC.m_flow)
  connect(h.y, conAirNoC.h)
  connect(Xi.y, conAirNoC.Xi)
  connect(m_flow.y, conAirWithC.m_flow)
  connect(h.y, conAirWithC.h)
  connect(Xi.y, conAirWithC.Xi)
  connect(m_flow.y, conDryAirNoC.m_flow)
  connect(h.y, conDryAirNoC.h)
  connect(m_flow.y, conDryAirWithC.m_flow)
  connect(h.y, conDryAirWithC.h)
  connect(C.y, conAirWithC.C)
  connect(C.y, conDryAirWithC.C)
end AirToOutlet;


within AixLib.Fluid.FMI.Conversion.Validation;
model AirToOutletFlowReversal
  "Validation model for air to outlet converter with flow reversal enabled"
  extends AixLib.Fluid.FMI.Conversion.Validation.AirToOutlet(
    allowFlowReversal = true);
  BoundaryCondition bouAirNoC(
    redeclare package Medium = AixLib.Media.Air (
      X_default={0.015, 0.985}))
      "Boundary condition"
  BoundaryCondition bouAirWithC(
    redeclare package Medium = AixLib.Media.Air (
    X_default={0.015, 0.985},
    extraPropertiesNames={"CO2"}))
   "Boundary condition"
  BoundaryCondition bouDryAirNoC(
    redeclare package Medium = Modelica.Media.Air.SimpleAir)
    "Boundary condition"
  BoundaryCondition bouDryAirWithC(
    redeclare package Medium = Modelica.Media.Air.SimpleAir (
      extraPropertiesNames={"CO2"}))
     "Boundary condition"

protected
  model BoundaryCondition
    extends Modelica.Blocks.Icons.Block;

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

    final parameter Boolean use_p_in=false
      "= true to use a pressure from connector, false to output Medium.p_default";

    Adaptors.Inlet bouInlAirNoC(
      redeclare package Medium = Medium,
      allowFlowReversal=true,
      use_p_in=false)                "Boundary condition for air inlet"
    Sources.Boundary_pT bou(
      redeclare package Medium = Medium,
      nPorts=1,
      p=Medium.p_default + 1000,
      T=298.15,
      C=fill(0.7, Medium.nC)) "Boundary condition"
    Interfaces.Inlet inlet(
      use_p_in=use_p_in,
      redeclare package Medium = Medium,
      allowFlowReversal=true)
      "Fluid inlet"

    FixedResistances.PressureDrop res(
      redeclare package Medium = Medium,
      m_flow_nominal=0.1,
      dp_nominal=1000,
      linearized=true) "Flow resistance"
  equation
    connect(bouInlAirNoC.inlet, inlet)
    connect(res.port_b, bou.ports[1])
    connect(res.port_a, bouInlAirNoC.port_b)
  end BoundaryCondition;

equation
  connect(bouAirNoC.inlet, conAirNoC.outlet)
  connect(bouAirWithC.inlet, conAirWithC.outlet)
  connect(bouDryAirNoC.inlet, conDryAirNoC.outlet)
  connect(bouDryAirWithC.inlet, conDryAirWithC.outlet)
end AirToOutletFlowReversal;


within AixLib.Fluid.FMI.Conversion.Validation;
model InletToAirDryAir
  "Validation model for inlet to air with dry air medium"
  extends AixLib.Fluid.FMI.Conversion.Validation.InletToAirMoistAir(
    redeclare replaceable package Medium = Modelica.Media.Air.SimpleAir);
end InletToAirDryAir;


within AixLib.Fluid.FMI.Conversion.Validation;
model InletToAirDryAirCO2
  "Validation model for inlet to air with dry air medium and CO2"
  extends AixLib.Fluid.FMI.Conversion.Validation.InletToAirMoistAirCO2(
    redeclare replaceable package Medium = Modelica.Media.Air.SimpleAir(extraPropertiesNames={"CO2"}));
end InletToAirDryAirCO2;


within AixLib.Fluid.FMI.Conversion.Validation;
model InletToAirMoistAir
  "Validation model for inlet to AixLib.Media.Air conversion without trace substances"
  extends Modelica.Icons.Example;

  replaceable package Medium = AixLib.Media.Air
     constrainedby Modelica.Media.Interfaces.PartialMedium "Medium model";
  parameter Boolean use_p_in = false
    "= true to use a pressure from connector, false to output Medium.p_default"

  Modelica.Blocks.Sources.Constant m_flow(k=0.2) "Mass flow rate"
  Modelica.Blocks.Sources.Constant T(k=295.13) "Room temperature"

  AixLib.Fluid.FMI.Conversion.InletToAir conAir(
    redeclare package Medium = Medium, allowFlowReversal=false)
                                       "Converter for air"

  Source_T sou(
    redeclare package Medium = Medium,
    use_p_in=use_p_in,
    allowFlowReversal=false)
    "Source for mass flow rate and pressure"
  Modelica.Blocks.Sources.Constant pIn(k=100000) "Inlet pressure"
  Modelica.Blocks.Sources.Constant X_w_in(k=0.01) "Inlet mass fraction"
  Modelica.Blocks.Sources.Constant C[Medium.nC](each k=0.01)
  if Medium.nC > 0 "Trace substances for forward flow"
  AixLib.Fluid.FMI.Conversion.InletToAir conAirRevFlo(redeclare package
      Medium = Medium, allowFlowReversal=true)
    "Converter for air with reverse flow"
  Source_T souRev(
    redeclare package Medium = Medium,
    use_p_in=use_p_in,
    allowFlowReversal=true)
    "Source for mass flow rate and pressure with reverse flow"
  Modelica.Blocks.Sources.Constant TRev(k=283.15)
    "Temperature for reverse flow"
  Modelica.Blocks.Sources.Constant X_w(k=0.012)
    "Mass flow fraction for reverse flow"
equation
  connect(m_flow.y, sou.m_flow_in)
  connect(sou.outlet, conAir.inlet)
  connect(sou.T_in, T.y)
  connect(pIn.y, sou.p_in)
  connect(X_w_in.y, sou.X_w_in)
  connect(C.y, sou.C_in)
  connect(m_flow.y, souRev.m_flow_in)
  connect(souRev.outlet, conAirRevFlo.inlet)
  connect(souRev.T_in, T.y)
  connect(pIn.y, souRev.p_in)
  connect(X_w_in.y, souRev.X_w_in)
  connect(C.y, souRev.C_in)
  connect(TRev.y, conAirRevFlo.TAirZon)
  connect(X_w.y, conAirRevFlo.X_wZon)
end InletToAirMoistAir;


within AixLib.Fluid.FMI.Conversion.Validation;
model InletToAirMoistAirCO2
  "Validation model for inlet to AixLib.Media.Air conversion with C02 trace substances"
  extends AixLib.Fluid.FMI.Conversion.Validation.InletToAirDryAir(
    redeclare replaceable package Medium = AixLib.Media.Air(extraPropertiesNames={"CO2"}));
  Modelica.Blocks.Sources.Constant CRev[Medium.nC](each k=0.8)
              "Trace substance for reverse flow"
equation
  connect(CRev.y, conAirRevFlo.CZon)
end InletToAirMoistAirCO2;


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


within AixLib.Fluid.FMI.ExportContainers;
partial block HVACZone
  "Partial block to export an HVAC system that has no radiative component and that serves multiple zones as an FMU"

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

  // Set allowFlowReversal = true to get access to the states of the zone.
  Interfaces.Outlet fluPor[size(hvacAda.fluPor, 1)](
    redeclare each final package Medium = Medium,
    each final use_p_in=false,
    each final allowFlowReversal=true) "Fluid connector"

  Modelica.Blocks.Interfaces.RealInput TRadZon(
    final unit="K",
    displayUnit="degC") "Radiative temperature of the zone"

  Modelica.Blocks.Interfaces.RealOutput QGaiRad_flow(final unit="W")
    "Radiant heat input into zone (positive if heat gain)"

  Modelica.Blocks.Interfaces.RealOutput QGaiSenCon_flow(final unit="W")
    "Convective sensible heat input into zone (positive if heat gain)"

  Modelica.Blocks.Interfaces.RealOutput QGaiLat_flow(final unit="W")
    "Latent heat input into zone (positive if heat gain)"

  Adaptors.HVAC hvacAda(redeclare final package Medium = Medium)
    "Adapter between the HVAC supply and return air, and its connectors for the FMU"

equation
  connect(hvacAda.fluPor, fluPor)
end HVACZone;


within AixLib.Fluid.FMI.ExportContainers;
partial block HVACZones
  "Partial block to export an HVAC system that has no radiative component and that serves multiple zones as an FMU"

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"
  parameter Integer nZon(min=1)
    "Number of thermal zones served by the HVAC system";

  parameter Integer nPorts(min=2)
    "Number of fluid ports for each zone (must be the same for every zone)";

  // Set allowFlowReversal = true to get access to the states of the zone.
  Interfaces.Outlet fluPor[nZon, nPorts](
    redeclare each final package Medium = Medium,
    each final use_p_in = false,
    each final allowFlowReversal = true) "Fluid connectors"

  Modelica.Blocks.Interfaces.RealInput TRadZon[nZon](
    each final unit="K",
    each displayUnit="degC") "Radiative temperature of the zone"

  Modelica.Blocks.Interfaces.RealOutput QGaiRad_flow[nZon](each final unit="W")
    "Radiant heat input into the zones (positive if heat gain)"

  Modelica.Blocks.Interfaces.RealOutput QGaiSenCon_flow[nZon](each final unit="W")
    "Convective sensible heat input into the zones (positive if heat gain)"

  Modelica.Blocks.Interfaces.RealOutput QGaiLat_flow[nZon](each final unit="W")
    "Latent heat input into the zones (positive if heat gain)"

  Adaptors.HVAC hvacAda[nZon](redeclare each final package Medium =
        Medium, each final nPorts=nPorts)
    "Adapter between the HVAC supply and return air, and its connectors for the FMU"

equation
  for iZon in 1:nZon loop
    for iPor in 1:nPorts loop
      connect(hvacAda[iZon].fluPor[iPor], fluPor[iZon, iPor])
    end for;
  end for;
end HVACZones;


within AixLib.Fluid.FMI;
package ExportContainers "Package with containers to export thermofluid flow models"
  extends Modelica.Icons.Package;
end ExportContainers;


within AixLib.Fluid.FMI.ExportContainers;
partial block PartialTwoPort
  "Partial block to be used as a container to export a thermofluid flow model with two ports"
  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

  parameter Boolean allowFlowReversal = true
    "= true to allow flow reversal, false restricts to design direction (inlet -> outlet)"

  parameter Boolean use_p_in = true
    "= true to use a pressure from connector, false to output Medium.p_default"

  Interfaces.Inlet inlet(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_p_in=use_p_in) "Fluid inlet"

  Interfaces.Outlet outlet(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_p_in=use_p_in) "Fluid outlet"
end PartialTwoPort;


within AixLib.Fluid.FMI.ExportContainers;
block ReplaceableTwoPort
  "Container to export thermofluid flow models with two ports as an FMU"
  extends AixLib.Fluid.FMI.ExportContainers.PartialTwoPort;
  replaceable AixLib.Fluid.Interfaces.PartialTwoPort com
    constrainedby AixLib.Fluid.Interfaces.PartialTwoPort(
      redeclare final package Medium = Medium,
      final allowFlowReversal=allowFlowReversal)
    "Component that holds the actual model"

  Modelica.Blocks.Sources.RealExpression dpCom(y=com.port_a.p - com.port_b.p)
    if use_p_in "Pressure drop of the component"

protected
  AixLib.Fluid.FMI.Adaptors.Inlet bouIn(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_p_in=use_p_in) "Boundary model for inlet"

  AixLib.Fluid.FMI.Adaptors.Outlet bouOut(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_p_in=use_p_in) "Boundary component for outlet"

  Modelica.Blocks.Math.Feedback pOut
    if use_p_in "Pressure at component outlet"
equation
  connect(pOut.u1, bouIn.p)
  connect(com.port_a, bouIn.port_b)
  connect(com.port_b, bouOut.port_a)
  connect(inlet, bouIn.inlet)
  connect(bouOut.outlet, outlet)
  connect(dpCom.y, pOut.u2)
  connect(pOut.y, bouOut.p)
end ReplaceableTwoPort;


within AixLib.Fluid.FMI.ExportContainers;
partial block ThermalZone
  "Partial block to export a model of a thermal zone as an FMU"
  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

  parameter Integer nPorts(min=2) "Number of fluid ports";

  Interfaces.Inlet fluPor[nPorts](
    redeclare each final package Medium = Medium,
    each final use_p_in=false,
    each final allowFlowReversal=true) "Fluid connector"

  Adaptors.ThermalZone theZonAda(
    redeclare final package Medium = Medium,
    final nPorts=nPorts)
    "Adapter between the HVAC supply and return air, and its connectors for the FMU"

equation
  connect(theZonAda.fluPor, fluPor)
end ThermalZone;


within AixLib.Fluid.FMI.ExportContainers;
partial block ThermalZones
  "Partial block to export a model of multiple thermal zones as an FMU"

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

  parameter Integer nZon(min=1)
    "Number of thermal zones in this container";
  parameter Integer nPorts(min=2)
    "Number of fluid ports for each zone (must be the same for every zone)";

  Interfaces.Inlet fluPor[nZon, nPorts](
    redeclare each final package Medium = Medium,
    each final use_p_in=false,
    each final allowFlowReversal=true) "Fluid connectors"

  Adaptors.ThermalZone theZonAda[nZon](
    redeclare each final package Medium = Medium,
    each final nPorts=nPorts)
    "Adapter between the HVAC supply and return air, and its connectors for the FMU"

equation
  for iZon in 1:nZon loop
    for iPor in 1:nPorts loop
      connect(theZonAda[iZon].fluPor[iPor], fluPor[iZon, iPor])
    end for;
  end for;
end ThermalZones;


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


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
block Fan "Declaration of an FMU that exports a fan"
   extends AixLib.Fluid.FMI.ExportContainers.ReplaceableTwoPort(
     redeclare replaceable package Medium =  AixLib.Media.Air,
     redeclare final Movers.FlowControlled_dp com(
      nominalValuesDefineDefaultPressureCurve=true,
      final m_flow_nominal=m_flow_nominal,
      final use_inputFilter=
                          false,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState));

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.01
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal(displayUnit="Pa")
     = 500 "Pressure drop at nominal mass flow rate";

  Modelica.Blocks.Interfaces.RealInput dp_in(min=0, final unit="Pa")
    "Prescribed pressure rise"

equation
  connect(dp_in, com.dp_in)
end Fan;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
block FlowSplitter_u "Declaration of an FMU that exports a flow splitter"
   extends AixLib.Fluid.FMI.FlowSplitter_u(
     redeclare replaceable package Medium = AixLib.Media.Air,
        nout=2,
        m_flow_nominal={0.1, 0.2},
        allowFlowReversal(start=false));
end FlowSplitter_u;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
block HeaterCooler_u
  "Declaration of an FMU that exports an ideal heater or cooler with prescribed heat flow rate"
   extends AixLib.Fluid.FMI.ExportContainers.ReplaceableTwoPort(
     redeclare replaceable package Medium = AixLib.Media.Air,
     redeclare final AixLib.Fluid.HeatExchangers.HeaterCooler_u com(
      final m_flow_nominal=m_flow_nominal,
      final dp_nominal=if use_p_in then dp_nominal else 0,
      final Q_flow_nominal=Q_flow_nominal,
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState));

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.01
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal(displayUnit="Pa")=
       0 "Pressure";
  parameter Modelica.Units.SI.HeatFlowRate Q_flow_nominal=100
    "Heat flow rate at u=1, positive for heating";
  Modelica.Blocks.Interfaces.RealInput u(min=0, max=1, unit="1")
    "Control input"
equation
  connect(com.u, u)
end HeaterCooler_u;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
block Heater_T
  "Declaration of an FMU that exports an ideal heater or cooler with prescribed outlet temperature"
   extends AixLib.Fluid.FMI.ExportContainers.ReplaceableTwoPort(
     redeclare replaceable package Medium = AixLib.Media.Air,
     redeclare final AixLib.Fluid.HeatExchangers.Heater_T com(
      final m_flow_nominal=m_flow_nominal,
      final dp_nominal=if use_p_in then dp_nominal else 0,
      final QMax_flow=QMax_flow,
      final energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState));

  parameter Modelica.Units.SI.HeatFlowRate QMax_flow=Modelica.Constants.inf
    "Maximum heat flow rate for heating (positive)";
  parameter Modelica.Units.SI.HeatFlowRate QMin_flow=-Modelica.Constants.inf
    "Maximum heat flow rate for cooling (negative)";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.01
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal(displayUnit="Pa")
     = 0 "Pressure";

  Modelica.Blocks.Interfaces.RealInput TSet(unit="K", displayUnit="degC")
    "Set point temperature of the fluid that leaves port_b"

  Modelica.Blocks.Interfaces.RealOutput Q_flow(unit="W")
    "Heat added to the fluid (if flow is from port_a to port_b)"

equation
  connect(com.TSet, TSet)
  connect(com.Q_flow, Q_flow)
end Heater_T;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
block Humidifier_u
  "Declaration of an FMU that exports an ideal humidifier"
   extends AixLib.Fluid.FMI.ExportContainers.ReplaceableTwoPort(
     redeclare replaceable package Medium = AixLib.Media.Air,
     redeclare final AixLib.Fluid.Humidifiers.Humidifier_u com(
      final m_flow_nominal=m_flow_nominal,
      final dp_nominal=if use_p_in then dp_nominal else 0,
      final mWat_flow_nominal=mWat_flow_nominal,
      final energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState));

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

  parameter Modelica.Units.SI.PressureDifference dp_nominal(displayUnit="Pa")
     = 0 "Pressure";

  parameter Modelica.Units.SI.MassFlowRate mWat_flow_nominal=0.01*0.005
    "Water mass flow rate at u=1, positive for humidification";

  Modelica.Blocks.Interfaces.RealInput u(min=0, max=1, unit="1")
    "Control input"
equation
  connect(com.u, u)
end Humidifier_u;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
block HVACZone
  "Declaration of an FMU that exports a simple convective only HVAC system"
  extends AixLib.Fluid.FMI.ExportContainers.HVACZone(
    redeclare final package Medium = MediumA, hvacAda(nPorts=2));

  replaceable package MediumA = AixLib.Media.Air "Medium for air";
  replaceable package MediumW = AixLib.Media.Water "Medium for water";

  parameter Boolean allowFlowReversal = false
    "= true to allow flow reversal, false restricts to design direction (inlet -> outlet)"

  //////////////////////////////////////////////////////////
  // Heat recovery effectiveness
  parameter Real eps = 0.8 "Heat recovery effectiveness";

  /////////////////////////////////////////////////////////
  // Design air conditions
  parameter Modelica.Units.SI.Temperature TASup_nominal=291.15
    "Nominal air temperature supplied to room";
  parameter Modelica.Units.SI.DimensionlessRatio wASup_nominal=0.012
    "Nominal air humidity ratio supplied to room [kg/kg] assuming 90% relative humidity";
  parameter Modelica.Units.SI.Temperature TRooSet=297.15
    "Nominal room air temperature";
  parameter Modelica.Units.SI.Temperature TOut_nominal=303.15
    "Design outlet air temperature";
  parameter Modelica.Units.SI.Temperature THeaRecLvg=TOut_nominal - eps*(
      TOut_nominal - TRooSet) "Air temperature leaving the heat recovery";
  parameter Modelica.Units.SI.DimensionlessRatio wHeaRecLvg=0.0135
    "Air humidity ratio leaving the heat recovery [kg/kg]";

  /////////////////////////////////////////////////////////
  // Cooling loads and air mass flow rates
  parameter Real UA(unit="W/K") = 10E3 "Average UA-value of the room";
  parameter Modelica.Units.SI.HeatFlowRate QRooInt_flow=1000
    "Internal heat gains of the room";
  parameter Modelica.Units.SI.HeatFlowRate QRooC_flow_nominal=-QRooInt_flow -
      UA/30*(TOut_nominal - TRooSet) "Nominal cooling load of the room";
  parameter Modelica.Units.SI.MassFlowRate mA_flow_nominal=1.3*
      QRooC_flow_nominal/1006/(TASup_nominal - TRooSet)
    "Nominal air mass flow rate, increased by factor 1.3 to allow for recovery after temperature setback";
  parameter Modelica.Units.SI.TemperatureDifference dTFan=2
    "Estimated temperature raise across fan that needs to be made up by the cooling coil";
  parameter Modelica.Units.SI.HeatFlowRate QCoiC_flow_nominal=mA_flow_nominal*(
      TASup_nominal - THeaRecLvg - dTFan)*1006 + mA_flow_nominal*(wASup_nominal
       - wHeaRecLvg)*2458.3e3
    "Cooling load of coil, taking into account outside air sensible and latent heat removal";

  /////////////////////////////////////////////////////////
  // Water temperatures and mass flow rates
  parameter Modelica.Units.SI.Temperature TWSup_nominal=285.15
    "Water supply temperature";
  parameter Modelica.Units.SI.Temperature TWRet_nominal=289.15
    "Water return temperature";
  parameter Modelica.Units.SI.MassFlowRate mW_flow_nominal=-QCoiC_flow_nominal/
      (TWRet_nominal - TWSup_nominal)/4200 "Nominal water mass flow rate";
  /////////////////////////////////////////////////////////
  // HVAC models
  Modelica.Blocks.Sources.Constant zero(k=0) "Zero output signal"
  Movers.FlowControlled_m_flow fan(
    redeclare package Medium = MediumA,
    m_flow_nominal=mA_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=allowFlowReversal,
    nominalValuesDefineDefaultPressureCurve=true)
                                         "Supply air fan"
  HeatExchangers.ConstantEffectiveness hex(
    redeclare package Medium1 = MediumA,
    redeclare package Medium2 = MediumA,
    m1_flow_nominal=mA_flow_nominal,
    m2_flow_nominal=mA_flow_nominal,
    dp1_nominal=200,
    dp2_nominal=200,
    eps=eps,
    allowFlowReversal1=allowFlowReversal,
    allowFlowReversal2=allowFlowReversal) "Heat recovery"

  replaceable AixLib.Fluid.HeatExchangers.ConstantEffectiveness cooCoi(
    dp1_nominal=6000,
    dp2_nominal=200,
    show_T=true,
    allowFlowReversal1=allowFlowReversal,
    allowFlowReversal2=allowFlowReversal) constrainedby
    AixLib.Fluid.Interfaces.PartialFourPortInterface(
        redeclare package Medium1 = MediumW,
        redeclare package Medium2 = MediumA,
        m1_flow_nominal=mW_flow_nominal,
        m2_flow_nominal=mA_flow_nominal)
    "Cooling coil (with sensible cooling only)"

  AixLib.Fluid.Sources.Outside out(
    redeclare package Medium = MediumA,
    nPorts=2) "Outside air boundary condition"
  Sources.MassFlowSource_T souWat(
    redeclare package Medium = MediumW,
    nPorts=1,
    use_m_flow_in=true,
    T=TWSup_nominal) "Source for water flow rate"
  AixLib.Fluid.Sources.Boundary_pT sinWat(
    redeclare package Medium = MediumW, nPorts=1) "Sink for water circuit"
  Modelica.Blocks.Sources.Constant mAir_flow(k=mA_flow_nominal)
    "Fan air flow rate"
  Sensors.TemperatureTwoPort senTemHXOut(
    redeclare package Medium = MediumA,
    m_flow_nominal=mA_flow_nominal,
    allowFlowReversal=allowFlowReversal)
    "Temperature sensor for heat recovery outlet on supply side"
  Sensors.TemperatureTwoPort senTemSupAir(
    redeclare package Medium = MediumA,
    m_flow_nominal=mA_flow_nominal,
    allowFlowReversal=allowFlowReversal) "Temperature sensor for supply air"
  Modelica.Blocks.Logical.OnOffController con(bandwidth=1)
    "Controller for coil water flow rate"
  Modelica.Blocks.Sources.Constant TRooSetPoi(k=TRooSet)
    "Room temperature set point"
  Modelica.Blocks.Math.BooleanToReal mWat_flow(
    realTrue = 0,
    realFalse = mW_flow_nominal)
    "Conversion from boolean to real for water flow rate"
  BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    pAtmSou=AixLib.BoundaryConditions.Types.DataSource.Parameter,
    TDryBul=TOut_nominal,
    filNam=Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
    TDryBulSou=AixLib.BoundaryConditions.Types.DataSource.File,
    computeWetBulbTemperature=false) "Weather data reader"
  BoundaryConditions.WeatherData.Bus weaBus "Weather data bus"
  Modelica.Blocks.Interfaces.RealOutput TOut(final unit="K")
    "Outdoor temperature"
  FixedResistances.PressureDrop resSup1(
    redeclare package Medium = MediumA,
    linearized=true,
    dp_nominal=10,
    m_flow_nominal=mA_flow_nominal) "Fixed resistance for supply air inlet"
  FixedResistances.PressureDrop resRet1(
    redeclare package Medium = MediumA,
    dp_nominal=200,
    linearized=true,
    m_flow_nominal=mA_flow_nominal) "Fixed resistance for return air duct"
equation
  connect(zero.y, QGaiRad_flow)
  connect(zero.y, QGaiSenCon_flow)
  connect(zero.y, QGaiLat_flow)
  connect(out.ports[1],hex. port_a1)
  connect(out.ports[2],hex. port_b2)
  connect(souWat.ports[1],cooCoi. port_a1)
  connect(fan.m_flow_in,mAir_flow. y)
  connect(hex.port_b1,senTemHXOut. port_a)
  connect(senTemHXOut.port_b,cooCoi. port_a2)
  connect(cooCoi.port_b2,senTemSupAir. port_a)
  connect(senTemSupAir.port_b,fan. port_a)
  connect(TRooSetPoi.y,con. reference)
  connect(con.y,mWat_flow. u)
  connect(mWat_flow.y,souWat. m_flow_in)
  connect(weaDat.weaBus,weaBus)
  connect(TOut,weaBus. TDryBul)
  connect(sinWat.ports[1], cooCoi.port_b1)
  connect(con.u, hvacAda.TAirZon[1])
  connect(resSup1.port_b, hvacAda.ports[1])
  connect(resRet1.port_a, hvacAda.ports[2])
  connect(fan.port_b, resSup1.port_a)
  connect(resRet1.port_b, hex.port_a2)
  connect(out.weaBus, weaBus)
end HVACZone;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
block HVACZones
  "Declaration of an FMU that exports a simple convective only HVAC system for two zones"
  extends AixLib.Fluid.FMI.ExportContainers.HVACZones(
    redeclare final package Medium = MediumA,
    nZon = 2,
    nPorts = 3);

  replaceable package MediumA = AixLib.Media.Air "Medium for air";
  replaceable package MediumW = AixLib.Media.Water "Medium for water";

  parameter Boolean allowFlowReversal = false
    "= true to allow flow reversal, false restricts to design direction (inlet -> outlet)"

 // parameter Modelica.Units.SI.Volume V=6*10*3 "Room volume";
  //////////////////////////////////////////////////////////
  // Heat recovery effectiveness
  parameter Real eps = 0.8 "Heat recovery effectiveness";

  /////////////////////////////////////////////////////////
  // Design air conditions
  parameter Modelica.Units.SI.Temperature TASup_nominal=291.15
    "Nominal air temperature supplied to room";
  parameter Modelica.Units.SI.DimensionlessRatio wASup_nominal=0.012
    "Nominal air humidity ratio supplied to room [kg/kg] assuming 90% relative humidity";
  parameter Modelica.Units.SI.Temperature TRooSet=297.15
    "Nominal room air temperature";
  parameter Modelica.Units.SI.Temperature TOut_nominal=303.15
    "Design outlet air temperature";
  parameter Modelica.Units.SI.Temperature THeaRecLvg=TOut_nominal - eps*(
      TOut_nominal - TRooSet) "Air temperature leaving the heat recovery";
  parameter Modelica.Units.SI.DimensionlessRatio wHeaRecLvg=0.0135
    "Air humidity ratio leaving the heat recovery [kg/kg]";

  /////////////////////////////////////////////////////////
  // Cooling loads and air mass flow rates
  parameter Real UA(unit="W/K") = 10E3 "Average UA-value of the room";
  parameter Modelica.Units.SI.HeatFlowRate QRooInt_flow=1000
    "Internal heat gains of the room";
  parameter Modelica.Units.SI.HeatFlowRate QRooC_flow_nominal=-QRooInt_flow -
      UA/30*(TOut_nominal - TRooSet) "Nominal cooling load of the room";
  parameter Modelica.Units.SI.MassFlowRate mA_flow_nominal=1.3*
      QRooC_flow_nominal/1006/(TASup_nominal - TRooSet)
    "Nominal air mass flow rate, increased by factor 1.3 to allow for recovery after temperature setback";
  parameter Modelica.Units.SI.TemperatureDifference dTFan=2
    "Estimated temperature raise across fan that needs to be made up by the cooling coil";
  parameter Modelica.Units.SI.HeatFlowRate QCoiC_flow_nominal=mA_flow_nominal*(
      TASup_nominal - THeaRecLvg - dTFan)*1006 + mA_flow_nominal*(wASup_nominal
       - wHeaRecLvg)*2458.3e3
    "Cooling load of coil, taking into account outside air sensible and latent heat removal";

  /////////////////////////////////////////////////////////
  // Water temperatures and mass flow rates
  parameter Modelica.Units.SI.Temperature TWSup_nominal=285.15
    "Water supply temperature";
  parameter Modelica.Units.SI.Temperature TWRet_nominal=289.15
    "Water return temperature";
  parameter Modelica.Units.SI.MassFlowRate mW_flow_nominal=-QCoiC_flow_nominal/
      (TWRet_nominal - TWSup_nominal)/4200 "Nominal water mass flow rate";
  /////////////////////////////////////////////////////////
  // HVAC models
  Modelica.Blocks.Sources.Constant zer[nZon](each k=0) "Zero output signal"
  Movers.FlowControlled_m_flow fan(
    redeclare package Medium = MediumA,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=mA_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=allowFlowReversal) "Supply air fan"
  HeatExchangers.ConstantEffectiveness hex(
    redeclare package Medium1 = MediumA,
    redeclare package Medium2 = MediumA,
    m1_flow_nominal=mA_flow_nominal,
    m2_flow_nominal=mA_flow_nominal,
    dp1_nominal=200,
    dp2_nominal=200,
    eps=eps,
    allowFlowReversal1=allowFlowReversal,
    allowFlowReversal2=allowFlowReversal) "Heat recovery"

  replaceable AixLib.Fluid.HeatExchangers.ConstantEffectiveness cooCoi(
    dp1_nominal=6000,
    dp2_nominal=200,
    show_T=true,
    allowFlowReversal1=allowFlowReversal,
    allowFlowReversal2=allowFlowReversal) constrainedby
    AixLib.Fluid.Interfaces.PartialFourPortInterface(
        redeclare package Medium1 = MediumW,
        redeclare package Medium2 = MediumA,
        m1_flow_nominal=mW_flow_nominal,
        m2_flow_nominal=mA_flow_nominal)
    "Cooling coil (with sensible cooling only)"

  AixLib.Fluid.Sources.Outside out(
    redeclare package Medium = MediumA,
    nPorts=3) "Outside air boundary condition"
  Sources.MassFlowSource_T souWat(
    nPorts=1,
    redeclare package Medium = MediumW,
    use_m_flow_in=true,
    T=TWSup_nominal) "Source for water flow rate"
  AixLib.Fluid.Sources.Boundary_pT sinWat(
    redeclare package Medium = MediumW, nPorts=1) "Sink for water circuit"
  Modelica.Blocks.Sources.Constant mAir_flow(k=mA_flow_nominal)
    "Fan air flow rate"
  Sensors.TemperatureTwoPort senTemHXOut(
    redeclare package Medium = MediumA,
    m_flow_nominal=mA_flow_nominal,
    allowFlowReversal=allowFlowReversal)
    "Temperature sensor for heat recovery outlet on supply side"
  Sensors.TemperatureTwoPort senTemSupAir(
    redeclare package Medium = MediumA,
    m_flow_nominal=mA_flow_nominal,
    allowFlowReversal=allowFlowReversal) "Temperature sensor for supply air"
  Modelica.Blocks.Logical.OnOffController con(bandwidth=1)
    "Controller for coil water flow rate"
  Modelica.Blocks.Sources.Constant TRooSetPoi(k=TRooSet)
    "Room temperature set point"
  Modelica.Blocks.Math.BooleanToReal mWat_flow(
    realTrue = 0,
    realFalse = mW_flow_nominal)
    "Conversion from boolean to real for water flow rate"
  BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    pAtmSou=AixLib.BoundaryConditions.Types.DataSource.Parameter,
    TDryBul=TOut_nominal,
    filNam=Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
    TDryBulSou=AixLib.BoundaryConditions.Types.DataSource.File,
    computeWetBulbTemperature=false) "Weather data reader"
  BoundaryConditions.WeatherData.Bus weaBus "Weather data bus"
  Modelica.Blocks.Interfaces.RealOutput TOut(final unit="K")
    "Outdoor temperature"
  Modelica.Blocks.Math.Min min
  Movers.FlowControlled_m_flow fan2(
    redeclare package Medium = MediumA,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    allowFlowReversal=allowFlowReversal,
    nominalValuesDefineDefaultPressureCurve=true,
    m_flow_nominal=mA_flow_nominal/10,
    inputType=AixLib.Fluid.Types.InputType.Constant)
    "Supply air fan that extracts a constant amount of outside air"
  AixLib.Fluid.FixedResistances.PressureDrop res(
    redeclare package Medium = MediumA,
    m_flow_nominal=0.1*mA_flow_nominal,
    dp_nominal=200,
    linearized=true) "Fixed resistance for exhaust air duct"
  AixLib.Fluid.FixedResistances.PressureDrop resSup1(
    redeclare package Medium = MediumA,
    linearized=true,
    m_flow_nominal=0.5*mA_flow_nominal,
    dp_nominal=10) "Fixed resistance for supply air inlet"
  AixLib.Fluid.FixedResistances.PressureDrop resSup2(
    redeclare package Medium = MediumA,
    linearized=true,
    m_flow_nominal=0.5*mA_flow_nominal,
    dp_nominal=10) "Fixed resistance for supply air inlet"
  AixLib.Fluid.FixedResistances.PressureDrop resRet1(
    redeclare package Medium = MediumA,
    dp_nominal=200,
    linearized=true,
    m_flow_nominal=0.5*mA_flow_nominal) "Fixed resistance for return air duct"
  AixLib.Fluid.FixedResistances.PressureDrop resRet2(
    redeclare package Medium = MediumA,
    dp_nominal=200,
    linearized=true,
    m_flow_nominal=0.5*mA_flow_nominal) "Fixed resistance for return air duct"
equation
  connect(zer.y, QGaiRad_flow)
  connect(zer.y, QGaiSenCon_flow)
  connect(zer.y, QGaiLat_flow)
  connect(out.ports[1],hex. port_a1)
  connect(out.ports[2],hex. port_b2)
  connect(souWat.ports[1],cooCoi. port_a1)
  connect(fan.m_flow_in,mAir_flow. y)
  connect(hex.port_b1,senTemHXOut. port_a)
  connect(senTemHXOut.port_b,cooCoi. port_a2)
  connect(cooCoi.port_b2,senTemSupAir. port_a)
  connect(senTemSupAir.port_b,fan. port_a)
  connect(TRooSetPoi.y,con. reference)
  connect(con.y,mWat_flow. u)
  connect(mWat_flow.y,souWat. m_flow_in)
  connect(weaDat.weaBus,weaBus)
  connect(TOut,weaBus. TDryBul)
  connect(sinWat.ports[1], cooCoi.port_b1)
  connect(min.y, con.u)
  connect(fan2.port_b, res.port_a)
  connect(res.port_b, out.ports[3])
  connect(fan.port_b, resSup1.port_a)
  connect(fan.port_b, resSup2.port_a)
  connect(resSup1.port_b, hvacAda[1].ports[1])
  connect(resSup2.port_b, hvacAda[2].ports[1])
  connect(hvacAda[1].ports[3], fan2.port_a)
  connect(hvacAda[2].ports[3], fan2.port_a)
  connect(hvacAda[1].TAirZon[1], min.u1)
  connect(hvacAda[2].TAirZon[1], min.u2)
  connect(resRet1.port_b, hex.port_a2)
  connect(resRet2.port_b, hex.port_a2)
  connect(resRet1.port_a, hvacAda[1].ports[2])
  connect(resRet2.port_a, hvacAda[2].ports[2])
  connect(out.weaBus, weaBus)
end HVACZones;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
block IdealSource_m_flow "Declaration of an FMU that exports a mass flow source and sink"
   extends AixLib.Fluid.FMI.ExportContainers.PartialTwoPort(
     redeclare replaceable package Medium = AixLib.Media.Air);

  Modelica.Blocks.Interfaces.RealInput m_flow_in(final unit="kg/s")
    "Prescribed mass flow rate"

equation
  assert(abs(inlet.m_flow-outlet.m_flow) < 1E-2,
  "The mass flow rate of port_a and port_b is not conserved.
  This indicates a wrong configuration of your system model.");
  outlet.m_flow = m_flow_in;

  // We use connect statements
  // because outlet.backward and inlet.backward
  // is removed if allowFlowReversal=false.
  // The connect is applied on the components of the port
  // to avoid also connecting the mass flow rate which would
  // yield an overdetermined system of equations.
  connect(inlet.forward, outlet.forward);
  connect(outlet.backward, inlet.backward);
  connect(outlet.p, inlet.p);
end IdealSource_m_flow;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
block MixingVolume "Declaration of an FMU that exports a control volume"
  extends AixLib.Fluid.FMI.ExportContainers.PartialTwoPort(
    redeclare package Medium = AixLib.Media.Air);

  parameter Modelica.Units.SI.Volume V=1 "Volume";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.01
    "Nominal mass flow rate";

protected
  AixLib.Fluid.FMI.Adaptors.Inlet bouIn(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal) "Boundary model for inlet"
  AixLib.Fluid.FMI.Adaptors.Outlet bouOut(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal) "Boundary component for outlet"

  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final allowFlowReversal=allowFlowReversal,
    final V=V,
    nPorts=2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    massDynamics=Modelica.Fluid.Types.Dynamics.SteadyState) "Control volume"
equation
  connect(inlet, bouIn.inlet)
  connect(bouOut.outlet, outlet)
  connect(bouIn.p, bouOut.p)
  connect(bouIn.port_b, vol.ports[1])
  connect(vol.ports[2], bouOut.port_a)
end MixingVolume;


Collection of example models

within AixLib.Fluid.FMI.ExportContainers.Examples;
package FMUs "Collection of example models"
  extends Modelica.Icons.ExamplesPackage;
end FMUs;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
block PressureDrop
  "Declaration of an FMU that exports a fixed resistance"
   extends AixLib.Fluid.FMI.ExportContainers.ReplaceableTwoPort(
     redeclare replaceable package Medium = AixLib.Media.Air, redeclare final
      AixLib.Fluid.FixedResistances.PressureDrop com(final m_flow_nominal=
          m_flow_nominal, final dp_nominal=if use_p_in then dp_nominal else 0));

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.01
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal(displayUnit="Pa")
     = 100 "Pressure drop at nominal mass flow rate";
end PressureDrop;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
block ResistanceVolume
  "Declaration of an FMU that exports a flow resistance and control volume"
  extends AixLib.Fluid.FMI.ExportContainers.PartialTwoPort(
    redeclare package Medium = AixLib.Media.Air);

  parameter Modelica.Units.SI.Volume V=1 "Volume";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.01
    "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal=100
    "Nominal pressure drop";

  Modelica.Blocks.Sources.RealExpression dpCom(y=res.port_a.p - res.port_b.p)
    if use_p_in "Pressure drop of the component"

protected
  AixLib.Fluid.FMI.Adaptors.Inlet bouIn(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_p_in=use_p_in) "Boundary model for inlet"
  AixLib.Fluid.FMI.Adaptors.Outlet bouOut(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_p_in=use_p_in) "Boundary component for outlet"

  Modelica.Blocks.Math.Feedback pOut if use_p_in "Pressure at component outlet"

  FixedResistances.PressureDrop res(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final dp_nominal=if use_p_in then dp_nominal else 0,
    final allowFlowReversal=allowFlowReversal) "Flow resistance"
  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    nPorts=2,
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final allowFlowReversal=allowFlowReversal,
    final V=V,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    massDynamics=Modelica.Fluid.Types.Dynamics.SteadyState) "Control volume"

equation
  connect(inlet, bouIn.inlet)
  connect(bouOut.outlet, outlet)
  connect(pOut.u1, bouIn.p)
  connect(pOut.y, bouOut.p)
  connect(res.port_b, vol.ports[1])
  connect(bouIn.port_b, res.port_a)
  connect(dpCom.y, pOut.u2)
  connect(vol.ports[2], bouOut.port_a)
end ResistanceVolume;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
model ThermalZone
  "Declaration of an FMU that exports a thermal zone"
  extends AixLib.Fluid.FMI.ExportContainers.ThermalZone(
    redeclare final package Medium = MediumA,
    nPorts =  2);

  replaceable package MediumA = AixLib.Media.Air "Medium for air";

  parameter Modelica.Units.SI.Volume V=6*10*3 "Room volume";

  /////////////////////////////////////////////////////////
  // Air temperatures at design conditions
  parameter Modelica.Units.SI.Temperature TASup_nominal=273.15 + 18
    "Nominal air temperature supplied to room";
  parameter Modelica.Units.SI.Temperature TRooSet=273.15 + 24
    "Nominal room air temperature";
  parameter Modelica.Units.SI.Temperature TOut_nominal=273.15 + 30
    "Design outlet air temperature";

  /////////////////////////////////////////////////////////
  // Cooling loads and air mass flow rates
  parameter Modelica.Units.SI.HeatFlowRate QRooInt_flow=1000
    "Internal heat gains of the room";
  parameter Modelica.Units.SI.HeatFlowRate QRooC_flow_nominal=-QRooInt_flow -
      10E3/30*(TOut_nominal - TRooSet) "Nominal cooling load of the room";
  parameter Modelica.Units.SI.MassFlowRate mA_flow_nominal=1.3*
      QRooC_flow_nominal/1006/(TASup_nominal - TRooSet)
    "Nominal air mass flow rate, increased by factor 1.3 to allow for recovery after temperature setback";

  BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    pAtmSou=AixLib.BoundaryConditions.Types.DataSource.Parameter,
    TDryBul=TOut_nominal,
    filNam=Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
    TDryBulSou=AixLib.BoundaryConditions.Types.DataSource.File,
    computeWetBulbTemperature=false) "Weather data reader"
  BoundaryConditions.WeatherData.Bus weaBus "Weather data bus"
  Modelica.Blocks.Interfaces.RealOutput TOut(final unit="K")
    "Outdoor temperature"
  Modelica.Blocks.Interfaces.RealOutput TRad(final unit="K")
    "Radiative temperature"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature TAirOut
    "Outside air temperature"
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=10000/30)
    "Thermal conductance with the ambient"
  Modelica.Thermal.HeatTransfer.Sources.FixedHeatFlow preHea(Q_flow=
    QRooInt_flow) "Prescribed heat flow"
  MixingVolumes.MixingVolumeMoistAir vol(
    redeclare package Medium = MediumA,
    m_flow_nominal=mA_flow_nominal,
    V=V,
    mSenFac=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nPorts=2)
  Modelica.Blocks.Sources.Constant mWat_flow(k=0.0) "mass flow rate"
  Sensors.MassFlowRate senMasFlo[nPorts](redeclare final package Medium = MediumA)
    "Mass flow rate sensor to connect thermal adapter with thermal zone."
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSen
    "Temperature sensor (that approximates the radiative temperature by the air temperature)"
equation
  connect(weaDat.weaBus,weaBus)
  connect(theCon.port_b,vol. heatPort)
  connect(preHea.port,vol. heatPort)
  connect(TAirOut.T, weaBus.TDryBul)
  connect(TAirOut.port, theCon.port_a)

  connect(theZonAda.heaPorAir, vol.heatPort)
  connect(TOut, weaBus.TDryBul)
  connect(vol.mWat_flow, mWat_flow.y)
  connect(theZonAda.ports, senMasFlo.port_a)
  connect(senMasFlo.port_b, vol.ports[1:2])
  connect(temSen.port, vol.heatPort)
  connect(temSen.T, TRad)
end ThermalZone;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
model ThermalZones
  "Declaration of an FMU that exports multiple thermal zones"
  extends AixLib.Fluid.FMI.ExportContainers.ThermalZones(
    redeclare final package Medium = MediumA,
    nPorts =  3,
    nZon = 2);

  replaceable package MediumA = AixLib.Media.Air "Medium for air";

  parameter Modelica.Units.SI.Volume V=6*10*3 "Room volume";

  /////////////////////////////////////////////////////////
  // Air temperatures at design conditions
  parameter Modelica.Units.SI.Temperature TASup_nominal=273.15 + 18
    "Nominal air temperature supplied to room";
  parameter Modelica.Units.SI.Temperature TRooSet=273.15 + 24
    "Nominal room air temperature";
  parameter Modelica.Units.SI.Temperature TOut_nominal=273.15 + 30
    "Design outlet air temperature";

  /////////////////////////////////////////////////////////
  // Cooling loads and air mass flow rates
  parameter Modelica.Units.SI.HeatFlowRate QRooInt_flow=1000
    "Internal heat gains of the room";
  parameter Modelica.Units.SI.HeatFlowRate QRooC_flow_nominal=-QRooInt_flow -
      10E3/30*(TOut_nominal - TRooSet) "Nominal cooling load of the room";
  parameter Modelica.Units.SI.MassFlowRate mA_flow_nominal=1.3*
      QRooC_flow_nominal/1006/(TASup_nominal - TRooSet)
    "Nominal air mass flow rate, increased by factor 1.3 to allow for recovery after temperature setback";

  BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    pAtmSou=AixLib.BoundaryConditions.Types.DataSource.Parameter,
    TDryBul=TOut_nominal,
    filNam=Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
    TDryBulSou=AixLib.BoundaryConditions.Types.DataSource.File,
    computeWetBulbTemperature=false) "Weather data reader"
  BoundaryConditions.WeatherData.Bus weaBus "Weather data bus"
  Modelica.Blocks.Interfaces.RealOutput TOut(final unit="K")
    "Outdoor temperature"
  Modelica.Blocks.Interfaces.RealOutput TRad1(final unit="K")
    "Radiative temperature"
  Modelica.Blocks.Interfaces.RealOutput TRad2(final unit="K")
    "Radiative temperature"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature TAirOut
    "Outside air temperature"
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon1(G=10000/30)
    "Thermal conductance with the ambient"
  Modelica.Thermal.HeatTransfer.Sources.FixedHeatFlow preHea1(Q_flow=
        QRooInt_flow) "Prescribed heat flow for internal gains"
  MixingVolumes.MixingVolume vol1(
    redeclare package Medium = MediumA,
    m_flow_nominal=mA_flow_nominal,
    V=V,
    mSenFac=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nPorts=3) "Room volume"
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon2(G=10000/30)
    "Thermal conductance with the ambient"
  Modelica.Thermal.HeatTransfer.Sources.FixedHeatFlow preHea2(Q_flow=
        QRooInt_flow) "Prescribed heat flow for internal gains"
  MixingVolumes.MixingVolume vol2(
    redeclare package Medium = MediumA,
    m_flow_nominal=mA_flow_nominal,
    V=V,
    mSenFac=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nPorts=3) "Room volume"
  Sensors.MassFlowRate senMasFlo[nPorts](redeclare final package Medium = MediumA)
    "Mass flow rate sensor to connect thermal adapter with thermal zone."
  Sensors.MassFlowRate senMasFlo1[nPorts](
                                         redeclare final package Medium = MediumA)
    "Mass flow rate sensor to connect thermal adapter with thermal zone."
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSen1
    "Temperature sensor (that approximates the radiative temperature by the air temperature)"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSen2
    "Temperature sensor (that approximates the radiative temperature by the air temperature)"

equation
  connect(weaDat.weaBus,weaBus)
  connect(theCon1.port_b, vol1.heatPort)
  connect(preHea1.port, vol1.heatPort)
  connect(TAirOut.T, weaBus.TDryBul)
  connect(TAirOut.port, theCon1.port_a)

  connect(TOut, weaBus.TDryBul)
  connect(theCon2.port_b, vol2.heatPort)
  connect(preHea2.port, vol2.heatPort)
  connect(TAirOut.port, theCon2.port_a)
  connect(vol1.heatPort, theZonAda[1].heaPorAir)
  connect(vol2.heatPort, theZonAda[2].heaPorAir)
  connect(theZonAda[2].ports, senMasFlo1.port_a)
  connect(senMasFlo.port_a, theZonAda[1].ports)
  connect(senMasFlo1.port_b, vol2.ports[1:3])
  connect(senMasFlo.port_b, vol1.ports[1:3])
  connect(temSen1.port, vol1.heatPort)
  connect(temSen2.port, vol2.heatPort)
  connect(temSen1.T, TRad1)
  connect(temSen2.T, TRad2)
end ThermalZones;


within AixLib.Fluid.FMI.ExportContainers.Examples.FMUs;
block TwoPortPassThrough
  "Declaration of an FMU that exports a block that simply passes all the inputs to the outputs"
   extends AixLib.Fluid.FMI.ExportContainers.PartialTwoPort(
     redeclare replaceable package Medium = AixLib.Media.Air);

equation
  connect(inlet, outlet)
end TwoPortPassThrough;


Collection of validation models

within AixLib.Fluid.FMI.ExportContainers;
package Validation "Collection of validation models"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


within AixLib.Fluid.FMI.ExportContainers.Validation.FMUs;
block HVACThermalZoneAir1 "Validation model for the convective HVAC system"
  extends AixLib.Fluid.FMI.ExportContainers.HVACZone(
    redeclare package Medium = AixLib.Media.Air,
    hvacAda(nPorts=2));

protected
  Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=1) "Boundary condition"
  FixedResistances.PressureDrop sup(
    redeclare final package Medium = Medium,
    final m_flow_nominal=0.1,
    final dp_nominal=200,
    from_dp=true) "Supply air duct"
  FixedResistances.PressureDrop ret(
    redeclare final package Medium = Medium,
    final m_flow_nominal=0.1,
    final dp_nominal=200) "Return air duct"
  Modelica.Blocks.Sources.Constant zero(k=0) "Zero output signal"
protected
  Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    m_flow=0.1) "Mass flow source"
equation
  connect(bou.ports[1], ret.port_b)
  connect(zero.y, QGaiRad_flow)
  connect(zero.y, QGaiSenCon_flow)
  connect(zero.y, QGaiLat_flow)
  connect(sup.port_b, hvacAda.ports[1])
  connect(ret.port_a, hvacAda.ports[2])
  connect(sou.ports[1], sup.port_a)
end HVACThermalZoneAir1;


within AixLib.Fluid.FMI.ExportContainers.Validation.FMUs;
block HVACThermalZoneAir2 "Validation model for the convective HVAC system"
  extends AixLib.Fluid.FMI.ExportContainers.Validation.FMUs.HVACThermalZoneAir1(
    redeclare package Medium = AixLib.Media.Air(extraPropertiesNames={"CO2"}));
end HVACThermalZoneAir2;


within AixLib.Fluid.FMI.ExportContainers.Validation.FMUs;
block HVACThermalZoneAir3 "Validation model for the convective HVAC system"
  extends AixLib.Fluid.FMI.ExportContainers.Validation.FMUs.HVACThermalZoneAir1(
    redeclare package Medium = AixLib.Media.Air(extraPropertiesNames={"CO2", "VOC", "CH2O"}));
end HVACThermalZoneAir3;


within AixLib.Fluid.FMI.ExportContainers.Validation.FMUs;
block HVACThermalZoneSimpleAir1
  "Validation model for the convective HVAC system"
  extends AixLib.Fluid.FMI.ExportContainers.Validation.FMUs.HVACThermalZoneAir1(
    redeclare package Medium = Modelica.Media.Air.SimpleAir);
end HVACThermalZoneSimpleAir1;


within AixLib.Fluid.FMI.ExportContainers.Validation.FMUs;
block HVACThermalZoneSimpleAir2
  "Validation model for the convective HVAC system"
  extends AixLib.Fluid.FMI.ExportContainers.Validation.FMUs.HVACThermalZoneAir1(
    redeclare package Medium = Modelica.Media.Air.SimpleAir(extraPropertiesNames={"CO2"}));
end HVACThermalZoneSimpleAir2;


within AixLib.Fluid.FMI.ExportContainers.Validation.FMUs;
block HVACThermalZoneSimpleAir3
  "Validation model for the convective HVAC system"
  extends AixLib.Fluid.FMI.ExportContainers.Validation.FMUs.HVACThermalZoneAir1(
    redeclare package Medium = Modelica.Media.Air.SimpleAir(extraPropertiesNames={"CO2", "VOC", "CH2O"}));
end HVACThermalZoneSimpleAir3;


within AixLib.Fluid.FMI.ExportContainers.Validation;
package FMUs "Collection of validation models for FMU export"
  extends Modelica.Icons.ExamplesPackage;
end FMUs;


Validation of simple thermal zone

within AixLib.Fluid.FMI.ExportContainers.Validation.FMUs;
block ThermalZoneAir1 "Validation of simple thermal zone"
  extends AixLib.Fluid.FMI.ExportContainers.ThermalZone(
    redeclare package Medium = AixLib.Media.Air,
    nPorts =  2);

  parameter Modelica.Units.SI.Volume V=6*10*3 "Room volume";

  /////////////////////////////////////////////////////////
  // Air temperatures at design conditions
  parameter Modelica.Units.SI.Temperature TASup_nominal=273.15 + 18
    "Nominal air temperature supplied to room";
  parameter Modelica.Units.SI.Temperature TRooSet=273.15 + 24
    "Nominal room air temperature";
  parameter Modelica.Units.SI.Temperature TOut_nominal=273.15 + 30
    "Design outlet air temperature";

  /////////////////////////////////////////////////////////
  // Cooling loads and air mass flow rates
  parameter Modelica.Units.SI.HeatFlowRate QRooInt_flow=1000
    "Internal heat gains of the room";
  parameter Modelica.Units.SI.HeatFlowRate QRooC_flow_nominal=-QRooInt_flow -
      10E3/30*(TOut_nominal - TRooSet) "Nominal cooling load of the room";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=1.3*
      QRooC_flow_nominal/1006/(TASup_nominal - TRooSet)
    "Nominal air mass flow rate, increased by factor 1.3 to allow for recovery after temperature setback";

  Modelica.Blocks.Interfaces.RealOutput TOut(final unit="K")
    "Outdoor temperature"

  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature TOut1
    "Outside temperature"

  Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=10000/30)
    "Thermal conductance with the ambient"
  Modelica.Thermal.HeatTransfer.Sources.FixedHeatFlow preHea(Q_flow=
    QRooInt_flow) "Prescribed heat flow"
  MixingVolumes.MixingVolume vol(
    redeclare package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    V=V,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nPorts=2,
    mSenFac=3)
  Modelica.Blocks.Sources.Constant TOutSou(k=303.15) "Outdoor temperature"
protected
  FixedResistances.PressureDrop res1(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final dp_nominal=10,
    linearized=true) "Flow resistance"
  FixedResistances.PressureDrop res2(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final dp_nominal=10,
    linearized=true) "Flow resistance"
public
  Sensors.MassFlowRate senMasFlo[nPorts](redeclare final package Medium = Medium)
    "Mass flow rate sensor to connect thermal adapter with thermal zone."
equation
  connect(theCon.port_b,vol. heatPort)
  connect(preHea.port,vol. heatPort)
  connect(TOut1.port,theCon. port_a)
  connect(vol.heatPort, theZonAda.heaPorAir)
  connect(TOutSou.y, TOut1.T)
  connect(TOut, TOutSou.y)
  connect(res2.port_b, vol.ports[1])
  connect(res1.port_b, vol.ports[2])
  connect(senMasFlo.port_a, theZonAda.ports)
  connect(senMasFlo[1].port_b, res1.port_a)
  connect(senMasFlo[2].port_b, res2.port_a)
end ThermalZoneAir1;


Validation of simple thermal zone

within AixLib.Fluid.FMI.ExportContainers.Validation.FMUs;
block ThermalZoneAir2 "Validation of simple thermal zone"
  extends AixLib.Fluid.FMI.ExportContainers.Validation.FMUs.ThermalZoneAir1(
    redeclare package Medium = AixLib.Media.Air(extraPropertiesNames={"CO2"}));
end ThermalZoneAir2;


Validation of simple thermal zone

within AixLib.Fluid.FMI.ExportContainers.Validation.FMUs;
block ThermalZoneAir3 "Validation of simple thermal zone"
  extends AixLib.Fluid.FMI.ExportContainers.Validation.FMUs.ThermalZoneAir1(
    redeclare package Medium = AixLib.Media.Air(extraPropertiesNames={"CO2", "VOC", "CH2O"}));
end ThermalZoneAir3;


Validation of simple thermal zone

within AixLib.Fluid.FMI.ExportContainers.Validation.FMUs;
block ThermalZoneSimpleAir1 "Validation of simple thermal zone"
  extends AixLib.Fluid.FMI.ExportContainers.Validation.FMUs.ThermalZoneAir1(
    redeclare package Medium = Modelica.Media.Air.SimpleAir);
end ThermalZoneSimpleAir1;


Validation of simple thermal zone

within AixLib.Fluid.FMI.ExportContainers.Validation.FMUs;
block ThermalZoneSimpleAir2 "Validation of simple thermal zone"
  extends AixLib.Fluid.FMI.ExportContainers.Validation.FMUs.ThermalZoneAir1(
    redeclare package Medium = Modelica.Media.Air.SimpleAir(extraPropertiesNames={"CO2"}));
end ThermalZoneSimpleAir2;


Validation of simple thermal zone

within AixLib.Fluid.FMI.ExportContainers.Validation.FMUs;
block ThermalZoneSimpleAir3 "Validation of simple thermal zone"
  extends AixLib.Fluid.FMI.ExportContainers.Validation.FMUs.ThermalZoneAir1(
    redeclare package Medium = Modelica.Media.Air.SimpleAir(extraPropertiesNames={"CO2", "VOC", "CH2O"}));
end ThermalZoneSimpleAir3;


Type definition for fluid properties

within AixLib.Fluid.FMI.Interfaces;
connector FluidProperties "Type definition for fluid properties"
  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

  Medium.Temperature T "Temperature";
  AixLib.Fluid.FMI.Interfaces.MassFractionConnector X_w
    if Medium.nXi > 0 "Water vapor mass fractions per kg total air";
  Medium.ExtraProperty C[Medium.nC] "Properties c_i/m";
end FluidProperties;


Connector for fluid inlet

within AixLib.Fluid.FMI.Interfaces;
connector Inlet "Connector for fluid inlet"
  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

  parameter Boolean use_p_in = true
    "= true to use a pressure from connector, false to output Medium.p_default"

  parameter Boolean allowFlowReversal = true
    "= true to allow flow reversal, false restricts to design direction (inlet -> outlet)"

  input Medium.MassFlowRate m_flow
    "Mass flow rate from the connection point into the component";
  AixLib.Fluid.FMI.Interfaces.PressureInput p
  if use_p_in "Thermodynamic pressure in the connection point";

  input AixLib.Fluid.FMI.Interfaces.FluidProperties forward(
    redeclare final package Medium = Medium) "Inflowing properties";
  output AixLib.Fluid.FMI.Interfaces.FluidProperties backward(
    redeclare final package Medium = Medium)
    if allowFlowReversal "Outflowing properties";
end Inlet;


Connector for fluid outlet

within AixLib.Fluid.FMI.Interfaces;
connector Outlet "Connector for fluid outlet"
  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

  parameter Boolean use_p_in = true
    "= true to use pressure connector, false to remove it"

  parameter Boolean allowFlowReversal = true
    "= true to allow flow reversal, false restricts to design direction (inlet -> outlet)"

  output Medium.MassFlowRate m_flow
    "Mass flow rate from the connection point into the component";
  AixLib.Fluid.FMI.Interfaces.PressureOutput p
    if use_p_in "Thermodynamic pressure in the connection point";

  input AixLib.Fluid.FMI.Interfaces.FluidProperties backward(
    redeclare final package Medium = Medium)
    if allowFlowReversal "Inflowing properties";

  output AixLib.Fluid.FMI.Interfaces.FluidProperties forward(
    redeclare final package Medium = Medium) "Outflowing properties";
end Outlet;


within AixLib.Fluid.FMI;
package Interfaces "Package with interfaces for models that serves as an FMU container"
  extends Modelica.Icons.InterfacesPackage;
end Interfaces;


Flow splitter

within AixLib.Fluid.FMI.Validation;
model FlowSplitter_u "Flow splitter"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air "Medium model";

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

  parameter Boolean use_p_in = false
    "= true to use a pressure from connector, false to output Medium.p_default"
  parameter Boolean allowFlowReversal = false
    "= true to allow flow reversal, false restricts to design direction (inlet -> outlet)"

  Modelica.Blocks.Sources.Constant uSpl(k=1.0) "Control signal for heater"

  Source_T sou(
    redeclare package Medium = Medium,
    use_p_in=use_p_in,
    allowFlowReversal = allowFlowReversal)
    "Source for mass flow rate and pressure"

  Modelica.Blocks.Sources.Constant m_flow(k=m_flow_nominal*3) "Mass flow rate"

  Modelica.Blocks.Sources.Constant pIn(k=100000) "Inlet pressure"

  Modelica.Blocks.Sources.Constant TIn(k=293.15) "Inlet temperature"

  Modelica.Blocks.Sources.Constant X_w_in(k=0.01) "Inlet mass fraction"

  Modelica.Blocks.Sources.Constant C[Medium.nC](each k=0.01)
  if Medium.nC > 0 "Trace substances for forward flow"
  ExportContainers.Examples.FMUs.FlowSplitter_u floSpl(
    redeclare package Medium = Medium,
    use_p_in=use_p_in,
    allowFlowReversal=allowFlowReversal)
equation
  connect(m_flow.y, sou.m_flow_in)
  connect(sou.p_in, pIn.y)
  connect(TIn.y, sou.T_in)
  connect(C.y, sou.C_in)
  connect(X_w_in.y, sou.X_w_in)
  connect(floSpl.inlet, sou.outlet)
  connect(floSpl.u[1], uSpl.y)
  connect(floSpl.u[2], uSpl.y)
end FlowSplitter_u;


within AixLib.Fluid.FMI.Validation;
model HeaterFan
  "Heater and fan in series, model configured to allow flow reversal"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=Q_flow_nominal/1000/
      10 "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal(displayUnit="Pa")
     = 2000 "Pressure";
  parameter Modelica.Units.SI.HeatFlowRate Q_flow_nominal=1000
    "Heat flow rate at u=1, positive for heating";

  parameter Boolean use_p_in = true
    "= true to use a pressure from connector, false to output Medium.p_default"

  ExportContainers.Examples.FMUs.Fan floMac(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    use_p_in=use_p_in) "Flow machine with pressure raise as an input"

  ExportContainers.Examples.FMUs.HeaterCooler_u hea(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    Q_flow_nominal=Q_flow_nominal,
    use_p_in=use_p_in) "Heater"

  Modelica.Blocks.Sources.Constant dp(k=dp_nominal) "Pressure raise of fan"

  Modelica.Blocks.Sources.Constant uHea(k=0.2) "Control signal for heater"

  Source_T sou(redeclare package Medium = Medium, use_p_in=use_p_in)
    "Source for mass flow rate and pressure"

  Sink_T sin(redeclare package Medium = Medium, use_p_in=use_p_in)
    "Sink for flow rate, and source for backflow properties"

  Modelica.Blocks.Sources.Constant m_flow(k=m_flow_nominal) "Mass flow rate"

  Modelica.Blocks.Sources.Constant pIn(k=100000) "Inlet pressure"

  Modelica.Blocks.Sources.Constant TIn(k=293.15) "Inlet temperature"

  Modelica.Blocks.Sources.Constant X_w_in(k=0.01) "Inlet mass fraction"

  Modelica.Blocks.Sources.Constant TBac(k=303.15)
    "Temperature of backward flow"

  Modelica.Blocks.Sources.Constant X_w_bac(k=0.015)
    "Moisture mass fraction for back flow"

  Modelica.Blocks.Sources.Constant CBac[Medium.nC](each k=0.01)
  if Medium.nC > 0 "Trace substances for back flow"

  Modelica.Blocks.Sources.Constant C[Medium.nC](each k=0.01)
  if Medium.nC > 0 "Trace substances for forward flow"
equation
  connect(uHea.y, hea.u)
  connect(dp.y, floMac.dp_in)
  connect(sou.outlet, hea.inlet)
  connect(hea.outlet, floMac.inlet)
  connect(sin.inlet, floMac.outlet)
  connect(sin.T_in, TBac.y)
  connect(CBac.y, sin.C_in)
  connect(m_flow.y, sou.m_flow_in)
  connect(sou.p_in, pIn.y)
  connect(TIn.y, sou.T_in)
  connect(C.y, sou.C_in)
  connect(X_w_in.y, sou.X_w_in)
  connect(sin.X_w_in, X_w_bac.y)
end HeaterFan;


within AixLib.Fluid.FMI.Validation;
model HeaterFanPressureDriven
  "Heater and fan in series, model configured to allow flow reversal and pressure driven flow rate"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=Q_flow_nominal/1000/
      10 "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal(displayUnit="Pa")
     = 2000 "Pressure";
  parameter Modelica.Units.SI.HeatFlowRate Q_flow_nominal=1000
    "Heat flow rate at u=1, positive for heating";

  constant Boolean use_p_in = true
    "Set to true, as this model computes the mass flow rate based on the pressure drop"

  ExportContainers.Examples.FMUs.Fan floMac(
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    use_p_in=use_p_in) "Flow machine with pressure raise as an input"

  ExportContainers.Examples.FMUs.HeaterCooler_u hea(
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    Q_flow_nominal=Q_flow_nominal,
    use_p_in=use_p_in) "Heater"

  Modelica.Blocks.Sources.Constant dp(k=dp_nominal) "Pressure raise of fan"

  Modelica.Blocks.Sources.Constant uHea(k=0.2) "Control signal for heater"

  Source_T sou(redeclare package Medium = Medium, use_p_in=use_p_in)
    "Source for mass flow rate and pressure"

  Sink_T sin(redeclare package Medium = Medium, use_p_in=use_p_in)
    "Sink for flow rate, and source for backflow properties"

  Modelica.Blocks.Sources.Constant m_flow(k=m_flow_nominal) "Mass flow rate"

  Modelica.Blocks.Sources.Constant pIn(k=100000) "Inlet pressure"

  Modelica.Blocks.Sources.Constant TIn(k=293.15) "Inlet temperature"

  Modelica.Blocks.Sources.Constant X_w_in(k=0.01) "Inlet mass fraction"

  Modelica.Blocks.Sources.Constant TBac(k=303.15)
    "Temperature of backward flow"

  Modelica.Blocks.Sources.Constant X_w_bac(k=0.015)
    "Moisture mass fraction for back flow"

  Modelica.Blocks.Sources.Constant CBac[Medium.nC](each k=0.01)
  if Medium.nC > 0 "Trace substances for back flow"

  Modelica.Blocks.Sources.Constant C[Medium.nC](each k=0.01)
  if Medium.nC > 0 "Trace substances for forward flow"
  Modelica.Blocks.Math.InverseBlockConstraints invBloCon
    "Block to set up residual function for nonlinear system of equation for pressure drop and mass flow rate"
equation
  connect(uHea.y, hea.u)
  connect(dp.y, floMac.dp_in)
  connect(sou.outlet, hea.inlet)
  connect(hea.outlet, floMac.inlet)
  connect(sin.inlet, floMac.outlet)
  connect(sin.T_in, TBac.y)
  connect(CBac.y, sin.C_in)
  connect(sou.p_in, pIn.y)
  connect(TIn.y, sou.T_in)
  connect(C.y, sou.C_in)
  connect(pIn.y, invBloCon.u1)
  connect(sin.p, invBloCon.u2)
  connect(invBloCon.y1, sou.m_flow_in)
  connect(X_w_in.y, sou.X_w_in)
  connect(X_w_bac.y, sin.X_w_in)
end HeaterFanPressureDriven;


within AixLib.Fluid.FMI.Validation;
model HeaterFan_noReverseFlow
  "Heater and fan in series, model configured to not allow flow reversal"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air "Medium model";

  final parameter Boolean allowFlowReversal = false
    "= true to allow flow reversal, false restricts to design direction (inlet -> outlet)";

  parameter Boolean use_p_in = false
    "= true to use a pressure from connector, false to output Medium.p_default"

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=Q_flow_nominal/1000/
      10 "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal(displayUnit="Pa")
     = 2000 "Pressure";
  parameter Modelica.Units.SI.HeatFlowRate Q_flow_nominal=1000
    "Heat flow rate at u=1, positive for heating";

  ExportContainers.Examples.FMUs.Fan floMac(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    final allowFlowReversal=allowFlowReversal,
    use_p_in=use_p_in) "Flow machine with pressure raise as an input"

  ExportContainers.Examples.FMUs.HeaterCooler_u hea(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    Q_flow_nominal=Q_flow_nominal,
    final allowFlowReversal=allowFlowReversal,
    use_p_in=use_p_in) "Heater"

  Modelica.Blocks.Sources.Constant dp(k=1000) "Pressure raise of fan"

  Modelica.Blocks.Sources.Constant uHea(k=0.2) "Control signal for heater"

  Source_T sou(
    redeclare package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    use_p_in=use_p_in) "Source for mass flow rate and pressure"

  Modelica.Blocks.Sources.Constant m_flow(k=m_flow_nominal) "Mass flow rate"

  Modelica.Blocks.Sources.Constant pIn(k=100000) "Inlet pressure"

  Modelica.Blocks.Sources.Constant TIn(k=293.15) "Inlet temperature"

  Modelica.Blocks.Sources.Constant X_w_in(k=0.01) "Inlet mass fraction"

  Modelica.Blocks.Sources.Constant C[Medium.nC](each k=0.01)
  if Medium.nC > 0 "Trace substances for forward flow"
equation
  connect(uHea.y, hea.u)
  connect(dp.y, floMac.dp_in)
  connect(sou.outlet, hea.inlet)
  connect(hea.outlet, floMac.inlet)
  connect(m_flow.y, sou.m_flow_in)
  connect(sou.p_in, pIn.y)
  connect(TIn.y, sou.T_in)
  connect(C.y, sou.C_in)
  connect(X_w_in.y, sou.X_w_in)
end HeaterFan_noReverseFlow;


Collection of validation models

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


within AixLib.Fluid;
package Geothermal "Package with models for geothermal heat exchange"
  extends Modelica.Icons.VariantsPackage;
end Geothermal;


within AixLib.Fluid.Geothermal.Aquifer;
model MultiWell "Model of a single well for aquifer thermal energy storage"
 replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium in the component"
  parameter Integer nVol(min=1)=10 "Number of control volumes used in discretization"
  parameter Modelica.Units.SI.Height h "Aquifer thickness";
  parameter Modelica.Units.SI.Length d "Distance between wells";
  parameter Modelica.Units.SI.Height length
    "Length of one well, used to compute pressure drop";
  parameter Real nPai=1 "Number of paired wells";
  parameter Modelica.Units.SI.Radius rWB=0.1 "Wellbore radius"
  parameter Modelica.Units.SI.Radius rMax=d/2 "Domain radius"
  parameter Real griFac(min=1) = 1.15 "Grid factor for spacing"
  parameter Modelica.Units.SI.Temperature TCol_start=283.15
     "Initial temperature of cold well"
  parameter Modelica.Units.SI.Temperature THot_start=TCol_start
     "Initial temperature of warm well"
  parameter Modelica.Units.SI.Temperature TGroCol=285.15
     "Undisturbed ground temperature (cold well)"
  parameter Modelica.Units.SI.Temperature TGroHot=TGroCol
     "Undisturbed ground temperature (warm well)"
  parameter AixLib.Fluid.Geothermal.Aquifer.Data.Template aquDat
     "Aquifer thermal properties"
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal "Nominal mass flow rate"
  parameter Modelica.Units.SI.PressureDifference dpAquifer_nominal(displayUnit= "Pa")=
    m_flow_nominal/nPai*Modelica.Constants.g_n/2/Modelica.Constants.pi/h/aquDat.K*log(d/rWB)
     "Pressure drop at nominal mass flow rate in the aquifer"
  final parameter Modelica.Units.SI.PressureDifference dpWell_nominal(
      displayUnit="Pa") = 2*resWelCol.dp_nominal
    "Pressure drop at nominal mass flow rate in the well"
  parameter Modelica.Units.SI.PressureDifference dpExt_nominal(displayUnit="Pa")
    "Pressure drop at nominal mass flow rate in the above-surface system (used to size the head of the well pump)"

  final parameter Modelica.Units.SI.Radius rVol[nVol](
    each final fixed=false)
    "Radius to the center of the i-th domain";

  Modelica.Blocks.Interfaces.RealInput u(
      final min=-1,
      final max=1,
      final unit="1")
      "Pump control input (-1: extract from hot well, +1: extract from cold well, 0: off)"
  Modelica.Blocks.Interfaces.RealOutput PTot(
    final unit="W")
  "Total power consumed by all circulation pumps in the wells"

  Modelica.Fluid.Interfaces.FluidPort_a port_Col(
    redeclare final package Medium = Medium) "Fluid connector"

  Modelica.Fluid.Interfaces.FluidPort_a port_Hot(
    redeclare final package Medium = Medium) "Fluid connector"

  Modelica.Units.SI.Temperature TAquHot[nVol] = heaCapHot.T
    "Temperatures of the hot aquifer";
  Modelica.Units.SI.Temperature TAquCol[nVol] = heaCapCol.T
    "Temperatures of the cold aquifer";

  Movers.Preconfigured.SpeedControlled_y pumCol(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal/nPai,
    final dp_nominal=resAqu.dpMea_nominal + 2* resWelCol.dp_nominal + dpExt_nominal)
       "Pump to extract from cold well"
  Movers.Preconfigured.SpeedControlled_y pumHot(
    redeclare final package Medium = Medium,
    final m_flow_nominal=pumCol.m_flow_nominal,
    final dp_nominal=pumCol.dp_nominal) "Pump to extract from hot well"

  FixedResistances.PressureDrop resWelCol(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal/nPai,
    final from_dp=false,
    dp_nominal=
        Modelica.Fluid.Pipes.BaseClasses.WallFriction.Detailed.pressureLoss_m_flow(
        m_flow=m_flow_nominal/nPai,
        rho_a=rhoWat,
        rho_b=rhoWat,
        mu_a=mu,
        mu_b=mu,
        length=length,
        diameter=rWB,
        roughness=2.5e-5)) "Pressure drop in the cold well"

  // The resistances on the hot side are set to be the negative of the cold
  // side, as they have the same geometry and flow rate.
  // This reduces the size of the nonlinear system of equations,
  // and leads to fewer equations in the model.
  Movers.BaseClasses.IdealSource resWelHot(
    redeclare final package Medium = Medium,
    final allowFlowReversal=true,
    final control_m_flow=false,
    final control_dp=true,
    final m_flow_small=1E-4*abs(m_flow_nominal))
    "Pressure drop in the warm well"

  BaseClasses.MassFlowRateMultiplier mulCol(
    redeclare final package Medium = Medium,
    k=nPai)
    "Mass flow multiplier for the cold well"
  BaseClasses.MassFlowRateMultiplier mulHot(
    redeclare final package Medium = Medium,
    k=nPai)
    "Mass flow multiplier for the warm well"

  Modelica.Blocks.Math.Add addPum(
    final k1=1,
    final k2=1)
    "Sum of pump electrical power"

  Sensors.RelativePressure dpWelCol(
    redeclare package Medium = Medium)
    "Pressure drop of cold well"

  Airflow.Multizone.Point_m_flow resAqu(
    redeclare final package Medium = Medium,
    m=1,
    final dpMea_nominal=dpAquifer_nominal,
    final mMea_flow_nominal=m_flow_nominal/nPai)
    "Pressure drop in the cold and warm side of the aquifer"
protected
  parameter Modelica.Units.SI.Radius r[nVol + 1](each fixed=false)
    "Radius to the boundary of the i-th domain";
  parameter Modelica.Units.SI.HeatCapacity C[nVol](each fixed=false)
    "Heat capacity of segment";
  parameter Modelica.Units.SI.Volume VWat[nVol](each fixed=false)
    "Volumes of water";
  parameter Modelica.Units.SI.ThermalResistance R[nVol](each fixed=false)
    "Thermal resistances between nodes";
  parameter Real cAqu(fixed=false)
    "Heat capacity normalized with volume for aquifer";
  parameter Real kVol(fixed=false)
    "Heat conductivity normalized with volume";
  parameter Modelica.Units.SI.DynamicViscosity mu=Medium.dynamicViscosity(Medium.setState_pTX(
    Medium.p_default,
    Medium.T_default,
    Medium.X_default)) "Water dynamic viscosity";
  parameter Modelica.Units.SI.SpecificHeatCapacity cpWat=Medium.specificHeatCapacityCp(Medium.setState_pTX(
    Medium.p_default,
    Medium.T_default,
    Medium.X_default)) "Water specific heat capacity";
  parameter Modelica.Units.SI.Density rhoWat=Medium.density(Medium.setState_pTX(
    Medium.p_default,
    Medium.T_default,
    Medium.X_default)) "Water density";
  parameter Modelica.Units.SI.ThermalConductivity kWat=Medium.thermalConductivity(Medium.setState_pTX(
    Medium.p_default,
    Medium.T_default,
    Medium.X_default)) "Water thermal conductivity";

  AixLib.Fluid.MixingVolumes.MixingVolume volCol[nVol](
    redeclare final package Medium = Medium,
    each final T_start=TCol_start,
    each final m_flow_nominal=m_flow_nominal,
    final V=VWat,
    each nPorts=2)
    "Array of fluid volumes representing the fluid flow in the cold side of the aquifer"

  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCapCol[nVol](C=C,
      each T(start=TCol_start, fixed=true))
    "Array of thermal capacitor in the cold side of the aquifer"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor theResCol[nVol](R=R)
    "Array of thermal resistances in the cold side of the aquifer"

  MixingVolumes.MixingVolume volHot[nVol](
    redeclare final package Medium = Medium,
    each T_start=THot_start,
    each m_flow_nominal=m_flow_nominal,
    V=VWat,
    each nPorts=2)
    "Array of fluid volumes representing the fluid flow in the warm side of the aquifer"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCapHot[nVol](C=C,
    each T(start=THot_start, fixed=true))
    "Array of thermal capacitor in the warm side of the aquifer"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor theResHot[nVol](R=R)
    "Array of thermal resistances in the warm side of the aquifer"

  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature groTemCol(
    final T=TGroCol)
    "Boundary condition ground temperature in the cold side of the aquifer"
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature groTemHot(
    final T=TGroHot)
    "Boundary condition ground temperature in the warm side of the aquifer"

  Modelica.Blocks.Nonlinear.Limiter limCol(final uMax=1, final uMin=0) "Limiter for pump signal"
  Modelica.Blocks.Math.Gain gaiCon(final k=-1) "Inversion of control signal"
  Modelica.Blocks.Nonlinear.Limiter limHot(final uMax=1, final uMin=0) "Limiter for pump signal"

  Modelica.Blocks.Math.Gain gaiPum(final k=nPai) "Gain used to scale the pump electrical power"
initial equation
  assert(rWB < rMax, "In " + getInstanceName() + ": Require rWB < rMax");
  assert(0 < rWB, "In " + getInstanceName() + ": Require 0 < rWB");
  assert(rMax <= d/2, "In " + getInstanceName() + ": Require rMax <= d/2");

  cAqu=aquDat.dSoi*aquDat.cSoi*(1-aquDat.phi);
  kVol=kWat*aquDat.phi+aquDat.kSoi*(1-aquDat.phi);

  r[1] = rWB;
  for i in 2:nVol+1 loop
    r[i]= r[i-1] + (rMax - rWB)  * (1-griFac)/(1-griFac^(nVol)) * griFac^(i-2);
  end for;
  for i in 1:nVol loop
    rVol[i] = (r[i]+r[i+1])/2;
  end for;
  for i in 1:nVol loop
    C[i] = cAqu*h*3.14*(r[i+1]^2-r[i]^2);
  end for;
  for i in 1:nVol loop
    VWat[i] = aquDat.phi*h*3.14*(r[i+1]^2-r[i]^2);
  end for;

  R[nVol]=Modelica.Math.log(rMax/rVol[nVol])/(2*Modelica.Constants.pi*kVol*h);
  for i in 1:nVol-1 loop
    R[i] = Modelica.Math.log(rVol[i+1]/rVol[i])/(2*Modelica.Constants.pi*kVol*h);
  end for;

equation
  if nVol > 1 then
    for i in 1:(nVol - 1) loop
      connect(volCol[i].ports[2], volCol[i + 1].ports[1]);
    end for;
  end if;
  connect(resAqu.port_b, volCol[nVol].ports[2])
  connect(resAqu.port_a, volHot[nVol].ports[2])
  connect(pumCol.port_a, volCol[1].ports[1])
  connect(pumHot.port_a, volHot[1].ports[1])

  if nVol > 1 then
    for i in 1:(nVol - 1) loop
      connect(volHot[i].ports[2], volHot[i + 1].ports[1]);
    end for;
  end if;

  if nVol > 1 then
    for i in 1:(nVol - 1) loop
      connect(heaCapCol[i + 1].port, theResCol[i].port_b);
    end for;
  end if;

  if nVol > 1 then
    for i in 1:(nVol - 1) loop
      connect(heaCapHot[i + 1].port, theResHot[i].port_b);
    end for;
  end if;

  connect(groTemCol.port, theResCol[nVol].port_b)

  connect(volCol.heatPort, heaCapCol.port)
  connect(theResCol.port_a, volCol.heatPort)
  connect(groTemHot.port, theResHot[nVol].port_b)
  connect(volHot.heatPort, heaCapHot.port)
  connect(theResHot.port_a, volHot.heatPort)
  connect(pumCol.port_b, resWelCol.port_a)
  connect(resWelHot.port_a, pumHot.port_b)
  connect(limCol.y, pumCol.y)
  connect(gaiCon.y, limHot.u)
  connect(limHot.y, pumHot.y)
  connect(gaiCon.u, u)
  connect(limCol.u, u)
  connect(resWelCol.port_b, mulCol.port_a)
  connect(mulCol.port_b, port_Col)
  connect(mulHot.port_b, port_Hot)
  connect(resWelHot.port_b, mulHot.port_a)
  connect(pumCol.P, addPum.u1)
  connect(addPum.y, gaiPum.u)
  connect(pumHot.P, addPum.u2)
  connect(gaiPum.y, PTot)
  connect(dpWelCol.port_a, resWelCol.port_b)
  connect(dpWelCol.port_b, resWelCol.port_a)
  connect(dpWelCol.p_rel, resWelHot.dp_in)
end MultiWell;


within AixLib.Fluid.Geothermal;
package Aquifer "Package with aquifer thermal energy storage models"
  extends Modelica.Icons.Package;
end Aquifer;


within AixLib.Fluid.Geothermal.Aquifer;
package Data "Collection of data records for aquifer thermal energy storage"
  extends Modelica.Icons.MaterialPropertiesPackage;
end Data;


Medium model

within AixLib.Fluid.Geothermal.Aquifer.Examples;
model CoolingOffice
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Water "Medium model";

  parameter Modelica.Units.SI.HeatFlowRate QCoo_flow_nominal=30000
    "Cooling power";
  parameter Modelica.Units.SI.TemperatureDifference deltaT=4 "Temperature difference at heat exchanger";
  parameter Modelica.Units.SI.SpecificHeatCapacity cpWat=4186 "Heat capacity of water";
  parameter Modelica.Units.SI.MassFlowRate mWat_flow_nominal=
    QCoo_flow_nominal/(deltaT*cpWat) "Nominal water mass flow rate";

  HeatExchangers.HeaterCooler_u hea(
    redeclare package Medium = Medium,
    m_flow_nominal=mWat_flow_nominal,
    dp_nominal=100,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    Q_flow_nominal=QCoo_flow_nominal) "Heat exchanger"
  Modelica.Blocks.Sources.Constant uPum(k=1) "Pump control signal"
  Modelica.Blocks.Sources.Constant uHea(k=1) "Heat load control signal"
  MultiWell aquWel(
    redeclare package Medium = Medium,
    nVol=80,
    h=20,
    d=1000,
    length=40,
    TCol_start=285.15,
    THot_start=285.15,
    aquDat=AixLib.Fluid.Geothermal.Aquifer.Data.Rock(),
    m_flow_nominal=mWat_flow_nominal,
    dpExt_nominal=5000)
                       "Acquifer well"
  Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=1) "Pressure boundary condition"
equation
  connect(uHea.y, hea.u)
  connect(aquWel.port_Hot, hea.port_b)
  connect(aquWel.port_Col, hea.port_a)
  connect(bou.ports[1], hea.port_a)
  connect(uPum.y, aquWel.u)
end CoolingOffice;


within AixLib.Fluid.Geothermal.Aquifer;
package Examples "Example models for AixLib.Fluid.Geothermal.Aquifer"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


within AixLib.Fluid.Geothermal.Aquifer.Validation;
model NumberWells
  "Test model for aquifer thermal energy storage with multiple wells"
  extends Modelica.Icons.Example;

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

  model MyWell = MultiWell (
    redeclare package Medium = AixLib.Media.Water,
    nVol=50,
    h=10,
    d=4800,
    length=200,
    THot_start=303.15,
    TGroCol=273.15,
    TGroHot=303.15,
    aquDat=AixLib.Fluid.Geothermal.Aquifer.Data.Rock(),
    m_flow_nominal=0.1,
    dpExt_nominal=0) "Well model";

  MyWell aquWel1(d=4800)
  "ATES with one pair of wells"

  MyWell aquWel2(
    d=4800,
     nPai=2,
     m_flow_nominal=0.2)
    "ATES with two pairs of wells"
  Modelica.Blocks.Sources.Constant uPum(k=1) "Pump control signal"
  Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=2) "Sink"
  Modelica.Blocks.Sources.RealExpression temWel1(
    y=aquWel1.TAquHot[10])
    "Temperature output from aquifer model with one pair of wells"
  Modelica.Blocks.Sources.RealExpression temWel2(
    y=aquWel2.TAquHot[10])
    "Temperature output from aquifer model with two pairs of wells"
  AixLib.Utilities.Diagnostics.CheckEquality cheEqu
    "Assertion that checks for equality of results"
equation
  connect(uPum.y, aquWel1.u)
  connect(aquWel2.u,uPum. y)
  connect(aquWel2.port_Col, aquWel2.port_Hot)
  connect(aquWel1.port_Col, aquWel1.port_Hot)
  connect(bou.ports[1], aquWel1.port_Hot)
  connect(bou.ports[2], aquWel2.port_Hot)
  connect(cheEqu.u1, temWel1.y)
  connect(cheEqu.u2, temWel2.y)
end NumberWells;


within AixLib.Fluid.Geothermal.Aquifer;
package Validation "Validation models for aquifer thermal energy storage"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


within AixLib.Fluid.Geothermal.Aquifer.Validation;
model SimulationTest
  "Test model for aquifer thermal energy storage in comparison with other geothermal simulators"
  extends Modelica.Icons.Example;

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

  MultiWell aquWel(
    redeclare package Medium = Medium,
    nVol=232,
    h=200,
    d=4800,
    length=40,
    griFac=1.1,
    TCol_start=307.15,
    THot_start=393.15,
    TGroCol=307.15,
    TGroHot=393.15,
    aquDat=AixLib.Fluid.Geothermal.Aquifer.Data.Rock(),
    m_flow_nominal=1,
    dpExt_nominal=0) "Aquifer wells"
  Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=1) "Boundary condition for pressure"
  Modelica.Blocks.Sources.CombiTimeTable uPum(table=[0.0,-1; 86400*120,-1; 86400
        *120,0; 86400*180,0; 86400*180,1; 86400*300,1; 86400*300,0])
    "Pump control signal"
equation
  connect(aquWel.port_Col, aquWel.port_Hot)
  connect(bou.ports[1], aquWel.port_Hot)
  connect(uPum.y[1], aquWel.u)
end SimulationTest;


Borefield model containing single U-tube boreholes. This model simulates a borefield containing one or many single U-tube boreholes
using the parameters in the borFieDat record.

within AixLib.Fluid.Geothermal.Borefields;
model OneUTube
  "Borefield model containing single U-tube boreholes"
  extends AixLib.Fluid.Geothermal.Borefields.BaseClasses.PartialBorefield(
    redeclare AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.OneUTube borHol);
end OneUTube;


Package with borefield models

within AixLib.Fluid.Geothermal;
package Borefields "Package with borefield models"
  extends Modelica.Icons.VariantsPackage;
end Borefields;


Borefield model containing double U-tube boreholes. This model simulates a borefield containing one or many double U-tube boreholes
using the parameters in the borFieDat record.

within AixLib.Fluid.Geothermal.Borefields;
model TwoUTubes
  "Borefield model containing double U-tube boreholes"
  extends AixLib.Fluid.Geothermal.Borefields.BaseClasses.PartialBorefield(
    redeclare AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.TwoUTube borHol);
end TwoUTubes;


Package with type definitions

within AixLib.Fluid.Geothermal.Borefields;
package Types "Package with type definitions"
  extends Modelica.Icons.TypesPackage;

  type BoreholeConfiguration = enumeration(
      SingleUTube
    "Single U-tube configuration",
      DoubleUTubeParallel
    "Double U-tube configuration with pipes connected in parallel",
      DoubleUTubeSeries
    "Double U-tube configuration with pipes connected in series")
  "Enumaration to define the borehole configurations"
end Types;


User's Guide

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

end UsersGuide;


within AixLib.Fluid.Geothermal.Borefields;
package BaseClasses "Base classes used in AixLib.Fluid.HeatExchangers.Ground"
extends Modelica.Icons.BasesPackage;
end BaseClasses;