diff --git a/model/simulationtests/lib/python_plugin_program.csv b/model/simulationtests/lib/python_plugin_program.csv new file mode 100644 index 000000000..00aecc5b7 --- /dev/null +++ b/model/simulationtests/lib/python_plugin_program.csv @@ -0,0 +1,2 @@ +numerator,denominator +0,0 diff --git a/model/simulationtests/lib/python_plugin_program.py b/model/simulationtests/lib/python_plugin_program.py new file mode 100644 index 000000000..65e4a9b4c --- /dev/null +++ b/model/simulationtests/lib/python_plugin_program.py @@ -0,0 +1,44 @@ +from pyenergyplus.plugin import EnergyPlusPlugin + +import os, sys +sys.path.append('C:/Python38/Lib/site-packages') # this should (needs to?) be same version as E+'s version +import pandas as pd + +class <%= pluginClassName %>(EnergyPlusPlugin): + + def __init__(self): + super().__init__() + self.do_setup = True + + self.df = pd.read_csv(os.path.join(os.path.dirname(__file__), 'python_plugin_program.csv')) + + def on_end_of_zone_timestep_before_zone_reporting(self, state) -> int: + if self.do_setup: + self.data['zone_volumes'] = [] + self.data['zone_temps'] = [] + zone_names = <%= model.getThermalZones.map(&:nameString).sort %> + for zone_name in zone_names: + handle = self.api.exchange.get_internal_variable_handle(state, 'Zone Air Volume', zone_name) + zone_volume = self.api.exchange.get_internal_variable_value(state, handle) + self.data['zone_volumes'].append(zone_volume) + self.data['zone_temps'].append( + self.api.exchange.get_variable_handle(state, 'Zone Mean Air Temperature', zone_name) + ) + self.data['avg_temp_variable'] = self.api.exchange.get_global_handle(state, '<%= py_var.nameString %>') + self.data['trend'] = self.api.exchange.get_trend_handle(state, '<%= py_trend_var.nameString %>') + self.data['running_avg_temp_variable'] = self.api.exchange.get_global_handle(state, '<%= py_var2.nameString %>') + self.do_setup = False + zone_temps = list() + for t_handle in self.data['zone_temps']: + zone_temps.append(self.api.exchange.get_variable_value(state, t_handle)) + numerator = float(self.df.iloc[0]['numerator']) + denominator = float(self.df.iloc[0]['denominator']) + for i in range(len(self.data['zone_volumes'])): + numerator += self.data['zone_volumes'][i] * zone_temps[i] + denominator += self.data['zone_volumes'][i] + average_temp = numerator / denominator + self.api.exchange.set_global_value(state, self.data['avg_temp_variable'], average_temp) + + past_daily_avg_temp = self.api.exchange.get_trend_average(state, self.data['trend'], 96) + self.api.exchange.set_global_value(state, self.data['running_avg_temp_variable'], past_daily_avg_temp) + return 0 diff --git a/model/simulationtests/python_plugin_2.rb b/model/simulationtests/python_plugin_2.rb new file mode 100644 index 000000000..1a21a03d8 --- /dev/null +++ b/model/simulationtests/python_plugin_2.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +require 'openstudio' +require_relative 'lib/baseline_model' +require 'tmpdir' +require 'erb' + +model = BaselineModel.new + +# make a 1 story, 100m X 50m, 5 zone core/perimeter building +model.add_geometry({ 'length' => 100, + 'width' => 50, + 'num_floors' => 1, + 'floor_to_floor_height' => 4, + 'plenum_height' => 0, + 'perimeter_zone_depth' => 3 }) + +# assign constructions from a local library to the walls/windows/etc. in the model +model.set_constructions + +# set whole building space type; simplified 90.1-2004 Large Office Whole Building +model.set_space_type + +# add design days to the model (Chicago) +model.add_design_days + +# Add a PythonPlugin:Variable (all OS SDK PythonPluginVariable objects are +# translated to a single E+ PythonPlugin:Variables (extensible object)) +py_var = OpenStudio::Model::PythonPluginVariable.new(model) +py_var.setName('AverageBuildingTemp') + +# Add a PythonPlugin:OutputVariable for that variable +py_out_var = OpenStudio::Model::PythonPluginOutputVariable.new(py_var) +py_out_var.setName('Averaged Building Temperature') +py_out_var.setTypeofDatainVariable('Averaged') +py_out_var.setUpdateFrequency('ZoneTimestep') +py_out_var.setUnits('C') + +# Add a regular Output:Variable that references it +out_var = OpenStudio::Model::OutputVariable.new('PythonPlugin:OutputVariable', model) +out_var.setKeyValue(py_out_var.nameString) +out_var.setReportingFrequency('Timestep') + +# Add output variables for Zone Mean Air Temperature, so we can compare +outputVariable = OpenStudio::Model::OutputVariable.new('Zone Mean Air Temperature', model) +outputVariable.setReportingFrequency('Timestep') + +# Trend Variable: while this is a fully functioning object, you're probably +# best just using a storage variable on the Python side (eg: a list) +py_trend_var = OpenStudio::Model::PythonPluginTrendVariable.new(py_var) +py_trend_var.setName('Running Averaged Building Temperature') +n_timesteps = 24 * model.getTimestep.numberOfTimestepsPerHour +py_trend_var.setNumberofTimestepstobeLogged(n_timesteps) + +py_var2 = OpenStudio::Model::PythonPluginVariable.new(model) +py_var2.setName('RunningAverageBuildingTemp') + +py_out_trend_var = OpenStudio::Model::PythonPluginOutputVariable.new(py_var2) +py_out_trend_var.setName('Running Averaged Building Temperature') +py_out_trend_var.setTypeofDatainVariable('Averaged') +py_out_trend_var.setUpdateFrequency('ZoneTimestep') +py_out_trend_var.setUnits('C') + +out_trend_var = OpenStudio::Model::OutputVariable.new('PythonPlugin:OutputVariable', model) +out_trend_var.setReportingFrequency('Timestep') + +pluginClassName = 'AverageZoneTemps' + +# get the python plugin program (erb template) +pluginTemplatePath = File.join(File.dirname(__FILE__), 'lib/python_plugin_program.py') +in_py = '' +File.open(pluginTemplatePath, 'r') do |file| + in_py = file.read +end + +dataPath = File.join(File.dirname(__FILE__), 'lib/python_plugin_program.csv') +OpenStudio::Model::ExternalFile.getExternalFile(model, dataPath) + +# configure plugin template with variable values +renderer = ERB.new(in_py) +out_py = renderer.result(binding) + +# Write it to a temporary directory so we don't pollute the current directory +# ExternalFile will copy it +pluginPath = File.join(Dir.tmpdir, 'python_plugin_program.py') +File.open(pluginPath, 'w') do |file| + file << out_py +end + +# create the external file object +external_file = OpenStudio::Model::ExternalFile.getExternalFile(model, pluginPath) +external_file = external_file.get + +# create the python plugin instance object +python_plugin_instance = OpenStudio::Model::PythonPluginInstance.new(external_file, pluginClassName) +python_plugin_instance.setRunDuringWarmupDays(false) + +# save the OpenStudio model (.osm) +model.save_openstudio_osm({ 'osm_save_directory' => Dir.pwd, 'osm_name' => 'in.osm' }) diff --git a/model_tests.rb b/model_tests.rb index 68cd75f83..f17db2463 100644 --- a/model_tests.rb +++ b/model_tests.rb @@ -968,6 +968,15 @@ def test_python_plugin_osm result = sim_test('python_plugin.osm') end + def test_python_plugin_2_rb + result = sim_test('python_plugin_2.rb') + end + + # TODO: To be added in the next official release after: 3.5.0 + # def test_python_plugin_2_osm + # result = sim_test('python_plugin_2.osm') + # end + def test_refrigeration_system_rb result = sim_test('refrigeration_system.rb') end