within AixLib.Airflow.Multizone;
model MediumColumn
  "Vertical shaft with no friction and no storage of heat and mass"

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

  parameter Modelica.Units.SI.Length h(min=0) = 3 "Height of shaft";
  parameter AixLib.Airflow.Multizone.Types.densitySelection densitySelection
    "Select how to pick density"

  Modelica.Fluid.Interfaces.FluidPort_a port_a(
    redeclare package Medium = Medium,
    p(start=Medium.p_default))
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b(
    redeclare package Medium = Medium,
    p(start=Medium.p_default))
    "Fluid connector b (positive design flow direction is from port_a to port_b)"

  Modelica.Units.SI.VolumeFlowRate V_flow
    "Volume flow rate at inflowing port (positive when flow from port_a to port_b)";
  Modelica.Units.SI.MassFlowRate m_flow
    "Mass flow rate from port_a to port_b (m_flow > 0 is design flow direction)";
  Modelica.Units.SI.PressureDifference dp(displayUnit="Pa")
    "Pressure difference between port_a and port_b";
  Modelica.Units.SI.Density rho "Density in medium column";
protected
  Medium.ThermodynamicState sta_a=Medium.setState_phX(
      port_a.p,
      actualStream(port_a.h_outflow),
      actualStream(port_a.Xi_outflow)) "Medium properties in port_a";
  Medium.MassFraction Xi[Medium.nXi] "Mass fraction used to compute density";
initial equation
  // The next assert tests for all allowed values of the enumeration.
  // Testing against densitySelection > 0 gives an error in OpenModelica as enumerations start with 1.
  assert(densitySelection == AixLib.Airflow.Multizone.Types.densitySelection.fromTop
     or densitySelection == AixLib.Airflow.Multizone.Types.densitySelection.fromBottom
     or densitySelection == AixLib.Airflow.Multizone.Types.densitySelection.actual,
    "You need to set the parameter \"densitySelection\" for the model MediumColumn.");
equation
  // Design direction of mass flow rate
  m_flow = port_a.m_flow;

  // Pressure difference between ports
  // Xi is computed first as it is used in two expression, and in one
  // of them only one component is used.
  // We test for Medium.nXi == 0 as Modelica.Media.Air.SimpleAir has no
  // moisture and hence Xi[1] is an illegal statement.
  // We first compute temperature and then invoke a density function that
  // takes temperature as an argument. Simply calling a density function
  // of a medium that takes enthalpy as an argument would be dangerous
  // as different media can have different datum for the enthalpy.
  if (densitySelection == AixLib.Airflow.Multizone.Types.densitySelection.fromTop) then
      Xi = inStream(port_a.Xi_outflow);
      rho = AixLib.Utilities.Psychrometrics.Functions.density_pTX(
        p=Medium.p_default,
        T=Medium.temperature(Medium.setState_phX(port_a.p, inStream(port_a.h_outflow), Xi)),
        X_w=if Medium.nXi == 0 then 0 else Xi[1]);
  elseif (densitySelection == AixLib.Airflow.Multizone.Types.densitySelection.fromBottom) then
      Xi = inStream(port_b.Xi_outflow);
      rho = AixLib.Utilities.Psychrometrics.Functions.density_pTX(
        p=Medium.p_default,
        T=Medium.temperature(Medium.setState_phX(port_b.p, inStream(port_b.h_outflow), Xi)),
        X_w=if Medium.nXi == 0 then 0 else Xi[1]);
   else
      Xi = actualStream(port_a.Xi_outflow);
      rho = AixLib.Utilities.Psychrometrics.Functions.density_pTX(
        p=Medium.p_default,
        T=Medium.temperature(Medium.setState_phX(port_a.p, actualStream(port_a.h_outflow), Xi)),
        X_w=if Medium.nXi == 0 then 0 else Xi[1]);
  end if;

  V_flow = m_flow/Medium.density(sta_a);

  dp = port_a.p - port_b.p;
  dp = -h*rho*Modelica.Constants.g_n;

  // Isenthalpic state transformation (no storage and no loss of energy)
  port_a.h_outflow = inStream(port_b.h_outflow);
  port_b.h_outflow = inStream(port_a.h_outflow);

  // Mass balance (no storage)
  port_a.m_flow + port_b.m_flow = 0;

  // Transport of substances
  port_a.Xi_outflow = inStream(port_b.Xi_outflow);
  port_b.Xi_outflow = inStream(port_a.Xi_outflow);

  port_a.C_outflow = inStream(port_b.C_outflow);
  port_b.C_outflow = inStream(port_a.C_outflow);
end MediumColumn;


within AixLib.Airflow.Multizone;
model MediumColumnDynamic
  "Vertical shaft with no friction and storage of heat and mass"
  extends AixLib.Fluid.Interfaces.LumpedVolumeDeclarations(
    final massDynamics=energyDynamics);

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

  parameter Modelica.Units.SI.Length h(min=0) = 3 "Height of shaft";

  Modelica.Fluid.Interfaces.FluidPort_a port_a(
    redeclare final package Medium = Medium,
    p(start=Medium.p_default))
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b(
    redeclare final package Medium = Medium,
    p(start=Medium.p_default))
    "Fluid connector b (positive design flow direction is from port_a to port_b)"

  parameter Modelica.Units.SI.Volume V "Volume in medium shaft";

  // Heat transfer through boundary
  parameter Boolean use_HeatTransfer = false
    "= true to use the HeatTransfer model"

  // m_flow_nominal is not used by vol, since this component
  // can only be configured as a dynamic model.
  // Therefore we set it to about 10 air changes per hour
  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    final nPorts=2,
    redeclare final package Medium = Medium,
    final m_flow_nominal=V/360,
    final V=V,
    final energyDynamics=energyDynamics,
    final massDynamics=massDynamics,
    final p_start=p_start,
    final T_start=T_start,
    final X_start=X_start,
    final C_start=C_start) "Air volume in the shaft"

  MediumColumn colTop(
    redeclare final package Medium = Medium,
    final densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom,
    h=h/2)
    "Medium column that connects to top port"

  MediumColumn colBot(
    redeclare final package Medium = Medium,
    final densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop,
    h=h/2)
    "Medium colum that connects to bottom port"

  replaceable model HeatTransfer =
      Modelica.Fluid.Vessels.BaseClasses.HeatTransfer.IdealHeatTransfer
    constrainedby
    Modelica.Fluid.Vessels.BaseClasses.HeatTransfer.PartialVesselHeatTransfer
    "Wall heat transfer"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort if use_HeatTransfer
    "Heat port to exchange energy with the fluid volume"

equation
  connect(colBot.port_a, vol.ports[1])
  connect(vol.ports[2], colTop.port_b)
  connect(colTop.port_a, port_a)

  connect(colBot.port_b, port_b)
  connect(heatPort, vol.heatPort)
end MediumColumnDynamic;


Orifice. This model describes the mass flow rate and pressure difference relation
of an orifice in the form

within AixLib.Airflow.Multizone;
model Orifice "Orifice"
  extends AixLib.Airflow.Multizone.Coefficient_V_flow(
    m=0.5,
    final C=CD*A*sqrt(2.0/rho_default));

  parameter Modelica.Units.SI.Area A "Area of orifice"
  parameter Real CD=0.65 "Discharge coefficient"

  Modelica.Units.SI.Velocity v(nominal=1) = V_flow/A "Average velocity";
end Orifice;


within AixLib.Airflow;
package Multizone "Package with models for multizone airflow and contaminant transport"
  extends Modelica.Icons.VariantsPackage;
end Multizone;


Powerlaw with flow coefficient and flow exponent fitted based on 2 datapoints. Model that fits the flow coefficient of the massflow version of the
orifice equation based on 2 datapoints of mass flow rate and pressure difference.

within AixLib.Airflow.Multizone;
model Points_m_flow
  "Powerlaw with flow coefficient and flow exponent fitted based on 2 datapoints"
  extends AixLib.Airflow.Multizone.Coefficient_m_flow(final k=
    mMea_flow_nominal[1]/(dpMea_nominal[1]^m2),
    final m=m2);

  parameter Modelica.Units.SI.PressureDifference dpMea_nominal[2](each displayUnit="Pa")
    "Pressure difference of two test points"
  parameter Modelica.Units.SI.MassFlowRate mMea_flow_nominal[2]
    "Mass flow rate of two test points"
protected
  parameter Real m2 = (log(mMea_flow_nominal[1]) - log(mMea_flow_nominal[2]))/
             (log(dpMea_nominal[1]) -log(dpMea_nominal[2])) "Flow exponent";
end Points_m_flow;


Powerlaw with flow coeffient fitted based on flow exponent and 1 datapoint. Model that fits the flow coefficient of the massflow version of the
orifice equation based on 1 datapoint of mass flow rate and pressure difference, and given flow exponent.

within AixLib.Airflow.Multizone;
model Point_m_flow
  "Powerlaw with flow coeffient fitted based on flow exponent and 1 datapoint"
  extends AixLib.Airflow.Multizone.Coefficient_m_flow(final k=mMea_flow_nominal/
        (dpMea_nominal^m));
  parameter Modelica.Units.SI.PressureDifference dpMea_nominal(displayUnit="Pa")
    "Pressure difference of test point"
  parameter Modelica.Units.SI.MassFlowRate mMea_flow_nominal
    "Mass flow rate of test point"
end Point_m_flow;


Mass flow(y-axis) vs Pressure(x-axis) cubic spline fit model based from table data, with last two points linearly interpolated. This model describes the one-directional pressure driven air flow through an
opening based on user-provided tabular data describing the relation between mass flow rate
and pressure difference over the component.

within AixLib.Airflow.Multizone;
model Table_m_flow
  "Mass flow(y-axis) vs Pressure(x-axis) cubic spline fit model based from table data, with last two points linearly interpolated"
  extends AixLib.Airflow.Multizone.BaseClasses.PartialOneWayFlowElement(
    m_flow = AixLib.Utilities.Math.Functions.interpolate(
      u=dp,
      xd=dpMea_nominal,
      yd=mMea_flow_nominal,
      d=d),
    final m_flow_nominal=max(abs(dpMea_nominal[1]), abs(dpMea_nominal[end])));

  parameter Modelica.Units.SI.PressureDifference dpMea_nominal[:](each displayUnit="Pa")
    "Pressure difference of test points"
  parameter Modelica.Units.SI.MassFlowRate mMea_flow_nominal[:]
    "Mass flow rate of test points"
protected
  parameter Real[size(dpMea_nominal, 1)] d = AixLib.Utilities.Math.Functions.splineDerivatives(
    x=dpMea_nominal,
    y=mMea_flow_nominal,
    ensureMonotonicity=true)
    "Derivatives at the support points";
initial equation
  assert(size(dpMea_nominal, 1) == size(mMea_flow_nominal, 1),
    "Size of parameters are size(dpMea_nominal, 1) = " + String(size(dpMea_nominal, 1)) +
    " and size(mMea_flow_nominal, 1) = " + String(size(mMea_flow_nominal, 1)) + ". They must be equal.");
end Table_m_flow;


Volume flow(y-axis) vs Pressure(x-axis) cubic spline fit model based on table data, with last two points linearly interpolated. This model describes the one-directional pressure driven air flow through an
opening based on user-provided tabular data describing the relation between volume flow rate
and pressure difference over the component.

within AixLib.Airflow.Multizone;
model Table_V_flow
  "Volume flow(y-axis) vs Pressure(x-axis) cubic spline fit model based on table data, with last two points linearly interpolated"
  extends AixLib.Airflow.Multizone.Table_m_flow(
    final mMea_flow_nominal = VMea_flow_nominal*rho_default);

  parameter Modelica.Units.SI.VolumeFlowRate VMea_flow_nominal[:]
    "Volume flow rate of test points"
initial equation
  assert(size(dpMea_nominal, 1) == size(VMea_flow_nominal, 1),
    "Size of parameters are size(dpMea_nominal, 1) = " + String(size(dpMea_nominal, 1)) +
    " and size(VMea_flow_nominal, 1) = " + String(size(VMea_flow_nominal, 1)) + ". They must be equal.");
end Table_V_flow;


User's Guide

within AixLib.Airflow.Multizone;
package UsersGuide "User's Guide"
  extends Modelica.Icons.Information;

end UsersGuide;


Zonal flow with input air change per second. This model computes the air exchange between volumes.

within AixLib.Airflow.Multizone;
model ZonalFlow_ACS "Zonal flow with input air change per second"
  extends AixLib.Airflow.Multizone.BaseClasses.ZonalFlow;

  parameter Boolean useDefaultProperties = false
    "Set to true to use constant density";
  parameter Modelica.Units.SI.Volume V "Volume of room";

  Modelica.Blocks.Interfaces.RealInput ACS
    "Air change per seconds, relative to the smaller of the two volumes"
protected
  Modelica.Units.SI.VolumeFlowRate V_flow
    "Volume flow rate at standard pressure";
  Modelica.Units.SI.MassFlowRate m_flow "Mass flow rate";
  parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX(T=Medium.T_default,
         p=Medium.p_default, X=Medium.X_default);
  parameter Modelica.Units.SI.Density rho_default=Medium.density(sta_default)
    "Density, used to compute fluid volume";

  Medium.ThermodynamicState sta_a1_inflow=
      Medium1.setState_phX(port_a1.p, port_b1.h_outflow, port_b1.Xi_outflow)
    "Medium properties in port_a1";
  Medium.ThermodynamicState sta_a2_inflow=
      Medium1.setState_phX(port_a2.p, port_b2.h_outflow, port_b2.Xi_outflow)
    "Medium properties in port_a2";

equation
  when useDefaultProperties and initial() then
   assert( abs(1-rho_default/((Medium.density(sta_a1_inflow) + Medium.density(sta_a2_inflow))/2))  < 0.2,
    "Wrong density. Densities need to match.");
// The next three lines have been removed as they cause
// a compilation error in OpenModelica.
//    + "\n Medium.density(sta_a1) = " + String(Medium.density(sta_a1_inflow))
//    + "\n Medium.density(sta_a2) = " + String(Medium.density(sta_a2_inflow))
//    + "\n rho_nominal            = " + String(rho_default));
  end when;
  V_flow = V * ACS;
  m_flow / V_flow = if useDefaultProperties then rho_default else (Medium.density(sta_a1_inflow) + Medium.density(sta_a2_inflow))/2;
  // assign variable in base class
  port_a1.m_flow = m_flow;
  port_a2.m_flow = m_flow;
end ZonalFlow_ACS;


within AixLib.Airflow.Multizone;
model ZonalFlow_m_flow "Zonal flow with input air change per second"
  extends AixLib.Airflow.Multizone.BaseClasses.ZonalFlow;
   Modelica.Blocks.Interfaces.RealInput mAB_flow "Mass flow rate from A to B, positive if flow from port_a1 to port_b1"
  Modelica.Blocks.Interfaces.RealInput mBA_flow "Mass flow rate from B to A, positive if flow from port_a2 to port_b2"

equation
  port_a1.m_flow = mAB_flow;
  port_a2.m_flow = mBA_flow;
end ZonalFlow_m_flow;


within AixLib.Airflow.Multizone.BaseClasses;
partial model Door
  "Partial door model for bi-directional flow"
  extends AixLib.Fluid.Interfaces.PartialFourPortInterface(
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium,
    final allowFlowReversal1=true,
    final allowFlowReversal2=true,
    final m1_flow_nominal=10/3600*1.2,
    final m2_flow_nominal=m1_flow_nominal,
    final m1_flow_small=1E-4*abs(m1_flow_nominal),
    final m2_flow_small=1E-4*abs(m2_flow_nominal));

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

  parameter Modelica.Units.SI.Length wOpe=0.9 "Width of opening"
  parameter Modelica.Units.SI.Length hOpe=2.1 "Height of opening"

  parameter Modelica.Units.SI.PressureDifference dp_turbulent(
    min=0,
    displayUnit="Pa") = 0.01
    "Pressure difference where laminar and turbulent flow relation coincide"

  Modelica.Units.SI.VolumeFlowRate VAB_flow(nominal=0.001)
    "Volume flow rate from A to B if positive";
  Modelica.Units.SI.VolumeFlowRate VBA_flow(nominal=0.001)
    "Volume flow rate from B to A if positive";

  input Modelica.Units.SI.Velocity vAB(nominal=0.01)
    "Average velocity from A to B";
  input Modelica.Units.SI.Velocity vBA(nominal=0.01)
    "Average velocity from B to A";

protected
  final parameter Modelica.Units.SI.Area AOpe=wOpe*hOpe "Open aperture area";

  constant Real conTP = AixLib.Media.Air.dStp*Modelica.Media.IdealGases.Common.SingleGasesData.Air.R_s
    "Conversion factor for converting temperature difference to pressure difference";

  parameter Medium.ThermodynamicState sta_default=Medium.setState_pTX(
      T=Medium.T_default,
      p=Medium.p_default,
      X=Medium.X_default);

  parameter Modelica.Units.SI.Density rho_default=Medium.density(sta_default)
    "Density";

  Modelica.Units.SI.VolumeFlowRate VABp_flow(nominal=0.001)
    "Volume flow rate from A to B if positive due to static pressure difference";
  Modelica.Units.SI.MassFlowRate mABt_flow(nominal=0.001)
    "Mass flow rate from A to B if positive due to buoyancy";

equation
  // Net flow rate
  port_a1.m_flow = (rho_default * VABp_flow/2 + mABt_flow);
  port_b2.m_flow = (rho_default * VABp_flow/2 - mABt_flow);

  // Average velocity (using the whole orifice area)
  VAB_flow = (max(port_a1.m_flow, 0) + max(port_b2.m_flow, 0))/rho_default;
  VBA_flow = (max(port_a2.m_flow, 0) + max(port_b1.m_flow, 0))/rho_default;

  // Energy balance (no storage, no heat loss/gain)
  port_a1.h_outflow = inStream(port_b1.h_outflow);
  port_b1.h_outflow = inStream(port_a1.h_outflow);
  port_a2.h_outflow = inStream(port_b2.h_outflow);
  port_b2.h_outflow = inStream(port_a2.h_outflow);

  // Mass balance (no storage)
  port_a1.m_flow = -port_b1.m_flow;
  port_a2.m_flow = -port_b2.m_flow;

  port_a1.Xi_outflow = inStream(port_b1.Xi_outflow);
  port_b1.Xi_outflow = inStream(port_a1.Xi_outflow);
  port_a2.Xi_outflow = inStream(port_b2.Xi_outflow);
  port_b2.Xi_outflow = inStream(port_a2.Xi_outflow);

  // Transport of trace substances
  port_a1.C_outflow = inStream(port_b1.C_outflow);
  port_b1.C_outflow = inStream(port_a1.C_outflow);
  port_a2.C_outflow = inStream(port_b2.C_outflow);
  port_b2.C_outflow = inStream(port_a2.C_outflow);
end Door;


Door model using discretization along height coordinate. This is a partial model for the bi-directional air flow through a door.

within AixLib.Airflow.Multizone.BaseClasses;
partial model DoorDiscretized
  "Door model using discretization along height coordinate"
  extends AixLib.Airflow.Multizone.BaseClasses.TwoWayFlowElementBuoyancy;

  parameter Integer nCom=10 "Number of compartments for the discretization";

  parameter Modelica.Units.SI.PressureDifference dp_turbulent(
    min=0,
    displayUnit="Pa") = 0.01
    "Pressure difference where laminar and turbulent flow relation coincide. Recommended: 0.01";

  Modelica.Units.SI.PressureDifference dpAB[nCom](each nominal=1)
    "Pressure difference between compartments";
  Modelica.Units.SI.Velocity v[nCom](each nominal=0.01)
    "Velocity in compartment from A to B";
  Modelica.Units.SI.Velocity vTop "Velocity at top of opening from A to B";
  Modelica.Units.SI.Velocity vBot "Velocity at bottom of opening from A to B";

protected
  parameter Modelica.Units.SI.Length dh=hOpe/nCom "Height of each compartment";

  parameter Medium.ThermodynamicState sta_default=Medium.setState_pTX(
      T=Medium.T_default,
      p=Medium.p_default,
      X=Medium.X_default);

  parameter Modelica.Units.SI.Density rho_default=Medium.density(sta_default)
    "Density, used to compute fluid volume";

  parameter Real hAg[nCom](each unit="m2/s2")=
    {Modelica.Constants.g_n*(hA - (i - 0.5)*dh) for i in 1:nCom}
    "Product g*h_i for each compartment";

  parameter Real hBg[nCom](each unit="m2/s2")=
    {Modelica.Constants.g_n*(hB - (i - 0.5)*dh) for i in 1:nCom}
    "Product g*h_i for each compartment";
  Modelica.Units.SI.AbsolutePressure pA[nCom](each nominal=101325)
    "Pressure in compartments of room A";
  Modelica.Units.SI.AbsolutePressure pB[nCom](each nominal=101325)
    "Pressure in compartments of room B";

  Modelica.Units.SI.VolumeFlowRate dV_flow[nCom]
    "Volume flow rate through compartment from A to B";
  Modelica.Units.SI.VolumeFlowRate dVAB_flow[nCom]
    "Volume flow rate through compartment from A to B if positive";
  Modelica.Units.SI.VolumeFlowRate dVBA_flow[nCom]
    "Volume flow rate through compartment from B to A if positive";
  Modelica.Units.SI.VolumeFlowRate VZerCom_flow=VZer_flow/nCom
    "Small flow rate for regularization";

  Real m(min=0.5, max=1) "Flow exponent, m=0.5 for turbulent, m=1 for laminar";
  Real CVal "Flow coefficient for each compartment, C = V_flow/ dp^m";
  Modelica.Units.SI.Area dA "Compartment area";
  Real gaiFlo[nCom] "Gain to sum up the positive flows and set the negative to zero in a differentiable way";
equation
  dA = A/nCom;

  for i in 1:nCom loop
    // pressure drop in each compartment
    pA[i] = port_a1.p + rho_a1_inflow*hAg[i];
    pB[i] = port_a2.p + rho_a2_inflow*hBg[i];
    dpAB[i] = pA[i] - pB[i];
    v[i] = dV_flow[i]/dA;
    // assignment of net volume flows
    gaiFlo[i] = AixLib.Utilities.Math.Functions.smoothHeaviside(x=dV_flow[i], delta=VZerCom_flow);
    dVAB_flow[i] =  dV_flow[i] * gaiFlo[i];
    dVBA_flow[i] = -dV_flow[i] * (1-gaiFlo[i]);
  end for;
  // add positive and negative flows
  VAB_flow = sum(dVAB_flow);
  VBA_flow = sum(dVBA_flow);
  vTop = v[nCom];
  vBot = v[1];
end DoorDiscretized;


Interface that defines parameters for error control. This is an interface that defines parameters used for error control.

within AixLib.Airflow.Multizone.BaseClasses;
model ErrorControl "Interface that defines parameters for error control"
  parameter Boolean forceErrorControlOnFlow = true
    "Flag to force error control on m_flow. Set to true if interested in flow rate"
end ErrorControl;


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


Partial model for flow resistance with one-way flow. This partial model is used to model one way flow-elements.
It holds the conservation equations and should be extended by
definition of one of the following variables:

within AixLib.Airflow.Multizone.BaseClasses;
partial model PartialOneWayFlowElement
  "Partial model for flow resistance with one-way flow"
  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface(
    final allowFlowReversal=true);
  extends AixLib.Airflow.Multizone.BaseClasses.ErrorControl;

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

  parameter Boolean useDefaultProperties=true
    "Set to false to use density and viscosity based on actual medium state, rather than using default values"
  parameter Modelica.Units.SI.PressureDifference dp_turbulent(min=0, displayUnit="Pa") = 0.1
    "Pressure difference where laminar and turbulent flow relation coincide. Recommended = 0.1"

  Modelica.Units.SI.VolumeFlowRate V_flow = m_flow/rho
    "Volume flow rate through the component";
  Modelica.Units.SI.Density rho "Fluid density at port_a";

protected
  parameter Medium.ThermodynamicState sta_default=Medium.setState_pTX(
      T=Medium.T_default,
      p=Medium.p_default,
      X=Medium.X_default)
    "State of the medium at the medium default properties";
  parameter Modelica.Units.SI.Density rho_default=Medium.density(sta_default)
    "Density at the medium default properties";
  parameter Modelica.Units.SI.DynamicViscosity dynVis_default=
    Medium.dynamicViscosity(sta_default)
    "Dynamic viscosity at the medium default properties";

  Medium.ThermodynamicState sta "State of the medium in the component";
  Modelica.Units.SI.DynamicViscosity dynVis "Dynamic viscosity";
  Real mExc(quantity="Mass", final unit="kg")
    "Air mass exchanged (for purpose of error control only)";

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

equation
  if forceErrorControlOnFlow then
    der(mExc) = port_a.m_flow;
  else
    der(mExc) = 0;
  end if;

  if useDefaultProperties then
    sta = sta_default;
    rho = rho_default;
    dynVis = dynVis_default;

  else
    sta = if homotopyInitialization then
        Medium.setState_phX(
          port_a.p,
          homotopy(
            actual=actualStream(port_a.h_outflow),
            simplified=inStream(port_a.h_outflow)),
          homotopy(
            actual=actualStream(port_a.Xi_outflow),
            simplified=inStream(port_a.Xi_outflow)))
      else
        Medium.setState_phX(
          port_a.p,
          actualStream(port_a.h_outflow),
          actualStream(port_a.Xi_outflow));

    rho = Medium.density(sta);
    dynVis = Medium.dynamicViscosity(sta);
  end if;

  // Isenthalpic state transformation (no storage and no loss of energy)
  port_a.h_outflow = inStream(port_b.h_outflow);
  port_b.h_outflow = inStream(port_a.h_outflow);

  // Mass balance (no storage)
  port_a.m_flow + port_b.m_flow = 0;

  // Transport of substances
  port_a.Xi_outflow = inStream(port_b.Xi_outflow);
  port_b.Xi_outflow = inStream(port_a.Xi_outflow);

  port_a.C_outflow = inStream(port_b.C_outflow);
  port_b.C_outflow = inStream(port_a.C_outflow);

  m_flow=port_a.m_flow;
end PartialOneWayFlowElement;


Power law used in orifice equations. This model describes the mass flow rate and pressure difference relation
of an orifice in the form

within AixLib.Airflow.Multizone.BaseClasses;
function powerLaw "Power law used in orifice equations"
  extends Modelica.Icons.Function;

  input Real C "Flow coefficient, C = V_flow/ dp^m";
  input Modelica.Units.SI.PressureDifference dp(displayUnit="Pa")
    "Pressure difference";
  input Real m(min=0.5, max=1)
    "Flow exponent, m=0.5 for turbulent, m=1 for laminar";
  input Modelica.Units.SI.PressureDifference dp_turbulent(
    min=0,
    displayUnit="Pa") = 0.001 "Pressure difference where regularization starts";
  output Modelica.Units.SI.VolumeFlowRate V_flow "Volume flow rate";
protected
  constant Real gamma(min=1) = 1.5
    "Normalized flow rate where dphi(0)/dpi intersects phi(1)";
  Real a
    "Polynomial coefficient for regularized implementation of flow resistance";
  Real b
    "Polynomial coefficient for regularized implementation of flow resistance";
  Real c
    "Polynomial coefficient for regularized implementation of flow resistance";
  Real d
    "Polynomial coefficient for regularized implementation of flow resistance";
  Real pi "Normalized pressure";
  Real pi2 "Square of normalized pressure";
algorithm
 if (dp >= dp_turbulent) then
   V_flow :=C *dp^m;
 elseif (dp <= -dp_turbulent) then
   V_flow :=-C*(-dp)^m;
 else
   a := gamma;
   b := 1/8*m^2 - 3*gamma - 3/2*m + 35.0/8;
   c := -1/4*m^2 + 3*gamma + 5/2*m - 21.0/4;
   d := 1/8*m^2 - gamma - m + 15.0/8;
   pi  := dp/dp_turbulent;
   pi2 := pi*pi;
   V_flow :=C *dp_turbulent^m * pi * ( a + pi2 * ( b + pi2 * ( c + pi2 * d)));
 end if;
end powerLaw;


Power law used in orifice equations when m is constant. This model describes the mass flow rate and pressure difference relation
of an orifice in the form

within AixLib.Airflow.Multizone.BaseClasses;
function powerLawFixedM
  "Power law used in orifice equations when m is constant"
  extends Modelica.Icons.Function;

  input Real C "Flow coefficient, C = V_flow/ dp^m";
  input Modelica.Units.SI.PressureDifference dp(displayUnit="Pa")
    "Pressure difference";
  input Real m(min=0.5, max=1)
    "Flow exponent, m=0.5 for turbulent, m=1 for laminar";
  input Real a "Polynomial coefficient";
  input Real b "Polynomial coefficient";
  input Real c "Polynomial coefficient";
  input Real d "Polynomial coefficient";
  input Modelica.Units.SI.PressureDifference dp_turbulent(min=0) = 0.001
    "Pressure difference where regularization starts";
  output Modelica.Units.SI.VolumeFlowRate V_flow "Volume flow rate";
protected
  constant Real gamma(min=1) = 1.5
    "Normalized flow rate where dphi(0)/dpi intersects phi(1)";
  Real pi "Normalized pressure";
  Real pi2 "Square of normalized pressure";
algorithm
 if (dp >= dp_turbulent) then
   V_flow :=C *dp^m;
 elseif (dp <= -dp_turbulent) then
   V_flow :=-C*(-dp)^m;
 else
   pi  := dp/dp_turbulent;
   pi2 := pi*pi;
   V_flow :=C *dp_turbulent^m * pi * ( a + pi2 * ( b + pi2 * ( c + pi2 * d)));
 end if;
end powerLawFixedM;


Power law resistance parameters. Parameters that are required for the components that implement a power law resistance.

within AixLib.Airflow.Multizone.BaseClasses;
model PowerLawResistanceParameters "Power law resistance parameters"
  parameter Real m(min=0.5, max=1)
    "Flow exponent, m=0.5 for turbulent, m=1 for laminar";
protected
  constant Real gamma(min=1) = 1.5
    "Normalized flow rate where dphi(0)/dpi intersects phi(1)";

  parameter Real a = gamma
    "Polynomial coefficient for regularized implementation of flow resistance";
  parameter Real b = 1/8*m^2 - 3*gamma - 3/2*m + 35.0/8
    "Polynomial coefficient for regularized implementation of flow resistance";
  parameter Real c = -1/4*m^2 + 3*gamma + 5/2*m - 21.0/4
    "Polynomial coefficient for regularized implementation of flow resistance";
  parameter Real d = 1/8*m^2 - gamma - m + 15.0/8
    "Polynomial coefficient for regularized implementation of flow resistance";
end PowerLawResistanceParameters;


Flow resistance that uses the power law

within AixLib.Airflow.Multizone.BaseClasses;
partial model TwoWayFlowElement "Flow resistance that uses the power law"
  extends AixLib.Fluid.Interfaces.PartialFourPortInterface(
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium,
    final allowFlowReversal1=true,
    final allowFlowReversal2=true,
    final m1_flow_nominal=10/3600*1.2,
    final m2_flow_nominal=m1_flow_nominal,
    final m1_flow_small=1E-4*abs(m1_flow_nominal),
    final m2_flow_small=1E-4*abs(m2_flow_nominal));
  extends AixLib.Airflow.Multizone.BaseClasses.ErrorControl;

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

  parameter Modelica.Units.SI.Velocity vZer=0.001
    "Minimum velocity to prevent zero flow. Recommended: 0.001";

  Modelica.Units.SI.VolumeFlowRate VAB_flow(nominal=0.001)
    "Volume flow rate from A to B if positive";
  Modelica.Units.SI.VolumeFlowRate VBA_flow(nominal=0.001)
    "Volume flow rate from B to A if positive";
  Modelica.Units.SI.MassFlowRate mAB_flow(nominal=0.001)
    "Mass flow rate from A to B if positive";
  Modelica.Units.SI.MassFlowRate mBA_flow(nominal=0.001)
    "Mass flow rate from B to A if positive";

  Modelica.Units.SI.Velocity vAB(nominal=0.01) "Average velocity from A to B";
  Modelica.Units.SI.Velocity vBA(nominal=0.01) "Average velocity from B to A";

  Modelica.Units.SI.Density rho_a1_inflow
    "Density of air flowing in from port_a1";
  Modelica.Units.SI.Density rho_a2_inflow
    "Density of air flowing in from port_a2";

  Modelica.Units.SI.Area A "Face area";
protected
  Modelica.Units.SI.VolumeFlowRate VZer_flow(fixed=false)
    "Minimum net volume flow rate to prevent zero flow";

  Modelica.Units.SI.Mass mExcAB(start=0, fixed=true)
    "Air mass exchanged (for purpose of error control only)";
  Modelica.Units.SI.Mass mExcBA(start=0, fixed=true)
    "Air mass exchanged (for purpose of error control only)";

  Medium.MassFraction Xi_a1_inflow[Medium1.nXi]
    "Mass fraction of medium that flows in at port a1";
  Medium.MassFraction Xi_a2_inflow[Medium2.nXi]
    "Mass fraction of medium that flows in at port a2";
equation
  // enforcing error control on both direction rather than on the sum only
  // gives higher robustness. The reason may be that for bi-directional flow,
  // (VAB_flow - VBA_flow) may be close to zero.
  if forceErrorControlOnFlow then
    der(mExcAB) = mAB_flow;
    der(mExcBA) = mBA_flow;
  else
    der(mExcAB) = 0;
    der(mExcBA) = 0;
  end if;

  // Compute the density of the inflowing media.
  // Note that Modelica.Media.Air.SimpleAir does not contain moisture,
  // and hence we check for Medium?.nXi == 0.
  // We first compute temperature and then invoke a density function that
  // takes temperature as an argument. Simply calling a density function
  // of a medium that takes enthalpy as an argument would be dangerous
  // as different media can have different datum for the enthalpy.
  Xi_a1_inflow = inStream(port_a1.Xi_outflow);
  rho_a1_inflow = AixLib.Utilities.Psychrometrics.Functions.density_pTX(
        p=port_a1.p,
        T=Medium1.temperature(state_a1_inflow),
        X_w=if Medium1.nXi == 0 then 0 else Xi_a1_inflow[1]);
  Xi_a2_inflow = inStream(port_a2.Xi_outflow);
  rho_a2_inflow = AixLib.Utilities.Psychrometrics.Functions.density_pTX(
        p=port_a2.p,
        T=Medium2.temperature(state_a2_inflow),
        X_w=if Medium2.nXi == 0 then 0 else Xi_a2_inflow[1]);

  VZer_flow = vZer*A;

  mAB_flow = rho_a1_inflow*VAB_flow;
  mBA_flow = rho_a2_inflow*VBA_flow;
  // Average velocity (using the whole orifice area)
  vAB = VAB_flow/A;
  vBA = VBA_flow/A;

  port_a1.m_flow = mAB_flow;
  port_a2.m_flow = mBA_flow;

  // Energy balance (no storage, no heat loss/gain)
  port_a1.h_outflow = inStream(port_b1.h_outflow);
  port_b1.h_outflow = inStream(port_a1.h_outflow);
  port_a2.h_outflow = inStream(port_b2.h_outflow);
  port_b2.h_outflow = inStream(port_a2.h_outflow);

  // Mass balance (no storage)
  port_a1.m_flow = -port_b1.m_flow;
  port_a2.m_flow = -port_b2.m_flow;

  port_a1.Xi_outflow = inStream(port_b1.Xi_outflow);
  port_b1.Xi_outflow = inStream(port_a1.Xi_outflow);
  port_a2.Xi_outflow = inStream(port_b2.Xi_outflow);
  port_b2.Xi_outflow = inStream(port_a2.Xi_outflow);

  // Transport of trace substances
  port_a1.C_outflow = inStream(port_b1.C_outflow);
  port_b1.C_outflow = inStream(port_a1.C_outflow);
  port_a2.C_outflow = inStream(port_b2.C_outflow);
  port_b2.C_outflow = inStream(port_a2.C_outflow);
end TwoWayFlowElement;


Flow resistance that uses the power law. This is a partial model for models that describe the bi-directional
air flow through large openings.

within AixLib.Airflow.Multizone.BaseClasses;
partial model TwoWayFlowElementBuoyancy
  "Flow resistance that uses the power law"
  extends AixLib.Airflow.Multizone.BaseClasses.TwoWayFlowElement;

  parameter Modelica.Units.SI.Length wOpe=0.9 "Width of opening"
  parameter Modelica.Units.SI.Length hOpe=2.1 "Height of opening"

  parameter Modelica.Units.SI.Length hA=2.7/2
    "Height of reference pressure zone A"
  parameter Modelica.Units.SI.Length hB=2.7/2
    "Height of reference pressure zone B"
end TwoWayFlowElementBuoyancy;


Wind pressure coefficient for low-rise buildings. This function computes the wind pressure coefficient for
low-rise buildings with rectangular shape.
The correlation is the data fit from Swami and Chandra (1987),
who fitted a function to various wind pressure coefficients from the literature.
The same correlation is also implemented in CONTAM (Persily and Ivy, 2001).

within AixLib.Airflow.Multizone.BaseClasses;
function windPressureLowRise "Wind pressure coefficient for low-rise buildings"
  extends Modelica.Icons.Function;

  input Real Cp0
    "Wind pressure coefficient for normal wind incidence angle";
  input Modelica.Units.SI.Angle alpha
    "Wind incidence angle (0: normal to wall)";
  input Real G "Natural logarithm of side ratio";
  output Real Cp "Wind pressure coefficient";
protected
  constant Modelica.Units.SI.Angle pi2=2*Modelica.Constants.pi;
  constant Modelica.Units.SI.Angle aRDel=5*Modelica.Constants.pi/180
    "Lower bound where transition occurs";
  constant Modelica.Units.SI.Angle aRDel2=aRDel/2
    "Half-width of transition interval";
  constant Modelica.Units.SI.Angle aRMax=175*Modelica.Constants.pi/180
    "Upper bound where transition occurs";
  Real a180 = Modelica.Math.log(1.248 - 0.703 +
              0.131*Modelica.Math.sin(2*Modelica.Constants.pi*G)^3
              + 0.071*G^2) "Attenuation factor at 180 degree incidence angle";

  Modelica.Units.SI.Angle aR "alpha, restricted to 0...pi";
  Modelica.Units.SI.Angle incAng2 "0.5*wind incidence angle";
  Real sinA2 "=sin(alpha/2)";
  Real cosA2 "=cos(alpha/2)";
  Real a "Attenuation factor";
algorithm
  // Restrict incAng to [0...pi]

  // Change sign to positive
  aR :=if alpha < 0 then -alpha else alpha;
  // Constrain to [0...2*pi]
  if aR > pi2 then
    aR := aR - integer(aR/pi2)*pi2;
  end if;
  // Constrain to [0...pi]
  if aR > Modelica.Constants.pi then
    aR := pi2-aR;
  end if;

  // Evaluate eqn. 2-1 from FSEC-CR-163-86
  incAng2 :=aR/2;
  sinA2 :=Modelica.Math.sin(incAng2);
  cosA2 :=Modelica.Math.cos(incAng2);
  // Implementation of the wind pressure coefficient that is once
  // continuously differentiable for all incidence angles
  if aR < aRDel then
    Cp :=Cp0*AixLib.Utilities.Math.Functions.regStep(
      y1=Modelica.Math.log(1.248 - 0.703*sinA2 - 1.175*Modelica.Math.sin(aR)^2
         + 0.131*Modelica.Math.sin(2*aR*G)^3 + 0.769*cosA2 + 0.071*G^2*sinA2^2
         + 0.717*cosA2^2),
      y2=1,
      x=aR - aRDel2,
      x_small=aRDel2);
  elseif aR > aRMax then
    Cp :=Cp0*AixLib.Utilities.Math.Functions.regStep(
      y1=a180,
      y2=Modelica.Math.log(1.248 - 0.703*sinA2 - 1.175*Modelica.Math.sin(aR)^2
         + 0.131*Modelica.Math.sin(2*aR*G)^3 + 0.769*cosA2 + 0.071*G^2*sinA2^2
         + 0.717*cosA2^2),
      x=aR + aRDel2 - Modelica.Constants.pi,
      x_small=aRDel2);
  else
    Cp :=Cp0*Modelica.Math.log(1.248 - 0.703*sinA2 - 1.175*Modelica.Math.sin(aR)^2 +
      0.131*Modelica.Math.sin(2*aR*G)^3 + 0.769*cosA2 + 0.071*G^2*sinA2^2 + 0.717*cosA2^2);
  end if;
end windPressureLowRise;


Flow across zonal boundaries of a room

within AixLib.Airflow.Multizone.BaseClasses;
partial model ZonalFlow "Flow across zonal boundaries of a room"
  extends AixLib.Fluid.Interfaces.PartialFourPortInterface(
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium,
    final allowFlowReversal1 = false,
    final allowFlowReversal2 = false,
    final m1_flow_nominal = 10/3600*1.2,
    final m2_flow_nominal = m1_flow_nominal,
    final m1_flow_small=1E-4*abs(m1_flow_nominal),
    final m2_flow_small=1E-4*abs(m2_flow_nominal));

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

equation
  // Energy balance (no storage, no heat loss/gain)
  port_a1.h_outflow = inStream(port_b1.h_outflow);
  port_b1.h_outflow = inStream(port_a1.h_outflow);
  port_a2.h_outflow = inStream(port_b2.h_outflow);
  port_b2.h_outflow = inStream(port_a2.h_outflow);

  // Mass balance (no storage)
  port_a1.m_flow = -port_b1.m_flow;
  port_a2.m_flow = -port_b2.m_flow;

  port_a1.Xi_outflow = inStream(port_b1.Xi_outflow);
  port_b1.Xi_outflow = inStream(port_a1.Xi_outflow);
  port_a2.Xi_outflow = inStream(port_b2.Xi_outflow);
  port_b2.Xi_outflow = inStream(port_a2.Xi_outflow);

  // Transport of trace substances
  port_a1.C_outflow = inStream(port_b1.C_outflow);
  port_b1.C_outflow = inStream(port_a1.C_outflow);
  port_a2.C_outflow = inStream(port_b2.C_outflow);
  port_b2.C_outflow = inStream(port_a2.C_outflow);
end ZonalFlow;


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


Test model for power law function. This examples demonstrates the

Buildings.Airflow.Multizone.BaseClasses.powerLaw
function.

within AixLib.Airflow.Multizone.BaseClasses.Examples;
model PowerLaw "Test model for power law function"
  extends Modelica.Icons.Example;
  parameter Real C = 2/10^m "Flow coefficient, k = V_flow/ dp^m";

  parameter Real m(min=0.5, max=1) = 0.5
    "Flow exponent, m=0.5 for turbulent, m=1 for laminar";
  parameter Modelica.Units.SI.PressureDifference dp_turbulent(min=0) = 5
    "Pressure difference where regularization starts";

  Modelica.Units.SI.PressureDifference dp "Pressure difference";
  Modelica.Units.SI.VolumeFlowRate V_flow "Volume flow rate";
equation
  dp = 10*(-1+2*time);
  V_flow = AixLib.Airflow.Multizone.BaseClasses.powerLaw(
    dp=dp,
    C=C,
    m=m,
    dp_turbulent=dp_turbulent);
end PowerLaw;


Test model for power law function. This examples demonstrates the

Buildings.Airflow.Multizone.BaseClasses.powerLaw
and

Buildings.Airflow.Multizone.BaseClasses.powerLawFixedM
functions.
They need to return the same function value.
This is verified by an assert statement.

within AixLib.Airflow.Multizone.BaseClasses.Examples;
model PowerLawFixedM "Test model for power law function"
  extends Modelica.Icons.Example;
  parameter Real C = 2/10^m "Flow coefficient, C = V_flow/ dp^m";

  constant Real m(min=0.5, max=1) = 0.5
    "Flow exponent, m=0.5 for turbulent, m=1 for laminar";
  parameter Modelica.Units.SI.PressureDifference dp_turbulent(min=0) = 5
    "Pressure difference where regularization starts";

  Modelica.Units.SI.PressureDifference dp "Pressure difference";
  Modelica.Units.SI.VolumeFlowRate V_flow
    "Volume flow rate computed with model powerLaw";
  Modelica.Units.SI.VolumeFlowRate VFixed_flow
    "Volume flow rate computed with model powerLawFixed";

  constant Real gamma(min=1) = 1.5
    "Normalized flow rate where dphi(0)/dpi intersects phi(1)";
  constant Real a = gamma
    "Polynomial coefficient for regularized implementation of flow resistance";
  constant Real b = 1/8*m^2 - 3*gamma - 3/2*m + 35.0/8
    "Polynomial coefficient for regularized implementation of flow resistance";
  constant Real c = -1/4*m^2 + 3*gamma + 5/2*m - 21.0/4
    "Polynomial coefficient for regularized implementation of flow resistance";
  constant Real d = 1/8*m^2 - gamma - m + 15.0/8
    "Polynomial coefficient for regularized implementation of flow resistance";

equation
  dp = 10*(-1+2*time);
  V_flow = AixLib.Airflow.Multizone.BaseClasses.powerLaw(
    dp=dp,
    C=C,
    m=m,
    dp_turbulent=dp_turbulent);
  VFixed_flow = AixLib.Airflow.Multizone.BaseClasses.powerLawFixedM(
    C=C,
    dp=dp,
    m=m,
    a=a,
    b=b,
    c=c,
    d=d,
    dp_turbulent=dp_turbulent);
  assert(abs(V_flow-VFixed_flow) < 1E-10, "Error: The two implementations of the power law model need to give identical results");
end PowerLawFixedM;


Test model for wind pressure function. This examples demonstrates the

AixLib.Airflow.Multizone.BaseClasses.windPressureLowRise
function.

within AixLib.Airflow.Multizone.BaseClasses.Examples;
model WindPressureLowRise "Test model for wind pressure function"
  extends Modelica.Icons.Example;
  parameter Real Cp0 = 0.6
    "Wind pressure coefficient for normal wind incidence angle";
  Modelica.Units.SI.Angle incAng "Wind incidence angle (0: normal to wall)";
  parameter Real G = Modelica.Math.log(0.5) "Natural logarithm of side ratio";
  Real Cp "Wind pressure coefficient";
equation
  incAng=time*2*Modelica.Constants.pi;
  Cp =AixLib.Airflow.Multizone.BaseClasses.windPressureLowRise(
    Cp0=Cp0,
    G=G,
    alpha=incAng);
end WindPressureLowRise;


Test model for wind pressure profile function. This examples demonstrates the

AixLib.Airflow.Multizone.BaseClasses.windPressureProfile
function.

within AixLib.Airflow.Multizone.BaseClasses.Examples;
model WindPressureProfile
  "Test model for wind pressure profile function"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.Angle incAngSurNor[:](
    each displayUnit="deg")=
      {0, 45, 90, 135, 180, 225, 270, 315}*Modelica.Constants.pi/180
    "Wind incidence angles, relative to the surface normal (normal=0), first point must be 0, last smaller than 2 pi(=360 deg)";
  parameter Real Cp[:](
    each final unit="1")=
      {0.4, 0.1, -0.3, -0.35, -0.2, -0.35, -0.3, 0.1}
    "Cp values at the corresponding incAngSurNor";

  Modelica.Units.SI.Angle  alpha "Wind incidence angle (0: normal to wall)";
  Real CpAct "Wind pressure coefficient";

protected
  final parameter Integer n=size(incAngSurNor, 1)
    "Number of data points provided by user";
  final parameter Modelica.Units.SI.Angle incAngExt[n + 3](each displayUnit=
        "deg") = cat(
    1,
    {incAngSurNor[n - 1] - (2*Modelica.Constants.pi)},
    incAngSurNor,
    2*Modelica.Constants.pi .+ {incAngSurNor[1],incAngSurNor[2]})
    "Extended number of incidence angles";
  final parameter Real CpExt[n+3]=cat(1, {Cp[n-1]}, Cp, {Cp[1], Cp[2]})
    "Extended number of Cp values";

  final parameter Real[n+3] deri=
      AixLib.Utilities.Math.Functions.splineDerivatives(
      x=incAngExt,
      y=CpExt,
      ensureMonotonicity=false) "Derivatives for table interpolation";

  Modelica.Blocks.Sources.Ramp ramp(
    duration=500,
    height=3*360,
    offset=-360)
    "Ramp model generating a singal from -360 to 720";

initial equation
  assert(size(incAngSurNor, 1) == size(Cp, 1), "In " + getInstanceName() +
    ": Size of parameters are size(CpincAng, 1) = " + String(size(incAngSurNor,
    1)) + " and size(Cp, 1) = " + String(size(Cp, 1)) + ". They must be equal.");

  assert(abs(incAngSurNor[1]) < 1E-4, "In " + getInstanceName() +
    ": First point in the table CpAngAtt must be 0.");

  assert(2*Modelica.Constants.pi - incAngSurNor[end] > 1E-4, "In " +
    getInstanceName() +
    ": Last point in the table CpAngAtt must be smaller than 2 pi (360 deg).");

equation
   alpha=Modelica.Constants.D2R*ramp.y;
   CpAct =AixLib.Airflow.Multizone.BaseClasses.windPressureProfile(
    alpha=alpha,
    incAngTab=incAngExt,
    CpTab=CpExt,
    d=deri) "Actual wind pressure coefficient";
end WindPressureProfile;


within AixLib.Airflow.Multizone.Examples;
model ChimneyShaftNoVolume
  "Model with chimney effect and a steady-state model of a shaft"
  extends Modelica.Icons.Example;
  package Medium = Modelica.Media.Air.SimpleAir;

  AixLib.Fluid.MixingVolumes.MixingVolume roo(
    V=2.5*5*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=273.15 + 20,
    redeclare package Medium = Medium,
    m_flow_nominal=0.05,
    p_start=101325,
    nPorts=3) "Air volume of a room"
  AixLib.Airflow.Multizone.Orifice oriChiTop(
    m=0.5,
    redeclare package Medium = Medium,
    A=0.01) "Orifice at top"
  AixLib.Fluid.Sources.MassFlowSource_T boundary(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    T=293.15,
    nPorts=1) "Mass flow rate boundary condition"
  AixLib.Fluid.Sources.Boundary_pT bou0(
    redeclare package Medium = Medium,
    T=273.15,
    nPorts=2) "Pressure boundary condition"
  AixLib.Airflow.Multizone.Orifice oriBot(
    m=0.5,
    redeclare package Medium = Medium,
    A=0.01) "Orifice"
  Modelica.Blocks.Sources.CombiTimeTable mRoo_flow(tableOnFile=false, table=[0,
        0.05; 600,0.05; 601,0; 1800,0; 1801,-0.05; 2400,-0.05; 2401,0; 3600,0])
    "Mass flow into and out of room to fill the medium column with air of different temperature"
  MediumColumn staOut(
    redeclare package Medium = Medium,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop,
    h=1.5) "Model for stack effect outside the room"
  AixLib.Airflow.Multizone.Orifice oriChiBot(
    m=0.5,
    redeclare package Medium = Medium,
    A=0.01) "Orifice at bottom"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea
    "Prescribed heat flow rate"
  Modelica.Blocks.Continuous.LimPID con(
    Td=10,
    yMax=1,
    yMin=-1,
    Ti=60,
    controllerType=Modelica.Blocks.Types.SimpleController.P,
    k=5) "Controller to maintain volume temperature"
  Modelica.Blocks.Sources.Constant TSet(k=293.15) "Temperature set point"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSen
    "Temperature sensor"
  Modelica.Blocks.Math.Gain gain(k=3000) "Gain for heat flow rate"
  AixLib.Airflow.Multizone.MediumColumn sha(redeclare package Medium = Medium,
      densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.actual) "Shaft of chimney"
  MediumColumn staIn(
    redeclare package Medium = Medium,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom,
    h=1.5) "Model for stack effect inside the room"

equation
  connect(TSet.y, con.u_s)
  connect(temSen.T, con.u_m)
  connect(gain.u, con.y)
  connect(gain.y, preHea.Q_flow)
  connect(sha.port_a, oriChiTop.port_a)
  connect(sha.port_b, oriChiBot.port_b)
  connect(staOut.port_b, oriBot.port_a)
  connect(preHea.port, roo.heatPort)
  connect(roo.heatPort, temSen.port)
  connect(bou0.ports[1], oriChiTop.port_b)
  connect(bou0.ports[2], staOut.port_a)
  connect(oriBot.port_b, staIn.port_a)
  connect(mRoo_flow.y[1], boundary.m_flow_in)
  connect(boundary.ports[1], roo.ports[1])
  connect(roo.ports[2], staIn.port_b)
  connect(roo.ports[3], oriChiBot.port_a)
end ChimneyShaftNoVolume;


within AixLib.Airflow.Multizone.Examples;
model ChimneyShaftWithVolume
  "Model with chimney effect and a dynamic model of a shaft"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;

  AixLib.Fluid.MixingVolumes.MixingVolume roo(
    V=2.5*5*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=273.15 + 20,
    redeclare package Medium = Medium,
    m_flow_nominal=0.05,
    p_start=101325,
    nPorts=3) "Air volume of a room"
  AixLib.Airflow.Multizone.Orifice oriChiTop(
    m=0.5,
    redeclare package Medium = Medium,
    A=0.01) "Orifice at top"
  AixLib.Fluid.Sources.MassFlowSource_T boundary(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    T=293.15,
    nPorts=1) "Mass flow rate boundary condition"
  AixLib.Fluid.Sources.Boundary_pT bou0(
    redeclare package Medium = Medium,
    T=273.15,
    nPorts=2) "Pressure boundary condition"
  AixLib.Airflow.Multizone.Orifice oriBot(
    m=0.5,
    redeclare package Medium = Medium,
    A=0.01) "Orifice"
  Modelica.Blocks.Sources.CombiTimeTable mRoo_flow(tableOnFile=false, table=[0,
        0.05; 600,0.05; 601,0; 1800,0; 1801,-0.05; 2400,-0.05; 2401,0; 3600,0])
    "Mass flow into and out of room to fill the medium column with air of different temperature"
  MediumColumn staOut(
    redeclare package Medium = Medium,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop,
    h=1.5) "Model for stack effect outside the room"
  AixLib.Airflow.Multizone.Orifice oriChiBot(
    m=0.5,
    redeclare package Medium = Medium,
    A=0.01) "Orifice at bottom"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea
    "Prescribed heat flow"
  Modelica.Blocks.Continuous.LimPID con(
    Td=10,
    yMax=1,
    yMin=-1,
    Ti=60,
    controllerType=Modelica.Blocks.Types.SimpleController.P,
    k=5) "Controller to maintain volume temperature"
  Modelica.Blocks.Sources.Constant TSet(k=293.15) "Temperature set point"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSen
    "Temperature sensor"
  Modelica.Blocks.Math.Gain gain(k=3000) "Gain for heat flow rate"
  AixLib.Airflow.Multizone.MediumColumnDynamic sha(
    redeclare package Medium = Medium,
    V=3,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Shaft of chimney"
  MediumColumn staIn(
    redeclare package Medium = Medium,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom,
    h=1.5) "Model for stack effect inside the room"

equation
  connect(TSet.y, con.u_s)
  connect(temSen.T, con.u_m)
  connect(gain.u, con.y)
  connect(gain.y, preHea.Q_flow)
  connect(sha.port_a, oriChiTop.port_a)
  connect(sha.port_b, oriChiBot.port_b)
  connect(staOut.port_b, oriBot.port_a)
  connect(preHea.port, roo.heatPort)
  connect(roo.heatPort, temSen.port)
  connect(bou0.ports[1], oriChiTop.port_b)
  connect(bou0.ports[2], staOut.port_a)
  connect(oriBot.port_b, staIn.port_a)
  connect(mRoo_flow.y[1], boundary.m_flow_in)
  connect(boundary.ports[1], roo.ports[1])
  connect(roo.ports[2], staIn.port_b)
  connect(roo.ports[3], oriChiBot.port_a)
end ChimneyShaftWithVolume;


Model with three closed doors

within AixLib.Airflow.Multizone.Examples;
model ClosedDoors "Model with three closed doors"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Specialized.Air.PerfectGas;

  AixLib.Airflow.Multizone.DoorDiscretizedOperable dooAB(
    redeclare package Medium = Medium,
    LClo=20*1E-4,
    forceErrorControlOnFlow=true) "Discretized door"

  AixLib.Fluid.MixingVolumes.MixingVolume volA(
    redeclare package Medium = Medium,
    V=2.5*5*5,
    nPorts=4,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01) "Control volume"
  AixLib.Fluid.MixingVolumes.MixingVolume volB(
    redeclare package Medium = Medium,
    V=2.5*5*5,
    nPorts=4,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01) "Control volume"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHeaFlo
    "Prescribed heat flow rate boundary condition"
  Modelica.Blocks.Sources.Sine sinBou(f=1/3600)
    "Signal for heat flow rate boundary condition"
  Modelica.Blocks.Math.Gain gai(k=100) "Gain for heat flow boundary condition"
  AixLib.Fluid.MixingVolumes.MixingVolume volC(
    redeclare package Medium = Medium,
    V=2.5*5*5,
    nPorts=4,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01) "Control volume"
  AixLib.Airflow.Multizone.DoorDiscretizedOperable dooAC(
    redeclare package Medium = Medium,
    LClo=20*1E-4,
    forceErrorControlOnFlow=true) "Discretized door"
  Modelica.Blocks.Sources.Constant yDoor(k=0) "Input signal for door opening"
  AixLib.Airflow.Multizone.DoorDiscretizedOperable dooBC(
    redeclare package Medium = Medium,
    LClo=20*1E-4,
    forceErrorControlOnFlow=true) "Discretized door"
equation
  connect(gai.y, preHeaFlo.Q_flow)
  connect(sinBou.y, gai.u)
  connect(yDoor.y, dooAB.y)
  connect(yDoor.y, dooAC.y)
  connect(yDoor.y, dooBC.y)
  connect(preHeaFlo.port, volB.heatPort)
  connect(volC.ports[1], dooAC.port_b1)
  connect(volC.ports[2], dooAC.port_a2)
  connect(volC.ports[3], dooBC.port_b1)
  connect(volC.ports[4], dooBC.port_a2)
  connect(volB.ports[1], dooAB.port_b1)
  connect(volB.ports[2], dooAB.port_a2)
  connect(volB.ports[3], dooBC.port_a1)
  connect(volB.ports[4], dooBC.port_b2)
  connect(volA.ports[1], dooAC.port_b2)
  connect(volA.ports[2], dooAC.port_a1)
  connect(volA.ports[3], dooAB.port_b2)
  connect(volA.ports[4], dooAB.port_a1)
end ClosedDoors;


within AixLib.Airflow.Multizone.Examples;
model CO2TransportStep "Model with transport of CO2 through buoyancy driven flow"
  extends AixLib.Airflow.Multizone.Validation.ThreeRoomsContam(
    volWes(nPorts=5),
    volTop(nPorts=3),
    volEas(nPorts=6));

  AixLib.Fluid.Sensors.TraceSubstances CO2SenTop(
    redeclare package Medium = Medium,
    warnAboutOnePortConnection = false)
    "CO2 sensor"
  AixLib.Fluid.Sensors.TraceSubstances CO2SenWes(
    redeclare package Medium = Medium,
    warnAboutOnePortConnection = false)
    "CO2 sensor"
  AixLib.Fluid.Sensors.TraceSubstances CO2SenEas(
    redeclare package Medium = Medium,
    warnAboutOnePortConnection = false)
    "CO2 sensor"
  Modelica.Blocks.Sources.Pulse pulse(
    amplitude=8.18E-6,
    width=1/24/10,
    period=86400,
    startTime=3600) "Pulse signal for CO2 flow source"
  AixLib.Fluid.Sources.TraceSubstancesFlowSource sou(
    redeclare package Medium = Medium,
    use_m_flow_in=true,
    nPorts=1) "CO2 source"
equation
  connect(sou.m_flow_in, pulse.y)
  connect(sou.ports[1], volWes.ports[4])
  connect(CO2SenWes.port, volWes.ports[5])
  connect(CO2SenTop.port, volTop.ports[3])
  connect(CO2SenEas.port, volEas.ports[6])
end CO2TransportStep;


within AixLib.Airflow.Multizone.Examples;
model NaturalVentilation
  "Model with flow reversal due to density difference"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Air;

  AixLib.Fluid.MixingVolumes.MixingVolume volA(
    redeclare package Medium = Medium,
    V=2.5*10*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=273.15 + 18,
    nPorts=2,
    m_flow_nominal=0.001) "Control volume"

  AixLib.Airflow.Multizone.Orifice oriOutBot(
    redeclare package Medium = Medium,
    A=0.1,
    m=0.5,
    dp_turbulent=0.1) "Orifice"
  AixLib.Airflow.Multizone.MediumColumn colOut(
    redeclare package Medium = Medium,
    h=3,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.Orifice oriOutTop(
    redeclare package Medium = Medium,
    A=0.1,
    m=0.5,
    dp_turbulent=0.1) "Orifice"
  AixLib.Airflow.Multizone.MediumColumn colRooTop(
    redeclare package Medium = Medium,
    h=3,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom)
    "Medium column to compute static pressure of air"
  AixLib.Fluid.MixingVolumes.MixingVolume volOut(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    V=1E10,
    T_start=273.15 + 20,
    nPorts=2,
    m_flow_nominal=0.001) "Control volume"

  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHeaFlo
    "Prescribed heat flow rate boundary condition"
  Modelica.Blocks.Sources.Step q_flow(
    height=-100,
    offset=100,
    startTime=3600) "Step function for heat flow boundary condition"
equation
  connect(q_flow.y, preHeaFlo.Q_flow)
  connect(oriOutBot.port_b, volOut.ports[1])
  connect(preHeaFlo.port, volA.heatPort)
  connect(volA.ports[1], oriOutBot.port_a)
  connect(volA.ports[2], colRooTop.port_b)
  connect(colRooTop.port_a, oriOutTop.port_a)
  connect(volOut.ports[2], colOut.port_b)
  connect(colOut.port_a, oriOutTop.port_b)
end NaturalVentilation;


within AixLib.Airflow.Multizone.Examples;
model OneEffectiveAirLeakageArea "Model with an effective air leakage area"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;

  AixLib.Fluid.MixingVolumes.MixingVolume volA(
    redeclare package Medium = Medium,
    V=2.5*5*5,
    nPorts=2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01) "Control volume"
  AixLib.Fluid.MixingVolumes.MixingVolume volB(
    redeclare package Medium = Medium,
    V=2.5*5*5,
    nPorts=1,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01) "Control volume"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHeaFlo
    "Prescribed heat flow rate boundary condition"
  Modelica.Blocks.Sources.Sine heaFloBou(f=1/3600)
    "Signal for heat flow rate boundary condition"
  Modelica.Blocks.Math.Gain gai(k=100)
    "Gain for heat flow rate boundary condition"
  AixLib.Airflow.Multizone.EffectiveAirLeakageArea cra(redeclare package
      Medium = Medium, L=20E-4)
    "Crack model, parameterized with effective leakage area"
  AixLib.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_m_flow_in=true) "Prescribed mass flow rate boundary condition"
  Modelica.Blocks.Sources.Ramp ramSou(
    duration=3600,
    height=0.01,
    offset=0,
    startTime=1800)
    "Ramp signal for prescribed mass flow rate boundary condition"
equation
  connect(sou.ports[1], volA.ports[1])
  connect(ramSou.y, sou.m_flow_in)
  connect(volB.ports[1], cra.port_b)
  connect(volA.ports[2], cra.port_a)
  connect(preHeaFlo.port, volB.heatPort)
  connect(gai.y, preHeaFlo.Q_flow)
  connect(gai.u, heaFloBou.y)
end OneEffectiveAirLeakageArea;


Model with one open and one closed door

within AixLib.Airflow.Multizone.Examples;
model OneOpenDoor "Model with one open and one closed door"
  extends Modelica.Icons.Example;

  package Medium = Modelica.Media.Air.SimpleAir;

  AixLib.Airflow.Multizone.DoorDiscretizedOpen dooOpe(
    redeclare package Medium = Medium) "Discretized door"

  AixLib.Fluid.MixingVolumes.MixingVolume volA(
    redeclare package Medium = Medium,
    V=2.5*5*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nPorts=4,
    m_flow_nominal=0.01) "Control volume"
  AixLib.Fluid.MixingVolumes.MixingVolume volB(
    redeclare package Medium = Medium,
    V=2.5*5*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    nPorts=4,
    m_flow_nominal=0.01) "Control volume"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHeaFlo
    "Prescribed heat flow rate boundary condition"
  Modelica.Blocks.Sources.Sine heaSou(f=1/3600)
    "Signal for heat flow rate boundary condition"
  Modelica.Blocks.Math.Gain gai(k=100)
    "Gain for heat flow rate boundary condition"
  AixLib.Airflow.Multizone.DoorDiscretizedOperable dooOpeClo(redeclare package
      Medium = Medium, LClo=20*1E-4) "Discretized door"
  Modelica.Blocks.Sources.Ramp ramp(
    duration=120,
    height=1,
    offset=0,
    startTime=1000) "Ramp signal for door opening"
equation
  connect(gai.y, preHeaFlo.Q_flow)
  connect(heaSou.y, gai.u)
  connect(ramp.y, dooOpeClo.y)
  connect(preHeaFlo.port, volB.heatPort)
  connect(volA.ports[1], dooOpeClo.port_b2)
  connect(volA.ports[2], dooOpeClo.port_a1)
  connect(volA.ports[3], dooOpe.port_b2)
  connect(volA.ports[4], dooOpe.port_a1)
  connect(volB.ports[1], dooOpe.port_b1)
  connect(volB.ports[2], dooOpe.port_a2)
  connect(volB.ports[3], dooOpeClo.port_b1)
  connect(volB.ports[4], dooOpeClo.port_a2)
end OneOpenDoor;


within AixLib.Airflow.Multizone.Examples;
model OneRoom
  "Model with one room for the validation of the multizone air exchange models"

  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;

  AixLib.Fluid.MixingVolumes.MixingVolume volEas(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=273.15 + 20,
    V=2.5*5*5,
    nPorts=2,
    m_flow_nominal=0.001,
    massDynamics=Modelica.Fluid.Types.Dynamics.SteadyStateInitial)
    "Control volume for room"

  AixLib.Airflow.Multizone.Orifice oriOutBot(
    redeclare package Medium = Medium,
    A=0.01,
    m=0.5) "Orifice at bottom"
  AixLib.Airflow.Multizone.MediumColumn colOutTop(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.Orifice oriOutTop(
    redeclare package Medium = Medium,
    A=0.01,
    m=0.5) "Orifice at top"
  AixLib.Airflow.Multizone.MediumColumn colEasInTop(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom)
    "Medium column to compute static pressure of air"
  AixLib.Fluid.MixingVolumes.MixingVolume volOut(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=273.15 + 10,
    V=1E12,
    p_start=Medium.p_default,
    nPorts=2,
    m_flow_nominal=0.001)
    "Control volume for outside"

  AixLib.Airflow.Multizone.MediumColumn colEasInBot(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.MediumColumn colOutBot(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop)
    "Medium column to compute static pressure of air"
equation
  connect(colEasInTop.port_a, oriOutTop.port_a)
  connect(colEasInTop.port_b, volEas.ports[1])
  connect(colEasInBot.port_a, volEas.ports[2])
  connect(colEasInBot.port_b, oriOutBot.port_a)
  connect(oriOutBot.port_b, colOutBot.port_b)
  connect(colOutBot.port_a, volOut.ports[1])
  connect(colOutTop.port_b, volOut.ports[2])
  connect(colOutTop.port_a, oriOutTop.port_b)
end OneRoom;


Model with an orifice

within AixLib.Airflow.Multizone.Examples;
model Orifice "Model with an orifice"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  AixLib.Airflow.Multizone.Orifice ori(redeclare package Medium = Medium, A=
        0.2) "Orifice"
  AixLib.Fluid.Sources.Boundary_pT roo1(
    redeclare package Medium = Medium,
    use_p_in=true,
    nPorts=1,
    T=278.15) "Pressure boundary condition"
  AixLib.Fluid.Sources.Boundary_pT roo2(
    redeclare package Medium = Medium,
    use_p_in=true,
    nPorts=1,
    T=293.15) "Pressure boundary condition"
  Modelica.Blocks.Sources.Ramp ram(
    duration=0.5,
    height=2,
    offset=-1,
    startTime=0.25) "Ramp signal for pressure boundary condition"
  Modelica.Blocks.Sources.Constant preBou(k=100000)
    "Constant pressure boundary condition"
  Modelica.Blocks.Math.Add add "Adder for pressure boundary condition"
  AixLib.Fluid.Sensors.DensityTwoPort den1(
    redeclare package Medium = Medium,
    m_flow_nominal=0.1,
    initType=Modelica.Blocks.Types.Init.InitialState) "Density sensor"
  AixLib.Fluid.Sensors.DensityTwoPort den2(
    redeclare package Medium = Medium,
    m_flow_nominal=0.1,
    initType=Modelica.Blocks.Types.Init.InitialState) "Density sensor"
equation
  connect(preBou.y, add.u1)
  connect(ram.y, add.u2)
  connect(preBou.y, roo1.p_in)
  connect(add.y, roo2.p_in)
  connect(roo1.ports[1], den1.port_a)
  connect(den1.port_b, ori.port_a)
  connect(ori.port_b, den2.port_a)
  connect(den2.port_b, roo2.ports[1])
end Orifice;


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


Model with powerlaw models

within AixLib.Airflow.Multizone.Examples;
model PowerLaw "Model with powerlaw models"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  Coefficient_m_flow pow_m_flow(
    redeclare package Medium = Medium,
    m=0.59,
    k=3.33e-5) "Mass flow rate based on powerlaw, direct input for m and C"
  AixLib.Fluid.Sources.Boundary_pT roo1(
    redeclare package Medium = Medium,
    use_p_in=true,
    nPorts=1,
    T=278.15) "Room 1"
  AixLib.Fluid.Sources.Boundary_pT roo2(
    redeclare package Medium = Medium,
    use_p_in=true,
    nPorts=1,
    T=293.15) "Room 2"
  Modelica.Blocks.Sources.Ramp ramp1(
    duration=0.5,
    height=6,
    offset=-3,
    startTime=0.25) "Ramp"
  Modelica.Blocks.Sources.Constant pressure(k=100000) "Pressure"
  Modelica.Blocks.Math.Add add "Add"
  AixLib.Fluid.Sensors.DensityTwoPort den1(
    redeclare package Medium = Medium,
    m_flow_nominal=0.1,
    tau=0,
    initType=Modelica.Blocks.Types.Init.InitialState) "Density sensor"
  AixLib.Fluid.Sensors.DensityTwoPort den2(
    redeclare package Medium = Medium,
    m_flow_nominal=0.1,
    tau=0,
    initType=Modelica.Blocks.Types.Init.InitialState) "Density sensor"
  Coefficient_V_flow pow_V_flow(
    redeclare package Medium = Medium,
    m=0.59,
    C=3.33e-5/1.2)
    "Volume flow rate based on powerlaw, direct input for m and C"
  Point_m_flow pow_1dat(
    redeclare package Medium = Medium,
    dpMea_nominal = 50,
    m=0.59,
    mMea_flow_nominal=1.2/3600)
    "Mass flow rate based on powerlaw, input of m and 1 test data point."
  Points_m_flow pow_2dat(
    redeclare package Medium = Medium,
    dpMea_nominal = {1, 50},
    mMea_flow_nominal={0.12, 1.2}/3600)
    "Mass flow rate based on powerlaw, input of 2 test data points."
equation
  connect(pressure.y, add.u1)
  connect(ramp1.y, add.u2)
  connect(pressure.y, roo1.p_in)
  connect(add.y, roo2.p_in)
  connect(roo1.ports[1], den1.port_a)
  connect(den1.port_b, pow_m_flow.port_a)
  connect(pow_m_flow.port_b, den2.port_a)
  connect(den2.port_b, roo2.ports[1])
  connect(den1.port_b, pow_V_flow.port_a)
  connect(pow_V_flow.port_b, den2.port_a)
  connect(den1.port_b, pow_1dat.port_a)
  connect(den1.port_b, pow_2dat.port_a)
  connect(pow_1dat.port_b, den2.port_a)
  connect(pow_2dat.port_b, den2.port_a)
end PowerLaw;


within AixLib.Airflow.Multizone.Examples;
model PressurizationData
  "Model showing how the 'Powerlaw_1DataPoint' model can be used when data is available from a pressurization test."
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;

  parameter Real n50=3 "ACH50, air changes at 50 Pa";

  BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    filNam=Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/weatherdata/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos"))
  Fluid.Sources.Outside_CpLowRise west(
    redeclare package Medium = Medium,
    s=5,
    azi=AixLib.Types.Azimuth.W,
    Cp0=0.6,
    nPorts=1) "Model with outside conditions"
  Fluid.Sources.Outside_CpLowRise east(
    redeclare package Medium = Medium,
    s=5,
    azi=AixLib.Types.Azimuth.E,
    Cp0=0.6,
    nPorts=1) "Model with outside conditions"
  Fluid.MixingVolumes.MixingVolume room(
    redeclare package Medium = Medium,
    V=2.5*5*5,
    nPorts=2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01) "Room model"
  AixLib.Airflow.Multizone.Point_m_flow pow_1dat(
    dpMea_nominal(displayUnit="Pa") = 50,
    redeclare package Medium = Medium,
    m=0.66,
    mMea_flow_nominal=0.5*(room.V*n50*1.2))
    "Crack in envelope representing 50% of the leakage area"
  AixLib.Airflow.Multizone.Point_m_flow pow_1dat1(
    dpMea_nominal(displayUnit="Pa") = 50,
    redeclare package Medium = Medium,
    m=0.66,
    mMea_flow_nominal=0.5*(room.V*n50*1.2))
    "Crack in envelope representing 50% of the leakage area"
equation
  connect(weaDat.weaBus, west.weaBus)
  connect(east.weaBus, weaDat.weaBus)
  connect(east.ports[1], pow_1dat.port_a)
  connect(pow_1dat.port_b,room. ports[1])
  connect(pow_1dat1.port_a,room. ports[2])
  connect(pow_1dat1.port_b, west.ports[1])
end PressurizationData;


within AixLib.Airflow.Multizone.Examples;
model ReverseBuoyancy
  "Model with four rooms and buoyancy-driven air circulation that reverses direction"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  AixLib.Fluid.MixingVolumes.MixingVolume volBotEas(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    V=2.5*5*5,
    T_start=273.15 + 25,
    nPorts=5,
    m_flow_nominal=0.001) "Volume of bottom floor, east room"
  AixLib.Airflow.Multizone.Orifice oriOutBot(
    redeclare package Medium = Medium,
    m=0.5,
    A=0.01,
    dp_turbulent=0.1) "Orifice at bottom"
  AixLib.Airflow.Multizone.MediumColumn colOutTop(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.Orifice oriOutTop(
    redeclare package Medium = Medium,
    m=0.5,
    A=0.01,
    dp_turbulent=0.1) "Orifice at top"
  AixLib.Airflow.Multizone.MediumColumn colEasInTop(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.MediumColumn colEasInBot(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.MediumColumn colOutBot(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop)
    "Medium column to compute static pressure of air"
  MediumColumn colWesBot(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.Orifice oriWesTop(
    redeclare package Medium = Medium,
    m=0.5,
    A=0.01,
    dp_turbulent=0.1) "Orifice between west rooms"
  AixLib.Airflow.Multizone.MediumColumn colWesTop(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.DoorDiscretizedOperable dooOpeClo(
    redeclare package Medium = Medium,
    LClo=20*1E-4,
    wOpe=1,
    hOpe=2.2,
    hA=3/2,
    hB=3/2,
    CDOpe=0.78,
    CDClo=0.78,
    nCom=10,
    vZer=0.01,
    dp_turbulent=0.1) "Discretized door"
  Fluid.Delays.DelayFirstOrder volBotWes(
    redeclare package Medium = Medium,
    m_flow_nominal=1.2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    tau=2.5*5*5,
    T_start=273.15 + 22,
    nPorts=3,
    p_start=101325) "Volume of bottom floor, west room"
  Modelica.Blocks.Sources.Constant ope(k=1) "Constant signal for door opening"
  AixLib.Airflow.Multizone.MediumColumn col1EasBot(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.Orifice oriEasTop(
    redeclare package Medium = Medium,
    m=0.5,
    A=0.01,
    dp_turbulent=0.1) "Orifice between east rooms"
  AixLib.Airflow.Multizone.MediumColumn colEasTop(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop)
    "Medium column to compute static pressure of air"
  AixLib.Fluid.MixingVolumes.MixingVolume volTopEas(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    V=2.5*5*10,
    T_start=273.15 + 21,
    nPorts=3,
    m_flow_nominal=0.001) "Volume of top floor, east room"
  AixLib.Fluid.MixingVolumes.MixingVolume volTopWes(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=273.15 + 20,
    V=2.5*5*10,
    nPorts=3,
    m_flow_nominal=0.001) "Volume of top floor, west room"
  AixLib.Airflow.Multizone.DoorDiscretizedOperable dooOpeCloTop(
    redeclare package Medium = Medium,
    LClo=20*1E-4,
    wOpe=1,
    hOpe=2.2,
    hA=3/2,
    hB=3/2,
    CDOpe=0.78,
    CDClo=0.78,
    nCom=10,
    vZer=0.01,
    dp_turbulent=0.1) "Discretized door"
  AixLib.Fluid.Sources.Boundary_pT volOut(
    redeclare package Medium = Medium,
    p=100000,
    T=283.15,
    nPorts=2) "Ambient conditions"
equation
  connect(ope.y, dooOpeClo.y)
  connect(ope.y, dooOpeCloTop.y)
  connect(oriEasTop.port_b, colEasTop.port_b)
  connect(oriWesTop.port_b, colWesBot.port_a)
  connect(oriWesTop.port_a, colWesTop.port_b)
  connect(oriOutBot.port_b, colOutBot.port_b)
  connect(colEasInBot.port_b, oriOutBot.port_a)
  connect(colEasInTop.port_a, oriOutTop.port_a)
  connect(oriOutTop.port_b, colOutTop.port_a)
  connect(volBotWes.ports[1], dooOpeClo.port_b2)
  connect(volBotWes.ports[2], dooOpeClo.port_a1)
  connect(colWesBot.port_b, volBotWes.ports[3])
  connect(volTopWes.ports[1], colWesTop.port_a)
  connect(volTopWes.ports[2], dooOpeCloTop.port_b2)
  connect(volTopWes.ports[3], dooOpeCloTop.port_a1)
  connect(volTopEas.ports[1], dooOpeCloTop.port_b1)
  connect(dooOpeCloTop.port_a2, volTopEas.ports[2])
  connect(colEasTop.port_a, volTopEas.ports[3])
  connect(oriEasTop.port_a, col1EasBot.port_a)
  connect(dooOpeClo.port_b1, volBotEas.ports[1])
  connect(dooOpeClo.port_a2, volBotEas.ports[2])
  connect(colEasInBot.port_a, volBotEas.ports[3])
  connect(colEasInTop.port_b, volBotEas.ports[4])
  connect(col1EasBot.port_b, volBotEas.ports[5])
  connect(colOutBot.port_a, volOut.ports[1])
  connect(colOutTop.port_b, volOut.ports[2])
end ReverseBuoyancy;


within AixLib.Airflow.Multizone.Examples;
model ReverseBuoyancy3Zones
  "Model with three rooms and buoyancy-driven air circulation that reverses direction"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  AixLib.Fluid.MixingVolumes.MixingVolume volEas(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    V=2.5*5*5,
    T_start=273.15 + 25,
    nPorts=5,
    m_flow_nominal=0.001) "Control volume for east room"
  AixLib.Airflow.Multizone.Orifice oriOutBot(
    redeclare package Medium = Medium,
    m=0.5,
    A=0.01,
    dp_turbulent=0.1) "Orifice at bottom of east room"
  AixLib.Airflow.Multizone.MediumColumn colOutTop(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.Orifice oriOutTop(
    redeclare package Medium = Medium,
    m=0.5,
    A=0.01,
    dp_turbulent=0.1) "Orifice at top of east room"
  AixLib.Airflow.Multizone.MediumColumn colEasInTop(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom)
    "Medium column to compute static pressure of air"
  AixLib.Fluid.MixingVolumes.MixingVolume volOut(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    V=1E12,
    T_start=273.15 + 15,
    nPorts=2,
    m_flow_nominal=0.001) "Control volume for outside"
  AixLib.Airflow.Multizone.MediumColumn colEasInBot(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.MediumColumn colOutBot(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.MediumColumn colWesBot(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.Orifice oriWesTop(
    redeclare package Medium = Medium,
    m=0.5,
    A=0.01,
    dp_turbulent=0.1) "Orifice between west room and top"
  AixLib.Airflow.Multizone.MediumColumn colWesTop(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.DoorDiscretizedOperable dooOpeClo(
    redeclare package Medium = Medium,
    LClo=20*1E-4,
    wOpe=1,
    hOpe=2.2,
    hA=3/2,
    hB=3/2,
    CDOpe=0.78,
    CDClo=0.78,
    nCom=10,
    vZer=0.01,
    dp_turbulent=0.1) "Discretized door"
  Modelica.Blocks.Sources.Constant ope(k=1) "Constant signal for door opening"
  AixLib.Airflow.Multizone.MediumColumn col1EasBot(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromBottom)
    "Medium column to compute static pressure of air"
  AixLib.Airflow.Multizone.Orifice oriEasTop(
    redeclare package Medium = Medium,
    m=0.5,
    A=0.01,
    dp_turbulent=0.1) "Orifice between east room and top"
  AixLib.Airflow.Multizone.MediumColumn colEasTop(
    redeclare package Medium = Medium,
    h=1.5,
    densitySelection=AixLib.Airflow.Multizone.Types.densitySelection.fromTop)
    "Medium column to compute static pressure of air"
  AixLib.Fluid.MixingVolumes.MixingVolume volTop(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    T_start=273.15 + 20,
    m_flow_nominal=0.001,
    V=2.5*10*10,
    nPorts=2) "Control volume for top floor room"
  AixLib.Fluid.MixingVolumes.MixingVolume volWes(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    V=2.5*5*5,
    T_start=273.15 + 22,
    nPorts=3,
    m_flow_nominal=0.001) "Control volume for west room"
equation
  connect(dooOpeClo.port_b2, volWes.ports[1])
  connect(dooOpeClo.port_a1, volWes.ports[2])
  connect(dooOpeClo.port_b1, volEas.ports[1])
  connect(dooOpeClo.port_a2, volEas.ports[2])
  connect(colWesTop.port_b, oriWesTop.port_a)
  connect(oriWesTop.port_b, colWesBot.port_a)
  connect(colWesBot.port_b, volWes.ports[3])
  connect(colEasTop.port_b, oriEasTop.port_b)
  connect(oriEasTop.port_a, col1EasBot.port_a)
  connect(colEasInBot.port_a, volEas.ports[3])
  connect(colEasInTop.port_b, volEas.ports[4])
  connect(col1EasBot.port_b, volEas.ports[5])
  connect(colOutTop.port_b, volOut.ports[1])
  connect(volOut.ports[2], colOutBot.port_a)
  connect(colOutBot.port_b, oriOutBot.port_b)
  connect(oriOutBot.port_a, colEasInBot.port_b)
  connect(colEasInTop.port_a, oriOutTop.port_a)
  connect(oriOutTop.port_b, colOutTop.port_a)
  connect(ope.y, dooOpeClo.y)
  connect(colWesTop.port_a, volTop.ports[1])
  connect(colEasTop.port_a, volTop.ports[2])
end ReverseBuoyancy3Zones;


within AixLib.Airflow.Multizone.Examples;
model TrickleVent
  "Model with a trickle vent modelled using the models with flow based on tabulated data"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;

  BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
      filNam=Modelica.Utilities.Files.loadResource(
        "modelica://AixLib/Resources/weatherdata/USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos"))
    "Weather data reader"
  Fluid.Sources.Outside_CpLowRise west(
    redeclare package Medium = Medium,
    s=5,
    azi=AixLib.Types.Azimuth.W,
    Cp0=0.6,
    nPorts=1) "Model with outside conditions"
  Fluid.Sources.Outside_CpLowRise east(
    redeclare package Medium = Medium,
    s=5,
    azi=AixLib.Types.Azimuth.E,
    Cp0=0.6,
    nPorts=1) "Model with outside conditions"
  Fluid.MixingVolumes.MixingVolume room(
    redeclare package Medium = Medium,
    V=2.5*5*5,
    nPorts=2,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01) "Room model"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHea
    "Prescribed heat flow"
  Modelica.Blocks.Continuous.LimPID con(
    Td=10,
    yMax=1,
    yMin=-1,
    Ti=60,
    controllerType=Modelica.Blocks.Types.SimpleController.P,
    k=5) "Controller to maintain volume temperature"
  Modelica.Blocks.Sources.Constant TSet(k=293.15) "Temperature set point"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSen
    "Temperature sensor"
  Modelica.Blocks.Math.Gain gain(k=3000) "Gain block"
  AixLib.Airflow.Multizone.Table_m_flow tabDat_m_flow(
    redeclare package Medium = Medium,
    dpMea_nominal = {-50,  -25,  -10,  -5,  -3,  -2,  -1,  0,  1,  2,  3,  4.5,  50},
    mMea_flow_nominal = {-0.08709, -0.06158, -0.03895, -0.02754, -0.02133, -0.01742, -0.01232, 0, 0.01232, 0.01742, 0.02133, 0.02613, 0.02614})
    "Self regulating trickle vent"
  AixLib.Airflow.Multizone.Table_V_flow tabDat_V_flow(
    redeclare package Medium = Medium,
    dpMea_nominal = {-50,  -25,  -10,  -5,  -3,  -2,  -1,  0,  1,  2,  3,  4.5,  50},
    VMea_flow_nominal = {-0.104508, -0.073896, -0.04674, -0.033048, -0.025596, -0.020904, -0.014784, 0, 0.014784, 0.020904, 0.025596, 0.031356,  0.031368})
   "Self regulating trickle vent"
equation
  connect(weaDat.weaBus, west.weaBus)
  connect(east.weaBus, weaDat.weaBus)
  connect(TSet.y,con. u_s)
  connect(temSen.T,con. u_m)
  connect(gain.u,con. y)
  connect(gain.y,preHea. Q_flow)
  connect(room.heatPort, temSen.port)
  connect(preHea.port,room. heatPort)
  connect(east.ports[1], tabDat_m_flow.port_a)
  connect(tabDat_m_flow.port_b,room. ports[1])
  connect(tabDat_V_flow.port_a,room. ports[2])
  connect(tabDat_V_flow.port_b, west.ports[1])
end TrickleVent;


within AixLib.Airflow.Multizone.Examples;
model ZonalFlow "Model with prescribed air exchange between two volumes"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Air;
  parameter Modelica.Units.SI.Volume volA=100 "Volume of room A";
  parameter Modelica.Units.SI.Volume volB=1 "Volume of room B";
  AixLib.Fluid.MixingVolumes.MixingVolume rooA(
    V=volA,
    redeclare package Medium = Medium,
    X_start={0.015,0.985},
    T_start=303.15,
    nPorts=4,
    m_flow_nominal=0.001,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial) "Room A"
  AixLib.Fluid.MixingVolumes.MixingVolume rooB(
    V=volB,
    redeclare package Medium = Medium,
    X_start={0.01,0.99},
    T_start=293.15,
    nPorts=4,
    m_flow_nominal=0.001,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial) "Room B"
  Modelica.Blocks.Sources.Constant ACS(k=5/3600) "Air change rate per second"
  ZonalFlow_ACS zonFlo(
    redeclare package Medium = Medium,
    V=min(volA, volB))
    "Model for prescribed flow exchange between two volume with air change per second as input"
  Modelica.Blocks.Sources.Constant m_flow(k=0.02) "Exchange mass flow rate"
  ZonalFlow_m_flow floExc(redeclare package Medium = Medium)
    "Model for prescribed flow exchange between two volumes with mass flow rate as inputs"
equation
  connect(rooA.ports[1], zonFlo.port_a1)
  connect(zonFlo.port_b1, rooB.ports[1])
  connect(zonFlo.port_b2, rooA.ports[2])
  connect(zonFlo.port_a2, rooB.ports[2])
  connect(zonFlo.ACS, ACS.y)
  connect(floExc.mAB_flow, m_flow.y)
  connect(m_flow.y, floExc.mBA_flow)
  connect(floExc.port_a1, rooA.ports[3])
  connect(floExc.port_b1, rooB.ports[3])
  connect(floExc.port_a2, rooB.ports[4])
  connect(floExc.port_b2, rooA.ports[4])
end ZonalFlow;


Package with type definitions

within AixLib.Airflow.Multizone;
package Types "Package with type definitions"
  extends Modelica.Icons.TypesPackage;
end Types;


within AixLib.Airflow.Multizone.Validation;
model DoorOpenClosed
  "Model with operable door and door that is always open"
  extends Modelica.Icons.Example;

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

  Fluid.Sources.Boundary_pT bouA(
    redeclare package Medium = Medium,
    p(displayUnit="Pa") = 101330,
    T=294.15,
    nPorts=5) "Boundary condition at side a"

  Fluid.Sources.Boundary_pT bouB(
    redeclare package Medium = Medium,
    p(displayUnit="Pa") = 101325,
    T=293.15,
    nPorts=5) "Boundary condition at side b"

  AixLib.Airflow.Multizone.DoorOpen doo(
    redeclare package Medium = Medium)
    "Open door that is always open"

  EffectiveAirLeakageArea lea(
    redeclare package Medium = Medium,
    m=0.65,
    dp_turbulent=0.01,
    L=20*1E-4) "Leakage flow element"
  DoorOperable dooOpeClo(
    redeclare package Medium = Medium,
    LClo=20*1E-4)
               "Operable door"
  Modelica.Blocks.Sources.Step yDoo(startTime=0.5) "Door opening signal"
  Fluid.Sensors.MassFlowRate senMasFlo1(redeclare package Medium = Medium)
    "Mass flow rate sensor"
  Fluid.Sensors.MassFlowRate senMasFlo2(
    redeclare package Medium = Medium)
    "Mass flow rate sensor"
  Modelica.Blocks.Math.Add mNet_flow(y(final unit="kg/s"))
    "Net air flow rate from A to B through the operable door"
  Modelica.Blocks.Sources.Step yDooCom(
    height=-1,
    offset=1,
    startTime=0.5) "Outputs 1 if door is closed"
  Modelica.Blocks.Math.Product mNetClo_flow(y(final unit="kg/s"))
    "Mass flow rate if door is closed"
equation
  connect(bouA.ports[1], doo.port_a1)
  connect(bouA.ports[2], doo.port_b2)
  connect(bouA.ports[3], lea.port_a)
  connect(bouA.ports[4], dooOpeClo.port_a1)
  connect(bouA.ports[5], dooOpeClo.port_b2)
  connect(doo.port_b1, bouB.ports[1])
  connect(doo.port_a2, bouB.ports[2])
  connect(lea.port_b, bouB.ports[3])
  connect(yDoo.y, dooOpeClo.y)
  connect(dooOpeClo.port_b1, senMasFlo1.port_a)
  connect(senMasFlo1.port_b, bouB.ports[4])
  connect(dooOpeClo.port_a2,senMasFlo2. port_a)
  connect(senMasFlo2.port_b, bouB.ports[5])
  connect(senMasFlo1.m_flow, mNet_flow.u1)
  connect(senMasFlo2.m_flow, mNet_flow.u2)
  connect(yDooCom.y, mNetClo_flow.u2)
  connect(mNet_flow.y, mNetClo_flow.u1)
end DoorOpenClosed;


Validation model to verify one way flow implementation


within AixLib.Airflow.Multizone.Validation;
model OpenDoorBuoyancyDynamic
  "Model with open door and buoyancy driven flow only"
  extends Modelica.Icons.Example;

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

  AixLib.Airflow.Multizone.DoorOpen doo(
    redeclare package Medium = Medium)
    "Door"

  AixLib.Fluid.Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=1) "Pressure boundary"

  Fluid.MixingVolumes.MixingVolume bouA(
    redeclare package Medium = Medium,
    T_start=292.15,
    V=2.5*5*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01,
    nPorts=3) "Boundary condition at side a"

  AixLib.Fluid.MixingVolumes.MixingVolume bouB(
    redeclare package Medium = Medium,
    T_start=294.15,
    V=2.5*5*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01,
    nPorts=2) "Boundary condition at side b"
  DoorDiscretizedOpen dooDis(
    forceErrorControlOnFlow=false,
    redeclare package Medium = Medium,
    vZer=0.000001) "Door"
  Fluid.Sources.Boundary_pT bou1(
    redeclare package Medium = Medium,
    nPorts=1) "Pressure boundary"
  Fluid.MixingVolumes.MixingVolume bouADis(
    redeclare package Medium = Medium,
    T_start=292.15,
    V=2.5*5*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01,
    nPorts=3) "Boundary condition at side a"
  Fluid.MixingVolumes.MixingVolume bouBDis(
    redeclare package Medium = Medium,
    T_start=294.15,
    V=2.5*5*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01,
    nPorts=2) "Boundary condition at side b"
equation
  connect(doo.port_b1, bouB.ports[1])
  connect(doo.port_a2, bouB.ports[2])
  connect(doo.port_a1, bouA.ports[1])
  connect(doo.port_b2, bouA.ports[2])
  connect(bou.ports[1], bouA.ports[3])
  connect(bou1.ports[1], bouADis.ports[1])
  connect(dooDis.port_a1, bouADis.ports[2])
  connect(dooDis.port_b2, bouADis.ports[3])
  connect(dooDis.port_b1, bouBDis.ports[1])
  connect(dooDis.port_a2, bouBDis.ports[2])
end OpenDoorBuoyancyDynamic;


within AixLib.Airflow.Multizone.Validation;
model OpenDoorBuoyancyPressureDynamic
  "Model with open door and buoyancy and pressure driven flow"
  extends Modelica.Icons.Example;

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

  AixLib.Airflow.Multizone.DoorOpen doo(redeclare package Medium = Medium)
    "Door"

  AixLib.Fluid.Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=1) "Pressure boundary"

  Fluid.MixingVolumes.MixingVolume bouA(
    redeclare package Medium = Medium,
    T_start=292.15,
    V=2.5*5*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01,
    nPorts=3) "Boundary condition at side a"

  AixLib.Fluid.MixingVolumes.MixingVolume bouB(
    redeclare package Medium = Medium,
    T_start=294.15,
    p_start=101320,
    V=2.5*5*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01,
    nPorts=2) "Boundary condition at side b"
  DoorDiscretizedOpen dooDis(redeclare package Medium = Medium) "Door"
  Fluid.Sources.Boundary_pT bouDis(redeclare package Medium = Medium, nPorts=1)
    "Pressure boundary"
  Fluid.MixingVolumes.MixingVolume bouADis(
    redeclare package Medium = Medium,
    T_start=292.15,
    V=2.5*5*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01,
    nPorts=3) "Boundary condition at side a"
  Fluid.MixingVolumes.MixingVolume bouBDis(
    redeclare package Medium = Medium,
    T_start=294.15,
    p_start=101320,
    V=2.5*5*5,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    m_flow_nominal=0.01,
    nPorts=2) "Boundary condition at side b"
equation
  connect(doo.port_b1, bouB.ports[1])
  connect(doo.port_a2, bouB.ports[2])
  connect(doo.port_a1, bouA.ports[1])
  connect(doo.port_b2, bouA.ports[2])
  connect(bou.ports[1], bouA.ports[3])

  connect(dooDis.port_b1, bouBDis.ports[1])
  connect(dooDis.port_a2, bouBDis.ports[2])
  connect(dooDis.port_a1, bouADis.ports[1])
  connect(dooDis.port_b2, bouADis.ports[2])
  connect(bouDis.ports[1], bouADis.ports[3])
end OpenDoorBuoyancyPressureDynamic;


within AixLib.Airflow.Multizone.Validation;
model OpenDoorPressure
  "Model with one open door and only pressure-driven flow"
  extends Modelica.Icons.Example;

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

  AixLib.Airflow.Multizone.DoorOpen doo(
    redeclare package Medium = Medium)
    "Door"

  Fluid.Sources.Boundary_pT bouA(
    redeclare package Medium = Medium,
    p(displayUnit="Pa") = 101325,
    nPorts=4) "Boundary condition at side a"

  Fluid.Sources.Boundary_pT bouB(
    redeclare package Medium = Medium,
    use_p_in=true,
    p(displayUnit="Pa"),
    nPorts=4) "Boundary condition at side b"

  DoorDiscretizedOpen dooDis(
    redeclare package Medium = Medium,
    hA=2.1/2,
    hB=2.1/2) "Door"
  Modelica.Blocks.Sources.TimeTable bouPre(
    table=[
      0.0,0;
      1,0;
      1,5;
      2,5;
      2,10;
      3,10;
      3,15;
      4,15;
      4,20;
      5,20;
      5,25;
      6,25;
      6,30;
      7,30;
      7,35;
      8,35;
      8,40;
      24,40],
    timeScale=3600,
    offset=101325-20) "Pressure boundary condition"
equation
  connect(doo.port_b1, bouB.ports[1])
  connect(doo.port_a2, bouB.ports[2])
  connect(doo.port_a1, bouA.ports[1])
  connect(doo.port_b2, bouA.ports[2])
  connect(bouA.ports[3], dooDis.port_a1)
  connect(bouA.ports[4], dooDis.port_b2)
  connect(dooDis.port_b1, bouB.ports[3])
  connect(dooDis.port_a2, bouB.ports[4])
  connect(bouPre.y, bouB.p_in)
end OpenDoorPressure;


within AixLib.Airflow.Multizone.Validation;
model OpenDoorTemperature
  "Model with one open door and only temperature-driven flow"
  extends Modelica.Icons.Example;

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

  AixLib.Airflow.Multizone.DoorOpen doo(
    redeclare package Medium = Medium)
    "Door"

  Fluid.Sources.Boundary_pT bouA(
    redeclare package Medium = Medium,
    nPorts=4) "Boundary condition at side a"

  Fluid.Sources.Boundary_pT bouB(
    redeclare package Medium = Medium,
    use_T_in=true,
    nPorts=4) "Boundary condition at side b"

  DoorDiscretizedOpen dooDis(
    redeclare package Medium = Medium,
    hA=2.1/2,
    hB=2.1/2) "Door"
  Modelica.Blocks.Sources.TimeTable bouTem(
    table=[
      0.0,0;
      1,0;
      1,5;
      2,5;
      2,10;
      3,10;
      3,15;
      4,15;
      4,20;
      5,20;
      5,25;
      6,25;
      6,30;
      7,30;
      7,35;
      8,35;
      8,40;
      24,40],
    timeScale=3600,
    offset=273.15) "Temperature boundary condition"
equation
  connect(doo.port_b1, bouB.ports[1])
  connect(doo.port_a2, bouB.ports[2])
  connect(doo.port_a1, bouA.ports[1])
  connect(doo.port_b2, bouA.ports[2])
  connect(bouA.ports[3], dooDis.port_a1)
  connect(bouA.ports[4], dooDis.port_b2)
  connect(dooDis.port_b1, bouB.ports[3])
  connect(dooDis.port_a2, bouB.ports[4])
  connect(bouTem.y, bouB.T_in)
end OpenDoorTemperature;


Collection of validation models

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