Source code for viiapackage.connections.auto_interfaces

### ===================================================================================================================
###   Automated interface creation
### ===================================================================================================================
# Copyright ©VIIA 2024

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

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

# References for functions and classes in the rhdhv_fem package
from rhdhv_fem.shapes import Pile, Reinforcements, LineMass

# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
    from viiapackage.viiaStatus import ViiaProject
from viiapackage.connections.details import Detail
from viiapackage.connections.helper_functions import viia_load_connections_library, viia_get_existing_connections,\
    _viia_check_input, _get_shape_direction, _get_identity_string_from_shape, _wrong_load_path, \
    _get_pairs_with_target, _convert2list, _filter_lines, _get_shape_type
from rhdhv_fem.geometries.geometry_models.profile_geometries import TrussProfile


### ===================================================================================================================
###   2. Function to auto generate all hinges in the model
### ===================================================================================================================

[docs]def viia_auto_interfaces( project: ViiaProject, is_linear: bool = False, override_connections: List[List[str]] = None, disable_connections: List[List[str]] = None, disable_interfaces: List[str] = None, replace_interfaces: Dict[str, Union[str, Any]] = None): """ This function generates interfaces in the model based on the BoD. It creates interfaces only between connecting shapes. The function uses the 'viia_auto_connections_library.yaml' in order to determine which connection type is to be applied. It does not create interfaces (hinges) between beams, therefore use the viia_auto_hinge function. Currently implemented interfaces: 'D2.01', 'D2.01a', 'D2.01b', 'D2.03', 'D2.07', 'D2.11', 'D2.14' and 'D4.02'. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - is_linear (bool): Boolean to apply linear or non-linear material properties for the interfaces. Default value is False, applying non-linear behaviour. - override_connections (list of list of str): List of lists of shape pairs to override automatic implementation. Input order for shapes is important and should follow definitions required for viia_create_connection(). e.g. [['source_shape_name', 'target_shape_name', 'detail', kwargs(opt)], ['N0-WANDEN-MW-KLEI<1945-100-2','N1-VLOEREN-LIN-HBV-PLANKEN-0.022-0.05-0.15-0.65-0','NOCON-L'], {'flip':True}]. Default value is None. - disable_connections (list of list of str): List of lists of shape pairs to disable automatic implementation. Input order for shapes is not important. Default value is None. e.g. [['source_shape_name', 'target_shape_name'], ['N0-WANDEN-MW-KLEI<1945-100-2','N1-VLOEREN-LIN-HBV-PLANKEN-0.022-0.05-0.15-0.65-0']]. - disable_interfaces (list of str): List of interfaces to disable, e.g. ['D2.01', 'D2.07']. Default value is None. In that case, the following list is used: ['D2.03', 'D2.07', 'D2.11', 'D2.14', 'D4.02', 'D2.01b'] This list is based on the current modelling strategy, and makes sure only the basic interfaces are applied. If no interfaces should be disabled, supply the following input value: [] . - replace_interfaces (dict): Dictionary of old to new interface mappings. Default value is None. e.g. {'D2.01': ['D2.01', {'is_linear': True}], 'D2.07': 'D2.01'}. """ reset_unit_check = False if project.rhdhvDIANA.model_created and project.check_units_bool: project.set_standard_units() project.check_units_bool = False reset_unit_check = True if project.analysis_type == 'SBS' and not is_linear: warnings.warn( f"Analysis type is SBS. Material models for interface connections changed to linear.") is_linear = True if override_connections is None: override_connections = [] # Default disabled interfaces, based on current modelling strategy if disable_interfaces is None: disable_interfaces = ['D2.03', 'D2.07', 'D2.11', 'D2.14', 'D4.02', 'D2.01b'] if replace_interfaces is None: replace_interfaces = {} if disable_connections is None: disable_connections = [] # Load yaml connections library database, conn_lookup = viia_load_connections_library(project) # set of shapes already connected connected = viia_get_existing_connections(project) # Check function input _viia_check_input(item=override_connections, name='override_connections') _viia_check_input(item=disable_connections, name='disable_connections') _viia_check_input(item=disable_interfaces, name='disable_interfaces') _viia_check_input(item=replace_interfaces, name='replace_interfaces') # Add disabled pairs to already connected for pair in disable_connections: connected.add(frozenset((pair[0], pair[1]))) project.write_log(f"INFO: {str(pair)} is set to be disabled by the user for automatic connections.") # Dictionary for shapes to be connected connections = {} # Set for shapes to be disconnected to avoid unnecessary unites to_disconnect = set() for shape in \ [shape for shape in project.collections.shapes if not isinstance(shape, (Pile, Reinforcements, LineMass))]: conn_shapes = shape.get_connecting_shapes() if not conn_shapes: project.write_log( f"WARNING: {shape.name} is not connected to anything! It might be a modeling mistake! " f"It will be ignored for automatic connections.") continue for conn_shape in \ [shape for shape in conn_shapes if not isinstance(shape, (Pile, Reinforcements, LineMass))]: pair = frozenset((shape.name, conn_shape.name,)) if pair in connections or pair in connected: continue connecting_lines = shape.get_connecting_lines(shape=conn_shape, include_openings=False) if connecting_lines: shape_dir = connecting_lines[0] shape_direction = _get_shape_direction(shape_dir) conn_shape_direction = _get_shape_direction(shape_dir) else: shape_direction = _get_shape_direction(shape) conn_shape_direction = _get_shape_direction(conn_shape) shape_info = _get_identity_string_from_shape(shape, conn_shape_direction) conn_shape_info = _get_identity_string_from_shape(conn_shape, shape_direction) connection_fingerprint = frozenset((conn_shape_info, shape_info,)) if connection_fingerprint not in conn_lookup: continue if _wrong_load_path(shape, conn_shape) and not conn_lookup[connection_fingerprint]['ignore_path']: _, _, applicable = Detail.get_detail( project=project, detail_nr='NOCON-L').get_master_slave_relationship( project=project, shape_1=shape, shape_2=conn_shape) if applicable: connections[pair] = {'master': shape.name, 'slave': conn_shape.name, 'type': 'NOCON-L'} continue # Determine master, slave and if connection is applicable for interface type connection_detail = conn_lookup[connection_fingerprint]['connection'] detail_obj = Detail.get_detail(project=project, detail_nr=connection_detail) master, slave, applicable = detail_obj.get_master_slave_relationship( project=project, shape_1=shape, shape_2=conn_shape) if applicable: connections[pair] = {'master': master.name, 'slave': slave.name, 'type': connection_detail} to_disconnect |= _get_pairs_with_target(master, slave) # Apply automatic exceptions for shape in [shape for shape in project.collections.shapes if not isinstance(shape, (Pile, Reinforcements))]: for exception in database['Exceptions']: disconnect_details = _convert2list(exception.get('disconnect_detail', [])) if exception['key'] in shape.name: connection_made = False conn_shapes = shape.get_connecting_shapes() types = {} disconnects = set() for conn_shape in [shape for shape in conn_shapes if not isinstance(shape, (Pile, Reinforcements))]: connecting = None if exception['type'] == 'line': connecting = shape.get_connecting_lines(shape=conn_shape, include_openings=False) else: connecting = shape.get_connecting_nodes(shape=conn_shape, include_openings=False) if not connecting: disconnects.add(conn_shape) continue conn_shape_type = _get_shape_type(conn_shape.name) types[conn_shape_type] = conn_shape disconnects.add(conn_shape) for desired_type in _convert2list(exception.get('connect')): if desired_type in types: selected_shape = types[desired_type] disconnects.remove(selected_shape) pair = frozenset((shape.name, selected_shape.name)) if pair not in connected: connections[pair] = { 'master': shape.name, 'slave': selected_shape.name, 'type': exception['connect_detail']} connection_made = True break # Only apply disconnects if a connection was made if connection_made: for disconnect in disconnects: pair = frozenset((shape.name, disconnect.name)) if pair not in connections and pair not in connected: for detail in disconnect_details: # Check if detail is applicable detail_obj = Detail.get_detail(project=project, detail_nr=detail) _, _, applicable = detail_obj.get_master_slave_relationship( project=project, shape_1=shape, shape_2=disconnect) if applicable: connections[pair] = { 'master': shape.name, 'slave': disconnect.name, 'type': detail} break # Manual exception application oder: # Apply exceptions # Replace interfaces # Disable interfaces # Apply Exceptions for exception in override_connections: pair = frozenset((exception[0], exception[1])) if len(exception) == 3: connections[pair] = { 'master': exception[0], 'slave': exception[1], 'type': exception[2]} else: connections[pair] = { 'master': exception[0], 'slave': exception[1], 'type': exception[2], 'kwargs': exception[3]} # apply disconnects for pair in to_disconnect: if pair not in connections and pair not in connected: pair_l = list(pair) connections[pair] = {'master': pair_l[0], 'slave': pair_l[1], 'type': 'NOCON-L'} # Store truss elements to implement the exception later truss_objects = list() for line in project.collections.lines: if isinstance(line.geometry.geometry_model, TrussProfile): truss_objects.append(line.name) # Apply interfaces for connection in connections.values(): conn = connection['type'] # Replace interfaces if connection['type'] in replace_interfaces: conn = replace_interfaces[connection['type']] if isinstance(conn, list): connection["kwargs"] = conn[1] conn = conn[0] project.write_log( f"WARNING: Connection type {connection['type']} is being replaced by {conn} per users request.") # Disable Interfaces if conn in disable_interfaces: project.write_log(f"Warning: Connection type {conn} is being disabled per users request.", print_message=False) continue # Exception for truss elements if truss_objects: if connection['master'] in truss_objects: project.write_log( f"WARNING: Connection is not created for {connection['master']}, as it is a truss element.") continue if connection['slave'] in truss_objects: project.write_log( f"WARNING: Connection is not created for {connection['slave']}, as it is a truss element.") continue kwargs = connection.get('kwargs', {}) kwargs['is_linear'] = kwargs.get('is_linear', is_linear) project.viia_create_connection(source=connection['master'], target=connection['slave'], detail=conn, **kwargs) project.write_log( f"INFO: connection is defined between shapes {connection['master']} and {connection['slave']} with " f"type {conn}.") if reset_unit_check: project.check_units_bool = True
### =================================================================================================================== ### 3. End of script ### ===================================================================================================================