# accim - Adaptive-Comfort-Control-Implemented Model
# Copyright (C) 2021-2025 Daniel Sánchez-García
# accim is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
# accim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Class for accim."""
from accim.utils import get_idd_path_from_ep_version
[docs]
class accimJob():
"""Class to start the process to add the class ``accim.sim.accis.addAccis``.
:param filename_temp: the filename of the idf
:param ScriptType: Inherited from class ``accim.sim.accis.addAccis``
:param EnergyPlus_version: Inherited from class ``accim.sim.accis.addAccis``
:param TempCtrl: Inherited from class ``accim.sim.accis.addAccis``
:param verboseMode: Inherited from class ``accim.sim.accis.addAccis``
:param accimNotWorking: True if problems detected in class ``accim.sim.accis.addAccis``
"""
from os import listdir
import numpy
from accim.sim.accim_IDFgeneration import \
inputData,\
genIDF
from accim.sim.accim_Base import \
setComfFieldsPeople, \
saveaccim, \
setPMVsetpoint, \
addControlFilesObjects, \
addOutputVariableDictionaryObject, \
addOutputEnergyManagementSystem
from accim.sim.accim_Base_EMS import \
addEMSActuatorsBase, \
addEMSOutputVariableBase, \
addEMSPCMBase, \
addEMSProgramsBase, \
addEMSSensorsBase, \
addGlobVarList, \
addIntVarList, \
addOutputVariablesStandard, \
addOutputVariablesSimplified, \
addOutputVariablesDetailed, \
removeExistingOutputVariables, \
removeDuplicatedOutputVariables, \
outputsSpecified, \
genOutputDataframe, \
takeOutputDataFrame, \
makeAverages
from accim.sim.accim_ExistingHVAC import \
addForscriptSchExistHVAC
from accim.sim.accim_ExistingHVAC_EMS import \
addEMSSensorsExisHVAC
from accim.sim.accim_VRFsystem import \
addBaseSchedules, \
addCurveObj, \
addDetHVACobj, \
addForscriptSchVRFsystem, \
addOpTempTherm, \
addVRFsystemSch, \
checkVentIsOn, \
setAvailSchOn
from accim.sim.accim_VRFsystem_EMS import \
addEMSSensorsVRFsystem
from accim.utils import amend_idf_version_from_dsb, get_idd_path_from_ep_version
from accim.sim.utils import scan_zones
def __init__(self,
filename_temp,
ScriptType: str = None,
EnergyPlus_version: str = 'auto',
TempCtrl: str = None,
verboseMode: bool = True,
accimNotWorking: bool = False):
"""
Constructor method.
"""
import eppy
from eppy.modeleditor import IDF
self.accimNotWorking = accimNotWorking
from accim.utils import amend_idf_version_from_dsb
from besos.eppy_funcs import get_building
fname1 = filename_temp + '.idf'
# Checking if idf version is suitable: when exported from Designbuilder 7.X, the version is 9.4.0.002
amend_idf_version_from_dsb(fname1)
idf_created = False
if EnergyPlus_version.lower() != 'auto':
iddfile = get_idd_path_from_ep_version(EnergyPlus_version=EnergyPlus_version)
# if EnergyPlus_version.lower() == '9.1':
# iddfile = 'C:/EnergyPlusV9-1-0/Energy+.idd'
# elif EnergyPlus_version.lower() == '9.2':
# iddfile = 'C:/EnergyPlusV9-2-0/Energy+.idd'
# elif EnergyPlus_version.lower() == '9.3':
# iddfile = 'C:/EnergyPlusV9-3-0/Energy+.idd'
# elif EnergyPlus_version.lower() == '9.4':
# iddfile = 'C:/EnergyPlusV9-4-0/Energy+.idd'
# elif EnergyPlus_version.lower() == '9.5':
# iddfile = 'C:/EnergyPlusV9-5-0/Energy+.idd'
# elif EnergyPlus_version.lower() == '9.6':
# iddfile = 'C:/EnergyPlusV9-6-0/Energy+.idd'
# elif EnergyPlus_version.lower() == '22.1':
# iddfile = 'C:/EnergyPlusV22-1-0/Energy+.idd'
# elif EnergyPlus_version.lower() == '22.2':
# iddfile = 'C:/EnergyPlusV22-2-0/Energy+.idd'
# elif EnergyPlus_version.lower() == '23.1':
# iddfile = 'C:/EnergyPlusV23-1-0/Energy+.idd'
# elif EnergyPlus_version.lower() == '23.2':
# iddfile = 'C:/EnergyPlusV23-2-0/Energy+.idd'
# elif EnergyPlus_version.lower() == '24.1':
# iddfile = 'C:/EnergyPlusV24-1-0/Energy+.idd'
# elif EnergyPlus_version.lower() == '24.2':
# iddfile = 'C:/EnergyPlusV24-2-0/Energy+.idd'
# elif EnergyPlus_version.lower() == '25.1':
# iddfile = 'C:/EnergyPlusV25-1-0/Energy+.idd'
# else:
if iddfile == 'not-supported':
raise ValueError("""EnergyPlus version not supported.\n
Only works for versions between EnergyPlus 9.1 (enter 9.1) and EnergyPlus 25.1 (enter 25.1)""")
if verboseMode:
print('IDD location is: '+iddfile)
IDF.setiddname(iddfile)
self.idf0 = IDF(fname1)
idf_from_eppy = True
else:
self.idf0 = get_building(fname1)
EnergyPlus_version = '.'.join([str(i) for i in self.idf0.idd_version[:2]])
if verboseMode:
print('IDD location is: '+self.idf0.iddname)
idf_from_eppy = False
self.idf0.savecopy(filename_temp+'_pymod.idf')
self.filename = filename_temp+'_pymod'
fname1 = self.filename+'.idf'
if idf_from_eppy:
self.idf1 = IDF(fname1)
else:
self.idf1 = get_building(fname1)
self.filename = filename_temp+'_pymod'
self.output_idf_dict = {}
# print(self.filename)
# Scanning occupied zones using function
self.scan_zones()
# Scanning occupied zones
# self.occupiedZones_orig = []
#
# # Check if model comes from OpenStudio
#
# # Check if ZoneList or SpaceList are used
# occupiedZones_orig_osm = []
#
# self.spacelist_use = False
# try:
# if len(self.idf1.idfobjects['SPACELIST']) > 0:
# self.spacelist_use = True
# self.spacenames_for_ems_uniquekey = []
# self.spacenames_for_ems_name = []
# self.spacenames_for_ems_uniquekey_people = []
# self.zonenames_for_ems_with_sl = []
# for people in self.idf1.idfobjects['PEOPLE']:
# for spacelist in [i for i in self.idf1.idfobjects['SPACELIST'] if i.Name == people.Zone_or_ZoneList_or_Space_or_SpaceList_Name]:
# for space in [i for i in self.idf1.idfobjects['SPACE'] if i.Space_Type == spacelist.Name]:
# self.spacenames_for_ems_uniquekey.append(f'{space.Name} {spacelist.Name}')
# self.spacenames_for_ems_name.append(space.Name)
# self.spacenames_for_ems_uniquekey_people.append(f'{space.Name} {people.Name}')
# occupiedZones_orig_osm.append(space.Zone_Name)
# for zone in [i for i in self.idf1.idfobjects['ZONE'] if space.Zone_Name == i.Name]:
# self.zonenames_for_ems_with_sl.append(zone.Name)
# self.spacenames_for_ems_uniquekey = list(set(self.spacenames_for_ems_uniquekey))
# self.spacenames_for_ems_name = list(set(self.spacenames_for_ems_name))
# self.spacenames_for_ems_uniquekey_people = list(set(self.spacenames_for_ems_uniquekey_people))
# except KeyError:
# idd = '-'.join([str(i) for i in self.idf1.idd_version])
# print('Searching Spacelist objects returned KeyError. '
# f'That means these are not supported in the EnergyPlus version {idd}')
#
# # occupiedZones_orig_osm = []
# # if len([h for h in self.idf1.idfobjects['zonelist']]) > 0:
# # if len(self.idf1.idfobjects['zone']) == 1:
# # no_of_zones = range(1, 2)
# # else:
# # no_of_zones = range(1, len(self.idf1.idfobjects['zone']))
# #
# # for i in no_of_zones:
# # for j in self.idf1.idfobjects['zonelist']:
# # for k in self.idf1.idfobjects['zone']:
# # if k.Name in j[f'Zone_{i}_Name']:
# # occupiedZones_orig_osm.append(k.Name)
# #
# # # for i in self.idf1.idfobjects['zone']:
# # # if all(i.Name not in [j for j in occupiedZones_orig_osm]):
# # # occupiedZones_orig_osm.append(i.Name)
#
# occupiedZones_orig_dsb = []
# for i in self.idf1.idfobjects['ZONE']:
# for k in self.idf1.idfobjects['PEOPLE']:
# try:
# if i.Name == k.Zone_or_ZoneList_or_Space_or_SpaceList_Name:
# occupiedZones_orig_dsb.append(i.Name.upper())
# except eppy.bunch_subclass.BadEPFieldError:
# if i.Name == k.Zone_or_ZoneList_Name:
# occupiedZones_orig_dsb.append(i.Name.upper())
#
# if self.spacelist_use:
# self.occupiedZones_orig = occupiedZones_orig_osm
# self.occupiedZones = [i.replace(' ', '_') for i in self.occupiedZones_orig]
# self.origin_dsb = False
# self.ems_objs_name = self.spacenames_for_ems_name
# self.ems_objs_key = self.spacenames_for_ems_uniquekey
# self.ems_zonenames = self.zonenames_for_ems_with_sl
# self.ems_zonenames_underscore = [i.replace(' ', '_') for i in self.ems_zonenames]
# else:
# self.occupiedZones_orig = occupiedZones_orig_dsb
# self.occupiedZones = [i.replace(':', '_') for i in self.occupiedZones_orig]
# self.origin_dsb = True
# self.ems_objs_name = self.occupiedZones
# self.ems_objs_key = self.occupiedZones_orig
# self.ems_zonenames = self.occupiedZones_orig
# self.ems_zonenames_underscore = self.occupiedZones_orig
if verboseMode:
print(f'The occupied zones in the model {filename_temp} are:')
print(*self.occupiedZones_orig, sep="\n")
self.ismixedmode = False
if (ScriptType.lower() == 'vrfsystem_mm' or
ScriptType.lower() == 'vrf_mm' or
ScriptType.lower() == 'existinghvac_mm' or
ScriptType.lower() == 'ex_mm'
):
self.ismixedmode = True
self.windownamelist_orig = []
for i in [window.Name for window in
self.idf1.idfobjects
['AirflowNetwork:MultiZone:Component:DetailedOpening']
if window.Name.endswith('_Win')
or window.Name.endswith('_Door')
]:
for k in self.occupiedZones_orig:
if i.split('_')[0].lower() == k.lower():
self.windownamelist_orig.append(i)
self.windownamelist = [i.replace(':', '_') for i in self.windownamelist_orig]
# print(self.windownamelist_orig)
self.windownamelist_orig_split = ([i.split('_') for i in self.windownamelist_orig])
# print(self.windownamelist_orig_split)
if verboseMode:
print(f'The windows and doors in the model {filename_temp} are:')
print(*self.windownamelist, sep="\n")
if 'vrf' in ScriptType.lower():
self.zonenames = self.occupiedZones
self.zonenames_orig = self.occupiedZones_orig
if verboseMode:
print(f'The zones in the model {filename_temp} are:')
print(*self.zonenames, sep="\n")
elif 'ex' in ScriptType.lower():
TSPtypes = [
'ThermostatSetpoint:SingleHeating',
'ThermostatSetpoint:SingleCooling',
# ThermostatSetpoint:SingleHeatingOrCooling objects are not supported
# 'ThermostatSetpoint:SingleHeatingOrCooling',
'ThermostatSetpoint:DualSetpoint'
]
self.ZCTlist = [i for i in self.idf1.idfobjects['ZONECONTROL:THERMOSTAT']]
self.HVACzonelist = []
for i in range(len(TSPtypes)):
temp1 = []
temp2 = []
temp3 = []
if len(self.idf1.idfobjects[TSPtypes[i]]) > 0:
for j in range(len(self.ZCTlist)):
if self.ZCTlist[j].Control_1_Object_Type in TSPtypes[i]:
temp1.append(self.ZCTlist[j].Zone_or_ZoneList_Name.upper())
if ':' in self.ZCTlist[j].Zone_or_ZoneList_Name:
temp2.append(self.ZCTlist[j].Zone_or_ZoneList_Name.upper().replace(":", "_"))
else:
# temp_space =
# temp2.append(self.ZCTlist[j].Zone_or_ZoneList_Name.upper().replace(" ", "_"))
temp2.append([i.Name.upper() for i in self.idf1.idfobjects['SPACE'] if i.Zone_Name.upper() == self.ZCTlist[j].Zone_or_ZoneList_Name.upper()][0])
temp3.append(self.ZCTlist[j].Control_1_Name)
self.HVACzonelist.append([TSPtypes[i], temp1, temp2, temp3])
del temp1, temp2, temp3
if verboseMode:
for i in range(len(self.HVACzonelist)):
if len(self.HVACzonelist[i][3]) == 0:
print(f'There are no {self.HVACzonelist[i][0]} objects in the model')
else:
print(f'Regarding {self.HVACzonelist[i][0]} objects:')
print(f'The zones with {self.HVACzonelist[i][0]} are:')
print(*self.HVACzonelist[i][1], sep="\n")
print(f'And the existing ThermostatSetpoint objects related to {self.HVACzonelist[i][0]} are:')
print(*self.HVACzonelist[i][3], sep="\n")
self.zonenames_orig = []
# todo currently all zones regardless the single or dual thermostat object are merged in zonenames_orig;
# this would be desirable to be amended
for i in range(len(self.HVACzonelist)):
for k in range(len(self.HVACzonelist[i][1])):
if self.HVACzonelist[i][1][k] in self.zonenames_orig:
continue
else:
self.zonenames_orig.append(self.HVACzonelist[i][1][k])
if self.origin_dsb:
self.zonenames = [i.replace(':', '_') for i in self.zonenames_orig]
else:
self.zonenames = [i.replace(' ', '_') for i in self.zonenames_orig]
if ScriptType.lower() == 'existinghvac_mm' or ScriptType.lower() == 'ex_mm':
self.HVACdict = {
# todo if there is a Coil:Heating:Whatever and another Coil:Heating:DifferentWhatever
# coils and windows sensors will be duplicated and simulation will crash; it needs to be solved.
# Group Heating and Cooling Coils
'Coil:Cooling:Water': 'Cooling Coil Total Cooling Rate',
'Coil:Cooling:Water:DetailedGeometry': 'Cooling Coil Total Cooling Rate',
# not supported
# 'CoilSystem:Cooling:Water:HeatExchangerAssisted':'',
'CoilSystem:Cooling:Water': 'Coil System Water Total Cooling Rate',
'Coil:Heating:Water': 'Heating Coil Heating Energy',
'Coil:Heating:Steam': 'Heating Coil Heating Energy',
'Coil:Heating:Electric': 'Heating Coil Heating Energy',
'Coil:Heating:Electric:MultiStage': 'Heating Coil Heating Energy',
'Coil:Heating:Desuperheater': 'Heating Coil Heating Energy',
'Coil:Cooling:DX:VariableRefrigerantFlow': 'Cooling Coil Total Cooling Rate',
'Coil:Heating:DX:VariableRefrigerantFlow': 'Heating Coil Heating Energy',
'Coil:Cooling:DX:VariableRefrigerantFlow:FluidTemperatureControl': 'Cooling Coil Total Cooling Rate',
'Coil:Heating:DX:VariableRefrigerantFlow:FluidTemperatureControl': 'Heating Coil Heating Energy',
'Coil:Heating:Fuel': 'Heating Coil Heating Energy',
'Coil:Heating:Gas:MultiStage': 'Heating Coil Heating Energy',
'Coil:Cooling:DX:SingleSpeed': 'Cooling Coil Total Cooling Rate',
'Coil:Cooling:DX:TwoSpeed': 'Cooling Coil Total Cooling Rate',
'Coil:Cooling:DX:TwoStageWithHumidityControlMode': 'Cooling Coil Total Cooling Rate',
'Coil:Cooling:DX:MultiSpeed': 'Cooling Coil Total Cooling Rate',
'Coil:Cooling:DX:VariableSpeed': 'Cooling Coil Total Cooling Rate',
# not supported
# 'CoilPerformance:DX:Cooling': '',
'Coil:Heating:DX:SingleSpeed': 'Heating Coil Heating Energy',
'Coil:Heating:DX:MultiSpeed': 'Heating Coil Heating Energy',
'Coil:Heating:DX:VariableSpeed': 'Heating Coil Heating Energy',
'Coil:WaterHeating:Desuperheater': 'Water Heater Heating Energy',
# not supported
# 'CoilSystem:Cooling:DX': '',
# 'CoilSystem:Heating:DX': '',
# 'CoilSystem:Cooling:DX:HeatExchangerAssisted': '',
# 'CoilSystem:IntegratedHeatPump:AirSource': '',
# 'Coil:WaterHeating:AirToWaterHeatPump:Pumped': 'Heating Coil Heating Energy',
# 'Coil:WaterHeating:AirToWaterHeatPump:Wrapped': 'Heating Coil Heating Energy',
# 'Coil:WaterHeating:AirToWaterHeatPump:VariableSpeed': 'Cooling Coil Electricity Energy',
'Coil:Cooling:WaterToAirHeatPump:ParameterEstimation': 'Cooling Coil Total Cooling Rate',
'Coil:Cooling:WaterToAirHeatPump:EquationFit': 'Cooling Coil Total Cooling Rate',
'Coil:Cooling:WaterToAirHeatPump:VariableSpeedEquationFit': 'Cooling Coil Total Cooling Rate',
'Coil:Heating:WaterToAirHeatPump:ParameterEstimation': 'Heating Coil Heating Energy',
'Coil:Heating:WaterToAirHeatPump:EquationFit': 'Heating Coil Heating Energy',
'Coil:Heating:WaterToAirHeatPump:VariableSpeedEquationFit': 'Heating Coil Heating Energy',
'Coil:Cooling:DX:SingleSpeed:ThermalStorage': 'Cooling Coil Total Cooling Rate',
# not supported
# 'Secondary Coils of DX System and Heat Pump':'',
'Coil:Cooling:DX': 'Cooling Coil Total Cooling Rate',
'Coil:Cooling:DX:CurveFit:Performance': 'Cooling Coil Total Cooling Rate',
'Coil:Cooling:DX:CurveFit:OperatingMode': 'Cooling Coil Total Cooling Rate',
'Coil:Cooling:DX:CurveFit:Speed': 'Cooling Coil Total Cooling Rate',
# Group – Radiative / Convective Units
# todo many objects have heating or cooling outputs for the same field
'ZoneHVAC:Baseboard:RadiantConvective:Water': 'Baseboard Total Heating Rate',
'ZoneHVAC:Baseboard:RadiantConvective:Steam': 'Baseboard Total Heating Rate',
'ZoneHVAC:Baseboard:RadiantConvective:Electric': 'Baseboard Total Heating Rate',
'ZoneHVAC:CoolingPanel:RadiantConvective:Water': 'Cooling Panel Total Cooling Rate',
'ZoneHVAC:Baseboard:Convective:Water': 'Baseboard Total Heating Rate',
'ZoneHVAC:Baseboard:Convective:Electric': 'Baseboard Total Heating Rate',
# not supported
# ZoneHVAC:LowTemperatureRadiant:VariableFlow can be a chilled ceiling: Zone Radiant HVAC Cooling Rate
# also it can be a heated floor: Zone Radiant HVAC Heating Rate
# 'ZoneHVAC:LowTemperatureRadiant:VariableFlow': 'Zone Radiant HVAC Heating Energy',
# 'ZoneHVAC:LowTemperatureRadiant:ConstantFlow': 'Zone Radiant HVAC Heating Energy',
# 'ZoneHVAC:LowTemperatureRadiant:Electric': 'Zone Radiant HVAC Heating Energy',
# 'ZoneHVAC:LowTemperatureRadiant:SurfaceGroup': 'Zone Radiant HVAC Heating Energy',
# 'ZoneHVAC:HighTemperatureRadiant': 'Zone Radiant HVAC Heating Energy',
# 'ZoneHVAC:VentilatedSlab': '',
# 'ZoneHVAC:VentilatedSlab:SlabGroup': '',
# Group – Zone HVAC Air Loop Terminal Units
# 'AirTerminal:SingleDuct:ConstantVolume:Reheat': '',
# 'AirTerminal:SingleDuct:ConstantVolume:NoReheat': '',
# 'AirTerminal:SingleDuct:VAV:Reheat': '',
# 'AirTerminal:SingleDuct:VAV:Reheat:VariableSpeedFan': '',
# 'AirTerminal:SingleDuct:VAV:HeatAndCool:Reheat': '',
# 'AirTerminal:SingleDuct:VAV:NoReheat': '',
# 'AirTerminal:SingleDuct:VAV:HeatAndCool:NoReheat': '',
# 'AirTerminal:SingleDuct:SeriesPIU:Reheat': '',
# 'AirTerminal:SingleDuct:ParallelPIU:Reheat': '',
# 'AirTerminal:SingleDuct:ConstantVolume:FourPipeInduction': '',
# 'AirTerminal:SingleDuct:ConstantVolume:FourPipeBeam': '',
'AirTerminal:SingleDuct:ConstantVolume:CooledBeam': 'Zone Air Terminal Beam Chilled Water Energy',
# 'AirTerminal:SingleDuct:Mixer': '',
# 'AirTerminal:DualDuct:ConstantVolume': '',
# 'AirTerminal:DualDuct:VAV': '',
# 'AirTerminal:DualDuct:VAV:OutdoorAir': ''
}
HVACkeylist = list(self.HVACdict.keys())
self.ExisHVAC = []
for i in range(len(HVACkeylist)):
try:
temp = [i.Name for i in self.idf1.idfobjects[HVACkeylist[i]]]
temp_zone_orig = [i.Name.split(' ')[0] for i in self.idf1.idfobjects[HVACkeylist[i]]]
temp_zone = [i.Name.split(' ')[0].replace(':', '_') for i in self.idf1.idfobjects[HVACkeylist[i]]]
temp_win = []
for j in temp_zone:
for k in self.windownamelist:
if j in k:
temp_win.append(k)
# hasta aqui
if len(temp) == 0:
continue
else:
self.ExisHVAC.append([HVACkeylist[i], temp, temp_zone_orig, temp_zone, temp_win])
except KeyError:
if verboseMode:
print(f'{HVACkeylist[i]} HVAC SYSTEM IS NOT SUPPORTED')
continue
for i in range(len(self.ExisHVAC)):
for j in range(len(self.ExisHVAC[i][2])):
if self.ExisHVAC[i][2][j] not in self.zonenames_orig:
if verboseMode:
print(f'"{self.ExisHVAC[i][2][j]}" is not a valid room. \n'
f'That means "{self.ExisHVAC[i][1][j]}" is not named following [HVAC Zone] [HVAC Element] pattern or HVAC element is shared')
# raise ValueError
self.accimNotWorking = True
if verboseMode:
for i in range(len(self.ExisHVAC)):
print(f'The names of the existing {self.ExisHVAC[i][0]} objects are:')
print(*self.ExisHVAC[i][1], sep="\n")
print(f'The zones related to these {self.ExisHVAC[i][0]} objects are')
print(*self.ExisHVAC[i][2], sep='\n')
print(f'And the windows related to these {self.ExisHVAC[i][0]} objects are:')
print(*self.ExisHVAC[i][4], sep='\n')