Skip to content
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

HeatExchangerAirToAirSensibleAndLatent normalization divisor error #5145

Closed
jmarrec opened this issue Apr 15, 2024 · 5 comments · Fixed by #5193
Closed

HeatExchangerAirToAirSensibleAndLatent normalization divisor error #5145

jmarrec opened this issue Apr 15, 2024 · 5 comments · Fixed by #5193
Assignees
Labels
severity - Blocker Triage Issue needs to be assessed and labeled, further information on reported might be needed

Comments

@jmarrec
Copy link
Collaborator

jmarrec commented Apr 15, 2024

Issue overview

@mdahlhausen reported an issue where the Normalization Divisor is zero at #5099 (comment)

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?

Possible Solution

Details

Environment

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

  • Platform (Operating system, version): all
  • Version of OpenStudio (if using an intermediate build, include SHA): 3.8.0 circa RC1

Context

@jmarrec jmarrec added the Triage Issue needs to be assessed and labeled, further information on reported might be needed label Apr 15, 2024
@jmarrec
Copy link
Collaborator Author

jmarrec commented Apr 15, 2024

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
Copy link
Collaborator Author

jmarrec commented Apr 15, 2024

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 jmarrec self-assigned this Apr 15, 2024
@jmarrec
Copy link
Collaborator Author

jmarrec commented Apr 15, 2024

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

@jmarrec jmarrec closed this as completed Apr 15, 2024
@mdahlhausen mdahlhausen reopened this May 9, 2024
@mdahlhausen
Copy link
Collaborator

mdahlhausen commented May 9, 2024

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
Copy link
Collaborator Author

jmarrec commented May 13, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
severity - Blocker Triage Issue needs to be assessed and labeled, further information on reported might be needed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants