lib/openstudio-standards/btap/fileio.rb in openstudio-standards-0.2.2 vs lib/openstudio-standards/btap/fileio.rb in openstudio-standards-0.2.3
- old
+ new
@@ -16,11 +16,11 @@
# * License along with this library; if not, write to the Free Software
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
# **********************************************************************/
-require "#{File.dirname(__FILE__)}/btap"
+
require 'fileutils'
require 'csv'
require 'securerandom'
@@ -75,22 +75,22 @@
# @param folder [String} the path to the folder to be scanned.
# @param ext [String] the file extension name, ex ".epw"
def self.get_find_files_from_folder_by_extension(folder, ext)
Dir.glob("#{folder}/**/*#{ext}")
end
-
+
def self.delete_files_in_folder_by_extention(folder,ext)
BTAP::FileIO::get_find_files_from_folder_by_extension(folder, ext).each do |file|
FileUtils.rm(file)
#puts "#{file} deleted."
end
end
-
+
def self.find_file_in_folder_by_filename(folder,filename)
Dir.glob("#{folder}/**/*#{filename}")
end
-
+
def self.fix_url_to_path(url_string)
if url_string =~/\/([a-zA-Z]:.*)/
return $1
else
return url_string
@@ -156,15 +156,15 @@
model.removeObjects(handles)
# add new file to empty model
model.addObjects( new_model.toIdfFile.objects )
BTAP::runner_register("Info", "Model name is now #{model.building.get.name}.", runner)
end
-
-
-
-
+
+
+
+
# This method loads an Openstudio file into the model.
# @author Phylroy A. Lopez
# @param filepath [String] path to the OSM file.
# @param name [String] optional model name to be set to model.
# @return [OpenStudio::Model::Model] an OpenStudio model object.
@@ -259,14 +259,14 @@
end
model.removeObjects(handles)
# add new file to empty model
model.addObjects( new_model.toIdfFile.objects )
BTAP::runner_register("Info", "Model name is now #{model.building.get.name}.", runner)
-
-
-
-
+
+
+
+
end
# This method will save the model to an osm file.
# @author Phylroy A. Lopez
# @param model
@@ -301,19 +301,19 @@
#puts # empty line break
end
}
end
-
+
def self.get_timestep_data(osm_file,sql_file,variable_name_array, env_period = nil, hourly_time_step = nil )
column_data = get_timeseries_arrays(sql, env_period, hourly_time_step, "Boiler Fan Coil Part Load Ratio")
end
-
+
def self.convert_all_eso_to_csv(in_folder,out_folder)
list_of_csv_files = Array.new
FileUtils.mkdir_p(out_folder)
osmfiles = BTAP::FileIO::get_find_files_from_folder_by_extension(in_folder,".eso")
@@ -322,11 +322,11 @@
#Run ESO Vars command must be run in folder.
root_folder = Dir.getwd()
#puts File.dirname(eso_file_path)
Dir.chdir(File.dirname(eso_file_path))
if File.exist?("eplustbl.htm")
- File.open("dummy.rvi", 'w') {|f| f.write("") }
+ File.open("dummy.rvi", 'w') {|f| f.write("") }
system("#{BTAP::SimManager::ProcessManager::find_read_vars_eso()} dummy.rvi unlimited")
#get name of run from html file.
runname = ""
@@ -345,11 +345,11 @@
#puts "copy html results to #{out_folder}/#{runname}_eplustbl.htm"
FileUtils.cp("eplustbl.htm","#{out_folder}/#{runname}_eplustbl.htm")
#puts "copy sql results to #{out_folder}/#{runname}_eplusout.sql"
FileUtils.cp("eplusout.sql","#{out_folder}/#{runname}_eplusout.sql")
-
+
list_of_csv_files << "#{out_folder}/#{runname}_eplusout.csv"
end
Dir.chdir(root_folder)
end
return list_of_csv_files
@@ -361,12 +361,12 @@
# @param file The path to the csv file.
# @param searchHash
# @return matches A Array of rows that match the searchHash. The row is a Hash itself.
def self.csv_look_up_rows(file, searchHash)
options = {
- :headers => true,
- :converters => :numeric }
+ :headers => true,
+ :converters => :numeric }
table = CSV.read( file, options )
# we'll save the matches here
matches = nil
# save a copy of the headers
matches = table.find_all do |row|
@@ -390,11 +390,11 @@
# This method will read a CSV file and return the unique values in a given column header.
# @author Phylroy Lopez
# @param file The path to the csv file.
- # @param colHeader The header name in teh csv file.
+ # @param colHeader The header name in teh csv file.
# @return matches A Array of rows that match the searchHash. The row is a Hash itself.
def self.csv_look_up_unique_col_data(file, colHeader)
column_data = Array.new
CSV.foreach( file, :headers => true ) do |row|
column_data << row[colHeader] # For each row, give me the cell that is under the colHeader column
@@ -411,14 +411,14 @@
def self.terminus_hourly_output(csv_file)
#puts "Starting Terminus output processing."
#puts "reading #{csv_file} being processed"
#reads csv file into memory.
original = CSV.read(csv_file,
- {
- :headers => true, #This flag tell the parser that there are headers.
- :converters => :numeric #This tell it to convert string data into numeric when possible.
- }
+ {
+ :headers => true, #This flag tell the parser that there are headers.
+ :converters => :numeric #This tell it to convert string data into numeric when possible.
+ }
)
#puts "done reading #{csv_file} being processed"
# We are going to collect the header names that fit a pattern. But first we need to
# create array containers to save the header name. In ruby we can use the string header names
# as the array index.
@@ -479,22 +479,22 @@
#open up a new file to save the file to..Note: This will fail it the file is open in EXCEL.
CSV.open("#{csv_file}.terminus_hourly.csv", 'w') do |csv|
#Create header row for new terminus hourly file.
csv << [
- "Date/Time",
- "water_heater_gas_rate_total",
- "water_heater_electric_rate_total",
- "water_heater_heating_rate_total",
- "cooling_coil_electric_power_total",
- "cooling_coil_total_cooling_rate_total",
- "heating_coil_air_heating_rate_total",
- "heating_coil_gas_rate_total",
- "heating_coil_electric_power_total",
- "plant_supply_heating_demand_rate_total",
- "facility_total_electrical_demand_total",
- "boiler_gas_rate_total"
+ "Date/Time",
+ "water_heater_gas_rate_total",
+ "water_heater_electric_rate_total",
+ "water_heater_heating_rate_total",
+ "cooling_coil_electric_power_total",
+ "cooling_coil_total_cooling_rate_total",
+ "heating_coil_air_heating_rate_total",
+ "heating_coil_gas_rate_total",
+ "heating_coil_electric_power_total",
+ "plant_supply_heating_demand_rate_total",
+ "facility_total_electrical_demand_total",
+ "boiler_gas_rate_total"
]
original.each do |row|
# We are now writing data to the new csv file. This is where we can manipulate the data, row by row.
# sum the headers collected above and store in specific *_total variables.
@@ -514,22 +514,22 @@
#Write the data out. Should match header row as above.
csv << [
- row["Date/Time"], #Time index is hardcoded because every file will have a "Date/Time" column header.
- water_heater_gas_rate_total,
- water_heater_electric_rate_total,
- water_heater_heating_rate_total,
- cooling_coil_electric_power_total,
- cooling_coil_total_cooling_rate_total,
- heating_coil_air_heating_rate_total,
- heating_coil_gas_rate_total,
- heating_coil_electric_power_total,
- plant_supply_heating_demand_rate_total,
- facility_total_electrical_demand_total,
- boiler_gas_rate_headers_total
+ row["Date/Time"], #Time index is hardcoded because every file will have a "Date/Time" column header.
+ water_heater_gas_rate_total,
+ water_heater_electric_rate_total,
+ water_heater_heating_rate_total,
+ cooling_coil_electric_power_total,
+ cooling_coil_total_cooling_rate_total,
+ heating_coil_air_heating_rate_total,
+ heating_coil_gas_rate_total,
+ heating_coil_electric_power_total,
+ plant_supply_heating_demand_rate_total,
+ facility_total_electrical_demand_total,
+ boiler_gas_rate_headers_total
]
end
end
#puts "Ending Terminus output processing."
end
@@ -695,11 +695,11 @@
def self.compile_qaqc_results(output_folder)
full_json = []
Dir.foreach("#{output_folder}") do |folder|
next if folder == '.' or folder == '..'
- Dir.glob("#{output_folder}/#{folder}/qaqc.json") { |item|
+ Dir.glob("#{output_folder}/#{folder}/qaqc.json") { |item|
puts "Reading #{output_folder}/#{folder}/qaqc.json"
json = JSON.parse(File.read(item))
json['eplusout_err']['warnings'] = json['eplusout_err']['warnings'].size
json['eplusout_err']['severe'] = json['eplusout_err']['warnings'].size
json['eplusout_err']['fatal'] = json['eplusout_err']['warnings'].size
@@ -710,69 +710,139 @@
full_json << json
}
end
File.open("#{output_folder}/../RESULTS-#{Time.now.strftime("%m-%d-%Y")}.json", 'w') {|f| f.write(JSON.pretty_generate(full_json)) }
end
-
- # This is a simple example which uses rubyzip to
- # recursively generate a zip file from the contents of
- # a specified directory. The directory itself is not
- # included in the archive, rather just its contents.
- #
- # Usage:
- # require /path/to/the/ZipFileGenerator/Class
- # directoryToZip = "/tmp/input"
- # outputFile = "/tmp/out.zip"
- # zf = BTAP::FileIO::ZipFileGenerator.new(directoryToZip, outputFile)
- # zf.write()
- #
- # class ZipFileGenerator
- # # Initialize with the directory to zip and the location of the output archive.
- # def initialize(input_dir, output_file)
- # @input_dir = input_dir
- # @output_file = output_file
- # self.write()
- # end
- #
- # # Zip the input directory.
- # def write
- # entries = Dir.entries(@input_dir) - %w(. ..)
- #
- # ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |io|
- # write_entries entries, '', io
- # end
- # end
- #
- # private
- #
- # # A helper method to make the recursion work.
- # def write_entries(entries, path, io)
- # entries.each do |e|
- # zip_file_path = path == '' ? e : File.join(path, e)
- # disk_file_path = File.join(@input_dir, zip_file_path)
- # puts "Deflating #{disk_file_path}"
- #
- # if File.directory? disk_file_path
- # recursively_deflate_directory(disk_file_path, io, zip_file_path)
- # else
- # put_into_archive(disk_file_path, io, zip_file_path)
- # end
- # end
- # end
- #
- # def recursively_deflate_directory(disk_file_path, io, zip_file_path)
- # io.mkdir zip_file_path
- # subdir = Dir.entries(disk_file_path) - %w(. ..)
- # write_entries subdir, zip_file_path, io
- # end
- #
- # def put_into_archive(disk_file_path, io, zip_file_path)
- # io.get_output_stream(zip_file_path) do |f|
- # f.puts(File.open(disk_file_path, 'rb').read)
- # end
- # end
- # end
- #
+
+ def self.compare_osm_files(model_true, model_compare)
+ only_model_true = [] # objects only found in the true model
+ only_model_compare = [] # objects only found in the compare model
+ both_models = [] # objects found in both models
+ diffs = [] # differences between the two models
+ num_ignored = 0 # objects not compared because they don't have names
+
+ # Define types of objects to skip entirely during the comparison
+ object_types_to_skip = [
+ 'OS:EnergyManagementSystem:Sensor', # Names are UIDs
+ 'OS:EnergyManagementSystem:Program', # Names are UIDs
+ 'OS:EnergyManagementSystem:Actuator', # Names are UIDs
+ 'OS:Connection', # Names are UIDs
+ 'OS:PortList', # Names are UIDs
+ 'OS:Building', # Name includes timestamp of creation
+ 'OS:ModelObjectList' # Names are UIDs
+ ]
+
+ # Find objects in the true model only or in both models
+ model_true.getModelObjects.sort.each do |true_object|
+
+ # Skip comparison of certain object types
+ next if object_types_to_skip.include?(true_object.iddObject.name)
+
+ # Skip comparison for objects with no name
+ unless true_object.iddObject.hasNameField
+ num_ignored += 1
+ next
+ end
+
+ # Find the object with the same name in the other model
+ compare_object = model_compare.getObjectByTypeAndName(true_object.iddObject.type, true_object.name.to_s)
+ if compare_object.empty?
+ only_model_true << true_object
+ else
+ both_models << [true_object, compare_object.get]
+ end
+ end
+
+ # Report a diff for each object found in only the true model
+ only_model_true.each do |true_object|
+ diffs << "A #{true_object.iddObject.name} called '#{true_object.name}' was found only in the before model"
+ end
+
+ # Find objects in compare model only
+ model_compare.getModelObjects.sort.each do |compare_object|
+
+ # Skip comparison of certain object types
+ next if object_types_to_skip.include?(compare_object.iddObject.name)
+
+ # Skip comparison for objects with no name
+ unless compare_object.iddObject.hasNameField
+ num_ignored += 1
+ next
+ end
+
+ # Find the object with the same name in the other model
+ true_object = model_true.getObjectByTypeAndName(compare_object.iddObject.type, compare_object.name.to_s)
+ if true_object.empty?
+ only_model_compare << compare_object
+ end
+ end
+
+ # Report a diff for each object found in only the compare model
+ only_model_compare.each do |compare_object|
+ #diffs << "An object called #{compare_object.name} of type #{compare_object.iddObject.name} was found only in the compare model"
+ diffs << "A #{compare_object.iddObject.name} called '#{compare_object.name}' was found only in the after model"
+ end
+
+ # Compare objects found in both models field by field
+ both_models.each do |b|
+ true_object = b[0]
+ compare_object = b[1]
+ idd_object = true_object.iddObject
+
+ true_object_num_fields = true_object.numFields
+ compare_object_num_fields = compare_object.numFields
+
+ # loop over fields skipping handle
+ (1...[true_object_num_fields, compare_object_num_fields].max).each do |i|
+
+ field_name = idd_object.getField(i).get.name
+
+ # Don't compare node, branch, or port names because they are populated with IDs
+ next if field_name.include?('Node Name')
+ next if field_name.include?('Branch Name')
+ next if field_name.include?('Inlet Port')
+ next if field_name.include?('Outlet Port')
+ next if field_name.include?('Inlet Node')
+ next if field_name.include?('Outlet Node')
+ next if field_name.include?('Port List')
+ next if field_name.include?('Cooling Control Zone or Zone List Name')
+ next if field_name.include?('Heating Control Zone or Zone List Name')
+ next if field_name.include?('Heating Zone Fans Only Zone or Zone List Name')
+
+ # Don't compare the names of schedule type limits
+ # because they appear to be created non-deteministically
+ next if field_name.include?('Schedule Type Limits Name')
+
+ # Get the value from the true object
+ true_value = ""
+ if i < true_object_num_fields
+ true_value = true_object.getString(i).to_s
+ end
+ true_value = "-" if true_value.empty?
+
+ # Get the same value from the compare object
+ compare_value = ""
+ if i < compare_object_num_fields
+ compare_value = compare_object.getString(i).to_s
+ end
+ compare_value = "-" if compare_value.empty?
+
+ # Round long numeric fields
+ true_value = true_value.to_f.round(5) unless true_value.to_f.zero?
+ compare_value = compare_value.to_f.round(5) unless compare_value.to_f.zero?
+
+ # Move to the next field if no difference was found
+ next if true_value == compare_value
+
+ # Report the difference
+ diffs << "For #{true_object.iddObject.name} called '#{true_object.name}' field '#{field_name}': before model = #{true_value}, after model = #{compare_value}"
+
+ end
+
+ end
+
+ return diffs
+ end
end #FileIO
\ No newline at end of file