Source code for viiapackage.geometry.viia_create_cavity_walls

### ===================================================================================================================
###   Functionality for cavity walls
### ===================================================================================================================
# Copyright ©VIIA 2024

### ===================================================================================================================
###    1. Import modules
### ===================================================================================================================

# General imports
from typing import List, Union, Optional
import math
import warnings
import numpy as np

# References for functions and classes in the DataFusr py-base package
from datafusr_py_base.deprecation import deprecation_input_error

# References for functions and classes in the rhdhv_fem package
from rhdhv_fem.fem_math import fem_purge_points, fem_distance_coordinates, fem_unit_vector, fem_compare_values, \
    fem_vector_2_points, fem_point_to_plane, fem_plane_plane_intersection, fem_longest_distance, \
    fem_distance_point_to_line, fem_point_on_line, fem_parallel_vectors, fem_distance_point_to_plane, \
    fem_points_in_line, fem_smaller, fem_compare_coordinates, fem_parallel_planes
from rhdhv_fem.shapes import Wall, Surfaces
from rhdhv_fem.connections import Spring
from rhdhv_fem.shape_geometries import Node, Line
from rhdhv_fem.fem_shape_geometries import fem_create_line

# References for functions and classes in the viiaPackage
from viiapackage.geometry.viia_structural_type import StructuralType


### ===================================================================================================================
###    2. Function viia_create_cavity_wall_ties
### ===================================================================================================================

[docs]@deprecation_input_error( package='viiapackage', since='67.0', old_arg_types=[str, (list, Wall, str), float, float, str, bool, float, float, bool], old_arg_names=[ 'project', 'wall', 'cavity', 'outer_wall_thickness', 'outer_wall_material', 'flip', 'edge_distance', 'wall_tie_distance', 'no_ties']) def viia_create_cavity_wall_ties( project: 'ViiaProject', inner_leaf: Wall, outer_leaf: Wall, edge_distance: float = 0.25, ties_per_m2: Optional[float] = None, wall_tie_distance: Optional[float] = None, snap_to_openings: bool = True, snap_to_contour: bool = True, offset_base: float = 0, offset_vertical: float = 0, lines_to_snap: Optional[List[Line]] = None): """ Function to create cavity wall ties between two walls. Function generates a grid of evenly distributed points over the inner leaf. Points in openings are disregarded. Points close to opening lines are snapped by default if they are within the given edge distance. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - inner_leaf (Wall): Wall object for the inner leaf. - outer_leaf (Wall): Wall object for the outer leaf. - edge_distance (float): Distance from which to snap points to openings or contour, if desired. Default is 0.25m - ties_per_m2 (float): Number of ties per m2. Default value is 4 ties/m2. - wall_tie_distance (float): Distance between ties, in [m]. - snap_to_openings (bool): Toggle for snapping to opening lines. - snap_to_contour (bool): Toggle for snapping to contour lines. - offset_base (float): Optional offset of the ties in the direction of the base of the wall in [m]. Default value 0. - offset_vertical (float): Optional offset of the ties in the vertical direction in [m]. Default value 0. - lines_to_snap (list): List of Line object to snap the ties nearby. Output: - list of wall tie objects, created in the Spring class """ if not inner_leaf.contour.is_vertical(): raise NotImplementedError( f"ERROR: Creation of cavity wall ties is only implemented for vertical walls. Wall {inner_leaf} is not " f"vertical, but has normal vector {inner_leaf.normal_vector()}.") if not fem_parallel_vectors(inner_leaf.normal_vector(), outer_leaf.normal_vector()): raise ValueError(f"ERROR: Inner leaf and outer leaf are not parallel ({inner_leaf.name}, {outer_leaf.name})") # Set default value for ties per m2 if no input is provided if ties_per_m2 is None and wall_tie_distance is None: ties_per_m2 = 4 # Convert ties per m2 to wall_tie_distance if ties_per_m2: if wall_tie_distance: raise AttributeError( f"ERROR: Input for both wall_tie_distance and ties_per_m2 is not allowed. Choose one of two.") wall_tie_distance = 1 / math.sqrt(ties_per_m2) # Find wall height sorted_points = sorted(inner_leaf.contour.get_points(), key=lambda x: x[2]) wall_bottom_z, wall_top_z = sorted_points[0][2], sorted_points[-1][2] wall_height = wall_top_z - wall_bottom_z # Find horizontal direction vector of wall plane_bottom_wall = [[0, 0, wall_bottom_z], [1, 0, wall_bottom_z], [1, 1, wall_bottom_z]] projected_line = [fem_point_to_plane(pt, plane_bottom_wall) for pt in inner_leaf.contour.get_points()] extremes = fem_longest_distance(projected_line) base_vector = np.array(extremes[1]) - np.array(extremes[0]) base_direction = base_vector / np.linalg.norm(base_vector) # Set reference point ref_point = np.array(extremes[0]) # Find vertical direction vector of wall vertical_direction = np.array([0, 0, 1]) vertical_vector = (ref_point + wall_height * vertical_direction) - ref_point # Compute amount of wall ties in each direction nr_ties_base = math.ceil(np.linalg.norm(base_vector) / wall_tie_distance) nr_ties_vertical = math.ceil(np.linalg.norm(vertical_vector) / wall_tie_distance) # Compute centroid to start the grid mid_point = np.array(inner_leaf.contour.get_centroid()) # Start point of grid, including offsets start_point = mid_point + base_direction * offset_base + vertical_direction * offset_vertical # Set array of distributed points along base base_points = np.linspace( start_point - nr_ties_base * wall_tie_distance * base_direction, start_point + nr_ties_base * wall_tie_distance * base_direction, num=2 * nr_ties_base + 1) # Set array of distributed points along vertical vertical_points = np.linspace( start_point - nr_ties_vertical * wall_tie_distance * vertical_direction, start_point + nr_ties_vertical * wall_tie_distance * vertical_direction, num=2 * nr_ties_vertical + 1) # Unzip coordinates along axes base_x, base_y, base_z = zip(*base_points) vert_x, vert_y, vert_z = zip(*vertical_points) # Loop over xy-coordinate pairs and add all z-coordinates wall_tie_coordinates_inner = [] for xy in zip(base_x, base_y): for z in vert_z: coordinate = [*xy, z] # Check if coordinate is within shape and not in opening if inner_leaf.is_point_in_shape(coordinate): # Add coordinate to list wall_tie_coordinates_inner.append(coordinate) # Snap coordinates to openings and contours if desired if snap_to_openings or snap_to_contour: wall_tie_coordinates_inner = viia_snap_coordinates_to_surface( surface=inner_leaf, coordinates=wall_tie_coordinates_inner, minimum_distance=edge_distance, snap_to_openings=snap_to_openings, snap_to_contour=snap_to_contour, lines_to_snap=lines_to_snap) # Project coordinates to outer leaf wall_tie_coordinates_outer = [fem_point_to_plane(coordinate, outer_leaf.contour.get_points()) for coordinate in wall_tie_coordinates_inner] # Create the wall tie objects in the Spring class cavity_wall_ties = [] for inner_leaf_coordinate, outer_leaf_coordinate in zip(wall_tie_coordinates_inner, wall_tie_coordinates_outer): # Check if any of the outer points are outside the contour of the outer leaf. This can happen due to # connecting the outer leafs in the corners if not outer_leaf.is_point_in_shape(outer_leaf_coordinate): project.write_log( f"WARNING: Point {outer_leaf_coordinate} is not in shape {outer_leaf}, wall tie is not created at this " f"location. This can be caused by a shortened outer leaf due to connecting.") continue cavity_wall_ties.append( project.viia_create_cavity_wall_tie(inner_leaf, outer_leaf, inner_leaf_coordinate, outer_leaf_coordinate)) # Check if number of ties corresponds with given amount, after adjustments and snapping if ties_per_m2: nr_ties_expected = inner_leaf.get_area() * ties_per_m2 else: nr_ties_expected = inner_leaf.get_area() * (1 / wall_tie_distance) ** 2 if len(cavity_wall_ties) / nr_ties_expected > 1.05: project.write_log( f"WARNING: Amount of ties on wall {inner_leaf.id} exceeds the required amount ({len(cavity_wall_ties)} > " f"{nr_ties_expected:.1f}). Use offsets to adjust, or remove objects to avoid overestimating the capacity " f"of the wall.") return cavity_wall_ties
### =================================================================================================================== ### 3. Function viia_create_outer_leaf ### ===================================================================================================================
[docs]def viia_create_outer_leaf( project: 'ViiaProject', wall: Union[Wall, str], cavity: float, outer_wall_thickness: Optional[float] = None, outer_wall_material: Optional[str] = None, flip: bool = False) -> Wall: """ Function to create an outer leaf for a specific wall. All points defining the wall object are translated outward so the gap between them, including thickness of the wall, will be equal to cavity. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - wall (Wall, str): Wall object or string describing the inner leaf. For instance 'wall_18'. - cavity (float): Cavity distance between the walls. - outer_wall_thickness (float): Optional thickness of the outer wall in [m]. If nothing is provided, thickness will be equal to the given wall. - outer_wall_material (str): Optional material of the outer wall. If nothing is provided, material will be equal to the given wall. - flip (bool): Toggle to flip the direction of creation of the wall. By default, the outer wall is created in the direction of the normal vector of the wall. Output: - Object reference to the newly created outer wall in the Wall class. """ # Obtain wall object if string is given if isinstance(wall, str): try: wall_id = wall.split('_')[1] except IndexError: raise ValueError(f"ERROR: String input for wall name is expected in the format 'wall_12', not {wall}.") wall = project.viia_get('walls', id=wall_id) if not wall: raise LookupError(f"ERROR: Wall with id {wall_id} not found in collections.") # Check if wall is vertical, function only available for vertical walls if not wall.is_vertical(): raise ValueError( f"ERROR: The inner leaf of the cavity wall should be vertical for the cavity wall functionality. Wall " f"{wall.name} is not vertical.") # Set thickness and material of outer leaf if not given inner_wall_thickness = wall.geometry.geometry_model.thickness outer_wall_thickness = inner_wall_thickness if not outer_wall_thickness else outer_wall_thickness outer_wall_material = wall.material.name if not outer_wall_material else outer_wall_material # Determine center to center distance ctc = inner_wall_thickness / 2 + cavity + outer_wall_thickness / 2 # Get normal vector of inner leaf, flip if desired normal_vector = np.array(wall.normal_vector()) normal_vector = -1 * normal_vector if flip else normal_vector # Get translation vector translation_vector = ctc * normal_vector # Compute the contour of the outer leaf inner_leaf_contour = np.array(wall.contour.get_points()) translation_array = np.ones_like(inner_leaf_contour) * translation_vector outer_leaf_contour = inner_leaf_contour + translation_array # Compute opening points on outer leaf outer_leaf_openings = np.array([]) if wall.openings: outer_leaf_openings = [] for opening in wall.openings: inner_leaf_opening = np.array(opening.get_points()) translation_array = np.ones_like(inner_leaf_opening) * translation_vector outer_leaf_openings.append((inner_leaf_opening + translation_array).tolist()) # Create the wall object outer_leaf = project.viia_create_wall( wall.layer, outer_wall_material, int(outer_wall_thickness * 1000), [outer_leaf_contour.tolist(), *outer_leaf_openings], cavity_inner_wall_id=wall.id) # Set the structural type to NSCE by default outer_leaf.add_meta_data({"structural_type": StructuralType.NSCE}) # Manual adjustment of name is required to follow viia naming convention outer_leaf.name = '-'.join(outer_leaf.name.split('-')[:-1]) + f"-{wall.id}B" return outer_leaf
### =================================================================================================================== ### 4. Function viia_create_dummy_plate_between_walls ### ===================================================================================================================
[docs]def viia_create_dummy_plate_between_walls( project: 'ViiaProject', wall_1: Wall, wall_2: Wall, material: str = 'LIN-DUMMY-PLATE', geometry: float = 100, z_coordinate: Optional[float] = None) -> Wall: """ Function to create a dummy plate between two walls. This can be used for cavity walls for instance. The local x-axis of the plate is set parallel to the wall. The dummy plate is created in the Floor class. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - wall_1 (Wall): Object reference of the first wall. - wall_2 (Wall): Object reference to the second wall. - material (str): Optional material to apply to the dummy plate, default is 'LIN-DUMMY-PLATE'. - geometry (float): Optional geometry to apply to the plate, default is 100mm. - z-coordinate (float): Optional z-coordinate for the plate, default is bottom of the walls Output: - Object reference to the newly created dummy plate in the Floor class. """ # Check if walls are on the same layer if not wall_1.layer == wall_2.layer: raise ValueError(f"ERROR: Dummy plate cannot be created between walls on different layers.") # Assemble bottom line points of walls: bottom_line_points_1 = [] for line in wall_1.get_bottom_edges(include_openings=True): for point in line.get_points(): bottom_line_points_1.append(point) bottom_line_points_1 = fem_longest_distance(bottom_line_points_1) bottom_line_points_2 = [] for line in wall_2.get_bottom_edges(include_openings=True): for point in line.get_points(): bottom_line_points_2.append(point) bottom_line_points_2 = fem_longest_distance(bottom_line_points_2) # Form the contour plate_contour = bottom_line_points_1 + bottom_line_points_2 # Replace z-coordinate by user input, if given if z_coordinate: plate_contour = [[point[0], point[1], z_coordinate] for point in plate_contour] # Find unit vector of direction of walls direction_wall_1 = fem_unit_vector(fem_vector_2_points(*bottom_line_points_1)) direction_wall_2 = fem_unit_vector(fem_vector_2_points(*bottom_line_points_2)) if not fem_parallel_vectors(direction_wall_1, direction_wall_2): raise ValueError(f"ERROR: Dummy plate function can only be used on walls that are parallel.") return project.viia_create_floor(wall_1.layer, material, geometry, [plate_contour], element_x_axis=direction_wall_1)
### =================================================================================================================== ### 5. Functions for snapping ### ===================================================================================================================
[docs]def viia_snap_coordinates_to_surface( surface: Surfaces, coordinates: List[List[float]], minimum_distance: float, snap_to_openings: bool = True, snap_to_contour: bool = True, lines_to_snap: List[Line] = None, snap_to_point: bool = True) \ -> List[List[float]]: """ This function can snap coordinates to contour lines or opening lines of a surface. This can be used while creating cavity wall ties. Coordinates within the prescribed minimum distance of lines are automatically snapped to the closest point on that line. Input: - surface (Surfaces): Surface object to snap the coordinates for. - coordinates (list): List of 3D coordinate pairs to check for snapping. - minimum_distance (float): Coordinates within this distance are snapped, in [m]. - snap_to_openings (bool): Select for snapping to openings. Default value True. - snap_to_contour (bool): Select for snapping to the contour. Default value True. - lines_to_snap (list): List of Line object to snap the ties nearby. Default value True. - snap_to_point (bool): Select for snapping to inner points. Default value True. Output: - Returns list of coordinates as a list of floats. Coordinates within the minimum distance of the lines are snapped to that line. Coordinates that are not within the minimum distance remain unchanged. """ # Collect new coordinates new_coordinates = [] # Collect the snapping lines if not lines_to_snap: lines_to_snap = [] if surface.internal_lines: lines_to_snap = lines_to_snap.extend(surface.internal_lines) wall_nodes = [] if snap_to_point: all_nodes_building = surface.project.collections.shape_nodes for node_building in all_nodes_building: surface.add_node(node_building, in_shape_check=True) wall_nodes = surface.get_nodes() # Loop over the coordinates for coordinate in coordinates: # Snap to internal lines or given lines snapped_to_line = False if lines_to_snap: for line_to_snap in lines_to_snap: snapped_coordinate = viia_snap_coordinate_to_line( line=line_to_snap, coordinate=coordinate, minimum_distance=minimum_distance) if snapped_coordinate: new_coordinates.append(snapped_coordinate) snapped_to_line = True # Break out from line findings break # Move to the next coordinate if snapped_to_line: continue # Snap point to opening if desired snapped_to_opening = False if snap_to_openings: # If point is closer to opening line than prescribed edge distance, project it on the line if surface.openings: for opening in surface.openings: for line in opening.get_lines(): snapped_coordinate = viia_snap_coordinate_to_line(line, coordinate, minimum_distance) if snapped_coordinate: new_coordinates.append(snapped_coordinate) snapped_to_opening = True break # Break if already snapped if snapped_to_opening: break # Continue to next coordinate if already snapped to opening if snapped_to_opening: continue # Snap point to contour if desired snapped_to_contour = False if snap_to_contour: for contour_line in surface.contour.get_lines(): snapped_coordinate = viia_snap_coordinate_to_line(contour_line, coordinate, minimum_distance) if snapped_coordinate: new_coordinates.append(snapped_coordinate) snapped_to_contour = True break # Snap to inner point on a line if desired snapped_to_point = False if snap_to_point: for node in wall_nodes: node_coordinates = node.coordinates if fem_smaller(fem_distance_coordinates(node_coordinates, coordinate), minimum_distance): snapped_coordinate = node_coordinates new_coordinates.append(snapped_coordinate) snapped_to_point = True break # Add original coordinate if snapping is not required if not any([snapped_to_line, snapped_to_opening, snapped_to_contour, snapped_to_point]): new_coordinates.append(coordinate) purged_new_coordinates = fem_purge_points(new_coordinates) return purged_new_coordinates
[docs]def viia_snap_coordinate_to_line(line: Line, coordinate: List[float], minimum_distance: float) -> List[float]: """ Function to snap a coordinate to a line. If the coordinate is closer to the line than the given minimum_distance, the coordinate is snapped to the closest point on the line. Input: - line (Line): Object reference of Line shape-geometry. - coordinate (list): List of three floats describing the coordinate. - minimum_distance (float): Distance within snapping is performed, in [m]. Output: - Snapped coordinate as list of floats. If snapping is not required, nothing is returned. """ # Find distance from coordinate to line (uses infinite line) distance, vector = fem_distance_point_to_line(coordinate, line.get_points()) if fem_smaller(distance, minimum_distance) and not fem_compare_values(distance, 0): intersection = list(np.array(coordinate) + np.array(vector)) # Add the projection instead of the original coordinate if projection is between # bounds of the line. if fem_point_on_line(intersection, line.get_points()): # Check if the projection is close to one of the line points for line_point in line.get_points(): if fem_smaller(fem_distance_coordinates(intersection, line_point), minimum_distance): return line_point return intersection # Check if the point itself is close to one of the line points for line_point in line.get_points(): if fem_smaller(fem_distance_coordinates(coordinate, line_point), minimum_distance): return line_point
### =================================================================================================================== ### 6. viia_connect_cavity_walls ### ===================================================================================================================
[docs]def viia_connect_cavity_walls( project: 'ViiaProject', cavity_wall_1: Wall, cavity_wall_2: Wall, adjust_second: bool = True): """ This function connects two vertical and rectangular (cavity) walls by adjusting the contours of both walls such that the intersection line of the wall planes is included in both wall contours. If parallel walls are given, their contours remain unchanged. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - cavity_wall_1 (obj): Wall to be connected to cavity_wall_2. - cavity_wall_2 (obj): Wall to be connected to cavity_wall_1. - adjust_second (bool): If is true cavity_wall_2 will also be changed. If false cavity_wall_2 will remain unchanged, this can be needed at specific T-sections. Output: - The (cavity) walls are connected, nothing is returned. """ # Calculate the intersection line of the wall planes intersection_line = fem_plane_plane_intersection(cavity_wall_1.get_points(), cavity_wall_2.get_points()) if intersection_line is None: return None else: intersection_point, intersection_direction = intersection_line[0], intersection_line[1] pairs = [[cavity_wall_1, cavity_wall_2]] if adjust_second: pairs = [[cavity_wall_1, cavity_wall_2], [cavity_wall_2, cavity_wall_1]] for cavity_walls in pairs: adjust_shape = cavity_walls[0] # shape to adjust target_shape_points = cavity_walls[1].contour.get_points() # Get vertical lines that are closest to intersection vertical_lines = adjust_shape.contour.get_vertical() distances = [fem_distance_coordinates(vertical_line.get_center_point(), intersection_point) for vertical_line in vertical_lines] dis_lin = zip(vertical_lines, distances) # make list of list with lines and distance closest_line = sorted(dis_lin, key=lambda x: x[1])[0][0] # get line with the smallest distance closest_lines = [ line for line in vertical_lines if fem_points_in_line(closest_line.get_points()+line.get_points())] # Get nodes that need to be updated closest_line_nodes = list(set([node for line in closest_lines for node in line.get_nodes()])) # Get direction in which the nodes should be moved horizontal_line_direction = adjust_shape.contour.get_horizontal()[0].get_direction().vector # Make list with updated contour points new_contour_points = [] first_node = True for node in adjust_shape.contour.get_nodes(): if node in closest_line_nodes: # Check if the other line (not the intersecting line) is perpendicular, else add point add_original = False for line in node.shape_geometries: if not isinstance(line, Line): continue if line not in adjust_shape.contour.lines: continue if line.is_vertical(): continue if not line.is_horizontal(): # Original node should be added also add_original = True # If add original is required it should be before the new one if it is first if first_node and add_original: if not (new_contour_points and fem_compare_coordinates( coordinate1=node.coordinates, coordinate2=new_contour_points[-1], precision=project.check_precision)): new_contour_points.append(node.coordinates) # Project node on target plane and add to new contour point = fem_point_to_plane( point=node.coordinates, plane=target_shape_points, direction=horizontal_line_direction, return_intersection=True) if not (new_contour_points and fem_compare_coordinates( coordinate1=point, coordinate2=new_contour_points[-1], precision=project.check_precision)): new_contour_points.append(point) # If add original is required it should be after the new one if it is last if not first_node and add_original: if not (new_contour_points and fem_compare_coordinates( coordinate1=node.coordinates, coordinate2=new_contour_points[-1], precision=project.check_precision)): new_contour_points.append(node.coordinates) # Set first node to False in the first loop if first_node: first_node = False else: # In case multiple intersections, due to openings first_node = True else: if not (new_contour_points and fem_compare_coordinates( coordinate1=node.coordinates, coordinate2=new_contour_points[-1], precision=project.check_precision)): new_contour_points.append(node.coordinates) first_node = True # Update contour adjust_shape.contour = project.create_polyline(new_contour_points)
### =================================================================================================================== ### 7. viia_move_cavity_wall_tie ### ===================================================================================================================
[docs]def viia_move_cavity_wall_tie(project: 'ViiaProject', cavity_wall_tie: Spring, new_coordinates: List[float]) -> Spring: """ Function to move a cavity wall tie object to a new specified coordinate on the inner leaf. A vector is constructed from the old coordinates of the node on the source connecting shape to the new coordinates. The node on the target connecting shape is moved along this same vector. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - cavity_wall_tie (obj): Object reference to the cavity wall tie that needs to be moved. - new_coordinates (list of 3 floats): New coordinates of the wall tie object. The new position should be provided on the inner leaf. Output: - Spring object and two internal points on the walls to which it connects are moved accordingly. The updated spring object is also returned. """ def _remove_connecting_shapes(wall_tie_node: Node): """ Removes the node from any connected shape, except the shapes the wall tie is attached to.""" for shape in wall_tie_node.get_shapes(): if shape is cavity_wall_tie.connecting_shapes['source_connecting_shape'] or \ shape is cavity_wall_tie.connecting_shapes['target_connecting_shape']: continue if shape.internal_points and wall_tie_node in shape.internal_points: shape.remove_internal_point(node=wall_tie_node) elif wall_tie_node in shape.contour.get_nodes(): shape.contour.remove_node(node=wall_tie_node) # Determine source and target nodes and shapes and save old coordinates node_source = cavity_wall_tie.connecting_shapes['source_shape_geometry'] wall_source = cavity_wall_tie.connecting_shapes['source_connecting_shape'] node_target = cavity_wall_tie.connecting_shapes['target_shape_geometry'] wall_target = cavity_wall_tie.connecting_shapes['target_connecting_shape'] old_coordinates = node_source.coordinates # Determine vector if not given vector = np.array(new_coordinates) - np.array(old_coordinates) # Determine new locations of nodes new_node_source = np.array(node_source.coordinates) + vector new_node_target = np.array(node_target.coordinates) + vector # Check if modified coordinates are within shapes for node, wall in zip([new_node_source, new_node_target], [wall_source, wall_target]): if not wall.is_point_in_shape(node.tolist()): raise ValueError( f"ERROR: Point {node.tolist()} is not in {wall.name}. Cannot move tie {cavity_wall_tie.name} to new " f"coordinates {new_coordinates}.") # Remove any connecting shapes to the node (except for the connecting wall) _remove_connecting_shapes(wall_tie_node=node_source) _remove_connecting_shapes(wall_tie_node=node_target) # Move the nodes node_source.move(new_node_source.tolist()) node_target.move(new_node_target.tolist()) # Notification for the user project.write_log( f"The wall tie {cavity_wall_tie.name} is successfully moved from {old_coordinates} to {new_coordinates}.") return cavity_wall_tie
### =================================================================================================================== ### 8. Remove cavity wall tie ### ===================================================================================================================
[docs]def viia_remove_cavity_wall_tie(project: 'ViiaProject', tie: Spring): """ This function removes a cavity wall tie from the project. The corresponding internal points on the inner and outer leaf will also be removed. If these nodes are not used by other shapes, they will also be removed. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - tie (obj): Object reference to the spring describing the wall tie. Output: - Returns the spring object will be deleted from the project. The shape geometries to which it connects will also be removed if they are not used by other shapes. """ conn_shapes = tie.connecting_shapes source_shape, target_shape = conn_shapes['source_connecting_shape'], conn_shapes['target_connecting_shape'] source_node, target_node = conn_shapes['source_shape_geometry'], conn_shapes['target_shape_geometry'] # Remove spring object. tie.remove_connection() # See if any shape geometries need to be removed. for shape, node in zip([source_shape, target_shape], [source_node, target_node]): # Check types if not isinstance(shape, Wall): raise TypeError( f"ERROR: viia_remove_cavity_wall_tie was expecting a Wall as connecting shape for tie {tie.name}, " f"not {type(shape)}.") if not isinstance(node, Node): raise TypeError( f"ERROR: viia_remove_cavity_wall_tie was expecting a Node as shape geometry for tie {tie.name}, not " f"{type(node)}.") # Remove internal point from wall object if this wall is the only shape it connects to. if len(node.get_usages_shapes()) == 1 and len(node.get_usages_shape_geometries()) == 0: shape.remove_internal_point(node) # Remove the node, it is checked automatically if it's still in use by other shape geometries node.remove_shape_geometry()
### =================================================================================================================== ### 9. Get functions for cavity walls ### ===================================================================================================================
[docs]def viia_get_total_cavity_wall_width(project: 'ViiaProject', leaf_1: Wall, leaf_2: Wall) -> float: """ This function calculates the total width of the cavity wall. This is the thickness of the inner leaf plus the cavity plus the thickness of the outer leaf. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - leaf_1 (Wall): One leaf of the cavity wall - leaf_2 (Wall): The other leaf of the cavity wall Output: - Returns the total width of the cavity wall, in [m]. """ if not abs(leaf_1.id - leaf_2.id) == 9000: warnings.warn( f"WARNING: Wall '{leaf_1.name}' doesn't seem to form a cavity wall with '{leaf_2.name}'. Total width of " f"the wall may not be correct.") # Determine thicknesses of both leafs thickness_leaf_1 = leaf_1.geometry.geometry_model.thickness thickness_leaf_2 = leaf_2.geometry.geometry_model.thickness # Determine ctc distance between leafs ctc_distance = viia_get_ctc_cavity_walls(project=project, leaf_1=leaf_1, leaf_2=leaf_2) # Determine and return the total width return ctc_distance + 0.5 * thickness_leaf_1 + 0.5 * thickness_leaf_2
[docs]def viia_get_outer_leaf(project: 'ViiaProject', inner_leaf: Wall) -> Optional[Wall]: """ This function will collect the outer leaf for a certain inner leaf. The cavity wall consists of an inner leaf with an ID, the corresponding outer lead has an ID + 9000. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - inner_leaf (obj): Object reference of the inner leaf of the cavity wall. Output: - Returns the outer leaf of the cavity wall, if it could be found. """ return project.viia_get_shape(collection_name='walls', _id=inner_leaf.id + 9000)
[docs]def viia_get_ctc_cavity_walls(project: 'ViiaProject', leaf_1: Wall, leaf_2: Wall) -> float: """ This function calculates the center-to-center distance of the inner and outer leaf of the cavity wall in the model. This is half the thickness of the inner leaf plus the cavity plus half the thickness of the outer leaf. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - leaf_1 (Wall): One leaf of the cavity wall - leaf_2 (Wall): The other leaf of the cavity wall Output: - Returns the total width of the cavity wall, in [m]. """ # Check if planes are parallel if not fem_parallel_planes( plane1=leaf_1.contour.get_points(), plane2=leaf_2.contour.get_points(), precision=project.check_precision): raise ValueError( f"ERROR: The leafs of the cavity wall are not parallel. Please check wall {leaf_1.name} and {leaf_2.name}.") # Determine ctc distance between leafs random_point_leaf_1 = leaf_1.contour.get_points()[0] contour_leaf_2 = leaf_2.contour.get_points() return fem_distance_point_to_plane( point=random_point_leaf_1, plane=contour_leaf_2, precision=project.check_precision)
### =================================================================================================================== ### 10. Create wall ties on line ### ===================================================================================================================
[docs]def viia_create_wall_ties_on_line( project: 'ViiaProject', inner_leaf: Wall, outer_leaf: Wall, inner_line: List[List[float]], outer_line: List[List[Union[int, float]]], wall_tie_distance: Optional[float] = 0.5, snap_to_points: bool = True, minimum_distance: Optional[float] = 0.25): """ This function will create wall ties between the provided inner and outer leaf, along the required line. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - inner_leaf (obj): Object reference of the inner leaf of the cavity wall. - outer_leaf (obj): Object reference of the outer leaf of the cavity wall. - inner_line (list of list of float): Line on the inner leaf on which the wall ties are applied. - outer_line (list of list of float): Line on the outer leaf on which the wall ties are applied. - wall_tie_distance (float): Distance between ties, in [m]. Default value is 0.5 m. - snap_to_points (bool): Toggle for snapping to nearby points. Default value is True. - minimum_distance (float): Minimum distance to snap to nearby points. Default value is 0.25 m. Output: - Returns list of newly created cavity wall ties. """ # Check if lines are parallel to each other inner_line = fem_create_line(project=project, point_list=inner_line) outer_line = fem_create_line(project=project, point_list=outer_line) # Find horizontal direction vector of inner wall base_vector_inner_line = np.array(inner_line[1]) - np.array(inner_line[0]) base_direction_inner_line = base_vector_inner_line / np.linalg.norm(base_vector_inner_line) # Find horizontal direction vector of outer wall base_vector_outer_line = np.array(outer_line[1]) - np.array(outer_line[0]) base_direction_outer_line = base_vector_outer_line / np.linalg.norm(base_vector_outer_line) # Check if walls are parallel if not fem_parallel_vectors(base_vector_inner_line, base_vector_outer_line): raise ValueError(f"ERROR: Inner line and outer line are not parallel ({inner_line.name}, {outer_line.name})") # Compute number of wall ties inner_line_length = inner_line.get_length() outer_line_length = outer_line.get_length() if not fem_compare_values(inner_line_length, outer_line_length): warnings.warn("WARNING: The selected lines have different lengths, the shortest line is selected.") length = min(inner_line_length, outer_line_length) nr_ties = math.ceil(length/wall_tie_distance) # Define the positions of the wall ties # Find center of the line # Start point of grid, including offsets start_point = inner_line.get_center_point() # Set array of distributed points along base base_points = np.linspace( start_point - nr_ties/2 * wall_tie_distance * base_direction_inner_line, start_point + nr_ties/2 * wall_tie_distance * base_direction_inner_line, num=nr_ties + 1) if snap_to_points: # Loops through all the nodes in the building, checks if they are within the shape and adds them all_nodes_building = inner_leaf.project.collections.point_shape_geometries for node_building in all_nodes_building: inner_leaf.add_node(node_building, in_shape_check=True) wall_nodes = inner_leaf.get_nodes() # Loop over xyz-coordinate pairs and create a list with the inner leaf tie coordinates wall_tie_coordinates_inner = [] for point in base_points: coordinate = point.tolist() # Check if coordinate is within shape and not in opening if inner_line.is_point_on_line(coordinate, precision=project.check_precision): if snap_to_points: for node in wall_nodes: node_coordinates = node.coordinates if inner_line.is_point_on_line(node_coordinates, precision=project.check_precision) and \ fem_smaller(fem_distance_coordinates(node_coordinates, coordinate), minimum_distance): coordinate = node_coordinates wall_tie_coordinates_inner.append(coordinate) wall_tie_coordinates_inner = fem_purge_points(wall_tie_coordinates_inner) # Extrapolate the inner leaf tie coordinates to the outer leaf to obtain the outer leaf tie coordinates # Find the distance between each inner wall tie coordinate and the outer leaf line outer_line_coordinates = [ outer_line.node_start.coordinates, outer_line.node_end.coordinates] distance_to_outer = [] for coordinate in wall_tie_coordinates_inner: distance = fem_distance_point_to_line(coordinate, outer_line_coordinates) distance_to_outer.append(distance[1]) # Loop over the two lists and add up the distance and the inner wall tie coordinate = outer wall tie coordinate wall_tie_coordinates_outer = [] for i, j in zip(wall_tie_coordinates_inner, distance_to_outer): n = [i[x] + j[x] for x in range(len(i))] wall_tie_coordinates_outer.append(n) # Create the wall tie objects in the Spring class cavity_wall_ties = [] for inner_leaf_coordinate, outer_leaf_coordinate in zip(wall_tie_coordinates_inner, wall_tie_coordinates_outer): # Check if any of the outer points are outside the contour of the outer leaf. This can happen due to # connecting the outer leafs in the corners if not outer_leaf.is_point_in_shape(outer_leaf_coordinate): project.write_log( f"WARNING: Point {wall_tie_coordinates_outer} is not in shape {outer_line}, wall tie is not created at " f"this location. This can be caused by a shortened outer leaf due to connecting.") continue cavity_wall_ties.append( project.viia_create_cavity_wall_tie(inner_leaf, outer_leaf, inner_leaf_coordinate, outer_leaf_coordinate)) # Return created wall ties return cavity_wall_ties
### =================================================================================================================== ### 11. End of script ### ===================================================================================================================