def test_newidfobject(): """py.test for newidfobject""" # make a blank idf # make a function for this and then continue. idf = IDF() idf.new() objtype = 'material:airgap'.upper() obj = idf.newidfobject(objtype, Name='Argon') obj = idf.newidfobject(objtype, Name='Krypton') obj = idf.newidfobject(objtype, Name='Xenon') assert idf.model.dt[objtype] == [['MATERIAL:AIRGAP', 'Argon'], ['MATERIAL:AIRGAP', 'Krypton'], ['MATERIAL:AIRGAP', 'Xenon'], ] # remove an object idf.popidfobject(objtype, 1) assert idf.model.dt[objtype] == [['MATERIAL:AIRGAP', 'Argon'], ['MATERIAL:AIRGAP', 'Xenon'], ] lastobject = idf.idfobjects[objtype][-1] idf.removeidfobject(lastobject) assert idf.model.dt[objtype] == [['MATERIAL:AIRGAP', 'Argon'], ] # copyidfobject onlyobject = idf.idfobjects[objtype][0] idf.copyidfobject(onlyobject) assert idf.model.dt[objtype] == [['MATERIAL:AIRGAP', 'Argon'], ['MATERIAL:AIRGAP', 'Argon'], ] # test some functions objtype = 'FENESTRATIONSURFACE:DETAILED' obj = idf.newidfobject(objtype, Name='A Wall') assert obj.coords == [] assert obj.fieldvalues[1] == 'A Wall'
def test_newidfobject(): """py.test for newidfobject""" # make a blank idf # make a function for this and then continue. idf = IDF() idf.new() objtype = "material:airgap".upper() obj = idf.newidfobject(objtype, Name="Argon") obj = idf.newidfobject(objtype, Name="Krypton") obj = idf.newidfobject(objtype, Name="Xenon") assert idf.model.dt[objtype] == [ ["MATERIAL:AIRGAP", "Argon"], ["MATERIAL:AIRGAP", "Krypton"], ["MATERIAL:AIRGAP", "Xenon"], ] # remove an object idf.popidfobject(objtype, 1) assert idf.model.dt[objtype] == [ ["MATERIAL:AIRGAP", "Argon"], ["MATERIAL:AIRGAP", "Xenon"], ] lastobject = idf.idfobjects[objtype][-1] idf.removeidfobject(lastobject) assert idf.model.dt[objtype] == [["MATERIAL:AIRGAP", "Argon"]] # copyidfobject onlyobject = idf.idfobjects[objtype][0] idf.copyidfobject(onlyobject) assert idf.model.dt[objtype] == [ ["MATERIAL:AIRGAP", "Argon"], ["MATERIAL:AIRGAP", "Argon"], ] # remove all objects idf.removeallidfobjects(objtype) assert len(idf.idfobjects[objtype]) == 0 # test some functions objtype = "FENESTRATIONSURFACE:DETAILED" obj = idf.newidfobject(objtype, Name="A Wall") assert obj.coords == [] assert obj.fieldvalues[1] == "A Wall" # test defaultvalues=True and defaultvalues=False sim_deftrue = idf.newidfobject("SimulationControl".upper(), defaultvalues=True) assert sim_deftrue.Do_Zone_Sizing_Calculation == "No" sim_deffalse = idf.newidfobject("SimulationControl".upper(), defaultvalues=False) assert sim_deffalse.Do_Zone_Sizing_Calculation == ""
def test_newidfobject(): """py.test for newidfobject""" # make a blank idf # make a function for this and then continue. idf = IDF() idf.new() objtype = 'material:airgap'.upper() obj = idf.newidfobject(objtype, Name='Argon') obj = idf.newidfobject(objtype, Name='Krypton') obj = idf.newidfobject(objtype, Name='Xenon') assert idf.model.dt[objtype] == [ ['MATERIAL:AIRGAP', 'Argon'], ['MATERIAL:AIRGAP', 'Krypton'], ['MATERIAL:AIRGAP', 'Xenon'], ] # remove an object idf.popidfobject(objtype, 1) assert idf.model.dt[objtype] == [ ['MATERIAL:AIRGAP', 'Argon'], ['MATERIAL:AIRGAP', 'Xenon'], ] lastobject = idf.idfobjects[objtype][-1] idf.removeidfobject(lastobject) assert idf.model.dt[objtype] == [ ['MATERIAL:AIRGAP', 'Argon'], ] # copyidfobject onlyobject = idf.idfobjects[objtype][0] idf.copyidfobject(onlyobject) assert idf.model.dt[objtype] == [ ['MATERIAL:AIRGAP', 'Argon'], ['MATERIAL:AIRGAP', 'Argon'], ] # test some functions objtype = 'FENESTRATIONSURFACE:DETAILED' obj = idf.newidfobject(objtype, Name='A Wall') assert obj.coords == [] assert obj.fieldvalues[1] == 'A Wall' # test defaultvalues=True and defaultvalues=False sim_deftrue = idf.newidfobject('SimulationControl'.upper(), defaultvalues=True) assert sim_deftrue.Do_Zone_Sizing_Calculation == 'No' sim_deffalse = idf.newidfobject('SimulationControl'.upper(), defaultvalues=False) assert sim_deffalse.Do_Zone_Sizing_Calculation == ''
def test_newidfobject(): """py.test for newidfobject""" # make a blank idf # make a function for this and then continue. idf = IDF() idf.new() objtype = 'material:airgap'.upper() obj = idf.newidfobject(objtype, Name='Argon') obj = idf.newidfobject(objtype, Name='Krypton') obj = idf.newidfobject(objtype, Name='Xenon') assert idf.model.dt[objtype] == [['MATERIAL:AIRGAP', 'Argon'], ['MATERIAL:AIRGAP', 'Krypton'], ['MATERIAL:AIRGAP', 'Xenon'], ] # remove an object idf.popidfobject(objtype, 1) assert idf.model.dt[objtype] == [['MATERIAL:AIRGAP', 'Argon'], ['MATERIAL:AIRGAP', 'Xenon'], ] lastobject = idf.idfobjects[objtype][-1] idf.removeidfobject(lastobject) assert idf.model.dt[objtype] == [['MATERIAL:AIRGAP', 'Argon'], ] # copyidfobject onlyobject = idf.idfobjects[objtype][0] idf.copyidfobject(onlyobject) assert idf.model.dt[objtype] == [['MATERIAL:AIRGAP', 'Argon'], ['MATERIAL:AIRGAP', 'Argon'], ] # test some functions objtype = 'FENESTRATIONSURFACE:DETAILED' obj = idf.newidfobject(objtype, Name='A Wall') assert obj.coords == [] assert obj.fieldvalues[1] == 'A Wall' # test defaultvalues=True and defaultvalues=False sim_deftrue = idf.newidfobject('SimulationControl'.upper(), defaultvalues=True) assert sim_deftrue.Do_Zone_Sizing_Calculation == 'No' sim_deffalse = idf.newidfobject('SimulationControl'.upper(), defaultvalues=False) assert sim_deffalse.Do_Zone_Sizing_Calculation == ''
def test_newidfobject(): """py.test for newidfobject""" # make a blank idf # make a function for this and then continue. idf = IDF() idf.new() objtype = 'material:airgap'.upper() obj = idf.newidfobject(objtype, Name='Argon') obj = idf.newidfobject(objtype, Name='Krypton') obj = idf.newidfobject(objtype, Name='Xenon') assert idf.model.dt[objtype] == [ ['MATERIAL:AIRGAP', 'Argon'], ['MATERIAL:AIRGAP', 'Krypton'], ['MATERIAL:AIRGAP', 'Xenon'], ] # remove an object idf.popidfobject(objtype, 1) assert idf.model.dt[objtype] == [ ['MATERIAL:AIRGAP', 'Argon'], ['MATERIAL:AIRGAP', 'Xenon'], ] lastobject = idf.idfobjects[objtype][-1] idf.removeidfobject(lastobject) assert idf.model.dt[objtype] == [ ['MATERIAL:AIRGAP', 'Argon'], ] # copyidfobject onlyobject = idf.idfobjects[objtype][0] idf.copyidfobject(onlyobject) assert idf.model.dt[objtype] == [ ['MATERIAL:AIRGAP', 'Argon'], ['MATERIAL:AIRGAP', 'Argon'], ] # test some functions objtype = 'FENESTRATIONSURFACE:DETAILED' obj = idf.newidfobject(objtype, Name='A Wall') assert obj.coords == [] assert obj.fieldvalues[1] == 'A Wall'
materials[-1].Conductivity = 0.16 materials[-1].Density = 600 materials[-1].Specific_Heat = 1500 # <codecell> print(materials[-1]) # <headingcell level=4> # Copy an existing material # <codecell> Peanutbuttermaterial = materials[-1] idf1.copyidfobject(Peanutbuttermaterial) materials = idf1.idfobjects["MATERIAL"] len(materials) materials[-1] # <headingcell level=2> # Python lesson 3: indentation and looping through lists # <markdowncell> # I'm tired of doing all this work, it's time to make python do some heavy lifting for us! # <markdowncell> # Python can go through each item in a list and perform an operation on any (or every) item in the list.
def delete_cmplx_HVAC_keep_DHW(originalidf_path,savefolder_path): """ An advanced automatic version of the delete_cmplx_HVAC() in version 1.0. Now it is possible to seperate DHW and HVAC systems without manual intervention The only requirement is that DHW elements should have an identifier, or common string throughout. This could be "DHW" or "SHW" or any other string portion that is used repeatedly in all DHW objects. Run Order: 1) Identify HVAC keys in IDF 2) Getting objects from the HVAC keys 3) Searching for DHW objects 4) Deleting all HVAC keys 5) Recreating DHW objects and defining into the IDF :param originalidf_path: Path for the original IDF with a complex HVAC data :param savefolder_path: Path to use where all converted files will be saved :return: """ # Backup originalidf = IDF(originalidf_path) building=originalidf.idfobjects["Building".upper()][0].Name originalidf.saveas(f"{savefolder_path}\\{building}_BuildME_interim.idf") idf=IDF(f"{savefolder_path}\\{building}_BuildME_interim.idf") name=input(colored(0, 255, 255, "Please enter str value for the common DHW naming used in your IDF file:")) # Lets find out all available keys in the IDF file allkeys = idfobjectkeys(idf) # Getting all possible HVAC-related keys by filtering... HVAC_related_list= allkeys[allkeys.index('HVACTEMPLATE:THERMOSTAT'):] HVAC_related_list = HVAC_related_list[:HVAC_related_list.index('MATRIX:TWODIMENSION')] findDHWlist=HVAC_related_list # Gathering all objects individually in our HVAC list and creating a homogenous list list=[] for items in findDHWlist: HVACobjects=idf.idfobjects[items.upper()] for obj in HVACobjects: list.append(obj) # Finding all DHW fields and their corresponding overarching objects objectswithDHW = [] allparameterfields=[] for newobj in list: for fields in newobj.fieldnames: if newobj[fields]!="": if type(newobj[fields])==str: if name in newobj[fields]: allparameterfields.append(newobj[fields]) objectswithDHW.append(newobj) # Deleting all HVAC and DHW objects in our filtered list for HVAC_obj in HVAC_related_list: idf.removeallidfobjects(HVAC_obj) # Recreating DHW elements objectswithDHW_reduced=reduce(lambda l, x: l.append(x) or l if x not in l else l, objectswithDHW, []) for newobjects in objectswithDHW_reduced: idf.copyidfobject(newobjects) idf.saveas(f"{savefolder_path}\\{building}_BuildME_cleaned.idf") editedidf=idf return editedidf
def createnurbsshading(idd_filename, idf_filename, base_surface, shading_str, ctrl_points, evaluated_points=20): ########################################## # loading base surface data from idf file ########################################## # check if IDD has been already set try: IDF.setiddname(idd_filename) except modeleditor.IDDAlreadySetError as e: pass # load idf file into idf collection idf_collection = IDF(idf_filename) # find the named base_surface in idf collection walls = idf_collection.idfobjects['BuildingSurface:Detailed'.upper()] for wall in walls: if wall.Name == base_surface: # named base_surface is now contained in wall break else: # named base_surface was not found in idf file print('epnurbs.createshading: unable to find the base surface', base_surface, 'in', idf_filename) return ################################# # calculating NURBS curve points ################################# from geomdl import NURBS from geomdl import utilities curve = NURBS.Curve() # 1/delta corresponds to the number of trapezoids used in approximation of NURBS shading curve.delta = 1/evaluated_points curve.degree = 3 # add weight 1 to each control point # unless it has been already weighted for cpt in ctrl_points: if len(cpt)<4: cpt.append(1.0) # sets curve control points curve.set_ctrlpts(ctrl_points) # sets curve knot vector curve.knotvector = utilities.generate_knot_vector(curve.degree, len(curve.ctrlpts)) # evaluates curve points curve.evaluate() # make a local copy of evaluated curve points crv_points = curve.curvepts ################################################################################### # feet of perpendiculars from the remaining NURBS curve points to the base surface ################################################################################### # getting the found base_surface's coordinates coord = wall.coords ulc = coord[0] blc = coord[1] brc = coord[2] # find the base_surface's plane normal and normalize it N = crossproduct( (blc[0]-ulc[0], blc[1]-ulc[1], blc[2]-ulc[2]), \ (brc[0]-ulc[0], brc[1]-ulc[1], brc[2]-ulc[2]) ) N = normalize(N) # calculate feet of perpendiculars feet_points = [foot(crv_points[i], ulc, N) for i in range(len(crv_points))] ################################################################################# # create string of idf definitions for trapezoids that approximate NURBS shading ################################################################################# idf_total_shading_def = "" for i in range(1, len(crv_points)): # the width of a trapezoid must be at least 0.01 if distance(crv_points[i-1], crv_points[i])>=0.01 and \ distance(feet_points[i-1], feet_points[i])>=0.01: # are trapezoid arms at least 0.01 or do we have a triangle? if distance(crv_points[i-1], feet_points[i-1])>=0.01: if distance(crv_points[i], feet_points[i])>=0.01: # both arms are at least 0.01, so we have a trapezoid vertices_str = "{:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}".format( feet_points[i-1][0], feet_points[i-1][1], feet_points[i-1][2], crv_points[i-1][0], crv_points[i-1][1], crv_points[i-1][2], crv_points[i][0], crv_points[i][1], crv_points[i][2], feet_points[i][0], feet_points[i][1], feet_points[i][2]) countervertices_str = "{:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}".format( feet_points[i][0], feet_points[i][1], feet_points[i][2], crv_points[i][0], crv_points[i][1], crv_points[i][2], crv_points[i-1][0], crv_points[i-1][1], crv_points[i-1][2], feet_points[i-1][0], feet_points[i-1][1], feet_points[i-1][2]) single_shading_def = shading_str.replace('<IDX>', str(i)).replace('<BASESURFACE>', base_surface).replace('<VERTICES>', vertices_str).replace('<COUNTERVERTICES>', countervertices_str) idf_total_shading_def = idf_total_shading_def + single_shading_def else: # arm i is less than 0.01, so we have a triangle vertices_str = "{:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}".format( feet_points[i-1][0], feet_points[i-1][1], feet_points[i-1][2], crv_points[i-1][0], crv_points[i-1][1], crv_points[i-1][2], feet_points[i][0], feet_points[i][1], feet_points[i][2]) countervertices_str = "{:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}".format( feet_points[i][0], feet_points[i][1], feet_points[i][2], crv_points[i-1][0], crv_points[i-1][1], crv_points[i-1][2], feet_points[i-1][0], feet_points[i-1][1], feet_points[i-1][2]) single_shading_def = shading_str.replace('<IDX>', str(i)).replace('<BASESURFACE>', base_surface).replace('<VERTICES>', vertices_str).replace('<COUNTERVERTICES>', countervertices_str) idf_total_shading_def = idf_total_shading_def + single_shading_def else: # arm i-1 is less than 0.01, but do we still have a triangle? if distance(crv_points[i], feet_points[i])>=0.01: # we have a triangle vertices_str = "{:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}".format( feet_points[i-1][0], feet_points[i-1][1], feet_points[i-1][2], crv_points[i][0], crv_points[i][1], crv_points[i][2], feet_points[i][0], feet_points[i][1], feet_points[i][2]) countervertices_str = "{:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}".format( feet_points[i][0], feet_points[i][1], feet_points[i][2], crv_points[i][0], crv_points[i][1], crv_points[i][2], feet_points[i-1][0], feet_points[i-1][1], feet_points[i-1][2]) single_shading_def = shading_str.replace('<IDX>', str(i)).replace('<BASESURFACE>', base_surface).replace('<VERTICES>', vertices_str).replace('<COUNTERVERTICES>', countervertices_str) idf_total_shading_def = idf_total_shading_def + single_shading_def else: # we do not have a shading element in this case pass # create idf shading objects from the string containing shading definitions from io import StringIO idf_shading = IDF(StringIO(idf_total_shading_def)) # copy idf shading objects to the existing idf file shadings = idf_shading.idfobjects["SHADING:ZONE:DETAILED"] for shading in shadings: idf_collection.copyidfobject(shading) ############################### # at the end, save the changes ############################### idf_collection.save()
def GeoGen(sheet): num_of_zones = sheet.Range("A7").Value construction_template = sheet.Range("AN7").Value #Initialize a blank idf file ts = time.time() #st = datetime.datetime.fromtimestamp(ts).strftime('%Y%m%d %H%M%S') st = datetime.datetime.fromtimestamp(ts).strftime('%Y%m%d-%H%M') blankstr = "" new_idf = IDF(StringIO(blankstr)) new_idf.idfname = "NongeoXport " + st + ".idf" #Import Constructoin Templates const_fname = "ahshrae901_construction_templates.idf" my_const = IDF(const_fname) #Copy All Construction Templates into New idf file materials = my_const.idfobjects['material'.upper()] for i in range(len(materials)): new_idf.copyidfobject(materials[i]) materials_nomass = my_const.idfobjects['material:nomass'.upper()] for i in range(len(materials_nomass)): new_idf.copyidfobject(materials_nomass[i]) materials_airgap = my_const.idfobjects['material:airgap'.upper()] for i in range(len(materials_airgap)): new_idf.copyidfobject(materials_airgap[i]) window_materials_sgs = my_const.idfobjects[ 'windowmaterial:simpleglazingsystem'.upper()] for i in range(len(window_materials_sgs)): new_idf.copyidfobject(window_materials_sgs[i]) window_materials_glz = my_const.idfobjects[ 'windowmaterial:glazing'.upper()] for i in range(len(window_materials_glz)): new_idf.copyidfobject(window_materials_glz[i]) constructions = my_const.idfobjects['construction'.upper()] for i in range(len(constructions)): new_idf.copyidfobject(constructions[i]) #Assign Surface Construction Names Based on User Templates Selections interior_floor_const = "Interior Floor" interior_wall_const = "Interior Wall" interior_ceiling_const = "Interior Ceiling" exterior_floor_const = "ExtSlabCarpet 4in ClimateZone 1-8" if construction_template == "ASHRAE 90.1 2010 Climate Zone 1": exterior_wall_const = "ASHRAE 90.1-2010 ExtWall Mass ClimateZone 1" exterior_window_const = "ASHRAE 90.1-2010 ExtWindow Metal ClimateZone 1" exterior_roof_const = "ASHRAE 90.1-2010 ExtRoof IEAD ClimateZone 1" elif construction_template == "ASHRAE 90.1 2010 Climate Zone 2": exterior_wall_const = "ASHRAE 90.1-2010 ExtWall Mass ClimateZone 2" exterior_window_const = "ASHRAE 90.1-2010 ExtWindow Metal ClimateZone 2" exterior_roof_const = "ASHRAE 90.1-2010 ExtRoof IEAD ClimateZone 2-8" elif construction_template == "ASHRAE 90.1 2010 Climate Zone 3": exterior_wall_const = "ASHRAE 90.1-2010 ExtWall Mass ClimateZone 3" exterior_window_const = "ASHRAE 90.1-2010 ExtWindow Metal ClimateZone 3" exterior_roof_const = "ASHRAE 90.1-2010 ExtRoof IEAD ClimateZone 2-8" elif construction_template == "ASHRAE 90.1 2010 Climate Zone 4": exterior_wall_const = "ASHRAE 90.1-2010 ExtWall Mass ClimateZone 4" exterior_window_const = "ASHRAE 90.1-2010 ExtWindow Metal ClimateZone 4-6" exterior_roof_const = "ASHRAE 90.1-2010 ExtRoof IEAD ClimateZone 2-8" elif construction_template == "ASHRAE 90.1 2010 Climate Zone 5": exterior_wall_const = "ASHRAE 90.1-2010 ExtWall Mass ClimateZone 5" exterior_window_const = "ASHRAE 90.1-2010 ExtWindow Metal ClimateZone 4-6" exterior_roof_const = "ASHRAE 90.1-2010 ExtRoof IEAD ClimateZone 2-8" elif construction_template == "ASHRAE 90.1 2010 Climate Zone 6": exterior_wall_const = "ASHRAE 90.1-2010 ExtWall Mass ClimateZone 6" exterior_window_const = "ASHRAE 90.1-2010 ExtWindow Metal ClimateZone 4-6" exterior_roof_const = "ASHRAE 90.1-2010 ExtRoof IEAD ClimateZone 2-8" elif construction_template == "ASHRAE 90.1 2010 Climate Zone 7": exterior_wall_const = "ASHRAE 90.1-2010 ExtWall Mass ClimateZone 7-8" exterior_window_const = "ASHRAE 90.1-2010 ExtWindow Metal ClimateZone 7-8" exterior_roof_const = "ASHRAE 90.1-2010 ExtRoof IEAD ClimateZone 2-8" elif construction_template == "ASHRAE 90.1 2010 Climate Zone 8": exterior_wall_const = "ASHRAE 90.1-2010 ExtWall Mass ClimateZone 7-8" exterior_window_const = "ASHRAE 90.1-2010 ExtWindow Metal ClimateZone 7-8" exterior_roof_const = "ASHRAE 90.1-2010 ExtRoof IEAD ClimateZone 2-8" ####Start Writing idf Objects ##General idf Objects for IDFXporter #Define Version Objects version = new_idf.newidfobject("version".upper()) version.Version_Identifier = 8.5 #Define Simulation Control Objects simctrl = new_idf.newidfobject("simulationcontrol".upper()) simctrl.Do_Zone_Sizing_Calculation = "Yes" simctrl.Do_System_Sizing_Calculation = "Yes" simctrl.Do_Plant_Sizing_Calculation = "Yes" simctrl.Run_Simulation_for_Sizing_Periods = "No" simctrl.Run_Simulation_for_Weather_File_Run_Periods = "Yes" #Define Building Object building = new_idf.newidfobject("building".upper()) building.Name = "My Building" building.North_Axis = 0 #Define Run Periods Object run_period = new_idf.newidfobject("runperiod".upper()) run_period.Name = "Run Period 1" run_period.Begin_Month = 1 run_period.Begin_Day_of_Month = 1 run_period.End_Month = 12 run_period.End_Day_of_Month = 31 run_period.Day_of_Week_for_Start_Day = "Thursday" run_period.Use_Weather_File_Rain_Indicators = "Yes" run_period.Use_Weather_File_Snow_Indicators = "Yes" #Define Schedule Type Limits Objects sch_tp_limit_1 = new_idf.newidfobject("scheduletypelimits".upper()) sch_tp_limit_1.Name = "Any Number" sch_tp_limit_2 = new_idf.newidfobject("scheduletypelimits".upper()) sch_tp_limit_2.Name = "Fraction" sch_tp_limit_3 = new_idf.newidfobject("scheduletypelimits".upper()) sch_tp_limit_3.Name = "Temperature" sch_tp_limit_4 = new_idf.newidfobject("scheduletypelimits".upper()) sch_tp_limit_4.Name = "On/Off" sch_tp_limit_5 = new_idf.newidfobject("scheduletypelimits".upper()) sch_tp_limit_5.Name = "Control Type" sch_tp_limit_6 = new_idf.newidfobject("scheduletypelimits".upper()) sch_tp_limit_6.Name = "Humidity" sch_tp_limit_7 = new_idf.newidfobject("scheduletypelimits".upper()) sch_tp_limit_7.Name = "Number" ##Additional idf Objects from Params #Define Shadow Calculation Objects shadow_calc = new_idf.newidfobject("shadowcalculation".upper()) shadow_calc.Calculation_Method = "AverageOverDaysInFrequency" #Define SurfaceConvectionAlgorithm:Inside Objects surf_conv_algorithm_in = new_idf.newidfobject( "SurfaceConvectionAlgorithm:Inside".upper()) surf_conv_algorithm_in.Algorithm = "TARP" #Define SurfaceConvectionAlgorithm:Outside Objects surf_conv_algorithm_out = new_idf.newidfobject( "SurfaceConvectionAlgorithm:Outside".upper()) surf_conv_algorithm_out.Algorithm = "DOE-2" #Define HeatBalanceAlgorithm Objects hb_algorithm = new_idf.newidfobject("HeatBalanceAlgorithm".upper()) hb_algorithm.Algorithm = "ConductionTransferFunction" #Define SurfaceProperty:OtherSideConditionsModel Objects surf_prop_oscm = new_idf.newidfobject( "SurfaceProperty:OtherSideConditionsModel".upper()) surf_prop_oscm.Name = "GapConvectionModel" #Define ConvergenceLimits Objects conv_limits = new_idf.newidfobject("ConvergenceLimits".upper()) conv_limits.Minimum_System_Timestep = 0 #Define Global Geometry Rules Objects rule = new_idf.newidfobject("globalgeometryrules".upper()) rule.Starting_Vertex_Position = "UpperLeftCorner" rule.Vertex_Entry_Direction = "Counterclockwise" rule.Coordinate_System = "Relative" rule.Daylighting_Reference_Point_Coordinate_System = "Relative" rule.Rectangular_Surface_Coordinate_System = "Relative" #Define SizingParameters Objects szparams = new_idf.newidfobject("sizing:parameters".upper()) szparams.Heating_Sizing_Factor = 1.25 szparams.Cooling_Sizing_Factor = 1.15 ####Non-Geometric Generator Starts for id in range(7, int(7 + num_of_zones)): zone_name = sheet.Range("B" + str(id)).Value # zone_name = "Thermal Zone: " + zone_name # zone_name = zone_name zone_origin_x = sheet.Range("C" + str(id)).Value zone_origin_y = sheet.Range("D" + str(id)).Value zone_origin_z = sheet.Range("E" + str(id)).Value # prmtr_or_not = sheet.Range("F"+str(id)).Value roof_or_not = sheet.Range("M" + str(id)).Value zone_height = sheet.Range("H" + str(id)).Value zone_length = sheet.Range("I" + str(id)).Value zone_width = sheet.Range("G" + str(id)).Value / sheet.Range("I" + str(id)).Value prmtr1_normal = sheet.Range("K" + str(id)).Value prmtr2_normal = sheet.Range("L" + str(id)).Value grndflr_or_not = sheet.Range("N" + str(id)).Value wind2wall_ratio = sheet.Range("O" + str(id)).Value wind_sill_height = sheet.Range("P" + str(id)).Value #Define Zone Objects zone = new_idf.newidfobject("zone".upper()) zone.Name = zone_name zone.X_Origin = zone_origin_x zone.Y_Origin = zone_origin_y zone.Z_Origin = zone_origin_z zone.Ceiling_Height = zone_height #Define BuildingSurface:Detailed Objects, default as adiabatic, and interior constructions surface_1 = new_idf.newidfobject("buildingsurface:detailed".upper()) surface_1.Name = zone_name + " Surface 1" surface_1.Surface_Type = "Floor" surface_1.Construction_Name = interior_floor_const surface_1.Zone_Name = zone_name surface_1.Outside_Boundary_Condition = "Adiabatic" surface_1.Sun_Exposure = "NoSun" surface_1.Wind_Exposure = "NoWind" surface_1.Vertex_1_Xcoordinate = zone_length surface_1.Vertex_1_Ycoordinate = zone_width surface_1.Vertex_1_Zcoordinate = 0 surface_1.Vertex_2_Xcoordinate = zone_length surface_1.Vertex_2_Ycoordinate = 0 surface_1.Vertex_2_Zcoordinate = 0 surface_1.Vertex_3_Xcoordinate = 0 surface_1.Vertex_3_Ycoordinate = 0 surface_1.Vertex_3_Zcoordinate = 0 surface_1.Vertex_4_Xcoordinate = 0 surface_1.Vertex_4_Ycoordinate = zone_width surface_1.Vertex_4_Zcoordinate = 0 surface_2 = new_idf.newidfobject("buildingsurface:detailed".upper()) surface_2.Name = zone_name + " Surface 2" surface_2.Surface_Type = "Wall" surface_2.Construction_Name = interior_wall_const surface_2.Zone_Name = zone_name surface_2.Outside_Boundary_Condition = "Adiabatic" surface_2.Sun_Exposure = "NoSun" surface_2.Wind_Exposure = "NoWind" surface_2.Vertex_1_Xcoordinate = 0 surface_2.Vertex_1_Ycoordinate = zone_width surface_2.Vertex_1_Zcoordinate = zone_height surface_2.Vertex_2_Xcoordinate = 0 surface_2.Vertex_2_Ycoordinate = zone_width surface_2.Vertex_2_Zcoordinate = 0 surface_2.Vertex_3_Xcoordinate = 0 surface_2.Vertex_3_Ycoordinate = 0 surface_2.Vertex_3_Zcoordinate = 0 surface_2.Vertex_4_Xcoordinate = 0 surface_2.Vertex_4_Ycoordinate = 0 surface_2.Vertex_4_Zcoordinate = zone_height surface_3 = new_idf.newidfobject("buildingsurface:detailed".upper()) surface_3.Name = zone_name + " Surface 3" surface_3.Surface_Type = "Wall" surface_3.Construction_Name = interior_wall_const surface_3.Zone_Name = zone_name surface_3.Outside_Boundary_Condition = "Adiabatic" surface_3.Sun_Exposure = "NoSun" surface_3.Wind_Exposure = "NoWind" surface_3.Vertex_1_Xcoordinate = zone_length surface_3.Vertex_1_Ycoordinate = zone_width surface_3.Vertex_1_Zcoordinate = zone_height surface_3.Vertex_2_Xcoordinate = zone_length surface_3.Vertex_2_Ycoordinate = zone_width surface_3.Vertex_2_Zcoordinate = 0 surface_3.Vertex_3_Xcoordinate = 0 surface_3.Vertex_3_Ycoordinate = zone_width surface_3.Vertex_3_Zcoordinate = 0 surface_3.Vertex_4_Xcoordinate = 0 surface_3.Vertex_4_Ycoordinate = zone_width surface_3.Vertex_4_Zcoordinate = zone_height surface_4 = new_idf.newidfobject("buildingsurface:detailed".upper()) surface_4.Name = zone_name + " Surface 4" surface_4.Surface_Type = "Wall" surface_4.Construction_Name = interior_wall_const surface_4.Zone_Name = zone_name surface_4.Outside_Boundary_Condition = "Adiabatic" surface_4.Sun_Exposure = "NoSun" surface_4.Wind_Exposure = "NoWind" surface_4.Vertex_1_Xcoordinate = zone_length surface_4.Vertex_1_Ycoordinate = 0 surface_4.Vertex_1_Zcoordinate = zone_height surface_4.Vertex_2_Xcoordinate = zone_length surface_4.Vertex_2_Ycoordinate = 0 surface_4.Vertex_2_Zcoordinate = 0 surface_4.Vertex_3_Xcoordinate = zone_length surface_4.Vertex_3_Ycoordinate = zone_width surface_4.Vertex_3_Zcoordinate = 0 surface_4.Vertex_4_Xcoordinate = zone_length surface_4.Vertex_4_Ycoordinate = zone_width surface_4.Vertex_4_Zcoordinate = zone_height surface_5 = new_idf.newidfobject("buildingsurface:detailed".upper()) surface_5.Name = zone_name + " Surface 5" surface_5.Surface_Type = "Wall" surface_5.Construction_Name = interior_wall_const surface_5.Zone_Name = zone_name surface_5.Outside_Boundary_Condition = "Adiabatic" surface_5.Sun_Exposure = "NoSun" surface_5.Wind_Exposure = "NoWind" surface_5.Vertex_1_Xcoordinate = 0 surface_5.Vertex_1_Ycoordinate = 0 surface_5.Vertex_1_Zcoordinate = zone_height surface_5.Vertex_2_Xcoordinate = 0 surface_5.Vertex_2_Ycoordinate = 0 surface_5.Vertex_2_Zcoordinate = 0 surface_5.Vertex_3_Xcoordinate = zone_length surface_5.Vertex_3_Ycoordinate = 0 surface_5.Vertex_3_Zcoordinate = 0 surface_5.Vertex_4_Xcoordinate = zone_length surface_5.Vertex_4_Ycoordinate = 0 surface_5.Vertex_4_Zcoordinate = zone_height surface_6 = new_idf.newidfobject("buildingsurface:detailed".upper()) surface_6.Name = zone_name + " Surface 6" surface_6.Zone_Name = zone_name surface_6.Vertex_1_Xcoordinate = zone_length surface_6.Vertex_1_Ycoordinate = 0 surface_6.Vertex_1_Zcoordinate = zone_height surface_6.Vertex_2_Xcoordinate = zone_length surface_6.Vertex_2_Ycoordinate = zone_width surface_6.Vertex_2_Zcoordinate = zone_height surface_6.Vertex_3_Xcoordinate = 0 surface_6.Vertex_3_Ycoordinate = zone_width surface_6.Vertex_3_Zcoordinate = zone_height surface_6.Vertex_4_Xcoordinate = 0 surface_6.Vertex_4_Ycoordinate = 0 surface_6.Vertex_4_Zcoordinate = zone_height #Change Surface Type if Roof if roof_or_not == "y": surface_6.Surface_Type = "Roof" surface_6.Construction_Name = exterior_roof_const surface_6.Outside_Boundary_Condition = "Outdoors" surface_6.Sun_Exposure = "SunExposed" surface_6.Wind_Exposure = "WindExposed" else: surface_6.Surface_Type = "Ceiling" surface_6.Construction_Name = interior_ceiling_const surface_6.Outside_Boundary_Condition = "Adiabatic" surface_6.Sun_Exposure = "NoSun" surface_6.Wind_Exposure = "NoWind" #Change Surface Type if Ground Floor if grndflr_or_not == "y": surface_1.Construction_Name = exterior_floor_const surface_1.Outside_Boundary_Condition = "Ground" else: pass #Change Surface Type if Perimeter 1 Normal is non-zero if prmtr1_normal == 0: surface_3.Construction_Name = exterior_wall_const surface_3.Outside_Boundary_Condition = "Outdoors" surface_3.Sun_Exposure = "SunExposed" surface_3.Wind_Exposure = "WindExposed" if wind2wall_ratio > 0: sub_surface = new_idf.newidfobject( "FenestrationSurface:Detailed".upper()) sub_surface.Name = zone_name + " Sub Surface 3" sub_surface.Surface_Type = "Window" sub_surface.Construction_Name = exterior_window_const sub_surface.Building_Surface_Name = surface_3.Name sub_surface.Vertex_1_Xcoordinate = zone_length - 0.0254 sub_surface.Vertex_1_Ycoordinate = zone_width sub_surface.Vertex_1_Zcoordinate = zone_length * zone_height * wind2wall_ratio / ( zone_length - 0.0254 * 2) + wind_sill_height sub_surface.Vertex_2_Xcoordinate = zone_length - 0.0254 sub_surface.Vertex_2_Ycoordinate = zone_width sub_surface.Vertex_2_Zcoordinate = wind_sill_height sub_surface.Vertex_3_Xcoordinate = 0.0254 sub_surface.Vertex_3_Ycoordinate = zone_width sub_surface.Vertex_3_Zcoordinate = wind_sill_height sub_surface.Vertex_4_Xcoordinate = 0.0254 sub_surface.Vertex_4_Ycoordinate = zone_width sub_surface.Vertex_4_Zcoordinate = sub_surface.Vertex_1_Zcoordinate else: pass elif prmtr1_normal == 90: surface_4.Construction_Name = exterior_wall_const surface_4.Outside_Boundary_Condition = "Outdoors" surface_4.Sun_Exposure = "SunExposed" surface_4.Wind_Exposure = "WindExposed" if wind2wall_ratio > 0: sub_surface = new_idf.newidfobject( "FenestrationSurface:Detailed".upper()) sub_surface.Name = zone_name + " Sub Surface 4" sub_surface.Surface_Type = "Window" sub_surface.Construction_Name = exterior_window_const sub_surface.Building_Surface_Name = surface_4.Name sub_surface.Vertex_1_Xcoordinate = zone_length sub_surface.Vertex_1_Ycoordinate = 0.0254 sub_surface.Vertex_1_Zcoordinate = zone_width * zone_height * wind2wall_ratio / ( zone_width - 0.0254 * 2) + wind_sill_height # sub_surface.Vertex_1_Zcoordinate = zone_length*zone_height*wind2wall_ratio/sub_surface.Vertex_1_Xcoordinate+wind_sill_height sub_surface.Vertex_2_Xcoordinate = zone_length sub_surface.Vertex_2_Ycoordinate = 0.0254 sub_surface.Vertex_2_Zcoordinate = wind_sill_height sub_surface.Vertex_3_Xcoordinate = zone_length sub_surface.Vertex_3_Ycoordinate = zone_width - 0.0254 sub_surface.Vertex_3_Zcoordinate = wind_sill_height sub_surface.Vertex_4_Xcoordinate = zone_length sub_surface.Vertex_4_Ycoordinate = zone_width - 0.0254 sub_surface.Vertex_4_Zcoordinate = sub_surface.Vertex_1_Zcoordinate else: pass elif prmtr1_normal == 180: surface_5.Construction_Name = exterior_wall_const surface_5.Outside_Boundary_Condition = "Outdoors" surface_5.Sun_Exposure = "SunExposed" surface_5.Wind_Exposure = "WindExposed" if wind2wall_ratio > 0: sub_surface = new_idf.newidfobject( "FenestrationSurface:Detailed".upper()) sub_surface.Name = zone_name + " Sub Surface 5" sub_surface.Surface_Type = "Window" sub_surface.Construction_Name = exterior_window_const sub_surface.Building_Surface_Name = surface_5.Name sub_surface.Vertex_1_Xcoordinate = 0.0254 sub_surface.Vertex_1_Ycoordinate = 0 sub_surface.Vertex_1_Zcoordinate = zone_length * zone_height * wind2wall_ratio / ( zone_length - 0.0254 * 2) + wind_sill_height # sub_surface.Vertex_1_Zcoordinate = zone_length*zone_height*wind2wall_ratio/sub_surface.Vertex_1_Xcoordinate+wind_sill_height sub_surface.Vertex_2_Xcoordinate = 0.0254 sub_surface.Vertex_2_Ycoordinate = 0 sub_surface.Vertex_2_Zcoordinate = wind_sill_height sub_surface.Vertex_3_Xcoordinate = zone_length - 0.0254 sub_surface.Vertex_3_Ycoordinate = 0 sub_surface.Vertex_3_Zcoordinate = wind_sill_height sub_surface.Vertex_4_Xcoordinate = zone_length - 0.0254 sub_surface.Vertex_4_Ycoordinate = 0 sub_surface.Vertex_4_Zcoordinate = sub_surface.Vertex_1_Zcoordinate else: pass elif prmtr1_normal == 270: surface_2.Construction_Name = exterior_wall_const surface_2.Outside_Boundary_Condition = "Outdoors" surface_2.Sun_Exposure = "SunExposed" surface_2.Wind_Exposure = "WindExposed" if wind2wall_ratio > 0: sub_surface = new_idf.newidfobject( "FenestrationSurface:Detailed".upper()) sub_surface.Name = zone_name + " Sub Surface 2" sub_surface.Surface_Type = "Window" sub_surface.Construction_Name = exterior_window_const sub_surface.Building_Surface_Name = surface_2.Name sub_surface.Vertex_1_Xcoordinate = 0 sub_surface.Vertex_1_Ycoordinate = zone_width - 0.0254 sub_surface.Vertex_1_Zcoordinate = zone_width * zone_height * wind2wall_ratio / ( zone_width - 2 * 0.0254) + wind_sill_height # sub_surface.Vertex_1_Zcoordinate = zone_length*zone_height*wind2wall_ratio/sub_surface.Vertex_1_Xcoordinate+wind_sill_height sub_surface.Vertex_2_Xcoordinate = 0 sub_surface.Vertex_2_Ycoordinate = zone_width - 0.0254 sub_surface.Vertex_2_Zcoordinate = wind_sill_height sub_surface.Vertex_3_Xcoordinate = 0 sub_surface.Vertex_3_Ycoordinate = 0.0254 sub_surface.Vertex_3_Zcoordinate = wind_sill_height sub_surface.Vertex_4_Xcoordinate = 0 sub_surface.Vertex_4_Ycoordinate = 0.0254 sub_surface.Vertex_4_Zcoordinate = sub_surface.Vertex_1_Zcoordinate else: pass else: pass #Change Surface Type if Perimeter 2 Normal is non-zero if prmtr2_normal == 0: surface_3.Construction_Name = exterior_wall_const surface_3.Outside_Boundary_Condition = "Outdoors" surface_3.Sun_Exposure = "SunExposed" surface_3.Wind_Exposure = "WindExposed" if wind2wall_ratio > 0: sub_surface = new_idf.newidfobject( "FenestrationSurface:Detailed".upper()) sub_surface.Name = zone_name + " Sub Surface 3" sub_surface.Surface_Type = "Window" sub_surface.Construction_Name = exterior_window_const sub_surface.Building_Surface_Name = surface_3.Name sub_surface.Vertex_1_Xcoordinate = zone_length - 0.0254 sub_surface.Vertex_1_Ycoordinate = zone_width sub_surface.Vertex_1_Zcoordinate = zone_length * zone_height * wind2wall_ratio / ( zone_length - 0.0254 * 2) + wind_sill_height sub_surface.Vertex_2_Xcoordinate = zone_length - 0.0254 sub_surface.Vertex_2_Ycoordinate = zone_width sub_surface.Vertex_2_Zcoordinate = wind_sill_height sub_surface.Vertex_3_Xcoordinate = 0.0254 sub_surface.Vertex_3_Ycoordinate = zone_width sub_surface.Vertex_3_Zcoordinate = wind_sill_height sub_surface.Vertex_4_Xcoordinate = 0.0254 sub_surface.Vertex_4_Ycoordinate = zone_width sub_surface.Vertex_4_Zcoordinate = sub_surface.Vertex_1_Zcoordinate else: pass elif prmtr2_normal == 90: surface_4.Construction_Name = exterior_wall_const surface_4.Outside_Boundary_Condition = "Outdoors" surface_4.Sun_Exposure = "SunExposed" surface_4.Wind_Exposure = "WindExposed" if wind2wall_ratio > 0: sub_surface = new_idf.newidfobject( "FenestrationSurface:Detailed".upper()) sub_surface.Name = zone_name + " Sub Surface 4" sub_surface.Surface_Type = "Window" sub_surface.Construction_Name = exterior_window_const sub_surface.Building_Surface_Name = surface_4.Name sub_surface.Vertex_1_Xcoordinate = zone_length sub_surface.Vertex_1_Ycoordinate = 0.0254 sub_surface.Vertex_1_Zcoordinate = zone_width * zone_height * wind2wall_ratio / ( zone_width - 0.0254 * 2) + wind_sill_height # sub_surface.Vertex_1_Zcoordinate = zone_length*zone_height*wind2wall_ratio/sub_surface.Vertex_1_Xcoordinate+wind_sill_height sub_surface.Vertex_2_Xcoordinate = zone_length sub_surface.Vertex_2_Ycoordinate = 0.0254 sub_surface.Vertex_2_Zcoordinate = wind_sill_height sub_surface.Vertex_3_Xcoordinate = zone_length sub_surface.Vertex_3_Ycoordinate = zone_width - 0.0254 sub_surface.Vertex_3_Zcoordinate = wind_sill_height sub_surface.Vertex_4_Xcoordinate = zone_length sub_surface.Vertex_4_Ycoordinate = zone_width - 0.0254 sub_surface.Vertex_4_Zcoordinate = sub_surface.Vertex_1_Zcoordinate else: pass elif prmtr2_normal == 180: surface_5.Construction_Name = exterior_wall_const surface_5.Outside_Boundary_Condition = "Outdoors" surface_5.Sun_Exposure = "SunExposed" surface_5.Wind_Exposure = "WindExposed" if wind2wall_ratio > 0: sub_surface = new_idf.newidfobject( "FenestrationSurface:Detailed".upper()) sub_surface.Name = zone_name + " Sub Surface 5" sub_surface.Surface_Type = "Window" sub_surface.Construction_Name = exterior_window_const sub_surface.Building_Surface_Name = surface_5.Name sub_surface.Vertex_1_Xcoordinate = 0.0254 sub_surface.Vertex_1_Ycoordinate = 0 sub_surface.Vertex_1_Zcoordinate = zone_length * zone_height * wind2wall_ratio / ( zone_length - 0.0254 * 2) + wind_sill_height # sub_surface.Vertex_1_Zcoordinate = zone_length*zone_height*wind2wall_ratio/sub_surface.Vertex_1_Xcoordinate+wind_sill_height sub_surface.Vertex_2_Xcoordinate = 0.0254 sub_surface.Vertex_2_Ycoordinate = 0 sub_surface.Vertex_2_Zcoordinate = wind_sill_height sub_surface.Vertex_3_Xcoordinate = zone_length - 0.0254 sub_surface.Vertex_3_Ycoordinate = 0 sub_surface.Vertex_3_Zcoordinate = wind_sill_height sub_surface.Vertex_4_Xcoordinate = zone_length - 0.0254 sub_surface.Vertex_4_Ycoordinate = 0 sub_surface.Vertex_4_Zcoordinate = sub_surface.Vertex_1_Zcoordinate else: pass elif prmtr2_normal == 270: surface_2.Construction_Name = exterior_wall_const surface_2.Outside_Boundary_Condition = "Outdoors" surface_2.Sun_Exposure = "SunExposed" surface_2.Wind_Exposure = "WindExposed" if wind2wall_ratio > 0: sub_surface = new_idf.newidfobject( "FenestrationSurface:Detailed".upper()) sub_surface.Name = zone_name + " Sub Surface 2" sub_surface.Surface_Type = "Window" sub_surface.Construction_Name = exterior_window_const sub_surface.Building_Surface_Name = surface_2.Name sub_surface.Vertex_1_Xcoordinate = 0 sub_surface.Vertex_1_Ycoordinate = zone_width - 0.0254 sub_surface.Vertex_1_Zcoordinate = zone_width * zone_height * wind2wall_ratio / ( zone_width - 2 * 0.0254) + wind_sill_height # sub_surface.Vertex_1_Zcoordinate = zone_length*zone_height*wind2wall_ratio/sub_surface.Vertex_1_Xcoordinate+wind_sill_height sub_surface.Vertex_2_Xcoordinate = 0 sub_surface.Vertex_2_Ycoordinate = zone_width - 0.0254 sub_surface.Vertex_2_Zcoordinate = wind_sill_height sub_surface.Vertex_3_Xcoordinate = 0 sub_surface.Vertex_3_Ycoordinate = 0.0254 sub_surface.Vertex_3_Zcoordinate = wind_sill_height sub_surface.Vertex_4_Xcoordinate = 0 sub_surface.Vertex_4_Ycoordinate = 0.0254 sub_surface.Vertex_4_Zcoordinate = sub_surface.Vertex_1_Zcoordinate else: pass else: pass new_idf.save() this_idf = new_idf.idfname # dir_path = os.path.dirname(os.path.realpath(this_idf)) # # os.chdir('..') # shutil.copy2(dir_path, '/templates/) # complete target filename given # shutil.copy2('/src/file.ext', '/dst/dir') # target filename is /dst/dir/file.ext pre, ext = os.path.splitext(this_idf) os.rename(this_idf, pre + ".pxt")
def convert_idf_to_BuildME(idf_path, save_folder_path, replace_string="-en-std-replaceme", replace_string_value="-non-standard", base=False): """ This function creates an individual edited IDF file that is compatible with BuildME framework Requirements for a clear run: 1)Window-Skylight Difference should be addressed in construction name (e.g. name contains "sky") Run Order: -Adds replaceme fields, -Renames Materials, -Renames Constructions, -Defines non-standard U values; if replace_string_value is set accordingly. :param idf_path: path for idf file to be converted, standard file should be used forthe non-standard IDF creation :param save_folder_path: New location folder path, where the new IDF will be saved :param replace_string: Replaceme content (i.e., -en-std-replaceme-res-replacemem-chrt-replaceme), should start with "-" :param replace_string_value: Replacer string corresponding to the replace string (i.e., -standard-RES0-1920), should start with "-" :param base: Boolean value to declare whether the IDF file corresponding to a base IDF :return: A compatible file with BuildME for an IDF with specific features """ idf1 = IDF(idf_path) print( "Conversion is initialized, construction and surface objects are being converted..." ) # CONVERTING CONSTRUCTION NAMES AND INSERTING REPLACEME STRINGS: # It inserts a replaceme string to the building surface object's construction field, adds a new identical construction to the construction object # and renames the construction name corresponding to the replaceme string for items in idf1.idfobjects["BuildingSurface:Detailed".upper()]: for obj in idf1.idfobjects["Construction".upper()]: if obj.Name == items.Construction_Name: if items.Surface_Type == "Roof": items.Construction_Name = f"ext-roof{replace_string}" newcons = idf1.copyidfobject(obj) newcons.Name = f"ext-roof{replace_string_value}" if items.Surface_Type == "Ceiling": if "int" or "ceiling" in items.Construction_Name: items.Construction_Name = f"int-ceiling{replace_string}" newcons = idf1.copyidfobject(obj) newcons.Name = f"int-ceiling{replace_string_value}" if items.Surface_Type == "Floor": if items.Outside_Boundary_Condition == "Surface": items.Construction_Name = f"int-floor{replace_string}" newcons = idf1.copyidfobject(obj) newcons.Name = f"int-floor{replace_string_value}" if items.Outside_Boundary_Condition == "Adiabatic" or items.Outside_Boundary_Condition == "Ground" or items.Outside_Boundary_Condition == "Outdoors" or items.Outside_Boundary_Condition == "GroundSlabPreprocessorAverage": items.Construction_Name = f"ext-floor{replace_string}" newcons = idf1.copyidfobject(obj) newcons.Name = f"ext-floor{replace_string_value}" if items.Surface_Type == "Wall": if "int" in items.Construction_Name: items.Construction_Name = f"int-wall{replace_string}" newcons = idf1.copyidfobject(obj) newcons.Name = f"int-wall{replace_string_value}" else: items.Construction_Name = f"ext-wall{replace_string}" newcons = idf1.copyidfobject(obj) newcons.Name = f"ext-wall{replace_string_value}" if 'CONSTRUCTION:FFACTORGROUNDFLOOR' in [x for x in idf1.idfobjects]: for floor in idf1.idfobjects[ "Construction:FfactorGroundFloor".upper()]: if floor.Name == items.Construction_Name: if items.Surface_Type == "Floor": if items.Outside_Boundary_Condition == "GroundFCfactorMethod": items.Construction_Name = f"{items.Construction_Name}{replace_string}" newcons = idf1.copyidfobject(floor) newcons.Name = f"{floor.Name}{replace_string_value}" if 'CONSTRUCTION:CFACTORUNDERGROUNDWALL' in [ x for x in idf1.idfobjects ]: for wall in idf1.idfobjects[ "Construction:CfactorUndergroundWall".upper()]: if wall.Name == items.Construction_Name: if items.Surface_Type == "Wall": if items.Outside_Boundary_Condition == "GroundFCfactorMethod" or items.Outside_Boundary_Condition == "Adiabatic": items.Construction_Name = f"{items.Construction_Name}{replace_string}" newcons = idf1.copyidfobject(wall) newcons.Name = f"{wall.Name}{replace_string_value}" if 'WINDOW' in [x for x in idf1.idfobjects]: for fenest in idf1.idfobjects["Window".upper()]: fenest.Construction_Name = f"ext-window{replace_string}" if 'DOOR' in [x for x in idf1.idfobjects]: for fenest in idf1.idfobjects["Door".upper()]: fenest.Construction_Name = f"ext-door{replace_string}" if 'FENESTRATIONSURFACE:DETAILED' in [x for x in idf1.idfobjects]: for fenest in idf1.idfobjects["FenestrationSurface:Detailed".upper()]: for obj in idf1.idfobjects["Construction".upper()]: if fenest.Construction_Name == obj.Name: if fenest.Surface_Type == "Window": # there might be skylights: if "sky" in fenest.Construction_Name: fenest.Construction_Name = f"ext-skywindow{replace_string}" newcons = idf1.copyidfobject(obj) newcons.Name = f"ext-skywindow{replace_string_value}" else: fenest.Construction_Name = f"ext-window{replace_string}" newcons = idf1.copyidfobject(obj) newcons.Name = f"ext-window{replace_string_value}" if fenest.Surface_Type == "GlassDoor": fenest.Construction_Name = f"ext-window{replace_string}" newcons = idf1.copyidfobject(obj) newcons.Name = f"ext-window{replace_string_value}" if fenest.Surface_Type == "Door": fenest.Construction_Name = f"ext-door{replace_string}" newcons = idf1.copyidfobject(obj) newcons.Name = f"ext-door{replace_string_value}" # Deleting duplicated construction names unique = reduce(lambda l, x: l.append(x) or l if x not in l else l, idf1.idfobjects["Construction".upper()], []) idf1.removeallidfobjects("CONSTRUCTION") for newcons in unique: idf1.copyidfobject(newcons) if 'CONSTRUCTION:FFACTORGROUNDFLOOR' in [x for x in idf1.idfobjects]: unique = reduce( lambda l, x: l.append(x) or l if x not in l else l, idf1.idfobjects["Construction:FfactorGroundFloor".upper()], []) idf1.removeallidfobjects("CONSTRUCTION:FFACTORGROUNDFLOOR") for newcons in unique: idf1.copyidfobject(newcons) if 'CONSTRUCTION:CFACTORUNDERGROUNDWALL' in [x for x in idf1.idfobjects]: unique = reduce( lambda l, x: l.append(x) or l if x not in l else l, idf1.idfobjects["Construction:CfactorUndergroundWall".upper()], []) idf1.removeallidfobjects("CONSTRUCTION:CFACTORUNDERGROUNDWALL") for newcons in unique: idf1.copyidfobject(newcons) print("Material objects are being converted...") # CONVERTING MATERIALS # For the -non-standard version, values are replaced with %30 worse performing numbers based on the standard/base version. if "-non-standard" in replace_string_value: for items in idf1.idfobjects["Material".upper()]: items.Conductivity = float(items.Conductivity) * 1.3 items.Name = f"{items.Name}{replace_string_value}" for items in idf1.idfobjects["Material:NoMass".upper()]: items.Thermal_Resistance = float(items.Thermal_Resistance) * 0.7 items.Name = f"{items.Name}{replace_string_value}" for items in idf1.idfobjects[ "WindowMaterial:SimpleGlazingSystem".upper()]: items.UFactor = float(items.UFactor) * 1.3 items.Name = f"{items.Name}{replace_string_value}" if 'CONSTRUCTION:FFACTORGROUNDFLOOR' in [x for x in idf1.idfobjects]: for items in idf1.idfobjects[ "Construction:FfactorGroundFloor".upper()]: items.FFactor = float(items.FFactor) * 0.7 if 'CONSTRUCTION:CFACTORUNDERGROUNDWALL' in [ x for x in idf1.idfobjects ]: for items in idf1.idfobjects[ "Construction:CfactorUndergroundWall".upper()]: items.CFactor = float(items.CFactor) * 1.3 # Material names are changed as they represent different values across standards, some identifiers are added based on replacer_string. else: for items in idf1.idfobjects["Material".upper()]: items.Name = f"{items.Name}{replace_string_value}" for items in idf1.idfobjects["Material:NoMass".upper()]: items.Name = f"{items.Name}{replace_string_value}" for items in idf1.idfobjects[ "WindowMaterial:SimpleGlazingSystem".upper()]: items.Name = f"{items.Name}{replace_string_value}" # Construction material layer names are matched with above material changes for items in idf1.idfobjects["Construction".upper()]: for fields in items.fieldnames: if fields == "key": continue if fields == "Name": continue else: if items[fields] == "": continue else: items[fields] = items[fields] + f"{replace_string_value}" # SAVING THE IDF FILE building = str(idf1.idfobjects["Building".upper()][0].Name) if base == True: idf1.idfobjects["Building".upper( )][0].Name = f"BASE{building}{replace_string_value}" idf1.saveas( f"{save_folder_path}/BASE{building}{replace_string_value}-converted.idf" ) else: idf1.idfobjects[ "Building".upper()][0].Name = f"{building}{replace_string_value}" idf1.saveas( f"{save_folder_path}/{building}{replace_string_value}-converted.idf" ) print( f'{idf1.idfobjects["Building".upper()][0].Name} IDF file is converted and saved...' ) return idf1
# <headingcell level=3> # Copying/Adding an idf object # <markdowncell> # Having deleted two "MATERIAL" objects, we have only one left. Let us make a copy of this object and add it to our idf file # <codecell> onlymaterial = idf.idfobjects["MATERIAL"][0] # <codecell> idf.copyidfobject(onlymaterial) # <codecell> print(idf.idfobjects["MATERIAL"]) # <markdowncell> # So now we have a copy of the material. You can use this method to copy idf objects from other idf files too. # <headingcell level=2> # Making an idf object with named arguments # <markdowncell>
class Model: """ The environment class. """ model_import_flag = False @classmethod def set_energyplus_folder(cls, path): """ Add the pyenergyplus into the path so the program can find the EnergyPlus. :parameter path: The installation path of the EnergyPlus 9.3.0. :type path: str :return: None """ sys.path.insert(0, path) IDF.setiddname(f"{path}Energy+.idd") cls.model_import_flag = True def __init__(self, idf_file_name: str = None, prototype: str = None, climate_zone: str = None, weather_file: str = None, heating_type: str = None, foundation_type: str = None, agent: Agent = None, reward=None, eplus_naming_dict=None, eplus_var_types=None, buffer_capacity=None, buffer_seed=None, buffer_chkpt_dir=None, tmp_idf_path=None): """ Initialize the building by loading the IDF file to the model. :parameter idf_file_name: The relative path to the IDF file. Use it if you want to use your own model. :parameter prototype: Either "multi" and "single", indicates the Multi-family low-rise apartment building and Single-family detached house. :parameter climate_zone: The climate zone code of the building. Please refer to https://codes.iccsafe.org/content/iecc2018/chapter-3-ce-general-requirements. :parameter weather_file: The relative path to the weather file associate with the building. :parameter heating_type: Select one from "electric", "gas", "oil", and "pump" :parameter foundation_type: Select one from "crawspace", "heated", "slab", and "unheated" :parameter agent: The user-defined Agent class object if the agent is implemented in a class. :parameter reward: The user-defined reward class object that contains a reward(state, actions) method. :parameter eplus_naming_dict: A dictionary map the state variable name to some specified names. :parameter eplus_var_types: A dictionary contains the state name and the state source location. :parameter buffer_capacity: The maximum number of historical state, action, new_state pair store in the buffer. :parameter buffer_seed: The random seed when sample from the buffer. :parameter buffer_chkpt_dir: The location of the buffer checkpoint should save. """ if not Model.model_import_flag: raise ImportError("You have to set the energyplus folder first") self.api = None self.current_state = dict() self.idf = None self.occupancy = None self.run_parameters = None self.queue = EventQueue() self.agent = agent self.ignore_list = set() self.zone_names = None self.thermal_names = None self.counter = 0 self.replay = ReplayBuffer(buffer_capacity, buffer_seed, buffer_chkpt_dir) self.warmup_complete = False self.terminate = False self.wait_for_step = Event() self.wait_for_state = Event() self.parent, self.child_energy = Pipe(duplex=True) self.child = None self.use_lock = False self.reward = reward self.eplus_naming_dict = dict( ) if eplus_naming_dict is None else eplus_naming_dict self.eplus_var_types = dict( ) if eplus_var_types is None else eplus_var_types self.prev_reward = None self.total_timestep = -1 self.leap_weather = False self.state_modifier = StateModifier() # TODO: Validate input parameters if idf_file_name is None and climate_zone is not None: idf_file_name = f"./buildings/{prototype}_{climate_zone}_{heating_type}_{foundation_type}.idf" if weather_file is None and climate_zone is not None: weather_file = f"./weathers/{climate_zone}.epw" if tmp_idf_path is None: self.input_idf = "input.idf" else: self.input_idf = os.path.join(tmp_idf_path, "input.idf") self.run_parameters = ["-d", "result", self.input_idf] if weather_file: self.run_parameters = ["-w", weather_file] + self.run_parameters with open(weather_file, 'r') as w_file: for line in w_file: line = line.split(',') if len(line) > 3 and line[0].upper( ) == "HOLIDAYS/DAYLIGHT SAVINGS": self.leap_weather = True if line[1].upper( ) == "YES" else False break try: self.idf = IDF(idf_file_name) except: raise ValueError( "IDF file is damaged or not match with your EnergyPlus version." ) @staticmethod def name_reformat(name): """ Convert the entry from the space separated entry to the underline separated entry to match the IDF. :parameter name: The space separated entry. :return: The underline separated entry. """ name = name.replace(' ', '_').replace(':', '').split('_') return '_'.join([word for word in name]) def list_all_available_configurations(self): """ Generate a list of all type of components appeared in the current building. :return: list of components entry. """ return list(self.idf.idfobjects.keys()) def get_all_configurations(self): """ Read the IDF file, and return the content formatted with the IDD file. :return: the full IDF file with names and comments aside. """ return self.idf.idfobjects def get_sub_configuration(self, idf_header_name: str): """ Show all available settings for the given type of component. :parameter idf_header_name: The type of the component. :return: List of settings entry. """ idf_header_name = idf_header_name.upper() if not self.idf.idfobjects.get(idf_header_name): raise KeyError(f"No {idf_header_name} section in current IDF file") return self.idf.idfobjects[idf_header_name][0].fieldnames def get_available_names_under_group(self, idf_header_name: str): """ Given the type of components, find all available components in the building by their entry. :parameter idf_header_name: The type of the component. :return: List of names. """ idf_header_name = idf_header_name.upper() available_names = self.get_sub_configuration(idf_header_name) if "Name" in available_names: return [ entry["Name"] for entry in self.idf.idfobjects[idf_header_name] ] else: for field_name in available_names: if "name" in field_name.lower(): return [ entry[field_name] for entry in self.idf.idfobjects[idf_header_name] ] raise KeyError(f"No entry field available for {idf_header_name}") def get_configuration(self, idf_header_name: str, component_name: str = None): """ Given the type of component, the entry of the target component, find the settings of that component. :parameter idf_header_name: The type of the component. :parameter component_name: The entry of the component. :return: Settings of this component. """ idf_header_name = idf_header_name.upper() if component_name is None: return self.idf.idfobjects[idf_header_name] else: names = self.get_available_names_under_group(idf_header_name) if component_name in names: return self.idf.idfobjects[idf_header_name][names.index( component_name)] else: raise KeyError( f"Failed to locate {component_name} in {idf_header_name}") def get_value_range(self, idf_header_name: str, field_name: str, validate: bool = False): """ Get the range of acceptable values of the specific setting. :parameter idf_header_name: The type of the component. :parameter field_name: The setting entry. :parameter validate: Set to True to check the current value is valid or not. :return: Validation result or the range of all acceptable values retrieved from the IDD file. """ idf_header_name = idf_header_name.upper() field_name = field_name.replace(' ', '_') if field_name not in self.get_sub_configuration(idf_header_name): raise KeyError( f"Failed to locate {field_name} in {idf_header_name}") if validate: return self.idf.idfobjects[idf_header_name][0].checkrange( field_name) else: return self.idf.idfobjects[idf_header_name][0].getrange(field_name) def add_configuration(self, idf_header_name: str, values: dict = None): """ Create and add a new component into the building model with the specific type and setting values. :parameter idf_header_name: The type of the component. :parameter values: A dictionary map the setting entry and the setting value. :return: The new component. """ idf_header_name = idf_header_name.upper() object = self.idf.newidfobject(idf_header_name.upper()) if values is None: return object for key, value in values.items(): key = Model.name_reformat(key) if isinstance(value, (int, float)): exec(f"object.{key} = {value}") else: exec(f"object.{key} = '{value}'") return object def delete_configuration(self, idf_header_name: str, component_name: str = None): """ Delete an existing component from the building model. :parameter idf_header_name: The type of the component. :parameter component_name: The entry of the component. :return: None. """ idf_header_name = idf_header_name.upper() if not self.idf.idfobjects.get(idf_header_name): raise KeyError(f"No {idf_header_name} section in current IDF file") if component_name is None: while len(self.idf.idfobjects[idf_header_name]): self.idf.popidfobject(idf_header_name, 0) else: names = self.get_available_names_under_group(idf_header_name) if component_name in names: self.idf.popidfobject(idf_header_name, names.index(component_name)) else: raise KeyError( f"Failed to locate {component_name} in {idf_header_name}") def edit_configuration(self, idf_header_name: str, identifier: dict, update_values: dict): """ Edit an existing component in the building model. :parameter idf_header_name: The type of the component. :parameter identifier: A dictionary map the setting entry and the setting value to locate the target component. :parameter update_values: A dictionary map the setting entry and the setting value that needs to update. :return: None. """ idf_header_name = idf_header_name.upper() if not self.idf.idfobjects.get(idf_header_name): raise KeyError(f"No {idf_header_name} section in current IDF file") fields = self.get_sub_configuration(idf_header_name) for entry in self.idf.idfobjects[idf_header_name]: valid = True for key, value in identifier.items(): key = Model.name_reformat(key) if key in fields: if entry[key] != value: valid = False if valid: for key, value in update_values.items(): key = Model.name_reformat(key) if isinstance(value, (int, float)): exec(f"entry.{key} = {value}") else: exec(f"entry.{key} = '{value}'") def _get_thermal_names(self): """ Initialize all available thermal zones. :return: None """ people_zones = self.get_configuration("People") self.thermal_names = dict() for zone in people_zones: try: if zone[Model.name_reformat("Thermal Comfort Model 1 Type")]: self.thermal_names[zone["Name"]] = zone[ Model.name_reformat("Zone or ZoneList Name")] except BadEPFieldError: pass def save_idf_file(self, path: str): """ Save the modified building model for EnergyPlus simulation :parameter path: The relative path to the modified IDF file. :return: None. """ self.idf.saveas(path) def _initialization(self): """ Initialize the EnergyPlus simulation by letting the EnergyPlus finish the warmup. :return: None """ if not self.api.exchange.api_data_fully_ready(): return self.warmup_complete = True def _generate_output_files(self): """ Assert errors to terminate the simulation after the warmup in order to generate the EDD file to list all available actions for the current building. :return: None """ assert False def _step_callback(self): """ Get the state value at each timestep, and modify the building model based on the actions from the ``EventQueue``. :return: None """ # print("Child: Not ready") if not self.api.exchange.api_data_fully_ready( ) or not self.warmup_complete: return current_state = dict() # print("Child: Simulating") current_state["timestep"] = self.counter # print(self.get_date()) current_state["time"] = self.get_date() current_state["temperature"] = dict() current_state["occupancy"] = dict() current_state["terminate"] = self.total_timestep == self.counter # if self.occupancy is not None: # current_state["occupancy"] = {zone: value[self.counter] for zone, value in self.occupancy.items()} for name in self.zone_names: handle = self.api.exchange.get_variable_handle( "Zone People Occupant Count", name) if handle == -1: continue current_state["occupancy"][ name] = self.api.exchange.get_variable_value(handle) for name in self.zone_names: handle = self.api.exchange.get_variable_handle( "Zone Air Temperature", name) if handle == -1: continue # print("Child: Simulating 2") current_state["temperature"][ name] = self.api.exchange.get_variable_value(handle) handle = self.api.exchange.get_meter_handle("Heating:EnergyTransfer") current_state["energy"] = self.api.exchange.get_meter_value(handle) if self.reward is not None: current_state["reward"] = self.prev_reward # print("Child: Simulating 1") if "Zone Thermal Comfort Fanger Model PMV" in self.get_available_names_under_group( "Output:Variable"): current_state["PMV"] = dict() for zone in self.thermal_names: handle = self.api.exchange.get_variable_handle( "Zone Thermal Comfort Fanger Model PMV", zone) if handle == -1: continue current_state["PMV"][self.thermal_names[ zone]] = self.api.exchange.get_variable_value(handle) # Add state values state_vars = self.get_current_state_variables() # Add for temp extra output for entry in self.idf.idfobjects['OUTPUT:VARIABLE']: # we only care about the output vars for Gnu-RL if (entry['Variable_Name'], entry['Key_Value']) in self.eplus_naming_dict.keys() or \ (entry['Variable_Name'], entry['Key_Value']) in state_vars: var_name = entry['Variable_Name'] # if the key value is not associated with a zone return None for variable handler # key_val = entry['Key_Value'] if entry['Key_Value'] != '*' else None if entry['Key_Value'] == '*': key_val = self.eplus_var_types.get(var_name, None) if key_val is None: continue else: key_val = entry['Key_Value'] handle = self.api.exchange.get_variable_handle( var_name, key_val) if handle == -1: continue # name the state value based on Gnu-RL paper key = self.eplus_naming_dict.get( (var_name, entry['Key_Value']), f"{var_name}_{key_val}") current_state[key] = self.api.exchange.get_variable_value( handle) self.state_modifier.get_update_states(current_state, self) # current_state.update(update_dict) # print(current_state) if self.use_lock: # print("Child: Sending current states") self.child_energy.send(current_state) self.wait_for_state.set() # Take all actions self.wait_for_step.clear() # print("Child: Waiting for actions") if not self.child_energy.poll(): self.wait_for_step.wait() # print("Child: Receiving actions") events = self.child_energy.recv() else: # print(self.current_state) if self.counter != 0: self.replay.push( self.current_state, self.queue.get_event(self.current_state["timestep"]), current_state, current_state["terminate"]) self.current_state = current_state # self.historical_values.append(self.current_state) events = self.queue.trigger(self.counter) self.counter += 1 # Trigger modifiers # for modifier in self.modifier: # modifier.update(current_state) # print("Child: executing actions") # print("Child: Printing Reward") # Calculate Reward if self.reward is not None: self.prev_reward = self.reward.reward(current_state, events) # Trigger events for key in events["actuator"]: component_type, control_type, actuator_key = key.split("|*|") value = events["actuator"][key][1] handle = self.api.exchange.get_actuator_handle( component_type, control_type, actuator_key) if handle == -1: raise ValueError('Actuator handle could not be found: ', component_type, control_type, actuator_key) self.api.exchange.set_actuator_value(handle, value) for key in events["global"]: var_name = key value = events["global"][key][1] handle = self.api.exchange.get_global_handle(var_name) if handle == -1: raise ValueError('Actuator handle could not be found: ', component_type, control_type, actuator_key) self.api.exchange.set_global_value(handle, value) # if self.use_lock: # # wait for next call of step # self.wait_for_step.clear() # self.wait_for_step.wait() # else: if not self.use_lock and self.agent: self.agent.step(self.current_state, self.queue, self.counter - 1) def step(self, action_list=None): """ Add all actions into the ``EventQueue``, and then generate the state value of the next timestep. :parameter action_list: list of dictionarys contains the arguments for ``EventQueue.schedule_event()``. :return: The state value of the current timestep. """ if action_list is not None: for action in action_list: self.queue.schedule_event(**action) # print("Parent: Sending actions") self.parent.send(self.queue.trigger(self.counter)) self.counter += 1 # Let process grab and execute actions # print("Parent: Releasing child's lock") self.wait_for_state.clear() self.wait_for_step.set() # print("Parent: Waiting for state values") if not self.parent.poll(): self.wait_for_state.wait() self.wait_for_state.clear() current_state = self.parent.recv() # if isinstance(current_state, dict): self.replay.push(self.current_state, self.queue.get_event(self.current_state["timestep"]), current_state, current_state["terminate"]) self.current_state = current_state # self.historical_values.append(self.current_state) if current_state["terminate"]: self.terminate = True self.parent.send(self.queue.trigger(self.counter)) self.wait_for_step.set() self.child.join() # self.replay.terminate() self.wait_for_state.clear() # print("Parent: received state values") return self.current_state def is_terminate(self): """ Determine if the simulation is finished or not. :return: True if the simulation is done, and False otherwise. """ return self.terminate def reset(self): """ Clear the actions and buffer, reset the environment and start the simulation. :return: The initial state of the simulation. """ self._init_simulation() self.counter = 0 self.total_timestep = self.get_total_timestep() - 1 self.queue = EventQueue() self.replay.reset() # self.ignore_list = set() self.wait_for_state.clear() self.wait_for_step.clear() self.terminate = False self.use_lock = True self.parent, self.child_energy = Pipe(duplex=True) self.child = Process(target=self.simulate) self.child.start() # print("Waiting") if not self.parent.poll(): self.wait_for_state.wait() self.current_state = self.parent.recv() # self.historical_values.append(self.current_state) self.wait_for_state.clear() return self.current_state def get_total_timestep(self): if "-w" not in self.run_parameters: return self.get_configuration( "Timestep")[0].Number_of_Timesteps_per_Hour * 24 * 8 elif len(self.get_configuration("RunPeriod")) == 0: raise ValueError( "Your IDF files does not specify the run period." "Please manually edit the IDF file or use Model().set_runperiod(...)" ) run_period = self.get_configuration("RunPeriod")[0] start = datetime( year=run_period.Begin_Year if run_period.Begin_Year else 2000, month=run_period.Begin_Month, day=run_period.Begin_Day_of_Month) end = datetime( year=run_period.End_Year if run_period.End_Year else start.year, month=run_period.End_Month, day=run_period.End_Day_of_Month) end += timedelta(days=1) if not self.leap_weather: offset = 0 for year in range(start.year, end.year + 1): if isleap(year) and datetime(year, 2, 29) > start and datetime( year, 2, 29) < end: offset += 1 end -= timedelta(days=offset) timestep = self.get_configuration( "Timestep")[0].Number_of_Timesteps_per_Hour if 60 % timestep != 0: timestep = 60 // round(60 / timestep) return int((end - start).total_seconds() // 3600 * timestep) def simulate(self, terminate_after_warmup=False): """ Run the whole simulation once. If user use this method instead of the reset function, the user need to provide the Agent. :parameter terminate_after_warmup: True if the simulation should terminate after the warmup. :return: None. """ from pyenergyplus.api import EnergyPlusAPI self.replay.set_ignore(self.state_modifier.get_ignore_by_checkpoint()) if not self.use_lock: self._init_simulation() # for entry in self.zone_names: # print(entry) # self.current_handle["temperature"][entry] = \ # self.api.exchange.get_variable_handle("Zone Air Temperature", entry) # self.current_handle["temperature"] = self.api.exchange.get_variable_handle("SITE OUTDOOR AIR DRYBULB TEMPERATURE", "ENVIRONMENT") # self.current_handle["energy"] = self.api.exchange.get_meter_handle("Electricity:Facility") self.api = EnergyPlusAPI() if not terminate_after_warmup: self.api.runtime.callback_after_new_environment_warmup_complete( self._initialization) self.api.runtime.callback_begin_system_timestep_before_predictor( self._step_callback) else: self.api.runtime.callback_begin_new_environment( self._generate_output_files) self.api.runtime.run_energyplus(self.run_parameters) # if self.use_lock: # self.child_energy.send("Terminated") # self.wait_for_state.set() # else: # self.replay.terminate() def _init_simulation(self): """ Save the modified building model and initialize the zones for states. :return: None. """ try: self.get_configuration("Output:Variable", "Zone People Occupant Count") except KeyError: self.add_configuration( "Output:Variable", { "Key Value": '*', "Variable Name": "Zone People Occupant Count", "Reporting Frequency": "Timestep" }) self.idf.saveas(self.input_idf) self.use_lock = False self.zone_names = self.get_available_names_under_group("Zone") self._get_thermal_names() self.warmup_complete = False def get_current_state_variables(self): """ Find the current entries in the state. :return: List of entry names that is currently available in the state. """ state_values = list( set(self.get_possible_state_variables()) - self.ignore_list) state_values.sort() return state_values def select_state_variables(self, entry=None, index=None): """ Select interested state entries. If selected entry is not available for the current building, it will be ignored. :parameter entry: Entry names and corresponding objects that the state of the environment should have. :parameter index: Index of all available entries that the state of the environment should have. :return: None. """ current_state = self.get_current_state_variables() if entry is None: entry = list() elif isinstance(entry, tuple): entry = list(entry) if index is not None: if isinstance(index, int): index = [index] for i in index: if i < len(current_state): entry.append(current_state[i]) self.ignore_list = set( self.get_possible_state_variables()) - set(entry) def add_state_variables(self, entry): """ Add entries to the state. If selected entry is not available for the current building, it will be ignored. :parameter entry: Entry names and corresponding objects that the state of the environment should have. :return: None. """ if not self.ignore_list: return if isinstance(entry, tuple): entry = [entry] self.ignore_list -= set(entry) def remove_state_variables(self, entry): """ Remove entries from the state. If selected entry is not available in the state, it will be ignored. :parameter entry: Entry names and corresponding objects that the state of the environment should not have. :return: None. """ if isinstance(entry, tuple): entry = [entry] self.ignore_list = self.ignore_list.union(set(entry)) def pop_state_variables(self, index): """ Remove entries from the state by its index. If selected index is not available in the state, it will be ignored. :parameter index: Entry index that the state of the environment should not have. :return: All entry names that is removed. """ current_state = self.get_current_state_variables() pop_values = list() if isinstance(index, int): index = [index] for i in index: if i < len(current_state): self.ignore_list.add(current_state[i]) pop_values.append(current_state[i]) return pop_values def get_possible_state_variables(self): """ Get all available state entries. This list of entries only depends on the building architecture. :return: List of available state entry names. """ output = [(var["Variable_Name"], var["Key_Value"]) for var in self.get_configuration("Output:Variable") if var["Key_Value"] != "*"] output.sort() return output def get_possible_actions(self): """ Get all available actions that the user-defined agent can take. This list of actions only depends on the building architecture. :return: List of available actions in dictionaries. """ if not os.path.isfile("./result/eplusout.edd"): if not self.get_configuration("Output:EnergyManagementSystem"): self.add_configuration( "Output:EnergyManagementSystem", values={ "Actuator Availability Dictionary Reporting": "Verbose", "Internal Variable Availability Dictionary Reporting": "Verbose", "EMS Runtime Language Debug Output Level": "ErrorsOnly" }) try: self.simulate(terminate_after_warmup=True) except AssertionError: pass actions = list() with open("./result/eplusout.edd", 'r') as edd: for line in edd: line = line.strip() if len(line) == 0 or line[0] == '!': continue line = line.split(',') actions.append({ "Component Type": line[2], "Control Type": line[3], "Actuator Key": line[1] }) return actions def get_link_zones(self): """ Generate a graph that shows the connectivity of zones of the current building. :return: A bi-directional graph represented by a dictionary where the key is the source zone name and the value is a set of all neighbor zone name. """ link_zones = {"Outdoor": set()} wall_to_zone = {} walls = self.get_configuration("BuildingSurface:Detailed") for wall in walls: if wall.Surface_Type != "WALL": continue wall_to_zone[wall.Name] = wall.Zone_Name link_zones[wall.Zone_Name] = set() for wall in walls: if wall.Surface_Type != "WALL": continue if wall.Outside_Boundary_Condition == "Outdoors": link_zones[wall.Zone_Name].add("Outdoor") link_zones["Outdoor"].add(wall.Zone_Name) elif wall.Outside_Boundary_Condition_Object: link_zones[wall_to_zone[ wall.Outside_Boundary_Condition_Object]].add( wall.Zone_Name) link_zones[wall.Zone_Name].add( wall_to_zone[wall.Outside_Boundary_Condition_Object]) return link_zones def get_date(self): """ Get the current time in the simulation environment. :return: None """ year = self.api.exchange.year() month = self.api.exchange.month() day = self.api.exchange.day_of_month() hour = self.api.exchange.hour() minute = self.api.exchange.minutes() current_time = datetime(year, month, day, hour) + timedelta(minutes=minute) return current_time def get_windows(self): """ Get the zone-window matching dictionary based on the IDF file. :return: A dictionary where key is the zone name, and the value is a set of window available in the zone. """ zone_window = { name: set() for name in self.get_available_names_under_group("Zone") } all_window = dict() for window in self.get_configuration("FenestrationSurface:Detailed"): if window.Surface_Type != "WINDOW": continue all_window[window.Building_Surface_Name] = window.Name for wall in self.get_configuration("BuildingSurface:Detailed"): if wall.Surface_Type != "WALL": continue if wall.Name in all_window: zone_window[wall.Zone_Name].add(all_window[wall.Name]) return zone_window def get_doors(self): """ Get the zone-door matching dictionary based on the IDF file. :return: A dictionary where key is the zone name, and the value is a set of door available in the zone. """ zone_door = { name: set() for name in self.get_available_names_under_group("Zone") } all_door = dict() for door in self.get_configuration("FenestrationSurface:Detailed"): if door.Surface_Type != "GLASSDOOR": continue all_door[door.Building_Surface_Name] = door.Name for wall in self.get_configuration("BuildingSurface:Detailed"): if wall.Surface_Type != "WALL": continue if wall.Name in all_door: zone_door[wall.Zone_Name].add(all_door[wall.Name]) return zone_door def get_lights(self): """ Get the zone-light matching dictionary based on the IDF file. :return: A dictionary where key is the zone name, and the value is a set of light available in the zone. """ zone_light = { name: set() for name in self.get_available_names_under_group("Zone") } for light in self.get_configuration("Lights"): zone_light[light.Zone_or_ZoneList_Name].add(light.Name) return zone_light def get_blinds(self): """ Get the zone-blind matching dictionary based on the IDF file. :return: A dictionary where key is the zone name, and the value is a set of blind available in the zone. """ window_with_blinds = set() for shade in self.get_configuration("WindowShadingControl"): window_with_blinds.add(shade.Fenestration_Surface_1_Name) zone_blinds = self.get_windows() for zone in zone_blinds: zone_blinds[zone] = zone_blinds[zone].intersection( window_with_blinds) return zone_blinds def set_blinds(self, windows, blind_material_name=None, shading_control_type='AlwaysOff', setpoint=50, agent_control=False): """ Install blinds that can be controlled on some given windows. :param windows: An iterable object that includes all windows' name that plan to install the blind. :param blind_material_name: The name of an existing blind in the IDF file as the blind for all windows. :param shading_control_type: Specify default EPlus control strategy (only works if control=False) :param setpoint: Specify default blind angle. :param agent_control: False if using a default EPlus control strategy or no control (ie blinds always off). True if using an external agent to control the blinds. :return: None """ if agent_control: shading_control_type = 'OnIfScheduleAllows' blind_material = None if blind_material_name: try: blind_material = self.get_configuration( "WindowMaterial:Blind", blind_material_name) except KeyError: pass zone_window = self.get_windows() for zone in zone_window: for window in zone_window[zone]: if window in windows: window_idf = self.get_configuration( "FenestrationSurface:Detailed", window) if blind_material is None: blind = { "Name": f"{window}_blind", "Slat Orientation": "Horizontal", "Slat Width": 0.025, "Slat Separation": 0.01875, "Front Side Slat Beam Solar Reflectance": 0.8, "Back Side Slat Beam Solar Reflectance": 0.8, "Front Side Slat Diffuse Solar Reflectance": 0.8, "Back Side Slat Diffuse Solar Reflectance": 0.8, "Slat Beam Visible Transmittance": 0.0 } blind_mat = self.add_configuration( "WindowMaterial:Blind", values=blind) else: blind_mat = self.idf.copyidfobject(blind_material) blind_mat.Name = blind_mat.Name + f" {window}" shading = { "Name": f"{window}_blind_shading", "Zone Name": zone, "Shading Type": "InteriorBlind", "Shading Device Material Name": f"{blind_mat.Name}", "Shading Control Type": shading_control_type, "Setpoint": setpoint, "Type of Slat Angle Control for Blinds": "ScheduledSlatAngle", "Fenestration Surface 1 Name": window_idf.Name } if agent_control: shading[ "Slat Angle Schedule Name"] = f"{window}_shading_schedule" shading["Multiple Surface Control Type"] = "Group" shading["Shading Control Is Scheduled"] = "Yes" angle_schedule = { "Name": f"{window}_shading_schedule", "Schedule Type Limits Name": "Angle", "Hourly Value": 45 } self.add_configuration("Schedule:Constant", values=angle_schedule) self.add_configuration("WindowShadingControl", values=shading) def set_occupancy(self, occupancy, locations): """ Include the occupancy schedule generated by the OccupancyGenerator to the model as the occupancy data in EnergyPlus simulated environment is broken. :param occupancy: Numpy matrix contains the number of occupanct in each zone at each time slot. :param locations: List of zone names. :return: None """ occupancy = occupancy.astype(int) self.occupancy = { locations[i]: occupancy[i, :] for i in range(len(locations)) } if "Outdoor" in self.occupancy.keys(): self.occupancy.pop("Outdoor") if "busy" in self.occupancy.keys(): self.occupancy.pop("busy") def set_runperiod(self, days, start_year: int = 2000, start_month: int = 1, start_day: int = 1, specify_year: bool = False): """ Set the simulation run period. :param days: How many days in total the simulation should perform. :param start_year: Start from which year :param start_month: Start from which month of the year :param start_day: Start from which day of the month :param specify_year: Use default year or a specific year when simulation is within a year. :return: None """ if "-w" not in self.run_parameters: raise KeyError("You must include a weather file to set run period") start = datetime(start_year, start_month, start_day) end = start + timedelta(days=days - 1) if not self.leap_weather: test_year = start_year - 1 while datetime(test_year, 1, 1) < end: test_year += 1 if not isleap(test_year): continue if datetime(test_year, 2, 29) > start and datetime( test_year, 2, 29) < end: end += timedelta(days=1) values = { "Begin Month": start_month, "Begin Day of Month": start_day, "End Month": end.month, "End Day of Month": end.day } if end.year != start_year or specify_year: values.update({"Begin Year": start_year, "End Year": end.year}) run_setting = self.get_configuration("RunPeriod") if len(run_setting) == 0: values["Name"] = "RunPeriod 1" self.add_configuration("RunPeriod", values) else: name = self.get_configuration("RunPeriod")[0].Name self.edit_configuration("RunPeriod", {"Name": name}, values) def set_timestep(self, timestep_per_hour): """ Set the timestep per hour for the simulation. :param timestep_per_hour: How many timesteps within a hour. :return: None """ self.get_configuration( "Timestep")[0].Number_of_Timesteps_per_Hour = timestep_per_hour def add_state_modifier(self, model): """ Add a state modifier model, including predictive model, state estimator, controller, etc. :param model: A class object that follows the template (contains step(true_state, environment) method). :return: None """ self.state_modifier.add_model(model) def flatten_state(self, order, state=None): """ Flatten the state to a list of values by a given order. :param order: The order that the values should follow. :param state: The state to flatten. If not specified, then the current state is selected. :return: List of values follows the given order. """ if state is None: state = self.current_state return [self.current_state.get(name, None) for name in order] def sample_buffer(self, batch_size): """ Sample a batch of experience from the replay buffer. :param batch_size: Number of entries in a batch. :return: (state, action, next state, is terminate) """ return self.replay.sample(batch_size) def sample_flattened_buffer(self, order, batch_size): """ Sample a batch of experience from the replay buffer and flatten the states by a given order. :param order: The order that the values should follow. :param batch_size: Number of entries in a batch. :return: (state, action, next state, is terminate) where states are flatten. """ state, action, next_state, done = self.replay.sample(batch_size) for i, row in state: state[i] = self.flatten_state(order, row) for i, row in next_state: next_state[i] = self.flatten_state(order, row) return state, action, next_state, done
def pickupHVACsystem(idfHVACfile, idfObject, zoneloadlist, NodeNumb): idfHVAC = IDF(idfHVACfile) loopLen = len(idfObject.idfobjects['SIZING:ZONE']) for j in range(1, loopLen): idfHVAC.copyidfobject(idfHVAC.idfobjects["SIZING:SYSTEM"][0]) addedobject = idfHVAC.idfobjects["SIZING:SYSTEM"][j] addedobject.AirLoop_Name = 'Air Loop HVAC ' + str(j + 1) idfHVAC.copyidfobject( idfHVAC.idfobjects["AIRTERMINAL:SINGLEDUCT:UNCONTROLLED"][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects["ZONEHVAC:EQUIPMENTLIST"][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects["ZONEHVAC:EQUIPMENTCONNECTIONS"][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['FAN:ONOFF'][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['COIL:COOLING:DX:SINGLESPEED'][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['COIL:HEATING:ELECTRIC'][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['COIL:HEATING:DX:SINGLESPEED'][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['AIRLOOPHVAC:UNITARYSYSTEM'][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['CONTROLLER:OUTDOORAIR'][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['CONTROLLER:MECHANICALVENTILATION'][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['AIRLOOPHVAC:CONTROLLERLIST'][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['AVAILABILITYMANAGER:SCHEDULED'][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['AVAILABILITYMANAGER:SCHEDULED'][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['AVAILABILITYMANAGERASSIGNMENTLIST'][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['AVAILABILITYMANAGERASSIGNMENTLIST'][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['OUTDOORAIR:NODELIST'][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['NODELIST'][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['NODELIST'][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['NODELIST'][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['AIRLOOPHVAC'][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['OUTDOORAIR:MIXER'][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['AIRLOOPHVAC:OUTDOORAIRSYSTEM:EQUIPMENTLIST'] [0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['AIRLOOPHVAC:OUTDOORAIRSYSTEM'][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['AIRLOOPHVAC:ZONESPLITTER'][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['AIRLOOPHVAC:SUPPLYPATH'][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['AIRLOOPHVAC:ZONEMIXER'][0]) idfHVAC.copyidfobject( idfHVAC.idfobjects['AirLoopHVAC:ReturnPath'.upper()][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['BRANCH'][0]) idfHVAC.copyidfobject(idfHVAC.idfobjects['BRANCHLIST'][0]) ##idfHVAC.printidf() for i in range(0, loopLen): ZoneName = idfObject.idfobjects['SIZING:ZONE'][ i].Zone_or_ZoneList_Name.upper() ClgLoad = zoneloadlist['LoadClg_' + ZoneName] HtgLoad = zoneloadlist['LoadHtg_' + ZoneName] SingleDuct = idfHVAC.idfobjects['AIRTERMINAL:SINGLEDUCT:UNCONTROLLED'][ i] SingleDuct.Name = 'Diffuser ' + str(i + 1) SingleDuct.Zone_Supply_Air_Node_Name = 'Node ' + str(loopLen + 5 + i * 5 + NodeNumb) EquipmentLs = idfHVAC.idfobjects['ZONEHVAC:EQUIPMENTLIST'][i] EquipmentLs.Name = ZoneName + ' Equipment List' EquipmentLs.Zone_Equipment_1_Name = SingleDuct.Name EquipmentConnect = idfHVAC.idfobjects['ZONEHVAC:EQUIPMENTCONNECTIONS'][ i] EquipmentConnect.Zone_Name = ZoneName EquipmentConnect.Zone_Conditioning_Equipment_List_Name = ZoneName + ' Equipment List' EquipmentConnect.Zone_Air_Inlet_Node_or_NodeList_Name = ZoneName + ' Inlet Node List' EquipmentConnect.Zone_Air_Node_Name = 'Node ' + str(i + 1 + NodeNumb) EquipmentConnect.Zone_Return_Air_Node_or_NodeList_Name = 'Node ' + str( 6 * loopLen + 4 + 4 * i + NodeNumb) FAN = idfHVAC.idfobjects['FAN:ONOFF'][i] FAN.Name = 'Fan On Off ' + str(i + 1) FAN.Air_Inlet_Node_Name = 'Node ' + str(6 * loopLen + 3 + 4 * i + NodeNumb) FAN.Air_Outlet_Node_Name = 'Unitary - DX Heat Pump - Cycling - Elec reheat ' + str( i + 1) + ' Fan - Cooling Coil Node' CoolingDX = idfHVAC.idfobjects['COIL:COOLING:DX:SINGLESPEED'][i] CoolingDX.Name = '1 Spd DX Clg Coil ' + str(i + 1) CoolingDX.Air_Inlet_Node_Name = 'Unitary - DX Heat Pump - Cycling - Elec reheat ' + str( i + 1) + ' Fan - Cooling Coil Node' CoolingDX.Air_Outlet_Node_Name = 'Unitary - DX Heat Pump - Cycling - Elec reheat ' + str( i + 1) + ' Cooling Coil - Heating Coil Node' HeatingElec = idfHVAC.idfobjects['COIL:HEATING:ELECTRIC'][i] HeatingElec.Name = 'Elec Htg Coil ' + str(i + 1) HeatingElec.Air_Inlet_Node_Name = 'Unitary - DX Heat Pump - Cycling - Elec reheat ' + str( i + 1) + ' Heating Coil - Supplemental Coil Node' HeatingElec.Air_Outlet_Node_Name = 'Node ' + str(loopLen + 2 + 5 * i + NodeNumb) HeatingDX = idfHVAC.idfobjects['COIL:HEATING:DX:SINGLESPEED'][i] HeatingDX.Name = '1 Spd DX Htg Coil ' + str(i + 1) HeatingDX.Air_Inlet_Node_Name = 'Unitary - DX Heat Pump - Cycling - Elec reheat ' + str( i + 1) + ' Cooling Coil - Heating Coil Node' HeatingDX.Air_Outlet_Node_Name = 'Unitary - DX Heat Pump - Cycling - Elec reheat ' + str( i + 1) + ' Heating Coil - Supplemental Coil Node' UnitarySystem = idfHVAC.idfobjects['AIRLOOPHVAC:UNITARYSYSTEM'][i] UnitarySystem.Name = 'Unitary - DX Heat Pump - Cycling - Elec reheat ' + str( i + 1) UnitarySystem.Controlling_Zone_or_Thermostat_Location = ZoneName UnitarySystem.Air_Inlet_Node_Name = 'Node ' + str(loopLen * 6 + 3 + 4 * i + NodeNumb) UnitarySystem.Air_Outlet_Node_Name = 'Node ' + str(loopLen + 2 + 5 * i + NodeNumb) UnitarySystem.Supply_Fan_Name = FAN.Name UnitarySystem.Heating_Coil_Name = HeatingDX.Name UnitarySystem.Cooling_Coil_Name = CoolingDX.Name UnitarySystem.Supplemental_Heating_Coil_Name = HeatingElec.Name ControllerOA = idfHVAC.idfobjects['CONTROLLER:OUTDOORAIR'][i] ControllerOA.Name = 'Outdoor Air Controller ' + str(i + 1) ControllerOA.Relief_Air_Outlet_Node_Name = 'Node ' + str(loopLen * 6 + 2 + 4 * i + NodeNumb) ControllerOA.Return_Air_Node_Name = 'Node ' + str(loopLen + 1 + 5 * i + NodeNumb) ControllerOA.Mixed_Air_Node_Name = 'Node ' + str(loopLen * 6 + 3 + 4 * i + NodeNumb) ControllerOA.Actuator_Node_Name = 'Node ' + str(loopLen * 6 + 1 + 4 * i + NodeNumb) ControllerOA.Mechanical_Ventilation_Controller_Name = 'Controller Mechanical Ventilation ' + str( i + 1) ControllerVent = idfHVAC.idfobjects[ 'CONTROLLER:MECHANICALVENTILATION'][i] ControllerVent.Name = 'Controller Mechanical Ventilation ' + str(i + 1) ControllerVent.Zone_1_Name = ZoneName ControllerVent.Design_Specification_Outdoor_Air_Object_Name_1 = idfObject.idfobjects[ 'SIZING:ZONE'][i].Design_Specification_Outdoor_Air_Object_Name ControllerVent.Design_Specification_Zone_Air_Distribution_Object_Name_1 = idfObject.idfobjects[ 'SIZING:ZONE'][ i].Design_Specification_Zone_Air_Distribution_Object_Name OAControllerls = idfHVAC.idfobjects['AIRLOOPHVAC:CONTROLLERLIST'][i] OAControllerls.Name = 'OA System ' + str(i + 1) + ' Controller List' OAControllerls.Controller_1_Name = ControllerOA.Name AVAILALHVAC = idfHVAC.idfobjects['AVAILABILITYMANAGER:SCHEDULED'][2 * i] AVAILALHVAC.Name = 'Air Loop HVAC ' + str(i + 1) + ' Availability Manager' AVAILALHVAC.Schedule_Name = 'Always On Discrete' AVAILOA = idfHVAC.idfobjects['AVAILABILITYMANAGER:SCHEDULED'][2 * i + 1] AVAILOA.Name = 'OA System ' + str(i + 1) + ' Availability Manager' AVAILOA.Schedule_Name = 'Always On Discrete' AMAL = idfHVAC.idfobjects['AVAILABILITYMANAGERASSIGNMENTLIST'][2 * i] AMAL.Name = 'Air Loop HVAC ' + str(i + 1) + 'Availability Manager List' AMAL.Availability_Manager_1_Name = AVAILALHVAC.Name AMAL2 = idfHVAC.idfobjects['AVAILABILITYMANAGERASSIGNMENTLIST'][2 * i + 1] AMAL2.Name = 'OA System ' + str(i + 1) + ' Availability Manager List' AMAL2.Availability_Manager_1_Name = AVAILOA.Name OANodeLs = idfHVAC.idfobjects['OUTDOORAIR:NODELIST'][i] OANodeLs.Node_or_NodeList_Name_1 = 'Node ' + str(6 * loopLen + 1 + 4 * i + NodeNumb) NodeLs_ZoneInlet = idfHVAC.idfobjects['NODELIST'][i] NodeLs_ZoneInlet.Name = ZoneName + " Inlet Node List" NodeLs_ZoneInlet.Node_1_Name = 'Node ' + str(loopLen + 5 + 5 * i + NodeNumb) NodeLs_ALSupplyOut = idfHVAC.idfobjects['NODELIST'][loopLen + 2 * i] NodeLs_ALDemandIn = idfHVAC.idfobjects['NODELIST'][loopLen + 2 * i + 1] NodeLs_ALSupplyOut.Name = 'Air Loop HVAC ' + str( i + 1) + ' Supply Outlet Nodes' NodeLs_ALDemandIn.Name = 'Air Loop HVAC ' + str( i + 1) + ' Demand Inlet Nodes' NodeLs_ALSupplyOut.Node_1_Name = 'Node ' + str(loopLen + 2 + 5 * i + NodeNumb) NodeLs_ALDemandIn.Node_1_Name = 'Node ' + str(loopLen + 3 + 5 * i + NodeNumb) ALHVAC = idfHVAC.idfobjects['AIRLOOPHVAC'][i] ALHVAC.Name = 'Air Loop HVAC ' + str(i + 1) ALHVAC.Availability_Manager_List_Name = AMAL.Name ALHVAC.Branch_List_Name = 'Air Loop HVAC ' + str( i + 1) + ' Supply Branches' ALHVAC.Supply_Side_Inlet_Node_Name = 'Node ' + str(loopLen + 1 + 5 * i + NodeNumb) ALHVAC.Demand_Side_Outlet_Node_Name = 'Node ' + str(loopLen + 4 + 5 * i + NodeNumb) ALHVAC.Demand_Side_Inlet_Node_Names = NodeLs_ALDemandIn.Name ALHVAC.Supply_Side_Outlet_Node_Names = NodeLs_ALSupplyOut.Name OAMixer = idfHVAC.idfobjects['OUTDOORAIR:MIXER'][i] OAMixer.Name = 'OA System ' + str(i + 1) + ' Outdoor Air Mixer' OAMixer.Mixed_Air_Node_Name = 'Node ' + str(6 * loopLen + 3 + 4 * i + NodeNumb) OAMixer.Outdoor_Air_Stream_Node_Name = 'Node ' + str(6 * loopLen + 1 + 4 * i + NodeNumb) OAMixer.Relief_Air_Stream_Node_Name = 'Node ' + str(6 * loopLen + 2 + 4 * i + NodeNumb) OAMixer.Return_Air_Stream_Node_Name = 'Node ' + str(loopLen + 1 + 5 * i + NodeNumb) ALOASysEquip = idfHVAC.idfobjects[ 'AIRLOOPHVAC:OUTDOORAIRSYSTEM:EQUIPMENTLIST'][i] ALOASysEquip.Name = 'OA System ' + str(i + 1) + ' Equipment List' ALOASysEquip.Component_1_Name = OAMixer.Name OASys = idfHVAC.idfobjects['AIRLOOPHVAC:OUTDOORAIRSYSTEM'][i] OASys.Name = 'OA System ' + str(i + 1) OASys.Controller_List_Name = OAControllerls.Name OASys.Outdoor_Air_Equipment_List_Name = ALOASysEquip.Name OASys.Availability_Manager_List_Name = AMAL2.Name ZoneSplitter = idfHVAC.idfobjects['AIRLOOPHVAC:ZONESPLITTER'][i] ZoneSplitter.Name = 'Air Loop HVAC Zone Splitter ' + str(i + 1) ZoneSplitter.Inlet_Node_Name = 'Node ' + str(loopLen + 3 + 5 * i + NodeNumb) ZoneSplitter.Outlet_1_Node_Name = 'Node ' + str(loopLen + 5 + 5 * i + NodeNumb) SupplyPath = idfHVAC.idfobjects['AIRLOOPHVAC:SUPPLYPATH'][i] NodeNumber = str(loopLen + 3 + 5 * i + NodeNumb) SupplyPath.Name = 'Air Loop HVAC ' + str( i + 1) + ' Node ' + NodeNumber + ' Supply Path' SupplyPath.Supply_Air_Path_Inlet_Node_Name = 'Node ' + NodeNumber SupplyPath.Component_1_Name = 'Air Loop HVAC Zone Splitter ' + str(i + 1) ZoneMixer = idfHVAC.idfobjects['AIRLOOPHVAC:ZONEMIXER'][i] ZoneMixer.Name = 'Air Loop HVAC Zone Mixer ' + str(i + 1) ZoneMixer.Outlet_Node_Name = 'Node ' + str(loopLen + 4 + 5 * i + NodeNumb) ZoneMixer.Inlet_1_Node_Name = 'Node ' + str(loopLen * 6 + 4 + 4 * i + NodeNumb) RetPath = idfHVAC.idfobjects['AirLoopHVAC:ReturnPath'.upper()][i] RetPath.Name = 'Air Loop HVAC ' + str(i + 1) + ' Return Path' RetPath.Return_Air_Path_Outlet_Node_Name = 'Node ' + str(loopLen + 4 + 5 * i + NodeNumb) RetPath.Component_1_Name = ZoneMixer.Name Branch = idfHVAC.idfobjects['BRANCH'][i] Branch.Name = 'Air Loop HVAC ' + str(i + 1) + ' Main Branch' Branch.Component_1_Name = OASys.Name Branch.Component_1_Inlet_Node_Name = 'Node ' + str(loopLen + 1 + i * 5 + NodeNumb) Branch.Component_1_Outlet_Node_Name = 'Node ' + str(loopLen * 6 + 3 + 4 * i + NodeNumb) Branch.Component_2_Name = UnitarySystem.Name Branch.Component_2_Inlet_Node_Name = Branch.Component_1_Outlet_Node_Name Branch.Component_2_Outlet_Node_Name = 'Node ' + str(loopLen + 2 + 5 * i + NodeNumb) BranchLs = idfHVAC.idfobjects['BRANCHLIST'][i] BranchLs.Name = ALHVAC.Branch_List_Name BranchLs.Branch_1_Name = Branch.Name if abs(int(HtgLoad)) in range(0, 4689) and abs(int(ClgLoad)) in range( 0, 3399): FAN.Maximum_Flow_Rate = '0.1665974474496' CoolingDX.Gross_Rated_Total_Cooling_Capacity = '3399.62441399778' CoolingDX.Gross_Rated_Cooling_COP = '3.78' HeatingDX.Gross_Rated_Heating_Capacity = '4689.13712275556' HeatingDX.Gross_Rated_Heating_COP = '3.08' elif abs(int(HtgLoad)) in range(0, 7912) and abs( int(ClgLoad)) in range(0, 7033): FAN.Maximum_Flow_Rate = '0.3246998409216' CoolingDX.Gross_Rated_Total_Cooling_Capacity = '7033.705684133' CoolingDX.Gross_Rated_Cooling_COP = '3.52' HeatingDX.Gross_Rated_Heating_Capacity = '7912.91889465' HeatingDX.Gross_Rated_Heating_COP = '2.93' elif abs(int(HtgLoad)) in range(0, 11722) and abs( int(ClgLoad)) in range(0, 10550): FAN.Maximum_Flow_Rate = '0.533300610816' CoolingDX.Gross_Rated_Total_Cooling_Capacity = '10550.5585262' CoolingDX.Gross_Rated_Cooling_COP = '3.55' HeatingDX.Gross_Rated_Heating_Capacity = '11722.8428068889' HeatingDX.Gross_Rated_Heating_COP = '2.7' else: print("HVAC Sizes could not meet demand") print(abs(HtgLoad), abs(ClgLoad)) return idfHVAC
def createrectshading(idd_filename, idf_filename, base_surface, shading_str, start_point, end_point, depths): ########################################## # loading base surface data from idf file ########################################## # check if IDD has been already set try: IDF.setiddname(idd_filename) except modeleditor.IDDAlreadySetError as e: pass # load idf file into idf collection idf_collection = IDF(idf_filename) # find the named base_surface in idf collection walls = idf_collection.idfobjects['BuildingSurface:Detailed'.upper()] for wall in walls: if wall.Name == base_surface: # named base_surface is now contained in wall break else: # named base_surface was not found in idf file print('epnurbs.createrectshading: unable to find the base surface', base_surface, 'in', idf_filename) return ############################################################################## # feet of perpendiculars from the start and the end point to the base surface ############################################################################## # getting the found base_surface's coordinates coord = wall.coords ulc = coord[0] blc = coord[1] brc = coord[2] # find the base_surface's plane normal and normalize it N = crossproduct( (blc[0]-ulc[0], blc[1]-ulc[1], blc[2]-ulc[2]), \ (brc[0]-ulc[0], brc[1]-ulc[1], brc[2]-ulc[2]) ) N = normalize(N) # calculate feet of perpendiculars start_foot = foot(start_point, ulc, N) end_foot = foot(end_point, ulc, N) # steps between the start and the end point num = len(depths) step = [(end_foot[0] - start_foot[0]) / num, (end_foot[1] - start_foot[1]) / num, (end_foot[2] - start_foot[2]) / num] ######################################################################## # create string of idf definitions for a sequence of shading rectangles ######################################################################## idf_total_shading_def = "" for i in range(num): if depths[i] > 0.01: # two base_surface vertices are start_foot + i*step and start_foot + (i+1)*step # the other two vertices are at depth[i] distance from the base surface v1 = [ start_foot[0] + i * step[0], start_foot[1] + i * step[1], start_foot[2] + i * step[2] ] v2 = [ v1[0] + depths[i] * N[0], v1[1] + depths[i] * N[1], v1[2] + depths[i] * N[2] ] v4 = [ start_foot[0] + (i + 1) * step[0], start_foot[1] + (i + 1) * step[1], start_foot[2] + (i + 1) * step[2] ] v3 = [ v4[0] + depths[i] * N[0], v4[1] + depths[i] * N[1], v4[2] + depths[i] * N[2] ] vertices_str = "{:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}".format( v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], v3[0], v3[1], v3[2], v4[0], v4[1], v4[2]) countervertices_str = "{:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}".format( v4[0], v4[1], v4[2], v3[0], v3[1], v3[2], v2[0], v2[1], v2[2], v1[0], v1[1], v1[2]) single_shading_def = shading_str.replace('<IDX>', str(i)).replace( '<BASESURFACE>', base_surface).replace('<VERTICES>', vertices_str).replace( '<COUNTERVERTICES>', countervertices_str) idf_total_shading_def = idf_total_shading_def + single_shading_def else: # we do not have a shading rectangle if it is not deep enough pass # create idf shading objects from the string containing shading definitions from io import StringIO idf_shading = IDF(StringIO(idf_total_shading_def)) # copy idf shading objects to the existing idf file shadings = idf_shading.idfobjects["SHADING:ZONE:DETAILED"] for shading in shadings: idf_collection.copyidfobject(shading) ############################### # at the end, save the changes ############################### idf_collection.save()
def create_combined_idf_archetype(save_folder_path, idflist=list): """ Combines all materials, constructions stored in different idfs and merges them into a single idf file Run Order: 1)Gathers all material and construction objects into lists stored in idfs 2)Selects the base IDF and deletes all construction and material objects 3)Creates new materials and constructions from the lists created in 1. 4)Redefines output variables in a way that BuildME accepts 5)Saves a new idf file, ready to be used with BuildME :param save_folder_path: save folder, where the new merged IDF will be saved :param idflist: idf list containing the idf files to be merged this list should only contain an archetype type at a time (e.g. only SFH) :return: a new merged archetype idf for BuildME """ cons_list = [] mat_list = [] matnomass_list = [] matwin_list = [] ffloor_list = [] cwall_list = [] merged_idf = "" # Gets required objects in all idf files and puts in a list for idf in idflist: fhandle = StringIO(idf) idf = IDF(fhandle) for cons in idf.idfobjects["Construction".upper()]: cons_list.append(cons) for mat in idf.idfobjects["Material".upper()]: mat_list.append(mat) for matnomass in idf.idfobjects["Material:NoMass".upper()]: matnomass_list.append(matnomass) for matwin in idf.idfobjects[ "WindowMaterial:SimpleGlazingSystem".upper()]: matwin_list.append(matwin) for ffloor in idf.idfobjects[ "Construction:FfactorGroundFloor".upper()]: ffloor_list.append(ffloor) for cwall in idf.idfobjects[ "Construction:CfactorUndergroundWall".upper()]: cwall_list.append(cwall) # Removes duplicate elements cons_list = reduce(lambda l, x: l.append(x) or l if x not in l else l, cons_list, []) mat_list = reduce(lambda l, x: l.append(x) or l if x not in l else l, mat_list, []) matnomass_list = reduce(lambda l, x: l.append(x) or l if x not in l else l, matnomass_list, []) matwin_list = reduce(lambda l, x: l.append(x) or l if x not in l else l, matwin_list, []) ffloor_list = reduce(lambda l, x: l.append(x) or l if x not in l else l, ffloor_list, []) cwall_list = reduce(lambda l, x: l.append(x) or l if x not in l else l, cwall_list, []) newobjects = cons_list + matnomass_list + matwin_list + ffloor_list + mat_list + cwall_list reduced_newobjects = reduce( lambda l, x: l.append(x) or l if x not in l else l, newobjects, []) # Selects the base IDF, deletes all of the objects and rewrites new objects from other IDFs for idf in idflist: fhandle = StringIO(idf) idf = IDF(fhandle) print( f'Now, {idf.idfobjects["Building".upper()][0].Name} values are being written to the merged IDF...' ) if "BASE" in idf.idfobjects["Building".upper()][0].Name: idf.removeallidfobjects("CONSTRUCTION") idf.removeallidfobjects("MATERIAL") idf.removeallidfobjects("MATERIAL:NOMASS") idf.removeallidfobjects("WINDOWMATERIAL:SIMPLEGLAZINGSYSTEM") idf.removeallidfobjects("CONSTRUCTION:FFACTORGROUNDFLOOR") idf.removeallidfobjects("CONSTRUCTION:CFACTORUNDERGROUNDWALL") # Output Variables needs to be redefined for BuildME idf.removeallidfobjects("OUTPUT:VARIABLEDICTIONARY") idf.removeallidfobjects("OUTPUT:SURFACES:DRAWING") idf.removeallidfobjects("OUTPUT:CONSTRUCTIONS") idf.removeallidfobjects("OUTPUT:TABLE:SUMMARYREPORTS") idf.removeallidfobjects("OUTPUT:TABLE:MONTHLY") idf.removeallidfobjects("OUTPUTCONTROL:TABLE:STYLE") idf.removeallidfobjects("OUTPUT:VARIABLE") idf.removeallidfobjects("OUTPUT:METER") print("Output Variables are being changed...") # New output variables are defined by using existing SFH archetype located in data/archetype folder SFH_IDF = IDF("..//data//archetype//USA//SFH.idf") outputlist = [] objlist = [ "OUTPUT:METER", "OUTPUT:VARIABLEDICTIONARY", "OUTPUT:SURFACES:DRAWING", "OUTPUT:CONSTRUCTIONS", "OUTPUT:TABLE:SUMMARYREPORTS", "OUTPUT:TABLE:MONTHLY", "OUTPUTCONTROL:TABLE:STYLE", "OUTPUT:VARIABLE" ] for obj in objlist: if obj in [x for x in SFH_IDF.idfobjects]: for item in SFH_IDF.idfobjects[f"{obj}"]: outputlist.append(item) for newobj in outputlist: idf.copyidfobject(newobj) # Checks if there are still duplicates seen = set() dupes = [] for x in reduced_newobjects: if x.Name in seen: dupes.append(x.Name) else: seen.add(x.Name) idf.copyidfobject(x) # Renames the merged IDF building = idf.idfobjects["Building".upper()][0].Name building = building.split("-")[0] idf.idfobjects["Building".upper()][0].Name = building building = building.split("BASE")[1] idf.saveas(f"{save_folder_path}/{building}-BuildME.idf") merged_idf = idf print("The new merged idf file is successfully created") return merged_idf
def generate_idf3(save_path, idf_data, idf_surfaces, idf_fenestration, wall_type, window_type, save_as): mater = 'Material' # All glazi = 'WindowMaterial:SimpleGlazingSystem' # All const = 'Construction' # All surfa = 'BuildingSurface:Detailed' # For geometry fenes = 'FenestrationSurface:Detailed' # For geometry gen00 = 'Version' # All gen01 = 'SimulationControl' # All gen02 = 'RunPeriod' # All gen03 = 'Building' # All gen04 = 'Timestep' # All gen05 = 'SizingPeriod:WeatherFileDays' # All gen06 = 'RunPeriodControl:DaylightSavingTime' # All gen07 = 'Site:GroundTemperature:BuildingSurface' # All gen08 = 'GlobalGeometryRules' # All zon01 = 'ZoneVentilation:DesignFlowRate' # For geometry zon02 = 'ZoneInfiltration:DesignFlowRate' # For geometry zon03 = 'ZoneList' # For geometry zon04 = 'Zone' # For geometry lds01 = 'People' # For geometry lds02 = 'Lights' # For geometry lds03 = 'ElectricEquipment' # For geometry sch01 = 'Schedule:Compact' sch02 = 'ScheduleTypeLimits' sch03 = 'Schedule:Day:Interval' sch04 = 'Schedule:Week:Daily' sch05 = 'Schedule:Year' sch06 = 'Schedule:Constant' hvc01 = 'HVACTemplate:Thermostat' # All hvc02 = 'HVACTemplate:Zone:VAV' # For geometry hvc03 = 'HVACTemplate:System:PackagedVAV' # All out01 = 'Output:Surfaces:Drawing' out02 = 'OutputControl:Table:Style' out03 = 'Output:Table:SummaryReports' del_01 = 'LifeCycleCost:Parameters' del_02 = 'LifeCycleCost:UsePriceEscalation' del_03 = 'ZoneControl:Thermostat' del_04 = 'ThermostatSetpoint:DualSetpoint' del_05 = 'HVACTemplate:Zone:IdealLoadsAirSystem' del_06 = 'DesignSpecification:OutdoorAir' del_07 = 'Sizing:Parameters' del_08 = 'Material:AirGap' del_09 = 'WindowMaterial:Blind' del_10 = 'WindowMaterial:Glazing' del_11 = 'WindowProperty:FrameAndDivider' del_12 = 'WindowProperty:ShadingControl' del_13 = 'Site:Location' del_14 = 'Output:VariableDictionary' del_15 = 'Output:SQLite' del_16 = 'LifeCycleCost:NonrecurringCost' # Assignments gen00 = idf_data.idfobjects[gen00.upper()][0] gen01 = idf_data.idfobjects[gen01.upper()][0] gen02 = idf_data.idfobjects[gen02.upper()][0] gen03 = idf_data.idfobjects[gen03.upper()][0] gen04 = idf_data.idfobjects[gen04.upper()][0] gen05 = idf_data.idfobjects[gen05.upper()][0] gen06 = idf_data.idfobjects[gen06.upper()][0] gen07 = idf_data.idfobjects[gen07.upper()][0] gen08 = idf_data.idfobjects[gen08.upper()][0] zon01 = idf_surfaces.idfobjects[zon01.upper()][0] zon02 = idf_surfaces.idfobjects[zon02.upper()][0] zon03 = idf_surfaces.idfobjects[zon03.upper()][0] zon04 = idf_surfaces.idfobjects[zon04.upper()] lds01 = idf_surfaces.idfobjects[lds01.upper()][0] lds02 = idf_surfaces.idfobjects[lds02.upper()][0] lds03 = idf_surfaces.idfobjects[lds03.upper()][0] sch01 = idf_data.idfobjects[sch01.upper()] sch02 = idf_data.idfobjects[sch02.upper()] sch03 = idf_data.idfobjects[sch03.upper()] sch04 = idf_data.idfobjects[sch04.upper()] sch05 = idf_data.idfobjects[sch05.upper()] sch06 = idf_data.idfobjects[sch06.upper()] hvc01 = idf_data.idfobjects[hvc01.upper()][0] hvc02 = idf_surfaces.idfobjects[hvc02.upper()] hvc03 = idf_data.idfobjects[hvc03.upper()][0] out01 = idf_data.idfobjects[out01.upper()][0] out02 = idf_data.idfobjects[out02.upper()][0] out03 = idf_data.idfobjects[out03.upper()][0] # MATERIALS mater = idf_data.idfobjects[mater.upper()] glazi = idf_data.idfobjects[glazi.upper()] # CONSTRUCTION const = idf_data.idfobjects[const.upper()] # GEOMETRY surface_objects = idf_surfaces.idfobjects[surfa.upper()] fenestration_objects = idf_fenestration.idfobjects[fenes.upper()] # CREATE NEW IDF FILE # with common info and specific geometry idf2 = IDF() idf2.new() # copy over relevant info from source idfs # GENERAL idf2.copyidfobject(gen00) idf2.copyidfobject(gen01) idf2.copyidfobject(gen02) idf2.copyidfobject(gen03) idf2.copyidfobject(gen04) idf2.copyidfobject(gen05) idf2.copyidfobject(gen06) idf2.copyidfobject(gen07) idf2.copyidfobject(gen08) # ZONE idf2.copyidfobject(zon01) idf2.copyidfobject(zon02) idf2.copyidfobject(zon03) # idf2.copyidfobject(zon04) # LOADS idf2.copyidfobject(lds01) idf2.copyidfobject(lds02) idf2.copyidfobject(lds03) # HVAC idf2.copyidfobject(hvc01) # idf2.copyidfobject(hvc02) idf2.copyidfobject(hvc03) # OUTPUT idf2.copyidfobject(out01) idf2.copyidfobject(out02) idf2.copyidfobject(out03) # SCHEDULES for i in sch01: idf2.copyidfobject(i) for i in sch02: idf2.copyidfobject(i) for i in sch03: idf2.copyidfobject(i) for i in sch04: idf2.copyidfobject(i) for i in sch05: idf2.copyidfobject(i) for i in sch06: idf2.copyidfobject(i) for i in zon04: idf2.copyidfobject(i) for i in hvc02: idf2.copyidfobject(i) # MATERIALS for i in mater: idf2.copyidfobject(i) # GLAZING for i in glazi: idf2.copyidfobject(i) # CONSTRUCTIONS for i in const: idf2.copyidfobject(i) # Basic setup done # Now I need geometry from a specific file. # GEOMETRY for i in surface_objects: idf2.copyidfobject(i) for i in fenestration_objects: idf2.copyidfobject(i) new_surface = idf2.idfobjects[surfa.upper()] new_subsurface = idf2.idfobjects[fenes.upper()] # Now I want to change the construction of each surface and glazing. floor_type = 'Slab A' ceiling_type = 'Slab A' roof_type = 'Roof A' for surface in new_surface: if surface['Surface_Type'] == 'Floor': surface.Construction_Name = floor_type for surface in new_surface: if surface['Surface_Type'] == 'Ceiling': surface.Construction_Name = ceiling_type for surface in new_surface: if surface['Surface_Type'] == 'Roof': surface.Construction_Name = roof_type for surface in new_surface: if surface['Surface_Type'] == 'Wall': surface.Construction_Name = wall_type for subsurface in new_subsurface: if subsurface['Surface_Type'] == 'Window': subsurface.Construction_Name = window_type # Save it to the disk. idf2.saveas('{}/{}.idf'.format(save_path, save_as)) idf_file = '{}/{}.idf'.format(save_path, save_as) return idf_file
def createnurbsopening(idd_filename, idf_filename, base_surface, opening_str, ctrl_points, evaluated_points=50): ########################################## # loading base surface data from idf file ########################################## # check if IDD has been already set try: IDF.setiddname(idd_filename) except modeleditor.IDDAlreadySetError as e: pass # load idf file into idf collection idf_collection = IDF(idf_filename) # find the named base_surface in idf collection walls = idf_collection.idfobjects['BuildingSurface:Detailed'.upper()] for wall in walls: if wall.Name == base_surface: # named base_surface is now contained in wall break else: # named base_surface was not found in idf file print('epnurbs.createopening: unable to find the base surface', base_surface, 'in', idf_filename) return ################################# # calculating NURBS curve points ################################# from geomdl import NURBS from geomdl import utilities # one has to exclude checks for leading zeros and trailing ones in geomdl.utilities.check_knot_vector # in order for this knotvector to be allowed! utilities.check_knot_vector = new_check_knot_vector curve = NURBS.Curve() # 1/delta corresponds to the number of evaluated points of a NURBS curve that defines the opening curve.delta = 1/evaluated_points curve.degree = 3 # add weight 1 to each control point for cpt in ctrl_points: if len(cpt)<4: cpt.append(1.0) # add copy of the first degree control points in order to close NURBS curve extra_points = ctrl_points[:curve.degree] ctrl_points.extend(extra_points) # sets curve control points curve.set_ctrlpts(ctrl_points) # sets knot vector for closed NURBS curve # note that the length of ctrl_points has increased for curve.degree due to extend operation knotstep = 1/(len(ctrl_points) + curve.degree) curve.knotvector = [i*knotstep for i in range(len(ctrl_points)+curve.degree+1)] # note that for the closed NURBS curve # we have to use only the domain [knotvector[curve.degree], knotvector[len(ctrl_points)]]!!! # evaluates curve points curve.evaluate(curve.degree*knotstep, len(ctrl_points)*knotstep) # make a local copy of evaluated curve points crv_points = curve.curvepts ######################################################################### # feet of perpendiculars from the NURBS curve points to the base surface, # just to make sure we are not getting out of its plane ######################################################################### # getting the found base_surface's coordinates coord = wall.coords ulc = coord[0] blc = coord[1] brc = coord[2] # find the base_surface's plane normal and normalize it u = [ blc[0]-ulc[0], blc[1]-ulc[1], blc[2]-ulc[2] ] v = [ brc[0]-blc[0], brc[1]-blc[1], brc[2]-blc[2] ] N = crossproduct(u,v) N = normalize(N) # calculate feet of perpendiculars feet_points = [foot(crv_points[i], ulc, N) for i in range(len(crv_points))] ####################################################################################### # create a list of 0.1m squares that partition the whole wall surface # !!! it is assumed that the wall surface is a rectangle # !!! three of whose vertices are ulc, blc and brc (and the fourth one is ulc+brc-blc) ####################################################################################### # a new orthonormal system for the wall surface u = normalize(u) v = crossproduct(N,u) v = normalize(v) # transform feet_point coordinates to the uv system # since N, u, v is an orthonormal system we have that # for each vector X = <X,N>N + <X,u>u + <X,v>v # this will be applied to vectors feet_points-ulc which belong to the wall surface plane feet_points_uv = [ [dotproduct(subtract(fp, ulc), u), dotproduct(subtract(fp, ulc), v)] for fp in feet_points] # form a list of squares and a list of their centers in uv coordinates squaresize = 0.1 maxi = int(length(subtract(blc, ulc))/squaresize)-1 maxj = int(length(subtract(brc, blc))/squaresize)-1 squarelist = [ [ [ ((i+0.5)*squaresize, (j+0.5)*squaresize), ((i+1.5)*squaresize, (j+0.5)*squaresize), ((i+1.5)*squaresize, (j+1.5)*squaresize), ((i+0.5)*squaresize, (j+1.5)*squaresize) ] for j in range(maxj) ] for i in range(maxi)] squarecenters = [ ( (i+1)*squaresize, (j+1)*squaresize ) for i in range(maxi) for j in range(maxj) ] # for each square check whether its center belongs to the polygon defined by NURBS curve points import matplotlib.path as mplPath path = mplPath.Path(feet_points_uv) inside = path.contains_points(squarecenters) # select only those squares whose centers are inside the NURBS curve insideindices = [ [ j for j in range(maxj) if inside[i*maxj+j] ] for i in range(maxi) ] # now insideindices[i] contains all j such that squarelist[i][j] is inside a NURBS curve ################################################################################# # create string of idf definitions for rectangles that approximate NURBS opening ################################################################################# idf_total_opening_def = "" for i in range(maxi): # go through all inside squares within i-th column, if there are any if len(insideindices[i])>0: # pn and kn contain the beginning and the end of # subsequences of consecutive numbers within the insideindices[i] list pn = insideindices[i][0] kn = insideindices[i][0] for k in range(1, len(insideindices[i])+1): if k<len(insideindices[i]) and insideindices[i][k]==insideindices[i][k-1]+1: # consecutive subsequence goes on kn = insideindices[i][k] else: # consecutive subsequence had ended, so create an opening element # squarelist[i][pn][0],[1] contain its first two vertices in uv system, # squarelist[i][kn][2],[3] contain its last two vertices in uv system # recalculate vertices in xyz system first vertex1 = [ ulc[0] + squarelist[i][pn][0][0] * u[0] + squarelist[i][pn][0][1] * v[0], ulc[1] + squarelist[i][pn][0][0] * u[1] + squarelist[i][pn][0][1] * v[1], ulc[2] + squarelist[i][pn][0][0] * u[2] + squarelist[i][pn][0][1] * v[2] ] vertex2 = [ ulc[0] + squarelist[i][pn][1][0] * u[0] + squarelist[i][pn][1][1] * v[0], ulc[1] + squarelist[i][pn][1][0] * u[1] + squarelist[i][pn][1][1] * v[1], ulc[2] + squarelist[i][pn][1][0] * u[2] + squarelist[i][pn][1][1] * v[2] ] vertex3 = [ ulc[0] + squarelist[i][kn][2][0] * u[0] + squarelist[i][kn][2][1] * v[0], ulc[1] + squarelist[i][kn][2][0] * u[1] + squarelist[i][kn][2][1] * v[1], ulc[2] + squarelist[i][kn][2][0] * u[2] + squarelist[i][kn][2][1] * v[2] ] vertex4 = [ ulc[0] + squarelist[i][kn][3][0] * u[0] + squarelist[i][kn][3][1] * v[0], ulc[1] + squarelist[i][kn][3][0] * u[1] + squarelist[i][kn][3][1] * v[1], ulc[2] + squarelist[i][kn][3][0] * u[2] + squarelist[i][kn][3][1] * v[2] ] vert_str = "{:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}".format( vertex1[0], vertex1[1], vertex1[2], vertex2[0], vertex2[1], vertex2[2], vertex3[0], vertex3[1], vertex3[2], vertex4[0], vertex4[1], vertex4[2]) countervert_str = "{:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}, {:f}".format( vertex1[0], vertex1[1], vertex1[2], vertex4[0], vertex4[1], vertex4[2], vertex3[0], vertex3[1], vertex3[2], vertex2[0], vertex2[1], vertex2[2]) # fill out the entries in the opening string definition single_opening_def = opening_str.replace('<IDX>', str(i)+'_'+str(pn)).replace('<BASESURFACE>', base_surface).replace('<VERTICES>', vert_str).replace('<COUNTERVERTICES>', countervert_str) idf_total_opening_def = idf_total_opening_def + single_opening_def # a new consecutive subsequence has just begun provided that k<len(insideindices[i]) if k<len(insideindices[i]): pn = insideindices[i][k] kn = insideindices[i][k] # create idf opening objects from the string containing opening definitions from io import StringIO idf_opening = IDF(StringIO(idf_total_opening_def)) # copy idf shading objects to the existing idf file openings = idf_opening.idfobjects["FENESTRATIONSURFACE:DETAILED"] for opening in openings: idf_collection.copyidfobject(opening) ############################### # at the end, save the changes ############################### idf_collection.save()