diff --git a/conf/bardoux.yml b/conf/bardoux.yml new file mode 100644 index 0000000..81905a1 --- /dev/null +++ b/conf/bardoux.yml @@ -0,0 +1,14 @@ +building_name: 8_bardoux + +name: raw +suffix: no_hvac + +Terrain: City + +os_ep_path: "C:/openstudioapplication-1.8.0/EnergyPlus" + +zones: [] + +loops: [] + +equipments: [] diff --git a/conf_geometry/bardoux.yml b/conf_geometry/bardoux.yml new file mode 100644 index 0000000..d0a0580 --- /dev/null +++ b/conf_geometry/bardoux.yml @@ -0,0 +1,329 @@ +name: 8_bardoux +glasswool_new: 0.14 + +blocks: + ss2_alt: &ss2_alt -8 + ss1_alt: &ss1_alt -4 + rdc_alt: &rdc_alt 0 + r1_alt: &r1_alt 3 + r2_alt: &r2_alt 6 + r3_alt: &r3_alt 9 + r4_alt: &r4_alt 12 + d0: 19 + d1: 3 + d2: 5 + d3: 4 + d4: 4 + d5: 3 + z0: 12 + annexe: + - ["-d1", 0, *rdc_alt] + - ["-d1", "d3+d4", *rdc_alt] + - ["-d1-d2", "d3+d4", *rdc_alt] + - ["-d1-d2", 0, *rdc_alt] + cour_drouet: + - ["-d1", 0, *rdc_alt] + - [0, 0, *rdc_alt] + - [0, "d3+d4", *rdc_alt] + - ["-d1", "d3+d4", *rdc_alt] + car_path: + - ["d0", "d3+d4", *rdc_alt] + - ["d0", "d3+d4+d5", *rdc_alt] + - ["-d1-d2", "d3+d4+d5", *rdc_alt] + - ["-d1-d2", "d3+d4", *rdc_alt] + caves: + - [0, 0, *rdc_alt] + - ["d0", 0, *rdc_alt] + - ["d0", "d3+d4", *rdc_alt] + - [0, "d3+d4", *rdc_alt] + r1_passerelle: + - ["-d1","d3", *r1_alt] + - [0,"d3", *r1_alt] + - [0, "d3+d4", *r1_alt] + - ["-d1", "d3+d4", *r1_alt] + r1_annexe: + - ["-d1-d2", 0, *r1_alt] + - ["-d1", 0, *r1_alt] + - ["-d1", "d3+d4", *r1_alt] + - ["-d1-d2", "d3+d4", *r1_alt] + r1_annexe_b: + - ["-d1-d2", "d3+d4", *r1_alt] + - ["-d1", "d3+d4", *r1_alt] + - ["-d1", "d3+d4+d5", *r1_alt] + - ["-d1-d2", "d3+d4+d5", *r1_alt] + r1_1: + - [0, 0, *r1_alt] + - ["d0", 0, *r1_alt] + - ["d0", "d3+d4", *r1_alt] + - [0, "d3+d4", *r1_alt] + r1_2: + - [0, "d3+d4", *r1_alt] + - ["d0", "d3+d4", *r1_alt] + - ["d0", "d3+d4+d5", *r1_alt] + - [0, "d3+d4+d5", *r1_alt] + h0: 2 + h1: 3 + h2: 2.5 + h3: 1 + h4: 1.8 + toit1_1: + - [0, 0, "z0+h0"] + - [3, 0, "z0+h1"] + - [3, "d3+d4+d5", "z0+h1"] + - [0, "d3+d4+d5", "z0+h0"] + toit1_2: + - [3, 0, "z0+h1"] + - ["3+7", 0, "z0+h2"] + - ["3+7", "d3+d4+d5", "z0+h2"] + - [3, "d3+d4+d5", "z0+h1"] + toit2_1: + - ["3+7", 0, "z0+h2"] + - ["3+7+9", 0, "z0+h3"] + - ["3+7", "d3/2+d4/2+d5/2", "z0+h2"] + toit2_2: + - ["3+7", "d3/2+d4/2+d5/2", "z0+h2"] + - ["3+7+9", "d3+d4+d5", "z0+h3"] + - ["3+7", "d3+d4+d5", "z0+h2"] + toit3_1: + - ["3+7", "d3/2+d4/2+d5/2", "z0+h2"] + - ["3+7+9", 0, "z0+h3"] + - ["3+7+9", "d3/2+d4/2+d5/2", "z0+h4"] + toit3_2: + - ["3+7", "d3/2+d4/2+d5/2", "z0+h2"] + - ["3+7+9", "d3/2+d4/2+d5/2", "z0+h4"] + - ["3+7+9", "d3+d4+d5", "z0+h3"] + +immeuble_8: + ss2: + height: 4 + use_blocks_vars: 1 + constructions: + walls: townhouse_basement + floors: townhouse_basement + roofs: townhouse_basement + walls: + - ["d0", 0, *ss2_alt] + - ["d0", "d3+d4+d5", *ss2_alt] + - ["-d1-d2", "d3+d4+d5", *ss2_alt] + - ["-d1-d2", 0, *ss2_alt] + ss1: + height: 4 + use_blocks_vars: 1 + constructions: + walls: townhouse_basement + floors: townhouse_basement + roofs: townhouse_basement + floors: + - ["d0", 0, *ss1_alt] + - ["d0", "d3+d4+d5", *ss1_alt] + - ["-d1-d2", "d3+d4+d5", *ss1_alt] + - ["-d1-d2", 0, *ss1_alt] + roofs: [annexe, cour_drouet, car_path, caves] + walls: + - ["d0", 0, *ss1_alt] + - ["d0", "d3+d4+d5", *ss1_alt] + - ["-d1-d2", "d3+d4+d5", *ss1_alt] + - ["-d1-d2", 0, *ss1_alt] + rdc: + height: 3 + use_blocks_vars: 1 + constructions: + floors: townhouse_basement + walls: + - [0, 0, *rdc_alt] + - ["d0", 0, *rdc_alt] + - ["d0", "d3+d4", *rdc_alt] + - [0, "d3+d4", *rdc_alt] + apertures: + numbers: [0, 2, 1, 0] + widths: [0, 1.4, 2, 0] + heights: [0, 1.5, 3, 0] + sill_heights: [0, 1, 0, 0] + types: + - none + - aperture + - door + - none + rdc_annexe: + height: 3 + use_blocks_vars: 1 + constructions: + floors: townhouse_basement + walls: + - ["-d1-d2", 0, *rdc_alt] + - ["-d1", 0, *rdc_alt] + - ["-d1", "d3+d4", *rdc_alt] + - ["-d1-d2", "d3+d4", *rdc_alt] + r1: + height: 3 + use_blocks_vars: 1 + floors: [r1_1, r1_2] + roofs: + - [0, 0, *r2_alt] + - ["d0", 0, *r2_alt] + - ["d0", "d3+d4+d5", *r2_alt] + - [0, "d3+d4+d5", *r2_alt] + walls: + - [0, 0, *r1_alt] + - ["d0", 0, *r1_alt] + - ["d0", "d3+d4+d5", *r1_alt] + - [0, "d3+d4+d5", *r1_alt] + - [0, "d3+d4", *r1_alt] + - [0, "d3", *r1_alt] + remove_wall: [4] + apertures: + numbers: [0, 3, 0, 1, 0, 1] + widths: [0, 1.3, 0, 1.3, 0, 1.3] + heights: [0, 1.5, 0, 1.5, 0, 1.5] + sill_heights: [0, 1, 0, 1, 0, 1] + types: + - none + - aperture + - none + - aperture + - none + - aperture + r1_annexe: + height: 3 + use_blocks_vars: 1 + floors: [r1_passerelle, r1_annexe, r1_annexe_b] + roofs: + - ["-d1-d2", 0, *r2_alt] + - ["-d1", 0, *r2_alt] + - ["-d1", "d3", *r2_alt] + - [0, "d3", *r2_alt] + - [0, "d3+d4", *r2_alt] + - ["-d1", "d3+d4", *r2_alt] + - ["-d1", "d3+d4+d5", *r2_alt] + - ["-d1-d2", "d3+d4+d5", *r2_alt] + walls: + - ["-d1-d2", 0, *r1_alt] + - ["-d1", 0, *r1_alt] + - ["-d1", "d3", *r1_alt] + - [0, "d3", *r1_alt] + - [0, "d3+d4", *r1_alt] + - ["-d1", "d3+d4", *r1_alt] + - ["-d1", "d3+d4+d5", *r1_alt] + - ["-d1-d2", "d3+d4+d5", *r1_alt] + - ["-d1-d2", "d3+d4", *r1_alt] + - ["-d1-d2", "d3", *r1_alt] + remove_wall: [3] + apertures: + numbers: [0, 1, 1, 0, 1, 1, 0, 0, 0, 1] + widths: [0, 2, 1, 0, 1.3, 1.3, 0, 0, 0, 1.3] + heights: [0, 2.5, 2.5, 0, 1.5, 1.5, 0, 0, 0, 1.5] + sill_heights: [0, 0, 0, 0, 1, 1, 0, 0, 0, 1] + constructions: + - none + - window_pvc + - window_pvc + - none + - lb + - lb + - none + - none + - none + - lb + types: + - none + - door + - door + - none + - aperture + - aperture + - none + - aperture + - none + - aperture + r2: + height: 3 + use_blocks_vars: 1 + walls: + - [0, "d3", *r2_alt] + - [0, 0, *r2_alt] + - ["d0", 0, *r2_alt] + - ["d0", "d3+d4+d5", *r2_alt] + - [0, "d3+d4+d5", *r2_alt] + - [0, "d3+d4", *r2_alt] + remove_wall: [5] + apertures: + numbers: [1,0,3,0,1,0] + r2_annexe: + height: 3 + use_blocks_vars: 1 + walls: + - ["-d1-d2", 0, *r2_alt] + - ["-d1", 0, *r2_alt] + - ["-d1", "d3", *r2_alt] + - [0, "d3", *r2_alt] + - [0, "d3+d4", *r2_alt] + - ["-d1", "d3+d4", *r2_alt] + - ["-d1", "d3+d4+d5", *r2_alt] + - ["-d1-d2", "d3+d4+d5", *r2_alt] + - ["-d1-d2", "d3+d4", *r2_alt] + - ["-d1-d2", "d3", *r2_alt] + remove_wall: [3] + apertures: + numbers: [0, 1, 1, 0, 0, 1, 0, 1, 0, 1] + widths: [0, 1.3, 2.5, 0 ,0, 1.3, 0, 1.3, 0, 1.3] + heights: [0, 1.5, 1.5, 0, 0, 1.5, 0, 1.5, 0, 1.5] + sill_heights: [0, 1, 1, 0, 0, 1, 0, 1, 0, 1] + r3: + height: 3 + use_blocks_vars: 1 + walls: + - [0, 0, *r3_alt] + - ["d0", 0, *r3_alt] + - ["d0", "d3+d4+d5", *r3_alt] + - [0, "d3+d4+d5", *r3_alt] + - [0, "d3+d4", *r3_alt] + - [0, "d3", *r3_alt] + apertures: + numbers: [0,3,0,1,1,1] + r4: + use_blocks_vars: 1 + floors: + - [0, 0, *r4_alt] + - ["d0", 0, *r4_alt] + - ["d0", "d3+d4+d5", *r4_alt] + - [0, "d3+d4+d5", *r4_alt] + roofs: [toit1_1, toit1_2, toit2_1, toit2_2, toit3_1, toit3_2] + vasistas: + numbers: [0,3] + sill_heights: [0,2] + walls: + - [0, 0, *r4_alt, recent_light_renovation] + - [3, 0, *r4_alt, recent_light_renovation] + - ["3+7", 0, *r4_alt, recent_light_renovation] + - ["d0", 0, *r4_alt] + - ["d0", "d3/2+d4/2+d5/2", *r4_alt] + - ["d0", "d3+d4+d5", *r4_alt, recent_light_renovation] + - ["3+7", "d3+d4+d5", *r4_alt, recent_light_renovation] + - [3, "d3+d4+d5", *r4_alt, recent_light_renovation] + - [0, "d3+d4+d5", *r4_alt, recent_light_renovation] + - [0, "d3+d4", *r4_alt, recent_light_renovation] + - [0, "d3", *r4_alt, recent_light_renovation] + heights: [2,3,2.5,1,1.8,1,2.5,3,2,2,2] + elements: + half_lune_1: + index: 3 + geometry: + - ["d0", "d3/2+d4/2+d5/2-0.8", "z0+0.5"] + - ["d0", "d3/2+d4/2+d5/2", "z0+0.5"] + - ["d0", "d3/2+d4/2+d5/2", "z0+1.2"] + - ["d0", "d3/2+d4/2+d5/2-0.6", "z0+0.9"] + construction: simple_glass_wall + type: aperture + half_lune_2: + index: 4 + geometry: + - ["d0", "d3/2+d4/2+d5/2", "z0+1.2"] + - ["d0", "d3/2+d4/2+d5/2", "z0+0.5"] + - ["d0", "d3/2+d4/2+d5/2+0.8", "z0+0.5"] + - ["d0", "d3/2+d4/2+d5/2+0.6", "z0+0.9"] + construction: simple_glass_wall + type: aperture + apertures: + numbers: [0,0,0,0,0,0,0,0,1,1,1] + heights: [0,0,0,0,0,0,0,0,1.3,1.3,1.3] + sill_heights: [0,0,0,0,0,0,0,0,0.5,0.5,0.5] diff --git a/generate_geometry_from_yaml.py b/generate_geometry_from_yaml.py index 93f2133..ed2a9c8 100644 --- a/generate_geometry_from_yaml.py +++ b/generate_geometry_from_yaml.py @@ -9,10 +9,10 @@ from ladybug_geometry.geometry3d import Face3D, Point3D -from src.idfhub.common import get_logger, eval_expr, GEOMETRY, BLOCKS, get_variables +from idfhub.common import get_logger, eval_expr, GEOMETRY, BLOCKS, get_variables -from src.idfhub.helpers.geometry import complex_room, ApertureManager, add_aperture -from src.idfhub.helpers.matlib import CONSTLIB +from idfhub.helpers.geometry import complex_room, ApertureManager, add_aperture +from idfhub.helpers.matlib import CONSTLIB LOGGER = get_logger(log_level=logging.INFO) @@ -22,11 +22,12 @@ common_height = GEOMETRY.get("height", 3) def prepare( - coordinates: list[list[float]], - variables: dict|None = None + coordinates: list[list[float|str]], + *, + variables: dict ) -> list[Point3D]: - """prepare for complex_room method""" - if variables is None: + """evaluate user formulas and prepare for complex_room method""" + if len(variables) == 0: return [ Point3D(*row) for row in coordinates @@ -39,6 +40,78 @@ def prepare( for row in coordinates ] +def dispatch_apertures( + *, + apertures: dict[str, list], + manager: ApertureManager, + destination_faces: list[Face3D] +): + """dispatch apertures on destination faces + numbers is a mandatory key in the apertures dict + """ + ap_numbers = apertures["numbers"] + try: + ap_widths = apertures["widths"] + except KeyError: + ap_widths = [] + try: + ap_heights = apertures["heights"] + except KeyError: + ap_heights = [] + try: + ap_sill_heights = apertures["sill_heights"] + except KeyError: + ap_sill_heights = [] + try: + ap_constructions = apertures["constructions"] + except KeyError: + ap_constructions = [] + try: + ap_types = apertures["types"] + except KeyError: + ap_types = [] + for destination_face in destination_faces: + j = int(destination_face.identifier.split("_")[-1]) + try: + ap_count = ap_numbers[j] + except IndexError: + continue + if ap_count == 0: + LOGGER.warning("skipping aperture on %s", destination_face) + continue + try: + ap_width = ap_widths[j] + except IndexError: + ap_width = apertures.get("width", 1.2) + try: + ap_height = ap_heights[j] + except IndexError: + ap_height = apertures.get("height", 1.3) + try: + ap_sill_height = ap_sill_heights[j] + except IndexError: + ap_sill_height = apertures.get("sill_height", 1) + try: + ap_type = ap_types[j] + except IndexError: + ap_type = apertures.get("type", "aperture") + manager.fix_dim( + width = ap_width, + height = ap_height, + sill_height = ap_sill_height + ) + try: + ap_construction_name = ap_constructions[j] + except IndexError: + ap_construction_name = apertures.get("construction") + ap_construction=CONSTLIB.get(ap_construction_name) + manager.face = destination_face + manager.set_u_v_bounds() + manager.add_from_border( + construction=ap_construction, + count=ap_count, + aperture_type=ap_type + ) for building_name, building_metadata in GEOMETRY.items(): if not isinstance(building_metadata, dict): @@ -50,32 +123,55 @@ def prepare( if not isinstance(level_metadata, dict): continue if "walls" not in level_metadata: - LOGGER.error("no floors key for %s, skipping the level", level_name) + LOGGER.error("no walls key for %s, skipping the level", level_name) continue LOGGER.info("Generating %s", level_name) level_variables = get_variables(level_metadata) - wall_points: list[list[float]] = [] + if level_metadata.get("use_blocks_vars", 0) == 1: + level_variables = {**level_variables, **get_variables(BLOCKS)} + wall_points: list[list[float | str]] = [] for x in level_metadata["walls"]: if isinstance(x, str): if x in BLOCKS: wall_points = [*wall_points, *BLOCKS[x]] if isinstance(x, list): - wall_points = [*wall_points, x] + wall_points = [*wall_points, x[0:3]] walls = prepare(wall_points, variables=level_variables) - floors = [] - if "floors" in level_metadata: - LOGGER.info("custom floors for %s", level_name) - for x in level_metadata["floors"]: - if isinstance(x, str): - if x in BLOCKS: - floors.append(prepare(BLOCKS[x])) + surfaces: dict[str, list[list[Point3D]]] = {"floors": [], "roofs": []} + for key, surface in surfaces.items(): + if key in level_metadata: + pts: list[list[float | str]] = [] + LOGGER.info("custom %s for %s", key, level_name) + for x in level_metadata[key]: + if isinstance(x, str): + if x in BLOCKS: + surface.append( + prepare( + BLOCKS[x], + variables=get_variables(BLOCKS) + ) + ) + if isinstance(x, list): + pts.append(x) + if len(pts) > 0: + surface.append( + prepare( + pts, + variables=level_variables + ) + ) + height=level_metadata.get("height", common_height) + if "heights" in level_metadata: + height = level_metadata["heights"] level = complex_room( walls, - height=level_variables["height"], + height=height, identifier=level_name, - floors = floors if floors else None, - use_polyface = not floors + floors = surfaces["floors"], + roofs = surfaces["roofs"], + use_polyface = False, + remove_wall=level_metadata.get("remove_wall", []) ) building.append(level) building_dict[level_name] = level @@ -104,9 +200,14 @@ def prepare( continue constructions = level_metadata.get("constructions", {}) level_walls: list[Wall] = [] + level_roofs: list[RoofCeiling] = [] for face in site[building_name][level_name].faces: if isinstance(face.type, Wall): - construction_name = constructions.get("walls") + number = int(face.identifier.split("_")[-1]) + try: + construction_name = level_metadata["walls"][number][3] + except IndexError: + construction_name = constructions.get("walls") construction = CONSTLIB.get(construction_name) if construction is not None: face.properties.energy.construction = construction @@ -122,83 +223,40 @@ def prepare( if isinstance(face.type, RoofCeiling): construction_name = constructions.get("roofs") construction = CONSTLIB.get(construction_name) + if face.boundary_condition == Outdoors(): + level_roofs.append(face) if construction is None: continue if face.boundary_condition != Outdoors(): LOGGER.info("Setting roof construction %s on %s", construction_name, face) face.properties.energy.construction = construction - # now we can add apertures - apertures = level_metadata.get("apertures", {}) - if "numbers" not in apertures: - LOGGER.warning("NO APERTURE ON LEVEL %s", level_name) + + # now we can add apertures and vasistas using an aperture manager + apertures_keys = ["apertures", "vasistas"] + apm = ApertureManager(site[building_name][level_name]) + windows_doors = level_metadata.get("apertures", {}) + if "numbers" not in windows_doors: + LOGGER.warning("NO WINDOW OR DOOR ON LEVEL %s", level_name) else: - numbers = apertures["numbers"] - try: - widths = apertures["widths"] - except KeyError: - widths = [] - try: - heights = apertures["heights"] - except KeyError: - heights = [] - try: - sill_heights = apertures["sill_heights"] - except KeyError: - sill_heights = [] - try: - constructions = apertures["constructions"] - except KeyError: - constructions = [] - try: - aperture_types = apertures["types"] - except KeyError: - aperture_types = [] - for i, face in enumerate(level_walls): - try: - count = numbers[i] - except IndexError: - LOGGER.warning("aperture index error") - continue - if not count: - LOGGER.warning("skipping aperture on %s", face) - continue - apm = ApertureManager(site[building_name][level_name]) - try: - width = widths[i] - except IndexError: - width = apertures.get("width", 1.2) - try: - height = heights[i] - except IndexError: - height = apertures.get("height", 1.3) - try: - sill_height = sill_heights[i] - except IndexError: - sill_height = apertures.get("sill_height", 1) - try: - aperture_type = aperture_types[i] - except IndexError: - aperture_type = apertures.get("type", "aperture") - apm.fix_dim( - width = width, - height = height, - sill_height = sill_height - ) - try: - construction_name = constructions[i] - except IndexError: - construction_name = apertures.get("construction") - construction=CONSTLIB.get(construction_name) - apm.face = face - apm.set_u_v_bounds() - apm.add_from_border( - construction=construction, - count=count, - aperture_type=aperture_type - ) + dispatch_apertures( + apertures=windows_doors, + manager=apm, + destination_faces=level_walls + ) + vasistas = level_metadata.get("vasistas", {}) + if "numbers" not in vasistas: + LOGGER.warning("NO VASISTAS ON LEVEL %s", level_name) + else: + dispatch_apertures( + apertures=vasistas, + manager=apm, + destination_faces=level_roofs + ) # now we can add single elements if any elements = level_metadata.get("elements", {}) level_variables = get_variables(level_metadata) + if level_metadata.get("use_blocks_vars", 0) == 1: + level_variables = {**level_variables, **get_variables(BLOCKS)} for element_name, element_metadata in elements.items(): if "geometry" not in element_metadata: continue diff --git a/generate_hvac.py b/generate_hvac.py index c853f05..f4eb8bd 100644 --- a/generate_hvac.py +++ b/generate_hvac.py @@ -320,6 +320,7 @@ def basic_zone_sizing(zone_name: str): } USE_AIR = 0 +ground_temperature() for equipment_name in EQUIPMENTS: if PUMP in equipment_name: @@ -338,7 +339,6 @@ def basic_zone_sizing(zone_name: str): equipments[equipment_name] = pipe continue if BOREHOLE in equipment_name: - ground_temperature() borehole = vertical_geoexchanger(equipment_name) equipments[equipment_name] = borehole continue diff --git a/src/idfhub/common.py b/src/idfhub/common.py index 02285b3..3584a4d 100644 --- a/src/idfhub/common.py +++ b/src/idfhub/common.py @@ -109,8 +109,16 @@ def load_config(repo_root:str, file_name:str = "configuration.yml") -> dict: def get_variables(metadata: dict) -> dict: """return dict of variables""" variables = {} - variables["height"] = metadata.get("height", COMMON_HEIGHT) - variables["altitude"] = metadata.get("altitude", 0) + accepted_keys = [ + "height", + "altitude", + "d0","d1","d2","d3","d4","d5", + "h0","h1","h2","h3","h4","h5", + "z0" + ] + for key in accepted_keys: + if key in metadata: + variables[key] = metadata[key] return variables def eval_expr(expr, variables): diff --git a/src/idfhub/helpers/geometry.py b/src/idfhub/helpers/geometry.py index a02a802..335c4dd 100644 --- a/src/idfhub/helpers/geometry.py +++ b/src/idfhub/helpers/geometry.py @@ -57,38 +57,58 @@ def box_room( ) def create_walls( - pts:list[Point3D], - height: float, -) -> list[Face3D]: + *, + pts: list[Point3D], + height: float|list[float] = 3, + remove_wall: list[int] | None = None +) -> dict[str, Face3D]: """create walls from lower and upper points""" - up = Vector3D(0, 0, height) - pts_u = [pt.move(up) for pt in pts] + if remove_wall is None: + remove_wall = [] + if isinstance(height, list): + LOGGER.debug("having a list of heights %s", height) + if len(height) != len(pts): + pts_u = [pt.move(Vector3D(0,0,height[0])) for pt in pts] + else: + pts_u = [pt.move(Vector3D(0,0,height[i])) for i,pt in enumerate(pts)] + else: + up = Vector3D(0, 0, height) + pts_u = [pt.move(up) for pt in pts] i_inds = list(range(len(pts))) j_inds = i_inds[1:] + [0] - return [ - Face3D([ + if remove_wall: + LOGGER.info("removing wall %s", remove_wall) + return { + i: Face3D([ pts[i], pts[j_inds[i]], pts_u[j_inds[i]], pts_u[i] ]) - for i in i_inds - ] + for i in i_inds if i not in remove_wall + } def create_floors_roofs( + *, floors:list[list[Point3D]], - height: float -) -> tuple[list[Point3D], list[Point3D]]: + height: float = 3, + roofs:list[list[Point3D]]|None = None +) -> tuple[list[Face3D], list[Face3D]]: """create floors and roofs""" - up = Vector3D(0, 0, height) + if roofs is None: + roofs = [] all_floors: list[Face3D] = [] for floor in [Face3D(pts) for pts in floors]: if floor.normal.z > 0: LOGGER.debug("flipping to have a floor") all_floors.append(floor.flip()) else: - all_floors.append(floor.flip()) - all_up_pts = [[pt.move(up) for pt in pts] for pts in floors] + all_floors.append(floor) + if len(roofs) == 0: + up = Vector3D(0, 0, height) + all_up_pts = [[pt.move(up) for pt in pts] for pts in floors] + else: + all_up_pts = roofs all_roofs: list[Face3D] = [] for roof in [Face3D(pts) for pts in all_up_pts]: if roof.normal.z < 0: @@ -107,23 +127,41 @@ def create_floors_roofs( def complex_room( pts:list[Point3D], - height:float, + height:float|list[float], identifier:str, floors:list[list[Point3D]]|None = None, - use_polyface:bool = True + roofs:list[list[Point3D]]|None = None, + use_polyface:bool = True, + remove_wall:list[int]|None = None ) -> Room: """create a complex room""" + if floors is None: + floors = [] + if roofs is None: + roofs = [] + if remove_wall is None: + remove_wall = [] # on crée les murs d'après les glacis de points - walls = create_walls(pts, height) + walls = create_walls(pts=pts, height=height, remove_wall=remove_wall) # on crée sol(s) et plafond(s) - if not floors: - all_floors, all_roofs = create_floors_roofs([Face3D(pts)], height) + # si l'utilisateur fournit une liste de hauteurs, il doit définir le plafond dans le yaml + # s'il ne l'a pas fait, on produit un plafond plat + room_height = max(height) if isinstance(height, list) else height + if len(floors) == 0: + all_floors, all_roofs = create_floors_roofs( + floors=[Face3D(pts)], + height=room_height + ) else: - all_floors, all_roofs = create_floors_roofs(floors, height) + all_floors, all_roofs = create_floors_roofs( + floors=floors, + height=room_height, + roofs=roofs + ) if use_polyface: polyface = Polyface3D.from_faces( - [*all_floors, *walls, *all_roofs], + [*all_floors, *walls.values(), *all_roofs], tolerance = TOLERANCE ) output_room = Room.from_polyface3d( @@ -134,19 +172,19 @@ def complex_room( hb_faces = [] for i, f3d in enumerate(all_floors): hb = Face( - identifier=f'{identifier}_{FLOOR}{i}', + identifier=f'{identifier}_{FLOOR}_{i}', geometry=f3d ) hb_faces.append(hb) for i, f3d in enumerate(all_roofs): hb = Face( - identifier=f'{identifier}_{ROOF}{i}', + identifier=f'{identifier}_{ROOF}_{i}', geometry=f3d ) hb_faces.append(hb) - for i, f3d in enumerate(walls): + for key, f3d in walls.items(): hb = Face( - identifier=f'{identifier}_{WALL}{i}', + identifier=f'{identifier}_{WALL}_{key}', geometry=f3d ) hb_faces.append(hb) diff --git a/src/idfhub/helpers/material.py b/src/idfhub/helpers/material.py index f191aa5..02fdb1c 100644 --- a/src/idfhub/helpers/material.py +++ b/src/idfhub/helpers/material.py @@ -6,10 +6,49 @@ from honeybee_energy.construction.opaque import OpaqueConstruction from honeybee_energy.construction.window import WindowConstruction -osb_20mm = EnergyMaterial( - identifier='OSB_20mm', +from idfhub.common import GEOMETRY + +# constantes qui peuvent être utilisées dans le yml +# pour définir des épaisseurs custom +OSB = "osb" +GLASSWOOL_OLD = "glasswool_old" +GLASSWOOL_NEW = "glasswool_new" +GLASSWOOL_2000 = "glasswool_2000" +PAREMENT = "amiante_ciment" +CONCRETE = "concrete" +PLASTER = "plaster" +AIR_CAVITY = "air_cavity" +PARPAING = "parpaing" +CUT_STONE = "cut_stone" +OLD_MASONRY = "old_masonry" +LIME_PLASTER = "lime_plaster" +GYPSUM_BOARD = "gypsum_board" + +# épaisseurs par défaut +THICKNESS = { + OSB: 0.02, # 2 cm + GLASSWOOL_OLD: 0.1, + GLASSWOOL_NEW: 0.12, + GLASSWOOL_2000: 0.1, + PAREMENT: 0.02, + CONCRETE: 0.2, + PLASTER: 0.02, + AIR_CAVITY: 0.05, + PARPAING: 0.1, + CUT_STONE: 0.6, + OLD_MASONRY: 0.4, + LIME_PLASTER: 0.02, + GYPSUM_BOARD: 0.0127, +} + +def thickness(name): + """fetch the thickness from the yml conf""" + return GEOMETRY.get(name, THICKNESS[name]) + +osb = EnergyMaterial( + identifier=OSB, roughness='MediumRough', - thickness=0.020, # 2 cm + thickness=thickness(OSB), conductivity=0.13, # W/m·K density=600, # kg/m3 specific_heat=1600 # J/kg·K @@ -17,46 +56,46 @@ # Laine de verre ancienne (années 70–90) glasswool_old = EnergyMaterial( - identifier='GlassWool_Old', + identifier=GLASSWOOL_OLD, roughness='Rough', - thickness=0.100, + thickness=thickness(GLASSWOOL_OLD), conductivity=0.050, density=18, specific_heat=840 ) # laine de verre récente glasswool_new = EnergyMaterial( - identifier='GlassWool_New', + identifier=GLASSWOOL_NEW, roughness='MediumRough', - thickness=0.120, + thickness=thickness(GLASSWOOL_NEW), conductivity=0.032, density=12, specific_heat=840 ) # laine de verre des années 2000 glasswool_2000 = EnergyMaterial( - identifier='GlassWool_2000', + identifier=GLASSWOOL_2000, roughness='MediumRough', - thickness=0.100, + thickness=thickness(GLASSWOOL_2000), conductivity=0.040, density=15, specific_heat=840 ) # parement extérieur en amiante parement = EnergyMaterial( - identifier='AmianteCiment_20mm', + identifier=PAREMENT, roughness='Rough', - thickness=0.020, + thickness=thickness(PAREMENT), conductivity=0.35, density=1600, specific_heat=900 ) # plancher en béton -concrete_200mm = EnergyMaterial( - identifier='Concrete_200mm', +concrete = EnergyMaterial( + identifier=CONCRETE, roughness='MediumRough', - thickness=0.20, + thickness=thickness(CONCRETE), conductivity=1.75, density=2300, specific_heat=900 @@ -64,29 +103,93 @@ # éléments pour un mur en parpaing creux plaster = EnergyMaterial( - identifier='Plaster_2cm', - thickness=0.02, + identifier=PLASTER, + thickness=thickness(PLASTER), conductivity=0.7, density=900, specific_heat=1000 ) air_cavity = EnergyMaterial( - identifier='AirCavity_5cm', - thickness=0.05, + identifier=AIR_CAVITY, + thickness=thickness(AIR_CAVITY), conductivity=0.025, # conductivité de l’air density=1.2, specific_heat=1005 ) parpaing = EnergyMaterial( - identifier='Parpaing_10cm', - thickness=0.10, + identifier=PARPAING, + thickness=thickness(PARPAING), conductivity=0.72, density=800, specific_heat=840 ) +# pierre de taille +cut_stone = EnergyMaterial( + identifier=CUT_STONE, + roughness="Rough", + thickness=thickness(CUT_STONE), + conductivity=1.5, + density=2100, + specific_heat=900, + thermal_absorptance=0.9, + solar_absorptance=0.6, + visible_absorptance=0.6 +) + +# maçonnerie ancienne +old_masonry = EnergyMaterial( + identifier=OLD_MASONRY, + roughness="Rough", + thickness=thickness(OLD_MASONRY), + conductivity=1, + density=1700, + specific_heat=900, + thermal_absorptance=0.9, + solar_absorptance=0.7, + visible_absorptance=0.7 +) + +# enduit à la chaux +lime_plaster = EnergyMaterial( + identifier=LIME_PLASTER, + roughness="Smooth", + thickness=thickness(LIME_PLASTER), + conductivity=0.80, + density=1700, + specific_heat=1000, + thermal_absorptance=0.90, + solar_absorptance=0.30, + visible_absorptance=0.30 +) + +# plaque au platre +gypsum_board = EnergyMaterial( + identifier="gypsum_board", + thickness=0.0127, + conductivity=0.16, + density=800, + specific_heat=1090, + thermal_absorptance=0.9, + solar_absorptance=0.5, + visible_absorptance=0.5 +) + +# tuile +roman_tile = EnergyMaterial( + identifier="roman_tile", + roughness="MediumRough", + thickness=0.02, + conductivity=0.85, + density=1900, + specific_heat=840, + thermal_absorptance=0.90, + solar_absorptance=0.75, + visible_absorptance=0.75 +) + # verre glass = EnergyWindowMaterialGlazing( @@ -99,7 +202,7 @@ emissivity_back=0.84 ) -glass_2cm = EnergyWindowMaterialGlazing( +thick_glass_for_wall = EnergyWindowMaterialGlazing( identifier="thick_glass_for_wall", thickness=0.02, # 2 cm conductivity=1.0, # verre @@ -110,12 +213,30 @@ ) gap = EnergyWindowMaterialGas( - identifier="air_gap_16mm", + identifier="air_gap", gas_type="Air", thickness=0.016 ) # compositions +# outside layer always first +townhouse_basement = OpaqueConstruction( + identifier="townhouse_basement", + materials=[ + cut_stone + ] +) + +recent_light_renovation = OpaqueConstruction( + identifier="recent_light_renovation", + materials=[ + osb, + glasswool_new, + gypsum_board + ] +) + + wall_parpaing = OpaqueConstruction( identifier='wall_parpaing', materials=[ @@ -127,28 +248,28 @@ ) wall_osb = OpaqueConstruction( - identifier='Wall_osb', + identifier='wall_osb', materials=[ - osb_20mm, + parement, glasswool_old, - parement + osb ] ) floor_internal = OpaqueConstruction( - identifier='Floor_Internal', + identifier='floor_internal', materials=[ - concrete_200mm + concrete ] ) window_pvc = WindowConstruction( - identifier="Fenetre_PVC_Double", + identifier="fenetre_pvc_double", materials=[glass, gap, glass] ) simple_glass_wall = WindowConstruction( - identifier="paroi murale simple vitrage", - materials=[glass_2cm] + identifier="mur_simple_vitrage", + materials=[thick_glass_for_wall] ) diff --git a/src/idfhub/helpers/materials.xlsx b/src/idfhub/helpers/materials.xlsx new file mode 100644 index 0000000..a6c9588 Binary files /dev/null and b/src/idfhub/helpers/materials.xlsx differ diff --git a/src/idfhub/helpers/matlib.py b/src/idfhub/helpers/matlib.py index 16988cf..6df3d6f 100644 --- a/src/idfhub/helpers/matlib.py +++ b/src/idfhub/helpers/matlib.py @@ -3,7 +3,8 @@ from .material import ( wall_osb, wall_parpaing, floor_internal, - window_pvc, simple_glass_wall + window_pvc, simple_glass_wall, + townhouse_basement, recent_light_renovation ) CONSTLIB = { @@ -11,5 +12,7 @@ "wall_parpaing": wall_parpaing, "floor_internal": floor_internal, "window_pvc": window_pvc, - "simple_glass_wall": simple_glass_wall + "simple_glass_wall": simple_glass_wall, + "townhouse_basement": townhouse_basement, + "recent_light_renovation": recent_light_renovation }