Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion highlevel_tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def test_rbs_are_defined_sim_tests
def test_pys_are_defined_sim_tests
all_python_paths = Dir.glob(File.join($ModelDir, '*.py'))
all_python_filenames = all_python_paths.map { |p| File.basename(p) }
all_python_filenames -= ['python_plugin_program.py']
all_python_filenames -= ['python_plugin_program.py', 'python_plugin_search_paths_script.py']

content = File.read('model_tests.rb')
sim_test_re = Regexp.new('def test_.*\n(?:\s*#)*\s+result = sim_test\(\'(?<filename>.*\.py)\'\)\n(?:\s*#)*\s+end')
Expand Down
2 changes: 1 addition & 1 deletion model/simulationtests/python_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def on_end_of_zone_timestep_before_zone_reporting(self, state) -> int:

# Write it to a temporary directory so we don't pollute the current directory
# ExternalFile will copy it
pluginPath = Path(tempfile.gettempdir()) / "python_plugin_program.py"
pluginPath = Path(tempfile.gettempdir()) / f"{Path(__file__).stem}_program.py"
pluginPath.write_text(python_plugin_file_content)

# create the external file object
Expand Down
3 changes: 2 additions & 1 deletion model/simulationtests/python_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ def on_end_of_zone_timestep_before_zone_reporting(self, state) -> int:

# 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')
stem = File.basename(__FILE__, File.extname(__FILE__))
pluginPath = File.join(Dir.tmpdir, "#{stem}_program.py")
Comment on lines +114 to +115
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dynamic #{stem_program.py}

File.write(pluginPath, python_plugin_file_content)

# create the external file object
Expand Down
1 change: 1 addition & 0 deletions model/simulationtests/python_plugin_program.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# This file is used by python_plugin.osm
from pyenergyplus.plugin import EnergyPlusPlugin


Expand Down
132 changes: 132 additions & 0 deletions model/simulationtests/python_plugin_search_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import tempfile
from pathlib import Path

import openstudio

from lib.baseline_model import BaselineModel

model = BaselineModel()

# 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()

zone_names = sorted([x.nameString() for x in model.getThermalZones()])

zone_names_str_list = '["' + '", "'.join(zone_names) + '"]'

# Add a PythonPlugin:Variable (all OS SDK PythonPluginVariable objects are
# translated to a single E+ PythonPlugin:Variables (extensible object))
py_var = openstudio.model.PythonPluginVariable(model)
py_var.setName("AverageBuildingTemp")

# Add a PythonPlugin:OutputVariable for that variable
py_out_var = openstudio.model.PythonPluginOutputVariable(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("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("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(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(model)
py_var2.setName("RunningAverageBuildingTemp")

py_out_trend_var = openstudio.model.PythonPluginOutputVariable(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("PythonPlugin:OutputVariable", model)
out_trend_var.setReportingFrequency("Timestep")

pluginClassName = "AverageZoneTemps"

python_plugin_file_content = f"""from pyenergyplus.plugin import EnergyPlusPlugin
# NOTE: This external script must be locatable, so we'll add it to the PythonPluginSearchPaths
import python_plugin_search_paths_script


class {pluginClassName}(EnergyPlusPlugin):

def __init__(self):
super().__init__()
self.do_setup = True

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 = {zone_names_str_list}
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 = 0.0
denominator = 0.0
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'], {n_timesteps})
self.api.exchange.set_global_value(state, self.data['running_avg_temp_variable'], past_daily_avg_temp)
return 0
"""

# Write it to a temporary directory so we don't pollute the current directory
# ExternalFile will copy it
pluginPath = Path(tempfile.gettempdir()) / f"{Path(__file__).stem}_program.py"
pluginPath.write_text(python_plugin_file_content)

# create the external file object
external_file = openstudio.model.ExternalFile.getExternalFile(model, str(pluginPath))
external_file = external_file.get()

# create the python plugin instance object
python_plugin_instance = openstudio.model.PythonPluginInstance(external_file, pluginClassName)
python_plugin_instance.setRunDuringWarmupDays(False)

# create the python plugin search paths object (this test should fail without it)
python_plugin_search_paths = model.getPythonPluginSearchPaths()
python_plugin_search_paths.setAddCurrentWorkingDirectorytoSearchPath(True)
python_plugin_search_paths.setAddInputFileDirectorytoSearchPath(True)
python_plugin_search_paths.setAddepinEnvironmentVariabletoSearchPath(True)
python_plugin_search_paths.addSearchPath(Path(__file__).parent)

# save the OpenStudio model (.osm)
model.save_openstudio_osm(osm_save_directory=None, osm_name="in.osm")
137 changes: 137 additions & 0 deletions model/simulationtests/python_plugin_search_paths.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# frozen_string_literal: true

require 'openstudio'
require_relative 'lib/baseline_model'
require 'tmpdir'

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

zone_names = model.getThermalZones.map(&:nameString).sort

zone_names_str_list = '["' + zone_names.join('", "') + '"]'

# 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'

python_plugin_file_content = ''"from pyenergyplus.plugin import EnergyPlusPlugin
# NOTE: This external script must be locatable, so we'll add it to the PythonPluginSearchPaths
import python_plugin_search_paths_script


class #{pluginClassName}(EnergyPlusPlugin):

def __init__(self):
super().__init__()
self.do_setup = True

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 = #{zone_names_str_list}
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 = 0.0
denominator = 0.0
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'], #{n_timesteps})
self.api.exchange.set_global_value(state, self.data['running_avg_temp_variable'], past_daily_avg_temp)
return 0
"''

# Write it to a temporary directory so we don't pollute the current directory
# ExternalFile will copy it
stem = File.basename(__FILE__, File.extname(__FILE__))
pluginPath = File.join(Dir.tmpdir, "#{stem}_program.py")
File.write(pluginPath, python_plugin_file_content)

# 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)

# create the python plugin search paths object (this test should fail without it)
python_plugin_search_paths = model.getPythonPluginSearchPaths
python_plugin_search_paths.setAddCurrentWorkingDirectorytoSearchPath(true)
python_plugin_search_paths.setAddInputFileDirectorytoSearchPath(true)
python_plugin_search_paths.setAddepinEnvironmentVariabletoSearchPath(true)
python_plugin_search_paths.addSearchPath(File.dirname(__FILE__))
Comment on lines +129 to +134
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you confirm this fails without this object please?


# save the OpenStudio model (.osm)
model.save_openstudio_osm({ 'osm_save_directory' => Dir.pwd, 'osm_name' => 'in.osm' })
2 changes: 2 additions & 0 deletions model/simulationtests/python_plugin_search_paths_script.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# This file is used by python_plugin_search_paths
print('hello world')
13 changes: 13 additions & 0 deletions model_tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1537,6 +1537,19 @@ def test_python_plugin_osm
result = sim_test('python_plugin.osm')
end

# TODO: To be added in the next official release after: 3.9.0
# def test_python_plugin_search_paths_osm
# result = sim_test('python_plugin_search_paths.osm')
# end

def test_python_plugin_search_paths_rb
result = sim_test('python_plugin_search_paths.rb')
end

def test_python_plugin_search_paths_py
result = sim_test('python_plugin_search_paths.py')
end

def test_refrigeration_system_rb
result = sim_test('refrigeration_system.rb')
end
Expand Down
5 changes: 3 additions & 2 deletions test_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -708,11 +708,12 @@ def sim_test(filename, options = {})
cbor_target_path = File.join(files_dir, File.basename(cbor_ori_path))

FileUtils.cp(cbor_ori_path, cbor_target_path)
when 'python_plugin.osm'
when 'python_plugin.osm', 'python_plugin_search_paths.osm'
# We need to manually copy the supporting schedule into
# the testruns folder for the simulation to be able to find it
program_file_name = "#{File.basename(filename, File.extname(filename))}_program.py"
plugin_ori_path = File.join(File.dirname(__FILE__),
'model/simulationtests/python_plugin_program.py')
'model/simulationtests', program_file_name)
plugin_ori_path = File.realpath(plugin_ori_path)

# Have to make the directory first
Expand Down
Loading