### ===================================================================================================================
### 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
### ===================================================================================================================