Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 60 additions & 5 deletions docs/continuum_mechanics/functions/doc_rotation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,31 @@ All mechanics methods that accept NumPy arrays validate the input shape and rais

Expected input shapes:

- ``apply``: 1D array, 3 elements (or Nx3 array for batch)
- ``apply_tensor``: 2D array, shape (3, 3)
- ``apply_stress``, ``apply_strain``: 1D array, 6 elements
- ``apply_stiffness``, ``apply_compliance``: 2D array, shape (6, 6)
.. list-table::
:widths: 35 30 30
:header-rows: 1

* - Method
- Single rotation
- Batch (N rotations)
* - ``apply``
- ``(3,)``
- ``(N, 3)``
* - ``apply_tensor``
- ``(3, 3)``
- ``(3, 3, N)``
* - ``apply_stress``, ``apply_strain``
- ``(6,)``
- ``(6, N)``
* - ``apply_stiffness``, ``apply_compliance``
- ``(6, 6)``
- ``(6, 6, N)``
* - ``apply_strain_concentration``, ``apply_stress_concentration``
- ``(6, 6)``
- ``(6, 6, N)``
* - ``as_voigt_stress_rotation``, ``as_voigt_strain_rotation``
- returns ``(6, 6)``
- returns ``(N, 6, 6)``

.. note::

Expand Down Expand Up @@ -396,7 +417,41 @@ Example 2: Stress Transformation in a Rotated Element
print("Global stress:", sigma_global)
print("Local stress:", sigma_local)

Example 3: Averaging Orientations
Example 3: Batch Rotation of Gauss-Point Quantities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When working with finite element data, you often need to rotate stress or strain
at many Gauss points simultaneously. The ``Rotation`` class supports batch operations
natively:

.. code-block:: python

import simcoon as smc
import numpy as np

N = 100 # number of Gauss points

# DR contains incremental rotation matrices at each Gauss point: shape (3, 3, N)
DR = np.random.randn(3, 3, N)
# (in practice DR comes from polar decomposition of F)

# Build batch rotations from (N, 3, 3) matrices
rotations = smc.Rotation.from_matrix(DR.transpose(2, 0, 1))

# Rotate all Gauss-point stresses at once: shape (6, N)
stress = np.random.randn(6, N)
stress_rotated = rotations.apply_stress(stress) # (6, N)

# Rotate stiffness tensors: shape (6, 6, N)
L = np.zeros((6, 6, N))
for i in range(N):
L[:, :, i] = smc.L_iso([210e3, 0.3], "Enu")
L_rotated = rotations.apply_stiffness(L) # (6, 6, N)

# Get all 6x6 Voigt rotation matrices at once
QS = rotations.as_voigt_stress_rotation() # (N, 6, 6)

Example 4: Averaging Orientations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: python
Expand Down
25 changes: 25 additions & 0 deletions docs/cpp_api/simulation/rotation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,31 @@ Apply Methods
The ``active`` parameter controls whether the rotation is **active** (alibi, rotating the object)
or **passive** (alias, rotating the coordinate system).

.. code-block:: cpp

// Get the 6x6 Voigt rotation matrices directly
Rotation r = Rotation::from_axis_angle(M_PI/4, 3);

arma::mat QS = r.as_voigt_stress_rotation(); // 6x6 stress rotation
arma::mat QE = r.as_voigt_strain_rotation(); // 6x6 strain rotation

// Manual rotation via matrices (equivalent to apply_stiffness):
arma::mat L_rot = QS * L * QS.t();

// Passive rotation (alias convention):
arma::mat QS_passive = r.as_voigt_stress_rotation(false);

.. code-block:: python

r = smc.Rotation.from_axis_angle(np.pi/4, 3)

QS = r.as_voigt_stress_rotation() # (6, 6) stress rotation matrix
QE = r.as_voigt_strain_rotation() # (6, 6) strain rotation matrix

# Batch: N rotations produce (N, 6, 6) arrays
rots = smc.Rotation.random(100)
QS_batch = rots.as_voigt_stress_rotation() # (100, 6, 6)

Operations
----------

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
#pragma once

namespace simcoon_docs {

constexpr auto CppRotation_class = R"pbdoc(
Internal C++ rotation backend using unit quaternions (scalar-last).

End users should use ``simcoon.Rotation`` instead, which inherits from
``scipy.spatial.transform.Rotation`` and delegates mechanics operations
to this class.
)pbdoc";

constexpr auto CppRotation_from_quat = R"pbdoc(
Create a rotation from a quaternion in scalar-last convention.

Parameters
----------
quat : numpy.ndarray
A 1D array of 4 elements [qx, qy, qz, qw] (scalar-last).

Returns
-------
_CppRotation
A rotation object.

Examples
--------
.. code-block:: python

import numpy as np
import simcoon as smc

q = np.array([0, 0, np.sin(np.pi/4), np.cos(np.pi/4)]) # 90deg around z
r = smc.Rotation.from_quat(q)
)pbdoc";

constexpr auto as_voigt_stress_rotation = R"pbdoc(
Get the 6x6 rotation matrix for stress tensors in Voigt notation.

The stress rotation matrix :math:`Q_\sigma` satisfies
:math:`\boldsymbol{\sigma}' = Q_\sigma \boldsymbol{\sigma}`.

Parameters
----------
active : bool, optional
If True (default), returns the active (alibi) rotation matrix.
If False, returns the passive (alias) rotation matrix.

Returns
-------
numpy.ndarray
A (6, 6) rotation matrix for stress tensors.
For batch rotations, returns (N, 6, 6).

Examples
--------
.. code-block:: python

import simcoon as smc
import numpy as np

r = smc.Rotation.from_axis_angle(np.pi/4, 3)
QS = r.as_voigt_stress_rotation() # (6, 6)

# Batch: N rotations
rots = smc.Rotation.random(100)
QS_batch = rots.as_voigt_stress_rotation() # (100, 6, 6)
)pbdoc";

constexpr auto as_voigt_strain_rotation = R"pbdoc(
Get the 6x6 rotation matrix for strain tensors in Voigt notation.

The strain rotation matrix :math:`Q_\varepsilon` satisfies
:math:`\boldsymbol{\varepsilon}' = Q_\varepsilon \boldsymbol{\varepsilon}`.

Parameters
----------
active : bool, optional
If True (default), returns the active (alibi) rotation matrix.
If False, returns the passive (alias) rotation matrix.

Returns
-------
numpy.ndarray
A (6, 6) rotation matrix for strain tensors.
For batch rotations, returns (N, 6, 6).

Examples
--------
.. code-block:: python

import simcoon as smc
import numpy as np

r = smc.Rotation.from_axis_angle(np.pi/4, 3)
QE = r.as_voigt_strain_rotation() # (6, 6)
)pbdoc";

constexpr auto apply_tensor = R"pbdoc(
Apply the rotation to a 3x3 tensor: :math:`R \cdot T \cdot R^T`.

Parameters
----------
m : numpy.ndarray
A (3, 3) tensor. For batch rotations, shape (3, 3, N).
inverse : bool, optional
If True, applies the inverse rotation. Default is False.

Returns
-------
numpy.ndarray
The rotated tensor, same shape as input.

Examples
--------
.. code-block:: python

import simcoon as smc
import numpy as np

r = smc.Rotation.from_axis_angle(np.pi/2, 3)
T = np.diag([1.0, 2.0, 3.0])
T_rot = r.apply_tensor(T)
)pbdoc";

constexpr auto apply_stress = R"pbdoc(
Apply the rotation to a stress vector in Voigt notation.

Voigt convention: :math:`[\sigma_{11}, \sigma_{22}, \sigma_{33}, \sigma_{12}, \sigma_{13}, \sigma_{23}]`.

Parameters
----------
sigma : numpy.ndarray
A 1D array of 6 elements (single rotation) or shape (6, N) for batch.
active : bool, optional
If True (default), active rotation. If False, passive rotation.

Returns
-------
numpy.ndarray
The rotated stress vector, same shape as input.

Examples
--------
.. code-block:: python

import simcoon as smc
import numpy as np

r = smc.Rotation.from_axis_angle(np.pi/4, 3)
sigma = np.array([100.0, 50.0, 25.0, 10.0, 5.0, 2.0])
sigma_rot = r.apply_stress(sigma)
)pbdoc";

constexpr auto apply_strain = R"pbdoc(
Apply the rotation to a strain vector in Voigt notation.

Voigt convention: :math:`[\varepsilon_{11}, \varepsilon_{22}, \varepsilon_{33}, 2\varepsilon_{12}, 2\varepsilon_{13}, 2\varepsilon_{23}]`.

Parameters
----------
epsilon : numpy.ndarray
A 1D array of 6 elements (single rotation) or shape (6, N) for batch.
active : bool, optional
If True (default), active rotation. If False, passive rotation.

Returns
-------
numpy.ndarray
The rotated strain vector, same shape as input.

Examples
--------
.. code-block:: python

import simcoon as smc
import numpy as np

r = smc.Rotation.from_axis_angle(np.pi/4, 3)
epsilon = np.array([0.01, -0.005, -0.005, 0.002, 0.001, 0.0])
epsilon_rot = r.apply_strain(epsilon)
)pbdoc";

constexpr auto apply_stiffness = R"pbdoc(
Apply the rotation to a 6x6 stiffness matrix.

Computes :math:`L' = Q_\sigma \cdot L \cdot Q_\sigma^T`.

Parameters
----------
L : numpy.ndarray
A (6, 6) stiffness matrix (single) or (6, 6, N) for batch.
active : bool, optional
If True (default), active rotation. If False, passive rotation.

Returns
-------
numpy.ndarray
The rotated stiffness matrix, same shape as input.

Examples
--------
.. code-block:: python

import simcoon as smc
import numpy as np

r = smc.Rotation.from_axis_angle(np.pi/4, 3)
L = smc.L_iso(np.array([210000, 0.3]), "Enu")
L_rot = r.apply_stiffness(L)
)pbdoc";

constexpr auto apply_compliance = R"pbdoc(
Apply the rotation to a 6x6 compliance matrix.

Computes :math:`M' = Q_\varepsilon \cdot M \cdot Q_\varepsilon^T`.

Parameters
----------
M : numpy.ndarray
A (6, 6) compliance matrix (single) or (6, 6, N) for batch.
active : bool, optional
If True (default), active rotation. If False, passive rotation.

Returns
-------
numpy.ndarray
The rotated compliance matrix, same shape as input.

Examples
--------
.. code-block:: python

import simcoon as smc
import numpy as np

r = smc.Rotation.from_axis_angle(np.pi/4, 3)
M = smc.M_iso(np.array([210000, 0.3]), "Enu")
M_rot = r.apply_compliance(M)
)pbdoc";

constexpr auto apply_strain_concentration = R"pbdoc(
Apply the rotation to a 6x6 strain concentration tensor.

Computes :math:`A' = Q_\varepsilon \cdot A \cdot Q_\sigma^T`.

Parameters
----------
A : numpy.ndarray
A (6, 6) strain concentration tensor (single) or (6, 6, N) for batch.
active : bool, optional
If True (default), active rotation. If False, passive rotation.

Returns
-------
numpy.ndarray
The rotated strain concentration tensor, same shape as input.
)pbdoc";

constexpr auto apply_stress_concentration = R"pbdoc(
Apply the rotation to a 6x6 stress concentration tensor.

Computes :math:`B' = Q_\sigma \cdot B \cdot Q_\varepsilon^T`.

Parameters
----------
B : numpy.ndarray
A (6, 6) stress concentration tensor (single) or (6, 6, N) for batch.
active : bool, optional
If True (default), active rotation. If False, passive rotation.

Returns
-------
numpy.ndarray
The rotated stress concentration tensor, same shape as input.
)pbdoc";

} // namespace simcoon_docs
Loading
Loading