lib/measures/tbd/resources/ua.rb in tbd-3.2.1 vs lib/measures/tbd/resources/ua.rb in tbd-3.2.2

- old
+ new

@@ -37,31 +37,32 @@ mth = "TBD::#{__callee__}" res = { uo: nil, m: nil } cl1 = OpenStudio::Model::Model cl2 = OpenStudio::Model::LayeredConstruction cl3 = Numeric + cl4 = String - return mismatch("model", model, cl1, mth, DBG, res) unless model.is_a?(cl1) - return mismatch("id", id, String, mth, DBG, res) unless id.is_a?(String) - return mismatch("lc", lc, cl2, mth, DBG, res) unless lc.is_a?(cl2) - return mismatch("hloss", hloss, cl3, mth, DBG, res) unless hloss.is_a?(cl3) - return mismatch("film", film, cl3, mth, DBG, res) unless film.is_a?(cl3) - return mismatch("Ut", ut, cl3, mth, DBG, res) unless ut.is_a?(cl3) + return mismatch("model", model, cl1, mth, DBG, res) unless model.is_a?(cl1) + return mismatch("id" , id, cl4, mth, DBG, res) unless id.is_a?(cl4) + return mismatch("lc" , lc, cl2, mth, DBG, res) unless lc.is_a?(cl2) + return mismatch("hloss", hloss, cl3, mth, DBG, res) unless hloss.is_a?(cl3) + return mismatch("film" , film, cl3, mth, DBG, res) unless film.is_a?(cl3) + return mismatch("Ut" , ut, cl3, mth, DBG, res) unless ut.is_a?(cl3) loss = 0.0 # residual heatloss (not assigned) [W/K] area = lc.getNetArea lyr = insulatingLayer(lc) lyr[:index] = nil unless lyr[:index].is_a?(Numeric) lyr[:index] = nil unless lyr[:index] >= 0 lyr[:index] = nil unless lyr[:index] < lc.layers.size - return invalid("'#{id}' layer index", mth, 0, ERR, res) unless lyr[:index] - return zero("'#{id}': heatloss", mth, WRN, res) unless hloss > TOL - return zero("'#{id}': films", mth, WRN, res) unless film > TOL - return zero("'#{id}': Ut", mth, WRN, res) unless ut > TOL - return invalid("'#{id}': Ut", mth, 0, WRN, res) unless ut < 5.678 - return zero("'#{id}': net area (m2)", mth, ERR, res) unless area > TOL + return invalid("'#{id}' layer index", mth, 0, ERR, res) unless lyr[:index] + return zero("'#{id}': heatloss" , mth, WRN, res) unless hloss > TOL + return zero("'#{id}': films" , mth, WRN, res) unless film > TOL + return zero("'#{id}': Ut" , mth, WRN, res) unless ut > TOL + return invalid("'#{id}': Ut" , mth, 0, WRN, res) unless ut < 5.678 + return zero("'#{id}': net area (m2)", mth, ERR, res) unless area > TOL # First, calculate initial layer RSi to initially meet Ut target. rt = 1 / ut # target construction Rt ro = rsi(lc, film) # current construction Ro new_r = lyr[:r] + (rt - ro) # new, un-derated layer RSi @@ -74,20 +75,20 @@ return zero("'#{id}': new Rsi", mth, ERR, res) unless new_r > 0.001 if lyr[:type] == :massless m = lc.getLayer(lyr[:index]).to_MasslessOpaqueMaterial - return invalid("'#{id}' massless layer?", mth, 0) if m.empty? + return invalid("'#{id}' massless layer?", mth, 0, DBG, res) if m.empty? m = m.get.clone(model).to_MasslessOpaqueMaterial.get m.setName("#{id} uprated") new_r = 0.001 unless new_r > 0.001 loss = (new_u - 1 / new_r) * area unless new_r > 0.001 m.setThermalResistance(new_r) else # type == :standard m = lc.getLayer(lyr[:index]).to_StandardOpaqueMaterial - return invalid("'#{id}' standard layer?", mth, 0) if m.empty? + return invalid("'#{id}' standard layer?", mth, 0, DBG, res) if m.empty? m = m.get.clone(model).to_StandardOpaqueMaterial.get m.setName("#{id} uprated") k = m.thermalConductivity @@ -139,15 +140,16 @@ # @return [Bool] true if successfully uprated def uprate(model = nil, s = {}, argh = {}) mth = "TBD::#{__callee__}" cl1 = OpenStudio::Model::Model cl2 = Hash + cl3 = OpenStudio::Model::LayeredConstruction a = false - return mismatch("model", model, cl1, mth, DBG, a) unless model.is_a?(cl1) - return mismatch("surfaces", s, cl2, mth, DBG, a) unless s.is_a?(cl2) - return mismatch("argh", model, cl1, mth, DBG, a) unless argh.is_a?(cl2) + return mismatch("model" , model, cl1, mth, DBG, a) unless model.is_a?(cl1) + return mismatch("surfaces", s, cl2, mth, DBG, a) unless s.is_a?(cl2) + return mismatch("argh" , model, cl1, mth, DBG, a) unless argh.is_a?(cl2) argh[:uprate_walls ] = false unless argh.key?(:uprate_walls ) argh[:uprate_roofs ] = false unless argh.key?(:uprate_roofs ) argh[:uprate_floors] = false unless argh.key?(:uprate_floors) argh[:wall_ut ] = 5.678 unless argh.key?(:wall_ut ) @@ -166,210 +168,217 @@ groups[:floor][:ut] = argh[:floor_ut ] groups[:wall ][:op] = argh[:wall_option ] groups[:roof ][:op] = argh[:roof_option ] groups[:floor][:op] = argh[:floor_option ] - groups.each do |label, g| + groups.each do |type, g| next unless g[:up] next unless g[:ut].is_a?(Numeric) next unless g[:ut] < 5.678 + typ = type + typ = :ceiling if typ == :roof # fix in future revision. TO-DO. coll = {} area = 0 film = 100000000000000 lc = nil id = "" all = g[:op].downcase == "all wall constructions" || g[:op].downcase == "all roof constructions" || g[:op].downcase == "all floor constructions" if g[:op].empty? - log(ERR, "Construction to uprate? (#{mth})") + log(ERR, "Construction (#{type}) to uprate? (#{mth})") elsif all - model.getSurfaces.each do |sss| - next unless sss.surfaceType.downcase.include?(label.to_s) - next unless sss.outsideBoundaryCondition.downcase == "outdoors" - next if sss.construction.empty? - next if sss.construction.get.to_LayeredConstruction.empty? + s.each do |nom, surface| + next unless surface.key?(:deratable ) + next unless surface.key?(:type ) + next unless surface.key?(:construction) + next unless surface.key?(:filmRSI ) + next unless surface.key?(:index ) + next unless surface.key?(:ltype ) + next unless surface.key?(:r ) + next unless surface[:deratable ] + next unless surface[:type ] == typ + next unless surface[:construction].is_a?(cl3) + next if surface[:index ].nil? - c = sss.construction.get.to_LayeredConstruction.get - i = c.nameString + # Retain lowest surface film resistance (e.g. tilted surfaces). + c = surface[:construction] + i = c.nameString + aire = c.getNetArea + film = surface[:filmRSI] if surface[:filmRSI] < film - # Reliable unless referenced by other surface types e.g. floor vs wall. - if c.getNetArea > area - area = c.getNetArea + # Retain construction covering largest area. The following conditional + # is reliable UNLESS linked to other deratable surface types e.g. both + # floors AND walls (see "elsif lc" corrections below). + if aire > area lc = c + area = aire id = i end - film = sss.filmResistance if sss.filmResistance < film - nom = sss.nameString - coll[i] = { area: c.getNetArea, lc: c, s: {} } unless coll.key?(i) - coll[i][:s][nom] = { a: sss.netArea } unless coll[i][:s].key?(nom) + coll[i] = { area: aire, lc: c, s: {} } unless coll.key?(i) + coll[i][:s][nom] = { a: surface[:net] } unless coll[i][:s].key?(nom) end else id = g[:op] - c = model.getConstructionByName(id) + lc = model.getConstructionByName(id) + log(ERR, "Construction '#{id}'? (#{mth})") if lc.empty? + next if lc.empty? - if c.empty? - log(ERR, "Construction '#{id}'? (#{mth})") - else - c = c.get.to_LayeredConstruction + lc = lc.get.to_LayeredConstruction + log(ERR, "'#{id}' layered construction? (#{mth})") if lc.empty? + next if lc.empty? - if c.empty? - log(ERR, "'#{id}' layered construction? (#{mth})") - else - lc = c.get - area = lc.getNetArea - coll[id] = { area: area, lc: lc, s: {} } + lc = lc.get + area = lc.getNetArea + coll[id] = { area: area, lc: lc, s: {} } - model.getSurfaces.each do |sss| - next unless sss.surfaceType.downcase.include?(label.to_s) - next unless sss.outsideBoundaryCondition.downcase == "outdoors" - next if sss.construction.empty? - next if sss.construction.get.to_LayeredConstruction.empty? - lc = sss.construction.get.to_LayeredConstruction.get - next unless id == lc.nameString - nom = sss.nameString - film = sss.filmResistance if sss.filmResistance < film - ok = coll[id][:s].key?(nom) - coll[id][:s][nom] = { a: sss.netArea } unless ok - end - end + s.each do |nom, surface| + next unless surface.key?(:deratable ) + next unless surface.key?(:type ) + next unless surface.key?(:construction) + next unless surface.key?(:filmRSI ) + next unless surface.key?(:index ) + next unless surface.key?(:ltype ) + next unless surface.key?(:r ) + next unless surface[:deratable ] + next unless surface[:type ] == typ + next unless surface[:construction].is_a?(cl3) + next if surface[:index ].nil? + + i = surface[:construction].nameString + next unless i == id + + # Retain lowest surface film resistance (e.g. tilted surfaces). + film = surface[:filmRSI] if surface[:filmRSI] < film + + coll[i][:s][nom] = { a: surface[:net] } unless coll[i][:s].key?(nom) end end if coll.empty? - log(ERR, "No #{label} construction to uprate - skipping (#{mth})") + log(ERR, "No #{type} construction to uprate - skipping (#{mth})") next - elsif lc # valid layered construction - good to uprate! - # Ensure lc is referenced by surface types == label. - model.getSurfaces.each do |sss| - next if sss.construction.empty? - next if sss.construction.get.to_LayeredConstruction.empty? - c = sss.construction.get.to_LayeredConstruction.get - i = c.nameString - next unless coll.key?(i) - - unless sss.surfaceType.downcase.include?(label.to_s) - log(ERR, "Uprating #{label.to_s}, not '#{sss.nameString}' (#{mth})") - cloned = c.clone(model).to_LayeredConstruction.get - cloned.setName("'#{i}' cloned") - sss.setConstruction(cloned) - ok = s.key?(sss.nameString) - s[sss.nameString][:construction] = cloned if ok - coll[i][:s].delete(sss.nameString) - coll[i][:area] = c.getNetArea - next - end - end - + elsif lc + # Valid layered construction - good to uprate! lyr = insulatingLayer(lc) lyr[:index] = nil unless lyr[:index].is_a?(Numeric) lyr[:index] = nil unless lyr[:index] >= 0 lyr[:index] = nil unless lyr[:index] < lc.layers.size log(ERR, "Insulation index for '#{id}'? (#{mth})") unless lyr[:index] next unless lyr[:index] - hloss = 0 # sum of applicable psi & khi effects [W/K] - coll.each do |i, col| - next unless col.key?(:s) - next unless col.is_a?(Hash) + # Ensure lc is exclusively linked to deratable surfaces of right type. + # If not, assign new lc clone to non-targeted surfaces. + s.each do |nom, surface| + next unless surface.key?(:type ) + next unless surface.key?(:deratable ) + next unless surface.key?(:construction) + next unless surface[:construction].is_a?(cl3) + next unless surface[:construction] == lc + ok = true + ok = false unless surface[:type ] == typ + ok = false unless surface[:deratable] + ok = false unless coll.key?(id) + ok = false unless coll[id][:s].key?(nom) + + unless ok + log(WRN, "Cloning '#{nom}' construction - not '#{id}' (#{mth})") + sss = model.getSurfaceByName(nom) + next if sss.empty? + + sss = sss.get + cloned = lc.clone(model).to_LayeredConstruction.get + cloned.setName("#{nom} - cloned") + sss.setConstruction(cloned) + surface[:construction] = cloned + coll[id][:s].delete(nom) + end + end + + hloss = 0 # sum of applicable psi & khi effects [W/K] + + # Tally applicable psi + khi losses. Possible construction reassignment. + coll.each do |i, col| col[:s].keys.each do |nom| next unless s.key?(nom) - next unless s[nom].key?(:deratable ) next unless s[nom].key?(:construction) next unless s[nom].key?(:index ) next unless s[nom].key?(:ltype ) next unless s[nom].key?(:r ) - next unless s[nom].key?(:type ) - next unless s[nom][:deratable] - type = s[nom][:type].to_s.downcase - type = "roof" if type == "ceiling" - next unless type.include?(label.to_s) - # Tally applicable psi + khi. - hloss += s[nom][:heatloss] if s[nom].key?(:heatloss) + hloss += s[nom][:heatloss ] if s[nom].key?(:heatloss) + next if s[nom][:construction] == lc - # Skip construction reassignment if already referencing right one. - unless s[nom][:construction] == lc - sss = model.getSurfaceByName(nom) - next if sss.empty? - sss = sss.get + # Reassign construction unless referencing lc. + sss = model.getSurfaceByName(nom) + next if sss.empty? - if sss.isConstructionDefaulted - set = defaultConstructionSet(model, sss) - constructions = set.defaultExteriorSurfaceConstructions.get + sss = sss.get - case sss.surfaceType.downcase - when "roofceiling" - constructions.setRoofCeilingConstruction(lc) - when "floor" - constructions.setFloorConstruction(lc) - else - constructions.setWallConstruction(lc) - end - else - sss.setConstruction(lc) - end + if sss.isConstructionDefaulted + set = defaultConstructionSet(model, sss) # building? story? + constructions = set.defaultExteriorSurfaceConstructions - s[nom][:construction] = lc # reset TBD attributes - s[nom][:index ] = lyr[:index] - s[nom][:ltype ] = lyr[:type ] - s[nom][:r ] = lyr[:r ] # temporary + unless constructions.empty? + constructions = constructions.get + constructions.setWallConstruction(lc) if typ == :wall + constructions.setFloorConstruction(lc) if typ == :floor + constructions.setRoofCeilingConstruction(lc) if typ == :ceiling + end + else + sss.setConstruction(lc) end + + s[nom][:construction] = lc # reset TBD attributes + s[nom][:index ] = lyr[:index] + s[nom][:ltype ] = lyr[:type ] + s[nom][:r ] = lyr[:r ] # temporary end end # Merge to ensure a single entry for coll Hash. coll.each do |i, col| next if i == id - next unless coll.key?(id) - coll[id][:area] += col[:area] col[:s].each do |nom, sss| coll[id][:s][nom] = sss unless coll[id][:s].key?(nom) end end coll.delete_if { |i, _| i != id } log(DBG, "Collection == 1? for '#{id}' (#{mth})") unless coll.size == 1 next unless coll.size == 1 + area = lc.getNetArea + coll[id][:area] = area res = uo(model, lc, id, hloss, film, g[:ut]) log(ERR, "Unable to uprate '#{id}' (#{mth})") unless res[:uo] && res[:m] next unless res[:uo] && res[:m] lyr = insulatingLayer(lc) # Loop through coll :s, and reset :r - likely modified by uo(). coll.values.first[:s].keys.each do |nom| next unless s.key?(nom) - next unless s[nom].key?(:deratable ) - next unless s[nom].key?(:construction) - next unless s[nom].key?(:index ) - next unless s[nom].key?(:ltype ) - next unless s[nom].key?(:type ) + next unless s[nom].key?(:index) + next unless s[nom].key?(:ltype) + next unless s[nom].key?(:r ) + next unless s[nom][:index] == lyr[:index] + next unless s[nom][:ltype] == lyr[:type ] - next unless s[nom][:deratable ] - next unless s[nom][:construction] == lc - next unless s[nom][:index ] == lyr[:index] - next unless s[nom][:ltype ] == lyr[:type] - - type = s[nom][:type].to_s.downcase - type = "roof" if type == "ceiling" - next unless type.include?(label.to_s) - next unless s[nom].key?(:r) - s[nom][:r] = lyr[:r] # final + s[nom][:r] = lyr[:r] # uprated insulating RSi factor, before derating end - argh[:wall_uo ] = res[:uo] if label == :wall - argh[:roof_uo ] = res[:uo] if label == :roof - argh[:floor_uo] = res[:uo] if label == :floor + argh[:wall_uo ] = res[:uo] if typ == :wall + argh[:roof_uo ] = res[:uo] if typ == :ceiling + argh[:floor_uo] = res[:uo] if typ == :floor else log(ERR, "Nilled construction to uprate - (#{mth})") return false end end @@ -918,10 +927,10 @@ model = "" model = "* model : #{ua[:file]}" if ua.key?(:file) && lang == :en model = "* modèle : #{ua[:file]}" if ua.key?(:file) && lang == :fr model += " (v#{ua[:version]})" if ua.key?(:version) report << model unless model.empty? - report << "* TBD : v3.2.1" + report << "* TBD : v3.2.2" report << "* date : #{ua[:date]}" if lang == :en report << "* status : #{msg(status)}" unless status.zero? report << "* status : success !" if status.zero?