Skip to content

Commit

Permalink
Added a list of "interface nodes" on FEM object. The interface nodes …
Browse files Browse the repository at this point in the history
…should be evaluated when joining Parts/Assemblies with eachother. If the nodes are coincident with nodes in other part, then it will replace interface node (and its uses) for the coincident node. This way you build models seperately and define which points you wish to connect to other models (assuming you know the coincident points).
  • Loading branch information
Krande committed Nov 3, 2021
1 parent 3f891c1 commit 13aed28
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/ada/concepts/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ def __contains__(self, item):
def __len__(self):
return len(self._nodes)

def __iter__(self):
def __iter__(self) -> Iterable[Node]:
return iter(self._nodes)

def __getitem__(self, index):
Expand Down
44 changes: 43 additions & 1 deletion src/ada/concepts/levels.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@

if TYPE_CHECKING:
from ada.fem.meshing import GmshOptions
from ada.fem.results import Results

_step_types = Union[StepSteadyState, StepEigen, StepImplicit, StepExplicit]

Expand Down Expand Up @@ -977,7 +978,7 @@ def to_fem(
exit_on_complete=True,
run_in_shell=False,
make_zip_file=False,
):
) -> "Results":
"""
Create a FEM input file deck for executing fem analysis in a specified FEM format.
Currently there is limited write support for the following FEM formats:
Expand Down Expand Up @@ -1241,6 +1242,41 @@ def user(self) -> User:
def convert_options(self) -> _ConvertOptions:
return self._convert_options

def __add__(self, other: Union[Assembly, Part]):
for n in other.fem.interface_nodes:
for p in self.get_all_parts_in_assembly(True):
res = p.fem.nodes.get_by_volume(n.p)
if res is not None:
replace_node = res[0]
for ref in n.refs:
if type(ref) is Connector:
if n == ref.n1:
ref.n1 = replace_node
if n == ref.n2:
ref.n2 = replace_node
elif type(ref) is Csys:
pass
# n_i = ref.nodes.index(n)
# ref.nodes.pop(n_i)
# ref.nodes.insert(n_i, replace_node)
else:
raise NotImplementedError()
break

for n in other.fem.nodes:
n.parent = self.fem
self.fem.nodes.add(n, allow_coincident=True)

for name, con in other.fem.connectors.items():
con.parent = self.fem
self.fem.connectors[name] = con

for name, con_sec in other.fem.connector_sections.items():
con_sec.parent = self.fem
self.fem.connector_sections[name] = con_sec

return self

def __repr__(self):
nbms = len([bm for p in self.get_all_subparts() for bm in p.beams]) + len(self.beams)
npls = len([pl for p in self.get_all_subparts() for pl in p.plates]) + len(self.plates)
Expand Down Expand Up @@ -1315,6 +1351,8 @@ class FEM:
initial_state: PredefinedField = field(default=None, init=True)
subroutine: str = field(default=None, init=True)

interface_nodes: List[Node] = field(init=False, default_factory=list)

def __post_init__(self):
self.nodes.parent = self
self.elements.parent = self
Expand Down Expand Up @@ -1491,6 +1529,10 @@ def add_spring(self, spring: Spring) -> Spring:
self.springs[spring.name] = spring
return spring

def add_interface_nodes(self, interface_nodes: List[Node]):
for n in interface_nodes:
self.interface_nodes.append(n)

def create_fem_elem_from_obj(self, obj, el_type=None) -> Elem:
"""Converts structural object to FEM elements. Currently only BEAM is supported"""
from ada.fem.shapes import ElemType
Expand Down
4 changes: 2 additions & 2 deletions src/ada/concepts/points.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

if TYPE_CHECKING:
from ada import Beam
from ada.fem import Bc, Elem
from ada.fem import Bc, Csys, Elem

numeric = Union[int, float, np.number]

Expand Down Expand Up @@ -83,7 +83,7 @@ def parent(self, value):
self._parent = value

@property
def refs(self) -> List[Union["Elem", "Beam"]]:
def refs(self) -> List[Union["Elem", "Beam", "Csys"]]:
return self._refs

def __getitem__(self, index):
Expand Down
11 changes: 7 additions & 4 deletions src/ada/fem/common.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, List

import numpy as np

from ada.config import Settings

if TYPE_CHECKING:
from ada import FEM
from ada import FEM, Node


class FemBase:
Expand Down Expand Up @@ -64,14 +64,17 @@ def __init__(
name,
definition=TYPES_DEFINITIONS.COORDINATES,
system=TYPES_SYSTEM.RECTANGULAR,
nodes=None,
nodes: List["Node"] = None,
coords=None,
metadata=None,
parent: "FEM" = None,
):
super().__init__(name, metadata, parent)
self._definition = definition
self._system = system
if nodes is not None:
for n in nodes:
n.refs.append(self)
self._nodes = nodes
self._coords = coords

Expand All @@ -84,7 +87,7 @@ def system(self):
return self._system

@property
def nodes(self):
def nodes(self) -> List["Node"]:
return self._nodes

@property
Expand Down
21 changes: 18 additions & 3 deletions src/ada/fem/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,15 @@ class Eccentricity:
sh_ecc_vector: np.ndarray = None


class ConnectorTypes:
BUSHING = "bushing"

all = [BUSHING]


class Connector(Elem):
CON_TYPES = ConnectorTypes

def __init__(
self,
name,
Expand All @@ -218,7 +226,7 @@ def __init__(
):
if type(n1) is not Node or type(n2) is not Node:
raise ValueError("Connector Start\\end must be nodes")
super(Connector, self).__init__(el_id, [n1, n2], "CONNECTOR")
super(Connector, self).__init__(el_id, [n1, n2], ElemType.CONNECTOR_SHAPES.CONNECTOR)
super(Elem, self).__init__(name, metadata, parent)
self._n1 = n1
self._n2 = n2
Expand All @@ -239,10 +247,18 @@ def con_sec(self) -> "ConnectorSection":
def n1(self) -> Node:
return self._n1

@n1.setter
def n1(self, value: Node):
self._n1 = value

@property
def n2(self) -> Node:
return self._n2

@n2.setter
def n2(self, value: Node):
self._n2 = value

@property
def csys(self) -> Csys:
return self._csys
Expand All @@ -263,8 +279,7 @@ def __init__(self, name, el_id, el_type, stiff, fem_set: "FemSet", metadata=None
self._fem_set = fem_set

@property
def fem_set(self):
""":rtype: ada.fem.sets.FemSet"""
def fem_set(self) -> "FemSet":
return self._fem_set

@property
Expand Down
2 changes: 2 additions & 0 deletions src/ada/fem/formats/abaqus/write/helper_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ def get_instance_name(obj, written_on_assembly_level: bool) -> str:
obj_on_assembly_level = False

if written_on_assembly_level is True and obj_on_assembly_level is False:
if obj.parent is None:
raise AttributeError
return f"{obj.parent.instance_name}.{obj_ref}"
else:
return str(obj_ref)
8 changes: 4 additions & 4 deletions src/ada/fem/formats/abaqus/write/write_connectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
from ada.fem import Connector, ConnectorSection


def connector_str(connector: "Connector", fem_writer) -> str:
def connector_str(connector: "Connector", written_on_assembly_level: bool) -> str:
csys_ref = "" if connector.csys is None else f'\n "{connector.csys.name}",'

end1 = get_instance_name(connector.n1, fem_writer)
end2 = get_instance_name(connector.n2, fem_writer)
end1 = get_instance_name(connector.n1, written_on_assembly_level)
end2 = get_instance_name(connector.n2, written_on_assembly_level)
return f"""**
** ----------------------------------------------------------------
** Connector element representing {connector.name}
Expand All @@ -25,7 +25,7 @@ def connector_str(connector: "Connector", fem_writer) -> str:
*Connector Section, elset={connector.name}, behavior={connector.con_sec.name}
{connector.con_type},{csys_ref}
**
{csys_str(connector.csys, fem_writer)}
{csys_str(connector.csys, written_on_assembly_level)}
**"""


Expand Down
4 changes: 2 additions & 2 deletions src/ada/fem/formats/abaqus/write/write_orientations.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def orientations_str(assembly: "Assembly", fem_writer) -> str:
return cstr.strip()


def csys_str(csys: "Csys", fem_writer):
def csys_str(csys: "Csys", written_on_assembly_level: bool):
name = csys.name
ori_str = f'*Orientation, name="{name}"'
if csys.nodes is None and csys.coords is None:
Expand All @@ -35,7 +35,7 @@ def csys_str(csys: "Csys", fem_writer):
if len(csys.nodes) != 3:
raise ValueError("CSYS number of nodes must be 3")
ori_str += ", SYSTEM=RECTANGULAR, DEFINITION=NODES\n {},{},{}".format(
*[get_instance_name(no, fem_writer) for no in csys.nodes]
*[get_instance_name(no, written_on_assembly_level) for no in csys.nodes]
)
else:
ax, ay, az = csys.coords[0]
Expand Down
2 changes: 1 addition & 1 deletion src/ada/fem/formats/abaqus/write/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ def connector_sections_str(self):

@property
def connectors_str(self):
return "\n".join([connector_str(con, self) for con in self.assembly.fem.connectors.values()])
return "\n".join([connector_str(con, True) for con in self.assembly.fem.connectors.values()])

@property
def amplitude_str(self):
Expand Down
2 changes: 1 addition & 1 deletion src/ada/fem/formats/calculix/write/write_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def elwriter(eltype, fem_sec: FemSection, elements: Iterable[Elem]):

sub_eltype = el_type_sub(eltype, fem_sec)
el_set_str = f", ELSET={fem_sec.elset.name}" if fem_sec.elset is not None else ""
el_str = "\n".join(map(write_elem, elements))
el_str = "\n".join((write_elem(el, True) for el in elements))

return f"""*ELEMENT, type={sub_eltype}{el_set_str}\n{el_str}\n"""

Expand Down
18 changes: 14 additions & 4 deletions src/ada/fem/sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class FemSet(FemBase):
TYPES = SetTypes

def __init__(
self, name, members: Union[None, List[Union["Elem", Node]]], set_type=None, metadata=None, parent=None
self, name, members: Union[None, List[Union["Elem", "Node", tuple]]], set_type=None, metadata=None, parent=None
):
super().__init__(name, metadata, parent)
if set_type is None:
Expand Down Expand Up @@ -69,14 +69,24 @@ def __repr__(self):
return f'FemSet({self.name}, type: "{self.type}", members: "{len(self.members)}")'


def eval_set_type_from_members(members: List[Union["Elem", Node]]):
def eval_set_type_from_members(members: List[Union["Elem", Node]]) -> str:
from ada.fem import Elem

res = set([type(mem) for mem in members])
if len(res) == 1 and type(members[0]) is Node:
return "nset"
return FemSet.TYPES.NSET
elif len(res) == 1 and type(members[0]) is Elem:
return "elset"
return FemSet.TYPES.ELSET
elif len(res) == 1 and type(members[0]) is tuple:
return FemSet.TYPES.NSET
else:
raise ValueError("Currently Mixed Femsets are not allowed")
# return "mixed"


def is_lazy(members: List[Union["Elem", Node]]) -> bool:
res = set([type(mem) for mem in members])
if len(res) == 1 and type(members[0]) is tuple:
return True
else:
return False
15 changes: 8 additions & 7 deletions src/ada/fem/shapes/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ class ShellShapes:
all = [TRI, TRI7, TRI6, QUAD, QUAD8]


class ConnectorShapes:
CONNECTOR = "CONNECTOR"
all = [CONNECTOR]


class SolidShapes:
HEX8 = "HEXAHEDRON"
HEX20 = "HEXAHEDRON20"
Expand Down Expand Up @@ -56,10 +61,7 @@ class ElemType:
SHELL_SHAPES = ShellShapes
SOLID_SHAPES = SolidShapes
POINT_SHAPES = PointShapes

MASSES = ["MASS", "ROTARYI"]
SPRINGS1n = ["SPRING1"]
CONNECTORS = ["CONNECTOR"]
CONNECTOR_SHAPES = ConnectorShapes

all = [SHELL, SOLID, LINE]

Expand Down Expand Up @@ -188,9 +190,8 @@ def is_valid_elem(elem_type):
ElemType.LINE_SHAPES.all
+ ElemType.SHELL_SHAPES.all
+ ElemType.SOLID_SHAPES.all
+ ElemType.MASSES
+ ElemType.SPRINGS1n
+ ElemType.CONNECTORS
+ ElemType.POINT_SHAPES.all
+ ElemType.CONNECTOR_SHAPES.all
)
valid_element_types_upper = [x.upper() for x in valid_element_types]
value = elem_type.upper()
Expand Down

0 comments on commit 13aed28

Please sign in to comment.