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
Conversation
Your on a roll. Is getFieldName on your list? I'd love that for eio reporting. |
OK, so I guess that would just be a reformatting of the fieldname passed to these functions. |
Do you mean pass in the epJSON field name "thermal_conductivity" and return the IDD field name "Thermal Conductivity"? |
Yes, I had an issue with getting field names during the sizer function refactor. |
And what happens when we change thermal_conductivity to material_thermal_conductivity? Are the field names frozen from here forward? I better get #8691 wrapped up quick. |
Seems it needs to be an index/vector, but then that index can't change in the future. |
Real64 coeff = state.dataInputProcessing->inputProcessor->getRealFieldValue( | ||
state, CurrentModuleObject, fields, objectSchemaProps, "air_mass_flow_coefficient_at_reference_conditions"); |
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.
@amirroth When trying to apply the new getFieldValue
functions in an actual getInput function, it seemed silly to have patterns like
Real64 coeff = 0.0;
state.dataInputProcessing->inputProcessor->getFieldValue(state, CurrentModuleObject, fields, objectSchemaProps, "air_mass_flow_coefficient_at_reference_conditions", coeff);
By moving to explicitly typed functions, this reduces to one line. Seems more readable.
Real64 coeff = state.dataInputProcessing->inputProcessor->getRealFieldValue(state, CurrentModuleObject, fields, objectSchemaProps, "air_mass_flow_coefficient_at_reference_conditions");
if (is_empty) { | ||
isDefaulted = findDefault(value, schema_field_obj); | ||
} else { | ||
value = -99999; // autosize and autocalculate |
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.
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.
auto tempFieldVal = fields.at("heating_supply_air_flow_rate");
if (tempFieldVal == "Autosize") {
loc_m_HeatingSAFMethod_SAFlow = DataSizing::AutoSize;
} else {
loc_m_HeatingSAFMethod_SAFlow = fields.at("heating_supply_air_flow_rate");
}
Do you need to check for?
\autosizable
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.
I'm not 100% certain about that. In getRealFieldvalue
I mimicked the code in findDefault
for handling \autosize. It seems a bit fragile to assume if there's a string in a numeric field then it's autosize (or autocalculate). I'm not sure if illegal use of autosize or nonesense words get trapped during initial input processing (and that could be different if the incoming file is idf vs epJSON). Need to do some testing on that.
numericFieldValue = | ||
state->dataInputProcessing->inputProcessor->getRealFieldValue(*state, obj_type1, wh1, objectSchemaProps, "heater_maximum_capacity"); | ||
EXPECT_EQ(numericFieldValue, -99999); | ||
// Even a field that is not autoszable will return -99999 here (assuming that gets checked upon epJSON input processing) |
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.
I guess it is safe to assume this will always be caught. I wonder if this is actually unit tested somewhere.
** Severe ** <root>[Building][Building][loads_convergence_tolerance_value] - Value type "string" for input "autosize" not permitted by 'type' constraint.
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.
Not sure why it says this input field type is "constraint"?
"loads_convergence_tolerance_value": {
"type": "number",
This is ready for review. There is a design doc that shows prototype getInput blocks using these new functions. It still seems like a lot of lines of code are required to set things up before processing the individual fields. Suggestions are welecome. |
} | ||
|
||
Real64 InputProcessor::getRealFieldValue( | ||
EnergyPlusData &state, std::string const &objectWord, json const &ep_object, json const &schema_obj_props, std::string const &fieldName) |
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.
Does it make sense to have separate functions for Real
and Int
?
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.
Good question. I started to do that and then realized that the epJSON schema does not differentiate between real and integer: "type": "number"
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.
getRealFieldVal could be changed to getNumericFieldVal if there is no differentiation between int and real. I wonder if returning a real will have any effect when reading into an int variable but since we do that now anyway with getObjectItem's Numbers(x) it shouldn't be a problem.
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.
If you wanted to return int
or Real
, you would need to have separate functions since they'd likely have the same input parameter signature and only change the return type.
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.
I will also say with the following, which I saw in an outdated comment in this PR:
Real64 coeff = 0.0;
state.dataInputProcessing->inputProcessor->getFieldValue(state, CurrentModuleObject, fields, objectSchemaProps, "air_mass_flow_coefficient_at_reference_conditions", coeff);
It does seem odd to not directly return the fieldValue, however, this function could be overloaded to include int
or string
as input types with their own unique functions. Such as below:
int coeff = 0.0;
state.dataInputProcessing->inputProcessor->getFieldValue(state, CurrentModuleObject, fields, objectSchemaProps, "air_mass_flow_coefficient_at_reference_conditions", coeff);
std::string coeff = "look a coefficient!";
state.dataInputProcessing->inputProcessor->getFieldValue(state, CurrentModuleObject, fields, objectSchemaProps, "air_mass_flow_coefficient_at_reference_conditions", coeff);
This means there is only ever one function name to call.
Otherwise, you'll need to have getRealFieldValue
, getIntFieldValue
, and getAlphaFieldValue
functions like below:
Real64 coeff = state.dataInputProcessing->inputProcessor->getRealFieldValue(state, CurrentModuleObject, fields, objectSchemaProps, "air_mass_flow_coefficient_at_reference_conditions");
// Load the material derived type from the input data. | ||
state.dataMaterial->Material(MaterNum).Group = RegularMaterial; | ||
state.dataMaterial->Material(MaterNum).Name = materialName; | ||
|
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);
and auto & ip = state.dataInputProcessing->inputProcessor;
will make the rest of statements in this function into one liners.
|
||
std::string roughness = state.dataInputProcessing->inputProcessor->getAlphaFieldValue( | ||
state, state.dataHeatBalMgr->CurrentModuleObject, objectFields, objectSchemaProps, "roughness"); | ||
ValidateMaterialRoughness(state, MaterNum, roughness, ErrorsFound); |
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.
Not for this PR, but just noting that ValidateMaterialRoughness
and functions of similar ilk will be going soon.
} 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Need to do something if isDefaulted
is false
?
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.
Another good question. This block of code is borrowed from elsewhere (somewhere in the getObjectItem
chain. isDefaulted
gets used elsewhere for some statistics on the number of defaulted fields in the input file. This could probably be simplified a bit here.
} | ||
|
||
Real64 InputProcessor::getRealFieldValue( | ||
EnergyPlusData &state, std::string const &objectWord, json const &ep_object, json const &schema_obj_props, std::string const &fieldName) |
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.
If you wanted to return int
or Real
, you would need to have separate functions since they'd likely have the same input parameter signature and only change the return type.
} | ||
|
||
Real64 InputProcessor::getRealFieldValue( | ||
EnergyPlusData &state, std::string const &objectWord, json const &ep_object, json const &schema_obj_props, std::string const &fieldName) |
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.
I will also say with the following, which I saw in an outdated comment in this PR:
Real64 coeff = 0.0;
state.dataInputProcessing->inputProcessor->getFieldValue(state, CurrentModuleObject, fields, objectSchemaProps, "air_mass_flow_coefficient_at_reference_conditions", coeff);
It does seem odd to not directly return the fieldValue, however, this function could be overloaded to include int
or string
as input types with their own unique functions. Such as below:
int coeff = 0.0;
state.dataInputProcessing->inputProcessor->getFieldValue(state, CurrentModuleObject, fields, objectSchemaProps, "air_mass_flow_coefficient_at_reference_conditions", coeff);
std::string coeff = "look a coefficient!";
state.dataInputProcessing->inputProcessor->getFieldValue(state, CurrentModuleObject, fields, objectSchemaProps, "air_mass_flow_coefficient_at_reference_conditions", coeff);
This means there is only ever one function name to call.
Otherwise, you'll need to have getRealFieldValue
, getIntFieldValue
, and getAlphaFieldValue
functions like below:
Real64 coeff = state.dataInputProcessing->inputProcessor->getRealFieldValue(state, CurrentModuleObject, fields, objectSchemaProps, "air_mass_flow_coefficient_at_reference_conditions");
return value; | ||
} | ||
|
||
json InputProcessor::getObjectSchemaProps(EnergyPlusData &state, std::string const &objectWord) |
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.
This will create a copy of the schema json everytime it is called. It should really return json const &
, especially since getPatternProperties
returns json const &
:
json const &InputProcessor::getPatternProperties(EnergyPlusData &state, json const &schema_obj)
and you return from getPatternProperties
json schema_obj_props = getPatternProperties(state, object_schema);
return schema_obj_props;
If this current function is called many times it will cause performance issues.
@mbadams5 Is there anywhere in the current input processing that distinguishes between int and real? |
@mjwitte No, but mostly that is an artifact of the old InputProcessor and IDD. The IDD does not distinguish between real and int, however, json schema can use |
@mbadams5 Actually, there are some fields in the IDD that are tagged with I think I've addressed your |
std::string InputProcessor::getAlphaFieldValue(json const &ep_object, json const &schema_obj_props, std::string const &fieldName) | ||
{ | ||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you convert this to an assert
statement instead of the FatalErrorMessage
? Asserts only work in debug builds, so is this something only developers will ever hit? If not, then it should probably be the FatalError.
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.
I changed to assert
because this is a developer error (same for the others). The function arguments for state
and objectType
were only being used for these error messages that should never happen unless the developer makes a mistake. So it seemed reasonable to use an assert
and drop those.
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.
@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 comment
The 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 schema_obj_props
. So empty
will be true if the field name does not exist in schema_obj_props
.
value = valuePair.first; | ||
isDefaulted = valuePair.second; | ||
} else { | ||
assert(false); // String value requested but field type in numeric |
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.
Same comment about change to assert
here. This assert
statement provides little information except for the inline comment.
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.
I can revert this, but I like the leaner function signature. If there's a better approach, I'm open to suggestions.
Real64 InputProcessor::getRealFieldValue(json const &ep_object, json const &schema_obj_props, std::string const &fieldName) | ||
{ | ||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Same question about change to assert
.
{ | ||
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 |
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.
Same question about change to assert
.
@mjwitte I see in the IDD where it has The |
Unit tests run to completion on Win64. This seems ready and any remaining items can be addressed as needed with follow up issues. I am a little surprised there is an indication of a speed improvement albeit small. I will wait on CI for a bit. |
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.
I see no significant issues. I am not sure int vs real functions is even an issue.
Failed unit test PackagedTerminalHP_VSCoils_Sizing on Mac is likely unrelated. |
Pull request overview
getObjectItem
.getObjectSchemaProps(state, CurrentModuleObject)
getAlphaFieldValue(state, CurrentModuleObject, objectFields, objectSchemaProps, "alpha_field_name")
getRealFieldValue(state, CurrentModuleObject, objectFields, objectSchemaProps, "real_field_name")
getIntFieldValue(state, CurrentModuleObject, objectFields, objectSchemaProps, "integer_field_name")
ToDos/Questions:
getObjectItem
?Look for more code re-use opportunities in InputProcessor (e.g. getDefaultValue also searches for the object and field schema)Pull Request Author
Add to this list or remove from it as applicable. This is a simple templated set of guidelines.
Reviewer
This will not be exhaustively relevant to every PR.