From 4e233c87ee39fda7f2e09f595e8144a7186c7b96 Mon Sep 17 00:00:00 2001 From: michbeg Date: Thu, 22 Jun 2017 19:12:36 -0400 Subject: [PATCH 01/13] Add methods to find adjacent buildings and determine wall boundary conditions --- bpeng/simulation/base.py | 120 ++++++++++++---------------- bpeng/simulation/geometry.py | 146 ++++++++++++++++++++++++++++++++++- 2 files changed, 194 insertions(+), 72 deletions(-) diff --git a/bpeng/simulation/base.py b/bpeng/simulation/base.py index 206c190..8964816 100644 --- a/bpeng/simulation/base.py +++ b/bpeng/simulation/base.py @@ -18,8 +18,8 @@ from .building_hvac_oplus import (add_designspec_airdist, add_sch_compact, add_s add_sizing_zone, add_thermostat_dual_setpoints, add_zone_equipmentconnection, add_zone_equipmentlist, add_zone_ideal_airloads, add_zone_thermostat) from .design_days import add_dday -from .geometry import (ceiling_vertex, coord_to_meters, fix_degenerate_surface, fix_dup_id, floor_vertex, - offset_center_ratio, tuplelist_to_df, walls_vertex) +from .geometry import (ceiling_vertex, coord_to_meters, fix_dup_id, floor_vertex, + offset_center_ratio, walls_vertex, extract_geom, find_adjacent_bldg) # TODO: put NYCShpVal class in a new file @@ -613,36 +613,19 @@ class BaseSim: # Load shapefile data build_shp = self.building_shp + adjacent_building = False - # Meter Conversion - long_m, lat_m = coord_to_meters() - - # TODO: Create function for geometry extraction for building shapefile - # Extract geometry Points - geom_div_col = tuplelist_to_df(df=build_shp, geom_colname=geometry_colname, bin_colname=buildingid_colname, - conversion_long=long_m, conversion_lat=lat_m) - - # Remove points that will create denegerate surfaces - geom_div_col = fix_degenerate_surface(geom_div_col) - - # Convert heighroof to meters - build_shp[build_height_colname] *= 0.3048 - - # Database for walls, roof and ceilings - # Set origin to 0 - x0, y0 = geom_div_col.iloc[0, 0] - - geom_origin = geom_div_col.copy() - for i in range(len(geom_origin.index)): - for j in range(len(geom_origin.columns)): - if geom_origin.iloc[i, j] is not None: - lst = list(geom_origin.iloc[i, j]) - lst[0] -= x0 - lst[1] -= y0 - geom_origin.iloc[i, j] = tuple(lst) + # Check for adjacent buildings if surrounding building shapefiles were loaded + if self.building_shading_shp is not None: + # Load shading building shapefile + shade_shp = self.building_shading_shp + # Find adjacent buildings + build_shp = find_adjacent_bldg(build_shp, shade_shp, geom_colname=geometry_colname) + adjacent_building = True - build_shp_ep = build_shp.merge(geom_origin, left_on=buildingid_colname, right_index=True) - build_shp_ep.index = list(build_shp_ep[buildingid_colname]) + # Extract Geometry + build_shp_ep = extract_geom(build_shp, geom_colname=geometry_colname, bin_colname=buildingid_colname, + build_height_colname=build_height_colname) # Create list of (x,y,z) coordinates for each floor height for floors, roofs, and walls. # These coordinates will be processed when adding the wall floors and roofs to the IDF file. @@ -653,11 +636,16 @@ class BaseSim: num_floor = [] index = [] year = [] - for bin_ in geom_div_col.index: - input_ = geom_origin.ix[bin_] - h = build_shp_ep.ix[bin_][build_height_colname] - num_floor_tmp = build_shp_ep.ix[bin_][num_floors_colname] - year_tmp = build_shp_ep.ix[bin_][cnstrct_yr_colname] + obc = [] + sunexp = [] + windexp = [] + window = [] + for bin_ in build_shp_ep.index: + col = list(range(len(build_shp_ep.loc[bin_, geometry_colname].exterior.coords))) + input_ = build_shp_ep.loc[bin_, col] + h = build_shp_ep.loc[bin_, build_height_colname] + num_floor_tmp = build_shp_ep.loc[bin_, num_floors_colname] + year_tmp = build_shp_ep.loc[bin_, cnstrct_yr_colname] h_l = list(np.linspace(0, h, num=(num_floor_tmp + 1))) for i in range(len(h_l) - 1): index.append(str(bin_) + " floor" + str(i + 1)) @@ -668,6 +656,10 @@ class BaseSim: ceiling.append(ceiling_vertex(input_, hh)) floor.append(floor_vertex(input_, hl)) year.append(int(year_tmp)) + obc.append(build_shp_ep.loc[bin_, 'wall_bound_cond'][i]) + sunexp.append(build_shp_ep.loc[bin_, 'wall_sun_exposed'][i]) + windexp.append(build_shp_ep.loc[bin_, 'wall_wind_exposed'][i]) + window.append(build_shp_ep.loc[bin_, 'wall_windows'][i]) input_ep_all = pd.DataFrame() input_ep_all["walls"] = walls @@ -675,6 +667,10 @@ class BaseSim: input_ep_all["roof"] = ceiling input_ep_all["num_floor"] = num_floor input_ep_all["year"] = year + input_ep_all['wall_bound_cond'] = obc + input_ep_all['wall_sun_exposed'] = sunexp + input_ep_all['wall_wind_exposed'] = windexp + input_ep_all['wall_windows'] = window input_ep_all.index = index ####################################################################### @@ -687,28 +683,8 @@ class BaseSim: shade_shp = self.building_shading_shp # Extract geometry Points - shade_geom_div_col = tuplelist_to_df(df=shade_shp, geom_colname=geometry_colname, - bin_colname=buildingid_colname, conversion_long=long_m, - conversion_lat=lat_m) - - # Convert heightroof to meters - shade_shp[build_height_colname] *= 0.3048 - - # Remove points that will create denegerate surfaces - shade_geom_div_col = fix_degenerate_surface(shade_geom_div_col) - - # Database for walls, roof and ceilings - shade_origin = shade_geom_div_col.copy() - for i in range(len(shade_origin.index)): - for j in range(len(shade_origin.columns)): - if shade_origin.iloc[i, j] is not None: - lst = list(shade_origin.iloc[i, j]) - lst[0] -= x0 - lst[1] -= y0 - shade_origin.iloc[i, j] = tuple(lst) - - shade_shp_ep = shade_shp.merge(shade_origin, left_on=buildingid_colname, right_index=True) - shade_shp_ep.index = list(shade_shp_ep[buildingid_colname]) + shade_shp_ep = extract_geom(shade_shp, geom_colname=geometry_colname, bin_colname=buildingid_colname, + build_height_colname=build_height_colname) # Create list of (x,y,z) coordinates for floors, roofs, and walls of shading buildings. # These coordinates will be processed when adding the Shading:Building:Detailed to the IDF file. @@ -717,9 +693,10 @@ class BaseSim: floor = [] ceiling = [] index = [] - for bin_ in shade_geom_div_col.index: - input_ = shade_origin.ix[bin_] - h = shade_shp_ep.ix[bin_][build_height_colname] + for bin_ in shade_shp_ep.index: + col = list(range(len(shade_shp_ep.loc[bin_, geometry_colname].exterior.coords))) + input_ = shade_shp_ep.loc[bin_, col] + h = shade_shp_ep.loc[bin_, build_height_colname] h_l = list(np.linspace(0, h, num=2)) index.append(str(bin_)) hl = h_l[0] # lower height, or floor height, for a given floor @@ -785,18 +762,23 @@ class BaseSim: zone_name = "Zone " + str(bin_) zone = self.zone_add(zone_name=zone_name) zone_list.append(zone) - w_l = input_ep.ix[bin_]["walls"] - f = input_ep.ix[bin_]["floor"] - r = input_ep.ix[bin_]["roof"] + w_l = input_ep.loc[bin_, "walls"] + w_bc = input_ep.loc[bin_, "wall_bound_cond"] + w_se = input_ep.loc[bin_, "wall_sun_exposed"] + w_we = input_ep.loc[bin_, "wall_wind_exposed"] + w_w = input_ep.loc[bin_, "wall_windows"] + f = input_ep.loc[bin_, "floor"] + r = input_ep.loc[bin_, "roof"] # Walls and Window i = 1 - for w in w_l: - self.bsd_add(base_wall_name + " " + str(i), cons_wall, zone, obc="Outdoors", obc_obj=None, - sun_exp="SunExposed", w_exp="WindExposed", nb_v=4, coords=w, surface_type="Wall") - lst_bsd = list(self.idf("buildingsurface:detailed").filter("name", base_wall_name + " " + str(i))) - self.extwindow_wall_ratio_add(cons_window, surface_type="Window", - window_to_wall_ratio=window_to_wall_ratio, list_bsd=lst_bsd) + for w, bc, se, we, ew in zip(w_l, w_bc, w_se, w_we, w_w): + self.bsd_add(base_wall_name + " " + str(i), cons_wall, zone, obc=bc, obc_obj=None, + sun_exp=se, w_exp=we, nb_v=4, coords=w, surface_type="Wall") + if ew is True: + lst_bsd = list(self.idf("buildingsurface:detailed").filter("name", base_wall_name + " " + str(i))) + self.extwindow_wall_ratio_add(cons_window, surface_type="Window", + window_to_wall_ratio=window_to_wall_ratio, list_bsd=lst_bsd) i += 1 # Ground Floor and Roof underneath diff --git a/bpeng/simulation/geometry.py b/bpeng/simulation/geometry.py index f2c7659..b732a60 100644 --- a/bpeng/simulation/geometry.py +++ b/bpeng/simulation/geometry.py @@ -10,7 +10,7 @@ import math import numpy as np import pandas as pd -from shapely.geometry import Point, Polygon +from shapely.geometry import Point, Polygon, LineString, MultiLineString from geopy.distance import vincenty @@ -227,7 +227,7 @@ def poly_to_tuplelist(geometry, conversion_long, conversion_lat): return [(coords[0][i]*conversion_long, coords[1][i]*conversion_lat) for i in range(len(coords[0]))] -def tuplelist_to_df(df, geom_colname, bin_colname, conversion_long, conversion_lat): +def tuplelist_to_df(df, geom_colname, bin_colname, conversion_long=1, conversion_lat=1): """ Extract all points from multiple shapely Polygon to pandas DataFrame. """ @@ -237,7 +237,7 @@ def tuplelist_to_df(df, geom_colname, bin_colname, conversion_long, conversion_l return pd.DataFrame(temp_lt, index=list(df[bin_colname].astype(float))) -def tuple_convert_float(x, conversion_long, conversion_lat): +def tuple_convert_float(x, conversion_long=1, conversion_lat=1): """ Convert tuple content to float Convert degree coordinates to meters @@ -295,3 +295,143 @@ def string_to_polygon(polygon_string): tup.append(string_to_pointlist(i)) poly = Polygon([[p.x, p.y] for p in tup]) return poly + + +def poly_inters(geometry1, geometry2): + """ + Look for intersection geometry between two different geometries + Only returns intersection geometry if is shapely LineString or MultiLineString + + Args: + + geometry1 (shapely.Polygon) + geometry2 (shapely.Polygon) + + Returns: + + shapely.LineString or shapely.MultiLineString: line or multi-line geometry + """ + inters = geometry1.intersection(geometry2) + if isinstance(inters, (LineString, MultiLineString)): + return inters + + +def find_adjacent_bldg(build_shp, bldg_surround, geom_colname): + """ + Find building surfaces that have adjacent buildings + """ + + build_shp['adjbldg_surface'] = "" + build_shp['adjbldg_index'] = "" + + for i in build_shp.index: + sfn = [] # surface index numbers that have adjacent buildings + abi = [] # surrounding building index number of building that is adjacent to main building + L = [{'index': ix, 'geom': poly_inters(build_shp.loc[i, geom_colname], x)} for ix, x in + zip(bldg_surround.index, bldg_surround[geom_colname]) if + poly_inters(build_shp.loc[i, geom_colname], x) is not None] + for com_geom in L: + if isinstance(com_geom['geom'], MultiLineString): + for com_geom2 in com_geom['geom']: + for n, a in enumerate(build_shp.loc[i, geom_colname].exterior.coords): + if n < len(build_shp.loc[i, geom_colname].exterior.coords)-1: + line = LineString([a, build_shp.loc[i, geom_colname].exterior.coords[n+1]]) + if line == com_geom2: + sfn.append(n) + abi.append(com_geom['index']) + else: + for n, a in enumerate(build_shp.loc[i, geom_colname].exterior.coords): + if n < len(build_shp.loc[i, geom_colname].exterior.coords)-1: + line = LineString([a, build_shp.loc[i, geom_colname].exterior.coords[n+1]]) + if line == com_geom['geom']: + sfn.append(n) + abi.append(com_geom['index']) + build_shp['adjbldg_surface'][i] = sfn + build_shp['adjbldg_index'][i] = abi + + return build_shp + +def extract_geom(build_shp, geom_colname, bin_colname, build_height_colname): + """ + Extract geometry points from shapefile + """ + # Meter Conversion + long_m, lat_m = coord_to_meters() + + # Extract geometry Points + geom_div_col = tuplelist_to_df(df=build_shp, geom_colname=geom_colname, bin_colname=bin_colname, + conversion_long=long_m, conversion_lat=lat_m) + + # Remove points that will create denegerate surfaces + geom_div_col = fix_degenerate_surface(geom_div_col) + + # Convert heighroof to meters + build_shp[build_height_colname] *= 0.3048 + + # Database for walls, roof and ceilings + # Set origin to 0 + x0, y0 = geom_div_col.iloc[0, 0] + + geom_origin = geom_div_col.copy() + for i in range(len(geom_origin.index)): + for j in range(len(geom_origin.columns)): + if geom_origin.iloc[i, j] is not None: + lst = list(geom_origin.iloc[i, j]) + lst[0] -= x0 + lst[1] -= y0 + geom_origin.iloc[i, j] = tuple(lst) + + build_shp_ep = build_shp.merge(geom_origin, left_on=bin_colname, right_index=True) + build_shp_ep.index = list(build_shp_ep[bin_colname]) + + return build_shp_ep + + +def boundary_condition(build_shp, bldg_surround, geom_colname, build_height_colname, num_floors_colname, + surface_number_colname='adjbldg_surface', adjacent_bldg_index_colname='adjbldg_index'): + """ + Determine external wall boundary conditions, sun exposure status, wind exposure status and window status + + Args: + + build_shp (pandas.DataFrame) + building shapefile dataframe, index is building bin, must contain surface number + and adjacent building index columns (see geometry.find_adjacent_bldg) + """ + + build_shp['wall_bound_cond'] = '' + build_shp['wall_sun_exposed'] = '' + build_shp['wall_wind_exposed'] = '' + build_shp['wall_windows'] = '' + + for bin_ in build_shp.index: + build_height_per_floor = build_shp.loc[bin_, build_height_colname] / build_shp.loc[bin_, num_floors_colname] + obc_list = [] + sunexp_list = [] + windexp_list = [] + window_list = [] + num_surf = len(build_shp.loc[bin_, geom_colname].exterior.coords)-1 + for floor_num in range(1, int(build_shp.loc[bin_, num_floors_colname])+1): + obc_temp_list = ['Outdoor'] * num_surf + sunexp_temp_list = ['SunExposed'] * num_surf + windexp_temp_list = ['WindExposed'] * num_surf + window_temp_list = [True] * num_surf + for adj_ix, surf_num in zip(build_shp.loc[bin_, adjacent_bldg_index_colname], + build_shp.loc[bin_, surface_number_colname]): + adjbldg_height = bldg_surround.loc[adj_ix, build_height_colname] + height_diff = (floor_num * build_height_per_floor) - adjbldg_height + if height_diff <= (build_height_per_floor / 2): + obc_temp_list[surf_num] = 'Adiabatic' + sunexp_temp_list[surf_num] = 'NoSun' + windexp_temp_list[surf_num] = 'NoWind' + window_temp_list[surf_num] = False + obc_list.append(obc_temp_list) + sunexp_list.append(sunexp_temp_list) + windexp_list.append(windexp_temp_list) + window_list.append(window_temp_list) + build_shp['wall_bound_cond'][bin_] = obc_list + build_shp['wall_sun_exposed'][bin_] = sunexp_list + build_shp['wall_wind_exposed'][bin_] = windexp_list + build_shp['wall_windows'][bin_] = window_list + + return build_shp -- GitLab From ad9784eeaecc0b988b6c0d422c7c113d7d08e8c8 Mon Sep 17 00:00:00 2001 From: michbeg Date: Thu, 22 Jun 2017 19:23:53 -0400 Subject: [PATCH 02/13] Formatting --- bpeng/simulation/base.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/bpeng/simulation/base.py b/bpeng/simulation/base.py index 8964816..717b7c4 100644 --- a/bpeng/simulation/base.py +++ b/bpeng/simulation/base.py @@ -771,15 +771,13 @@ class BaseSim: r = input_ep.loc[bin_, "roof"] # Walls and Window - i = 1 - for w, bc, se, we, ew in zip(w_l, w_bc, w_se, w_we, w_w): - self.bsd_add(base_wall_name + " " + str(i), cons_wall, zone, obc=bc, obc_obj=None, + for i, (w, bc, se, we, ew) in enumerate(zip(w_l, w_bc, w_se, w_we, w_w)): + self.bsd_add(base_wall_name + " " + str(i+1), cons_wall, zone, obc=bc, obc_obj=None, sun_exp=se, w_exp=we, nb_v=4, coords=w, surface_type="Wall") if ew is True: - lst_bsd = list(self.idf("buildingsurface:detailed").filter("name", base_wall_name + " " + str(i))) + lst_bsd = list(self.idf("buildingsurface:detailed").filter("name", base_wall_name + " " + str(i+1))) self.extwindow_wall_ratio_add(cons_window, surface_type="Window", window_to_wall_ratio=window_to_wall_ratio, list_bsd=lst_bsd) - i += 1 # Ground Floor and Roof underneath nb_v_f = len(f) -- GitLab From 30400d3eedbda8db889daff3f4034ff0b6033bb2 Mon Sep 17 00:00:00 2001 From: michbeg Date: Mon, 26 Jun 2017 18:51:22 -0400 Subject: [PATCH 03/13] Fix boundary condition method --- bpeng/simulation/base.py | 118 ++++++++++++++++++++--------------- bpeng/simulation/geometry.py | 49 ++++++++++++--- 2 files changed, 110 insertions(+), 57 deletions(-) diff --git a/bpeng/simulation/base.py b/bpeng/simulation/base.py index 717b7c4..1e1312c 100644 --- a/bpeng/simulation/base.py +++ b/bpeng/simulation/base.py @@ -19,7 +19,7 @@ from .building_hvac_oplus import (add_designspec_airdist, add_sch_compact, add_s add_zone_equipmentlist, add_zone_ideal_airloads, add_zone_thermostat) from .design_days import add_dday from .geometry import (ceiling_vertex, coord_to_meters, fix_dup_id, floor_vertex, - offset_center_ratio, walls_vertex, extract_geom, find_adjacent_bldg) + offset_center_ratio, walls_vertex, extract_geom, find_adjacent_bldg, boundary_condition) # TODO: put NYCShpVal class in a new file @@ -93,9 +93,9 @@ class BaseSim: # file, given the coordinates, construction, zone, and boundary conditions of a surface. def __init__(self, building_shapefile, building_name=None, idf_start_file=None, building_shading_shapefile=None, - build_height_colname=NYCShpVal.HEIGHT, geometry_colname=NYCShpVal.GEOM, - buildingid_colname=NYCShpVal.BIN, num_floors_colname=NYCShpVal.NFLOOR, - cnstrct_yr_colname=NYCShpVal.CONSYR): + build_height_colname=NYCShpVal.HEIGHT.value, geometry_colname=NYCShpVal.GEOM.value, + buildingid_colname=NYCShpVal.BIN.value, num_floors_colname=NYCShpVal.NFLOOR.value, + cnstrct_yr_colname=NYCShpVal.CONSYR.value): """ Args: @@ -132,58 +132,60 @@ class BaseSim: """ # Assert if columns taht will be used exist in building shapefile - assert buildingid_colname.value in building_shapefile, "Missing column {} in building shapefile"\ + assert buildingid_colname in building_shapefile, "Missing column {} in building shapefile"\ .format(buildingid_colname) - assert cnstrct_yr_colname.value in building_shapefile, "Missing column {} in building shapefile"\ + assert cnstrct_yr_colname in building_shapefile, "Missing column {} in building shapefile"\ .format(cnstrct_yr_colname) - assert geometry_colname.value in building_shapefile, "Missing column {} in building shapefile"\ + assert geometry_colname in building_shapefile, "Missing column {} in building shapefile"\ .format(geometry_colname) - assert build_height_colname.value in building_shapefile, "Missing column {} in building shapefile"\ + assert build_height_colname in building_shapefile, "Missing column {} in building shapefile"\ .format(build_height_colname) - assert num_floors_colname.value in building_shapefile, "Missing column {} in building shapefile"\ + assert num_floors_colname in building_shapefile, "Missing column {} in building shapefile"\ .format(num_floors_colname) # Convert columns to float - col_float = [buildingid_colname.value, cnstrct_yr_colname.value, build_height_colname.value, - num_floors_colname.value] + col_float = [buildingid_colname, cnstrct_yr_colname, build_height_colname, + num_floors_colname] building_shapefile[col_float] = building_shapefile[col_float].astype(float) # Assert data type of geometry - geom_type1 = building_shapefile[geometry_colname.value].apply(type).value_counts().index[0] + geom_type1 = building_shapefile[geometry_colname].apply(type).value_counts().index[0] assert geom_type1 == Polygon, 'Geometry in building shapefile is not ' \ 'shapely.geometry.Polygon type.' - # Assert if height inputs is more than 5 ft - assert (building_shapefile[build_height_colname.value] > 5).all(), "One or more building height is zero" + # Assert if height inputs is more than 5 ft = 1.5 meters + building_shapefile[build_height_colname] *= 0.3048 + assert (building_shapefile[build_height_colname] > 1.5).all(), "One or more building height is zero" # Assert building with area under 50 sqm long_m, lat_m = coord_to_meters() - areas = building_shapefile[geometry_colname.value].apply(lambda x: x.area * long_m * lat_m) + areas = building_shapefile[geometry_colname].apply(lambda x: x.area * long_m * lat_m) assert (areas > 50).all(), "One or more building has area below 50 sqm" # Perform same checks on shading building shapefile if building_shading_shapefile is not None: # Check if important columns exist in shading building shapefile - assert buildingid_colname.value in building_shading_shapefile, "Missing column {} in building shapefile"\ + assert buildingid_colname in building_shading_shapefile, "Missing column {} in building shapefile"\ .format(buildingid_colname) - assert cnstrct_yr_colname.value in building_shading_shapefile, "Missing column {} in building shapefile"\ + assert cnstrct_yr_colname in building_shading_shapefile, "Missing column {} in building shapefile"\ .format(cnstrct_yr_colname) - assert geometry_colname.value in building_shading_shapefile, "Missing column {} in building shapefile"\ + assert geometry_colname in building_shading_shapefile, "Missing column {} in building shapefile"\ .format(geometry_colname) - assert build_height_colname.value in building_shading_shapefile, "Missing column {} in building shapefile"\ + assert build_height_colname in building_shading_shapefile, "Missing column {} in building shapefile"\ .format(build_height_colname) - assert num_floors_colname.value in building_shading_shapefile, "Missing column {} in building shapefile"\ + assert num_floors_colname in building_shading_shapefile, "Missing column {} in building shapefile"\ .format(num_floors_colname) # Convert columns to float building_shading_shapefile[col_float] = building_shading_shapefile[col_float].astype(float) # Function fix_dup_id automatically solves duplicate BIN from NYC building footprints shapefile building_shading_shapefile = fix_dup_id(building_shading_shapefile, - buildid_colname=buildingid_colname.value) + buildid_colname=buildingid_colname) # Check data type of geometry - geom_type2 = building_shading_shapefile[geometry_colname.value].apply(type).value_counts().index[0] + geom_type2 = building_shading_shapefile[geometry_colname].apply(type).value_counts().index[0] assert geom_type2 == Polygon, 'Geometry in shading building shapefile is not ' \ 'shapely.geometry.Polygon type.' - # Remove shading building with height below 5 ft + # Remove shading building with height below 5 ft = 1.5 meters + building_shading_shapefile[build_height_colname] *= 0.3048 building_shading_shapefile = building_shading_shapefile[building_shading_shapefile[build_height_colname - .value] > 5].copy() + ] > 1.5].copy() # Remove shading building with area under 50 sqm - areas = building_shading_shapefile[geometry_colname.value].apply(lambda x: x.area * long_m * lat_m) + areas = building_shading_shapefile[geometry_colname].apply(lambda x: x.area * long_m * lat_m) area_too_small_index = areas.index[areas < 50] if area_too_small_index.tolist(): building_shading_shapefile = building_shading_shapefile.drop(area_too_small_index) @@ -201,11 +203,11 @@ class BaseSim: self.building_name = building_name self.building_shp = building_shapefile self.building_shading_shp = building_shading_shapefile - self.height_colname = build_height_colname.value - self.geom_colname = geometry_colname.value - self.bin_colname = buildingid_colname.value - self.nfloor_colname = num_floors_colname.value - self.consyr_colname = cnstrct_yr_colname.value + self.height_colname = build_height_colname + self.geom_colname = geometry_colname + self.bin_colname = buildingid_colname + self.nfloor_colname = num_floors_colname + self.consyr_colname = cnstrct_yr_colname def bsd_add(self, name, cons_name, zone_name, obc, sun_exp, w_exp, nb_v, coords, surface_type=None, obc_obj=None): """ @@ -625,7 +627,14 @@ class BaseSim: # Extract Geometry build_shp_ep = extract_geom(build_shp, geom_colname=geometry_colname, bin_colname=buildingid_colname, - build_height_colname=build_height_colname) + build_height_colname=build_height_colname, origin_coords=None) + # Extract origin coordinates + origin_point = list(build_shp_ep['origin'])[0] + # Set building external wall surfaces boundary conditions + if adjacent_building is True: + build_shp_ep = boundary_condition(build_shp_ep, shade_shp, geom_colname=geometry_colname, + build_height_colname=build_height_colname, + num_floors_colname=num_floors_colname) # Create list of (x,y,z) coordinates for each floor height for floors, roofs, and walls. # These coordinates will be processed when adding the wall floors and roofs to the IDF file. @@ -656,10 +665,11 @@ class BaseSim: ceiling.append(ceiling_vertex(input_, hh)) floor.append(floor_vertex(input_, hl)) year.append(int(year_tmp)) - obc.append(build_shp_ep.loc[bin_, 'wall_bound_cond'][i]) - sunexp.append(build_shp_ep.loc[bin_, 'wall_sun_exposed'][i]) - windexp.append(build_shp_ep.loc[bin_, 'wall_wind_exposed'][i]) - window.append(build_shp_ep.loc[bin_, 'wall_windows'][i]) + if adjacent_building is True: + obc.append(build_shp_ep.loc[bin_, 'wall_bound_cond'][i]) + sunexp.append(build_shp_ep.loc[bin_, 'wall_sun_exposed'][i]) + windexp.append(build_shp_ep.loc[bin_, 'wall_wind_exposed'][i]) + window.append(build_shp_ep.loc[bin_, 'wall_windows'][i]) input_ep_all = pd.DataFrame() input_ep_all["walls"] = walls @@ -667,10 +677,11 @@ class BaseSim: input_ep_all["roof"] = ceiling input_ep_all["num_floor"] = num_floor input_ep_all["year"] = year - input_ep_all['wall_bound_cond'] = obc - input_ep_all['wall_sun_exposed'] = sunexp - input_ep_all['wall_wind_exposed'] = windexp - input_ep_all['wall_windows'] = window + if adjacent_building is True: + input_ep_all['wall_bound_cond'] = obc + input_ep_all['wall_sun_exposed'] = sunexp + input_ep_all['wall_wind_exposed'] = windexp + input_ep_all['wall_windows'] = window input_ep_all.index = index ####################################################################### @@ -680,11 +691,11 @@ class BaseSim: assert self.building_shading_shp is not None, "No shading building shapefile input." # Load shading building shapefile - shade_shp = self.building_shading_shp + # shade_shp = self.building_shading_shp # Extract geometry Points shade_shp_ep = extract_geom(shade_shp, geom_colname=geometry_colname, bin_colname=buildingid_colname, - build_height_colname=build_height_colname) + build_height_colname=build_height_colname, origin_coords=origin_point) # Create list of (x,y,z) coordinates for floors, roofs, and walls of shading buildings. # These coordinates will be processed when adding the Shading:Building:Detailed to the IDF file. @@ -763,18 +774,27 @@ class BaseSim: zone = self.zone_add(zone_name=zone_name) zone_list.append(zone) w_l = input_ep.loc[bin_, "walls"] - w_bc = input_ep.loc[bin_, "wall_bound_cond"] - w_se = input_ep.loc[bin_, "wall_sun_exposed"] - w_we = input_ep.loc[bin_, "wall_wind_exposed"] - w_w = input_ep.loc[bin_, "wall_windows"] f = input_ep.loc[bin_, "floor"] r = input_ep.loc[bin_, "roof"] # Walls and Window - for i, (w, bc, se, we, ew) in enumerate(zip(w_l, w_bc, w_se, w_we, w_w)): - self.bsd_add(base_wall_name + " " + str(i+1), cons_wall, zone, obc=bc, obc_obj=None, - sun_exp=se, w_exp=we, nb_v=4, coords=w, surface_type="Wall") - if ew is True: + if adjacent_building is True: + w_bc = input_ep.loc[bin_, "wall_bound_cond"] + w_se = input_ep.loc[bin_, "wall_sun_exposed"] + w_we = input_ep.loc[bin_, "wall_wind_exposed"] + w_w = input_ep.loc[bin_, "wall_windows"] + for i, (w, bc, se, we, ew) in enumerate(zip(w_l, w_bc, w_se, w_we, w_w)): + self.bsd_add(base_wall_name + " " + str(i+1), cons_wall, zone, obc=bc, obc_obj=None, + sun_exp=se, w_exp=we, nb_v=4, coords=w, surface_type="Wall") + if ew is True: + lst_bsd = list(self.idf("buildingsurface:detailed").filter("name", base_wall_name + + " " + str(i+1))) + self.extwindow_wall_ratio_add(cons_window, surface_type="Window", + window_to_wall_ratio=window_to_wall_ratio, list_bsd=lst_bsd) + else: + for i, w in enumerate(w_l): + self.bsd_add(base_wall_name + " " + str(i+1), cons_wall, zone, obc="Outdoors", obc_obj=None, + sun_exp="SunExposed", w_exp="WindExposed", nb_v=4, coords=w, surface_type="Wall") lst_bsd = list(self.idf("buildingsurface:detailed").filter("name", base_wall_name + " " + str(i+1))) self.extwindow_wall_ratio_add(cons_window, surface_type="Window", window_to_wall_ratio=window_to_wall_ratio, list_bsd=lst_bsd) diff --git a/bpeng/simulation/geometry.py b/bpeng/simulation/geometry.py index b732a60..65400d1 100644 --- a/bpeng/simulation/geometry.py +++ b/bpeng/simulation/geometry.py @@ -346,14 +346,30 @@ def find_adjacent_bldg(build_shp, bldg_surround, geom_colname): if line == com_geom['geom']: sfn.append(n) abi.append(com_geom['index']) - build_shp['adjbldg_surface'][i] = sfn - build_shp['adjbldg_index'][i] = abi + build_shp.set_value(i, 'adjbldg_surface', sfn) + build_shp.set_value(i, 'adjbldg_index', abi) return build_shp -def extract_geom(build_shp, geom_colname, bin_colname, build_height_colname): +def extract_geom(build_shp, geom_colname, bin_colname, build_height_colname, origin_coords=None): """ Extract geometry points from shapefile + + Args: + + build_shp + + geom_colname + + bin_colname + + build_height_colname + + origin_coords + + Returns: + + pandas.DataFrame """ # Meter Conversion long_m, lat_m = coord_to_meters() @@ -365,12 +381,13 @@ def extract_geom(build_shp, geom_colname, bin_colname, build_height_colname): # Remove points that will create denegerate surfaces geom_div_col = fix_degenerate_surface(geom_div_col) - # Convert heighroof to meters - build_shp[build_height_colname] *= 0.3048 - # Database for walls, roof and ceilings # Set origin to 0 - x0, y0 = geom_div_col.iloc[0, 0] + if origin_coords is None: + x0, y0 = geom_div_col.iloc[0, 0] + build_shp['origin'] = [(x0, y0)] * len(build_shp) + else: + x0, y0 = origin_coords geom_origin = geom_div_col.copy() for i in range(len(geom_origin.index)): @@ -397,6 +414,22 @@ def boundary_condition(build_shp, bldg_surround, geom_colname, build_height_coln build_shp (pandas.DataFrame) building shapefile dataframe, index is building bin, must contain surface number and adjacent building index columns (see geometry.find_adjacent_bldg) + + bldg_surround + + geom_colname + + build_height_colname + + num_floors_colname + + surface_number_colname + + adjacent_bldg_index_colname + + Returns: + + pandas.DataFrame """ build_shp['wall_bound_cond'] = '' @@ -412,7 +445,7 @@ def boundary_condition(build_shp, bldg_surround, geom_colname, build_height_coln window_list = [] num_surf = len(build_shp.loc[bin_, geom_colname].exterior.coords)-1 for floor_num in range(1, int(build_shp.loc[bin_, num_floors_colname])+1): - obc_temp_list = ['Outdoor'] * num_surf + obc_temp_list = ['Outdoors'] * num_surf sunexp_temp_list = ['SunExposed'] * num_surf windexp_temp_list = ['WindExposed'] * num_surf window_temp_list = [True] * num_surf -- GitLab From 7cf1600bbc194c6030e539bccd972d1f3649b19b Mon Sep 17 00:00:00 2001 From: michbeg Date: Mon, 26 Jun 2017 18:52:44 -0400 Subject: [PATCH 04/13] Fix column name variable issue --- bpeng/simulation/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bpeng/simulation/base.py b/bpeng/simulation/base.py index 1e1312c..12c764f 100644 --- a/bpeng/simulation/base.py +++ b/bpeng/simulation/base.py @@ -199,7 +199,7 @@ class BaseSim: self.idf = idf_source.copy() self.build_type = self.__class__.__name__ - self.building_id = list(building_shapefile[buildingid_colname.value]) + self.building_id = list(building_shapefile[buildingid_colname]) self.building_name = building_name self.building_shp = building_shapefile self.building_shading_shp = building_shading_shapefile -- GitLab From 17f53e1164e0390cc796c328508a94e5760a9574 Mon Sep 17 00:00:00 2001 From: michbeg Date: Thu, 29 Jun 2017 18:22:47 -0400 Subject: [PATCH 05/13] Update midrise init method and docstrings --- bpeng/simulation/base.py | 4 +-- bpeng/simulation/geometry.py | 59 +++++++++++++++++++++++++++--------- bpeng/simulation/midrise.py | 6 ++-- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/bpeng/simulation/base.py b/bpeng/simulation/base.py index 12c764f..8639767 100644 --- a/bpeng/simulation/base.py +++ b/bpeng/simulation/base.py @@ -182,8 +182,8 @@ class BaseSim: 'shapely.geometry.Polygon type.' # Remove shading building with height below 5 ft = 1.5 meters building_shading_shapefile[build_height_colname] *= 0.3048 - building_shading_shapefile = building_shading_shapefile[building_shading_shapefile[build_height_colname - ] > 1.5].copy() + building_shading_shapefile = building_shading_shapefile[building_shading_shapefile[build_height_colname] + > 1.5].copy() # Remove shading building with area under 50 sqm areas = building_shading_shapefile[geometry_colname].apply(lambda x: x.area * long_m * lat_m) area_too_small_index = areas.index[areas < 50] diff --git a/bpeng/simulation/geometry.py b/bpeng/simulation/geometry.py index 65400d1..c7d92bc 100644 --- a/bpeng/simulation/geometry.py +++ b/bpeng/simulation/geometry.py @@ -319,6 +319,23 @@ def poly_inters(geometry1, geometry2): def find_adjacent_bldg(build_shp, bldg_surround, geom_colname): """ Find building surfaces that have adjacent buildings + + Args: + + build_shp (pandas.DataFrame) + building shapefile dataframe + + bldg_surround (pandas.DataFrame) + surrounding building shapefile dataframe + + geom_colname (str) + Column name in build_shp DataFrame corresponding to buidling geometry Polygon + + Returns: + + pandas.DataFrame: building shapefile dataframe with 2 new columns + Column named 'adjbldg_surface', list of building surface index number with adjacent building + column named 'adjbldg_index', list of dataframe index of surrounding buildings taht are adjacent """ build_shp['adjbldg_surface'] = "" @@ -357,19 +374,25 @@ def extract_geom(build_shp, geom_colname, bin_colname, build_height_colname, ori Args: - build_shp + build_shp (pandas.DataFrame) + building shapefile dataframe - geom_colname + geom_colname (str) + Column name in build_shp DataFrame corresponding to buidling geometry Polygon - bin_colname + bin_colname (str) + Column name in build_shp DataFrame corresponding to building identification number - build_height_colname + build_height_colname (str) + Column name in build_shp DataFrame corresponding to building height in meters - origin_coords + origin_coords (tuple) + tuple of origin point coordinates, default value to None Returns: - pandas.DataFrame + pandas.DataFrame: building shapefile dataframe with extracted coordinate points from building + footprint geometry """ # Meter Conversion long_m, lat_m = coord_to_meters() @@ -382,7 +405,7 @@ def extract_geom(build_shp, geom_colname, bin_colname, build_height_colname, ori geom_div_col = fix_degenerate_surface(geom_div_col) # Database for walls, roof and ceilings - # Set origin to 0 + # Set origin to 0 if origin point coordinates not provided if origin_coords is None: x0, y0 = geom_div_col.iloc[0, 0] build_shp['origin'] = [(x0, y0)] * len(build_shp) @@ -413,23 +436,29 @@ def boundary_condition(build_shp, bldg_surround, geom_colname, build_height_coln build_shp (pandas.DataFrame) building shapefile dataframe, index is building bin, must contain surface number - and adjacent building index columns (see geometry.find_adjacent_bldg) + and adjacent building index columns (see method geometry.find_adjacent_bldg) - bldg_surround + bldg_surround (pandas.DataFrame) + surrounding building shapefile dataframe - geom_colname + geom_colname (str) + Column name in build_shp DataFrame corresponding to buidling geometry Polygon - build_height_colname + build_height_colname (str) + Column name in build_shp DataFrame corresponding to building height in meters - num_floors_colname + num_floors_colname (str) + Column name in build_shp DataFrame corresponding to number of floors in building - surface_number_colname + surface_number_colname (str) + Column name in build_shp DataFrame corresponding to surface index number that has an adjacent building - adjacent_bldg_index_colname + adjacent_bldg_index_colname (str) + Column name in build_shp DataFrame corresponding to adjacent building index number from bldg_surround Returns: - pandas.DataFrame + pandas.DataFrame: building shapefile dataframe with boundary condition info """ build_shp['wall_bound_cond'] = '' diff --git a/bpeng/simulation/midrise.py b/bpeng/simulation/midrise.py index 9a351ea..c746303 100644 --- a/bpeng/simulation/midrise.py +++ b/bpeng/simulation/midrise.py @@ -266,9 +266,9 @@ class MidRiseApart(BaseSim): ########################################################## # Functions def __init__(self, building_shapefile, building_name=None, idf_start_file=None, building_shading_shapefile=None, - build_height_colname=NYCShpVal.HEIGHT, geometry_colname=NYCShpVal.GEOM, - buildingid_colname=NYCShpVal.BIN, num_floors_colname=NYCShpVal.NFLOOR, - cnstrct_yr_colname=NYCShpVal.CONSYR): + build_height_colname=NYCShpVal.HEIGHT.value, geometry_colname=NYCShpVal.GEOM.value, + buildingid_colname=NYCShpVal.BIN.value, num_floors_colname=NYCShpVal.NFLOOR.value, + cnstrct_yr_colname=NYCShpVal.CONSYR.value): super().__init__(building_shapefile=building_shapefile, building_name=building_name, idf_start_file=idf_start_file, building_shading_shapefile=building_shading_shapefile, build_height_colname=build_height_colname, geometry_colname=geometry_colname, -- GitLab From 7ef20394e2ddd20cd08ba141211a3b85cafb0bef Mon Sep 17 00:00:00 2001 From: michbeg Date: Tue, 11 Jul 2017 18:03:22 -0400 Subject: [PATCH 06/13] Fix pandas.DataFrame warnings --- bpeng/simulation/geometry.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bpeng/simulation/geometry.py b/bpeng/simulation/geometry.py index c7d92bc..5ef926f 100644 --- a/bpeng/simulation/geometry.py +++ b/bpeng/simulation/geometry.py @@ -491,9 +491,9 @@ def boundary_condition(build_shp, bldg_surround, geom_colname, build_height_coln sunexp_list.append(sunexp_temp_list) windexp_list.append(windexp_temp_list) window_list.append(window_temp_list) - build_shp['wall_bound_cond'][bin_] = obc_list - build_shp['wall_sun_exposed'][bin_] = sunexp_list - build_shp['wall_wind_exposed'][bin_] = windexp_list - build_shp['wall_windows'][bin_] = window_list + build_shp.set_value(bin_, 'wall_bound_cond', obc_list) + build_shp.set_value(bin_, 'wall_sun_exposed', sunexp_list) + build_shp.set_value(bin_, 'wall_wind_exposed', windexp_list) + build_shp.set_value(bin_, 'wall_windows', window_list) return build_shp -- GitLab From 5cfbd1d4037362f5192c6e1d07b056f39204c8e7 Mon Sep 17 00:00:00 2001 From: michbeg Date: Tue, 11 Jul 2017 18:04:24 -0400 Subject: [PATCH 07/13] Create unit test for boundary condition and window creation based on adjacent buildings --- tests/simulation/test_midrise_idf.py | 34 +++++++++--- tests/simulation/test_shapefiles.py | 81 +++++++++++++++++++++++----- 2 files changed, 95 insertions(+), 20 deletions(-) diff --git a/tests/simulation/test_midrise_idf.py b/tests/simulation/test_midrise_idf.py index 414af6d..2a09ad7 100644 --- a/tests/simulation/test_midrise_idf.py +++ b/tests/simulation/test_midrise_idf.py @@ -1,7 +1,9 @@ import pandas as pd from tests.simulation.test_shapefiles import TestShpMidRise -from bpeng.simulation import MidRiseApart, geometry +from bpeng.simulation import MidRiseApart + +from shapely import wkt class TestMidRiseApart: @@ -10,9 +12,16 @@ class TestMidRiseApart: test_shading_shp1 = pd.DataFrame(TestShpMidRise.VALUES2.value, columns=TestShpMidRise.COLNAMES.value) test_shading_shp2 = pd.DataFrame(TestShpMidRise.VALUES3.value, columns=TestShpMidRise.COLNAMES.value) test_shading_shp3 = pd.DataFrame(TestShpMidRise.VALUES4.value, columns=TestShpMidRise.COLNAMES.value) - test_shading_shp = pd.concat([test_shading_shp1, test_shading_shp2, test_shading_shp3], ignore_index=True) - test_shp.geometry = test_shp.geometry.apply(geometry.string_to_polygon) - test_shading_shp.geometry = test_shading_shp.geometry.apply(geometry.string_to_polygon) + test_shading_shp4 = pd.DataFrame(TestShpMidRise.VALUES5.value, columns=TestShpMidRise.COLNAMES.value) + test_shading_shp5 = pd.DataFrame(TestShpMidRise.VALUES6.value, columns=TestShpMidRise.COLNAMES.value) + test_shading_shp6 = pd.DataFrame(TestShpMidRise.VALUES7.value, columns=TestShpMidRise.COLNAMES.value) + test_shading_shp7 = pd.DataFrame(TestShpMidRise.VALUES8.value, columns=TestShpMidRise.COLNAMES.value) + test_shading_shp8 = pd.DataFrame(TestShpMidRise.VALUES9.value, columns=TestShpMidRise.COLNAMES.value) + test_shading_shp = pd.concat([test_shading_shp1, test_shading_shp2, test_shading_shp3, + test_shading_shp4, test_shading_shp5, test_shading_shp6, + test_shading_shp7, test_shading_shp8], ignore_index=True) + test_shp.geometry = test_shp.geometry.apply(wkt.loads) + test_shading_shp.geometry = test_shading_shp.geometry.apply(wkt.loads) def setup_class(self): self.midrise_idf = MidRiseApart(building_shapefile=self.test_shp, @@ -22,12 +31,23 @@ class TestMidRiseApart: def test_init(self): assert self.midrise_idf.build_type == 'MidRiseApart' assert self.midrise_idf.building_name == 'test_building' - assert self.midrise_idf.building_id == [1051959.0] + assert self.midrise_idf.building_id == [1051964.0] def test_idf_creation(self): self.midrise_idf.idf_creation(add_shading_analysis=True, window_to_wall_ratio=0.16, infiltration_value='Automatic', add_people=True, people_value='Automatic', add_equipment=True, equipment_value='Automatic', add_light=True, light_value='Automatic', solar_dist_method="FullExterior", output_table=False) - bsd = self.midrise_idf.idf("BuildingSurface:Detailed").filter('name', 'Wall 1051959.0 floor1 1').one - assert bsd['Construction Name']['Name'] == 'Steel Frame Res Ext Wall_stucco_pre1980' + bsd1 = self.midrise_idf.idf("BuildingSurface:Detailed").filter('name', 'Wall 1051964.0 floor1 1').one + bsd2 = self.midrise_idf.idf("BuildingSurface:Detailed").filter('name', 'Wall 1051964.0 floor1 2').one + fsd = self. midrise_idf.idf("FenestrationSurface:Detailed").filter('name', + 'Sub Surface Wall 1051964.0 floor1 1') + assert bsd1['Construction Name']['Name'] == 'Steel Frame Res Ext Wall_stucco_pre1980' + assert bsd2['Construction Name']['Name'] == 'Steel Frame Res Ext Wall_stucco_pre1980' + assert bsd1['Outside Boundary Condition'] == 'Adiabatic' + assert bsd2['Outside Boundary Condition'] == 'Outdoors' + assert bsd1['Sun Exposure'] == 'NoSun' + assert bsd2['Sun Exposure'] == 'SunExposed' + assert bsd1['Wind Exposure'] == 'NoWind' + assert bsd2['Wind Exposure'] == 'WindExposed' + assert not fsd diff --git a/tests/simulation/test_shapefiles.py b/tests/simulation/test_shapefiles.py index d5a28fc..cfe9853 100644 --- a/tests/simulation/test_shapefiles.py +++ b/tests/simulation/test_shapefiles.py @@ -12,22 +12,35 @@ class TestShpMidRise(Enum): 'shape_area', 'shape_len'] - # building geometry 1 - GEOMETRY1 = 'POLYGON ((-73.94641126946065 40.79089312357564, -73.94652458117535 40.79073642966144, ' \ - '-73.9466023711673 40.79076892427322, -73.94650177155994 40.79090804098926, ' \ - '-73.94647668522865 40.790897561923, -73.94646397442928 40.79091513919471, ' \ - '-73.94641126946065 40.79089312357564))' + # building geometry 1 (bin: 1051964, address: 181 E 104th St, NY) + GEOMETRY1 = 'POLYGON ((-73.94596233622588 40.79065547716238, -73.94604781310119 40.7905372769665, ' \ + '-73.94612853526787 40.7905709961518, -73.9460870898285 40.79062830804775, ' \ + '-73.94605107454727 40.79067811229478, -73.94604237051999 40.79069014881455, ' \ + '-73.94602400833764 40.79068247875713, -73.94602450182865 40.79069488878773, ' \ + '-73.9459646908888 40.79077759696064, -73.94590348792313 40.79075203185335, ' \ + '-73.94595893503435 40.79067535772172, -73.94597743292805 40.79066178414216, ' \ + '-73.94596233622588 40.79065547716238))' - VALUES1 = [[1016320026, 1051959.0, 1910.0, GEOMETRY1, 10.0, 15.0364937268, 4.0, 1543.26117443, 179.44512646599998]] + VALUES1 = [[1016320032, 1051964.0, 1910.0, GEOMETRY1, 7.0, 63.82037224, 5.0, 1991.0637196, 231.99688142]] - # shading building geometry 2 - GEOMETRY2 = 'POLYGON ((-73.94546926801122 40.79076781832001, -73.9455561859173 40.79064762546366, ' \ - '-73.9456969441075 40.79070642342177, -73.94587247261181 40.79077974460716, ' \ - '-73.94578555510427 40.79089993769955, -73.94546926801122 40.79076781832001))' + # shading building geometry 2 (bin: 1051963, address: 177 E 104th St, NY) + GEOMETRY2 = 'POLYGON ((-73.94604237051999 40.79069014881455, -73.94605107454727 40.79067811229478, ' \ + '-73.94608635747097 40.79069285018935, -73.94612237273373 40.79064304593144, ' \ + '-73.9460870898285 40.79062830804775, -73.94612853526787 40.7905709961518, ' \ + '-73.94628358156309 40.79063576138588, -73.94624213621681 40.79069307333704, ' \ + '-73.94621172126159 40.79068036839109, -73.94617570604544 40.79073017267666, ' \ + '-73.94620612101659 40.79074287763203, -73.94616522828537 40.79079942618801, ' \ + '-73.94609790764348 40.79077130540711, -73.94608081746951 40.79079493825651, ' \ + '-73.94614813812807 40.79082305904733, -73.94612588579498 40.79085383046117, ' \ + '-73.9461094325772 40.79087658292265, -73.94595438589057 40.79081181745673, ' \ + '-73.9459936027641 40.79075758607897, -73.9460624098071 40.79078632806144, ' \ + '-73.94607898756577 40.7907634027685, -73.94602120164649 40.79073926391423, ' \ + '-73.94601794801139 40.79074376219215, -73.94600692808851 40.79073915907394, ' \ + '-73.94604237051999 40.79069014881455))' - VALUES2 = [[1016320037, 1051968.0, 1958.0, GEOMETRY2, 10.0, 18.47014921, 1.0, 4993.71774248, 299.811633249]] + VALUES2 = [[1016320030, 1051963.0, 1890.0, GEOMETRY2, 9.0, 76.61020529, 5.0, 4065.28627347, 429.49754844]] - # shading building geometry 3 (with duplicate bin) + # shading building geometry 3 (with duplicate bin: 1000000.0) GEOMETRY3 = 'POLYGON ((-73.94645658458069 40.79132475276936, -73.9464908028793 40.79127743384843, ' \ '-73.94655181519008 40.79119306274159, -73.94657866764616 40.79120427921903, ' \ '-73.94659165741159 40.79120970445152, -73.94658277311777 40.79122198946571, ' \ @@ -35,9 +48,51 @@ class TestShpMidRise(Enum): VALUES3 = [[1016320150, 1000000.0, 1900.0, GEOMETRY3, 11.0, 42.96, 3.0, 689.202639689, 134.673569074]] - # shading building geometry 4 (with duplicate bin) + # shading building geometry 4 (with duplicate bin: 1000000.0) GEOMETRY4 = 'POLYGON ((-73.94564604994339 40.79052335826294, -73.94569347039969 40.79045778364416, ' \ '-73.94592137466176 40.79055298367584, -73.94587395554622 40.7906185592885, ' \ '-73.94564604994339 40.79052335826294))' VALUES4 = [[1016320034, 1000000.0, 1910.0, GEOMETRY4, 10.0, 46.6717958, 4.0, 1963.1642707, 198.546474432]] + + # shading building geometry 5 (bin: 1051981.0, address: 183 E 104th St, NY) + GEOMETRY5 = 'POLYGON ((-73.94587395554622 40.7906185592885, -73.94592137466176 40.79055298367584, ' \ + '-73.94595943134696 40.79050035825642, -73.94604781310119 40.7905372769665, ' \ + '-73.94596233622588 40.79065547716238, -73.94587395554622 40.7906185592885))' + + VALUES5 = [[1016320133, 1051981.0, 1910.0, GEOMETRY5, 10.0, 43.35636275, 4.0, 1372.290304, 154.132200505]] + + # shading building geometry 6 (bin: 1051967.0, address: 1888 3rd Ave, NY) + GEOMETRY6 = 'POLYGON ((-73.9456969441075 40.79070642342177, -73.9455561859173 40.79064762546366, ' \ + '-73.94564604994339 40.79052335826294, -73.94587395554622 40.7906185592885, ' \ + '-73.94596233622588 40.79065547716238, -73.94588741115615 40.79075908693297, ' \ + '-73.94571188269057 40.79068576486964, -73.9456969441075 40.79070642342177))' + + VALUES6 = [[1016320035, 1051967.0, 1910.0, GEOMETRY6, 10.0, 37.69420342, 3.0, 4686.6799767, 303.199763188]] + + # shading building geometry 7 (bin: 1051970.0, address: 174 E 105th St, NY) + GEOMETRY7 = 'POLYGON ((-73.94569847240513 40.79102035847318, -73.94578555510427 40.79089993769955, ' \ + '-73.94587247261181 40.79077974460716, -73.94596598089974 40.79081880447469, ' \ + '-73.94583300936029 40.79100268281859, -73.94579198092889 40.79105941848044, ' \ + '-73.94569847240513 40.79102035847318))' + + VALUES7 = [[1016320041, 1051970.0, 1930.0, GEOMETRY7, 10.0, 28.12992946, 2.0, 2955.51593943, 259.155336771]] + + # shading building geometry 8 (bin: 1051971.0, address: 174 E 105th St, NY) + GEOMETRY8 = 'POLYGON ((-73.94579198092889 40.79105941848044, -73.94583300936029 40.79100268281859, ' \ + '-73.94585370967813 40.79101132924661, -73.9458689570428 40.79100729045944, ' \ + '-73.94588938522058 40.79097904234269, -73.94588218742871 40.79097603579368, ' \ + '-73.94596662933809 40.79085926697368, -73.94606609429911 40.79090081583407, ' \ + '-73.94596325151728 40.79104303235219, -73.94595820464599 40.79104092461549, ' \ + '-73.94595579791481 40.79104425173722, -73.94595679010888 40.7910567880714, ' \ + '-73.94597331769842 40.7910636918535, -73.94594044306935 40.79110915352002, ' \ + '-73.94593362260169 40.7911185848655, -73.94579198092889 40.79105941848044))' + + VALUES8 = [[1016320042, 1051971.0, 1910.0, GEOMETRY8, 10.0, 63.040, 6.0, 3017.23605191, 268.065277115]] + + # shading building geometry 9 (bin: 1051968.0, address: 1892 3rd Ave, NY) + GEOMETRY9 = 'POLYGON ((-73.94546926801122 40.79076781832001, -73.9455561859173 40.79064762546366, ' \ + '-73.9456969441075 40.79070642342177, -73.94587247261181 40.79077974460716, ' \ + '-73.94578555510427 40.79089993769955, -73.94546926801122 40.79076781832001))' + + VALUES9 = [[1016320037, 1051968.0, 1958.0, GEOMETRY9, 10.0, 18.47014921, 1.0, 4993.71774248, 299.811633249]] -- GitLab From 8d1b5fce2675c55ad8f6b8499e72fab3e8db39dc Mon Sep 17 00:00:00 2001 From: michbeg Date: Wed, 12 Jul 2017 11:09:19 -0400 Subject: [PATCH 08/13] Replace print statements to logging --- bpeng/simulation/base.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/bpeng/simulation/base.py b/bpeng/simulation/base.py index 8639767..61adf13 100644 --- a/bpeng/simulation/base.py +++ b/bpeng/simulation/base.py @@ -4,22 +4,24 @@ Base class for idf file creation (EnergyPlus input file). import abc import datetime +import logging import math import os import time -from enum import Enum import numpy as np import pandas as pd -from shapely.geometry import Polygon from oplus import IDF +from enum import Enum +from shapely.geometry import Polygon + from .building_hvac_oplus import (add_designspec_airdist, add_sch_compact, add_sch_constant, add_sch_typelimits, add_sizing_zone, add_thermostat_dual_setpoints, add_zone_equipmentconnection, add_zone_equipmentlist, add_zone_ideal_airloads, add_zone_thermostat) from .design_days import add_dday -from .geometry import (ceiling_vertex, coord_to_meters, fix_dup_id, floor_vertex, - offset_center_ratio, walls_vertex, extract_geom, find_adjacent_bldg, boundary_condition) +from .geometry import (boundary_condition, ceiling_vertex, coord_to_meters, extract_geom, find_adjacent_bldg, + fix_dup_id, floor_vertex, offset_center_ratio, walls_vertex) # TODO: put NYCShpVal class in a new file @@ -611,7 +613,7 @@ class BaseSim: # IDF creation start1 = time.clock() - print("Starting IDF creation.") + logging.info("Starting IDF creation for %s", self.building_name) # Load shapefile data build_shp = self.building_shp @@ -732,6 +734,9 @@ class BaseSim: # One window object is added per wall with outdoor outside boundary condition # One zone is added per floor and per building. # TODO: Create function to add BuildingSurface:Detailed objects to idf + + logging.info('Adding building surfaces, fenestration surfaces and zones') + input_ep = input_ep_all.copy() zone_list = [] @@ -883,6 +888,7 @@ class BaseSim: # Shading:Building:Detailed if add_shading_analysis is True: + logging.info('Adding surrounding buildings for shading analysis') assert self.building_shading_shp is not None, "No shading building shapefile input." input_shade_ep = input_shade_ep_all.copy() @@ -932,7 +938,7 @@ class BaseSim: # HVAC start = time.clock() - print('Creating ZoneHVAC:IdealLoadsAirSystem objects to idf file') + logging.info('Creating ZoneHVAC:IdealLoadsAirSystem objects to idf file') zone_eq_name = "ZoneHVAC:IdealLoadsAirSystem" temperature_sch_type = add_sch_typelimits(self.idf, "Temperature", None, None, "Continuous", "temperature") @@ -959,8 +965,8 @@ class BaseSim: " Zone Return Outlet", zone_eqlist=eqlist, zone_inlet_node=node_inlet) - print("Time to add ZoneHVAC IDFObject: {} minutes for {} zones.".format((time.clock() - start)/60, - len(self.idf("zone")))) + logging.info("Time to add ZoneHVAC IDFObject: %.3g seconds for %i zones", (time.clock() - start), + len(self.idf("zone"))) # Check for OutputControl:Table:Style object wanted if output_table is False: @@ -991,7 +997,7 @@ class BaseSim: if "Surface Outside Face Sunlit Fraction" in output_variables: self.idf.add_object("Output:Variable,*,Surface Outside Face Sunlit Fraction,hourly; !- Zone Average []") - print("Time taken to create IDF: {}".format((time.clock() - start1)/60)) + logging.info("Time taken to create IDF: %.3g seconds", ((time.clock() - start1))) return self.idf -- GitLab From 9c2adef6c569fb28793ea1ed92c45ea6b2dfb1e7 Mon Sep 17 00:00:00 2001 From: michbeg Date: Wed, 12 Jul 2017 11:25:22 -0400 Subject: [PATCH 09/13] Update unit test --- tests/simulation/test_midrise_idf.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/simulation/test_midrise_idf.py b/tests/simulation/test_midrise_idf.py index 2a09ad7..d517ea7 100644 --- a/tests/simulation/test_midrise_idf.py +++ b/tests/simulation/test_midrise_idf.py @@ -40,14 +40,24 @@ class TestMidRiseApart: light_value='Automatic', solar_dist_method="FullExterior", output_table=False) bsd1 = self.midrise_idf.idf("BuildingSurface:Detailed").filter('name', 'Wall 1051964.0 floor1 1').one bsd2 = self.midrise_idf.idf("BuildingSurface:Detailed").filter('name', 'Wall 1051964.0 floor1 2').one - fsd = self. midrise_idf.idf("FenestrationSurface:Detailed").filter('name', - 'Sub Surface Wall 1051964.0 floor1 1') + bsd3 = self.midrise_idf.idf("BuildingSurface:Detailed").filter('name', 'Wall 1051964.0 floor3 3').one + bsd4 = self.midrise_idf.idf("BuildingSurface:Detailed").filter('name', 'Wall 1051964.0 floor5 5').one + fsd1 = self. midrise_idf.idf("FenestrationSurface:Detailed").filter('name', + 'Sub Surface Wall 1051964.0 floor1 1') + fsd2 = self. midrise_idf.idf("FenestrationSurface:Detailed").filter('name', + 'Sub Surface Wall 1051964.0 floor4 1') + # Check wall construction assert bsd1['Construction Name']['Name'] == 'Steel Frame Res Ext Wall_stucco_pre1980' assert bsd2['Construction Name']['Name'] == 'Steel Frame Res Ext Wall_stucco_pre1980' + # Check boundary conditions assert bsd1['Outside Boundary Condition'] == 'Adiabatic' assert bsd2['Outside Boundary Condition'] == 'Outdoors' + assert bsd3['Outside Boundary Condition'] == 'Adiabatic' + assert bsd4['Outside Boundary Condition'] == 'Adiabatic' assert bsd1['Sun Exposure'] == 'NoSun' assert bsd2['Sun Exposure'] == 'SunExposed' assert bsd1['Wind Exposure'] == 'NoWind' assert bsd2['Wind Exposure'] == 'WindExposed' - assert not fsd + # Check fenestration surfaces + assert not fsd1 + assert fsd2 -- GitLab From d10bc9ec8d688b01903214f544e160367d3ef13f Mon Sep 17 00:00:00 2001 From: michbeg Date: Wed, 12 Jul 2017 11:31:01 -0400 Subject: [PATCH 10/13] Fix formatting --- bpeng/simulation/base.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bpeng/simulation/base.py b/bpeng/simulation/base.py index 61adf13..83cd021 100644 --- a/bpeng/simulation/base.py +++ b/bpeng/simulation/base.py @@ -135,15 +135,15 @@ class BaseSim: """ # Assert if columns taht will be used exist in building shapefile assert buildingid_colname in building_shapefile, "Missing column {} in building shapefile"\ - .format(buildingid_colname) + .format(buildingid_colname) assert cnstrct_yr_colname in building_shapefile, "Missing column {} in building shapefile"\ - .format(cnstrct_yr_colname) + .format(cnstrct_yr_colname) assert geometry_colname in building_shapefile, "Missing column {} in building shapefile"\ - .format(geometry_colname) + .format(geometry_colname) assert build_height_colname in building_shapefile, "Missing column {} in building shapefile"\ - .format(build_height_colname) + .format(build_height_colname) assert num_floors_colname in building_shapefile, "Missing column {} in building shapefile"\ - .format(num_floors_colname) + .format(num_floors_colname) # Convert columns to float col_float = [buildingid_colname, cnstrct_yr_colname, build_height_colname, num_floors_colname] @@ -164,15 +164,15 @@ class BaseSim: if building_shading_shapefile is not None: # Check if important columns exist in shading building shapefile assert buildingid_colname in building_shading_shapefile, "Missing column {} in building shapefile"\ - .format(buildingid_colname) + .format(buildingid_colname) assert cnstrct_yr_colname in building_shading_shapefile, "Missing column {} in building shapefile"\ - .format(cnstrct_yr_colname) + .format(cnstrct_yr_colname) assert geometry_colname in building_shading_shapefile, "Missing column {} in building shapefile"\ - .format(geometry_colname) + .format(geometry_colname) assert build_height_colname in building_shading_shapefile, "Missing column {} in building shapefile"\ - .format(build_height_colname) + .format(build_height_colname) assert num_floors_colname in building_shading_shapefile, "Missing column {} in building shapefile"\ - .format(num_floors_colname) + .format(num_floors_colname) # Convert columns to float building_shading_shapefile[col_float] = building_shading_shapefile[col_float].astype(float) # Function fix_dup_id automatically solves duplicate BIN from NYC building footprints shapefile -- GitLab From 08bb22b4b5d72b3faeae5a4a01158264408ceb1e Mon Sep 17 00:00:00 2001 From: michbeg Date: Wed, 12 Jul 2017 11:58:06 -0400 Subject: [PATCH 11/13] Fix pylint error --- bpeng/simulation/base.py | 4 ++-- bpeng/simulation/geometry.py | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/bpeng/simulation/base.py b/bpeng/simulation/base.py index 83cd021..8122bb3 100644 --- a/bpeng/simulation/base.py +++ b/bpeng/simulation/base.py @@ -629,7 +629,7 @@ class BaseSim: # Extract Geometry build_shp_ep = extract_geom(build_shp, geom_colname=geometry_colname, bin_colname=buildingid_colname, - build_height_colname=build_height_colname, origin_coords=None) + origin_coords=None) # Extract origin coordinates origin_point = list(build_shp_ep['origin'])[0] # Set building external wall surfaces boundary conditions @@ -697,7 +697,7 @@ class BaseSim: # Extract geometry Points shade_shp_ep = extract_geom(shade_shp, geom_colname=geometry_colname, bin_colname=buildingid_colname, - build_height_colname=build_height_colname, origin_coords=origin_point) + origin_coords=origin_point) # Create list of (x,y,z) coordinates for floors, roofs, and walls of shading buildings. # These coordinates will be processed when adding the Shading:Building:Detailed to the IDF file. diff --git a/bpeng/simulation/geometry.py b/bpeng/simulation/geometry.py index 5ef926f..ac843e6 100644 --- a/bpeng/simulation/geometry.py +++ b/bpeng/simulation/geometry.py @@ -14,7 +14,7 @@ from shapely.geometry import Point, Polygon, LineString, MultiLineString from geopy.distance import vincenty -# pylint: disable=invalid-name +# pylint: disable=invalid-name,too-many-nested-blocks,too-many-locals def reverse_list(lst): return [lst[-i] for i in range(1, len(lst) + 1)] @@ -368,7 +368,8 @@ def find_adjacent_bldg(build_shp, bldg_surround, geom_colname): return build_shp -def extract_geom(build_shp, geom_colname, bin_colname, build_height_colname, origin_coords=None): + +def extract_geom(build_shp, geom_colname, bin_colname, origin_coords=None): """ Extract geometry points from shapefile @@ -383,9 +384,6 @@ def extract_geom(build_shp, geom_colname, bin_colname, build_height_colname, ori bin_colname (str) Column name in build_shp DataFrame corresponding to building identification number - build_height_colname (str) - Column name in build_shp DataFrame corresponding to building height in meters - origin_coords (tuple) tuple of origin point coordinates, default value to None -- GitLab From 94530d21fe5da0d491d004108452e16de18539ae Mon Sep 17 00:00:00 2001 From: michbeg Date: Wed, 12 Jul 2017 12:15:58 -0400 Subject: [PATCH 12/13] Update import sorting order --- bpeng/simulation/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bpeng/simulation/base.py b/bpeng/simulation/base.py index 8122bb3..9df524d 100644 --- a/bpeng/simulation/base.py +++ b/bpeng/simulation/base.py @@ -8,12 +8,12 @@ import logging import math import os import time +from enum import Enum import numpy as np import pandas as pd from oplus import IDF -from enum import Enum from shapely.geometry import Polygon from .building_hvac_oplus import (add_designspec_airdist, add_sch_compact, add_sch_constant, add_sch_typelimits, -- GitLab From 8e8995c697bef1f53477e6e2ae093174307a9af2 Mon Sep 17 00:00:00 2001 From: michbeg Date: Wed, 12 Jul 2017 18:18:03 -0400 Subject: [PATCH 13/13] Add checks to base init method and logging warnings --- bpeng/simulation/base.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bpeng/simulation/base.py b/bpeng/simulation/base.py index 9df524d..28fb38a 100644 --- a/bpeng/simulation/base.py +++ b/bpeng/simulation/base.py @@ -159,6 +159,9 @@ class BaseSim: long_m, lat_m = coord_to_meters() areas = building_shapefile[geometry_colname].apply(lambda x: x.area * long_m * lat_m) assert (areas > 50).all(), "One or more building has area below 50 sqm" + # Assert number of vertices in building geometry + num_vert = building_shapefile[geometry_colname].apply(lambda x: len(x.exterior.coords)) + assert (num_vert < 120).all(), "One or more building footprint geometry has more than 120 vertices" # Perform same checks on shading building shapefile if building_shading_shapefile is not None: @@ -184,13 +187,25 @@ class BaseSim: 'shapely.geometry.Polygon type.' # Remove shading building with height below 5 ft = 1.5 meters building_shading_shapefile[build_height_colname] *= 0.3048 + if building_shading_shapefile[building_shading_shapefile[build_height_colname] <= 1.5].empty is False: + logging.warning('One or more shading building has height below 1.5 m, ' + 'these buildings were deleted') building_shading_shapefile = building_shading_shapefile[building_shading_shapefile[build_height_colname] > 1.5].copy() # Remove shading building with area under 50 sqm areas = building_shading_shapefile[geometry_colname].apply(lambda x: x.area * long_m * lat_m) area_too_small_index = areas.index[areas < 50] if area_too_small_index.tolist(): + logging.warning('One or more shading building has area under 50 sqm, ' + 'these buildings were deleted') building_shading_shapefile = building_shading_shapefile.drop(area_too_small_index) + # Remove shading building with more than 120 vertices in footprint geometry + num_vert = building_shading_shapefile[geometry_colname].apply(lambda x: len(x.exterior.coords)) + num_vert_too_big_index = num_vert.index[num_vert > 120] + if num_vert_too_big_index.tolist(): + logging.warning('One or more shading building footprint geometry has more than 120 vertices, ' + 'these buildings were deleted') + building_shading_shapefile = building_shading_shapefile.drop(num_vert_too_big_index) # Load starting idf file dir_path = os.path.dirname(os.path.abspath(__file__)) -- GitLab