Source code for OpenSTRAN.Node

from dataclasses import dataclass, field, asdict

from .Coordinates import Coordinate

from typing import Any


[docs] @dataclass(slots=True) class Node(): """A class representing a node in the structural analysis model. :ivar coordinates: The coordinate object defining the node's position :type coordinates: Coordinate :ivar node_ID: Unique identifier for the node :type node_ID: int :ivar mesh_node: Indicates if this is a mesh node. Defaults to False :type mesh_node: bool :ivar Fx: Applied force in the x-direction in kips. Defaults to 0.0 :type Fx: float :ivar Fy: Applied force in the y-direction in kips. Defaults to 0.0 :type Fy: float :ivar Fz: Applied force in the z-direction in kips. Defaults to 0.0 :type Fz: float :ivar Mx: Applied moment about the x-axis in kip-ft. Defaults to 0.0 :type Mx: float :ivar My: Applied moment about the y-axis in kip-ft. Defaults to 0.0 :type My: float :ivar Mz: Applied moment about the z-axis in kip-ft. Defaults to 0.0 :type Mz: float :ivar restraint: List of 6 integers indicating restrained degrees of freedom [Ux, Uy, Uz, φx, φy, φz]. Defaults to [0, 0, 0, 0, 0, 0] :type restraint: list[int] :ivar plane: The plane associated with the node. Defaults to None :type plane: str | None """ coordinates: Coordinate node_ID: int mesh_node: bool = False Fx: float = 0.0 Fy: float = 0.0 Fz: float = 0.0 Mx: float = 0.0 My: float = 0.0 Mz: float = 0.0 Ux: float = 0.0 Uy: float = 0.0 Uz: float = 0.0 phi_x: float = 0.0 phi_y: float = 0.0 phi_z: float = 0.0 eFx: float = 0.0 eFy: float = 0.0 eFz: float = 0.0 eMx: float = 0.0 eMy: float = 0.0 eMz: float = 0.0 Rx: float = 0.0 Ry: float = 0.0 Rz: float = 0.0 Rmx: float = 0.0 Rmy: float = 0.0 Rmz: float = 0.0 plane: str | None = None restraint: list[int] = field(default_factory=lambda: [0, 0, 0, 0, 0, 0]) def __post_init__(self) -> None: """Set default restraints based on the plane constraint. If a plane is specified, sets the appropriate out-of-plane restraints. """ if self.plane is None: return elif self.plane == 'xy': self.restraint = [0, 0, 1, 1, 1, 0] elif self.plane == 'yz': self.restraint = [1, 0, 0, 0, 1, 1] elif self.plane == 'zx': self.restraint = [0, 1, 0, 1, 0, 1]
[docs] def properties(self) -> dict[str, Any]: """Return the dataclass properties as a dictionary. :returns: Dictionary of this instance's fields :rtype: dict[str, Any] """ return asdict(self)
[docs] def add_restraint(self, restraint: list[int]) -> None: """Add restraint to the nodal degrees of freedom. :param restraint: List of 6 integers (0 or 1) indicating which degrees of freedom are restrained. Format: [Ux, Uy, Uz, φx, φy, φz], where 1 means restrained and 0 means free. Examples: - Pinned node: [1, 1, 1, 0, 0, 0] - Fixed node: [1, 1, 1, 1, 1, 1] - Roller in x-direction: [0, 1, 1, 0, 0, 0] :type restraint: list[int] :raises ValueError: If restraint is not a list of exactly 6 integers, each being 0 or 1 """ # check the user defined list is properly formatted if type(restraint) == list and len(restraint) == 6 and restraint == [n for n in restraint if n in [0, 1]]: # restrain the node self.restraint = restraint else: raise ValueError('{msg1}\n{msg2}\n{msg3}\n{msg4}\n{msg5}\n{msg6}'.format( msg1=str('restraint: "{r}" not recognized.'.format( r=restraint ) ), msg2=str('restraint must be a list of 0s and 1s:'), msg3=str('Degrees of Freedom: [Ux, Uy, Uz, φx, φy, φz]'), msg4=str("Pinned Node: [1,1,1,0,0,0]"), msg5=str("Fixed Node: [1,1,1,1,1,1]"), msg6=str("Roller in the x direction: [0,1,1,0,0,0]") ))
[docs] def add_load(self, mag: float, lType: str = 'moment', direction: str = 'y') -> None: """Add a load to the node. :param mag: Magnitude of the load. Units are kips for forces and kip-ft for moments. :type mag: float :param lType: Type of load. Either 'moment' or 'force'. Defaults to 'moment'. :type lType: str :param direction: Direction of the load. 'X', 'Y', or 'Z'. Defaults to 'y'. :type direction: str :note: For moments, the magnitude is multiplied by 12 (kip-in to kip-ft conversion) """ if lType == 'moment': if direction == 'X': self.Mx += mag*12 if direction == 'Y': self.My += mag*12 if direction == 'Z': self.Mz += mag*12 else: if direction == 'X': self.Fx += mag if direction == 'Y': self.Fy += mag if direction == 'Z': self.Fz += mag