urbanopt-reporting 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,147 @@
1
+ <?xml version="1.0"?>
2
+ <measure>
3
+ <schema_version>3.0</schema_version>
4
+ <name>export_time_series_loads_csv</name>
5
+ <uid>9fcf6116-c2eb-43d6-93f0-e1bdd822f768</uid>
6
+ <version_id>3a539fd4-3978-4d0f-bd25-a54905fc7306</version_id>
7
+ <version_modified>20201212T061249Z</version_modified>
8
+ <xml_checksum>9BF1E6AC</xml_checksum>
9
+ <class_name>ExportTimeSeriesLoadsCSV</class_name>
10
+ <display_name>ExportTimeSeriesLoadsCSV</display_name>
11
+ <description>This measure will add the required output variables and create a CSV file with plant loop level mass flow rates and temperatures for use in a Modelica simulation. Note that this measure has certain
12
+ requirements for naming of hydronic loops (discussed in the modeler description section).</description>
13
+ <modeler_description>This measure is currently configured to report the temperatures and mass flow rates at the demand outlet and inlet nodes of hot water and chilled water loops, after adding the required output variables to the model. These values can be used to calculate the sum of the demand-side loads, and could thus represent the load on a connection to a district thermal energy system, or on
14
+ building-level primary equipment. This measure assumes that the model includes hydronic HVAC loops, and that the hot water and chilled water loop names can each be uniquely identified by a user-provided string. This measure also assumes that there is a single heating hot water loop
15
+ and a single chilled-water loop per building.</modeler_description>
16
+ <arguments>
17
+ <argument>
18
+ <name>hhw_loop_name</name>
19
+ <display_name>Name or Partial Name of Heating Hot Water Loop, non-case-sensitive</display_name>
20
+ <type>String</type>
21
+ <required>true</required>
22
+ <model_dependent>false</model_dependent>
23
+ <default_value>hot</default_value>
24
+ </argument>
25
+ <argument>
26
+ <name>chw_loop_name</name>
27
+ <display_name>Name or Partial Name of Chilled Water Loop, non-case-sensitive</display_name>
28
+ <type>String</type>
29
+ <required>true</required>
30
+ <model_dependent>false</model_dependent>
31
+ <default_value>chilled</default_value>
32
+ </argument>
33
+ <argument>
34
+ <name>dec_places_mass_flow</name>
35
+ <display_name>Number of Decimal Places to Round Mass Flow Rate</display_name>
36
+ <description>Number of decimal places to which mass flow rate will be rounded</description>
37
+ <type>Integer</type>
38
+ <required>true</required>
39
+ <model_dependent>false</model_dependent>
40
+ <default_value>3</default_value>
41
+ </argument>
42
+ <argument>
43
+ <name>dec_places_temp</name>
44
+ <display_name>Number of Decimal Places to Round Temperature</display_name>
45
+ <description>Number of decimal places to which temperature will be rounded</description>
46
+ <type>Integer</type>
47
+ <required>true</required>
48
+ <model_dependent>false</model_dependent>
49
+ <default_value>1</default_value>
50
+ </argument>
51
+ </arguments>
52
+ <outputs />
53
+ <provenances />
54
+ <tags>
55
+ <tag>Reporting.QAQC</tag>
56
+ </tags>
57
+ <attributes>
58
+ <attribute>
59
+ <name>Measure Type</name>
60
+ <value>ReportingMeasure</value>
61
+ <datatype>string</datatype>
62
+ </attribute>
63
+ <attribute>
64
+ <name>Intended Software Tool</name>
65
+ <value>OpenStudio Application</value>
66
+ <datatype>string</datatype>
67
+ </attribute>
68
+ <attribute>
69
+ <name>Intended Software Tool</name>
70
+ <value>Parametric Analysis Tool</value>
71
+ <datatype>string</datatype>
72
+ </attribute>
73
+ </attributes>
74
+ <files>
75
+ <file>
76
+ <filename>LICENSE.md</filename>
77
+ <filetype>md</filetype>
78
+ <usage_type>license</usage_type>
79
+ <checksum>CD7F5672</checksum>
80
+ </file>
81
+ <file>
82
+ <filename>README.md.erb</filename>
83
+ <filetype>erb</filetype>
84
+ <usage_type>readmeerb</usage_type>
85
+ <checksum>703C9964</checksum>
86
+ </file>
87
+ <file>
88
+ <filename>.gitkeep</filename>
89
+ <filetype>gitkeep</filetype>
90
+ <usage_type>doc</usage_type>
91
+ <checksum>00000000</checksum>
92
+ </file>
93
+ <file>
94
+ <filename>USA_CO_Golden-NREL.724666_TMY3.epw</filename>
95
+ <filetype>epw</filetype>
96
+ <usage_type>test</usage_type>
97
+ <checksum>BDF687C1</checksum>
98
+ </file>
99
+ <file>
100
+ <filename>report.html.in</filename>
101
+ <filetype>in</filetype>
102
+ <usage_type>resource</usage_type>
103
+ <checksum>3F69E3FB</checksum>
104
+ </file>
105
+ <file>
106
+ <filename>example_model.osm</filename>
107
+ <filetype>osm</filetype>
108
+ <usage_type>test</usage_type>
109
+ <checksum>15AD3659</checksum>
110
+ </file>
111
+ <file>
112
+ <filename>os_lib_helper_methods.rb</filename>
113
+ <filetype>rb</filetype>
114
+ <usage_type>resource</usage_type>
115
+ <checksum>5C26809A</checksum>
116
+ </file>
117
+ <file>
118
+ <filename>export_time_series_modelica_test.rb</filename>
119
+ <filetype>rb</filetype>
120
+ <usage_type>test</usage_type>
121
+ <checksum>B595A09A</checksum>
122
+ </file>
123
+ <file>
124
+ <filename>building_loads.csv</filename>
125
+ <filetype>csv</filetype>
126
+ <usage_type>test</usage_type>
127
+ <checksum>1E8091B8</checksum>
128
+ </file>
129
+ <file>
130
+ <version>
131
+ <software_program>OpenStudio</software_program>
132
+ <identifier>3.0.1</identifier>
133
+ <min_compatible>3.0.1</min_compatible>
134
+ </version>
135
+ <filename>measure.rb</filename>
136
+ <filetype>rb</filetype>
137
+ <usage_type>script</usage_type>
138
+ <checksum>C172F942</checksum>
139
+ </file>
140
+ <file>
141
+ <filename>README.md</filename>
142
+ <filetype>md</filetype>
143
+ <usage_type>readme</usage_type>
144
+ <checksum>08F470AC</checksum>
145
+ </file>
146
+ </files>
147
+ </measure>
@@ -0,0 +1,399 @@
1
+ # *******************************************************************************
2
+ # OpenStudio(R), Copyright (c) 2008-2019, 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
+ module OsLib_HelperMethods
37
+ # populate choice argument from model objects
38
+ def self.populateChoiceArgFromModelObjects(model, modelObject_args_hash, includeBuilding = nil)
39
+ # populate choice argument for constructions that are applied to surfaces in the model
40
+ modelObject_handles = OpenStudio::StringVector.new
41
+ modelObject_display_names = OpenStudio::StringVector.new
42
+
43
+ # looping through sorted hash of constructions
44
+ modelObject_args_hash.sort.map do |key, value|
45
+ modelObject_handles << value.handle.to_s
46
+ modelObject_display_names << key
47
+ end
48
+
49
+ unless includeBuilding.nil?
50
+ # add building to string vector with space type
51
+ building = model.getBuilding
52
+ modelObject_handles << building.handle.to_s
53
+ modelObject_display_names << includeBuilding
54
+ end
55
+
56
+ result = { 'modelObject_handles' => modelObject_handles, 'modelObject_display_names' => modelObject_display_names }
57
+ return result
58
+ end
59
+
60
+ # create variables in run from user arguments
61
+ def self.createRunVariables(runner, model, user_arguments, arguments)
62
+ result = {}
63
+
64
+ error = false
65
+ # use the built-in error checking
66
+ unless runner.validateUserArguments(arguments, user_arguments)
67
+ error = true
68
+ runner.registerError('Invalid argument values.')
69
+ end
70
+
71
+ user_arguments.each do |argument|
72
+ # get argument info
73
+ arg = user_arguments[argument]
74
+ arg_type = arg.print.lines($/)[1]
75
+
76
+ # create argument variable
77
+ if arg_type.include? 'Double, Required'
78
+ eval("result[\"#{arg.name}\"] = runner.getDoubleArgumentValue(\"#{arg.name}\", user_arguments)")
79
+ elsif arg_type.include? 'Integer, Required'
80
+ eval("result[\"#{arg.name}\"] = runner.getIntegerArgumentValue(\"#{arg.name}\", user_arguments)")
81
+ elsif arg_type.include? 'String, Required'
82
+ eval("result[\"#{arg.name}\"] = runner.getStringArgumentValue(\"#{arg.name}\", user_arguments)")
83
+ elsif arg_type.include? 'Boolean, Required'
84
+ eval("result[\"#{arg.name}\"] = runner.getBoolArgumentValue(\"#{arg.name}\", user_arguments)")
85
+ elsif arg_type.include? 'Choice, Required'
86
+ eval("result[\"#{arg.name}\"] = runner.getStringArgumentValue(\"#{arg.name}\", user_arguments)")
87
+ else
88
+ puts 'not setup to handle all argument types yet, or any optional arguments'
89
+ end
90
+ end
91
+
92
+ if error
93
+ return false
94
+ else
95
+ return result
96
+ end
97
+ end
98
+
99
+ # check choice argument made from model objects
100
+ def self.checkChoiceArgFromModelObjects(object, variableName, to_ObjectType, runner, user_arguments)
101
+ apply_to_building = false
102
+ modelObject = nil
103
+ if object.empty?
104
+ handle = runner.getStringArgumentValue(variableName, user_arguments)
105
+ if handle.empty?
106
+ runner.registerError("No #{variableName} was chosen.") # this logic makes this not work on an optional model object argument
107
+ else
108
+ runner.registerError("The selected #{variableName} with handle '#{handle}' was not found in the model. It may have been removed by another measure.")
109
+ end
110
+ return false
111
+ else
112
+ if !eval("object.get.#{to_ObjectType}").empty?
113
+ modelObject = eval("object.get.#{to_ObjectType}").get
114
+ elsif !object.get.to_Building.empty?
115
+ apply_to_building = true
116
+ else
117
+ runner.registerError("Script Error - argument not showing up as #{variableName}.")
118
+ return false
119
+ end
120
+ end
121
+
122
+ result = { 'modelObject' => modelObject, 'apply_to_building' => apply_to_building }
123
+ end
124
+
125
+ # check choice argument made from model objects
126
+ def self.checkOptionalChoiceArgFromModelObjects(object, variableName, to_ObjectType, runner, user_arguments)
127
+ apply_to_building = false
128
+ modelObject = nil
129
+ if object.empty?
130
+ handle = runner.getOptionalStringArgumentValue(variableName, user_arguments)
131
+ if handle.empty?
132
+ # do nothing, this is a valid option
133
+ puts 'hello'
134
+ modelObject = nil
135
+ apply_to_building = false
136
+ else
137
+ runner.registerError("The selected #{variableName} with handle '#{handle}' was not found in the model. It may have been removed by another measure.")
138
+ return false
139
+ end
140
+ else
141
+ if !eval("object.get.#{to_ObjectType}").empty?
142
+ modelObject = eval("object.get.#{to_ObjectType}").get
143
+ elsif !object.get.to_Building.empty?
144
+ apply_to_building = true
145
+ else
146
+ runner.registerError("Script Error - argument not showing up as #{variableName}.")
147
+ return false
148
+ end
149
+ end
150
+
151
+ result = { 'modelObject' => modelObject, 'apply_to_building' => apply_to_building }
152
+ end
153
+
154
+ # check value of double arguments
155
+ def self.checkDoubleAndIntegerArguments(runner, user_arguments, arg_check_hash)
156
+ error = false
157
+
158
+ # get hash values
159
+ min = arg_check_hash['min']
160
+ max = arg_check_hash['max']
161
+ min_eq_bool = arg_check_hash['min_eq_bool']
162
+ max_eq_bool = arg_check_hash['max_eq_bool']
163
+
164
+ arg_check_hash['arg_array'].each do |argument|
165
+ argument = user_arguments[argument]
166
+
167
+ # get arg values
168
+ arg_value = nil
169
+ if argument.hasValue
170
+ arg_value = argument.valueDisplayName.to_f # instead of valueAsDouble so it allows integer arguments as well
171
+ elsif argument.hasDefaultValue
172
+ arg_value = argument.defaultValueDisplayName.to_f
173
+ end
174
+ arg_display = argument.displayName
175
+
176
+ unless min.nil?
177
+ if min_eq_bool
178
+ if arg_value < min
179
+ runner.registerError("Please enter value greater than or equal to #{min} for #{arg_display}.") # add in argument display name
180
+ error = true
181
+ end
182
+ else
183
+ if arg_value <= min
184
+ runner.registerError("Please enter value greater than #{min} for #{arg_display}.") # add in argument display name
185
+ error = true
186
+ end
187
+ end
188
+ end
189
+ unless max.nil?
190
+ if max_eq_bool
191
+ if arg_value > max
192
+ runner.registerError("Please enter value less than or equal to #{max} for #{arg_display}.") # add in argument display name
193
+ error = true
194
+ end
195
+ else
196
+ if arg_value >= max
197
+ runner.registerError("Please enter value less than #{max} for #{arg_display}.") # add in argument display name
198
+ error = true
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ # check for any errors
205
+ if error
206
+ return false
207
+ else
208
+ return true
209
+ end
210
+ end
211
+
212
+ # open channel to log info/warning/error messages
213
+ def self.setup_log_msgs(runner, debug = false)
214
+ # Open a channel to log info/warning/error messages
215
+ @msg_log = OpenStudio::StringStreamLogSink.new
216
+ if debug
217
+ @msg_log.setLogLevel(OpenStudio::Debug)
218
+ else
219
+ @msg_log.setLogLevel(OpenStudio::Info)
220
+ end
221
+ @start_time = Time.new
222
+ @runner = runner
223
+ end
224
+
225
+ # Get all the log messages and put into output
226
+ # for users to see.
227
+ def self.log_msgs
228
+ @msg_log.logMessages.each do |msg|
229
+ # DLM: you can filter on log channel here for now
230
+ if /openstudio.*/.match(msg.logChannel) # /openstudio\.model\..*/
231
+ # Skip certain messages that are irrelevant/misleading
232
+ next if msg.logMessage.include?('Skipping layer') || # Annoying/bogus "Skipping layer" warnings
233
+ msg.logChannel.include?('runmanager') || # RunManager messages
234
+ msg.logChannel.include?('setFileExtension') || # .ddy extension unexpected
235
+ msg.logChannel.include?('Translator') || # Forward translator and geometry translator
236
+ msg.logMessage.include?('UseWeatherFile') # 'UseWeatherFile' is not yet a supported option for YearDescription
237
+
238
+ # Report the message in the correct way
239
+ if msg.logLevel == OpenStudio::Info
240
+ @runner.registerInfo(msg.logMessage)
241
+ elsif msg.logLevel == OpenStudio::Warn
242
+ @runner.registerWarning("[#{msg.logChannel}] #{msg.logMessage}")
243
+ elsif msg.logLevel == OpenStudio::Error
244
+ @runner.registerError("[#{msg.logChannel}] #{msg.logMessage}")
245
+ elsif msg.logLevel == OpenStudio::Debug && @debug
246
+ @runner.registerInfo("DEBUG - #{msg.logMessage}")
247
+ end
248
+ end
249
+ end
250
+ @runner.registerInfo("Total Time = #{(Time.new - @start_time).round}sec.")
251
+ end
252
+
253
+ def self.check_upstream_measure_for_arg(runner, arg_name)
254
+ # 2.x methods (currently setup for measure display name but snake_case arg names)
255
+ arg_name_value = {}
256
+ runner.workflow.workflowSteps.each do |step|
257
+ if step.to_MeasureStep.is_initialized
258
+ measure_step = step.to_MeasureStep.get
259
+
260
+ measure_name = measure_step.measureDirName
261
+ if measure_step.name.is_initialized
262
+ measure_name = measure_step.name.get # this is instance name in PAT
263
+ end
264
+ if measure_step.result.is_initialized
265
+ result = measure_step.result.get
266
+ result.stepValues.each do |arg|
267
+ name = arg.name
268
+ value = arg.valueAsVariant.to_s
269
+ if name == arg_name
270
+ arg_name_value[:value] = value
271
+ arg_name_value[:measure_name] = measure_name
272
+ return arg_name_value # stop after find first one
273
+ end
274
+ end
275
+ else
276
+ # puts "No result for #{measure_name}"
277
+ end
278
+ else
279
+ # puts "This step is not a measure"
280
+ end
281
+ end
282
+
283
+ return arg_name_value
284
+ end
285
+
286
+ # populate choice argument from model objects. areaType should be string like "floorArea" or "exteriorArea"
287
+ # note: it seems like spaceType.floorArea does account for multiplier, so I don't have to call this method unless I have a custom collection of spaces.
288
+ def self.getAreaOfSpacesInArray(model, spaceArray, areaType = 'floorArea')
289
+ # find selected floor spaces, make array and get floor area.
290
+ totalArea = 0
291
+ spaceAreaHash = {}
292
+ spaceArray.each do |space|
293
+ spaceArea = eval("space.#{areaType}*space.multiplier")
294
+ spaceAreaHash[space] = spaceArea
295
+ totalArea += spaceArea
296
+ end
297
+
298
+ result = { 'totalArea' => totalArea, 'spaceAreaHash' => spaceAreaHash }
299
+ return result
300
+ end
301
+
302
+ # runs conversion and neat string, and returns value with units in string, optionally before or after the value
303
+ def self.neatConvertWithUnitDisplay(double, fromString, toString, digits, unitBefore = false, unitAfter = true, space = true, parentheses = true)
304
+ # convert units
305
+ doubleConverted = OpenStudio.convert(double, fromString, toString)
306
+ if !doubleConverted.nil?
307
+ doubleConverted = doubleConverted.get
308
+ else
309
+ puts "Couldn't convert values, check string choices passed in. From: #{fromString}, To: #{toString}"
310
+ end
311
+
312
+ # get neat version of converted
313
+ neatConverted = OpenStudio.toNeatString(doubleConverted, digits, true)
314
+
315
+ # add prefix
316
+ if unitBefore
317
+ if space == true && parentheses == true
318
+ prefix = "(#{toString}) "
319
+ elsif space == true && parentheses == false
320
+ prefix = "(#{toString})"
321
+ elsif space == false && parentheses == true
322
+ prefix = "#{toString} "
323
+ else
324
+ prefix = toString.to_s
325
+ end
326
+ else
327
+ prefix = ''
328
+ end
329
+
330
+ # add suffix
331
+ if unitAfter
332
+ if space == true && parentheses == true
333
+ suffix = " (#{toString})"
334
+ elsif space == true && parentheses == false
335
+ suffix = "(#{toString})"
336
+ elsif space == false && parentheses == true
337
+ suffix = " #{toString}"
338
+ else
339
+ suffix = toString.to_s
340
+ end
341
+ else
342
+ suffix = ''
343
+ end
344
+
345
+ finalString = "#{prefix}#{neatConverted}#{suffix}"
346
+
347
+ return finalString
348
+ end
349
+
350
+ # helper that loops through lifecycle costs getting total costs under "Construction" and add to counter if occurs during year 0
351
+ def self.getTotalCostForObjects(objectArray, category = 'Construction', onlyYearFromStartZero = true)
352
+ counter = 0
353
+ objectArray.each do |object|
354
+ object_LCCs = object.lifeCycleCosts
355
+ object_LCCs.each do |object_LCC|
356
+ if object_LCC.category == category
357
+ if onlyYearFromStartZero == false || object_LCC.yearsFromStart == 0
358
+ counter += object_LCC.totalCost
359
+ end
360
+ end
361
+ end
362
+ end
363
+
364
+ return counter
365
+ end
366
+
367
+ # helper that loops through lifecycle costs getting total costs under "Construction" and add to counter if occurs during year 0
368
+ def self.getSpaceTypeStandardsInformation(spaceTypeArray)
369
+ # hash of space types
370
+ spaceTypeStandardsInfoHash = {}
371
+
372
+ spaceTypeArray.each do |spaceType|
373
+ # get standards building
374
+ if !spaceType.standardsBuildingType.empty?
375
+ standardsBuilding = spaceType.standardsBuildingType.get
376
+ else
377
+ standardsBuilding = nil
378
+ end
379
+
380
+ # get standards space type
381
+ if !spaceType.standardsSpaceType.empty?
382
+ standardsSpaceType = spaceType.standardsSpaceType.get
383
+ else
384
+ standardsSpaceType = nil
385
+ end
386
+
387
+ # populate hash
388
+ spaceTypeStandardsInfoHash[spaceType] = [standardsBuilding, standardsSpaceType]
389
+ end
390
+
391
+ return spaceTypeStandardsInfoHash
392
+ end
393
+
394
+ # OpenStudio has built in toNeatString method
395
+ # OpenStudio::toNeatString(double,2,true)# double,decimals, show commas
396
+
397
+ # OpenStudio has built in helper for unit conversion. That can be done using OpenStudio::convert() as shown below.
398
+ # OpenStudio::convert(double,"from unit string","to unit string").get
399
+ end