Class: ZoneReport

Inherits:
OpenStudio::Measure::ReportingMeasure
  • Object
show all
Defined in:
lib/measures/ZoneReport/measure.rb

Overview

start the measure

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#zone_collectionObject (readonly)

Accessor to support unit tests



681
682
683
# File 'lib/measures/ZoneReport/measure.rb', line 681

def zone_collection
  @zone_collection
end

Instance Method Details

#arguments(model = nil) ⇒ Object

define the arguments that the user will input



19
20
21
22
# File 'lib/measures/ZoneReport/measure.rb', line 19

def arguments(model = nil)
  args = OpenStudio::Measure::OSArgumentVector.new
  return args
end

#convert_prop(property, final_units) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/measures/ZoneReport/measure.rb', line 58

def convert_prop(property, final_units)
  return ['-', '-'] unless property

  return [property[0].to_f.round(2), ''] if final_units.empty?
  return [(property[0].to_f * 100).round(2), '%'] if final_units == '%'
  return [property[0].to_f.round(2), 'COP'] if final_units == 'COP'

  initial_units = eplus_to_openstudio(property[1])
  converted = OpenStudio.convert(property[0].to_f, initial_units, final_units)
  if converted.empty?
    "Could not convert from #{initial_units} to #{final_units}"
  else
    final_units = final_units.gsub('inH_{2}O', 'in. w.c.')
    [converted.get.round(2), final_units]
  end
end

#energyPlusOutputRequests(runner, user_arguments) ⇒ Object

return a vector of IdfObject’s to request EnergyPlus objects needed by the run method



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/measures/ZoneReport/measure.rb', line 25

def energyPlusOutputRequests(runner, user_arguments)
  super(runner, user_arguments)

  result = OpenStudio::IdfObjectVector.new

  # use the built-in error checking
  if !runner.validateUserArguments(arguments, user_arguments)
    return result
  end

  request = OpenStudio::IdfObject.load('Output:Table:SummaryReports,AllSummaryAndSizingPeriod;').get
  result << request

  return result
end

#eplus_to_openstudio(unitstr) ⇒ Object

Convert a value from the view TabularDataWithStrings from the unit represented there to the given unit. We make a bunch of special cases for unit aliases used by EnergyPlus which are not in the default list of OpenStudio conversions We also special case a few conversions for unitless ‘units’ so that our final display makes more sense to the user. When the property is not present (for instance, cooled beams don’t have properties in the sql file under 1.5) this method will return “-” as a placeholder.



54
55
56
# File 'lib/measures/ZoneReport/measure.rb', line 54

def eplus_to_openstudio(unitstr)
  unitstr.gsub('m3', 'm^3').gsub('pa', 'Pa').gsub('m2', 'm^2').gsub(' per ', '/')
end

#getDataByColumn(colName) ⇒ Object



664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
# File 'lib/measures/ZoneReport/measure.rb', line 664

def getDataByColumn(colName)
  strvec_HVACEquipment_query = "SELECT #{colName} FROM tabulardatawithstrings WHERE "
  strvec_HVACEquipment_query << "ReportName='EquipmentSummary' and "
  strvec_HVACEquipment_query << "ReportForString='Entire Facility'"
  strvec_HVACEquipment_query << 'ORDER BY TableName, ColumnName, RowName, Units, Value'

  query_results = @sqlFile.execAndReturnVectorOfString(strvec_HVACEquipment_query).get

  if query_results.empty?
    @runner.registerError("Could not get data for requested Column #{colName}.")
    return []
  else
    return query_results
  end
end

#getDetailsData(report, forstring, table, row, column, units, final_units) ⇒ Object

Fetch a value from the tabulardatawithstrings database view If final_units is “s” the value is returned unchanged as a string Otherwise the value is converted from units to final_units - units is specified in energy plus style (m2, m3, etc) and final_units should be open studio style (m^2, m^3, …) If the data is not found or cannot be converted a warning is registered and “” or 0.0 is returned.



623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
# File 'lib/measures/ZoneReport/measure.rb', line 623

def getDetailsData(report, forstring, table, row, column, units, final_units)
  if report == 'ZoneComponentLoadSummary'
    forstring.upcase!
  end

  str_HVACEquipment_query = 'SELECT Value FROM tabulardatawithstrings WHERE '
  str_HVACEquipment_query << "ReportName='#{report}' AND "
  str_HVACEquipment_query << "ReportForString='#{forstring}' AND "
  str_HVACEquipment_query << "TableName='#{table}' AND "
  str_HVACEquipment_query << "RowName LIKE '#{row}' AND "
  str_HVACEquipment_query << "ColumnName='#{column}' AND "
  str_HVACEquipment_query << "Units='#{units}'"

  query_results = @sqlFile.execAndReturnFirstString(str_HVACEquipment_query)

  if query_results.empty?

    @runner.registerWarning("Could not get data for #{report} #{forstring} #{table} #{row} #{column}.")
    return final_units == 's' ? '' : 0.0

  else
    r = query_results.get
    if report == 'ZoneComponentLoadSummary'
      @testData["#{@currentZoneName}_#{table}_#{row}"] = r
    end

    if final_units == 's'
      return r
    else
      converted = OpenStudio.convert(r.to_f, eplus_to_openstudio(units), final_units)
      if converted.empty?
        @runner.registerError("Could not convert #{r} from #{units} to #{final_units}")
        return 0.0
      else
        return converted.get.round(2)
      end
    end

  end
end

#getPctLoad(val, total) ⇒ Object



584
585
586
587
588
589
590
# File 'lib/measures/ZoneReport/measure.rb', line 584

def getPctLoad(val, total)
  if val != '' && total != 0
    return (val * 100 / total).round(2)
  else
    return 0
  end
end

#getResourceFileData(fileName) ⇒ Object



592
593
594
595
596
597
598
599
600
601
602
603
604
# File 'lib/measures/ZoneReport/measure.rb', line 592

def getResourceFileData(fileName)
  data_in_path = "#{File.dirname(__FILE__)}/resources/#{fileName}"
  if !File.exist?(data_in_path)
    data_in_path = "#{File.dirname(__FILE__)}/#{fileName}"
  end

  html_in = ''
  File.open(data_in_path, 'r') do |file|
    html_in = file.read
  end

  html_in
end

#nameObject

define the name that a user will see, this method may be deprecated as the display name in PAT comes from the name field in measure.xml



14
15
16
# File 'lib/measures/ZoneReport/measure.rb', line 14

def name
  return 'Zone Report'
end

#niceClassName(class_name) ⇒ Object

Method to make class names nicer (remove OS and _)



42
43
44
45
46
# File 'lib/measures/ZoneReport/measure.rb', line 42

def niceClassName(class_name)
  class_name = class_name.gsub('OS_', '')
  class_name = class_name.tr('_', ' ')
  return class_name
end

#properties_for_cooling_coil(e, props, parent) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/measures/ZoneReport/measure.rb', line 75

def properties_for_cooling_coil(e, props, parent)
  autosized = case e.iddObjectType
                when OpenStudio::Model::CoilCoolingDXSingleSpeed.iddObjectType
                  coil = e.to_CoilCoolingDXSingleSpeed.get
                  coil.isRatedTotalCoolingCapacityAutosized && coil.isRatedSensibleHeatRatioAutosized && coil.isRatedAirFlowRateAutosized ? 'Yes' : 'No'
                when OpenStudio::Model::CoilCoolingWater.iddObjectType
                  coil = e.to_CoilCoolingWater.get
                  coil.isDesignWaterFlowRateAutosized && coil.isDesignAirFlowRateAutosized && coil.isDesignInletWaterTemperatureAutosized ? 'Yes' : 'No'
                when OpenStudio::Model::CoilCoolingWaterToAirHeatPumpEquationFit.iddObjectType
                  coil = e.to_CoilCoolingWaterToAirHeatPumpEquationFit.get
                  coil.isRatedTotalCoolingCapacityAutosized && coil.isRatedSensibleHeatRatioAutosized && coil.isRatedAirFlowRateAutosized ? 'Yes' : 'No'
                else
                  '?'
              end

  coolingcap = convert_prop(props['Nominal Total Capacity'], 'kBtu/hr')
  coolingeff = convert_prop(props['Nominal Efficiency'], 'COP')
  sensibleheatratio = convert_prop(props['Nominal Sensible Heat Ratio'], '')
  parent_name = parent.name.get
  name = e.name.get
  coiltype = niceClassName(e.iddObjectType.valueName)

  { 'Terminal/Zone Equip Name' => parent_name, 'Coil Type' => coiltype, 'Name' => name, 'Autosized' => autosized, 'Nominal Capacity' => coolingcap, 'Nominal Efficiency' => coolingeff, 'Nominal SHR' => sensibleheatratio }
end

#properties_for_fan(equipment, properties, parent) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/measures/ZoneReport/measure.rb', line 147

def properties_for_fan(equipment, properties, parent)
  autosized = case equipment.iddObjectType
                when OpenStudio::Model::FanConstantVolume.iddObjectType
                  fan = equipment.to_FanConstantVolume.get
                  fan.isMaximumFlowRateAutosized ? 'Yes' : 'No'
                when OpenStudio::Model::FanVariableVolume.iddObjectType
                  fan = equipment.to_FanVariableVolume.get
                  fan.isMaximumFlowRateAutosized ? 'Yes' : 'No'
                when OpenStudio::Model::FanOnOff.iddObjectType
                  fan = equipment.to_FanOnOff.get
                  fan.isMaximumFlowRateAutosized ? 'Yes' : 'No'
                when OpenStudio::Model::FanZoneExhaust.iddObjectType
                  'N/A'
                else
                  '?'
              end

  name = equipment.name.get
  parent_name = parent.name.get
  maxflowrate = convert_prop(properties['Max Air Flow Rate'], 'cfm')
  fanpressure = convert_prop(properties['Delta Pressure'], 'inH_{2}O')
  totalefficiency = convert_prop(properties['Total Efficiency'], '%')
  ratedfanpower = convert_prop(properties['Rated Electric Power'], 'W')
  { 'Terminal/Zone Equip Name' => parent_name, 'Fan Type' => niceClassName(equipment.iddObjectType.valueName), 'Name' => name, 'Autosized' => autosized, 'Max Flow' => maxflowrate, 'Efficiency' => totalefficiency, 'Pressure' => fanpressure, 'Power' => ratedfanpower }
end

#properties_for_heating_coil(e, props, parent) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/measures/ZoneReport/measure.rb', line 100

def properties_for_heating_coil(e, props, parent)
  autosized = '-'
  eff_units = 'COP'
  heatingcap = ['-', '-']
  heatingeff = ['-', '-']
  case e.iddObjectType
    when OpenStudio::Model::CoilHeatingElectric.iddObjectType
      coil = e.to_CoilHeatingElectric.get
      autosized = coil.isNominalCapacityAutosized ? 'Yes' : 'No'
      heatingcap = convert_prop(props['Nominal Total Capacity'], 'kW')
      heatingeff = convert_prop(props['Nominal Efficiency'], '%')
    when OpenStudio::Model::CoilHeatingGas.iddObjectType
      coil = e.to_CoilHeatingGas.get
      autosized = coil.isNominalCapacityAutosized ? 'Yes' : 'No'
      heatingcap = convert_prop(props['Nominal Total Capacity'], 'kBtu/hr')
      heatingeff = convert_prop(props['Nominal Efficiency'], 'COP')
    when OpenStudio::Model::CoilHeatingDXSingleSpeed.iddObjectType
      coil = e.to_CoilHeatingDXSingleSpeed.get
      autosized = coil.isRatedTotalHeatingCapacityAutosized && coil.isRatedAirFlowRateAutosized ? 'Yes' : 'No'
      heatingcap = convert_prop(props['Nominal Total Capacity'], 'kBtu/hr')
      heatingeff = convert_prop(props['Nominal Efficiency'], 'COP')
    when OpenStudio::Model::CoilHeatingWater.iddObjectType
      coil = e.to_CoilHeatingWater.get
      autosized = coil.isMaximumWaterFlowRateAutosized && coil.isRatedCapacityAutosized && coil.isUFactorTimesAreaValueAutosized ? 'Yes' : 'No'
      heatingcap = convert_prop(props['Nominal Total Capacity'], 'kBtu/hr')
      heatingeff = ['-', '-']
    when OpenStudio::Model::CoilHeatingWaterBaseboard.iddObjectType
      coil = e.to_CoilHeatingWaterBaseboard.get
      autosized = coil.isMaximumWaterFlowRateAutosized && coil.isUFactorTimesAreaValueAutosized ? 'Yes' : 'No'
      heatingcap = convert_prop(props['Nominal Total Capacity'], 'kBtu/hr')
      heatingeff = ['-', '-']
    when OpenStudio::Model::CoilHeatingWaterToAirHeatPumpEquationFit.iddObjectType
      coil = e.to_CoilHeatingWaterToAirHeatPumpEquationFit.get
      autosized = coil.isRatedHeatingCapacityAutosized && coil.isRatedAirFlowRateAutosized && coil.isRatedWaterFlowRateAutosized ? 'Yes' : 'No'
      heatingcap = convert_prop(props['Nominal Total Capacity'], 'kBtu/hr')
      heatingeff = convert_prop(props['Nominal Efficiency'], 'COP')
    else
      autosized = '?'
  end

  parent_name = parent.name.get
  name = e.name.get
  coiltype = niceClassName(e.iddObjectType.valueName)

  { 'Terminal/Zone Equip Name' => parent_name, 'Coil Type' => coiltype, 'Name' => name, 'Autosized' => autosized, 'Nominal Capacity' => heatingcap, 'Nominal Efficiency' => heatingeff }
end

#properties_for_zone_equipment(equipment, equipment_properties, parent) ⇒ Object

Each equipment is grouped into Heating, Cooling or Fans and has properties extracted appropriate for that group. This method returns a tuple [equipment group, properties] where properties is a hash of name, value or name, [value, units] pairs



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/measures/ZoneReport/measure.rb', line 175

def properties_for_zone_equipment(equipment, equipment_properties, parent)
  # Make properties an empty hash if it was nil
  equipment_properties ||= {}
  if /^OS_Coil_Cooling/.match?(equipment.iddObjectType.valueName)
    return 'Cooling', properties_for_cooling_coil(equipment, equipment_properties, parent)
  elsif /^OS_Coil_Heating/.match?(equipment.iddObjectType.valueName)
    return 'Heating', properties_for_heating_coil(equipment, equipment_properties, parent)
  elsif /^OS_Fan/.match?(equipment.iddObjectType.valueName)
    return 'Fans', properties_for_fan(equipment, equipment_properties, parent)
  elsif OpenStudio::Model::ZoneHVACBaseboardConvectiveElectric.iddObjectType == equipment.iddObjectType
    baseboard = equipment.to_ZoneHVACBaseboardConvectiveElectric.get
    autosized = baseboard.isNominalCapacityAutosized ? 'Yes' : 'No'
    heatingcap = convert_prop(equipment_properties['Design Size Nominal Capacity'], 'kW')
    name = baseboard.name.get
    coiltype = niceClassName(baseboard.iddObjectType.valueName)
    return 'Heating', { 'Terminal/Zone Equip Name' => name, 'Coil Type' => coiltype, 'Name' => name, 'Autosized' => autosized, 'Nominal Capacity' => heatingcap, 'Nominal Efficiency' => [100, '%'] }
  else
    return nil, nil
  end
end

#run(runner, user_arguments) ⇒ Object

define what happens when the measure is run



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
# File 'lib/measures/ZoneReport/measure.rb', line 197

def run(runner, user_arguments)
  super(runner, user_arguments)

  # use the built-in error checking
  if !runner.validateUserArguments(arguments, user_arguments)
    return false
  end

  @runner = runner

  # get the last model and sql file

  model = runner.lastOpenStudioModel
  if model.empty?
    runner.registerError('Cannot find last model.')
    return false
  end
  model = model.get

  @sqlFile = runner.lastEnergyPlusSqlFile
  if @sqlFile.empty?
    runner.registerError('Cannot find last sql file.')
    return false
  end
  @sqlFile = @sqlFile.get
  model.setSqlFile(@sqlFile)

  @graph_data = []

  # Collect and collate information about the hvac equipment
  # When we are done we will have a hash of equipment keyed by equipment name
  # who's values are hashes keyed by the property name.  The values of the
  # property hashes are [value, unit] pairs
  # Some types of equipment (for instance CoilCoolingCooledBeams) are not present
  # in the database as of OS 1.5

  # find table names, etc. in the SupportZoneHVACEquipFields
  tableNames = getDataByColumn('TableName')
  columnNames = getDataByColumn('ColumnName')
  rowNames = getDataByColumn('RowName')
  units = getDataByColumn('Units')
  values = getDataByColumn('Value')
  equipment_rows = tableNames.zip(columnNames, rowNames, units, values)
  equipment = {}
  equipment_rows.each do |r|
    _, field, name, units, value = r
    if name != 'None'
      eh = equipment[name] || {}
      eh[field] = [value, units]
      equipment[name] = eh
    end
  end

  @zone_collection = []

  @testData = {}

  # Go through each zone in the model and collect all the zone equipment
  model.getThermalZones.sort.each do |thermalZone|
    # Skip unconditioned zones
    if thermalZone.thermostatSetpointDualSetpoint.empty?
      @runner.registerInfo("Skipping #{thermalZone.name} because it is unconditioned.")
      next
    end

    # Skip plenums
    if thermalZone.isPlenum == true
      @runner.registerInfo("Skipping #{thermalZone.name} because it is a plenum.")
      next
    end

    zone_equipment = {}

    thermalZone.equipment.each do |e|
      childquipment = e.to_ParentObject.get.children
      # Look for data on the top level equipment and each of that equipment's children
      found_primary_heat = false
      ([e] + childquipment).each do |ce|
        ename = ce.name.get.upcase
        reporting_type, equipment_properties = properties_for_zone_equipment(ce, equipment[ename], e)
        if equipment_properties
          # The first heating coil in a zone equipment chain will be primary heating; all
          # following heating coils will be marked as Backup Heating.
          if reporting_type == 'Heating'
            reporting_type = 'Backup Heating' if found_primary_heat
            found_primary_heat = true
          end
          # Find the priority for heating or cooling equipment - the priority is based on the 'parent' zone equipment priority
          if reporting_type == 'Heating'
            equipment_properties['Priority'] = (thermalZone.equipmentInHeatingOrder.index { |ze| ze.name.get == e.name.get && ze.iddObjectType == e.iddObjectType }) + 1
          end
          if reporting_type == 'Cooling'
            equipment_properties['Priority'] = (thermalZone.equipmentInCoolingOrder.index { |ze| ze.name.get == e.name.get && ze.iddObjectType == e.iddObjectType }) + 1
          end
          # If we don't yet have equipment of this type, make a new array.  Then add
          # our equipment information to the array and update our zone equipment hash.
          equip_of_type = zone_equipment[reporting_type] || []
          equip_of_type << equipment_properties
          zone_equipment[reporting_type] = equip_of_type
        end
      end
    end

    zoneMetrics = {}
    zoneMetrics[:name] = !thermalZone.name.empty? ? thermalZone.name.get : ''
    zoneMetrics[:equipment] = zone_equipment
    zoneMetrics[:area] = OpenStudio.convert(thermalZone.floorArea, 'm^2', 'ft^2').get.round(2)

    @currentZoneName = zoneMetrics[:name]

    vals = {}

    vals[:va] = getDetailsData('LightingSummary', 'Entire Facility', 'Interior Lighting', "#{zoneMetrics[:name]}%", 'Lighting Power Density', 'W/m2', 'W/ft^2').round(2)
    vals[:vb] = getDetailsData('LightingSummary', 'Entire Facility', 'Interior Lighting', "#{zoneMetrics[:name]}%", 'Full Load Hours/Week', 'hr', 'hr').round(2)

    vals[:vc] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Electricity', "InteriorLights:Electricity:Zone:#{zoneMetrics[:name]}", 'Electricity Annual Value', 'GJ', 'kWh').round(2)

    vals[:vd] = getDetailsData('InputVerificationandResultsSummary', 'Entire Facility', 'Zone Summary', zoneMetrics[:name], 'Plug and Process', 'W/m2', 'W/ft^2').round(2)

    vals[:ve] = getDetailsData('InputVerificationandResultsSummary', 'Entire Facility', 'Zone Summary', zoneMetrics[:name], 'Area', 'm2', 'ft^2').round(2)

    vals[:vf] = getDetailsData('InputVerificationandResultsSummary', 'Entire Facility', 'Zone Summary', zoneMetrics[:name], 'Conditioned (Y/N)', '', 's')
    if vals[:vf] == '' then vals[:vf] = 'No' end

    vals[:vg] = getDetailsData('HVACSizingSummary', 'Entire Facility', 'Zone Heating', zoneMetrics[:name], 'User Design Load', 'W', 'kBtu/hr').round(2)
    vals[:vh] = getDetailsData('HVACSizingSummary', 'Entire Facility', 'Zone Heating', zoneMetrics[:name], 'User Design Air Flow', 'm3/s', 'ft^3/s').round(2)
    vals[:vi] = getDetailsData('HVACSizingSummary', 'Entire Facility', 'Zone Cooling', zoneMetrics[:name], 'User Design Load', 'W', 'kBtu/hr').round(2)
    vals[:vj] = getDetailsData('HVACSizingSummary', 'Entire Facility', 'Zone Cooling', zoneMetrics[:name], 'User Design Air Flow', 'm3/s', 'ft^3/s').round(2)

    vals[:vk] = getDetailsData('OutdoorAirSummary', 'Entire Facility', 'Average Outdoor Air During Occupied Hours', zoneMetrics[:name], 'Mechanical Ventilation', 'ach', 'ach').round(2)
    vals[:vl] = getDetailsData('OutdoorAirSummary', 'Entire Facility', 'Average Outdoor Air During Occupied Hours', zoneMetrics[:name], 'Infiltration', 'ach', 'ach').round(2)

    vals[:vm] = getDetailsData('InputVerificationandResultsSummary', 'Entire Facility', 'Zone Summary', zoneMetrics[:name], 'People', 'm2 per person', 'ft^2/person').round(2)

    vals[:vn] = getDetailsData('HVACSizingSummary', 'Entire Facility', 'Zone Cooling', zoneMetrics[:name], 'Date/Time Of Peak', '', 's')
    vals[:vo] = getDetailsData('HVACSizingSummary', 'Entire Facility', 'Zone Heating', zoneMetrics[:name], 'Date/Time Of Peak', '', 's')

    vals[:vp] = getDetailsData('SystemSummary', 'Entire Facility', 'Time Setpoint Not Met', zoneMetrics[:name], 'During Heating', 'hr', 'hr')
    vals[:vq] = getDetailsData('SystemSummary', 'Entire Facility', 'Time Setpoint Not Met', zoneMetrics[:name], 'During Cooling', 'hr', 'hr')

    vals[:vr] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Gas', "InteriorEquipment:Gas:Zone:#{zoneMetrics[:name]}", 'Electricity Annual Value', 'GJ', 'Therm').round(2)
    vals[:vs] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Electricity', "InteriorEquipment:Electricity:Zone:#{zoneMetrics[:name]}", 'Electricity Annual Value', 'GJ', 'kWh').round(2)

    vals[:vt] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Gas', "InteriorEquipment:Gas:Zone:#{zoneMetrics[:name]}", 'Gas Maximum  Value', 'W', 'kBtu/hr').round(2)
    vals[:vu] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Gas', "InteriorEquipment:Gas:Zone:#{zoneMetrics[:name]}", 'Timestamp of Maximum', '', 's')

    vals[:vv] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Electricity', "InteriorLights:Electricity:Zone:#{zoneMetrics[:name]}", 'Electricity Maximum Value', 'W', 'kW').round(2)
    vals[:vw] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Electricity', "InteriorLights:Electricity:Zone:#{zoneMetrics[:name]}", 'Timestamp of Maximum', '', 's')

    # X unused

    vals[:vy] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Electricity', "InteriorEquipment:Electricity:Zone:#{zoneMetrics[:name]}", 'Electricity Maximum Value', 'W', 'kW').round(2)
    vals[:vz] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Electricity', "InteriorEquipment:Electricity:Zone:#{zoneMetrics[:name]}", 'Timestamp of Maximum', '', 's')

    vals[:vaa] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Other', "InteriorEquipment:DistrictHeating:Zone:#{zoneMetrics[:name]}", 'Annual Value', 'GJ', 'kBtu').round(2)
    vals[:vab] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Other', "InteriorEquipment:DistrictHeating:Zone:#{zoneMetrics[:name]}", 'Maximum Value', 'W', 'kBtu/hr').round(2)
    vals[:vac] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Other', "InteriorEquipment:DistrictHeating:Zone:#{zoneMetrics[:name]}", 'Timestamp of Maximum', '', 's')

    vals[:vad] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Other', "Heating:EnergyTransfer:Zone:#{zoneMetrics[:name]}", 'Annual Value', 'GJ', 'kBtu').round(2)
    vals[:vae] = (getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Other', "Heating:EnergyTransfer:Zone:#{zoneMetrics[:name]}", 'Maximum Value', 'W', 'kBtu/hr') / zoneMetrics[:area]).round(2)
    vals[:vaf] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Other', "Heating:EnergyTransfer:Zone:#{zoneMetrics[:name]}", 'Timestamp of Maximum', '', 's')

    vals[:vag] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Other', "Cooling:EnergyTransfer:Zone:#{zoneMetrics[:name]}", 'Annual Value', 'GJ', 'kBtu').round(2)
    vals[:vah] = (getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Other', "Cooling:EnergyTransfer:Zone:#{zoneMetrics[:name]}", 'Maximum Value', 'W', 'kBtu/hr') / zoneMetrics[:area]).round(2)
    vals[:vai] = getDetailsData('EnergyMeters', 'Entire Facility', 'Annual and Peak Values - Other', "Cooling:EnergyTransfer:Zone:#{zoneMetrics[:name]}", 'Timestamp of Maximum', '', 's')

    vals[:vaj] = zoneHeatComponentCalc('People', zoneMetrics)
    vals[:vak] = zoneHeatComponentCalc('Lights', zoneMetrics)
    vals[:val] = zoneHeatComponentCalc('Equipment', zoneMetrics)
    vals[:vam] = zoneHeatComponentCalc('Refrigeration', zoneMetrics)
    vals[:van] = zoneHeatComponentCalc('Water Use Equipment', zoneMetrics)
    vals[:vao] = zoneHeatComponentCalc('HVAC Equipment Losses', zoneMetrics)
    vals[:vap] = zoneHeatComponentCalc('Power Generation Equipment', zoneMetrics)
    vals[:vaq] = zoneHeatComponentCalc('Infiltration', zoneMetrics)
    vals[:var] = zoneHeatComponentCalc('Zone Ventilation', zoneMetrics)
    vals[:vas] = zoneHeatComponentCalc('Interzone Mixing', zoneMetrics)
    vals[:vat] = zoneHeatComponentCalc('Exterior Wall', zoneMetrics)
    vals[:vau] = zoneHeatComponentCalc('Interzone Wall', zoneMetrics)
    vals[:vav] = zoneHeatComponentCalc('Ground Contact Wall', zoneMetrics)
    vals[:vaw] = zoneHeatComponentCalc('Other Wall', zoneMetrics)
    vals[:vax] = zoneHeatComponentCalc('Opaque Door', zoneMetrics)
    vals[:vay] = zoneHeatComponentCalc('Roof', zoneMetrics)
    vals[:vaz] = zoneHeatComponentCalc('Interzone Ceiling', zoneMetrics)
    vals[:vba] = zoneHeatComponentCalc('Other Roof', zoneMetrics)
    vals[:vbb] = zoneHeatComponentCalc('Exterior Floor', zoneMetrics)
    vals[:vbc] = zoneHeatComponentCalc('Interzone Floor', zoneMetrics)
    vals[:vbd] = zoneHeatComponentCalc('Ground Contact Floor', zoneMetrics)
    vals[:vbe] = zoneHeatComponentCalc('Other Floor', zoneMetrics)
    vals[:vbf] = zoneHeatComponentCalc('Fenestration Conduction', zoneMetrics)
    vals[:vbg] = zoneHeatComponentCalc('Fenestration Solar', zoneMetrics)

    vals[:vbh] = getDetailsData('ZoneComponentLoadSummary', (zoneMetrics[:name]).to_s, 'Heating Peak Conditions', 'Time of Peak Load', 'Value', '', 's')

    vals[:vbi] = zoneCoolComponentCalc('People', zoneMetrics)
    vals[:vbj] = zoneCoolComponentCalc('Lights', zoneMetrics)
    vals[:vbk] = zoneCoolComponentCalc('Equipment', zoneMetrics)
    vals[:vbl] = zoneCoolComponentCalc('Refrigeration', zoneMetrics)
    vals[:vbm] = zoneCoolComponentCalc('Water Use Equipment', zoneMetrics)
    vals[:vbn] = zoneCoolComponentCalc('HVAC Equipment Losses', zoneMetrics)
    vals[:vbo] = zoneCoolComponentCalc('Power Generation Equipment', zoneMetrics)
    vals[:vbp] = zoneCoolComponentCalc('Infiltration', zoneMetrics)
    vals[:vbq] = zoneCoolComponentCalc('Zone Ventilation', zoneMetrics)
    vals[:vbr] = zoneCoolComponentCalc('Interzone Mixing', zoneMetrics)
    vals[:vbs] = zoneCoolComponentCalc('Exterior Wall', zoneMetrics)
    vals[:vbt] = zoneCoolComponentCalc('Interzone Wall', zoneMetrics)
    vals[:vbu] = zoneCoolComponentCalc('Ground Contact Wall', zoneMetrics)
    vals[:vbv] = zoneCoolComponentCalc('Other Wall', zoneMetrics)
    vals[:vbw] = zoneCoolComponentCalc('Opaque Door', zoneMetrics)
    vals[:vbx] = zoneCoolComponentCalc('Roof', zoneMetrics)
    vals[:vby] = zoneCoolComponentCalc('Interzone Ceiling', zoneMetrics)
    vals[:vbz] = zoneCoolComponentCalc('Other Roof', zoneMetrics)
    vals[:vca] = zoneCoolComponentCalc('Exterior Floor', zoneMetrics)
    vals[:vcb] = zoneCoolComponentCalc('Interzone Floor', zoneMetrics)
    vals[:vcc] = zoneCoolComponentCalc('Ground Contact Floor', zoneMetrics)
    vals[:vcd] = zoneCoolComponentCalc('Other Floor', zoneMetrics)
    vals[:vce] = zoneCoolComponentCalc('Fenestration Conduction', zoneMetrics)
    vals[:vcf] = zoneCoolComponentCalc('Fenestration Solar', zoneMetrics)

    vals[:vcg] = getDetailsData('ZoneComponentLoadSummary', (zoneMetrics[:name]).to_s, 'Cooling Peak Conditions', 'Time of Peak Load', 'Value', '', 's')

    # vals = loadTestVals( vals )

    vals[:sumBasicHeating] = (vals[:vaj] + vals[:vak] + vals[:val] + vals[:vam] + vals[:vaq] + vals[:var] + vals[:vas]).round(2)
    vals[:sumBasicCooling] = (vals[:vbi] + vals[:vbj] + vals[:vbk] + vals[:vbl] + vals[:vbp] + vals[:vbq] + vals[:vbr]).round(2)

    vals[:sumOtherHeating] = (vals[:van] + vals[:vao] + vals[:vap]).round(2)
    vals[:sumOtherCooling] = (vals[:vbm] + vals[:vbn] + vals[:vbo]).round(2)

    vals[:sumWallDoorHeating] = (vals[:vat] + vals[:vau] + vals[:vav] + vals[:vaw] + vals[:vax]).round(2)
    vals[:sumWallDoorCooling] = (vals[:vbs] + vals[:vbt] + vals[:vbu] + vals[:vbv] + vals[:vbw]).round(2)

    vals[:sumRoofCeilingHeating] = (vals[:vay] + vals[:vaz] + vals[:vba]).round(2)
    vals[:sumRoofCeilingCooling] = (vals[:vbx] + vals[:vby] + vals[:vbz]).round(2)

    vals[:sumFloorHeating] = (vals[:vbb] + vals[:vbc] + vals[:vbd] + vals[:vbe]).round(2)
    vals[:sumFloorCooling] = (vals[:vca] + vals[:vcb] + vals[:vcc] + vals[:vcd]).round(2)

    vals[:sumWindowsHeating] = (vals[:vbf] + vals[:vbg]).round(2)
    vals[:sumWindowsCooling] = (vals[:vce] + vals[:vcf]).round(2)

    vals[:sumHeatingTotal] = (vals[:sumBasicHeating] + vals[:sumOtherHeating] + vals[:sumWallDoorHeating] + vals[:sumRoofCeilingHeating] + vals[:sumFloorHeating] + vals[:sumWindowsHeating]).round(2)
    vals[:sumCoolingTotal] = (vals[:sumBasicCooling] + vals[:sumOtherCooling] + vals[:sumWallDoorCooling] + vals[:sumRoofCeilingCooling] + vals[:sumFloorCooling] + vals[:sumWindowsCooling]).round(2)

    zoneMetrics[:vals] = vals

    @zone_collection.push(zoneMetrics)

    stacked_bars(zoneMetrics)
  end

  @zone_collection = @zone_collection.sort_by { |z| z[:name] }

  equipment_lengths = @zone_collection.map { |z| z[:equipment].values.map(&:length) }
  max_zone_equipments = equipment_lengths.flatten.max

  # Convert the graph data to JSON
  # This measure requires ruby 2.0.0 to create the JSON for the report graph
  if RUBY_VERSION >= '2.0.0'
    require 'json'
    @graph_data = @graph_data.to_json
  else
    runner.registerInfo("This Measure needs Ruby 2.0.0 to generate timeseries graphs on the report.  You have Ruby #{RUBY_VERSION}.  OpenStudio 1.4.2 and higher user Ruby 2.0.0.")
  end

  web_asset_path = OpenStudio.getSharedResourcesPath / OpenStudio::Path.new('web_assets')

  html_in = getResourceFileData('report.html.in')

  # configure template with variable values
  renderer = ERB.new(html_in)
  html_out = renderer.result(binding)

  writeResourceFileData('report.html', html_out)
  # copyResourceFile( "graph_resource.js" )
  # copyResourceFile( "style_resource.css" )

  # closing the sql file
  @sqlFile.close

  # reporting final condition
  runner.registerFinalCondition("Successfully finished writing 'Zone Report'.")

  return true
end

#stacked_bars(zoneMetrics) ⇒ Object



490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
# File 'lib/measures/ZoneReport/measure.rb', line 490

def stacked_bars(zoneMetrics)
    # people, lights, equipment, refrigeration, other, infiltration, zone ventilation,
    # interzone mixing, walls/doors, roof/ceiling, floor, windows

    vals = zoneMetrics[:vals]

    heatingVals = [vals[:vaj],
                   vals[:vak],
                   vals[:val],
                   vals[:vam],
                   vals[:sumOtherHeating],
                   vals[:vaq],
                   vals[:var],
                   vals[:vas],
                   vals[:sumWallDoorHeating],
                   vals[:sumRoofCeilingHeating],
                   vals[:sumFloorHeating],
                   vals[:sumWindowsHeating]]

    coolingVals = [vals[:vbi],
                   vals[:vbj],
                   vals[:vbk],
                   vals[:vbl],
                   vals[:sumOtherCooling],
                   vals[:vbp],
                   vals[:vbq],
                   vals[:vbr],
                   vals[:sumWallDoorCooling],
                   vals[:sumRoofCeilingCooling],
                   vals[:sumFloorCooling],
                   vals[:sumWindowsCooling]]

    positiveHeating = heatingVals.select { |v| v >= 0 }.inject { |sum, x| sum + x } || 0
    negativeHeating = heatingVals.select { |v| v < 0 }.inject { |sum, x| sum + x } || 0

    positiveCooling = coolingVals.select { |v| v >= 0 }.inject { |sum, x| sum + x } || 0
    negativeCooling = coolingVals.select { |v| v < 0 }.inject { |sum, x| sum + x } || 0

    maxPositive = positiveHeating > positiveCooling ? positiveHeating : positiveCooling
    maxNegative = negativeHeating < negativeCooling ? negativeHeating : negativeCooling

    maxPositive = maxPositive == 0 ? maxNegative * -0.20 : maxPositive
    maxNegative = maxNegative == 0 ? maxPositive * -0.10 : maxNegative

    maxPositive += (maxPositive * 0.20).round(2)
    maxNegative += (maxNegative * 0.10).round(2)

    stacked_vals = [[0, maxPositive, maxNegative, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, vals[:sumHeatingTotal], 0],
                    [2,
                     vals[:vaj],
                     vals[:vak],
                     vals[:val],
                     vals[:vam],
                     vals[:sumOtherHeating],
                     vals[:vaq],
                     vals[:var],
                     vals[:vas],
                     vals[:sumWallDoorHeating],
                     vals[:sumRoofCeilingHeating],
                     vals[:sumFloorHeating],
                     vals[:sumWindowsHeating],
                     0,
                     0],
                    [3,
                     vals[:vbi],
                     vals[:vbj],
                     vals[:vbk],
                     vals[:vbl],
                     vals[:sumOtherCooling],
                     vals[:vbp],
                     vals[:vbq],
                     vals[:vbr],
                     vals[:sumWallDoorCooling],
                     vals[:sumRoofCeilingCooling],
                     vals[:sumFloorCooling],
                     vals[:sumWindowsCooling],
                     0,
                     0],
                    [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, vals[:sumCoolingTotal]],
                    [5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

    # Add the hourly load data to JSON for the report.html
    graph = {}
    graph['title'] = ''
    graph['xaxislabel'] = ''
    graph['yaxislabel'] = 'Contribution Btu/hr/ft2'
    graph['labels'] = ['index', 'people', 'lights', 'equipment', 'refrigeration', 'other', 'infiltration', 'zone_ventilation', 'interzone_mixing', 'walls/doors', 'roof/ceiling', 'floor', 'windows', 'net/heating', 'net/cooling']
    graph['colors'] = ['#888855', '#AAAA55', '#3333AA', '#8888FF', '#888888', '#9999FF', '#AAAAFF', '#AA6666', '#777733', '#888833', '#999933', '#9999FF', '#FF9999', '#9999FF']
    graph['data'] = stacked_vals

    @graph_data << graph
end

#writeResourceFileData(fileName, data) ⇒ Object



606
607
608
609
610
611
612
613
614
615
616
# File 'lib/measures/ZoneReport/measure.rb', line 606

def writeResourceFileData(fileName, data)
  File.open("./#{fileName}", 'w') do |file|
    file << data
    # make sure data is written to the disk one way or the other
    begin
      file.fsync
    rescue StandardError
      file.flush
    end
  end
end

#zoneCoolComponentCalc(component, zoneMetrics) ⇒ Object



486
487
488
# File 'lib/measures/ZoneReport/measure.rb', line 486

def zoneCoolComponentCalc(component, zoneMetrics)
  (getDetailsData('ZoneComponentLoadSummary', (zoneMetrics[:name]).to_s, 'Estimated Cooling Peak Load Components', component, 'Total', 'W', 'Btu/hr') / zoneMetrics[:area]).round(2)
end

#zoneHeatComponentCalc(component, zoneMetrics) ⇒ Object



482
483
484
# File 'lib/measures/ZoneReport/measure.rb', line 482

def zoneHeatComponentCalc(component, zoneMetrics)
  (getDetailsData('ZoneComponentLoadSummary', (zoneMetrics[:name]).to_s, 'Estimated Heating Peak Load Components', component, 'Total', 'W', 'Btu/hr') / zoneMetrics[:area]).round(2)
end