Skip to content

Commit

Permalink
Merge develop; fix conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
amirroth committed Sep 3, 2024
2 parents 9006e21 + a7fe8f7 commit 8a3d43f
Show file tree
Hide file tree
Showing 75 changed files with 1,479 additions and 2,126 deletions.
89 changes: 89 additions & 0 deletions design/FY2023/ImprovedErrorHandling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Modern Error Reporting

One feature request pretty much summarizes the issue and solution for our error reporting:

> "The current solution of writing out the error file to a text file without establishing a standard procedure makes it difficult for third-party vendors to translate the content.
We would prefer to start establishing a standard template for warnings/error records with unique IDs being written to the JSON files. "

OK, so the big picture is we want an error file in JSON format with some form of ID associated with the error messages.
A nice side benefit of this would be that we could generate a bit of documentation that cross references the ID.
The documentation could be built up over time with additions like typical causes, tips and tricks for fixing or working around, etc.

## Detailed task list

To meet this need, I think development would follow something like this:

- Analyze existing structure, figuring out a feasible plan for refactoring error message calls
- Create a new error manager in EnergyPlus that tracks errors in a nice structure
- Refactor calls to error message routines to use a vector of strings rather than individual calls to *Continue*
- Refactor the error message routines to not just emit to the err file but also add them to the new error manager
- (at this point I should be able to get no diffs)
- Try to find "nearly" identical error messages and bring them together
- I would expect this to cause valid diffs in the err file as minor wording changes are collected together
- Create a set of ID categories, and modify the error emission to report the error ID
- Use the error manager to generate the JSON error file
- Create an auto-generated document for the different error message IDs

## Error File Contents

I would start creating the error file with something like this, but am totally open to feedback, and it also may naturally change as development commences:

```json
{
"summary": {
"outcome": "fatal",
"num_severe": 2,
"num_warnings": 1,
"runtime": 210.2,
"timestamp": "2023-04-24T13:05:30Z"
},
"fatal": {
"id": "F1000",
"summary": "EnergyPlus failed because of a Meta-reason",
},
"severe": [
{
"id": "S1021",
"synopsis": "Flow controller did not converge",
"extra_info": "Air loop 'AirLoop' diverged with oscillating flow rate. Flow controller 'ControlStrategy' is set to 'InverseMobiusStrip', which only exists in hyper dimensional systems. Choose a more appropriate option."
},
{
"id": "S3001",
"synopsis": "Encountered meta-building in the input file",
"extra_info": "Building 'MetaBuilding' was declared with meta-latitude '\\342\\200\\234' and meta-longitude '\\742\\234\\876', which are not supported in this version of EnergyPlus"
}
],
"warnings": [
{
"id": "W4040",
"synopsis": "Timestep is set to 3600 seconds, which is incompatible with some cool short time step model, suggest setting it to 3599 or less.",
"extra_info": ""
}
],
"information": [
"Testing Individual Branch Integrity",
"All Branches passed integrity testing",
"Testing Individual Supply Air Path Integrity",
"All Supply Air Paths passed integrity testing",
"Testing Individual Return Air Path Integrity",
"All Return Air Paths passed integrity testing",
"No node connection errors were found.",
"Beginning Simulation",
"EnergyPlus Completed Successfully"
]
}
```

We'll need to keep in mind how new errors can be introduced into the category system to future proof it.
Many tools do something like: A001, A002, B001, B002, so I would probably start by doing something similar.

Things I still need to solve:
- Should I include timestamp info for every single warning?
- Add structure for recurring
- Add extra flag for whether warmup, sizing, or kickoff were true

## Current questions for dev team

- What should we name this JSON based error file?
- Do we just keep a list of message string templates in a single dedicated file, and use these in calls to errors?
- Anyone have strong feelings on the ID or JSON form?
Original file line number Diff line number Diff line change
Expand Up @@ -2431,7 +2431,7 @@ \subsubsection{Outputs}\label{outputs-indoorlivingwall}
\item
Zone,Average,Indoor Living Wall Evapotranspiration Rate {[}\si{\evapotranspirationRate}{]}
\item
Zone,Average,Indoor Living Wall Energy Required For Evapotranspiration Per Unit Area {[}\si{\watt\per\area}{]}
Zone,Average,Indoor Living Wall Energy Rate Required For Evapotranspiration Per Unit Area {[}\si{\watt\per\area}{]}
\item
Zone,Average,Indoor Living Wall LED Operational PPFD {[}\si{\umolperAreaperSecond}{]}
\item
Expand All @@ -2446,7 +2446,50 @@ \subsubsection{Outputs}\label{outputs-indoorlivingwall}
Zone,Sum,Indoor Living Wall LED Electricity Energy {[}J{]}
\end{itemize}

\paragraph{Indoor Living Wall Plant Surface Temperature {[}C{]}}\label{indoor-living-wall-plant-surface-temperature-c}

This output is the plant surface temperature in C. Plant surface temperature is determined using surface heat balance of indoor living walls.

\paragraph{Indoor Living Wall Sensible Heat Gain Rate {[}W{]}}\label{indoor-living-wall-sensible-heat-gain-rate-w}

This output is the sensible heat gain rate from indoor living walls in W and determined by surface heat balance. Positive sign represents heat loss from plants or heat gain to indoor space; negative sign represents heat gain to plants or heat loss from indoor space.

\paragraph{Indoor Living Wall Latent Heat Gain Rate {[}W{]}}\label{indoor-living-wall-latent-heat-gain-rate-w}

This output is the latent heat gain rate from indoor living walls in W. Latent heat gain is determined based on the increase of enthalpy due to the increase of absolute humidity (or humidity ratio) from plant evapotranspiration.

\paragraph{Indoor Living Wall Evapotranspiration Rate {[}\si{\evapotranspirationRate}{]}}\label{indoor-living-wall-evapotranspiration-rate}

This output is evapotranspiration (ET) rate of indoor living walls in \si{\evapotranspirationRate}. The ET rate is directly calculated by ET models(Penman-Monteith model, Stanghellini model or ET model from users).

\paragraph{Indoor Living Wall Energy Rate Required For Evapotranspiration Per Unit Area {[}\si{\watt\per\area}{]}}\label{indoor-living-wall-evapotranspiration-per-unit-area}

This output is energy rate per unit area required for plant evapotranspiration (ET) of indoor living walls in \si{\watt\per\area}. The energy rate per unit area for ET is determined by latent heat of vaporization for total ET over time period and surface area.

\paragraph{Indoor Living Wall LED Operational PPFD {[}\si{\umolperAreaperSecond}{]}}\label{indoor-living-wall-led-operational-ppfd}

This output is operational photosynthetic photon flux density (PPFD) of LED light in \si{\umolperAreaperSecond}. The operational PPFD of LED light is determined based on lighting method. If lighting method is Daylight, the operational LED PPFD is zero. If lighting method is LED, the operational LED PPFD is nominal LED PPFD. If lighting method is LED-Daylight, the operational LED PPFD is calculated based on targeted PPFD and daylighting level with the consideration of nominal LED PPFD.

\paragraph{Indoor Living Wall PPFD {[}\si{\umolperAreaperSecond}{]}}\label{indoor-living-wall-ppfd}

This output is the actual PPFD including LED and/or daylight for indoor living walls in \si{\umolperAreaperSecond}.

\paragraph{Indoor Living Wall Vapor Pressure Deficit {[}Pa{]}}\label{indoor-living-wall-vpd-pa}

This output is the vapor pressure deficit in Pa. The output represents the difference between saturated vapor pressure and vapor pressure of the moist air of current condition.

\paragraph{Indoor Living Wall LED Sensible Heat Gain Rate {[}W{]}}\label{indoor-living-wall-led-sensible-heat-gain-rate-w}

This output is the LED sensible heat gain rate for indoor living walls in W. The output determines convective heat gain from LED lights when LED lights are on.

\paragraph{Indoor Living Wall LED Operational Power {[}W{]}}\label{indoor-living-wall-led-operational-power-w}

This output is LED operational power for indoor living walls in W. The LED operational power is determined by LED nominal power in proportion to the ratio of LED operational PPFD to LED nominal PPFD.

\paragraph{Indoor Living Wall LED Electricity Energy {[}J{]}}\label{indoor-living-wall-led-electricity-energy-j}

This output is LED electricity energy for indoor living walls in J. The LED electricity energy is calculated by LED operational power multiplied by time period.

\subsection{ElectricEquipment:ITE:AirCooled}\label{electricequipmentiteaircooled}

This object describes air-cooled electric information technology equipment (ITE) which has variable power consumption as a function of loading and temperature.
Expand Down
5 changes: 1 addition & 4 deletions src/EnergyPlus/AirLoopHVACDOAS.cc
Original file line number Diff line number Diff line change
Expand Up @@ -999,7 +999,6 @@ namespace AirLoopHVACDOAS {
state.dataLoopNodes->Node(this->m_FanOutletNodeNum).MassFlowRateMaxAvail = sizingMassFlow;
state.dataLoopNodes->Node(this->m_FanOutletNodeNum).MassFlowRateMax = sizingMassFlow;
}
bool errorsFound = false;
if (this->m_FanIndex > 0 && this->m_FanTypeNum == SimAirServingZones::CompType::Fan_ComponentModel) {
state.dataFans->fans(this->m_FanIndex)->maxAirFlowRate = sizingMassFlow / state.dataEnvrn->StdRhoAir;
state.dataFans->fans(this->m_FanIndex)->minAirFlowRate = 0.0;
Expand All @@ -1008,9 +1007,7 @@ namespace AirLoopHVACDOAS {
state.dataLoopNodes->Node(this->m_FanOutletNodeNum).MassFlowRateMaxAvail = sizingMassFlow;
state.dataLoopNodes->Node(this->m_FanOutletNodeNum).MassFlowRateMax = sizingMassFlow;
}
if (errorsFound) {
ShowFatalError(state, "Preceding sizing errors cause program termination");
}

state.dataSize->CurSysNum = state.dataHVACGlobal->NumPrimaryAirSys + this->m_AirLoopDOASNum + 1;
state.dataSize->CurOASysNum = this->m_OASystemNum;
}
Expand Down
6 changes: 1 addition & 5 deletions src/EnergyPlus/AirflowNetwork/src/Solver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,12 +470,8 @@ namespace AirflowNetwork {

Real64 flowRate = fan->maxAirFlowRate;
flowRate *= m_state.dataEnvrn->StdRhoAir;
bool nodeErrorsFound{false};
int inletNode = fan->inletNodeNum;
int outletNode = fan->outletNodeNum;
if (nodeErrorsFound) {
success = false;
}
HVAC::FanType fanType = fan->type;
if (fanType != HVAC::FanType::Exhaust) {
ShowSevereError(m_state,
Expand Down Expand Up @@ -4607,7 +4603,7 @@ namespace AirflowNetwork {
int compnum = compnum_iter->second;
AirflowNetworkLinkageData(count).CompNum = compnum;

auto &surf = m_state.dataSurface->Surface(MultizoneSurfaceData(count).SurfNum);
auto const &surf = m_state.dataSurface->Surface(MultizoneSurfaceData(count).SurfNum);

switch (AirflowNetworkLinkageData(count).element->type()) {
case ComponentType::DOP: {
Expand Down
4 changes: 2 additions & 2 deletions src/EnergyPlus/Boilers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ void GetBoilerInput(EnergyPlusData &state)
ShowSevereError(
state, fmt::format("{}{}=\"{}\",", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
ShowContinueError(state, format("Invalid {}={:.3R}", state.dataIPShortCut->cNumericFieldNames(2), state.dataIPShortCut->rNumericArgs(2)));
ShowSevereError(state, format("...{} must be greater than 0.0", state.dataIPShortCut->cNumericFieldNames(2)));
ShowContinueError(state, format("...{} must be greater than 0.0", state.dataIPShortCut->cNumericFieldNames(2)));
ErrorsFound = true;
} else if (state.dataIPShortCut->rNumericArgs(2) > 1.0) {
ShowWarningError(state,
Expand Down Expand Up @@ -288,7 +288,7 @@ void GetBoilerInput(EnergyPlusData &state)
ShowSevereError(state,
fmt::format("{}{}=\"{}\"", RoutineName, state.dataIPShortCut->cCurrentModuleObject, state.dataIPShortCut->cAlphaArgs(1)));
ShowContinueError(state, format("Invalid {}={}", state.dataIPShortCut->cAlphaFieldNames(4), state.dataIPShortCut->cAlphaArgs(4)));
ShowSevereError(state, format("...{} not found.", state.dataIPShortCut->cAlphaFieldNames(4)));
ShowContinueError(state, format("...{} not found.", state.dataIPShortCut->cAlphaFieldNames(4)));
ErrorsFound = true;
}
thisBoiler.VolFlowRate = state.dataIPShortCut->rNumericArgs(3);
Expand Down
7 changes: 0 additions & 7 deletions src/EnergyPlus/BranchInputManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1026,7 +1026,6 @@ namespace BranchInputManager {
int NumNumbers; // Used to retrieve numbers from IDF
int NumAlphas; // Used to retrieve names from IDF
int NumParams;
bool ErrFound = false;
state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, "NodeList", NumParams, NumAlphas, NumNumbers);
NodeNums.dimension(NumParams, 0);
state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, CurrentModuleObject, NumParams, NumAlphas, NumNumbers);
Expand Down Expand Up @@ -1063,12 +1062,6 @@ namespace BranchInputManager {
cNumericFields.deallocate();
lAlphaBlanks.deallocate();
lNumericBlanks.deallocate();
if (ErrFound) {
ShowSevereError(
state,
format("{} Invalid {} Input, preceding condition(s) will likely cause termination.", RoutineName, CurrentModuleObject));
state.dataBranchInputManager->InvalidBranchDefinitions = true;
}
TestInletOutletNodes(state);
state.dataBranchInputManager->GetBranchInputOneTimeFlag = false;
}
Expand Down
28 changes: 14 additions & 14 deletions src/EnergyPlus/ChillerElectricEIR.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2403,16 +2403,16 @@ void ElectricEIRChillerSpecs::calculate(EnergyPlusData &state, Real64 &MyLoad, b
}
} break;
case DataPlant::CondenserFlowControl::ModulatedDeltaTemperature: {
Real64 Cp = FluidProperties::GetSpecificHeatGlycol(state,
state.dataPlnt->PlantLoop(this->CWPlantLoc.loopNum).FluidName,
this->CondInletTemp,
state.dataPlnt->PlantLoop(this->CWPlantLoc.loopNum).FluidIndex,
RoutineName);
Real64 CpCond = FluidProperties::GetSpecificHeatGlycol(state,
state.dataPlnt->PlantLoop(this->CWPlantLoc.loopNum).FluidName,
this->CondInletTemp,
state.dataPlnt->PlantLoop(this->CWPlantLoc.loopNum).FluidIndex,
RoutineName);
Real64 condDT = 0.0;
if (this->CondDTScheduleNum > 0) {
condDT = ScheduleManager::GetCurrentScheduleValue(state, this->CondDTScheduleNum);
}
this->CondMassFlowRate = this->QCondenser / (Cp * condDT);
this->CondMassFlowRate = this->QCondenser / (CpCond * condDT);
} break;
default: {
this->CondMassFlowRate = this->CondMassFlowRateMax;
Expand All @@ -2431,13 +2431,13 @@ void ElectricEIRChillerSpecs::calculate(EnergyPlusData &state, Real64 &MyLoad, b
if (this->CondMassFlowRate > DataBranchAirLoopPlant::MassFlowTolerance) {
// If Heat Recovery specified for this vapor compression chiller, then Qcondenser will be adjusted by this subroutine
if (this->HeatRecActive) this->calcHeatRecovery(state, this->QCondenser, this->CondMassFlowRate, condInletTemp, this->QHeatRecovered);
Cp = FluidProperties::GetSpecificHeatGlycol(state,
state.dataPlnt->PlantLoop(this->CDPlantLoc.loopNum).FluidName,
condInletTemp,
state.dataPlnt->PlantLoop(this->CDPlantLoc.loopNum).FluidIndex,
RoutineName);
Real64 CpCond = FluidProperties::GetSpecificHeatGlycol(state,
state.dataPlnt->PlantLoop(this->CDPlantLoc.loopNum).FluidName,
condInletTemp,
state.dataPlnt->PlantLoop(this->CDPlantLoc.loopNum).FluidIndex,
RoutineName);

this->CondOutletTemp = this->QCondenser / this->CondMassFlowRate / Cp + condInletTemp;
this->CondOutletTemp = this->QCondenser / this->CondMassFlowRate / CpCond + condInletTemp;
} else {
ShowSevereError(state, format("CalcElectricEIRChillerModel: Condenser flow = 0, for ElectricEIRChiller={}", this->Name));
ShowContinueErrorTimeStamp(state, "");
Expand All @@ -2457,8 +2457,8 @@ void ElectricEIRChillerSpecs::calculate(EnergyPlusData &state, Real64 &MyLoad, b
if (this->HeatRecActive) this->calcHeatRecovery(state, this->QCondenser, this->CondMassFlowRate, condInletTemp, this->QHeatRecovered);

if (CondMassFlowRate > 0.0) {
Cp = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(this->CondInletNodeNum).HumRat);
CondOutletTemp = CondInletTemp + QCondenser / CondMassFlowRate / Cp;
Real64 CpCond = Psychrometrics::PsyCpAirFnW(state.dataLoopNodes->Node(this->CondInletNodeNum).HumRat);
CondOutletTemp = CondInletTemp + QCondenser / CondMassFlowRate / CpCond;
} else {
this->CondOutletTemp = condInletTemp;
}
Expand Down
2 changes: 1 addition & 1 deletion src/EnergyPlus/ChillerExhaustAbsorption.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1362,7 +1362,7 @@ void ExhaustAbsorberSpecs::size(EnergyPlusData &state)
if (this->CondVolFlowRateWasAutoSized) {
if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
ShowSevereError(state, format("SizeExhaustAbsorber: ChillerHeater:Absorption:DoubleEffect=\"{}\", autosize error.", this->Name));
ShowSevereError(state, "Autosizing of Exhaust Fired Absorption Chiller condenser flow rate requires a condenser");
ShowContinueError(state, "Autosizing of Exhaust Fired Absorption Chiller condenser flow rate requires a condenser");
ShowContinueError(state, "loop Sizing:Plant object.");
ErrorsFound = true;
}
Expand Down
Loading

0 comments on commit 8a3d43f

Please sign in to comment.