openstudio-common-measures 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +6 -3
  4. data/Rakefile +17 -3
  5. data/lib/measures/AddDaylightSensors/LICENSE.md +27 -0
  6. data/lib/measures/AddDaylightSensors/README.md +136 -0
  7. data/lib/measures/AddDaylightSensors/README.md.erb +42 -0
  8. data/lib/measures/AddDaylightSensors/measure.rb +521 -0
  9. data/lib/measures/AddDaylightSensors/measure.xml +233 -0
  10. data/lib/measures/EnableDemandControlledVentilation/LICENSE.md +27 -0
  11. data/lib/measures/EnableDemandControlledVentilation/README.md +32 -0
  12. data/lib/measures/EnableDemandControlledVentilation/README.md.erb +42 -0
  13. data/lib/measures/EnableDemandControlledVentilation/measure.rb +154 -0
  14. data/lib/measures/EnableDemandControlledVentilation/measure.xml +99 -0
  15. data/lib/measures/EnableEconomizerControl/LICENSE.md +27 -0
  16. data/lib/measures/EnableEconomizerControl/README.md +48 -0
  17. data/lib/measures/EnableEconomizerControl/README.md.erb +42 -0
  18. data/lib/measures/EnableEconomizerControl/measure.rb +172 -0
  19. data/lib/measures/EnableEconomizerControl/measure.xml +124 -0
  20. data/lib/measures/IncreaseInsulationRValueForExteriorWalls/LICENSE.md +27 -0
  21. data/lib/measures/IncreaseInsulationRValueForExteriorWalls/README.md +64 -0
  22. data/lib/measures/IncreaseInsulationRValueForExteriorWalls/README.md.erb +42 -0
  23. data/lib/measures/IncreaseInsulationRValueForExteriorWalls/measure.rb +422 -0
  24. data/lib/measures/IncreaseInsulationRValueForExteriorWalls/measure.xml +150 -0
  25. data/lib/measures/IncreaseInsulationRValueForRoofs/LICENSE.md +27 -0
  26. data/lib/measures/IncreaseInsulationRValueForRoofs/README.md +64 -0
  27. data/lib/measures/IncreaseInsulationRValueForRoofs/README.md.erb +42 -0
  28. data/lib/measures/IncreaseInsulationRValueForRoofs/measure.rb +422 -0
  29. data/lib/measures/IncreaseInsulationRValueForRoofs/measure.xml +143 -0
  30. data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/LICENSE.md +27 -0
  31. data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/README.md +97 -0
  32. data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/README.md.erb +42 -0
  33. data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/measure.rb +450 -0
  34. data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/measure.xml +186 -0
  35. data/lib/measures/ReduceLightingLoadsByPercentage/LICENSE.md +27 -0
  36. data/lib/measures/ReduceLightingLoadsByPercentage/README.md +96 -0
  37. data/lib/measures/ReduceLightingLoadsByPercentage/README.md.erb +42 -0
  38. data/lib/measures/ReduceLightingLoadsByPercentage/measure.rb +513 -0
  39. data/lib/measures/ReduceLightingLoadsByPercentage/measure.xml +191 -0
  40. data/lib/measures/ReduceSpaceInfiltrationByPercentage/LICENSE.md +27 -0
  41. data/lib/measures/ReduceSpaceInfiltrationByPercentage/README.md +104 -0
  42. data/lib/measures/ReduceSpaceInfiltrationByPercentage/README.md.erb +42 -0
  43. data/lib/measures/ReduceSpaceInfiltrationByPercentage/measure.rb +349 -0
  44. data/lib/measures/ReduceSpaceInfiltrationByPercentage/measure.xml +181 -0
  45. data/lib/measures/ReduceVentilationByPercentage/LICENSE.md +27 -0
  46. data/lib/measures/ReduceVentilationByPercentage/README.md +40 -0
  47. data/lib/measures/ReduceVentilationByPercentage/README.md.erb +42 -0
  48. data/lib/measures/ReduceVentilationByPercentage/measure.rb +291 -0
  49. data/lib/measures/ReduceVentilationByPercentage/measure.xml +96 -0
  50. data/lib/measures/add_ems_to_control_ev_charging/{ReadMe.MD → README.md} +0 -0
  51. data/lib/measures/add_ev_load/{ReadMe.MD → README.md} +0 -0
  52. data/lib/measures/create_variable_speed_rtu/LICENSE.md +27 -0
  53. data/lib/measures/create_variable_speed_rtu/README.md +120 -0
  54. data/lib/measures/create_variable_speed_rtu/README.md.erb +42 -0
  55. data/lib/measures/create_variable_speed_rtu/measure.rb +539 -0
  56. data/lib/measures/create_variable_speed_rtu/measure.xml +207 -0
  57. data/lib/measures/generic_qaqc/measure.xml +14 -14
  58. data/lib/measures/generic_qaqc/resources/check_envelope_conductance.rb +7 -1
  59. data/lib/measures/generic_qaqc/resources/check_eui_by_end_use.rb +10 -11
  60. data/lib/measures/openstudio_results/README.md +5 -1
  61. data/lib/measures/openstudio_results/measure.rb +12 -8
  62. data/lib/measures/openstudio_results/measure.xml +56 -36
  63. data/lib/measures/openstudio_results/resources/os_lib_reporting.rb +115 -37
  64. data/lib/measures/openstudio_results/resources/os_lib_schedules.rb +27 -25
  65. data/lib/measures/set_exterior_walls_and_floors_to_adiabatic/README.md +16 -0
  66. data/lib/measures/set_exterior_walls_and_floors_to_adiabatic/measure.rb +32 -1
  67. data/lib/measures/set_exterior_walls_and_floors_to_adiabatic/measure.xml +31 -12
  68. data/lib/measures/view_data/measure.xml +8 -8
  69. data/lib/measures/view_data/resources/va3c.rb +50 -46
  70. data/lib/measures/view_model/resources/va3c.rb +50 -46
  71. data/lib/openstudio/common_measures/version.rb +1 -1
  72. data/openstudio-common-measures.gemspec +2 -2
  73. metadata +59 -9
@@ -0,0 +1,27 @@
1
+ OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are permitted
4
+ provided that the following conditions are met:
5
+
6
+ (1) Redistributions of source code must retain the above copyright notice, this list of conditions
7
+ and the following disclaimer.
8
+
9
+ (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions
10
+ and the following disclaimer in the documentation and/or other materials provided with the distribution.
11
+
12
+ (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse
13
+ or promote products derived from this software without specific prior written permission from the
14
+ respective party.
15
+
16
+ (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other
17
+ derivative works may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar
18
+ designation without specific prior written permission from Alliance for Sustainable Energy, LLC.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
21
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, THE UNITED STATES GOVERNMENT,
23
+ OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
25
+ OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,64 @@
1
+
2
+
3
+ ###### (Automatically generated documentation)
4
+
5
+ # Increase R-value of Insulation for Exterior Walls to a Specific Value
6
+
7
+ ## Description
8
+
9
+
10
+ ## Modeler Description
11
+
12
+
13
+ ## Measure Type
14
+ ModelMeasure
15
+
16
+ ## Taxonomy
17
+
18
+
19
+ ## Arguments
20
+
21
+
22
+ ### Insulation R-value (ft^2*h*R/Btu).
23
+
24
+ **Name:** r_value,
25
+ **Type:** Double,
26
+ **Units:** ,
27
+ **Required:** true,
28
+ **Model Dependent:** false
29
+
30
+ ### Allow both increase and decrease in R-value to reach requested target?
31
+
32
+ **Name:** allow_reduction,
33
+ **Type:** Boolean,
34
+ **Units:** ,
35
+ **Required:** true,
36
+ **Model Dependent:** false
37
+
38
+ ### Increase in Material and Installation Costs for Construction per Area Used ($/ft^2).
39
+
40
+ **Name:** material_cost_increase_ip,
41
+ **Type:** Double,
42
+ **Units:** ,
43
+ **Required:** true,
44
+ **Model Dependent:** false
45
+
46
+ ### One Time Retrofit Cost to Add Insulation to Construction ($/ft^2).
47
+
48
+ **Name:** one_time_retrofit_cost_ip,
49
+ **Type:** Double,
50
+ **Units:** ,
51
+ **Required:** true,
52
+ **Model Dependent:** false
53
+
54
+ ### Year to Incur One Time Retrofit Cost (whole years).
55
+
56
+ **Name:** years_until_retrofit_cost,
57
+ **Type:** Integer,
58
+ **Units:** ,
59
+ **Required:** true,
60
+ **Model Dependent:** false
61
+
62
+
63
+
64
+
@@ -0,0 +1,42 @@
1
+ <%#= README.md.erb is used to auto-generate README.md. %>
2
+ <%#= To manually maintain README.md throw away README.md.erb and manually edit README.md %>
3
+ ###### (Automatically generated documentation)
4
+
5
+ # <%= name %>
6
+
7
+ ## Description
8
+ <%= description %>
9
+
10
+ ## Modeler Description
11
+ <%= modelerDescription %>
12
+
13
+ ## Measure Type
14
+ <%= measureType %>
15
+
16
+ ## Taxonomy
17
+ <%= taxonomy %>
18
+
19
+ ## Arguments
20
+
21
+ <% arguments.each do |argument| %>
22
+ ### <%= argument[:display_name] %>
23
+ <%= argument[:description] %>
24
+ **Name:** <%= argument[:name] %>,
25
+ **Type:** <%= argument[:type] %>,
26
+ **Units:** <%= argument[:units] %>,
27
+ **Required:** <%= argument[:required] %>,
28
+ **Model Dependent:** <%= argument[:model_dependent] %>
29
+ <% end %>
30
+
31
+ <% if arguments.size == 0 %>
32
+ <%= "This measure does not have any user arguments" %>
33
+ <% end %>
34
+
35
+ <% if outputs.size > 0 %>
36
+ ## Outputs
37
+ <% output_names = [] %>
38
+ <% outputs.each do |output| %>
39
+ <% output_names << output[:display_name] %>
40
+ <% end %>
41
+ <%= output_names.join(", ") %>
42
+ <% end %>
@@ -0,0 +1,422 @@
1
+ # *******************************************************************************
2
+ # OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
3
+ # All rights reserved.
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # (1) Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # (3) Neither the name of the copyright holder nor the names of any contributors
15
+ # may be used to endorse or promote products derived from this software without
16
+ # specific prior written permission from the respective party.
17
+ #
18
+ # (4) Other than as required in clauses (1) and (2), distributions in any form
19
+ # of modifications or other derivative works may not use the "OpenStudio"
20
+ # trademark, "OS", "os", or any other confusingly similar designation without
21
+ # specific prior written permission from Alliance for Sustainable Energy, LLC.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
24
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
27
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
28
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
30
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+ # *******************************************************************************
35
+
36
+ # start the measure
37
+ class IncreaseInsulationRValueForExteriorWalls < OpenStudio::Measure::ModelMeasure
38
+ # define the name that a user will see
39
+ def name
40
+ return 'Increase R-value of Insulation for Exterior Walls to a Specific Value'
41
+ end
42
+
43
+ # define the arguments that the user will input
44
+ def arguments(model)
45
+ args = OpenStudio::Measure::OSArgumentVector.new
46
+
47
+ # make an argument insulation R-value
48
+ r_value = OpenStudio::Measure::OSArgument.makeDoubleArgument('r_value', true)
49
+ r_value.setDisplayName('Insulation R-value (ft^2*h*R/Btu).')
50
+ r_value.setDefaultValue(13.0)
51
+ args << r_value
52
+
53
+ # make bool argument to allow both increase and decrease in R value
54
+ allow_reduction = OpenStudio::Measure::OSArgument.makeBoolArgument('allow_reduction', true)
55
+ allow_reduction.setDisplayName('Allow both increase and decrease in R-value to reach requested target?')
56
+ allow_reduction.setDefaultValue(false)
57
+ args << allow_reduction
58
+
59
+ # make an argument for material and installation cost
60
+ material_cost_increase_ip = OpenStudio::Measure::OSArgument.makeDoubleArgument('material_cost_increase_ip', true)
61
+ material_cost_increase_ip.setDisplayName('Increase in Material and Installation Costs for Construction per Area Used ($/ft^2).')
62
+ material_cost_increase_ip.setDefaultValue(0.0)
63
+ args << material_cost_increase_ip
64
+
65
+ # make an argument for demolition cost
66
+ one_time_retrofit_cost_ip = OpenStudio::Measure::OSArgument.makeDoubleArgument('one_time_retrofit_cost_ip', true)
67
+ one_time_retrofit_cost_ip.setDisplayName('One Time Retrofit Cost to Add Insulation to Construction ($/ft^2).')
68
+ one_time_retrofit_cost_ip.setDefaultValue(0.0)
69
+ args << one_time_retrofit_cost_ip
70
+
71
+ # make an argument for duration in years until costs start
72
+ years_until_retrofit_cost = OpenStudio::Measure::OSArgument.makeIntegerArgument('years_until_retrofit_cost', true)
73
+ years_until_retrofit_cost.setDisplayName('Year to Incur One Time Retrofit Cost (whole years).')
74
+ years_until_retrofit_cost.setDefaultValue(0)
75
+ args << years_until_retrofit_cost
76
+
77
+ return args
78
+ end
79
+
80
+ # define what happens when the measure is run
81
+ def run(model, runner, user_arguments)
82
+ super(model, runner, user_arguments)
83
+
84
+ # use the built-in error checking
85
+ if !runner.validateUserArguments(arguments(model), user_arguments)
86
+ return false
87
+ end
88
+
89
+ # assign the user inputs to variables
90
+ r_value = runner.getDoubleArgumentValue('r_value', user_arguments)
91
+ allow_reduction = runner.getBoolArgumentValue('allow_reduction', user_arguments)
92
+ material_cost_increase_ip = runner.getDoubleArgumentValue('material_cost_increase_ip', user_arguments)
93
+ one_time_retrofit_cost_ip = runner.getDoubleArgumentValue('one_time_retrofit_cost_ip', user_arguments)
94
+ years_until_retrofit_cost = runner.getIntegerArgumentValue('years_until_retrofit_cost', user_arguments)
95
+
96
+ # set limit for minimum insulation. This is used to limit input and for inferring insulation layer in construction.
97
+ min_expected_r_value_ip = 1 # ip units
98
+
99
+ # check the R-value for reasonableness
100
+ if (r_value < 0) || (r_value > 500)
101
+ runner.registerError("The requested wall insulation R-value of #{r_value} ft^2*h*R/Btu was above the measure limit.")
102
+ return false
103
+ elsif r_value > 40
104
+ runner.registerWarning("The requested wall insulation R-value of #{r_value} ft^2*h*R/Btu is abnormally high.")
105
+ elsif r_value < min_expected_r_value_ip
106
+ runner.registerWarning("The requested wall insulation R-value of #{r_value} ft^2*h*R/Btu is abnormally low.")
107
+ end
108
+
109
+ # check lifecycle arguments for reasonableness
110
+ if (years_until_retrofit_cost < 0) && (years_until_retrofit_cost > 100)
111
+ runner.registerError('Year to incur one time retrofit cost should be a non-negative integer less than or equal to 100.')
112
+ end
113
+
114
+ # 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
115
+ def neat_numbers(number, roundto = 2) # round to 0 or 2)
116
+ if roundto == 2
117
+ number = format '%.2f', number
118
+ else
119
+ number = number.round
120
+ end
121
+ # regex to add commas
122
+ number.to_s.reverse.gsub(/([0-9]{3}(?=([0-9])))/, '\\1,').reverse
123
+ end
124
+
125
+ # helper to make it easier to do unit conversions on the fly
126
+ def unit_helper(number, from_unit_string, to_unit_string)
127
+ converted_number = OpenStudio.convert(OpenStudio::Quantity.new(number, OpenStudio.createUnit(from_unit_string).get), OpenStudio.createUnit(to_unit_string).get).get.value
128
+ end
129
+
130
+ # convert r_value and material_cost to si for future use
131
+ r_value_si = unit_helper(r_value, 'ft^2*h*R/Btu', 'm^2*K/W')
132
+ material_cost_increase_si = unit_helper(material_cost_increase_ip, '1/ft^2', '1/m^2')
133
+
134
+ # create an array of exterior walls and find range of starting construction R-value (not just insulation layer)
135
+ surfaces = model.getSurfaces
136
+ exterior_surfaces = []
137
+ exterior_surface_constructions = []
138
+ exterior_surface_construction_names = []
139
+ ext_wall_resistance = []
140
+ surfaces.each do |surface|
141
+ if (surface.outsideBoundaryCondition == 'Outdoors') && (surface.surfaceType == 'Wall')
142
+ exterior_surfaces << surface
143
+ ext_wall_const = surface.construction.get
144
+ # only add construction if it hasn't been added yet
145
+ if !exterior_surface_construction_names.include?(ext_wall_const.name.to_s)
146
+ exterior_surface_constructions << ext_wall_const.to_Construction.get
147
+ end
148
+ exterior_surface_construction_names << ext_wall_const.name.to_s
149
+ ext_wall_resistance << 1 / ext_wall_const.thermalConductance.to_f
150
+ end
151
+ end
152
+
153
+ # nothing will be done if there are no exterior surfaces
154
+ if exterior_surfaces.empty?
155
+ runner.registerAsNotApplicable('Model does not have any exterior walls.')
156
+ return true
157
+ end
158
+
159
+ # report strings for initial condition
160
+ initial_string = []
161
+ exterior_surface_constructions.uniq.each do |exterior_surface_construction|
162
+ # unit conversion of wall insulation from SI units (M^2*K/W) to IP units (ft^2*h*R/Btu)
163
+ initial_conductance_ip = unit_helper(1 / exterior_surface_construction.thermalConductance.to_f, 'm^2*K/W', 'ft^2*h*R/Btu')
164
+ initial_string << "#{exterior_surface_construction.name} (R-#{(format '%.1f', initial_conductance_ip)})"
165
+ end
166
+ runner.registerInitialCondition("The building had #{initial_string.size} exterior wall constructions: #{initial_string.sort.join(', ')}.")
167
+
168
+ # hashes to track constructions and materials made by the measure, to avoid duplicates
169
+ constructions_hash_old_new = {}
170
+ constructions_hash_new_old = {} # used to get netArea of new construction and then cost objects of construction it replaced
171
+ materials_hash = {}
172
+
173
+ # array and counter for new constructions that are made, used for reporting final condition
174
+ final_constructions_array = []
175
+
176
+ # loop through all constructions and materials used on exterior walls, edit and clone
177
+ exterior_surface_constructions.each do |exterior_surface_construction|
178
+ construction_layers = exterior_surface_construction.layers
179
+ max_thermal_resistance_material = ''
180
+ max_thermal_resistance_material_index = ''
181
+ materials_in_construction = construction_layers.map.with_index do |layer, i|
182
+ { 'name' => layer.name.to_s,
183
+ 'index' => i,
184
+ 'nomass' => !layer.to_MasslessOpaqueMaterial.empty?,
185
+ 'r_value' => layer.to_OpaqueMaterial.get.thermalResistance,
186
+ 'mat' => layer }
187
+ end
188
+
189
+ no_mass_materials = materials_in_construction.select { |mat| mat['nomass'] == true }
190
+ # measure will select the no mass material with the highest r-value as the insulation layer
191
+ # if no mass materials are present, the measure will select the material with the highest r-value per inch
192
+ if !no_mass_materials.empty?
193
+ thermal_resistance_values = no_mass_materials.map { |mat| mat['r_value'] }
194
+ max_mat_hash = no_mass_materials.select { |mat| mat['r_value'] >= thermal_resistance_values.max }
195
+ else
196
+ thermal_resistance_per_thickness_values = materials_in_construction.map { |mat| mat['r_value'] / mat['mat'].thickness }
197
+ target_index = thermal_resistance_per_thickness_values.index(thermal_resistance_per_thickness_values.max)
198
+ max_mat_hash = materials_in_construction.select { |mat| mat['index'] == target_index }
199
+ thermal_resistance_values = materials_in_construction.map { |mat| mat['r_value'] }
200
+ end
201
+ max_thermal_resistance_material = max_mat_hash[0]['mat']
202
+ max_thermal_resistance_material_index = max_mat_hash[0]['index']
203
+ max_thermal_resistance = max_thermal_resistance_material.to_OpaqueMaterial.get.thermalResistance
204
+
205
+ if max_thermal_resistance <= unit_helper(min_expected_r_value_ip, 'ft^2*h*R/Btu', 'm^2*K/W')
206
+ runner.registerWarning("Construction '#{exterior_surface_construction.name}' does not appear to have an insulation layer and was not altered.")
207
+ elsif (max_thermal_resistance >= r_value_si) && !allow_reduction
208
+ runner.registerInfo("The insulation layer of construction #{exterior_surface_construction.name} exceeds the requested R-Value. It was not altered.")
209
+ else
210
+ # clone the construction
211
+ final_construction = exterior_surface_construction.clone(model)
212
+ final_construction = final_construction.to_Construction.get
213
+ final_construction.setName("#{exterior_surface_construction.name} adj ext wall insulation")
214
+ final_constructions_array << final_construction
215
+
216
+ # loop through lifecycle costs getting total costs under "Construction" or "Salvage" category and add to counter if occurs during year 0
217
+ const_LCCs = final_construction.lifeCycleCosts
218
+ cost_added = false
219
+ const_LCC_cat_const = false
220
+ updated_cost_si = 0
221
+ const_LCCs.each do |const_LCC|
222
+ if (const_LCC.category == 'Construction') && (material_cost_increase_si != 0)
223
+ const_LCC_cat_const = true # need this test to add proper lcc if it didn't exist to start with
224
+ # if multiple LCC objects associated with construction only adjust the cost of one of them.
225
+ if !cost_added
226
+ const_LCC.setCost(const_LCC.cost + material_cost_increase_si)
227
+ else
228
+ 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.")
229
+ end
230
+ updated_cost_si += const_LCC.cost
231
+ end
232
+ end
233
+
234
+ if cost_added
235
+ 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).")
236
+ end
237
+
238
+ # add construction object if it didnt exist to start with and a cost increase was requested
239
+ if (const_LCC_cat_const == false) && (material_cost_increase_si != 0)
240
+ lcc_for_uncosted_const = OpenStudio::Model::LifeCycleCost.createLifeCycleCost('LCC_increase_insulation', final_construction, material_cost_increase_si, 'CostPerArea', 'Construction', 20, 0).get
241
+ 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.")
242
+ end
243
+
244
+ # add one time cost if requested
245
+ if one_time_retrofit_cost_ip > 0
246
+ one_time_retrofit_cost_si = unit_helper(one_time_retrofit_cost_ip, '1/ft^2', '1/m^2')
247
+ 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.
248
+ 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 wall insulation.")
249
+ end
250
+
251
+ # push to hashes
252
+ constructions_hash_old_new[exterior_surface_construction.name.to_s] = final_construction
253
+ constructions_hash_new_old[final_construction] = exterior_surface_construction # push the object to hash key vs. name
254
+
255
+ # find already cloned insulation material and link to construction
256
+ target_material = max_thermal_resistance_material
257
+ found_material = false
258
+ materials_hash.each do |orig, new|
259
+ if target_material.name.to_s == orig
260
+ new_material = new
261
+ materials_hash[max_thermal_resistance_material.name.to_s] = new_material
262
+ final_construction.eraseLayer(max_thermal_resistance_material_index)
263
+ final_construction.insertLayer(max_thermal_resistance_material_index, new_material)
264
+ found_material = true
265
+ end
266
+ end
267
+
268
+ # clone and edit insulation material and link to construction
269
+ if found_material == false
270
+ new_material = max_thermal_resistance_material.clone(model)
271
+ new_material = new_material.to_OpaqueMaterial.get
272
+ new_material.setName("#{max_thermal_resistance_material.name}_R-value #{r_value} (ft^2*h*R/Btu)")
273
+ materials_hash[max_thermal_resistance_material.name.to_s] = new_material
274
+ final_construction.eraseLayer(max_thermal_resistance_material_index)
275
+ final_construction.insertLayer(max_thermal_resistance_material_index, new_material)
276
+ runner.registerInfo("For construction'#{final_construction.name}', material'#{new_material.name}' was altered.")
277
+
278
+ # edit insulation material
279
+ new_material_matt = new_material.to_Material
280
+ if !new_material_matt.empty?
281
+ starting_thickness = new_material_matt.get.thickness
282
+ target_thickness = starting_thickness * r_value_si / thermal_resistance_values.max
283
+ final_thickness = new_material_matt.get.setThickness(target_thickness)
284
+ end
285
+ new_material_massless = new_material.to_MasslessOpaqueMaterial
286
+ if !new_material_massless.empty?
287
+ final_thermal_resistance = new_material_massless.get.setThermalResistance(r_value_si)
288
+ end
289
+ new_material_airgap = new_material.to_AirGap
290
+ if !new_material_airgap.empty?
291
+ final_thermal_resistance = new_material_airgap.get.setThermalResistance(r_value_si)
292
+ end
293
+ end
294
+ end
295
+ end
296
+
297
+ # loop through construction sets used in the model
298
+ default_construction_sets = model.getDefaultConstructionSets
299
+ default_construction_sets.each do |default_construction_set|
300
+ if default_construction_set.directUseCount > 0
301
+ default_surface_const_set = default_construction_set.defaultExteriorSurfaceConstructions
302
+ if !default_surface_const_set.empty?
303
+ starting_construction = default_surface_const_set.get.wallConstruction
304
+
305
+ # creating new default construction set
306
+ new_default_construction_set = default_construction_set.clone(model)
307
+ new_default_construction_set = new_default_construction_set.to_DefaultConstructionSet.get
308
+ new_default_construction_set.setName("#{default_construction_set.name} adj ext wall insulation")
309
+
310
+ # create new surface set and link to construction set
311
+ new_default_surface_const_set = default_surface_const_set.get.clone(model)
312
+ new_default_surface_const_set = new_default_surface_const_set.to_DefaultSurfaceConstructions.get
313
+ new_default_surface_const_set.setName("#{default_surface_const_set.get.name} adj ext wall insulation")
314
+ new_default_construction_set.setDefaultExteriorSurfaceConstructions(new_default_surface_const_set)
315
+
316
+ # use the hash to find the proper construction and link to new_default_surface_const_set
317
+ target_const = new_default_surface_const_set.wallConstruction
318
+ if !target_const.empty?
319
+ target_const = target_const.get.name.to_s
320
+ found_const_flag = false
321
+ constructions_hash_old_new.each do |orig, new|
322
+ if target_const == orig
323
+ final_construction = new
324
+ new_default_surface_const_set.setWallConstruction(final_construction)
325
+ found_const_flag = true
326
+ end
327
+ end
328
+ if found_const_flag == false # this should never happen but is just an extra test in case something goes wrong with the measure code
329
+ runner.registerWarning("Measure couldn't find the construction named '#{target_const}' in the exterior surface hash.")
330
+ end
331
+ end
332
+
333
+ # swap all uses of the old construction set for the new
334
+ construction_set_sources = default_construction_set.sources
335
+ construction_set_sources.each do |construction_set_source|
336
+ building_source = construction_set_source.to_Building
337
+ # if statement for each type of object than can use a DefaultConstructionSet
338
+ if !building_source.empty?
339
+ building_source = building_source.get
340
+ building_source.setDefaultConstructionSet(new_default_construction_set)
341
+ end
342
+ building_story_source = construction_set_source.to_BuildingStory
343
+ if !building_story_source.empty?
344
+ building_story_source = building_story_source.get
345
+ building_story_source.setDefaultConstructionSet(new_default_construction_set)
346
+ end
347
+ space_type_source = construction_set_source.to_SpaceType
348
+ if !space_type_source.empty?
349
+ space_type_source = space_type_source.get
350
+ space_type_source.setDefaultConstructionSet(new_default_construction_set)
351
+ end
352
+ space_source = construction_set_source.to_Space
353
+ if !space_source.empty?
354
+ space_source = space_source.get
355
+ space_source.setDefaultConstructionSet(new_default_construction_set)
356
+ end
357
+ end
358
+
359
+ end
360
+ end
361
+ end
362
+
363
+ # link cloned and edited constructions for surfaces with hard assigned constructions
364
+ exterior_surfaces.each do |exterior_surface|
365
+ if !exterior_surface.isConstructionDefaulted && !exterior_surface.construction.empty?
366
+
367
+ # use the hash to find the proper construction and link to surface
368
+ target_const = exterior_surface.construction
369
+ if !target_const.empty?
370
+ target_const = target_const.get.name.to_s
371
+ constructions_hash_old_new.each do |orig, new|
372
+ if target_const == orig
373
+ final_construction = new
374
+ exterior_surface.setConstruction(final_construction)
375
+ end
376
+ end
377
+ end
378
+
379
+ end
380
+ end
381
+
382
+ # report strings for final condition
383
+ final_string = [] # not all exterior wall constructions, but only new ones made. If wall didn't have insulation and was not altered we don't want to show it
384
+ affected_area_si = 0
385
+ totalCost_of_affected_area = 0
386
+ yr0_capital_totalCosts = 0
387
+ final_constructions_array.each do |final_construction|
388
+ # unit conversion of wall insulation from SI units (M^2*K/W) to IP units (ft^2*h*R/Btu)
389
+ final_conductance_ip = unit_helper(1 / final_construction.thermalConductance.to_f, 'm^2*K/W', 'ft^2*h*R/Btu')
390
+ final_string << "#{final_construction.name} (R-#{(format '%.1f', final_conductance_ip)})"
391
+ affected_area_si += final_construction.getNetArea
392
+
393
+ # loop through lifecycle costs getting total costs under "Construction" or "Salvage" category and add to counter if occurs during year 0
394
+ const_LCCs = final_construction.lifeCycleCosts
395
+ const_LCCs.each do |const_LCC|
396
+ if (const_LCC.category == 'Construction') || (const_LCC.category == 'Salvage')
397
+ if const_LCC.yearsFromStart == 0
398
+ yr0_capital_totalCosts += const_LCC.totalCost
399
+ end
400
+ end
401
+ end
402
+ end
403
+
404
+ # 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)
405
+ if affected_area_si == 0
406
+ runner.registerAsNotApplicable('No exterior walls were altered.')
407
+ return true
408
+ # affected_area_ip = affected_area_si
409
+ else
410
+ # ip construction area for reporting
411
+ affected_area_ip = unit_helper(affected_area_si, 'm^2', 'ft^2')
412
+ end
413
+
414
+ # report final condition
415
+ runner.registerFinalCondition("The existing insulation for exterior walls was set 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} exterior wall constructions: #{final_string.sort.join(', ')}.")
416
+
417
+ return true
418
+ end
419
+ end
420
+
421
+ # this allows the measure to be used by the application
422
+ IncreaseInsulationRValueForExteriorWalls.new.registerWithApplication