Coverage for src/meshpy/core/coupling.py: 94%
35 statements
« prev ^ index » next coverage.py v7.9.0, created at 2025-06-13 04:26 +0000
« prev ^ index » next coverage.py v7.9.0, created at 2025-06-13 04:26 +0000
1# The MIT License (MIT)
2#
3# Copyright (c) 2018-2025 MeshPy Authors
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to deal
7# in the Software without restriction, including without limitation the rights
8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9# copies of the Software, and to permit persons to whom the Software is
10# furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in
13# all copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21# THE SOFTWARE.
22"""This module implements a class to couple geometry together."""
24from typing import List as _List
25from typing import Union as _Union
27import numpy as _np
29import meshpy.core.conf as _conf
30from meshpy.core.boundary_condition import (
31 BoundaryConditionBase as _BoundaryConditionBase,
32)
33from meshpy.core.conf import mpy as _mpy
34from meshpy.core.geometry_set import GeometrySet as _GeometrySet
35from meshpy.core.geometry_set import GeometrySetBase as _GeometrySetBase
36from meshpy.core.node import Node as _Node
39class Coupling(_BoundaryConditionBase):
40 """Represents a coupling between geometries in 4C."""
42 def __init__(
43 self,
44 geometry: _Union[_GeometrySetBase, _List[_Node]],
45 coupling_type: _Union[_conf.BoundaryCondition, str],
46 coupling_dof_type: _Union[_conf.CouplingDofType, dict],
47 *,
48 check_overlapping_nodes: bool = True,
49 ):
50 """Initialize this object.
52 Args:
53 geometry: Geometry set or nodes that should be coupled.
54 coupling_type: If this is a string, this will be the section that
55 this coupling will be added to. If it is a mpy.bc, the section
56 will be determined automatically.
57 coupling_dof_type: If this is a dictionary it is the dictionary
58 that will be used in the input file, otherwise it has to be
59 of type mpy.coupling_dof.
60 check_overlapping_nodes: If all nodes of this coupling condition
61 have to be at the same physical position.
62 """
64 if isinstance(geometry, _GeometrySetBase):
65 pass
66 elif isinstance(geometry, list):
67 geometry = _GeometrySet(geometry)
68 else:
69 raise TypeError(
70 f"Coupling expects a GeometrySetBase item, got {type(geometry)}"
71 )
73 # Couplings only work for point sets
74 if (
75 isinstance(geometry, _GeometrySetBase)
76 and geometry.geometry_type is not _mpy.geo.point
77 ):
78 raise TypeError("Couplings are only implemented for point sets.")
80 super().__init__(geometry, bc_type=coupling_type, data=coupling_dof_type)
81 self.check_overlapping_nodes = check_overlapping_nodes
83 # Perform sanity checks for this boundary condition
84 self.check()
86 def check(self):
87 """Check that all nodes that are coupled have the same position
88 (depending on the check_overlapping_nodes parameter)."""
90 if not self.check_overlapping_nodes:
91 return
93 nodes = self.geometry_set.get_points()
94 diff = _np.zeros([len(nodes), 3])
95 for i, node in enumerate(nodes):
96 # Get the difference to the first node
97 diff[i, :] = node.coordinates - nodes[0].coordinates
98 if _np.max(_np.linalg.norm(diff, axis=1)) > _mpy.eps_pos:
99 raise ValueError(
100 "The nodes given to Coupling do not have the same position."
101 )
104def coupling_factory(geometry, coupling_type, coupling_dof_type, **kwargs):
105 """Create coupling conditions for the nodes in geometry.
107 Some solvers only allow coupling conditions containing two points at
108 once, in that case we have to create multiple coupling conditions
109 between the individual points to ensure the correct representation
110 of the coupling.
111 """
113 if coupling_type.is_point_coupling_pairwise():
114 main_node = geometry[0]
115 return [
116 Coupling([main_node, node], coupling_type, coupling_dof_type, **kwargs)
117 for node in geometry[1:]
118 ]
119 else:
120 return [Coupling(geometry, coupling_type, coupling_dof_type, **kwargs)]