santoshphilip / eppy

scripting language for E+, Energyplus
MIT License
155 stars 67 forks source link

Parsing issues when using older IDD files/specifying IDD files for use. #439

Closed JKesslerPhD closed 6 months ago

JKesslerPhD commented 6 months ago

I'm setting the IDD to 9.5 by providing a path to the EnergyPlus 9.5 IDD:

idd_path = V9-5-0-Energy+.idd

IDF.setiddname(idd_path)

the older IDD files do not have a "Space Name" attribute. However, the IDF parser appears to be adding keys in an order in which it defines a "Space_Name" attribute which then incorrectly populates all other attributes in the IDD file.

I'm not sure if the issue is that the IDF.setiddname function does not actually result in the IDF parser utilizing the IDD file, or if there is something else going on.

Example below. A "Space Name" was added, when the value populated ("Outdoors") actually corresponds to the "Outside Boundary Condition" attribute. As seen, the "Number of vertices" attribute is then populated with a value that should be "Vertex 1 XCoordinate".

BuildingSurface:Detailed, Roof_right_unit1, !- Name Wall, !- Surface Type Gable_end, !- Construction Name attic_unit1, !- Zone Name Outdoors, !- Space Name , !- Outside Boundary Condition SunExposed, !- Outside Boundary Condition Object WindExposed, !- Sun Exposure , !- Wind Exposure 3, !- View Factor to Ground 12.1330909462833, !- Number of Vertices 0, !- Vertex 1 Xcoordinate 5.19302682926829, !- Vertex 1 Ycoordinate 12.1330909462833, !- Vertex 1 Zcoordinate 9.09981820971244, !- Vertex 2 Xcoordinate 5.19302682926829, !- Vertex 2 Ycoordinate 12.1330909462833, !- Vertex 2 Zcoordinate 4.54990910485622, !- Vertex 3 Xcoordinate 6.6995631975537; !- Vertex 3 Ycoordinate

santoshphilip commented 6 months ago

I see the error happening in your example. Let me try a few examples on my own and see what I get

santoshphilip commented 6 months ago

I am not seeing a Space Name in the IDD file for E+ version 9.5.0

The snippet of the IDD file looks like this:

BuildingSurface:Detailed,
  \memo Allows for detailed entry of building heat transfer surfaces. Does not include subsurfaces such as windows or doors.
  \extensible:3 -- duplicate last set of x,y,z coordinates (last 3 fields), remembering to remove ; from "inner" fields.
  \format vertices
  \min-fields 19
  A1 , \field Name
       \required-field
       \type alpha
       \reference SurfaceNames
       \reference SurfAndSubSurfNames
       \reference AllHeatTranSurfNames
       \reference OutFaceEnvNames
       \reference AllHeatTranAngFacNames
       \reference RadiantSurfaceNames
       \reference AllShadingAndHTSurfNames
       \reference FloorSurfaceNames
  A2 , \field Surface Type
       \required-field
       \type choice
       \key Floor
       \key Wall
       \key Ceiling
       \key Roof
  A3 , \field Construction Name
       \required-field
       \note To be matched with a construction in this input file
       \type object-list
       \object-list ConstructionNames
  A4 , \field Zone Name
       \required-field
       \note Zone the surface is a part of
       \type object-list
       \object-list ZoneNames
  A5 , \field Outside Boundary Condition
       \required-field
       \type choice
       \key Adiabatic
       \key Surface
       \key Zone
       \key Outdoors
       \key Foundation
       \key Ground
       \key GroundFCfactorMethod
       \key OtherSideCoefficients
       \key OtherSideConditionsModel
       \key GroundSlabPreprocessorAverage
       \key GroundSlabPreprocessorCore
       \key GroundSlabPreprocessorPerimeter
       \key GroundBasementPreprocessorAverageWall
       \key GroundBasementPreprocessorAverageFloor
       \key GroundBasementPreprocessorUpperWall
       \key GroundBasementPreprocessorLowerWall
  A6,  \field Outside Boundary Condition Object
       \type object-list
       \object-list OutFaceEnvNames
       \note Non-blank only if the field Outside Boundary Condition is Surface,
       \note Zone, OtherSideCoefficients or OtherSideConditionsModel
       \note If Surface, specify name of corresponding surface in adjacent zone or
       \note specify current surface name for internal partition separating like zones
       \note If Zone, specify the name of the corresponding zone and
       \note the program will generate the corresponding interzone surface
       \note If Foundation, specify the name of the corresponding Foundation object and
       \note the program will calculate the heat transfer appropriately
       \note If OtherSideCoefficients, specify name of SurfaceProperty:OtherSideCoefficients
       \note If OtherSideConditionsModel, specify name of SurfaceProperty:OtherSideConditionsModel
  A7 , \field Sun Exposure
       \type choice
       \key SunExposed
       \key NoSun
       \default SunExposed
  A8,  \field Wind Exposure
       \type choice
       \key WindExposed
       \key NoWind
       \default WindExposed
  N1,  \field View Factor to Ground
       \type real
       \note From the exterior of the surface
       \note Unused if one uses the "reflections" options in Solar Distribution in Building input
       \note unless a DaylightingDevice:Shelf or DaylightingDevice:Tubular object has been specified.
       \note autocalculate will automatically calculate this value from the tilt of the surface
       \autocalculatable
       \minimum 0.0
       \maximum 1.0
       \default autocalculate
  N2 , \field Number of Vertices
       \note shown with 120 vertex coordinates -- extensible object
       \note  "extensible" -- duplicate last set of x,y,z coordinates (last 3 fields),
       \note remembering to remove ; from "inner" fields.
       \note for clarity in any error messages, renumber the fields as well.
       \note (and changing z terminator to a comma "," for all but last one which needs a semi-colon ";")
       \autocalculatable
       \minimum 3
       \default autocalculate
       \note vertices are given in GlobalGeometryRules coordinates -- if relative, all surface coordinates
       \note are "relative" to the Zone Origin. If world, then building and zone origins are used
       \note for some internal calculations, but all coordinates are given in an "absolute" system.
  N3,  \field Vertex 1 X-coordinate
       \begin-extensible
       \required-field
       \units m
       \type real
  N4 , \field Vertex 1 Y-coordinate
       \required-field
       \units m
       \type real
  N5 , \field Vertex 1 Z-coordinate
       \required-field
       \units m
       \type real
  N6,  \field Vertex 2 X-coordinate
       \required-field
       \units m
       \type real
  N7,  \field Vertex 2 Y-coordinate
       \required-field
       \units m
       \type real
  N8,  \field Vertex 2 Z-coordinate
       \required-field
       \units m
       \type real
  N9,  \field Vertex 3 X-coordinate
       \required-field
       \units m
       \type real
  N10, \field Vertex 3 Y-coordinate
       \required-field
       \units m
       \type real
  N11, \field Vertex 3 Z-coordinate
       \required-field
       \units m
       \type real
  N12, \field Vertex 4 X-coordinate
       \units m
       \type real
  N13, \field Vertex 4 Y-coordinate
       \type real
       \units m
  N14, \field Vertex 4 Z-coordinate
       \units m
       \type real
  N15, \field Vertex 5 X-coordinate
       \units m
       \type real
  N16, \field Vertex 5 Y-coordinate
       \type real
       \units m
  N17, \field Vertex 5 Z-coordinate
       \units m
       \type real

 <------ snip ----->

  N360, \field Vertex 120 X-coordinate
       \units m
       \type real
  N361, \field Vertex 120 Y-coordinate
       \units m
       \type real
  N362; \field Vertex 120 Z-coordinate
       \units m
       \type real
JKesslerPhD commented 6 months ago

Yeah, there is no 'space name' field in the 9.5 IDD, but the parser seems to want to populate a space name field. I think the issue is that the IDF.setiddname() method isn't forcing the IDF parser to use the specified IDD file, and is instead defaulting to 23.2.

IDF.idd_version

Out[163]: (9, 5, 0)

IDF.idd_info[0]

Out[164]: [{'memo': ['Specifies the EnergyPlus version of the IDF file.'], 'unique-object': [''], 'format': ['singleLine'], 'group': 'Simulation Parameters', 'idfobj': 'Version'}, {'field': ['Version Identifier'], 'default': ['23.2']}]

santoshphilip commented 6 months ago

I also looked at the file Energy+.schema.epJSON I am not seeing a space_name there either

I don't think there is a Space Name in E+ version 9.5.0

I tested with some sample code and it does not add Space Name field. (Maybe your IDD is corrupted ? )

import eppy

idf = eppy.newidf(version="9.5") 
# I am using an easier to use function - documented here
# https://eppy.readthedocs.io/en/master/eppy.html#module-eppy

surf = idf.newidfobject("BuildingSurface:Detailed", Name='asurface')

surf

BUILDINGSURFACE:DETAILED,
    asurface,                 !- Name
    ,                         !- Surface Type
    ,                         !- Construction Name
    ,                         !- Zone Name
    ,                         !- Outside Boundary Condition
    ,                         !- Outside Boundary Condition Object
    SunExposed,               !- Sun Exposure
    WindExposed,              !- Wind Exposure
    autocalculate,            !- View Factor to Ground
    autocalculate;            !- Number of Vertices
santoshphilip commented 6 months ago

tested with E+ version 22.2 This has Space Name

Let me know if you are able to find out what is going on in your example

see code:

import eppy
idf = eppy.newidf(version="22.2")
surf = idf.newidfobject("BuildingSurface:Detailed", Name='asurface')

surf

BUILDINGSURFACE:DETAILED,
    asurface,                 !- Name
    ,                         !- Surface Type
    ,                         !- Construction Name
    ,                         !- Zone Name
    ,                         !- Space Name
    ,                         !- Outside Boundary Condition
    ,                         !- Outside Boundary Condition Object
    SunExposed,               !- Sun Exposure
    WindExposed,              !- Wind Exposure
    autocalculate,            !- View Factor to Ground
    autocalculate;            !- Number of Vertices
santoshphilip commented 6 months ago

responding to your earlier comment. eppy will not use the wrong IDD. It does not default to any IDD. It can only use what you specified.

Can you look inside the IDD and see if it has Space Name in it ?

JKesslerPhD commented 6 months ago

I think I figured it out -- if I load a more recent IDD file first (e.g. 23.2) then the IDF.setiddname() method won't allow the IDD file to be updated. I had been catching and ignoring that exception.

Is there functionality to unset and reset the idd file that is used?

santoshphilip commented 6 months ago

Also try this

print(idf.idd_version)
santoshphilip commented 6 months ago

looks like you already did that idd_version

Can you post your code snippet that created the issue ?

santoshphilip commented 6 months ago

I read your comment more carefully. it is very strange behavior - what you are seeing should not happen

Let me try a few more tests.

In any case please post your code

JKesslerPhD commented 6 months ago

class IDFModel():
    def __init__(self, idf_path, idd_path):
        self.version = ()
        self.surfaces = []
        self.materials = []
        self.constructions = []
        self.idf = self.load_idf(idf_path, idd_path)
        self.floor_space = None

def load_idf(self, idf_path, idd_path):        
        try: 
            IDF.setiddname(idd_path)
            return IDF(idf_path)
        except Exception as e:
            print(f"Unable to load IDF file due to error {e}")
...

m1 = IDFModel(idf_1, 23_2path)
m2 = IDFModel(idf_2, 9_5path)
santoshphilip commented 6 months ago

I think I figured it out -- if I load a more recent IDD file first (e.g. 23.2) then the IDF.setiddname() method won't allow the IDD file to be updated. I had been catching and ignoring that exception.

Is there functionality to unset and reset the idd file that is used?

from tests/pytest_helpers.py. this can break things badly - if you have one IDF file in one version and another IDF file in another version. Remember that eppy can have only one IDD file at any moment

def safeIDDreset():
    """reset the IDD for testing and catch the exception"""
    try:
        eppy.modeleditor.IDF.resetidd()
    except eppy.modeleditor.IDDResetError as e:
        pass
santoshphilip commented 6 months ago

I see from your code that you want to to have files of different versions open at the same time. eppy will not work for that :-(

Allowing for this open a a whole can of worms. If you try to copy an idfobject from one file to another, they may have different fields I don't know of an easy way to make this happen.

So I explicitly stopped eppy from opening files of different version at the same time

JKesslerPhD commented 6 months ago

I've gone ahead and modified the modeleditor.py file to better meet my needs by adding an overwrite flag that defaults to false.

    @classmethod
    def setiddname(cls, iddname, testing=False, overwrite=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 or overwrite:
            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)
santoshphilip commented 6 months ago

that will work for your purposes.

Use with care. Allowing for switching IDD midstream can lead to hard to resolve errors. Once you have reset the IDD, you cannot use any files that were opened with a different IDD unless you reset IDD again. This can be hard to keep track of

santoshphilip commented 6 months ago

close this issue if you think it has been resolved. I am signing off :-)

JKesslerPhD commented 6 months ago

Thanks!

Functionality for reading seems to work, as the dictionary is populated prior to resetting the IDD. I assume writing is absolutely broken as the IDD will be whatever the last-specified IDD is. I assume some namespacing changes could fix that too.

m1.idf.idfobjects["BUILDINGSURFACE:DETAILED"][4]
Out[29]: 

BuildingSurface:Detailed,
    Dining_Wall_North-PPAutoCreateOther,    !- Name
    Wall,                     !- Surface Type
    int-walls,                !- Construction Name
    Kitchen,                  !- Zone Name
    ,                         !- Outside Boundary Condition
    Surface,                  !- Outside Boundary Condition Object
    Dining_Wall_North,        !- Sun Exposure
    NoSun,                    !- Wind Exposure
    NoWind,                   !- View Factor to Ground
    AutoCalculate,            !- Number of Vertices
    4,                        !- Vertex 1 Xcoordinate
    0,                        !- Vertex 1 Ycoordinate
    16.4427,                  !- Vertex 1 Zcoordinate
    3.0488,                   !- Vertex 2 Xcoordinate
    0,                        !- Vertex 2 Ycoordinate
    16.4427,                  !- Vertex 2 Zcoordinate
    0,                        !- Vertex 3 Xcoordinate
    22.6087,                  !- Vertex 3 Ycoordinate
    16.4427,                  !- Vertex 3 Zcoordinate
    0,                        !- Vertex 4 Xcoordinate
    22.6087,                  !- Vertex 4 Ycoordinate
    16.4427,                  !- Vertex 4 Zcoordinate
    3.0488;                   !- Vertex 5 Xcoordinate

m2.idf.idfobjects["BUILDINGSURFACE:DETAILED"][4]
Out[30]: 

BuildingSurface:Detailed,
    Dining_Wall_North-PPAutoCreateOther,    !- Name
    Wall,                     !- Surface Type
    int-walls,                !- Construction Name
    Kitchen,                  !- Zone Name
    ,                         !- Space Name
    Surface,                  !- Outside Boundary Condition
    Dining_Wall_North,        !- Outside Boundary Condition Object
    NoSun,                    !- Sun Exposure
    NoWind,                   !- Wind Exposure
    AutoCalculate,            !- View Factor to Ground
    4,                        !- Number of Vertices
    0,                        !- Vertex 1 Xcoordinate
    16.4427,                  !- Vertex 1 Ycoordinate
    3.0488,                   !- Vertex 1 Zcoordinate
    0,                        !- Vertex 2 Xcoordinate
    16.4427,                  !- Vertex 2 Ycoordinate
    0,                        !- Vertex 2 Zcoordinate
    22.6087,                  !- Vertex 3 Xcoordinate
    16.4427,                  !- Vertex 3 Ycoordinate
    0,                        !- Vertex 3 Zcoordinate
    22.6087,                  !- Vertex 4 Xcoordinate
    16.4427,                  !- Vertex 4 Ycoordinate
    3.0488;                   !- Vertex 4 Zcoordinate