Skip to content

Commit

Permalink
Merge pull request #8738 from NREL/epJSONhelpers
Browse files Browse the repository at this point in the history
Thank you @mjwitte for this effort.
  • Loading branch information
rraustad committed Jun 12, 2021
2 parents e7a3474 + d718ebd commit 5358d3e
Show file tree
Hide file tree
Showing 6 changed files with 614 additions and 152 deletions.
97 changes: 97 additions & 0 deletions design/FY2021/Design-epJSONHelpers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
epJSON helper functions to get field values
================

**M.J. Witte, GARD Analytics, Inc.**

- Original, May 17, 2021
- Revision Date


## Objective ##
From issue [#7632](https://github.com/NREL/EnergyPlus/issues/7632)

It would be useful to have an input processor function that

1. Takes a field name, validates the field name, and returns the input value or it's default.
2. To validate field name, will likely have to pass in cCurrentObjectItem (object class name) as an argument.
3. If the field doesn't have a declared default, then numeric should return zero, and string should return blank.
4. The EIRWWHP test for no default and no input is unusual, but for objects that want to test this for a given field, perhaps this function could return a status flag? (0 = user input, 1 = default, -1 = no user input or default) or ???

For a field where you simply want to get the value or the default and move on, the goal would be something like:

`loc_m_HeatingCoilName = inputProcessor->getFieldValue("heating_coil_name", fields, cCurrentObjectItem, status);`

## Design ##
Here's a prototype `getInput` function using 3 new functions:

getObjectSchemaProps(state, CurrentModuleObject)
getRealFieldValue(state, CurrentModuleObject, objectFields, objectSchemaProps, "field_name")
getAlphaFieldValue(state, CurrentModuleObject, objectFields, objectSchemaProps, "field_name")

// Get the schema properties for the current object type
InputProcessor::json objectSchemaProps;
CurrentModuleObject = "AirflowNetwork:MultiZone:Surface:Crack";
objectSchemaProps = state.dataInputProcessing->inputProcessor->getObjectSchemaProps(state, CurrentModuleObject);

// Get all instances of this object type from the input file
auto instances = state.dataInputProcessing->inputProcessor->epJSON.find(CurrentModuleObject);

if (instances != state.dataInputProcessing->inputProcessor->epJSON.end()) {
auto &instancesValue = instances.value();

for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
auto const &objectFields = instance.value();
auto const &thisObjectName = UtilityRoutines::MakeUPPERCase(instance.key());
state.dataInputProcessing->inputProcessor->markObjectAsUsed(CurrentModuleObject, instance.key());
// For incoming idf, maintain object order
++instanceCounter;
itemNum = state.dataInputProcessing->inputProcessor->getIDFObjNum(state, state.dataHeatBalMgr->CurrentModuleObject, instanceCounter);

// Get simple input fields (if blank will return default or blank or zero)
Real64 crack(itemNum).coeff = state.dataInputProcessing->inputProcessor->getRealFieldValue(
state, CurrentModuleObject, objectFields, objectSchemaProps, "air_mass_flow_coefficient_at_reference_conditions");
std::string crack(itemNum).referenceCrackConditionsName = state.dataInputProcessing->inputProcessor->getAlphaFieldValue(
state, CurrentModuleObject, objectFields, objectSchemaProps, "reference_crack_conditions");
}
}

Here's a prototype section for retrieving extensible fields:
For this object, the extensible group is named "managers".
The extensible group name and field names can be found in the schema (Energy+.schema.epJSON).

auto extensibles = objectFields.find("managers");
auto const &extensionSchemaProps = objectSchemaProps["managers"]["items"]["properties"];
if (extensibles != objectFields.end()) {
auto extensiblesArray = extensibles.value();
int numExtensibles = extensiblesArray.size();
// Allocate and initialize object arrays that are sized by the number of extensible field groups
state.dataSystemAvailabilityManager->SysAvailMgrListData(Item).NumItems = numExtensibles;
state.dataSystemAvailabilityManager->SysAvailMgrListData(Item).AvailManagerName.allocate(numExtensibles);
state.dataSystemAvailabilityManager->SysAvailMgrListData(Item).cAvailManagerType.allocate(numExtensibles);
state.dataSystemAvailabilityManager->SysAvailMgrListData(Item).AvailManagerType.allocate(numExtensibles);
for (int extItem = 1; extItem <= numExtensibles; ++extItem) {
state.dataSystemAvailabilityManager->SysAvailMgrListData(Item).AvailManagerName = "";
state.dataSystemAvailabilityManager->SysAvailMgrListData(Item).cAvailManagerType = "";
state.dataSystemAvailabilityManager->SysAvailMgrListData(Item).AvailManagerType = 0;
}

// Loop through each extensible field group
int listItem = 0;
for (auto extensibleInstance : extensiblesArray) {
++listItem;
state.dataSystemAvailabilityManager->SysAvailMgrListData(Item).AvailManagerName(listItem) =
state.dataInputProcessing->inputProcessor->getAlphaFieldValue(
state, cCurrentModuleObject, extensibleInstance, extensionSchemaProps, "availability_manager_name");
std::string availManagerObjType = state.dataInputProcessing->inputProcessor->getAlphaFieldValue(
state, cCurrentModuleObject, extensibleInstance, extensionSchemaProps, "availability_manager_object_type");
state.dataSystemAvailabilityManager->SysAvailMgrListData(Item).cAvailManagerType(listItem) = availManagerObjType;
state.dataSystemAvailabilityManager->SysAvailMgrListData(Item).AvailManagerType(listItem) =
ValidateAndSetSysAvailabilityManagerType(state, availManagerObjType);
}
}

## Questions ##
1. Do we want more functions to help with some of the setup shown here?
115 changes: 51 additions & 64 deletions src/EnergyPlus/HeatBalanceManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1562,75 +1562,62 @@ namespace HeatBalanceManager {
MaterNum = 0;

// Regular Materials
auto &ip = state.dataInputProcessing->inputProcessor;

state.dataHeatBalMgr->CurrentModuleObject = "Material";
for (Loop = 1; Loop <= RegMat; ++Loop) {
auto const instances = ip->epJSON.find(state.dataHeatBalMgr->CurrentModuleObject);
if (instances != ip->epJSON.end()) {
auto const &objectSchemaProps = ip->getObjectSchemaProps(state, state.dataHeatBalMgr->CurrentModuleObject);

// Call Input Get routine to retrieve material data
state.dataInputProcessing->inputProcessor->getObjectItem(state,
state.dataHeatBalMgr->CurrentModuleObject,
Loop,
MaterialNames,
MaterialNumAlpha,
MaterialProps,
MaterialNumProp,
IOStat,
state.dataIPShortCut->lNumericFieldBlanks,
state.dataIPShortCut->lAlphaFieldBlanks,
state.dataIPShortCut->cAlphaFieldNames,
state.dataIPShortCut->cNumericFieldNames);
if (GlobalNames::VerifyUniqueInterObjectName(state,
state.dataHeatBalMgr->UniqueMaterialNames,
MaterialNames(1),
state.dataHeatBalMgr->CurrentModuleObject,
state.dataIPShortCut->cAlphaFieldNames(1),
ErrorsFound)) {
continue;
}
// Load the material derived type from the input data.
++MaterNum;
state.dataMaterial->Material(MaterNum).Group = RegularMaterial;
state.dataMaterial->Material(MaterNum).Name = MaterialNames(1);

ValidateMaterialRoughness(state, MaterNum, MaterialNames(2), ErrorsFound);

state.dataMaterial->Material(MaterNum).Thickness = MaterialProps(1);
state.dataMaterial->Material(MaterNum).Conductivity = MaterialProps(2);
state.dataMaterial->Material(MaterNum).Density = MaterialProps(3);
state.dataMaterial->Material(MaterNum).SpecHeat = MaterialProps(4);
// min fields is 6 -- previous four will be there
if (MaterialNumProp >= 5) {
state.dataMaterial->Material(MaterNum).AbsorpThermal = MaterialProps(5);
state.dataMaterial->Material(MaterNum).AbsorpThermalInput = MaterialProps(5);
} else {
state.dataMaterial->Material(MaterNum).AbsorpThermal = 0.9;
state.dataMaterial->Material(MaterNum).AbsorpThermalInput = 0.9;
}
if (MaterialNumProp >= 6) {
state.dataMaterial->Material(MaterNum).AbsorpSolar = MaterialProps(6);
state.dataMaterial->Material(MaterNum).AbsorpSolarInput = MaterialProps(6);
} else {
state.dataMaterial->Material(MaterNum).AbsorpSolar = 0.7;
state.dataMaterial->Material(MaterNum).AbsorpSolarInput = 0.7;
}
if (MaterialNumProp >= 7) {
state.dataMaterial->Material(MaterNum).AbsorpVisible = MaterialProps(7);
state.dataMaterial->Material(MaterNum).AbsorpVisibleInput = MaterialProps(7);
} else {
state.dataMaterial->Material(MaterNum).AbsorpVisible = 0.7;
state.dataMaterial->Material(MaterNum).AbsorpVisibleInput = 0.7;
}

if (state.dataMaterial->Material(MaterNum).Conductivity > 0.0) {
state.dataHeatBal->NominalR(MaterNum) =
state.dataMaterial->Material(MaterNum).Thickness / state.dataMaterial->Material(MaterNum).Conductivity;
state.dataMaterial->Material(MaterNum).Resistance = state.dataHeatBal->NominalR(MaterNum);
} else {
ShowSevereError(state, "Positive thermal conductivity required for material " + state.dataMaterial->Material(MaterNum).Name);
ErrorsFound = true;
int counter = 0;
auto &instancesValue = instances.value();
for (auto instance = instancesValue.begin(); instance != instancesValue.end(); ++instance) {
auto const &objectFields = instance.value();
auto const &thisObjectName = UtilityRoutines::MakeUPPERCase(instance.key());
ip->markObjectAsUsed(state.dataHeatBalMgr->CurrentModuleObject, instance.key());
std::string materialName = thisObjectName;

if (GlobalNames::VerifyUniqueInterObjectName(state,
state.dataHeatBalMgr->UniqueMaterialNames,
materialName,
state.dataHeatBalMgr->CurrentModuleObject,
state.dataIPShortCut->cAlphaFieldNames(1),
ErrorsFound)) {
continue;
}
// For incoming idf, maintain object order
++counter;
MaterNum = ip->getIDFObjNum(state, state.dataHeatBalMgr->CurrentModuleObject, counter);

// Load the material derived type from the input data.
auto &thisMaterial = state.dataMaterial->Material(MaterNum);
thisMaterial.Group = RegularMaterial;
thisMaterial.Name = materialName;

std::string roughness = ip->getAlphaFieldValue(objectFields, objectSchemaProps, "roughness");
ValidateMaterialRoughness(state, MaterNum, roughness, ErrorsFound);

thisMaterial.Thickness = ip->getRealFieldValue(objectFields, objectSchemaProps, "thickness");
thisMaterial.Conductivity = ip->getRealFieldValue(objectFields, objectSchemaProps, "conductivity");
thisMaterial.Density = ip->getRealFieldValue(objectFields, objectSchemaProps, "density");
thisMaterial.SpecHeat = ip->getRealFieldValue(objectFields, objectSchemaProps, "specific_heat");
thisMaterial.AbsorpThermal = ip->getRealFieldValue(objectFields, objectSchemaProps, "thermal_absorptance");
thisMaterial.AbsorpThermalInput = thisMaterial.AbsorpThermal;
thisMaterial.AbsorpSolar = ip->getRealFieldValue(objectFields, objectSchemaProps, "solar_absorptance");
thisMaterial.AbsorpSolarInput = thisMaterial.AbsorpSolar;
thisMaterial.AbsorpVisible = ip->getRealFieldValue(objectFields, objectSchemaProps, "visible_absorptance");
thisMaterial.AbsorpVisibleInput = thisMaterial.AbsorpVisible;

if (thisMaterial.Conductivity > 0.0) {
state.dataHeatBal->NominalR(MaterNum) = thisMaterial.Thickness / thisMaterial.Conductivity;
thisMaterial.Resistance = state.dataHeatBal->NominalR(MaterNum);
} else {
ShowSevereError(state, "Positive thermal conductivity required for material " + thisMaterial.Name);
ErrorsFound = true;
}
}
MaterNum = counter; // This works here, because this is the first material type processed
}

// Add the 6" heavy concrete for constructions defined with F or C factor method
if (TotFfactorConstructs + TotCfactorConstructs >= 1) {
++MaterNum;
Expand Down
111 changes: 111 additions & 0 deletions src/EnergyPlus/InputProcessing/InputProcessor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,117 @@ bool InputProcessor::getDefaultValue(EnergyPlusData &state, std::string const &o
return defaultFound;
}

std::string InputProcessor::getAlphaFieldValue(json const &ep_object, json const &schema_obj_props, std::string const &fieldName)
{
// Return the value of fieldName in ep_object as a string.
// If the field is not present in ep_object then return its default if there is one, or return an empty string
auto const &schema_field_obj = schema_obj_props[fieldName];
assert(!schema_field_obj.empty()); // Check that field name exists in the schema for this object type
bool isDefaulted = false;
std::string value;
auto it = ep_object.find(fieldName);
if (it != ep_object.end()) {
auto const &field_value = it.value();
if (field_value.is_string()) {
auto valuePair = getObjectItemValue(field_value.get<std::string>(), schema_field_obj);
value = valuePair.first;
isDefaulted = valuePair.second;
} else {
assert(false); // String value requested but field type is numeric
}
} else {
isDefaulted = findDefault(value, schema_field_obj);
if (!isDefaulted) {
value = "";
}
}
return value;
}

Real64 InputProcessor::getRealFieldValue(json const &ep_object, json const &schema_obj_props, std::string const &fieldName)
{
// Return the value of fieldName in ep_object as a Real64.
// If the field value is a string, then assum autosize and return -99999.
// If the field is not present in ep_object then return its default if there is one, or return 0.0
auto const &schema_field_obj = schema_obj_props[fieldName];
assert(!schema_field_obj.empty()); // Check that field name exists in the schema for this object type
bool isDefaulted = false;
Real64 value = 0.0;
auto it = ep_object.find(fieldName);
if (it != ep_object.end()) {
auto const &field_value = it.value();
if (field_value.is_number()) {
if (field_value.is_number_integer()) {
value = field_value.get<std::int64_t>();
} else {
value = field_value.get<double>();
}
} else {
bool is_empty = field_value.get<std::string>().empty();
if (is_empty) {
isDefaulted = findDefault(value, schema_field_obj);
} else {
value = -99999; // autosize and autocalculate
}
}
} else {
isDefaulted = findDefault(value, schema_field_obj);
if (!isDefaulted) {
value = 0.0;
}
}
return value;
}

int InputProcessor::getIntFieldValue(json const &ep_object, json const &schema_obj_props, std::string const &fieldName)
{
// Return the value of fieldName in ep_object as an integer (rounded to nearest integer if the input value is real).
// If the field value is a string, then assume autosize or autocalulate and return -99999.
// If the field is not present in ep_object then return its default if there is one, or return 0

auto const &schema_field_obj = schema_obj_props[fieldName];
assert(!schema_field_obj.empty()); // Check that field name exists in the schema for this object type
bool isDefaulted = false;
int value = 0;
Real64 defaultValue = 0.0;
auto it = ep_object.find(fieldName);
if (it != ep_object.end()) {
auto const &field_value = it.value();
if (field_value.is_number()) {
if (field_value.is_number_integer()) {
value = field_value.get<std::int64_t>();
} else {
value = nint(field_value.get<double>());
}
} else {
bool is_empty = field_value.get<std::string>().empty();
if (is_empty) {
isDefaulted = findDefault(defaultValue, schema_field_obj);
} else {
value = -99999; // autosize and autocalculate
}
}
} else {
isDefaulted = findDefault(defaultValue, schema_field_obj);
if (isDefaulted) {
value = nint(defaultValue);
} else {
value = 0.0;
}
}
return value;
}

const json &InputProcessor::getObjectSchemaProps(EnergyPlusData &state, std::string const &objectWord)
{
auto const &schema_properties = schema.at("properties");
const json &object_schema = schema_properties.at(objectWord);
assert(!object_schema.empty()); // If this fails, the object type does not exist in the schema

auto const &schema_obj_props = getPatternProperties(state, object_schema);
return schema_obj_props;
}

std::pair<std::string, bool> InputProcessor::getObjectItemValue(std::string const &field_value, json const &schema_field_obj)
{
std::pair<std::string, bool> output;
Expand Down
8 changes: 8 additions & 0 deletions src/EnergyPlus/InputProcessing/InputProcessor.hh
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ public:

bool getDefaultValue(EnergyPlusData &state, std::string const &objectWord, std::string const &fieldName, std::string &value);

std::string getAlphaFieldValue(json const &ep_object, json const &schema_obj_props, std::string const &fieldName);

Real64 getRealFieldValue(json const &ep_object, json const &schema_obj_props, std::string const &fieldName);

int getIntFieldValue(json const &ep_object, json const &schema_obj_props, std::string const &fieldName);

const json &getObjectSchemaProps(EnergyPlusData &state, std::string const &objectWord);

std::pair<std::string, bool> getObjectItemValue(std::string const &field_value, json const &schema_field_obj);

void getObjectItem(EnergyPlusData &state,
Expand Down

5 comments on commit 5358d3e

@nrel-bot-3
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

develop (rraustad) - x86_64-MacOS-10.15-clang-11.0.0: OK (2369 of 2370 tests passed, 0 test warnings)

Failures:\n

EnergyPlusFixture Test Summary

  • Passed: 1145
  • Failed: 1

Build Badge Test Badge

@nrel-bot-2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

develop (rraustad) - x86_64-Linux-Ubuntu-18.04-gcc-7.5: OK (2390 of 2390 tests passed, 0 test warnings)

Build Badge Test Badge

@nrel-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

develop (rraustad) - Win64-Windows-10-VisualStudio-16: OK (2341 of 2341 tests passed, 0 test warnings)

Build Badge Test Badge

@nrel-bot-2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

develop (rraustad) - x86_64-Linux-Ubuntu-18.04-gcc-7.5-UnitTestsCoverage-Debug: OK (1646 of 1646 tests passed, 0 test warnings)

Build Badge Test Badge Coverage Badge

@nrel-bot-2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

develop (rraustad) - x86_64-Linux-Ubuntu-18.04-gcc-7.5-IntegrationCoverage-Debug: OK (725 of 726 tests passed, 0 test warnings)

Failures:\n

integration Test Summary

  • Passed: 725
  • Timeout: 1

Build Badge Test Badge Coverage Badge

Please sign in to comment.