Module EnergyModel

Expand source code
# Copyright (C) 2024
# Wassim Jabi <wassim.jabi@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program 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 Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with
# this program. If not, see <https://www.gnu.org/licenses/>.

import topologic_core as topologic
import shutil
import math
from collections import OrderedDict
import os
from os.path import exists
from datetime import datetime
import warnings

try:
    from tqdm.auto import tqdm
except:
    print("EnergyModel - Installing required tqdm library.")
    try:
        os.system("pip install tqdm")
    except:
        os.system("pip install tqdm --user")
    try:
        from tqdm.auto import tqdm
        print("EnergyModel - tqdm library installed correctly.")
    except:
        warnings.warn("EnergyModel - Error: Could not import tqdm.")

class EnergyModel:
    '''
    @staticmethod
    def ByOSMFile(file):
        """
        Creates an EnergyModel from the input OSM file path.

        Parameters
        ----------
        path : string
            The path to the input .OSM file.

        Returns
        -------
        openstudio.openstudiomodelcore.Model
            The OSM model.

        """
        if not file:
            print("EnergyModel.ByOSMFile - Error: The input path is not valid. Returning None.")
            return None
        osModel = file.read()
        if osModel.isNull():
            print("EnergyModel.ByOSMFile - Error: The openstudio model is null. Returning None.")
            return None
        else:
            osModel = osModel.get()
        return osModel
    '''

    @staticmethod
    def ByOSMPath(path: str):
        """
        Creates an EnergyModel from the input OSM file path.
        
        Parameters
        ----------
        path : string
            The path to the input .OSM file.

        Returns
        -------
        openstudio.openstudiomodelcore.Model
            The OSM model.

        """
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
        except:
            print("EnergyModel.ByOSMPath - Information: Installing required openstudio library.")
            try:
                os.system("pip install openstudio")
            except:
                os.system("pip install openstudio --user")
            try:
                import openstudio
                openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
                print("EnergyModel.ByOSMPath - Information: openstudio library installed correctly.")
            except:
                warnings.warn("EnergyModel.ByOSMPath - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
                return None
        
        if not path:
            print("EnergyModel.ByOSMPath - Error: The input path is not valid. Returning None.")
            return None
        translator = openstudio.osversion.VersionTranslator()
        osmPath = openstudio.openstudioutilitiescore.toPath(path)
        osModel = translator.loadModel(osmPath)
        if osModel.isNull():
            print("EnergyModel.ByImportedOSM - Error: The openstudio model is null. Returning None.")
            return None
        else:
            osModel = osModel.get()
        return osModel
    
    @staticmethod
    def ByTopology(building,
                   shadingSurfaces  = None,
                   osModelPath : str = None,
                   weatherFilePath : str = None,
                   designDayFilePath  : str = None,
                   floorLevels : list = None,
                   buildingName : str = "TopologicBuilding",
                   buildingType : str = "Commercial",
                   northAxis : float = 0.0,
                   glazingRatio : float = 0.0,
                   coolingTemp : float = 25.0,
                   heatingTemp : float = 20.0,
                   defaultSpaceType : str = "189.1-2009 - Office - WholeBuilding - Lg Office - CZ4-8",
                   spaceNameKey : str = "TOPOLOGIC_name",
                   spaceTypeKey : str = "TOPOLOGIC_type"):
        """
            Creates an EnergyModel from the input topology and parameters.

        Parameters
        ----------
        building : topologic_core.CellComplex or topologic_core.Cell
            The input building topology.
        shadingSurfaces : topologic_core.Topology , optional
            The input topology for shading surfaces. The default is None.
        osModelPath : str , optional
            The path to the template OSM file. The default is "./assets/EnergyModel/OSMTemplate-OfficeBuilding-3.5.0.osm".
        weatherFilePath : str , optional
            The input energy plus weather (epw) file. The default is "./assets/EnergyModel/GBR_London.Gatwick.037760_IWEC.epw".
        designDayFilePath : str , optional
            The input design day (ddy) file path. The default is "./assets/EnergyModel/GBR_London.Gatwick.037760_IWEC.ddy",
        floorLevels : list , optional
            The list of floor level Z heights including the lowest most and the highest most levels. If set to None, this method will attempt to
            find the floor levels from the horizontal faces of the input topology
        buildingName : str , optional
            The desired name of the building. The default is "TopologicBuilding".
        buildingType : str , optional
            The building type. The default is "Commercial".
        defaultSpaceType : str , optional
            The default space type to apply to spaces that do not have a type assigned in their dictionary. The default is "189.1-2009 - Office - WholeBuilding - Lg Office - CZ4-8".
        northAxis : float , optional
            The counter-clockwise angle in degrees from the positive Y-axis representing the direction of the north axis. The default is 0.0.
        glazingRatio : float , optional
            The glazing ratio (ratio of windows to wall) to use for exterior vertical walls that do not have apertures. If you do not wish to use a glazing ratio, set it to 0. The default is 0.
        coolingTemp : float , optional
            The desired temperature in degrees at which the cooling system should activate. The default is 25.0.
        heatingTemp : float , optional
            The desired temperature in degrees at which the heating system should activate. The default is 25.0..
        spaceNameKey : str , optional
            The dictionary key to use to find the space name value. The default is "Name".
        spaceTypeKey : str , optional
            The dictionary key to use to find the space type value. The default is "Type".

        Returns
        -------
        openstudio.openstudiomodelcore.Model
            The created OSM model.

        """
        from topologicpy.Face import Face
        from topologicpy.Cell import Cell
        from topologicpy.Topology import Topology
        from topologicpy.Dictionary import Dictionary
        from topologicpy.Aperture import Aperture
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
        except:
            print("EnergyModel.ByTopology - Information: Installing required openstudio library.")
            try:
                os.system("pip install openstudio")
            except:
                os.system("pip install openstudio --user")
            try:
                import openstudio
                openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
                print("EnergyModel.ByTopology - Information: openstudio library installed correctly.")
            except:
                warnings.warn("EnergyModel.ByTopology - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
                return None

        def getKeyName(d, keyName):
            keys = d.Keys()
            for key in keys:
                if key.lower() == keyName.lower():
                    return key
            return None
        
        def createUniqueName(name, nameList, number):
            if number > 9999:
                return name+"_9999"
            if not (name in nameList):
                return name
            elif not ((name+"_"+"{:04d}".format(number)) in nameList):
                return name+"_"+"{:04d}".format(number)
            else:
                return createUniqueName(name,nameList, number+1)
        
        def getFloorLevels(building):
            from topologicpy.Vertex import Vertex
            from topologicpy.Cell import Cell
            from topologicpy.CellComplex import CellComplex

            if Topology.IsInstance(building, "CellComplex"):
                d = CellComplex.Decompose(building)
                bhf = d['bottomHorizontalFaces']
                ihf = d['internalHorizontalFaces']
                thf = d ['topHorizontalFaces']
                hf = bhf+ihf+thf
            elif Topology.IsInstance(building, "Cell"):
                d = Cell.Decompose(building)
                bhf = d['bottomHorizontalFaces']
                thf = d ['topHorizontalFaces']
                hf = bhf+thf
            else:
                return None
            floorLevels = [Vertex.Z(Topology.Centroid(f)) for f in hf]
            floorLevels = list(set(floorLevels))
            floorLevels.sort()
            return floorLevels
        
        if not osModelPath:
            import os
            osModelPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "OSMTemplate-OfficeBuilding-3.5.0.osm")
        if not weatherFilePath or not designDayFilePath:
            import os
            weatherFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.epw")
            designDayFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.ddy")
        translator = openstudio.osversion.VersionTranslator()
        osmFile = openstudio.openstudioutilitiescore.toPath(osModelPath)
        osModel = translator.loadModel(osmFile)
        if osModel.isNull():
            print("EnergyModel.ByTopology - Error: The openstudio model is null. Returning None.")
            return None
        else:
            osModel = osModel.get()
        osEPWFile = openstudio.openstudioutilitiesfiletypes.EpwFile.load(openstudio.toPath(weatherFilePath))
        if osEPWFile.is_initialized():
            osEPWFile = osEPWFile.get()
            openstudio.model.WeatherFile.setWeatherFile(osModel, osEPWFile)
        ddyModel = openstudio.openstudioenergyplus.loadAndTranslateIdf(openstudio.toPath(designDayFilePath))
        if ddyModel.is_initialized():
            ddyModel = ddyModel.get()
            for ddy in ddyModel.getObjectsByType(openstudio.IddObjectType("OS:SizingPeriod:DesignDay")):
                osModel.addObject(ddy.clone())
        else:
            print("EnergyModel.ByTopology - Error: The ddy file is not initialized. Returning None.")
            return None
        osBuilding = osModel.getBuilding()
        if not floorLevels:
            floorLevels = getFloorLevels(building)
        osBuilding.setStandardsNumberOfStories(len(floorLevels) - 1)
        osBuilding.setNominalFloortoFloorHeight(max(floorLevels) / osBuilding.standardsNumberOfStories().get())
        osBuilding.setDefaultConstructionSet(osModel.getDefaultConstructionSets()[0])
        osBuilding.setDefaultScheduleSet(osModel.getDefaultScheduleSets()[0])
        osBuilding.setName(buildingName)
        osBuilding.setStandardsBuildingType(buildingType)
        osBuilding.setSpaceType(osModel.getSpaceTypeByName(defaultSpaceType).get())
        for storyNumber in range(osBuilding.standardsNumberOfStories().get()):
            osBuildingStory = openstudio.model.BuildingStory(osModel)
            osBuildingStory.setName("STORY_" + str(storyNumber))
            osBuildingStory.setNominalZCoordinate(floorLevels[storyNumber])
            osBuildingStory.setNominalFloortoFloorHeight(osBuilding.nominalFloortoFloorHeight().get())
        osBuilding.setNorthAxis(northAxis)
        heatingScheduleConstant = openstudio.model.ScheduleConstant(osModel)
        heatingScheduleConstant.setValue(heatingTemp)
        coolingScheduleConstant = openstudio.model.ScheduleConstant(osModel)
        coolingScheduleConstant.setValue(coolingTemp)
        osThermostat = openstudio.model.ThermostatSetpointDualSetpoint(osModel)
        osThermostat.setHeatingSetpointTemperatureSchedule(heatingScheduleConstant)
        osThermostat.setCoolingSetpointTemperatureSchedule(coolingScheduleConstant)
        osBuildingStorys = list(osModel.getBuildingStorys())
        osBuildingStorys.sort(key=lambda x: x.nominalZCoordinate().get())
        osSpaces = []
        spaceNames = []
        if Topology.IsInstance(building, "CellComplex"):
            building_cells = Topology.SubTopologies(building, "Cell")
        elif Topology.IsInstance(building, "Cell"):
            building_cells = [building]
        for spaceNumber, buildingCell in enumerate(building_cells):
            osSpace = openstudio.model.Space(osModel)
            osSpaceZ = buildingCell.CenterOfMass().Z()
            osBuildingStory = osBuildingStorys[0]
            for x in osBuildingStorys:
                osBuildingStoryZ = x.nominalZCoordinate().get()
                if osBuildingStoryZ + x.nominalFloortoFloorHeight().get() < osSpaceZ:
                    continue
                if osBuildingStoryZ < osSpaceZ:
                    osBuildingStory = x
                break
            osSpace.setBuildingStory(osBuildingStory)
            cellDictionary = Topology.Dictionary(buildingCell)
            if not cellDictionary == None:
                keys = Dictionary.Keys(cellDictionary)
            else:
                keys = []
            if len(keys) > 0:
                if spaceTypeKey:
                    keyType = getKeyName(cellDictionary, spaceTypeKey)
                else:
                    keyType = getKeyName(cellDictionary, 'type')
                if keyType:
                    osSpaceTypeName = Dictionary.ValueAtKey(cellDictionary,keyType)
                else:
                    osSpaceTypeName = defaultSpaceType
                if osSpaceTypeName:
                    sp_ = osModel.getSpaceTypeByName(osSpaceTypeName)
                    if sp_.is_initialized():
                        osSpace.setSpaceType(sp_.get())
                if spaceNameKey:
                    keyName = getKeyName(cellDictionary, spaceNameKey)

                else:
                    keyName = getKeyName(cellDictionary, 'name')
                osSpaceName = None
                if keyName:
                    osSpaceName = createUniqueName(Dictionary.ValueAtKey(cellDictionary,keyName),spaceNames, 1)
                if osSpaceName:
                    osSpace.setName(osSpaceName)
            else:
                osSpaceName = "SPACE_" + "{:04d}".format(spaceNumber)
                osSpace.setName(osSpaceName)
                sp_ = osModel.getSpaceTypeByName(defaultSpaceType)
                if sp_.is_initialized():
                    osSpace.setSpaceType(sp_.get())
            spaceNames.append(osSpaceName)
            cellFaces = Topology.SubTopologies(buildingCell, "Face")
            if cellFaces:
                for faceNumber, buildingFace in enumerate(cellFaces):
                    osFacePoints = []
                    for vertex in Topology.SubTopologies(buildingFace.ExternalBoundary(), "Vertex"):
                        osFacePoints.append(openstudio.Point3d(vertex.X(), vertex.Y(), vertex.Z()))
                    osSurface = openstudio.model.Surface(osFacePoints, osModel)
                    faceNormal = Face.Normal(buildingFace)
                    osFaceNormal = openstudio.Vector3d(faceNormal[0], faceNormal[1], faceNormal[2])
                    osFaceNormal.normalize()
                    if osFaceNormal.dot(osSurface.outwardNormal()) < 1e-6:
                        osSurface.setVertices(list(reversed(osFacePoints)))
                    osSurface.setSpace(osSpace)
                    faceCells = Topology.AdjacentTopologies(buildingFace, building, topologyType="cell")
                    if len(faceCells) == 1: #Exterior Surfaces
                        osSurface.setOutsideBoundaryCondition("Outdoors")
                        if (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) > 135) or (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) < 45):
                            osSurface.setSurfaceType("RoofCeiling")
                            osSurface.setOutsideBoundaryCondition("Outdoors")
                            osSurface.setName(osSpace.name().get() + "_TopHorizontalSlab_" + str(faceNumber))
                            if max(list(map(lambda vertex: vertex.Z(), Topology.SubTopologies(buildingFace, "Vertex")))) < 1e-6:
                                osSurface.setSurfaceType("Floor")
                                osSurface.setOutsideBoundaryCondition("Ground")
                                osSurface.setName(osSpace.name().get() + "_BottomHorizontalSlab_" + str(faceNumber))
                        else:
                            osSurface.setSurfaceType("Wall")
                            osSurface.setOutsideBoundaryCondition("Outdoors")
                            osSurface.setName(osSpace.name().get() + "_ExternalVerticalFace_" + str(faceNumber))
                            # Check for exterior apertures
                            faceDictionary = buildingFace.GetDictionary()
                            apertures = []
                            _ = buildingFace.Apertures(apertures)
                            if len(apertures) > 0:
                                for aperture in apertures:
                                    osSubSurfacePoints = []
                                    apertureFace = Aperture.Topology(aperture)
                                    for vertex in Topology.SubTopologies(apertureFace.ExternalBoundary(), "Vertex"):
                                        osSubSurfacePoints.append(openstudio.Point3d(vertex.X(), vertex.Y(), vertex.Z()))
                                    osSubSurface = openstudio.model.SubSurface(osSubSurfacePoints, osModel)
                                    apertureFaceNormal = Face.Normal(apertureFace)
                                    osSubSurfaceNormal = openstudio.Vector3d(apertureFaceNormal[0], apertureFaceNormal[1], apertureFaceNormal[2])
                                    osSubSurfaceNormal.normalize()
                                    if osSubSurfaceNormal.dot(osSubSurface.outwardNormal()) < 1e-6:
                                        osSubSurface.setVertices(list(reversed(osSubSurfacePoints)))
                                    osSubSurface.setSubSurfaceType("FixedWindow")
                                    osSubSurface.setSurface(osSurface)
                            else:
                                    # Get the dictionary keys
                                    keys = faceDictionary.Keys()
                                    if ('TOPOLOGIC_glazing_ratio' in keys):
                                        faceGlazingRatio = Dictionary.ValueAtKey(faceDictionary,'TOPOLOGIC_glazing_ratio')
                                        if faceGlazingRatio and faceGlazingRatio >= 0.01:
                                            osSurface.setWindowToWallRatio(faceGlazingRatio)
                                    else:
                                        if glazingRatio > 0.01: #Glazing ratio must be more than 1% to make any sense.
                                            osSurface.setWindowToWallRatio(glazingRatio)
                    else: #Interior Surfaces
                        if (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) > 135):
                            osSurface.setSurfaceType("Floor")
                            osSurface.setName(osSpace.name().get() + "_InternalHorizontalFace_" + str(faceNumber))
                        elif (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) < 40):
                            osSurface.setSurfaceType("RoofCeiling")
                            osSurface.setName(osSpace.name().get() + "_InternalHorizontalFace_" + str(faceNumber))
                        else:
                            osSurface.setSurfaceType("Wall")
                            osSurface.setName(osSpace.name().get() + "_InternalVerticalFace_" + str(faceNumber))
                        # Check for interior apertures
                        faceDictionary = buildingFace.GetDictionary()
                        apertures = []
                        _ = buildingFace.Apertures(apertures)
                        if len(apertures) > 0:
                            for aperture in apertures:
                                osSubSurfacePoints = []
                                apertureFace = Aperture.Topology(aperture)
                                for vertex in Topology.SubTopologies(apertureFace.ExternalBoundary(), "Vertex"):
                                    osSubSurfacePoints.append(openstudio.Point3d(vertex.X(), vertex.Y(), vertex.Z()))
                                osSubSurface = openstudio.model.SubSurface(osSubSurfacePoints, osModel)
                                apertureFaceNormal = Face.Normal(apertureFace)
                                osSubSurfaceNormal = openstudio.Vector3d(apertureFaceNormal[0], apertureFaceNormal[1], apertureFaceNormal[2])
                                osSubSurfaceNormal.normalize()
                                if osSubSurfaceNormal.dot(osSubSurface.outwardNormal()) < 1e-6:
                                    osSubSurface.setVertices(list(reversed(osSubSurfacePoints)))
                                osSubSurface.setSubSurfaceType("Door") #We are assuming all interior apertures to be doors
                                osSubSurface.setSurface(osSurface)

            osThermalZone = openstudio.model.ThermalZone(osModel)
            osThermalZone.setVolume(Cell.Volume(buildingCell))
            osThermalZone.setName(osSpace.name().get() + "_THERMAL_ZONE")
            osThermalZone.setUseIdealAirLoads(True)
            osThermalZone.setVolume(Cell.Volume(buildingCell))
            osThermalZone.setThermostatSetpointDualSetpoint(osThermostat)
            osSpace.setThermalZone(osThermalZone)

            for x in osSpaces:
                if osSpace.boundingBox().intersects(x.boundingBox()):
                    osSpace.matchSurfaces(x)
            osSpaces.append(osSpace)

        
        if shadingSurfaces:
            osShadingGroup = openstudio.model.ShadingSurfaceGroup(osModel)
            for faceIndex, shadingFace in enumerate(Topology.SubTopologies(shadingSurfaces, "Face")):
                facePoints = []
                for aVertex in Topology.SubTopologies(shadingFace.ExternalBoundary(), "Vertex"):
                    facePoints.append(openstudio.Point3d(aVertex.X(), aVertex.Y(), aVertex.Z()))
                aShadingSurface = openstudio.model.ShadingSurface(facePoints, osModel)
                faceNormal = Face.Normal(shadingFace)
                osFaceNormal = openstudio.Vector3d(faceNormal[0], faceNormal[1], faceNormal[2])
                osFaceNormal.normalize()
                if osFaceNormal.dot(aShadingSurface.outwardNormal()) < 0:
                    aShadingSurface.setVertices(list(reversed(facePoints)))
                aShadingSurface.setName("SHADINGSURFACE_" + str(faceIndex))
                aShadingSurface.setShadingSurfaceGroup(osShadingGroup)

        osModel.purgeUnusedResourceObjects()
        return osModel
    
    @staticmethod
    def ColumnNames(model, reportName, tableName):
        """
            Returns the list of column names given an OSM model, report name, and table name.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        reportName : str
            The input report name.
        tableName : str
            The input table name.

        Returns
        -------
        list
            the list of column names.

        """
        sql = model.sqlFile().get()
        query = "SELECT ColumnName FROM tabulardatawithstrings WHERE ReportName = '"+reportName+"' AND TableName = '"+tableName+"'"
        columnNames = sql.execAndReturnVectorOfString(query).get()
        return list(OrderedDict( (x,1) for x in columnNames ).keys()) #Making a unique list and keeping its order

    @staticmethod
    def DefaultConstructionSets(model):
        """
            Returns the default construction sets in the input OSM model.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        list
            The default construction sets.

        """
        sets = model.getDefaultConstructionSets()
        names = []
        for aSet in sets:
            names.append(aSet.name().get())
        return [sets, names]
    
    @staticmethod
    def DefaultScheduleSets(model):
        """
            Returns the default schedule sets found in the input OSM model.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        list
            The list of default schedule sets.

        """
        sets = model.getDefaultScheduleSets()
        names = []
        for aSet in sets:
            names.append(aSet.name().get())
        return [sets, names]
    
    @staticmethod
    def ExportToGBXML(model, path, overwrite=False):
        """
            Exports the input OSM model to a GBXML file.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        path : str
            The path for saving the file.
        overwrite : bool, optional
            If set to True any file with the same name is over-written. The default is False.

        Returns
        -------
        bool
            True if the file is written successfully. False otherwise.

        """
        from os.path import exists
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
        except:
            print("EnergyModel.ExportToGBXML - Information: Installing required openstudio library.")
            try:
                os.system("pip install openstudio")
            except:
                os.system("pip install openstudio --user")
            try:
                import openstudio
                openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
                print("EnergyModel.ExportToGBXML - Information: openstudio library installed correctly.")
            except:
                warnings.warn("EnergyModel.ExportToGBXML - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
                return None
        
        # Make sure the file extension is .xml
        ext = path[len(path)-4:len(path)]
        if ext.lower() != ".xml":
            path = path+".xml"
        
        if not overwrite and exists(path):
            print("EnergyModel.ExportToGBXML - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
            return None

        return openstudio.gbxml.GbXMLForwardTranslator().modelToGbXML(model, openstudio.openstudioutilitiescore.toPath(path))

    
    @staticmethod
    def ExportToOSM(model, path, overwrite=False):
        """
            Exports the input OSM model to an OSM file.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        path : str
            The path for saving the file.
        overwrite : bool, optional
            If set to True any file with the same name is over-written. The default is False.

        Returns
        -------
        bool
            True if the file is written successfully. False otherwise.

        """
        from os.path import exists
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
        except:
            print("EnergyModel.ExportToOSM - Information: Installing required openstudio library.")
            try:
                os.system("pip install openstudio")
            except:
                os.system("pip install openstudio --user")
            try:
                import openstudio
                openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
                print("EnergyModel.ExportToOSM - Information: openstudio library installed correctly.")
            except:
                warnings.warn("EnergyModel.ExportToOSM - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
                return None
        
        # Make sure the file extension is .osm
        ext = path[len(path)-4:len(path)]
        if ext.lower() != ".osm":
            path = path+".osm"
        
        if not overwrite and exists(path):
            print("EnergyModel.ExportToOSM - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
            return None
        osCondition = False
        osPath = openstudio.openstudioutilitiescore.toPath(path)
        osCondition = model.save(osPath, overwrite)
        return osCondition
    
    @staticmethod
    def GBXMLString(model):
        """
            Returns the GBXML string of the input OSM model.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        str
            The gbxml string.

        """
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
        except:
            print("EnergyModel.GBXMLString - Information: Installing required openstudio library.")
            try:
                os.system("pip install openstudio")
            except:
                os.system("pip install openstudio --user")
            try:
                import openstudio
                openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
                print("EnergyModel.GBXMLString - Information: openstudio library installed correctly.")
            except:
                warnings.warn("EnergyModel.GBXMLString - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
                return None
        return openstudio.gbxml.GbXMLForwardTranslator().modelToGbXMLString(model)
    
    @staticmethod
    def Query(model,
              reportName : str = "HVACSizingSummary",
              reportForString : str = "Entire Facility",
              tableName : str = "Zone Sensible Cooling",
              columnName : str = "Calculated Design Load",
              rowNames : list = [],
              units : str = "W"):
        """
            Queries the model for values.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        reportName : str , optional
            The input report name. The default is "HVACSizingSummary".
        reportForString : str, optional
            The input report for string. The default is "Entire Facility".
        tableName : str , optional
            The input table name. The default is "Zone Sensible Cooling".
        columnName : str , optional
            The input column name. The default is "Calculated Design Load".
        rowNames : list , optional
            The input list of row names. The default is [].
        units : str , optional
            The input units. The default is "W".

        Returns
        -------
        list
            The list of values.

        """
        
        def doubleValueFromQuery(sqlFile, reportName, reportForString,
                                 tableName, columnName, rowName,
                                 units):
            query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='" + reportName + "' AND ReportForString='" + reportForString + "' AND TableName = '" + tableName + "' AND RowName = '" + rowName + "' AND ColumnName= '" + columnName + "' AND Units='" + units + "'";
            osOptionalDoubleValue = sqlFile.execAndReturnFirstDouble(query)
            if (osOptionalDoubleValue.is_initialized()):
                return osOptionalDoubleValue.get()
            else:
                return None
        
        sqlFile = model.sqlFile().get()
        returnValues = []
        for rowName in rowNames:
            returnValues.append(doubleValueFromQuery(sqlFile, reportName, reportForString, tableName, columnName, rowName, units))
        return returnValues
    
    @staticmethod
    def ReportNames(model):
        """
            Returns the report names found in the input OSM model.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        list
            The list of report names found in the input OSM model.

        """
        sql = model.sqlFile().get()
        reportNames = sql.execAndReturnVectorOfString("SELECT ReportName FROM tabulardatawithstrings").get()
        return list(OrderedDict( (x,1) for x in reportNames ).keys()) #Making a unique list and keeping its order

    @staticmethod
    def RowNames(model, reportName, tableName):
        """
            Returns the list of row names given an OSM model, report name, and table name.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        reportName : str
            The input name of the report.
        tableName : str
            The input name of the table.

        Returns
        -------
        list
            The list of row names.

        """
        sql = model.sqlFile().get()
        query = "SELECT RowName FROM tabulardatawithstrings WHERE ReportName = '"+reportName+"' AND TableName = '"+tableName+"'"
        columnNames = sql.execAndReturnVectorOfString(query).get()
        return list(OrderedDict( (x,1) for x in columnNames ).keys()) #Making a unique list and keeping its order

    @staticmethod
    def Run(model, weatherFilePath: str = None, osBinaryPath : str = None, outputFolder : str = None, removeFiles : bool = False):
        """
            Runs an energy simulation.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        weatherFilePath : str
            The path to the epw weather file.
        osBinaryPath : str
            The path to the openstudio binary.
        outputFolder : str
            The path to the output folder.
        removeFiles : bool , optional
            If set to True, the working files are removed at the end of the process. The default is False.

        Returns
        -------
        model : openstudio.openstudiomodelcore.Model
            The simulated OSM model.

        """
        import os
        import time
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
        except:
            print("EnergyModel.Run - Information: Installing required openstudio library.")
            try:
                os.system("pip install openstudio")
            except:
                os.system("pip install openstudio --user")
            try:
                import openstudio
                openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
                print("EnergyModel.Run - Information: openstudio library installed correctly.")
            except:
                warnings.warn("EnergyModel.Run - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
                return None
        def deleteOldFiles(path):
            onemonth = (time.time()) - 30 * 86400
            try:
                for filename in os.listdir(path):
                    if os.path.getmtime(os.path.join(path, filename)) < onemonth:
                        if os.path.isfile(os.path.join(path, filename)):
                            os.remove(os.path.join(path, filename))
                        elif os.path.isdir(os.path.join(path, filename)):
                            shutil.rmtree((os.path.join(path, filename)))
            except:
                pass
        if not weatherFilePath:
            weatherFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.epw")
        if removeFiles:
            deleteOldFiles(outputFolder)
        pbar = tqdm(desc='Running Simulation', total=100, leave=False)
        utcnow = datetime.utcnow()
        timestamp = utcnow.strftime("UTC-%Y-%m-%d-%H-%M-%S")
        if not outputFolder:
            home = os.path.expanduser('~')
            outputFolder = os.path.join(home, "EnergyModels", timestamp)
        else:
            outputFolder = os.path.join(outputFolder, timestamp)
        os.mkdir(outputFolder)
        pbar.update(10)
        osmPath = os.path.join(outputFolder, model.getBuilding().name().get() + ".osm")
        model.save(openstudio.openstudioutilitiescore.toPath(osmPath), True)
        oswPath = os.path.join(outputFolder, model.getBuilding().name().get() + ".osw")
        pbar.update(20)
        #print("oswPath = "+oswPath)
        workflow = model.workflowJSON()
        workflow.setSeedFile(openstudio.openstudioutilitiescore.toPath(osmPath))
        pbar.update(30)
        #print("Seed File Set")
        workflow.setWeatherFile(openstudio.openstudioutilitiescore.toPath(weatherFilePath))
        pbar.update(40)
        #print("Weather File Set")
        workflow.saveAs(openstudio.openstudioutilitiescore.toPath(oswPath))
        pbar.update(50)
        #print("OSW File Saved")
        cmd = osBinaryPath+" run -w " + "\"" + oswPath + "\""
        pbar.update(60)
        os.system(cmd)
        #print("Simulation DONE")
        sqlPath = os.path.join(os.path.join(outputFolder,"run"), "eplusout.sql")
        pbar.update(100)
        #print("sqlPath = "+sqlPath)
        osSqlFile = openstudio.SqlFile(openstudio.openstudioutilitiescore.toPath(sqlPath))
        model.setSqlFile(osSqlFile)
        pbar.close()
        return model
    
    @staticmethod
    def SpaceDictionaries(model):
        """
            Return the space dictionaries found in the input OSM model.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        dict
            The dictionary of space types, names, and colors found in the input OSM model. The dictionary has the following keys:
            - "types"
            - "names"
            - "colors"

        """
        types = model.getSpaceTypes()
        names = []
        colors = []
        for aType in types:
            names.append(aType.name().get())
            red = aType.renderingColor().get().renderingRedValue()
            green = aType.renderingColor().get().renderingGreenValue()
            blue = aType.renderingColor().get().renderingBlueValue()
            colors.append([red,green,blue])
        return {'types': types, 'names': names, 'colors': colors}
    
    @staticmethod
    def SpaceTypes(model):
        """
            Return the space types found in the input OSM model.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        list
            The list of space types

        """
        return model.getSpaceTypes()
    
    @staticmethod
    def SpaceTypeNames(model):
        """
            Return the space type names found in the input OSM model.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        list
            The list of space type names

        """
        types = model.getSpaceTypes()
        names = []
        colors = []
        for aType in types:
            names.append(aType.name().get())
        return names
    
    @staticmethod
    def SpaceColors(model):
        """
            Return the space colors found in the input OSM model.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        list
            The list of space colors. Each item is a three-item list representing the red, green, and blue values of the color.

        """
        types = model.getSpaceTypes()
        colors = []
        for aType in types:
            red = aType.renderingColor().get().renderingRedValue()
            green = aType.renderingColor().get().renderingGreenValue()
            blue = aType.renderingColor().get().renderingBlueValue()
            colors.append([red,green,blue])
        return colors
    
    @staticmethod
    def SqlFile(model):
        """
            Returns the SQL file found in the input OSM model.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        SQL file
            The SQL file found in the input OSM model.

        """
        return model.sqlFile().get()
    
    @staticmethod
    def TableNames(model, reportName):
        """
            Returns the table names found in the input OSM model and report name.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        reportName : str
            The input report name.

        Returns
        -------
        list
            The list of table names found in the input OSM model and report name.

        """
        sql = model.sqlFile().get()
        tableNames = sql.execAndReturnVectorOfString("SELECT TableName FROM tabulardatawithstrings WHERE ReportName='"+reportName+"'").get()
        return list(OrderedDict( (x,1) for x in tableNames ).keys()) #Making a unique list and keeping its order

    @staticmethod
    def Topologies(model, tolerance=0.0001):
        """
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        dict
            The dictionary of topologies found in the input OSM model. The keys of the dictionary are:
            - "cells"
            - "apertures"
            - "shadingFaces"

        """
        from topologicpy.Vertex import Vertex
        from topologicpy.Edge import Edge
        from topologicpy.Wire import Wire
        from topologicpy.Face import Face
        from topologicpy.Shell import Shell
        from topologicpy.Cell import Cell
        from topologicpy.Cluster import Cluster
        from topologicpy.Dictionary import Dictionary
        from topologicpy.Aperture import Aperture
        from topologicpy.Context import Context
        from topologicpy.Topology import Topology

        def surfaceToFace(surface):
            surfaceEdges = []
            surfaceVertices = surface.vertices()
            for i in range(len(surfaceVertices)-1):
                sv = Vertex.ByCoordinates(surfaceVertices[i].x(), surfaceVertices[i].y(), surfaceVertices[i].z())
                ev = Vertex.ByCoordinates(surfaceVertices[i+1].x(), surfaceVertices[i+1].y(), surfaceVertices[i+1].z())
                edge = Edge.ByStartVertexEndVertex(sv, ev, tolerance=tolerance, silent=False)
                if not edge:
                    continue
                surfaceEdges.append(edge)
            sv = Vertex.ByCoordinates(surfaceVertices[len(surfaceVertices)-1].x(), surfaceVertices[len(surfaceVertices)-1].y(), surfaceVertices[len(surfaceVertices)-1].z())
            ev = Vertex.ByCoordinates(surfaceVertices[0].x(), surfaceVertices[0].y(), surfaceVertices[0].z())
            edge = Edge.ByStartVertexEndVertex(sv, ev, tolerance=tolerance, silent=False)
            surfaceEdges.append(edge)
            surfaceWire = Wire.ByEdges(surfaceEdges, tolerance=tolerance)
            internalBoundaries = []
            surfaceFace = Face.ByWires(surfaceWire, internalBoundaries, tolerance=tolerance)
            return surfaceFace
        
        def addApertures(face, apertures):
            usedFaces = []
            for aperture in apertures:
                cen = aperture.CenterOfMass()
                try:
                    params = face.ParametersAtVertex(cen)
                    u = params[0]
                    v = params[1]
                    w = 0.5
                except:
                    u = 0.5
                    v = 0.5
                    w = 0.5
                context = Context.ByTopologyParameters(face, u, v, w)
                _ = Aperture.ByTopologyContext(aperture, context)
            return face
        spaces = list(model.getSpaces())
        
        cells = []
        apertures = []
        shadingFaces = []
        shadingSurfaces = list(model.getShadingSurfaces())
        
        for aShadingSurface in shadingSurfaces:
            shadingFace = surfaceToFace(aShadingSurface)
            if aShadingSurface.shadingSurfaceGroup().is_initialized():
                shadingGroup = aShadingSurface.shadingSurfaceGroup().get()
                if shadingGroup.space().is_initialized():
                    space = shadingGroup.space().get()
                    osTransformation = space.transformation()
                    osTranslation = osTransformation.translation()
                    osMatrix = osTransformation.rotationMatrix()
                    rotation11 = osMatrix[0, 0]
                    rotation12 = osMatrix[0, 1]
                    rotation13 = osMatrix[0, 2]
                    rotation21 = osMatrix[1, 0]
                    rotation22 = osMatrix[1, 1]
                    rotation23 = osMatrix[1, 2]
                    rotation31 = osMatrix[2, 0]
                    rotation32 = osMatrix[2, 1]
                    rotation33 = osMatrix[2, 2]
                    shadingFace = topologic.TopologyUtility.Transform(shadingFace, osTranslation.x(), osTranslation.y(), osTranslation.z(), rotation11, rotation12, rotation13, rotation21, rotation22, rotation23, rotation31, rotation32, rotation33)
            shadingFaces.append(shadingFace)
        
        for count, aSpace in enumerate(spaces):
            osTransformation = aSpace.transformation()
            osTranslation = osTransformation.translation()
            osMatrix = osTransformation.rotationMatrix()
            rotation11 = osMatrix[0, 0]
            rotation12 = osMatrix[0, 1]
            rotation13 = osMatrix[0, 2]
            rotation21 = osMatrix[1, 0]
            rotation22 = osMatrix[1, 1]
            rotation23 = osMatrix[1, 2]
            rotation31 = osMatrix[2, 0]
            rotation32 = osMatrix[2, 1]
            rotation33 = osMatrix[2, 2]
            spaceFaces = []
            surfaces = aSpace.surfaces()

            for aSurface in surfaces:
                aFace = surfaceToFace(aSurface)
                aFace = topologic.TopologyUtility.Transform(aFace, osTranslation.x(), osTranslation.y(), osTranslation.z(), rotation11, rotation12, rotation13, rotation21, rotation22, rotation23, rotation31, rotation32, rotation33)
                subSurfaces = aSurface.subSurfaces()
                for aSubSurface in subSurfaces:
                    aperture = surfaceToFace(aSubSurface)
                    aperture = topologic.TopologyUtility.Transform(aperture, osTranslation.x(), osTranslation.y(), osTranslation.z(), rotation11, rotation12, rotation13, rotation21, rotation22, rotation23, rotation31, rotation32, rotation33)
                    apertures.append(aperture)
                addApertures(aFace, apertures)
                spaceFaces.append(aFace)
            spaceFaces = [x for x in spaceFaces if Topology.IsInstance(x, "Face")]
            spaceCell = Cell.ByFaces(spaceFaces, tolerance=tolerance)
            if not spaceCell:
                spaceCell = Shell.ByFaces(spaceFaces, tolerance=tolerance)
            if not Topology.IsInstance(spaceCell, "Cell"):
                spaceCell = Cluster.ByTopologies(spaceFaces)
            if Topology.IsInstance(spaceCell, "Topology"): #debugging
                # Set Dictionary for Cell
                keys = []
                values = []

                keys.append("TOPOLOGIC_id")
                keys.append("TOPOLOGIC_name")
                keys.append("TOPOLOGIC_type")
                keys.append("TOPOLOGIC_color")
                spaceID = str(aSpace.handle()).replace('{','').replace('}','')
                values.append(spaceID)
                values.append(aSpace.name().get())
                spaceTypeName = "Unknown"
                red = 255
                green = 255
                blue = 255
                
                if (aSpace.spaceType().is_initialized()):
                    if(aSpace.spaceType().get().name().is_initialized()):
                        spaceTypeName = aSpace.spaceType().get().name().get()
                    if(aSpace.spaceType().get().renderingColor().is_initialized()):
                        red = aSpace.spaceType().get().renderingColor().get().renderingRedValue()
                        green = aSpace.spaceType().get().renderingColor().get().renderingGreenValue()
                        blue = aSpace.spaceType().get().renderingColor().get().renderingBlueValue()
                values.append(spaceTypeName)
                values.append([red, green, blue])
                d = Dictionary.ByKeysValues(keys, values)
                spaceCell = Topology.SetDictionary(spaceCell, d)
                cells.append(spaceCell)
        return {'cells':cells, 'apertures':apertures, 'shadingFaces': shadingFaces}

    @staticmethod
    def Units(model, reportName, tableName, columnName):
        """
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        reportName : str
            The input report name.
        tableName : str
            The input table name.
        columnName : str
            The input column name.

        Returns
        -------
        str
            The units string found in the input OSM model, report name, table name, and column name.

        """
        # model = item[0]
        # reportName = item[1]
        # tableName = item[2]
        # columnName = item[3]
        sql = model.sqlFile().get()
        query = "SELECT Units FROM tabulardatawithstrings WHERE ReportName = '"+reportName+"' AND TableName = '"+tableName+"' AND ColumnName = '"+columnName+"'"
        units = sql.execAndReturnFirstString(query)
        if (units.is_initialized()):
            units = units.get()
        else:
            print("EnergyModel.Units - Error: Could not retrieve the units. Returning None.")
            return None
        return units
    

Classes

class EnergyModel

@staticmethod def ByOSMFile(file): """ Creates an EnergyModel from the input OSM file path.

Parameters
----------
path : string
    The path to the input .OSM file.

Returns
-------
openstudio.openstudiomodelcore.Model
    The OSM model.

"""
if not file:
    print("EnergyModel.ByOSMFile - Error: The input path is not valid. Returning None.")
    return None
osModel = file.read()
if osModel.isNull():
    print("EnergyModel.ByOSMFile - Error: The openstudio model is null. Returning None.")
    return None
else:
    osModel = osModel.get()
return osModel
Expand source code
class EnergyModel:
    '''
    @staticmethod
    def ByOSMFile(file):
        """
        Creates an EnergyModel from the input OSM file path.

        Parameters
        ----------
        path : string
            The path to the input .OSM file.

        Returns
        -------
        openstudio.openstudiomodelcore.Model
            The OSM model.

        """
        if not file:
            print("EnergyModel.ByOSMFile - Error: The input path is not valid. Returning None.")
            return None
        osModel = file.read()
        if osModel.isNull():
            print("EnergyModel.ByOSMFile - Error: The openstudio model is null. Returning None.")
            return None
        else:
            osModel = osModel.get()
        return osModel
    '''

    @staticmethod
    def ByOSMPath(path: str):
        """
        Creates an EnergyModel from the input OSM file path.
        
        Parameters
        ----------
        path : string
            The path to the input .OSM file.

        Returns
        -------
        openstudio.openstudiomodelcore.Model
            The OSM model.

        """
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
        except:
            print("EnergyModel.ByOSMPath - Information: Installing required openstudio library.")
            try:
                os.system("pip install openstudio")
            except:
                os.system("pip install openstudio --user")
            try:
                import openstudio
                openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
                print("EnergyModel.ByOSMPath - Information: openstudio library installed correctly.")
            except:
                warnings.warn("EnergyModel.ByOSMPath - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
                return None
        
        if not path:
            print("EnergyModel.ByOSMPath - Error: The input path is not valid. Returning None.")
            return None
        translator = openstudio.osversion.VersionTranslator()
        osmPath = openstudio.openstudioutilitiescore.toPath(path)
        osModel = translator.loadModel(osmPath)
        if osModel.isNull():
            print("EnergyModel.ByImportedOSM - Error: The openstudio model is null. Returning None.")
            return None
        else:
            osModel = osModel.get()
        return osModel
    
    @staticmethod
    def ByTopology(building,
                   shadingSurfaces  = None,
                   osModelPath : str = None,
                   weatherFilePath : str = None,
                   designDayFilePath  : str = None,
                   floorLevels : list = None,
                   buildingName : str = "TopologicBuilding",
                   buildingType : str = "Commercial",
                   northAxis : float = 0.0,
                   glazingRatio : float = 0.0,
                   coolingTemp : float = 25.0,
                   heatingTemp : float = 20.0,
                   defaultSpaceType : str = "189.1-2009 - Office - WholeBuilding - Lg Office - CZ4-8",
                   spaceNameKey : str = "TOPOLOGIC_name",
                   spaceTypeKey : str = "TOPOLOGIC_type"):
        """
            Creates an EnergyModel from the input topology and parameters.

        Parameters
        ----------
        building : topologic_core.CellComplex or topologic_core.Cell
            The input building topology.
        shadingSurfaces : topologic_core.Topology , optional
            The input topology for shading surfaces. The default is None.
        osModelPath : str , optional
            The path to the template OSM file. The default is "./assets/EnergyModel/OSMTemplate-OfficeBuilding-3.5.0.osm".
        weatherFilePath : str , optional
            The input energy plus weather (epw) file. The default is "./assets/EnergyModel/GBR_London.Gatwick.037760_IWEC.epw".
        designDayFilePath : str , optional
            The input design day (ddy) file path. The default is "./assets/EnergyModel/GBR_London.Gatwick.037760_IWEC.ddy",
        floorLevels : list , optional
            The list of floor level Z heights including the lowest most and the highest most levels. If set to None, this method will attempt to
            find the floor levels from the horizontal faces of the input topology
        buildingName : str , optional
            The desired name of the building. The default is "TopologicBuilding".
        buildingType : str , optional
            The building type. The default is "Commercial".
        defaultSpaceType : str , optional
            The default space type to apply to spaces that do not have a type assigned in their dictionary. The default is "189.1-2009 - Office - WholeBuilding - Lg Office - CZ4-8".
        northAxis : float , optional
            The counter-clockwise angle in degrees from the positive Y-axis representing the direction of the north axis. The default is 0.0.
        glazingRatio : float , optional
            The glazing ratio (ratio of windows to wall) to use for exterior vertical walls that do not have apertures. If you do not wish to use a glazing ratio, set it to 0. The default is 0.
        coolingTemp : float , optional
            The desired temperature in degrees at which the cooling system should activate. The default is 25.0.
        heatingTemp : float , optional
            The desired temperature in degrees at which the heating system should activate. The default is 25.0..
        spaceNameKey : str , optional
            The dictionary key to use to find the space name value. The default is "Name".
        spaceTypeKey : str , optional
            The dictionary key to use to find the space type value. The default is "Type".

        Returns
        -------
        openstudio.openstudiomodelcore.Model
            The created OSM model.

        """
        from topologicpy.Face import Face
        from topologicpy.Cell import Cell
        from topologicpy.Topology import Topology
        from topologicpy.Dictionary import Dictionary
        from topologicpy.Aperture import Aperture
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
        except:
            print("EnergyModel.ByTopology - Information: Installing required openstudio library.")
            try:
                os.system("pip install openstudio")
            except:
                os.system("pip install openstudio --user")
            try:
                import openstudio
                openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
                print("EnergyModel.ByTopology - Information: openstudio library installed correctly.")
            except:
                warnings.warn("EnergyModel.ByTopology - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
                return None

        def getKeyName(d, keyName):
            keys = d.Keys()
            for key in keys:
                if key.lower() == keyName.lower():
                    return key
            return None
        
        def createUniqueName(name, nameList, number):
            if number > 9999:
                return name+"_9999"
            if not (name in nameList):
                return name
            elif not ((name+"_"+"{:04d}".format(number)) in nameList):
                return name+"_"+"{:04d}".format(number)
            else:
                return createUniqueName(name,nameList, number+1)
        
        def getFloorLevels(building):
            from topologicpy.Vertex import Vertex
            from topologicpy.Cell import Cell
            from topologicpy.CellComplex import CellComplex

            if Topology.IsInstance(building, "CellComplex"):
                d = CellComplex.Decompose(building)
                bhf = d['bottomHorizontalFaces']
                ihf = d['internalHorizontalFaces']
                thf = d ['topHorizontalFaces']
                hf = bhf+ihf+thf
            elif Topology.IsInstance(building, "Cell"):
                d = Cell.Decompose(building)
                bhf = d['bottomHorizontalFaces']
                thf = d ['topHorizontalFaces']
                hf = bhf+thf
            else:
                return None
            floorLevels = [Vertex.Z(Topology.Centroid(f)) for f in hf]
            floorLevels = list(set(floorLevels))
            floorLevels.sort()
            return floorLevels
        
        if not osModelPath:
            import os
            osModelPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "OSMTemplate-OfficeBuilding-3.5.0.osm")
        if not weatherFilePath or not designDayFilePath:
            import os
            weatherFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.epw")
            designDayFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.ddy")
        translator = openstudio.osversion.VersionTranslator()
        osmFile = openstudio.openstudioutilitiescore.toPath(osModelPath)
        osModel = translator.loadModel(osmFile)
        if osModel.isNull():
            print("EnergyModel.ByTopology - Error: The openstudio model is null. Returning None.")
            return None
        else:
            osModel = osModel.get()
        osEPWFile = openstudio.openstudioutilitiesfiletypes.EpwFile.load(openstudio.toPath(weatherFilePath))
        if osEPWFile.is_initialized():
            osEPWFile = osEPWFile.get()
            openstudio.model.WeatherFile.setWeatherFile(osModel, osEPWFile)
        ddyModel = openstudio.openstudioenergyplus.loadAndTranslateIdf(openstudio.toPath(designDayFilePath))
        if ddyModel.is_initialized():
            ddyModel = ddyModel.get()
            for ddy in ddyModel.getObjectsByType(openstudio.IddObjectType("OS:SizingPeriod:DesignDay")):
                osModel.addObject(ddy.clone())
        else:
            print("EnergyModel.ByTopology - Error: The ddy file is not initialized. Returning None.")
            return None
        osBuilding = osModel.getBuilding()
        if not floorLevels:
            floorLevels = getFloorLevels(building)
        osBuilding.setStandardsNumberOfStories(len(floorLevels) - 1)
        osBuilding.setNominalFloortoFloorHeight(max(floorLevels) / osBuilding.standardsNumberOfStories().get())
        osBuilding.setDefaultConstructionSet(osModel.getDefaultConstructionSets()[0])
        osBuilding.setDefaultScheduleSet(osModel.getDefaultScheduleSets()[0])
        osBuilding.setName(buildingName)
        osBuilding.setStandardsBuildingType(buildingType)
        osBuilding.setSpaceType(osModel.getSpaceTypeByName(defaultSpaceType).get())
        for storyNumber in range(osBuilding.standardsNumberOfStories().get()):
            osBuildingStory = openstudio.model.BuildingStory(osModel)
            osBuildingStory.setName("STORY_" + str(storyNumber))
            osBuildingStory.setNominalZCoordinate(floorLevels[storyNumber])
            osBuildingStory.setNominalFloortoFloorHeight(osBuilding.nominalFloortoFloorHeight().get())
        osBuilding.setNorthAxis(northAxis)
        heatingScheduleConstant = openstudio.model.ScheduleConstant(osModel)
        heatingScheduleConstant.setValue(heatingTemp)
        coolingScheduleConstant = openstudio.model.ScheduleConstant(osModel)
        coolingScheduleConstant.setValue(coolingTemp)
        osThermostat = openstudio.model.ThermostatSetpointDualSetpoint(osModel)
        osThermostat.setHeatingSetpointTemperatureSchedule(heatingScheduleConstant)
        osThermostat.setCoolingSetpointTemperatureSchedule(coolingScheduleConstant)
        osBuildingStorys = list(osModel.getBuildingStorys())
        osBuildingStorys.sort(key=lambda x: x.nominalZCoordinate().get())
        osSpaces = []
        spaceNames = []
        if Topology.IsInstance(building, "CellComplex"):
            building_cells = Topology.SubTopologies(building, "Cell")
        elif Topology.IsInstance(building, "Cell"):
            building_cells = [building]
        for spaceNumber, buildingCell in enumerate(building_cells):
            osSpace = openstudio.model.Space(osModel)
            osSpaceZ = buildingCell.CenterOfMass().Z()
            osBuildingStory = osBuildingStorys[0]
            for x in osBuildingStorys:
                osBuildingStoryZ = x.nominalZCoordinate().get()
                if osBuildingStoryZ + x.nominalFloortoFloorHeight().get() < osSpaceZ:
                    continue
                if osBuildingStoryZ < osSpaceZ:
                    osBuildingStory = x
                break
            osSpace.setBuildingStory(osBuildingStory)
            cellDictionary = Topology.Dictionary(buildingCell)
            if not cellDictionary == None:
                keys = Dictionary.Keys(cellDictionary)
            else:
                keys = []
            if len(keys) > 0:
                if spaceTypeKey:
                    keyType = getKeyName(cellDictionary, spaceTypeKey)
                else:
                    keyType = getKeyName(cellDictionary, 'type')
                if keyType:
                    osSpaceTypeName = Dictionary.ValueAtKey(cellDictionary,keyType)
                else:
                    osSpaceTypeName = defaultSpaceType
                if osSpaceTypeName:
                    sp_ = osModel.getSpaceTypeByName(osSpaceTypeName)
                    if sp_.is_initialized():
                        osSpace.setSpaceType(sp_.get())
                if spaceNameKey:
                    keyName = getKeyName(cellDictionary, spaceNameKey)

                else:
                    keyName = getKeyName(cellDictionary, 'name')
                osSpaceName = None
                if keyName:
                    osSpaceName = createUniqueName(Dictionary.ValueAtKey(cellDictionary,keyName),spaceNames, 1)
                if osSpaceName:
                    osSpace.setName(osSpaceName)
            else:
                osSpaceName = "SPACE_" + "{:04d}".format(spaceNumber)
                osSpace.setName(osSpaceName)
                sp_ = osModel.getSpaceTypeByName(defaultSpaceType)
                if sp_.is_initialized():
                    osSpace.setSpaceType(sp_.get())
            spaceNames.append(osSpaceName)
            cellFaces = Topology.SubTopologies(buildingCell, "Face")
            if cellFaces:
                for faceNumber, buildingFace in enumerate(cellFaces):
                    osFacePoints = []
                    for vertex in Topology.SubTopologies(buildingFace.ExternalBoundary(), "Vertex"):
                        osFacePoints.append(openstudio.Point3d(vertex.X(), vertex.Y(), vertex.Z()))
                    osSurface = openstudio.model.Surface(osFacePoints, osModel)
                    faceNormal = Face.Normal(buildingFace)
                    osFaceNormal = openstudio.Vector3d(faceNormal[0], faceNormal[1], faceNormal[2])
                    osFaceNormal.normalize()
                    if osFaceNormal.dot(osSurface.outwardNormal()) < 1e-6:
                        osSurface.setVertices(list(reversed(osFacePoints)))
                    osSurface.setSpace(osSpace)
                    faceCells = Topology.AdjacentTopologies(buildingFace, building, topologyType="cell")
                    if len(faceCells) == 1: #Exterior Surfaces
                        osSurface.setOutsideBoundaryCondition("Outdoors")
                        if (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) > 135) or (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) < 45):
                            osSurface.setSurfaceType("RoofCeiling")
                            osSurface.setOutsideBoundaryCondition("Outdoors")
                            osSurface.setName(osSpace.name().get() + "_TopHorizontalSlab_" + str(faceNumber))
                            if max(list(map(lambda vertex: vertex.Z(), Topology.SubTopologies(buildingFace, "Vertex")))) < 1e-6:
                                osSurface.setSurfaceType("Floor")
                                osSurface.setOutsideBoundaryCondition("Ground")
                                osSurface.setName(osSpace.name().get() + "_BottomHorizontalSlab_" + str(faceNumber))
                        else:
                            osSurface.setSurfaceType("Wall")
                            osSurface.setOutsideBoundaryCondition("Outdoors")
                            osSurface.setName(osSpace.name().get() + "_ExternalVerticalFace_" + str(faceNumber))
                            # Check for exterior apertures
                            faceDictionary = buildingFace.GetDictionary()
                            apertures = []
                            _ = buildingFace.Apertures(apertures)
                            if len(apertures) > 0:
                                for aperture in apertures:
                                    osSubSurfacePoints = []
                                    apertureFace = Aperture.Topology(aperture)
                                    for vertex in Topology.SubTopologies(apertureFace.ExternalBoundary(), "Vertex"):
                                        osSubSurfacePoints.append(openstudio.Point3d(vertex.X(), vertex.Y(), vertex.Z()))
                                    osSubSurface = openstudio.model.SubSurface(osSubSurfacePoints, osModel)
                                    apertureFaceNormal = Face.Normal(apertureFace)
                                    osSubSurfaceNormal = openstudio.Vector3d(apertureFaceNormal[0], apertureFaceNormal[1], apertureFaceNormal[2])
                                    osSubSurfaceNormal.normalize()
                                    if osSubSurfaceNormal.dot(osSubSurface.outwardNormal()) < 1e-6:
                                        osSubSurface.setVertices(list(reversed(osSubSurfacePoints)))
                                    osSubSurface.setSubSurfaceType("FixedWindow")
                                    osSubSurface.setSurface(osSurface)
                            else:
                                    # Get the dictionary keys
                                    keys = faceDictionary.Keys()
                                    if ('TOPOLOGIC_glazing_ratio' in keys):
                                        faceGlazingRatio = Dictionary.ValueAtKey(faceDictionary,'TOPOLOGIC_glazing_ratio')
                                        if faceGlazingRatio and faceGlazingRatio >= 0.01:
                                            osSurface.setWindowToWallRatio(faceGlazingRatio)
                                    else:
                                        if glazingRatio > 0.01: #Glazing ratio must be more than 1% to make any sense.
                                            osSurface.setWindowToWallRatio(glazingRatio)
                    else: #Interior Surfaces
                        if (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) > 135):
                            osSurface.setSurfaceType("Floor")
                            osSurface.setName(osSpace.name().get() + "_InternalHorizontalFace_" + str(faceNumber))
                        elif (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) < 40):
                            osSurface.setSurfaceType("RoofCeiling")
                            osSurface.setName(osSpace.name().get() + "_InternalHorizontalFace_" + str(faceNumber))
                        else:
                            osSurface.setSurfaceType("Wall")
                            osSurface.setName(osSpace.name().get() + "_InternalVerticalFace_" + str(faceNumber))
                        # Check for interior apertures
                        faceDictionary = buildingFace.GetDictionary()
                        apertures = []
                        _ = buildingFace.Apertures(apertures)
                        if len(apertures) > 0:
                            for aperture in apertures:
                                osSubSurfacePoints = []
                                apertureFace = Aperture.Topology(aperture)
                                for vertex in Topology.SubTopologies(apertureFace.ExternalBoundary(), "Vertex"):
                                    osSubSurfacePoints.append(openstudio.Point3d(vertex.X(), vertex.Y(), vertex.Z()))
                                osSubSurface = openstudio.model.SubSurface(osSubSurfacePoints, osModel)
                                apertureFaceNormal = Face.Normal(apertureFace)
                                osSubSurfaceNormal = openstudio.Vector3d(apertureFaceNormal[0], apertureFaceNormal[1], apertureFaceNormal[2])
                                osSubSurfaceNormal.normalize()
                                if osSubSurfaceNormal.dot(osSubSurface.outwardNormal()) < 1e-6:
                                    osSubSurface.setVertices(list(reversed(osSubSurfacePoints)))
                                osSubSurface.setSubSurfaceType("Door") #We are assuming all interior apertures to be doors
                                osSubSurface.setSurface(osSurface)

            osThermalZone = openstudio.model.ThermalZone(osModel)
            osThermalZone.setVolume(Cell.Volume(buildingCell))
            osThermalZone.setName(osSpace.name().get() + "_THERMAL_ZONE")
            osThermalZone.setUseIdealAirLoads(True)
            osThermalZone.setVolume(Cell.Volume(buildingCell))
            osThermalZone.setThermostatSetpointDualSetpoint(osThermostat)
            osSpace.setThermalZone(osThermalZone)

            for x in osSpaces:
                if osSpace.boundingBox().intersects(x.boundingBox()):
                    osSpace.matchSurfaces(x)
            osSpaces.append(osSpace)

        
        if shadingSurfaces:
            osShadingGroup = openstudio.model.ShadingSurfaceGroup(osModel)
            for faceIndex, shadingFace in enumerate(Topology.SubTopologies(shadingSurfaces, "Face")):
                facePoints = []
                for aVertex in Topology.SubTopologies(shadingFace.ExternalBoundary(), "Vertex"):
                    facePoints.append(openstudio.Point3d(aVertex.X(), aVertex.Y(), aVertex.Z()))
                aShadingSurface = openstudio.model.ShadingSurface(facePoints, osModel)
                faceNormal = Face.Normal(shadingFace)
                osFaceNormal = openstudio.Vector3d(faceNormal[0], faceNormal[1], faceNormal[2])
                osFaceNormal.normalize()
                if osFaceNormal.dot(aShadingSurface.outwardNormal()) < 0:
                    aShadingSurface.setVertices(list(reversed(facePoints)))
                aShadingSurface.setName("SHADINGSURFACE_" + str(faceIndex))
                aShadingSurface.setShadingSurfaceGroup(osShadingGroup)

        osModel.purgeUnusedResourceObjects()
        return osModel
    
    @staticmethod
    def ColumnNames(model, reportName, tableName):
        """
            Returns the list of column names given an OSM model, report name, and table name.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        reportName : str
            The input report name.
        tableName : str
            The input table name.

        Returns
        -------
        list
            the list of column names.

        """
        sql = model.sqlFile().get()
        query = "SELECT ColumnName FROM tabulardatawithstrings WHERE ReportName = '"+reportName+"' AND TableName = '"+tableName+"'"
        columnNames = sql.execAndReturnVectorOfString(query).get()
        return list(OrderedDict( (x,1) for x in columnNames ).keys()) #Making a unique list and keeping its order

    @staticmethod
    def DefaultConstructionSets(model):
        """
            Returns the default construction sets in the input OSM model.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        list
            The default construction sets.

        """
        sets = model.getDefaultConstructionSets()
        names = []
        for aSet in sets:
            names.append(aSet.name().get())
        return [sets, names]
    
    @staticmethod
    def DefaultScheduleSets(model):
        """
            Returns the default schedule sets found in the input OSM model.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        list
            The list of default schedule sets.

        """
        sets = model.getDefaultScheduleSets()
        names = []
        for aSet in sets:
            names.append(aSet.name().get())
        return [sets, names]
    
    @staticmethod
    def ExportToGBXML(model, path, overwrite=False):
        """
            Exports the input OSM model to a GBXML file.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        path : str
            The path for saving the file.
        overwrite : bool, optional
            If set to True any file with the same name is over-written. The default is False.

        Returns
        -------
        bool
            True if the file is written successfully. False otherwise.

        """
        from os.path import exists
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
        except:
            print("EnergyModel.ExportToGBXML - Information: Installing required openstudio library.")
            try:
                os.system("pip install openstudio")
            except:
                os.system("pip install openstudio --user")
            try:
                import openstudio
                openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
                print("EnergyModel.ExportToGBXML - Information: openstudio library installed correctly.")
            except:
                warnings.warn("EnergyModel.ExportToGBXML - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
                return None
        
        # Make sure the file extension is .xml
        ext = path[len(path)-4:len(path)]
        if ext.lower() != ".xml":
            path = path+".xml"
        
        if not overwrite and exists(path):
            print("EnergyModel.ExportToGBXML - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
            return None

        return openstudio.gbxml.GbXMLForwardTranslator().modelToGbXML(model, openstudio.openstudioutilitiescore.toPath(path))

    
    @staticmethod
    def ExportToOSM(model, path, overwrite=False):
        """
            Exports the input OSM model to an OSM file.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        path : str
            The path for saving the file.
        overwrite : bool, optional
            If set to True any file with the same name is over-written. The default is False.

        Returns
        -------
        bool
            True if the file is written successfully. False otherwise.

        """
        from os.path import exists
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
        except:
            print("EnergyModel.ExportToOSM - Information: Installing required openstudio library.")
            try:
                os.system("pip install openstudio")
            except:
                os.system("pip install openstudio --user")
            try:
                import openstudio
                openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
                print("EnergyModel.ExportToOSM - Information: openstudio library installed correctly.")
            except:
                warnings.warn("EnergyModel.ExportToOSM - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
                return None
        
        # Make sure the file extension is .osm
        ext = path[len(path)-4:len(path)]
        if ext.lower() != ".osm":
            path = path+".osm"
        
        if not overwrite and exists(path):
            print("EnergyModel.ExportToOSM - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
            return None
        osCondition = False
        osPath = openstudio.openstudioutilitiescore.toPath(path)
        osCondition = model.save(osPath, overwrite)
        return osCondition
    
    @staticmethod
    def GBXMLString(model):
        """
            Returns the GBXML string of the input OSM model.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        str
            The gbxml string.

        """
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
        except:
            print("EnergyModel.GBXMLString - Information: Installing required openstudio library.")
            try:
                os.system("pip install openstudio")
            except:
                os.system("pip install openstudio --user")
            try:
                import openstudio
                openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
                print("EnergyModel.GBXMLString - Information: openstudio library installed correctly.")
            except:
                warnings.warn("EnergyModel.GBXMLString - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
                return None
        return openstudio.gbxml.GbXMLForwardTranslator().modelToGbXMLString(model)
    
    @staticmethod
    def Query(model,
              reportName : str = "HVACSizingSummary",
              reportForString : str = "Entire Facility",
              tableName : str = "Zone Sensible Cooling",
              columnName : str = "Calculated Design Load",
              rowNames : list = [],
              units : str = "W"):
        """
            Queries the model for values.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        reportName : str , optional
            The input report name. The default is "HVACSizingSummary".
        reportForString : str, optional
            The input report for string. The default is "Entire Facility".
        tableName : str , optional
            The input table name. The default is "Zone Sensible Cooling".
        columnName : str , optional
            The input column name. The default is "Calculated Design Load".
        rowNames : list , optional
            The input list of row names. The default is [].
        units : str , optional
            The input units. The default is "W".

        Returns
        -------
        list
            The list of values.

        """
        
        def doubleValueFromQuery(sqlFile, reportName, reportForString,
                                 tableName, columnName, rowName,
                                 units):
            query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='" + reportName + "' AND ReportForString='" + reportForString + "' AND TableName = '" + tableName + "' AND RowName = '" + rowName + "' AND ColumnName= '" + columnName + "' AND Units='" + units + "'";
            osOptionalDoubleValue = sqlFile.execAndReturnFirstDouble(query)
            if (osOptionalDoubleValue.is_initialized()):
                return osOptionalDoubleValue.get()
            else:
                return None
        
        sqlFile = model.sqlFile().get()
        returnValues = []
        for rowName in rowNames:
            returnValues.append(doubleValueFromQuery(sqlFile, reportName, reportForString, tableName, columnName, rowName, units))
        return returnValues
    
    @staticmethod
    def ReportNames(model):
        """
            Returns the report names found in the input OSM model.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        list
            The list of report names found in the input OSM model.

        """
        sql = model.sqlFile().get()
        reportNames = sql.execAndReturnVectorOfString("SELECT ReportName FROM tabulardatawithstrings").get()
        return list(OrderedDict( (x,1) for x in reportNames ).keys()) #Making a unique list and keeping its order

    @staticmethod
    def RowNames(model, reportName, tableName):
        """
            Returns the list of row names given an OSM model, report name, and table name.

        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        reportName : str
            The input name of the report.
        tableName : str
            The input name of the table.

        Returns
        -------
        list
            The list of row names.

        """
        sql = model.sqlFile().get()
        query = "SELECT RowName FROM tabulardatawithstrings WHERE ReportName = '"+reportName+"' AND TableName = '"+tableName+"'"
        columnNames = sql.execAndReturnVectorOfString(query).get()
        return list(OrderedDict( (x,1) for x in columnNames ).keys()) #Making a unique list and keeping its order

    @staticmethod
    def Run(model, weatherFilePath: str = None, osBinaryPath : str = None, outputFolder : str = None, removeFiles : bool = False):
        """
            Runs an energy simulation.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        weatherFilePath : str
            The path to the epw weather file.
        osBinaryPath : str
            The path to the openstudio binary.
        outputFolder : str
            The path to the output folder.
        removeFiles : bool , optional
            If set to True, the working files are removed at the end of the process. The default is False.

        Returns
        -------
        model : openstudio.openstudiomodelcore.Model
            The simulated OSM model.

        """
        import os
        import time
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
        except:
            print("EnergyModel.Run - Information: Installing required openstudio library.")
            try:
                os.system("pip install openstudio")
            except:
                os.system("pip install openstudio --user")
            try:
                import openstudio
                openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
                print("EnergyModel.Run - Information: openstudio library installed correctly.")
            except:
                warnings.warn("EnergyModel.Run - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
                return None
        def deleteOldFiles(path):
            onemonth = (time.time()) - 30 * 86400
            try:
                for filename in os.listdir(path):
                    if os.path.getmtime(os.path.join(path, filename)) < onemonth:
                        if os.path.isfile(os.path.join(path, filename)):
                            os.remove(os.path.join(path, filename))
                        elif os.path.isdir(os.path.join(path, filename)):
                            shutil.rmtree((os.path.join(path, filename)))
            except:
                pass
        if not weatherFilePath:
            weatherFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.epw")
        if removeFiles:
            deleteOldFiles(outputFolder)
        pbar = tqdm(desc='Running Simulation', total=100, leave=False)
        utcnow = datetime.utcnow()
        timestamp = utcnow.strftime("UTC-%Y-%m-%d-%H-%M-%S")
        if not outputFolder:
            home = os.path.expanduser('~')
            outputFolder = os.path.join(home, "EnergyModels", timestamp)
        else:
            outputFolder = os.path.join(outputFolder, timestamp)
        os.mkdir(outputFolder)
        pbar.update(10)
        osmPath = os.path.join(outputFolder, model.getBuilding().name().get() + ".osm")
        model.save(openstudio.openstudioutilitiescore.toPath(osmPath), True)
        oswPath = os.path.join(outputFolder, model.getBuilding().name().get() + ".osw")
        pbar.update(20)
        #print("oswPath = "+oswPath)
        workflow = model.workflowJSON()
        workflow.setSeedFile(openstudio.openstudioutilitiescore.toPath(osmPath))
        pbar.update(30)
        #print("Seed File Set")
        workflow.setWeatherFile(openstudio.openstudioutilitiescore.toPath(weatherFilePath))
        pbar.update(40)
        #print("Weather File Set")
        workflow.saveAs(openstudio.openstudioutilitiescore.toPath(oswPath))
        pbar.update(50)
        #print("OSW File Saved")
        cmd = osBinaryPath+" run -w " + "\"" + oswPath + "\""
        pbar.update(60)
        os.system(cmd)
        #print("Simulation DONE")
        sqlPath = os.path.join(os.path.join(outputFolder,"run"), "eplusout.sql")
        pbar.update(100)
        #print("sqlPath = "+sqlPath)
        osSqlFile = openstudio.SqlFile(openstudio.openstudioutilitiescore.toPath(sqlPath))
        model.setSqlFile(osSqlFile)
        pbar.close()
        return model
    
    @staticmethod
    def SpaceDictionaries(model):
        """
            Return the space dictionaries found in the input OSM model.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        dict
            The dictionary of space types, names, and colors found in the input OSM model. The dictionary has the following keys:
            - "types"
            - "names"
            - "colors"

        """
        types = model.getSpaceTypes()
        names = []
        colors = []
        for aType in types:
            names.append(aType.name().get())
            red = aType.renderingColor().get().renderingRedValue()
            green = aType.renderingColor().get().renderingGreenValue()
            blue = aType.renderingColor().get().renderingBlueValue()
            colors.append([red,green,blue])
        return {'types': types, 'names': names, 'colors': colors}
    
    @staticmethod
    def SpaceTypes(model):
        """
            Return the space types found in the input OSM model.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        list
            The list of space types

        """
        return model.getSpaceTypes()
    
    @staticmethod
    def SpaceTypeNames(model):
        """
            Return the space type names found in the input OSM model.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        list
            The list of space type names

        """
        types = model.getSpaceTypes()
        names = []
        colors = []
        for aType in types:
            names.append(aType.name().get())
        return names
    
    @staticmethod
    def SpaceColors(model):
        """
            Return the space colors found in the input OSM model.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        list
            The list of space colors. Each item is a three-item list representing the red, green, and blue values of the color.

        """
        types = model.getSpaceTypes()
        colors = []
        for aType in types:
            red = aType.renderingColor().get().renderingRedValue()
            green = aType.renderingColor().get().renderingGreenValue()
            blue = aType.renderingColor().get().renderingBlueValue()
            colors.append([red,green,blue])
        return colors
    
    @staticmethod
    def SqlFile(model):
        """
            Returns the SQL file found in the input OSM model.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.

        Returns
        -------
        SQL file
            The SQL file found in the input OSM model.

        """
        return model.sqlFile().get()
    
    @staticmethod
    def TableNames(model, reportName):
        """
            Returns the table names found in the input OSM model and report name.
        
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        reportName : str
            The input report name.

        Returns
        -------
        list
            The list of table names found in the input OSM model and report name.

        """
        sql = model.sqlFile().get()
        tableNames = sql.execAndReturnVectorOfString("SELECT TableName FROM tabulardatawithstrings WHERE ReportName='"+reportName+"'").get()
        return list(OrderedDict( (x,1) for x in tableNames ).keys()) #Making a unique list and keeping its order

    @staticmethod
    def Topologies(model, tolerance=0.0001):
        """
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        tolerance : float , optional
            The desired tolerance. The default is 0.0001.

        Returns
        -------
        dict
            The dictionary of topologies found in the input OSM model. The keys of the dictionary are:
            - "cells"
            - "apertures"
            - "shadingFaces"

        """
        from topologicpy.Vertex import Vertex
        from topologicpy.Edge import Edge
        from topologicpy.Wire import Wire
        from topologicpy.Face import Face
        from topologicpy.Shell import Shell
        from topologicpy.Cell import Cell
        from topologicpy.Cluster import Cluster
        from topologicpy.Dictionary import Dictionary
        from topologicpy.Aperture import Aperture
        from topologicpy.Context import Context
        from topologicpy.Topology import Topology

        def surfaceToFace(surface):
            surfaceEdges = []
            surfaceVertices = surface.vertices()
            for i in range(len(surfaceVertices)-1):
                sv = Vertex.ByCoordinates(surfaceVertices[i].x(), surfaceVertices[i].y(), surfaceVertices[i].z())
                ev = Vertex.ByCoordinates(surfaceVertices[i+1].x(), surfaceVertices[i+1].y(), surfaceVertices[i+1].z())
                edge = Edge.ByStartVertexEndVertex(sv, ev, tolerance=tolerance, silent=False)
                if not edge:
                    continue
                surfaceEdges.append(edge)
            sv = Vertex.ByCoordinates(surfaceVertices[len(surfaceVertices)-1].x(), surfaceVertices[len(surfaceVertices)-1].y(), surfaceVertices[len(surfaceVertices)-1].z())
            ev = Vertex.ByCoordinates(surfaceVertices[0].x(), surfaceVertices[0].y(), surfaceVertices[0].z())
            edge = Edge.ByStartVertexEndVertex(sv, ev, tolerance=tolerance, silent=False)
            surfaceEdges.append(edge)
            surfaceWire = Wire.ByEdges(surfaceEdges, tolerance=tolerance)
            internalBoundaries = []
            surfaceFace = Face.ByWires(surfaceWire, internalBoundaries, tolerance=tolerance)
            return surfaceFace
        
        def addApertures(face, apertures):
            usedFaces = []
            for aperture in apertures:
                cen = aperture.CenterOfMass()
                try:
                    params = face.ParametersAtVertex(cen)
                    u = params[0]
                    v = params[1]
                    w = 0.5
                except:
                    u = 0.5
                    v = 0.5
                    w = 0.5
                context = Context.ByTopologyParameters(face, u, v, w)
                _ = Aperture.ByTopologyContext(aperture, context)
            return face
        spaces = list(model.getSpaces())
        
        cells = []
        apertures = []
        shadingFaces = []
        shadingSurfaces = list(model.getShadingSurfaces())
        
        for aShadingSurface in shadingSurfaces:
            shadingFace = surfaceToFace(aShadingSurface)
            if aShadingSurface.shadingSurfaceGroup().is_initialized():
                shadingGroup = aShadingSurface.shadingSurfaceGroup().get()
                if shadingGroup.space().is_initialized():
                    space = shadingGroup.space().get()
                    osTransformation = space.transformation()
                    osTranslation = osTransformation.translation()
                    osMatrix = osTransformation.rotationMatrix()
                    rotation11 = osMatrix[0, 0]
                    rotation12 = osMatrix[0, 1]
                    rotation13 = osMatrix[0, 2]
                    rotation21 = osMatrix[1, 0]
                    rotation22 = osMatrix[1, 1]
                    rotation23 = osMatrix[1, 2]
                    rotation31 = osMatrix[2, 0]
                    rotation32 = osMatrix[2, 1]
                    rotation33 = osMatrix[2, 2]
                    shadingFace = topologic.TopologyUtility.Transform(shadingFace, osTranslation.x(), osTranslation.y(), osTranslation.z(), rotation11, rotation12, rotation13, rotation21, rotation22, rotation23, rotation31, rotation32, rotation33)
            shadingFaces.append(shadingFace)
        
        for count, aSpace in enumerate(spaces):
            osTransformation = aSpace.transformation()
            osTranslation = osTransformation.translation()
            osMatrix = osTransformation.rotationMatrix()
            rotation11 = osMatrix[0, 0]
            rotation12 = osMatrix[0, 1]
            rotation13 = osMatrix[0, 2]
            rotation21 = osMatrix[1, 0]
            rotation22 = osMatrix[1, 1]
            rotation23 = osMatrix[1, 2]
            rotation31 = osMatrix[2, 0]
            rotation32 = osMatrix[2, 1]
            rotation33 = osMatrix[2, 2]
            spaceFaces = []
            surfaces = aSpace.surfaces()

            for aSurface in surfaces:
                aFace = surfaceToFace(aSurface)
                aFace = topologic.TopologyUtility.Transform(aFace, osTranslation.x(), osTranslation.y(), osTranslation.z(), rotation11, rotation12, rotation13, rotation21, rotation22, rotation23, rotation31, rotation32, rotation33)
                subSurfaces = aSurface.subSurfaces()
                for aSubSurface in subSurfaces:
                    aperture = surfaceToFace(aSubSurface)
                    aperture = topologic.TopologyUtility.Transform(aperture, osTranslation.x(), osTranslation.y(), osTranslation.z(), rotation11, rotation12, rotation13, rotation21, rotation22, rotation23, rotation31, rotation32, rotation33)
                    apertures.append(aperture)
                addApertures(aFace, apertures)
                spaceFaces.append(aFace)
            spaceFaces = [x for x in spaceFaces if Topology.IsInstance(x, "Face")]
            spaceCell = Cell.ByFaces(spaceFaces, tolerance=tolerance)
            if not spaceCell:
                spaceCell = Shell.ByFaces(spaceFaces, tolerance=tolerance)
            if not Topology.IsInstance(spaceCell, "Cell"):
                spaceCell = Cluster.ByTopologies(spaceFaces)
            if Topology.IsInstance(spaceCell, "Topology"): #debugging
                # Set Dictionary for Cell
                keys = []
                values = []

                keys.append("TOPOLOGIC_id")
                keys.append("TOPOLOGIC_name")
                keys.append("TOPOLOGIC_type")
                keys.append("TOPOLOGIC_color")
                spaceID = str(aSpace.handle()).replace('{','').replace('}','')
                values.append(spaceID)
                values.append(aSpace.name().get())
                spaceTypeName = "Unknown"
                red = 255
                green = 255
                blue = 255
                
                if (aSpace.spaceType().is_initialized()):
                    if(aSpace.spaceType().get().name().is_initialized()):
                        spaceTypeName = aSpace.spaceType().get().name().get()
                    if(aSpace.spaceType().get().renderingColor().is_initialized()):
                        red = aSpace.spaceType().get().renderingColor().get().renderingRedValue()
                        green = aSpace.spaceType().get().renderingColor().get().renderingGreenValue()
                        blue = aSpace.spaceType().get().renderingColor().get().renderingBlueValue()
                values.append(spaceTypeName)
                values.append([red, green, blue])
                d = Dictionary.ByKeysValues(keys, values)
                spaceCell = Topology.SetDictionary(spaceCell, d)
                cells.append(spaceCell)
        return {'cells':cells, 'apertures':apertures, 'shadingFaces': shadingFaces}

    @staticmethod
    def Units(model, reportName, tableName, columnName):
        """
        Parameters
        ----------
        model : openstudio.openstudiomodelcore.Model
            The input OSM model.
        reportName : str
            The input report name.
        tableName : str
            The input table name.
        columnName : str
            The input column name.

        Returns
        -------
        str
            The units string found in the input OSM model, report name, table name, and column name.

        """
        # model = item[0]
        # reportName = item[1]
        # tableName = item[2]
        # columnName = item[3]
        sql = model.sqlFile().get()
        query = "SELECT Units FROM tabulardatawithstrings WHERE ReportName = '"+reportName+"' AND TableName = '"+tableName+"' AND ColumnName = '"+columnName+"'"
        units = sql.execAndReturnFirstString(query)
        if (units.is_initialized()):
            units = units.get()
        else:
            print("EnergyModel.Units - Error: Could not retrieve the units. Returning None.")
            return None
        return units

Static methods

def ByOSMPath(path: str)

Creates an EnergyModel from the input OSM file path.

Parameters

path : string
The path to the input .OSM file.

Returns

openstudio.openstudiomodelcore.Model
The OSM model.
Expand source code
@staticmethod
def ByOSMPath(path: str):
    """
    Creates an EnergyModel from the input OSM file path.
    
    Parameters
    ----------
    path : string
        The path to the input .OSM file.

    Returns
    -------
    openstudio.openstudiomodelcore.Model
        The OSM model.

    """
    try:
        import openstudio
        openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
    except:
        print("EnergyModel.ByOSMPath - Information: Installing required openstudio library.")
        try:
            os.system("pip install openstudio")
        except:
            os.system("pip install openstudio --user")
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
            print("EnergyModel.ByOSMPath - Information: openstudio library installed correctly.")
        except:
            warnings.warn("EnergyModel.ByOSMPath - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
            return None
    
    if not path:
        print("EnergyModel.ByOSMPath - Error: The input path is not valid. Returning None.")
        return None
    translator = openstudio.osversion.VersionTranslator()
    osmPath = openstudio.openstudioutilitiescore.toPath(path)
    osModel = translator.loadModel(osmPath)
    if osModel.isNull():
        print("EnergyModel.ByImportedOSM - Error: The openstudio model is null. Returning None.")
        return None
    else:
        osModel = osModel.get()
    return osModel
def ByTopology(building, shadingSurfaces=None, osModelPath: str = None, weatherFilePath: str = None, designDayFilePath: str = None, floorLevels: list = None, buildingName: str = 'TopologicBuilding', buildingType: str = 'Commercial', northAxis: float = 0.0, glazingRatio: float = 0.0, coolingTemp: float = 25.0, heatingTemp: float = 20.0, defaultSpaceType: str = '189.1-2009 - Office - WholeBuilding - Lg Office - CZ4-8', spaceNameKey: str = 'TOPOLOGIC_name', spaceTypeKey: str = 'TOPOLOGIC_type')

Creates an EnergyModel from the input topology and parameters.

Parameters

building : topologic_core.CellComplex or topologic_core.Cell
The input building topology.
shadingSurfaces : topologic_core.Topology , optional
The input topology for shading surfaces. The default is None.
osModelPath : str , optional
The path to the template OSM file. The default is "./assets/EnergyModel/OSMTemplate-OfficeBuilding-3.5.0.osm".
weatherFilePath : str , optional
The input energy plus weather (epw) file. The default is "./assets/EnergyModel/GBR_London.Gatwick.037760_IWEC.epw".
designDayFilePath : str , optional
The input design day (ddy) file path. The default is "./assets/EnergyModel/GBR_London.Gatwick.037760_IWEC.ddy",
floorLevels : list , optional
The list of floor level Z heights including the lowest most and the highest most levels. If set to None, this method will attempt to find the floor levels from the horizontal faces of the input topology
buildingName : str , optional
The desired name of the building. The default is "TopologicBuilding".
buildingType : str , optional
The building type. The default is "Commercial".
defaultSpaceType : str , optional
The default space type to apply to spaces that do not have a type assigned in their dictionary. The default is "189.1-2009 - Office - WholeBuilding - Lg Office - CZ4-8".
northAxis : float , optional
The counter-clockwise angle in degrees from the positive Y-axis representing the direction of the north axis. The default is 0.0.
glazingRatio : float , optional
The glazing ratio (ratio of windows to wall) to use for exterior vertical walls that do not have apertures. If you do not wish to use a glazing ratio, set it to 0. The default is 0.
coolingTemp : float , optional
The desired temperature in degrees at which the cooling system should activate. The default is 25.0.
heatingTemp : float , optional
The desired temperature in degrees at which the heating system should activate. The default is 25.0..
spaceNameKey : str , optional
The dictionary key to use to find the space name value. The default is "Name".
spaceTypeKey : str , optional
The dictionary key to use to find the space type value. The default is "Type".

Returns

openstudio.openstudiomodelcore.Model
The created OSM model.
Expand source code
@staticmethod
def ByTopology(building,
               shadingSurfaces  = None,
               osModelPath : str = None,
               weatherFilePath : str = None,
               designDayFilePath  : str = None,
               floorLevels : list = None,
               buildingName : str = "TopologicBuilding",
               buildingType : str = "Commercial",
               northAxis : float = 0.0,
               glazingRatio : float = 0.0,
               coolingTemp : float = 25.0,
               heatingTemp : float = 20.0,
               defaultSpaceType : str = "189.1-2009 - Office - WholeBuilding - Lg Office - CZ4-8",
               spaceNameKey : str = "TOPOLOGIC_name",
               spaceTypeKey : str = "TOPOLOGIC_type"):
    """
        Creates an EnergyModel from the input topology and parameters.

    Parameters
    ----------
    building : topologic_core.CellComplex or topologic_core.Cell
        The input building topology.
    shadingSurfaces : topologic_core.Topology , optional
        The input topology for shading surfaces. The default is None.
    osModelPath : str , optional
        The path to the template OSM file. The default is "./assets/EnergyModel/OSMTemplate-OfficeBuilding-3.5.0.osm".
    weatherFilePath : str , optional
        The input energy plus weather (epw) file. The default is "./assets/EnergyModel/GBR_London.Gatwick.037760_IWEC.epw".
    designDayFilePath : str , optional
        The input design day (ddy) file path. The default is "./assets/EnergyModel/GBR_London.Gatwick.037760_IWEC.ddy",
    floorLevels : list , optional
        The list of floor level Z heights including the lowest most and the highest most levels. If set to None, this method will attempt to
        find the floor levels from the horizontal faces of the input topology
    buildingName : str , optional
        The desired name of the building. The default is "TopologicBuilding".
    buildingType : str , optional
        The building type. The default is "Commercial".
    defaultSpaceType : str , optional
        The default space type to apply to spaces that do not have a type assigned in their dictionary. The default is "189.1-2009 - Office - WholeBuilding - Lg Office - CZ4-8".
    northAxis : float , optional
        The counter-clockwise angle in degrees from the positive Y-axis representing the direction of the north axis. The default is 0.0.
    glazingRatio : float , optional
        The glazing ratio (ratio of windows to wall) to use for exterior vertical walls that do not have apertures. If you do not wish to use a glazing ratio, set it to 0. The default is 0.
    coolingTemp : float , optional
        The desired temperature in degrees at which the cooling system should activate. The default is 25.0.
    heatingTemp : float , optional
        The desired temperature in degrees at which the heating system should activate. The default is 25.0..
    spaceNameKey : str , optional
        The dictionary key to use to find the space name value. The default is "Name".
    spaceTypeKey : str , optional
        The dictionary key to use to find the space type value. The default is "Type".

    Returns
    -------
    openstudio.openstudiomodelcore.Model
        The created OSM model.

    """
    from topologicpy.Face import Face
    from topologicpy.Cell import Cell
    from topologicpy.Topology import Topology
    from topologicpy.Dictionary import Dictionary
    from topologicpy.Aperture import Aperture
    try:
        import openstudio
        openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
    except:
        print("EnergyModel.ByTopology - Information: Installing required openstudio library.")
        try:
            os.system("pip install openstudio")
        except:
            os.system("pip install openstudio --user")
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
            print("EnergyModel.ByTopology - Information: openstudio library installed correctly.")
        except:
            warnings.warn("EnergyModel.ByTopology - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
            return None

    def getKeyName(d, keyName):
        keys = d.Keys()
        for key in keys:
            if key.lower() == keyName.lower():
                return key
        return None
    
    def createUniqueName(name, nameList, number):
        if number > 9999:
            return name+"_9999"
        if not (name in nameList):
            return name
        elif not ((name+"_"+"{:04d}".format(number)) in nameList):
            return name+"_"+"{:04d}".format(number)
        else:
            return createUniqueName(name,nameList, number+1)
    
    def getFloorLevels(building):
        from topologicpy.Vertex import Vertex
        from topologicpy.Cell import Cell
        from topologicpy.CellComplex import CellComplex

        if Topology.IsInstance(building, "CellComplex"):
            d = CellComplex.Decompose(building)
            bhf = d['bottomHorizontalFaces']
            ihf = d['internalHorizontalFaces']
            thf = d ['topHorizontalFaces']
            hf = bhf+ihf+thf
        elif Topology.IsInstance(building, "Cell"):
            d = Cell.Decompose(building)
            bhf = d['bottomHorizontalFaces']
            thf = d ['topHorizontalFaces']
            hf = bhf+thf
        else:
            return None
        floorLevels = [Vertex.Z(Topology.Centroid(f)) for f in hf]
        floorLevels = list(set(floorLevels))
        floorLevels.sort()
        return floorLevels
    
    if not osModelPath:
        import os
        osModelPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "OSMTemplate-OfficeBuilding-3.5.0.osm")
    if not weatherFilePath or not designDayFilePath:
        import os
        weatherFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.epw")
        designDayFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.ddy")
    translator = openstudio.osversion.VersionTranslator()
    osmFile = openstudio.openstudioutilitiescore.toPath(osModelPath)
    osModel = translator.loadModel(osmFile)
    if osModel.isNull():
        print("EnergyModel.ByTopology - Error: The openstudio model is null. Returning None.")
        return None
    else:
        osModel = osModel.get()
    osEPWFile = openstudio.openstudioutilitiesfiletypes.EpwFile.load(openstudio.toPath(weatherFilePath))
    if osEPWFile.is_initialized():
        osEPWFile = osEPWFile.get()
        openstudio.model.WeatherFile.setWeatherFile(osModel, osEPWFile)
    ddyModel = openstudio.openstudioenergyplus.loadAndTranslateIdf(openstudio.toPath(designDayFilePath))
    if ddyModel.is_initialized():
        ddyModel = ddyModel.get()
        for ddy in ddyModel.getObjectsByType(openstudio.IddObjectType("OS:SizingPeriod:DesignDay")):
            osModel.addObject(ddy.clone())
    else:
        print("EnergyModel.ByTopology - Error: The ddy file is not initialized. Returning None.")
        return None
    osBuilding = osModel.getBuilding()
    if not floorLevels:
        floorLevels = getFloorLevels(building)
    osBuilding.setStandardsNumberOfStories(len(floorLevels) - 1)
    osBuilding.setNominalFloortoFloorHeight(max(floorLevels) / osBuilding.standardsNumberOfStories().get())
    osBuilding.setDefaultConstructionSet(osModel.getDefaultConstructionSets()[0])
    osBuilding.setDefaultScheduleSet(osModel.getDefaultScheduleSets()[0])
    osBuilding.setName(buildingName)
    osBuilding.setStandardsBuildingType(buildingType)
    osBuilding.setSpaceType(osModel.getSpaceTypeByName(defaultSpaceType).get())
    for storyNumber in range(osBuilding.standardsNumberOfStories().get()):
        osBuildingStory = openstudio.model.BuildingStory(osModel)
        osBuildingStory.setName("STORY_" + str(storyNumber))
        osBuildingStory.setNominalZCoordinate(floorLevels[storyNumber])
        osBuildingStory.setNominalFloortoFloorHeight(osBuilding.nominalFloortoFloorHeight().get())
    osBuilding.setNorthAxis(northAxis)
    heatingScheduleConstant = openstudio.model.ScheduleConstant(osModel)
    heatingScheduleConstant.setValue(heatingTemp)
    coolingScheduleConstant = openstudio.model.ScheduleConstant(osModel)
    coolingScheduleConstant.setValue(coolingTemp)
    osThermostat = openstudio.model.ThermostatSetpointDualSetpoint(osModel)
    osThermostat.setHeatingSetpointTemperatureSchedule(heatingScheduleConstant)
    osThermostat.setCoolingSetpointTemperatureSchedule(coolingScheduleConstant)
    osBuildingStorys = list(osModel.getBuildingStorys())
    osBuildingStorys.sort(key=lambda x: x.nominalZCoordinate().get())
    osSpaces = []
    spaceNames = []
    if Topology.IsInstance(building, "CellComplex"):
        building_cells = Topology.SubTopologies(building, "Cell")
    elif Topology.IsInstance(building, "Cell"):
        building_cells = [building]
    for spaceNumber, buildingCell in enumerate(building_cells):
        osSpace = openstudio.model.Space(osModel)
        osSpaceZ = buildingCell.CenterOfMass().Z()
        osBuildingStory = osBuildingStorys[0]
        for x in osBuildingStorys:
            osBuildingStoryZ = x.nominalZCoordinate().get()
            if osBuildingStoryZ + x.nominalFloortoFloorHeight().get() < osSpaceZ:
                continue
            if osBuildingStoryZ < osSpaceZ:
                osBuildingStory = x
            break
        osSpace.setBuildingStory(osBuildingStory)
        cellDictionary = Topology.Dictionary(buildingCell)
        if not cellDictionary == None:
            keys = Dictionary.Keys(cellDictionary)
        else:
            keys = []
        if len(keys) > 0:
            if spaceTypeKey:
                keyType = getKeyName(cellDictionary, spaceTypeKey)
            else:
                keyType = getKeyName(cellDictionary, 'type')
            if keyType:
                osSpaceTypeName = Dictionary.ValueAtKey(cellDictionary,keyType)
            else:
                osSpaceTypeName = defaultSpaceType
            if osSpaceTypeName:
                sp_ = osModel.getSpaceTypeByName(osSpaceTypeName)
                if sp_.is_initialized():
                    osSpace.setSpaceType(sp_.get())
            if spaceNameKey:
                keyName = getKeyName(cellDictionary, spaceNameKey)

            else:
                keyName = getKeyName(cellDictionary, 'name')
            osSpaceName = None
            if keyName:
                osSpaceName = createUniqueName(Dictionary.ValueAtKey(cellDictionary,keyName),spaceNames, 1)
            if osSpaceName:
                osSpace.setName(osSpaceName)
        else:
            osSpaceName = "SPACE_" + "{:04d}".format(spaceNumber)
            osSpace.setName(osSpaceName)
            sp_ = osModel.getSpaceTypeByName(defaultSpaceType)
            if sp_.is_initialized():
                osSpace.setSpaceType(sp_.get())
        spaceNames.append(osSpaceName)
        cellFaces = Topology.SubTopologies(buildingCell, "Face")
        if cellFaces:
            for faceNumber, buildingFace in enumerate(cellFaces):
                osFacePoints = []
                for vertex in Topology.SubTopologies(buildingFace.ExternalBoundary(), "Vertex"):
                    osFacePoints.append(openstudio.Point3d(vertex.X(), vertex.Y(), vertex.Z()))
                osSurface = openstudio.model.Surface(osFacePoints, osModel)
                faceNormal = Face.Normal(buildingFace)
                osFaceNormal = openstudio.Vector3d(faceNormal[0], faceNormal[1], faceNormal[2])
                osFaceNormal.normalize()
                if osFaceNormal.dot(osSurface.outwardNormal()) < 1e-6:
                    osSurface.setVertices(list(reversed(osFacePoints)))
                osSurface.setSpace(osSpace)
                faceCells = Topology.AdjacentTopologies(buildingFace, building, topologyType="cell")
                if len(faceCells) == 1: #Exterior Surfaces
                    osSurface.setOutsideBoundaryCondition("Outdoors")
                    if (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) > 135) or (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) < 45):
                        osSurface.setSurfaceType("RoofCeiling")
                        osSurface.setOutsideBoundaryCondition("Outdoors")
                        osSurface.setName(osSpace.name().get() + "_TopHorizontalSlab_" + str(faceNumber))
                        if max(list(map(lambda vertex: vertex.Z(), Topology.SubTopologies(buildingFace, "Vertex")))) < 1e-6:
                            osSurface.setSurfaceType("Floor")
                            osSurface.setOutsideBoundaryCondition("Ground")
                            osSurface.setName(osSpace.name().get() + "_BottomHorizontalSlab_" + str(faceNumber))
                    else:
                        osSurface.setSurfaceType("Wall")
                        osSurface.setOutsideBoundaryCondition("Outdoors")
                        osSurface.setName(osSpace.name().get() + "_ExternalVerticalFace_" + str(faceNumber))
                        # Check for exterior apertures
                        faceDictionary = buildingFace.GetDictionary()
                        apertures = []
                        _ = buildingFace.Apertures(apertures)
                        if len(apertures) > 0:
                            for aperture in apertures:
                                osSubSurfacePoints = []
                                apertureFace = Aperture.Topology(aperture)
                                for vertex in Topology.SubTopologies(apertureFace.ExternalBoundary(), "Vertex"):
                                    osSubSurfacePoints.append(openstudio.Point3d(vertex.X(), vertex.Y(), vertex.Z()))
                                osSubSurface = openstudio.model.SubSurface(osSubSurfacePoints, osModel)
                                apertureFaceNormal = Face.Normal(apertureFace)
                                osSubSurfaceNormal = openstudio.Vector3d(apertureFaceNormal[0], apertureFaceNormal[1], apertureFaceNormal[2])
                                osSubSurfaceNormal.normalize()
                                if osSubSurfaceNormal.dot(osSubSurface.outwardNormal()) < 1e-6:
                                    osSubSurface.setVertices(list(reversed(osSubSurfacePoints)))
                                osSubSurface.setSubSurfaceType("FixedWindow")
                                osSubSurface.setSurface(osSurface)
                        else:
                                # Get the dictionary keys
                                keys = faceDictionary.Keys()
                                if ('TOPOLOGIC_glazing_ratio' in keys):
                                    faceGlazingRatio = Dictionary.ValueAtKey(faceDictionary,'TOPOLOGIC_glazing_ratio')
                                    if faceGlazingRatio and faceGlazingRatio >= 0.01:
                                        osSurface.setWindowToWallRatio(faceGlazingRatio)
                                else:
                                    if glazingRatio > 0.01: #Glazing ratio must be more than 1% to make any sense.
                                        osSurface.setWindowToWallRatio(glazingRatio)
                else: #Interior Surfaces
                    if (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) > 135):
                        osSurface.setSurfaceType("Floor")
                        osSurface.setName(osSpace.name().get() + "_InternalHorizontalFace_" + str(faceNumber))
                    elif (math.degrees(math.acos(osSurface.outwardNormal().dot(openstudio.Vector3d(0, 0, 1)))) < 40):
                        osSurface.setSurfaceType("RoofCeiling")
                        osSurface.setName(osSpace.name().get() + "_InternalHorizontalFace_" + str(faceNumber))
                    else:
                        osSurface.setSurfaceType("Wall")
                        osSurface.setName(osSpace.name().get() + "_InternalVerticalFace_" + str(faceNumber))
                    # Check for interior apertures
                    faceDictionary = buildingFace.GetDictionary()
                    apertures = []
                    _ = buildingFace.Apertures(apertures)
                    if len(apertures) > 0:
                        for aperture in apertures:
                            osSubSurfacePoints = []
                            apertureFace = Aperture.Topology(aperture)
                            for vertex in Topology.SubTopologies(apertureFace.ExternalBoundary(), "Vertex"):
                                osSubSurfacePoints.append(openstudio.Point3d(vertex.X(), vertex.Y(), vertex.Z()))
                            osSubSurface = openstudio.model.SubSurface(osSubSurfacePoints, osModel)
                            apertureFaceNormal = Face.Normal(apertureFace)
                            osSubSurfaceNormal = openstudio.Vector3d(apertureFaceNormal[0], apertureFaceNormal[1], apertureFaceNormal[2])
                            osSubSurfaceNormal.normalize()
                            if osSubSurfaceNormal.dot(osSubSurface.outwardNormal()) < 1e-6:
                                osSubSurface.setVertices(list(reversed(osSubSurfacePoints)))
                            osSubSurface.setSubSurfaceType("Door") #We are assuming all interior apertures to be doors
                            osSubSurface.setSurface(osSurface)

        osThermalZone = openstudio.model.ThermalZone(osModel)
        osThermalZone.setVolume(Cell.Volume(buildingCell))
        osThermalZone.setName(osSpace.name().get() + "_THERMAL_ZONE")
        osThermalZone.setUseIdealAirLoads(True)
        osThermalZone.setVolume(Cell.Volume(buildingCell))
        osThermalZone.setThermostatSetpointDualSetpoint(osThermostat)
        osSpace.setThermalZone(osThermalZone)

        for x in osSpaces:
            if osSpace.boundingBox().intersects(x.boundingBox()):
                osSpace.matchSurfaces(x)
        osSpaces.append(osSpace)

    
    if shadingSurfaces:
        osShadingGroup = openstudio.model.ShadingSurfaceGroup(osModel)
        for faceIndex, shadingFace in enumerate(Topology.SubTopologies(shadingSurfaces, "Face")):
            facePoints = []
            for aVertex in Topology.SubTopologies(shadingFace.ExternalBoundary(), "Vertex"):
                facePoints.append(openstudio.Point3d(aVertex.X(), aVertex.Y(), aVertex.Z()))
            aShadingSurface = openstudio.model.ShadingSurface(facePoints, osModel)
            faceNormal = Face.Normal(shadingFace)
            osFaceNormal = openstudio.Vector3d(faceNormal[0], faceNormal[1], faceNormal[2])
            osFaceNormal.normalize()
            if osFaceNormal.dot(aShadingSurface.outwardNormal()) < 0:
                aShadingSurface.setVertices(list(reversed(facePoints)))
            aShadingSurface.setName("SHADINGSURFACE_" + str(faceIndex))
            aShadingSurface.setShadingSurfaceGroup(osShadingGroup)

    osModel.purgeUnusedResourceObjects()
    return osModel
def ColumnNames(model, reportName, tableName)

Returns the list of column names given an OSM model, report name, and table name.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.
reportName : str
The input report name.
tableName : str
The input table name.

Returns

list
the list of column names.
Expand source code
@staticmethod
def ColumnNames(model, reportName, tableName):
    """
        Returns the list of column names given an OSM model, report name, and table name.

    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.
    reportName : str
        The input report name.
    tableName : str
        The input table name.

    Returns
    -------
    list
        the list of column names.

    """
    sql = model.sqlFile().get()
    query = "SELECT ColumnName FROM tabulardatawithstrings WHERE ReportName = '"+reportName+"' AND TableName = '"+tableName+"'"
    columnNames = sql.execAndReturnVectorOfString(query).get()
    return list(OrderedDict( (x,1) for x in columnNames ).keys()) #Making a unique list and keeping its order
def DefaultConstructionSets(model)

Returns the default construction sets in the input OSM model.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.

Returns

list
The default construction sets.
Expand source code
@staticmethod
def DefaultConstructionSets(model):
    """
        Returns the default construction sets in the input OSM model.

    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.

    Returns
    -------
    list
        The default construction sets.

    """
    sets = model.getDefaultConstructionSets()
    names = []
    for aSet in sets:
        names.append(aSet.name().get())
    return [sets, names]
def DefaultScheduleSets(model)

Returns the default schedule sets found in the input OSM model.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.

Returns

list
The list of default schedule sets.
Expand source code
@staticmethod
def DefaultScheduleSets(model):
    """
        Returns the default schedule sets found in the input OSM model.

    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.

    Returns
    -------
    list
        The list of default schedule sets.

    """
    sets = model.getDefaultScheduleSets()
    names = []
    for aSet in sets:
        names.append(aSet.name().get())
    return [sets, names]
def ExportToGBXML(model, path, overwrite=False)

Exports the input OSM model to a GBXML file.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.
path : str
The path for saving the file.
overwrite : bool, optional
If set to True any file with the same name is over-written. The default is False.

Returns

bool
True if the file is written successfully. False otherwise.
Expand source code
@staticmethod
def ExportToGBXML(model, path, overwrite=False):
    """
        Exports the input OSM model to a GBXML file.

    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.
    path : str
        The path for saving the file.
    overwrite : bool, optional
        If set to True any file with the same name is over-written. The default is False.

    Returns
    -------
    bool
        True if the file is written successfully. False otherwise.

    """
    from os.path import exists
    try:
        import openstudio
        openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
    except:
        print("EnergyModel.ExportToGBXML - Information: Installing required openstudio library.")
        try:
            os.system("pip install openstudio")
        except:
            os.system("pip install openstudio --user")
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
            print("EnergyModel.ExportToGBXML - Information: openstudio library installed correctly.")
        except:
            warnings.warn("EnergyModel.ExportToGBXML - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
            return None
    
    # Make sure the file extension is .xml
    ext = path[len(path)-4:len(path)]
    if ext.lower() != ".xml":
        path = path+".xml"
    
    if not overwrite and exists(path):
        print("EnergyModel.ExportToGBXML - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
        return None

    return openstudio.gbxml.GbXMLForwardTranslator().modelToGbXML(model, openstudio.openstudioutilitiescore.toPath(path))
def ExportToOSM(model, path, overwrite=False)

Exports the input OSM model to an OSM file.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.
path : str
The path for saving the file.
overwrite : bool, optional
If set to True any file with the same name is over-written. The default is False.

Returns

bool
True if the file is written successfully. False otherwise.
Expand source code
@staticmethod
def ExportToOSM(model, path, overwrite=False):
    """
        Exports the input OSM model to an OSM file.
    
    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.
    path : str
        The path for saving the file.
    overwrite : bool, optional
        If set to True any file with the same name is over-written. The default is False.

    Returns
    -------
    bool
        True if the file is written successfully. False otherwise.

    """
    from os.path import exists
    try:
        import openstudio
        openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
    except:
        print("EnergyModel.ExportToOSM - Information: Installing required openstudio library.")
        try:
            os.system("pip install openstudio")
        except:
            os.system("pip install openstudio --user")
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
            print("EnergyModel.ExportToOSM - Information: openstudio library installed correctly.")
        except:
            warnings.warn("EnergyModel.ExportToOSM - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
            return None
    
    # Make sure the file extension is .osm
    ext = path[len(path)-4:len(path)]
    if ext.lower() != ".osm":
        path = path+".osm"
    
    if not overwrite and exists(path):
        print("EnergyModel.ExportToOSM - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
        return None
    osCondition = False
    osPath = openstudio.openstudioutilitiescore.toPath(path)
    osCondition = model.save(osPath, overwrite)
    return osCondition
def GBXMLString(model)

Returns the GBXML string of the input OSM model.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.

Returns

str
The gbxml string.
Expand source code
@staticmethod
def GBXMLString(model):
    """
        Returns the GBXML string of the input OSM model.

    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.

    Returns
    -------
    str
        The gbxml string.

    """
    try:
        import openstudio
        openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
    except:
        print("EnergyModel.GBXMLString - Information: Installing required openstudio library.")
        try:
            os.system("pip install openstudio")
        except:
            os.system("pip install openstudio --user")
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
            print("EnergyModel.GBXMLString - Information: openstudio library installed correctly.")
        except:
            warnings.warn("EnergyModel.GBXMLString - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
            return None
    return openstudio.gbxml.GbXMLForwardTranslator().modelToGbXMLString(model)
def Query(model, reportName: str = 'HVACSizingSummary', reportForString: str = 'Entire Facility', tableName: str = 'Zone Sensible Cooling', columnName: str = 'Calculated Design Load', rowNames: list = [], units: str = 'W')

Queries the model for values.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.
reportName : str , optional
The input report name. The default is "HVACSizingSummary".
reportForString : str, optional
The input report for string. The default is "Entire Facility".
tableName : str , optional
The input table name. The default is "Zone Sensible Cooling".
columnName : str , optional
The input column name. The default is "Calculated Design Load".
rowNames : list , optional
The input list of row names. The default is [].
units : str , optional
The input units. The default is "W".

Returns

list
The list of values.
Expand source code
@staticmethod
def Query(model,
          reportName : str = "HVACSizingSummary",
          reportForString : str = "Entire Facility",
          tableName : str = "Zone Sensible Cooling",
          columnName : str = "Calculated Design Load",
          rowNames : list = [],
          units : str = "W"):
    """
        Queries the model for values.

    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.
    reportName : str , optional
        The input report name. The default is "HVACSizingSummary".
    reportForString : str, optional
        The input report for string. The default is "Entire Facility".
    tableName : str , optional
        The input table name. The default is "Zone Sensible Cooling".
    columnName : str , optional
        The input column name. The default is "Calculated Design Load".
    rowNames : list , optional
        The input list of row names. The default is [].
    units : str , optional
        The input units. The default is "W".

    Returns
    -------
    list
        The list of values.

    """
    
    def doubleValueFromQuery(sqlFile, reportName, reportForString,
                             tableName, columnName, rowName,
                             units):
        query = "SELECT Value FROM tabulardatawithstrings WHERE ReportName='" + reportName + "' AND ReportForString='" + reportForString + "' AND TableName = '" + tableName + "' AND RowName = '" + rowName + "' AND ColumnName= '" + columnName + "' AND Units='" + units + "'";
        osOptionalDoubleValue = sqlFile.execAndReturnFirstDouble(query)
        if (osOptionalDoubleValue.is_initialized()):
            return osOptionalDoubleValue.get()
        else:
            return None
    
    sqlFile = model.sqlFile().get()
    returnValues = []
    for rowName in rowNames:
        returnValues.append(doubleValueFromQuery(sqlFile, reportName, reportForString, tableName, columnName, rowName, units))
    return returnValues
def ReportNames(model)

Returns the report names found in the input OSM model.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.

Returns

list
The list of report names found in the input OSM model.
Expand source code
@staticmethod
def ReportNames(model):
    """
        Returns the report names found in the input OSM model.

    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.

    Returns
    -------
    list
        The list of report names found in the input OSM model.

    """
    sql = model.sqlFile().get()
    reportNames = sql.execAndReturnVectorOfString("SELECT ReportName FROM tabulardatawithstrings").get()
    return list(OrderedDict( (x,1) for x in reportNames ).keys()) #Making a unique list and keeping its order
def RowNames(model, reportName, tableName)

Returns the list of row names given an OSM model, report name, and table name.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.
reportName : str
The input name of the report.
tableName : str
The input name of the table.

Returns

list
The list of row names.
Expand source code
@staticmethod
def RowNames(model, reportName, tableName):
    """
        Returns the list of row names given an OSM model, report name, and table name.

    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.
    reportName : str
        The input name of the report.
    tableName : str
        The input name of the table.

    Returns
    -------
    list
        The list of row names.

    """
    sql = model.sqlFile().get()
    query = "SELECT RowName FROM tabulardatawithstrings WHERE ReportName = '"+reportName+"' AND TableName = '"+tableName+"'"
    columnNames = sql.execAndReturnVectorOfString(query).get()
    return list(OrderedDict( (x,1) for x in columnNames ).keys()) #Making a unique list and keeping its order
def Run(model, weatherFilePath: str = None, osBinaryPath: str = None, outputFolder: str = None, removeFiles: bool = False)

Runs an energy simulation.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.
weatherFilePath : str
The path to the epw weather file.
osBinaryPath : str
The path to the openstudio binary.
outputFolder : str
The path to the output folder.
removeFiles : bool , optional
If set to True, the working files are removed at the end of the process. The default is False.

Returns

model : openstudio.openstudiomodelcore.Model
The simulated OSM model.
Expand source code
@staticmethod
def Run(model, weatherFilePath: str = None, osBinaryPath : str = None, outputFolder : str = None, removeFiles : bool = False):
    """
        Runs an energy simulation.
    
    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.
    weatherFilePath : str
        The path to the epw weather file.
    osBinaryPath : str
        The path to the openstudio binary.
    outputFolder : str
        The path to the output folder.
    removeFiles : bool , optional
        If set to True, the working files are removed at the end of the process. The default is False.

    Returns
    -------
    model : openstudio.openstudiomodelcore.Model
        The simulated OSM model.

    """
    import os
    import time
    try:
        import openstudio
        openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
    except:
        print("EnergyModel.Run - Information: Installing required openstudio library.")
        try:
            os.system("pip install openstudio")
        except:
            os.system("pip install openstudio --user")
        try:
            import openstudio
            openstudio.Logger.instance().standardOutLogger().setLogLevel(openstudio.Fatal)
            print("EnergyModel.Run - Information: openstudio library installed correctly.")
        except:
            warnings.warn("EnergyModel.Run - Error: Could not import openstudio.Please try to install openstudio manually. Returning None.")
            return None
    def deleteOldFiles(path):
        onemonth = (time.time()) - 30 * 86400
        try:
            for filename in os.listdir(path):
                if os.path.getmtime(os.path.join(path, filename)) < onemonth:
                    if os.path.isfile(os.path.join(path, filename)):
                        os.remove(os.path.join(path, filename))
                    elif os.path.isdir(os.path.join(path, filename)):
                        shutil.rmtree((os.path.join(path, filename)))
        except:
            pass
    if not weatherFilePath:
        weatherFilePath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets", "EnergyModel", "GBR_London.Gatwick.037760_IWEC.epw")
    if removeFiles:
        deleteOldFiles(outputFolder)
    pbar = tqdm(desc='Running Simulation', total=100, leave=False)
    utcnow = datetime.utcnow()
    timestamp = utcnow.strftime("UTC-%Y-%m-%d-%H-%M-%S")
    if not outputFolder:
        home = os.path.expanduser('~')
        outputFolder = os.path.join(home, "EnergyModels", timestamp)
    else:
        outputFolder = os.path.join(outputFolder, timestamp)
    os.mkdir(outputFolder)
    pbar.update(10)
    osmPath = os.path.join(outputFolder, model.getBuilding().name().get() + ".osm")
    model.save(openstudio.openstudioutilitiescore.toPath(osmPath), True)
    oswPath = os.path.join(outputFolder, model.getBuilding().name().get() + ".osw")
    pbar.update(20)
    #print("oswPath = "+oswPath)
    workflow = model.workflowJSON()
    workflow.setSeedFile(openstudio.openstudioutilitiescore.toPath(osmPath))
    pbar.update(30)
    #print("Seed File Set")
    workflow.setWeatherFile(openstudio.openstudioutilitiescore.toPath(weatherFilePath))
    pbar.update(40)
    #print("Weather File Set")
    workflow.saveAs(openstudio.openstudioutilitiescore.toPath(oswPath))
    pbar.update(50)
    #print("OSW File Saved")
    cmd = osBinaryPath+" run -w " + "\"" + oswPath + "\""
    pbar.update(60)
    os.system(cmd)
    #print("Simulation DONE")
    sqlPath = os.path.join(os.path.join(outputFolder,"run"), "eplusout.sql")
    pbar.update(100)
    #print("sqlPath = "+sqlPath)
    osSqlFile = openstudio.SqlFile(openstudio.openstudioutilitiescore.toPath(sqlPath))
    model.setSqlFile(osSqlFile)
    pbar.close()
    return model
def SpaceColors(model)

Return the space colors found in the input OSM model.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.

Returns

list
The list of space colors. Each item is a three-item list representing the red, green, and blue values of the color.
Expand source code
@staticmethod
def SpaceColors(model):
    """
        Return the space colors found in the input OSM model.
    
    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.

    Returns
    -------
    list
        The list of space colors. Each item is a three-item list representing the red, green, and blue values of the color.

    """
    types = model.getSpaceTypes()
    colors = []
    for aType in types:
        red = aType.renderingColor().get().renderingRedValue()
        green = aType.renderingColor().get().renderingGreenValue()
        blue = aType.renderingColor().get().renderingBlueValue()
        colors.append([red,green,blue])
    return colors
def SpaceDictionaries(model)

Return the space dictionaries found in the input OSM model.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.

Returns

dict
The dictionary of space types, names, and colors found in the input OSM model. The dictionary has the following keys: - "types" - "names" - "colors"
Expand source code
@staticmethod
def SpaceDictionaries(model):
    """
        Return the space dictionaries found in the input OSM model.
    
    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.

    Returns
    -------
    dict
        The dictionary of space types, names, and colors found in the input OSM model. The dictionary has the following keys:
        - "types"
        - "names"
        - "colors"

    """
    types = model.getSpaceTypes()
    names = []
    colors = []
    for aType in types:
        names.append(aType.name().get())
        red = aType.renderingColor().get().renderingRedValue()
        green = aType.renderingColor().get().renderingGreenValue()
        blue = aType.renderingColor().get().renderingBlueValue()
        colors.append([red,green,blue])
    return {'types': types, 'names': names, 'colors': colors}
def SpaceTypeNames(model)

Return the space type names found in the input OSM model.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.

Returns

list
The list of space type names
Expand source code
@staticmethod
def SpaceTypeNames(model):
    """
        Return the space type names found in the input OSM model.
    
    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.

    Returns
    -------
    list
        The list of space type names

    """
    types = model.getSpaceTypes()
    names = []
    colors = []
    for aType in types:
        names.append(aType.name().get())
    return names
def SpaceTypes(model)

Return the space types found in the input OSM model.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.

Returns

list
The list of space types
Expand source code
@staticmethod
def SpaceTypes(model):
    """
        Return the space types found in the input OSM model.
    
    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.

    Returns
    -------
    list
        The list of space types

    """
    return model.getSpaceTypes()
def SqlFile(model)

Returns the SQL file found in the input OSM model.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.

Returns

SQL file
The SQL file found in the input OSM model.
Expand source code
@staticmethod
def SqlFile(model):
    """
        Returns the SQL file found in the input OSM model.
    
    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.

    Returns
    -------
    SQL file
        The SQL file found in the input OSM model.

    """
    return model.sqlFile().get()
def TableNames(model, reportName)

Returns the table names found in the input OSM model and report name.

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.
reportName : str
The input report name.

Returns

list
The list of table names found in the input OSM model and report name.
Expand source code
@staticmethod
def TableNames(model, reportName):
    """
        Returns the table names found in the input OSM model and report name.
    
    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.
    reportName : str
        The input report name.

    Returns
    -------
    list
        The list of table names found in the input OSM model and report name.

    """
    sql = model.sqlFile().get()
    tableNames = sql.execAndReturnVectorOfString("SELECT TableName FROM tabulardatawithstrings WHERE ReportName='"+reportName+"'").get()
    return list(OrderedDict( (x,1) for x in tableNames ).keys()) #Making a unique list and keeping its order
def Topologies(model, tolerance=0.0001)

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.
tolerance : float , optional
The desired tolerance. The default is 0.0001.

Returns

dict
The dictionary of topologies found in the input OSM model. The keys of the dictionary are: - "cells" - "apertures" - "shadingFaces"
Expand source code
@staticmethod
def Topologies(model, tolerance=0.0001):
    """
    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.
    tolerance : float , optional
        The desired tolerance. The default is 0.0001.

    Returns
    -------
    dict
        The dictionary of topologies found in the input OSM model. The keys of the dictionary are:
        - "cells"
        - "apertures"
        - "shadingFaces"

    """
    from topologicpy.Vertex import Vertex
    from topologicpy.Edge import Edge
    from topologicpy.Wire import Wire
    from topologicpy.Face import Face
    from topologicpy.Shell import Shell
    from topologicpy.Cell import Cell
    from topologicpy.Cluster import Cluster
    from topologicpy.Dictionary import Dictionary
    from topologicpy.Aperture import Aperture
    from topologicpy.Context import Context
    from topologicpy.Topology import Topology

    def surfaceToFace(surface):
        surfaceEdges = []
        surfaceVertices = surface.vertices()
        for i in range(len(surfaceVertices)-1):
            sv = Vertex.ByCoordinates(surfaceVertices[i].x(), surfaceVertices[i].y(), surfaceVertices[i].z())
            ev = Vertex.ByCoordinates(surfaceVertices[i+1].x(), surfaceVertices[i+1].y(), surfaceVertices[i+1].z())
            edge = Edge.ByStartVertexEndVertex(sv, ev, tolerance=tolerance, silent=False)
            if not edge:
                continue
            surfaceEdges.append(edge)
        sv = Vertex.ByCoordinates(surfaceVertices[len(surfaceVertices)-1].x(), surfaceVertices[len(surfaceVertices)-1].y(), surfaceVertices[len(surfaceVertices)-1].z())
        ev = Vertex.ByCoordinates(surfaceVertices[0].x(), surfaceVertices[0].y(), surfaceVertices[0].z())
        edge = Edge.ByStartVertexEndVertex(sv, ev, tolerance=tolerance, silent=False)
        surfaceEdges.append(edge)
        surfaceWire = Wire.ByEdges(surfaceEdges, tolerance=tolerance)
        internalBoundaries = []
        surfaceFace = Face.ByWires(surfaceWire, internalBoundaries, tolerance=tolerance)
        return surfaceFace
    
    def addApertures(face, apertures):
        usedFaces = []
        for aperture in apertures:
            cen = aperture.CenterOfMass()
            try:
                params = face.ParametersAtVertex(cen)
                u = params[0]
                v = params[1]
                w = 0.5
            except:
                u = 0.5
                v = 0.5
                w = 0.5
            context = Context.ByTopologyParameters(face, u, v, w)
            _ = Aperture.ByTopologyContext(aperture, context)
        return face
    spaces = list(model.getSpaces())
    
    cells = []
    apertures = []
    shadingFaces = []
    shadingSurfaces = list(model.getShadingSurfaces())
    
    for aShadingSurface in shadingSurfaces:
        shadingFace = surfaceToFace(aShadingSurface)
        if aShadingSurface.shadingSurfaceGroup().is_initialized():
            shadingGroup = aShadingSurface.shadingSurfaceGroup().get()
            if shadingGroup.space().is_initialized():
                space = shadingGroup.space().get()
                osTransformation = space.transformation()
                osTranslation = osTransformation.translation()
                osMatrix = osTransformation.rotationMatrix()
                rotation11 = osMatrix[0, 0]
                rotation12 = osMatrix[0, 1]
                rotation13 = osMatrix[0, 2]
                rotation21 = osMatrix[1, 0]
                rotation22 = osMatrix[1, 1]
                rotation23 = osMatrix[1, 2]
                rotation31 = osMatrix[2, 0]
                rotation32 = osMatrix[2, 1]
                rotation33 = osMatrix[2, 2]
                shadingFace = topologic.TopologyUtility.Transform(shadingFace, osTranslation.x(), osTranslation.y(), osTranslation.z(), rotation11, rotation12, rotation13, rotation21, rotation22, rotation23, rotation31, rotation32, rotation33)
        shadingFaces.append(shadingFace)
    
    for count, aSpace in enumerate(spaces):
        osTransformation = aSpace.transformation()
        osTranslation = osTransformation.translation()
        osMatrix = osTransformation.rotationMatrix()
        rotation11 = osMatrix[0, 0]
        rotation12 = osMatrix[0, 1]
        rotation13 = osMatrix[0, 2]
        rotation21 = osMatrix[1, 0]
        rotation22 = osMatrix[1, 1]
        rotation23 = osMatrix[1, 2]
        rotation31 = osMatrix[2, 0]
        rotation32 = osMatrix[2, 1]
        rotation33 = osMatrix[2, 2]
        spaceFaces = []
        surfaces = aSpace.surfaces()

        for aSurface in surfaces:
            aFace = surfaceToFace(aSurface)
            aFace = topologic.TopologyUtility.Transform(aFace, osTranslation.x(), osTranslation.y(), osTranslation.z(), rotation11, rotation12, rotation13, rotation21, rotation22, rotation23, rotation31, rotation32, rotation33)
            subSurfaces = aSurface.subSurfaces()
            for aSubSurface in subSurfaces:
                aperture = surfaceToFace(aSubSurface)
                aperture = topologic.TopologyUtility.Transform(aperture, osTranslation.x(), osTranslation.y(), osTranslation.z(), rotation11, rotation12, rotation13, rotation21, rotation22, rotation23, rotation31, rotation32, rotation33)
                apertures.append(aperture)
            addApertures(aFace, apertures)
            spaceFaces.append(aFace)
        spaceFaces = [x for x in spaceFaces if Topology.IsInstance(x, "Face")]
        spaceCell = Cell.ByFaces(spaceFaces, tolerance=tolerance)
        if not spaceCell:
            spaceCell = Shell.ByFaces(spaceFaces, tolerance=tolerance)
        if not Topology.IsInstance(spaceCell, "Cell"):
            spaceCell = Cluster.ByTopologies(spaceFaces)
        if Topology.IsInstance(spaceCell, "Topology"): #debugging
            # Set Dictionary for Cell
            keys = []
            values = []

            keys.append("TOPOLOGIC_id")
            keys.append("TOPOLOGIC_name")
            keys.append("TOPOLOGIC_type")
            keys.append("TOPOLOGIC_color")
            spaceID = str(aSpace.handle()).replace('{','').replace('}','')
            values.append(spaceID)
            values.append(aSpace.name().get())
            spaceTypeName = "Unknown"
            red = 255
            green = 255
            blue = 255
            
            if (aSpace.spaceType().is_initialized()):
                if(aSpace.spaceType().get().name().is_initialized()):
                    spaceTypeName = aSpace.spaceType().get().name().get()
                if(aSpace.spaceType().get().renderingColor().is_initialized()):
                    red = aSpace.spaceType().get().renderingColor().get().renderingRedValue()
                    green = aSpace.spaceType().get().renderingColor().get().renderingGreenValue()
                    blue = aSpace.spaceType().get().renderingColor().get().renderingBlueValue()
            values.append(spaceTypeName)
            values.append([red, green, blue])
            d = Dictionary.ByKeysValues(keys, values)
            spaceCell = Topology.SetDictionary(spaceCell, d)
            cells.append(spaceCell)
    return {'cells':cells, 'apertures':apertures, 'shadingFaces': shadingFaces}
def Units(model, reportName, tableName, columnName)

Parameters

model : openstudio.openstudiomodelcore.Model
The input OSM model.
reportName : str
The input report name.
tableName : str
The input table name.
columnName : str
The input column name.

Returns

str
The units string found in the input OSM model, report name, table name, and column name.
Expand source code
@staticmethod
def Units(model, reportName, tableName, columnName):
    """
    Parameters
    ----------
    model : openstudio.openstudiomodelcore.Model
        The input OSM model.
    reportName : str
        The input report name.
    tableName : str
        The input table name.
    columnName : str
        The input column name.

    Returns
    -------
    str
        The units string found in the input OSM model, report name, table name, and column name.

    """
    # model = item[0]
    # reportName = item[1]
    # tableName = item[2]
    # columnName = item[3]
    sql = model.sqlFile().get()
    query = "SELECT Units FROM tabulardatawithstrings WHERE ReportName = '"+reportName+"' AND TableName = '"+tableName+"' AND ColumnName = '"+columnName+"'"
    units = sql.execAndReturnFirstString(query)
    if (units.is_initialized()):
        units = units.get()
    else:
        print("EnergyModel.Units - Error: Could not retrieve the units. Returning None.")
        return None
    return units