Source code for viiapackage.VIIAClassDefinition

### =============================================================================================================== ###
###                                                                                                                 ###
###                                             VIIAClassDefinition.py                                              ###
###                                                                                                                 ###
### =============================================================================================================== ###
# This module ``VIIAClassDefinition`` defines the classification for the elements in the DIANA models for seismic
# evaluation by VIIA. Written for NLTH, MRS and SBS. It applies the classification system and is based on:

# VIIA_QE_R376 Basis of Design Retrofit Advice NLTH, v11.0, d.d. 29 August 2024
# VIIA_QE_R376 Basis of Design Step-by-Step MRS, v1.0, d.d. 21 January 2022
# VIIA_QE_R1674 Uitgangspuntenrapport engineering NLPO, v1.0, d.d. 1 August 2019

# This script was based upon the Constitution For Python At VIIA
# For use by VIIA
# Copyright RHDHV

### ===================================================================================================================
###    Contents
### ===================================================================================================================

#   1. Import modules

#   2. Helper functions for shape creation in VIIA

#   3. VIIA project settings for naming convention - Shape creation

#   4. Create shapes on grid functions

#   5. VIIA project settings for naming convention - Material creation

#   6. VIIA project settings for naming convention - Geometry creation

#   7. VIIA project settings for naming convention - Connection creation

#   8. Remove VIIA shape objects

#   9. Getters for VIIA shape objects

#   10. Compare shape names

#   11. End of script

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

# General imports
from __future__ import annotations
import warnings
from typing import TYPE_CHECKING, Optional, List, Union, Dict

# References for functions and classes in the rhdhv_fem package
from rhdhv_fem.shapes import Shapes, Wall, Floor, Roof, Beam, Column, Fstrip, Surfaces, MainSurfaceReinforcement, \
    Regions
from rhdhv_fem.connections import Connections
from rhdhv_fem.materials import Material, LineMassMaterialModel
from rhdhv_fem.groups import Layer
from rhdhv_fem.grid import Grid, Level, Gridline
from rhdhv_fem.fem_math import fem_point_to_plane, fem_length_plane_in_direction, fem_cross_product_vector, \
    fem_vector_in_plane, fem_normal_vector, fem_length_vector, fem_vector_2_points, fem_unit_vector_2_points, \
    fem_parallel_vectors, fem_compare_coordinates, fem_horizontal_vector_in_plane, fem_unit_vector, fem_flip_vector
from rhdhv_fem.shape_geometries import Node

# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
    from viiapackage.viiaStatus import ViiaProject
from viiapackage.viiaGeneral import _viia_get_material_database
from viiapackage.geometry.viia_structural_type import StructuralType
from viiapackage.materials.viia_get_material_model_properties import _viia_hollow_core_slabs


### ===================================================================================================================
###    2. Helper functions for shape creation in VIIA
### ===================================================================================================================

def _name_check(project: ViiaProject, name: Union[str, Layer], collection_name: str):
    """ Function checks input for name and creates layer based on the input, old-style naming is handled here."""
    # Check the input for name
    if isinstance(name, Layer):
        layer = name
    else:
        # Check if layer already exists
        layer = project.find(description=name, collection='layers')

        # Create layer if the layer was not found and the name provided in the list of allowed layer names in VIIA
        if layer is None:
            try:
                layer = project.viia_create_layer(name=name.upper())
            except ValueError:
                pass

    # Bypass will be deprecated in future releases
    new_id = None
    if layer is None:

        # --------------- Will be removed ---------------
        # The name is provided as string, this bypasses the auto functions
        # ID must be provided manually in the name (last part) and is checked for duplicates
        warnings.warn(
            "WARNING: Manual name entry will be deprecated in future releases. Try entering a layer object or name of "
            "the layer instead and use the auto-naming functionality.",
            DeprecationWarning, stacklevel=2)
        new_id = name.split('-')[-1]
        # Check if new wall is a cavity wall, if so add 9000 to ID
        if 'B' in new_id:
            new_id = 9000 + int(new_id.replace('B', ''))
        else:
            try:
                new_id = int(new_id)
            except ValueError:
                raise ValueError(
                    f"ERROR: The name of the shape is expected to end with the ID (split after last '-'), the name "
                    f"provided was {name}. Please correct.")
        if not hasattr(project.collections, collection_name):
            raise ValueError("ERROR: Something went wrong. Please check the collection name.")
        for existing in getattr(project.collections, collection_name):
            if existing.id == new_id:
                raise ValueError(f"ERROR: {collection_name[:-1]} with id {new_id} already exists")
        layer_name = name.split('-')[0]
        if layer_name == 'FUNDERINGSSTROKEN' or layer_name == 'FUNDERINGSWANDEN':
            layer_name = 'F'
        layer = project.find(description=layer_name, collection='layers')
        # Create layer if the layer was not found and the name provided in the list of allowed layer names in VIIA
        if layer is None:
            layer_name = name.split('-')[0]
            if layer_name == 'FUNDERINGSSTROKEN' or layer_name == 'FUNDERINGSWANDEN':
                layer_name = 'F'
            layer = project.viia_create_layer(name=layer_name)
        # --------------- Will be removed ---------------

    # Check if the layer is retrieved correctly
    if layer is None:
        raise ValueError(
            f"ERROR: The layer was not provided correctly, check your input. Make sure you select from: "
            f"'F', 'Bx', 'Nx' layers.")

    # Return the layer
    return layer, new_id


def _level_check(project, point):
    """ Convert level to coordinate for further processing the point."""
    if not isinstance(point, list) or len(point) != 3:
        return point
    if isinstance(point[1], Level) or isinstance(point[1], Level):
        raise ValueError("ERROR: You cannot provide a level for x- or y-coordinate of the point.")
    if isinstance(point[2], Level):
        point[2] = point[2].z_coordinate
    if isinstance(point[2], str):
        levels = []
        for level in project.collections.levels:
            if point[2] == level.name:
                levels.append(level)
        if len(levels) > 1:
            raise ValueError("ERROR: Ambigious use of level, multiple levels found with this name in the project.")
        if len(levels) == 1:
            point[2] = levels[0].z_coordinate
    return point


def _is_masonry(material_name):
    masonry_materials = ['MW-KLEI', 'MW-KZS', 'MW-PORISO', 'MW-AAC']
    return any(masonry in material_name for masonry in masonry_materials)


### ===================================================================================================================
###    3. VIIA project settings for naming convention - Shape creation
### ===================================================================================================================

[docs]def _viia_create_beam( project: ViiaProject, name: Union[str, Layer], material: str, geometry: str, points: List[List[Union[float, str, Shapes, Level]]], is_truss: bool = False, is_roof_beam: bool = False) -> Beam: """ VIIA function to create a beam shape based on layer, material, geometry and contour points. The beam can be optionally turned into a truss element. .. warning:: In this release a string containing the full name is still accepted as input for name. In future releases this functionality will be deprecated. Enter a layer object instead. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - name (str or Layer): Name of the layer as a string, or the Layer object itself, to which the newly created column should be added. Full name of shape is also accepted, not applying auto naming function, this will be removed in future releases. - material (str): Name of the material of the beam shape to be created, complying VIIA naming convention. - geometry (str): Name of the geometry of the beam shape to be created, complying VIIA naming convention. - points (list of list of floats and shapes): The start and end point of the beam shape. Each point is a list of 3 floats or 2 floats and 1 shape. If 3 floats are given, the unmodified point is used. If 2 floats and 1 shape is given, the unknown coordinate at the index of the shape is calculated by projection on this shape. Shapes can be inputted as objects or strings e.g. [[19.2, 6, "roof_1"], [0, 1, 4]] or [[19.2, roof_1, 15], [0, 1, 4]]. The z-coordinate can also be provided as level object, or name of the level. - is_truss (bool): Select to transform the beam object into a truss object. Default value is False. - is_roof_beam (bool): Select to indicate if the beam is part of the roof structure (and is plotted in the roof plot also in case of no roof sheeting). Default value is False. Output: - Returns the created beam object reference. - The beam is added to the project and to the layer. """ # Check the input for name layer, new_id = _name_check(project=project, name=name, collection_name='beams') # Create the material material = project.viia_create_material(material_name=material) # Assemble name if new_id is None: new_id = project.find_max_id(project.collections.beams) + 1 name = f'{layer.name}-BALKEN-{material.name}-{geometry}-{new_id}' # Create the new beam and add to project new_beam = project.create_specified_beam( name=name, contour=project.create_line( [project.viia_projection_on_surface(_level_check(project, point)) for point in points]), material=material, geometry=project.viia_create_geometry(geometry_name=geometry, material_object=material, class_type='Beam')) # Assign id new_beam.id = new_id # Add new beam to layer layer.add_shape(new_beam) # Set beam as roof beam if requested if is_roof_beam: project.project_specific['lists']['roof_beams'].append(new_beam) new_beam.add_meta_data({'roof_beams': True}) # Transform beam into truss if is_truss: project.viia_truss(new_beam) # Return the newly created beam return new_beam
[docs]def _viia_create_column( project: ViiaProject, name: Union[str, Layer], material: str, geometry: str, points: List[List[Union[float, str, Shapes, Level]]], is_truss: bool = False) -> Column: """ VIIA function to create a column shape based on layer, material, geometry and contour points. The column can be optionally turned into a truss element. .. warning:: In this release a string containing the full name is still accepted as input for name. In future releases this functionality will be deprecated. Enter a layer object instead. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - name (str or Layer): Name of the layer as a string, or the Layer object itself, to which the newly created column should be added. Full name of shape is also accepted, not applying auto naming function, this will be removed in future releases. - material (str): Name of the material of the column shape to be created, complying VIIA naming convention. - geometry (str): Name of the geometry of the column shape to be created, complying VIIA naming convention. - points (list of list of floats and shapes): The start and end point of the column shape. Each point is a list of 3 floats or 2 floats and 1 shape. If 3 floats are given, the unmodified point is used. If 2 floats and 1 shape is given, the unknown coordinate at the index of the shape is calculated by projection on this shape. Shapes can be inputted as objects or strings e.g. [[19.2, 6, "roof_1"], [0, 1, 4]] or [[19.2, roof_1, 15], [0, 1, 4]]. The z-coordinate can also be provided as level object, or name of the level. - is_truss (bool): Select to transform the column object into a truss object. Default value is False. Output: - Returns the created column object reference. - The column is added to the project and to the layer. """ # Check the input for name layer, new_id = _name_check(project=project, name=name, collection_name='columns') # Create the material material = project.viia_create_material(material_name=material) # Assemble name if new_id is None: new_id = project.find_max_id(project.collections.columns) + 1 name = f'{layer.name}-KOLOMMEN-{material.name}-{geometry}-{new_id}' # Create the new column and add to project new_column = project.create_column( name=name, contour=project.create_line( [project.viia_projection_on_surface(_level_check(project, point)) for point in points]), material=material, geometry=project.viia_create_geometry(geometry_name=geometry, material_object=material, class_type='Column')) # Set structural type to PSSE by default new_column.add_meta_data({"structural_type": StructuralType.PSSE}) # Assign id new_column.id = new_id # Add new column to layer layer.add_shape(new_column) # Transform beam into truss if is_truss: project.viia_truss(new_column) # Return the newly created column return new_column
[docs]def _viia_create_floor( project: ViiaProject, name: Union[str, Layer], material: str, geometry: str, points: List[List[List[Union[float, str, Shapes, Level]]]], element_x_axis: Optional[Union[List[float], str]] = None) -> Floor: """ VIIA function to create a floor shape based on layer, material, geometry and contour points. The points list can also contain the openings in the floor shape. .. warning:: In this release a string containing the full name is still accepted as input for name. In future releases this functionality will be deprecated. Enter a layer object instead. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - name (str or Layer): Name of the layer as a string, or the Layer object itself, to which the newly created floor should be added. Full name of shape is also accepted, not applying auto naming function, this will be removed in future releases. - material (str): Name of the material of the floor shape to be created, complying VIIA naming convention. - geometry (str): Name of the geometry of the floor shape to be created, complying VIIA naming convention. If naming convention dictates that the geometry is incorporated into the material (e.g. wood plank floors), setting the geometry to 0 skips the geometry parameter when generating the object name. - points (list of list of list of floats and shapes): The points of the contour and opening of the floor shape. Each point is a list of 3 floats or 2 floats and 1 shape. If 3 floats are given, the unmodified point is used. If 2 floats and 1 shape is given, the unknown coordinate at the index of the shape is calculated by projection on this shape. Shapes can be inputted as objects or strings. e.g. [[[0, 0, 0], [1, 0, 0], [1, "wall_1", 0], [0, wall_1, 0]], [coordinates of opening1], [coordinates of opening2] etc.]. The z-coordinate can also be provided as level object, or name of the level. - element_x_axis (list of floats): Optional input to define direction of the local x-axis of the floor, which also defines the spanning direction of the floor. Default value is None, in which case the element-x-axis is set to the direction of the intersection of the surface with a horizontal plane. In case of a floor (with a horizontal surface), the local x-axis is set in the global x-direction. Alternative also the name of the direction can be provided. Output: - Returns the created floor object reference. - The floor is added to the project and to the layer. """ # Check the input for name layer, new_id = _name_check(project=project, name=name, collection_name='floors') # Create the contour and openings floor_polylines = [ project.create_polyline( [project.viia_projection_on_surface(_level_check(project, point)) for point in point_set]) for point_set in points] if any([part in material for part in ["PLANKEN", "PLATEN", "HBV-"]]) and geometry not in [0, '0']: warnings.warn(f"For the geometry of equivalent plates the geometry should be entered as 0, " f"now {geometry} was given. This is not possible anymore.", DeprecationWarning, stacklevel=2) geometry = 0 # Create the material, HBV timber floors/roofs require additional parameters if 'HBV-' in material and "PLATEN" not in material: # Select the NPR used npr_year = int(project.project_information['npr_versie'].split('NPR9998:')[-1][0:4]) if npr_year >= 2020: # Get the required parameters for floor direction = element_x_axis if isinstance(element_x_axis, str): direction_obj = project.get_direction(element_x_axis) if direction_obj is None: direction_obj = project.get_direction(element_x_axis.upper()) if direction_obj is None: raise ValueError(f"ERROR: Unknown input for direction provided: {element_x_axis}.") direction = direction_obj.vector if direction is None: direction = [1, 0, 0] if not fem_vector_in_plane(vector=direction, plane=floor_polylines[0].get_points()): raise ValueError( "ERROR: The direction for the local x-axis is not in plane of the floor, please check.") span = round(fem_length_plane_in_direction( surface=floor_polylines[0].get_points(), direction=direction), 2) perpendicular = fem_cross_product_vector(a=direction, b=fem_normal_vector(floor_polylines[0].get_points())) width = round(fem_length_plane_in_direction( surface=floor_polylines[0].get_points(), direction=perpendicular), 2) material += f'-{round(span, 2)}-{round(width, 2)}' material = project.viia_create_material(material_name=material, hbv_span=span, hbv_width=width) else: material = project.viia_create_material(material_name=material) else: material = project.viia_create_material(material_name=material) # Assemble name if new_id is None: new_id = project.find_max_id(project.collections.floors) + 1 name = f'{layer.name}-VLOEREN-{material.name}-{geometry}-{new_id}' if geometry in [0, '0']: name = f'{layer.name}-VLOEREN-{material.name}-{new_id}' # Create the new floor and add to project new_floor = project.create_floor( name=name, contour=floor_polylines[0], material=material, geometry=project.viia_create_geometry(geometry_name=geometry, material_object=material, class_type='Floor'), openings=floor_polylines[1:]) # Update the x-axis direction if provided if element_x_axis: new_floor.update_local_x_axis(element_x_axis) # Assign id new_floor.id = new_id # Add new floor to layer layer.add_shape(new_floor) # Return the newly created floor return new_floor
[docs]def _viia_create_roof( project: ViiaProject, name: Union[str, Layer], material: str, geometry: str, points: List[List[List[Union[float, str, Shapes, Level]]]], element_x_axis: Optional[Union[List[float], str]] = None) -> Roof: """ VIIA function to create a roof shape based on layer, material, geometry and contour points. The points list can also contain the openings in the roof shape. .. warning:: In this release a string containing the full name is still accepted as input for name. In future releases this functionality will be deprecated. Enter a layer object instead. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - name (str or Layer): Name of the layer as a string, or the Layer object itself, to which the newly created roof should be added. Full name of shape is also accepted, not applying auto naming function, this will be removed in future releases. - material (str): Name of the material of the roof shape to be created, complying VIIA naming convention. - geometry (str): Name of the geometry of the roof shape to be created, complying VIIA naming convention. If naming convention dictates that the geometry is incorporated into the material (e.g. wood plank roofs), setting the geometry to 0 skips the geometry parameter when generating the object name. - points (list of list of list of floats and shapes): The points of the contour and opening of the roof shape. Each point is a list of 3 floats or 2 floats and 1 shape. If 3 floats are given, the unmodified point is used. If 2 floats and 1 shape is given, the unknown coordinate at the index of the shape is calculated by projection on this shape. Shapes can be inputted as objects or strings. e.g. [[[0, 0, 0], [1, 0, 0], [1, "wall_1", 0], [0, wall_1, 0]], [coordinates of opening1], [coordinates of opening2] etc.]. The z-coordinate can also be provided as level object, or name of the level. - element_x_axis (list of floats): Optional input to define direction of the local x-axis of the roof, which also defines the spanning direction of the roof. Default value is None, in which case the element-x-axis is set to the direction of the intersection of the surface with a horizontal plane. In case of a roof (with a horizontal surface), the local x-axis is set in the global x-direction. Output: - Returns the created roof object reference. - The roof is added to the project and to the layer. """ # Check the input for name layer, new_id = _name_check(project=project, name=name, collection_name='roofs') if any([part in material for part in ["PLANKEN", "PLATEN", "HBV-"]]) and geometry not in [0, '0']: warnings.warn(f"For the geometry of equivalent plates the geometry should be entered as 0, " f"now {geometry} was given. This is not possible anymore.", DeprecationWarning, stacklevel=2) geometry = 0 # Create the contour and openings roof_polylines = [ project.create_polyline( [project.viia_projection_on_surface(_level_check(project, point)) for point in point_set]) for point_set in points] # Create the material material = project.viia_create_material(material_name=material) # Assemble name if new_id is None: new_id = project.find_max_id(project.collections.roofs) + 1 name = f'{layer.name}-DAKEN-{material.name}-{geometry}-{new_id}' if geometry in [0, '0']: name = f'{layer.name}-DAKEN-{material.name}-{new_id}' # Create the new roof and add to project new_roof = project.create_roof( name=name, contour=roof_polylines[0], material=material, geometry=project.viia_create_geometry(geometry_name=geometry, material_object=material, class_type='Roof'), openings=roof_polylines[1:]) # Update the x-axis direction if provided if element_x_axis: new_roof.update_local_x_axis(element_x_axis) # Assign id new_roof.id = new_id # Add new roof to layer layer.add_shape(new_roof) # Return the newly created roof return new_roof
[docs]def _viia_create_wall( project: ViiaProject, name: Union[str, Layer], material: str, geometry: str, points: List[List[List[Union[float, str, Shapes, Level]]]], cavity_inner_wall_id: Optional[int] = None, structural_type: str = 'psse') -> Wall: """ VIIA function to create a wall shape based on naming convention for shape, material, geometry and contour points. The points list can also contain the openings in the wall shape. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - name (str or Layer): Name of the layer as a string, or the Layer object itself, to which the newly created column should be added. Full name of shape is also accepted, not applying auto naming function, this will be removed in future releases. - material (str): Name of the material of the wall shape to be created, complying VIIA naming convention. - geometry (str): Name of the geometry of the wall shape to be created, complying VIIA naming convention. - points (list of list of floats and shapes): The points of the contour and opening of the wall shape. Each point is a list of 3 floats or 2 floats and 1 shape. If 3 floats are given, the unmodified point is used. If 2 floats and 1 shape is given, the unknown coordinate at the index of the shape is calculated by projection on this shape. Shapes can be inputted as objects or strings. e.g. [[[0, 0, 0], [1, 0, 0], [1, "wall_1", 0], [0, wall_1, 0]], [coordinates of opening1], [coordinates of opening2] etc.]. The z-coordinate can also be provided as level object, or name of the level. - cavity_inner_wall_id (int): In case of an outer leaf of a cavity wall, the ID will be set based on the ID of the inner leaf. This number should be provided in that case. - structural_type (str): Optional structural type, can be either 'psse' or 'nsce'. Output: - Returns the created wall object reference. The ID of the newly created wall is set to the number supplied in the name. For cavity walls, 9000 is added to the id that is supplied. Therefore, if the inner leaf has id = 5, the outer leaf will have id = 9005. - The wall is added to the project and to the layer. """ # Check the input for name layer, new_id = _name_check(project=project, name=name, collection_name='walls') if isinstance(material, str) and any([part in material for part in ["HSB-"]]) and geometry not in [0, '0']: warnings.warn(f"For the geometry of equivalent plates the geometry should be entered as 0, " f"now {geometry} was given. This is not possible anymore.", DeprecationWarning, stacklevel=2) geometry = 0 # Check material if isinstance(material, str): material = project.viia_create_material(material_name=material) # Assemble name if new_id is None: if cavity_inner_wall_id: new_id = 9000 + cavity_inner_wall_id # Check if id is already present in project for existing_wall in project.collections.walls: if existing_wall.id == new_id: raise ValueError(f"ERROR: Inner leaf {existing_wall.name} is already connected to an outer leaf.") else: new_id = project.find_max_id(project.collections.walls) + 1 wall_type = 'WANDEN' name = f'{layer.name}-{wall_type}-{material.name}-{geometry}-{new_id}' if geometry in [0, '0']: name = f'{layer.name}-{wall_type}-{material.name}-{new_id}' # Create the contour and openings wall_polylines = [ project.create_polyline( [project.viia_projection_on_surface(_level_check(project, point)) for point in point_set]) for point_set in points] # Create direction for HSB walls with element_x_axis in vertical direction element_x_axis = None if 'HSB' in material.name: wall_normal_vector = fem_normal_vector( surface=wall_polylines[0].get_points(), precision=project.check_precision) wall_horizontal_vector = fem_horizontal_vector_in_plane( surface=wall_polylines[0].get_points(), precision=project.check_precision) element_x_axis_vector = fem_unit_vector( vector=fem_cross_product_vector(a=wall_normal_vector, b=wall_horizontal_vector), precision=project.check_precision) if element_x_axis_vector[2] < 0: element_x_axis_vector = fem_flip_vector(a=element_x_axis_vector) element_x_axis = project.create_direction(vector=element_x_axis_vector) # Create the new wall and add to project new_wall = project.create_wall( name=name, contour=wall_polylines[0], material=material, geometry=project.viia_create_geometry(geometry_name=geometry, material_object=material, class_type='Wall'), openings=wall_polylines[1:], element_x_axis=element_x_axis) # Add the structural type to the metadata if structural_type: type_dict = {'psse': StructuralType.PSSE, 'nsce': StructuralType.NSCE} new_wall.add_meta_data({"structural_type": type_dict.get(structural_type)}) # Assign id new_wall.id = new_id # Add new wall to layer layer.add_shape(new_wall) # Return the newly created wall return new_wall
[docs]def _viia_create_fstrip( project: ViiaProject, name: Union[str, Layer], material: str, geometry: str, points: List[List[List[Union[float, str, Shapes, Level]]]], element_x_axis: Optional[Union[List[float], str]] = None) -> Fstrip: """ VIIA function to create a foundation strip shape based on layer, material, geometry and contour points. The points list can also contain the openings in the foundation strip shape. .. warning:: In this release a string containing the full name is still accepted as input for name. In future releases this functionality will be deprecated. Enter a layer object instead. .. note:: Foundation strips can only be created in the layer for foundations 'F'. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - name (str or Layer): Name of the layer as a string, or the Layer object itself, to which the newly created foundation strip should be added. - material (str): Name of the material of the foundation strip shape to be created, complying VIIA naming convention. - geometry (str): Name of the geometry of the foundation strip shape to be created, complying VIIA naming convention. If naming convention dictates that the geometry is incorporated into the material (e.g. wood plank foundation strips), setting the geometry to 0 skips the geometry parameter when generating the object name. - points (list of list of floats and shapes): The points of the contour and opening of the foundation strip shape. Each point is a list of 3 floats or 2 floats and 1 shape. If 3 floats are given, the unmodified point is used. If 2 floats and 1 shape is given, the unknown coordinate at the index of the shape is calculated by projection on this shape. Shapes can be inputted as objects or strings. e.g. [[[0, 0, 0], [1, 0, 0], [1, "wall_1", 0], [0, wall_1, 0]], [coordinates of opening1], [coordinates of opening2] etc.]. The z-coordinate can also be provided as level object, or name of the level. Output: - Returns the created fstrip object reference. - The fstrip is added to the project and to the layer. """ # Check the input for name layer, new_id = _name_check(project=project, name=name, collection_name='fstrips') # Check if the layer is the foundation layer if layer.name != 'F': raise ValueError( f"ERROR: The fstrip can only be created in the foundation layer (layer 'F') and not in {layer.name}.") # Check material if isinstance(material, str): material = project.viia_create_material(material_name=material) # Assemble name if new_id is None: new_id = project.find_max_id(project.collections.fstrips) + 1 name = f'{layer.name}-STROKEN-{material.name}-{geometry}-{new_id}' if geometry == 0: name = f'{layer.name}-STROKEN-{material.name}-{new_id}' # Create the contour and openings fstrip_polylines = [ project.create_polyline( [project.viia_projection_on_surface(_level_check(project, point)) for point in point_set]) for point_set in points] # Create the new fstrip and add to project new_fstrip = project.create_fstrip( name=name, contour=fstrip_polylines[0], material=material, geometry=project.viia_create_geometry(geometry_name=geometry, material_object=material, class_type='Fstrip'), openings=fstrip_polylines[1:]) # Update the x-axis direction if provided if element_x_axis: new_fstrip.update_local_axis(element_x_axis) # The z-axis should be in the global negative z-direction if not fem_compare_coordinates( coordinate1=new_fstrip.z_axis_direction().vector, coordinate2=[0, 0, -1], precision=project.check_precision): new_fstrip.flip() # Assign id new_fstrip.id = new_id # Add new fstrip to layer layer.add_shape(new_fstrip) # Return the newly created fstrip return new_fstrip
[docs]def _viia_create_main_surface_reinforcement( project: ViiaProject, name: Union[str, Layer], material: str, geometry: str, points: List[List[Union[float, str, Shapes, Level]]], host_members: Optional[List[Union[Floor, Wall]]] = None, discretisation: str = 'element', element_x_axis: Optional[Union[List[float], str]] = None, regions: Optional[List['Regions']] = None) \ -> MainSurfaceReinforcement: """ VIIA function to create main surface reinforcement shape based on layer, material, geometry and contour points. The points list can also contain the openings in the main surface reinforcement shape. .. warning:: In this release a string containing the full name is still accepted as input for name. In future releases this functionality will be deprecated. Enter a layer object instead. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - name (str or Layer): Name of the layer as a string, or the Layer object itself, to which the newly created main surface reinforcement should be added. - material (str): Name of the material of the main surface reinforcement shape to be created, complying VIIA naming convention. - geometry (str): Name of the geometry of the main surface reinforcement shape to be created, complying VIIA naming convention. - points (list of list of floats and shapes): The points of the contour and opening of the main surface reinforcement shape. Each point is a list of 3 floats or 2 floats and 1 shape. If 3 floats are given, the unmodified point is used. If 2 floats and 1 shape is given, the unknown coordinate at the index of the shape is calculated by projection on this shape. Shapes can be inputted as objects or strings. e.g. [[[0, 0, 0], [1, 0, 0], [1, "wall_1", 0], [0, wall_1, 0]], [coordinates of opening1], [coordinates of opening2] etc.]. The z-coordinate can also be provided as level object, or name of the level. - host_members (obj): Optional input for a list of object reference of the shape to which the reinforcement belongs. Default value is None. - discretisation (str): Setting for the meshing of the surface reinforcement. Meshing can be based on the mesh elements it is embedded in, 'element', or based on the shape itself, 'section'. Default value is 'element'. - element_x_axis (obj): Optional input to define direction of the local x-axis of the roof, which also defines the spanning direction of the roof. Default value is None, in which case the element-x-axis is set to the direction of the intersection of the surface with a horizontal plane. In case of a roof (with a horizontal surface), the local x-axis is set in the global x-direction. - regions (list of obj): List of object references of a region (surface) within the surface shape. It may not overlap the contour of the surface shape, but openings might be. Openings need to be set in the surface shape and cannot be set as part of the region. Default value is None. Output: - Returns the created main surface reinforcement object reference. - The main surface reinforcement is added to the project and to the layer. """ # Check the input for name layer, new_id = _name_check(project=project, name=name, collection_name='main_surface_reinforcements') # Create material material = project.viia_create_material(material_name=material) # Assemble name if new_id is None: new_id = project.find_max_id(project.collections.main_surface_reinforcements) + 1 name = f'{layer.name}-WAPENING-{material.name}-{geometry}-{new_id}' # Create the contour and openings surface_reinforcement_polylines = [ project.create_polyline( [project.viia_projection_on_surface(_level_check(project, point)) for point in point_set]) for point_set in points] # Create the new main surface reinforcement and add to project new_main_surface_reinforcement = project.create_specified_main_surface_reinforcement( name=name, contour=surface_reinforcement_polylines[0], material=material, geometry=project.viia_create_geometry(geometry_name=geometry, material_object=material, class_type='MainSurfaceReinforcement'), openings=surface_reinforcement_polylines[1:], discretisation=discretisation, host_members=host_members, regions=regions) # Update the x-axis direction if provided if element_x_axis: new_main_surface_reinforcement.update_local_x_axis(element_x_axis) # Assign id new_main_surface_reinforcement.id = new_id # Add new main surface reinforcement to layer layer.add_shape(new_main_surface_reinforcement) # Return the newly created main surface reinforcement return new_main_surface_reinforcement
### =================================================================================================================== ### 4. Create shapes on grid functions ### ===================================================================================================================
[docs]def _viia_create_beams_on_grid( project: ViiaProject, layer: str, beam_list: List[List[Union[str, int]]], grid: Optional[Union[Grid, int, str]] = None, is_truss: bool = False) -> List[Beam]: """ This function is used to set up the beams for a specific layer, making use of a grid system. A beam list is given as input, in which each row represents a beam. Each beam is in turn represented by a list of five values; starting point, end point, z-coordinates, material and cross-section. The points to place the beams should be given in grid notation, for instance 'A3'. The beams can be optionally turned into truss elements. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - layer (str): String specifying the layer, conform VIIA naming convention. For example: 'N0'. - beam_list (list): List containing the information of the individual beams, for example: [['D1', 'D9', [2.5, 3.5], 'HOUT', '100x100'], ['D9', 'L9', 2.5, 'HOUT', '100x100'], [[0, 5.3], [7.5, 5.3], 4.6, 'HOUT', '100x100'] This will create three timber beams with a cross-section 100mm x 100mm. The first beam is created from grid point D1 at an elevation of 2.5 m to D9 at an elevation of 3.5. The second beam is created from grid point D9 to L9 at an elevation of 2.5 m. The third beam is created at coordinates [0, 5.3] and [7.5, 5.3] at an elevation of 4.6 m. Note that to create an inclined beam, the heights must be given as a list - grid (Grid, int, str): Optional object, id or name of the grid to use. By default, the first grid in the collection is used. - is_truss (bool): Select to transform the beam object into a truss object. Default value is False. Output: - Each beam described in the beam list is added to the project and the relevant collections. - A list containing all beam objects is returned. :Example:: >>> project.viia_create_beams_on_grid( layer='N0', beam_list=[['D1', 'D9', 3.0, 'MW-KLEI<1945', 210], ['D9', 'L9', [2.0, 3.0], 'MW-KLEI<1945', 210], [[0, 5.3], [7.5, 5.3], 4.6, 'HOUT', '100x100']]) """ # Select the correct grid from collections, if not provided first one will be used grid_obj = project.find_grid(grid_description=grid) beams = [] for beam_item in beam_list: if isinstance(beam_item[0], str): x1, y1 = grid_obj[beam_item[0]] else: x1, y1 = beam_item[0] if isinstance(beam_item[1], str): x2, y2 = grid_obj[beam_item[1]] else: x2, y2 = beam_item[1] if isinstance(beam_item[2], List): z1, z2 = beam_item[2] else: z1, z2 = beam_item[2], beam_item[2] beam = _viia_create_beam( project=project, name=layer, material=beam_item[3], geometry=beam_item[4], points=[[x1, y1, z1], [x2, y2, z2]], is_truss=is_truss) if beam.contour.is_vertical(): project.write_log(f"WARNING: Beam {beam.name} is vertical. Consider if it should be a column instead.") beams.append(beam) return beams
[docs]def _viia_create_beams_on_grid_in_shape( project: ViiaProject, layer: Union[str, Layer], grid: Union[Grid, int, str], gridlines: List[Union[str, int]], shape: Surfaces, material: str, geometry: str, is_truss: bool = False, is_roof_beam: bool = False) -> \ List[Beam]: """ This function can be used to create beams that lie inside a surface shape using the grids. The best use of this function is for purlins and rafters, but can also be used for beams supporting a floor. Beams will be created on all the given gridlines, and they will be cut by the contour of the given surface shape. The beams can be optionally turned into truss elements. .. note:: Currently, this function only works for gridlines that are parallel to either the x-axis or the y-axis. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - layer (obj): Layer object in which the beams are located. Alternative (str): String representing the layer name, conforming to VIIA naming convention. E.g. 'N0'. - grid (obj): Grid object that the beams are plotted on. Alternative (int): ID of the grid object. Alternative (str): Name of the grid object. - gridlines (list of str or int): List containing the names of the gridlines on which the beams are plotted. The entries in the list can be either strings or integers. - shape (obj): The surface shape that the beams are created in. - material (str): Name of the material of the beam shape to be created, complying VIIA naming convention. - geometry (str): Name of the geometry of the beam shape to be created, complying VIIA naming convention. - is_roof_beam (bool): Select to indicate if the beam is part of the roof structure (and is plotted in the roof plot also in case of no roof sheeting). Default value is False. Output: - Each beam described in the beam list is added to the project and the relevant collections. - A list containing all beam objects is returned. """ # Get layer if defined as string if isinstance(layer, str): layer = project.find(description=layer, collection='layers') # Select the correct grid from collections, if not provided first one will be used grid_obj = project.find_grid(grid_description=grid) # Turn all gridlines into strings gridlines = [str(x) for x in gridlines] # Get the beam direction from the given gridlines beam_direction = None for gridline_group in grid_obj.gridline_groups: if str(gridlines[0]) in gridline_group.gridline_names: if gridline_group.vector[0] == 0 and gridline_group.vector[1] == 1: beam_direction = 'y' elif gridline_group.vector[0] == 1 and gridline_group.vector[1] == 0: beam_direction = 'x' else: raise NotImplementedError(f'ERROR: Creating beams in a shape on a diagonal grid is not supported yet.') if not beam_direction: raise ValueError(f'ERROR: Couldn\'t find the given gridlines in the given grid.') # Get all the coordinates x1, x2, y1, y2, z1, z2 = None, None, None, None, None, None # For the order of the z-coordinates, the direction of the must be determined, since the x- and y-coordinates are # created from minimum to maximum. slope = shape.element_x_axis.vector if shape.element_x_axis.vector[2] != 0 else shape.y_axis_direction().vector if slope[2] == 0: z1 = shape.contour.get_points()[0][2] z2 = z1 elif beam_direction == 'x': if (slope[0] > 0 and slope[2] > 0) or (slope[0] < 0 and slope[2] < 0): z1 = shape.contour.get_min_z() z2 = shape.contour.get_max_z() elif (slope[0] < 0 and slope[2] > 0) or (slope[0] > 0 and slope[2] < 0): z1 = shape.contour.get_max_z() z2 = shape.contour.get_min_z() else: if (slope[1] > 0 and slope[2] > 0) or (slope[1] < 0 and slope[2] < 0): z1 = shape.contour.get_min_z() z2 = shape.contour.get_max_z() elif (slope[1] < 0 and slope[2] > 0) or (slope[1] > 0 and slope[2] < 0): z1 = shape.contour.get_max_z() z2 = shape.contour.get_min_z() # Set the x- or y-coordinates depending on the beam direction if beam_direction.lower() == 'x': x1 = shape.contour.get_min_x() x2 = shape.contour.get_max_x() else: y1 = shape.contour.get_min_y() y2 = shape.contour.get_max_y() beams = [] for gridline in gridlines: # Set the x- or y-coordinates depending on the beam direction if beam_direction.lower() == 'x': y1 = grid_obj[gridline] y2 = y1 else: x1 = grid_obj[gridline] x2 = x1 # In case z1 and z2 were not found, it means the beams run along the length of the roof. # The new coordinates, for every gridline, is calculated by equating the previous obtained z1 and z2. if z1 == z2: z1 = fem_point_to_plane( [x1, y1, 0], plane=shape.contour.get_points(), direction=[0, 0, 1], return_intersection=True)[2] z2 = z1 # Get the points of the line on the contour line = shape.contour.intersections_with_line([[x1, y1, z1], [x2, y2, z2]]) # Create the beam if len(line) == 2: points = line elif len(line) > 2: if beam_direction.lower == 'x': line.sort(key=lambda x: x[0]) points = [line[0], line[-1]] else: line.sort(key=lambda y: y[1]) points = [line[0], line[-1]] else: project.write_log( f"WARNING: Couldn\'t find two points for beam on gridline {gridline}. It was not created.") beam = _viia_create_beam( project=project, name=layer, material=material, geometry=geometry, points=points, is_truss=is_truss, is_roof_beam=is_roof_beam) beams.append(beam) beam.add_meta_data({'grid': grid_obj}) return beams
[docs]def viia_create_collar_ties_on_grid( project: ViiaProject, layer: Union[str, Layer], grid: Union[Grid, int, str], gridlines: List[Union[str, int]], shapes: List[Shapes], beam_height: Union[float, Level, str], material: str, geometry: str, floor_level: Optional[Union[float, Level, str]] = None, is_truss: bool = True, is_roof_beam: bool = False) -> List[Beam]: """ This function can be used to created collar ties between two roof shapes using the grids. Beams will be created on all the given gridlines, and they will be cut by the two roof plates. The beams can be optionally turned into truss elements. .. note:: Currently, this function only works for gridlines that are parallel to either the x-axis or the y-axis. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - layer (obj): Layer object in which the beams are located. Alternative (str): String representing the layer name, conforming to VIIA naming convention. E.g. 'N0'. - grid (obj): Grid object that the beams are plotted on. Alternative (int): ID of the grid object. Alternative (str): Name of the grid object. - gridlines (list of str or int): List containing the names of the gridlines on which the beams are plotted. The entries in the list can be either strings or integers. - shapes (list of obj): List of two surface shapes that the beams are created between. - beam_height (float): Elevation of the collar ties measured from the floor defined in floor_level. Alternative 1 (obj): Level object specifying the elevation of the collar ties measured from the ground. This level must be part of the given grid. Alternative 2 (str): String representation of a level object specifying the elevation of the collar ties measured from the ground. This level must be part of the given grid. - material (str): Name of the material of the beam shape to be created, complying VIIA naming convention. - geometry (str): Name of the geometry of the beam shape to be created, complying VIIA naming convention. - floor_level (float): Optional argument that specifies the height of the floor that may be used along with beam_height to determine the elevation at which to create the beams. This argument is required when beam_height is not a Level object. Alternative 1 (obj): Level object specifying the elevation of the floor. Alternative 2 (str): String representation of a level object specifying the elevation of the floor. - is_truss (bool): Select to transform the collar tie objects into truss objects. Default value is True. - is_roof_beam (bool): Select to indicate if the beam is part of the roof structure (and is plotted in the roof plot also in case of no roof sheeting). Default value is False. Output: - Each beam described in the beam list is added to the project and the relevant collections. - A list containing all beam objects is returned. """ # Check the shapes input if len(shapes) != 2: raise ValueError(f"ERROR: Two surface shapes must be given to create collar ties.") # Get layer if defined as string if isinstance(layer, str): layer = project.find(description=layer, collection='layers') # Select the correct grid from collections, if not provided first one will be used grid_obj = project.find_grid(grid_description=grid) # Turn all gridlines into strings gridlines = [str(x) for x in gridlines] # Get the beam direction from the given gridlines beam_direction = None for gridline_group in grid_obj.gridline_groups: if str(gridlines[0]) in gridline_group.gridline_names: if gridline_group.vector[0] == 0 and gridline_group.vector[1] == 1: beam_direction = 'y' elif gridline_group.vector[0] == 1 and gridline_group.vector[1] == 0: beam_direction = 'x' else: project.write_log(f"WARNING: Creating collar ties on diagonal grid is not supported yet.") if not beam_direction: raise ValueError(f"ERROR: Couldn't find the given gridlines in the given grid.") # Get the z coordinate of the beams using the floor level and beam height if isinstance(beam_height, Level): z = beam_height.z_coordinate elif isinstance(beam_height, str): z = grid_obj[beam_height] else: if isinstance(floor_level, Level): z = beam_height + floor_level.z_coordinate elif isinstance(floor_level, str): z = beam_height + grid_obj[floor_level] else: z = beam_height + floor_level beams = [] for gridline in gridlines: # Get the points by projecting them to the two roof planes if beam_direction == 'x': y = grid_obj[gridline] pt1 = fem_point_to_plane( point=[0, y, z], plane=shapes[0].contour.get_points(), direction=[1, 0, 0], return_intersection=True) pt2 = fem_point_to_plane( point=[0, y, z], plane=shapes[1].contour.get_points(), direction=[1, 0, 0], return_intersection=True) else: x = grid_obj[gridline] pt1 = fem_point_to_plane( point=[x, 0, z], plane=shapes[0].contour.get_points(), direction=[0, 1, 0], return_intersection=True) pt2 = fem_point_to_plane( point=[x, 0, z], plane=shapes[1].contour.get_points(), direction=[0, 1, 0], return_intersection=True) # Create the beam if pt1 and pt2: beam = _viia_create_beam( project=project, name=layer, material=material, geometry=geometry, points=[pt1, pt2], is_truss=is_truss, is_roof_beam=is_roof_beam) beams.append(beam) beam.add_meta_data({'grid': grid_obj}) else: project.write_log( f"WARNING: Couldn't find two points for collar tie on gridline {gridline}. It was not created.") return beams
[docs]def _viia_create_columns_on_grid( project: ViiaProject, layer: str, column_list: List[List[Union[str, int]]], grid: Optional[Union[Grid, int, str]] = None, is_truss: bool = False) -> List[Column]: """ This function is used to set up the columns for a specific layer, making use of a grid system. A column list is given as input, in which each row represents a column. Each column is in turn represented by a list of five values; starting point, end point, z-coordinate, material and cross-section. The points to place the columns should be given in grid notation, for instance 'A3'. The columns can be optionally turned into truss elements. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - layer (str): String specifying the layer, conform VIIA naming convention. For example: 'N0'. - column_list (list): List containing the information of the individual columns, for example: [['D1', 0.0, 3.0, 'LIN-BETON', '210x210'], ['E4', 0.0, 3.5, 'LIN-HOUT', 'D180'], [[4.86, 6.01], 0.0, 3.0, 'LIN-HOUT', '50x100']. This will create three timber columns with cross-sections 210x210, D180 and 50x100. The first column is created at grid point D1 from an elevation of 0 m to 3.0 m. The second column is created at grid point E4 from an elevation of 0 m to 3.5 m. The third column is created at x-coordinate 4.86 and y-coordinate 6.01 and spans from an elevation of 0 m to 3 m. - grid (Grid, int, str): Optional object, id or name of the grid to use. By default, the first grid in the collection is used. - is_truss: transforms all columns into trusses. Default value is False. Output: - Each column described in the column list is added to the project and the relevant collections. - A list containing all column objects is returned. Example: .. code-block:: python project.viia_create_columns_on_grid( layer='N0', column_list=[['D1', 0.0, 3.0, 'LIN-BETON', '210x210'], ['E4', 0.0, 3.5, 'LIN-HOUT', 'D180'], [[4.86, 6.01], 0.0, 3.0, 'LIN-HOUT', '50x100']]) """ # Select the correct grid from collections, if not provided first one will be used grid_obj = project.find_grid(grid_description=grid) columns = [] for column_item in column_list: if isinstance(column_item[0], str): x1, y1 = grid_obj[column_item[0]] else: x1, y1 = column_item[0] z1, z2 = column_item[1], column_item[2] column = _viia_create_column( project=project, name=layer, material=column_item[3], geometry=column_item[4], points=[[x1, y1, z1], [x1, y1, z2]], is_truss=is_truss) columns.append(column) return columns
[docs]def _viia_create_floors_on_grid( project: ViiaProject, layer: str, z_coordinate: float, floor_list: List[List[Union[str, float, list]]], grid: Optional[Union[Grid, int, str]] = None) -> List[Floor]: """ This function is used to set up the floors for a specific layer, making use of a grid system. A floor list is given as input, in which each row represents a floor. Each floor is in turn represented by a list of corner points, a material, a geometry and an optional element_x_axis. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - layer (str): String specifying the layer, conform VIIA naming convention. For example: 'N0'. - z_coordinate (float): The z_coordinate of the floors, in [m]. - floor_list (list): List containing the information of the individual floors, for example [['A1', 'B1', 'B2', 'A2', 'LIN-HBV-PLANKEN-0.03-0.040-0.150-0.600', 0, [0, 1, 0], ['A2', 'B2', 'B1', 'C1', 'C3', 'A3', 'LIN-BETON', 300],[[0,0], [7.5,0], [7.5,7.5], [0,7.5], 'LIN-BETON', 100]. This will create three rectangular floors. The geometry of the floor shape to be created, needs to comply to VIIA naming convention. If naming convention dictates that the geometry is incorporated into the material (e.g. wood plank floors), setting the geometry to 0 skips the geometry parameter when generating the object name. Optionally a local x-axis can be defined. - grid (Grid, int, str): Optional object, id or name of the grid to use. By default, the first grid in the collection is used. Output: - Each floor described in the floor list is added to the project and the relevant collections. - A list containing all floor objects is returned. :Example: project.create_floors_on_grid('N1', 3.0, floor_list) An example of a floor list is: floor_list = [['A1', 'B1', 'B2', 'A2', 'LIN-HBV-PLANKEN-0.03-0.040-0.150-0.600', 0, [0, 1, 0]], ['A2', 'B2', 'B1', 'C1', 'C3', 'A3', 'LIN-BETON', 300], [[0,0], [7.5,0], [7.5,7.5], [0,7.5], 'LIN-BETON', 100]] The first floor in this list spans between gridpoints A1, B1, B2 and A2, is a 'PLANKEN' floor with the specified spacing and geometry parameters, and spans in the y direction([0, 1, 0]). The second floor spans between six gridpoints, is made of concrete and has a thickness of 300mm. The element_x_axis is omitted, which means it will have default value [1, 0, 0]. The third floor spans between four x and y coordinates, is made of concrete and has a thickness of 100mm. The element_x_axis is omitted, which means it will have default value [1, 0, 0]. """ # Select the correct grid from collections, if not provided first one will be used grid_obj = project.find_grid(grid_description=grid) floors = [] for floor_item in floor_list: if isinstance(floor_item[-1], list) or floor_item[-1] == 'x' or floor_item[-1] == 'y': coordinate_index_end = -3 element_x_axis = floor_item[-1] else: coordinate_index_end = -2 element_x_axis = None points = [] for xy_coordinate in floor_item[0:coordinate_index_end]: if isinstance(xy_coordinate[0], str) and isinstance(xy_coordinate[1], str): points.append(grid_obj[xy_coordinate] + [z_coordinate]) else: points.append(xy_coordinate + [z_coordinate]) floor = _viia_create_floor( project=project, name=layer, material=floor_item[coordinate_index_end], geometry=floor_item[coordinate_index_end + 1], points=[points], element_x_axis=element_x_axis) floors.append(floor) return floors
[docs]def _viia_create_walls_on_grid( project: ViiaProject, layer: Union[str, Layer], z_bottom: float, z_top: float, wall_list: List[List[Union[str, int]]], grid: Optional[Union[Grid, int, str]] = None, structural_type: str = 'psse') -> List[Wall]: """ This function is used to set up the walls for a specific layer, making use of a grid system. A wall list is given as input, in which each row represents a wall. Each wall is in turn represented by a list of four values; starting point, end point, material and thickness. The points to place the wall should be given in grid notation, for instance 'A3'. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - layer (str): String specifying the layer, conform VIIA naming convention. For example: 'N0'. - z_bottom (float): The z_coordinate of the bottom of the wall, in [m]. - z_top (float): The z_coordinate of the top of the wall, in [m]. - wall_list (list): List containing the information of the individual walls, for example [['D1', 'D9', 'MW-KLEI<1945', 210], ['D9', 'L9', 'MW-KLEI<1945', 210],[[0,0], [7.5,0], 'MW-KLEI<1945', 210]]. This will create masonry wall with a thickness of 210mm between grid point D1 and D9, a second wall between grid-points D9 and L9 and third wall between coordinates [0,0] and [7.5,0]. - grid (Grid, int, str): Optional object, id or name of the grid to use. By default, the first grid in the collection is used. - is_foundationwalls (bool): Optional boolean to create foundation walls. Default set to False. - structural_type (str): Optional structural type, can be either 'psse' or 'nsce'. Output: - Each wall described in the wall list is added to the project and the relevant collections. - A list containing all wall objects is returned. Example: .. code-block:: python project.viia_create_walls_on_grid( layer='N0', z_coordinate_bottom=0.0, z_coordinate_top=3.0, wall_list=[['D1', 'D9', 'MW-KLEI<1945', 210], ['D9', 'L9', 'MW-KLEI<1945', 210], [[0,0], [7.5,0], 'MW-KLEI<1945', 210]]) """ # Select the correct grid from collections, if not provided first one will be used grid_obj = project.find_grid(grid_description=grid) walls = [] for wall_item in wall_list: if isinstance(wall_item[0], str): x1, y1 = grid_obj[wall_item[0]] else: x1, y1 = wall_item[0] if isinstance(wall_item[1], str): x2, y2 = grid_obj[wall_item[1]] else: x2, y2 = wall_item[1] wall = _viia_create_wall( project=project, name=layer, material=wall_item[2], geometry=wall_item[3], points=[[[x1, y1, z_bottom], [x2, y2, z_bottom], [x2, y2, z_top], [x1, y1, z_top]]], structural_type=structural_type) walls.append(wall) return walls
### =================================================================================================================== ### 5. VIIA project settings for naming convention - Material creation ### ===================================================================================================================
[docs]def _viia_create_material( project: ViiaProject, material_name: str, hbv_span: Optional[float] = None, hbv_width: Optional[float] = None): """ This function contains all settings for the materials that are set by the naming convention used in the VIIA project. These settings are project specific. If material names that are passed are not conform naming convention, an error may be raised. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - material_name (str): Name of the material, using the standard for VIIA project (see UPR). - hbv_span (float): Span of the floor for timber floors and roofs, in [m]. - hbv_width (float): Width of the floor, perpendicular to the span, for timber floors and roofs, in [m]. Output: - Updates the material name depending on the VIIA project settings. - Returns the object reference to the material (if not present, it is created) """ # Name material in accordance to analysis type if project.analysis_type == 'SBS' and not material_name.startswith('LIN-'): material_name = f'LIN-{material_name}' # The elementsize influences the material model for masonry if _is_masonry(material_name) and 'LIN' not in material_name: # If the element size is already present in the material name nothing is done if not ('0.25x0.25' in material_name or '0.5x0.5' in material_name or '0.1x0.1' in material_name): # In the other case the elementsize is not present in the material name and is added material_name += '-' + str(project.elementsize) + 'x' + str(project.elementsize) # Check if this material has already been created for material_object in project.collections.materials: if material_object.name == material_name: return material_object # If it is not created yet, it is created return project.viia_materials(material_name=material_name, hbv_span=hbv_span, hbv_width=hbv_width)
### =================================================================================================================== ### 6. VIIA project settings for naming convention - Geometry creation ### ===================================================================================================================
[docs]def _viia_create_geometry(project: ViiaProject, geometry_name: str, material_object: Material, shape_object: Optional[Union[Shapes, str]] = None, class_type: Optional[str] = None): """ This function contains all settings for the geometries that are set by the naming convention used in the VIIA project. These settings are project specific. If geometry names that are passed are not conform naming convention, an error may be raised. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - geometry_name (str): Name of the geometry, using the standard for VIIA project (see UPR). - material_object (obj): Object reference to the material of the shape. - shape_object (obj): Object reference to the shape the material is applied on. If class_type is provided the shape is not required. Default value is None. Required if the class_type is not provided. - class_type (str): Optional input of the type of object to apply the geometry on, for example 'Floor' or 'Lintel', default value is None. Required if the shape_object is not provided. Output: - Updates the geometry name depending on the VIIA project settings. - Returns the object reference to the geometry (if not present, it is created) """ # Determine the name of the class of the shape the geometry is assigned to if class_type is None: if isinstance(shape_object, str): class_type = shape_object else: class_type = shape_object.__class__.__name__ # Update the name of the geometry for walls, floors and subclasses if class_type in ['Floor', 'Fstrip', 'Wall', 'Roof']: if len(str(geometry_name).split('-')) == 1: # When geometry has a length of 1, this should be a value and is the thickness of the shape in [mm] # The thickness in the geometry name is in [mm] and is set to general in [m] if 'COSTEEL' in material_object.name: thickness = round(float(geometry_name) / 1000, project.rounding_precision + 1) else: thickness = round(float(geometry_name) / 1000, project.rounding_precision) if class_type in ['Floor', 'Fstrip', 'Roof']: # Overwrite thickness if the shape material is of material group 'PLANKEN' or 'PLATEN' if 'PLATEN' in material_object.name or 'PLANKEN' in material_object.name: # Example material name: 'LIN-HBV-PLATEN-0.012-0.038-0.089-0.600' # formulas from https://royalhaskoningdhv.app.box.com/files/0/f/6184347125/1/f_100856578472 json_data = _viia_get_material_database(project) thickness_plate = float(material_object.name.split('-')[3]) width_beam = float(material_object.name.split('-')[4]) height_beam = float(material_object.name.split('-')[5]) spacing_beams = float(material_object.name.split('-')[6]) density_beam = json_data[0]['TimberFrame']['HSB']['density_beam'] density_plate = json_data[0]['TimberFrame']['HSB']['density_plate'] density_hbv = (density_plate * spacing_beams * thickness_plate + density_beam * width_beam * height_beam) / \ (spacing_beams * thickness_plate + width_beam * height_beam) thickness = round(((density_plate * spacing_beams * thickness_plate + density_beam * width_beam * height_beam) / density_hbv / spacing_beams), 5) # Update the geometry name geometry_name = '-'.join(['VLOERHBV', str(round(1000 * thickness, 2))]) elif 'COMBI-DATO' in material_object.name or 'COMBI-VBI' in material_object.name or \ 'LIN-RIB' in material_object.name or 'SANDWICH' in material_object.name or \ 'LEWIS' in material_object.name or 'GOLF' in material_object.name or \ 'UNIDEK' in material_object.name: json_data = _viia_get_material_database(project) if ('COMBI-DATO' in material_object.name or 'COMBI-VBI' in material_object.name) and \ project.analysis_type == 'SBS': material_group = 'Linear' elif 'GOLF' in material_object.name or 'UNIDEK' in material_object.name: material_group = 'LinearDirectStiffness' else: material_group = 'LinearOrthotropic' thickness = json_data[0][material_group][material_object.name]['thickness'] geometry_name = '-'.join(['VLOER', str(int(round(1000 * thickness, 0)))]) elif 'LIN-STDAK-SAB' in material_object.name or 'LIN-TROG-HOUT' in material_object.name: json_data = _viia_get_material_database(project) thickness = json_data[0]['LinearOrthotropicSteel'][material_object.name]['thickness'] geometry_name = '-'.join(['VLOER', str(int(round(1000 * thickness, 0)))]) elif 'KPV' in material_object.name: thickness = _viia_hollow_core_slabs(project=project, material_name=material_object.name)['equivalent thickness'] geometry_name = '-'.join(['VLOERKPV', str(int(round(1000 * thickness, 0)))]) elif 'LIN-COSTEEL' in material_object.name: geometry_name = '-'.join(['VLOER', str(round(1000 * thickness, 1))]) else: geometry_name = '-'.join(['VLOER', str(int(1000 * thickness))]) elif class_type == 'Wall': # Overwrite thickness if the shape material is of material group 'HSB' if 'HSB' in material_object.name: # Example material name: 'LIN-HSB-0.024-0.050-0.075-0.400' # formulas from https://royalhaskoningdhv.app.box.com/files/0/f/6184347125/1/f_100856578472 json_data = _viia_get_material_database(project) thickness_plate = float(material_object.name.split('-')[2]) width_beam = float(material_object.name.split('-')[3]) height_beam = float(material_object.name.split('-')[4]) spacing_beams = float(material_object.name.split('-')[5]) density_beam = json_data[0]['TimberFrame']['HSB']['density_beam'] density_plate = json_data[0]['TimberFrame']['HSB']['density_plate'] density_hsb = (density_plate * spacing_beams * thickness_plate + density_beam * width_beam * height_beam) / \ (spacing_beams * thickness_plate + width_beam * height_beam) thickness = round(((density_plate * spacing_beams * thickness_plate + density_beam * width_beam * height_beam) / density_hsb / spacing_beams), 5) # Update the geometry with the correct string if 'HSB' in material_object.name: geometry_name = '-'.join(['WANDHSB', str(1000 * thickness)]) else: geometry_name = '-'.join(['WAND', str(1000 * thickness)]) # Update the name of the geometry for columns if class_type in ['Column', 'Beam', 'Lintel']: if isinstance(geometry_name, (int, float)): geometry_name = 'D' + str(geometry_name) if class_type == 'Column': geometry_name = '-'.join(['KOLOM', str(geometry_name)]) elif class_type == 'Beam' or class_type == 'Lintel': if len(geometry_name.split('-')) == 1: # Initially the length of the geometry is 1 # there are no '-' in the geometry of beams in the modelscript if class_type == 'Lintel': subclass = 'LATEI' else: subclass = 'BALK' # Update the name of the geometry with the standardized items and follow up geometry_name = '-'.join([subclass, geometry_name]) if geometry_name == 'LIJNMASSA' and hasattr(material_object, 'material_model') and \ isinstance(material_object.material_model, LineMassMaterialModel): geometry_name += '-DISCREET' pass # Check if this geometry has already been created for geometry_object in project.collections.geometries: if geometry_object.name == geometry_name: return geometry_object # If it is not created yet, it is created return project.viia_geometries(geometry_name)
### =================================================================================================================== ### 7. VIIA project settings for naming convention - Connection creation ### ===================================================================================================================
[docs]def _viia_create_cavity_wall_tie(project: ViiaProject, inner_leaf: Wall, outer_leaf: Wall, point_inner: Union[List[float], Node], point_outer: Union[List[float], Node], material: Optional[Material] = None): """ Function to create a cavity wall tie between two walls. The two points that are given are added as internal points to their corresponding Wall object, and a spring is constructed between these two points. By default, the VIIA convention for cavity wall tie material is used. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - layer (str, Layer): String or Layer object to which the tie should be added. - inner_leaf (Wall): Object reference to the inner leaf wall. - outer_leaf (Wall): Object reference to the outer leaf wall. - point_inner (List, Node): Coordinate or Node on inner leaf. - point_outer (List, Node): Coordinate or Node on outer leaf. - material (Material): Optional object reference to a material for the tie. Output: - Spring object describing the cavity wall tie """ # Convert coordinates to nodes if not isinstance(point_inner, Node): point_inner = project.create_node(point_inner) if not isinstance(point_outer, Node): point_outer = project.create_node(point_outer) # Check if walls are parallel if not fem_parallel_vectors(inner_leaf.normal_vector(), outer_leaf.normal_vector()): raise ValueError(f"ERROR: Walls {inner_leaf.name} and {outer_leaf.name} are not parallel, cannot create cavity " f"wall tie.") # Check if direction of tie corresponds to normal vector of walls if not fem_parallel_vectors( inner_leaf.normal_vector(), fem_unit_vector_2_points(point_inner.coordinates, point_outer.coordinates)): raise ValueError( f"ERROR: Direction of cavity wall tie does not correspond to normal vectors of inner and outer leafs. " f"(Direction of tie {fem_unit_vector_2_points(point_inner.coordinates, point_outer.coordinates)} is not " f"the same as normal vector of inner leaf {inner_leaf.normal_vector()}).") # Create material if not given if not material: # Determine cavity distance ctc_distance = fem_length_vector(fem_vector_2_points(point_inner.coordinates, point_outer.coordinates)) cavity = \ ctc_distance - inner_leaf.geometry.geometry_model.thickness / 2 - \ outer_leaf.geometry.geometry_model.thickness / 2 # Create material based on cavity wall_tie_material_name = F"SPOUWANKER-{int(round(cavity * 1000, 0))}" # Get or create material material = project.viia_create_material(wall_tie_material_name) # Add nodes to wall objects as internal points if point_inner not in inner_leaf.get_nodes(): inner_leaf.add_internal_point(point_inner) if point_outer not in outer_leaf.get_nodes(): outer_leaf.add_internal_point(point_outer) # Assemble name and determine layer new_id = len(project.collections.springs) + 1 layer_name = inner_leaf.name.split('-')[0] name = f"{layer_name}-WANDEN-{inner_leaf.id}-SPOUWANKER-{new_id}" # Create spring object spring = project.create_spring( name=name, connecting_shapes={'source_connecting_shape': inner_leaf, 'target_connecting_shape': outer_leaf, 'source_shape_geometry': point_inner, 'target_shape_geometry': point_outer}, material=material) return spring
### =================================================================================================================== ### 8. Remove VIIA shape objects ### ===================================================================================================================
[docs]def _viia_remove_shape(project: ViiaProject, shape: Union[str, Shapes]): """ Function to remove a shape from project in the VIIA project. In VIIA certain data is saved to the meta-data. These object references should be removed as well at the moment the referenced shape is removed. Normal FEM procedure to remove the shape is performed afterward. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - shape (obj): Object reference of the shape to be removed. Alternative input can be the name or ID of the shape. Output: - Any reference in the meta-data for VIIA is removed. - The object is deleted in DIANA (if software is DIANA, model created and not a sub-shape). - Before deleting from PY-memory all collections are checked and updated. - The object is deleted in PY-memory. """ if isinstance(shape, str): shape = project.viia_get(collection='shapes', name=shape) elif isinstance(shape, int): shape = project.viia_get(collection='shapes', id=shape) if not isinstance(shape, Shapes): raise ValueError(f"ERROR: You are trying to remove a shape, but shape {shape} is not recognised or found.") project.write_log(f"Starting to remove shape '{shape.name}'.") for project_shape in project.collections.shapes: if project_shape.meta_data: if 'supported_wall' in project_shape.meta_data and project_shape.meta_data['supported_wall'] == shape: project_shape.meta_data['supported_wall'] = None if 'supported_column' in project_shape.meta_data and project_shape.meta_data['supported_column'] == shape: project_shape.meta_data['supported_column'] = None if 'foundation_wall' in project_shape.meta_data and project_shape.meta_data['foundation_wall'] == shape: project_shape.meta_data['foundation_wall'] = None if 'foundation_cavity_wall' in project_shape.meta_data and \ project_shape.meta_data['foundation_cavity_wall'] == shape: project_shape.meta_data['foundation_cavity_wall'] = None if 'equiv_dimensions' in project_shape.meta_data: if 'equiv_wall' in project_shape.meta_data['equiv_dimensions'] and \ project_shape.meta_data['equiv_dimensions']['equiv_wall'] == shape: project_shape.meta_data['equiv_dimensions']['equiv_wall'] = None if 'equiv_column' in project_shape.meta_data['equiv_dimensions'] and \ project_shape.meta_data['equiv_dimensions']['equiv_column'] == shape: project_shape.meta_data['equiv_dimensions']['equiv_column'] = None if 'normal_wall' in project_shape.meta_data['equiv_dimensions'] and \ project_shape.meta_data['equiv_dimensions']['normal_wall'] == shape: project_shape.meta_data['equiv_dimensions']['normal_wall'] = None if 'normal_column' in project_shape.meta_data['equiv_dimensions'] and \ project_shape.meta_data['equiv_dimensions']['normal_column'] == shape: project_shape.meta_data['equiv_dimensions']['normal_column'] = None if 'cavity_extension_wall' in project_shape.meta_data['equiv_dimensions'] and \ project_shape.meta_data['equiv_dimensions']['cavity_extension_wall'] == shape: project_shape.meta_data['equiv_extensions']['cavity_extension_wall'] = None shape.remove_shape()
### =================================================================================================================== ### 9. Getters for VIIA shape objects ### ===================================================================================================================
[docs]def viia_get( project: ViiaProject, collection: Optional[str] = 'all', name: Optional[str] = None, id: Optional[int] = None, unique_id: Optional[str] = None): """ This function will return the object with the name that is provided. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - collection (str): Name of the collection of shapes to look in. Default set to all collections present in the project instance. - name (str): Name that will be used for searching. - id (int): ID that will be used for searching. - unique_id (str): Unique-ID that will be used for searching. Output: - Returns the object with the provided name. If no object fulfills the requirements, None will be returned. """ if name and id: raise ValueError("ERROR: One one of 'name' or 'id' can be specified for viia_get") if not isinstance(name, str) and name is not None: raise ValueError("ERROR: Name should be a string") if not isinstance(id, int) and id is not None: raise ValueError("ERROR: ID should be a integer") if not isinstance(unique_id, str) and unique_id is not None: raise ValueError("ERROR: Unique-ID should be a string") # Get the collection collection = collection.lower() if collection == 'all': object_collection = project.collections.get_all_objects() elif hasattr(project.collections, collection): object_collection = getattr(project.collections, collection) else: if hasattr(project.collections, collection + 's'): object_collection = getattr(project.collections, collection + 's') else: raise ValueError(f"ERROR: The collection {collection} was not recognised.") def get_name(collection_list, name_getter): for list_item in collection_list: if hasattr(list_item, 'name'): if list_item.name == name_getter: return list_item if project.analysis_type == 'SBS' and 'LIN-' not in name_getter: alt_name_getter = name_getter.split('-') if type(list_item) in [Beam, Column, Floor, Roof, Wall, Fstrip]: lin_idx = 2 elem_size_idx = 4 elif isinstance(list_item, Material): lin_idx = 0 elem_size_idx = len(alt_name_getter) - 1 else: continue if _is_masonry(name_getter): if len(alt_name_getter) < elem_size_idx: continue del alt_name_getter[elem_size_idx] alt_name_getter.insert(lin_idx, 'LIN') alt_name_getter = '-'.join(alt_name_getter) if list_item.name == alt_name_getter: return list_item return None def get_id(collection_list, id_getter): for list_item in collection_list: if hasattr(list_item, 'id'): if list_item.id == id_getter: return list_item elif hasattr(list_item, 'ID'): if list_item.ID == id_getter: return list_item return None def get_unique_id(collection_list, unique_id_getter): for list_item in collection_list: if hasattr(list_item, 'unique_id'): if list_item.unique_id == unique_id_getter: return list_item return None item = None if name: item = get_name(object_collection, name) elif id: item = get_id(object_collection, id) elif unique_id: item = get_unique_id(object_collection, unique_id) if item: return item return None
[docs]def viia_get_shape( project: ViiaProject, collection_name: Optional[str] = 'all', name: Optional[str] = None, _id: Optional[int] = None, point: Optional[List[float]] = None, gridline: Optional[Union[Gridline, str]] = None, grid: Optional[Union[Grid, str]] = None, level: Optional[Union[Level, str]] = None, layer: Optional[Union[Layer, str]] = None) -> Optional[Shapes]: """ This function can be used to find a shape based on some queries from user. It will return a shape object. In case multiple shapes comply to the query, the first shape is returned and a warning is printed. The user can provide a name or ID of the shape. The shape with this name or ID is returned. The user can provide a point (x-, y- and z-coordinate as list). In this case, the function will return the shape for which the point is within the surface of the surface-shape (excluding the openings), the point is on the line of a line-shape or if the coordinate is the point of the point-shape. The user can also provide a gridline. In this case, the function will return the shape for which the gridline is aligned (vertically) and on the surface plane if it is a surface-shape, the shape is parallel to and on the gridline if it is a line-shape, or the point-shape if the point is on the vertical plane of the gridline. The user can also provide a level. In this case, the function will return a shape on the level (horizontally). Different queries can be combined to filter on multiple inputs at the same time. .. note:: Finding shape based on the gridline, level or coordinate is not available for volume-shapes. Input: - project (obj): Project object reference containing collections of fem objects and project variables. - collection_name (str): Name of the collection of shapes to look in. Default set to all collections present in the project instance. - name (str): Name that will be used for searching, this may be a part of the name too. Function is not case-sensitive. Default value is None, in which case it is not checking for name. - _id (int): ID that will be used for searching. Default value is None, in which case it is not checking for ID. - point (list of 3 floats): List of the x-, y- and z-coordinates of the point to be used for searching, values in [m]. Default value is None, in which case it is not checking for point. - gridline (obj or str): Object reference or name of the gridline, specifying the vertical plane in which the shape is searched. In case the gridline is specified as string, the grid should be provided too. This function then will check the gridlines of that grid. Default value is None, in which case it is not checking for gridline. - grid (obj or str): Object reference or name of the grid. The grid is only required if the gridline is specified as string. In that case the grid should contain the required gridline. In any other situation input for the grid is ignored. Default value is None. - level (obj or str): Object reference or name of the level, specifying the horizontal plane in which the shape is searched. Default value is None, in which case it is not checking for level. - layer (obj or str): Object reference or the name of the layer in which to search for the requested shape. Output: - Returns the shape object from the requested collection, filtered for the requested layer if any, filtered for the shapes that comply to the requested inputs. If no shape is found, None is returned. If multiple shapes have been found, the first one is returned. The user is notified of this. """ # Get the shape with the functionality in FEM-client return project.get_shape( collection_name=collection_name, name=name, _id=_id, point=point, gridline=gridline, grid=grid, level=level, layer=layer)
[docs]def viia_get_connection( project: ViiaProject, collection_name: Optional[str] = 'all', name: Optional[str] = None, _id: Optional[int] = None, point: Optional[List[float]] = None) -> Optional[Connections]: """ This function can be used to find a connection based on some queries from user. It will return the connection object. In case multiple conections comply to the query, the first connection is returned and a warning is printed. The user can provide a name or ID of the connection. The connection with this name or ID is returned. Note that in case of ID the input for the collections should be narrowed. The user can provide a point (x-, y- and z-coordinate as list). In this case, the function will return the connection for which the point is on the node, line or surface of the connection. Alternative input can be object reference of Node. .. note:: Finding connections is not available for volume-shapes with connections applied. Input: - project (obj): Project object reference containing collections of fem objects and project variables. - collection_name (str): Name of the collection of connections to look in. Default set to all collections for connections present in the project instance. - name (str): Name that will be used for searching, this may be a part of the name too. Function is not case-sensitive. Default value is None, in which case it is not checking for name. - _id (int): ID that will be used for searching. Default value is None, in which case it is not checking for ID. - point (list of 3 floats): List of the x-, y- and z-coordinates of the point to be used for searching, values in [m]. Default value is None, in which case it is not checking for point. Alternative input object reference of Node. Output: - Returns the connection object from the requested collection, filtered for the requested layer if any, filtered for the connections that comply to the requested inputs. If no connection is found, None is returned. If multiple connections have been found, the first one is returned. The user is notified of this. """ # Get the connection with the functionality in FEM-client return project.get_connection(collection_name=collection_name, name=name, _id=_id, point=point)
[docs]def viia_get_level(project: ViiaProject, shape: Shapes) -> Optional[Level]: """ Function to return the level of the shape. This is based on the VIIA naming convention. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - shape (obj): Shape of which the level is returned. Output: - Returns the level of the shape as a string, for example 'N0'. """ if shape not in project.collections.shapes: return None # Determine the level of the shape by the first part of the name return shape.name.split('-')[0]
### =================================================================================================================== ### 10. End of script ### =================================================================================================================== def viia_compare_shape_name(project, shape_name_1: str, shape_name_2: str, exclude: Optional[List[str]] = None) -> bool: """ This function can be used to compare the name of the shapes. Select parts of the name to exclude in the comparison. The function has been designed for the situation that the shapes are filtered between A10/A13 and A12/A15, but the density can change between them. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - shape_name_1, shape_name_2 (str): The name of the shape to be used in the comparison. - exclude (list of str): Excluding parts for the comparison. Default value is None, using ['DENSIT'], excluding any parts with adjusted density name-parts. Output: - Returns boolean if the shape-names are evaluated the same. """ if exclude is None: exclude = ['DENSIT'] # Comparison can be between "B1-VLOEREN-LIN-BETON-DENSIT214-150-1" and "B1-VLOEREN-LIN-BETON-150-1" # But also "B1-VLOEREN-LIN-BETON-DENSIT214-150-1" and "B1-VLOEREN-LIN-BETON-DENSIT400-150-1" def _split_and_remove_density(f: str) -> List[str]: """ Remove unwanted parts of the name for the comparison.""" return [p for p in f.split('-') if all([ex not in p for ex in exclude])] f1_parts = _split_and_remove_density(f=shape_name_1) f2_parts = _split_and_remove_density(f=shape_name_2) if len(f1_parts) != len(f2_parts): return False return all([part == f2_parts[i] for i, part in enumerate(f1_parts)]) ### =================================================================================================================== ### 11. End of script ### ===================================================================================================================