Skip to content

operations

Add(*args) dataclass

Bases: _NaryMixin, ScalarExpr

Scalar addition (n-ary).

Source code in geomech/core/operations/addition.py
def __init__(self, *args):
    self.nodes = _flatten_nodes(args, Add, ExprType.SCALAR)

Cross(l, r) dataclass

Bases: _BinaryMixin, VectorExpr

Cross product of two 3-vectors → vector.

Source code in geomech/core/operations/geometry.py
def __init__(self, l, r):
    if l.type == ExprType.VECTOR and r.type == ExprType.VECTOR:
        ls = getattr(l, "size", None)
        rs = getattr(r, "size", None)
        if ls is not None and rs is not None and ls != rs:
            raise SizeMismatchError("Cross", ls, rs)
        self.nodes = [l, r]
    else:
        raise ExpressionMismatchError("Cross", l.type, r.type)

Dot(l, r) dataclass

Bases: _BinaryMixin, ScalarExpr

Dot product of two vectors → scalar.

Source code in geomech/core/operations/geometry.py
def __init__(self, l, r):
    if l.type == ExprType.VECTOR and r.type == ExprType.VECTOR:
        ls = getattr(l, "size", None)
        rs = getattr(r, "size", None)
        if ls is not None and rs is not None and ls != rs:
            raise SizeMismatchError("Dot", ls, rs)
        self.nodes = [l, r]
    else:
        raise ExpressionMismatchError("Dot", l.type, r.type)

Hat(expr) dataclass

Bases: _UnaryMixin, MatrixExpr

Hat map: R^3 → so(3).

Source code in geomech/core/operations/geometry.py
def __init__(self, expr):
    if expr.type == ExprType.VECTOR:
        self.nodes = [expr]
    else:
        raise ExpressionMismatchError("Hat", ExprType.VECTOR, expr.type)

MAdd(*args) dataclass

Bases: _NaryMixin, MatrixExpr

Matrix addition (n-ary).

Source code in geomech/core/operations/addition.py
def __init__(self, *args):
    self.nodes = _flatten_nodes(args, MAdd, ExprType.MATRIX)

MMMul(l, r) dataclass

Bases: _BinaryMixin, MatrixExpr

Matrix-Matrix multiplication.

Source code in geomech/core/operations/multiplication.py
def __init__(self, l, r):
    if l.type == ExprType.MATRIX and r.type == ExprType.MATRIX:
        _check_mul_sizes("MMMul", l, r)
        self.nodes = [l, r]
    else:
        raise ExpressionMismatchError("MMMul", l.type, r.type)

MVMul(l, r) dataclass

Bases: _BinaryMixin, VectorExpr

Matrix-Vector multiplication.

Source code in geomech/core/operations/multiplication.py
def __init__(self, l, r):
    if l.type == ExprType.MATRIX and r.type == ExprType.VECTOR:
        _check_mul_sizes("MVMul", l, r)
        self.nodes = [l, r]
    elif l.type == ExprType.VECTOR and r.type == ExprType.MATRIX:
        # Transpose(v) * M case
        from geomech.core.operations.geometry import Transpose

        if isinstance(l, Transpose):
            self.nodes = [l, r]
        else:
            raise ExpressionMismatchError("MVMul", l.type, r.type)
    else:
        raise ExpressionMismatchError("MVMul", l.type, r.type)

Mul(l, r) dataclass

Bases: _BinaryMixin, ScalarExpr

Scalar multiplication.

Source code in geomech/core/operations/multiplication.py
def __init__(self, l, r):
    l, r = _wrap_numeric(l), _wrap_numeric(r)
    if l.type == ExprType.SCALAR and r.type == ExprType.SCALAR:
        self.nodes = [l, r]
    else:
        raise ExpressionMismatchError("Mul", l.type, r.type)

SMMul(l, r) dataclass

Bases: _BinaryMixin, MatrixExpr

Scalar-Matrix multiplication. Normalized: left=matrix, right=scalar.

Source code in geomech/core/operations/multiplication.py
def __init__(self, l, r):
    l, r = _wrap_numeric(l), _wrap_numeric(r)
    if l.type == ExprType.MATRIX and r.type == ExprType.SCALAR:
        self.nodes = [l, r]
    elif l.type == ExprType.SCALAR and r.type == ExprType.MATRIX:
        self.nodes = [r, l]
    else:
        raise ExpressionMismatchError("SMMul", l.type, r.type)

SVMul(l, r) dataclass

Bases: _BinaryMixin, VectorExpr

Scalar-Vector multiplication. Normalized: left=vector, right=scalar.

Source code in geomech/core/operations/multiplication.py
def __init__(self, l, r):
    l, r = _wrap_numeric(l), _wrap_numeric(r)
    if l.type == ExprType.VECTOR and r.type == ExprType.SCALAR:
        self.nodes = [l, r]
    elif l.type == ExprType.SCALAR and r.type == ExprType.VECTOR:
        self.nodes = [r, l]
    else:
        raise ExpressionMismatchError("SVMul", l.type, r.type)

TimeDerivative(expr) dataclass

Bases: _CalcUnaryMixin, Expr

Time derivative d/dt{expr}. Preserves the type of its inner expression.

Source code in geomech/core/operations/calculus.py
def __init__(self, expr):
    self.nodes = [expr]

t_integrate()

∫(d/dt(x)) dt = x — cancellation.

Source code in geomech/core/operations/calculus.py
def t_integrate(self):
    """∫(d/dt(x)) dt = x — cancellation."""
    return self.expr

TimeIntegral(expr) dataclass

Bases: _CalcUnaryMixin, Expr

Time integral ∫{expr}dt. Preserves the type of its inner expression.

Source code in geomech/core/operations/calculus.py
def __init__(self, expr):
    self.nodes = [expr]

t_diff()

d/dt(∫x dt) = x — cancellation.

Source code in geomech/core/operations/calculus.py
def t_diff(self):
    """d/dt(∫x dt) = x — cancellation."""
    return self.expr

Transpose(expr=None) dataclass

Bases: _BaseMixin, Expr

Transpose operator. Preserves the type of its inner expression.

Source code in geomech/core/operations/geometry.py
def __init__(self, expr=None):
    if expr is not None:
        self.nodes = [expr]
    else:
        self.nodes = []

VAdd(*args) dataclass

Bases: _NaryMixin, VectorExpr

Vector addition (n-ary).

Source code in geomech/core/operations/addition.py
def __init__(self, *args):
    self.nodes = _flatten_nodes(args, VAdd, ExprType.VECTOR)

VVMul(l, r) dataclass

Bases: _BinaryMixin, Expr

Vector-Vector multiplication. Result type depends on Transpose: Transpose(v) * w → scalar v * Transpose(w) → matrix

Source code in geomech/core/operations/multiplication.py
def __init__(self, l, r):
    from geomech.core.operations.geometry import Transpose

    if l.type == ExprType.VECTOR and r.type == ExprType.VECTOR:
        if isinstance(l, Transpose) and not isinstance(r, Transpose):
            # v^T * w → scalar: sizes must match
            _check_mul_sizes("VVMul", l, r)
            self.nodes = [l, r]
            self._result_type = ExprType.SCALAR
        elif not isinstance(l, Transpose) and isinstance(r, Transpose):
            # v * w^T → matrix (outer product): any sizes valid
            self.nodes = [l, r]
            self._result_type = ExprType.MATRIX
        else:
            raise ExpressionMismatchError("VVMul", l.type, r.type)
    else:
        raise ExpressionMismatchError("VVMul", l.type, r.type)

Variation(expr) dataclass

Bases: _CalcUnaryMixin, Expr

Variation operator δ{expr}. Preserves the type of its inner expression.

Source code in geomech/core/operations/calculus.py
def __init__(self, expr):
    from geomech.core.base.expressions import TS2, TSO3

    # δ(δ(...)) is a second-order variation — vanishes in Hamilton's principle
    if isinstance(expr, Variation):
        raise ValueError(
            f"Cannot take variation of a variation: δ(δ({expr.expr})). "
            f"Second-order variations vanish in Hamilton's principle."
        )
    # δ(ξ) or δ(η) where ξ/η are already variation vectors is nonsensical
    if isinstance(expr, TS2) and expr.S2 is not None:
        var_name = expr.S2.manifold_info.variation_vector_name
        if expr.name == var_name:
            raise ValueError(
                f"Cannot take variation of variation vector {expr}. "
                f"The variation vector is already an infinitesimal perturbation."
            )
    if isinstance(expr, TSO3) and expr.SO3 is not None:
        var_name = expr.SO3.manifold_info.variation_vector_name
        if expr.name == var_name:
            raise ValueError(
                f"Cannot take variation of variation vector {expr}. "
                f"The variation vector is already an infinitesimal perturbation."
            )
    self.nodes = [expr]

t_diff()

d/dt(δx) — put TimeDerivative on the outside.

δ and d/dt commute, but the expression tree produces TimeDerivative(Variation(x)) when taking the variation of d/dt(x). This override ensures the IBP target matches that structure.

Source code in geomech/core/operations/calculus.py
def t_diff(self):
    """d/dt(δx) — put TimeDerivative on the outside.

    δ and d/dt commute, but the expression tree produces
    TimeDerivative(Variation(x)) when taking the variation of d/dt(x).
    This override ensures the IBP target matches that structure.
    """
    return TimeDerivative(self)

Vee(expr) dataclass

Bases: _UnaryMixin, VectorExpr

Vee map: so(3) → R^3.

Source code in geomech/core/operations/geometry.py
def __init__(self, expr):
    if expr.type == ExprType.MATRIX:
        self.nodes = [expr]
    else:
        raise ExpressionMismatchError("Vee", ExprType.MATRIX, expr.type)