Source code for eppy.modeleditor

# Copyright (c) 2012, 2022 Santosh Philip
# Copyright (c) 2016 Jamie Bull
# Copyright (c) 2021 Jeremy Lerond
# =======================================================================
#  Distributed under the MIT License.
#  (See accompanying file LICENSE or copy at
#  http://opensource.org/licenses/MIT)
# =======================================================================
"""functions to edit the E+ model"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import copy
import itertools
import os
import platform
import warnings

from io import StringIO

import eppy.EPlusInterfaceFunctions.iddgroups as iddgroups
import eppy.function_helpers
from eppy.iddcurrent import iddcurrent
from eppy.idfreader import idfreader1
from eppy.idfreader import convertafield
from eppy.idfreader import makeabunch
from eppy.runner.run_functions import run
from eppy.runner.run_functions import wrapped_help_text
from eppy import idfreader
import eppy.ext_field_functions as extff


[docs]class NoObjectError(Exception): """Exception Object""" pass
[docs]class NotSameObjectError(Exception): """Exception Object""" pass
[docs]class IDDNotSetError(Exception): """Exception Object""" pass
[docs]class IDDAlreadySetError(Exception): """Exception Object""" pass
[docs]class IDDResetError(Exception): """Exception Object""" pass
[docs]def almostequal(first, second, places=7, printit=True): """ Test if two values are equal to a given number of places. This is based on python's unittest so may be covered by Python's license. """ if first == second: return True if round(abs(second - first), places) != 0: if printit: print(round(abs(second - first), places)) print("notalmost: %s != %s to %i places" % (first, second, places)) return False else: return True
[docs]def poptrailing(lst): """Remove trailing blank items from lst.""" while lst and lst[-1] == "": lst.pop() return lst
[docs]def extendlist(lst, i, value=""): """extend the list so that you have i-th value""" if i == None: pass elif i < len(lst): pass else: lst.extend([value] * (i - len(lst) + 1))
[docs]def newrawobject(data, commdct, key, block=None, defaultvalues=True): """Make a new object for the given key. Parameters ---------- data : Eplusdata object Data dictionary and list of objects for the entire model. commdct : list of dicts Comments from the IDD file describing each item type in `data`. key : str Object type of the object to add . Returns ------- list A list of field values for the new object. """ dtls = data.dtls key = key.upper() key_i = dtls.index(key) key_comm = commdct[key_i] # set default values if defaultvalues: obj = [comm.get("default", [""])[0] for comm in key_comm] else: obj = ["" for comm in key_comm] if not block: inblock = ["does not start with N"] * len(obj) else: inblock = block[key_i] for i, (f_comm, f_val, f_iddname) in enumerate(zip(key_comm, obj, inblock)): if i == 0: obj[i] = key else: obj[i] = convertafield(f_comm, f_val, f_iddname) obj = poptrailing(obj) # remove the blank items in a repeating field. return obj
[docs]def addthisbunch(bunchdt, data, commdct, thisbunch, theidf): """add a bunch to model. abunch usually comes from another idf file or it can be used to copy within the idf file""" key = thisbunch.key.upper() obj = copy.copy(thisbunch.obj) abunch = obj2bunch(data, commdct, obj) bunchdt[key].append(abunch) return abunch
[docs]def obj2bunch(data, commdct, obj): """make a new bunch object using the data object""" dtls = data.dtls key = obj[0].upper() key_i = dtls.index(key) abunch = makeabunch(commdct, obj, key_i) return abunch
[docs]def namebunch(abunch, aname): """give the bunch object a name, if it has a Name field""" if abunch.Name == None: pass else: abunch.Name = aname return abunch
[docs]def addobject(bunchdt, data, commdct, key, theidf, aname=None, **kwargs): """add an object to the eplus model""" obj = newrawobject(data, commdct, key) abunch = obj2bunch(data, commdct, obj) if aname: namebunch(abunch, aname) data.dt[key].append(obj) bunchdt[key].append(abunch) for key, value in list(kwargs.items()): abunch[key] = value return abunch
[docs]def getnamedargs(*args, **kwargs): """allows you to pass a dict and named args so you can pass ({'a':5, 'b':3}, c=8) and get dict(a=5, b=3, c=8)""" adict = {} for arg in args: if isinstance(arg, dict): adict.update(arg) adict.update(kwargs) return adict
[docs]def addobject1(bunchdt, data, commdct, key, **kwargs): """add an object to the eplus model""" obj = newrawobject(data, commdct, key) abunch = obj2bunch(data, commdct, obj) data.dt[key].append(obj) bunchdt[key].append(abunch) # adict = getnamedargs(*args, **kwargs) for kkey, value in kwargs.items(): abunch[kkey] = value return abunch
[docs]def getobject(bunchdt, key, name): """get the object if you have the key and the name returns a list of objects, in case you have more than one You should not have more than one""" # TODO : throw exception if more than one object, or return more objects idfobjects = bunchdt[key] if idfobjects: # second item in list is a unique ID unique_id = idfobjects[0].objls[1] theobjs = [ idfobj for idfobj in idfobjects if idfobj[unique_id].upper() == name.upper() ] try: return theobjs[0] except IndexError: return None
def __objecthasfields(bunchdt, data, commdct, idfobject, places=7, **kwargs): """test if the idf object has the field values in kwargs""" for key, value in list(kwargs.items()): if not isfieldvalue( bunchdt, data, commdct, idfobject, key, value, places=places ): return False return True
[docs]def getobjects(bunchdt, data, commdct, key, places=7, **kwargs): """get all the objects of key that matches the fields in ``**kwargs``""" idfobjects = bunchdt[key] allobjs = [] for obj in idfobjects: if __objecthasfields(bunchdt, data, commdct, obj, places=places, **kwargs): allobjs.append(obj) return allobjs
[docs]def iddofobject(data, commdct, key): """from commdct, return the idd of the object key""" dtls = data.dtls i = dtls.index(key) return commdct[i]
[docs]def getextensibleindex(bunchdt, data, commdct, key, objname): """get the index of the first extensible item""" theobject = getobject(bunchdt, key, objname) if theobject == None: return None theidd = iddofobject(data, commdct, key) extensible_i = [i for i in range(len(theidd)) if "begin-extensible" in theidd[i]] try: extensible_i = extensible_i[0] except IndexError: return theobject
[docs]def removeextensibles(bunchdt, data, commdct, key, objname): """remove the extensible items in the object""" theobject = getobject(bunchdt, key, objname) if theobject == None: return theobject theidd = iddofobject(data, commdct, key) extensible_i = [i for i in range(len(theidd)) if "begin-extensible" in theidd[i]] try: extensible_i = extensible_i[0] except IndexError: return theobject while True: try: popped = theobject.obj.pop(extensible_i) except IndexError: break return theobject
[docs]def getfieldcomm(bunchdt, data, commdct, idfobject, fieldname): """get the idd comment for the field""" key = idfobject.obj[0].upper() keyi = data.dtls.index(key) fieldi = idfobject.objls.index(fieldname) thiscommdct = commdct[keyi][fieldi] return thiscommdct
[docs]def is_retaincase(bunchdt, data, commdct, idfobject, fieldname): """test if case has to be retained for that field""" thiscommdct = getfieldcomm(bunchdt, data, commdct, idfobject, fieldname) return "retaincase" in thiscommdct
[docs]def isfieldvalue(bunchdt, data, commdct, idfobj, fieldname, value, places=7): """test if idfobj.field == value""" # do a quick type check # if type(idfobj[fieldname]) != type(value): # return False # takes care of autocalculate and real # check float thiscommdct = getfieldcomm(bunchdt, data, commdct, idfobj, fieldname) if "type" in thiscommdct: if thiscommdct["type"][0] in ("real", "integer"): # test for autocalculate try: if idfobj[fieldname].upper() == "AUTOCALCULATE": if value.upper() == "AUTOCALCULATE": return True except AttributeError: pass return almostequal(float(idfobj[fieldname]), float(value), places, False) # check retaincase if is_retaincase(bunchdt, data, commdct, idfobj, fieldname): return idfobj[fieldname] == value else: return idfobj[fieldname].upper() == value.upper()
[docs]def equalfield(bunchdt, data, commdct, idfobj1, idfobj2, fieldname, places=7): """returns true if the two fields are equal will test for retaincase places is used if the field is float/real""" # TODO test if both objects are of same type key1 = idfobj1.obj[0].upper() key2 = idfobj2.obj[0].upper() if key1 != key2: raise NotSameObjectError vee2 = idfobj2[fieldname] return isfieldvalue(bunchdt, data, commdct, idfobj1, fieldname, vee2, places=places)
[docs]def getrefnames(idf, objname): """get the reference names for this object""" iddinfo = idf.idd_info dtls = idf.model.dtls index = dtls.index(objname) fieldidds = iddinfo[index] for fieldidd in fieldidds: if "field" in fieldidd: if fieldidd["field"][0].endswith("Name"): if "reference" in fieldidd: return fieldidd["reference"] else: return []
[docs]def getallobjlists(idf, refname): """get all object-list fields for refname return a list: [('OBJKEY', refname, fieldindexlist), ...] where fieldindexlist = index of the field where the object-list = refname """ dtls = idf.model.dtls objlists = [] for i, fieldidds in enumerate(idf.idd_info): indexlist = [] for j, fieldidd in enumerate(fieldidds): if "object-list" in fieldidd: if fieldidd["object-list"][0].upper() == refname.upper(): indexlist.append(j) if indexlist != []: objkey = dtls[i] objlists.append((objkey, refname, indexlist)) return objlists
[docs]def rename(idf, objkey, objname, newname): """rename all the refrences to this objname""" refnames = getrefnames(idf, objkey) for refname in refnames: objlists = getallobjlists(idf, refname) # [('OBJKEY', refname, fieldindexlist), ...] for refname in refnames: # TODO : there seems to be a duplication in this loop. Check. # refname appears in both loops for robjkey, refname, fieldindexlist in objlists: idfobjects = idf.idfobjects[robjkey] for idfobject in idfobjects: for findex in fieldindexlist: # for each field if idfobject[idfobject.objls[findex]] == objname: idfobject[idfobject.objls[findex]] = newname theobject = idf.getobject(objkey, objname) fieldname = [item for item in theobject.objls if item.endswith("Name")][0] theobject[fieldname] = newname return theobject
[docs]def zonearea(idf, zonename, debug=False): """zone area""" zone = idf.getobject("ZONE", zonename) surfs = idf.idfobjects["BuildingSurface:Detailed".upper()] zone_surfs = [s for s in surfs if s.Zone_Name == zone.Name] floors = [s for s in zone_surfs if s.Surface_Type.upper() == "FLOOR"] if debug: print(len(floors)) print([floor.area for floor in floors]) # area = sum([floor.area for floor in floors]) if floors != []: area = zonearea_floor(idf, zonename) else: area = zonearea_roofceiling(idf, zonename) return area
[docs]def zonearea_floor(idf, zonename, debug=False): """zone area - floor""" zone = idf.getobject("ZONE", zonename) surfs = idf.idfobjects["BuildingSurface:Detailed".upper()] zone_surfs = [s for s in surfs if s.Zone_Name == zone.Name] floors = [s for s in zone_surfs if s.Surface_Type.upper() == "FLOOR"] if debug: print(len(floors)) print([floor.area for floor in floors]) area = sum([floor.area for floor in floors]) return area
[docs]def zonearea_roofceiling(idf, zonename, debug=False): """zone area - roof, ceiling""" zone = idf.getobject("ZONE", zonename) surfs = idf.idfobjects["BuildingSurface:Detailed".upper()] zone_surfs = [s for s in surfs if s.Zone_Name == zone.Name] floors = [s for s in zone_surfs if s.Surface_Type.upper() in ["ROOF", "CEILING"]] if debug: print(len(floors)) print([floor.area for floor in floors]) area = sum([floor.area for floor in floors]) return area
[docs]def zone_height_min2max(idf, zonename, debug=False): """zone height = max-min""" zone = idf.getobject("ZONE", zonename) surfs = idf.idfobjects["BuildingSurface:Detailed".upper()] zone_surfs = [s for s in surfs if s.Zone_Name == zone.Name] surf_xyzs = [eppy.function_helpers.getcoords(s) for s in zone_surfs] surf_xyzs = list(itertools.chain(*surf_xyzs)) surf_zs = [z for x, y, z in surf_xyzs] topz = max(surf_zs) botz = min(surf_zs) height = topz - botz return height
[docs]def zoneheight(idf, zonename, debug=False): """zone height""" zone = idf.getobject("ZONE", zonename) surfs = idf.idfobjects["BuildingSurface:Detailed".upper()] zone_surfs = [s for s in surfs if s.Zone_Name == zone.Name] floors = [s for s in zone_surfs if s.Surface_Type.upper() == "FLOOR"] roofs = [s for s in zone_surfs if s.Surface_Type.upper() == "ROOF"] if floors == [] or roofs == []: height = zone_height_min2max(idf, zonename) else: height = zone_floor2roofheight(idf, zonename) return height
[docs]def zone_floor2roofheight(idf, zonename, debug=False): """zone floor to roof height""" zone = idf.getobject("ZONE", zonename) surfs = idf.idfobjects["BuildingSurface:Detailed".upper()] zone_surfs = [s for s in surfs if s.Zone_Name == zone.Name] floors = [s for s in zone_surfs if s.Surface_Type.upper() == "FLOOR"] roofs = [s for s in zone_surfs if s.Surface_Type.upper() == "ROOF"] ceilings = [s for s in zone_surfs if s.Surface_Type.upper() == "CEILING"] topsurfaces = roofs + ceilings topz = [] for topsurface in topsurfaces: for coord in topsurface.coords: topz.append(coord[-1]) topz = max(topz) botz = [] for floor in floors: for coord in floor.coords: botz.append(coord[-1]) botz = min(botz) height = topz - botz return height
[docs]def zonevolume(idf, zonename): """zone volume""" area = zonearea(idf, zonename) height = zoneheight(idf, zonename) volume = area * height return volume
[docs]def refname2key(idf, refname): """return all keys that have the reference name""" return [item[0] for item in getallobjlists(idf, refname)]
[docs]def copyidf(idf_source): """returns an in memory copy of idf It copies only the idfobjects. epw, idfname are not copied Parameters ---------- idf_source : IDF modelmaker.IDF object. Returns ------- modelmaker.IDF object """ new_idf = IDF(StringIO("")) for key in idf_source.idfobjects: if key.upper() != "version".upper(): for obj in idf_source.idfobjects[key]: new_idf.copyidfobject(obj) return new_idf
[docs]class IDF(object): """ The IDF class holds all the information about an EnergyPlus IDF. Attributes ---------- iddname : str Name of the IDD currently being used by eppy. As a class attribute, this is set for all IDFs which are currently being processed and cannot be changed for an individual IDF. iddinfo : list Comments and metadata about fields in the IDD. block : list Field names in the IDD. idfname : str Path to the IDF file. idfobjects : list List of EpBunch objects in the IDF. model : Eplusdata object Data dictionary and list of objects for the entire model. outputtype : str How to format the output of IDF.print or IDF.save, IDF.saveas or IDF.savecopy. The options are: 'standard', 'nocomment', 'nocomment1', 'nocomment2', and 'compressed'. """ iddname = None idd_info = None block = None def __init__(self, idfname=None, epw=None): """ Parameters ---------- idfname : str, optional Path to an IDF file (which does not have to exist yet). epw : str, optional File path to the EPW file to use if running the IDF. """ # import pdb; pdb.set_trace() if idfname != None: self.idfname = idfname try: self.idfabsname = os.path.abspath(self.idfname) except TypeError as e: self.idfabsname = None pass # it is file handle. the code can handle that self.read() if epw != None: self.epw = epw self.outputtype = "standard" """ Methods to set up the IDD."""
[docs] @classmethod def setiddname(cls, iddname, testing=False): """ Set the path to the EnergyPlus IDD for the version of EnergyPlus which is to be used by eppy. Parameters ---------- iddname : str Path to the IDD file. testing : bool Flag to use if running tests since we may want to ignore the `IDDAlreadySetError`. Raises ------ IDDAlreadySetError """ if cls.iddname == None: cls.iddname = iddname cls.idd_info = None cls.block = None elif cls.iddname == iddname: pass else: if testing == False: errortxt = "IDD file is set to: %s" % (cls.iddname,) raise IDDAlreadySetError(errortxt)
[docs] @classmethod def resetidd(cls): """resets the IDD for testing. Users should not use this It will raise the exception IDDResetError Returns ------- None """ cls.iddname = None cls.idd_info = None cls.block = None cls.idd_info = None cls.idd_index = None cls.idd_version = None raise IDDResetError("IDD should never be reset unless you are doing test runs")
[docs] @classmethod def getiddname(cls): """Get the name of the current IDD used by eppy. Returns ------- str """ return cls.iddname
[docs] @classmethod def setidd(cls, iddinfo, iddindex, block, idd_version): """Set the IDD to be used by eppy. Parameters ---------- iddinfo : list Comments and metadata about fields in the IDD. block : list Field names in the IDD. """ cls.idd_info = iddinfo cls.block = block cls.idd_index = iddindex cls.idd_version = idd_version
"""Methods to do with reading an IDF."""
[docs] def initread(self, idfname): """ Use the current IDD and read an IDF from file. If the IDD has not yet been initialised then this is done first. Parameters ---------- idf_name : str Path to an IDF file. """ with open(idfname, "r") as _: # raise nonexistent file error early if idfname doesn't exist pass iddfhandle = StringIO(iddcurrent.iddtxt) if self.getiddname() == None: self.setiddname(iddfhandle) self.idfname = idfname try: self.idfabsname = os.path.abspath(self.idfname) except TypeError as e: pass # it is file handle. the code can handle that self.read()
[docs] def initreadtxt(self, idftxt): """ Use the current IDD and read an IDF from text data. If the IDD has not yet been initialised then this is done first. Parameters ---------- idftxt : str Text representing an IDF file. """ iddfhandle = StringIO(iddcurrent.iddtxt) if self.getiddname() == None: self.setiddname(iddfhandle) idfhandle = StringIO(idftxt) self.idfname = idfhandle try: self.idfabsname = os.path.abspath(self.idfname) except TypeError as e: pass # it is file handle. the code can handle that self.read()
[docs] def read(self): """ Read the IDF file and the IDD file. If the IDD file had already been read, it will not be read again. Read populates the following data structures: - idfobjects : list - model : list - idd_info : list - idd_index : dict """ if self.getiddname() == None: errortxt = ( "IDD file needed to read the idf file. " "Set it using IDF.setiddname(iddfile)" ) raise IDDNotSetError(errortxt) readout = idfreader1( self.idfname, self.iddname, self, commdct=self.idd_info, block=self.block ) (self.idfobjects, block, self.model, idd_info, idd_index, idd_version) = readout self.__class__.setidd(idd_info, idd_index, block, idd_version)
"""Methods to do with creating a new blank IDF object."""
[docs] def new(self, fname=None): """Create a blank new idf file. Filename is optional. Parameters ---------- fname : str, optional Path to an IDF. This does not need to be set at this point. """ self.initnew(fname)
[docs] def initnew(self, fname): """ Use the current IDD and create a new empty IDF. If the IDD has not yet been initialised then this is done first. Parameters ---------- fname : str, optional Path to an IDF. This does not need to be set at this point. """ iddfhandle = StringIO(iddcurrent.iddtxt) if self.getiddname() == None: self.setiddname(iddfhandle) idfhandle = StringIO("") self.idfname = idfhandle try: self.idfabsname = os.path.abspath(self.idfname) except TypeError as e: pass # it is file handle. the code can handle that self.read() if fname: self.idfname = fname try: self.idfabsname = os.path.abspath(self.idfname) except TypeError as e: pass # it is file handle. the code can handle that
"""Methods to do with manipulating the objects in an IDF object."""
[docs] def newidfobject(self, key, defaultvalues=True, **kwargs): """ Add a new idfobject to the model. If you don't specify a value for a field, the default value will be set. For example :: newidfobject("CONSTRUCTION") newidfobject("CONSTRUCTION", Name='Interior Ceiling_class', Outside_Layer='LW Concrete', Layer_2='soundmat') Parameters ---------- key : str The type of IDF object. defaultvalues: boolean default is True. If True default values WILL be set. If False, default values WILL NOT be set **kwargs Keyword arguments in the format `field=value` used to set the value of fields in the IDF object when it is created. Returns ------- EpBunch object """ obj = newrawobject( self.model, self.idd_info, key, block=self.block, defaultvalues=defaultvalues, ) # add fields if there are not enough fields in the IDD to match the fields in kwargs dtls = self.model.dtls key = obj[0].upper() key_i = dtls.index(key) objfields = [comm.get("field") for comm in self.idd_info[key_i]] # check if there are enough fields in the IDD to match the kwargs if len(kwargs) > ( len(objfields) - 1 ): # objfields has placeholder for key. So subtract 1 # -- increase the number of fields in the IDD (in block and commdct) n = len(kwargs) - ( len(objfields) - 1 ) # objfields has placeholder for key. So subtract 1 key_txt = key obj_i = key_i block = self.block commdct = self.idd_info objfields = extff.increaseIDDfields(block, commdct, obj_i, key_txt, n) abunch = obj2bunch(self.model, self.idd_info, obj) self.idfobjects[key].append(abunch) for k, v in list(kwargs.items()): abunch[k] = v return abunch
[docs] def popidfobject(self, key, index): """Pop an IDF object from the IDF. Parameters ---------- key : str The type of IDF object. index : int The index of the object to pop. Returns ------- EpBunch object. """ return self.idfobjects[key].pop(index)
[docs] def removeidfobject(self, idfobject): """Remove an IDF object from the IDF. Parameters ---------- idfobject : EpBunch object The IDF object to remove. """ key = idfobject.key.upper() self.idfobjects[key].remove(idfobject)
[docs] def removeallidfobjects(self, idfobject): """Remove all IDF object of a certain type from the IDF. Parameters ---------- idfobject : EpBunch object The IDF object to remove. """ while len(self.idfobjects[idfobject]) > 0: self.popidfobject(idfobject, 0)
[docs] def copyidfobject(self, idfobject): """Add an IDF object to the IDF. Parameters ---------- idfobject : EpBunch object The IDF object to remove. This usually comes from another idf file, or it can be used to copy within this idf file. """ return addthisbunch(self.idfobjects, self.model, self.idd_info, idfobject, self)
[docs] def getobject(self, key, name): """Fetch an IDF object given key and name. Parameters ---------- key : str The type of IDF object. name : str The name of the object to fetch. Returns ------- EpBunch object. """ return getobject(self.idfobjects, key, name)
[docs] def getextensibleindex(self, key, name): """ Get the index of the first extensible item. Only for internal use. # TODO : hide this Parameters ---------- key : str The type of IDF object. name : str The name of the object to fetch. Returns ------- int """ return getextensibleindex(self.idfobjects, self.model, self.idd_info, key, name)
[docs] def removeextensibles(self, key, name): """ Remove extensible items in the object of key and name. Only for internal use. # TODO : hide this Parameters ---------- key : str The type of IDF object. name : str The name of the object to fetch. Returns ------- EpBunch object """ return removeextensibles(self.idfobjects, self.model, self.idd_info, key, name)
"""Methods to do with outputting an IDF."""
[docs] def printidf(self): """Print the IDF.""" print(self.idfstr())
[docs] def idfstr(self): """String representation of the IDF. Returns ------- str """ if self.outputtype == "standard": astr = "" else: astr = self.model.__repr__() if self.outputtype == "standard": astr = "" dtls = self.model.dtls for objname in dtls: for obj in self.idfobjects[objname]: astr = astr + obj.__repr__() elif self.outputtype == "nocomment": return astr elif self.outputtype == "nocomment1": slist = astr.split("\n") slist = [item.strip() for item in slist] astr = "\n".join(slist) elif self.outputtype == "nocomment2": slist = astr.split("\n") slist = [item.strip() for item in slist] slist = [item for item in slist if item != ""] astr = "\n".join(slist) elif self.outputtype == "compressed": slist = astr.split("\n") slist = [item.strip() for item in slist] astr = " ".join(slist) else: raise ValueError("%s is not a valid outputtype" % self.outputtype) return astr
[docs] def save(self, filename=None, lineendings="default", encoding="latin-1"): """ Save the IDF as a text file with the optional filename passed, or with the current idfname of the IDF. Parameters ---------- filename : str, optional Filepath to save the file. If None then use the IDF.idfname parameter. Also accepts a file handle. lineendings : str, optional Line endings to use in the saved file. Options are 'default', 'windows' and 'unix' the default is 'default' which uses the line endings for the current system. encoding : str, optional Encoding to use for the saved file. The default is 'latin-1' which is compatible with the EnergyPlus IDFEditor. """ if self.idfabsname is None and isinstance(self.idfname, str): # this happens when the user sets self.idfname to an in-memory idf self.idfabsname = os.path.abspath(self.idfname) if filename is None: filename = self.idfabsname s = self.idfstr() if lineendings == "default": system = platform.system() s = "!- {} Line endings \n".format(system) + s slines = s.splitlines() s = os.linesep.join(slines) elif lineendings == "windows": s = "!- Windows Line endings \n" + s slines = s.splitlines() s = "\r\n".join(slines) elif lineendings == "unix": s = "!- Unix Line endings \n" + s slines = s.splitlines() s = "\n".join(slines) s = s.encode(encoding) try: with open(filename, "wb") as idf_out: idf_out.write(s) except TypeError: # in the case that filename is a file handle try: filename.write(s) except TypeError: filename.write(s.decode(encoding)) try: pass except Exception as e: raise e else: pass
[docs] def saveas(self, filename, lineendings="default", encoding="latin-1"): """Save the IDF as a text file with the filename passed. Parameters ---------- filename : str Filepath to to set the idfname attribute to and save the file as. lineendings : str, optional Line endings to use in the saved file. Options are 'default', 'windows' and 'unix' the default is 'default' which uses the line endings for the current system. encoding : str, optional Encoding to use for the saved file. The default is 'latin-1' which is compatible with the EnergyPlus IDFEditor. """ self.idfname = filename try: self.idfabsname = os.path.abspath(self.idfname) except TypeError as e: pass # it is file handle. the code can handle that self.save(filename, lineendings, encoding)
[docs] def savecopy(self, filename, lineendings="default", encoding="latin-1"): """Save a copy of the file with the filename passed. Parameters ---------- filename : str Filepath to save the file. lineendings : str, optional Line endings to use in the saved file. Options are 'default', 'windows' and 'unix' the default is 'default' which uses the line endings for the current system. encoding : str, optional Encoding to use for the saved file. The default is 'latin-1' which is compatible with the EnergyPlus IDFEditor. """ self.save(filename, lineendings, encoding)
[docs] @wrapped_help_text(run) def run(self, **kwargs): """Run an IDF file with a given EnergyPlus weather file. This is a wrapper for the EnergyPlus command line interface. Parameters ---------- kwargs : See eppy.runner.functions.run() """ def _makerandomword(length=6): import uuid fullword = uuid.uuid4().hex return fullword[:length] def _maketempname(idfname, randlength=6): idfname = str(idfname) t_suffix = _makerandomword(randlength) randomname = f"{t_suffix}.idf" if str(idfname[-4:]) == ".idf": temp_name = f"{idfname[:-4]}_{randomname}" else: temp_name = randomname return temp_name # write the IDF to the current directory idfname = self.idfname idfabsname = self.idfabsname temp_name = _maketempname(idfname) self.saveas(temp_name) # if `idd` is not passed explicitly, use the IDF.iddname idd = kwargs.pop("idd", self.iddname) epw = kwargs.pop("weather", self.epw) try: run(self, weather=epw, idd=idd, **kwargs) finally: self.idfname = idfname self.idfabsname = idfabsname os.remove(temp_name)
[docs] def runfile(self, **kwargs): """Run an IDF file on the disk with a given EnergyPlus weather file. This is a wrapper for the EnergyPlus command line interface. This is different from run() which can run a file that is only in memory Parameters ---------- kwargs : See eppy.runner.functions.run() """ idd = kwargs.pop("idd", self.iddname) epw = kwargs.pop("weather", self.epw) try: run(self, weather=epw, idd=idd, **kwargs) finally: # os.remove("in.idf") pass
[docs] def getiddgroupdict(self): """Return a idd group dictionary sample: {'Plant-Condenser Loops': ['PlantLoop', 'CondenserLoop'], 'Compliance Objects': ['Compliance:Building'], 'Controllers': ['Controller:WaterCoil', 'Controller:OutdoorAir', 'Controller:MechanicalVentilation', 'AirLoopHVAC:ControllerList'], ...} Returns ------- dict """ return iddgroups.commdct2grouplist(self.idd_info)
[docs] def copyidf(self): """Return a copy of IDF Returns ------- modeleditor.IDF object """ return copyidf(self)