class BTAP::Bridging
—- —- —- —- —- —- —- —- —- —- —- —- —- —- —- #
Constants
- DBG
- ERR
- FTL
- INF
- TOL
- TOL2
- WRN
Attributes
@return [Hash] logged messages TBD reports back to BTAP
@return [Hash] BTAP/TBD Hash
, specific to an OpenStudio model
@return [Hash] TBD tallies e.g. total lengths of linear thermal bridges
Public Class Methods
Initialize OpenStudio model-specific BTAP/TBD data - uprates/derates.
@param model [OpenStudio::Model::Model] a model @param argh [Hash] BTAP/TBD argument hash
# File lib/openstudio-standards/btap/bridging.rb, line 1314 def initialize(model = nil, argh = {}) @model = {} @tally = {} @feedback = { logs: [] } lgs = @feedback[:logs] argh[:interpolate] = false unless argh.key?(:interpolate) # BTAP generates free-floating, unoccupied spaces (e.g. attics) as # 'indirectly conditioned', rather than 'unconditioned' (e.g. vented # attics). For instance, all outdoor-facing sloped roof surfaces of an # attic in BTAP are insulated, while attic floors remain uninsulated. BTAP # adds to the thermal zone of each unoccupied space a thermostat without # referencing heating and/or cooling setpoint schedule objects. These # conditions do not meet TBD's internal 'plenum' logic/check (which is # based on OpenStudio-Standards), and so TBD ends up tagging such spaces # as unconditioned. Consequently, TBD attempts to up/de-rate attic floors # - not sloped roof surfaces. The upstream BTAP solution will undoubtedly # need revision. In the meantime, and in an effort to harmonize TBD with # BTAP's current approach, an OpenStudio model may be temporarily # modified prior to TBD processes, ensuring that each attic space is # temporarily mistaken as a conditioned plenum. The return variable of the # following method is a Hash holding temporarily-modified spaces, # i.e. buffer zones. buffers = self.alter_buffers(model) # Populate BTAP/TBD inputs with BTAP & OpenStudio model parameters, # which returns 'true' if successful. Check @feedback logs if failure to # populate (e.g. invalid argument hash, invalid OpenStudio model). return unless self.populate(model, argh) # Initialize loop controls and flags. initial = true complies = false comply = {} # specific to :walls, :floors & :roofs perform = :lp # Low-performance wall constructions (revise, TO-DO ...) quality = :bad # default PSI factors - BTAP users can reset to :good quality = :good if argh.key?(:quality) && argh[:quality] == :good combo = "#{perform.to_s}_#{quality.to_s}".to_sym # e.g. :lp_bad args = {} # initialize native TBD arguments # Initialize surface types & native TBD args (if uprating). [:walls, :floors, :roofs].each do |stypes| next if @model[stypes].empty? next unless argh.key?(stypes) next unless argh[stypes].key?(:ut) stype = stypes.to_s.chop uprate = "uprate_#{stypes.to_s}".to_sym option = "#{stype}_option".to_sym ut = "#{stype}_ut".to_sym args[uprate ] = true args[option ] = "ALL #{stype} constructions" args[ut ] = argh[stypes][:ut] comply[stypes] = false @model[:constructions] = {} unless @model.key?(:constructions) end args[:io_path] = @model[combo] # contents of a "tbd.json" file args[:option ] = "" # safeguard loop do if initial initial = false else # Subsequent runs. Upgrade technologies. Reset TBD args. if quality == :bad quality = :good combo = "#{perform.to_s}_#{quality.to_s}".to_sym args[:io_path] = @model[combo] elsif perform == :lp # Switch 'perform' from :lp to :hp - reset quality to :bad. perform = :hp quality = :bad combo = "#{perform.to_s}_#{quality.to_s}".to_sym args[:io_path] = @model[combo] end # Delete previously-generated TBD args Uo key/value pairs. [:walls, :floors, :roofs].each do |stypes| next unless comply.key?(stypes) uo = "#{stypes.to_s.chop}_uo".to_sym args.delete(uo) if args.key?(uo) end # Reset previous @model constructions. @model.delete(:constructions) if @model.key?(:constructions) @model[:constructions] = {} end # Run TBD on cloned OpenStudio model - compliant combo? mdl = OpenStudio::Model::Model.new mdl.addObjects(model.toIdfFile.objects) TBD.clean! res = TBD.process(mdl, args) # Halt all processes if fatal errors raised by TBD (e.g. badly formatted # TBD arguments, poorly-structured OpenStudio models). if TBD.fatal? TBD.logs.each { |lg| lgs << lg[:message] if lg[:level] == TBD::FTL } break end complies = true # Check if TBD-uprated Uo factors are valid: TBD args Hash holds (new) # uprated Uo keys/values for :walls, :floors AND/OR :roofs if uprating # is successful. In most cases, uprating tends to fail for wall # constructions rather than roof or floor constructions, due to the # typically larger density of linear thermal bridging per surface type # area. Yet even if all constructions were successfully uprated by TBD, # one must then determine if BTAP holds admissible (i.e. costed) # assembly variants with corresponding Uo factors (see :uos key). If # TBD-uprated Uo factors are lower than any of these admissible BTAP Uo # factors, then no commercially available solution can been identified. [:walls, :floors, :roofs].each do |stypes| next unless comply.key?(stypes) # true only if uprating stype_uo = "#{stypes.to_s.chop}_uo".to_sym target = nil # uprated Uo (if successful) target = args[stype_uo] if args.key?(stype_uo) # ... may be nil comply[stypes] = true @model[stypes].each do |id, surface| next unless surface.key?(:sptype) sptype = surface[:sptype] # e.g. :office next unless @model[:sptypes].key?(sptype) next unless @model[:sptypes][sptype].key?(stypes) next unless @model[:sptypes][sptype][stypes].key?(perform) construction = @model[:sptypes][sptype][stypes][perform] uo = nil ok = true uo = self.costed_uo(construction, target) if target ok = false if uo.nil? uo = target if ok && argh[:interpolate] uo = self.lowest_uo(construction) unless ok # fallback comply[stypes] = false unless ok unless @model[:constructions].key?(construction) @model[:constructions][construction] = {} @model[:constructions][construction][:stypes ] = stypes @model[:constructions][construction][:uo ] = uo @model[:constructions][construction][:compliant] = ok @model[:constructions][construction][:surfaces ] = {} end face = model.getSurfaceByName(id) next if face.empty? face = face.get @model[:constructions][construction][:surfaces][id] = face end complies = false unless comply[stypes] end break if complies # Final BTAP uprating option, yet non-compliant: TBD's uprating # features are requested, yet unable to locate either a physically- or # economically-plausible Uo + PSI combo for 1x or more surface types. break if combo == :hp_good end # of loop # Post-loop steps (if uprating). [:walls, :floors, :roofs].each do |stypes| next unless comply.key?(stypes) # true only if uprating # Cancel uprating request before final derating. stype = stypes.to_s.chop uprate = "uprate_#{stypes.to_s}".to_sym option = "#{stype}_option".to_sym ut = "#{stype}_ut".to_sym args.delete(uprate) args.delete(option) args.delete(ut ) # Set uprated Uo factor for each BTAP 'deratable' construction. @model[:constructions].each do |id, construction| next unless construction.key?(:stypes ) next unless construction.key?(:uo ) next unless construction.key?(:compliant) next unless construction.key?(:surfaces ) next unless construction[:stypes ] == stypes next if construction[:surfaces].empty? construction[:surfaces].values.each { |surface| surface.setConstruction(construction[:uo]) } end end @model[:comply ] = comply @model[:complies] = complies @model[:perform ] = perform @model[:quality ] = quality @model[:combo ] = combo # Run "process" TBD one last time, on "model" (not cloned "mdl"). TBD.clean! res = TBD.process(model, args) @model[:io ] = res[:io ] # TBD outputs (i.e. "tbd.out.json") @model[:surfaces] = res[:surfaces] # TBD derated surface data @model[:argh ] = argh # method argument Hash @model[:args ] = args # last TBD inputs (i.e. "tbd.json") self.gen_tallies # tallies for BTAP costing self.gen_feedback # log success messages for BTAP self.purge_buffer_schedules(model, buffers) end
Public Instance Methods
Modify BTAP-generated ‘buffer zones’ (e.g. attics) to ensure TBD tags these as indirectly conditioned spaces (e.g. plenums).
@param model [OpenStudio::Model::Model] a model
@return [Array] identifiers of modified buffer spaces in model
# File lib/openstudio-standards/btap/bridging.rb, line 1537 def alter_buffers(model = nil) buffers = [] sched = nil lgs = @feedback[:logs] cl = OpenStudio::Model::Model lgs << "Invalid OpenStudio model (buffers)" unless model.is_a?(cl) return buffers unless model.is_a?(cl) model.getSpaces.each do |space| next if space.partofTotalFloorArea next if space.thermalZone.empty? id = space.nameString zone = space.thermalZone.get next if zone.isPlenum next if zone.thermostat.empty? tstat = zone.thermostat.get staged = tstat.respond_to?(:heatingTemperatureSetpointSchedule) tstat = tstat.to_ZoneControlThermostatStagedDualSetpoint.get if staged tstat = tstat.to_ThermostatSetpointDualSetpoint.get unless staged if sched.nil? name = "TBD attic setpoint sched" sched = OpenStudio::Model::ScheduleCompact.new(model) sched.setName(name) end tstat.setHeatingTemperatureSetpointSchedule(sched) if staged tstat.setHeatingSetpointTemperatureSchedule(sched) unless staged buffers << id end buffers end
Generate BTAP/TBD post-processing feedback.
@return [Boolean] true if valid BTAP/TBD model
# File lib/openstudio-standards/btap/bridging.rb, line 1891 def gen_feedback lgs = @feedback[:logs] return false unless @model.key?(:complies) # all model constructions return false unless @model.key?(:comply ) # surface type specific ... return false unless @model.key?(:argh ) # BTAP/TBD inputs + ouputs argh = @model[:argh] # Uprating. Report first on surface types (compliant or not). [:walls, :floors, :roofs].each do |stypes| next unless @model[:comply].key?(stypes) ut = format("%.3f", argh[stypes][:ut]) lg = "Compliant " if @model[:comply][stypes] lg = "Non-compliant " unless @model[:comply][stypes] lg += "#{stypes}: Ut #{ut} W/m2.K" lgs << lg # Report then on required Uo factor per construction (compliant or not). @model[:constructions].each do |id, construction| next unless construction.key?(:stypes ) next unless construction.key?(:uo ) next unless construction.key?(:compliant) next unless construction.key?(:surfaces ) next unless construction[:stypes ] == stypes next if construction[:surfaces].empty? uo = format("%.3f", construction[:uo]) lg = " Compliant " if construction[:compliant] lg = " Non-compliant " unless construction[:compliant] lg += "#{id} Uo #{uo} (W/K.m2)" lgs << lg end end # Summary of TBD-derated constructions. @model[:osm].getSurfaces.each do |s| next if s.construction.empty? next if s.construction.get.to_LayeredConstruction.empty? lc = s.construction.get.to_LayeredConstruction.get id = lc.nameString next unless id.include?(" c tbd") rsi = TBD.rsi(lc, s.filmResistance) usi = format("%.3f", 1/rsi) rsi = format("%.1f", rsi) area = format("%.1f", lc.getNetArea) + " m2" lgs << "~ '#{id}' derated Rsi: #{rsi} [Usi #{usi} x #{area}]" end # Log PSI factor tallies (per thermal bridge type). if @tally.key?(:edges) @tally[:edges].each do |type, e| next if type == :transition lgs << "# '#{type}' (#{e.size}x):" e.each do |psi, length| l = format("%.2f", length) lgs << "... PSI set '#{psi}' : #{l} m" end end end true end
Generate BTAP/TBD tallies
@return [Boolean] true if BTAP/TBD tally is successful
# File lib/openstudio-standards/btap/bridging.rb, line 1859 def gen_tallies edges = {} return false unless @model.key?(:io) return false unless @model[:io].key?(:edges) @model[:io][:edges].each do |e| # Content of TBD-generated 'edges' (hashes): # psi: BTAP PSI set ID, e.g. "BTAP-ExteriorWall-Mass-6 good" # type: thermal bridge type, e.g. :corner # length: (in m) # surfaces: linked OpenStudio surface IDs edges[e[:type]] = {} unless edges.key?(e[:type]) edges[e[:type]][e[:psi]] = 0 unless edges[e[:type]].key?(e[:psi]) edges[e[:type]][e[:psi]] += e[:length] end return false if edges.empty? @tally[:edges] = edges # Add final selection of (uprated) Uo factors per BTAP construction. return true unless @model.key?(:constructions) @tally[:constructions] = @model[:constructions] true end
# File lib/openstudio-standards/btap/bridging.rb, line 1960 def get_material_quantities() material_quantities = {} csv = CSV.read("#{File.dirname(__FILE__)}/../../../data/inventory/thermal_bridging.csv", headers: true) tally_edges = @tally[:edges].transform_keys(&:to_s) #tally_edges = JSON.parse('{"edges":{"jamb":{"BTAP-ExteriorWall-SteelFramed-1 good":13.708557548340757},"sill":{"BTAP-ExteriorWall-SteelFramed-1 good":90.13000000000001},"head":{"BTAP-ExteriorWall-SteelFramed-1 good":90.13000000000001},"gradeconvex":{"BTAP-ExteriorWall-SteelFramed-1 good":90.4348},"parapetconvex":{"BTAP-ExteriorWall-SteelFramed-1 good":45.2174},"parapet":{"BTAP-ExteriorWall-SteelFramed-1 good":45.2174},"transition":{"BTAP-ExteriorWall-SteelFramed-1 good":71.16038874419307},"cornerconvex":{"BTAP-ExteriorWall-SteelFramed-1 good":12.1952}}}')['edges'] tally_edges.each do |edge_type_full, value| edge_type = edge_type_full.delete_suffix('convex') if ['head', 'jamb', 'sill'].include?(edge_type) edge_type = 'fenestration' end value.each do |wall_ref_and_quality, quantity| /(.*)\s(.*)/ =~ wall_ref_and_quality wall_reference = $1 quality = $2 if wall_reference =='BTAP-ExteriorWall-SteelFramed-1' wall_reference = 'BTAP-ExteriorWall-SteelFramed-2' end if edge_type == 'transition' next end result = csv.find { |row| row['edge_type'] == edge_type && row['quality'] == quality && row['wall_reference'] == wall_reference } if result.nil? puts ("#{edge_type}-#{wall_reference}-#{quality}") puts "not found in tb database" next end # Split material_opaque_id_layers = result['material_opaque_id_layers'].split(",") id_layers_quantity_multipliers = result['id_layers_quantity_multipliers'].split(",") material_opaque_id_layers.zip(id_layers_quantity_multipliers).each do |id, scale| if material_quantities[id].nil? then material_quantities[id] = 0.0 end material_quantities[id] = material_quantities[id] + scale.to_f * quantity.to_f end end end material_opaque_id_quantities = [] material_quantities.each do |id,quantity| material_opaque_id_quantities << { 'materials_opaque_id' => id, 'quantity' => quantity, 'domain'=> 'thermal_bridging' } end return material_opaque_id_quantities end
Generate (native) TBD input hash.
@param perform [Symbol] :lp or :hp wall variant @param quality [Symbol] :bad or :good PSI-factor
@return [Hash] native TBD inputs
# File lib/openstudio-standards/btap/bridging.rb, line 1803 def inputs(perform = :hp, quality = :good) input = {} psis = {} # construction-specific PSI sets sptypes = {} # space type-specific references to previous PSI sets perform = :hp unless perform == :lp || perform == :hp quality = :good unless quality == :bad || quality == :good # Once building-type construction selection is introduced within BTAP, # define default TBD "building" PSI set. In the meantime, this is added # strictly as a backup solution (just in case). building = self.set(STEL1, quality) if perform == :lp building = self.set(STEL2, quality) if perform == :hp psis[ building[:id] ] = building # Collect unique BTAP/TBD instances. combo = "#{perform.to_s}_#{quality.to_s}".to_sym @model[:sptypes].values.each do |sptype| next unless sptype.key?(combo) psi = sptype[combo] next if psis.key?(psi[:id]) psis[ psi[:id] ] = psi end # TBD JSON schema added as a reminder. No schema validation in BTAP. schema = "https://github.com/rd2/tbd/blob/master/tbd.schema.json" input[:schema ] = schema input[:description] = "TBD input for BTAP" # append run # ? input[:psis ] = psis.values @model[:sptypes].values.each do |sptype| next unless sptype.key?(combo) next unless sptype.key?(:sptype) next if sptypes.key?(sptype[:sptype]) sptypes[ sptype[:sptype] ] = { psi: sptype[combo][:id] } end sptypes.each do |id, sptype| input[:spacetypes] = [] unless input.key?(:spacetypes) input[:spacetypes] << { id: id, psi: sptype[:psi] } end input[:building] = { psi: building[:id] } input end
Fetch min U-factor of outdoor-facing OpenStudio model surface types.
@param model [OpenStudio::Model::Model] a model @param stype [Symbol] model surface type (e.g. :walls)
@return [Float] min U factor (default 5.678 W/m2.K)
# File lib/openstudio-standards/btap/bridging.rb, line 1632 def minU(model = nil, stypes = :walls) u = UMAX lgs = @feedback[:logs] cl = OpenStudio::Model::Model stype = stypes.to_s.chop.downcase ok = stype == "wall" || stype == "floor" || stype == "roof" stype = "wall" unless ok lgs << "Invalid OpenStudio model (#{stypes} minU)" unless model.is_a?(cl) return u unless model.is_a?(cl) model.getSurfaces.each do |s| next unless s.surfaceType.downcase.include?(stype) next unless s.outsideBoundaryCondition.downcase == "outdoors" next if s.construction.empty? next if s.construction.get.to_LayeredConstruction.empty? lc = s.construction.get.to_LayeredConstruction.get uo = 1 / TBD.rsi(lc, s.filmResistance) u = [uo, u].min end # u0 = format("%.3f", u) # TEMPORARY # puts "~~ Extracted #{stypes} minU (#{u0}) W/m2.K from OpenStudio model" u end
Populate BTAP/TBD model with BTAP
& OpenStudio model parameters.
@param model [OpenStudio::Model::Model] a model @param argh [Hash] BTAP/TBD argument hash
@return [Boolean] true if valid (check @feedback logs if false)
# File lib/openstudio-standards/btap/bridging.rb, line 1667 def populate(model = nil, argh = {}) cl = OpenStudio::Model::Model args = { option: "(non thermal bridging)" } # for initial TBD dry run lgs = @feedback[:logs] lgs << "Invalid OpenStudio model to de/up-rate" unless model.is_a?(cl) lgs << "Invalid BTAP/TBD argument Hash" unless argh.is_a?(Hash) lgs << "Empty BTAP/TBD argument hash" if argh.empty? return false unless model.is_a?(cl) return false unless argh.is_a?(Hash) return false if argh.empty? # Fetch number of stories in OpenStudio model. stories = model.getBuilding.standardsNumberOfAboveGroundStories stories = stories.get unless stories.empty? stories = model.getBuildingStorys.size unless stories.is_a?(Integer) @model[:stories] = stories @model[:stories] = 1 if stories < 1 @model[:stories] = 999 if stories > 999 @model[:spaces ] = {} @model[:sptypes] = {} # Run TBD on a cloned OpenStudio model (dry run). mdl = OpenStudio::Model::Model.new mdl.addObjects(model.toIdfFile.objects) TBD.clean! res = TBD.process(mdl, args) surfaces = res[:surfaces] # TBD validation of OpenStudio model. lgs << "TBD-identified FATAL error(s):" if TBD.fatal? lgs << "TBD-identified non-FATAL error(s):" if TBD.error? TBD.logs.each { |log| lgs << log[:message] } if TBD.fatal? || TBD.error? return false if TBD.fatal? lgs << "TBD: no deratable surfaces in model" if surfaces.nil? return false if surfaces.nil? # Initialize deratable walls, exposed floors & roofs. [:walls, :floors, :roofs].each { |stypes| @model[stypes] = {} } surfaces.each do |id, surface| next unless surface.key?(:type ) # :wall, :floor, :ceiling next unless surface.key?(:space ) # OpenStudio space object next unless surface.key?(:deratable) # true/false next unless surface[:deratable] stypes = :walls if surface[:type] == :wall stypes = :floors if surface[:type] == :floor stypes = :roofs if surface[:type] == :ceiling next unless stypes == :walls || stypes == :floors || stypes == :roofs space = surface[:space].nameString sptype = surface[:stype].nameString if surface.key?(:stype) sptype = "" unless surface.key?(:stype) typ = self.spacetype(sptype, @model[:stories]) # e.g. :office # Keep track of individual surface's space and spacetype keyword. @model[stypes][id] = {} @model[stypes][id][:space ] = space @model[stypes][id][:sptype] = typ # Keep track of individual spaces and spacetypes. exists = @model[:spaces].key?(space) @model[:spaces][space] = {} unless exists @model[:spaces][space][:sptype] = typ unless exists exists = @model[:sptypes].key?(typ) @model[:sptypes][typ ] = {} unless exists @model[:sptypes][typ ][:sptype] = sptype unless exists next if @model[:sptypes][typ].key?(stypes) # Low- vs Hi-Performance BTAP assemblies. lo = self.assembly(typ, stypes, :lp) hi = self.assembly(typ, stypes, :hp) @model[:sptypes][typ][stypes] = {} @model[:sptypes][typ][stypes][:lp] = lo @model[:sptypes][typ][stypes][:hp] = hi next unless stypes == :walls # Fetch bad vs good PSI factor sets - strictly a function of walls. @model[:sptypes][typ][:lp_bad ] = self.set(lo, :bad ) @model[:sptypes][typ][:lp_good] = self.set(lo, :good) @model[:sptypes][typ][:hp_bad ] = self.set(hi, :bad ) @model[:sptypes][typ][:hp_good] = self.set(hi, :good) end # BTAP-fed Uo (+ optional Ut) factors. [:walls, :floors, :roofs].each do |stypes| lgs << "Missing BTAP/TBD #{stypes}" unless argh.key?(stypes) lgs << "Missing BTAP/TBD #{stypes} Uo" unless argh[stypes].key?(:uo) return false unless argh.key?(stypes) return false unless argh[stypes].key?(:uo) next if @model[stypes].empty? uo = argh[stypes][:uo] ok = uo.is_a?(Numeric) && uo.between?(UMIN, UMAX) next if ok uo = self.minU(model, stypes) ok = uo.is_a?(Numeric) && uo.between?(UMIN, UMAX) lgs << "Invalid BTAP/TBD #{stypes} Uo" unless ok return false unless ok argh[stypes][:uo] = uo next unless argh[stypes].key?(:ut) argh[stypes][:ut] = uo end # Generate native TBD input Hashes for the model, for both :good & :bad # PSI factor sets. The typical TBD use case involves writing out the # contents of either Hash (e.g. JSON::pretty_generate) as a "tbd.json" # input file, to save under a standard OpenStudio "files" folder. At # runtime, TBD then reopens the JSON file and populates its own data # model in memory. Yet BTAP is not a typical use case. To avoid writing # out (then re-reading) TBD JSON files/hashes (i.e. resource intensive), # BTAP/TBD instead populates the TBD data model directly. @model[:lp_bad ] = self.inputs(:lp, :bad ) @model[:lp_good] = self.inputs(:lp, :good) @model[:hp_bad ] = self.inputs(:hp, :bad ) @model[:hp_good] = self.inputs(:hp, :good) @model[:osm ] = model true end
Remove previously BTAP/TBD-added heating setpoint schedules for ‘buffer zones’ (e.g. attics).
@param model [OpenStudio::Model::Model] a model @param buffers [Array] identifiers of modified buffer spaces in model
@return [Boolean] true if successful
# File lib/openstudio-standards/btap/bridging.rb, line 1582 def purge_buffer_schedules(model = nil, buffers = []) scheds = [] lgs = @feedback[:logs] cl = OpenStudio::Model::Model lgs << "Invalid OpenStudio model (purge)" unless model.is_a?(cl) lgs << "Invalid BTAP/TBD buffers" unless buffers.is_a?(Array) return false unless model.is_a?(cl) return false unless buffers.is_a?(Array) buffers.each do |id| space = model.getSpaceByName(id) next if space.empty? space = space.get next if space.thermalZone.empty? zone = space.thermalZone.get next if zone.thermostat.empty? tstat = zone.thermostat.get staged = tstat.respond_to?(:heatingTemperatureSetpointSchedule) tstat = tstat.to_ZoneControlThermostatStagedDualSetpoint.get if staged tstat = tstat.to_ThermostatSetpointDualSetpoint.get unless staged sched = tstat.heatingTemperatureSetpointSchedule if staged sched = tstat.heatingSetpointTemperatureSchedule unless staged next if sched.empty? sched = sched.get scheds << sched.nameString tstat.resetHeatingSetpointTemperatureSchedule unless staged tstat.resetHeatingTemperatureSetpointSchedule if staged end scheds.each do |sched| schd = model.getScheduleByName(sched) next if schd.empty? schd = schd.get schd.remove end true end