Conversation
Declare and populate the Fastor dependency using FetchContent (pointing to the master branch). As Fastor is header-only, no build step is performed — FetchContent_Populate is used and the source dir is printed for visibility. The Fastor include path is added to the simcoon target as SYSTEM PUBLIC (via $<BUILD_INTERFACE:${fastor_SOURCE_DIR}>) to consume the headers and suppress warnings.
Introduce include/simcoon/Continuum_mechanics/Functions/fastor_bridge.hpp providing conversion and utility helpers between Armadillo and Fastor tensor types. Adds a voigt_map constant and functions: zero-copy arma_to_fastor2 (const and non-const), fastor2_to_arma, voigt_to_fastor4, fastor4_to_voigt (stiffness/Voigt convention), and push_forward_4 / pull_back_4 for 4th-order tensor transforms. Header includes license boilerplate and documents column-major zero-copy for 3x3 matrices and the minor-symmetry handling used in Voigt conversions.
Introduce Python bindings for simcoon continuum-mechanics tensors. Adds header declare register_tensor and implementation that registers VoigtType and Tensor4Type enums and two internal classes (_CppTensor2, _CppTensor4) via pybind11. The bindings provide constructors, static factories (from_mat, from_voigt, zeros, identity, etc.), properties (mat, voigt, vtype/type), methods (rotate, push_forward, pull_back, contract), arithmetic operators, and __repr__ implementations. Includes numpy input validation helpers and conversions using carma/armadillo and depends on existing simcoon C++ tensor and Rotation types to expose C++ tensor functionality to Python.
Add new src/Continuum_mechanics/Functions/tensor.cpp implementing tensor2 and tensor4 types. Provides constructors, Voigt conversions, Fastor/Armadillo bridges, rotation, push-forward/pull-back, contraction, dyadic operations, Mises/dev/trace helpers, and arithmetic operators. Includes Fastor caching and conversion utilities to accelerate 4th-order tensor ops and integrates with existing Rotation/constitutive helpers for continuum mechanics use.
Implement Tensor4 inversion and dyadic utilities and add type-aware Voigt handling. C++: add voigt<->full-index converters that apply type-dependent shear/normal factors, implement tensor4::inverse(), ensure push/pull/rotate use full<->Voigt conversions, and expose module-level _dyadic/_auto_dyadic functions. Python: export dyadic and auto_dyadic, bind Tensor4.inverse(), add high-level Tensor2/Tensor4 API docs and constructors (strain_concentration, stress_concentration, identity2, deviatoric2) and Voigt/rotation/push-pull semantics. Tests: add unit tests covering dyadic, auto_dyadic, inverse roundtrips, concentration types, factor correctness, and push/pull/rotation consistency; include test binary. These changes ensure correct handling of stiffness/compliance and concentration tensors across conversions and Python bindings.
Add new tensor functionality, error checks and Python bindings. - Extend tensor2 API: unary -, scalar /, element-wise % and /, !=, and reduction helpers (sum, accu, norm, det, abs, trans). Change dev() to return tensor2 and adapt Mises() accordingly. - Extend tensor4 API: unary -, scalar /, element-wise %, !=, and safety checks on construction/inversion. - Improve robustness: replace assert-based checks and direct inv() calls with argument validation and guarded arma::inv() usage that throws informative exceptions on errors. - Update Python wrappers to expose new operators (__neg__, __truediv__, __mod__, __ne__) and ensure correct overload bindings. - Update tests to cover dev() return type, Schur (element-wise) product, and energy sum via sum(sigma % eps). Also update test binary. These changes add safer validation, richer arithmetic and convenience functions, and expose them to Python while keeping the existing raw arma::vec/mat UMAT interface intact.
Rewrite tensor Python bindings to unify single and batched Tensor2/Tensor4 objects and add batch-aware operations. The Python tensor module now dispatches by shape ((6,) or (3,3) for single, (N,6) or (N,3,3) for batches), supports indexing/iteration, numpy interop, elementwise arithmetic, broadcasting, rotation, push-forward/pull-back, von Mises/trace over batches, and double-contraction. Corresponding C++ batch APIs were declared (batch rotate/push/pull/contract/inverse/mises/trace) and bound into the Python layer. Also updated package exports to expose double_contract, added an examples script demonstrating single/batch usage, and included a test for batched tensors.
Add an optional metric (bool, default=true) argument to push_forward and pull_back for tensor2/tensor4 and their batch variants. The Python API, pybind wrappers, headers and implementations were updated to accept and forward the flag. When metric=true the code applies the Piola/J scaling (1/J or J) for stress/stiffness/compliance types; metric=false performs pure geometric transport (no J factor). Default behavior is unchanged (backwards-compatible). Files updated: header, implementations, pybind wrapper, and Python bindings.
Refactor and optimize tensor-related code across C++ and Python. In fastor_bridge.hpp: replace verbose Voigt↔4th-order conversions with indexed loops and hoist multiplications in push_forward_4 for fewer operations; remove the redundant pull_back_4 wrapper. In tensor.hpp/.cpp: remove mutable symmetry bookkeeping, simplify constructors and setters, make is_symmetric a direct check, and replace manual Voigt contraction with a matrix multiply for performance and clarity. In python tensor factory: add a shared _typed_factory and route stiffness/compliance/strain_concentration/stress_concentration/from_mat to it to eliminate duplicated code. Also optimize batch_trace to compute trace directly from Voigt rows. These changes reduce code duplication, improve performance, and simplify maintenance.
Introduce an operator* overload on tensor4 to allow contraction with a tensor2 (L * eps → sigma). The declaration was added to include/simcoon/Continuum_mechanics/Functions/tensor.hpp and the implementation in src/Continuum_mechanics/Functions/tensor.cpp simply delegates to the existing contract(t) method, enabling direct multiplication of fourth-order and second-order tensors.
Python: Add Tensor4.__mul__ handling for Tensor2 by delegating to contract(), enabling Tensor4 * Tensor2 semantics. C++: Vectorize and optimize batch_mises by computing deviatoric components and using closed-form per-column sqrt expressions for stress/strain Mises values (avoids per-column tensor construction). Optimize batch_contract with a fast path for a single Tensor4 (uses direct Armadillo mat-vec/mat-mat) and simplify the general loop to use direct matrix multiplies, removing unnecessary tensor object construction. These changes improve performance and reduce allocations while preserving existing behavior.
Introduce _infer_contraction_vtype to determine output Voigt type from Tensor4 type and refactor Tensor4.contract to use fast numpy paths: a single-matrix matmul path when the Tensor4 is single, and a batched np.einsum path for batch Tensor4s (with broadcasting for single Tensor2). This reduces pybind11 overhead and avoids the generic _batch_t4_contract for common cases, while preserving correct output construction via Tensor2._from_single_cpp and Tensor2._from_batch_voigt.
|
Great work on this PR — the API design and test coverage are excellent. I've pushed a commit to address a few issues I spotted during review: 1. Binary committed to repo 2. Fastor pinned to a release tag 3. Lambda capture-by-reference UB in pybind11 bindings 4. Thread-safety note on Fastor cache Feel free to keep or drop the commit as you see fit. |
39866f3 to
aa04300
Compare
Rewrite simcoon Tensor2 and Tensor4 to store data purely as numpy arrays ((6,), (N,6), (6,6), (N,6,6)) and use Python string type tags (e.g. 'stress', 'strain', 'stiffness') instead of exposing C++ enums. Batch operations are implemented with numpy (einsum/matrix ops) and C++ batch helpers where appropriate; single-point Tensor4 still delegates some ops to temporary C++ objects when needed. APIs changed: factory signatures accept type strings (from_voigt/from_mat/zeros/identity/etc.), .vtype/.type now return strings, and construction/concatenation/stacking helpers were adjusted (from_list, from_columns, concatenate). Many methods were simplified to avoid single-vs-batch branching and to rely on numpy broadcasting; Voigt/3x3 conversions and rotation handling were updated accordingly. Tests were updated to match the new string-based types and storage model.
Introduce batch free functions to build (6,6,N) stress/strain Voigt rotation matrices from N quaternions: declarations added to the public header, implementations in rotation.cpp (using OpenMP to parallelize per-quaternion work), and pybind11 bindings that accept (N,4) numpy arrays and return (N,6,6). The Python Rotation wrapper now uses these batch bindings to avoid Python-level loops when producing Voigt rotation matrices. Functions default to active=true and expect quaternions in scalar-last [qx,qy,qz,qw] form.
Delete the entire include/simcoon/FTensor header tree, since Fastor is now replacing FTensor
Refactor push_forward_4 to perform the push-forward as four factored contractions (T1, T2, T3, result) instead of the previous deeply nested loops. This reduces the theoretical operation count from O(3^8)=6561 to 4*O(3^5)=972 and adds explanatory comments; three intermediate Fastor::Tensor<double,3,3,3,3> temporaries are used. Also remove the FTensor dependency and a set of FTensor-based transfer function declarations from transfer.hpp (drop #include <simcoon/FTensor.hpp> and related vec_/mat_/FTensor conversion prototypes), cleaning up the header and decoupling it from FTensor types.
Replace FTensor usage with Fastor in the continuum mechanics derivatives implementation. Removed FTensor include from the header and swapped includes in the .cpp to use <Fastor/Fastor.h> and a simcoon fastor_bridge. The dinvSdSsym function now converts the Armadillo matrix to a Fastor tensor, computes the fourth-order tensor via einsum (invS_ik*invS_jl + invS_il*invS_jk), and returns the Voigt-converted result multiplied by 0.5. This migrates tensor computation to Fastor and removes the old FTensor dependency.
Replace the mutable Fastor tensor + validity flag with std::optional to manage the lazy Fastor cache and simplify copy/move/assign logic. Update _ensure_fastor to emplace the cached tensor and adjust callers (fastor(), push_forward(), pull_back()) to dereference the optional. Improve API docs/warnings about thread-safety and add <optional> include. Delegate dyadic/symmetric-dyadic implementations to simcoon contimech helpers (include contimech.hpp) and add new free functions: sym_dyadic and auto_sym_dyadic. Refactor Mises/dev to call simcoon helpers and add OpenMP parallel for pragmas (if N > 100) to several batch loops for simple parallelization. Minor cleanup of constructors/assignments to account for the optional cache.
Introduce symmetric dyadic ops and convert tensor data to Fortran (F) order for zero-copy transfers to underlying arma/C++ code. Changes include: - Export sym_dyadic and auto_sym_dyadic from package init. - Add _to_f_cube/_from_f_cube helpers to convert between (N,R,C) C-order and (R,C,N) F-order. - Return rotation matrices as (3,3,N) F-order for zero-copy to C++. - Convert Tensor4 and Tensor2 batch inputs/outputs to F-order when calling C++ batch functions and convert results back. - Add Tensor2._to_cpp and use fast single-item C++ paths for rotate/push_forward/pull_back when operating on single tensors. - Change _TensorBase.__len__ to raise on single tensors (single objects are not iterable by length). - Add a note about Tensor4 C++ cache thread-safety and that Python creates fresh C++ objects per operation. These changes optimize zero-copy interop with the C++ backend and add symmetric dyadic operations for single Tensor2 inputs.
Replace manual numpy<->arma conversion helpers with carma helpers (arr_to_mat/arr_to_cube and cube_to_arr) and simplify 2D (N,6) <-> arma(6,N) conversions via transpose. Update batch tensor functions to use carma conversions (reducing custom copy loops) and adjust comments accordingly. Remove np3d_to_cube / cube_to_np3d implementations. Add Python bindings for sym_dyadic and auto_sym_dyadic (symmetric Voigt dyadic products) with docstrings.
Replace the old FTensor usage with Fastor: swap include and adapt functions to use Fastor tensors via a new fastor_bridge. Add helpers to convert arma::mat to Fastor tensors, to convert Voigt<->4th-order Fastor, and utility routines (symmetry identity, push/pull wrappers, B-correction and Jaumann correction). Refactor multiple objective-rate conversion routines (e.g. DtauDe_2_DSDE, Dtau_LieDD_2_DSDE, DtauDe_JaumannDD_2_DSDE, DSDE_2_DtauDe, DSDE_2_Dtau_LieDD, DSDE_2_Dtau_JaumannDD, Dtau_LieDD_* converters, Dsigma_LieDD_* converters, DSDE_DBiotStressDU) to use these helpers, simplifying tensor contractions and removing FTensor-specific index boilerplate. Returns remain in Voigt matrix form via fastor4_to_voigt. This improves clarity and centralizes tensor conversions and objective-rate correction logic.
Replace FTensor usage in test/Libraries/Continuum_mechanics/Tobjective_rates.cpp with Fastor: update includes (<Fastor/Fastor.h> and fastor_bridge.hpp), remove FTensor using/headers, and use Fastor::Tensor for 4th-order tensors. Replace mat_FTensor helpers with explicit accumulation loops to build the BBBB tensor and convert it to Voigt via fastor4_to_voigt. Compute N (N_mat) by an explicit double contraction of the Fastor 4th-order tensor with D_test and use it when forming Omega_test. Keeps existing eigen/decomposition and get_BBBB calls while adapting test computations to the Fastor-based representation.
Replace usage of the old FTensor API with Fastor in Ttransfer tests: update file header, includes (add Fastor and fastor_bridge), and rename the test to Fastor_transfer. Convert rank-2 and Voigt/rank-4 round-trip checks to use arma_to_fastor2/fastor2_to_arma and voigt_to_fastor4/fastor4_to_voigt helpers, adjust types, and keep numeric assertions. Remove deprecated FTensor1/FTensor2-specific roundtrip tests that relied on the old FTensor interface.
Add OpenMP detection and conditional linking for the simcoon target in CMakeLists.txt. This finds OpenMP and, if available, links OpenMP::OpenMP_CXX privately to simcoon to enable parallel batch tensor operations; no-op when OpenMP is not found.
Rename test_single_len_is_one to test_single_len_raises and update the assertion to expect a TypeError when calling len(t) on a single Tensor2 instance. This aligns the test with the API behavior that single tensors should not support length semantics.
Replace manual Fastor tensor construction with arma_to_fastor2 (respecting Voigt flag) and include objective_rates.hpp. Simplify push_forward/pull_back by converting F/invF via arma_to_fastor2(false) instead of element-wise copies. Add new tensor4::push_forward overloads that accept a CoRate and a tau tensor: perform a Lie-level push-forward, apply corotational rate corrections (Jaumann, Green-Naghdi, Logarithmic variants) using Dtau_* helpers, and apply metric scaling (Kirchhoff→Cauchy) according to tensor type. Also add a generic arma::mat wrapper with size validation.
Replace the old memcpy-based to_fastor2 with a wrapper that calls arma_to_fastor2 and accepts a symmetric flag (default true). For non-symmetric tensors (F and invF) callers now pass symmetric=false to ensure correct ordering/transpose instead of using a raw memcpy. Updated call sites in DtauDe_2_DSDE, Dtau_LieDD_2_DSDE, DtauDe_JaumannDD_2_DSDE, DSDE_2_DtauDe, DSDE_2_Dtau_LieDD and DSDE_2_Dtau_JaumannDD and added a clarifying comment.
Replace manual Fastor::Tensor declaration and memcpy with arma_to_fastor2(mat::fixed<3,3>(invS)) when building invS_ in dinvSdSsym. This simplifies the code, avoids raw memory copies, and lets the helper auto-detect symmetry while keeping the einsum usage unchanged.
Replace manual Fastor::Tensor construction and memcpy with arma_to_fastor2(mat::fixed<3,3>(...)) for both A and B. This simplifies conversion from Armadillo matrices, auto-detects symmetry, and improves safety/readability while keeping the einsum and voigt extraction logic unchanged.
Introduce examples/umats/EPICP_tensor.cpp: an external UMAT plugin implementing an elastic-plastic model (isotropic hardening, von Mises, CCP return mapping) using the Tensor2/Tensor4 API at the interface level. The implementation uses typed stiffness and stress/strain tensors for safety and clarity while keeping the return-mapping inner loop in raw Voigt arma::vec for performance. Computes consistent tangent, work contributions, and updates state variables; exposes create_api/destroy_api for plugin loading. Build instructions and material/state variable layout are documented in the file header.
Add helpers to parse string names into VoigtType and Tensor4Type enums and provide convenience overloads that accept type names as std::string. Implemented parse_voigt_type and parse_tensor4_type with clear invalid_argument errors for unknown strings, added tensor2::from_voigt(const arma::vec&, const std::string&) and tensor4::tensor4(const arma::mat&, const std::string&) which delegate to the existing enum-based APIs. Changes in include/simcoon/Continuum_mechanics/Functions/tensor.hpp and src/Continuum_mechanics/Functions/tensor.cpp to expose these new overloads and parsing logic for easier string-based usage.
Rename and refactor EPICP UMAT example to use the Tensor2/Tensor4 interface. The file was moved to examples/umats/external/umat_plugin_ext_EPICP.cpp and documentation/comments were updated to describe typed stiffness, explicit from_voigt usage, and L.contract for stiffness*strain -> stress. The return-mapping inner loop remains in raw Voigt vectors for numerical efficiency; Mises now dispatches via the typed stress. Also updated the build/example compile command (macOS) and performed minor formatting/clarity tweaks (loop formatting, shortened comments, small reorderings) without changing the core algorithmic behavior.
Introduce examples/umats/external/umat_plugin_ext_NEOHC.cpp: a new external UMAT plugin implementing a compressible Neo-Hookean hyperelastic law using the Tensor2/Tensor4 API. The plugin reconstructs F from the Green–Lagrange strain, computes PKII (S = mu*(I - C^{-1}) + lambda*ln(J)*C^{-1}), pushes it forward to obtain the Cauchy stress, and builds both reference and spatial tangents via tensor4 push_forward. Material inputs are E, nu, alpha and a single state variable T_init; create_api/destroy_api are exported for dynamic loading by simcoon as an external UMAT.
Declare and implement string-based overloads for tensor2::zeros and tensor2::identity that parse a VoigtType from a string (using parse_voigt_type). Update the pybind wrapper to disambiguate the existing VoigtType overloads with static_cast so bindings remain stable after adding the new overloads. Files changed: header (declare overloads), tensor.cpp (implementations), and Python wrapper (explicit static_cast for zeros/identity).
Refactor tensor4 push/pull and batch ops, and expand docs.
- Add tensor4_kernel helper to select the 3x3 contraction matrix for push/pull based on Tensor4Type and direction (F, F^{-1}, F^T, F^{-T}); reject concentration tensor types with an error.
- Simplify push_forward/pull_back implementations to use the kernel and unify Piola (J) scaling logic for stiffness vs. compliance.
- Harden batch APIs by adding check_batch_broadcast to validate cube slice counts (prevent silent buffer overreads) and call it from all batch_* helpers (rotate, push_forward, pull_back, contract, rotate_t4, push_forward_t4, pull_back_t4).
- Improve corotational-rate push_forward: restrict B-correction to stiffness/generic tangents, compute the Truesdell (Lie) Kirchhoff tangent first, apply B^(4) corrections, and then apply metric scaling; clarify that the three logarithmic variants share the same B^(4) correction here and that their integrator differences live in objective_rates.cpp.
- Expand and clarify header comments for CoRate and push/pull behavior, documenting which kernels apply to stiffness vs. compliance and noting where integrator-level differences are implemented.
These changes improve correctness (prevent OOB reads), clarify intent, and centralize the index-kernel logic for tensor4 transformations.
This pull request introduces major improvements to the rotation and tensor mechanics infrastructure in the codebase, focusing on a new, comprehensive
Rotationclass with full Python and C++ support, enhanced documentation and examples, and the integration of the Fastor tensor library for efficient tensor operations. The changes modernize the rotation API, provide seamless interoperability withscipy.spatial.transform.Rotation, and introduce robust conversion utilities between Armadillo and Fastor tensors.Rotation and Tensor Mechanics Enhancements:
Introduced a new
Rotationclass (Python and C++) that inherits fromscipy.spatial.transform.Rotation, providing a unified and numerically stable interface for 3D rotations, SLERP interpolation, and direct Voigt notation operations. This includes support for various Euler angle conventions, intrinsic/extrinsic modes, and full Python bindings with NumPy integration. Extensive new and updated examples demonstrate usage and interoperability with SciPy, including batch operations and interpolation. [1] [2] [3] [4]Added a new C++ header
fastor_bridge.hppproviding efficient, zero-copy conversion utilities between Armadillo matrices and Fastor tensors, as well as Voigt-to-tensor and tensor-to-Voigt mappings and push-forward/pull-back operations for 4th-order tensors. This sets the stage for improved performance and maintainability in tensor mechanics code.Build System Integration:
Documentation Improvements:
Rotationmodule, updated function references and descriptions, and included a "What's New" section for Simcoon 2.0. Documentation now clearly distinguishes between themathsandrotationmodules and highlights new features and usage patterns. [1] [2] [3] [4] [5]