diff --git a/src/EnergyPlus/HeatBalanceManager.cc b/src/EnergyPlus/HeatBalanceManager.cc index faeed7bf571..23e7a3a5bff 100644 --- a/src/EnergyPlus/HeatBalanceManager.cc +++ b/src/EnergyPlus/HeatBalanceManager.cc @@ -674,7 +674,7 @@ namespace HeatBalanceManager { state.dataHeatBal->TempConvergTol = BuildingNumbers(3); if (state.dataHeatBal->TempConvergTol <= 0.0) { ShowSevereError(state, - format("{}{}: {} value invalid, [{:.3R}]", RoutineName, state.dataHeatBalMgr->CurrentModuleObject, state.dataIPShortCut->cNumericFieldNames(2), state.dataHeatBal->TempConvergTol)); + format("{}{}: {} value invalid, [{:.3R}]", RoutineName, state.dataHeatBalMgr->CurrentModuleObject, state.dataIPShortCut->cNumericFieldNames(3), state.dataHeatBal->TempConvergTol)); ErrorsFound = true; } // Solar Distribution diff --git a/src/EnergyPlus/InputProcessing/InputProcessor.cc b/src/EnergyPlus/InputProcessing/InputProcessor.cc index aabfd9999ec..ce56362a369 100644 --- a/src/EnergyPlus/InputProcessing/InputProcessor.cc +++ b/src/EnergyPlus/InputProcessing/InputProcessor.cc @@ -563,7 +563,8 @@ const json &InputProcessor::getObjectInstances(std::string const &ObjType) return epJSON.find(ObjType).value(); } -InputProcessor::MaxFields InputProcessor::findMaxFields(EnergyPlusData &state, json const &ep_object, std::string const &extension_key, json const &legacy_idd) +InputProcessor::MaxFields InputProcessor::findMaxFields( + EnergyPlusData &state, json const &ep_object, std::string const &extension_key, json const &legacy_idd, std::size_t const min_fields) { InputProcessor::MaxFields maxFields; if (!state.dataGlobal->isEpJSON) { @@ -577,6 +578,8 @@ InputProcessor::MaxFields InputProcessor::findMaxFields(EnergyPlusData &state, j } } else { auto const &legacy_idd_fields = legacy_idd["fields"]; + // start with at least min_fields as the number of fields + maxFields.max_fields = min_fields; for (auto const &field : ep_object.items()) { auto const &field_key = field.key(); if (field_key == extension_key) continue; @@ -691,13 +694,13 @@ void InputProcessor::setObjectItemValue(EnergyPlusData &state, } } if (field_type == "a") { - if (within_max_fields) NumAlphas++; + if (within_max_fields) NumAlphas = alpha_index; if (is_AlphaFieldNames) { AlphaFieldNames()(alpha_index) = (state.dataGlobal->isEpJSON) ? field : legacy_field_info.at("field_name").get(); } alpha_index++; } else if (field_type == "n") { - if (within_max_fields) NumNumbers++; + if (within_max_fields) NumNumbers = numeric_index; if (is_NumericFieldNames) { NumericFieldNames()(numeric_index) = (state.dataGlobal->isEpJSON) ? field : legacy_field_info.at("field_name").get(); } @@ -764,6 +767,11 @@ void InputProcessor::getObjectItem(EnergyPlusData &state, auto const &legacy_idd_fields = legacy_idd["fields"]; auto const &schema_name_field = epJSON_schema_it_val.find("name"); auto const has_idd_name_field = schema_name_field != epJSON_schema_it_val.end(); + auto const &found_min_fields = epJSON_schema_it_val.find("min_fields"); + size_t min_fields = 0; + if (found_min_fields != epJSON_schema_it_val.end()) { + min_fields = found_min_fields.value(); + } auto key = legacy_idd.find("extension"); std::string extension_key; @@ -777,7 +785,7 @@ void InputProcessor::getObjectItem(EnergyPlusData &state, int alpha_index = 1; int numeric_index = 1; - auto maxFields = findMaxFields(state, obj_val, extension_key, legacy_idd); + auto maxFields = findMaxFields(state, obj_val, extension_key, legacy_idd, min_fields); Alphas = ""; Numbers = 0; diff --git a/src/EnergyPlus/InputProcessing/InputProcessor.hh b/src/EnergyPlus/InputProcessing/InputProcessor.hh index e2b5c4827eb..0fcece48502 100644 --- a/src/EnergyPlus/InputProcessing/InputProcessor.hh +++ b/src/EnergyPlus/InputProcessing/InputProcessor.hh @@ -242,7 +242,8 @@ private: std::size_t max_extensible_fields = 0; }; - MaxFields findMaxFields(EnergyPlusData &state, json const &ep_object, std::string const &extension_key, json const &legacy_idd); + MaxFields findMaxFields( + EnergyPlusData &state, json const &ep_object, std::string const &extension_key, json const &legacy_idd, std::size_t const min_fields); void setObjectItemValue(EnergyPlusData &state, json const &ep_object, diff --git a/testfiles/RefBldgMediumOfficeNew2004_Chicago_epJSON.epJSON b/testfiles/RefBldgMediumOfficeNew2004_Chicago_epJSON.epJSON index 398a142502a..5a7a60447fb 100644 --- a/testfiles/RefBldgMediumOfficeNew2004_Chicago_epJSON.epJSON +++ b/testfiles/RefBldgMediumOfficeNew2004_Chicago_epJSON.epJSON @@ -6897,11 +6897,11 @@ "variable_details": [ { "aggregation_type_for_variable_or_meter": "SumOrAverage", - "variable_or_meter_name": "Cooling Coil Electric Power" + "variable_or_meter_name": "Cooling Coil Electricity Rate" }, { "aggregation_type_for_variable_or_meter": "Maximum", - "variable_or_meter_name": "Cooling Coil Electric Power" + "variable_or_meter_name": "Cooling Coil Electricity Rate" } ] }, @@ -6943,11 +6943,11 @@ "variable_details": [ { "aggregation_type_for_variable_or_meter": "SumOrAverage", - "variable_or_meter_name": "Fan Electric Power" + "variable_or_meter_name": "Fan Electricity Rate" }, { "aggregation_type_for_variable_or_meter": "Maximum", - "variable_or_meter_name": "Fan Electric Power" + "variable_or_meter_name": "Fan Electricity Rate" } ] }, diff --git a/tst/EnergyPlus/unit/InputProcessor.unit.cc b/tst/EnergyPlus/unit/InputProcessor.unit.cc index d1d969b5d8b..53e17bb0842 100644 --- a/tst/EnergyPlus/unit/InputProcessor.unit.cc +++ b/tst/EnergyPlus/unit/InputProcessor.unit.cc @@ -52,6 +52,8 @@ // EnergyPlus Headers #include "Fixtures/InputProcessorFixture.hh" +#include +#include #include #include #include @@ -4223,6 +4225,103 @@ TEST_F(InputProcessorFixture, reportIDFRecordsStats_extensible_fields) } +TEST_F(InputProcessorFixture, epJSONgetObjectItem_minfields) +{ + + json root; + std::string obj_name1 = "Building"; + std::string name1 = "Building 1"; + json bldg1 = {{"loads_convergence_tolerance_value", 0.1}, {"terrain", "Ocean"}}; + EXPECT_TRUE(bldg1.is_object()); + root[obj_name1][name1] = bldg1; + + std::string obj_name2 = "Material:NoMass"; + std::string name2 = "Standard insulation_01"; + json mat1 = {{"name", name1}, {"roughness", "MediumRough"}, {"thermal_resistance", 2.0}, {"solar_absorptance", 0.5}}; + EXPECT_TRUE(mat1.is_object()); + root[obj_name2][name2] = mat1; + + inputProcessor->epJSON = root; + // getEpJSON(); + + int numAlphas = 0; + int numNumbers = 0; + int ioStat = 0; + state->dataGlobal->isEpJSON = true; + inputProcessor->initializeMaps(); + + int maxAlphas = 20; + int maxNumbers = 20; + state->dataIPShortCut->lNumericFieldBlanks.allocate(maxNumbers); + state->dataIPShortCut->lAlphaFieldBlanks.allocate(maxAlphas); + state->dataIPShortCut->cAlphaFieldNames.allocate(maxAlphas); + state->dataIPShortCut->cNumericFieldNames.allocate(maxNumbers); + state->dataIPShortCut->cAlphaArgs.allocate(maxAlphas); + state->dataIPShortCut->rNumericArgs.allocate(maxNumbers); + state->dataIPShortCut->lNumericFieldBlanks = false; + state->dataIPShortCut->lAlphaFieldBlanks = false; + state->dataIPShortCut->cAlphaFieldNames = " "; + state->dataIPShortCut->cNumericFieldNames = " "; + state->dataIPShortCut->cAlphaArgs = " "; + state->dataIPShortCut->rNumericArgs = 0.0; + + inputProcessor->getObjectItem(*state, + obj_name1, + 1, + state->dataIPShortCut->cAlphaArgs, + numAlphas, + state->dataIPShortCut->rNumericArgs, + numNumbers, + ioStat, + state->dataIPShortCut->lNumericFieldBlanks, + state->dataIPShortCut->lAlphaFieldBlanks, + state->dataIPShortCut->cAlphaFieldNames, + state->dataIPShortCut->cNumericFieldNames); + + // For Building, min-fields is 8, which is the entire object, regardless of the number of input object fields + EXPECT_EQ(numAlphas, 3); + EXPECT_EQ(numNumbers, 5); + + // User inputs from above + // Note even though choice keys are case-sensitive during epJSON processing, getObjectItem pushes Alphas to UPPERcase + EXPECT_EQ(state->dataIPShortCut->cAlphaArgs(1), name1); // Building Name field is tagged with /retaincase + EXPECT_EQ(state->dataIPShortCut->cAlphaArgs(2), "OCEAN"); + EXPECT_NEAR(state->dataIPShortCut->rNumericArgs(2), 0.1, 0.0001); + // Defaults from schema + EXPECT_EQ(state->dataIPShortCut->cAlphaArgs(3), "FULLEXTERIOR"); + EXPECT_NEAR(state->dataIPShortCut->rNumericArgs(1), 0.0, 0.0001); + EXPECT_NEAR(state->dataIPShortCut->rNumericArgs(3), 0.4, 0.0001); + EXPECT_NEAR(state->dataIPShortCut->rNumericArgs(4), 25.0, 0.0001); + EXPECT_NEAR(state->dataIPShortCut->rNumericArgs(5), 1.0, 0.0001); + + inputProcessor->getObjectItem(*state, + obj_name2, + 1, + state->dataIPShortCut->cAlphaArgs, + numAlphas, + state->dataIPShortCut->rNumericArgs, + numNumbers, + ioStat, + state->dataIPShortCut->lNumericFieldBlanks, + state->dataIPShortCut->lAlphaFieldBlanks, + state->dataIPShortCut->cAlphaFieldNames, + state->dataIPShortCut->cNumericFieldNames); + + // For Material:NoMass, min-fields is 3, but the input object above takes it to A2 and N3 + EXPECT_EQ(numAlphas, 2); + EXPECT_EQ(numNumbers, 3); + + // User inputs from above + // Note even though choice keys are case-sensitive during epJSON processing, getObjectItem pushes Alphas to UPPERcase + EXPECT_EQ(state->dataIPShortCut->cAlphaArgs(1), UtilityRoutines::MakeUPPERCase(name2)); // Material Name field is NOT tagged with /retaincase + EXPECT_EQ(state->dataIPShortCut->cAlphaArgs(2), "MEDIUMROUGH"); + EXPECT_NEAR(state->dataIPShortCut->rNumericArgs(1), 2.0, 0.0001); + EXPECT_NEAR(state->dataIPShortCut->rNumericArgs(3), 0.5, 0.0001); + // Defaults from schema + EXPECT_NEAR(state->dataIPShortCut->rNumericArgs(2), 0.9, 0.0001); + // Fields beyond min-fields come back as blank or zero, even if they have a default + EXPECT_NEAR(state->dataIPShortCut->rNumericArgs(4), 0.0, 0.0001); +} /* TEST_F( InputProcessorFixture, processIDF_json )