The cartesian_vector and random distribution types now live in
mp_units::utility. Turn the transitional mp_units:: shims into proper
[[deprecated]] aliases (gcc-12 keeps a plain using-declaration because
CTAD through a deprecated alias template is broken there), and migrate
all in-tree consumers to mp_units::utility:: so the deprecations don't
trip -Werror. cartesian_tensor keeps no shim: it is unreleased (added in
2.6.0, never shipped), so it lives only at mp_units::utility.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`embed` is the canonical inclusion of the plane into space and `project` the
canonical projection back, for both vectors and tensors:
- vector: embed (x,y) -> (x,y,0); project (x,y,z) -> (x,y)
- tensor: embed the 2x2 into the top-left block of a zeroed 3x3; project keeps
that top-left 2x2 block
They are explicit named free functions (no implicit cross-dimension conversion),
each defined for a single source dimension (embed: 2D only; project: 3D only).
The zero fill is the additive identity derived from a component (`x - x`), so no
value-initialization of the element type is required.
Also route `tensor_product` through `cartesian_tensor_from` instead of a
default-constructed result, matching the other constructing operations (the
element type need not be default-constructible).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`cartesian_vector<T, N>` and `cartesian_tensor<T, N>` now take a compile-time
dimension N in {2, 3} (default 3), so a planar model pays nothing for an unused
third component. Operations close at a matching N: tensor_product of two
N-vectors yields an N x N tensor, inner_product(N x N tensor, N-vector) yields
an N-vector, and mixing dimensions is ill-formed. vector_product is the 3D axial
vector for N=3 and the 2D perp-dot (pseudo)scalar for N=2. N is deduced from the
argument count (2/3 components -> 2D/3D vector; 4/9 -> 2x2/3x3 tensor).
Also:
- element conversions build via member initializer lists (no default-construct-
then-assign), and every constructing tensor operation builds through a new
cartesian_tensor_from helper instead of a default-constructed result, so the
element type need not be default-constructible
- a compile-time `extent` query (per-axis count, std::extent semantics) on both,
usable as both `x.extent` and `x.extent()`
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The converting and variadic constructors (and converting assignment) keyed their implicit/explicit
decision on `std::convertible_to` alone, which let a truncating floating-point -> integer element
conversion (e.g. `cartesian_vector<double>` -> `cartesian_vector<int>`) convert implicitly -
inconsistent with a quantity's rep, where the conversion must also be non-truncating.
Add a `detail::ImplicitlyConvertibleScalar` guard that keeps `std::convertible_to` (an element type
need not be fundamental and may define its own implicit-conversion rules) and defers the
non-truncating decision to the `implicitly_scalable` customization point, passing identical `one`
units so it degenerates to the rep-only decision. Deferring to the CPO rather than hardcoding its
`treat_as_floating_point`-based default keeps element conversions consistent with a user's
`implicitly_scalable` specialization. An FP target or a widening stays implicit; an FP -> integer
element conversion becomes explicit (still constructible). Converting assignment is restricted to the
implicit case for the same reason.
Adds static_assert coverage to the cartesian vector/tensor runtime tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add `cartesian_tensor<T>`, a fixed 3x3 second-order Cartesian tensor that
serves as a representation type for tensor-character quantities (e.g.
`isq::stress`, `isq::strain`, `isq::moment_of_inertia`), which previously had
no instantiable representation. It mirrors the `cartesian_vector` design
(hidden-friend operations in a `*_iface` base, const-qualified operator
constraints, no `operator%`).
Operations follow ISO 80000-2:2019 clause 18 (second order): `+`, `-`, unary
`-`, scalar `*`//`, compound assignments, `==`; `tensor_product` (dyadic of two
vectors, 2-18.21), `inner_product` (tensor.tensor 2-18.23, tensor.vector
2-18.24), `scalar_product` (double-dot `:`, 2-18.25), and a Frobenius norm.
Fourth-order `T(x)S` (2-18.22), `transpose`/`trace`, and `a.T` are intentionally
out of scope for now.
Activate the previously stubbed tensor representation concepts: `Tensor` is a
permissive mirror of `Vector` (a tensor of order zero is a scalar and of order
one is a vector, ISO 80000-2:2019 18), so real scalars and `cartesian_vector`
also satisfy tensor character as degenerate lower-rank cases. A genuine
second-order `cartesian_tensor` opts out of the lower-rank `Vector` concept via
`disable_vector`, and `std::complex` opts out of `Tensor` via `disable_tensor`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>