within Buildings.ThermalZones.Detailed.BaseClasses;
model CFDAirHeatMassBalance
  "Heat and mass balance of the air based on computational fluid dynamics"
  extends Buildings.ThermalZones.Detailed.BaseClasses.PartialAirHeatMassBalance;

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

  // Assumptions
  parameter Modelica.Fluid.Types.Dynamics massDynamics
    "Formulation of mass balance"
  // Initialization
  parameter Medium.AbsolutePressure p_start "Start value of pressure"

  parameter String cfdFilNam "CFD input file name"
  parameter Boolean useCFD=true
    "Set to false to deactivate the CFD interface and use instead yFixed as output"

  parameter Modelica.Units.SI.Time samplePeriod(min=100*Modelica.Constants.eps)
    "Sample period of component"

  parameter Boolean haveSensor
    "Flag, true if the model has at least one sensor";
  parameter Integer nSen(min=0)
    "Number of sensors that are connected to CFD output";
  parameter String sensorName[nSen]
    "Names of sensors as declared in the CFD input file";
  parameter String portName[nPorts]
    "Names of fluid ports as declared in the CFD input file";
  parameter Real uSha_fixed[nConExtWin]
    "Constant control signal for the shading device (0: unshaded; 1: fully shaded)";

  CFDExchange cfd(
    final cfdFilNam=cfdFilNam,
    final startTime=startTime,
    final activateInterface=useCFD,
    final samplePeriod=if useCFD then samplePeriod else Modelica.Constants.inf,
    final nWri=kFluIntC_inflow + Medium.nC*nPorts,
    final nRea=kSen + nSen,
    final nSur=nSur,
    final surIde=surIde,
    final haveShade=haveShade,
    final haveSensor=haveSensor,
    final sensorName=sensorName,
    final portName=portName,
    final yFixed=yFixed,
    final nXi=Medium.nXi,
    final nC=Medium.nC,
    rho_start=rho_start,
    nConExtWin=NConExtWin) "Block that exchanges data with the CFD simulation"

  Modelica.Blocks.Interfaces.RealOutput yCFD[nSen] if haveSensor
    "Sensor for output from CFD"

protected
  parameter Modelica.Units.SI.Time startTime(fixed=false)
    "First sample time instant.";

  // Values that are used for yFixed
  parameter Real yFixed[kSen + nSen](each fixed=false)
    "Values used for yFixed in CFDExchange";

  parameter Modelica.Units.SI.HeatFlowRate Q_flow_fixed[kSurBou + nSurBou]=fill(
      0, kSurBou + nSurBou) "Surface heat flow rate used for yFixed"
  parameter Modelica.Units.SI.Temperature TRooAve_fixed=Medium.T_default
    "Average room air temperature used for yFixed"
  parameter Modelica.Units.SI.Temperature TSha_fixed[NConExtWin]=fill(Medium.T_default,
      NConExtWin) "Shade temperature used for yFixed"
  parameter Modelica.Units.SI.Temperature T_outflow_fixed[nPorts]=fill(Medium.T_default,
      nPorts)
    "Temperature of the fluid that flows into the HVAC system used for yFixed"
  parameter Real Xi_outflow_fixed[nPorts*Medium.nXi](each fixed=false)
    "Species concentration of the fluid that flows into the HVAC system used for yFixed"
  parameter Real C_outflow_fixed[nPorts*Medium.nC](each fixed=false)
    "Trace substances of the fluid that flows into the HVAC system used for yFixed"

  parameter Modelica.Units.SI.Density rho_start=Medium.density(
      Medium.setState_pTX(
      T=Medium.T_default,
      p=p_start,
      X=Medium.X_default)) "Density, used to compute fluid mass";

  final parameter CFDSurfaceIdentifier surIde[kSurBou + nSurBou]=
      assignSurfaceIdentifier(
      nConExt=nConExt,
      nConExtWin=nConExtWin,
      nConPar=nConPar,
      nConBou=nConBou,
      nSurBou=nSurBou,
      nSur=nSur,
      haveShade=haveShade,
      nameConExt=datConExt.name,
      AConExt=datConExt.A,
      tilConExt=datConExt.til,
      bouConConExt=datConExt.boundaryCondition,
      nameConExtWin=datConExtWin.name,
      AConExtWin=datConExtWin.AOpa,
      tilConExtWin=datConExtWin.til,
      bouConConExtWin=datConExtWin.boundaryCondition,
      AGla=datConExtWin.AGla,
      AFra=datConExtWin.AFra,
      uSha=uSha_fixed,
      nameConPar=datConPar.name,
      AConPar=datConPar.A,
      tilConPar=datConPar.til,
      bouConConPar=datConPar.boundaryCondition,
      nameConBou=datConBou.name,
      AConBou=datConBou.A,
      tilConBou=datConBou.til,
      bouConConBou=datConBou.boundaryCondition,
      nameSurBou=surBou.name,
      ASurBou=surBou.A,
      tilSurBou=surBou.til,
      bouConSurBou=surBou.boundaryCondition)
    "Names of all surfaces in the order in which their properties are sent to CFD"

  // Interfaces between the CFD block and the heat ports of this model
  // Here, we directly access datConExt instead of surIde. The reason is
  // the Dymola thinks that surIde.bouCon is not fixed at translation time
  // and then refuses to use this parameter to conditionally remove connectors
  // in CFDSurfaceInterface.
  CFDSurfaceInterface cfdConExt[NConExt](final bouCon=datConExt[:].boundaryCondition)
   if haveConExt "Interface to heat port of exterior constructions"

  CFDSurfaceInterface cfdConExtWin[NConExtWin](final bouCon=datConExtWin[:].boundaryCondition)
    if haveConExtWin
    "Interface to heat port of opaque part of exterior constructions with window"

  CFDSurfaceInterface cfdGlaUns[NConExtWin](final bouCon=datConExtWin[:].boundaryCondition)
    if haveConExtWin "Interface to heat port of unshaded part of glass"

  CFDSurfaceInterface cfdGlaSha[NConExtWin](final bouCon=datConExtWin[:].boundaryCondition)
    if haveShade "Interface to heat port of shaded part of glass"

  CFDSurfaceInterface cfdConExtWinFra[NConExtWin](final bouCon=datConExtWin[:].boundaryCondition)
    if haveConExtWin "Interface to heat port of window frame"

  CFDSurfaceInterface cfdConPar_a[NConPar](final bouCon=datConPar[:].boundaryCondition)
    if haveConPar
    "Interface to heat port of surface a of partition constructions"

  CFDSurfaceInterface cfdConPar_b[NConPar](final bouCon=datConPar[:].boundaryCondition)
    if haveConPar
    "Interface to heat port of surface b of partition constructions"

  CFDSurfaceInterface cfdConBou[NConBou](final bouCon=datConBou[:].boundaryCondition)
    if haveConBou
    "Interface to heat port that connects to room-side surface of constructions that expose their other surface to the outside"

  CFDSurfaceInterface cfdSurBou[NSurBou](final bouCon=surBou[:].boundaryCondition)
    if haveSurBou
    "Interface to heat port of surfaces of models that compute the heat conduction outside of this room"

  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature cfdHeaPorAir
    "Interface to heat port of air node"

  CFDFluidInterface fluInt(
    redeclare final package Medium = Medium,
    final massDynamics=massDynamics,
    final nPorts=nPorts,
    final V=V,
    final p_start=p_start,
    rho_start=rho_start) "Fluid interface"

  // The following list declares the first index minus 1
  // of the input and output signals to the CFD block.
  // These parameters are then used to loop over the connectors, such
  // as
  //    for i in kConExt+1:kConExt+nConExt loop
  //      ...
  //    end for;
  final parameter Integer kConExt=0
    "Offset used to connect CFD signals to conExt";
  final parameter Integer kConExtWin=kConExt + nConExt
    "Offset used to connect CFD signals to conExtWin";
  final parameter Integer kGlaUns=kConExtWin + nConExtWin
    "Offset used to connect CFD signals to glaUns";
  final parameter Integer kGlaSha=kGlaUns + nConExtWin
    "Offset used to connect CFD signals to glaSha";
  final parameter Integer kConExtWinFra=if haveShade then kGlaSha + nConExtWin
       else kGlaSha "Offset used to connect CFD signals to glaSha";
  final parameter Integer kConPar_a=kConExtWinFra + nConExtWin
    "Offset used to connect CFD signals to conPar_a";
  final parameter Integer kConPar_b=kConPar_a + nConPar
    "Offset used to connect CFD signals to conPar_b";
  final parameter Integer kConBou=kConPar_b + nConPar
    "Offset used to connect CFD signals to conBou";
  final parameter Integer kSurBou=kConBou + nConBou
    "Offset used to connect CFD signals to surBou";
  final parameter Integer kHeaPorAir=kSurBou + nSurBou
    "Offset used to connect CFD output signal to air heat port (to send average temperature from CFD to Modelica)";
  //  final parameter Integer kUSha = kHeaPorAir + 1
  final parameter Integer kUSha=kSurBou + nSurBou
    "Offset used to connect CFD signals to input signal of shade";
  final parameter Integer kQRadAbs_flow=if haveShade then kUSha + nConExtWin
       else kUSha
    "Offset used to connect CFD signals to input signal that contains the radiation absorbed by the shade";
  // Because heaPorAir is only receiving T from CFD, but does not send Q_flow to CFD, there is no '+1' increment
  // for kTSha
  final parameter Integer kTSha=kHeaPorAir + 1
    "Offset used to connect CFD signals to output signal that contains the shade temperature";

  final parameter Integer kQConGai_flow=if haveShade then kQRadAbs_flow +
      nConExtWin else kQRadAbs_flow
    "Offset used to connect CFD signals to input signal for connect convective sensible heat gain";

  final parameter Integer kQLatGai_flow=kQConGai_flow + 1
    "Offset used to connect CFD signals to input signal for connect radiative heat gain";

  final parameter Integer kFluIntP=kQLatGai_flow + 1
    "Offset used to connect CFD signals to input signal for pressure from the fluid ports";

  final parameter Integer kFluIntM_flow=kFluIntP + 1
    "Offset used to connect CFD signals to input signals for mass flow rate from the fluid ports";
  final parameter Integer kFluIntT_inflow=kFluIntM_flow + nPorts
    "Offset used to connect CFD signals to input signals for inflowing temperature from the fluid ports";
  final parameter Integer kFluIntXi_inflow=kFluIntT_inflow + nPorts
    "Offset used to connect CFD signals to input signals for inflowing species concentration from the fluid ports";

  final parameter Integer kFluIntC_inflow=kFluIntXi_inflow + nPorts*Medium.nXi
    "Offset used to connect CFD signals to input signals for inflowing trace substances from the fluid ports";

  // Input signals to fluInt block
  final parameter Integer kFluIntT_outflow=if haveShade then kTSha + nConExtWin
       else kTSha
    "Offset used to connect CFD signals to outgoing temperature for the fluid ports";
  final parameter Integer kFluIntXi_outflow=kFluIntT_outflow + nPorts
    "Offset used to connect CFD signals to outgoing species concentration for the fluid ports";
  final parameter Integer kFluIntC_outflow=kFluIntXi_outflow + nPorts*Medium.nXi
    "Offset used to connect CFD signals to outgoing trace substances for the fluid ports";
  final parameter Integer kSen=kFluIntC_outflow + nPorts*Medium.nC
    "Offset used to connect CFD signals to output sensor";

  final parameter Integer nSur=kSurBou + nSurBou "Number of surfaces";
protected
  function assignSurfaceIdentifier

    input Integer nConExt(min=0) "Number of exterior constructions";
    input Integer nConExtWin(min=0) "Number of window constructions";
    input Integer nConPar(min=0) "Number of partition constructions";
    input Integer nConBou(min=0)
      "Number of constructions that have their outside surface exposed to the boundary of this room";
    input Integer nSurBou(min=0)
      "Number of surface heat transfer models that connect to constructions that are modeled outside of this room";
    input Integer nSur(min=2) "Total number of surfaces";

    input Boolean haveShade
      "Flag, set to true if any of the window in this room has a shade";
    /*
    // Declaration of counters used in the loop.
    // This could be computed (again) in this function, but using it
    // as a function arguments avoids code duplication.
    input Integer kConExt "Offset used to connect CFD signals to conExt";
    input Integer kConExtWin "Offset used to connect CFD signals to conExtWin";
    input Integer kGlaUns "Offset used to connect CFD signals to glaUns";
    input Integer kGlaSha "Offset used to connect CFD signals to glaSha";
    input Integer kConExtWinFra "Offset used to connect CFD signals to glaSha";
    input Integer kConPar_a "Offset used to connect CFD signals to conPar_a";
    input Integer kConPar_b "Offset used to connect CFD signals to conPar_b";
    input Integer kConBou "Offset used to connect CFD signals to conBou";
    input Integer kSurBou "Offset used to connect CFD signals to surBou";
*/
    // Declaration of construction data
    input String nameConExt[nConExt] "Surface name";
    input Modelica.Units.SI.Area AConExt[nConExt] "Surface area";
    input Modelica.Units.SI.Angle tilConExt[nConExt] "Surface tilt";
    input Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions bouConConExt[nConExt]
      "Boundary condition";

    input String nameConExtWin[nConExtWin] "Surface name";
    input Modelica.Units.SI.Area AConExtWin[nConExtWin] "Surface area";
    input Modelica.Units.SI.Angle tilConExtWin[nConExtWin] "Surface tilt";
    input Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions bouConConExtWin[
      nConExtWin] "Boundary condition";
    input Modelica.Units.SI.Area AGla[nConExtWin] "Surface area";
    input Modelica.Units.SI.Area AFra[nConExtWin] "Surface area";
    input Real uSha[nConExtWin] "Shade ratio";
    input String nameConPar[nConPar] "Surface name";
    input Modelica.Units.SI.Area AConPar[nConPar] "Surface area";
    input Modelica.Units.SI.Angle tilConPar[nConPar] "Surface tilt";
    input Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions bouConConPar[nConPar]
      "Boundary condition";

    input String nameConBou[nConBou] "Surface name";
    input Modelica.Units.SI.Area AConBou[nConBou] "Surface area";
    input Modelica.Units.SI.Angle tilConBou[nConBou] "Surface tilt";
    input Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions bouConConBou[nConBou]
      "Boundary condition";

    input String nameSurBou[nSurBou] "Surface name";
    input Modelica.Units.SI.Area ASurBou[nSurBou] "Surface area";
    input Modelica.Units.SI.Angle tilSurBou[nSurBou] "Surface tilt";
    input Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions bouConSurBou[nSurBou]
      "Boundary condition";

    output CFDSurfaceIdentifier id[nSur] "Name of all surfaces";

  algorithm
    id := cat(
        1,
        {CFDSurfaceIdentifier(
          name=nameConExt[i],
          A=AConExt[i],
          til=tilConExt[i],
          bouCon=bouConConExt[i]) for i in 1:nConExt},
        {CFDSurfaceIdentifier(
          name=nameConExtWin[i],
          A=AConExtWin[i],
          til=tilConExtWin[i],
          bouCon=bouConConExtWin[i]) for i in 1:nConExtWin},
        {CFDSurfaceIdentifier(
          name=nameConExtWin[i] + " (glass, unshaded)",
          A=AGla[i]*(1-uSha[i]),
          til=tilConExtWin[i],
          bouCon=bouConConExtWin[i]) for i in 1:nConExtWin},
        {CFDSurfaceIdentifier(
          name=nameConExtWin[i] + " (glass, shaded)",
          A=AGla[i]*uSha[i],
          til=tilConExtWin[i],
          bouCon=bouConConExtWin[i]) for i in 1:(if haveShade then nConExtWin
         else 0)},
        {CFDSurfaceIdentifier(
          name=nameConExtWin[i] + " (frame)",
          A=AFra[i],
          til=tilConExtWin[i],
          bouCon=bouConConExtWin[i]) for i in 1:nConExtWin},
        {CFDSurfaceIdentifier(
          name=nameConPar[i] + " (surface a)",
          A=AConPar[i],
          til=tilConPar[i],
          bouCon=bouConConPar[i]) for i in 1:nConPar},
        {CFDSurfaceIdentifier(
          name=nameConPar[i] + " (surface b)",
          A=AConPar[i],
          til=tilConPar[i] + Modelica.Constants.pi/180,
          bouCon=bouConConPar[i]) for i in 1:nConPar},
        {CFDSurfaceIdentifier(
          name=nameConBou[i],
          A=AConBou[i],
          til=tilConBou[i],
          bouCon=bouConConBou[i]) for i in 1:nConBou},
        {CFDSurfaceIdentifier(
          name=nameSurBou[i],
          A=ASurBou[i],
          til=tilSurBou[i],
          bouCon=bouConSurBou[i]) for i in 1:nSurBou});
  end assignSurfaceIdentifier;

public
  Modelica.Blocks.Math.Add QTotCon_flow
    "Total sensible convective heat flow rate added to the room"
  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor senHeaFlo
    "Sensor for heat flow added through the port heaPorAir"
  to_W QTotCon_flow_W
initial equation
   startTime = time;

  for i in 1:nPorts loop
    for j in 1:Medium.nXi loop
      Xi_outflow_fixed[(i - 1)*Medium.nXi + j] = Medium.X_default[j];
    end for;
  end for;

  for i in 1:nPorts loop
    for j in 1:Medium.nC loop
      C_outflow_fixed[(i - 1)*Medium.nC + j] = 0;
    end for;
  end for;

  // Assignment of yFixed
  for i in 1:kSurBou + nSurBou loop
    yFixed[i] = Q_flow_fixed[i];
  end for;
  yFixed[kHeaPorAir + 1] = TRooAve_fixed;
  if haveShade then
    for i in 1:nConExtWin loop
      yFixed[kTSha + i] = TSha_fixed[i];
    end for;
  end if;
  for i in 1:nPorts loop
    yFixed[kFluIntT_outflow + i] = T_outflow_fixed[i];
    for j in 1:Medium.nXi loop
      yFixed[kFluIntXi_outflow + (i - 1)*Medium.nXi + j] = Xi_outflow_fixed[(i
         - 1)*Medium.nXi + j];
    end for;
    for j in 1:Medium.nC loop
      yFixed[kFluIntC_outflow + (i - 1)*Medium.nC + j] = C_outflow_fixed[(i - 1)
        *Medium.nC + j];
    end for;
  end for;
  for i in 1:nSen loop
    yFixed[kSen + i] = 0;
  end for;
equation
  //////////////////////////////////////////////////////////////////////
  // Data exchange with CFD block
  if haveConExt then
    for i in 1:nConExt loop
      if datConExt[i].boundaryCondition == Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions.Temperature then
        connect(cfd.u[kConExt + i], cfdConExt[i].T_out)
        connect(cfd.y[kConExt + i], cfdConExt[i].Q_flow_in)
      else
        connect(cfd.u[kConExt + i], cfdConExt[i].Q_flow_out)
        connect(cfd.y[kConExt + i], cfdConExt[i].T_in)
      end if;
    end for;
  end if;

  if haveConExtWin then
    for i in 1:nConExtWin loop
      if datConExtWin[i].boundaryCondition == Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions.Temperature then
        connect(cfd.u[kConExtWin + i], cfdConExtWin[i].T_out)

        connect(cfd.y[kConExtWin + i], cfdConExtWin[i].Q_flow_in)
        connect(cfd.u[kGlaUns + i], cfdGlaUns[i].T_out)
        connect(cfd.y[kGlaUns + i], cfdGlaUns[i].Q_flow_in)
        connect(cfd.u[kConExtWinFra + i], cfdConExtWinFra[i].T_out)
        connect(cfd.y[kConExtWinFra + i], cfdConExtWinFra[i].Q_flow_in)
      else
        connect(cfd.u[kConExtWin + i], cfdConExtWin[i].Q_flow_out)
        connect(cfd.y[kConExtWin + i], cfdConExtWin[i].T_in)
        connect(cfd.u[kGlaUns + i], cfdGlaUns[i].Q_flow_out)
        connect(cfd.y[kGlaUns + i], cfdGlaUns[i].T_in)
        connect(cfd.u[kConExtWinFra + i], cfdConExtWinFra[i].Q_flow_out)
        connect(cfd.y[kConExtWinFra + i], cfdConExtWinFra[i].T_in)
      end if;
    end for;
  end if;

  if haveShade then
    for i in 1:nConExtWin loop
      if datConExtWin[i].boundaryCondition == Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions.Temperature then
        connect(cfd.u[kGlaSha + i], cfdGlaSha[i].T_out)
        connect(cfd.y[kGlaSha + i], cfdGlaSha[i].Q_flow_in)
      else
        connect(cfd.u[kGlaSha + i], cfdGlaSha[i].Q_flow_out)
        connect(cfd.y[kGlaSha + i], cfdGlaSha[i].T_in)
      end if;
    end for;
  end if;

  if haveConPar then
    for i in 1:nConPar loop
      if datConPar[i].boundaryCondition == Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions.Temperature then
        connect(cfd.u[kConPar_a + i], cfdConPar_a[i].T_out)
        connect(cfd.y[kConPar_a + i], cfdConPar_a[i].Q_flow_in)
        connect(cfd.u[kConPar_b + i], cfdConPar_b[i].T_out)
        connect(cfd.y[kConPar_b + i], cfdConPar_b[i].Q_flow_in)
      else
        connect(cfd.u[kConPar_a + i], cfdConPar_a[i].Q_flow_out)
        connect(cfd.y[kConPar_a + i], cfdConPar_a[i].T_in)
        connect(cfd.u[kConPar_b + i], cfdConPar_b[i].Q_flow_out)
        connect(cfd.y[kConPar_b + i], cfdConPar_b[i].T_in)
      end if;
    end for;
  end if;

  if haveConBou then
    for i in 1:nConBou loop
      if datConBou[i].boundaryCondition == Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions.Temperature then
        connect(cfd.u[kConBou + i], cfdConBou[i].T_out)
        connect(cfd.y[kConBou + i], cfdConBou[i].Q_flow_in)
      else
        connect(cfd.u[kConBou + i], cfdConBou[i].Q_flow_out)
        connect(cfd.y[kConBou + i], cfdConBou[i].T_in)
      end if;
    end for;
  end if;

  if haveSurBou then
    for i in 1:nSurBou loop
      if surBou[i].boundaryCondition == Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions.Temperature then
        connect(cfd.u[kSurBou + i], cfdSurBou[i].T_out)
        connect(cfd.y[kSurBou + i], cfdSurBou[i].Q_flow_in)
      else
        connect(cfd.u[kSurBou + i], cfdSurBou[i].Q_flow_out)
        connect(cfd.y[kSurBou + i], cfdSurBou[i].T_in)
      end if;
    end for;
  end if;

  connect(cfdConExt.port, conExt)
  connect(cfdConExtWin.port, conExtWin)
  connect(cfdGlaUns.port, glaUns)
  connect(cfdGlaSha.port, glaSha)
  connect(cfdConExtWinFra.port, conExtWinFra)
  connect(cfdConPar_a.port, conPar_a)
  connect(cfdConPar_b.port, conPar_b)
  connect(cfdConBou.port, conBou)
  connect(cfdSurBou.port, conSurBou)
  // Connections to heat port of air volume
  connect(cfd.y[kHeaPorAir + 1], cfdHeaPorAir.T)
  // Connections to shade
  if haveShade then
    for i in 1:nConExtWin loop
      connect(cfd.u[kUSha + i], uSha[i])
      connect(cfd.u[kQRadAbs_flow + i], QRadAbs_flow[i])
      connect(cfd.y[kTSha + i], TSha[i])
    end for;
  end if;

  // Connection for heat gain that is added to the room
  // (averaged over the whole room air volume)
  connect(QTotCon_flow_W.y, cfd.u[kQConGai_flow + 1])
  connect(QLat_flow, cfd.u[kQLatGai_flow + 1])

  // Connections to fluid port
  connect(ports, fluInt.ports)

  // Output signals from fluInt block

  // The pressure of the air volume will be sent from Modelica to CFD
  connect(cfd.u[kFluIntP + 1], fluInt.p)
  for i in 1:nPorts loop
    connect(cfd.u[kFluIntM_flow + i], fluInt.m_flow[i])
    connect(cfd.u[kFluIntT_inflow + i], fluInt.T_inflow[i])
    for j in 1:Medium.nXi loop
      connect(cfd.u[kFluIntXi_inflow + (i - 1)*Medium.nXi + j], fluInt.Xi_inflow[
        (i - 1)*Medium.nXi + j])
    end for;
    for j in 1:Medium.nC loop
      connect(cfd.u[kFluIntC_inflow + (i - 1)*Medium.nC + j], fluInt.C_inflow[(
        i - 1)*Medium.nC + j])
    end for;
  end for;
  // Input signals to fluInt block
  // The pressures of ports[2:nPorts] will be sent from CFD to Modelica
  for i in 1:nPorts loop
    connect(cfd.y[kFluIntT_outflow + i], fluInt.T_outflow[i])
    for j in 1:Medium.nXi loop
      connect(cfd.y[kFluIntXi_outflow + (i - 1)*Medium.nXi + j], fluInt.Xi_outflow[
        (i - 1)*Medium.nXi + j])
    end for;
    for j in 1:Medium.nC loop
      connect(cfd.y[kFluIntC_outflow + (i - 1)*Medium.nC + j], fluInt.C_outflow[
        (i - 1)*Medium.nC + j])
    end for;
  end for;

  // Connections for sensor signal
  if haveSensor then
    for i in 1:nSen loop
      connect(cfd.y[kSen + i], yCFD[i])
    end for;
  end if;

  connect(heaPorAir, senHeaFlo.port_a)
  connect(senHeaFlo.port_b, cfdHeaPorAir.port)
  connect(senHeaFlo.Q_flow, QTotCon_flow.u1)
  connect(QCon_flow, QTotCon_flow.u2)

  connect(QTotCon_flow.y, QTotCon_flow_W.u)
end CFDAirHeatMassBalance;


Block that exchanges data with the CFD code. This block samples interface variables and exchanges data with the CFD code.

within Buildings.ThermalZones.Detailed.BaseClasses;
block CFDExchange "Block that exchanges data with the CFD code"
  extends Modelica.Blocks.Interfaces.DiscreteBlock(
    firstTrigger(start=false,
                 fixed=true));
  parameter String cfdFilNam "CFD input file name"
  parameter Boolean activateInterface=true
    "Set to false to deactivate interface and use instead yFixed as output"
  parameter Integer nXi
    "Number of independent species concentration of the inflowing medium";
  parameter Integer nC "Number of trace substances of the inflowing medium";
  parameter Integer nWri(min=0)
    "Number of values to write to the CFD simulation";
  parameter Integer nRea(min=0)
    "Number of double values to be read from the CFD simulation";
  parameter Integer flaWri[nWri] = ones(nWri)
    "Flag for double values (0: use current value, 1: use average over interval, 2: use integral over interval)"
  parameter Real yFixed[nRea] "Fixed output, used if activateInterface=false"
  parameter Integer nSur(min=2) "Number of surfaces";
  parameter Integer nConExtWin(min=0)
    "number of exterior construction with window";
  parameter CFDSurfaceIdentifier surIde[nSur] "Surface identifiers";
  parameter Boolean haveShade
    "Set to true if at least one window in the room has a shade";
  parameter Boolean haveSensor
    "Flag, true if the model has at least one sensor";
  parameter String sensorName[:]
    "Names of sensors as declared in the CFD input file";
  parameter String portName[:]
    "Names of fluid ports as declared in the CFD input file";
  parameter Boolean verbose=false "Set to true for verbose output";
  parameter Modelica.Units.SI.Density rho_start "Density at initial state";

  CFDThread CFDThre = CFDThread()
   "Allocate memory for cosimulation variables via constructor and send stop command to FFD via destructor";

  Modelica.Blocks.Interfaces.RealInput u[nWri] "Inputs to CFD"
  discrete Modelica.Blocks.Interfaces.RealOutput y[nRea] "Outputs received from CFD"

  Real uInt[nWri] "Value of integral";
  discrete Real uIntPre[nWri] "Value of integral at previous sampling instance";
  discrete Real uWri[nWri] "Value to be sent to the CFD interface";

protected
  final parameter Integer nSen(min=0) = size(sensorName, 1)
    "Number of sensors that are connected to CFD output";
  final parameter Integer nPorts=size(portName, 1)
    "Number of fluid ports for the HVAC inlet and outlets";
  discrete Modelica.Units.SI.Time modTimRea(fixed=false)
    "Current model time received from CFD";

  discrete Integer retVal(start=0, fixed=true) "Return value from CFD";

  ///////////////////////////////////////////////////////////////////////////
  // Function that sends the parameters of the model from Modelica to CFD
  function sendParameters
    input String cfdFilNam "CFD input file name";
    input String[nSur] name "Surface names";
    input Modelica.Units.SI.Area[nSur] A "Surface areas";
    input Modelica.Units.SI.Angle[nSur] til "Surface tilt";
    input Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions[nSur] bouCon
      "Type of boundary condition";
    input Integer nPorts(min=0)
      "Number of fluid ports for the HVAC inlet and outlets";
    input String portName[nPorts]
      "Names of fluid ports as declared in the CFD input file";
    input Boolean haveSensor "Flag, true if the model has at least one sensor";
    input String sensorName[nSen]
      "Names of sensors as declared in the CFD input file";
    input Boolean haveShade "Flag, true if the windows have a shade";
    input Integer nSur(min=2) "Number of surfaces";
    input Integer nSen(min=0)
      "Number of sensors that are connected to CFD output";
    input Integer nConExtWin(min=0)
      "number of exterior construction with window";
    input Integer nXi
      "Number of independent species concentration of the inflowing medium";
    input Integer nC "Number of trace substances of the inflowing medium";
    input Modelica.Units.SI.Density rho_start "Density at initial state";
  protected
    Integer coSimFlag=0;
  algorithm
    for i in 1:nSur loop
      assert(A[i] > 0, "Surface must be bigger than zero.");
    end for;

    coSimFlag := cfdStartCosimulation(
        cfdFilNam,
        name,
        A,
        til,
        bouCon,
        nPorts,
        portName,
        haveSensor,
        sensorName,
        haveShade,
        nSur,
        nSen,
        nConExtWin,
        nXi,
        nC,
        rho_start);
    assert(coSimFlag < 0.5, "Could not start the cosimulation.");

  end sendParameters;

  ///////////////////////////////////////////////////////////////////////////
  // Function that exchanges data during the time stepping between
  // Modelica and CFD.
  function exchange
    input Integer flag "Communication flag to write to CFD";
    input Modelica.Units.SI.Time t
      "Current simulation time in seconds to write";
    input Modelica.Units.SI.Time dt(min=100*Modelica.Constants.eps)
      "Requested time step length";
    input Real[nU] u "Input for CFD";
    input Integer nU "Number of inputs for CFD";
    input Real[nY] yFixed "Fixed values (used for debugging only)";
    input Integer nY "Number of outputs from CFD";
    output Modelica.Units.SI.Time modTimRea
      "Current model time in seconds read from CFD";
    output Real[nY] y "Output computed by CFD";
    output Integer retVal
      "The exit value, which is negative if an error occurred";
  algorithm
    (modTimRea,y,retVal) := cfdExchangeData(
        flag,
        t,
        dt,
        u,
        nU,
        nY);
  end exchange;

  ///////////////////////////////////////////////////////////////////////////
  // Function that returns strings that are not unique.
  function returnNonUniqueStrings
    input Integer n(min=2) "Number entries";
    input Boolean ideNam[n - 1]
      "Flag that is set to true if the name is used more than once";
    input String names[n] "Names";
    output String s "String with non-unique names";
  algorithm
    s := "";
    for i in 1:n - 1 loop
      if ideNam[i] then
        s := s + "\n  '" + names[i] + "'";
      end if;
    end for;
  end returnNonUniqueStrings;

  // This function does not work because Dymola 2014 has problems with
  // handling strings in an algorithm section
  function assertStringsAreUnique
    input String descriptiveName
      "Descriptive name of what is tested, such as 'sensor' or 'ports'";
    input Integer n(min=2) "Number of strings";
    input String names[n] "Names";
  protected
    Boolean ideNam[n-1]
      "Flag that is set to true if the name is used more than once";

  algorithm
      // Loop over all names to verify that they are unique
    if n > 1 then
      for i in 1:n-1 loop
        for j in i+1:n loop
          ideNam[i] := Modelica.Utilities.Strings.isEqual(names[i], names[j]);
          if ideNam[i] then
            break;
          end if;
        end for; // j
      end for; // i

      assert( not Modelica.Math.BooleanVectors.anyTrue(ideNam),
      "For the CFD interface, all " + descriptiveName +
      " must have a name that is unique within each room.
      The following "
        + descriptiveName + " names are used more than once in the room model:" +
      returnNonUniqueStrings(n, ideNam, names));
    else
      ideNam :=fill(false, max(0, n - 1));
    end if;
  end assertStringsAreUnique;

initial equation
  // Diagnostics output
  if verbose then
   Modelica.Utilities.Streams.print(string="
CFDExchange has the following surfaces:");
    for i in 1:nSur loop
      Modelica.Utilities.Streams.print(string="
  name = " + surIde[i].name + "
  A    = " + String(surIde[i].A) + " [m2]
  tilt = " + String(surIde[i].til*180/Modelica.Constants.pi) + " [deg]");
  end for;

    if haveSensor then
      Modelica.Utilities.Streams.print(string="
CFDExchange has the following sensors:");
      for i in 1:nSen loop
        Modelica.Utilities.Streams.print(string="  " + sensorName[i]);
      end for;
    else
      Modelica.Utilities.Streams.print(string="CFDExchange has no sensors.");
    end if;
end if;

  // Assert that the surface, sensor and ports have a name,
  // and that that name is unique.
  // Otherwise, stop with an error.

  assertStringsAreUnique(descriptiveName="surface",
                         n=nSur,
                         names={surIde[i].name for i in 1:nSur});
  assertStringsAreUnique(descriptiveName="sensor",
                         n=nSen,
                         names=sensorName);
  assertStringsAreUnique(descriptiveName="ports",
                         n=nPorts,
                         names=portName);

  // Send parameters to the CFD interface
  sendParameters(
    cfdFilNam=cfdFilNam,
    name=surIde[:].name,
    A=surIde[:].A,
    til=surIde[:].til,
    bouCon=surIde[:].bouCon,
    haveSensor=haveSensor,
    portName=portName,
    sensorName=sensorName,
    haveShade=haveShade,
    nSur=nSur,
    nSen=nSen,
    nConExtWin=nConExtWin,
    nPorts=nPorts,
    nXi=nXi,
    nC=nC,
    rho_start=rho_start);

  // Assignment of parameters and start values
  uInt = zeros(nWri);
  uIntPre = zeros(nWri);
  for i in 1:nWri loop
    assert(flaWri[i] >= 0 and flaWri[i] <= 2,
      "Parameter flaWri out of range for " + String(i) + "-th component.");
  end for;

  // Assign uWri and y. This avoids a translation warning in Dymola
  // as otherwise, not all initial values are specified.
  // However, uWri and y are only used below in the body of the 'when'
  // block after they have been assigned.
  uWri = u;
  y=yFixed;

  modTimRea = time;
equation
  for i in 1:nWri loop
    der(uInt[i]) = if (flaWri[i] > 0) then u[i] else 0;
  end for;

  when sampleTrigger then
    // Compute value that will be sent to the CFD interface
    for i in 1:nWri loop
      if (flaWri[i] == 0) then
        uWri[i] =  pre(u[i]);
      elseif (flaWri[i] == 1) then
        if (time<startTime+0.1*samplePeriod) then
           uWri[i] =  pre(u[i]);
           // Set the correct initial data
        else
           uWri[i] =  (uInt[i] - pre(uIntPre[i]))/samplePeriod;
        // Average value over the sampling interval
        end if;
      else
        uWri[i] =  uInt[i] - pre(uIntPre[i]);
        // Integral over the sampling interval
      end if;
    end for;

    // Store current value of integral
    uIntPre = uInt;
  end when;

algorithm

  when sampleTrigger then
    // Exchange data
    if activateInterface then
      (modTimRea,y,retVal) := exchange(
        flag=0,
        t=time,
        dt=samplePeriod,
        u=uWri,
        nU=size(u, 1),
        yFixed=yFixed,
        nY=size(y, 1));
    else
      modTimRea := time;
      y := yFixed;
      retVal := 0;
    end if;

    // Check for valid return flags
    assert(retVal >= 0,
      "Obtained negative return value during data transfer with CFD.\n" +
      "   Aborting simulation. Check CFD log file.\n" +
      "   Received: retVal = " + String(retVal));
  end when;
end CFDExchange;


Exchange data between CFD and Modelica. This function calls a C function to conduct the data exchange between Modelica and CFD program during the coupled simulation.

within Buildings.ThermalZones.Detailed.BaseClasses;
function cfdExchangeData "Exchange data between CFD and Modelica"
  extends Modelica.Icons.Function;
  input Integer flag "Communication flag to CFD";
  input Modelica.Units.SI.Time t "Current Modelica simulation time to CFD";
  input Modelica.Units.SI.Time dt(min=100*Modelica.Constants.eps)
    "Requested synchronization time step size";
  input Real[nU] u "Input to CFD";
  input Integer nU "Number of inputs to CFD";
  input Integer nY "Number of outputs from CFD";
  output Modelica.Units.SI.Time modTimRea "Current model time from CFD";
  output Real[nY] y "Output computed by CFD";
  output Integer retVal "Return value for CFD simulation status";
external"C" retVal = cfdExchangeData(
    t,
    dt,
    u,
    nU,
    nY,
    modTimRea,
    y)

end cfdExchangeData;


Medium in the component

within Buildings.ThermalZones.Detailed.BaseClasses;
model CFDFluidInterface
 extends Buildings.BaseClasses.BaseIcon;

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

  // Assumptions
  parameter Modelica.Fluid.Types.Dynamics massDynamics=Modelica.Fluid.Types.Dynamics.DynamicFreeInitial
    "Formulation of mass balance"
  // Initialization
  parameter Medium.AbsolutePressure p_start = Medium.p_default
    "Start value of pressure"

  // Parameters for the model
  parameter Integer nPorts(min=0)=0 "Number of ports"
  parameter Modelica.Units.SI.Density rho_start
    "Density, used to compute fluid mass";
  parameter Modelica.Units.SI.Volume V "Volume";
  final parameter Modelica.Units.SI.Mass m_start=rho_start*V
    "Initial mass of air inside the room.";

  Modelica.Blocks.Interfaces.RealInput T_outflow[nPorts](
  each start=Medium.T_default,
  each nominal=300,
  each unit="K",
  each displayUnit="degC") "Temperature if m_flow < 0"

  Modelica.Blocks.Interfaces.RealInput Xi_outflow[nPorts*Medium.nXi](
  each min=0,
  each max=1,
  each unit="1") if Medium.nXi > 0 "Species concentration if m_flow < 0"

  Modelica.Blocks.Interfaces.RealInput C_outflow[nPorts*Medium.nC](
  each min=0) if Medium.nC > 0 "Trace substances if m_flow < 0"

  Modelica.Blocks.Interfaces.RealOutput p(
    start=p_start,
    min=80000,
    nominal=100000,
    max=120000,
    unit="Pa") "Room-averaged total pressure"

  Modelica.Blocks.Interfaces.RealOutput m_flow[nPorts](
  each quantity="MassFlowRate",
  each unit="kg/s") "Mass flow rates"

  Modelica.Blocks.Interfaces.RealOutput T_inflow[nPorts](
  each start=Medium.T_default,
  each min=200,
  each max=373.15,
  each nominal=300,
  each unit="K",
  each displayUnit="degC") "Temperature if m_flow >= 0"

  Modelica.Blocks.Interfaces.RealOutput Xi_inflow[nPorts*Medium.nXi](
  each min=0,
  each max=1,
  each unit="1")
  if Medium.nXi > 0 "Species concentration if m_flow >= 0"

  Modelica.Blocks.Interfaces.RealOutput C_inflow[nPorts*Medium.nC](
  each min=0)
  if Medium.nC > 0 "Trace substances if m_flow >= 0"

  // Fluid port
  Modelica.Fluid.Vessels.BaseClasses.VesselFluidPorts_b ports[nPorts](
      redeclare each final package Medium = Medium) "Fluid inlets and outlets"

protected
  Modelica.Blocks.Interfaces.RealOutput Xi_inflow_internal[max(nPorts, nPorts*Medium.nXi)](
  each min=0,
  each max=1,
  each unit="1") "Species concentration if m_flow >= 0";

  Modelica.Blocks.Interfaces.RealOutput C_inflow_internal[max(nPorts, nPorts*Medium.nC)](
  each min=0) "Trace substances if m_flow >= 0";

  Modelica.Blocks.Interfaces.RealInput Xi_outflow_internal[max(nPorts, nPorts*Medium.nXi)](
  each min=0,
  each max=1,
  each unit="1") "Species concentration if m_flow < 0";

  Modelica.Blocks.Interfaces.RealInput C_outflow_internal[max(nPorts, nPorts*Medium.nC)](
  each min=0) "Trace substances if m_flow < 0";

  Modelica.Units.SI.MassFlowRate[Medium.nXi] mbXi_flow
    "Substance mass flows across boundaries";
  Modelica.Units.SI.MassFlowRate ports_mXi_flow[nPorts,Medium.nXi];

initial equation
  //Disable it for shoebox model test
  //assert(nPorts >= 2, "The CFD model requires at least two fluid connections.");

  if massDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial then
    p = p_start;
  else
    if massDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial then
      der(p) = 0;
    end if;
  end if;

equation
  // Internal connectors
  if Medium.nXi > 0 then
    connect(Xi_inflow_internal,  Xi_inflow);
    connect(Xi_outflow_internal, Xi_outflow);
  else
    Xi_inflow_internal  = fill(0, nPorts);
    Xi_outflow_internal = fill(0, nPorts);
  end if;
  if Medium.nC > 0 then
    connect(C_inflow_internal,  C_inflow);
    connect(C_outflow_internal, C_outflow);
  else
    C_inflow_internal  = fill(0, nPorts);
    C_outflow_internal = fill(0, nPorts);
  end if;

  // Mass and pressure balance of bulk volume.
  // The assumption is that change in temperature does not change the pressure.
  // Otherwise, we would need to use the BaseProperties of the medium model.
  for i in 1:nPorts loop
    ports_mXi_flow[i,:] = ports[i].m_flow * actualStream(ports[i].Xi_outflow);
  end for;
  for i in 1:Medium.nXi loop
    mbXi_flow[i] = sum(ports_mXi_flow[:,i]);
  end for;
  if massDynamics == Modelica.Fluid.Types.Dynamics.SteadyState then
    0 = sum(ports.m_flow) + sum(mbXi_flow);
  else
    // For the change in pressure, we neglect the term sum(mbXi_flow)
    // as this term is small compared to sum(ports.m_flow) but it
    // introduces a nonlinear equation
    //    der(p) = p_start*(sum(ports.m_flow) + sum(mbXi_flow))/m_start;
    der(p) = p_start*(sum(ports.m_flow))/m_start;
  end if;

  // Connection of input signals to ports.
  // Connect pressures.
  for i in 1:nPorts loop
    ports[i].p = p;
  end for;
  // Connect enthalpy, mass fraction and trace substances.
  for i in 1:nPorts loop
    ports[i].h_outflow = Medium.specificEnthalpy_pTX(
       p=p,
       T=T_outflow[i],
       X=Xi_outflow_internal[(i-1)*Medium.nXi+1:i*Medium.nXi]);
    ports[i].Xi_outflow = Xi_outflow_internal[(i-1)*Medium.nXi+1:i*Medium.nXi];
    ports[i].C_outflow  = C_outflow_internal[ (i-1)*Medium.nC +1:i*Medium.nC];
  end for;

  // Connection of ports to output signals.
   for i in 1:nPorts loop
     m_flow[i] = ports[i].m_flow;
     T_inflow[i] = Medium.temperature(Medium.setState_phX(
       p = p,
       h = inStream(ports[i].h_outflow),
       X = inStream(ports[i].Xi_outflow)));

     for j in 1:Medium.nXi loop
       Xi_inflow_internal[(i-1)*Medium.nXi+j] = inStream(ports[i].Xi_outflow[j]);
     end for;

     for j in 1:Medium.nC loop
       C_inflow_internal[(i-1)*Medium.nC+j]    = inStream(ports[i].C_outflow[j]);
     end for;

   end for;
end CFDFluidInterface;


Start the coupled simulation with CFD.

within Buildings.ThermalZones.Detailed.BaseClasses;
function cfdStartCosimulation "Start the coupled simulation with CFD"
  extends Modelica.Icons.Function;
  input String cfdFilNam "CFD input file name";
  input String[nSur] name "Surface names";
  input Modelica.Units.SI.Area[nSur] A "Surface areas";
  input Modelica.Units.SI.Angle[nSur] til "Surface tilt";
  input Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions[nSur] bouCon
    "Type of boundary condition";
  input Integer nPorts(min=0)
    "Number of fluid ports for the HVAC inlet and outlets";
  input String portName[nPorts]
    "Names of fluid ports as declared in the CFD input file";
  input Boolean haveSensor "Flag, true if the model has at least one sensor";
  input String sensorName[nSen]
    "Names of sensors as declared in the CFD input file";
  input Boolean haveShade "Flag, true if the windows have a shade";
  input Integer nSur "Number of surfaces";
  input Integer nSen(min=0)
    "Number of sensors that are connected to CFD output";
  input Integer nConExtWin(min=0) "number of exterior construction with window";
  input Integer nXi(min=0) "Number of independent species";
  input Integer nC(min=0) "Number of trace substances";
  input Modelica.Units.SI.Density rho_start "Density at initial state";
  output Integer retVal
    "Return value of the function (0 indicates CFD successfully started.)";
external"C" retVal = cfdStartCosimulation(
    cfdFilNam,
    name,
    A,
    til,
    bouCon,
    nPorts,
    portName,
    haveSensor,
    sensorName,
    haveShade,
    nSur,
    nSen,
    nConExtWin,
    nXi,
    nC,
    rho_start)

end cfdStartCosimulation;


Boundary condition used in the CFD simulation. This model is used to connect temperatures and heat flow rates between the
block that communicates with the CFD program
and the heat port of the model that encapsulates the air heat and mass balance.

within Buildings.ThermalZones.Detailed.BaseClasses;
model CFDSurfaceInterface
 extends Buildings.BaseClasses.BaseIcon;
  parameter Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions bouCon
    "Boundary condition used in the CFD simulation"

  Modelica.Blocks.Interfaces.RealInput Q_flow_in
 if bouCon == Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions.Temperature
    "Surface heat flow rate, used for temperature boundary condition"

  Modelica.Blocks.Interfaces.RealOutput T_out
 if bouCon == Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions.Temperature
    "Surface temperature, used for temperature boundary condition"

  Modelica.Blocks.Interfaces.RealOutput Q_flow_out
 if bouCon == Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions.HeatFlowRate
    "Surface heat flow rate, used for temperature boundary condition"

  Modelica.Blocks.Interfaces.RealInput T_in
 if bouCon == Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions.HeatFlowRate
    "Surface temperature, used for temperature boundary condition"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port "Heat ports"

  // Internal connectors to change causality depending on the specifie
  // boundary condition
protected
  Modelica.Blocks.Interfaces.RealInput Q_flow_internal "Surface heat flow rate";

  Modelica.Blocks.Interfaces.RealOutput T_internal "Surface temperature";

equation
  connect(T_internal,      T_out);
  connect(Q_flow_internal, Q_flow_in);
  connect(T_internal,      T_in);
  connect(Q_flow_internal, Q_flow_out);
  T_internal      = port.T;
  Q_flow_internal = port.Q_flow;
end CFDSurfaceInterface;


Data records for construction data. Record that defines the number of constructions that are
used in the room model.

within Buildings.ThermalZones.Detailed.BaseClasses;
record ConstructionNumbers "Data records for construction data"
  extends Modelica.Icons.Record;

  ////////////////////////////////////////////////////////////////////////
  // Number of constructions and surface areas
  parameter Integer nConExt(min=0) "Number of exterior constructions"
  parameter Integer nConExtWin(min=0) "Number of window constructions"

  parameter Integer nConPar(min=0) "Number of partition constructions"

  parameter Integer nConBou(min=0)
    "Number of constructions that have their outside surface exposed to the boundary of this room"

  parameter Integer nSurBou(min=0)
    "Number of surface heat transfer models that connect to constructions that are modeled outside of this room"

  // Dimensions of components and connectors
  final parameter Integer NConExt(min=1) = max(1, nConExt)
    "Number of elements for exterior constructions"

  final parameter Integer NConExtWin(min=1)=max(1, nConExtWin)
    "Number of elements for exterior constructions with windows"

  final parameter Integer NConPar(min=1)=max(1, nConPar)
    "Number of elements for partition constructions"

  final parameter Integer NConBou(min=1)=max(1, nConBou)
    "Number of elements for constructions that have their outside surface exposed to the boundary of this room"

  final parameter Integer NSurBou(min=1)=max(1, nSurBou)
    "Number of elements for surface heat transfer models that connect to constructions that are modeled outside of this room"

  // Flags to conditionally remove components
  final parameter Boolean haveConExt = nConExt > 0
    "Flag to conditionally remove components"
  final parameter Boolean haveConExtWin = nConExtWin > 0
    "Flag to conditionally remove components"
  final parameter Boolean haveConPar = nConPar > 0
    "Flag to conditionally remove components"
  final parameter Boolean haveConBou = nConBou > 0
    "Flag to conditionally remove components"
  final parameter Boolean haveSurBou = nSurBou > 0
    "Flag to conditionally remove components"

end ConstructionNumbers;


Model for convection and radiation bounary condition of exterior constructions. This model computes the boundary conditions for the outside-facing surface of
opaque constructions.

within Buildings.ThermalZones.Detailed.BaseClasses;
model ExteriorBoundaryConditions
  "Model for convection and radiation bounary condition of exterior constructions"
  parameter Integer nCon(min=1) "Number of exterior constructions"

  parameter Boolean linearizeRadiation
    "Set to true to linearize emissive power";

  replaceable parameter ParameterConstruction conPar[nCon] constrainedby
    ParameterConstruction "Records for construction"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a opa_a[nCon]
    "Heat port at surface a of opaque construction"
  parameter Buildings.HeatTransfer.Types.ExteriorConvection conMod=
  Buildings.HeatTransfer.Types.ExteriorConvection.TemperatureWind
    "Convective heat transfer model for opaque part of the constructions"
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hFixed=10.0
    "Constant convection coefficient for opaque part of the constructions"

  // The convection coefficients are not final to allow a user to individually
  // assign them.
  // We reassign the tilt since a roof has been declared in the room model as the
  // ceiling (of the room)
  HeatTransfer.Convection.Exterior conOpa[nCon](
    A=AOpa,
    final til=Modelica.Constants.pi*ones(nCon) .- conPar[:].til,
    final azi=conPar[:].azi,
    each conMod=conMod,
    each hFixed=hFixed) "Convection model for opaque part of the wall"

  SkyRadiationExchange skyRadExc(
    final n=nCon,
    final A=AOpa,
    final absIR=conPar[:].layers.absIR_a,
    vieFacSky={(Modelica.Constants.pi - conPar[i].til)./Modelica.Constants.pi for i in 1:nCon})
    "Infrared radiative heat exchange with sky"
  BoundaryConditions.WeatherData.Bus weaBus

  BoundaryConditions.SolarIrradiation.DirectTiltedSurface HDirTil[
            nCon](
    final til=conPar[:].til,
    final azi=conPar[:].azi) "Direct solar irradiation on the surface"
  BoundaryConditions.SolarIrradiation.DiffusePerez HDifTil[nCon](
    final til=conPar[:].til,
    final azi=conPar[:].azi) "Diffuse solar irradiation"
  Modelica.Blocks.Math.Add HTotConExt[nCon](
    final k1=conPar[:].layers.absSol_a .* AOpa,
    final k2=conPar[:].layers.absSol_a .* AOpa) "Total solar irradiation"
  Buildings.HeatTransfer.Sources.PrescribedHeatFlow solHeaGaiConExt[nCon]
    "Total solar heat gain of the surface"

protected
  parameter Modelica.Units.SI.Area AOpa[nCon]=conPar[:].A
    "Area of opaque construction";

  Buildings.HeatTransfer.Sources.PrescribedTemperature TAirConExt[
    nCon] "Outside air temperature for exterior constructions"
  Modelica.Blocks.Routing.Replicator repConExt(nout=nCon) "Signal replicator"

  Modelica.Blocks.Routing.Replicator repConExt1(
                                               nout=nCon) "Signal replicator"
  Modelica.Blocks.Routing.Replicator repConExt2(
                                               nout=nCon) "Signal replicator"

equation
  connect(conOpa.solid, opa_a)
  connect(skyRadExc.port, opa_a)

  connect(TAirConExt.port, conOpa.fluid)
  connect(repConExt.y, TAirConExt.T)
  connect(repConExt.u, weaBus.TDryBul)
  connect(skyRadExc.TOut, weaBus.TDryBul)
  connect(skyRadExc.TBlaSky, weaBus.TBlaSky)
  for i in 1:nCon loop
  connect(weaBus, HDirTil[i].weaBus)
  connect(HDifTil[i].weaBus, weaBus)
   end for;
  connect(HTotConExt.y, solHeaGaiConExt.Q_flow)
  connect(solHeaGaiConExt.port, opa_a)
  connect(HDirTil.H, HTotConExt.u1)
  connect(HDifTil.H, HTotConExt.u2)
  connect(repConExt2.u, weaBus.winDir)
  connect(repConExt1.u, weaBus.winSpe)
  connect(repConExt1.y, conOpa.v)
  connect(repConExt2.y, conOpa.dir)
end ExteriorBoundaryConditions;


Model for exterior boundary conditions for constructions with a window. This model computes the boundary conditions for the outside-facing surface of
opaque constructions and of windows.

within Buildings.ThermalZones.Detailed.BaseClasses;
model ExteriorBoundaryConditionsWithWindow
  "Model for exterior boundary conditions for constructions with a window"
  extends Buildings.ThermalZones.Detailed.BaseClasses.ExteriorBoundaryConditions(
   final AOpa=conPar[:].AOpa,
   redeclare Buildings.ThermalZones.Detailed.BaseClasses.ParameterConstructionWithWindow conPar);

  final parameter Modelica.Units.SI.Area AWin[nCon]=conPar[:].hWin .* conPar[:].wWin
    "Window area"

  final parameter Boolean haveExteriorShade[nCon] = conPar[:].glaSys.haveExteriorShade
    "Set to true if window has exterior shade (at surface a)"
  final parameter Boolean haveInteriorShade[nCon] = conPar[:].glaSys.haveInteriorShade
    "Set to true if window has interior shade (at surface b)"

  final parameter Boolean haveShade=
    Modelica.Math.BooleanVectors.anyTrue(haveExteriorShade) or
    Modelica.Math.BooleanVectors.anyTrue(haveInteriorShade)
    "Set to true if window system has a shade"

  final parameter Boolean haveOverhangOrSideFins=
    Modelica.Math.BooleanVectors.anyTrue(conPar.haveOverhangOrSideFins)
    "Flag, true if the room has at least one window with either an overhang or side fins";

  Buildings.HeatTransfer.Windows.FixedShade sha[nCon](
    final conPar=conPar,
    azi=conPar.azi)
    if haveOverhangOrSideFins "Shade due to overhang or side fins"

  Modelica.Blocks.Interfaces.RealInput uSha[nCon](
    each min=0,
    each max=1) if haveShade
    "Control signal for the shading device, 0: unshaded; 1: fully shaded"

  Modelica.Blocks.Interfaces.RealInput QAbsSolSha_flow[nCon](
    each final unit="W",
    each quantity="Power") "Solar radiation absorbed by shade"

  HeatTransfer.Windows.ExteriorHeatTransfer conExtWin[nCon](
    final A=conPar[:].AWin,
    final fFra=conPar[:].fFra,
    each final linearizeRadiation = linearizeRadiation,
    final vieFacSky={(Modelica.Constants.pi - conPar[i].til) ./ Modelica.Constants.pi for i in 1:nCon},
    final absIRSha_air=conPar[:].glaSys.shade.absIR_a,
    final absIRSha_glass=conPar[:].glaSys.shade.absIR_b,
    final tauIRSha_air=conPar[:].glaSys.shade.tauIR_a,
    final tauIRSha_glass=conPar[:].glaSys.shade.tauIR_b,
    final haveExteriorShade=haveExteriorShade,
    final haveInteriorShade=haveInteriorShade)
    "Exterior convection of the window"

  SkyRadiationExchange skyRadExcWin(
    final n=nCon,
    final absIR=conPar[:].glaSys.absIRFra,
    vieFacSky={(Modelica.Constants.pi - conPar[i].til) ./ Modelica.Constants.pi for i in
            1:nCon},
    final A=conPar[:].AWin .* conPar[:].fFra)
    "Infrared radiative heat exchange between window frame and sky"
  HeatTransfer.Interfaces.RadiosityOutflow JOutUns[nCon]
    "Outgoing radiosity that connects to unshaded part of glass at exterior side"
  HeatTransfer.Interfaces.RadiosityInflow JInUns[nCon]
    "Incoming radiosity that connects to unshaded part of glass at exterior side"
  HeatTransfer.Interfaces.RadiosityOutflow JOutSha[nCon]
    if haveShade
    "Outgoing radiosity that connects to shaded part of glass at exterior side"
  HeatTransfer.Interfaces.RadiosityInflow JInSha[nCon]
    if haveShade
    "Incoming radiosity that connects to shaded part of glass at exterior side"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a glaUns[nCon]
    "Heat port at unshaded glass of exterior-facing surface"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a glaSha[nCon]
    if haveShade "Heat port at shaded glass of exterior-facing surface"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a fra[nCon](
    each T(
      nominal=300,
      start=283.15))
    "Heat port at frame of exterior-facing surface"
  Modelica.Blocks.Math.Add HTotConExtWinFra[nCon](
     final k1=conPar[:].fFra .* conPar[:].glaSys.absSolFra .* conPar[:].AWin,
     final k2=conPar[:].fFra .* conPar[:].glaSys.absSolFra .* conPar[:].AWin)
    "Total solar irradiation on window frame"
  Buildings.HeatTransfer.Sources.PrescribedHeatFlow solHeaGaiConWin[nCon]
    "Total solar heat gain of the window frame"
  Modelica.Blocks.Interfaces.RealOutput HDir[nCon](
     each final quantity="RadiantEnergyFluenceRate",
     each final unit="W/m2") "Direct solar irradition on tilted surface"
  Modelica.Blocks.Interfaces.RealOutput HDif[nCon](
     each final quantity="RadiantEnergyFluenceRate",
     each final unit="W/m2") "Diffuse solar irradiation on tilted surface"
  Modelica.Blocks.Interfaces.RealOutput inc[nCon](
    each final quantity="Angle",
    each final unit="rad",
    each displayUnit="deg") "Incidence angle"

protected
  Buildings.HeatTransfer.Sources.PrescribedTemperature TAirConExtWin[
    nCon] "Outside air temperature for window constructions"
  Modelica.Blocks.Routing.Replicator repConExtWin(final nout=nCon)
    "Signal replicator"
  Modelica.Blocks.Routing.Replicator repConExtWinVWin(final nout=nCon)
    "Signal replicator"
  Modelica.Blocks.Routing.Replicator repConExtWinTSkyBla(final nout=nCon)
    "Signal replicator"
equation
  connect(uSha, conExtWin.uSha)
  connect(JInUns,conExtWin. JInUns)
  connect(conExtWin.JOutUns,JOutUns)
  connect(conExtWin.glaUns,glaUns)
  connect(conExtWin.glaSha,glaSha)
  connect(conExtWin.JOutSha,JOutSha)
  connect(conExtWin.JInSha,JInSha)
  connect(conExtWin.frame,fra)
  connect(TAirConExtWin.port,conExtWin. air)
  connect(TAirConExtWin.T,repConExtWin. y)
  connect(repConExtWin.u, weaBus.TDryBul)
  connect(repConExtWinVWin.y,conExtWin. vWin)
  connect(repConExtWinVWin.u, weaBus.winSpe)
  connect(HTotConExtWinFra.y, solHeaGaiConWin.Q_flow)
  connect(solHeaGaiConWin.port, fra)
  connect(HDifTil.H, HDif)
  connect(HDirTil.inc, inc)
  connect(HTotConExtWinFra.u2, HDifTil.H)
  connect(skyRadExcWin.TOut, weaBus.TDryBul)
  connect(skyRadExcWin.TBlaSky, weaBus.TBlaSky)
  connect(skyRadExcWin.port, fra)
  connect(repConExtWin.y, conExtWin.TOut)
  connect(repConExtWinTSkyBla.y, conExtWin.TBlaSky)
  connect(repConExtWinTSkyBla.u, weaBus.TBlaSky)
  for i in 1:nCon loop
    connect(sha[i].weaBus, weaBus)
  end for;

  connect(HDirTil.inc, sha.incAng)
  // OpenModelica does not remove the connect statement if conExtWin.QSolAbs_flow
  // is removed.
  if haveShade then
    connect(QAbsSolSha_flow, conExtWin.QSolAbs_flow)
  end if;

  connect(sha.HDirTilUns, HDirTil.H)

  if haveOverhangOrSideFins then
    connect(sha.HDirTil, HTotConExtWinFra.u1)
    connect(sha.HDirTil, HDir)
  else
    connect(HDirTil.H, HTotConExtWinFra.u1)
    connect(HDirTil.H, HDir)
  end if;
end ExteriorBoundaryConditionsWithWindow;


Model to convert internal heat gain signals. This model computes the radiant, convective sensible and latent heat flow rate.
Input into this model are these three components in units of [W/m2].
The inputs need to be positive quantities if heat or moisture is added
to the room.
The outputs are

within Buildings.ThermalZones.Detailed.BaseClasses;
model HeatGain "Model to convert internal heat gain signals"
  extends Buildings.BaseClasses.BaseIcon;

  parameter Modelica.Units.SI.Area AFlo "Floor area";

  Modelica.Blocks.Interfaces.RealInput qGai_flow[3]
    "Radiant, convective sensible and latent heat input into room (positive if heat gain)"
  Modelica.Blocks.Interfaces.RealOutput QRad_flow(unit="W")
    "Radiant heat input into room (positive if heat gain)"
  Modelica.Blocks.Interfaces.RealOutput QCon_flow(unit="W")
    "Convective sensible heat input into room (positive if heat gain)"
  Modelica.Blocks.Interfaces.RealOutput QLat_flow(unit="W")
    "Latent heat input into room (positive if heat gain)"
equation
  {QRad_flow, QCon_flow, QLat_flow} = AFlo .* qGai_flow;
end HeatGain;


Infrared radiation heat exchange between the room facing surfaces. This model computes the infrared radiative heat transfer between the interior
surfaces of a room. Each opaque surface emits radiation according to

within Buildings.ThermalZones.Detailed.BaseClasses;
model InfraredRadiationExchange
  "Infrared radiation heat exchange between the room facing surfaces"
  extends Buildings.ThermalZones.Detailed.BaseClasses.PartialSurfaceInterfaceRadiative;

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

  parameter Boolean linearizeRadiation
    "Set to true to linearize emissive power";
  parameter Boolean sampleModel=false
    "Set to true to time-sample the model, which can give shorter simulation time if there is already time sampling in the system model"

  HeatTransfer.Interfaces.RadiosityInflow JInConExtWin[NConExtWin]
 if haveConExtWin
    "Incoming radiosity that connects to non-frame part of the window"
  HeatTransfer.Interfaces.RadiosityOutflow JOutConExtWin[NConExtWin]
    "Outgoing radiosity that connects to non-frame part of the window"
protected
  constant Real T30(unit="K3") = 293.15^3 "Nominal temperature";
  constant Real T40(unit="K4") = 293.15^4 "Nominal temperature";

  final parameter Integer NOpa=NConExt + 2*NConExtWin + 2*NConPar + NConBou +
      NSurBou "Number of opaque surfaces, including the window frame";
  final parameter Integer nOpa=nConExt + 2*nConExtWin + 2*nConPar + nConBou +
      nSurBou "Number of opaque surfaces, including the window frame";
  final parameter Integer NWin=NConExtWin "Number of window surfaces";
  final parameter Integer nWin=nConExtWin "Number of window surfaces";
  final parameter Integer NTot=NOpa + NWin "Total number of surfaces";
  final parameter Integer nTot=nOpa + nWin "Total number of surfaces";
  final parameter Real epsOpa[nOpa](
    each min=0,
    each max=1,
    each fixed=false) "Absorptivity of opaque surfaces";
  final parameter Real rhoOpa[nOpa](
    each min=0,
    each max=1,
    each fixed=false) "Reflectivity of opaque surfaces";
  final parameter Modelica.Units.SI.Area AOpa[nOpa](each fixed=false)
    "Surface area of opaque surfaces";
  final parameter Modelica.Units.SI.Area A[nTot](each fixed=false)
    "Surface areas";
  final parameter Real kOpa[nOpa](each unit="W/K4", each fixed=false)
    "Product sigma*epsilon*A for opaque surfaces";
  final parameter Real kOpaInv[nOpa](each unit="K4/W", each fixed=false)
    "Inverse of kOpa, used to avoid having to use a safe division";
  final parameter Real F[nTot, nTot](
    each min=0,
    each max=1,
    each fixed=false) "View factor from surface i to j";

  parameter Modelica.Units.SI.Time t0(fixed=false) "First sample time instant";

  Buildings.HeatTransfer.Interfaces.RadiosityInflow JInConExtWin_internal[
    NConExtWin](start=AConExtWinGla*0.8*Modelica.Constants.sigma*293.15^4,
      each fixed=sampleModel and nConExtWin > 0)
    "Incoming radiosity that connects to non-frame part of the window";

  Modelica.Units.SI.HeatFlowRate J[nTot](
    each max=0,
    start=-A .* 0.8*Modelica.Constants.sigma*293.15^4,
    fixed={sampleModel and (i <= nOpa or i > nOpa + nWin) for i in 1:nTot},
    each nominal=-10*0.8*Modelica.Constants.sigma*293.15^4)
    "Radiosity leaving the surface";

  Modelica.Units.SI.HeatFlowRate G[nTot](
    each min=0,
    start=A .* 0.8*Modelica.Constants.sigma*293.15^4,
    each nominal=10*0.8*Modelica.Constants.sigma*293.15^4)
    "Radiosity entering the surface";

  Modelica.Units.SI.Temperature TOpa[nOpa](each start=293.15, each nominal=
        293.15) "Temperature of opaque surfaces";
  Real T4Opa[nOpa](
    each unit="K4",
    each start=T40,
    each nominal=293.15^4) "Forth power of temperature of opaque surfaces";
  Modelica.Units.SI.HeatFlowRate Q_flow[nTot](each start=0, each fixed=
        sampleModel) "Heat flow rate at surfaces";
  parameter Modelica.Units.SI.Temperature T0=293.15
    "Temperature used to linearize radiative heat transfer";
  final parameter Real T03(
    min=0,
    unit="K3") = T0^3 "3rd power of temperature T0";
  // Modelica.Units.SI.HeatFlowRate sumEBal(start=0, fixed=sampleModel)
  //   "Sum of energy balance, should be zero";
initial equation
  assert(homotopyInitialization, "In " + getInstanceName() +
    ": The constant homotopyInitialization has been modified from its default value. This constant will be removed in future releases.",
    level = AssertionLevel.warning);

  // The next loops build the array epsOpa, AOpa and kOpa that simplify
  // the model equations.
  // These arrays store the values of the constructios in the following order
  // [x[1:NConExt] x[1:NConPar] x[1: NConPar] x[1: NConBou] x[1: NSurBou] x[1: NConExtWin] x[1: NConExtWin]]
  // where x is epsOpa, AOpa or kOpa.
  // The last two entries are for the opaque wall that contains a window, and for the window frame.
  for i in 1:nConExt loop
    epsOpa[i] = epsConExt[i];
    AOpa[i] = AConExt[i];
    kOpa[i] = Modelica.Constants.sigma*epsConExt[i]*AOpa[i];
  end for;
  for i in 1:nConPar loop
    epsOpa[i + nConExt] = epsConPar_a[i];
    AOpa[i + nConExt] = AConPar[i];
    kOpa[i + nConExt] = Modelica.Constants.sigma*epsConPar_a[i]*AOpa[i +
      nConExt];
    epsOpa[i + nConExt + nConPar] = epsConPar_b[i];
    AOpa[i + nConExt + nConPar] = AConPar[i];
    kOpa[i + nConExt + nConPar] = Modelica.Constants.sigma*epsConPar_b[i]*AOpa[
      i + nConExt + nConPar];
  end for;
  for i in 1:nConBou loop
    epsOpa[i + nConExt + 2*nConPar] = epsConBou[i];
    AOpa[i + nConExt + 2*nConPar] = AConBou[i];
    kOpa[i + nConExt + 2*nConPar] = Modelica.Constants.sigma*epsConBou[i]*AOpa[
      i + nConExt + 2*nConPar];
  end for;
  for i in 1:nSurBou loop
    epsOpa[i + nConExt + 2*nConPar + nConBou] = epsSurBou[i];
    AOpa[i + nConExt + 2*nConPar + nConBou] = ASurBou[i];
    kOpa[i + nConExt + 2*nConPar + nConBou] = Modelica.Constants.sigma*
      epsSurBou[i]*AOpa[i + nConExt + 2*nConPar + nConBou];
  end for;
  for i in 1:nConExtWin loop
    // Opaque part of construction that has a window embedded
    epsOpa[i + nConExt + 2*nConPar + nConBou + nSurBou] = epsConExtWinOpa[i];
    AOpa[i + nConExt + 2*nConPar + nConBou + nSurBou] = AConExtWinOpa[i];
    kOpa[i + nConExt + 2*nConPar + nConBou + nSurBou] = Modelica.Constants.sigma
      *epsConExtWinOpa[i]*AOpa[i + nConExt + 2*nConPar + nConBou + nSurBou];
    // Window frame
    epsOpa[i + nConExt + 2*nConPar + nConBou + nSurBou + nConExtWin] =
      epsConExtWinFra[i];
    AOpa[i + nConExt + 2*nConPar + nConBou + nSurBou + nConExtWin] =
      AConExtWinFra[i];
    kOpa[i + nConExt + 2*nConPar + nConBou + nSurBou + nConExtWin] = Modelica.Constants.sigma
      *epsConExtWinFra[i]*AOpa[i + nConExt + 2*nConPar + nConBou + nSurBou +
      nConExtWin];
  end for;
  // Vector with all surface areas.
  // The next loops build the array A that simplifies
  // the model equations.
  // These array stores the values of the constructios in the following order
  // [AOpa[1:nConExt] AOpa[1:nConPar] AOpa[1: nConPar] AOpa[1: nConBou] AOpa[1: nSurBou]
  //  AOpa[1: nConExtWin] AOpa[1: nConExtWin] AGla[1: nConExtWin]]
  // since nWin=nConExtWin.
  for i in 1:nOpa loop
    A[i] = AOpa[i];
  end for;
  for i in 1:nWin loop
    A[i + nOpa] = AConExtWinGla[i];
  end for;
  // Reflectivity for opaque surfaces
  rhoOpa = 1 .- epsOpa;
  // View factors from surface i to surface j
  for i in 1:nTot loop
    for j in 1:nTot loop
      F[i, j] = A[j]/sum((A[k]) for k in 1:nTot);
    end for;
  end for;
  for i in 1:nOpa loop
    kOpaInv[i] = 1/kOpa[i];
  end for;
  // Test whether the view factors add up to one, or the sum is zero in case there
  // is only one construction
  for i in 1:nTot loop
    assert((abs(1 - sum(F[i, j] for j in 1:nTot))) < 1E-10,
      "Program error: Sum 1 of view factors is " + String(sum(F[i, j] for j in
      1:nTot)));
  end for;

  t0 = time;
  ////////////////////////////////////////////////////////////////////
equation
  // Conditional connector
  connect(JInConExtWin, JInConExtWin_internal);
  if not haveConExtWin then
    JInConExtWin_internal = fill(0, NConExtWin);
  end if;
  // Assign temperature of opaque surfaces
  for i in 1:nConExt loop
    TOpa[i] = conExt[i].T;
  end for;
  for i in 1:nConPar loop
    TOpa[i + nConExt] = conPar_a[i].T;
    TOpa[i + nConExt + nConPar] = conPar_b[i].T;
  end for;
  for i in 1:nConBou loop
    TOpa[i + nConExt + 2*nConPar] = conBou[i].T;
  end for;
  for i in 1:nSurBou loop
    TOpa[i + nConExt + 2*nConPar + nConBou] = conSurBou[i].T;
  end for;
  for i in 1:nConExtWin loop
    TOpa[i + nConExt + 2*nConPar + nConBou + nSurBou] = conExtWin[i].T;
    TOpa[i + nConExt + 2*nConPar + nConBou + nConExtWin + nSurBou] =
      conExtWinFra[i].T;
  end for;
  // Incoming radiosity at each surface
  // is equal to the negative of the outgoing radiosity of
  // all other surfaces times the view factor
  if sampleModel then
    // experimental mode to sample the model which can give shorter
    // simulation time if there is already a sampling in the system model
    when sample(t0, 2*60) then
      G = -transpose(F)*pre(J);
      // Net heat exchange
      Q_flow = -pre(J) - G;
      // Outgoing radiosity
      // Sum of energy balance
      // Remove sumEBal and assert statement for final release
      // sumEBal = sum(conExt.Q_flow) + sum(conPar_a.Q_flow) + sum(conPar_b.Q_flow)
      //    + sum(conBou.Q_flow) + sum(conSurBou.Q_flow) + sum(conExtWin.Q_flow)
      //    + sum(conExtWinFra.Q_flow) + (sum(JInConExtWin_internal) - sum(
      //   JOutConExtWin));
    end when;
  else
    G = -transpose(F)*J;
    // Net heat exchange
    Q_flow = -J - G;
    // Outgoing radiosity
    // Sum of energy balance
    // Remove sumEBal and assert statement for final release
    // sumEBal = sum(conExt.Q_flow) + sum(conPar_a.Q_flow) + sum(conPar_b.Q_flow)
    //    + sum(conBou.Q_flow) + sum(conSurBou.Q_flow) + sum(conExtWin.Q_flow) +
    //   sum(conExtWinFra.Q_flow) + (sum(JInConExtWin_internal) - sum(
    //   JOutConExtWin));
    // assert(abs(sumEBal) < 1E-1, "Program error: Energy is not conserved in InfraredRadiationExchange.
    // Sum of all energy is " + String(sumEBal));
  end if;

  // Opaque surfaces.
  // If kOpa[j]=absIR[j]*A[j] < 1E-28, then A < 1E-20 and the surface is
  // from a dummy construction. In this situation, we set T40=293.15^4 to
  // avoid a singularity.
  for j in 1:nOpa loop
    //   T4Opa[j] = if (kOpa[j] > 1E-28) then (Q_flow[j]-epsOpa[j] * G[j])/kOpa[j] else T40;
    T4Opa[j] = (-J[j] - rhoOpa[j]*G[j])*kOpaInv[j];
  end for;
  // 4th power of temperature
  if linearizeRadiation then
    TOpa = (T4Opa .+ 3*T40)/(4*T30);
    // Based on T4 = 4*T30*T-3*T40
  else
    if homotopyInitialization then
      TOpa = homotopy(actual=Buildings.Utilities.Math.Functions.powerLinearized(
        x=T4Opa,
        x0=243.15^4,
        n=0.25), simplified=(T4Opa .+ 3*T40)/(4*T30));
    else
      TOpa = Buildings.Utilities.Math.Functions.powerLinearized(
        x=T4Opa,
        x0=243.15^4,
        n=0.25);
    end if;
  end if;
  // Assign radiosity that comes from window
  // and that leaves window.
  // J < 0 because it leaves the surface
  // G > 0 because it strikes the surface
  for j in 1:nWin loop
    J[j + nOpa] = -JInConExtWin_internal[j];
    G[j + nOpa] = +JOutConExtWin[j];
  end for;

  // Assign heat exchange to connectors
  for i in 1:nConExt loop
    Q_flow[i] = conExt[i].Q_flow;
  end for;
  if nConExt == 0 then
    conExt[1].T = T0;
  end if;
  for i in 1:nConPar loop
    Q_flow[i + nConExt] = conPar_a[i].Q_flow;
    Q_flow[i + nConExt + nConPar] = conPar_b[i].Q_flow;
  end for;
  if nConPar == 0 then
    conPar_a[1].T = T0;
    conPar_b[1].T = T0;
  end if;
  for i in 1:nConBou loop
    Q_flow[i + nConExt + 2*nConPar] = conBou[i].Q_flow;
  end for;
  if nConBou == 0 then
    conBou[1].T = T0;
  end if;
  for i in 1:nSurBou loop
    Q_flow[i + nConExt + 2*nConPar + nConBou] = conSurBou[i].Q_flow;
  end for;
  if nSurBou == 0 then
    conSurBou[1].T = T0;
  end if;
  for i in 1:nConExtWin loop
    Q_flow[i + nConExt + 2*nConPar + nConBou + nSurBou] = conExtWin[i].Q_flow;
    Q_flow[i + nConExt + 2*nConPar + nConBou + nSurBou + nConExtWin] =
      conExtWinFra[i].Q_flow;
  end for;
  if nConExtWin == 0 then
    conExtWin[1].T = T0;
    conExtWinFra[1].T = T0;
    JOutConExtWin[1] = 0;
  end if;
end InfraredRadiationExchange;


Infrared radiative heat gain distribution between the room facing surfaces.

within Buildings.ThermalZones.Detailed.BaseClasses;
model InfraredRadiationGainDistribution
  "Infrared radiative heat gain distribution between the room facing surfaces"
  extends Buildings.ThermalZones.Detailed.BaseClasses.PartialSurfaceInterfaceRadiative;
  parameter Boolean haveShade "Set to true if a shade is present";

  Modelica.Blocks.Interfaces.RealInput uSha[NConExtWin](each min=0, each max=1)
    if haveShade
    "Control signal for the shading device (removed if no shade is present)"

  Modelica.Blocks.Interfaces.RealInput Q_flow
    "Radiative heat input into room (positive if heat gain)"
  Buildings.HeatTransfer.Interfaces.RadiosityOutflow[NConExtWin] JOutConExtWin
    "Outgoing radiosity that connects to shaded and unshaded part of glass"
protected
  Real fraConExt[NConExt] = AEpsConExt*sumAEpsInv
    "Fraction of infrared radiant heat gain absorbed by exterior constructions";
  Real fraConExtWinOpa[NConExtWin] = AEpsConExtWinOpa*sumAEpsInv
    "Fraction of infrared radiant heat gain absorbed by opaque part of exterior constructions that have a window";
  Real fraConExtWinGla[NConExtWin] = (AEpsConExtWinSha + AEpsConExtWinUns)*sumAEpsInv
    "Fraction of infrared radiant heat gain absorbed by opaque part of glass constructions that have a window";
  Real fraConExtWinFra[NConExtWin] = AEpsConExtWinFra*sumAEpsInv
    "Fraction of infrared radiant heat gain absorbed by window frame of exterior constructions that have a window";

  Real fraConPar_a[NConPar] = AEpsConPar_a*sumAEpsInv
    "Fraction of infrared radiant heat gain absorbed by partition constructions surface a";
  Real fraConPar_b[NConPar] = AEpsConPar_b*sumAEpsInv
    "Fraction of infrared radiant heat gain absorbed by partition constructions surface b";
  Real fraConBou[NConBou] = AEpsConBou*sumAEpsInv
    "Fraction of infrared radiant heat gain absorbed by constructions with exterior boundary conditions exposed to outside of room model";
  Real fraSurBou[NSurBou] = AEpsSurBou*sumAEpsInv
    "Fraction of infrared radiant heat gain absorbed by surface models of constructions that are modeled outside of this room";

 parameter Real AEpsConExt[NConExt] = {AConExt[i]*epsConExt[i] for i in 1:NConExt}
    "Absorptivity times area of exterior constructions";
 parameter Real AEpsConExtWinOpa[NConExtWin] = {AConExtWinOpa[i]*epsConExtWinOpa[i] for i in 1:NConExtWin}
    "Absorptivity times area of opaque part of exterior constructions that contain a window";
 Real AEpsConExtWinUns[NConExtWin] = {shaSig[i].yCom * AConExtWinGla[i]*epsConExtWinUns[i]
     for i in 1:NConExtWin}
    "Absorptivity times area of unshaded window constructions";
 Real AEpsConExtWinSha[NConExtWin] = {shaSig[i].y    * AConExtWinGla[i]*epsConExtWinSha[i]
    for i in 1:NConExtWin}
    "Absorptivity times area of shaded window constructions";
 parameter Real AEpsConExtWinFra[NConExtWin] = {AConExtWinFra[i]*epsConExtWinFra[i] for i in 1:NConExtWin}
    "Absorptivity times area of window frame";
 parameter Real AEpsConPar_a[NConPar] = {AConPar[i]*epsConPar_a[i] for i in 1:NConPar}
    "Absorptivity times area of partition constructions surface a";
 parameter Real AEpsConPar_b[NConPar] = {AConPar[i]*epsConPar_b[i] for i in 1:NConPar}
    "Absorptivity times area of partition constructions surface b";
 parameter Real AEpsConBou[NConBou] = {AConBou[i]*epsConBou[i] for i in 1:NConBou}
    "Absorptivity times area of constructions with exterior boundary conditions exposed to outside of room model";
 parameter Real AEpsSurBou[NSurBou] = {ASurBou[i]*epsSurBou[i] for i in 1:NSurBou}
    "Absorptivity times area of surface models of constructions that are modeled outside of this room";

 parameter Real sumAEpsNoWin(fixed=false)
    "Sum of absorptivity times area of all constructions except for windows";
 Real sumAEpsInv
    "Inverse of sum of absorptivity times area of all constructions including windows";

 Buildings.HeatTransfer.Windows.BaseClasses.ShadingSignal shaSig[NConExtWin](
      each final haveShade=haveShade)
    "Block to constrain the shading control signal to be strictly within (0, 1) if a shade is present";
initial equation
  sumAEpsNoWin = sum(AEpsConExt)+sum(AEpsConExtWinOpa)+sum(AEpsConExtWinFra)
                +sum(AEpsConPar_a)+sum(AEpsConPar_b)+sum(AEpsConBou)+sum(AEpsSurBou);
equation
  connect(uSha, shaSig.u);

  sumAEpsInv   = 1.0/(sumAEpsNoWin + sum(AEpsConExtWinUns) + sum(AEpsConExtWinSha));

  // Infrared radiative heat flow
  // If a construction is not present, we assign the temperature of the connector to 20 degC.
  if haveConExt then
    conExt.Q_flow    = -fraConExt*Q_flow;
  else
    conExt[1].T = 293.15;
  end if;

  if haveConExtWin then
    conExtWin.Q_flow = -fraConExtWinOpa*Q_flow;
  else
    conExtWin[1].T = 293.15;
  end if;

  if haveConPar then
    conPar_a.Q_flow  = -fraConPar_a*Q_flow;
    conPar_b.Q_flow  = -fraConPar_b*Q_flow;
  else
    conPar_a[1].T = 293.15;
    conPar_b[1].T = 293.15;
  end if;

  if haveConBou then
    conBou.Q_flow    = -fraConBou*Q_flow;
  else
    conBou[1].T = 293.15;
  end if;

  if haveSurBou then
    conSurBou.Q_flow    = -fraSurBou*Q_flow;
  else
    conSurBou[1].T = 293.15;
  end if;

  // This model makes the simplification that the shade, the glass and the frame have
  // the same absorptivity in the infrared region
  JOutConExtWin        = +fraConExtWinGla*Q_flow;
  if haveConExtWin then
     conExtWinFra.Q_flow  = -fraConExtWinFra*Q_flow;
  else
     conExtWinFra[1].T = 293.15;
  end if;

  // Check for conservation of energy
  assert(abs(1 - sum(fraConExt) - sum(fraConExtWinOpa)- sum(fraConExtWinGla) - sum(fraConExtWinFra)
           - sum(fraConPar_a) - sum(fraConPar_b)
           - sum(fraConBou) - sum(fraSurBou))  < 1E-5,
           "Programming error: Radiation balance is wrong. Check equations.");
end InfraredRadiationGainDistribution;


within Buildings.ThermalZones.Detailed.BaseClasses;
model MixedAirHeatMassBalance
  "Heat and mass balance of the air, assuming completely mixed air"
  extends Buildings.ThermalZones.Detailed.BaseClasses.PartialAirHeatMassBalance;
  extends Buildings.Fluid.Interfaces.LumpedVolumeDeclarations;

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

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal(min=0)
    "Nominal mass flow rate"

  parameter Buildings.HeatTransfer.Types.InteriorConvection conMod
    "Convective heat transfer model for opaque constructions"
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hFixed
    "Constant convection coefficient for opaque constructions"

  parameter Boolean use_C_flow
    "Set to true to enable input connector for trace substance"

  Modelica.Blocks.Interfaces.RealInput C_flow[Medium.nC] if use_C_flow
    "Trace substance mass flow rate added to the room air. Enable if use_C_flow = true"

  // Mixing volume
  Fluid.MixingVolumes.MixingVolumeMoistAir vol(
    redeclare package Medium = Medium,
    final energyDynamics=energyDynamics,
    final massDynamics=massDynamics,
    final V=V,
    final p_start=p_start,
    final T_start=T_start,
    final X_start=X_start,
    final C_start=C_start,
    final C_nominal=C_nominal,
    final mSenFac=mSenFac,
    final m_flow_nominal = m_flow_nominal,
    final prescribedHeatFlowRate = true,
    final nPorts=nPorts,
    m_flow_small=1E-4*abs(m_flow_nominal),
    allowFlowReversal=true,
    final use_C_flow=use_C_flow)  "Room air volume"

  // Convection models
  HeatTransfer.Convection.Interior convConExt[NConExt](
    final A=AConExt,
    final til = datConExt.til,
    each conMod=conMod,
    each hFixed=hFixed,
    each final homotopyInitialization=homotopyInitialization)
    if haveConExt "Convective heat transfer"

  HeatTransfer.Convection.Interior convConExtWin[NConExtWin](
    final A=AConExtWinOpa,
    final til = datConExtWin.til,
    each conMod=conMod,
    each hFixed=hFixed,
    each final homotopyInitialization=homotopyInitialization)
    if haveConExtWin "Convective heat transfer"

  HeatTransfer.Windows.InteriorHeatTransferConvective convConWin[NConExtWin](
    final fFra=datConExtWin.fFra,
    final haveExteriorShade={datConExtWin[i].glaSys.haveExteriorShade for i in 1:NConExtWin},
    final haveInteriorShade={datConExtWin[i].glaSys.haveInteriorShade for i in 1:NConExtWin},
    final til=datConExtWin.til,
    each conMod=conMod,
    each hFixed=hFixed,
    final A=AConExtWinGla + AConExtWinFra)
    if haveConExtWin "Model for convective heat transfer at window"

  HeatTransfer.Convection.Interior convConPar_a[nConPar](
    final A=AConPar,
    final til=Modelica.Constants.pi .- datConPar.til,
    each conMod=conMod,
    each hFixed=hFixed,
    each final homotopyInitialization=homotopyInitialization)
    if haveConPar "Convective heat transfer"

  HeatTransfer.Convection.Interior convConPar_b[nConPar](
    final A=AConPar,
    final til = datConPar.til,
    each conMod=conMod,
    each hFixed=hFixed,
    each final homotopyInitialization=homotopyInitialization)
    if haveConPar "Convective heat transfer"

  HeatTransfer.Convection.Interior convConBou[nConBou](
    final A=AConBou,
    final til = datConBou.til,
    each conMod=conMod,
    each hFixed=hFixed,
    each final homotopyInitialization=homotopyInitialization)
    if haveConBou "Convective heat transfer"

  HeatTransfer.Convection.Interior convSurBou[nSurBou](
    final A=ASurBou,
    final til = surBou.til,
    each conMod=conMod,
    each hFixed=hFixed,
    each final homotopyInitialization=homotopyInitialization)
    if haveSurBou "Convective heat transfer"

  // Latent and convective sensible heat gains
protected
  constant Modelica.Units.SI.SpecificEnergy h_fg=
      Buildings.Media.Air.enthalpyOfCondensingGas(273.15 + 37)
    "Latent heat of water vapor";

  Modelica.Blocks.Math.Gain mWat_flow(
    final k(unit="kg/J")=1/h_fg,
    u(final unit="W"),
    y(final unit="kg/s")) "Water flow rate due to latent heat gain"

  HeatTransfer.Sources.PrescribedHeatFlow conQCon_flow
    "Converter for convective heat flow rate"

  HeatTransfer.Sources.PrescribedHeatFlow conQLat_flow
    "Converter for latent heat flow rate"

  // Thermal collectors
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector theConConExt(final m=nConExt)
    if haveConExt
    "Thermal collector to convert from vector to scalar connector"
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector theConConExtWin(final m=nConExtWin)
    if haveConExtWin
    "Thermal collector to convert from vector to scalar connector"
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector theConConWin(final m=nConExtWin)
    if haveConExtWin
    "Thermal collector to convert from vector to scalar connector"

  Modelica.Thermal.HeatTransfer.Components.ThermalCollector theConConPar_a(final m=nConPar)
    if haveConPar
    "Thermal collector to convert from vector to scalar connector"
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector theConConPar_b(final m=nConPar)
    if haveConPar
    "Thermal collector to convert from vector to scalar connector"
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector theConConBou(final m=nConBou)
    if haveConBou
    "Thermal collector to convert from vector to scalar connector"
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector theConSurBou(final m=nSurBou)
    if haveSurBou
    "Thermal collector to convert from vector to scalar connector"
initial equation
  assert(homotopyInitialization, "In " + getInstanceName() +
    ": The constant homotopyInitialization has been modified from its default value. This constant will be removed in future releases.",
    level = AssertionLevel.warning);

equation
  connect(convConPar_a.fluid,theConConPar_a.port_a)
  connect(convConPar_b.fluid,theConConPar_b.port_a)
  connect(convConBou.fluid,theConConBou.port_a)
  connect(convSurBou.fluid,theConSurBou.port_a)
  connect(theConConPar_a.port_b,vol.heatPort)
  connect(theConConPar_b.port_b,vol.heatPort)
  connect(theConConBou.port_b,vol.heatPort)
  connect(theConSurBou.port_b,vol.heatPort)
  connect(conExtWinFra,convConWin.frame)
  connect(convConExt.solid, conExt)
  connect(convConExt.fluid,theConConExt.port_a)
  connect(theConConExt.port_b,vol.heatPort)
  connect(theConConExtWin.port_b,vol.heatPort)
  connect(convConExtWin.fluid,theConConExtWin.port_a)
  connect(convConExtWin.solid, conExtWin)
  connect(theConConWin.port_b,vol.heatPort)
  connect(convConWin.air,theConConWin.port_a)
  connect(convConWin.glaSha, glaSha)
  connect(convConWin.glaUns, glaUns)
  connect(convConPar_a.solid, conPar_a)
  connect(convConPar_b.solid, conPar_b)
  connect(convConBou.solid, conBou)
  connect(convSurBou.solid, conSurBou)
  for i in 1:nPorts loop
    connect(vol.ports[i], ports[i])
  end for;
  connect(heaPorAir, vol.heatPort)
  connect(uSha, convConWin.uSha)
  connect(convConWin.QRadAbs_flow, QRadAbs_flow)
  connect(convConWin.TSha, TSha)
  connect(conQCon_flow.port, vol.heatPort)
  connect(QCon_flow, conQCon_flow.Q_flow)
  connect(QLat_flow, mWat_flow.u)
  connect(mWat_flow.y, vol.mWat_flow)
  connect(conQLat_flow.Q_flow, QLat_flow)
  connect(conQLat_flow.port, vol.heatPort)
  connect(vol.C_flow, C_flow)
end MixedAirHeatMassBalance;


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


within Buildings.ThermalZones.Detailed.BaseClasses;
partial model PartialAirHeatMassBalance
  "Partial model for heat and mass balance of the air"
  extends Buildings.ThermalZones.Detailed.BaseClasses.ConstructionRecords;

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"
  // Port definitions
  parameter Integer nPorts=0 "Number of ports"

  parameter Boolean haveShade
    "Set to true if at least one window has an interior or exterior shade";

  parameter Modelica.Units.SI.Volume V "Volume";

  // Input/output signals
  Modelica.Blocks.Interfaces.RealInput uSha[NConExtWin] if haveShade
    "Input connector, used to scale the surface area to take into account an operable shading device, 0: unshaded; 1: fully shaded"
  Modelica.Blocks.Interfaces.RealInput QRadAbs_flow[NConExtWin](
  each final unit="W")
  if haveShade
    "Total net radiation that is absorbed by the shade (positive if absorbed)"

  Modelica.Blocks.Interfaces.RealInput QCon_flow
    "Convective sensible heat gains of the room"
  Modelica.Blocks.Interfaces.RealInput QLat_flow
    "Latent heat gains for the room"

  Modelica.Blocks.Interfaces.RealOutput TSha[NConExtWin](
   each final unit="K",
   each final quantity="ThermodynamicTemperature")
   if haveShade "Shade temperature"

  // Fluid port
  Modelica.Fluid.Vessels.BaseClasses.VesselFluidPorts_b ports[nPorts](
      redeclare each final package Medium = Medium) "Fluid inlets and outlets"

  // Heat ports
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heaPorAir
    "Heat port to air volume"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conExt[NConExt]
  if haveConExt
    "Heat port that connects to room-side surface of exterior constructions"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conExtWin[NConExtWin]
  if haveConExtWin
    "Heat port that connects to room-side surface of exterior constructions that contain a window"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a glaUns[NConExtWin]
  if haveConExtWin
    "Heat port that connects to room-side surface of unshaded glass"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a glaSha[NConExtWin]
    if haveShade "Heat port that connects to room-side surface of shaded glass"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conExtWinFra[NConExtWin]
  if haveConExtWin
    "Heat port that connects to room-side surface of window frame"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conPar_a[NConPar]
  if haveConPar
    "Heat port that connects to room-side surface a of partition constructions"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conPar_b[NConPar]
  if haveConPar
    "Heat port that connects to room-side surface b of partition constructions"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conBou[NConBou]
  if haveConBou
    "Heat port that connects to room-side surface of constructions that expose their other surface to the outside"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conSurBou[NSurBou]
  if haveSurBou
    "Heat port to surfaces of models that compute the heat conduction outside of this room"

  // Surface areas
protected
  final parameter Modelica.Units.SI.Area AConExt[NConExt]=datConExt.A
    "Areas of exterior constructions";
  final parameter Modelica.Units.SI.Area AConExtWinOpa[NConExtWin]=datConExtWin.AOpa
    "Opaque areas of exterior construction that have a window";
  final parameter Modelica.Units.SI.Area AConExtWinGla[NConExtWin]=(1 .-
      datConExtWin.fFra) .* datConExtWin.AWin
    "Glass areas of exterior construction that have a window";
  final parameter Modelica.Units.SI.Area AConExtWinFra[NConExtWin]=datConExtWin.fFra
       .* datConExtWin.AWin
    "Frame areas of exterior construction that have a window";
  final parameter Modelica.Units.SI.Area AConPar[NConPar]=datConPar.A
    "Areas of partition constructions";
  final parameter Modelica.Units.SI.Area AConBou[NConBou]=datConBou.A
    "Areas of constructions with exterior boundary conditions exposed to outside of room model";
  final parameter Modelica.Units.SI.Area ASurBou[NSurBou]=surBou.A
    "Area of surface models of constructions that are modeled outside of this room";
end PartialAirHeatMassBalance;


Partial record for constructions. This data record is used to set the parameters of constructions that do not have a window.

within Buildings.ThermalZones.Detailed.BaseClasses;
record PartialParameterConstruction "Partial record for constructions"
  extends Modelica.Icons.Record;

  parameter String name = ""
    "Surface name. Optional for MixedAir, required for FFD.";

  parameter Buildings.HeatTransfer.Data.OpaqueConstructions.Generic
    layers "Material properties of opaque construction"

  parameter Modelica.Units.SI.Angle til "Surface tilt";
  parameter Modelica.Units.SI.Angle azi "Surface azimuth";
  final parameter Boolean is_floor=til > 2.74889125 and til < 3.53428875
    "Flag, true if construction is a floor"
  final parameter Boolean is_ceiling=til > -0.392699 and til < 0.392699
    "Flag, true if construction is a floor"
//  final parameter Integer nLay(min=1, fixed=true) = size(layers.material, 1)
//    "Number of layers";
//  final parameter Integer nSta[:](each min=1)={layers.material[i].nSta for i in 1:size(layers.material, 1)}
//    "Number of states"
  parameter Boolean steadyStateInitial=false
    "=true initializes dT(0)/dt=0, false initializes T(0) at fixed temperature using T_a_start and T_b_start"
  parameter Modelica.Units.SI.Temperature T_a_start=293.15
    "Initial temperature at port_a, used if steadyStateInitial = false"
  parameter Modelica.Units.SI.Temperature T_b_start=293.15
    "Initial temperature at port_b, used if steadyStateInitial = false"

  parameter Boolean stateAtSurface_a=true
    "=true, a state will be at the surface a"
  parameter Boolean stateAtSurface_b=true
    "=true, a state will be at the surface b"

  parameter Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions boundaryCondition=
    Buildings.ThermalZones.Detailed.Types.CFDBoundaryConditions.Temperature
    "Boundary condition used in the CFD simulation"

end PartialParameterConstruction;


Partial model that is used for infrared radiation balance. This partial model is used as a base class for models that need to exchange
heat with room-facing surfaces. It defines parameters for the surface areas.
The model is used as a base class to implement the convective model, and the various
radiation models.

within Buildings.ThermalZones.Detailed.BaseClasses;
partial model PartialSurfaceInterface
  "Partial model that is used for infrared radiation balance"
  extends Buildings.ThermalZones.Detailed.BaseClasses.ConstructionRecords;

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conExt[NConExt]
    "Heat port that connects to room-side surface of exterior constructions"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conExtWin[NConExtWin]
    "Heat port that connects to room-side surface of exterior constructions that contain a window"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conExtWinFra[NConExtWin]
    "Heat port that connects to room-side surface of window frame"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conPar_a[NConPar]
    "Heat port that connects to room-side surface a of partition constructions"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conPar_b[NConPar]
    "Heat port that connects to room-side surface b of partition constructions"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conBou[NConBou]
    "Heat port that connects to room-side surface of constructions that expose their other surface to the outside"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a conSurBou[NSurBou]
    "Heat port to surfaces of models that compute the heat conduction outside of this room"
protected
  final parameter String instanceName = getInstanceName() "Name of the instance";
  final parameter Modelica.Units.SI.Area AConExt[NConExt]=datConExt.A
    "Areas of exterior constructions";
  final parameter Modelica.Units.SI.Area AConExtWinOpa[NConExtWin]=datConExtWin.AOpa
    "Opaque areas of exterior construction that have a window";
  final parameter Modelica.Units.SI.Area AConExtWinGla[NConExtWin]=(1 .-
      datConExtWin.fFra) .* datConExtWin.AWin
    "Glass areas of exterior construction that have a window";
  final parameter Modelica.Units.SI.Area AConExtWinFra[NConExtWin]=datConExtWin.fFra
       .* datConExtWin.AWin
    "Frame areas of exterior construction that have a window";
  final parameter Modelica.Units.SI.Area AConPar[NConPar]=datConPar.A
    "Areas of partition constructions";
  final parameter Modelica.Units.SI.Area AConBou[NConBou]=datConBou.A
    "Areas of constructions with exterior boundary conditions exposed to outside of room model";
  final parameter Modelica.Units.SI.Area ASurBou[NSurBou]=surBou.A
    "Area of surface models of constructions that are modeled outside of this room";

protected
  function checkSurfaceAreas
    input Integer n "Number of surfaces";
    input Modelica.Units.SI.Area A[:] "Surface areas";
    input String name "Name of the surface data record, used in error message";
  algorithm
    if n == 0 then
      assert(Modelica.Math.Vectors.norm(v=A, p=1) < 1E-10,
      "Error in declaration of room model: Construction record '" +
      name +
      "' has the following areas: " +
      Modelica.Math.Vectors.toString(A) +
      "However, the room model is declared as having zero surfaces.
Check the parameters of the room model.");
    else
      for i in 1:n loop
        assert(A[i] > 0, "Error in declaration of room model: Construction record '" + name + "' has the following areas: " +
      Modelica.Math.Vectors.toString(A) +
      "However, the surface areas must be bigger than zero.
Check the parameters of the room model.");
      end for;
    end if;
  end checkSurfaceAreas;
initial algorithm
  checkSurfaceAreas(nConExt,    datConExt.A,       instanceName + ".datConExt");
  checkSurfaceAreas(nConExtWin, datConExtWin.AWin, instanceName + ".datConExtWin");
  checkSurfaceAreas(nConPar,    datConPar.A,       instanceName + ".datConPar");
  checkSurfaceAreas(nConBou,    datConBou.A,       instanceName + ".datConBou");
  checkSurfaceAreas(nSurBou,    surBou.A,          instanceName + ".surBou");
end PartialSurfaceInterface;


Partial model that is used for infrared radiation balance. This partial model is used as a base class for models that need to exchange
heat with room-facing surfaces by radiation. It declares parameters that are
needed for the radiative balance.

within Buildings.ThermalZones.Detailed.BaseClasses;
partial model PartialSurfaceInterfaceRadiative
  "Partial model that is used for infrared radiation balance"
  extends Buildings.ThermalZones.Detailed.BaseClasses.PartialSurfaceInterface;

protected
  parameter Modelica.Units.SI.Emissivity epsConExt[NConExt]=datConExt.layers.absIR_b
    "Absorptivity of exterior constructions";
  parameter Modelica.Units.SI.Emissivity epsConExtWinOpa[NConExtWin]=
      datConExtWin.layers.absIR_b
    "Absorptivity of opaque part of exterior constructions that contain a window";
  parameter Modelica.Units.SI.Emissivity epsConExtWinUns[NConExtWin]={(
      datConExtWin[i].glaSys.glass[size(datConExtWin[i].glaSys.glass, 1)].absIR_b)
      for i in 1:NConExtWin}
    "Absorptivity of unshaded part of window constructions";
  parameter Modelica.Units.SI.Emissivity epsConExtWinSha[NConExtWin]=
      datConExtWin.glaSys.shade.absIR_a
    "Absorptivity of shaded part of window constructions";
  parameter Modelica.Units.SI.Emissivity epsConExtWinFra[NConExtWin]=
      datConExtWin.glaSys.absIRFra "Absorptivity of window frame";
  parameter Modelica.Units.SI.Emissivity epsConPar_a[NConPar]=datConPar.layers.absIR_a
    "Absorptivity of partition constructions surface a";
  parameter Modelica.Units.SI.Emissivity epsConPar_b[NConPar]=datConPar.layers.absIR_b
    "Absorptivity of partition constructions surface b";
  parameter Modelica.Units.SI.Emissivity epsConBou[NConBou]=datConBou.layers.absIR_b
    "Absorptivity of constructions with exterior boundary conditions exposed to outside of room model";
  parameter Modelica.Units.SI.Emissivity epsSurBou[NSurBou]=surBou.absIR
    "Absorptivity of surface models of constructions that are modeled outside of this room";
end PartialSurfaceInterfaceRadiative;


Model to connect between signals and heat port for radiative gains of the room.

within Buildings.ThermalZones.Detailed.BaseClasses;
model RadiationAdapter
  "Model to connect between signals and heat port for radiative gains of the room"
  extends Buildings.BaseClasses.BaseIcon;

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a rad
    "Port for radiative heat gain and radiation temperature"
public
  Modelica.Blocks.Interfaces.RealInput TRad "Radiation temperature of room"
  Modelica.Blocks.Interfaces.RealOutput QRad_flow "Radiative heat gain"
equation
  QRad_flow = rad.Q_flow;
  rad.T = TRad;
end RadiationAdapter;