Skip to content

Refactor boundary condition #326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 89 additions & 51 deletions src/meshpy/core/boundary_condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,69 +22,107 @@
"""This module implements a class to represent boundary conditions in
MeshPy."""

import warnings as _warnings
from typing import Dict as _Dict
from typing import Optional as _Optional
from typing import Union as _Union

import meshpy.core.conf as _conf
from meshpy.core.base_mesh_item import BaseMeshItem as _BaseMeshItem
from meshpy.core.conf import mpy as _mpy
from meshpy.core.container import ContainerBase as _ContainerBase
from meshpy.core.geometry_set import GeometrySet as _GeometrySet
from meshpy.core.geometry_set import GeometrySetBase as _GeometrySetBase
from meshpy.utils.nodes import find_close_nodes as _find_close_nodes


class BoundaryConditionBase(_BaseMeshItem):
"""This is a base object, which represents one boundary condition in the
input file, e.g. Dirichlet, Neumann, coupling or beam-to-solid."""

def __init__(self, geometry_set, bc_type=None, **kwargs):
"""Initialize the object.

Args
----
geometry_set: GeometrySet
Geometry that this boundary condition acts on.
bc_type: mpy.bc
Type of the boundary condition.
"""Base class for boundary conditions."""

def __init__(
self,
geometry_set: _GeometrySetBase,
bc_type: _Union[_conf.BoundaryCondition, str],
**kwargs,
):
"""Initialize the boundary condition.

Args:
geometry_set: Geometry that this boundary condition acts on.
bc_type: Type of the boundary condition.
"""

super().__init__(**kwargs)
self.bc_type = bc_type
self.geometry_set = geometry_set

@classmethod
def from_dict(cls, geometry_set, bc_key, bc_dict):
"""This function acts as a factory and creates the correct boundary
condition object from a dictionary parsed from an input file."""

del bc_dict["E"]

if bc_key in (
_mpy.bc.dirichlet,
_mpy.bc.neumann,
_mpy.bc.locsys,
_mpy.bc.beam_to_solid_surface_meshtying,
_mpy.bc.beam_to_solid_surface_contact,
_mpy.bc.beam_to_solid_volume_meshtying,
) or isinstance(bc_key, str):
# Normal boundary condition (including beam-to-solid conditions).
from meshpy.four_c.boundary_condition import (
BoundaryCondition as _BoundaryCondition,
)

boundary_condition = _BoundaryCondition(
geometry_set, bc_dict, bc_type=bc_key
)
elif bc_key is _mpy.bc.point_coupling:
# Coupling condition.
from meshpy.core.coupling import Coupling as _Coupling

boundary_condition = _Coupling(
geometry_set,
bc_key,
bc_dict,
check_overlapping_nodes=False,
check_at_init=False,
)
else:
raise ValueError("Got unexpected boundary condition!")

boundary_condition.check()
return boundary_condition

class BoundaryCondition(BoundaryConditionBase):
"""This object represents one boundary condition, e.g., Dirichlet, Neumann,
..."""

def __init__(
self,
geometry_set: _GeometrySetBase,
data: _Dict,
bc_type: _Union[_conf.BoundaryCondition, str],
*,
double_nodes: _Optional[_conf.DoubleNodes] = None,
**kwargs,
):
"""Initialize the object.

Args:
geometry_set: Geometry that this boundary condition acts on.
data: Data defining the properties of this boundary condition.
bc_type: Type of the boundary condition.
double_nodes: Depending on this parameter, it will be checked if point
Neumann conditions do contain nodes at the same spatial positions.
"""

super().__init__(geometry_set, bc_type, data=data, **kwargs)
self.double_nodes = double_nodes

# Perform some sanity checks for this boundary condition.
self.check()

def check(self):
"""Check for point Neumann boundaries that there is not a double Node
in the set.

Duplicate nodes in a point Neumann boundary condition can lead
to the same force being applied multiple times at the same
spatial position, which results in incorrect load application.
"""

if self.double_nodes is _mpy.double_nodes.keep:
return

if (
self.bc_type == _mpy.bc.neumann
and self.geometry_set.geometry_type == _mpy.geo.point
):
my_nodes = self.geometry_set.get_points()
partners = _find_close_nodes(my_nodes)
# Create a list with nodes that will not be kept in the set.
double_node_list = []
for node_list in partners:
for i, node in enumerate(node_list):
if i > 0:
double_node_list.append(node)
if (
len(double_node_list) > 0
and self.double_nodes is _mpy.double_nodes.remove
):
# Create the a new geometry set with the unique nodes.
self.geometry_set = _GeometrySet(
[node for node in my_nodes if (node not in double_node_list)]
)
elif len(double_node_list) > 0:
_warnings.warn(
"There are overlapping nodes in this point Neumann boundary, and it is not "
"specified on how to handle them!"
)


class BoundaryConditionContainer(_ContainerBase):
Expand Down
47 changes: 21 additions & 26 deletions src/meshpy/core/coupling.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,47 +21,43 @@
# THE SOFTWARE.
"""This module implements a class to couple geometry together."""

from typing import List as _List
from typing import Union as _Union

import numpy as _np

import meshpy.core.conf as _conf
from meshpy.core.boundary_condition import (
BoundaryConditionBase as _BoundaryConditionBase,
)
from meshpy.core.conf import mpy as _mpy
from meshpy.core.geometry_set import GeometrySet as _GeometrySet
from meshpy.core.geometry_set import GeometrySetBase as _GeometrySetBase
from meshpy.core.node import Node as _Node


class Coupling(_BoundaryConditionBase):
"""Represents a coupling between geometries in 4C."""

def __init__(
self,
geometry,
coupling_type,
geometry: _Union[_GeometrySetBase, _List[_Node]],
coupling_type: _Union[_conf.BoundaryCondition, str],
coupling_dof_type,
*,
check_overlapping_nodes=True,
check_at_init=True,
check_overlapping_nodes: bool = True,
**kwargs,
):
"""Initialize this object.

Args
----
geometry: GeometrySet, [Nodes], int
Geometry that this boundary condition acts on.
coupling_type: mpy.bc
Type of point coupling.
coupling_dof_type: mpy.coupling_dof, str
If this is a string it is the string that will be used in the input
file, otherwise it has to be of type mpy.coupling_dof.
check_overlapping_nodes: bool
If all nodes of this coupling condition have to be at the same
physical position.
check_at_init: bool
If the previous check should be performed at initialization. This is required
when importing a coupling from an input file as the nodes themselves are not build
up when the coupling is read.
Args:
geometry: Geometry set or nodes that should be coupled.
coupling_type: Type of coupling.
coupling_dof_type: mpy.coupling_dof, str
If this is a string it is the string that will be used in the input
file, otherwise it has to be of type mpy.coupling_dof.
check_overlapping_nodes: If all nodes of this coupling condition
have to be at the same physical position.
"""

if isinstance(geometry, _GeometrySetBase):
Expand All @@ -84,9 +80,8 @@ def __init__(
self.coupling_dof_type = coupling_dof_type
self.check_overlapping_nodes = check_overlapping_nodes

if check_at_init:
# Perform the checks on this boundary condition
self.check()
# Perform sanity checks for this boundary condition
self.check()

def check(self):
"""Check that all nodes that are coupled have the same position
Expand All @@ -110,7 +105,7 @@ def dump_to_list(self):
condition."""

if isinstance(self.coupling_dof_type, dict):
bc_dict = self.coupling_dof_type
data = self.coupling_dof_type
else:
# In this case we have to check which beams are connected to the node.
# TODO: Coupling also makes sense for different beam types, this can
Expand Down Expand Up @@ -144,9 +139,9 @@ def dump_to_list(self):
"Coupling beams of different types is not yet possible!"
)

bc_dict = beam_four_c_type.get_coupling_dict(self.coupling_dof_type)
data = beam_four_c_type.get_coupling_dict(self.coupling_dof_type)

return [{"E": self.geometry_set.i_global, **bc_dict}]
return [{"E": self.geometry_set.i_global, **data}]


def coupling_factory(geometry, coupling_type, coupling_dof_type, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion src/meshpy/cosserat_curve/warping_along_cosserat_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
import quaternion as _quaternion
from numpy.typing import NDArray as _NDArray

from meshpy.core.boundary_condition import BoundaryCondition as _BoundaryCondition
from meshpy.core.conf import mpy as _mpy
from meshpy.core.geometry_set import GeometrySet as _GeometrySet
from meshpy.core.mesh import Mesh as _Mesh
from meshpy.core.node import Node as _Node
from meshpy.core.node import NodeCosserat as _NodeCosserat
from meshpy.core.rotation import Rotation as _Rotation
from meshpy.cosserat_curve.cosserat_curve import CosseratCurve as _CosseratCurve
from meshpy.four_c.boundary_condition import BoundaryCondition as _BoundaryCondition
from meshpy.four_c.function_utility import (
create_linear_interpolation_function as _create_linear_interpolation_function,
)
Expand Down
10 changes: 5 additions & 5 deletions src/meshpy/four_c/beam_interaction_conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
from typing import Optional as _Optional

import meshpy.core.conf as _conf
from meshpy.core.boundary_condition import BoundaryCondition as _BoundaryCondition
from meshpy.core.geometry_set import GeometrySet as _GeometrySet
from meshpy.core.mesh import Mesh as _Mesh
from meshpy.four_c.boundary_condition import BoundaryCondition as _BoundaryCondition


def get_next_possible_id_for_boundary_condition(
Expand Down Expand Up @@ -67,11 +67,11 @@ def get_next_possible_id_for_boundary_condition(

# compare string of each condition with input and store existing ids
for bc in found_conditions:
if condition_string in bc.bc_dict:
existing_ids.append(bc.bc_dict[condition_string])
if condition_string in bc.data:
existing_ids.append(bc.data[condition_string])
else:
raise KeyError(
f"The key {condition_string} is not in the bc_dict {bc.bc_dict}"
f"The key {condition_string} is not in the data {bc.data}"
)

# return lowest found id
Expand Down Expand Up @@ -125,7 +125,7 @@ def add_beam_interaction_condition(
for geometry_set in [geometry_set_1, geometry_set_2]:
mesh.add(
_BoundaryCondition(
geometry_set, bc_dict={condition_string: id}, bc_type=bc_type
geometry_set, data={condition_string: id}, bc_type=bc_type
)
)

Expand Down
2 changes: 1 addition & 1 deletion src/meshpy/four_c/beam_potential.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"""This file includes functions to ease the creation of input files using beam
interaction potentials."""

from meshpy.four_c.boundary_condition import BoundaryCondition as _BoundaryCondition
from meshpy.core.boundary_condition import BoundaryCondition as _BoundaryCondition


class BeamPotential:
Expand Down
Loading