"""
Defines various utilities including:
- parse_patran_syntax
- parse_patran_syntax_dict
- Position
- PositionWRT
- transform_load
"""
from __future__ import annotations
from copy import deepcopy
from typing import TYPE_CHECKING
import numpy as np # type: ignore
from numpy import cross, dot # type: ignore
from pyNastran.utils import deprecated
from pyNastran.utils.numpy_utils import integer_types
from pyNastran.bdf.patran_utils.colon_syntax import (
parse_patran_syntax, parse_patran_syntax_dict, parse_patran_syntax_dict_map,
write_patran_syntax_dict) # pragma: disable=unused-import
if TYPE_CHECKING: # pragma: no cover
from pyNastran.nptyping_interface import NDArray3float
from pyNastran.bdf.bdf import BDF
from pyNastran.bdf.cards.coordinate_systems import Coord
[docs]
def parse_femap_syntax(lines: list[str]) -> np.ndarray:
"""Parses the following syntax from FEMAP:
Add 1646 0 1
Add 1422 1502 1
Add 1505 1645 1
.. note:: A list of lines is expected
"""
assert isinstance(lines, list), lines
values = []
for line in lines:
line = line.strip()
if len(line) == 0:
continue
assert '\n' not in line, line
sline = line.split()
assert len(sline) == 4, sline
assert sline[0] == 'Add', sline
word, start, stop, step = sline
istart = int(start)
if stop == '0':
values.append(istart)
elif step == '1':
istop = int(stop)
valuesi = np.arange(istart, istop+1)
values.append(valuesi)
else:
istop = int(stop)
istep = int(step)
valuesi = np.arange(istart, istop+1, istep)
values.append(valuesi)
values2 = np.unique(np.hstack(values))
return values2
def _get_femap_comments_dict(data_dict, word: str):
word = word.lower()
comment_dict = {}
for pid, prop in data_dict.items():
lines = prop.comment.split('\n')
commenti = ''
for line in lines:
if word in line.lower():
base, commenti = line.split(':', 1)
break
comment_dict[pid] = commenti.strip()
return comment_dict
[docs]
def Position(xyz: NDArray3float, cid: int, model: BDF) -> np.ndarray:
"""
Gets the point in the global XYZ coordinate system.
Parameters
----------
xyz : (3,) ndarray
the position of the GRID in an arbitrary coordinate system
cid : int
the coordinate ID for xyz
model : BDF()
the BDF model object
Returns
-------
xyz2 : (3,) ndarray
the position of the GRID in an arbitrary coordinate system
"""
cp_ref = _coord(model, cid)
xyz2 = cp_ref.transform_node_to_global(xyz)
return xyz2
[docs]
def PositionWRT(xyz: NDArray3float, cid: int, cid_new: int, model: BDF) -> NDArray3float:
"""
Gets the location of the GRID which started in some arbitrary system and
returns it in the desired coordinate system
Parameters
----------
xyz : (3, ) float ndarray
the position of the GRID in an arbitrary coordinate system
cid : int
the coordinate ID for xyz
cid_new : int
the desired coordinate ID
model : BDF()
the BDF model object
Returns
-------
xyz_local : (3, ) float ndarray
the position of the GRID in an arbitrary coordinate system
"""
if cid == cid_new: # same coordinate system
return xyz
cp_ref = _coord(model, cid)
coord_to_ref = _coord(model, cid_new)
if 0: # pragma: no cover
# pGlobal = pLocal1 * beta1 + porigin1
# pGlobal = pLocal2 * beta2 + porigin2
# pLocal1 * beta1 + porigin1 = pLocal2 * beta2 + porigin2
# plocal1 * beta1 + porigin1 - porigin2 = plocal2 * beta2
# (plocal1 * beta1 + porigin1 - porigin2) * beta2.T = plocal2
# convert R-Theta-Z_1 to xyz_1
p1_local = cp_ref.coord_to_xyz(xyz)
# transform xyz_1 to xyz_2
p2_local = dot(
(p1_local @ cp_ref.beta()) + cp_ref.origin - coord_to_ref.origin,
coord_to_ref.beta().T)
# convert xyz_2 to R-Theta-Z_2
xyz_local = coord_to_ref.xyz_to_coord(p2_local)
else:
# converting the xyz point arbitrary->global
xyz_global = cp_ref.transform_node_to_global(xyz)
# now converting it to the output coordinate system
xyz_local = coord_to_ref.transform_node_to_local(xyz_global)
return xyz_local
[docs]
def get_xyz_cid0_dict(model: BDF,
xyz_cid0: dict[int, NDArray3float]=None) -> dict[int, NDArray3float]:
"""
helper method
Parameters
----------
model : BDF()
a BDF object
xyz_cid0 : None / dict[int] = (3, ) ndarray
the nodes in the global coordinate system
Returns
-------
xyz_cid0_dict
"""
if xyz_cid0 is None:
xyz = {}
for nid, node in model.nodes.items():
xyz[nid] = node.get_position()
else:
xyz = xyz_cid0
return xyz
[docs]
def split_eids_along_nids(model: BDF, eids: list[int], nids: list[int]) -> None:
"""
Disassociate a list of elements along a list of nodes.
The expected use of this function is that you have two bodies that
are incorrectly equivalenced and you would like to create duplicate
nodes at the same location and associate the new nodes with one half
of the elements.
Pick the nodes along the line and the elements along one side of the line.
Parameters
----------
model : BDF()
the BDF model
eids : list/tuple
element ids to disassociate
nids : list/tuple
node ids to disassociate
Implicitly returns model with additional nodes.
Notes
-----
xref should be set to False for this function.
"""
#assert model.xref == False, model.xref
nid = max(model.nodes.keys()) + 1
nid_map = {}
for nidi in nids:
node = model.nodes[nidi]
node2 = deepcopy(node)
node2.nid = nid
model.nodes[nid] = node2
nid_map[nidi] = nid
nid += 1
for eid in eids:
nodes = []
elem = model.elements[eid]
for nidi in elem.nodes:
if nidi in nid_map:
nodes.append(nid_map[nidi])
else:
nodes.append(nidi)
assert len(np.unique(nodes)) == len(nodes), 'nodes=%s' % nodes
elem.nodes = nodes
def _coord(model: BDF, cid: int) -> Coord:
"""helper method"""
if isinstance(cid, integer_types):
cp_ref = model.Coord(cid)
else:
cp_ref = cid
return cp_ref