From ec73fd620c8870134fc7d7ee42cc3a79ab667877 Mon Sep 17 00:00:00 2001 From: Scott Horowitz Date: Fri, 15 Nov 2024 21:49:11 -0700 Subject: [PATCH] Bugfix for foundation walls with non-integer values for depths/distances. --- HPXMLtoOpenStudio/measure.xml | 10 ++-- HPXMLtoOpenStudio/resources/hvac_sizing.rb | 66 ++++++++++++++------- HPXMLtoOpenStudio/resources/math.rb | 19 ++++-- HPXMLtoOpenStudio/tests/test_hvac_sizing.rb | 48 +++++++++++++++ 4 files changed, 114 insertions(+), 29 deletions(-) diff --git a/HPXMLtoOpenStudio/measure.xml b/HPXMLtoOpenStudio/measure.xml index 6136e50ea4..acf153b252 100644 --- a/HPXMLtoOpenStudio/measure.xml +++ b/HPXMLtoOpenStudio/measure.xml @@ -3,8 +3,8 @@ 3.1 hpxm_lto_openstudio b1543b30-9465-45ff-ba04-1d1f85e763bc - b20ae61b-bf29-4be9-8e25-a44b3b12c232 - 2024-11-16T00:20:48Z + 531c1027-0d75-4343-a8ce-6717ad2b9924 + 2024-11-16T04:46:12Z D8922A73 HPXMLtoOpenStudio HPXML to OpenStudio Translator @@ -393,7 +393,7 @@ hvac_sizing.rb rb resource - FEB17D7B + C6F9CE12 internal_gains.rb @@ -423,7 +423,7 @@ math.rb rb resource - FEB72476 + CE6D107B meta_measure.rb @@ -693,7 +693,7 @@ test_hvac_sizing.rb rb test - 561E6631 + E1BC3865 test_lighting.rb diff --git a/HPXMLtoOpenStudio/resources/hvac_sizing.rb b/HPXMLtoOpenStudio/resources/hvac_sizing.rb index 152ae72d86..408e380b02 100644 --- a/HPXMLtoOpenStudio/resources/hvac_sizing.rb +++ b/HPXMLtoOpenStudio/resources/hvac_sizing.rb @@ -4795,15 +4795,36 @@ def self.get_foundation_wall_above_grade_ufactor(foundation_wall, include_insula assembly_r = Material.FoundationWallMaterial(foundation_wall.type, foundation_wall.thickness).rvalue assembly_r += Material.AirFilmVertical.rvalue + Material.AirFilmOutside.rvalue - if include_insulation_layers - if foundation_wall.insulation_interior_distance_to_top == 0 && foundation_wall.insulation_interior_distance_to_bottom > 0 - assembly_r += foundation_wall.insulation_interior_r_value - end - if foundation_wall.insulation_exterior_distance_to_top == 0 && foundation_wall.insulation_exterior_distance_to_bottom > 0 - assembly_r += foundation_wall.insulation_exterior_r_value - end + if not include_insulation_layers + return 1.0 / assembly_r + end + + ag_depth = foundation_wall.height - foundation_wall.depth_below_grade + wall_ins_dist_bottom_to_grade_int = ag_depth - foundation_wall.insulation_interior_distance_to_bottom + wall_ins_dist_top_to_grade_int = ag_depth - foundation_wall.insulation_interior_distance_to_top + wall_ins_dist_bottom_to_grade_ext = ag_depth - foundation_wall.insulation_exterior_distance_to_bottom + wall_ins_dist_top_to_grade_ext = ag_depth - foundation_wall.insulation_exterior_distance_to_top + # Perform calculation for each 1ft bin of above grade depth + sum_u_wall = 0.0 + for distance_to_grade in 1..ag_depth.ceil + r_wall = assembly_r + + bin_dist_top_to_grade = [distance_to_grade, ag_depth].min + bin_dist_bottom_to_grade = distance_to_grade - 1 + bin_size = bin_dist_top_to_grade - bin_dist_bottom_to_grade # Last bin may be less than 1 ft + + # Add interior insulation R-value at this depth? + bin_frac_insulated_int = MathTools.overlap(bin_dist_bottom_to_grade, bin_dist_top_to_grade, wall_ins_dist_bottom_to_grade_int, wall_ins_dist_top_to_grade_int) / bin_size + r_wall += foundation_wall.insulation_interior_r_value * bin_frac_insulated_int # Interior insulation at this depth, add R-value + + # Add exterior insulation R-value at this depth? + bin_frac_insulated_ext = MathTools.overlap(bin_dist_bottom_to_grade, bin_dist_top_to_grade, wall_ins_dist_bottom_to_grade_ext, wall_ins_dist_top_to_grade_ext) / bin_size + r_wall += foundation_wall.insulation_exterior_r_value * bin_frac_insulated_ext # Exterior insulation at this depth, add R-value + + sum_u_wall += (1.0 / r_wall) * bin_size end - return 1.0 / assembly_r + u_wall = sum_u_wall / ag_depth + return u_wall end # Calculates the foundation wall below grade effective U-factor according to Manual J Section A12-4. @@ -4832,22 +4853,27 @@ def self.get_foundation_wall_below_grade_ufactor(foundation_wall, include_soil, # Perform calculation for each 1ft bin of below grade depth sum_u_wall = 0.0 - wall_depth_above_grade = foundation_wall.depth_below_grade.ceil - for distance_to_grade in 1..wall_depth_above_grade + for distance_to_grade in 1..foundation_wall.depth_below_grade.ceil # Calculate R-wall at this depth r_wall = wall_constr_rvalue - Material.AirFilmOutside.rvalue - bin_distance_to_grade = distance_to_grade - 0.5 # Use e.g. 2.5 ft for the 2ft-3ft bin - r_soil = (Math::PI * bin_distance_to_grade / 2.0) / ground_conductivity - if (distance_to_grade > wall_ins_dist_top_to_grade_int) && (distance_to_grade <= wall_ins_dist_bottom_to_grade_int) - r_wall += wall_ins_rvalue_int # Interior insulation at this depth, add R-value - end - if (distance_to_grade > wall_ins_dist_top_to_grade_ext) && (distance_to_grade <= wall_ins_dist_bottom_to_grade_ext) - r_wall += wall_ins_rvalue_ext # Exterior insulation at this depth, add R-value - end + bin_dist_top_to_grade = distance_to_grade - 1 + bin_dist_bottom_to_grade = [distance_to_grade, foundation_wall.depth_below_grade].min + bin_size = bin_dist_bottom_to_grade - bin_dist_top_to_grade # Last bin may be less than 1 ft + bin_avg_dist_to_grade = (bin_dist_top_to_grade + bin_dist_bottom_to_grade) / 2.0 + + # Add interior insulation R-value at this depth? + bin_frac_insulated_int = MathTools.overlap(bin_dist_top_to_grade, bin_dist_bottom_to_grade, wall_ins_dist_top_to_grade_int, wall_ins_dist_bottom_to_grade_int) / bin_size + r_wall += wall_ins_rvalue_int * bin_frac_insulated_int + + # Add exterior insulation R-value at this depth? + bin_frac_insulated_ext = MathTools.overlap(bin_dist_top_to_grade, bin_dist_bottom_to_grade, wall_ins_dist_top_to_grade_ext, wall_ins_dist_bottom_to_grade_ext) / bin_size + r_wall += wall_ins_rvalue_ext * bin_frac_insulated_ext # Exterior insulation at this depth, add R-value + if include_soil - sum_u_wall += 1.0 / (r_soil + r_wall) + r_soil = (Math::PI * bin_avg_dist_to_grade / 2.0) / ground_conductivity + sum_u_wall += (1.0 / (r_soil + r_wall)) * bin_size else - sum_u_wall += 1.0 / r_wall + sum_u_wall += (1.0 / r_wall) * bin_size end end u_wall = sum_u_wall / foundation_wall.depth_below_grade diff --git a/HPXMLtoOpenStudio/resources/math.rb b/HPXMLtoOpenStudio/resources/math.rb index f351ead325..e6d61b5abe 100644 --- a/HPXMLtoOpenStudio/resources/math.rb +++ b/HPXMLtoOpenStudio/resources/math.rb @@ -34,7 +34,7 @@ def self.interp4(x, y, x1, x2, y1, y2, fx1y1, fx1y2, fx2y1, fx2y2) + (fx2y2 / ((x2 - x1) * (y2 - y1))) * (x - x1) * (y - y1) end - # Calculate the result of a biquadratic polynomial with independent variables. + # Calculates the result of a biquadratic polynomial with independent variables. # x and y, and a list of coefficients, c: # # z = c[1] + c[2]*x + c[3]*x**2 + c[4]*y + c[5]*y**2 + c[6]*x*y @@ -52,7 +52,7 @@ def self.biquadratic(x, y, c) return z end - # Calculate the result of a quadratic polynomial with independent variable. + # Calculates the result of a quadratic polynomial with independent variable. # x and a list of coefficients, c: # # y = c[1] + c[2]*x + c[3]*x**2 @@ -70,9 +70,9 @@ def self.quadratic(x, c) return y end - # Calculate the result of a bicubic polynomial with independent variables. + # Calculates the result of a bicubic polynomial with independent variables. # x and y, and a list of coefficients, c: - + # # z = c[1] + c[2]*x + c[3]*y + c[4]*x**2 + c[5]*x*y + c[6]*y**2 + \ # c[7]*x**3 + c[8]*y*x**2 + c[9]*x*y**2 + c[10]*y**3 # @@ -91,6 +91,17 @@ def self.bicubic(x, y, c) return z end + # Calculates the overlap distance of two 1D line segments. + # + # @param min1 [Double] min value of line 1 + # @param max1 [Double] max value of line 1 + # @param min2 [Double] min value of line 2 + # @param max2 [Double] max value of line 2 + # @return [Double] overlap distance + def self.overlap(min1, max1, min2, max2) + return [0.0, [max1, max2].min - [min1, min2].max].max + end + # Determine if a guess is within tolerance for convergence. # If not, output a new guess using the Newton-Raphson method. # diff --git a/HPXMLtoOpenStudio/tests/test_hvac_sizing.rb b/HPXMLtoOpenStudio/tests/test_hvac_sizing.rb index 1fb1b7731f..b04bbcb7f1 100644 --- a/HPXMLtoOpenStudio/tests/test_hvac_sizing.rb +++ b/HPXMLtoOpenStudio/tests/test_hvac_sizing.rb @@ -1678,6 +1678,54 @@ def test_manual_j_basement_wall_below_grade_ufactor end end + def test_foundation_wall_non_integer_values + tol = 0.01 # 1% + + # Test wall insulation covering most of above and below-grade portions of wall + fwall = HPXML::FoundationWall.new(nil) + fwall.height = 5.0 + fwall.depth_below_grade = 1.5 + fwall.type = HPXML::FoundationWallTypeSolidConcrete + fwall.thickness = 4.0 # in + fwall.insulation_interior_r_value = 10.0 + fwall.insulation_exterior_r_value = 0.0 + fwall.insulation_interior_distance_to_top = 0.3 + fwall.insulation_exterior_distance_to_top = 0.0 + fwall.insulation_interior_distance_to_bottom = 4.9 + fwall.insulation_exterior_distance_to_bottom = 0.0 + rvalue = 1.0 / HVACSizing.get_foundation_wall_below_grade_ufactor(fwall, false, nil) + assert_in_epsilon(10.25, rvalue, tol) + rvalue = 1.0 / HVACSizing.get_foundation_wall_above_grade_ufactor(fwall, true) + assert_in_epsilon(9.6, rvalue, tol) + + # Same as above but test exterior wall insulation + fwall.insulation_interior_r_value = 0.0 + fwall.insulation_exterior_r_value = 10.0 + fwall.insulation_interior_distance_to_top = 0.0 + fwall.insulation_exterior_distance_to_top = 0.3 + fwall.insulation_interior_distance_to_bottom = 0.0 + fwall.insulation_exterior_distance_to_bottom = 4.9 + rvalue = 1.0 / HVACSizing.get_foundation_wall_below_grade_ufactor(fwall, false, nil) + assert_in_epsilon(10.25, rvalue, tol) + rvalue = 1.0 / HVACSizing.get_foundation_wall_above_grade_ufactor(fwall, true) + assert_in_epsilon(9.6, rvalue, tol) + + # Test small coverage of below-grade portion of wall, no coverage of above-grade + fwall.insulation_exterior_distance_to_top = 4.4 + rvalue = 1.0 / HVACSizing.get_foundation_wall_below_grade_ufactor(fwall, false, nil) + assert_in_epsilon(2.7, rvalue, tol) + rvalue = 1.0 / HVACSizing.get_foundation_wall_above_grade_ufactor(fwall, true) + assert_in_epsilon(1.2, rvalue, tol) + + # Test small coverage of above-grade portion of wall, no coverage of below-grade + fwall.insulation_exterior_distance_to_top = 2.3 + fwall.insulation_exterior_distance_to_bottom = 3.5 + rvalue = 1.0 / HVACSizing.get_foundation_wall_below_grade_ufactor(fwall, false, nil) + assert_in_epsilon(1.0, rvalue, tol) + rvalue = 1.0 / HVACSizing.get_foundation_wall_above_grade_ufactor(fwall, true) + assert_in_epsilon(2.1, rvalue, tol) + end + def test_multiple_zones # Run base-zones-spaces-multiple.xml args_hash = {}