Modules

tnsgrt.prism

class tnsgrt.prism.Prism(p: int = 3, top_radius: float = 1, bottom_radius: float = 1, height: float = 1, alpha: float | None = None, calculate_equilibrium=True, equilibrium_method=typing.Literal['analytic', 'numeric'], **kwargs)

Constructs a Snelson prism with p bars

Parameters:
  • p – number of bars

  • top_radius – the top radius

  • bottom_radius – the bottom radius

  • height – the radius of the prism

  • alpha – the twist angle

  • calculate_equilibrium – if True calculates equilibrium

  • equilibrium_method – choice of method for computing equilibrium

  • **kwargs – See below

Keyword Arguments:
  • bar (bool=True) – if True add bars

  • bottom (bool=True) – if True add bottom strings

  • top (bool=True) – if True add top strings

  • vertical (bool=True) – if True add vertical strings

  • diagonal (bool=False) – if True add diagonal strings

Notes:

  1. Unloaded equilibrium is possible only if

    \[\frac{\pi}{2} - \frac{\pi}{p} \leq \alpha \leq \frac{\pi}{2}\]

    when both vertical and diagonal strings are present

  2. If diagonal strings are not present, then unloaded equilibrium is possible only if

    \[\alpha = \frac{\pi}{2} - \frac{\pi}{p}\]

    This is the default prism configuration

  3. If vertical strings are not present, then unloaded equilibrium is possible only if

    \[\alpha = \frac{\pi}{2}\]
  4. Additional keyword arguments are passed to tnsgrt.structure.Structure

tnsgrt.michell

class tnsgrt.michell.Michell(n: int = 6, beta: float = 0.7853981633974483, q: int = 4, radius: float = 1, **kwargs)

Constructs a circular Michell truss with n sides and q layers

Parameters:
  • n – the number of sides

  • beta – the angle between the radius and the outermost bar

  • q – the number of layers

  • radius – the radius of the structure

  • **kwargs – See below

Keyword Arguments:
  • spiral (bool=True) – if True add spiral members

  • radial (bool=True) – if True add radial members

  • outer (bool=True) – if True add outer members

  • inner (bool=True) – if True add inner members

  • center (bool=True) – if True add center members

Notes:

  1. If \(\rho\) is equal to radius, then the radii as a function of the layer number i is

\[r(i) = \rho \, a^i, \qquad a = \frac{\beta}{\sin(\beta + \pi)}\]
  1. The radii are convergent only if \(a < 1\), that is

    \[\beta < \frac{\pi}{2} - \frac{\pi}{2 n}\]

    The default \(\beta = \pi/4\) leads to convergent designs for all \(n > 2\).

  2. Additional keyword arguments are passed to tnsgrt.structure.Structure

tnsgrt.structure

class tnsgrt.structure.Property

Base class for storing properties

Derived classes should implement dataclass from dataclasses

class tnsgrt.structure.Structure(nodes: _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] = array([], shape=(3, 0), dtype=float64), members: _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] = array([], shape=(2, 0), dtype=int64), number_of_strings: int = 0, node_tags: Dict[str, ndarray[Any, dtype[int64]]] | None = None, member_tags: Dict[str, ndarray[Any, dtype[int64]]] | None = None, label: str = None)

A Structure object

Parameters:
  • nodes – a 3 x n array representing the Structure’s nodes

  • members – a 3 x m array representing the Structure’s members

  • number_of_strings – the number of strings in Structure

  • node_tags – a dictionary with the node tags

  • member_tags – a dictionary with the member tags

  • label – the Structure’s label

class MemberProperty(lambda_: float = 0.0, force: float = 0.0, stiffness: float = 0.0, volume: float = 0.0, radius: float = 0.01, inner_radius: float = 0, mass: float = 1.0, rest_length: float = 0.0, yld: float = 250000000.0, density: float = 7850.0, modulus: float = 200000000000.0, visible: bool = True, facecolor: object = (1, 0, 0), edgecolor: object = (1, 0, 0), linewidth: int = 2, linestyle: str = '-')

Subclass representing properties of members

class NodeProperty(radius: float = 0.002, visible: bool = True, constraint: object = None, facecolor: object = (0, 0.447, 0.741), edgecolor: object = (0, 0.447, 0.741))

Subclass representing properties of nodes

add_member_tag(tag: str, indices: int | ndarray[Any, dtype[int64]]) None

Add members with indices in indices to the member tag tag

Create tag if it does not already exist

Parameters:
  • tag – the member tag

  • indices – the member indices

add_members(members: _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], number_of_strings: int | None = None, member_tags: Dict[str, ndarray[Any, dtype[int64]]] | None = None) None

Add members and tags to current structure

Parameters:
  • members – the members to be added

  • number_of_strings – the number of strings; if not None, then the first number_of_strings members are tagged as ‘strings’ and the remaining members as ‘bars’

  • member_tags – the new members’ tags

add_node_tag(tag: str, indices: ndarray[Any, dtype[int64]]) None

Add nodes with indices in indices to the node tag tag

Create tag if it does not already exist

Parameters:
  • tag – the node tag

  • indices – the node indices

add_nodes(nodes: _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], node_tags: Dict[str, ndarray[Any, dtype[int64]]] | None = None) None

Add nodes to the Structure

Parameters:
  • nodes – the nodes to add

  • node_tags – the node tags to add

copy() Structure
Returns:

copy of the Structure

delete_member_tag(tag: str) None

Delete member tag tag

Parameters:

tag – the member tag to be deleted

delete_node_tag(tag: str) None

Delete node tag tag

Parameters:

tag – the node tag to be deleted

equilibrium(force: _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | None = None, lambda_bar: float | None = None, equalities: List[_SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]] | None = None, epsilon: float = 1e-07) ndarray[Any, dtype[ScalarType]]

Solves for the set of internal forces that ensures the equilibrium of the current Structure in response to the vector of external forces forces

Solve the force equilibrium equation

\[A \lambda = f, \quad \lambda \in \Lambda\]

in which:

  • \(A\): is a matrix representing the element vectors

  • \(f\): is the vector of external forces

  • \(\lambda\): is the vector of force coefficients

  • \(\Lambda\): is a set of constraints on the force coefficients

    (see Notes below)

Parameters:
  • force – a 3 x n array of external forces or None

  • lambda_bar – the normalizing factor

  • equalities – a list of lists of member indices which are constrained to have the same force coefficient

  • epsilon – numerical accuracy

Notes:

  1. If the \(i\) th element is a string then

    \[\Lambda = \{ \lambda : \qquad \lambda_i \geq 0 \quad \text{ if } i\text{th element is a string} \}\]
  2. All elements in equalities are set equal. For example, if:

    equalities = [[0, 1, 3], [2, 4]]
    

    then the constraints

    \[\lambda_0 = \lambda_1 = \lambda_3, \qquad \lambda_2 = \lambda_4\]

    are added to the constraint set \(\Lambda\).

  3. If force=None then the sum of the bar force coefficients equals lambda_bar. That is, the following modified problem is solved:

    \[A \lambda = 0, \quad \mathbf{e}^T \lambda = \bar{\lambda}, \quad \lambda \in \Lambda\]

    in which \(\mathbf{e}\) is a vector that has 1 for bars and 0 for strings.

  4. If force is not None and lambda_bar is also not None then the following problem is solved

    \[A \lambda = f, \quad \mathbf{e}^T \lambda = \bar{\lambda}, \quad \lambda \in \Lambda\]

    WARNING: This problem may not be feasible for all \(\bar{\lambda} > 0\)!

get_center_of_mass() ndarray[Any, dtype[float64]]
Returns:

the Structure’s center of mass

get_centroid() ndarray[Any, dtype[float64]]
Returns:

the Structure’s geometric center or centroid

get_close_nodes(radius=1e-06) Tuple[Set[int], ndarray[Any, dtype[ScalarType]]]

Returns the set of nodes that lie within radius distance to other nodes in Structure and the corresponding map

For example, if:

close_nodes, close_nodes_map = s.get_close_nodes()

is such that:

close_nodes = {1, 3}
close_nodes_map = [0,0,2,2,4]

then node 1 is close to node 0 and node 3 is close to node 2.

Parameters:

radius – the proximity radius

Returns:

tuple with close_nodes and close_nodes_map

get_colinear_nodes(epsilon: float = 1e-08, return_members: bool = False) ndarray[Any, dtype[int64]] | Tuple[ndarray[Any, dtype[int64]], ndarray[Any, dtype[int64]]]
Params epsilon:

accuracy of co-linearity test

Params return_members:

if True returns tuple of member indices as well

Returns:

an array with the indices of the co-linear nodes or tuple with an array with the indices of the co-linear nodes and an array with the member indices

Notes:

  1. Co-linear nodes are nodes that have only 2 co-linear members connected to them

get_member_length() ndarray[Any, dtype[float64]]
Returns:

an array with the Structure’s member lengths

get_member_properties(index: int | Sequence[int] | slice, *labels: str) DataFrame

Retrieve member properties

Parameters:
  • index – the member index

  • *labels – the member property labels

Returns:

datafrome with the selected properties

WARNING: tnsgrt.structure.Structure.get_member_properties() uses pandas’ loc method that includes the last element of slices; See pandas documentation for details

get_member_tags(index: int) List[str]

A list with the tags for the member with index index

Parameters:

index – the index of the member

Returns:

list of tags

get_member_vectors() ndarray[Any, dtype[float64]]
Returns:

a 3 x m array with the Structure’s members

get_members_by_tag(*tag: str) ndarray[Any, dtype[int64]]

Return a list of member indices that have given tags

Parameters:

*tag – the tag

Returns:

list of member indices

get_members_per_node() ndarray[Any, dtype[ScalarType]]
Returns:

number of members connected to each node

get_node_properties(index: int | Sequence[int] | slice, *labels: str) DataFrame

Retrieve node properties

Parameters:
  • index – the node index

  • *labels – the node property labels

Returns:

datafrome with the selected properties

WARNING: tnsgrt.structure.Structure.get_node_properties() uses pandas’ loc method that includes the last element of slices; See pandas documentation for details

get_node_values(index: int | Sequence[int] | slice) ndarray[Any, dtype[ScalarType]]

Get node values

Parameters:

index – the index of the nodes to get; can be a slice, integer, or sequence

Returns:

the node values

get_nodes_by_tag(*tag: str) ndarray[Any, dtype[int64]]

Return a list of node indices that have given tags

Parameters:

*tag – the tag

Returns:

list of node indices

get_number_of_members() int
Returns:

the number of members in Structure

get_number_of_members_by_tag(tag: str) int

Return the number of members with tag tag

Parameters:

tag – the tags

Returns:

the number of members

get_number_of_nodes() int
Returns:

the number of nodes in Structure

get_number_of_nodes_by_tag(tag: str) int

Return the number of members with tag tag

Parameters:

tag – the tags

Returns:

the number of members

get_slack_members(epsilon: bool = 1e-08) Index
Returns:

the index of members with small force coefficients

get_unused_nodes() ndarray[Any, dtype[int64]]
Returns:

an array with the indices of the unused nodes

has_member_tag(index: int, tag: str) bool

Return True if member with index index has tag tag

Parameters:
  • index – the index of the member

  • tag – the tag

Returns:

True or False

has_unused_nodes() bool
Returns:

True if there are no unused nodes

merge(s: Structure) None

Merge s with the current structure

Parameters:

s – the Structure to merge

merge_close_nodes(radius: float = 1e-06, verbose: bool = False) None

Merge then remove all nodes in Structure which lie within radius

Parameters:
  • radius – the proximity radius

  • verbose – if True issues a warning with number of nodes removed after the merge

merge_colinear_nodes(epsilon: float = 1e-08) None

Merge members in co-linear nodes

See tnsgrt.structure.Structure.get_colinear_nodes()

Parameters:

epsilon – accuracy of co-linearity test

Notes:

  1. If the co-linear members are a bar and a string, the node and members are skipped

  2. New member inherit all tag from co-linear members

  3. All dependent nodes are removed and co-linear members are replaced

merge_overlapping_members(verbose: bool = False) None

Merge overlapping members in structure

Notes:

  1. Overlapping members are members that share the same set of nodes

  2. The properties ‘lambda_’, ‘force’, ‘mass’, and ‘volume’ are summed on the merged member

  3. The remaining member has as tags the union of all tags in the merged members

reflect(v: ndarray[Any, dtype[ScalarType]], p: ndarray[Any, dtype[ScalarType]] | None = None) Structure

Reflects the structure about a plane normal to the vector v, passing through the point p. If no point is given, it defaults to the origin.

Parameters:
  • v – the 3D normal vector

  • p – the 3D origin vector

Returns:

self

remove_member_tag(tag: str, indices: ndarray[Any, dtype[int64]]) None

Remove members with indices in indices from the existing member tag tag

Parameters:
  • tag – the member tag

  • indices – the member indices

remove_members(members_to_be_deleted: _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | None = None, verbose: bool = False)

Remove members from Structure

Parameters:
  • members_to_be_deleted – the indices of the members to be deleted

  • verbose – if True warns of the members to be deleted

Returns:

remove_node_tag(tag: str, indices: ndarray[Any, dtype[int64]]) None

Remove nodes with indices in indices from the existing node tag tag

Parameters:
  • tag – the node tag

  • indices – the node indices

remove_nodes(nodes_to_be_removed: _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | None = None, verify_if_unused: bool = True, verbose: bool = False) None

Remove nodes from structure

Parameters:
  • nodes_to_be_removed – the indices of the nodes to be deleted; if None, delete all currently unused nodes

  • verify_if_unused – if True verifies if the nodes to be deleted are not in use

  • verbose – if True warns of the nodes to be deleted

rotate(v: ndarray[Any, dtype[ScalarType]]) Structure

Rotate all nodes of the Structure by the 3D vector v

Parameters:

v – the 3D rotation vector

Returns:

self

Notes:

  1. See scipy.spatial.transform.Rotation.from_rotvec() for details

set_member_properties(index: int | Sequence[int] | slice, labels: str | Sequence[str], values: Any, *vargs, scalar: bool = True) None

Set member properties

See tnsgrt.structure.Structure._set_dataframe()

Parameters:
  • index – the element index

  • labels – the property label(s)

  • values – the values to set to

  • *vargs – label/values pairs

  • scalar – if True, value is set to an array of len(index)

set_node_properties(index: int | Sequence[int] | slice, labels: str | Sequence[str], values: Any, *vargs, scalar: bool = True) None

Set node properties

See tnsgrt.structure.Structure._set_dataframe()

Parameters:
  • index – the element index

  • labels – the property label(s)

  • values – the values to set to

  • *vargs – label/values pairs

  • scalar – if True, value is set to an array of len(index)

set_node_values(index: int | Sequence[int] | slice, nodes: ndarray[Any, dtype[ScalarType]]) None

Set node values

Parameters:
  • index – the index of the nodes to set; can be a slice, integer, or sequence

  • nodes – the values to set

set_nodes(nodes: _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]) None

Set nodes of the Structure

Parameters:

nodes – the nodes

stiffness(force: _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | None = None, epsilon: float = 1e-06, storage: str = 'sparse', apply_rigid_body_constraint: bool = False, apply_planar_constraint: bool = False)

Computes

  • normal: potential energy (1 x 1)

  • F: force vectors (3 x n)

  • K: stiffness matrix (3 n x 3 n)

  • M: mass matrix (n x 1)

for the current Structure. The mass and stiffness matrices are returned in the form of an object of the class tnsgrt.stiffness.Stiffness

Parameters:
  • epsilon – numerical accuracy

  • storage – if sparse stores the resulting stiffness and mass matrices in sparse csr format

  • apply_rigid_body_constraint – if True apply 3D rigid body constraints

  • apply_planar_constraint – if True apply 2D constraints

Returns:

tuple (S, F, normal)

translate(v: ndarray[Any, dtype[ScalarType]]) Structure

Translate all nodes of the Structure by the 3D vector normal

Parameters:

v – the 3D translation vector

Returns:

self

update_member_properties(property_name: str | Iterable[str] | None = None) None

Update Structure’s member properties

If property_name is

  • ‘stiffness’: calculate stiffness and rest_length based on modulus, radius, inner_radius and lambda_

  • ‘mass’: calculate mass and volume based on radius and density

  • ‘force’: calculate force based on lambda_

Parameters:

property_name – the property name to update; if None, update all properties

tnsgrt.structure.merge(*s: Structure) Structure

Returns a new structure in which all structures given in s are merged

Parameters:

*s – the structures to merge

Returns:

the merged structure

tnsgrt.structure.reflect(s: Structure, v: ndarray[Any, dtype[ScalarType]], p: ndarray[Any, dtype[ScalarType]]) Structure

Returns a copy of s in which all nodes are reflected about a plane normal to the vector v, passing through the point p.

If no point is given, it defaults to the origin.

Parameters:
  • s – the structure to reflect

  • v – the 3D normal vector

  • p – the 3D origin vector

Returns:

the reflected structure

tnsgrt.structure.rotate(s: Structure, v: ndarray[Any, dtype[ScalarType]]) Structure

Returns a copy of s in which all nodes are rotated around the 3D vector v

Parameters:
  • s – the structure to rotate

  • v – the 3D rotation vector

Returns:

the rotated structure

Notes:

  1. See scipy.spatial.transform.Rotation.from_rotvec() for details

tnsgrt.structure.translate(s: Structure, v: ndarray[Any, dtype[ScalarType]]) Structure

Returns a copy of s in which all nodes are translated by the 3D vector v

Parameters:
  • s – the structure to translate

  • v – the 3D translation vector

Returns:

the translated structure

tnsgrt.plotter

class tnsgrt.plotter.plotter.Plotter

Base class for structure plotters

static cone(node1: ndarray[Any, dtype[ScalarType]], node2: ndarray[Any, dtype[ScalarType]], base_radius: float = 0.01, n: int = 12) Tuple[ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]]]

Return xyz grid points for cylinder with center aligned with the vector defined by node1 and node2

Parameters:
  • node1 – center of the base of the cylinder

  • node2 – center of the top of the cylinder

  • base_radius – radius of the cylinder, ignore if volume is positive

  • n – number of sides

Returns:

tuple with x, y, and z points

static cylinder(node1: ndarray[Any, dtype[ScalarType]], node2: ndarray[Any, dtype[ScalarType]], radius: float = 0.01, n: int = 12) Tuple[ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]]]

Return xyz grid points for cylinder with center aligned with the vector defined by node1 and node2

Parameters:
  • node1 – center of the base of the cylinder

  • node2 – center of the top of the cylinder

  • radius – radius of the cylinder, ignore if volume is positive

  • n – number of sides

Returns:

tuple with x, y, and z points

plot(*s: Structure, **kwargs) None

Plot structure

Parameters:
  • *s – the structure or sequence of structures

  • **kwargs – additional keyword arguments

static truncated_cylinder(node1: ndarray[Any, dtype[ScalarType]], node2: ndarray[Any, dtype[ScalarType]], base_radius: float = 0.01, top_radius: float = 0.01, n: int = 12) Tuple[ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]]]

Return xyz grid points for cylinder with center aligned with the vector defined by node1 and node2

Parameters:
  • node1 – center of the base of the cylinder

  • node2 – center of the top of the cylinder

  • base_radius – radius of the cylinder base

  • top_radius – radius of the cylinder top

  • n – number of sides

Returns:

tuple with x, y, and z points

static unit_cylinder(n: int = 10, radius: float = 1, height: float = 1) Tuple[ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]]]

Return xyz grid points for solid cylinder centered at the origin

Parameters:
  • n – number of sides

  • radius – radius of cylinder

  • height – height of cylinder

Returns:

tuple with x, y, and z points

static unit_sphere(n: int = 10, radius: float = 1) Tuple[ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]]]

Return xyz grid points for solid sphere centered at the origin

Parameters:
  • n – number of sides

  • radius – radius of sphere

Returns:

tuple with x, y, and z points

static unit_truncated_cylinder(n: int = 10, base_radius: float = 1, top_radius: float = 1, height: float = 1) Tuple[ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]]]

Return xyz grid points for solid truncated cylinder centered at the origin

Parameters:
  • n – number of sides

  • base_radius – radius of the cylinder base

  • top_radius – radius of the cylinder top

  • height – height of cylinder

Returns:

tuple with x, y, and z points

class tnsgrt.plotter.matplotlib.MatplotlibPlotter(plotter: MatplotlibPlotter | None = None, fig: Figure | None = None, ax: Axes | None = None)

Matplotlib based structure plotter

Parameters:
  • plottertnsgrt.plotter.matplotlib.Matplotlib object

  • figmatplotlib.figure object

  • axmatplotlib.pyplot.axis object

get_handles() Tuple[Figure, Axes]
Returns:

tuple with matplotlib figure and axis

plot(*s: Structure, **kwargs) None

Plot structure

Parameters:
  • *s – the structure or sequence of structures

  • **kwargs – additional keyword arguments

view_init(elev=0, azim=0, roll=0) None

Set view

Parameters:
  • elev – elevation

  • azim – azimuth

  • roll – roll

class tnsgrt.plotter.vispy.VisPyPlotter(camera=None, scene=None)

VisPy based structure plotter

Parameters:
  • camera – dict with camera settings

  • scene – dict with vispy scene settings

get_canvas()
Returns:

vispy canvas

plot(*s: Structure, **kwargs)

Plot structure

Parameters:
  • *s – the structure or sequence of structures

  • **kwargs – additional keyword arguments

tnsgrt.stiffness

class tnsgrt.stiffness.NodeConstraint(constraint: ndarray[Any, dtype[ScalarType]] | None = None, displacement: ndarray[Any, dtype[ScalarType]] | None = None, epsilon: float = 1e-08)

Object representing a node constraint

Parameters:
  • constraint – the constraint coefficient array

  • epsilon – precision used to assess numerical rank

Notes:

  1. If \(x\) is the node coordinate and \(A\) is the constraint coefficient then

    \[A x = 0\]

    then the equivalent constraint and its null space

    \[R \, x = 0, \quad x = T \, y, \quad R R^T = I, \quad T^T T = I\]

    are constructed using tnsgrt.utils.orthogonalize()

static node_constraint(nodes: ndarray[Any, dtype[ScalarType]], constraints: Sequence[NodeConstraint], storage: Literal['sparse', 'dense'] = 'sparse') Tuple[ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]]] | Tuple[csr_array, csr_array]

Construct the constraint associated with the given nodes and node constraints

The resulting tuple is compatible with apply_constraint()

Parameters:
  • nodes – 3 x n array of nodes

  • constraints – list with n constraints

  • storage – if sparse, returns sparse arrays

Returns:

tuple with the constraint matrix, and its null space

static planar_constraint(nodes: ndarray[Any, dtype[ScalarType]], normal: ndarray[Any, dtype[ScalarType]] | None = None, epsilon: float = 1e-08, storage='sparse')

Construct the planar constraint associated with the given nodes and normal vector

The resulting tuple is compatible with apply_constraint()

Parameters:
  • nodes – 3 x n array of nodes

  • normal – list with n constraints

  • epsilon – accuracy

  • storage – if sparse, returns sparse arrays

Returns:

tuple with the constraint matrix, and its null space

static rigid_body_constraint(nodes: ndarray[Any, dtype[ScalarType]], epsilon: float = 1e-08) Tuple[ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]]]

Construct rigid-body constraint associated with the given nodes

The resulting tuple is compatible with apply_constraint()

Parameters:
  • nodes – 3 x n array of nodes

  • epsilon – precision used to assess numerical rank

Returns:

tuple with the constraint matrix, and its null space

static rigid_body_three_point_constraint(nodes: ndarray[Any, dtype[ScalarType]], epsilon: float = 1e-08) Tuple[NodeConstraint, NodeConstraint, NodeConstraint]

Return three constraints on three given nodes to prevent rigid motion.

Parameters:
  • nodes – 3 x 3 array of nodes (column oriented)

  • epsilon – accuracy to assess co-linearity

Returns:

tuple with 3 node constraints

Notes:

  1. The constraints are as follows: - Node 0 is fixed - Node 1 is allowed to move in the line 0-1 - Node 2 is allowed to move in the plane 0-1-2

class tnsgrt.stiffness.Stiffness(K: ndarray[Any, dtype[ScalarType]] | csr_matrix, M: ndarray[Any, dtype[ScalarType]] | csr_matrix | None = None)

Model for a constrained mechanical system consisting of

  • \(K\): the stiffness matrix

  • \(M\): the mass matrix

  • \(R\): a matrix representing node displacement constraints

  • \(T\): a matrix representing allowed node displacements

such that

\[V = \frac{1}{2} y^T K y + \frac{1}{2} \ddot{y}^T M \ddot{y}, \qquad R \, x = 0, \qquad x = T y\]

Constructor parameters:

Parameters:
  • K – the stiffness matrix

  • M – the mass matrix

Notes:

  1. The stiffness and mass matrices are stored in the reduced coordinates \(y\)

  2. In global coordinates

    \[V = \frac{1}{2} x^T K_x x + \frac{1}{2} \ddot{x}^T M_x \ddot{x}\]

    in which

    \[K_x = T^T K T, \qquad M_x = T^T M T\]
  3. Use tnsgrt.stiffness.Stiffness.apply_constraint() to apply constraints to the model

  4. Node constraints are enforced to be orthogonal so that

    \[R \, x = 0, \quad x = T y, \qquad R R^T = I, \quad T^T T = I, \quad R \, T = 0\]
apply_constraint(R: ndarray[Any, dtype[ScalarType]] | csr_matrix, T: ndarray[Any, dtype[ScalarType]] | csr_matrix | None = None, local: bool = True, verbose: bool = True, epsilon: float = 1e-08)

Apply the constraint

\[R \, y = 0, \qquad y = T z\]

to the current or the global Stiffness object coordinate

Parameters:
  • R – the constraint coefficient matrix

  • T – the allowed node displacements; if None constraint is normalized

  • local – if True the constraint is applied

  • verbose – if True print warnings

  • epsilon – precision used to assess numerical rank

Returns:

Notes:

  1. If local = True and the current constraints are

    \[R_y \, x = 0, \quad x = T_y \, y, \qquad R^{}_y R_y^T = I, \quad T_y^T T^{}_y = I, \quad R_y T_y = 0\]

    then after applying the new constraints

    \[R \, y = R \, T_y^T x = R_z x = 0, \qquad x = T_y \, y = T_y T z = T_z z,\]

    in which

    \[R_z = R \, T_y^T, \qquad T_z = T_y T\]

    and

    \[V = \frac{1}{2} z^T K_z \, z + \frac{1}{2} \ddot{z}^T M_z \, \ddot{z}, \qquad R_z x = 0, \quad x = R_z \, z,\]

    in which

    \[K_z = T^T K T, \qquad M_z = T^T M T\]
  2. If local = False and the constraints are interpreted as

    \[R \, x = 0, \quad x = T \, z\]

    which is first converted to the local constraint

    \[R \, x = R \, T_y y = \tilde{R} y = 0, \qquad \tilde{R} = R \, T_y,\]

    before applying

displacements(f: ndarray[Any, dtype[ScalarType]])

Calculate displacements due to the application of a global force

Parameters:

f – 3 x m array of forces

Returns:

the displacements

Notes:

  1. The displacements are calculated in global coordinates

    \[x = T K^{-1} T^T f\]
eigs(k: int = 12, which: Literal['LM', 'SM', 'LR', 'SR', 'LI', 'SI'] = 'SM')

Compute the eigenvalues and eigenvectors of the stiffness matrix

If the stiffness matrix is stored as a sparse array, return only the first k eigenvalue/eigenvector pairs

Parameters:
  • k – the number of eigenvalues

  • which – which eigenvalues to compute; see scipy.sparse.linalg.eigsh() for details

Returns:

tuple with eigenvalues, and eigenvectors

Notes:

  1. The eigenvalues and eigenvectors are calculated by solving the eigenvalue problem

    \[K y = \lambda \, y, \qquad x = T \, y\]
modes(k: int = 12, units='Hz', which: Literal['LM', 'SM'] = 'SM')

Compute the natural frequencies [rad/s] and mode vectors

Parameters:
  • k – the number of eigenvalues

  • which – which eigenvalues to compute; see scipy.sparse.linalg.eigsh() for details

  • units – return frequencies in Hz if units = 'Hz'

Returns:

tuple with eigenvalues, and eigenvectors

Notes:

  1. The natural frequencies and mode vectors are calculated by solving the generalized eigenvalue problem

    \[K y = \omega^2 M y, \qquad x = T \, y\]

tnsgrt.utils

class tnsgrt.utils.Colors(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Default colors

BLUE = (0, 0.447, 0.741)
BROWN = (0.635, 0.078, 0.184)
GREEN = (0.466, 0.674, 0.188)
LIGHT_BLUE = (0.301, 0.745, 0.933)
ORANGE = (0.85, 0.325, 0.098)
PURPLE = (0.494, 0.184, 0.556)
YELLOW = (0.929, 0.694, 0.125)
tnsgrt.utils.is_colinear(a: ndarray[Any, dtype[ScalarType]], b: ndarray[Any, dtype[ScalarType]], epsilon: float = 1e-08) bool

Two vectors are colinear if their orthogonal projection is small

Parameters:
  • a – first vector

  • b – second vector

  • epsilon – accuracy

Returns:

True if colinear

tnsgrt.utils.norm(a: ndarray[Any, dtype[ScalarType]] | csr_matrix) float
Parameters:

a – the array

Returns:

the 2-norm of the array

tnsgrt.utils.orthogonalize(a: ndarray[Any, dtype[ScalarType]] | csr_matrix, epsilon: float = 1e-08, mode: Literal['reduced', 'complete'] = 'reduced') Tuple[int, ndarray[Any, dtype[ScalarType]], ndarray[Any, dtype[ScalarType]]] | Tuple[int, ndarray[Any, dtype[ScalarType]]]

Ortoghonalize the constraint

\[A^T \, x = 0\]
Parameters:
  • a – the coefficient array \(A\)

  • epsilon – the accuracy with which to evaluate the rank

  • mode – ‘complete’ or ‘reduced’

Returns:

tuple with rank, the orthogonalized coefficient array, and its null space if mode is ‘complete’

Notes:

  1. If mode = 'reduced' the constraint coefficient is normalized at the constructor by calculating the reduced SVD decomposition

    \[A = V S U^T, \quad V^T V = I, \quad U^T U = I, \quad S \text{ is diagonal}\]

    Assuming that \(A\) is full rank, the equivalent orthogonal constraint

    \[A^T x = U S V^T x = 0 \quad \Leftrightarrow \quad V^T x = 0\]

    is obtained in which the coefficient is the orthogonal matrix \(V\)

  2. If mode = 'complete' the constraint coefficient is normalized at the constructor by calculating the complete SVD decomposition

    \[A = \tilde{V} \tilde{S} \tilde{U}^T, \quad \tilde{V}^T \tilde{V} = \tilde{V} \tilde{V}^T = I, \quad \tilde{U}^T \tilde{U} = \tilde{U} \tilde{U}^T = I, \quad \tilde{S} \text{ is diagonal}\]

    Assuming that \(A\) is full rank, partition

    \[\begin{split}\begin{bmatrix} V & T \end{bmatrix} = \tilde{V}, \qquad \begin{bmatrix} U & Q \end{bmatrix} = \tilde{U}, \qquad \tilde{S} = \begin{bmatrix} S \\ 0 \end{bmatrix}, \quad S \text{ is diagonal}\end{split}\]

    to obtain the equivalent orthogonal constraint and its solution

    \[A^T x = U S V^T x = 0 \quad \Leftrightarrow \quad V^T x = 0, \qquad x = T \, y\]

    The matrix \(T\) is an orthogonal basis for the constraint null space

  3. The above factorizations are modified to take into account the numerical rank of \(A\) when it is rank-deficient

tnsgrt.utils.rotation_2d(phi: float) ndarray[Any, dtype[ScalarType]]

Return a 2D rotation matrix

Parameters:

phi – the rotation angle in radians

Returns:

the rotation matrix

tnsgrt.utils.rotation_3d(v: ndarray[Any, dtype[ScalarType]]) ndarray[Any, dtype[ScalarType]]

Return a 3D rotation matrix

Parameters:

v – the vector around which to rotate; its norm is the angle to rotate

Returns:

the rotation matrix

Notes:

  1. See scipy.spatial.transform.Rotation.from_rotvec() for details