Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2361,19 +2361,64 @@ models for convection coefficients.
Material Surface Properties
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Three actuators are available for controlling the surface properties
material related to absorptance. Those material layers used in a
Construction object that lie at the outside and the inside of the
assembly determine the surface properties of a heat transfer surface. 
Actuators called “Material” are available with the control types
“Surface Property Solar Absorptance,” “Surface Property Thermal
Absorptance,” and “Surface Property Visible Absorptance.”  These are
dimensionless parameters between 0.0 and 1.0.  These actuators are
useful for modeling switchable coatings such as thermochromic paints.
Note that for a single-layer construction, both the inside and outside
properties will be overwritten. For overwriting outside properties
alone, please use “MaterialProperty:VariableAbsorptance” (see
InputOutputReference).
Nine actuators with the component type ``Material`` are available for
controlling material absorptance. The actuator unique identifier is a
material name, not a surface name. Therefore, an actuator affects every
opaque Construction in which that material is exposed on the
corresponding face. The first layer of a Construction supplies its
outside-face properties, and the last layer supplies its inside-face
properties. In a single-layer Construction, the same material supplies
both faces.

For each of the Solar, Thermal, and Visible properties, the available
control types are as follows:

- ``Surface Property <Property> Absorptance`` is the legacy control
type and applies the same value to both faces of the material.

- ``Surface Property <Property> Absorptance Outside Face`` applies
only when the material is the outside layer of a Construction.

- ``Surface Property <Property> Absorptance Inside Face`` applies only
when the material is the inside layer of a Construction.

A face-specific actuator takes precedence over a legacy actuator when
both are active for the same property and face. Setting the
face-specific actuator to ``Null`` relinquishes its control: an active
legacy actuator then supplies the value. If both actuators are
``Null``, EnergyPlus restores the value supplied by the material input.
The actuator values are dimensionless, and the effective value is
limited to the range 0.0001 through 0.9999.

For example, consider a single-layer Construction with an active legacy
Thermal actuator set to 0.30 and an active Inside Face Thermal actuator
set to 0.60. The effective outside-face value is 0.30 and the
inside-face value is 0.60. Setting the Inside Face actuator to ``Null``
makes both faces 0.30; subsequently setting the legacy actuator to
``Null`` restores the material input values.

Use the ``BeginZoneTimestepBeforeInitHeatBalance`` calling point to
apply a new value during the current zone timestep. A value assigned at
a later calling point is not applied to the surface heat balance until
the next initialization.

For a surface whose exposed materials have a referenced face-specific
Thermal actuator, EnergyPlus registers the ``Surface Thermal
Absorptance Outside Face`` and ``Surface Thermal Absorptance Inside
Face`` output variables. A referenced face-specific Solar actuator
similarly registers the ``Surface Solar Absorptance Outside Face`` and
``Surface Solar Absorptance Inside Face`` output variables. A property
without a referenced face-specific actuator retains its legacy
unqualified output-variable name.

These actuators are useful for modeling switchable coatings such as
thermochromic paints. For schedule- or curve-based control of Thermal
and Solar absorptance, ``MaterialProperty:VariableAbsorptance`` provides
a dedicated alternative. That object is evaluated after the EMS
material actuators at the surface level. If both mechanisms target the
same property and face, ``MaterialProperty:VariableAbsorptance``
determines the effective value; overlapping controls should therefore
be used only when this precedence is intended.

Surface Construction State
~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
97 changes: 72 additions & 25 deletions src/EnergyPlus/HeatBalanceSurfaceManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
#include <WCECommon.hpp>
#include <WCEMultiLayerOptics.hpp>
#include <WCESingleLayerOptics.hpp>
#include <WCETarcog.hpp>

// EnergyPlus Headers
#include <EnergyPlus/ChilledCeilingPanelSimple.hh>
Expand All @@ -82,15 +81,13 @@
#include <EnergyPlus/DataLoopNode.hh>
#include <EnergyPlus/DataMoistureBalance.hh>
#include <EnergyPlus/DataMoistureBalanceEMPD.hh>
#include <EnergyPlus/DataRoomAirModel.hh>
#include <EnergyPlus/DataRuntimeLanguage.hh>
#include <EnergyPlus/DataSizing.hh>
#include <EnergyPlus/DataSurfaces.hh>
#include <EnergyPlus/DataSystemVariables.hh>
#include <EnergyPlus/DataViewFactorInformation.hh>
#include <EnergyPlus/DataWindowEquivalentLayer.hh>
#include <EnergyPlus/DataZoneEnergyDemands.hh>
#include <EnergyPlus/DataZoneEquipment.hh>
#include <EnergyPlus/DaylightingDevices.hh>
#include <EnergyPlus/DaylightingManager.hh>
#include <EnergyPlus/DisplayRoutines.hh>
Expand Down Expand Up @@ -123,7 +120,6 @@
#include <EnergyPlus/ThermalComfort.hh>
#include <EnergyPlus/TranspiredCollector.hh>
#include <EnergyPlus/UtilityRoutines.hh>
#include <EnergyPlus/WindowComplexManager.hh>
#include <EnergyPlus/WindowEquivalentLayer.hh>
#include <EnergyPlus/WindowManager.hh>
#include <EnergyPlus/WindowManagerExteriorData.hh>
Expand Down Expand Up @@ -1697,6 +1693,16 @@ void AllocateSurfaceHeatBalArrays(EnergyPlusData &state)
state.dataHeatBalSurf->SurfAbsThermalInt.dimension(state.dataSurface->TotSurfaces, 0.0);

DisplayString(state, "Setting up Surface Reporting Variables");
auto materialUsesEMSActuator = [&state](Material::MaterialBase const *material, std::string_view controlType) {
return material != nullptr && std::any_of(state.dataRuntimeLang->EMSActuatorAvailable.begin(),
state.dataRuntimeLang->EMSActuatorAvailable.end(),
[material, controlType](auto const &actuator) {
return actuator.handleCount > 0 && Util::SameString(actuator.ComponentTypeName, "Material") &&
Util::SameString(actuator.UniqueIDName, material->Name) &&
Util::SameString(actuator.ControlTypeName, controlType);
});
};

// Setup surface report variables CurrentModuleObject='Opaque Surfaces'
for (int loop = 1; loop <= state.dataSurface->TotSurfaces; ++loop) {
auto &surface = state.dataSurface->Surface(loop);
Expand Down Expand Up @@ -2181,6 +2187,8 @@ void AllocateSurfaceHeatBalArrays(EnergyPlusData &state)
if (!construction.TypeIsWindow) {
bool useInsideThermalAbsorptance = false;
bool useInsideSolarAbsorptance = false;
int const outsideMaterialNum = construction.LayerPoint(1);
auto const *outsideMaterial = outsideMaterialNum > 0 ? state.dataMaterial->materials(outsideMaterialNum) : nullptr;
int const insideMaterialNum = construction.LayerPoint(construction.TotLayers);
if (insideMaterialNum > 0) {
auto const *insideMaterial = state.dataMaterial->materials(insideMaterialNum);
Expand All @@ -2194,8 +2202,12 @@ void AllocateSurfaceHeatBalArrays(EnergyPlusData &state)
insideVariableAbsorptanceAllowed && (insideMaterial->absorpVarCtrlSignalIn == Material::VariableAbsCtrlSignal::Scheduled
? insideMaterial->absorpSolarVarSchedIn != nullptr
: insideMaterial->absorpSolarVarCurveIn != nullptr);
useInsideThermalAbsorptance = insideMaterial->hasAbsorpThermalInputIn || useInsideThermalVariableAbsorptance;
useInsideSolarAbsorptance = insideMaterial->hasAbsorpSolarInputIn || useInsideSolarVariableAbsorptance;
bool const useThermalEMSActuator = materialUsesEMSActuator(outsideMaterial, "Surface Property Thermal Absorptance Outside Face") ||
materialUsesEMSActuator(insideMaterial, "Surface Property Thermal Absorptance Inside Face");
bool const useSolarEMSActuator = materialUsesEMSActuator(outsideMaterial, "Surface Property Solar Absorptance Outside Face") ||
materialUsesEMSActuator(insideMaterial, "Surface Property Solar Absorptance Inside Face");
useInsideThermalAbsorptance = insideMaterial->hasAbsorpThermalInputIn || useInsideThermalVariableAbsorptance || useThermalEMSActuator;
useInsideSolarAbsorptance = insideMaterial->hasAbsorpSolarInputIn || useInsideSolarVariableAbsorptance || useSolarEMSActuator;
}

std::string_view const thermalAbsorptanceName =
Expand Down Expand Up @@ -4817,37 +4829,72 @@ void InitEMSControlledSurfaceProperties(EnergyPlusData &state)

auto &s_mat = state.dataMaterial;

state.dataGlobal->AnySurfPropOverridesInModel = false;
// first determine if anything needs to be done, once yes, then always init
for (auto const *mat : s_mat->materials) {
if (mat->group != Material::Group::Regular) {
continue;
}
// Once an override has been active, continue initializing so setting the last actuator to Null restores the input values.
if (!state.dataGlobal->AnySurfPropOverridesInModel) {
for (auto const *mat : s_mat->materials) {
if (mat->group != Material::Group::Regular) {
continue;
}

if ((mat->AbsorpSolarEMSOverrideOn) || (mat->AbsorpThermalEMSOverrideOn) || (mat->AbsorpVisibleEMSOverrideOn)) {
state.dataGlobal->AnySurfPropOverridesInModel = true;
break;
if ((mat->AbsorpSolarEMSOverrideOn) || (mat->AbsorpThermalEMSOverrideOn) || (mat->AbsorpVisibleEMSOverrideOn) ||
(mat->AbsorpSolarOutEMSOverrideOn) || (mat->AbsorpThermalOutEMSOverrideOn) || (mat->AbsorpVisibleOutEMSOverrideOn) ||
(mat->AbsorpSolarInEMSOverrideOn) || (mat->AbsorpThermalInEMSOverrideOn) || (mat->AbsorpVisibleInEMSOverrideOn)) {
state.dataGlobal->AnySurfPropOverridesInModel = true;
break;
}
}
}

if (!state.dataGlobal->AnySurfPropOverridesInModel) {
return; // quick return if nothing has ever needed to be done
}

// first, loop over materials
// why is this a second loop?
auto const getEMSOverrideValue =
[](Real64 inputValue, bool legacyOverrideOn, Real64 legacyOverrideValue, bool faceOverrideOn, Real64 faceOverrideValue) {
if (faceOverrideOn) {
return std::clamp(faceOverrideValue, 0.0001, 0.9999);
}
if (legacyOverrideOn) {
return std::clamp(legacyOverrideValue, 0.0001, 0.9999);
}
return inputValue;
};

// First, loop over materials. Face-specific actuators take precedence over legacy both-face actuators.
for (auto *mat : s_mat->materials) {
if (mat->group != Material::Group::Regular) {
continue;
}
mat->AbsorpSolarOut = mat->AbsorpSolarEMSOverrideOn ? max(min(mat->AbsorpSolarEMSOverride, 0.9999), 0.0001) : mat->AbsorpSolarInputOut;
mat->AbsorpThermalOut =
mat->AbsorpThermalEMSOverrideOn ? max(min(mat->AbsorpThermalEMSOverride, 0.9999), 0.0001) : mat->AbsorpThermalInputOut;
mat->AbsorpVisibleOut =
mat->AbsorpVisibleEMSOverrideOn ? max(min(mat->AbsorpVisibleEMSOverride, 0.9999), 0.0001) : mat->AbsorpVisibleInputOut;
mat->AbsorpSolarIn = mat->AbsorpSolarEMSOverrideOn ? max(min(mat->AbsorpSolarEMSOverride, 0.9999), 0.0001) : mat->AbsorpSolarInputIn;
mat->AbsorpThermalIn = mat->AbsorpThermalEMSOverrideOn ? max(min(mat->AbsorpThermalEMSOverride, 0.9999), 0.0001) : mat->AbsorpThermalInputIn;
mat->AbsorpVisibleIn = mat->AbsorpVisibleEMSOverrideOn ? max(min(mat->AbsorpVisibleEMSOverride, 0.9999), 0.0001) : mat->AbsorpVisibleInputIn;
mat->AbsorpSolarOut = getEMSOverrideValue(mat->AbsorpSolarInputOut,
mat->AbsorpSolarEMSOverrideOn,
mat->AbsorpSolarEMSOverride,
mat->AbsorpSolarOutEMSOverrideOn,
mat->AbsorpSolarOutEMSOverride);
mat->AbsorpThermalOut = getEMSOverrideValue(mat->AbsorpThermalInputOut,
mat->AbsorpThermalEMSOverrideOn,
mat->AbsorpThermalEMSOverride,
mat->AbsorpThermalOutEMSOverrideOn,
mat->AbsorpThermalOutEMSOverride);
mat->AbsorpVisibleOut = getEMSOverrideValue(mat->AbsorpVisibleInputOut,
mat->AbsorpVisibleEMSOverrideOn,
mat->AbsorpVisibleEMSOverride,
mat->AbsorpVisibleOutEMSOverrideOn,
mat->AbsorpVisibleOutEMSOverride);
mat->AbsorpSolarIn = getEMSOverrideValue(mat->AbsorpSolarInputIn,
mat->AbsorpSolarEMSOverrideOn,
mat->AbsorpSolarEMSOverride,
mat->AbsorpSolarInEMSOverrideOn,
mat->AbsorpSolarInEMSOverride);
mat->AbsorpThermalIn = getEMSOverrideValue(mat->AbsorpThermalInputIn,
mat->AbsorpThermalEMSOverrideOn,
mat->AbsorpThermalEMSOverride,
mat->AbsorpThermalInEMSOverrideOn,
mat->AbsorpThermalInEMSOverride);
mat->AbsorpVisibleIn = getEMSOverrideValue(mat->AbsorpVisibleInputIn,
mat->AbsorpVisibleEMSOverrideOn,
mat->AbsorpVisibleEMSOverride,
mat->AbsorpVisibleInEMSOverrideOn,
mat->AbsorpVisibleInEMSOverride);
} // loop over materials

// second, loop over constructions
Expand Down
42 changes: 42 additions & 0 deletions src/EnergyPlus/Material.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2857,6 +2857,48 @@ void GetMaterialData(EnergyPlusData &state, bool &ErrorsFound) // set to true if
"[ ]",
mat->AbsorpVisibleEMSOverrideOn,
mat->AbsorpVisibleEMSOverride);
SetupEMSActuator(state,
"Material",
mat->Name,
"Surface Property Solar Absorptance Outside Face",
"[ ]",
mat->AbsorpSolarOutEMSOverrideOn,
mat->AbsorpSolarOutEMSOverride);
SetupEMSActuator(state,
"Material",
mat->Name,
"Surface Property Thermal Absorptance Outside Face",
"[ ]",
mat->AbsorpThermalOutEMSOverrideOn,
mat->AbsorpThermalOutEMSOverride);
SetupEMSActuator(state,
"Material",
mat->Name,
"Surface Property Visible Absorptance Outside Face",
"[ ]",
mat->AbsorpVisibleOutEMSOverrideOn,
mat->AbsorpVisibleOutEMSOverride);
SetupEMSActuator(state,
"Material",
mat->Name,
"Surface Property Solar Absorptance Inside Face",
"[ ]",
mat->AbsorpSolarInEMSOverrideOn,
mat->AbsorpSolarInEMSOverride);
SetupEMSActuator(state,
"Material",
mat->Name,
"Surface Property Thermal Absorptance Inside Face",
"[ ]",
mat->AbsorpThermalInEMSOverrideOn,
mat->AbsorpThermalInEMSOverride);
SetupEMSActuator(state,
"Material",
mat->Name,
"Surface Property Visible Absorptance Inside Face",
"[ ]",
mat->AbsorpVisibleInEMSOverrideOn,
mat->AbsorpVisibleInEMSOverride);
}
}

Expand Down
Loading
Loading