Class: OpenStudioResults
- Inherits:
-
OpenStudio::Measure::ReportingMeasure
- Object
- OpenStudio::Measure::ReportingMeasure
- OpenStudioResults
- Defined in:
- lib/measures/openstudio_results/measure.rb
Overview
start the measure
Instance Method Summary collapse
-
#arguments(model = nil) ⇒ Object
define the arguments that the user will input.
-
#description ⇒ Object
human readable description.
- #energyPlusOutputRequests(runner, user_arguments) ⇒ Object
-
#modeler_description ⇒ Object
human readable description of modeling approach.
-
#name ⇒ Object
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.
- #outputs ⇒ Object
- #possible_sections ⇒ Object
-
#run(runner, user_arguments) ⇒ Object
define what happens when the measure is run.
Instance Method Details
#arguments(model = nil) ⇒ Object
define the arguments that the user will input
95 96 97 98 99 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 |
# File 'lib/measures/openstudio_results/measure.rb', line 95 def arguments(model = nil) args = OpenStudio::Measure::OSArgumentVector.new chs = OpenStudio::StringVector.new chs << 'IP' chs << 'SI' chs << 'OS' units = OpenStudio::Measure::OSArgument.makeChoiceArgument('units', chs, true) units.setDisplayName('Unit System') units.setDescription('IP = Inch Pound, SI = International System, OS = OpenStudio') units.setDefaultValue('IP') args << units # populate arguments possible_sections.each do |method_name| # get display name arg = OpenStudio::Measure::OSArgument.makeBoolArgument(method_name, true) display_name = eval("OsLib_Reporting.#{method_name}(nil,nil,nil,true)[:title]") arg.setDisplayName(display_name) arg.setDefaultValue(true) args << arg end # monthly_details (added this argument to avoid cluttering up output for use cases where monthly data isn't needed) # todo - could extend outputs to list these outputs when argument is true reg_monthly_details = OpenStudio::Measure::OSArgument.makeBoolArgument('reg_monthly_details', true) reg_monthly_details.setDisplayName('Report monthly fuel and enduse breakdown to registerValue') reg_monthly_details.setDescription('This argument does not effect HTML file, instead it makes data from individal cells of monthly tables avaiable for machine readable values in the resulting OpenStudio Workflow file.') reg_monthly_details.setDefaultValue(false) # set to false so no impact on existing projects using the measure args << reg_monthly_details # add energyplus output tables, used by revit systems analysis energyplus_reports = OpenStudio::Measure::OSArgument::makeBoolArgument('energyplus_reports', true) energyplus_reports.setDisplayName('Add EnergyPlus Summary Reports?') energyplus_reports.setDefaultValue(false) args << energyplus_reports args end |
#description ⇒ Object
human readable description
22 23 24 |
# File 'lib/measures/openstudio_results/measure.rb', line 22 def description 'This measure creates high level tables and charts pulling both from model inputs and EnergyPlus results. It has building level information as well as detail on space types, thermal zones, HVAC systems, envelope characteristics, and economics. Click the heading above a chart to view a table of the chart data.' end |
#energyPlusOutputRequests(runner, user_arguments) ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 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 172 173 174 175 176 |
# File 'lib/measures/openstudio_results/measure.rb', line 135 def energyPlusOutputRequests(runner, user_arguments) super(runner, user_arguments) result = OpenStudio::IdfObjectVector.new # use the built-in error checking unless runner.validateUserArguments(arguments, user_arguments) return result end if runner.getBoolArgumentValue('hvac_load_profile', user_arguments) result << OpenStudio::IdfObject.load('Output:Variable,,Site Outdoor Air Drybulb Temperature,monthly;').get end if runner.getBoolArgumentValue('zone_condition_section', user_arguments) result << OpenStudio::IdfObject.load('Output:Variable,,Zone Air Temperature,hourly;').get result << OpenStudio::IdfObject.load('Output:Variable,,Zone Air Relative Humidity,hourly;').get end # gather monthly consumption data for all possible additional fuels category_strs = [] OpenStudio::EndUseCategoryType.getValues.each do |category_type| category_str = OpenStudio::EndUseCategoryType.new(category_type).valueDescription category_strs << category_str end additional_fuel_types = ['FuelOilNo1', 'FuelOilNo2', 'PropaneGas', 'Coal', 'Diesel', 'Gasoline', 'OtherFuel1', 'OtherFuel2'] # additional_fuel_types = OsLib_Reporting.fuel_type_names # getting all fuels instead of additional until fixed in OS additional_fuel_types.each do |additional_fuel_type| monthly_array = ['Output:Table:Monthly'] monthly_array << "Building Energy Performance - #{additional_fuel_type}" monthly_array << '2' category_strs.each do |category_string| monthly_array << "#{category_string}:#{additional_fuel_type}" monthly_array << 'SumOrAverage' end # add ; to end of string result << OpenStudio::IdfObject.load("#{monthly_array.join(',')};").get end result end |
#modeler_description ⇒ Object
human readable description of modeling approach
27 28 29 |
# File 'lib/measures/openstudio_results/measure.rb', line 27 def modeler_description 'For the most part consumption data comes from the tabular EnergyPlus results, however there are a few requests added for time series results. Space type and loop details come from the OpenStudio model. The code for this is modular, making it easy to use as a template for your own custom reports. The structure of the report uses bootstrap, and the graphs use dimple js. The new measure warning section will show warnings generated by upstream measures. It will not show forward translation warnings, EnergyPlus warnings, or warnings that might be reported by this measure.' end |
#name ⇒ Object
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
17 18 19 |
# File 'lib/measures/openstudio_results/measure.rb', line 17 def name 'OpenStudio Results' end |
#outputs ⇒ Object
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/measures/openstudio_results/measure.rb', line 178 def outputs result = OpenStudio::Measure::OSOutputVector.new result << OpenStudio::Measure::OSOutput.makeDoubleOutput('electricity_ip') # kWh result << OpenStudio::Measure::OSOutput.makeDoubleOutput('natural_gas_ip') # MBtu result << OpenStudio::Measure::OSOutput.makeDoubleOutput('additional_fuel_ip') # MBtu result << OpenStudio::Measure::OSOutput.makeDoubleOutput('district_heating_ip') # MBtu result << OpenStudio::Measure::OSOutput.makeDoubleOutput('district_cooling_ip') # MBtu result << OpenStudio::Measure::OSOutput.makeDoubleOutput('total_site_eui') # kBtu/ft^2 result << OpenStudio::Measure::OSOutput.makeDoubleOutput('eui') # kBtu/ft^2 result << OpenStudio::Measure::OSOutput.makeDoubleOutput('net_site_energy') # kBtu result << OpenStudio::Measure::OSOutput.makeDoubleOutput('annual_peak_electric_demand') # kW result << OpenStudio::Measure::OSOutput.makeDoubleOutput('unmet_hours_during_occupied_cooling') # hr result << OpenStudio::Measure::OSOutput.makeDoubleOutput('unmet_hours_during_occupied_heating') # hr result << OpenStudio::Measure::OSOutput.makeDoubleOutput('first_year_capital_cost') # $ result << OpenStudio::Measure::OSOutput.makeDoubleOutput('annual_utility_cost') # $ result << OpenStudio::Measure::OSOutput.makeDoubleOutput('total_lifecycle_cost') # $ # add warning counts result << OpenStudio::Measure::OSOutput.makeDoubleOutput('number_of_measures_with_warnings') result << OpenStudio::Measure::OSOutput.makeDoubleOutput('number_warnings') return result end |
#possible_sections ⇒ Object
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/measures/openstudio_results/measure.rb', line 31 def possible_sections result = [] # methods for sections in order that they will appear in report result << 'building_summary_section' # still need to extend building summary # still need to populate site performance result << 'annual_overview_section' result << 'monthly_overview_section' result << 'utility_bills_rates_section' result << 'envelope_section_section' result << 'space_type_breakdown_section' result << 'space_type_details_section' result << 'interior_lighting_section' # consider binning to space types result << 'plug_loads_section' result << 'exterior_light_section' result << 'water_use_section' result << 'hvac_load_profile' # TODO: - turn on hvac_part_load_profile_table once I have data for it result << 'zone_condition_section' result << 'zone_summary_section' result << 'zone_equipment_detail_section' # TODO: - add in content from other measures # result << 'air_loop_summary_section' # TODO: - stub only result << 'air_loops_detail_section' # later - on all loop detail sections get hard-sized value # result << 'plant_loop_summary_section' # TODO: - stub only result << 'plant_loops_detail_section' result << 'outdoor_air_section' result << 'cost_summary_section' # find out how to get lifecycle cost with utility escalation # consider second cost table listing all lifecycle cost objects in OSM (since can't see in GUI) result << 'source_energy_section' # result << 'co2_and_other_emissions_section' # TODO: - add emissions factors object to our template model # result << 'typical_load_profiles_section' # TODO: - stub only result << 'schedules_overview_section' # TODO: - clean up code to gather schedule profiles so I don't have to grab every 15 minutes # add in section to report warnings for this and other measures result << 'measure_warning_section' # see the method below in os_lib_reporting.rb to see a simple example of code to make a section of tables # result << 'template_section' # TODO: - some tables are so long on real models you loose header. Should we have scrolling within a table? # TODO: - maybe sorting as well if it doesn't slow process down too much result end |
#run(runner, user_arguments) ⇒ Object
define what happens when the measure is run
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 |
# File 'lib/measures/openstudio_results/measure.rb', line 205 def run(runner, user_arguments) super(runner, user_arguments) # get sql, model, and web assets setup = OsLib_Reporting.setup(runner) unless setup return false end model = setup[:model] # workspace = setup[:workspace] sql_file = setup[:sqlFile] web_asset_path = setup[:web_asset_path] # assign the user inputs to variables args = runner.getArgumentValues(arguments, user_arguments) args = Hash[args.collect{ |k, v| [k.to_s, v] }] energyplus_reports = runner.getBoolArgumentValue('energyplus_reports', user_arguments) unless args return false end # get units, runner's is used by revit systems analysis units = args['units'] units = runner.unitsPreference if units == 'OS' is_ip_units = units == 'IP' ? true : false # reporting final condition runner.registerInitialCondition("Gathering data from EnergyPlus SQL file and OSM model. Will report in #{units} Units") # create a array of sections to loop through in erb file @sections = [] # check units of tabular E+ results column_units_query = "SELECT DISTINCT units FROM tabulardatawithstrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' and TableName='Building Area'" energy_plus_area_units = sql_file.execAndReturnVectorOfString(column_units_query) if energy_plus_area_units.is_initialized if energy_plus_area_units.get.empty? runner.registerError("Can't find any contents in Building Area Table to get tabular units. Measure can't run") return false end else runner.registerError("Can't find Building Area to get tabular units. Measure can't run") return false end begin runner.registerValue('standards_gem_version', OpenstudioStandards::VERSION) rescue StandardError end begin runner.registerValue('workflow_gem_version', OpenStudio::Workflow::VERSION) rescue StandardError end if energy_plus_area_units.get.first.to_s == 'm2' # generate data for requested sections sections_made = 0 possible_sections.each do |method_name| next unless args[method_name] section = false eval("section = OsLib_Reporting.#{method_name}(model,sql_file,runner,false,is_ip_units)") display_name = eval("OsLib_Reporting.#{method_name}(nil,nil,nil,true)[:title]") if section @sections << section sections_made += 1 # look for empty tables and warn if skipped because returned empty section[:tables].each do |table| if !table runner.registerWarning("A table in #{display_name} section returned false and was skipped.") section[:messages] = ["One or more tables in #{display_name} section returned false and was skipped."] end end else runner.registerWarning("#{display_name} section returned false and was skipped.") section = {} section[:title] = display_name.to_s section[:tables] = [] section[:messages] = [] section[:messages] << "#{display_name} section returned false and was skipped." @sections << section end rescue StandardError => e display_name = eval("OsLib_Reporting.#{method_name}(nil,nil,nil,true)[:title]") if display_name.nil? then display_name == method_name end runner.registerWarning("#{display_name} section failed and was skipped because: #{e}. Detail on error follows: #{e.backtrace.join("\n")}") # add in section heading with message if section fails section = {} section[:title] = display_name.to_s section[:tables] = [] section[:messages] = [] section[:messages] << "#{display_name} section failed and was skipped because: #{e}. Detail on error is in Measure Warnings section, if enabled at the bottom of this report." # backtrace is now in Measure Warning section and doesn't need to be in line with the report section. # section[:messages] << [e.backtrace.join("\n").to_s] @sections << section end else wrong_tabular_units_string = 'IP units were provided, SI units were expected. Leave EnergyPlus tabular results in SI units to run this report.' runner.registerWarning(wrong_tabular_units_string) section = {} section[:title] = 'Tabular EnergyPlus results provided in wrong units.' section[:tables] = [] section[:messages] = [] section[:messages] << wrong_tabular_units_string @sections << section end # read in template html_in_path = "#{File.dirname(__FILE__)}/resources/report.html.erb" if File.exist?(html_in_path) html_in_path = html_in_path else html_in_path = "#{File.dirname(__FILE__)}/report.html.erb" end html_in = '' File.open(html_in_path, 'r') do |file| html_in = file.read end # configure template with variable values, resources_path needed to find javascript web libraries for html resources_path = "#{File.dirname(__FILE__)}/resources/" renderer = ERB.new(html_in) html_out = renderer.result(binding) # add energyplus reports, used by revit systems analysis html_out = OsLib_Reporting.add_energyplus_reports(runner, html_out) if energyplus_reports # write html file html_out_path = './report.html' File.open(html_out_path, 'w') do |file| file << html_out # make sure data is written to the disk one way or the other begin file.fsync rescue StandardError file.flush end end # adding additional runner.registerValues needed for project scripts in 2.x PAT # note: these are not in begin rescue like individual sections. Won't fail gracefully if any SQL query's can't be found # annual_peak_electric_demand annual_peak_electric_demand_k_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='DemandEndUseComponentsSummary' and ReportForString='Entire Facility' and TableName='End Uses' and RowName= 'Total End Uses' and ColumnName='Electricity' and Units='W'" annual_peak_electric_demand_kw = OpenStudio.convert(sql_file.execAndReturnFirstDouble(annual_peak_electric_demand_k_query).get, 'W', 'kW').get runner.registerValue('annual_peak_electric_demand', annual_peak_electric_demand_kw, 'kW') # get base year for use in first_year_cap_cost baseYrString_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' and ReportForString='Entire Facility' and TableName='Life-Cycle Cost Parameters' and RowName= 'Base Date' and ColumnName= 'Value'" baseYrString = sql_file.execAndReturnFirstString(baseYrString_query).get # get first_year_cap_cost first_year_cap_cost_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' and ReportForString='Entire Facility' and TableName='Capital Cash Flow by Category (Without Escalation)' and RowName= '#{baseYrString}' and ColumnName= 'Total'" first_year_cap_cost = sql_file.execAndReturnFirstDouble(first_year_cap_cost_query).get runner.registerValue('first_year_capital_cost', first_year_cap_cost, '$') # annual_utility_cost annual_utility_cost = sql_file.annualTotalUtilityCost if annual_utility_cost.is_initialized runner.registerValue('annual_utility_cost', annual_utility_cost.get, '$') else runner.registerValue('annual_utility_cost', 0.0, '$') end # total_lifecycle_cost total_lifecycle_cost_query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='Life-Cycle Cost Report' and ReportForString='Entire Facility' and TableName='Present Value by Year' and RowName= 'TOTAL' and ColumnName= 'Present Value of Costs'" runner.registerValue('total_lifecycle_cost', sql_file.execAndReturnFirstDouble(total_lifecycle_cost_query).get, '$') # closing the sql file sql_file.close # reporting final condition runner.registerFinalCondition("Generated report with #{sections_made} sections to #{html_out_path}.") true end |