openstudio-analysis 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: be9085031fd83339356ac4cab40cf3bdf81cae72
4
- data.tar.gz: bb97893884c7f03736007c404b1cd45428ea90bb
3
+ metadata.gz: 0aca7ae94b4ed75738defa00e6044a374c68b0ee
4
+ data.tar.gz: c4f630cb8897524e16b021546e93f0c5b148aeb8
5
5
  SHA512:
6
- metadata.gz: 3fbaacff064b7527ed31a31fd971c000a37c2c0ad79e078e09a6eed89d1d504797e3bdef7db3649310f2b1db3e63a0cbe64eb19c80570db12fd6c87d85aa0667
7
- data.tar.gz: b6b7388412ea35b65dc0a2705f7b176059e2df2def43fc3bf0eaac34dab89f25190374a9b124339e56b5538c253c78a72ecce4277862ba72200ea9968adba515
6
+ metadata.gz: 16d7f355acd2f253b27dd48fdff4fec5244f9eb5afcd99bd1c9403b7d0f6db15e30908625c27ddf28062ce4cb1d4207a3900aa65b3c539031fc9d39521a15b06
7
+ data.tar.gz: de301fb8cb527f079fb5b4aa3a7927aa772a1f77691c7d07e7b078941331bbb8218f0918ad5fee498fbee57779fbc707d545858bbb4d31e8da0bc45f070954e8
@@ -5,11 +5,17 @@ module OpenStudio
5
5
  attr_reader :variables
6
6
  attr_reader :models
7
7
  attr_reader :weather_files
8
-
8
+ attr_reader :measure_path
9
+ attr_reader :export_path
10
+ attr_reader :variables
11
+
9
12
  # remove these once we have classes to construct the JSON file
10
13
  attr_reader :name
11
14
  attr_reader :number_of_samples
15
+ attr_reader :template_json
12
16
 
17
+ # methods to override instance variables
18
+
13
19
  # pass in the filename to read
14
20
  def initialize(xls_filename)
15
21
  @root_path = File.expand_path(File.dirname(xls_filename))
@@ -29,7 +35,10 @@ module OpenStudio
29
35
  @export_path = "./export"
30
36
  @measure_path = "./measures"
31
37
  @number_of_samples = 0
32
-
38
+ @template_json = nil
39
+ end
40
+
41
+ def process
33
42
  @setup = parse_setup()
34
43
  @variables = parse_variables()
35
44
 
@@ -43,7 +52,6 @@ module OpenStudio
43
52
  File.open(filename, 'w') { |f| f << JSON.pretty_generate(@variables) }
44
53
  end
45
54
 
46
-
47
55
  def validate_analysis
48
56
  # Setup the paths and do some error checking
49
57
  raise "Measures directory '#{@measure_path}' does not exist" unless Dir.exists?(@measure_path)
@@ -68,28 +76,71 @@ module OpenStudio
68
76
  end
69
77
 
70
78
  FileUtils.mkdir_p(@export_path)
79
+
80
+ # verify that all continuous variables have all the data needed and create a name maps
81
+ variable_names = []
82
+ @variables['data'].each do |measure|
83
+ if measure['enabled'] && measure['name'] != 'baseline'
84
+ measure['variables'].each do |variable|
85
+ # Determine if row is suppose to be an argument or a variable to be perturbed.
86
+ if variable['variable_type'] == 'variable'
87
+ variable_names << variable['display_name']
88
+ if variable['method'] == 'static'
89
+ # add this as an argument
90
+ # check something
91
+ elsif variable['method'] == 'lhs'
92
+ if variable['type'] == 'enum'
93
+ # check something
94
+ else # must be an integer or double
95
+ if variable['distribution']['min'].nil? || variable['distribution']['min'] == ""
96
+ raise "Variable #{measure['name']}:#{variable['name']} must have a minimum"
97
+ end
98
+ if variable['distribution']['max'].nil? || variable['distribution']['max'] == ""
99
+ raise "Variable #{measure['name']}:#{variable['name']} must have a maximum"
100
+ end
101
+ if variable['distribution']['mean'].nil? || variable['distribution']['mean'] == ""
102
+ raise "Variable #{measure['name']}:#{variable['name']} must have a mean"
103
+ end
104
+ if variable['distribution']['stddev'].nil? || variable['distribution']['stddev'] == ""
105
+ raise "Variable #{measure['name']}:#{variable['name']} must have a stddev"
106
+ end
107
+ if variable['distribution']['min'] > variable['distribution']['max']
108
+ raise "Variable min is greater than variable max for #{measure['name']}:#{variable['name']}"
109
+ end
110
+ end
111
+ elsif variable['method'] == 'pivot'
112
+ # check something
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ dupes = variable_names.find_all { |e| variable_names.count(e) > 1 }.uniq
120
+ if dupes.count > 0
121
+ raise "duplicate variable names found in list #{dupes.inspect}"
122
+ end
123
+
124
+ # most of the checks will raise a runtime exception, so this true will never be called
125
+ true
71
126
  end
72
127
 
73
128
  def save_analysis
74
- # save the format in the OpenStudio analysis json format
75
- new_json = translate_to_analysis_json()
129
+ # save the format in the OpenStudio analysis json format template without
130
+ # the correct weather files or models
131
+ @template_json = translate_to_analysis_json_template()
132
+
133
+ #validate_template_json
76
134
 
77
135
  # iterate over each model and save the zip and json
78
136
  @models.each do |model|
79
137
  save_analysis_zip(model)
80
- save_analysis_json(new_json, model)
138
+ analysis_json = create_analysis_json(@template_json, model)
81
139
  end
82
140
  end
83
141
 
84
- protected
85
-
86
- # helper method for ERB
87
- def get_binding
88
- binding
89
- end
90
-
91
142
  # TODO: move this into a new class that helps construct this file
92
- def translate_to_analysis_json
143
+ def translate_to_analysis_json_template
93
144
  # Load in the templates for constructing the JSON file
94
145
  template_root = File.join(File.dirname(__FILE__), "../../templates")
95
146
  analysis_template = ERB.new(File.open("#{template_root}/analysis.json.erb", 'r').read)
@@ -148,8 +199,9 @@ module OpenStudio
148
199
  end
149
200
  ag = JSON.parse(argument_template.result(get_binding))
150
201
  end
202
+ raise "Argument '#{@variable['name']}' did not process. Most likely it did not have all parameters defined." if ag.nil?
151
203
  wf['arguments'] << ag
152
- else # must be a vriable
204
+ else # must be a variable
153
205
  vr = nil
154
206
  if @variable['method'] == 'static'
155
207
  # add this as an argument
@@ -165,6 +217,7 @@ module OpenStudio
165
217
  @values_and_weights = @variable['distribution']['enumerations'].map { |v| {value: v} }.to_json
166
218
  vr =JSON.parse(pivot_variable_template.result(get_binding))
167
219
  end
220
+ raise "variable was nil after processing" if vr.nil?
168
221
  wf['variables'] << vr
169
222
  end
170
223
  end
@@ -176,6 +229,14 @@ module OpenStudio
176
229
  openstudio_analysis_json
177
230
  end
178
231
 
232
+ protected
233
+
234
+ # helper method for ERB
235
+ def get_binding
236
+ binding
237
+ end
238
+
239
+
179
240
  # Package up the seed, weather files, and measures
180
241
  def save_analysis_zip(model)
181
242
  zipfile_name = "#{@export_path}/#{model[:name]}.zip"
@@ -206,7 +267,7 @@ module OpenStudio
206
267
  end
207
268
  end
208
269
 
209
- def save_analysis_json(analysis_json, model)
270
+ def create_analysis_json(analysis_json, model)
210
271
  # Set the seed model in the analysis_json
211
272
  analysis_json['analysis']['seed']['file_type'] = model[:type]
212
273
  # This is the path that will be seen on the server when this runs
@@ -227,8 +288,7 @@ module OpenStudio
227
288
 
228
289
  File.open("#{@export_path}/#{model[:name]}.json", "w") { |f| f << JSON.pretty_generate(analysis_json) }
229
290
  end
230
-
231
-
291
+
232
292
  # parse_setup will pull out the data on the "setup" tab and store it in memory for later use
233
293
  def parse_setup()
234
294
  rows = @xls.sheet('Setup').parse()
@@ -302,6 +362,11 @@ module OpenStudio
302
362
  # be omitted as an intermediate step
303
363
  def parse_variables()
304
364
  rows = @xls.sheet('Variables').parse()
365
+
366
+ if !rows
367
+ raise "Could not find the sheet name 'Variables' in excel file #{@root_path}"
368
+ end
369
+
305
370
  data = {}
306
371
  data['data'] = []
307
372
 
@@ -1,6 +1,6 @@
1
1
  module OpenStudio
2
2
  module Analysis
3
- VERSION = "0.1.3"
4
- OPENSTUDIO_VERSION = "1.1.2"
3
+ VERSION = "0.1.4"
4
+ OPENSTUDIO_VERSION = "1.1.4"
5
5
  end
6
6
  end
@@ -1,16 +1,19 @@
1
1
  {
2
2
  "analysis": {
3
- "display_name": "<%= @name %>",
3
+ "display_name": "<%= @name %>",
4
4
  "name": "<%= @name.downcase.gsub(" ", "_") %>",
5
5
  "algorithm": {
6
6
  "sample_method": "lhs",
7
- "sensitivity_method": "single_measure",
8
7
  "number_of_samples": <%= @number_of_samples %>
9
8
  },
10
9
  "parameter_space": [],
11
10
  "problem": {
12
11
  "number_of_samples_KEEP_HERE_UNTIL_ALGO_IS_IMPLEMENTED": <%= @number_of_samples %>,
13
12
  "number_of_samples": <%= @number_of_samples %>,
13
+ "algorithm": {
14
+ "number_of_samples": <%= @number_of_samples %>,
15
+ "sample_method": "all_variables"
16
+ },
14
17
  "name": "Problem",
15
18
  "workflow": []
16
19
  },
@@ -0,0 +1,36 @@
1
+ {
2
+ "classname": "IncreaseInsulationRValueForRoofs",
3
+ "path": "./measures/IncreaseInsulationRValueForRoofs",
4
+ "name": "Set R-value of Insulation for Roofs to a Specific Value",
5
+ "measure_type": "RubyMeasure",
6
+ "arguments": [
7
+ {
8
+ "local_variable": "r_value",
9
+ "variable_type": "Double",
10
+ "name": "r_value",
11
+ "display_name": "Insulation R-value (ft^2*h*R/Btu).",
12
+ "default_value": 30.0
13
+ },
14
+ {
15
+ "local_variable": "material_cost_increase_ip",
16
+ "variable_type": "Double",
17
+ "name": "material_cost_increase_ip",
18
+ "display_name": "Increase in Material and Installation Costs for Construction per Area Used ($/ft^2).",
19
+ "default_value": 0.0
20
+ },
21
+ {
22
+ "local_variable": "one_time_retrofit_cost_ip",
23
+ "variable_type": "Double",
24
+ "name": "one_time_retrofit_cost_ip",
25
+ "display_name": "One Time Retrofit Cost to Add Insulation to Construction ($/ft^2).",
26
+ "default_value": 0.0
27
+ },
28
+ {
29
+ "local_variable": "years_until_retrofit_cost",
30
+ "variable_type": "Integer",
31
+ "name": "years_until_retrofit_cost",
32
+ "display_name": "Year to Incur One Time Retrofit Cost (whole years).",
33
+ "default_value": 0
34
+ }
35
+ ]
36
+ }
@@ -0,0 +1,375 @@
1
+ #start the measure
2
+ class IncreaseInsulationRValueForRoofs < OpenStudio::Ruleset::ModelUserScript
3
+
4
+ #define the name that a user will see
5
+ def name
6
+ return "Increase R-value of Insulation for Roofs to a Specific Value"
7
+ end
8
+
9
+ #define the arguments that the user will input
10
+ def arguments(model)
11
+ args = OpenStudio::Ruleset::OSArgumentVector.new
12
+
13
+ #make an argument insulation R-value
14
+ r_value = OpenStudio::Ruleset::OSArgument::makeDoubleArgument("r_value",true)
15
+ r_value.setDisplayName("Insulation R-value (ft^2*h*R/Btu).")
16
+ r_value.setDefaultValue(30.0)
17
+ args << r_value
18
+
19
+ #make an argument for material and installation cost
20
+ material_cost_increase_ip = OpenStudio::Ruleset::OSArgument::makeDoubleArgument("material_cost_increase_ip",true)
21
+ material_cost_increase_ip.setDisplayName("Increase in Material and Installation Costs for Construction per Area Used ($/ft^2).")
22
+ material_cost_increase_ip.setDefaultValue(0.0)
23
+ args << material_cost_increase_ip
24
+
25
+ #make an argument for demolition cost
26
+ one_time_retrofit_cost_ip = OpenStudio::Ruleset::OSArgument::makeDoubleArgument("one_time_retrofit_cost_ip",true)
27
+ one_time_retrofit_cost_ip.setDisplayName("One Time Retrofit Cost to Add Insulation to Construction ($/ft^2).")
28
+ one_time_retrofit_cost_ip.setDefaultValue(0.0)
29
+ args << one_time_retrofit_cost_ip
30
+
31
+ #make an argument for duration in years until costs start
32
+ years_until_retrofit_cost = OpenStudio::Ruleset::OSArgument::makeIntegerArgument("years_until_retrofit_cost",true)
33
+ years_until_retrofit_cost.setDisplayName("Year to Incur One Time Retrofit Cost (whole years).")
34
+ years_until_retrofit_cost.setDefaultValue(0)
35
+ args << years_until_retrofit_cost
36
+
37
+ return args
38
+ end #end the arguments method
39
+
40
+ #define what happens when the measure is run
41
+ def run(model, runner, user_arguments)
42
+ super(model, runner, user_arguments)
43
+
44
+ #use the built-in error checking
45
+ if not runner.validateUserArguments(arguments(model), user_arguments)
46
+ return false
47
+ end
48
+
49
+ #assign the user inputs to variables
50
+ r_value = runner.getDoubleArgumentValue("r_value",user_arguments)
51
+ material_cost_increase_ip = runner.getDoubleArgumentValue("material_cost_increase_ip",user_arguments)
52
+ one_time_retrofit_cost_ip = runner.getDoubleArgumentValue("one_time_retrofit_cost_ip",user_arguments)
53
+ years_until_retrofit_cost = runner.getIntegerArgumentValue("years_until_retrofit_cost",user_arguments)
54
+
55
+ #set limit for minimum insulation. This is used to limit input and for inferring insulation layer in construction.
56
+ min_expected_r_value_ip = 1 #ip units
57
+
58
+ #check the R-value for reasonableness
59
+ if r_value < 0 or r_value > 500
60
+ runner.registerError("The requested roof insulation R-value of #{r_value} ft^2*h*R/Btu was above the measure limit.")
61
+ return false
62
+ elsif r_value > 60
63
+ runner.registerWarning("The requested roof insulation R-value of #{r_value} ft^2*h*R/Btu is abnormally high.")
64
+ elsif r_value < min_expected_r_value_ip
65
+ runner.registerWarning("The requested roof insulation R-value of #{r_value} ft^2*h*R/Btu is abnormally low.")
66
+ end
67
+
68
+ #check lifecycle arguments for reasonableness
69
+ if not years_until_retrofit_cost >= 0 and not years_until_retrofit_cost <= 100
70
+ runner.registerError("Year to incur one time retrofit cost should be a non-negative integer less than or equal to 100.")
71
+ end
72
+
73
+ #short def to make numbers pretty (converts 4125001.25641 to 4,125,001.26 or 4,125,001). The definition be called through this measure
74
+ def neat_numbers(number, roundto = 2) #round to 0 or 2)
75
+ if roundto == 2
76
+ number = sprintf "%.2f", number
77
+ else
78
+ number = number.round
79
+ end
80
+ #regex to add commas
81
+ number.to_s.reverse.gsub(%r{([0-9]{3}(?=([0-9])))}, "\\1,").reverse
82
+ end #end def neat_numbers
83
+
84
+ #helper to make it easier to do unit conversions on the fly
85
+ def unit_helper(number,from_unit_string,to_unit_string)
86
+ converted_number = OpenStudio::convert(OpenStudio::Quantity.new(number, OpenStudio::createUnit(from_unit_string).get), OpenStudio::createUnit(to_unit_string).get).get.value
87
+ end
88
+
89
+ #convert r_value and material_cost to si for future use
90
+ r_value_si = unit_helper(r_value, "ft^2*h*R/Btu","m^2*K/W")
91
+ material_cost_increase_si = unit_helper(material_cost_increase_ip,"1/ft^2","1/m^2")
92
+
93
+ #create an array of roofs and find range of starting construction R-value (not just insulation layer)
94
+ surfaces = model.getSurfaces
95
+ exterior_surfaces = []
96
+ exterior_surface_constructions = []
97
+ exterior_surface_construction_names = []
98
+ roof_resistance = []
99
+ surfaces.each do |surface|
100
+ if surface.outsideBoundaryCondition == "Outdoors" and surface.surfaceType == "RoofCeiling"
101
+ exterior_surfaces << surface
102
+ roof_const = surface.construction.get
103
+ #only add construction if it hasn't been added yet
104
+ if not exterior_surface_construction_names.include?(roof_const.name.to_s)
105
+ exterior_surface_constructions << roof_const.to_Construction.get
106
+ end
107
+ exterior_surface_construction_names << roof_const.name.to_s
108
+ roof_resistance << 1/roof_const.thermalConductance.to_f
109
+ end
110
+ end
111
+
112
+ # nothing will be done if there are no exterior surfaces
113
+ if exterior_surfaces.empty?
114
+ runner.registerAsNotApplicable("Model does not have any roofs.")
115
+ end
116
+
117
+ #report strings for initial condition
118
+ initial_string = []
119
+ exterior_surface_constructions.uniq.each do |exterior_surface_construction|
120
+ #unit conversion of roof insulation from SI units (M^2*K/W) to IP units (ft^2*h*R/Btu)
121
+ initial_conductance_ip = unit_helper(1/exterior_surface_construction.thermalConductance.to_f,"m^2*K/W", "ft^2*h*R/Btu")
122
+ initial_string << "#{exterior_surface_construction.name.to_s} (R-#{(sprintf "%.1f",initial_conductance_ip)})"
123
+ end
124
+ runner.registerInitialCondition("The building had #{initial_string.size} roof constructions: #{initial_string.sort.join(", ")}.")
125
+
126
+ #hashes to track constructions and materials made by the measure, to avoid duplicates
127
+ constructions_hash_old_new = {}
128
+ constructions_hash_new_old = {} #used to get netArea of new construction and then cost objects of construction it replaced
129
+ materials_hash = {}
130
+
131
+ #array and counter for new constructions that are made, used for reporting final condition
132
+ final_constructions_array = []
133
+
134
+ #loop through all constructions and materials used on roofs, edit and clone
135
+ exterior_surface_constructions.each do |exterior_surface_construction|
136
+ construction_layers = exterior_surface_construction.layers
137
+ max_thermal_resistance_material = ""
138
+ max_thermal_resistance_material_index = ""
139
+ counter = 0
140
+ thermal_resistance_values = []
141
+
142
+ #loop through construction layers and infer insulation layer/material
143
+ construction_layers.each do |construction_layer|
144
+ construction_layer_r_value = construction_layer.to_OpaqueMaterial.get.thermalResistance
145
+ if not thermal_resistance_values.empty?
146
+ if construction_layer_r_value > thermal_resistance_values.max
147
+ max_thermal_resistance_material = construction_layer
148
+ max_thermal_resistance_material_index = counter
149
+ end
150
+ end
151
+ thermal_resistance_values << construction_layer_r_value
152
+ counter = counter + 1
153
+ end
154
+
155
+ if not thermal_resistance_values.max > unit_helper(min_expected_r_value_ip, "ft^2*h*R/Btu","m^2*K/W")
156
+ runner.registerWarning("Construction '#{exterior_surface_construction.name.to_s}' does not appear to have an insulation layer and was not altered.")
157
+ elsif not thermal_resistance_values.max < r_value_si
158
+ runner.registerInfo("The insulation layer of construction #{exterior_surface_construction.name.to_s} exceeds the requested R-Value. It was not altered.")
159
+ else
160
+ #clone the construction
161
+ final_construction = exterior_surface_construction.clone(model)
162
+ final_construction = final_construction.to_Construction.get
163
+ final_construction.setName("#{exterior_surface_construction.name.to_s} adj roof insulation")
164
+ final_constructions_array << final_construction
165
+
166
+ #loop through lifecycle costs getting total costs under "Construction" or "Salvage" category and add to counter if occurs during year 0
167
+ const_LCCs = final_construction.lifeCycleCosts
168
+ cost_added = false
169
+ const_LCC_cat_const = false
170
+ updated_cost_si = 0
171
+ const_LCCs.each do |const_LCC|
172
+ if const_LCC.category == "Construction" and material_cost_increase_si != 0
173
+ const_LCC_cat_const = true #need this test to add proper lcc if it didn't exist to start with
174
+ # if multiple LCC objects associated with construction only adjust the cost of one of them.
175
+ if not cost_added
176
+ const_LCC.setCost(const_LCC.cost + material_cost_increase_si)
177
+ else
178
+ runner.registerInfo("More than one LifeCycleCost object with a category of Construction was associated with #{final_construction.name}. Cost was only adjusted for one of the LifeCycleCost objects.")
179
+ end
180
+ updated_cost_si += const_LCC.cost
181
+ end
182
+ end #end of const_LCCs.each
183
+
184
+ #add construction object if it didnt exist to start with and a cost increase was requested
185
+ if const_LCC_cat_const == false and material_cost_increase_si != 0
186
+ lcc_for_uncosted_const = OpenStudio::Model::LifeCycleCost.createLifeCycleCost("LCC_increase_insulation", final_construction, material_cost_increase_si, "CostPerArea", "Construction", 20, 0).get
187
+ runner.registerInfo("No material or installation costs existed for #{final_construction.name}. Created a new LifeCycleCost object with a material and installation cost of #{neat_numbers(unit_helper(lcc_for_uncosted_const.cost,"1/m^2","1/ft^2"))} ($/ft^2). Assumed capitol cost in first year, an expected life of 20 years, and no O & M costs.")
188
+ end
189
+
190
+ if cost_added
191
+ runner.registerInfo("Adjusting material and installation cost for #{final_construction.name} to #{neat_numbers(unit_helper(updated_cost_si,"1/m^2","1/ft^2"))} ($/ft^2).")
192
+ end
193
+
194
+ #add one time cost if requested
195
+ if one_time_retrofit_cost_ip > 0
196
+ one_time_retrofit_cost_si = unit_helper(one_time_retrofit_cost_ip,"1/ft^2","1/m^2")
197
+ lcc_retrofit_specific = OpenStudio::Model::LifeCycleCost.createLifeCycleCost("LCC_retrofit_specific", final_construction, one_time_retrofit_cost_si, "CostPerArea", "Construction", 0, years_until_retrofit_cost).get #using 0 for repeat period since one time cost.
198
+ runner.registerInfo("Adding one time cost of #{neat_numbers(unit_helper(lcc_retrofit_specific.cost,"1/m^2","1/ft^2"))} ($/ft^2) related to retrofit of roof insulation.")
199
+ end
200
+
201
+ #push to hashes
202
+ constructions_hash_old_new[exterior_surface_construction.name.to_s] = final_construction
203
+ constructions_hash_new_old[final_construction] = exterior_surface_construction #push the object to hash key vs. name
204
+
205
+ #find already cloned insulation material and link to construction
206
+ target_material = max_thermal_resistance_material
207
+ found_material = false
208
+ materials_hash.each do |orig,new|
209
+ if target_material.name.to_s == orig
210
+ new_material = new
211
+ materials_hash[max_thermal_resistance_material.name.to_s] = new_material
212
+ final_construction.eraseLayer(max_thermal_resistance_material_index)
213
+ final_construction.insertLayer(max_thermal_resistance_material_index,new_material)
214
+ found_material = true
215
+ end
216
+ end
217
+
218
+ #clone and edit insulation material and link to construction
219
+ if found_material == false
220
+ new_material = max_thermal_resistance_material.clone(model)
221
+ new_material = new_material.to_OpaqueMaterial.get
222
+ new_material.setName("#{max_thermal_resistance_material.name.to_s}_R-value #{r_value} (ft^2*h*R/Btu)")
223
+ materials_hash[max_thermal_resistance_material.name.to_s] = new_material
224
+ final_construction.eraseLayer(max_thermal_resistance_material_index)
225
+ final_construction.insertLayer(max_thermal_resistance_material_index,new_material)
226
+ runner.registerInfo("For construction'#{final_construction.name.to_s}', material'#{new_material.name.to_s}' was altered.")
227
+
228
+ #edit insulation material
229
+ new_material_matt = new_material.to_Material
230
+ if not new_material_matt.empty?
231
+ starting_thickness = new_material_matt.get.thickness
232
+ target_thickness = starting_thickness * r_value_si / thermal_resistance_values.max
233
+ final_thickness = new_material_matt.get.setThickness(target_thickness)
234
+ end
235
+ new_material_massless = new_material.to_MasslessOpaqueMaterial
236
+ if not new_material_massless.empty?
237
+ final_thermal_resistance = new_material_massless.get.setThermalResistance(r_value_si)
238
+ end
239
+ new_material_airgap = new_material.to_AirGap
240
+ if not new_material_airgap.empty?
241
+ final_thermal_resistance = new_material_airgap.get.setThermalResistance(r_value_si)
242
+ end
243
+ end #end of if found material is false
244
+ end #end of if not thermal_resistance_values.max >
245
+ end #end of loop through unique roof constructions
246
+
247
+ #loop through construction sets used in the model
248
+ default_construction_sets = model.getDefaultConstructionSets
249
+ default_construction_sets.each do |default_construction_set|
250
+ if default_construction_set.directUseCount > 0
251
+ default_surface_const_set = default_construction_set.defaultExteriorSurfaceConstructions
252
+ if not default_surface_const_set.empty?
253
+ starting_construction = default_surface_const_set.get.roofCeilingConstruction
254
+
255
+ #creating new default construction set
256
+ new_default_construction_set = default_construction_set.clone(model)
257
+ new_default_construction_set = new_default_construction_set.to_DefaultConstructionSet.get
258
+ new_default_construction_set.setName("#{default_construction_set.name.to_s} adj roof insulation")
259
+
260
+ #create new surface set and link to construction set
261
+ new_default_surface_const_set = default_surface_const_set.get.clone(model)
262
+ new_default_surface_const_set = new_default_surface_const_set.to_DefaultSurfaceConstructions.get
263
+ new_default_surface_const_set.setName("#{default_surface_const_set.get.name.to_s} adj roof insulation")
264
+ new_default_construction_set.setDefaultExteriorSurfaceConstructions(new_default_surface_const_set)
265
+
266
+ #use the hash to find the proper construction and link to new_default_surface_const_set
267
+ target_const = new_default_surface_const_set.roofCeilingConstruction
268
+ if not target_const.empty?
269
+ target_const = target_const.get.name.to_s
270
+ found_const_flag = false
271
+ constructions_hash_old_new.each do |orig,new|
272
+ if target_const == orig
273
+ final_construction = new
274
+ new_default_surface_const_set.setRoofCeilingConstruction(final_construction)
275
+ found_const_flag = true
276
+ end
277
+ end
278
+ if found_const_flag == false # this should never happen but is just an extra test in case something goes wrong with the measure code
279
+ runner.registerWarning("Measure couldn't find the construction named '#{target_const}' in the exterior surface hash.")
280
+ end
281
+ end
282
+
283
+ #swap all uses of the old construction set for the new
284
+ construction_set_sources = default_construction_set.sources
285
+ construction_set_sources.each do |construction_set_source|
286
+ building_source = construction_set_source.to_Building
287
+ # if statement for each type of object than can use a DefaultConstructionSet
288
+ if not building_source.empty?
289
+ building_source = building_source.get
290
+ building_source.setDefaultConstructionSet(new_default_construction_set)
291
+ end
292
+ building_story_source = construction_set_source.to_BuildingStory
293
+ if not building_story_source.empty?
294
+ building_story_source = building_story_source.get
295
+ building_story_source.setDefaultConstructionSet(new_default_construction_set)
296
+ end
297
+ space_type_source = construction_set_source.to_SpaceType
298
+ if not space_type_source.empty?
299
+ space_type_source = space_type_source.get
300
+ space_type_source.setDefaultConstructionSet(new_default_construction_set)
301
+ end
302
+ space_source = construction_set_source.to_Space
303
+ if not space_source.empty?
304
+ space_source = space_source.get
305
+ space_source.setDefaultConstructionSet(new_default_construction_set)
306
+ end
307
+ end #end of construction_set_sources.each do
308
+
309
+ end #end of if not default_surface_const_set.empty?
310
+ end #end of if default_construction_set.directUseCount > 0
311
+ end #end of loop through construction sets
312
+
313
+ #link cloned and edited constructions for surfaces with hard assigned constructions
314
+ exterior_surfaces.each do |exterior_surface|
315
+ if not exterior_surface.isConstructionDefaulted and not exterior_surface.construction.empty?
316
+
317
+ #use the hash to find the proper construction and link to surface
318
+ target_const = exterior_surface.construction
319
+ if not target_const.empty?
320
+ target_const = target_const.get.name.to_s
321
+ constructions_hash_old_new.each do |orig,new|
322
+ if target_const == orig
323
+ final_construction = new
324
+ exterior_surface.setConstruction(final_construction)
325
+ end
326
+ end
327
+ end
328
+
329
+ end #end of if not exterior_surface.isConstructionDefaulted and not exterior_surface.construction.empty?
330
+ end #end of exterior_surfaces.each do
331
+
332
+ #report strings for final condition
333
+ final_string = [] #not all exterior roof constructions, but only new ones made. If roof didn't have insulation and was not altered we don't want to show it
334
+ affected_area_si = 0
335
+ totalCost_of_affected_area = 0
336
+ yr0_capital_totalCosts = 0
337
+ final_constructions_array.each do |final_construction|
338
+
339
+ #unit conversion of roof insulation from SI units (M^2*K/W) to IP units (ft^2*h*R/Btu)
340
+ final_conductance_ip = unit_helper(1/final_construction.thermalConductance.to_f,"m^2*K/W", "ft^2*h*R/Btu")
341
+ final_string << "#{final_construction.name.to_s} (R-#{(sprintf "%.1f",final_conductance_ip)})"
342
+ affected_area_si = affected_area_si + final_construction.getNetArea
343
+
344
+ #loop through lifecycle costs getting total costs under "Construction" or "Salvage" category and add to counter if occurs during year 0
345
+ const_LCCs = final_construction.lifeCycleCosts
346
+ const_LCCs.each do |const_LCC|
347
+ if const_LCC.category == "Construction" or const_LCC.category == "Salvage"
348
+ if const_LCC.yearsFromStart == 0
349
+ yr0_capital_totalCosts += const_LCC.totalCost
350
+ end
351
+ end
352
+ end
353
+
354
+ end #end of final_constructions_array.each do
355
+
356
+ #add not applicable test if there were exterior roof constructions but non of them were altered (already enough insulation or doesn't look like insulated wall)
357
+ if affected_area_si == 0
358
+ runner.registerAsNotApplicable("No roofs were altered.")
359
+ affected_area_ip = affected_area_si
360
+ else
361
+ #ip construction area for reporting
362
+ affected_area_ip = unit_helper(affected_area_si,"m^2","ft^2")
363
+ end
364
+
365
+ #report final condition
366
+ runner.registerFinalCondition("The existing insulation for roofs was increased to R-#{r_value}. This was accomplished for an initial cost of #{one_time_retrofit_cost_ip} ($/sf) and an increase of #{material_cost_increase_ip} ($/sf) for construction. This was applied to #{neat_numbers(affected_area_ip,0)} (ft^2) across #{final_string.size} roof constructions: #{final_string.sort.join(", ")}.")
367
+
368
+ return true
369
+
370
+ end #end the run method
371
+
372
+ end #end the measure
373
+
374
+ #this allows the measure to be used by the application
375
+ IncreaseInsulationRValueForRoofs.new.registerWithApplication