NREL / OpenStudio

OpenStudio is a cross-platform collection of software tools to support whole building energy modeling using EnergyPlus and advanced daylight analysis using Radiance.
https://www.openstudio.net/
Other
484 stars 185 forks source link

HeatExchangerAirToAirSensibleAndLatent normalization divisor error #5145

Closed jmarrec closed 1 month ago

jmarrec commented 2 months ago

Issue overview

@mdahlhausen reported an issue where the Normalization Divisor is zero at https://github.com/NREL/OpenStudio/pull/5099#issuecomment-2050633394

Need to determine what's causing this and ensure it doesn't happen.

Current Behavior

Expected Behavior

Steps to Reproduce

@mdahlhausen can you please help here?

1. 2. 3. 4.

Possible Solution

Details

Environment

Some additional details about your environment for this issue (if relevant):

Context

jmarrec commented 2 months ago

I'm guessing the issue might be VT, or maybe a backward-compatible setter. I can't reproduce with model

include OpenStudio::Model
m = Model.new

# Hx does not have curves assigned yet
hx = HeatExchangerAirToAirSensibleAndLatent.new(m)
puts hx
OS:HeatExchanger:AirToAir:SensibleAndLatent,
  {8c6f9457-55f0-4773-9e9a-7a90368cdbd9}, !- Handle
  Heat Exchanger Air To Air Sensible And Latent 2, !- Name
  {ace21dbd-41a6-4465-b0d2-1e263620a860}, !- Availability Schedule
  autosize,                               !- Nominal Supply Air Flow Rate {m3/s}
  0.76,                                   !- Sensible Effectiveness at 100% Heating Air Flow {dimensionless}
  0.68,                                   !- Latent Effectiveness at 100% Heating Air Flow {dimensionless}
  0.76,                                   !- Sensible Effectiveness at 100% Cooling Air Flow {dimensionless}
  0.68,                                   !- Latent Effectiveness at 100% Cooling Air Flow {dimensionless}
  ,                                       !- Supply Air Inlet Node
  ,                                       !- Supply Air Outlet Node
  ,                                       !- Exhaust Air Inlet Node
  ,                                       !- Exhaust Air Outlet Node
  0,                                      !- Nominal Electric Power {W}
  Yes,                                    !- Supply Air Outlet Temperature Control
  Plate,                                  !- Heat Exchanger Type
  None,                                   !- Frost Control Type
  1.7,                                    !- Threshold Temperature {C}
  ,                                       !- Initial Defrost Time Fraction {dimensionless}
  ,                                       !- Rate of Defrost Time Fraction Increase {1/K}
  Yes,                                    !- Economizer Lockout
  ,                                       !- Sensible Effectiveness of Heating Air Flow Curve Name
  ,                                       !- Latent Effectiveness of Heating Air Flow Curve Name
  ,                                       !- Sensible Effectiveness of Cooling Air Flow Curve Name
  ;                                       !- Latent Effectiveness of Cooling Air Flow Curve Name

hx.assignHistoricalEffectivenessCurves

m.getTableLookups.map{|t| [t.nameString, t.normalizationDivisor]}.to_h
=> {"Heat Exchanger Air To Air Sensible And Latent 1_LatCoolEff"=>0.68,
 "Heat Exchanger Air To Air Sensible And Latent 1_SensCoolEff"=>0.76,
 "Heat Exchanger Air To Air Sensible And Latent 1_LatHeatEff"=>0.68,
 "Heat Exchanger Air To Air Sensible And Latent 1_SensHeatEff"=>0.76}

Can't do it either with the backward-compatible setters...

m = Model.new
hx = HeatExchangerAirToAirSensibleAndLatent.new(m)
hx.setSensibleEffectivenessat75HeatingAirFlow(0.68)
hx.setLatentEffectivenessat75HeatingAirFlow(0.68)
hx.setSensibleEffectivenessat75CoolingAirFlow(0.68)
hx.setLatentEffectivenessat75CoolingAirFlow(0.68)
m.getTableLookups.map{|t| [t.nameString, t.normalizationDivisor]}.to_h
=> {"Heat Exchanger Air To Air Sensible And Latent 1_LatCoolEff"=>0.68,
 "Heat Exchanger Air To Air Sensible And Latent 1_SensCoolEff"=>0.76,
 "Heat Exchanger Air To Air Sensible And Latent 1_LatHeatEff"=>0.68,
 "Heat Exchanger Air To Air Sensible And Latent 1_SensHeatEff"=>0.76}
jmarrec commented 2 months ago

Can't reproduce either via VT.

/usr/local/openstudio-3.7.0/bin/openstudio -e "m = OpenStudio::Model::Model.new; hx = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(m); m.save('test.osm')"
[1] test(main)> OpenStudio.openStudioLongVersion
=> "3.8.0-beta+d2f0bdd21a"
[2] test(main)> m = osload('test.osm')
=> #<OpenStudio::Model::Model:0x00007f15f8477b00 @__swigtype__="_p_openstudio__model__Model">
[3] test(main)> m.getTableLookups.map{|t| [t.nameString, t.normalizationDivisor]}.to_h
=> {"Heat Exchanger Air To Air Sensible And Latent 1_LatCoolEff"=>0.68,
 "Heat Exchanger Air To Air Sensible And Latent 1_LatHeatEff"=>0.68,
 "Heat Exchanger Air To Air Sensible And Latent 1_SensCoolEff"=>0.76,
 "Heat Exchanger Air To Air Sensible And Latent 1_SensHeatEff"=>0.76}
jmarrec commented 2 months ago

Apparently it's going to be fixed on openstudio-standards.

mdahlhausen commented 2 months ago

Reopening this issue, because it is still possible to cause a divide by zero error in a common use case of the air to air HX:

model = OpenStudio::Model::Model.new
heat_exchanger = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(model)
heat_exchanger.setLatentEffectivenessat100HeatingAirFlow(0.0)
heat_exchanger.setLatentEffectivenessat75HeatingAirFlow(0.0)
heat_exchanger.setLatentEffectivenessat100CoolingAirFlow(0.0)
heat_exchanger.setLatentEffectivenessat75CoolingAirFlow(0.0)
model.save('hx_out.osm', true)

creates a model with a Table with a 0 normalization divisor:

OS:HeatExchanger:AirToAir:SensibleAndLatent,
  {5e0c8290-c998-417e-871a-5e1d81fdebca}, !- Handle
  Heat Exchanger Air To Air Sensible And Latent 1, !- Name
  {75a43233-8ab7-4b33-9d8e-e1ce660b3080}, !- Availability Schedule
  autosize,                               !- Nominal Supply Air Flow Rate {m3/s}
  0.76,                                   !- Sensible Effectiveness at 100% Heating Air Flow {dimensionless}
  0,                                      !- Latent Effectiveness at 100% Heating Air Flow {dimensionless}
  0.76,                                   !- Sensible Effectiveness at 100% Cooling Air Flow {dimensionless}
  0,                                      !- Latent Effectiveness at 100% Cooling Air Flow {dimensionless}
  ,                                       !- Supply Air Inlet Node
  ,                                       !- Supply Air Outlet Node
  ,                                       !- Exhaust Air Inlet Node
  ,                                       !- Exhaust Air Outlet Node
  0,                                      !- Nominal Electric Power {W}
  Yes,                                    !- Supply Air Outlet Temperature Control
  Plate,                                  !- Heat Exchanger Type
  None,                                   !- Frost Control Type
  1.7,                                    !- Threshold Temperature {C}
  ,                                       !- Initial Defrost Time Fraction {dimensionless}
  ,                                       !- Rate of Defrost Time Fraction Increase {1/K}
  Yes,                                    !- Economizer Lockout
  ,                                       !- Sensible Effectiveness of Heating Air Flow Curve Name
  {961209b4-3204-4900-a992-c77738ff3203}, !- Latent Effectiveness of Heating Air Flow Curve Name
  ,                                       !- Sensible Effectiveness of Cooling Air Flow Curve Name
  {ac21cf30-8b67-45c9-bac1-f7bee22148a3}; !- Latent Effectiveness of Cooling Air Flow Curve Name

OS:Table:Lookup,
  {ac21cf30-8b67-45c9-bac1-f7bee22148a3}, !- Handle
  Heat Exchanger Air To Air Sensible And Latent 1_LatCoolEff, !- Name
  {ec872863-15b9-4ae8-b55a-3854b2709453}, !- Independent Variable List Name
  DivisorOnly,                            !- Normalization Method
  0,                                      !- Normalization Divisor
  0,                                      !- Minimum Output {BasedOnField A5}
  10,                                     !- Maximum Output {BasedOnField A5}
  Dimensionless,                          !- Output Unit Type
  ,                                       !- External File Name
  ,                                       !- External File Column Number
  ,                                       !- External File Starting Row Number
  0,                                      !- Output Value 1 {BasedOnField A5}
  0;                                      !- Output Value 2 {BasedOnField A5}

A proposed solution is to check if the effectiveness is zero, and don't create the air flow curve if so.

jmarrec commented 1 month ago
t = TableLookup.new(m)
t.setNormalizationDivisor(0)
=> true
puts t

OS:Table:Lookup,
  {d7e6d211-cf84-4977-a37e-09c35bd1128a}, !- Handle
  Table Lookup 1,                         !- Name
  {d1a909ea-8356-411e-868f-c8f5962c4f51}, !- Independent Variable List Name
  None,                                   !- Normalization Method
  0,                                      !- Normalization Divisor
  ,                                       !- Minimum Output {BasedOnField A5}
  ,                                       !- Maximum Output {BasedOnField A5}
  Dimensionless;                          !- Output Unit Type
OS:Table:Lookup,
  {d7e6d211-cf84-4977-a37e-09c35bd1128a}, !- Handle
  Table Lookup 1,                         !- Name
  {d1a909ea-8356-411e-868f-c8f5962c4f51}, !- Independent Variable List Name
  None,                                   !- Normalization Method
  0,                                      !- Normalization Divisor
  ,                                       !- Minimum Output {BasedOnField A5}
  ,                                       !- Maximum Output {BasedOnField A5}
  Dimensionless;                          !- Output Unit Type
OS:Table:Lookup,
  {d7e6d211-cf84-4977-a37e-09c35bd1128a}, !- Handle
  Table Lookup 1,                         !- Name
  {d1a909ea-8356-411e-868f-c8f5962c4f51}, !- Independent Variable List Name
  None,                                   !- Normalization Method
  0,                                      !- Normalization Divisor
  ,                                       !- Minimum Output {BasedOnField A5}
  ,                                       !- Maximum Output {BasedOnField A5}
  Dimensionless;                          !- Output Unit Type

The problem is what we copied the E+ idd blindly, and it does not have any constraints on the value it accepts at all.

I will reject 0.