within Buildings.Fluid.FMI;
model Sink_T
  "Model of a sink with temperature for reverse flow as an input that can be exported as an FMU"
  extends Modelica.Blocks.Icons.Block;
  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)"

  Modelica.Blocks.Interfaces.RealInput T_in(unit="K",
                                            displayUnit="degC",
                                            min=0)
    "Prescribed boundary temperature"
  Modelica.Blocks.Interfaces.RealInput X_w_in(unit="1")
    if Medium.nXi > 0 "Prescribed boundary composition"

  Modelica.Blocks.Interfaces.RealInput C_in[Medium.nC]
    "Prescribed boundary composition"

  Interfaces.Inlet inlet(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_p_in=use_p_in) "Fluid port"
  Buildings.Fluid.FMI.Interfaces.PressureOutput p
  if use_p_in "Pressure"
protected
  input Buildings.Fluid.FMI.Interfaces.FluidProperties bacPro_internal(
    redeclare final package Medium = Medium)
    "Internal connector for fluid properties for back flow";
  Buildings.Fluid.FMI.Interfaces.PressureOutput p_in_internal
    "Internal connector for pressure";
  output Buildings.Fluid.FMI.Interfaces.MassFractionConnector X_w_in_internal
    "Internal connector for mass fraction of forward flow properties";

equation
 // Conditional connector for flow reversal
  connect(inlet.backward, bacPro_internal);
  connect(bacPro_internal.X_w, X_w_in_internal);
  if allowFlowReversal and Medium.nXi > 0 then
    connect(X_w_in_internal, X_w_in);
  else
    X_w_in_internal = 0;
  end if;

  if allowFlowReversal then
    bacPro_internal.T  = T_in;
    bacPro_internal.C  = C_in;
  else
    bacPro_internal.T = Medium.T_default;
    bacPro_internal.C  = fill(0, Medium.nC);
  end if;

  // Propagate pressure to output signal connector
  // using conditional connectors
  if use_p_in then
    connect(inlet.p, p_in_internal);
  else
    p_in_internal = Medium.p_default;
  end if;
  connect(p, p_in_internal);
end Sink_T;


within Buildings.Fluid.FMI;
model Source_T
  "Model of a boundary with mass flow rate, pressure and temperature as an input that can be exported as an FMU"
  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)"

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

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

  Buildings.Fluid.FMI.Interfaces.PressureInput p_in
    if use_p_in "Prescribed boundary pressure"
  Modelica.Blocks.Interfaces.RealInput T_in(unit="K",
                                            displayUnit="degC",
                                            min=0)
    "Prescribed boundary temperature"
  Modelica.Blocks.Interfaces.RealInput X_w_in(unit="1")
    if Medium.nXi > 0 "Prescribed boundary composition"

  Modelica.Blocks.Interfaces.RealInput C_in[Medium.nC]
    "Prescribed boundary composition"

  Interfaces.Outlet outlet(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_p_in=use_p_in) "Fluid port"
protected
  Buildings.Fluid.FMI.Interfaces.PressureOutput p_in_internal
    "Internal connector for pressure";
  input Buildings.Fluid.FMI.Interfaces.MassFractionConnector X_w_in_internal
    "Internal connector for mass fraction of forward flow properties";
initial equation
   assert(Medium.nXi < 2,
   "The medium must have zero or one independent mass fraction Medium.nXi.");
equation
   // Conditional connect statements for pressure
   if use_p_in then
     connect(p_in, p_in_internal);
   else
     p_in_internal = Medium.p_default;
   end if;
   connect(outlet.p, p_in_internal);

  // Conditional connectors for mass fraction
  if Medium.nXi > 0 then
    connect(X_w_in_internal, X_w_in);
  else
    X_w_in_internal = 0;
  end if;
  connect(outlet.forward.X_w, X_w_in_internal);

  outlet.m_flow = m_flow_in;

  outlet.forward.T  = T_in;
  outlet.forward.C  = C_in;
end Source_T;


User's Guide

within Buildings.Fluid.FMI;
package UsersGuide "User's Guide"
  extends Modelica.Icons.Information;
end UsersGuide;


within Buildings.Fluid.FMI.Adaptors;
model HVAC
  "Adaptor for connecting an HVAC system to signal ports which then can be exposed at an FMI interface"

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

  // Don't use

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

  Modelica.Fluid.Interfaces.FluidPorts_b ports[nPorts](
    redeclare each final package Medium = Medium)

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

protected
  Sources.MassFlowSource_T bou(
    final nPorts=nPorts,
    redeclare final package Medium = Medium,
    final use_m_flow_in=false,
    final use_T_in=true,
    final use_X_in=Medium.nXi > 0,
    final use_C_in=Medium.nC > 0,
    final m_flow=0) "Boundary conditions for HVAC system"

  Buildings.Fluid.FMI.Conversion.AirToOutlet con[nPorts](
      redeclare each final package Medium = Medium,
      each final allowFlowReversal=true)
    "Converter between the different connectors"

  Sensors.MassFlowRate senMasFlo[nPorts](
    redeclare each final package Medium = Medium,
    each allowFlowReversal=true) "Mass flow rate sensor"

  Buildings.Fluid.FMI.BaseClasses.X_w_toX x_w_toX(
    redeclare final package Medium = Medium)
    if Medium.nXi > 0 "Conversion from X_w to X"

  Modelica.Blocks.Sources.RealExpression hSup[nPorts](
    final y={inStream(ports[i].h_outflow) for i in 1:nPorts})
    "Supply air specific enthalpy"

  RealVectorExpression XiSup[nPorts](
    each final n = Medium.nXi,
    final y={inStream(ports[i].Xi_outflow) for i in 1:nPorts})
    if Medium.nXi > 0 "Water vapor concentration of supply air"

  RealVectorExpression CSup[nPorts](
    each final n=Medium.nC,
    final y={inStream(ports[i].C_outflow) for i in 1:nPorts})
    if Medium.nC > 0 "Trace substance concentration of supply air"

  ///////////////////////////////////////////////////////////////////////////
  // Internal blocks
  block RealVectorExpression
    "Set vector output signal to a time varying vector Real expression"
    parameter Integer n "Dimension of output signal";
    Modelica.Blocks.Interfaces.RealOutput[n] y "Value of Real output"

  end RealVectorExpression;

initial equation
   assert(Medium.nXi < 2,
   "The medium must have zero or one independent mass fraction Medium.nXi.");
equation

  connect(con.outlet, fluPor)
  connect(senMasFlo.m_flow, con.m_flow)
  connect(hSup.y, con.h)
  for i in 1:nPorts loop
    connect(XiSup[i].y, con[i].Xi)
    connect(CSup[i].y, con[i].C)
  end for;
  connect(senMasFlo.port_b, bou.ports)
  connect(ports, senMasFlo.port_a)
  connect(con[1].TAirZon, bou.T_in)
  connect(con[1].X_wZon, x_w_toX.X_w)
  connect(x_w_toX.X, bou.X_in)
  connect(con[1].CZon, bou.C_in)
  connect(con.TAirZon, TAirZon)
  connect(con.X_wZon, X_wZon)
  connect(con.CZon, CZon)
end HVAC;


within Buildings.Fluid.FMI.Adaptors;
model Inlet "Adaptor for connecting a fluid inlet to the FMI interface"

  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"

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

  Modelica.Fluid.Interfaces.FluidPort_b port_b(
    redeclare final package Medium=Medium) "Fluid port"
  Buildings.Fluid.FMI.Interfaces.PressureOutput p
  if use_p_in "Pressure"
protected
  input Buildings.Fluid.FMI.Interfaces.FluidProperties bacPro_internal(
    redeclare final package Medium = Medium)
    "Internal connector for fluid properties for back flow";
  Buildings.Fluid.FMI.Interfaces.PressureOutput p_in_internal
    "Internal connector for pressure";
  output Buildings.Fluid.FMI.Interfaces.MassFractionConnector X_w_in_internal
    "Internal connector for mass fraction of forward flow properties";
  output Buildings.Fluid.FMI.Interfaces.MassFractionConnector X_w_out_internal
    "Internal connector for mass fraction of backward flow properties";
initial equation
   assert(Medium.nXi < 2,
   "The medium must have zero or one independent mass fraction Medium.nXi.");
equation
  // To locally balance the model, the pressure is only imposed at the
  // outlet model.
  // The sign is negative because inlet.m_flow > 0
  // means that fluid flows out of this component
  -port_b.m_flow     = inlet.m_flow;

  port_b.h_outflow  = Medium.specificEnthalpy_pTX(
                        p = p_in_internal,
                        T = inlet.forward.T,
                        X = if Medium.nXi == 1 then cat(1, {X_w_in_internal}, {1-X_w_in_internal}) else zeros(Medium.nX));

  port_b.C_outflow  = inlet.forward.C;

  // Conditional connector for mass fraction for forward flow
  if Medium.nXi == 0 then
    X_w_in_internal = 0;
  else
    connect(X_w_in_internal, inlet.forward.X_w);
  end if;
  port_b.Xi_outflow = fill(X_w_in_internal, Medium.nXi);

  // Conditional connector for flow reversal
  connect(inlet.backward, bacPro_internal);

  // Mass fraction for reverse flow
  X_w_out_internal = if Medium.nXi > 0 and allowFlowReversal then inStream(port_b.Xi_outflow[1]) else 0;
  connect(bacPro_internal.X_w, X_w_out_internal);

  if allowFlowReversal then
    bacPro_internal.T  = Medium.temperature_phX(
                           p = p_in_internal,
                           h = inStream(port_b.h_outflow),
                           X = cat(1, inStream(port_b.Xi_outflow), {1-sum(inStream(port_b.Xi_outflow))}));
    bacPro_internal.C  = inStream(port_b.C_outflow);
  else
    bacPro_internal.T  = Medium.T_default;
    bacPro_internal.C  = fill(0, Medium.nC);
  end if;

  // Conditional connectors for pressure
  if use_p_in then
  connect(inlet.p, p_in_internal);
  else
    p_in_internal = Medium.p_default;
  end if;
  connect(p, p_in_internal);
end Inlet;


within Buildings.Fluid.FMI.Adaptors;
model Outlet "Adaptor for connecting a fluid outlet to the FMI interface"

  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"

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

  Modelica.Fluid.Interfaces.FluidPort_a port_a(
    redeclare final package Medium=Medium) "Fluid port"
  Buildings.Fluid.FMI.Interfaces.PressureInput p
    if use_p_in "Pressure to be sent to outlet"
protected
  output Buildings.Fluid.FMI.Interfaces.FluidProperties bacPro_internal(
    redeclare final package Medium = Medium)
    "Internal connector for fluid properties for back flow";
  Buildings.Fluid.FMI.Interfaces.PressureOutput p_in_internal
    "Internal connector for pressure";

  input Buildings.Fluid.FMI.Interfaces.MassFractionConnector X_w_in_internal
    "Internal connector for mass fraction of forward flow properties";
  output Buildings.Fluid.FMI.Interfaces.MassFractionConnector X_w_out_internal
    "Internal connector for mass fraction of backward flow properties";
initial equation
   assert(Medium.nXi < 2,
   "The medium must have zero or one independent mass fraction Medium.nXi.");
equation
  port_a.m_flow = outlet.m_flow;

  outlet.forward.T = Medium.temperature_phX(
    p = p_in_internal,
    h = inStream(port_a.h_outflow),
    X = cat(1, inStream(port_a.Xi_outflow), {1-sum(inStream(port_a.Xi_outflow))}));

  inStream(port_a.C_outflow)  = outlet.forward.C;

  // Mass fraction for forward flow
  X_w_in_internal = if Medium.nXi > 0 then inStream(port_a.Xi_outflow[1]) else 0;
  connect(outlet.forward.X_w, X_w_in_internal);

  // Conditional connector for mass fraction for backward flow
  if Medium.nXi > 0 and allowFlowReversal then
    connect(X_w_out_internal, outlet.backward.X_w);
  else
    X_w_out_internal = 0;
  end if;
  port_a.Xi_outflow = fill(X_w_out_internal, Medium.nXi);

  // Conditional connector for flow reversal
  connect(outlet.backward, bacPro_internal);
  if not allowFlowReversal then
    bacPro_internal.T  = Medium.T_default;
    bacPro_internal.C  = fill(0, Medium.nC);
    if Medium.nXi > 0 then
      // This test for nXi is needed for Buildings.Fluid.FMI.Validation.HeaterFan_noReverseFlow
      // to work with Buildings.Media.Water
      connect(bacPro_internal.X_w, X_w_out_internal);
    end if;
  end if;
  bacPro_internal.T  = Medium.temperature_phX(
    p = p_in_internal,
    h = port_a.h_outflow,
    X = cat(1, port_a.Xi_outflow, {1-sum(port_a.Xi_outflow)}));
  bacPro_internal.C  = port_a.C_outflow;

  // Conditional connectors for pressure
  if use_p_in then
    connect(outlet.p, p_in_internal);
  else
    p_in_internal = Medium.p_default;
  end if;
  connect(p, p_in_internal);
  port_a.p = p_in_internal;
end Outlet;


within Buildings.Fluid.FMI;
package Adaptors "Package with adaptors to connect models with fluid ports and models with signal ports"
  extends Modelica.Icons.Package;
end Adaptors;


within Buildings.Fluid.FMI.Adaptors;
model ThermalZone
  "Adaptor for connecting a thermal zone to signal ports which then can be exposed at an FMI interface"

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

  // Don't use

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

  Modelica.Fluid.Interfaces.FluidPorts_b ports[nPorts](
    redeclare each final package Medium = Medium)

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heaPorAir
    "Heat port for sensible heat input"

protected
  x_i_toX_w x_i_toX(
    redeclare final package Medium = Medium)
 if Medium.nXi > 0 "Conversion from x_i to X_w"

  RealVectorExpression XiSup(
    final n=Medium.nXi,
    final y=inStream(ports[1].Xi_outflow))
    if Medium.nXi > 0
      "Water vapor concentration of supply air"

  RealVectorExpression CSup(
    final n=Medium.nC,
    final y=inStream(ports[1].C_outflow))
 if Medium.nC > 0 "Trace substance concentration of supply air"

  Sources.MassFlowSource_T bou[nPorts](
    each final nPorts=1,
    redeclare each final package Medium = Medium,
    each final use_T_in=true,
    each final use_C_in=Medium.nC > 0,
    each final use_X_in=Medium.nXi > 0,
    each use_m_flow_in=true) "Mass flow source"

  Conversion.InletToAir con[nPorts](redeclare each final package Medium =
        Medium) "Connector between FMI signals and real input and real outputs"

  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor senTemAir
    "Room air temperature sensor"

  BaseClasses.X_w_toX x_w_toX[nPorts](redeclare final package Medium = Medium)
    if Medium.nXi > 0 "Conversion from X_w to X"

  Modelica.Blocks.Math.MultiSum multiSum(final nu=nPorts, final k=fill(1,
        nPorts)) "Sum of air mass flow rates"

  Buildings.Utilities.Diagnostics.AssertEquality assEqu(
    message="\"Mass flow rate does not balance. The sum needs to be zero.",
      threShold=1E-4)
    "Tests whether the mass flow rates balance to zero"
  Modelica.Blocks.Sources.Constant const(final k=0) "Outputs zero"

  ///////////////////////////////////////////////////////////////////////////
  // Internal blocks
  block RealVectorExpression
    "Set vector output signal to a time varying vector Real expression"
    parameter Integer n "Dimension of output signal";
    Modelica.Blocks.Interfaces.RealOutput[n] y "Value of Real output"
  end RealVectorExpression;

  block x_i_toX_w "Conversion from Xi to X"
    extends Modelica.Blocks.Icons.Block;

    replaceable package Medium =
      Modelica.Media.Interfaces.PartialMedium "Medium model within the source"
    Modelica.Blocks.Interfaces.RealInput Xi[Medium.nXi](each final unit="kg/kg")
   if Medium.nXi > 0 "Water vapor concentration in kg/kg total air"

    Modelica.Blocks.Interfaces.RealOutput X_w(
      final unit="kg/kg") "Water vapor concentration in kg/kg total air"
  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";

  equation
  // Conditional connector
  connect(Xi_internal, Xi);
  if Medium.nXi == 0 then
    Xi_internal = zeros(Medium.nXi);
  end if;

  X_w_internal = sum(Xi_internal);
  connect( X_w, X_w_internal)
  end x_i_toX_w;

initial equation
   assert(Medium.nXi < 2,
   "The medium must have zero or one independent mass fraction Medium.nXi.");
equation

  for i in 1:nPorts loop
    connect(bou[i].ports[1], ports[i])
  end for;
  connect(con.inlet, fluPor)
  connect(con.X_w, x_w_toX.X_w)

  connect(x_w_toX.X, bou.X_in)
  connect(con.C, bou.C_in)
  connect(heaPorAir, senTemAir.port)
  connect(XiSup.y, x_i_toX.Xi)
  connect(con.T, bou.T_in)
  connect(con[1:nPorts].m_flow, multiSum.u[1:nPorts])
  connect(multiSum.y, assEqu.u2)
  connect(const.y, assEqu.u1)
  for i in 1:nPorts loop
  connect(senTemAir.T, con[i].TAirZon)
  if Medium.nXi > 0 then
    connect(x_i_toX.X_w, con[i].X_wZon)
  end if;
  connect(CSup.y, con[i].CZon)
  end for;
  connect(bou.m_flow_in, con.m_flow)
end ThermalZone;


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


within Buildings.Fluid.FMI.Adaptors.Examples;
model ThermalZoneHVACNoExhaust
  "Example of a thermal zone and an HVAC system both exposed using the FMI adaptor"
  extends Modelica.Icons.Example;

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

  Buildings.Fluid.FMI.Adaptors.HVAC hvacAda(
    redeclare final package Medium=MediumA, nPorts=2)
    "Adaptor for an HVAC system that is exposed through an FMI interface"

  parameter Modelica.Units.SI.HeatFlowRate Q_flow_nominal=30*6*6
    "Nominal heat loss of the room";

  parameter Modelica.Units.SI.Volume VRoo=6*6*2.7 "Room volume";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=VRoo*2*1.2/3600
    "Nominal mass flow rate";

  Buildings.Fluid.FMI.Adaptors.ThermalZone con(
    redeclare package Medium = MediumA,
    nPorts=2) "Adaptor for thermal zone"
  Modelica.Blocks.Sources.Pulse TSet(
    amplitude=4,
    period=86400,
    offset=273.15 + 16,
    startTime=7*3600) "Setpoint for room temperature"
  Controls.Continuous.LimPID conPI(
    k=1,
    yMax=1,
    yMin=0,
    Ti=120) "Controller"
  Movers.FlowControlled_m_flow mov(
    redeclare package Medium = MediumA,
    m_flow_nominal=m_flow_nominal,
    addPowerToMedium=false,
    nominalValuesDefineDefaultPressureCurve=true,
    dp_nominal=1200,
    inputType=Buildings.Fluid.Types.InputType.Constant,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    use_riseTime=false) "Fan or pump"
  HeatExchangers.HeaterCooler_u
    hea(
    redeclare package Medium = MediumA,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=1000,
    Q_flow_nominal=Q_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState) "Heater"

  Sources.Boundary_pT out(
    redeclare package Medium = MediumA,
    nPorts=2,
    use_T_in=true) "Pressure and temperature source for outdoor"
  HeatExchangers.ConstantEffectiveness hex(
    redeclare package Medium1 = MediumA,
    redeclare package Medium2 = MediumA,
    m1_flow_nominal=m_flow_nominal,
    m2_flow_nominal=m_flow_nominal,
    dp1_nominal=200,
    dp2_nominal=200) "Heat recovery"
  MixingVolumes.MixingVolume vol(
    redeclare package Medium = MediumA,
    mSenFac=3,
    m_flow_nominal=m_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    V=VRoo,
    nPorts=2) "Room volume"
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=
    Q_flow_nominal/20)
    "Thermal conductance with the ambient"
  Buildings.HeatTransfer.Sources.PrescribedTemperature TBou
    "Fixed temperature boundary condition"
  Modelica.Blocks.Sources.RealExpression TOutZon(
    y=273.15 + 16 - 5*cos(time/86400*2*Modelica.Constants.pi))
    "Outdoor temperature used for the thermal zone model"
  Modelica.Blocks.Sources.RealExpression TOutHVAC(
    y=273.15 + 16 - 5*cos(time/86400*2*Modelica.Constants.pi))
    "Outdoor temperature used for the HVAC model"
equation
  connect(mov.port_b,hea. port_a)
  connect(hex.port_b1,mov. port_a)
  connect(out.ports[1],hex. port_b2)
  connect(out.ports[2],hex. port_a1)
  connect(conPI.y, hea.u)
  connect(TBou.port,theCon. port_a)
  connect(vol.heatPort, theCon.port_b)
  connect(conPI.u_s, TSet.y)
  connect(TOutZon.y, TBou.T)
  connect(hvacAda.TAirZon[1], conPI.u_m)
  connect(con.ports[1], vol.ports[1])
  connect(vol.heatPort, con.heaPorAir)
  connect(con.ports[2], vol.ports[2])
  connect(hvacAda.fluPor, con.fluPor)
  connect(hea.port_b, hvacAda.ports[1])
  connect(hvacAda.ports[2], hex.port_a2)
  connect(TOutHVAC.y, out.T_in)
end ThermalZoneHVACNoExhaust;


within Buildings.Fluid.FMI.Adaptors.Examples;
model ThermalZoneHVACWithExhaust
  "Example of a thermal zone and an HVAC system both exposed using the FMI adaptor"
  extends Buildings.Fluid.FMI.Adaptors.Examples.ThermalZoneHVACNoExhaust(
    hvacAda(nPorts=3),
    out(nPorts=3),
    con(nPorts=3),
    vol(nPorts=3));

  Movers.FlowControlled_m_flow exh(
    redeclare package Medium = MediumA,
    addPowerToMedium=false,
    nominalValuesDefineDefaultPressureCurve=true,
    dp_nominal=1200,
    inputType=Buildings.Fluid.Types.InputType.Constant,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    use_riseTime=false,
    m_flow_nominal=0.1*m_flow_nominal) "Constant air exhaust"
equation
  connect(hvacAda.ports[3], exh.port_a)
  connect(exh.port_b, out.ports[3])
  connect(con.ports[3], vol.ports[3])
end ThermalZoneHVACWithExhaust;


Conversion from h to T

within Buildings.Fluid.FMI.BaseClasses;
block hOut_toT "Conversion from h to T"
  extends Modelica.Blocks.Icons.Block;

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"
  Modelica.Blocks.Interfaces.RealInput h(final unit="J/kg") "Specific enthalpy"

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

  Modelica.Blocks.Interfaces.RealOutput T(final unit="K",
                                            displayUnit="degC") "Temperature"
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";

equation
  // Conditional connectors
  connect(Xi_internal, Xi);
  if Medium.nXi == 0 then
    Xi_internal = zeros(Medium.nXi);
  end if;

 T = Medium.temperature_phX(
      p=Medium.p_default,
      h=h,
      X=Xi_internal);
end hOut_toT;


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


Conversion from Xi to X

within Buildings.Fluid.FMI.BaseClasses;
block X_w_toX "Conversion from Xi to X"
  extends Modelica.Blocks.Icons.Block;

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"
  Modelica.Blocks.Interfaces.RealInput X_w(final unit="kg/kg")
     if Medium.nXi > 0 "Water mass fraction per total air mass"
  Modelica.Blocks.Interfaces.RealOutput X[Medium.nX](
    each final unit="kg/kg",
    final quantity=Medium.substanceNames) "Prescribed fluid composition"
protected
  Modelica.Blocks.Interfaces.RealInput X_w_internal(final unit="kg/kg")
    "Internal connector for water mass fraction per total air mass";
equation
  // Conditional connector
  connect(X_w_internal, X_w);
  if Medium.nXi == 0 then
    X_w_internal = 0;
  end if;
  // Assign vector to output connector
 X = if Medium.nX == 1 then ones(Medium.nX) else cat(1, {X_w_internal}, {1-X_w_internal});
end X_w_toX;


within Buildings.Fluid.FMI.Conversion;
block AirToOutlet
  "Conversion from real signals for a fluid to a Buildings.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"

  Buildings.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 Buildings.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 Buildings.Fluid.FMI.Conversion;
block InletToAir
  "Conversion from real signals for a fluid to a Buildings.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)"

  Buildings.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 Buildings.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 Buildings.Fluid.FMI;
package Conversion "Package with blocks that convert between different connectors"
  extends Modelica.Icons.Package;
end Conversion;


within Buildings.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"

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

  Buildings.Fluid.FMI.Conversion.AirToOutlet conAirWithC(
    redeclare package Medium = Buildings.Media.Air(extraPropertiesNames={"CO2"}),
    final allowFlowReversal=allowFlowReversal)
    "Converter for air with trace substances"
  Buildings.Fluid.FMI.Conversion.AirToOutlet conDryAirNoC(
    redeclare package Medium = Modelica.Media.Air.SimpleAir,
    final allowFlowReversal=allowFlowReversal)
    "Converter for dry air without trace substances"
  Buildings.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 Buildings.Fluid.FMI.Conversion.Validation;
model AirToOutletFlowReversal
  "Validation model for air to outlet converter with flow reversal enabled"
  extends Buildings.Fluid.FMI.Conversion.Validation.AirToOutlet(
    allowFlowReversal = true);
  BoundaryCondition bouAirNoC(
    redeclare package Medium = Buildings.Media.Air (
      X_default={0.015, 0.985}))
      "Boundary condition"
  BoundaryCondition bouAirWithC(
    redeclare package Medium = Buildings.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 Buildings.Fluid.FMI.Conversion.Validation;
model InletToAirDryAir
  "Validation model for inlet to air with dry air medium"
  extends Buildings.Fluid.FMI.Conversion.Validation.InletToAirMoistAir(
    redeclare replaceable package Medium = Modelica.Media.Air.SimpleAir);
end InletToAirDryAir;


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


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

  replaceable package Medium = Buildings.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"

  Buildings.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"
  Buildings.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 Buildings.Fluid.FMI.Conversion.Validation;
model InletToAirMoistAirCO2
  "Validation model for inlet to Buildings.Media.Air conversion with C02 trace substances"
  extends Buildings.Fluid.FMI.Conversion.Validation.InletToAirDryAir(
    redeclare replaceable package Medium = Buildings.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 Buildings.Fluid.FMI.Conversion;
package Validation "Collection of models that illustrate model use and test models"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


within Buildings.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 Buildings.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 Buildings.Fluid.FMI;
package ExportContainers "Package with containers to export thermofluid flow models"
  extends Modelica.Icons.Package;
end ExportContainers;


within Buildings.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 Buildings.Fluid.FMI.ExportContainers;
block ReplaceableTwoPort
  "Container to export thermofluid flow models with two ports as an FMU"
  extends Buildings.Fluid.FMI.ExportContainers.PartialTwoPort;
  replaceable Buildings.Fluid.Interfaces.PartialTwoPort com
    constrainedby Buildings.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
  Buildings.Fluid.FMI.Adaptors.Inlet bouIn(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_p_in=use_p_in) "Boundary model for inlet"

  Buildings.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 Buildings.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 Buildings.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 Buildings.Fluid.FMI.ExportContainers;
package Examples "Collection of models that illustrate model use and test models"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


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

  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 Buildings.Fluid.FMI.ExportContainers.Examples.FMUs;
block FlowSplitter_u "Declaration of an FMU that exports a flow splitter"
   extends Buildings.Fluid.FMI.FlowSplitter_u(
     redeclare replaceable package Medium = Buildings.Media.Air,
        nout=2,
        m_flow_nominal={0.1, 0.2},
        allowFlowReversal(start=false));
end FlowSplitter_u;


within Buildings.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 Buildings.Fluid.FMI.ExportContainers.ReplaceableTwoPort(
     redeclare replaceable package Medium = Buildings.Media.Air,
     redeclare final Buildings.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 Buildings.Fluid.FMI.ExportContainers.Examples.FMUs;
block Heater_T
  "Declaration of an FMU that exports an ideal heater or cooler with prescribed outlet temperature"
   extends Buildings.Fluid.FMI.ExportContainers.ReplaceableTwoPort(
     redeclare replaceable package Medium = Buildings.Media.Air,
     redeclare final Buildings.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 Buildings.Fluid.FMI.ExportContainers.Examples.FMUs;
block Humidifier_u
  "Declaration of an FMU that exports an ideal humidifier"
   extends Buildings.Fluid.FMI.ExportContainers.ReplaceableTwoPort(
     redeclare replaceable package Medium = Buildings.Media.Air,
     redeclare final Buildings.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 Buildings.Fluid.FMI.ExportContainers.Examples.FMUs;
block HVACZone
  "Declaration of an FMU that exports a simple convective only HVAC system"
  extends Buildings.Fluid.FMI.ExportContainers.HVACZone(
    redeclare final package Medium = MediumA, hvacAda(nPorts=2));

  replaceable package MediumA = Buildings.Media.Air "Medium for air";
  replaceable package MediumW = Buildings.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 Buildings.Fluid.HeatExchangers.ConstantEffectiveness cooCoi(
    dp1_nominal=6000,
    dp2_nominal=200,
    show_T=true,
    allowFlowReversal1=allowFlowReversal,
    allowFlowReversal2=allowFlowReversal) constrainedby
    Buildings.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)"

  Buildings.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"
  Buildings.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=Buildings.BoundaryConditions.Types.DataSource.Parameter,
    TDryBul=TOut_nominal,
    filNam=Modelica.Utilities.Files.loadResource("modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
    TDryBulSou=Buildings.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 Buildings.Fluid.FMI.ExportContainers.Examples.FMUs;
block HVACZones
  "Declaration of an FMU that exports a simple convective only HVAC system for two zones"
  extends Buildings.Fluid.FMI.ExportContainers.HVACZones(
    redeclare final package Medium = MediumA,
    nZon = 2,
    nPorts = 3);

  replaceable package MediumA = Buildings.Media.Air "Medium for air";
  replaceable package MediumW = Buildings.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 Buildings.Fluid.HeatExchangers.ConstantEffectiveness cooCoi(
    dp1_nominal=6000,
    dp2_nominal=200,
    show_T=true,
    allowFlowReversal1=allowFlowReversal,
    allowFlowReversal2=allowFlowReversal) constrainedby
    Buildings.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)"

  Buildings.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"
  Buildings.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=Buildings.BoundaryConditions.Types.DataSource.Parameter,
    TDryBul=TOut_nominal,
    filNam=Modelica.Utilities.Files.loadResource("modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
    TDryBulSou=Buildings.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=Buildings.Fluid.Types.InputType.Constant)
    "Supply air fan that extracts a constant amount of outside air"
  Buildings.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"
  Buildings.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"
  Buildings.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"
  Buildings.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"
  Buildings.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 Buildings.Fluid.FMI.ExportContainers.Examples.FMUs;
block IdealSource_m_flow "Declaration of an FMU that exports a mass flow source and sink"
   extends Buildings.Fluid.FMI.ExportContainers.PartialTwoPort(
     redeclare replaceable package Medium = Buildings.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 Buildings.Fluid.FMI.ExportContainers.Examples.FMUs;
block MixingVolume "Declaration of an FMU that exports a control volume"
  extends Buildings.Fluid.FMI.ExportContainers.PartialTwoPort(
    redeclare package Medium = Buildings.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
  Buildings.Fluid.FMI.Adaptors.Inlet bouIn(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal) "Boundary model for inlet"
  Buildings.Fluid.FMI.Adaptors.Outlet bouOut(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal) "Boundary component for outlet"

  Buildings.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 Buildings.Fluid.FMI.ExportContainers.Examples;
package FMUs "Collection of example models"
  extends Modelica.Icons.ExamplesPackage;
end FMUs;


within Buildings.Fluid.FMI.ExportContainers.Examples.FMUs;
block PressureDrop
  "Declaration of an FMU that exports a fixed resistance"
   extends Buildings.Fluid.FMI.ExportContainers.ReplaceableTwoPort(
     redeclare replaceable package Medium = Buildings.Media.Air, redeclare final
      Buildings.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 Buildings.Fluid.FMI.ExportContainers.Examples.FMUs;
block ResistanceVolume
  "Declaration of an FMU that exports a flow resistance and control volume"
  extends Buildings.Fluid.FMI.ExportContainers.PartialTwoPort(
    redeclare package Medium = Buildings.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
  Buildings.Fluid.FMI.Adaptors.Inlet bouIn(
    redeclare final package Medium=Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_p_in=use_p_in) "Boundary model for inlet"
  Buildings.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"
  Buildings.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 Buildings.Fluid.FMI.ExportContainers.Examples.FMUs;
model ThermalZone
  "Declaration of an FMU that exports a thermal zone"
  extends Buildings.Fluid.FMI.ExportContainers.ThermalZone(
    redeclare final package Medium = MediumA,
    nPorts =  2);

  replaceable package MediumA = Buildings.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=Buildings.BoundaryConditions.Types.DataSource.Parameter,
    TDryBul=TOut_nominal,
    filNam=Modelica.Utilities.Files.loadResource("modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
    TDryBulSou=Buildings.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"
  Buildings.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 Buildings.Fluid.FMI.ExportContainers.Examples.FMUs;
model ThermalZones
  "Declaration of an FMU that exports multiple thermal zones"
  extends Buildings.Fluid.FMI.ExportContainers.ThermalZones(
    redeclare final package Medium = MediumA,
    nPorts =  3,
    nZon = 2);

  replaceable package MediumA = Buildings.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=Buildings.BoundaryConditions.Types.DataSource.Parameter,
    TDryBul=TOut_nominal,
    filNam=Modelica.Utilities.Files.loadResource("modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
    TDryBulSou=Buildings.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"
  Buildings.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 Buildings.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 Buildings.Fluid.FMI.ExportContainers.PartialTwoPort(
     redeclare replaceable package Medium = Buildings.Media.Air);

equation
  connect(inlet, outlet)
end TwoPortPassThrough;


Collection of validation models

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


Validation model for connected single thermal zone and HVAC system. This example is the base case model which is used to validate
the coupling of a convective thermal zone with an air-based HVAC system.

within Buildings.Fluid.FMI.ExportContainers.Validation;
model RoomHVAC
  "Validation model for connected single thermal zone and HVAC system"
 extends Modelica.Icons.Example;

  Buildings.Fluid.FMI.ExportContainers.Examples.FMUs.HVACZone hvaCon(
    redeclare Buildings.Fluid.HeatExchangers.WetCoilEffectivenessNTU cooCoi(
      use_Q_flow_nominal=true,
      Q_flow_nominal=hvaCon.QCoiC_flow_nominal,
      T_a1_nominal=hvaCon.TWSup_nominal,
      T_a2_nominal=hvaCon.THeaRecLvg,
      w_a2_nominal=hvaCon.wHeaRecLvg,
      dp1_nominal=6000,
      dp2_nominal=200,
      show_T=true,
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
      allowFlowReversal1=hvaCon.allowFlowReversal,
      allowFlowReversal2=hvaCon.allowFlowReversal))
    "Block that encapsulates the HVAC system"
  Buildings.Fluid.FMI.ExportContainers.Examples.FMUs.ThermalZone rooCon
    "Block that encapsulates the thermal zone"
  BaseCase baseCase
  Examples.FMUs.HVACZones hvaCon2(
    redeclare Buildings.Fluid.HeatExchangers.WetCoilEffectivenessNTU cooCoi(
      use_Q_flow_nominal=true,
      Q_flow_nominal=hvaCon2.QCoiC_flow_nominal,
      T_a1_nominal=hvaCon2.TWSup_nominal,
      T_a2_nominal=hvaCon2.THeaRecLvg,
      w_a2_nominal=hvaCon2.wHeaRecLvg,
      dp1_nominal=6000,
      dp2_nominal=200,
      show_T=true,
      energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
      allowFlowReversal1=hvaCon2.allowFlowReversal,
      allowFlowReversal2=hvaCon2.allowFlowReversal),
    UA = 20E3,
    QRooInt_flow = 2000,
    fan2(constantMassFlowRate=0))
    "Block that encapsulates the HVAC system"
  TwoRooms rooCon2 "Model with two rooms"

protected
    model BaseCase "Base case model used for the validation of the FMI interfaces"
    extends Buildings.Examples.Tutorial.SpaceCooling.System3(
      vol(energyDynamics=
      Modelica.Fluid.Types.Dynamics.FixedInitial),
      hex(dp1_nominal=200 + 10,
          dp2_nominal=200 + 200));
    end BaseCase;

  model TwoRooms "Model with two simple thermal zones, each having three air flow paths"
    extends
      Buildings.Fluid.FMI.ExportContainers.Examples.FMUs.ThermalZones;
  end TwoRooms;

equation

  connect(hvaCon.fluPor, rooCon.fluPor)
  connect(hvaCon2.fluPor, rooCon2.fluPor)
  connect(rooCon.TRad, hvaCon.TRadZon)
  connect(rooCon2.TRad1, hvaCon2.TRadZon[1])
  connect(rooCon2.TRad2, hvaCon2.TRadZon[2])
end RoomHVAC;


within Buildings.Fluid.FMI.ExportContainers.Validation.FMUs;
block HVACThermalZoneAir1 "Validation model for the convective HVAC system"
  extends Buildings.Fluid.FMI.ExportContainers.HVACZone(
    redeclare package Medium = Buildings.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 Buildings.Fluid.FMI.ExportContainers.Validation.FMUs;
block HVACThermalZoneAir2 "Validation model for the convective HVAC system"
  extends Buildings.Fluid.FMI.ExportContainers.Validation.FMUs.HVACThermalZoneAir1(
    redeclare package Medium = Buildings.Media.Air(extraPropertiesNames={"CO2"}));
end HVACThermalZoneAir2;


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


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


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


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


within Buildings.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 Buildings.Fluid.FMI.ExportContainers.Validation.FMUs;
block ThermalZoneAir1 "Validation of simple thermal zone"
  extends Buildings.Fluid.FMI.ExportContainers.ThermalZone(
    redeclare package Medium = Buildings.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"

  Buildings.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 Buildings.Fluid.FMI.ExportContainers.Validation.FMUs;
block ThermalZoneAir2 "Validation of simple thermal zone"
  extends Buildings.Fluid.FMI.ExportContainers.Validation.FMUs.ThermalZoneAir1(
    redeclare package Medium = Buildings.Media.Air(extraPropertiesNames={"CO2"}));
end ThermalZoneAir2;


Validation of simple thermal zone

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


Validation of simple thermal zone

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


Validation of simple thermal zone

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


Validation of simple thermal zone

within Buildings.Fluid.FMI.ExportContainers.Validation.FMUs;
block ThermalZoneSimpleAir3 "Validation of simple thermal zone"
  extends Buildings.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 Buildings.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";
  Buildings.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 Buildings.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";
  Buildings.Fluid.FMI.Interfaces.PressureInput p
  if use_p_in "Thermodynamic pressure in the connection point";

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


Connector for fluid outlet

within Buildings.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";
  Buildings.Fluid.FMI.Interfaces.PressureOutput p
    if use_p_in "Thermodynamic pressure in the connection point";

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

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


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


Flow splitter

within Buildings.Fluid.FMI.Validation;
model FlowSplitter_u "Flow splitter"
  extends Modelica.Icons.Example;
  package Medium = Buildings.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;