-
Notifications
You must be signed in to change notification settings - Fork 376
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
epJSON helper functions to get field values #8738
Changes from all commits
de3cf57
147ad9d
a6a34ed
4d5b8cb
9ddda8c
4f35d70
8c041ff
89ad8e5
6485bb4
b94cdf5
b9a7cc1
6f2a1ff
eb465d4
88916fe
a186722
d153e42
c51b17a
17244fb
55b3d88
abe0a36
3bc61ef
94da998
716831d
22debf6
013a58d
0da17b0
fca03ef
4c3a045
855a779
9132be6
c9328ff
97353a1
b7abee8
d718ebd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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? |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not for this PR, but just noting that |
||
|
||
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; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did you convert this to an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mjwitte can you give me an example of why schema_field_obj.empty() would ever be false? I get the misspelled field name issue but are there other instances? Inputs past min-fields? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see this is merged, but just for the record, this line is looking at the schema (not the user input) in |
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see in here where uppercase is set. Is getObjectItemValue where a string gets pushed to uppercase? Can retain case be used here so developers don't make a mistake? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Never mind.
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question about change to |
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to do something if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another good question. This block of code is borrowed from elsewhere (somewhere in the |
||
} else { | ||
value = -99999; // autosize and autocalculate | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess you assume here if !is_empty then the input is either blank or is set to Autosize. I explicitly tested for that but maybe with this method you don't have to test. Anything other than the word autosize would be caught by the inputprocessor.
Do you need to check for?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not 100% certain about that. In |
||
} | ||
} | ||
} 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"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure how you would do this only once but this one line, auto const &schema_properties = schema.at("properties");, is applicable to all objects. Here it is set up again for each call. |
||
const json &object_schema = schema_properties.at(objectWord); | ||
assert(!object_schema.empty()); // If this fails, the object type does not exist in the schema | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question about change to |
||
|
||
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; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a stylistic thing, but defining
auto & Mat = state.dataMaterial->Material(MaterNum);
andauto & ip = state.dataInputProcessing->inputProcessor;
will make the rest of statements in this function into one liners.