Test model with sampleModel = true. This model is identical to

Buildings.ThermalZones.Detailed.Validation.TestConditionalConstructions.OnlySurfaceBoundary,
except that it sets sampleModel=true.

within Buildings.ThermalZones.Detailed.Validation.TestConditionalConstructions.SampledModel;
model OnlySurfaceBoundary "Test model with sampleModel = true"
  extends Buildings.ThermalZones.Detailed.Validation.TestConditionalConstructions.OnlySurfaceBoundary(
    roo(sampleModel=true));
end OnlySurfaceBoundary;


within Buildings.ThermalZones.Detailed.Validation.TestConditionalConstructions;
package SampledModel "Validation for conditional constructions but with sampled models"
  extends Modelica.Icons.ExamplesPackage;
end SampledModel;


Block to write to an EnergyPlus actuator. Block that writes to an EMS actuator object in EnergyPlus.

within Buildings.ThermalZones.EnergyPlus_9_6_0;
block Actuator
  "Block to write to an EnergyPlus actuator"
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.PartialEnergyPlusObject;
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.Synchronize.ObjectSynchronizer;
  parameter String variableName
    "Actuated component unique name in the EnergyPlus idf file";
  parameter String componentType
    "Actuated component type";
  parameter String controlType
    "Actuated component control type";
  parameter Buildings.ThermalZones.EnergyPlus_9_6_0.Types.Units unit
    "Unit of variable as used in Modelica"
  Modelica.Blocks.Interfaces.RealInput u
    "Continuous input signal to be written to EnergyPlus"
  Modelica.Blocks.Interfaces.RealOutput y
    "Value written to EnergyPlus (use for direct dependency of Actuators and Schedules)"

protected
  constant Integer nParOut=0
    "Number of parameter values retrieved from EnergyPlus";
  constant Integer nInp=1
    "Number of inputs";
  constant Integer nOut=0
    "Number of outputs";
  constant Integer nDer=0
    "Number of derivatives";
  constant Integer nY=nOut+nDer+1
    "Size of output vector of exchange function";
  parameter Integer nObj(
    fixed=false,
    start=0)
    "Total number of Spawn objects in building";
  final parameter String unitString=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.getUnitAsString(unit)
    "Unit as a string";
  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject adapter=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject(
    objectType=3,
    startTime=startTime,
    modelicaNameBuilding=modelicaNameBuilding,
    modelicaInstanceName=modelicaInstanceName,
    spawnExe=spawnExe,
    idfVersion=idfVersion,
    idfName=idfName,
    epwName=epwName,
    runPeriod=runPeriod,
    relativeSurfaceTolerance=relativeSurfaceTolerance,
    epName=variableName,
    usePrecompiledFMU=usePrecompiledFMU,
    fmuName=fmuName,
    buildingsRootFileLocation=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.buildingsRootFileLocation,
    logLevel=logLevel,
    printUnit=false,
    jsonName="emsActuators",
    jsonKeysValues="        \"variableName\": \""+variableName+"\",
        \"componentType\": \""+componentType+"\",
        \"controlType\": \""+controlType+"\",
        \"unit\": \""+unitString+"\",
        \"fmiName\": \""+variableName+"_"+componentType+"\"",
    parOutNames=fill("",nParOut),
    parOutUnits=fill("",nParOut),
    nParOut=nParOut,
    inpNames={componentType},
    inpUnits={unitString},
    nInp=nInp,
    outNames=fill("",nOut),
    outUnits=fill("",nOut),
    nOut=nOut,
    derivatives_structure=fill(fill(nDer,2),nDer),
    nDer=nDer,
    derivatives_delta=fill(0,nDer))
    "Class to communicate with EnergyPlus";
  Real yEP[nY]
    "Output of exchange function";

initial equation
  assert(
    not usePrecompiledFMU,
    "Use of pre-compiled FMU is not supported for block Actuator.");
  nObj=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.initialize(
    adapter=adapter,
    isSynchronized=building.isSynchronized);

equation
  yEP=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.exchange(
    adapter=adapter,
    nY=nY,
    u={u,round(time,1E-3)},
    dummy=nObj);
  y=yEP[1];
  nObj=synBui.synchronize.done;
end Actuator;


Model that declares a building to which EnergyPlus objects belong to. Model that declares building-level specifications for Spawn of EnergyPlus.

within Buildings.ThermalZones.EnergyPlus_9_6_0;
model Building
  "Model that declares a building to which EnergyPlus objects belong to"
  extends Modelica.Blocks.Icons.Block;

  constant String spawnExe="spawn-0.5.0-ab07bde9bb"
      "Name of the spawn executable, without extension, such as spawn-0.5.0-ab07bde9bbaaa"

  constant String idfVersion = "9_6_0"
    "IDF version with underscore, used for error report";

  final constant String modelicaNameBuilding=getInstanceName()
    "Name of this instance"

  constant Boolean usePrecompiledFMU=false
    "Set to true to use pre-compiled FMU with name specified by fmuName";
  constant String fmuName=""
    "Specify if a pre-compiled FMU should be used instead of EnergyPlus (for development)";

  parameter String idfName
    "Name of the IDF file"

  parameter String epwName
    "Name of the EPW file"

  parameter String weaName
    "Name of the weather file, in .mos format and with .mos extension"

  parameter Buildings.ThermalZones.EnergyPlus_9_6_0.Types.LogLevels logLevel=Buildings.ThermalZones.EnergyPlus_9_6_0.Types.LogLevels.Warning
    "Log level of EnergyPlus output"

  parameter Boolean computeWetBulbTemperature=true
    "If true, then this model computes the wet bulb temperature"

  parameter Buildings.ThermalZones.EnergyPlus_9_6_0.Data.RunPeriod runPeriod
      "EnergyPlus RunPeriod configuration"
  parameter Boolean setInitialRadiativeHeatGainToZero = true
    "If true, then the radiative heat gain sent from Modelica to EnergyPlus is zero during the model initialization"

  parameter Real relativeSurfaceTolerance(min=1E-12) = 1E-6
    "Relative tolerance of surface temperature calculations"

  parameter Boolean printUnits=true
    "Set to true to print units of OutputVariable instances to log file"

  BoundaryConditions.WeatherData.Bus weaBus
    "Weather data bus"
  BaseClasses.Synchronize.SynchronizeConnector synchronize
    "Connector that synchronizes all Spawn objects of this buildings"
  Real isSynchronized
    "Flag used to synchronize Spawn objects"

protected
  Real synchronization_done=synchronize.done
    "Intermediate variable as acausal connectors cannot be used in the algorithm section";
/*
  final parameter String idf=Modelica.Utilities.Files.loadResource(idfName)
      "idf file to be loaded into the FMU";
  final parameter String epw=Modelica.Utilities.Files.loadResource(epwName)
      "idf file to be loaded into the FMU";
*/

  BoundaryConditions.WeatherData.ReaderTMY3 weaDat(
    final filNam=weaName,
    final computeWetBulbTemperature=computeWetBulbTemperature)
    "Weather data reader"

equation
  synchronize.do=0;
  connect(weaDat.weaBus,weaBus)

algorithm
  isSynchronized := synchronization_done;
end Building;


Model to exchange heat of an opaque construction with EnergyPlus. Model that interfaces with the EnergyPlus object BuildingSurface:Detailed.
It sets in EnergyPlus the temperature of the front and back surface
to the values obtained from Modelica through the heat ports
of this model,
and imposes the heat flow rate obtained from EnergyPlus at the heat ports
of this model.

within Buildings.ThermalZones.EnergyPlus_9_6_0;
model OpaqueConstruction
  "Model to exchange heat of an opaque construction with EnergyPlus"
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.PartialEnergyPlusObject;
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.Synchronize.ObjectSynchronizer;
  parameter String surfaceName
    "Surface unique name in the EnergyPlus idf file";
  final parameter Modelica.Units.SI.Area A(final fixed=false, min=1E-10)
    "Surface area";
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heaPorFro
    "Heat port for front surface"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_b heaPorBac
    "Heat port for back surface"
  Modelica.Units.SI.HeatFlux qFro_flow
    "Heat flow rate at front surface per unit area";
  Modelica.Units.SI.HeatFlux qBac_flow
    "Heat flow rate at front surface per unit area";

protected
  constant Integer nParOut=1
    "Number of parameter values retrieved from EnergyPlus";
  constant Integer nInp=2
    "Number of inputs";
  constant Integer nOut=2
    "Number of outputs";
  constant Integer nDer=0
    "Number of derivatives";
  constant Integer nY=nOut+nDer+1
    "Size of output vector of exchange function";
  parameter Integer nObj(
    fixed=false,
    start=0)
    "Total number of Spawn objects in building";
  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject adapter=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject(
    objectType=6,
    startTime=startTime,
    modelicaNameBuilding=modelicaNameBuilding,
    modelicaInstanceName=modelicaInstanceName,
    spawnExe=spawnExe,
    idfVersion=idfVersion,
    idfName=idfName,
    epwName=epwName,
    runPeriod=runPeriod,
    relativeSurfaceTolerance=relativeSurfaceTolerance,
    epName=surfaceName,
    usePrecompiledFMU=usePrecompiledFMU,
    fmuName=fmuName,
    buildingsRootFileLocation=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.buildingsRootFileLocation,
    logLevel=logLevel,
    printUnit=false,
    jsonName="buildingSurfaceDetailed",
    jsonKeysValues="        \"name\": \""+surfaceName+"\"",
    parOutNames={"A"},
    parOutUnits={"m2"},
    nParOut=nParOut,
    inpNames={"TFront","TBack"},
    inpUnits={"K","K"},
    nInp=nInp,
    outNames={"QFront_flow","QBack_flow"},
    outUnits={"W","W"},
    nOut=nOut,
    derivatives_structure=fill(fill(nDer,2),nDer),
    nDer=nDer,
    derivatives_delta=fill(0,nDer))
    "Class to communicate with EnergyPlus";
  //////////
  // The derivative structure was:
  //  derivatives_structure={{1,1},{2,2}},
  //  nDer=nDer,
  //  derivatives_delta={0.01,0.01}
  // This has been removed due to numerical noise,
  // see https://github.com/lbl-srg/modelica-buildings/issues/2358#issuecomment-819578850
  //////////
  Real yEP[nY]
    "Output of exchange function";
  Modelica.Units.SI.Time tNext(start=startTime, fixed=true)
    "Next sampling time";
  discrete Modelica.Units.SI.Time tLast(fixed=true, start=startTime)
    "Last time of data exchange";
  discrete Modelica.Units.SI.Time dtLast
    "Time step since the last synchronization";
  discrete Modelica.Units.SI.Temperature TFroLast
    "Front surface temperature at last sampling";
  discrete Modelica.Units.SI.Temperature TBacLast
    "Back surface temperature at last sampling";
  discrete Modelica.Units.SI.HeatFlowRate QFroLast_flow(fixed=false, start=0)
    "Surface heat flow rate at front if T = TLast";
  discrete Modelica.Units.SI.HeatFlowRate QBacLast_flow(fixed=false, start=0)
    "Surface heat flow rate at back if T = TLast";
//   discrete Real dQFro_flow_dT(
//     final unit="W/K")
//     "Derivative dQFroCon_flow / dT";
//   discrete Real dQBac_flow_dT(
//     final unit="W/K")
//     "Derivative dQBacCon_flow / dT";

initial equation
  assert(
    not usePrecompiledFMU,
    "Use of pre-compiled FMU is not supported for ZoneSurface.");
  nObj=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.initialize(
    adapter=adapter,
    isSynchronized=building.isSynchronized);
  {A}=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.getParameters(
    adapter=adapter,
    nParOut=nParOut,
    isSynchronized=nObj);
  assert(
    A > 0,
    "Surface area must not be zero.");
equation
  // Make sure the heat ports are connected.
  // These statements must be in the equation section. Otherwise,
  // Dymola 2021 does trigger an error during the symbolic processing
  // rather than these assertions if the heat port is not connected.
  assert(cardinality(heaPorFro) > 0,
    "In " + getInstanceName() +": The heat port heaPorFro must be connected to another heat port.");
  assert(cardinality(heaPorBac) > 0,
    "In " + getInstanceName() +": The heat port heaPorBac must be connected to another heat port.");

  when {initial(),time >= pre(tNext)} then
    // Initialization of output variables.
    TFroLast=heaPorFro.T;
    TBacLast=heaPorBac.T;
    dtLast=time-pre(tLast);
    yEP=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.exchange(
      adapter=adapter,
      nY=nY,
      u={heaPorFro.T,heaPorBac.T,round(time,1E-3)},
      dummy=A);
    QFroLast_flow=-yEP[1];
    QBacLast_flow=-yEP[2];
    //dQFro_flow_dT=-yEP[3];
    //dQBac_flow_dT=-yEP[4];
    tNext=yEP[3];
    tLast=time;
  end when;
  heaPorFro.Q_flow=QFroLast_flow; //+(heaPorFro.T-TFroLast)*dQFro_flow_dT;
  heaPorBac.Q_flow=QBacLast_flow; //+(heaPorBac.T-TBacLast)*dQBac_flow_dT;
  qFro_flow=heaPorFro.Q_flow/A;
  qBac_flow=heaPorBac.Q_flow/A;
  nObj=synBui.synchronize.done;
end OpaqueConstruction;


Block to read an EnergyPlus output variable. Block that retrieves an output variable from EnergyPlus.

within Buildings.ThermalZones.EnergyPlus_9_6_0;
model OutputVariable
  "Block to read an EnergyPlus output variable"
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.PartialEnergyPlusObject;
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.Synchronize.ObjectSynchronizer;
  parameter String name
    "EnergyPlus name of the output variable as in the EnergyPlus .rdd or .mdd file";
  parameter String key
    "EnergyPlus key of the output variable";
  parameter Boolean isDirectDependent=false
    "Set to false for states or weather variables, or true for algebraic variables with direct dependency on input variables";
  Modelica.Blocks.Interfaces.RealInput directDependency if isDirectDependent
    "Set to algebraic variable on which this output directly depends on"
  discrete Modelica.Blocks.Interfaces.RealOutput y
    "Output received from EnergyPlus"

protected
  final parameter Boolean printUnit=building.printUnits
    "Set to true to print unit of OutputVariable objects to log file"
  Modelica.Blocks.Interfaces.RealInput directDependency_in_internal
    "Needed to connect to conditional connector";
  constant Integer nParOut=0
    "Number of parameter values retrieved from EnergyPlus";
  constant Integer nInp=0
    "Number of inputs";
  constant Integer nOut=1
    "Number of outputs";
  constant Integer nDer=0
    "Number of derivatives";
  constant Integer nY=nOut+nDer+1
    "Size of output vector of exchange function";
  parameter Integer nObj(
    fixed=false,
    start=0)
    "Total number of Spawn objects in building";
  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject adapter=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject(
    objectType=4,
    startTime=startTime,
    modelicaNameBuilding=modelicaNameBuilding,
    modelicaInstanceName=modelicaInstanceName,
    spawnExe=spawnExe,
    idfVersion=idfVersion,
    idfName=idfName,
    epwName=epwName,
    runPeriod=runPeriod,
    relativeSurfaceTolerance=relativeSurfaceTolerance,
    epName=name,
    usePrecompiledFMU=usePrecompiledFMU,
    fmuName=fmuName,
    buildingsRootFileLocation=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.buildingsRootFileLocation,
    logLevel=logLevel,
    printUnit=printUnit,
    jsonName="outputVariables",
    jsonKeysValues="        \"name\": \""+name+"\",
        \"key\": \""+key+"\",
        \"fmiName\": \""+name+"_"+key+"\"",
    parOutNames=fill("",nParOut),
    parOutUnits=fill("",nParOut),
    nParOut=nParOut,
    inpNames=fill("",nInp),
    inpUnits=fill("",nInp),
    nInp=0,
    outNames={key},
    outUnits=fill("",nOut),
    nOut=nOut,
    derivatives_structure=fill(fill(nDer,2),nDer),
    nDer=nDer,
    derivatives_delta=fill(0,nDer))
    "Class to communicate with EnergyPlus";
  Real yEP[nY]
    "Output of exchange function";
  Modelica.Units.SI.Time tNext(start=startTime, fixed=true)
    "Next sampling time";

initial equation
  assert(
    not usePrecompiledFMU,
    "Use of pre-compiled FMU is not supported for block OutputVariable.");
  nObj=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.initialize(
    adapter=adapter,
    isSynchronized=building.isSynchronized);

equation
  if isDirectDependent then
    connect(directDependency,directDependency_in_internal);
  else
    directDependency_in_internal=0;
  end if;
  when {initial(),time >= pre(tNext)} then
    yEP=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.exchange(
      adapter=adapter,
      nY=nY,
      u={round(time,1E-3),directDependency_in_internal},
      dummy=nObj);
    y=yEP[1];
    tNext=yEP[2];
  end when;
  nObj=synBui.synchronize.done;
end OutputVariable;


within Buildings.ThermalZones;
package EnergyPlus_9_6_0 "Package with models to connect to EnergyPlus"
  extends Modelica.Icons.Package;
end EnergyPlus_9_6_0;


Block to write to an EnergyPlus schedule. Block that writes to a schedule object in EnergyPlus.

within Buildings.ThermalZones.EnergyPlus_9_6_0;
block Schedule
  "Block to write to an EnergyPlus schedule"
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.PartialEnergyPlusObject;
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.Synchronize.ObjectSynchronizer;
  parameter String name
    "Name of schedule";
  parameter Buildings.ThermalZones.EnergyPlus_9_6_0.Types.Units unit
    "Unit of variable as used in Modelica"
  Modelica.Blocks.Interfaces.RealInput u
    "Continuous input signal to be written to EnergyPlus"
  Modelica.Blocks.Interfaces.RealOutput y
    "Value written to EnergyPlus (use for direct dependency of Actuators and Schedules)"

protected
  constant Integer nParOut=0
    "Number of parameter values retrieved from EnergyPlus";
  constant Integer nInp=1
    "Number of inputs";
  constant Integer nOut=0
    "Number of outputs";
  constant Integer nDer=0
    "Number of derivatives";
  constant Integer nY=nOut+nDer+1
    "Size of output vector of exchange function";
  parameter Integer nObj(
    fixed=false,
    start=0)
    "Total number of Spawn objects in building";
  final parameter String unitString=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.getUnitAsString(unit)
    "Unit as a string";
  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject adapter=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject(
    objectType=2,
    startTime=startTime,
    modelicaNameBuilding=modelicaNameBuilding,
    modelicaInstanceName=modelicaInstanceName,
    spawnExe=spawnExe,
    idfVersion=idfVersion,
    idfName=idfName,
    epwName=epwName,
    runPeriod=runPeriod,
    relativeSurfaceTolerance=relativeSurfaceTolerance,
    epName=name,
    usePrecompiledFMU=usePrecompiledFMU,
    fmuName=fmuName,
    buildingsRootFileLocation=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.buildingsRootFileLocation,
    logLevel=logLevel,
    printUnit=false,
    jsonName="schedules",
    jsonKeysValues="        \"name\": \""+name+"\",
        \"unit\": \""+unitString+"\",
        \"fmiName\": \""+name+"_"+modelicaInstanceName+"\"",
    parOutNames=fill("",nParOut),
    parOutUnits=fill("",nParOut),
    nParOut=nParOut,
    inpNames={modelicaInstanceName},
    inpUnits={unitString},
    nInp=nInp,
    outNames=fill("",nOut),
    outUnits=fill("",nOut),
    nOut=nOut,
    derivatives_structure=fill(fill(nDer,2),nDer),
    nDer=nDer,
    derivatives_delta=fill(0,nDer))
    "Class to communicate with EnergyPlus";

  Real yEP[nY]
    "Output of exchange function";

initial equation
  assert(
    not usePrecompiledFMU,
    "Use of pre-compiled FMU is not supported for block Schedule.");
  nObj=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.initialize(
    adapter=adapter,
    isSynchronized=building.isSynchronized);

equation
  yEP=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.exchange(
    adapter=adapter,
    nY=nY,
    u={u,round(time,1E-3)},
    dummy=nObj);
  y=yEP[1];
  nObj=synBui.synchronize.done;
end Schedule;


within Buildings.ThermalZones.EnergyPlus_9_6_0;
model ThermalZone
  "Model to connect to an EnergyPlus thermal zone"
  extends
    Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.PartialEnergyPlusObject;
  parameter String zoneName
    "Name of the thermal zone as specified in the EnergyPlus input";
  parameter Integer nPorts=0
    "Number of fluid ports (equals to 2 for one inlet and one outlet)"
  ////////////////////////////////////////////////////////////////////////////
  // Media declaration. This is identical to
  // Buildings.Fluid.Interfaces.LumpedVolumeDeclarations, except
  // that the comments have been changed to avoid a confusion about
  // what energyDynamics refers to.
  replaceable package Medium=Modelica.Media.Interfaces.PartialMedium
    "Medium in the component"
  // Ports
  parameter Boolean use_C_flow=false
    "Set to true to enable input connector for trace substance that is connected to room air"
  // Initialization
  parameter Medium.AbsolutePressure p_start=Medium.p_default
    "Start value of zone air pressure"
  parameter Medium.Temperature T_start=Medium.T_default
    "Start value of zone air temperature"
  parameter Medium.MassFraction X_start[Medium.nX](
    quantity=Medium.substanceNames)=Medium.X_default
    "Start value of zone air mass fractions m_i/m"
  parameter Medium.ExtraProperty C_start[Medium.nC](
    quantity=Medium.extraPropertiesNames)=fill(
    0,
    Medium.nC)
    "Start value of zone air trace substances"
  parameter Medium.ExtraProperty C_nominal[Medium.nC](
    quantity=Medium.extraPropertiesNames)=fill(
    1E-2,
    Medium.nC)
    "Nominal value of zone air trace substances. (Set to typical order of magnitude.)"
  final parameter Modelica.Units.SI.Volume V=fmuZon.V "Zone volume";
  final parameter Modelica.Units.SI.Area AFlo=fmuZon.AFlo "Floor area";
  final parameter Real mSenFac(
    min=1)=fmuZon.mSenFac
    "Factor for scaling the sensible thermal mass of the zone air volume"
  Modelica.Blocks.Interfaces.RealInput qGai_flow[3](
    each unit="W/m2")
    "Radiant, convective sensible and latent heat input into room (positive if heat gain)"
  Modelica.Blocks.Interfaces.RealInput[Medium.nC] C_flow if use_C_flow
    "Trace substance mass flow rate added to the medium"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heaPorAir
    "Heat port to air volume"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heaPorRad
    "Heat port to radiative temperature and radiative energy balance"

  Modelica.Fluid.Vessels.BaseClasses.VesselFluidPorts_b ports[nPorts](
    redeclare each package Medium=Medium)
    "Fluid inlets and outlets"
  Modelica.Blocks.Interfaces.RealOutput TAir(
    final unit="K",
    displayUnit="degC")
    "Air temperature of the zone"
  Modelica.Blocks.Interfaces.RealOutput TRad(
    final unit="K",
    displayUnit="degC")
    "Radiative temperature of the zone"
  Modelica.Blocks.Interfaces.RealOutput phi(
    final unit="1")
    "Relative humidity"

protected
  constant Modelica.Units.SI.SpecificEnergy h_fg=Medium.enthalpyOfCondensingGas(
      273.15 + 37) "Latent heat of water vapor";
  final parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=V*3/3600
    "Nominal mass flow rate (used for regularization)";

  final parameter Boolean setInitialRadiativeHeatGainToZero = building.setInitialRadiativeHeatGainToZero
    "If true, then the radiative heat gain sent from Modelica to EnergyPlus is zero during the model initialization"

  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.ThermalZoneAdapter fmuZon(
    final modelicaNameBuilding=modelicaNameBuilding,
    final modelicaInstanceName=modelicaInstanceName,
    final spawnExe=spawnExe,
    final idfVersion=idfVersion,
    final idfName=idfName,
    final epwName=epwName,
    final runPeriod=runPeriod,
    final relativeSurfaceTolerance=relativeSurfaceTolerance,
    final setInitialRadiativeHeatGainToZero=setInitialRadiativeHeatGainToZero,
    final zoneName=zoneName,
    final nFluPor=nPorts,
    final usePrecompiledFMU=usePrecompiledFMU,
    final fmuName=fmuName,
    final logLevel=logLevel)
    "FMU zone adapter"
  Buildings.Fluid.Interfaces.ConservationEquation vol(
    redeclare final package Medium=Medium,
    final energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    final massDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    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 use_mWat_flow=Medium.nXi > 0,
    final use_C_flow=use_C_flow,
    final fluidVolume=V,
    final nPorts=nPorts)
    "Air volume of the thermal zone"
  Buildings.ThermalZones.Detailed.BaseClasses.HeatGain heaGai(
    final AFlo=AFlo)
    "Model to convert internal heat gains"
  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"
  Modelica.Blocks.Math.Add QLat_flow(final k1=1, final k2=1)
    "Total latent heat gains of the zone"
  Modelica.Blocks.Math.Add QGaiSenLat_flow(
    final k1=1,
    final k2=1)
    "Sensible and latent heat gains of the zone"
  Modelica.Blocks.Math.Add QConSen_flow(
    final k1=1,
    final k2=1)
    "Convective sensible heat gains of the zone from EnergyPlus and Modelica input connector qGai_flow"
  Buildings.HeatTransfer.Sources.PrescribedHeatFlow conQCon_flow
    "Converter for convective heat flow rate"
  final parameter String substanceName="CO2"
    "Name of trace substance";
  final parameter Modelica.Units.SI.MolarMass MM=Modelica.Media.IdealGases.Common.SingleGasesData.CO2.MM
    "Molar mass of the trace substance";
  Modelica.Blocks.Routing.Replicator QPeaRep(
    nout=Medium.nC) if use_C_flow
    "Replicator to convert QPea_flow into a vector"
  Modelica.Blocks.Math.Add CTot_flow[Medium.nC](
    each final k1=1,
    final k2={
      if
        (Modelica.Utilities.Strings.isEqual(
        string1=Medium.extraPropertiesNames[i],
        string2=substanceName,
        caseSensitive=false)) then
        3.82E-8*Modelica.Media.IdealGases.Common.SingleGasesData.CO2.MM/Modelica.Media.IdealGases.Common.SingleGasesData.Air.MM
      else
        0 for i in 1:Medium.nC},
    u1(
      each final unit="W")) if use_C_flow
    "Total trace substance flow rate"
  Buildings.Fluid.Sensors.MassFlowRate senMasFlo[nPorts](
    redeclare each final package Medium=Medium,
    each final allowFlowReversal=true)
    "Mass flow rate sensor"
  Modelica.Blocks.Sources.RealExpression TAirIn[nPorts](
    y=Medium.temperature(
      state=Medium.setState_phX(
        p=ports.p,
        h=inStream(ports.h_outflow),
        X=inStream(ports.Xi_outflow)))) if nPorts > 0
    "Temperature that the air has if it were flowing into the room"
  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor heaFloSen
    "Heat flow sensor"
  Buildings.HeatTransfer.Sources.PrescribedTemperature preTem
    "Port temperature"
  Modelica.Blocks.Sources.RealExpression TFlu(
    y=Medium.temperature_phX(
      p=vol.medium.p,
      h=vol.hOut,
      X=cat(1,vol.XiOut,{1-sum(vol.XiOut)})))
    "Air temperature of control volume"
  Modelica.Blocks.Sources.RealExpression pFlu(
    y=vol.medium.p)
    "Air pressure"
  Utilities.Psychrometrics.Phi_pTX relHum
    "Relative humidity"
  Controls.OBC.CDL.Reals.Divide X_w
    "Water vapor mass fraction per kg total air"

  HeatTransfer.Sources.PrescribedTemperature preRadTem
    "Prescribed radiative temperature"
  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor radHeaFloSen
    "Radiative heat flow sensor"
  Modelica.Blocks.Math.Add QRad_flow(
    final k1=1,
    final k2=1,
    u1(final unit="W"),
    u2(final unit="W"),
    y(final unit="W"))
    "Total radiant heat gains of the zone"
initial equation
  assert(
    idfName <> "",
    "Must provide the name of the fmu file.");
  assert(
    zoneName <> "",
    "Must provide the name of the zone.");
// assert(nPorts >= 2, "The zone must have at least one air inlet and outlet.");

equation
  connect(heaGai.qGai_flow,qGai_flow)
  connect(fmuZon.TRad,TRad)
  connect(QGaiSenLat_flow.u1,QConSen_flow.y)
  connect(QGaiSenLat_flow.u2, QLat_flow.y)
  connect(QGaiSenLat_flow.y,conQCon_flow.Q_flow)
  connect(conQCon_flow.port,heaPorAir)
  connect(QLat_flow.y, mWat_flow.u)
  connect(mWat_flow.y,vol.mWat_flow)
  connect(CTot_flow.y,vol.C_flow)
  connect(C_flow,CTot_flow.u1)
  for i in 1:nPorts loop
    connect(ports[i],senMasFlo[i].port_a)
    connect(fmuZon.m_flow[i],senMasFlo[i].m_flow)
    connect(senMasFlo[i].port_b,vol.ports[i])
  end for;
  connect(fmuZon.TInlet,TAirIn.y)
  connect(TFlu.y,preTem.T)
  connect(heaFloSen.port_b,preTem.port)
  connect(heaFloSen.port_a,heaPorAir)
  connect(TFlu.y,fmuZon.T)
  connect(TFlu.y,TAir)
  connect(heaFloSen.Q_flow,vol.Q_flow)
  connect(vol.XiOut[1],fmuZon.X_w)
  connect(X_w.y,relHum.X_w)
  connect(vol.mXiOut[1],X_w.u1)
  connect(vol.mOut,X_w.u2)
  connect(TFlu.y,relHum.T)
  connect(pFlu.y,relHum.p)
  connect(relHum.phi,phi)
  connect(QPeaRep.y,CTot_flow.u2)
  connect(QPeaRep.u,fmuZon.QPeo_flow)
  connect(QConSen_flow.u2,heaGai.QCon_flow)
  connect(fmuZon.QCon_flow,QConSen_flow.u1)
  connect(fmuZon.QLat_flow, QLat_flow.u1)
  connect(heaGai.QLat_flow, QLat_flow.u2)
  connect(radHeaFloSen.port_b, preRadTem.port)
  connect(radHeaFloSen.port_a, heaPorRad)
  connect(fmuZon.TRad, preRadTem.T)
  connect(fmuZon.QGaiRad_flow, QRad_flow.y)
  connect(QRad_flow.u1, heaGai.QRad_flow)
  connect(QRad_flow.u2, radHeaFloSen.Q_flow)
end ThermalZone;


EnergyPlus package user's guide

within Buildings.ThermalZones.EnergyPlus_9_6_0;
package UsersGuide
  "EnergyPlus package user's guide"
  extends Modelica.Icons.Information;

  class Installation
    "Installing binaries"
    extends Modelica.Icons.Information;
  end Installation;

  class GettingStarted
    "Getting started"
    extends Modelica.Icons.Information;
  end GettingStarted;

  class Conventions
    "Conventions"
    extends Modelica.Icons.Information;
  end Conventions;

  class UnitConversion
    "Unit Conversion"
    extends Modelica.Icons.Information;
  end UnitConversion;

  class EnergyPlusWarmUp
    "EnergyPlus warm-up"
    extends Modelica.Icons.Information;
  end EnergyPlusWarmUp;

  class KnownIssues
    "Known issues"
    extends Modelica.Icons.Information;
  end KnownIssues;

  class NotesForDymola
    "Notes for Dymola"
    extends Modelica.Icons.Information;
  end NotesForDymola;
end UsersGuide;


Model to exchange heat with a inside-facing surface of a thermal zone. Block that sends for a room-side facing surface its temperature to EnergyPlus and receives the
room-side heat flow rate from EnergyPlus.

within Buildings.ThermalZones.EnergyPlus_9_6_0;
model ZoneSurface
  "Model to exchange heat with a inside-facing surface of a thermal zone"
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.PartialEnergyPlusObject;
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.Synchronize.ObjectSynchronizer;
  parameter String surfaceName
    "Surface unique name in the EnergyPlus idf file";
  final parameter Modelica.Units.SI.Area A(final fixed=false, min=1E-10)
    "Surface area";
  Modelica.Blocks.Interfaces.RealInput T(
    final unit="K",
    displayUnit="degC",
    final quantity="ThermodynamicTemperature")
    "Surface temperature"
  Modelica.Blocks.Interfaces.RealOutput Q_flow(
    final unit="W",
    final quantity="Power")
    "Net heat flow rate from the thermal zone to the surface (positive if surface is cold)"
  Modelica.Blocks.Interfaces.RealOutput q_flow(
    final unit="W/m2",
    final quantity="HeatFlux")
    "Net heat flux from the thermal zone to the surface (positive if surface is cold)"

protected
  constant Integer nParOut=1
    "Number of parameter values retrieved from EnergyPlus";
  constant Integer nInp=1
    "Number of inputs";
  constant Integer nOut=1
    "Number of outputs";
  constant Integer nDer=0
    "Number of derivatives";
  constant Integer nY=nOut+nDer+1
    "Size of output vector of exchange function";
  parameter Integer nObj(
    fixed=false,
    start=0)
    "Total number of Spawn objects in building";
  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject adapter=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject(
    objectType=5,
    startTime=startTime,
    modelicaNameBuilding=modelicaNameBuilding,
    modelicaInstanceName=modelicaInstanceName,
    spawnExe=spawnExe,
    idfVersion=idfVersion,
    idfName=idfName,
    epwName=epwName,
    runPeriod=runPeriod,
    relativeSurfaceTolerance=relativeSurfaceTolerance,
    epName=surfaceName,
    usePrecompiledFMU=usePrecompiledFMU,
    fmuName=fmuName,
    buildingsRootFileLocation=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.buildingsRootFileLocation,
    logLevel=logLevel,
    printUnit=false,
    jsonName="zoneSurfaces",
    jsonKeysValues="        \"name\": \""+surfaceName+"\"",
    parOutNames={"A"},
    parOutUnits={"m2"},
    nParOut=nParOut,
    inpNames={"T"},
    inpUnits={"K"},
    nInp=nInp,
    outNames={"Q_flow"},
    outUnits={"W"},
    nOut=nOut,
    derivatives_structure=fill(fill(nDer,2),nDer),
    nDer=nDer,
    derivatives_delta=fill(0,nDer))
    "Class to communicate with EnergyPlus";
  //////////
  // The derivative structure was:
  //  derivatives_structure={{1,1}},
  //  nDer=nDer,
  //  derivatives_delta={0.1}
  // This has been removed due to numerical noise,
  // see https://github.com/lbl-srg/modelica-buildings/issues/2358#issuecomment-819578850
  //////////
  Real yEP[nY]
    "Output of exchange function";
  Modelica.Units.SI.Time tNext(start=startTime, fixed=true)
    "Next sampling time";
  discrete Modelica.Units.SI.Time tLast(fixed=true, start=startTime)
    "Last time of data exchange";
  discrete Modelica.Units.SI.Time dtLast
    "Time step since the last synchronization";
  discrete Modelica.Units.SI.Temperature TLast
    "Surface temperature at last sampling";
  discrete Modelica.Units.SI.HeatFlowRate QLast_flow(fixed=false, start=0)
    "Surface heat flow rate if T = TLast";
//  discrete Real dQ_flow_dT(
//    final unit="W/K")
//    "Derivative dQCon_flow / dT";

initial equation
  assert(
    not usePrecompiledFMU,
    "Use of pre-compiled FMU is not supported for ZoneSurface.");
  nObj=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.initialize(
    adapter=adapter,
    isSynchronized=building.isSynchronized);
  {A}=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.getParameters(
    adapter=adapter,
    nParOut=nParOut,
    isSynchronized=nObj);
  assert(
    A > 0,
    "Surface area must not be zero.");

equation
  when {initial(),time >= pre(tNext)} then
    // Initialization of output variables.
    TLast=T;
    dtLast=time-pre(tLast);
    yEP=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.exchange(
      adapter=adapter,
      nY=nY,
      u={T,round(time,1E-3)},
      dummy=A);
    QLast_flow=yEP[1];
    //dQ_flow_dT=yEP[2];
    //tNext=yEP[3];
    tNext=yEP[2];
    tLast=time;
  end when;
  //Q_flow=QLast_flow+(T-TLast)*dQ_flow_dT;
  Q_flow=QLast_flow;
  q_flow=Q_flow/A;
  nObj=synBui.synchronize.done;

The temperature of this surface will then be set to the value received
at the connector T, and the net heat flow rate
received from the thermal zone is produced at the output Q_flow.
The output q_flow = Q_flow / A is the heat flux
per unit area of the surface.

The model

Buildings.ThermalZones.EnergyPlus_9_6_0.Examples.SingleFamilyHouse.HeatPumpRadiantHeatingGroundHeatTransfer
illustrates this use. Note that if the ground heat transfer were modeled in EnergyPlus, then

Buildings.ThermalZones.EnergyPlus_9_6_0.OpaqueConstruction
should have been used, which is simpler to setup.

",
      revisions="

February 9, 2021, by Michael Wetter:

First implementation.

This is for
issue 2358.

"),
    Icon(
      graphics={
        Rectangle(
          lineColor={95,95,95},
          fillColor={95,95,95},
          fillPattern=FillPattern.Solid,
          extent={{-78,-64},{70,68}}),
        Rectangle(
          lineColor={117,148,176},
          fillColor={170,213,255},
          fillPattern=FillPattern.Solid,
          extent={{56,-50},{-62,56}}),
        Rectangle(
          extent={{-62,-44},{56,-50}},
          lineColor={238,46,47},
          fillColor={238,46,47},
          fillPattern=FillPattern.Solid),
        Text(
          extent={{-112,72},{-136,29}},
          textColor={0,0,0},
          textString="T"),
        Text(
          extent={{144,110},{106,71}},
          textColor={0,0,0},
          textString="Q_flow"),
        Text(
          extent={{144,-10},{106,-49}},
          textColor={0,0,0},
          textString="q_flow")}));
end ZoneSurface;


Exchange the values with the EnergyPlus thermal zone. External function that exchanges data with EnergyPlus.

within Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses;
pure function exchange
  "Exchange the values with the EnergyPlus thermal zone"
  extends Modelica.Icons.Function;
  input Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject adapter
    "External object";
  input Integer nY
    "Size of output y";
  input Real u[:]
    "Input values. First all inputs, then the current model time";
  input Real dummy
    "Dummy value (used to force Modelica tools to call initialize())";
  output Real y[nY]
    "Output values. First all outputs, then all derivatives, then next event time";
external "C" exchange_Modelica_EnergyPlus_9_6_0(adapter, u, dummy, y)
end exchange;


within Buildings.ThermalZones.EnergyPlus_9_6_0;
package BaseClasses "Package with base classes for Buildings.ThermalZones.EnergyPlus_9_6_0"
  extends Modelica.Icons.BasesPackage;
    constant String buildingsRootFileLocation=
      Modelica.Utilities.Files.loadResource("modelica://Buildings/legal.html")
      "Path to top-level legal.html of the Buildings library (used to find the spawn executable)";
end BaseClasses;


Partial definitions of an EnergyPlus object. Partial model for an EnergyPlus object.

within Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses;
partial block PartialEnergyPlusObject
  "Partial definitions of an EnergyPlus object"
  extends Modelica.Blocks.Icons.Block;
  outer Buildings.ThermalZones.EnergyPlus_9_6_0.Building building
    "Building-level declarations";

protected
  constant String modelicaNameBuilding=building.modelicaNameBuilding
    "Name of the building to which this output variable belongs to"
  constant String modelicaInstanceName=getInstanceName()
    "Name of this instance"
  constant String spawnExe=building.spawnExe
      "Name of the spawn executable, without extension, such as spawn-0.2.0-d7f1e095f3"
  constant String idfVersion = building.idfVersion
    "IDF version with underscore, used for error report"
  final parameter String idfName=building.idfName
    "Name of the IDF file that contains this zone";
  final parameter String epwName=building.epwName
    "Name of the EnergyPlus weather file (but with mos extension)";
  final parameter Real relativeSurfaceTolerance=building.relativeSurfaceTolerance
    "Relative tolerance of surface temperature calculations";

  final parameter Buildings.ThermalZones.EnergyPlus_9_6_0.Data.RunPeriod runPeriod=building.runPeriod
      "EnergyPlus RunPeriod configuration"

  final parameter Boolean usePrecompiledFMU=building.usePrecompiledFMU
    "Set to true to use pre-compiled FMU with name specified by fmuName"
  final parameter String fmuName=building.fmuName
    "Specify if a pre-compiled FMU should be used instead of EnergyPlus (mainly for development)"
  final parameter Buildings.ThermalZones.EnergyPlus_9_6_0.Types.LogLevels logLevel=building.logLevel
    "LogLevels of EnergyPlus output"
  parameter Modelica.Units.SI.Time startTime(fixed=false)
    "Simulation start time";
  function round
    input Real u;
    input Real accuracy;
    output Real y;

  algorithm
    y :=
      if
        (u > 0) then
        floor(
          u/accuracy+0.5)*accuracy
      else
        ceil(
          u/accuracy-0.5)*accuracy;
  end round;

initial equation
  startTime=time;
end PartialEnergyPlusObject;


Block that interacts with this EnergyPlus zone. Block that exchanges data between Modelica and EnergyPlus.
This block is calling the C functions to initialize EnergyPlus,
exchange data with EnergyPlus, and free the memory, through the destructor
of its class adapter, of EnergyPlus.

within Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses;
model ThermalZoneAdapter
  "Block that interacts with this EnergyPlus zone"
  extends Modelica.Blocks.Icons.Block;
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.Synchronize.ObjectSynchronizer;

  constant String modelicaNameBuilding
    "Name of the building to which this thermal zone belongs to"
  constant String modelicaInstanceName=getInstanceName()
    "Name of this instance"
  constant String spawnExe
      "Name of the spawn executable, without extension, such as spawn-0.2.0-d7f1e095f3"
  constant String idfVersion
    "IDF version with underscore, used for error report"

  parameter String idfName
    "Name of the IDF file that contains this zone";
  parameter String epwName
    "Name of the Energyplus weather file including the epw extension";
  parameter Real relativeSurfaceTolerance
    "Relative tolerance of surface temperature calculations";
  parameter Buildings.ThermalZones.EnergyPlus_9_6_0.Data.RunPeriod runPeriod
      "EnergyPlus RunPeriod configuration"

  parameter String zoneName
    "Name of the thermal zone as specified in the EnergyPlus input";
  parameter Boolean usePrecompiledFMU=false
    "Set to true to use pre-compiled FMU with name specified by fmuName"
  parameter String fmuName=""
    "Specify if a pre-compiled FMU should be used instead of EnergyPlus (mainly for development)"
  parameter Buildings.ThermalZones.EnergyPlus_9_6_0.Types.LogLevels logLevel=Buildings.ThermalZones.EnergyPlus_9_6_0.Types.LogLevels.Warning
    "LogLevels of EnergyPlus output"
  parameter Boolean setInitialRadiativeHeatGainToZero
    "If true, then the radiative heat gain sent from Modelica to EnergyPlus is zero during the model initialization"

  parameter Integer nFluPor
    "Number of fluid ports (Set to 2 for one inlet and one outlet)";
  final parameter Modelica.Units.SI.Area AFlo(fixed=false) "Floor area";
  final parameter Modelica.Units.SI.Volume V(fixed=false) "Zone volume";
  final parameter Real mSenFac(
    fixed=false)
    "Factor for scaling the sensible thermal mass of the zone air volume";

  Modelica.Blocks.Interfaces.RealInput T(
    final unit="K",
    displayUnit="degC")
    "Zone air temperature"
  Modelica.Blocks.Interfaces.RealInput X_w(
    final unit="kg/kg")
    "Zone air mass fraction in kg/kg total air"
  Modelica.Blocks.Interfaces.RealInput m_flow[nFluPor](
    each final unit="kg/s")
    "Mass flow rate"
  Modelica.Blocks.Interfaces.RealInput TInlet[nFluPor](
    each final unit="K",
    each displayUnit="degC")
    "Air inlet temperatures"
  Modelica.Blocks.Interfaces.RealInput QGaiRad_flow(
    final unit="W")
    "Radiative heat gain"
  Modelica.Blocks.Interfaces.RealOutput TRad(
    final unit="K",
    displayUnit="degC")
    "Radiative temperature"
  Modelica.Blocks.Interfaces.RealOutput QCon_flow(
    final unit="W")
    "Convective sensible heat to be added to zone air"
  Modelica.Blocks.Interfaces.RealOutput QLat_flow(
    final unit="W")
    "Latent heat to be added to zone air"
  Modelica.Blocks.Interfaces.RealOutput QPeo_flow(
    final unit="W")
    "Total heat gain from people, to be used for optional computation of CO2 released"

protected
  constant Integer nParOut=3
    "Number of parameter values retrieved from EnergyPlus";
  constant Integer nInp=5
    "Number of inputs";
  constant Integer nOut=4
    "Number of outputs";
  constant Integer nDer=0
    "Number of derivatives";
  constant Integer nY=nOut+nDer+1
    "Size of output vector of exchange function";
  parameter Integer nObj(
    fixed=false,
    start=0)
    "Total number of Spawn objects in building";

  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject adapter=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.SpawnExternalObject(
    objectType=1,
    startTime=startTime,
    modelicaNameBuilding=modelicaNameBuilding,
    modelicaInstanceName=modelicaInstanceName,
    spawnExe=spawnExe,
    idfVersion=idfVersion,
    idfName=idfName,
    epwName=epwName,
    runPeriod=runPeriod,
    relativeSurfaceTolerance=relativeSurfaceTolerance,
    epName=zoneName,
    usePrecompiledFMU=usePrecompiledFMU,
    fmuName=fmuName,
    buildingsRootFileLocation=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.buildingsRootFileLocation,
    logLevel=logLevel,
    printUnit=false,
    jsonName="zones",
    jsonKeysValues="        \"name\": \""+zoneName+"\"",
    parOutNames={"AFlo","V","mSenFac"},
    parOutUnits={"m2","m3","1"},
    nParOut=nParOut,
    inpNames={"T","X","mInlets_flow","TAveInlet","QGaiRad_flow"},
    inpUnits={"K","1","kg/s","K","W"},
    nInp=nInp,
    outNames={"TRad","QConSen_flow","QLat_flow","QPeo_flow"},
    outUnits={"K","W","W","W"},
    nOut=nOut,
    derivatives_structure=fill(fill(nDer,2),nDer),
    nDer=nDer,
    derivatives_delta=fill(0,nDer))
    "Class to communicate with EnergyPlus";
  //////////
  // The derivative structure was:
  //     derivatives_structure={{2,1}},
  //  nDer=nDer,
  //  derivatives_delta={0.1}
  // This has been removed due to numerical noise,
  // see https://github.com/lbl-srg/modelica-buildings/issues/2358#issuecomment-819578850
  //////////

  Real yEP[nY]
    "Output of exchange function";

  parameter Modelica.Units.SI.MassFlowRate m_flow_small(fixed=false)
    "Small mass flow rate used to avoid TAveInlet = 0";
  parameter Modelica.Units.SI.Time startTime(fixed=false)
    "Simulation start time";

  discrete Modelica.Units.SI.Time tNext
    "Next sampling time";
  //Modelica.Units.SI.Time tNextEP(start=startTime-1, fixed=true) "Next sampling time requested from EnergyPlus";
  // constant Real dT_dtMax(unit="K/s") = 0.000001 "Bound on temperature derivative to reduce or increase time step";
  //  Modelica.Units.SI.Time dtMax(displayUnit="min", start=600, fixed=true) "Maximum time step before next sampling";
  discrete Modelica.Units.SI.Time tLast
    "Last time of data exchange";
  discrete Modelica.Units.SI.Time dtLast
    "Time step since the last synchronization";
  discrete Modelica.Units.SI.MassFlowRate mInlet_flow
    "Time averaged inlet mass flow rate";
  discrete Modelica.Units.SI.Temperature TAveInlet
    "Time averaged inlet temperature";
  discrete Modelica.Units.SI.Temperature TRooLast
    "Room air temperature at last sampling";
  discrete Modelica.Units.SI.HeatFlowRate QGaiRadAve_flow
   "Radiative heat flow rate averaged over the past synchronization time step";

  Modelica.Units.SI.Energy EGaiRad(unbounded=true)
   "Radiative energy exchanged since the start of the simulation";
  discrete Modelica.Units.SI.Energy EGaiRadLast(unbounded=true)
   "Radiative energy exchanged at the last synchronization time step";

//  discrete Real dQCon_flow_dT(
//    final unit="W/K")
//    "Derivative dQCon_flow / dT";

  discrete Modelica.Units.SI.HeatFlowRate QConLast_flow(fixed=false, start=0)
    "Convective sensible heat to be added to zone air if T = TRooLast";
  function round
    input Real u;
    input Real accuracy;
    output Real y;

  algorithm
    y :=
      if
        (u > 0) then
        floor(
          u/accuracy+0.5)*accuracy
      else
        ceil(
          u/accuracy-0.5)*accuracy;
  end round;

initial equation
  if usePrecompiledFMU then
    assert(
      Modelica.Utilities.Strings.length(fmuName) > 1,
      "If usePrecompiledFMU = true, must set parameter fmuName");
  end if;

  nObj=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.initialize(
    adapter=adapter,
    isSynchronized=building.isSynchronized);

  {AFlo, V, mSenFac}=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.getParameters(
    adapter=adapter,
    nParOut=nParOut,
    isSynchronized=nObj);

  m_flow_small=V*3*1.2/3600*1E-10;
  startTime=time;

  // Initialization of output variables.
  TRooLast=T;
  dtLast=0;
  mInlet_flow=0;
  TAveInlet=T;

  QGaiRadAve_flow = if setInitialRadiativeHeatGainToZero then 0 else QGaiRad_flow;
  tLast=time;
  EGaiRad = 0;
  EGaiRadLast = 0;

  // Synchronization with EnergyPlus
  // Below, the term X_w/(1.-X_w) is for conversion from kg/kg_total_air (Modelica) to kg/kg_dry_air (EnergyPlus)
  yEP=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.exchange(
    adapter=adapter,
    nY=nY,
    u={ T, X_w/(1.-X_w), mInlet_flow, TAveInlet, QGaiRadAve_flow, round(time,1E-3)},
    dummy=AFlo);

  TRad=yEP[1];
  QConLast_flow=yEP[2];
  QLat_flow=yEP[3];
  QPeo_flow=yEP[4];
  tNext=yEP[5];

  assert(AFlo > 0, "Floor area must not be zero.");
  assert(V > 0, "Volume must not be zero.");
  assert(mSenFac > 0.9999, "mSenFac must be at least 1.");

equation
  if usePrecompiledFMU then
    // For JModelica, this must be in the equation section rather than the initial equation section
    assert(Modelica.Utilities.Strings.length(fmuName) > 1,
      "If usePrecompiledFMU = true, must set parameter fmuName");
  end if;

  // Integrate GQaiRad_flow, which breaks any algebraic loop if QGaiRad_flow is
  // a function of TRad
  der(EGaiRad) = QGaiRad_flow;

  // Synchronization with EnergyPlus
  when {time >= pre(tNext)} then
    // Initialization of output variables.
    TRooLast=T;
    dtLast=time-pre(tLast);
    mInlet_flow=noEvent(
      sum(
        if m_flow[i] > 0 then
          m_flow[i]
        else
          0 for i in 1:nFluPor));
    TAveInlet=noEvent(
      sum(
        if m_flow[i] > 0 then
          TInlet[i]*m_flow[i]
        else
          0 for i in 1:nFluPor)+m_flow_small*pre(TAveInlet)/(mInlet_flow+m_flow_small));
    // Below, the term X_w/(1.-X_w) is for conversion from kg/kg_total_air (Modelica) to kg/kg_dry_air (EnergyPlus)
    QGaiRadAve_flow = (EGaiRadLast-pre(EGaiRadLast))/dtLast;

    // Call EnergyPlus
    yEP=Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.exchange(
      adapter=adapter,
      nY=nY,
      u={T,X_w/(1.-X_w),pre(mInlet_flow),TAveInlet,QGaiRadAve_flow,round(time,1E-3)},
      dummy=AFlo);
    // Assign return values of EnergyPlus
    TRad=yEP[1];
    QConLast_flow=yEP[2];
    QLat_flow=yEP[3];
    QPeo_flow=yEP[4];
    tNext=yEP[5];
    tLast=time;
    // Store current value of exchanged radiative heat
    EGaiRadLast = EGaiRad;
  end when;
  //QCon_flow=QConLast_flow+(T-TRooLast)*dQCon_flow_dT;
  QCon_flow=QConLast_flow;

  synBui.synchronize.done=nObj;
end ThermalZoneAdapter;


Block that synchronizes an object. Block with outer declaration that is used to synchronize the calls to the Spawn objects.

within Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.Synchronize;
block ObjectSynchronizer
  "Block that synchronizes an object"
  outer Buildings.ThermalZones.EnergyPlus_9_6_0.Building building
    "Reference to outer building model";
  SynchronizeBuilding synBui
    "Model that synchronize the Spawn objects"

equation
  connect(building.synchronize,synBui.synchronize);
end ObjectSynchronizer;


within Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses;
package Synchronize "Package with classes to synchronize Spawn objects"
end Synchronize;


Model to synchronize the Spawn objects that belong to a building. Model that is used to synchronize the calls to the Spawn objects.

within Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.Synchronize;
model SynchronizeBuilding
  "Model to synchronize the Spawn objects that belong to a building"
  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.Synchronize.SynchronizeConnector synchronize
    "Connector that is used to synchronize objects";
end SynchronizeBuilding;


Validation model for the class and functions that instantiate and communicate with an FMU for Model Exchange. Validation model that communicates with an FMU that emulates simple thermal zones.
All thermal zones are identical.

within Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.Validation;
model FMUZoneAdapterZones1
  "Validation model for the class and functions that instantiate and communicate with an FMU for Model Exchange"
  extends Modelica.Icons.Example;
  parameter Modelica.Units.SI.HeatCapacity CZon=6*6*2.7*1.2*1006
    "Heat capacity of zone air";
  inner Building building(
    idfName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/Data/ThermalZones/EnergyPlus_9_6_0/Examples/RefBldgSmallOffice/RefBldgSmallOfficeNew2004_Chicago.idf"),
    epwName=Modelica.Utilities.Files.loadResource(
    "modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw"),
    weaName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
    computeWetBulbTemperature=false,
    usePrecompiledFMU=false)
    "Building model"
  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.ThermalZoneAdapter fmuZonCor(
    modelicaNameBuilding=building.modelicaNameBuilding,
    final spawnExe=building.spawnExe,
    final idfVersion=building.idfVersion,
    final idfName=building.idfName,
    final epwName=building.epwName,
    final relativeSurfaceTolerance=building.relativeSurfaceTolerance,
    final zoneName="Core_ZN",
    usePrecompiledFMU=true,
    final fmuName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/src/ThermalZones/EnergyPlus_9_6_0/FMUs/Zones1.fmu"),
    logLevel=building.logLevel,
    setInitialRadiativeHeatGainToZero=true,
    final nFluPor=2)
    "Adapter to EnergyPlus"
  Modelica.Blocks.Sources.RealExpression X_w(
    y=0.01)
    "Zone absolute humidity"
  Modelica.Blocks.Sources.RealExpression mIn_flow(
    y=0)
    "Inlet mass flow rate"
  Modelica.Blocks.Math.Gain mOut_flow(
    k=-1)
    "Outlet mass flow rate"
  Modelica.Blocks.Sources.RealExpression TIn[2](
    each y=293.15)
    "Inlet temperature"
  Modelica.Blocks.Sources.RealExpression QGaiRad_flow(
    y=0)
    "Radiative heat gain for the zone"
  Modelica.Blocks.Continuous.Integrator TZonCor(
    k=1/CZon,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=294.15,
    y(final unit="K",
      displayUnit="degC"))
    "Zone air temperature"

equation
  connect(X_w.y,fmuZonCor.X_w)
  connect(fmuZonCor.m_flow[1],mIn_flow.y)
  connect(mOut_flow.u,mIn_flow.y)
  connect(mOut_flow.y,fmuZonCor.m_flow[2])
  connect(TIn.y,fmuZonCor.TInlet)
  connect(fmuZonCor.QGaiRad_flow,QGaiRad_flow.y)
  connect(TZonCor.y,fmuZonCor.T)
  connect(fmuZonCor.QCon_flow,TZonCor.u)
end FMUZoneAdapterZones1;


Validation model for the class and functions that instantiate and communicate with an FMU for Model Exchange. Validation model that communicates with an FMU that emulates two simple thermal zones.
Both thermal zones are identical.

within Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.Validation;
model FMUZoneAdapterZones2
  "Validation model for the class and functions that instantiate and communicate with an FMU for Model Exchange"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.HeatCapacity CZon=6*6*2.7*1.2*1006
    "Heat capacity of zone air";
  inner Building building(
    idfName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/Data/ThermalZones/EnergyPlus_9_6_0/Examples/RefBldgSmallOffice/RefBldgSmallOfficeNew2004_Chicago.idf"),
    epwName=Modelica.Utilities.Files.loadResource(
    "modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw"),
    weaName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
    computeWetBulbTemperature=false,
    usePrecompiledFMU=false)
    "Building model"
  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.ThermalZoneAdapter fmuZonCor(
    final modelicaNameBuilding=building.modelicaNameBuilding,
    final spawnExe=building.spawnExe,
    final idfVersion=building.idfVersion,
    final idfName=building.idfName,
    final epwName=building.epwName,
    final relativeSurfaceTolerance=building.relativeSurfaceTolerance,
    final zoneName="Core_ZN",
    usePrecompiledFMU=true,
    final fmuName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/src/ThermalZones/EnergyPlus_9_6_0/FMUs/Zones3.fmu"),
    logLevel=building.logLevel,
    setInitialRadiativeHeatGainToZero=true,
    final nFluPor=2)
    "Adapter to EnergyPlus"
  Modelica.Blocks.Sources.RealExpression X_w(
    y=0.01)
    "Zone absolute humidity"
  Modelica.Blocks.Sources.RealExpression mIn_flow(
    y=0)
    "Inlet mass flow rate"
  Modelica.Blocks.Math.Gain mOut_flow(
    k=-1)
    "Outlet mass flow rate"
  Modelica.Blocks.Sources.RealExpression TIn[2](
    each y=293.15)
    "Inlet temperature"
  Modelica.Blocks.Sources.RealExpression QGaiRad_flow(
    y=0)
    "Radiative heat gain for the zone"
  Modelica.Blocks.Continuous.Integrator TZonCor(
    k=1/CZon,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=294.15,
    y(final unit="K",
      displayUnit="degC"))
    "Zone air temperature"
  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.ThermalZoneAdapter fmuZonSou(
    final modelicaNameBuilding=building.modelicaNameBuilding,
    final spawnExe=building.spawnExe,
    final idfVersion=building.idfVersion,
    final idfName=building.idfName,
    final epwName=building.epwName,
    final relativeSurfaceTolerance=building.relativeSurfaceTolerance,
    final zoneName="South_ZN",
    usePrecompiledFMU=true,
    final fmuName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/src/ThermalZones/EnergyPlus_9_6_0/FMUs/Zones3.fmu"),
    logLevel=building.logLevel,
    setInitialRadiativeHeatGainToZero=true,
    final nFluPor=2)
    "Adapter to EnergyPlus"
  Modelica.Blocks.Continuous.Integrator TZonSou(
    k=1/CZon,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=294.15,
    y(final unit="K",
      displayUnit="degC"))
    "Zone air temperature"

equation
  connect(X_w.y,fmuZonCor.X_w)
  connect(fmuZonCor.m_flow[1],mIn_flow.y)
  connect(mOut_flow.u,mIn_flow.y)
  connect(mOut_flow.y,fmuZonCor.m_flow[2])
  connect(TIn.y,fmuZonCor.TInlet)
  connect(fmuZonCor.QGaiRad_flow,QGaiRad_flow.y)
  connect(X_w.y,fmuZonSou.X_w)
  connect(fmuZonSou.m_flow[1],mIn_flow.y)
  connect(mOut_flow.y,fmuZonSou.m_flow[2])
  connect(TIn.y,fmuZonSou.TInlet)
  connect(fmuZonSou.QGaiRad_flow,QGaiRad_flow.y)
  connect(TZonCor.y,fmuZonCor.T)
  connect(fmuZonCor.QCon_flow,TZonCor.u)
  connect(fmuZonSou.QCon_flow,TZonSou.u)
  connect(TZonSou.y,fmuZonSou.T)
end FMUZoneAdapterZones2;


Validation model for the class and functions that instantiate and communicate with an FMU for Model Exchange. Validation model that communicates with an FMU that emulates three simple thermal zones.
All thermal zones are identical.

within Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.Validation;
model FMUZoneAdapterZones3
  "Validation model for the class and functions that instantiate and communicate with an FMU for Model Exchange"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.HeatCapacity CZon=6*6*2.7*1.2*1006
    "Heat capacity of zone air";
  inner Building building(
    idfName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/Data/ThermalZones/EnergyPlus_9_6_0/Examples/RefBldgSmallOffice/RefBldgSmallOfficeNew2004_Chicago.idf"),
    epwName=Modelica.Utilities.Files.loadResource(
    "modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw"),
    weaName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
    computeWetBulbTemperature=false,
    usePrecompiledFMU=false)
    "Building model"
  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.ThermalZoneAdapter fmuZonCor(
    final modelicaNameBuilding=building.modelicaNameBuilding,
    final spawnExe=building.spawnExe,
    final idfVersion=building.idfVersion,
    final idfName=building.idfName,
    final epwName=building.epwName,
    final relativeSurfaceTolerance=building.relativeSurfaceTolerance,
    final zoneName="Core_ZN",
    usePrecompiledFMU=true,
    final fmuName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/src/ThermalZones/EnergyPlus_9_6_0/FMUs/Zones3.fmu"),
    logLevel=building.logLevel,
    setInitialRadiativeHeatGainToZero=true,
    final nFluPor=2)
    "Adapter to EnergyPlus"
  Modelica.Blocks.Sources.RealExpression X_w(
    y=0.01)
    "Zone absolute humidity"
  Modelica.Blocks.Sources.RealExpression mIn_flow(
    y=0)
    "Inlet mass flow rate"
  Modelica.Blocks.Math.Gain mOut_flow(
    k=-1)
    "Outlet mass flow rate"
  Modelica.Blocks.Sources.RealExpression TIn[2](
    each y=293.15)
    "Inlet temperature"
  Modelica.Blocks.Sources.RealExpression QGaiRad_flow(
    y=0)
    "Radiative heat gain for the zone"
  Modelica.Blocks.Continuous.Integrator TZonCor(
    k=1/CZon,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=294.15,
    y(final unit="K",
      displayUnit="degC"))
    "Zone air temperature"
  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.ThermalZoneAdapter fmuZonSou(
    final modelicaNameBuilding=building.modelicaNameBuilding,
    final spawnExe=building.spawnExe,
    final idfVersion=building.idfVersion,
    final idfName=building.idfName,
    final epwName=building.epwName,
    final relativeSurfaceTolerance=building.relativeSurfaceTolerance,
    final zoneName="South_ZN",
    usePrecompiledFMU=true,
    final fmuName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/src/ThermalZones/EnergyPlus_9_6_0/FMUs/Zones3.fmu"),
    logLevel=building.logLevel,
    setInitialRadiativeHeatGainToZero=true,
    final nFluPor=2)
    "Adapter to EnergyPlus"
  Modelica.Blocks.Continuous.Integrator TZonSou(
    k=1/CZon,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=294.15,
    y(final unit="K",
      displayUnit="degC"))
    "Zone air temperature"
  Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses.ThermalZoneAdapter fmuZonNor(
    final modelicaNameBuilding=building.modelicaNameBuilding,
    final spawnExe=building.spawnExe,
    final idfVersion=building.idfVersion,
    final idfName=building.idfName,
    final epwName=building.epwName,
    final relativeSurfaceTolerance=building.relativeSurfaceTolerance,
    final zoneName="North_ZN",
    usePrecompiledFMU=true,
    final fmuName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/src/ThermalZones/EnergyPlus_9_6_0/FMUs/Zones3.fmu"),
    logLevel=building.logLevel,
    setInitialRadiativeHeatGainToZero=true,
    final nFluPor=2)
    "Adapter to EnergyPlus"
  Modelica.Blocks.Continuous.Integrator TZonNor(
    k=1/CZon,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=294.15,
    y(final unit="K",
      displayUnit="degC"))
    "Zone air temperature"

equation
  connect(X_w.y,fmuZonCor.X_w)
  connect(fmuZonCor.m_flow[1],mIn_flow.y)
  connect(mOut_flow.u,mIn_flow.y)
  connect(mOut_flow.y,fmuZonCor.m_flow[2])
  connect(TIn.y,fmuZonCor.TInlet)
  connect(fmuZonCor.QGaiRad_flow,QGaiRad_flow.y)
  connect(X_w.y,fmuZonSou.X_w)
  connect(fmuZonSou.m_flow[1],mIn_flow.y)
  connect(mOut_flow.y,fmuZonSou.m_flow[2])
  connect(TIn.y,fmuZonSou.TInlet)
  connect(fmuZonSou.QGaiRad_flow,QGaiRad_flow.y)
  connect(TZonCor.y,fmuZonCor.T)
  connect(X_w.y,fmuZonNor.X_w)
  connect(fmuZonNor.m_flow[1],mIn_flow.y)
  connect(mOut_flow.y,fmuZonNor.m_flow[2])
  connect(TIn.y,fmuZonNor.TInlet)
  connect(fmuZonNor.QGaiRad_flow,QGaiRad_flow.y)
  connect(fmuZonCor.QCon_flow,TZonCor.u)
  connect(fmuZonSou.QCon_flow,TZonSou.u)
  connect(TZonSou.y,fmuZonSou.T)
  connect(fmuZonNor.QCon_flow,TZonNor.u)
  connect(TZonNor.y,fmuZonNor.T)
end FMUZoneAdapterZones3;


Collection of validation models

within Buildings.ThermalZones.EnergyPlus_9_6_0.BaseClasses;
package Validation "Collection of validation models"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


Package containing data for EnergyPlus

within Buildings.ThermalZones.EnergyPlus_9_6_0;
package Data "Package containing data for EnergyPlus"
extends Modelica.Icons.MaterialPropertiesPackage;
end Data;


Record for EnergyPlus RunPeriod. Record containing the configuration of the EnergyPlus RunPeriod object.

within Buildings.ThermalZones.EnergyPlus_9_6_0.Data;
record RunPeriod "Record for EnergyPlus RunPeriod"
  extends Modelica.Icons.Record;

  parameter Buildings.ThermalZones.EnergyPlus_9_6_0.Types.WeekDays startDayOfYear = Buildings.ThermalZones.EnergyPlus_9_6_0.Types.WeekDays.Sunday
    "Week day of the first day that is simulated";

  parameter Boolean applyWeekEndHolidayRule = false "Set to true to apply week-end holiday rules";
  parameter Boolean use_weatherFileDaylightSavingPeriod = false
    "Set to true to apply the daylight saving period from the weather data file if present";
  parameter Boolean use_weatherFileHolidaysAndSpecialDays = false
    "Set to true to apply holidays and special days from the weather data file if present";
  parameter Boolean use_weatherFileRainIndicators = true
    "Set to true to use rain indicators from the weather file";
  parameter Boolean use_weatherFileSnowIndicators = true
    "Set to true to use rain indicators from the weather file";
end RunPeriod;


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


within Buildings.ThermalZones.EnergyPlus_9_6_0.Examples.SingleFamilyHouse;
model AirHeating
  "Example model with an air-based heating system that conditions a thermal zone in EnergyPlus"
  extends Modelica.Icons.Example;
  package Medium=Buildings.Media.Air
    "Medium model";
  inner Building building(
    idfName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/Data/ThermalZones/EnergyPlus_9_6_0/Examples/SingleFamilyHouse_TwoSpeed_ZoneAirBalance/SingleFamilyHouse_TwoSpeed_ZoneAirBalance.idf"),
    weaName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.mos"),
    epwName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/weatherdata/USA_IL_Chicago-OHare.Intl.AP.725300_TMY3.epw"),
    computeWetBulbTemperature=false)
    "Building model"
  constant Modelica.Units.SI.Volume VRoo=453.138 "Room volume";
  constant Modelica.Units.SI.Area AFlo=185.834
    "Floor area of the whole floor of the building";
  parameter Modelica.Units.SI.MassFlowRate mOut_flow_nominal=0.3*VRoo*1.2/3600
    "Outdoor air mass flow rate, assuming constant infiltration air flow rate";
  parameter Modelica.Units.SI.MassFlowRate mRec_flow_nominal=8*VRoo*1.2/3600
    "Nominal mass flow rate for recirculated air";
  Buildings.ThermalZones.EnergyPlus_9_6_0.ThermalZone zon(
    redeclare package Medium=Medium,
    zoneName="LIVING ZONE",
    nPorts=4)
    "Thermal zone"
  Fluid.Movers.FlowControlled_m_flow fan(
    redeclare package Medium=Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    m_flow_nominal=mRec_flow_nominal,
    nominalValuesDefineDefaultPressureCurve=true)
    "Fan"
  Controls.OBC.CDL.Reals.Sources.Pulse TSet(
    shift(
      displayUnit="h")=21600,
    amplitude=6,
    period(
      displayUnit="d")=86400,
    offset=273.15+16,
    y(unit="K",
      displayUnit="degC"))
    "Setpoint for room air"
  Controls.OBC.CDL.Reals.PID conPID(
    controllerType=Buildings.Controls.OBC.CDL.Types.SimpleController.PI,
    k=1,
    Ti(
      displayUnit="min")=1800,
    yMax=1,
    yMin=0,
    u_s(
      unit="K",
      displayUnit="degC"),
    u_m(
      unit="K",
      displayUnit="degC"))
    "Controller for heater"
  Fluid.HeatExchangers.Heater_T hea(
    redeclare final package Medium=Medium,
    m_flow_nominal=mRec_flow_nominal,
    dp_nominal=200,
    tau=0,
    show_T=true,
    QMax_flow=4000)
    "Ideal heater"
  Fluid.Sources.Boundary_pT pAtm(
    redeclare package Medium=Medium,
    nPorts=1)
    "Boundary condition"
  Fluid.FixedResistances.PressureDrop duc(
    redeclare package Medium=Medium,
    allowFlowReversal=false,
    linearized=true,
    from_dp=true,
    dp_nominal=100,
    m_flow_nominal=mOut_flow_nominal)
    "Duct resistance (to decouple room and outside pressure)"
  Fluid.Sources.MassFlowSource_WeatherData freshAir(
    redeclare package Medium=Medium,
    m_flow=mOut_flow_nominal,
    nPorts=1)
    "Outside air supply"
  Controls.OBC.CDL.Reals.Hysteresis sta1(
    uLow=0.05,
    uHigh=0.5)
    "Hysteresis to switch on stage 1"
  Controls.OBC.CDL.Conversions.BooleanToReal mSetFan1_flow(
    realTrue=mRec_flow_nominal/2)
    "Mass flow rate for 1st stage"
  Controls.OBC.CDL.Reals.Hysteresis sta2(
    uLow=0.5,
    uHigh=0.75)
    "Hysteresis to switch on stage 2"
  Controls.OBC.CDL.Conversions.BooleanToReal mSetFan2_flow(
    realTrue=mRec_flow_nominal/2)
    "Mass flow rate added for 2nd stage"
  Controls.OBC.CDL.Reals.Add m_fan_set
    "Mass flow rate for fan"
  Controls.OBC.CDL.Reals.Add TAirLvgSet
    "Set point temperature for air leaving the heater"
  Controls.OBC.CDL.Reals.AddParameter TSupMin(
    p=2)
    "Minimum supply air temperature"

  Modelica.Blocks.Sources.Constant qIntGai[3](each k=0)
    "Internal heat gains, set to zero because these are modeled in EnergyPlus"
  Controls.OBC.CDL.Reals.MultiplyByParameter gai(final k=8) "Gain factor"
initial equation
  // Stop simulation if the hard-coded values differ from the ones computed by EnergyPlus.
  assert(
    abs(
      VRoo-zon.V) < 0.01,
    "Zone volume VRoo differs from volume returned by EnergyPlus.");
  assert(
    abs(
      AFlo-zon.AFlo) < 0.01,
    "Zone floor area AFlo differs from area returned by EnergyPlus.");

equation
  connect(TSet.y,conPID.u_s)
  connect(conPID.u_m,zon.TAir)
  connect(fan.port_b,hea.port_a)
  connect(building.weaBus,freshAir.weaBus)
  connect(duc.port_a,zon.ports[1])
  connect(freshAir.ports[1],zon.ports[2])
  connect(fan.port_a,zon.ports[3])
  connect(hea.port_b,zon.ports[4])
  connect(duc.port_b,pAtm.ports[1])
  connect(conPID.y,sta1.u)
  connect(sta1.y,mSetFan1_flow.u)
  connect(conPID.y,sta2.u)
  connect(sta2.y,mSetFan2_flow.u)
  connect(mSetFan2_flow.y,m_fan_set.u1)
  connect(mSetFan1_flow.y,m_fan_set.u2)
  connect(m_fan_set.y,fan.m_flow_in)
  connect(TAirLvgSet.y,hea.TSet)
  connect(zon.TAir,TSupMin.u)
  connect(TSupMin.y,TAirLvgSet.u2)
  connect(zon.qGai_flow, qIntGai.y)
  connect(conPID.y, gai.u)
  connect(gai.y, TAirLvgSet.u1)
end AirHeating;


Example model with a schedule that overrides a schedule in EnergyPlus. Example model that demonstrates how to override a schedule in EnergyPlus.
The model overrides the EnergyPlus schedule INTERMITTENT,
which is used by EnergyPlus to control the equipment in the thermal zone.

within Buildings.ThermalZones.EnergyPlus_9_6_0.Examples.SingleFamilyHouse;
model EquipmentSchedule
  "Example model with a schedule that overrides a schedule in EnergyPlus"
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.Examples.SingleFamilyHouse.Unconditioned;
  Buildings.ThermalZones.EnergyPlus_9_6_0.Schedule schInt(
    name="INTERMITTENT",
    unit=Buildings.ThermalZones.EnergyPlus_9_6_0.Types.Units.Normalized)
    "Block that writes to the EnergyPlus schedule INTERMITTENT"
  Buildings.Controls.OBC.CDL.Reals.Sources.Pulse intLoaFra(
    shift(
      displayUnit="h")=25200,
    period(
      displayUnit="d")=86400)
    "Schedule for fraction of internal loads"

equation
  connect(schInt.u,intLoaFra.y)
end EquipmentSchedule;


within Buildings.ThermalZones.EnergyPlus_9_6_0.Examples.SingleFamilyHouse;
model HeatPumpRadiantHeatingGroundHeatTransfer
  "Example model with one thermal zone with a radiant floor and ground heat transfer modeled in Modelica"
  extends
    Buildings.ThermalZones.EnergyPlus_9_6_0.Examples.SingleFamilyHouse.Unconditioned;
  package MediumW=Buildings.Media.Water
    "Water medium";
  package MediumG=Buildings.Media.Antifreeze.EthyleneGlycolWater(property_T=293.15, X_a=0.40)
    "Water glycol";
  constant Modelica.Units.SI.Area AFlo=185.8 "Floor area";
  parameter Modelica.Units.SI.HeatFlowRate QHea_flow_nominal=8000
    "Nominal heat flow rate for heating";
  parameter Modelica.Units.SI.MassFlowRate mHea_flow_nominal=QHea_flow_nominal/
      4200/5 "Design water mass flow rate for heating";
  parameter Modelica.Units.SI.MassFlowRate mBor_flow_nominal=2*mHea_flow_nominal*(1-1/4)*4200/3500
   "Design water mass flow rate for heating";
  parameter HeatTransfer.Data.OpaqueConstructions.Generic layFlo(
    nLay=3,
    material={
      Buildings.HeatTransfer.Data.Solids.Concrete(x=0.08),
      Buildings.HeatTransfer.Data.Solids.InsulationBoard(x=0.20),
      Buildings.HeatTransfer.Data.Solids.Concrete(x=0.2)})
    "Material layers from surface a to b (8cm concrete, 20 cm insulation, 20 cm concrete)"
  parameter HeatTransfer.Data.Solids.Generic soil(
    x=2,
    k=1.3,
    c=800,
    d=1500)
    "Soil properties"
  Buildings.ThermalZones.EnergyPlus_9_6_0.ZoneSurface livFlo(
    surfaceName="Living:Floor")
    "Surface of living room floor"
  Fluid.HeatExchangers.RadiantSlabs.ParallelCircuitsSlab slaFlo(
    redeclare package Medium=MediumW,
    allowFlowReversal=false,
    layers=layFlo,
    iLayPip=1,
    pipe=Fluid.Data.Pipes.PEX_DN_15(),
    sysTyp=Buildings.Fluid.HeatExchangers.RadiantSlabs.Types.SystemType.Floor,
    disPip=0.3,
    nCir=3,
    A=AFlo,
    m_flow_nominal=mHea_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    from_dp=true,
    show_T=true)
    "Slab for floor with embedded pipes, connected to soil"
  Fluid.Sources.Boundary_ph pre(
    redeclare package Medium=MediumW,
    p(displayUnit="Pa")=300000,
    nPorts=1)
    "Pressure boundary condition"
  HeatTransfer.Sources.PrescribedHeatFlow preHeaLivFlo
    "Surface heat flow rate"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor TSurLivFlo
    "Surface temperature for floor of living room"
  Controls.OBC.CDL.Reals.Sources.Constant TSetRooHea(k(
      final unit="K",
      displayUnit="degC") = 293.15, y(final unit="K", displayUnit="degC"))
    "Room temperture set point for heating"
  Fluid.Movers.SpeedControlled_y pum(
    redeclare package Medium=MediumW,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per(
      pressure(
        V_flow=2*{0,mHea_flow_nominal}/1000,
        dp=2*{14000,0}),
      speed_nominal,
      constantSpeed,
      speeds),
    inputType=Buildings.Fluid.Types.InputType.Continuous)
    "Pump"
  HeatTransfer.Conduction.SingleLayer soi(
    A=AFlo,
    material=soil,
    steadyStateInitial=true,
    stateAtSurface_a=false,
    stateAtSurface_b=false,
    T_a_start=283.15,
    T_b_start=283.75)
    "2m deep soil (per definition on p.4 of ASHRAE 140-2007)"
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature TSoi(
    T=293.15)
    "Boundary condition for construction"

  Controls.OBC.RadiantSystems.Heating.HighMassSupplyTemperature_TRoom conHea(
    TSupSet_max=313.15,
    controllerType=Buildings.Controls.OBC.CDL.Types.SimpleController.P,
    k=2,
    Ti=7200,
    Td=600)
    "Controller for radiant heating system"
  Controls.OBC.CDL.Reals.PIDWithReset conSup(
    final controllerType=Buildings.Controls.OBC.CDL.Types.SimpleController.PI,
    k=4,
    Ti(displayUnit="min") = 60,
    r=10,
    final yMax=1,
    final yMin=0.2,
    final reverseActing=true,
    y_reset=0.2) "Controller for heat pump"
  Controls.OBC.CDL.Reals.Switch swiHeaPum "Switch for heat pump signal"
  Controls.OBC.CDL.Reals.Sources.Constant off(
    final k = 0)
    "Output 0 to switch heater off"
  Fluid.HeatPumps.ScrollWaterToWater heaPum(
    redeclare package Medium1 = MediumW,
    redeclare package Medium2 = MediumG,
    allowFlowReversal1=false,
    allowFlowReversal2=false,
    m1_flow_nominal=mHea_flow_nominal,
    m2_flow_nominal=mBor_flow_nominal,
    show_T=true,
    dp1_nominal=10000,
    dp2_nominal=10000,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    enable_temperature_protection=true,
    TEvaMin=268.15,
    datHeaPum=
        Buildings.Fluid.HeatPumps.Data.ScrollWaterToWater.Heating.ClimateMaster_TMW036_12kW_4_90COP_R410A())
      "Heat pump"
  Fluid.Movers.SpeedControlled_y pumBor(
    redeclare package Medium = MediumG,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per(
      pressure(V_flow=2*{0,mBor_flow_nominal}/1000,
      dp=2*{60000+10000,0}),
      speed_nominal,
      constantSpeed,
      speeds),
    inputType=Buildings.Fluid.Types.InputType.Continuous) "Pump"
 Fluid.Geothermal.Boreholes.UTube borHol(
    redeclare package Medium = MediumG,
    hBor=150,
    dp_nominal=60000,
    dT_dz=0.0015,
    samplePeriod=604800,
    m_flow_nominal=mBor_flow_nominal,
    redeclare parameter HeatTransfer.Data.BoreholeFillings.Bentonite matFil,
    redeclare parameter HeatTransfer.Data.Soil.Sandstone matSoi,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Borehole heat exchanger"
  Fluid.Sources.Boundary_ph pre1(
    redeclare package Medium = MediumG,
    p(displayUnit="Pa") = 300000,
    nPorts=1)
    "Pressure boundary condition"
  Modelica.Blocks.Math.Add TOpe(
    k1=0.5,
    k2=0.5,
    u1(final unit="K", displayUnit="degC"),
    u2(final unit="K", displayUnit="degC"),
    y(final unit="K", displayUnit="degC"))
      "Operative temperature"
  Modelica.Blocks.Sources.RealExpression QCon(y=heaPum.QCon_flow)
    "Condenser heat flow rate"
  Modelica.Blocks.Sources.RealExpression PEle1(y=heaPum.P + pum.P + pumBor.P)
    "Electricity use"
  Modelica.Blocks.Continuous.Integrator EHea(
    k(final unit="1/m2")=1/AFlo,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=0,
    u(final unit="W"),
    y(final unit="J/m2",
      displayUnit="kW.h/m2"))
    "Produced heat per unit area of floor"
  Modelica.Blocks.Continuous.Integrator EEle(
    k(final unit="1/m2")=1/AFlo,
    initType=Modelica.Blocks.Types.Init.InitialState,
    y_start=1E-10,
    u(final unit="W"),
    y(final unit="J/m2",
      displayUnit="kW.h/m2"))
    "Electricity use per floor area"
  Controls.OBC.CDL.Reals.Divide COP "Coefficient of performance"
  Controls.OBC.CDL.Logical.Sources.Pulse ava(
    width=22/24,
    period=24*3600,
    shift=7*3600)
    "Availability schedule to block heat pump operation in early morning (assuming grid is at capacity)"
  Controls.OBC.CDL.Reals.Switch swiPum "Switch for circulation pumps"
  Controls.OBC.CDL.Logical.And onHeaPum "On/off signal for heat pump"
  Fluid.Sensors.TemperatureTwoPort senTemSup(
    redeclare package Medium = MediumW,
    allowFlowReversal=false,
    m_flow_nominal=mHea_flow_nominal,
    tau=0,
    transferHeat=true) "Water supply temperature"
  Modelica.Blocks.Continuous.FirstOrder firOrdTRad(
    T(displayUnit="min") = 600,
    initType=Modelica.Blocks.Types.Init.SteadyState,
    y_start(
      unit="K",
      displayUnit="degC") = 293.15,
    u(
     final unit="K",
     displayUnit="degC"),
    y(final unit="K",
      displayUnit="degC"))
    "First order filter to avoid step change in radiative temperature after EnergyPlus sampling"
initial equation
  // The floor area can be obtained from EnergyPlus, but it is a structural parameter used to
  // size the system and therefore we hard-code it here.
  assert(
    abs(
      AFlo-zon.AFlo) < 0.1,
    "Floor area AFlo differs from EnergyPlus floor area.");

equation
  connect(livFlo.Q_flow,preHeaLivFlo.Q_flow)
  connect(preHeaLivFlo.port,slaFlo.surf_a)
  connect(TSurLivFlo.port,slaFlo.surf_a)
  connect(TSurLivFlo.T,livFlo.T)
  connect(TSoi.port,soi.port_a)
  connect(soi.port_b,slaFlo.surf_b)
  connect(conHea.TSupSet, conSup.u_s)
  connect(conSup.y, swiHeaPum.u1)
  connect(off.y, swiHeaPum.u3)
  connect(pum.port_b, heaPum.port_a1)
  connect(swiHeaPum.y, heaPum.y)
  connect(borHol.port_b, pumBor.port_a)
  connect(pumBor.port_b, heaPum.port_a2)
  connect(heaPum.port_b2, borHol.port_a)
  connect(pre1.ports[1], borHol.port_a)
  connect(slaFlo.port_b, pre.ports[1])
  connect(zon.TAir, TOpe.u1)
  connect(EHea.u, QCon.y)
  connect(EEle.u, PEle1.y)
  connect(EEle.y, COP.u2)
  connect(EHea.y, COP.u1)
  connect(conHea.yPum, swiPum.u1)
  connect(ava.y, swiPum.u2)
  connect(swiPum.y, pum.y)
  connect(swiPum.y, pumBor.y)
  connect(conHea.on, onHeaPum.u1)
  connect(onHeaPum.y, swiHeaPum.u2)
  connect(ava.y, onHeaPum.u2)
  connect(swiPum.u3, off.y)
  connect(onHeaPum.y, conSup.trigger)
  connect(slaFlo.port_a, senTemSup.port_b)
  connect(senTemSup.port_a, heaPum.port_b1)
  connect(senTemSup.T, conSup.u_m)
  connect(TSetRooHea.y, conHea.TRooSet)
  connect(TOpe.u2, firOrdTRad.y)
  connect(zon.TRad, firOrdTRad.u)
  connect(TOpe.y, conHea.TRoo)
  connect(slaFlo.port_b, pum.port_a)
end HeatPumpRadiantHeatingGroundHeatTransfer;


Example model with one actuator that controls the lights in EnergyPlus. Example of a building that uses an EMS actuator to assign the lighting power in EnergyPlus.
The lights are on 30 minutes before sunset, and remain on until 22:00.

within Buildings.ThermalZones.EnergyPlus_9_6_0.Examples.SingleFamilyHouse;
model LightsControl
  "Example model with one actuator that controls the lights in EnergyPlus"
  extends Buildings.ThermalZones.EnergyPlus_9_6_0.Examples.SingleFamilyHouse.Unconditioned;
  Buildings.ThermalZones.EnergyPlus_9_6_0.Actuator actLig(
    unit=Buildings.ThermalZones.EnergyPlus_9_6_0.Types.Units.Power,
    variableName="LIVING ZONE Lights",
    componentType="Lights",
    controlType="Electricity Rate")
    "Actuator for lights"
  Buildings.ThermalZones.EnergyPlus_9_6_0.OutputVariable ligPow(
    name="Lights Electricity Rate",
    key="LIVING ZONE Lights",
    isDirectDependent=true,
    y(final unit="W"))
    "Block that reads the lighting power consumption from EnergyPlus"
  Controls.OBC.CDL.Utilities.SunRiseSet sunRiseSet(
    lat=0.73268921998722,
    lon=-1.5344934783534,
    timZon=-21600)
    "Block that computes sunrise and sunset"
  Controls.OBC.CDL.Reals.Modulo mod1
    "Output time of day"
  Controls.OBC.CDL.Reals.Sources.Constant day(
    k=24*3600)
    "Outputs one day"
  Controls.OBC.CDL.Reals.Sources.CivilTime modTim
    "Model time"
  Controls.OBC.CDL.Reals.LessThreshold lesEquThr(
    t=22*3600)
    "Check whether time is earlier than 22:00"
  Controls.OBC.CDL.Reals.Subtract timToSunSet
    "Time to next sunset"
  Controls.OBC.CDL.Reals.LessThreshold lesEquThr1(
    t=1800)
    "Block that outputs true if sun sets in less than 30 minutes"
  Controls.OBC.CDL.Logical.Or or2
    "Output true if the lights should be on based on sun position"
  Controls.OBC.CDL.Logical.And and2
    "Output true if the lights should be on"
  Controls.OBC.CDL.Conversions.BooleanToReal PLig(
    realTrue=1000)
    "Lighting power"
  Controls.OBC.CDL.Logical.Not not1
    "Output true if the sun is down"
  Controls.OBC.CDL.Reals.GreaterThreshold greEquThr(
    t=12*3600)
    "Output true after noon"
  Controls.OBC.CDL.Logical.And and1
    "Output true if time of day allows lights to be on"

equation
  connect(day.y,mod1.u2)
  connect(mod1.u1,modTim.y)
  connect(mod1.y,lesEquThr.u)
  connect(sunRiseSet.nextSunSet,timToSunSet.u1)
  connect(modTim.y,timToSunSet.u2)
  connect(lesEquThr1.u,timToSunSet.y)
  connect(lesEquThr1.y,or2.u1)
  connect(or2.y,and2.u1)
  connect(and2.y,PLig.u)
  connect(actLig.u,PLig.y)
  connect(not1.y,or2.u2)
  connect(not1.u,sunRiseSet.sunUp)
  connect(greEquThr.u,mod1.y)
  connect(greEquThr.y,and1.u2)
  connect(and1.u1,lesEquThr.y)
  connect(and1.y,and2.u2)
  connect(actLig.y,ligPow.directDependency)
end LightsControl;


within Buildings.ThermalZones.EnergyPlus_9_6_0.Examples;
package SingleFamilyHouse "Package with examples that illustrate the use of the EnergyPlus objects"
  extends Modelica.Icons.ExamplesPackage;
end SingleFamilyHouse;


within Buildings.ThermalZones.EnergyPlus_9_6_0.Examples.SingleFamilyHouse;
model RadiantHeatingCooling_TRoom
  "Example model with one thermal zone with a radiant floor where the cooling is controlled based on the room air temperature"
  extends
    Buildings.ThermalZones.EnergyPlus_9_6_0.Examples.SingleFamilyHouse.Unconditioned(
     building(
        idfName=Modelica.Utilities.Files.loadResource(
          "modelica://Buildings/Resources/Data/ThermalZones/EnergyPlus_9_6_0/Examples/SingleFamilyHouse_TwoSpeed_ZoneAirBalance/SingleFamilyHouse_TwoSpeed_ZoneAirBalance_aboveSoil.idf")));
  package MediumW=Buildings.Media.Water
    "Water medium";
  constant Modelica.Units.SI.Area AFlo=185.8 "Floor area";
  parameter Modelica.Units.SI.HeatFlowRate QHea_flow_nominal=8000
    "Nominal heat flow rate for heating";
  parameter Modelica.Units.SI.MassFlowRate mHea_flow_nominal=QHea_flow_nominal/
      4200/10 "Design water mass flow rate for heating";
  parameter Modelica.Units.SI.HeatFlowRate QCoo_flow_nominal=-3000
    "Nominal heat flow rate for cooling";
  parameter Modelica.Units.SI.MassFlowRate mCoo_flow_nominal=-QCoo_flow_nominal
      /4200/5 "Design water mass flow rate for heating";
  parameter HeatTransfer.Data.OpaqueConstructions.Generic layFloSoi(
      nLay=4,
      material={Buildings.HeatTransfer.Data.Solids.Concrete(x=0.08),
        Buildings.HeatTransfer.Data.Solids.InsulationBoard(x=0.20),
        Buildings.HeatTransfer.Data.Solids.Concrete(x=0.2),
        HeatTransfer.Data.Solids.Generic(
        x=2,
        k=1.3,
        c=800,
        d=1500)})
    "Material layers from surface a to b (8cm concrete, 20 cm insulation, 20 cm concrete, 200 cm soil, below which is the undisturbed soil assumed)"
  parameter HeatTransfer.Data.OpaqueConstructions.Generic layCei(
    nLay=4,
    material={
      Buildings.HeatTransfer.Data.Solids.Concrete(x=0.08),
      Buildings.HeatTransfer.Data.Solids.InsulationBoard(x=0.10),
      Buildings.HeatTransfer.Data.Solids.Concrete(x=0.18),
      Buildings.HeatTransfer.Data.Solids.Concrete(x=0.02)})
    "Material layers from surface a to b (8cm concrete, 10 cm insulation, 18+2 cm concrete)"
  // Floor slab
  Fluid.HeatExchangers.RadiantSlabs.ParallelCircuitsSlab slaFlo(
    redeclare package Medium = MediumW,
    allowFlowReversal=false,
    layers=layFloSoi,
    iLayPip=1,
    pipe=Fluid.Data.Pipes.PEX_DN_15(),
    sysTyp=Buildings.Fluid.HeatExchangers.RadiantSlabs.Types.SystemType.Floor,
    disPip=0.30,
    nCir=3,
    A=AFlo,
    m_flow_nominal=mHea_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    from_dp=true,
    show_T=true) "Slab for floor with embedded pipes, connected to soil"
  Fluid.Sources.Boundary_ph pre(
    redeclare package Medium=MediumW,
    p(displayUnit="Pa")=300000,
    nPorts=1)
    "Pressure boundary condition"
  Controls.OBC.CDL.Reals.Sources.Constant TSetRooHea(
    k(final unit="K",
      displayUnit="degC")=293.15,
    y(final unit="K",
      displayUnit="degC"))
    "Room temperture set point for heating"
  Fluid.Movers.SpeedControlled_y pum(
    redeclare package Medium=MediumW,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per(
      pressure(
        V_flow=2*{0,mHea_flow_nominal}/1000,
        dp=2*{14000,0}),
      speed_nominal,
      constantSpeed,
      speeds),
    inputType=Buildings.Fluid.Types.InputType.Continuous)
    "Pump"
  Fluid.HeatExchangers.Heater_T hea(
    redeclare final package Medium=MediumW,
    allowFlowReversal=false,
    m_flow_nominal=mHea_flow_nominal,
    dp_nominal=10000,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Ideal heater"
  // Ceiling slab
  Fluid.HeatExchangers.RadiantSlabs.ParallelCircuitsSlab slaCei(
    redeclare package Medium=MediumW,
    allowFlowReversal=false,
    layers=layCei,
    iLayPip=3,
    pipe=Fluid.Data.Pipes.PEX_DN_15(),
    sysTyp=Buildings.Fluid.HeatExchangers.RadiantSlabs.Types.SystemType.Ceiling_Wall_or_Capillary,
    disPip=0.2,
    nCir=4,
    A=AFlo,
    m_flow_nominal=mCoo_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    show_T=true)
    "Slab for ceiling with embedded pipes"
  Fluid.Sources.Boundary_ph prePre(
    redeclare package Medium=MediumW,
    nPorts=1,
    p(displayUnit="Pa")=300000)
    "Pressure boundary condition"
  Fluid.Sources.MassFlowSource_T masFloSouCoo(
    redeclare package Medium=MediumW,
    use_m_flow_in=true,
    use_T_in=true,
    nPorts=1)
    "Mass flow source for cooling water at prescribed temperature"
  Controls.OBC.CDL.Reals.Sources.Constant TSetRooCoo(
    k(final unit="K",
      displayUnit="degC")=299.15,
    y(final unit="K",
      displayUnit="degC")) "Room temperture set point for cooling"
  Controls.OBC.CDL.Conversions.BooleanToReal booToRea(
    realTrue=mCoo_flow_nominal)
    "Cooling water mass flow rate"
  Buildings.ThermalZones.EnergyPlus_9_6_0.OpaqueConstruction attFlo(
    surfaceName="Attic:LivingFloor")
    "Floor of the attic above the living room"
  Buildings.ThermalZones.EnergyPlus_9_6_0.OpaqueConstruction livFlo(surfaceName="Living:Floor")
    "Floor of the living room"

  Controls.OBC.RadiantSystems.Heating.HighMassSupplyTemperature_TRoom conHea(
      TSupSet_max=313.15)
    "Controller for radiant heating system"
  Controls.OBC.RadiantSystems.Cooling.HighMassSupplyTemperature_TRoomRelHum
    conCoo(TSupSet_min=289.15) "Controller for radiant cooling"
initial equation
  // The floor area can be obtained from EnergyPlus, but it is a structural parameter used to
  // size the system and therefore we hard-code it here.
  assert(
    abs(
      AFlo-zon.AFlo) < 0.1,
    "Floor area AFlo differs from EnergyPlus floor area.");

equation
  connect(masFloSouCoo.ports[1],slaCei.port_a)
  connect(prePre.ports[1],slaCei.port_b)
  connect(booToRea.y,masFloSouCoo.m_flow_in)
  connect(attFlo.heaPorFro,slaCei.surf_a)
  connect(slaCei.surf_b,attFlo.heaPorBac)
  connect(TSetRooHea.y, conHea.TRooSet)
  connect(pum.y, conHea.yPum)
  connect(pum.port_b,hea.port_a)
  connect(hea.port_b,slaFlo.port_a)
  connect(livFlo.heaPorFro, slaFlo.surf_a)
  connect(slaFlo.surf_b, livFlo.heaPorBac)
  connect(zon.TAir, conHea.TRoo)
  connect(slaFlo.port_b,pum.port_a)
  connect(slaFlo.port_b,pre.ports[1])
  connect(conHea.TSupSet, hea.TSet)
  connect(conCoo.on, booToRea.u)
  connect(conCoo.TRooSet, TSetRooCoo.y)
  connect(zon.TAir, conCoo.TRoo)
  connect(conCoo.phiRoo, zon.phi)
  connect(conCoo.TSupSet, masFloSouCoo.T_in)
end RadiantHeatingCooling_TRoom;


within Buildings.ThermalZones.EnergyPlus_9_6_0.Examples.SingleFamilyHouse;
model RadiantHeatingCooling_TSurface
  "Example model with one thermal zone with a radiant floor where the cooling is controlled based on the surface temperature set point"
  extends
    Buildings.ThermalZones.EnergyPlus_9_6_0.Examples.SingleFamilyHouse.Unconditioned(
      building(
        idfName=Modelica.Utilities.Files.loadResource(
          "modelica://Buildings/Resources/Data/ThermalZones/EnergyPlus_9_6_0/Examples/SingleFamilyHouse_TwoSpeed_ZoneAirBalance/SingleFamilyHouse_TwoSpeed_ZoneAirBalance_aboveSoil.idf")));
  package MediumW=Buildings.Media.Water
    "Water medium";
  constant Modelica.Units.SI.Area AFlo=185.8 "Floor area";
  parameter Modelica.Units.SI.HeatFlowRate QHea_flow_nominal=8000
    "Nominal heat flow rate for heating";
  parameter Modelica.Units.SI.MassFlowRate mHea_flow_nominal=QHea_flow_nominal/
      4200/10 "Design water mass flow rate for heating";
  parameter Modelica.Units.SI.HeatFlowRate QCoo_flow_nominal=-3000
    "Nominal heat flow rate for cooling";
  parameter Modelica.Units.SI.MassFlowRate mCoo_flow_nominal=-QCoo_flow_nominal
      /4200/5 "Design water mass flow rate for heating";
  parameter HeatTransfer.Data.OpaqueConstructions.Generic layFloSoi(nLay=4,
      material={Buildings.HeatTransfer.Data.Solids.Concrete(x=0.08),
        Buildings.HeatTransfer.Data.Solids.InsulationBoard(x=0.20),
        Buildings.HeatTransfer.Data.Solids.Concrete(x=0.2),
        HeatTransfer.Data.Solids.Generic(
        x=2,
        k=1.3,
        c=800,
        d=1500)})
    "Material layers from surface a to b (8cm concrete, 20 cm insulation, 20 cm concrete, 200 cm soil, below which is the undisturbed soil assumed)"
  parameter HeatTransfer.Data.OpaqueConstructions.Generic layCei(
    nLay=4,
    material={
      Buildings.HeatTransfer.Data.Solids.Concrete(x=0.08),
      Buildings.HeatTransfer.Data.Solids.InsulationBoard(x=0.10),
      Buildings.HeatTransfer.Data.Solids.Concrete(x=0.18),
      Buildings.HeatTransfer.Data.Solids.Concrete(x=0.02)})
    "Material layers from surface a to b (8cm concrete, 10 cm insulation, 18+2 cm concrete)"
  // Floor slab
  Fluid.HeatExchangers.RadiantSlabs.ParallelCircuitsSlab slaFlo(
    redeclare package Medium = MediumW,
    allowFlowReversal=false,
    layers=layFloSoi,
    iLayPip=1,
    pipe=Fluid.Data.Pipes.PEX_DN_15(),
    sysTyp=Buildings.Fluid.HeatExchangers.RadiantSlabs.Types.SystemType.Floor,
    disPip=0.3,
    nCir=3,
    A=AFlo,
    m_flow_nominal=mHea_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    from_dp=true,
    show_T=true) "Slab for floor with embedded pipes, connected to soil"
  Fluid.Sources.Boundary_ph pre(
    redeclare package Medium=MediumW,
    p(displayUnit="Pa")=300000,
    nPorts=1)
    "Pressure boundary condition"
  Controls.OBC.CDL.Reals.Sources.Constant TSetRooHea(
    k(final unit="K",
      displayUnit="degC")=293.15,
    y(final unit="K",
      displayUnit="degC"))
    "Room temperture set point for heating"
  Fluid.Movers.SpeedControlled_y pum(
    redeclare package Medium=MediumW,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    per(
      pressure(
        V_flow=2*{0,mHea_flow_nominal}/1000,
        dp=2*{14000,0}),
      speed_nominal,
      constantSpeed,
      speeds),
    inputType=Buildings.Fluid.Types.InputType.Continuous)
    "Pump"
  Fluid.HeatExchangers.Heater_T hea(
    redeclare final package Medium=MediumW,
    allowFlowReversal=false,
    m_flow_nominal=mHea_flow_nominal,
    dp_nominal=10000,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Ideal heater"
  // Ceiling slab
  Fluid.HeatExchangers.RadiantSlabs.ParallelCircuitsSlab slaCei(
    redeclare package Medium=MediumW,
    allowFlowReversal=false,
    layers=layCei,
    iLayPip=3,
    pipe=Fluid.Data.Pipes.PEX_DN_15(),
    sysTyp=Buildings.Fluid.HeatExchangers.RadiantSlabs.Types.SystemType.Ceiling_Wall_or_Capillary,
    disPip=0.2,
    nCir=4,
    A=AFlo,
    m_flow_nominal=mCoo_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    show_T=true)
    "Slab for ceiling with embedded pipes"
  Fluid.Sources.Boundary_ph prePre(
    redeclare package Medium=MediumW,
    nPorts=1,
    p(displayUnit="Pa")=300000)
    "Pressure boundary condition"
  Fluid.Sources.MassFlowSource_T masFloSouCoo(
    redeclare package Medium=MediumW,
    use_m_flow_in=true,
    use_T_in=true,
    nPorts=1)
    "Mass flow source for cooling water at prescribed temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TSetSurCooOn(k(
      final unit="K",
      displayUnit="degC") = 293.15, y(final unit="K", displayUnit="degC"))
    "Surface temperture set point for cooling"
  Controls.OBC.CDL.Conversions.BooleanToReal booToRea(
    realTrue=mCoo_flow_nominal)
    "Cooling water mass flow rate"
  Buildings.ThermalZones.EnergyPlus_9_6_0.OpaqueConstruction attFlo(
    surfaceName="Attic:LivingFloor")
    "Floor of the attic above the living room"
  Buildings.ThermalZones.EnergyPlus_9_6_0.OpaqueConstruction livFlo(surfaceName="Living:Floor")
    "Floor of the living room"

  Controls.OBC.RadiantSystems.Heating.HighMassSupplyTemperature_TRoom conHea(
      TSupSet_max=313.15)
    "Controller for radiant heating system"
  Controls.OBC.RadiantSystems.Cooling.HighMassSupplyTemperature_TSurRelHum
    conCoo(TSupSet_min=289.15) "Controller for radiant cooling"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor TSur
    "Surface temperature"
  Buildings.Controls.OBC.CDL.Reals.Sources.Constant TSetSurOff(k(
      final unit="K",
      displayUnit="degC") = 303.15, y(final unit="K", displayUnit="degC"))
    "Surface temperture set point to switch system off"
  Controls.OBC.CDL.Reals.Greater enaCoo(h=1)
    "Switch to enable and disable cooling"
  Buildings.Controls.OBC.CDL.Reals.Switch TSetSurCoo
    "Set point for surface temperature for cooling system"
  Controls.OBC.CDL.Reals.AddParameter TOffSet(p=3)
    "Off set before switching on the cooling system"
initial equation
  // The floor area can be obtained from EnergyPlus, but it is a structural parameter used to
  // size the system and therefore we hard-code it here.
  assert(
    abs(
      AFlo-zon.AFlo) < 0.1,
    "Floor area AFlo differs from EnergyPlus floor area.");

equation
  connect(masFloSouCoo.ports[1],slaCei.port_a)
  connect(prePre.ports[1],slaCei.port_b)
  connect(booToRea.y,masFloSouCoo.m_flow_in)
  connect(attFlo.heaPorFro,slaCei.surf_a)
  connect(slaCei.surf_b,attFlo.heaPorBac)
  connect(TSetRooHea.y, conHea.TRooSet)
  connect(pum.y, conHea.yPum)
  connect(pum.port_b,hea.port_a)
  connect(hea.port_b,slaFlo.port_a)
  connect(livFlo.heaPorFro, slaFlo.surf_a)
  connect(slaFlo.surf_b, livFlo.heaPorBac)
  connect(slaFlo.port_b,pum.port_a)
  connect(zon.TAir, conHea.TRoo)
  connect(slaFlo.port_b,pre.ports[1])
  connect(conHea.TSupSet, hea.TSet)
  connect(conCoo.on, booToRea.u)
  connect(zon.TAir, conCoo.TRoo)
  connect(conCoo.phiRoo, zon.phi)
  connect(conCoo.TSupSet, masFloSouCoo.T_in)
  connect(attFlo.heaPorBac, TSur.port)
  connect(TSur.T, conCoo.TSur)
  connect(zon.TAir, enaCoo.u1)
  connect(TSetSurCooOn.y, TSetSurCoo.u1)
  connect(TSetSurOff.y, TSetSurCoo.u3)
  connect(enaCoo.y, TSetSurCoo.u2)
  connect(TSetSurCoo.y, conCoo.TSurSet)
  connect(enaCoo.u2, TOffSet.y)
  connect(TOffSet.u, TSetRooHea.y)
end RadiantHeatingCooling_TSurface;