Conversation
| # If impossible to meet the requested Ut, TBD hardsets Uo to UMIN while | ||
| # raising warnings, namely when: | ||
| # - calculated Uo is negative, i.e. ( ∑psi • L )/A > 0.277 | ||
| # - calculated layer r violates E+ material constraints, e.g. |
There was a problem hiding this comment.
To trigger OpenStudio/EnergyPlus material thickness/conductivity thresholds, one must request unreasonably low U-factors (Uo < 0.010 W/m2.K, i.e. R-ip > 568). This may happen when attempting to meet NECB (2017 through 2025) prescriptive requirements, through uprating.
The previous version would bail out (with an error message) when hitting such thresholds, leaving the construction unaltered. The revised solution raises a big red warning, yet resets the construction Uo to UMIN (0.010 W/m2.K).
| end | ||
|
|
||
| it "can check for balcony sills (ASHRAE 90.1 2022)" do | ||
| it "can check for balcony sills (ASHRAE 90.1 2022/25)" do |
There was a problem hiding this comment.
Not seeing any changes between 2022 vs 2025 ASHRAE 90.1 editions.
| # ... fall under the scope of requirement 5.5.5.5. There is much uncertainty | ||
| # on how to model items falling under 5.5.5.5. For this reason, the 8x | ||
| # TBD-built-in ASHRAE PSI sets have 0 W/K per meter assigned for edges under | ||
| # 5.5.5.5. This is discussed here: |
There was a problem hiding this comment.
The only issue with thermal bridging in ASHRAE 90.1 2022/25 is requirement 5.5.5.5, discussed here.
| # sets while resetting non-zero PSI-factors. | ||
| # Users can always write up a custom ASHRAE 90.1 (A10) PSI-factor set on | ||
| # file (tbd.json), initially based on the built-in 90.1 sets while resetting | ||
| # non-zero PSI-factors. |
There was a problem hiding this comment.
One can still rely on TBD for linear/point thermal bridges falling under ASHRAE 90.1 requirement 5.5.5.5, via a custom JSON file. Suggestions are also detailed here.
| if sss.isConstructionDefaulted | ||
| set = defaultConstructionSet(sss) # building? story? | ||
| # Area-weighted surface air film resistances. | ||
| col[:film] = 1 / ( col[:fA] / col[:area] ) |
There was a problem hiding this comment.
Other than input validation changes and minor fixes, the key change is having the (uprated) uo method rely on a surface area weighted average of applicable surface air film resistances. Helps when dealing with:
- surfaces with different tilts
- (uprated) constructions referenced by both outdoor-facing and interzone surfaces
| # Although the roof construction is correctly uprated, it is not possible to | ||
| # completely uprate the wall construction. It is therefore capped at the | ||
| # minimum allowed Uo-factor, or ~9 ft of XPS insulation. | ||
| expect(argh[:wall_uo].round(3)).to eq(0.010) # RSi 100.00 (R568) |
There was a problem hiding this comment.
Expected outcome when failing to uprate material thickness/conductivity, e.g.:
- WARNING message: "Unable to completely uprate ... "
- uprated construction hard set at UMIN (0.010 W/m2.K), or R-ip 568
| # e.g. tiltx == 210° if tilt == 30°, so convert tiltx to 150° | ||
| # e.g. tiltx == 330° if tilt == 150°, so convert tiltx to 30° | ||
| # e.g. tiltx == 275° if tilt == 95°, so convert tiltx to 85° | ||
| tiltx = Math::PI - tilt if tiltx > Math::PI |
There was a problem hiding this comment.
New OSut filmResistances method (see PR), more aligned with EnergyPlus reported values when dealing with non-vertical interzone paired surfaces.
| log(WRN, "Can't set #{h} W/K to #{id} #{mth}") if loss > TOL | ||
|
|
||
| u | ||
| uo |
There was a problem hiding this comment.
Nasty oversight at this stage. Fixed.
| # Side test, simple model: | ||
| # - 3x roofceiling surfaces | ||
| # - different tilts | ||
| # - different outside boundary conditions |
There was a problem hiding this comment.
US DOE Prototypes (or other similar archetypes) typically do not hold instances of building envelope surfaces that differ in tilt and exposure (within the same model), i.e. with different surface air film resistances. This new tests breaks down uprating/derating steps when processing 3 different roof/ceiling surfaces:
- 1x flat, outdoor-facing roof
- 1x insulated (interzone) attic floor/ceiling
- 1x sloped, outdoor-facing roof
Checking if the solution ensures a final, area-weighted roof Ut meeting NECB 2025 targets.
| expect(m3.thickness.round(4)).to eq(0.1256) | ||
| expect(m1.thermalConductivity.round(4)).to eq(0.0151) | ||
| expect(m2.thermalConductivity.round(4)).to eq(0.0160) | ||
| expect(m3.thermalConductivity.round(4)).to eq(0.0170) |
There was a problem hiding this comment.
Derated insulation materials have different k values, as assigned heat loss (from thermal bridging, in W/K) is intentionally spread out unequally.
| uT = uA / area | ||
|
|
||
| # Area-weighted Ut factors meet NECB requirement. | ||
| expect(uT.round(3)).to eq(u.round(3)) |
There was a problem hiding this comment.
Uprated (then derated) roof constructions ultimately meet the NECB 2025 required roof Ut requirement (area-weighted average).
| expect(u3.round(5)).to eq(0.07738) # R73.38 | ||
|
|
||
| # Matains overall area-weighted (uprated) u0. | ||
| # Maintains overall area-weighted (uprated) u0. |
| # Collection of one or several constructions to uprate. | ||
| coll = {} | ||
| op = g[:op].downcase | ||
| op = g[:op] |
There was a problem hiding this comment.
Fixed. This prevented TBD from correctly uprating user-selected constructions.
| # Adjust if beyond admissible range. | ||
| if u < UMIN | ||
| negative("#{id}: new Uo", mth, WRN) | ||
| negative("#{id}: new Uo", mth, INF) |
There was a problem hiding this comment.
Lower-level issues are tagged as informative, leaving the higher-level issue raised as a warning.
| r = RMIN if r < RMIN | ||
| loss = (u - 1 / r) * area if r < RMIN | ||
| if r < RMIN | ||
| r = RMIN |
| end | ||
|
|
||
| coll[id] = {} | ||
| lc = lc.get |
There was a problem hiding this comment.
Fixed. Also prevented TBD from correctly uprating individual constructions.
|
|
||
| if col[:area] < TOL | ||
| empty("#{id} area", mth, WRN) | ||
| next |
There was a problem hiding this comment.
Should never occur - expected to be caught much earlier in the solution. Adding as a precaution (i.e. avoids divide by zero crashes). Multiple fixes in this commit to (finally) catch zero'ed denominators.
| loss = (de_u - 1 / de_r) * net unless de_r > RMIN | ||
| if de_r < RMIN | ||
| de_r = RMIN | ||
| loss = (de_u - 1 / de_r) * net |
| ut = format("%.3f", g[:ut]) | ||
| output = "An initial #{label.to_s} Uo of #{uo} W/m2•K is required to " \ | ||
| "achieve an overall Ut of #{ut} W/m2•K for #{g[:op]}" | ||
| output = "An area-weighted #{label.to_s} Uo of #{uo} W/m2•K is " \ |
There was a problem hiding this comment.
TBD's initial uprating solution would glob all (e.g. wall) constructions together and attempt to uprate a single common wall construction. This offered a few advantages:
- more permissive, accommodating
- easier to calculate
- easier to communicate to users
The downside was that properties (like thermal inertia, or lack of) of individual constructions would be reset by the method.
The revised approach maintains user-defined properties of non-insulating layers of individual up/de-rated constructions. The downside is that TBD may now fail to uprate one wall construction in a model (given the extent of heat loss from thermal bridging vs exposed wall area), while succeeding for two others. If TBD fails to uprate one construction, it raises a warning and then defaults the construction Uo to 0.01 W/m2.K. However, the reported Uo (e.g. for all walls) is now an area-weighted Uo (mixing both successful and failed uprated constructions). This may be confusing at first - proceed with caution.
| TBD.clean! | ||
|
|
||
| name = "Typical Insulated Metal Building Wall R-11.9 1" | ||
| file = File.join(__dir__, "files/osms/in/warehouse.osm") |
| argh[:option ] = "spandrel HP (BETBG)" | ||
| argh[:uprate_walls] = true | ||
| argh[:wall_option ] = name | ||
| argh[:wall_ut ] = 0.210 # NECB CZ7 2017 (RSi 4.76 / R27) |
There was a problem hiding this comment.
TBD fails to uprate the selected construction:
- modest floor-to-floor height
- important fenestrated areas
- i.e. small net area
- linear thermal bridging heat loss too great
- NECB 2017-2025 targets too ambitious
TBD no longer spreads out extra heat loss from "office" thermal bridging amongst other walls constructions (i.e. "fine" and "bulk" storage walls) as before.
OpenStudio feedback:
The issue is raised in red (i.e. "Unable to completely uprate ..."), yet TBD will nonetheless proceed with the uprating/derating steps (with a reset construction Uo of 0.010 W/m2.K). As suggested in TBD's Guide section on Uprating:
We strongly recommend to first investigate this feature while relying on Apply Measures Now feedback (ideally UNCHECKing the Alter OpenStudio model option), to get a sense of how significant the uprating calculations may end up altering your initial designs. In some cases, EnergyPlus’ CTF convergence calculations may even fail with very thick (uprated) insulation layers !
| next unless surface.key?(:type) | ||
|
|
||
| heating = -50 if spts | ||
| heating = -24 if spts |
There was a problem hiding this comment.
Avoids a potential divide by zero crash. Not expecting many thermostats with heating setpoints that low anyways. Rare cases like frozen vaccine storage (-15° down to -80°C), maybe.

Updates/tests updates to OSut gem files (see OSut PR).