Source code for pyfem.util.transformations

# SPDX-License-Identifier: MIT
# Copyright (c) 2011–2026 Joris J.C. Remmers

from numpy import array, dot, ndarray, empty, ix_
from scipy.linalg import norm
from typing import Union

from .dataStructures import GlobalData


[docs] def getRotationMatrix(el_coords: ndarray) -> ndarray: """ Compute the 2D rotation matrix for an element. Calculates the rotation matrix that transforms from global coordinates to element coordinates based on the element's orientation in 2D space. Args: el_coords: Element node coordinates as (2, 2) array where rows are nodes Returns: 2x2 rotation matrix for coordinate transformation Raises: NotImplementedError: If element coordinates are not 2D (shape[1] != 2) """ # Check the dimension of physical space if el_coords.shape[1] != 2: raise NotImplementedError('Rotation matrix only implemented for 2D situation') # Compute the (undeformed) element length l0 = norm(el_coords[1] - el_coords[0]) # Set up the rotation matrix to rotate a global # coordinate to an element coordinate (see Ch 1.3) sinalpha = (el_coords[1, 1] - el_coords[0, 1]) / l0 cosalpha = (el_coords[1, 0] - el_coords[0, 0]) / l0 return array([[cosalpha, sinalpha], [-sinalpha, cosalpha]])
[docs] def vectorToElementCoordinates(a: ndarray, el_coords: ndarray) -> ndarray: """ Transform a vector from global to element coordinates. Rotates a vector (or block of vectors) from the global coordinate system to the element coordinate system. Handles vectors with multiple components. Args: a: Vector in global coordinates with shape (n,) where n is divisible by 2 el_coords: Element node coordinates as (2, 2) array Returns: Vector in element coordinates with same shape as input Raises: RuntimeError: If vector length is not divisible by rotation matrix size """ R = getRotationMatrix(el_coords) a_bar = empty(a.shape) if len(a_bar) % len(R) != 0: raise RuntimeError('Vector does not have the right shape to be rotated') for i in range(len(a_bar) // len(R)): a_bar[len(R) * i:len(R) * (i + 1)] = dot(R, a[len(R) * i:len(R) * (i + 1)]) return a_bar
[docs] def matrixToElementCoordinates(a: ndarray, el_coords: ndarray) -> ndarray: """ Transform a matrix from global to element coordinates. Rotates a square matrix (or block matrix) from the global coordinate system to the element coordinate system using: a_bar = R @ a @ R^T Args: a: Square matrix in global coordinates with shape (m, m) where m is divisible by 2 el_coords: Element node coordinates as (2, 2) array Returns: Matrix in element coordinates with same shape as input Raises: RuntimeError: If matrix dimensions are not divisible by rotation matrix size """ R = getRotationMatrix(el_coords) a_bar = empty(a.shape) if a_bar.shape[0] % len(R) != 0 or a_bar.shape[1] % len(R) != 0: raise RuntimeError('Matrix does not have the right shape to be rotated') for i in range(a_bar.shape[0] // len(R)): iran = list(range(len(R) * i, len(R) * (i + 1))) for j in range(a_bar.shape[1] // len(R)): jran = list(range(len(R) * j, len(R) * (j + 1))) a_bar[ix_(iran, jran)] = dot(dot(R, a[ix_(iran, jran)]), R.transpose()) return a_bar
[docs] def vectorToGlobalCoordinates(a_bar: ndarray, el_coords: ndarray) -> ndarray: """ Transform a vector from element to global coordinates. Rotates a vector (or block of vectors) from the element coordinate system back to the global coordinate system. Inverse of vectorToElementCoordinates. Args: a_bar: Vector in element coordinates with shape (n,) where n is divisible by 2 el_coords: Element node coordinates as (2, 2) array Returns: Vector in global coordinates with same shape as input Raises: RuntimeError: If vector length is not divisible by rotation matrix size """ R = getRotationMatrix(el_coords) a = empty(a_bar.shape) if len(a) % len(R) != 0: raise RuntimeError('Vector does not have the right shape to be rotated') for i in range(len(a) // len(R)): a[len(R) * i:len(R) * (i + 1)] = dot(R.transpose(), a_bar[len(R) * i:len(R) * (i + 1)]) return a
[docs] def matrixToGlobalCoordinates(a_bar: ndarray, el_coords: ndarray) -> ndarray: """ Transform a matrix from element to global coordinates. Rotates a square matrix (or block matrix) from the element coordinate system back to the global coordinate system using: a = R^T @ a_bar @ R Inverse of matrixToElementCoordinates. Args: a_bar: Square matrix in element coordinates with shape (m, m) where m is divisible by 2 el_coords: Element node coordinates as (2, 2) array Returns: Matrix in global coordinates with same shape as input Raises: RuntimeError: If matrix dimensions are not divisible by rotation matrix size """ R = getRotationMatrix(el_coords) a = empty(a_bar.shape) if a.shape[0] % len(R) != 0 or a.shape[1] % len(R) != 0: raise RuntimeError('Matrix does not have the right shape to be rotated') for i in range(a.shape[0] // len(R)): iran = list(range(len(R) * i, len(R) * (i + 1))) for j in range(a.shape[1] // len(R)): jran = list(range(len(R) * j, len(R) * (j + 1))) a[ix_(iran, jran)] = dot(dot(R.transpose(), a_bar[ix_(iran, jran)]), R) return a
[docs] def toElementCoordinates(a: Union[ndarray], el_coords: ndarray) -> ndarray: """ Transform array (vector or matrix) from global to element coordinates. Dispatcher function that automatically detects input type (1D vector or 2D matrix) and applies the appropriate transformation. Rotates from global to element coordinate system. Args: a: Input vector (shape (n,)) or matrix (shape (m, m)) in global coordinates el_coords: Element node coordinates as (2, 2) array Returns: Transformed array in element coordinates with same shape as input Raises: NotImplementedError: If input is not a 1D or 2D ndarray RuntimeError: If array dimensions are incompatible with rotation matrix """ # Vector if isinstance(a, ndarray) and len(a.shape) == 1: return vectorToElementCoordinates(a, el_coords) # Matrix elif isinstance(a, ndarray) and len(a.shape) == 2: return matrixToElementCoordinates(a, el_coords) # Error else: raise NotImplementedError('Rotation to element coordinate system only works for vectors and matrices.')
[docs] def toGlobalCoordinates(a: Union[ndarray], el_coords: ndarray) -> ndarray: """ Transform array (vector or matrix) from element to global coordinates. Dispatcher function that automatically detects input type (1D vector or 2D matrix) and applies the appropriate transformation. Rotates from element to global coordinate system. Inverse of toElementCoordinates. Args: a: Input vector (shape (n,)) or matrix (shape (m, m)) in element coordinates el_coords: Element node coordinates as (2, 2) array Returns: Transformed array in global coordinates with same shape as input Raises: NotImplementedError: If input is not a 1D or 2D ndarray RuntimeError: If array dimensions are incompatible with rotation matrix """ # Vector if isinstance(a, ndarray) and len(a.shape) == 1: return vectorToGlobalCoordinates(a, el_coords) # Matrix elif isinstance(a, ndarray) and len(a.shape) == 2: return matrixToGlobalCoordinates(a, el_coords) # Error else: raise NotImplementedError('Rotation to global coordinate system only works for vectors and matrices.')