openstudio-standards 0.8.5 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/data/standards/test_performance_expected_dd_results.csv +90 -90
- data/lib/openstudio-standards/btap/activity.rb +858 -61
- data/lib/openstudio-standards/btap/attributes.rb +297 -103
- data/lib/openstudio-standards/btap/bridging.rb +1335 -593
- data/lib/openstudio-standards/btap/btap.rb +60 -181
- data/lib/openstudio-standards/btap/carbon/btap_carbon.rb +199 -142
- data/lib/openstudio-standards/btap/common/constructions.json +1275 -0
- data/lib/openstudio-standards/btap/common/costs.csv +1969 -0
- data/lib/openstudio-standards/btap/common/costs_local_factors.csv +2315 -0
- data/lib/openstudio-standards/btap/common/materials_glazing.csv +61 -0
- data/lib/openstudio-standards/btap/{common_resources → common}/materials_hvac.csv +1685 -1685
- data/lib/openstudio-standards/btap/{common_resources → common}/materials_lighting.csv +266 -266
- data/lib/openstudio-standards/btap/common/materials_opaque.csv +214 -0
- data/lib/openstudio-standards/btap/common/necb_building_types.csv +35 -0
- data/lib/openstudio-standards/btap/common/necb_space_types.csv +110 -0
- data/lib/openstudio-standards/btap/common/thermal_bridging.csv +73 -0
- data/lib/openstudio-standards/btap/costing/btap_costing.rb +346 -429
- data/lib/openstudio-standards/btap/costing/database.rb +104 -0
- data/lib/openstudio-standards/btap/costing/daylighting_sensor_control_costing.rb +356 -344
- data/lib/openstudio-standards/btap/costing/dcv_costing.rb +282 -296
- data/lib/openstudio-standards/btap/costing/envelope_costing.rb +179 -192
- data/lib/openstudio-standards/btap/costing/heating_cooling_costing.rb +2477 -2316
- data/lib/openstudio-standards/btap/costing/led_lighting_costing.rb +127 -142
- data/lib/openstudio-standards/btap/costing/lighting_costing.rb +216 -198
- data/lib/openstudio-standards/btap/costing/nv_costing.rb +522 -523
- data/lib/openstudio-standards/btap/costing/pv_ground_costing.rb +649 -649
- data/lib/openstudio-standards/btap/costing/shw_costing.rb +626 -625
- data/lib/openstudio-standards/btap/costing/thermal_bridging_costing.rb +72 -0
- data/lib/openstudio-standards/btap/costing/ventilation_costing.rb +2363 -2361
- data/lib/openstudio-standards/btap/deprecated.rb +1033 -0
- data/lib/openstudio-standards/btap/envelope.rb +313 -572
- data/lib/openstudio-standards/btap/fileio.rb +152 -1070
- data/lib/openstudio-standards/btap/geometry.rb +486 -766
- data/lib/openstudio-standards/btap/linear_regression.rb +130 -0
- data/lib/openstudio-standards/btap/paths.rb +46 -0
- data/lib/openstudio-standards/btap/qaqc/analysis.rb +163 -0
- data/lib/openstudio-standards/btap/qaqc/data.rb +2293 -0
- data/lib/openstudio-standards/btap/qaqc/datapoint.rb +641 -0
- data/lib/openstudio-standards/btap/schedules.rb +11 -611
- data/lib/openstudio-standards/btap/structure.rb +574 -189
- data/lib/openstudio-standards/constructions/materials/information.rb +1 -1
- data/lib/openstudio-standards/create_typical/create_typical.rb +1 -1
- data/lib/openstudio-standards/exterior_lighting/create.rb +135 -85
- data/lib/openstudio-standards/exterior_lighting/data/typical_exterior_lighting.json +20 -0
- data/lib/openstudio-standards/exterior_lighting/information.rb +23 -2
- data/lib/openstudio-standards/geometry/create.rb +7 -3
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +3 -2
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +3 -2
- data/lib/openstudio-standards/prototypes/common/data/thermostat_schedule_lookup.json +84 -84
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +4 -4
- data/lib/openstudio-standards/refrigeration/create_walkin.rb +45 -5
- data/lib/openstudio-standards/refrigeration/data/refrigerated_walkins.csv +75 -75
- data/lib/openstudio-standards/schedules/parametric.rb +6 -0
- data/lib/openstudio-standards/service_water_heating/create_typical.rb +10 -3
- data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +30 -14
- data/lib/openstudio-standards/service_water_heating/information.rb +4 -1
- data/lib/openstudio-standards/sql_file/unmet_hours.rb +2 -1
- data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +3 -1
- data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +3 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.boilers.json +12 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +18432 -3072
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.boilers.json +26 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +8532 -1422
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.boilers.json +26 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +8532 -1422
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.boilers.json +16 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.computer_room_acs.json +30 -30
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +7560 -1260
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.PumpVariableSpeed.rb +1 -2
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.boilers.json +16 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +7560 -1260
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.PumpVariableSpeed.rb +1 -2
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.boilers.json +22 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +7236 -1206
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.furnaces.json +223 -19
- data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.schedules.json +37 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.boilers.json +3 -0
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.construction_properties.json +6900 -1150
- data/lib/openstudio-standards/standards/cbes/data/cbes.schedules.json +1 -1
- data/lib/openstudio-standards/standards/deer/data/deer.schedules.json +1 -1
- data/lib/openstudio-standards/standards/necb/BTAP1980TO2010/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +6 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/NECB2011_QAQC.json +2988 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +25 -3
- data/lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb +672 -534
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Hospital.osm +1791 -1792
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMultiTower.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPTownHouse.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeHotel.osm +5 -6
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeOffice.osm +8 -9
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +10 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +14 -15
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Outpatient.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +14 -6
- data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +542 -130
- data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +30 -63
- data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +7 -2
- data/lib/openstudio-standards/standards/necb/NECB2015/data/surface_thermal_transmittance.json +90 -0
- data/lib/openstudio-standards/standards/necb/NECB2015/necb_2015.rb +8 -6
- data/lib/openstudio-standards/standards/necb/NECB2017/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/NECB2017/necb_2017.rb +2 -1
- data/lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb +0 -117
- data/lib/openstudio-standards/standards/necb/NECB2020/data/surface_thermal_transmittance.json +76 -45
- data/lib/openstudio-standards/standards/necb/NECB2020/necb_2020.rb +218 -0
- data/lib/openstudio-standards/standards/necb/common/construction_defaults.osm +12 -7
- data/lib/openstudio-standards/standards/standard.rb +1 -1
- data/lib/openstudio-standards/version.rb +1 -1
- data/lib/openstudio-standards.rb +9 -4
- metadata +59 -66
- data/data/inventory/thermal_bridging.csv +0 -90
- data/data/standards/OpenStudio_Standards-ashrae_90_1-ALL-comstock(space_types).xlsx +0 -0
- data/data/standards/openstudio_standards_duplicates_log.csv +0 -7962
- data/lib/openstudio-standards/btap/NECB_building_types.csv +0 -35
- data/lib/openstudio-standards/btap/NECB_space_types.csv +0 -109
- data/lib/openstudio-standards/btap/WeatherData1.json +0 -2110
- data/lib/openstudio-standards/btap/WeatherData1.xlsx +0 -0
- data/lib/openstudio-standards/btap/btap_result.rb +0 -1144
- data/lib/openstudio-standards/btap/btap_test_helper.rb +0 -86
- data/lib/openstudio-standards/btap/common_paths.rb +0 -81
- data/lib/openstudio-standards/btap/common_resources/ConstructionProperties.csv +0 -52
- data/lib/openstudio-standards/btap/common_resources/Constructions.csv +0 -37
- data/lib/openstudio-standards/btap/common_resources/construction_sets.csv +0 -1270
- data/lib/openstudio-standards/btap/common_resources/constructions_glazing.csv +0 -121
- data/lib/openstudio-standards/btap/common_resources/constructions_opaque.csv +0 -2256
- data/lib/openstudio-standards/btap/common_resources/costs.csv +0 -1897
- data/lib/openstudio-standards/btap/common_resources/costs_local_factors.csv +0 -2315
- data/lib/openstudio-standards/btap/common_resources/materials_glazing.csv +0 -35
- data/lib/openstudio-standards/btap/common_resources/materials_opaque.csv +0 -164
- data/lib/openstudio-standards/btap/costing/btap_measure_helper.rb +0 -359
- data/lib/openstudio-standards/btap/costing/btap_workflow.rb +0 -117
- data/lib/openstudio-standards/btap/costing/copy_test_results_files_to_expected_results.rb +0 -11
- data/lib/openstudio-standards/btap/costing/cost_building_from_file.rb +0 -136
- data/lib/openstudio-standards/btap/costing/costing_database_wrapper.rb +0 -173
- data/lib/openstudio-standards/btap/costing/dummy.epw +0 -8768
- data/lib/openstudio-standards/btap/costing/dummy.osm +0 -5320
- data/lib/openstudio-standards/btap/costing/neb_end_use_prices.csv +0 -42
- data/lib/openstudio-standards/btap/costing/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/btap/costing/necb_reference_runs.csv +0 -28705
- data/lib/openstudio-standards/btap/costing/parallel_tests.rb +0 -92
- data/lib/openstudio-standards/btap/costing/test_list.txt +0 -15
- data/lib/openstudio-standards/btap/costing/test_run_all_test_locally.rb +0 -27
- data/lib/openstudio-standards/btap/costing/test_run_costing_tests.rb +0 -80
- data/lib/openstudio-standards/btap/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/btap/utilities/Add_template_field_to_json.rb +0 -22
- data/lib/openstudio-standards/btap/utilities/convert_surfaces_to_adiabatic_necb_8426.rb +0 -204
- data/lib/openstudio-standards/btap/utilities/generate_space_types.rb +0 -8
- data/lib/openstudio-standards/btap/utilities/necb_to_epw_map.rb +0 -50
- data/lib/openstudio-standards/btap/utilities/os_sim_extract.rb +0 -48
- data/lib/openstudio-standards/btap/utilities/rename_surfaces.rb +0 -24
- data/lib/openstudio-standards/btap/utilities/round_surf_coords.rb +0 -32
- data/lib/openstudio-standards/btap/utilities/sched_create.rb +0 -145
- data/lib/openstudio-standards/btap/utilities/set_mult_to_adiabatic.rb +0 -53
- data/lib/openstudio-standards/btap/utilities/space_type.rb +0 -78
- data/lib/openstudio-standards/btap/utilities/weatherData1_xlsx_to_json.rb +0 -56
- data/lib/openstudio-standards/btap/vintagizer.rb +0 -161
- data/lib/openstudio-standards/btap/visualization.rb +0 -72
- data/lib/openstudio-standards/standards/necb/NECB2011/data/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/standards/necb/common/btap_analysis.rb +0 -79
- data/lib/openstudio-standards/standards/necb/common/btap_data.rb +0 -2656
- data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +0 -653
- data/lib/openstudio-standards/standards/necb/common/necb_reference_runs.csv +0 -28705
- data/lib/openstudio-standards/standards/necb/common/utility_pricing_2025-02-20.csv +0 -92
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_frame.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_glazing.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_opaque.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/hvac_vent_ahu.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/lighting.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/lighting_sets.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/locations.csv +0 -0
- /data/lib/openstudio-standards/btap/{costing → common}/mech_sizing.json +0 -0
|
@@ -17,255 +17,54 @@
|
|
|
17
17
|
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
18
18
|
# **********************************************************************/
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
require 'fileutils'
|
|
23
|
-
require 'csv'
|
|
24
20
|
require 'securerandom'
|
|
25
21
|
require 'digest'
|
|
26
22
|
|
|
27
|
-
|
|
28
23
|
module BTAP
|
|
29
24
|
module FileIO
|
|
30
25
|
|
|
31
|
-
#Test Constructions Module
|
|
32
|
-
if __FILE__ == $0
|
|
33
|
-
require 'test/unit'
|
|
34
|
-
class FileIOTests < Test::Unit::TestCase
|
|
35
|
-
|
|
36
|
-
def setup
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def test_load_save_DOE_file()
|
|
40
|
-
BTAP::FileIO::load_e_quest(BTAP::OS_RUBY_PATH + '\Resources\eQuest_3.64\4StoreyBuilding.inp')
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
# Get the name of the model.
|
|
47
|
-
# @author Phylroy A. Lopez
|
|
48
|
-
# @return [String] the name of the model.
|
|
49
|
-
def self.get_name(model)
|
|
50
|
-
unless model.building.get.name.empty?
|
|
51
|
-
return model.building.get.name.get.to_s
|
|
52
|
-
else
|
|
53
|
-
return ""
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# @author Phylroy A. Lopez
|
|
58
|
-
# Get the name of the model.
|
|
59
|
-
# @author Phylroy A. Lopez
|
|
60
|
-
# @return [String] the name of the model.
|
|
61
|
-
def self.set_name(model,name)
|
|
62
|
-
unless model.building.empty?
|
|
63
|
-
model.building.get.setName(name)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# @author Phylroy A. Lopez
|
|
68
|
-
# Get the name of the model.
|
|
69
|
-
# @author Phylroy A. Lopez
|
|
70
|
-
# @return [String] the name of the model.
|
|
71
|
-
def self.set_sql_file(model,sql_path)
|
|
72
|
-
model.setSqlFile(OpenStudio::Path.new( sql_path) )
|
|
73
|
-
end
|
|
74
|
-
#@author Phylroy A. Lopez
|
|
75
|
-
# Get the filepath of all files with extention
|
|
76
|
-
# @param folder [String} the path to the folder to be scanned.
|
|
77
|
-
# @param ext [String] the file extension name, ex ".epw"
|
|
78
|
-
def self.get_find_files_from_folder_by_extension(folder, ext)
|
|
79
|
-
Dir.glob("#{folder}/**/*#{ext}")
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def self.delete_files_in_folder_by_extention(folder,ext)
|
|
83
|
-
BTAP::FileIO::get_find_files_from_folder_by_extension(folder, ext).each do |file|
|
|
84
|
-
FileUtils.rm(file)
|
|
85
|
-
#puts "#{file} deleted."
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def self.find_file_in_folder_by_filename(folder,filename)
|
|
90
|
-
Dir.glob("#{folder}/**/*#{filename}")
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def self.fix_url_to_path(url_string)
|
|
94
|
-
if url_string =~/\/([a-zA-Z]:.*)/
|
|
95
|
-
return $1
|
|
96
|
-
else
|
|
97
|
-
return url_string
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
# This method loads an OpenStudio file into the model.
|
|
103
|
-
# @author Phylroy A. Lopez
|
|
104
|
-
# @param filepath [String] path to the OSM file.
|
|
105
|
-
# @param name [String] optional model name to be set to model.
|
|
106
|
-
# @return [OpenStudio::Model::Model] an OpenStudio model object.
|
|
107
|
-
def self.load_idf(filepath, name = "")
|
|
108
|
-
#load file
|
|
109
|
-
unless File.exist?(filepath)
|
|
110
|
-
raise 'File does not exist: ' + filepath.to_s
|
|
111
|
-
end
|
|
112
|
-
#puts "loading file #{filepath}..."
|
|
113
|
-
model_path = OpenStudio::Path.new(filepath.to_s)
|
|
114
|
-
#Upgrade version if required.
|
|
115
|
-
version_translator = OpenStudio::OSVersion::VersionTranslator.new
|
|
116
|
-
model = OpenStudio::EnergyPlus::loadAndTranslateIdf(model_path)
|
|
117
|
-
version_translator.errors.each {|error| puts "Error: #{error.logMessage}\n\n"}
|
|
118
|
-
version_translator.warnings.each {|warning| puts "Warning: #{warning.logMessage}\n\n"}
|
|
119
|
-
#If model did not load correctly.
|
|
120
|
-
if model.empty?
|
|
121
|
-
raise 'something went wrong'
|
|
122
|
-
end
|
|
123
|
-
model = model.get
|
|
124
|
-
if name != ""
|
|
125
|
-
self.set_name(model,name)
|
|
126
|
-
end
|
|
127
|
-
#puts "File #{filepath} loaded."
|
|
128
|
-
return model
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def self.replace_model(model,new_model,runner = nil)
|
|
132
|
-
# pull original weather file object over
|
|
133
|
-
weather_file = new_model.getOptionalWeatherFile
|
|
134
|
-
if not weather_file.empty?
|
|
135
|
-
weather_file.get.remove
|
|
136
|
-
BTAP::runner_register("Info", "Removed alternate model's weather file object.",runner)
|
|
137
|
-
end
|
|
138
|
-
original_weather_file = model.getOptionalWeatherFile
|
|
139
|
-
if not original_weather_file.empty?
|
|
140
|
-
original_weather_file.get.clone(new_model)
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# pull original design days over
|
|
144
|
-
new_model.getDesignDays.sort.each { |designDay|
|
|
145
|
-
designDay.remove
|
|
146
|
-
}
|
|
147
|
-
model.getDesignDays.sort.each { |designDay|
|
|
148
|
-
designDay.clone(new_model)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
# swap underlying data in model with underlying data in new_model
|
|
152
|
-
# remove existing objects from model
|
|
153
|
-
handles = OpenStudio::UUIDVector.new
|
|
154
|
-
model.objects.each do |obj|
|
|
155
|
-
handles << obj.handle
|
|
156
|
-
end
|
|
157
|
-
model.removeObjects(handles)
|
|
158
|
-
# add new file to empty model
|
|
159
|
-
model.addObjects( new_model.toIdfFile.objects )
|
|
160
|
-
BTAP::runner_register("Info", "Model name is now #{model.building.get.name}.", runner)
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
26
|
# This method loads an OpenStudio file into the model.
|
|
168
27
|
# @author Phylroy A. Lopez
|
|
169
28
|
# @param filepath [String] path to the OSM file.
|
|
170
29
|
# @param name [String] optional model name to be set to model.
|
|
171
30
|
# @return [OpenStudio::Model::Model] an OpenStudio model object.
|
|
172
31
|
def self.load_osm(filepath, name = "")
|
|
173
|
-
|
|
174
|
-
#load file
|
|
175
32
|
unless File.exist?(filepath)
|
|
176
33
|
raise 'File does not exist: ' + filepath.to_s
|
|
177
34
|
end
|
|
178
|
-
#puts "loading file #{filepath}..."
|
|
179
35
|
model_path = OpenStudio::Path.new(filepath.to_s)
|
|
180
|
-
|
|
36
|
+
|
|
37
|
+
# Upgrade version if required.
|
|
181
38
|
version_translator = OpenStudio::OSVersion::VersionTranslator.new
|
|
182
39
|
model = version_translator.loadModel(model_path)
|
|
183
|
-
version_translator.errors.each {|error| puts "Error: #{error.logMessage}\n
|
|
184
|
-
version_translator.warnings.each {|warning| puts "Warning: #{warning.logMessage}\n
|
|
185
|
-
#If model did not load correctly.
|
|
40
|
+
version_translator.errors.each { |error| puts "Error: #{error.logMessage}\n" }
|
|
41
|
+
version_translator.warnings.each { |warning| puts "Warning: #{warning.logMessage}\n" }
|
|
186
42
|
if model.empty?
|
|
187
|
-
raise "
|
|
43
|
+
raise "Could not load #{filepath}"
|
|
188
44
|
end
|
|
189
45
|
model = model.get
|
|
190
46
|
if name != "" and not name.nil?
|
|
191
47
|
self.set_name(model,name)
|
|
192
48
|
end
|
|
193
|
-
#puts "File #{filepath} loaded."
|
|
194
49
|
|
|
195
50
|
return model
|
|
196
51
|
end
|
|
197
52
|
|
|
198
|
-
#
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
53
|
+
#load a model into OS & version translates, exiting and erroring if a problem is found
|
|
54
|
+
def self.safe_load_model(model_path_string)
|
|
55
|
+
model_path = OpenStudio::Path.new(model_path_string)
|
|
56
|
+
if OpenStudio::exists(model_path)
|
|
57
|
+
versionTranslator = OpenStudio::OSVersion::VersionTranslator.new
|
|
58
|
+
model = versionTranslator.loadModel(model_path)
|
|
59
|
+
if model.empty?
|
|
60
|
+
raise "Version translation failed for #{model_path_string}"
|
|
61
|
+
else
|
|
62
|
+
model = model.get
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
raise "#{model_path_string} couldn't be found"
|
|
206
66
|
end
|
|
207
|
-
#puts "loading equest file #{filepath}. This will only convert geometry."
|
|
208
|
-
#Create an instancse of a DOE model
|
|
209
|
-
doe_model = BTAP::EQuest::DOEBuilding.new()
|
|
210
|
-
#Load the inp data into the DOE model.
|
|
211
|
-
doe_model.load_inp(filepath)
|
|
212
|
-
|
|
213
|
-
#Convert the model to a OSM format.
|
|
214
|
-
model = doe_model.create_openstudio_model_new()
|
|
215
|
-
return model
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
#This method will inject OSM objects from a OSM file/library into the current
|
|
219
|
-
# model.
|
|
220
|
-
# @author Phylroy A. Lopez
|
|
221
|
-
# @param filepath [String] path to the OSM library file.
|
|
222
|
-
# @return [OpenStudio::Model::Model] an OpenStudio model object (self reference).
|
|
223
|
-
def self.inject_osm_file(model, filepath)
|
|
224
|
-
osm_data = BTAP::FileIO::load_osm(filepath)
|
|
225
|
-
model.addObjects(osm_data.objects);
|
|
226
67
|
return model
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
# This method will return a deep copy of the model.
|
|
230
|
-
# Simply because I don't trust the clone method yet.
|
|
231
|
-
# @author Phylroy A. Lopez
|
|
232
|
-
# @return [OpenStudio::Model::Model] a copy of the OpenStudio model object.
|
|
233
|
-
def self.deep_copy(model,bool = true)
|
|
234
|
-
return model.clone(bool).to_Model
|
|
235
|
-
|
|
236
|
-
# pull original weather file object over
|
|
237
|
-
weather_file = new_model.getOptionalWeatherFile
|
|
238
|
-
if not weather_file.empty?
|
|
239
|
-
weather_file.get.remove
|
|
240
|
-
BTAP::runner_register("Info", "Removed alternate model's weather file object.",runner)
|
|
241
|
-
end
|
|
242
|
-
original_weather_file = model.getOptionalWeatherFile
|
|
243
|
-
if not original_weather_file.empty?
|
|
244
|
-
original_weather_file.get.clone(new_model)
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
# pull original design days over
|
|
248
|
-
new_model.getDesignDays.sort.each { |designDay|
|
|
249
|
-
designDay.remove
|
|
250
|
-
}
|
|
251
|
-
model.getDesignDays.sort.each { |designDay|
|
|
252
|
-
designDay.clone(new_model)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
# swap underlying data in model with underlying data in new_model
|
|
256
|
-
# remove existing objects from model
|
|
257
|
-
handles = OpenStudio::UUIDVector.new
|
|
258
|
-
model.objects.each do |obj|
|
|
259
|
-
handles << obj.handle
|
|
260
|
-
end
|
|
261
|
-
model.removeObjects(handles)
|
|
262
|
-
# add new file to empty model
|
|
263
|
-
model.addObjects( new_model.toIdfFile.objects )
|
|
264
|
-
BTAP::runner_register("Info", "Model name is now #{model.building.get.name}.", runner)
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
68
|
end
|
|
270
69
|
|
|
271
70
|
# This method will save the model to an osm file.
|
|
@@ -304,404 +103,50 @@ module BTAP
|
|
|
304
103
|
}
|
|
305
104
|
end
|
|
306
105
|
|
|
106
|
+
# This method will return a deep copy of the model.
|
|
107
|
+
# Simply because I don't trust the clone method yet.
|
|
108
|
+
# @author Phylroy A. Lopez
|
|
109
|
+
# @return [OpenStudio::Model::Model] a copy of the OpenStudio model object.
|
|
110
|
+
def self.deep_copy(model,bool = true)
|
|
111
|
+
return model.clone(bool).to_Model
|
|
307
112
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
def self.convert_all_eso_to_csv(in_folder,out_folder)
|
|
317
|
-
list_of_csv_files = Array.new
|
|
318
|
-
FileUtils.mkdir_p(out_folder)
|
|
319
|
-
osmfiles = BTAP::FileIO::get_find_files_from_folder_by_extension(in_folder,".eso")
|
|
320
|
-
|
|
321
|
-
osmfiles.each do |eso_file_path|
|
|
322
|
-
|
|
323
|
-
#Run ESO Vars command must be run in folder.
|
|
324
|
-
root_folder = Dir.getwd()
|
|
325
|
-
#puts File.dirname(eso_file_path)
|
|
326
|
-
Dir.chdir(File.dirname(eso_file_path))
|
|
327
|
-
if File.exist?("eplustbl.htm")
|
|
328
|
-
File.open("dummy.rvi", 'w') {|f| f.write("") }
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
system("#{BTAP::SimManager::ProcessManager::find_read_vars_eso()} dummy.rvi unlimited")
|
|
332
|
-
#get name of run from html file.
|
|
333
|
-
runname = ""
|
|
334
|
-
f = File.open("eplustbl.htm")
|
|
335
|
-
f.each_line do |line|
|
|
336
|
-
if line =~ /<p>Building: <b>(.*)<\/b><\/p>/
|
|
337
|
-
#puts "Found name: #{$1}"
|
|
338
|
-
runname = $1
|
|
339
|
-
break
|
|
340
|
-
end
|
|
341
|
-
end
|
|
342
|
-
f.close
|
|
343
|
-
#copy files over with distinct names
|
|
344
|
-
#puts "copy hourly results to #{out_folder}/#{runname}_eplusout.csv"
|
|
345
|
-
FileUtils.cp("eplusout.csv","#{out_folder}/#{runname}_eplusout.csv")
|
|
346
|
-
#puts "copy html results to #{out_folder}/#{runname}_eplustbl.htm"
|
|
347
|
-
FileUtils.cp("eplustbl.htm","#{out_folder}/#{runname}_eplustbl.htm")
|
|
348
|
-
#puts "copy sql results to #{out_folder}/#{runname}_eplusout.sql"
|
|
349
|
-
FileUtils.cp("eplusout.sql","#{out_folder}/#{runname}_eplusout.sql")
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
list_of_csv_files << "#{out_folder}/#{runname}_eplusout.csv"
|
|
353
|
-
end
|
|
354
|
-
Dir.chdir(root_folder)
|
|
355
|
-
end
|
|
356
|
-
return list_of_csv_files
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
# This method will read a CSV file and return rows as hashes based on the selection given.
|
|
361
|
-
# @author Phylroy Lopez
|
|
362
|
-
# @param file The path to the csv file.
|
|
363
|
-
# @param searchHash
|
|
364
|
-
# @return matches A Array of rows that match the searchHash. The row is a Hash itself.
|
|
365
|
-
def self.csv_look_up_rows(file, searchHash)
|
|
366
|
-
options = {
|
|
367
|
-
:headers => true,
|
|
368
|
-
:converters => :numeric }
|
|
369
|
-
table = CSV.read( file, options )
|
|
370
|
-
# we'll save the matches here
|
|
371
|
-
matches = nil
|
|
372
|
-
# save a copy of the headers
|
|
373
|
-
matches = table.find_all do |row|
|
|
374
|
-
row
|
|
375
|
-
match = true
|
|
376
|
-
searchHash.keys.each do |key|
|
|
377
|
-
match = match && ( row[key] == searchHash[key] )
|
|
378
|
-
end
|
|
379
|
-
match
|
|
380
|
-
end
|
|
381
|
-
return matches
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
def self.csv_look_up_unique_row(file, searchHash)
|
|
385
|
-
#Load Vintage database information.
|
|
386
|
-
matches = BTAP::FileIO::csv_look_up_rows(file, searchHash)
|
|
387
|
-
raise( "Error: CSV lookup found more than one row that met criteria #{searchHash} in #{@file} ") if matches.size() > 1
|
|
388
|
-
raise( "Error: CSV lookup found no rows that met criteria #{searchHash} in #{@file}") if matches.size() < 1
|
|
389
|
-
return matches[0]
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
# This method will read a CSV file and return the unique values in a given column header.
|
|
394
|
-
# @author Phylroy Lopez
|
|
395
|
-
# @param file The path to the csv file.
|
|
396
|
-
# @param colHeader The header name in teh csv file.
|
|
397
|
-
# @return matches A Array of rows that match the searchHash. The row is a Hash itself.
|
|
398
|
-
def self.csv_look_up_unique_col_data(file, colHeader)
|
|
399
|
-
column_data = Array.new
|
|
400
|
-
CSV.foreach( file, :headers => true ) do |row|
|
|
401
|
-
column_data << row[colHeader] # For each row, give me the cell that is under the colHeader column
|
|
402
|
-
end
|
|
403
|
-
return column_data.sort!.uniq
|
|
404
|
-
end
|
|
405
|
-
|
|
406
|
-
def self.sum_row_headers(row,headers)
|
|
407
|
-
total = 0.0
|
|
408
|
-
headers.each { |header| total = total + row[header] }
|
|
409
|
-
return total
|
|
410
|
-
end
|
|
411
|
-
|
|
412
|
-
def self.terminus_hourly_output(csv_file)
|
|
413
|
-
#puts "Starting Terminus output processing."
|
|
414
|
-
#puts "reading #{csv_file} being processed"
|
|
415
|
-
#reads csv file into memory.
|
|
416
|
-
original = CSV.read(csv_file,
|
|
417
|
-
{
|
|
418
|
-
:headers => true, #This flag tell the parser that there are headers.
|
|
419
|
-
:converters => :numeric #This tell it to convert string data into numeric when possible.
|
|
420
|
-
}
|
|
421
|
-
)
|
|
422
|
-
#puts "done reading #{csv_file} being processed"
|
|
423
|
-
# We are going to collect the header names that fit a pattern. But first we need to
|
|
424
|
-
# create array containers to save the header name. In ruby we can use the string header names
|
|
425
|
-
# as the array index.
|
|
426
|
-
|
|
427
|
-
#Create arrays to store the header names for each type.
|
|
428
|
-
waterheater_gas_rate_headers = Array.new()
|
|
429
|
-
waterheater_electric_rate_headers = Array.new()
|
|
430
|
-
waterheater_heating_rate_headers = Array.new()
|
|
431
|
-
cooling_coil_electric_power_headers = Array.new()
|
|
432
|
-
cooling_coil_total_cooling_rate_headers = Array.new()
|
|
433
|
-
heating_coil_air_heating_rate_headers = Array.new()
|
|
434
|
-
heating_coil_gas_rate_headers = Array.new()
|
|
435
|
-
plant_supply_heating_demand_rate_headers = Array.new()
|
|
436
|
-
facility_total_electrical_demand_headers = Array.new()
|
|
437
|
-
boiler_gas_rate_headers = Array.new()
|
|
438
|
-
time_index = Array.new()
|
|
439
|
-
boiler_gas_rate_headers = Array.new()
|
|
440
|
-
heating_coil_electric_power_headers = Array.new()
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
#remove rows 2-169 (or 1-168 in computer array terms)
|
|
444
|
-
original = self.remove_rows_from_csv_table(0,72,original)
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
#Scan the CSV file to file all the headers that match the pattern. This will go through all the headers and find
|
|
448
|
-
# any header that matches our regular expression if a match is made, the header name is stuffed into the string array.
|
|
449
|
-
original.headers.each do |header|
|
|
450
|
-
stripped_header = header.strip
|
|
451
|
-
waterheater_electric_rate_headers << header if stripped_header =~/^.*:Water Heater Electric Power \[W\]\(Hourly\)$/
|
|
452
|
-
waterheater_gas_rate_headers << header if stripped_header =~/^.*:Water Heater Gas Rate \[W\]\(Hourly\)$/
|
|
453
|
-
waterheater_heating_rate_headers << header if stripped_header =~/^.*:Water Heater Heating Rate \[W\]\(Hourly\)$/
|
|
454
|
-
cooling_coil_electric_power_headers << header if stripped_header =~/^.*:Cooling Coil Electric Power \[W\]\(Hourly\)$/
|
|
455
|
-
cooling_coil_total_cooling_rate_headers << header if stripped_header =~/^.*:Cooling Coil Total Cooling Rate \[W\]\(Hourly\)$/
|
|
456
|
-
heating_coil_air_heating_rate_headers << header if stripped_header =~/^.*:Heating Coil Air Heating Rate \[W\]\(Hourly\)$/
|
|
457
|
-
heating_coil_gas_rate_headers << header if stripped_header =~/^.*:Heating Coil Gas Rate \[W\]\(Hourly\)$/
|
|
458
|
-
heating_coil_electric_power_headers << header if stripped_header =~/^.*:Heating Coil Electric Power \[W\]\(Hourly\)$/
|
|
459
|
-
plant_supply_heating_demand_rate_headers << header if stripped_header =~/^(?!SWH PLANT LOOP).*:Plant Supply Side Heating Demand Rate \[W\]\(Hourly\)$/
|
|
460
|
-
facility_total_electrical_demand_headers << header if stripped_header =~/^.*:Facility Total Electric Demand Power \[W\]\(Hourly\)$/
|
|
461
|
-
boiler_gas_rate_headers << header if stripped_header =~/^.*:Boiler Gas Rate \[W\]\(Hourly\)/
|
|
462
|
-
|
|
463
|
-
end
|
|
464
|
-
#Debug printout stuff. Make sure the output it captures the headers you want otherwise modify the regex above
|
|
465
|
-
#puts waterheater_gas_rate_headers
|
|
466
|
-
#puts waterheater_electric_rate_headers
|
|
467
|
-
#puts waterheater_heating_rate_headers
|
|
468
|
-
|
|
469
|
-
#puts cooling_coil_electric_power_headers
|
|
470
|
-
#puts cooling_coil_total_cooling_rate_headers
|
|
471
|
-
|
|
472
|
-
#puts heating_coil_air_heating_rate_headers
|
|
473
|
-
#puts heating_coil_gas_rate_headers
|
|
474
|
-
|
|
475
|
-
#puts plant_supply_heating_demand_rate_headers
|
|
476
|
-
#puts facility_total_electrical_demand_headers
|
|
477
|
-
#puts boiler_gas_rate_headers
|
|
478
|
-
#puts heating_coil_electric_power_headers
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
#open up a new file to save the file to..Note: This will fail it the file is open in EXCEL.
|
|
482
|
-
CSV.open("#{csv_file}.terminus_hourly.csv", 'w') do |csv|
|
|
483
|
-
#Create header row for new terminus hourly file.
|
|
484
|
-
csv << [
|
|
485
|
-
"Date/Time",
|
|
486
|
-
"water_heater_gas_rate_total",
|
|
487
|
-
"water_heater_electric_rate_total",
|
|
488
|
-
"water_heater_heating_rate_total",
|
|
489
|
-
"cooling_coil_electric_power_total",
|
|
490
|
-
"cooling_coil_total_cooling_rate_total",
|
|
491
|
-
"heating_coil_air_heating_rate_total",
|
|
492
|
-
"heating_coil_gas_rate_total",
|
|
493
|
-
"heating_coil_electric_power_total",
|
|
494
|
-
"plant_supply_heating_demand_rate_total",
|
|
495
|
-
"facility_total_electrical_demand_total",
|
|
496
|
-
"boiler_gas_rate_total"
|
|
497
|
-
]
|
|
498
|
-
original.each do |row|
|
|
499
|
-
|
|
500
|
-
# We are now writing data to the new csv file. This is where we can manipulate the data, row by row.
|
|
501
|
-
# sum the headers collected above and store in specific *_total variables.
|
|
502
|
-
# This is done via a small function self.sum_row_headers. There may only be a single
|
|
503
|
-
# header collected.. That is fine. It is better to be flexible than hardcode anything.
|
|
504
|
-
water_heater_gas_rate_total = self.sum_row_headers(row,waterheater_gas_rate_headers)
|
|
505
|
-
water_heater_electric_rate_total = self.sum_row_headers(row,waterheater_electric_rate_headers)
|
|
506
|
-
water_heater_heating_rate_total = self.sum_row_headers(row,waterheater_heating_rate_headers)
|
|
507
|
-
cooling_coil_electric_power_total = self.sum_row_headers(row, cooling_coil_electric_power_headers)
|
|
508
|
-
cooling_coil_total_cooling_rate_total = self.sum_row_headers(row, cooling_coil_total_cooling_rate_headers)
|
|
509
|
-
heating_coil_air_heating_rate_total = self.sum_row_headers(row, heating_coil_air_heating_rate_headers)
|
|
510
|
-
heating_coil_gas_rate_total = self.sum_row_headers(row, heating_coil_gas_rate_headers)
|
|
511
|
-
heating_coil_electric_power_total = self.sum_row_headers(row, heating_coil_electric_power_headers)
|
|
512
|
-
plant_supply_heating_demand_rate_total = self.sum_row_headers(row, plant_supply_heating_demand_rate_headers)
|
|
513
|
-
facility_total_electrical_demand_total = self.sum_row_headers(row, facility_total_electrical_demand_headers)
|
|
514
|
-
boiler_gas_rate_headers_total = self.sum_row_headers(row, boiler_gas_rate_headers)
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
#Write the data out. Should match header row as above.
|
|
519
|
-
csv << [
|
|
520
|
-
row["Date/Time"], #Time index is hardcoded because every file will have a "Date/Time" column header.
|
|
521
|
-
water_heater_gas_rate_total,
|
|
522
|
-
water_heater_electric_rate_total,
|
|
523
|
-
water_heater_heating_rate_total,
|
|
524
|
-
cooling_coil_electric_power_total,
|
|
525
|
-
cooling_coil_total_cooling_rate_total,
|
|
526
|
-
heating_coil_air_heating_rate_total,
|
|
527
|
-
heating_coil_gas_rate_total,
|
|
528
|
-
heating_coil_electric_power_total,
|
|
529
|
-
plant_supply_heating_demand_rate_total,
|
|
530
|
-
facility_total_electrical_demand_total,
|
|
531
|
-
boiler_gas_rate_headers_total
|
|
532
|
-
]
|
|
533
|
-
end
|
|
113
|
+
# pull original weather file object over
|
|
114
|
+
weather_file = new_model.getOptionalWeatherFile
|
|
115
|
+
if not weather_file.empty?
|
|
116
|
+
weather_file.get.remove
|
|
117
|
+
BTAP::runner_register("Info", "Removed alternate model's weather file object.",runner)
|
|
534
118
|
end
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
def self.remove_rows_from_csv_table(start_index,stop_index,table)
|
|
539
|
-
total_rows_to_remove = stop_index - start_index
|
|
540
|
-
(0..total_rows_to_remove-1).each do |counter|
|
|
541
|
-
table.delete(start_index)
|
|
119
|
+
original_weather_file = model.getOptionalWeatherFile
|
|
120
|
+
if not original_weather_file.empty?
|
|
121
|
+
original_weather_file.get.clone(new_model)
|
|
542
122
|
end
|
|
543
|
-
return table
|
|
544
|
-
end
|
|
545
123
|
|
|
124
|
+
# pull original design days over
|
|
125
|
+
new_model.getDesignDays.sort.each { |designDay|
|
|
126
|
+
designDay.remove
|
|
127
|
+
}
|
|
128
|
+
model.getDesignDays.sort.each { |designDay|
|
|
129
|
+
designDay.clone(new_model)
|
|
130
|
+
}
|
|
546
131
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
model = versionTranslator.loadModel(model_path)
|
|
553
|
-
if model.empty?
|
|
554
|
-
raise "Version translation failed for #{model_path_string}"
|
|
555
|
-
else
|
|
556
|
-
model = model.get
|
|
557
|
-
end
|
|
558
|
-
else
|
|
559
|
-
raise "#{model_path_string} couldn't be found"
|
|
132
|
+
# swap underlying data in model with underlying data in new_model
|
|
133
|
+
# remove existing objects from model
|
|
134
|
+
handles = OpenStudio::UUIDVector.new
|
|
135
|
+
model.objects.each do |obj|
|
|
136
|
+
handles << obj.handle
|
|
560
137
|
end
|
|
561
|
-
|
|
138
|
+
model.removeObjects(handles)
|
|
139
|
+
# add new file to empty model
|
|
140
|
+
model.addObjects( new_model.toIdfFile.objects )
|
|
141
|
+
BTAP::runner_register("Info", "Model name is now #{model.building.get.name}.", runner)
|
|
562
142
|
end
|
|
563
143
|
|
|
564
|
-
#function to wrap debug == true puts
|
|
565
144
|
def debug_puts(puts_text)
|
|
566
145
|
if Debug_Mode == true
|
|
567
146
|
puts "#{puts_text}"
|
|
568
147
|
end
|
|
569
148
|
end
|
|
570
149
|
|
|
571
|
-
def get_timeseries_arrays(openstudio_sql_file, timestep, variable_name_array, regex_name_filter = /.*/, env_period = nil)
|
|
572
|
-
returnArray = Array.new()
|
|
573
|
-
variable_name_array.each do |variable_name|
|
|
574
|
-
possible_key_values = openstudio_sql_file.availableKeyValues(env_period,timestep,variable_name)
|
|
575
|
-
possible_variable_names = openstudio_sql_file.availableVariableNames(env_period,timestep).include?(variable_name)
|
|
576
|
-
if not possible_variable_names.nil? and possible_variable_names.include?(variable_name) and not possible_key_values.nil?
|
|
577
|
-
possible_key_values.get.sort.each do |key_value|
|
|
578
|
-
unless regex_name_filter.match(key_value).nil?
|
|
579
|
-
returnArray << get_timeseries_array(openstudio_sql_file, timestep, variable_name, key_value)
|
|
580
|
-
end
|
|
581
|
-
end
|
|
582
|
-
end
|
|
583
|
-
return returnArray
|
|
584
|
-
end
|
|
585
|
-
end
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
#gets a time series data vector from the sql file and puts the values into a standard array of numbers
|
|
591
|
-
def get_timeseries_array(openstudio_sql_file, timestep, variable_name, key_value)
|
|
592
|
-
zone_time_step = "Zone Timestep"
|
|
593
|
-
hourly_time_step = "Hourly"
|
|
594
|
-
hvac_time_step = "HVAC System Timestep"
|
|
595
|
-
timestep = hourly_time_step
|
|
596
|
-
env_period = openstudio_sql_file.availableEnvPeriods[0]
|
|
597
|
-
#puts openstudio_sql_file.class
|
|
598
|
-
#puts env_period.class
|
|
599
|
-
#puts timestep.class
|
|
600
|
-
#puts variable_name.class
|
|
601
|
-
#puts key_value.class
|
|
602
|
-
key_value = key_value.upcase #upper cases the key_value b/c it is always uppercased in the sql file.
|
|
603
|
-
#timestep = timestep.capitalize #capitalize the timestep b/c it is always capitalized in the sql file
|
|
604
|
-
#timestep = timestep.split(" ").each{|word| word.capitalize!}.join(" ")
|
|
605
|
-
#returns an array of all keyValues matching the variable name, envPeriod, and reportingFrequency
|
|
606
|
-
#we'll use this to check if the query will work before we send it.
|
|
607
|
-
puts "*#{env_period}*#{timestep}*#{variable_name}"
|
|
608
|
-
time_series_array = []
|
|
609
|
-
puts env_period.class
|
|
610
|
-
if env_period.nil?
|
|
611
|
-
|
|
612
|
-
time_series_array = [nil]
|
|
613
|
-
return time_series_array
|
|
614
|
-
end
|
|
615
|
-
possible_env_periods = openstudio_sql_file.availableEnvPeriods()
|
|
616
|
-
if possible_env_periods.nil?
|
|
617
|
-
time_series_array = [nil]
|
|
618
|
-
return time_series_array
|
|
619
|
-
end
|
|
620
|
-
possible_timesteps = openstudio_sql_file.availableReportingFrequencies(env_period)
|
|
621
|
-
if possible_timesteps.nil?
|
|
622
|
-
time_series_array = [nil]
|
|
623
|
-
return time_series_array
|
|
624
|
-
end
|
|
625
|
-
possible_variable_names = openstudio_sql_file.availableVariableNames(env_period,timestep)
|
|
626
|
-
if possible_variable_names.nil?
|
|
627
|
-
time_series_array = [nil]
|
|
628
|
-
return time_series_array
|
|
629
|
-
end
|
|
630
|
-
possible_key_values = openstudio_sql_file.availableKeyValues(env_period,timestep,variable_name)
|
|
631
|
-
if possible_key_values.nil?
|
|
632
|
-
time_series_array = [nil]
|
|
633
|
-
return time_series_array
|
|
634
|
-
end
|
|
635
|
-
|
|
636
|
-
if possible_key_values.include? key_value and
|
|
637
|
-
possible_variable_names.include? variable_name and
|
|
638
|
-
possible_env_periods.include? env_period and
|
|
639
|
-
possible_timesteps.include? timestep
|
|
640
|
-
#the query is valid
|
|
641
|
-
time_series = openstudio_sql_file.timeSeries(env_period, timestep, variable_name, key_value)
|
|
642
|
-
if time_series #checks to see if time_series exists
|
|
643
|
-
time_series = time_series.get.values
|
|
644
|
-
debug_puts " #{key_value} time series length = #{time_series.size}"
|
|
645
|
-
for i in 0..(time_series.size - 1)
|
|
646
|
-
#puts "#{i.to_s} -- #{time_series[i]}"
|
|
647
|
-
time_series_array << time_series[i]
|
|
648
|
-
end
|
|
649
|
-
end
|
|
650
|
-
else
|
|
651
|
-
#do this if the query is not valid. The comments might help troubleshoot.
|
|
652
|
-
time_series_array = [nil]
|
|
653
|
-
debug_puts "***The pieces below do NOT make a valid query***"
|
|
654
|
-
debug_puts " *#{key_value}* - this key value might not exist for the variable you are looking for"
|
|
655
|
-
debug_puts " *#{timestep}* - this value should be Hourly, Monthly, Zone Timestep, HVAC System Timestep, etc"
|
|
656
|
-
debug_puts " *#{variable_name}* - every word should be capitalized EG: Refrigeration System Total Compressor Electric Energy "
|
|
657
|
-
debug_puts " *#{env_period}* - you can get an array of all the valid env periods by using the sql_file.availableEnvPeriods() method "
|
|
658
|
-
debug_puts " Possible key values: #{possible_key_values}"
|
|
659
|
-
debug_puts " Possible Variable Names: #{possible_variable_names}"
|
|
660
|
-
debug_puts " Possible run periods: #{possible_env_periods}"
|
|
661
|
-
debug_puts " Possible timesteps: #{possible_timesteps}"
|
|
662
|
-
end
|
|
663
|
-
return time_series_array
|
|
664
|
-
end
|
|
665
|
-
|
|
666
|
-
#gets the average of the numbers in an array
|
|
667
|
-
def non_zero_array_average(arr)
|
|
668
|
-
debug_puts "average of the entire array = #{arr.inject{ |sum, el| sum + el }.to_f / arr.size}"
|
|
669
|
-
arr.delete(0)
|
|
670
|
-
debug_puts "average of the non-zero numbers in the array = #{arr.inject{ |sum, el| sum + el }.to_f / arr.size}"
|
|
671
|
-
return arr.inject{ |sum, el| sum + el }.to_f / arr.size
|
|
672
|
-
end
|
|
673
|
-
|
|
674
|
-
#method for converting from IP to SI if you know the strings of the input and the output
|
|
675
|
-
def ip_to_si(number, ip_unit_string, si_unit_string)
|
|
676
|
-
ip_unit = OpenStudio::createUnit(ip_unit_string, "IP".to_UnitSystem).get
|
|
677
|
-
si_unit = OpenStudio::createUnit(si_unit_string, "SI".to_UnitSystem).get
|
|
678
|
-
#puts "#{ip_unit} --> #{si_unit}"
|
|
679
|
-
ip_quantity = OpenStudio::Quantity.new(number, ip_unit)
|
|
680
|
-
si_quantity = OpenStudio::convert(ip_quantity, si_unit).get
|
|
681
|
-
#puts "#{ip_quantity} = #{si_quantity}"
|
|
682
|
-
return si_quantity.value
|
|
683
|
-
end
|
|
684
|
-
|
|
685
|
-
def self.compile_qaqc_results(output_folder)
|
|
686
|
-
full_json = []
|
|
687
|
-
Dir.foreach("#{output_folder}") do |folder|
|
|
688
|
-
next if folder == '.' or folder == '..'
|
|
689
|
-
Dir.glob("#{output_folder}/#{folder}/qaqc.json") { |item|
|
|
690
|
-
puts "Reading #{output_folder}/#{folder}/qaqc.json"
|
|
691
|
-
json = JSON.parse(File.read(item))
|
|
692
|
-
json['eplusout_err']['warnings'] = json['eplusout_err']['warnings'].size
|
|
693
|
-
json['eplusout_err']['severe'] = json['eplusout_err']['warnings'].size
|
|
694
|
-
json['eplusout_err']['fatal'] = json['eplusout_err']['warnings'].size
|
|
695
|
-
json['run_uuid'] = SecureRandom.uuid
|
|
696
|
-
bldg = json['building']['name'].split('-')
|
|
697
|
-
json['building_type'] = bldg[1]
|
|
698
|
-
json['template'] = bldg[0]
|
|
699
|
-
full_json << json
|
|
700
|
-
}
|
|
701
|
-
end
|
|
702
|
-
File.open("#{output_folder}/../RESULTS-#{Time.now.strftime("%m-%d-%Y")}.json", 'w') {|f| f.write(JSON.pretty_generate(full_json)) }
|
|
703
|
-
end
|
|
704
|
-
|
|
705
150
|
def self.compare_osm_files(model_true, model_compare)
|
|
706
151
|
only_model_true = [] # objects only found in the true model
|
|
707
152
|
only_model_compare = [] # objects only found in the compare model
|
|
@@ -837,438 +282,90 @@ module BTAP
|
|
|
837
282
|
diffs << "For #{true_object.iddObject.name} called '#{true_object.name}' field '#{field_name}': before model = #{true_value}, after model = #{compare_value}"
|
|
838
283
|
|
|
839
284
|
end
|
|
840
|
-
|
|
841
285
|
end
|
|
842
286
|
|
|
843
287
|
return diffs
|
|
844
288
|
end
|
|
845
289
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
# {8adb3faa-8e6a-48e3-bd73-ba6a02154b02}, !- Handle
|
|
856
|
-
# 1/2IN Gypsum, !- Name
|
|
857
|
-
# Smooth, !- Roughness
|
|
858
|
-
# 0.0127, !- Thickness {m}
|
|
859
|
-
# 0.16, !- Conductivity {W/m-K}
|
|
860
|
-
# 784.9, !- Density {kg/m3}
|
|
861
|
-
# 830.000000000001, !- Specific Heat {J/kg-K}
|
|
862
|
-
# 0.9, !- Thermal Absorptance
|
|
863
|
-
# 0.4, !- Solar Absorptance
|
|
864
|
-
# 0.4; !- Visible Absorptance
|
|
865
|
-
# ===================================================================
|
|
866
|
-
#
|
|
867
|
-
# ===================================================================
|
|
868
|
-
# {
|
|
869
|
-
# "Handle": "{8adb3faa-8e6a-48e3-bd73-ba6a02154b02}",
|
|
870
|
-
# "Name": "1/2IN Gypsum",
|
|
871
|
-
# "Roughness": "Smooth",
|
|
872
|
-
# "Thickness {m}": "0.0127",
|
|
873
|
-
# "Conductivity {W/m-K}": "0.16",
|
|
874
|
-
# "Density {kg/m3}": "784.9",
|
|
875
|
-
# "Specific Heat {J/kg-K}": "830.000000000001",
|
|
876
|
-
# "Thermal Absorptance": "0.9",
|
|
877
|
-
# "Solar Absorptance": "0.4",
|
|
878
|
-
# "Visible Absorptance": "0.4"
|
|
879
|
-
# },
|
|
880
|
-
# ===================================================================
|
|
881
|
-
def self.idf_to_h(obj)
|
|
882
|
-
# split idf object by line
|
|
883
|
-
obj_string = obj.to_s.split("\n")
|
|
884
|
-
new_obj_hash = {}
|
|
885
|
-
|
|
886
|
-
# itterate through each line and split the value and field
|
|
887
|
-
# and assign it to the hash
|
|
888
|
-
obj_string.each_with_index {|line,i|
|
|
889
|
-
next if i == 0
|
|
890
|
-
line.gsub!(/(\,|\;)/, '') # remove commas and semi-colons
|
|
891
|
-
line.strip! # remove whitespace at the end and the beginning of the string
|
|
892
|
-
v,k = line.split(/\s*\!\-\s+/) # split the line into at the string '!-' including the spaces before and after
|
|
893
|
-
new_obj_hash[k] = v
|
|
894
|
-
}
|
|
895
|
-
new_obj_hash
|
|
896
|
-
end
|
|
897
|
-
|
|
898
|
-
# This method uses idf_to_h(obj) method, but deletes the fields named 'Handle' and 'Name'
|
|
899
|
-
#
|
|
900
|
-
# @author Padmassun Rajakareyar
|
|
901
|
-
# @param obj [OpenStudio::ModelObject]
|
|
902
|
-
# @return new_obj_hash [Hash] idf object converted to hash.
|
|
903
|
-
# @example
|
|
904
|
-
# Converts the following IDF (openstudio::ModelObject) to
|
|
905
|
-
# ==================================================================
|
|
906
|
-
# OS:Material,
|
|
907
|
-
# {8adb3faa-8e6a-48e3-bd73-ba6a02154b02}, !- Handle
|
|
908
|
-
# 1/2IN Gypsum, !- Name
|
|
909
|
-
# Smooth, !- Roughness
|
|
910
|
-
# 0.0127, !- Thickness {m}
|
|
911
|
-
# 0.16, !- Conductivity {W/m-K}
|
|
912
|
-
# 784.9, !- Density {kg/m3}
|
|
913
|
-
# 830.000000000001, !- Specific Heat {J/kg-K}
|
|
914
|
-
# 0.9, !- Thermal Absorptance
|
|
915
|
-
# 0.4, !- Solar Absorptance
|
|
916
|
-
# 0.4; !- Visible Absorptance
|
|
917
|
-
# ===================================================================
|
|
918
|
-
#
|
|
919
|
-
# ===================================================================
|
|
920
|
-
# {
|
|
921
|
-
# "Roughness": "Smooth",
|
|
922
|
-
# "Thickness {m}": "0.0127",
|
|
923
|
-
# "Conductivity {W/m-K}": "0.16",
|
|
924
|
-
# "Density {kg/m3}": "784.9",
|
|
925
|
-
# "Specific Heat {J/kg-K}": "830.000000000001",
|
|
926
|
-
# "Thermal Absorptance": "0.9",
|
|
927
|
-
# "Solar Absorptance": "0.4",
|
|
928
|
-
# "Visible Absorptance": "0.4"
|
|
929
|
-
# },
|
|
930
|
-
# ===================================================================
|
|
931
|
-
def self.idf_to_h_clean(obj)
|
|
932
|
-
# converts the idf object to hash
|
|
933
|
-
idf_hash = idf_to_h(obj)
|
|
934
|
-
|
|
935
|
-
# remove the field named `Handle` and `Name` from the idf_hash
|
|
936
|
-
idf_hash.delete("Handle") if idf_hash.key?("Handle")
|
|
937
|
-
idf_hash.delete("Handle".to_sym) if idf_hash.key?("Handle".to_sym)
|
|
938
|
-
|
|
939
|
-
idf_hash.delete("Name") if idf_hash.key?("Name")
|
|
940
|
-
idf_hash.delete("Name".to_sym) if idf_hash.key?("Name".to_sym)
|
|
941
|
-
|
|
942
|
-
# Loop through idf_hash and delete any field that matched the UUID regex
|
|
943
|
-
# idf_hash.each {|k,v|
|
|
944
|
-
# idf_hash.delete(k) if /^\{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\}$/.match(v)
|
|
945
|
-
# }
|
|
946
|
-
# idf_hash
|
|
947
|
-
end
|
|
948
|
-
|
|
949
|
-
# This method will group similar objects under a key that has all the
|
|
950
|
-
# values of an IDF object which excludes Handles and Names.
|
|
951
|
-
# NOTE: The objexts grouped should have the fields in the same order.
|
|
952
|
-
# If not, then it would not be consired as a duplicate.
|
|
953
|
-
#
|
|
954
|
-
# @author Padmassun Rajakareyar
|
|
955
|
-
# @param obj_array [Array] An array of idf_hash. Each idf_hash was created by idf_to_h_clean or idf_to_h method
|
|
956
|
-
# @return grouped_objs [Hash] grouped idf_hash based on values within the obj_array.
|
|
957
|
-
# @example Output:
|
|
958
|
-
# "[\"Smooth\", \"0.216479986995276\", \"0.9\", \"0.7\", \"0.8\"]": [
|
|
959
|
-
# {
|
|
960
|
-
# "Handle": "{a7c45cf6-166d-48a6-9750-efb5c3386d91}",
|
|
961
|
-
# "Name": "Typical Carpet Pad",
|
|
962
|
-
# "Roughness": "Smooth",
|
|
963
|
-
# "Thermal Resistance {m2-K/W}": "0.216479986995276",
|
|
964
|
-
# "Thermal Absorptance": "0.9",
|
|
965
|
-
# "Solar Absorptance": "0.7",
|
|
966
|
-
# "Visible Absorptance": "0.8"
|
|
967
|
-
# },
|
|
968
|
-
# {
|
|
969
|
-
# "Handle": "{9873fb84-bfaf-459d-8b70-2c3f5722bda9}",
|
|
970
|
-
# "Name": "Typical Carpet Pad 1",
|
|
971
|
-
# "Roughness": "Smooth",
|
|
972
|
-
# "Thermal Resistance {m2-K/W}": "0.216479986995276",
|
|
973
|
-
# "Thermal Absorptance": "0.9",
|
|
974
|
-
# "Solar Absorptance": "0.7",
|
|
975
|
-
# "Visible Absorptance": "0.8"
|
|
976
|
-
# },
|
|
977
|
-
# {
|
|
978
|
-
# "Handle": "{63a12315-1de4-453a-b154-e6e6e9871be2}",
|
|
979
|
-
# "Name": "Typical Carpet Pad 4",
|
|
980
|
-
# "Roughness": "Smooth",
|
|
981
|
-
# "Thermal Resistance {m2-K/W}": "0.216479986995276",
|
|
982
|
-
# "Thermal Absorptance": "0.9",
|
|
983
|
-
# "Solar Absorptance": "0.7",
|
|
984
|
-
# "Visible Absorptance": "0.8"
|
|
985
|
-
# }
|
|
986
|
-
# ],
|
|
987
|
-
def self.group_similar_objects(obj_array)
|
|
988
|
-
# group [objects] by values except Handles and Name
|
|
989
|
-
grouped_objs = obj_array.group_by{ |idf_hash|
|
|
990
|
-
out = []
|
|
991
|
-
# skip Handle and Name keys
|
|
992
|
-
#
|
|
993
|
-
# ideally the `keys` of the `idf_hash` should be sorted,
|
|
994
|
-
# but I'll leave it alone for now
|
|
995
|
-
idf_hash.each {|key, val|
|
|
996
|
-
next if key == "Handle"
|
|
997
|
-
next if key == "Name"
|
|
998
|
-
out << val # push all the values into an array. This becomes the key of the hash that contains the duplicate objects
|
|
999
|
-
}
|
|
1000
|
-
out
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
# Sort the grouped [objects] by Name.
|
|
1004
|
-
# This is doen such that the first object will always have the smaller name
|
|
1005
|
-
grouped_objs.each {|key, dup_array|
|
|
1006
|
-
dup_array.sort_by{|idf_hash|
|
|
1007
|
-
# puts idf_hash
|
|
1008
|
-
idf_hash['Name'] # Sort by Name
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
return grouped_objs
|
|
1012
|
-
end
|
|
1013
|
-
|
|
1014
|
-
# Replace the UUID of the duplicate material, unless it contains ", !- Handle"
|
|
1015
|
-
# This is done so after the model has been written to the disk, It can be read and
|
|
1016
|
-
# the duplicate materials can be removed safely.
|
|
1017
|
-
#
|
|
1018
|
-
# @author Padmassun Rajakareyar
|
|
1019
|
-
# @param model [OpenStudio::Model]
|
|
1020
|
-
# @param grouped_objs [Hash] Output provided by group_similar_objects()
|
|
1021
|
-
# @return model_string [String] An OpenStudio::Model object converted to a string
|
|
1022
|
-
def self.replace_duplicate_obj_handles(model, grouped_objs)
|
|
1023
|
-
model_string = model.to_s # convert the OS:Model into a String
|
|
1024
|
-
grouped_objs.each {|key, dup_array|
|
|
1025
|
-
dup_array.each_with_index {|idf_hash, index |
|
|
1026
|
-
next if index == 0 # skipping index 0, because it has the shortest name and considered as the original
|
|
1027
|
-
|
|
1028
|
-
# givn that the idf_hash['Handle'] => '{8c88931b-e19d-479b-ac71-138d18c97cc9}'
|
|
1029
|
-
# The following regex matches "{8c88931b-e19d-479b-ac71-138d18c97cc9}" in the following line
|
|
1030
|
-
# {8c88931b-e19d-479b-ac71-138d18c97cc9}, !- Layer 1
|
|
1031
|
-
#
|
|
1032
|
-
# but matches nothing if the line has the keyword '!- Handle' in it e.g.
|
|
1033
|
-
# {8c88931b-e19d-479b-ac71-138d18c97cc9}, !- Handle
|
|
1034
|
-
replace_regex = idf_hash['Handle'].to_s.gsub('{', '\{'). # escape brackets
|
|
1035
|
-
gsub('}', '\}'). # escape brackets
|
|
1036
|
-
gsub('-', '\-') + # escape dashes
|
|
1037
|
-
'(?!.*?\!\-(\s)*Handle)' # making sure the matched handle is not part of the line that contains the substring '!- Handle'
|
|
1038
|
-
replace_regex = Regexp.new(replace_regex)
|
|
1039
|
-
# p replace_regex
|
|
1040
|
-
|
|
1041
|
-
# replace duplicate handles with the handle found at index 0
|
|
1042
|
-
model_string.gsub!(replace_regex, dup_array[0]['Handle'])# {|match| puts match; dup_array[0]['Handle']}
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
return model_string
|
|
1046
|
-
end
|
|
1047
|
-
|
|
1048
|
-
# This method gets the model string, writes it in a temporary location, reads it, and
|
|
1049
|
-
# converts it to an OpenStudio Model using a VersionTranslater
|
|
1050
|
-
#
|
|
1051
|
-
# @author Padmassun Rajakareyar
|
|
1052
|
-
# @param model_string [String] An OpenStudio::Model object converted to a string
|
|
1053
|
-
# @return model [OpenStudio::Model]
|
|
1054
|
-
def self.get_OS_Model_from_string(model_string)
|
|
1055
|
-
require 'securerandom'
|
|
1056
|
-
require 'fileutils'
|
|
1057
|
-
# make temerorary directory called `temp` to store the osm file
|
|
1058
|
-
FileUtils.mkdir_p(File.join('.', 'temp'))
|
|
1059
|
-
# Using SecureRandom to generate the UUID to keep the osm file unique
|
|
1060
|
-
temp_filename =File.join('.', 'temp',SecureRandom.uuid.to_s + '.osm')
|
|
1061
|
-
# write the model string as a file
|
|
1062
|
-
File.open(temp_filename, 'w') { |file| file.write(model_string) }
|
|
1063
|
-
# use a VersionTranslator to read the osm model
|
|
1064
|
-
translator = OpenStudio::OSVersion::VersionTranslator.new
|
|
1065
|
-
path = OpenStudio::Path.new(temp_filename)
|
|
1066
|
-
# read the model
|
|
1067
|
-
model = translator.loadModel(path)
|
|
1068
|
-
model = model.get
|
|
1069
|
-
# remove the temporary model
|
|
1070
|
-
FileUtils.rm(temp_filename)
|
|
1071
|
-
return model
|
|
1072
|
-
end
|
|
1073
|
-
|
|
1074
|
-
# Eleminates duplicate ModelObjects of the given ModelObject type,
|
|
1075
|
-
# based on the values of each fields within the ModelObject
|
|
1076
|
-
#
|
|
1077
|
-
# @author Padmassun Rajakareyar
|
|
1078
|
-
# @param model [OpenStudio::Model]
|
|
1079
|
-
# @param model_obj_type [String] ModelObject type e.g. "OS:Material"
|
|
1080
|
-
# @return new_model [OpenStudio::Model] Returns new model (OS:Model) if the changes were made.
|
|
1081
|
-
# or else returns the old model if no changes were made.
|
|
1082
|
-
def self.eleminate_duplicate_objs(model, model_obj_type) # = "OS:Material")
|
|
1083
|
-
model_objs_json = {}
|
|
1084
|
-
|
|
1085
|
-
# convert each of the ModelObjectas a hash for easy parsing
|
|
1086
|
-
model.getModelObjects.sort.each {|obj|
|
|
1087
|
-
# hsh = idf_to_h_clean(obj) # stores the idf converted to hash, without UUIDs and Name field
|
|
1088
|
-
|
|
1089
|
-
# create a hash containing all the model objects sorted by the name of the model object.
|
|
1090
|
-
# e.g the `OS:Construction` ModelObject type will e placed within the `OS:Construction` key, and
|
|
1091
|
-
# each ModelObject has been converted to an idf_hash and pushed into the array with the appropriate key.
|
|
1092
|
-
(model_objs_json[obj.iddObject.name.to_s] ||= []) << idf_to_h(obj) # unless hsh.empty?
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
# isolate a single ModelObject type specified by the model_obj_type variable
|
|
1096
|
-
mat_array = model_objs_json[model_obj_type]
|
|
1097
|
-
if mat_array.nil? # return the old model if model_obj_type is not found
|
|
1098
|
-
puts "Skipping because ModelObject of type [#{model_obj_type}] was not found"
|
|
1099
|
-
return model
|
|
290
|
+
def self.set_all_epw_paths(osm_data)
|
|
291
|
+
osm_data.each do |hash|
|
|
292
|
+
hash.each do |key, value|
|
|
293
|
+
#Check if value is in a epw file path format like this "USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw"
|
|
294
|
+
if value.match?(/.*\.epw/)
|
|
295
|
+
# Get name of file without path from value
|
|
296
|
+
hash[key] = File.basename(value)
|
|
297
|
+
end
|
|
298
|
+
end
|
|
1100
299
|
end
|
|
1101
|
-
|
|
1102
|
-
grouped_objs = group_similar_objects(mat_array)
|
|
1103
|
-
# replace handles of duplicate objects
|
|
1104
|
-
model_string = replace_duplicate_obj_handles(model, grouped_objs)
|
|
1105
|
-
# write the model string to a file and read it as a model
|
|
1106
|
-
new_model = get_OS_Model_from_string(model_string)
|
|
300
|
+
end
|
|
1107
301
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
puts "Skipping ModelObject of type [#{model_obj_type}] With data [#{object.inspect}] does not have a field called 'Name'"
|
|
1115
|
-
next
|
|
1116
|
-
end
|
|
1117
|
-
name = object['Name']
|
|
1118
|
-
# puts "object: [#{object}]"
|
|
1119
|
-
# puts "object['Name']: [#{object['Name']}]"
|
|
1120
|
-
# get the object to delete by name
|
|
1121
|
-
obj_to_delete = new_model.getModelObjectByName(name)
|
|
1122
|
-
if obj_to_delete.empty? # check if the object to be deleted is initialized (or present within the new model)
|
|
1123
|
-
puts "ModelObject of type [#{model_obj_type}] with name [#{object['Name']}] does not exist in new model"
|
|
1124
|
-
else
|
|
1125
|
-
puts "ModelObject of type [#{model_obj_type}] with name [#{object['Name']}] was deleted"
|
|
1126
|
-
obj_to_delete = obj_to_delete.get # get the modelObject if it is initialized
|
|
1127
|
-
obj_to_delete.remove # remove object form the model
|
|
302
|
+
def self.set_all_data_time(osm_data)
|
|
303
|
+
osm_data.each do |hash|
|
|
304
|
+
hash.each do |key, value|
|
|
305
|
+
#Check if value is in a date-time format like this "2024-10-17 01:19:35 UTC"
|
|
306
|
+
if value.match?(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC/)
|
|
307
|
+
hash[key] = "2024-01-01 01:00:00 UTC"
|
|
1128
308
|
end
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
# File.open('./models/after.osm', 'w') { |file| file.write(new_model.to_s) }
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
# File.open("./models/grp_#{model_obj_type}.json", 'w') { |file| file.write(JSON.pretty_generate(grouped_objs)) }
|
|
1135
|
-
|
|
1136
|
-
return new_model
|
|
1137
|
-
# File.open('./models/after.osm', 'w') { |file| file.write(model_string) }
|
|
1138
|
-
# puts("\n\n\n" + "=="*10 + "\n\n")
|
|
1139
|
-
# puts JSON.pretty_generate(grouped_mats)
|
|
309
|
+
end
|
|
310
|
+
end
|
|
1140
311
|
end
|
|
1141
312
|
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
# @return new_model [OpenStudio::Model] Returns new model after removing the
|
|
1159
|
-
# duplicate ModelObjects atated abioe.
|
|
1160
|
-
def self.remove_duplicate_materials_and_constructions(model)
|
|
1161
|
-
old_number_of_objects = model.getModelObjects.length
|
|
1162
|
-
new_model = model
|
|
1163
|
-
# eleminate dplicate Material objects
|
|
1164
|
-
obj_types = [
|
|
1165
|
-
"OS:Material",
|
|
1166
|
-
"OS:Material:NoMass",
|
|
1167
|
-
"OS:WindowMaterial:SimpleGlazingSystem",
|
|
1168
|
-
"OS:WindowMaterial:Glazing",
|
|
1169
|
-
"OS:WindowMaterial:Gas",
|
|
1170
|
-
"OS:StandardsInformation:Material"
|
|
1171
|
-
]
|
|
1172
|
-
|
|
1173
|
-
obj_types.each {|model_obj_type|
|
|
1174
|
-
new_model = eleminate_duplicate_objs(new_model, model_obj_type)
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
# eleminate dplicate Construction objects
|
|
1178
|
-
obj_types = [
|
|
1179
|
-
"OS:Construction",
|
|
1180
|
-
"OS:DefaultSurfaceConstructions",
|
|
1181
|
-
"OS:DefaultSubSurfaceConstructions",
|
|
1182
|
-
"OS:DefaultConstructionSet",
|
|
1183
|
-
"OS:StandardsInformation:Construction",
|
|
1184
|
-
]
|
|
1185
|
-
obj_types.each {|model_obj_type|
|
|
1186
|
-
new_model = eleminate_duplicate_objs(new_model, model_obj_type)
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
new_number_of_objects = new_model.getModelObjects.length
|
|
1190
|
-
|
|
1191
|
-
puts "Number of objects removed: #{old_number_of_objects - new_number_of_objects}"
|
|
1192
|
-
return new_model
|
|
313
|
+
def self.save_osm_file(osm_data, output_path)
|
|
314
|
+
File.open(output_path, 'w') do |file|
|
|
315
|
+
osm_data.each do |hash|
|
|
316
|
+
file.puts "#{hash["type"]},"
|
|
317
|
+
hash.each do |key, value|
|
|
318
|
+
next if key == "type"
|
|
319
|
+
is_last_key = (hash.keys - [:type]).last == key
|
|
320
|
+
if key == :id
|
|
321
|
+
file.puts " #{value},"
|
|
322
|
+
else
|
|
323
|
+
file.puts " #{value}#{is_last_key ? ';' : ','}".ljust(42) + " !- #{key}"
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
file.puts
|
|
327
|
+
end
|
|
328
|
+
end
|
|
1193
329
|
end
|
|
1194
330
|
|
|
331
|
+
def self.rename_handles(osm_data)
|
|
1195
332
|
|
|
1196
|
-
|
|
1197
|
-
|
|
333
|
+
hash = Digest::SHA1.hexdigest("fixed_seed")
|
|
334
|
+
handle_map = {}
|
|
335
|
+
new_handle_index = 0
|
|
336
|
+
object_type_index = 0
|
|
337
|
+
last_object_type = nil
|
|
1198
338
|
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
if line.start_with?('OS:')
|
|
1208
|
-
current_hash = { type: line.split(/[;,]/).first }
|
|
1209
|
-
data << current_hash
|
|
1210
|
-
elsif line.include?('!-')
|
|
1211
|
-
key, value = line.split('!-').map(&:strip)
|
|
1212
|
-
current_hash[value] = key.chomp(',;').strip
|
|
339
|
+
osm_data.each do |hash|
|
|
340
|
+
if hash['Handle']
|
|
341
|
+
#puts hash
|
|
342
|
+
#puts "#{hash['type']} == #{last_object_type}"
|
|
343
|
+
if hash['type'] != last_object_type
|
|
344
|
+
last_object_type = hash['type']
|
|
345
|
+
object_type_index += 1
|
|
346
|
+
new_handle_index = 1
|
|
1213
347
|
else
|
|
1214
|
-
|
|
348
|
+
new_handle_index += 1
|
|
1215
349
|
end
|
|
350
|
+
|
|
351
|
+
# create a formatted string that is 12 charecters created with new_handle_index, but padded with 0s on the left.
|
|
352
|
+
formatted_index = new_handle_index.to_s.rjust(12, '0')
|
|
353
|
+
formatted_index_object_type = object_type_index.to_s.rjust(4, '0')
|
|
354
|
+
new_handle = "{00000000-0000-0000-#{formatted_index_object_type}-#{formatted_index}}"
|
|
355
|
+
handle_map[hash['Handle']] = new_handle
|
|
356
|
+
hash['Handle'] = new_handle
|
|
1216
357
|
end
|
|
1217
|
-
|
|
1218
|
-
data
|
|
1219
358
|
end
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
hash.keys.each do |key|
|
|
1223
|
-
new_key = key.to_s.sub(/[;,]$/, '')
|
|
1224
|
-
hash[new_key] = hash.delete(key)
|
|
1225
|
-
end
|
|
1226
|
-
|
|
359
|
+
|
|
360
|
+
osm_data.each do |hash|
|
|
1227
361
|
hash.each do |key, value|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
362
|
+
if handle_map.key?(value)
|
|
363
|
+
hash[key] = handle_map[value]
|
|
364
|
+
end
|
|
1231
365
|
end
|
|
1232
366
|
end
|
|
1233
|
-
|
|
1234
|
-
def self.rename_handles(osm_data)
|
|
1235
|
-
|
|
1236
|
-
hash = Digest::SHA1.hexdigest("fixed_seed")
|
|
1237
|
-
handle_map = {}
|
|
1238
|
-
new_handle_index = 0
|
|
1239
|
-
object_type_index = 0
|
|
1240
|
-
last_object_type = nil
|
|
1241
|
-
|
|
1242
|
-
osm_data.each do |hash|
|
|
1243
|
-
if hash['Handle']
|
|
1244
|
-
#puts hash
|
|
1245
|
-
#puts "#{hash['type']} == #{last_object_type}"
|
|
1246
|
-
if hash['type'] != last_object_type
|
|
1247
|
-
last_object_type = hash['type']
|
|
1248
|
-
object_type_index += 1
|
|
1249
|
-
new_handle_index = 1
|
|
1250
|
-
else
|
|
1251
|
-
new_handle_index += 1
|
|
1252
|
-
end
|
|
1253
367
|
|
|
1254
|
-
|
|
1255
|
-
formatted_index = new_handle_index.to_s.rjust(12, '0')
|
|
1256
|
-
formatted_index_object_type = object_type_index.to_s.rjust(4, '0')
|
|
1257
|
-
new_handle = "{00000000-0000-0000-#{formatted_index_object_type}-#{formatted_index}}"
|
|
1258
|
-
handle_map[hash['Handle']] = new_handle
|
|
1259
|
-
hash['Handle'] = new_handle
|
|
1260
|
-
end
|
|
1261
|
-
end
|
|
1262
|
-
|
|
1263
|
-
osm_data.each do |hash|
|
|
1264
|
-
hash.each do |key, value|
|
|
1265
|
-
if handle_map.key?(value)
|
|
1266
|
-
hash[key] = handle_map[value]
|
|
1267
|
-
end
|
|
1268
|
-
end
|
|
1269
|
-
end
|
|
1270
|
-
|
|
1271
|
-
# Go through all hashes in osm_data and if the "Name" field value fits this pattern {9386c18d-e70a-447e-8b69-9a0a39fd8f06} replace it with an incremented value.
|
|
368
|
+
# Go through all hashes in osm_data and if the "Name" field value fits this pattern {9386c18d-e70a-447e-8b69-9a0a39fd8f06} replace it with an incremented value.
|
|
1272
369
|
name_map = {}
|
|
1273
370
|
new_name_index = 0
|
|
1274
371
|
osm_data.each do |hash|
|
|
@@ -1288,58 +385,40 @@ module BTAP
|
|
|
1288
385
|
end
|
|
1289
386
|
end
|
|
1290
387
|
end
|
|
388
|
+
end
|
|
1291
389
|
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
def self.save_osm_file(osm_data, output_path)
|
|
1299
|
-
File.open(output_path, 'w') do |file|
|
|
1300
|
-
osm_data.each do |hash|
|
|
1301
|
-
file.puts "#{hash["type"]},"
|
|
1302
|
-
hash.each do |key, value|
|
|
1303
|
-
next if key == "type"
|
|
1304
|
-
is_last_key = (hash.keys - [:type]).last == key
|
|
1305
|
-
if key == :id
|
|
1306
|
-
file.puts " #{value},"
|
|
1307
|
-
else
|
|
1308
|
-
file.puts " #{value}#{is_last_key ? ';' : ','}".ljust(42) + " !- #{key}"
|
|
1309
|
-
end
|
|
1310
|
-
end
|
|
1311
|
-
file.puts
|
|
1312
|
-
end
|
|
1313
|
-
end
|
|
390
|
+
def self.remove_special_characters(hash)
|
|
391
|
+
hash.keys.each do |key|
|
|
392
|
+
new_key = key.to_s.sub(/[;,]$/, '')
|
|
393
|
+
hash[new_key] = hash.delete(key)
|
|
1314
394
|
end
|
|
1315
395
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
#Check if value is in a date-time format like this "2024-10-17 01:19:35 UTC"
|
|
1320
|
-
if value.match?(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC/)
|
|
1321
|
-
hash[key] = "2024-01-01 01:00:00 UTC"
|
|
1322
|
-
end
|
|
1323
|
-
end
|
|
1324
|
-
end
|
|
396
|
+
hash.each do |key, value|
|
|
397
|
+
next unless value.is_a?(String)
|
|
398
|
+
hash[key] = value.gsub(/[;,]$/, '')
|
|
1325
399
|
end
|
|
400
|
+
end
|
|
1326
401
|
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
402
|
+
def self.clean_osm_file(file_path:, output_path:)
|
|
403
|
+
osm_data = []
|
|
404
|
+
current_hash = {}
|
|
405
|
+
File.foreach(file_path) do |line|
|
|
406
|
+
line.strip!
|
|
407
|
+
next if line.empty?
|
|
408
|
+
if line.start_with?('OS:')
|
|
409
|
+
current_hash = { type: line.split(/[;,]/).first }
|
|
410
|
+
osm_data << current_hash
|
|
411
|
+
elsif line.include?('!-')
|
|
412
|
+
key, value = line.split('!-').map(&:strip)
|
|
413
|
+
current_hash[value] = key.chomp(',;').strip
|
|
414
|
+
elsif line.include?('! ') # for lines that have '! ' but not '!-'
|
|
415
|
+
key, value = line.split('! ').map(&:strip)
|
|
416
|
+
current_hash[value] = key.chomp(',;').strip
|
|
417
|
+
else
|
|
418
|
+
current_hash[:id] ||= line.chomp(',;').strip
|
|
1336
419
|
end
|
|
1337
420
|
end
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
osm_data = read_osm_file(file_path)
|
|
1341
421
|
osm_data.sort_by! { |hash| [hash[:type], hash['Name'] || ''] }
|
|
1342
|
-
|
|
1343
422
|
handles = osm_data.map { |hash| hash['Handle'] }.compact
|
|
1344
423
|
duplicates = handles.select { |e| handles.count(e) > 1 }
|
|
1345
424
|
raise "Duplicate handles found: #{duplicates.uniq}" if duplicates.any?
|
|
@@ -1348,19 +427,22 @@ module BTAP
|
|
|
1348
427
|
set_all_data_time(osm_data)
|
|
1349
428
|
set_all_epw_paths(osm_data)
|
|
1350
429
|
rename_handles(osm_data)
|
|
1351
|
-
save_osm_file(osm_data, output_path)
|
|
1352
|
-
end
|
|
1353
430
|
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
431
|
+
File.open(output_path, 'w') do |file|
|
|
432
|
+
osm_data.each do |hash|
|
|
433
|
+
file.puts "#{hash["type"]},"
|
|
434
|
+
hash.each do |key, value|
|
|
435
|
+
next if key == "type"
|
|
436
|
+
is_last_key = (hash.keys - [:type]).last == key
|
|
437
|
+
if key == :id
|
|
438
|
+
file.puts " #{value},"
|
|
439
|
+
else
|
|
440
|
+
file.puts " #{value}#{is_last_key ? ';' : ','}".ljust(42) + " !- #{key}"
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
file.puts
|
|
444
|
+
end
|
|
1363
445
|
end
|
|
1364
446
|
end
|
|
1365
|
-
end
|
|
1366
|
-
end
|
|
447
|
+
end
|
|
448
|
+
end
|