diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..a00ad8b --- /dev/null +++ b/.clang-format @@ -0,0 +1,141 @@ +--- +# Rust-style formatting for C++17 (ExaConstit) +Language: Cpp +Standard: c++17 + +# Line length matching rustfmt default +ColumnLimit: 100 + +# Indentation - Rust uses 4 spaces everywhere +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +ContinuationIndentWidth: 4 +AccessModifierOffset: -4 +IndentCaseLabels: false +IndentCaseBlocks: false +IndentGotoLabels: false +IndentPPDirectives: None +IndentExternBlock: NoIndent +IndentWrappedFunctionNames: false + +# Braces - Rust style (same line for functions/structs) +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true + +# Function and control flow formatting +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortLambdasOnASingleLine: Empty + +# Alignment - keep things clean like Rust +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: None +AlignConsecutiveDeclarations: None +AlignConsecutiveMacros: None +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: true + +# Pointer and reference alignment (Rust style: left-aligned with type) +PointerAlignment: Left +ReferenceAlignment: Left +DerivePointerAlignment: false + +# Spacing +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false + +# Line breaking +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true + +# Bin packing and arguments +BinPackArguments: false +BinPackParameters: false + +# Constructor initializer and inheritance +ConstructorInitializerIndentWidth: 4 +CompactNamespaces: false +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 + +# Include sorting - ExaConstit headers first, then TPLs, then stdlib +# NOTE: clang-format will alphabetize within each priority group +SortIncludes: CaseSensitive +IncludeBlocks: Regroup +IncludeCategories: + # ExaConstit project headers (quoted includes from project directories) + - Regex: '^"(boundary_conditions|fem_operators|mfem_expt|models|options|postprocessing|sim_state|solvers|system_driver|umats|utilities)/' + Priority: 1 + - Regex: '^"(system_driver)' + Priority: 1 + # Third-party library headers (RAJA, mfem, ecmech, snls, caliper) + - Regex: '^"(RAJA|mfem|ecmech|snls|caliper)' + Priority: 2 + # Standard C++ library (angle brackets, no file extension) + - Regex: '^<[a-z_]+>$' + Priority: 3 + # C standard library (angle brackets with .h extension) + - Regex: '^<[a-z_]+\.h>$' + Priority: 3 + # Catch-all for anything else + - Regex: '.*' + Priority: 99 + +# Namespace formatting +NamespaceIndentation: None +FixNamespaceComments: true + +# Penalties (for breaking decisions) +PenaltyBreakAssignment: 100 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 + +# Misc +ReflowComments: true +SortUsingDeclarations: true \ No newline at end of file diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..77413db --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,3 @@ +# .git-blame-ignore-revs +# First clang-format run so lots of changes... +fec3a715e3c8c9e86a92c03f8e6501ad58bbe68f \ No newline at end of file diff --git a/.github/workflows/build-ecmech/action.yml b/.github/workflows/build-ecmech/action.yml index 7b605f4..f876fbe 100644 --- a/.github/workflows/build-ecmech/action.yml +++ b/.github/workflows/build-ecmech/action.yml @@ -17,7 +17,7 @@ runs: steps: - name: Install ECMech run: | - git clone --single-branch --branch v0.4.1 --depth 1 ${{ inputs.ecmech-repo }} ${{ inputs.ecmech-dir }}; + git clone --single-branch --branch v0.4.3 --depth 1 ${{ inputs.ecmech-repo }} ${{ inputs.ecmech-dir }}; cd ${{ inputs.ecmech-dir }}; git submodule init; git submodule update; diff --git a/.github/workflows/build-mfem/action.yml b/.github/workflows/build-mfem/action.yml index 8a2c2f8..7eaa887 100644 --- a/.github/workflows/build-mfem/action.yml +++ b/.github/workflows/build-mfem/action.yml @@ -39,6 +39,7 @@ runs: -DMFEM_USE_CUDA=OFF \ -DMFEM_USE_OPENMP=OFF \ -DMFEM_USE_RAJA=ON -DRAJA_DIR=${{ inputs.raja-dir }} \ + -DCMAKE_CXX_STANDARD=17 \ -DCMAKE_BUILD_TYPE=Release make -j3; make install; diff --git a/.github/workflows/build-raja/action.yml b/.github/workflows/build-raja/action.yml index 81e597d..9964924 100644 --- a/.github/workflows/build-raja/action.yml +++ b/.github/workflows/build-raja/action.yml @@ -14,7 +14,7 @@ runs: steps: - name: Install RAJA run: | - git clone --single-branch --branch v2024.07.0 --depth 1 ${{ inputs.raja-repo }} ${{ inputs.raja-dir }}; + git clone --single-branch --branch v2025.09.1 --depth 1 ${{ inputs.raja-repo }} ${{ inputs.raja-dir }}; cd ${{ inputs.raja-dir }}; git submodule init; git submodule update; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 723c1e9..6ff47f4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -53,7 +53,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install numpy + pip install numpy pandas toml # Only get MPI if defined for the job. # TODO: It would be nice to have only one step, e.g. with a dedicated @@ -61,9 +61,12 @@ jobs: - name: get MPI (Linux) if: matrix.mpi == 'parallel' && matrix.os == 'ubuntu-latest' run: | - sudo apt-get install mpich libmpich-dev - export MAKE_CXX_FLAG="MPICXX=mpic++" - + sudo apt-get install openmpi-bin libopenmpi-dev + # Set MPI oversubscription globally for the job + echo "OMPI_MCA_rmaps_base_oversubscribe=1" >> $GITHUB_ENV + echo "OMPI_MCA_btl_vader_single_copy_mechanism=none" >> $GITHUB_ENV + echo "OMPI_MCA_btl_base_warn_component_unused=0" >> $GITHUB_ENV + # export MAKE_CXX_FLAG="MPICXX=mpic++" # Get RAJA through cache, or build it. # Install will only run on cache miss. - name: cache raja @@ -72,7 +75,7 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.RAJA_TOP_DIR }} - key: ${{ runner.os }}-build-${{ env.RAJA_TOP_DIR }}-v2.02 + key: ${{ runner.os }}-build-${{ env.RAJA_TOP_DIR }}-v2.03 - name: get raja if: matrix.mpi == 'parallel' && steps.raja-cache.outputs.cache-hit != 'true' @@ -88,7 +91,7 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.ECMECH_TOP_DIR }} - key: ${{ runner.os }}-build-${{ env.ECMECH_TOP_DIR }}-v2.02 + key: ${{ runner.os }}-build-${{ env.ECMECH_TOP_DIR }}-v2.04 - name: get ecmech if: matrix.mpi == 'parallel' && steps.ecmech-cache.outputs.cache-hit != 'true' @@ -140,7 +143,7 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.MFEM_TOP_DIR }} - key: ${{ runner.os }}-build-${{ env.MFEM_TOP_DIR }}-v2.04 + key: ${{ runner.os }}-build-${{ env.MFEM_TOP_DIR }}-v2.06 - name: install mfem if: matrix.mpi == 'parallel' && steps.mfem-cache.outputs.cache-hit != 'true' @@ -164,4 +167,13 @@ jobs: - name: cmake unit tests if: matrix.build-system == 'cmake' run: | - cd ${{ github.workspace }}/build/ && ctest --output-on-failure \ No newline at end of file + cd ${{ github.workspace }}/build/ + # Ensure MPI settings are active + export OMPI_MCA_rmaps_base_oversubscribe=1 + export OMPI_MCA_btl_vader_single_copy_mechanism=none + # Run tests with better output + ctest --output-on-failure --verbose + env: + OMPI_MCA_rmaps_base_oversubscribe: 1 + OMPI_MCA_btl_vader_single_copy_mechanism: none + OMPI_MCA_btl_base_warn_component_unused: 0 \ No newline at end of file diff --git a/README.md b/README.md index c64e1c9..37d2d5a 100644 --- a/README.md +++ b/README.md @@ -1,112 +1,444 @@ -# ExaConstit App +
-Updated: Feb. 6, 2025 +``` +███████╗██╗ ██╗ █████╗ ██████╗ ██████╗ ███╗ ██╗███████╗████████╗██╗████████╗ +██╔════╝╚██╗██╔╝██╔══██╗██╔════╝██╔═══██╗████╗ ██║██╔════╝╚══██╔══╝██║╚══██╔══╝ +█████╗ ╚███╔╝ ███████║██║ ██║ ██║██╔██╗ ██║███████╗ ██║ ██║ ██║ +██╔══╝ ██╔██╗ ██╔══██║██║ ██║ ██║██║╚██╗██║╚════██║ ██║ ██║ ██║ +███████╗██╔╝ ██╗██║ ██║╚██████╗╚██████╔╝██║ ╚████║███████║ ██║ ██║ ██║ +╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ +``` + +**High-Performance Crystal Plasticity & Micromechanics Simulation** + +*Velocity-based finite element framework for polycrystalline materials* + +[Quick Start](#quick-start) • [Documentation](#documentation) • [Examples](#examples) • [Contributing](#contributing) + +
+ +--- + +## What is ExaConstit? + +ExaConstit is a cutting-edge, **velocity-based finite element code** designed for high-fidelity simulation of polycrystalline materials. Built on LLNL's MFEM library, it delivers unprecedented performance for crystal plasticity and micromechanics modeling on leadership-class HPC systems. + +### Key Applications +- **Crystal Plasticity Simulations** - Grain-level deformation analysis +- **Bulk Constitutive Properties** - Homogenization of polycrystalline materials +- **Additive Manufacturing** - Process-structure-property relationships +- **Experimental Validation** - Lattice strain calculations for diffraction experiments + +## Features + +### **Advanced Finite Element Framework** +- **Velocity-Based Formulation** - Updated Lagrangian with superior convergence +- **Multi-Material Support** - Heterogeneous material regions +- **Adaptive Time Stepping** - Automatic timestep control for robustness + +### **Crystal Plasticity Modeling** +- **ExaCMech Integration** - Advanced crystal plasticity constitutive models +- **Multi-Crystal Support** - BCC, FCC, and HCP crystal structures +- **Grain-Level Resolution** - Individual grain orientations and properties +- **State Variable Evolution** - Full history-dependent material behavior + +### **High-Performance Computing** +- **GPU Acceleration** - CUDA and HIP support for maximum performance +- **MPI Parallelization** - Scales to tens of thousands of processors +- **Memory Efficiency** - Matrix-free partial assembly algorithms +- **Performance Portability** - RAJA framework for unified CPU/GPU code + +### **Material Model Flexibility** +- **ExaCMech Library** - State-of-the-art crystal plasticity models +- **UMAT Interface** - Abaqus-compatible user material subroutines +- **Custom Models** - Extensible architecture for new constitutive laws +- **Multi-Model Regions** - Different materials in different regions + +### **Advanced Post-Processing** +- **Visualization Output** - VisIt, ParaView, and ADIOS2 support +- **Volume Averaging** - Macroscopic stress-strain behavior and other useful parameters +- **Lattice Strain Analysis** - In-situ diffraction experiment simulation +- **Python Tools** - Comprehensive analysis and plotting scripts + +## Quick Start + +### Prerequisites +```bash +# Essential dependencies +MPI implementation (OpenMPI, MPICH, Intel MPI) +MFEM (v4.8+) with parallel/GPU support +ExaCMech (v0.4.3+) crystal plasticity library +RAJA (≥2024.07.x) performance portability +CMake (3.24+) +``` + +### Installation -Version 0.8.0 +ExaConstit provides automated installation scripts for different platforms. For detailed instructions, see [Installation Guide](docs/install.md). -# Description: -A principal purpose of this code app is to probe the deformation response of polycrystalline materials; for example, in homogenization to obtain bulk constitutive properties of metals. This is a nonlinear quasi-static, implicit solid mechanics code built on the MFEM library based on an updated Lagrangian formulation (velocity based). - -Currently, only Dirichlet boundary conditions (homogeneous and inhomogeneous by degree-of-freedom component) have been implemented. Neumann (traction) boundary conditions and a body force are not implemented. These Dirichlet boundary conditions can applied per surface/boundary using either the use of applied velocity or applied velocity gradients (constant strain) boundary conditions. One can also mix and match the use of those two boundary condition types across the various boundaries of the problem in order to more complicated material deformations such as pure torsion. Additionally, we support changing boundary conditions. So, it's possible to run cyclic, strain-rate jump tests, or a number of other type simulations. +#### Quick Start -On the material modelling front of things, ExaConstit can easily handle various material models. We provide a base class, `ExaModel`, to build each material model or class of material models. We currently support two very different material model libraries/interfaces through UMATs or ExaCMech. Crystal plasticity model capabilities are primarily provided through the ExaCMech library. +**Linux (Intel CPU)** +```bash +./scripts/install/unix_cpu_intel_install.sh +``` + +**macOS** +```bash +./scripts/install/unix_cpu_mac_install.sh +``` + +**NVIDIA GPU (CUDA)** +```bash +./scripts/install/unix_gpu_cuda_install.sh +``` + +**AMD GPU (HIP/ROCm)** +```bash +./scripts/install/unix_gpu_hip_install.sh +``` + +#### Before First Run + +⚠️ **You must customize the build configuration for your system.** + +Edit the appropriate config file in `scripts/install/configs/` and update: +- Compiler paths and versions +- MPI installation location +- Python executable path +- Module load commands (HPC systems) -Through the ExaCMech library, we are able to offer a range of crystal plasticity models that can run on the GPU. The current models that are available are a power law slip kinetic model with both nonlinear and linear variations of a voce hardening law for BCC and FCC materials, and a single Kocks-Mecking dislocation density hardening model with balanced thermally activated slip kinetics with phonon drag effects for BCC, FCC, and HCP materials. Any future model types to the current list are a simple addition within ExaConstit, but they will need to be implemented within ExaCMech. Given the templated structure of ExaCMech, some additions would be comparatively straightforward. +See the [Installation Guide](docs/install.md) for detailed setup instructions. -The code is capable of running on the GPU by making use of either a partial assembly formulation (no global matrix formed) or element assembly (only element assembly formed) of our typical FEM code. These methods currently only implement a simple matrix-free jacobi preconditioner. The MFEM team is currently working on other matrix-free preconditioners. Additionally, ExaConstit can be built to run with either CUDA or HIP-support in-order to run on most GPU-capable machines out there. +#### Build Options +```bash +# Clean rebuild +REBUILD=ON ./scripts/install/unix_gpu_cuda_install.sh -The code supports constant time steps, user-supplied variable time steps, or automatically calculated time steps. Boundary conditions are supplied for the velocity field on a surface. The code supports a number of different preconditioned Krylov iterative solvers (PCG, GMRES, MINRES) for either symmetric or nonsymmetric positive-definite systems. We also support either a newton raphson or newton raphson with a line search for the nonlinear solve. We might eventually look into supporting a nonlinear solver such as L-BFGS as well. +# Target specific GPU architecture +CMAKE_GPU_ARCHITECTURES=80 ./scripts/install/unix_gpu_cuda_install.sh -Finally, we support being able to make use of full integration or BBar type integration schemes to be used with various models. The default feature is to perform full integration of the element at the quadrature point. The BBar integration performs full integration of the deviatoric response with an element average integration for the volume response. The BBar method is based on the work given in [this paper](https://doi.org/10.1002/nme.1620150914) and more specifically we make use of Eq 23. It should be noted that currently we don't support a partial assembly formulation for the BBar integrations. +# Adjust parallel jobs +MAKE_JOBS=16 ./scripts/install/unix_cpu_intel_install.sh +``` +**Note for MI300A users:** Set `HSA_XNACK=1` before running simulations. -## Remark: -This code is still very much actively being developed. It should be expected that breaking changes can and will occur. So, we make no guarantees about stability at this point in time. Any available release should be considered stable but may be lacking several features of interest that are found in the ```exaconstit-dev``` branch. +For troubleshooting, manual builds, and advanced configuration, see the [Installation Guide](docs/install.md). -Currently, the code has been tested using monotonic and cyclic loading with either an auto-generated mesh that has been instantiated with grain data from some voxel data set or meshes formed from ```MFEM v1.0```. Meshes produced from Neper can also be used but do require some additional post-processing. See the ```Script``` section for ways to accomplishing this. +#### **Manual Build** +```bash +# Clone and prepare +git clone https://github.com/LLNL/blt.git cmake/blt +mkdir build && cd build -ExaCMech models are capable of running on the GPU. However, we currently have no plans for doing the same for UMAT-based kernels. The ExaCMech material class can be used as a guide for how to do the necessary set-up, material kernel, and post-processing step if a user would like to expand the UMAT features and submit a pull request to add the capabilities into ExaConstit. +# Configure +cmake .. \ + -DENABLE_MPI=ON \ + -DMFEM_DIR=${MFEM_INSTALL_DIR} \ + -DECMECH_DIR=${EXACMECH_INSTALL_DIR} \ + -DRAJA_DIR=${RAJA_INSTALL_DIR} -See the included ```options.toml``` to see all of the various different options that are allowable in this code and their default values. +# Build +make -j $(nproc) +``` -A TOML parser has been included within this directory, since it has an MIT license. The repository for it can be found at: https://github.com/ToruNiina/toml11/. +### First Simulation +```bash +# Run a crystal plasticity example +cd test/data +mpirun -np 4 ../../build/mechanics -opt voce_full.toml -Example UMATs maybe obtained from https://web.njit.edu/~sac3/Software.html . We have not included them due to a question of licensing. The ones that have been run and are known to work are the linear elasticity model and the neo-Hookean material. The ```umat_tests``` subdirectory in the ```src``` directory can be used as a guide for how to convert your own UMATs over to one with which ExaConstit can interface. +# Generate stress-strain plots +python ../../scripts/postprocessing/macro_stress_strain_plot.py +``` -Note: the grain.txt, props.txt and state.txt files are expected inputs for crystal-plasticity problems. If a mesh is provided it should be in the MFEM or cubit format which has the grains IDs already assigned to the element attributes. +## Examples -# Scripts -Useful scripts are provided within the ```scripts``` directory. The ```mesh_generator``` executable when generated can create an ```MFEM v1.0``` mesh for auto-generated mesh when provided a grain ID file. It is also capable of taking in a ```vtk``` mesh file that MFEM is capable of reading, and then it will generate the appropriate ```MFEM v1.0``` file format with the boundary element attributes being generated in the same way ExaConstit expects them. The ```vtk``` mesh currently needs to be a rectilinear mesh in order to work. All of the options for ```mesh_generator``` can be viewed by running ```./mesh_generator --help``` +### **Crystal Plasticity Simulation** +```toml +# options.toml - Crystal plasticity configuration +grain_file = "grain.txt" +orientation_file = "orientations.txt" -If you have version 4 of ```Neper``` then you can make use of the `-faset 'faces'` option while meshing and output things as a `gmsh` v2.2 file. Afterwards, you can make use of the `neper_v4_mesh.py` cli script in `scripts/meshing` to automatically use the faset information and autogenerate the boundary attributes that `MFEM\ExaConstit` can understand and use. Although, you will need to check and see which face corresponds to what boundary attribute, so you can correctly apply boundary conditions to the body. Further information is provided in the top level comment of the script for how to do this. +[Mesh] +filename = "polycrystal.mesh" -For older versions of neper v2-v3, an additional python script is provided called ```fepx2mfem_mesh.py``` that provides a method to convert from a mesh generated using Neper v3.5.2 in the FEpX format into the ```vtk``` format that can now be converted over to the ```MFEM v1.0``` format using the ```mesh_generator``` script. +[Materials] +[[Materials.regions]] +material_name = "titanium_alloy" +mech_type = "ExaCMech" -# Examples +[Materials.regions.model.ExaCMech] +shortcut = "evptn_HCP_A" -Several small examples that you can run are found in the ```test/data``` directory. These examples cover a wide range of different use cases of the code, but the `toml` file for each test case may not be representative of all the options as found in the `src/options.toml` file. +``` -# Postprocessing +### **Post-Processing Workflow** +```bash +# Extract stress-strain data +python scripts/postprocessing/macro_stress_strain_plot.py output/ -The ```scripts/postprocessing``` directory contains several useful post-processing tools. The ```macro_stress_strain_plot.py``` file can be used to generate macroscopic stress strain plots. An example script ```adios2_example.py``` is provided as example for how to make use of the ```ADIOS2``` post-processing files if ```MFEM``` was compiled with ```ADIOS2``` support. It's highly recommended to install ```MFEM``` with this library if you plan to be doing a lot of post-processing of data in python. +# Calculate lattice strains (experimental validation) +python scripts/postprocessing/calc_lattice_strain.py \ + --config lattice_strain_config.json -A set of scripts to perform lattice strain calculations similar to those found in powder diffraction type experiments can be found in the ```scripts/postprocessing``` directory. The appropriate python scripts are: `adios2_extraction.py`, `strain_Xtal_to_Sample.py`, and `calc_lattice_strain.py`. In order to use these scripts, one needs to run with the `light_up=true` option set in the `Visualization` table of your simulation option file. Alternatively, if you just use the `light_up` option and provide the necessary parameters as defined in the `src/options.toml` file you a set of insitu lattice strain calculations will be done. The cost of these insitu calculations is fairly nominal and are generally advisable to use when performing large scale simulations where this data is desireable. +# Generate visualization files +python scripts/postprocessing/adios2_example.py results.bp +``` -# Workflow Examples +## Output and Visualization + +### **Version 0.9 Output Updates** +ExaConstit v0.9 introduces significant improvements to output management and file organization: + +#### **Modern Configuration Support** +- **Legacy compatibility**: Previous option file formats continue to work +- **Conversion utility**: Use our conversion script to migrate to the modern TOML format: + ```bash + python scripts/exaconstit_old2new_options.py old_options.toml -o new_options.toml + ``` + +#### **Enhanced Output Files** +- **Headers included**: All simulation output files now contain descriptive headers +- **Time and volume data**: Automatically included in all output files so the auto_dt_file has been removed +- **Improved format**: Enhanced data organization (note: format differs from previous versions) +- **Basename-based directories**: Output location determined by `basename` and `Postprocessing.Projections.output_directory` settings in options file + ```toml + # if not provided defaults to option file name + basename = "exaconstit" # Creates output sub-directory: exaconstit/ + ``` + +#### **Advanced Visualization Control** +- **Backward compatibility**: Visualization files remain compatible with previous versions +- **User-friendly naming**: Visualization variable names updated for better clarity +- **Selective field output**: Specify exactly which fields to save (new capability): + ```toml + [PostProcessing.projections] + # Some of these values are only compatible with ExaCMech + enabled_projections = ["stress", "von_mises", "volume", "centroid", "dpeff", "elastic_strain"] + # if set to true then all defaults are outputted by default + auto_enable_compatible = false + ``` + +### **Migration Guide for Existing Users** +- **Existing simulations**: Previous option files work without modification +- **Output processing**: Update post-processing scripts to handle new file headers +- **Directory structure**: Account for new basename-based output organization +- **Visualization workflows**: Existing VisIt/ParaView workflows remain functional + +## Advanced Features + +### **Mesh Generation & Processing** +- **Auto-Generated Meshes** - From grain ID files +- **Neper Integration** - v4 mesh processing with boundary detection +- **Format Conversion** - VTK to MFEM +- **Boundary Attribute** - Automatic boundary labelling + +#### **Mesh Generator Utility** +The `mesh_generator` executable provides flexible mesh creation and conversion: +```bash +# Create MFEM mesh from grain ID file +./mesh_generator --grain_file grains.txt --output polycrystal.mesh + +# Convert VTK mesh to MFEM format with boundary attributes +./mesh_generator --vtk_input mesh.vtk --output converted.mesh + +# View all options +./mesh_generator --help +``` -We've provided several different useful workflows in the `workflows` directory. One is an optimization set of scripts that makes use of a genetic algorithm to optimize material parameters based on experimental results. Internally, it makes use of either a simple workflow manager for something like a workstation or it can leverage the python bindings to the Flux job queue manager created initially by LLNL to run on large HPC systems. +**Capabilities**: +- **Auto-generated meshes** from grain ID files +- **VTK to MFEM conversion** with automatic boundary attribute generation +- **Boundary Attribute** compatible with ExaConstit requirements -The other workflow is based on a UQ workflow for metal additive manufacturing that was developed as part of the ExaAM project. You can view the open short workshop paper for an overview of the ExaAM project's workflow and the results https://doi.org/10.1145/3624062.3624103 . This workflow connects microstructures provided by an outside code such as LLNL's ExaCA code (https://github.com/LLNL/ExaCA) or other sources such as nf-HEDM methods to local properties to be used by a part scale application code. The goal here is to utilize ExaConstit to run a ton of simulations rather than experiments in order to obtain data that can be used to parameterize macroscopic material models such as an anisotropic yield surface. +#### **Neper Integration** +**For Neper v4 users**: +```bash +# Generate mesh with face information +neper -M n100-id1.tess -faset 'faces' -format gmsh2.2 -# Installing Notes: +# Convert to ExaConstit format +python scripts/meshing/neper_v4_mesh.py input.msh output.mesh +``` -* git clone the LLNL BLT library into cmake directory. It can be obtained at https://github.com/LLNL/blt.git -* MFEM will need to be built with hypre v2.26.0-v2.30.0; metis5; RAJA v2022.x+; and optionally Conduit, ADIOS2, or ZLIB. - * Conduit and ADIOS2 supply output support. ZLIB allows MFEM to read in gzip mesh files or save data as being compressed. - * You'll need to use the exaconstit-dev branch of MFEM found on this fork of MFEM: https://github.com/rcarson3/mfem.git - * We do plan on upstreaming the necessary changes needed for ExaConstit into the master branch of MFEM, so you'll no longer be required to do this - * Version 0.8.0 of ExaConstit is compatible with the following mfem hash: - 31b42daa3cdddeff04ce3f59befa769b262facd7 - or - 29a8e15382682babe0f5c993211caa3008e1ec96 - * Version 0.7.0 of Exaconstit is compatible with the following mfem hash 78a95570971c5278d6838461da6b66950baea641 - * Version 0.6.0 of ExaConstit is compatible with the following mfem hash 1b31e07cbdc564442a18cfca2c8d5a4b037613f0 - * Version 0.5.0 of ExaConstit required 5ebca1fc463484117c0070a530855f8cbc4d619e - * ExaCMech is required for ExaConstit to be built and can be obtained at https://github.com/LLNL/ExaCMech.git and now requires the develop branch. ExaCMech depends internally on SNLS, from https://github.com/LLNL/SNLS.git. We depend on v0.4.1 of ExaCMech as of this point in time. - * GPU-builds of ExaCMech >= v0.4.1 and thus ExaConstit now require the RAJA Portability Suite (RAJA, Umpire, and CHAI) to compile and run on the GPU. We currently leverage the `v2024.07.0` tag for all of the RAJA Portability Suite repos. - * For versions of ExaCMech >= 0.3.3, you'll need to add `-DENABLE_SNLS_V03=ON` to the cmake commands as a number of cmake changes were made to that library and SNLS. -* RAJA is required for ExaConstit to be built and should be the same one that ExaCMech and MFEM are built with. It can be obtained at https://github.com/LLNL/RAJA. Currently, RAJA >= 2022.10.x is required for ExaConstit due to a dependency update in MFEMv4.5. -* An example install bash script for unix systems can be found in ```scripts/install/unix_install_example.sh```. This is provided as an example of how to install ExaConstit and its dependencies, but it is not guaranteed to work on every system. A CUDA version of that script is also included in that folder (`unix_gpu_cuda_install_example.sh`), and only minor modifications are required if using a version of Cmake >= 3.18.*. In those cases ```CUDA_ARCH``` has been changed to ```CMAKE_CUDA_ARCHITECTURES```. You'll also need to look up what you're CUDA architecture compute capability is set to and modify that within the script. Currently, it is set to ```sm_70``` which is associated with the Volta architecture. We also have a HIP version included in that folder (`unix_gpu_cuda_install_example.sh`). It's based on a LLNL El Capitan-like system build of things so things might need tweaking for other AMD GPU machines. +**For Neper v2-v3 users**: +```bash +# Convert FEpX format to VTK +python scripts/meshing/fepx2mfem_mesh.py fepx_mesh.txt vtk_mesh.vtk +# Then use mesh_generator for final conversion +./mesh_generator --vtk_input vtk_mesh.vtk --output final.mesh +``` -* Create a build directory and cd into there -* Run ```cmake .. -DENABLE_MPI=ON -DENABLE_FORTRAN=OFF -DMFEM_DIR{mfem's installed cmake location} -DBLT_SOURCE_DIR=${BLT cloned location if not located in cmake directory} -DECMECH_DIR=${ExaCMech installed cmake location} -DRAJA_DIR={RAJA installed location} -DSNLS_DIR={SNLS installed cmake location}``` -* Run ```make -j 4``` +#### **Required Input Files for Crystal Plasticity** +When setting up crystal plasticity simulations, you need (file names can be different): +##### **Essential Files** +- **`grain.txt`**: Element-to-grain ID mapping (one ID per element) +- **`props.txt`**: Material parameters for each grain type/material +- **`state.txt`**: Initial internal state variables (typically zeros) +- **`orientations.txt`**: Crystal orientations (quaternions) +- **`regions.txt`**: Mapping from grain-to-region ID mapping -# Future Implemenations Notes: - -* Multiple phase materials -* Commonly used post-processing tools either through Python or C++ code -# Contributors: -* Robert A. Carson (Principal Developer) - * carson16@llnl.gov +##### **Mesh Requirements** +- **Format**: MFEM v1.0 or Cubit format +- **Grain IDs**: Must be assigned to element attributes in the mesh +- **Boundary attributes**: Required for boundary condition application -* Nathan Barton +### **Experimental Integration** +- **Lattice Strain Calculations** - Powder diffraction simulation +- **In-Situ Analysis** - Real-time lattice strain monitoring +- **Microstructure Coupling** - Integration with ExaCA and other tools -* Steven R. Wopschall (initial contributions) +#### **Stress-Strain Analysis** +```bash +# Generate macroscopic stress-strain plots +python scripts/postprocessing/macro_stress_strain_plot.py +``` -* Jamie Bramwell (initial contributions) +#### **Lattice Strain Analysis** +Simulate powder diffraction experiments with in-situ lattice strain calculations: +```bash +# Extract lattice strain data from ADIOS2 files +python scripts/postprocessing/adios2_extraction.py -# CONTRIBUTING +# Transform crystal strains to sample coordinates +python scripts/postprocessing/strain_Xtal_to_Sample.py -ExaConstit is distributed under the terms of the BSD-3-Clause license. All new contributions must be made under this license. +# Calculate lattice strains for specific HKL directions +python scripts/postprocessing/calc_lattice_strain.py +``` -# Citation -If you're using ExaConstit and would like to cite us please use the below `bibtex` entry. Additionally, we would love to be able to point to ExaConstit's use in the literature and elsewhere so feel free to message us with a link to your work as Google Scholar does not always pick up the below citation. We can then list your work among the others that have used our code. +**Enable lattice strain output** in your simulation: +```toml +[Visualizations] +light_up = true # Enables in-situ lattice strain calculations +# Configure specific HKL directions and parameters in options.toml ``` + +##### **ADIOS2 Integration** +For large-scale data analysis (recommended for extensive post-processing): +```bash +# Example ADIOS2 data processing +python scripts/postprocessing/adios2_example.py + +# Requires MFEM built with ADIOS2 support +``` + +### **Materials Science Workflows** + +#### **Parameter Optimization** +Multi-objective genetic algorithm-based optimization for material parameter identification: +```bash +# Optimize material parameters against experimental data +cd workflows/optimization/ +python ExaConstit_NSGA3.py +``` + +**Features**: +- **Flux integration**: Leverage LLNL's Flux job manager for HPC systems +- **Workstation support**: Simple workflow manager for desktop systems +- **Multi-objective optimization**: Fit multiple experimental datasets simultaneously + +#### **Uncertainty Quantification (UQ)** +ExaAM integration for additive manufacturing applications: +```bash +# UQ workflow for process-structure-property relationships +cd workflows/Stage3/pre_main_post_script +python chal_prob_full.py +``` + +**Applications**: +- **Microstructure-property linkage**: Connect ExaCA microstructures to mechanical properties +- **Part-scale modeling**: Generate data for macroscopic material model parameterization +- **Process optimization**: Optimize additive manufacturing parameters +- **Anisotropic yield surface**: Development from polycrystal simulations + +**Academic Reference**: [ExaAM UQ Workflow Paper](https://doi.org/10.1145/3624062.3624103) + +## Documentation + +### **Getting Started** +- [Developer's Guide](developers_guide.md) - Complete development documentation +- [Configuration Reference](src/options.toml) - All available simulation options + +### **Scientific Background** +- **Crystal Plasticity Theory** - Micromechanics fundamentals +- **Finite Element Implementation** - Velocity-based formulation details +- **GPU Acceleration** - Performance optimization strategies + +### **Tutorials & Examples** +- **Basic Simulations** - Simple deformation tests +- **Complex Loading** - Cyclic and multiaxial loading +- **Multi-Material Problems** - Composite and layered materials +- **Experimental Validation** - Lattice strain analysis + +## Ecosystem & Integration + +### **Related LLNL Projects** +- **[ExaCMech](https://github.com/LLNL/ExaCMech)** - Crystal plasticity constitutive models +- **[ExaCA](https://github.com/LLNL/ExaCA)** - Cellular automata code for alloy nucleation and solidification +- **[MFEM](https://mfem.org)** - Finite element methods library +- **ExaAM** - Exascale Computing Project project on additive manufacturing for process-structure-properties calculations + +### **Third-Party Tools** +- **Neper** - Polycrystal mesh generation +- **VisIt/ParaView** - Visualization and analysis +- **ADIOS2** - High-performance I/O +- **Python Ecosystem** - NumPy, SciPy, Matplotlib integration + +## Performance & Scalability + +### **Benchmarks** +- **CPU Performance** - Scales to 1000+ MPI processes +- **GPU Acceleration** - 15-25x speedup on V100 or MI250x/MI300a systems +- **Memory Efficiency** - Matrix-free algorithms reduce memory footprint +- **I/O Performance** - ADIOS2 integration for petascale data management + +### **Optimization Features** +- **Partial Assembly** - Matrix-free operator evaluation +- **Device Memory Management** - Automatic host/device transfers +- **Communication Optimization** - Minimal MPI collective operations + +## Contributing + +We welcome contributions from the materials science and computational mechanics communities! + +### **Development** +```bash +# Fork the repository and create a feature branch +git checkout -b feature/amazing-new-capability + +# Make your changes with comprehensive tests +# Follow our C++17 coding standards + +# Submit a pull request with detailed description +``` + +### **Contribution Areas** +- **Material Models** - New constitutive relationships +- **Boundary Conditions** - Extended loading capabilities such as Neumann BCs or periodic BCs +- **Post-Processing** - Analysis and visualization tools +- **Performance** - GPU optimization and scalability +- **Documentation** - Tutorials and examples + +### **Getting Help** +- **GitHub Issues** - Bug reports and feature requests +- **Discussions** - Technical questions and community support +- **Documentation** - Comprehensive guides and API reference + +## License & Citation + +ExaConstit is distributed under the **BSD-3-Clause license**. All contributions must be made under this license. + +### **Citation** +If you use ExaConstit in your research, please cite the below. Additionally, we would love to be able to point to ExaConstit's use in the literature and elsewhere so feel free to message us with a link to your work as Google Scholar does not always pick up the below citation. We can then list your work among the others that have used our code. + +```bibtex @misc{ exaconstit, title = {{ExaConstit}}, author = {Carson, Robert A. and Wopschall, Steven R. and Bramwell, Jamie A.}, @@ -123,10 +455,28 @@ annote = { } ``` -# LICENSE +### LICENSE License is under the BSD-3-Clause license. See [LICENSE](LICENSE) file for details. And see also the [NOTICE](NOTICE) file. `SPDX-License-Identifier: BSD-3-Clause` ``LLNL-CODE-793434`` + +## Core Team + +### **Lawrence Livermore National Laboratory** +- **Robert A. Carson** (Principal Developer) - carson16@llnl.gov +- **Nathan Barton** - Initial Development +- **Steven R. Wopschall** - Initial Development +- **Jamie Bramwell** - Initial Development + +--- + +
+ +**Built at Lawrence Livermore National Laboratory** + +*Advancing materials science through high-performance computing* + +
\ No newline at end of file diff --git a/cmake/CMakeBasics.cmake b/cmake/CMakeBasics.cmake index 6e36490..6473f8d 100644 --- a/cmake/CMakeBasics.cmake +++ b/cmake/CMakeBasics.cmake @@ -4,7 +4,7 @@ set(PACKAGE_BUGREPORT "carson16@llnl.gov") set(EXACONSTIT_VERSION_MAJOR 0) -set(EXACONSTIT_VERSION_MINOR 8) +set(EXACONSTIT_VERSION_MINOR 9) set(EXACONSTIT_VERSION_PATCH \"0\") set(HEADER_INCLUDE_DIR diff --git a/cmake/ExaConstitOptions.cmake b/cmake/ExaConstitOptions.cmake index a9908d6..0ba1d29 100644 --- a/cmake/ExaConstitOptions.cmake +++ b/cmake/ExaConstitOptions.cmake @@ -10,12 +10,3 @@ option(ENABLE_CUDA "Enable CUDA" OFF) option(ENABLE_HIP "Enable HIP" OFF) option(ENABLE_OPENMP "Enable OpenMP" OFF) - -option(ENABLE_SNLS_V03 "Enable building library with v0.3.0+ of SNLS" OFF) - -# Force atleast static if user turns off both -# if(NOT BUILD_STATIC_LIBS AND NOT BUILD_SHARED_LIBS) -# message("Both static and shared libaries were disabled." -# "Building static libraries re-enabled.") -# set(BUILD_STATIC_LIBS ON CACHE BOOL "Build static libraries" FORCE) -# endif(NOT BUILD_STATIC_LIBS AND NOT BUILD_SHARED_LIBS) diff --git a/cmake/blt b/cmake/blt index fb4246b..e783e30 160000 --- a/cmake/blt +++ b/cmake/blt @@ -1 +1 @@ -Subproject commit fb4246b8bae74c3d7291bef9698fd38863844680 +Subproject commit e783e30f2823ee1a208f7f90741b41c1f5a08063 diff --git a/cmake/thirdpartylibraries/SetupThirdPartyLibraries.cmake b/cmake/thirdpartylibraries/SetupThirdPartyLibraries.cmake index d38ab81..bf0df07 100644 --- a/cmake/thirdpartylibraries/SetupThirdPartyLibraries.cmake +++ b/cmake/thirdpartylibraries/SetupThirdPartyLibraries.cmake @@ -8,7 +8,8 @@ set(_tpls snls exacmech mfem - caliper) + caliper + threads) foreach(_tpl ${_tpls}) string(TOUPPER ${_tpl} _uctpl) @@ -138,4 +139,69 @@ if (DEFINED CALIPER_DIR) endif() else() message("Caliper support disabled") +endif() + +################################ +# Threads (platform-specific) +################################ + +set(EXACONSTIT_THREADS_EXPLICIT_LINK FALSE CACHE INTERNAL "Whether explicit thread linking is required") + +if(UNIX AND NOT APPLE) + find_package(Threads REQUIRED) + include(CheckCXXSourceCompiles) + + # Test 1: Basic thread support without any flags + set(CMAKE_REQUIRED_LIBRARIES_SAVE ${CMAKE_REQUIRED_LIBRARIES}) + set(CMAKE_REQUIRED_LIBRARIES "") + + check_cxx_source_compiles(" + #include + #include + #include + #include + int main() { + std::atomic counter{0}; + std::mutex m; + std::condition_variable cv; + + std::thread t([&]{ + std::unique_lock lock(m); + counter++; + cv.notify_one(); + }); + + t.join(); + return counter.load(); + }" THREADS_IMPLICIT_LINK) + + # Test 2: If implicit didn't work, verify explicit works + if(NOT THREADS_IMPLICIT_LINK) + set(CMAKE_REQUIRED_LIBRARIES Threads::Threads) + check_cxx_source_compiles(" + #include + int main() { + std::thread t([]{}); + t.join(); + return 0; + }" THREADS_EXPLICIT_WORKS) + + if(NOT THREADS_EXPLICIT_WORKS) + message(FATAL_ERROR "Threading support not functional even with explicit linking!") + endif() + endif() + + # Restore + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES_SAVE}) + + # Register if needed + if(NOT THREADS_IMPLICIT_LINK) + message(STATUS " Result: Explicit pthread linking REQUIRED") + set(EXACONSTIT_THREADS_EXPLICIT_LINK TRUE CACHE INTERNAL "Whether explicit thread linking is required") + else() + message(STATUS " Result: pthread implicitly linked (no action needed)") + endif() + +elseif(APPLE) + message(STATUS "Threads support built-in on macOS") endif() \ No newline at end of file diff --git a/developers_guide.md b/developers_guide.md new file mode 100644 index 0000000..ca152e6 --- /dev/null +++ b/developers_guide.md @@ -0,0 +1,784 @@ +# ExaConstit Developer's Guide + +## Table of Contents +1. [Introduction](#introduction) +2. [Prerequisites](#prerequisites) +3. [Installation](#installation) +4. [Dependency Version Compatibility](#dependency-version-compatibility) +5. [Codebase Overview](#codebase-overview) +6. [Source Directory Structure](#source-directory-structure) +7. [Key Components](#key-components) +8. [Configuration System](#configuration-system) +9. [Advanced Solver Configuration](#advanced-solver-configuration) +10. [Building and Testing](#building-and-testing) +11. [Development Workflow](#development-workflow) +12. [UMAT Development Resources](#umat-development-resources) +13. [Contributing Guidelines](#contributing-guidelines) + +## Introduction + +ExaConstit is a high-performance, velocity-based, updated Lagrangian finite element code for nonlinear solid mechanics problems with a focus on micromechanics modeling. Built on the MFEM library, it specializes in crystal plasticity simulations and bulk constitutive property determination for polycrystalline materials. + +**Key Features:** +- Velocity-based updated Lagrangian formulation +- Crystal plasticity and micromechanics modeling +- GPU acceleration with CUDA/HIP support +- MPI parallelization for HPC systems +- Integration with ExaCMech material library +- UMAT interface support +- Advanced post-processing capabilities + +## Prerequisites + +### Required Knowledge +- **C++17**: Modern C++ standards and best practices +- **Finite Element Method (FEM)**: Theory and implementation +- **Solid Mechanics**: Nonlinear mechanics, crystal plasticity +- **Numerical Methods**: Newton-Raphson, Krylov iterative solvers +- **Parallel Computing**: MPI, OpenMP, GPU programming concepts + +### System Requirements +- C++17 compatible compiler (GCC 7+, Clang 5+, Intel 19+) +- MPI implementation (OpenMPI, MPICH, Intel MPI) +- CMake 3.24 or higher +- Git for version control + +## Installation + +### Quick Start +For detailed installation instructions, refer to the build scripts in `scripts/install/`: + +- **Linux/Unix**: `scripts/install/unix_install_example.sh` +- **GPU (CUDA)**: `scripts/install/unix_gpu_cuda_install_example.sh` +- **GPU (HIP/AMD)**: `scripts/install/unix_gpu_hip_install_example.sh` + +### Dependencies + +**Core Dependencies:** +- **MFEM** (v4.8+): Finite element library with parallel/GPU support +- **ExaCMech** (v0.4.3+): Crystal plasticity constitutive model library +- **RAJA** (≥2024.07.x): Performance portability framework +- **UMPIRE** (≥2024.07.x): (GPU-only) Performance portability framework +- **CHAI** (≥2024.07.x): (GPU-only) Performance portability framework +- **BLT**: LLNL build system +- **SNLS**: Nonlinear solver library + +**Optional Dependencies:** +- **ADIOS2**: (MFEM-based) High-performance I/O for visualization +- **Caliper**: Performance profiling + +### Basic Build Process +```bash +git submodule init && git submodule update + +# Create build directory +mkdir build && cd build + +# Configure +cmake .. \ + -DENABLE_MPI=ON \ + -DENABLE_FORTRAN=OFF \ + -DMFEM_DIR=${MFEM_INSTALL_DIR} \ + -DECMECH_DIR=${EXACMECH_INSTALL_DIR} \ + -DRAJA_DIR=${RAJA_INSTALL_DIR} \ + -DSNLS_DIR=${SNLS_INSTALL_DIR} + +# Build +make -j 4 +``` + +## Dependency Version Compatibility + +### **MFEM Requirements** +ExaConstit requires a specific MFEM development branch with ExaConstit-specific features: + +#### **Current Requirements** +- **Repository**: https://github.com/rcarson3/mfem.git +- **Branch**: `exaconstit-dev` +- **Version Dependencies**: + - **v0.9.0**: Compatible with MFEM hashes `a6bb7b7c2717e991b52ad72460f212f7aec1173e` + - **v0.8.0**: Compatible with MFEM hashes `31b42daa3cdddeff04ce3f59befa769b262facd7` or `29a8e15382682babe0f5c993211caa3008e1ec96` + - **v0.7.0**: Compatible with MFEM hash `78a95570971c5278d6838461da6b66950baea641` + - **v0.6.0**: Compatible with MFEM hash `1b31e07cbdc564442a18cfca2c8d5a4b037613f0` + - **v0.5.0**: Required MFEM hash `5ebca1fc463484117c0070a530855f8cbc4d619e` + +#### **MFEM Build Requirements** +```bash +# Required dependencies for MFEM +cmake .. \ + -DMFEM_USE_MPI=ON \ + -DMFEM_USE_METIS_5=ON \ + -DMFEM_USE_HYPRE=ON \ # v2.26.0-v2.30.0 + -DMFEM_USE_RAJA=ON \ # v2022.x+ + -DMFEM_USE_ADIOS2=ON \ # Optional: high-performance I/O + -DMFEM_USE_ZLIB=ON # Optional: compressed mesh support +``` + +**Note**: Future releases will integrate these changes into MFEM master branch, eliminating the need for the development fork. + +### **ExaCMech Version Requirements** +- **Repository**: https://github.com/LLNL/ExaCMech.git +- **Branch**: `develop` (required) +- **Version**: v0.4.3+ required +- **SNLS Dependency**: https://github.com/LLNL/SNLS.git + +### **RAJA Portability Suite** +For GPU builds of ExaCMech >= v0.4.3: + +#### **Required Components** +- **RAJA**: Performance portability framework +- **Umpire**: Memory management +- **CHAI**: Array abstraction + +#### **Version Requirements** +- **Tag**: `v2024.07.0` for all RAJA Portability Suite repositories +- **Important**: All RAJA suite components (RAJA, Umpire, CHAI) must use matching versions +- **Minimum RAJA**: v2024.07.0 +- **Note**: Version mismatch between RAJA components can cause build failures or runtime errors + +### **Additional Dependencies** +- **HYPRE**: v2.26.0 - v2.30.0 (algebraic multigrid / various preconditioners) +- **METIS**: Version 5 (mesh partitioning) +- **ADIOS2**: Optional (high-performance parallel I/O) +- **ZLIB**: Optional (compressed mesh and data support) + +## Codebase Overview + +ExaConstit follows a modular architecture designed for extensibility and performance: + +``` +ExaConstit/ +├── src/ # Main source code +├── test/ # Test cases and examples +├── scripts/ # Build scripts and utilities +├── workflows/ # Optimization and UQ workflows +└── cmake/ # Build system configuration +``` + +### Design Philosophy +- **Modularity**: Clear separation of concerns between FEM operators, material models, and solvers +- **Performance**: GPU acceleration and memory-efficient algorithms +- **Extensibility**: Plugin architecture for material models and boundary conditions +- **Standards**: Modern C++17 practices and comprehensive documentation + +## Source Directory Structure + +The `src/` directory contains the core ExaConstit implementation organized into modular components: + +### Primary Files +- **`mechanics_driver.cpp`**: Main application entry point and simulation orchestration +- **`system_driver.hpp/cpp`**: Core driver class managing the Newton-Raphson solution process +- **`userumat.h`**: Interface definitions for UMAT material model integration + +### Key Directories + +#### `boundary_conditions/` +**Purpose**: Boundary condition management and enforcement +- **`BCData.hpp/cpp`**: Data structures for boundary condition storage +- **`BCManager.hpp/cpp`**: Boundary condition application and management + +**Key Features**: +- Dirichlet velocity and velocity gradient boundary conditions +- Time-dependent BC scaling and ramping +- Component-wise BC application for selective spatial directions +- Support for multiple BC regions with different behaviors + +#### `fem_operators/` +**Purpose**: Finite element operators and integration routines +- **`mechanics_operator.hpp/cpp`**: Nonlinear mechanics operator implementation +- **`mechanics_operator_ext.hpp/cpp`**: Extended operator functionality +- **`mechanics_integrators.hpp/cpp`**: Element-level integration kernels + +**Key Features**: +- Element assembly (EA) and partial assembly (PA) modes +- B-bar integration for near-incompressible materials +- GPU-optimized kernel implementations +- Matrix-free operator evaluation + +#### `models/` +**Purpose**: Material constitutive model interface and implementations +- **`mechanics_model.hpp/cpp`**: Abstract base class `ExaModel` interface +- **`mechanics_ecmech.hpp/cpp`**: ExaCMech crystal plasticity integration +- **`mechanics_umat.hpp/cpp`**: UMAT interface implementation +- **`mechanics_multi_model.hpp/cpp`**: Multi-region material management + +**Supported Models**: +- ExaCMech crystal plasticity (FCC, BCC, HCP) +- User-defined UMAT subroutines +- Multi-material region support + +#### `options/` +**Purpose**: Configuration file parsing and option management +- **`option_parser_v2.hpp/cpp`**: Modern TOML-based parser +- **`option_material.cpp`**: Material configuration parsing +- **`option_mesh.cpp`**: Mesh and geometry options +- **`option_boundary_conditions.cpp`**: BC configuration parsing +- **`option_solvers.cpp`**: Linear and nonlinear solver settings +- **`option_time.cpp`**: Time-stepping parameters +- **`option_post_processing.cpp`**: Post-processing configuration +- **`option_enum.cpp`**: Enumeration type conversions +- **`option_util.hpp`**: Utility functions for option parsing + +**Features**: +- Backward compatibility with legacy formats +- Hierarchical configuration structure +- Comprehensive validation and error reporting + +#### `postprocessing/` +**Purpose**: Output management and field calculations +- **`postprocessing_driver.hpp/cpp`**: Main post-processing orchestration +- **`postprocessing_file_manager.hpp`**: File I/O and directory management +- **`projection_class.hpp/cpp`**: Field projection operations +- **`mechanics_lightup.hpp/cpp`**: Lattice strain calculations + +**Capabilities**: +- Volume-averaged stress/strain/deformation gradient +- Lattice strain calculations for diffraction experiments +- Visualization output (VisIt, ParaView, ADIOS2) +- Structured output file organization + +#### `sim_state/` +**Purpose**: Simulation state management and field storage +- **`simulation_state.hpp/cpp`**: Central state container class + +**Manages**: +- Finite element spaces and mesh data +- Solution vectors (displacement, velocity) +- Material properties and state variables +- Time-stepping information + +#### `solvers/` +**Purpose**: Linear and nonlinear solver implementations +- **`mechanics_solver.hpp/cpp`**: Newton-Raphson solver and variants + +**Features**: +- Standard Newton-Raphson +- Newton with line search +- Adaptive step size control +- Device-aware implementations + +#### `umat_tests/` +**Purpose**: Example UMAT implementations for testing +- **`umat.f`**: Example Fortran UMAT implementation +- **`umat.cxx`**: Example C++ UMAT implementation +- **`userumat.cxx`**: UMAT loader example +- **`userumat.h`**: UMAT interface definitions + +#### `utilities/` +**Purpose**: Helper functions and utility classes +- **`mechanics_log.hpp`**: Logging and performance monitoring +- **`unified_logger.hpp/cpp`**: Unified logging system for all components +- **`mechanics_kernels.hpp/cpp`**: Computational kernels for mechanics operations +- **`assembly_ops.hpp`**: Assembly operation utilities +- **`rotations.hpp`**: Rotation and orientation utilities +- **`strain_measures.hpp`**: Strain computation utilities +- **`dynamic_umat_loader.hpp/cpp`**: Runtime UMAT library loading + +**Provides**: +- Performance profiling integration +- Mathematical operations for mechanics +- Debugging and diagnostic tools +- Dynamic loading of user material models + +#### `mfem_expt/` +**Purpose**: MFEM extensions and experimental features +- **`partial_qspace.hpp/cpp`**: Partial quadrature space implementations +- **`partial_qfunc.hpp/cpp`**: Partial quadrature function utilities + +**Features**: +- Experimental finite element enhancements +- Performance optimizations for specific use cases +- Research and development components + +### Organization Principles +- **Modular Design**: Clear separation between components +- **Header/Implementation Pairs**: Consistent `.hpp/.cpp` organization +- **Device Portability**: GPU-aware implementations throughout +- **Template Usage**: Modern C++17 templates for performance +- **Namespace Structure**: `exaconstit::` for internal components + +## Key Components + +### SystemDriver Class +The `SystemDriver` class orchestrates the entire simulation workflow, managing the Newton-Raphson solution process and coordinating between components. + +**Responsibilities**: +- Newton-Raphson nonlinear solution management +- Linear solver and preconditioner setup +- Boundary condition enforcement and updates +- Material model coordination +- Solution advancement + +**Key Methods**: +```cpp +void SystemDriver::Solve(); // Main Newton-Raphson solution +void SystemDriver::SolveInit(); // Initial corrector step for BC changes +void SystemDriver::UpdateEssBdr(); // Update essential boundary conditions +void SystemDriver::UpdateVelocity(); // Apply velocity boundary conditions +void SystemDriver::UpdateModel(); // Update material models after convergence +``` + +### NonlinearMechOperator Class +The finite element operator extending MFEM's NonlinearForm that provides: +- Residual evaluation for Newton-Raphson iterations +- Jacobian computation and assembly +- Essential DOF management +- Support for different assembly strategies (PA/EA/FULL) + +### SimulationState Class +Central container managing all simulation data and providing unified access to: +- Mesh and finite element spaces +- Solution fields (velocity, displacement) +- Material properties and state variables +- Quadrature functions for field data +- Time-stepping information + +### Material Model Interface +Base class `ExaModel` defines the constitutive model interface: +```cpp +// Main execution method for material model computations +virtual void ModelSetup(nqpts, nelems, space_dim, nnodes, + jacobian, loc_grad, vel) = 0; + +// Update state variables after converged solution +virtual void UpdateModelVars() = 0; + +// Get material properties for this region +const std::vector& GetMaterialProperties() const; +``` + +### MultiExaModel Class +Manages multiple material regions within a single simulation: +- Coordinates material model execution across regions +- Routes region-specific data from SimulationState +- Handles heterogeneous material configurations + +### PostProcessingDriver Class +Manages all output and post-processing operations: +- Volume averaging calculations (stress, strain, etc.) +- Field projections for visualization +- File output management +- Support for VisIt, ParaView, and ADIOS2 + +### BCManager Class +Singleton pattern manager for boundary conditions: +- Tracks time-dependent boundary condition changes +- Manages multiple BC types (velocity, velocity gradient) +- Provides BC data to SystemDriver and operators + +## Configuration System + +ExaConstit uses TOML-based configuration files for all simulation parameters: + +### Main Configuration File (`options.toml`) +```toml +basename = "simulation_name" +version = "0.9.0" + +[Mesh] +filename = "mesh.mesh" +refine_serial = 0 + +[Time.Fixed] +dt = 1.0e-3 +t_final = 1.0 +[Solvers] + assembly = "ea" + [Solvers.Krylov] + rel_tol = 1.0e-12 + abs_tol = 1.0e-30 + linear_solver = "CG" + +[Materials] +# Material definitions... + +[BCs] +# Boundary condition specifications... +``` + +### Modular Configuration +- **External material files**: `materials = ["material1.toml", "material2.toml"]` +- **Grain data files**: `grain_file = "grain.txt"`, `orientation_file = "orientations.txt"` + +## Advanced Solver Configuration + +### **Assembly Methods** +ExaConstit supports multiple finite element assembly strategies optimized for different hardware: + +#### **Partial Assembly (PA)** +```toml +[Solvers] +assembly = "PA" +``` +- **Memory efficient**: No global matrix formation +- **GPU optimized**: Ideal for GPU acceleration only for very high p-refinement +- **Matrix-free**: Jacobian actions computed on-the-fly +- **Preconditioning**: Currently limited to Jacobi preconditioning + +#### **Element Assembly (EA)** +```toml +[Solvers] +assembly = "EA" +``` +- **Element-level**: Only element matrices formed +- **Memory balanced**: minimal memory requirements for quadratic or fewer elements +- **GPU compatible**: Supports GPU execution +- **Flexibility**: Suitable for complex material models +- **Preconditioning**: Currently limited to Jacobi preconditioning + + +#### **Full Assembly** +```toml +[Solvers] +assembly = "FULL" +``` +- **Traditional**: Complete global matrix assembly +- **Preconditioning**: Full preconditioner options available +- **Memory intensive**: Requires moderate memory for large problems due to sparse matrix formats +- **CPU optimized**: Best initial set-up for investigating new material models + +### **Integration Schemes** + +#### **Default Integration** +```toml +[Solvers] +integ_model = "DEFAULT" +``` +- **Full integration**: Complete quadrature point evaluation +- **Standard**: Traditional finite element approach +- **Most materials**: Suitable for general material models + +#### **B-Bar Integration** +```toml +[Solvers] +integ_model = "BBAR" +``` +- **Mixed formulation**: Deviatoric fully integrated and elemental averaged volume contribution +- **Near-incompressible**: Prevents volumetric locking +- **Advanced**: Based on Hughes-Brezzi formulation (Equation 23) +- **Limitation**: Not compatible with partial assembly + +### **Linear Solver Options** + +#### **Krylov Methods** +```toml +[Solvers.Krylov] +linear_solver = "GMRES" # or "cg", "minres" +rel_tol = 1.0e-6 +abs_tol = 1.0e-10 +max_iter = 1000 +``` + +**GMRES**: General minimal residual +- **Nonsymmetric systems**: Handles general Jacobian matrices +- **Memory**: Requires restart for memory management +- **Robust**: Suitable for challenging material models + +**Conjugate Gradient (CG)**: +- **Symmetric positive definite**: Requires symmetric Jacobian +- **Memory efficient**: Minimal memory requirements +- **Fast convergence**: Optimal for appropriate problems + +**MINRES**: Minimal residual for symmetric indefinite +- **Symmetric indefinite**: Handles saddle point problems +- **Specialized**: Useful for constrained problems + +#### **Preconditioner Options** +```toml +[Solvers.Krylov] +preconditioner = "JACOBI" # Assembly-dependent options +``` + +**Assembly-Dependent Availability:** + +For **PA/EA Assembly** (limited to): +- **JACOBI**: Diagonal scaling, GPU-compatible (automatic selection) + +For **FULL Assembly** (all options available): +- **JACOBI**: Diagonal scaling +- **AMG**: Algebraic multigrid +- **ILU**: Incomplete LU factorization +- **L1GS**: ℓ¹-scaled Gauss-Seidel +- **CHEBYSHEV**: Polynomial smoother + +**Preconditioner Details:** + +**JACOBI** (Diagonal Scaling): +- **Characteristics**: Simple and fast, works everywhere but slow convergence +- **Assembly**: Works with PA, EA, and FULL +- **GPU**: Fully GPU-compatible +- **Use case**: Default for PA/EA assembly, baseline option + +**AMG** (Algebraic Multigrid): +- **Characteristics**: Fewer iterations but expensive setup, can fail on some problems +- **Implementation**: HYPRE BoomerAMG +- **Configuration**: Pre-tuned for 3D elasticity +- **Use case**: Large-scale problems with single materials + +**ILU** (Incomplete LU Factorization): +- **Characteristics**: Good middle-ground option +- **Implementation**: HYPRE Euclid +- **Use case**: Particularly useful for multi-material systems +- **Try this**: If JACOBI convergence is too slow + +**L1GS** (ℓ¹-Scaled Gauss-Seidel): +- **Characteristics**: Advanced smoother +- **Implementation**: HYPRE smoother +- **Use case**: Multi-material systems with contrasting properties +- **Try this**: When materials have very different stiffness values + +**CHEBYSHEV** (Chebyshev Polynomial): +- **Characteristics**: Polynomial smoother +- **Implementation**: HYPRE smoother +- **Use case**: Problems with multiple material scales +- **Try this**: For heterogeneous material distributions + +**Practical Selection Guidelines:** + +For **Single Material Problems**: +- Start with JACOBI (simple, predictable) +- Try AMG if convergence is slow +- Use ILU as a reliable alternative + +For **Multi-Material Systems**: +- Start with ILU (good middle-ground) +- Try L1GS for contrasting material properties +- Use CHEBYSHEV for multiple material scales +- AMG may struggle with material interfaces + +**Performance Tips**: +- PA/EA assembly automatically uses JACOBI +- If JACOBI convergence is too slow with FULL assembly, try ILU → L1GS → CHEBYSHEV +- AMG has high setup cost but fewer iterations +- Multi-material systems often benefit from experimenting with different preconditioners + +### **Nonlinear Solver Configuration** + +#### **Newton-Raphson Variants** +```toml +[Solvers.NR] +nonlinear_solver = "NR" # or "NRLS" +rel_tol = 1.0e-5 +abs_tol = 1.0e-10 +max_iter = 25 +``` + +**Standard Newton-Raphson**: +- **Full steps**: Always takes complete Newton step +- **Fast convergence**: Quadratic convergence near solution +- **Robustness**: May fail for poor initial guesses + +**Newton with Line Search**: +- **Globalization**: Backtracking line search for robustness +- **Convergence**: Improved convergence from poor starting points +- **Cost**: Additional function evaluations per iteration + +## Building and Testing + +### Build Configuration Options +```bash +# Enable GPU support +-DENABLE_CUDA=ON +-DENABLE_HIP=ON + +# Enable specific features +-DENABLE_CALIPER=ON +``` + +### Running Tests +```bash +# Run example simulations +cd test/data +mpirun -np 4 ../../build/mechanics -opt example.toml +``` + +### Example Workflows +The `test/data/` directory contains various example cases: +- **Crystal plasticity simulations** +- **Multi-material problems** +- **Complex boundary condition examples** +- **GPU acceleration tests** + +## Development Workflow + +### Code Organization Best Practices +1. **Header-only utilities**: Place in `utilities/` directory +2. **New material models**: Extend `ExaModel` base class in `models/` +3. **Post-processing features**: Add to `postprocessing/` directory +4. **Configuration options**: Update corresponding `option_*.cpp` files + +### Adding New Features + +#### New Material Model +1. Create header/source in `models/mechanics_newmodel.hpp/cpp` +2. Inherit from `ExaModel` base class +3. Implement required virtual methods +4. Add configuration parsing support +5. Update `CMakeLists.txt` + +#### New Boundary Condition Type +1. Extend `BCManager` class +2. Add parsing support in `option_boundary_conditions.cpp` +3. Update documentation and examples + +### Performance Considerations +- **GPU kernels**: Use RAJA for performance portability +- **Memory management**: Follow MFEM memory patterns +- **MPI communication**: Minimize collective operations +- **Assembly strategy**: Choose PA vs EA based on problem size + +### Debugging and Profiling +- **Caliper integration**: Built-in performance profiling +- **MFEM debugging**: Use MFEM's debugging capabilities +- **GPU debugging**: CUDA/HIP debugging tools +- **MPI debugging**: TotalView, DDT support + +## UMAT Development Resources + +### **Interface Requirements** +While UMAT interfaces are traditionally described using Fortran signatures, ExaConstit supports implementation in **Fortran, C++, or C**: + +#### **Standard UMAT Signature** (Fortran style) +```fortran +SUBROUTINE UMAT(STRESS,STATEV,DDSDDE,SSE,SPD,SCD, + 1 RPL,DDSDDT,DRPLDE,DRPLDT, + 2 STRAN,DSTRAN,TIME,DTIME,TEMP,DTEMP,PREDEF,DPRED,CMNAME, + 3 NDI,NSHR,NTENS,NSTATV,PROPS,NPROPS,COORDS,DROT,PNEWDT, + 4 CELENT,DFGRD0,DFGRD1,NOEL,NPT,LAYER,KSPT,KSTEP,KINC) +``` + +#### **C++ Implementation Example** +```cpp +extern "C" void umat_(double* stress, double* statev, double* ddsdde, + double* sse, double* spd, double* scd, + // ... additional parameters + int* ndi, int* nshr, int* ntens, int* nstatv, + double* props, int* nprops, + // ... remaining parameters + ); +``` + +### **UMAT Development Best Practices** + +#### **Memory Management** +- **ExaConstit handles**: State variable allocation and persistence +- **UMAT responsible**: Local variable management within subroutine +- **No dynamic allocation**: Avoid malloc/new within UMAT calls + +#### **Thread Safety** +- **No global variables**: UMATs must be thread-safe +- **Local computations**: All calculations using passed parameters +- **State persistence**: Only through provided state variable arrays + +#### **Error Handling** +- **Convergence issues**: Set appropriate flags for Newton-Raphson +- **Material failure**: Handle through state variables or stress reduction +- **Numerical stability**: Check for divide-by-zero and overflow conditions + +#### **Performance Considerations** +- **CPU execution only**: No current GPU acceleration for UMATs but might be possible in future updates +- **Vectorization**: Ensure compiler optimization is possible +- **Minimal function calls**: Reduce computational overhead within UMAT + +### **Development Resources** + +#### **Reference Implementations** +- **`src/umat_tests/`**: Example UMAT implementations and conversion guides +- **Template UMATs**: Starting points for custom development + +#### **External Resources** +- **NJIT UMAT Collection**: https://web.njit.edu/~sac3/Software.html +- **Academic examples**: Various constitutive models available +- **License considerations**: Verify licensing before use + +#### **Build System Integration** +```bash +# Compile UMAT to shared library (Fortran) +gfortran -shared -fPIC -o my_umat.so my_umat.f90 + +# Compile UMAT (C++) +g++ -shared -fPIC -o my_umat.so my_umat.cpp + +# Compile UMAT (C) +gcc -shared -fPIC -o my_umat.so my_umat.c +``` + +#### **Configuration Integration** +```toml +[Materials.regions.model.UMAT] +library_path = "/path/to/my_umat.so" +num_props = 8 +num_state_vars = 12 +props = [ + 210000.0, # Young's modulus + 0.3, # Poisson's ratio + # ... additional parameters +] +``` + +## Contributing Guidelines + +### Code Standards +- **C++17 compliance**: Use modern C++ features +- **Documentation**: Doxygen-style comments for all public interfaces +- **Testing**: Include test cases for new features +- **Performance**: Maintain GPU and MPI scalability +- **Name Formating**: + - Function names should be in `PascalCase` for any file but those related to IO (src/options/* and src/utilities/unified_loggers.*) which are `snake_case`. + - Class / enum names should be in `PascalCase` + - Enum values should be `UPPER_CASE` + - Class member variables going forward should be `snake_case` and preferably have a `m_` prefix. However, the `m_` prefix is **not** required if it makes things harder to understand. We're still converting variables over from previous in-consistent naming conventions so if you spot something that needs fixing please do so. + - Local / function variables going forward should be `snake_case`. Like above we are slowly in the process of converting old code over to this new format so feel free to help out if you can. + - If doing formatting changes split those into their own commits so it's easier to track changes. Additionally try to change the world all at once and do things in piece meal as it makes it easier to track down where a bug might have been introduced during renaming of things. +- **Code Formating**: We have a `.clang-format` that we make use to enfore a unified coding experience across the code base. An example of how to run the formatter is: `find src -type f \( -name "*.cpp" -o -name "*.hpp" -o -name "*.h" \) ! -path "*/TOML_Reader/*" -exec $CLANG_FORMAT -i {} +` . Note, if you see any changes in the `src/TOML_Reader` directory to revert those changes as that is a TPL that we directly include in the repo and not something we want to update unless directly bringing in the changes from its upstream repo. + +### Pull Request Process +1. Fork the repository (if non-LLNL employee) +2. Create feature branch from `exaconstit-dev` +3. Implement changes with tests +4. Ensure all existing tests pass +5. Submit pull request with detailed description + +### Licensing +- **BSD-3-Clause license**: All contributions must use this license +- **Third-party code**: Ensure compatible licensing for external dependencies + +### Getting Help +- **Primary Developer**: Robert A. Carson (carson16@llnl.gov) +- **GitHub Issues**: Report bugs and feature requests +- **Documentation**: Refer to MFEM and ExaCMech documentation for underlying libraries + +## Additional Resources + +### Related Projects +- **ExaCMech**: Crystal plasticity library (https://github.com/LLNL/ExaCMech) +- **MFEM**: Finite element library (https://mfem.org) +- **ExaCA**: Cellular automata for microstructure generation + +### Workflows and Applications +- **Optimization workflows**: Multi-objective genetic algorithm parameter optimization +- **UQ workflows**: Uncertainty quantification for additive manufacturing +- **Post-processing tools**: Python scripts for data analysis + +### Citation +If using ExaConstit in your research, please cite: +```bibtex +@misc{ exaconstit, +title = {{ExaConstit}}, +author = {Carson, Robert A. and Wopschall, Steven R. and Bramwell, Jamie A.}, +abstractNote = {The principal purpose of this code is to determine bulk constitutive properties and response of polycrystalline materials. This is a nonlinear quasi-static, implicit solid mechanics code built on the MFEM library based on an updated Lagrangian formulation (velocity based). Within this context, there is flexibility in the type of constitutive model employed, with the code allowing for various UMATs to be interfaced within the code framework or for the use of the ExaCMech library. Using crystal-mechanics-based constitutive models, the code can be used, for example, to compute homogenized response behavior over a polycrystal. }, +howpublished = {[Computer Software] \url{https://doi.org/10.11578/dc.20191024.2}}, +url = {https://github.com/LLNL/ExaConstit}, +doi = {10.11578/dc.20191024.2}, +year = {2019}, +month = {Aug}, +annote = { + https://www.osti.gov//servlets/purl/1571640 + https://www.osti.gov/biblio/1571640-exaconstit +} +} +``` + +--- + +This guide provides a foundation for new developers to understand and contribute to ExaConstit. For specific implementation details, refer to the extensive inline documentation throughout the codebase and the example configurations in `test/data/`. \ No newline at end of file diff --git a/doc/install.md b/doc/install.md new file mode 100644 index 0000000..1471ffb --- /dev/null +++ b/doc/install.md @@ -0,0 +1,813 @@ +# ExaConstit Installation Guide + +ExaConstit provides a modular build system with automated installation scripts for different platforms and backends. The build system automatically handles all dependencies including RAJA, MFEM, ExaCMech, Hypre, and METIS. + +--- + +## Table of Contents + +- [Quick Start](#quick-start) +- [Build System Architecture](#build-system-architecture) +- [First-Time Setup](#first-time-setup) +- [Advanced Configuration](#advanced-configuration) +- [Build Locations and Output](#build-locations-and-output) +- [Troubleshooting](#troubleshooting) +- [Manual Build](#manual-build-advanced-users) + +--- + +## Quick Start + +### **1. Download the Repository** +```bash +# Clone the repository +git clone https://github.com/LLNL/ExaConstit.git +cd ExaConstit + +# Create a separate build directory (recommended) +cd .. +mkdir exaconstit_builds +cd exaconstit_builds +``` + +### **2. Choose Your Platform** + +#### **Intel CPU Systems (Linux)** +```bash +../ExaConstit/scripts/install/unix_cpu_intel_install.sh +``` + +#### **MacOS Systems** +```bash +../ExaConstit/scripts/install/unix_cpu_mac_install.sh +``` + +#### **NVIDIA GPU Systems (CUDA)** +```bash +../ExaConstit/scripts/install/unix_gpu_cuda_install.sh +``` + +#### **AMD GPU Systems (HIP/ROCm)** +```bash +../ExaConstit/scripts/install/unix_gpu_hip_install.sh +``` + +**Note for MI300A users:** Set `HSA_XNACK=1` in your environment before running simulations. This is required due to unified memory requirements and current limitations in MFEM's HIP backend. + +--- + +## Build System Architecture + +The installation framework is organized into three components: +``` +scripts/install/ +├── common/ +│ ├── dependency_versions.sh # Centralized version control +│ ├── preflight_checks.sh # Validation and utilities +│ └── build_functions.sh # Shared build logic +├── configs/ +│ ├── cpu_intel_config.sh # Intel compiler configuration +│ ├── cpu_mac_config.sh # macOS configuration +│ ├── gpu_cuda_config.sh # NVIDIA CUDA configuration +│ └── gpu_hip_config.sh # AMD HIP configuration +└── unix_*_install.sh # Platform-specific entry points +``` + +- **common/**: Shared build logic used across all platforms +- **configs/**: Platform-specific compiler paths, flags, and settings +- **Entry scripts**: Simple launchers that source the appropriate config and common functions + +--- + +## First-Time Setup + +Before running an install script, you'll need to customize the configuration file for your system. + +### **Step 1: Edit the Configuration File** + +Navigate to the ExaConstit repository and open the appropriate config file in `scripts/install/configs/` with either a built in editor or something like VSCode: +```bash +cd ExaConstit + +# For Intel CPU builds +code scripts/install/configs/cpu_intel_config.sh + +# For CUDA GPU builds +code scripts/install/configs/gpu_cuda_config.sh + +# For HIP/AMD GPU builds +code scripts/install/configs/gpu_hip_config.sh + +# For macOS builds +code scripts/install/configs/cpu_mac_config.sh +``` + +### **Step 2: Update Compiler Paths and Versions** + +Each config file has a clearly marked section at the top for system-specific paths. Update these for your environment: + +#### **Intel CPU Configuration Example** +```bash +########################################### +# Compiler Versions and Base Paths +########################################### +INTEL_VERSION="2023.2.1-magic" # Update to your Intel version +COMPILER_VERSION="intel-${INTEL_VERSION}" +INTEL_BASE="/usr/tce/packages/intel/${COMPILER_VERSION}" # Update to your path + +MPI_IMPL="mvapich2" # Or openmpi, mpich, etc. +MPI_VERSION="2.3.7" # Update to your MPI version +MPI_COMPILER_VERSION="${MPI_IMPL}-${MPI_VERSION}" +MPI_BASE="/usr/tce/packages/${MPI_IMPL}/${MPI_COMPILER_VERSION}-${COMPILER_VERSION}" # update to your path + +PYTHON_VERSION="3.12.2" # Update to your Python version +PYTHON_BASE="/usr/apps/python-${PYTHON_VERSION}" # Update to your path +``` + +**How to find your paths:** +```bash +# Find your compilers +which icc # Intel C compiler +which icpc # Intel C++ compiler +which mpicc # MPI C wrapper +which mpicxx # MPI C++ wrapper +which python3 # Python executable + +# Get version information +icc --version +mpicc --version +python3 --version +``` + +#### **CUDA GPU Configuration Example** +```bash +########################################### +# Compiler Versions and Base Paths +########################################### +# Host Compiler +CLANG_VERSION="ibm-14.0.5" # Update to your Clang version +COMPILER_VERSION="clang-${CLANG_VERSION}" +CLANG_BASE="/usr/tce/packages/clang/${COMPILER_VERSION}" + +# CUDA +CUDA_VERSION="11.8.0" # Update to your CUDA version +CUDA_BASE="/usr/tce/packages/cuda/cuda-${CUDA_VERSION}" # Update to your CUDA Path + +# MPI +MPI_IMPL="spectrum-mpi" # Update to your MPI implementation / version / path +MPI_VERSION="rolling-release" +MPI_COMPILER_VERSION="${MPI_IMPL}-${MPI_VERSION}" +MPI_BASE="/usr/tce/packages/${MPI_IMPL}/${MPI_COMPILER_VERSION}-${COMPILER_VERSION}" + +# Python +PYTHON_VERSION="3.8.2" # Like stated earlier update to your version / path +PYTHON_BASE="/usr/tce/packages/python/python-${PYTHON_VERSION}" +``` + +**How to find CUDA paths:** +```bash +# Find CUDA installation +which nvcc +nvcc --version + +# CUDA is typically at /usr/local/cuda or /usr/local/cuda-11.8 +echo $CUDA_HOME # May already be set +``` + +#### **HIP/AMD GPU Configuration Example** +```bash +########################################### +# Compiler Versions and Base Paths +########################################### +# ROCm Compiler +# Update all of the below to your own relevant versions / paths / anything specific to your +# system +ROCM_VERSION="6.4.2" +ROCM_MAGIC_SUFFIX="magic" +COMPILER_VERSION="rocmcc-${ROCM_VERSION}-${ROCM_MAGIC_SUFFIX}" +ROCM_BASE="/usr/tce/packages/rocmcc/${COMPILER_VERSION}" + +# MPI - Cray MPICH +MPI_IMPL="cray-mpich" +MPI_VERSION="9.0.1" +MPI_COMPILER_VERSION="${MPI_IMPL}-${MPI_VERSION}" +MPI_BASE="/usr/tce/packages/${MPI_IMPL}/${MPI_COMPILER_VERSION}-${COMPILER_VERSION}" + +# Python +PYTHON_VERSION="3.9.12" +PYTHON_BASE="/usr/tce/packages/python/python-${PYTHON_VERSION}" +``` + +**How to find ROCm paths:** +```bash +# Find ROCm installation +which amdclang +which hipcc +hipcc --version + +# ROCm is typically at /opt/rocm or /opt/rocm-6.4.2 +echo $ROCM_PATH # May already be set +``` + +#### **macOS Configuration Example** +```bash +########################################### +# User-Configurable Paths +########################################### +# Homebrew location +HOMEBREW_PREFIX="${HOMEBREW_PREFIX:-/opt/homebrew}" # or /usr/local for Intel Macs + +# System Clang (usually fine as-is) +CLANG_BASE="/usr/bin" + +# MPI installation (REQUIRED: Update this!) +MPI_BASE="${HOME}/local/bin" # Update to your MPI location +# Common locations: +# Homebrew: /opt/homebrew/bin +# MacPorts: /opt/local/bin +# Anaconda: ${HOME}/anaconda3/bin + +# Python location (REQUIRED: Update this!) +PYTHON_BASE="${HOME}/anaconda3/bin" # Update to your Python location +# Common locations: +# Homebrew: /opt/homebrew/bin +# System: /usr/bin +``` + +**How to find paths on macOS:** +```bash +# Check architecture +uname -m # arm64 for Apple Silicon, x86_64 for Intel + +# Find MPI (install if missing: brew install open-mpi) +which mpicc +which mpicxx + +# Find Python +which python3 +python3 --version # Should be 3.8 or newer + +# Check Homebrew prefix +brew --prefix +``` + +### **Step 3: Update Module Commands (HPC Systems Only)** + +If you're on an HPC system with a module system, update the `module load` commands to match your system: +```bash +########################################### +# Module Loading +########################################### +module load intel/2023.2.1-magic # Update to match your system's modules +module load CMake/3.26.3 +module load python/3.12 +module list +``` + +**How to find available modules:** +```bash +module avail # List all available modules +module avail intel # Search for Intel modules +module avail cuda # Search for CUDA modules +module list # Show currently loaded modules +``` + +If your system doesn't use modules (like most macOS or personal Linux systems), you can comment out or remove the module commands. + +### **Step 4: Run the Install Script** + +Once you've customized the config file, run the appropriate install script from your build directory: +```bash +cd ../exaconstit_builds # Or wherever you want to build +../ExaConstit/scripts/install/unix_cpu_intel_install.sh # Or appropriate script +``` + +The script will: +1. Validate your configuration +2. Display a build summary +3. Download and build all dependencies +4. Build ExaConstit +5. Save detailed logs in each component's build directory + +**Expected build time:** 10 minutes to 45 minutes depending on system / parallelism / GPU builds or not. + +--- + +## Advanced Configuration + +### **Updating Dependency Versions** + +All dependency versions are centralized in `common/dependency_versions.sh`: +```bash +# Edit version file +code ExaConstit/scripts/install/common/dependency_versions.sh +``` +```bash +# Portability libraries +export CAMP_VER="v2025.09.2" # Update to newer version +export RAJA_VER="v2025.09.1" +export UMPIRE_VER="v2025.09.0" +export CHAI_VER="v2025.09.1" + +# Material models +export EXACMECH_REPO="https://github.com/LLNL/ExaCMech.git" +export EXACMECH_BRANCH="develop" # Change to different branch if needed + +# FEM infrastructure +export HYPRE_VER="v2.32.0" # Update to newer version +export METIS_VER="5.1.0" + +export MFEM_REPO="https://github.com/rcarson3/mfem.git" +export MFEM_BRANCH="exaconstit-dev" # Change branch if needed + +# Main application +export EXACONSTIT_REPO="https://github.com/llnl/ExaConstit.git" +export EXACONSTIT_BRANCH="exaconstit-dev" # Change branch if needed + +# Build standards +export CMAKE_CXX_STANDARD="17" +export CMAKE_BUILD_TYPE="Release" +``` + +After updating versions, **all** build scripts will automatically use the new versions. No other changes needed. + +### **Changing GPU Architecture** + +Override the default GPU architecture at runtime: +```bash +# CUDA: Target Ampere A100 instead of default Volta V100 +CMAKE_GPU_ARCHITECTURES=80 ./unix_gpu_cuda_install.sh + +# HIP: Target MI250X instead of default MI300A +CMAKE_GPU_ARCHITECTURES=gfx90a ./unix_gpu_hip_install.sh +``` + +Common GPU architectures: + +**NVIDIA CUDA:** +- `60` - Pascal (P100) +- `70` - Volta (V100) +- `75` - Turing (RTX 20xx, T4) +- `80` - Ampere (A100) +- `86` - Ampere (RTX 30xx, A40) +- `89` - Ada Lovelace (RTX 40xx, L40) +- `90` - Hopper (H100) + +**AMD HIP:** +- `gfx906` - MI50 +- `gfx908` - MI100 +- `gfx90a` - MI200 series (MI210, MI250) +- `gfx940` - MI300X (compute-only) +- `gfx942` - MI300A (APU) +- `gfx942:xnack+` - MI300A with unified memory support + +### **Build Control Options** + +Control the build behavior with environment variables: +```bash +# Clean rebuild (removes all build directories and rebuilds from scratch) +REBUILD=ON ./unix_gpu_hip_install.sh + +# Force submodule updates (syncs and updates all git submodules) +SYNC_SUBMODULES=ON ./unix_gpu_cuda_install.sh + +# Adjust parallel build jobs (default is 4) +MAKE_JOBS=16 ./unix_cpu_intel_install.sh + +# Combine multiple options +REBUILD=ON MAKE_JOBS=8 CMAKE_GPU_ARCHITECTURES=80 ./unix_gpu_cuda_install.sh +``` + +**Available environment variables:** +- `REBUILD` - `ON` to clean and rebuild, `OFF` to reuse existing builds (default: `OFF`) +- `SYNC_SUBMODULES` - `ON` to force submodule sync, `OFF` to skip (default: `OFF`) +- `MAKE_JOBS` - Number of parallel build jobs (default: `4`) +- `CMAKE_GPU_ARCHITECTURES` - GPU architecture target (default varies by platform) +- `MFEM_HIP_ARCHITECTURES` - MFEM-specific HIP arch (HIP only, default: `gfx942`) +- `OPENMP_ON` - Enable OpenMP (default: `OFF`) +- `ENABLE_TESTS_EXACONSTIT` - Build tests (default: `ON`) + +### **Using Different Repositories or Branches** + +To use a fork or different branch, edit `common/dependency_versions.sh`: +```bash +# Use your fork of MFEM +export MFEM_REPO="https://github.com/YOUR_USERNAME/mfem.git" +export MFEM_BRANCH="my-custom-feature" + +# Use development branch of ExaConstit +export EXACONSTIT_BRANCH="develop" + +# Use a different ExaCMech repository +export EXACMECH_REPO="https://github.com/YOUR_USERNAME/ExaCMech.git" +export EXACMECH_BRANCH="custom-material-models" +``` + +To use a specific commit instead of a branch: +```bash +# The build scripts will clone the repo, then manually checkout the commit: +cd mfem && git checkout abc123def456 +cd ../exaconstit_builds +# Re-run the build script with REBUILD=ON +``` + +### **Custom Compiler Flags** + +You can add custom flags by editing the config file: +```bash +# In your config file (e.g., configs/gpu_cuda_config.sh) + +# Add optimization flags +export CMAKE_CXX_FLAGS="-fPIC -std=c++17 --gcc-toolchain=${GCC_BASE} -O3 -march=native" + +# Add debugging symbols +export CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -g" + +# Add preprocessor definitions +export CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -DMY_CUSTOM_DEFINE" +``` + +--- + +## Build Locations and Output + +By default, all builds install to your build directory: +``` +your_build_directory/ +├── camp/ +│ ├── build_${BUILD_SUFFIX}/ # Build artifacts +│ └── install_${BUILD_SUFFIX}/ # Installed library +├── RAJA/ +│ ├── build_${BUILD_SUFFIX}/ +│ └── install_${BUILD_SUFFIX}/ +├── Umpire/ # GPU builds only +│ ├── build_${BUILD_SUFFIX}/ +│ └── install_${BUILD_SUFFIX}/ +├── CHAI/ # GPU builds only +│ ├── build_${BUILD_SUFFIX}/ +│ └── install_${BUILD_SUFFIX}/ +├── ExaCMech/ +│ ├── build_${BUILD_SUFFIX}/ +│ └── install_${BUILD_SUFFIX}/ +├── hypre/ +│ ├── build_${BUILD_SUFFIX}/ +│ └── src/hypre_${BUILD_SUFFIX}/ # Installed library +├── metis-5.1.0/ +│ └── install_${BUILD_SUFFIX}/ +├── mfem/ +│ ├── build_${BUILD_SUFFIX}/ +│ └── install_${BUILD_SUFFIX}/ +└── ExaConstit/ + ├── build_${BUILD_SUFFIX}/ # Build artifacts + └── install_dir/ # Final installation +``` + +Where `${BUILD_SUFFIX}` is: +- `cpu` for CPU builds +- `cuda` for CUDA/NVIDIA builds +- `hip` for HIP/AMD builds + +### **Build Logs** + +Build logs are saved in each component's build directory with standardized names: +- `my__config` - CMake configuration output +- `my__build` - Compilation output +- `my__install` - Installation output + +Example: To check why RAJA failed to build: +```bash +cd RAJA/build_cuda +less my_raja_build # View the build log +``` + +### **Disk Space Requirements** + +Typical disk space usage: +- **CPU build**: ~1 GB total +- **GPU build (CUDA/HIP)**: ~2 GB total + - Includes additional Umpire and CHAI libraries + - GPU architectures add to binary sizes + +--- + +## Troubleshooting + +### **Configuration Issues** + +#### **"Module not found" errors** +``` +ERROR: Unable to locate a modulefile for 'intel/2023.2.1-magic' +``` + +**Solution:** +- Check available modules: `module avail intel` +- Update the module version in your config file +- If not using a module system, comment out the `module load` commands + +#### **"Compiler not found" errors** +``` +CMake Error: CMAKE_C_COMPILER not found +``` + +**Solution:** +- Verify the compiler path: `ls -la /path/to/compiler` +- Check that the executable exists: `which icc` or `which clang` +- Update the `*_BASE` variable in your config file +- Ensure the compiler is in your `PATH` + +#### **"Python not found" errors** +``` +Could NOT find Python3 (missing: Python3_EXECUTABLE) +``` + +**Solution:** +- Verify Python installation: `which python3` or `which python` +- Check Python version (must be 3.8+): `python3 --version` +- Update `PYTHON_BASE` in your config file +- If using Anaconda: `conda activate your_env` before building + +#### **MPI errors** +``` +Could not find MPI compiler wrappers +``` + +**Solution:** +- Verify MPI installation: `which mpicc && which mpicxx` +- Update `MPI_BASE` in your config file +- Test MPI: `mpicc --version` +- On macOS, install with: `brew install open-mpi` + +### **Build Failures** + +#### **Out of memory during compilation** +``` +c++: fatal error: Killed signal terminated program cc1plus +``` + +**Solution:** +- Reduce parallel jobs: `MAKE_JOBS=2 ./unix_*_install.sh` +- Close other applications +- Add swap space if building on a resource-constrained system + +#### **Disk space errors** +``` +No space left on device +``` + +**Solution:** +- Check available space: `df -h .` +- Clean previous builds: `REBUILD=ON ./unix_*_install.sh` +- Build in a location with more space +- Remove old build directories + +#### **Dependency build fails partway through** + +**Solution:** +1. Check the specific log file in the failing component's build directory +2. Common issues: + - Missing system libraries (install via package manager) + - Version incompatibilities (check `dependency_versions.sh`) + - Network issues during git clone (retry with `SYNC_SUBMODULES=ON`) +3. Try a clean rebuild: `REBUILD=ON ./unix_*_install.sh` +4. Build dependencies individually to isolate the issue + +#### **Git submodule errors** +``` +fatal: unable to access 'https://github.com/...': Failed to connect +``` + +**Solution:** +- Check network connectivity +- If behind a firewall, configure git proxy +- Clone repositories manually if needed +- Force submodule sync: `SYNC_SUBMODULES=ON ./unix_*_install.sh` + +### **Platform-Specific Issues** + +#### **macOS: "xcrun: error: invalid active developer path"** + +**Solution:** +```bash +xcode-select --install +``` + +#### **macOS: Missing dependencies** + +**Solution:** +```bash +# Install Homebrew if not already installed +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + +# Install required tools +brew install cmake +brew install open-mpi +brew install python3 +``` + +#### **macOS: Architecture mismatch (Apple Silicon vs Intel)** + +**Solution:** +- Ensure all dependencies are built for the same architecture +- For Apple Silicon, use ARM-native tools: `arch -arm64 brew install ...` +- For Intel compatibility on Apple Silicon: `arch -x86_64 brew install ...` + +#### **HPC: Module conflicts** + +**Solution:** +```bash +# Clear all modules and reload +# Some HPC will automatically switch things for you +# So only do this if your HPC center advises you to purge modules +module purge +module load intel/2023.2.1-magic +module load cmake/3.26.3 +# ... load other required modules +``` + +#### **HPC: Quota exceeded** + +**Solution:** +- Build in your scratch space instead of home directory +- Clean old builds regularly +- Check quota: `quota -s` or `lfs quota -h $HOME` + +### **GPU-Specific Issues** + +#### **CUDA: "nvcc not found"** + +**Solution:** +- Verify CUDA installation: `which nvcc` +- Update `CUDA_BASE` in `gpu_cuda_config.sh` +- Load CUDA module if on HPC: `module load cuda` +- Set `CUDA_HOME` environment variable + +#### **CUDA: Architecture mismatch** +``` +nvcc fatal: Unsupported gpu architecture 'compute_XX' +``` + +**Solution:** +- Check your GPU architecture: `nvidia-smi` +- Update `CMAKE_GPU_ARCHITECTURES` to match your hardware +- Common fix: `CMAKE_GPU_ARCHITECTURES=70 ./unix_gpu_cuda_install.sh` + +#### **HIP: "amdclang not found"** + +**Solution:** +- Verify ROCm installation: `which amdclang` +- Update `ROCM_BASE` in `gpu_hip_config.sh` +- Load ROCm modules: `module load rocm` +- Set `ROCM_PATH` environment variable + +#### **HIP: MI300A memory issues** + +**Solution:** +- Set unified memory flag: `export HSA_XNACK=1` +- Verify architecture: `CMAKE_GPU_ARCHITECTURES=gfx942:xnack+` +- Check system: `rocminfo | grep xnack` + +### **Runtime Issues** + +#### **Segmentation fault on startup** + +**Possible causes:** +1. Library path issues - Ensure `LD_LIBRARY_PATH` includes all dependency lib directories +2. ABI incompatibility - Rebuild with consistent compiler versions +3. Missing runtime dependencies - Check with `ldd` on the executable + +#### **MPI initialization failures** + +**Solution:** +```bash +# Test MPI installation +mpirun -np 2 hostname + +# Verify MPI library paths +ldd ExaConstit/build_*/mechanics_driver | grep mpi + +# Check module environment +module list +``` + +### **Getting Help** + +If you encounter issues not covered here: + +1. **Check the build logs** + - Navigate to the failing component's build directory + - Review `my__config`, `my__build`, or `my__install` + - Look for specific error messages + +2. **Verify your configuration** + - Confirm all paths in your config file are correct + - Test each tool independently: `which compiler`, `mpicc --version`, etc. + +3. **Search existing issues** + - Check the [GitHub Issues](https://github.com/LLNL/ExaConstit/issues) page + - Search for similar error messages + +4. **Open a new issue** + - Go to [GitHub Issues](https://github.com/LLNL/ExaConstit/issues/new) + - Include: + - Your platform and OS version (`uname -a`, `lsb_release -a`, etc.) + - The config file you're using + - Relevant sections from error logs + - Steps you've already tried + - Output of `module list` (if applicable) + +--- + +## Manual Build (Advanced Users) + +If you prefer to build manually or need more control over the build process: + +### **Prerequisites** + +You'll need to manually build all dependencies first: +1. **CAMP** (v2025.09.2) +2. **RAJA** (v2025.09.1) +3. **Umpire** (v2025.09.0) - GPU builds only +4. **CHAI** (v2025.09.1) - GPU builds only +5. **ExaCMech** (develop branch) +6. **Hypre** (v2.32.0) +7. **METIS** (5.1.0) +8. **MFEM** (exaconstit-smart-ptrs branch) + +See `scripts/install/common/build_functions.sh` for the exact build commands and CMake options. + +### **Building ExaConstit** + +Once all dependencies are built: +```bash +# 1. Clone ExaConstit +git clone https://github.com/LLNL/ExaConstit.git +cd ExaConstit +git checkout the_great_refactoring +git submodule update --init --recursive + +# 2. Create build directory +mkdir build && cd build + +# 3. Configure (CPU example) +cmake .. \ + -DCMAKE_CXX_COMPILER=mpicxx \ + -DCMAKE_C_COMPILER=mpicc \ + -DENABLE_TESTS=ON \ + -DENABLE_OPENMP=OFF \ + -DENABLE_FORTRAN=OFF \ + -DPYTHON_EXECUTABLE=/usr/bin/python3 \ + -DMFEM_DIR=${MFEM_INSTALL_DIR}/lib/cmake/mfem/ \ + -DECMECH_DIR=${EXACMECH_INSTALL_DIR}/ \ + -DSNLS_DIR=${EXACMECH_INSTALL_DIR}/ \ + -DRAJA_DIR=${RAJA_INSTALL_DIR}/lib/cmake/raja/ \ + -Dcamp_DIR=${CAMP_INSTALL_DIR}/lib/cmake/camp/ \ + -DCMAKE_BUILD_TYPE=Release + +# 4. Build +make -j $(nproc) + +# 5. Test +ctest +``` + +### **GPU Build Options** + +For CUDA builds, add: +```bash +cmake .. \ + ... (all the above options) ... \ + -DCMAKE_CXX_COMPILER=${CUDA_ROOT}/bin/nvcc \ + -DCMAKE_CUDA_COMPILER=${CUDA_ROOT}/bin/nvcc \ + -DCMAKE_CUDA_HOST_COMPILER=${HOST_CXX_COMPILER} \ + -DCMAKE_CUDA_ARCHITECTURES=80 \ + -DENABLE_CUDA=ON \ + -DFMT_DIR=${UMPIRE_INSTALL_DIR}/lib64/cmake/fmt \ + -DUMPIRE_DIR=${UMPIRE_INSTALL_DIR}/lib64/cmake/umpire \ + -DCHAI_DIR=${CHAI_INSTALL_DIR}/lib/cmake/chai +``` + +For HIP builds, add: +```bash +cmake .. \ + ... (all the above options) ... \ + -DCMAKE_CXX_COMPILER=${ROCM_ROOT}/bin/amdclang++ \ + -DCMAKE_HIP_COMPILER=${ROCM_ROOT}/bin/amdclang++ \ + -DCMAKE_HIP_ARCHITECTURES=gfx942 \ + -DENABLE_HIP=ON \ + -DFMT_DIR=${UMPIRE_INSTALL_DIR}/lib64/cmake/fmt \ + -DUMPIRE_DIR=${UMPIRE_INSTALL_DIR}/lib64/cmake/umpire \ + -DCHAI_DIR=${CHAI_INSTALL_DIR}/lib/cmake/chai +``` + +--- + +## Next Steps + +After successful installation: + +- **Run the test suite**: `cd ExaConstit/build_*/` then `ctest` or `make test` +- **Try example problems**: See `examples/` directory +- **Read the documentation**: Check the `docs/` folder for detailed usage guides +- **Join the community**: Open issues or discussions on GitHub + +For questions about using ExaConstit after installation, see the main [README](../README.md) and documentation in the `docs/` folder. \ No newline at end of file diff --git a/mechanics.bash b/mechanics.bash deleted file mode 100755 index 7d174a6..0000000 --- a/mechanics.bash +++ /dev/null @@ -1,3 +0,0 @@ -#The options.toml file contains all of the options that drive the simulations - -srun -ppdebug -n1 ./mechanics_driver -opt options.toml diff --git a/scripts/install/common/build_functions.sh b/scripts/install/common/build_functions.sh new file mode 100644 index 0000000..c7e8001 --- /dev/null +++ b/scripts/install/common/build_functions.sh @@ -0,0 +1,562 @@ +#!/usr/bin/env bash +# Common build functions for all ExaConstit dependencies + +# Logging wrapper +run_with_log() { + local log="$1"; shift + "$@" |& tee "$log" +} + +# Clone repository only if missing, initialize submodules on first clone +clone_if_missing() { + local repo="$1" branch="$2" dest="$3" + if [ ! -d "$dest/.git" ]; then + echo "Cloning ${dest}..." + git clone --branch "$branch" "$repo" "$dest" + cd "$dest" + if [ -f .gitmodules ]; then + git submodule update --init --recursive + fi + cd "$BASE_DIR" + else + echo "${dest} already exists, skipping clone." + fi +} + +# Optional: force submodule sync when explicitly requested +sync_submodules() { + local dest="$1" + if [ "${SYNC_SUBMODULES}" = "ON" ] && [ -f "$dest/.gitmodules" ]; then + echo "Syncing submodules in ${dest}..." + cd "$dest" + git submodule sync --recursive + git submodule update --init --recursive + cd "$BASE_DIR" + fi +} + +# Respect REBUILD flag when preparing build directories +prepare_build_dir() { + local dir="$1" + if [ "${REBUILD}" = "ON" ]; then + mkdir -p "$dir" + rm -rf "$dir"/* + echo "Cleaned build directory: ${dir}" + else + if [ ! -d "$dir" ]; then + mkdir -p "$dir" + echo "Created build directory: ${dir}" + else + echo "Reusing existing build directory: ${dir}" + fi + fi +} + +########################################### +# CAMP +########################################### +build_camp() { + echo "==========================================" + echo "Building CAMP" + echo "==========================================" + + clone_if_missing "https://github.com/LLNL/camp.git" "${CAMP_VER}" "${BASE_DIR}/camp" + sync_submodules "${BASE_DIR}/camp" + + prepare_build_dir "${BASE_DIR}/camp/build_${BUILD_SUFFIX}" + cd "${BASE_DIR}/camp/build_${BUILD_SUFFIX}" + + local CMAKE_ARGS=( + -DCMAKE_INSTALL_PREFIX=../install_${BUILD_SUFFIX}/ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + -DENABLE_TESTS=OFF + -DENABLE_OPENMP="${OPENMP_ON}" + -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" + -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" + -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" + -DCMAKE_CXX_STANDARD="${CMAKE_CXX_STANDARD}" + -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS}" + ) + + if [ "${BUILD_TYPE}" != "cpu" ]; then + CMAKE_ARGS+=( + -DCMAKE_${GPU_BACKEND}_ARCHITECTURES="${CMAKE_GPU_ARCHITECTURES}" + -DCMAKE_${GPU_BACKEND}_COMPILER="${CMAKE_GPU_COMPILER}" + -DCMAKE_${GPU_BACKEND}_FLAGS="${CMAKE_GPU_FLAGS}" + -DENABLE_${GPU_BACKEND}=ON + ) + fi + + run_with_log my_camp_config cmake ../ "${CMAKE_ARGS[@]}" + run_with_log my_camp_build make -j "${MAKE_JOBS}" + run_with_log my_camp_install make install + + CAMP_ROOT="${BASE_DIR}/camp/install_${BUILD_SUFFIX}" + export CAMP_ROOT + echo "CAMP installed to: ${CAMP_ROOT}" + cd "${BASE_DIR}" +} + +########################################### +# RAJA +########################################### +build_raja() { + echo "==========================================" + echo "Building RAJA" + echo "==========================================" + + clone_if_missing "https://github.com/LLNL/RAJA.git" "${RAJA_VER}" "${BASE_DIR}/RAJA" + sync_submodules "${BASE_DIR}/RAJA" + + prepare_build_dir "${BASE_DIR}/RAJA/build_${BUILD_SUFFIX}" + cd "${BASE_DIR}/RAJA/build_${BUILD_SUFFIX}" + + local CMAKE_ARGS=( + -DCMAKE_INSTALL_PREFIX=../install_${BUILD_SUFFIX}/ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + -DENABLE_TESTS=OFF + -DRAJA_ENABLE_TESTS=OFF + -DRAJA_ENABLE_EXAMPLES=OFF + -DRAJA_ENABLE_BENCHMARKS=OFF + -DRAJA_ENABLE_REPRODUCERS=OFF + -DRAJA_ENABLE_EXERCISES=OFF + -DRAJA_ENABLE_VECTORIZATION=OFF + -DRAJA_ENABLE_DOCUMENTATION=OFF + -DRAJA_USE_DOUBLE=ON + -DRAJA_TIMER=chrono + -DENABLE_OPENMP="${OPENMP_ON}" + -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" + -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" + -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" + -DCMAKE_CXX_STANDARD="${CMAKE_CXX_STANDARD}" + -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS}" + -Dcamp_DIR="${CAMP_ROOT}/lib/cmake/camp" + ) + + if [ "${BUILD_TYPE}" != "cpu" ]; then + CMAKE_ARGS+=( + -DCMAKE_${GPU_BACKEND}_ARCHITECTURES="${CMAKE_GPU_ARCHITECTURES}" + -DCMAKE_${GPU_BACKEND}_COMPILER="${CMAKE_GPU_COMPILER}" + -DCMAKE_${GPU_BACKEND}_FLAGS="${CMAKE_GPU_FLAGS}" + -DENABLE_${GPU_BACKEND}=ON + ) + if [ "${GPU_BACKEND}" = "CUDA" ]; then + CMAKE_ARGS+=( + -DRAJA_USE_BARE_PTR=ON + ) + fi + fi + + run_with_log my_raja_config cmake ../ "${CMAKE_ARGS[@]}" + run_with_log my_raja_build make -j "${MAKE_JOBS}" + run_with_log my_raja_install make install + + RAJA_ROOT="${BASE_DIR}/RAJA/install_${BUILD_SUFFIX}" + export RAJA_ROOT + echo "RAJA installed to: ${RAJA_ROOT}" + cd "${BASE_DIR}" +} + +########################################### +# Umpire (GPU only) +########################################### +build_umpire() { + if [ "${BUILD_TYPE}" = "cpu" ]; then + echo "Skipping Umpire (not needed for CPU builds)" + return 0 + fi + + echo "==========================================" + echo "Building Umpire" + echo "==========================================" + + clone_if_missing "https://github.com/LLNL/Umpire.git" "${UMPIRE_VER}" "${BASE_DIR}/Umpire" + sync_submodules "${BASE_DIR}/Umpire" + + prepare_build_dir "${BASE_DIR}/Umpire/build_${BUILD_SUFFIX}" + cd "${BASE_DIR}/Umpire/build_${BUILD_SUFFIX}" + + local CMAKE_ARGS=( + -DCMAKE_INSTALL_PREFIX=../install_${BUILD_SUFFIX}/ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + -DENABLE_TESTS=OFF + -DENABLE_OPENMP="${OPENMP_ON}" + -DENABLE_MPI=OFF + -DUMPIRE_ENABLE_C=OFF + -DENABLE_FORTRAN=OFF + -DENABLE_GMOCK=OFF + -DUMPIRE_ENABLE_IPC_SHARED_MEMORY=OFF + -DUMPIRE_ENABLE_TOOLS=ON + -DUMPIRE_ENABLE_BACKTRACE=ON + -DUMPIRE_ENABLE_BACKTRACE_SYMBOLS=ON + -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" + -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" + -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" + -DCMAKE_CXX_STANDARD="${CMAKE_CXX_STANDARD}" + -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS}" + -DCMAKE_${GPU_BACKEND}_ARCHITECTURES="${CMAKE_GPU_ARCHITECTURES}" + -DCMAKE_${GPU_BACKEND}_COMPILER="${CMAKE_GPU_COMPILER}" + -DCMAKE_${GPU_BACKEND}_FLAGS="${CMAKE_GPU_FLAGS}" + -DENABLE_${GPU_BACKEND}=ON + -Dcamp_DIR="${CAMP_ROOT}/lib/cmake/camp" + ) + + run_with_log my_umpire_config cmake ../ "${CMAKE_ARGS[@]}" + run_with_log my_umpire_build make -j "${MAKE_JOBS}" + run_with_log my_umpire_install make install + + UMPIRE_ROOT="${BASE_DIR}/Umpire/install_${BUILD_SUFFIX}" + export UMPIRE_ROOT + + # Find fmt directory + FMT_DIR_CMAKE=$(find "${UMPIRE_ROOT}" -name 'fmtConfig.cmake' -print -quit || true) + if [ -n "${FMT_DIR_CMAKE}" ]; then + FMT_DIR=$(dirname "${FMT_DIR_CMAKE}") + else + FMT_DIR="${UMPIRE_ROOT}" + fi + export FMT_DIR + + echo "Umpire installed to: ${UMPIRE_ROOT}" + echo "fmt found at: ${FMT_DIR}" + cd "${BASE_DIR}" +} + +########################################### +# CHAI (GPU only) +########################################### +build_chai() { + if [ "${BUILD_TYPE}" = "cpu" ]; then + echo "Skipping CHAI (not needed for CPU builds)" + return 0 + fi + + echo "==========================================" + echo "Building CHAI" + echo "==========================================" + + clone_if_missing "https://github.com/LLNL/CHAI.git" "${CHAI_VER}" "${BASE_DIR}/CHAI" + sync_submodules "${BASE_DIR}/CHAI" + + prepare_build_dir "${BASE_DIR}/CHAI/build_${BUILD_SUFFIX}" + cd "${BASE_DIR}/CHAI/build_${BUILD_SUFFIX}" + + local CMAKE_ARGS=( + -DCMAKE_INSTALL_PREFIX=../install_${BUILD_SUFFIX}/ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + -DENABLE_TESTS=OFF + -DENABLE_EXAMPLES=OFF + -DENABLE_DOCS=OFF + -DENABLE_GMOCK=OFF + -DENABLE_OPENMP="${OPENMP_ON}" + -DENABLE_MPI=OFF + -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" + -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" + -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" + -DCMAKE_CXX_STANDARD="${CMAKE_CXX_STANDARD}" + -DCMAKE_C_FLAGS="${CMAKE_C_FLAGS}" + -DCMAKE_${GPU_BACKEND}_ARCHITECTURES="${CMAKE_GPU_ARCHITECTURES}" + -DCMAKE_${GPU_BACKEND}_COMPILER="${CMAKE_GPU_COMPILER}" + -DCMAKE_${GPU_BACKEND}_FLAGS="${CMAKE_GPU_FLAGS}" + -DENABLE_${GPU_BACKEND}=ON + -DCHAI_ENABLE_RAJA_PLUGIN=ON + -DCHAI_ENABLE_RAJA_NESTED_TEST=OFF + -DCHAI_THIN_GPU_ALLOCATE="${CHAI_THIN_GPU_ALLOCATE}" + -DCHAI_ENABLE_PINNED="${CHAI_ENABLE_PINNED}" + -DCHAI_DISABLE_RM="${CHAI_DISABLE_RM}" + -DCHAI_ENABLE_PICK="${CHAI_ENABLE_PICK}" + -DCHAI_DEBUG="${CHAI_DEBUG}" + -DCHAI_ENABLE_GPU_SIMULATION_MODE="${CHAI_ENABLE_GPU_SIMULATION_MODE}" + -DCHAI_ENABLE_UM="${CHAI_ENABLE_UM}" + -DCHAI_ENABLE_MANAGED_PTR="${CHAI_ENABLE_MANAGED_PTR}" + -DCHAI_ENABLE_MANAGED_PTR_ON_GPU="${CHAI_ENABLE_MANAGED_PTR_ON_GPU}" + -Dfmt_DIR="${FMT_DIR}" + -Dumpire_DIR="${UMPIRE_ROOT}" + -DRAJA_DIR="${RAJA_ROOT}" + -Dcamp_DIR="${CAMP_ROOT}" + ) + + run_with_log my_chai_config cmake ../ "${CMAKE_ARGS[@]}" + run_with_log my_chai_build make -j "${MAKE_JOBS}" + run_with_log my_chai_install make install + + CHAI_ROOT="${BASE_DIR}/CHAI/install_${BUILD_SUFFIX}" + export CHAI_ROOT + echo "CHAI installed to: ${CHAI_ROOT}" + cd "${BASE_DIR}" +} + +########################################### +# ExaCMech +########################################### +build_exacmech() { + echo "==========================================" + echo "Building ExaCMech" + echo "==========================================" + + clone_if_missing "${EXACMECH_REPO}" "${EXACMECH_BRANCH}" "${BASE_DIR}/ExaCMech" + sync_submodules "${BASE_DIR}/ExaCMech" + + prepare_build_dir "${BASE_DIR}/ExaCMech/build_${BUILD_SUFFIX}" + cd "${BASE_DIR}/ExaCMech/build_${BUILD_SUFFIX}" + + local CMAKE_ARGS=( + -DCMAKE_INSTALL_PREFIX=../install_${BUILD_SUFFIX}/ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + -DENABLE_TESTS=OFF + -DENABLE_MINIAPPS=OFF + -DENABLE_OPENMP="${OPENMP_ON}" + -DBUILD_SHARED_LIBS=OFF + -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" + -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" + -DCMAKE_CXX_STANDARD="${CMAKE_CXX_STANDARD}" + -DRAJA_DIR="${RAJA_ROOT}/lib/cmake/raja" + -DCAMP_DIR="${CAMP_ROOT}/lib/cmake/camp" + ) + + if [ "${BUILD_TYPE}" != "cpu" ]; then + CMAKE_ARGS+=( + -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" + -DCMAKE_${GPU_BACKEND}_ARCHITECTURES="${CMAKE_GPU_ARCHITECTURES}" + -DCMAKE_${GPU_BACKEND}_COMPILER="${CMAKE_GPU_COMPILER}" + -DCMAKE_${GPU_BACKEND}_FLAGS="${CMAKE_GPU_FLAGS}" + -DENABLE_${GPU_BACKEND}=ON + -DFMT_DIR="${FMT_DIR}" + -DUMPIRE_DIR="${UMPIRE_ROOT}/lib64/cmake/umpire" + -DCHAI_DIR="${CHAI_ROOT}/lib/cmake/chai" + ) + fi + + run_with_log my_ecmech_config cmake ../ "${CMAKE_ARGS[@]}" + run_with_log my_ecmech_build make -j "${MAKE_JOBS}" + run_with_log my_ecmech_install make install + + ECMECH_ROOT="${BASE_DIR}/ExaCMech/install_${BUILD_SUFFIX}" + export ECMECH_ROOT + echo "ExaCMech installed to: ${ECMECH_ROOT}" + cd "${BASE_DIR}" +} + +########################################### +# Hypre +########################################### +build_hypre() { + echo "==========================================" + echo "Building Hypre" + echo "==========================================" + + if [ ! -d "${BASE_DIR}/hypre" ]; then + git clone https://github.com/hypre-space/hypre.git --branch "${HYPRE_VER}" --single-branch "${BASE_DIR}/hypre" + fi + + prepare_build_dir "${BASE_DIR}/hypre/build_${BUILD_SUFFIX}" + cd "${BASE_DIR}/hypre/build_${BUILD_SUFFIX}" + + run_with_log my_hypre_config cmake ../src \ + -DCMAKE_INSTALL_PREFIX=../src/hypre_${BUILD_SUFFIX}/ \ + -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" \ + -DMPI_CXX_COMPILER="${MPI_CXX_COMPILER}" \ + -DMPI_C_COMPILER="${MPI_C_COMPILER}" \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + + run_with_log my_hypre_build make -j "${MAKE_JOBS}" + run_with_log my_hypre_install make install + + HYPRE_ROOT="${BASE_DIR}/hypre/src/hypre_${BUILD_SUFFIX}" + export HYPRE_ROOT + echo "Hypre installed to: ${HYPRE_ROOT}" + cd "${BASE_DIR}" +} + +########################################### +# METIS +########################################### +build_metis() { + echo "==========================================" + echo "Building METIS" + echo "==========================================" + + if [ ! -d "${BASE_DIR}/metis-${METIS_VER}" ]; then + curl -o metis-${METIS_VER}.tar.gz "${METIS_URL}" + tar -xzf metis-${METIS_VER}.tar.gz + rm metis-${METIS_VER}.tar.gz + fi + + cd "${BASE_DIR}/metis-${METIS_VER}" + + # METIS doesn't have a proper incremental build, so always clean + make distclean 2>/dev/null || true + + prepare_build_dir "${BASE_DIR}/metis-${METIS_VER}/install_${BUILD_SUFFIX}" + + run_with_log my_metis_config make config \ + prefix="${BASE_DIR}/metis-${METIS_VER}/install_${BUILD_SUFFIX}" \ + CC="${CMAKE_C_COMPILER}" \ + CXX="${CMAKE_CXX_COMPILER}" + + run_with_log my_metis_build make -j "${MAKE_JOBS}" + run_with_log my_metis_install make install + + METIS_ROOT="${BASE_DIR}/metis-${METIS_VER}/install_${BUILD_SUFFIX}" + export METIS_ROOT + echo "METIS installed to: ${METIS_ROOT}" + cd "${BASE_DIR}" +} + +########################################### +# MFEM +########################################### +build_mfem() { + echo "==========================================" + echo "Building MFEM" + echo "==========================================" + + clone_if_missing "${MFEM_REPO}" "${MFEM_BRANCH}" "${BASE_DIR}/mfem" + # Don't sync submodules for MFEM to preserve local changes + + prepare_build_dir "${BASE_DIR}/mfem/build_${BUILD_SUFFIX}" + cd "${BASE_DIR}/mfem/build_${BUILD_SUFFIX}" + + local CMAKE_ARGS=( + -DMFEM_USE_MPI=YES + -DMFEM_USE_SIMD=NO + -DMETIS_DIR="${METIS_ROOT}" + -DHYPRE_DIR="${HYPRE_ROOT}" + -DMFEM_USE_RAJA=YES + -DRAJA_DIR="${RAJA_ROOT}" + -DRAJA_REQUIRED_PACKAGES="camp" + -DMFEM_USE_CAMP=ON + -Dcamp_DIR="${CAMP_ROOT}/lib/cmake/camp" + -DMFEM_USE_OPENMP="${OPENMP_ON}" + -DMFEM_USE_ZLIB=YES + -DCMAKE_INSTALL_PREFIX=../install_${BUILD_SUFFIX}/ + -DCMAKE_CXX_STANDARD="${CMAKE_CXX_STANDARD}" + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + ) + + if [ "${BUILD_TYPE}" = "cpu" ]; then + CMAKE_ARGS+=( + -DCMAKE_CXX_COMPILER="${MPI_CXX_COMPILER}" + ) + else + CMAKE_ARGS+=( + -DCMAKE_CXX_COMPILER="${CMAKE_GPU_COMPILER}" + -DMPI_CXX_COMPILER="${MPI_CXX_COMPILER}" + -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" + -DMFEM_USE_${GPU_BACKEND}=ON + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + ) + + if [ "${GPU_BACKEND}" = "CUDA" ]; then + CMAKE_ARGS+=( + -DCMAKE_CUDA_COMPILER="${CMAKE_GPU_COMPILER}" + -DCMAKE_CUDA_HOST_COMPILER="${CMAKE_CXX_COMPILER}" + -DCMAKE_CUDA_ARCHITECTURES="${CMAKE_GPU_ARCHITECTURES}" + -DCMAKE_CUDA_FLAGS="${CMAKE_GPU_FLAGS}" + -DENABLE_CUDA=ON + ) + elif [ "${GPU_BACKEND}" = "HIP" ]; then + CMAKE_ARGS+=( + -DHIP_ARCH="${MFEM_HIP_ARCHITECTURES}" + -DCMAKE_HIP_ARCHITECTURES="${MFEM_HIP_ARCHITECTURES}" + ) + fi + fi + + run_with_log my_mfem_config cmake ../ "${CMAKE_ARGS[@]}" + run_with_log my_mfem_build make -j "${MAKE_JOBS}" + run_with_log my_mfem_install make install + + MFEM_ROOT="${BASE_DIR}/mfem/install_${BUILD_SUFFIX}" + export MFEM_ROOT + echo "MFEM installed to: ${MFEM_ROOT}" + cd "${BASE_DIR}" +} + +########################################### +# ExaConstit +########################################### +build_exaconstit() { + echo "==========================================" + echo "Building ExaConstit" + echo "==========================================" + + clone_if_missing "${EXACONSTIT_REPO}" "${EXACONSTIT_BRANCH}" "${BASE_DIR}/ExaConstit" + sync_submodules "${BASE_DIR}/ExaConstit" + + prepare_build_dir "${BASE_DIR}/ExaConstit/build_${BUILD_SUFFIX}" + cd "${BASE_DIR}/ExaConstit/build_${BUILD_SUFFIX}" + + local CMAKE_ARGS=( + -DCMAKE_CXX_STANDARD="${CMAKE_CXX_STANDARD}" + -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" + -DMPI_CXX_COMPILER="${MPI_CXX_COMPILER}" + -DPYTHON_EXECUTABLE="${PYTHON_EXECUTABLE}" + -DENABLE_TESTS="${ENABLE_TESTS_EXACONSTIT}" + -DENABLE_OPENMP="${OPENMP_ON}" + -DENABLE_FORTRAN=OFF + -DENABLE_SNLS_V03=ON + -DCMAKE_INSTALL_PREFIX=../install_dir/ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + -DMFEM_DIR="${MFEM_ROOT}/lib/cmake/mfem" + -DECMECH_DIR="${ECMECH_ROOT}" + -DSNLS_DIR="${ECMECH_ROOT}" + -DRAJA_DIR="${RAJA_ROOT}/lib/cmake/raja" + -DCAMP_DIR="${CAMP_ROOT}/lib/cmake/camp" + ) + + if [ "${BUILD_TYPE}" = "cpu" ]; then + CMAKE_ARGS+=( + -DCMAKE_CXX_COMPILER="${MPI_CXX_COMPILER}" + ) + else + CMAKE_ARGS+=( + -DCMAKE_CXX_COMPILER="${CMAKE_GPU_COMPILER}" + -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" + -DCMAKE_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}" + -DCMAKE_${GPU_BACKEND}_COMPILER="${CMAKE_GPU_COMPILER}" + -DCMAKE_${GPU_BACKEND}_ARCHITECTURES="${CMAKE_GPU_ARCHITECTURES}" + -DENABLE_${GPU_BACKEND}=ON + -DFMT_DIR="${FMT_DIR}" + -DUMPIRE_DIR="${UMPIRE_ROOT}/lib64/cmake/umpire" + -DCHAI_DIR="${CHAI_ROOT}/lib/cmake/chai" + ) + + if [ "${GPU_BACKEND}" = "CUDA" ]; then + CMAKE_ARGS+=( + -DCMAKE_CUDA_FLAGS="${CMAKE_GPU_FLAGS}" + -DBLT_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}" + ) + elif [ "${GPU_BACKEND}" = "HIP" ]; then + CMAKE_ARGS+=( + -DCMAKE_HIP_FLAGS="${CMAKE_GPU_FLAGS}" + ) + fi + fi + + run_with_log my_exconstit_config cmake ../ "${CMAKE_ARGS[@]}" + run_with_log my_exconstit_build make -j "${MAKE_JOBS}" + + EXACONSTIT_ROOT="${BASE_DIR}/ExaConstit/install_dir" + export EXACONSTIT_ROOT + echo "==========================================" + echo "ExaConstit build complete!" + echo "Install prefix: ${EXACONSTIT_ROOT}" + echo "==========================================" + cd "${BASE_DIR}" +} + +########################################### +# Main orchestration function +########################################### +build_all_dependencies() { + build_camp + build_raja + build_umpire + build_chai + build_exacmech + build_hypre + build_metis + build_mfem + build_exaconstit +} \ No newline at end of file diff --git a/scripts/install/common/dependency_versions.sh b/scripts/install/common/dependency_versions.sh new file mode 100644 index 0000000..7ddb73d --- /dev/null +++ b/scripts/install/common/dependency_versions.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Central version control for all dependencies + +# Portability libraries +export CAMP_VER="v2025.09.2" +export RAJA_VER="v2025.09.1" +export UMPIRE_VER="v2025.09.0" +export CHAI_VER="v2025.09.1" + +# Material models +export EXACMECH_REPO="https://github.com/LLNL/ExaCMech.git" +export EXACMECH_BRANCH="develop" + +# FEM infrastructure +export HYPRE_VER="v2.32.0" +export METIS_VER="5.1.0" +export METIS_URL="https://mfem.github.io/tpls/metis-${METIS_VER}.tar.gz" + +export MFEM_REPO="https://github.com/rcarson3/mfem.git" +export MFEM_BRANCH="exaconstit-dev" + +# Main application +export EXACONSTIT_REPO="https://github.com/llnl/ExaConstit.git" +export EXACONSTIT_BRANCH="exaconstit-dev" + +# Build standards +export CMAKE_CXX_STANDARD="17" +export CMAKE_BUILD_TYPE="Release" \ No newline at end of file diff --git a/scripts/install/common/preflight_checks.sh b/scripts/install/common/preflight_checks.sh new file mode 100644 index 0000000..b6867ae --- /dev/null +++ b/scripts/install/common/preflight_checks.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +# Preflight checks and utility functions + +# Resolve BASE_DIR portably across systems +resolve_base_dir() { + if command -v readlink >/dev/null 2>&1 && readlink -f "$0" >/dev/null 2>&1; then + SCRIPT=$(readlink -f "$0") + BASE_DIR=$(dirname "$SCRIPT") + else + # Mac-compatible fallback + SCRIPT="$0" + BASE_DIR=$(cd "$(dirname "$SCRIPT")"; pwd -P) + fi + export BASE_DIR + cd "$BASE_DIR" +} + +# Check for required executables and paths +check_required_paths() { + local missing=0 + for p in "$@"; do + if [[ "$p" == */bin/* ]]; then + if [ ! -x "$p" ]; then + echo "ERROR: Missing executable: $p" >&2 + missing=1 + fi + else + if [ ! -e "$p" ]; then + echo "ERROR: Missing path: $p" >&2 + missing=1 + fi + fi + done + if [ "$missing" -ne 0 ]; then + echo "ERROR: Required paths missing. Exiting." >&2 + exit 1 + fi +} + +# Check for required commands +check_required_commands() { + local missing=0 + for cmd in "$@"; do + if ! command -v "$cmd" >/dev/null 2>&1; then + echo "ERROR: Required command not found: $cmd" >&2 + missing=1 + fi + done + if [ "$missing" -ne 0 ]; then + echo "ERROR: Required commands missing. Exiting." >&2 + exit 1 + fi +} + +# Print build configuration summary +print_build_summary() { + echo "==========================================" + echo "ExaConstit Build Configuration" + echo "==========================================" + echo "BASE_DIR: ${BASE_DIR}" + echo "BUILD_TYPE: ${BUILD_TYPE}" + echo "BUILD_SUFFIX: ${BUILD_SUFFIX}" + echo "REBUILD: ${REBUILD}" + echo "SYNC_SUBMODULES: ${SYNC_SUBMODULES}" + echo "" + echo "Compilers:" + echo " C: ${CMAKE_C_COMPILER}" + echo " CXX: ${CMAKE_CXX_COMPILER}" + if [ "${BUILD_TYPE}" != "cpu" ]; then + echo " GPU: ${CMAKE_GPU_COMPILER}" + echo " GPU Arch: ${CMAKE_GPU_ARCHITECTURES}" + fi + echo "" + echo "MPI Wrappers:" + echo " mpicc: ${MPI_C_COMPILER}" + echo " mpicxx: ${MPI_CXX_COMPILER}" + echo " mpifort: ${MPI_Fortran_COMPILER}" + echo "" + echo "Flags:" + echo " CXX: ${CMAKE_CXX_FLAGS}" + if [ "${BUILD_TYPE}" != "cpu" ]; then + echo " GPU: ${CMAKE_GPU_FLAGS}" + fi + echo " Linker: ${CMAKE_EXE_LINKER_FLAGS}" + echo "" + echo "Key Versions:" + echo " CAMP: ${CAMP_VER}" + echo " RAJA: ${RAJA_VER}" + if [ "${BUILD_TYPE}" != "cpu" ]; then + echo " Umpire: ${UMPIRE_VER}" + echo " CHAI: ${CHAI_VER}" + fi + echo " Hypre: ${HYPRE_VER}" + echo " MFEM: ${MFEM_BRANCH}" + echo " ExaCMech: ${EXACMECH_BRANCH}" + echo " ExaConstit: ${EXACONSTIT_BRANCH}" + echo "==========================================" +} + +# Validate configuration before proceeding +validate_configuration() { + echo "Validating configuration..." + + # Check compilers exist + check_required_paths "${CMAKE_C_COMPILER}" "${CMAKE_CXX_COMPILER}" + + if [ "${BUILD_TYPE}" != "cpu" ]; then + check_required_paths "${CMAKE_GPU_COMPILER}" + fi + + # Check MPI wrappers + check_required_paths "${MPI_C_COMPILER}" "${MPI_CXX_COMPILER}" "${MPI_Fortran_COMPILER}" + + # Check required commands + check_required_commands git cmake make curl tar + + echo "Configuration validation complete." +} \ No newline at end of file diff --git a/scripts/install/configs/cpu_intel_config.sh b/scripts/install/configs/cpu_intel_config.sh new file mode 100644 index 0000000..063b41e --- /dev/null +++ b/scripts/install/configs/cpu_intel_config.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# Configuration for Intel CPU builds + +# Build type identification +export BUILD_TYPE="cpu" +export BUILD_SUFFIX="cpu" + +########################################### +# Compiler Versions and Base Paths +########################################### +INTEL_VERSION="2023.2.1-magic" +COMPILER_VERSION="intel-${INTEL_VERSION}" +INTEL_BASE="/usr/tce/packages/intel/${COMPILER_VERSION}" + +MPI_IMPL="mvapich2" +MPI_VERSION="2.3.7" +MPI_COMPILER_VERSION="${MPI_IMPL}-${MPI_VERSION}" +MPI_BASE="/usr/tce/packages/${MPI_IMPL}/${MPI_COMPILER_VERSION}-${COMPILER_VERSION}" + +PYTHON_VERSION="3.12.2" +PYTHON_BASE="/usr/apps/python-${PYTHON_VERSION}" + +########################################### +# Module Loading +########################################### +module load intel/${INTEL_VERSION} +module load CMake/3.26.3 +module load python/3.12 +module list + +########################################### +# Compilers +########################################### +export CMAKE_C_COMPILER="${INTEL_BASE}/bin/icx" +export CMAKE_CXX_COMPILER="${INTEL_BASE}/bin/icpx" + +########################################### +# MPI Wrappers +########################################### +export MPI_C_COMPILER="${MPI_BASE}/bin/mpicc" +export MPI_CXX_COMPILER="${MPI_BASE}/bin/mpicxx" +export MPI_Fortran_COMPILER="${MPI_BASE}/bin/mpifort" + +########################################### +# Python +########################################### +export PYTHON_EXECUTABLE="${PYTHON_BASE}/bin/python" + +########################################### +# Build Flags +########################################### +export CMAKE_CXX_FLAGS="-fPIC" +export CMAKE_C_FLAGS="-fPIC" +export CMAKE_EXE_LINKER_FLAGS="" + +########################################### +# Build Options +########################################### +export OPENMP_ON="OFF" +export ENABLE_TESTS_EXACONSTIT="ON" +export MAKE_JOBS="${MAKE_JOBS:-4}" + +########################################### +# GPU Settings (Not Applicable) +########################################### +export GPU_BACKEND="NONE" +export CMAKE_GPU_COMPILER="" +export CMAKE_GPU_ARCHITECTURES="" +export CMAKE_GPU_FLAGS="" + +########################################### +# CHAI Options (Not Used in CPU Build) +########################################### +export CHAI_DISABLE_RM="OFF" +export CHAI_THIN_GPU_ALLOCATE="OFF" +export CHAI_ENABLE_PINNED="OFF" +export CHAI_ENABLE_PICK="OFF" +export CHAI_DEBUG="OFF" +export CHAI_ENABLE_GPU_SIMULATION_MODE="OFF" +export CHAI_ENABLE_UM="OFF" +export CHAI_ENABLE_MANAGED_PTR="OFF" +export CHAI_ENABLE_MANAGED_PTR_ON_GPU="OFF" \ No newline at end of file diff --git a/scripts/install/configs/cpu_mac_config.sh b/scripts/install/configs/cpu_mac_config.sh new file mode 100644 index 0000000..b2598c8 --- /dev/null +++ b/scripts/install/configs/cpu_mac_config.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +# Configuration for Mac CPU builds (Apple Silicon or Intel) + +# Build type identification +export BUILD_TYPE="cpu" +export BUILD_SUFFIX="cpu" + +########################################### +# User-Configurable Paths +########################################### +# IMPORTANT: Update these paths for your local Mac environment +# These are example paths - you MUST customize them for your system + +# Homebrew location (typical paths shown) +# Apple Silicon: /opt/homebrew +# Intel Mac: /usr/local +HOMEBREW_PREFIX="${HOMEBREW_PREFIX:-/opt/homebrew}" + +# System Clang (or specify Homebrew LLVM if preferred) +# System Clang is typically fine for macOS +CLANG_BASE="/usr/bin" + +# MPI installation location +# Options: +# - Homebrew: ${HOMEBREW_PREFIX}/bin +# - MacPorts: /opt/local/bin +# - Custom build: ${HOME}/local/bin +# - Anaconda: ${HOME}/anaconda3/bin +MPI_BASE="${HOME}/local/bin" + +# Python location +# Options: +# - Homebrew: ${HOMEBREW_PREFIX}/bin/python3 +# - Anaconda: ${HOME}/anaconda3/bin/python +# - System: /usr/bin/python3 +PYTHON_BASE="${HOME}/anaconda3/bin" + +########################################### +# Compiler Detection +########################################### +# Note: No module system on Mac, so we rely on PATH and explicit settings + +# Check if we're on Apple Silicon or Intel +if [[ $(uname -m) == "arm64" ]]; then + MAC_ARCH="arm64" + echo "Detected Apple Silicon (ARM64)" +else + MAC_ARCH="x86_64" + echo "Detected Intel Mac (x86_64)" +fi + +########################################### +# Compilers +########################################### +export CMAKE_C_COMPILER="${CLANG_BASE}/clang" +export CMAKE_CXX_COMPILER="${CLANG_BASE}/clang++" + +########################################### +# MPI Wrappers +########################################### +export MPI_C_COMPILER="${MPI_BASE}/mpicc" +export MPI_CXX_COMPILER="${MPI_BASE}/mpicxx" +export MPI_Fortran_COMPILER="${MPI_BASE}/mpifort" + +########################################### +# Python +########################################### +export PYTHON_EXECUTABLE="${PYTHON_BASE}/python" + +########################################### +# Build Flags +########################################### +# Mac-specific: may need to handle SDK location +# Homebrew libraries are in ${HOMEBREW_PREFIX}/lib +export CMAKE_CXX_FLAGS="-fPIC" +export CMAKE_C_FLAGS="-fPIC" +export CMAKE_EXE_LINKER_FLAGS="" + +# Optional: Add Homebrew library paths if needed +# export CMAKE_EXE_LINKER_FLAGS="-L${HOMEBREW_PREFIX}/lib -Wl,-rpath,${HOMEBREW_PREFIX}/lib" + +########################################### +# Build Options +########################################### +export OPENMP_ON="OFF" +export ENABLE_TESTS_EXACONSTIT="ON" + +# Mac-specific: may want to limit parallelism to avoid overheating +export MAKE_JOBS="${MAKE_JOBS:-$(sysctl -n hw.ncpu)}" + +########################################### +# GPU Settings (Not Applicable) +########################################### +export GPU_BACKEND="NONE" +export CMAKE_GPU_COMPILER="" +export CMAKE_GPU_ARCHITECTURES="" +export CMAKE_GPU_FLAGS="" + +########################################### +# CHAI Options (Not Used in CPU Build) +########################################### +export CHAI_DISABLE_RM="OFF" +export CHAI_THIN_GPU_ALLOCATE="OFF" +export CHAI_ENABLE_PINNED="OFF" +export CHAI_ENABLE_PICK="OFF" +export CHAI_DEBUG="OFF" +export CHAI_ENABLE_GPU_SIMULATION_MODE="OFF" +export CHAI_ENABLE_UM="OFF" +export CHAI_ENABLE_MANAGED_PTR="OFF" +export CHAI_ENABLE_MANAGED_PTR_ON_GPU="OFF" + +########################################### +# Mac-Specific Notes +########################################### +echo "==========================================" +echo "Mac Build Configuration Notes" +echo "==========================================" +echo "Architecture: ${MAC_ARCH}" +echo "Homebrew prefix: ${HOMEBREW_PREFIX}" +echo "" +echo "IMPORTANT: Verify these paths are correct for your system:" +echo " Compilers: ${CLANG_BASE}" +echo " MPI: ${MPI_BASE}" +echo " Python: ${PYTHON_BASE}" +echo "" +echo "If builds fail, common issues:" +echo " 1. MPI not installed: brew install open-mpi" +echo " 2. CMake too old: brew install cmake" +echo " 3. Wrong Python: Set PYTHON_BASE in this config" +echo " 4. Path issues: Ensure MPI/Python are in your PATH" +echo "==========================================" \ No newline at end of file diff --git a/scripts/install/configs/gpu_cuda_config.sh b/scripts/install/configs/gpu_cuda_config.sh new file mode 100644 index 0000000..1ca82d6 --- /dev/null +++ b/scripts/install/configs/gpu_cuda_config.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash +# Configuration for CUDA GPU builds + +# Build type identification +export BUILD_TYPE="cuda" +export BUILD_SUFFIX="cuda" +export GPU_BACKEND="CUDA" + +########################################### +# Compiler Versions and Base Paths +########################################### +# Host Compiler +CLANG_VERSION="ibm-14.0.5" +COMPILER_VERSION="clang-${CLANG_VERSION}" +CLANG_BASE="/usr/tce/packages/clang/${COMPILER_VERSION}" + +# GCC for toolchain +GCC_VERSION="11.2.1" +GCC_BASE="/usr/tce/packages/gcc/gcc-${GCC_VERSION}" +GCC_ARCH_SUBDIR="ppc64le-redhat-linux/11" # Architecture-specific lib path + +# CUDA +CUDA_VERSION="11.8.0" +CUDA_BASE="/usr/tce/packages/cuda/cuda-${CUDA_VERSION}" + +# MPI +MPI_IMPL="spectrum-mpi" +MPI_VERSION="rolling-release" +MPI_COMPILER_VERSION="${MPI_IMPL}-${MPI_VERSION}" +MPI_BASE="/usr/tce/packages/${MPI_IMPL}/${MPI_COMPILER_VERSION}-${COMPILER_VERSION}" + +# Python +PYTHON_VERSION="3.8.2" +PYTHON_BASE="/usr/tce/packages/python/python-${PYTHON_VERSION}" + +########################################### +# Module Loading +########################################### +module load clang/${CLANG_VERSION} +module load cmake/3.29.2 +module load cuda/${CUDA_VERSION} +module list + +########################################### +# Compilers +########################################### +export CMAKE_C_COMPILER="${CLANG_BASE}/bin/clang" +export CMAKE_CXX_COMPILER="${CLANG_BASE}/bin/clang++" +export CMAKE_GPU_COMPILER="${CUDA_BASE}/bin/nvcc" + +########################################### +# MPI Wrappers +########################################### +export MPI_C_COMPILER="${MPI_BASE}/bin/mpicc" +export MPI_CXX_COMPILER="${MPI_BASE}/bin/mpicxx" +export MPI_Fortran_COMPILER="${MPI_BASE}/bin/mpifort" + +########################################### +# Python +########################################### +export PYTHON_EXECUTABLE="${PYTHON_BASE}/bin/python3" + +########################################### +# GPU Architecture (Configurable) +########################################### +# Default to Volta (SM_70), can override with environment variable +# Common options: 60 (Pascal), 70 (Volta), 75 (Turing), 80 (Ampere), 86 (Ampere), 90 (Hopper) +export CMAKE_GPU_ARCHITECTURES="${CMAKE_GPU_ARCHITECTURES:-70}" + +########################################### +# Build Flags +########################################### +export CMAKE_CXX_FLAGS="-fPIC -std=c++17 --gcc-toolchain=${GCC_BASE}" +export CMAKE_C_FLAGS="-fPIC" +export CMAKE_GPU_FLAGS="-restrict --expt-extended-lambda -Xcompiler --gcc-toolchain=${GCC_BASE} -Xnvlink --suppress-stack-size-warning -std=c++17" + +# Linker flags for GCC toolchain integration +GCC_LIB_PATH="${GCC_BASE}/rh/usr/lib/gcc/${GCC_ARCH_SUBDIR}" +export CMAKE_EXE_LINKER_FLAGS="-L${GCC_LIB_PATH} -Wl,-rpath,${GCC_LIB_PATH}" + +# BLT-specific flags (used by some dependencies) +export BLT_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}" + +########################################### +# Build Options +########################################### +export OPENMP_ON="OFF" +export ENABLE_TESTS_EXACONSTIT="ON" +export MAKE_JOBS="${MAKE_JOBS:-4}" + +########################################### +# CHAI Options +########################################### +# Conservative settings for V100 GPUs +export CHAI_DISABLE_RM="OFF" # Keep resource manager enabled +export CHAI_THIN_GPU_ALLOCATE="OFF" # Use full allocations for stability +export CHAI_ENABLE_PINNED="ON" +export CHAI_ENABLE_PICK="ON" +export CHAI_DEBUG="OFF" +export CHAI_ENABLE_GPU_SIMULATION_MODE="OFF" +export CHAI_ENABLE_UM="ON" +export CHAI_ENABLE_MANAGED_PTR="ON" +export CHAI_ENABLE_MANAGED_PTR_ON_GPU="ON" + +########################################### +# CUDA-Specific Build Options +########################################### +# Ensure NVCC uses the correct host compiler +export CUDAHOSTCXX="${CMAKE_CXX_COMPILER}" +export CUDA_TOOLKIT_ROOT_DIR="${CUDA_BASE}" \ No newline at end of file diff --git a/scripts/install/configs/gpu_hip_config.sh b/scripts/install/configs/gpu_hip_config.sh new file mode 100644 index 0000000..89e8470 --- /dev/null +++ b/scripts/install/configs/gpu_hip_config.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +# Configuration for HIP GPU builds (AMD GPUs) + +# Build type identification +export BUILD_TYPE="hip" +export BUILD_SUFFIX="hip" +export GPU_BACKEND="HIP" + +########################################### +# Compiler Versions and Base Paths +########################################### +# ROCm Compiler +ROCM_VERSION="6.4.2" +ROCM_MAGIC_SUFFIX="magic" +COMPILER_VERSION="rocmcc-${ROCM_VERSION}-${ROCM_MAGIC_SUFFIX}" +ROCM_BASE="/usr/tce/packages/rocmcc/${COMPILER_VERSION}" + +# MPI - Cray MPICH +MPI_IMPL="cray-mpich" +MPI_VERSION="9.0.1" +MPI_COMPILER_VERSION="${MPI_IMPL}-${MPI_VERSION}" +MPI_BASE="/usr/tce/packages/${MPI_IMPL}/${MPI_COMPILER_VERSION}-${COMPILER_VERSION}" + +# Cray PE paths for linking +CRAY_MPICH_VERSION="${MPI_VERSION}" +CRAY_LIBFABRIC_VERSION="2.1" +CRAY_PMI_VERSION="6.1.16" +CRAY_PALS_VERSION="1.2.12" + +# Python +PYTHON_VERSION="3.9.12" +PYTHON_BASE="/usr/tce/packages/python/python-${PYTHON_VERSION}" + +########################################### +# Module Loading +########################################### +module load cmake/3.29.2 +module load rocmcc/${ROCM_VERSION}-${ROCM_MAGIC_SUFFIX} +module load rocm/${ROCM_VERSION} +module load ${MPI_IMPL}/${MPI_VERSION} +module list + +########################################### +# Compilers +########################################### +export CMAKE_C_COMPILER="${ROCM_BASE}/bin/amdclang" +export CMAKE_CXX_COMPILER="${ROCM_BASE}/bin/amdclang++" +export CMAKE_GPU_COMPILER="${ROCM_BASE}/bin/amdclang++" + +########################################### +# MPI Wrappers +########################################### +export MPI_C_COMPILER="${MPI_BASE}/bin/mpicc" +export MPI_CXX_COMPILER="${MPI_BASE}/bin/mpicxx" +export MPI_Fortran_COMPILER="${MPI_BASE}/bin/mpifort" + +########################################### +# Python +########################################### +export PYTHON_EXECUTABLE="${PYTHON_BASE}/bin/python3" + +########################################### +# GPU Architectures (Configurable) +########################################### +# Default to MI300A with xnack+ for unified memory +# Common options: +# gfx908 (MI100) +# gfx90a (MI200 series) +# gfx940 (MI300X - compute only) +# gfx942 (MI300A - APU with xnack support) +# gfx942:xnack+ (MI300A with unified memory) +export CMAKE_GPU_ARCHITECTURES="${CMAKE_GPU_ARCHITECTURES:-gfx942:xnack+}" + +# MFEM has issues with xnack+ in its compilation, so use base arch +export MFEM_HIP_ARCHITECTURES="${MFEM_HIP_ARCHITECTURES:-gfx942}" + +# Also set AMDGPU_TARGETS for completeness +export AMDGPU_TARGETS="${CMAKE_GPU_ARCHITECTURES}" + +########################################### +# Build Flags +########################################### +export CMAKE_CXX_FLAGS="-fPIC -std=c++17 -munsafe-fp-atomics" +export CMAKE_C_FLAGS="-fPIC" +export CMAKE_GPU_FLAGS="-munsafe-fp-atomics -fgpu-rdc" + +########################################### +# MPI Linking Flags (Cray-Specific) +########################################### +# Cray MPICH requires explicit linking to GTL and OFI libraries +MPICH_GTL_LIB="/opt/cray/pe/mpich/${CRAY_MPICH_VERSION}/gtl/lib" +MPICH_OFI_AMD_LIB="/opt/cray/pe/mpich/${CRAY_MPICH_VERSION}/ofi/amd/6.0/lib" + +# Runtime library paths for Cray PE +CRAY_LIBFABRIC_LIB="/opt/cray/libfabric/${CRAY_LIBFABRIC_VERSION}/lib64" +CRAY_PMI_LIB="/opt/cray/pe/pmi/${CRAY_PMI_VERSION}/lib" +CRAY_PALS_LIB="/opt/cray/pe/pals/${CRAY_PALS_VERSION}/lib" +ROCM_LLVM_LIB="/opt/rocm-${ROCM_VERSION}/llvm/lib" + +# Construct the full MPI linking flags +MPI_CRAY_RPATH_FLAGS="-Wl,-rpath,${CRAY_LIBFABRIC_LIB}:${CRAY_PMI_LIB}:${CRAY_PALS_LIB}:${ROCM_LLVM_LIB}" +MPI_CRAY_LINK_FLAGS="-lxpmem" # Cray xpmem for shared memory + +export CMAKE_EXE_LINKER_FLAGS="-lroctx64 -Wl,-rpath,${MPICH_OFI_AMD_LIB} ${MPI_CRAY_RPATH_FLAGS} -L${MPICH_GTL_LIB} -lmpi_gtl_hsa -Wl,-rpath,${MPICH_GTL_LIB} ${MPI_CRAY_LINK_FLAGS}" + +########################################### +# Build Options +########################################### +export OPENMP_ON="OFF" +export ENABLE_TESTS_EXACONSTIT="ON" +export MAKE_JOBS="${MAKE_JOBS:-4}" + +########################################### +# CHAI Options (MI300-Specific Tuning) +########################################### +# Aggressive settings optimized for MI300A APU architecture +# CHAI_DISABLE_RM=ON: Disable resource manager for APU unified memory +# CHAI_THIN_GPU_ALLOCATE=ON: Use thin allocations for better APU performance +export CHAI_DISABLE_RM="ON" +export CHAI_THIN_GPU_ALLOCATE="ON" +export CHAI_ENABLE_PINNED="ON" +export CHAI_ENABLE_PICK="ON" +export CHAI_DEBUG="OFF" +export CHAI_ENABLE_GPU_SIMULATION_MODE="OFF" +export CHAI_ENABLE_UM="ON" +export CHAI_ENABLE_MANAGED_PTR="ON" +export CHAI_ENABLE_MANAGED_PTR_ON_GPU="ON" + +########################################### +# HIP-Specific Build Options +########################################### +export ROCM_PATH="${ROCM_BASE}" +export HIP_PLATFORM="amd" +export HIP_COMPILER="clang" \ No newline at end of file diff --git a/scripts/install/unix_cpu_intel_install.sh b/scripts/install/unix_cpu_intel_install.sh new file mode 100644 index 0000000..b2eb274 --- /dev/null +++ b/scripts/install/unix_cpu_intel_install.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# ExaConstit CPU build with Intel compilers + +set -Eeuo pipefail +trap 'echo "Build failed at line $LINENO while running: $BASH_COMMAND" >&2' ERR + +# Resolve script directory +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# Source common infrastructure +source "${SCRIPT_DIR}/common/dependency_versions.sh" +source "${SCRIPT_DIR}/common/preflight_checks.sh" +source "${SCRIPT_DIR}/common/build_functions.sh" + +# Resolve BASE_DIR and change to it +resolve_base_dir + +# Source configuration +source "${SCRIPT_DIR}/configs/cpu_intel_config.sh" + +# User-controllable options +export REBUILD="${REBUILD:-OFF}" +export SYNC_SUBMODULES="${SYNC_SUBMODULES:-OFF}" + +# Validate and summarize +validate_configuration +print_build_summary + +# Build everything +build_all_dependencies + +echo "" +echo "==========================================" +echo "Build complete!" +echo "==========================================" \ No newline at end of file diff --git a/scripts/install/unix_cpu_intel_install_example.sh b/scripts/install/unix_cpu_intel_install_example.sh deleted file mode 100644 index 6ad5e0a..0000000 --- a/scripts/install/unix_cpu_intel_install_example.sh +++ /dev/null @@ -1,280 +0,0 @@ -#!/usr/bin/bash -# For ease all of this should be run in its own directory -# Build and run this in $SCRATCH/csm3_builds/ - -SCRIPT=$(readlink -f "$0") -BASE_DIR=$(dirname "$SCRIPT") - -# On macs the above two lines won't work but can be replaced with this line -# BASE_DIR=$(cd "$(dirname "$0")"; pwd -P) - -module load intel/2023.2.1-magic -module load CMake/3.26.3 -module load python/3.12 -module list - -CC="/usr/tce/packages/intel/intel-2023.2.1-magic/bin/icx" -CXX="/usr/tce/packages/intel/intel-2023.2.1-magic/bin/icpx" -MPICXX="/usr/tce/packages/mvapich2/mvapich2-2.3.7-intel-2023.2.1-magic/bin/mpicxx" -MPICC="/usr/tce/packages/mvapich2/mvapich2-2.3.7-intel-2023.2.1-magic/bin/mpicc" -PYTHON_EXE="/usr/apps/python-3.12.2/bin/python" - -#Build raja -if [ ! -d "camp" ]; then - git clone https://github.com/LLNL/camp.git -b v2024.07.0 - cd ${BASE_DIR}/camp - git submodule init - git submodule update - - if [ ! -d "build" ]; then - mkdir build - cd ${BASE_DIR}/camp/build - rm -rf * - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DRAJA_TIMER=chrono \ - -DENABLE_OPENMP=OFF \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${CXX} \ - -DENABLE_CUDA=OFF |& tee my_camp_config - make -j 2 |& tee my_camp_build - make install |& tee my_camp_install - fi -fi - -OLCF_CAMP_ROOT=${BASE_DIR}/camp/install_dir/ - -cd ${BASE_DIR} - -#exit -if [ ! -d "RAJA" ]; then - git clone https://github.com/LLNL/RAJA.git -b v2024.07.0 - cd ${BASE_DIR}/RAJA - git submodule init - git submodule update - cd ${BASE_DIR}/RAJA - if [ ! -d "build" ]; then - mkdir build - cd ${BASE_DIR}/RAJA/build - rm -rf * - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DRAJA_ENABLE_TESTS=OFF \ - -DRAJA_ENABLE_EXAMPLES=OFF \ - -DRAJA_ENABLE_BENCHMARKS=OFF \ - -DRAJA_TIMER=chrono \ - -DENABLE_OPENMP=OFF \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${CXX} \ - -DENABLE_CUDA=OFF \ - -Dcamp_DIR=${OLCF_CAMP_ROOT} |& tee my_raja_config - make -j 4 |& tee my_raja_build - make install |& tee my_raja_install - fi -fi - -OLCF_RAJA_ROOT=${BASE_DIR}/RAJA/install_dir/ - -echo ${OLCF_RAJA_ROOT} - -cd ${BASE_DIR} -if [ ! -d "ExaCMech" ]; then - # Clone the repo - git clone https://github.com/LLNL/ExaCMech.git - cd ${BASE_DIR}/ExaCMech - # Checkout the branch that has the HIP features on it - git checkout develop - # Update all the various submodules - git submodule init && git submodule update - if [ ! -d "${BASE_DIR}/ExaCMech/build" ]; then - mkdir build - cd ${BASE_DIR}/ExaCMech/build - rm -rf * - - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DENABLE_MINIAPPS=OFF \ - -DENABLE_OPENMP=OFF \ - -DRAJA_DIR=${OLCF_RAJA_ROOT}/lib/cmake/raja/ \ - -DBUILD_SHARED_LIBS=OFF \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${CXX} \ - -DENABLE_CUDA=OFF \ - -Dcamp_DIR=${OLCF_CAMP_ROOT}/lib/cmake/camp |& tee my_exacmech_config - - make -j 4 |& tee my_exacmech_build - make install |& tee my_exacmech_install - fi -fi -cd ${BASE_DIR} - -# Now to build our MFEM dependencies -# First let's install Hypre v2.23.0 -cd ${BASE_DIR} -if [ ! -d "hypre" ]; then - - git clone https://github.com/hypre-space/hypre.git --branch v2.30.0 --single-branch - cd ${BASE_DIR}/hypre/ - mkdir build - cd ${BASE_DIR}/hypre/build - rm -rf * - # Based on their install instructions - # This should work on most systems - # Hypre's default suggestions of just using configure don't always work - cmake ../src -DCMAKE_INSTALL_PREFIX=../src/hypre/ \ - -DWITH_MPI=TRUE \ - -DCMAKE_C_COMPILER=${MPICC} \ - -DCMAKE_CXX_COMPILER=${MPICXX} \ - -DCMAKE_Fortran_COMPILER=${MPIFORT} \ - -DCMAKE_BUILD_TYPE=Release \ - |& tee my_hypre_config - - make -j 4 |& tee my_hypre_build - make install |& tee my_hypre_install - - cd ${BASE_DIR}/hypre/src/hypre - OLCF_HYPRE_ROOT="$(pwd)" - -else - - echo " hypre already built " - OLCF_HYPRE_ROOT=${BASE_DIR}/hypre/src/hypre - -fi - -cd ${BASE_DIR} - -if [ ! -d "metis-5.1.0" ]; then - - curl -o metis-5.1.0.tar.gz https://mfem.github.io/tpls/metis-5.1.0.tar.gz - tar -xzf metis-5.1.0.tar.gz - rm metis-5.1.0.tar.gz - cd metis-5.1.0 - mkdir install_dir - make config prefix=${BASE_DIR}/metis-5.1.0/install_dir/ CC=${CC} CXX=${CXX} |& tee my_metis_config - make -j 4 |& tee my_metis_build - make install |& tee my_metis_install - cd ${BASE_DIR}/metis-5.1.0/install_dir/ - OLCF_METIS_ROOT="$(pwd)" -else - - echo " metis-5.1.0 already built " - OLCF_METIS_ROOT=${BASE_DIR}/metis-5.1.0/install_dir/ - -fi - -cd ${BASE_DIR} -if [ ! -d "ADIOS2" ]; then - # Clone the repo - git clone https://github.com/ornladios/ADIOS2.git - cd ${BASE_DIR}/ADIOS2 - # Checkout the branch that has the HIP features on it - git checkout v2.10.1 - # Update all the various submodules - git submodule init && git submodule update - - cd ${BASE_DIR} - if [ ! -d "${BASE_DIR}/ADIOS2/build" ]; then - cd ${BASE_DIR}/ADIOS2 - mkdir build - cd ${BASE_DIR}/ADIOS2/build - rm -rf * - - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${CXX} \ - -DADIOS2_USE_MPI=ON \ - -DADIOS2_USE_Blosc2=OFF \ - -DADIOS2_USE_BZip2=OFF \ - -DADIOS2_USE_ZeroMQ=OFF \ - -DADIOS2_USE_Endian_Reverse=OFF \ - -DADIOS2_USE_Fortran=OFF \ - -DADIOS2_USE_Python=OFF \ - -DADIOS2_USE_HDF5=OFF \ - -DADIOS2_USE_MPI=ON \ - -DADIOS2_USE_PNG=OFF \ - -DBUILD_SHARED_LIBS=ON \ - -DADIOS2_USE_SZ=OFF \ - -DADIOS2_USE_ZFP=OFF - - - make -j 4 |& tee my_adios2_build - make install |& tee my_adios2_install - fi -fi - -cd ${BASE_DIR} - -if [ ! -d "mfem" ]; then - git clone https://github.com/rcarson3/mfem.git - cd ${BASE_DIR}/mfem/ - git checkout exaconstit-dev - if [ ! -d "build" ]; then - mkdir build - fi - cd ${BASE_DIR}/mfem/build - LOCAL_CMAKE_MFEM="$(which cmake)" - echo "NOTE: MFEM: cmake = $LOCAL_CMAKE_MFEM" - #All the options - cmake ../ -DMFEM_USE_MPI=YES -DMFEM_USE_SIMD=NO\ - -DCMAKE_CXX_COMPILER=${MPICXX} \ - -DMETIS_DIR=${OLCF_METIS_ROOT} \ - -DHYPRE_DIR=${OLCF_HYPRE_ROOT} \ - -DCMAKE_INSTALL_PREFIX=../install_dir/ \ - -DMFEM_USE_OPENMP=OFF \ - -DMFEM_USE_RAJA=YES \ - -DRAJA_DIR:PATH=${OLCF_RAJA_ROOT} \ - -DMFEM_USE_ZLIB=YES \ - -DMFEM_USE_ADIOS2=ON \ - -DADIOS2_DIR=${BASE_DIR}/ADIOS2/install_dir/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DRAJA_REQUIRED_PACKAGES="camp" \ - -DMFEM_USE_CAMP=ON \ - -Dcamp_DIR:PATH=${OLCF_CAMP_ROOT}/lib/cmake/camp/ \ - -DCMAKE_CXX_STANDARD=14 \ - -DCMAKE_BUILD_TYPE=Release \ - |& tee my_mfem_config - - make -j 4 |& tee my_mfem_build - make install |& tee my_mfem_install -fi - -cd ${BASE_DIR} - -if [ ! -d "ExaConstit" ]; then - git clone https://github.com/llnl/ExaConstit.git - cd ${BASE_DIR}/ExaConstit/ - git checkout exaconstit-dev - git submodule init && git submodule update - - cd ${BASE_DIR}/ExaConstit/ - if [ ! -d "build" ]; then - mkdir build - fi - - cd ${BASE_DIR}/ExaConstit/build && rm -rf * - LOCAL_CMAKE_MFEM="$(which cmake)" - echo "NOTE: ExaConstit: cmake = $LOCAL_CMAKE_MFEM" - - cmake ../ -DCMAKE_C_COMPILER=${MPICC} \ - -DCMAKE_CXX_COMPILER=${MPICXX} \ - -DENABLE_TESTS=ON \ - -DENABLE_OPENMP=OFF \ - -DENABLE_FORTRAN=OFF \ - -DPYTHON_EXECUTABLE=${PYTHON_EXE} \ - -DMFEM_DIR=${BASE_DIR}/mfem/install_dir/lib/cmake/mfem/ \ - -DECMECH_DIR=${BASE_DIR}/ExaCMech/install_dir/ \ - -DSNLS_DIR=${BASE_DIR}/ExaCMech/install_dir/ \ - -DENABLE_SNLS_V03=ON \ - -DCMAKE_INSTALL_PREFIX=../install_dir/ \ - -DRAJA_DIR:PATH=${OLCF_RAJA_ROOT}/lib/cmake/raja/ \ - -DCMAKE_BUILD_TYPE=Release \ - -Dcamp_DIR=${OLCF_CAMP_ROOT}/lib/cmake/camp |& tee my_exconstit_config - - make -j 4|& tee my_exconstit_build - -fi diff --git a/scripts/install/unix_cpu_mac_install.sh b/scripts/install/unix_cpu_mac_install.sh new file mode 100644 index 0000000..307b027 --- /dev/null +++ b/scripts/install/unix_cpu_mac_install.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# ExaConstit CPU build for macOS + +set -Eeuo pipefail +trap 'echo "Build failed at line $LINENO while running: $BASH_COMMAND" >&2' ERR + +# Resolve script directory (Mac-compatible) +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# Source common infrastructure +source "${SCRIPT_DIR}/common/dependency_versions.sh" +source "${SCRIPT_DIR}/common/preflight_checks.sh" +source "${SCRIPT_DIR}/common/build_functions.sh" + +# Resolve BASE_DIR and change to it +resolve_base_dir + +# Source configuration +source "${SCRIPT_DIR}/configs/cpu_mac_config.sh" + +# User-controllable options +export REBUILD="${REBUILD:-OFF}" +export SYNC_SUBMODULES="${SYNC_SUBMODULES:-OFF}" + +# Validate and summarize +validate_configuration +print_build_summary + +# Build everything +build_all_dependencies + +echo "" +echo "==========================================" +echo "Build complete!" +echo "==========================================" \ No newline at end of file diff --git a/scripts/install/unix_gpu_cuda_install.sh b/scripts/install/unix_gpu_cuda_install.sh new file mode 100644 index 0000000..2ea5520 --- /dev/null +++ b/scripts/install/unix_gpu_cuda_install.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# ExaConstit CUDA build + +set -Eeuo pipefail +trap 'echo "Build failed at line $LINENO while running: $BASH_COMMAND" >&2' ERR + +# Resolve script directory +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# Source common infrastructure +source "${SCRIPT_DIR}/common/dependency_versions.sh" +source "${SCRIPT_DIR}/common/preflight_checks.sh" +source "${SCRIPT_DIR}/common/build_functions.sh" + +# Resolve BASE_DIR and change to it +resolve_base_dir + +# Source configuration +source "${SCRIPT_DIR}/configs/gpu_cuda_config.sh" + +# User-controllable options +export REBUILD="${REBUILD:-OFF}" +export SYNC_SUBMODULES="${SYNC_SUBMODULES:-OFF}" + +# Validate and summarize +validate_configuration +print_build_summary + +# Build everything +build_all_dependencies + +echo "" +echo "==========================================" +echo "Build complete!" +echo "==========================================" \ No newline at end of file diff --git a/scripts/install/unix_gpu_cuda_install_example.sh b/scripts/install/unix_gpu_cuda_install_example.sh deleted file mode 100644 index b9edc10..0000000 --- a/scripts/install/unix_gpu_cuda_install_example.sh +++ /dev/null @@ -1,476 +0,0 @@ -#!/usr/bin/bash -# For ease all of this should be run in its own directory - -SCRIPT=$(readlink -f "$0") -BASE_DIR=$(dirname "$SCRIPT") - -echo $BASH_VERSION - -# This is a bit system dependent but for El Capitan-like systems the below should work -# You should be able to modify it to work for your own system easily enough. -# Most of the options are defined by the first set of bash variables defined -# below. You'll likely need to modify the ROCM_BASE, MPIHOME, and then the various -# MPI/linker flags -# While this is largely targeted towards AMD GPU builds, you can probably update -# it easily enough for a NVidia GPU build of things... -module load cmake/3.29.2 clang/ibm-14.0.5 cuda/11.8.0 - -CLANG_BASE="/usr/tce/packages/clang/clang-ibm-14.0.5/" -NVCC_BASE="" -CC="${CLANG_BASE}/bin/clang" -CXX="${CLANG_BASE}/bin/clang++" - - -GCC_HOME="/usr/tce/packages/gcc/gcc-11.2.1" -CUDA_VER="11.8.0" -CUDA_TOOLKIT_ROOT_DIR="/usr/tce/packages/cuda/cuda-${CUDA_VER}" -NVCC="${CUDA_TOOLKIT_ROOT_DIR}/bin/nvcc" - -BLT_EXE_LINKER_FLAGS="-L${GCC_HOME}/rh/usr/lib/gcc/ppc64le-redhat-linux/11 -Wl,-rpath,${GCC_HOME}/rh/usr/lib/gcc/ppc64le-redhat-linux/11" - -MPIHOME="/usr/tce/packages/spectrum-mpi/spectrum-mpi-rolling-release-clang-ibm-14.0.5/" -MPICXX="${MPIHOME}/bin/mpicxx" -MPICC="${MPIHOME}/bin/mpicc" -MPIFORT="${MPIHOME}/bin/mpifort" -CUDAON="ON" -OPENMP_ON="OFF" -GPU_TARGETS="70" -CXX_FLAGS="-fPIC -std=c++17 --gcc-toolchain=${GCC_HOME}" -CUDA_FLAGS="-restrict --expt-extended-lambda -Xcompiler --gcc-toolchain=${GCC_HOME} -Xnvlink --suppress-stack-size-warning -std=c++17" - -PYTHON_EXE="/usr/tce/packages/python/python-3.8.2/bin/python3" -# Various build options for our various libaries -UMPIRE_ENABLE_TOOLS="ON" -UMPIRE_ENABLE_BACKTRACE="ON" -UMPIRE_ENABLE_BACKTRACE_SYMBOLS="ON" -# On V100s turn this off -CHAI_DISABLE_RM="OFF" -# Only for MI300a s other systems we need to turn this off -CHAI_THIN_GPU_ALLOCATE="OFF" -CHAI_ENABLE_PINNED="ON" -CHAI_ENABLE_PICK="ON" -CHAI_DEBUG="OFF" -CHAI_ENABLE_GPU_SIMULATION_MODE="OFF" -CHAI_ENABLE_UM="ON" -CHAI_ENABLE_MANAGED_PTR="ON" -CHAI_ENABLE_MANAGED_PTR_ON_GPU="ON" - -#Build camp -if [ ! -d "camp" ]; then - git clone https://github.com/LLNL/camp.git -b v2024.07.0 - cd ${BASE_DIR}/camp - git submodule init - git submodule update -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/camp/build_cuda" ]; then - cd ${BASE_DIR}/camp - mkdir build_cuda - cd ${BASE_DIR}/camp/build_cuda - rm -rf * - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir_cuda/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DENABLE_OPENMP=${OPENMP_ON} \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${CXX} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DCMAKE_CUDA_FLAGS="${CUDA_FLAGS}" \ - -DCMAKE_CUDA_COMPILER=${NVCC} \ - -DCMAKE_CUDA_HOST_COMPILER=${CXX} \ - -DCMAKE_CUDA_ARCHITECTURES=${GPU_TARGETS} \ - -DBLT_EXE_LINKER_FLAGS="${BLT_EXE_LINKER_FLAGS}" \ - -DENABLE_CUDA=${CUDAON} - make -j 2 - make install -fi - -CAMP_ROOT=${BASE_DIR}/camp/install_dir_cuda/ -echo ${CAMP_ROOT} -cd ${BASE_DIR} - -#exit -if [ ! -d "RAJA" ]; then - git clone https://github.com/LLNL/RAJA.git -b v2024.07.0 - cd ${BASE_DIR}/RAJA - git submodule init - git submodule update -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/RAJA/build_cuda" ]; then - cd ${BASE_DIR}/RAJA - mkdir build_cuda - cd ${BASE_DIR}/RAJA/build_cuda - rm -rf * - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir_cuda/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DRAJA_ENABLE_TESTS=OFF \ - -DRAJA_ENABLE_EXAMPLES=OFF \ - -DRAJA_ENABLE_BENCHMARKS=OFF \ - -DRAJA_ENABLE_REPRODUCERS=OFF \ - -DRAJA_ENABLE_EXERCISES=OFF \ - -DRAJA_ENABLE_VECTORIZATION=OFF \ - -DRAJA_ENABLE_DOCUMENTATION=OFF \ - -DRAJA_USE_DOUBLE=ON \ - -DRAJA_USE_BARE_PTR=ON \ - -DRAJA_TIMER=chrono \ - -DENABLE_OPENMP=${OPENMP_ON} \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${CXX} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DCMAKE_CUDA_FLAGS="${CUDA_FLAGS}" \ - -DBLT_EXE_LINKER_FLAGS="${BLT_EXE_LINKER_FLAGS}" \ - -DCMAKE_CUDA_COMPILER=${NVCC} \ - -DCMAKE_CUDA_HOST_COMPILER=${CXX} \ - -DCMAKE_CUDA_ARCHITECTURES=${GPU_TARGETS} \ - -DENABLE_CUDA=${CUDAON} \ - -Dcamp_DIR=${CAMP_ROOT} - make -j 4 - make install -fi - -RAJA_ROOT=${BASE_DIR}/RAJA/install_dir_cuda/ -echo ${RAJA_ROOT} -cd ${BASE_DIR} - -if [ ! -d "Umpire" ]; then - git clone https://github.com/LLNL/Umpire.git -b v2024.07.0 - cd ${BASE_DIR}/Umpire - git submodule init - git submodule update -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/Umpire/build_cuda" ]; then - cd ${BASE_DIR}/Umpire - mkdir build_cuda - cd ${BASE_DIR}/Umpire/build_cuda - rm -rf * - - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir_cuda/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DENABLE_OPENMP=${OPENMP_ON} \ - -DENABLE_MPI=OFF \ - -DUMPIRE_ENABLE_C=OFF \ - -DENABLE_FORTRAN=OFF \ - -DENABLE_GMOCK=OFF \ - -DUMPIRE_ENABLE_IPC_SHARED_MEMORY=OFF \ - -DUMPIRE_ENABLE_TOOLS=${UMPIRE_ENABLE_TOOLS} \ - -DUMPIRE_ENABLE_BACKTRACE=${UMPIRE_ENABLE_BACKTRACE} \ - -DUMPIRE_ENABLE_BACKTRACE_SYMBOLS=${UMPIRE_ENABLE_BACKTRACE_SYMBOLS} \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${CXX} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DCMAKE_CUDA_FLAGS="${CUDA_FLAGS}" \ - -DBLT_EXE_LINKER_FLAGS="${BLT_EXE_LINKER_FLAGS}" \ - -DCMAKE_CUDA_COMPILER=${NVCC} \ - -DCMAKE_CUDA_HOST_COMPILER=${CXX} \ - -DCMAKE_CUDA_ARCHITECTURES=${GPU_TARGETS} \ - -DENABLE_CUDA=${CUDAON} \ - -Dcamp_DIR=${CAMP_ROOT} - - make -j 4 - make install -fi - -UMPIRE_ROOT=${BASE_DIR}/Umpire/install_dir_cuda/ -echo ${UMPIRE_ROOT} -cd ${BASE_DIR} - -if [ ! -d "CHAI" ]; then - git clone https://github.com/LLNL/CHAI.git -b v2024.07.0 - cd ${BASE_DIR}/CHAI - git submodule init - git submodule update -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/CHAI/build_cuda" ]; then - cd ${BASE_DIR}/CHAI - mkdir build_cuda - cd ${BASE_DIR}/CHAI/build_cuda - rm -rf * - - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir_cuda/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DENABLE_EXAMPLES=OFF \ - -DENABLE_DOCS=OFF \ - -DENABLE_GMOCK=OFF \ - -DENABLE_OPENMP=${OPENMP_ON} \ - -DENABLE_MPI=OFF \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${CXX} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DCMAKE_CUDA_FLAGS="${CUDA_FLAGS}" \ - -DBLT_EXE_LINKER_FLAGS="${BLT_EXE_LINKER_FLAGS}" \ - -DCMAKE_CUDA_COMPILER=${NVCC} \ - -DCMAKE_CUDA_HOST_COMPILER=${CXX} \ - -DCMAKE_CUDA_ARCHITECTURES=${GPU_TARGETS} \ - -DENABLE_CUDA=${CUDAON} \ - -DCHAI_ENABLE_RAJA_PLUGIN=ON \ - -DCHAI_ENABLE_RAJA_NESTED_TEST=OFF \ - -DCHAI_ENABLE_PINNED=${CHAI_ENABLE_PINNED} \ - -DCHAI_DISABLE_RM=${CHAI_DISABLE_RM} \ - -DCHAI_THIN_GPU_ALLOCATE=${CHAI_THIN_GPU_ALLOCATE} \ - -DCHAI_ENABLE_PICK=${CHAI_ENABLE_PICK} \ - -DCHAI_DEBUG=${CHAI_DEBUG} \ - -DCHAI_ENABLE_GPU_SIMULATION_MODE=${CHAI_ENABLE_GPU_SIMULATION_MODE} \ - -DCHAI_ENABLE_UM=${CHAI_ENABLE_UM} \ - -DCHAI_ENABLE_MANAGED_PTR=${CHAI_ENABLE_MANAGED_PTR} \ - -DCHAI_ENABLE_MANAGED_PTR_ON_GPU=${CHAI_ENABLE_MANAGED_PTR_ON_GPU} \ - -Dfmt_DIR=${UMPIRE_ROOT} \ - -Dumpire_DIR=${UMPIRE_ROOT} \ - -DRAJA_DIR=${RAJA_ROOT} \ - -Dcamp_DIR=${CAMP_ROOT} - make -j 4 - make install -fi - -CHAI_ROOT=${BASE_DIR}/CHAI/install_dir_cuda/ -echo ${CHAI_ROOT} -cd ${BASE_DIR} - -if [ ! -d "ExaCMech" ]; then - # Clone the repo - git clone https://github.com/LLNL/ExaCMech.git - cd ${BASE_DIR}/ExaCMech - # Checkout the branch that has the HIP features on it - git checkout develop - # Update all the various submodules - git submodule init && git submodule update -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/ExaCMech/build_cuda" ]; then - cd ${BASE_DIR}/ExaCMech - mkdir build_cuda - cd ${BASE_DIR}/ExaCMech/build_cuda - rm -rf * - - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir_cuda/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DENABLE_MINIAPPS=OFF \ - -DENABLE_OPENMP=${OPENMP_ON} \ - -DBUILD_SHARED_LIBS=OFF \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${CXX} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DCMAKE_CUDA_FLAGS="${CUDA_FLAGS}" \ - -DBLT_EXE_LINKER_FLAGS="${BLT_EXE_LINKER_FLAGS}" \ - -DCMAKE_CUDA_COMPILER=${NVCC} \ - -DCMAKE_CUDA_HOST_COMPILER=${CXX} \ - -DCMAKE_CUDA_ARCHITECTURES=${GPU_TARGETS} \ - -DENABLE_CUDA=${CUDAON} \ - -DFMT_DIR=${UMPIRE_ROOT}/lib64/cmake/fmt \ - -DUMPIRE_DIR=${UMPIRE_ROOT}/lib64/cmake/umpire \ - -DRAJA_DIR=${RAJA_ROOT}/lib/cmake/raja \ - -DCHAI_DIR=${CHAI_ROOT}/lib/cmake/chai \ - -DCAMP_DIR=${CAMP_ROOT}/lib/cmake/camp - - make -j 4 - make install -fi - -ECMECH_ROOT=${BASE_DIR}/ExaCMech/install_dir_cuda/ -echo ${ECMECH_ROOT} -cd ${BASE_DIR} - -# Now to build our MFEM dependencies -# First let's install Hypre v2.23.0 -cd ${BASE_DIR} -if [ ! -d "hypre" ]; then - git clone https://github.com/hypre-space/hypre.git --branch v2.32.0 --single-branch -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/hypre/build_cuda" ]; then - cd ${BASE_DIR}/hypre/ - mkdir build_cuda - cd ${BASE_DIR}/hypre/build_cuda - rm -rf * - # Based on their install instructions - # This should work on most systems - # Hypre's default suggestions of just using configure don't always work - cmake ../src -DCMAKE_INSTALL_PREFIX=../src/hypre_hip/ \ - -DCMAKE_C_COMPILER=${CC} \ - -DMPI_CXX_COMPILER=${MPICXX} \ - -DMPI_C_COMPILER=${MPICC} \ - -DCMAKE_BUILD_TYPE=Release \ - |& tee my_hypre_config - - make -j 4 |& tee my_hypre_build - make install |& tee my_hypre_install - - cd ${BASE_DIR}/hypre/src/hypre_hip - HYPRE_ROOT="$(pwd)" - -else - - echo " hypre already built " - HYPRE_ROOT=${BASE_DIR}/hypre/src/hypre_hip - -fi - -cd ${BASE_DIR} - -if [ ! -d "metis-5.1.0" ]; then - - curl -o metis-5.1.0.tar.gz https://mfem.github.io/tpls/metis-5.1.0.tar.gz - tar -xzf metis-5.1.0.tar.gz - rm metis-5.1.0.tar.gz -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/metis-5.1.0/install_dir_cuda" ]; then - cd ${BASE_DIR}/metis-5.1.0 - mkdir install_dir_cuda - make distclean - make config prefix=${BASE_DIR}/metis-5.1.0/install_dir_cuda/ CC=${CC} CXX=${CXX} |& tee my_metis_config - make -j 4 |& tee my_metis_build - make install |& tee my_metis_install - cd ${BASE_DIR}/metis-5.1.0/install_dir_cuda/ - METIS_ROOT="$(pwd)" -else - echo " metis-5.1.0 already built " - METIS_ROOT=${BASE_DIR}/metis-5.1.0/install_dir_cuda/ -fi - -# cd ${BASE_DIR} -# if [ ! -d "ADIOS2" ]; then -# # Clone the repo -# git clone https://github.com/ornladios/ADIOS2.git -# cd ${BASE_DIR}/ADIOS2 -# # Checkout the branch that has the HIP features on it -# git checkout v2.10.0 -# # Update all the various submodules -# git submodule init && git submodule update -# fi -# cd ${BASE_DIR} -# if [ ! -d "${BASE_DIR}/ADIOS2/build_cuda" ]; then -# cd ${BASE_DIR}/ADIOS2 -# mkdir build_cuda -# cd ${BASE_DIR}/ADIOS2/build_cuda -# rm -rf * - -# cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir_cuda/ \ -# -DCMAKE_BUILD_TYPE=Release \ -# -DCMAKE_C_COMPILER=${CC} \ -# -DCMAKE_CXX_COMPILER=${CXX} \ -# -DADIOS2_USE_MPI=ON \ -# -DADIOS2_USE_Blosc2=OFF \ -# -DADIOS2_USE_BZip2=OFF \ -# -DADIOS2_USE_ZeroMQ=OFF \ -# -DADIOS2_USE_Endian_Reverse=OFF \ -# -DADIOS2_USE_Fortran=OFF \ -# -DADIOS2_USE_Python=ON \ -# -DPYTHON_EXECUTABLE=${PYTHON_EXE} \ -# -DADIOS2_USE_HDF5=OFF \ -# -DADIOS2_USE_MPI=ON \ -# -DADIOS2_USE_PNG=OFF \ -# -DBUILD_SHARED_LIBS=ON \ -# -DADIOS2_USE_SZ=OFF \ -# -DADIOS2_USE_ZFP=OFF - - -# make -j 16 |& tee my_adios2_build -# make install |& tee my_adios2_install -# fi - - -cd ${BASE_DIR} - -if [ ! -d "mfem" ]; then - git clone https://github.com/rcarson3/mfem.git - cd ${BASE_DIR}/mfem/ - git checkout exaconstit-dev -fi - -cd ${BASE_DIR} - -if [ ! -d "${BASE_DIR}/mfem/build_cuda" ]; then - mkdir ${BASE_DIR}/mfem/build_cuda - cd ${BASE_DIR}/mfem/build_cuda - LOCAL_CMAKE_MFEM="$(which cmake)" - echo "NOTE: MFEM: cmake = $LOCAL_CMAKE_MFEM" - #All the options - cmake ../ -DMFEM_USE_MPI=YES -DMFEM_USE_SIMD=NO\ - -DMETIS_DIR=${METIS_ROOT} \ - -DHYPRE_DIR=${HYPRE_ROOT} \ - -DMFEM_USE_RAJA=YES \ - -DRAJA_DIR:PATH=${RAJA_ROOT} \ - -DRAJA_REQUIRED_PACKAGES="camp" \ - -DMFEM_USE_CAMP=ON \ - -Dcamp_DIR:PATH=${CAMP_ROOT}/lib/cmake/camp/ \ - -DMFEM_USE_OPENMP=${OPENMP_ON} \ - -DMFEM_USE_ZLIB=YES \ - -DCMAKE_INSTALL_PREFIX=../install_dir_cuda/ \ - -DCMAKE_CXX_STANDARD=17 \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${CXX} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DCMAKE_CUDA_FLAGS="${CUDA_FLAGS}" \ - -DCMAKE_CUDA_COMPILER=${NVCC} \ - -DCMAKE_CUDA_HOST_COMPILER=${CXX} \ - -DCMAKE_CUDA_ARCHITECTURES=${GPU_TARGETS} \ - -DENABLE_CUDA=${CUDAON} \ - -DMFEM_USE_CUDA=${CUDAON} \ - -DCMAKE_BUILD_TYPE=Release \ - |& tee my_mfem_config - # -DMFEM_USE_MAGMA=ON \ - # -DMAGMA_DIR=${BASE_DIR}/magma/install_dir/ \ - # -DMFEM_USE_ADIOS2=ON \ - # -DADIOS2_DIR=${BASE_DIR}/ADIOS2/install_dir_cuda/ \ - - make -j 16 |& tee my_mfem_build - make install |& tee my_mfem_install -fi - -cd ${BASE_DIR} - -# : << 'END_COMMENT' -if [ ! -d "ExaConstit" ]; then - git clone https://github.com/llnl/ExaConstit.git - cd ${BASE_DIR}/ExaConstit/ - git checkout exaconstit-dev - git submodule init && git submodule update -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/ExaConstit/build_cuda" ]; then - cd ${BASE_DIR}/ExaConstit/ - mkdir build_cuda - - cd ${BASE_DIR}/ExaConstit/build_cuda #&& rm -rf * - LOCAL_CMAKE_MFEM="$(which cmake)" - echo "NOTE: ExaConstit: cmake = $LOCAL_CMAKE_MFEM" - - cmake ../ \ - -DCMAKE_CXX_STANDARD=17 \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${CXX} \ - -DMPI_CXX_COMPILER=${MPICXX} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DCMAKE_CUDA_FLAGS="${CUDA_FLAGS}" \ - -DBLT_EXE_LINKER_FLAGS="${BLT_EXE_LINKER_FLAGS}" \ - -DCMAKE_CUDA_COMPILER=${NVCC} \ - -DCMAKE_CUDA_HOST_COMPILER=${CXX} \ - -DCMAKE_CUDA_ARCHITECTURES=${GPU_TARGETS} \ - -DENABLE_CUDA=${CUDAON} \ - -DPYTHON_EXECUTABLE=${PYTHON_EXE} \ - -DENABLE_TESTS=ON \ - -DENABLE_OPENMP=OFF \ - -DENABLE_FORTRAN=OFF \ - -DENABLE_SNLS_V03=ON \ - -DCMAKE_INSTALL_PREFIX=../install_dir/ \ - -DRAJA_DIR:PATH=${RAJA_ROOT}/lib/cmake/raja/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DMFEM_DIR=${BASE_DIR}/mfem/install_dir_cuda/lib/cmake/mfem/ \ - -DECMECH_DIR=${BASE_DIR}/ExaCMech/install_dir_cuda/ \ - -DSNLS_DIR=${BASE_DIR}/ExaCMech/install_dir_cuda/ \ - -DFMT_DIR=${UMPIRE_ROOT}/lib64/cmake/fmt \ - -DUMPIRE_DIR=${UMPIRE_ROOT}/lib64/cmake/umpire \ - -DRAJA_DIR=${RAJA_ROOT}/lib/cmake/raja \ - -DCHAI_DIR=${CHAI_ROOT}/lib/cmake/chai \ - -DCAMP_DIR=${CAMP_ROOT}/lib/cmake/camp |& tee my_exconstit_config - - make -j 4|& tee my_exconstit_build -fi -###END_COMMENT diff --git a/scripts/install/unix_gpu_hip_install.sh b/scripts/install/unix_gpu_hip_install.sh new file mode 100644 index 0000000..22495ac --- /dev/null +++ b/scripts/install/unix_gpu_hip_install.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# ExaConstit HIP build for AMD GPUs + +set -Eeuo pipefail +trap 'echo "Build failed at line $LINENO while running: $BASH_COMMAND" >&2' ERR + +# Resolve script directory +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# Source common infrastructure +source "${SCRIPT_DIR}/common/dependency_versions.sh" +source "${SCRIPT_DIR}/common/preflight_checks.sh" +source "${SCRIPT_DIR}/common/build_functions.sh" + +# Resolve BASE_DIR and change to it +resolve_base_dir + +# Source configuration +source "${SCRIPT_DIR}/configs/gpu_hip_config.sh" + +# User-controllable options +export REBUILD="${REBUILD:-OFF}" +export SYNC_SUBMODULES="${SYNC_SUBMODULES:-OFF}" + +# Validate and summarize +validate_configuration +print_build_summary + +# Build everything +build_all_dependencies + +echo "" +echo "==========================================" +echo "Build complete!" +echo "==========================================" \ No newline at end of file diff --git a/scripts/install/unix_gpu_hip_install_example.sh b/scripts/install/unix_gpu_hip_install_example.sh deleted file mode 100644 index fa15503..0000000 --- a/scripts/install/unix_gpu_hip_install_example.sh +++ /dev/null @@ -1,462 +0,0 @@ -#!/usr/bin/bash -# For ease all of this should be run in its own directory - -SCRIPT=$(readlink -f "$0") -BASE_DIR=$(dirname "$SCRIPT") - -echo $BASH_VERSION - -# This is a bit system dependent but for El Capitan-like systems the below should work -# You should be able to modify it to work for your own system easily enough. -# Most of the options are defined by the first set of bash variables defined -# below. You'll likely need to modify the ROCM_BASE, MPIHOME, and then the various -# MPI/linker flags -# While this is largely targeted towards AMD GPU builds, you can probably update -# it easily enough for a NVidia GPU build of things... -module load cmake/3.29.2 rocmcc/6.3.1-magic rocm/6.3.1 cray-mpich/8.1.31 - -ROCM_BASE="/usr/tce/packages/rocmcc/rocmcc-6.3.1-magic/" -CC="${ROCM_BASE}/bin/amdclang" -CXX="${ROCM_BASE}/bin/amdclang++" -HIPCC="${ROCM_BASE}/bin/hipcc" -MPIHOME="/usr/tce/packages/cray-mpich/cray-mpich-8.1.31-rocmcc-6.3.1-magic/" -MPILIBHOME="/opt/cray/pe/mpich/8.1.31/gtl/lib" -MPIAMDHOME="/opt/cray/pe/mpich/8.1.31/ofi/amd/6.0/lib" -MPICRAYFLAGS="-Wl,-rpath,/opt/cray/libfabric/2.1/lib64:/opt/cray/pe/pmi/6.1.15/lib:/opt/cray/pe/pals/1.2.12/lib:/opt/rocm-6.3.1/llvm/lib -lxpmem" -MPICXX="$MPIHOME/bin/mpicxx" -MPICC="$MPIHOME/bin/mpicc" -MPIFORT="$MPIHOME/bin/mpifort" -ROCMON="ON" -OPENMP_ON="OFF" -LOC_ROCM_ARCH="gfx942" -GPU_TARGETS="gfx942" -AMDGPU_TARGETS="gfx942" -CXX_FLAGS="-fPIC -std=c++17 -munsafe-fp-atomics" - -EXE_LINK_FLAGS="--hip-link -lroctx64 -Wl,-rpath,${MPIAMDHOME} ${MPICRAYFLAGS} -L${MPILIBHOME} -lmpi_gtl_hsa -Wl,-rpath,${MPILIBHOME}" -PYTHON_EXE="/usr/tce/packages/python/python-3.9.12/bin/python3" -# Various build options for our various libaries -UMPIRE_ENABLE_TOOLS="ON" -UMPIRE_ENABLE_BACKTRACE="ON" -UMPIRE_ENABLE_BACKTRACE_SYMBOLS="ON" -# On V100s turn this off -CHAI_DISABLE_RM="ON" -# Only for MI300a s other systems we need to turn this off -CHAI_THIN_GPU_ALLOCATE="ON" -CHAI_ENABLE_PINNED="ON" -CHAI_ENABLE_PICK="ON" -CHAI_DEBUG="OFF" -CHAI_ENABLE_GPU_SIMULATION_MODE="OFF" -CHAI_ENABLE_UM="ON" -CHAI_ENABLE_MANAGED_PTR="ON" -CHAI_ENABLE_MANAGED_PTR_ON_GPU="ON" - -#Build camp -if [ ! -d "camp" ]; then - git clone https://github.com/LLNL/camp.git -b v2024.07.0 - cd ${BASE_DIR}/camp - git submodule init - git submodule update -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/camp/build_hip" ]; then - cd ${BASE_DIR}/camp - mkdir build_hip - cd ${BASE_DIR}/camp/build_hip - rm -rf * - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir_hip/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DENABLE_OPENMP=OFF \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${HIPCC} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DCMAKE_HIP_ARCHITECTURES=${LOC_ROCM_ARCH} \ - -DENABLE_HIP=$ROCMON - make -j 2 - make install -fi - -CAMP_ROOT=${BASE_DIR}/camp/install_dir_hip/ -echo ${CAMP_ROOT} -cd ${BASE_DIR} - -#exit -if [ ! -d "RAJA" ]; then - git clone https://github.com/LLNL/RAJA.git -b v2024.07.0 - cd ${BASE_DIR}/RAJA - git submodule init - git submodule update -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/RAJA/build_hip" ]; then - cd ${BASE_DIR}/RAJA - mkdir build_hip - cd ${BASE_DIR}/RAJA/build_hip - rm -rf * - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir_hip/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DRAJA_ENABLE_TESTS=OFF \ - -DRAJA_ENABLE_EXAMPLES=OFF \ - -DRAJA_ENABLE_BENCHMARKS=OFF \ - -DRAJA_ENABLE_REPRODUCERS=OFF \ - -DRAJA_ENABLE_EXERCISES=OFF \ - -DRAJA_ENABLE_VECTORIZATION=OFF \ - -DRAJA_ENABLE_DOCUMENTATION=OFF \ - -DRAJA_USE_DOUBLE=ON \ - -DRAJA_USE_BARE_PTR=ON \ - -DRAJA_TIMER=chrono \ - -DENABLE_OPENMP=${OPENMP_ON} \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${HIPCC} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DENABLE_HIP=${ROCMON} \ - -DCMAKE_HIP_ARCHITECTURES=${LOC_ROCM_ARCH} \ - -DGPU_TARGETS=${LOCM_ROCM_ARCH} \ - -DAMDGPU_TARGETS=${LOCM_ROCM_ARCH} \ - -DHIP_CXX_COMPILER=${HIPCC} \ - -Dcamp_DIR=${CAMP_ROOT} - make -j 4 - make install -fi - -RAJA_ROOT=${BASE_DIR}/RAJA/install_dir_hip/ -echo ${RAJA_ROOT} -cd ${BASE_DIR} - -if [ ! -d "Umpire" ]; then - git clone https://github.com/LLNL/Umpire.git -b v2024.07.0 - cd ${BASE_DIR}/Umpire - git submodule init - git submodule update -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/Umpire/build_hip" ]; then - cd ${BASE_DIR}/Umpire - mkdir build_hip - cd ${BASE_DIR}/Umpire/build_hip - rm -rf * - - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir_hip/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DENABLE_OPENMP=${OPENMP_ON} \ - -DENABLE_MPI=OFF \ - -DUMPIRE_ENABLE_C=OFF \ - -DENABLE_FORTRAN=OFF \ - -DENABLE_GMOCK=OFF \ - -DUMPIRE_ENABLE_IPC_SHARED_MEMORY=OFF \ - -DUMPIRE_ENABLE_TOOLS=${UMPIRE_ENABLE_TOOLS} \ - -DUMPIRE_ENABLE_BACKTRACE=${UMPIRE_ENABLE_BACKTRACE} \ - -DUMPIRE_ENABLE_BACKTRACE_SYMBOLS=${UMPIRE_ENABLE_BACKTRACE_SYMBOLS} \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${HIPCC} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DENABLE_HIP=${ROCMON} \ - -DCMAKE_HIP_ARCHITECTURES=${LOC_ROCM_ARCH} \ - -DGPU_TARGETS=${LOCM_ROCM_ARCH} \ - -DAMDGPU_TARGETS=${LOCM_ROCM_ARCH} \ - -DHIP_CXX_COMPILER=${HIPCC} \ - -Dcamp_DIR=${CAMP_ROOT} - - make -j 4 - make install -fi - -UMPIRE_ROOT=${BASE_DIR}/Umpire/install_dir_hip/ -echo ${UMPIRE_ROOT} -cd ${BASE_DIR} - -if [ ! -d "CHAI" ]; then - git clone https://github.com/LLNL/CHAI.git -b v2024.07.0 - cd ${BASE_DIR}/CHAI - git submodule init - git submodule update -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/CHAI/build_hip" ]; then - cd ${BASE_DIR}/CHAI - mkdir build_hip - cd ${BASE_DIR}/CHAI/build_hip - rm -rf * - - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir_hip/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DENABLE_EXAMPLES=OFF \ - -DENABLE_DOCS=OFF \ - -DENABLE_GMOCK=OFF \ - -DENABLE_OPENMP=${OPENMP_ON} \ - -DENABLE_MPI=OFF \ - -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${HIPCC} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DENABLE_HIP=${ROCMON} \ - -DCMAKE_HIP_ARCHITECTURES=${LOC_ROCM_ARCH} \ - -DGPU_TARGETS=${LOCM_ROCM_ARCH} \ - -DAMDGPU_TARGETS=${LOCM_ROCM_ARCH} \ - -DHIP_CXX_COMPILER=${HIPCC} \ - -DCHAI_ENABLE_RAJA_PLUGIN=ON \ - -DCHAI_ENABLE_RAJA_NESTED_TEST=OFF \ - -DCHAI_ENABLE_PINNED=${CHAI_ENABLE_PINNED} \ - -DCHAI_DISABLE_RM=${CHAI_DISABLE_RM} \ - -DCHAI_THIN_GPU_ALLOCATE=${CHAI_THIN_GPU_ALLOCATE} \ - -DCHAI_ENABLE_PICK=${CHAI_ENABLE_PICK} \ - -DCHAI_DEBUG=${CHAI_DEBUG} \ - -DCHAI_ENABLE_GPU_SIMULATION_MODE=${CHAI_ENABLE_GPU_SIMULATION_MODE} \ - -DCHAI_ENABLE_UM=${CHAI_ENABLE_UM} \ - -DCHAI_ENABLE_MANAGED_PTR=${CHAI_ENABLE_MANAGED_PTR} \ - -DCHAI_ENABLE_MANAGED_PTR_ON_GPU=${CHAI_ENABLE_MANAGED_PTR_ON_GPU} \ - -Dfmt_DIR=${UMPIRE_ROOT} \ - -Dumpire_DIR=${UMPIRE_ROOT} \ - -DRAJA_DIR=${RAJA_ROOT} \ - -Dcamp_DIR=${CAMP_ROOT} - make -j 4 - make install -fi - -CHAI_ROOT=${BASE_DIR}/CHAI/install_dir_hip/ -echo ${CHAI_ROOT} -cd ${BASE_DIR} - -if [ ! -d "ExaCMech" ]; then - # Clone the repo - git clone https://github.com/LLNL/ExaCMech.git - cd ${BASE_DIR}/ExaCMech - # Checkout the branch that has the HIP features on it - git checkout develop - # Update all the various submodules - git submodule init && git submodule update -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/ExaCMech/build_hip" ]; then - cd ${BASE_DIR}/ExaCMech - mkdir build_hip - cd ${BASE_DIR}/ExaCMech/build_hip - rm -rf * - - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir_hip/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=OFF \ - -DENABLE_MINIAPPS=OFF \ - -DENABLE_OPENMP=${OPENMP_ON} \ - -DBUILD_SHARED_LIBS=OFF \ - -DCMAKE_CXX_COMPILER=${HIPCC} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DENABLE_HIP=$ROCMON \ - -DCMAKE_HIP_ARCHITECTURES=${LOC_ROCM_ARCH} \ - -DGPU_TARGETS=${LOCM_ROCM_ARCH} \ - -DAMDGPU_TARGETS=${LOCM_ROCM_ARCH} \ - -DHIP_CXX_COMPILER=${HIPCC} \ - -DFMT_DIR=${UMPIRE_ROOT}/lib64/cmake/fmt \ - -DUMPIRE_DIR=${UMPIRE_ROOT}/lib64/cmake/umpire \ - -DRAJA_DIR=${RAJA_ROOT}/lib/cmake/raja \ - -DCHAI_DIR=${CHAI_ROOT}/lib/cmake/chai \ - -DCAMP_DIR=${CAMP_ROOT}/lib/cmake/camp - - make -j 4 - make install -fi - -ECMECH_ROOT=${BASE_DIR}/ExaCMech/install_dir_hip/ -echo ${ECMECH_ROOT} -cd ${BASE_DIR} - -# Now to build our MFEM dependencies -# First let's install Hypre v2.23.0 -cd ${BASE_DIR} -if [ ! -d "hypre" ]; then - git clone https://github.com/hypre-space/hypre.git --branch v2.32.0 --single-branch -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/hypre/build_hip" ]; then - cd ${BASE_DIR}/hypre/ - mkdir build_hip - cd ${BASE_DIR}/hypre/build_hip - rm -rf * - # Based on their install instructions - # This should work on most systems - # Hypre's default suggestions of just using configure don't always work - cmake ../src -DCMAKE_INSTALL_PREFIX=../src/hypre_hip/ \ - -DCMAKE_C_COMPILER=${CC} \ - -DMPI_CXX_COMPILER=${MPICXX} \ - -DMPI_C_COMPILER=${MPICC} \ - -DCMAKE_BUILD_TYPE=Release \ - |& tee my_hypre_config - - make -j 4 |& tee my_hypre_build - make install |& tee my_hypre_install - - cd ${BASE_DIR}/hypre/src/hypre_hip - HYPRE_ROOT="$(pwd)" - -else - - echo " hypre already built " - HYPRE_ROOT=${BASE_DIR}/hypre/src/hypre_hip - -fi - -cd ${BASE_DIR} - -if [ ! -d "metis-5.1.0" ]; then - - curl -o metis-5.1.0.tar.gz https://mfem.github.io/tpls/metis-5.1.0.tar.gz - tar -xzf metis-5.1.0.tar.gz - rm metis-5.1.0.tar.gz -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/metis-5.1.0/install_dir_hip" ]; then - cd ${BASE_DIR}/metis-5.1.0 - mkdir install_dir_hip - make distclean - make config prefix=${BASE_DIR}/metis-5.1.0/install_dir_hip/ CC=${CC} CXX=${CXX} |& tee my_metis_config - make -j 4 |& tee my_metis_build - make install |& tee my_metis_install - cd ${BASE_DIR}/metis-5.1.0/install_dir_hip/ - METIS_ROOT="$(pwd)" -else - echo " metis-5.1.0 already built " - METIS_ROOT=${BASE_DIR}/metis-5.1.0/install_dir_hip/ -fi - -# cd ${BASE_DIR} -# if [ ! -d "ADIOS2" ]; then -# # Clone the repo -# git clone https://github.com/ornladios/ADIOS2.git -# cd ${BASE_DIR}/ADIOS2 -# # Checkout the branch that has the HIP features on it -# git checkout v2.10.0 -# # Update all the various submodules -# git submodule init && git submodule update -# fi -# cd ${BASE_DIR} -# if [ ! -d "${BASE_DIR}/ADIOS2/build_hip" ]; then -# cd ${BASE_DIR}/ADIOS2 -# mkdir build_hip -# cd ${BASE_DIR}/ADIOS2/build_hip -# rm -rf * - -# cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir_hip/ \ -# -DCMAKE_BUILD_TYPE=Release \ -# -DCMAKE_C_COMPILER=${CC} \ -# -DCMAKE_CXX_COMPILER=${CXX} \ -# -DADIOS2_USE_MPI=ON \ -# -DADIOS2_USE_Blosc2=OFF \ -# -DADIOS2_USE_BZip2=OFF \ -# -DADIOS2_USE_ZeroMQ=OFF \ -# -DADIOS2_USE_Endian_Reverse=OFF \ -# -DADIOS2_USE_Fortran=OFF \ -# -DADIOS2_USE_Python=ON \ -# -DPYTHON_EXECUTABLE=${PYTHON_EXE} \ -# -DADIOS2_USE_HDF5=OFF \ -# -DADIOS2_USE_MPI=ON \ -# -DADIOS2_USE_PNG=OFF \ -# -DBUILD_SHARED_LIBS=ON \ -# -DADIOS2_USE_SZ=OFF \ -# -DADIOS2_USE_ZFP=OFF - - -# make -j 16 |& tee my_adios2_build -# make install |& tee my_adios2_install -# fi - - -cd ${BASE_DIR} - -if [ ! -d "mfem" ]; then - git clone https://github.com/rcarson3/mfem.git - cd ${BASE_DIR}/mfem/ - git checkout exaconstit-dev -fi - -cd ${BASE_DIR} - -if [ ! -d "${BASE_DIR}/mfem/build_hip" ]; then - mkdir ${BASE_DIR}/mfem/build_hip - cd ${BASE_DIR}/mfem/build_hip - LOCAL_CMAKE_MFEM="$(which cmake)" - echo "NOTE: MFEM: cmake = $LOCAL_CMAKE_MFEM" - #All the options - cmake ../ -DMFEM_USE_MPI=YES -DMFEM_USE_SIMD=NO\ - -DMETIS_DIR=${METIS_ROOT} \ - -DHYPRE_DIR=${HYPRE_ROOT} \ - -DMFEM_USE_RAJA=YES \ - -DRAJA_DIR:PATH=${RAJA_ROOT} \ - -DRAJA_REQUIRED_PACKAGES="camp" \ - -DMFEM_USE_CAMP=ON \ - -Dcamp_DIR:PATH=${CAMP_ROOT}/lib/cmake/camp/ \ - -DMFEM_USE_OPENMP=${OPENMP_ON} \ - -DMFEM_USE_ZLIB=YES \ - -DCMAKE_CXX_COMPILER=${HIPCC} \ - -DMPI_CXX_COMPILER=${MPICXX} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DCMAKE_INSTALL_PREFIX=../install_dir_hip/ \ - -DCMAKE_CXX_STANDARD=17 \ - -DMFEM_USE_HIP=${ROCMON} \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_HIP_ARCHITECTURES=${LOC_ROCM_ARCH} \ - -DHIP_ARCH=${LOC_ROCM_ARCH} \ - -DGPU_TARGETS=${LOCM_ROCM_ARCH} \ - -DAMDGPU_TARGETS=${LOCM_ROCM_ARCH} \ - -DHIP_CXX_COMPILER=${HIPCC} \ - |& tee my_mfem_config - # -DMFEM_USE_MAGMA=ON \ - # -DMAGMA_DIR=${BASE_DIR}/magma/install_dir/ \ - # -DMFEM_USE_ADIOS2=ON \ - # -DADIOS2_DIR=${BASE_DIR}/ADIOS2/install_dir_hip/ \ - - make -j 16 |& tee my_mfem_build - make install |& tee my_mfem_install -fi - -cd ${BASE_DIR} - -# : << 'END_COMMENT' -if [ ! -d "ExaConstit" ]; then - git clone https://github.com/llnl/ExaConstit.git - cd ${BASE_DIR}/ExaConstit/ - git checkout exaconstit-dev - git submodule init && git submodule update -fi -cd ${BASE_DIR} -if [ ! -d "${BASE_DIR}/ExaConstit/build_hip" ]; then - cd ${BASE_DIR}/ExaConstit/ - mkdir build_hip - - cd ${BASE_DIR}/ExaConstit/build_hip #&& rm -rf * - LOCAL_CMAKE_MFEM="$(which cmake)" - echo "NOTE: ExaConstit: cmake = $LOCAL_CMAKE_MFEM" - - cmake ../ -DCMAKE_C_COMPILER=${CC} \ - -DCMAKE_CXX_COMPILER=${HIPCC} \ - -DMPI_CXX_COMPILER=${MPICXX} \ - -DHIP_CXX_COMPILER=${HIPCC} \ - -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" \ - -DCMAKE_EXE_LINKER_FLAGS="${EXE_LINK_FLAGS}" \ - -DPYTHON_EXECUTABLE=${PYTHON_EXE} \ - -DENABLE_TESTS=ON \ - -DENABLE_OPENMP=OFF \ - -DENABLE_FORTRAN=OFF \ - -DENABLE_HIP=${ROCMON} \ - -DENABLE_SNLS_V03=ON \ - -DCMAKE_INSTALL_PREFIX=../install_dir/ \ - -DRAJA_DIR:PATH=${RAJA_ROOT}/lib/cmake/raja/ \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_HIP_ARCHITECTURES=${LOC_ROCM_ARCH} \ - -DGPU_TARGETS=${LOCM_ROCM_ARCH} \ - -DAMDGPU_TARGETS=${LOCM_ROCM_ARCH} \ - -DMFEM_DIR=${BASE_DIR}/mfem/install_dir_hip/lib/cmake/mfem/ \ - -DECMECH_DIR=${BASE_DIR}/ExaCMech/install_dir_hip/ \ - -DSNLS_DIR=${BASE_DIR}/ExaCMech/install_dir_hip/ \ - -DFMT_DIR=${UMPIRE_ROOT}/lib64/cmake/fmt \ - -DUMPIRE_DIR=${UMPIRE_ROOT}/lib64/cmake/umpire \ - -DRAJA_DIR=${RAJA_ROOT}/lib/cmake/raja \ - -DCHAI_DIR=${CHAI_ROOT}/lib/cmake/chai \ - -DCAMP_DIR=${CAMP_ROOT}/lib/cmake/camp |& tee my_exconstit_config - - make -j 4|& tee my_exconstit_build -fi -###END_COMMENT diff --git a/scripts/install/unix_install_example.sh b/scripts/install/unix_install_example.sh deleted file mode 100644 index f31a759..0000000 --- a/scripts/install/unix_install_example.sh +++ /dev/null @@ -1,198 +0,0 @@ -#!/bin/bash -# The below is a bash script that should work on most UNIX systems to download all of ExaConstit and its dependencies -# and then install them. -# -# For ease all of this should be run in its own directory -SCRIPT=$(readlink -f "$0") -BASE_DIR=$(dirname "$SCRIPT") - -# Set this to your location of python -# for example PYTHON_EXE for an anaconda build of python -# on a mac might be somewhere like: -PYTHON_EXE="/Users/USER/anaconda3/bin/python" - -# If you are using SPACK or have another module like system to set-up your developer environment -# you'll want to load up the necessary compilers and devs environments -# In other words make sure what ever MPI you want is loaded, C++, C, and Fortran compilers are loaded, and -# a cmake version b/t 3.12 and 3.18. - -# Build raja -if [ ! -d "raja" ]; then - git clone --recursive https://github.com/llnl/raja.git --branch v2024.07.0 --single-branch - cd ${BASE_DIR}/raja - # Instantiate all the submodules - git submodule init - git submodule update - # Build everything - mkdir build - cd ${BASE_DIR}/raja/build/ - # GPU build - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir/ \ - -DENABLE_OPENMP=OFF \ - -DRAJA_TIMER=chrono \ - -DENABLE_TESTS=OFF \ - -DCMAKE_BUILD_TYPE=Release - make -j 4 - make install -else - - echo " RAJA already built " - -fi - -# Now to build ExaCMech -cd ${BASE_DIR} - -if [ ! -d "ExaCMech" ]; then - - git clone https://github.com/LLNL/ExaCMech.git --single-branch - cd ${BASE_DIR}/ExaCMech - # Instantiate all the submodules - git submodule init - git submodule update - # Build everything - mkdir build - cd ${BASE_DIR}/ExaCMech/build - # GPU build - cmake ../ -DCMAKE_INSTALL_PREFIX=../install_dir/ \ - -DRAJA_DIR=${BASE_DIR}/raja/install_dir/lib/cmake/raja/ \ - -DENABLE_OPENMP=OFF \ - -DENABLE_TESTS=OFF \ - -DENABLE_MINIAPPS=OFF \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=OFF - make -j 4 - make install -else - - echo " ExaCMech already built " - -fi - -# Now to build our MFEM dependencies -# First let's install Hypre v2.20.0 -cd ${BASE_DIR} -if [ ! -d "hypre" ]; then - - git clone https://github.com/hypre-space/hypre.git --branch v2.30.0 --single-branch - cd ${BASE_DIR}/hypre/src - # Based on their install instructions - # This should work on most systems - # Hypre's default suggestions of just using configure don't always work - ./configure CC=mpicc CXX=mpicxx FC=mpif90 - make -j 4 - make install - cd hypre - HYPRE_DIR="$(pwd)" - -else - - echo " hypre already built " - HYPRE_DIR=${BASE_DIR}/hypre/src/hypre - -fi - -# Now to install metis-5.1.0 -# It appears that there are some minor differences in performance between metis-4 and metis-5 -# If you'd like to install metis-4 instead here's the commands needed -# uncomment the below and then comment the metis-5 commands -# cd ${BASE_DIR} -# curl -o metis-4.0.3.tar.gz http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/OLD/metis-4.0.3.tar.gz -# tar -xzf metis-4.0.3.tar.gz -# rm metis-4.0.3.tar.gz -# cd metis-4.0.3 -# make -# METIS_DIR="$(pwd)" -# metis-5 install down below -cd ${BASE_DIR} - -if [ ! -d "metis-5.1.0" ]; then - - curl -o metis-5.1.0.tar.gz https://mfem.github.io/tpls/metis-5.1.0.tar.gz - tar -xzf metis-5.1.0.tar.gz - rm metis-5.1.0.tar.gz - cd metis-5.1.0 - mkdir install_dir - make config prefix=${BASE_DIR}/metis-5.1.0/install_dir/ - make -j 4 - make install - cd ${BASE_DIR}/metis-5.1.0/install_dir/ - METIS_DIR="$(pwd)" -else - - echo " metis-5.1.0 already built " - METIS_DIR=${BASE_DIR}/metis-5.1.0/install_dir/ - -fi - -# If you want anyother MFEM options installed like Conduit, ADIOS2, or etc. install them now -# We can now install MFEM with relevant data for ExaConstit - -cd ${BASE_DIR} - -if [ ! -d "mfem" ]; then - - git clone https://github.com/rcarson3/mfem.git --branch exaconstit-dev --single-branch - cd ${BASE_DIR}/mfem/ - mkdir build - cd ${BASE_DIR}/mfem/build/ - # All the options - cmake ../ -DMFEM_USE_MPI=ON -DMFEM_USE_SIMD=OFF\ - -DMETIS_DIR=${METIS_DIR} \ - -DHYPRE_DIR=${HYPRE_DIR} \ - -DCMAKE_INSTALL_PREFIX=../install_dir/ \ - -DMFEM_USE_OPENMP=OFF \ - -DMFEM_USE_RAJA=ON -DRAJA_DIR=${BASE_DIR}/raja/install_dir/ \ - -DCMAKE_BUILD_TYPE=Release - # The below are the relevant lines needed for ADIOS2 and conduit. You'll want to put them - # before the -DCMAKE_BUILD_TYPE call - # -DMFEM_USE_ADIOS2=ON -DADIOS2_DIR=${ADIOS2_DIR} \ - # -DMFEM_USE_CONDUIT=ON -DConduit_REQUIRED_PACKAGES=HDF5 -DCONDUIT_DIR=${CONDUIT_DIR} \ - # -DHDF5_ROOT:PATH=${HDF5_DIR} \ - make -j 4 - make install - -else - - echo " MFEM already built " - -fi - -#We can finally install ExaConstit -cd ${BASE_DIR} - -if [ ! -d "ExaConstit" ]; then - - git clone https://github.com/LLNL/ExaConstit.git - cd ${BASE_DIR}/ExaConstit/ - # Instantiate all the submodules - git submodule init - git submodule update - # Build everything - mkdir build - cd ${BASE_DIR}/ExaConstit/build/ - - cmake ../ -DENABLE_MPI=ON -DENABLE_FORTRAN=ON \ - -DPYTHON_EXECUTABLE=${PYTHON_EXE} \ - -DMFEM_DIR=${BASE_DIR}/mfem/install_dir/lib/cmake/mfem/ \ - -DECMECH_DIR=${BASE_DIR}/ExaCMech/install_dir/ \ - -DRAJA_DIR=${BASE_DIR}/raja/install_dir/lib/cmake/raja/ \ - -DSNLS_DIR=${BASE_DIR}/ExaCMech/install_dir/ \ - -DENABLE_SNLS_V03=ON \ - -DCMAKE_BUILD_TYPE=Release \ - -DENABLE_TESTS=ON - # Sometimes the cmake systems can be a bit difficult and not properly find the MFEM installed location - # using the above. If that's the case the below should work: - # -DMFEM_DIR=${BASE_DIR}/mfem/install_dir/ \ - - make -j 4 - # Check and make sure everything installed correctly by running the test suite - make test - -else - - echo " ExaConstit already built " - -fi - -# ExaConstit is now installed diff --git a/scripts/meshing/mesh_generator.cpp b/scripts/meshing/mesh_generator.cpp index 52ab16d..507ae81 100644 --- a/scripts/meshing/mesh_generator.cpp +++ b/scripts/meshing/mesh_generator.cpp @@ -160,7 +160,8 @@ void setElementGrainIDs(Mesh *mesh, const Vector grainMap, int ncols, int offset // loop over elements for (int i = 0; iGetNE(); ++i) { - mesh->SetAttribute(i, data[ncols * i + offset]); + const int grainID = static_cast(data[ncols * i + offset]); + mesh->SetAttribute(i, grainID); } return; diff --git a/scripts/postprocessing/adios2_example.py b/scripts/postprocessing/adios2_example.py index 36d4ef5..71d7ba2 100755 --- a/scripts/postprocessing/adios2_example.py +++ b/scripts/postprocessing/adios2_example.py @@ -88,7 +88,7 @@ istep = 0 for fstep in fh: for i in range(nranks): - arr = fstep.read('ElementVolume', block_id=i) + arr = fstep.read('Element Volumes', block_id=i) ev[index[i, 0]:index[i, 1], istep] = arr[con1d[i]] istep = istep + 1 @@ -109,12 +109,12 @@ # Note this method requires us to define start and count. We can't just # set step_start and step_count. Also, note the transpose at the end to work # in the same way as the previous method - arr = fh.read('HydrostaticStress', start=[0], count=[isize], step_start=0, step_count=steps-1, block_id=i).T + arr = fh.read('Hydrostatic Stress', start=[0], count=[isize], step_start=0, step_count=steps-1, block_id=i).T hss[index[i, 0]:index[i, 1], :] = arr[con1d[i], :] - arr = fh.read('VonMisesStress', start=[0], count=[isize], step_start=0, step_count=steps-1, block_id=i).T + arr = fh.read('Von Mises Stress', start=[0], count=[isize], step_start=0, step_count=steps-1, block_id=i).T vm[index[i, 0]:index[i, 1], :] = arr[con1d[i], :] - arr1 = fstep.read('LatticeOrientation', start=[0, 0], count=[isize, 4], step_start=0, step_count=steps-1, block_id=i) + arr1 = fstep.read('Crystal Orientations', start=[0, 0], count=[isize, 4], step_start=0, step_count=steps-1, block_id=i) quats[:, index[i, 0]:index[i, 1], :] = np.swapaxes(arr1[:, con1d[i], :], 0, 2) #%% # Always make sure to close the file when you're finished loading data from it diff --git a/scripts/postprocessing/adios2_extraction.py b/scripts/postprocessing/adios2_extraction.py index d3f7987..52eda81 100644 --- a/scripts/postprocessing/adios2_extraction.py +++ b/scripts/postprocessing/adios2_extraction.py @@ -55,12 +55,12 @@ # different variables are stored in different ways - not all variables are supported by this script # this script should work for any variables that are saved off for every element - some examples of working variables are given below vars_out = [ - 'DpEff' , - 'ElementVolume' , - 'LatticeOrientation' , - 'ShearRate' , - 'Stress' , - 'XtalElasticStrain' + 'Equivalent Plastic Strain Rate' , + 'Element Volumes' , + 'Crystal Orientations' , + 'Shearing Rate' , + 'Cauchy Stress' , + 'Elastic Strains' ] #!!! #%% Open ADIOS2 file and explore variables. (USER INPUTS HERE) diff --git a/scripts/postprocessing/macro_stress_strain_plot.py b/scripts/postprocessing/macro_stress_strain_plot.py index 2269d56..cbd55c6 100755 --- a/scripts/postprocessing/macro_stress_strain_plot.py +++ b/scripts/postprocessing/macro_stress_strain_plot.py @@ -23,42 +23,50 @@ fig, ax = plt.subplots(1) -#number of time steps nsteps = 40 - # uncomment the below when the fileLoc is valid #data = np.loadtxt(fileLoc+'avg_stress.txt', comments='%') # only here to have something that'll plot -data = np.ones((nsteps, 6)) +data = np.ones((nsteps, 8)) +# First two columns are time and volume +# Next 6 columns are the Cauchy stress in voigt notation +sig = data[:,4] +vol = data[:,1] +time = data[:,0] +nsteps = data.shape[0] + 1 -epsdot = 1e-3 +sig = np.r_[0, sig] +vol = np.r_[1, vol] +time = np.r_[0, time] +# If setup for it you can also request either the eulerian strain or deformation gradient +# in which case most of the below can be ignored -sig = data[:,2] -# uncomment the below when the fileLoc is valid -#time = np.loadtxt(fileLoc+'custom_dt.txt') +epsdot = 1e-3 # only here to have something that'll plot -time = np.ones(nsteps) - eps = np.zeros(nsteps) for i in range(0, nsteps): - dtime = time[i] + if (i == 0): + dtime = time[i] + else: + dtime = time[i] - time[i - 1] + # Stress is not always monotonically increasing so this is not always the + # best assumption if sig[i] - sig[i - 1] >= 0: eps[i] = eps[i - 1] + epsdot * dtime else: eps[i] = eps[i - 1] - epsdot * dtime -ax.plot(eps, sig, 'r') - +# For true strain the np.log(1 + eps) will provide the correct conversion +ax.plot(np.log(1.0 + eps), sig, 'r') ax.grid() -# change this to fit your data +# change this to fit your data # ax.axis([0, 0.01, 0, 0.3]) -ax.set_ylabel('Macroscopic engineering stress [GPa]') -ax.set_xlabel('Macroscopic engineering strain [-]') +ax.set_ylabel('Macroscopic true stress [GPa]') +ax.set_xlabel('Macroscopic true strain [-]') -plt.close() fig.show() plt.show() \ No newline at end of file diff --git a/scripts/postprocessing/xtal_light_up/light_up_py/fiber_calcs_rank.py b/scripts/postprocessing/xtal_light_up/light_up_py/fiber_calcs_rank.py index b0075eb..ec820d6 100644 --- a/scripts/postprocessing/xtal_light_up/light_up_py/fiber_calcs_rank.py +++ b/scripts/postprocessing/xtal_light_up/light_up_py/fiber_calcs_rank.py @@ -118,7 +118,7 @@ def fiber_calc_ranks(args): s_dir = np.asarray([0.0,0.0,1.0]) - top = fh.read('ElementVolume' , block_id = 0) + top = fh.read('Element Volumes' , block_id = 0) # If we want per element quantities then uncomment below block # elem_vols = np.empty((steps, conshape[0])) @@ -142,20 +142,20 @@ def fiber_calc_ranks(args): isize = con1d[ii].shape[0] * conshape[1] # Read all of the data in - ev_local = np.ascontiguousarray(fh.read('ElementVolume', start = [0], count = [isize], step_selection = [0 , steps] , block_id = ii).reshape((steps, isize))[:, con1d[ii]]) + ev_local = np.ascontiguousarray(fh.read('Element Volumes', start = [0], count = [isize], step_selection = [0 , steps] , block_id = ii).reshape((steps, isize))[:, con1d[ii]]) # Provide info later related to RVE size so can see how many elements are # actually used in the fiber calculations total_volume += np.sum(ev_local, axis=1) - xtal_oris_local = arr = np.ascontiguousarray(fh.read('LatticeOrientation', start = [0, 0], count = [isize, 4], step_selection = [0 , steps] , block_id = ii).reshape((steps, isize, 4))[:, con1d[ii], :]) + xtal_oris_local = arr = np.ascontiguousarray(fh.read('Crystal Orientations', start = [0, 0], count = [isize, 4], step_selection = [0 , steps] , block_id = ii).reshape((steps, isize, 4))[:, con1d[ii], :]) - elas_strain_local = np.ascontiguousarray(fh.read('XtalElasticStrain', start = [0, 0], count = [isize, 6], step_selection = [0 , steps] , block_id = ii).reshape((steps, isize, 6))[:, con1d[ii], :]) + elas_strain_local = np.ascontiguousarray(fh.read('Elastic Strains', start = [0, 0], count = [isize, 6], step_selection = [0 , steps] , block_id = ii).reshape((steps, isize, 6))[:, con1d[ii], :]) - stress_local = np.ascontiguousarray(fh.read('Stress', start = [0, 0], count = [isize, 6], step_selection = [0 , steps - 1] , block_id = ii).reshape((steps - 1, isize, 6))[:, con1d[ii], :]) + stress_local = np.ascontiguousarray(fh.read('Cauchy Stress', start = [0, 0], count = [isize, 6], step_selection = [0 , steps - 1] , block_id = ii).reshape((steps - 1, isize, 6))[:, con1d[ii], :]) - top = fh.read('ShearRate' , block_id = 0) - gdots_local = np.ascontiguousarray(fh.read('ShearRate', start = [0, 0], count = [isize, top.shape[1]], step_selection = [0 , steps - 1] , block_id = ii).reshape((steps - 1, isize, top.shape[1]))[:, con1d[ii], :]) + top = fh.read('Shearing Rate' , block_id = 0) + gdots_local = np.ascontiguousarray(fh.read('Shearing Rate', start = [0, 0], count = [isize, top.shape[1]], step_selection = [0 , steps - 1] , block_id = ii).reshape((steps - 1, isize, top.shape[1]))[:, con1d[ii], :]) in_fibers_local = np.zeros((hkl.shape[0], steps, elas_strain_local.shape[1]), dtype=bool) @@ -164,7 +164,8 @@ def fiber_calc_ranks(args): ev_local1 = np.ascontiguousarray(ev_local[1:steps,:]) # All of our local calculations - xlup.strain_lattice2sample(xtal_oris_local, elas_strain_local) + # We're already in the sample frame as ExaConstit as of v0.9 automatically converts it for us + # xlup.strain_lattice2sample(xtal_oris_local, elas_strain_local) xlup.calc_lattice_strains(elas_strain_local, s_dir, ev_local, in_fibers_local, lattice_strains, lattice_vols, True) xlup.calc_directional_stiffness_lattice_fiber(stress_local, elas_strain_local[1:steps,:,:], lattice_dir_stiff, ev_local1, in_fiber_local1, True) xlup.calc_taylor_factors_lattice_fiber(gdots_local, lattice_tay_fact, lattice_eps_rate, ev_local1, in_fiber_local1, True) @@ -222,7 +223,7 @@ def fiber_calc_ranks(args): # s.write("Strains", strains, shape=strains.shape, start=[0,0,0], count=strains.shape) # s.write("DirectionalModulus", direct_stiffness, shape=direct_stiffness.shape, start=[0,0], count=direct_stiffness.shape) # s.write("TaylorFactor", tay_fact, shape=tay_fact.shape, start=[0,0], count=tay_fact.shape) - # s.write("DpEff", eps_rate, shape=eps_rate.shape, start=[0,0], count=eps_rate.shape) + # s.write("EquivalentPlasticStrainRate", eps_rate, shape=eps_rate.shape, start=[0,0], count=eps_rate.shape) tf_total = time.time() print('%.3f seconds to process %s.' % (tf_total - ts_total, "all items")) diff --git a/src/BCData.cpp b/src/BCData.cpp deleted file mode 100644 index 02cc510..0000000 --- a/src/BCData.cpp +++ /dev/null @@ -1,118 +0,0 @@ - -#include "mfem.hpp" -#include "BCData.hpp" - -using namespace mfem; - -BCData::BCData() -{ - // TODO constructor stub -} - -BCData::~BCData() -{ - // TODO destructor stub -} - -void BCData::setDirBCs(Vector& y) -{ - // When doing the velocity based methods we only - // need to do the below. - y = 0.0; - y[0] = essVel[0] * scale[0]; - y[1] = essVel[1] * scale[1]; - y[2] = essVel[2] * scale[2]; -} - -void BCData::setScales() -{ - switch (compID) { - case 7: - scale[0] = 1.0; - scale[1] = 1.0; - scale[2] = 1.0; - break; - case 1: - scale[0] = 1.0; - scale[1] = 0.0; - scale[2] = 0.0; - break; - case 2: - scale[0] = 0.0; - scale[1] = 1.0; - scale[2] = 0.0; - break; - case 3: - scale[0] = 0.0; - scale[1] = 0.0; - scale[2] = 1.0; - break; - case 4: - scale[0] = 1.0; - scale[1] = 1.0; - scale[2] = 0.0; - break; - case 5: - scale[0] = 0.0; - scale[1] = 1.0; - scale[2] = 1.0; - break; - case 6: - scale[0] = 1.0; - scale[1] = 0.0; - scale[2] = 1.0; - break; - case 0: - scale[0] = 0.0; - scale[1] = 0.0; - scale[2] = 0.0; - break; - } -} - -void BCData::getComponents(int id, Array &component) -{ - switch (id) { - case 0: - component[0] = false; - component[1] = false; - component[2] = false; - break; - - case 1: - component[0] = true; - component[1] = false; - component[2] = false; - break; - case 2: - component[0] = false; - component[1] = true; - component[2] = false; - break; - case 3: - component[0] = false; - component[1] = false; - component[2] = true; - break; - case 4: - component[0] = true; - component[1] = true; - component[2] = false; - break; - case 5: - component[0] = false; - component[1] = true; - component[2] = true; - break; - case 6: - component[0] = true; - component[1] = false; - component[2] = true; - break; - case 7: - component[0] = true; - component[1] = true; - component[2] = true; - break; - } -} diff --git a/src/BCData.hpp b/src/BCData.hpp deleted file mode 100644 index 360a40b..0000000 --- a/src/BCData.hpp +++ /dev/null @@ -1,26 +0,0 @@ - -#ifndef BCDATA -#define BCDATA - -#include "mfem.hpp" -#include "mfem/linalg/vector.hpp" -#include - -class BCData -{ - public: - BCData(); - ~BCData(); - - // scales for nonzero Dirichlet BCs - double essVel[3]; - double scale[3]; - int compID; - - void setDirBCs(mfem::Vector& y); - - void setScales(); - - static void getComponents(int id, mfem::Array &component); -}; -#endif diff --git a/src/BCManager.cpp b/src/BCManager.cpp deleted file mode 100644 index 1151614..0000000 --- a/src/BCManager.cpp +++ /dev/null @@ -1,145 +0,0 @@ - - -#include "mfem.hpp" -#include "BCManager.hpp" -#include - -using namespace mfem; - - -void BCManager::updateBCData(std::unordered_map> & ess_bdr, - mfem::Array2D & scale, - mfem::Vector & vgrad, - std::unordered_map> & component) -{ - ess_bdr["total"] = 0; - scale = 0.0; - - auto ess_comp = map_ess_comp["total"].find(step)->second; - auto ess_id = map_ess_id["total"].find(step)->second; - - Array cmp_row; - cmp_row.SetSize(3); - - component["total"] = false; - cmp_row = false; - - for (std::uint32_t i = 0; i < ess_id.size(); ++i) { - // set the active boundary attributes - if (ess_comp[i] != 0) { - const int bcID = ess_id[i] - 1; - ess_bdr["total"][bcID] = 1; - BCData::getComponents(std::abs(ess_comp[i]), cmp_row); - - component["total"](bcID, 0) = cmp_row[0]; - component["total"](bcID, 1) = cmp_row[1]; - component["total"](bcID, 2) = cmp_row[2]; - } - } - - updateBCData(ess_bdr["ess_vel"], scale, component["ess_vel"]); - updateBCData(ess_bdr["ess_vgrad"], vgrad, component["ess_vgrad"]); -} - -void BCManager::updateBCData(mfem::Array & ess_bdr, mfem::Array2D & scale, mfem::Array2D & component) -{ - m_bcInstances.clear(); - ess_bdr = 0; - scale = 0.0; - - // The size here is set explicitly - component.SetSize(ess_bdr.Size(), 3); - Array cmp_row; - cmp_row.SetSize(3); - - component = false; - cmp_row = false; - - if (map_ess_vel.find(step) == map_ess_vel.end()) - { - return; - } - - auto ess_vel = map_ess_vel.find(step)->second; - auto ess_comp = map_ess_comp["ess_vel"].find(step)->second; - auto ess_id = map_ess_id["ess_vel"].find(step)->second; - - for (std::uint32_t i = 0; i < ess_id.size(); ++i) { - // set the active boundary attributes - if (ess_comp[i] != 0) { - // set the boundary condition id based on the attribute id - int bcID = ess_id[i]; - - // instantiate a boundary condition manager instance and - // create a BCData object - BCData & bc = this->CreateBCs(bcID); - - // set the velocity component values - bc.essVel[0] = ess_vel[3 * i]; - bc.essVel[1] = ess_vel[3 * i + 1]; - bc.essVel[2] = ess_vel[3 * i + 2]; - bc.compID = ess_comp[i]; - - // set the boundary condition scales - bc.setScales(); - - scale(bcID - 1, 0) = bc.scale[0]; - scale(bcID - 1, 1) = bc.scale[1]; - scale(bcID - 1, 2) = bc.scale[2]; - ess_bdr[bcID - 1] = 1; - } - } - - for (std::uint32_t i = 0; i < ess_id.size(); ++i) { - // set the active boundary attributes - if (ess_comp[i] != 0) { - const int bcID = ess_id[i] - 1; - ess_bdr[bcID] = 1; - BCData::getComponents(ess_comp[i], cmp_row); - component(bcID, 0) = cmp_row[0]; - component(bcID, 1) = cmp_row[1]; - component(bcID, 2) = cmp_row[2]; - } - } -} - -void BCManager::updateBCData(mfem::Array & ess_bdr, mfem::Vector & vgrad, mfem::Array2D & component) -{ - ess_bdr = 0; - vgrad.HostReadWrite(); - vgrad = 0.0; - - // The size here is set explicitly - component.SetSize(ess_bdr.Size(), 3); - Array cmp_row; - cmp_row.SetSize(3); - - component = false; - cmp_row = false; - - if (map_ess_vgrad.find(step) == map_ess_vgrad.end()) - { - return; - } - - auto ess_vgrad = map_ess_vgrad.find(step)->second; - auto ess_comp = map_ess_comp["ess_vgrad"].find(step)->second; - auto ess_id = map_ess_id["ess_vgrad"].find(step)->second; - - for (std::uint32_t i = 0; i < ess_vgrad.size(); ++i) { - vgrad(i) = ess_vgrad.at(i); - } - - for (std::uint32_t i = 0; i < ess_id.size(); ++i) { - // set the active boundary attributes - if (ess_comp[i] != 0) { - const int bcID = ess_id[i] - 1; - ess_bdr[bcID] = 1; - BCData::getComponents(ess_comp[i], cmp_row); - component(bcID, 0) = cmp_row[0]; - component(bcID, 1) = cmp_row[1]; - component(bcID, 2) = cmp_row[2]; - } - } -} - diff --git a/src/BCManager.hpp b/src/BCManager.hpp deleted file mode 100644 index f54e575..0000000 --- a/src/BCManager.hpp +++ /dev/null @@ -1,93 +0,0 @@ - -#ifndef BCMANAGER -#define BCMANAGER - -#include "BCData.hpp" -#include "option_parser.hpp" - -// C/C++ includes -#include // for std::unordered_map -#include -#include -#include - - -class BCManager -{ - public: - static BCManager & getInstance() - { - static BCManager bcManager; - return bcManager; - } - - void init(const std::vector &uStep, - const std::unordered_map> &ess_vel, - const std::unordered_map> &ess_vgrad, - const map_of_imap &ess_comp, - const map_of_imap &ess_id) { - std::call_once(init_flag, [&](){ - updateStep = uStep; - map_ess_vel = ess_vel; - map_ess_vgrad = ess_vgrad; - map_ess_comp = ess_comp; - map_ess_id = ess_id; - }); - } - - BCData & GetBCInstance(int bcID) - { - return m_bcInstances.find(bcID)->second; - } - - const BCData & GetBCInstance(int bcID) const - { - return m_bcInstances.find(bcID)->second; - } - - BCData & CreateBCs(int bcID) - { - return m_bcInstances[bcID]; - } - - std::unordered_map&GetBCInstances() - { - return m_bcInstances; - } - - void updateBCData(std::unordered_map> & ess_bdr, - mfem::Array2D & scale, - mfem::Vector & vgrad, - std::unordered_map> & component); - - bool getUpdateStep(int step_) - { - if(std::find(updateStep.begin(), updateStep.end(), step_) != updateStep.end()) { - step = step_; - return true; - } - else { - return false; - } - } - private: - BCManager() {} - BCManager(const BCManager&) = delete; - BCManager& operator=(const BCManager &) = delete; - BCManager(BCManager &&) = delete; - BCManager & operator=(BCManager &&) = delete; - - void updateBCData(mfem::Array & ess_bdr, mfem::Vector & vgrad, mfem::Array2D & component); - void updateBCData(mfem::Array & ess_bdr, mfem::Array2D & scale, mfem::Array2D & component); - - std::once_flag init_flag; - int step = 0; - std::unordered_map m_bcInstances; - std::vector updateStep; - std::unordered_map> map_ess_vel; - std::unordered_map> map_ess_vgrad; - map_of_imap map_ess_comp; - map_of_imap map_ess_id; -}; - -#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3517b24..24e830a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,48 +3,79 @@ #------------------------------------------------------------------------------ set(EXACONSTIT_HEADERS ${HEADER_INCLUDE_DIR}/ExaConstit_Version.h - BCData.hpp - BCManager.hpp - mechanics_model.hpp - mechanics_integrators.hpp - mechanics_ecmech.hpp - mechanics_kernels.hpp - mechanics_lightup.hpp - mechanics_log.hpp - mechanics_umat.hpp - mechanics_operator_ext.hpp - mechanics_operator.hpp - mechanics_solver.hpp system_driver.hpp - option_types.hpp - option_parser.hpp - userumat.h - ./TOML_Reader/toml.hpp + boundary_conditions/BCData.hpp + boundary_conditions/BCManager.hpp + fem_operators/mechanics_integrators.hpp + fem_operators/mechanics_operator_ext.hpp + fem_operators/mechanics_operator.hpp + mfem_expt/partial_qspace.hpp + mfem_expt/partial_qfunc.hpp + models/mechanics_model.hpp + models/mechanics_ecmech.hpp + models/mechanics_multi_model.hpp + models/mechanics_umat.hpp + options/option_parser_v2.hpp + postprocessing/projection_class.hpp + postprocessing/postprocessing_driver.hpp + postprocessing/mechanics_lightup.hpp + sim_state/simulation_state.hpp + solvers/mechanics_solver.hpp + utilities/dynamic_function_loader.hpp + utilities/mechanics_kernels.hpp + utilities/mechanics_log.hpp + utilities/assembly_ops.hpp + utilities/rotations.hpp + utilities/strain_measures.hpp + utilities/unified_logger.hpp + umats/userumat.h + umats/unified_umat_loader.hpp + TOML_Reader/toml.hpp ) set(EXACONSTIT_SOURCES - BCData.cpp - BCManager.cpp - mechanics_model.cpp - mechanics_integrators.cpp - mechanics_ecmech.cpp - mechanics_kernels.cpp - mechanics_umat.cpp - mechanics_operator_ext.cpp - mechanics_operator.cpp - mechanics_solver.cpp system_driver.cpp - option_parser.cpp - ./umat_tests/userumat.cxx + boundary_conditions/BCData.cpp + boundary_conditions/BCManager.cpp + fem_operators/mechanics_integrators.cpp + fem_operators/mechanics_operator_ext.cpp + fem_operators/mechanics_operator.cpp + mfem_expt/partial_qspace.cpp + mfem_expt/partial_qfunc.cpp + models/mechanics_model.cpp + models/mechanics_ecmech.cpp + models/mechanics_umat.cpp + models/mechanics_multi_model.cpp + options/option_parser_v2.cpp + options/option_boundary_conditions.cpp + options/option_enum.cpp + options/option_material.cpp + options/option_mesh.cpp + options/option_post_processing.cpp + options/option_solvers.cpp + options/option_time.cpp + postprocessing/postprocessing_driver.cpp + postprocessing/projection_class.cpp + postprocessing/mechanics_lightup.cpp + sim_state/simulation_state.cpp + solvers/mechanics_solver.cpp + utilities/mechanics_kernels.cpp + utilities/unified_logger.cpp ) if (ENABLE_FORTRAN) - list(APPEND EXACONSTIT_SOURCES ./umat_tests/umat.f) + list(APPEND EXACONSTIT_SOURCES ./umats/umat.f) else() - list(APPEND EXACONSTIT_SOURCES ./umat_tests/umat.cxx) + list(APPEND EXACONSTIT_SOURCES ./umats/umat.cxx) endif() - + +set(DYNAMIC_LOADING_LIBS) + +# Windows uses kernel32 automatically +if(UNIX) + list(APPEND DYNAMIC_LOADING_LIBS ${CMAKE_DL_LIBS}) +endif() #------------------------------------------------------------------------------ # Dependencies #------------------------------------------------------------------------------ @@ -81,6 +112,15 @@ if(ENABLE_CALIPER) list(APPEND EXACONSTIT_DEPENDS caliper) endif() +if(UNIX AND NOT APPLE AND TARGET Threads::Threads) + # Check if we determined explicit linking is needed + if(EXACONSTIT_THREADS_EXPLICIT_LINK) + list(APPEND EXACONSTIT_DEPENDS Threads::Threads) + endif() +endif() + +list(APPEND EXACONSTIT_DEPENDS ${DYNAMIC_LOADING_LIBS}) + message("-- EXACONSTIT_DEPENDS: ${EXACONSTIT_DEPENDS}") #------------------------------------------------------------------------------ diff --git a/src/boundary_conditions/BCData.cpp b/src/boundary_conditions/BCData.cpp new file mode 100644 index 0000000..3714bc1 --- /dev/null +++ b/src/boundary_conditions/BCData.cpp @@ -0,0 +1,111 @@ +#include "boundary_conditions/BCData.hpp" + +#include "mfem.hpp" + +BCData::BCData() { + // TODO constructor stub +} + +BCData::~BCData() { + // TODO destructor stub +} + +void BCData::SetDirBCs(mfem::Vector& y) { + // When doing the velocity based methods we only + // need to do the below. + y = 0.0; + y[0] = ess_vel[0] * scale[0]; + y[1] = ess_vel[1] * scale[1]; + y[2] = ess_vel[2] * scale[2]; +} + +void BCData::SetScales() { + switch (comp_id) { + case 7: + scale[0] = 1.0; + scale[1] = 1.0; + scale[2] = 1.0; + break; + case 1: + scale[0] = 1.0; + scale[1] = 0.0; + scale[2] = 0.0; + break; + case 2: + scale[0] = 0.0; + scale[1] = 1.0; + scale[2] = 0.0; + break; + case 3: + scale[0] = 0.0; + scale[1] = 0.0; + scale[2] = 1.0; + break; + case 4: + scale[0] = 1.0; + scale[1] = 1.0; + scale[2] = 0.0; + break; + case 5: + scale[0] = 0.0; + scale[1] = 1.0; + scale[2] = 1.0; + break; + case 6: + scale[0] = 1.0; + scale[1] = 0.0; + scale[2] = 1.0; + break; + case 0: + scale[0] = 0.0; + scale[1] = 0.0; + scale[2] = 0.0; + break; + } +} + +void BCData::GetComponents(int id, mfem::Array& component) { + switch (id) { + case 0: + component[0] = false; + component[1] = false; + component[2] = false; + break; + + case 1: + component[0] = true; + component[1] = false; + component[2] = false; + break; + case 2: + component[0] = false; + component[1] = true; + component[2] = false; + break; + case 3: + component[0] = false; + component[1] = false; + component[2] = true; + break; + case 4: + component[0] = true; + component[1] = true; + component[2] = false; + break; + case 5: + component[0] = false; + component[1] = true; + component[2] = true; + break; + case 6: + component[0] = true; + component[1] = false; + component[2] = true; + break; + case 7: + component[0] = true; + component[1] = true; + component[2] = true; + break; + } +} diff --git a/src/boundary_conditions/BCData.hpp b/src/boundary_conditions/BCData.hpp new file mode 100644 index 0000000..075e46b --- /dev/null +++ b/src/boundary_conditions/BCData.hpp @@ -0,0 +1,106 @@ + +#ifndef BCDATA +#define BCDATA + +#include "mfem.hpp" +#include "mfem/linalg/vector.hpp" + +#include + +/** + * @brief Individual boundary condition data container and processor + * + * @details This class stores and processes data for a single boundary condition instance. + * It handles the application of Dirichlet boundary conditions for velocity-based formulations + * and manages component-wise scaling for different constraint types. + * + * The class supports component-wise boundary conditions where different velocity components + * can be constrained independently using a component ID system: + * - 0: No constraints + * - 1: X-component only + * - 2: Y-component only + * - 3: Z-component only + * - 4: X and Y components + * - 5: Y and Z components + * - 6: X and Z components + * - 7: All components (X, Y, Z) + */ +class BCData { +public: + /** + * @brief Default constructor + * + * @details Initializes a BCData object with default values. Currently a stub + * implementation that should be expanded based on initialization requirements. + */ + BCData(); + + /** + * @brief Destructor + * + * @details Cleans up BCData resources. Currently a stub implementation. + */ + ~BCData(); + + /** @brief Essential velocity values for each component [x, y, z] */ + double ess_vel[3]; + + /** @brief Scaling factors for each velocity component [x, y, z] */ + double scale[3]; + + /** @brief Component ID indicating which velocity components are constrained */ + int comp_id; + + /** + * @brief Apply Dirichlet boundary conditions to a velocity vector + * + * @param y Output velocity vector where boundary conditions will be applied + * + * @details Sets the velocity vector components based on the essential velocity values + * and their corresponding scaling factors. For velocity-based methods, this function: + * - Initializes the output vector to zero + * - Applies scaled essential velocities: y[i] = ess_vel[i] * scale[i] + * + * This is used during the assembly process to enforce velocity boundary conditions. + */ + void SetDirBCs(mfem::Vector& y); + + /** + * @brief Set scaling factors based on component ID + * + * @details Configures the scale array based on the comp_id value to determine which + * velocity components should be constrained. The scaling pattern is: + * - comp_id = 0: No scaling (all zeros) + * - comp_id = 1: X-component only (1,0,0) + * - comp_id = 2: Y-component only (0,1,0) + * - comp_id = 3: Z-component only (0,0,1) + * - comp_id = 4: X,Y components (1,1,0) + * - comp_id = 5: Y,Z components (0,1,1) + * - comp_id = 6: X,Z components (1,0,1) + * - comp_id = 7: All components (1,1,1) + */ + void SetScales(); + + /** + * @brief Static utility to decode component ID into boolean flags + * + * @param id Component ID to decode + * @param component Output array of boolean flags for each component [x, y, z] + * + * @details Converts a component ID integer into a boolean array indicating which + * velocity components are active. This is used throughout the boundary condition + * system to determine which degrees of freedom should be constrained. + * + * The mapping follows the same pattern as SetScales(): + * - id = 0: (false, false, false) + * - id = 1: (true, false, false) + * - id = 2: (false, true, false) + * - id = 3: (false, false, true) + * - id = 4: (true, true, false) + * - id = 5: (false, true, true) + * - id = 6: (true, false, true) + * - id = 7: (true, true, true) + */ + static void GetComponents(int id, mfem::Array& component); +}; +#endif diff --git a/src/boundary_conditions/BCManager.cpp b/src/boundary_conditions/BCManager.cpp new file mode 100644 index 0000000..5f0e7db --- /dev/null +++ b/src/boundary_conditions/BCManager.cpp @@ -0,0 +1,143 @@ + + +#include "boundary_conditions/BCManager.hpp" + +#include "mfem.hpp" + +#include + +void BCManager::UpdateBCData(std::unordered_map>& ess_bdr, + mfem::Array2D& scale, + mfem::Vector& vgrad, + std::unordered_map>& component) { + ess_bdr["total"] = 0; + scale = 0.0; + + auto ess_comp = map_ess_comp["total"].find(step)->second; + auto ess_id = map_ess_id["total"].find(step)->second; + + mfem::Array cmp_row; + cmp_row.SetSize(3); + + component["total"] = false; + cmp_row = false; + + for (size_t i = 0; i < ess_id.size(); ++i) { + // set the active boundary attributes + if (ess_comp[i] != 0) { + const int bcID = ess_id[i] - 1; + ess_bdr["total"][bcID] = 1; + BCData::GetComponents(std::abs(ess_comp[i]), cmp_row); + + component["total"](bcID, 0) = cmp_row[0]; + component["total"](bcID, 1) = cmp_row[1]; + component["total"](bcID, 2) = cmp_row[2]; + } + } + + UpdateBCData(ess_bdr["ess_vel"], scale, component["ess_vel"]); + UpdateBCData(ess_bdr["ess_vgrad"], vgrad, component["ess_vgrad"]); +} + +void BCManager::UpdateBCData(mfem::Array& ess_bdr, + mfem::Array2D& scale, + mfem::Array2D& component) { + m_bc_instances.clear(); + ess_bdr = 0; + scale = 0.0; + + // The size here is set explicitly + component.SetSize(ess_bdr.Size(), 3); + mfem::Array cmp_row; + cmp_row.SetSize(3); + + component = false; + cmp_row = false; + + if (map_ess_vel.find(step) == map_ess_vel.end()) { + return; + } + + auto ess_vel = map_ess_vel.find(step)->second; + auto ess_comp = map_ess_comp["ess_vel"].find(step)->second; + auto ess_id = map_ess_id["ess_vel"].find(step)->second; + + for (size_t i = 0; i < ess_id.size(); ++i) { + // set the active boundary attributes + if (ess_comp[i] != 0) { + // set the boundary condition id based on the attribute id + int bcID = ess_id[i]; + + // instantiate a boundary condition manager instance and + // create a BCData object + BCData& bc = this->CreateBCs(bcID); + + // set the velocity component values + bc.ess_vel[0] = ess_vel[3 * i]; + bc.ess_vel[1] = ess_vel[3 * i + 1]; + bc.ess_vel[2] = ess_vel[3 * i + 2]; + bc.comp_id = ess_comp[i]; + + // set the boundary condition scales + bc.SetScales(); + + scale(bcID - 1, 0) = bc.scale[0]; + scale(bcID - 1, 1) = bc.scale[1]; + scale(bcID - 1, 2) = bc.scale[2]; + ess_bdr[bcID - 1] = 1; + } + } + + for (size_t i = 0; i < ess_id.size(); ++i) { + // set the active boundary attributes + if (ess_comp[i] != 0) { + const int bcID = ess_id[i] - 1; + ess_bdr[bcID] = 1; + BCData::GetComponents(ess_comp[i], cmp_row); + component(bcID, 0) = cmp_row[0]; + component(bcID, 1) = cmp_row[1]; + component(bcID, 2) = cmp_row[2]; + } + } +} + +void BCManager::UpdateBCData(mfem::Array& ess_bdr, + mfem::Vector& vgrad, + mfem::Array2D& component) { + ess_bdr = 0; + vgrad.HostReadWrite(); + vgrad = 0.0; + auto data = vgrad.HostReadWrite(); + + // The size here is set explicitly + component.SetSize(ess_bdr.Size(), 3); + mfem::Array cmp_row; + cmp_row.SetSize(3); + + component = false; + cmp_row = false; + + if (map_ess_vgrad.find(step) == map_ess_vgrad.end()) { + return; + } + + auto ess_vgrad = map_ess_vgrad.find(step)->second; + auto ess_comp = map_ess_comp["ess_vgrad"].find(step)->second; + auto ess_id = map_ess_id["ess_vgrad"].find(step)->second; + + for (size_t i = 0; i < ess_vgrad.size(); ++i) { + data[i] = ess_vgrad.at(i); + } + + for (size_t i = 0; i < ess_id.size(); ++i) { + // set the active boundary attributes + if (ess_comp[i] != 0) { + const int bcID = ess_id[i] - 1; + ess_bdr[bcID] = 1; + BCData::GetComponents(ess_comp[i], cmp_row); + component(bcID, 0) = cmp_row[0]; + component(bcID, 1) = cmp_row[1]; + component(bcID, 2) = cmp_row[2]; + } + } +} diff --git a/src/boundary_conditions/BCManager.hpp b/src/boundary_conditions/BCManager.hpp new file mode 100644 index 0000000..8252523 --- /dev/null +++ b/src/boundary_conditions/BCManager.hpp @@ -0,0 +1,261 @@ + +#ifndef BCMANAGER +#define BCMANAGER + +#include "boundary_conditions/BCData.hpp" +#include "options/option_parser_v2.hpp" + +// C/C++ includes +#include +#include +#include // for std::unordered_map +#include + +/** + * @brief Singleton manager for all boundary conditions in the simulation + * + * @details This class implements the Singleton pattern to provide centralized management + * of boundary conditions throughout the simulation. It coordinates time-dependent boundary + * conditions, manages multiple BCData instances, and provides the interface between + * the options system and the finite element assembly process. + * + * Key responsibilities: + * - Manage time-dependent boundary condition changes + * - Store and organize essential velocity and velocity gradient data + * - Create and maintain BCData instances for each boundary + * - Coordinate between different boundary condition types (velocity vs velocity gradient) + * - Provide thread-safe initialization and access + * + * The class supports complex boundary condition scenarios including: + * - Multi-step boundary condition evolution + * - Mixed velocity and velocity gradient constraints + * - Component-wise boundary condition application + * - Time-dependent boundary condition updates + */ +class BCManager { +public: + /** + * @brief Get the singleton instance of BCManager + * + * @return Reference to the singleton BCManager instance + * + * @details Implements the Meyer's singleton pattern for thread-safe initialization. + * The instance is created on first call and persists for the lifetime of the program. + */ + static BCManager& GetInstance() { + static BCManager bc_manager; + return bc_manager; + } + + /** + * @brief Initialize the BCManager with time-dependent boundary condition data + * + * @param u_step Vector of time steps when boundary conditions should be updated + * @param ess_vel Map from time step to essential velocity values + * @param ess_vgrad Map from time step to essential velocity gradient values + * @param ess_comp Map from BC type and time step to component IDs + * @param ess_id Map from BC type and time step to boundary IDs + * + * @details Thread-safe initialization using std::call_once. This method should be called + * once during simulation setup to configure all time-dependent boundary condition data. + * The data structures support complex time-dependent scenarios where different boundaries + * can have different constraint patterns that change over time. + * + * The map_of_imap type represents nested maps: map>> + * where the outer key is the BC type ("ess_vel", "ess_vgrad", "total") and the inner + * key is the time step number. + */ + void Init(const std::vector& u_step, + const std::unordered_map>& ess_vel, + const std::unordered_map>& ess_vgrad, + const map_of_imap& ess_comp, + const map_of_imap& ess_id) { + std::call_once(init_flag, [&]() { + update_step = u_step; + map_ess_vel = ess_vel; + map_ess_vgrad = ess_vgrad; + map_ess_comp = ess_comp; + map_ess_id = ess_id; + }); + } + + /** + * @brief Get a boundary condition instance by ID + * + * @param bcID Boundary condition identifier + * @return Reference to the BCData instance for the specified boundary + * + * @details Provides access to a specific boundary condition instance. The bcID + * corresponds to mesh boundary attributes. Used during assembly to access + * boundary condition data for specific mesh boundaries. + */ + BCData& GetBCInstance(int bcID) { + return m_bc_instances.find(bcID)->second; + } + + /** + * @brief Get a boundary condition instance by ID (const version) + * + * @param bcID Boundary condition identifier + * @return Const reference to the BCData instance for the specified boundary + * + * @details Const version of GetBCInstance for read-only access to boundary condition data. + */ + const BCData& GetBCInstance(int bcID) const { + return m_bc_instances.find(bcID)->second; + } + + /** + * @brief Create or access a boundary condition instance + * + * @param bcID Boundary condition identifier + * @return Reference to the BCData instance (created if it doesn't exist) + * + * @details Creates a new BCData instance if one doesn't exist for the given bcID, + * or returns a reference to the existing instance. This is used during boundary + * condition setup to ensure all required BCData objects are available. + */ + BCData& CreateBCs(int bcID) { + return m_bc_instances[bcID]; + } + + /** + * @brief Get all boundary condition instances + * + * @return Reference to the map containing all BCData instances + * + * @details Provides access to the complete collection of boundary condition instances. + * Useful for iteration or bulk operations on all boundary conditions. + */ + std::unordered_map& GetBCInstances() { + return m_bc_instances; + } + + /** + * @brief Update boundary condition data for the current time step + * + * @param ess_bdr Map of essential boundary arrays by BC type + * @param scale 2D array of scaling factors for boundary conditions + * @param vgrad Vector of velocity gradient values + * @param component Map of component activation arrays by BC type + * + * @details Main coordination method that updates all boundary condition data structures + * for the current simulation time step. This method: + * 1. Clears previous boundary condition data + * 2. Sets up combined boundary condition information + * 3. Calls specialized update methods for velocity and velocity gradient BCs + * 4. Coordinates between different boundary condition types + * + * This is called at the beginning of each time step where boundary conditions change. + */ + void UpdateBCData(std::unordered_map>& ess_bdr, + mfem::Array2D& scale, + mfem::Vector& vgrad, + std::unordered_map>& component); + + /** + * @brief Check if the current step requires boundary condition updates + * + * @param step_ Time step number to check + * @return True if boundary conditions should be updated at this step + * + * @details Determines whether boundary conditions need to be updated at the specified + * time step by checking against the list of update steps provided during initialization. + * If an update is needed, the internal step counter is also updated. + */ + bool GetUpdateStep(int step_) { + if (std::find(update_step.begin(), update_step.end(), step_) != update_step.end()) { + step = step_; + return true; + } else { + return false; + } + } + +private: + /** + * @brief Private constructor for singleton pattern + * + * @details Default constructor is private to enforce singleton pattern. + */ + BCManager() {} + + /** + * @brief Deleted copy constructor for singleton pattern + */ + BCManager(const BCManager&) = delete; + + /** + * @brief Deleted copy assignment operator for singleton pattern + */ + BCManager& operator=(const BCManager&) = delete; + + /** + * @brief Deleted move constructor for singleton pattern + */ + BCManager(BCManager&&) = delete; + + /** + * @brief Deleted move assignment operator for singleton pattern + */ + BCManager& operator=(BCManager&&) = delete; + + /** + * @brief Update velocity gradient boundary condition data + * + * @param ess_bdr Essential boundary array for velocity gradient BCs + * @param vgrad Velocity gradient vector to populate + * @param component Component activation array for velocity gradient BCs + * + * @details Specialized update method for velocity gradient boundary conditions. + * Processes the velocity gradient data for the current time step and sets up + * the appropriate data structures for finite element assembly. + */ + void + UpdateBCData(mfem::Array& ess_bdr, mfem::Vector& vgrad, mfem::Array2D& component); + + /** + * @brief Update velocity boundary condition data + * + * @param ess_bdr Essential boundary array for velocity BCs + * @param scale Scaling factors for velocity BCs + * @param component Component activation array for velocity BCs + * + * @details Specialized update method for velocity boundary conditions. Creates BCData + * instances for each active boundary, sets up scaling factors, and prepares data + * structures for finite element assembly. This method: + * 1. Clears existing BCData instances + * 2. Processes essential velocity data for the current time step + * 3. Creates BCData objects with appropriate velocity and component settings + * 4. Sets up scaling and boundary activation arrays + */ + void UpdateBCData(mfem::Array& ess_bdr, + mfem::Array2D& scale, + mfem::Array2D& component); + + /** @brief Thread-safe initialization flag */ + std::once_flag init_flag; + + /** @brief Current simulation time step */ + int step = 0; + + /** @brief Collection of boundary condition data instances */ + std::unordered_map m_bc_instances; + + /** @brief Time steps when boundary conditions should be updated */ + std::vector update_step; + + /** @brief Essential velocity values by time step */ + std::unordered_map> map_ess_vel; + + /** @brief Essential velocity gradient values by time step */ + std::unordered_map> map_ess_vgrad; + + /** @brief Component IDs by BC type and time step */ + map_of_imap map_ess_comp; + + /** @brief Boundary IDs by BC type and time step */ + map_of_imap map_ess_id; +}; + +#endif diff --git a/src/fem_operators/mechanics_integrators.cpp b/src/fem_operators/mechanics_integrators.cpp new file mode 100644 index 0000000..9ade98d --- /dev/null +++ b/src/fem_operators/mechanics_integrators.cpp @@ -0,0 +1,2152 @@ + + +#include "fem_operators/mechanics_integrators.hpp" + +#include "utilities/assembly_ops.hpp" +#include "utilities/mechanics_log.hpp" + +#include "RAJA/RAJA.hpp" +#include "mfem.hpp" +#include "mfem/general/forall.hpp" + +#include +#include // cerr +#include // log + +// Outside of the UMAT function calls this should be the function called +// to assemble our residual vectors. +void ExaNLFIntegrator::AssembleElementVector(const mfem::FiniteElement& el, + mfem::ElementTransformation& Ttr, + const mfem::Vector& elfun, + mfem::Vector& elvect) { + CALI_CXX_MARK_SCOPE("enlfi_assembleElemVec"); + int dof = el.GetDof(), dim = el.GetDim(); + + mfem::DenseMatrix DSh, DS; + mfem::DenseMatrix Jpt; + mfem::DenseMatrix PMatI, PMatO; + // This is our stress tensor + mfem::DenseMatrix P(3); + + DSh.SetSize(dof, dim); + DS.SetSize(dof, dim); + Jpt.SetSize(dim); + + // PMatI would be our velocity in this case + PMatI.UseExternalData(elfun.GetData(), dof, dim); + elvect.SetSize(dof * dim); + + // PMatO would be our residual vector + elvect = 0.0; + PMatO.UseExternalData(elvect.HostReadWrite(), dof, dim); + + const mfem::IntegrationRule* ir = IntRule; + if (!ir) { + ir = &(mfem::IntRules.Get(el.GetGeomType(), + 2 * el.GetOrder() + 1)); // must match quadrature space + } + + for (int i = 0; i < ir->GetNPoints(); i++) { + const mfem::IntegrationPoint& ip = ir->IntPoint(i); + Ttr.SetIntPoint(&ip); + + // compute Jacobian of the transformation + Jpt = Ttr.InverseJacobian(); // Jrt = dxi / dX + + el.CalcDShape(ip, DSh); + Mult(DSh, Jpt, DS); // dN_a(xi) / dX = dN_a(xi)/dxi * dxi/dX + + double stress[6]; + GetQFData( + Ttr.ElementNo, i, stress, m_sim_state->GetQuadratureFunction("cauchy_stress_end")); + // Could probably later have this only set once... + // Would reduce the number mallocs that we're doing and + // should potentially provide a small speed boost. + /** + * @brief Map Voigt notation stress components to full 3x3 symmetric stress tensor. + * + * Converts stress data from Voigt notation [σ_xx, σ_yy, σ_zz, σ_xy, σ_xz, σ_yz] + * to full symmetric 3x3 stress tensor for use in matrix operations. + * The symmetry is enforced by setting P(i,j) = P(j,i) for off-diagonal terms. + */ + P(0, 0) = stress[0]; + P(1, 1) = stress[1]; + P(2, 2) = stress[2]; + P(1, 2) = stress[3]; + P(0, 2) = stress[4]; + P(0, 1) = stress[5]; + + P(2, 1) = P(1, 2); + P(2, 0) = P(0, 2); + P(1, 0) = P(0, 1); + + DS *= (Ttr.Weight() * ip.weight); + AddMult(DS, P, PMatO); + } + + return; +} + +void ExaNLFIntegrator::AssembleElementGrad(const mfem::FiniteElement& el, + mfem::ElementTransformation& Ttr, + const mfem::Vector& /*elfun*/, + mfem::DenseMatrix& elmat) { + CALI_CXX_MARK_SCOPE("enlfi_assembleElemGrad"); + int dof = el.GetDof(), dim = el.GetDim(); + + mfem::DenseMatrix DSh, DS, Jrt; + + // Now time to start assembling stuff + mfem::DenseMatrix grad_trans, temp; + mfem::DenseMatrix tan_stiff; + + constexpr int ngrad_dim2 = 36; + double matGrad[ngrad_dim2]; + + // temp1 is now going to become the transpose Bmatrix as seen in + // [B^t][tan_stiff][B] + grad_trans.SetSize(dof * dim, 6); + // We need a temp matrix to store our first matrix results as seen in here + temp.SetSize(6, dof * dim); + + tan_stiff.UseExternalData(&matGrad[0], 6, 6); + + DSh.SetSize(dof, dim); + DS.SetSize(dof, dim); + Jrt.SetSize(dim); + elmat.SetSize(dof * dim); + + const mfem::IntegrationRule* ir = IntRule; + if (!ir) { + ir = &(mfem::IntRules.Get(el.GetGeomType(), + 2 * el.GetOrder() + 1)); // <--- must match quadrature space + } + + elmat = 0.0; + + for (int i = 0; i < ir->GetNPoints(); i++) { + const mfem::IntegrationPoint& ip = ir->IntPoint(i); + Ttr.SetIntPoint(&ip); + CalcInverse(Ttr.Jacobian(), Jrt); + + el.CalcDShape(ip, DSh); + Mult(DSh, Jrt, DS); + + GetQFData( + Ttr.ElementNo, i, matGrad, m_sim_state->GetQuadratureFunction("tangent_stiffness")); + // temp1 is B^t + GenerateGradMatrix(DS, grad_trans); + // We multiple our quadrature wts here to our tan_stiff matrix + tan_stiff *= ip.weight * Ttr.Weight(); + // We use kgeom as a temporary matrix + // kgeom = [Cstiff][B] + MultABt(tan_stiff, grad_trans, temp); + // We now add our [B^t][kgeom] product to our tangent stiffness matrix that + // we want to output to our material tangent stiffness matrix + AddMult(grad_trans, temp, elmat); + } + + return; +} + +// This performs the assembly step of our RHS side of our system: +// f_ik = +void ExaNLFIntegrator::AssemblePA(const mfem::FiniteElementSpace& fes) { + CALI_CXX_MARK_SCOPE("enlfi_assemblePA"); + mfem::Mesh* mesh = fes.GetMesh(); + const mfem::FiniteElement& el = *fes.GetFE(0); + space_dims = el.GetDim(); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + + nqpts = ir->GetNPoints(); + nnodes = el.GetDof(); + nelems = fes.GetNE(); + + auto W = ir->GetWeights().Read(); + geom = mesh->GetGeometricFactors(*ir, mfem::GeometricFactors::JACOBIANS); + + // return a pointer to beginning step stress. This is used for output visualization + auto stress_end = m_sim_state->GetQuadratureFunction("cauchy_stress_end"); + + if ((space_dims == 1) || (space_dims == 2)) { + MFEM_ABORT("Dimensions of 1 or 2 not supported."); + } else { + const int dim = 3; + + if (grad.Size() != (nqpts * dim * nnodes)) { + grad.SetSize(nqpts * dim * nnodes, mfem::Device::GetMemoryType()); + { + mfem::DenseMatrix DSh; + const int offset = nnodes * dim; + double* qpts_dshape_data = grad.HostReadWrite(); + for (int i = 0; i < nqpts; i++) { + const mfem::IntegrationPoint& ip = ir->IntPoint(i); + DSh.UseExternalData(&qpts_dshape_data[offset * i], nnodes, dim); + el.CalcDShape(ip, DSh); + } + } + grad.UseDevice(true); + } + + // geom->J really isn't going to work for us as of right now. We could just reorder it + // to the version that we want it to be in instead... + if (jacobian.Size() != (dim * dim * nqpts * nelems)) { + jacobian.SetSize(dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); + jacobian.UseDevice(true); + } + + if (dmat.Size() != (dim * dim * nqpts * nelems)) { + dmat.SetSize(dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); + dmat.UseDevice(true); + } + + const int DIM2 = 2; + const int DIM3 = 3; + const int DIM4 = 4; + std::array perm4{{3, 2, 1, 0}}; + std::array perm3{{2, 1, 0}}; + + RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{dim, dim, nqpts, nelems}}, + perm4); + RAJA::View> J(jacobian.ReadWrite(), + layout_jacob); + + RAJA::Layout layout_stress = RAJA::make_permuted_layout({{2 * dim, nqpts, nelems}}, + perm3); + RAJA::View> S(stress_end->ReadWrite(), + layout_stress); + + RAJA::View> D(dmat.ReadWrite(), + layout_jacob); + + RAJA::Layout layout_geom = RAJA::make_permuted_layout({{nqpts, dim, dim, nelems}}, + perm4); + RAJA::View> geom_j_view( + geom->J.Read(), layout_geom); + const int nqpts_ = nqpts; + const int dim_ = dim; + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i) { + for (int j = 0; j < nqpts_; j++) { + for (int k = 0; k < dim_; k++) { + for (int l = 0; l < dim_; l++) { + J(l, k, j, i) = geom_j_view(j, l, k, i); + } + } + } + }); + + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i_elems) { + double adj[dim_ * dim_]; + // So, we're going to say this view is constant however we're going to mutate the values + // only in that one scoped section for the quadrature points. adj is actually in row + // major memory order but if we set this to col. major than this view will act as the + // transpose of adj A which is what we want. + RAJA::View> A(&adj[0], dim_, dim_); + // RAJA::View > A(&adj[0], + // layout_adj); + for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { + // If we scope this then we only need to carry half the number of variables around + // with us for the adjugate term. + { + const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 + const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 + const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 + const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 + const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 + const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 + const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 + const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 + const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 + // adj(J) + adj[0] = (J22 * J33) - (J23 * J32); // 0,0 + adj[1] = (J32 * J13) - (J12 * J33); // 0,1 + adj[2] = (J12 * J23) - (J22 * J13); // 0,2 + adj[3] = (J31 * J23) - (J21 * J33); // 1,0 + adj[4] = (J11 * J33) - (J13 * J31); // 1,1 + adj[5] = (J21 * J13) - (J11 * J23); // 1,2 + adj[6] = (J21 * J32) - (J31 * J22); // 2,0 + adj[7] = (J31 * J12) - (J11 * J32); // 2,1 + adj[8] = (J11 * J22) - (J12 * J21); // 2,2 + } + + D(0, 0, j_qpts, i_elems) = S(0, j_qpts, i_elems) * A(0, 0) + + S(5, j_qpts, i_elems) * A(0, 1) + + S(4, j_qpts, i_elems) * A(0, 2); + D(1, 0, j_qpts, i_elems) = S(0, j_qpts, i_elems) * A(1, 0) + + S(5, j_qpts, i_elems) * A(1, 1) + + S(4, j_qpts, i_elems) * A(1, 2); + D(2, 0, j_qpts, i_elems) = S(0, j_qpts, i_elems) * A(2, 0) + + S(5, j_qpts, i_elems) * A(2, 1) + + S(4, j_qpts, i_elems) * A(2, 2); + + D(0, 1, j_qpts, i_elems) = S(5, j_qpts, i_elems) * A(0, 0) + + S(1, j_qpts, i_elems) * A(0, 1) + + S(3, j_qpts, i_elems) * A(0, 2); + D(1, 1, j_qpts, i_elems) = S(5, j_qpts, i_elems) * A(1, 0) + + S(1, j_qpts, i_elems) * A(1, 1) + + S(3, j_qpts, i_elems) * A(1, 2); + D(2, 1, j_qpts, i_elems) = S(5, j_qpts, i_elems) * A(2, 0) + + S(1, j_qpts, i_elems) * A(2, 1) + + S(3, j_qpts, i_elems) * A(2, 2); + + D(0, 2, j_qpts, i_elems) = S(4, j_qpts, i_elems) * A(0, 0) + + S(3, j_qpts, i_elems) * A(0, 1) + + S(2, j_qpts, i_elems) * A(0, 2); + D(1, 2, j_qpts, i_elems) = S(4, j_qpts, i_elems) * A(1, 0) + + S(3, j_qpts, i_elems) * A(1, 1) + + S(2, j_qpts, i_elems) * A(1, 2); + D(2, 2, j_qpts, i_elems) = S(4, j_qpts, i_elems) * A(2, 0) + + S(3, j_qpts, i_elems) * A(2, 1) + + S(2, j_qpts, i_elems) * A(2, 2); + } // End of doing J_{ij}\sigma_{jk} / nqpts loop + }); // End of elements + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i_elems) { + for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { + for (int i = 0; i < dim_; i++) { + for (int j = 0; j < dim_; j++) { + D(j, i, j_qpts, i_elems) *= W[j_qpts]; + } + } + } + }); + } // End of if statement +} + +// In the below function we'll be applying the below action on our material +// tangent matrix C^{tan} at each quadrature point as: +// D_{ijkm} = 1 / det(J) * w_{qpt} * adj(J)^T_{ij} C^{tan}_{ijkl} adj(J)_{lm} +// where D is our new 4th order tensor, J is our jacobian calculated from the +// mesh geometric factors, and adj(J) is the adjugate of J. +void ExaNLFIntegrator::AssembleGradPA(const mfem::Vector& /* x */, + const mfem::FiniteElementSpace& fes) { + this->AssembleGradPA(fes); +} + +// In the below function we'll be applying the below action on our material +// tangent matrix C^{tan} at each quadrature point as: +// D_{ijkm} = 1 / det(J) * w_{qpt} * adj(J)^T_{ij} C^{tan}_{ijkl} adj(J)_{lm} +// where D is our new 4th order tensor, J is our jacobian calculated from the +// mesh geometric factors, and adj(J) is the adjugate of J. +void ExaNLFIntegrator::AssembleGradPA(const mfem::FiniteElementSpace& fes) { + CALI_CXX_MARK_SCOPE("enlfi_assemblePAG"); + mfem::Mesh* mesh = fes.GetMesh(); + const mfem::FiniteElement& el = *fes.GetFE(0); + space_dims = el.GetDim(); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + + nqpts = ir->GetNPoints(); + nnodes = el.GetDof(); + nelems = fes.GetNE(); + auto W = ir->GetWeights().Read(); + + if ((space_dims == 1) || (space_dims == 2)) { + MFEM_ABORT("Dimensions of 1 or 2 not supported."); + } else { + const int dim = 3; + + if (grad.Size() != (nqpts * dim * nnodes)) { + grad.SetSize(nqpts * dim * nnodes, mfem::Device::GetMemoryType()); + { + mfem::DenseMatrix DSh; + const int offset = nnodes * dim; + double* qpts_dshape_data = grad.HostReadWrite(); + for (int i = 0; i < nqpts; i++) { + const mfem::IntegrationPoint& ip = ir->IntPoint(i); + DSh.UseExternalData(&qpts_dshape_data[offset * i], nnodes, dim); + el.CalcDShape(ip, DSh); + } + } + grad.UseDevice(true); + } + + // geom->J really isn't going to work for us as of right now. We could just reorder it + // to the version that we want it to be in instead... + if (jacobian.Size() != (dim * dim * nqpts * nelems)) { + jacobian.SetSize(dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); + jacobian.UseDevice(true); + + geom = mesh->GetGeometricFactors(*ir, mfem::GeometricFactors::JACOBIANS); + + const int DIM4 = 4; + std::array perm4{{3, 2, 1, 0}}; + + RAJA::Layout layout_jacob = RAJA::make_permuted_layout( + {{dim, dim, nqpts, nelems}}, perm4); + RAJA::View> J(jacobian.ReadWrite(), + layout_jacob); + + RAJA::Layout layout_geom = RAJA::make_permuted_layout({{nqpts, dim, dim, nelems}}, + perm4); + RAJA::View> geom_j_view( + geom->J.Read(), layout_geom); + const int nqpts_ = nqpts; + const int dim_ = dim; + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i) { + for (int j = 0; j < nqpts_; j++) { + for (int k = 0; k < dim_; k++) { + for (int l = 0; l < dim_; l++) { + J(l, k, j, i) = geom_j_view(j, l, k, i); + } + } + } + }); + } + + if (pa_dmat.Size() != (dim * dim * dim * dim * nqpts * nelems)) { + pa_dmat.SetSize(dim * dim * dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); + pa_dmat.UseDevice(true); + } + + if (pa_mat.Size() != (dim * dim * dim * dim * nqpts * nelems)) { + pa_mat.SetSize(dim * dim * dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); + pa_mat.UseDevice(true); + } + + TransformMatGradTo4D(m_sim_state->GetQuadratureFunction("tangent_stiffness"), pa_mat); + + pa_dmat = 0.0; + + const int DIM2 = 2; + const int DIM4 = 4; + const int DIM6 = 6; + std::array perm6{{5, 4, 3, 2, 1, 0}}; + std::array perm4{{3, 2, 1, 0}}; + std::array perm2{{1, 0}}; + + // bunch of helper RAJA views to make dealing with data easier down below in our kernel. + + RAJA::Layout layout_4Dtensor = RAJA::make_permuted_layout( + {{dim, dim, dim, dim, nqpts, nelems}}, perm6); + RAJA::View> C(pa_mat.Read(), + layout_4Dtensor); + // Swapped over to row order since it makes sense in later applications... + // Should make C row order as well for PA operations + RAJA::View> D( + pa_dmat.ReadWrite(), nelems, nqpts, dim, dim, dim, dim); + + RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{dim, dim, nqpts, nelems}}, + perm4); + RAJA::View> J(jacobian.ReadWrite(), + layout_jacob); + + RAJA::Layout layout_adj = RAJA::make_permuted_layout({{dim, dim}}, perm2); + + const int nqpts_ = nqpts; + const int dim_ = dim; + // This loop we'll want to parallelize the rest are all serial for now. + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i_elems) { + double adj[dim_ * dim_]; + double c_detJ; + // So, we're going to say this view is constant however we're going to mutate the values + // only in that one scoped section for the quadrature points. + RAJA::View> A(&adj[0], + layout_adj); + for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { + // If we scope this then we only need to carry half the number of variables around + // with us for the adjugate term. + { + const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 + const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 + const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 + const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 + const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 + const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 + const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 + const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 + const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 + const double detJ = J11 * (J22 * J33 - J32 * J23) - + /* */ J21 * (J12 * J33 - J32 * J13) + + /* */ J31 * (J12 * J23 - J22 * J13); + c_detJ = 1.0 / detJ * W[j_qpts]; + // adj(J) + adj[0] = (J22 * J33) - (J23 * J32); // 0,0 + adj[1] = (J32 * J13) - (J12 * J33); // 0,1 + adj[2] = (J12 * J23) - (J22 * J13); // 0,2 + adj[3] = (J31 * J23) - (J21 * J33); // 1,0 + adj[4] = (J11 * J33) - (J13 * J31); // 1,1 + adj[5] = (J21 * J13) - (J11 * J23); // 1,2 + adj[6] = (J21 * J32) - (J31 * J22); // 2,0 + adj[7] = (J31 * J12) - (J11 * J32); // 2,1 + adj[8] = (J11 * J22) - (J12 * J21); // 2,2 + } + // Unrolled part of the loops just so we wouldn't have so many nested ones. + // If we were to get really ambitious we could eliminate also the m indexed + // loop... + for (int n = 0; n < dim_; n++) { + for (int m = 0; m < dim_; m++) { + for (int l = 0; l < dim_; l++) { + D(i_elems, j_qpts, 0, 0, l, n) += + (A(0, 0) * C(0, 0, l, m, j_qpts, i_elems) + + A(1, 0) * C(1, 0, l, m, j_qpts, i_elems) + + A(2, 0) * C(2, 0, l, m, j_qpts, i_elems)) * + A(m, n); + D(i_elems, j_qpts, 0, 1, l, n) += + (A(0, 0) * C(0, 1, l, m, j_qpts, i_elems) + + A(1, 0) * C(1, 1, l, m, j_qpts, i_elems) + + A(2, 0) * C(2, 1, l, m, j_qpts, i_elems)) * + A(m, n); + D(i_elems, j_qpts, 0, 2, l, n) += + (A(0, 0) * C(0, 2, l, m, j_qpts, i_elems) + + A(1, 0) * C(1, 2, l, m, j_qpts, i_elems) + + A(2, 0) * C(2, 2, l, m, j_qpts, i_elems)) * + A(m, n); + D(i_elems, j_qpts, 1, 0, l, n) += + (A(0, 1) * C(0, 0, l, m, j_qpts, i_elems) + + A(1, 1) * C(1, 0, l, m, j_qpts, i_elems) + + A(2, 1) * C(2, 0, l, m, j_qpts, i_elems)) * + A(m, n); + D(i_elems, j_qpts, 1, 1, l, n) += + (A(0, 1) * C(0, 1, l, m, j_qpts, i_elems) + + A(1, 1) * C(1, 1, l, m, j_qpts, i_elems) + + A(2, 1) * C(2, 1, l, m, j_qpts, i_elems)) * + A(m, n); + D(i_elems, j_qpts, 1, 2, l, n) += + (A(0, 1) * C(0, 2, l, m, j_qpts, i_elems) + + A(1, 1) * C(1, 2, l, m, j_qpts, i_elems) + + A(2, 1) * C(2, 2, l, m, j_qpts, i_elems)) * + A(m, n); + D(i_elems, j_qpts, 2, 0, l, n) += + (A(0, 2) * C(0, 0, l, m, j_qpts, i_elems) + + A(1, 2) * C(1, 0, l, m, j_qpts, i_elems) + + A(2, 2) * C(2, 0, l, m, j_qpts, i_elems)) * + A(m, n); + D(i_elems, j_qpts, 2, 1, l, n) += + (A(0, 2) * C(0, 1, l, m, j_qpts, i_elems) + + A(1, 2) * C(1, 1, l, m, j_qpts, i_elems) + + A(2, 2) * C(2, 1, l, m, j_qpts, i_elems)) * + A(m, n); + D(i_elems, j_qpts, 2, 2, l, n) += + (A(0, 2) * C(0, 2, l, m, j_qpts, i_elems) + + A(1, 2) * C(1, 2, l, m, j_qpts, i_elems) + + A(2, 2) * C(2, 2, l, m, j_qpts, i_elems)) * + A(m, n); + } + } + } // End of Dikln = adj(J)_{ji} C_{jklm} adj(J)_{mn} loop + + // Unrolled part of the loops just so we wouldn't have so many nested ones. + for (int n = 0; n < dim_; n++) { + for (int l = 0; l < dim_; l++) { + D(i_elems, j_qpts, l, n, 0, 0) *= c_detJ; + D(i_elems, j_qpts, l, n, 0, 1) *= c_detJ; + D(i_elems, j_qpts, l, n, 0, 2) *= c_detJ; + D(i_elems, j_qpts, l, n, 1, 0) *= c_detJ; + D(i_elems, j_qpts, l, n, 1, 1) *= c_detJ; + D(i_elems, j_qpts, l, n, 1, 2) *= c_detJ; + D(i_elems, j_qpts, l, n, 2, 0) *= c_detJ; + D(i_elems, j_qpts, l, n, 2, 1) *= c_detJ; + D(i_elems, j_qpts, l, n, 2, 2) *= c_detJ; + } + } // End of D_{ijkl} *= 1/det(J) * w_{qpt} loop + } // End of quadrature loop + }); // End of Elements loop + } // End of else statement +} + +// Here we're applying the following action operation using the assembled "D" 2nd order +// tensor found above: +// y_{ik} = \nabla_{ij}\phi^T_{\epsilon} D_{jk} +void ExaNLFIntegrator::AddMultPA(const mfem::Vector& /*x*/, mfem::Vector& y) const { + CALI_CXX_MARK_SCOPE("enlfi_amPAV"); + if ((space_dims == 1) || (space_dims == 2)) { + MFEM_ABORT("Dimensions of 1 or 2 not supported."); + } else { + const int dim = 3; + const int DIM3 = 3; + const int DIM4 = 4; + + std::array perm3{{2, 1, 0}}; + std::array perm4{{3, 2, 1, 0}}; + // Swapped over to row order since it makes sense in later applications... + // Should make C row order as well for PA operations + RAJA::Layout layout_tensor = RAJA::make_permuted_layout({{dim, dim, nqpts, nelems}}, + perm4); + RAJA::View> D(dmat.Read(), + layout_tensor); + // Our field variables that are inputs and outputs + RAJA::Layout layout_field = RAJA::make_permuted_layout({{nnodes, dim, nelems}}, + perm3); + RAJA::View> Y(y.ReadWrite(), layout_field); + // Transpose of the local gradient variable + RAJA::Layout layout_grads = RAJA::make_permuted_layout({{nnodes, dim, nqpts}}, perm3); + RAJA::View> Gt(grad.Read(), + layout_grads); + + const int nqpts_ = nqpts; + const int dim_ = dim; + const int nnodes_ = nnodes; + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i_elems) { + for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { + for (int k = 0; k < dim_; k++) { + for (int j = 0; j < dim_; j++) { + for (int i = 0; i < nnodes_; i++) { + Y(i, k, i_elems) += Gt(i, j, j_qpts) * D(j, k, j_qpts, i_elems); + } + } + } // End of the final action of Y_{ik} += Gt_{ij} T_{jk} + } // End of nQpts + }); // End of nelems + } // End of if statement +} + +// Here we're applying the following action operation using the assembled "D" 4th order +// tensor found above: +// y_{ik} = \nabla_{ij}\phi^T_{\epsilon} D_{jklm} \nabla_{mn}\phi_{\epsilon} x_{nl} +void ExaNLFIntegrator::AddMultGradPA(const mfem::Vector& x, mfem::Vector& y) const { + CALI_CXX_MARK_SCOPE("enlfi_amPAG"); + if ((space_dims == 1) || (space_dims == 2)) { + MFEM_ABORT("Dimensions of 1 or 2 not supported."); + } else { + const int dim = 3; + const int DIM2 = 2; + const int DIM3 = 3; + const int DIM6 = 6; + + std::array perm3{{2, 1, 0}}; + std::array perm2{{1, 0}}; + // Swapped over to row order since it makes sense in later applications... + // Should make C row order as well for PA operations + RAJA::View> D( + pa_dmat.Read(), nelems, nqpts, dim, dim, dim, dim); + // Our field variables that are inputs and outputs + RAJA::Layout layout_field = RAJA::make_permuted_layout({{nnodes, dim, nelems}}, + perm3); + RAJA::View> X(x.Read(), layout_field); + RAJA::View> Y(y.ReadWrite(), layout_field); + // Transpose of the local gradient variable + RAJA::Layout layout_grads = RAJA::make_permuted_layout({{nnodes, dim, nqpts}}, perm3); + RAJA::View> Gt(grad.Read(), + layout_grads); + + // View for our temporary 2d array + RAJA::Layout layout_adj = RAJA::make_permuted_layout({{dim, dim}}, perm2); + const int nqpts_ = nqpts; + const int dim_ = dim; + const int nnodes_ = nnodes; + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i_elems) { + for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { + double T[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + for (int i = 0; i < dim_; i++) { + for (int j = 0; j < dim_; j++) { + for (int k = 0; k < nnodes_; k++) { + T[0] += D(i_elems, j_qpts, 0, 0, i, j) * Gt(k, j, j_qpts) * + X(k, i, i_elems); + T[1] += D(i_elems, j_qpts, 1, 0, i, j) * Gt(k, j, j_qpts) * + X(k, i, i_elems); + T[2] += D(i_elems, j_qpts, 2, 0, i, j) * Gt(k, j, j_qpts) * + X(k, i, i_elems); + T[3] += D(i_elems, j_qpts, 0, 1, i, j) * Gt(k, j, j_qpts) * + X(k, i, i_elems); + T[4] += D(i_elems, j_qpts, 1, 1, i, j) * Gt(k, j, j_qpts) * + X(k, i, i_elems); + T[5] += D(i_elems, j_qpts, 2, 1, i, j) * Gt(k, j, j_qpts) * + X(k, i, i_elems); + T[6] += D(i_elems, j_qpts, 0, 2, i, j) * Gt(k, j, j_qpts) * + X(k, i, i_elems); + T[7] += D(i_elems, j_qpts, 1, 2, i, j) * Gt(k, j, j_qpts) * + X(k, i, i_elems); + T[8] += D(i_elems, j_qpts, 2, 2, i, j) * Gt(k, j, j_qpts) * + X(k, i, i_elems); + } + } + } // End of doing tensor contraction of D_{jkmo}G_{op}X_{pm} + + RAJA::View> Tview(&T[0], + layout_adj); + for (int k = 0; k < dim_; k++) { + for (int j = 0; j < dim_; j++) { + for (int i = 0; i < nnodes_; i++) { + Y(i, k, i_elems) += Gt(i, j, j_qpts) * Tview(j, k); + } + } + } // End of the final action of Y_{ik} += Gt_{ij} T_{jk} + } // End of nQpts + }); // End of nelems + } // End of if statement +} + +// This assembles the diagonal of our LHS which can be used as a preconditioner +void ExaNLFIntegrator::AssembleGradDiagonalPA(mfem::Vector& diag) const { + CALI_CXX_MARK_SCOPE("enlfi_AssembleGradDiagonalPA"); + + const mfem::IntegrationRule& ir = + m_sim_state->GetQuadratureFunction("tangent_stiffness")->GetSpaceShared()->GetIntRule(0); + auto W = ir.GetWeights().Read(); + + if ((space_dims == 1) || (space_dims == 2)) { + MFEM_ABORT("Dimensions of 1 or 2 not supported."); + } else { + const int dim = 3; + + const int DIM2 = 2; + const int DIM3 = 3; + const int DIM4 = 4; + + std::array perm4{{3, 2, 1, 0}}; + std::array perm3{{2, 1, 0}}; + std::array perm2{{1, 0}}; + + // bunch of helper RAJA views to make dealing with data easier down below in our kernel. + + RAJA::Layout layout_tensor = RAJA::make_permuted_layout( + {{2 * dim, 2 * dim, nqpts, nelems}}, perm4); + RAJA::View> K( + m_sim_state->GetQuadratureFunction("tangent_stiffness")->Read(), layout_tensor); + + // Our field variables that are inputs and outputs + RAJA::Layout layout_field = RAJA::make_permuted_layout({{nnodes, dim, nelems}}, + perm3); + RAJA::View> Y(diag.ReadWrite(), + layout_field); + + RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{dim, dim, nqpts, nelems}}, + perm4); + RAJA::View> J(jacobian.Read(), + layout_jacob); + + RAJA::Layout layout_adj = RAJA::make_permuted_layout({{dim, dim}}, perm2); + + RAJA::Layout layout_grads = RAJA::make_permuted_layout({{nnodes, dim, nqpts}}, perm3); + RAJA::View> Gt(grad.Read(), + layout_grads); + + const int nqpts_ = nqpts; + const int dim_ = dim; + const int nnodes_ = nnodes; + // This loop we'll want to parallelize the rest are all serial for now. + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i_elems) { + double adj[dim_ * dim_]; + double c_detJ; + // So, we're going to say this view is constant however we're going to mutate the values + // only in that one scoped section for the quadrature points. + RAJA::View> A(&adj[0], + layout_adj); + for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { + // If we scope this then we only need to carry half the number of variables around + // with us for the adjugate term. + { + const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 + const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 + const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 + const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 + const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 + const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 + const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 + const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 + const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 + const double detJ = J11 * (J22 * J33 - J32 * J23) - + /* */ J21 * (J12 * J33 - J32 * J13) + + /* */ J31 * (J12 * J23 - J22 * J13); + c_detJ = 1.0 / detJ * W[j_qpts]; + // adj(J) + adj[0] = (J22 * J33) - (J23 * J32); // 0,0 + adj[1] = (J32 * J13) - (J12 * J33); // 0,1 + adj[2] = (J12 * J23) - (J22 * J13); // 0,2 + adj[3] = (J31 * J23) - (J21 * J33); // 1,0 + adj[4] = (J11 * J33) - (J13 * J31); // 1,1 + adj[5] = (J21 * J13) - (J11 * J23); // 1,2 + adj[6] = (J21 * J32) - (J31 * J22); // 2,0 + adj[7] = (J31 * J12) - (J11 * J32); // 2,1 + adj[8] = (J11 * J22) - (J12 * J21); // 2,2 + } + for (int knodes = 0; knodes < nnodes_; knodes++) { + const double bx = Gt(knodes, 0, j_qpts) * A(0, 0) + + Gt(knodes, 1, j_qpts) * A(0, 1) + + Gt(knodes, 2, j_qpts) * A(0, 2); + + const double by = Gt(knodes, 0, j_qpts) * A(1, 0) + + Gt(knodes, 1, j_qpts) * A(1, 1) + + Gt(knodes, 2, j_qpts) * A(1, 2); + + const double bz = Gt(knodes, 0, j_qpts) * A(2, 0) + + Gt(knodes, 1, j_qpts) * A(2, 1) + + Gt(knodes, 2, j_qpts) * A(2, 2); + + Y(knodes, 0, i_elems) += + c_detJ * + (bx * (bx * K(0, 0, j_qpts, i_elems) + by * K(0, 5, j_qpts, i_elems) + + bz * K(0, 4, j_qpts, i_elems)) + + by * (bx * K(5, 0, j_qpts, i_elems) + by * K(5, 5, j_qpts, i_elems) + + bz * K(5, 4, j_qpts, i_elems)) + + bz * (bx * K(4, 0, j_qpts, i_elems) + by * K(4, 5, j_qpts, i_elems) + + bz * K(4, 4, j_qpts, i_elems))); + + Y(knodes, 1, i_elems) += + c_detJ * + (bx * (bx * K(5, 5, j_qpts, i_elems) + by * K(5, 1, j_qpts, i_elems) + + bz * K(5, 3, j_qpts, i_elems)) + + by * (bx * K(1, 5, j_qpts, i_elems) + by * K(1, 1, j_qpts, i_elems) + + bz * K(1, 3, j_qpts, i_elems)) + + bz * (bx * K(3, 5, j_qpts, i_elems) + by * K(3, 1, j_qpts, i_elems) + + bz * K(3, 3, j_qpts, i_elems))); + + Y(knodes, 2, i_elems) += + c_detJ * + (bx * (bx * K(4, 4, j_qpts, i_elems) + by * K(4, 3, j_qpts, i_elems) + + bz * K(4, 2, j_qpts, i_elems)) + + by * (bx * K(3, 4, j_qpts, i_elems) + by * K(3, 3, j_qpts, i_elems) + + bz * K(3, 2, j_qpts, i_elems)) + + bz * (bx * K(2, 4, j_qpts, i_elems) + by * K(2, 3, j_qpts, i_elems) + + bz * K(2, 2, j_qpts, i_elems))); + } + } + }); + } +} + +/// Method defining element assembly. +/** The result of the element assembly is added and stored in the @a emat + Vector. */ +void ExaNLFIntegrator::AssembleGradEA(const mfem::Vector& /*x*/, + const mfem::FiniteElementSpace& fes, + mfem::Vector& emat) { + AssembleEA(fes, emat); +} +void ExaNLFIntegrator::AssembleEA(const mfem::FiniteElementSpace& fes, mfem::Vector& emat) { + CALI_CXX_MARK_SCOPE("enlfi_assembleEA"); + mfem::Mesh* mesh = fes.GetMesh(); + const mfem::FiniteElement& el = *fes.GetFE(0); + space_dims = el.GetDim(); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + + nqpts = ir->GetNPoints(); + nnodes = el.GetDof(); + nelems = fes.GetNE(); + auto W = ir->GetWeights().Read(); + + if ((space_dims == 1) || (space_dims == 2)) { + MFEM_ABORT("Dimensions of 1 or 2 not supported."); + } else { + const int dim = 3; + + if (grad.Size() != (nqpts * dim * nnodes)) { + grad.SetSize(nqpts * dim * nnodes, mfem::Device::GetMemoryType()); + { + mfem::DenseMatrix DSh; + const int offset = nnodes * dim; + double* qpts_dshape_data = grad.HostReadWrite(); + for (int i = 0; i < nqpts; i++) { + const mfem::IntegrationPoint& ip = ir->IntPoint(i); + DSh.UseExternalData(&qpts_dshape_data[offset * i], nnodes, dim); + el.CalcDShape(ip, DSh); + } + } + grad.UseDevice(true); + } + + // geom->J really isn't going to work for us as of right now. We could just reorder it + // to the version that we want it to be in instead... + if (jacobian.Size() != (dim * dim * nqpts * nelems)) { + jacobian.SetSize(dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); + jacobian.UseDevice(true); + + geom = mesh->GetGeometricFactors(*ir, mfem::GeometricFactors::JACOBIANS); + + const int DIM4 = 4; + std::array perm4{{3, 2, 1, 0}}; + + RAJA::Layout layout_jacob = RAJA::make_permuted_layout( + {{dim, dim, nqpts, nelems}}, perm4); + RAJA::View> J(jacobian.ReadWrite(), + layout_jacob); + + RAJA::Layout layout_geom = RAJA::make_permuted_layout({{nqpts, dim, dim, nelems}}, + perm4); + RAJA::View> geom_j_view( + geom->J.Read(), layout_geom); + const int nqpts_ = nqpts; + const int dim_ = dim; + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i) { + for (int j = 0; j < nqpts_; j++) { + for (int k = 0; k < dim_; k++) { + for (int l = 0; l < dim_; l++) { + J(l, k, j, i) = geom_j_view(j, l, k, i); + } + } + } + }); + } + + const int DIM2 = 2; + const int DIM3 = 3; + const int DIM4 = 4; + + std::array perm4{{3, 2, 1, 0}}; + std::array perm3{{2, 1, 0}}; + std::array perm2{{1, 0}}; + + // bunch of helper RAJA views to make dealing with data easier down below in our kernel. + + RAJA::Layout layout_tensor = RAJA::make_permuted_layout( + {{2 * dim, 2 * dim, nqpts, nelems}}, perm4); + RAJA::View> K( + m_sim_state->GetQuadratureFunction("tangent_stiffness")->Read(), layout_tensor); + + // Our field variables that are inputs and outputs + RAJA::Layout layout_field = RAJA::make_permuted_layout( + {{nnodes * dim, nnodes * dim, nelems}}, perm3); + RAJA::View> E(emat.ReadWrite(), + layout_field); + + RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{dim, dim, nqpts, nelems}}, + perm4); + RAJA::View> J(jacobian.Read(), + layout_jacob); + + RAJA::Layout layout_adj = RAJA::make_permuted_layout({{dim, dim}}, perm2); + + RAJA::Layout layout_grads = RAJA::make_permuted_layout({{nnodes, dim, nqpts}}, perm3); + RAJA::View> Gt(grad.Read(), + layout_grads); + + const int nqpts_ = nqpts; + const int dim_ = dim; + const int nnodes_ = nnodes; + // This loop we'll want to parallelize the rest are all serial for now. + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i_elems) { + double adj[dim_ * dim_]; + double c_detJ; + // So, we're going to say this view is constant however we're going to mutate the values + // only in that one scoped section for the quadrature points. + RAJA::View> A(&adj[0], + layout_adj); + for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { + // If we scope this then we only need to carry half the number of variables around + // with us for the adjugate term. + { + const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 + const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 + const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 + const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 + const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 + const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 + const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 + const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 + const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 + const double detJ = J11 * (J22 * J33 - J32 * J23) - + /* */ J21 * (J12 * J33 - J32 * J13) + + /* */ J31 * (J12 * J23 - J22 * J13); + c_detJ = 1.0 / detJ * W[j_qpts]; + // adj(J) + adj[0] = (J22 * J33) - (J23 * J32); // 0,0 + adj[1] = (J32 * J13) - (J12 * J33); // 0,1 + adj[2] = (J12 * J23) - (J22 * J13); // 0,2 + adj[3] = (J31 * J23) - (J21 * J33); // 1,0 + adj[4] = (J11 * J33) - (J13 * J31); // 1,1 + adj[5] = (J21 * J13) - (J11 * J23); // 1,2 + adj[6] = (J21 * J32) - (J31 * J22); // 2,0 + adj[7] = (J31 * J12) - (J11 * J32); // 2,1 + adj[8] = (J11 * J22) - (J12 * J21); // 2,2 + } + for (int knds = 0; knds < nnodes_; knds++) { + const double bx = Gt(knds, 0, j_qpts) * A(0, 0) + + Gt(knds, 1, j_qpts) * A(0, 1) + Gt(knds, 2, j_qpts) * A(0, 2); + + const double by = Gt(knds, 0, j_qpts) * A(1, 0) + + Gt(knds, 1, j_qpts) * A(1, 1) + Gt(knds, 2, j_qpts) * A(1, 2); + + const double bz = Gt(knds, 0, j_qpts) * A(2, 0) + + Gt(knds, 1, j_qpts) * A(2, 1) + Gt(knds, 2, j_qpts) * A(2, 2); + + const double k11x = c_detJ * (bx * K(0, 0, j_qpts, i_elems) + + by * K(0, 5, j_qpts, i_elems) + + bz * K(0, 4, j_qpts, i_elems)); + const double k11y = c_detJ * (bx * K(5, 0, j_qpts, i_elems) + + by * K(5, 5, j_qpts, i_elems) + + bz * K(5, 4, j_qpts, i_elems)); + const double k11z = c_detJ * (bx * K(4, 0, j_qpts, i_elems) + + by * K(4, 5, j_qpts, i_elems) + + bz * K(4, 4, j_qpts, i_elems)); + + const double k12x = c_detJ * (bx * K(0, 5, j_qpts, i_elems) + + by * K(0, 1, j_qpts, i_elems) + + bz * K(0, 3, j_qpts, i_elems)); + const double k12y = c_detJ * (bx * K(5, 5, j_qpts, i_elems) + + by * K(5, 1, j_qpts, i_elems) + + bz * K(5, 3, j_qpts, i_elems)); + const double k12z = c_detJ * (bx * K(4, 5, j_qpts, i_elems) + + by * K(4, 1, j_qpts, i_elems) + + bz * K(4, 3, j_qpts, i_elems)); + + const double k13x = c_detJ * (bx * K(0, 4, j_qpts, i_elems) + + by * K(0, 3, j_qpts, i_elems) + + bz * K(0, 2, j_qpts, i_elems)); + const double k13y = c_detJ * (bx * K(5, 4, j_qpts, i_elems) + + by * K(5, 3, j_qpts, i_elems) + + bz * K(5, 2, j_qpts, i_elems)); + const double k13z = c_detJ * (bx * K(4, 4, j_qpts, i_elems) + + by * K(4, 3, j_qpts, i_elems) + + bz * K(4, 2, j_qpts, i_elems)); + + const double k21x = c_detJ * (bx * K(5, 0, j_qpts, i_elems) + + by * K(5, 5, j_qpts, i_elems) + + bz * K(5, 4, j_qpts, i_elems)); + const double k21y = c_detJ * (bx * K(1, 0, j_qpts, i_elems) + + by * K(1, 5, j_qpts, i_elems) + + bz * K(1, 4, j_qpts, i_elems)); + const double k21z = c_detJ * (bx * K(3, 0, j_qpts, i_elems) + + by * K(3, 5, j_qpts, i_elems) + + bz * K(3, 4, j_qpts, i_elems)); + + const double k22x = c_detJ * (bx * K(5, 5, j_qpts, i_elems) + + by * K(5, 1, j_qpts, i_elems) + + bz * K(5, 3, j_qpts, i_elems)); + const double k22y = c_detJ * (bx * K(1, 5, j_qpts, i_elems) + + by * K(1, 1, j_qpts, i_elems) + + bz * K(1, 3, j_qpts, i_elems)); + const double k22z = c_detJ * (bx * K(3, 5, j_qpts, i_elems) + + by * K(3, 1, j_qpts, i_elems) + + bz * K(3, 3, j_qpts, i_elems)); + + const double k23x = c_detJ * (bx * K(5, 4, j_qpts, i_elems) + + by * K(5, 3, j_qpts, i_elems) + + bz * K(5, 2, j_qpts, i_elems)); + const double k23y = c_detJ * (bx * K(1, 4, j_qpts, i_elems) + + by * K(1, 3, j_qpts, i_elems) + + bz * K(1, 2, j_qpts, i_elems)); + const double k23z = c_detJ * (bx * K(3, 4, j_qpts, i_elems) + + by * K(3, 3, j_qpts, i_elems) + + bz * K(3, 2, j_qpts, i_elems)); + + const double k31x = c_detJ * (bx * K(4, 0, j_qpts, i_elems) + + by * K(4, 5, j_qpts, i_elems) + + bz * K(4, 4, j_qpts, i_elems)); + const double k31y = c_detJ * (bx * K(3, 0, j_qpts, i_elems) + + by * K(3, 5, j_qpts, i_elems) + + bz * K(3, 4, j_qpts, i_elems)); + const double k31z = c_detJ * (bx * K(2, 0, j_qpts, i_elems) + + by * K(2, 5, j_qpts, i_elems) + + bz * K(2, 4, j_qpts, i_elems)); + + const double k32x = c_detJ * (bx * K(4, 5, j_qpts, i_elems) + + by * K(4, 1, j_qpts, i_elems) + + bz * K(4, 3, j_qpts, i_elems)); + const double k32y = c_detJ * (bx * K(3, 5, j_qpts, i_elems) + + by * K(3, 1, j_qpts, i_elems) + + bz * K(3, 3, j_qpts, i_elems)); + const double k32z = c_detJ * (bx * K(2, 5, j_qpts, i_elems) + + by * K(2, 1, j_qpts, i_elems) + + bz * K(2, 3, j_qpts, i_elems)); + + const double k33x = c_detJ * (bx * K(4, 4, j_qpts, i_elems) + + by * K(4, 3, j_qpts, i_elems) + + bz * K(4, 2, j_qpts, i_elems)); + const double k33y = c_detJ * (bx * K(3, 4, j_qpts, i_elems) + + by * K(3, 3, j_qpts, i_elems) + + bz * K(3, 2, j_qpts, i_elems)); + const double k33z = c_detJ * (bx * K(2, 4, j_qpts, i_elems) + + by * K(2, 3, j_qpts, i_elems) + + bz * K(2, 2, j_qpts, i_elems)); + + for (int lnds = 0; lnds < nnodes_; lnds++) { + const double gx = Gt(lnds, 0, j_qpts) * A(0, 0) + + Gt(lnds, 1, j_qpts) * A(0, 1) + + Gt(lnds, 2, j_qpts) * A(0, 2); + + const double gy = Gt(lnds, 0, j_qpts) * A(1, 0) + + Gt(lnds, 1, j_qpts) * A(1, 1) + + Gt(lnds, 2, j_qpts) * A(1, 2); + + const double gz = Gt(lnds, 0, j_qpts) * A(2, 0) + + Gt(lnds, 1, j_qpts) * A(2, 1) + + Gt(lnds, 2, j_qpts) * A(2, 2); + + E(lnds, knds, i_elems) += gx * k11x + gy * k11y + gz * k11z; + E(lnds, knds + nnodes_, i_elems) += gx * k12x + gy * k12y + gz * k12z; + E(lnds, knds + 2 * nnodes_, i_elems) += gx * k13x + gy * k13y + gz * k13z; + + E(lnds + nnodes_, knds, i_elems) += gx * k21x + gy * k21y + gz * k21z; + E(lnds + nnodes_, knds + nnodes_, i_elems) += gx * k22x + gy * k22y + + gz * k22z; + E(lnds + nnodes_, knds + 2 * nnodes_, i_elems) += gx * k23x + gy * k23y + + gz * k23z; + + E(lnds + 2 * nnodes_, knds, i_elems) += gx * k31x + gy * k31y + gz * k31z; + E(lnds + 2 * nnodes_, knds + nnodes_, i_elems) += gx * k32x + gy * k32y + + gz * k32z; + E(lnds + 2 * nnodes_, knds + 2 * nnodes_, i_elems) += gx * k33x + + gy * k33y + gz * k33z; + } + } + } + }); + } +} + +// Outside of the UMAT function calls this should be the function called +// to assemble our residual vectors. +void ICExaNLFIntegrator::AssembleElementVector(const mfem::FiniteElement& el, + mfem::ElementTransformation& Ttr, + const mfem::Vector& elfun, + mfem::Vector& elvect) { + CALI_CXX_MARK_SCOPE("icenlfi_assembleElemVec"); + int dof = el.GetDof(), dim = el.GetDim(); + + mfem::DenseMatrix DSh, DS, elem_deriv_shapes_loc; + mfem::DenseMatrix Jpt; + mfem::DenseMatrix PMatI, PMatO; + // This is our stress tensor + mfem::DenseMatrix P; + mfem::DenseMatrix grad_trans; + // temp1 is now going to become the transpose Bmatrix as seen in + // [B^t][tan_stiff][B] + grad_trans.SetSize(dof * dim, 6); + + DSh.SetSize(dof, dim); + DS.SetSize(dof, dim); + elem_deriv_shapes_loc.SetSize(dof, dim); + elem_deriv_shapes_loc = 0.0; + Jpt.SetSize(dim); + + // PMatI would be our velocity in this case + PMatI.UseExternalData(elfun.GetData(), dof, dim); + elvect.SetSize(dof * dim); + + // PMatO would be our residual vector + elvect = 0.0; + PMatO.UseExternalData(elvect.HostReadWrite(), dof * dim, 1); + + const mfem::IntegrationRule* ir = IntRule; + if (!ir) { + ir = &(mfem::IntRules.Get(el.GetGeomType(), + 2 * el.GetOrder() + 1)); // must match quadrature space + } + + const mfem::IntegrationRule* irc = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + double eVol = 0.0; + /** + * @brief Compute element-averaged shape function derivatives for B-bar method. + * + * This loop integrates shape function derivatives over the entire element volume + * to compute volume-averaged quantities needed for the B-bar method. The averaged + * derivatives prevent volumetric locking in incompressible material problems. + * + * Process: + * 1. Integrate ∂N/∂x derivatives weighted by Jacobian and quadrature weights + * 2. Accumulate total element volume (eVol) + * 3. Normalize by total volume to obtain element averages + */ + for (int i = 0; i < irc->GetNPoints(); i++) { + const mfem::IntegrationPoint& ip = irc->IntPoint(i); + Ttr.SetIntPoint(&ip); + + // compute Jacobian of the transformation + Jpt = Ttr.InverseJacobian(); // Jrt = dxi / dX + + el.CalcDShape(ip, DSh); + Mult(DSh, Jpt, DS); // dN_a(xi) / dX = dN_a(xi)/dxi * dxi/dX + DS *= (Ttr.Weight() * ip.weight); + elem_deriv_shapes_loc += DS; + + eVol += (Ttr.Weight() * ip.weight); + } + + elem_deriv_shapes_loc *= (1.0 / eVol); + + double stress[6]; + + P.UseExternalData(&stress[0], 6, 1); + + for (int i = 0; i < ir->GetNPoints(); i++) { + const mfem::IntegrationPoint& ip = ir->IntPoint(i); + Ttr.SetIntPoint(&ip); + + // compute Jacobian of the transformation + Jpt = Ttr.InverseJacobian(); // Jrt = dxi / dX + + el.CalcDShape(ip, DSh); + Mult(DSh, Jpt, DS); // dN_a(xi) / dX = dN_a(xi)/dxi * dxi/dX + + GetQFData( + Ttr.ElementNo, i, stress, m_sim_state->GetQuadratureFunction("cauchy_stress_end")); + GenerateGradBarMatrix(DS, elem_deriv_shapes_loc, grad_trans); + + grad_trans *= (ip.weight * Ttr.Weight()); + AddMult(grad_trans, P, PMatO); + } + + return; +} + +void ICExaNLFIntegrator::AssembleElementGrad(const mfem::FiniteElement& el, + mfem::ElementTransformation& Ttr, + const mfem::Vector& /*elfun*/, + mfem::DenseMatrix& elmat) { + CALI_CXX_MARK_SCOPE("icenlfi_assembleElemGrad"); + int dof = el.GetDof(), dim = el.GetDim(); + + mfem::DenseMatrix DSh, DS, elem_deriv_shapes_loc, Jrt; + + // Now time to start assembling stuff + mfem::DenseMatrix grad_trans, temp; + mfem::DenseMatrix tan_stiff; + + constexpr int ngrad_dim2 = 36; + double matGrad[ngrad_dim2]; + + // temp1 is now going to become the transpose Bmatrix as seen in + // [B^t][tan_stiff][B] + grad_trans.SetSize(dof * dim, 6); + // We need a temp matrix to store our first matrix results as seen in here + temp.SetSize(6, dof * dim); + + tan_stiff.UseExternalData(&matGrad[0], 6, 6); + + DSh.SetSize(dof, dim); + DS.SetSize(dof, dim); + elem_deriv_shapes_loc.SetSize(dof, dim); + elem_deriv_shapes_loc = 0.0; + Jrt.SetSize(dim); + elmat.SetSize(dof * dim); + + const mfem::IntegrationRule* ir = IntRule; + if (!ir) { + ir = &(mfem::IntRules.Get(el.GetGeomType(), + 2 * el.GetOrder() + 1)); // <--- must match quadrature space + } + + elmat = 0.0; + + const mfem::IntegrationRule* irc = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + double eVol = 0.0; + + for (int i = 0; i < irc->GetNPoints(); i++) { + const mfem::IntegrationPoint& ip = irc->IntPoint(i); + Ttr.SetIntPoint(&ip); + + // compute Jacobian of the transformation + Jrt = Ttr.InverseJacobian(); // Jrt = dxi / dX + + el.CalcDShape(ip, DSh); + Mult(DSh, Jrt, DS); // dN_a(xi) / dX = dN_a(xi)/dxi * dxi/dX + DS *= (Ttr.Weight() * ip.weight); + elem_deriv_shapes_loc += DS; + + eVol += (Ttr.Weight() * ip.weight); + } + + elem_deriv_shapes_loc *= (1.0 / eVol); + + for (int i = 0; i < ir->GetNPoints(); i++) { + const mfem::IntegrationPoint& ip = ir->IntPoint(i); + Ttr.SetIntPoint(&ip); + CalcInverse(Ttr.Jacobian(), Jrt); + + el.CalcDShape(ip, DSh); + Mult(DSh, Jrt, DS); + + GetQFData( + Ttr.ElementNo, i, matGrad, m_sim_state->GetQuadratureFunction("tangent_stiffness")); + // temp1 is B^t + GenerateGradBarMatrix(DS, elem_deriv_shapes_loc, grad_trans); + // We multiple our quadrature wts here to our tan_stiff matrix + tan_stiff *= ip.weight * Ttr.Weight(); + // We use kgeom as a temporary matrix + // kgeom = [Cstiff][B] + MultABt(tan_stiff, grad_trans, temp); + // We now add our [B^t][kgeom] product to our tangent stiffness matrix that + // we want to output to our material tangent stiffness matrix + AddMult(grad_trans, temp, elmat); + } + + return; +} + +/// Method defining element assembly. +/** The result of the element assembly is added and stored in the @a emat + Vector. */ +void ICExaNLFIntegrator::AssembleGradEA(const mfem::Vector& /*x*/, + const mfem::FiniteElementSpace& fes, + mfem::Vector& emat) { + AssembleEA(fes, emat); +} +void ICExaNLFIntegrator::AssembleEA(const mfem::FiniteElementSpace& fes, mfem::Vector& emat) { + CALI_CXX_MARK_SCOPE("icenlfi_assembleEA"); + const mfem::FiniteElement& el = *fes.GetFE(0); + space_dims = el.GetDim(); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + + nqpts = ir->GetNPoints(); + nnodes = el.GetDof(); + nelems = fes.GetNE(); + auto W = ir->GetWeights().Read(); + + if ((space_dims == 1) || (space_dims == 2)) { + MFEM_ABORT("Dimensions of 1 or 2 not supported."); + } + + else { + const int dim = 3; + + const int DIM2 = 2; + const int DIM3 = 3; + const int DIM4 = 4; + + std::array perm4{{3, 2, 1, 0}}; + std::array perm3{{2, 1, 0}}; + std::array perm2{{1, 0}}; + + // bunch of helper RAJA views to make dealing with data easier down below in our kernel. + + // Our field variables that are inputs and outputs + + RAJA::Layout layout_egrads = RAJA::make_permuted_layout({{nnodes, dim, nelems}}, + perm3); + RAJA::View> elem_deriv_shapes_view( + elem_deriv_shapes.Read(), layout_egrads); + + RAJA::Layout layout_tensor = RAJA::make_permuted_layout( + {{2 * dim, 2 * dim, nqpts, nelems}}, perm4); + RAJA::View> K( + m_sim_state->GetQuadratureFunction("tangent_stiffness")->Read(), layout_tensor); + + // Our field variables that are inputs and outputs + RAJA::Layout layout_field = RAJA::make_permuted_layout( + {{nnodes * dim, nnodes * dim, nelems}}, perm3); + RAJA::View> E(emat.ReadWrite(), + layout_field); + + RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{dim, dim, nqpts, nelems}}, + perm4); + RAJA::View> J(jacobian.Read(), + layout_jacob); + + RAJA::Layout layout_adj = RAJA::make_permuted_layout({{dim, dim}}, perm2); + + RAJA::Layout layout_grads = RAJA::make_permuted_layout({{nnodes, dim, nqpts}}, perm3); + RAJA::View> Gt(grad.Read(), + layout_grads); + + const double i3 = 1.0 / 3.0; + const int nqpts_ = nqpts; + const int dim_ = dim; + const int nnodes_ = nnodes; + // This loop we'll want to parallelize the rest are all serial for now. + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i_elems) { + double adj[dim_ * dim_]; + double c_detJ; + double idetJ; + // So, we're going to say this view is constant however we're going to mutate the values + // only in that one scoped section for the quadrature points. + RAJA::View> A(&adj[0], + layout_adj); + for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { + // If we scope this then we only need to carry half the number of variables around + // with us for the adjugate term. + { + const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 + const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 + const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 + const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 + const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 + const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 + const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 + const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 + const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 + const double detJ = J11 * (J22 * J33 - J32 * J23) - + /* */ J21 * (J12 * J33 - J32 * J13) + + /* */ J31 * (J12 * J23 - J22 * J13); + idetJ = 1.0 / detJ; + c_detJ = detJ * W[j_qpts]; + // adj(J) + adj[0] = (J22 * J33) - (J23 * J32); // 0,0 + adj[1] = (J32 * J13) - (J12 * J33); // 0,1 + adj[2] = (J12 * J23) - (J22 * J13); // 0,2 + adj[3] = (J31 * J23) - (J21 * J33); // 1,0 + adj[4] = (J11 * J33) - (J13 * J31); // 1,1 + adj[5] = (J21 * J13) - (J11 * J23); // 1,2 + adj[6] = (J21 * J32) - (J31 * J22); // 2,0 + adj[7] = (J31 * J12) - (J11 * J32); // 2,1 + adj[8] = (J11 * J22) - (J12 * J21); // 2,2 + } + for (int knds = 0; knds < nnodes_; knds++) { + const double bx = idetJ * (Gt(knds, 0, j_qpts) * A(0, 0) + + Gt(knds, 1, j_qpts) * A(0, 1) + + Gt(knds, 2, j_qpts) * A(0, 2)); + + const double by = idetJ * (Gt(knds, 0, j_qpts) * A(1, 0) + + Gt(knds, 1, j_qpts) * A(1, 1) + + Gt(knds, 2, j_qpts) * A(1, 2)); + + const double bz = idetJ * (Gt(knds, 0, j_qpts) * A(2, 0) + + Gt(knds, 1, j_qpts) * A(2, 1) + + Gt(knds, 2, j_qpts) * A(2, 2)); + const double b4 = i3 * (elem_deriv_shapes_view(knds, 0, i_elems) - bx); + const double b5 = b4 + bx; + const double b6 = i3 * (elem_deriv_shapes_view(knds, 1, i_elems) - by); + const double b7 = b6 + by; + const double b8 = i3 * (elem_deriv_shapes_view(knds, 2, i_elems) - bz); + const double b9 = b8 + bz; + + const double k11w = + c_detJ * (b4 * K(1, 1, j_qpts, i_elems) + b4 * K(1, 2, j_qpts, i_elems) + + b5 * K(1, 0, j_qpts, i_elems) + by * K(1, 5, j_qpts, i_elems) + + bz * K(1, 4, j_qpts, i_elems) + b4 * K(2, 1, j_qpts, i_elems) + + b4 * K(2, 2, j_qpts, i_elems) + b5 * K(2, 0, j_qpts, i_elems) + + by * K(2, 5, j_qpts, i_elems) + bz * K(2, 4, j_qpts, i_elems)); + + const double k11x = c_detJ * (b4 * K(0, 1, j_qpts, i_elems) + + b4 * K(0, 2, j_qpts, i_elems) + + b5 * K(0, 0, j_qpts, i_elems) + + by * K(0, 5, j_qpts, i_elems) + + bz * K(0, 4, j_qpts, i_elems)); + + const double k11y = c_detJ * (b4 * K(5, 1, j_qpts, i_elems) + + b4 * K(5, 2, j_qpts, i_elems) + + b5 * K(5, 0, j_qpts, i_elems) + + by * K(5, 5, j_qpts, i_elems) + + bz * K(5, 4, j_qpts, i_elems)); + + const double k11z = c_detJ * (b4 * K(4, 1, j_qpts, i_elems) + + b4 * K(4, 2, j_qpts, i_elems) + + b5 * K(4, 0, j_qpts, i_elems) + + by * K(4, 5, j_qpts, i_elems) + + bz * K(4, 4, j_qpts, i_elems)); + + const double k12w = + c_detJ * (b6 * K(1, 0, j_qpts, i_elems) + b6 * K(1, 2, j_qpts, i_elems) + + b7 * K(1, 1, j_qpts, i_elems) + bx * K(1, 5, j_qpts, i_elems) + + bz * K(1, 3, j_qpts, i_elems) + b6 * K(2, 0, j_qpts, i_elems) + + b6 * K(2, 2, j_qpts, i_elems) + b7 * K(2, 1, j_qpts, i_elems) + + bx * K(2, 5, j_qpts, i_elems) + bz * K(2, 3, j_qpts, i_elems)); + + const double k12x = c_detJ * (b6 * K(0, 0, j_qpts, i_elems) + + b6 * K(0, 2, j_qpts, i_elems) + + b7 * K(0, 1, j_qpts, i_elems) + + bx * K(0, 5, j_qpts, i_elems) + + bz * K(0, 3, j_qpts, i_elems)); + + const double k12y = c_detJ * (b6 * K(5, 0, j_qpts, i_elems) + + b6 * K(5, 2, j_qpts, i_elems) + + b7 * K(5, 1, j_qpts, i_elems) + + bx * K(5, 5, j_qpts, i_elems) + + bz * K(5, 3, j_qpts, i_elems)); + + const double k12z = c_detJ * (b6 * K(4, 0, j_qpts, i_elems) + + b6 * K(4, 2, j_qpts, i_elems) + + b7 * K(4, 1, j_qpts, i_elems) + + bx * K(4, 5, j_qpts, i_elems) + + bz * K(4, 3, j_qpts, i_elems)); + + const double k13w = + c_detJ * (b8 * K(1, 0, j_qpts, i_elems) + b8 * K(1, 1, j_qpts, i_elems) + + b9 * K(1, 2, j_qpts, i_elems) + bx * K(1, 4, j_qpts, i_elems) + + by * K(1, 3, j_qpts, i_elems) + b8 * K(2, 0, j_qpts, i_elems) + + b8 * K(2, 1, j_qpts, i_elems) + b9 * K(2, 2, j_qpts, i_elems) + + bx * K(2, 4, j_qpts, i_elems) + by * K(2, 3, j_qpts, i_elems)); + + const double k13x = c_detJ * (b8 * K(0, 0, j_qpts, i_elems) + + b8 * K(0, 1, j_qpts, i_elems) + + b9 * K(0, 2, j_qpts, i_elems) + + bx * K(0, 4, j_qpts, i_elems) + + by * K(0, 3, j_qpts, i_elems)); + + const double k13y = c_detJ * (b8 * K(5, 0, j_qpts, i_elems) + + b8 * K(5, 1, j_qpts, i_elems) + + b9 * K(5, 2, j_qpts, i_elems) + + bx * K(5, 4, j_qpts, i_elems) + + by * K(5, 3, j_qpts, i_elems)); + + const double k13z = c_detJ * (b8 * K(4, 0, j_qpts, i_elems) + + b8 * K(4, 1, j_qpts, i_elems) + + b9 * K(4, 2, j_qpts, i_elems) + + bx * K(4, 4, j_qpts, i_elems) + + by * K(4, 3, j_qpts, i_elems)); + + const double k21w = + c_detJ * (b4 * K(0, 1, j_qpts, i_elems) + b4 * K(0, 2, j_qpts, i_elems) + + b5 * K(0, 0, j_qpts, i_elems) + by * K(0, 5, j_qpts, i_elems) + + bz * K(0, 4, j_qpts, i_elems) + b4 * K(2, 1, j_qpts, i_elems) + + b4 * K(2, 2, j_qpts, i_elems) + b5 * K(2, 0, j_qpts, i_elems) + + by * K(2, 5, j_qpts, i_elems) + bz * K(2, 4, j_qpts, i_elems)); + + const double k21x = c_detJ * (b4 * K(1, 1, j_qpts, i_elems) + + b4 * K(1, 2, j_qpts, i_elems) + + b5 * K(1, 0, j_qpts, i_elems) + + by * K(1, 5, j_qpts, i_elems) + + bz * K(1, 4, j_qpts, i_elems)); + + const double k21y = c_detJ * (b4 * K(5, 1, j_qpts, i_elems) + + b4 * K(5, 2, j_qpts, i_elems) + + b5 * K(5, 0, j_qpts, i_elems) + + by * K(5, 5, j_qpts, i_elems) + + bz * K(5, 4, j_qpts, i_elems)); + + const double k21z = c_detJ * (b4 * K(3, 1, j_qpts, i_elems) + + b4 * K(3, 2, j_qpts, i_elems) + + b5 * K(3, 0, j_qpts, i_elems) + + by * K(3, 5, j_qpts, i_elems) + + bz * K(3, 4, j_qpts, i_elems)); + + const double k22w = + c_detJ * (b6 * K(0, 0, j_qpts, i_elems) + b6 * K(0, 2, j_qpts, i_elems) + + b7 * K(0, 1, j_qpts, i_elems) + bx * K(0, 5, j_qpts, i_elems) + + bz * K(0, 3, j_qpts, i_elems) + b6 * K(2, 0, j_qpts, i_elems) + + b6 * K(2, 2, j_qpts, i_elems) + b7 * K(2, 1, j_qpts, i_elems) + + bx * K(2, 5, j_qpts, i_elems) + bz * K(2, 3, j_qpts, i_elems)); + + const double k22x = c_detJ * (b6 * K(1, 0, j_qpts, i_elems) + + b6 * K(1, 2, j_qpts, i_elems) + + b7 * K(1, 1, j_qpts, i_elems) + + bx * K(1, 5, j_qpts, i_elems) + + bz * K(1, 3, j_qpts, i_elems)); + + const double k22y = c_detJ * (b6 * K(5, 0, j_qpts, i_elems) + + b6 * K(5, 2, j_qpts, i_elems) + + b7 * K(5, 1, j_qpts, i_elems) + + bx * K(5, 5, j_qpts, i_elems) + + bz * K(5, 3, j_qpts, i_elems)); + + const double k22z = c_detJ * (b6 * K(3, 0, j_qpts, i_elems) + + b6 * K(3, 2, j_qpts, i_elems) + + b7 * K(3, 1, j_qpts, i_elems) + + bx * K(3, 5, j_qpts, i_elems) + + bz * K(3, 3, j_qpts, i_elems)); + + const double k23w = + c_detJ * (b8 * K(0, 0, j_qpts, i_elems) + b8 * K(0, 1, j_qpts, i_elems) + + b9 * K(0, 2, j_qpts, i_elems) + bx * K(0, 4, j_qpts, i_elems) + + by * K(0, 3, j_qpts, i_elems) + b8 * K(2, 0, j_qpts, i_elems) + + b8 * K(2, 1, j_qpts, i_elems) + b9 * K(2, 2, j_qpts, i_elems) + + bx * K(2, 4, j_qpts, i_elems) + by * K(2, 3, j_qpts, i_elems)); + + const double k23x = c_detJ * (b8 * K(1, 0, j_qpts, i_elems) + + b8 * K(1, 1, j_qpts, i_elems) + + b9 * K(1, 2, j_qpts, i_elems) + + bx * K(1, 4, j_qpts, i_elems) + + by * K(1, 3, j_qpts, i_elems)); + + const double k23y = c_detJ * (b8 * K(5, 0, j_qpts, i_elems) + + b8 * K(5, 1, j_qpts, i_elems) + + b9 * K(5, 2, j_qpts, i_elems) + + bx * K(5, 4, j_qpts, i_elems) + + by * K(5, 3, j_qpts, i_elems)); + + const double k23z = c_detJ * (b8 * K(3, 0, j_qpts, i_elems) + + b8 * K(3, 1, j_qpts, i_elems) + + b9 * K(3, 2, j_qpts, i_elems) + + bx * K(3, 4, j_qpts, i_elems) + + by * K(3, 3, j_qpts, i_elems)); + + const double k31w = + c_detJ * (b4 * K(0, 1, j_qpts, i_elems) + b4 * K(0, 2, j_qpts, i_elems) + + b5 * K(0, 0, j_qpts, i_elems) + by * K(0, 5, j_qpts, i_elems) + + bz * K(0, 4, j_qpts, i_elems) + b4 * K(1, 1, j_qpts, i_elems) + + b4 * K(1, 2, j_qpts, i_elems) + b5 * K(1, 0, j_qpts, i_elems) + + by * K(1, 5, j_qpts, i_elems) + bz * K(1, 4, j_qpts, i_elems)); + + const double k31x = c_detJ * (b4 * K(2, 1, j_qpts, i_elems) + + b4 * K(2, 2, j_qpts, i_elems) + + b5 * K(2, 0, j_qpts, i_elems) + + by * K(2, 5, j_qpts, i_elems) + + bz * K(2, 4, j_qpts, i_elems)); + + const double k31y = c_detJ * (b4 * K(4, 1, j_qpts, i_elems) + + b4 * K(4, 2, j_qpts, i_elems) + + b5 * K(4, 0, j_qpts, i_elems) + + by * K(4, 5, j_qpts, i_elems) + + bz * K(4, 4, j_qpts, i_elems)); + + const double k31z = c_detJ * (b4 * K(3, 1, j_qpts, i_elems) + + b4 * K(3, 2, j_qpts, i_elems) + + b5 * K(3, 0, j_qpts, i_elems) + + by * K(3, 5, j_qpts, i_elems) + + bz * K(3, 4, j_qpts, i_elems)); + + const double k32w = + c_detJ * (b6 * K(0, 0, j_qpts, i_elems) + b6 * K(0, 2, j_qpts, i_elems) + + b7 * K(0, 1, j_qpts, i_elems) + bx * K(0, 5, j_qpts, i_elems) + + bz * K(0, 3, j_qpts, i_elems) + b6 * K(1, 0, j_qpts, i_elems) + + b6 * K(1, 2, j_qpts, i_elems) + b7 * K(1, 1, j_qpts, i_elems) + + bx * K(1, 5, j_qpts, i_elems) + bz * K(1, 3, j_qpts, i_elems)); + + const double k32x = c_detJ * (b6 * K(2, 0, j_qpts, i_elems) + + b6 * K(2, 2, j_qpts, i_elems) + + b7 * K(2, 1, j_qpts, i_elems) + + bx * K(2, 5, j_qpts, i_elems) + + bz * K(2, 3, j_qpts, i_elems)); + + const double k32y = c_detJ * (b6 * K(4, 0, j_qpts, i_elems) + + b6 * K(4, 2, j_qpts, i_elems) + + b7 * K(4, 1, j_qpts, i_elems) + + bx * K(4, 5, j_qpts, i_elems) + + bz * K(4, 3, j_qpts, i_elems)); + + const double k32z = c_detJ * (b6 * K(3, 0, j_qpts, i_elems) + + b6 * K(3, 2, j_qpts, i_elems) + + b7 * K(3, 1, j_qpts, i_elems) + + bx * K(3, 5, j_qpts, i_elems) + + bz * K(3, 3, j_qpts, i_elems)); + + const double k33w = + c_detJ * (b8 * K(0, 0, j_qpts, i_elems) + b8 * K(0, 1, j_qpts, i_elems) + + b9 * K(0, 2, j_qpts, i_elems) + bx * K(0, 4, j_qpts, i_elems) + + by * K(0, 3, j_qpts, i_elems) + b8 * K(1, 0, j_qpts, i_elems) + + b8 * K(1, 1, j_qpts, i_elems) + b9 * K(1, 2, j_qpts, i_elems) + + bx * K(1, 4, j_qpts, i_elems) + by * K(1, 3, j_qpts, i_elems)); + + const double k33x = c_detJ * (b8 * K(2, 0, j_qpts, i_elems) + + b8 * K(2, 1, j_qpts, i_elems) + + b9 * K(2, 2, j_qpts, i_elems) + + bx * K(2, 4, j_qpts, i_elems) + + by * K(2, 3, j_qpts, i_elems)); + + const double k33y = c_detJ * (b8 * K(4, 0, j_qpts, i_elems) + + b8 * K(4, 1, j_qpts, i_elems) + + b9 * K(4, 2, j_qpts, i_elems) + + bx * K(4, 4, j_qpts, i_elems) + + by * K(4, 3, j_qpts, i_elems)); + + const double k33z = c_detJ * (b8 * K(3, 0, j_qpts, i_elems) + + b8 * K(3, 1, j_qpts, i_elems) + + b9 * K(3, 2, j_qpts, i_elems) + + bx * K(3, 4, j_qpts, i_elems) + + by * K(3, 3, j_qpts, i_elems)); + + for (int lnds = 0; lnds < nnodes_; lnds++) { + const double gx = idetJ * (Gt(lnds, 0, j_qpts) * A(0, 0) + + Gt(lnds, 1, j_qpts) * A(0, 1) + + Gt(lnds, 2, j_qpts) * A(0, 2)); + + const double gy = idetJ * (Gt(lnds, 0, j_qpts) * A(1, 0) + + Gt(lnds, 1, j_qpts) * A(1, 1) + + Gt(lnds, 2, j_qpts) * A(1, 2)); + + const double gz = idetJ * (Gt(lnds, 0, j_qpts) * A(2, 0) + + Gt(lnds, 1, j_qpts) * A(2, 1) + + Gt(lnds, 2, j_qpts) * A(2, 2)); + + const double g4 = i3 * (elem_deriv_shapes_view(lnds, 0, i_elems) - gx); + const double g5 = g4 + gx; + const double g6 = i3 * (elem_deriv_shapes_view(lnds, 1, i_elems) - gy); + const double g7 = g6 + gy; + const double g8 = i3 * (elem_deriv_shapes_view(lnds, 2, i_elems) - gz); + const double g9 = g8 + gz; + + E(lnds, knds, i_elems) += g4 * k11w + g5 * k11x + gy * k11y + gz * k11z; + E(lnds, knds + nnodes_, i_elems) += g4 * k12w + g5 * k12x + gy * k12y + + gz * k12z; + E(lnds, knds + 2 * nnodes_, i_elems) += g4 * k13w + g5 * k13x + gy * k13y + + gz * k13z; + + E(lnds + nnodes_, knds, i_elems) += g6 * k21w + g7 * k21x + gx * k21y + + gz * k21z; + E(lnds + nnodes_, knds + nnodes_, i_elems) += g6 * k22w + g7 * k22x + + gx * k22y + gz * k22z; + E(lnds + nnodes_, knds + 2 * nnodes_, i_elems) += g6 * k23w + g7 * k23x + + gx * k23y + gz * k23z; + + E(lnds + 2 * nnodes_, knds, i_elems) += g8 * k31w + g9 * k31x + gx * k31y + + gy * k31z; + E(lnds + 2 * nnodes_, knds + nnodes_, i_elems) += g8 * k32w + g9 * k32x + + gx * k32y + gy * k32z; + E(lnds + 2 * nnodes_, knds + 2 * nnodes_, i_elems) += g8 * k33w + + g9 * k33x + + gx * k33y + gy * k33z; + } + } + } + }); + } +} + +// This assembles the diagonal of our LHS which can be used as a preconditioner +void ICExaNLFIntegrator::AssembleGradDiagonalPA(mfem::Vector& diag) const { + CALI_CXX_MARK_SCOPE("icenlfi_AssembleGradDiagonalPA"); + + const mfem::IntegrationRule& ir = + m_sim_state->GetQuadratureFunction("tangent_stiffness")->GetSpaceShared()->GetIntRule(0); + auto W = ir.GetWeights().Read(); + + if ((space_dims == 1) || (space_dims == 2)) { + MFEM_ABORT("Dimensions of 1 or 2 not supported."); + } else { + const int dim = 3; + + const int DIM2 = 2; + const int DIM3 = 3; + const int DIM4 = 4; + + // bunch of helper RAJA views to make dealing with data easier down below in our kernel. + + std::array perm4{{3, 2, 1, 0}}; + std::array perm3{{2, 1, 0}}; + std::array perm2{{1, 0}}; + + // bunch of helper RAJA views to make dealing with data easier down below in our kernel. + + RAJA::Layout layout_tensor = RAJA::make_permuted_layout( + {{2 * dim, 2 * dim, nqpts, nelems}}, perm4); + RAJA::View> K( + m_sim_state->GetQuadratureFunction("tangent_stiffness")->Read(), layout_tensor); + + // Our field variables that are inputs and outputs + RAJA::Layout layout_field = RAJA::make_permuted_layout({{nnodes, dim, nelems}}, + perm3); + RAJA::View> Y(diag.ReadWrite(), + layout_field); + + RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{dim, dim, nqpts, nelems}}, + perm4); + RAJA::View> J(jacobian.Read(), + layout_jacob); + + RAJA::Layout layout_adj = RAJA::make_permuted_layout({{dim, dim}}, perm2); + + RAJA::Layout layout_grads = RAJA::make_permuted_layout({{nnodes, dim, nqpts}}, perm3); + RAJA::View> Gt(grad.Read(), + layout_grads); + + RAJA::Layout layout_egrads = RAJA::make_permuted_layout({{nnodes, dim, nelems}}, + perm3); + RAJA::View> elem_deriv_shapes_view( + elem_deriv_shapes.Read(), layout_egrads); + + const double i3 = 1.0 / 3.0; + const int nqpts_ = nqpts; + const int dim_ = dim; + const int nnodes_ = nnodes; + // This loop we'll want to parallelize the rest are all serial for now. + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i_elems) { + double adj[dim_ * dim_]; + double c_detJ; + double idetJ; + // So, we're going to say this view is constant however we're going to mutate the values + // only in that one scoped section for the quadrature points. + RAJA::View> A(&adj[0], + layout_adj); + for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { + // If we scope this then we only need to carry half the number of variables around + // with us for the adjugate term. + { + const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 + const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 + const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 + const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 + const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 + const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 + const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 + const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 + const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 + const double detJ = J11 * (J22 * J33 - J32 * J23) - + /* */ J21 * (J12 * J33 - J32 * J13) + + /* */ J31 * (J12 * J23 - J22 * J13); + idetJ = 1.0 / detJ; + c_detJ = detJ * W[j_qpts]; + // adj(J) + adj[0] = (J22 * J33) - (J23 * J32); // 0,0 + adj[1] = (J32 * J13) - (J12 * J33); // 0,1 + adj[2] = (J12 * J23) - (J22 * J13); // 0,2 + adj[3] = (J31 * J23) - (J21 * J33); // 1,0 + adj[4] = (J11 * J33) - (J13 * J31); // 1,1 + adj[5] = (J21 * J13) - (J11 * J23); // 1,2 + adj[6] = (J21 * J32) - (J31 * J22); // 2,0 + adj[7] = (J31 * J12) - (J11 * J32); // 2,1 + adj[8] = (J11 * J22) - (J12 * J21); // 2,2 + } + for (int knds = 0; knds < nnodes_; knds++) { + const double bx = idetJ * (Gt(knds, 0, j_qpts) * A(0, 0) + + Gt(knds, 1, j_qpts) * A(0, 1) + + Gt(knds, 2, j_qpts) * A(0, 2)); + + const double by = idetJ * (Gt(knds, 0, j_qpts) * A(1, 0) + + Gt(knds, 1, j_qpts) * A(1, 1) + + Gt(knds, 2, j_qpts) * A(1, 2)); + + const double bz = idetJ * (Gt(knds, 0, j_qpts) * A(2, 0) + + Gt(knds, 1, j_qpts) * A(2, 1) + + Gt(knds, 2, j_qpts) * A(2, 2)); + const double b4 = i3 * (elem_deriv_shapes_view(knds, 0, i_elems) - bx); + const double b5 = b4 + bx; + const double b6 = i3 * (elem_deriv_shapes_view(knds, 1, i_elems) - by); + const double b7 = b6 + by; + const double b8 = i3 * (elem_deriv_shapes_view(knds, 2, i_elems) - bz); + const double b9 = b8 + bz; + + const double k11w = + c_detJ * (b4 * K(1, 1, j_qpts, i_elems) + b4 * K(1, 2, j_qpts, i_elems) + + b5 * K(1, 0, j_qpts, i_elems) + by * K(1, 5, j_qpts, i_elems) + + bz * K(1, 4, j_qpts, i_elems) + b4 * K(2, 1, j_qpts, i_elems) + + b4 * K(2, 2, j_qpts, i_elems) + b5 * K(2, 0, j_qpts, i_elems) + + by * K(2, 5, j_qpts, i_elems) + bz * K(2, 4, j_qpts, i_elems)); + + const double k11x = c_detJ * (b4 * K(0, 1, j_qpts, i_elems) + + b4 * K(0, 2, j_qpts, i_elems) + + b5 * K(0, 0, j_qpts, i_elems) + + by * K(0, 5, j_qpts, i_elems) + + bz * K(0, 4, j_qpts, i_elems)); + + const double k11y = c_detJ * (b4 * K(5, 1, j_qpts, i_elems) + + b4 * K(5, 2, j_qpts, i_elems) + + b5 * K(5, 0, j_qpts, i_elems) + + by * K(5, 5, j_qpts, i_elems) + + bz * K(5, 4, j_qpts, i_elems)); + + const double k11z = c_detJ * (b4 * K(4, 1, j_qpts, i_elems) + + b4 * K(4, 2, j_qpts, i_elems) + + b5 * K(4, 0, j_qpts, i_elems) + + by * K(4, 5, j_qpts, i_elems) + + bz * K(4, 4, j_qpts, i_elems)); + + const double k22w = + c_detJ * (b6 * K(0, 0, j_qpts, i_elems) + b6 * K(0, 2, j_qpts, i_elems) + + b7 * K(0, 1, j_qpts, i_elems) + bx * K(0, 5, j_qpts, i_elems) + + bz * K(0, 3, j_qpts, i_elems) + b6 * K(2, 0, j_qpts, i_elems) + + b6 * K(2, 2, j_qpts, i_elems) + b7 * K(2, 1, j_qpts, i_elems) + + bx * K(2, 5, j_qpts, i_elems) + bz * K(2, 3, j_qpts, i_elems)); + + const double k22x = c_detJ * (b6 * K(1, 0, j_qpts, i_elems) + + b6 * K(1, 2, j_qpts, i_elems) + + b7 * K(1, 1, j_qpts, i_elems) + + bx * K(1, 5, j_qpts, i_elems) + + bz * K(1, 3, j_qpts, i_elems)); + + const double k22y = c_detJ * (b6 * K(5, 0, j_qpts, i_elems) + + b6 * K(5, 2, j_qpts, i_elems) + + b7 * K(5, 1, j_qpts, i_elems) + + bx * K(5, 5, j_qpts, i_elems) + + bz * K(5, 3, j_qpts, i_elems)); + + const double k22z = c_detJ * (b6 * K(3, 0, j_qpts, i_elems) + + b6 * K(3, 2, j_qpts, i_elems) + + b7 * K(3, 1, j_qpts, i_elems) + + bx * K(3, 5, j_qpts, i_elems) + + bz * K(3, 3, j_qpts, i_elems)); + + const double k33w = + c_detJ * (b8 * K(0, 0, j_qpts, i_elems) + b8 * K(0, 1, j_qpts, i_elems) + + b9 * K(0, 2, j_qpts, i_elems) + bx * K(0, 4, j_qpts, i_elems) + + by * K(0, 3, j_qpts, i_elems) + b8 * K(1, 0, j_qpts, i_elems) + + b8 * K(1, 1, j_qpts, i_elems) + b9 * K(1, 2, j_qpts, i_elems) + + bx * K(1, 4, j_qpts, i_elems) + by * K(1, 3, j_qpts, i_elems)); + + const double k33x = c_detJ * (b8 * K(2, 0, j_qpts, i_elems) + + b8 * K(2, 1, j_qpts, i_elems) + + b9 * K(2, 2, j_qpts, i_elems) + + bx * K(2, 4, j_qpts, i_elems) + + by * K(2, 3, j_qpts, i_elems)); + + const double k33y = c_detJ * (b8 * K(4, 0, j_qpts, i_elems) + + b8 * K(4, 1, j_qpts, i_elems) + + b9 * K(4, 2, j_qpts, i_elems) + + bx * K(4, 4, j_qpts, i_elems) + + by * K(4, 3, j_qpts, i_elems)); + + const double k33z = c_detJ * (b8 * K(3, 0, j_qpts, i_elems) + + b8 * K(3, 1, j_qpts, i_elems) + + b9 * K(3, 2, j_qpts, i_elems) + + bx * K(3, 4, j_qpts, i_elems) + + by * K(3, 3, j_qpts, i_elems)); + + Y(knds, 0, i_elems) += b4 * k11w + b5 * k11x + by * k11y + bz * k11z; + Y(knds, 1, i_elems) += b6 * k22w + b7 * k22x + bx * k22y + bz * k22z; + Y(knds, 2, i_elems) += b8 * k33w + b9 * k33x + bx * k33y + by * k33z; + } + } + }); + } +} + +// This performs the assembly step of our RHS side of our system: +// f_ik = +void ICExaNLFIntegrator::AssemblePA(const mfem::FiniteElementSpace& fes) { + CALI_CXX_MARK_SCOPE("icenlfi_assemblePA"); + mfem::Mesh* mesh = fes.GetMesh(); + const mfem::FiniteElement& el = *fes.GetFE(0); + space_dims = el.GetDim(); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + + nqpts = ir->GetNPoints(); + nnodes = el.GetDof(); + nelems = fes.GetNE(); + + auto W = ir->GetWeights().Read(); + geom = mesh->GetGeometricFactors(*ir, mfem::GeometricFactors::JACOBIANS); + + if ((space_dims == 1) || (space_dims == 2)) { + MFEM_ABORT("Dimensions of 1 or 2 not supported."); + } else { + const int dim = 3; + + if (grad.Size() != (nqpts * dim * nnodes)) { + grad.SetSize(nqpts * dim * nnodes, mfem::Device::GetMemoryType()); + { + mfem::DenseMatrix DSh; + const int offset = nnodes * dim; + double* qpts_dshape_data = grad.HostReadWrite(); + for (int i = 0; i < nqpts; i++) { + const mfem::IntegrationPoint& ip = ir->IntPoint(i); + DSh.UseExternalData(&qpts_dshape_data[offset * i], nnodes, dim); + el.CalcDShape(ip, DSh); + } + } + grad.UseDevice(true); + } + + if (elem_deriv_shapes.Size() != (nnodes * dim * nelems)) { + elem_deriv_shapes.SetSize(nnodes * space_dims * nelems, mfem::Device::GetMemoryType()); + elem_deriv_shapes.UseDevice(); + } + + elem_deriv_shapes = 0.0; + + // geom->J really isn't going to work for us as of right now. We could just reorder it + // to the version that we want it to be in instead... + if (jacobian.Size() != (dim * dim * nqpts * nelems)) { + jacobian.SetSize(dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); + jacobian.UseDevice(true); + } + + const int DIM2 = 2; + const int DIM3 = 3; + const int DIM4 = 4; + std::array perm4{{3, 2, 1, 0}}; + std::array perm3{{2, 1, 0}}; + std::array perm2{{1, 0}}; + + RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{dim, dim, nqpts, nelems}}, + perm4); + RAJA::View> J(jacobian.ReadWrite(), + layout_jacob); + + RAJA::Layout layout_geom = RAJA::make_permuted_layout({{nqpts, dim, dim, nelems}}, + perm4); + RAJA::View> geom_j_view( + geom->J.Read(), layout_geom); + + RAJA::Layout layout_egrads = RAJA::make_permuted_layout({{nnodes, dim, nelems}}, + perm3); + RAJA::View> elem_deriv_shapes_view( + elem_deriv_shapes.ReadWrite(), layout_egrads); + + // Transpose of the local gradient variable + RAJA::Layout layout_grads = RAJA::make_permuted_layout({{nnodes, dim, nqpts}}, perm3); + RAJA::View> Gt(grad.Read(), + layout_grads); + + RAJA::Layout layout_adj = RAJA::make_permuted_layout({{dim, dim}}, perm2); + const int nqpts_ = nqpts; + const int dim_ = dim; + const int nnodes_ = nnodes; + + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i) { + for (int j = 0; j < nqpts_; j++) { + for (int k = 0; k < dim_; k++) { + for (int l = 0; l < dim_; l++) { + J(l, k, j, i) = geom_j_view(j, l, k, i); + } + } + } + }); + + // This loop we'll want to parallelize the rest are all serial for now. + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i_elems) { + double adj[dim_ * dim_]; + double c_detJ; + double volume = 0.0; + // So, we're going to say this view is constant however we're going to mutate the values + // only in that one scoped section for the quadrature points. + RAJA::View> A(&adj[0], + layout_adj); + for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { + // If we scope this then we only need to carry half the number of variables around + // with us for the adjugate term. + { + const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 + const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 + const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 + const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 + const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 + const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 + const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 + const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 + const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 + const double detJ = J11 * (J22 * J33 - J32 * J23) - + /* */ J21 * (J12 * J33 - J32 * J13) + + /* */ J31 * (J12 * J23 - J22 * J13); + c_detJ = W[j_qpts]; + volume += c_detJ * detJ; + // adj(J) + adj[0] = (J22 * J33) - (J23 * J32); // 0,0 + adj[1] = (J32 * J13) - (J12 * J33); // 0,1 + adj[2] = (J12 * J23) - (J22 * J13); // 0,2 + adj[3] = (J31 * J23) - (J21 * J33); // 1,0 + adj[4] = (J11 * J33) - (J13 * J31); // 1,1 + adj[5] = (J21 * J13) - (J11 * J23); // 1,2 + adj[6] = (J21 * J32) - (J31 * J22); // 2,0 + adj[7] = (J31 * J12) - (J11 * J32); // 2,1 + adj[8] = (J11 * J22) - (J12 * J21); // 2,2 + } + for (int knds = 0; knds < nnodes_; knds++) { + elem_deriv_shapes_view(knds, 0, i_elems) += c_detJ * + (Gt(knds, 0, j_qpts) * A(0, 0) + + Gt(knds, 1, j_qpts) * A(0, 1) + + Gt(knds, 2, j_qpts) * A(0, 2)); + + elem_deriv_shapes_view(knds, 1, i_elems) += c_detJ * + (Gt(knds, 0, j_qpts) * A(1, 0) + + Gt(knds, 1, j_qpts) * A(1, 1) + + Gt(knds, 2, j_qpts) * A(1, 2)); + + elem_deriv_shapes_view(knds, 2, i_elems) += c_detJ * + (Gt(knds, 0, j_qpts) * A(2, 0) + + Gt(knds, 1, j_qpts) * A(2, 1) + + Gt(knds, 2, j_qpts) * A(2, 2)); + } // End of nnodes + } // End of nqpts + + double ivol = 1.0 / volume; + + for (int knds = 0; knds < nnodes_; knds++) { + elem_deriv_shapes_view(knds, 0, i_elems) *= ivol; + elem_deriv_shapes_view(knds, 1, i_elems) *= ivol; + elem_deriv_shapes_view(knds, 2, i_elems) *= ivol; + } + }); // End of mfem::MFEM_FORALL + + } // End of space dims if else +} + +// Here we're applying the following action operation using the assembled "D" 2nd order +// tensor found above: +// y_{ik} = \nabla_{ij}\phi^T_{\epsilon} D_{jk} +void ICExaNLFIntegrator::AddMultPA(const mfem::Vector& /*x*/, mfem::Vector& y) const { + CALI_CXX_MARK_SCOPE("icenlfi_amPAV"); + + // return a pointer to beginning step stress. This is used for output visualization + auto stress_end = m_sim_state->GetQuadratureFunction("cauchy_stress_end"); + + const mfem::IntegrationRule& ir = + m_sim_state->GetQuadratureFunction("tangent_stiffness")->GetSpaceShared()->GetIntRule(0); + auto W = ir.GetWeights().Read(); + + if ((space_dims == 1) || (space_dims == 2)) { + MFEM_ABORT("Dimensions of 1 or 2 not supported."); + } else { + const int dim = 3; + const int DIM2 = 2; + const int DIM3 = 3; + const int DIM4 = 4; + + std::array perm4{{3, 2, 1, 0}}; + std::array perm3{{2, 1, 0}}; + std::array perm2{{1, 0}}; + + RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{dim, dim, nqpts, nelems}}, + perm4); + RAJA::View> J(jacobian.Read(), + layout_jacob); + + RAJA::Layout layout_stress = RAJA::make_permuted_layout({{2 * dim, nqpts, nelems}}, + perm3); + RAJA::View> S(stress_end->ReadWrite(), + layout_stress); + + // Our field variables that are inputs and outputs + RAJA::Layout layout_field = RAJA::make_permuted_layout({{nnodes, dim, nelems}}, + perm3); + RAJA::View> Y(y.ReadWrite(), layout_field); + // Transpose of the local gradient variable + RAJA::Layout layout_grads = RAJA::make_permuted_layout({{nnodes, dim, nqpts}}, perm3); + RAJA::View> Gt(grad.Read(), + layout_grads); + + RAJA::Layout layout_egrads = RAJA::make_permuted_layout({{nnodes, dim, nelems}}, + perm3); + RAJA::View> elem_deriv_shapes_view( + elem_deriv_shapes.Read(), layout_egrads); + + RAJA::Layout layout_adj = RAJA::make_permuted_layout({{dim, dim}}, perm2); + + const double i3 = 1.0 / 3.0; + const int nqpts_ = nqpts; + const int dim_ = dim; + const int nnodes_ = nnodes; + + // This loop we'll want to parallelize the rest are all serial for now. + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i_elems) { + double adj[dim_ * dim_]; + double c_detJ; + double idetJ; + // So, we're going to say this view is constant however we're going to mutate the values + // only in that one scoped section for the quadrature points. + RAJA::View> A(&adj[0], + layout_adj); + for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { + // If we scope this then we only need to carry half the number of variables around + // with us for the adjugate term. + { + const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 + const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 + const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 + const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 + const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 + const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 + const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 + const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 + const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 + const double detJ = J11 * (J22 * J33 - J32 * J23) - + /* */ J21 * (J12 * J33 - J32 * J13) + + /* */ J31 * (J12 * J23 - J22 * J13); + idetJ = 1.0 / detJ; + c_detJ = detJ * W[j_qpts]; + // adj(J) + adj[0] = (J22 * J33) - (J23 * J32); // 0,0 + adj[1] = (J32 * J13) - (J12 * J33); // 0,1 + adj[2] = (J12 * J23) - (J22 * J13); // 0,2 + adj[3] = (J31 * J23) - (J21 * J33); // 1,0 + adj[4] = (J11 * J33) - (J13 * J31); // 1,1 + adj[5] = (J21 * J13) - (J11 * J23); // 1,2 + adj[6] = (J21 * J32) - (J31 * J22); // 2,0 + adj[7] = (J31 * J12) - (J11 * J32); // 2,1 + adj[8] = (J11 * J22) - (J12 * J21); // 2,2 + } + for (int knds = 0; knds < nnodes_; knds++) { + const double bx = idetJ * (Gt(knds, 0, j_qpts) * A(0, 0) + + Gt(knds, 1, j_qpts) * A(0, 1) + + Gt(knds, 2, j_qpts) * A(0, 2)); + + const double by = idetJ * (Gt(knds, 0, j_qpts) * A(1, 0) + + Gt(knds, 1, j_qpts) * A(1, 1) + + Gt(knds, 2, j_qpts) * A(1, 2)); + + const double bz = idetJ * (Gt(knds, 0, j_qpts) * A(2, 0) + + Gt(knds, 1, j_qpts) * A(2, 1) + + Gt(knds, 2, j_qpts) * A(2, 2)); + + const double b4 = i3 * (elem_deriv_shapes_view(knds, 0, i_elems) - bx); + const double b5 = b4 + bx; + const double b6 = i3 * (elem_deriv_shapes_view(knds, 1, i_elems) - by); + const double b7 = b6 + by; + const double b8 = i3 * (elem_deriv_shapes_view(knds, 2, i_elems) - bz); + const double b9 = b8 + bz; + + Y(knds, 0, i_elems) += c_detJ * (b4 * S(1, j_qpts, i_elems) + + b4 * S(2, j_qpts, i_elems) + + b5 * S(0, j_qpts, i_elems) + + by * S(5, j_qpts, i_elems) + + bz * S(4, j_qpts, i_elems)); + + Y(knds, 1, i_elems) += c_detJ * (b6 * S(0, j_qpts, i_elems) + + b6 * S(2, j_qpts, i_elems) + + b7 * S(1, j_qpts, i_elems) + + bx * S(5, j_qpts, i_elems) + + bz * S(3, j_qpts, i_elems)); + + Y(knds, 2, i_elems) += c_detJ * (b8 * S(0, j_qpts, i_elems) + + b8 * S(1, j_qpts, i_elems) + + b9 * S(2, j_qpts, i_elems) + + bx * S(4, j_qpts, i_elems) + + by * S(3, j_qpts, i_elems)); + } // End of nnodes + } // End of nQpts + }); // End of nelems + } // End of if statement +} diff --git a/src/fem_operators/mechanics_integrators.hpp b/src/fem_operators/mechanics_integrators.hpp new file mode 100644 index 0000000..fb7d4f7 --- /dev/null +++ b/src/fem_operators/mechanics_integrators.hpp @@ -0,0 +1,938 @@ +#ifndef MECHANICS_INTEG +#define MECHANICS_INTEG + +#include "sim_state/simulation_state.hpp" + +#include "mfem.hpp" + +#include +#include +#include + +/** + * @brief Nonlinear form integrator for general solid mechanics problems with material model + * integration. + * + * ExaNLFIntegrator implements a comprehensive finite element integrator specifically designed + * for ExaConstit's solid mechanics applications, including crystal plasticity, large deformation + * mechanics, and general material model integration. This integrator serves as the foundation + * for nonlinear finite element assembly operations in updated Lagrangian formulations. + * + * The integrator provides: + * - Element vector assembly for residual computation (internal forces) + * - Element matrix assembly for Jacobian computation (tangent stiffness) + * - Partial assembly (PA) operations for memory-efficient matrix-free methods + * - Element assembly (EA) operations for minimal memory usage + * - Device-compatible implementations for CPU and GPU execution + * + * Key features for crystal plasticity and micromechanics: + * - Integration with ExaConstit's material model framework + * - Support for heterogeneous material regions through SimulationState + * - Quadrature function data access for stress and tangent stiffness + * - Optimized assembly operations for large-scale simulations + * - Compatibility with MFEM's assembly level abstractions + * + * Assembly strategy support: + * - Traditional element-wise assembly for small problems + * - Partial assembly for memory-efficient large-scale problems + * - Element assembly for memory-constrained environments + * - Mixed assembly strategies for heterogeneous hardware + * + * The integrator coordinates with SimulationState to access: + * - Current stress tensors from material model evaluations + * - Material tangent stiffness matrices for linearization + * - Geometric data for coordinate transformations + * - Quadrature point data for integration operations + * + * @ingroup ExaConstit_fem_operators + */ +class ExaNLFIntegrator : public mfem::NonlinearFormIntegrator { +protected: + /** @brief Reference to simulation state for accessing mesh, fields, and material data */ + std::shared_ptr m_sim_state; + + /** @brief Working vector for material data storage during assembly operations */ + mfem::Vector dmat; + + /** @brief Gradient data vector for partial assembly operations */ + mfem::Vector grad; + + /** @brief Partial assembly material data vector */ + mfem::Vector pa_mat; + + /** @brief Partial assembly diagonal material data vector */ + mfem::Vector pa_dmat; + + /** @brief Jacobian transformation data vector for geometric operations */ + mfem::Vector jacobian; + + /** @brief Geometric factors for mesh transformation operations (not owned) */ + const mfem::GeometricFactors* geom; // Not owned + + /** @brief Spatial dimension of the finite element problem */ + int space_dims; + + /** @brief Number of finite elements in the mesh */ + int nelems; + + /** @brief Number of quadrature points per element */ + int nqpts; + + /** @brief Number of nodes (degrees of freedom) per element */ + int nnodes; + +public: + /** + * @brief Construct integrator with simulation state reference. + * + * @param sim_state Reference to simulation state containing mesh, fields, and material data + * + * Initializes the nonlinear form integrator with access to the simulation state, + * enabling integration with ExaConstit's material model framework and data management. + * The integrator is ready for element assembly operations upon construction. + * + * The constructor establishes: + * - Reference to simulation state for data access + * - Foundation for subsequent assembly strategy configuration + * - Integration with MFEM's NonlinearFormIntegrator interface + * + * @note Simulation state reference must remain valid for integrator lifetime + * @note Working vectors are allocated lazily during first assembly operations + */ + ExaNLFIntegrator(std::shared_ptr sim_state) : m_sim_state(sim_state) {} + + /** + * @brief Virtual destructor for proper cleanup of derived classes. + * + * Ensures proper cleanup of integrator resources and derived class data. + * The destructor handles cleanup of working vectors and any allocated + * data structures used during assembly operations. + * + * @note Base class destructor handles MFEM NonlinearFormIntegrator cleanup + * @note Working vectors are automatically cleaned up by MFEM Vector destructors + */ + virtual ~ExaNLFIntegrator() {} + + /// This doesn't do anything at this point. We can add the functionality + /// later on if a use case arises. + using mfem::NonlinearFormIntegrator::GetElementEnergy; + /** + * @brief Compute element energy contribution (placeholder implementation). + * + * @param el Finite element for energy computation + * @param Ttr Element transformation for coordinate mapping + * @param elfun Element solution vector + * @return Element energy contribution (currently always returns 0.0) + * + * This method provides the interface for element energy computation but + * currently returns zero. The functionality can be added later if energy + * calculations become required for the application. + * + * Potential future uses: + * - Total strain energy computation for post-processing + * - Energy-based error estimation for adaptive refinement + * - Thermodynamic consistency checks in material models + * - Variational constitutive updates + * + * @note Current implementation is placeholder returning 0.0 + * @note Can be extended for specific energy computation requirements + */ + virtual double GetElementEnergy([[maybe_unused]] const mfem::FiniteElement& el, + [[maybe_unused]] mfem::ElementTransformation& Ttr, + [[maybe_unused]] const mfem::Vector& elfun) override { + return 0.0; + }; + + using mfem::NonlinearFormIntegrator::AssembleElementVector; + /** + * @brief Assemble element residual vector for internal force computation. + * + * @param el Finite element providing shape functions and geometric information + * @param Ttr Element transformation for coordinate mapping + * @param elfun Element solution vector (typically nodal velocities or displacements) + * @param elvect Output element residual vector representing internal forces + * + * Computes the element contribution to the nonlinear residual vector, representing + * the internal forces arising from stress divergence in the current configuration. + * This is the core element-level computation in Newton-Raphson iterations. + * + * The assembly process: + * 1. Computes shape function derivatives in physical coordinates + * 2. Retrieves current stress state from quadrature function data + * 3. Integrates B^T * σ over element volume using Gauss quadrature + * 4. Accumulates contributions from all quadrature points + * + * Stress tensor handling: + * - Accesses Cauchy stress from simulation state quadrature functions + * - Uses full 3x3 stress tensor with proper symmetry treatment + * - Integrates stress divergence contribution to residual vector + * + * The residual represents the out-of-balance internal forces: + * f_internal = ∫_Ω B^T(x) σ(x) dΩ + * + * where B is the strain-displacement matrix and σ is the Cauchy stress tensor. + * + * Performance optimizations: + * - Reuses matrices across quadrature points for memory efficiency + * - Direct external data access for input/output vectors + * - Optimized matrix-vector operations using MFEM routines + * + * @note Assumes 3D problems with symmetric stress tensors + * @note Integration rule must match quadrature space for stress data + * @note Caliper profiling enabled for performance monitoring + */ + virtual void AssembleElementVector(const mfem::FiniteElement& el, + mfem::ElementTransformation& Ttr, + const mfem::Vector& elfun, + mfem::Vector& elvect) override; + + /** + * @brief Assemble element tangent stiffness matrix for Newton-Raphson linearization. + * + * @param el Finite element providing shape functions and geometric information + * @param Ttr Element transformation for coordinate mapping + * @param elfun Element solution vector (unused in current implementation) + * @param elmat Output element stiffness matrix + * + * Computes the element tangent stiffness matrix used in Newton-Raphson linearization, + * representing the derivative of internal forces with respect to nodal displacements. + * This matrix is essential for convergence of nonlinear iterations. + * + * The assembly process: + * 1. Computes shape function derivatives in physical coordinates + * 2. Retrieves material tangent stiffness from quadrature function data + * 3. Constructs strain-displacement B-matrix for current configuration + * 4. Integrates B^T * C * B over element volume using Gauss quadrature + * + * Tangent stiffness computation: + * K_element = ∫_Ω B^T(x) C(x) B(x) dΩ + * + * where: + * - B is the strain-displacement matrix (6×3n for 3D elements) + * - C is the material tangent stiffness matrix (6×6 for 3D) + * - Integration performed over current (deformed) element volume + * + * Material tangent matrix: + * - Accesses 6×6 tangent stiffness from material model evaluations + * - Uses Voigt notation for symmetric tensor operations + * - Includes both material and geometric stiffness contributions + * + * The algorithm performs the matrix triple product efficiently: + * 1. Computes temp = C * B (intermediate result) + * 2. Computes K += B^T * temp (final contribution) + * 3. Accumulates contributions from all quadrature points + * + * Performance considerations: + * - Optimized matrix operations using MFEM dense matrix routines + * - Memory reuse for intermediate matrices across quadrature points + * - Integration weights incorporated efficiently + * + * @note Material tangent matrix assumed to be 6×6 in Voigt notation + * @note B-matrix construction handles 3D elements with proper DOF ordering + * @note Caliper profiling enabled for performance analysis + */ + virtual void AssembleElementGrad(const mfem::FiniteElement& el, + mfem::ElementTransformation& Ttr, + const mfem::Vector& /*elfun*/, + mfem::DenseMatrix& elmat) override; + + /** + * @brief Initialize partial assembly data structures for gradient (Jacobian) operations. + * + * @param x Solution vector for state-dependent assembly (unused in current implementation) + * @param fes Finite element space providing mesh and element information + * + * Prepares geometric and material data structures needed for efficient partial + * assembly Jacobian operations. This method precomputes transformation data + * and material property layouts optimized for matrix-free operations. + * + * The gradient assembly setup includes: + * 1. Computing and storing shape function derivatives at quadrature points + * 2. Preparing 4D tensor layouts for material tangent operations + * 3. Setting up geometric factors for coordinate transformations + * 4. Organizing data for vectorized element-wise operations + * + * 4D tensor transformation: + * Applies the transformation: D_ijkm = (1/det(J)) * w_qpt * adj(J)^T_ij * C^tan_ijkl * + * adj(J)_lm where: + * - D is the transformed 4th order tensor for partial assembly + * - J is the Jacobian matrix from geometric factors + * - C^tan is the material tangent stiffness tensor + * - adj(J) is the adjugate of the Jacobian matrix + * + * Performance optimizations: + * - Precomputes shape function derivatives for all quadrature points + * - Uses RAJA views with optimized memory layouts for target architecture + * - Enables vectorization across elements and quadrature points + * - Supports both CPU and GPU execution + * + * @note Current implementation delegates to single-argument version + * @note Shape function derivatives cached for reuse in gradient operations + * @note 4D tensor layout optimized for specific hardware architectures + */ + virtual void AssembleGradPA(const mfem::Vector& /* x */, + const mfem::FiniteElementSpace& fes) override; + /** + * @brief Initialize partial assembly data structures for gradient operations. + * + * @param fes Finite element space providing mesh and element information + * + * Performs the core setup for partial assembly gradient operations by precomputing + * geometric factors and material data layouts. This method transforms material + * tangent data into optimized formats for efficient matrix-vector operations. + * + * The setup process includes: + * 1. Computing spatial dimensions and element characteristics + * 2. Precomputing shape function derivatives at all quadrature points + * 3. Transforming material tangent tensors for partial assembly operations + * 4. Setting up memory layouts optimized for target hardware + * + * Shape function derivative computation: + * - Calculates ∂N/∂ξ derivatives for all quadrature points + * - Stores in device-compatible format for GPU execution + * - Organizes data for efficient vectorized operations + * - Reuses derivatives across multiple gradient assembly calls + * + * Material tensor transformation: + * - Applies geometric transformations to material tangent matrices + * - Incorporates quadrature weights and Jacobian determinants + * - Uses 4D tensor layouts optimized for partial assembly operations + * - Enables efficient matrix-vector products in AddMultGradPA() + * + * The method prepares data structures for: + * - Fast Jacobian-vector products via AddMultGradPA() + * - Diagonal assembly for preconditioning via AssembleGradDiagonalPA() + * - Memory-efficient operations without explicit matrix storage + * + * @note Must be called before AddMultGradPA() and diagonal assembly operations + * @note Material tangent data accessed from simulation state quadrature functions + * @note Supports only 3D problems (1D and 2D abort with error message) + */ + virtual void AssembleGradPA(const mfem::FiniteElementSpace& fes) override; + + /** + * @brief Apply partial assembly gradient (Jacobian) operation. + * + * @param x Input vector for Jacobian-vector product + * @param y Output vector for accumulated result + * + * Performs the partial assembly Jacobian-vector product operation using + * precomputed geometric factors and transformed material tangent data. + * This operation computes the action of the tangent stiffness matrix + * without explicit matrix assembly, providing memory-efficient Newton-Raphson iterations. + * + * The operation computes: y += K * x, where K is the tangent stiffness matrix + * represented implicitly through partial assembly data structures. + * + * Algorithm overview: + * 1. Uses precomputed shape function derivatives and material data + * 2. Performs element-wise matrix-vector operations + * 3. Applies geometric transformations on-the-fly + * 4. Accumulates contributions to global vector + * + * Memory efficiency features: + * - No explicit stiffness matrix storage required + * - Vectorized operations over elements and quadrature points + * - Device-compatible implementation for GPU acceleration + * - Minimal working memory requirements + * + * Performance characteristics: + * - Computational complexity: O(nelems × nqpts × ndof²) + * - Memory complexity: O(nelems × nqpts) for material data + * - Excellent parallel scaling for large problems + * - Cache-friendly memory access patterns + * + * The method is called repeatedly during Krylov solver iterations + * within Newton-Raphson steps, making performance optimization critical. + * + * @note Requires prior AssembleGradPA() call for data structure setup + * @note Input and output vectors must match finite element space dimensions + * @note Essential boundary conditions handled by calling operator + */ + virtual void AddMultGradPA(const mfem::Vector& x, mfem::Vector& y) const override; + + using mfem::NonlinearFormIntegrator::AssemblePA; + /** + * @brief Initialize partial assembly data structures for residual operations. + * + * @param fes Finite element space providing mesh and element information + * + * Performs the initial setup for partial assembly operations by precomputing + * and storing geometric factors needed for efficient element-wise operations. + * This method amortizes setup costs across multiple residual evaluations. + * + * The setup process includes: + * 1. Extracting mesh and finite element information + * 2. Computing integration rule and weights + * 3. Storing geometric factors for coordinate transformations + * 4. Precomputing element-invariant quantities + * + * Geometric factor computation: + * - Retrieves Jacobian matrices for all elements and quadrature points + * - Stores transformation data in device-compatible format + * - Enables efficient coordinate mapping during assembly + * + * Memory allocation strategy: + * - Allocates working vectors with appropriate device memory types + * - Sizes vectors based on problem dimensions and mesh size + * - Prepares data structures for GPU execution when available + * + * The method prepares for: + * - Fast element vector assembly via AddMultPA() + * - Reuse of geometric data across multiple assembly calls + * - Device-compatible data layouts for GPU execution + * + * @note Must be called before AddMultPA() operations + * @note Geometric factors cached for reuse across assembly calls + * @note Caliper profiling scope for performance monitoring + */ + virtual void AssemblePA(const mfem::FiniteElementSpace& fes) override; + /** + * @brief Apply partial assembly element vector operation. + * + * @param x Input vector (unused in current implementation for residual assembly) + * @param y Output vector for accumulated element contributions + * + * Performs the partial assembly element vector operation, computing element + * residual contributions using precomputed geometric factors and current + * stress data. This operation is optimized for memory efficiency and + * computational performance in large-scale simulations. + * + * The partial assembly approach: + * - Uses precomputed geometric factors from AssemblePA() + * - Accesses stress data directly from quadrature functions + * - Performs element-wise operations without global matrix assembly + * - Accumulates results directly into global vector + * + * Operation sequence: + * 1. Initializes output vector appropriately + * 2. Loops over all elements in parallel-friendly manner + * 3. Applies element-wise stress integration + * 4. Accumulates results into global degrees of freedom + * + * Memory efficiency features: + * - Minimal working memory requirements + * - Direct access to stress quadrature function data + * - Vectorized operations over elements and quadrature points + * - Device-compatible implementation for GPU execution + * + * This method is called repeatedly during nonlinear iterations, + * so performance optimization is critical for overall solver efficiency. + * + * @note Input vector x currently unused for stress-based residual assembly + * @note Output vector y must be properly sized for true DOF space + * @note Requires prior AssemblePA() call for geometric factor setup + */ + virtual void AddMultPA(const mfem::Vector& /*x*/, mfem::Vector& y) const override; + + /** + * @brief Assemble diagonal entries for partial assembly preconditioning. + * + * @param diag Output vector for diagonal entries of the tangent stiffness matrix + * + * Computes diagonal entries of the tangent stiffness matrix using partial + * assembly techniques, providing diagonal approximations essential for + * Jacobi preconditioning in iterative linear solvers. + * + * The diagonal computation extracts entries: diag[i] = K[i,i] where K is + * the tangent stiffness matrix represented through partial assembly data. + * + * Algorithm approach: + * 1. Uses precomputed material tangent data from AssembleGradPA() + * 2. Extracts diagonal contributions element-by-element + * 3. Applies geometric transformations for diagonal terms + * 4. Assembles global diagonal through element restriction operations + * + * Diagonal extraction strategy: + * - Computes element-wise diagonal contributions + * - Uses vectorized operations for efficiency + * - Handles geometric transformations appropriately + * - Accumulates to global diagonal vector + * + * The diagonal approximation quality affects: + * - Jacobi preconditioner effectiveness + * - Krylov solver convergence rates + * - Overall Newton-Raphson performance + * - Numerical stability of iterative methods + * + * Memory and performance characteristics: + * - Linear scaling with problem size + * - Device-compatible implementation + * - Efficient vectorized operations + * - Minimal additional memory requirements + * + * @note Requires prior AssembleGradPA() call for material data setup + * @note Output vector must be properly sized for finite element space + * @note Diagonal quality depends on material tangent matrix conditioning + */ + virtual void AssembleGradDiagonalPA(mfem::Vector& diag) const override; + /** + * @brief Perform element assembly for gradient operations with solution vector. + * + * @param x Solution vector for state-dependent assembly (unused in current implementation) + * @param fes Finite element space providing mesh and element information + * @param ea_data Output vector for assembled element matrix data + * + * Performs element assembly for gradient operations, computing and storing + * complete element matrices in a format suitable for element assembly (EA) + * operations. This method delegates to the base element assembly routine. + * + * Element assembly characteristics: + * - Computes full element stiffness matrices + * - Stores matrices in contiguous device-compatible format + * - Enables exact matrix-vector products through explicit element matrices + * - Provides maximum memory efficiency for large problems + * + * The method serves as an interface for state-dependent element assembly + * while currently delegating to the stateless version for implementation. + * + * @note Current implementation delegates to AssembleEA(fes, ea_data) + * @note Solution vector x currently unused but available for future extensions + * @note Element matrices stored in ea_data with specific layout requirements + */ + virtual void AssembleGradEA(const mfem::Vector& /* x */, + const mfem::FiniteElementSpace& fes, + mfem::Vector& ea_data) override; + /** + * @brief Perform element assembly for gradient operations. + * + * @param fes Finite element space providing mesh and element information + * @param emat Output vector for assembled element matrix data + * + * Computes and stores complete element stiffness matrices for all elements + * in the mesh, providing an element assembly (EA) representation of the + * tangent stiffness operator for memory-constrained applications. + * + * Element assembly process: + * 1. Iterates over all elements in the mesh + * 2. Computes full element stiffness matrices + * 3. Stores matrices in contiguous device memory format + * 4. Organizes data for efficient element-wise matrix-vector products + * + * Memory layout: + * - Matrices stored element-by-element in contiguous memory + * - Dense matrices with row-major ordering within each element + * - Device-compatible allocation for GPU execution + * - Total size: nelems × (ndof×ncomps)² entries + * + * Performance characteristics: + * - Higher assembly cost compared to partial assembly + * - Minimal memory usage compared to global matrix assembly + * - Exact operator representation without approximation + * - Excellent performance for high DOF-per-element problems + * + * The element matrices enable: + * - Exact matrix-vector products in element assembly operators + * - Minimal memory footprint for large-scale problems + * - Natural parallelization over elements + * - Cache-friendly memory access patterns + * + * @note Supports only 3D problems (1D and 2D problems abort with error) + * @note Uses RAJA views for optimized memory layouts and vectorization + * @note Caliper profiling enabled for performance monitoring + */ + virtual void AssembleEA(const mfem::FiniteElementSpace& fes, mfem::Vector& emat) override; +}; + +/** + * @brief B-bar method integrator for incompressible and nearly incompressible solid mechanics. + * + * ICExaNLFIntegrator extends ExaNLFIntegrator to implement the B-bar method for handling + * incompressible and nearly incompressible materials. This integrator is essential for + * crystal plasticity simulations where volume preservation constraints arise from + * incompressible plastic deformation or nearly incompressible elastic behavior. + * + * The B-bar method (Hughes, 1980): + * - Modifies the strain-displacement B-matrix to avoid volumetric locking + * - Uses volume-averaged dilatational strains to improve element performance + * - Maintains accuracy for incompressible and nearly incompressible materials + * - Enables stable finite element solutions for high bulk modulus problems + * + * Mathematical foundation: + * The B-bar method splits the strain into volumetric and deviatoric parts: + * ε = ε_vol + ε_dev, where ε_vol is volume-averaged over the element + * + * This approach prevents spurious pressure oscillations and volumetric locking + * that can occur with standard displacement-based finite elements when dealing + * with incompressible or nearly incompressible material behavior. + * + * Applications in crystal plasticity: + * - Incompressible plastic deformation in crystal slip + * - Nearly incompressible elastic response in metals + * - Volume-preserving deformation in single crystal simulations + * - Polycrystalline materials with incompressible phases + * + * Key features: + * - Inherits all standard solid mechanics capabilities from ExaNLFIntegrator + * - Modifies B-matrix construction for volumetric strain averaging + * - Maintains compatibility with all assembly strategies (PA, EA, standard) + * - Provides stable solutions for high bulk modulus materials + * - Supports large deformation kinematics with volume preservation + * + * Implementation details: + * - Computes element-averaged volumetric strain gradients + * - Modifies standard B-matrix with B-bar corrections + * - Uses Hughes' formulation from "The Finite Element Method" Section 4.5.2 + * - Maintains computational efficiency comparable to standard elements + * + * @ingroup ExaConstit_fem_operators + */ +class ICExaNLFIntegrator : public ExaNLFIntegrator { +private: + /** @brief Element-averaged shape function derivatives for B-bar computation */ + mfem::Vector elem_deriv_shapes; + +public: + /** + * @brief Construct B-bar integrator with simulation state reference. + * + * @param sim_state Reference to simulation state containing mesh, fields, and material data + * + * Initializes the B-bar method integrator by calling the base ExaNLFIntegrator + * constructor and preparing data structures for B-bar method computations. + * The integrator is ready for element assembly operations with incompressible + * material handling upon construction. + * + * The constructor establishes: + * - Base class initialization for standard solid mechanics operations + * - Foundation for B-bar method implementation + * - Integration with ExaConstit's material model framework + * + * @note Simulation state reference must remain valid for integrator lifetime + * @note B-bar specific working vectors allocated during first assembly operation + */ + ICExaNLFIntegrator(std::shared_ptr sim_state) : ExaNLFIntegrator(sim_state) {} + /** + * @brief Virtual destructor for proper cleanup of derived class resources. + * + * Ensures proper cleanup of B-bar integrator resources including any + * working vectors allocated for element-averaged calculations. The + * destructor handles cleanup of both base class and derived class data. + * + * @note Base class destructor handles ExaNLFIntegrator cleanup + * @note B-bar specific vectors automatically cleaned up by MFEM Vector destructors + */ + virtual ~ICExaNLFIntegrator() {} + + /// This doesn't do anything at this point. We can add the functionality + /// later on if a use case arises. + using ExaNLFIntegrator::GetElementEnergy; + + using mfem::NonlinearFormIntegrator::AssembleElementVector; + /** + * @brief Assemble element residual vector using B-bar method for incompressible materials. + * + * @param el Finite element providing shape functions and geometric information + * @param Ttr Element transformation for coordinate mapping + * @param elfun Element solution vector (typically nodal velocities or displacements) + * @param elvect Output element residual vector representing internal forces + * + * Computes the element residual vector using the B-bar method to handle + * incompressible and nearly incompressible material behavior. This method + * modifies the standard residual computation to include volume-averaged + * strain measures that prevent volumetric locking. + * + * B-bar residual computation: + * 1. Computes element-averaged volumetric strain gradients over element volume + * 2. Constructs modified B-bar matrix with volumetric strain averaging + * 3. Retrieves current stress state from quadrature function data + * 4. Integrates B-bar^T * σ over element volume using Gauss quadrature + * + * Volume averaging process: + * - Integrates shape function derivatives over entire element + * - Normalizes by total element volume to obtain averages + * - Uses averaged derivatives to modify B-matrix construction + * - Maintains consistency with incompressible deformation constraints + * + * The B-bar matrix modification: + * B-bar = B_standard + B_volumetric_correction + * where B_volumetric_correction ensures proper volume averaging + * + * This approach prevents: + * - Volumetric locking in nearly incompressible materials + * - Spurious pressure oscillations in incompressible flow + * - Poor conditioning in high bulk modulus problems + * - Artificial stiffening due to volumetric constraints + * + * Performance considerations: + * - Requires additional integration loop for volume averaging + * - Slightly higher computational cost than standard elements + * - Significantly improved convergence for incompressible problems + * - Maintains stability for high bulk modulus materials + * + * @note Implements Hughes' B-bar method from FEM book Section 4.5.2 + * @note Requires compatible stress tensor data in simulation state + * @note Caliper profiling enabled for performance monitoring + */ + virtual void AssembleElementVector(const mfem::FiniteElement& el, + mfem::ElementTransformation& Ttr, + const mfem::Vector& elfun, + mfem::Vector& elvect) override; + + /** + * @brief Assemble element tangent stiffness matrix using B-bar method. + * + * @param el Finite element providing shape functions and geometric information + * @param Ttr Element transformation for coordinate mapping + * @param elfun Element solution vector (unused in current implementation) + * @param elmat Output element stiffness matrix + * + * Computes the element tangent stiffness matrix using the B-bar method for + * proper handling of incompressible and nearly incompressible materials. + * This method ensures consistent linearization of the B-bar residual formulation. + * + * B-bar tangent stiffness computation: + * K_element = ∫_Ω B-bar^T(x) C(x) B-bar(x) dΩ + * + * The algorithm includes: + * 1. Computing element-averaged volumetric strain gradients + * 2. Constructing B-bar matrix with volume averaging corrections + * 3. Retrieving material tangent stiffness from quadrature function data + * 4. Integrating B-bar^T * C * B-bar over element volume + * + * Volume averaging for stiffness: + * - Uses same element-averaged derivatives as in residual computation + * - Ensures consistency between residual and tangent matrix + * - Maintains proper Newton-Raphson convergence properties + * - Preserves quadratic convergence near solution + * + * B-bar matrix construction: + * - Modifies volumetric strain components with element averages + * - Preserves deviatoric strain components from standard B-matrix + * - Ensures proper rank and stability for incompressible problems + * - Maintains compatibility with material tangent matrix structure + * + * Material tangent integration: + * - Uses full 6×6 material tangent matrix in Voigt notation + * - Applies B-bar transformation consistently with residual + * - Incorporates geometric transformations and quadrature weights + * - Ensures symmetric tangent matrix for proper solver behavior + * + * The resulting stiffness matrix provides: + * - Stable tangent stiffness for incompressible materials + * - Proper conditioning for nearly incompressible problems + * - Consistent linearization of B-bar residual formulation + * - Quadratic Newton-Raphson convergence properties + * + * @note Consistent with B-bar residual formulation in AssembleElementVector + * @note Material tangent matrix assumed to be 6×6 in Voigt notation + * @note Caliper profiling enabled for performance analysis + */ + virtual void AssembleElementGrad(const mfem::FiniteElement& el, + mfem::ElementTransformation& Ttr, + const mfem::Vector& /*elfun*/, + mfem::DenseMatrix& elmat) override; + + // This method doesn't easily extend to PA formulation, so we're punting on + // it for now. + using ExaNLFIntegrator::AddMultGradPA; + using ExaNLFIntegrator::AssembleGradPA; + + /** + * @brief Initialize partial assembly data structures for B-bar residual operations. + * + * @param fes Finite element space providing mesh and element information + * + * Performs setup for B-bar method partial assembly operations by precomputing + * geometric factors and element-averaged quantities needed for efficient + * incompressible material handling in matrix-free operations. + * + * B-bar partial assembly setup: + * 1. Calls base class AssemblePA() for standard geometric factors + * 2. Computes element-averaged shape function derivatives + * 3. Stores volume-averaged data for B-bar matrix construction + * 4. Prepares data structures for efficient B-bar operations + * + * Element averaging computation: + * - Integrates shape function derivatives over each element + * - Normalizes by element volume to obtain averaged quantities + * - Stores averaged derivatives for use in AddMultPA operations + * - Enables consistent B-bar method in partial assembly framework + * + * The setup enables: + * - Memory-efficient B-bar residual assembly via AddMultPA() + * - Reuse of element-averaged data across multiple assembly calls + * - Device-compatible data layouts for GPU execution + * - Efficient handling of incompressible material constraints + * + * Performance characteristics: + * - Slightly higher setup cost due to volume averaging + * - Amortized over multiple assembly operations + * - Maintains memory efficiency of partial assembly approach + * - Enables stable solutions for incompressible problems + * + * @note Must be called before AddMultPA() operations for B-bar method + * @note Element averaging data cached for reuse across assembly calls + * @note Compatible with base class partial assembly infrastructure + */ + virtual void AssemblePA(const mfem::FiniteElementSpace& fes) override; + /** + * @brief Apply partial assembly B-bar element vector operation. + * + * @param x Input vector (unused in current implementation for residual assembly) + * @param y Output vector for accumulated element contributions + * + * Performs the partial assembly B-bar element vector operation, computing + * element residual contributions using precomputed geometric factors and + * element-averaged quantities. This provides memory-efficient B-bar method + * implementation for large-scale incompressible material simulations. + * + * B-bar partial assembly operation: + * - Uses precomputed element-averaged shape function derivatives + * - Constructs B-bar matrices on-the-fly during assembly + * - Accesses stress data directly from quadrature functions + * - Accumulates B-bar contributions directly into global vector + * + * The operation sequence: + * 1. Loops over all elements using precomputed geometric data + * 2. Constructs B-bar matrix using element-averaged derivatives + * 3. Applies stress integration with B-bar formulation + * 4. Accumulates results into global degrees of freedom + * + * Volume averaging integration: + * - Uses cached element-averaged derivatives from AssemblePA() + * - Applies B-bar corrections to volumetric strain components + * - Maintains computational efficiency of partial assembly + * - Prevents volumetric locking in incompressible materials + * + * Memory efficiency features: + * - Minimal additional memory for element averaging data + * - Direct access to stress quadrature function data + * - Vectorized operations over elements and quadrature points + * - Device-compatible implementation for GPU execution + * + * This method provides the core B-bar computation in Newton-Raphson + * iterations while maintaining the memory efficiency advantages of + * partial assembly for large-scale simulations. + * + * @note Requires prior AssemblePA() call for B-bar geometric factor setup + * @note Input vector x currently unused for stress-based residual assembly + * @note Output vector y must be properly sized for true DOF space + */ + virtual void AddMultPA(const mfem::Vector& /*x*/, mfem::Vector& y) const override; + /** + * @brief Assemble diagonal entries for B-bar partial assembly preconditioning. + * + * @param diag Output vector for diagonal entries of the B-bar tangent stiffness matrix + * + * Computes diagonal entries of the B-bar tangent stiffness matrix using + * partial assembly techniques, providing diagonal approximations essential + * for Jacobi preconditioning in iterative linear solvers for incompressible + * material problems. + * + * B-bar diagonal computation: + * 1. Uses precomputed element-averaged derivatives from AssembleGradPA() + * 2. Constructs B-bar matrix modifications for diagonal extraction + * 3. Applies material tangent data with B-bar transformations + * 4. Assembles global diagonal through element restriction operations + * + * The diagonal extraction process: + * - Accounts for B-bar modifications in volumetric strain components + * - Maintains consistency with B-bar tangent stiffness formulation + * - Uses vectorized operations for computational efficiency + * - Handles geometric transformations appropriately + * + * Diagonal quality considerations: + * - B-bar method affects diagonal structure and conditioning + * - Improved conditioning for incompressible material problems + * - Better preconditioner effectiveness for nearly incompressible materials + * - Enhanced Krylov solver convergence for high bulk modulus problems + * + * Performance characteristics: + * - Linear scaling with problem size + * - Device-compatible implementation for GPU execution + * - Efficient vectorized operations over elements + * - Minimal additional memory requirements beyond standard diagonal assembly + * + * The resulting diagonal provides: + * - Effective preconditioning for B-bar systems + * - Stable iterative solver behavior for incompressible problems + * - Consistent approximation quality across material parameter ranges + * - Robust performance for nearly incompressible materials + * + * @note Requires prior AssembleGradPA() call for B-bar material data setup + * @note Diagonal entries reflect B-bar modifications for incompressible behavior + * @note Caliper profiling enabled for performance monitoring + */ + virtual void AssembleGradDiagonalPA(mfem::Vector& diag) const override; + + /** + * @brief Perform B-bar element assembly for gradient operations with solution vector. + * + * @param x Solution vector for state-dependent assembly (unused in current implementation) + * @param fes Finite element space providing mesh and element information + * @param ea_data Output vector for assembled element matrix data + * + * Performs B-bar element assembly for gradient operations, computing and storing + * complete B-bar element stiffness matrices in a format suitable for element + * assembly (EA) operations. This method delegates to the base element assembly + * routine while maintaining B-bar method consistency. + * + * B-bar element assembly characteristics: + * - Computes full B-bar element stiffness matrices + * - Stores matrices in contiguous device-compatible format + * - Enables exact B-bar matrix-vector products through explicit element matrices + * - Provides maximum memory efficiency for large incompressible problems + * + * The method serves as an interface for state-dependent B-bar element assembly + * while currently delegating to the stateless version for implementation. + * Future extensions could include solution-dependent B-bar modifications. + * + * @note Current implementation delegates to AssembleEA(fes, ea_data) + * @note Solution vector x currently unused but available for future B-bar extensions + * @note Element matrices include B-bar modifications for incompressible behavior + */ + virtual void AssembleGradEA(const mfem::Vector& /* x */, + const mfem::FiniteElementSpace& fes, + mfem::Vector& ea_data) override; + + /** + * @brief Perform B-bar element assembly for gradient operations. + * + * @param fes Finite element space providing mesh and element information + * @param emat Output vector for assembled B-bar element matrix data + * + * Computes and stores complete B-bar element stiffness matrices for all elements + * in the mesh, providing an element assembly (EA) representation of the B-bar + * tangent stiffness operator for memory-constrained incompressible material applications. + * + * B-bar element assembly process: + * 1. Iterates over all elements in the mesh + * 2. Computes element-averaged volumetric derivatives for each element + * 3. Constructs B-bar element stiffness matrices with volume averaging + * 4. Stores matrices in contiguous device memory format + * + * B-bar matrix computation: + * - Computes element volume through integration of Jacobian determinants + * - Calculates element-averaged shape function derivatives + * - Constructs B-bar matrices with volumetric strain averaging + * - Integrates B-bar^T * C * B-bar over element volume + * + * Memory layout: + * - B-bar matrices stored element-by-element in contiguous memory + * - Dense matrices with row-major ordering within each element + * - Device-compatible allocation for GPU execution + * - Total size: nelems × (ndof×ncomps)² entries + * + * Performance characteristics: + * - Higher assembly cost due to B-bar volume averaging computations + * - Minimal memory usage compared to global B-bar matrix assembly + * - Exact B-bar operator representation without approximation + * - Excellent stability for incompressible material problems + * + * The B-bar element matrices enable: + * - Exact B-bar matrix-vector products in element assembly operators + * - Stable solutions for incompressible and nearly incompressible materials + * - Memory-efficient representation for large-scale problems + * - Natural parallelization over elements with B-bar consistency + * + * @note Supports only 3D problems (1D and 2D problems abort with error) + * @note Uses RAJA views for optimized B-bar memory layouts and vectorization + * @note Caliper profiling enabled for performance monitoring + */ + virtual void AssembleEA(const mfem::FiniteElementSpace& fes, mfem::Vector& emat) override; +}; + +// } + +#endif diff --git a/src/fem_operators/mechanics_operator.cpp b/src/fem_operators/mechanics_operator.cpp new file mode 100644 index 0000000..b95cd74 --- /dev/null +++ b/src/fem_operators/mechanics_operator.cpp @@ -0,0 +1,331 @@ + +#include "fem_operators/mechanics_operator.hpp" + +#include "models/mechanics_multi_model.hpp" +#include "utilities/mechanics_kernels.hpp" +#include "utilities/mechanics_log.hpp" +#include "utilities/unified_logger.hpp" + +#include "RAJA/RAJA.hpp" +#include "mfem/general/forall.hpp" + +#include +#include +#include + +NonlinearMechOperator::NonlinearMechOperator(mfem::Array& ess_bdr, + mfem::Array2D& ess_bdr_comp, + std::shared_ptr sim_state) + : mfem::NonlinearForm(sim_state->GetMeshParFiniteElementSpace().get()), + ess_bdr_comps(ess_bdr_comp), m_sim_state(sim_state) { + CALI_CXX_MARK_SCOPE("mechop_class_setup"); + mfem::Vector* rhs; + rhs = nullptr; + + const auto& options = m_sim_state->GetOptions(); + auto loc_fe_space = m_sim_state->GetMeshParFiniteElementSpace(); + + // Define the parallel nonlinear form + h_form = std::make_unique( + m_sim_state->GetMeshParFiniteElementSpace().get()); + + // Set the essential boundary conditions + h_form->SetEssentialBC(ess_bdr, ess_bdr_comps, rhs); + + // Set the essential boundary conditions that we can store on our class + SetEssentialBC(ess_bdr, ess_bdr_comps, rhs); + + assembly = options.solvers.assembly; + + model = std::make_shared(m_sim_state, options); + // Add the user defined integrator + if (options.solvers.integ_model == IntegrationModel::DEFAULT) { + h_form->AddDomainIntegrator(new ExaNLFIntegrator(m_sim_state)); + } else if (options.solvers.integ_model == IntegrationModel::BBAR) { + h_form->AddDomainIntegrator(new ICExaNLFIntegrator(m_sim_state)); + } + + if (assembly == AssemblyType::PA) { + h_form->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL, mfem::ElementDofOrdering::NATIVE); + diag.SetSize(loc_fe_space->GetTrueVSize(), mfem::Device::GetMemoryType()); + diag.UseDevice(true); + diag = 1.0; + prec_oper = std::make_shared(diag, + this->GetEssentialTrueDofs()); + } else if (assembly == AssemblyType::EA) { + h_form->SetAssemblyLevel(mfem::AssemblyLevel::ELEMENT, mfem::ElementDofOrdering::NATIVE); + diag.SetSize(loc_fe_space->GetTrueVSize(), mfem::Device::GetMemoryType()); + diag.UseDevice(true); + diag = 1.0; + prec_oper = std::make_shared(diag, + this->GetEssentialTrueDofs()); + } + + // So, we're going to originally support non tensor-product type elements originally. + const mfem::ElementDofOrdering ordering = mfem::ElementDofOrdering::NATIVE; + // const ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC; + elem_restrict_lex = loc_fe_space->GetElementRestriction(ordering); + + el_x.SetSize(elem_restrict_lex->Height(), mfem::Device::GetMemoryType()); + el_x.UseDevice(true); + px.SetSize(P->Height(), mfem::Device::GetMemoryType()); + px.UseDevice(true); + + { + const mfem::FiniteElement& el = *loc_fe_space->GetFE(0); + const int space_dims = el.GetDim(); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + ; + + const int nqpts = ir->GetNPoints(); + const int ndofs = el.GetDof(); + const int nelems = loc_fe_space->GetNE(); + + el_jac.SetSize(space_dims * space_dims * nqpts * nelems, mfem::Device::GetMemoryType()); + el_jac.UseDevice(true); + + qpts_dshape.SetSize(nqpts * space_dims * ndofs, mfem::Device::GetMemoryType()); + qpts_dshape.UseDevice(true); + { + mfem::DenseMatrix DSh; + const int offset = ndofs * space_dims; + double* qpts_dshape_data = qpts_dshape.HostReadWrite(); + for (int i = 0; i < nqpts; i++) { + const mfem::IntegrationPoint& ip = ir->IntPoint(i); + DSh.UseExternalData(&qpts_dshape_data[offset * i], ndofs, space_dims); + el.CalcDShape(ip, DSh); + } + } + } +} + +const mfem::Array& NonlinearMechOperator::GetEssTDofList() { + return h_form->GetEssentialTrueDofs(); +} + +void NonlinearMechOperator::UpdateEssTDofs(const mfem::Array& ess_bdr, bool mono_def_flag) { + if (mono_def_flag) { + h_form->SetEssentialTrueDofs(ess_bdr); + ess_tdof_list = ess_bdr; + } else { + // Set the essential boundary conditions + h_form->SetEssentialBC(ess_bdr, ess_bdr_comps, nullptr); + auto tmp = h_form->GetEssentialTrueDofs(); + // Set the essential boundary conditions that we can store on our class + SetEssentialBC(ess_bdr, ess_bdr_comps, nullptr); + } +} + +// compute: y = H(x,p) +void NonlinearMechOperator::Mult(const mfem::Vector& k, mfem::Vector& y) const { + CALI_CXX_MARK_SCOPE("mechop_Mult"); + // We first run a setup step before actually doing anything. + // We'll want to move this outside of Mult() at some given point in time + // and have it live in the NR solver itself or whatever solver + // we're going to be using. + Setup(k); + // We now perform our element vector operation. + CALI_MARK_BEGIN("mechop_mult_setup"); + // Assemble our operator + h_form->Setup(); + CALI_MARK_END("mechop_mult_setup"); + CALI_MARK_BEGIN("mechop_mult_Mult"); + h_form->Mult(k, y); + CALI_MARK_END("mechop_mult_Mult"); +} + +template +void NonlinearMechOperator::Setup(const mfem::Vector& k) const { + CALI_CXX_MARK_SCOPE("mechop_setup"); + // Wanted to put this in the mechanics_solver.cpp file, but I would have needed to update + // Solver class to use the NonlinearMechOperator instead of Operator class. + // We now update our end coordinates based on the solved for velocity. + if (upd_crds) { + UpdateEndCoords(k); + } + + // This performs the computation of the velocity gradient if needed, + // det(J), material tangent stiffness matrix, state variable update, + // stress update, and other stuff that might be needed in the integrators. + auto loc_fe_space = m_sim_state->GetMeshParFiniteElementSpace(); + + const mfem::FiniteElement& el = *loc_fe_space->GetFE(0); + const int space_dims = el.GetDim(); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + ; + + const int nqpts = ir->GetNPoints(); + const int ndofs = el.GetDof(); + const int nelems = loc_fe_space->GetNE(); + + SetupJacobianTerms(); + + // We can now make the call to our material model set-up stage... + // Everything else that we need should live on the class. + // Within this function the model just needs to produce the Cauchy stress + // and the material tangent matrix (d \sigma / d Vgrad_{sym}) + // bool succeed_t = false; + bool succeed = false; + try { + // Takes in k vector and transforms into into our E-vector array + P->Mult(k, px); + elem_restrict_lex->Mult(px, el_x); + model->ModelSetup(nqpts, nelems, space_dims, ndofs, el_jac, qpts_dshape, el_x); + succeed = true; + } catch (const std::exception& exc) { + // catch anything thrown within try block that derives from std::exception + MFEM_WARNING_0(exc.what()); + succeed = false; + } catch (...) { + succeed = false; + } + // MPI_Allreduce(&succeed_t, &succeed, 1, MPI_C_BOOL, MPI_LAND, MPI_COMM_WORLD); + if (!succeed) { + throw std::runtime_error(std::string( + "Material model setup portion of code failed for at least one integration point.")); + } +} // End of model setup + +void NonlinearMechOperator::SetupJacobianTerms() const { + auto mesh = m_sim_state->GetMesh(); + auto fe_space = m_sim_state->GetMeshParFiniteElementSpace(); + const mfem::FiniteElement& el = *fe_space->GetFE(0); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + ; + + const int space_dims = el.GetDim(); + const int nqpts = ir->GetNPoints(); + const int nelems = fe_space->GetNE(); + + // We need to make sure these are deleted at the start of each iteration + // since we have meshes that are constantly changing. + mesh->DeleteGeometricFactors(); + const mfem::GeometricFactors* geom = mesh->GetGeometricFactors( + *ir, mfem::GeometricFactors::JACOBIANS); + // geom->J really isn't going to work for us as of right now. We could just reorder it + // to the version that we want it to be in instead... + + const int DIM4 = 4; + std::array perm4{{3, 2, 1, 0}}; + // bunch of helper RAJA views to make dealing with data easier down below in our kernel. + RAJA::Layout layout_jacob = RAJA::make_permuted_layout( + {{space_dims, space_dims, nqpts, nelems}}, perm4); + RAJA::View> jac_view(el_jac.ReadWrite(), + layout_jacob); + + RAJA::Layout layout_geom = RAJA::make_permuted_layout( + {{nqpts, space_dims, space_dims, nelems}}, perm4); + RAJA::View> geom_j_view(geom->J.Read(), + layout_geom); + + const int nqpts1 = nqpts; + const int space_dims1 = space_dims; + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i) { + const int nqpts_ = nqpts1; + const int space_dims_ = space_dims1; + for (int j = 0; j < nqpts_; j++) { + for (int k = 0; k < space_dims_; k++) { + for (int l = 0; l < space_dims_; l++) { + jac_view(l, k, j, i) = geom_j_view(j, l, k, i); + } + } + } + }); +} + +void NonlinearMechOperator::CalculateDeformationGradient(mfem::QuadratureFunction& def_grad) const { + auto mesh = m_sim_state->GetMesh(); + auto fe_space = m_sim_state->GetMeshParFiniteElementSpace(); + const mfem::FiniteElement& el = *fe_space->GetFE(0); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + ; + + const int nqpts = ir->GetNPoints(); + const int nelems = fe_space->GetNE(); + const int ndofs = fe_space->GetFE(0)->GetDof(); + + auto x_ref = m_sim_state->GetRefCoords(); + auto x_cur = m_sim_state->GetCurrentCoords(); + // Since we never modify our mesh nodes during this operations this is okay. + mfem::GridFunction* nodes = + x_ref.get(); // set a nodes grid function to global current configuration + int owns_nodes = 0; + mesh->SwapNodes(nodes, owns_nodes); // pmesh has current configuration nodes + SetupJacobianTerms(); + + mfem::Vector x_true(fe_space->TrueVSize(), mfem::Device::GetMemoryType()); + + x_cur->GetTrueDofs(x_true); + // Takes in k vector and transforms into into our E-vector array + P->Mult(x_true, px); + elem_restrict_lex->Mult(px, el_x); + + def_grad = 0.0; + exaconstit::kernel::GradCalc( + nqpts, nelems, ndofs, el_jac.Read(), qpts_dshape.Read(), el_x.Read(), def_grad.ReadWrite()); + + // We're returning our mesh nodes to the original object they were pointing to. + // So, we need to cast away the const here. + // We just don't want other functions outside this changing things. + nodes = x_cur.get(); + mesh->SwapNodes(nodes, owns_nodes); + // Delete the old geometric factors since they dealt with the original reference frame. + mesh->DeleteGeometricFactors(); +} + +// Update the end coords used in our model +void NonlinearMechOperator::UpdateEndCoords(const mfem::Vector& vel) const { + m_sim_state->GetPrimalField()->operator=(vel); + m_sim_state->UpdateNodalEndCoords(); +} + +// Compute the Jacobian from the nonlinear form +mfem::Operator& NonlinearMechOperator::GetGradient(const mfem::Vector& x) const { + CALI_CXX_MARK_SCOPE("mechop_getgrad"); + jacobian = &h_form->GetGradient(x); + // Reset our preconditioner operator aka recompute the diagonal for our jacobi. + jacobian->AssembleDiagonal(diag); + return *jacobian; +} + +// Compute the Jacobian from the nonlinear form +mfem::Operator& NonlinearMechOperator::GetUpdateBCsAction(const mfem::Vector& k, + const mfem::Vector& x, + mfem::Vector& y) const { + CALI_CXX_MARK_SCOPE("mechop_GetUpdateBCsAction"); + // We first run a setup step before actually doing anything. + // We'll want to move this outside of Mult() at some given point in time + // and have it live in the NR solver itself or whatever solver + // we're going to be using. + Setup(k); + // We now perform our element vector operation. + mfem::Vector resid(y); + resid.UseDevice(true); + mfem::Array zero_tdofs; + CALI_MARK_BEGIN("mechop_h_form_LocalGrad"); + h_form->Setup(); + h_form->SetEssentialTrueDofs(zero_tdofs); + auto& loc_jacobian = h_form->GetGradient(x); + loc_jacobian.Mult(x, y); + h_form->SetEssentialTrueDofs(ess_tdof_list); + h_form->Mult(k, resid); + jacobian = &h_form->GetGradient(x); + CALI_MARK_END("mechop_h_form_LocalGrad"); + + { + auto I = ess_tdof_list.Read(); + auto size = ess_tdof_list.Size(); + auto Y = y.Write(); + // Need to get rid of all the constrained values here + mfem::forall(size, [=] MFEM_HOST_DEVICE(int i) { + Y[I[i]] = 0.0; + }); + } + + y += resid; + return *jacobian; +} \ No newline at end of file diff --git a/src/fem_operators/mechanics_operator.hpp b/src/fem_operators/mechanics_operator.hpp new file mode 100644 index 0000000..3a83b76 --- /dev/null +++ b/src/fem_operators/mechanics_operator.hpp @@ -0,0 +1,466 @@ + +#ifndef mechanics_operator_hpp +#define mechanics_operator_hpp + +#include "fem_operators/mechanics_integrators.hpp" +#include "fem_operators/mechanics_operator_ext.hpp" +#include "models/mechanics_model.hpp" +#include "options/option_parser_v2.hpp" +#include "sim_state/simulation_state.hpp" + +#include "mfem.hpp" + +#include +/** + * @brief Central nonlinear mechanics operator for updated Lagrangian finite element formulations. + * + * NonlinearMechOperator drives the entire ExaConstit nonlinear mechanics system, implementing + * an updated Lagrangian finite element formulation for large deformation solid mechanics. + * It manages the Newton-Raphson solver, Krylov iterative solvers, material models, and + * coordinates the interaction between finite element operations and constitutive models. + * + * The class extends MFEM's NonlinearForm to provide specialized mechanics operations including: + * - Updated Lagrangian formulation with current configuration updates + * - Material model integration (crystal plasticity, UMAT, multi-model support) + * - Partial and element assembly support for high-performance computing + * - Jacobian computation and preconditioning for Newton-Raphson convergence + * - Deformation gradient calculation and coordinate updates + * - Essential boundary condition management + * + * Key features for large-scale simulations: + * - GPU/CPU device compatibility through MFEM's device abstraction + * - Memory-efficient partial assembly operations + * - Support for heterogeneous material regions + * - Automatic coordinate updating for finite deformation problems + * - Integration with ExaConstit's simulation state management + * + * The operator works in conjunction with SimulationState to manage: + * - Current and reference configurations + * - Material state variables across time steps + * - Boundary condition updates + * - Multi-material region handling + * + * @ingroup ExaConstit_fem_operators + */ +class NonlinearMechOperator : public mfem::NonlinearForm { +protected: + /** @brief MFEM parallel nonlinear form for distributed memory computations */ + std::unique_ptr h_form; + + /** @brief Diagonal vector for Jacobian preconditioning operations */ + mutable mfem::Vector diag; + + /** @brief Shape function derivatives at quadrature points for element operations */ + mutable mfem::Vector qpts_dshape; + + /** @brief Element-wise solution vector in local element ordering */ + mutable mfem::Vector el_x; + + /** @brief Prolongation operation intermediate vector for assembly operations */ + mutable mfem::Vector px; + + /** @brief Element Jacobian matrices for geometric transformation computations */ + mutable mfem::Vector el_jac; + + /** @brief Pointer to current Jacobian operator for Newton-Raphson iterations */ + mutable mfem::Operator* jacobian; + + /** @brief Jacobi preconditioner for iterative linear solvers */ + mutable std::shared_ptr prec_oper; + + /** @brief Element restriction operator for local-to-global degree of freedom mapping */ + const mfem::Operator* elem_restrict_lex; + + /** @brief Assembly strategy (FULL, PARTIAL, ELEMENT) controlling computational approach */ + AssemblyType assembly; + + /** @brief Material model manager handling constitutive relationships */ + std::shared_ptr model; + + /** @brief Essential boundary condition component specification array */ + const mfem::Array2D& ess_bdr_comps; + + /** @brief Reference to simulation state for accessing mesh, fields, and configuration data */ + std::shared_ptr m_sim_state; + +public: + /** + * @brief Construct nonlinear mechanics operator with boundary conditions and simulation state. + * + * @param ess_bdr Array of essential boundary attributes for Dirichlet conditions + * @param ess_bdr_comp Component specification for essential boundary conditions + * @param sim_state Reference to simulation state containing mesh, fields, and options + * + * Initializes the complete nonlinear mechanics system including: + * - Parallel nonlinear form setup with proper boundary condition handling + * - Material model instantiation based on simulation options + * - Assembly strategy configuration (partial/element/full assembly) + * - Device memory allocation for vectors and working arrays + * - Integration rule and shape function derivative precomputation + * - Preconditioner setup for iterative linear solvers + * + * The constructor configures the finite element space, sets up domain integrators + * based on the chosen integration model (default or B-bar), and prepares all + * necessary data structures for efficient nonlinear solver operations. + * + * Memory allocation is device-aware and will utilize GPU memory when available. + * The operator is ready for Newton-Raphson iterations upon construction completion. + */ + NonlinearMechOperator(mfem::Array& ess_bdr, + mfem::Array2D& ess_bdr_comp, + std::shared_ptr sim_state); + + /** + * @brief Compute Jacobian operator for Newton-Raphson linearization. + * + * @param x Current solution vector for Jacobian evaluation point + * @return Reference to assembled Jacobian operator + * + * Computes the tangent stiffness matrix (Jacobian) of the nonlinear residual with respect + * to the solution vector. This is the core linearization operation in Newton-Raphson + * iterations, providing the linear system operator for computing solution updates. + * + * The method: + * 1. Calls the underlying MFEM nonlinear form Jacobian computation + * 2. Assembles the diagonal for preconditioner updates + * 3. Returns reference to the assembled operator for linear solver use + * + * The Jacobian includes contributions from: + * - Material tangent stiffness (constitutive Jacobian) + * - Geometric stiffness from large deformation effects + * - Essential boundary condition enforcement + * + * Performance is optimized through partial assembly when enabled, avoiding + * explicit matrix formation while maintaining operator functionality. + * + * @note The returned operator is suitable for use with MFEM's iterative solvers + * @note Diagonal assembly enables efficient Jacobi preconditioning + */ + virtual mfem::Operator& GetGradient(const mfem::Vector& x) const override; + + /** + * @brief Compute linearized residual update for boundary condition changes. + * + * @param k Current solution vector + * @param x Linearization point for Jacobian evaluation + * @param y Output vector for the linearized residual update + * @return Reference to Jacobian operator for the updated boundary conditions + * + * Computes the effect of boundary condition changes on the linearized system + * by providing both the residual update and modified Jacobian. This enables + * efficient handling of time-dependent or load-step-dependent boundary conditions + * without full system reassembly. + * + * The algorithm: + * 1. Temporarily removes essential boundary condition constraints + * 2. Computes unconstrained Jacobian-vector product + * 3. Evaluates residual with updated boundary conditions + * 4. Combines linearized and nonlinear contributions + * 5. Restores essential boundary condition enforcement + * + * Applications include: + * - Progressive loading with evolving Dirichlet conditions + * - Contact boundary condition updates during nonlinear iterations + * - Multi-physics coupling with changing interface conditions + * - Adaptive boundary condition strategies + * + * The method maintains consistency with the Newton-Raphson framework while + * efficiently handling boundary condition modifications during the solution process. + * + * @note Requires proper Setup() call before use to ensure consistent state + * @note Output vector y contains both Jacobian action and residual contributions + */ + virtual mfem::Operator& + GetUpdateBCsAction(const mfem::Vector& k, const mfem::Vector& x, mfem::Vector& y) const; + + /** + * @brief Evaluate nonlinear residual vector for current solution state. + * + * @param k Solution vector (typically velocity or displacement increment) + * @param y Output residual vector + * + * Computes the nonlinear residual vector representing the out-of-balance forces + * in the discretized momentum balance equation. This is the core function + * evaluation in Newton-Raphson iterations, measuring how far the current + * solution is from satisfying the equilibrium equations. + * + * The residual computation includes: + * 1. Current configuration update using solution vector k + * 2. Deformation gradient calculation at quadrature points + * 3. Material model evaluation (stress and tangent computation) + * 4. Integration of internal force contributions + * 5. Application of essential boundary conditions + * + * Performance optimizations: + * - Device-aware memory operations for GPU execution + * - Efficient coordinate update and geometric calculations + * - Optimized material model calls with vectorized operations + * - Caliper profiling markers for performance analysis + * + * The method coordinates with SimulationState to update: + * - Nodal coordinates for updated Lagrangian formulation + * - Material state variables based on computed deformation + * - Boundary condition applications + * + * @note Calls Setup() to update coordinates and material state + * @note Residual is computed in the current (deformed) configuration + */ + virtual void Mult(const mfem::Vector& k, mfem::Vector& y) const override; + + /// Sets all of the data up for the Mult and GetGradient method + /// This is of significant interest to be able to do partial assembly operations. + using mfem::NonlinearForm::Setup; + + /** + * @brief Setup deformation state and material properties for current solution. + * + * @tparam upd_crds Boolean controlling whether to update nodal coordinates + * @param k Solution vector for deformation gradient computation + * + * Prepares all necessary geometric and material quantities for residual and + * Jacobian computations. This is a critical setup phase that coordinates + * the updated Lagrangian formulation with material model requirements. + * + * The setup process includes: + * 1. Coordinate update (if upd_crds=true) for current configuration + * 2. Jacobian matrix computation for geometric transformations + * 3. Deformation gradient calculation at quadrature points + * 4. Material model setup with current state variables + * 5. Error handling for material model convergence issues + * + * Template parameter usage: + * - upd_crds=true: Full setup for residual evaluation (updates mesh geometry) + * - upd_crds=false: Linearization setup for Jacobian evaluation (geometry fixed) + * + * The method ensures consistency between: + * - Current mesh configuration and solution vector + * - Material state variables and computed deformation + * - Integration point data and finite element discretization + * + * Error handling includes detection of material model failures and provides + * descriptive error messages for debugging convergence issues. + * + * @note Template instantiation enables compile-time optimization + * @note Material model setup may fail for extreme deformations + * @throws std::runtime_error if material model setup fails + */ + template + void Setup(const mfem::Vector& k) const; + + /** + * @brief Compute geometric transformation matrices for finite element operations. + * + * Sets up Jacobian matrices needed for coordinate transformations between + * reference and current configurations in the updated Lagrangian formulation. + * This includes computation of geometric factors required for integration + * point operations and material model evaluations. + * + * The method: + * 1. Temporarily swaps mesh coordinates to current configuration + * 2. Computes geometric transformation matrices + * 3. Updates integration point geometric data + * 4. Restores original mesh coordinate state + * 5. Invalidates cached geometric factors for next update + * + * This separation enables efficient recomputation of geometric quantities + * without affecting the overall mesh data structure and allows material + * models to access current configuration geometry consistently. + * + * @note Requires current nodal coordinates to be available in SimulationState + * @note Invalidates mesh geometric factors cache for consistency + */ + void SetupJacobianTerms() const; + + /** + * @brief Calculate deformation gradient tensor at all quadrature points. + * + * @param def_grad Output quadrature function for deformation gradient storage + * + * Computes the deformation gradient tensor F = ∂x/∂X at each quadrature point, + * where x is the current position and X is the reference position. This is + * the fundamental kinematic quantity for finite deformation mechanics and + * serves as input to material constitutive models. + * + * The calculation involves: + * 1. Current configuration setup and coordinate transformation + * 2. Shape function derivative evaluation at quadrature points + * 3. Deformation gradient computation using nodal displacements + * 4. Storage in device-compatible quadrature function format + * + * The deformation gradient enables material models to: + * - Compute finite strain measures (Green-Lagrange, logarithmic, etc.) + * - Evaluate stress in current or reference configurations + * - Update internal state variables consistently with large deformation + * + * Performance considerations: + * - Optimized kernel operations for GPU execution + * - Memory-efficient quadrature point data layout + * - Consistent with partial assembly operations + * + * @note Output def_grad must be properly sized for all mesh quadrature points + * @note Deformation gradient computation assumes updated Lagrangian formulation + */ + void CalculateDeformationGradient(mfem::QuadratureFunction& def_grad) const; + + /** + * @brief Update nodal coordinates with computed velocity solution. + * + * @param vel Velocity vector solution from Newton-Raphson iteration + * + * Updates the current nodal coordinates using the velocity solution from + * the nonlinear solver. This is essential for the updated Lagrangian + * formulation where the finite element mesh follows the material deformation. + * + * The update process: + * 1. Copies velocity solution to simulation state primal field + * 2. Triggers coordinate update in simulation state management + * 3. Ensures consistency between solution vector and mesh geometry + * + * This method maintains the updated Lagrangian framework by ensuring that: + * - Mesh nodes track material particle positions + * - Geometric calculations reflect current configuration + * - Material models receive consistent deformation data + * + * @note Must be called after each Newton-Raphson iteration for consistency + * @note Coordinates are updated in simulation state for global accessibility + */ + void UpdateEndCoords(const mfem::Vector& vel) const; + + /** + * @brief Update essential boundary condition specifications. + * + * @param ess_bdr New essential boundary attribute specifications + * @param mono_def_flag Flag controlling monolithic or component-wise BC application + * + * Updates the essential boundary condition specification for the nonlinear form, + * enabling dynamic boundary condition changes during the simulation. This is + * essential for progressive loading, contact problems, and multi-physics coupling. + * + * Update modes: + * - mono_def_flag=true: Direct DOF specification for monolithic problems + * - mono_def_flag=false: Component-wise BC specification for vector problems + * + * The method updates both: + * - Internal nonlinear form boundary condition storage + * - Class-level essential DOF lists for consistent access + * + * Applications include: + * - Time-dependent boundary conditions + * - Load stepping with evolving constraints + * - Contact and interface condition updates + * - Multi-stage loading protocols + * + * @note Boundary condition changes affect Jacobian structure and preconditioning + * @note Component specification must match finite element space dimensions + */ + void UpdateEssTDofs(const mfem::Array& ess_bdr, bool mono_def_flag); + + /** + * @brief Retrieve list of essential (constrained) true degrees of freedom. + * + * @return Constant reference to array of essential true DOF indices + * + * Provides access to the current set of constrained degrees of freedom, + * which is essential for linear solver setup, preconditioning, and + * solution vector manipulation in constrained problems. + * + * The essential DOF list includes: + * - Dirichlet boundary condition constraints + * - Multi-point constraints if present + * - Any other DOF constraints from the finite element formulation + * + * Applications: + * - Linear solver constraint enforcement + * - Preconditioner setup for constrained systems + * - Solution vector post-processing + * - Convergence monitoring for unconstrained DOFs + * + * @note List reflects current boundary condition state + * @note Indices are in true (globally numbered) DOF space + */ + const mfem::Array& GetEssTDofList(); + + /** + * @brief Access material model for constitutive relationship queries. + * + * @return Pointer to active material model instance + * + * Provides access to the material model instance for external queries + * about constitutive relationships, state variable access, and material + * property information. This enables coupling with post-processing, + * adaptive algorithms, and multi-physics solvers. + * + * The material model provides: + * - Stress and tangent stiffness computation + * - State variable access and manipulation + * - Material property queries + * - Constitutive model-specific operations + * + * Common uses: + * - Post-processing stress and strain calculations + * - Adaptive mesh refinement based on material state + * - Multi-physics coupling with thermal or electromagnetic models + * - Material failure and damage assessment + * + * @note Returned pointer should not be deleted by caller + * @note Material model lifetime matches operator lifetime + */ + std::shared_ptr GetModel() const { + return model; + } + + /** + * @brief Access Jacobi preconditioner for linear solver operations. + * + * @return Pointer to partial assembly preconditioner instance + * + * Provides access to the configured Jacobi preconditioner for use with + * iterative linear solvers in Newton-Raphson iterations. The preconditioner + * is automatically updated with diagonal information from Jacobian assembly. + * + * Preconditioner features: + * - L1-Jacobi smoothing for effective preconditioning + * - Essential boundary condition handling + * - Device-compatible operations for GPU execution + * - Automatic diagonal updates during Jacobian assembly + * + * The preconditioner is optimized for: + * - Partial assembly operations (matrix-free) + * - Large-scale parallel computations + * - Mixed precision iterative solvers + * - Contact and constrained problems + * + * @return nullptr if partial assembly is not enabled + * @note Preconditioner state automatically maintained during solution + */ + std::shared_ptr GetPAPreconditioner() { + return prec_oper; + } + + /** + * @brief Clean up mechanics operator resources and material model. + * + * Destructor for NonlinearMechOperator that properly deallocates resources + * and ensures clean shutdown of the mechanics system. This includes cleanup + * of both MFEM resources and ExaConstit-specific material model instances. + * + * Cleanup responsibilities: + * - Deletes material model instance and associated resources + * - Deallocates MFEM parallel nonlinear form + * - Releases any allocated preconditioner operators + * - Ensures proper cleanup of device memory allocations + * + * The destructor handles: + * - Material model deletion with proper ExaModel cleanup + * - MFEM parallel nonlinear form deallocation + * - Preconditioner operator cleanup if allocated + * - Device memory management for GPU-allocated vectors + * + * @note Material model deletion includes cleanup of constitutive model resources + * @note MFEM nonlinear form cleanup handles integrator deallocation + * @note Preconditioner cleanup performed automatically when needed + */ + virtual ~NonlinearMechOperator() = default; +}; + +#endif /* mechanics_operator_hpp */ diff --git a/src/fem_operators/mechanics_operator_ext.cpp b/src/fem_operators/mechanics_operator_ext.cpp new file mode 100644 index 0000000..3423beb --- /dev/null +++ b/src/fem_operators/mechanics_operator_ext.cpp @@ -0,0 +1,53 @@ + +#include "fem_operators/mechanics_operator_ext.hpp" + +#include "fem_operators/mechanics_integrators.hpp" +#include "fem_operators/mechanics_operator.hpp" +#include "utilities/mechanics_log.hpp" + +#include "RAJA/RAJA.hpp" +#include "mfem.hpp" +#include "mfem/general/forall.hpp" + +MechOperatorJacobiSmoother::MechOperatorJacobiSmoother(const mfem::Vector& d, + const mfem::Array& ess_tdofs, + const double dmpng) + : mfem::Solver(d.Size()), ndofs(d.Size()), dinv(ndofs), damping(dmpng), + ess_tdof_list(ess_tdofs), residual(ndofs) { + Setup(d); +} + +void MechOperatorJacobiSmoother::Setup(const mfem::Vector& diag) { + residual.UseDevice(true); + dinv.UseDevice(true); + const double delta = damping; + auto D = diag.Read(); + auto DI = dinv.Write(); + mfem::forall(ndofs, [=] MFEM_HOST_DEVICE(int i) { + DI[i] = delta / D[i]; + }); + auto I = ess_tdof_list.Read(); + mfem::forall(ess_tdof_list.Size(), [=] MFEM_HOST_DEVICE(int i) { + DI[I[i]] = delta; + }); +} + +void MechOperatorJacobiSmoother::Mult(const mfem::Vector& x, mfem::Vector& y) const { + MFEM_ASSERT(x.Size() == ndofs, "invalid input vector"); + MFEM_ASSERT(y.Size() == ndofs, "invalid output vector"); + + if (iterative_mode && oper) { + oper->Mult(y, residual); // r = A x + subtract(x, residual, residual); // r = b - A x + } else { + residual = x; + y.UseDevice(true); + y = 0.0; + } + auto DI = dinv.Read(); + auto R = residual.Read(); + auto Y = y.ReadWrite(); + mfem::forall(ndofs, [=] MFEM_HOST_DEVICE(int i) { + Y[i] += DI[i] * R[i]; + }); +} \ No newline at end of file diff --git a/src/fem_operators/mechanics_operator_ext.hpp b/src/fem_operators/mechanics_operator_ext.hpp new file mode 100644 index 0000000..1e798c3 --- /dev/null +++ b/src/fem_operators/mechanics_operator_ext.hpp @@ -0,0 +1,194 @@ +#ifndef mechanics_operator_ext_hpp +#define mechanics_operator_ext_hpp + +#include "fem_operators/mechanics_integrators.hpp" + +#include "mfem.hpp" + +/** + * @brief L1-Jacobi smoothing preconditioner for mechanics finite element operators. + * + * MechOperatorJacobiSmoother implements an efficient Jacobi-type preconditioner specifically + * designed for mechanics problems with essential boundary conditions. The preconditioner + * uses diagonal scaling with damping to provide effective preconditioning for iterative + * linear solvers in Newton-Raphson frameworks. + * + * Key features for mechanics applications: + * - L1-Jacobi scaling for improved convergence on mechanics problems + * - Proper essential boundary condition handling with identity scaling + * - Damping parameter for stability control and convergence tuning + * - Device-compatible implementation for GPU acceleration + * - Integration with partial and element assembly operators + * + * The L1-Jacobi approach: + * - Uses L1 norm of matrix rows for diagonal approximation when full diagonal unavailable + * - Provides more robust scaling than simple Jacobi for some problem types + * - Incorporates damping for stability in challenging nonlinear problems + * - Handles essential boundary conditions through identity preconditioning + * + * Essential boundary condition treatment: + * - Essential DOFs receive identity preconditioning (scaling factor = damping) + * - Maintains consistency with constrained operator structure + * - Preserves constraint satisfaction during iterative solution + * - Prevents ill-conditioning from constraint enforcement + * + * Performance characteristics: + * - Setup cost: O(ndof) diagonal inverse computation + * - Application cost: O(ndof) scaled vector addition + * - Memory usage: O(ndof) for diagonal storage + * - Device execution: Full GPU compatibility for large-scale problems + * + * @ingroup ExaConstit_fem_operators + */ +class MechOperatorJacobiSmoother : public mfem::Solver { +public: + /** + * @brief Construct Jacobi smoother with diagonal vector and essential boundary conditions. + * + * @param d Diagonal vector (or approximation) for preconditioning scaling + * @param ess_tdofs Array of essential true DOF indices + * @param damping Damping parameter for stability control (default: 1.0) + * + * Initializes the Jacobi smoother by computing damped diagonal inverse and + * setting up essential boundary condition handling. The damping parameter + * provides stability control and can improve convergence for difficult problems. + * + * Initialization process: + * 1. Sets up solver with system size from diagonal vector + * 2. Allocates device-compatible vectors for diagonal inverse and residual + * 3. Calls Setup() to compute damped diagonal inverse + * 4. Configures essential boundary condition treatment + * + * Damping parameter effects: + * - damping < 1.0: Under-relaxation for stability in difficult problems + * - damping = 1.0: Standard Jacobi scaling (default) + * - damping > 1.0: Over-relaxation (use with caution) + * + * Essential boundary condition setup: + * - Essential DOFs receive identity scaling (dinv[i] = damping) + * - Maintains consistency with constrained system structure + * - Prevents numerical issues from constraint enforcement + * + * @note Diagonal vector ownership not transferred to smoother + * @note Essential DOF array reference must remain valid for smoother lifetime + * @note Damping parameter affects both regular and essential DOFs + */ + MechOperatorJacobiSmoother(const mfem::Vector& d, + const mfem::Array& ess_tdofs, + const double damping = 1.0); + ~MechOperatorJacobiSmoother() {} + + /** + * @brief Apply Jacobi preconditioning to input vector. + * + * @param x Input vector (right-hand side or residual) + * @param y Output vector (preconditioned result) + * + * Applies damped Jacobi preconditioning to the input vector, providing + * diagonal scaling with proper essential boundary condition handling. + * The method supports both direct and iterative application modes. + * + * Application modes: + * - Direct mode (iterative_mode=false): y = dinv .* x + * - Iterative mode (iterative_mode=true): y += dinv .* (x - A*y) + * + * Direct mode application: + * - Simple diagonal scaling of input vector + * - Efficient for basic preconditioning in Krylov solvers + * - Cost: O(ndof) vector operations + * + * Iterative mode application: + * - Computes residual r = x - A*y using provided operator + * - Updates solution y += dinv .* r + * - Suitable for stationary iteration and smoothing applications + * + * Implementation features: + * - Device-compatible vector operations for GPU execution + * - Vectorized scaling operations for performance + * - Proper handling of essential boundary conditions + * - Integration with MFEM's solver framework + * + * Error checking: + * - Validates input and output vector sizes + * - Ensures dimensional consistency for safe operation + * + * @note Iterative mode requires valid operator pointer from SetOperator() + * @note All vector operations performed on device when available + * @note Essential boundary conditions handled automatically through diagonal setup + */ + void Mult(const mfem::Vector& x, mfem::Vector& y) const; + + /** + * @brief Set operator for iterative mode residual computation. + * + * @param op Reference to operator for residual computation in iterative mode + * + * Configures the smoother for iterative mode operation by storing a reference + * to the linear operator. This enables residual-based smoothing operations + * commonly used in multigrid and stationary iteration methods. + * + * The operator is used for: + * - Residual computation: r = b - A*x in iterative mode + * - Stationary iteration: x_new = x_old + dinv .* r + * - Smoothing operations in multigrid hierarchies + * + * @note Operator reference must remain valid for smoother lifetime + * @note Required for iterative_mode=true in Mult() operations + */ + void SetOperator(const mfem::Operator& op) { + oper = &op; + } + + /** + * @brief Setup diagonal inverse with damping and boundary condition handling. + * + * @param diag Diagonal vector for inverse computation and scaling setup + * + * Computes the damped diagonal inverse required for Jacobi preconditioning, + * including proper treatment of essential boundary conditions. This method + * can be called multiple times to update the preconditioner with new diagonal + * information during Newton-Raphson iterations. + * + * The setup algorithm: + * 1. Configures vectors for device execution + * 2. Computes damped diagonal inverse: dinv[i] = damping / diag[i] + * 3. Applies essential boundary condition treatment: dinv[ess_dof] = damping + * 4. Ensures all operations are device-compatible for GPU execution + * + * Diagonal inverse computation: + * - Standard DOFs: Uses damped inverse of provided diagonal entries + * - Essential DOFs: Uses damping parameter directly for identity scaling + * - Device execution: Vectorized operations for GPU performance + * + * Essential boundary condition handling: + * - Overwrites diagonal inverse for essential DOFs with damping value + * - Provides identity preconditioning for constrained degrees of freedom + * - Maintains numerical stability and constraint satisfaction + * + * @note Can be called multiple times to update diagonal information + * @note All operations performed on device when GPU execution enabled + * @note Essential DOF treatment ensures stable constraint handling + */ + void Setup(const mfem::Vector& diag); + +private: + /** @brief Total number of degrees of freedom in the system */ + const int ndofs; + + /** @brief Diagonal inverse with damping for preconditioning application */ + mfem::Vector dinv; + + /** @brief Damping parameter for stability and convergence control */ + const double damping; + + /** @brief Reference to essential true DOF indices for boundary condition handling */ + const mfem::Array& ess_tdof_list; + + /** @brief Working vector for residual computation in iterative mode */ + mutable mfem::Vector residual; + + /** @brief Pointer to operator for iterative mode residual computation */ + const mfem::Operator* oper; +}; + +#endif /* mechanics_operator_hpp */ diff --git a/src/mechanics_driver.cpp b/src/mechanics_driver.cpp index 180769e..4ff9bee 100644 --- a/src/mechanics_driver.cpp +++ b/src/mechanics_driver.cpp @@ -1,1284 +1,390 @@ -// *********************************************************************** -// # ExaConstit App -// ## Author: Robert A. Carson -// carson16@llnl.gov -// Steven R. Wopschall -// wopschall1@llnl.gov -// Jamie Bramwell -// bramwell1@llnl.gov -// Date: Aug. 6, 2017 -// Updated: Oct. 7, 2019 -// -// # Description: -// The purpose of this code app is to determine bulk constitutive properties of metals. -// This is a nonlinear quasi-static, implicit solid mechanics code built on the MFEM library based -// on an updated Lagrangian formulation (velocity based). Currently, only Dirichlet boundary conditions -// (homogeneous and inhomogeneous by dof component) have been implemented. Neumann (traction) boundary -// conditions and a body force are not implemented. A new ExaModel class allows one to implement -// arbitrary constitutive models. The code currently successfully allows for various UMATs to be -// interfaced within the code framework. Development work is currently focused on allowing for the -// mechanical models to run on GPGPUs. The code supports either constant time steps or user supplied -// delta time steps. Boundary conditions are supplied for the velocity field applied on a surface. -// It supports a number of different preconditioned Krylov iterative solvers (PCG, GMRES, MINRES) -// for either symmetric or nonsymmetric positive-definite systems. -// -// ## Remark: -// See the included options.toml to see all of the various different options that are allowable in this -// code and their default values. A TOML parser has been included within this directory, since it has -// an MIT license. The repository for it can be found at: https://github.com/skystrife/cpptoml . Example -// UMATs maybe obtained from https://web.njit.edu/~sac3/Software.html . We have not included them due to -// a question of licensing. The ones that have been run and are known to work are the linear elasticity -// model and the neo-Hookean material. Although, we might be able to provide an example interface so -// users can base their interface/build scripts off of what's known to work. -// Note: the grain.txt, props.txt and state.txt files are expected inputs for CP problems, -// specifically ones that use the Abaqus UMAT interface class under the ExaModel. -// -// # Installing Notes: -// * git clone the LLNL BLT library into cmake directory. It can be obtained at https://github.com/LLNL/blt.git -// * MFEM will need to be built with Conduit (built with HDF5). The easiest way to install Conduit -// is to use spack install instruction provided by Conduit. -// * ExaCMech is required for ExaConstit to be built and can be obtained at https://github.com/LLNL/ExaCMech.git. -// * Create a build directory and cd into there -// * Run ```cmake .. -DENABLE_MPI=ON -DENABLE_FORTRAN=ON -DMFEM_DIR{mfem's installed cmake location} -// -DBLT_SOURCE_DIR=${BLT cloned location} -DECMECH_DIR=${ExaCMech installed cmake location} -// -DRAJA_DIR={RAJA installed location} -DSNLS_DIR={SNLS location in ExaCMech} -// -DMETIS_DIR={Metis used in mfem location} -DHYPRE_DIR={HYPRE install location} -// -DCONDUIT_DIR={Conduit install location} -DHDF5_ROOT:PATH={HDF5 install location}``` -// * Run ```make -j 4``` -// -// # Future Implemenations Notes: -// * Visco-plasticity constitutive model -// * GPGPU material models -// * A more in-depth README that better covers the different options available. -// * debug ability to read different mesh formats -// * An up-to-date example options.toml file -// *********************************************************************** -#include "mfem.hpp" -#include "mfem/general/forall.hpp" -#include "mechanics_log.hpp" +/** + * @file mechanics_driver.cpp + * @brief Main application driver for ExaConstit velocity-based finite element simulations. + * + * @details ExaConstit is a high-performance, parallel finite element application for nonlinear + * solid mechanics simulations with emphasis on crystal plasticity and micromechanics modeling. + * This driver implements a velocity-based, updated Lagrangian finite element framework designed + * for large-scale materials science applications on leadership-class computing systems. + * + * **Key Capabilities:** + * - **Velocity-Based Formulation**: Updated Lagrangian kinematics with velocity primary variables + * - **Crystal Plasticity**: Advanced polycrystalline material modeling with grain-level resolution + * - **Large Deformation**: Geometrically nonlinear analysis for finite strain applications + * - **Multi-Material Support**: Heterogeneous material regions with different constitutive models + * - **Adaptive Time Stepping**: Automatic time step control based on Newton-Raphson convergence + * - **High-Performance Computing**: MPI parallelization with GPU acceleration support + * - **Advanced Solvers**: Newton-Raphson with line search and Krylov iterative linear solvers + * + * **Supported Material Models:** + * - **ExaCMech**: LLNL's crystal plasticity library with advanced hardening models + * - **UMAT Interface**: Abaqus-compatible user material subroutines + * - **Multi-Model Regions**: Different material models in different mesh regions + * - **History Variables**: Full support for internal state variable evolution + * + * **Computational Architecture:** + * - **MFEM Framework**: Built on LLNL's MFEM finite element library + * - **RAJA Performance Portability**: CPU/OpenMP/GPU execution with unified code + * - **Device-Aware Memory**: Automatic host/device memory management + * - **Partial Assembly**: Memory-efficient matrix-free operator evaluation + * - **Scalable I/O**: Parallel visualization and data output capabilities + * + * **Simulation Workflow:** + * 1. **Initialization**: MPI setup, option parsing, device configuration + * 2. **Mesh Setup**: Parallel mesh loading and finite element space creation + * 3. **Material Initialization**: State variables, grain orientations, and material properties + * 4. **Solver Configuration**: Newton-Raphson and linear solver setup with preconditioning + * 5. **Time Stepping**: Main simulation loop with boundary condition updates + * 6. **Post-Processing**: Field projection, volume averaging, and visualization output + * 7. **Performance Analysis**: Timing data and scalability metrics collection + * + * **Input Requirements:** + * - **options.toml**: Primary configuration file with all simulation parameters + * - **Mesh File**: Parallel-compatible mesh (typically .mesh format) + * - **Material Properties**: Material parameter files (props.txt for crystal plasticity) + * - **State Variables**: Initial internal state variable values (state.txt) + * - **Grain Data**: Crystal orientation data (grain.txt for crystal plasticity applications) + * + * **Key Dependencies:** + * - **MFEM**: Finite element framework with parallel/GPU support + * - **HYPRE**: Algebraic multigrid preconditioning and linear solvers + * - **ExaCMech**: Crystal plasticity constitutive model library + * - **RAJA**: Performance portability and GPU execution framework + * - **Conduit**: Data management and I/O for visualization + * - **Caliper**: Performance profiling and analysis toolkit + * + * **Usage:** + * ```bash + * mpirun -np ./mechanics [-opt options_file.toml] + * ``` + * + * **Performance Considerations:** + * - Designed for leadership-class HPC systems (CPU clusters and GPU systems) + * - Scales to thousands of MPI processes with efficient domain decomposition + * - GPU acceleration available for material model evaluation and linear algebra + * - Memory-efficient algorithms suitable for large-scale polycrystalline simulations + * + * @note This application is designed for materials science research and industrial + * applications requiring high-fidelity simulation of polycrystalline materials + * under complex loading conditions. + * + * @author LLNL ExaConstit Development Team (Lead Author: Robert Carson (carson16@llnl.gov)) + * @ingroup ExaConstit_applications + */ +#include "boundary_conditions/BCData.hpp" +#include "boundary_conditions/BCManager.hpp" +#include "mfem_expt/partial_qfunc.hpp" +#include "mfem_expt/partial_qspace.hpp" +#include "options/option_parser_v2.hpp" +#include "postprocessing/postprocessing_driver.hpp" +#include "postprocessing/postprocessing_file_manager.hpp" +#include "sim_state/simulation_state.hpp" #include "system_driver.hpp" -#include "BCData.hpp" -#include "BCManager.hpp" -#include "option_parser.hpp" -#include -#include - -using namespace std; -using namespace mfem; - -// set kinematic functions and boundary condition functions -void ReferenceConfiguration(const Vector &x, Vector &y); -void DirBdrFunc(int attr_id, Vector &y); - -// This initializes some grid function -void InitGridFunction(const Vector & /*x*/, Vector &y); - -// material input check routine -bool checkMaterialArgs(MechType mt, bool cp, int ngrains, int numProps, - int numStateVars); - -// material state variable and grain data setter routine -void setStateVarData(Vector* sVars, Vector* orient, ParFiniteElementSpace *fes, - int grainOffset, int grainIntoStateVarOffset, - int stateVarSize, QuadratureFunction* qf); - -// initialize a quadrature function with a single input value, val. -void initQuadFunc(QuadratureFunction *qf, double val); - -// initialize a quadrature function that is really a tensor with the identity matrix. -// currently only works for 3x3 tensors. -void initQuadFuncTensorIdentity(QuadratureFunction *qf, ParFiniteElementSpace *fes); - -// set the time step on the boundary condition objects -void setBCTimeStep(double dt, int nDBC); +#include "utilities/mechanics_log.hpp" +#include "utilities/unified_logger.hpp" -// set the element grain ids from vector data populated from a -// grain map input text file -void setElementGrainIDs(Mesh *mesh, const Vector grainMap, int ncols, int offset); - -// used to reset boundary conditions from MFEM convention using -// Make3D() called from the mesh constructor to ExaConstit convention -void setBdrConditions(Mesh *mesh); - -// reorder mesh elements in MFEM generated mesh using Make3D() in -// mesh constructor so that the ordering matches the element ordering -// in the input grain map (e.g. from CA calculation) -void reorderMeshElements(Mesh *mesh, const int *nxyz); +#include "mfem.hpp" +#include "mfem/general/forall.hpp" -// Projects the element attribute to GridFunction nodes -// This also assumes the GridFunction is an L2 FE space -void projectElemAttr2GridFunc(Mesh *mesh, ParGridFunction *elem_attr); +#include +#include +#include -int main(int argc, char *argv[]) -{ - CALI_INIT - CALI_CXX_MARK_FUNCTION; - CALI_MARK_BEGIN("main_driver_init"); - // Initialize MPI. - int num_procs, myid; - MPI_Init(&argc, &argv); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - MPI_Comm_rank(MPI_COMM_WORLD, &myid); +/** + * @brief Main application entry point for ExaConstit finite element simulations. + * + * @param argc Number of command line arguments + * @param argv Array of command line argument strings + * @return Exit status (0 for success, 1 for failure) + * + * Orchestrates the complete ExaConstit simulation workflow from initialization through + * final results output. The function implements a time-stepping algorithm for solving + * nonlinear solid mechanics problems with advanced material models and boundary conditions. + * + * **PHASE 1: PARALLEL INITIALIZATION AND SETUP** + */ +int main(int argc, char* argv[]) { + // Initialize Caliper performance profiling system for detailed performance analysis + CALI_INIT + CALI_CXX_MARK_FUNCTION; + CALI_MARK_BEGIN("main_driver_init"); + /* + * MPI Environment Setup: + * - Initialize MPI for parallel execution across distributed memory systems + * - Query total process count and local rank for parallel coordination + * - Initialize HYPRE if version supports it (parallel linear algebra) + */ + int num_procs, myid; + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + MPI_Comm_rank(MPI_COMM_WORLD, &myid); #if (MFEM_HYPRE_VERSION >= 21900) - Hypre::Init(); -#endif -// Used to scope the main program away from the main MPI Init and Finalize calls -{ - // Here we start a timer to time everything - double start = MPI_Wtime(); - // Here we're going to measure the times of each solve. - // It'll give us a good idea of strong and weak scaling in - // comparison to the global value of things. - // It'll make it easier to point out where some scaling issues might - // be occurring. - std::vector times; - double t1, t2; - // print the version of the code being run - if (myid == 0) { - printf("MFEM Version: %d \n", GetVersion()); - } - - // All of our options are parsed in this file by default - const char *toml_file = "options.toml"; - - // We're going to use the below to allow us to easily swap between different option files - OptionsParser args(argc, argv); - args.AddOption(&toml_file, "-opt", "--option", "Option file to use."); - args.Parse(); - if (!args.Good()) { - if (myid == 0) { - args.PrintUsage(cout); - } - CALI_MARK_END("main_driver_init"); - MPI_Finalize(); - return 1; - } - - ExaOptions toml_opt(toml_file); - toml_opt.parse_options(myid); - - // Set the device info here: - // Enable hardware devices such as GPUs, and programming models such as - // CUDA, OCCA, RAJA and OpenMP based on command line options. - // The current backend priority from highest to lowest is: 'occa-cuda', - // 'raja-cuda', 'cuda', 'occa-omp', 'raja-omp', 'omp', 'occa-cpu', 'raja-cpu', 'cpu'. - - std::string device_config = "cpu"; - - if (toml_opt.rtmodel == RTModel::CPU) { - device_config = "cpu"; - } - else if (toml_opt.rtmodel == RTModel::OPENMP) { - device_config = "raja-omp"; - } - else if (toml_opt.rtmodel == RTModel::GPU) { -#if defined(RAJA_ENABLE_CUDA) - device_config = "raja-cuda"; -#elif defined(RAJA_ENABLE_HIP) - device_config = "raja-hip"; -#endif - } - Device device; - - if (toml_opt.rtmodel == RTModel::GPU) - { - device.SetMemoryTypes(MemoryType::HOST_64, MemoryType::DEVICE); - } - - device.Configure(device_config.c_str()); - - if (myid == 0) { - printf("\n"); - device.Print(); - printf("\n"); - } - // Check to see if a custom dt file was used - // if so read that in and if not set the nsteps that we're going to use - if (toml_opt.dt_cust) { - if (myid == 0) { - printf("Reading in custom dt file. \n"); - } - ifstream idt(toml_opt.dt_file.c_str()); - if (!idt && myid == 0) { - cerr << "\nCannot open grain map file: " << toml_opt.grain_map << '\n' << endl; - } - // Now we're calculating the final time - toml_opt.cust_dt.Load(idt, toml_opt.nsteps); - toml_opt.t_final = 0.0; - for (int i = 0; i < toml_opt.nsteps; i++) { - toml_opt.t_final += toml_opt.cust_dt[i]; - } - - idt.close(); - } - else { - toml_opt.nsteps = ceil(toml_opt.t_final / toml_opt.dt_min); - if (myid==0) { - printf("number of steps %d \n", toml_opt.nsteps); - } - } - - times.reserve(toml_opt.nsteps); - - // Check material model argument input parameters for valid combinations - if (myid == 0) { - printf("after input before checkMaterialArgs. \n"); - } - bool err = checkMaterialArgs(toml_opt.mech_type, toml_opt.cp, - toml_opt.ngrains, toml_opt.nProps, toml_opt.numStateVars); - if (!err && myid == 0) { - cerr << "\nInconsistent material input; check args" << '\n'; - } - - // Open the mesh - if (myid == 0) { - printf("before reading the mesh. \n"); - } - // declare pointer to parallel mesh object - ParMesh *pmesh = NULL; - { - Mesh mesh; - Vector g_map; - if ((toml_opt.mesh_type == MeshType::CUBIT) || (toml_opt.mesh_type == MeshType::OTHER)) { - mesh = Mesh(toml_opt.mesh_file.c_str(), 1, 1, true); - } - else { - if (toml_opt.nxyz[0] <= 0 || toml_opt.mxyz[0] <= 0) { - cerr << "\nMust input mesh geometry/discretization for hex_mesh_gen" << '\n'; - } - - // use constructor to generate a 3D cuboidal mesh with 8 node hexes - // The false at the end is to tell the inline mesh generator to use the lexicographic ordering of the mesh - // The newer space-filling ordering option that was added in the pre-okina tag of MFEM resulted in a noticeable divergence - // of the material response for a monotonic tension test using symmetric boundary conditions out to 1% strain. - mesh = - Mesh::MakeCartesian3D(toml_opt.nxyz[0], toml_opt.nxyz[1], toml_opt.nxyz[2], Element::HEXAHEDRON, - toml_opt.mxyz[0], toml_opt.mxyz[1], toml_opt.mxyz[2], false); - } - - // read in the grain map if using a MFEM auto generated cuboidal mesh - if (toml_opt.mesh_type == MeshType::AUTO) { - if (myid == 0) { - printf("using mfem hex mesh generator \n"); - } - - ifstream igmap(toml_opt.grain_map.c_str()); - if (!igmap && myid == 0) { - cerr << "\nCannot open grain map file: " << toml_opt.grain_map << '\n' << endl; - } - - int gmapSize = mesh.GetNE(); - g_map.Load(igmap, gmapSize); - igmap.close(); - - //// reorder elements to conform to ordering convention in grain map file - // No longer needed for the CA stuff. It's now ordered as X->Y->Z - // reorderMeshElements(mesh, &toml_opt.nxyz[0]); - - // reset boundary conditions from - setBdrConditions(&mesh); - - // set grain ids as element attributes on the mesh - // The offset of where the grain index is located is - // location - 1. - setElementGrainIDs(&mesh, g_map, 1, 0); - } - - // We need to check to see if our provided mesh has a different order than - // the order provided. If we see a difference we either increase our order seen - // in the options file or we increase the mesh ordering. I'm pretty sure this - // was causing a problem earlier with our auto-generated mesh and if we wanted - // to use a higher order FE space. - // So we can't really do the GetNodalFESpace it appears if we're given - // an initial mesh. It looks like NodalFESpace is initially set to - // NULL and only if we swap the mesh nodes does this actually - // get set... - // So, we're just going to set the mesh order to at least be 1. Although, - // I would like to see this change sometime in the future. - int mesh_order = 1; // mesh->GetNodalFESpace()->GetOrder(0); - if (mesh_order > toml_opt.order) { - toml_opt.order = mesh_order; - } - if (mesh_order <= toml_opt.order) { - if (myid == 0) { - printf("Increasing the order of the mesh to %d\n", toml_opt.order); - } - mesh_order = toml_opt.order; - mesh.SetCurvature(mesh_order); - } - - // mesh refinement if specified in input - for (int lev = 0; lev < toml_opt.ser_ref_levels; lev++) { - mesh.UniformRefinement(); - } - - pmesh = new ParMesh(MPI_COMM_WORLD, mesh); - for (int lev = 0; lev < toml_opt.par_ref_levels; lev++) { - pmesh->UniformRefinement(); - } - pmesh->SetAttributes(); - } // Mesh related calls - // Called only once - { - BCManager& bcm = BCManager::getInstance(); - bcm.init(toml_opt.updateStep, toml_opt.map_ess_vel, toml_opt.map_ess_vgrad, toml_opt.map_ess_comp, - toml_opt.map_ess_id); - } - - CALI_MARK_END("main_driver_init"); - - if (myid == 0) { - printf("after mesh section. \n"); - } - - int dim = pmesh->Dimension(); - - // Define the finite element spaces for displacement field - FiniteElementCollection *fe_coll = NULL; - fe_coll = new H1_FECollection(toml_opt.order, dim); - ParFiniteElementSpace fe_space(pmesh, fe_coll, dim); - // All of our data is going to be saved off as element average of the field - // It would be nice if we could have it one day saved off as the raw quadrature - // fields as well to perform analysis on - int order_0 = 0; - - // Here we're setting up a discontinuous so that we'll use later to interpolate - // our quadrature functions from - L2_FECollection l2_fec(order_0, dim); - ParFiniteElementSpace l2_fes(pmesh, &l2_fec); - ParFiniteElementSpace l2_fes_pl(pmesh, &l2_fec, 1); - ParFiniteElementSpace l2_fes_ori(pmesh, &l2_fec, 4, mfem::Ordering::byVDIM); - ParFiniteElementSpace l2_fes_cen(pmesh, &l2_fec, dim, mfem::Ordering::byVDIM); - ParFiniteElementSpace l2_fes_voigt(pmesh, &l2_fec, 6, mfem::Ordering::byVDIM); - ParFiniteElementSpace l2_fes_tens(pmesh, &l2_fec, 9, mfem::Ordering::byVDIM); - ParFiniteElementSpace l2_fes_hard(pmesh, &l2_fec, toml_opt.hard_size, mfem::Ordering::byVDIM); - ParFiniteElementSpace l2_fes_gdots(pmesh, &l2_fec, toml_opt.gdot_size, mfem::Ordering::byVDIM); - - ParGridFunction vonMises(&l2_fes); - vonMises = 0.0; - ParGridFunction volume(&l2_fes); - ParGridFunction hydroStress(&l2_fes); - hydroStress = 0.0; - ParGridFunction stress(&l2_fes_voigt); - stress = 0.0; - // Only used for light-up scripts at this point - ParGridFunction *elem_centroid = nullptr; - ParGridFunction *elastic_strain = nullptr; -#ifdef MFEM_USE_ADIOS2 - ParGridFunction *elem_attr = nullptr; - if (toml_opt.adios2) { - elem_attr = new ParGridFunction(&l2_fes); - projectElemAttr2GridFunc(pmesh, elem_attr); - } -#endif - - ParGridFunction dpeff(&l2_fes_pl); - ParGridFunction pleff(&l2_fes_pl); - ParGridFunction hardness(&l2_fes_hard); - ParGridFunction quats(&l2_fes_ori); - ParGridFunction gdots(&l2_fes_gdots); - - if (toml_opt.mech_type == MechType::EXACMECH) { - if (toml_opt.light_up) { - elem_centroid = new ParGridFunction(&l2_fes_cen); - elastic_strain = new ParGridFunction(&l2_fes_voigt); - } - } - - HYPRE_Int glob_size = fe_space.GlobalTrueVSize(); - - pmesh->PrintInfo(); - - // Print the mesh statistics - if (myid == 0) { - std::cout << "***********************************************************\n"; - std::cout << "dim(u) = " << glob_size << "\n"; - std::cout << "***********************************************************\n"; - } - - // determine the type of grain input for crystal plasticity problems - int ori_offset = 0; // note: numMatVars >= 1, no null state vars by construction - if (toml_opt.cp) { - if (toml_opt.ori_type == OriType::EULER) { - ori_offset = 3; - } - else if (toml_opt.ori_type == OriType::QUAT) { - ori_offset = 4; - } - else if (toml_opt.ori_type == OriType::CUSTOM) { - if (toml_opt.grain_custom_stride == 0) { - cerr << "\nMust specify a grain stride for grain_custom input" << '\n'; - } - ori_offset = toml_opt.grain_custom_stride; - } - } - - // set the offset for the matVars quadrature function. This is the number of - // state variables (stored at each integration point) and then the grain offset, - // which is the number of variables defining the grain data stored at each - // integration point. In general, these may come in as different data sets, - // even though they will be stored in a single material state variable - // quadrature function. - int matVarsOffset = toml_opt.numStateVars + ori_offset; - - // Define a quadrature space and material history variable QuadratureFunction. - int intOrder = 2 * toml_opt.order + 1; - QuadratureSpace qspace(pmesh, intOrder); // 3rd order polynomial for 2x2x2 quadrature - // for first order finite elements. - QuadratureFunction matVars0(&qspace, matVarsOffset); - initQuadFunc(&matVars0, 0.0); - - // Used for post processing steps - QuadratureSpace qspace0(pmesh, 1); - QuadratureFunction elemMatVars(&qspace0, matVarsOffset); - elemMatVars = 0.0; - - // read in material properties and state variables files for use with ALL models - // store input data on Vector object. The material properties vector will be - // passed into the Nonlinear mech operator constructor to initialize the material - // properties vector on the model and the state variables vector will be used with - // the grain data vector (if crystal plasticity) to populate the material state - // vector quadrature function. It is assumed that the state variables input file - // are initial values for all state variables applied to all quadrature points. - // There is not a separate initialization file for each quadrature point - Vector matProps; - Vector stateVars; - if (myid == 0) { - printf("before reading in matProps and stateVars. \n"); - } - { // read in props, material state vars and grains if crystal plasticity - ifstream iprops(toml_opt.props_file.c_str()); - if (!iprops && myid == 0) { - cerr << "\nCannot open material properties file: " << toml_opt.props_file << '\n' << endl; - } - - // load material properties - matProps.Load(iprops, toml_opt.nProps); - iprops.close(); - - if (myid == 0) { - printf("after loading matProps. \n"); - } - - // read in state variables file - ifstream istateVars(toml_opt.state_file.c_str()); - if (!istateVars && myid == 0) { - cerr << "\nCannot open state variables file: " << toml_opt.state_file << '\n' << endl; - } - - // load state variables - stateVars.Load(istateVars, toml_opt.numStateVars); - istateVars.close(); - if (myid == 0) { - printf("after loading stateVars. \n"); - } - - // if using a crystal plasticity model then get grain orientation data - // declare a vector to hold the grain orientation input data. This data is per grain - // with a stride set previously as grain_offset - Vector g_orient; - if (myid == 0) { - printf("before loading g_orient. \n"); - } - if (toml_opt.cp) { - // set the grain orientation vector from the input grain file - ifstream igrain(toml_opt.ori_file.c_str()); - if (!igrain && myid == 0) { - cerr << "\nCannot open orientation file: " << toml_opt.ori_file << '\n' << endl; - } - // load separate grain file - int gsize = ori_offset * toml_opt.ngrains; - g_orient.Load(igrain, gsize); - igrain.close(); - if (myid == 0) { - printf("after loading g_orient. \n"); - } - } // end if (cp) - - // set the state var data on the quadrature function - if (myid == 0) { - printf("before setStateVarData. \n"); - } - setStateVarData(&stateVars, &g_orient, &fe_space, ori_offset, - toml_opt.grain_statevar_offset, toml_opt.numStateVars, &matVars0); - if (myid == 0) { - printf("after setStateVarData. \n"); - } - } // end read of mat props, state vars and grains - - // Declare quadrature functions to store a vector representation of the - // Cauchy stress, in Voigt notation (s_11, s_22, s_33, s_23, s_13, s_12), for - // the beginning of the step and the end of the step. - int stressOffset = 6; - QuadratureFunction sigma0(&qspace, stressOffset); - QuadratureFunction sigma1(&qspace, stressOffset); - QuadratureFunction q_vonMises(&qspace, 1); - initQuadFunc(&sigma0, 0.0); - initQuadFunc(&sigma1, 0.0); - initQuadFunc(&q_vonMises, 0.0); - - // The tangent stiffness of the Cauchy stress will - // actually be the real material tangent stiffness (4th order tensor) and have - // 36 components due to symmetry. - int matGradOffset = 36; - QuadratureFunction matGrd(&qspace, matGradOffset); - initQuadFunc(&matGrd, 0.0); - - // define the end of step (or incrementally updated) material history - // variables - int vdim = matVars0.GetVDim(); - QuadratureFunction matVars1(&qspace, vdim); - initQuadFunc(&matVars1, 0.0); - - // declare a quadrature function to store the beginning step kinematic variables - // for any incremental kinematics. Right now this is used to store the beginning - // step deformation gradient on the model. - int kinDim = 9; - QuadratureFunction kinVars0(&qspace, kinDim); - initQuadFuncTensorIdentity(&kinVars0, &fe_space); - - // Define a grid function for the global reference configuration, the beginning - // step configuration, the global deformation, the current configuration/solution - // guess, and the incremental nodal displacements - ParGridFunction x_ref(&fe_space); - ParGridFunction x_beg(&fe_space); - ParGridFunction x_cur(&fe_space); - // x_diff would be our displacement - ParGridFunction x_diff(&fe_space); - ParGridFunction v_cur(&fe_space); - - // define a vector function coefficient for the initial deformation - // (based on a velocity projection) and reference configuration. - // Additionally define a vector function coefficient for computing - // the grid velocity prior to a velocity projection - VectorFunctionCoefficient refconfig(dim, ReferenceConfiguration); - - // Initialize the reference and beginning step configuration grid functions - // with the refconfig vector function coefficient. - x_beg.ProjectCoefficient(refconfig); - x_ref.ProjectCoefficient(refconfig); - x_cur.ProjectCoefficient(refconfig); - - // Define grid function for the velocity solution grid function - // WITH Dirichlet BCs - - // Define a VectorFunctionCoefficient to initialize a grid function - VectorFunctionCoefficient init_grid_func(dim, InitGridFunction); - - // initialize boundary condition, velocity, and - // incremental nodal displacment grid functions by projection the - // VectorFunctionCoefficient function onto them - x_diff.ProjectCoefficient(init_grid_func); - v_cur.ProjectCoefficient(init_grid_func); - - // Construct the nonlinear mechanics operator. Note that q_grain0 is - // being passed as the matVars0 quadarture function. This is the only - // history variable considered at this moment. Consider generalizing - // this where the grain info is a possible subset only of some - // material history variable quadrature function. Also handle the - // case where there is no grain data. - if (myid == 0) { - printf("before SystemDriver constructor. \n"); - } - - // Now to make sure all of our state variables and other such type of variables are on the device. - // If we don't do the below than whenever var = #.# for example will occur back on the host and then - // brought back to the device. - matVars0.UseDevice(true); - matVars1.UseDevice(true); - sigma0.UseDevice(true); - sigma1.UseDevice(true); - matGrd.UseDevice(true); - kinVars0.UseDevice(true); - q_vonMises.UseDevice(true); - matProps.UseDevice(true); - - { - // fix me: should the mesh nodes be on the device? - GridFunction *nodes = &x_cur; // set a nodes grid function to global current configuration - int owns_nodes = 0; - pmesh->SwapNodes(nodes, owns_nodes); // pmesh has current configuration nodes - nodes = NULL; - } - - SystemDriver oper(fe_space, - toml_opt, matVars0, - matVars1, sigma0, sigma1, matGrd, - kinVars0, q_vonMises, &elemMatVars, x_ref, x_beg, x_cur, - matProps, matVarsOffset); - - if (toml_opt.visit || toml_opt.conduit || toml_opt.paraview || toml_opt.adios2) { - oper.ProjectVolume(volume); - } - if (myid == 0) { - printf("after SystemDriver constructor. \n"); - } - - // get the essential true dof list. This may not be used. - const Array ess_tdof_list = oper.GetEssTDofList(); - - // declare incremental nodal displacement solution vector - Vector v_sol(fe_space.TrueVSize()); v_sol.UseDevice(true); - Vector v_prev(fe_space.TrueVSize()); v_prev.UseDevice(true);// this sizing is correct - v_sol = 0.0; - - // Save data for VisIt visualization. - // The below is used to take advantage of mfem's custom Visit plugin - // It could also allow for restart files later on. - // If we have large simulations although the current method of printing everything - // as text will cause issues. The data should really be saved in some binary format. - // If you don't then you'll often find that the printed data lags behind where - // the simulation is currently at. This really becomes noticiable if you have - // a lot of data that you want to output for the user. It might be nice if this - // was either a netcdf or hdf5 type format instead. - CALI_MARK_BEGIN("main_vis_init"); - VisItDataCollection visit_dc(toml_opt.basename, pmesh); - ParaViewDataCollection paraview_dc(toml_opt.basename, pmesh); -#ifdef MFEM_USE_CONDUIT - ConduitDataCollection conduit_dc(toml_opt.basename, pmesh); -#endif -#ifdef MFEM_USE_ADIOS2 - const std::string basename = toml_opt.basename + ".bp"; - ADIOS2DataCollection *adios2_dc = new ADIOS2DataCollection(MPI_COMM_WORLD, basename, pmesh); -#endif - if (toml_opt.paraview) { - paraview_dc.SetLevelsOfDetail(toml_opt.order); - paraview_dc.SetDataFormat(VTKFormat::BINARY); - paraview_dc.SetHighOrderOutput(false); - - paraview_dc.RegisterField("ElementVolume", &volume); - - if (toml_opt.mech_type == MechType::EXACMECH) { - if(toml_opt.light_up) { - oper.ProjectCentroid(*elem_centroid); - oper.ProjectElasticStrains(*elastic_strain); - oper.ProjectOrientation(quats); - paraview_dc.RegisterField("ElemCentroid", elem_centroid); - paraview_dc.RegisterField("XtalElasticStrain", elastic_strain); - paraview_dc.RegisterField("LatticeOrientation", &quats); - } - } - - paraview_dc.SetCycle(0); - paraview_dc.SetTime(0.0); - paraview_dc.Save(); - - paraview_dc.RegisterField("Displacement", &x_diff); - paraview_dc.RegisterField("Stress", &stress); - paraview_dc.RegisterField("Velocity", &v_cur); - paraview_dc.RegisterField("VonMisesStress", &vonMises); - paraview_dc.RegisterField("HydrostaticStress", &hydroStress); - - if (toml_opt.mech_type == MechType::EXACMECH) { - // We also want to project the values out originally - // so our initial values are correct - oper.ProjectDpEff(dpeff); - oper.ProjectEffPlasticStrain(pleff); - oper.ProjectOrientation(quats); - oper.ProjectShearRate(gdots); - oper.ProjectH(hardness); - - paraview_dc.RegisterField("DpEff", &dpeff); - paraview_dc.RegisterField("EffPlasticStrain", &pleff); - if(!toml_opt.light_up) { - paraview_dc.RegisterField("LatticeOrientation", &quats); - } - paraview_dc.RegisterField("ShearRate", &gdots); - paraview_dc.RegisterField("Hardness", &hardness); - } - } - - if (toml_opt.visit) { - visit_dc.SetPrecision(12); - - visit_dc.RegisterField("ElementVolume", &volume); - - if (toml_opt.mech_type == MechType::EXACMECH) { - if(toml_opt.light_up) { - oper.ProjectCentroid(*elem_centroid); - oper.ProjectElasticStrains(*elastic_strain); - oper.ProjectOrientation(quats); - visit_dc.RegisterField("ElemCentroid", elem_centroid); - visit_dc.RegisterField("XtalElasticStrain", elastic_strain); - visit_dc.RegisterField("LatticeOrientation", &quats); - } - } - - visit_dc.SetCycle(0); - visit_dc.SetTime(0.0); - visit_dc.Save(); - - visit_dc.RegisterField("Displacement", &x_diff); - visit_dc.RegisterField("Stress", &stress); - visit_dc.RegisterField("Velocity", &v_cur); - visit_dc.RegisterField("VonMisesStress", &vonMises); - visit_dc.RegisterField("HydrostaticStress", &hydroStress); - - if (toml_opt.mech_type == MechType::EXACMECH) { - // We also want to project the values out originally - // so our initial values are correct - - oper.ProjectDpEff(dpeff); - oper.ProjectEffPlasticStrain(pleff); - oper.ProjectOrientation(quats); - oper.ProjectShearRate(gdots); - oper.ProjectH(hardness); - - visit_dc.RegisterField("DpEff", &dpeff); - visit_dc.RegisterField("EffPlasticStrain", &pleff); - if(!toml_opt.light_up) { - visit_dc.RegisterField("LatticeOrientation", &quats); - } - visit_dc.RegisterField("ShearRate", &gdots); - visit_dc.RegisterField("Hardness", &hardness); - } - } - -#ifdef MFEM_USE_CONDUIT - if (toml_opt.conduit) { - // conduit_dc.SetProtocol("json"); - conduit_dc.RegisterField("ElementVolume", &volume); - - conduit_dc.SetCycle(0); - conduit_dc.SetTime(0.0); - conduit_dc.Save(); - - conduit_dc.RegisterField("Displacement", &x_diff); - conduit_dc.RegisterField("Stress", &stress); - conduit_dc.RegisterField("Velocity", &v_cur); - conduit_dc.RegisterField("VonMisesStress", &vonMises); - conduit_dc.RegisterField("HydrostaticStress", &hydroStress); - - if (toml_opt.mech_type == MechType::EXACMECH) { - // We also want to project the values out originally - // so our initial values are correct - oper.ProjectDpEff(dpeff); - oper.ProjectEffPlasticStrain(pleff); - oper.ProjectOrientation(quats); - oper.ProjectShearRate(gdots); - oper.ProjectH(hardness); - - conduit_dc.RegisterField("DpEff", &dpeff); - conduit_dc.RegisterField("EffPlasticStrain", &pleff); - conduit_dc.RegisterField("LatticeOrientation", &quats); - conduit_dc.RegisterField("ShearRate", &gdots); - conduit_dc.RegisterField("Hardness", &hardness); - } - } + mfem::Hypre::Init(); #endif -#ifdef MFEM_USE_ADIOS2 - if (toml_opt.adios2) { - adios2_dc->SetParameter("SubStreams", std::to_string(num_procs / 2) ); - - adios2_dc->RegisterField("ElementAttribute", elem_attr); - adios2_dc->RegisterField("ElementVolume", &volume); - - if (toml_opt.mech_type == MechType::EXACMECH) { - if(toml_opt.light_up) { - oper.ProjectCentroid(*elem_centroid); - oper.ProjectElasticStrains(*elastic_strain); - oper.ProjectOrientation(quats); - adios2_dc->RegisterField("ElemCentroid", elem_centroid); - adios2_dc->RegisterField("XtalElasticStrain", elastic_strain); - adios2_dc->RegisterField("LatticeOrientation", &quats); - } - } - - adios2_dc->SetCycle(0); - adios2_dc->SetTime(0.0); - adios2_dc->Save(); - - adios2_dc->DeregisterField("ElementAttribute"); - adios2_dc->RegisterField("Displacement", &x_diff); - adios2_dc->RegisterField("Stress", &stress); - adios2_dc->RegisterField("Velocity", &v_cur); - adios2_dc->RegisterField("VonMisesStress", &vonMises); - adios2_dc->RegisterField("HydrostaticStress", &hydroStress); - - if (toml_opt.mech_type == MechType::EXACMECH) { - // We also want to project the values out originally - // so our initial values are correct - oper.ProjectDpEff(dpeff); - oper.ProjectEffPlasticStrain(pleff); - oper.ProjectOrientation(quats); - oper.ProjectShearRate(gdots); - oper.ProjectH(hardness); - - adios2_dc->RegisterField("DpEff", &dpeff); - adios2_dc->RegisterField("EffPlasticStrain", &pleff); - // We should already have this registered if using the light-up script - if(!toml_opt.light_up) { - adios2_dc->RegisterField("LatticeOrientation", &quats); - } - adios2_dc->RegisterField("ShearRate", &gdots); - adios2_dc->RegisterField("Hardness", &hardness); - } - } -#endif - if (myid == 0) { - printf("after visualization if-block \n"); - } - CALI_MARK_END("main_vis_init"); - // initialize/set the time - double t = 0.0; - oper.SetTime(t); - - bool last_step = false; - - double dt_real; - - for (int ti = 1; ti <= toml_opt.nsteps; ti++) { - if (myid == 0) { - printf("inside timestep loop %d \n", ti); - } - // Get out our current delta time step - if (toml_opt.dt_cust) { - dt_real = toml_opt.cust_dt[ti - 1]; - } - else if (toml_opt.dt_auto) { - const double dt_system = oper.GetDt(); - dt_real = min(dt_system, toml_opt.t_final - t); - } - else { - dt_real = min(toml_opt.dt, toml_opt.t_final - t); - } - - // compute current time - t = t + dt_real; - last_step = (std::abs(t - toml_opt.t_final) <= std::abs(1e-3 * dt_real)); - - // set time on the simulation variables and the model through the - // nonlinear mechanics operator class - oper.SetTime(t); - oper.SetDt(dt_real); - oper.solVars.SetLastStep(last_step); - - // If our boundary condition changes for a step, we need to have an initial - // corrector step that ensures the solver has an easier time solving the PDE. - t1 = MPI_Wtime(); - if (BCManager::getInstance().getUpdateStep(ti)) { - if (myid == 0) { - std::cout << "Changing boundary conditions this step: " << ti << std::endl; - } - v_prev = v_sol; - // Update the BC data - oper.UpdateEssBdr(); - oper.UpdateVelocity(v_cur, v_sol); - oper.SolveInit(v_prev, v_sol); - // oper.SolveInit(v_sol); - // distribute the solution vector to v_cur - v_cur.Distribute(v_sol); - } - oper.UpdateVelocity(v_cur, v_sol); - // This will always occur - oper.Solve(v_sol); - - // Our expected dt could have changed - if (toml_opt.dt_auto) { - t = oper.solVars.GetTime(); - dt_real = oper.solVars.GetDTime(); - // Check to see if this has changed or not - last_step = (std::abs(t - toml_opt.t_final) <= std::abs(1e-3 * dt_real)); - } - - t2 = MPI_Wtime(); - times[ti - 1] = t2 - t1; - - // distribute the solution vector to v_cur - v_cur.Distribute(v_sol); - - // find the displacement vector as u = x_cur - x_reference - subtract(x_cur, x_ref, x_diff); - // update the beginning step stress and material state variables - // prior to the next time step for all Exa material models - // This also updates the deformation gradient with the beginning step - // deformation gradient stored on an Exa model - - oper.UpdateModel(); - - // Update our beginning time step coords with our end time step coords - x_beg = x_cur; - - if (last_step || (ti % toml_opt.vis_steps) == 0) { - if (myid == 0) { - cout << "step " << ti << ", t = " << t << endl; - } - CALI_MARK_BEGIN("main_vis_update"); - if (toml_opt.visit || toml_opt.conduit || toml_opt.paraview || toml_opt.adios2) { - // mesh and stress output. Consider moving this to a separate routine - // We might not want to update the vonMises stuff - oper.ProjectModelStress(stress); - oper.ProjectVolume(volume); - oper.ProjectVonMisesStress(vonMises, stress); - oper.ProjectHydroStress(hydroStress, stress); - - if (toml_opt.mech_type == MechType::EXACMECH) { - if(toml_opt.light_up) { - oper.ProjectCentroid(*elem_centroid); - oper.ProjectElasticStrains(*elastic_strain); - } - oper.ProjectDpEff(dpeff); - oper.ProjectEffPlasticStrain(pleff); - oper.ProjectOrientation(quats); - oper.ProjectShearRate(gdots); - oper.ProjectH(hardness); + // Scope block to ensure proper MPI cleanup and resource deallocation + { + /* + * Performance Timing Setup: + * - Initialize wall-clock timer for total simulation time measurement + * - Set up per-timestep timing vector for performance analysis + * - Enable detailed timing data for strong/weak scaling studies + */ + double start = MPI_Wtime(); + /** + * **PHASE 2: COMMAND LINE PROCESSING AND CONFIGURATION** + */ + + /* + * Options File Processing: + * - Default to "options.toml" configuration file + * - Support command line override with -opt/--option flag + * - Enable multiple configuration scenarios without recompilation + */ + const char* toml_file = "options.toml"; + mfem::OptionsParser args(argc, argv); + args.AddOption(&toml_file, "-opt", "--option", "Option file to use."); + args.Parse(); + // Error handling for invalid command line arguments + if (!args.Good()) { + if (myid == 0) { + args.PrintUsage(std::cout); } - } - - if (toml_opt.visit) { - visit_dc.SetCycle(ti); - visit_dc.SetTime(t); - // Our visit data is now saved off - visit_dc.Save(); - } - if (toml_opt.paraview) { - paraview_dc.SetCycle(ti); - paraview_dc.SetTime(t); - // Our paraview data is now saved off - paraview_dc.Save(); - } -#ifdef MFEM_USE_CONDUIT - if (toml_opt.conduit) { - conduit_dc.SetCycle(ti); - conduit_dc.SetTime(t); - // Our conduit data is now saved off - conduit_dc.Save(); - } -#endif -#ifdef MFEM_USE_ADIOS2 - if (toml_opt.adios2) { - adios2_dc->SetCycle(ti); - adios2_dc->SetTime(t); - // Our adios2 data is now saved off - adios2_dc->Save(); - } -#endif - CALI_MARK_END("main_vis_update"); - } // end output scope - if (last_step) { - break; - } - } // end loop over time steps - - // Free the used memory. - delete pmesh; - // Now find out how long everything took to run roughly - double end = MPI_Wtime(); - - double sim_time = end - start; - double avg_sim_time; - - MPI_Allreduce(&sim_time, &avg_sim_time, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - int world_size; - MPI_Comm_size(MPI_COMM_WORLD, &world_size); - - { - std::ostringstream oss; - - oss << "./time/time_solve." << myid << ".txt"; - std::string file_name = oss.str(); - std::ofstream file; - file.open(file_name, std::ios::out | std::ios::app); - - for (int i = 0; i < toml_opt.nsteps; i++) { - std::ostringstream strs; - strs << setprecision(8) << times[i] << "\n"; - std::string str = strs.str(); - file << str; - } - - file.close(); - } - - - if (myid == 0) { - printf("The process took %lf seconds to run\n", (avg_sim_time / world_size)); - } - - if(toml_opt.light_up) { - delete elem_centroid; - delete elastic_strain; - } - -#ifdef MFEM_USE_ADIOS2 - if (toml_opt.adios2) { - delete elem_attr; - } - delete adios2_dc; + CALI_MARK_END("main_driver_init"); + MPI_Finalize(); + return 1; + } + + // Print MFEM version information for reproducibility and debugging + if (myid == 0) { + printf("MFEM Version: %d \n", mfem::GetVersion()); + } + + /* + * Configuration File Parsing: + * - Load complete simulation configuration from TOML file + * - Validate all required parameters and consistency checks + * - Print configuration summary for verification + */ + ExaOptions toml_opt; + toml_opt.parse_options(toml_file, myid); + + exaconstit::UnifiedLogger& logger = exaconstit::UnifiedLogger::get_instance(); + logger.initialize(toml_opt); + + toml_opt.print_options(); + + /** + * **PHASE 3: DEVICE CONFIGURATION AND MEMORY MANAGEMENT** + */ + + /* + * Device Execution Model Setup: + * - Configure CPU, OpenMP, or GPU execution based on options + * - Set up RAJA performance portability layer for device-agnostic kernels + * - Priority order: GPU (CUDA/HIP) > OpenMP > CPU for optimal performance + */ + std::string device_config = "cpu"; + + if (toml_opt.solvers.rtmodel == RTModel::CPU) { + device_config = "cpu"; + } else if (toml_opt.solvers.rtmodel == RTModel::OPENMP) { + device_config = "raja-omp"; + } else if (toml_opt.solvers.rtmodel == RTModel::GPU) { +#if defined(RAJA_ENABLE_CUDA) + device_config = "raja-cuda"; +#elif defined(RAJA_ENABLE_HIP) + device_config = "raja-hip"; #endif - -} // Used to ensure any mpi functions are scopped to only this section - MPI_Barrier(MPI_COMM_WORLD); - MPI_Finalize(); - - return 0; -} - -void ReferenceConfiguration(const Vector &x, Vector &y) -{ - // set the reference, stress free, configuration - y = x; -} - -void InitGridFunction(const Vector & /*x*/, Vector &y) -{ - y = 0.; -} - -bool checkMaterialArgs(MechType mt, bool cp, int ngrains, int numProps, - int numStateVars) -{ - bool err = true; - - if (cp && (ngrains < 1)) { - cerr << "\nSpecify number of grains for use with cp input arg." << '\n'; - err = false; - } - - if (mt != MechType::NOTYPE && (numProps < 1)) { - cerr << "\nMust specify material properties for mechanical model or cp calculation." << '\n'; - err = false; - } - - // always input a state variables file with initial values for all models - if (numStateVars < 1) { - cerr << "\nMust specifiy state variables." << '\n'; - } - - return err; -} - -void setStateVarData(Vector* sVars, Vector* orient, ParFiniteElementSpace *fes, - int grainSize, int grainIntoStateVarOffset, - int stateVarSize, QuadratureFunction* qf) -{ - // put element grain orientation data on the quadrature points. - const IntegrationRule *ir; - double* qf_data = qf->HostReadWrite(); - int qf_offset = qf->GetVDim(); // offset = grainSize + stateVarSize - QuadratureSpaceBase* qspace = qf->GetSpace(); - - int myid; - MPI_Comm_rank(MPI_COMM_WORLD, &myid); - - // check to make sure the sum of the input sizes matches the offset of - // the input quadrature function - if (qf_offset != (grainSize + stateVarSize)) { - if (myid == 0) { - cerr << "\nsetStateVarData: Input state variable and grain sizes do not " - "match quadrature function initialization." << '\n'; - } - } - - // get the data for the material state variables and grain orientations for - // nonzero grainSize(s), which implies a crystal plasticity calculation - double* grain_data = NULL; - if (grainSize > 0) { - grain_data = orient->HostReadWrite(); - } - - double* sVars_data = sVars->HostReadWrite(); - int elem_atr; - - int offset1; - int offset2; - if (grainIntoStateVarOffset < 0) { // put grain data at end - // print warning to screen since this case could arise from a user - // simply not setting this parameter - if (myid == 0) { - std::cout << "warning::setStateVarData grain data placed at end of" - << " state variable array. Check grain_statevar_offset input arg." << "\n"; - } - - offset1 = stateVarSize - 1; - offset2 = qf_offset; - } - else if (grainIntoStateVarOffset == 0) { // put grain data at beginning - offset1 = -1; - offset2 = grainSize; - } - else { // put grain data somewhere in the middle - offset1 = grainIntoStateVarOffset - 1; - offset2 = grainIntoStateVarOffset + grainSize; - } - - // loop over elements - for (int i = 0; i < fes->GetNE(); ++i) { - ir = &(qspace->GetIntRule(i)); - - // full history variable offset including grain data - int elem_offset = qf_offset * ir->GetNPoints(); - - // get the element attribute. Note this assumes that there is an element attribute - // for all elements in the mesh corresponding to the grain id to which the element - // belongs. - elem_atr = fes->GetAttribute(i) - 1; - // loop over quadrature points - for (int j = 0; j < ir->GetNPoints(); ++j) { - // loop over quadrature point material state variable data - double varData; - int igrain = 0; - int istateVar = 0; - for (int k = 0; k < qf_offset; ++k) { - // index into either the grain data or the material state variable - // data depending on the setting of offset1 and offset2. This handles - // tacking on the grain data at the beginning of the total material - // state variable quadarture function, the end, or somewhere in the - // middle, which is dictated by grainIntoStateVarOffset, which is - // ultimately a program input. If grainSize == 0 for non-crystal - // plasticity problems, we never get into the if-block that gets - // data from the grain_data. In fact, grain_data should be a null - // pointer - if (k > offset1 && k < offset2) { - varData = grain_data[grainSize * (elem_atr) + igrain]; - ++igrain; + } + /* + * MFEM Device Configuration: + * - Configure device memory management for host/device data movement + * - Set up automatic memory synchronization for CPU/GPU execution + * - Enable high-performance device kernels for linear algebra operations + */ + mfem::Device device; + if (toml_opt.solvers.rtmodel == RTModel::GPU) { + device.SetMemoryTypes(mfem::MemoryType::HOST_64, mfem::MemoryType::DEVICE); + } + device.Configure(device_config.c_str()); + + // Print device configuration for verification and debugging + if (myid == 0) { + printf("\n"); + device.Print(); + printf("\n"); + } + + /** + * **PHASE 4: SIMULATION STATE AND MESH INITIALIZATION** + */ + + /* + * SimulationState Creation: + * - Initialize complete simulation state from parsed options + * - Set up parallel mesh with domain decomposition + * - Create finite element spaces and degree-of-freedom mappings + * - Initialize all quadrature functions for material state variables + * - Set up boundary condition management systems + */ + auto sim_state = std::make_shared(toml_opt); + + auto pmesh = sim_state->GetMesh(); + + CALI_MARK_END("main_driver_init"); + /* + * Mesh and DOF Information: + * - Query mesh dimension and finite element space size + * - Print parallel mesh statistics for load balancing verification + * - Display total degrees of freedom for memory estimation + */ + HYPRE_Int glob_size = sim_state->GetMeshParFiniteElementSpace()->GlobalTrueVSize(); + pmesh->PrintInfo(); + + if (myid == 0) { + std::cout << "***********************************************************\n"; + std::cout << "dim(u) = " << glob_size << "\n"; + std::cout << "***********************************************************\n"; + } + + /** + * **PHASE 5: FIELD INITIALIZATION AND GRID FUNCTIONS** + */ + + /* + * Grid Function Setup: + * - Get displacement and velocity field references from simulation state + * - Initialize vector coefficient function for zero initial conditions + * - Project initial conditions onto finite element spaces + * - Prepare fields for time-stepping algorithm + */ + + auto x_diff = sim_state->GetDisplacement(); + auto v_cur = sim_state->GetVelocity(); + + x_diff->operator=(0.0); + v_cur->operator=(0.0); + + /** + * **PHASE 6: SOLVER SYSTEM CONSTRUCTION** + */ + + /* + * SystemDriver Initialization: + * - Create main simulation driver with complete solver configuration + * - Initialize Newton-Raphson nonlinear solver with line search options + * - Set up Krylov iterative linear solvers with algebraic multigrid preconditioning + * - Configure essential boundary condition enforcement + * - Prepare material model interfaces and state variable management + */ + SystemDriver oper(sim_state); + + // Get essential true DOF list for boundary condition enforcement + const mfem::Array ess_tdof_list = oper.GetEssTDofList(); + /* + * PostProcessing Setup: + * - Initialize post-processing driver for field projection and output + * - Set up volume averaging calculations for homogenization + * - Configure visualization data collection (VisIt, ParaView, ADIOS2) + * - Prepare performance and convergence monitoring + */ + PostProcessingDriver post_process(sim_state, toml_opt); + /** + * **PHASE 7: MAIN TIME-STEPPING LOOP** + */ + + /* + * Time-Stepping Algorithm: + * - Implements implicit time integration with Newton-Raphson iteration + * - Supports adaptive time stepping based on convergence behavior + * - Handles time-dependent boundary conditions with smooth transitions + * - Performs material state updates and post-processing at each step + */ + int ti = 0; + auto v_sol = sim_state->GetPrimalField(); + while (!sim_state->IsFinished()) { + ti++; + // Print timestep information and timing statistics + if (myid == 0) { + std::cout << "Simulation cycle: " << ti << std::endl; + sim_state->PrintTimeStats(); } - else { - varData = sVars_data[istateVar]; - ++istateVar; + /* + * Current Time Step Processing: + * - Retrieve current simulation time and time step size + * - Update time-dependent material properties and boundary conditions + * - Prepare solver state for current time increment + */ + + /* + * Boundary Condition Change Detection: + * - Check if boundary conditions change for current time step + * - Apply corrector step (SolveInit) for smooth BC transitions + * - This prevents convergence issues with sudden load changes + */ + if (BCManager::GetInstance().GetUpdateStep(ti)) { + if (myid == 0) { + std::cout << "Changing boundary conditions this step: " << ti << std::endl; + } + + // Update boundary condition data and apply corrector step + oper.UpdateEssBdr(); + oper.UpdateVelocity(); + oper.SolveInit(); } - - qf_data[(elem_offset * i) + qf_offset * j + k] = varData; - } // end loop over material state variables - } // end loop over quadrature points - } // end loop over elements - - // Set the pointers to null after using them to hopefully stop any weirdness from happening -} - -void initQuadFunc(QuadratureFunction *qf, double val) -{ - double* qf_data = qf->ReadWrite(); - const int npts = qf->Size(); - - // The below should be exactly the same as what - // the other for loop is trying to accomplish - MFEM_FORALL(i, npts, { - qf_data[i] = val; - }); -} - -void initQuadFuncTensorIdentity(QuadratureFunction *qf, ParFiniteElementSpace *fes) -{ - double* qf_data = qf->ReadWrite(); - const int qf_offset = qf->GetVDim(); // offset at each integration point - QuadratureSpaceBase* qspace = qf->GetSpace(); - const IntegrationRule *ir = &(qspace->GetIntRule(0)); - const int int_pts = ir->GetNPoints(); - const int nelems = fes->GetNE(); - - // loop over elements - MFEM_FORALL(i, nelems, { - const int elem_offset = qf_offset * int_pts; - // Hard coded this for now for a 3x3 matrix - // Fix later if we update - for (int j = 0; j < int_pts; ++j) { - qf_data[i * elem_offset + j * qf_offset] = 1.0; - qf_data[i * elem_offset + j * qf_offset + 1] = 0.0; - qf_data[i * elem_offset + j * qf_offset + 2] = 0.0; - qf_data[i * elem_offset + j * qf_offset + 3] = 0.0; - qf_data[i * elem_offset + j * qf_offset + 4] = 1.0; - qf_data[i * elem_offset + j * qf_offset + 5] = 0.0; - qf_data[i * elem_offset + j * qf_offset + 6] = 0.0; - qf_data[i * elem_offset + j * qf_offset + 7] = 0.0; - qf_data[i * elem_offset + j * qf_offset + 8] = 1.0; - } - }); -} - -void setBdrConditions(Mesh *mesh) -{ - // modify MFEM auto cuboidal hex mesh generation boundary - // attributes to correspond to correct ExaConstit boundary conditions. - // Look at ../../mesh/mesh.cpp Make3D() to see how boundary attributes - // are set and modify according to ExaConstit convention - - // loop over boundary elements - for (int i = 0; iGetNBE(); ++i) { - int bdrAttr = mesh->GetBdrAttribute(i); - - switch (bdrAttr) { - // note, srw wrote SetBdrAttribute() in ../../mesh/mesh.hpp - case 1: - mesh->SetBdrAttribute(i, 1); // bottom - break; - case 2: - mesh->SetBdrAttribute(i, 3); // front - break; - case 3: - mesh->SetBdrAttribute(i, 5); // right - break; - case 4: - mesh->SetBdrAttribute(i, 6); // back - break; - case 5: - mesh->SetBdrAttribute(i, 2); // left - break; - case 6: - mesh->SetBdrAttribute(i, 4); // top - break; - } - } - - return; -} - -void reorderMeshElements(Mesh *mesh, const int *nxyz) -{ - // reorder mesh elements depending on how the - // computational cells are ordered in the grain map file. - - // Right now, the element ordering in the grain map file - // starts at (0,0,0) and increments in z, y, then x coordinate - // directions. - - // MFEM Make3D(.) mesh gen increments in x, y, then z. - - Array order(nxyz[0] * nxyz[1] * nxyz[2]); - int id = 0; - int k = 0; - for (int z = 0; z < nxyz[2]; ++z) { - for (int y = 0; y < nxyz[1]; ++y) { - for (int x = 0; x < nxyz[0]; ++x) { - id = (nxyz[2] * nxyz[1]) * x + nxyz[2] * y + z; - order[k] = id; - ++k; - } - } - } - - mesh->ReorderElements(order, true); - - return; -} - -void setElementGrainIDs(Mesh *mesh, const Vector grainMap, int ncols, int offset) -{ - // after a call to reorderMeshElements, the elements in the serial - // MFEM mesh should be ordered the same as the input grainMap - // vector. Set the element attribute to the grain id. This vector - // has stride of 4 with the id in the 3rd position indexing from 0 - - const double* data = grainMap.HostRead(); - - // loop over elements - for (int i = 0; iGetNE(); ++i) { - mesh->SetAttribute(i, data[ncols * i + offset]); - } - - return; -} - -// Projects the element attribute to GridFunction nodes -// This also assumes this the GridFunction is an L2 FE space -void projectElemAttr2GridFunc(Mesh *mesh, ParGridFunction *elem_attr) { - // loop over elementsQ - elem_attr->HostRead(); - ParFiniteElementSpace *pfes = elem_attr->ParFESpace(); - Array vdofs; - for (int i = 0; i < mesh->GetNE(); ++i) { - pfes->GetElementVDofs(i, vdofs); - const double ea = static_cast(mesh->GetAttribute(i)); - elem_attr->SetSubVector(vdofs, ea); - } -} + /* + * Main Solution Process: + * 1. Update velocity field with current boundary conditions + * 2. Solve nonlinear system using Newton-Raphson iteration + * 3. Check convergence and handle potential failures + */ + oper.UpdateVelocity(); + oper.Solve(); + + /* + * Time Step Completion: + * - Advance simulation time and check for final step + * - Update material state variables with converged solution + * - Perform post-processing calculations and output generation + */ + sim_state->FinishCycle(); + oper.UpdateModel(); + post_process.Update(ti, sim_state->GetTrueCycleTime()); + } // end loop over time steps + + /** + * **PHASE 8: SIMULATION COMPLETION AND PERFORMANCE ANALYSIS** + */ + + /* + * Performance Timing Collection: + * - Measure total simulation wall-clock time + * - Compute average timing across all MPI processes + * - Report performance metrics for scalability analysis + */ + double end = MPI_Wtime(); + + double sim_time = end - start; + double avg_sim_time; + + MPI_Allreduce(&sim_time, &avg_sim_time, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + int world_size; + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + if (myid == 0) { + printf("The process took %lf seconds to run\n", (avg_sim_time / world_size)); + } + logger.shutdown(); + } // End of main simulation scope for proper resource cleanup + + /* + * MPI Cleanup and Termination: + * - Synchronize all processes before exit + * - Finalize MPI environment and clean up resources + * - Return success status to operating system + */ + + MPI_Barrier(MPI_COMM_WORLD); + MPI_Finalize(); + + return 0; +} \ No newline at end of file diff --git a/src/mechanics_ecmech.cpp b/src/mechanics_ecmech.cpp deleted file mode 100644 index 7d2e5e9..0000000 --- a/src/mechanics_ecmech.cpp +++ /dev/null @@ -1,450 +0,0 @@ -#include "mfem.hpp" -#include "mfem/general/forall.hpp" -#include "ECMech_cases.h" -#include "ECMech_const.h" - -#include "mechanics_model.hpp" -#include "mechanics_log.hpp" -#include "mechanics_ecmech.hpp" -#include "BCManager.hpp" -#include // log -#include -#include // cerr -#include "RAJA/RAJA.hpp" -#include "mechanics_kernels.hpp" - -using namespace mfem; - -namespace { - -// Sets-up everything for the kernel -void kernel_setup(const int npts, const int nstatev, - const double dt, const double temp_k, const double* vel_grad_array, - const double* stress_array, const double* state_vars_array, - double* stress_svec_p_array, double* d_svec_p_array, - double* w_vec_array, double* vol_ratio_array, - double* eng_int_array, double* tempk_array, double* dEff) -{ - // vgrad is kinda a pain to deal with as a raw 1d array, so we're - // going to just use a RAJA view here. The data is taken to be in col. major format. - // It might be nice to eventually create a type alias for the below or - // maybe something like it. - - const int ind_int_eng = nstatev - ecmech::ne; - const int ind_vols = ind_int_eng - 1; - - const int DIM = 3; - std::array perm {{ 2, 1, 0 } }; - RAJA::Layout layout = RAJA::make_permuted_layout({{ ecmech::ndim, ecmech::ndim, npts } }, perm); - RAJA::View > vgrad_view(vel_grad_array, layout); - - MFEM_FORALL(i_pts, npts, { - // Might want to eventually set these all up using RAJA views. It might simplify - // things later on. - // These are our inputs - const double* state_vars = &(state_vars_array[i_pts * nstatev]); - const double* stress = &(stress_array[i_pts * ecmech::nsvec]); - // Here is all of our ouputs - double* eng_int = &(eng_int_array[i_pts * ecmech::ne]); - double* w_vec = &(w_vec_array[i_pts * ecmech::nwvec]); - double* vol_ratio = &(vol_ratio_array[i_pts * ecmech::nvr]); - // A few variables are set up as the 6-vec deviatoric + tr(tens) values - int ind_svecp = i_pts * ecmech::nsvp; - double* stress_svec_p = &(stress_svec_p_array[ind_svecp]); - double* d_svec_p = &(d_svec_p_array[ind_svecp]); - - tempk_array[i_pts] = temp_k; - - for (int i = 0; i < ecmech::ne; i++) { - eng_int[i] = state_vars[ind_int_eng + i]; - } - - // Here we have the skew portion of our velocity gradient as represented as an - // axial vector. - w_vec[0] = 0.5 * (vgrad_view(2, 1, i_pts) - vgrad_view(1, 2, i_pts)); - w_vec[1] = 0.5 * (vgrad_view(0, 2, i_pts) - vgrad_view(2, 0, i_pts)); - w_vec[2] = 0.5 * (vgrad_view(1, 0, i_pts) - vgrad_view(0, 1, i_pts)); - - // Really we're looking at the negative of J but this will do... - double d_mean = -ecmech::onethird * (vgrad_view(0, 0, i_pts) + vgrad_view(1, 1, i_pts) + vgrad_view(2, 2, i_pts)); - // The 1st 6 components are the symmetric deviatoric portion of our velocity gradient - // The last value is simply the trace of the deformation rate - d_svec_p[0] = vgrad_view(0, 0, i_pts) + d_mean; - d_svec_p[1] = vgrad_view(1, 1, i_pts) + d_mean; - d_svec_p[2] = vgrad_view(2, 2, i_pts) + d_mean; - d_svec_p[3] = 0.5 * (vgrad_view(2, 1, i_pts) + vgrad_view(1, 2, i_pts)); - d_svec_p[4] = 0.5 * (vgrad_view(2, 0, i_pts) + vgrad_view(0, 2, i_pts)); - d_svec_p[5] = 0.5 * (vgrad_view(1, 0, i_pts) + vgrad_view(0, 1, i_pts)); - d_svec_p[6] = -3.0 * d_mean; - - double d_vecd_sm[ecmech::ntvec]; - ecmech::svecToVecd(d_vecd_sm, d_svec_p); - dEff[i_pts] = ecmech::vecd_Deff(d_vecd_sm); - - vol_ratio[0] = state_vars[ind_vols]; - vol_ratio[1] = vol_ratio[0] * exp(d_svec_p[ecmech::iSvecP] * dt); - vol_ratio[3] = vol_ratio[1] - vol_ratio[0]; - vol_ratio[2] = vol_ratio[3] / (dt * 0.5 * (vol_ratio[0] + vol_ratio[1])); - - for (int i = 0; i < ecmech::nsvec; i++) { - stress_svec_p[i] = stress[i]; - } - - double stress_mean = -ecmech::onethird * (stress[0] + stress[1] + stress[2]); - stress_svec_p[0] += stress_mean; - stress_svec_p[1] += stress_mean; - stress_svec_p[2] += stress_mean; - stress_svec_p[ecmech::iSvecP] = stress_mean; - }); // end of npts loop -} // end of set-up func - -// Retrieves the stress and reorders it into the desired 6 vec format. A copy of that vector -// is sent back to the CPU for the time being. It also stores all of the state variables into their -// appropriate vector. Finally, it saves off the material tangent stiffness vector. In the future, -// if PA is used then the 4D 3x3x3x3 tensor is saved off rather than the 6x6 2D matrix. -void kernel_postprocessing(const int npts, const int nstatev, const double dt, const double* dEff, - const double* stress_svec_p_array, const double* vol_ratio_array, - const double* eng_int_array, const double* beg_state_vars_array, - double* state_vars_array, double* stress_array, - double* ddsdde_array, Assembly assembly) -{ - const int ind_int_eng = nstatev - ecmech::ne; - const int ind_pl_work = ecmech::evptn::iHistA_flowStr; - const int ind_vols = ind_int_eng - 1; - - MFEM_FORALL(i_pts, npts, { - // These are our outputs - double* state_vars = &(state_vars_array[i_pts * nstatev]); - const double* beg_state_vars = &(beg_state_vars_array[i_pts * nstatev]); - double* stress = &(stress_array[i_pts * ecmech::nsvec]); - // Here is all of our ouputs - const double* eng_int = &(eng_int_array[i_pts * ecmech::ne]); - const double* vol_ratio = &(vol_ratio_array[i_pts * ecmech::nvr]); - // A few variables are set up as the 6-vec deviatoric + tr(tens) values - int ind_svecp = i_pts * ecmech::nsvp; - const double* stress_svec_p = &(stress_svec_p_array[ind_svecp]); - - // We need to update our state variables to include the volume ratio and - // internal energy portions - state_vars[ind_vols] = vol_ratio[1]; - for (int i = 0; i < ecmech::ne; i++) { - state_vars[ind_int_eng + i] = eng_int[i]; - } - - if(dEff[i_pts] > ecmech::idp_tiny_sqrt) { - state_vars[ind_pl_work] *= dEff[i_pts] * dt; - } else { - state_vars[ind_pl_work] = 0.0; - } - state_vars[ind_pl_work] += beg_state_vars[ind_pl_work]; - - // Here we're converting back from our deviatoric + pressure representation of our - // Cauchy stress back to the Voigt notation of stress. - double stress_mean = -stress_svec_p[ecmech::iSvecP]; - for (int i = 0; i < ecmech::nsvec; i++) { - stress[i] = stress_svec_p[i]; - } - - stress[0] += stress_mean; - stress[1] += stress_mean; - stress[2] += stress_mean; - }); // end of npts loop - - // No need to transpose this if running on the GPU and doing EA - if ((assembly == Assembly::EA) and mfem::Device::Allows(Backend::DEVICE_MASK)) { return; } - else - { - // std::cout << "rotate tan stiffness mat" << std::endl; - MFEM_FORALL(i_pts, npts, { - // ExaCMech saves this in Row major, so we need to get out the transpose. - // The good thing is we can do this all in place no problem. - double* ddsdde = &(ddsdde_array[i_pts * ecmech::nsvec * ecmech::nsvec]); - for (int i = 0; i < ecmech::nsvec; ++i) { - for (int j = i + 1; j < ecmech::nsvec; ++j) { - double tmp = ddsdde[(ecmech::nsvec * j) +i]; - ddsdde[(ecmech::nsvec * j) +i] = ddsdde[(ecmech::nsvec * i) +j]; - ddsdde[(ecmech::nsvec * i) +j] = tmp; - } - } - }); - } -} // end of post-processing func - -// The different CPU, OpenMP, and GPU kernels aren't needed here, since they're -// defined in ExaCMech itself. -void kernel(const ecmech::matModelBase* mat_model_base, - const int npts, const double dt, double* state_vars_array, - double* stress_svec_p_array, double* d_svec_p_array, - double* w_vec_array, double* ddsdde_array, - double* vol_ratio_array, double* eng_int_array, - double* tempk_array, double* sdd_array) -{ - mat_model_base->getResponseECM(dt, d_svec_p_array, w_vec_array, vol_ratio_array, - eng_int_array, stress_svec_p_array, state_vars_array, - tempk_array, sdd_array, ddsdde_array, npts); -} - -} // End private namespace - - -ExaCMechModel::ExaCMechModel( - mfem::QuadratureFunction *_q_stress0, mfem::QuadratureFunction *_q_stress1, - mfem::QuadratureFunction *_q_matGrad, mfem::QuadratureFunction *_q_matVars0, - mfem::QuadratureFunction *_q_matVars1, - mfem::ParGridFunction* _beg_coords, mfem::ParGridFunction* _end_coords, - mfem::Vector *_props, int _nProps, int _nStateVars, double _temp_k, - ecmech::ExecutionStrategy _accel, Assembly _assembly, std::string mat_model_name - ) : - ExaModel(_q_stress0, _q_stress1, _q_matGrad, _q_matVars0, _q_matVars1, - _beg_coords, _end_coords, _props, _nProps, _nStateVars, _assembly), - temp_k(_temp_k), accel(_accel) -{ - setup_data_structures(); - setup_model(mat_model_name); -} - -void ExaCMechModel::setup_data_structures() { - // First find the total number of points that we're dealing with so nelems * nqpts - const int vdim = stress0->GetVDim(); - const int size = stress0->Size(); - const int npts = size / vdim; - // Now initialize all of the vectors that we'll be using with our class - vel_grad_array = new mfem::Vector(npts * ecmech::ndim * ecmech::ndim, mfem::Device::GetMemoryType()); - eng_int_array = new mfem::Vector(npts * ecmech::ne, mfem::Device::GetMemoryType()); - w_vec_array = new mfem::Vector(npts * ecmech::nwvec, mfem::Device::GetMemoryType()); - vol_ratio_array = new mfem::Vector(npts * ecmech::nvr, mfem::Device::GetMemoryType()); - stress_svec_p_array = new mfem::Vector(npts * ecmech::nsvp, mfem::Device::GetMemoryType()); - d_svec_p_array = new mfem::Vector(npts * ecmech::nsvp, mfem::Device::GetMemoryType()); - tempk_array = new mfem::Vector(npts, mfem::Device::GetMemoryType()); - sdd_array = new mfem::Vector(npts * ecmech::nsdd, mfem::Device::GetMemoryType()); - eff_def_rate = new mfem::Vector(npts, mfem::Device::GetMemoryType()); - // If we're using a Device we'll want all of these vectors on it and staying there. - // Also, note that UseDevice() only returns a boolean saying if it's on the device or not - // rather than telling the vector whether or not it needs to lie on the device. - vel_grad_array->UseDevice(true); *vel_grad_array = 0.0; - eng_int_array->UseDevice(true); *eng_int_array = 0.0; - w_vec_array->UseDevice(true); *w_vec_array = 0.0; - vol_ratio_array->UseDevice(true); *vol_ratio_array = 0.0; - stress_svec_p_array->UseDevice(true); *stress_svec_p_array = 0.0; - d_svec_p_array->UseDevice(true); *d_svec_p_array = 0.0; - tempk_array->UseDevice(true); *tempk_array = 0.0; - sdd_array->UseDevice(true); *sdd_array = 0.0; - eff_def_rate->UseDevice(true); *eff_def_rate = 0.0; -} - -void ExaCMechModel::setup_model(std::string mat_model_name) { - // First aspect is setting up our various map structures - index_map = ecmech::modelParamIndexMap(mat_model_name); - // additional terms we need to add - index_map["num_volumes"] = 1; - index_map["index_volume"] = index_map["index_slip_rates"] + index_map["num_slip_system"]; - index_map["num_internal_energy"] = ecmech::ne; - index_map["index_internal_energy"] = index_map["index_volume"] + index_map["num_volumes"]; - - { - std::string s_shrateEff = "shrateEff"; - std::string s_shrEff = "shrEff"; - std::string s_pl_work = "pl_work"; - std::string s_quats = "quats"; - std::string s_gdot = "gdot"; - std::string s_hard = "hardness"; - std::string s_ieng = "int_eng"; - std::string s_rvol = "rel_vol"; - std::string s_est = "elas_strain"; - - std::pair i_sre = std::make_pair(index_map["index_effective_shear_rate"], 1); - std::pair i_se = std::make_pair(index_map["index_effective_shear"], 1); - std::pair i_plw = std::make_pair(index_map["index_flow_strength"], 1); - std::pair i_q = std::make_pair(index_map["index_lattice_ori"], 4); - std::pair i_g = std::make_pair(index_map["index_slip_rates"], index_map["num_slip_system"]); - std::pair i_h = std::make_pair(index_map["index_hardness"], index_map["num_hardening"]); - std::pair i_en = std::make_pair(index_map["index_internal_energy"], ecmech::ne); - std::pair i_rv = std::make_pair(index_map["index_volume"], 1); - std::pair i_est = std::make_pair(index_map["index_dev_elas_strain"], ecmech::ntvec); - - qf_mapping[s_shrateEff] = i_sre; - qf_mapping[s_shrEff] = i_se; - qf_mapping[s_pl_work] = i_plw; - qf_mapping[s_quats] = i_q; - qf_mapping[s_gdot] = i_g; - qf_mapping[s_hard] = i_h; - qf_mapping[s_ieng] = i_en; - qf_mapping[s_rvol] = i_rv; - qf_mapping[s_est] = i_est; - } - - // Now we can create our model - mat_model_base = ecmech::makeMatModel(mat_model_name); - // and update our model strides from the default values - size_t num_state_vars = index_map["num_hist"] + ecmech::ne + 1; - std::vector strides; - // Deformation rate stride - strides.push_back(ecmech::nsvp); - // Spin rate stride - strides.push_back(ecmech::ndim); - // Volume ratio stride - strides.push_back(ecmech::nvr); - // Internal energy stride - strides.push_back(ecmech::ne); - // Stress vector stride - strides.push_back(ecmech::nsvp); - // History variable stride - strides.push_back(num_state_vars); - // Temperature stride - strides.push_back(1); - // SDD stride - strides.push_back(ecmech::nsdd); - // Update our stride values from the default as our history strides are different - mat_model_base->updateStrides(strides); - - // Now get out the parameters to instantiate our history variables - // Opts and strs are just empty vectors of int and strings - std::vector params; - std::vector opts; - std::vector strs; - - for (int i = 0; i < matProps->Size(); i++) { - params.push_back(matProps->Elem(i)); - } - - // We really shouldn't see this change over time at least for our applications. - mat_model_base->initFromParams(opts, params, strs); - mat_model_base->complete(); - mat_model_base->setExecutionStrategy(accel); - - std::vector histInit; - { - std::vector names; - std::vector plot; - std::vector state; - mat_model_base->getHistInfo(names, histInit, plot, state); - } - - init_state_vars(histInit); -} - -void ExaCMechModel::init_state_vars(std::vector hist_init) -{ - mfem::Vector histInit(index_map["num_hist"], mfem::Device::GetMemoryType()); - histInit.UseDevice(true); histInit.HostReadWrite(); - assert(hist_init.size() == index_map["num_hist"]); - - for (uint i = 0; i < hist_init.size(); i++) { - histInit(i) = hist_init.at(i); - } - - const double* histInit_vec = histInit.Read(); - double* state_vars = matVars0->ReadWrite(); - - const size_t qf_size = (matVars0->Size()) / (matVars0->GetVDim()); - - const size_t vdim = matVars0->GetVDim(); - - const size_t ind_dp_eff = index_map["index_effective_shear_rate"]; - const size_t ind_eql_pl_strain = index_map["index_effective_shear"]; - const size_t ind_pl_work = index_map["index_flow_strength"]; - const size_t ind_num_evals = index_map["index_num_func_evals"]; - const size_t ind_hardness = index_map["index_hardness"]; - const size_t ind_vols = index_map["index_volume"]; - const size_t ind_int_eng = index_map["index_internal_energy"]; - const size_t ind_dev_elas_strain = index_map["index_dev_elas_strain"]; - const size_t ind_gdot = index_map["index_slip_rates"]; - const size_t num_slip = index_map["num_slip_system"]; - const size_t num_hardness = index_map["num_hardening"]; - - mfem::MFEM_FORALL(i, qf_size, { - const size_t ind = i * vdim; - - state_vars[ind + ind_dp_eff] = histInit_vec[ind_dp_eff]; - state_vars[ind + ind_eql_pl_strain] = histInit_vec[ind_eql_pl_strain]; - state_vars[ind + ind_pl_work] = histInit_vec[ind_pl_work]; - state_vars[ind + ind_num_evals] = histInit_vec[ind_num_evals]; - state_vars[ind + ind_vols] = 1.0; - - for (size_t j = 0; j < num_hardness; j++) { - state_vars[ind + ind_hardness + j] = histInit_vec[ind_hardness + j]; - } - - for (size_t j = 0; j < ecmech::ne; j++) { - state_vars[ind + ind_int_eng + j] = 0.0; - } - - for (size_t j = 0; j < ecmech::ntvec; j++) { - state_vars[ind + ind_dev_elas_strain + j] = histInit_vec[ind_dev_elas_strain + j]; - } - - for (size_t j = 0; j < num_slip; j++) { - state_vars[ind + ind_gdot + j] = histInit_vec[ind_gdot + j]; - } - }); -} - -// Our model set-up makes use of several preprocessing kernels, -// the actual material model kernel, and finally a post-processing kernel. -void ExaCMechModel::ModelSetup(const int nqpts, const int nelems, const int /*space_dim*/, - const int nnodes, const Vector &jacobian, - const Vector &loc_grad, const Vector &vel) -{ - const int nstatev = numStateVars; - - const double *jacobian_array = jacobian.Read(); - const double *loc_grad_array = loc_grad.Read(); - const double *vel_array = vel.Read(); - - // Here we call an initialization function which sets the end step stress - // and state variable variables to the initial time step values. - // Then the pointer to the underlying data array is returned and - // operated on to those end time step variables - double* state_vars_array = StateVarsSetup(); - const double *state_vars_beg = matVars0->Read(); - double* stress_array = StressSetup(); - // If we require a 4D tensor for PA applications then we might - // need to use something other than this for our applications. - QuadratureFunction* matGrad_qf = matGrad; - *matGrad_qf = 0.0; - double* ddsdde_array = matGrad_qf->ReadWrite(); - // All of these variables are stored on the material model class using - // the vector class. - *vel_grad_array = 0.0; - double* vel_grad_array_data = vel_grad_array->ReadWrite(); - double* stress_svec_p_array_data = stress_svec_p_array->ReadWrite(); - double* d_svec_p_array_data = d_svec_p_array->ReadWrite(); - double* w_vec_array_data = w_vec_array->ReadWrite(); - double* vol_ratio_array_data = vol_ratio_array->ReadWrite(); - double* eng_int_array_data = eng_int_array->ReadWrite(); - double* tempk_array_data = tempk_array->ReadWrite(); - double* sdd_array_data = sdd_array->ReadWrite(); - - const int npts = nqpts * nelems; - double* dEff = eff_def_rate->Write(); - - // If we're on the initial step we need to first calculate a - // solution where our vgrad is the 0 tensor across the entire - // body. After we obtain this we calculate our actual velocity - // gradient and use that to obtain the appropriate stress field - // but don't calculate the material tangent stiffness tensor. - // Any other step is much simpler, and we just calculate the - // velocity gradient, run our model, and then obtain our material - // tangent stiffness matrix. - CALI_MARK_BEGIN("ecmech_setup"); - exaconstit::kernel::grad_calc(nqpts, nelems, nnodes, jacobian_array, loc_grad_array, - vel_array, vel_grad_array_data); - - kernel_setup(npts, nstatev, dt, temp_k, vel_grad_array_data, - stress_array, state_vars_array, stress_svec_p_array_data, - d_svec_p_array_data, w_vec_array_data, - vol_ratio_array_data, eng_int_array_data, tempk_array_data, dEff); - CALI_MARK_END("ecmech_setup"); - CALI_MARK_BEGIN("ecmech_kernel"); - kernel(mat_model_base, npts, dt, state_vars_array, - stress_svec_p_array_data, d_svec_p_array_data, w_vec_array_data, - ddsdde_array, vol_ratio_array_data, eng_int_array_data, - tempk_array_data, sdd_array_data); - CALI_MARK_END("ecmech_kernel"); - - CALI_MARK_BEGIN("ecmech_postprocessing"); - kernel_postprocessing(npts, nstatev, dt, dEff, stress_svec_p_array_data, - vol_ratio_array_data, eng_int_array_data, state_vars_beg, state_vars_array, - stress_array, ddsdde_array, assembly); - CALI_MARK_END("ecmech_postprocessing"); -} // End of ModelSetup function diff --git a/src/mechanics_ecmech.hpp b/src/mechanics_ecmech.hpp deleted file mode 100644 index 8ffe24e..0000000 --- a/src/mechanics_ecmech.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include "mfem.hpp" -#include "ECMech_const.h" -#include "ECMech_matModelBase.h" -#include "mechanics_model.hpp" - -/// Base class for all of our ExaCMechModels. -class ExaCMechModel : public ExaModel -{ - protected: - - // Current temperature in Kelvin degrees - double temp_k; - - // A pointer to our actual material model class that ExaCMech uses. - // The childern classes to this class will have also have another variable - // that actually contains the real material model that is then dynamically casted - // to this base class during the instantiation of the class. - ecmech::matModelBase* mat_model_base; - - // Our accelartion that we are making use of. - ecmech::ExecutionStrategy accel; - - // Temporary variables that we'll be making use of when running our - // models. - mfem::Vector *vel_grad_array; - mfem::Vector *eng_int_array; - mfem::Vector *w_vec_array; - mfem::Vector *vol_ratio_array; - mfem::Vector *stress_svec_p_array; - mfem::Vector *d_svec_p_array; - mfem::Vector *tempk_array; - mfem::Vector *sdd_array; - mfem::Vector *eff_def_rate; - - std::map index_map; - - public: - ExaCMechModel(mfem::QuadratureFunction *_q_stress0, mfem::QuadratureFunction *_q_stress1, - mfem::QuadratureFunction *_q_matGrad, mfem::QuadratureFunction *_q_matVars0, - mfem::QuadratureFunction *_q_matVars1, - mfem::ParGridFunction* _beg_coords, mfem::ParGridFunction* _end_coords, - mfem::Vector *_props, int _nProps, int _nStateVars, double _temp_k, - ecmech::ExecutionStrategy _accel, Assembly _assembly, std::string mat_model_name); - - ~ExaCMechModel() - { - delete vel_grad_array; - delete eng_int_array; - delete w_vec_array; - delete vol_ratio_array; - delete stress_svec_p_array; - delete d_svec_p_array; - delete tempk_array; - delete sdd_array; - delete eff_def_rate; - delete mat_model_base; - } - - void setup_data_structures(); - void setup_model(std::string mat_model_name); - void init_state_vars(std::vector hist_init); - - /** This model takes in the velocity, det(jacobian), and local_grad/jacobian. - * It then computes velocity gradient symm and skw tensors and passes - * that to our material model in order to get out our Cauchy stress and - * the material tangent matrix (d \sigma / d Vgrad_{sym}). It also - * updates all of the state variables that live at the quadrature pts. - */ - void ModelSetup(const int nqpts, const int nelems, const int /*space_dim*/, - const int nnodes, const mfem::Vector &jacobian, - const mfem::Vector &loc_grad, const mfem::Vector &vel) override; - - /// If we needed to do anything to our state variables once things are solved - /// for we do that here. - virtual void UpdateModelVars() override {} - void calcDpMat(mfem::QuadratureFunction &/* DpMat */) const override {} -}; - diff --git a/src/mechanics_integrators.cpp b/src/mechanics_integrators.cpp deleted file mode 100644 index 382c588..0000000 --- a/src/mechanics_integrators.cpp +++ /dev/null @@ -1,2088 +0,0 @@ - -#include "mfem.hpp" -#include "mfem/general/forall.hpp" -#include "mechanics_integrators.hpp" -#include "mechanics_log.hpp" -#include "BCManager.hpp" -#include // log -#include -#include // cerr -#include "RAJA/RAJA.hpp" - -using namespace mfem; -using namespace std; - -// member functions for the ExaNLFIntegrator -double ExaNLFIntegrator::GetElementEnergy( - const FiniteElement &el, - ElementTransformation &Ttr, - const Vector &elfun) -{ - // we are not interested in the element energy at this time - (void) el; - (void) Ttr; - (void) elfun; - - return 0.0; -} - -// Outside of the UMAT function calls this should be the function called -// to assemble our residual vectors. -void ExaNLFIntegrator::AssembleElementVector( - const FiniteElement &el, - ElementTransformation &Ttr, - const Vector &elfun, Vector &elvect) -{ - CALI_CXX_MARK_SCOPE("enlfi_assembleElemVec"); - int dof = el.GetDof(), dim = el.GetDim(); - - DenseMatrix DSh, DS; - DenseMatrix Jpt; - DenseMatrix PMatI, PMatO; - // This is our stress tensor - DenseMatrix P(3); - - DSh.SetSize(dof, dim); - DS.SetSize(dof, dim); - Jpt.SetSize(dim); - - // PMatI would be our velocity in this case - PMatI.UseExternalData(elfun.GetData(), dof, dim); - elvect.SetSize(dof * dim); - - // PMatO would be our residual vector - elvect = 0.0; - PMatO.UseExternalData(elvect.HostReadWrite(), dof, dim); - - const IntegrationRule *ir = IntRule; - if (!ir) { - ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); // must match quadrature space - } - - for (int i = 0; i < ir->GetNPoints(); i++) { - const IntegrationPoint &ip = ir->IntPoint(i); - Ttr.SetIntPoint(&ip); - - // compute Jacobian of the transformation - Jpt = Ttr.InverseJacobian(); // Jrt = dxi / dX - - el.CalcDShape(ip, DSh); - Mult(DSh, Jpt, DS); // dN_a(xi) / dX = dN_a(xi)/dxi * dxi/dX - - double stress[6]; - model->GetElementStress(Ttr.ElementNo, i, false, stress, 6); - // Could probably later have this only set once... - // Would reduce the number mallocs that we're doing and - // should potentially provide a small speed boost. - P(0, 0) = stress[0]; - P(1, 1) = stress[1]; - P(2, 2) = stress[2]; - P(1, 2) = stress[3]; - P(0, 2) = stress[4]; - P(0, 1) = stress[5]; - - P(2, 1) = P(1, 2); - P(2, 0) = P(0, 2); - P(1, 0) = P(0, 1); - - DS *= (Ttr.Weight() * ip.weight); - AddMult(DS, P, PMatO); - } - - return; -} - -void ExaNLFIntegrator::AssembleElementGrad( - const FiniteElement &el, - ElementTransformation &Ttr, - const Vector & /*elfun*/, DenseMatrix &elmat) -{ - CALI_CXX_MARK_SCOPE("enlfi_assembleElemGrad"); - int dof = el.GetDof(), dim = el.GetDim(); - - DenseMatrix DSh, DS, Jrt; - - // Now time to start assembling stuff - DenseMatrix grad_trans, temp; - DenseMatrix tan_stiff; - - constexpr int ngrad_dim2 = 36; - double matGrad[ngrad_dim2]; - // Delta in our timestep - double dt = model->GetModelDt(); - - // temp1 is now going to become the transpose Bmatrix as seen in - // [B^t][tan_stiff][B] - grad_trans.SetSize(dof * dim, 6); - // We need a temp matrix to store our first matrix results as seen in here - temp.SetSize(6, dof * dim); - - tan_stiff.UseExternalData(&matGrad[0], 6, 6); - - DSh.SetSize(dof, dim); - DS.SetSize(dof, dim); - Jrt.SetSize(dim); - elmat.SetSize(dof * dim); - - const IntegrationRule *ir = IntRule; - if (!ir) { - ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); // <--- must match quadrature space - } - - elmat = 0.0; - - for (int i = 0; i < ir->GetNPoints(); i++) { - const IntegrationPoint &ip = ir->IntPoint(i); - Ttr.SetIntPoint(&ip); - CalcInverse(Ttr.Jacobian(), Jrt); - - el.CalcDShape(ip, DSh); - Mult(DSh, Jrt, DS); - - model->GetElementMatGrad(Ttr.ElementNo, i, matGrad, ngrad_dim2); - // temp1 is B^t - model->GenerateGradMatrix(DS, grad_trans); - // We multiple our quadrature wts here to our tan_stiff matrix - tan_stiff *= dt * ip.weight * Ttr.Weight(); - // We use kgeom as a temporary matrix - // kgeom = [Cstiff][B] - MultABt(tan_stiff, grad_trans, temp); - // We now add our [B^t][kgeom] product to our tangent stiffness matrix that - // we want to output to our material tangent stiffness matrix - AddMult(grad_trans, temp, elmat); - } - - return; -} - -// This performs the assembly step of our RHS side of our system: -// f_ik = -void ExaNLFIntegrator::AssemblePA(const FiniteElementSpace &fes) -{ - CALI_CXX_MARK_SCOPE("enlfi_assemblePA"); - Mesh *mesh = fes.GetMesh(); - const FiniteElement &el = *fes.GetFE(0); - space_dims = el.GetDim(); - const IntegrationRule *ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); - - nqpts = ir->GetNPoints(); - nnodes = el.GetDof(); - nelems = fes.GetNE(); - - auto W = ir->GetWeights().Read(); - geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS); - - // return a pointer to beginning step stress. This is used for output visualization - QuadratureFunction *stress_end = model->GetStress1(); - - if ((space_dims == 1) || (space_dims == 2)) { - MFEM_ABORT("Dimensions of 1 or 2 not supported."); - } - else { - const int dim = 3; - - if (grad.Size() != (nqpts * dim * nnodes)) { - grad.SetSize(nqpts * dim * nnodes, mfem::Device::GetMemoryType()); - { - DenseMatrix DSh; - const int offset = nnodes * dim; - double *qpts_dshape_data = grad.HostReadWrite(); - for (int i = 0; i < nqpts; i++) { - const IntegrationPoint &ip = ir->IntPoint(i); - DSh.UseExternalData(&qpts_dshape_data[offset * i], nnodes, dim); - el.CalcDShape(ip, DSh); - } - } - grad.UseDevice(true); - } - - // geom->J really isn't going to work for us as of right now. We could just reorder it - // to the version that we want it to be in instead... - if (jacobian.Size() != (dim * dim * nqpts * nelems)) { - jacobian.SetSize(dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); - jacobian.UseDevice(true); - } - - if (dmat.Size() != (dim * dim * nqpts * nelems)) { - dmat.SetSize(dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); - dmat.UseDevice(true); - } - - const int DIM2 = 2; - const int DIM3 = 3; - const int DIM4 = 4; - std::array perm4 {{ 3, 2, 1, 0 } }; - std::array perm3 {{ 2, 1, 0 } }; - - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > J(jacobian.ReadWrite(), layout_jacob); - - RAJA::Layout layout_stress = RAJA::make_permuted_layout({{ 2 * dim, nqpts, nelems } }, perm3); - RAJA::View > S(stress_end->ReadWrite(), - layout_stress); - - RAJA::View > D(dmat.ReadWrite(), layout_jacob); - - RAJA::Layout layout_geom = RAJA::make_permuted_layout({{ nqpts, dim, dim, nelems } }, perm4); - RAJA::View > geom_j_view(geom->J.Read(), layout_geom); - const int nqpts_ = nqpts; - const int dim_ = dim; - MFEM_FORALL(i, nelems, { - for (int j = 0; j < nqpts_; j++) { - for (int k = 0; k < dim_; k++) { - for (int l = 0; l < dim_; l++) { - J(l, k, j, i) = geom_j_view(j, l, k, i); - } - } - } - }); - - MFEM_FORALL(i_elems, nelems, { - double adj[dim_ * dim_]; - // So, we're going to say this view is constant however we're going to mutate the values only in - // that one scoped section for the quadrature points. - // adj is actually in row major memory order but if we set this to col. major than this view - // will act as the transpose of adj A which is what we want. - RAJA::View > A(&adj[0], dim_, dim_); - // RAJA::View > A(&adj[0], layout_adj); - for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { - // If we scope this then we only need to carry half the number of variables around with us for - // the adjugate term. - { - const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 - const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 - const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 - const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 - const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 - const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 - const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 - const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 - const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 - // adj(J) - adj[0] = (J22 * J33) - (J23 * J32); // 0,0 - adj[1] = (J32 * J13) - (J12 * J33); // 0,1 - adj[2] = (J12 * J23) - (J22 * J13); // 0,2 - adj[3] = (J31 * J23) - (J21 * J33); // 1,0 - adj[4] = (J11 * J33) - (J13 * J31); // 1,1 - adj[5] = (J21 * J13) - (J11 * J23); // 1,2 - adj[6] = (J21 * J32) - (J31 * J22); // 2,0 - adj[7] = (J31 * J12) - (J11 * J32); // 2,1 - adj[8] = (J11 * J22) - (J12 * J21); // 2,2 - } - - D(0, 0, j_qpts, i_elems) = S(0, j_qpts, i_elems) * A(0, 0) + - S(5, j_qpts, i_elems) * A(0, 1) + - S(4, j_qpts, i_elems) * A(0, 2); - D(1, 0, j_qpts, i_elems) = S(0, j_qpts, i_elems) * A(1, 0) + - S(5, j_qpts, i_elems) * A(1, 1) + - S(4, j_qpts, i_elems) * A(1, 2); - D(2, 0, j_qpts, i_elems) = S(0, j_qpts, i_elems) * A(2, 0) + - S(5, j_qpts, i_elems) * A(2, 1) + - S(4, j_qpts, i_elems) * A(2, 2); - - D(0, 1, j_qpts, i_elems) = S(5, j_qpts, i_elems) * A(0, 0) + - S(1, j_qpts, i_elems) * A(0, 1) + - S(3, j_qpts, i_elems) * A(0, 2); - D(1, 1, j_qpts, i_elems) = S(5, j_qpts, i_elems) * A(1, 0) + - S(1, j_qpts, i_elems) * A(1, 1) + - S(3, j_qpts, i_elems) * A(1, 2); - D(2, 1, j_qpts, i_elems) = S(5, j_qpts, i_elems) * A(2, 0) + - S(1, j_qpts, i_elems) * A(2, 1) + - S(3, j_qpts, i_elems) * A(2, 2); - - D(0, 2, j_qpts, i_elems) = S(4, j_qpts, i_elems) * A(0, 0) + - S(3, j_qpts, i_elems) * A(0, 1) + - S(2, j_qpts, i_elems) * A(0, 2); - D(1, 2, j_qpts, i_elems) = S(4, j_qpts, i_elems) * A(1, 0) + - S(3, j_qpts, i_elems) * A(1, 1) + - S(2, j_qpts, i_elems) * A(1, 2); - D(2, 2, j_qpts, i_elems) = S(4, j_qpts, i_elems) * A(2, 0) + - S(3, j_qpts, i_elems) * A(2, 1) + - S(2, j_qpts, i_elems) * A(2, 2); - } // End of doing J_{ij}\sigma_{jk} / nqpts loop - }); // End of elements - MFEM_FORALL(i_elems, nelems, { - for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { - for (int i = 0; i < dim_; i++) { - for (int j = 0; j < dim_; j++) { - D(j, i, j_qpts, i_elems) *= W[j_qpts]; - } - } - } - }); - } // End of if statement -} - -// In the below function we'll be applying the below action on our material -// tangent matrix C^{tan} at each quadrature point as: -// D_{ijkm} = 1 / det(J) * w_{qpt} * adj(J)^T_{ij} C^{tan}_{ijkl} adj(J)_{lm} -// where D is our new 4th order tensor, J is our jacobian calculated from the -// mesh geometric factors, and adj(J) is the adjugate of J. -void ExaNLFIntegrator::AssembleGradPA(const mfem::Vector &/* x */, const FiniteElementSpace &fes) -{ - this->AssembleGradPA(fes); -} - -// In the below function we'll be applying the below action on our material -// tangent matrix C^{tan} at each quadrature point as: -// D_{ijkm} = 1 / det(J) * w_{qpt} * adj(J)^T_{ij} C^{tan}_{ijkl} adj(J)_{lm} -// where D is our new 4th order tensor, J is our jacobian calculated from the -// mesh geometric factors, and adj(J) is the adjugate of J. -void ExaNLFIntegrator::AssembleGradPA(const FiniteElementSpace &fes) -{ - CALI_CXX_MARK_SCOPE("enlfi_assemblePAG"); - Mesh *mesh = fes.GetMesh(); - const FiniteElement &el = *fes.GetFE(0); - space_dims = el.GetDim(); - const IntegrationRule *ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); - - nqpts = ir->GetNPoints(); - nnodes = el.GetDof(); - nelems = fes.GetNE(); - auto W = ir->GetWeights().Read(); - - if ((space_dims == 1) || (space_dims == 2)) { - MFEM_ABORT("Dimensions of 1 or 2 not supported."); - } - else { - const int dim = 3; - - if (grad.Size() != (nqpts * dim * nnodes)) { - grad.SetSize(nqpts * dim * nnodes, mfem::Device::GetMemoryType()); - { - DenseMatrix DSh; - const int offset = nnodes * dim; - double *qpts_dshape_data = grad.HostReadWrite(); - for (int i = 0; i < nqpts; i++) { - const IntegrationPoint &ip = ir->IntPoint(i); - DSh.UseExternalData(&qpts_dshape_data[offset * i], nnodes, dim); - el.CalcDShape(ip, DSh); - } - } - grad.UseDevice(true); - } - - // geom->J really isn't going to work for us as of right now. We could just reorder it - // to the version that we want it to be in instead... - if (jacobian.Size() != (dim * dim * nqpts * nelems)) { - jacobian.SetSize(dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); - jacobian.UseDevice(true); - - geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS); - - const int DIM4 = 4; - std::array perm4 {{ 3, 2, 1, 0 } }; - - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > J(jacobian.ReadWrite(), layout_jacob); - - RAJA::Layout layout_geom = RAJA::make_permuted_layout({{ nqpts, dim, dim, nelems } }, perm4); - RAJA::View > geom_j_view(geom->J.Read(), layout_geom); - const int nqpts_ = nqpts; - const int dim_ = dim; - MFEM_FORALL(i, nelems, { - for (int j = 0; j < nqpts_; j++) { - for (int k = 0; k < dim_; k++) { - for (int l = 0; l < dim_; l++) { - J(l, k, j, i) = geom_j_view(j, l, k, i); - } - } - } - }); - } - - if (pa_dmat.Size() != (dim * dim * dim * dim * nqpts * nelems)) { - pa_dmat.SetSize(dim * dim * dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); - pa_dmat.UseDevice(true); - } - - pa_dmat = 0.0; - - const int DIM2 = 2; - const int DIM4 = 4; - const int DIM6 = 6; - std::array perm6 {{ 5, 4, 3, 2, 1, 0 } }; - std::array perm4 {{ 3, 2, 1, 0 } }; - std::array perm2 {{ 1, 0 } }; - - // bunch of helper RAJA views to make dealing with data easier down below in our kernel. - - RAJA::Layout layout_4Dtensor = RAJA::make_permuted_layout({{ dim, dim, dim, dim, nqpts, nelems } }, perm6); - RAJA::View > C(model->GetMTanData(), layout_4Dtensor); - // Swapped over to row order since it makes sense in later applications... - // Should make C row order as well for PA operations - RAJA::View > D(pa_dmat.ReadWrite(), nelems, nqpts, dim, dim, dim, dim); - - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > J(jacobian.ReadWrite(), layout_jacob); - - RAJA::Layout layout_adj = RAJA::make_permuted_layout({{ dim, dim } }, perm2); - - double dt = model->GetModelDt(); - const int nqpts_ = nqpts; - const int dim_ = dim; - // This loop we'll want to parallelize the rest are all serial for now. - MFEM_FORALL(i_elems, nelems, { - double adj[dim_ * dim_]; - double c_detJ; - // So, we're going to say this view is constant however we're going to mutate the values only in - // that one scoped section for the quadrature points. - RAJA::View > A(&adj[0], layout_adj); - for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { - // If we scope this then we only need to carry half the number of variables around with us for - // the adjugate term. - { - const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 - const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 - const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 - const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 - const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 - const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 - const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 - const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 - const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 - const double detJ = J11 * (J22 * J33 - J32 * J23) - - /* */ J21 * (J12 * J33 - J32 * J13) + - /* */ J31 * (J12 * J23 - J22 * J13); - c_detJ = 1.0 / detJ * W[j_qpts] * dt; - // adj(J) - adj[0] = (J22 * J33) - (J23 * J32); // 0,0 - adj[1] = (J32 * J13) - (J12 * J33); // 0,1 - adj[2] = (J12 * J23) - (J22 * J13); // 0,2 - adj[3] = (J31 * J23) - (J21 * J33); // 1,0 - adj[4] = (J11 * J33) - (J13 * J31); // 1,1 - adj[5] = (J21 * J13) - (J11 * J23); // 1,2 - adj[6] = (J21 * J32) - (J31 * J22); // 2,0 - adj[7] = (J31 * J12) - (J11 * J32); // 2,1 - adj[8] = (J11 * J22) - (J12 * J21); // 2,2 - } - // Unrolled part of the loops just so we wouldn't have so many nested ones. - // If we were to get really ambitious we could eliminate also the m indexed - // loop... - for (int n = 0; n < dim_; n++) { - for (int m = 0; m < dim_; m++) { - for (int l = 0; l < dim_; l++) { - D(i_elems, j_qpts, 0, 0, l, n) += (A(0, 0) * C(0, 0, l, m, j_qpts, i_elems) + - A(1, 0) * C(1, 0, l, m, j_qpts, i_elems) + - A(2, 0) * C(2, 0, l, m, j_qpts, i_elems)) * A(m, n); - D(i_elems, j_qpts, 0, 1, l, n) += (A(0, 0) * C(0, 1, l, m, j_qpts, i_elems) + - A(1, 0) * C(1, 1, l, m, j_qpts, i_elems) + - A(2, 0) * C(2, 1, l, m, j_qpts, i_elems)) * A(m, n); - D(i_elems, j_qpts, 0, 2, l, n) += (A(0, 0) * C(0, 2, l, m, j_qpts, i_elems) + - A(1, 0) * C(1, 2, l, m, j_qpts, i_elems) + - A(2, 0) * C(2, 2, l, m, j_qpts, i_elems)) * A(m, n); - D(i_elems, j_qpts, 1, 0, l, n) += (A(0, 1) * C(0, 0, l, m, j_qpts, i_elems) + - A(1, 1) * C(1, 0, l, m, j_qpts, i_elems) + - A(2, 1) * C(2, 0, l, m, j_qpts, i_elems)) * A(m, n); - D(i_elems, j_qpts, 1, 1, l, n) += (A(0, 1) * C(0, 1, l, m, j_qpts, i_elems) + - A(1, 1) * C(1, 1, l, m, j_qpts, i_elems) + - A(2, 1) * C(2, 1, l, m, j_qpts, i_elems)) * A(m, n); - D(i_elems, j_qpts, 1, 2, l, n) += (A(0, 1) * C(0, 2, l, m, j_qpts, i_elems) + - A(1, 1) * C(1, 2, l, m, j_qpts, i_elems) + - A(2, 1) * C(2, 2, l, m, j_qpts, i_elems)) * A(m, n); - D(i_elems, j_qpts, 2, 0, l, n) += (A(0, 2) * C(0, 0, l, m, j_qpts, i_elems) + - A(1, 2) * C(1, 0, l, m, j_qpts, i_elems) + - A(2, 2) * C(2, 0, l, m, j_qpts, i_elems)) * A(m, n); - D(i_elems, j_qpts, 2, 1, l, n) += (A(0, 2) * C(0, 1, l, m, j_qpts, i_elems) + - A(1, 2) * C(1, 1, l, m, j_qpts, i_elems) + - A(2, 2) * C(2, 1, l, m, j_qpts, i_elems)) * A(m, n); - D(i_elems, j_qpts, 2, 2, l, n) += (A(0, 2) * C(0, 2, l, m, j_qpts, i_elems) + - A(1, 2) * C(1, 2, l, m, j_qpts, i_elems) + - A(2, 2) * C(2, 2, l, m, j_qpts, i_elems)) * A(m, n); - } - } - } // End of Dikln = adj(J)_{ji} C_{jklm} adj(J)_{mn} loop - - // Unrolled part of the loops just so we wouldn't have so many nested ones. - for (int n = 0; n < dim_; n++) { - for (int l = 0; l < dim_; l++) { - D(i_elems, j_qpts, l, n, 0, 0) *= c_detJ; - D(i_elems, j_qpts, l, n, 0, 1) *= c_detJ; - D(i_elems, j_qpts, l, n, 0, 2) *= c_detJ; - D(i_elems, j_qpts, l, n, 1, 0) *= c_detJ; - D(i_elems, j_qpts, l, n, 1, 1) *= c_detJ; - D(i_elems, j_qpts, l, n, 1, 2) *= c_detJ; - D(i_elems, j_qpts, l, n, 2, 0) *= c_detJ; - D(i_elems, j_qpts, l, n, 2, 1) *= c_detJ; - D(i_elems, j_qpts, l, n, 2, 2) *= c_detJ; - } - } // End of D_{ijkl} *= 1/det(J) * w_{qpt} loop - } // End of quadrature loop - }); // End of Elements loop - } // End of else statement -} - -// Here we're applying the following action operation using the assembled "D" 2nd order -// tensor found above: -// y_{ik} = \nabla_{ij}\phi^T_{\epsilon} D_{jk} -void ExaNLFIntegrator::AddMultPA(const mfem::Vector & /*x*/, mfem::Vector &y) const -{ - CALI_CXX_MARK_SCOPE("enlfi_amPAV"); - if ((space_dims == 1) || (space_dims == 2)) { - MFEM_ABORT("Dimensions of 1 or 2 not supported."); - } - else { - const int dim = 3; - const int DIM3 = 3; - const int DIM4 = 4; - - std::array perm3 {{ 2, 1, 0 } }; - std::array perm4 {{ 3, 2, 1, 0 } }; - // Swapped over to row order since it makes sense in later applications... - // Should make C row order as well for PA operations - RAJA::Layout layout_tensor = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > D(dmat.Read(), layout_tensor); - // Our field variables that are inputs and outputs - RAJA::Layout layout_field = RAJA::make_permuted_layout({{ nnodes, dim, nelems } }, perm3); - RAJA::View > Y(y.ReadWrite(), layout_field); - // Transpose of the local gradient variable - RAJA::Layout layout_grads = RAJA::make_permuted_layout({{ nnodes, dim, nqpts } }, perm3); - RAJA::View > Gt(grad.Read(), layout_grads); - - const int nqpts_ = nqpts; - const int dim_ = dim; - const int nnodes_ = nnodes; - MFEM_FORALL(i_elems, nelems, { - for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { - for (int k = 0; k < dim_; k++) { - for (int j = 0; j < dim_; j++) { - for (int i = 0; i < nnodes_; i++) { - Y(i, k, i_elems) += Gt(i, j, j_qpts) * D(j, k, j_qpts, i_elems); - } - } - } // End of the final action of Y_{ik} += Gt_{ij} T_{jk} - } // End of nQpts - }); // End of nelems - } // End of if statement -} - -// Here we're applying the following action operation using the assembled "D" 4th order -// tensor found above: -// y_{ik} = \nabla_{ij}\phi^T_{\epsilon} D_{jklm} \nabla_{mn}\phi_{\epsilon} x_{nl} -void ExaNLFIntegrator::AddMultGradPA(const mfem::Vector &x, mfem::Vector &y) const -{ - CALI_CXX_MARK_SCOPE("enlfi_amPAG"); - if ((space_dims == 1) || (space_dims == 2)) { - MFEM_ABORT("Dimensions of 1 or 2 not supported."); - } - else { - const int dim = 3; - const int DIM2 = 2; - const int DIM3 = 3; - const int DIM6 = 6; - - std::array perm3 {{ 2, 1, 0 } }; - std::array perm2 {{ 1, 0 } }; - // Swapped over to row order since it makes sense in later applications... - // Should make C row order as well for PA operations - RAJA::View > D(pa_dmat.Read(), nelems, nqpts, dim, dim, dim, dim); - // Our field variables that are inputs and outputs - RAJA::Layout layout_field = RAJA::make_permuted_layout({{ nnodes, dim, nelems } }, perm3); - RAJA::View > X(x.Read(), layout_field); - RAJA::View > Y(y.ReadWrite(), layout_field); - // Transpose of the local gradient variable - RAJA::Layout layout_grads = RAJA::make_permuted_layout({{ nnodes, dim, nqpts } }, perm3); - RAJA::View > Gt(grad.Read(), layout_grads); - - // View for our temporary 2d array - RAJA::Layout layout_adj = RAJA::make_permuted_layout({{ dim, dim } }, perm2); - const int nqpts_ = nqpts; - const int dim_ = dim; - const int nnodes_ = nnodes; - MFEM_FORALL(i_elems, nelems, { - for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { - double T[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - for (int i = 0; i < dim_; i++) { - for (int j = 0; j < dim_; j++) { - for (int k = 0; k < nnodes_; k++) { - T[0] += D(i_elems, j_qpts, 0, 0, i, j) * Gt(k, j, j_qpts) * X(k, i, i_elems); - T[1] += D(i_elems, j_qpts, 1, 0, i, j) * Gt(k, j, j_qpts) * X(k, i, i_elems); - T[2] += D(i_elems, j_qpts, 2, 0, i, j) * Gt(k, j, j_qpts) * X(k, i, i_elems); - T[3] += D(i_elems, j_qpts, 0, 1, i, j) * Gt(k, j, j_qpts) * X(k, i, i_elems); - T[4] += D(i_elems, j_qpts, 1, 1, i, j) * Gt(k, j, j_qpts) * X(k, i, i_elems); - T[5] += D(i_elems, j_qpts, 2, 1, i, j) * Gt(k, j, j_qpts) * X(k, i, i_elems); - T[6] += D(i_elems, j_qpts, 0, 2, i, j) * Gt(k, j, j_qpts) * X(k, i, i_elems); - T[7] += D(i_elems, j_qpts, 1, 2, i, j) * Gt(k, j, j_qpts) * X(k, i, i_elems); - T[8] += D(i_elems, j_qpts, 2, 2, i, j) * Gt(k, j, j_qpts) * X(k, i, i_elems); - } - } - } // End of doing tensor contraction of D_{jkmo}G_{op}X_{pm} - - RAJA::View > Tview(&T[0], layout_adj); - for (int k = 0; k < dim_; k++) { - for (int j = 0; j < dim_; j++) { - for (int i = 0; i < nnodes_; i++) { - Y(i, k, i_elems) += Gt(i, j, j_qpts) * Tview(j, k); - } - } - } // End of the final action of Y_{ik} += Gt_{ij} T_{jk} - } // End of nQpts - }); // End of nelems - } // End of if statement -} - -// This assembles the diagonal of our LHS which can be used as a preconditioner -void ExaNLFIntegrator::AssembleGradDiagonalPA(Vector &diag) const -{ - CALI_CXX_MARK_SCOPE("enlfi_AssembleGradDiagonalPA"); - - const IntegrationRule &ir = model->GetMatGrad()->GetSpace()->GetIntRule(0); - auto W = ir.GetWeights().Read(); - - if ((space_dims == 1) || (space_dims == 2)) { - MFEM_ABORT("Dimensions of 1 or 2 not supported."); - } - else { - const int dim = 3; - - const int DIM2 = 2; - const int DIM3 = 3; - const int DIM4 = 4; - - std::array perm4 {{ 3, 2, 1, 0 } }; - std::array perm3 {{ 2, 1, 0 } }; - std::array perm2 {{ 1, 0 } }; - - // bunch of helper RAJA views to make dealing with data easier down below in our kernel. - - RAJA::Layout layout_tensor = RAJA::make_permuted_layout({{ 2 * dim, 2 * dim, nqpts, nelems } }, perm4); - RAJA::View > K(model->GetMatGrad()->Read(), layout_tensor); - - // Our field variables that are inputs and outputs - RAJA::Layout layout_field = RAJA::make_permuted_layout({{ nnodes, dim, nelems } }, perm3); - RAJA::View > Y(diag.ReadWrite(), layout_field); - - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > J(jacobian.Read(), layout_jacob); - - RAJA::Layout layout_adj = RAJA::make_permuted_layout({{ dim, dim } }, perm2); - - RAJA::Layout layout_grads = RAJA::make_permuted_layout({{ nnodes, dim, nqpts } }, perm3); - RAJA::View > Gt(grad.Read(), layout_grads); - - double dt = model->GetModelDt(); - const int nqpts_ = nqpts; - const int dim_ = dim; - const int nnodes_ = nnodes; - // This loop we'll want to parallelize the rest are all serial for now. - MFEM_FORALL(i_elems, nelems, { - double adj[dim_ * dim_]; - double c_detJ; - // So, we're going to say this view is constant however we're going to mutate the values only in - // that one scoped section for the quadrature points. - RAJA::View > A(&adj[0], layout_adj); - for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { - // If we scope this then we only need to carry half the number of variables around with us for - // the adjugate term. - { - const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 - const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 - const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 - const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 - const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 - const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 - const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 - const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 - const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 - const double detJ = J11 * (J22 * J33 - J32 * J23) - - /* */ J21 * (J12 * J33 - J32 * J13) + - /* */ J31 * (J12 * J23 - J22 * J13); - c_detJ = 1.0 / detJ * W[j_qpts] * dt; - // adj(J) - adj[0] = (J22 * J33) - (J23 * J32); // 0,0 - adj[1] = (J32 * J13) - (J12 * J33); // 0,1 - adj[2] = (J12 * J23) - (J22 * J13); // 0,2 - adj[3] = (J31 * J23) - (J21 * J33); // 1,0 - adj[4] = (J11 * J33) - (J13 * J31); // 1,1 - adj[5] = (J21 * J13) - (J11 * J23); // 1,2 - adj[6] = (J21 * J32) - (J31 * J22); // 2,0 - adj[7] = (J31 * J12) - (J11 * J32); // 2,1 - adj[8] = (J11 * J22) - (J12 * J21); // 2,2 - } - for (int knodes = 0; knodes < nnodes_; knodes++) { - const double bx = Gt(knodes, 0, j_qpts) * A(0, 0) - + Gt(knodes, 1, j_qpts) * A(0, 1) - + Gt(knodes, 2, j_qpts) * A(0, 2); - - const double by = Gt(knodes, 0, j_qpts) * A(1, 0) - + Gt(knodes, 1, j_qpts) * A(1, 1) - + Gt(knodes, 2, j_qpts) * A(1, 2); - - const double bz = Gt(knodes, 0, j_qpts) * A(2, 0) - + Gt(knodes, 1, j_qpts) * A(2, 1) - + Gt(knodes, 2, j_qpts) * A(2, 2); - - Y(knodes, 0, i_elems) += c_detJ * (bx * (bx * K(0, 0, j_qpts, i_elems) - + by * K(0, 5, j_qpts, i_elems) - + bz * K(0, 4, j_qpts, i_elems)) - + by * (bx * K(5, 0, j_qpts, i_elems) - + by * K(5, 5, j_qpts, i_elems) - + bz * K(5, 4, j_qpts, i_elems)) - + bz * (bx * K(4, 0, j_qpts, i_elems) - + by * K(4, 5, j_qpts, i_elems) - + bz * K(4, 4, j_qpts, i_elems))); - - Y(knodes, 1, i_elems) += c_detJ * (bx * (bx * K(5, 5, j_qpts, i_elems) - + by * K(5, 1, j_qpts, i_elems) - + bz * K(5, 3, j_qpts, i_elems)) - + by * (bx * K(1, 5, j_qpts, i_elems) - + by * K(1, 1, j_qpts, i_elems) - + bz * K(1, 3, j_qpts, i_elems)) - + bz * (bx * K(3, 5, j_qpts, i_elems) - + by * K(3, 1, j_qpts, i_elems) - + bz * K(3, 3, j_qpts, i_elems))); - - Y(knodes, 2, i_elems) += c_detJ * (bx * (bx * K(4, 4, j_qpts, i_elems) - + by * K(4, 3, j_qpts, i_elems) - + bz * K(4, 2, j_qpts, i_elems)) - + by * (bx * K(3, 4, j_qpts, i_elems) - + by * K(3, 3, j_qpts, i_elems) - + bz * K(3, 2, j_qpts, i_elems)) - + bz * (bx * K(2, 4, j_qpts, i_elems) - + by * K(2, 3, j_qpts, i_elems) - + bz * K(2, 2, j_qpts, i_elems))); - } - } - }); - } -} - -/// Method defining element assembly. -/** The result of the element assembly is added and stored in the @a emat - Vector. */ -void ExaNLFIntegrator::AssembleGradEA(const Vector& /*x*/,const FiniteElementSpace &fes, Vector &emat) { - AssembleEA(fes, emat); -} -void ExaNLFIntegrator::AssembleEA(const FiniteElementSpace &fes, Vector &emat) -{ - CALI_CXX_MARK_SCOPE("enlfi_assembleEA"); - Mesh *mesh = fes.GetMesh(); - const FiniteElement &el = *fes.GetFE(0); - space_dims = el.GetDim(); - const IntegrationRule *ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); - - nqpts = ir->GetNPoints(); - nnodes = el.GetDof(); - nelems = fes.GetNE(); - auto W = ir->GetWeights().Read(); - - if ((space_dims == 1) || (space_dims == 2)) { - MFEM_ABORT("Dimensions of 1 or 2 not supported."); - } - else { - const int dim = 3; - - if (grad.Size() != (nqpts * dim * nnodes)) { - grad.SetSize(nqpts * dim * nnodes, mfem::Device::GetMemoryType()); - { - DenseMatrix DSh; - const int offset = nnodes * dim; - double *qpts_dshape_data = grad.HostReadWrite(); - for (int i = 0; i < nqpts; i++) { - const IntegrationPoint &ip = ir->IntPoint(i); - DSh.UseExternalData(&qpts_dshape_data[offset * i], nnodes, dim); - el.CalcDShape(ip, DSh); - } - } - grad.UseDevice(true); - } - - // geom->J really isn't going to work for us as of right now. We could just reorder it - // to the version that we want it to be in instead... - if (jacobian.Size() != (dim * dim * nqpts * nelems)) { - jacobian.SetSize(dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); - jacobian.UseDevice(true); - - geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS); - - const int DIM4 = 4; - std::array perm4 {{ 3, 2, 1, 0 } }; - - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > J(jacobian.ReadWrite(), layout_jacob); - - RAJA::Layout layout_geom = RAJA::make_permuted_layout({{ nqpts, dim, dim, nelems } }, perm4); - RAJA::View > geom_j_view(geom->J.Read(), layout_geom); - const int nqpts_ = nqpts; - const int dim_ = dim; - MFEM_FORALL(i, nelems, { - for (int j = 0; j < nqpts_; j++) { - for (int k = 0; k < dim_; k++) { - for (int l = 0; l < dim_; l++) { - J(l, k, j, i) = geom_j_view(j, l, k, i); - } - } - } - }); - } - - const int DIM2 = 2; - const int DIM3 = 3; - const int DIM4 = 4; - - std::array perm4 {{ 3, 2, 1, 0 } }; - std::array perm3 {{ 2, 1, 0 } }; - std::array perm2 {{ 1, 0 } }; - - // bunch of helper RAJA views to make dealing with data easier down below in our kernel. - - RAJA::Layout layout_tensor = RAJA::make_permuted_layout({{ 2 * dim, 2 * dim, nqpts, nelems } }, perm4); - RAJA::View > K(model->GetMatGrad()->Read(), layout_tensor); - - // Our field variables that are inputs and outputs - RAJA::Layout layout_field = RAJA::make_permuted_layout({{ nnodes * dim, nnodes * dim, nelems } }, perm3); - RAJA::View > E(emat.ReadWrite(), layout_field); - - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > J(jacobian.Read(), layout_jacob); - - RAJA::Layout layout_adj = RAJA::make_permuted_layout({{ dim, dim } }, perm2); - - RAJA::Layout layout_grads = RAJA::make_permuted_layout({{ nnodes, dim, nqpts } }, perm3); - RAJA::View > Gt(grad.Read(), layout_grads); - - double dt = model->GetModelDt(); - const int nqpts_ = nqpts; - const int dim_ = dim; - const int nnodes_ = nnodes; - // This loop we'll want to parallelize the rest are all serial for now. - MFEM_FORALL(i_elems, nelems, { - double adj[dim_ * dim_]; - double c_detJ; - // So, we're going to say this view is constant however we're going to mutate the values only in - // that one scoped section for the quadrature points. - RAJA::View > A(&adj[0], layout_adj); - for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { - // If we scope this then we only need to carry half the number of variables around with us for - // the adjugate term. - { - const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 - const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 - const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 - const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 - const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 - const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 - const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 - const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 - const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 - const double detJ = J11 * (J22 * J33 - J32 * J23) - - /* */ J21 * (J12 * J33 - J32 * J13) + - /* */ J31 * (J12 * J23 - J22 * J13); - c_detJ = 1.0 / detJ * W[j_qpts] * dt; - // adj(J) - adj[0] = (J22 * J33) - (J23 * J32); // 0,0 - adj[1] = (J32 * J13) - (J12 * J33); // 0,1 - adj[2] = (J12 * J23) - (J22 * J13); // 0,2 - adj[3] = (J31 * J23) - (J21 * J33); // 1,0 - adj[4] = (J11 * J33) - (J13 * J31); // 1,1 - adj[5] = (J21 * J13) - (J11 * J23); // 1,2 - adj[6] = (J21 * J32) - (J31 * J22); // 2,0 - adj[7] = (J31 * J12) - (J11 * J32); // 2,1 - adj[8] = (J11 * J22) - (J12 * J21); // 2,2 - } - for (int knds = 0; knds < nnodes_; knds++) { - const double bx = Gt(knds, 0, j_qpts) * A(0, 0) - + Gt(knds, 1, j_qpts) * A(0, 1) - + Gt(knds, 2, j_qpts) * A(0, 2); - - const double by = Gt(knds, 0, j_qpts) * A(1, 0) - + Gt(knds, 1, j_qpts) * A(1, 1) - + Gt(knds, 2, j_qpts) * A(1, 2); - - const double bz = Gt(knds, 0, j_qpts) * A(2, 0) - + Gt(knds, 1, j_qpts) * A(2, 1) - + Gt(knds, 2, j_qpts) * A(2, 2); - - - const double k11x = c_detJ * (bx * K(0, 0, j_qpts, i_elems) - + by * K(0, 5, j_qpts, i_elems) - + bz * K(0, 4, j_qpts, i_elems)); - const double k11y = c_detJ * (bx * K(5, 0, j_qpts, i_elems) - + by * K(5, 5, j_qpts, i_elems) - + bz * K(5, 4, j_qpts, i_elems)); - const double k11z = c_detJ * (bx * K(4, 0, j_qpts, i_elems) - + by * K(4, 5, j_qpts, i_elems) - + bz * K(4, 4, j_qpts, i_elems)); - - const double k12x = c_detJ * (bx * K(0, 5, j_qpts, i_elems) - + by * K(0, 1, j_qpts, i_elems) - + bz * K(0, 3, j_qpts, i_elems)); - const double k12y = c_detJ * (bx * K(5, 5, j_qpts, i_elems) - + by * K(5, 1, j_qpts, i_elems) - + bz * K(5, 3, j_qpts, i_elems)); - const double k12z = c_detJ * (bx * K(4, 5, j_qpts, i_elems) - + by * K(4, 1, j_qpts, i_elems) - + bz * K(4, 3, j_qpts, i_elems)); - - const double k13x = c_detJ * (bx * K(0, 4, j_qpts, i_elems) - + by * K(0, 3, j_qpts, i_elems) - + bz * K(0, 2, j_qpts, i_elems)); - const double k13y = c_detJ * (bx * K(5, 4, j_qpts, i_elems) - + by * K(5, 3, j_qpts, i_elems) - + bz * K(5, 2, j_qpts, i_elems)); - const double k13z = c_detJ * (bx * K(4, 4, j_qpts, i_elems) - + by * K(4, 3, j_qpts, i_elems) - + bz * K(4, 2, j_qpts, i_elems)); - - const double k21x = c_detJ * (bx * K(5, 0, j_qpts, i_elems) - + by * K(5, 5, j_qpts, i_elems) - + bz * K(5, 4, j_qpts, i_elems)); - const double k21y = c_detJ * (bx * K(1, 0, j_qpts, i_elems) - + by * K(1, 5, j_qpts, i_elems) - + bz * K(1, 4, j_qpts, i_elems)); - const double k21z = c_detJ * (bx * K(3, 0, j_qpts, i_elems) - + by * K(3, 5, j_qpts, i_elems) - + bz * K(3, 4, j_qpts, i_elems)); - - const double k22x = c_detJ * (bx * K(5, 5, j_qpts, i_elems) - + by * K(5, 1, j_qpts, i_elems) - + bz * K(5, 3, j_qpts, i_elems)); - const double k22y = c_detJ * (bx * K(1, 5, j_qpts, i_elems) - + by * K(1, 1, j_qpts, i_elems) - + bz * K(1, 3, j_qpts, i_elems)); - const double k22z = c_detJ * (bx * K(3, 5, j_qpts, i_elems) - + by * K(3, 1, j_qpts, i_elems) - + bz * K(3, 3, j_qpts, i_elems)); - - const double k23x = c_detJ * (bx * K(5, 4, j_qpts, i_elems) - + by * K(5, 3, j_qpts, i_elems) - + bz * K(5, 2, j_qpts, i_elems)); - const double k23y = c_detJ * (bx * K(1, 4, j_qpts, i_elems) - + by * K(1, 3, j_qpts, i_elems) - + bz * K(1, 2, j_qpts, i_elems)); - const double k23z = c_detJ * (bx * K(3, 4, j_qpts, i_elems) - + by * K(3, 3, j_qpts, i_elems) - + bz * K(3, 2, j_qpts, i_elems)); - - const double k31x = c_detJ * (bx * K(4, 0, j_qpts, i_elems) - + by * K(4, 5, j_qpts, i_elems) - + bz * K(4, 4, j_qpts, i_elems)); - const double k31y = c_detJ * (bx * K(3, 0, j_qpts, i_elems) - + by * K(3, 5, j_qpts, i_elems) - + bz * K(3, 4, j_qpts, i_elems)); - const double k31z = c_detJ * (bx * K(2, 0, j_qpts, i_elems) - + by * K(2, 5, j_qpts, i_elems) - + bz * K(2, 4, j_qpts, i_elems)); - - const double k32x = c_detJ * (bx * K(4, 5, j_qpts, i_elems) - + by * K(4, 1, j_qpts, i_elems) - + bz * K(4, 3, j_qpts, i_elems)); - const double k32y = c_detJ * (bx * K(3, 5, j_qpts, i_elems) - + by * K(3, 1, j_qpts, i_elems) - + bz * K(3, 3, j_qpts, i_elems)); - const double k32z = c_detJ * (bx * K(2, 5, j_qpts, i_elems) - + by * K(2, 1, j_qpts, i_elems) - + bz * K(2, 3, j_qpts, i_elems)); - - const double k33x = c_detJ * (bx * K(4, 4, j_qpts, i_elems) - + by * K(4, 3, j_qpts, i_elems) - + bz * K(4, 2, j_qpts, i_elems)); - const double k33y = c_detJ * (bx * K(3, 4, j_qpts, i_elems) - + by * K(3, 3, j_qpts, i_elems) - + bz * K(3, 2, j_qpts, i_elems)); - const double k33z = c_detJ * (bx * K(2, 4, j_qpts, i_elems) - + by * K(2, 3, j_qpts, i_elems) - + bz * K(2, 2, j_qpts, i_elems)); - - for (int lnds = 0; lnds < nnodes_; lnds++) { - const double gx = Gt(lnds, 0, j_qpts) * A(0, 0) - + Gt(lnds, 1, j_qpts) * A(0, 1) - + Gt(lnds, 2, j_qpts) * A(0, 2); - - const double gy = Gt(lnds, 0, j_qpts) * A(1, 0) - + Gt(lnds, 1, j_qpts) * A(1, 1) - + Gt(lnds, 2, j_qpts) * A(1, 2); - - const double gz = Gt(lnds, 0, j_qpts) * A(2, 0) - + Gt(lnds, 1, j_qpts) * A(2, 1) - + Gt(lnds, 2, j_qpts) * A(2, 2); - - - E(lnds, knds, i_elems) += gx * k11x + gy * k11y + gz * k11z; - E(lnds, knds + nnodes_, i_elems) += gx * k12x + gy * k12y + gz * k12z; - E(lnds, knds + 2 * nnodes_, i_elems) += gx * k13x + gy * k13y + gz * k13z; - - E(lnds + nnodes_, knds, i_elems) += gx * k21x + gy * k21y + gz * k21z; - E(lnds + nnodes_, knds + nnodes_, i_elems) += gx * k22x + gy * k22y + gz * k22z; - E(lnds + nnodes_, knds + 2 * nnodes_, i_elems) += gx * k23x + gy * k23y + gz * k23z; - - E(lnds + 2 * nnodes_, knds, i_elems) += gx * k31x + gy * k31y + gz * k31z; - E(lnds + 2 * nnodes_, knds + nnodes_, i_elems) += gx * k32x + gy * k32y + gz * k32z; - E(lnds + 2 * nnodes_, knds + 2 * nnodes_, i_elems) += gx * k33x + gy * k33y + gz * k33z; - } - } - } - }); - } -} - -// Outside of the UMAT function calls this should be the function called -// to assemble our residual vectors. -void ICExaNLFIntegrator::AssembleElementVector( - const FiniteElement &el, - ElementTransformation &Ttr, - const Vector &elfun, Vector &elvect) -{ - CALI_CXX_MARK_SCOPE("icenlfi_assembleElemVec"); - int dof = el.GetDof(), dim = el.GetDim(); - - DenseMatrix DSh, DS, eDS_loc; - DenseMatrix Jpt; - DenseMatrix PMatI, PMatO; - // This is our stress tensor - DenseMatrix P; - DenseMatrix grad_trans; - // temp1 is now going to become the transpose Bmatrix as seen in - // [B^t][tan_stiff][B] - grad_trans.SetSize(dof * dim, 6); - - DSh.SetSize(dof, dim); - DS.SetSize(dof, dim); - eDS_loc.SetSize(dof, dim); - eDS_loc = 0.0; - Jpt.SetSize(dim); - - // PMatI would be our velocity in this case - PMatI.UseExternalData(elfun.GetData(), dof, dim); - elvect.SetSize(dof * dim); - - // PMatO would be our residual vector - elvect = 0.0; - PMatO.UseExternalData(elvect.HostReadWrite(), dof * dim, 1); - - const IntegrationRule *ir = IntRule; - if (!ir) { - ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); // must match quadrature space - } - - const IntegrationRule *irc = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); - double eVol = 0.0; - - for (int i = 0; i < irc->GetNPoints(); i++) { - const IntegrationPoint &ip = irc->IntPoint(i); - Ttr.SetIntPoint(&ip); - - // compute Jacobian of the transformation - Jpt = Ttr.InverseJacobian(); // Jrt = dxi / dX - - el.CalcDShape(ip, DSh); - Mult(DSh, Jpt, DS); // dN_a(xi) / dX = dN_a(xi)/dxi * dxi/dX - DS *= (Ttr.Weight() * ip.weight); - eDS_loc += DS; - - eVol += (Ttr.Weight() * ip.weight); - - } - - eDS_loc *= (1.0 / eVol); - - double stress[6]; - - P.UseExternalData(&stress[0], 6, 1); - - for (int i = 0; i < ir->GetNPoints(); i++) { - const IntegrationPoint &ip = ir->IntPoint(i); - Ttr.SetIntPoint(&ip); - - // compute Jacobian of the transformation - Jpt = Ttr.InverseJacobian(); // Jrt = dxi / dX - - el.CalcDShape(ip, DSh); - Mult(DSh, Jpt, DS); // dN_a(xi) / dX = dN_a(xi)/dxi * dxi/dX - - model->GetElementStress(Ttr.ElementNo, i, false, &stress[0], 6); - model->GenerateGradBarMatrix(DS, eDS_loc, grad_trans); - grad_trans *= (ip.weight * Ttr.Weight()); - AddMult(grad_trans, P, PMatO); - - } - - return; -} - -void ICExaNLFIntegrator::AssembleElementGrad( - const FiniteElement &el, - ElementTransformation &Ttr, - const Vector & /*elfun*/, DenseMatrix &elmat) -{ - CALI_CXX_MARK_SCOPE("icenlfi_assembleElemGrad"); - int dof = el.GetDof(), dim = el.GetDim(); - - DenseMatrix DSh, DS, eDS_loc, Jrt; - - // Now time to start assembling stuff - DenseMatrix grad_trans, temp; - DenseMatrix tan_stiff; - - constexpr int ngrad_dim2 = 36; - double matGrad[ngrad_dim2]; - // Delta in our timestep - double dt = model->GetModelDt(); - - // temp1 is now going to become the transpose Bmatrix as seen in - // [B^t][tan_stiff][B] - grad_trans.SetSize(dof * dim, 6); - // We need a temp matrix to store our first matrix results as seen in here - temp.SetSize(6, dof * dim); - - tan_stiff.UseExternalData(&matGrad[0], 6, 6); - - DSh.SetSize(dof, dim); - DS.SetSize(dof, dim); - eDS_loc.SetSize(dof, dim); - eDS_loc = 0.0; - Jrt.SetSize(dim); - elmat.SetSize(dof * dim); - - const IntegrationRule *ir = IntRule; - if (!ir) { - ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); // <--- must match quadrature space - } - - elmat = 0.0; - - const IntegrationRule *irc = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); - double eVol = 0.0; - - for (int i = 0; i < irc->GetNPoints(); i++) { - const IntegrationPoint &ip = irc->IntPoint(i); - Ttr.SetIntPoint(&ip); - - // compute Jacobian of the transformation - Jrt = Ttr.InverseJacobian(); // Jrt = dxi / dX - - el.CalcDShape(ip, DSh); - Mult(DSh, Jrt, DS); // dN_a(xi) / dX = dN_a(xi)/dxi * dxi/dX - DS *= (Ttr.Weight() * ip.weight); - eDS_loc += DS; - - eVol += (Ttr.Weight() * ip.weight); - - } - - eDS_loc *= (1.0 / eVol); - - for (int i = 0; i < ir->GetNPoints(); i++) { - const IntegrationPoint &ip = ir->IntPoint(i); - Ttr.SetIntPoint(&ip); - CalcInverse(Ttr.Jacobian(), Jrt); - - el.CalcDShape(ip, DSh); - Mult(DSh, Jrt, DS); - - model->GetElementMatGrad(Ttr.ElementNo, i, matGrad, ngrad_dim2); - // temp1 is B^t - model->GenerateGradBarMatrix(DS, eDS_loc, grad_trans); - // We multiple our quadrature wts here to our tan_stiff matrix - tan_stiff *= dt * ip.weight * Ttr.Weight(); - // We use kgeom as a temporary matrix - // kgeom = [Cstiff][B] - MultABt(tan_stiff, grad_trans, temp); - // We now add our [B^t][kgeom] product to our tangent stiffness matrix that - // we want to output to our material tangent stiffness matrix - AddMult(grad_trans, temp, elmat); - } - - return; -} - -/// Method defining element assembly. -/** The result of the element assembly is added and stored in the @a emat - Vector. */ -void ICExaNLFIntegrator::AssembleGradEA(const Vector& /*x*/,const FiniteElementSpace &fes, Vector &emat) { - AssembleEA(fes, emat); -} -void ICExaNLFIntegrator::AssembleEA(const mfem::FiniteElementSpace &fes, mfem::Vector &emat) -{ - CALI_CXX_MARK_SCOPE("icenlfi_assembleEA"); - const FiniteElement &el = *fes.GetFE(0); - space_dims = el.GetDim(); - const IntegrationRule *ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); - - nqpts = ir->GetNPoints(); - nnodes = el.GetDof(); - nelems = fes.GetNE(); - auto W = ir->GetWeights().Read(); - - if ((space_dims == 1) || (space_dims == 2)) { - MFEM_ABORT("Dimensions of 1 or 2 not supported."); - } - - else { - const int dim = 3; - - const int DIM2 = 2; - const int DIM3 = 3; - const int DIM4 = 4; - - std::array perm4 {{ 3, 2, 1, 0 } }; - std::array perm3 {{ 2, 1, 0 } }; - std::array perm2 {{ 1, 0 } }; - - // bunch of helper RAJA views to make dealing with data easier down below in our kernel. - - // Our field variables that are inputs and outputs - - RAJA::Layout layout_egrads = RAJA::make_permuted_layout({{ nnodes, dim, nelems } }, perm3); - RAJA::View > eDS_view(eDS.Read(), layout_egrads); - - RAJA::Layout layout_tensor = RAJA::make_permuted_layout({{ 2 * dim, 2 * dim, nqpts, nelems } }, perm4); - RAJA::View > K(model->GetMatGrad()->Read(), layout_tensor); - - // Our field variables that are inputs and outputs - RAJA::Layout layout_field = RAJA::make_permuted_layout({{ nnodes * dim, nnodes * dim, nelems } }, perm3); - RAJA::View > E(emat.ReadWrite(), layout_field); - - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > J(jacobian.Read(), layout_jacob); - - RAJA::Layout layout_adj = RAJA::make_permuted_layout({{ dim, dim } }, perm2); - - RAJA::Layout layout_grads = RAJA::make_permuted_layout({{ nnodes, dim, nqpts } }, perm3); - RAJA::View > Gt(grad.Read(), layout_grads); - - double dt = model->GetModelDt(); - const double i3 = 1.0 / 3.0; - const int nqpts_ = nqpts; - const int dim_ = dim; - const int nnodes_ = nnodes; - // This loop we'll want to parallelize the rest are all serial for now. - MFEM_FORALL(i_elems, nelems, { - double adj[dim_ * dim_]; - double c_detJ; - double idetJ; - // So, we're going to say this view is constant however we're going to mutate the values only in - // that one scoped section for the quadrature points. - RAJA::View > A(&adj[0], layout_adj); - for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { - // If we scope this then we only need to carry half the number of variables around with us for - // the adjugate term. - { - const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 - const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 - const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 - const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 - const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 - const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 - const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 - const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 - const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 - const double detJ = J11 * (J22 * J33 - J32 * J23) - - /* */ J21 * (J12 * J33 - J32 * J13) + - /* */ J31 * (J12 * J23 - J22 * J13); - idetJ = 1.0 / detJ; - c_detJ = detJ * W[j_qpts] * dt; - // adj(J) - adj[0] = (J22 * J33) - (J23 * J32); // 0,0 - adj[1] = (J32 * J13) - (J12 * J33); // 0,1 - adj[2] = (J12 * J23) - (J22 * J13); // 0,2 - adj[3] = (J31 * J23) - (J21 * J33); // 1,0 - adj[4] = (J11 * J33) - (J13 * J31); // 1,1 - adj[5] = (J21 * J13) - (J11 * J23); // 1,2 - adj[6] = (J21 * J32) - (J31 * J22); // 2,0 - adj[7] = (J31 * J12) - (J11 * J32); // 2,1 - adj[8] = (J11 * J22) - (J12 * J21); // 2,2 - } - for (int knds = 0; knds < nnodes_; knds++) { - const double bx = idetJ * (Gt(knds, 0, j_qpts) * A(0, 0) - + Gt(knds, 1, j_qpts) * A(0, 1) - + Gt(knds, 2, j_qpts) * A(0, 2)); - - const double by = idetJ * (Gt(knds, 0, j_qpts) * A(1, 0) - + Gt(knds, 1, j_qpts) * A(1, 1) - + Gt(knds, 2, j_qpts) * A(1, 2)); - - const double bz = idetJ * (Gt(knds, 0, j_qpts) * A(2, 0) - + Gt(knds, 1, j_qpts) * A(2, 1) - + Gt(knds, 2, j_qpts) * A(2, 2)); - const double b4 = i3 * (eDS_view(knds, 0, i_elems) - bx); - const double b5 = b4 + bx; - const double b6 = i3 * (eDS_view(knds, 1, i_elems) - by); - const double b7 = b6 + by; - const double b8 = i3 * (eDS_view(knds, 2, i_elems) - bz); - const double b9 = b8 + bz; - - - const double k11w = c_detJ * (b4 * K(1, 1, j_qpts, i_elems) - + b4 * K(1, 2, j_qpts, i_elems) - + b5 * K(1, 0, j_qpts, i_elems) - + by * K(1, 5, j_qpts, i_elems) - + bz * K(1, 4, j_qpts, i_elems) - + b4 * K(2, 1, j_qpts, i_elems) - + b4 * K(2, 2, j_qpts, i_elems) - + b5 * K(2, 0, j_qpts, i_elems) - + by * K(2, 5, j_qpts, i_elems) - + bz * K(2, 4, j_qpts, i_elems)); - - const double k11x = c_detJ * (b4 * K(0, 1, j_qpts, i_elems) - + b4 * K(0, 2, j_qpts, i_elems) - + b5 * K(0, 0, j_qpts, i_elems) - + by * K(0, 5, j_qpts, i_elems) - + bz * K(0, 4, j_qpts, i_elems)); - - const double k11y = c_detJ * (b4 * K(5, 1, j_qpts, i_elems) - + b4 * K(5, 2, j_qpts, i_elems) - + b5 * K(5, 0, j_qpts, i_elems) - + by * K(5, 5, j_qpts, i_elems) - + bz * K(5, 4, j_qpts, i_elems)); - - const double k11z = c_detJ * (b4 * K(4, 1, j_qpts, i_elems) - + b4 * K(4, 2, j_qpts, i_elems) - + b5 * K(4, 0, j_qpts, i_elems) - + by * K(4, 5, j_qpts, i_elems) - + bz * K(4, 4, j_qpts, i_elems)); - - const double k12w = c_detJ * (b6 * K(1, 0, j_qpts, i_elems) - + b6 * K(1, 2, j_qpts, i_elems) - + b7 * K(1, 1, j_qpts, i_elems) - + bx * K(1, 5, j_qpts, i_elems) - + bz * K(1, 3, j_qpts, i_elems) - + b6 * K(2, 0, j_qpts, i_elems) - + b6 * K(2, 2, j_qpts, i_elems) - + b7 * K(2, 1, j_qpts, i_elems) - + bx * K(2, 5, j_qpts, i_elems) - + bz * K(2, 3, j_qpts, i_elems)); - - const double k12x = c_detJ * (b6 * K(0, 0, j_qpts, i_elems) - + b6 * K(0, 2, j_qpts, i_elems) - + b7 * K(0, 1, j_qpts, i_elems) - + bx * K(0, 5, j_qpts, i_elems) - + bz * K(0, 3, j_qpts, i_elems)); - - const double k12y = c_detJ * (b6 * K(5, 0, j_qpts, i_elems) - + b6 * K(5, 2, j_qpts, i_elems) - + b7 * K(5, 1, j_qpts, i_elems) - + bx * K(5, 5, j_qpts, i_elems) - + bz * K(5, 3, j_qpts, i_elems)); - - const double k12z = c_detJ * (b6 * K(4, 0, j_qpts, i_elems) - + b6 * K(4, 2, j_qpts, i_elems) - + b7 * K(4, 1, j_qpts, i_elems) - + bx * K(4, 5, j_qpts, i_elems) - + bz * K(4, 3, j_qpts, i_elems)); - - const double k13w = c_detJ * (b8 * K(1, 0, j_qpts, i_elems) - + b8 * K(1, 1, j_qpts, i_elems) - + b9 * K(1, 2, j_qpts, i_elems) - + bx * K(1, 4, j_qpts, i_elems) - + by * K(1, 3, j_qpts, i_elems) - + b8 * K(2, 0, j_qpts, i_elems) - + b8 * K(2, 1, j_qpts, i_elems) - + b9 * K(2, 2, j_qpts, i_elems) - + bx * K(2, 4, j_qpts, i_elems) - + by * K(2, 3, j_qpts, i_elems)); - - const double k13x = c_detJ * (b8 * K(0, 0, j_qpts, i_elems) - + b8 * K(0, 1, j_qpts, i_elems) - + b9 * K(0, 2, j_qpts, i_elems) - + bx * K(0, 4, j_qpts, i_elems) - + by * K(0, 3, j_qpts, i_elems)); - - const double k13y = c_detJ * (b8 * K(5, 0, j_qpts, i_elems) - + b8 * K(5, 1, j_qpts, i_elems) - + b9 * K(5, 2, j_qpts, i_elems) - + bx * K(5, 4, j_qpts, i_elems) - + by * K(5, 3, j_qpts, i_elems)); - - const double k13z = c_detJ * (b8 * K(4, 0, j_qpts, i_elems) - + b8 * K(4, 1, j_qpts, i_elems) - + b9 * K(4, 2, j_qpts, i_elems) - + bx * K(4, 4, j_qpts, i_elems) - + by * K(4, 3, j_qpts, i_elems)); - - const double k21w = c_detJ * (b4 * K(0, 1, j_qpts, i_elems) - + b4 * K(0, 2, j_qpts, i_elems) - + b5 * K(0, 0, j_qpts, i_elems) - + by * K(0, 5, j_qpts, i_elems) - + bz * K(0, 4, j_qpts, i_elems) - + b4 * K(2, 1, j_qpts, i_elems) - + b4 * K(2, 2, j_qpts, i_elems) - + b5 * K(2, 0, j_qpts, i_elems) - + by * K(2, 5, j_qpts, i_elems) - + bz * K(2, 4, j_qpts, i_elems)); - - const double k21x = c_detJ * (b4 * K(1, 1, j_qpts, i_elems) - + b4 * K(1, 2, j_qpts, i_elems) - + b5 * K(1, 0, j_qpts, i_elems) - + by * K(1, 5, j_qpts, i_elems) - + bz * K(1, 4, j_qpts, i_elems)); - - const double k21y = c_detJ * (b4 * K(5, 1, j_qpts, i_elems) - + b4 * K(5, 2, j_qpts, i_elems) - + b5 * K(5, 0, j_qpts, i_elems) - + by * K(5, 5, j_qpts, i_elems) - + bz * K(5, 4, j_qpts, i_elems)); - - const double k21z = c_detJ * (b4 * K(3, 1, j_qpts, i_elems) - + b4 * K(3, 2, j_qpts, i_elems) - + b5 * K(3, 0, j_qpts, i_elems) - + by * K(3, 5, j_qpts, i_elems) - + bz * K(3, 4, j_qpts, i_elems)); - - const double k22w = c_detJ * (b6 * K(0, 0, j_qpts, i_elems) - + b6 * K(0, 2, j_qpts, i_elems) - + b7 * K(0, 1, j_qpts, i_elems) - + bx * K(0, 5, j_qpts, i_elems) - + bz * K(0, 3, j_qpts, i_elems) - + b6 * K(2, 0, j_qpts, i_elems) - + b6 * K(2, 2, j_qpts, i_elems) - + b7 * K(2, 1, j_qpts, i_elems) - + bx * K(2, 5, j_qpts, i_elems) - + bz * K(2, 3, j_qpts, i_elems)); - - const double k22x = c_detJ * (b6 * K(1, 0, j_qpts, i_elems) - + b6 * K(1, 2, j_qpts, i_elems) - + b7 * K(1, 1, j_qpts, i_elems) - + bx * K(1, 5, j_qpts, i_elems) - + bz * K(1, 3, j_qpts, i_elems)); - - const double k22y = c_detJ * (b6 * K(5, 0, j_qpts, i_elems) - + b6 * K(5, 2, j_qpts, i_elems) - + b7 * K(5, 1, j_qpts, i_elems) - + bx * K(5, 5, j_qpts, i_elems) - + bz * K(5, 3, j_qpts, i_elems)); - - const double k22z = c_detJ * (b6 * K(3, 0, j_qpts, i_elems) - + b6 * K(3, 2, j_qpts, i_elems) - + b7 * K(3, 1, j_qpts, i_elems) - + bx * K(3, 5, j_qpts, i_elems) - + bz * K(3, 3, j_qpts, i_elems)); - - const double k23w = c_detJ * (b8 * K(0, 0, j_qpts, i_elems) - + b8 * K(0, 1, j_qpts, i_elems) - + b9 * K(0, 2, j_qpts, i_elems) - + bx * K(0, 4, j_qpts, i_elems) - + by * K(0, 3, j_qpts, i_elems) - + b8 * K(2, 0, j_qpts, i_elems) - + b8 * K(2, 1, j_qpts, i_elems) - + b9 * K(2, 2, j_qpts, i_elems) - + bx * K(2, 4, j_qpts, i_elems) - + by * K(2, 3, j_qpts, i_elems)); - - const double k23x = c_detJ * (b8 * K(1, 0, j_qpts, i_elems) - + b8 * K(1, 1, j_qpts, i_elems) - + b9 * K(1, 2, j_qpts, i_elems) - + bx * K(1, 4, j_qpts, i_elems) - + by * K(1, 3, j_qpts, i_elems)); - - const double k23y = c_detJ * (b8 * K(5, 0, j_qpts, i_elems) - + b8 * K(5, 1, j_qpts, i_elems) - + b9 * K(5, 2, j_qpts, i_elems) - + bx * K(5, 4, j_qpts, i_elems) - + by * K(5, 3, j_qpts, i_elems)); - - const double k23z = c_detJ * (b8 * K(3, 0, j_qpts, i_elems) - + b8 * K(3, 1, j_qpts, i_elems) - + b9 * K(3, 2, j_qpts, i_elems) - + bx * K(3, 4, j_qpts, i_elems) - + by * K(3, 3, j_qpts, i_elems)); - - const double k31w = c_detJ * (b4 * K(0, 1, j_qpts, i_elems) - + b4 * K(0, 2, j_qpts, i_elems) - + b5 * K(0, 0, j_qpts, i_elems) - + by * K(0, 5, j_qpts, i_elems) - + bz * K(0, 4, j_qpts, i_elems) - + b4 * K(1, 1, j_qpts, i_elems) - + b4 * K(1, 2, j_qpts, i_elems) - + b5 * K(1, 0, j_qpts, i_elems) - + by * K(1, 5, j_qpts, i_elems) - + bz * K(1, 4, j_qpts, i_elems)); - - const double k31x = c_detJ * (b4 * K(2, 1, j_qpts, i_elems) - + b4 * K(2, 2, j_qpts, i_elems) - + b5 * K(2, 0, j_qpts, i_elems) - + by * K(2, 5, j_qpts, i_elems) - + bz * K(2, 4, j_qpts, i_elems)); - - const double k31y = c_detJ * (b4 * K(4, 1, j_qpts, i_elems) - + b4 * K(4, 2, j_qpts, i_elems) - + b5 * K(4, 0, j_qpts, i_elems) - + by * K(4, 5, j_qpts, i_elems) - + bz * K(4, 4, j_qpts, i_elems)); - - const double k31z = c_detJ * (b4 * K(3, 1, j_qpts, i_elems) - + b4 * K(3, 2, j_qpts, i_elems) - + b5 * K(3, 0, j_qpts, i_elems) - + by * K(3, 5, j_qpts, i_elems) - + bz * K(3, 4, j_qpts, i_elems)); - - const double k32w = c_detJ * (b6 * K(0, 0, j_qpts, i_elems) - + b6 * K(0, 2, j_qpts, i_elems) - + b7 * K(0, 1, j_qpts, i_elems) - + bx * K(0, 5, j_qpts, i_elems) - + bz * K(0, 3, j_qpts, i_elems) - + b6 * K(1, 0, j_qpts, i_elems) - + b6 * K(1, 2, j_qpts, i_elems) - + b7 * K(1, 1, j_qpts, i_elems) - + bx * K(1, 5, j_qpts, i_elems) - + bz * K(1, 3, j_qpts, i_elems)); - - const double k32x = c_detJ * (b6 * K(2, 0, j_qpts, i_elems) - + b6 * K(2, 2, j_qpts, i_elems) - + b7 * K(2, 1, j_qpts, i_elems) - + bx * K(2, 5, j_qpts, i_elems) - + bz * K(2, 3, j_qpts, i_elems)); - - const double k32y = c_detJ * (b6 * K(4, 0, j_qpts, i_elems) - + b6 * K(4, 2, j_qpts, i_elems) - + b7 * K(4, 1, j_qpts, i_elems) - + bx * K(4, 5, j_qpts, i_elems) - + bz * K(4, 3, j_qpts, i_elems)); - - const double k32z = c_detJ * (b6 * K(3, 0, j_qpts, i_elems) - + b6 * K(3, 2, j_qpts, i_elems) - + b7 * K(3, 1, j_qpts, i_elems) - + bx * K(3, 5, j_qpts, i_elems) - + bz * K(3, 3, j_qpts, i_elems)); - - const double k33w = c_detJ * (b8 * K(0, 0, j_qpts, i_elems) - + b8 * K(0, 1, j_qpts, i_elems) - + b9 * K(0, 2, j_qpts, i_elems) - + bx * K(0, 4, j_qpts, i_elems) - + by * K(0, 3, j_qpts, i_elems) - + b8 * K(1, 0, j_qpts, i_elems) - + b8 * K(1, 1, j_qpts, i_elems) - + b9 * K(1, 2, j_qpts, i_elems) - + bx * K(1, 4, j_qpts, i_elems) - + by * K(1, 3, j_qpts, i_elems)); - - const double k33x = c_detJ * (b8 * K(2, 0, j_qpts, i_elems) - + b8 * K(2, 1, j_qpts, i_elems) - + b9 * K(2, 2, j_qpts, i_elems) - + bx * K(2, 4, j_qpts, i_elems) - + by * K(2, 3, j_qpts, i_elems)); - - const double k33y = c_detJ * (b8 * K(4, 0, j_qpts, i_elems) - + b8 * K(4, 1, j_qpts, i_elems) - + b9 * K(4, 2, j_qpts, i_elems) - + bx * K(4, 4, j_qpts, i_elems) - + by * K(4, 3, j_qpts, i_elems)); - - const double k33z = c_detJ * (b8 * K(3, 0, j_qpts, i_elems) - + b8 * K(3, 1, j_qpts, i_elems) - + b9 * K(3, 2, j_qpts, i_elems) - + bx * K(3, 4, j_qpts, i_elems) - + by * K(3, 3, j_qpts, i_elems)); - - for (int lnds = 0; lnds < nnodes_; lnds++) { - const double gx = idetJ * (Gt(lnds, 0, j_qpts) * A(0, 0) - + Gt(lnds, 1, j_qpts) * A(0, 1) - + Gt(lnds, 2, j_qpts) * A(0, 2)); - - const double gy = idetJ * (Gt(lnds, 0, j_qpts) * A(1, 0) - + Gt(lnds, 1, j_qpts) * A(1, 1) - + Gt(lnds, 2, j_qpts) * A(1, 2)); - - const double gz = idetJ * (Gt(lnds, 0, j_qpts) * A(2, 0) - + Gt(lnds, 1, j_qpts) * A(2, 1) - + Gt(lnds, 2, j_qpts) * A(2, 2)); - - const double g4 = i3 * (eDS_view(lnds, 0, i_elems) - gx); - const double g5 = g4 + gx; - const double g6 = i3 * (eDS_view(lnds, 1, i_elems) - gy); - const double g7 = g6 + gy; - const double g8 = i3 * (eDS_view(lnds, 2, i_elems) - gz); - const double g9 = g8 + gz; - - E(lnds, knds, i_elems) += g4 * k11w + g5 * k11x + gy * k11y + gz * k11z; - E(lnds, knds + nnodes_, i_elems) += g4 * k12w + g5 * k12x + gy * k12y + gz * k12z; - E(lnds, knds + 2 * nnodes_, i_elems) += g4 * k13w + g5 * k13x + gy * k13y + gz * k13z; - - E(lnds + nnodes_, knds, i_elems) += g6 * k21w + g7 * k21x + gx * k21y + gz * k21z; - E(lnds + nnodes_, knds + nnodes_, i_elems) += g6 * k22w + g7 * k22x + gx * k22y + gz * k22z; - E(lnds + nnodes_, knds + 2 * nnodes_, i_elems) += g6 * k23w + g7 * k23x + gx * k23y + gz * k23z; - - E(lnds + 2 * nnodes_, knds, i_elems) += g8 * k31w + g9 * k31x + gx * k31y + gy * k31z; - E(lnds + 2 * nnodes_, knds + nnodes_, i_elems) += g8 * k32w + g9 * k32x + gx * k32y + gy * k32z; - E(lnds + 2 * nnodes_, knds + 2 * nnodes_, i_elems) += g8 * k33w + g9 * k33x + gx * k33y + gy * k33z; - } - } - } - }); - } - -} - -// This assembles the diagonal of our LHS which can be used as a preconditioner -void ICExaNLFIntegrator::AssembleGradDiagonalPA(Vector &diag) const -{ - CALI_CXX_MARK_SCOPE("icenlfi_AssembleGradDiagonalPA"); - const IntegrationRule &ir = model->GetMatGrad()->GetSpace()->GetIntRule(0); - auto W = ir.GetWeights().Read(); - - if ((space_dims == 1) || (space_dims == 2)) { - MFEM_ABORT("Dimensions of 1 or 2 not supported."); - } - else { - const int dim = 3; - - const int DIM2 = 2; - const int DIM3 = 3; - const int DIM4 = 4; - - // bunch of helper RAJA views to make dealing with data easier down below in our kernel. - - std::array perm4 {{ 3, 2, 1, 0 } }; - std::array perm3 {{ 2, 1, 0 } }; - std::array perm2 {{ 1, 0 } }; - - // bunch of helper RAJA views to make dealing with data easier down below in our kernel. - - RAJA::Layout layout_tensor = RAJA::make_permuted_layout({{ 2 * dim, 2 * dim, nqpts, nelems } }, perm4); - RAJA::View > K(model->GetMatGrad()->Read(), layout_tensor); - - // Our field variables that are inputs and outputs - RAJA::Layout layout_field = RAJA::make_permuted_layout({{ nnodes, dim, nelems } }, perm3); - RAJA::View > Y(diag.ReadWrite(), layout_field); - - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > J(jacobian.Read(), layout_jacob); - - RAJA::Layout layout_adj = RAJA::make_permuted_layout({{ dim, dim } }, perm2); - - RAJA::Layout layout_grads = RAJA::make_permuted_layout({{ nnodes, dim, nqpts } }, perm3); - RAJA::View > Gt(grad.Read(), layout_grads); - - RAJA::Layout layout_egrads = RAJA::make_permuted_layout({{ nnodes, dim, nelems } }, perm3); - RAJA::View > eDS_view(eDS.Read(), layout_egrads); - - double dt = model->GetModelDt(); - const double i3 = 1.0 / 3.0; - const int nqpts_ = nqpts; - const int dim_ = dim; - const int nnodes_ = nnodes; - // This loop we'll want to parallelize the rest are all serial for now. - MFEM_FORALL(i_elems, nelems, { - double adj[dim_ * dim_]; - double c_detJ; - double idetJ; - // So, we're going to say this view is constant however we're going to mutate the values only in - // that one scoped section for the quadrature points. - RAJA::View > A(&adj[0], layout_adj); - for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { - // If we scope this then we only need to carry half the number of variables around with us for - // the adjugate term. - { - const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 - const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 - const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 - const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 - const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 - const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 - const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 - const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 - const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 - const double detJ = J11 * (J22 * J33 - J32 * J23) - - /* */ J21 * (J12 * J33 - J32 * J13) + - /* */ J31 * (J12 * J23 - J22 * J13); - idetJ = 1.0 / detJ; - c_detJ = detJ * W[j_qpts] * dt; - // adj(J) - adj[0] = (J22 * J33) - (J23 * J32); // 0,0 - adj[1] = (J32 * J13) - (J12 * J33); // 0,1 - adj[2] = (J12 * J23) - (J22 * J13); // 0,2 - adj[3] = (J31 * J23) - (J21 * J33); // 1,0 - adj[4] = (J11 * J33) - (J13 * J31); // 1,1 - adj[5] = (J21 * J13) - (J11 * J23); // 1,2 - adj[6] = (J21 * J32) - (J31 * J22); // 2,0 - adj[7] = (J31 * J12) - (J11 * J32); // 2,1 - adj[8] = (J11 * J22) - (J12 * J21); // 2,2 - } - for (int knds = 0; knds < nnodes_; knds++) { - const double bx = idetJ * (Gt(knds, 0, j_qpts) * A(0, 0) - + Gt(knds, 1, j_qpts) * A(0, 1) - + Gt(knds, 2, j_qpts) * A(0, 2)); - - const double by = idetJ * (Gt(knds, 0, j_qpts) * A(1, 0) - + Gt(knds, 1, j_qpts) * A(1, 1) - + Gt(knds, 2, j_qpts) * A(1, 2)); - - const double bz = idetJ * (Gt(knds, 0, j_qpts) * A(2, 0) - + Gt(knds, 1, j_qpts) * A(2, 1) - + Gt(knds, 2, j_qpts) * A(2, 2)); - const double b4 = i3 * (eDS_view(knds, 0, i_elems) - bx); - const double b5 = b4 + bx; - const double b6 = i3 * (eDS_view(knds, 1, i_elems) - by); - const double b7 = b6 + by; - const double b8 = i3 * (eDS_view(knds, 2, i_elems) - bz); - const double b9 = b8 + bz; - - const double k11w = c_detJ * (b4 * K(1, 1, j_qpts, i_elems) - + b4 * K(1, 2, j_qpts, i_elems) - + b5 * K(1, 0, j_qpts, i_elems) - + by * K(1, 5, j_qpts, i_elems) - + bz * K(1, 4, j_qpts, i_elems) - + b4 * K(2, 1, j_qpts, i_elems) - + b4 * K(2, 2, j_qpts, i_elems) - + b5 * K(2, 0, j_qpts, i_elems) - + by * K(2, 5, j_qpts, i_elems) - + bz * K(2, 4, j_qpts, i_elems)); - - const double k11x = c_detJ * (b4 * K(0, 1, j_qpts, i_elems) - + b4 * K(0, 2, j_qpts, i_elems) - + b5 * K(0, 0, j_qpts, i_elems) - + by * K(0, 5, j_qpts, i_elems) - + bz * K(0, 4, j_qpts, i_elems)); - - const double k11y = c_detJ * (b4 * K(5, 1, j_qpts, i_elems) - + b4 * K(5, 2, j_qpts, i_elems) - + b5 * K(5, 0, j_qpts, i_elems) - + by * K(5, 5, j_qpts, i_elems) - + bz * K(5, 4, j_qpts, i_elems)); - - const double k11z = c_detJ * (b4 * K(4, 1, j_qpts, i_elems) - + b4 * K(4, 2, j_qpts, i_elems) - + b5 * K(4, 0, j_qpts, i_elems) - + by * K(4, 5, j_qpts, i_elems) - + bz * K(4, 4, j_qpts, i_elems)); - - const double k22w = c_detJ * (b6 * K(0, 0, j_qpts, i_elems) - + b6 * K(0, 2, j_qpts, i_elems) - + b7 * K(0, 1, j_qpts, i_elems) - + bx * K(0, 5, j_qpts, i_elems) - + bz * K(0, 3, j_qpts, i_elems) - + b6 * K(2, 0, j_qpts, i_elems) - + b6 * K(2, 2, j_qpts, i_elems) - + b7 * K(2, 1, j_qpts, i_elems) - + bx * K(2, 5, j_qpts, i_elems) - + bz * K(2, 3, j_qpts, i_elems)); - - const double k22x = c_detJ * (b6 * K(1, 0, j_qpts, i_elems) - + b6 * K(1, 2, j_qpts, i_elems) - + b7 * K(1, 1, j_qpts, i_elems) - + bx * K(1, 5, j_qpts, i_elems) - + bz * K(1, 3, j_qpts, i_elems)); - - const double k22y = c_detJ * (b6 * K(5, 0, j_qpts, i_elems) - + b6 * K(5, 2, j_qpts, i_elems) - + b7 * K(5, 1, j_qpts, i_elems) - + bx * K(5, 5, j_qpts, i_elems) - + bz * K(5, 3, j_qpts, i_elems)); - - const double k22z = c_detJ * (b6 * K(3, 0, j_qpts, i_elems) - + b6 * K(3, 2, j_qpts, i_elems) - + b7 * K(3, 1, j_qpts, i_elems) - + bx * K(3, 5, j_qpts, i_elems) - + bz * K(3, 3, j_qpts, i_elems)); - - const double k33w = c_detJ * (b8 * K(0, 0, j_qpts, i_elems) - + b8 * K(0, 1, j_qpts, i_elems) - + b9 * K(0, 2, j_qpts, i_elems) - + bx * K(0, 4, j_qpts, i_elems) - + by * K(0, 3, j_qpts, i_elems) - + b8 * K(1, 0, j_qpts, i_elems) - + b8 * K(1, 1, j_qpts, i_elems) - + b9 * K(1, 2, j_qpts, i_elems) - + bx * K(1, 4, j_qpts, i_elems) - + by * K(1, 3, j_qpts, i_elems)); - - const double k33x = c_detJ * (b8 * K(2, 0, j_qpts, i_elems) - + b8 * K(2, 1, j_qpts, i_elems) - + b9 * K(2, 2, j_qpts, i_elems) - + bx * K(2, 4, j_qpts, i_elems) - + by * K(2, 3, j_qpts, i_elems)); - - const double k33y = c_detJ * (b8 * K(4, 0, j_qpts, i_elems) - + b8 * K(4, 1, j_qpts, i_elems) - + b9 * K(4, 2, j_qpts, i_elems) - + bx * K(4, 4, j_qpts, i_elems) - + by * K(4, 3, j_qpts, i_elems)); - - const double k33z = c_detJ * (b8 * K(3, 0, j_qpts, i_elems) - + b8 * K(3, 1, j_qpts, i_elems) - + b9 * K(3, 2, j_qpts, i_elems) - + bx * K(3, 4, j_qpts, i_elems) - + by * K(3, 3, j_qpts, i_elems)); - - Y(knds, 0, i_elems) += b4 * k11w + b5 * k11x + by * k11y + bz * k11z; - Y(knds, 1, i_elems) += b6 * k22w + b7 * k22x + bx * k22y + bz * k22z; - Y(knds, 2, i_elems) += b8 * k33w + b9 * k33x + bx * k33y + by * k33z; - - } - } - }); - } -} - -// This performs the assembly step of our RHS side of our system: -// f_ik = -void ICExaNLFIntegrator::AssemblePA(const FiniteElementSpace &fes) -{ - CALI_CXX_MARK_SCOPE("icenlfi_assemblePA"); - Mesh *mesh = fes.GetMesh(); - const FiniteElement &el = *fes.GetFE(0); - space_dims = el.GetDim(); - const IntegrationRule *ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); - - nqpts = ir->GetNPoints(); - nnodes = el.GetDof(); - nelems = fes.GetNE(); - - auto W = ir->GetWeights().Read(); - geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS); - - if ((space_dims == 1) || (space_dims == 2)) { - MFEM_ABORT("Dimensions of 1 or 2 not supported."); - } - else { - const int dim = 3; - - if (grad.Size() != (nqpts * dim * nnodes)) { - grad.SetSize(nqpts * dim * nnodes, mfem::Device::GetMemoryType()); - { - DenseMatrix DSh; - const int offset = nnodes * dim; - double *qpts_dshape_data = grad.HostReadWrite(); - for (int i = 0; i < nqpts; i++) { - const IntegrationPoint &ip = ir->IntPoint(i); - DSh.UseExternalData(&qpts_dshape_data[offset * i], nnodes, dim); - el.CalcDShape(ip, DSh); - } - } - grad.UseDevice(true); - } - - if (eDS.Size() != (nnodes * dim * nelems)) { - eDS.SetSize(nnodes * space_dims * nelems, mfem::Device::GetMemoryType()); - eDS.UseDevice(); - } - - eDS = 0.0; - - // geom->J really isn't going to work for us as of right now. We could just reorder it - // to the version that we want it to be in instead... - if (jacobian.Size() != (dim * dim * nqpts * nelems)) { - jacobian.SetSize(dim * dim * nqpts * nelems, mfem::Device::GetMemoryType()); - jacobian.UseDevice(true); - } - - const int DIM2 = 2; - const int DIM3 = 3; - const int DIM4 = 4; - std::array perm4 {{ 3, 2, 1, 0 } }; - std::array perm3 {{ 2, 1, 0 } }; - std::array perm2 {{ 1, 0 } }; - - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > J(jacobian.ReadWrite(), layout_jacob); - - RAJA::Layout layout_geom = RAJA::make_permuted_layout({{ nqpts, dim, dim, nelems } }, perm4); - RAJA::View > geom_j_view(geom->J.Read(), layout_geom); - - RAJA::Layout layout_egrads = RAJA::make_permuted_layout({{ nnodes, dim, nelems } }, perm3); - RAJA::View > eDS_view(eDS.ReadWrite(), layout_egrads); - - // Transpose of the local gradient variable - RAJA::Layout layout_grads = RAJA::make_permuted_layout({{ nnodes, dim, nqpts } }, perm3); - RAJA::View > Gt(grad.Read(), layout_grads); - - RAJA::Layout layout_adj = RAJA::make_permuted_layout({{ dim, dim } }, perm2); - const int nqpts_ = nqpts; - const int dim_ = dim; - const int nnodes_ = nnodes; - - MFEM_FORALL(i, nelems, { - for (int j = 0; j < nqpts_; j++) { - for (int k = 0; k < dim_; k++) { - for (int l = 0; l < dim_; l++) { - J(l, k, j, i) = geom_j_view(j, l, k, i); - } - } - } - }); - - // This loop we'll want to parallelize the rest are all serial for now. - MFEM_FORALL(i_elems, nelems, { - double adj[dim_ * dim_]; - double c_detJ; - double volume = 0.0; - // So, we're going to say this view is constant however we're going to mutate the values only in - // that one scoped section for the quadrature points. - RAJA::View > A(&adj[0], layout_adj); - for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { - // If we scope this then we only need to carry half the number of variables around with us for - // the adjugate term. - { - const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 - const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 - const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 - const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 - const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 - const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 - const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 - const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 - const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 - const double detJ = J11 * (J22 * J33 - J32 * J23) - - /* */ J21 * (J12 * J33 - J32 * J13) + - /* */ J31 * (J12 * J23 - J22 * J13); - c_detJ = W[j_qpts]; - volume += c_detJ * detJ; - // adj(J) - adj[0] = (J22 * J33) - (J23 * J32); // 0,0 - adj[1] = (J32 * J13) - (J12 * J33); // 0,1 - adj[2] = (J12 * J23) - (J22 * J13); // 0,2 - adj[3] = (J31 * J23) - (J21 * J33); // 1,0 - adj[4] = (J11 * J33) - (J13 * J31); // 1,1 - adj[5] = (J21 * J13) - (J11 * J23); // 1,2 - adj[6] = (J21 * J32) - (J31 * J22); // 2,0 - adj[7] = (J31 * J12) - (J11 * J32); // 2,1 - adj[8] = (J11 * J22) - (J12 * J21); // 2,2 - } - for (int knds = 0; knds < nnodes_; knds++) { - eDS_view(knds, 0, i_elems) += c_detJ * (Gt(knds, 0, j_qpts) * A(0, 0) - + Gt(knds, 1, j_qpts) * A(0, 1) - + Gt(knds, 2, j_qpts) * A(0, 2)); - - eDS_view(knds, 1, i_elems) += c_detJ * (Gt(knds, 0, j_qpts) * A(1, 0) - + Gt(knds, 1, j_qpts) * A(1, 1) - + Gt(knds, 2, j_qpts) * A(1, 2)); - - eDS_view(knds, 2, i_elems) += c_detJ * (Gt(knds, 0, j_qpts) * A(2, 0) - + Gt(knds, 1, j_qpts) * A(2, 1) - + Gt(knds, 2, j_qpts) * A(2, 2)); - } // End of nnodes - } // End of nqpts - - double ivol = 1.0 / volume; - - for (int knds = 0; knds < nnodes_; knds++) { - eDS_view(knds, 0, i_elems) *= ivol; - eDS_view(knds, 1, i_elems) *= ivol; - eDS_view(knds, 2, i_elems) *= ivol; - } - }); // End of MFEM_FORALL - - } // End of space dims if else -} - -// Here we're applying the following action operation using the assembled "D" 2nd order -// tensor found above: -// y_{ik} = \nabla_{ij}\phi^T_{\epsilon} D_{jk} -void ICExaNLFIntegrator::AddMultPA(const mfem::Vector & /*x*/, mfem::Vector &y) const -{ - CALI_CXX_MARK_SCOPE("icenlfi_amPAV"); - - // return a pointer to beginning step stress. This is used for output visualization - QuadratureFunction *stress_end = model->GetStress1(); - - const IntegrationRule &ir = model->GetMatGrad()->GetSpace()->GetIntRule(0); - auto W = ir.GetWeights().Read(); - - if ((space_dims == 1) || (space_dims == 2)) { - MFEM_ABORT("Dimensions of 1 or 2 not supported."); - } - else { - - const int dim = 3; - const int DIM2 = 2; - const int DIM3 = 3; - const int DIM4 = 4; - - std::array perm4 {{ 3, 2, 1, 0 } }; - std::array perm3 {{ 2, 1, 0 } }; - std::array perm2 {{ 1, 0 } }; - - - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > J(jacobian.Read(), layout_jacob); - - RAJA::Layout layout_stress = RAJA::make_permuted_layout({{ 2 * dim, nqpts, nelems } }, perm3); - RAJA::View > S(stress_end->ReadWrite(), - layout_stress); - - // Our field variables that are inputs and outputs - RAJA::Layout layout_field = RAJA::make_permuted_layout({{ nnodes, dim, nelems } }, perm3); - RAJA::View > Y(y.ReadWrite(), layout_field); - // Transpose of the local gradient variable - RAJA::Layout layout_grads = RAJA::make_permuted_layout({{ nnodes, dim, nqpts } }, perm3); - RAJA::View > Gt(grad.Read(), layout_grads); - - RAJA::Layout layout_egrads = RAJA::make_permuted_layout({{ nnodes, dim, nelems } }, perm3); - RAJA::View > eDS_view(eDS.Read(), layout_egrads); - - RAJA::Layout layout_adj = RAJA::make_permuted_layout({{ dim, dim } }, perm2); - - const double i3 = 1.0 / 3.0; - const int nqpts_ = nqpts; - const int dim_ = dim; - const int nnodes_ = nnodes; - - // This loop we'll want to parallelize the rest are all serial for now. - MFEM_FORALL(i_elems, nelems, { - double adj[dim_ * dim_]; - double c_detJ; - double idetJ; - // So, we're going to say this view is constant however we're going to mutate the values only in - // that one scoped section for the quadrature points. - RAJA::View > A(&adj[0], layout_adj); - for (int j_qpts = 0; j_qpts < nqpts_; j_qpts++) { - // If we scope this then we only need to carry half the number of variables around with us for - // the adjugate term. - { - const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 - const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 - const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 - const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 - const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 - const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 - const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 - const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 - const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 - const double detJ = J11 * (J22 * J33 - J32 * J23) - - /* */ J21 * (J12 * J33 - J32 * J13) + - /* */ J31 * (J12 * J23 - J22 * J13); - idetJ = 1.0 / detJ; - c_detJ = detJ * W[j_qpts]; - // adj(J) - adj[0] = (J22 * J33) - (J23 * J32); // 0,0 - adj[1] = (J32 * J13) - (J12 * J33); // 0,1 - adj[2] = (J12 * J23) - (J22 * J13); // 0,2 - adj[3] = (J31 * J23) - (J21 * J33); // 1,0 - adj[4] = (J11 * J33) - (J13 * J31); // 1,1 - adj[5] = (J21 * J13) - (J11 * J23); // 1,2 - adj[6] = (J21 * J32) - (J31 * J22); // 2,0 - adj[7] = (J31 * J12) - (J11 * J32); // 2,1 - adj[8] = (J11 * J22) - (J12 * J21); // 2,2 - } - for (int knds = 0; knds < nnodes_; knds++) { - const double bx = idetJ * (Gt(knds, 0, j_qpts) * A(0, 0) - + Gt(knds, 1, j_qpts) * A(0, 1) - + Gt(knds, 2, j_qpts) * A(0, 2)); - - const double by = idetJ * (Gt(knds, 0, j_qpts) * A(1, 0) - + Gt(knds, 1, j_qpts) * A(1, 1) - + Gt(knds, 2, j_qpts) * A(1, 2)); - - const double bz = idetJ * (Gt(knds, 0, j_qpts) * A(2, 0) - + Gt(knds, 1, j_qpts) * A(2, 1) - + Gt(knds, 2, j_qpts) * A(2, 2)); - - const double b4 = i3 * (eDS_view(knds, 0, i_elems) - bx); - const double b5 = b4 + bx; - const double b6 = i3 * (eDS_view(knds, 1, i_elems) - by); - const double b7 = b6 + by; - const double b8 = i3 * (eDS_view(knds, 2, i_elems) - bz); - const double b9 = b8 + bz; - - Y(knds, 0, i_elems) += c_detJ * (b4 * S(1, j_qpts, i_elems) - + b4 * S(2, j_qpts, i_elems) - + b5 * S(0, j_qpts, i_elems) - + by * S(5, j_qpts, i_elems) - + bz * S(4, j_qpts, i_elems)); - - Y(knds, 1, i_elems) += c_detJ * (b6 * S(0, j_qpts, i_elems) - + b6 * S(2, j_qpts, i_elems) - + b7 * S(1, j_qpts, i_elems) - + bx * S(5, j_qpts, i_elems) - + bz * S(3, j_qpts, i_elems)); - - Y(knds, 2, i_elems) += c_detJ * (b8 * S(0, j_qpts, i_elems) - + b8 * S(1, j_qpts, i_elems) - + b9 * S(2, j_qpts, i_elems) - + bx * S(4, j_qpts, i_elems) - + by * S(3, j_qpts, i_elems)); - }// End of nnodes - } // End of nQpts - }); // End of nelems - } // End of if statement -} diff --git a/src/mechanics_integrators.hpp b/src/mechanics_integrators.hpp deleted file mode 100644 index d780746..0000000 --- a/src/mechanics_integrators.hpp +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef MECHANICS_INTEG -#define MECHANICS_INTEG - -#include "mfem.hpp" -#include "mechanics_model.hpp" - -#include -#include -#include - -/// A NonlinearForm Integrator specifically built around the ExaModel class -/// and really focused around dealing with your general solid mechanics type -/// problems. -class ExaNLFIntegrator : public mfem::NonlinearFormIntegrator -{ - protected: - ExaModel *model; - // Will take a look and see what I need and don't need for this. - mfem::Vector dmat; - mfem::Vector grad; - mfem::Vector *tan_mat; // Not owned - mfem::Vector pa_dmat; - mfem::Vector jacobian; - const mfem::GeometricFactors *geom; // Not owned - int space_dims, nelems, nqpts, nnodes; - - public: - ExaNLFIntegrator(ExaModel *m) : model(m) { } - - virtual ~ExaNLFIntegrator() { } - - /// This doesn't do anything at this point. We can add the functionality - /// later on if a use case arises. - using mfem::NonlinearFormIntegrator::GetElementEnergy; - virtual double GetElementEnergy(const mfem::FiniteElement &el, - mfem::ElementTransformation &Ttr, - const mfem::Vector &elfun) override; - - using mfem::NonlinearFormIntegrator::AssembleElementVector; - /// Assembles the Div(sigma) term / RHS terms of our linearized system of equations. - virtual void AssembleElementVector(const mfem::FiniteElement &el, - mfem::ElementTransformation &Ttr, - const mfem::Vector &elfun, mfem::Vector &elvect) override; - - /// Assembles our gradient matrix (K matrix as seen in typical mechanics FEM formulations) - virtual void AssembleElementGrad(const mfem::FiniteElement &el, - mfem::ElementTransformation &Ttr, - const mfem::Vector & /*elfun*/, mfem::DenseMatrix &elmat) override; - - // We currently don't have the AssemblePADiagonal still need to work out what this - // would look like for the 4D tensor contraction operation - - /** @brief Performs the initial assembly operation on our 4D stiffness tensor - * combining the adj(J) terms, quad pt wts, and det(J) terms. - * - * In the below function we'll be applying the below action on our material - * tangent matrix C^{tan} at each quadrature point as: - * D_{ijkm} = 1 / det(J) * w_{qpt} * adj(J)^T_{ij} C^{tan}_{ijkl} adj(J)_{lm} - * where D is our new 4th order tensor, J is our jacobian calculated from the - * mesh geometric factors, and adj(J) is the adjugate of J. - */ - virtual void AssembleGradPA(const mfem::Vector &/* x */, const mfem::FiniteElementSpace &fes) override; - virtual void AssembleGradPA(const mfem::FiniteElementSpace &fes) override; - virtual void AddMultGradPA(const mfem::Vector &x, mfem::Vector &y) const override; - - using mfem::NonlinearFormIntegrator::AssemblePA; - virtual void AssemblePA(const mfem::FiniteElementSpace &fes) override; - virtual void AddMultPA(const mfem::Vector & /*x*/, mfem::Vector &y) const override; - - virtual void AssembleGradDiagonalPA(mfem::Vector &diag) const override; - - /// Method defining element assembly. - /** The result of the element assembly is added and stored in the @a emat - Vector. */ - virtual void AssembleGradEA(const mfem::Vector &/* x */, const mfem::FiniteElementSpace &fes, mfem::Vector & ea_data) override; - virtual void AssembleEA(const mfem::FiniteElementSpace &fes, mfem::Vector &emat) override; -}; - -/// A NonlinearForm Integrator specifically built around the ExaModel class -/// and really focused around dealing with incompressible type solid mechanics -/// problems. It implements the Bbar method given in TRJ Hughes The Finite Element -/// Method book section 4.5.2. -class ICExaNLFIntegrator : public ExaNLFIntegrator -{ - private: - // Will take a look and see what I need and don't need for this. - mfem::Vector eDS; - public: - ICExaNLFIntegrator(ExaModel *m) : ExaNLFIntegrator(m) { } - - virtual ~ICExaNLFIntegrator() { } - - /// This doesn't do anything at this point. We can add the functionality - /// later on if a use case arises. - using ExaNLFIntegrator::GetElementEnergy; - - using mfem::NonlinearFormIntegrator::AssembleElementVector; - /// Assembles the Div(sigma) term / RHS terms of our linearized system of equations. - virtual void AssembleElementVector(const mfem::FiniteElement &el, - mfem::ElementTransformation &Ttr, - const mfem::Vector &elfun, mfem::Vector &elvect) override; - - /// Assembles our gradient matrix (K matrix as seen in typical mechanics FEM formulations) - virtual void AssembleElementGrad(const mfem::FiniteElement &el, - mfem::ElementTransformation &Ttr, - const mfem::Vector & /*elfun*/, mfem::DenseMatrix &elmat) override; - - // This method doesn't easily extend to PA formulation, so we're punting on - // it for now. - using ExaNLFIntegrator::AssembleGradPA; - using ExaNLFIntegrator::AddMultGradPA; - - // using mfem::NonlinearFormIntegrator::AssemblePA; - // We've got to override this as well for the Bbar method... - virtual void AssemblePA(const mfem::FiniteElementSpace &fes) override; - virtual void AddMultPA(const mfem::Vector & /*x*/, mfem::Vector &y) const override; - - virtual void AssembleGradDiagonalPA(mfem::Vector &diag) const override; - - /// Method defining element assembly. - /** The result of the element assembly is added and stored in the @a emat - Vector. */ - virtual void AssembleGradEA(const mfem::Vector &/* x */, const mfem::FiniteElementSpace &fes, mfem::Vector & ea_data) override; - virtual void AssembleEA(const mfem::FiniteElementSpace &fes, mfem::Vector &emat) override; -}; - -// } - -#endif diff --git a/src/mechanics_kernels.cpp b/src/mechanics_kernels.cpp deleted file mode 100644 index c5aef5a..0000000 --- a/src/mechanics_kernels.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "mechanics_kernels.hpp" -#include "mfem/general/forall.hpp" - -namespace exaconstit{ -namespace kernel { - -void grad_calc(const int nqpts, const int nelems, const int nnodes, - const double *jacobian_data, const double *loc_grad_data, - const double *field_data, double* field_grad_array) -{ - const int DIM4 = 4; - const int DIM3 = 3; - const int DIM2 = 2; - std::array perm4 {{ 3, 2, 1, 0 } }; - std::array perm3{{ 2, 1, 0 } }; - std::array perm2{{ 1, 0 } }; - - const int dim = 3; - const int space_dim2 = dim * dim; - - // bunch of helper RAJA views to make dealing with data easier down below in our kernel. - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > J(jacobian_data, layout_jacob); - // vgrad - RAJA::Layout layout_grad = RAJA::make_permuted_layout({{ dim, dim, nqpts, nelems } }, perm4); - RAJA::View > field_grad_view(field_grad_array, layout_grad); - // velocity - RAJA::Layout layout_field = RAJA::make_permuted_layout({{ nnodes, dim, nelems } }, perm3); - RAJA::View > field_view(field_data, layout_field); - // loc_grad - RAJA::Layout layout_loc_grad = RAJA::make_permuted_layout({{ nnodes, dim, nqpts } }, perm3); - RAJA::View > loc_grad_view(loc_grad_data, layout_loc_grad); - - RAJA::Layout layout_jinv = RAJA::make_permuted_layout({{ dim, dim } }, perm2); - - mfem::MFEM_FORALL(i_elems, nelems, { - for (int j_qpts = 0; j_qpts < nqpts; j_qpts++) { - const double J11 = J(0, 0, j_qpts, i_elems); // 0,0 - const double J21 = J(1, 0, j_qpts, i_elems); // 1,0 - const double J31 = J(2, 0, j_qpts, i_elems); // 2,0 - const double J12 = J(0, 1, j_qpts, i_elems); // 0,1 - const double J22 = J(1, 1, j_qpts, i_elems); // 1,1 - const double J32 = J(2, 1, j_qpts, i_elems); // 2,1 - const double J13 = J(0, 2, j_qpts, i_elems); // 0,2 - const double J23 = J(1, 2, j_qpts, i_elems); // 1,2 - const double J33 = J(2, 2, j_qpts, i_elems); // 2,2 - const double detJ = J11 * (J22 * J33 - J32 * J23) - - /* */ J21 * (J12 * J33 - J32 * J13) + - /* */ J31 * (J12 * J23 - J22 * J13); - const double c_detJ = 1.0 / detJ; - // adj(J) - const double A11 = c_detJ * ((J22 * J33) - (J23 * J32)); - const double A12 = c_detJ * ((J32 * J13) - (J12 * J33)); - const double A13 = c_detJ * ((J12 * J23) - (J22 * J13)); - const double A21 = c_detJ * ((J31 * J23) - (J21 * J33)); - const double A22 = c_detJ * ((J11 * J33) - (J13 * J31)); - const double A23 = c_detJ * ((J21 * J13) - (J11 * J23)); - const double A31 = c_detJ * ((J21 * J32) - (J31 * J22)); - const double A32 = c_detJ * ((J31 * J12) - (J11 * J32)); - const double A33 = c_detJ * ((J11 * J22) - (J12 * J21)); - const double A[space_dim2] = { A11, A21, A31, A12, A22, A32, A13, A23, A33 }; - // Raja view to make things easier again - - RAJA::View > jinv_view(&A[0], layout_jinv); - // All of the data down below is ordered in column major order - for (int t = 0; t < dim; t++) { - for (int s = 0; s < dim; s++) { - for (int r = 0; r < nnodes; r++) { - for (int q = 0; q < dim; q++) { - field_grad_view(q, t, j_qpts, i_elems) += field_view(r, q, i_elems) * - loc_grad_view(r, s, j_qpts) * jinv_view(s, t); - } - } - } - } // End of loop used to calculate field gradient - } // end of forall loop for quadrature points - }); // end of forall loop for number of elements -} // end of kernel_grad_calc - -} -} \ No newline at end of file diff --git a/src/mechanics_kernels.hpp b/src/mechanics_kernels.hpp deleted file mode 100644 index c4ae9c3..0000000 --- a/src/mechanics_kernels.hpp +++ /dev/null @@ -1,267 +0,0 @@ -#ifndef MECHANICS_KERNELS -#define MECHANICS_KERNELS - -#include "mfem.hpp" -#include "RAJA/RAJA.hpp" -#include "option_types.hpp" -#include "mfem/general/forall.hpp" - -namespace exaconstit { -namespace kernel { -/// Performs all the calculations related to calculating the gradient of a 3D vector field -/// grad_array should be set to 0.0 outside of this function. -// It is assumed that whatever data pointers being passed in is consistent with -// with the execution strategy being used by the MFEM_FORALL. -void grad_calc(const int nqpts, const int nelems, const int nnodes, - const double *jacobian_data, const double *loc_grad_data, - const double *field_data, double* field_grad_array); -//Computes the volume average values of values that lie at the quadrature points -template -void ComputeVolAvgTensor(const mfem::ParFiniteElementSpace* fes, - const mfem::QuadratureFunction* qf, - mfem::Vector& tensor, int size, - RTModel &class_device) -{ - mfem::Mesh *mesh = fes->GetMesh(); - const mfem::FiniteElement &el = *fes->GetFE(0); - const mfem::IntegrationRule *ir = &(mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1));; - - const int nqpts = ir->GetNPoints(); - const int nelems = fes->GetNE(); - const int npts = nqpts * nelems; - - const double* W = ir->GetWeights().Read(); - const mfem::GeometricFactors *geom = mesh->GetGeometricFactors(*ir, mfem::GeometricFactors::DETERMINANTS); - - double el_vol = 0.0; - int my_id; - MPI_Comm_rank(MPI_COMM_WORLD, &my_id); - mfem::Vector data(size); - - const int DIM2 = 2; - std::array perm2 {{ 1, 0 } }; - RAJA::Layout layout_geom = RAJA::make_permuted_layout({{ nqpts, nelems } }, perm2); - - mfem::Vector wts(geom->detJ); - RAJA::View > wts_view(wts.ReadWrite(), layout_geom); - RAJA::View > j_view(geom->detJ.Read(), layout_geom); - - RAJA::RangeSegment default_range(0, npts); - - mfem::MFEM_FORALL(i, nelems, { - const int nqpts_ = nqpts; - for (int j = 0; j < nqpts_; j++) { - wts_view(j, i) = j_view(j, i) * W[j]; - } - }); - - if (class_device == RTModel::CPU) { - const double* qf_data = qf->HostRead(); - const double* wts_data = wts.HostRead(); - for (int j = 0; j < size; j++) { - RAJA::ReduceSum seq_sum(0.0); - RAJA::ReduceSum vol_sum(0.0); - RAJA::forall(default_range, [ = ] (int i_npts){ - const double* val = &(qf_data[i_npts * size]); - seq_sum += wts_data[i_npts] * val[j]; - vol_sum += wts_data[i_npts]; - }); - data[j] = seq_sum.get(); - el_vol = vol_sum.get(); - } - } -#if defined(RAJA_ENABLE_OPENMP) - if (class_device == RTModel::OPENMP) { - const double* qf_data = qf->HostRead(); - const double* wts_data = wts.HostRead(); - for (int j = 0; j < size; j++) { - RAJA::ReduceSum omp_sum(0.0); - RAJA::ReduceSum vol_sum(0.0); - RAJA::forall(default_range, [ = ] (int i_npts){ - const double* val = &(qf_data[i_npts * size]); - omp_sum += wts_data[i_npts] * val[j]; - vol_sum += wts_data[i_npts]; - }); - data[j] = omp_sum.get(); - el_vol = vol_sum.get(); - } - } -#endif -#if defined(RAJA_ENABLE_CUDA) || defined(RAJA_ENABLE_HIP) - if (class_device == RTModel::GPU) { - const double* qf_data = qf->Read(); - const double* wts_data = wts.Read(); -#if defined(RAJA_ENABLE_CUDA) - using gpu_reduce = RAJA::cuda_reduce; - using gpu_policy = RAJA::cuda_exec<1024>; -#else - using gpu_reduce = RAJA::hip_reduce; - using gpu_policy = RAJA::hip_exec<1024>; -#endif - for (int j = 0; j < size; j++) { - RAJA::ReduceSum gpu_sum(0.0); - RAJA::ReduceSum vol_sum(0.0); - RAJA::forall(default_range, [ = ] RAJA_DEVICE(int i_npts){ - const double* val = &(qf_data[i_npts * size]); - gpu_sum += wts_data[i_npts] * val[j]; - vol_sum += wts_data[i_npts]; - }); - data[j] = gpu_sum.get(); - el_vol = vol_sum.get(); - } - } -#endif - - for (int i = 0; i < size; i++) { - tensor[i] = data[i]; - } - - MPI_Allreduce(data.HostRead(), tensor.HostReadWrite(), size, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - if (vol_avg) { - double temp = el_vol; - - // Here we find what el_vol should be equal to - MPI_Allreduce(&temp, &el_vol, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - - // We meed to multiple by 1/V by our tensor values to get the appropriate - // average value for the tensor in the end. - double inv_vol = 1.0 / el_vol; - - for (int m = 0; m < size; m++) { - tensor[m] *= inv_vol; - } - } -} - -//Computes the volume average values of values that lie at the quadrature points -//but only computes the values that aren't filtered out -// aka It only includes values that are set to true in filter -// It also returns the volume that corresponds to the values that were filtered -template -double ComputeVolAvgTensorFilter(const mfem::ParFiniteElementSpace* fes, - const mfem::QuadratureFunction* qf, - const mfem::Array* filter, - mfem::Vector& tensor, int size, - const RTModel &class_device) -{ - mfem::Mesh *mesh = fes->GetMesh(); - const mfem::FiniteElement &el = *fes->GetFE(0); - const mfem::IntegrationRule *ir = &(mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1));; - - const int nqpts = ir->GetNPoints(); - const int nelems = fes->GetNE(); - const int npts = nqpts * nelems; - - const double* W = ir->GetWeights().Read(); - const mfem::GeometricFactors *geom = mesh->GetGeometricFactors(*ir, mfem::GeometricFactors::DETERMINANTS); - - double el_vol = 0.0; - int my_id; - MPI_Comm_rank(MPI_COMM_WORLD, &my_id); - mfem::Vector data(size); - - const int DIM2 = 2; - std::array perm2 {{ 1, 0 } }; - RAJA::Layout layout_geom = RAJA::make_permuted_layout({{ nqpts, nelems } }, perm2); - - mfem::Vector wts(geom->detJ); - RAJA::View > wts_view(wts.ReadWrite(), layout_geom); - RAJA::View > j_view(geom->detJ.Read(), layout_geom); - - RAJA::RangeSegment default_range(0, npts); - - mfem::MFEM_FORALL(i, nelems, { - const int nqpts_ = nqpts; - for (int j = 0; j < nqpts_; j++) { - wts_view(j, i) = j_view(j, i) * W[j]; - } - }); - - if (class_device == RTModel::CPU) { - const double* qf_data = qf->HostRead(); - const bool* filter_data = filter->HostRead(); - const double* wts_data = wts.HostRead(); - for (int j = 0; j < size; j++) { - RAJA::ReduceSum seq_sum(0.0); - RAJA::ReduceSum vol_sum(0.0); - RAJA::forall(default_range, [ = ] (int i_npts){ - if (!filter_data[i_npts]) return; - const double* val = &(qf_data[i_npts * size]); - seq_sum += wts_data[i_npts] * val[j]; - vol_sum += wts_data[i_npts]; - }); - data[j] = seq_sum.get(); - el_vol = vol_sum.get(); - } - } -#if defined(RAJA_ENABLE_OPENMP) - if (class_device == RTModel::OPENMP) { - const double* qf_data = qf->HostRead(); - const bool* filter_data = filter->HostRead(); - const double* wts_data = wts.HostRead(); - for (int j = 0; j < size; j++) { - RAJA::ReduceSum omp_sum(0.0); - RAJA::ReduceSum vol_sum(0.0); - RAJA::forall(default_range, [ = ] (int i_npts){ - if (!filter_data[i_npts]) return; - const double* val = &(qf_data[i_npts * size]); - omp_sum += wts_data[i_npts] * val[j]; - vol_sum += wts_data[i_npts]; - }); - data[j] = omp_sum.get(); - el_vol = vol_sum.get(); - } - } -#endif -#if defined(RAJA_ENABLE_CUDA) || defined(RAJA_ENABLE_HIP) - if (class_device == RTModel::GPU) { - const double* qf_data = qf->Read(); - const bool* filter_data = filter->Read(); - const double* wts_data = wts.Read(); -#if defined(RAJA_ENABLE_CUDA) - using gpu_reduce = RAJA::cuda_reduce; - using gpu_policy = RAJA::cuda_exec<1024>; -#else - using gpu_reduce = RAJA::hip_reduce; - using gpu_policy = RAJA::hip_exec<1024>; -#endif - for (int j = 0; j < size; j++) { - RAJA::ReduceSum gpu_sum(0.0); - RAJA::ReduceSum vol_sum(0.0); - RAJA::forall(default_range, [ = ] RAJA_DEVICE(int i_npts){ - if (!filter_data[i_npts]) return; - const double* val = &(qf_data[i_npts * size]); - gpu_sum += wts_data[i_npts] * val[j]; - vol_sum += wts_data[i_npts]; - }); - data[j] = gpu_sum.get(); - el_vol = vol_sum.get(); - } - } -#endif - - for (int i = 0; i < size; i++) { - tensor[i] = data[i]; - } - - MPI_Allreduce(data.HostRead(), tensor.HostReadWrite(), size, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - - double temp = el_vol; - // Here we find what el_vol should be equal to - MPI_Allreduce(&temp, &el_vol, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - - if (vol_avg) { - // We meed to multiple by 1/V by our tensor values to get the appropriate - // average value for the tensor in the end. - double inv_vol = (fabs(el_vol) > 1e-14) ? 1.0 / el_vol : 0.0; - - for (int m = 0; m < size; m++) { - tensor[m] *= inv_vol; - } - } - return el_vol; -} - -} -} -#endif diff --git a/src/mechanics_lightup.hpp b/src/mechanics_lightup.hpp deleted file mode 100644 index 919c5b9..0000000 --- a/src/mechanics_lightup.hpp +++ /dev/null @@ -1,662 +0,0 @@ -#pragma once - -#include "option_types.hpp" -#include "mechanics_kernels.hpp" - -#include "mfem.hpp" -#include "mfem/general/forall.hpp" - -#include "SNLS_linalg.h" -#include "ECMech_const.h" -#include "ECMech_gpu_portability.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -template -class LightUp { -public: - -LightUp(const std::vector> &hkls, - const double distance_tolerance, - const double s_dir[3], - const mfem::ParFiniteElementSpace* pfes, - mfem::QuadratureSpaceBase* qspace, - const std::unordered_map > &qf_mapping, - const RTModel &rtmodel, - const std::string &lattice_basename, - const double lattice_params[3]); - -~LightUp() = default; - -void calculate_lightup_data(const mfem::QuadratureFunction& history, - const mfem::QuadratureFunction& stress); - -void calculate_in_fibers(const mfem::QuadratureFunction& history, - const size_t quats_offset, - const size_t hkl_index); - - -void calc_lattice_strains(const mfem::QuadratureFunction& history, - const size_t strain_offset, - const size_t quats_offset, - const size_t rel_vol_offset, - std::vector& lattice_strains_output, - std::vector& lattice_volumes_output); - -void calc_lattice_taylor_factor_dpeff(const mfem::QuadratureFunction& history, - const size_t dpeff_offset, - const size_t gdot_offset, - const size_t gdot_length, - std::vector &lattice_tay_facs, - std::vector &lattice_dpeff); - -void calc_lattice_directional_stiffness(const mfem::QuadratureFunction& history, - const mfem::QuadratureFunction& stress, - const size_t strain_offset, - const size_t quats_offset, - const size_t rel_vol_offset, - std::vector> &lattice_dir_stiff); - -private: - std::vector> m_hkls; - const double m_distance_tolerance; - double m_s_dir[3]; - const mfem::ParFiniteElementSpace* m_pfes; - const size_t m_npts; - const RTModel m_class_device; - const std::unordered_map > m_qf_mapping; - const std::string m_lattice_basename; - const LatticeType m_lattice; - mfem::QuadratureFunction m_workspace; - std::vector> m_in_fibers; - std::vector m_rmat_fr_qsym_c_dir; -}; - -class LatticeTypeCubic { -public: -static constexpr size_t NSYM = 24; - -LatticeTypeCubic(const double lattice_param_a[3]) -{ - symmetric_cubic_quaternions(); - compute_lattice_b_param(lattice_param_a); -} - -~LatticeTypeCubic() = default; - -void -compute_lattice_b_param(const double lparam_a[3]) -{ - constexpr double FRAC_PI_2 = 1.57079632679489661923132169163975144; - const double cellparms[6] = {lparam_a[0], lparam_a[1], lparam_a[2], FRAC_PI_2, FRAC_PI_2, FRAC_PI_2}; - - const double alfa = cellparms[3]; - const double beta = cellparms[4]; - const double gamma = cellparms[5]; - - const double cosalfar = (cos(beta) * cos(gamma) - cos(alfa)) / (sin(beta) * sin(gamma)); - const double sinalfar = sqrtf(1.0 - cosalfar * cosalfar); - - const double a[3] = {cellparms[0], 0.0, 0.0}; - const double b[3] = {cellparms[1] * cos(gamma), cellparms[1] * sin(gamma), 0.0}; - const double c[3] = {cellparms[2] * cos(beta), -cellparms[2] * cosalfar * sin(beta), cellparms[2] * sinalfar * sin(beta)}; - - // Cell volume - double vol[3] = {}; - auto cross_prod = [&](const double* const vec1, - const double* const vec2, - double* const prod) { - prod[0] = vec1[1] * vec2[2] - vec1[2] * vec2[1]; - prod[1] = vec1[2] * vec2[0] - vec1[0] * vec2[2]; - prod[2] = vec1[0] * vec2[1] - vec1[1] * vec2[0]; - }; - - cross_prod(b, c, vol); - const double inv_vol = 1.0 / snls::linalg::dotProd<3>(a, vol); - - // Reciprocal lattice vectors - auto cross_prod_inv_v = [&](const double* const vec1, const double* const vec2, double* cross_prod_v) { - cross_prod(vec1, vec2, cross_prod_v); - cross_prod_v[0] *= inv_vol; - cross_prod_v[1] *= inv_vol; - cross_prod_v[2] *= inv_vol; - }; - - double * latb[3] = {&lattice_b[0], &lattice_b[3], &lattice_b[6]}; - // B takes components in the reciprocal lattice to X - cross_prod_inv_v(b, c, latb[0]); - cross_prod_inv_v(c, a, latb[1]); - cross_prod_inv_v(a, b, latb[2]); -} - -void -symmetric_cubic_quaternions() -{ - constexpr double PI = 3.14159265358979323846264338327950288; - constexpr double FRAC_PI_2 = 1.57079632679489661923132169163975144; - constexpr double FRAC_PI_3 = 1.04719755119659774615421446109316763; - - constexpr double angle_axis_symm [NSYM][4] = { - {0.0, 1.0, 0.0, 0.0}, // identity - {FRAC_PI_2, 1.0, 0.0, 0.0}, // fourfold about 1 0 0 (x1) - {PI, 1.0, 0.0, 0.0}, // - {FRAC_PI_2 * 3.0, 1.0, 0.0, 0.0}, // - {FRAC_PI_2, 0.0, 1.0, 0.0}, // fourfold about 0 1 0 (x2) - {PI, 0.0, 1.0, 0.0}, // - {FRAC_PI_2 * 3.0, 0.0, 1.0, 0.0}, // - {FRAC_PI_2, 0.0, 0.0, 1.0}, // fourfold about 0 0 1 (x3) - {PI, 0.0, 0.0, 1.0}, // - {FRAC_PI_2 * 3.0, 0.0, 0.0, 1.0}, // - {FRAC_PI_3 * 2.0, 1.0, 1.0, 1.0}, // threefold about 1 1 1 - {FRAC_PI_3 * 4.0, 1.0, 1.0, 1.0}, // - {FRAC_PI_3 * 2.0, -1.0, 1.0, 1.0}, // threefold about -1 1 1 - {FRAC_PI_3 * 4.0, -1.0, 1.0, 1.0}, // - {FRAC_PI_3 * 2.0, -1.0, -1.0, 1.0}, // threefold about -1 -1 1 - {FRAC_PI_3 * 4.0, -1.0, -1.0, 1.0}, // - {FRAC_PI_3 * 2.0, 1.0, -1.0, 1.0}, // threefold about 1 -1 1 - {FRAC_PI_3 * 4.0, 1.0, -1.0, 1.0}, // - {PI, 1.0, 1.0, 0.0}, // twofold about 1 1 0 - {PI, -1.0, 1.0, 0.0}, // twofold about -1 1 0 - {PI, 1.0, 0.0, 1.0}, // twofold about 1 0 1 - {PI, 0.0, 1.0, 1.0}, // twofold about 0 1 1 - {PI, -1.0, 0.0, 1.0}, // twofold about -1 0 1 - {PI, 0.0, -1.0, 1.0}, // twofold about 0 -1 1 - }; - - constexpr double inv2 = 1.0 / 2.0; - - for (size_t isym = 0; isym < NSYM; isym++) { - double *symm_quat = &quat_symm[isym * 4]; - const double s = sin(inv2 * angle_axis_symm[isym][0]); - symm_quat[0] = cos(inv2 * angle_axis_symm[isym][0]); - double inv_norm_axis = 1.0 / snls::linalg::norm<3>(&angle_axis_symm[isym][1]); - symm_quat[1] = s * angle_axis_symm[isym][1] * inv_norm_axis; - symm_quat[2] = s * angle_axis_symm[isym][2] * inv_norm_axis; - symm_quat[3] = s * angle_axis_symm[isym][3] * inv_norm_axis; - - inv_norm_axis = 1.0; - if (symm_quat[0] < 0.0) { - inv_norm_axis *= -1.0; - } - - symm_quat[0] *= inv_norm_axis; - symm_quat[1] *= inv_norm_axis; - symm_quat[2] *= inv_norm_axis; - symm_quat[3] *= inv_norm_axis; - } -} - -public: - double lattice_b[3 * 3]; - double quat_symm[24 * 4]; -}; - -namespace no_std { -template -struct IsStdArray : std::false_type {}; -template -struct IsStdArray> : std::true_type {}; -} - -template -void printArray(std::ostream &stream, std::array &array) { - stream << "\"[ "; - for (size_t i = 0; i < N - 1; i++) { - stream << std::scientific << std::setprecision(6) << array[i] << ","; - } - stream << array[N - 1] << " ]\"\t"; -} - -template -void printValues(std::ostream &stream, T& t) { - if constexpr (no_std::IsStdArray::value) { - printArray(stream, t); - } - else { - stream << std::scientific << std::setprecision(6) << t << "\t"; - } -} - -__ecmech_hdev__ -inline -void -quat2rmat(const double* const quat, - double* const rmats) -{ - double qbar = quat[0] * quat[0] - (quat[1] * quat[1] + quat[2] * quat[2] + quat[3] * quat[3]); - - double* rmat[3] = {&rmats[0], &rmats[3], &rmats[6]}; - - rmat[0][0] = qbar + 2.0 * quat[1] * quat[1]; - rmat[1][0] = 2.0 * (quat[1] * quat[2] + quat[0] * quat[3]); - rmat[2][0] = 2.0 * (quat[1] * quat[3] - quat[0] * quat[2]); - - rmat[0][1] = 2.0 * (quat[1] * quat[2] - quat[0] * quat[3]); - rmat[1][1] = qbar + 2.0 * quat[2] * quat[2]; - rmat[2][1] = 2.0 * (quat[2] * quat[3] + quat[0] * quat[1]); - - rmat[0][2] = 2.0 * (quat[1] * quat[3] + quat[0] * quat[2]); - rmat[1][2] = 2.0 * (quat[2] * quat[3] - quat[0] * quat[1]); - rmat[2][2] = qbar + 2.0 * quat[3] * quat[3]; -} - -template -LightUp::LightUp(const std::vector> &hkls, - const double distance_tolerance, - const double s_dir[3], - const mfem::ParFiniteElementSpace* pfes, - mfem::QuadratureSpaceBase* qspace, - const std::unordered_map > &qf_mapping, - const RTModel &rtmodel, - const std::string &lattice_basename, - const double lattice_params[3]) : - m_hkls(hkls), - m_distance_tolerance(distance_tolerance), - m_pfes(pfes), - m_npts(qspace->GetSize()), - m_class_device(rtmodel), - m_qf_mapping(qf_mapping), - m_lattice_basename(lattice_basename), - m_lattice(lattice_params) -{ - m_s_dir[0] = s_dir[0]; - m_s_dir[1] = s_dir[1]; - m_s_dir[2] = s_dir[2]; - - m_workspace.SetSpace(qspace, 3); - - const double inv_s_norm = 1.0 / snls::linalg::norm<3>(s_dir); - m_s_dir[0] *= inv_s_norm; - m_s_dir[1] *= inv_s_norm; - m_s_dir[2] *= inv_s_norm; - - auto lat_vec_ops_b = m_lattice.lattice_b; - // First one we'll always set to be all the values - m_in_fibers.push_back(mfem::Array(m_npts)); - for (auto &hkl: hkls) { - m_in_fibers.push_back(mfem::Array(m_npts)); - // Computes reciprocal lattice B but different from HEXRD we return as row matrix as that's the easiest way of doing things - double c_dir[3]; - // compute crystal direction from planeData - snls::linalg::matTVecMult<3,3>(lat_vec_ops_b, hkl.data(), c_dir); - - const double inv_c_norm = 1.0 / snls::linalg::norm<3>(c_dir); - c_dir[0] *= inv_c_norm; - c_dir[1] *= inv_c_norm; - c_dir[2] *= inv_c_norm; - - // Could maybe move this over to a vec if we want this to be easily generic over a ton of symmetry conditions... - double rmat_fr_qsym_c_dir[LatticeType::NSYM][3] = {}; - mfem::Vector tmp(LatticeType::NSYM * 3); - for (size_t isym=0; isym < LatticeType::NSYM; isym++) { - double rmat[3 * 3] = {}; - quat2rmat(&m_lattice.quat_symm[isym * 4], rmat); - snls::linalg::matTVecMult<3,3>(rmat, c_dir, rmat_fr_qsym_c_dir[isym]); - tmp(isym * 3 + 0) = rmat_fr_qsym_c_dir[isym][0]; - tmp(isym * 3 + 1) = rmat_fr_qsym_c_dir[isym][1]; - tmp(isym * 3 + 2) = rmat_fr_qsym_c_dir[isym][2]; - } - tmp.UseDevice(true); - m_rmat_fr_qsym_c_dir.push_back(tmp); - } - - m_hkls.insert(m_hkls.begin(), {0.0, 0.0, 0.0}); - int my_id; - MPI_Comm_rank(MPI_COMM_WORLD, &my_id); - // Now we're going to save off the lattice values to a file - if (my_id == 0) { - - auto file_line_print = [&](auto& basename, auto& name, auto &m_hkls) { - std::string filename = basename + name; - std::ofstream file; - file.open(filename, std::ios_base::out); - - file << "#" << "\t"; - - for (auto& item : m_hkls) { - file << std::setprecision(1) << "\"[ " < = find_unique_tolerance::(&rmat_fr_qsym_c_dir, f64::sqrt(f64::EPSILON)); - - // Move all of the above to the object constructor - // rmat_fr_qsym_c_dir move to an mfem vector and then use it's data down here - // same with s_dir and c_dir - // Here iterate on which HKL we're using maybe have a map for these rmat_fr_qsym_c_dir and c_dir -} - -template -void -LightUp::calculate_lightup_data(const mfem::QuadratureFunction& history, - const mfem::QuadratureFunction& stress) -{ - std::string s_estrain = "elas_strain"; - std::string s_rvol = "rel_vol"; - std::string s_quats = "quats"; - std::string s_gdot = "gdot"; - std::string s_shrateEff = "shrateEff"; - - const size_t quats_offset = m_qf_mapping.find(s_quats)->second.first; - const size_t strain_offset = m_qf_mapping.find(s_estrain)->second.first; - const size_t rel_vol_offset = m_qf_mapping.find(s_rvol)->second.first; - const size_t dpeff_offset = m_qf_mapping.find(s_shrateEff)->second.first; - const size_t gdot_offset = m_qf_mapping.find(s_gdot)->second.first; - const size_t gdot_length = m_qf_mapping.find(s_gdot)->second.second; - - m_in_fibers[0] = true; - for (size_t ihkl = 0; ihkl < m_rmat_fr_qsym_c_dir.size(); ihkl++) { - calculate_in_fibers(history, quats_offset, ihkl); - } - - std::vector lattice_strains_output; - std::vector lattice_volumes_output; - - calc_lattice_strains(history, strain_offset, quats_offset, rel_vol_offset, lattice_strains_output, lattice_volumes_output); - - std::vector lattice_dpeff_output; - std::vector lattice_tayfac_output; - - calc_lattice_taylor_factor_dpeff(history, dpeff_offset, gdot_offset, gdot_length, lattice_tayfac_output, lattice_dpeff_output); - - std::vector> lattice_dir_stiff_output; - - calc_lattice_directional_stiffness(history, stress, strain_offset, quats_offset, rel_vol_offset, lattice_dir_stiff_output); - - int my_id; - MPI_Comm_rank(MPI_COMM_WORLD, &my_id); - // Now we're going to save off the lattice values to a file - if (my_id == 0) { - - auto file_line_print = [&](auto& basename, auto& name, auto &vec) { - std::string filename = basename + name; - std::ofstream file; - file.open(filename, std::ios_base::app); - - for (auto& item : vec) { - printValues(file, item); - } - file << std::endl; - - file.close(); - }; - - file_line_print(m_lattice_basename, "strains.txt", lattice_strains_output); - file_line_print(m_lattice_basename, "volumes.txt", lattice_volumes_output); - file_line_print(m_lattice_basename, "dpeff.txt", lattice_dpeff_output); - file_line_print(m_lattice_basename, "taylor_factor.txt", lattice_tayfac_output); - file_line_print(m_lattice_basename, "directional_stiffness.txt", lattice_dir_stiff_output); - } - -} - -template -void -LightUp::calculate_in_fibers(const mfem::QuadratureFunction& history, - const size_t quats_offset, - const size_t hkl_index) -{ - // Same could be said for in_fiber down here - // that way we just need to know which hkl and quats we're running with - const size_t vdim = history.GetVDim(); - const auto history_data = history.Read(); - - // First hkl_index is always completely true so we can easily - // compute the total volume average values - auto in_fiber_view = m_in_fibers[hkl_index + 1].Write(); - auto rmat_fr_qsym_c_dir = m_rmat_fr_qsym_c_dir[hkl_index].Read(); - - mfem::Vector s_dir(3); - s_dir[0] = m_s_dir[0]; s_dir[1] = m_s_dir[1]; s_dir[2] = m_s_dir[2]; - auto s_dir_data = s_dir.Read(); - auto distance_tolerance = m_distance_tolerance; - - mfem::MFEM_FORALL(iquats, m_npts, { - // for(size_t iquats = 0; iquats < m_npts; iquats++) { - - const auto quats = &history_data[iquats * vdim + quats_offset]; - double rmat[3 * 3] = {}; - quat2rmat(quats, rmat); - - double sine = -10; - for (size_t isym = 0; isym < LatticeType::NSYM; isym++) { - double prod[3] = {}; - snls::linalg::matVecMult<3,3>(rmat, &rmat_fr_qsym_c_dir[isym * 3], prod); - double tmp = snls::linalg::dotProd<3>(s_dir_data, prod); - sine = (tmp > sine) ? tmp : sine; - } - if (fabs(sine) > 1.00000001) { - sine = (sine >= 0) ? 1.0 : -1.0; - } - in_fiber_view[iquats] = acos(sine) <= distance_tolerance; - }); -} - -template -void -LightUp::calc_lattice_strains(const mfem::QuadratureFunction& history, - const size_t strain_offset, - const size_t quats_offset, - const size_t rel_vol_offset, - std::vector& lattice_strains_output, - std::vector& lattice_volumes_output) -{ - const double project_vec[6] = {m_s_dir[0] * m_s_dir[0], - m_s_dir[1] * m_s_dir[1], - m_s_dir[2] * m_s_dir[2], - 2.0 * m_s_dir[1] * m_s_dir[2], - 2.0 * m_s_dir[0] * m_s_dir[2], - 2.0 * m_s_dir[0] * m_s_dir[1]}; - - const size_t vdim = history.GetVDim(); - const auto history_data = history.Read(); - m_workspace = 0.0; - auto lattice_strains = m_workspace.Write(); - - // Only need to compute this once - mfem::MFEM_FORALL(iqpts, m_npts, { - // for(size_t iqpts = 0; iqpts < m_npts; iqpts++) { - const auto strain_lat = &history_data[iqpts * vdim + strain_offset]; - const auto quats = &history_data[iqpts * vdim + quats_offset]; - const auto rel_vol = history_data[iqpts * vdim + rel_vol_offset]; - - double strain[6] = {}; - { - double strainm[3 * 3] = {}; - double* strain_m[3] = {&strainm[0], &strainm[3], &strainm[6]}; - const double t1 = ecmech::sqr2i * strain_lat[0]; - const double t2 = ecmech::sqr6i * strain_lat[1]; - // - // Volume strain is ln(V^e_mean) term aka ln(relative volume) - // Our plastic deformation has a det(1) aka no change in volume change - const double elas_vol_strain = log(rel_vol); - // We output elastic strain formulation such that the relationship - // between V^e and \varepsilon is just V^e = I + \varepsilon - strain_m[0][0] = (t1 - t2) + elas_vol_strain; // 11 - strain_m[1][1] = (-t1 - t2) + elas_vol_strain ; // 22 - strain_m[2][2] = ecmech::sqr2b3 * strain_lat[1] + elas_vol_strain; // 33 - strain_m[1][2] = ecmech::sqr2i * strain_lat[4]; // 23 - strain_m[2][0] = ecmech::sqr2i * strain_lat[3]; // 31 - strain_m[0][1] = ecmech::sqr2i * strain_lat[2]; // 12 - - strain_m[2][1] = strain_m[1][2]; - strain_m[0][2] = strain_m[2][0]; - strain_m[1][0] = strain_m[0][1]; - - double rmat[3 * 3] = {}; - double strain_samp[3 * 3] = {}; - - quat2rmat(quats, rmat); - snls::linalg::rotMatrix<3, false>(strainm, rmat, strain_samp); - - strain_m[0] = &strain_samp[0]; - strain_m[1] = &strain_samp[3]; - strain_m[2] = &strain_samp[6]; - strain[0] = strain_m[0][0]; - strain[1] = strain_m[1][1]; - strain[2] = strain_m[2][2]; - strain[3] = strain_m[1][2]; - strain[4] = strain_m[0][2]; - strain[5] = strain_m[0][1]; - - } - const double proj_strain = snls::linalg::dotProd<6>(project_vec, strain); - lattice_strains[iqpts] = proj_strain; - - }); - - for (const auto& in_fiber_hkl : m_in_fibers){ - mfem::Vector lattice_strain_hkl(1); - const double lat_vol = exaconstit::kernel::ComputeVolAvgTensorFilter(m_pfes, &m_workspace, &in_fiber_hkl, lattice_strain_hkl, 1, m_class_device); - - lattice_volumes_output.push_back(lat_vol); - lattice_strains_output.push_back(lattice_strain_hkl(0)); - } -} - -template -void -LightUp::calc_lattice_taylor_factor_dpeff(const mfem::QuadratureFunction& history, - const size_t dpeff_offset, - const size_t gdot_offset, - const size_t gdot_length, - std::vector &lattice_tay_facs, - std::vector &lattice_dpeff) -{ - - const size_t vdim = history.GetVDim(); - const auto history_data = history.Read(); - m_workspace = 0.0; - auto lattice_tayfac_dpeffs = m_workspace.Write(); - - // Only need to compute this once - mfem::MFEM_FORALL(iqpts, m_npts, { - // for(size_t iqpts = 0; iqpts < m_npts; iqpts++) { - const auto dpeff = &history_data[iqpts * vdim + dpeff_offset]; - const auto gdots = &history_data[iqpts * vdim + gdot_offset]; - auto lattice_tayfac_dpeff = &lattice_tayfac_dpeffs[iqpts * 2]; - double abs_gdot = 0.0; - for (size_t islip = 0; islip < gdot_length; islip++) { - abs_gdot += fabs(gdots[islip]); - } - lattice_tayfac_dpeff[0] = (fabs(*dpeff) <= 1.0e-14) ? 0.0 : (abs_gdot / *dpeff); - lattice_tayfac_dpeff[1] = *dpeff; - }); - - for (const auto& in_fiber_hkl : m_in_fibers){ - mfem::Vector lattice_tayfac_dpeff_hkl(2); - [[maybe_unused]] double _ = exaconstit::kernel::ComputeVolAvgTensorFilter(m_pfes, &m_workspace, &in_fiber_hkl, lattice_tayfac_dpeff_hkl, 2, m_class_device); - lattice_tay_facs.push_back(lattice_tayfac_dpeff_hkl(0)); - lattice_dpeff.push_back(lattice_tayfac_dpeff_hkl(1)); - } -} - - -template -void -LightUp::calc_lattice_directional_stiffness(const mfem::QuadratureFunction& history, - const mfem::QuadratureFunction& stress, - const size_t strain_offset, - const size_t quats_offset, - const size_t rel_vol_offset, - std::vector> &lattice_dir_stiff) -{ - - const size_t vdim = history.GetVDim(); - const auto history_data = history.Read(); - const auto stress_data = stress.Read(); - m_workspace = 0.0; - auto lattice_directional_stiffness = m_workspace.Write(); - - // Only need to compute this once - mfem::MFEM_FORALL(iqpts, m_npts, { - // for(size_t iqpts = 0; iqpts < m_npts; iqpts++) { - const auto strain_lat = &history_data[iqpts * vdim + strain_offset]; - const auto quats = &history_data[iqpts * vdim + quats_offset]; - const auto rel_vol = history_data[iqpts * vdim + rel_vol_offset]; - const auto stress_l = &stress_data[iqpts * 6]; - auto lds = &lattice_directional_stiffness[iqpts * 3]; - - double strain[6] = {}; - { - double strainm[3 * 3] = {}; - double* strain_m[3] = {&strainm[0], &strainm[3], &strainm[6]}; - const double t1 = ecmech::sqr2i * strain_lat[0]; - const double t2 = ecmech::sqr6i * strain_lat[1]; - // - // Volume strain is ln(V^e_mean) term aka ln(relative volume) - // Our plastic deformation has a det(1) aka no change in volume change - const double elas_vol_strain = log(rel_vol); - // We output elastic strain formulation such that the relationship - // between V^e and \varepsilon is just V^e = I + \varepsilon - strain_m[0][0] = (t1 - t2) + elas_vol_strain; // 11 - strain_m[1][1] = (-t1 - t2) + elas_vol_strain ; // 22 - strain_m[2][2] = ecmech::sqr2b3 * strain_lat[1] + elas_vol_strain; // 33 - strain_m[1][2] = ecmech::sqr2i * strain_lat[4]; // 23 - strain_m[2][0] = ecmech::sqr2i * strain_lat[3]; // 31 - strain_m[0][1] = ecmech::sqr2i * strain_lat[2]; // 12 - - strain_m[2][1] = strain_m[1][2]; - strain_m[0][2] = strain_m[2][0]; - strain_m[1][0] = strain_m[0][1]; - - double rmat[3 * 3] = {}; - double strain_samp[3 * 3] = {}; - - quat2rmat(quats, rmat); - - snls::linalg::rotMatrix<3, false>(strainm, rmat, strain_samp); - - strain_m[0] = &strain_samp[0]; - strain_m[1] = &strain_samp[3]; - strain_m[2] = &strain_samp[6]; - - strain[0] = strain_m[0][0]; - strain[1] = strain_m[1][1]; - strain[2] = strain_m[2][2]; - strain[3] = strain_m[1][2]; - strain[4] = strain_m[0][2]; - strain[5] = strain_m[0][1]; - } - - for (size_t ipt = 0; ipt < 3; ipt++) { - lds[ipt] = (fabs(strain[ipt]) < 1e-12) ? 0.0 : (stress_l[ipt] / strain[ipt]); - } - }); - - for (const auto& in_fiber_hkl : m_in_fibers){ - mfem::Vector lattice_direct_stiff(3); - [[maybe_unused]] double _ = exaconstit::kernel::ComputeVolAvgTensorFilter(m_pfes, &m_workspace, &in_fiber_hkl, lattice_direct_stiff, 3, m_class_device); - std::array stiff_tmp; - for (size_t ipt = 0; ipt < 3; ipt++) { - stiff_tmp[ipt] = lattice_direct_stiff(ipt); - } - lattice_dir_stiff.push_back(stiff_tmp); - } -} - -using LightUpCubic = LightUp; \ No newline at end of file diff --git a/src/mechanics_log.hpp b/src/mechanics_log.hpp deleted file mode 100644 index 00b13a6..0000000 --- a/src/mechanics_log.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef MECHANICS_LOG -#define MECHANICS_LOG - -#ifdef HAVE_CALIPER -#include "caliper/cali.h" -#include "caliper/cali-mpi.h" -#define CALI_INIT \ - cali_mpi_init(); \ - cali_init(); -#else -#define CALI_CXX_MARK_FUNCTION -#define CALI_MARK_BEGIN(name) -#define CALI_MARK_END(name) -#define CALI_CXX_MARK_SCOPE(name) -#define CALI_INIT -#endif - -#endif \ No newline at end of file diff --git a/src/mechanics_model.cpp b/src/mechanics_model.cpp deleted file mode 100644 index bd481c4..0000000 --- a/src/mechanics_model.cpp +++ /dev/null @@ -1,1061 +0,0 @@ -#include "mfem.hpp" -#include "mfem/general/forall.hpp" -#include "mechanics_model.hpp" -#include "mechanics_log.hpp" -#include "BCManager.hpp" -#include // log -#include -#include // cerr -#include "RAJA/RAJA.hpp" - -using namespace mfem; -using namespace std; - -void computeDefGrad(QuadratureFunction *qf, ParFiniteElementSpace *fes, - Vector &x0) -{ - const FiniteElement *fe; - const IntegrationRule *ir; - double* qf_data = qf->ReadWrite(); - int qf_offset = qf->GetVDim(); // offset at each integration point - QuadratureSpaceBase* qspace = qf->GetSpace(); - - ParGridFunction x_gf; - - double* vals = x0.ReadWrite(); - - const int NE = fes->GetNE(); - - x_gf.MakeTRef(fes, vals); - x_gf.SetFromTrueVector(); - - - // loop over elements - for (int i = 0; i < NE; ++i) { - // get element transformation for the ith element - ElementTransformation* Ttr = fes->GetElementTransformation(i); - fe = fes->GetFE(i); - - // declare data to store shape function gradients - // and element Jacobians - DenseMatrix Jrt, DSh, DS, PMatI, Jpt, F0, F1; - int dof = fe->GetDof(), dim = fe->GetDim(); - - if (qf_offset != (dim * dim)) { - mfem_error("computeDefGrd0 stride input arg not dim*dim"); - } - - DSh.SetSize(dof, dim); - DS.SetSize(dof, dim); - Jrt.SetSize(dim); - Jpt.SetSize(dim); - F0.SetSize(dim); - F1.SetSize(dim); - PMatI.SetSize(dof, dim); - - // get element physical coordinates - // Array vdofs; - // Vector el_x; - // fes->GetElementVDofs(i, vdofs); - // x0.GetSubVector(vdofs, el_x); - // PMatI.UseExternalData(el_x.ReadWrite(), dof, dim); - - // get element physical coordinates - Array vdofs(dof * dim); - Vector el_x(PMatI.Data(), dof * dim); - fes->GetElementVDofs(i, vdofs); - - x_gf.GetSubVector(vdofs, el_x); - - ir = &(qspace->GetIntRule(i)); - int elem_offset = qf_offset * ir->GetNPoints(); - - // loop over integration points where the quadrature function is - // stored - for (int j = 0; j < ir->GetNPoints(); ++j) { - const IntegrationPoint &ip = ir->IntPoint(j); - Ttr->SetIntPoint(&ip); - CalcInverse(Ttr->Jacobian(), Jrt); - - fe->CalcDShape(ip, DSh); - Mult(DSh, Jrt, DS); - MultAtB(PMatI, DS, Jpt); - - // store local beginning step deformation gradient for a given - // element and integration point from the quadrature function - // input argument. We want to set the new updated beginning - // step deformation gradient (prior to next time step) to the current - // end step deformation gradient associated with the converged - // incremental solution. The converged _incremental_ def grad is Jpt - // that we just computed above. We compute the updated beginning - // step def grad as F1 = Jpt*F0; F0 = F1; We do this because we - // are not storing F1. - int k = 0; - for (int n = 0; n < dim; ++n) { - for (int m = 0; m < dim; ++m) { - F0(m, n) = qf_data[i * elem_offset + j * qf_offset + k]; - ++k; - } - } - - // compute F1 = Jpt*F0; - Mult(Jpt, F0, F1); - - // set new F0 = F1 - F0 = F1; - - // loop over element Jacobian data and populate - // quadrature function with the new F0 in preparation for the next - // time step. Note: offset0 should be the - // number of true state variables. - k = 0; - for (int m = 0; m < dim; ++m) { - for (int n = 0; n < dim; ++n) { - qf_data[i * elem_offset + j * qf_offset + k] = - F0(n, m); - ++k; - } - } - } - - Ttr = NULL; - } - - fe = NULL; - ir = NULL; - qf_data = NULL; - qspace = NULL; - - return; -} - -ExaModel::ExaModel(mfem::QuadratureFunction *q_stress0, mfem::QuadratureFunction *q_stress1, - mfem::QuadratureFunction *q_matGrad, mfem::QuadratureFunction *q_matVars0, - mfem::QuadratureFunction *q_matVars1, - mfem::ParGridFunction* _beg_coords, mfem::ParGridFunction* _end_coords, - mfem::Vector *props, int nProps, int nStateVars, Assembly _assembly) : - numProps(nProps), numStateVars(nStateVars), - beg_coords(_beg_coords), - end_coords(_end_coords), - stress0(q_stress0), - stress1(q_stress1), - matGrad(q_matGrad), - matVars0(q_matVars0), - matVars1(q_matVars1), - matProps(props), - assembly(_assembly) - { - if (assembly == Assembly::PA) { - int npts = q_matGrad->Size() / q_matGrad->GetVDim(); - matGradPA.SetSize(81 * npts, mfem::Device::GetMemoryType()); - matGradPA.UseDevice(true); - } - } - -// This method sets the end time step stress to the beginning step -// and then returns the internal data pointer of the end time step -// array. -double* ExaModel::StressSetup() -{ - const double *stress_beg = stress0->Read(); - double *stress_end = stress1->ReadWrite(); - const int N = stress0->Size(); - MFEM_FORALL(i, N, stress_end[i] = stress_beg[i]; ); - - return stress_end; -} - -// This methods set the end time step state variable array to the -// beginning time step values and then returns the internal data pointer -// of the end time step array. -double* ExaModel::StateVarsSetup() -{ - const double *state_vars_beg = matVars0->Read(); - double *state_vars_end = matVars1->ReadWrite(); - - const int N = matVars0->Size(); - MFEM_FORALL(i, N, state_vars_end[i] = state_vars_beg[i]; ); - - return state_vars_end; -} - -// the getter simply returns the beginning step stress -void ExaModel::GetElementStress(const int elID, const int ipNum, - bool beginStep, double* stress, int numComps) -{ - const IntegrationRule *ir = NULL; - double* qf_data = NULL; - int qf_offset = 0; - QuadratureFunction* qf = NULL; - QuadratureSpaceBase* qspace = NULL; - - if (beginStep) { - qf = stress0; - } - else { - qf = stress1; - } - - qf_data = qf->HostReadWrite(); - qf_offset = qf->GetVDim(); - qspace = qf->GetSpace(); - - // check offset to input number of components - if (qf_offset != numComps) { - cerr << "\nGetElementStress: number of components does not match quad func offset" - << endl; - } - - ir = &(qspace->GetIntRule(elID)); - int elem_offset = qf_offset * ir->GetNPoints(); - - for (int i = 0; iHostReadWrite(); - qf_offset = qf->GetVDim(); - qspace = qf->GetSpace(); - - // check offset to input number of components - if (qf_offset != numComps) { - cerr << "\nSetElementStress: number of components does not match quad func offset" - << endl; - } - - ir = &(qspace->GetIntRule(elID)); - int elem_offset = qf_offset * ir->GetNPoints(); - - for (int i = 0; iReadWrite(); - qf_offset = qf->GetVDim(); - qspace = qf->GetSpace(); - - // check offset to input number of components - if (qf_offset != numComps) { - cerr << "\nGetElementStateVars: num. components does not match quad func offset" - << endl; - } - - ir = &(qspace->GetIntRule(elID)); - int elem_offset = qf_offset * ir->GetNPoints(); - - for (int i = 0; iReadWrite(); - qf_offset = qf->GetVDim(); - qspace = qf->GetSpace(); - - // check offset to input number of components - if (qf_offset != numComps) { - cerr << "\nSetElementStateVars: num. components does not match quad func offset" - << endl; - } - - ir = &(qspace->GetIntRule(elID)); - int elem_offset = qf_offset * ir->GetNPoints(); - - for (int i = 0; iHostReadWrite(); - qf_offset = qf->GetVDim(); - qspace = qf->GetSpace(); - - // check offset to input number of components - if (qf_offset != numComps) { - cerr << "\nGetElementMatGrad: num. components does not match quad func offset" - << endl; - } - - ir = &(qspace->GetIntRule(elID)); - int elem_offset = qf_offset * ir->GetNPoints(); - - for (int i = 0; iReadWrite(); - qf_offset = qf->GetVDim(); - qspace = qf->GetSpace(); - - // check offset to input number of components - if (qf_offset != numComps) { - cerr << "\nSetElementMatGrad: num. components does not match quad func offset" - << endl; - } - - ir = &(qspace->GetIntRule(elID)); - int elem_offset = qf_offset * ir->GetNPoints(); - - for (int i = 0; iReadWrite(); - for (int i = 0; i < matProps->Size(); i++) { - props[i] = mpdata[i]; - } - - return; -} - -void ExaModel::SetMatProps(double* props, int size) -{ - matProps->NewDataAndSize(props, size); - return; -} - -void ExaModel::UpdateStress() -{ - stress0->Swap(*stress1); -} - -void ExaModel::UpdateStateVars() -{ - matVars0->Swap(*matVars1); -} - -void ExaModel::UpdateEndCoords(const Vector& vels) -{ - int size; - - size = vels.Size(); - - Vector end_crds(size); - - end_crds = 0.0; - - // tdofs sounds like it should hold the data points of interest, since the GetTrueDofs() - // points to the underlying data in the GridFunction if all the TDofs lie on a processor - Vector bcrds; - bcrds.SetSize(size); - // beg_coords is the beginning time step coordinates - beg_coords->GetTrueDofs(bcrds); - int size2 = bcrds.Size(); - - if (size != size2) { - mfem_error("TrueDofs and Vel Solution vector sizes are different"); - } - - const double* bcrd = bcrds.Read(); - const double* vel = vels.Read(); - double* end_crd = end_crds.ReadWrite(); - const double dt_ = this->dt; - // Perform a simple time integration to get our new end time step coordinates - MFEM_FORALL(i, size, { - end_crd[i] = vel[i] * dt_ + bcrd[i]; - }); - - // Now make sure the update gets sent to all the other processors that have ghost copies - // of our data. - end_coords->Distribute(end_crds); - - return; -} - -// A helper function that takes in a 3x3 rotation matrix and converts it over -// to a unit quaternion. -// rmat should be constant here... -void ExaModel::RMat2Quat(const DenseMatrix& rmat, Vector& quat) -{ - double inv2 = 1.0 / 2.0; - double phi = 0.0; - static const double eps = numeric_limits::epsilon(); - double tr_r = 0.0; - double inv_sin = 0.0; - double s = 0.0; - - - quat = 0.0; - - tr_r = rmat(0, 0) + rmat(1, 1) + rmat(2, 2); - phi = inv2 * (tr_r - 1.0); - phi = min(phi, 1.0); - phi = max(phi, -1.0); - phi = acos(phi); - if (abs(phi) < eps) { - quat[3] = 1.0; - } - else { - inv_sin = 1.0 / sin(phi); - quat[0] = phi; - quat[1] = inv_sin * inv2 * (rmat(2, 1) - rmat(1, 2)); - quat[2] = inv_sin * inv2 * (rmat(0, 2) - rmat(2, 0)); - quat[3] = inv_sin * inv2 * (rmat(1, 0) - rmat(0, 1)); - } - - s = sin(inv2 * quat[0]); - quat[0] = cos(quat[0] * inv2); - quat[1] = s * quat[1]; - quat[2] = s * quat[2]; - quat[3] = s * quat[3]; - - return; -} - -// A helper function that takes in a unit quaternion and and returns a 3x3 rotation -// matrix. -void ExaModel::Quat2RMat(const Vector& quat, DenseMatrix& rmat) -{ - double qbar = 0.0; - - qbar = quat[0] * quat[0] - (quat[1] * quat[1] + quat[2] * quat[2] + quat[3] * quat[3]); - - rmat(0, 0) = qbar + 2.0 * quat[1] * quat[1]; - rmat(1, 0) = 2.0 * (quat[1] * quat[2] + quat[0] * quat[3]); - rmat(2, 0) = 2.0 * (quat[1] * quat[3] - quat[0] * quat[2]); - - rmat(0, 1) = 2.0 * (quat[1] * quat[2] - quat[0] * quat[3]); - rmat(1, 1) = qbar + 2.0 * quat[2] * quat[2]; - rmat(2, 1) = 2.0 * (quat[2] * quat[3] + quat[0] * quat[1]); - - rmat(0, 2) = 2.0 * (quat[1] * quat[3] + quat[0] * quat[2]); - rmat(1, 2) = 2.0 * (quat[2] * quat[3] - quat[0] * quat[1]); - rmat(2, 2) = qbar + 2.0 * quat[3] * quat[3]; - - return; -} - -// The below method computes the polar decomposition of a 3x3 matrix using a method -// proposed in: https://animation.rwth-aachen.de/media/papers/2016-MIG-StableRotation.pdf -// The paper listed provides a fast and robust way to obtain the rotation portion -// of a positive definite 3x3 matrix which then allows for the easy computation -// of U and V. -void ExaModel::CalcPolarDecompDefGrad(DenseMatrix& R, DenseMatrix& U, - DenseMatrix& V, double err) -{ - DenseMatrix omega_mat, temp; - DenseMatrix def_grad(R, 3); - - int dim = 3; - Vector quat; - - int max_iter = 500; - - double norm, inv_norm; - - double ac1[3], ac2[3], ac3[3]; - double w_top[3], w[3]; - double w_bot, w_norm, w_norm_inv2, w_norm_inv; - double cth, sth; - double r1da1, r2da2, r3da3; - - quat.SetSize(4); - omega_mat.SetSize(dim); - temp.SetSize(dim); - - quat = 0.0; - - RMat2Quat(def_grad, quat); - - norm = quat.Norml2(); - - inv_norm = 1.0 / norm; - - quat *= inv_norm; - - Quat2RMat(quat, R); - - ac1[0] = def_grad(0, 0); ac1[1] = def_grad(1, 0); ac1[2] = def_grad(2, 0); - ac2[0] = def_grad(0, 1); ac2[1] = def_grad(1, 1); ac2[2] = def_grad(2, 1); - ac3[0] = def_grad(0, 2); ac3[1] = def_grad(1, 2); ac3[2] = def_grad(2, 2); - - for (int i = 0; i < max_iter; i++) { - // The dot products that show up in the paper - r1da1 = R(0, 0) * ac1[0] + R(1, 0) * ac1[1] + R(2, 0) * ac1[2]; - r2da2 = R(0, 1) * ac2[0] + R(1, 1) * ac2[1] + R(2, 1) * ac2[2]; - r3da3 = R(0, 2) * ac3[0] + R(1, 2) * ac3[1] + R(2, 2) * ac3[2]; - - // The summed cross products that show up in the paper - w_top[0] = (-R(2, 0) * ac1[1] + R(1, 0) * ac1[2]) + - (-R(2, 1) * ac2[1] + R(1, 1) * ac2[2]) + - (-R(2, 2) * ac3[1] + R(1, 2) * ac3[2]); - - w_top[1] = (R(2, 0) * ac1[0] - R(0, 0) * ac1[2]) + - (R(2, 1) * ac2[0] - R(0, 1) * ac2[2]) + - (R(2, 2) * ac3[0] - R(0, 2) * ac3[2]); - - w_top[2] = (-R(1, 0) * ac1[0] + R(0, 0) * ac1[1]) + - (-R(1, 1) * ac2[0] + R(0, 1) * ac2[1]) + - (-R(1, 2) * ac3[0] + R(0, 2) * ac3[1]); - - w_bot = (1.0 / (abs(r1da1 + r2da2 + r3da3) + err)); - // The axial vector that shows up in the paper - w[0] = w_top[0] * w_bot; w[1] = w_top[1] * w_bot; w[2] = w_top[2] * w_bot; - // The norm of the axial vector - w_norm = sqrt(w[0] * w[0] + w[1] * w[1] + w[2] * w[2]); - // If the norm is below our desired error we've gotten our solution - // So we can break out of the loop - if (w_norm < err) { - break; - } - // The exponential mapping for an axial vector - // The 3x3 case has been explicitly unrolled here - w_norm_inv2 = 1.0 / (w_norm * w_norm); - w_norm_inv = 1.0 / w_norm; - - sth = sin(w_norm) * w_norm_inv; - cth = (1.0 - cos(w_norm)) * w_norm_inv2; - - omega_mat(0, 0) = 1.0 - cth * (w[2] * w[2] + w[1] * w[1]); - omega_mat(1, 1) = 1.0 - cth * (w[2] * w[2] + w[0] * w[0]); - omega_mat(2, 2) = 1.0 - cth * (w[1] * w[1] + w[0] * w[0]); - - omega_mat(0, 1) = -sth * w[2] + cth * w[1] * w[0]; - omega_mat(0, 2) = sth * w[1] + cth * w[2] * w[0]; - - omega_mat(1, 0) = sth * w[2] + cth * w[0] * w[1]; - omega_mat(1, 2) = -sth * w[0] + cth * w[2] * w[1]; - - omega_mat(2, 0) = -sth * w[1] + cth * w[0] * w[2]; - omega_mat(2, 1) = sth * w[0] + cth * w[2] * w[1]; - - Mult(omega_mat, R, temp); - R = temp; - } - - // Now that we have the rotation portion of our deformation gradient - // the left and right stretch tensors are easy to find. - MultAtB(R, def_grad, U); - MultABt(def_grad, R, V); - - return; -} - -// This method calculates the Eulerian strain which is given as: -// e = 1/2 (I - B^(-1)) = 1/2 (I - F(^-T)F^(-1)) -void ExaModel::CalcEulerianStrain(DenseMatrix& E, const DenseMatrix &F) -{ - int dim = 3; - - DenseMatrix Finv(dim), Binv(dim); - - double half = 1.0 / 2.0; - - CalcInverse(F, Finv); - - MultAtB(Finv, Finv, Binv); - - E = 0.0; - - for (int j = 0; j < dim; j++) { - for (int i = 0; i < dim; i++) { - E(i, j) -= half * Binv(i, j); - } - - E(j, j) += half; - } - - return; -} - -// This method calculates the Lagrangian strain which is given as: -// E = 1/2 (C - I) = 1/2 (F^(T)F - I) -void ExaModel::CalcLagrangianStrain(DenseMatrix& E, const DenseMatrix &F) -{ - int dim = 3; - - // DenseMatrix F(Jpt, dim); - DenseMatrix C(dim); - - double half = 1.0 / 2.0; - - MultAtB(F, F, C); - - E = 0.0; - - for (int j = 0; j < dim; j++) { - for (int i = 0; i < dim; i++) { - E(i, j) += half * C(i, j); - } - - E(j, j) -= half; - } - - return; -} - -// This method calculates the Biot strain which is given as: -// E = (U - I) or sometimes seen as E = (V - I) if R = I -void ExaModel::CalcBiotStrain(DenseMatrix& E, const DenseMatrix &F) -{ - int dim = 3; - - DenseMatrix rmat(F, dim); - DenseMatrix umat, vmat; - - umat.SetSize(dim); - vmat.SetSize(dim); - - CalcPolarDecompDefGrad(rmat, umat, vmat); - - E = umat; - E(0, 0) -= 1.0; - E(1, 1) -= 1.0; - E(2, 2) -= 1.0; - - return; -} - -void ExaModel::CalcLogStrain(DenseMatrix& E, const DenseMatrix &F) -{ - // calculate current end step logorithmic strain (Hencky Strain) - // which is taken to be E = ln(U) = 1/2 ln(C), where C = (F_T)F. - // We have incremental F from MFEM, and store F0 (Jpt0) so - // F = F_hat*F0. With F, use a spectral decomposition on C to obtain a - // form where we only have to take the natural log of the - // eigenvalues - // UMAT uses the E = ln(V) approach instead - - DenseMatrix B; - - constexpr int dim = 3; - - B.SetSize(dim); - // F.SetSize(dim); - - // F = Jpt; - - MultABt(F, F, B); - - // compute eigenvalue decomposition of B - double lambda[dim]; - double vec[dim * dim]; - // fix_me: was failing - B.CalcEigenvalues(&lambda[0], &vec[0]); - - // compute ln(V) using spectral representation - E = 0.0; - for (int i = 0; iSize() / matGrad->GetVDim(); - - const int dim = 3; - const int dim2 = 6; - - const int DIM5 = 5; - const int DIM3 = 3; - std::array perm5 {{ 4, 3, 2, 1, 0 } }; - std::array perm3 {{ 2, 1, 0 } }; - - // bunch of helper RAJA views to make dealing with data easier down below in our kernel. - RAJA::Layout layout_4Dtensor = RAJA::make_permuted_layout({{ dim, dim, dim, dim, npts } }, perm5); - RAJA::View > cmat_4d(matGradPA.ReadWrite(), layout_4Dtensor); - - // bunch of helper RAJA views to make dealing with data easier down below in our kernel. - RAJA::Layout layout_2Dtensor = RAJA::make_permuted_layout({{ dim2, dim2, npts } }, perm3); - RAJA::View > cmat(matGrad->Read(), layout_2Dtensor); - - // This sets up our 4D tensor to be the same as the 2D tensor which takes advantage of symmetry operations - MFEM_FORALL(i, npts, { - cmat_4d(0, 0, 0, 0, i) = cmat(0, 0, i); - cmat_4d(1, 1, 0, 0, i) = cmat(1, 0, i); - cmat_4d(2, 2, 0, 0, i) = cmat(2, 0, i); - cmat_4d(1, 2, 0, 0, i) = cmat(3, 0, i); - cmat_4d(2, 1, 0, 0, i) = cmat_4d(1, 2, 0, 0, i); - cmat_4d(2, 0, 0, 0, i) = cmat(4, 0, i); - cmat_4d(0, 2, 0, 0, i) = cmat_4d(2, 0, 0, 0, i); - cmat_4d(0, 1, 0, 0, i) = cmat(5, 0, i); - cmat_4d(1, 0, 0, 0, i) = cmat_4d(0, 1, 0, 0, i); - - cmat_4d(0, 0, 1, 1, i) = cmat(0, 1, i); - cmat_4d(1, 1, 1, 1, i) = cmat(1, 1, i); - cmat_4d(2, 2, 1, 1, i) = cmat(2, 1, i); - cmat_4d(1, 2, 1, 1, i) = cmat(3, 1, i); - cmat_4d(2, 1, 1, 1, i) = cmat_4d(1, 2, 1, 1, i); - cmat_4d(2, 0, 1, 1, i) = cmat(4, 1, i); - cmat_4d(0, 2, 1, 1, i) = cmat_4d(2, 0, 1, 1, i); - cmat_4d(0, 1, 1, 1, i) = cmat(5, 1, i); - cmat_4d(1, 0, 1, 1, i) = cmat_4d(0, 1, 1, 1, i); - - cmat_4d(0, 0, 2, 2, i) = cmat(0, 2, i); - cmat_4d(1, 1, 2, 2, i) = cmat(1, 2, i); - cmat_4d(2, 2, 2, 2, i) = cmat(2, 2, i); - cmat_4d(1, 2, 2, 2, i) = cmat(3, 2, i); - cmat_4d(2, 1, 2, 2, i) = cmat_4d(1, 2, 2, 2, i); - cmat_4d(2, 0, 2, 2, i) = cmat(4, 2, i); - cmat_4d(0, 2, 2, 2, i) = cmat_4d(2, 0, 2, 2, i); - cmat_4d(0, 1, 2, 2, i) = cmat(5, 2, i); - cmat_4d(1, 0, 2, 2, i) = cmat_4d(0, 1, 2, 2, i); - - cmat_4d(0, 0, 1, 2, i) = cmat(0, 3, i); - cmat_4d(1, 1, 1, 2, i) = cmat(1, 3, i); - cmat_4d(2, 2, 1, 2, i) = cmat(2, 3, i); - cmat_4d(1, 2, 1, 2, i) = cmat(3, 3, i); - cmat_4d(2, 1, 1, 2, i) = cmat_4d(1, 2, 1, 2, i); - cmat_4d(2, 0, 1, 2, i) = cmat(4, 3, i); - cmat_4d(0, 2, 1, 2, i) = cmat_4d(2, 0, 1, 2, i); - cmat_4d(0, 1, 1, 2, i) = cmat(5, 3, i); - cmat_4d(1, 0, 1, 2, i) = cmat_4d(0, 1, 1, 2, i); - - cmat_4d(0, 0, 2, 1, i) = cmat(0, 3, i); - cmat_4d(1, 1, 2, 1, i) = cmat(1, 3, i); - cmat_4d(2, 2, 2, 1, i) = cmat(2, 3, i); - cmat_4d(1, 2, 2, 1, i) = cmat(3, 3, i); - cmat_4d(2, 1, 2, 1, i) = cmat_4d(1, 2, 1, 2, i); - cmat_4d(2, 0, 2, 1, i) = cmat(4, 3, i); - cmat_4d(0, 2, 2, 1, i) = cmat_4d(2, 0, 1, 2, i); - cmat_4d(0, 1, 2, 1, i) = cmat(5, 3, i); - cmat_4d(1, 0, 2, 1, i) = cmat_4d(0, 1, 1, 2, i); - - cmat_4d(0, 0, 2, 0, i) = cmat(0, 4, i); - cmat_4d(1, 1, 2, 0, i) = cmat(1, 4, i); - cmat_4d(2, 2, 2, 0, i) = cmat(2, 4, i); - cmat_4d(1, 2, 2, 0, i) = cmat(3, 4, i); - cmat_4d(2, 1, 2, 0, i) = cmat_4d(1, 2, 2, 0, i); - cmat_4d(2, 0, 2, 0, i) = cmat(4, 4, i); - cmat_4d(0, 2, 2, 0, i) = cmat_4d(2, 0, 2, 0, i); - cmat_4d(0, 1, 2, 0, i) = cmat(5, 4, i); - cmat_4d(1, 0, 2, 0, i) = cmat_4d(0, 1, 2, 0, i); - - cmat_4d(0, 0, 0, 2, i) = cmat(0, 4, i); - cmat_4d(1, 1, 0, 2, i) = cmat(1, 4, i); - cmat_4d(2, 2, 0, 2, i) = cmat(2, 4, i); - cmat_4d(1, 2, 0, 2, i) = cmat(3, 4, i); - cmat_4d(2, 1, 0, 2, i) = cmat_4d(1, 2, 2, 0, i); - cmat_4d(2, 0, 0, 2, i) = cmat(4, 4, i); - cmat_4d(0, 2, 0, 2, i) = cmat_4d(2, 0, 2, 0, i); - cmat_4d(0, 1, 0, 2, i) = cmat(5, 4, i); - cmat_4d(1, 0, 0, 2, i) = cmat_4d(0, 1, 2, 0, i); - - cmat_4d(0, 0, 0, 1, i) = cmat(0, 5, i); - cmat_4d(1, 1, 0, 1, i) = cmat(1, 5, i); - cmat_4d(2, 2, 0, 1, i) = cmat(2, 5, i); - cmat_4d(1, 2, 0, 1, i) = cmat(3, 5, i); - cmat_4d(2, 1, 0, 1, i) = cmat_4d(1, 2, 0, 1, i); - cmat_4d(2, 0, 0, 1, i) = cmat(4, 5, i); - cmat_4d(0, 2, 0, 1, i) = cmat_4d(2, 0, 0, 1, i); - cmat_4d(0, 1, 0, 1, i) = cmat(5, 5, i); - cmat_4d(1, 0, 0, 1, i) = cmat_4d(0, 1, 0, 1, i); - - cmat_4d(0, 0, 1, 0, i) = cmat(0, 5, i); - cmat_4d(1, 1, 1, 0, i) = cmat(1, 5, i); - cmat_4d(2, 2, 1, 0, i) = cmat(2, 5, i); - cmat_4d(1, 2, 1, 0, i) = cmat(3, 5, i); - cmat_4d(2, 1, 1, 0, i) = cmat_4d(1, 2, 0, 1, i); - cmat_4d(2, 0, 1, 0, i) = cmat(4, 5, i); - cmat_4d(0, 2, 1, 0, i) = cmat_4d(2, 0, 0, 1, i); - cmat_4d(0, 1, 1, 0, i) = cmat(5, 5, i); - cmat_4d(1, 0, 1, 0, i) = cmat_4d(0, 1, 0, 1, i); - }); -} diff --git a/src/mechanics_model.hpp b/src/mechanics_model.hpp deleted file mode 100644 index 631b872..0000000 --- a/src/mechanics_model.hpp +++ /dev/null @@ -1,243 +0,0 @@ -#ifndef MECHANICS_MODEL -#define MECHANICS_MODEL - -#include "option_types.hpp" - -#include "mfem.hpp" - -#include -#include -#include - -/// free function to compute the beginning step deformation gradient to store -/// on a quadrature function -void computeDefGrad(mfem::QuadratureFunction *qf, mfem::ParFiniteElementSpace *fes, - mfem::Vector &x0); - -class ExaModel -{ - public: - int numProps; - int numStateVars; - bool init_step = false; - - protected: - - double dt, t; - - // -------------------------------------------------------------------------- - // The velocity method requires us to retain both the beggining and end time step - // coordinates of the mesh. We need these to be able to compute the correct - // incremental deformation gradient (using the beg. time step coords) and the - // velocity gradient (uses the end time step coords). - - mfem::ParGridFunction* beg_coords; - mfem::ParGridFunction* end_coords; - - // --------------------------------------------------------------------------- - // STATE VARIABLES and PROPS common to all user defined models - - // The beginning step stress and the end step (or incrementally upated) stress - mfem::QuadratureFunction *stress0; - mfem::QuadratureFunction *stress1; - - // The updated material tangent stiffness matrix, which will need to be - // stored after an EvalP call and used in a later AssembleH call - mfem::QuadratureFunction *matGrad; - - // quadrature vector function coefficients for any history variables at the - // beginning of the step and end (or incrementally updated) step. - mfem::QuadratureFunction *matVars0; - mfem::QuadratureFunction *matVars1; - - // Stores the von Mises / hydrostatic scalar stress measure - // we use this array to compute both the hydro and von Mises stress quantities - mfem::QuadratureFunction *vonMises; - - // add vector for material properties, which will be populated based on the - // requirements of the user defined model. The properties are expected to be - // the same at all quadrature points. That is, the material properties are - // constant and not dependent on space - mfem::Vector *matProps; - Assembly assembly; - // Temporary fix just to make sure things work - mfem::Vector matGradPA; - - std::unordered_map > qf_mapping; - // --------------------------------------------------------------------------- - - public: - ExaModel(mfem::QuadratureFunction *q_stress0, mfem::QuadratureFunction *q_stress1, - mfem::QuadratureFunction *q_matGrad, mfem::QuadratureFunction *q_matVars0, - mfem::QuadratureFunction *q_matVars1, - mfem::ParGridFunction* _beg_coords, mfem::ParGridFunction* _end_coords, - mfem::Vector *props, int nProps, int nStateVars, Assembly _assembly); - - virtual ~ExaModel() { } - - /// This function is used in generating the B matrix commonly seen in the formation of - /// the material tangent stiffness matrix in mechanics [B^t][Cstiff][B] - virtual void GenerateGradMatrix(const mfem::DenseMatrix& DS, mfem::DenseMatrix& B); - - /// This function is used in generating the Bbar matrix seen in the formation of - /// the material tangent stiffness matrix in mechanics [B^t][Cstiff][B] for - /// incompressible materials - virtual void GenerateGradBarMatrix(const mfem::DenseMatrix& DS, const mfem::DenseMatrix& eDS, mfem::DenseMatrix& B); - - /// This function is used in generating the B matrix that's used in the formation - /// of the geometric stiffness contribution of the stiffness matrix seen in mechanics - /// as [B^t][sigma][B] - virtual void GenerateGradGeomMatrix(const mfem::DenseMatrix& DS, mfem::DenseMatrix& Bgeom); - - /** @brief This function is responsible for running the entire model and will be the - * external function that other classes/people can call. - * - * It will consist of 3 stages/kernels: - * 1.) A set-up kernel/stage that computes all of the needed values for the material model - * 2.) A kernel that runs the material model (an t = 0 version of this will exist as well) - * 3.) A post-processing kernel/stage that does everything after the kernel - * e.g. All of the data is put back into the correct format here and re-arranged as needed - * By having this function, we only need to ever right one integrator for everything. - * It also allows us to run these models on the GPU even if the rest of the assembly operation - * can't be there yet. If UMATs are used then these operations won't occur on the GPU. - * - * We'll need to supply the number of quadrature pts, number of elements, the dimension - * of the space we're working with, the number of nodes for an element, the jacobian associated - * with the transformation from the reference element to the local element, the quadrature integration wts, - * and the velocity field at the elemental level (space_dim * nnodes * nelems). - */ - virtual void ModelSetup(const int nqpts, const int nelems, const int space_dim, - const int nnodes, const mfem::Vector &jacobian, - const mfem::Vector &loc_grad, const mfem::Vector &vel) = 0; - - /// routine to update the beginning step deformation gradient. This must - /// be written by a model class extension to update whatever else - /// may be required for that particular model - virtual void UpdateModelVars() = 0; - - /// set time on the base model class - void SetModelTime(const double time) { t = time; } - - /// set delta timestep on the base model class - void SetModelDt(const double dtime) { dt = dtime; } - - /// Get delta timestep on the base model class - double GetModelDt() { return dt; } - - /// return a pointer to beginning step stress. This is used for output visualization - mfem::QuadratureFunction *GetStress0() { return stress0; } - - /// return a pointer to beginning step stress. This is used for output visualization - mfem::QuadratureFunction *GetStress1() { return stress1; } - - /// function to set the internal von Mises QuadratureFuntion pointer to some - /// outside source - void setVonMisesPtr(mfem::QuadratureFunction* vm_ptr) { vonMises = vm_ptr; } - - /// return a pointer to von Mises stress quadrature function for visualization - mfem::QuadratureFunction *GetVonMises() { return vonMises; } - - /// return a pointer to the matVars0 quadrature function - mfem::QuadratureFunction *GetMatVars0() { return matVars0; } - - /// return a pointer to the matGrad quadrature function - mfem::QuadratureFunction *GetMatGrad() { return matGrad; } - - /// return a pointer to the matProps vector - mfem::Vector *GetMatProps() { return matProps; } - - /// routine to get element stress at ip point. These are the six components of - /// the symmetric Cauchy stress where standard Voigt notation is being used - void GetElementStress(const int elID, const int ipNum, bool beginStep, - double* stress, int numComps); - - /// set the components of the member function end stress quadrature function with - /// the updated stress - void SetElementStress(const int elID, const int ipNum, bool beginStep, - double* stress, int numComps); - - /// routine to get the element statevars at ip point. - void GetElementStateVars(const int elID, const int ipNum, bool beginStep, - double* stateVars, int numComps); - - /// routine to set the element statevars at ip point - void SetElementStateVars(const int elID, const int ipNum, bool beginStep, - double* stateVars, int numComps); - - /// routine to get the material properties data from the decorated mfem vector - void GetMatProps(double* props); - - /// setter for the material properties data on the user defined model object - void SetMatProps(double* props, int size); - - /// routine to set the material Jacobian for this element and integration point. - void SetElementMatGrad(const int elID, const int ipNum, double* grad, int numComps); - - /// routine to get the material Jacobian for this element and integration point - void GetElementMatGrad(const int elId, const int ipNum, double* grad, int numComps); - - /// routine to update beginning step stress with end step values - void UpdateStress(); - - /// routine to update beginning step state variables with end step values - void UpdateStateVars(); - - /// Update the End Coordinates using a simple Forward Euler Integration scheme - /// The beggining time step coordinates should be updated outside of the model routines - void UpdateEndCoords(const mfem::Vector& vel); - - /// This method performs a fast approximate polar decomposition for 3x3 matrices - /// The deformation gradient or 3x3 matrix of interest to be decomposed is passed - /// in as the initial R matrix. The error on the solution can be set by the user. - void CalcPolarDecompDefGrad(mfem::DenseMatrix& R, mfem::DenseMatrix& U, - mfem::DenseMatrix& V, double err = 1e-12); - - /// Lagrangian is simply E = 1/2(F^tF - I) - void CalcLagrangianStrain(mfem::DenseMatrix& E, const mfem::DenseMatrix &F); - - /// Eulerian is simply e = 1/2(I - F^(-t)F^(-1)) - void CalcEulerianStrain(mfem::DenseMatrix& E, const mfem::DenseMatrix &F); - - /// Biot strain is simply B = U - I - void CalcBiotStrain(mfem::DenseMatrix& E, const mfem::DenseMatrix &F); - - /// Log strain is equal to e = 1/2 * ln(C) or for UMATs its e = 1/2 * ln(B) - void CalcLogStrain(mfem::DenseMatrix& E, const mfem::DenseMatrix &F); - - /// Converts a unit quaternion over to rotation matrix - void Quat2RMat(const mfem::Vector& quat, mfem::DenseMatrix& rmat); - - /// Converts a rotation matrix over to a unit quaternion - void RMat2Quat(const mfem::DenseMatrix& rmat, mfem::Vector& quat); - - /// Returns a pointer to our 4D material tangent stiffness tensor - const double *GetMTanData(){ return matGradPA.Read(); } - - /// Converts a normal 2D stiffness tensor into it's equivalent 4D stiffness - /// tensor - void TransformMatGradTo4D(); - - /// This method sets the end time step stress to the beginning step - /// and then returns the internal data pointer of the end time step - /// array. - double* StressSetup(); - - /// This methods set the end time step state variable array to the - /// beginning time step values and then returns the internal data pointer - /// of the end time step array. - double* StateVarsSetup(); - - /// This function calculates the plastic strain rate tensor (D^p) with - /// a DpMat that's a full 3x3 matrix rather than a 6-dim vector just so - /// we can re-use storage from the deformation gradient tensor. - virtual void calcDpMat(mfem::QuadratureFunction &DpMat) const = 0; - - /// Returns an unordered map that maps a given variable name to its - /// its location and length within the state variable variable. - const std::unordered_map > *GetQFMapping() - { - return &qf_mapping; - } -}; - -#endif \ No newline at end of file diff --git a/src/mechanics_operator.cpp b/src/mechanics_operator.cpp deleted file mode 100644 index 0c8ff05..0000000 --- a/src/mechanics_operator.cpp +++ /dev/null @@ -1,478 +0,0 @@ - -#include "mechanics_operator.hpp" -#include "mfem/general/forall.hpp" -#include "mechanics_log.hpp" -#include "mechanics_ecmech.hpp" -#include "mechanics_kernels.hpp" -#include "RAJA/RAJA.hpp" -#include "ECMech_const.h" -#include -#include -#include - -using namespace mfem; - -namespace { - -struct ModelOptions { - mfem::QuadratureFunction *q_stress0; - mfem::QuadratureFunction *q_stress1; - mfem::QuadratureFunction *q_matGrad; - mfem::QuadratureFunction *q_matVars0; - mfem::QuadratureFunction *q_matVars1; - mfem::QuadratureFunction *q_defGrad0; - mfem::ParGridFunction* beg_coords; - mfem::ParGridFunction* end_coords; - mfem::Vector *props; - int nProps; - int nStateVars; - mfem::ParFiniteElementSpace* fes; - double temp_k; - ecmech::ExecutionStrategy accel; - std::string mat_model_name; - Assembly assembly; -}; - -ExaModel* makeMatModelUMAT(const ModelOptions & mod_options) { - ExaModel* matModel = nullptr; - - auto umat = new AbaqusUmatModel( - mod_options.q_stress0, - mod_options.q_stress1, - mod_options.q_matGrad, - mod_options.q_matVars0, - mod_options.q_matVars1, - mod_options.q_defGrad0, - mod_options.beg_coords, - mod_options.end_coords, - mod_options.props, - mod_options.nProps, - mod_options.nStateVars, - mod_options.fes, - mod_options.assembly - ); - matModel = dynamic_cast(umat); - - return matModel; -} - -ExaModel* makeMatModelExaCMech(const ModelOptions & mod_options) { - ExaModel* matModel = nullptr; - - auto ecmech = new ExaCMechModel( - mod_options.q_stress0, - mod_options.q_stress1, - mod_options.q_matGrad, - mod_options.q_matVars0, - mod_options.q_matVars1, - mod_options.beg_coords, - mod_options.end_coords, - mod_options.props, - mod_options.nProps, - mod_options.nStateVars, - mod_options.temp_k, - mod_options.accel, - mod_options.assembly, - mod_options.mat_model_name - ); - matModel = dynamic_cast(ecmech); - return matModel; -} - -ExaModel* makeMatModel(const ExaOptions &sim_options, const ModelOptions & mod_options) { - ExaModel* matModel = nullptr; - - if (sim_options.mech_type == MechType::UMAT) { - matModel = makeMatModelUMAT(mod_options); - } - else if (sim_options.mech_type == MechType::EXACMECH) { - matModel = makeMatModelExaCMech(mod_options); - } - - if (matModel == nullptr) { - MFEM_ABORT("Somehow you managed to ask for a material model that can't be created..."); - } - - return matModel; -} -} - - -NonlinearMechOperator::NonlinearMechOperator(ParFiniteElementSpace &fes, - Array &ess_bdr, - Array2D &ess_bdr_comp, - ExaOptions &options, - QuadratureFunction &q_matVars0, - QuadratureFunction &q_matVars1, - QuadratureFunction &q_sigma0, - QuadratureFunction &q_sigma1, - QuadratureFunction &q_matGrad, - QuadratureFunction &q_kinVars0, - QuadratureFunction &q_vonMises, - ParGridFunction &ref_crds, - ParGridFunction &beg_crds, - ParGridFunction &end_crds, - Vector &matProps, - int nStateVars) - : NonlinearForm(&fes), fe_space(fes), x_ref(ref_crds), x_cur(end_crds), ess_bdr_comps(ess_bdr_comp) -{ - CALI_CXX_MARK_SCOPE("mechop_class_setup"); - Vector * rhs; - rhs = NULL; - - mech_type = options.mech_type; - - // Define the parallel nonlinear form - Hform = new ParNonlinearForm(&fes); - - // Set the essential boundary conditions - Hform->SetEssentialBC(ess_bdr, ess_bdr_comps, rhs); - - // Set the essential boundary conditions that we can store on our class - SetEssentialBC(ess_bdr, ess_bdr_comps, rhs); - - assembly = options.assembly; - - auto mod_options = ModelOptions{}; - mod_options.q_stress0 = &q_sigma0; - mod_options.q_stress1 = &q_sigma1; - mod_options.q_matGrad = &q_matGrad; - mod_options.q_matVars0 = &q_matVars0; - mod_options.q_matVars1 = &q_matVars1; - mod_options.q_defGrad0 = &q_kinVars0; - mod_options.beg_coords = &beg_crds; - mod_options.end_coords = &end_crds; - mod_options.props = &matProps; - mod_options.nProps = options.nProps; - mod_options.nStateVars = nStateVars; - mod_options.fes = &fes; - mod_options.temp_k = options.temp_k; - mod_options.assembly = assembly; - mod_options.mat_model_name = options.shortcut; - - { - mod_options.accel = ecmech::ExecutionStrategy::CPU; - - if (options.rtmodel == RTModel::CPU) { - mod_options.accel = ecmech::ExecutionStrategy::CPU; - } - else if (options.rtmodel == RTModel::OPENMP) { - mod_options.accel = ecmech::ExecutionStrategy::OPENMP; - } - else if (options.rtmodel == RTModel::GPU) { - mod_options.accel = ecmech::ExecutionStrategy::GPU; - } - } - - model = makeMatModel(options, mod_options); - - // Add the user defined integrator - if (options.integ_type == IntegrationType::FULL) { - Hform->AddDomainIntegrator(new ExaNLFIntegrator(dynamic_cast(model))); - } - else if (options.integ_type == IntegrationType::BBAR) { - Hform->AddDomainIntegrator(new ICExaNLFIntegrator(dynamic_cast(model))); - } - - if (assembly == Assembly::PA) { - Hform->SetAssemblyLevel(mfem::AssemblyLevel::PARTIAL, ElementDofOrdering::NATIVE); - diag.SetSize(fe_space.GetTrueVSize(), Device::GetMemoryType()); - diag.UseDevice(true); - diag = 1.0; - prec_oper = new MechOperatorJacobiSmoother(diag, this->GetEssentialTrueDofs()); - } - else if (assembly == Assembly::EA) { - Hform->SetAssemblyLevel(mfem::AssemblyLevel::ELEMENT, ElementDofOrdering::NATIVE); - diag.SetSize(fe_space.GetTrueVSize(), Device::GetMemoryType()); - diag.UseDevice(true); - diag = 1.0; - prec_oper = new MechOperatorJacobiSmoother(diag, this->GetEssentialTrueDofs()); - } - - // So, we're going to originally support non tensor-product type elements originally. - const ElementDofOrdering ordering = ElementDofOrdering::NATIVE; - // const ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC; - elem_restrict_lex = fe_space.GetElementRestriction(ordering); - - el_x.SetSize(elem_restrict_lex->Height(), Device::GetMemoryType()); - el_x.UseDevice(true); - px.SetSize(P->Height(), Device::GetMemoryType()); - px.UseDevice(true); - - { - const FiniteElement &el = *fe_space.GetFE(0); - const int space_dims = el.GetDim(); - const IntegrationRule *ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1));; - - const int nqpts = ir->GetNPoints(); - const int ndofs = el.GetDof(); - const int nelems = fe_space.GetNE(); - - el_jac.SetSize(space_dims * space_dims * nqpts * nelems, Device::GetMemoryType()); - el_jac.UseDevice(true); - - qpts_dshape.SetSize(nqpts * space_dims * ndofs, Device::GetMemoryType()); - qpts_dshape.UseDevice(true); - { - DenseMatrix DSh; - const int offset = ndofs * space_dims; - double *qpts_dshape_data = qpts_dshape.HostReadWrite(); - for (int i = 0; i < nqpts; i++) { - const IntegrationPoint &ip = ir->IntPoint(i); - DSh.UseExternalData(&qpts_dshape_data[offset * i], ndofs, space_dims); - el.CalcDShape(ip, DSh); - } - } - } - - // We'll probably want to eventually add a print settings into our option class that tells us whether - // or not we're going to be printing this. - - model->setVonMisesPtr(&q_vonMises); -} - -const Array &NonlinearMechOperator::GetEssTDofList() -{ - return Hform->GetEssentialTrueDofs(); -} - -ExaModel *NonlinearMechOperator::GetModel() const -{ - return model; -} - -void NonlinearMechOperator::UpdateEssTDofs(const Array &ess_bdr, bool mono_def_flag) -{ - if (mono_def_flag) { - Hform->SetEssentialTrueDofs(ess_bdr); - ess_tdof_list = ess_bdr; - } - else { - // Set the essential boundary conditions - Hform->SetEssentialBC(ess_bdr, ess_bdr_comps, nullptr); - auto tmp = Hform->GetEssentialTrueDofs(); - // Set the essential boundary conditions that we can store on our class - SetEssentialBC(ess_bdr, ess_bdr_comps, nullptr); - } -} - -// compute: y = H(x,p) -void NonlinearMechOperator::Mult(const Vector &k, Vector &y) const -{ - CALI_CXX_MARK_SCOPE("mechop_Mult"); - // We first run a setup step before actually doing anything. - // We'll want to move this outside of Mult() at some given point in time - // and have it live in the NR solver itself or whatever solver - // we're going to be using. - Setup(k); - // We now perform our element vector operation. - if (assembly == Assembly::PA) { - CALI_CXX_MARK_SCOPE("mechop_PA_PreSetup"); - model->TransformMatGradTo4D(); - } - CALI_MARK_BEGIN("mechop_mult_setup"); - // Assemble our operator - Hform->Setup(); - CALI_MARK_END("mechop_mult_setup"); - CALI_MARK_BEGIN("mechop_mult_Mult"); - Hform->Mult(k, y); - CALI_MARK_END("mechop_mult_Mult"); -} - -template -void NonlinearMechOperator::Setup(const Vector &k) const -{ - CALI_CXX_MARK_SCOPE("mechop_setup"); - // Wanted to put this in the mechanics_solver.cpp file, but I would have needed to update - // Solver class to use the NonlinearMechOperator instead of Operator class. - // We now update our end coordinates based on the solved for velocity. - if(upd_crds) { - UpdateEndCoords(k); - } - - // This performs the computation of the velocity gradient if needed, - // det(J), material tangent stiffness matrix, state variable update, - // stress update, and other stuff that might be needed in the integrators. - - const FiniteElement &el = *fe_space.GetFE(0); - const int space_dims = el.GetDim(); - const IntegrationRule *ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1));; - - const int nqpts = ir->GetNPoints(); - const int ndofs = el.GetDof(); - const int nelems = fe_space.GetNE(); - - SetupJacobianTerms(); - - // We can now make the call to our material model set-up stage... - // Everything else that we need should live on the class. - // Within this function the model just needs to produce the Cauchy stress - // and the material tangent matrix (d \sigma / d Vgrad_{sym}) - bool succeed_t = false; - bool succeed = false; - try{ - if (mech_type == MechType::UMAT) { - model->ModelSetup(nqpts, nelems, space_dims, ndofs, el_jac, qpts_dshape, k); - } - else { - // Takes in k vector and transforms into into our E-vector array - P->Mult(k, px); - elem_restrict_lex->Mult(px, el_x); - model->ModelSetup(nqpts, nelems, space_dims, ndofs, el_jac, qpts_dshape, el_x); - } - succeed_t = true; - } - catch(const std::exception &exc) { - // catch anything thrown within try block that derives from std::exception - MFEM_WARNING(exc.what()); - succeed_t = false; - } - catch(...) { - succeed_t = false; - } - MPI_Allreduce(&succeed_t, &succeed, 1, MPI_C_BOOL, MPI_LAND, MPI_COMM_WORLD); - if (!succeed) { - throw std::runtime_error(std::string("Material model setup portion of code failed for at least one integration point.")); - } -} // End of model setup - -void NonlinearMechOperator::SetupJacobianTerms() const -{ - - Mesh *mesh = fe_space.GetMesh(); - const FiniteElement &el = *fe_space.GetFE(0); - const int space_dims = el.GetDim(); - const IntegrationRule *ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); - - const int nqpts = ir->GetNPoints(); - const int nelems = fe_space.GetNE(); - - // We need to make sure these are deleted at the start of each iteration - // since we have meshes that are constantly changing. - mesh->DeleteGeometricFactors(); - const GeometricFactors *geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS); - // geom->J really isn't going to work for us as of right now. We could just reorder it - // to the version that we want it to be in instead... - - const int DIM4 = 4; - std::array perm4 {{ 3, 2, 1, 0 } }; - // bunch of helper RAJA views to make dealing with data easier down below in our kernel. - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ space_dims, space_dims, nqpts, nelems } }, perm4); - RAJA::View > jac_view(el_jac.ReadWrite(), layout_jacob); - - RAJA::Layout layout_geom = RAJA::make_permuted_layout({{ nqpts, space_dims, space_dims, nelems } }, perm4); - RAJA::View > geom_j_view(geom->J.Read(), layout_geom); - - const int nqpts1 = nqpts; - const int space_dims1 = space_dims; - MFEM_FORALL(i, nelems, - { - const int nqpts_ = nqpts1; - const int space_dims_ = space_dims1; - for (int j = 0; j < nqpts_; j++) { - for (int k = 0; k < space_dims_; k++) { - for (int l = 0; l < space_dims_; l++) { - jac_view(l, k, j, i) = geom_j_view(j, l, k, i); - } - } - } - }); -} - -void NonlinearMechOperator::CalculateDeformationGradient(mfem::QuadratureFunction &def_grad) const -{ - Mesh *mesh = fe_space.GetMesh(); - - //Since we never modify our mesh nodes during this operations this is okay. - mfem::GridFunction *nodes = const_cast(&x_ref); // set a nodes grid function to global current configuration - int owns_nodes = 0; - mesh->SwapNodes(nodes, owns_nodes); // pmesh has current configuration nodes - SetupJacobianTerms(); - - const IntegrationRule *ir = &(IntRules.Get(fe_space.GetFE(0)->GetGeomType(), 2 * fe_space.GetFE(0)->GetOrder() + 1));; - - const int nqpts = ir->GetNPoints(); - const int ndofs = fe_space.GetFE(0)->GetDof(); - const int nelems = fe_space.GetNE(); - - Vector x_true(fe_space.TrueVSize(), mfem::Device::GetMemoryType()); - - x_cur.GetTrueDofs(x_true); - // Takes in k vector and transforms into into our E-vector array - P->Mult(x_true, px); - elem_restrict_lex->Mult(px, el_x); - - def_grad = 0.0; - exaconstit::kernel::grad_calc(nqpts, nelems, ndofs, el_jac.Read(), qpts_dshape.Read(), el_x.Read(), def_grad.ReadWrite()); - - //We're returning our mesh nodes to the original object they were pointing to. - //So, we need to cast away the const here. - //We just don't want other functions outside this changing things. - nodes = const_cast(&x_cur); - mesh->SwapNodes(nodes, owns_nodes); - //Delete the old geometric factors since they dealt with the original reference frame. - mesh->DeleteGeometricFactors(); - -} - -// Update the end coords used in our model -void NonlinearMechOperator::UpdateEndCoords(const Vector& vel) const -{ - model->UpdateEndCoords(vel); -} - -// Compute the Jacobian from the nonlinear form -Operator &NonlinearMechOperator::GetGradient(const Vector &x) const -{ - CALI_CXX_MARK_SCOPE("mechop_getgrad"); - Jacobian = &Hform->GetGradient(x); - // Reset our preconditioner operator aka recompute the diagonal for our jacobi. - Jacobian->AssembleDiagonal(diag); - return *Jacobian; -} - -// Compute the Jacobian from the nonlinear form -Operator& NonlinearMechOperator::GetUpdateBCsAction(const Vector &k, const Vector &x, Vector &y) const -{ - - CALI_CXX_MARK_SCOPE("mechop_GetUpdateBCsAction"); - // We first run a setup step before actually doing anything. - // We'll want to move this outside of Mult() at some given point in time - // and have it live in the NR solver itself or whatever solver - // we're going to be using. - Setup(k); - // We now perform our element vector operation. - Vector resid(y); resid.UseDevice(true); - Array zero_tdofs; - if (assembly == Assembly::PA) { - CALI_CXX_MARK_SCOPE("mechop_PA_BC_PreSetup"); - model->TransformMatGradTo4D(); - } - - CALI_MARK_BEGIN("mechop_Hform_LocalGrad"); - Hform->Setup(); - Hform->SetEssentialTrueDofs(zero_tdofs); - auto &loc_jacobian = Hform->GetGradient(x); - loc_jacobian.Mult(x, y); - Hform->SetEssentialTrueDofs(ess_tdof_list); - Hform->Mult(k, resid); - Jacobian = &Hform->GetGradient(x); - CALI_MARK_END("mechop_Hform_LocalGrad"); - - { - auto I = ess_tdof_list.Read(); - auto size = ess_tdof_list.Size(); - auto Y = y.Write(); - // Need to get rid of all the constrained values here - MFEM_FORALL(i, size, Y[I[i]] = 0.0; ); - } - - y += resid; - return *Jacobian; -} - -NonlinearMechOperator::~NonlinearMechOperator() -{ - delete model; - delete Hform; -} diff --git a/src/mechanics_operator.hpp b/src/mechanics_operator.hpp deleted file mode 100644 index 2d027a7..0000000 --- a/src/mechanics_operator.hpp +++ /dev/null @@ -1,102 +0,0 @@ - -#ifndef mechanics_operator_hpp -#define mechanics_operator_hpp - -#include "mfem.hpp" -#include "mechanics_integrators.hpp" -#include "mechanics_model.hpp" -#include "mechanics_umat.hpp" -#include "option_parser.hpp" -#include "mechanics_operator_ext.hpp" - -// The NonlinearMechOperator class is what really drives the entire system. -// It's responsible for calling the Newton Rhapson solver along with several of -// our post-processing steps. It also contains all of the relevant information -// related to our Krylov iterative solvers. -class NonlinearMechOperator : public mfem::NonlinearForm -{ - protected: - - mfem::ParFiniteElementSpace &fe_space; - mfem::ParNonlinearForm *Hform; - mutable mfem::Vector diag, qpts_dshape, el_x, px, el_jac; - mutable mfem::Operator *Jacobian; - const mfem::Vector *x; - const mfem::ParGridFunction &x_ref; - const mfem::ParGridFunction &x_cur; - mutable PANonlinearMechOperatorGradExt *pa_oper; - mutable MechOperatorJacobiSmoother *prec_oper; - const mfem::Operator *elem_restrict_lex; - Assembly assembly; - /// nonlinear model - ExaModel *model; - /// Variable telling us if we should use the UMAT specific - /// stuff - MechType mech_type; - - const mfem::Array2D &ess_bdr_comps; - - public: - NonlinearMechOperator(mfem::ParFiniteElementSpace &fes, - mfem::Array &ess_bdr, - mfem::Array2D &ess_bdr_comp, - ExaOptions &options, - mfem::QuadratureFunction &q_matVars0, - mfem::QuadratureFunction &q_matVars1, - mfem::QuadratureFunction &q_sigma0, - mfem::QuadratureFunction &q_sigma1, - mfem::QuadratureFunction &q_matGrad, - mfem::QuadratureFunction &q_kinVars0, - mfem::QuadratureFunction &q_vonMises, - mfem::ParGridFunction &ref_crds, - mfem::ParGridFunction &beg_crds, - mfem::ParGridFunction &end_crds, - mfem::Vector &matProps, - int nStateVars); - - /// Computes our jacobian operator for the entire system to be used within - /// the newton raphson solver. - virtual mfem::Operator &GetGradient(const mfem::Vector &x) const override; - - /// This computes the necessary quantities needed for when the BCs have been - /// updated. So, we need the old Jacobian operator and old residual term - /// that now includes the additional force term from the change in BCs on - /// the unconstrained nodes. - virtual mfem::Operator& GetUpdateBCsAction(const mfem::Vector &k, - const mfem::Vector &x, - mfem::Vector &y) const; - - /// Performs the action of our function / force vector - virtual void Mult(const mfem::Vector &k, mfem::Vector &y) const override; - - /// Sets all of the data up for the Mult and GetGradient method - /// This is of significant interest to be able to do partial assembly operations. - using mfem::NonlinearForm::Setup; - - template - void Setup(const mfem::Vector &k) const; - - void SetupJacobianTerms() const; - void CalculateDeformationGradient(mfem::QuadratureFunction &def_grad) const; - - // We need the solver to update the end coords after each iteration has been complete - // We'll also want to have a way to update the coords before we start running the simulations. - // It might also allow us to set a velocity at every point, so we could test the models almost - // as if we're doing a MPS. - void UpdateEndCoords(const mfem::Vector& vel) const; - - // Update the essential boundary conditions - void UpdateEssTDofs(const mfem::Array &ess_bdr, bool mono_def_flag); - - /// Get essential true dof list, if required - const mfem::Array &GetEssTDofList(); - - ExaModel *GetModel() const; - - MechOperatorJacobiSmoother *GetPAPreconditioner(){ return prec_oper; } - - virtual ~NonlinearMechOperator(); -}; - - -#endif /* mechanics_operator_hpp */ diff --git a/src/mechanics_operator_ext.cpp b/src/mechanics_operator_ext.cpp deleted file mode 100644 index b9d6d82..0000000 --- a/src/mechanics_operator_ext.cpp +++ /dev/null @@ -1,328 +0,0 @@ -#include "mfem.hpp" -#include "mfem/general/forall.hpp" -#include "mechanics_operator_ext.hpp" -#include "mechanics_integrators.hpp" -#include "mechanics_log.hpp" -#include "mechanics_operator.hpp" -#include "RAJA/RAJA.hpp" - -using namespace mfem; - -MechOperatorJacobiSmoother::MechOperatorJacobiSmoother(const Vector &d, - const Array &ess_tdofs, - const double dmpng) - : - Solver(d.Size()), - N(d.Size()), - dinv(N), - damping(dmpng), - ess_tdof_list(ess_tdofs), - residual(N) -{ - Setup(d); -} - -void MechOperatorJacobiSmoother::Setup(const Vector &diag) -{ - residual.UseDevice(true); - dinv.UseDevice(true); - const double delta = damping; - auto D = diag.Read(); - auto DI = dinv.Write(); - MFEM_FORALL(i, N, DI[i] = delta / D[i]; ); - auto I = ess_tdof_list.Read(); - MFEM_FORALL(i, ess_tdof_list.Size(), DI[I[i]] = delta; ); -} - -void MechOperatorJacobiSmoother::Mult(const Vector &x, Vector &y) const -{ - MFEM_ASSERT(x.Size() == N, "invalid input vector"); - MFEM_ASSERT(y.Size() == N, "invalid output vector"); - - if (iterative_mode && oper) { - oper->Mult(y, residual); // r = A x - subtract(x, residual, residual); // r = b - A x - } - else { - residual = x; - y.UseDevice(true); - y = 0.0; - } - auto DI = dinv.Read(); - auto R = residual.Read(); - auto Y = y.ReadWrite(); - MFEM_FORALL(i, N, Y[i] += DI[i] * R[i]; ); -} - -NonlinearMechOperatorExt::NonlinearMechOperatorExt(NonlinearForm *_oper_mech) - : Operator(_oper_mech->FESpace()->GetTrueVSize()), oper_mech(_oper_mech) -{ - // empty -} - -PANonlinearMechOperatorGradExt::PANonlinearMechOperatorGradExt(NonlinearForm *_oper_mech, const mfem::Array &ess_tdofs) : - NonlinearMechOperatorExt(_oper_mech), fes(_oper_mech->FESpace()), ess_tdof_list(ess_tdofs) -{ - // So, we're going to originally support non tensor-product type elements originally. - const ElementDofOrdering ordering = ElementDofOrdering::NATIVE; - // const ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC; - elem_restrict_lex = fes->GetElementRestriction(ordering); - P = fes->GetProlongationMatrix(); - if (elem_restrict_lex) { - localX.SetSize(elem_restrict_lex->Height(), Device::GetMemoryType()); - localY.SetSize(elem_restrict_lex->Height(), Device::GetMemoryType()); - px.SetSize(elem_restrict_lex->Width(), Device::GetMemoryType()); - ones.SetSize(elem_restrict_lex->Width(), Device::GetMemoryType()); - ones.UseDevice(true); // ensure 'x = 1.0' is done on device - localY.UseDevice(true); // ensure 'localY = 0.0' is done on device - localX.UseDevice(true); - px.UseDevice(true); - ones = 1.0; - } -} - -void PANonlinearMechOperatorGradExt::Assemble() -{ - CALI_CXX_MARK_SCOPE("PA_Assemble"); - Array &integrators = *oper_mech->GetDNFI(); - const int num_int = integrators.Size(); - for (int i = 0; i < num_int; ++i) { - integrators[i]->AssemblePA(*oper_mech->FESpace()); - integrators[i]->AssembleGradPA(*oper_mech->FESpace()); - } -} - -void PANonlinearMechOperatorGradExt::AssembleDiagonal(Vector &diag) const -{ - CALI_CXX_MARK_SCOPE("AssembleDiagonal"); - Array &integrators = *oper_mech->GetDNFI(); - const int num_int = integrators.Size(); - - if (elem_restrict_lex) { - localY = 0.0; - for (int i = 0; i < num_int; ++i) { - integrators[i]->AssembleGradDiagonalPA(localY); - } - - elem_restrict_lex->MultTranspose(localY, px); - P->MultTranspose(px, diag); - } - else { - diag.UseDevice(true); // typically this is a large vector, so store on device - diag = 0.0; - for (int i = 0; i < num_int; ++i) { - integrators[i]->AssembleGradDiagonalPA(diag); - } - } - - // Apply the essential boundary conditions - auto Y = diag.ReadWrite(); - auto I = ess_tdof_list.Read(); - - MFEM_FORALL(i, ess_tdof_list.Size(), Y[I[i]] = 1.0; ); -} - -void PANonlinearMechOperatorGradExt::Mult(const Vector &x, Vector &y) const -{ - TMult(x, y); -} - -void PANonlinearMechOperatorGradExt::LocalMult(const Vector &x, Vector &y) const -{ - TMult(x, y); -} - -template -void PANonlinearMechOperatorGradExt::TMult(const Vector &x, Vector &y) const -{ - CALI_CXX_MARK_SCOPE("PA_Mult"); - Array &integrators = *oper_mech->GetDNFI(); - const int num_int = integrators.Size(); - - // Apply the essential boundary conditions - ones = x; - auto I = ess_tdof_list.Read(); - auto Y = ones.ReadWrite(); - MFEM_FORALL(i, ess_tdof_list.Size(), Y[I[i]] = 0.0; ); - - if (elem_restrict_lex) { - P->Mult(ones, px); - elem_restrict_lex->Mult(px, localX); - localY = 0.0; - for (int i = 0; i < num_int; ++i) { - integrators[i]->AddMultGradPA(localX, localY); - } - - elem_restrict_lex->MultTranspose(localY, px); - P->MultTranspose(px, y); - } - else { - y.UseDevice(true); // typically this is a large vector, so store on device - y = 0.0; - for (int i = 0; i < num_int; ++i) { - integrators[i]->AddMultGradPA(x, y); - } - } - - // Only apply essential boundary conditions if we don't need to perform the - // local action of the matrix - if(!local_action) { - // Apply the essential boundary conditions - Y = y.ReadWrite(); - MFEM_FORALL(i, ess_tdof_list.Size(), Y[I[i]] = 0.0; ); - } -} - -void PANonlinearMechOperatorGradExt::MultVec(const Vector &x, Vector &y) const -{ - Array &integrators = *oper_mech->GetDNFI(); - const int num_int = integrators.Size(); - if (elem_restrict_lex) { - P->Mult(x, px); - elem_restrict_lex->Mult(px, localX); - localY = 0.0; - for (int i = 0; i < num_int; ++i) { - integrators[i]->AddMultPA(localX, localY); - } - - elem_restrict_lex->MultTranspose(localY, px); - P->MultTranspose(px, y); - } - else { - y.UseDevice(true); // typically this is a large vector, so store on device - y = 0.0; - for (int i = 0; i < num_int; ++i) { - integrators[i]->AddMultPA(x, y); - } - } - // Apply the essential boundary conditions - auto I = ess_tdof_list.Read(); - auto Y = y.ReadWrite(); - MFEM_FORALL(i, ess_tdof_list.Size(), Y[I[i]] = 0.0; ); -} - -// Data and methods for element-assembled bilinear forms -EANonlinearMechOperatorGradExt::EANonlinearMechOperatorGradExt(NonlinearForm *_oper_mech, const mfem::Array &ess_tdofs) - : PANonlinearMechOperatorGradExt(_oper_mech, ess_tdofs) -{ - NE = _oper_mech->FESpace()->GetMesh()->GetNE(); - elemDofs = _oper_mech->FESpace()->GetFE(0)->GetDof() * _oper_mech->FESpace()->GetFE(0)->GetDim(); - - ea_data.SetSize(NE * elemDofs * elemDofs, Device::GetMemoryType()); - ea_data.UseDevice(true); -} - -void EANonlinearMechOperatorGradExt::Assemble() -{ - ea_data = 0.0; - - CALI_CXX_MARK_SCOPE("EA_Assemble"); - Array &integrators = *oper_mech->GetDNFI(); - const int num_int = integrators.Size(); - for (int i = 0; i < num_int; ++i) { - integrators[i]->AssemblePA(*oper_mech->FESpace()); - integrators[i]->AssembleEA(*oper_mech->FESpace(), ea_data); - } -} - -void EANonlinearMechOperatorGradExt::AssembleDiagonal(Vector &diag) const -{ - CALI_CXX_MARK_SCOPE("eaAssembleDiagonal"); - - const bool useRestrict = true && elem_restrict_lex; - if (!useRestrict) { - diag.UseDevice(true); // typically this is a large vector, so store on device - diag = 0.0; - } - else { - localY = 0.0; - } - - // Apply the Element Matrices - const int NDOFS = elemDofs; - auto Y = Reshape(useRestrict ? localY.ReadWrite() : diag.ReadWrite(), NDOFS, NE); - auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, NE); - const int elemDofs_ = elemDofs; - MFEM_FORALL(glob_j, NE * NDOFS, - { - const int NDOFS = elemDofs_; - const int e = glob_j / NDOFS; - const int j = glob_j % NDOFS; - Y(j, e) = A(j, j, e); - }); - - // Apply the Element Restriction transposed - if (useRestrict) { - elem_restrict_lex->MultTranspose(localY, px); - P->MultTranspose(px, diag); - } - - // Apply the essential boundary conditions - auto R = diag.ReadWrite(); - auto I = ess_tdof_list.Read(); - - MFEM_FORALL(i, ess_tdof_list.Size(), R[I[i]] = 1.0; ); -} - -void EANonlinearMechOperatorGradExt::Mult(const Vector &x, Vector &y) const -{ - TMult(x, y); -} - -void EANonlinearMechOperatorGradExt::LocalMult(const Vector &x, Vector &y) const -{ - TMult(x, y); -} - -template -void EANonlinearMechOperatorGradExt::TMult(const Vector &x, Vector &y) const -{ - // Apply the Element Restriction - // Apply the essential boundary conditions - ones = x; - auto I = ess_tdof_list.Read(); - auto R = ones.ReadWrite(); - MFEM_FORALL(i, ess_tdof_list.Size(), R[I[i]] = 0.0; ); - - const bool useRestrict = true && elem_restrict_lex; - if (!useRestrict) { - y.UseDevice(true); // typically this is a large vector, so store on device - y = 0.0; - } - else { - P->Mult(ones, px); - elem_restrict_lex->Mult(px, localX); - localY = 0.0; - } - - // Apply the Element Matrices - const int NDOFS = elemDofs; - auto X = Reshape(useRestrict ? localX.Read() : ones.Read(), NDOFS, NE); - auto Y = Reshape(useRestrict ? localY.ReadWrite() : y.ReadWrite(), NDOFS, NE); - auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, NE); - MFEM_FORALL(glob_j, NE * NDOFS, - { - const int NDOFS_ = NDOFS; - const int e = glob_j / NDOFS_; - const int j = glob_j % NDOFS_; - double res = 0.0; - for (int i = 0; i < NDOFS_; i++) { - res += A(i, j, e) * X(i, e); - } - - Y(j, e) += res; - }); - // Apply the Element Restriction transposed - if (useRestrict) { - elem_restrict_lex->MultTranspose(localY, px); - P->MultTranspose(px, y); - } - - // Only apply essential boundary conditions if we don't need to perform the - // local action of the matrix - if(!local_action){ - // Apply the essential boundary conditions - R = y.ReadWrite(); - MFEM_FORALL(i, ess_tdof_list.Size(), R[I[i]] = 0.0; ); - } -} diff --git a/src/mechanics_operator_ext.hpp b/src/mechanics_operator_ext.hpp deleted file mode 100644 index a4bcae3..0000000 --- a/src/mechanics_operator_ext.hpp +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef mechanics_operator_ext_hpp -#define mechanics_operator_ext_hpp - -#include "mfem.hpp" -#include "mechanics_integrators.hpp" - -// The NonlinearMechOperatorExt class contains all of the relevant info related to our -// partial assembly class. -class NonlinearMechOperatorExt : public mfem::Operator -{ - protected: - mfem::NonlinearForm *oper_mech; // Not owned - public: - - NonlinearMechOperatorExt(mfem::NonlinearForm *_mech_operator); - - virtual mfem::MemoryClass GetMemoryClass() const - { return mfem::Device::GetMemoryClass(); } - - // Any assembly operation we would need to use before we might need to use - // the Mult operator. - virtual void Assemble() = 0; - - // Here we would assemble the diagonal of any matrix-like operation we might be - // performing. - virtual void AssembleDiagonal(mfem::Vector &diag) const = 0; -}; - -// We'll pass this on through the GetGradient method which can be used -// within our Iterative solver. -class PANonlinearMechOperatorGradExt : public NonlinearMechOperatorExt -{ - protected: - const mfem::FiniteElementSpace *fes; // Not owned - mutable mfem::Vector localX, localY, ones, px; - const mfem::Operator *elem_restrict_lex; // Not owned - const mfem::Operator *P; - const mfem::Array &ess_tdof_list; - public: - PANonlinearMechOperatorGradExt(mfem::NonlinearForm *_mech_operator, - const mfem::Array &ess_tdofs); - - virtual void Assemble() override; - virtual void AssembleDiagonal(mfem::Vector &diag) const override; - template - void TMult(const mfem::Vector &x, mfem::Vector &y) const; - virtual void Mult(const mfem::Vector &x, mfem::Vector &y) const override; - virtual void LocalMult(const mfem::Vector &x, mfem::Vector &y) const; - virtual void MultVec(const mfem::Vector &x, mfem::Vector &y) const; -}; - -// We'll pass this on through the GetGradient method which can be used -// within our Iterative solver. -class EANonlinearMechOperatorGradExt : public PANonlinearMechOperatorGradExt -{ - protected: - int NE; - int elemDofs; - mfem::Vector ea_data; - int nf_int, nf_bdr; - int faceDofs; - public: - EANonlinearMechOperatorGradExt(mfem::NonlinearForm *_mech_operator, - const mfem::Array &ess_tdofs); - - void Assemble() override; - - virtual void AssembleDiagonal(mfem::Vector &diag) const override; - // using PANonlinearMechOperatorGradExt::AssembleDiagonal; - template - void TMult(const mfem::Vector &x, mfem::Vector &y) const; - void Mult(const mfem::Vector &x, mfem::Vector &y) const override; - void LocalMult(const mfem::Vector &x, mfem::Vector &y) const override; - - using PANonlinearMechOperatorGradExt::MultVec; - // void MultVec(const mfem::Vector &x, mfem::Vector &y) const; -}; - -/// Jacobi smoothing for a given bilinear form (no matrix necessary). -/// We're going to be using a l1-jacobi here. -/** Useful with tensorized, partially assembled operators. Can also be defined - by given diagonal vector. This is basic Jacobi iteration; for tolerances, - iteration control, etc. wrap with SLISolver. */ -class MechOperatorJacobiSmoother : public mfem::Solver -{ - public: - - /** Application is by the *inverse* of the given vector. It is assumed that - the underlying operator acts as the identity on entries in ess_tdof_list, - corresponding to (assembled) DIAG_ONE policy or ConstratinedOperator in - the matrix-free setting. */ - MechOperatorJacobiSmoother(const mfem::Vector &d, - const mfem::Array &ess_tdofs, - const double damping = 1.0); - ~MechOperatorJacobiSmoother() {} - - void Mult(const mfem::Vector &x, mfem::Vector &y) const; - - void SetOperator(const mfem::Operator &op) { oper = &op; } - - void Setup(const mfem::Vector &diag); - - private: - const int N; - mfem::Vector dinv; - const double damping; - const mfem::Array &ess_tdof_list; - mutable mfem::Vector residual; - - const mfem::Operator *oper; -}; - - -#endif /* mechanics_operator_hpp */ diff --git a/src/mechanics_solver.cpp b/src/mechanics_solver.cpp deleted file mode 100644 index 2840ea7..0000000 --- a/src/mechanics_solver.cpp +++ /dev/null @@ -1,281 +0,0 @@ - -#include "mfem.hpp" -#include "mechanics_solver.hpp" -#include "mfem/linalg/linalg.hpp" -#include "mfem/general/globals.hpp" -#include "mechanics_log.hpp" -#include -#include -#include -#include - - -using namespace std; -using namespace mfem; - -void ExaNewtonSolver::SetOperator(const Operator &op) -{ - oper = &op; - height = op.Height(); - width = op.Width(); - MFEM_ASSERT(height == width, "square Operator is required."); - - r.SetSize(width, Device::GetMemoryType()); r.UseDevice(true); - c.SetSize(width, Device::GetMemoryType()); c.UseDevice(true); -} - -void ExaNewtonSolver::SetOperator(const NonlinearForm &op) -{ - oper_mech = &op; - oper = &op; - height = op.Height(); - width = op.Width(); - MFEM_ASSERT(height == width, "square NonlinearForm is required."); - - r.SetSize(width, Device::GetMemoryType()); r.UseDevice(true); - c.SetSize(width, Device::GetMemoryType()); c.UseDevice(true); -} - -void ExaNewtonSolver::Mult(const Vector &b, Vector &x) const -{ - CALI_CXX_MARK_SCOPE("NR_solver"); - MFEM_ASSERT(oper != NULL, "the Operator is not set (use SetOperator)."); - MFEM_ASSERT(prec != NULL, "the Solver is not set (use SetSolver)."); - - int it; - double norm0, norm, norm_max; - double norm_prev, norm_ratio; - const bool have_b = (b.Size() == Height()); - - // Might want to use this to fix things later on for example when we have a - // large residual. We might also want to eventually try and find a converged - // relaxation factor which would mean resetting our solution vector a few times. - Vector x_prev(x.Size()); - x_prev.UseDevice(true); - - if (!iterative_mode) { - x = 0.0; - } - - x_prev = x; - - oper_mech->Mult(x, r); - if (have_b) { - r -= b; - } - - norm0 = norm = norm_prev = Norm(r); - norm_ratio = 1.0; - // Set the value for the norm that we'll exit on - norm_max = std::max(rel_tol * norm, abs_tol); - - prec->iterative_mode = false; - double scale = 1.0; - - // x_{i+1} = x_i - [DF(x_i)]^{-1} [F(x_i)-b] - for (it = 0; true; it++) { - // Make sure the norm is finite - MFEM_ASSERT(IsFinite(norm), "norm = " << norm); - if (print_level >= 0) { - mfem::out << "Newton iteration " << setw(2) << it - << " : ||r|| = " << norm; - if (it > 0) { - mfem::out << ", ||r||/||r_0|| = " << norm / norm0; - } - mfem::out << '\n'; - } - // See if our solution has converged and we can quit - if (norm <= norm_max) { - converged = 1; - break; - } - // See if we've gone over the max number of desired iterations - if (it >= max_iter) { - converged = 0; - break; - } - - prec->SetOperator(oper_mech->GetGradient(x)); - CALI_MARK_BEGIN("krylov_solver"); - prec->Mult(r, c); // c = [DF(x_i)]^{-1} [F(x_i)-b] - // ExaConstit may use GMRES here - - CALI_MARK_END("krylov_solver"); - const double c_scale = scale; - if (c_scale == 0.0) { - converged = 0; - break; - } - - add(x, -c_scale, c, x); // full update to the current config - // ExaConstit (srw) - - // We now get our new residual - oper_mech->Mult(x, r); - if (have_b) { - r -= b; - } - - // Find our new norm and save our previous time step value. - norm_prev = norm; - norm = Norm(r); - // We're going to more or less use a heuristic method here for now if - // our ratio is greater than 1e-1 then we'll set our scaling factor for - // the next iteration to 0.5. - // We want to do this since it's not uncommon for us to run into the case - // where our solution is oscillating over the one we actually want. - // Eventually, we'll fix this in our scaling factor function. - norm_ratio = norm / norm_prev; - - if (norm_ratio > 5.0e-1) { - scale = 0.5; - if (print_level >= 0) { - mfem::out << "The relaxation factor for the next iteration has been reduced to " << scale << "\n"; - } - } - else { - scale = 1.0; - } - } - - final_iter = it; - final_norm = norm; -} - -void ExaNewtonSolver::CGSolver(mfem::Operator &oper, const mfem::Vector &b, mfem::Vector &x) const -{ - prec->SetOperator(oper); - CALI_MARK_BEGIN("krylov_solver"); - prec->Mult(b, x); // c = [DF(x_i)]^{-1} [F(x_i)-b] - // ExaConstit may use GMRES here - - CALI_MARK_END("krylov_solver"); -} - -void ExaNewtonLSSolver::Mult(const Vector &b, Vector &x) const -{ - CALI_CXX_MARK_SCOPE("NRLS_solver"); - MFEM_ASSERT(oper != NULL, "the Operator is not set (use SetOperator)."); - MFEM_ASSERT(prec != NULL, "the Solver is not set (use SetSolver)."); - - int it; - double norm0, norm, norm_max; - const bool have_b = (b.Size() == Height()); - - // Might want to use this to fix things later on for example when we have a - // large residual. We might also want to eventually try and find a converged - // relaxation factor which would mean resetting our solution vector a few times. - Vector x_prev(x.Size()); - Vector Jr(x.Size()); - Jr.UseDevice(true); - x_prev.UseDevice(true); - - if (!iterative_mode) { - x = 0.0; - } - - x_prev = x; - - oper_mech->Mult(x, r); - if (have_b) { - r -= b; - } - - norm0 = norm = Norm(r); - // Set the value for the norm that we'll exit on - norm_max = std::max(rel_tol * norm, abs_tol); - - prec->iterative_mode = false; - double scale = 1.0; - - // x_{i+1} = x_i - [DF(x_i)]^{-1} [F(x_i)-b] - for (it = 0; true; it++) { - // Make sure the norm is finite - MFEM_ASSERT(IsFinite(norm), "norm = " << norm); - if (print_level >= 0) { - mfem::out << "Newton iteration " << setw(2) << it - << " : ||r|| = " << norm; - if (it > 0) { - mfem::out << ", ||r||/||r_0|| = " << norm / norm0; - } - mfem::out << '\n'; - } - // See if our solution has converged and we can quit - if (norm <= norm_max) { - converged = 1; - break; - } - // See if we've gone over the max number of desired iterations - if (it >= max_iter) { - converged = 0; - break; - } - - prec->SetOperator(oper_mech->GetGradient(x)); - CALI_MARK_BEGIN("krylov_solver"); - prec->Mult(r, c); // c = [DF(x_i)]^{-1} [F(x_i)-b] - // ExaConstit may use GMRES here - CALI_MARK_END("krylov_solver"); - // This line search method is based on the quadratic variation of the norm - // of the residual line search described in this conference paper: - // https://doi.org/10.1007/978-3-642-01970-8_46 . We can probably do better - // than this one. - { - CALI_CXX_MARK_SCOPE("Line Search"); - x_prev = x; - add(x, -1.0, c, x); - oper_mech->Mult(x, r); - if(have_b) { - r -= b; - } - double q1 = norm; - double q3 = Norm(r); - x = x_prev; - add(x, -0.5, c, x); - oper_mech->Mult(x, r); - if(have_b) { - r -= b; - } - double q2 = Norm(r); - - double eps = (3.0 * q1 - 4.0 * q2 + q3) / (4.0 * (q1 - 2.0 * q2 + q3)); - - if ((q1 - 2.0 * q2 + q3) > 0 && eps > 0 && eps < 1) { - scale = eps; - } else if (q3 < q1) { - scale = 1.0; - } else { - // We should probably just quit if this is the case... - scale = 0.05; - } - - if (print_level >= 0) { - mfem::out << "The relaxation factor for this iteration is " << scale << std::endl; - } - - x = x_prev; - } - - const double c_scale = scale; - if (c_scale == 0.0) { - converged = 0; - break; - } - - add(x, -c_scale, c, x); // full update to the current config - // ExaConstit (srw) - - // We now get our new residual - oper_mech->Mult(x, r); - if (have_b) { - r -= b; - } - - // Find our new norm - norm = Norm(r); - - } - - final_iter = it; - final_norm = norm; -} \ No newline at end of file diff --git a/src/mechanics_solver.hpp b/src/mechanics_solver.hpp deleted file mode 100644 index e7ad238..0000000 --- a/src/mechanics_solver.hpp +++ /dev/null @@ -1,73 +0,0 @@ - -#ifndef MECHANICS_SOLVER -#define MECHANICS_SOLVER - -#include "mfem/linalg/solvers.hpp" - - -/// Newton's method for solving F(x)=b for a given operator F. -/** The method GetGradient() must be implemented for the operator F. - The preconditioner is used (in non-iterative mode) to evaluate - the action of the inverse gradient of the operator. */ -class ExaNewtonSolver : public mfem::IterativeSolver -{ - protected: - mutable mfem::Vector r, c; - const mfem::NonlinearForm* oper_mech; - - public: - ExaNewtonSolver() { } - -#ifdef MFEM_USE_MPI - ExaNewtonSolver(MPI_Comm _comm) : IterativeSolver(_comm) { } - -#endif - virtual void SetOperator(const mfem::Operator &op); - virtual void SetOperator(const mfem::NonlinearForm &op); - - /// Set the linear solver for inverting the Jacobian. - /** This method is equivalent to calling SetPreconditioner(). */ - virtual void SetSolver(mfem::Solver &solver) { prec = &solver; } - - virtual void CGSolver(mfem::Operator &oper, const mfem::Vector &b, mfem::Vector &x) const; - - /// Solve the nonlinear system with right-hand side @a b. - /** If `b.Size() != Height()`, then @a b is assumed to be zero. */ - virtual void Mult(const mfem::Vector &b, mfem::Vector &x) const; - - // We're going to comment this out for now. - /** @brief This method can be overloaded in derived classes to implement line - search algorithms. */ - /** The base class implementation (NewtonSolver) simply returns 1. A return - value of 0 indicates a failure, interrupting the Newton iteration. */ - // virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const - // { return 1.0; } -}; - -/// Newton's method for solving F(x)=b for a given operator F and makes use of a -/// line search method. -/** The method GetGradient() must be implemented for the operator F. - The preconditioner is used (in non-iterative mode) to evaluate - the action of the inverse gradient of the operator. */ -class ExaNewtonLSSolver : public ExaNewtonSolver -{ - public: - ExaNewtonLSSolver() { } - -#ifdef MFEM_USE_MPI - ExaNewtonLSSolver(MPI_Comm _comm) : ExaNewtonSolver(_comm) { } -#endif - - using ExaNewtonSolver::SetOperator; - - using ExaNewtonSolver::SetSolver; - virtual void SetSolver(mfem::Solver &solver) { prec = &solver; } - - using ExaNewtonSolver::CGSolver; - /// Solve the nonlinear system with right-hand side @a b. - /** If `b.Size() != Height()`, then @a b is assumed to be zero. */ - virtual void Mult(const mfem::Vector &b, mfem::Vector &x) const; - -}; - -#endif diff --git a/src/mechanics_umat.cpp b/src/mechanics_umat.cpp deleted file mode 100644 index 57f2149..0000000 --- a/src/mechanics_umat.cpp +++ /dev/null @@ -1,593 +0,0 @@ -#include "mechanics_umat.hpp" -#include "BCManager.hpp" -#include // log -#include -#include // cerr -#include "RAJA/RAJA.hpp" -#include "mfem/fem/qfunction.hpp" - - -using namespace mfem; -using namespace std; - -void AbaqusUmatModel::UpdateModelVars() -{ - // update the beginning step deformation gradient - QuadratureFunction* defGrad = defGrad0; - double* dgrad0 = defGrad->HostReadWrite(); - double* dgrad1 = end_def_grad.HostReadWrite(); - // We just need to update our beginning of time step def. grad. with our - // end step def. grad. now that they are equal. - for (int i = 0; i < defGrad->Size(); i++) { - dgrad0[i] = dgrad1[i]; - } -} - -// Work through the initialization of all of this... -void AbaqusUmatModel::init_loc_sf_grads(ParFiniteElementSpace *fes) -{ - const FiniteElement *fe; - const IntegrationRule *ir; - QuadratureFunction* _defgrad0 = defGrad0; - QuadratureSpaceBase* qspace = _defgrad0->GetSpace(); - - ir = &(qspace->GetIntRule(0)); - - const int NE = fes->GetNE(); - const int NQPTS = ir->GetNPoints(); - - // get element transformation for the 0th element - // We just want to get some basic stuff for now - fe = fes->GetFE(0); - - // declare data to store shape function gradients - // and element Jacobians - DenseMatrix Jrt, DSh, DS; - int dof = fe->GetDof(), dim = fe->GetDim(); - const int VDIM = dof * dim; - - DSh.SetSize(dof, dim); - // This should probably be linked to the underlying quadrature function - DS.SetSize(dof, dim); - Jrt.SetSize(dim); - - // We now have enough information to create our loc0_sf_grad - - loc0_sf_grad.SetSpace(qspace, VDIM); - double* data = loc0_sf_grad.HostReadWrite(); - - // loop over elements - for (int i = 0; i < NE; ++i) { - // get element transformation for the ith element - ElementTransformation* Ttr = fes->GetElementTransformation(i); - fe = fes->GetFE(i); - - // PMatI.UseExternalData(el_x.ReadWrite(), dof, dim); - - ir = &(qspace->GetIntRule(i)); - - // loop over integration points where the quadrature function is - // stored - for (int j = 0; j < NQPTS; ++j) { - // The offset is the current location of the data - int offset = (i * NQPTS * VDIM) + (j * VDIM); - double* data_offset = data + offset; - - DS.UseExternalData(data_offset, dof, dim); - - const IntegrationPoint &ip = ir->IntPoint(j); - Ttr->SetIntPoint(&ip); - CalcInverse(Ttr->Jacobian(), Jrt); - - fe->CalcDShape(ip, DSh); - Mult(DSh, Jrt, DS); - } - } -} - -void AbaqusUmatModel::init_incr_end_def_grad() -{ - const IntegrationRule *ir; - QuadratureFunction* _defgrad0 = defGrad0; - QuadratureSpaceBase* qspace = _defgrad0->GetSpace(); - - ir = &(qspace->GetIntRule(0)); - - const int TOTQPTS = qspace->GetSize(); - const int NQPTS = ir->GetNPoints(); - // We've got the same elements everywhere so we can do this. - // If this assumption is no longer true we need to update the code - const int NE = TOTQPTS / NQPTS; - const int VDIM = _defgrad0->GetVDim(); - - incr_def_grad.SetSpace(qspace, VDIM); - incr_def_grad = 0.0; - double* incr_data = incr_def_grad.HostReadWrite(); - - end_def_grad.SetSpace(qspace, VDIM); - end_def_grad = 0.0; - double* end_data = end_def_grad.HostReadWrite(); - - // loop over elements - for (int i = 0; i < NE; ++i) { - // loop over integration points where the quadrature function is - // stored - for (int j = 0; j < NQPTS; ++j) { - // The offset is the current location of the data - int offset = (i * NQPTS * VDIM) + (j * VDIM); - double* incr_data_offset = incr_data + offset; - double* end_data_offset = end_data + offset; - - // It's now just initialized to being the identity matrix - incr_data_offset[0] = 1.0; - incr_data_offset[4] = 1.0; - incr_data_offset[8] = 1.0; - - // It's now just initialized to being the identity matrix - end_data_offset[0] = 1.0; - end_data_offset[4] = 1.0; - end_data_offset[8] = 1.0; - } - } -} - -void AbaqusUmatModel::calc_incr_end_def_grad(const Vector &x0) -{ - const IntegrationRule *ir; - QuadratureFunction* _defgrad0 = defGrad0; - QuadratureSpaceBase* qspace = _defgrad0->GetSpace(); - - ir = &(qspace->GetIntRule(0)); - - const int tot_qpts = qspace->GetSize(); - const int nqpts = ir->GetNPoints(); - // We've got the same type of elements everywhere so we can do this. - // If this assumption is no longer true we need to update the code - const int ne = tot_qpts / nqpts; - const int vdim = _defgrad0->GetVDim(); - // We also assume we're only dealing with 3D type elements. - // If we aren't then this needs to change... - const int dim = 3; - const int vdim2 = loc0_sf_grad.GetVDim(); - const int dof = vdim2 / dim; - - double* incr_data = incr_def_grad.HostReadWrite(); - double* end_data = end_def_grad.HostReadWrite(); - double* int_data = _defgrad0->HostReadWrite(); - double* ds_data = loc0_sf_grad.HostReadWrite(); - - ParGridFunction x_gf; - // This is quite dangerous potentially and we should try and fix this - // maybe with pargrid function or somewhere else - double* vals = const_cast(x0.HostRead()); - - x_gf.MakeTRef(loc_fes, vals); - x_gf.SetFromTrueVector(); - x_gf.HostReadWrite(); - - DenseMatrix f_incr(dim, dim); - DenseMatrix f_end(dim, dim); - DenseMatrix f_beg(dim, dim); - DenseMatrix f_beg_invr(dim, dim); - DenseMatrix DS(dof, dim); - DenseMatrix PMatI(dof, dim); - // The below are constant but will change between steps - Array vdofs(vdim2); - Vector el_x(PMatI.Data(), vdim2); - - // loop over elements - for (int i = 0; i < ne; ++i) { - loc_fes->GetElementVDofs(i, vdofs); - // Our PMatI is now updated to the correct elemental values - x_gf.GetSubVector(vdofs, el_x); - // loop over integration points where the quadrature function is - // stored - for (int j = 0; j < nqpts; ++j) { - // The offset is the current location of the data - int offset = (i * nqpts * vdim) + (j * vdim); - int offset2 = (i * nqpts * vdim2) + (j * vdim2); - double* incr_data_offset = incr_data + offset; - double* end_data_offset = end_data + offset; - double* int_data_offset = int_data + offset; - double* ds_data_offset = ds_data + offset2; - - f_end.UseExternalData(end_data_offset, dim, dim); - f_beg.UseExternalData(int_data_offset, dim, dim); - f_incr.UseExternalData(incr_data_offset, dim, dim); - DS.UseExternalData(ds_data_offset, dof, dim); - - // Get the inverse of the beginning time step def. grad - f_beg_invr = f_beg; - f_beg_invr.Invert(); - - // Find the end time step def. grad - MultAtB(PMatI, DS, f_end); - - // Our incremental def. grad is now - Mult(f_end, f_beg_invr, f_incr); - } - } -} - -void AbaqusUmatModel::CalcLogStrainIncrement(DenseMatrix& dE, const DenseMatrix &Jpt) -{ - // calculate incremental logorithmic strain (Hencky Strain) - // which is taken to be E = ln(U_hat) = 1/2 ln(C_hat), where - // C_hat = (F_hat_T)F_hat, where F_hat = Jpt1 on the model - // (available from MFEM element transformation computations). - // We can compute F_hat, so use a spectral decomposition on C_hat to - // obtain a form where we only have to take the natural log of the - // eigenvalues - // UMAT uses the E = ln(V) approach instead - - DenseMatrix F_hat, B_hat; - - constexpr int dim = 3; - - F_hat.SetSize(dim); - B_hat.SetSize(dim); - - F_hat = Jpt; - - MultABt(F_hat, F_hat, B_hat); - - // compute eigenvalue decomposition of B - double lambda[dim]; - double vec[dim * dim]; - B_hat.CalcEigenvalues(&lambda[0], &vec[0]); - - // compute ln(B) using spectral representation - dE = 0.0; - for (int i = 0; iGetTrueDofs(temp); - // Creating a new vector that's going to be used for our - // UMAT custom Hform->Mult - const Vector crd(temp.HostReadWrite(), temp.Size()); - calc_incr_end_def_grad(crd); - } - - // ====================================================== - // Set UMAT input arguments - // ====================================================== - - // initialize Umat variables - int ndi = 3; // number of direct stress components - int nshr = 3; // number of shear stress components - int ntens = ndi + nshr; - int noel = 0; - int npt = 0; - int layer = 0; - int kspt = 0; - int kstep = 0; - int kinc = 0; - - // set properties and state variables length (hard code for now); - int nprops = numProps; - int nstatv = numStateVars; - - double pnewdt = 10.0; // revisit this - mfem::Vector props(nprops); // populate from the mat props vector wrapped by matProps on the base class - mfem::Vector statev(nstatv); // populate from the state variables associated with this element/ip - - double rpl = 0.0; // volumetric heat generation per unit time, not considered - double drpldt = 0.0; // variation of rpl wrt temperature set to 0.0 - double tempk = 300.0; // no thermal considered at this point - double dtemp = 0.0; // no increment in thermal considered at this point - double predef = 0.0; // no interpolated values of predefined field variables at ip point - double dpred = 0.0; // no array of increments of predefined field variables - double sse = 0.0; // specific elastic strain energy, mainly for output - double spd = 0.0; // specific plastic dissipation, mainly for output - double scd = 0.0; // specific creep dissipation, mainly for output - double cmname = 0.0; // user defined UMAT name - double celent = 0.0; // set element length - - // integration point coordinates - // a material model shouldn't need this ever - double coords[3] = { 0, 0, 0 }; - - // set the time step - double deltaTime = dt; // set on the ExaModel base class - - // set time. Abaqus has odd increment definition. time[1] is the value of total - // time at the beginning of the current increment. Since we are iterating from - // tn to tn+1, this is just tn. time[0] is value of step time at the beginning - // of the current increment. What is step time if not tn? It seems as though - // they sub-increment between tn->tn+1, where there is a Newton Raphson loop - // advancing the sub-increment. For now, set time[0] is set to t - dt/ - double time[2]; - time[0] = t - dt; - time[1] = t; - - double stress[6]; // Cauchy stress at ip - double ddsdt[6]; // variation of the stress increments wrt to temperature, set to 0.0 - double drplde[6]; // variation of rpl wrt strain increments, set to 0.0 - double stran[6]; // array containing total strains at beginning of the increment - double dstran[6]; // array of strain increments - - double *drot; // rotation matrix for finite deformations - double dfgrd0[9]; // deformation gradient at beginning of increment - double dfgrd1[9]; // defomration gradient at the end of the increment. - // set to zero if nonlinear geometric effects are not - // included in the step as is the case for ExaConstit - - QuadratureFunction* _defgrad0 = defGrad0; - - double* defgrad0 = _defgrad0->HostReadWrite(); - double* defgrad1 = end_def_grad.HostReadWrite(); - double* incr_defgrad = incr_def_grad.HostReadWrite(); - DenseMatrix incr_dgrad, dgrad0, dgrad1; - - const int vdim = end_def_grad.GetVDim(); - double ddsdde[36]; // output Jacobian matrix of the constitutive model. - // ddsdde(i,j) defines the change in the ith stress component - // due to an incremental perturbation in the jth strain increment - - const int DIM4 = 4; - - std::array perm4 {{ 3, 2, 1, 0 } }; - // bunch of helper RAJA views to make dealing with data easier down below in our kernel. - RAJA::Layout layout_jacob = RAJA::make_permuted_layout({{ space_dim, space_dim, nqpts, nelems } }, perm4); - RAJA::View > J(jacobian.HostRead(), layout_jacob); - - for (int elemID = 0; elemID < nelems; elemID++) { - for (int ipID = 0; ipID < nqpts; ipID++) { - // compute characteristic element length - const double J11 = J(0, 0, ipID, elemID); // 0,0 - const double J21 = J(1, 0, ipID, elemID); // 1,0 - const double J31 = J(2, 0, ipID, elemID); // 2,0 - const double J12 = J(0, 1, ipID, elemID); // 0,1 - const double J22 = J(1, 1, ipID, elemID); // 1,1 - const double J32 = J(2, 1, ipID, elemID); // 2,1 - const double J13 = J(0, 2, ipID, elemID); // 0,2 - const double J23 = J(1, 2, ipID, elemID); // 1,2 - const double J33 = J(2, 2, ipID, elemID); // 2,2 - const double detJ = J11 * (J22 * J33 - J32 * J23) - - /* */ J21 * (J12 * J33 - J32 * J13) + - /* */ J31 * (J12 * J23 - J22 * J13); - CalcElemLength(detJ); - celent = elemLength; - - const int offset = elemID * nqpts * vdim + ipID * vdim; - - noel = elemID; // element id - npt = ipID; // integration point number - - // initialize 1d arrays - for (int i = 0; i<6; ++i) { - stress[i] = 0.0; - ddsdt[i] = 0.0; - drplde[i] = 0.0; - stran[i] = 0.0; - dstran[i] = 0.0; - } - - // initialize 6x6 2d arrays - for (int i = 0; i<6; ++i) { - for (int j = 0; j<6; ++j) { - ddsdde[(i * 6) + j] = 0.0; - } - } - - - incr_dgrad.UseExternalData((incr_defgrad + offset), 3, 3); - dgrad0.UseExternalData((defgrad0 + offset), 3, 3); - dgrad1.UseExternalData((defgrad1 + offset), 3, 3); - - DenseMatrix Uincr(3), Vincr(3); - DenseMatrix Rincr(incr_dgrad, 3); - CalcPolarDecompDefGrad(Rincr, Uincr, Vincr); - - drot = Rincr.GetData(); - - // populate the beginning step and end step (or best guess to end step - // within the Newton iterations) of the deformation gradients - for (int i = 0; i +#include +#include +#include +#include + +namespace mfem::expt { + +/// Copy the data from @a qf. +// this is wrong we need to check and see if first the sizes are equal if so it's a simple +// copy. If not then we need to want to check and see if the meshes are equal, +// integration rules are same or integration rule are same then we can fill things up easy +// peasy +PartialQuadratureFunction& PartialQuadratureFunction::operator=(const QuadratureFunction& qf) { + MFEM_ASSERT(qf.GetVDim() == vdim, "Vector dimensions don't match"); + MFEM_ASSERT(qf.GetSpaceShared()->GetSize() >= part_quad_space->GetSize(), + "QuadratureSpace sizes aren't of equivalent sizes"); + + if (qf.GetSpaceShared()->GetSize() == part_quad_space->GetSize()) { + Vector::operator=(qf); + return *this; + } else { + // Very basic check to see if the two spaces are roughly equivalent... + // We would need to do a much more thorough job if we wanted to be 100% certain + MFEM_ASSERT(qf.GetSpaceShared()->GetMeshShared()->GetNE() == + part_quad_space->GetMeshShared()->GetNE(), + "QSpaces have mesh's with different # of elements"); + MFEM_ASSERT(qf.GetSpaceShared()->GetOrder() == part_quad_space->GetOrder(), + "QSpaces don't have the same integration order"); + // We now need to copy all of the relevant data over that we'll need + auto l2g = part_quad_space->local2global.Read(); + auto loc_offsets = part_quad_space->offsets.Read(); + auto global_offsets = (part_quad_space->global_offsets.Size() > 1) + ? part_quad_space->global_offsets.Read() + : loc_offsets; + auto qf_data = qf.Read(); + auto loc_data = this->ReadWrite(); + + auto NE = part_quad_space->GetNE(); + // For now this is fine. Later on we might want to leverage like RAJA views and the + // IndexLayout to make things even more performant. Additionally, we could look at using 2D + // kernels if need be but probably overkill for now... + const auto vdim_ = vdim; + mfem::forall(NE, [=] MFEM_HOST_DEVICE(int ie) { + const int global_idx = l2g[ie]; + const int global_offset_idx = global_offsets[global_idx]; + const int local_offset_idx = loc_offsets[ie]; + const int nqpts = loc_offsets[ie + 1] - local_offset_idx; + const int npts = nqpts * vdim_; + for (int jv = 0; jv < npts; jv++) { + loc_data[local_offset_idx * vdim_ + jv] = qf_data[global_offset_idx * vdim_ + jv]; + } + }); + } + return *this; +} + +/// Takes in a quadrature function and fill with either the values contained in this +/// class or the default value provided by users. +void PartialQuadratureFunction::FillQuadratureFunction(QuadratureFunction& qf, const bool fill) { + if (qf.GetSpaceShared()->GetSize() == part_quad_space->GetSize()) { + qf = *this; + } else { + // Very basic check to see if the two spaces are roughly equivalent... + // We would need to do a much more thorough job if we wanted to be 100% certain + MFEM_ASSERT(qf.GetVDim() == vdim, "Vector dimensions don't match"); + MFEM_ASSERT(qf.GetSpaceShared()->GetMeshShared()->GetNE() == + part_quad_space->GetMeshShared()->GetNE(), + "QSpaces have mesh's with different # of elements"); + MFEM_ASSERT(qf.GetSpaceShared()->GetOrder() == part_quad_space->GetOrder(), + "QSpaces don't have the same integration order"); + // We now need to copy all of the relevant data over that we'll need + auto l2g = part_quad_space->local2global.Read(); + auto offsets = part_quad_space->offsets.Read(); + auto global_offsets = (part_quad_space->global_offsets.Size() > 1) + ? part_quad_space->global_offsets.Read() + : offsets; + auto qf_data = qf.ReadWrite(); + auto loc_data = this->Read(); + // First set all values to default + if (fill) { + qf = default_value; + } + auto NE = part_quad_space->GetNE(); + // Then copy our partial values to their proper places + const auto vdim_ = vdim; + mfem::forall(NE, [=] MFEM_HOST_DEVICE(int ie) { + const int global_idx = l2g[ie]; + const int global_offset_idx = global_offsets[global_idx]; + const int local_offset_idx = offsets[ie]; + const int nqpts = offsets[ie + 1] - local_offset_idx; + const int npts = nqpts * vdim_; + for (int jv = 0; jv < npts; jv++) { + qf_data[global_offset_idx * vdim_ + jv] = loc_data[local_offset_idx * vdim_ + jv]; + } + }); + } +} + +} // namespace mfem::expt \ No newline at end of file diff --git a/src/mfem_expt/partial_qfunc.hpp b/src/mfem_expt/partial_qfunc.hpp new file mode 100644 index 0000000..5e6bb83 --- /dev/null +++ b/src/mfem_expt/partial_qfunc.hpp @@ -0,0 +1,560 @@ +#pragma once + +#include "mfem_expt/partial_qspace.hpp" + +#include "mfem/config/config.hpp" +#include "mfem/fem/qfunction.hpp" +#include "mfem/fem/qspace.hpp" +#include "mfem/general/forall.hpp" + +#include +#include +#include +#include +#include + +namespace mfem::expt { + +/** + * @brief Class for representing quadrature functions on a subset of mesh elements. + * + * PartialQuadratureFunction extends MFEM's QuadratureFunction to efficiently store and + * manipulate quadrature point data for only a subset of mesh elements. This is essential + * in ExaConstit for multi-material simulations where different constitutive models and + * state variables apply to different regions of the mesh. + * + * The class maintains compatibility with MFEM's QuadratureFunction interface while + * providing optimized memory usage and performance for partial element sets. It handles + * the mapping between partial and full quadrature spaces automatically and provides + * default values for elements not in the partial set. + * + * Key features: + * - Memory-efficient storage for sparse element data + * - Automatic handling of default values for non-partial elements + * - Full compatibility with MFEM's QuadratureFunction operations + * - Efficient data transfer between partial and full quadrature spaces + * - Support for multi-component vector fields at quadrature points + * + * @ingroup ExaConstit_mfem_expt + */ +class PartialQuadratureFunction : public QuadratureFunction { +private: + /** + * @brief Reference to the specialized PartialQuadratureSpace. + * + * This shared pointer maintains a reference to the PartialQuadratureSpace that + * defines the element subset and quadrature point layout for this function. + * The space provides the mapping between local and global element indices + * needed for efficient data access and manipulation. + */ + std::shared_ptr part_quad_space; + + /** + * @brief Default value for elements not in the partial set. + * + * This value is returned when accessing data for elements that are not + * included in the partial quadrature space. It allows the function to + * appear as if it has values defined over the entire mesh while only + * storing data for the relevant subset of elements. + */ + double default_value; + +public: + /** + * @brief Constructor with shared_ptr to PartialQuadratureSpace. + * + * @param qspace_ Shared pointer to the PartialQuadratureSpace defining the element subset + * @param vdim_ Vector dimension of the function (number of components per quadrature point) + * @param default_val Default value for elements not in the partial set + * + * This is the recommended constructor that creates a PartialQuadratureFunction with + * proper memory management using shared_ptr. The vector dimension determines how many + * scalar values are stored at each quadrature point (e.g., vdim=1 for scalar fields, + * vdim=3 for vector fields, vdim=9 for tensor fields). + */ + PartialQuadratureFunction(std::shared_ptr qspace_, + int vdim_ = 1, + double default_val = -1.0) + : QuadratureFunction(std::static_pointer_cast(qspace_), vdim_), + part_quad_space(std::move(qspace_)), default_value(default_val) {} + + /** + * @brief Constructor with raw pointer to PartialQuadratureSpace (deprecated). + * + * @param qspace_ Raw pointer to the PartialQuadratureSpace defining the element subset + * @param vdim_ Vector dimension of the function (number of components per quadrature point) + * @param default_val Default value for elements not in the partial set + * + * @deprecated Use constructor with std::shared_ptr instead for better + * memory management + */ + [[deprecated("Use constructor with std::shared_ptr instead")]] + PartialQuadratureFunction(PartialQuadratureSpace* qspace_, + int vdim_ = 1, + double default_val = -1.0) + : PartialQuadratureFunction(ptr_utils::borrow_ptr(qspace_), vdim_, default_val) {} + + /** + * @brief Get the specialized PartialQuadratureSpace as shared_ptr. + * + * @return Shared pointer to the underlying PartialQuadratureSpace + * + * This method provides access to the PartialQuadratureSpace that defines the + * element subset and quadrature point layout for this function. Useful for + * accessing mapping information and space properties. + */ + [[nodiscard]] + std::shared_ptr GetPartialSpaceShared() const { + return part_quad_space; + } + + /** + * @brief Get the specialized PartialQuadratureSpace as raw pointer (deprecated). + * + * @return Raw pointer to the underlying PartialQuadratureSpace + * + * @deprecated Use GetPartialSpaceShared() instead for better memory management + */ + [[deprecated("Use GetPartialSpaceShared() instead")]] [[nodiscard]] + PartialQuadratureSpace* GetPartialSpace() const { + return part_quad_space.get(); + } + + /** + * @brief Set this equal to a constant value. + * + * @param value Constant value to assign to all quadrature points in the partial set + * @return Reference to this PartialQuadratureFunction for method chaining + * + * This operator assigns the specified constant value to all quadrature points + * within the partial element set. Elements outside the partial set are not + * affected and will continue to return the default value. + */ + PartialQuadratureFunction& operator=(double value) override { + QuadratureFunction::operator=(value); + return *this; + } + + /** + * @brief Copy the data from a Vector. + * + * @param vec Vector containing the data to copy (must match the size of this function) + * @return Reference to this PartialQuadratureFunction for method chaining + * + * This operator copies data from a Vector into the PartialQuadratureFunction. + * The vector size must exactly match the size of the partial quadrature space. + * The data is interpreted as [comp0_qp0, comp1_qp0, ..., comp0_qp1, comp1_qp1, ...]. + */ + PartialQuadratureFunction& operator=(const Vector& vec) override { + MFEM_ASSERT(part_quad_space && vec.Size() == this->Size(), ""); + QuadratureFunction::operator=(vec); + return *this; + } + + /** + * @brief Copy the data from another QuadratureFunction. + * + * @param qf Source QuadratureFunction to copy data from + * @return Reference to this PartialQuadratureFunction for method chaining + * + * This operator intelligently copies data from a QuadratureFunction, handling + * both cases where the source function has the same size (direct copy) or + * different size (element-by-element mapping). For different sizes, it validates + * mesh compatibility and integration rule consistency before performing the + * element-wise data transfer using the local-to-global mapping. + */ + PartialQuadratureFunction& operator=(const QuadratureFunction& qf); + + /** + * @brief Fill a global QuadratureFunction with data from this partial function. + * + * @param qf Reference to the global QuadratureFunction to fill + * @param fill Whether to initialize non-partial elements with default value + * + * This method transfers data from the PartialQuadratureFunction to a global + * QuadratureFunction that spans the entire mesh. For elements in the partial set, + * it copies the stored values. For elements not in the partial set, it optionally + * fills with the default value if fill=true. + * + * The method handles two cases: + * 1. Same size spaces: Direct copy operation + * 2. Different size spaces: Element-by-element mapping with validation + * + * Validation checks ensure compatible vector dimensions, mesh compatibility, + * and matching integration orders before performing the data transfer. + */ + void FillQuadratureFunction(QuadratureFunction& qf, const bool fill = false); + + /** + * @brief Override ProjectGridFunction to project only onto the partial space. + * + * @param gf GridFunction to project (parameter currently unused) + * + * This method is currently unsupported and will abort if called. It's included + * for interface completeness and may be implemented in future versions to + * project GridFunction data onto the partial quadrature space. + */ + void ProjectGridFunction([[maybe_unused]] const GridFunction& gf) override { + MFEM_ABORT("Unsupported case."); + } + + /** + * @brief Return all values associated with mesh element as a reference Vector. + * + * @param idx Global element index + * @param values Output vector that will reference the internal data or be filled with defaults + * + * This method provides access to all quadrature point values for the specified element. + * For elements in the partial set, it creates a reference to the internal data for + * efficient access. For elements not in the partial set, it creates a new vector + * filled with default values. + * + * The values vector is organized as [comp0_qp0, comp1_qp0, ..., comp0_qp1, comp1_qp1, ...]. + */ + virtual void GetValues(int idx, Vector& values) override { + const int local_index = part_quad_space->GlobalToLocal(idx); + // If global_offsets.Size() == 1 then we'll always + // go down this path + if (local_index > -1) { + const int s_offset = part_quad_space->offsets[local_index]; + const int sl_size = part_quad_space->offsets[local_index + 1] - s_offset; + values.MakeRef(*this, vdim * s_offset, vdim * sl_size); + } else { + const int s_offset = part_quad_space->global_offsets[idx]; + const int sl_size = part_quad_space->global_offsets[idx + 1] - s_offset; + values.Destroy(); + values.SetSize(vdim * sl_size); + values.HostWrite(); + for (int i = 0; i < values.Size(); i++) { + values(i) = default_value; + } + } + } + + /** + * @brief Return all values associated with mesh element as a copy Vector. + * + * @param idx Global element index + * @param values Output vector to store the copied values + * + * This method retrieves all quadrature point values for the specified element as + * a copy rather than a reference. For elements in the partial set, it copies the + * stored values. For elements not in the partial set, it fills the output vector + * with default values. + * + * The values vector is organized as [comp0_qp0, comp1_qp0, ..., comp0_qp1, comp1_qp1, ...]. + */ + virtual void GetValues(int idx, Vector& values) const override { + const int local_index = part_quad_space->GlobalToLocal(idx); + // If global_offsets.Size() == 1 then we'll always + // go down this path + if (local_index > -1) { + const int s_offset = part_quad_space->offsets[local_index]; + const int sl_size = part_quad_space->offsets[local_index + 1] - s_offset; + values.SetSize(vdim * sl_size); + values.HostWrite(); + const real_t* q = HostRead() + vdim * s_offset; + for (int i = 0; i < values.Size(); i++) { + values(i) = *(q++); + } + } else { + const int s_offset = part_quad_space->global_offsets[idx]; + const int sl_size = part_quad_space->global_offsets[idx + 1] - s_offset; + values.SetSize(vdim * sl_size); + values.HostWrite(); + for (int i = 0; i < values.Size(); i++) { + values(i) = default_value; + } + } + } + + /** + * @brief Return quadrature function values at a specific integration point as reference. + * + * @param idx Global element index + * @param ip_num Quadrature point number within the element + * @param values Output vector that will reference the internal data or be filled with defaults + * + * This method provides access to the values at a single quadrature point within an element. + * For elements in the partial set, it creates a reference to the internal data. + * For elements not in the partial set, it creates a new vector filled with default values. + */ + virtual void GetValues(int idx, const int ip_num, Vector& values) override { + const int local_index = part_quad_space->GlobalToLocal(idx); + // If global_offsets.Size() == 1 then we'll always + // go down this path + if (local_index > -1) { + const int s_offset = (part_quad_space->offsets[local_index] + ip_num) * vdim; + values.MakeRef(*this, s_offset, vdim); + } else { + values.Destroy(); + values.SetSize(vdim); + values.HostWrite(); + for (int i = 0; i < values.Size(); i++) { + values(i) = default_value; + } + } + } + + /** + * @brief Return quadrature function values at a specific integration point as copy. + * + * @param idx Global element index + * @param ip_num Quadrature point number within the element + * @param values Output vector to store the copied values + * + * This method retrieves the values at a single quadrature point within an element + * as a copy rather than a reference. For elements in the partial set, it copies the + * stored values. For elements not in the partial set, it fills the output vector + * with default values. + */ + virtual void GetValues(int idx, const int ip_num, Vector& values) const override { + const int local_index = part_quad_space->GlobalToLocal(idx); + // If global_offsets.Size() == 1 then we'll always + // go down this path + if (local_index > -1) { + const int s_offset = (part_quad_space->offsets[local_index] + ip_num) * vdim; + const real_t* q = HostRead() + s_offset; + values.SetSize(vdim); + values.HostWrite(); + for (int i = 0; i < values.Size(); i++) { + values(i) = *(q++); + } + } else { + values.Destroy(); + values.SetSize(vdim); + values.HostWrite(); + for (int i = 0; i < values.Size(); i++) { + values(i) = default_value; + } + } + } + + /** + * @brief Return all values associated with mesh element as a reference DenseMatrix. + * + * @param idx Global element index + * @param values Output matrix that will reference the internal data or be filled with defaults + * + * This method provides access to all quadrature point values for the specified element + * in matrix form. For elements in the partial set, it creates a memory alias to the + * internal data for efficient access. For elements not in the partial set, it creates + * a new matrix filled with default values. + * + * The matrix entry (i,j) corresponds to the i-th vector component at the j-th quadrature point. + */ + virtual void GetValues(int idx, DenseMatrix& values) override { + const int local_index = part_quad_space->GlobalToLocal(idx); + // If global_offsets.Size() == 1 then we'll always + // go down this path + if (local_index > -1) { + const int s_offset = part_quad_space->offsets[local_index]; + const int sl_size = part_quad_space->offsets[local_index + 1] - s_offset; + // Make the values matrix memory an alias of the quadrature function memory + Memory& values_mem = values.GetMemory(); + values_mem.Delete(); + values_mem.MakeAlias(GetMemory(), vdim * s_offset, vdim * sl_size); + values.SetSize(vdim, sl_size); + } else { + const int s_offset = part_quad_space->global_offsets[idx]; + const int sl_size = part_quad_space->global_offsets[idx + 1] - s_offset; + values.Clear(); + values.SetSize(vdim, sl_size); + values.HostWrite(); + for (int j = 0; j < sl_size; j++) { + for (int i = 0; i < vdim; i++) { + values(i, j) = default_value; + } + } + } + } + + /** + * @brief Return all values associated with mesh element as a copy DenseMatrix. + * + * @param idx Global element index + * @param values Output matrix to store the copied values + * + * This method retrieves all quadrature point values for the specified element as + * a copy in matrix form. For elements in the partial set, it copies the stored values. + * For elements not in the partial set, it fills the output matrix with default values. + * + * The matrix entry (i,j) corresponds to the i-th vector component at the j-th quadrature point. + */ + virtual void GetValues(int idx, DenseMatrix& values) const override { + const int local_index = part_quad_space->GlobalToLocal(idx); + // If global_offsets.Size() == 1 then we'll always + // go down this path + if (local_index > -1) { + const int s_offset = part_quad_space->offsets[local_index]; + const int sl_size = part_quad_space->offsets[local_index + 1] - s_offset; + values.SetSize(vdim, sl_size); + values.HostWrite(); + const real_t* q = HostRead() + vdim * s_offset; + for (int j = 0; j < sl_size; j++) { + for (int i = 0; i < vdim; i++) { + values(i, j) = *(q++); + } + } + } else { + // Make the values matrix memory an alias of the quadrature function memory + const int s_offset = part_quad_space->global_offsets[idx]; + const int sl_size = part_quad_space->global_offsets[idx + 1] - s_offset; + values.Clear(); + values.SetSize(vdim, sl_size); + values.HostWrite(); + for (int j = 0; j < sl_size; j++) { + for (int i = 0; i < vdim; i++) { + values(i, j) = default_value; + } + } + } + } + + /** + * @brief Get the IntegrationRule associated with entity (element or face). + * + * This uses the base class implementation from QuadratureFunction to provide + * access to the integration rules associated with mesh entities. + */ + using QuadratureFunction::GetIntRule; + + /** + * @brief Write the PartialQuadratureFunction to a stream. + * + * @param out Output stream to write the function data + * + * This method serializes the PartialQuadratureFunction to a stream. Currently, + * it only supports partial spaces that cover the full mesh (optimization case). + * For true partial spaces, an error is thrown indicating the feature is not + * yet implemented. + */ + virtual void Save(std::ostream& out) const override { + if (part_quad_space->global_offsets.Size() == 1) { + QuadratureFunction::Save(out); + return; + } + MFEM_ABORT("Currently not supported for PartialQuadratureFunctions"); + } + + /** + * @brief Write the PartialQuadratureFunction to an output stream in VTU format. + * + * @param out Output stream for VTU data + * @param format VTK format (ASCII or BINARY) + * @param compression_level Compression level for binary output + * @param field_name Name of the field in the VTU file + * + * This method saves the quadrature function data to ParaView's VTU format for + * visualization. Currently only supported for partial spaces that cover the full + * mesh. For true partial spaces, an error is thrown indicating the feature is + * not yet implemented. + */ + virtual void SaveVTU(std::ostream& out, + VTKFormat format = VTKFormat::ASCII, + int compression_level = 0, + const std::string& field_name = "u") const override { + if (part_quad_space->global_offsets.Size() == 1) { + QuadratureFunction::SaveVTU(out, format, compression_level, field_name); + return; + } + MFEM_ABORT("Currently not supported for PartialQuadratureFunctions"); + } + + /** + * @brief Save the PartialQuadratureFunction to a VTU (ParaView) file. + * + * @param filename Output filename (extension ".vtu" will be appended) + * @param format VTK format (ASCII or BINARY) + * @param compression_level Compression level for binary output + * @param field_name Name of the field in the VTU file + * + * This method saves the quadrature function data to a ParaView VTU file for + * visualization. Currently only supported for partial spaces that cover the full + * mesh. For true partial spaces, an error is thrown indicating the feature is + * not yet implemented. + */ + virtual void SaveVTU(const std::string& filename, + VTKFormat format = VTKFormat::ASCII, + int compression_level = 0, + const std::string& field_name = "u") const override { + if (part_quad_space->global_offsets.Size() == 1) { + QuadratureFunction::SaveVTU(filename, format, compression_level, field_name); + return; + } + MFEM_ABORT("Currently not supported for PartialQuadratureFunctions"); + } + + /** + * @brief Return the integral of the quadrature function (vdim = 1 only). + * + * @return Integral value over the partial domain + * + * This method computes the integral of the quadrature function over the elements + * in the partial space. Currently only supported for partial spaces that cover + * the full mesh. For true partial spaces, an error is thrown indicating the + * feature is not yet implemented. + */ + [[nodiscard]] virtual real_t Integrate() const override { + if (part_quad_space->global_offsets.Size() == 1) { + return QuadratureFunction::Integrate(); + } + MFEM_ABORT("Currently not supported for PartialQuadratureFunctions"); + return default_value; + } + + /** + * @brief Integrate the vector-valued quadrature function. + * + * @param integrals Output vector to store integration results (one per vector component) + * + * This method computes the integral of each component of a vector-valued quadrature + * function over the partial domain. Currently only supported for partial spaces that + * cover the full mesh. For true partial spaces, an error is thrown indicating the + * feature is not yet implemented. + */ + virtual void Integrate(Vector& integrals) const override { + if (part_quad_space->global_offsets.Size() == 1) { + QuadratureFunction::Integrate(integrals); + return; + } + MFEM_ABORT("Currently not supported for PartialQuadratureFunctions"); + } + + /** + * @brief Factory method to create a shared_ptr PartialQuadratureFunction. + * + * @param qspace Shared pointer to the PartialQuadratureSpace + * @param vdim Vector dimension of the function (default: 1) + * @param default_val Default value for elements not in partial set (default: -1.0) + * @return Shared pointer to the created PartialQuadratureFunction + * + * This factory method provides the recommended way to create PartialQuadratureFunction + * objects with proper memory management using shared_ptr. The vector dimension + * determines how many components the function has at each quadrature point. + */ + static std::shared_ptr Create( + std::shared_ptr qspace, int vdim = 1, double default_val = -1.0) { + return std::make_shared(std::move(qspace), vdim, default_val); + } + + /** + * @brief Factory method to create a shared_ptr PartialQuadratureFunction (deprecated). + * + * @param qspace Raw pointer to the PartialQuadratureSpace + * @param vdim Vector dimension of the function (default: 1) + * @param default_val Default value for elements not in partial set (default: -1.0) + * @return Shared pointer to the created PartialQuadratureFunction + * + * @deprecated Use Create() with std::shared_ptr instead for better + * memory management + */ + [[deprecated("Use Create() with std::shared_ptr instead")]] + static std::shared_ptr + Create(PartialQuadratureSpace* qspace, int vdim = 1, double default_val = -1.0) { + return std::make_shared( + ptr_utils::borrow_ptr(qspace), vdim, default_val); + } +}; + +} // namespace mfem::expt \ No newline at end of file diff --git a/src/mfem_expt/partial_qspace.cpp b/src/mfem_expt/partial_qspace.cpp new file mode 100644 index 0000000..2e0261f --- /dev/null +++ b/src/mfem_expt/partial_qspace.cpp @@ -0,0 +1,222 @@ +#include "mfem_expt/partial_qspace.hpp" + +namespace mfem::expt { +/// Class representing a subset of a QuadratureSpace, for efficient operations on subdomains. +// // Maps local indices to global mesh element indices +// mfem::Array local2global; + +// // Maps global mesh element indices to local indices (-1 if not in partial set) +// mfem::Array2D global2local; + +// Implementation of GetGeometricFactorWeights required by the base class +const mfem::Vector& PartialQuadratureSpace::GetGeometricFactorWeights() const { + // We'll create a partial weight vector from the full mesh's geometric factors + auto flags = mfem::GeometricFactors::DETERMINANTS; + // TODO: assumes only one integration rule. This should be fixed once + // Mesh::GetGeometricFactors accepts a QuadratureSpace instead of + // IntegrationRule. + const mfem::IntegrationRule& ir = GetIntRule(0); + auto* geom = mesh->GetGeometricFactors(ir, flags); + + // We need to extract only the weights for our partial elements + mfem::Vector& partial_weights = const_cast(weights); + partial_weights.SetSize(size); + partial_weights = 0.0; + + // Fill in the weights for our partial elements + for (int i = 0; i < local2global.Size(); i++) { + int global_idx = local2global[i]; + const int s_offset = offsets[i]; + const int e_offset = offsets[i + 1]; + const int num_qpoints = e_offset - s_offset; + + // Copy the weights for this element from the full mesh + for (int j = 0; j < num_qpoints; j++) { + // This is a simplified approach - a more accurate implementation would + // need to map to the correct quadrature point indices in the full mesh + partial_weights(s_offset + j) = geom->detJ(global_idx * num_qpoints + j); + } + } + return weights; +} + +void PartialQuadratureSpace::ConstructOffsets() { + // Set up offsets based on our partial element set + const int num_partial_elem = local2global.Size(); + offsets.SetSize(num_partial_elem + 1); + int offset = 0; + for (int i = 0; i < num_partial_elem; i++) { + offsets[i] = offset; + // Get the global element index + int global_elem_idx = local2global[i]; + // Get geometry for the element + const size_t geom = static_cast(mesh->GetElementBaseGeometry(global_elem_idx)); + MFEM_ASSERT(int_rule[geom] != NULL, "Missing integration rule."); + offset += int_rule[geom]->GetNPoints(); + } + offsets[num_partial_elem] = size = offset; +} + +void PartialQuadratureSpace::ConstructGlobalOffsets() { + // Set up offsets based on our partial element set + const int num_elems = global2local.Size(); + if (num_elems != 1) { + global_offsets.SetSize(num_elems + 1); + int offset = 0; + for (int i = 0; i < num_elems; i++) { + global_offsets[i] = offset; + // Get geometry for the element + const size_t geom = static_cast(mesh->GetElementBaseGeometry(i)); + MFEM_ASSERT(int_rule[geom] != NULL, "Missing integration rule."); + offset += int_rule[geom]->GetNPoints(); + } + global_offsets[num_elems] = offset; + } else { + global_offsets.SetSize(1); + global_offsets[0] = 0; + } +} + +void PartialQuadratureSpace::Construct() { + ConstructIntRules(mesh->Dimension()); + ConstructOffsets(); + ConstructGlobalOffsets(); +} + +void PartialQuadratureSpace::ConstructMappings(std::shared_ptr mesh_, + mfem::Array& partial_index) { + // First, construct the mapping arrays + int num_elements = mesh_->GetNE(); + + int partial_count = 0; + if (partial_index.Size() == 0) { + partial_count = num_elements; + } else { + // Count how many elements are in our partial set + for (int i = 0; i < num_elements; i++) { + if (partial_index[i]) { + partial_count++; + } + } + } + // Initialize local2global array + local2global.SetSize(partial_count); + // Set up global2local mapping with -1 as default (not in partial set) + // If partial_count == num_elements then this is a quadspace equiv + if (partial_count != num_elements) { + global2local.SetSize(num_elements); + for (int i = 0; i < num_elements; i++) { + global2local[i] = -1; + } + + // Fill the mapping arrays + int local_idx = 0; + for (int i = 0; i < num_elements; i++) { + if (partial_index[i]) { + local2global[local_idx] = i; + global2local[i] = local_idx; + local_idx++; + } + } + } else { + for (int i = 0; i < num_elements; i++) { + local2global[i] = i; + } + global2local.SetSize(1); + global2local[0] = 0; + } +} + +/// Create a PartialQuadratureSpace based on the global rules from #IntRules. +PartialQuadratureSpace::PartialQuadratureSpace(std::shared_ptr mesh_, + int order_, + mfem::Array& partial_index) + : QuadratureSpaceBase(mesh_, order_) { + ConstructMappings(mesh_, partial_index); + // Now construct the quadrature space internals + Construct(); +} + +/// Create a PartialQuadratureSpace with an mfem::IntegrationRule, valid only when +/// the mesh has one element type. +PartialQuadratureSpace::PartialQuadratureSpace(std::shared_ptr mesh_, + const mfem::IntegrationRule& ir, + mfem::Array& partial_index) + : QuadratureSpaceBase(mesh_, mesh_->GetTypicalElementGeometry(), ir) { + MFEM_VERIFY(mesh->GetNumGeometries(mesh->Dimension()) <= 1, + "Constructor not valid for mixed meshes"); + ConstructMappings(mesh_, partial_index); + // Now construct the offsets + ConstructOffsets(); +} + +/// Read a PartialQuadratureSpace from the stream @a in. +PartialQuadratureSpace::PartialQuadratureSpace(std::shared_ptr mesh_, std::istream& in) + : QuadratureSpaceBase(mesh_) { + const char* msg = "invalid input stream"; + std::string ident; + + // Read header information + in >> ident; + MFEM_VERIFY(ident == "PartialQuadratureSpace", msg); + in >> ident; + MFEM_VERIFY(ident == "Type:", msg); + in >> ident; + if (ident == "default_quadrature") { + in >> ident; + MFEM_VERIFY(ident == "Order:", msg); + in >> order; + } else { + MFEM_ABORT("unknown PartialQuadratureSpace type: " << ident); + return; + } + + // Read partial space mapping information + in >> ident; + MFEM_VERIFY(ident == "PartialIndices:", msg); + int size; + in >> size; + local2global.SetSize(size); + + // Read local2global array + for (int i = 0; i < size; i++) { + in >> local2global[i]; + } + + // Set up global2local mapping + int num_elements = mesh->GetNE(); + if (size != num_elements) { + global2local.SetSize(num_elements); + for (int i = 0; i < num_elements; i++) { + global2local[i] = -1; + } + + // Build the inverse mapping + for (int i = 0; i < size; i++) { + int global_idx = local2global[i]; + global2local[global_idx] = i; + } + } else { + global2local.SetSize(1); + global2local[0] = 0; + } + + // Now construct the quadrature space internals + Construct(); +} + +/// Save the PartialQuadratureSpace to a stream +void PartialQuadratureSpace::Save(std::ostream& out) const { + out << "PartialQuadratureSpace\n" + << "Type: default_quadrature\n" + << "Order: " << order << '\n'; + + // Save the partial space mapping information + out << "PartialIndices: " << local2global.Size() << '\n'; + for (int i = 0; i < local2global.Size(); i++) { + out << local2global[i] << " "; + } + out << "\n"; +} + +} // namespace mfem::expt diff --git a/src/mfem_expt/partial_qspace.hpp b/src/mfem_expt/partial_qspace.hpp new file mode 100644 index 0000000..58bf7c9 --- /dev/null +++ b/src/mfem_expt/partial_qspace.hpp @@ -0,0 +1,458 @@ +#pragma once + +#include "mfem/config/config.hpp" +#include "mfem/fem/qspace.hpp" + +#include +#include +#include +#include +#include + +namespace mfem::expt { + +/** + * @brief Class representing a subset of a QuadratureSpace for efficient operations on subdomains. + * + * PartialQuadratureSpace extends MFEM's QuadratureSpaceBase to provide efficient finite element + * operations on subsets of mesh elements. This is particularly useful in ExaConstit for handling + * multi-material simulations where different constitutive models apply to different regions of + * the mesh (e.g., different crystal orientations in polycrystalline materials). + * + * The class maintains bidirectional mappings between local element indices (within the partial set) + * and global element indices (in the full mesh), enabling efficient data access and computation + * while maintaining compatibility with MFEM's finite element framework. + * + * Key features: + * - Efficient memory usage by storing data only for active elements + * - Optimized performance through local-to-global index mapping + * - Full compatibility with MFEM's QuadratureSpaceBase interface + * - Support for both partial and full mesh coverage with automatic optimization + * + * @ingroup ExaConstit_mfem_expt + */ +class PartialQuadratureSpace : public mfem::QuadratureSpaceBase { +protected: + friend class PartialQuadratureFunction; // Uses the offsets. + /** + * @brief Maps local element indices to global mesh element indices. + * + * This array provides the forward mapping from the local element indexing scheme + * used within the PartialQuadratureSpace to the global element indexing scheme + * of the underlying mesh. Size equals the number of elements in the partial set. + * + * local2global[local_idx] = global_idx + */ + mfem::Array local2global; + /** + * @brief Maps global mesh element indices to local element indices. + * + * This array provides the reverse mapping from global mesh element indices to + * local partial space indices. Contains -1 for elements not in the partial set. + * Size equals the total number of elements in the mesh, or 1 if optimization + * for full-space coverage is enabled. + * + * global2local[global_idx] = local_idx (or -1 if not in partial set) + */ + mfem::Array global2local; + /** + * @brief Maps global mesh element indices to quadrature point offsets. + * + * This array provides offset information for all elements in the global mesh, + * facilitating efficient data transfer between partial and full quadrature spaces. + * Used internally for mapping operations when the partial space doesn't cover + * the entire mesh. + * + * global_offsets[global_idx] = starting offset for element's quadrature points + */ + mfem::Array global_offsets; + +protected: + /** + * @brief Implementation of GetGeometricFactorWeights required by the base class. + * + * @return Const reference to the geometric factor weights vector + * + * This method computes and returns the geometric factor weights (determinants of + * element transformations) for elements in the partial quadrature space. It extracts + * the relevant weights from the full mesh's geometric factors and constructs a + * partial weights vector containing only the data for elements in the partial set. + * + * The weights are essential for proper numerical integration over the partial domain. + * Currently assumes a single integration rule type across all elements. + */ + virtual const mfem::Vector& GetGeometricFactorWeights() const override; + + /** + * @brief Constructs the offset arrays for quadrature points in partial elements. + * + * This method builds the offsets array that maps local element indices to their + * corresponding quadrature point ranges in the flattened data structure. It iterates + * through all elements in the partial set and accumulates the number of quadrature + * points based on the integration rules for each element's geometry type. + * + * The offsets array has size (num_partial_elements + 1), where offsets[i] gives + * the starting index for element i's quadrature points, and offsets[i+1] - offsets[i] + * gives the number of quadrature points for element i. + */ + void ConstructOffsets(); + + /** + * @brief Constructs global offset arrays for full mesh compatibility. + * + * This method builds the global_offsets array that provides offset information + * for all elements in the global mesh, facilitating data mapping between partial + * and full quadrature spaces. When the partial space covers all elements, + * this creates a minimal offset array. Otherwise, it creates a complete mapping + * for all global elements. + * + * Used internally for efficient data transfer operations between partial and + * full quadrature functions. + */ + void ConstructGlobalOffsets(); + + /** + * @brief Main construction method that builds all internal data structures. + * + * This method orchestrates the construction of the PartialQuadratureSpace by + * calling the appropriate sub-construction methods in the correct order: + * 1. ConstructIntRules() - sets up integration rules for the mesh dimension + * 2. ConstructOffsets() - builds local element offset arrays + * 3. ConstructGlobalOffsets() - builds global element offset arrays + * + * This method should be called after all mapping arrays have been initialized. + */ + void Construct(); + + /** + * @brief Constructs local-to-global and global-to-local element mappings. + * + * @param mesh_ Shared pointer to the mesh object + * @param partial_index Boolean array indicating which elements are included in the partial set + * + * This method builds the bidirectional mapping between local element indices (in the partial + * space) and global element indices (in the full mesh). It handles two cases: + * 1. Partial coverage: Creates both local2global and global2local mapping arrays + * 2. Full coverage: Optimizes for the case where all elements are included + * + * The local2global array maps from local element index to global element index. + * The global2local array maps from global element index to local element index (-1 if not + * included). + */ + void ConstructMappings(std::shared_ptr mesh, mfem::Array& partial_index); + +public: + /** + * @brief Create a PartialQuadratureSpace based on the global rules from IntRules. + * + * @param mesh_ Shared pointer to the mesh object + * @param order_ Integration order for automatic quadrature rule selection + * @param partial_index Boolean array indicating which elements to include in the partial set + * + * This constructor creates a PartialQuadratureSpace using automatically selected + * integration rules based on the specified order. The partial_index array determines + * which mesh elements are included in the partial quadrature space. If partial_index + * is empty or includes all elements, optimizations for full-space coverage are applied. + */ + PartialQuadratureSpace(std::shared_ptr mesh_, + int order_, + mfem::Array& partial_index); + + /** + * @brief Create a PartialQuadratureSpace based on the global rules from IntRules (deprecated). + * + * @param mesh_ Raw pointer to the mesh object + * @param order_ Integration order for automatic quadrature rule selection + * @param partial_index Boolean array indicating which elements to include in the partial set + * + * @deprecated Use constructor with std::shared_ptr instead for better memory + * management + */ + [[deprecated("Use constructor with std::shared_ptr instead")]] + PartialQuadratureSpace(mfem::Mesh* mesh_, int order_, mfem::Array& partial_index) + : PartialQuadratureSpace(ptr_utils::borrow_ptr(mesh_), order_, partial_index) {} + + /** + * @brief Constructor with explicit IntegrationRule for single-element-type meshes. + * + * @param mesh_ Shared pointer to the mesh object + * @param ir Integration rule to use for all elements + * @param partial_index Boolean array indicating which elements to include + * + * This constructor creates a PartialQuadratureSpace using a specific integration + * rule rather than deriving rules from an order. It's only valid for meshes + * that have a single element type (e.g., all tetrahedra or all hexahedra). + * + * The constructor verifies that the mesh has at most one geometry type before + * proceeding with construction. It then builds the element mappings and + * constructs the offset arrays for the specified partial element set. + */ + PartialQuadratureSpace(std::shared_ptr mesh_, + const mfem::IntegrationRule& ir, + mfem::Array& partial_index); + + /** + * @brief Create a PartialQuadratureSpace with an IntegrationRule (deprecated). + * + * @param mesh_ Raw pointer to the mesh object + * @param ir Integration rule to use for all elements + * @param partial_index Boolean array indicating which elements to include in the partial set + * + * @deprecated Use constructor with std::shared_ptr instead for better memory + * management + */ + [[deprecated("Use constructor with std::shared_ptr instead")]] + PartialQuadratureSpace(mfem::Mesh* mesh_, + const mfem::IntegrationRule& ir, + mfem::Array& partial_index) + : PartialQuadratureSpace(ptr_utils::borrow_ptr(mesh_), ir, partial_index) {} + + /** + * @brief Constructor that reads PartialQuadratureSpace from an input stream. + * + * @param mesh_ Shared pointer to the mesh object + * @param in Input stream containing serialized PartialQuadratureSpace data + * + * This constructor deserializes a PartialQuadratureSpace from a stream that was + * previously written using the Save() method. It reads the quadrature order + * and element mapping information, then reconstructs all internal data structures. + * + * The expected stream format includes: + * - Header: "PartialQuadratureSpace" + * - Type: "default_quadrature" + * - Order: Integration order + * - PartialIndices: Number of elements followed by local2global mapping + * + * After reading the mapping data, it calls Construct() to build the complete + * quadrature space data structures. + */ + PartialQuadratureSpace(std::shared_ptr mesh_, std::istream& in); + + /** + * @brief Read a PartialQuadratureSpace from the stream (deprecated). + * + * @param mesh_ Raw pointer to the mesh object + * @param in Input stream containing serialized PartialQuadratureSpace data + * + * @deprecated Use constructor with std::shared_ptr instead for better memory + * management + */ + [[deprecated("Use constructor with std::shared_ptr instead")]] + PartialQuadratureSpace(mfem::Mesh* mesh_, std::istream& in) + : PartialQuadratureSpace(ptr_utils::borrow_ptr(mesh_), in) {} + + /** + * @brief Converts a local element index to the corresponding global element index. + * + * @param local_idx Local element index in the partial quadrature space + * @return Global element index in the full mesh, or -1 if invalid local index + * + * This method provides the mapping from the local element indexing scheme used + * within the PartialQuadratureSpace to the global element indexing scheme of + * the underlying mesh. Essential for accessing mesh-level element data. + */ + [[nodiscard]] + int LocalToGlobal(int local_idx) const { + if (local_idx >= 0 && local_idx < local2global.Size()) { + return local2global[local_idx]; + } + return -1; + } + + /** + * @brief Converts a global element index to the corresponding local element index. + * + * @param global_idx Global element index in the full mesh + * @return Local element index in the partial space, or -1 if element not in partial set + * + * This method provides the reverse mapping from global mesh element indices to + * local partial space indices. Returns -1 for elements not included in the partial set. + * Handles the special case where the partial space covers the entire mesh. + */ + [[nodiscard]] + int GlobalToLocal(int global_idx) const { + if (global_idx >= 0 && global_idx < global2local.Size()) { + return global2local[global_idx]; + } else if (global_idx >= 0 && global2local.Size() == 1) { + return global_idx; + } + return -1; + } + + /** + * @brief Get read-only access to the global-to-local mapping array. + * + * @return Const reference to the global2local array + * + * The returned array maps global element indices to local element indices, + * with -1 indicating elements not in the partial set. For optimization, + * when the partial space covers all elements, this array has size 1. + */ + const mfem::Array& GetGlobal2Local() const { + return global2local; + } + + /** + * @brief Get read-only access to the local-to-global mapping array. + * + * @return Const reference to the local2global array + * + * The returned array provides the mapping from local element indices + * (within the partial space) to global element indices (in the full mesh). + */ + const mfem::Array& GetLocal2Global() const { + return local2global; + } + + /** + * @brief Get read-only access to the global offset array. + * + * @return Const reference to the global_offsets array + * + * The global offset array provides quadrature point offset information + * for all elements in the global mesh, facilitating efficient data + * transfer between partial and full quadrature spaces. + */ + const mfem::Array& GetGlobalOffset() const { + return global_offsets; + } + + /** + * @brief Get the number of elements in the local partial space. + * + * @return Number of elements included in this partial quadrature space + * + * This count represents the subset of mesh elements that are active + * in this PartialQuadratureSpace, which may be less than the total + * number of elements in the underlying mesh. + */ + int GetNumLocalElements() const { + return local2global.Size(); + } + + /** + * @brief Check if this partial space covers the entire mesh. + * + * @return True if all mesh elements are included in this partial space + * + * This method returns true when the partial space is actually equivalent + * to a full quadrature space, enabling certain optimizations in data + * handling and memory management. + */ + bool IsFullSpace() const { + return (global2local.Size() == 1); + } + + /** + * @brief Get the element transformation for a local entity index. + * + * @param idx Local element index in the partial space + * @return Pointer to the ElementTransformation for the corresponding global element + * + * This method converts the local element index to a global index and retrieves + * the element transformation from the underlying mesh. The transformation + * contains geometric information needed for integration and finite element assembly. + */ + [[nodiscard]] + virtual mfem::ElementTransformation* GetTransformation(int idx) override { + int global_idx = LocalToGlobal(idx); + return mesh->GetElementTransformation(global_idx); + } + + /** + * @brief Return the geometry type of the entity with local index idx. + * + * @param idx Local element index in the partial space + * @return Geometry type (e.g., Triangle, Quadrilateral, Tetrahedron, Hexahedron) + * + * This method maps the local element index to the global mesh and returns + * the geometric type of that element, which determines the appropriate + * integration rules and basis functions. + */ + [[nodiscard]] + virtual mfem::Geometry::Type GetGeometry(int idx) const override { + int global_idx = LocalToGlobal(idx); + return mesh->GetElementGeometry(global_idx); + } + + /** + * @brief Get the permuted quadrature point index (trivial for element spaces). + * + * @param idx Element index (unused for element quadrature spaces) + * @param iq Quadrature point index + * @return The same quadrature point index (no permutation for elements) + * + * For element quadrature spaces, quadrature point permutation is trivial, + * so this method simply returns the input quadrature point index unchanged. + */ + [[nodiscard]] + virtual int GetPermutedIndex([[maybe_unused]] int idx, int iq) const override { + // For element quadrature spaces, the permutation is trivial + return iq; + } + + /** + * @brief Save the PartialQuadratureSpace to a stream. + * + * @param out Output stream to write the PartialQuadratureSpace data + * + * This method serializes the PartialQuadratureSpace configuration to a stream, + * including the quadrature order and the mapping of partial elements. The output + * format can be read back using the stream constructor. + */ + virtual void Save(std::ostream& out) const override; + + /** + * @brief Returns the element index for the given ElementTransformation. + * + * @param T Reference to an ElementTransformation object + * @return Element index from the transformation (T.ElementNo) + * + * This method extracts the element index directly from the ElementTransformation + * object, providing the interface required by the QuadratureSpaceBase class. + */ + [[nodiscard]] + virtual int GetEntityIndex(const mfem::ElementTransformation& T) const override { + return T.ElementNo; + } + + // Factory methods + + /** + * @brief Factory method to create a shared_ptr PartialQuadratureSpace. + * + * @param mesh Shared pointer to the mesh object + * @param order Integration order for quadrature rules + * @param partial_index Boolean array indicating which elements to include + * @return Shared pointer to the created PartialQuadratureSpace + * + * This factory method provides the recommended way to create PartialQuadratureSpace + * objects with proper memory management using shared_ptr. It handles the construction + * of all internal data structures and mappings. + */ + static std::shared_ptr + Create(std::shared_ptr mesh, int order, mfem::Array partial_index) { + return std::make_shared(std::move(mesh), order, partial_index); + } + + /** + * @brief Factory method to create a shared_ptr PartialQuadratureSpace. + * + * @param mesh Raw pointer to the mesh object + * @param order Integration order for quadrature rules + * @param partial_index Boolean array indicating which elements to include + * @return Shared pointer to the created PartialQuadratureSpace + * + * This factory method provides the recommended way to create PartialQuadratureSpace + * objects with proper memory management using shared_ptr. It handles the construction + * of all internal data structures and mappings. + */ + [[deprecated("Use Create() with std::shared_ptr instead")]] + static std::shared_ptr + Create(mfem::Mesh* mesh, int order, mfem::Array partial_index) { + return std::make_shared( + ptr_utils::borrow_ptr(mesh), order, partial_index); + } +}; + +} // namespace mfem::expt \ No newline at end of file diff --git a/src/models/mechanics_ecmech.cpp b/src/models/mechanics_ecmech.cpp new file mode 100644 index 0000000..23b683d --- /dev/null +++ b/src/models/mechanics_ecmech.cpp @@ -0,0 +1,603 @@ +#include "models/mechanics_ecmech.hpp" + +#include "models/mechanics_model.hpp" +#include "utilities/mechanics_kernels.hpp" +#include "utilities/mechanics_log.hpp" +#include "utilities/unified_logger.hpp" + +#include "ECMech_cases.h" +#include "ECMech_const.h" +#include "RAJA/RAJA.hpp" +#include "mfem.hpp" +#include "mfem/general/forall.hpp" + +#include +#include // cerr +#include // log + +namespace { + +// Sets-up everything for the kernel +// UNCHANGED: This internal function doesn't need modification since it works with raw arrays +void kernel_setup(const int npts, + const int nstatev, + const double dt, + const double temp_k, + const double* vel_grad_array, + const double* stress_array, + const double* state_vars_array, + double* stress_svec_p_array, + double* d_svec_p_array, + double* w_vec_array, + double* vol_ratio_array, + double* eng_int_array, + double* tempk_array, + double* dEff) { + // vgrad is kinda a pain to deal with as a raw 1d array, so we're + // going to just use a RAJA view here. The data is taken to be in col. major format. + // It might be nice to eventually create a type alias for the below or + // maybe something like it. + + const int ind_int_eng = nstatev - ecmech::ne; + const int ind_vols = ind_int_eng - 1; + + const int DIM = 3; + std::array perm{{2, 1, 0}}; + RAJA::Layout layout = RAJA::make_permuted_layout({{ecmech::ndim, ecmech::ndim, npts}}, + perm); + RAJA::View> vgrad_view(vel_grad_array, + layout); + + mfem::forall(npts, [=] MFEM_HOST_DEVICE(int i_pts) { + // Might want to eventually set these all up using RAJA views. It might simplify + // things later on. + // These are our inputs + const double* state_vars = &(state_vars_array[i_pts * nstatev]); + const double* stress = &(stress_array[i_pts * ecmech::nsvec]); + // Here is all of our ouputs + double* eng_int = &(eng_int_array[i_pts * ecmech::ne]); + double* w_vec = &(w_vec_array[i_pts * ecmech::nwvec]); + double* vol_ratio = &(vol_ratio_array[i_pts * ecmech::nvr]); + // A few variables are set up as the 6-vec deviatoric + tr(tens) values + int ind_svecp = i_pts * ecmech::nsvp; + double* stress_svec_p = &(stress_svec_p_array[ind_svecp]); + double* d_svec_p = &(d_svec_p_array[ind_svecp]); + + tempk_array[i_pts] = temp_k; + + for (int i = 0; i < ecmech::ne; i++) { + eng_int[i] = state_vars[ind_int_eng + i]; + } + + // Here we have the skew portion of our velocity gradient as represented as an + // axial vector. + w_vec[0] = 0.5 * (vgrad_view(2, 1, i_pts) - vgrad_view(1, 2, i_pts)); + w_vec[1] = 0.5 * (vgrad_view(0, 2, i_pts) - vgrad_view(2, 0, i_pts)); + w_vec[2] = 0.5 * (vgrad_view(1, 0, i_pts) - vgrad_view(0, 1, i_pts)); + + // Really we're looking at the negative of J but this will do... + double d_mean = -ecmech::onethird * (vgrad_view(0, 0, i_pts) + vgrad_view(1, 1, i_pts) + + vgrad_view(2, 2, i_pts)); + // The 1st 6 components are the symmetric deviatoric portion of our velocity gradient + // The last value is simply the trace of the deformation rate + d_svec_p[0] = vgrad_view(0, 0, i_pts) + d_mean; + d_svec_p[1] = vgrad_view(1, 1, i_pts) + d_mean; + d_svec_p[2] = vgrad_view(2, 2, i_pts) + d_mean; + d_svec_p[3] = 0.5 * (vgrad_view(2, 1, i_pts) + vgrad_view(1, 2, i_pts)); + d_svec_p[4] = 0.5 * (vgrad_view(2, 0, i_pts) + vgrad_view(0, 2, i_pts)); + d_svec_p[5] = 0.5 * (vgrad_view(1, 0, i_pts) + vgrad_view(0, 1, i_pts)); + d_svec_p[6] = -3.0 * d_mean; + + double d_vecd_sm[ecmech::ntvec]; + ecmech::svecToVecd(d_vecd_sm, d_svec_p); + dEff[i_pts] = ecmech::vecd_Deff(d_vecd_sm); + + vol_ratio[0] = state_vars[ind_vols]; + vol_ratio[1] = vol_ratio[0] * exp(d_svec_p[ecmech::iSvecP] * dt); + vol_ratio[3] = vol_ratio[1] - vol_ratio[0]; + vol_ratio[2] = vol_ratio[3] / (dt * 0.5 * (vol_ratio[0] + vol_ratio[1])); + + for (int i = 0; i < ecmech::nsvec; i++) { + stress_svec_p[i] = stress[i]; + } + + double stress_mean = -ecmech::onethird * (stress[0] + stress[1] + stress[2]); + stress_svec_p[0] += stress_mean; + stress_svec_p[1] += stress_mean; + stress_svec_p[2] += stress_mean; + stress_svec_p[ecmech::iSvecP] = stress_mean; + }); // end of npts loop +} // end of set-up func + +// Retrieves the stress and reorders it into the desired 6 vec format. A copy of that vector +// is sent back to the CPU for the time being. It also stores all of the state variables into their +// appropriate vector. Finally, it saves off the material tangent stiffness vector. In the future, +// if PA is used then the 4D 3x3x3x3 tensor is saved off rather than the 6x6 2D matrix. +// UNCHANGED: This internal function doesn't need modification since it works with raw arrays +void kernel_postprocessing(const int npts, + const int nstatev, + const double dt, + const double* dEff, + const double* stress_svec_p_array, + const double* vol_ratio_array, + const double* eng_int_array, + const double* beg_state_vars_array, + double* state_vars_array, + double* stress_array, + double* ddsdde_array, + AssemblyType assembly) { + const int ind_int_eng = nstatev - ecmech::ne; + const int ind_pl_work = ecmech::evptn::iHistA_flowStr; + const int ind_vols = ind_int_eng - 1; + + mfem::forall(npts, [=] MFEM_HOST_DEVICE(int i_pts) { + // These are our outputs + double* state_vars = &(state_vars_array[i_pts * nstatev]); + const double* beg_state_vars = &(beg_state_vars_array[i_pts * nstatev]); + double* stress = &(stress_array[i_pts * ecmech::nsvec]); + // Here is all of our ouputs + const double* eng_int = &(eng_int_array[i_pts * ecmech::ne]); + const double* vol_ratio = &(vol_ratio_array[i_pts * ecmech::nvr]); + // A few variables are set up as the 6-vec deviatoric + tr(tens) values + int ind_svecp = i_pts * ecmech::nsvp; + const double* stress_svec_p = &(stress_svec_p_array[ind_svecp]); + + // We need to update our state variables to include the volume ratio and + // internal energy portions + state_vars[ind_vols] = vol_ratio[1]; + for (int i = 0; i < ecmech::ne; i++) { + state_vars[ind_int_eng + i] = eng_int[i]; + } + + if (dEff[i_pts] > ecmech::idp_tiny_sqrt) { + state_vars[ind_pl_work] *= dEff[i_pts] * dt; + } else { + state_vars[ind_pl_work] = 0.0; + } + state_vars[ind_pl_work] += beg_state_vars[ind_pl_work]; + + // Here we're converting back from our deviatoric + pressure representation of our + // Cauchy stress back to the Voigt notation of stress. + double stress_mean = -stress_svec_p[ecmech::iSvecP]; + for (int i = 0; i < ecmech::nsvec; i++) { + stress[i] = stress_svec_p[i]; + } + + stress[0] += stress_mean; + stress[1] += stress_mean; + stress[2] += stress_mean; + + double* ddsdde = &(ddsdde_array[i_pts * ecmech::nsvec * ecmech::nsvec]); + for (int i = 0; i < ecmech::nsvec * ecmech::nsvec; ++i) { + ddsdde[i] *= dt; + } + }); // end of npts loop + + // No need to transpose this if running on the GPU and doing EA + if ((assembly == AssemblyType::EA) and mfem::Device::Allows(mfem::Backend::DEVICE_MASK)) { + return; + } else { + mfem::forall(npts, [=] MFEM_HOST_DEVICE(int i_pts) { + // ExaCMech saves this in Row major, so we need to get out the transpose. + // The good thing is we can do this all in place no problem. + double* ddsdde = &(ddsdde_array[i_pts * ecmech::nsvec * ecmech::nsvec]); + for (int i = 0; i < ecmech::nsvec; ++i) { + for (int j = i + 1; j < ecmech::nsvec; ++j) { + double tmp = ddsdde[(ecmech::nsvec * j) + i]; + ddsdde[(ecmech::nsvec * j) + i] = ddsdde[(ecmech::nsvec * i) + j]; + ddsdde[(ecmech::nsvec * i) + j] = tmp; + } + } + }); + } +} // end of post-processing func + +// The different CPU, OpenMP, and GPU kernels aren't needed here, since they're +// defined in ExaCMech itself. +// UNCHANGED: This internal function doesn't need modification +void kernel(const ecmech::matModelBase* mat_model_base, + const int npts, + const double dt, + double* state_vars_array, + double* stress_svec_p_array, + double* d_svec_p_array, + double* w_vec_array, + double* ddsdde_array, + double* vol_ratio_array, + double* eng_int_array, + double* tempk_array, + double* sdd_array) { + mat_model_base->getResponseECM(dt, + d_svec_p_array, + w_vec_array, + vol_ratio_array, + eng_int_array, + stress_svec_p_array, + state_vars_array, + tempk_array, + sdd_array, + ddsdde_array, + npts); +} + +} // namespace + +// NEW CONSTRUCTOR IMPLEMENTATION: Much simpler parameter list +// The key insight is that instead of passing in all QuadratureFunctions and material properties, +// we only pass in the essential ExaCMech-specific parameters and use the region ID to access +// data through SimulationState when needed. +ExaCMechModel::ExaCMechModel(const int region, + int n_state_vars, + double temp_k, + ecmech::ExecutionStrategy accel, + const std::string& mat_model_name, + std::shared_ptr sim_state) + : ExaModel(region, n_state_vars, sim_state), // Call base constructor with region + temp_k(temp_k), accel(accel) { + // The setup process remains the same, but now we get data from SimulationState + SetupDataStructures(); + SetupModel(mat_model_name); +} + +// UPDATED: SetupDataStructures now gets QuadratureFunction info from SimulationState +// instead of using direct member variable access +void ExaCMechModel::SetupDataStructures() { + // Instead of using stress0 member variable, get it from SimulationState + auto stress0 = m_sim_state->GetQuadratureFunction("cauchy_stress_beg", m_region); + + // First find the total number of points that we're dealing with so nelems * nqpts + const int vdim = stress0->GetVDim(); + const int size = stress0->Size(); + const int npts = size / vdim; + + // Now initialize all of the vectors that we'll be using with our class + // These remain as member variables since they're working space, not persistent data storage + vel_grad_array = std::make_unique(npts * ecmech::ndim * ecmech::ndim, + mfem::Device::GetMemoryType()); + eng_int_array = std::make_unique(npts * ecmech::ne, + mfem::Device::GetMemoryType()); + w_vec_array = std::make_unique(npts * ecmech::nwvec, + mfem::Device::GetMemoryType()); + vol_ratio_array = std::make_unique(npts * ecmech::nvr, + mfem::Device::GetMemoryType()); + stress_svec_p_array = std::make_unique(npts * ecmech::nsvp, + mfem::Device::GetMemoryType()); + d_svec_p_array = std::make_unique(npts * ecmech::nsvp, + mfem::Device::GetMemoryType()); + tempk_array = std::make_unique(npts, mfem::Device::GetMemoryType()); + sdd_array = std::make_unique(npts * ecmech::nsdd, mfem::Device::GetMemoryType()); + eff_def_rate = std::make_unique(npts, mfem::Device::GetMemoryType()); + + // If we're using a Device we'll want all of these vectors on it and staying there. + // Also, note that UseDevice() only returns a boolean saying if it's on the device or not + // rather than telling the vector whether or not it needs to lie on the device. + vel_grad_array->UseDevice(true); + *vel_grad_array = 0.0; + eng_int_array->UseDevice(true); + *eng_int_array = 0.0; + w_vec_array->UseDevice(true); + *w_vec_array = 0.0; + vol_ratio_array->UseDevice(true); + *vol_ratio_array = 0.0; + stress_svec_p_array->UseDevice(true); + *stress_svec_p_array = 0.0; + d_svec_p_array->UseDevice(true); + *d_svec_p_array = 0.0; + tempk_array->UseDevice(true); + *tempk_array = 0.0; + sdd_array->UseDevice(true); + *sdd_array = 0.0; + eff_def_rate->UseDevice(true); + *eff_def_rate = 0.0; +} + +void ECMechSetupQuadratureFuncStatePair(const int region_id, + const std::string& mat_model_name, + std::shared_ptr sim_state) { + // First aspect is setting up our various map structures + auto index_map = ecmech::modelParamIndexMap(mat_model_name); + // additional terms we need to add + index_map["num_volumes"] = 1; + index_map["index_volume"] = index_map["index_slip_rates"] + index_map["num_slip_system"]; + index_map["num_internal_energy"] = ecmech::ne; + index_map["index_internal_energy"] = index_map["index_volume"] + index_map["num_volumes"]; + + { + // Set up the quadrature function mapping for this model + // This maps variable names to their locations within the state variable vector + std::string s_dplas_eff = "eq_pl_strain_rate"; + std::string s_eq_pl_str = "eq_pl_strain"; + std::string s_pl_work = "plastic_work"; + std::string s_quats = "quats"; + std::string s_gdot = "shear_rate"; + std::string s_hard = "hardness"; + std::string s_ieng = "internal_energy"; + std::string s_rvol = "relative_volume"; + std::string s_est = "elastic_strain"; + + std::pair i_sre = std::make_pair(index_map["index_effective_shear_rate"], 1); + std::pair i_se = std::make_pair(index_map["index_effective_shear"], 1); + std::pair i_plw = std::make_pair(index_map["index_flow_strength"], 1); + std::pair i_q = std::make_pair(index_map["index_lattice_ori"], 4); + std::pair i_g = std::make_pair(index_map["index_slip_rates"], + index_map["num_slip_system"]); + std::pair i_h = std::make_pair(index_map["index_hardness"], + index_map["num_hardening"]); + std::pair i_en = std::make_pair(index_map["index_internal_energy"], ecmech::ne); + std::pair i_rv = std::make_pair(index_map["index_volume"], 1); + std::pair i_est = std::make_pair(index_map["index_dev_elas_strain"], + ecmech::ntvec); + + sim_state->AddQuadratureFunctionStatePair(s_dplas_eff, i_sre, region_id); + sim_state->AddQuadratureFunctionStatePair(s_eq_pl_str, i_se, region_id); + sim_state->AddQuadratureFunctionStatePair(s_pl_work, i_plw, region_id); + sim_state->AddQuadratureFunctionStatePair(s_quats, i_q, region_id); + sim_state->AddQuadratureFunctionStatePair(s_gdot, i_g, region_id); + sim_state->AddQuadratureFunctionStatePair(s_hard, i_h, region_id); + sim_state->AddQuadratureFunctionStatePair(s_ieng, i_en, region_id); + sim_state->AddQuadratureFunctionStatePair(s_rvol, i_rv, region_id); + sim_state->AddQuadratureFunctionStatePair(s_est, i_est, region_id); + } +} + +// UPDATED: SetupModel now gets material properties from SimulationState instead of matProps member +void ExaCMechModel::SetupModel(const std::string& mat_model_name) { + // First aspect is setting up our various map structures + index_map = ecmech::modelParamIndexMap(mat_model_name); + // additional terms we need to add + index_map["num_volumes"] = 1; + index_map["index_volume"] = index_map["index_slip_rates"] + index_map["num_slip_system"]; + index_map["num_internal_energy"] = ecmech::ne; + index_map["index_internal_energy"] = index_map["index_volume"] + index_map["num_volumes"]; + + ECMechSetupQuadratureFuncStatePair(m_region, mat_model_name, m_sim_state); + + // Now we can create our model + mat_model_base = ecmech::makeMatModel(mat_model_name); + // and update our model strides from the default values + size_t num_state_vars = index_map["num_hist"] + ecmech::ne + 1; + std::vector strides; + // Deformation rate stride + strides.push_back(ecmech::nsvp); + // Spin rate stride + strides.push_back(ecmech::ndim); + // Volume ratio stride + strides.push_back(ecmech::nvr); + // Internal energy stride + strides.push_back(ecmech::ne); + // Stress vector stride + strides.push_back(ecmech::nsvp); + // History variable stride + strides.push_back(num_state_vars); + // Temperature stride + strides.push_back(1); + // SDD stride + strides.push_back(ecmech::nsdd); + // Update our stride values from the default as our history strides are different + mat_model_base->updateStrides(strides); + + // UPDATED: Get material properties from SimulationState instead of matProps member variable + const auto& mat_props = GetMaterialProperties(); + + // Now get out the parameters to instantiate our history variables + // Opts and strs are just empty vectors of int and strings + std::vector params; + std::vector opts; + std::vector strs; + + // Convert the material properties from SimulationState to the format ExaCMech expects + for (const auto& prop : mat_props) { + params.push_back(prop); + } + + // We really shouldn't see this change over time at least for our applications. + mat_model_base->initFromParams(opts, params, strs); + mat_model_base->complete(); + mat_model_base->setExecutionStrategy(accel); + + std::vector histInit; + { + std::vector names; + std::vector plot; + std::vector state; + mat_model_base->getHistInfo(names, histInit, plot, state); + } + + InitStateVars(histInit); +} + +// UPDATED: InitStateVars now gets matVars0 from SimulationState instead of member variable +void ExaCMechModel::InitStateVars(std::vector hist_init) { + mfem::Vector histInit(static_cast(index_map["num_hist"]), mfem::Device::GetMemoryType()); + histInit.UseDevice(true); + histInit.HostReadWrite(); + assert(hist_init.size() == index_map["num_hist"]); + + for (size_t i = 0; i < hist_init.size(); i++) { + histInit(static_cast(i)) = hist_init.at(i); + } + + const double* histInit_vec = histInit.Read(); + + // UPDATED: Get matVars0 from SimulationState instead of using member variable + auto matVars0 = m_sim_state->GetQuadratureFunction("state_var_beg", m_region); + double* state_vars = matVars0->ReadWrite(); + + const int qf_size = (matVars0->Size()) / (matVars0->GetVDim()); + const size_t vdim = static_cast(matVars0->GetVDim()); + + const size_t ind_dp_eff = index_map["index_effective_shear_rate"]; + const size_t ind_eql_pl_strain = index_map["index_effective_shear"]; + const size_t ind_pl_work = index_map["index_flow_strength"]; + const size_t ind_num_evals = index_map["index_num_func_evals"]; + const size_t ind_hardness = index_map["index_hardness"]; + const size_t ind_vols = index_map["index_volume"]; + const size_t ind_int_eng = index_map["index_internal_energy"]; + const size_t ind_dev_elas_strain = index_map["index_dev_elas_strain"]; + const size_t ind_gdot = index_map["index_slip_rates"]; + const size_t num_slip = index_map["num_slip_system"]; + const size_t num_hardness = index_map["num_hardening"]; + + mfem::forall(qf_size, [=] MFEM_HOST_DEVICE(int i) { + const size_t ind = static_cast(i) * vdim; + + state_vars[ind + ind_dp_eff] = histInit_vec[ind_dp_eff]; + state_vars[ind + ind_eql_pl_strain] = histInit_vec[ind_eql_pl_strain]; + state_vars[ind + ind_pl_work] = histInit_vec[ind_pl_work]; + state_vars[ind + ind_num_evals] = histInit_vec[ind_num_evals]; + state_vars[ind + ind_vols] = 1.0; + + for (size_t j = 0; j < num_hardness; j++) { + state_vars[ind + ind_hardness + j] = histInit_vec[ind_hardness + j]; + } + + for (size_t j = 0; j < ecmech::ne; j++) { + state_vars[ind + ind_int_eng + j] = 0.0; + } + + for (size_t j = 0; j < ecmech::ntvec; j++) { + state_vars[ind + ind_dev_elas_strain + j] = histInit_vec[ind_dev_elas_strain + j]; + } + + for (size_t j = 0; j < num_slip; j++) { + state_vars[ind + ind_gdot + j] = histInit_vec[ind_gdot + j]; + } + }); + m_sim_state->GetQuadratureFunction("state_var_end", m_region)->operator=(*matVars0.get()); +} + +// UPDATED: Our model set-up makes use of several preprocessing kernels, +// the actual material model kernel, and finally a post-processing kernel. +// Now uses accessor methods to get QuadratureFunctions from SimulationState +void ExaCMechModel::ModelSetup(const int nqpts, + const int nelems, + const int /*space_dim*/, + const int nnodes, + const mfem::Vector& jacobian, + const mfem::Vector& loc_grad, + const mfem::Vector& vel) { + auto& logger = exaconstit::UnifiedLogger::get_instance(); + std::string material_log = logger.get_material_log_filename("exacmech", m_region); + exaconstit::UnifiedLogger::ScopedCapture capture(material_log); + + const int nstatev = num_state_vars; + + const double* jacobian_array = jacobian.Read(); + const double* loc_grad_array = loc_grad.Read(); + const double* vel_array = vel.Read(); + + const double dt = m_sim_state->GetDeltaTime(); + + // Get the partial quadrature space information for this region + auto stress0 = m_sim_state->GetQuadratureFunction("cauchy_stress_beg", m_region); + auto qspace = stress0->GetPartialSpaceShared(); + + // Determine the actual number of local elements and mapping + const int* local2global_ptr = nullptr; + int local_nelems = nelems; // Default to global count + + if (!qspace->IsFullSpace()) { + // This is a true partial space - get the local element count and mapping + const auto& local2global = qspace->GetLocal2Global(); + local2global_ptr = local2global.Read(); + local_nelems = local2global.Size(); + } + + // Calculate the correct number of points for this region + const int npts = nqpts * local_nelems; + + // UPDATED: Here we call an initialization function which sets the end step stress + // and state variable variables to the initial time step values. + double* state_vars_array = + m_sim_state->GetQuadratureFunction("state_var_end", m_region)->ReadWrite(); + auto matVars0 = m_sim_state->GetQuadratureFunction("state_var_beg", m_region); + const double* state_vars_beg = matVars0->Read(); + double* stress_array = + m_sim_state->GetQuadratureFunction("cauchy_stress_end", m_region)->ReadWrite(); + + // UPDATED: Get matGrad from SimulationState instead of using member variable + auto matGrad_qf = m_sim_state->GetQuadratureFunction("tangent_stiffness", m_region); + *matGrad_qf = 0.0; + double* ddsdde_array = matGrad_qf->ReadWrite(); + + // All of these variables are stored on the material model class using + // the vector class - these remain unchanged since they're working space + *vel_grad_array = 0.0; + double* vel_grad_array_data = vel_grad_array->ReadWrite(); + double* stress_svec_p_array_data = stress_svec_p_array->ReadWrite(); + double* d_svec_p_array_data = d_svec_p_array->ReadWrite(); + double* w_vec_array_data = w_vec_array->ReadWrite(); + double* vol_ratio_array_data = vol_ratio_array->ReadWrite(); + double* eng_int_array_data = eng_int_array->ReadWrite(); + double* tempk_array_data = tempk_array->ReadWrite(); + double* sdd_array_data = sdd_array->ReadWrite(); + + double* dEff = eff_def_rate->Write(); + + CALI_MARK_BEGIN("ecmech_setup"); + + // UPDATED: Call GradCalc with proper element counts and optional mapping + exaconstit::kernel::GradCalc(nqpts, + local_nelems, + nelems, + nnodes, + jacobian_array, + loc_grad_array, + vel_array, + vel_grad_array_data, + local2global_ptr); + + kernel_setup(npts, + nstatev, + dt, + temp_k, + vel_grad_array_data, + stress_array, + state_vars_array, + stress_svec_p_array_data, + d_svec_p_array_data, + w_vec_array_data, + vol_ratio_array_data, + eng_int_array_data, + tempk_array_data, + dEff); + CALI_MARK_END("ecmech_setup"); + + CALI_MARK_BEGIN("ecmech_kernel"); + kernel(mat_model_base, + npts, + dt, + state_vars_array, + stress_svec_p_array_data, + d_svec_p_array_data, + w_vec_array_data, + ddsdde_array, + vol_ratio_array_data, + eng_int_array_data, + tempk_array_data, + sdd_array_data); + CALI_MARK_END("ecmech_kernel"); + + CALI_MARK_BEGIN("ecmech_postprocessing"); + kernel_postprocessing(npts, + nstatev, + dt, + dEff, + stress_svec_p_array_data, + vol_ratio_array_data, + eng_int_array_data, + state_vars_beg, + state_vars_array, + stress_array, + ddsdde_array, + assembly); + CALI_MARK_END("ecmech_postprocessing"); + + // Fill global data structures with region-specific results + auto global_stress = m_sim_state->GetQuadratureFunction("cauchy_stress_end"); + auto stress_final = m_sim_state->GetQuadratureFunction("cauchy_stress_end", m_region); + stress_final->FillQuadratureFunction(*global_stress); + + auto global_tangent_stiffness = m_sim_state->GetQuadratureFunction("tangent_stiffness"); + matGrad_qf->FillQuadratureFunction(*global_tangent_stiffness); +} // End of ModelSetup function diff --git a/src/models/mechanics_ecmech.hpp b/src/models/mechanics_ecmech.hpp new file mode 100644 index 0000000..566ff18 --- /dev/null +++ b/src/models/mechanics_ecmech.hpp @@ -0,0 +1,193 @@ +#pragma once + +#include "models/mechanics_model.hpp" + +#include "ECMech_const.h" +#include "ECMech_matModelBase.h" +#include "mfem.hpp" + +#include + +/** + * @brief Sets up ExaCMech Model quadrature function state pairs + * + * @param region_id - the region id associated with this model + * @param mat_model_name - the exacmech model shortcut name + * @param sim_stae - the SimulationState generally associated with the ExaModels and which will + * contain the quadrature function state pair + */ +void ECMechSetupQuadratureFuncStatePair(const int region_id, + const std::string& mat_model_name, + std::shared_ptr sim_state); + +/** + * @brief ExaCMech crystal plasticity material model implementation + * + * @details Implementation of ExaModel for ExaCMech crystal plasticity material models. + * Supports various crystal plasticity models (FCC, BCC, HCP) with different hardening + * laws and can execute on CPU, OpenMP, or GPU. + */ +class ExaCMechModel : public ExaModel { +protected: + /** @brief Current temperature in Kelvin degrees */ + double temp_k; + + /** + * @brief Pointer to ExaCMech material model base class instance + * + * @details The child classes to this class will have also have another variable + * that actually contains the real material model that is then dynamically casted + * to this base class during the instantiation of the class. + */ + ecmech::matModelBase* mat_model_base; + + /** @brief Execution strategy (CPU/OpenMP/GPU) for this model */ + ecmech::ExecutionStrategy accel; + + // RETAINED: Temporary variables that we'll be making use of when running our models. + // These are working space arrays specific to the ExaCMech model execution, + // not data storage, so they remain as member variables + + /** @brief Velocity gradient tensor components working array */ + std::unique_ptr vel_grad_array; + + /** @brief Internal energy components working array */ + std::unique_ptr eng_int_array; + + /** @brief Spin tensor components working array */ + std::unique_ptr w_vec_array; + + /** @brief Volume ratio data working array */ + std::unique_ptr vol_ratio_array; + + /** @brief Stress vector in pressure-deviatoric form working array */ + std::unique_ptr stress_svec_p_array; + + /** @brief Deformation rate vector in pressure-deviatoric form working array */ + std::unique_ptr d_svec_p_array; + + /** @brief Temperature array */ + std::unique_ptr tempk_array; + + /** @brief Symmetric deformation rate tensor working array */ + std::unique_ptr sdd_array; + + /** @brief Effective deformation rate working array */ + std::unique_ptr eff_def_rate; + + /** + * @brief Mapping from variable names to their locations within the state variable vector + * + * @details This is ExaCMech-specific and helps locate variables within the large state vector. + * Enables efficient access to specific quantities like slip rates, hardening variables, etc. + */ + std::map index_map; + +public: + /** + * @brief Construct an ExaCMech material model instance + * + * @param region Which material region this model manages (key for SimulationState access) + * @param n_state_vars Number of state variables + * @param temp_k Temperature in Kelvin + * @param accel Execution strategy (CPU/OpenMP/GPU) + * @param mat_model_name ExaCMech model name (e.g., "FCC_PowerVoce", "BCC_KMBalD") + * @param sim_state Reference to simulation state for data access + * + * @details Creates an ExaCMech material model instance for a specific region. + * Initializes working space arrays and sets up the ExaCMech material model based + * on the provided model name. + */ + ExaCMechModel(const int region, + int n_state_vars, + double temp_k, + ecmech::ExecutionStrategy accel, + const std::string& mat_model_name, + std::shared_ptr sim_state); + + /** + * @brief Destructor - cleans up working arrays and ExaCMech model instance + * + * @details Deallocates all dynamically allocated working space arrays and + * the ExaCMech material model instance. + */ + ~ExaCMechModel() = default; + + /** + * @brief Initialize working space arrays required for ExaCMech calculations + * + * @details Arrays are sized based on the number of quadrature points and allocated + * on the appropriate device (CPU/GPU). Instead of using stress0 member variable, + * gets it from SimulationState. + */ + void SetupDataStructures(); + + /** + * @brief Create the appropriate ExaCMech material model instance + * + * @param mat_model_name Name of the ExaCMech material model to instantiate + * + * @details Creates the appropriate ExaCMech material model instance based on the + * model name and sets up the index mapping for state variables. + */ + void SetupModel(const std::string& mat_model_name); + + /** + * @brief Initialize state variables at all quadrature points + * + * @param hist_init Initial values for state variables + * + * @details Initializes state variables at all quadrature points with the provided + * initial values. Sets up: + * - Effective shear rate and strain + * - Plastic work and flow strength + * - Crystal orientations (quaternions) + * - Slip rates and hardening variables + * - Internal energy and volume ratios + */ + void InitStateVars(std::vector hist_init); + + /** + * @brief Main ExaCMech model execution method + * + * @param nqpts Number of quadrature points per element + * @param nelems Number of elements in this batch + * @param space_dim Spatial dimension (unused in current implementation) + * @param nnodes Number of nodes per element + * @param jacobian Jacobian transformation matrices for elements + * @param loc_grad Local gradient operators + * @param vel Velocity field at elemental level + * + * @details This model takes in the velocity, det(jacobian), and local_grad/jacobian. + * It then computes velocity gradient symm and skw tensors and passes that to our + * material model in order to get out our Cauchy stress and the material tangent + * matrix (d \sigma / d Vgrad_{sym}). It also updates all of the state variables + * that live at the quadrature pts. + * + * Implements the three-stage process: + * 1. **Preprocessing**: Computes velocity gradients, deformation rates, and other kinematic + * quantities + * 2. **Material Model**: Calls ExaCMech crystal plasticity kernel to compute stress and tangent + * stiffness + * 3. **Postprocessing**: Converts results to appropriate format and updates state variables + * + * IMPLEMENTATION NOTE: This method's signature remains unchanged, but internally + * it uses the new accessor methods to get QuadratureFunctions from SimulationState + */ + void ModelSetup(const int nqpts, + const int nelems, + const int /*space_dim*/, + const int nnodes, + const mfem::Vector& jacobian, + const mfem::Vector& loc_grad, + const mfem::Vector& vel) override; + + /** + * @brief Update model state variables after solution convergence + * + * @details Empty implementation since ExaCMech handles state variable updates + * internally during ModelSetup. If we needed to do anything to our state variables + * once things are solved for we would do that here. + */ + virtual void UpdateModelVars() override {} +}; \ No newline at end of file diff --git a/src/models/mechanics_model.cpp b/src/models/mechanics_model.cpp new file mode 100644 index 0000000..1f6e0b2 --- /dev/null +++ b/src/models/mechanics_model.cpp @@ -0,0 +1,27 @@ +#include "models/mechanics_model.hpp" + +#include "utilities/mechanics_log.hpp" + +#include "RAJA/RAJA.hpp" +#include "mfem.hpp" +#include "mfem/general/forall.hpp" + +#include +#include // cerr +#include // log + +// NEW CONSTRUCTOR: Much simpler parameter list focused on essential information +// The region parameter is key - it tells this model instance which material region +// it should manage, enabling proper data access through SimulationState +ExaModel::ExaModel(const int region, int n_state_vars, std::shared_ptr sim_state) + : num_state_vars(n_state_vars), m_region(region), + assembly(sim_state->GetOptions().solvers.assembly), m_sim_state(sim_state) {} + +// Get material properties for this region from SimulationState +// This replaces direct access to the matProps vector member variable +const std::vector& ExaModel::GetMaterialProperties() const { + std::string region_name = m_sim_state->GetRegionName(m_region); + // Note: You'll need to expose this method in SimulationState or make it accessible + // For now, assuming there's a public getter or friend access + return m_sim_state->GetMaterialProperties(region_name); +} diff --git a/src/models/mechanics_model.hpp b/src/models/mechanics_model.hpp new file mode 100644 index 0000000..93e10d5 --- /dev/null +++ b/src/models/mechanics_model.hpp @@ -0,0 +1,137 @@ +#ifndef MECHANICS_MODEL +#define MECHANICS_MODEL + +#include "options/option_parser_v2.hpp" +#include "sim_state/simulation_state.hpp" + +#include "mfem.hpp" + +#include +#include +#include + +/** + * @brief Computes the beginning step deformation gradient and stores it in a quadrature function + * + * @param qf Pointer to the quadrature function where deformation gradient will be stored + * @param fes Pointer to the parallel finite element space for the mesh + * @param x0 Current nodal coordinates vector + * + * @details This function computes the incremental deformation gradient at each quadrature point by: + * 1. Looping over all elements in the finite element space + * 2. For each element, computing shape function gradients and element Jacobians + * 3. Computing the incremental deformation gradient as the transpose multiplication of element + * coordinates with shape function gradients + * 4. Updating the beginning step deformation gradient by multiplying with the previous gradient + * 5. Storing results in the provided quadrature function + */ +void computeDefGrad(mfem::QuadratureFunction* qf, + mfem::ParFiniteElementSpace* fes, + mfem::Vector& x0); + +/** + * @brief Base class for all material constitutive models in ExaConstit + * + * @details Provides a unified interface for different material model implementations + * including ExaCMech crystal plasticity models and UMAT interfaces. This class enables + * multi-material simulations by using region identifiers to access appropriate data + * subsets from SimulationState. + * + * The class follows a three-stage execution pattern: + * 1. Setup kernel: Computes kinematic quantities needed by the material model + * 2. Material kernel: Executes the actual constitutive model calculations + * 3. Post-processing kernel: Formats and stores results in appropriate data structures + */ +class ExaModel { +public: + /** @brief Number of state variables required by this material model */ + int num_state_vars; + +protected: + /** @brief Region identifier for this model instance - used to access region-specific data from + * SimulationState */ + int m_region; + /** @brief Assembly type specification (Full Assembly, Partial Assembly, or Element Assembly) */ + AssemblyType assembly; + /** @brief Reference to simulation state for accessing quadrature functions and other simulation + * data */ + std::shared_ptr m_sim_state; + // --------------------------------------------------------------------------- + +public: + /** + * @brief Construct a base ExaModel with region-specific capabilities + * + * @param region Material region identifier that this model instance manages + * @param n_state_vars Number of state variables required by this material model + * @param sim_state Reference to the simulation state for accessing region-specific data + * + * @details The region parameter enables multi-material simulations by allowing each + * model instance to access the correct data subset from SimulationState. + */ + ExaModel(const int region, int n_state_vars, std::shared_ptr sim_state); + + /** + * @brief Virtual destructor to ensure proper cleanup of derived class resources + */ + virtual ~ExaModel() {} + + /** + * @brief Get material properties for this model's region + * + * @return Constant reference to the material properties vector for this model's region + * + * @details Provides access to region-specific material properties, replacing direct + * access to material property vectors. This enables proper encapsulation and + * multi-material support. + */ + const std::vector& GetMaterialProperties() const; + + /** + * @brief Returns material model region id + * + * @return material model region id + */ + int GetRegionID() const { + return m_region; + } + + /** + * @brief Main material model execution method - must be implemented by all derived classes + * + * @param nqpts Number of quadrature points per element + * @param nelems Number of elements in this batch + * @param space_dim Spatial dimension (2D or 3D) + * @param nnodes Number of nodes per element + * @param jacobian Jacobian transformation matrices for elements + * @param loc_grad Local gradient operators + * @param vel Velocity field at elemental level (space_dim * nnodes * nelems) + * + * @details This function is responsible for running the entire model and consists of 3 + * stages/kernels: + * 1. A set-up kernel/stage that computes all of the needed values for the material model + * 2. A kernel that runs the material model (a t = 0 version of this will exist as well) + * 3. A post-processing kernel/stage that does everything after the kernel + * + * By having this unified function, we only need to write one integrator for everything. + * It also allows us to run these models on the GPU even if the rest of the assembly + * operation can't be there yet. If UMATs are used then these operations won't occur on the GPU. + */ + virtual void ModelSetup(const int nqpts, + const int nelems, + const int space_dim, + const int nnodes, + const mfem::Vector& jacobian, + const mfem::Vector& loc_grad, + const mfem::Vector& vel) = 0; + + /** + * @brief Update model state variables after a converged solution + * + * @details Default implementation does nothing; derived classes override if state + * variable updates are needed after successful solution convergence. + */ + virtual void UpdateModelVars() = 0; +}; + +#endif \ No newline at end of file diff --git a/src/models/mechanics_multi_model.cpp b/src/models/mechanics_multi_model.cpp new file mode 100644 index 0000000..1ee98c2 --- /dev/null +++ b/src/models/mechanics_multi_model.cpp @@ -0,0 +1,282 @@ +#include "models/mechanics_multi_model.hpp" + +#include "mfem_expt/partial_qfunc.hpp" +#include "mfem_expt/partial_qspace.hpp" +#include "models/mechanics_ecmech.hpp" +#include "models/mechanics_umat.hpp" +#include "umats/unified_umat_loader.hpp" +#include "utilities/dynamic_function_loader.hpp" +#include "utilities/mechanics_log.hpp" +#include "utilities/unified_logger.hpp" + +#include +#include +#include + +namespace fs = std::filesystem; + +/** + * @brief Resolve UMAT library paths with search path support + * + * @param library_path Relative or absolute path to UMAT library + * @param search_paths List of directories to search for the library + * @return Resolved absolute path to the library + * + * @details Resolves UMAT library paths by: + * 1. Using absolute paths as-is + * 2. Searching through provided search paths for relative paths + * 3. Checking current directory as fallback + * 4. Warning if library is not found + */ +fs::path resolveUmatLibraryPath(const fs::path& library_path, + const std::vector& search_paths) { + // If absolute path, use as-is + if (fs::path(library_path).is_absolute()) { + return library_path; + } + + // Search in specified paths + for (const auto& search_path : search_paths) { + auto full_path = fs::path(search_path) / library_path; + if (fs::exists(full_path)) { + return full_path.string(); + } + } + + // Try current directory + if (fs::exists(library_path)) { + return fs::absolute(library_path); + } + + MFEM_WARNING_0("Warning: UMAT library not found: " << library_path); + return library_path; // Return original path, let loader handle the error +} + +/** + * @brief Convert string-based load strategy to enum + * + * @param strategy_str String representation of load strategy + * @return Corresponding LoadStrategy enum value + * + * @details Converts string-based load strategy specifications from configuration files + * to the appropriate enum values. Supports "persistent", "load_on_setup", and "lazy_load" + * strategies. + */ +inline exaconstit::LoadStrategy stringToLoadStrategy(const std::string& strategy_str) { + if (strategy_str == "persistent") + return exaconstit::LoadStrategy::PERSISTENT; + if (strategy_str == "load_on_setup") + return exaconstit::LoadStrategy::LOAD_ON_SETUP; + if (strategy_str == "lazy_load") + return exaconstit::LoadStrategy::LAZY_LOAD; + + MFEM_WARNING_0("Warning: Unknown load strategy '" << strategy_str << "', using 'persistent'"); + return exaconstit::LoadStrategy::PERSISTENT; +} + +/** + * @brief Factory function to create appropriate material model type + * + * @param mat_config Material configuration options + * @param sim_state Reference to simulation state + * @return Unique pointer to created material model + * + * @details Factory function that creates the appropriate material model type (UMAT, ExaCMech, etc.) + * based on the material configuration. Handles library path resolution for UMAT models and + * parameter setup for all model types. + */ +std::unique_ptr CreateMaterialModel(const MaterialOptions& mat_config, + std::shared_ptr sim_state) { + if (mat_config.mech_type == MechType::UMAT && mat_config.model.umat.has_value()) { + const auto& umat_config = mat_config.model.umat.value(); + // Resolve library path using search paths + auto resolved_path = resolveUmatLibraryPath(umat_config.library_path, + umat_config.search_paths); + + const auto load_strategy = stringToLoadStrategy(umat_config.load_strategy); + // Create enhanced UMAT model + auto umat_model = std::make_unique( + mat_config.region_id - 1, + mat_config.state_vars.num_vars, + sim_state, + umat_config.enable_dynamic_loading ? resolved_path : "", + load_strategy, + umat_config.function_name); + + return umat_model; + } + // Handle other material types... + return nullptr; +} + +MultiExaModel::MultiExaModel(std::shared_ptr sim_state, const ExaOptions& options) + : ExaModel(-1, 0, sim_state) // Region -1, n_state_vars computed later +{ + CALI_CXX_MARK_SCOPE("composite_model_construction"); + + // The construction is now beautifully simple because SimulationState + // already handles all the complex region management for us + + m_num_regions = sim_state->GetNumberOfRegions(); + + // Create specialized models for each region + CreateChildModels(options); + + // Update our base class state variable count to reflect the maximum needed + // across all child models. This ensures compatibility with existing interfaces. + int max_state_vars = 0; + for (const auto& child : m_child_models) { + max_state_vars = std::max(max_state_vars, child->num_state_vars); + } + num_state_vars = max_state_vars; +} + +void MultiExaModel::CreateChildModels(const ExaOptions& options) { + // Create specialized material models for each region + // SimulationState already knows about regions, so we just create models + + m_child_models.reserve(options.materials.size()); + + for (size_t region_idx = 0; region_idx < options.materials.size(); ++region_idx) { + const auto& material = options.materials[region_idx]; + const int region_id = static_cast(region_idx); + if (!m_sim_state->IsRegionActive(region_id)) { + if (material.mech_type == MechType::EXACMECH) { + std::string model_name = material.model.exacmech ? material.model.exacmech->shortcut + : ""; + ECMechSetupQuadratureFuncStatePair(region_id, model_name, m_sim_state); + } + continue; + } + // Create the appropriate model type based on material specification + std::unique_ptr child_model; + + if (material.mech_type == MechType::UMAT) { + // Create UMAT model for this region + child_model = CreateMaterialModel(options.materials[region_idx], m_sim_state); + } else if (material.mech_type == MechType::EXACMECH) { + // Create ExaCMech model for this region + + // Determine execution strategy based on global solver settings + ecmech::ExecutionStrategy accel = ecmech::ExecutionStrategy::CPU; + if (options.solvers.rtmodel == RTModel::OPENMP) { + accel = ecmech::ExecutionStrategy::OPENMP; + } else if (options.solvers.rtmodel == RTModel::GPU) { + accel = ecmech::ExecutionStrategy::GPU; + } + + // Extract the material model name from the options + std::string model_name = material.model.exacmech ? material.model.exacmech->shortcut + : ""; + + child_model = std::make_unique( + region_idx, // Region this model handles + material.state_vars.num_vars, // State variables + material.temperature, // Operating temperature + accel, // Execution strategy (CPU/GPU) + model_name, // ExaCMech model type + m_sim_state // Shared simulation state + ); + } else { + throw std::runtime_error("Unknown material type for region " + + std::to_string(region_idx)); + } + + if (!child_model) { + throw std::runtime_error("Failed to create material model for region " + + std::to_string(region_idx)); + } + m_child_models.push_back(std::move(child_model)); + } +} + +void MultiExaModel::ModelSetup(const int nqpts, + const int nelems, + const int space_dim, + const int nnodes, + const mfem::Vector& jacobian, + const mfem::Vector& loc_grad, + const mfem::Vector& vel) { + CALI_CXX_MARK_SCOPE("composite_model_setup"); + + m_sim_state->SetupModelVariables(); + + // This is now incredibly simple because SimulationState handles all the complexity! + // Each child model automatically gets the right data for its region through SimulationState + + // Process each region - each child model operates on its own region's data + std::vector region_success(m_child_models.size()); + + for (size_t region_idx = 0; region_idx < m_child_models.size(); ++region_idx) { + region_success[region_idx] = SetupChildModel( + region_idx, nqpts, nelems, space_dim, nnodes, jacobian, loc_grad, vel); + } + + // Verify that all regions completed successfully across all MPI processes + if (!ValidateAllRegionsSucceeded(region_success)) { + throw std::runtime_error("One or more material regions failed during setup"); + } + + // No need for explicit result aggregation - SimulationState handles this automatically + // through the PartialQuadratureFunction system when child models write their results +} + +bool MultiExaModel::SetupChildModel(size_t region_idx, + const int nqpts, + const int nelems, + const int space_dim, + const int nnodes, + const mfem::Vector& jacobian, + const mfem::Vector& loc_grad, + const mfem::Vector& vel) const { + CALI_CXX_MARK_SCOPE("composite_setup_child"); + const int actual_region_id = m_child_models[region_idx]->GetRegionID(); + try { + // The beauty of this design: we just call the child model with the region index + // SimulationState automatically routes the right data to the right model! + auto& child_model = m_child_models[region_idx]; + + // The child model uses its region_idx to get region-specific data from SimulationState + // This is much cleaner than manually extracting and routing data + child_model->ModelSetup(nqpts, nelems, space_dim, nnodes, jacobian, loc_grad, vel); + + return true; + } catch (const std::exception& e) { + MFEM_WARNING_0("[Cycle " << std::to_string(m_sim_state->GetSimulationCycle() + 1) + << " ]Region " + std::to_string(actual_region_id) + + " failed: " + e.what()); + return false; + } catch (...) { + MFEM_WARNING_0("Region " + std::to_string(actual_region_id) + " failed with unknown error"); + return false; + } +} + +bool MultiExaModel::ValidateAllRegionsSucceeded(const std::vector& region_success) const { + // Check if all regions succeeded on this processor + bool local_success = std::all_of( + region_success.begin(), region_success.end(), [](bool success) { + return success; + }); + + // Use MPI collective operation to ensure global consistency + bool global_success = false; + MPI_Allreduce(&local_success, &global_success, 1, MPI_C_BOOL, MPI_LAND, MPI_COMM_WORLD); + + return global_success; +} + +void MultiExaModel::UpdateModelVars() { + // Coordinate state variable updates across all child models + for (auto& child : m_child_models) { + child->UpdateModelVars(); + } +} + +// Utility methods for external access +ExaModel* MultiExaModel::GetChildModel(int region_idx) const { + if (region_idx < 0 || region_idx >= static_cast(m_child_models.size())) { + return nullptr; + } + return m_child_models[static_cast(region_idx)].get(); +} \ No newline at end of file diff --git a/src/models/mechanics_multi_model.hpp b/src/models/mechanics_multi_model.hpp new file mode 100644 index 0000000..09e01b9 --- /dev/null +++ b/src/models/mechanics_multi_model.hpp @@ -0,0 +1,158 @@ +#pragma once + +#include "models/mechanics_model.hpp" +#include "sim_state/simulation_state.hpp" + +#include +#include + +/** + * @brief Multi material model that coordinates multiple region-specific models + * + * @details This class implements the Composite design pattern to manage multiple material + * models within a single simulation. From the outside, it looks and behaves exactly + * like any other ExaModel, but internally it coordinates multiple "child" models + * that handle different material regions. + * + * Key responsibilities: + * 1. Maintain mapping between elements and material regions + * 2. Route global simulation data to appropriate child models + * 3. Coordinate child model execution during setup phases + * 4. Aggregate results from child models into global data structures + * 5. Present a unified ExaModel interface to external code + * + * This design eliminates the need for external classes (like NonlinearMechOperator) + * to understand or manage multiple models, significantly simplifying the overall + * architecture while maintaining full flexibility for multi-material simulations. + */ +class MultiExaModel : public ExaModel { +private: + /** @brief Child models - one for each material region */ + std::vector> m_child_models; + + /** @brief Number of regions in this simulation */ + size_t m_num_regions; + +public: + /** + * @brief Construct a composite model from simulation options + * + * @param sim_state Reference to simulation state for data access + * @param options Simulation options containing material definitions + * + * @details This constructor analyzes the ExaOptions to determine how many regions + * are needed, creates appropriate child models for each region, and sets up + * all the internal data structures for efficient region management. + */ + MultiExaModel(std::shared_ptr sim_state, const ExaOptions& options); + + /** + * @brief Destructor - child models are automatically cleaned up by unique_ptr + */ + virtual ~MultiExaModel() = default; + + // ExaModel interface implementation + // These methods coordinate the child models while presenting a unified interface + + /** + * @brief Main model setup method - coordinates all child models + * + * @param nqpts Number of quadrature points per element + * @param nelems Number of elements in this batch + * @param space_dim Spatial dimension + * @param nnodes Number of nodes per element + * @param jacobian Jacobian transformation matrices for elements + * @param loc_grad Local gradient operators + * @param vel Velocity field at elemental level + * + * @details This method receives global simulation data and internally: + * 1. Extracts region-specific subsets of the data + * 2. Calls each child model with its appropriate data subset + * 3. Coordinates result collection back to global data structures + * + * From the caller's perspective, this looks identical to any single model setup. + */ + virtual void ModelSetup(const int nqpts, + const int nelems, + const int space_dim, + const int nnodes, + const mfem::Vector& jacobian, + const mfem::Vector& loc_grad, + const mfem::Vector& vel) override; + + /** + * @brief Update all child models' state variables + * + * @details This coordinates the state variable updates across all regions, + * ensuring that beginning-of-step values are properly synchronized. + */ + virtual void UpdateModelVars() override; + + /** + * @brief Get the number of material regions + * + * @return Number of material regions in this simulation + */ + size_t GetNumberOfRegions() const { + return m_child_models.size(); + } + + /** + * @brief Get a specific child model (for advanced use cases) + * + * @param region_idx Index of the region + * @return Pointer to the child model for the specified region + * + * @details This allows external code to access specific region models if needed, + * though in most cases the composite interface should be sufficient. + */ + ExaModel* GetChildModel(int region_idx) const; + +private: + /** + * @brief Create child models for each region + * + * @param options Simulation options containing material definitions + * + * @details This analyzes the material options and creates appropriate ExaModel + * instances (ExaCMech, UMAT, etc.) for each defined material region. + */ + void CreateChildModels(const ExaOptions& options); + + /** + * @brief Setup and execute a specific child model + * + * @param region_idx Index of the region to setup + * @param nqpts Number of quadrature points per element + * @param nelems Number of elements in this batch + * @param space_dim Spatial dimension + * @param nnodes Number of nodes per element + * @param jacobian Jacobian transformation matrices for elements + * @param loc_grad Local gradient operators + * @param vel Velocity field at elemental level + * @return True if child model setup succeeded, false otherwise + * + * @details This calls the child model for a specific region, letting SimulationState + * handle all the data routing and region-specific data management. + */ + bool SetupChildModel(size_t region_idx, + const int nqpts, + const int nelems, + const int space_dim, + const int nnodes, + const mfem::Vector& jacobian, + const mfem::Vector& loc_grad, + const mfem::Vector& vel) const; + + /** + * @brief Error handling and validation across regions + * + * @param region_success Vector indicating success/failure for each region + * @return True if all regions succeeded, false if any failed + * + * @details This method uses MPI collective operations to ensure that if any + * child model fails on any processor, the entire simulation knows + * about it and can respond appropriately. + */ + bool ValidateAllRegionsSucceeded(const std::vector& region_success) const; +}; \ No newline at end of file diff --git a/src/models/mechanics_umat.cpp b/src/models/mechanics_umat.cpp new file mode 100644 index 0000000..069ec6f --- /dev/null +++ b/src/models/mechanics_umat.cpp @@ -0,0 +1,892 @@ +#include "models/mechanics_umat.hpp" + +#include "boundary_conditions/BCManager.hpp" +#include "utilities/assembly_ops.hpp" +#include "utilities/strain_measures.hpp" +#include "utilities/unified_logger.hpp" + +#include "RAJA/RAJA.hpp" +#include "mfem/fem/qfunction.hpp" + +#include +#include // cerr +#include // log + +// NEW CONSTRUCTOR IMPLEMENTATION: Much simpler parameter list +// The key insight is that instead of passing in all QuadratureFunctions and material properties, +// we only pass in the essential UMAT-specific parameters and use the region ID to access +// data through SimulationState when needed. +AbaqusUmatModel::AbaqusUmatModel(const int region, + int n_state_vars, + std::shared_ptr sim_state, + const std::filesystem::path& umat_library_path_, + const exaconstit::LoadStrategy& load_strategy_, + const std::string umat_function_name_) + : ExaModel(region, n_state_vars, sim_state), umat_library_path(umat_library_path_), + umat_function(nullptr), load_strategy(load_strategy_), + use_dynamic_loading(!umat_library_path_.empty()), umat_function_name(umat_function_name_) { + // Initialize working space QuadratureFunctions + InitLocSFGrads(m_sim_state->GetMeshParFiniteElementSpace()); + InitIncrEndDefGrad(); + + // If using dynamic loading with PERSISTENT strategy, load immediately + if (use_dynamic_loading && load_strategy == exaconstit::LoadStrategy::PERSISTENT) { + if (!LoadUmatLibrary()) { + throw std::runtime_error("Failed to load UMAT library: " + umat_library_path.string()); + } + } else if (!use_dynamic_loading) { + // Use the built-in UMAT - UnifiedUmatLoader handles this with empty path + umat_function = exaconstit::UnifiedUmatLoader::LoadUmat( + "", exaconstit::LoadStrategy::PERSISTENT, ""); + if (!umat_function) { + throw std::runtime_error("No built-in UMAT available: " + + exaconstit::UnifiedUmatLoader::GetLastError()); + } + } +} + +AbaqusUmatModel::~AbaqusUmatModel() { + // Unload library if needed + if (use_dynamic_loading && load_strategy != exaconstit::LoadStrategy::PERSISTENT) { + UnloadUmatLibrary(); + } +} + +// NEW HELPER METHOD: Get defGrad0 from SimulationState instead of using member variable +// This enables dynamic access to the correct region-specific deformation gradient data +std::shared_ptr AbaqusUmatModel::GetDefGrad0() { + return m_sim_state->GetQuadratureFunction("def_grad_beg", m_region); +} + +// UPDATED: UpdateModelVars now gets defGrad0 from SimulationState instead of member variable +void AbaqusUmatModel::UpdateModelVars() { + GetDefGrad0()->operator=(*dynamic_cast(end_def_grad.get())); +} + +// Work through the initialization of all of this... +// UNCHANGED: This method doesn't directly access QuadratureFunctions that moved to SimulationState +void AbaqusUmatModel::InitLocSFGrads(std::shared_ptr fes) { + const mfem::FiniteElement* fe; + const mfem::IntegrationRule* ir; + + // UPDATED: Get defGrad0 from SimulationState to determine quadrature space + auto defGrad0 = GetDefGrad0(); + auto qspace = defGrad0->GetPartialSpaceShared(); + + ir = &(qspace->GetIntRule(0)); + + const int NE = qspace->GetNE(); + const int NQPTS = ir->GetNPoints(); + + // get element transformation for the 0th element + // We just want to get some basic stuff for now + fe = fes->GetFE(0); + + // declare data to store shape function gradients + // and element Jacobians + mfem::DenseMatrix Jrt, DSh, DS; + int dof = fe->GetDof(), dim = fe->GetDim(); + const int VDIM = dof * dim; + + DSh.SetSize(dof, dim); + // This should probably be linked to the underlying quadrature function + DS.SetSize(dof, dim); + Jrt.SetSize(dim); + + // We now have enough information to create our loc0_sf_grad + loc0_sf_grad = std::make_shared(qspace, VDIM); + double* data = loc0_sf_grad->HostReadWrite(); + auto l2g = qspace->GetLocal2Global(); + + // loop over elements + for (int i = 0; i < NE; ++i) { + const int ge = l2g[i]; + // get element transformation for the ith element + mfem::ElementTransformation* Ttr = fes->GetElementTransformation(ge); + fe = fes->GetFE(ge); + + // PMatI.UseExternalData(el_x.ReadWrite(), dof, dim); + + ir = &(qspace->GetIntRule(i)); + + // loop over integration points where the quadrature function is + // stored + for (int j = 0; j < NQPTS; ++j) { + // The offset is the current location of the data + int offset = (i * NQPTS * VDIM) + (j * VDIM); + double* data_offset = data + offset; + + DS.UseExternalData(data_offset, dof, dim); + + const mfem::IntegrationPoint& ip = ir->IntPoint(j); + Ttr->SetIntPoint(&ip); + CalcInverse(Ttr->Jacobian(), Jrt); + + fe->CalcDShape(ip, DSh); + Mult(DSh, Jrt, DS); + } + } +} + +// UPDATED: InitIncrEndDefGrad now gets defGrad0 from SimulationState +void AbaqusUmatModel::InitIncrEndDefGrad() { + const mfem::IntegrationRule* ir; + auto defGrad0 = GetDefGrad0(); + auto qspace = defGrad0->GetPartialSpaceShared(); + + ir = &(qspace->GetIntRule(0)); + + const int NQPTS = ir->GetNPoints(); + // We've got the same elements everywhere so we can do this. + // If this assumption is no longer true we need to update the code + const int NE = qspace->GetNE(); + const int VDIM = defGrad0->GetVDim(); + + incr_def_grad = std::make_shared(qspace, VDIM); + incr_def_grad->operator=(0.0); + double* incr_data = incr_def_grad->HostReadWrite(); + + end_def_grad = std::make_shared(qspace, VDIM); + end_def_grad->operator=(0.0); + double* end_data = end_def_grad->HostReadWrite(); + + // loop over elements + for (int i = 0; i < NE; ++i) { + // loop over integration points where the quadrature function is + // stored + for (int j = 0; j < NQPTS; ++j) { + // The offset is the current location of the data + int offset = (i * NQPTS * VDIM) + (j * VDIM); + double* incr_data_offset = incr_data + offset; + double* end_data_offset = end_data + offset; + + // It's now just initialized to being the identity matrix + incr_data_offset[0] = 1.0; + incr_data_offset[4] = 1.0; + incr_data_offset[8] = 1.0; + + // It's now just initialized to being the identity matrix + end_data_offset[0] = 1.0; + end_data_offset[4] = 1.0; + end_data_offset[8] = 1.0; + } + } +} + +// UPDATED: CalcIncrEndDefGrad now gets defGrad0 from SimulationState +void AbaqusUmatModel::CalcIncrEndDefGrad(const mfem::ParGridFunction& x0) { + auto loc_fes = m_sim_state->GetMeshParFiniteElementSpace(); + const mfem::IntegrationRule* ir; + auto defGrad0 = GetDefGrad0(); + auto qspace = defGrad0->GetPartialSpaceShared(); + + ir = &(qspace->GetIntRule(0)); + + const int nqpts = ir->GetNPoints(); + // We've got the same type of elements everywhere so we can do this. + // If this assumption is no longer true we need to update the code + const int ne = qspace->GetNE(); + const int vdim = defGrad0->GetVDim(); + // We also assume we're only dealing with 3D type elements. + // If we aren't then this needs to change... + const int dim = 3; + const int vdim2 = loc0_sf_grad->GetVDim(); + const int dof = vdim2 / dim; + + double* incr_data = incr_def_grad->HostReadWrite(); + double* end_data = end_def_grad->HostReadWrite(); + double* int_data = defGrad0->HostReadWrite(); + double* ds_data = loc0_sf_grad->HostReadWrite(); + + mfem::ParGridFunction x_gf(x0); + + mfem::DenseMatrix f_incr(dim, dim); + mfem::DenseMatrix f_end(dim, dim); + mfem::DenseMatrix f_beg(dim, dim); + mfem::DenseMatrix f_beg_invr(dim, dim); + mfem::DenseMatrix DS(dof, dim); + mfem::DenseMatrix PMatI(dof, dim); + // The below are constant but will change between steps + mfem::Array vdofs(vdim2); + mfem::Vector el_x(PMatI.Data(), vdim2); + auto l2g = qspace->GetLocal2Global(); + + // loop over elements + for (int i = 0; i < ne; ++i) { + const int ge = l2g[i]; + loc_fes->GetElementVDofs(ge, vdofs); + // Our PMatI is now updated to the correct elemental values + x_gf.GetSubVector(vdofs, el_x); + // loop over integration points where the quadrature function is + // stored + for (int j = 0; j < nqpts; ++j) { + // The offset is the current location of the data + int offset = (i * nqpts * vdim) + (j * vdim); + int offset2 = (i * nqpts * vdim2) + (j * vdim2); + double* incr_data_offset = incr_data + offset; + double* end_data_offset = end_data + offset; + double* int_data_offset = int_data + offset; + double* ds_data_offset = ds_data + offset2; + + f_end.UseExternalData(end_data_offset, dim, dim); + f_beg.UseExternalData(int_data_offset, dim, dim); + f_incr.UseExternalData(incr_data_offset, dim, dim); + DS.UseExternalData(ds_data_offset, dof, dim); + + // Get the inverse of the beginning time step def. grad + f_beg_invr = f_beg; + f_beg_invr.Invert(); + + // Find the end time step def. grad + MultAtB(PMatI, DS, f_end); + + // Our incremental def. grad is now + Mult(f_end, f_beg_invr, f_incr); + } + } +} + +// UNCHANGED: These strain calculation methods don't access QuadratureFunctions +void AbaqusUmatModel::CalcLogStrainIncrement(mfem::DenseMatrix& dE, const mfem::DenseMatrix& Jpt) { + // calculate incremental logorithmic strain (Hencky Strain) + // which is taken to be E = ln(U_hat) = 1/2 ln(C_hat), where + // C_hat = (F_hat_T)F_hat, where F_hat = Jpt1 on the model + // (available from MFEM element transformation computations). + // We can compute F_hat, so use a spectral decomposition on C_hat to + // obtain a form where we only have to take the natural log of the + // eigenvalues + // UMAT uses the E = ln(V) approach instead + + mfem::DenseMatrix F_hat, B_hat; + + constexpr int dim = 3; + + F_hat.SetSize(dim); + B_hat.SetSize(dim); + + F_hat = Jpt; + + MultABt(F_hat, F_hat, B_hat); + + // compute eigenvalue decomposition of B + double lambda[dim]; + double vec[dim * dim]; + B_hat.CalcEigenvalues(&lambda[0], &vec[0]); + + // compute ln(B) using spectral representation + dE = 0.0; + for (int i = 0; i < dim; ++i) { // outer loop for every eigenvalue/vector + for (int j = 0; j < dim; ++j) { // inner loops for diadic product of eigenvectors + for (int k = 0; k < dim; ++k) { + // Dense matrices are col. maj. representation, so the indices were + // reversed for it to be more cache friendly. + dE(k, j) += 0.5 * log(lambda[i]) * vec[i * dim + j] * vec[i * dim + k]; + } + } + } + + return; +} + +// This method calculates the Eulerian strain which is given as: +// e = 1/2 (I - B^(-1)) = 1/2 (I - F(^-T)F^(-1)) +// UNCHANGED: This method doesn't access QuadratureFunctions +void AbaqusUmatModel::CalcEulerianStrainIncr(mfem::DenseMatrix& dE, const mfem::DenseMatrix& Jpt) { + constexpr int dim = 3; + mfem::DenseMatrix Fincr(Jpt, dim); + mfem::DenseMatrix Finv(dim), Binv(dim); + + double half = 1.0 / 2.0; + + CalcInverse(Fincr, Finv); + + MultAtB(Finv, Finv, Binv); + + dE = 0.0; + + for (int j = 0; j < dim; j++) { + for (int i = 0; i < dim; i++) { + dE(i, j) -= half * Binv(i, j); + } + + dE(j, j) += half; + } +} + +// This method calculates the Lagrangian strain which is given as: +// E = 1/2 (C - I) = 1/2 (F^(T)F - I) +// UNCHANGED: This method doesn't access QuadratureFunctions +void AbaqusUmatModel::CalcLagrangianStrainIncr(mfem::DenseMatrix& dE, + const mfem::DenseMatrix& Jpt) { + mfem::DenseMatrix C; + + constexpr int dim = 3; + + double half = 1.0 / 2.0; + + C.SetSize(dim); + + MultAtB(Jpt, Jpt, C); + + dE = 0.0; + + for (int j = 0; j < dim; j++) { + for (int i = 0; i < dim; i++) { + dE(i, j) += half * C(i, j); + } + + dE(j, j) -= half; + } + + return; +} + +// UPDATED: Further testing needs to be conducted to make sure this still does everything it used to +// but it should. Since, it is just copy and pasted from the old EvalModel function and now +// has loops added to it. Now uses accessor methods to get QuadratureFunctions from SimulationState. +void AbaqusUmatModel::ModelSetup(const int nqpts, + const int nelems, + const int space_dim, + const int /*nnodes*/, + const mfem::Vector& jacobian, + const mfem::Vector& /*loc_grad*/, + const mfem::Vector& /*vel*/) { + auto& logger = exaconstit::UnifiedLogger::get_instance(); + std::string material_log = logger.get_material_log_filename("umat", m_region); + exaconstit::UnifiedLogger::ScopedCapture capture(material_log); + + // Load UMAT library if using on-demand loading + if (use_dynamic_loading && load_strategy == exaconstit::LoadStrategy::LOAD_ON_SETUP) { + if (!LoadUmatLibrary()) { + throw std::runtime_error("Failed to load UMAT library during ModelSetup: " + + umat_library_path.string()); + } + } + + // Get region-specific element information + auto stress0 = m_sim_state->GetQuadratureFunction("cauchy_stress_beg", m_region); + auto qspace = stress0->GetPartialSpaceShared(); + + // Determine actual elements to process for this region + const mfem::Array* local2global_ptr = nullptr; + int local_nelems = nelems; + + if (!qspace->IsFullSpace()) { + const auto& local2global = qspace->GetLocal2Global(); + local2global_ptr = &local2global; + local_nelems = local2global.Size(); + } + + // All of this should be scoped to limit at least some of our memory usage + { + const auto end_crds = m_sim_state->GetCurrentCoords(); + CalcIncrEndDefGrad(*end_crds); + } + + // ====================================================== + // Set UMAT input arguments + // ====================================================== + + // initialize Umat variables + int ndi = 3; // number of direct stress components + int nshr = 3; // number of shear stress components + int ntens = ndi + nshr; + int noel = 0; + int npt = 0; + int layer = 0; + int kspt = 0; + int kstep = 0; + int kinc = 0; + + // set properties and state variables length (hard code for now); + int nprops = static_cast(GetMaterialProperties().size()); + int nstatv = num_state_vars; + + double pnewdt = 10.0; // revisit this + // if get sub-1 value for auto throw exception to try again for auto dt + mfem::Vector props( + nprops); // populate from the mat props vector wrapped by matProps on the base class + mfem::Vector statev( + nstatv); // populate from the state variables associated with this element/ip + + double rpl = 0.0; // volumetric heat generation per unit time, not considered + double drpldt = 0.0; // variation of rpl wrt temperature set to 0.0 + double tempk = 300.0; // no thermal considered at this point + double dtemp = 0.0; // no increment in thermal considered at this point + double predef = 0.0; // no interpolated values of predefined field variables at ip point + double dpred = 0.0; // no array of increments of predefined field variables + double sse = 0.0; // specific elastic strain energy, mainly for output + double spd = 0.0; // specific plastic dissipation, mainly for output + double scd = 0.0; // specific creep dissipation, mainly for output + std::string cmname = ""; // user defined UMAT name + double celent = 0.0; // set element length + + // set the time step + double deltaTime = m_sim_state->GetDeltaTime(); // set on the ExaModel base class + + // set time. Abaqus has odd increment definition. time[1] is the value of total + // time at the beginning of the current increment. Since we are iterating from + // tn to tn+1, this is just tn. time[0] is value of step time at the beginning + // of the current increment. What is step time if not tn? It seems as though + // they sub-increment between tn->tn+1, where there is a Newton Raphson loop + // advancing the sub-increment. For now, set time[0] is set to t - dt/ + double time[2]; + time[0] = m_sim_state->GetTime() - deltaTime; + time[1] = m_sim_state->GetTime(); + + double stress[6]; // Cauchy stress at ip + double ddsdt[6]; // variation of the stress increments wrt to temperature, set to 0.0 + double drplde[6]; // variation of rpl wrt strain increments, set to 0.0 + double stran[6]; // array containing total strains at beginning of the increment + double dstran[6]; // array of strain increments + + double* drot; // rotation matrix for finite deformations + double dfgrd0[9]; // deformation gradient at beginning of increment + double dfgrd1[9]; // defomration gradient at the end of the increment. + // set to zero if nonlinear geometric effects are not + // included in the step as is the case for ExaConstit + + // UPDATED: Get defGrad0 from SimulationState instead of using member variable + auto defGrad0 = GetDefGrad0(); + + double* defgrad0 = defGrad0->HostReadWrite(); + double* defgrad1 = end_def_grad->HostReadWrite(); + double* incr_defgrad = incr_def_grad->HostReadWrite(); + mfem::DenseMatrix incr_dgrad, dgrad0, dgrad1; + + const int vdim = end_def_grad->GetVDim(); + double ddsdde[36]; // output Jacobian matrix of the constitutive model. + // ddsdde(i,j) defines the change in the ith stress component + // due to an incremental perturbation in the jth strain increment + + const int DIM4 = 4; + + std::array perm4{{3, 2, 1, 0}}; + // bunch of helper RAJA views to make dealing with data easier down below in our kernel. + RAJA::Layout layout_jacob = RAJA::make_permuted_layout( + {{space_dim, space_dim, nqpts, nelems}}, perm4); + RAJA::View> J(jacobian.HostRead(), + layout_jacob); + + auto geom = m_sim_state->GetMesh()->GetGeometricFactors(qspace->GetIntRule(0), + mfem::GeometricFactors::COORDINATES); + + const auto x = mfem::Reshape(geom->X.Read(), nqpts, 3, nelems); + + // Update the element/IP loops to use proper indexing: + for (int local_elemID = 0; local_elemID < local_nelems; local_elemID++) { + // Map to global element ID for accessing global data + const int global_elemID = local2global_ptr ? (*local2global_ptr)[local_elemID] + : local_elemID; + + for (int ipID = 0; ipID < nqpts; ipID++) { + // compute characteristic element length + const double J11 = J(0, 0, ipID, global_elemID); // 0,0 + const double J21 = J(1, 0, ipID, global_elemID); // 1,0 + const double J31 = J(2, 0, ipID, global_elemID); // 2,0 + const double J12 = J(0, 1, ipID, global_elemID); // 0,1 + const double J22 = J(1, 1, ipID, global_elemID); // 1,1 + const double J32 = J(2, 1, ipID, global_elemID); // 2,1 + const double J13 = J(0, 2, ipID, global_elemID); // 0,2 + const double J23 = J(1, 2, ipID, global_elemID); // 1,2 + const double J33 = J(2, 2, ipID, global_elemID); // 2,2 + const double detJ = J11 * (J22 * J33 - J32 * J23) - + /* */ J21 * (J12 * J33 - J32 * J13) + + /* */ J31 * (J12 * J23 - J22 * J13); + CalcElemLength(detJ); + celent = elem_length; + + // integration point coordinates + // a material model shouldn't need this ever + // not actually integration points but provide physical coords at integration points + double coords[3] = { + x(ipID, 0, global_elemID), x(ipID, 1, global_elemID), x(ipID, 2, global_elemID)}; + + const int offset = local_elemID * nqpts * vdim + ipID * vdim; + + noel = local_elemID; // element id + npt = ipID; // integration point number + + // initialize 1d arrays + for (int i = 0; i < 6; ++i) { + stress[i] = 0.0; + ddsdt[i] = 0.0; + drplde[i] = 0.0; + stran[i] = 0.0; + dstran[i] = 0.0; + } + + // initialize 6x6 2d arrays + for (int i = 0; i < 6; ++i) { + for (int j = 0; j < 6; ++j) { + ddsdde[(i * 6) + j] = 0.0; + } + } + + incr_dgrad.UseExternalData((incr_defgrad + offset), 3, 3); + dgrad0.UseExternalData((defgrad0 + offset), 3, 3); + dgrad1.UseExternalData((defgrad1 + offset), 3, 3); + + mfem::DenseMatrix Uincr(3), Vincr(3); + mfem::DenseMatrix Rincr(incr_dgrad, 3); + CalcPolarDecompDefGrad(Rincr, Uincr, Vincr); + + drot = Rincr.GetData(); + + // populate the beginning step and end step (or best guess to end step + // within the Newton iterations) of the deformation gradients + for (int i = 0; i < ndi; ++i) { + for (int j = 0; j < ndi; ++j) { + // Dense matrices have column major layout so the below is fine. + dfgrd0[(i * 3) + j] = dgrad0(j, i); + dfgrd1[(i * 3) + j] = dgrad1(j, i); + } + } + + // get state variables and material properties + // UPDATED: These methods now use accessor methods to get QuadratureFunctions from + // SimulationState + + GetQFData(local_elemID, + ipID, + statev.HostReadWrite(), + m_sim_state->GetQuadratureFunction("state_var_beg", m_region)); + { + const auto prop_data = GetMaterialProperties(); + int index = 0; + for (const auto& prop : prop_data) { + props(index++) = prop; + } + } + + // get element stress and make sure ordering is ok + double stressTemp[6]; + double stressTemp2[6]; + GetQFData(local_elemID, + ipID, + stressTemp, + m_sim_state->GetQuadratureFunction("cauchy_stress_beg", m_region)); + + // ensure proper ordering of the stress array. ExaConstit uses + // Voigt notation (11, 22, 33, 23, 13, 12), while + // ------------------------------------------------------------------ + // We use Voigt notation: (11, 22, 33, 23, 13, 12) + // + // ABAQUS USES: + // (11, 22, 33, 12, 13, 23) + // ------------------------------------------------------------------ + stress[0] = stressTemp[0]; + stress[1] = stressTemp[1]; + stress[2] = stressTemp[2]; + stress[3] = stressTemp[5]; + stress[4] = stressTemp[4]; + stress[5] = stressTemp[3]; + + // Abaqus does mention wanting to use a log strain for large strains + // It's also based on an updated lagrangian formulation so as long as + // we aren't generating any crazy strains do we really need to use the + // log strain? + mfem::DenseMatrix LogStrain; + LogStrain.SetSize(ndi); // ndi x ndi + CalcEulerianStrain(LogStrain, dgrad1); + + // populate STRAN (symmetric) + // ------------------------------------------------------------------ + // We use Voigt notation: (11, 22, 33, 23, 13, 12) + // + // ABAQUS USES: + // (11, 22, 33, 12, 13, 23) + // ------------------------------------------------------------------ + stran[0] = LogStrain(0, 0); + stran[1] = LogStrain(1, 1); + stran[2] = LogStrain(2, 2); + stran[3] = 2 * LogStrain(0, 1); + stran[4] = 2 * LogStrain(0, 2); + stran[5] = 2 * LogStrain(1, 2); + + // compute incremental strain, DSTRAN + mfem::DenseMatrix dLogStrain; + dLogStrain.SetSize(ndi); + CalcEulerianStrainIncr(dLogStrain, incr_dgrad); + + // populate DSTRAN (symmetric) + // ------------------------------------------------------------------ + // We use Voigt notation: (11, 22, 33, 23, 13, 12) + // + // ABAQUS USES: + // (11, 22, 33, 12, 13, 23) + // ------------------------------------------------------------------ + dstran[0] = dLogStrain(0, 0); + dstran[1] = dLogStrain(1, 1); + dstran[2] = dLogStrain(2, 2); + dstran[3] = 2 * dLogStrain(0, 1); + dstran[4] = 2 * dLogStrain(0, 2); + dstran[5] = 2 * dLogStrain(1, 2); + + // call c++ wrapper of umat routine + CallUmat(&stress[0], + statev.HostReadWrite(), + &ddsdde[0], + &sse, + &spd, + &scd, + &rpl, + ddsdt, + drplde, + &drpldt, + &stran[0], + &dstran[0], + time, + &deltaTime, + &tempk, + &dtemp, + &predef, + &dpred, + const_cast(cmname.c_str()), + &ndi, + &nshr, + &ntens, + &nstatv, + props.HostReadWrite(), + &nprops, + &coords[0], + drot, + &pnewdt, + &celent, + &dfgrd0[0], + &dfgrd1[0], + &noel, + &npt, + &layer, + &kspt, + &kstep, + &kinc); + + if (pnewdt < 1.0) { + throw std::runtime_error( + "UMAT time stepping needs to be reduced for at least 1 integration point"); + } + + // Due to how Abaqus has things ordered we need to swap the 4th and 6th columns + // and rows with one another for our C_stiffness matrix. + int j = 3; + // We could probably just replace this with a std::swap operation... + for (int i = 0; i < 6; i++) { + std::swap(ddsdde[(6 * i) + j], ddsdde[(6 * i) + 5]); + } + + for (int i = 0; i < 6; i++) { + std::swap(ddsdde[(6 * j) + i], ddsdde[(6 * 5) + i]); + } + + for (int i = 0; i < 36; i++) { + ddsdde[i] *= deltaTime; + } + + // set the material stiffness on the model + // UPDATED: This method now uses accessor methods to get QuadratureFunctions from + // SimulationState + SetQFData(local_elemID, + ipID, + ddsdde, + m_sim_state->GetQuadratureFunction("tangent_stiffness", m_region)); + + // set the updated stress on the model. Have to convert from Abaqus + // ordering to Voigt notation ordering + // ------------------------------------------------------------------ + // We use Voigt notation: (11, 22, 33, 23, 13, 12) + // + // ABAQUS USES: + // (11, 22, 33, 12, 13, 23) + // ------------------------------------------------------------------ + stressTemp2[0] = stress[0]; + stressTemp2[1] = stress[1]; + stressTemp2[2] = stress[2]; + stressTemp2[3] = stress[5]; + stressTemp2[4] = stress[4]; + stressTemp2[5] = stress[3]; + + // UPDATED: This method now uses accessor methods to get QuadratureFunctions from + // SimulationState + SetQFData(local_elemID, + ipID, + stressTemp2, + m_sim_state->GetQuadratureFunction("cauchy_stress_end", m_region)); + + // set the updated statevars + // UPDATED: This method now uses accessor methods to get QuadratureFunctions from + // SimulationState + SetQFData(local_elemID, + ipID, + statev.HostReadWrite(), + m_sim_state->GetQuadratureFunction("state_var_end", m_region)); + } + } + + auto global_stress = m_sim_state->GetQuadratureFunction("cauchy_stress_end"); + auto stress_final = m_sim_state->GetQuadratureFunction("cauchy_stress_end", m_region); + stress_final->FillQuadratureFunction(*global_stress); + + auto global_tangent_stiffness = m_sim_state->GetQuadratureFunction("tangent_stiffness"); + auto matGrad_qf = m_sim_state->GetQuadratureFunction("tangent_stiffness", m_region); + matGrad_qf->FillQuadratureFunction(*global_tangent_stiffness); + + // Unload library if using LOAD_ON_SETUP strategy + if (use_dynamic_loading && load_strategy == exaconstit::LoadStrategy::LOAD_ON_SETUP) { + UnloadUmatLibrary(); + } +} + +bool AbaqusUmatModel::SetUmatLibrary(const std::filesystem::path& library_path, + exaconstit::LoadStrategy strategy) { + // Unload current library if loaded + if (use_dynamic_loading) { + UnloadUmatLibrary(); + } + + umat_library_path = library_path; + load_strategy = strategy; + use_dynamic_loading = !library_path.empty(); + umat_function = nullptr; + + // Load immediately if using PERSISTENT strategy + if (use_dynamic_loading && strategy == exaconstit::LoadStrategy::PERSISTENT) { + return LoadUmatLibrary(); + } + + return true; +} + +bool AbaqusUmatModel::ReloadUmatLibrary() { + if (!use_dynamic_loading) { + return true; + } + + // Force unload and reload + exaconstit::UnifiedUmatLoader::UnloadUmat(umat_library_path.string()); + umat_function = nullptr; + + return LoadUmatLibrary(); +} + +bool AbaqusUmatModel::LoadUmatLibrary() { + if (!use_dynamic_loading || umat_function != nullptr) { + return true; // Already loaded or not using dynamic loading + } + + umat_function = exaconstit::UnifiedUmatLoader::LoadUmat( + umat_library_path.string(), load_strategy, umat_function_name); + + if (!umat_function) { + std::ostringstream err; + err << "Failed to load UMAT library: " << umat_library_path.string() + << "\nError: " << exaconstit::UnifiedUmatLoader::GetLastError(); + MFEM_ABORT_0(err.str()); + return false; + } + + return true; +} + +void AbaqusUmatModel::UnloadUmatLibrary() { + if (use_dynamic_loading && !umat_library_path.empty()) { + exaconstit::UnifiedUmatLoader::UnloadUmat(umat_library_path.string()); + umat_function = nullptr; + } +} + +void AbaqusUmatModel::CallUmat(double* stress, + double* statev, + double* ddsdde, + double* sse, + double* spd, + double* scd, + double* rpl, + double* ddsdt, + double* drplde, + double* drpldt, + double* stran, + double* dstran, + double* time, + double* deltaTime, + double* tempk, + double* dtemp, + double* predef, + double* dpred, + char* cmname, + int* ndi, + int* nshr, + int* ntens, + int* nstatv, + double* props, + int* nprops, + double* coords, + double* drot, + double* pnewdt, + double* celent, + double* dfgrd0, + double* dfgrd1, + int* noel, + int* npt, + int* layer, + int* kspt, + int* kstep, + int* kinc) { + if (!umat_function) { + MFEM_ABORT_0("UMAT function not available"); + } + + umat_function(stress, + statev, + ddsdde, + sse, + spd, + scd, + rpl, + ddsdt, + drplde, + drpldt, + stran, + dstran, + time, + deltaTime, + tempk, + dtemp, + predef, + dpred, + cmname, + ndi, + nshr, + ntens, + nstatv, + props, + nprops, + coords, + drot, + pnewdt, + celent, + dfgrd0, + dfgrd1, + noel, + npt, + layer, + kspt, + kstep, + kinc); +} + +// UNCHANGED: This method doesn't access QuadratureFunctions +void AbaqusUmatModel::CalcElemLength(const double elemVol) { + // It can also be approximated as the cube root of the element's volume. + // I think this one might be a little nicer to use because for distorted elements + // you might not want the largest length. + // According to + // https://abaqus-docs.mit.edu/2017/English/SIMACAEKEYRefMap/simakey-r-characteristiclength.htm + // it looks like this might be the right way to do it... + // although this does change from integration to integration point + // since we're using the determinate instead of the actual volume. However, + // it should be good enough for our needs... + elem_length = cbrt(elemVol); + + return; +} \ No newline at end of file diff --git a/src/models/mechanics_umat.hpp b/src/models/mechanics_umat.hpp new file mode 100644 index 0000000..dec14bc --- /dev/null +++ b/src/models/mechanics_umat.hpp @@ -0,0 +1,337 @@ +#ifndef MECHANICS_UMAT +#define MECHANICS_UMAT + +#include "models/mechanics_model.hpp" +#include "umats/unified_umat_loader.hpp" + +#include "mfem.hpp" + +#include +namespace fs = std::filesystem; + +/** + * @brief Enhanced Abaqus UMAT model with dynamic library loading support + * + * @details Implementation of ExaModel for Abaqus UMAT (User Material) interfaces. + * Supports both static linking and dynamic loading of UMAT shared libraries, enabling + * flexible material model integration without recompilation. + * + * Key features: + * - Dynamic loading of UMAT shared libraries + * - Support for multiple UMATs in different regions + * - Configurable loading strategies (persistent, on-demand, etc.) + * - Thread-safe library management + * - Automatic cleanup and error handling + */ +class AbaqusUmatModel : public ExaModel { +protected: + /** @brief Characteristic element length passed to UMAT */ + double elem_length; + + /** @brief Initial local shape function gradients working space */ + std::shared_ptr loc0_sf_grad; + + /** @brief Incremental deformation gradients working space */ + std::shared_ptr incr_def_grad; + + /** @brief End-of-step deformation gradients working space */ + std::shared_ptr end_def_grad; + + /** @brief Path to UMAT shared library */ + std::filesystem::path umat_library_path; + + /** @brief Pointer to loaded UMAT function */ + UmatFunction umat_function; + + /** @brief Loading strategy for the library */ + exaconstit::LoadStrategy load_strategy; + + /** @brief Flag to enable/disable dynamic loading */ + bool use_dynamic_loading; + + /** @brief UMAT function name if supplied */ + const std::string umat_function_name; + +public: + /** + * @brief Constructor with dynamic UMAT loading support + * + * @param region Region identifier + * @param n_state_vars Number of state variables + * @param sim_state Reference to simulation state + * @param umat_library_path Path to UMAT shared library (empty for static linking) + * @param load_strategy Strategy for loading/unloading the library + * @param umat_function_name UMAT function name that the user wants us to load + * + * @details Creates an Abaqus UMAT model instance with support for dynamic library loading. + * Initializes working space for deformation gradients and prepares for UMAT execution. + */ + AbaqusUmatModel( + const int region, + int n_state_vars, + std::shared_ptr sim_state, + const std::filesystem::path& umat_library_path_ = "", + const exaconstit::LoadStrategy& load_strategy_ = exaconstit::LoadStrategy::PERSISTENT, + const std::string umat_function_name_ = ""); + + /** + * @brief Destructor - cleans up resources and unloads library if needed + * + * @details Cleans up resources and unloads UMAT library if using non-persistent loading + * strategy. + */ + virtual ~AbaqusUmatModel(); + + /** + * @brief Get the beginning-of-step deformation gradient quadrature function + * + * @return Shared pointer to the beginning-of-step deformation gradient quadrature function + * + * @details Retrieves the deformation gradient at the beginning of the time step from + * SimulationState for this model's region. This replaces direct member variable access + * and enables dynamic access to the correct region-specific deformation gradient data. + */ + std::shared_ptr GetDefGrad0(); + + /** + * @brief Update beginning-of-step deformation gradient with converged values + * + * @details Updates the beginning-of-step deformation gradient with converged end-of-step + * values after successful solution convergence. We just need to update our beginning of + * time step def. grad. with our end step def. grad. now that they are equal. + */ + virtual void UpdateModelVars() override; + + /** + * @brief Main UMAT execution method + * + * @param nqpts Number of quadrature points per element + * @param nelems Number of elements in this batch + * @param space_dim Spatial dimension + * @param nnodes Number of nodes per element (unused in current implementation) + * @param jacobian Jacobian transformation matrices for elements + * @param loc_grad Local gradient operators (unused in current implementation) + * @param vel Velocity field at elemental level (unused in current implementation) + * + * @details Main UMAT execution method that: + * 1. Loads UMAT library if using on-demand loading + * 2. Computes incremental deformation gradients + * 3. Calls UMAT for each quadrature point with appropriate strain measures + * 4. Collects stress and tangent stiffness results + * 5. Updates state variables + * 6. Unloads library if using LOAD_ON_SETUP strategy + * + * Since, it is just copy and pasted from the old EvalModel function and now + * has loops added to it. Now uses accessor methods to get QuadratureFunctions from + * SimulationState. + */ + virtual void ModelSetup(const int nqpts, + const int nelems, + const int space_dim, + const int /*nnodes*/, + const mfem::Vector& jacobian, + const mfem::Vector& /*loc_grad*/, + const mfem::Vector& vel) override; + + /** + * @brief Configure dynamic loading of a UMAT library + * + * @param library_path Path to the UMAT shared library + * @param strategy Loading strategy to use + * @return True if library setup succeeded, false otherwise + * + * @details Configures dynamic loading of a UMAT library with the specified loading strategy. + */ + bool SetUmatLibrary(const std::filesystem::path& library_path, + exaconstit::LoadStrategy strategy = exaconstit::LoadStrategy::PERSISTENT); + + /** + * @brief Get the current UMAT library path + */ + const std::filesystem::path& GetUmatLibraryPath() const { + return umat_library_path; + } + + /** + * @brief Check if using dynamic loading + */ + bool UsingDynamicLoading() const { + return use_dynamic_loading; + } + + /** + * @brief Force reload of the current UMAT library + * + * @return True if reload succeeded, false otherwise + * + * @details Forces unloading and reloading of the current UMAT library, + * useful for development and testing. + */ + bool ReloadUmatLibrary(); + + /** + * @brief Load the UMAT shared library + * + * @return True if loading succeeded, false otherwise + * + * @details Loads the UMAT shared library and retrieves the UMAT function pointer. + */ + bool LoadUmatLibrary(); + + /** + * @brief Unload the currently loaded UMAT library + * + * @details Unloads the currently loaded UMAT library and resets the function pointer. + */ + void UnloadUmatLibrary(); + +protected: + /** + * @brief Call the UMAT function (either static or dynamic) + * + * @param stress Stress tensor components + * @param statev State variables array + * @param ddsdde Material tangent matrix + * @param sse Specific elastic strain energy + * @param spd Plastic dissipation + * @param scd Creep dissipation + * @param rpl Volumetric heat generation + * @param ddsdt Stress increment due to temperature + * @param drplde Heat generation rate due to strain + * @param drpldt Heat generation rate due to temperature + * @param stran Strain tensor + * @param dstran Strain increment + * @param time Current time and time at beginning of increment + * @param deltaTime Time increment + * @param tempk Temperature in Kelvin + * @param dtemp Temperature increment + * @param predef Predefined field variables + * @param dpred Predefined field variable increments + * @param cmname Material name + * @param ndi Number of direct stress components + * @param nshr Number of shear stress components + * @param ntens Total number of stress components + * @param nstatv Number of state variables + * @param props Material properties + * @param nprops Number of material properties + * @param coords Coordinates + * @param drot Rotation increment matrix + * @param pnewdt Suggested new time increment + * @param celent Characteristic element length + * @param dfgrd0 Deformation gradient at beginning of increment + * @param dfgrd1 Deformation gradient at end of increment + * @param noel Element number + * @param npt Integration point number + * @param layer Layer number + * @param kspt Section point number + * @param kstep Step number + * @param kinc Increment number + * + * @details Calls the UMAT function (either statically linked or dynamically loaded) + * with the standard Abaqus UMAT interface. + */ + void CallUmat(double* stress, + double* statev, + double* ddsdde, + double* sse, + double* spd, + double* scd, + double* rpl, + double* ddsdt, + double* drplde, + double* drpldt, + double* stran, + double* dstran, + double* time, + double* deltaTime, + double* tempk, + double* dtemp, + double* predef, + double* dpred, + char* cmname, + int* ndi, + int* nshr, + int* ntens, + int* nstatv, + double* props, + int* nprops, + double* coords, + double* drot, + double* pnewdt, + double* celent, + double* dfgrd0, + double* dfgrd1, + int* noel, + int* npt, + int* layer, + int* kspt, + int* kstep, + int* kinc); + + /** + * @brief Initialize local shape function gradients + * + * @param fes Parallel finite element space + * + * @details Initializes local shape function gradients for UMAT calculations. + */ + void InitLocSFGrads(const std::shared_ptr fes); + + /** + * @brief Initialize incremental and end-of-step deformation gradient quadrature functions + * + * @details Initializes incremental and end-of-step deformation gradient quadrature functions. + */ + void InitIncrEndDefGrad(); + + /** + * @brief Calculate incremental and end-of-step deformation gradients + * + * @param x0 Current coordinates grid function + * + * @details Calculates incremental and end-of-step deformation gradients from current mesh + * coordinates. + */ + void CalcIncrEndDefGrad(const mfem::ParGridFunction& x0); + + /** + * @brief Calculate logarithmic strain increment from deformation gradient + * + * @param dE Output strain increment matrix + * @param Jpt Deformation gradient at quadrature point + * + * @details Calculates logarithmic strain increment from deformation gradients for UMAT input. + */ + void CalcLogStrainIncrement(mfem::DenseMatrix& dE, const mfem::DenseMatrix& Jpt); + + /** + * @brief Calculate Eulerian strain increment from deformation gradient + * + * @param dE Output strain increment matrix + * @param Jpt Deformation gradient at quadrature point + * + * @details Calculates Eulerian strain increment from deformation gradients for UMAT input. + */ + void CalcEulerianStrainIncr(mfem::DenseMatrix& dE, const mfem::DenseMatrix& Jpt); + + /** + * @brief Calculate Lagrangian strain increment from deformation gradient + * + * @param dE Output strain increment matrix + * @param Jpt Deformation gradient at quadrature point + * + * @details Calculates Lagrangian strain increment from deformation gradients for UMAT input. + */ + void CalcLagrangianStrainIncr(mfem::DenseMatrix& dE, const mfem::DenseMatrix& Jpt); + + /** + * @brief Calculate element length from element volume + * + * @param elemVol Element volume + * + * @details Calculates characteristic element length as cube root of element volume. + */ + void CalcElemLength(const double elemVol); +}; + +#endif \ No newline at end of file diff --git a/src/option_parser.cpp b/src/option_parser.cpp deleted file mode 100644 index be9d399..0000000 --- a/src/option_parser.cpp +++ /dev/null @@ -1,929 +0,0 @@ - -#include "option_parser.hpp" -#include "RAJA/RAJA.hpp" -#include "TOML_Reader/toml.hpp" -#include "mfem.hpp" -#include "ECMech_cases.h" -#include -#include -#include - -inline bool if_file_exists (const std::string& name) { - std::ifstream f(name.c_str()); - return f.good(); -} - -// my_id corresponds to the processor id. -void ExaOptions::parse_options(int my_id) -{ - // From the toml file it finds all the values related to the mesh - get_mesh(); - // From the toml file it finds all the values related to state and mat'l - // properties - get_properties(); - // From the toml file it finds all the values related to the BCs - get_bcs(); - // From the toml file it finds all the values related to the model - get_model(); - // From the toml file it finds all the values related to the time - get_time_steps(); - // From the toml file it finds all the values related to the visualizations - get_visualizations(); - // From the toml file it finds all the values related to the Solvers - get_solvers(); - // If the processor is set 0 then the options are printed out. - if (my_id == 0) { - print_options(); - } -} - -// From the toml file it finds all the values related to state and mat'l -// properties -void ExaOptions::get_properties() -{ - const auto data = toml::parse(floc); - const auto& table = toml::find(data, "Properties"); - double _temp_k = toml::find_or(table, "temperature", 298.0); - - if (_temp_k <= 0.0) { - MFEM_ABORT("Properties.temperature is given in Kelvins and therefore can't be less than 0"); - } - - temp_k = _temp_k; - - // Check to see if our table exists - if (table.contains("Matl_Props")) { - // Material properties are obtained first - const auto& prop_table = toml::find(table, "Matl_Props"); - std::string _props_file = toml::find_or(prop_table, "floc", "props.txt"); - props_file = _props_file; - if (!if_file_exists(props_file)) - { - MFEM_ABORT("Property file does not exist"); - } - nProps = toml::find_or(prop_table, "num_props", 1); - } - else { - MFEM_ABORT("Properties.Matl_Props table was not provided in toml file"); - } - - // Check to see if our table exists - if (table.contains("State_Vars")) { - // State variable properties are now obtained - const auto& state_table = toml::find(table, "State_Vars"); - numStateVars = toml::find_or(state_table, "num_vars", 1); - std::string _state_file = toml::find_or(state_table, "floc", "state.txt"); - state_file = _state_file; - if (!if_file_exists(state_file)) - { - MFEM_ABORT("State file does not exist"); - } - } - else { - MFEM_ABORT("Properties.State_Vars table was not provided in toml file"); - } - - // Check to see if our table exists - if (table.contains("Grain")) { - // Grain related properties are now obtained - const auto& grain_table = toml::find(table, "Grain"); - grain_statevar_offset = toml::find_or(grain_table, "ori_state_var_loc", -1); - grain_custom_stride = toml::find_or(grain_table, "ori_stride", 0); - std::string _ori_type = toml::find_or(grain_table, "ori_type", "euler"); - ngrains = toml::find_or(grain_table, "num_grains", 0); - std::string _ori_file = toml::find_or(grain_table, "ori_floc", "ori.txt"); - ori_file = _ori_file; - std::string _grain_map = toml::find_or(grain_table, "grain_floc", "grain_map.txt"); - grain_map = _grain_map; - - if (grain_table.contains("ori_floc")) { - if (!if_file_exists(ori_file)) - { - MFEM_ABORT("Orientation file does not exist"); - } - } - - if (grain_table.contains("grain_floc")) { - if (grain_map.size() > 0 && !if_file_exists(grain_map) and (mesh_type == MeshType::AUTO)) - { - MFEM_ABORT("Grain file does not exist"); - } - } - - // I still can't believe C++ doesn't allow strings to be used in switch statements... - if ((_ori_type == "euler") || _ori_type == "Euler" || (_ori_type == "EULER")) { - ori_type = OriType::EULER; - } - else if ((_ori_type == "quat") || (_ori_type == "Quat") || (_ori_type == "quaternion") || (_ori_type == "Quaternion")) { - ori_type = OriType::QUAT; - } - else if ((_ori_type == "custom") || (_ori_type == "Custom") || (_ori_type == "CUSTOM")) { - ori_type = OriType::CUSTOM; - } - else { - MFEM_ABORT("Properties.Grain.ori_type was not provided a valid type."); - ori_type = OriType::NOTYPE; - } - } // end of if statement for grain data -} // End of propert parsing - -// From the toml file it finds all the values related to the BCs -void ExaOptions::get_bcs() -{ - const auto data = toml::parse(floc); - const auto& table = toml::find(data, "BCs"); - - changing_bcs = toml::find_or(table, "changing_ess_bcs", false); - mono_def_flag = toml::find_or(table, "expt_mono_def_flag", false); - - vgrad_origin = toml::find_or>(table, "vgrad_origin", {}); - vgrad_origin_flag = !vgrad_origin.empty(); - - if (vgrad_origin_flag && vgrad_origin.size() != 3) { - MFEM_ABORT("BCs.vgrad_origin when provided must contain 3 components."); - } - - if (!changing_bcs) { - std::vector _essential_ids = toml::find>(table, "essential_ids"); - if (_essential_ids.empty()) { - MFEM_ABORT("BCs.essential_ids was not provided any values."); - } - map_ess_id["total"][0] = std::vector(); - map_ess_id["total"][1] = _essential_ids; - - std::vector _essential_comp = toml::find>(table, "essential_comps"); - if (_essential_comp.empty()) { - MFEM_ABORT("BCs.essential_comps was not provided any values."); - } - - map_ess_comp["total"][0] = std::vector(); - map_ess_comp["total"][1] = _essential_comp; - - std::vector _ess_vel_comp; - std::vector _ess_vgrad_comp; - std::vector _ess_vel_id; - std::vector _ess_vgrad_id; - - int count = 0; - - int ess_vel_conditions = 0; - int ess_vgrad_conditions = 0; - - for (auto& item : _essential_comp) { - if (item >= 0) { - ess_vel_conditions++; - _ess_vel_comp.push_back(item); - _ess_vel_id.push_back(_essential_ids.at(count)); - _ess_vgrad_comp.push_back(0); - _ess_vgrad_id.push_back(_essential_ids.at(count)); - } else { - ess_vgrad_conditions++; - _ess_vel_comp.push_back(0); - _ess_vel_id.push_back(_essential_ids.at(count)); - _ess_vgrad_comp.push_back(std::abs(item)); - _ess_vgrad_id.push_back(_essential_ids.at(count)); - } - count++; - } - - map_ess_id["ess_vel"][0] = std::vector(); - map_ess_id["ess_vel"][1] = _ess_vel_id; - - map_ess_comp["ess_vel"][0] = std::vector(); - map_ess_comp["ess_vel"][1] = _ess_vel_comp; - - map_ess_id["ess_vgrad"][0] = std::vector(); - map_ess_id["ess_vgrad"][1] = _ess_vgrad_id; - - map_ess_comp["ess_vgrad"][0] = std::vector(); - map_ess_comp["ess_vgrad"][1] = _ess_vgrad_comp; - - // Getting out arrays of values isn't always the simplest thing to do using - // this TOML libary. - std::vector _essential_vals = toml::find_or>(table, "essential_vals", {}); - if (_essential_vals.empty() && ess_vel_conditions > 0) { - MFEM_ABORT("BCs.essential_vals was not provided any values but a boundary requires this."); - } - - std::vector> _essential_vgrad = toml::find_or>>(table, "essential_vel_grad", {{}}); - if (_essential_vgrad.empty() && ess_vgrad_conditions > 0) { - MFEM_ABORT("BCs.essential_vel_grad was not provided any values but a boundary requires this."); - } - - map_ess_vgrad[0] = std::vector(9, 0.0); - map_ess_vgrad[1] = std::vector(); - - for(auto && v : _essential_vgrad) { - map_ess_vgrad[1].insert(map_ess_vgrad[1].end(), v.begin(), v.end()); - } - - map_ess_vel[0] = std::vector(); - map_ess_vel[1] = _essential_vals; - updateStep.push_back(1); - } - else { - updateStep = toml::find>(table, "update_steps"); - - if (updateStep.empty()) { - MFEM_ABORT("BCs.update_steps was not provided any values."); - } - if (std::find(updateStep.begin(), updateStep.end(), 1) == updateStep.end()) { - MFEM_ABORT("BCs.update_steps must contain 1 in the array"); - } - - int size = updateStep.size(); - std::vector> nested_ess_ids = toml::find>>(table, "essential_ids"); - int ilength = 0; - map_ess_id["total"][0] = std::vector(); - map_ess_id["ess_vel"][0] = std::vector(); - map_ess_id["ess_vgrad"][0] = std::vector(); - for (const auto &vec : nested_ess_ids) { - int key = updateStep.at(ilength); - map_ess_id["total"][key] = std::vector(); - map_ess_id["ess_vel"][key] = std::vector(); - map_ess_id["ess_vgrad"][key] = std::vector(); - for (const auto &val : vec) { - map_ess_id["total"][key].push_back(val); - } - if (map_ess_id["total"][key].empty()) { - MFEM_ABORT("BCs.essential_ids contains empty array."); - } - ilength += 1; - } - - if (ilength != size) { - MFEM_ABORT("BCs.essential_ids did not contain the same number of arrays as number of update steps"); - } - - std::vector> nested_ess_comps = toml::find>>(table, "essential_comps"); - ilength = 0; - map_ess_comp["total"][0] = std::vector(); - map_ess_comp["ess_vel"][0] = std::vector(); - map_ess_comp["ess_vgrad"][0] = std::vector(); - - int ess_vel_conditions = 0; - int ess_vgrad_conditions = 0; - - for (const auto &vec : nested_ess_comps) { - int key = updateStep.at(ilength); - map_ess_comp["total"][key] = std::vector(); - map_ess_comp["ess_vel"][key] = std::vector(); - map_ess_comp["ess_vgrad"][key] = std::vector(); - int count = 0; - for (const auto &val : vec) { - map_ess_comp["total"][key].push_back(val); - if (val >= 0) { - ess_vel_conditions++; - map_ess_comp["ess_vel"][key].push_back(val); - map_ess_id["ess_vel"][key].push_back(map_ess_id["total"][key].at(count)); - map_ess_comp["ess_vgrad"][key].push_back(0); - map_ess_id["ess_vgrad"][key].push_back(map_ess_id["total"][key].at(count)); - } else { - ess_vgrad_conditions++; - map_ess_comp["ess_vel"][key].push_back(0); - map_ess_id["ess_vel"][key].push_back(map_ess_id["total"][key].at(count)); - map_ess_comp["ess_vgrad"][key].push_back(std::abs(val)); - map_ess_id["ess_vgrad"][key].push_back(map_ess_id["total"][key].at(count)); - } - count++; - } - if (map_ess_comp["total"][key].empty()) { - MFEM_ABORT("BCs.essential_comps contains empty array."); - } - ilength += 1; - } - - if (ilength != size) { - MFEM_ABORT("BCs.essential_comps did not contain the same number of arrays as number of update steps"); - } - - std::vector> nested_ess_vals = toml::find_or>>(table, "essential_vals", {{}}); - ilength = 0; - map_ess_vel[0] = std::vector(); - for (const auto &vec : nested_ess_vals) { - int key = updateStep.at(ilength); - map_ess_vel[key] = std::vector(); - for (const auto &val : vec) { - map_ess_vel[key].push_back(val); - } - if (map_ess_vel[key].empty() && ess_vel_conditions > 0) { - MFEM_ABORT("BCs.essential_vals contains empty array but a boundary requires this."); - } - ilength += 1; - } - - std::vector>> nested_ess_vgrad = toml::find_or>> >(table, "essential_vel_grad", {{{}}}); - ilength = 0; - map_ess_vgrad[0] = std::vector(9, 0.0); - - for (const auto &vec : nested_ess_vgrad) { - int key = updateStep.at(ilength); - map_ess_vgrad[key] = std::vector(); - for(auto && v : vec) { - map_ess_vgrad[key].insert(map_ess_vgrad[key].end(), v.begin(), v.end()); - } - if (map_ess_vgrad[key].empty() && ess_vgrad_conditions > 0) { - MFEM_ABORT("BCs.essential_vel_grad was not provided any values but a boundary requires this.."); - } - ilength += 1; - } - } -} // end of parsing BCs - -// From the toml file it finds all the values related to the model -void ExaOptions::get_model() -{ - const auto data = toml::parse(floc); - const auto& table = toml::find(data, "Model"); - std::string _mech_type = toml::find_or(table, "mech_type", ""); - - // I still can't believe C++ doesn't allow strings to be used in switch statements... - if ((_mech_type == "umat") || (_mech_type == "Umat") || (_mech_type == "UMAT") || (_mech_type == "UMat")) { - mech_type = MechType::UMAT; - } - else if ((_mech_type == "exacmech") || (_mech_type == "Exacmech") || (_mech_type == "ExaCMech") || (_mech_type == "EXACMECH")) { - mech_type = MechType::EXACMECH; - } - else { - MFEM_ABORT("Model.mech_type was not provided a valid type."); - mech_type = MechType::NOTYPE; - } - - cp = toml::find_or(table, "cp", false); - - if (mech_type == MechType::EXACMECH) { - if (!cp) { - MFEM_ABORT("Model.cp needs to be set to true when using ExaCMech based models."); - } - - if (ori_type != OriType::QUAT) { - MFEM_ABORT("Properties.Grain.ori_type is not set to quaternion for use with an ExaCMech model."); - } - - grain_statevar_offset = ecmech::evptn::iHistLbQ; - - if(table.contains("ExaCMech")) { - const auto& exacmech_table = toml::find(table, "ExaCMech"); - - shortcut = toml::find_or(exacmech_table, "shortcut", ""); - if (shortcut.size() == 0) { - std::string xtal_type = toml::find_or(exacmech_table, "xtal_type", ""); - std::string slip_type = toml::find_or(exacmech_table, "slip_type", ""); - shortcut = "evptn_"; - if ((xtal_type == "fcc") || (xtal_type == "FCC")) { - shortcut += "FCC_"; - } - else if ((xtal_type == "bcc") || (xtal_type == "BCC")) { - shortcut += "BCC_"; - } - else if ((xtal_type == "hcp") || (xtal_type == "HCP")) { - shortcut += "HCP_"; - } - else { - MFEM_ABORT("Model.ExaCMech.xtal_type was not provided a valid type."); - } - if ((slip_type == "mts") || (slip_type == "MTS") || (slip_type == "mtsdd") || (slip_type == "MTSDD")) { - if ((xtal_type == "hcp") || (xtal_type == "HCP")) { - shortcut += "A"; - } - else { - shortcut += "B"; - } - } - else if ((slip_type == "powervoce") || (slip_type == "PowerVoce") || (slip_type == "POWERVOCE")) { - if ((xtal_type == "hcp") || (xtal_type == "HCP")) { - MFEM_ABORT("Model.ExaCMech.slip_type can not be PowerVoce for HCP materials.") - } - shortcut += "A"; - } - else if ((slip_type == "powervocenl") || (slip_type == "PowerVoceNL") || (slip_type == "POWERVOCENL")) { - if ((xtal_type == "hcp") || (xtal_type == "HCP")) { - MFEM_ABORT("Model.ExaCMech.slip_type can not be PowerVoce for HCP materials.") - } - shortcut += "AH"; - } - else { - MFEM_ABORT("Model.ExaCMech.slip_type was not provided a valid type."); - } - } - - try { - [[maybe_unused]] auto unused = ecmech::makeMatModel(shortcut); - } catch(...) { - MFEM_ABORT("Model.ExaCMech.shortcut was not provided a valid name."); - } - - auto index_map = ecmech::modelParamIndexMap(shortcut); - auto num_props_check = index_map["num_params"]; - auto num_state_vars_check = index_map["num_hist"] + ecmech::ne + 1 - 4; - - gdot_size = index_map["num_slip_system"]; - hard_size = index_map["num_hardening"]; - - - if (numStateVars != (int) num_state_vars_check) { - MFEM_ABORT("Properties.State_Vars.num_vars needs " << num_state_vars_check << " values for the given material choice" - "Note: the number of values for a quaternion " - "are not included in this count."); - } - - if (nProps != (int) num_props_check) { - MFEM_ABORT("Properties.Matl_Props.num_props needs " << num_props_check << " values for the given material choice" - "Note: the number of values for a quaternion " - "are not included in this count."); - } - } - else { - MFEM_ABORT("The table Model.ExaCMech does not exist, but the model being used is ExaCMech."); - }// End if ExaCMech Table Exists - } -} // end of model parsing - -// From the toml file it finds all the values related to the time -void ExaOptions::get_time_steps() -{ - const auto data = toml::parse(floc); - const auto& table = toml::find(data, "Time"); - // First look at the fixed time stuff - // check to see if our table exists - if (table.contains("Fixed")) { - const auto& fixed_table = toml::find(table, "Fixed"); - dt_cust = false; - dt_auto = false; - dt = toml::find_or(fixed_table, "dt", 1.0); - dt_min = dt; - t_final = toml::find_or(fixed_table, "t_final", 1.0); - } - if (table.contains("Auto")) { - if (changing_bcs) { - MFEM_ABORT("Automatic time stepping is currently not compatible with changing boundary conditions"); - } - const auto& auto_table = toml::find(table, "Auto"); - dt_cust = false; - dt_auto = true; - dt = toml::find_or(auto_table, "dt_start", 1.0); - dt_scale = toml::find_or(auto_table, "dt_scale", 0.25); - if (dt_scale < 0.0 || dt_scale > 1.0) { - MFEM_ABORT("dt_scale for auto time stepping needs to be between 0 and 1."); - } - dt_min = toml::find_or(auto_table, "dt_min", 1.0); - dt_max = toml::find_or(auto_table, "dt_max", std::numeric_limits::max()); - t_final = toml::find_or(auto_table, "t_final", 1.0); - dt_file = toml::find_or(auto_table, "auto_dt_file", "auto_dt_out.txt"); - } - // Time to look at our custom time table stuff - // check to see if our table exists - if (table.contains("Custom")) { - const auto& cust_table = toml::find(table, "Custom"); - dt_cust = true; - dt_auto = false; - nsteps = toml::find_or(cust_table, "nsteps", 1); - std::string _dt_file = toml::find_or(cust_table, "floc", "custom_dt.txt"); - dt_file = _dt_file; - } -} // end of time step parsing - -// From the toml file it finds all the values related to the visualizations -void ExaOptions::get_visualizations() -{ - const auto data = toml::parse(floc); - const auto& table = toml::find(data, "Visualizations"); - vis_steps = toml::find_or(table, "steps", 1); - visit = toml::find_or(table, "visit", false); - conduit = toml::find_or(table, "conduit", false); - paraview = toml::find_or(table, "paraview", false); - adios2 = toml::find_or(table, "adios2", false); - if (conduit || adios2) { - if (conduit) { -#ifndef MFEM_USE_CONDUIT - MFEM_ABORT("MFEM was not built with conduit."); -#endif - } - else { -#ifndef MFEM_USE_ADIOS2 - MFEM_ABORT("MFEM was not built with ADIOS2"); -#endif - } - } - std::string _basename = toml::find_or(table, "floc", "results/exaconstit"); - basename = _basename; - std::string _avg_stress_fname = toml::find_or(table, "avg_stress_fname", "avg_stress.txt"); - avg_stress_fname = _avg_stress_fname; - bool _additional_avgs = toml::find_or(table, "additional_avgs", false); - additional_avgs = _additional_avgs; - std::string _avg_def_grad_fname = toml::find_or(table, "avg_def_grad_fname", "avg_def_grad.txt"); - avg_def_grad_fname = _avg_def_grad_fname; - std::string _avg_euler_strain_fname = toml::find_or(table, "avg_euler_strain_fname", "avg_euler_strain.txt"); - avg_euler_strain_fname = _avg_euler_strain_fname; - std::string _avg_pl_work_fname = toml::find_or(table, "avg_pl_work_fname", "avg_pl_work.txt"); - avg_pl_work_fname = _avg_pl_work_fname; - avg_eps_fname = toml::find_or(table, "avg_eps_fname", "avg_eps.txt"); - light_up = toml::find_or(table, "light_up", false); - if (light_up) { - - auto hkls = toml::find_or< std::vector> >(table, "light_up_hkl", {{}}); - - for (auto& hkl : hkls) { - std::array hkl_tmp = {hkl[0], hkl[1], hkl[2]}; - std::cout << "light-up hkls " << hkl_tmp[0] << " " << hkl_tmp[1] << " " << hkl_tmp[2] << std::endl; - light_hkls.push_back(hkl_tmp); - } - - light_dist_tol = toml::find_or(table, "light_dist_tol", {0.07}); - std::cout << "light-up distance tolerance " << light_dist_tol << std::endl; - auto s_dirs = toml::find_or>(table, "light_s_dir", {}); - - light_s_dir[0] = s_dirs[0]; - light_s_dir[1] = s_dirs[1]; - light_s_dir[2] = s_dirs[2]; - - std::cout << "light-up s direction " << light_s_dir[0] << " " << light_s_dir[1] << " " << light_s_dir[2] << std::endl; - - auto lparams = toml::find_or>(table, "lattice_params", {}); - - lattice_params[0] = lparams[0]; - lattice_params[1] = lparams[1]; - lattice_params[2] = lparams[2]; - - std::cout << "light-up lattice params " << lattice_params[0] << " " << lattice_params[1] << " " << lattice_params[2] << std::endl; - - lattice_basename = toml::find_or(table, "lattice_basename", "lattice_avg_"); - - } -} // end of visualization parsing - -// From the toml file it finds all the values related to the Solvers -void ExaOptions::get_solvers() -{ - const auto data = toml::parse(floc); - const auto& table = toml::find(data, "Solvers"); - std::string _assembly = toml::find_or(table, "assembly", "FULL"); - if ((_assembly == "FULL") || (_assembly == "full")) { - assembly = Assembly::FULL; - } - else if ((_assembly == "PA") || (_assembly == "pa")) { - assembly = Assembly::PA; - } - else if ((_assembly == "EA") || (_assembly == "ea")) { - assembly = Assembly::EA; - } - else { - MFEM_ABORT("Solvers.assembly was not provided a valid type."); - assembly = Assembly::NOTYPE; - } - - std::string _rtmodel = toml::find_or(table, "rtmodel", "CPU"); - if ((_rtmodel == "CPU") || (_rtmodel == "cpu")) { - rtmodel = RTModel::CPU; - } -#if defined(RAJA_ENABLE_OPENMP) - else if ((_rtmodel == "OPENMP") || (_rtmodel == "OpenMP")|| (_rtmodel == "openmp")) { - rtmodel = RTModel::OPENMP; - } -#endif -#if defined(RAJA_ENABLE_CUDA) || defined(RAJA_ENABLE_HIP) - else if ((_rtmodel == "GPU") || (_rtmodel == "gpu")) { - if (assembly == Assembly::FULL) { - MFEM_ABORT("Solvers.rtmodel can't be GPU if Solvers.rtmodel is FULL."); - } - rtmodel = RTModel::GPU; - } -#endif - else { - MFEM_ABORT("Solvers.rtmodel was not provided a valid type."); - rtmodel = RTModel::NOTYPE; - } - - if (table.contains("NR")) { - // Obtaining information related to the newton raphson solver - const auto& nr_table = toml::find(table, "NR"); - std::string _solver = toml::find_or(nr_table, "nl_solver", "NR"); - if ((_solver == "nr") || (_solver == "NR")) { - nl_solver = NLSolver::NR; - } - else if ((_solver == "nrls") || (_solver == "NRLS")) { - nl_solver = NLSolver::NRLS; - } - else { - MFEM_ABORT("Solvers.NR.nl_solver was not provided a valid type."); - nl_solver = NLSolver::NOTYPE; - } - newton_iter = toml::find_or(nr_table, "iter", 25); - newton_rel_tol = toml::find_or(nr_table, "rel_tol", 1e-5); - newton_abs_tol = toml::find_or(nr_table, "abs_tol", 1e-10); - } // end of NR info - - std::string _integ_model = toml::find_or(table, "integ_model", "FULL"); - if ((_integ_model == "FULL") || (_integ_model == "full")) { - integ_type = IntegrationType::FULL; - } - else if ((_integ_model == "BBAR") || (_integ_model == "bbar")) { - integ_type = IntegrationType::BBAR; - if (nl_solver == NLSolver::NR) { - std::cout << "BBar method performs better when paired with a NR solver with line search" << std::endl; - } - } - - if (table.contains("Krylov")) { - // Now getting information about the Krylov solvers used to the linearized - // system of equations of the nonlinear problem. - auto iter_table = toml::find(table, "Krylov"); - krylov_iter = toml::find_or(iter_table, "iter", 200); - krylov_rel_tol = toml::find_or(iter_table, "rel_tol", 1e-10); - krylov_abs_tol = toml::find_or(iter_table, "abs_tol", 1e-30); - std::string _solver = toml::find_or(iter_table, "solver", "GMRES"); - if ((_solver == "GMRES") || (_solver == "gmres")) { - solver = KrylovSolver::GMRES; - } - else if ((_solver == "PCG") || (_solver == "pcg")) { - solver = KrylovSolver::PCG; - } - else if ((_solver == "MINRES") || (_solver == "minres")) { - solver = KrylovSolver::MINRES; - } - else { - MFEM_ABORT("Solvers.Krylov.solver was not provided a valid type."); - solver = KrylovSolver::NOTYPE; - } - } // end of krylov solver info -} // end of solver parsing - -// From the toml file it finds all the values related to the mesh -void ExaOptions::get_mesh() -{ - // Refinement of the mesh and element order - const auto data = toml::parse(floc); - const auto& table = toml::find(data, "Mesh"); - ser_ref_levels = toml::find_or(table, "ref_ser", 0); - par_ref_levels = toml::find_or(table, "ref_par", 0); - order = toml::find_or(table, "p_refinement", 1); - // file location of the mesh - std::string _mesh_file = toml::find_or(table, "floc", "../../data/cube-hex-ro.mesh"); - mesh_file = _mesh_file; - // Type of mesh that we're reading/going to generate - std::string mtype = toml::find_or(table, "type", "other"); - if ((mtype == "cubit") || (mtype == "Cubit") || (mtype == "CUBIT")) { - mesh_type = MeshType::CUBIT; - } - else if ((mtype == "auto") || (mtype == "Auto") || (mtype == "AUTO")) { - mesh_type = MeshType::AUTO; - if (table.contains("Auto")){ - auto auto_table = toml::find(table, "Auto"); - std::vector _mxyz = toml::find>(auto_table, "length"); - if (_mxyz.size() != 3) { - MFEM_ABORT("Mesh.Auto.length was not provided a valid array of size 3."); - } - mxyz[0] = _mxyz[0]; - mxyz[1] = _mxyz[1]; - mxyz[2] = _mxyz[2]; - - std::vector _nxyz = toml::find>(auto_table, "ncuts"); - if (_nxyz.size() != 3) { - MFEM_ABORT("Mesh.Auto.ncuts was not provided a valid array of size 3."); - } - nxyz[0] = _nxyz[0]; - nxyz[1] = _nxyz[1]; - nxyz[2] = _nxyz[2]; - } - else { - MFEM_ABORT("Mesh.type was set to Auto but Mesh.Auto does not exist"); - } - } - else if ((mtype == "other") || (mtype == "Other") || (mtype == "OTHER")) { - mesh_type = MeshType::OTHER; - } - else { - MFEM_ABORT("Mesh.type was not provided a valid type."); - mesh_type = MeshType::NOTYPE; - } // end of mesh type parsing - - if (mesh_type == MeshType::OTHER || mesh_type == MeshType::CUBIT) { - if (!if_file_exists(mesh_file)) - { - MFEM_ABORT("Mesh file does not exist"); - } - } -} // End of mesh parsing - -void ExaOptions::print_options() -{ - std::cout << "Mesh file location: " << mesh_file << std::endl; - std::cout << "Mesh type: "; - if (mesh_type == MeshType::OTHER) { - std::cout << "other"; - } - else if (mesh_type == MeshType::CUBIT) { - std::cout << "cubit"; - } - else { - std::cout << "auto"; - } - std::cout << std::endl; - - std::cout << "Edge dimensions (mx, my, mz): " << mxyz[0] << " " << mxyz[1] << " " << mxyz[2] << std::endl; - std::cout << "Number of cells on an edge (nx, ny, nz): " << nxyz[0] << " " << nxyz[1] << " " << nxyz[2] << std::endl; - - std::cout << "Serial Refinement level: " << ser_ref_levels << std::endl; - std::cout << "Parallel Refinement level: " << par_ref_levels << std::endl; - std::cout << "P-refinement level: " << order << std::endl; - - std::cout << std::boolalpha; - if (dt_cust) { - std::cout << "Custom time stepping on" << std::endl; - std::cout << "Number of time steps (nsteps): " << nsteps << std::endl; - std::cout << "Custom time file loc (dt_file): " << dt_file << std::endl; - } - else if (dt_auto) - { - std::cout << "Auto time stepping on" << std::endl; - std::cout << "Final time (t_final): " << t_final << std::endl; - std::cout << "Initial time step (dt): " << dt << std::endl; - std::cout << "Minimum time step (dt): " << dt_min << std::endl; - std::cout << "Time step scale factor: " << dt_scale << std::endl; - std::cout << "Auto time step output file: " << dt_file << std::endl; - } - else - { - std::cout << "Constant time stepping on" << std::endl; - std::cout << "Final time (t_final): " << t_final << std::endl; - std::cout << "Time step (dt): " << dt << std::endl; - } - - std::cout << "Visit flag: " << visit << std::endl; - std::cout << "Conduit flag: " << conduit << std::endl; - std::cout << "Paraview flag: " << paraview << std::endl; - std::cout << "ADIOS2 flag: " << adios2 << std::endl; - std::cout << "Visualization steps: " << vis_steps << std::endl; - std::cout << "Visualization directory: " << basename << std::endl; - - std::cout << "Average stress filename: " << avg_stress_fname << std::endl; - if (additional_avgs) - { - std::cout << "Additional averages being computed" << std::endl; - std::cout << "Average deformation gradient filename: " << avg_def_grad_fname << std::endl; - std::cout << "Average plastic work filename: " << avg_pl_work_fname << std::endl; - std::cout << "Average eulerian strain filename: " << avg_euler_strain_fname << std::endl; - std::cout << "Average equivalent plastic strain filename: " << avg_eps_fname << std::endl; - - - } - else - { - std::cout << "No additional averages being computed" << std::endl; - } - std::cout << "Average stress filename: " << avg_stress_fname << std::endl; - std::cout << "Light-up flag: " << light_up << std::endl; - - if (light_up) { - for (auto& hkl : light_hkls) { - std::array hkl_tmp = {hkl[0], hkl[1], hkl[2]}; - std::cout << "light-up: hkls " << hkl_tmp[0] << " " << hkl_tmp[1] << " " << hkl_tmp[2] << std::endl; - } - std::cout << "light-up: distance tolerance " << light_dist_tol << std::endl; - std::cout << "light-up: s direction " << light_s_dir[0] << " " << light_s_dir[1] << " " << light_s_dir[2] << std::endl; - std::cout << "light-up: lattice params " << lattice_params[0] << " " << lattice_params[1] << " " << lattice_params[2] << std::endl; - std::cout << "light-up: lattice basename: " << lattice_basename << std::endl; - } - - if (nl_solver == NLSolver::NR) { - std::cout << "Nonlinear Solver is Newton Raphson" << std::endl; - } - else if (nl_solver == NLSolver::NRLS) { - std::cout << "Nonlinear Solver is Newton Raphson with a line search" << std::endl; - } - - std::cout << "Newton Raphson rel. tol.: " << newton_rel_tol << std::endl; - std::cout << "Newton Raphson abs. tol.: " << newton_abs_tol << std::endl; - std::cout << "Newton Raphson # of iter.: " << newton_iter << std::endl; - std::cout << "Newton Raphson grad debug: " << grad_debug << std::endl; - - if (integ_type == IntegrationType::FULL) { - std::cout << "Integration Type: Full" << std::endl; - } - else if (integ_type == IntegrationType::BBAR) { - std::cout << "Integration Type: BBar" << std::endl; - } - - std::cout << "Krylov solver: "; - if (solver == KrylovSolver::GMRES) { - std::cout << "GMRES"; - } - else if (solver == KrylovSolver::PCG) { - std::cout << "PCG"; - } - else { - std::cout << "MINRES"; - } - std::cout << std::endl; - - std::cout << "Krylov solver rel. tol.: " << krylov_rel_tol << std::endl; - std::cout << "Krylov solver abs. tol.: " << krylov_abs_tol << std::endl; - std::cout << "Krylov solver # of iter.: " << krylov_iter << std::endl; - - std::cout << "Matrix Assembly is: "; - if (assembly == Assembly::FULL) { - std::cout << "Full Assembly" << std::endl; - } - else if (assembly == Assembly::PA) { - std::cout << "Partial Assembly" << std::endl; - } - else { - std::cout << "Element Assembly" << std::endl; - } - - std::cout << "Runtime model is: "; - if (rtmodel == RTModel::CPU) { - std::cout << "CPU" << std::endl; - } - else if (rtmodel == RTModel::GPU) { - std::cout << "GPU" << std::endl; - } - else if (rtmodel == RTModel::OPENMP) { - std::cout << "OpenMP" << std::endl; - } - - std::cout << "Mechanical model library being used "; - - if (mech_type == MechType::UMAT) { - std::cout << "UMAT" << std::endl; - } - else if (mech_type == MechType::EXACMECH) { - - auto shortcut_delim = [](std::string & str, std::string delim) -> std::vector { - auto start = 0U; - auto end = str.find(delim); - std::vector sdelim; - while (end != std::string::npos) - { - sdelim.push_back(str.substr(start, end - start)); - start = end + delim.length(); - end = str.find(delim, start); - } - sdelim.push_back(str.substr(start, end - start)); - return sdelim; - }; - - auto sdelim = shortcut_delim(shortcut, "_"); - - std::cout << "ExaCMech" << std::endl; - std::cout << "ExaCMech shortcut name: " << shortcut << std::endl; - std::cout << "Crystal symmetry group is " << sdelim[1] << std::endl; - } - - std::cout << "Xtal Plasticity being used: " << cp << std::endl; - - std::cout << "Orientation file location: " << ori_file << std::endl; - std::cout << "Grain map file location: " << grain_map << std::endl; - std::cout << "Number of grains: " << ngrains << std::endl; - - std::cout << "Orientation type: "; - if (ori_type == OriType::EULER) { - std::cout << "euler"; - } - else if (ori_type == OriType::QUAT) { - std::cout << "quaternion"; - } - else { - std::cout << "custom"; - } - std::cout << std::endl; - - std::cout << "Custom stride to read grain map file: " << grain_custom_stride << std::endl; - std::cout << "Orientation offset in state variable file: " << grain_statevar_offset << std::endl; - - std::cout << "Number of properties: " << nProps << std::endl; - std::cout << "Property file location: " << props_file << std::endl; - - std::cout << "Number of state variables: " << numStateVars << std::endl; - std::cout << "State variable file location: " << state_file << std::endl; - - if (mono_def_flag) { - std::cout << "Making use of experimental monotonic deformation BCs option" << std::endl; - } - - for (const auto key: updateStep) - { - std::cout << "Starting on step " << key << " essential BCs values are:" << std::endl; - std::cout << "Essential ids are set as: "; - for (const auto & val: map_ess_id["total"][key]) { - std::cout << val << " "; - } - std::cout << std::endl << "Essential components are set as: "; - for (const auto & val: map_ess_comp["total"][key]) { - std::cout << val << " "; - } - if (map_ess_vel[key].size() > 0) { - std::cout << std::endl << "Essential boundary velocity values are set as: "; - for (const auto & val: map_ess_vel.at(key)) { - std::cout << val << " "; - } - } - if (map_ess_vgrad[key].size() > 0) { - std::cout << std::endl << "Essential boundary velocity gradients are set as: "; - for (const auto & val: map_ess_vgrad.at(key)) { - std::cout << val << " "; - } - } - std::cout << std::endl; - } -} // End of printing out options diff --git a/src/option_parser.hpp b/src/option_parser.hpp deleted file mode 100644 index 44aa925..0000000 --- a/src/option_parser.hpp +++ /dev/null @@ -1,274 +0,0 @@ - -#ifndef option_parser_hpp -#define option_parser_hpp - -#include -#include -#include -#include // for std::unordered_map -#include -#include "mfem.hpp" -#include "option_types.hpp" - -typedef std::map >> map_of_imap; - -class ExaOptions { - public: - - // mesh variables - std::string mesh_file; - MeshType mesh_type; - double mxyz[3]; // edge dimensions (mx, my, mz) - int nxyz[3]; // number of cells on an edge (nx, ny, nz) - - - // serial and parallel refinement levels - int ser_ref_levels; - int par_ref_levels; - - // polynomial interpolation order - int order; - - // final simulation time and time step (set each to 1.0 for - // single step debug) - double t_final; - double dt; - double dt_min; - double dt_max; - double dt_scale; - // We have a custom dt flag - bool dt_cust; - bool dt_auto; - // Number of time steps to take - int nsteps; - // File to read the custom time steps from - std::string dt_file; - // Vector to hold custom time steps if there are any - mfem::Vector cust_dt; - - // visualization input args - int vis_steps; - // visualization variable for visit - bool visit; - bool conduit; - bool paraview; - bool adios2; - // Where to store the end time step files - std::string basename; - // average stress file name - std::string avg_stress_fname; - std::string avg_pl_work_fname; - std::string avg_eps_fname; - std::string avg_def_grad_fname; - std::string avg_euler_strain_fname; - bool additional_avgs; - // light up values - bool light_up = false; - std::vector> light_hkls = {}; - double light_dist_tol = 0.0; - double light_s_dir[3] = {}; - double lattice_params[3] = {}; - std::string lattice_basename = "lattice_avg_"; - - // newton input args - double newton_rel_tol; - double newton_abs_tol; - int newton_iter; - NLSolver nl_solver; - - // Integration type - IntegrationType integ_type; - - // solver input args - // GMRES is currently set as the default iterative solver - // until the bug in the PCG solver is found and fixed. - bool grad_debug; - double krylov_rel_tol; - double krylov_abs_tol; - int krylov_iter; - - KrylovSolver solver; - - // input arg to specify crystal plasticity - bool cp; - - // The type of mechanical interface that we'll be using - MechType mech_type; - // shortcut name for the material we're using - std::string shortcut; - // gdot size is known now from option size - size_t gdot_size = 1; - size_t hard_size = 1; - // Specify the temperature of the material - double temp_k; - - - // grain input arguments - std::string ori_file; // grain orientations (F_p_inv for Curt's UMAT?) - std::string grain_map; // map of grain id to element centroid - int ngrains; - OriType ori_type; - int grain_custom_stride; // TODO check that this is used with "grain_custom" - int grain_statevar_offset; - - // material properties input arguments - std::string props_file; - int nProps; // at least have one dummy property - - // state variables file with constant values used to initialize ALL integration points - std::string state_file; - int numStateVars; // at least have one dummy property - - // boundary condition input args - bool changing_bcs = false; - std::vector updateStep; - // vector of velocity components for each attribute in ess_id if not - // using constant strain rate conditions - std::unordered_map > map_ess_vel; - // velocity gradient components if using constant strain rate - // conditions. The storage of the components is unrolled matrix in row ordering - std::unordered_map > map_ess_vgrad; - // component combo (free = 0, x = 1, y = 2, z = 3, - // xy = 4, yz = 5, xz = 6, xyz = 7) - // Negative values here would signify that we are using a velocity - // gradient constraint for a given boundary. - map_of_imap map_ess_comp; - // essential bc ids for the whole boundary - // Holds the total BC ids using key "total", - // those associated with ess_vel using "ess_vel", - // and finally those associated with ess_vgrad using "ess_vgrad". - map_of_imap map_ess_id; - - bool vgrad_origin_flag = false; - std::vector vgrad_origin; - - // experimental flag option - bool mono_def_flag = false; - - // Parse the TOML file for all of the various variables. - // In other words this is our driver to get all of the values. - void parse_options(int my_id); - - RTModel rtmodel; - Assembly assembly; - - ExaOptions(std::string _floc) : floc{_floc} - { - // Matl and State Property related variables - numStateVars = 1; - nProps = 1; - state_file = "state.txt"; - props_file = "props.txt"; - - // Grain related variables - grain_statevar_offset = -1; - grain_custom_stride = 1; - ori_type = OriType::EULER; - ngrains = 0; - grain_map = "grain_map.txt"; - ori_file = "ori.txt"; - - // Model related parameters - cp = false; - // umat = false; - // Want all of these to be not set. If they aren't specified - // then we want other things to fail in our driver file. - mech_type = MechType::NOTYPE; - // Specify the temperature of the material - temp_k = 298.; - - // Krylov Solver related variables - // We set the default solver as GMRES in case we accidentally end up dealing - // with a nonsymmetric matrix for our linearized system of equations. - solver = KrylovSolver::GMRES; - krylov_rel_tol = 1.0e-10; - krylov_abs_tol = 1.0e-30; - krylov_iter = 200; - - // NR parameters - newton_rel_tol = 1.0e-5; - newton_abs_tol = 1.0e-10; - newton_iter = 25; - nl_solver = NLSolver::NR; - grad_debug = false; - - // Integration type parameters - integ_type = IntegrationType::FULL; - - // Visualization related parameters - basename = "results/exaconstit"; - visit = false; - conduit = false; - paraview = false; - adios2 = false; - vis_steps = 1; - // - avg_stress_fname = "avg_stress.txt"; - avg_pl_work_fname = "avg_pl_work.txt"; - avg_def_grad_fname = "avg_def_grad.txt"; - avg_euler_strain_fname = "avg_euler_strain.txt"; - additional_avgs = false; - - // Time step related parameters - t_final = 1.0; - dt = 1.0; - dt_min = dt; - dt_max = dt; - dt_cust = false; - dt_auto = false; - nsteps = 1; - dt_file = "custom_dt.txt"; - - // Mesh related variables - ser_ref_levels = 0; - par_ref_levels = 0; - order = 1; - mesh_file = "../../data/cube-hex-ro.mesh"; - mesh_type = MeshType::OTHER; - - mxyz[0] = 1.0; - mxyz[1] = 1.0; - mxyz[2] = 1.0; - - nxyz[0] = 1; - nxyz[1] = 1; - nxyz[2] = 1; - - assembly = Assembly::FULL; - rtmodel = RTModel::CPU; - } // End of ExaOptions constructor - - virtual ~ExaOptions() {} - - protected: - std::string floc; - // From the toml file it finds all the values related to state and mat'l - // properties - void get_properties(); - - // From the toml file it finds all the values related to the BCs - void get_bcs(); - - // From the toml file it finds all the values related to the model - void get_model(); - - // From the toml file it finds all the values related to the time - void get_time_steps(); - - // From the toml file it finds all the values related to the visualizations - void get_visualizations(); - - // From the toml file it finds all the values related to the Solvers - void get_solvers(); - - // From the toml file it finds all the values related to the mesh - void get_mesh(); - - // Prints out a list of all the options being used - void print_options(); -}; - - - - -#endif /* option_parser_hpp */ diff --git a/src/option_types.hpp b/src/option_types.hpp deleted file mode 100644 index f5d2bc5..0000000 --- a/src/option_types.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef OPTION_TYPES -#define OPTION_TYPES - -// Taking advantage of C++11 to make it much clearer that we're using enums -enum class KrylovSolver { GMRES, PCG, MINRES, NOTYPE }; -enum class OriType { EULER, QUAT, CUSTOM, NOTYPE }; -enum class MeshType { CUBIT, AUTO, OTHER, NOTYPE }; -// Later on we'll want to support multiple different types here like -// BCC and HCP at a minimum. However, we'll need to wait on that support reaching -// ExaCMech -enum class XtalType { FCC, BCC, HCP, NOTYPE }; -// We currently only have support for UMATs and ExaCMech later on this might change -// to add support for more systems. -enum class MechType { UMAT, EXACMECH, NOTYPE }; -// Hardening law and slip kinetics we'll be using if ExaCMech is specified -// MTSDD refers to a MTS like slip kinetics with DD hardening evolution -// POWERVOCE refers to power law slip kinetics with a linear voce hardening law -// POWERVOCENL refers to power law slip kinetics with a nonlinear voce hardening law -// We might expand upon this later on as more options are added to ExaCMech -// If ExaCMech also eventually allows for the mix and match of different slip laws with -// power laws this will also change -enum class SlipType { MTSDD, POWERVOCE, POWERVOCENL, NOTYPE }; -// We're going to use this to determine what runtime model to use for our -// kernels and assembly operations. -enum class RTModel { CPU, GPU, OPENMP, NOTYPE }; -// The assembly model that we want to make use of FULL does the typical -// full assembly of all the elemental jacobian / tangent matrices, PA -// does a partial assembly type operations, and EA does an element assembly -// type operation. -// The full assembly should be faster for linear type elements and -// partial assembly should be faster for higher order elements. -// We'll have PA and EA on the GPU and the full might get on there as well at -// a later point in time. -// The PA is a matrix-free operation which means traditional preconditioners -// do not exist. Therefore, you'll be limited to Jacobi type preconditioners -// currently implemented. -enum class Assembly { PA, EA, FULL, NOTYPE }; - -// The nonlinear solver we're making use of to solve everything. -// The current options are Newton-Raphson or Newton-Raphson with a line search -enum class NLSolver { NR, NRLS, NOTYPE }; - -// Integration formulation that we want to use -enum class IntegrationType { FULL, BBAR, NOTYPE }; - -#endif diff --git a/src/options.toml b/src/options.toml index 48ee233..462dcd3 100644 --- a/src/options.toml +++ b/src/options.toml @@ -1,372 +1,815 @@ -# The below shows all of the options available and their default values. -# Although, it should be noted that the BCs options have no default values -# and require you to input ones that are appropriate for your problem. -# Also while the below is indented to make things easier to read the parser doesn't -# care about indentation. -# More information on TOML files can be found at: https://en.wikipedia.org/wiki/TOML -# and https://github.com/toml-lang/toml/blob/master/README.md -Version = "0.8.0" -[Properties] - # A base temperature that all models will initially run at - temperature = 298 - # The below informs us about the material properties to use - [Properties.Matl_Props] - floc = "props.txt" - num_props = 1 - # These options tell inform the program about the state variables - [Properties.State_Vars] - floc = "state.txt" - num_vars = 1 - # These options are only used in xtal plasticity problems - [Properties.Grain] - # Tells us where the orientations are located for either a UMAT or - # ExaCMech problem. -1 indicates that it goes at the end of the state - # variable file. - # If ExaCMech is used the loc value will be overriden with values that are - # consistent with the library's expected location +# ExaConstit Configuration File +# This file controls all aspects of your solid mechanics simulation. +# Lines starting with '#' are comments and will be ignored by the program. +# The file uses TOML format where options are organized in sections [Section] and subsections [[Section.Subsection]] + +# Version of ExaConstit this configuration is designed for +# This helps ensure compatibility - use the version shown when you run 'exaconstit --version' +Version = "0.9.0" + +# Base name for the simulation output directory +# This creates a subdirectory to organize all outputs from this simulation +# For example, if basename = "tensile_test", all outputs go in a "tensile_test" folder +# If not specified, defaults to the name of this configuration file (without .toml extension) +basename = "multi_material_test" + +# ===================================== +# MULTI-MATERIAL SETUP (Optional) +# ===================================== +# For simulations with multiple materials, you need to tell the code how to assign +# materials to different parts of your mesh + +# Maps grain IDs / existing mesh element attributes to material region numbers +# This file should have two columns: element attributes / Grain IDs -> material_region_number +# Example content: +# 1 1 +# 2 1 +# 3 2 +# This would assign element attributes 1-2 to material 1, element 3 to material 2 +region_mapping_file = "region_mapping.txt" + +# For automatically generated meshes with multiple materials +# This file assigns each element to a grain (crystal orientation) +# Format: one integer per line, each representing a grain ID +# Line 1 = grain ID for element 1, Line 2 = grain ID for element 2, etc. +grain_file = "grains.txt" + +# ===================================== +# MATERIAL DEFINITIONS +# ===================================== +# Define properties for each material in your simulation +# Use [[Materials]] to define multiple materials (note the double brackets) +# Each material must have a unique region_id starting from 0 + +[[Materials]] + # User-friendly name for this material (for output files and identification) + material_name = "aluminum_alloy" + + # Which region of the mesh uses this material (starts at 1, must be sequential: 1, 2, 3...) + # This corresponds to the material numbers in your region_mapping_file + region_id = 1 + + # Initial temperature in Kelvin (affects material properties) + # Room temperature ≈ 298K (25°C or 77°F) + temperature = 298.0 + + # Material model type - tells the code which physics engine to use: + # - "umat" = User Material subroutine (custom material behavior) + # - "exacmech" = ExaCMech crystal plasticity library (for metals) + # - "" = Empty string if defined in Model section below + mech_type = "exacmech" + + # ===== Material Properties ===== + [Materials.Properties] + # Option 1: Read properties from a file + floc = "props.txt" # Path to properties file + num_props = 20 # Number of property values to read + + # Option 2: Define properties directly here (choose either floc OR values) + # values = [210000.0, 0.3, 450.0, ...] # [Young's modulus, Poisson's ratio, yield stress, ...] + + # ===== Internal State Variables ===== + # These track the material's history (plastic strain, damage, etc.) + [Materials.State_Vars] + # Option 1: Read initial values from file + floc = "state.txt" # Path to state variables file + num_vars = 30 # Number of state variables + + # Option 2: Define initial values directly (usually zeros for virgin material) + # values = [0.0, 0.0, 0.0, ...] # Initial values (often all zeros) + + # ===== Crystal/Grain Information (for crystal plasticity only) ===== + [Materials.Grain] + # Where orientations are stored in the state variable array + # Use -1 to append at the end (recommended for UMATs) + # Positive number = specific location in state variable array + # Value ignored for any ExaCMech model ori_state_var_loc = -1 - # Optional - number of values associated with a custom orientation - ori_stride = 0 - # Required - the following options are available for orientation type: euler, quat/quaternion, or custom. - # If one of these options is not provided the program will exit early. - # ExaCMech expects everything to be quaternions - ori_type = "euler" - # Required - number of grains / unique orientations within an orientation file - num_grains = 0 - # Required - orientation file name - ori_floc = "ori.txt" - # If auto generating a mesh a grain file is needed that associates a given - # element to a grain. If you are using a mesh file this information should - # already be embedded in the mesh using something akin to the MFEM v1.0 mesh - # file element attributes, and therefore this option is ignored. - grain_floc = "grain_map.txt" -[BCs] - # Optional - tells the program that we'll have changing BCs - # Note: This option is currently not compatible with auto time stepping - changing_ess_bcs = false + + # If using custom orientation types we need to know the stride length values + # Value ignored for any ExaCMech model as always quaternions + ori_stride = 4 + + # How crystal orientations are represented: + # - "quat" or "quaternion" = 4 values per orientation (recommended for accuracy) + # - "euler" = 3 Euler angles in degrees (Bunge convention: Z-X-Z) + # - "custom" = User-defined format + # Value ignored for any ExaCMech model as always quaternions + ori_type = "quat" + + # Total number of unique crystal orientations in your simulation + # For single crystal: num_grains = 1 + # For polycrystal: num_grains = number of crystals + num_grains = 100 + + # File containing crystal orientations + # Format depends on ori_type: + # - Quaternions: 4 values per line (q0 q1 q2 q3) + # - Euler angles: 3 values per line in degrees (phi1 Phi phi2) + orientation_file = "orientations.txt" + + # Maps elements to grains (which orientation each element uses) + # Only needed for auto-generated meshes + # For mesh files, this info should be in element attributes + grain_file = "grain_map.txt" + + # ===== Material Model Configuration ===== + [Materials.Model] + # Must match the mech_type above (or define it here if not set above) + mech_type = "exacmech" + + # Is this a crystal plasticity model? + # - true = considers crystal orientation effects (required for ExaCMech) + # - false = isotropic material (same properties in all directions) + crystal_plasticity = true + + # ===== ExaCMech-Specific Settings ===== + [Materials.Model.ExaCMech] + # Model name from ExaCMech library + # This combines crystal structure + hardening law + kinetics + # Common options: + # FCC metals (aluminum, copper, nickel): + # - "evptn_FCC_A" = Linear Voce hardening + power law slip kinetics + # - "evptn_FCC_AH" = Nonlinear Voce hardening + power law slip kinetics + # - "evptn_FCC_B" = Kocks-Meckings single source DD model + MTS-like and phonon drag slip kinetics (temperature dependent) + # BCC metals (iron, tungsten): + # - "evptn_BCC_A" = Linear Voce hardening + power law slip kinetics + # - "evptn_BCC_B" = Kocks-Meckings single source DD model + MTS-like and phonon drag slip kinetics (temperature dependent) + # HCP metals (titanium, magnesium): + # - "evptn_HCP_A" = Kocks-Meckings single source DD model + MTS-like and phonon drag slip kinetics (temperature dependent) + shortcut = "evptn_FCC_A" - # The below gives an example of how to make use of changing essential BCs - # where we have the sample cyclically loaded. - # It would go in tandem with using the fixed time stepper with a dt = 0.1 - # and t_final = 7.0. +# ===== Second Material Example (UMAT) ===== +[[Materials]] + material_name = "custom_polymer" + region_id = 2 + temperature = 298.0 + mech_type = "umat" + + [Materials.Properties] + num_props = 2 + # Direct property definition - useful for simple materials + # For polymer: [Young's modulus, Poisson's ratio] + values = [3000.0, 0.35] + + [Materials.State_Vars] + num_vars = 5 + # Initial state (zeros for undeformed material) + values = [0.0, 0.0, 0.0, 0.0, 0.0] + + [Materials.Model] + mech_type = "umat" + # UMATs are typically isotropic + crystal_plasticity = false + + [Materials.Model.UMAT] + # Path to compiled UMAT library (.so or .dll file) + # Can be absolute path or relative to working directory + library_path = "./my_umat.so" + + # Name of the UMAT function in the library + # Must match the function name in your Fortran/C code + function_name = "my_polymer_model" + + # When to load the library: + # - "persistent" = load once at start, keep in memory (fastest) + # - "lazy_load" = load when first needed (saves memory) + # - "load_on_setup" = reload for each setup (for debugging) + load_strategy = "persistent" + + # Allow loading external libraries? + # Set false for security if using pre-installed UMATs only + enable_dynamic_loading = true + + # Directories to search for the UMAT library + # Searched in order if library_path is just a filename + search_paths = ["./", "./umats/", "/shared/umat_libs/"] - # Required if changing_ess_bcs = true - # The step number that a new BC is to be applied on. - # You must always have step 1 listed here, since that is the initial - # step that any BC is applied on. - #update_steps = [1, 11, 31, 51, 71] +# ===================================== +# BOUNDARY CONDITIONS +# ===================================== +# Define how your model is loaded and constrained +# BCs can change over time using the time_info section - # List the attributes associated with each boundary that essential - # BCs will be associated with a given update_steps index. - # It should be noted that while for this example all the attributes are the - # same through out the simulation this is not a constraint within ExaConstit. - # A user is allowed to change them to anything they like. However, users should - # still be careful when updating things. - # For example, we currently don't allow a user to say that they want the normal of a - # given boundary/plane to remain a given direction through-out deformation - # if a velocity is not supplied for that boundary. Therefore, - # Required - essential BC ids for the whole boundary for a given time step - #essential_ids = [[1, 2, 3, 4], - # [1, 2, 3, 4], - # [1, 2, 3, 4], - # [1, 2, 3, 4], - # [1, 2, 3, 4]] - # Required - component combo (x,y,z = -1, x = 1, y = 2, z = 3, xy = 4, yz = 5, xz = 6, free = 0) - # These numbers tell us which degrees of freedom are constrained for the given - # list of attributes provided within essential_ids - #essential_comps = [[3, 1, 2, 3], - # [3, 1, 2, 3], - # [3, 1, 2, 3], - # [3, 1, 2, 3], - # [3, 1, 2, 3]] - # Required - Vector of vals to be applied for each attribute - # The length of this should be #ids * dim of problem - #essential_vals = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001], - # [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.001], - # [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001], - # [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.001], - # [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001]] +[BCs] + # ===== Time-Dependent BC Control ===== + [BCs.time_info] + # How to specify when BCs change: + # Option 1: Based on cycle number (recommended for multi-step loading) + cycle_dependent = true + # Change BCs at cycle 100 and 200 + # A value of 1 is always required + cycles = [1, 100, 200] + + # Option 2: Based on simulation time (alternative method) + # Currently ignored but will be coming in a future release + # time_dependent = true + # times = [0.1, 0.2] # Change BCs at time 0.1 and 0.2 + + # Note: For all of the below BCs if we have changing BCs over several cycles then + # we need to have multiple [[BCs.velocity_bcs]] and [[BCs.velocity_gradient_bcs]] - # The below shows how to set up the essential BCs for just a simple - # non-changing BCs example. In this case, the sample will be deformed under - # monotonic loading. + # ===== Velocity Boundary Conditions ===== + # Apply specific velocities to nodes/surfaces + # ( think constant engineering strain rate BCs ) + [[BCs.velocity_bcs]] + # Which boundary markers/node sets to apply this BC to + # These numbers come from your mesh file's boundary markers + essential_ids = [1, 2, 3] + + # Which velocity components to constrain for each boundary marker + # This uses a binary encoding: + # 0 = no constraints (free) + # 1 = constrain X velocity only + # 2 = constrain Y velocity only + # 3 = constrain Z velocity only + # 4 = constrain X and Y velocities + # 5 = constrain Y and Z velocities + # 6 = constrain X and Z velocities + # 7 = constrain all velocities (X, Y, and Z) + # Example: [3, 1, 2] means: + # - Boundary 1: fix Z velocity only + # - Boundary 2: fix X velocity only + # - Boundary 3: fix Y velocity only + essential_comps = [3, 1, 2] + + # Velocity values for each constrained component + # Format: [vx, vy, vz] for each boundary marker + # Order matches essential_ids, components match essential_comps + # Example below: all velocities are zero (fixed boundaries) + essential_vals = [0.0, 0.0, 0.0, # Boundary 1 velocities + 0.0, 0.0, 0.0, # Boundary 2 velocities + 0.0, 0.0, 0.0] # Boundary 3 velocities + + # ===== Velocity Gradient Boundary Conditions ===== + # Apply a velocity gradient to a boundary + # ( think constant true strain rate BCs ) + [[BCs.velocity_gradient_bcs]] + # Boundary markers for velocity gradient BC + essential_ids = [4] + + # Which components to constrain (same encoding as above) + # Typically use 7 (all components) for velocity gradient + essential_comps = [7] + + # Velocity gradient tensor (3x3 matrix) + # This defines the velocity: v = L·(x - origin) + # Each row is [L11, L12, L13; L21, L22, L23; L31, L32, L33] + # Example: uniaxial tension in Z at strain rate 0.001/s + velocity_gradient = [[0.0, 0.0, 0.0], # dVx/dx, dVx/dy, dVx/dz + [0.0, 0.0, 0.0], # dVy/dx, dVy/dy, dVy/dz + [0.0, 0.0, 0.001]] # dVz/dx, dVz/dy, dVz/dz + + # Reference point for velocity gradient (default: origin) + # Velocities are: v = L·(x - origin) + # Currently this is assummed constant over all time steps + # but in future this could change over time + origin = [0.0, 0.0, 0.0] - # Required - essential BC ids for the whole boundary - essential_ids = [1, 2, 3, 4] - # Required = component combo (free = 0, x = 1, y = 2, z = 3, xy = 4, yz = 5, xz = 6, xyz = 7) - # Note: ExaConstit v0.5.0 and earlier had xyz set to -1. This change was broken in v0.6.0 - # These numbers tell us which degrees of freedom are constrained for the given - # list of attributes provided within essential_ids - # Negative values of the below signify that for a given essential BC id that - # we want to use a constant velocity gradient rather than directly supplying the - # velocity values. - essential_comps = [3, 1, 2, 3] - # Optional - Vector of vals to be applied for each attribute - # The length of this should be #ids * dim of problem - # This is required if constant strain rate is set to false - essential_vals = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001] - # Optional - Velocity gradient to be applied on the essential components and boundary IDs - # The length of this should be a 3x3 matrix as given below. - # As an example, we're supplying a matrix that should equate to the same uni-axial loading - # condition as up above - # This is needed to ensure a constant strain rate on the problem - essential_vel_grad = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.001]] + # ================================================================= + # EXPERIMENTAL: Monotonic Z-Direction Loading Boundary Condition + # ================================================================= + # Enables a specialized boundary condition for uniaxial monotonic loading + # in the z-direction, commonly used for material characterization and + # constitutive model validation. + # + # BEHAVIOR: + # - Applies pure tension or compression in the z-direction + # - Automatically constrains lateral surfaces to prevent shear deformation + # - Maintains kinematic compatibility to avoid artificial stress concentrations + # - Produces clean uniaxial stress states ideal for comparing with + # experimental stress-strain curves + # + # TYPICAL USE CASES: + # - Single crystal tensile/compression testing + # - Polycrystal aggregate response validation + # - Material parameter calibration + # - Crystal plasticity model verification + # + # REQUIREMENTS & LIMITATIONS: + # - Best performance with auto_time_stepping = true + # - May cause convergence issues or crashes with fixed time stepping + # - Recommended for simple geometries (rectangular domains) + # - Not suitable for complex loading paths or multiaxial conditions + # + # WARNING: This is an experimental feature under active development. + # Verify results against known analytical solutions before + # using for production simulations. + # + expt_mono_def_flag = false - # Optional - Point of origin used for the constant velocity gradient calculations - # The point of origin by default is calculated as the minimum x, y, z values - # in a mesh as of the beginning time step. If this is value variable is not - # provided / defined then the default value is used instead. - vgrad_origin = [0.0, 0.0, 0.0] +# ===================================== +# TIME STEPPING CONTROL +# ===================================== +# How the simulation advances through time +# Choose ONE of: Custom, Auto, or Fixed - # Required if changing_ess_bcs = true - # The step number that a new BC is to be applied on. - # You must always have step 1 listed here, since that is the initial - # step that any BC is applied on. - update_steps = [1, 11, 31, 51, 71] -[Model] - # Required - this option tells us to run using a UMAT or exacmech model - # Available options are either umat or exacmech - mech_type = "" - # This tells us that our model is a crystal plasticity problem - # If you are using exacmech in mech_type then this must be true - cp = false - # If ExaCMech models are being used the following options are - # needed - [Model.ExaCMech] - # Required as of v0.8.0 - # - the shortcut model name as defined by ExaCMech here: - # https://github.com/LLNL/ExaCMech/tree/v0.4.1/src/ecmech/cases - # Ge - # For example: - # FCC shortcut names: - # https://github.com/LLNL/ExaCMech/blob/v0.4.1/src/ecmech/cases/ECMech_cases_fcc.cxx#L14-L25 - # BCC shortcut names: - # https://github.com/LLNL/ExaCMech/blob/v0.4.1/src/ecmech/cases/ECMech_cases_bcc.cxx#L13-L15 - # HCP shortcut names: - # https://github.com/LLNL/ExaCMech/blob/v0.4.1/src/ecmech/cases/ECMech_cases_hcp.cxx#L10-L13 - shortcut = "" - # Example of an FCC Voce hardening model with linear hardening is: - # shortcut = "evptn_FCC_A" - # For conversion to the legacy way of doing it: - # "evptn_" is always required at the start of the shortcut name - # FCC, BCC, or HCP would then go next to denote the crystal symmetry - # Then the last set of letters denote the hardening and slip kinitics model - # For example an FCC voce hardening model with slip kinetics is: - # evptn_FCC_A - # where for BCC and FCC materials: - # A would refer to the voce with linear hardening and power law slip kinetics - # AH would refer to the voce with nonlinear hardening and power law slip kinetics - # B would refer to the below MTSDD model - # C and above for BCC materials are new advance models - # For HCP materials: - # A would refer to the MTSDD model as we don't - # have any other models supported for HCP at this time - # - # These options are now all legacy options as of v0.8.0 - # You can still use them but the shortcut key is the preferred method - # All of these are required if you make use of the legacy terms and not the shortcut - # Required - the xtal symmetry type - # FCC, BCC, and HCP are supported crystal types - xtal_type = "" - # Required - the slip kinetics and hardening form that we're going to be using - # The choices are either PowerVoce, PowerVoceNL, or MTSDD - # HCP is only available with MTSDD - slip_type = "" -# Options related to our time steps -# For the time options if all three or some combination of the following tables -# [Auto, Fixed, and Custom] are provided the priority of which one goes -# 1. Custom -# 2. Auto -# 3. Fixed -# -# Note: For fixed and auto time steppings the final simulation step is satified if -# abs(t_final - t_current) < abs(1e-3 * dt_current) -# Generally, the simulation driver will try to satisfy this to even tighter bounds -# but that is not always possible. [Time] - # This provides an automatic time-stepping algorithm for use cases where a fixed time - # step provides to large of an overload and custom dt is too tough. - # It's tunable so that one can change the scaling factor and minimum dt step size. - # This algorithm works is fairly simple and works as follows: - ## If the nonlinear solver fails cut initial time step by Time.Auto.dt_scale - ## and retry solve again (can repeat this process max 2 times) - ## current time step dt and time value updated if a failure occurs - # - ## Successful nonlinear solves and outputs dt value to auto_dt_out.txt - ## and then update dt by doing follows - ## n_newton_iteration_const is a constant value which determines how our - ## dt scale. It's designed in such a way such that as the number of iterations - ## taken by the nonlinear solver approaches Solvers.NR.iter the scaling of dt - ## goes to Time.Auto.dt_scale. If the number of iterations taken by the nonlinear - ## solver is less than this constant then dt will increase in value. - ## To summarize the earlier block of text, - ## dt_scaling > 1 when (n_newton_iteration_taken < n_newton_iteration_const) - ## dt_scaling approaches Time.Auto.dt_scale as n_newton_iteration_taken approaches - # n_newton_iteration_const = Time.Auto.dt_scale * Solvers.NR.iter - # n_newton_iteration_taken = number of nonlinear solver step's taken - # dt_scaling = n_newton_iteration_taken / n_newton_iteration_const - ## Solvers.NR.iter - # dt_new = dt_scaling * dt_old - # if (dt_new < Time.Auto.dt_min) then dt_new = Time.Auto.dt_min + # Which time stepping method to use: + # - "custom" = user-defined time steps from file (most control) + # - "auto" = adaptive time stepping (recommended for efficiency) + # - "fixed" = constant time step (simplest) + # Time stepping priority (highest to lowest): + # 1. Custom (if [Time.Custom] exists) + # 2. Auto (if [Time.Auto] exists) + # 3. Fixed (if [Time.Fixed] exists or as default) + time_type = "custom" + + # ===== Restart Options (Optional) ===== + # Continue from a previous simulation + # Currently ignored but will be coming in a near-future release + # restart = true + # restart_time = 0.5 # Time to restart from + # restart_cycle = 100 # Cycle number to restart from + + # ===== Automatic Time Stepping ===== + # Adjusts time step based on convergence behavior [Time.Auto] - # Initial time step size for the problem - # default value: 1.0 + # Starting time step size + # Smaller = more accurate but slower + # Typical range: 0.001 to 1.0 dt_start = 0.1 - # Minimum time step size that we want allowable for problem - # Note: You don't want to set this too small as this will affect - # how quickly the problem can back out. - # default value: 1.0 - dt_min = 0.05 - # Maximum time step size that we want allowable for problem - # Note: You don't want to set this too small as this will affect - # how quickly the problem can back out. - # default value: F64::max() - dt_max = 1e9 - # dt scaling factor as used in algorithm discussed above - # Note: This scaling factor needs to be between 0 and 1 - # default value: 0.25 + + # Minimum allowed time step + # Simulation fails if it needs smaller than this + # Too small = may never finish, too large = may not converge + dt_min = 0.001 + + # Maximum allowed time step + # Prevents steps from getting too large + # 1e9 = effectively no limit + dt_max = 1.0 + + # Reduction factor when convergence is difficult + # If solver struggles, multiply dt by this factor + # Must be between 0 and 1 (0.25 = reduce to 25%) dt_scale = 0.25 - # Final time step value that we are aiming to reach - # default value: 1.0 + + # Target end time for simulation t_final = 1.0 - # File name for the outputted time step value for each time step - # default value: "auto_dt_out.txt" - auto_dt_file = "auto_dt_out.txt" - # This field is used for when there are constant/fixed dt through-out the simulation + + # ===== Fixed Time Stepping ===== + # Same time step throughout simulation [Time.Fixed] - # Fixed time step we are taking - # default value: 1.0 - dt = 1.0 - # Final time step value that we are aiming to reach - # default value: 1.0 + # Time step size (constant throughout) + dt = 0.01 + + # Simulation end time t_final = 1.0 + + # ===== Custom Time Stepping ===== + # Full control over every time step [Time.Custom] - # Number of time steps for the simulation - nsteps = 1 - # File name that contains the dt to be applied for each time step + # Total number of time steps to take + nsteps = 1000 + + # File containing time step sizes + # Format: one number per line (dt for each step) + # Line 1 = dt for step 1, Line 2 = dt for step 2, etc. floc = "custom_dt.txt" -# Our visualizations options -[Visualizations] - # The stride that we want to use for when to take save off data for visualizations - steps = 1 - # We can save things off either using the MFEM's visit data format, - # the binary conduit data format, or the paraview binary data format. - # If you are running larger simulations it is recommended to use the conduit, paraview, or adios2 - # data formats to reduce the number of files created. If paraview is used make sure - # to have MFEM compiled with zlib so you're base64 binary data file is compressed. - visit = false - conduit = false - paraview = false - adios2 = false - # The folder or filename that we want the above visualization / post-processing - # files to be saved off to - floc = "results/exaconstit" - # Optional - the file name for our average stress file - avg_stress_fname = "avg_stress.txt" - # Optional - additional volume averages or body values are calculated - # these values include the average deformation gradient and if a - # ExaCMech model is being used the plastic work is also calculated - # Default value is set to false - additional_avgs = false - # Optional - the file name for our average deformation gradient file - avg_def_grad_fname = "avg_def_grad.txt" - # Optional - the file name for our plastic work file - avg_pl_work_fname = "avg_pl_work.txt" - # Optional - the file name for our average eulerian strain file - avg_euler_strain_fname = "avg_euler_strain.txt" - # Optional - the file name for our average equivalent plastic strain file - avg_eps_fname = "avg_eps.txt" - # Options to drive light_up type calculations in-situ in the code - light_up = false - # What HKL planes we want to do the light_up measurements on - light_up_hkl = [[1.0, 1.0, 1.0], - [2.0, 0.0, 0.0], - [2.0, 2.0, 0.0], - [3.0, 1.0, 1.0]] - # What tolerance in radians we want our measurements to be within for a given fiber - light_dist_tol = 0.0873 - # What our sample direction is for things - light_s_dir = [0.0, 0.0, 1.0] - # What our lattice spacing parameters are - lattice_params = [3.60, 3.60, 3.60] - lattice_basename = "lattice_avg_" + +# ===================================== +# SOLVER SETTINGS +# ===================================== +# Controls how the equations are solved numerically + [Solvers] - # Option for how our assembly operation is conducted. Possible choices are - # FULL, PA, EA - # Full assembly fully assembles the stiffness matrix - # Partial assembly is completely matrix free and only performs the action of - # the stiffness matrix. - # Element assembly only assembles the elemental contributions to the stiffness - # matrix in order to perform the actions of the overall matrix. + # ===== Matrix Assembly Strategy ===== + # How to build and store the stiffness matrix: + # - "FULL" = build complete matrix (most memory, best for CPU-only problems) + # - "PA" = partial assembly (least memory, best for higher-order p-refinement elements) + # - "EA" = element assembly (less memory, best for large problems run on GPUs) + # GPU requires PA or EA, CPU works with all options assembly = "FULL" - # Option for what our runtime is set to. Possible choices are CPU, OPENMP, or GPU - # Note that GPU replaced CUDA as on v0.7.0 of ExaConstit + + # ===== Execution Model ===== + # Where to run computations: + # - "CPU" = single processor core (simple, good for debugging or CPU only problems) + # These later options require RAJA/MFEM/ExaConstit to have been compiled with support for these + # - "OPENMP" = multiple CPU cores (faster for medium problems) + # - "GPU" = graphics card (fastest for large problems) rtmodel = "CPU" - # Option for determining whether we do full integration for our quadrature scheme - # or we do a BBar scheme where the volume contribution is an element average. - # Possible choices are FULL or BBAR + + # ===== Integration Scheme ===== + # How to handle material incompressibility: + # - "FULL" = standard integration (general purpose) + # - "BBAR" = B-bar method (for nearly incompressible materials) + # Use BBAR is really useful if your material has Poisson's ratio > 0.45 integ_model = "FULL" - # Options for our nonlinear solver - # The number of iterations should probably be low - # Some problems might have difficulty converging so you might need to relax - # the default tolerances - [Solvers.NR] - iter = 25 - # The relative tolerance that can determines when our NR can exit if the - # abs_tol isn't reached first - rel_tol = 1e-5 - # The absolute tolerance that can determines when our NR can exit if the - # rel_tol isn't reached first - abs_tol = 1e-10 - # The below option decides what nonlinear solver to use. - # Possible options are either "NR" (Newton Raphson) or "NRLS" (Newton Raphson - # with a line search) - nl_solver = "NR" - # Options for our iterative linear solver - # A lot of times the iterative solver converges fairly quickly to a solved value - # However, the solvers could at worst take DOFs iterations to converge. In most of these - # solid mechanics problems that almost never occcurs unless the mesh is incredibly coarse. + + # ===== Linear Solver Settings ===== + # Solves Ax=b at each Newton iteration [Solvers.Krylov] - # The number of iterations our Krylov solver may take before exiting - # 200 might be a bit low if you're using either the PA or EA assembly options - # You might want to then increase this to 1000-5000 iterations depending on - # your problem size + # Maximum iterations before giving up + # Increase if solver fails to converge + # EA/PA may need 1000-5000 iterations iter = 200 - # The relative tolerance that can determines when our kryloc can exit if the - # abs_tol isn't reached first - # It's possible to get away with smaller values here such as 1e-7 instead of - # the default value shown down below. + + # Relative tolerance for convergence + # Stop when: ||residual|| < rel_tol * ||initial residual|| + # Smaller = more accurate but slower rel_tol = 1e-10 - # The absolute tolerance that can determines when our kryloc can exit if the - # rel_tol isn't reached first - # It's possible to get away with smaller values here such as 1e-27 instead of - # the default value shown down below. + + # Absolute tolerance for convergence + # Stop when: ||residual|| < abs_tol + # Prevents over-solving when residual is already tiny abs_tol = 1e-30 - # The following Krylov solvers are available GMRES, PCG, and MINRES - # If you're stiffness matrix is known to be symmetric, such as what's the case - # with the current ExaCMech formulations, you should use the PCG solver instead + + # Linear solver algorithm: + # - "CG" = Conjugate Gradient (fastest for well-behaved symmetric problems) + # - "GMRES" = General solver (works for any problem, reliable but uses more memory) + # - "MINRES" = Minimal Residual (for symmetric problems when CG fails) + # - "BiCGSTAB" = Memory-efficient alternative to GMRES (faster but less robust) + # Use CG for typical material models, GMRES if unsure or have convergence issues solver = "GMRES" + + # Preconditioner to accelerate convergence: + # With multi-material systems you might find better convergence + # properties by testing out different preconditioners + # EA/PA will automatically run with JACOBI as the full matrix is + # not constructed + # - "JACOBI" = diagonal scaling (simple/fast, works everywhere but slow convergence) + # - "AMG" = Algebraic MultiGrid (fewer iterations but expensive setup, can fail on some problems) + # - "ILU" = Incomplete factorization (good middle-ground, useful for multi-material systems) + # - "L1GS" = advanced smoother (can help with multi-material systems with contrasting properties) + # - "CHEBYSHEV" = polynomial smoother (good for problems with multiple material scales) + # Try ILU / L1GS / CHEBYSHEV if JACOBI convergence is too slow + preconditioner = "JACOBI" + + # Output verbosity (0 = quiet, 1+ = show iterations) + print_level = 0 + + # ===== Nonlinear Solver Settings ===== + # Solves the overall nonlinear problem + [Solvers.NR] + # Maximum Newton-Raphson iterations per time step + # Increase if "failed to converge" errors occur + iter = 25 + + # Relative tolerance for equilibrium + # Stop when: force imbalance < rel_tol * applied force + rel_tol = 1e-5 + + # Absolute tolerance for equilibrium + # Stop when: force imbalance < abs_tol + abs_tol = 1e-10 + + # Nonlinear solver type: + # - "NR" = standard Newton-Raphson (usually sufficient) + # - "NRLS" = Newton with line search (for difficult convergence) + nl_solver = "NR" + +# ===================================== +# VISUALIZATION OUTPUT +# ===================================== +# Controls what visualization files are created for post-processing +# These files can be opened in ParaView, VisIt, or other tools + +[Visualizations] + # ===== Output Formats ===== + # Enable the formats you need (multiple can be true) + + # VisIt format (.visit files + data) + # Good for: VisIt software, time series data + visit = false + + # ParaView format (.pvtu/.vtu files) + # Good for: ParaView software, widely supported + # Recommended for most users + paraview = true + + # ADIOS2 format (high-performance I/O) + # Good for: very large simulations, supercomputers + adios2 = false + + # ===== Output Control ===== + # How often to write visualization files + # 1 = every time step (lots of files!) + # 10 = every 10th time step + # 100 = every 100th time step + output_frequency = 10 + + # ===== IMPORTANT: Visualization Output Location ===== + # This path is RELATIVE to the main output directory structure! + # Actual location will be: + # [PostProcessing.volume_averages.output_directory]/[basename]/[floc] + # + # Example: With output_directory = "./results", basename = "test", floc = "viz/": + # Actual path: ./results/test/viz/ + # + # Default "visualizations/" means files go to: + # [output_directory]/[basename]/visualizations/ + # + # Note: This is different from volume averages which go directly in: + # [output_directory]/[basename]/ + floc = "visualizations/" + +# ===================================== +# POST-PROCESSING OPTIONS +# ===================================== +# Analysis and data extraction from your simulation + +[PostProcessing] + # ===== Volume Averaging ===== + # Computes average quantities over the entire domain or by material + # Useful for: stress-strain curves, homogenized properties + [PostProcessing.volume_averages] + # Master switch for volume averaging + enabled = true + + # Which quantities to average: + # Each creates a separate output file with time history + + # Stress tensor (6 components: σxx, σyy, σzz, σxy, σyz, σxz) + stress = true + + # Deformation gradient tensor F (9 components) + # F relates current to reference configuration + def_grad = true + + # Euler strain tensor (6 components) + # Sometimes referred to as "true strain" or "logarithmic strain" in the 1D sense + # Note: "logarithmic strain" is a completely different measure when we move to 3D tensors + euler_strain = true + + # Integrated plastic work across entire volume + # Note: this is the only quantity that is the integrated quantity over the + # volume rather than a volume average value + # Useful for finding equivalent yield points across multiple + # loading directions + plastic_work = true + + # Equivalent plastic strain (scalar value) + # Single value representing accumulated plastic deformation + # Common measures: von Mises equivalent strain + # Useful for: failure prediction, hardening evolution + # Only available for ExaCMech models + eq_pl_strain = false + + # Elastic strain tensor (6 components) + # Only available for ExaCMech models + elastic_strain = false + + # How often to compute averages + # Must be multiple of visualization output_frequency + output_frequency = 1 + + # ===== OUTPUT DIRECTORY STRUCTURE ===== + # This setting determines the base directory for ALL simulation outputs + # The actual output structure will be: + # [output_directory]/[basename]/ <- Main simulation folder + # [output_directory]/[basename]/avg_*.txt <- Volume average files + # [output_directory]/[basename]/visualizations/ <- Visualization files + # [output_directory]/[basename]/restart/ <- Restart files (if enabled) + # + # Example: If output_directory = "./results" and basename = "tensile_test": + # ./results/tensile_test/ <- All outputs go here + # ./results/tensile_test/avg_stress_global.txt <- Note: filename NOT affected by basename + # ./results/tensile_test/avg_stress_region_aluminum_alloy_1.txt + # ./results/tensile_test/visualizations/solution_000010.vtu + # + # IMPORTANT: The basename only affects the subdirectory name, NOT the output filenames + # The actual filenames are controlled by the *_fname options below + output_directory = "./results/" + + # ===== MULTI-MATERIAL OUTPUT FILES ===== + # For simulations with multiple materials, EACH quantity generates: + # 1. A global average file: avg_[quantity]_global.txt + # - Contains volume-weighted average over ENTIRE domain + # - Useful for overall material response + # + # 2. Per-material files: avg_[quantity]_region_[material_name]_[region_id].txt + # - Contains average over that material region only + # - Useful for individual material behavior + # + # Example with two materials (aluminum and polymer): + # avg_stress_global.txt <- Combined response + # avg_stress_region_aluminum_alloy_1.txt <- Aluminum only (region 1) + # avg_stress_region_custom_polymer_2.txt <- Polymer only (region 2) + # + # File format (all files): + # Column 1: Time + # Column 2: Volume (total volume averaged over) + # Columns 3+: Component values + # Data is space-delimited, one row per time step + + # ===== Output File Names ===== + # These define the base names for averaged quantity files + # These are optional but the base names are provided down below + # Actual filenames will have _global or _region_[name]_[id] appended + + # Stress components file + # Columns: time, volume, σxx, σyy, σzz, σxy, σyz, σxz + # Creates: avg_stress_global.txt, avg_stress_region_*.txt + avg_stress_fname = "avg_stress.txt" + + # Deformation gradient file + # Columns: time, volume, F11, F12, F13, F21, F22, F23, F31, F32, F33 + # Creates: avg_def_grad_global.txt, avg_def_grad_region_*.txt + avg_def_grad_fname = "avg_def_grad.txt" + + # Euler strain file + # Columns: time, volume, εxx, εyy, εzz, εxy, εyz, εxz + # Creates: avg_euler_strain_global.txt, avg_euler_strain_region_*.txt + avg_euler_strain_fname = "avg_euler_strain.txt" + + # Plastic work file + # Columns: time, volume, plastic_work + # Creates: avg_pl_work_global.txt, avg_pl_work_region_*.txt + avg_pl_work_fname = "avg_pl_work.txt" + + # Equivalent plastic strain file + # Columns: time, volume, equivalent_plastic_strain + # Creates: avg_eq_pl_strain_global.txt, avg_eq_pl_strain_region_*.txt + avg_eq_pl_strain_fname = "avg_eq_pl_strain.txt" + + # Elastic strain file (ExaCMech only) + # Columns: time, volume, εe_xx, εe_yy, εe_zz, εe_xy, εe_yz, εe_xz + # Creates: avg_elastic_strain_global.txt, avg_elastic_strain_region_*.txt + avg_elastic_strain_fname = "avg_elastic_strain.txt" + + # ===== Crystal Orientation Analysis (Light-Up) ===== + # Tracks which crystals are favorably oriented for slip across all crystal systems + # Supports cubic, hexagonal, trigonal, rhombohedral, tetragonal, orthorhombic, monoclinic, triclinic + # Useful for: texture evolution, identifying active grains, powder diffraction simulation + [[PostProcessing.light_up]] + # Enable this analysis + enabled = true + + # Which material to analyze (must match Materials.material_name) + material_name = "aluminum_alloy" + + # Crystal system type - determines symmetry operations and lattice parameter requirements + # Supported values: 'CUBIC', 'HEXAGONAL', 'TRIGONAL', 'RHOMBOHEDRAL', + # 'TETRAGONAL', 'ORTHORHOMBIC', 'MONOCLINIC', 'TRICLINIC' + laue_type = 'CUBIC' + + # Crystal directions to monitor [h,k,l] + # These are Miller indices in crystal coordinates + # Examples: [1,1,1] = octahedral planes, [1,0,0] = cube faces, [1,1,0] = cube edges + # For hexagonal: [1,0,0] = basal, [0,0,1] = c-axis, [1,1,0] = prismatic + hkl_directions = [[1, 1, 1], [1, 0, 0], [1, 1, 0]] + + # Angular tolerance in radians + # Grains within this angle of target direction are considered "in-fiber" + # 0.0873 radians ≈ 5 degrees, 0.1745 radians ≈ 10 degrees + distance_tolerance = 0.0873 + + # Sample direction in lab coordinates [x,y,z] + # Used as reference for orientation analysis and lattice strain calculations + # [0,0,1] = Z direction (typical loading direction) + # [1,0,0] = X direction, [0,1,0] = Y direction + sample_direction = [0.0, 0.0, 1.0] + + # Crystal lattice parameters - requirements vary by crystal system: + # CUBIC: [a] (lattice parameter in Angstroms) + # HEXAGONAL: [a, c] (basal and c-axis parameters in Angstroms) + # TRIGONAL: [a, c] (basal and c-axis parameters in Angstroms) + # RHOMBOHEDRAL: [a, alpha] (lattice parameter in Angstroms, angle in radians) + # TETRAGONAL: [a, c] (basal and c-axis parameters in Angstroms) + # ORTHORHOMBIC: [a, b, c] (three lattice parameters in Angstroms) + # MONOCLINIC: [a, b, c, beta] (three lattice parameters in Angstroms, monoclinic angle in radians) + # TRICLINIC: [a, b, c, alpha, beta, gamma] (three lattice parameters in Angstroms, three angles in radians) + lattice_parameters = [3.6] # Cubic aluminum: a = 3.6 Angstroms + + # Base name for output files + # Creates files like: lattice_avg_directional_stiffness.txt, lattice_avg_dpeff.txt, lattice_avg_strains.txt... + # File naming automatically includes region number as well as quantity related to it + lattice_basename = "lattice_avg_" + + # Example: Hexagonal crystal system (e.g., titanium, zinc) + # [[PostProcessing.light_up]] + # enabled = true + # material_name = "titanium_alloy" + # laue_type = 'HEXAGONAL' + # hkl_directions = [[1, 0, 0], [0, 0, 1], [1, 1, 0]] # basal, c-axis, prismatic + # distance_tolerance = 0.0873 + # sample_direction = [0.0, 0.0, 1.0] + # lattice_parameters = [2.95, 4.68] # a = 2.95 Å, c = 4.68 Å for Ti + # lattice_basename = "ti_lattice_" + + # Example: Rhombohedral crystal system (e.g., some ceramics, bismuth) + # [[PostProcessing.light_up]] + # enabled = true + # material_name = "rhombohedral_ceramic" + # laue_type = 'RHOMBOHEDRAL' + # hkl_directions = [[1, 1, 1], [1, 0, 0], [1, 1, 0]] + # distance_tolerance = 0.0873 + # sample_direction = [0.0, 0.0, 1.0] + # lattice_parameters = [4.75, 1.0472] # a = 4.75 Å, alpha = 60° = 1.0472 radians + # lattice_basename = "rhombo_lattice_" + + # ===== Field Projections ===== + # Projects integration point data to nodes for visualization + [PostProcessing.projections] + # Field projection configuration for post-processing and visualization + # Format: "field_key" -> "Display Name in Visualization Tools" + # All fields listed below are available by default (except "all_state") + # + # ================================================================= + # GEOMETRIC FIELDS + # Basic geometric properties computed from finite element mesh + # ================================================================= + # "centroid" -> "Centroid" # Element centroid coordinates + # "volume" -> "Volume" # Element volume (or area in 2D) + # + # ================================================================= + # STRESS FIELDS + # Fundamental stress measures for solid mechanics analysis + # ================================================================= + # "cauchy" -> "Cauchy Stress" # Cauchy stress tensor (σ) + # "von_mises" -> "Von Mises Stress" # Von Mises equivalent stress (√(3/2 s:s)) + # "hydro" -> "Hydrostatic Stress" # Hydrostatic pressure (tr(σ)/3) + # + # ================================================================= + # GENERAL STATE VARIABLES + # Material state quantities from constitutive models + # ================================================================= + # "all_state" -> "All State Variables" # Complete state variable vector + # # (WARNING: Can be very large for complex models) + # + # ================================================================= + # EXACMECH CRYSTAL PLASTICITY FIELDS + # Specialized quantities for crystalline material modeling + # Available when using ExaCMech constitutive models + # ================================================================= + # "dpeff" -> "Equivalent Plastic Strain Rate" # √(2/3 ε̇ᵖ:ε̇ᵖ) + # "eps" -> "Equivalent Plastic Strain" # ∫√(2/3 ε̇ᵖ:ε̇ᵖ) dt + # "xtal_ori" -> "Crystal Orientations" # Crystal orientation tensors/quaternions + # "elastic_strain" -> "Elastic Strains" # Elastic strain tensor components in sample frame + # "hardness" -> "Hardness" # Crystal hardness parameters + # "shear_rate" -> "Shearing Rate" # Shear rate on slip systems + # + # ================================================================= + # CONFIGURATION + # ================================================================= + # + # Automatic field selection based on constitutive model + # Controls whether to automatically enable all fields compatible with your material model + auto_enable_compatible = true + # true -> Automatically project all fields supported by the active constitutive model + # (Geometric fields + Stress fields + model-specific state variables) + # Ignores 'enabled_projections' list when true + # Recommended for exploratory analysis and model validation + # false -> Use only fields specified in 'enabled_projections' list below + # Recommended for production runs or when targeting specific outputs + # Reduces computational overhead and output file size + # + # Manual field selection (only used when auto_enable_compatible = false) + # Specify which fields to project for visualization and analysis + # Leave empty ([]) to disable all projections + # Example configurations: + # enabled_projections = ["cauchy", "von_mises", "eps"] # Basic stress + plasticity + # enabled_projections = ["volume", "cauchy", "dpeff", "xtal_ori"] # Geometry + crystal fields + # enabled_projections = ["all_state"] # Everything (large output) + enabled_projections = [] + +# ===================================== +# MESH SETTINGS +# ===================================== +# Defines the geometry and discretization of your model + [Mesh] - # Serial uniform refinement level + # Mesh source: + # - "file" = load from mesh file (most common) + # - "auto" = generate simple box mesh (for testing or bringing over voxelized data) + type = "file" + + # ===== File-Based Mesh ===== + # Path to mesh file (MFEM format .mesh or .mesh.gz) + # Can use absolute or relative paths + floc = "../../data/my_model.mesh" + + # ===== Mesh Refinement ===== + # Subdivide elements for higher accuracy + + # Serial refinement (before domain decomposition) + # 0 = no refinement, 1 = split each element into 8 (linear hex) (3D) ref_ser = 0 - # Parallel uniform refinement level + + # Parallel refinement (after domain decomposition) + # Use for better load balancing on many processors ref_par = 0 - # The polynomial order of our shape functions - # Note this used to be prefinement + + # ===== Polynomial Order ===== + # Shape function order (higher = more accurate but expensive) + # 1 = linear elements (8-node hex, 4-node tet) + # 2 = quadratic elements (20-node hex, 10-node tet) + # 3+ = high-order elements (research use) p_refinement = 1 - # The location of our mesh - # If MFEM was compiled with MFEM_USE_ZLIB then this file may also be a - # a gzip file so *.gz file. - floc = "../../data/cube-hex-ro.mesh" - # Possible values here are cubit, auto, or other - # If one of these is not provided the program will exit early - type = "other" - # The below shows the necessary options needed to automatically generate a mesh - # This section is ignored if auto wasn't used for the type + + # ===== Periodic Boundaries ===== + # Connect opposite faces for periodic simulations + # Used for: representative volume elements (RVEs) + # Currently ignored as we don't yet support PBCs yet + # periodicity = false + + # ===== Auto-Generated Mesh ===== + # Creates a simple box mesh (useful for testing) [Mesh.Auto] - # Required - the mesh length in each direction - length = [1.0, 1.0, 1.0] - # Required - the number of cuts along each edge of the mesh are also needed - ncuts = [1, 1, 1] - - + # Physical dimensions [X_length, Y_length, Z_length] + mxyz = [1.0, 1.0, 1.0] + + # Number of elements [X_elements, Y_elements, Z_elements] + # Total elements = product of these numbers + nxyz = [10, 10, 10] \ No newline at end of file diff --git a/src/options/option_boundary_conditions.cpp b/src/options/option_boundary_conditions.cpp new file mode 100644 index 0000000..3502b4f --- /dev/null +++ b/src/options/option_boundary_conditions.cpp @@ -0,0 +1,536 @@ +#include "options/option_parser_v2.hpp" +#include "options/option_util.hpp" + +#include + +BCTimeInfo BCTimeInfo::from_toml(const toml::value& toml_input) { + BCTimeInfo info; + + if (toml_input.contains("time_dependent")) { + info.time_dependent = toml::find(toml_input, "time_dependent"); + } + + if (toml_input.contains("cycle_dependent")) { + info.cycle_dependent = toml::find(toml_input, "cycle_dependent"); + } + + if (toml_input.contains("times")) { + info.times = toml::find>(toml_input, "times"); + } + + if (toml_input.contains("cycles")) { + info.cycles = toml::find>(toml_input, "cycles"); + } + + return info; +} + +VelocityBC VelocityBC::from_toml(const toml::value& toml_input) { + VelocityBC bc; + + if (toml_input.contains("essential_ids")) { + bc.essential_ids = toml::find>(toml_input, "essential_ids"); + } + + if (toml_input.contains("essential_comps")) { + bc.essential_comps = toml::find>(toml_input, "essential_comps"); + } + + if (toml_input.contains("essential_vals")) { + bc.essential_vals = toml::find>(toml_input, "essential_vals"); + } + + return bc; +} + +VelocityGradientBC VelocityGradientBC::from_toml(const toml::value& toml_input) { + VelocityGradientBC bc; + + if (toml_input.contains("velocity_gradient")) { + auto temp = toml::find>>(toml_input, "velocity_gradient"); + for (const auto& items : temp) { + for (const auto& item : items) { + bc.velocity_gradient.push_back(item); + } + } + } + + if (toml_input.contains("essential_ids")) { + bc.essential_ids = toml::find>(toml_input, "essential_ids"); + } + + if (toml_input.contains("essential_comps")) { + bc.essential_comps = toml::find>(toml_input, "essential_comps"); + } + + if (toml_input.contains("origin")) { + auto origin = toml::find>(toml_input, "origin"); + if (origin.size() >= 3) { + bc.origin = std::array{origin[0], origin[1], origin[2]}; + } + } + + return bc; +} + +bool BoundaryOptions::validate() { + // For simplicity, use the legacy format if velocity_bcs is empty + auto is_empty = [](auto&& arg) -> bool { + return std::visit( + [](auto&& arg) -> bool { + return arg.empty(); + }, + arg); + }; + + if (velocity_bcs.empty() && !is_empty(legacy_bcs.essential_ids)) { + transform_legacy_format(); + } + + // Populate BCManager-compatible maps + populate_bc_manager_maps(); + + for (const auto& vel_bc : velocity_bcs) { + // Add this BC's data to the maps + for (size_t i = 0; i < vel_bc.essential_ids.size() && i < vel_bc.essential_comps.size(); + ++i) { + // Add to velocity-specific maps + if (vel_bc.essential_ids[i] <= 0) { + WARNING_0_OPT("WARNING: `BCs.velocity_bcs` has an `essential_ids` that <= 0. We've " + "fixed any negative values"); + } + if (vel_bc.essential_comps[i] < 0) { + WARNING_0_OPT("WARNING: `BCs.velocity_bcs` has an `essential_comps` that < 0. " + "We've fixed any negative values"); + } + } + if (vel_bc.essential_ids.size() != vel_bc.essential_comps.size()) { + WARNING_0_OPT("Error: `BCs.velocity_bcs` has unequal sizes of `essential_ids` and " + "`essential_comps`"); + return false; + } + // Add the values if available + if (vel_bc.essential_vals.size() != (3 * vel_bc.essential_ids.size())) { + WARNING_0_OPT("Error: `BCs.velocity_bcs` needs to have `essential_vals` that have 3 * " + "the size of `essential_ids` or `essential_comps` "); + return false; + } + } + + for (const auto& vgrad_bc : vgrad_bcs) { + // Add this BC's data to the maps + for (size_t i = 0; i < vgrad_bc.essential_ids.size() && i < vgrad_bc.essential_comps.size(); + ++i) { + // Add to velocity-specific maps + if (vgrad_bc.essential_ids[i] <= 0) { + WARNING_0_OPT("WARNING: `BCs.velocity_gradient_bcs` has an `essential_ids` that <= " + "0. We've fixed any negative values"); + } + if (vgrad_bc.essential_comps[i] < 0) { + WARNING_0_OPT("WARNING: `BCs.velocity_gradient_bcs` has an `essential_comps` that " + "< 0. We've fixed any negative values"); + } + } + + if (vgrad_bc.essential_ids.size() != vgrad_bc.essential_comps.size()) { + WARNING_0_OPT("Error: `BCs.velocity_gradient_bcs` has unequal sizes of `essential_ids` " + "and `essential_comps`"); + return false; + } + // Add the values if available + if (vgrad_bc.velocity_gradient.size() != 9) { + WARNING_0_OPT("Error: `BCs.velocity_gradient_bcs` needs to have `velocity_gradient` " + "needs to be a have 3 x 3 matrix"); + return false; + } + } + + if (time_info.cycles[0] != 1) { + WARNING_0_OPT("Error: `BCs.time_info` needs to have the first value be 1"); + return false; + } + + return true; +} + +void BoundaryOptions::transform_legacy_format() { + // Skip if we don't have legacy data + auto is_empty = [](auto&& arg) -> bool { + return std::visit( + [](auto&& arg) -> bool { + return arg.empty(); + }, + arg); + }; + + if (is_empty(legacy_bcs.essential_ids) || is_empty(legacy_bcs.essential_comps)) { + return; + } + + // First, ensure update_steps includes 1 (required for initialization) + if (legacy_bcs.update_steps.empty() || + std::find(legacy_bcs.update_steps.begin(), legacy_bcs.update_steps.end(), 1) == + legacy_bcs.update_steps.end()) { + legacy_bcs.update_steps.insert(legacy_bcs.update_steps.begin(), 1); + } + + // Transfer update_steps to the object field + update_steps = legacy_bcs.update_steps; + + // Handle time-dependent BCs case + if (legacy_bcs.changing_ess_bcs) { + // We need to match nested structures: + // For each update step, we need corresponding essential_ids, essential_comps, etc. + + // Validate that array sizes match number of update steps + const size_t num_steps = legacy_bcs.update_steps.size(); + // We expect nested arrays for time-dependent BCs + if (std::holds_alternative>>(legacy_bcs.essential_ids)) { + auto& nested_ess_ids = std::get>>( + legacy_bcs.essential_ids); + auto& nested_ess_comps = std::get>>( + legacy_bcs.essential_comps); + + if (is_empty(legacy_bcs.essential_vals)) { + std::vector> tmp = {}; + legacy_bcs.essential_vals.emplace<1>(tmp); + } + if (is_empty(legacy_bcs.essential_vel_grad)) { + std::vector>> tmp = {}; + legacy_bcs.essential_vel_grad.emplace<1>(tmp); + } + + auto& nested_ess_vals = std::get>>( + legacy_bcs.essential_vals); + auto& nested_ess_vgrads = std::get>>>( + legacy_bcs.essential_vel_grad); + + // Ensure sizes match + if (nested_ess_ids.size() != num_steps || nested_ess_comps.size() != num_steps) { + throw std::runtime_error("Mismatch in sizes of BC arrays vs. update_steps"); + } + + const auto empty_v1 = std::vector(); + const auto empty_v2 = std::vector>(); + // Process each time step + for (size_t i = 0; i < num_steps; ++i) { + const int step = legacy_bcs.update_steps[i]; + const auto& ess_ids = nested_ess_ids[i]; + const auto& ess_comps = nested_ess_comps[i]; + + const auto& ess_vals = (!is_empty(legacy_bcs.essential_vals)) ? nested_ess_vals[i] + : empty_v1; + const auto& ess_vgrads = (!is_empty(legacy_bcs.essential_vel_grad)) + ? nested_ess_vgrads[i] + : empty_v2; + + // Create BCs for this time step + create_boundary_conditions(step, ess_ids, ess_comps, ess_vals, ess_vgrads); + } + } + } + // Simple case: constant BCs + else { + // For non-changing BCs, we just have one set of values for all time steps + create_boundary_conditions( + 1, + std::get>(legacy_bcs.essential_ids), + std::get>(legacy_bcs.essential_comps), + std::get>(legacy_bcs.essential_vals), + std::get>>(legacy_bcs.essential_vel_grad)); + } +} + +// Helper method to create BC objects from legacy arrays +void BoundaryOptions::create_boundary_conditions( + int step, + const std::vector& ess_ids, + const std::vector& ess_comps, + const std::vector& essential_vals, + const std::vector>& essential_vel_grad) { + // Separate velocity and velocity gradient BCs + std::vector vel_ids, vel_comps, vgrad_ids, vgrad_comps; + + // Configure time dependency + time_info.cycle_dependent = true; + time_info.cycles.push_back(step); + + // Identify which BCs are velocity vs. velocity gradient + for (size_t i = 0; i < ess_ids.size() && i < ess_comps.size(); ++i) { + if (ess_comps[i] >= 0) { + vel_ids.push_back(ess_ids[i]); + vel_comps.push_back(ess_comps[i]); + } else { + vgrad_ids.push_back(ess_ids[i]); + vgrad_comps.push_back(std::abs(ess_comps[i])); + } + } + + // Create velocity BC if needed + if (!vel_ids.empty()) { + VelocityBC vel_bc; + vel_bc.essential_ids = vel_ids; + vel_bc.essential_comps = vel_comps; + + // Find velocity values for this step + if (essential_vals.size() >= vel_ids.size() * 3) { + vel_bc.essential_vals = essential_vals; + } + velocity_bcs.push_back(vel_bc); + } + + // Create velocity gradient BC if needed + if (!vgrad_ids.empty()) { + VelocityGradientBC vgrad_bc; + vgrad_bc.essential_ids = vgrad_ids; + vgrad_bc.essential_comps = vgrad_comps; + + // Find velocity gradient values for this step + if (!essential_vel_grad.empty()) { + // Flatten the 2D array to 1D + for (const auto& row : essential_vel_grad) { + vgrad_bc.velocity_gradient.insert( + vgrad_bc.velocity_gradient.end(), row.begin(), row.end()); + } + } + + // Set origin if needed + if (!legacy_bcs.vgrad_origin.empty() && legacy_bcs.vgrad_origin.size() >= 3) { + vgrad_bc.origin = std::array{ + legacy_bcs.vgrad_origin[0], legacy_bcs.vgrad_origin[1], legacy_bcs.vgrad_origin[2]}; + } + vgrad_bcs.push_back(vgrad_bc); + } +} + +void BoundaryOptions::populate_bc_manager_maps() { + // Initialize the map structures + map_ess_comp["total"] = std::unordered_map>(); + map_ess_comp["ess_vel"] = std::unordered_map>(); + map_ess_comp["ess_vgrad"] = std::unordered_map>(); + + map_ess_id["total"] = std::unordered_map>(); + map_ess_id["ess_vel"] = std::unordered_map>(); + map_ess_id["ess_vgrad"] = std::unordered_map>(); + + // Default entry for step 0 (used for initialization) + map_ess_comp["total"][0] = std::vector(); + map_ess_comp["ess_vel"][0] = std::vector(); + map_ess_comp["ess_vgrad"][0] = std::vector(); + + map_ess_id["total"][0] = std::vector(); + map_ess_id["ess_vel"][0] = std::vector(); + map_ess_id["ess_vgrad"][0] = std::vector(); + + map_ess_vel[0] = std::vector(); + map_ess_vgrad[0] = std::vector(9, 0.0); + + // Determine which step(s) this BC applies to + std::vector steps; + if (time_info.cycle_dependent && !time_info.cycles.empty()) { + update_steps = time_info.cycles; + } else if (update_steps.empty()) { + // Default to step 1 + update_steps = {1}; + } + + for (int step : update_steps) { + // Initialize maps for this step if needed + if (map_ess_comp["total"].find(step) == map_ess_comp["total"].end()) { + map_ess_comp["total"][step] = std::vector(); + map_ess_comp["ess_vel"][step] = std::vector(); + map_ess_comp["ess_vgrad"][step] = std::vector(); + + map_ess_id["total"][step] = std::vector(); + map_ess_id["ess_vel"][step] = std::vector(); + map_ess_id["ess_vgrad"][step] = std::vector(); + + map_ess_vel[step] = std::vector(); + map_ess_vgrad[step] = std::vector(9, 0.0); + } + } + + // Process velocity BCs + size_t index = 0; + for (const auto& vel_bc : velocity_bcs) { + const int step = update_steps[index]; + // Add this BC's data to the maps + for (size_t i = 0; i < vel_bc.essential_ids.size() && i < vel_bc.essential_comps.size(); + ++i) { + // Add to total maps + map_ess_id["total"][step].push_back(std::abs(vel_bc.essential_ids[i])); + map_ess_comp["total"][step].push_back(std::abs(vel_bc.essential_comps[i])); + + // Add to velocity-specific maps + map_ess_id["ess_vel"][step].push_back(std::abs(vel_bc.essential_ids[i])); + map_ess_comp["ess_vel"][step].push_back(std::abs(vel_bc.essential_comps[i])); + } + // Add the values if available + if (!vel_bc.essential_vals.empty()) { + // Add the values to the map + // Note: the original code expected values organized as triplets + // of x, y, z values for each BC + map_ess_vel[step] = vel_bc.essential_vals; + } + index++; + } + + index = 0; + // Process velocity gradient BCs + for (const auto& vgrad_bc : vgrad_bcs) { + const int step = update_steps[index]; + // Add this BC's data to the maps + for (size_t i = 0; i < vgrad_bc.essential_ids.size(); ++i) { + // Add to total maps with negative component to indicate vgrad BC + map_ess_id["total"][step].push_back(std::abs(vgrad_bc.essential_ids[i])); + map_ess_comp["total"][step].push_back(std::abs(vgrad_bc.essential_comps[i])); + + // Add to vgrad-specific maps + map_ess_id["ess_vgrad"][step].push_back(std::abs(vgrad_bc.essential_ids[i])); + map_ess_comp["ess_vgrad"][step].push_back(std::abs(vgrad_bc.essential_comps[i])); + } + // Add the gradient values if available + if (!vgrad_bc.velocity_gradient.empty()) { + map_ess_vgrad[step] = vgrad_bc.velocity_gradient; + } + index++; + } +} + +BoundaryOptions BoundaryOptions::from_toml(const toml::value& toml_input) { + BoundaryOptions options; + + if (toml_input.contains("expt_mono_def_flag")) { + options.legacy_bcs.mono_def_bcs = toml::find(toml_input, "expt_mono_def_flag"); + options.mono_def_bcs = options.legacy_bcs.mono_def_bcs; + } + + // Parse legacy format flags + if (toml_input.contains("changing_ess_bcs")) { + options.legacy_bcs.changing_ess_bcs = toml::find(toml_input, "changing_ess_bcs"); + } + + if (toml_input.contains("update_steps")) { + options.legacy_bcs.update_steps = toml::find>(toml_input, "update_steps"); + } + + if (toml_input.contains("time_info")) { + options.time_info = BCTimeInfo::from_toml(toml::find(toml_input, "time_info")); + } + + // Parse essential IDs based on format + if (toml_input.contains("essential_ids")) { + const auto& ids = toml_input.at("essential_ids"); + if (ids.is_array()) { + // Check if first element is also an array (nested arrays) + if (!ids.as_array().empty() && ids.as_array()[0].is_array()) { + // Nested arrays for time-dependent BCs + options.legacy_bcs.essential_ids = toml::find>>( + toml_input, "essential_ids"); + } else { + // Flat array for constant BCs + options.legacy_bcs.essential_ids = toml::find>(toml_input, + "essential_ids"); + } + } + } + + // Parse essential components based on format + if (toml_input.contains("essential_comps")) { + const auto& comps = toml_input.at("essential_comps"); + if (comps.is_array()) { + // Check if first element is also an array (nested arrays) + if (!comps.as_array().empty() && comps.as_array()[0].is_array()) { + // Nested arrays for time-dependent BCs + options.legacy_bcs.essential_comps = toml::find>>( + toml_input, "essential_comps"); + } else { + // Flat array for constant BCs + options.legacy_bcs.essential_comps = toml::find>( + toml_input, "essential_comps"); + } + } + } + + // Parse essential values based on format + if (toml_input.contains("essential_vals")) { + const auto& vals = toml_input.at("essential_vals"); + if (vals.is_array()) { + // Check if first element is also an array (nested arrays) + if (!vals.as_array().empty() && vals.as_array()[0].is_array()) { + // Nested arrays for time-dependent BCs + options.legacy_bcs.essential_vals = toml::find>>( + toml_input, "essential_vals"); + } else { + // Flat array for constant BCs + options.legacy_bcs.essential_vals = toml::find>( + toml_input, "essential_vals"); + } + } + } + + // Parse velocity gradient based on format + if (toml_input.contains("essential_vel_grad")) { + const auto& vgrad = toml_input.at("essential_vel_grad"); + if (vgrad.is_array()) { + // Check if we have a triple-nested array structure + if (!vgrad.as_array().empty() && vgrad.as_array()[0].is_array() && + !vgrad.as_array()[0].as_array().empty() && + vgrad.as_array()[0].as_array()[0].is_array()) { + // Triple-nested arrays for time-dependent BCs with 2D gradient matrices + options.legacy_bcs.essential_vel_grad = + toml::find>>>(toml_input, + "essential_vel_grad"); + } else { + // Double-nested arrays for constant BCs with 2D gradient matrix + options.legacy_bcs.essential_vel_grad = + toml::find>>(toml_input, "essential_vel_grad"); + } + } + } + + if (toml_input.contains("vgrad_origin")) { + options.legacy_bcs.vgrad_origin = toml::find>(toml_input, + "vgrad_origin"); + } + + // Parse modern structured format + if (toml_input.contains("velocity_bcs")) { + const auto vel_bcs = toml::find(toml_input, "velocity_bcs"); + if (vel_bcs.is_array()) { + for (const auto& bc : vel_bcs.as_array()) { + options.velocity_bcs.push_back(VelocityBC::from_toml(bc)); + } + } else { + options.velocity_bcs.push_back(VelocityBC::from_toml(vel_bcs)); + } + } + + if (toml_input.contains("velocity_gradient_bcs")) { + const auto vgrad_bcs = toml::find(toml_input, "velocity_gradient_bcs"); + if (vgrad_bcs.is_array()) { + for (const auto& bc : vgrad_bcs.as_array()) { + options.vgrad_bcs.push_back(VelocityGradientBC::from_toml(bc)); + } + } else { + options.vgrad_bcs.push_back(VelocityGradientBC::from_toml(vgrad_bcs)); + } + } + + return options; +} + +bool BCTimeInfo::validate() const { + // Implement validation logic + return true; +} + +bool VelocityBC::validate() const { + // Implement validation logic + return !essential_ids.empty() && !essential_comps.empty() && !essential_vals.empty(); +} + +bool VelocityGradientBC::validate() const { + // Implement validation logic + return !velocity_gradient.empty(); +} diff --git a/src/options/option_enum.cpp b/src/options/option_enum.cpp new file mode 100644 index 0000000..6ae4b99 --- /dev/null +++ b/src/options/option_enum.cpp @@ -0,0 +1,155 @@ +#include "options/option_parser_v2.hpp" +#include "options/option_util.hpp" + +/** + * @brief Convert string to MeshType enum + * @param str String representation of mesh type ("file", "auto") + * @return Corresponding MeshType enum value + */ +MeshType string_to_mesh_type(const std::string& str) { + static const std::map mapping = { + {"file", MeshType::FILE}, + {"auto", MeshType::AUTO}, + }; + + return string_to_enum(str, mapping, MeshType::NOTYPE, "mesh"); +} + +/** + * @brief Convert string to TimeStepType enum + * @param str String representation of time step type ("fixed", "auto", "custom") + * @return Corresponding TimeStepType enum value + */ +TimeStepType string_to_time_step_type(const std::string& str) { + static const std::map mapping = {{"fixed", TimeStepType::FIXED}, + {"auto", TimeStepType::AUTO}, + {"custom", TimeStepType::CUSTOM}}; + + return string_to_enum(str, mapping, TimeStepType::NOTYPE, "time step"); +} + +/** + * @brief Convert string to OriType enum + * @param str String representation of orientation type ("quat", "custom", "euler") + * @return Corresponding OriType enum value + */ +OriType string_to_ori_type(const std::string& str) { + static const std::map mapping = { + {"quat", OriType::QUAT}, {"custom", OriType::CUSTOM}, {"euler", OriType::EULER}}; + + return string_to_enum(str, mapping, OriType::NOTYPE, "orientation type"); +} + +/** + * @brief Convert string to MechType enum + * @param str String representation of mechanics type ("umat", "exacmech") + * @return Corresponding MechType enum value + */ +MechType string_to_mech_type(const std::string& str) { + static const std::map mapping = {{"umat", MechType::UMAT}, + {"exacmech", MechType::EXACMECH}}; + + return string_to_enum(str, mapping, MechType::NOTYPE, "material model"); +} + +/** + * @brief Convert string to RTModel enum + * @param str String representation of runtime model ("CPU", "OPENMP", "GPU") + * @return Corresponding RTModel enum value + */ +RTModel string_to_rt_model(const std::string& str) { + static const std::map mapping = { + {"CPU", RTModel::CPU}, {"OPENMP", RTModel::OPENMP}, {"GPU", RTModel::GPU}}; + + return string_to_enum(str, mapping, RTModel::NOTYPE, "runtime model"); +} + +/** + * @brief Convert string to AssemblyType enum + * @param str String representation of assembly type ("FULL", "PA", "EA") + * @return Corresponding AssemblyType enum value + */ +AssemblyType string_to_assembly_type(const std::string& str) { + static const std::map mapping = { + {"FULL", AssemblyType::FULL}, {"PA", AssemblyType::PA}, {"EA", AssemblyType::EA}}; + + return string_to_enum(str, mapping, AssemblyType::NOTYPE, "assembly"); +} + +/** + * @brief Convert string to IntegrationModel enum + * @param str String representation of integration model ("FULL", "BBAR") + * @return Corresponding IntegrationModel enum value + */ +IntegrationModel string_to_integration_model(const std::string& str) { + static const std::map mapping = { + {"FULL", IntegrationModel::DEFAULT}, {"BBAR", IntegrationModel::BBAR}}; + + return string_to_enum(str, mapping, IntegrationModel::NOTYPE, "integration model"); +} + +/** + * @brief Convert string to LinearSolverType enum + * @param str String representation of linear solver type ("CG", "GMRES", "MINRES", "BICGSTAB") + * @return Corresponding LinearSolverType enum value + */ +LinearSolverType string_to_linear_solver_type(const std::string& str) { + static const std::map mapping = { + {"CG", LinearSolverType::CG}, + {"PCG", LinearSolverType::CG}, + {"GMRES", LinearSolverType::GMRES}, + {"MINRES", LinearSolverType::MINRES}, + {"BICGSTAB", LinearSolverType::BICGSTAB}}; + + return string_to_enum(str, mapping, LinearSolverType::NOTYPE, "linear solver"); +} + +/** + * @brief Convert string to NonlinearSolverType enum + * @param str String representation of nonlinear solver type ("NR", "NRLS") + * @return Corresponding NonlinearSolverType enum value + */ +NonlinearSolverType string_to_nonlinear_solver_type(const std::string& str) { + static const std::map mapping = { + {"NR", NonlinearSolverType::NR}, {"NRLS", NonlinearSolverType::NRLS}}; + + return string_to_enum(str, mapping, NonlinearSolverType::NOTYPE, "nonlinear solver"); +} + +/** + * @brief Convert string to PreconditionerType enum + * @param str String representation of preconditioner type ("JACOBI", "AMG", "ILU", "L1GS", + * "CHEBYSHEV") + * @return Corresponding PreconditionerType enum value + */ +PreconditionerType string_to_preconditioner_type(const std::string& str) { + static const std::map mapping = { + {"JACOBI", PreconditionerType::JACOBI}, + {"AMG", PreconditionerType::AMG}, + {"ILU", PreconditionerType::ILU}, + {"L1GS", PreconditionerType::L1GS}, + {"CHEBYSHEV", PreconditionerType::CHEBYSHEV}, + }; + + return string_to_enum(str, mapping, PreconditionerType::NOTYPE, "preconditioner"); +} + +/** + * @brief Convert string to LatticeType enum + * @param str String representation of lattice type ("CUBIC", "HEXAGONAL", "TRIGONAL", + * "RHOMBOHEDRAL", "TETRAGONAL", "ORTHORHOMBIC", "MONOCLINIC", "TRICLINIC") + * @return Corresponding LatticeType enum value + */ +LatticeType string_to_lattice_type(const std::string& str) { + static const std::map mapping = { + {"CUBIC", LatticeType::CUBIC}, + {"HEXAGONAL", LatticeType::HEXAGONAL}, + {"TRIGONAL", LatticeType::TRIGONAL}, + {"RHOMBOHEDRAL", LatticeType::RHOMBOHEDRAL}, + {"TETRAGONAL", LatticeType::TETRAGONAL}, + {"ORTHORHOMBIC", LatticeType::ORTHORHOMBIC}, + {"MONOCLINIC", LatticeType::MONOCLINIC}, + {"TRICLINIC", LatticeType::TRICLINIC}}; + + return string_to_enum(str, mapping, LatticeType::CUBIC, "lattice type"); +} \ No newline at end of file diff --git a/src/options/option_material.cpp b/src/options/option_material.cpp new file mode 100644 index 0000000..8db740b --- /dev/null +++ b/src/options/option_material.cpp @@ -0,0 +1,514 @@ +#include "options/option_parser_v2.hpp" +#include "options/option_util.hpp" + +#include "ECMech_cases.h" +#include "ECMech_const.h" + +#include + +GrainInfo GrainInfo::from_toml(const toml::value& toml_input) { + GrainInfo info; + + if (toml_input.contains("orientation_file")) { + info.orientation_file = toml::find(toml_input, "orientation_file"); + } else if (toml_input.contains("ori_floc")) { + info.orientation_file = toml::find(toml_input, "ori_floc"); + } + + if (toml_input.contains("ori_state_var_loc")) { + info.ori_state_var_loc = toml::find(toml_input, "ori_state_var_loc"); + } + + if (toml_input.contains("ori_stride")) { + info.ori_stride = toml::find(toml_input, "ori_stride"); + } + + if (toml_input.contains("ori_type")) { + info.ori_type = string_to_ori_type(toml::find(toml_input, "ori_type")); + } + + if (toml_input.contains("num_grains")) { + info.num_grains = toml::find(toml_input, "num_grains"); + } + + if (toml_input.contains("grain_file")) { + info.grain_file = toml::find(toml_input, "grain_file"); + } else if (toml_input.contains("grain_floc")) { + info.grain_file = toml::find(toml_input, "grain_floc"); + } + + return info; +} + +MaterialProperties MaterialProperties::from_toml(const toml::value& toml_input) { + MaterialProperties props; + + if (toml_input.contains("floc")) { + props.properties_file = toml::find(toml_input, "floc"); + } + + if (toml_input.contains("num_props")) { + props.num_props = toml::find(toml_input, "num_props"); + } + + if (toml_input.contains("values")) { + props.properties = toml::find>(toml_input, "values"); + } else if (!props.properties_file.empty() && props.num_props > 0) { + // Load properties from file if specified and not already loaded + try { + props.properties = load_vector_from_file(props.properties_file, props.num_props); + } catch (const std::exception& e) { + std::ostringstream err; + err << "Warning: " << e.what(); + WARNING_0_OPT(err.str()); + } + } + return props; +} + +StateVariables StateVariables::from_toml(const toml::value& toml_input) { + StateVariables vars; + + if (toml_input.contains("floc")) { + vars.state_file = toml::find(toml_input, "floc"); + } + + if (toml_input.contains("num_vars") || toml_input.contains("num_state_vars")) { + // Support both "num_vars" and "num_state_vars" for backward compatibility + const auto& key = toml_input.contains("num_vars") ? "num_vars" : "num_state_vars"; + vars.num_vars = toml::find(toml_input, key); + } + + if (toml_input.contains("values")) { + vars.initial_values = toml::find>(toml_input, "values"); + } else if (!vars.state_file.empty() && vars.num_vars > 0) { + // Load state variables from file if specified and not already loaded + try { + vars.initial_values = load_vector_from_file(vars.state_file, vars.num_vars); + } catch (const std::exception& e) { + std::ostringstream err; + err << "Warning: " << e.what(); + WARNING_0_OPT(err.str()); + } + } + return vars; +} + +UmatOptions UmatOptions::from_toml(const toml::value& toml_input) { + UmatOptions options; + + // Existing fields + if (toml_input.contains("library_path") || toml_input.contains("library")) { + options.library_path = toml_input.contains("library_path") + ? toml::find(toml_input, "library_path") + : toml::find(toml_input, "library"); + } + + if (toml_input.contains("function_name")) { + options.function_name = toml::find(toml_input, "function_name"); + } + + if (toml_input.contains("thermal")) { + options.thermal = toml::find(toml_input, "thermal"); + } + + // New dynamic loading fields + if (toml_input.contains("load_strategy")) { + options.load_strategy = toml::find(toml_input, "load_strategy"); + } + + if (toml_input.contains("enable_dynamic_loading")) { + options.enable_dynamic_loading = toml::find(toml_input, "enable_dynamic_loading"); + } + + if (toml_input.contains("search_paths")) { + auto search_paths = toml::find>(toml_input, "search_paths"); + for (auto& search_path : search_paths) { + options.search_paths.push_back(search_path); + } + } + + return options; +} + +bool UmatOptions::is_valid_load_strategy() const { + return (load_strategy == "persistent" || load_strategy == "load_on_setup" || + load_strategy == "lazy_load"); +} + +std::string ExaCMechModelOptions::get_effective_shortcut() const { + if (!shortcut.empty()) { + return shortcut; + } + + // Derive shortcut from legacy fields + if (xtal_type.empty() || slip_type.empty()) { + return ""; + } + std::string derived_shortcut = "evptn_" + xtal_type; + // Map slip_type to the appropriate suffix + if (xtal_type == "FCC" || xtal_type == "BCC") { + if (slip_type == "POWERVOCE") { + derived_shortcut += "_A"; + } else if (slip_type == "POWERVOCENL") { + derived_shortcut += "_AH"; + } else if (slip_type == "MTSDD") { + derived_shortcut += "_B"; + } + } else if (xtal_type == "HCP") { + if (slip_type == "MTSDD") { + derived_shortcut += "_A"; + } + } + + return derived_shortcut; +} + +ExaCMechModelOptions ExaCMechModelOptions::from_toml(const toml::value& toml_input) { + ExaCMechModelOptions options; + + if (toml_input.contains("shortcut")) { + options.shortcut = toml::find(toml_input, "shortcut"); + } + + if (toml_input.contains("xtal_type")) { + options.xtal_type = toml::find(toml_input, "xtal_type"); + std::transform(options.xtal_type.begin(), + options.xtal_type.end(), + options.xtal_type.begin(), + [](unsigned char c) { + return std::toupper(c); + }); + } + + if (toml_input.contains("slip_type")) { + options.slip_type = toml::find(toml_input, "slip_type"); + std::transform(options.slip_type.begin(), + options.slip_type.end(), + options.slip_type.begin(), + [](unsigned char c) { + return std::toupper(c); + }); + } + + if (options.shortcut.empty()) { + options.shortcut = options.get_effective_shortcut(); + } + auto param_index = ecmech::modelParamIndexMap(options.shortcut); + options.gdot_size = param_index["num_slip_system"]; + options.hard_size = param_index["num_hardening"]; + + return options; +} + +MaterialModelOptions MaterialModelOptions::from_toml(const toml::value& toml_input) { + MaterialModelOptions model_options; + + if (toml_input.contains("cp")) { + model_options.crystal_plasticity = toml::find(toml_input, "cp"); + } + + // Parse UMAT-specific options + if (toml_input.contains("UMAT")) { + model_options.umat = UmatOptions::from_toml(toml::find(toml_input, "UMAT")); + } + + // Parse ExaCMech-specific options + if (toml_input.contains("ExaCMech")) { + model_options.exacmech = ExaCMechModelOptions::from_toml( + toml::find(toml_input, "ExaCMech")); + } + + return model_options; +} + +MaterialOptions MaterialOptions::from_toml(const toml::value& toml_input) { + MaterialOptions options; + + if (toml_input.contains("name")) { + options.material_name = toml::find(toml_input, "name"); + } + + if (toml_input.contains("region_id")) { + options.region_id = toml::find(toml_input, "region_id"); + } + + if (toml_input.contains("mech_type")) { + options.mech_type = string_to_mech_type(toml::find(toml_input, "mech_type")); + } + + if (toml_input.contains("temperature")) { + if (toml_input.at("temperature").is_integer()) { + options.temperature = static_cast(toml::find(toml_input, "temperature")); + } else { + options.temperature = toml::find(toml_input, "temperature"); + } + } + + // Parse material properties section + if (toml_input.contains("Properties") || toml_input.contains("Matl_Props")) { + // Support both naming conventions + const auto& props_key = toml_input.contains("Properties") ? "Properties" : "Matl_Props"; + options.properties = MaterialProperties::from_toml(toml::find(toml_input, props_key)); + } + + // Parse state variables section + if (toml_input.contains("State_Vars")) { + options.state_vars = StateVariables::from_toml(toml::find(toml_input, "State_Vars")); + } + + // Parse grain information section + if (toml_input.contains("Grain")) { + options.grain_info = GrainInfo::from_toml(toml::find(toml_input, "Grain")); + } + + // Parse model-specific options + if (toml_input.contains("Model")) { + options.model = MaterialModelOptions::from_toml(toml::find(toml_input, "Model")); + } + + return options; +} + +std::vector MaterialOptions::from_toml_array(const toml::value& toml_input) { + std::vector materials; + + // Check if we have an array of materials + if (toml_input.is_array()) { + const auto& arr = toml_input.as_array(); + for (const auto& item : arr) { + materials.push_back(MaterialOptions::from_toml(item)); + } + } + // If it's a single table, parse it as one material + else if (toml_input.is_table()) { + materials.push_back(MaterialOptions::from_toml(toml_input)); + } + + return materials; +} + +bool GrainInfo::validate() const { + // Implement validation logic + if (!orientation_file) { + WARNING_0_OPT("Error: Grain table was provided without providing an orientation file this " + "is required"); + return false; + } + + if (orientation_file) { + if (!std::filesystem::exists(*orientation_file)) { + std::ostringstream err; + err << "Error: Orientation file does not exist provided value: " << *orientation_file; + WARNING_0_OPT(err.str()); + return false; + } + } + + if (ori_type == OriType::NOTYPE) { + WARNING_0_OPT("Error: Orientation type within the Grain table was not provided a valid " + "value (quats, euler, or custom)"); + return false; + } + + if (ori_type == OriType::QUAT && ori_stride != 4) { + WARNING_0_OPT("Error: Orientation type `QUAT` within the Grain table was not provided a " + "valid stride: 4"); + return false; + } + + if (ori_type == OriType::EULER && ori_stride != 3) { + WARNING_0_OPT("Error: Orientation type `EULER` within the Grain table was not provided a " + "valid stride: 3"); + return false; + } + + if (num_grains < 1) { + WARNING_0_OPT("Error: num_grains was provided a value less than 1"); + return false; + } + + return true; +} + +bool MaterialProperties::validate() const { + if (static_cast(num_props) != properties.size()) { + WARNING_0_OPT("Error: MaterialProperties num_props != properties.size()"); + return false; + } + return true; +} + +bool StateVariables::validate() const { + if (static_cast(num_vars) != initial_values.size()) { + WARNING_0_OPT("Error: StateVariables num_vars != initial_values.size()"); + return false; + } + return true; +} + +bool UmatOptions::validate() const { + if (enable_dynamic_loading && library_path.empty()) { + WARNING_0_OPT("Error: UMAT library_path is required when dynamic loading is enabled"); + return false; + } + + if (!is_valid_load_strategy()) { + std::ostringstream err; + err << "Error: Invalid load_strategy '" << load_strategy + << "'. Must be 'persistent', 'load_on_setup', or 'lazy_load'"; + WARNING_0_OPT(err.str()); + return false; + } + + return true; +} + +bool ExaCMechModelOptions::validate() const { + // Implement validation logic + const auto eff_name = get_effective_shortcut(); + if (!eff_name.empty()) { + try { + ecmech::makeMatModel(eff_name); + } catch (const std::exception& e) { + std::ostringstream err; + err << "Error: ExaCMech model name not recognized and threw the following exception: " + << std::endl + << e.what(); + WARNING_0_OPT(err.str()); + return false; + } + } + return !eff_name.empty(); +} + +bool MaterialModelOptions::validate() const { + if (!umat and !exacmech) { + WARNING_0_OPT( + "Error: Model table has not provided either an ExaCMech or UMAT table within it."); + return false; + } + + if (umat) { + if (!umat->validate()) + return false; + } + + if (exacmech) { + if (!crystal_plasticity) { + WARNING_0_OPT("Error: Model table is using an ExaCMech table but has not set variable " + "crystal_plasticity as true."); + return false; + } + if (!exacmech->validate()) + return false; + } + + return true; +} + +bool MaterialOptions::validate() const { + std::string mat_name = material_name + "_" + std::to_string(region_id); + + if (mech_type == MechType::NOTYPE) { + std::ostringstream err; + err << "Error: Material table for material_name_region# " << mat_name + << " the mech_type was not set a valid option"; + WARNING_0_OPT(err.str()); + return false; + } + + if (temperature <= 0) { + std::ostringstream err; + err << "Error: Material table for material_name_region# " << mat_name + << " the temperature was provided a negative value"; + WARNING_0_OPT(err.str()); + return false; + } + + if (!properties.validate()) { + std::ostringstream err; + err << "Error: Material table for material_name_region# " << mat_name + << " the Properties table had errors"; + WARNING_0_OPT(err.str()); + return false; + } + if (!state_vars.validate()) { + std::ostringstream err; + err << "Error: Material table for material_name_region# " << mat_name + << " the State_Vars table had errors"; + WARNING_0_OPT(err.str()); + return false; + } + if (!model.validate()) { + std::ostringstream err; + err << "Error: Material table for material_name_region# " << mat_name + << " the Model table had errors"; + WARNING_0_OPT(err.str()); + return false; + } + + if (grain_info) { + if (!grain_info->validate()) { + std::ostringstream err; + err << "Error: Material table for material_name_region# " << mat_name + << " the Grain table had errors"; + WARNING_0_OPT(err.str()); + return false; + } + } + + if (model.crystal_plasticity) { + if (!grain_info) { + std::ostringstream err; + err << "Error: Material table for material_name_region# " << mat_name + << " the material model was set to use crystal plasticity model but the Grain " + "table was not set"; + WARNING_0_OPT(err.str()); + return false; + } + } + + if (model.exacmech) { + const int num_properties = properties.num_props; + const int num_state = state_vars.num_vars; + + auto index_map = ecmech::modelParamIndexMap(model.exacmech->shortcut); + if (index_map["num_params"] == 0) { + std::ostringstream err; + err << "Error: Material model requires do not match you provided: " << num_properties + << " and the model requires: " << index_map["num_params"] + << " model shortcut: " << model.exacmech->shortcut + << " material name: " << material_name << std::endl; + WARNING_0_OPT(err.str()); + return false; + } + if (index_map["num_params"] != static_cast(num_properties)) { + std::ostringstream err; + err << "Error: Number of parameters and what the model requires do not match you " + "provided: " + << num_properties << " and the model requires: " << index_map["num_params"] + << " model shortcut: " << model.exacmech->shortcut + << " material name: " << material_name << std::endl; + WARNING_0_OPT(err.str()); + return false; + } + + const size_t num_hist = index_map["num_hist"] - 4 + ecmech::ne + 1; + if ((index_map["num_hist"] - 4 + ecmech::ne + 1) != static_cast(num_state)) { + std::ostringstream err; + err << "Error: Number of state variables and what the model requires do not match you " + "provided: " + << num_state << " and the model requires: " << num_hist + << " model shortcut: " << model.exacmech->shortcut + << " material name: " << material_name << std::endl + << "Note: the number of state variables does not account for the quaternions but " + "does include the number of energy and relative volume" + << std::endl; + WARNING_0_OPT(err.str()); + return false; + } + } + + return true; +} \ No newline at end of file diff --git a/src/options/option_mesh.cpp b/src/options/option_mesh.cpp new file mode 100644 index 0000000..684f138 --- /dev/null +++ b/src/options/option_mesh.cpp @@ -0,0 +1,119 @@ +#include "options/option_parser_v2.hpp" +#include "options/option_util.hpp" + +#include + +namespace fs = std::filesystem; + +MeshOptions MeshOptions::from_toml(const toml::value& toml_input) { + MeshOptions options; + + if (toml_input.contains("type")) { + options.mesh_type = string_to_mesh_type(toml::find(toml_input, "type")); + } + + if (options.mesh_type == MeshType::FILE) { + if (toml_input.contains("floc")) { + options.mesh_file = toml::find(toml_input, "floc"); + } + } + + if (toml_input.contains("refine_serial") || toml_input.contains("ref_ser")) { + const auto& key = toml_input.contains("refine_serial") ? "refine_serial" : "ref_ser"; + options.ref_ser = toml::find(toml_input, key); + } + + if (toml_input.contains("refine_parallel") || toml_input.contains("ref_par")) { + const auto& key = toml_input.contains("refine_parallel") ? "refine_parallel" : "ref_par"; + options.ref_par = toml::find(toml_input, key); + } + + if (toml_input.contains("order") || toml_input.contains("p_refinement")) { + // Support both "order" and "p_refinement" for backward compatibility + const auto& key = toml_input.contains("order") ? "order" : "p_refinement"; + options.order = toml::find(toml_input, key); + } + + if (toml_input.contains("periodicity")) { + options.periodicity = toml::find(toml_input, "periodicity"); + } + + // Handle Auto mesh section + if (options.mesh_type == MeshType::AUTO) { + auto auto_section = toml::find(toml_input, "Auto"); + if (auto_section.contains("length") || auto_section.contains("mxyz")) { + const auto& key = auto_section.contains("length") ? "length" : "mxyz"; + auto length_array = toml::find>(auto_section, key); + if (length_array.size() >= 3) { + std::copy_n(length_array.begin(), 3, options.mxyz.begin()); + } + } + + if (auto_section.contains("ncuts") || auto_section.contains("nxyz")) { + const auto& key = auto_section.contains("ncuts") ? "ncuts" : "nxyz"; + auto ncuts_array = toml::find>(auto_section, key); + if (ncuts_array.size() >= 3) { + std::copy_n(ncuts_array.begin(), 3, options.nxyz.begin()); + } + } + } + + return options; +} + +bool MeshOptions::validate() const { + if (mesh_type == MeshType::NOTYPE) { + WARNING_0_OPT("Error: Mesh table was not provided an appropriate mesh type"); + return false; + } + + // For auto mesh generation, check that nxyz and mxyz are valid + if (mesh_type == MeshType::AUTO) { + for (size_t i = 0; i < 3; ++i) { + if (nxyz[i] <= 0) { + std::ostringstream err; + err << "Error: Invalid mesh discretization: nxyz[" << i << "] = " << nxyz[i] + << std::endl; + WARNING_0_OPT(err.str()); + return false; + } + if (mxyz[i] <= 0.0) { + std::ostringstream err; + err << "Error: Invalid mesh dimensions: mxyz[" << i << "] = " << mxyz[i] + << std::endl; + WARNING_0_OPT(err.str()); + return false; + } + } + } + + // Check that mesh file exists for CUBIT or OTHER mesh types + if ((mesh_type == MeshType::FILE) && !mesh_file.empty()) { + if (!fs::exists(mesh_file)) { + std::ostringstream err; + err << "Error: Mesh file '" << mesh_file << "' does not exist." << std::endl; + WARNING_0_OPT(err.str()); + return false; + } + } + + if (ref_ser < 0) { + WARNING_0_OPT( + "Error: Mesh table has `ref_ser` / `refine_serial` set to value less than 0."); + return false; + } + + if (ref_par < 0) { + WARNING_0_OPT( + "Error: Mesh table has `ref_par` / `refine_parallel` set to value less than 0."); + return false; + } + + if (order < 1) { + WARNING_0_OPT("Error: Mesh table has `p_refinement` / `order` set to value less than 1."); + return false; + } + + // Implement validation logic + return true; +} \ No newline at end of file diff --git a/src/options/option_parser_v2.cpp b/src/options/option_parser_v2.cpp new file mode 100644 index 0000000..efac46e --- /dev/null +++ b/src/options/option_parser_v2.cpp @@ -0,0 +1,1257 @@ +#include "options/option_parser_v2.hpp" + +#include "options/option_util.hpp" + +#include "ECMech_cases.h" +#include "mfem.hpp" + +#include +#include +#include +#include +#include +#include + +#include "TOML_Reader/toml.hpp" + +namespace fs = std::filesystem; + +// Implementation of the struct conversion methods +void ExaOptions::parse_options(const std::string& filename, int my_id) { + try { + // Parse the main TOML file + { + fs::path fpath(filename); + basename = fpath.stem().string(); + } + toml::value toml_input = toml::parse(filename); + + // Parse the full configuration + parse_from_toml(toml_input); + + // Validate the complete configuration + if (!validate()) { + WARNING_0_OPT("Error: Configuration validation failed."); + if (my_id == 0) { + mfem::mfem_error("MFEM_ABORT: Configuration validation failed for option file"); + } else { + mfem::mfem_error(""); + } + } + } catch (const std::exception& e) { + std::ostringstream oss; + oss << "Error parsing options: " << e.what(); + std::string err = oss.str(); + WARNING_0_OPT(err); + if (my_id == 0) { + mfem::mfem_error("MFEM_ABORT: Configuration validation failed for option file"); + } else { + mfem::mfem_error(""); + } + } +} + +void ExaOptions::parse_from_toml(const toml::value& toml_input) { + // Parse basic metadata + if (toml_input.contains("Version")) { + version = toml::find(toml_input, "Version"); + } + + if (toml_input.contains("basename")) { + basename = toml::find(toml_input, "basename"); + } + + // Check for modular configuration + if (toml_input.contains("materials")) { + auto mat_files = toml::find>(toml_input, "materials"); + for (auto& mat_file : mat_files) { + material_files.push_back(mat_file); + } + } + + if (toml_input.contains("post_processing")) { + post_processing_file = toml::find(toml_input, "post_processing"); + } + + if (toml_input.contains("grain_file")) { + grain_file = toml::find(toml_input, "grain_file"); + } + if (toml_input.contains("orientation_file")) { + orientation_file = toml::find(toml_input, "orientation_file"); + } + + // New fields for optional region mapping + if (toml_input.contains("region_mapping_file")) { + region_mapping_file = toml::find(toml_input, "region_mapping_file"); + } + + // Parse component sections + parse_mesh_options(toml_input); + parse_time_options(toml_input); + parse_solver_options(toml_input); + parse_boundary_options(toml_input); + parse_visualization_options(toml_input); + + // Parse materials from main file if no external files are specified + if (material_files.empty()) { + parse_material_options(toml_input); + } else { + load_material_files(); + } + + // Parse post-processing from main file if no external file is specified + if (!post_processing_file) { + parse_post_processing_options(toml_input); + } else { + load_post_processing_file(); + } +} + +void ExaOptions::parse_mesh_options(const toml::value& toml_input) { + if (toml_input.contains("Mesh")) { + mesh = MeshOptions::from_toml(toml::find(toml_input, "Mesh")); + } +} + +void ExaOptions::parse_time_options(const toml::value& toml_input) { + if (!toml_input.contains("Time")) { + return; + } + + const auto time_section = toml::find(toml_input, "Time"); + + // Parse restart options + if (time_section.contains("restart")) { + time.restart = toml::find(time_section, "restart"); + } + + if (time_section.contains("restart_time")) { + time.restart_time = toml::find(time_section, "restart_time"); + } + + if (time_section.contains("restart_cycle")) { + time.restart_cycle = toml::find(time_section, "restart_cycle"); + } + + // Parse nested time stepping sections + if (time_section.contains("Auto")) { + time.auto_time = TimeOptions::AutoTimeOptions::from_toml(toml::find(time_section, "Auto")); + } + + if (time_section.contains("Fixed")) { + time.fixed_time = TimeOptions::FixedTimeOptions::from_toml( + toml::find(time_section, "Fixed")); + } + + if (time_section.contains("Custom")) { + time.custom_time = TimeOptions::CustomTimeOptions::from_toml( + toml::find(time_section, "Custom")); + } + + // Determine which time stepping mode to use + time.determine_time_type(); +} + +void ExaOptions::parse_solver_options(const toml::value& toml_input) { + if (toml_input.contains("Solvers")) { + solvers = SolverOptions::from_toml(toml::find(toml_input, "Solvers")); + } +} + +void ExaOptions::parse_material_options(const toml::value& toml_input) { + // Check for materials array under "Materials" section + if (toml_input.contains("Materials")) { + auto materials_section = toml::find(toml_input, "Materials"); + materials = MaterialOptions::from_toml_array(materials_section); + } + // Legacy format - material properties directly in Properties section + else if (toml_input.contains("Properties")) { + MaterialOptions single_material; + + // Parse properties section + if (toml_input.at("Properties").contains("Properties")) { + single_material.properties = MaterialProperties::from_toml( + toml::find(toml_input.at("Properties"), "Properties")); + } + // Parse material variables if present + else if (toml_input.at("Properties").contains("Matl_Props")) { + single_material.properties = MaterialProperties::from_toml( + toml::find(toml_input.at("Properties"), "Matl_Props")); + } else { + single_material.properties = MaterialProperties::from_toml( + toml::find(toml_input, "Properties")); + } + + // Parse global temperature if present + if (toml_input.at("Properties").contains("temperature")) { + const auto props = toml_input.at("Properties"); + if (props.at("temperature").is_integer()) { + single_material.temperature = static_cast( + toml::find(props, "temperature")); + } else { + single_material.temperature = toml::find(props, "temperature"); + } + } + + // Parse state variables if present + if (toml_input.at("Properties").contains("State_Vars")) { + single_material.state_vars = StateVariables::from_toml( + toml::find(toml_input.at("Properties"), "State_Vars")); + } + + // Parse grain info if present + if (toml_input.at("Properties").contains("Grain")) { + single_material.grain_info = GrainInfo::from_toml( + toml::find(toml_input.at("Properties"), "Grain")); + } + + // Try to determine model type and options + if (toml_input.contains("Model")) { + parse_model_options(toml_input, single_material); + } + + // Add the single material + materials.push_back(single_material); + } + + int max_grains = -1; + int index = 0; + for (auto& mat : materials) { + // Grain info (if crystal plasticity) + if (mat.grain_info.has_value()) { + const auto& grain = mat.grain_info.value(); + if (grain.orientation_file.has_value()) { + if (!orientation_file.has_value()) { + orientation_file = grain.orientation_file.value(); + } + if (grain.orientation_file.value().compare(orientation_file.value()) != 0) { + MFEM_ABORT("Check material grain tables as orientation files in there are not " + "consistent between values listed elsewhere"); + } + } + + if (grain.grain_file.has_value()) { + if (!grain_file.has_value()) { + grain_file = grain.grain_file.value(); + } + if (grain.grain_file.value().compare(grain_file.value()) != 0) { + MFEM_ABORT("Check material grain tables as grain files in there are not " + "consistent between values listed elsewhere"); + } + } + + if (max_grains < grain.num_grains && index > 0) { + MFEM_ABORT("Check material grain tables as values in there are not consistent " + "between multiple materials"); + } + + max_grains = grain.num_grains; + index++; + } + } +} + +void ExaOptions::parse_model_options(const toml::value& toml_input, MaterialOptions& material) { + if (!toml_input.contains("Model")) { + return; + } + + const auto model_section = toml::find(toml_input, "Model"); + + // Parse common model properties + if (model_section.contains("mech_type")) { + std::string mech_type_str = toml::find(model_section, "mech_type"); + material.mech_type = string_to_mech_type(mech_type_str); + } + + if (model_section.contains("cp")) { + material.model.crystal_plasticity = toml::find(model_section, "cp"); + } + + // Parse ExaCMech-specific options + if (material.mech_type == MechType::EXACMECH && model_section.contains("ExaCMech")) { + material.model.exacmech = ExaCMechModelOptions::from_toml( + toml::find(model_section, "ExaCMech")); + + // Validate that we have a valid shortcut (either directly or derived) + std::string effective_shortcut = material.model.exacmech->get_effective_shortcut(); + + if (effective_shortcut.empty()) { + WARNING_0_OPT("Error: Invalid ExaCMech model configuration. Either shortcut or both " + "xtal_type and slip_type must be provided."); + } + // When using legacy parameters, set the derived shortcut for other code to use + if (material.model.exacmech->shortcut.empty() && !effective_shortcut.empty()) { + material.model.exacmech->shortcut = effective_shortcut; + } + + auto index_map = ecmech::modelParamIndexMap(material.model.exacmech->shortcut); + + // add more checks later like + material.model.exacmech->gdot_size = index_map["num_slip_system"]; + material.model.exacmech->hard_size = index_map["num_hardening"]; + } + + // Check for legacy format where mech_type was in Model section + if (material.mech_type == MechType::UMAT) { + // If mech_type is "umat" and there's no UMAT subsection, create default UMAT options + if (!model_section.contains("UMAT")) { + // Create UMAT options with defaults for legacy format + material.model.umat = UmatOptions{}; + // The defaults in UmatOptions should handle the rest + } + } + + // Parse UMAT-specific options + else if (material.mech_type == MechType::UMAT && model_section.contains("UMAT")) { + material.model.umat = UmatOptions::from_toml(toml::find(model_section, "UMAT")); + } +} + +void ExaOptions::parse_boundary_options(const toml::value& toml_input) { + if (toml_input.contains("BCs")) { + boundary_conditions = BoundaryOptions::from_toml(toml::find(toml_input, "BCs")); + + // Transform and validate + const bool bc_check = boundary_conditions.validate(); + if (!bc_check) { + throw std::runtime_error("BC validation failed"); + } + } +} + +void ExaOptions::parse_visualization_options(const toml::value& toml_input) { + if (toml_input.contains("Visualizations")) { + visualization = VisualizationOptions::from_toml(toml::find(toml_input, "Visualizations")); + } +} + +void ExaOptions::parse_post_processing_options(const toml::value& toml_input) { + post_processing = PostProcessingOptions::from_toml(toml_input); +} + +void ExaOptions::load_material_files() { + materials.clear(); + + for (const auto& file_path : material_files) { + try { + toml::value mat_toml = toml::parse(file_path); + + // Parse the material + auto material = MaterialOptions::from_toml(mat_toml); + + // Parse model options separately to handle the shortcut derivation + if (mat_toml.contains("Model")) { + parse_model_options(mat_toml, material); + } + + // Add the material to our list + materials.push_back(material); + + } catch (const std::exception& e) { + std::ostringstream err; + err << "Error parsing material file " << file_path << ": " << e.what(); + WARNING_0_OPT(err.str()); + throw; // Re-throw to propagate the error + } + } +} + +void ExaOptions::load_post_processing_file() { + if (post_processing_file.has_value()) { + try { + toml::value pp_toml = toml::parse(post_processing_file.value()); + post_processing = PostProcessingOptions::from_toml(pp_toml); + } catch (const std::exception& e) { + std::ostringstream err; + err << "Error parsing post-processing file " << post_processing_file.value() << ": " + << e.what(); + WARNING_0_OPT(err.str()); + throw; // Re-throw to propagate the error + } + } +} + +bool ExaOptions::validate() { + // Basic validation - could be expanded with more comprehensive checks + + if (!mesh.validate()) + return false; + if (!time.validate()) + return false; + if (!solvers.validate()) + return false; + if (!visualization.validate()) + return false; + if (!boundary_conditions.validate()) + return false; + + // Check that we have at least one material + if (materials.empty()) { + WARNING_0_OPT("Error: No materials defined in configuration."); + return false; + } + + if (materials.size() > 1) { + if (!region_mapping_file) { + WARNING_0_OPT("Error: region_mapping_file was not provided even though multiple " + "materials were asked for."); + return false; + } else if (mesh.mesh_type == MeshType::AUTO && !grain_file) { + WARNING_0_OPT("Error: region_mapping_file was provided but no grain_file was provided " + "when using auto mesh."); + return false; + } + } + + if (materials.size() > 1) { + if (!region_mapping_file) { + WARNING_0_OPT("Error: region_mapping_file was not provided even though multiple " + "materials were asked for."); + return false; + } else if (mesh.mesh_type == MeshType::AUTO && !grain_file) { + WARNING_0_OPT("Error: region_mapping_file was provided but no grain_file was provided " + "when using auto mesh."); + return false; + } + } + + size_t index = 1; + for (auto& mat : materials) { + if (!mat.validate()) + return false; + // Update the region_id value after validating + // everything so to make it easier for users to + // validation errors + mat.region_id = static_cast(index++); + } + + // Handle legacy "default_material" mapping + // If we have light_up configs with "default_material" and only one material, map them + bool has_default_material_configs = false; + for (const auto& light_config : post_processing.light_up_configs) { + if (light_config.material_name == "default_material") { + has_default_material_configs = true; + break; + } + } + + if (has_default_material_configs) { + if (materials.size() == 1) { + // Single material case: map default_material to the actual material name + std::string actual_material_name = materials[0].material_name; + for (auto& light_config : post_processing.light_up_configs) { + if (light_config.material_name == "default_material") { + light_config.material_name = actual_material_name; + std::ostringstream info; + info << "Info: Mapped default_material to '" << actual_material_name << "'"; + INFO_0_OPT(info.str()); + } + } + } else { + // Multiple materials: error - can't auto-resolve default_material + WARNING_0_OPT("Error: Found default_material in light_up config but multiple materials " + "defined. "); + WARNING_0_OPT("Please specify explicit material_name for each light_up configuration."); + return false; + } + } + + // Resolve light_up material names to region IDs + for (auto& light_config : post_processing.light_up_configs) { + if (light_config.enabled) { + if (!light_config.resolve_region_id(materials)) { + return false; + } + } + } + + // Build material-to-regions map for efficiency + std::unordered_multimap material_to_regions; + for (const auto& region : materials) { + material_to_regions.emplace(region.material_name, region.region_id); + } + + // Track which light-up configurations already exist to avoid duplicates + std::set> existing_configs; + + // First pass: collect existing configurations + for (const auto& lightup : post_processing.light_up_configs) { + existing_configs.insert(std::make_pair(lightup.material_name, lightup.region_id.value())); + } + + // Create a temporary vector to hold new LightUpOptions + std::vector additional_lightup_options; + + // Second pass: process each light-up option + for (auto& lightup : post_processing.light_up_configs) { + // Mark original options as user-generated + lightup.is_auto_generated = false; + + // Find all regions with this material + auto range = material_to_regions.equal_range(lightup.material_name); + std::vector regions_with_material; + + for (auto it = range.first; it != range.second; ++it) { + regions_with_material.push_back(it->second); + } + + if (regions_with_material.empty()) { + std::ostringstream info; + info << "Error: PostProcessing.light_up material '" << lightup.material_name + << "' not found in any region"; + WARNING_0_OPT(info.str()); + return false; + } + + // Sort for consistent ordering + std::sort(regions_with_material.begin(), regions_with_material.end()); + + // Update the original lightup with the first region ID + lightup.region_id = regions_with_material[0]; + + // Create duplicates for remaining regions if they don't already exist + for (size_t i = 1; i < regions_with_material.size(); ++i) { + int target_region_id = regions_with_material[i]; + + // Check if this configuration already exists + auto config_key = std::make_pair(lightup.material_name, target_region_id); + if (existing_configs.find(config_key) == existing_configs.end()) { + // Create duplicate + LightUpOptions duplicate = lightup; + duplicate.region_id = target_region_id; + duplicate.is_auto_generated = true; // Mark as auto-generated + + additional_lightup_options.push_back(duplicate); + existing_configs.insert(config_key); // Track that we've added this + } + } + } + + // Append the duplicated options to the main vector + if (!additional_lightup_options.empty()) { + post_processing.light_up_configs.insert(post_processing.light_up_configs.end(), + additional_lightup_options.begin(), + additional_lightup_options.end()); + } + + // Validate post-processing after region resolution + if (!post_processing.validate()) + return false; + + if (region_mapping_file) { + if (!std::filesystem::exists(*region_mapping_file)) { + std::ostringstream err; + err << "Error: Region mapping file: " << *region_mapping_file << " does not exist"; + WARNING_0_OPT(err.str()); + return false; + } + } + + if (grain_file) { + if (!std::filesystem::exists(*grain_file)) { + std::ostringstream err; + err << "Error: Grain file provided at top level: " << *grain_file << " does not exist"; + WARNING_0_OPT(err.str()); + return false; + } + } + + if (orientation_file) { + if (!std::filesystem::exists(*orientation_file)) { + std::ostringstream err; + err << "Error: Orientation file provided at top level: " << *orientation_file + << " does not exist"; + WARNING_0_OPT(err.str()); + return false; + } + } + + return true; +} + +// Implementation of validation methods for component structs + +void ExaOptions::print_options() const { + int myid; + MPI_Comm_rank(MPI_COMM_WORLD, &myid); + + if (myid != 0) + return; // Only print from rank 0 + + std::cout << "\n==================================================\n"; + std::cout << "ExaConstit Options Summary\n"; + std::cout << "==================================================\n"; + + // Basic info + std::cout << "\nSimulation Information:\n"; + std::cout << " Base name: " << basename << "\n"; + std::cout << " Version: " << version << "\n"; + + // Print each component + print_mesh_options(); + print_time_options(); + print_solver_options(); + print_material_options(); + print_boundary_options(); + print_visualization_options(); + print_post_processing_options(); + + // Configuration files + if (!material_files.empty() || post_processing_file.has_value() || + orientation_file.has_value() || grain_file.has_value() || region_mapping_file.has_value()) { + std::cout << "\nConfiguration Files:\n"; + + if (!material_files.empty()) { + std::cout << " Material files:\n"; + for (const auto& file : material_files) { + std::cout << " - " << file << "\n"; + } + } + + if (post_processing_file.has_value()) { + std::cout << " Post-processing file: " << post_processing_file.value() << "\n"; + } + + if (orientation_file.has_value()) { + std::cout << " Orientation file: " << orientation_file.value() << "\n"; + } + + if (grain_file.has_value()) { + std::cout << " Grain file: " << grain_file.value() << "\n"; + } + + if (region_mapping_file.has_value()) { + std::cout << " Region mapping file: " << region_mapping_file.value() << "\n"; + } + } + + std::cout << "\n==================================================\n\n"; +} + +void ExaOptions::print_mesh_options() const { + std::cout << "\nMesh Options:\n"; + + if (mesh.mesh_type == MeshType::FILE) { + std::cout << " Type: File-based mesh\n"; + std::cout << " Mesh file: " << mesh.mesh_file << "\n"; + } else if (mesh.mesh_type == MeshType::AUTO) { + std::cout << " Type: Auto-generated mesh\n"; + std::cout << " Dimensions (nx, ny, nz): " << mesh.nxyz[0] << " x " << mesh.nxyz[1] << " x " + << mesh.nxyz[2] << "\n"; + std::cout << " Physical size (x, y, z): " << mesh.mxyz[0] << " x " << mesh.mxyz[1] << " x " + << mesh.mxyz[2] << "\n"; + } + + std::cout << " Polynomial order: " << mesh.order << "\n"; + std::cout << " Serial refinement levels: " << mesh.ref_ser << "\n"; + std::cout << " Parallel refinement levels: " << mesh.ref_par << "\n"; + std::cout << " Periodicity: " << (mesh.periodicity ? "Enabled" : "Disabled") << "\n"; +} + +void ExaOptions::print_time_options() const { + std::cout << "\nTime Stepping Options:\n"; + + if (time.time_type == TimeStepType::FIXED) { + std::cout << " Type: Fixed time stepping\n"; + std::cout << " Time step (dt): " << time.fixed_time->dt << "\n"; + std::cout << " Final time: " << time.fixed_time->t_final << "\n"; + } else if (time.time_type == TimeStepType::AUTO) { + std::cout << " Type: Automatic time stepping\n"; + std::cout << " Initial dt: " << time.auto_time->dt_start << "\n"; + std::cout << " Minimum dt: " << time.auto_time->dt_min << "\n"; + std::cout << " Maximum dt: " << time.auto_time->dt_max << "\n"; + std::cout << " Scaling factor: " << time.auto_time->dt_scale << "\n"; + std::cout << " Final time: " << time.auto_time->t_final << "\n"; + } else if (time.time_type == TimeStepType::CUSTOM) { + std::cout << " Type: Custom time stepping\n"; + std::cout << " Number of steps: " << time.custom_time->nsteps << "\n"; + std::cout << " Custom dt file: " << time.custom_time->floc << "\n"; + if (!time.custom_time->dt_values.empty()) { + std::cout << " Total simulation time: " + << std::accumulate(time.custom_time->dt_values.begin(), + time.custom_time->dt_values.end(), + 0.0) + << "\n"; + } + } + + if (time.restart) { + std::cout << " Restart enabled:\n"; + std::cout << " Restart time: " << time.restart_time << "\n"; + std::cout << " Restart cycle: " << time.restart_cycle << "\n"; + } +} + +void ExaOptions::print_solver_options() const { + std::cout << "\nSolver Options:\n"; + + // Assembly and runtime + std::cout << " Assembly type: "; + switch (solvers.assembly) { + case AssemblyType::FULL: + std::cout << "Full assembly\n"; + break; + case AssemblyType::PA: + std::cout << "Partial assembly\n"; + break; + case AssemblyType::EA: + std::cout << "Element assembly\n"; + break; + default: + std::cout << "Unknown\n"; + break; + } + + std::cout << " Runtime model: "; + switch (solvers.rtmodel) { + case RTModel::CPU: + std::cout << "CPU\n"; + break; + case RTModel::OPENMP: + std::cout << "OpenMP\n"; + break; + case RTModel::GPU: + std::cout << "GPU\n"; + break; + default: + std::cout << "Unknown\n"; + break; + } + + std::cout << " Integration model: "; + switch (solvers.integ_model) { + case IntegrationModel::DEFAULT: + std::cout << "Default\n"; + break; + case IntegrationModel::BBAR: + std::cout << "B-bar\n"; + break; + default: + std::cout << "Unknown\n"; + break; + } + + // Linear solver + std::cout << "\n Linear solver:\n"; + std::cout << " Type: "; + switch (solvers.linear_solver.solver_type) { + case LinearSolverType::CG: + std::cout << "Conjugate Gradient\n"; + break; + case LinearSolverType::GMRES: + std::cout << "GMRES\n"; + break; + case LinearSolverType::MINRES: + std::cout << "MINRES\n"; + break; + case LinearSolverType::BICGSTAB: + std::cout << "BiCGSTAB\n"; + break; + default: + std::cout << "Unknown\n"; + break; + } + + std::cout << " Preconditioner: "; + switch (solvers.linear_solver.preconditioner) { + case PreconditionerType::JACOBI: + std::cout << "Jacobi\n"; + break; + case PreconditionerType::AMG: + std::cout << "AMG\n"; + break; + case PreconditionerType::ILU: + std::cout << "ILU\n"; + break; + case PreconditionerType::L1GS: + std::cout << "L1GS\n"; + break; + case PreconditionerType::CHEBYSHEV: + std::cout << "CHEBYSHEV\n"; + break; + default: + std::cout << "Unknown\n"; + break; + } + + std::cout << " Absolute tolerance: " << solvers.linear_solver.abs_tol << "\n"; + std::cout << " Relative tolerance: " << solvers.linear_solver.rel_tol << "\n"; + std::cout << " Maximum iterations: " << solvers.linear_solver.max_iter << "\n"; + std::cout << " Print level: " << solvers.linear_solver.print_level << "\n"; + + // Nonlinear solver + std::cout << "\n Nonlinear solver:\n"; + std::cout << " Type: "; + switch (solvers.nonlinear_solver.nl_solver) { + case NonlinearSolverType::NR: + std::cout << "Newton-Raphson\n"; + break; + case NonlinearSolverType::NRLS: + std::cout << "Newton-Raphson with line search\n"; + break; + default: + std::cout << "Unknown\n"; + break; + } + + std::cout << " Maximum iterations: " << solvers.nonlinear_solver.iter << "\n"; + std::cout << " Relative tolerance: " << solvers.nonlinear_solver.rel_tol << "\n"; + std::cout << " Absolute tolerance: " << solvers.nonlinear_solver.abs_tol << "\n"; +} + +void ExaOptions::print_material_options() const { + std::cout << "\nMaterial Options:\n"; + std::cout << " Number of materials: " << materials.size() << "\n"; + + for (size_t i = 0; i < materials.size(); ++i) { + const auto& mat = materials[i]; + std::cout << "\n Material " << i + 1 << ":\n"; + std::cout << " Name: " << mat.material_name << "\n"; + std::cout << " Region ID: " << mat.region_id << "\n"; + std::cout << " Temperature: " << mat.temperature << " K\n"; + + std::cout << " Mechanics type: "; + switch (mat.mech_type) { + case MechType::UMAT: + std::cout << "UMAT\n"; + break; + case MechType::EXACMECH: + std::cout << "ExaCMech\n"; + break; + default: + std::cout << "Unknown\n"; + break; + } + + std::cout << " Crystal plasticity: " + << (mat.model.crystal_plasticity ? "Enabled" : "Disabled") << "\n"; + + // Material properties + std::cout << " Properties file: " << mat.properties.properties_file << "\n"; + std::cout << " Number of properties: " << mat.properties.num_props << "\n"; + + // State variables + std::cout << " State variables file: " << mat.state_vars.state_file << "\n"; + std::cout << " Number of state variables: " << mat.state_vars.num_vars << "\n"; + + // Grain info (if crystal plasticity) + if (mat.grain_info.has_value()) { + const auto& grain = mat.grain_info.value(); + std::cout << " Grain information:\n"; + if (grain.orientation_file.has_value()) { + std::cout << " Orientation file: " << grain.orientation_file.value() << "\n"; + } + if (grain.grain_file.has_value()) { + std::cout << " Grain file: " << grain.grain_file.value() << "\n"; + } + std::cout << " Number of grains: " << grain.num_grains << "\n"; + std::cout << " Orientation type: "; + switch (grain.ori_type) { + case OriType::EULER: + std::cout << "Euler angles\n"; + break; + case OriType::QUAT: + std::cout << "Quaternions\n"; + break; + case OriType::CUSTOM: + std::cout << "Custom\n"; + break; + default: + std::cout << "Unknown\n"; + break; + } + std::cout << " Orientation state var location: " << grain.ori_state_var_loc + << "\n"; + std::cout << " Orientation stride: " << grain.ori_stride << "\n"; + } + + // Model-specific options + + if (mat.model.umat.has_value() && mat.mech_type == MechType::UMAT) { + const auto& umat = mat.model.umat.value(); + std::cout << " UMAT options:\n"; + std::cout << " Library: " << umat.library_path << "\n"; + std::cout << " Function: " << umat.function_name << "\n"; + std::cout << " Thermal: " << (umat.thermal ? "Enabled" : "Disabled") << "\n"; + std::cout << " Dynamic loading: " + << (umat.enable_dynamic_loading ? "Enabled" : "Disabled") << "\n"; + std::cout << " Load strategy: " << umat.load_strategy << "\n"; + if (!umat.search_paths.empty()) { + std::cout << " Search paths: "; + for (size_t j = 0; j < umat.search_paths.size(); ++j) { + std::cout << umat.search_paths[j]; + if (j < umat.search_paths.size() - 1) + std::cout << ", "; + } + std::cout << "\n"; + } + } + + if (mat.model.exacmech.has_value() && mat.mech_type == MechType::EXACMECH) { + const auto& ecmech = mat.model.exacmech.value(); + std::cout << " ExaCMech options:\n"; + std::cout << " Model: " << ecmech.get_effective_shortcut() << "\n"; + if (!ecmech.shortcut.empty()) { + std::cout << " Shortcut: " << ecmech.shortcut << "\n"; + } else { + std::cout << " Crystal type: " << ecmech.xtal_type << "\n"; + std::cout << " Slip type: " << ecmech.slip_type << "\n"; + } + } + } +} + +void ExaOptions::print_boundary_options() const { + std::cout << "\nBoundary Conditions:\n"; + + // Modern velocity BCs + if (!boundary_conditions.velocity_bcs.empty()) { + std::cout << " Velocity boundary conditions: " << boundary_conditions.velocity_bcs.size() + << "\n"; + for (size_t i = 0; i < boundary_conditions.velocity_bcs.size(); ++i) { + const auto& bc = boundary_conditions.velocity_bcs[i]; + std::cout << " BC " << i + 1 << ":\n"; + + // Print essential IDs + std::cout << " Essential IDs: "; + for (const auto& id : bc.essential_ids) { + std::cout << id << " "; + } + std::cout << "\n"; + + // Print essential components + std::cout << " Essential components: "; + for (const auto& comp : bc.essential_comps) { + std::cout << comp << " "; + } + std::cout << "\n"; + + // Print essential values - these are the actual velocity values + std::cout << " Essential values: "; + for (const auto& val : bc.essential_vals) { + std::cout << val << " "; + } + std::cout << "\n"; + } + } + + // Velocity gradient BCs + if (!boundary_conditions.vgrad_bcs.empty()) { + std::cout << " Velocity gradient boundary conditions: " + << boundary_conditions.vgrad_bcs.size() << "\n"; + for (size_t i = 0; i < boundary_conditions.vgrad_bcs.size(); ++i) { + const auto& bc = boundary_conditions.vgrad_bcs[i]; + std::cout << " VGrad BC " << i + 1 << ":\n"; + + // Print essential IDs + std::cout << " Essential IDs: "; + for (const auto& id : bc.essential_ids) { + std::cout << id << " "; + } + std::cout << "\n"; + + // Print the velocity gradient tensor (3x3 matrix stored as 9 values) + std::cout << " Velocity gradient tensor:\n"; + if (bc.velocity_gradient.size() >= 9) { + // Print as a 3x3 matrix for clarity + std::cout << " | " << std::setw(12) << bc.velocity_gradient[0] << " " + << std::setw(12) << bc.velocity_gradient[1] << " " << std::setw(12) + << bc.velocity_gradient[2] << " |\n"; + std::cout << " | " << std::setw(12) << bc.velocity_gradient[3] << " " + << std::setw(12) << bc.velocity_gradient[4] << " " << std::setw(12) + << bc.velocity_gradient[5] << " |\n"; + std::cout << " | " << std::setw(12) << bc.velocity_gradient[6] << " " + << std::setw(12) << bc.velocity_gradient[7] << " " << std::setw(12) + << bc.velocity_gradient[8] << " |\n"; + } else { + // Fallback if not exactly 9 values + std::cout << " Values: "; + for (const auto& val : bc.velocity_gradient) { + std::cout << val << " "; + } + std::cout << "\n"; + } + + // Print origin if specified + if (bc.origin.has_value()) { + std::cout << " Origin: (" << bc.origin->at(0) << ", " << bc.origin->at(1) + << ", " << bc.origin->at(2) << ")\n"; + } + + // Print time info if this BC is time-dependent + if (bc.time_info.time_dependent || bc.time_info.cycle_dependent) { + std::cout << " Time-dependent: " + << (bc.time_info.time_dependent ? "Yes" : "No") << "\n"; + std::cout << " Cycle-dependent: " + << (bc.time_info.cycle_dependent ? "Yes" : "No") << "\n"; + } + } + } + + // Time-dependent info (general) + if (boundary_conditions.time_info.time_dependent || + boundary_conditions.time_info.cycle_dependent) { + std::cout << "\n General time-dependent BC settings:\n"; + std::cout << " Time-dependent: " + << (boundary_conditions.time_info.time_dependent ? "Yes" : "No") << "\n"; + std::cout << " Cycle-dependent: " + << (boundary_conditions.time_info.cycle_dependent ? "Yes" : "No") << "\n"; + if (!boundary_conditions.update_steps.empty()) { + std::cout << " Update steps: "; + for (const auto& step : boundary_conditions.update_steps) { + std::cout << step << " "; + } + std::cout << "\n"; + } + } + + if (boundary_conditions.mono_def_bcs) { + std::cout + << "\n Experimental Feature: monotonic loading BCs in the Z-direction being applied\n"; + std::cout << " all other defined BC constraints will be ignored\n"; + } + + // Print the internal BCManager maps if they're populated + // These show how the BCs are organized by time step + if (!boundary_conditions.map_ess_vel.empty() || !boundary_conditions.map_ess_vgrad.empty() || + !boundary_conditions.map_ess_comp.empty() || !boundary_conditions.map_ess_id.empty()) { + std::cout << "\n BCManager internal mappings:\n"; + + // Print essential velocity map + if (!boundary_conditions.map_ess_vel.empty()) { + std::cout << " Essential velocities by step:\n"; + for (const auto& [step, values] : boundary_conditions.map_ess_vel) { + std::cout << " Step " << step << ": "; + // Print first few values to avoid overwhelming output + size_t count = 0; + for (const auto& val : values) { + if (count++ < 6) { // Show first 6 values + std::cout << val << " "; + } + } + if (values.size() > 6) { + std::cout << "... (" << values.size() << " total values)"; + } + std::cout << "\n"; + } + } + + // Print essential velocity gradient map + if (!boundary_conditions.map_ess_vgrad.empty()) { + std::cout << " Essential velocity gradients by step:\n"; + for (const auto& [step, values] : boundary_conditions.map_ess_vgrad) { + std::cout << " Step " << step << ": "; + if (values.size() >= 9) { + std::cout << "(3x3 tensor with " << values.size() / 9 << " tensors)\n"; + } else { + std::cout << values.size() << " values\n"; + } + } + } + + // Print essential components map + if (!boundary_conditions.map_ess_comp.empty()) { + std::cout << " Essential components mapping:\n"; + for (const auto& [type, step_map] : boundary_conditions.map_ess_comp) { + std::cout << " Type '" << type << "':\n"; + for (const auto& [step, comp_ids] : step_map) { + std::cout << " Step " << step << ": "; + size_t count = 0; + for (const auto& id : comp_ids) { + if (count++ < 10) { // Show first 10 component IDs + std::cout << id << " "; + } + } + if (comp_ids.size() > 10) { + std::cout << "... (" << comp_ids.size() << " total)"; + } + std::cout << "\n"; + } + } + } + + // Print essential IDs map + if (!boundary_conditions.map_ess_id.empty()) { + std::cout << " Essential IDs mapping:\n"; + for (const auto& [type, step_map] : boundary_conditions.map_ess_id) { + std::cout << " Type '" << type << "':\n"; + for (const auto& [step, ids] : step_map) { + std::cout << " Step " << step << ": "; + for (const auto& id : ids) { + std::cout << id << " "; + } + std::cout << "\n"; + } + } + } + } + + // Legacy format information if present + if (boundary_conditions.legacy_bcs.changing_ess_bcs) { + std::cout << "\n Legacy BC format detected:\n"; + std::cout << " Changing essential BCs: Yes\n"; + std::cout << " Update steps: "; + for (const auto& step : boundary_conditions.legacy_bcs.update_steps) { + std::cout << step << " "; + } + std::cout << "\n"; + } +} + +void ExaOptions::print_visualization_options() const { + std::cout << "\nVisualization Options:\n"; + std::cout << " VisIt: " << (visualization.visit ? "Enabled" : "Disabled") << "\n"; + std::cout << " ParaView: " << (visualization.paraview ? "Enabled" : "Disabled") << "\n"; + std::cout << " Conduit: " << (visualization.conduit ? "Enabled" : "Disabled") << "\n"; + std::cout << " ADIOS2: " << (visualization.adios2 ? "Enabled" : "Disabled") << "\n"; + std::cout << " Output frequency: " << visualization.output_frequency << "\n"; + std::cout << " Output location: " << visualization.floc << "\n"; +} + +void ExaOptions::print_post_processing_options() const { + std::cout << "\nPost-Processing Options:\n"; + + // Volume averages + const auto& vol_avg = post_processing.volume_averages; + std::cout << " Volume averages: " << (vol_avg.enabled ? "Enabled" : "Disabled") << "\n"; + if (vol_avg.enabled) { + std::cout << " Output directory: " << vol_avg.output_directory << "\n"; + std::cout << " Output frequency: " << vol_avg.output_frequency << "\n"; + std::cout << " Stress: " << (vol_avg.stress ? "Yes" : "No"); + if (vol_avg.stress) + std::cout << " (" << vol_avg.avg_stress_fname << ")"; + std::cout << "\n"; + + std::cout << " Deformation gradient: " << (vol_avg.def_grad ? "Yes" : "No"); + if (vol_avg.def_grad) + std::cout << " (" << vol_avg.avg_def_grad_fname << ")"; + std::cout << "\n"; + + std::cout << " Euler strain: " << (vol_avg.euler_strain ? "Yes" : "No"); + if (vol_avg.euler_strain) + std::cout << " (" << vol_avg.avg_euler_strain_fname << ")"; + std::cout << "\n"; + + std::cout << " Plastic work: " << (vol_avg.plastic_work ? "Yes" : "No"); + if (vol_avg.plastic_work) + std::cout << " (" << vol_avg.avg_pl_work_fname << ")"; + std::cout << "\n"; + + std::cout << " Equivalent plastic strain: " << (vol_avg.eq_pl_strain ? "Yes" : "No"); + if (vol_avg.eq_pl_strain) + std::cout << " (" << vol_avg.avg_eq_pl_strain_fname << ")"; + std::cout << "\n"; + + std::cout << " Elastic strain: " << (vol_avg.elastic_strain ? "Yes" : "No"); + if (vol_avg.elastic_strain) + std::cout << " (" << vol_avg.avg_elastic_strain_fname << ")"; + std::cout << "\n"; + + std::cout << " Additional averages: " << (vol_avg.additional_avgs ? "Yes" : "No") + << "\n"; + } + + // Projections + const auto& proj = post_processing.projections; + std::cout << " Projections:\n"; + std::cout << " Auto-enable compatible: " << (proj.auto_enable_compatible ? "Yes" : "No") + << "\n"; + if (!proj.enabled_projections.empty()) { + std::cout << " Enabled projections:\n"; + for (const auto& p : proj.enabled_projections) { + std::cout << " - " << p << "\n"; + } + } + + // Light-up options + const auto& light_configs = post_processing.light_up_configs; + if (light_configs.empty()) { + std::cout << " Light-up analysis: Disabled\n"; + } else { + std::cout << "\n=== LightUp Configuration Summary ===\n"; + + // Group by material for cleaner output + std::map> by_material; + for (const auto& lightup : light_configs) { + by_material[lightup.material_name].push_back(&lightup); + } + + for (const auto& [material, options] : by_material) { + std::cout << " Material: " << material << "\n"; + for (const auto* opt : options) { + auto& light = *opt; + std::cout << " Region "; + if (light.region_id.has_value()) { + std::cout << light.region_id.value(); + } else { + std::cout << "(unassigned)"; + } + std::cout << " (" << (light.is_auto_generated ? "auto" : "user") << ")\n"; + std::cout << "\n"; + std::cout << " Enabled: " << (light.enabled ? "Yes" : "No") << "\n"; + if (light.enabled) { + std::cout << " Laue Group: "; + switch (light.lattice_type) { + case LatticeType::CUBIC: { + std::cout << "cubic\n"; + break; + } + case LatticeType::HEXAGONAL: { + std::cout << "hexagonal\n"; + break; + } + case LatticeType::TRIGONAL: { + std::cout << "trigonal\n"; + break; + } + case LatticeType::RHOMBOHEDRAL: { + std::cout << "rhombohedral\n"; + break; + } + case LatticeType::TETRAGONAL: { + std::cout << "tetragonal\n"; + break; + } + case LatticeType::ORTHORHOMBIC: { + std::cout << "orthorhombic\n"; + break; + } + case LatticeType::MONOCLINIC: { + std::cout << "monoclinic\n"; + break; + } + case LatticeType::TRICLINIC: { + std::cout << "triclinic\n"; + break; + } + default: { + std::cout << "unknown\n"; + } + } + + std::cout << " Lattice parameters: ( "; + for (const auto& lp : light.lattice_parameters) { + std::cout << lp << " "; + } + std::cout << ")\n"; + + std::cout << " Distance tolerance: " << light.distance_tolerance << "\n"; + std::cout << " Sample direction: (" << light.sample_direction[0] << ", " + << light.sample_direction[1] << ", " << light.sample_direction[2] + << ")\n"; + std::cout << " Output basename: " << light.lattice_basename << "\n"; + + if (!light.hkl_directions.empty()) { + std::cout << " HKL directions:\n"; + for (const auto& hkl : light.hkl_directions) { + std::cout << " - [" << hkl[0] << ", " << hkl[1] << ", " << hkl[2] + << "]\n"; + } + } + } + } + } + std::cout << "=====================================\n\n"; + } +} \ No newline at end of file diff --git a/src/options/option_parser_v2.hpp b/src/options/option_parser_v2.hpp new file mode 100644 index 0000000..d38fac7 --- /dev/null +++ b/src/options/option_parser_v2.hpp @@ -0,0 +1,1497 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TOML_Reader/toml.hpp" + +// Enumeration types +/** + * @brief Enumeration for different mesh generation types + */ +enum class MeshType { + AUTO, /**< Automatically generated structured mesh */ + FILE, /**< Mesh loaded from external file */ + NOTYPE /**< Uninitialized or invalid mesh type */ +}; + +/** + * @brief Enumeration for time stepping strategies + */ +enum class TimeStepType { + FIXED, /**< Fixed time step size */ + AUTO, /**< Adaptive time stepping */ + CUSTOM, /**< Custom time steps from file */ + NOTYPE /**< Uninitialized or invalid time step type */ +}; + +/** + * @brief Enumeration for crystal orientation representation types + */ +enum class OriType { + EULER, /**< Euler angles representation */ + QUAT, /**< Quaternion representation */ + CUSTOM, /**< Custom orientation format */ + NOTYPE /**< Uninitialized or invalid orientation type */ +}; + +/** + * @brief Enumeration for material mechanics model types + */ +enum class MechType { + UMAT, /**< User-defined material subroutine */ + EXACMECH, /**< ExaCMech crystal plasticity model */ + NOTYPE /**< Uninitialized or invalid mechanics type */ +}; + +/** + * @brief Enumeration for runtime execution models + */ +enum class RTModel { + CPU, /**< CPU-only execution */ + OPENMP, /**< OpenMP parallel execution */ + GPU, /**< GPU accelerated execution */ + NOTYPE /**< Uninitialized or invalid runtime model */ +}; + +/** + * @brief Enumeration for finite element assembly strategies + */ +enum class AssemblyType { + FULL, /**< Full matrix assembly */ + PA, /**< Partial assembly */ + EA, /**< Element assembly */ + NOTYPE /**< Uninitialized or invalid assembly type */ +}; + +/** + * @brief Enumeration for integration models + */ +enum class IntegrationModel { + DEFAULT, /**< Standard integration */ + BBAR, /**< B-bar method for incompressible materials */ + NOTYPE /**< Uninitialized or invalid integration model */ +}; + +/** + * @brief Enumeration for linear solver types + */ +enum class LinearSolverType { + CG, /**< Conjugate Gradient solver */ + GMRES, /**< Generalized Minimal Residual solver */ + MINRES, /**< Minimal Residual solver */ + BICGSTAB, /**< BiCGSTAB Solver */ + NOTYPE /**< Uninitialized or invalid linear solver type */ +}; + +/** + * @brief Enumeration for nonlinear solver types + */ +enum class NonlinearSolverType { + NR, /**< Newton-Raphson method */ + NRLS, /**< Newton-Raphson with line search */ + NOTYPE /**< Uninitialized or invalid nonlinear solver type */ +}; + +/** + * @brief Enumeration for preconditioner types + */ +enum class PreconditionerType { + JACOBI, /**< Jacobi preconditioner */ + AMG, /**< Algebraic multigrid preconditioner (Full assembly only) */ + ILU, /**< Incomplete LU factorization preconditioner (Full assembly only) */ + L1GS, /**< l1-scaled block Gauss-Seidel/SSOR preconditioner (Full assembly only) */ + CHEBYSHEV, /**< Chebyshev preconditioner (Full assembly only) */ + NOTYPE /**< Uninitialized or invalid preconditioner type */ +}; + +enum class LatticeType { + CUBIC, + HEXAGONAL, + TRIGONAL, + RHOMBOHEDRAL, + TETRAGONAL, + ORTHORHOMBIC, + MONOCLINIC, + TRICLINIC +}; + +/** + * @brief Type alias for a nested unordered map structure used for boundary condition mapping + * + * This type represents a map where: + * - Key (string): Boundary condition type identifier (e.g., "total", "ess_vgrad") + * - Value: Map where: + * - Key (int): Time step or cycle number + * - Value (vector): List of boundary condition IDs or components for that step + */ +using map_of_imap = std::unordered_map>>; + +/** + * @brief Mesh configuration info + */ +struct MeshOptions { + /** + * @brief Type of mesh generation strategy to use + */ + MeshType mesh_type = MeshType::FILE; + + /** + * @brief Path to external mesh file (required when mesh_type = FILE) + */ + std::filesystem::path mesh_file; + + /** + * @brief Number of elements in each direction [nx, ny, nz] for auto-generated mesh + */ + std::array nxyz = {1, 1, 1}; + + /** + * @brief Physical domain size in each direction [x, y, z] for auto-generated mesh + */ + std::array mxyz = {1.0, 1.0, 1.0}; + + /** + * @brief Number of serial refinement levels to apply to mesh + */ + int ref_ser = 0; + + /** + * @brief Number of parallel refinement levels to apply to mesh + */ + int ref_par = 0; + + /** + * @brief Polynomial order for finite element basis functions + */ + int order = 1; + + /** + * @brief Whether to enforce periodic boundary conditions + */ + bool periodicity = false; + + // Validation + bool validate() const; + + // Conversion from toml + static MeshOptions from_toml(const toml::value& toml_input); +}; + +/** + * @brief Grain information for crystal plasticity models + */ +struct GrainInfo { + /** + * @brief Optional file path containing grain orientation data + */ + std::optional orientation_file; + + /** + * @brief Optional file path containing grain ID mapping data + */ + std::optional grain_file; + + /** + * @brief Location of orientation data within state variables array + */ + int ori_state_var_loc = -1; + + /** + * @brief Stride for accessing orientation data in state variables + */ + int ori_stride = 0; + + /** + * @brief Type of orientation representation (Euler, quaternion, custom) + */ + OriType ori_type = OriType::QUAT; + + /** + * @brief Total number of grains in the simulation + */ + int num_grains = 0; + + // Validation + bool validate() const; + + // Conversion from toml + static GrainInfo from_toml(const toml::value& toml_input); +}; + +/** + * @brief Material properties configuration + */ +struct MaterialProperties { + /** + * @brief File path containing material property values + */ + std::filesystem::path properties_file; + + /** + * @brief Number of material properties expected + */ + int num_props = 0; + + /** + * @brief Vector of material property values + */ + std::vector properties; + + // Validation + bool validate() const; + + // Conversion from toml + static MaterialProperties from_toml(const toml::value& toml_input); +}; + +/** + * @brief State variables configuration + */ +struct StateVariables { + /** + * @brief File path containing initial state variable values + */ + std::filesystem::path state_file; + + /** + * @brief Number of state variables per integration point + */ + int num_vars = 0; + + /** + * @brief Initial values for state variables + */ + std::vector initial_values; + + // Validation + bool validate() const; + + // Conversion from toml + static StateVariables from_toml(const toml::value& toml_input); +}; + +/** + * @brief UMAT-specific options + */ +struct UmatOptions { + /** + * @brief Path to the UMAT library file + */ + std::filesystem::path library_path; + + /** + * @brief Name of the UMAT function to call (default: "umat_call") + */ + std::string function_name = "umat_call"; + + /** + * @brief Whether thermal effects are enabled + */ + bool thermal = false; + + /** + * @brief Strategy for loading the library ("persistent", "load_on_setup", "lazy_load") + */ + std::string load_strategy = "persistent"; + + /** + * @brief Whether dynamic loading is enabled + */ + bool enable_dynamic_loading = true; + + /** + * @brief Additional search paths for UMAT libraries + */ + std::vector search_paths; + + /** + * @brief Validates if the load strategy is one of the accepted values + * @return true if load_strategy is valid, false otherwise + */ + bool is_valid_load_strategy() const; + + // Validation + bool validate() const; + + // Conversion from toml + static UmatOptions from_toml(const toml::value& toml_input); +}; + +/** + * @brief ExaCMech-specific options + */ +struct ExaCMechModelOptions { + /** + * @brief Direct shortcut specification for ExaCMech model + */ + std::string shortcut; + + /** + * @brief Size of slip rate tensor + */ + size_t gdot_size = 0; + + /** + * @brief Size of hardening matrix + */ + size_t hard_size = 0; + + /** + * @brief Crystal type (FCC, BCC, or HCP) - legacy approach + */ + std::string xtal_type; + + /** + * @brief Slip type (PowerVoce, PowerVoceNL, or MTSDD) - legacy approach + */ + std::string slip_type; + + /** + * @brief Get the effective shortcut name (either directly specified or derived from legacy + * fields) + * @return The shortcut string to use for ExaCMech + */ + std::string get_effective_shortcut() const; + + // Validation + bool validate() const; + + // Static conversion from TOML + static ExaCMechModelOptions from_toml(const toml::value& toml_input); +}; + +/** + * @brief Material model options + */ +struct MaterialModelOptions { + /** + * @brief Whether crystal plasticity is enabled for this material + */ + bool crystal_plasticity = true; + + /** + * @brief UMAT-specific configuration options (if using UMAT) + */ + std::optional umat; + + /** + * @brief ExaCMech-specific configuration options (if using ExaCMech) + */ + std::optional exacmech; + + // Validation + bool validate() const; + + // Conversion from toml + static MaterialModelOptions from_toml(const toml::value& toml_input); +}; + +/** + * @brief Material options for a specific material/region + */ +struct MaterialOptions { + /** + * @brief Descriptive name for this material + */ + std::string material_name = "default"; + + /** + * @brief Region/material attribute ID associated with this material + */ + int region_id = 1; + + /** + * @brief Type of mechanics model to use for this material + */ + MechType mech_type = MechType::NOTYPE; + + /** + * @brief Material property configuration + */ + MaterialProperties properties; + + /** + * @brief State variable configuration + */ + StateVariables state_vars; + + /** + * @brief Grain information (required for crystal plasticity) + */ + std::optional grain_info; + + /** + * @brief Model-specific configuration options + */ + MaterialModelOptions model; + + /** + * @brief Operating temperature in Kelvin + */ + double temperature = 298.0; + + // Validation + bool validate() const; + + // Conversion from toml + static MaterialOptions from_toml(const toml::value& toml_input); + + /** + * @brief Parse an array of materials from TOML input + * @param toml_input TOML value containing material array or single material + * @return Vector of MaterialOptions parsed from the input + */ + static std::vector from_toml_array(const toml::value& toml_input); +}; + +/** + * @brief Time stepping configuration + */ +struct TimeOptions { + /** + * @brief Type of time stepping strategy being used + */ + TimeStepType time_type = TimeStepType::FIXED; + + /** + * @brief Auto time stepping options + */ + struct AutoTimeOptions { + /** + * @brief Initial time step size for adaptive stepping + */ + double dt_start = 0.1; + + /** + * @brief Minimum allowed time step size + */ + double dt_min = 0.05; + + /** + * @brief Maximum allowed time step size + */ + double dt_max = 1e9; + + /** + * @brief Scaling factor for time step adjustment + */ + double dt_scale = 0.25; + + /** + * @brief Final simulation time + */ + double t_final = 1.0; + + static AutoTimeOptions from_toml(const toml::value& toml_input); + }; + + /** + * @brief Fixed time stepping options + */ + struct FixedTimeOptions { + /** + * @brief Fixed time step size for uniform stepping + */ + double dt = 1.0; + + /** + * @brief Final simulation time + */ + double t_final = 1.0; + + static FixedTimeOptions from_toml(const toml::value& toml_input); + }; + + /** + * @brief Custom time stepping options + */ + struct CustomTimeOptions { + /** + * @brief Number of time steps to take + */ + int nsteps = 1; + + /** + * @brief File path containing custom time step values + */ + std::filesystem::path floc = "custom_dt.txt"; + + /** + * @brief Vector of time step values loaded from file + */ + std::vector dt_values; + + /** + * @brief Load custom time step values from file + * @return true if successful, false if file couldn't be loaded + */ + bool load_custom_dt_values(); + + static CustomTimeOptions from_toml(const toml::value& toml_input); + }; + + /** + * @brief Auto time stepping configuration (if using AUTO mode) + */ + std::optional auto_time; + + /** + * @brief Fixed time stepping configuration (if using FIXED mode) + */ + std::optional fixed_time; + + /** + * @brief Custom time stepping configuration (if using CUSTOM mode) + */ + std::optional custom_time; + + /** + * @brief Whether this is a restart simulation + */ + bool restart = false; + + /** + * @brief Time to restart from (if restart is enabled) + */ + double restart_time = 0.0; + + /** + * @brief Cycle number to restart from (if restart is enabled) + */ + size_t restart_cycle = 0; + + /** + * @brief Determine which time stepping mode is active based on priority (Custom > Auto > Fixed) + */ + void determine_time_type(); + + // Static conversion from TOML + static TimeOptions from_toml(const toml::value& toml_input); + + // Validation + bool validate(); +}; + +/** + * @brief Linear solver configuration + */ +struct LinearSolverOptions { + /** + * @brief Type of iterative linear solver to use + */ + LinearSolverType solver_type = LinearSolverType::CG; + + /** + * @brief Preconditioner type for linear solver acceleration + */ + PreconditionerType preconditioner = PreconditionerType::AMG; + + /** + * @brief Absolute convergence tolerance for linear solver + */ + double abs_tol = 1e-30; + + /** + * @brief Relative convergence tolerance for linear solver + */ + double rel_tol = 1e-10; + + /** + * @brief Maximum number of linear solver iterations + */ + int max_iter = 1000; + + /** + * @brief Verbosity level for linear solver output (0 = silent) + */ + int print_level = 0; + + // Validation + bool validate() const; + + // Conversion from toml + static LinearSolverOptions from_toml(const toml::value& toml_input); +}; + +/** + * @brief Nonlinear solver configuration + */ +struct NonlinearSolverOptions { + /** + * @brief Maximum number of nonlinear iterations per time step + */ + int iter = 25; + + /** + * @brief Relative convergence tolerance for nonlinear solver + */ + double rel_tol = 1e-5; + + /** + * @brief Absolute convergence tolerance for nonlinear solver + */ + double abs_tol = 1e-10; + + /** + * @brief Type of nonlinear solver algorithm to use + */ + NonlinearSolverType nl_solver = NonlinearSolverType::NR; + + // Validation + bool validate() const; + + // Conversion from toml + static NonlinearSolverOptions from_toml(const toml::value& toml_input); +}; + +/** + * @brief Global solver configuration + */ +struct SolverOptions { + /** + * @brief Finite element assembly strategy for matrix construction + */ + AssemblyType assembly = AssemblyType::FULL; + + /** + * @brief Runtime execution model for computations + */ + RTModel rtmodel = RTModel::CPU; + + /** + * @brief Integration model for handling material nonlinearities + */ + IntegrationModel integ_model = IntegrationModel::DEFAULT; + + /** + * @brief Configuration for iterative linear solver + */ + LinearSolverOptions linear_solver; + + /** + * @brief Configuration for nonlinear Newton-Raphson solver + */ + NonlinearSolverOptions nonlinear_solver; + + // Validation + bool validate(); + + // Conversion from toml + static SolverOptions from_toml(const toml::value& toml_input); +}; + +/** + * @brief Time-dependent boundary condition configuration + */ +struct BCTimeInfo { + /** + * @brief Whether boundary conditions vary with simulation time + */ + bool time_dependent = false; + + /** + * @brief Whether boundary conditions vary with simulation cycle number + */ + bool cycle_dependent = false; + + /** + * @brief Time values for time-dependent boundary condition updates + */ + std::vector times; + + /** + * @brief Cycle numbers for cycle-dependent boundary condition updates + */ + std::vector cycles; + + // Validation + bool validate() const; + + // Conversion from toml + static BCTimeInfo from_toml(const toml::value& toml_input); +}; + +/** + * @brief Velocity boundary condition + */ +struct VelocityBC { + /** + * @brief Node or boundary attribute IDs where velocity BCs are applied + */ + std::vector essential_ids; + + /** + * @brief Component indices (0=x, 1=y, 2=z) for velocity constraints + */ + std::vector essential_comps; + + /** + * @brief Prescribed velocity values corresponding to essential_comps + */ + std::vector essential_vals; + + // Validation + bool validate() const; + + // Conversion from toml + static VelocityBC from_toml(const toml::value& toml_input); +}; + +/** + * @brief Velocity gradient boundary condition + */ +struct VelocityGradientBC { + /** + * @brief Velocity gradient tensor components (stored as flattened 3x3 matrix) + */ + std::vector velocity_gradient; + + /** + * @brief Component IDs for this boundary condition + */ + std::vector essential_comps; + + /** + * @brief Node/element IDs where this boundary condition applies + */ + std::vector essential_ids; + + /** + * @brief Time-dependent information for this boundary condition + */ + BCTimeInfo time_info; + + /** + * @brief Origin point for velocity gradient application + */ + std::optional> origin; + + // Validation + bool validate() const; + + // Conversion from toml + static VelocityGradientBC from_toml(const toml::value& toml_input); +}; + +/** + * @brief Legacy boundary condition format support for backward compatibility + */ +struct LegacyBC { + /** + * @brief Whether boundary conditions change over time + */ + bool changing_ess_bcs = false; + + /** + * @brief Experimental feature monotonic z-loading BCs better + * single crystal simulations + */ + bool mono_def_bcs = false; + + /** + * @brief Time steps at which boundary conditions are updated + */ + std::vector update_steps = {1}; + + /** + * @brief Essential boundary condition node IDs + * Can be either flat vector (constant BCs) or nested vector (time-dependent BCs) + */ + std::variant, std::vector>> essential_ids; + + /** + * @brief Essential boundary condition component IDs + * Can be either flat vector (constant BCs) or nested vector (time-dependent BCs) + */ + std::variant, std::vector>> essential_comps; + + /** + * @brief Essential boundary condition values + * Can be either flat vector (constant BCs) or nested vector (time-dependent BCs) + */ + std::variant, std::vector>> essential_vals; + + /** + * @brief Essential velocity gradient values + * Can be either double-nested (constant BCs) or triple-nested (time-dependent BCs) + */ + std::variant>, std::vector>>> + essential_vel_grad; + + /** + * @brief Origin point for velocity gradient boundary conditions + */ + std::vector vgrad_origin = {0.0, 0.0, 0.0}; +}; + +/** + * @brief Boundary conditions configuration + */ +struct BoundaryOptions { + /** + * @brief Modern structured velocity boundary conditions + */ + std::vector velocity_bcs; + + /** + * @brief Modern structured velocity gradient boundary conditions + */ + std::vector vgrad_bcs; + + /** + * @brief Legacy format support for direct compatibility + */ + LegacyBC legacy_bcs; + + /** + * @brief Type alias for nested boundary condition mapping + */ + using map_of_imap = std::unordered_map>>; + + /** + * @brief Maps time steps to velocity values for BCManager compatibility + */ + std::unordered_map> map_ess_vel; + + /** + * @brief Maps time steps to velocity gradient values for BCManager compatibility + */ + std::unordered_map> map_ess_vgrad; + + /** + * @brief Maps BC types and time steps to component IDs for BCManager compatibility + */ + map_of_imap map_ess_comp; + + /** + * @brief Maps BC types and time steps to node/element IDs for BCManager compatibility + */ + map_of_imap map_ess_id; + + /** + * @brief Time steps at which boundary conditions are updated + */ + std::vector update_steps; + + /** + * @brief Time-dependent boundary condition information + */ + BCTimeInfo time_info; + + /** + * @brief Experimental feature monotonic z-loading BCs better + * single crystal simulations + */ + bool mono_def_bcs = false; + + // Transform raw BC data into structured format during validation + bool validate(); + + /** + * @brief Transform legacy flat arrays into structured VelocityBC objects + */ + void transform_legacy_format(); + + /** + * @brief Populate the map structures expected by BCManager + */ + void populate_bc_manager_maps(); + + /** + * @brief Helper method to create BC objects from legacy arrays + * @param step Time step number + * @param ess_ids Essential boundary condition node IDs + * @param ess_comps Essential boundary condition component IDs + * @param essential_vals Essential boundary condition values + * @param essential_vel_grad Essential velocity gradient values + */ + void create_boundary_conditions(int step, + const std::vector& ess_ids, + const std::vector& ess_comps, + const std::vector& essential_vals, + const std::vector>& essential_vel_grad); + + // Conversion from toml + static BoundaryOptions from_toml(const toml::value& toml_input); +}; + +/** + * @brief Visualization options for lattice orientation + */ +struct LightUpOptions { + /** + * @brief Whether lattice orientation visualization is enabled + */ + bool enabled = false; + + /** + * @brief Name to match with MaterialOptions::material_name + */ + std::string material_name = ""; + + /** + * @brief Region ID (resolved during validation from material_name) + */ + std::optional region_id; + + /** + * @brief Crystal directions to visualize as [h,k,l] indices + */ + std::vector> hkl_directions; + + /** + * @brief Angular tolerance for lattice direction matching (radians) + */ + double distance_tolerance = 0.0873; + + /** + * @brief Sample reference direction for orientation comparison + */ + std::array sample_direction = {0.0, 0.0, 1.0}; + + /** + * @brief Lattice parameters + * 'cubic' a + * 'hexagonal' a, c + * 'trigonal' a, c + * 'rhombohedral' a, alpha (in radians) + * 'tetragonal' a, c + * 'orthorhombic' a, b, c + * 'monoclinic' a, b, c, beta (in radians) + * 'triclinic' a, b, c, alpha, beta, gamma (in radians) + */ + std::vector lattice_parameters = {3.6}; + + /** + * @brief Base filename for lattice orientation output files + */ + std::string lattice_basename = "lattice_avg_"; + + /** + * @brief Lattice type that the user has set + */ + LatticeType lattice_type = LatticeType::CUBIC; + + /** + * @brief note whether or not a light-up file was auto-generated + */ + bool is_auto_generated = false; + + /** + * @brief Equality operator for uniqueness checking + */ + bool operator==(const LightUpOptions& other) const { + // Compare all relevant fields except is_auto_generated + return material_name == other.material_name && region_id == other.region_id; + } + + // Validation + bool validate() const; + + // Conversion from toml + static LightUpOptions from_toml(const toml::value& toml_input); + + /** + * @brief Parse light up options from TOML with legacy format support + * @param toml_input TOML value containing light up configuration + * @return Vector of LightUpOptions (may be empty if disabled) + */ + static std::vector from_toml_with_legacy(const toml::value& toml_input); + + /** + * @brief Resolve material_name to region_id using material list + * @param materials Vector of material configurations to search + * @return true if material found and region_id resolved, false otherwise + */ + bool resolve_region_id(const std::vector& materials); +}; + +/** + * @brief Visualization and output options + */ +struct VisualizationOptions { + /** + * @brief Enable VisIt output format + */ + bool visit = false; + + /** + * @brief Enable ParaView output format + */ + bool paraview = false; + + /** + * @brief Enable Conduit output format + */ + bool conduit = false; + + /** + * @brief Enable ADIOS2 output format + */ + bool adios2 = false; + + /** + * @brief Frequency of visualization output (every N time steps) + */ + int output_frequency = 1; + + /** + * @brief Base path/filename for visualization output files + */ + std::filesystem::path floc = "results"; + + // Validation + bool validate() const; + + // Conversion from toml + static VisualizationOptions from_toml(const toml::value& toml_input); +}; + +/** + * @brief Volume average calculation options + */ +struct VolumeAverageOptions { + /** + * @brief Filename for averaged stress output + */ + std::filesystem::path avg_stress_fname = "avg_stress.txt"; + + /** + * @brief Filename for averaged deformation gradient output + */ + std::filesystem::path avg_def_grad_fname = "avg_def_grad.txt"; + + /** + * @brief Filename for averaged plastic work output + */ + std::filesystem::path avg_pl_work_fname = "avg_pl_work.txt"; + + /** + * @brief Filename for averaged equivalent plastic strain output + */ + std::filesystem::path avg_eq_pl_strain_fname = "avg_eq_pl_strain.txt"; + + /** + * @brief Filename for averaged Euler strain output + */ + std::filesystem::path avg_euler_strain_fname = "avg_euler_strain.txt"; + + /** + * @brief Filename for averaged elastic strain output + */ + std::filesystem::path avg_elastic_strain_fname = "avg_elastic_strain.txt"; + + /** + * @brief Whether volume averaging is enabled + */ + bool enabled = true; + + /** + * @brief Whether to output stress averages + */ + bool stress = true; + + /** + * @brief Whether to output deformation gradient averages + */ + bool def_grad = false; + + /** + * @brief Whether to output Euler strain averages + */ + bool euler_strain = false; + + /** + * @brief Whether to output equivalent plastic strain averages + */ + bool eq_pl_strain = false; + + /** + * @brief Whether to output plastic work averages + */ + bool plastic_work = false; + + /** + * @brief Whether to output elastic strain averages (ExaCMech only) + */ + bool elastic_strain = false; + + /** + * @brief Whether to output additional average quantities + */ + bool additional_avgs = false; + + /** + * @brief Output directory for volume average files + */ + std::filesystem::path output_directory = "results"; + + /** + * @brief Frequency of volume average output (every N time steps) + */ + int output_frequency = 1; + + // Validation + bool validate() const; + + // Conversion from toml + static VolumeAverageOptions from_toml(const toml::value& toml_input); + + /** + * @brief Parse volume average options from TOML with legacy format support + * @param toml_input TOML value containing volume average configuration + * @return VolumeAverageOptions parsed from input + */ + static VolumeAverageOptions from_toml_with_legacy(const toml::value& toml_input); +}; + +/** + * @brief Projection options for visualization + */ +struct ProjectionOptions { + /** + * @brief List of enabled projection types for visualization + */ + std::vector enabled_projections; + + /** + * @brief Whether to automatically enable compatible projections + */ + bool auto_enable_compatible = true; + + // Validation + bool validate() const; + + // Conversion from toml + static ProjectionOptions from_toml(const toml::value& toml_input); +}; + +/** + * @brief Post-processing options + */ +struct PostProcessingOptions { + /** + * @brief Configuration for volume-averaged quantity calculations + */ + VolumeAverageOptions volume_averages; + + /** + * @brief Configuration for field projection operations + */ + ProjectionOptions projections; + + /** + * @brief Light-up analysis configurations for crystal orientation visualization + */ + std::vector light_up_configs; + + // Validation + bool validate() const; + + // Conversion from toml + static PostProcessingOptions from_toml(const toml::value& toml_input); + + /** + * @brief Get all enabled light up configurations + * @return Vector of enabled LightUpOptions + */ + std::vector get_enabled_light_up_configs() const; + + /** + * @brief Get light up configuration for a specific region + * @param region_id Region ID to search for + * @return Pointer to LightUpOptions if found, nullptr otherwise + */ + LightUpOptions* get_light_up_config_for_region(int region_id); + + /** + * @brief Get light up configuration for a specific region (const version) + * @param region_id Region ID to search for + * @return Const pointer to LightUpOptions if found, nullptr otherwise + */ + const LightUpOptions* get_light_up_config_for_region(int region_id) const; +}; + +/** + * @brief Main options class for ExaConstit simulation configuration + */ +class ExaOptions { +public: + /** + * @brief Base name for output files (derived from input filename) + */ + std::string basename = "exaconstit"; + + /** + * @brief Version string for ExaConstit + */ + std::string version = "0.8.0"; + + /** + * @brief Mesh generation and refinement options + */ + MeshOptions mesh; + + /** + * @brief Time stepping configuration + */ + TimeOptions time; + + /** + * @brief Solver and assembly options + */ + SolverOptions solvers; + + /** + * @brief Visualization output options + */ + VisualizationOptions visualization; + + /** + * @brief Material configurations for all regions + */ + std::vector materials; + + /** + * @brief Boundary condition specifications + */ + BoundaryOptions boundary_conditions; + + /** + * @brief Post-processing and analysis options + */ + PostProcessingOptions post_processing; + + /** + * @brief Paths to external material configuration files + */ + std::vector material_files; + + /** + * @brief Path to external post-processing configuration file + */ + std::optional post_processing_file; + + /** + * @brief Optional orientation file path for grain data + */ + std::optional orientation_file; + + /** + * @brief Optional grain mapping file path + */ + std::optional grain_file; + + /** + * @brief Optional region mapping file path + */ + std::optional region_mapping_file; + + /** + * @brief Parse the main configuration file and populate all options + * @param filename Path to the TOML configuration file + * @param my_id MPI rank for error reporting + */ + void parse_options(const std::string& filename, int my_id); + + /** + * @brief Core option parsing from TOML value + * @param toml_input Parsed TOML configuration data + */ + void parse_from_toml(const toml::value& toml_input); + + /** + * @brief Validate all configuration options for consistency + * @return true if all options are valid, false otherwise + */ + bool validate(); + + /** + * @brief Print all options in a formatted way for debugging + */ + void print_options() const; + +private: + /** + * @brief Parse mesh-specific options from TOML input + * @param toml_input TOML value containing mesh configuration + */ + void parse_mesh_options(const toml::value& toml_input); + + /** + * @brief Parse time stepping options from TOML input + * @param toml_input TOML value containing time configuration + */ + void parse_time_options(const toml::value& toml_input); + + /** + * @brief Parse solver options from TOML input + * @param toml_input TOML value containing solver configuration + */ + void parse_solver_options(const toml::value& toml_input); + + /** + * @brief Parse material options from TOML input + * @param toml_input TOML value containing material configuration + */ + void parse_material_options(const toml::value& toml_input); + + /** + * @brief Parse boundary condition options from TOML input + * @param toml_input TOML value containing boundary condition configuration + */ + void parse_boundary_options(const toml::value& toml_input); + + /** + * @brief Parse visualization options from TOML input + * @param toml_input TOML value containing visualization configuration + */ + void parse_visualization_options(const toml::value& toml_input); + + /** + * @brief Parse post-processing options from TOML input + * @param toml_input TOML value containing post-processing configuration + */ + void parse_post_processing_options(const toml::value& toml_input); + + /** + * @brief Parse model-specific options for a material + * @param toml_input TOML value containing model configuration + * @param material Material object to populate with model options + */ + void parse_model_options(const toml::value& toml_input, MaterialOptions& material); + + /** + * @brief Load material configurations from external files + */ + void load_material_files(); + + /** + * @brief Load post-processing configuration from external file + */ + void load_post_processing_file(); + + /** + * @brief Print mesh options in formatted output + */ + void print_mesh_options() const; + + /** + * @brief Print time stepping options in formatted output + */ + void print_time_options() const; + + /** + * @brief Print solver options in formatted output + */ + void print_solver_options() const; + + /** + * @brief Print material options in formatted output + */ + void print_material_options() const; + + /** + * @brief Print boundary condition options in formatted output + */ + void print_boundary_options() const; + + /** + * @brief Print visualization options in formatted output + */ + void print_visualization_options() const; + + /** + * @brief Print post-processing options in formatted output + */ + void print_post_processing_options() const; +}; + +/** + * @brief Convert string to MeshType enum + * @param str String representation of mesh type ("file", "auto") + * @return Corresponding MeshType enum value, or NOTYPE if invalid + */ +MeshType string_to_mesh_type(const std::string& str); + +/** + * @brief Convert string to TimeStepType enum + * @param str String representation of time step type ("fixed", "auto", "custom") + * @return Corresponding TimeStepType enum value, or NOTYPE if invalid + */ +TimeStepType string_to_time_step_type(const std::string& str); + +/** + * @brief Convert string to MechType enum + * @param str String representation of mechanics type ("umat", "exacmech") + * @return Corresponding MechType enum value, or NOTYPE if invalid + */ +MechType string_to_mech_type(const std::string& str); + +/** + * @brief Convert string to RTModel enum + * @param str String representation of runtime model ("CPU", "OPENMP", "GPU") + * @return Corresponding RTModel enum value, or NOTYPE if invalid + */ +RTModel string_to_rt_model(const std::string& str); + +/** + * @brief Convert string to AssemblyType enum + * @param str String representation of assembly type ("FULL", "PA", "EA") + * @return Corresponding AssemblyType enum value, or NOTYPE if invalid + */ +AssemblyType string_to_assembly_type(const std::string& str); + +/** + * @brief Convert string to IntegrationModel enum + * @param str String representation of integration model ("FULL", "BBAR") + * @return Corresponding IntegrationModel enum value, or NOTYPE if invalid + */ +IntegrationModel string_to_integration_model(const std::string& str); + +/** + * @brief Convert string to LinearSolverType enum + * @param str String representation of linear solver type ("CG", "GMRES", "MINRES", "BICGSTAB") + * @return Corresponding LinearSolverType enum value + */ +LinearSolverType string_to_linear_solver_type(const std::string& str); + +/** + * @brief Convert string to NonlinearSolverType enum + * @param str String representation of nonlinear solver type ("NR", "NRLS") + * @return Corresponding NonlinearSolverType enum value, or NOTYPE if invalid + */ +NonlinearSolverType string_to_nonlinear_solver_type(const std::string& str); + +/** + * @brief Convert string to PreconditionerType enum + * @param str String representation of preconditioner type ("JACOBI", "AMG", "ILU", "L1GS", + * "CHEBYSHEV") + * @return Corresponding PreconditionerType enum value + */ +PreconditionerType string_to_preconditioner_type(const std::string& str); + +/** + * @brief Convert string to OriType enum + * @param str String representation of orientation type ("quat", "custom", "euler") + * @return Corresponding OriType enum value, or NOTYPE if invalid + */ +OriType string_to_ori_type(const std::string& str); + +/** + * @brief Convert string to LatticeType enum + * @param str String representation of lattice type ("CUBIC", "HEXAGONAL", "TRIGONAL", + * "RHOMBOHEDRAL", "TETRAGONAL", "ORTHORHOMBIC", "MONOCLINIC", "TRICLINIC") + * @return Corresponding LatticeType enum value + */ +LatticeType string_to_lattice_type(const std::string& str); \ No newline at end of file diff --git a/src/options/option_post_processing.cpp b/src/options/option_post_processing.cpp new file mode 100644 index 0000000..32b0faa --- /dev/null +++ b/src/options/option_post_processing.cpp @@ -0,0 +1,681 @@ +#include "options/option_parser_v2.hpp" +#include "options/option_util.hpp" + +#include +#include + +/** + * @brief Check if the TOML input contains legacy volume averaging options in [Visualizations] + */ +bool has_legacy_volume_averaging(const toml::value& toml_input) { + if (!toml_input.contains("Visualizations")) { + return false; + } + + const auto viz_table = toml::find(toml_input, "Visualizations"); + + // Check for legacy volume averaging indicators + return viz_table.contains("avg_stress_fname") || viz_table.contains("additional_avgs") || + viz_table.contains("avg_def_grad_fname") || viz_table.contains("avg_pl_work_fname") || + viz_table.contains("avg_euler_strain_fname"); +} + +/** + * @brief Check if the TOML input contains legacy light-up options in [Visualizations] + */ +bool has_legacy_light_up(const toml::value& toml_input) { + if (!toml_input.contains("Visualizations")) { + return false; + } + + const auto viz_table = toml::find(toml_input, "Visualizations"); + + // Check for legacy light-up indicators + return viz_table.contains("light_up") || viz_table.contains("light_up_hkl") || + viz_table.contains("light_dist_tol") || viz_table.contains("light_s_dir") || + viz_table.contains("lattice_params") || viz_table.contains("lattice_basename"); +} + +/** + * @brief Parse legacy light-up options from [Visualizations] table + */ +LightUpOptions parse_legacy_light_up(const toml::value& toml_input) { + LightUpOptions options; + + if (!toml_input.contains("Visualizations")) { + return options; + } + + const auto viz_table = toml::find(toml_input, "Visualizations"); + + // Check if light-up is enabled + if (viz_table.contains("light_up")) { + options.enabled = toml::find(viz_table, "light_up"); + } + + if (!options.enabled) { + return options; // Return early if not enabled + } + + // Parse HKL directions (light_up_hkl -> hkl_directions) + if (viz_table.contains("light_up_hkl")) { + const auto hkl_array = toml::find(viz_table, "light_up_hkl"); + if (hkl_array.is_array()) { + options.hkl_directions.clear(); + for (const auto& direction : hkl_array.as_array()) { + if (direction.is_array() && direction.as_array().size() >= 3) { + std::array hkl_dir; + if (direction.at(0).is(toml::value_t::integer)) { + auto dir_vec = toml::get>(direction); + std::copy_n(dir_vec.begin(), 3, hkl_dir.begin()); + } else { + auto dir_vec = toml::get>(direction); + std::copy_n(dir_vec.begin(), 3, hkl_dir.begin()); + } + options.hkl_directions.push_back(hkl_dir); + } + } + } + } + + // Parse distance tolerance (light_dist_tol -> distance_tolerance) + if (viz_table.contains("light_dist_tol")) { + options.distance_tolerance = toml::find(viz_table, "light_dist_tol"); + } + + // Parse sample direction (light_s_dir -> sample_direction) + if (viz_table.contains("light_s_dir")) { + const auto dir = toml::find(toml_input, "light_s_dir"); + if (dir.at(0).is(toml::value_t::integer)) { + auto dir_vec = toml::get>(dir); + if (dir_vec.size() >= 3) { + std::copy_n(dir_vec.begin(), 3, options.sample_direction.begin()); + } + } else { + auto dir_vec = toml::get>(dir); + if (dir_vec.size() >= 3) { + std::copy_n(dir_vec.begin(), 3, options.sample_direction.begin()); + } + } + } + + // Parse lattice parameters (lattice_params -> lattice_parameters) + if (viz_table.contains("lattice_params")) { + auto params = toml::find>(viz_table, "lattice_params"); + if (params.size() >= 3) { + options.lattice_parameters = {params[0]}; + } + } + + options.lattice_type = LatticeType::CUBIC; + + // Parse lattice basename + if (viz_table.contains("lattice_basename")) { + options.lattice_basename = toml::find(viz_table, "lattice_basename"); + } + + return options; +} + +LightUpOptions LightUpOptions::from_toml(const toml::value& toml_input) { + LightUpOptions options; + + if (toml_input.contains("light_up")) { + options.enabled = toml::find(toml_input, "light_up"); + } else if (toml_input.contains("enabled")) { + options.enabled = toml::find(toml_input, "enabled"); + } + + // Parse material association - REQUIRED for multi-material support + if (toml_input.contains("material_name")) { + options.material_name = toml::find(toml_input, "material_name"); + } else { + WARNING_0_OPT("Warning: LightUp configuration missing 'material_name' field"); + } + + if (toml_input.contains("light_up_hkl")) { + const auto hkl = toml::find(toml_input, "light_up_hkl"); + if (hkl.is_array()) { + for (const auto& dir : hkl.as_array()) { + if (dir.is_array() && dir.as_array().size() >= 3) { + std::array direction; + if (dir.at(0).is(toml::value_t::integer)) { + auto dir_vec = toml::get>(dir); + std::copy_n(dir_vec.begin(), 3, direction.begin()); + } else { + auto dir_vec = toml::get>(dir); + std::copy_n(dir_vec.begin(), 3, direction.begin()); + } + options.hkl_directions.push_back(direction); + } + } + } + } else if (toml_input.contains("hkl_directions")) { + const auto hkl = toml::find(toml_input, "hkl_directions"); + if (hkl.is_array()) { + for (const auto& dir : hkl.as_array()) { + if (dir.is_array() && dir.as_array().size() >= 3) { + std::array direction; + if (dir.at(0).is(toml::value_t::integer)) { + auto dir_vec = toml::get>(dir); + std::copy_n(dir_vec.begin(), 3, direction.begin()); + } else { + auto dir_vec = toml::get>(dir); + std::copy_n(dir_vec.begin(), 3, direction.begin()); + } + options.hkl_directions.push_back(direction); + } + } + } + } + + if (toml_input.contains("light_dist_tol")) { + options.distance_tolerance = toml::find(toml_input, "light_dist_tol"); + } else if (toml_input.contains("distance_tolerance")) { + options.distance_tolerance = toml::find(toml_input, "distance_tolerance"); + } + + if (toml_input.contains("light_s_dir")) { + const auto dir = toml::find(toml_input, "light_s_dir"); + if (dir.at(0).is(toml::value_t::integer)) { + auto dir_vec = toml::get>(dir); + if (dir_vec.size() >= 3) { + std::copy_n(dir_vec.begin(), 3, options.sample_direction.begin()); + } + } else { + auto dir_vec = toml::get>(dir); + if (dir_vec.size() >= 3) { + std::copy_n(dir_vec.begin(), 3, options.sample_direction.begin()); + } + } + } else if (toml_input.contains("sample_direction")) { + const auto dir = toml::find(toml_input, "sample_direction"); + if (dir.at(0).is(toml::value_t::integer)) { + auto dir_vec = toml::get>(dir); + if (dir_vec.size() >= 3) { + std::copy_n(dir_vec.begin(), 3, options.sample_direction.begin()); + } + } else { + auto dir_vec = toml::get>(dir); + if (dir_vec.size() >= 3) { + std::copy_n(dir_vec.begin(), 3, options.sample_direction.begin()); + } + } + } + + if (toml_input.contains("lattice_params")) { + auto params = toml::find>(toml_input, "lattice_params"); + if (params.size() >= 1) { + std::copy(params.begin(), params.end(), options.lattice_parameters.begin()); + } + } else if (toml_input.contains("lattice_parameters")) { + auto params = toml::find>(toml_input, "lattice_parameters"); + if (params.size() >= 1) { + std::copy(params.begin(), params.end(), options.lattice_parameters.begin()); + } + } + + if (toml_input.contains("laue_type")) { + auto laue_type = toml::find(toml_input, "laue_type"); + options.lattice_type = string_to_lattice_type(laue_type); + } + + if (toml_input.contains("lattice_basename")) { + options.lattice_basename = toml::find(toml_input, "lattice_basename"); + } + + return options; +} + +/** + * @brief Enhanced LightUpOptions::from_toml that handles legacy format + */ +std::vector LightUpOptions::from_toml_with_legacy(const toml::value& toml_input) { + std::vector light_up_configs; + + // First check if we have legacy format in [Visualizations] + if (has_legacy_light_up(toml_input)) { + auto legacy_options = parse_legacy_light_up(toml_input); + if (legacy_options.enabled) { + // Legacy format doesn't have material_name, so assign default + legacy_options.material_name = "default_material"; + light_up_configs.push_back(legacy_options); + std::cout << "Info: Legacy LightUp configuration detected. " + << "Assigned to default_material. Consider updating to new format." + << std::endl; + } + } + + // Then check for modern format in [PostProcessing.light_up] + // Modern format takes precedence if both exist + if (toml_input.contains("PostProcessing")) { + const auto post_proc = toml::find(toml_input, "PostProcessing"); + if (post_proc.contains("light_up")) { + const auto light_up_section = toml::find(post_proc, "light_up"); + + if (light_up_section.is_array()) { + // New array format: multiple light_up configurations + for (const auto& light_config : light_up_section.as_array()) { + auto light_options = LightUpOptions::from_toml(light_config); + if (light_options.enabled) { + if (light_options.material_name.empty()) { + WARNING_0_OPT("Warning: LightUp config in array missing material_name. " + "Skipping."); + continue; + } + light_up_configs.push_back(light_options); + } + } + } else { + // Single config format (legacy or modern) + auto modern_options = LightUpOptions::from_toml(light_up_section); + if (modern_options.enabled) { + // If no material_name specified in single config, assign default for backward + // compatibility + if (modern_options.material_name.empty()) { + modern_options.material_name = "default_material"; + std::cout + << "Info: Single LightUp configuration without material_name detected. " + << "Assigned to default_material. Consider adding material_name field." + << std::endl; + } + // Only add if we don't already have a legacy config (modern takes precedence) + if (light_up_configs.empty() || + light_up_configs[0].material_name != "default_material") { + light_up_configs.clear(); // Clear any legacy config + light_up_configs.push_back(modern_options); + } + } + } + } + } + + return light_up_configs; +} + +bool LightUpOptions::resolve_region_id(const std::vector& materials) { + for (const auto& material : materials) { + if (material.material_name == material_name) { + region_id = material.region_id; + return true; + } + } + std::ostringstream err; + err << "Error: LightUp configuration references unknown material: " << material_name + << std::endl; + WARNING_0_OPT(err.str()); + return false; +} + +bool LightUpOptions::validate() const { + if (!enabled) { + return true; + } + + if (material_name.empty()) { + WARNING_0_OPT("Error: LightUp configuration must specify a material_name"); + return false; + } + + if (hkl_directions.size() < 1) { + WARNING_0_OPT("Error: LightUp table did not provide any values in the hkl_directions"); + return false; + } + + if (distance_tolerance < 0) { + WARNING_0_OPT("Error: LightUp table did not provide a positive distance_tolerance value"); + return false; + } + + switch (lattice_type) { + case LatticeType::CUBIC: { + if (lattice_parameters.size() != 1) { + WARNING_0_OPT("Error: LightUp table did not provide the right number of " + "lattice_parameters: 'cubic' -> a"); + return false; + } + break; + } + case LatticeType::HEXAGONAL: + case LatticeType::TRIGONAL: + case LatticeType::TETRAGONAL: { + if (lattice_parameters.size() != 2) { + WARNING_0_OPT("Error: LightUp table did not provide the right number of " + "lattice_parameters: 'hexagonal / trigonal / tetragonal' -> a, c"); + return false; + } + break; + } + case LatticeType::RHOMBOHEDRAL: { + if (lattice_parameters.size() != 2) { + WARNING_0_OPT("Error: LightUp table did not provide the right number of " + "lattice_parameters: 'rhombohedral' -> a, alpha (in radians)"); + return false; + } + break; + } + case LatticeType::ORTHORHOMBIC: { + if (lattice_parameters.size() != 3) { + WARNING_0_OPT("Error: LightUp table did not provide the right number of " + "lattice_parameters: 'orthorhombic' -> a, b, c"); + return false; + } + break; + } + case LatticeType::MONOCLINIC: { + if (lattice_parameters.size() != 4) { + WARNING_0_OPT("Error: LightUp table did not provide the right number of " + "lattice_parameters: 'monoclinic' -> a, b, c, beta (in radians)"); + return false; + } + break; + } + case LatticeType::TRICLINIC: { + if (lattice_parameters.size() != 6) { + WARNING_0_OPT( + "Error: LightUp table did not provide the right number of lattice_parameters: " + "'triclinic' -> a, b, c, alpha, beta, gamma (in radians)"); + return false; + } + break; + } + default: + break; + } + + for (const auto lp : lattice_parameters) { + if (lp < 0) { + WARNING_0_OPT( + "Error: LightUp table did not provide a positive lattice_parameters value"); + return false; + } + } + + // Implement validation logic + return true; +} + +VisualizationOptions VisualizationOptions::from_toml(const toml::value& toml_input) { + VisualizationOptions options; + + if (toml_input.contains("visit")) { + options.visit = toml::find(toml_input, "visit"); + } + + if (toml_input.contains("paraview")) { + options.paraview = toml::find(toml_input, "paraview"); + } + + if (toml_input.contains("adios2")) { + options.adios2 = toml::find(toml_input, "adios2"); + } + + if (toml_input.contains("steps") || toml_input.contains("output_frequency")) { + // Support both naming conventions + const auto& freq_key = toml_input.contains("steps") ? "steps" : "output_frequency"; + options.output_frequency = toml::find(toml_input, freq_key); + } + + if (toml_input.contains("floc")) { + options.floc = toml::find(toml_input, "floc"); + } + + return options; +} + +bool VisualizationOptions::validate() const { + if (output_frequency < 1) { + WARNING_0_OPT("Error: Visualizations table did not provide a valid output frequency valid " + "as it was less than 1"); + return false; + } + return true; +} + +/** + * @brief Parse legacy volume averaging options from [Visualizations] table + */ +VolumeAverageOptions parse_legacy_volume_averaging(const toml::value& toml_input) { + VolumeAverageOptions options; + + if (!toml_input.contains("Visualizations")) { + return options; + } + + const auto viz_table = toml::find(toml_input, "Visualizations"); + + // Check if volume averaging should be enabled + // In legacy format, presence of avg_stress_fname means it's enabled + // or if one of the other fields are noted, but + options.enabled = true; + options.stress = true; // Stress was always enabled in legacy format + if (viz_table.contains("avg_stress_fname")) { + options.avg_stress_fname = toml::find(viz_table, "avg_stress_fname"); + } + + // Extract output directory from floc + if (viz_table.contains("floc")) { + options.output_directory = toml::find(viz_table, "floc"); + } + + // Extract output frequency from steps + if (viz_table.contains("steps")) { + options.output_frequency = toml::find(viz_table, "steps"); + } + + // Check for additional_avgs flag + bool additional_avgs = false; + if (viz_table.contains("additional_avgs")) { + additional_avgs = toml::find(viz_table, "additional_avgs"); + options.additional_avgs = additional_avgs; + } + + // Set deformation gradient options + if (additional_avgs || viz_table.contains("avg_def_grad_fname")) { + options.def_grad = true; + if (viz_table.contains("avg_def_grad_fname")) { + options.avg_def_grad_fname = toml::find(viz_table, "avg_def_grad_fname"); + } + } + + // Set plastic work options + if (additional_avgs || viz_table.contains("avg_pl_work_fname")) { + options.plastic_work = true; + if (viz_table.contains("avg_pl_work_fname")) { + options.avg_pl_work_fname = toml::find(viz_table, "avg_pl_work_fname"); + } + } + + if (additional_avgs || viz_table.contains("avg_eps_fname")) { + options.eq_pl_strain = true; + if (viz_table.contains("avg_eps_fname")) { + options.avg_eq_pl_strain_fname = toml::find(viz_table, "avg_eps_fname"); + } + } + + // Set Euler strain options + if (additional_avgs || viz_table.contains("avg_euler_strain_fname")) { + options.euler_strain = true; + if (viz_table.contains("avg_euler_strain_fname")) { + options.avg_euler_strain_fname = toml::find(viz_table, + "avg_euler_strain_fname"); + } + } + + if (additional_avgs || viz_table.contains("avg_elastic_strain_fname")) { + options.elastic_strain = true; + if (viz_table.contains("avg_elastic_strain_fname")) { + options.avg_elastic_strain_fname = toml::find(viz_table, + "avg_elastic_strain_fname"); + } + } + + return options; +} + +VolumeAverageOptions VolumeAverageOptions::from_toml(const toml::value& toml_input) { + VolumeAverageOptions options; + + if (toml_input.contains("enabled")) { + options.enabled = toml::find(toml_input, "enabled"); + } + + if (toml_input.contains("stress")) { + options.stress = toml::find(toml_input, "stress"); + } + + if (toml_input.contains("def_grad")) { + options.def_grad = toml::find(toml_input, "def_grad"); + } + + if (toml_input.contains("euler_strain")) { + options.euler_strain = toml::find(toml_input, "euler_strain"); + } + + if (toml_input.contains("plastic_work")) { + options.plastic_work = toml::find(toml_input, "plastic_work"); + } + + if (toml_input.contains("elastic_strain")) { + options.elastic_strain = toml::find(toml_input, "elastic_strain"); + } + + if (toml_input.contains("eq_pl_strain")) { + options.eq_pl_strain = toml::find(toml_input, "eq_pl_strain"); + } + + if (toml_input.contains("output_directory")) { + options.output_directory = toml::find(toml_input, "output_directory"); + } + + if (toml_input.contains("output_frequency")) { + options.output_frequency = toml::find(toml_input, "output_frequency"); + } + + return options; +} + +/** + * @brief Enhanced VolumeAverageOptions::from_toml that handles legacy format + */ +VolumeAverageOptions VolumeAverageOptions::from_toml_with_legacy(const toml::value& toml_input) { + VolumeAverageOptions options; + + // First check if we have legacy format in [Visualizations] + if (has_legacy_volume_averaging(toml_input)) { + options = parse_legacy_volume_averaging(toml_input); + } + + // Then check for modern format in [PostProcessing.volume_averages] + // Modern format takes precedence if both exist + if (toml_input.contains("PostProcessing")) { + const auto post_proc = toml::find(toml_input, "PostProcessing"); + if (post_proc.contains("volume_averages")) { + auto modern_options = VolumeAverageOptions::from_toml( + toml::find(post_proc, "volume_averages")); + // Only override legacy settings if modern ones are explicitly enabled + if (modern_options.enabled) { + options = modern_options; + } + } + } + + return options; +} + +bool VolumeAverageOptions::validate() const { + // Implement validation logic + if (!enabled) { + return true; + } + if (output_frequency < 1) { + WARNING_0_OPT("Error: VolumeAverage table did not provide a valid output frequency valid " + "as it was less than 1"); + return false; + } + return true; +} + +ProjectionOptions ProjectionOptions::from_toml(const toml::value& toml_input) { + ProjectionOptions options; + + if (toml_input.contains("enabled_projections")) { + options.enabled_projections = toml::find>(toml_input, + "enabled_projections"); + } + + if (toml_input.contains("auto_enable_compatible")) { + options.auto_enable_compatible = toml::find(toml_input, "auto_enable_compatible"); + } + + return options; +} + +bool ProjectionOptions::validate() const { + // Implement validation logic + return true; +} + +PostProcessingOptions PostProcessingOptions::from_toml(const toml::value& toml_input) { + PostProcessingOptions options; + + // Use the new legacy-aware parsing for volume averages + options.volume_averages = VolumeAverageOptions::from_toml_with_legacy(toml_input); + + // Use the new legacy-aware parsing for light-up options + options.light_up_configs = LightUpOptions::from_toml_with_legacy(toml_input); + + // Handle projections (existing code) + if (toml_input.contains("PostProcessing")) { + const auto post_proc = toml::find(toml_input, "PostProcessing"); + if (post_proc.contains("projections")) { + options.projections = ProjectionOptions::from_toml( + toml::find(post_proc, "projections")); + } + } + + return options; +} + +bool PostProcessingOptions::validate() const { + // Validate volume averages and projections + if (!volume_averages.validate()) + return false; + if (!projections.validate()) + return false; + + // Validate each light_up configuration + for (const auto& light_config : light_up_configs) { + if (!light_config.validate()) + return false; + } + return true; +} + +std::vector PostProcessingOptions::get_enabled_light_up_configs() const { + std::vector enabled_configs; + for (const auto& config : light_up_configs) { + if (config.enabled && config.region_id.has_value()) { + enabled_configs.push_back(config); + } + } + return enabled_configs; +} + +LightUpOptions* PostProcessingOptions::get_light_up_config_for_region(int region_id) { + for (auto& config : light_up_configs) { + if (config.enabled && config.region_id == region_id) { + return &config; + } + } + return nullptr; +} + +const LightUpOptions* PostProcessingOptions::get_light_up_config_for_region(int region_id) const { + for (const auto& config : light_up_configs) { + if (config.enabled && config.region_id == region_id) { + return &config; + } + } + return nullptr; +} \ No newline at end of file diff --git a/src/options/option_solvers.cpp b/src/options/option_solvers.cpp new file mode 100644 index 0000000..b5f8af7 --- /dev/null +++ b/src/options/option_solvers.cpp @@ -0,0 +1,203 @@ +#include "options/option_parser_v2.hpp" +#include "options/option_util.hpp" + +#include + +LinearSolverOptions LinearSolverOptions::from_toml(const toml::value& toml_input) { + LinearSolverOptions options; + + if (toml_input.contains("solver") || toml_input.contains("solver_type")) { + // Support both naming conventions + const auto& solver_key = toml_input.contains("solver") ? "solver" : "solver_type"; + options.solver_type = string_to_linear_solver_type( + toml::find(toml_input, solver_key)); + } + + if (toml_input.contains("preconditioner")) { + options.preconditioner = string_to_preconditioner_type( + toml::find(toml_input, "preconditioner")); + } + + if (toml_input.contains("abs_tol")) { + options.abs_tol = toml::find(toml_input, "abs_tol"); + } + + if (toml_input.contains("rel_tol")) { + options.rel_tol = toml::find(toml_input, "rel_tol"); + } + + if (toml_input.contains("max_iter") || toml_input.contains("iter")) { + // Support both naming conventions + const auto& iter_key = toml_input.contains("max_iter") ? "max_iter" : "iter"; + options.max_iter = toml::find(toml_input, iter_key); + } + + if (toml_input.contains("print_level")) { + options.print_level = toml::find(toml_input, "print_level"); + } + + return options; +} + +NonlinearSolverOptions NonlinearSolverOptions::from_toml(const toml::value& toml_input) { + NonlinearSolverOptions options; + + if (toml_input.contains("iter")) { + options.iter = toml::find(toml_input, "iter"); + } + + if (toml_input.contains("rel_tol")) { + options.rel_tol = toml::find(toml_input, "rel_tol"); + } + + if (toml_input.contains("abs_tol")) { + options.abs_tol = toml::find(toml_input, "abs_tol"); + } + + if (toml_input.contains("nl_solver")) { + options.nl_solver = string_to_nonlinear_solver_type( + toml::find(toml_input, "nl_solver")); + } + + return options; +} + +SolverOptions SolverOptions::from_toml(const toml::value& toml_input) { + SolverOptions options; + + if (toml_input.contains("assembly")) { + options.assembly = string_to_assembly_type(toml::find(toml_input, "assembly")); + } + + if (toml_input.contains("rtmodel")) { + options.rtmodel = string_to_rt_model(toml::find(toml_input, "rtmodel")); + } + + if (toml_input.contains("integ_model")) { + options.integ_model = string_to_integration_model( + toml::find(toml_input, "integ_model")); + } + + // Parse linear solver section + if (toml_input.contains("Krylov")) { + options.linear_solver = LinearSolverOptions::from_toml(toml::find(toml_input, "Krylov")); + } + + // Parse nonlinear solver section (NR = Newton-Raphson) + if (toml_input.contains("NR")) { + options.nonlinear_solver = NonlinearSolverOptions::from_toml(toml::find(toml_input, "NR")); + } + + return options; +} + +bool LinearSolverOptions::validate() const { + if (max_iter < 1) { + WARNING_0_OPT("Error: LinearSolver table did not provide a positive iteration count"); + return false; + } + + if (abs_tol < 0) { + WARNING_0_OPT("Error: LinearSolver table provided a negative absolute tolerance"); + return false; + } + + if (rel_tol < 0) { + WARNING_0_OPT("Error: LinearSolver table provided a negative relative tolerance"); + return false; + } + + if (solver_type == LinearSolverType::NOTYPE) { + WARNING_0_OPT("Error: LinearSolver table did not provide a valid solver type (CG, GMRES, " + "MINRES, or BICGSTAB)"); + return false; + } + + if (preconditioner == PreconditionerType::NOTYPE) { + WARNING_0_OPT("Error: LinearSolver table did not provide a valid preconditioner type " + "(JACOBI, AMG, ILU, L1GS, CHEBYSHEV)"); + return false; + } + + // Implement validation logic + return true; +} + +bool NonlinearSolverOptions::validate() const { + if (iter < 1) { + WARNING_0_OPT("Error: NonLinearSolver table did not provide a positive iteration count"); + return false; + } + + if (abs_tol < 0) { + WARNING_0_OPT("Error: NonLinearSolver table provided a negative absolute tolerance"); + return false; + } + + if (rel_tol < 0) { + WARNING_0_OPT("Error: NonLinearSolver table provided a negative relative tolerance"); + return false; + } + + if (nl_solver != NonlinearSolverType::NR && nl_solver != NonlinearSolverType::NRLS) { + WARNING_0_OPT("Error: NonLinearSolver table did not provide a valid nl_solver option (`NR` " + "or `NRLS`)"); + return false; + } + + // Implement validation logic + return true; +} + +bool SolverOptions::validate() { + if (!nonlinear_solver.validate()) + return false; + if (!linear_solver.validate()) + return false; + + if (assembly == AssemblyType::NOTYPE) { + WARNING_0_OPT( + "Error: Solver table did not provide a valid assembly option (`FULL`, `PA`, or `EA`)"); + return false; + } + + if (rtmodel == RTModel::NOTYPE) { + WARNING_0_OPT("Error: Solver table did not provide a valid rtmodel option (`CPU`, " + "`OPENMP`, or `GPU`)"); + return false; + } + + if (integ_model == IntegrationModel::NOTYPE) { + WARNING_0_OPT( + "Error: Solver table did not provide a valid integ_model option (`FULL` or `BBAR`)"); + return false; + } + + if (rtmodel == RTModel::GPU && assembly == AssemblyType::FULL) { + WARNING_0_OPT("Error: Solver table did not provide a valid assembly option when using GPU " + "rtmodel: `FULL` assembly can not be used with `GPU` rtmodels"); + return false; + } + + if (rtmodel == RTModel::GPU && linear_solver.preconditioner != PreconditionerType::JACOBI) { + WARNING_0_OPT("Warning: Solver table did not provide a valid preconditioner option when " + "using GPU rtmodel: `JACOBI` preconditioner is the only one that can be used " + "with `GPU` rtmodels"); + WARNING_0_OPT("Warning: Updating the preconditioner value for you to `JACOBI`"); + linear_solver.preconditioner = PreconditionerType::JACOBI; + } + + if (assembly != AssemblyType::FULL && + linear_solver.preconditioner != PreconditionerType::JACOBI) { + WARNING_0_OPT("Warning: Solver table did not provide a valid preconditioner option when " + "using either `EA` or `PA` assembly: `JACOBI` preconditioner is the only one " + "that can be used with those assembly options"); + WARNING_0_OPT("Warning: This can be a result of using legacy decks which did not have this " + "field and if so just ignore this warning."); + WARNING_0_OPT("Warning: Updating the preconditioner value for you to `JACOBI`"); + linear_solver.preconditioner = PreconditionerType::JACOBI; + } + + // Implement validation logic + return true; +} diff --git a/src/options/option_time.cpp b/src/options/option_time.cpp new file mode 100644 index 0000000..e8ac856 --- /dev/null +++ b/src/options/option_time.cpp @@ -0,0 +1,187 @@ +#include "options/option_parser_v2.hpp" +#include "options/option_util.hpp" + +// Time options nested classes implementation +TimeOptions::AutoTimeOptions +TimeOptions::AutoTimeOptions::from_toml(const toml::value& toml_input) { + AutoTimeOptions options; + + if (toml_input.contains("dt_start")) { + options.dt_start = toml::find(toml_input, "dt_start"); + } + + if (toml_input.contains("dt_min")) { + options.dt_min = toml::find(toml_input, "dt_min"); + } + + if (toml_input.contains("dt_max")) { + options.dt_max = toml::find(toml_input, "dt_max"); + } + + if (toml_input.contains("dt_scale")) { + options.dt_scale = toml::find(toml_input, "dt_scale"); + } + + if (toml_input.contains("t_final")) { + options.t_final = toml::find(toml_input, "t_final"); + } + + return options; +} + +TimeOptions::FixedTimeOptions +TimeOptions::FixedTimeOptions::from_toml(const toml::value& toml_input) { + FixedTimeOptions options; + + if (toml_input.contains("dt")) { + options.dt = toml::find(toml_input, "dt"); + } + + if (toml_input.contains("t_final")) { + options.t_final = toml::find(toml_input, "t_final"); + } + + return options; +} + +TimeOptions::CustomTimeOptions +TimeOptions::CustomTimeOptions::from_toml(const toml::value& toml_input) { + CustomTimeOptions options; + + if (toml_input.contains("nsteps")) { + options.nsteps = toml::find(toml_input, "nsteps"); + } + + if (toml_input.contains("floc")) { + options.floc = toml::find(toml_input, "floc"); + } + + return options; +} + +bool TimeOptions::CustomTimeOptions::load_custom_dt_values() { + try { + std::ifstream file(floc); + if (!file.is_open()) { + throw std::runtime_error("Cannot open file: " + floc.string()); + } + + dt_values.clear(); + double value; + while (file >> value) { + if (value <= 0) { + WARNING_0_OPT("Error: `Time.Custom` file had value less than 0"); + return false; + } + dt_values.push_back(value); + } + if (dt_values.size() >= static_cast(nsteps)) { + dt_values.resize(static_cast(nsteps)); + return true; + } else { + std::ostringstream err; + err << "Error: `Time.Custom` floc: " << floc << " provided does not contain " + << std::to_string(nsteps) + << " steps but rather: " << std::to_string(dt_values.size()); + WARNING_0_OPT(err.str()); + return false; + } + } catch (...) { + return false; + } +} + +void TimeOptions::determine_time_type() { + if (custom_time.has_value()) { + time_type = TimeStepType::CUSTOM; + } else if (auto_time.has_value()) { + time_type = TimeStepType::AUTO; + } else if (fixed_time.has_value()) { + time_type = TimeStepType::FIXED; + } else { + // Default to fixed with defaults + fixed_time = FixedTimeOptions{}; + time_type = TimeStepType::FIXED; + } +} + +TimeOptions TimeOptions::from_toml(const toml::value& toml_input) { + TimeOptions options; + + // Check for restart options + if (toml_input.contains("restart")) { + options.restart = toml::find(toml_input, "restart"); + } + + if (toml_input.contains("restart_time")) { + options.restart_time = toml::find(toml_input, "restart_time"); + } + + if (toml_input.contains("restart_cycle")) { + options.restart_cycle = toml::find(toml_input, "restart_cycle"); + } + + // Check for nested time stepping sections + if (toml_input.contains("Auto")) { + options.auto_time = AutoTimeOptions::from_toml(toml::find(toml_input, "Auto")); + } + + if (toml_input.contains("Fixed")) { + options.fixed_time = FixedTimeOptions::from_toml(toml::find(toml_input, "Fixed")); + } + + if (toml_input.contains("Custom")) { + options.custom_time = CustomTimeOptions::from_toml(toml::find(toml_input, "Custom")); + } + + // Determine which time stepping mode to use + options.determine_time_type(); + + return options; +} + +bool TimeOptions::validate() { + switch (time_type) { + case TimeStepType::CUSTOM: { + if (!custom_time.has_value()) { + return false; + } + return custom_time->load_custom_dt_values(); + } + case TimeStepType::AUTO: { + if (!auto_time.has_value()) { + return false; + } + const bool auto_time_good = auto_time->dt_min > 0.0 && auto_time->dt_start > 0.0 && + auto_time->dt_max > auto_time->dt_min && + auto_time->dt_scale > 0.0 && auto_time->dt_scale < 1.0 && + auto_time->t_final >= auto_time->dt_start; + if (!auto_time_good) { + std::ostringstream err; + err << "Error: Time.Auto had issues make sure it satisfies the following conditions:" + << std::endl; + err << " dt_min > 0.0; dt_start > 0.0; dt_max > dt_min;" << std::endl; + err << " dt_scale > 0.0; dt_scale < 1.0; t_final >= dt_start"; + WARNING_0_OPT(err.str()); + } + return auto_time_good; + } + case TimeStepType::FIXED: { + if (!fixed_time.has_value()) { + return false; + } + const bool fixed_time_good = fixed_time->dt > 0.0 && fixed_time->t_final >= fixed_time->dt; + if (!fixed_time_good) { + std::ostringstream err; + err << "Error: Time.Fixed had issues make sure it satisfies the following conditions:" + << std::endl; + err << " dt > 0.0; t_final > dt" << std::endl; + WARNING_0_OPT(err.str()); + } + return fixed_time_good; + } + default: + return false; + } + return true; +} diff --git a/src/options/option_util.hpp b/src/options/option_util.hpp new file mode 100644 index 0000000..d12cca1 --- /dev/null +++ b/src/options/option_util.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "mpi.h" + +/** + * Macro to simplify warning's so it only does it on Rank 0 and nowhere else + */ +#define WARNING_0_OPT(...) \ + { \ + int mpi_rank; \ + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); \ + if (mpi_rank == 0) { \ + std::cerr << (__VA_ARGS__) << std::endl; \ + } \ + } + +#define INFO_0_OPT(...) \ + { \ + int mpi_rank; \ + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); \ + if (mpi_rank == 0) { \ + std::cout << (__VA_ARGS__) << std::endl; \ + } \ + } + +/** + * @brief Convert string to enum with validation and error handling + * + * @tparam EnumType The enum type to convert to + * @param str String value to convert from configuration file + * @param mapping Map from string values to corresponding enum values + * @param default_value Default enum value to return if string not found in mapping + * @param enum_name Descriptive name of enum type for error message reporting + * + * @return EnumType value corresponding to input string, or default_value if not found + * + * @details This template function provides a unified way to convert string values + * from TOML configuration files to strongly-typed enum values. If the input string + * is not found in the mapping, a warning is printed and the default value is returned. + */ +template +inline EnumType string_to_enum(const std::string& str, + const std::map& mapping, + EnumType default_value, + const std::string& enum_name) { + auto it = mapping.find(str); + if (it != mapping.end()) { + return it->second; + } + std::ostringstream err; + err << "Warning: Unknown " << enum_name << " type '" << str << "', using default."; + WARNING_0_OPT(err.str()); + return default_value; +} + +/** + * @brief Load vector of double values from a text file + * + * @param filename Path to file containing whitespace-separated numeric values + * @param expected_size Expected number of values to read (0 = no size check) + * + * @return Vector of double values loaded from file + * + * @throws std::runtime_error if file cannot be opened for reading + * + * @details This function reads numeric values from a text file where values are + * separated by whitespace (spaces, tabs, newlines). If expected_size > 0 and the + * number of values read doesn't match, a warning is printed but execution continues. + */ +inline std::vector load_vector_from_file(const std::filesystem::path& filename, + int expected_size) { + std::vector result; + std::ifstream file(filename); + + if (!file.is_open()) { + throw std::runtime_error("Cannot open file: " + filename.string()); + } + + double value; + while (file >> value) { + result.push_back(value); + } + + if (expected_size > 0 && result.size() != static_cast(expected_size)) { + std::ostringstream err; + err << "Warning: File " << filename << " contains " << result.size() << " values, but " + << expected_size << " were expected."; + WARNING_0_OPT(err.str()); + } + + return result; +} diff --git a/src/options_v08.toml b/src/options_v08.toml new file mode 100644 index 0000000..48ee233 --- /dev/null +++ b/src/options_v08.toml @@ -0,0 +1,372 @@ +# The below shows all of the options available and their default values. +# Although, it should be noted that the BCs options have no default values +# and require you to input ones that are appropriate for your problem. +# Also while the below is indented to make things easier to read the parser doesn't +# care about indentation. +# More information on TOML files can be found at: https://en.wikipedia.org/wiki/TOML +# and https://github.com/toml-lang/toml/blob/master/README.md +Version = "0.8.0" +[Properties] + # A base temperature that all models will initially run at + temperature = 298 + # The below informs us about the material properties to use + [Properties.Matl_Props] + floc = "props.txt" + num_props = 1 + # These options tell inform the program about the state variables + [Properties.State_Vars] + floc = "state.txt" + num_vars = 1 + # These options are only used in xtal plasticity problems + [Properties.Grain] + # Tells us where the orientations are located for either a UMAT or + # ExaCMech problem. -1 indicates that it goes at the end of the state + # variable file. + # If ExaCMech is used the loc value will be overriden with values that are + # consistent with the library's expected location + ori_state_var_loc = -1 + # Optional - number of values associated with a custom orientation + ori_stride = 0 + # Required - the following options are available for orientation type: euler, quat/quaternion, or custom. + # If one of these options is not provided the program will exit early. + # ExaCMech expects everything to be quaternions + ori_type = "euler" + # Required - number of grains / unique orientations within an orientation file + num_grains = 0 + # Required - orientation file name + ori_floc = "ori.txt" + # If auto generating a mesh a grain file is needed that associates a given + # element to a grain. If you are using a mesh file this information should + # already be embedded in the mesh using something akin to the MFEM v1.0 mesh + # file element attributes, and therefore this option is ignored. + grain_floc = "grain_map.txt" +[BCs] + # Optional - tells the program that we'll have changing BCs + # Note: This option is currently not compatible with auto time stepping + changing_ess_bcs = false + + # The below gives an example of how to make use of changing essential BCs + # where we have the sample cyclically loaded. + # It would go in tandem with using the fixed time stepper with a dt = 0.1 + # and t_final = 7.0. + + # Required if changing_ess_bcs = true + # The step number that a new BC is to be applied on. + # You must always have step 1 listed here, since that is the initial + # step that any BC is applied on. + #update_steps = [1, 11, 31, 51, 71] + + # List the attributes associated with each boundary that essential + # BCs will be associated with a given update_steps index. + # It should be noted that while for this example all the attributes are the + # same through out the simulation this is not a constraint within ExaConstit. + # A user is allowed to change them to anything they like. However, users should + # still be careful when updating things. + # For example, we currently don't allow a user to say that they want the normal of a + # given boundary/plane to remain a given direction through-out deformation + # if a velocity is not supplied for that boundary. Therefore, + # Required - essential BC ids for the whole boundary for a given time step + #essential_ids = [[1, 2, 3, 4], + # [1, 2, 3, 4], + # [1, 2, 3, 4], + # [1, 2, 3, 4], + # [1, 2, 3, 4]] + # Required - component combo (x,y,z = -1, x = 1, y = 2, z = 3, xy = 4, yz = 5, xz = 6, free = 0) + # These numbers tell us which degrees of freedom are constrained for the given + # list of attributes provided within essential_ids + #essential_comps = [[3, 1, 2, 3], + # [3, 1, 2, 3], + # [3, 1, 2, 3], + # [3, 1, 2, 3], + # [3, 1, 2, 3]] + # Required - Vector of vals to be applied for each attribute + # The length of this should be #ids * dim of problem + #essential_vals = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001], + # [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.001], + # [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001], + # [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.001], + # [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001]] + + # The below shows how to set up the essential BCs for just a simple + # non-changing BCs example. In this case, the sample will be deformed under + # monotonic loading. + + # Required - essential BC ids for the whole boundary + essential_ids = [1, 2, 3, 4] + # Required = component combo (free = 0, x = 1, y = 2, z = 3, xy = 4, yz = 5, xz = 6, xyz = 7) + # Note: ExaConstit v0.5.0 and earlier had xyz set to -1. This change was broken in v0.6.0 + # These numbers tell us which degrees of freedom are constrained for the given + # list of attributes provided within essential_ids + # Negative values of the below signify that for a given essential BC id that + # we want to use a constant velocity gradient rather than directly supplying the + # velocity values. + essential_comps = [3, 1, 2, 3] + # Optional - Vector of vals to be applied for each attribute + # The length of this should be #ids * dim of problem + # This is required if constant strain rate is set to false + essential_vals = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001] + # Optional - Velocity gradient to be applied on the essential components and boundary IDs + # The length of this should be a 3x3 matrix as given below. + # As an example, we're supplying a matrix that should equate to the same uni-axial loading + # condition as up above + # This is needed to ensure a constant strain rate on the problem + essential_vel_grad = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.001]] + + # Optional - Point of origin used for the constant velocity gradient calculations + # The point of origin by default is calculated as the minimum x, y, z values + # in a mesh as of the beginning time step. If this is value variable is not + # provided / defined then the default value is used instead. + vgrad_origin = [0.0, 0.0, 0.0] + + # Required if changing_ess_bcs = true + # The step number that a new BC is to be applied on. + # You must always have step 1 listed here, since that is the initial + # step that any BC is applied on. + update_steps = [1, 11, 31, 51, 71] +[Model] + # Required - this option tells us to run using a UMAT or exacmech model + # Available options are either umat or exacmech + mech_type = "" + # This tells us that our model is a crystal plasticity problem + # If you are using exacmech in mech_type then this must be true + cp = false + # If ExaCMech models are being used the following options are + # needed + [Model.ExaCMech] + # Required as of v0.8.0 + # - the shortcut model name as defined by ExaCMech here: + # https://github.com/LLNL/ExaCMech/tree/v0.4.1/src/ecmech/cases + # Ge + # For example: + # FCC shortcut names: + # https://github.com/LLNL/ExaCMech/blob/v0.4.1/src/ecmech/cases/ECMech_cases_fcc.cxx#L14-L25 + # BCC shortcut names: + # https://github.com/LLNL/ExaCMech/blob/v0.4.1/src/ecmech/cases/ECMech_cases_bcc.cxx#L13-L15 + # HCP shortcut names: + # https://github.com/LLNL/ExaCMech/blob/v0.4.1/src/ecmech/cases/ECMech_cases_hcp.cxx#L10-L13 + shortcut = "" + # Example of an FCC Voce hardening model with linear hardening is: + # shortcut = "evptn_FCC_A" + # For conversion to the legacy way of doing it: + # "evptn_" is always required at the start of the shortcut name + # FCC, BCC, or HCP would then go next to denote the crystal symmetry + # Then the last set of letters denote the hardening and slip kinitics model + # For example an FCC voce hardening model with slip kinetics is: + # evptn_FCC_A + # where for BCC and FCC materials: + # A would refer to the voce with linear hardening and power law slip kinetics + # AH would refer to the voce with nonlinear hardening and power law slip kinetics + # B would refer to the below MTSDD model + # C and above for BCC materials are new advance models + # For HCP materials: + # A would refer to the MTSDD model as we don't + # have any other models supported for HCP at this time + # + # These options are now all legacy options as of v0.8.0 + # You can still use them but the shortcut key is the preferred method + # All of these are required if you make use of the legacy terms and not the shortcut + # Required - the xtal symmetry type + # FCC, BCC, and HCP are supported crystal types + xtal_type = "" + # Required - the slip kinetics and hardening form that we're going to be using + # The choices are either PowerVoce, PowerVoceNL, or MTSDD + # HCP is only available with MTSDD + slip_type = "" +# Options related to our time steps +# For the time options if all three or some combination of the following tables +# [Auto, Fixed, and Custom] are provided the priority of which one goes +# 1. Custom +# 2. Auto +# 3. Fixed +# +# Note: For fixed and auto time steppings the final simulation step is satified if +# abs(t_final - t_current) < abs(1e-3 * dt_current) +# Generally, the simulation driver will try to satisfy this to even tighter bounds +# but that is not always possible. +[Time] + # This provides an automatic time-stepping algorithm for use cases where a fixed time + # step provides to large of an overload and custom dt is too tough. + # It's tunable so that one can change the scaling factor and minimum dt step size. + # This algorithm works is fairly simple and works as follows: + ## If the nonlinear solver fails cut initial time step by Time.Auto.dt_scale + ## and retry solve again (can repeat this process max 2 times) + ## current time step dt and time value updated if a failure occurs + # + ## Successful nonlinear solves and outputs dt value to auto_dt_out.txt + ## and then update dt by doing follows + ## n_newton_iteration_const is a constant value which determines how our + ## dt scale. It's designed in such a way such that as the number of iterations + ## taken by the nonlinear solver approaches Solvers.NR.iter the scaling of dt + ## goes to Time.Auto.dt_scale. If the number of iterations taken by the nonlinear + ## solver is less than this constant then dt will increase in value. + ## To summarize the earlier block of text, + ## dt_scaling > 1 when (n_newton_iteration_taken < n_newton_iteration_const) + ## dt_scaling approaches Time.Auto.dt_scale as n_newton_iteration_taken approaches + # n_newton_iteration_const = Time.Auto.dt_scale * Solvers.NR.iter + # n_newton_iteration_taken = number of nonlinear solver step's taken + # dt_scaling = n_newton_iteration_taken / n_newton_iteration_const + ## Solvers.NR.iter + # dt_new = dt_scaling * dt_old + # if (dt_new < Time.Auto.dt_min) then dt_new = Time.Auto.dt_min + [Time.Auto] + # Initial time step size for the problem + # default value: 1.0 + dt_start = 0.1 + # Minimum time step size that we want allowable for problem + # Note: You don't want to set this too small as this will affect + # how quickly the problem can back out. + # default value: 1.0 + dt_min = 0.05 + # Maximum time step size that we want allowable for problem + # Note: You don't want to set this too small as this will affect + # how quickly the problem can back out. + # default value: F64::max() + dt_max = 1e9 + # dt scaling factor as used in algorithm discussed above + # Note: This scaling factor needs to be between 0 and 1 + # default value: 0.25 + dt_scale = 0.25 + # Final time step value that we are aiming to reach + # default value: 1.0 + t_final = 1.0 + # File name for the outputted time step value for each time step + # default value: "auto_dt_out.txt" + auto_dt_file = "auto_dt_out.txt" + # This field is used for when there are constant/fixed dt through-out the simulation + [Time.Fixed] + # Fixed time step we are taking + # default value: 1.0 + dt = 1.0 + # Final time step value that we are aiming to reach + # default value: 1.0 + t_final = 1.0 + [Time.Custom] + # Number of time steps for the simulation + nsteps = 1 + # File name that contains the dt to be applied for each time step + floc = "custom_dt.txt" +# Our visualizations options +[Visualizations] + # The stride that we want to use for when to take save off data for visualizations + steps = 1 + # We can save things off either using the MFEM's visit data format, + # the binary conduit data format, or the paraview binary data format. + # If you are running larger simulations it is recommended to use the conduit, paraview, or adios2 + # data formats to reduce the number of files created. If paraview is used make sure + # to have MFEM compiled with zlib so you're base64 binary data file is compressed. + visit = false + conduit = false + paraview = false + adios2 = false + # The folder or filename that we want the above visualization / post-processing + # files to be saved off to + floc = "results/exaconstit" + # Optional - the file name for our average stress file + avg_stress_fname = "avg_stress.txt" + # Optional - additional volume averages or body values are calculated + # these values include the average deformation gradient and if a + # ExaCMech model is being used the plastic work is also calculated + # Default value is set to false + additional_avgs = false + # Optional - the file name for our average deformation gradient file + avg_def_grad_fname = "avg_def_grad.txt" + # Optional - the file name for our plastic work file + avg_pl_work_fname = "avg_pl_work.txt" + # Optional - the file name for our average eulerian strain file + avg_euler_strain_fname = "avg_euler_strain.txt" + # Optional - the file name for our average equivalent plastic strain file + avg_eps_fname = "avg_eps.txt" + # Options to drive light_up type calculations in-situ in the code + light_up = false + # What HKL planes we want to do the light_up measurements on + light_up_hkl = [[1.0, 1.0, 1.0], + [2.0, 0.0, 0.0], + [2.0, 2.0, 0.0], + [3.0, 1.0, 1.0]] + # What tolerance in radians we want our measurements to be within for a given fiber + light_dist_tol = 0.0873 + # What our sample direction is for things + light_s_dir = [0.0, 0.0, 1.0] + # What our lattice spacing parameters are + lattice_params = [3.60, 3.60, 3.60] + lattice_basename = "lattice_avg_" +[Solvers] + # Option for how our assembly operation is conducted. Possible choices are + # FULL, PA, EA + # Full assembly fully assembles the stiffness matrix + # Partial assembly is completely matrix free and only performs the action of + # the stiffness matrix. + # Element assembly only assembles the elemental contributions to the stiffness + # matrix in order to perform the actions of the overall matrix. + assembly = "FULL" + # Option for what our runtime is set to. Possible choices are CPU, OPENMP, or GPU + # Note that GPU replaced CUDA as on v0.7.0 of ExaConstit + rtmodel = "CPU" + # Option for determining whether we do full integration for our quadrature scheme + # or we do a BBar scheme where the volume contribution is an element average. + # Possible choices are FULL or BBAR + integ_model = "FULL" + # Options for our nonlinear solver + # The number of iterations should probably be low + # Some problems might have difficulty converging so you might need to relax + # the default tolerances + [Solvers.NR] + iter = 25 + # The relative tolerance that can determines when our NR can exit if the + # abs_tol isn't reached first + rel_tol = 1e-5 + # The absolute tolerance that can determines when our NR can exit if the + # rel_tol isn't reached first + abs_tol = 1e-10 + # The below option decides what nonlinear solver to use. + # Possible options are either "NR" (Newton Raphson) or "NRLS" (Newton Raphson + # with a line search) + nl_solver = "NR" + # Options for our iterative linear solver + # A lot of times the iterative solver converges fairly quickly to a solved value + # However, the solvers could at worst take DOFs iterations to converge. In most of these + # solid mechanics problems that almost never occcurs unless the mesh is incredibly coarse. + [Solvers.Krylov] + # The number of iterations our Krylov solver may take before exiting + # 200 might be a bit low if you're using either the PA or EA assembly options + # You might want to then increase this to 1000-5000 iterations depending on + # your problem size + iter = 200 + # The relative tolerance that can determines when our kryloc can exit if the + # abs_tol isn't reached first + # It's possible to get away with smaller values here such as 1e-7 instead of + # the default value shown down below. + rel_tol = 1e-10 + # The absolute tolerance that can determines when our kryloc can exit if the + # rel_tol isn't reached first + # It's possible to get away with smaller values here such as 1e-27 instead of + # the default value shown down below. + abs_tol = 1e-30 + # The following Krylov solvers are available GMRES, PCG, and MINRES + # If you're stiffness matrix is known to be symmetric, such as what's the case + # with the current ExaCMech formulations, you should use the PCG solver instead + solver = "GMRES" +[Mesh] + # Serial uniform refinement level + ref_ser = 0 + # Parallel uniform refinement level + ref_par = 0 + # The polynomial order of our shape functions + # Note this used to be prefinement + p_refinement = 1 + # The location of our mesh + # If MFEM was compiled with MFEM_USE_ZLIB then this file may also be a + # a gzip file so *.gz file. + floc = "../../data/cube-hex-ro.mesh" + # Possible values here are cubit, auto, or other + # If one of these is not provided the program will exit early + type = "other" + # The below shows the necessary options needed to automatically generate a mesh + # This section is ignored if auto wasn't used for the type + [Mesh.Auto] + # Required - the mesh length in each direction + length = [1.0, 1.0, 1.0] + # Required - the number of cuts along each edge of the mesh are also needed + ncuts = [1, 1, 1] + + diff --git a/src/postprocessing/mechanics_lightup.cpp b/src/postprocessing/mechanics_lightup.cpp new file mode 100644 index 0000000..7135273 --- /dev/null +++ b/src/postprocessing/mechanics_lightup.cpp @@ -0,0 +1,809 @@ +#include "postprocessing/mechanics_lightup.hpp" + +#include "utilities/mechanics_kernels.hpp" +#include "utilities/rotations.hpp" + +#include "ECMech_const.h" +#include "ECMech_gpu_portability.h" +#include "SNLS_linalg.h" +#include "mfem/general/forall.hpp" + +/** + * @brief Type trait for detecting std::array types + * + * Helper template for template metaprogramming to distinguish + * std::array types from other types in generic printing functions. + */ +namespace no_std { +template +struct IsStdArray : std::false_type {}; +template +struct IsStdArray> : std::true_type {}; +} // namespace no_std + +/** + * @brief Print std::array to output stream with formatting + * + * @tparam T Array element type + * @tparam N Array size + * @param stream Output stream for writing + * @param array Array to print + * + * Formats std::array output as "[ val1, val2, val3 ]" with scientific + * notation and 6-digit precision. Used for consistent formatting of + * HKL directions and other array data in output files. + */ +template +void printArray(std::ostream& stream, std::array& array) { + stream << "\"[ "; + for (size_t i = 0; i < N - 1; i++) { + stream << std::scientific << std::setprecision(6) << array[i] << ","; + } + stream << array[N - 1] << " ]\"\t"; +} + +/** + * @brief Generic value printing with type-specific formatting + * + * @tparam T Value type + * @param stream Output stream for writing + * @param t Value to print + * + * Prints values with appropriate formatting based on type: + * - std::array types use printArray() for structured output + * - Other types use scientific notation with 6-digit precision + * + * Enables generic output formatting for different data types + * in LightUp file output operations. + */ +template +void printValues(std::ostream& stream, T& t) { + if constexpr (no_std::IsStdArray::value) { + printArray(stream, t); + } else { + stream << std::scientific << std::setprecision(6) << t << "\t"; + } +} + +/** + * @brief Generate region-specific lattice output basename + * + * @param lattice_basename Base filename from configuration + * @param region_id Region identifier + * @return Region-specific filename prefix + * + * Constructs unique output file basename by appending region identifier. + * Format: "basename_region_N_" where N is the region ID. Ensures + * separate output files for each material region in multi-region simulations. + */ +std::string get_lattice_basename(const fs::path& lattice_basename, const int region_id) { + return lattice_basename.string() + "region_" + std::to_string(region_id) + "_"; +} + +/** + * @brief Get crystallographic point group symmetry operations + * + * @param lattice_type Crystal system type specifying the point group + * @return Vector of quaternions representing symmetry operations + * + * Returns the complete set of point group symmetry operations for the + * specified crystal system as quaternions in the form [angle, x, y, z]. + * Each quaternion represents a rotation operation that maps crystallographic + * directions to their symmetrically equivalent counterparts. + * + * The number and type of symmetry operations depend on the crystal system: + * - Cubic: 24 operations (identity, 3-fold, 4-fold, 2-fold rotations) + * - Hexagonal: 12 operations (identity, 6-fold, 3-fold, 2-fold rotations) + * - Trigonal: 6 operations (identity, 3-fold, 2-fold rotations) + * - Rhombohedral: 6 operations (identity, 3-fold about [111], 2-fold perpendicular to [111]) + * - Tetragonal: 8 operations (identity, 4-fold, 2-fold rotations) + * - Orthorhombic: 4 operations (identity, three 2-fold rotations) + * - Monoclinic: 2 operations (identity, one 2-fold rotation) + * - Triclinic: 1 operation (identity only) + * + * These symmetry operations are used by LatticeTypeGeneral to generate + * symmetrically equivalent HKL directions for lattice strain calculations + * in powder diffraction simulations. + * + * @note Quaternions use the convention [angle, axis_x, axis_y, axis_z] + * where angle is in radians and the axis is normalized. + */ +std::vector> GetSymmetryGroups(const LatticeType& lattice_type) { + // If not mentioned specifically these are taken from: + // https://github.com/HEXRD/hexrd/blob/3060f506148ee29ef561c48c3331238e41fb928e/hexrd/rotations.py#L1327-L1514 + constexpr double PI = 3.14159265358979323846264338327950288; + constexpr double FRAC_PI_2 = 1.57079632679489661923132169163975144; + constexpr double FRAC_PI_3 = 1.04719755119659774615421446109316763; + const double SQRT3_2 = std::sqrt(3.0) / 2.0; + const double ISQRT6 = 1.0 / std::sqrt(6.0); + + std::vector> lattice_symm; + switch (lattice_type) { + case LatticeType::CUBIC: { + lattice_symm = { + {0.0, 1.0, 0.0, 0.0}, // identity + {FRAC_PI_2, 1.0, 0.0, 0.0}, // fourfold about 1 0 0 (x1) + {PI, 1.0, 0.0, 0.0}, // + {FRAC_PI_2 * 3.0, 1.0, 0.0, 0.0}, // + {FRAC_PI_2, 0.0, 1.0, 0.0}, // fourfold about 0 1 0 (x2) + {PI, 0.0, 1.0, 0.0}, // + {FRAC_PI_2 * 3.0, 0.0, 1.0, 0.0}, // + {FRAC_PI_2, 0.0, 0.0, 1.0}, // fourfold about 0 0 1 (x3) + {PI, 0.0, 0.0, 1.0}, // + {FRAC_PI_2 * 3.0, 0.0, 0.0, 1.0}, // + {FRAC_PI_3 * 2.0, 1.0, 1.0, 1.0}, // threefold about 1 1 1 + {FRAC_PI_3 * 4.0, 1.0, 1.0, 1.0}, // + {FRAC_PI_3 * 2.0, -1.0, 1.0, 1.0}, // threefold about -1 1 1 + {FRAC_PI_3 * 4.0, -1.0, 1.0, 1.0}, // + {FRAC_PI_3 * 2.0, -1.0, -1.0, 1.0}, // threefold about -1 -1 1 + {FRAC_PI_3 * 4.0, -1.0, -1.0, 1.0}, // + {FRAC_PI_3 * 2.0, 1.0, -1.0, 1.0}, // threefold about 1 -1 1 + {FRAC_PI_3 * 4.0, 1.0, -1.0, 1.0}, // + {PI, 1.0, 1.0, 0.0}, // twofold about 1 1 0 + {PI, -1.0, 1.0, 0.0}, // twofold about -1 1 0 + {PI, 1.0, 0.0, 1.0}, // twofold about 1 0 1 + {PI, 0.0, 1.0, 1.0}, // twofold about 0 1 1 + {PI, -1.0, 0.0, 1.0}, // twofold about -1 0 1 + {PI, 0.0, -1.0, 1.0}, // twofold about 0 -1 1 + }; + break; + } + case LatticeType::HEXAGONAL: { + lattice_symm = { + {0.0, 1.0, 0.0, 0.0}, // identity + {FRAC_PI_3, 0.0, 0.0, 1.0}, // sixfold about 0 0 1 (x3,c) + {FRAC_PI_3 * 2.0, 0.0, 0.0, 1.0}, + {PI, 0.0, 0.0, 1.0}, + {FRAC_PI_3 * 4.0, 0.0, 0.0, 1.0}, + {FRAC_PI_3 * 5.0, 0.0, 0.0, 1.0}, + {PI, 1.0, 0.0, 0.0}, // twofold about 2 -1 0 (x1,a1) + {PI, -0.5, SQRT3_2, 0.0}, // twofold about -1 2 0 (a2) + {PI, -0.5, -SQRT3_2, 0.0}, // twofold about -1 -1 0 (a3) + {PI, SQRT3_2, 0.5, 0.0}, // twofold about 1 0 0 + {PI, 0.0, 1.0, 0.0}, // twofold about -1 1 0 (x2) + {PI, -SQRT3_2, 0.5, 0.0} // twofold about 0 -1 0 + }; + break; + } + case LatticeType::TRIGONAL: { + lattice_symm = { + {0.0, 1.0, 0.0, 0.0}, // identity + {FRAC_PI_3 * 2.0, 0.0, 0.0, 1.0}, // threefold about 0001 (x3,c) + {FRAC_PI_3 * 4.0, 0.0, 0.0, 1.0}, + {PI, 1.0, 0.0, 0.0}, // twofold about 2 -1 -1 0 (x1,a1) + {PI, -0.5, SQRT3_2, 0.0}, // twofold about -1 2 -1 0 (a2) + {PI, -0.5, SQRT3_2, 0.0} // twofold about -1 -1 2 0 (a3) + }; + break; + } + case LatticeType::RHOMBOHEDRAL: { + // Claude generated these symmetry groups + lattice_symm = {{0.0, 1.0, 0.0, 0.0}, // identity + {FRAC_PI_3 * 2.0, + 1.0, + 1.0, + 1.0}, // 3-fold rotations about [111] direction (2 operations) + {FRAC_PI_3 * 4.0, 1.0, 1.0, 1.0}, + {PI, + 2.0 * ISQRT6, + -ISQRT6, + -ISQRT6}, // 2-fold rotations perpendicular to [111] (3 operations) + {PI, -ISQRT6, 2.0 * ISQRT6, -ISQRT6}, + {PI, -ISQRT6, -ISQRT6, 2.0 * ISQRT6}}; + break; + } + case LatticeType::TETRAGONAL: { + lattice_symm = { + {0.0, 1.0, 0.0, 0.0}, // identity + {FRAC_PI_2, 0.0, 0.0, 1.0}, // fourfold about 0 0 1 (x3) + {PI, 0.0, 0.0, 1.0}, + {FRAC_PI_2 * 3.0, 0.0, 0.0, 1.0}, + {PI, 1.0, 0.0, 0.0}, // twofold about 1 0 0 (x1) + {PI, 0.0, 1.0, 0.0}, // twofold about 0 1 0 (x2) + {PI, 1.0, 1.0, 0.0}, // twofold about 1 1 0 + {PI, -1.0, 1.0, 0.0} // twofold about -1 1 0 + }; + break; + } + case LatticeType::ORTHORHOMBIC: { + lattice_symm = { + {0.0, 1.0, 0.0, 0.0}, // identity + {PI, 1.0, 0.0, 0.0}, // twofold about 1 0 0 + {PI, 0.0, 1.0, 0.0}, // twofold about 0 1 0 + {PI, 0.0, 0.0, 1.0} // twofold about 0 0 1 + }; + break; + } + case LatticeType::MONOCLINIC: { + lattice_symm = { + {0.0, 1.0, 0.0, 0.0}, // identity + {PI, 0.0, 1.0, 0.0} // twofold about 010 (x2) + }; + break; + } + case LatticeType::TRICLINIC: { + lattice_symm = { + {0.0, 1.0, 0.0, 0.0} // identity + }; + break; + } + default: { + lattice_symm = { + {0.0, 1.0, 0.0, 0.0} // identity + }; + break; + } + } + return lattice_symm; +} + +/** + * @brief Get number of symmetry operations for a crystal system + * + * @param lattice_type Crystal system type specifying the point group + * @return Number of symmetry operations in the point group + * + * Returns the total number of symmetry operations for the specified + * crystal system's point group. This count includes the identity operation + * and all rotational symmetries of the crystal structure. + * + * The count varies by crystal system: + * - Cubic: 24 symmetry operations + * - Hexagonal: 12 symmetry operations + * - Trigonal: 6 symmetry operations + * - Rhombohedral: 6 symmetry operations + * - Tetragonal: 8 symmetry operations + * - Orthorhombic: 4 symmetry operations + * - Monoclinic: 2 symmetry operations + * - Triclinic: 1 symmetry operation + * + * This function is used to initialize the NSYM member variable in + * LatticeTypeGeneral and to allocate appropriate storage for + * symmetry-related calculations. + * + * @see GetSymmetryGroups() for the actual symmetry operation quaternions + */ +size_t GetNumberSymmetryOperations(const LatticeType& lattice_type) { + return GetSymmetryGroups(lattice_type).size(); +} + +LatticeTypeGeneral::LatticeTypeGeneral(const std::vector& lattice_param_a, + const LatticeType& lattice_type) + : NSYM(GetNumberSymmetryOperations(lattice_type)) { + SymmetricQuaternions(lattice_type); + ComputeLatticeBParam(lattice_param_a, lattice_type); +} + +void LatticeTypeGeneral::ComputeLatticeBParam(const std::vector& lparam_a, + const LatticeType& lattice_type) { + constexpr double FRAC_PI_2 = 1.57079632679489661923132169163975144; + constexpr double FRAC_PI_4_3 = FRAC_PI_2 * 4.0 / 3.0; + std::vector cellparms(6, 0.0); + + switch (lattice_type) { + case LatticeType::CUBIC: { + cellparms = {lparam_a[0], lparam_a[0], lparam_a[0], FRAC_PI_2, FRAC_PI_2, FRAC_PI_2}; + break; + } + case LatticeType::HEXAGONAL: + case LatticeType::TRIGONAL: { + cellparms = {lparam_a[0], lparam_a[0], lparam_a[1], FRAC_PI_2, FRAC_PI_2, FRAC_PI_4_3}; + break; + } + case LatticeType::RHOMBOHEDRAL: { + cellparms = {lparam_a[0], lparam_a[0], lparam_a[0], lparam_a[1], lparam_a[1], lparam_a[1]}; + break; + } + case LatticeType::TETRAGONAL: { + cellparms = {lparam_a[0], lparam_a[0], lparam_a[1], FRAC_PI_2, FRAC_PI_2, FRAC_PI_2}; + break; + } + case LatticeType::ORTHORHOMBIC: { + cellparms = {lparam_a[0], lparam_a[1], lparam_a[2], FRAC_PI_2, FRAC_PI_2, FRAC_PI_2}; + break; + } + case LatticeType::MONOCLINIC: { + cellparms = {lparam_a[0], lparam_a[1], lparam_a[2], FRAC_PI_2, lparam_a[3], FRAC_PI_2}; + break; + } + case LatticeType::TRICLINIC: { + cellparms = {lparam_a[0], lparam_a[1], lparam_a[2], lparam_a[3], lparam_a[4], lparam_a[5]}; + break; + } + default: { + cellparms = {lparam_a[0], lparam_a[0], lparam_a[0], FRAC_PI_2, FRAC_PI_2, FRAC_PI_2}; + break; + } + } + + const double alfa = cellparms[3]; + const double beta = cellparms[4]; + const double gamma = cellparms[5]; + + const double cosalfar = (cos(beta) * cos(gamma) - cos(alfa)) / (sin(beta) * sin(gamma)); + const double sinalfar = sqrt(1.0 - cosalfar * cosalfar); + + const double a[3] = {cellparms[0], 0.0, 0.0}; + const double b[3] = {cellparms[1] * cos(gamma), cellparms[1] * sin(gamma), 0.0}; + const double c[3] = {cellparms[2] * cos(beta), + -cellparms[2] * cosalfar * sin(beta), + cellparms[2] * sinalfar * sin(beta)}; + + // Cell volume + double vol[3] = {}; + auto cross_prod = [&](const double* const vec1, const double* const vec2, double* const prod) { + prod[0] = vec1[1] * vec2[2] - vec1[2] * vec2[1]; + prod[1] = vec1[2] * vec2[0] - vec1[0] * vec2[2]; + prod[2] = vec1[0] * vec2[1] - vec1[1] * vec2[0]; + }; + + cross_prod(b, c, vol); + const double inv_vol = 1.0 / snls::linalg::dotProd<3>(a, vol); + + // Reciprocal lattice vectors + auto cross_prod_inv_v = + [&](const double* const vec1, const double* const vec2, double* cross_prod_v) { + cross_prod(vec1, vec2, cross_prod_v); + cross_prod_v[0] *= inv_vol; + cross_prod_v[1] *= inv_vol; + cross_prod_v[2] *= inv_vol; + }; + + double* latb[3] = {&lattice_b[0], &lattice_b[3], &lattice_b[6]}; + // B takes components in the reciprocal lattice to X + cross_prod_inv_v(b, c, latb[0]); + cross_prod_inv_v(c, a, latb[1]); + cross_prod_inv_v(a, b, latb[2]); +} + +void LatticeTypeGeneral::SymmetricQuaternions(const LatticeType& lattice_type) { + constexpr double inv2 = 1.0 / 2.0; + auto angle_axis_symm = GetSymmetryGroups(lattice_type); + + for (size_t isym = 0; isym < NSYM * 4; isym++) { + quat_symm.push_back(0.0); + } + + for (size_t isym = 0; isym < NSYM; isym++) { + double* symm_quat = &quat_symm[isym * 4]; + const double s = sin(inv2 * angle_axis_symm[isym][0]); + symm_quat[0] = cos(inv2 * angle_axis_symm[isym][0]); + double inv_norm_axis = 1.0 / snls::linalg::norm<3>(&angle_axis_symm[isym][1]); + symm_quat[1] = s * angle_axis_symm[isym][1] * inv_norm_axis; + symm_quat[2] = s * angle_axis_symm[isym][2] * inv_norm_axis; + symm_quat[3] = s * angle_axis_symm[isym][3] * inv_norm_axis; + + inv_norm_axis = 1.0; + if (symm_quat[0] < 0.0) { + inv_norm_axis *= -1.0; + } + + symm_quat[0] *= inv_norm_axis; + symm_quat[1] *= inv_norm_axis; + symm_quat[2] *= inv_norm_axis; + symm_quat[3] *= inv_norm_axis; + } +} + +LightUp::LightUp(const std::vector>& hkls, + const double distance_tolerance, + const std::array s_dir, + std::shared_ptr qspace, + const std::shared_ptr sim_state, + const int region, + const RTModel& rtmodel, + const fs::path& lattice_basename, + const std::vector& lattice_params, + const LatticeType& lattice_type) + : m_hkls(hkls), m_distance_tolerance(distance_tolerance), + m_npts(static_cast(qspace->GetSize())), m_class_device(rtmodel), + m_sim_state(sim_state), m_region(region), + m_lattice_basename(get_lattice_basename(lattice_basename, region)), + m_lattice(lattice_params, lattice_type), m_workspace(qspace, 3) { + m_s_dir[0] = s_dir[0]; + m_s_dir[1] = s_dir[1]; + m_s_dir[2] = s_dir[2]; + + const double inv_s_norm = 1.0 / snls::linalg::norm<3>(m_s_dir); + m_s_dir[0] *= inv_s_norm; + m_s_dir[1] *= inv_s_norm; + m_s_dir[2] *= inv_s_norm; + + auto lat_vec_ops_b = m_lattice.lattice_b; + // First one we'll always set to be all the values + m_in_fibers.push_back(mfem::Array(static_cast(m_npts))); + for (auto& hkl : hkls) { + m_in_fibers.push_back(mfem::Array(static_cast(m_npts))); + // Computes reciprocal lattice B but different from HEXRD we return as row matrix as that's + // the easiest way of doing things + double c_dir[3]; + // compute crystal direction from planeData + snls::linalg::matTVecMult<3, 3>(lat_vec_ops_b, hkl.data(), c_dir); + + const double inv_c_norm = 1.0 / snls::linalg::norm<3>(c_dir); + c_dir[0] *= inv_c_norm; + c_dir[1] *= inv_c_norm; + c_dir[2] *= inv_c_norm; + + // Could maybe move this over to a vec if we want this to be easily generic over a ton of + // symmetry conditions... + std::vector> rmat_fr_qsym_c_dir; + mfem::Vector tmp(static_cast(m_lattice.NSYM) * 3); + for (size_t isym = 0; isym < m_lattice.NSYM; isym++) { + rmat_fr_qsym_c_dir.push_back({0.0, 0.0, 0.0}); + double rmat[3 * 3] = {}; + Quat2RMat(&m_lattice.quat_symm[isym * 4], rmat); + snls::linalg::matTVecMult<3, 3>(rmat, c_dir, rmat_fr_qsym_c_dir[isym].data()); + const int offset = static_cast(isym * 3); + tmp(offset + 0) = rmat_fr_qsym_c_dir[isym][0]; + tmp(offset + 1) = rmat_fr_qsym_c_dir[isym][1]; + tmp(offset + 2) = rmat_fr_qsym_c_dir[isym][2]; + } + tmp.UseDevice(true); + m_rmat_fr_qsym_c_dir.push_back(tmp); + } + + m_hkls.insert(m_hkls.begin(), {0.0, 0.0, 0.0}); + int my_id; + MPI_Comm_rank(MPI_COMM_WORLD, &my_id); + // Now we're going to save off the lattice values to a file + if (my_id == 0) { + auto file_line_print = [&](auto& basename, auto& name, auto& hkls) { + fs::path filename = basename; + filename += name; + std::ofstream file; + file.open(filename, std::ios_base::out); + + file << "#" << "\t"; + + for (auto& item : hkls) { + file << std::setprecision(1) << "\"[ " << item[0] << ", " << item[1] << ", " + << item[2] << " ]\"" << "\t"; + } + file << std::endl; + + file.close(); + }; + + file_line_print(m_lattice_basename, "strains.txt", m_hkls); + file_line_print(m_lattice_basename, "volumes.txt", m_hkls); + file_line_print(m_lattice_basename, "dpeff.txt", m_hkls); + file_line_print(m_lattice_basename, "taylor_factor.txt", m_hkls); + file_line_print(m_lattice_basename, "directional_stiffness.txt", m_hkls); + } + + /* add a working array for the QF and in_fiber arrays */ + // If we really wanted to we could lower try and calculate the elements + // that aren't unique here but that's not worth the effort at all given + // how fast things are + // let c_syms: Vec<[f64; 3]> = find_unique_tolerance::(&rmat_fr_qsym_c_dir, + // f64::sqrt(f64::EPSILON)); + + // Move all of the above to the object constructor + // rmat_fr_qsym_c_dir move to an mfem vector and then use it's data down here + // same with s_dir and c_dir + // Here iterate on which HKL we're using maybe have a map for these rmat_fr_qsym_c_dir and c_dir +} + +void LightUp::CalculateLightUpData( + const std::shared_ptr history, + const std::shared_ptr stress) { + std::string s_estrain = "elastic_strain"; + std::string s_rvol = "relative_volume"; + std::string s_quats = "quats"; + std::string s_gdot = "shear_rate"; + std::string s_shrateEff = "eq_pl_strain_rate"; + + const size_t quats_offset = static_cast( + m_sim_state->GetQuadratureFunctionStatePair(s_quats, m_region).first); + const size_t strain_offset = static_cast( + m_sim_state->GetQuadratureFunctionStatePair(s_estrain, m_region).first); + const size_t rel_vol_offset = static_cast( + m_sim_state->GetQuadratureFunctionStatePair(s_rvol, m_region).first); + const size_t dpeff_offset = static_cast( + m_sim_state->GetQuadratureFunctionStatePair(s_shrateEff, m_region).first); + const size_t gdot_offset = static_cast( + m_sim_state->GetQuadratureFunctionStatePair(s_gdot, m_region).first); + const size_t gdot_length = static_cast( + m_sim_state->GetQuadratureFunctionStatePair(s_gdot, m_region).second); + + m_in_fibers[0] = true; + for (size_t ihkl = 0; ihkl < m_rmat_fr_qsym_c_dir.size(); ihkl++) { + CalculateInFibers(history, quats_offset, ihkl); + } + + std::vector lattice_strains_output; + std::vector lattice_volumes_output; + + CalcLatticeStrains(history, + strain_offset, + quats_offset, + rel_vol_offset, + lattice_strains_output, + lattice_volumes_output); + + std::vector lattice_dpeff_output; + std::vector lattice_tayfac_output; + + CalcLatticeTaylorFactorDpeff(history, + dpeff_offset, + gdot_offset, + gdot_length, + lattice_tayfac_output, + lattice_dpeff_output); + + std::vector> lattice_dir_stiff_output; + + CalcLatticeDirectionalStiffness( + history, stress, strain_offset, quats_offset, rel_vol_offset, lattice_dir_stiff_output); + + int my_id; + MPI_Comm_rank(MPI_COMM_WORLD, &my_id); + // Now we're going to save off the lattice values to a file + if (my_id == 0) { + auto file_line_print = [&](auto& basename, auto& name, auto& vec) { + fs::path filename = basename; + filename += name; + std::ofstream file; + file.open(filename, std::ios_base::app); + + for (auto& item : vec) { + printValues(file, item); + } + file << std::endl; + + file.close(); + }; + + file_line_print(m_lattice_basename, "strains.txt", lattice_strains_output); + file_line_print(m_lattice_basename, "volumes.txt", lattice_volumes_output); + file_line_print(m_lattice_basename, "dpeff.txt", lattice_dpeff_output); + file_line_print(m_lattice_basename, "taylor_factor.txt", lattice_tayfac_output); + file_line_print(m_lattice_basename, "directional_stiffness.txt", lattice_dir_stiff_output); + } +} + +void LightUp::CalculateInFibers( + const std::shared_ptr history, + const size_t quats_offset, + const size_t hkl_index) { + // Same could be said for in_fiber down here + // that way we just need to know which hkl and quats we're running with + const size_t vdim = static_cast(history->GetVDim()); + const auto history_data = history->Read(); + + // First hkl_index is always completely true so we can easily + // compute the total volume average values + auto in_fiber_view = m_in_fibers[hkl_index + 1].Write(); + auto rmat_fr_qsym_c_dir = m_rmat_fr_qsym_c_dir[hkl_index].Read(); + + mfem::Vector s_dir(3); + s_dir[0] = m_s_dir[0]; + s_dir[1] = m_s_dir[1]; + s_dir[2] = m_s_dir[2]; + auto s_dir_data = s_dir.Read(); + auto distance_tolerance = m_distance_tolerance; + + const size_t NSYM = m_lattice.NSYM; + + mfem::forall(static_cast(m_npts), [=] MFEM_HOST_DEVICE(int i) { + const size_t iquats = static_cast(i); + + const auto quats = &history_data[iquats * vdim + quats_offset]; + double rmat[3 * 3] = {}; + Quat2RMat(quats, rmat); + + double sine = -10; + for (size_t isym = 0; isym < NSYM; isym++) { + double prod[3] = {}; + snls::linalg::matVecMult<3, 3>(rmat, &rmat_fr_qsym_c_dir[isym * 3], prod); + double tmp = snls::linalg::dotProd<3>(s_dir_data, prod); + sine = (tmp > sine) ? tmp : sine; + } + if (fabs(sine) > 1.00000001) { + sine = (sine >= 0) ? 1.0 : -1.0; + } + in_fiber_view[iquats] = acos(sine) <= distance_tolerance; + }); +} + +void LightUp::CalcLatticeStrains( + const std::shared_ptr history, + const size_t strain_offset, + const size_t quats_offset, + const size_t rel_vol_offset, + std::vector& lattice_strains_output, + std::vector& lattice_volumes_output) { + const double project_vec[6] = {m_s_dir[0] * m_s_dir[0], + m_s_dir[1] * m_s_dir[1], + m_s_dir[2] * m_s_dir[2], + 2.0 * m_s_dir[1] * m_s_dir[2], + 2.0 * m_s_dir[0] * m_s_dir[2], + 2.0 * m_s_dir[0] * m_s_dir[1]}; + + const size_t vdim = static_cast(history->GetVDim()); + const auto history_data = history->Read(); + m_workspace = 0.0; + auto lattice_strains = m_workspace.Write(); + + // Only need to compute this once + mfem::forall(static_cast(m_npts), [=] MFEM_HOST_DEVICE(int i) { + const size_t iqpts = static_cast(i); + + const auto strain_lat = &history_data[iqpts * vdim + strain_offset]; + const auto quats = &history_data[iqpts * vdim + quats_offset]; + const auto rel_vol = history_data[iqpts * vdim + rel_vol_offset]; + + double strain[6] = {}; + { + double strainm[3 * 3] = {}; + double* strain_m[3] = {&strainm[0], &strainm[3], &strainm[6]}; + const double t1 = ecmech::sqr2i * strain_lat[0]; + const double t2 = ecmech::sqr6i * strain_lat[1]; + // + // Volume strain is ln(V^e_mean) term aka ln(relative volume) + // Our plastic deformation has a det(1) aka no change in volume change + const double elas_vol_strain = log(rel_vol); + // We output elastic strain formulation such that the relationship + // between V^e and \varepsilon is just V^e = I + \varepsilon + strain_m[0][0] = (t1 - t2) + elas_vol_strain; // 11 + strain_m[1][1] = (-t1 - t2) + elas_vol_strain; // 22 + strain_m[2][2] = ecmech::sqr2b3 * strain_lat[1] + elas_vol_strain; // 33 + strain_m[1][2] = ecmech::sqr2i * strain_lat[4]; // 23 + strain_m[2][0] = ecmech::sqr2i * strain_lat[3]; // 31 + strain_m[0][1] = ecmech::sqr2i * strain_lat[2]; // 12 + + strain_m[2][1] = strain_m[1][2]; + strain_m[0][2] = strain_m[2][0]; + strain_m[1][0] = strain_m[0][1]; + + double rmat[3 * 3] = {}; + double strain_samp[3 * 3] = {}; + + Quat2RMat(quats, rmat); + snls::linalg::rotMatrix<3, false>(strainm, rmat, strain_samp); + + strain_m[0] = &strain_samp[0]; + strain_m[1] = &strain_samp[3]; + strain_m[2] = &strain_samp[6]; + strain[0] = strain_m[0][0]; + strain[1] = strain_m[1][1]; + strain[2] = strain_m[2][2]; + strain[3] = strain_m[1][2]; + strain[4] = strain_m[0][2]; + strain[5] = strain_m[0][1]; + } + const double proj_strain = snls::linalg::dotProd<6>(project_vec, strain); + lattice_strains[iqpts] = proj_strain; + }); + + for (const auto& in_fiber_hkl : m_in_fibers) { + mfem::Vector lattice_strain_hkl(1); + auto region_comm = m_sim_state->GetRegionCommunicator(m_region); + const double lat_vol = exaconstit::kernel::ComputeVolAvgTensorFilterFromPartial( + &m_workspace, &in_fiber_hkl, lattice_strain_hkl, 1, m_class_device, region_comm); + + lattice_volumes_output.push_back(lat_vol); + lattice_strains_output.push_back(lattice_strain_hkl(0)); + } +} + +void LightUp::CalcLatticeTaylorFactorDpeff( + const std::shared_ptr history, + const size_t dpeff_offset, + const size_t gdot_offset, + const size_t gdot_length, + std::vector& lattice_tay_facs, + std::vector& lattice_dpeff) { + const size_t vdim = static_cast(history->GetVDim()); + const auto history_data = history->Read(); + m_workspace = 0.0; + auto lattice_tayfac_dpeffs = m_workspace.Write(); + + // Only need to compute this once + mfem::forall(static_cast(m_npts), [=] MFEM_HOST_DEVICE(int i) { + const size_t iqpts = static_cast(i); + + const auto dpeff = &history_data[iqpts * vdim + dpeff_offset]; + const auto gdots = &history_data[iqpts * vdim + gdot_offset]; + auto lattice_tayfac_dpeff = &lattice_tayfac_dpeffs[iqpts * 2]; + double abs_gdot = 0.0; + for (size_t islip = 0; islip < gdot_length; islip++) { + abs_gdot += fabs(gdots[islip]); + } + lattice_tayfac_dpeff[0] = (fabs(*dpeff) <= 1.0e-14) ? 0.0 : (abs_gdot / *dpeff); + lattice_tayfac_dpeff[1] = *dpeff; + }); + + for (const auto& in_fiber_hkl : m_in_fibers) { + mfem::Vector lattice_tayfac_dpeff_hkl(2); + auto region_comm = m_sim_state->GetRegionCommunicator(m_region); + [[maybe_unused]] double _ = exaconstit::kernel::ComputeVolAvgTensorFilterFromPartial( + &m_workspace, &in_fiber_hkl, lattice_tayfac_dpeff_hkl, 2, m_class_device, region_comm); + lattice_tay_facs.push_back(lattice_tayfac_dpeff_hkl(0)); + lattice_dpeff.push_back(lattice_tayfac_dpeff_hkl(1)); + } +} + +void LightUp::CalcLatticeDirectionalStiffness( + const std::shared_ptr history, + const std::shared_ptr stress, + const size_t strain_offset, + const size_t quats_offset, + const size_t rel_vol_offset, + std::vector>& lattice_dir_stiff) { + const size_t vdim = static_cast(history->GetVDim()); + const auto history_data = history->Read(); + const auto stress_data = stress->Read(); + m_workspace = 0.0; + auto lattice_directional_stiffness = m_workspace.Write(); + + // Only need to compute this once + mfem::forall(static_cast(m_npts), [=] MFEM_HOST_DEVICE(int i) { + const size_t iqpts = static_cast(i); + + const auto strain_lat = &history_data[iqpts * vdim + strain_offset]; + const auto quats = &history_data[iqpts * vdim + quats_offset]; + const auto rel_vol = history_data[iqpts * vdim + rel_vol_offset]; + const auto stress_l = &stress_data[iqpts * 6]; + auto lds = &lattice_directional_stiffness[iqpts * 3]; + + double strain[6] = {}; + { + double strainm[3 * 3] = {}; + double* strain_m[3] = {&strainm[0], &strainm[3], &strainm[6]}; + const double t1 = ecmech::sqr2i * strain_lat[0]; + const double t2 = ecmech::sqr6i * strain_lat[1]; + // + // Volume strain is ln(V^e_mean) term aka ln(relative volume) + // Our plastic deformation has a det(1) aka no change in volume change + const double elas_vol_strain = log(rel_vol); + // We output elastic strain formulation such that the relationship + // between V^e and \varepsilon is just V^e = I + \varepsilon + strain_m[0][0] = (t1 - t2) + elas_vol_strain; // 11 + strain_m[1][1] = (-t1 - t2) + elas_vol_strain; // 22 + strain_m[2][2] = ecmech::sqr2b3 * strain_lat[1] + elas_vol_strain; // 33 + strain_m[1][2] = ecmech::sqr2i * strain_lat[4]; // 23 + strain_m[2][0] = ecmech::sqr2i * strain_lat[3]; // 31 + strain_m[0][1] = ecmech::sqr2i * strain_lat[2]; // 12 + + strain_m[2][1] = strain_m[1][2]; + strain_m[0][2] = strain_m[2][0]; + strain_m[1][0] = strain_m[0][1]; + + double rmat[3 * 3] = {}; + double strain_samp[3 * 3] = {}; + + Quat2RMat(quats, rmat); + + snls::linalg::rotMatrix<3, false>(strainm, rmat, strain_samp); + + strain_m[0] = &strain_samp[0]; + strain_m[1] = &strain_samp[3]; + strain_m[2] = &strain_samp[6]; + + strain[0] = strain_m[0][0]; + strain[1] = strain_m[1][1]; + strain[2] = strain_m[2][2]; + strain[3] = strain_m[1][2]; + strain[4] = strain_m[0][2]; + strain[5] = strain_m[0][1]; + } + + for (size_t ipt = 0; ipt < 3; ipt++) { + lds[ipt] = (fabs(strain[ipt]) < 1e-12) ? 0.0 : (stress_l[ipt] / strain[ipt]); + } + }); + + for (const auto& in_fiber_hkl : m_in_fibers) { + mfem::Vector lattice_direct_stiff(3); + auto region_comm = m_sim_state->GetRegionCommunicator(m_region); + [[maybe_unused]] double _ = exaconstit::kernel::ComputeVolAvgTensorFilterFromPartial( + &m_workspace, &in_fiber_hkl, lattice_direct_stiff, 3, m_class_device, region_comm); + std::array stiff_tmp; + for (size_t ipt = 0; ipt < 3; ipt++) { + stiff_tmp[ipt] = lattice_direct_stiff(static_cast(ipt)); + } + lattice_dir_stiff.push_back(stiff_tmp); + } +} \ No newline at end of file diff --git a/src/postprocessing/mechanics_lightup.hpp b/src/postprocessing/mechanics_lightup.hpp new file mode 100644 index 0000000..bddf55b --- /dev/null +++ b/src/postprocessing/mechanics_lightup.hpp @@ -0,0 +1,477 @@ +#pragma once + +#include "mfem_expt/partial_qfunc.hpp" +#include "mfem_expt/partial_qspace.hpp" +#include "options/option_parser_v2.hpp" +#include "sim_state/simulation_state.hpp" + +#include "mfem.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +/** + * @brief General crystal lattice structure and symmetry operations + * + * Provides crystal lattice parameters, reciprocal lattice vectors, + * and symmetry operations for all eight supported lattice types and their + * corresponding Laue groups (cubic to triclinic). Used by LightUp + * for crystal-structure-specific calculations. + * + * The class computes reciprocal lattice vectors from direct lattice + * parameters and generates symmetry-equivalent directions for HKL families + * based on the appropriate point group symmetries. + * + * Supported crystal systems: + * - Cubic (24 symmetry operations) + * - Hexagonal (12 symmetry operations) + * - Trigonal (6 symmetry operations) + * - Rhombohedral (6 symmetry operations) + * - Tetragonal (8 symmetry operations) + * - Orthorhombic (4 symmetry operations) + * - Monoclinic (2 symmetry operations) + * - Triclinic (1 symmetry operation) + * + * @ingroup ExaConstit_postprocessing_lightup + */ +class LatticeTypeGeneral { +public: + /** + * @brief Number of symmetry operations for the crystal lattice + * + * Point group symmetry operations (rotations and inversions) for the + * specified crystal system. The number varies by lattice type: + * cubic (24), hexagonal (12), trigonal (6), rhombohedral (6), tetragonal (8), + * orthorhombic (4), monoclinic (2), triclinic (1). + * Used for generating symmetrically equivalent crystallographic directions. + */ + const size_t NSYM = 1; + + /** + * @brief Constructor for general crystal lattice structure + * + * @param lattice_param_a Vector of lattice parameters specific to crystal system + * @param lattice_type Crystallographic lattice type enum specifying crystal system + * + * Initializes crystal lattice structure by computing reciprocal lattice vectors + * and generating point group symmetry operations for the specified crystal system. + * + * The constructor: + * 1. Determines the number of symmetry operations for the crystal system + * 2. Generates quaternion representations of all symmetry operations + * 3. Computes reciprocal lattice parameter matrix from direct lattice parameters + * 4. Stores lattice geometry for HKL direction transformations + * + * Lattice parameter requirements by crystal system: + * - **Cubic**: a (lattice parameter) + * - **Hexagonal**: a, c (basal and c-axis parameters) + * - **Trigonal**: a, c (basal and c-axis parameters) + * - **Rhombohedral**: a, α (lattice parameter and angle in radians) + * - **Tetragonal**: a, c (basal and c-axis parameters) + * - **Orthorhombic**: a, b, c (three distinct lattice parameters) + * - **Monoclinic**: a, b, c, β (three lattice parameters and monoclinic angle in radians) + * - **Triclinic**: a, b, c, α, β, γ (three lattice parameters and three angles in radians) + * + * The reciprocal lattice matrix enables transformation of Miller indices (HKL) + * to crystallographic direction vectors, while symmetry operations generate + * equivalent directions for powder diffraction calculations in LightUp analysis. + * + * @note All angular parameters must be provided in radians + * @see SymmetricQuaternions() for details on symmetry operation generation + * @see ComputeLatticeBParam() for reciprocal lattice computation + */ + LatticeTypeGeneral(const std::vector& lattice_param_a, const LatticeType& lattice_type); + + ~LatticeTypeGeneral() = default; + + /** + * @brief Compute reciprocal lattice parameter matrix + * + * @param lparam_a Direct lattice parameters + * @param lattice_type Crystal system type + * + * Computes the reciprocal lattice vectors (lattice_b matrix) from + * direct lattice parameters for any crystal system. The method handles + * the varying number of parameters required for each system: + * cubic (a), hexagonal/trigonal (a,c), tetragonal (a,c), + * orthorhombic (a,b,c), monoclinic (a,b,c,β), triclinic (a,b,c,α,β,γ). + * The reciprocal lattice is used to transform HKL indices to direction + * vectors in reciprocal space. + */ + void ComputeLatticeBParam(const std::vector& lparam_a, const LatticeType& lattice_type); + + /** + * @brief Generate and store symmetry operation quaternions for crystal system + * + * @param lattice_type Crystal system type specifying the point group + * + * Generates the complete set of point group symmetry operations for the + * specified crystal system and stores them in the quat_symm member variable. + * Each symmetry operation is represented as a quaternion in the form + * [angle, x, y, z] where angle is the rotation angle in radians and + * [x, y, z] is the normalized rotation axis. + * + * The method: + * 1. Calls GetSymmetryGroups() to obtain symmetry operations for the crystal system + * 2. Flattens the quaternion array into the quat_symm storage vector + * 3. Stores quaternions sequentially for efficient access during calculations + * + * The number and type of symmetry operations generated depend on the crystal system: + * - Cubic: 24 quaternions (full octahedral symmetry) + * - Hexagonal: 12 quaternions (hexagonal point group) + * - Trigonal: 6 quaternions (trigonal point group) + * - Rhombohedral: 6 quaternions (rhombohedral point group) + * - Tetragonal: 8 quaternions (tetragonal point group) + * - Orthorhombic: 4 quaternions (orthogonal symmetries) + * - Monoclinic: 2 quaternions (monoclinic symmetry) + * - Triclinic: 1 quaternion (identity only) + * + * These stored quaternions are subsequently used to generate symmetrically + * equivalent HKL directions during lattice strain calculations in the + * LightUp analysis framework. + * + * @note Called automatically during LatticeTypeGeneral construction + * @see GetSymmetryGroups() for symmetry operation generation + * @see quat_symm member variable for quaternion storage + */ + void SymmetricQuaternions(const LatticeType& lattice_type); + +public: + /** + * @brief Reciprocal lattice parameter matrix + * + * 3x3 matrix containing reciprocal lattice vectors as columns. + * Used to transform HKL indices to direction vectors in reciprocal space. + * Computed from direct lattice parameters in constructor. + */ + double lattice_b[3 * 3]; + std::vector quat_symm; +}; + +/** + * @brief Lattice strain analysis class for powder diffraction simulation + * + * The LightUp class performs in-situ lattice strain calculations that simulate + * powder diffraction experiments on polycrystalline materials. It computes + * lattice strains for specified crystallographic directions (HKL) based on + * crystal orientation evolution and stress state from ExaCMech simulations. + * + * Supports all eight crystal systems (cubic, hexagonal, trigonal, rhombohedral, + * tetragonal, orthorhombic, monoclinic, triclinic) through the generalized + * LatticeTypeGeneral class which provides appropriate symmetry operations for each system. + * + * Key capabilities: + * - Lattice strain calculation for multiple HKL directions + * - Taylor factor and plastic strain rate analysis + * - Directional stiffness computation + * - Volume-weighted averaging over grains/orientations + * - Real-time output for experimental comparison + * + * The class interfaces with ExaCMech state variables including: + * - Crystal orientations (quaternions) + * - Elastic strain tensors + * - Relative volume changes + * - Plastic strain rates and slip system activities + * + * Applications: + * - Validation against in-situ diffraction experiments + * - Prediction of lattice strain evolution during deformation + * - Analysis of load partitioning between crystallographic directions + * - Study of texture effects on mechanical response + * + * @ingroup ExaConstit_postprocessing_lightup + */ +class LightUp { +public: + /** + * @brief Constructor for LightUp analysis + * + * @param hkls Vector of HKL directions for lattice strain calculation + * @param distance_tolerance Angular tolerance for fiber direction matching + * @param s_dir Sample direction vector for reference frame + * @param qspace Partial quadrature space for region-specific operations + * @param sim_state Reference to simulation state for data access + * @param region Region index for analysis + * @param rtmodel Runtime model for device execution policy + * @param lattice_basename Base filename for output files + * @param lattice_params Crystal lattice parameters [a, b, c] + * + * Initializes LightUp analysis with specified crystallographic directions + * and computational parameters. The constructor: + * 1. Normalizes the sample direction vector + * 2. Computes reciprocal lattice vectors for each HKL direction + * 3. Applies crystal symmetry operations to create equivalent directions + * 4. Initializes in-fiber boolean arrays for each HKL direction + * 5. Sets up output files with HKL direction headers + * + * The distance_tolerance parameter controls the angular tolerance for + * determining which crystal orientations are "in-fiber" for each HKL direction. + * Uses the crystal system's symmetry operations to find equivalent directions. + */ + LightUp(const std::vector>& hkls, + const double distance_tolerance, + const std::array s_dir, + std::shared_ptr qspace, + const std::shared_ptr sim_state, + const int region, + const RTModel& rtmodel, + const fs::path& lattice_basename, + const std::vector& lattice_params, + const LatticeType& lattice_type); + + ~LightUp() = default; + + /** + * @brief Main entry point for LightUp data calculation + * + * @param history State variable quadrature function containing crystal data + * @param stress Stress quadrature function for current state + * + * Orchestrates the complete LightUp analysis pipeline: + * 1. Retrieves state variable offsets for orientations, strains, and rates + * 2. Sets up in-fiber calculations for all HKL directions + * 3. Computes lattice strains, Taylor factors, and directional stiffness + * 4. Outputs results to region-specific files with MPI rank 0 handling I/O + * + * This method is called at each output timestep to maintain continuous + * lattice strain evolution tracking throughout the simulation. + */ + void CalculateLightUpData(const std::shared_ptr history, + const std::shared_ptr stress); + + /** + * @brief Determine in-fiber orientations for a specific HKL direction + * + * @param history State variable data containing crystal orientations + * @param quats_offset Offset to quaternion data in state variable array + * @param hkl_index Index of HKL direction for calculation + * + * Determines which crystal orientations are "in-fiber" (aligned within + * the distance tolerance) for the specified HKL direction. Uses the + * appropriate crystal system's symmetry operations to find the maximum + * dot product between the sample direction and all symmetrically + * equivalent HKL directions. + * + * The algorithm: + * 1. Extracts quaternion orientations for each quadrature point + * 2. Converts quaternions to rotation matrices + * 3. Applies crystal system's symmetry operations to HKL directions + * 4. Computes alignment with sample direction using all equivalent directions + * 5. Sets boolean flags for orientations within angular tolerance + */ + void CalculateInFibers(const std::shared_ptr history, + const size_t quats_offset, + const size_t hkl_index); + + /** + * @brief Calculate lattice strains with volume weighting + * + * @param history State variable data + * @param strain_offset Offset to elastic strain data + * @param quats_offset Offset to quaternion orientation data + * @param rel_vol_offset Offset to relative volume data + * @param lattice_strains_output Output vector for lattice strain results + * @param lattice_volumes_output Output vector for volume weighting data + * + * Computes lattice strains by projecting elastic strain tensors onto the + * sample direction vector. The calculation accounts for crystal rotations + * and volume changes through the deformation history. + * + * Key steps: + * 1. Constructs projection vector from normalized sample direction + * 2. Rotates elastic strain from lattice to sample coordinates + * 3. Computes strain projection along sample direction + * 4. Applies volume-weighted averaging using in-fiber filters + * + * The method outputs both strain values and corresponding volumes for + * each HKL direction and overall average. + */ + void CalcLatticeStrains(const std::shared_ptr history, + const size_t strain_offset, + const size_t quats_offset, + const size_t rel_vol_offset, + std::vector& lattice_strains_output, + std::vector& lattice_volumes_output); + + /** + * @brief Calculate Taylor factors and effective plastic strain rates + * + * @param history State variable data + * @param dpeff_offset Offset to effective plastic strain rate data + * @param gdot_offset Offset to slip system rate data + * @param gdot_length Number of slip systems + * @param lattice_tay_facs Output vector for Taylor factors + * @param lattice_dpeff Output vector for effective plastic strain rates + * + * Computes Taylor factors as the ratio of total slip system activity to + * effective plastic strain rate. Taylor factors indicate the efficiency + * of plastic deformation for different crystal orientations. + * + * The calculation: + * 1. Sums absolute values of all slip system shear rates + * 2. Divides by effective plastic strain rate (with zero-division protection) + * 3. Applies volume-weighted averaging using in-fiber filters + * + * Results provide insight into plastic anisotropy and orientation effects + * on deformation resistance in textured polycrystalline materials. + */ + void CalcLatticeTaylorFactorDpeff( + const std::shared_ptr history, + const size_t dpeff_offset, + const size_t gdot_offset, + const size_t gdot_length, + std::vector& lattice_tay_facs, + std::vector& lattice_dpeff); + + /** + * @brief Calculate directional elastic stiffness properties + * + * @param history State variable data + * @param stress Stress quadrature function data + * @param strain_offset Offset to elastic strain data + * @param quats_offset Offset to quaternion orientation data + * @param rel_vol_offset Offset to relative volume data + * @param lattice_dir_stiff Output vector for directional stiffness values + * + * Computes directional elastic stiffness by analyzing the stress-strain + * relationship along crystal directions. The method rotates both stress + * and strain tensors to crystal coordinates and computes the ratio. + * + * The algorithm: + * 1. Projects stress and strain tensors onto sample direction + * 2. Accounts for crystal orientation through rotation matrices + * 3. Computes stiffness as stress/strain ratio (with zero-strain protection) + * 4. Applies volume-weighted averaging for each HKL direction + * + * Results provide directional elastic moduli for validation against + * experimental measurements and constitutive model verification. + */ + void CalcLatticeDirectionalStiffness( + const std::shared_ptr history, + const std::shared_ptr stress, + const size_t strain_offset, + const size_t quats_offset, + const size_t rel_vol_offset, + std::vector>& lattice_dir_stiff); + /** + * @brief Get the region ID for this LightUp instance + * + * @return Region identifier + * + * Returns the material region index associated with this LightUp analysis. + * Used for accessing region-specific data and organizing multi-region output. + */ + int GetRegionID() const { + return m_region; + } + +private: + /** + * @brief Vector of HKL crystallographic directions for analysis + * + * Contains the original HKL direction vectors specified by the user. + * A [0,0,0] entry is added at the beginning during construction to + * represent the overall average (all orientations). Each direction + * represents a family of crystallographic planes for diffraction analysis. + */ + std::vector> m_hkls; + /** + * @brief Angular tolerance for in-fiber determination + * + * Maximum angular deviation (in radians) for crystal orientations + * to be considered "in-fiber" for each HKL direction. Controls the + * selectivity of orientation filtering in lattice strain calculations. + */ + const double m_distance_tolerance; + /** + * @brief Normalized sample direction vector + * + * Three-component array defining the reference direction in sample + * coordinates. Normalized during construction and used for computing + * directional projections of stress and strain tensors. + */ + double m_s_dir[3]; + /** + * @brief Number of quadrature points in the region + * + * Total number of quadrature points for the partial quadrature space. + * Used for array sizing and loop bounds in device kernels. + */ + const size_t m_npts; + /** + * @brief Runtime execution model for device portability + * + * Specifies execution policy (CPU, OpenMP, GPU) for computational kernels. + * Enables device-portable execution across different hardware architectures. + */ + const RTModel m_class_device; + /** + * @brief Reference to simulation state database + * + * Provides access to state variable mappings, quadrature functions, + * and material properties for the analysis region. + */ + const std::shared_ptr m_sim_state; + /** + * @brief Material region identifier + * + * Index of the material region being analyzed. Used to access + * region-specific state variables and organize output files. + */ + const int m_region; + /** + * @brief Region-specific output file basename + * + * Base filename for all LightUp output files including region identifier. + * Constructed using get_lattice_basename() to ensure unique naming + * across multiple regions. + */ + const fs::path m_lattice_basename; + /** + * @brief Crystal lattice structure and symmetry operations + * + * Instance of LatticeTypeGeneral containing lattice parameters, + * reciprocal lattice vectors, and point group symmetry operations + * for the specified crystal system. Provides crystal structure + * information for all supported Laue groups from cubic to triclinic. + */ + const LatticeTypeGeneral m_lattice; + /** + * @brief Workspace for temporary calculations + * + * Partial quadrature function used as temporary storage for intermediate + * calculations. Avoids repeated memory allocations and enables efficient + * device-portable computations. + */ + mfem::expt::PartialQuadratureFunction m_workspace; + /** + * @brief In-fiber boolean arrays for each HKL direction + * + * Vector of boolean arrays indicating which quadrature points have + * crystal orientations aligned with each HKL direction (within tolerance). + * First entry [0] is always true (overall average), subsequent entries + * correspond to specific HKL directions. + */ + std::vector> m_in_fibers; + /** + * @brief Rotation matrices for crystal symmetry operations + * + * Vector of MFEM vectors containing rotation matrices that transform + * HKL directions through all crystal symmetry operations of the + * lattice's point group. Each vector contains NSYM*3 values representing + * the transformed direction vectors for one HKL direction, where NSYM + * is determined by the crystal system. + */ + std::vector m_rmat_fr_qsym_c_dir; +}; \ No newline at end of file diff --git a/src/postprocessing/postprocessing_driver.cpp b/src/postprocessing/postprocessing_driver.cpp new file mode 100644 index 0000000..8212eb2 --- /dev/null +++ b/src/postprocessing/postprocessing_driver.cpp @@ -0,0 +1,1717 @@ +#include "postprocessing/postprocessing_driver.hpp" + +#include "postprocessing/mechanics_lightup.hpp" +#include "postprocessing/postprocessing_file_manager.hpp" +#include "postprocessing/projection_class.hpp" +#include "utilities/mechanics_kernels.hpp" +#include "utilities/mechanics_log.hpp" +#include "utilities/rotations.hpp" + +#include "ECMech_const.h" +#include "SNLS_linalg.h" + +#include +namespace fs = std::filesystem; + +namespace { + +/** + * @brief Generic registration template for projection types + * + * @tparam T Projection class type to register + * @param region_model_types Vector of material model types per region + * @return Vector of shared projection instances, one per region plus global + * + * Creates projection instances for all regions plus one additional + * global instance. Each projection is wrapped in a shared_ptr for + * efficient memory management and polymorphic behavior. + * + * The template design enables type-safe registration of any + * projection class derived from ProjectionBase. + */ +template +std::vector> +RegisterGeneric(const std::vector& region_model_types) { + std::vector> base; + const size_t num_regions = region_model_types.size() + 1; + for (size_t i = 0; i < num_regions; i++) { + base.emplace_back(std::make_shared()); + } + return base; +} + +/** + * @brief Register centroid projections for all regions + * + * @param region_model_types Vector of material model types per region + * @return Vector of CentroidProjection instances + * + * Creates centroid projection instances that compute geometric + * centroids of mesh elements. Compatible with all material model + * types as it depends only on mesh geometry. + */ +std::vector> +RegisterCentroid(const std::vector& region_model_types) { + return RegisterGeneric(region_model_types); +} + +/** + * @brief Register volume projections for all regions + * + * @param region_model_types Vector of material model types per region + * @return Vector of VolumeProjection instances + * + * Creates volume projection instances that compute element volumes + * from integration of geometric determinants. Provides essential + * geometric information for visualization and volume averaging. + */ +std::vector> +RegisterVolume(const std::vector& region_model_types) { + return RegisterGeneric(region_model_types); +} + +/** + * @brief Register Cauchy stress projections for all regions + * + * @param region_model_types Vector of material model types per region + * @return Vector of CauchyStressProjection instances + * + * Creates projections for full Cauchy stress tensor (6 components + * in Voigt notation). Compatible with all material models that + * provide stress state information. + */ +std::vector> +RegisterCauchyStress(const std::vector& region_model_types) { + return RegisterGeneric(region_model_types); +} + +/** + * @brief Register Von Mises stress projections for all regions + * + * @param region_model_types Vector of material model types per region + * @return Vector of VonMisesStressProjection instances + * + * Creates projections that compute Von Mises equivalent stress + * from the Cauchy stress tensor. Provides scalar stress measure + * commonly used for yield and failure analysis. + */ +std::vector> +RegisterVMStress(const std::vector& region_model_types) { + return RegisterGeneric(region_model_types); +} + +/** + * @brief Register hydrostatic stress projections for all regions + * + * @param region_model_types Vector of material model types per region + * @return Vector of HydrostaticStressProjection instances + * + * Creates projections that compute hydrostatic (mean) stress + * component. Essential for analyzing volumetric deformation + * and pressure-dependent material behavior. + */ +std::vector> +RegisterHydroStress(const std::vector& region_model_types) { + return RegisterGeneric(region_model_types); +} + +/** + * @brief Register all state variables projections for all regions + * + * @param region_model_types Vector of material model types per region + * @return Vector of AllStateVariablesProjection instances + * + * Creates projections that output all available state variables + * for debugging and detailed analysis. State variable count and + * interpretation depend on the specific material model. + */ +std::vector> +RegisterAllState(const std::vector& region_model_types) { + return RegisterGeneric(region_model_types); +} + +/** + * @brief Generic registration template for ECMech-specific projections + * + * @tparam T ECMech projection class type + * @param sim_state Reference to simulation state for state variable queries + * @param region_model_types Vector of material model types per region + * @param key State variable key name for ECMech lookup + * @return Vector of ECMech projection instances + * + * Creates ECMech-specific projections with automatic state variable + * index resolution. Non-ECMech regions receive dummy projections + * with invalid indices. The maximum state variable length across + * all regions is tracked for consistent vector dimensions. + */ +template +std::vector> +RegisterECMech(const std::shared_ptr sim_state, + const std::vector& region_model_types, + const std::string key, + const std::string display_name) { + std::vector> base; + const size_t num_regions = region_model_types.size(); + int max_length = -1; + for (size_t i = 0; i < num_regions; i++) { + if (region_model_types[i] != MechType::EXACMECH) { + // Need to do a basic guard against non-ecmech models + base.emplace_back(std::make_shared("", -1, -1, display_name)); + continue; + } + auto [index, length] = sim_state->GetQuadratureFunctionStatePair(key, static_cast(i)); + base.emplace_back(std::make_shared(key, index, length, display_name)); + max_length = (max_length < length) ? length : max_length; + } + + if (base[0]->CanAggregateGlobally()) { + base.emplace_back(std::make_shared(key, 0, max_length, display_name)); + } + + return base; +} + +/** + * @brief Register DpEff (effective plastic strain rate) projections for ExaCMech + * + * @param sim_state Reference to simulation state for state variable queries + * @param region_model_types Vector of material model types per region + * @return Vector of DpEffProjection instances + * + * Creates DpEffProjection instances for regions with ExaCMech material models. + * Uses the "eq_pl_strain_rate" state variable key to access effective plastic + * strain rate data. Non-ExaCMech regions receive dummy projections. + */ +std::vector> +RegisterDpEffProjection(const std::shared_ptr sim_state, + const std::vector& region_model_types) { + std::string key = "eq_pl_strain_rate"; + std::string display_name = "Equivalent Plastic Strain Rate"; + return RegisterECMech(sim_state, region_model_types, key, display_name); +} + +/** + * @brief Register EPS (effective plastic strain) projections for ExaCMech + * + * @param sim_state Reference to simulation state for state variable queries + * @param region_model_types Vector of material model types per region + * @return Vector of EPSProjection instances + * + * Creates EPSProjection instances for regions with ExaCMech material models. + * Uses the "eq_pl_strain" state variable key to access effective plastic + * strain data. Non-ExaCMech regions receive dummy projections. + */ +std::vector> +RegisterEPSProjection(const std::shared_ptr sim_state, + const std::vector& region_model_types) { + std::string key = "eq_pl_strain"; + std::string display_name = "Equivalent Plastic Strain"; + return RegisterECMech(sim_state, region_model_types, key, display_name); +} + +/** + * @brief Register crystal orientation projections for ExaCMech + * + * @param sim_state Reference to simulation state for state variable queries + * @param region_model_types Vector of material model types per region + * @return Vector of XtalOrientationProjection instances + * + * Creates crystal orientation projection instances using the "quats" state + * variable key to access quaternion orientation data. Only compatible with + * ExaCMech material models that provide crystal orientation information. + */ +std::vector> +RegisterXtalOriProjection(const std::shared_ptr sim_state, + const std::vector& region_model_types) { + std::string key = "quats"; + std::string display_name = "Crystal Orientations"; + return RegisterECMech( + sim_state, region_model_types, key, display_name); +} + +/** + * @brief Register elastic strain projections for ExaCMech + * + * @param sim_state Reference to simulation state for state variable queries + * @param region_model_types Vector of material model types per region + * @return Vector of ElasticStrainProjection instances + * + * Creates elastic strain projection instances using the "elastic_strain" state + * variable key. Handles coordinate transformations and tensor reconstruction + * for ExaCMech elastic strain data. + */ +std::vector> +RegisterElasticStrainProjection(const std::shared_ptr sim_state, + const std::vector& region_model_types) { + std::string key = "elastic_strain"; + std::string display_name = "Elastic Strains"; + return RegisterECMech( + sim_state, region_model_types, key, display_name); +} + +/** + * @brief Register hardness projections for ExaCMech + * + * @param sim_state Reference to simulation state for state variable queries + * @param region_model_types Vector of material model types per region + * @return Vector of HardnessProjection instances + * + * Creates hardness projection instances using the "hardness" state variable + * key. Includes post-processing to ensure non-negative hardness values + * suitable for visualization and analysis. + */ +std::vector> +RegisterHardnessProjection(const std::shared_ptr sim_state, + const std::vector& region_model_types) { + std::string key = "hardness"; + std::string display_name = "Hardness"; + return RegisterECMech(sim_state, region_model_types, key, display_name); +} + +/** + * @brief Register shear rate projections for ExaCMech + * + * @param sim_state Reference to simulation state for state variable queries + * @param region_model_types Vector of material model types per region + * @return Vector of ShearingRateProjection instances + * + * Creates shear rate projection instances using the "shear_rate" state + * variable key. Provides access to macroscopic shear rate data for + * rate-dependent analysis and deformation characterization. + */ +std::vector> +RegisterShearRateProjection(const std::shared_ptr sim_state, + const std::vector& region_model_types) { + std::string key = "shear_rate"; + std::string display_name = "Shearing Rate"; + return RegisterECMech(sim_state, region_model_types, key, display_name); +} +} // namespace + +void PostProcessingDriver::RegisterProjection(const std::string& field) { + std::vector> projection_class; + + if (field == "centroid") { + projection_class = RegisterCentroid(m_region_model_types); + } else if (field == "volume") { + projection_class = RegisterVolume(m_region_model_types); + } else if (field == "cauchy") { + projection_class = RegisterCauchyStress(m_region_model_types); + } else if (field == "von_mises") { + projection_class = RegisterVMStress(m_region_model_types); + } else if (field == "hydro") { + projection_class = RegisterHydroStress(m_region_model_types); + } else if (field == "all_state") { + projection_class = RegisterAllState(m_region_model_types); + } else if (field == "dpeff") { + projection_class = RegisterDpEffProjection(m_sim_state, m_region_model_types); + } else if (field == "eps") { + projection_class = RegisterEPSProjection(m_sim_state, m_region_model_types); + } else if (field == "xtal_ori") { + projection_class = RegisterXtalOriProjection(m_sim_state, m_region_model_types); + } else if (field == "elastic_strain") { + projection_class = RegisterElasticStrainProjection(m_sim_state, m_region_model_types); + } else if (field == "hardness") { + projection_class = RegisterHardnessProjection(m_sim_state, m_region_model_types); + } else if (field == "shear_rate") { + projection_class = RegisterShearRateProjection(m_sim_state, m_region_model_types); + } else { + return; + } + + std::string field_name = field; + std::string display_name = projection_class[0]->GetDisplayName(); + using PTMC = ProjectionTraits::ModelCompatibility; + PTMC model_compatibility = projection_class[0]->model; + bool supports_global_aggregation = projection_class[0]->CanAggregateGlobally(); + + std::vector region_enabled; + std::vector region_length; + + for (size_t i = 0; i < m_region_model_types.size(); i++) { + const auto model = m_region_model_types[i]; + const auto project_model = projection_class[i]->model; + region_length.push_back(projection_class[i]->GetVectorDimension()); + if (project_model == PTMC::EXACMECH_ONLY && model == MechType::EXACMECH) { + region_enabled.push_back(true); + } else if (project_model == PTMC::EXACMECH_ONLY && model == MechType::UMAT) { + region_enabled.push_back(false); + } else if (project_model == PTMC::UMAT_ONLY && model == MechType::EXACMECH) { + region_enabled.push_back(false); + } else if (project_model == PTMC::UMAT_ONLY && model == MechType::UMAT) { + region_enabled.push_back(true); + } else if (project_model == PTMC::ALL_MODELS) { + region_enabled.push_back(true); + } else { + region_enabled.push_back(false); + } + } + if (supports_global_aggregation) { + region_enabled.push_back(true); + region_length.push_back( + projection_class[m_region_model_types.size()]->GetVectorDimension()); + } + + // Register the projection + m_registered_projections.push_back({field_name, + display_name, + model_compatibility, + region_enabled, + projection_class, + region_length, + supports_global_aggregation}); +} + +PostProcessingDriver::PostProcessingDriver(std::shared_ptr sim_state, + ExaOptions& options) + : m_sim_state(sim_state), m_mpi_rank(0), m_num_regions(sim_state->GetNumberOfRegions()), + m_aggregation_mode(AggregationMode::BOTH), + m_enable_visualization(options.visualization.visit || options.visualization.conduit || + options.visualization.paraview || options.visualization.adios2) { + MPI_Comm_rank(MPI_COMM_WORLD, &m_mpi_rank); + + MPI_Comm_size(MPI_COMM_WORLD, &m_num_mpi_rank); + + // Initialize file manager with proper ExaOptions handling + m_file_manager = std::make_unique(options); + + // Ensure output directory exists + if (!m_file_manager->EnsureOutputDirectoryExists()) { + if (m_mpi_rank == 0) { + std::cerr << "Warning: Failed to create output directory. Volume averaging may fail." + << std::endl; + } + } + + // Initialize region-specific data structures + m_region_model_types.resize(m_num_regions); + m_region_evec.resize(m_num_regions); + + int max_vdim = 0; + // Get model types for each region + for (size_t region = 0; region < m_num_regions; ++region) { + m_region_model_types[region] = sim_state->GetRegionModelType(region); + // Initialize region-specific element average buffer + if (auto pqf = sim_state->GetQuadratureFunction("cauchy_stress_end", + static_cast(region))) { + // Find maximum vdim across all possible quadrature functions for this region + for (const auto& field_name : + {"cauchy_stress_end", "state_var_end", "von_mises", "kinetic_grads"}) { + if (auto qf = sim_state->GetQuadratureFunction(field_name, + static_cast(region))) { + max_vdim = std::max(max_vdim, qf->GetVDim()); + } + } + // Create element average buffer with maximum dimension needed + m_region_evec[region] = std::make_unique( + pqf->GetPartialSpaceShared(), max_vdim); + } + } + + // Initialize global element average buffer + auto fe_space = sim_state->GetMeshParFiniteElementSpace(); + int global_max_vdim = max_vdim; // Accommodate stress tensors and other multi-component fields + m_global_evec = std::make_unique(global_max_vdim * fe_space->GetNE()); + m_global_evec->UseDevice(true); + + // Register default projections and volume calculations + RegisterDefaultVolumeCalculations(); + + // Initialize grid functions and data collections + if (m_enable_visualization) { + auto mesh = m_sim_state->GetMesh(); + if (m_num_regions == 1) { + auto l2g = sim_state->GetQuadratureFunction("cauchy_stress_end", 0) + ->GetPartialSpaceShared() + ->GetLocal2Global(); + mfem::Array pqs2submesh(l2g); + m_map_pqs2submesh.emplace(0, std::move(pqs2submesh)); + m_map_submesh.emplace(0, mesh); + } else { + for (int region = 0; region < static_cast(m_num_regions); ++region) { + mfem::Array domain(1); + domain[0] = region + 1; + auto submesh = mfem::ParSubMesh::CreateFromDomain(*mesh.get(), domain); + auto submesh_ptr = std::make_shared(std::move(submesh)); + m_map_submesh.emplace(region, std::move(submesh_ptr)); + + if (!m_sim_state->IsRegionActive(region)) { + continue; + } + + auto pqs = sim_state->GetQuadratureFunction("cauchy_stress_end", region) + ->GetPartialSpaceShared(); + auto l2g = pqs->GetLocal2Global(); + mfem::Array pqs2submesh(l2g.Size()); + for (int i = 0; i < l2g.Size(); i++) { + const int mapping = dynamic_cast(m_map_submesh[region].get()) + ->GetSubMeshElementFromParent(l2g[i]); + pqs2submesh[i] = mapping; + } + m_map_pqs2submesh.emplace(region, std::move(pqs2submesh)); + } + } + + RegisterDefaultProjections(); + InitializeGridFunctions(); + InitializeDataCollections(options); + } + + InitializeLightUpAnalysis(); +} + +std::shared_ptr +PostProcessingDriver::GetParFiniteElementSpace(const int region, const int vdim) { + if (!m_enable_visualization) { + return std::shared_ptr(); + } + + if (m_map_pfes.find(region) == m_map_pfes.end()) { + m_map_pfes.emplace(region, std::map>()); + } + + if (m_map_pfes[region].find(vdim) == m_map_pfes[region].end()) { + auto mesh = m_map_submesh[region]; + const int space_dim = mesh->SpaceDimension(); + std::string l2_fec_str = "L2_" + std::to_string(space_dim) + "D_P" + std::to_string(0); + auto l2_fec = m_sim_state->GetFiniteElementCollection(l2_fec_str); + auto value = std::make_shared( + mesh.get(), l2_fec.get(), vdim, mfem::Ordering::byVDIM); + m_map_pfes[region].emplace(vdim, std::move(value)); + } + return m_map_pfes[region][vdim]; +} + +void PostProcessingDriver::UpdateFields([[maybe_unused]] const int step, + [[maybe_unused]] const double time) { + for (int region = 0; region < static_cast(m_num_regions); ++region) { + if (!m_sim_state->IsRegionActive(region)) { + continue; + } + auto state_qf_avg = m_sim_state->GetQuadratureFunction("state_var_avg", region); + auto state_qf_end = m_sim_state->GetQuadratureFunction("state_var_end", region); + CalcElementAvg(state_qf_avg.get(), state_qf_end.get()); + auto cauchy_qf_avg = m_sim_state->GetQuadratureFunction("cauchy_stress_avg", region); + auto cauchy_qf_end = m_sim_state->GetQuadratureFunction("cauchy_stress_end", region); + CalcElementAvg(cauchy_qf_avg.get(), cauchy_qf_end.get()); + } + + // Execute projections based on aggregation mode + if (m_aggregation_mode == AggregationMode::PER_REGION || + m_aggregation_mode == AggregationMode::BOTH) { + // Process each region separately + for (int region = 0; region < static_cast(m_num_regions); ++region) { + if (!m_sim_state->IsRegionActive(region)) { + continue; + } + const size_t reg_idx = static_cast(region); + auto qpts2mesh = m_map_pqs2submesh[region]; + for (auto& reg : m_registered_projections) { + if (reg.region_enabled[reg_idx]) { + const auto gf_name = GetGridFunctionName(reg.display_name, region); + auto& grid_func = m_map_gfs[gf_name]; + reg.projection_class[reg_idx]->Execute( + m_sim_state, grid_func, qpts2mesh, region); + } + } + } + } + + if (m_aggregation_mode == AggregationMode::GLOBAL_COMBINED || + m_aggregation_mode == AggregationMode::BOTH) { + // Execute global aggregated projections + for (auto& reg : m_registered_projections) { + if (reg.supports_global_aggregation) { + ExecuteGlobalProjection(reg.display_name); + } + } + } +} + +void PostProcessingDriver::Update(const int step, const double time) { + CALI_CXX_MARK_SCOPE("postprocessing_update"); + UpdateFields(step, time); + // Check if we should output volume averages at this step + if (ShouldOutputAtStep(step)) { + PrintVolValues(time, m_aggregation_mode); + ClearVolumeAverageCache(); + } + + // Update data collections for visualization + if (m_enable_visualization) { + UpdateDataCollections(step, time); + } + + if (m_light_up_instances.size() > 0) { + UpdateLightUpAnalysis(); + } +} + +PostProcessingDriver::~PostProcessingDriver() = default; + +void PostProcessingDriver::PrintVolValues(const double time, AggregationMode mode) { + CALI_CXX_MARK_SCOPE("postprocessing_vol_values"); + + if (mode == AggregationMode::PER_REGION || mode == AggregationMode::BOTH) { + // Calculate per-region volume averages + for (int region = 0; region < static_cast(m_num_regions); ++region) { + if (!m_sim_state->IsRegionActive(region)) { + continue; + } + + for (auto& reg : m_registered_volume_calcs) { + if (reg.region_enabled[static_cast(region)]) { + reg.region_func(region, time); + } + } + } + } + + if (mode == AggregationMode::GLOBAL_COMBINED || mode == AggregationMode::BOTH) { + // Calculate global aggregated volume averages + for (auto& reg : m_registered_volume_calcs) { + if (reg.has_global_aggregation && reg.global_func) { + reg.global_func(time); + } + } + } +} + +PostProcessingDriver::CalcType PostProcessingDriver::GetCalcType(const std::string& calc_type_str) { + // Convert string identifiers to type-safe enums for internal processing + if (calc_type_str == "stress") { + return CalcType::STRESS; + } else if (calc_type_str == "def_grad") { + return CalcType::DEF_GRAD; + } else if (calc_type_str == "plastic_work" || calc_type_str == "pl_work") { + return CalcType::PLASTIC_WORK; + } else if (calc_type_str == "eq_pl_strain" || calc_type_str == "eps") { + return CalcType::EQ_PL_STRAIN; + } else if (calc_type_str == "euler_strain") { + return CalcType::EULER_STRAIN; + } else if (calc_type_str == "elastic_strain" || calc_type_str == "estrain") { + return CalcType::ELASTIC_STRAIN; + } else { + // Default fallback - could also throw an exception for strict validation + if (m_mpi_rank == 0) { + std::cerr << "Warning: Unknown calculation type '" << calc_type_str + << "', defaulting to stress" << std::endl; + } + return CalcType::STRESS; + } +} + +PostProcessingDriver::VolumeAverageData +PostProcessingDriver::CalculateVolumeAverage(CalcType calc_type, int region) { + if (!m_sim_state->IsRegionActive(region)) { + return VolumeAverageData(); + } + + std::shared_ptr qf; + int data_size; + std::string qf_name; + const size_t reg_idx = static_cast(region); + + // Configure calculation parameters based on type + switch (calc_type) { + case CalcType::STRESS: + qf_name = "cauchy_stress_end"; + data_size = 6; // Voigt notation: Sxx, Syy, Szz, Sxy, Sxz, Syz + break; + + case CalcType::DEF_GRAD: + qf_name = "kinetic_grads"; + data_size = 9; // Full 3x3 tensor: F11, F12, F13, F21, F22, F23, F31, F32, F33 + break; + + case CalcType::PLASTIC_WORK: + case CalcType::EQ_PL_STRAIN: + if (m_region_model_types[reg_idx] == MechType::UMAT) { + return VolumeAverageData(); + } + qf_name = "scalar"; + data_size = 1; // Scalar quantities + break; + + case CalcType::EULER_STRAIN: + qf_name = "kinetic_grads"; // Adjust this to your actual QF name for Euler strain + data_size = 9; // Voigt notation: E11, E22, E33, E23, E13, E12 + break; + + case CalcType::ELASTIC_STRAIN: + if (m_region_model_types[reg_idx] == MechType::UMAT) { + return VolumeAverageData(); + } + qf_name = "kinetic_grads"; // Adjust this to your actual QF name for elastic strain + data_size = 9; // Voigt notation: Ee11, Ee22, Ee33, Ee23, Ee13, Ee12 + break; + + default: + // This should never happen due to enum type safety, but defensive programming + if (m_mpi_rank == 0) { + std::cerr << "Error: Unhandled calculation type in CalculateVolumeAverage" << std::endl; + } + return VolumeAverageData(); + } + + // Get the quadrature function for this region + qf = m_sim_state->GetQuadratureFunction(qf_name, region); + if (!qf) { + // Region doesn't have this quadrature function - return invalid data + return VolumeAverageData(); + } + + // Handle calculation-specific preprocessing + switch (calc_type) { + case CalcType::EQ_PL_STRAIN: { + // Extract equivalent plastic strain from state variables + auto state_vars = m_sim_state->GetQuadratureFunction("state_var_end", region)->Read(); + const int vdim = m_sim_state->GetQuadratureFunction("state_var_end", region)->GetVDim(); + const int eps_ind = + m_sim_state->GetQuadratureFunctionStatePair("eq_pl_strain", region).first; + auto data = qf->Write(); + + // Copy equivalent plastic strain values to scalar quadrature function + mfem::forall(qf->Size(), [=] MFEM_HOST_DEVICE(int i) { + data[i] = state_vars[i * vdim + eps_ind]; + }); + break; + } + + case CalcType::PLASTIC_WORK: { + // Extract plastic work from state variables + auto state_vars = m_sim_state->GetQuadratureFunction("state_var_end", region)->Read(); + const int vdim = m_sim_state->GetQuadratureFunction("state_var_end", region)->GetVDim(); + + // NOTE: You'll need to update this line to match your actual plastic work state variable + // This is a placeholder - replace with your actual method to get plastic work index + const int pl_work_ind = + m_sim_state->GetQuadratureFunctionStatePair("plastic_work", region).first; + auto data = qf->Write(); + + // Copy plastic work values to scalar quadrature function + mfem::forall(qf->Size(), [=] MFEM_HOST_DEVICE(int i) { + data[i] = state_vars[i * vdim + pl_work_ind]; + }); + break; + } + + case CalcType::EULER_STRAIN: + case CalcType::DEF_GRAD: { + // Special handling for deformation gradient - assign global values to region + auto def_grad_global = m_sim_state->GetQuadratureFunction("kinetic_grads", -1); + if (def_grad_global) { + qf->operator=(0.0); + qf->operator=(*dynamic_cast(def_grad_global.get())); + } + break; + } + case CalcType::ELASTIC_STRAIN: { + auto state_vars = m_sim_state->GetQuadratureFunction("state_var_end", region)->Read(); + const int vdim = m_sim_state->GetQuadratureFunction("state_var_end", region)->GetVDim(); + const int ne = + m_sim_state->GetQuadratureFunction("state_var_end", region)->GetSpaceShared()->GetNE(); + const int estrain_ind = + m_sim_state->GetQuadratureFunctionStatePair("elastic_strain", region).first; + const int quats_ind = m_sim_state->GetQuadratureFunctionStatePair("quats", region).first; + const int rel_vol_ind = + m_sim_state->GetQuadratureFunctionStatePair("relative_volume", region).first; + qf->operator=(0.0); + auto data = qf->Write(); + + mfem::forall(ne, [=] MFEM_HOST_DEVICE(int i) { + const auto strain_lat = &state_vars[i * vdim + estrain_ind]; + const auto quats = &state_vars[i * vdim + quats_ind]; + const auto rel_vol = state_vars[i * vdim + rel_vol_ind]; + double* strain = &data[i * 9]; + + { + double strainm[3 * 3] = {}; + double* strain_m[3] = {&strainm[0], &strainm[3], &strainm[6]}; + const double t1 = ecmech::sqr2i * strain_lat[0]; + const double t2 = ecmech::sqr6i * strain_lat[1]; + // + // Volume strain is ln(V^e_mean) term aka ln(relative volume) + // Our plastic deformation has a det(1) aka no change in volume change + const double elas_vol_strain = log(rel_vol); + // We output elastic strain formulation such that the relationship + // between V^e and \varepsilon is just V^e = I + \varepsilon + strain_m[0][0] = (t1 - t2) + elas_vol_strain; // 11 + strain_m[1][1] = (-t1 - t2) + elas_vol_strain; // 22 + strain_m[2][2] = ecmech::sqr2b3 * strain_lat[1] + elas_vol_strain; // 33 + strain_m[1][2] = ecmech::sqr2i * strain_lat[4]; // 23 + strain_m[2][0] = ecmech::sqr2i * strain_lat[3]; // 31 + strain_m[0][1] = ecmech::sqr2i * strain_lat[2]; // 12 + + strain_m[2][1] = strain_m[1][2]; + strain_m[0][2] = strain_m[2][0]; + strain_m[1][0] = strain_m[0][1]; + + double rmat[3 * 3] = {}; + double strain_samp[3 * 3] = {}; + + Quat2RMat(quats, rmat); + snls::linalg::rotMatrix<3, false>(strainm, rmat, strain_samp); + + strain_m[0] = &strain_samp[0]; + strain_m[1] = &strain_samp[3]; + strain_m[2] = &strain_samp[6]; + strain[0] = strain_m[0][0]; + strain[1] = strain_m[1][1]; + strain[2] = strain_m[2][2]; + strain[3] = strain_m[1][2]; + strain[4] = strain_m[0][2]; + strain[5] = strain_m[0][1]; + strain[6] = 0.0; + strain[7] = 0.0; + strain[8] = 0.0; + } + }); + break; + } + + case CalcType::STRESS: + default: + // No special preprocessing needed for these types + // The quadrature function already contains the correct data + break; + } + + // Perform the volume integration to compute average + mfem::Vector avg_data(data_size); + double total_volume = 0.0; + + auto region_comm = m_sim_state->GetRegionCommunicator(region); + + switch (calc_type) { + case CalcType::PLASTIC_WORK: { + total_volume = exaconstit::kernel::ComputeVolAvgTensorFromPartial( + qf.get(), avg_data, data_size, m_sim_state->GetOptions().solvers.rtmodel, region_comm); + break; + } + default: { + total_volume = exaconstit::kernel::ComputeVolAvgTensorFromPartial( + qf.get(), avg_data, data_size, m_sim_state->GetOptions().solvers.rtmodel, region_comm); + break; + } + } + // Any post processing that might be needed + switch (calc_type) { + case CalcType::EULER_STRAIN: { + mfem::Vector avg_euler_strain(6); + { + mfem::DenseMatrix euler_strain(3, 3); + mfem::DenseMatrix def_grad(avg_data.HostReadWrite(), 3, 3); + int dim = 3; + mfem::DenseMatrix Finv(dim), Binv(dim); + double half = 1.0 / 2.0; + + mfem::CalcInverse(def_grad, Finv); + mfem::MultAtB(Finv, Finv, Binv); + + euler_strain = 0.0; + + for (int j = 0; j < dim; j++) { + for (int i = 0; i < dim; i++) { + euler_strain(i, j) -= half * Binv(i, j); + } + + euler_strain(j, j) += half; + } + avg_euler_strain(0) = euler_strain(0, 0); + avg_euler_strain(1) = euler_strain(1, 1); + avg_euler_strain(2) = euler_strain(2, 2); + avg_euler_strain(3) = euler_strain(1, 2); + avg_euler_strain(4) = euler_strain(0, 2); + avg_euler_strain(5) = euler_strain(0, 1); + } + return VolumeAverageData(total_volume, avg_euler_strain); + break; + } + case CalcType::ELASTIC_STRAIN: { + avg_data.SetSize(6); + break; + } + default: + break; + } + // Return the calculated data + return VolumeAverageData(total_volume, avg_data); +} + +PostProcessingDriver::VolumeAverageData +PostProcessingDriver::GetOrCalculateVolumeAverage(CalcType calc_type, int region) { + // First, check if we have cached data for this calculation type and region + auto cache_it = m_region_cache.find(calc_type); + if (cache_it != m_region_cache.end()) { + auto region_it = cache_it->second.find(region); + if (region_it != cache_it->second.end() && region_it->second.is_valid) { + // Found valid cached data - return it immediately + return region_it->second; + } + } + + // No cached data found - calculate it now + auto result = CalculateVolumeAverage(calc_type, region); + + // Cache the result for future use (even if invalid, to avoid repeated failed attempts) + m_region_cache[calc_type][region] = result; + + return result; +} + +void PostProcessingDriver::ClearVolumeAverageCache() { + // Clear all cached data at the beginning of each time step + m_region_cache.clear(); +} + +void PostProcessingDriver::VolumeAverage(const std::string& calc_type_str, + int region, + double time) { + if (region >= 0 && !m_sim_state->IsRegionActive(region)) { + return; + } + // Convert string to enum for internal processing + CalcType calc_type = GetCalcType(calc_type_str); + + // Calculate and cache the result + auto result = GetOrCalculateVolumeAverage(calc_type, region); + + if (!result.is_valid) { + // fix me + // Calculation failed (e.g., missing quadrature function) - skip output + // Could maybe add a warning for this so people are aware of which materials + // didn't have a valid calculation but only do it once per simulation... + return; + } + + // Write output using the file manager + auto region_name = m_sim_state->GetRegionName(region); + auto region_comm = m_sim_state->GetRegionCommunicator(region); + if (result.data.Size() == 1) { + // Scalar quantity + m_file_manager->WriteVolumeAverage(calc_type_str, + region, + region_name, + time, + result.volume, + result.data[0], + 1, + region_comm); + } else { + // Vector/tensor quantity + m_file_manager->WriteVolumeAverage(calc_type_str, + region, + region_name, + time, + result.volume, + result.data, + result.data.Size(), + region_comm); + } +} + +void PostProcessingDriver::GlobalVolumeAverage(const std::string& calc_type_str, double time) { + CalcType calc_type = GetCalcType(calc_type_str); + + // Determine expected data size for this calculation type + int data_size = 1; // Default for scalar quantities + switch (calc_type) { + case CalcType::STRESS: + case CalcType::EULER_STRAIN: + case CalcType::ELASTIC_STRAIN: + data_size = 6; // Tensor quantities in Voigt notation + break; + case CalcType::DEF_GRAD: + data_size = 9; // Full 3x3 tensor + break; + case CalcType::PLASTIC_WORK: + case CalcType::EQ_PL_STRAIN: + default: + data_size = 1; // Scalar quantities + break; + } + + // Initialize accumulators for volume-weighted averaging + mfem::Vector global_avg_data(data_size); + global_avg_data = 0.0; + double global_volume = 0.0; + + // Accumulate contributions from all regions + for (int region = 0; region < static_cast(m_num_regions); ++region) { + // Use cached data if available, calculate if not + auto region_data = GetOrCalculateVolumeAverage(calc_type, region); + // Now gather all region data to rank 0 + if (m_mpi_rank == 0) { + // Rank 0 receives from all region roots + const int root_rank = m_sim_state->GetRegionRootRank(region); + if (root_rank > m_mpi_rank) { + region_data.data.SetSize(data_size); + region_data.is_valid = true; + // Receive from the region root + MPI_Recv(region_data.data.HostWrite(), + data_size, + MPI_DOUBLE, + root_rank, + region, + MPI_COMM_WORLD, + MPI_STATUS_IGNORE); + MPI_Recv(®ion_data.volume, + 1, + MPI_DOUBLE, + root_rank, + static_cast(m_num_regions) * 2 + region, + MPI_COMM_WORLD, + MPI_STATUS_IGNORE); + } + } else { + // Other ranks send their region data if they're region roots + if (m_sim_state->IsRegionIORoot(region)) { + MPI_Send( + region_data.data.HostRead(), data_size, MPI_DOUBLE, 0, region, MPI_COMM_WORLD); + MPI_Send(®ion_data.volume, + 1, + MPI_DOUBLE, + 0, + static_cast(m_num_regions) * 2 + region, + MPI_COMM_WORLD); + } + } + + if (region_data.is_valid && region_data.volume > 0.0) { + // Add volume-weighted contribution to global average + for (int i = 0; i < data_size; ++i) { + if (calc_type != CalcType::PLASTIC_WORK) { + global_avg_data[i] += region_data.data[i] * region_data.volume; + } else { + global_avg_data[i] += region_data.data[i]; + } + } + global_volume += region_data.volume; + } + } + + // Normalize by total volume to get the true global average + if (global_volume > 0.0) { + if (calc_type != CalcType::PLASTIC_WORK) { + global_avg_data /= global_volume; + } + } else { + // No valid regions found - issue warning + if (m_mpi_rank == 0) { + std::cerr << "Warning: No valid regions found for global " << calc_type_str + << " calculation" << std::endl; + } + return; + } + + // Write global output (region = -1 indicates global file) + if (data_size == 1) { + // Scalar quantity + m_file_manager->WriteVolumeAverage( + calc_type_str, -1, "", time, global_volume, global_avg_data[0]); + } else { + // Vector/tensor quantity + m_file_manager->WriteVolumeAverage( + calc_type_str, -1, "", time, global_volume, global_avg_data); + } +} + +bool PostProcessingDriver::ShouldOutputAtStep(int step) const { + return m_file_manager->ShouldOutputAtStep(step); +} + +void PostProcessingDriver::VolumeAvgStress(const int region, const double time) { + VolumeAverage("stress", region, time); +} + +void PostProcessingDriver::GlobalVolumeAvgStress(const double time) { + GlobalVolumeAverage("stress", time); +} + +void PostProcessingDriver::VolumeAvgDefGrad(const int region, const double time) { + VolumeAverage("def_grad", region, time); +} + +void PostProcessingDriver::GlobalVolumeAvgDefGrad(const double time) { + GlobalVolumeAverage("def_grad", time); +} + +void PostProcessingDriver::VolumeEPS(const int region, const double time) { + VolumeAverage("eq_pl_strain", region, time); +} + +void PostProcessingDriver::GlobalVolumeEPS(const double time) { + GlobalVolumeAverage("eq_pl_strain", time); +} + +void PostProcessingDriver::VolumePlWork(const int region, const double time) { + VolumeAverage("plastic_work", region, time); +} + +void PostProcessingDriver::GlobalVolumePlWork(const double time) { + GlobalVolumeAverage("plastic_work", time); +} + +void PostProcessingDriver::VolumeAvgEulerStrain(const int region, const double time) { + VolumeAverage("euler_strain", region, time); +} + +void PostProcessingDriver::GlobalVolumeAvgEulerStrain(const double time) { + GlobalVolumeAverage("euler_strain", time); +} + +void PostProcessingDriver::VolumeAvgElasticStrain(const int region, const double time) { + VolumeAverage("elastic_strain", region, time); +} + +void PostProcessingDriver::GlobalVolumeAvgElasticStrain(const double time) { + GlobalVolumeAverage("elastic_strain", time); +} + +void PostProcessingDriver::RegisterDefaultProjections() { + const auto& projection_opts = m_sim_state->GetOptions().post_processing.projections; + + std::vector fields; + if (projection_opts.auto_enable_compatible) { + std::vector defaults = {"centroid", + "volume", + "cauchy", + "von_mises", + "hydro", + "dpeff", + "eps", + "xtal_ori", + "elastic_strain", + "hardness", + "shear_rate"}; + fields = defaults; + } else if (projection_opts.enabled_projections.size() > 0) { + fields = projection_opts.enabled_projections; + } + + for (const auto& field : fields) { + RegisterProjection(field); + } +} + +void PostProcessingDriver::RegisterDefaultVolumeCalculations() { + // Register volume average calculations with both per-region and global variants + // Only register if the corresponding option is enabled in ExaOptions + + const auto& vol_opts = m_sim_state->GetOptions().post_processing.volume_averages; + + if (vol_opts.enabled && vol_opts.stress) { + RegisterVolumeAverageFunction( + "stress", + "Volume Average Stress", + [this](int region, double time) { + VolumeAvgStress(region, time); + }, + [this](double time) { + GlobalVolumeAvgStress(time); + }, + true); + } + + if (vol_opts.enabled && vol_opts.def_grad) { + RegisterVolumeAverageFunction( + "def_grad", + "Volume Average Deformation Gradient", + [this](int region, double time) { + VolumeAvgDefGrad(region, time); + }, + [this](double time) { + GlobalVolumeAvgDefGrad(time); + }, + true); + } + + if (vol_opts.enabled && vol_opts.euler_strain) { + RegisterVolumeAverageFunction( + "euler_strain", + "Volume Average Euler Strain", + [this](int region, double time) { + VolumeAvgEulerStrain(region, time); + }, + [this](double time) { + GlobalVolumeAvgEulerStrain(time); + }, + true); + } + + if (vol_opts.enabled && vol_opts.plastic_work) { + RegisterVolumeAverageFunction( + "plastic_work", + "Volume Plastic Work", + [this](int region, double time) { + VolumePlWork(region, time); + }, + [this](double time) { + GlobalVolumePlWork(time); + }, + true); + } + + if (vol_opts.enabled && vol_opts.eq_pl_strain) { + RegisterVolumeAverageFunction( + "equivalent_plastic_strain", + "Volume Equivalent Plastic Strain", + [this](int region, double time) { + VolumeEPS(region, time); + }, + [this](double time) { + GlobalVolumeEPS(time); + }, + true); + } + + if (vol_opts.enabled && vol_opts.elastic_strain) { + RegisterVolumeAverageFunction( + "elastic_strain", + "Volume Average Elastic Strain", + [this](int region, double time) { + VolumeAvgElasticStrain(region, time); + }, + [this](double time) { + GlobalVolumeAvgElasticStrain(time); + }, + true); + } +} + +void PostProcessingDriver::RegisterVolumeAverageFunction( + const std::string& calc_name, + const std::string& display_name, + std::function region_func, + std::function global_func, + bool enabled) { + std::vector region_enabled(m_num_regions, enabled); + + m_registered_volume_calcs.push_back( + {calc_name, + display_name, + ProjectionTraits::ModelCompatibility::ALL_MODELS, // Default compatibility + region_enabled, + region_func, + global_func, + (global_func != nullptr)}); +} + +bool PostProcessingDriver::RegionHasQuadratureFunction(const std::string& field_name, + int region) const { + return m_sim_state->GetQuadratureFunction(field_name, region) != nullptr; +} + +std::vector +PostProcessingDriver::GetActiveRegionsForField(const std::string& field_name) const { + std::vector active_regions; + + auto find_lambda = [&](const int region) -> bool { + const auto gf_name = this->GetGridFunctionName(field_name, region); + return (this->m_map_gfs.find(gf_name) != this->m_map_gfs.end()) && + (m_sim_state->IsRegionActive(region)); + }; + + for (int region = 0; region < static_cast(m_num_regions); ++region) { + active_regions.push_back(find_lambda(region)); + } + return active_regions; +} + +std::string PostProcessingDriver::GetGridFunctionName(const std::string& field_name, + int region) const { + return field_name + " " + m_sim_state->GetRegionDisplayName(region); +} + +void PostProcessingDriver::ExecuteGlobalProjection(const std::string& field_name) { + if (m_num_regions == 1) { + return; + } + // Get all active regions for this field + auto active_regions = GetActiveRegionsForField(field_name); + if (active_regions.empty()) { + return; + } + // Combine region data into global grid function + CombineRegionDataToGlobal(field_name); +} + +void PostProcessingDriver::CombineRegionDataToGlobal(const std::string& field_name) { + auto global_gf_name = GetGridFunctionName(field_name, -1); // -1 indicates global + auto& global_gf = *m_map_gfs[global_gf_name]; + + // Initialize global grid function to zero + global_gf = 0.0; + + // Get active regions for this field + auto active_regions = GetActiveRegionsForField(field_name); + + int index = 0; + for (const auto active : active_regions) { + if (active) { + auto submesh = std::dynamic_pointer_cast(m_map_submesh[index]); + if (submesh) { + auto gf_name = GetGridFunctionName(field_name, index); // -1 indicates global + auto& gf = *m_map_gfs[gf_name]; + submesh->Transfer(gf, global_gf); + } + } + index += 1; + } +} + +void PostProcessingDriver::CalcElementAvg(mfem::expt::PartialQuadratureFunction* elemVal, + const mfem::expt::PartialQuadratureFunction* qf) { + CALI_CXX_MARK_SCOPE("calc_element_avg_partial"); + + auto pqs = qf->GetPartialSpaceShared(); + auto mesh = pqs->GetMeshShared(); + const mfem::FiniteElement& el = *m_sim_state->GetMeshParFiniteElementSpace()->GetFE(0); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + + const int nqpts = ir->GetNPoints(); + const int vdim = qf->GetVDim(); + const int NE = pqs->GetNE(); // Number of elements in this PARTIAL space (key difference!) + + const double* W = ir->GetWeights().Read(); + const mfem::GeometricFactors* geom = mesh->GetGeometricFactors( + *ir, mfem::GeometricFactors::DETERMINANTS); + + // KEY DIFFERENCE: Get the local-to-global element mapping for partial space + auto l2g = pqs->GetLocal2Global().Read(); // Maps local element index to global element index + auto loc_offsets = pqs->getOffsets().Read(); // Offsets for local data layout + // auto global_offsets = (pqs->GetGlobalOffset().Size() > 1) ? + // pqs->GetGlobalOffset().Read() : loc_offsets; // Offsets for global + // data layout + + auto qf_data = qf->Read(); // Partial quadrature function data (only for this region!) + auto elem_data = elemVal->ReadWrite(); // Element averages output (only for this region!) + auto j_data = geom->detJ.Read(); // Global geometric factors + + // Zero out element averages + *elemVal = 0.0; + + // KEY DIFFERENCE: Process only the elements that exist in this partial space + // The old version processed ALL elements (0 to nelems-1) + // The new version processes only local elements (0 to NE-1) and maps to global indices + mfem::forall(NE, [=] MFEM_HOST_DEVICE(int ie) { + const int global_elem = l2g[ie]; // Map local element to global element + const int local_offset = loc_offsets[ie]; // Offset into local data array + // const int global_offset = global_offsets[global_elem]; // Offset into global layout + const int npts_elem = loc_offsets[ie + 1] - local_offset; // Number of qpts for this element + + double vol = 0.0; + + // Calculate volume and weighted sum using actual quadrature points for this element + for (int iq = 0; iq < npts_elem; ++iq) { + // Use global element index for geometric factors (j_data) + const double wt = j_data[global_elem * nqpts + iq] * W[iq]; + vol += wt; + + for (int iv = 0; iv < vdim; ++iv) { + // Use local data layout for quadrature function values + const int local_idx = local_offset * vdim + iq * vdim + iv; + const double val = qf_data[local_idx]; + + // Store in local element index (ie, not global_elem!) + elem_data[ie * vdim + iv] += val * wt; + } + } + + // Normalize by volume to get element average + const double inv_vol = 1.0 / vol; + for (int iv = 0; iv < vdim; ++iv) { + elem_data[ie * vdim + iv] *= inv_vol; + } + }); +} + +void PostProcessingDriver::CalcGlobalElementAvg(mfem::Vector* elemVal, + const std::string& field_name) { + CALI_CXX_MARK_SCOPE("calc_global_element_avg"); + + auto fe_space = m_sim_state->GetMeshParFiniteElementSpace(); + const int nelems = fe_space->GetNE(); + + // Find the vector dimension by checking the first available region + int vdim = 1; + for (int region = 0; region < static_cast(m_num_regions); ++region) { + if (auto pqf = m_sim_state->GetQuadratureFunction(field_name, region)) { + if (vdim < pqf->GetVDim()) { + vdim = pqf->GetVDim(); + } + } + } + + // Ensure elemVal is sized correctly + if (elemVal->Size() != vdim * nelems) { + elemVal->SetSize(vdim * nelems); + elemVal->UseDevice(true); + } + + // Initialize to zero + *elemVal = 0.0; + double* global_data = elemVal->ReadWrite(); + + // Accumulate from all regions + for (size_t region = 0; region < m_num_regions; ++region) { + auto pqf = m_sim_state->GetQuadratureFunction(field_name, static_cast(region)); + if (!pqf || !m_region_evec[region]) { + continue; + } + + // Calculate element averages for this region + CalcElementAvg(m_region_evec[region].get(), pqf.get()); + + // Add this region's contribution to global averages + auto pqs = pqf->GetPartialSpaceShared(); + auto l2g = pqs->GetLocal2Global().Read(); + auto region_data = m_region_evec[region]->Read(); + const int NE_region = pqs->GetNE(); + const int local_vdim = pqf->GetVDim(); + + mfem::forall(NE_region, [=] MFEM_HOST_DEVICE(int ie) { + const int global_elem = l2g[ie]; + for (int iv = 0; iv < local_vdim; ++iv) { + global_data[global_elem * vdim + iv] = region_data[ie * local_vdim + iv]; + } + }); + } +} + +void PostProcessingDriver::InitializeGridFunctions() { + for (auto& reg : m_registered_projections) { + // Create per-region grid functions + int max_vdim = 0; + if (m_aggregation_mode == AggregationMode::PER_REGION || + m_aggregation_mode == AggregationMode::BOTH) { + for (size_t region = 0; region < m_num_regions; ++region) { + const int reg_int = static_cast(region); + if (reg.region_enabled[region]) { + const auto gf_name = GetGridFunctionName(reg.display_name, reg_int); + // Determine vector dimension from quadrature function + const int vdim = reg.region_length[region]; + max_vdim = (vdim > max_vdim) ? vdim : max_vdim; + auto fe_space = GetParFiniteElementSpace(reg_int, vdim); + m_map_gfs.emplace(gf_name, + std::make_shared(fe_space.get())); + m_map_gfs[gf_name]->operator=(0.0); + } + } + } + // Create global grid functions + if (reg.supports_global_aggregation && + (m_aggregation_mode == AggregationMode::GLOBAL_COMBINED || + m_aggregation_mode == AggregationMode::BOTH) && + (m_num_regions > 1)) { + if (max_vdim < 1) { + for (size_t region = 0; region < m_num_regions; ++region) { + if (reg.region_enabled[region]) { + const auto gf_name = GetGridFunctionName(reg.display_name, + static_cast(region)); + // Determine vector dimension from quadrature function + const int vdim = reg.region_length[region]; + max_vdim = (vdim > max_vdim) ? vdim : max_vdim; + } + } + } + + auto gf_name = GetGridFunctionName(reg.display_name, -1); + auto fe_space = m_sim_state->GetParFiniteElementSpace(max_vdim); + m_map_gfs.emplace(gf_name, std::make_shared(fe_space.get())); + m_map_gfs[gf_name]->operator=(0.0); + } + } + + if (m_aggregation_mode == AggregationMode::PER_REGION || + m_aggregation_mode == AggregationMode::BOTH) { + if (m_num_regions == 1) { + auto disp_gf_name = GetGridFunctionName("Displacement", 0); + auto vel_gf_name = GetGridFunctionName("Velocity", 0); + auto grain_gf_name = GetGridFunctionName("Grain ID", 0); + m_map_gfs.emplace(disp_gf_name, m_sim_state->GetDisplacement()); + m_map_gfs.emplace(vel_gf_name, m_sim_state->GetVelocity()); + m_map_gfs.emplace(grain_gf_name, m_sim_state->GetGrains()); + } + } + + if ((m_aggregation_mode == AggregationMode::GLOBAL_COMBINED || + m_aggregation_mode == AggregationMode::BOTH) && + (m_num_regions > 1)) { + auto disp_gf_name = GetGridFunctionName("Displacement", -1); + auto vel_gf_name = GetGridFunctionName("Velocity", -1); + auto grain_gf_name = GetGridFunctionName("Grain ID", -1); + m_map_gfs.emplace(disp_gf_name, m_sim_state->GetDisplacement()); + m_map_gfs.emplace(vel_gf_name, m_sim_state->GetVelocity()); + m_map_gfs.emplace(grain_gf_name, m_sim_state->GetGrains()); + } + + UpdateFields(static_cast(m_sim_state->GetSimulationCycle()), m_sim_state->GetTime()); +} + +void PostProcessingDriver::InitializeDataCollections(ExaOptions& options) { + auto output_dir_base = m_file_manager->GetVizDirectory(); + std::string visit_key = "visit_"; + std::string paraview_key = "paraview_"; +#if defined(MFEM_USE_ADIOS2) + std::string adios2_key = "adios2_"; +#endif + + auto data_collection_name = [](const std::string& input, const std::string& delimiter) { + auto pos = input.find(delimiter); + if (pos == std::string::npos) { + return input; // Delimiter not found, return entire string + } + return input.substr(0, pos); + }; + + if (m_aggregation_mode == AggregationMode::PER_REGION || + m_aggregation_mode == AggregationMode::BOTH) { + for (int region = 0; region < static_cast(m_num_regions); ++region) { + auto mesh = m_map_submesh[region]; + std::string region_postfix = "region_" + std::to_string(region + 1); + std::string display_region_postfix = " " + m_sim_state->GetRegionDisplayName(region); + fs::path output_dir = output_dir_base / region_postfix; + fs::path output_dir_vizs = output_dir / m_file_manager->GetBaseFilename(); + if (m_sim_state->IsRegionActive(region)) { + auto region_comm = m_sim_state->GetRegionCommunicator(region); + m_file_manager->EnsureDirectoryExists(output_dir, region_comm); + } + std::vector dcs_keys; + if (options.visualization.visit) { + std::string key = visit_key + region_postfix; + m_map_dcs.emplace(key, + std::make_unique( + output_dir_vizs.string(), mesh.get())); + m_map_dcs[key]->SetPrecision(10); + dcs_keys.push_back(key); + } + if (options.visualization.paraview) { + std::string key = paraview_key + region_postfix; + m_map_dcs.emplace(key, + std::make_unique( + output_dir_vizs.string(), mesh.get())); + auto& paraview = *( + dynamic_cast(m_map_dcs[key].get())); + paraview.SetLevelsOfDetail(options.mesh.order); + paraview.SetDataFormat(mfem::VTKFormat::BINARY); + paraview.SetHighOrderOutput(false); + dcs_keys.push_back(key); + } +#ifdef MFEM_USE_ADIOS2 + if (options.visualization.adios2) { + const std::string basename = output_dir_vizs.string() + ".bp"; + std::string key = adios2_key + region_postfix; + m_map_dcs.emplace(key, + std::make_unique( + MPI_COMM_WORLD, basename, mesh.get())); + auto& adios2 = *(dynamic_cast(m_map_dcs[key].get())); + adios2.SetParameter("SubStreams", std::to_string(m_num_mpi_rank / 2)); + dcs_keys.push_back(key); + } +#endif + for (auto& dcs_key : dcs_keys) { + auto& dcs = m_map_dcs[dcs_key]; + for (auto& [key, value] : m_map_gfs) { + if (key.find(display_region_postfix) != std::string::npos) { + std::string disp_key = data_collection_name(key, display_region_postfix); + dcs->RegisterField(disp_key, value.get()); + } + } + dcs->SetCycle(0); + dcs->SetTime(0.0); + dcs->Save(); + } + } + } + + if ((m_aggregation_mode == AggregationMode::GLOBAL_COMBINED || + m_aggregation_mode == AggregationMode::BOTH) && + (m_num_regions > 1)) { + auto mesh = m_sim_state->GetMesh(); + + std::string region_postfix = "global"; + std::string display_region_postfix = " " + m_sim_state->GetRegionDisplayName(-1); + fs::path output_dir = output_dir_base / region_postfix; + fs::path output_dir_vizs = output_dir / m_file_manager->GetBaseFilename(); + m_file_manager->EnsureDirectoryExists(output_dir); + std::vector dcs_keys; + if (options.visualization.visit) { + std::string key = visit_key + region_postfix; + m_map_dcs.emplace( + key, + std::make_unique(output_dir_vizs.string(), mesh.get())); + m_map_dcs[key]->SetPrecision(10); + dcs_keys.push_back(key); + } + if (options.visualization.paraview) { + std::string key = paraview_key + region_postfix; + m_map_dcs.emplace(key, + std::make_unique( + output_dir_vizs.string(), mesh.get())); + auto& paraview = *(dynamic_cast(m_map_dcs[key].get())); + paraview.SetLevelsOfDetail(options.mesh.order); + paraview.SetDataFormat(mfem::VTKFormat::BINARY); + paraview.SetHighOrderOutput(false); + dcs_keys.push_back(key); + } +#ifdef MFEM_USE_ADIOS2 + if (options.visualization.adios2) { + const std::string basename = output_dir_vizs.string() + ".bp"; + std::string key = adios2_key + region_postfix; + m_map_dcs.emplace( + key, + std::make_unique(MPI_COMM_WORLD, basename, mesh.get())); + auto& adios2 = *(dynamic_cast(m_map_dcs[key].get())); + adios2.SetParameter("SubStreams", std::to_string(m_num_mpi_rank / 2)); + dcs_keys.push_back(key); + } +#endif + + for (auto& dcs_key : dcs_keys) { + auto& dcs = m_map_dcs[dcs_key]; + for (auto& [key, value] : m_map_gfs) { + if (key.find(display_region_postfix) != std::string::npos) { + std::string disp_key = data_collection_name(key, display_region_postfix); + dcs->RegisterField(disp_key, value.get()); + } + } + dcs->SetCycle(0); + dcs->SetTime(0.0); + dcs->Save(); + } + } +} + +void PostProcessingDriver::UpdateDataCollections(const int step, const double time) { + for (auto& [tmp, dcs] : m_map_dcs) { + dcs->SetCycle(step); + dcs->SetTime(time); + dcs->Save(); + } +} + +void PostProcessingDriver::InitializeLightUpAnalysis() { + auto options = m_sim_state->GetOptions(); + // Clear any existing instances + m_light_up_instances.clear(); + + // Get enabled light_up configurations + auto enabled_configs = options.post_processing.get_enabled_light_up_configs(); + + if (!enabled_configs.empty() && m_mpi_rank == 0) { + std::cout << "Initializing LightUp analysis for " << enabled_configs.size() + << " material(s)" << std::endl; + } + + // Create LightUp instance for each enabled configuration + for (const auto& light_config : enabled_configs) { + if (!light_config.region_id.has_value() && m_mpi_rank == 0) { + std::cerr << "Error: LightUp config for material '" << light_config.material_name + << "' has unresolved region_id" << std::endl; + continue; + } + + int region_id = light_config.region_id.value() - 1; + + if (!m_sim_state->IsRegionActive(region_id)) { + continue; + } + + if (m_sim_state->IsRegionIORoot(region_id)) { + std::cout << " Creating LightUp for material '" << light_config.material_name + << "' (region " << region_id + 1 << ")" << std::endl; + } + + fs::path lattice_base = light_config.lattice_basename; + fs::path lattice_basename = m_file_manager->GetOutputDirectory() / lattice_base; + + auto light_up_instance = std::make_unique( + light_config.hkl_directions, + light_config.distance_tolerance, + light_config.sample_direction, + m_sim_state->GetQuadratureFunction("cauchy_stress_end", region_id) + ->GetPartialSpaceShared(), + m_sim_state, + region_id, // Use the resolved region_id + options.solvers.rtmodel, + lattice_basename, + light_config.lattice_parameters, + light_config.lattice_type); + + m_light_up_instances.push_back(std::move(light_up_instance)); + } +} + +void PostProcessingDriver::UpdateLightUpAnalysis() { + // Update all LightUp instances + for (auto& light_up : m_light_up_instances) { + const int region_id = light_up->GetRegionID(); + + auto state_vars = m_sim_state->GetQuadratureFunction("state_var_end", region_id); + auto stress = m_sim_state->GetQuadratureFunction("cauchy_stress_end", region_id); + + light_up->CalculateLightUpData(state_vars, stress); + } +} + +void PostProcessingDriver::EnableProjection(const std::string& field_name, + int region, + bool enable) { + for (auto& reg : m_registered_projections) { + if (reg.field_name == field_name && region < static_cast(reg.region_enabled.size())) { + reg.region_enabled[static_cast(region)] = enable; + } + } +} + +void PostProcessingDriver::EnableProjection(const std::string& field_name, bool enable) { + for (auto& reg : m_registered_projections) { + if (reg.field_name == field_name) { + std::fill(reg.region_enabled.begin(), reg.region_enabled.end(), enable); + } + } +} + +void PostProcessingDriver::EnableAllProjections() { + for (auto& reg : m_registered_projections) { + for (size_t region = 0; region < static_cast(m_num_regions); ++region) { + // Check compatibility with region's model type + bool compatible = true; + if (reg.model_compatibility == ProjectionTraits::ModelCompatibility::EXACMECH_ONLY && + m_region_model_types[region] != MechType::EXACMECH) { + compatible = false; + } + if (reg.model_compatibility == ProjectionTraits::ModelCompatibility::UMAT_ONLY && + m_region_model_types[region] != MechType::UMAT) { + compatible = false; + } + + // Only enable if compatible and has required data + if (compatible) { + reg.region_enabled[region] = true; + } + } + } +} + +std::vector> +PostProcessingDriver::GetAvailableProjections() const { + std::vector> available; + for (const auto& reg : m_registered_projections) { + available.emplace_back(reg.field_name, reg.display_name); + } + return available; +} + +size_t PostProcessingDriver::GetQuadratureFunctionSize() const { + // Return size based on one of the region quadrature functions + for (int region = 0; region < static_cast(m_num_regions); ++region) { + if (auto pqf = m_sim_state->GetQuadratureFunction("cauchy_stress_end", region)) { + return static_cast(pqf->GetSpaceShared()->GetSize()); + } + } + return 0; +} \ No newline at end of file diff --git a/src/postprocessing/postprocessing_driver.hpp b/src/postprocessing/postprocessing_driver.hpp new file mode 100644 index 0000000..3ccaa68 --- /dev/null +++ b/src/postprocessing/postprocessing_driver.hpp @@ -0,0 +1,984 @@ +#pragma once + +#include "postprocessing/projection_class.hpp" +#include "sim_state/simulation_state.hpp" +#include "utilities/mechanics_kernels.hpp" + +#include "ECMech_const.h" +#include "mfem.hpp" + +// Forward declaration to avoid circular includes +class PostProcessingFileManager; + +class LightUp; +/** + * @brief PostProcessingDriver handles all post-processing operations for ExaConstit simulations + * + * This class manages: + * 1. Projection of quadrature data to grid functions for visualization + * 2. Calculation of volume-averaged quantities (global and per-region) + * 3. Registration of data with visualization collections + * 4. Output of post-processed data at specified intervals + * 5. Multi-material support with region-specific and combined visualizations + */ +class PostProcessingDriver { +public: + /** + * @brief Aggregation mode for multi-region data + */ + enum class AggregationMode { + PER_REGION, // Process each region separately + GLOBAL_COMBINED, // Combine all regions into global fields + BOTH // Both per-region and global combined + }; + + /** + * @brief Construct a new PostProcessingDriver + * + * @param sim_state Reference to global simulation state + * @param options Simulation options + */ + PostProcessingDriver(std::shared_ptr sim_state, ExaOptions& options); + + /** + * @brief Destructor + */ + ~PostProcessingDriver(); + + /** + * @brief Update post-processing data for current step + * + * @param step Current time step + * @param time Current simulation time + */ + void Update(const int step, const double time); + + /** + * @brief Calculate and output volume-averaged quantities + * + * @param time Current simulation time + * @param mode Aggregation mode (default: BOTH) + */ + void PrintVolValues(const double time, AggregationMode mode = AggregationMode::BOTH); + + /** + * @brief Update data collections with current projection data + * + * @param step Current time step + * @param time Current simulation time + */ + void UpdateDataCollections(const int step, const double time); + + /** + * @brief Enable or disable a projection for a specific region + * + * @param field_name Name of the field + * @param region region index + * @param enable Whether to enable the projection + */ + void EnableProjection(const std::string& field_name, int region, bool enable = true); + + /** + * @brief Enable or disable a projection for all regions + * + * @param field_name Name of the field + * @param enable Whether to enable the projection + */ + void EnableProjection(const std::string& field_name, bool enable = true); + + /** + * @brief Enable or disable all projections based on model compatibility + * + * Automatically enables all projections that are compatible with each + * region's material model type. Uses the model_compatibility field in + * ProjectionRegistration to determine which projections should be enabled + * for EXACMECH_ONLY, UMAT_ONLY, or ALL_MODELS compatibility levels. + * + * Provides a convenient way to activate all appropriate projections + * without manually specifying each projection type for each region. + */ + void EnableAllProjections(); + + /** + * @brief Get list of all available projection types + * + * @return Vector of pairs containing field names and display names + * + * Returns information about all registered projection types for UI display + * or programmatic enumeration. Each pair contains the internal field name + * (for EnableProjection calls) and the user-friendly display name. + */ + std::vector> GetAvailableProjections() const; + + /** + * @brief Set aggregation mode for multi-region processing + */ + void SetAggregationMode(AggregationMode mode) { + m_aggregation_mode = mode; + } + + /** + * @brief Check if volume averages should be output at current step + * + * @param step Current time step number + * @return true if output should occur, false otherwise + * + * Determines output timing based on the configured output frequency + * in ExaOptions. Delegates to PostProcessingFileManager for + * consistent frequency control across all output operations. + */ + bool ShouldOutputAtStep(int step) const; + + // Returns a pointer to a ParFiniteElementSpace (PFES) that's ordered according to VDIMs + // and makes use of an L2 FiniteElementCollection + // If the vdim is not in the internal mapping than a new PFES will be created + std::shared_ptr GetParFiniteElementSpace(const int region, + const int vdim); + +private: + /** + * @brief Enumeration for volume average calculation types + * + * Provides type-safe identification of different calculation types to avoid + * string comparison overhead and prevent typos. Each type corresponds to + * a specific physical quantity computed in ExaConstit simulations. + */ + enum class CalcType { + STRESS, ///< Cauchy stress tensor (6 components in Voigt notation) + DEF_GRAD, ///< Deformation gradient tensor (9 components) + PLASTIC_WORK, ///< Accumulated plastic work (scalar) + EQ_PL_STRAIN, ///< Equivalent plastic strain (scalar) + EULER_STRAIN, ///< Euler strain tensor (6 components in Voigt notation) + ELASTIC_STRAIN ///< Elastic strain tensor (6 components in Voigt notation) + }; + + /** + * @brief Cached volume average data for a single region + * + * Stores the computed volume average and associated volume for a specific + * region to enable efficient reuse in global calculations. The cache prevents + * redundant quadrature function evaluations and volume integrations. + */ + struct VolumeAverageData { + double volume; ///< Total volume of the region + mfem::Vector data; ///< Volume-averaged quantity (scalar or tensor components) + bool is_valid; ///< Flag indicating whether the data is valid and usable + + /** + * @brief Default constructor for invalid data + */ + VolumeAverageData() : volume(0.0), data(1), is_valid(false) {} + + /** + * @brief Constructor for valid data + * @param vol Total volume of the region + * @param vec Volume-averaged data vector + */ + VolumeAverageData(double vol, const mfem::Vector& vec) + : volume(vol), data(vec), is_valid(true) {} + }; + + /** + * @brief Cache storage for volume average data + * + * Two-level map structure: m_region_cache[calc_type][region_id] = data + * Enables O(1) lookup of cached region data during global calculations. + * Cache is cleared each time step to ensure data freshness. + */ + std::map> m_region_cache; + + /** + * @brief Convert string calculation type to enum + * + * @param calc_type_str String identifier for calculation type + * @return Corresponding CalcType enum value + * + * Provides mapping from user-friendly string names to type-safe enums. + * Used to interface between public string-based API and internal enum-based + * implementation for improved performance and type safety. + */ + CalcType GetCalcType(const std::string& calc_type_str); + + /** + * @brief Calculate volume average for a specific region and calculation type + * + * @param calc_type Type of calculation to perform + * @param region Region index to process + * @return Volume average data containing volume and averaged quantities + * + * Core calculation method that handles all the complexity of: + * - Selecting appropriate quadrature functions + * - Processing state variables for derived quantities + * - Handling special cases (deformation gradient global assignment) + * - Performing volume integration using MFEM kernels + * + * This method encapsulates all calculation-specific logic and provides + * a uniform interface for all volume averaging operations. + */ + VolumeAverageData CalculateVolumeAverage(CalcType calc_type, int region); + + /** + * @brief Get cached data or calculate if not available + * + * @param calc_type Type of calculation + * @param region Region index + * @return Volume average data (from cache or newly calculated) + * + * Implements intelligent caching strategy: + * 1. Check cache for existing valid data + * 2. If found, return cached result (O(1) operation) + * 3. If not found, calculate and cache result for future use + * + * This method optimizes performance for workflows that compute both + * region-specific and global quantities by avoiding redundant calculations. + */ + VolumeAverageData GetOrCalculateVolumeAverage(CalcType calc_type, int region); + + /** + * @brief Registration structure for projection operations + * + * Contains all metadata and objects needed to manage a projection type + * across multiple material regions with appropriate model compatibility + * checking and dynamic enablement control. + */ + struct ProjectionRegistration { + /** + * @brief Unique field identifier for this projection + * + * String key used for projection lookup and grid function naming. + * Examples: "stress", "volume", "centroid", "elastic_strain" + */ + std::string field_name; + + /** + * @brief Human-readable display name + * + * User-friendly name for UI display and error messages. + * Examples: "Cauchy Stress", "Element Volumes", "Crystal Orientations" + */ + std::string display_name; + + /** + * @brief Material model compatibility requirements + * + * Specifies which material model types support this projection. + * Used during registration to avoid creating incompatible projections. + */ + ProjectionTraits::ModelCompatibility model_compatibility; + + /** + * @brief Per-region enablement flags + * + * Boolean vector indicating which regions have this projection enabled. + * Size equals m_num_regions. Allows selective projection execution. + */ + std::vector region_enabled; + + /** + * @brief Projection class instances per region + * + * Vector of ProjectionBase-derived objects, one per region plus one + * for global aggregation if supported. Handles actual projection execution. + */ + std::vector> projection_class; + + /** + * @brief Vector dimensions per region + * + * Stores the vector dimension (number of components) for this projection + * in each region. Used for grid function creation and validation. + */ + std::vector region_length; + + /** + * @brief Global aggregation support flag + * + * Indicates whether this projection type can be aggregated across regions + * into a unified global field. Affects global grid function creation. + */ + bool supports_global_aggregation = false; + }; + + /** + * @brief Registration structure for volume averaging calculations + * + * Contains function pointers and metadata for volume averaging operations + * that compute region-specific and globally aggregated scalar quantities + * from quadrature function data. + */ + struct VolumeAverageRegistration { + /** + * @brief Unique calculation identifier + * + * String key for the volume averaging calculation. + * Examples: "stress", "def_grad", "plastic_work", "equivalent_plastic_strain" + */ + std::string calc_name; + + /** + * @brief Human-readable display name + * + * User-friendly name for output headers and error messages. + * Examples: "Volume Average Stress", "Volume Plastic Work" + */ + std::string display_name; + + /** + * @brief Material model compatibility requirements + * + * Specifies which material models provide the required data for this + * calculation. Most volume averages work with all models. + */ + ProjectionTraits::ModelCompatibility model_compatibility; + + /** + * @brief Per-region enablement flags + * + * Boolean vector indicating which regions should perform this calculation. + * Enables selective volume averaging based on material properties. + */ + std::vector region_enabled; + + /** + * @brief Per-region calculation function + * + * Function pointer for region-specific volume averaging. Signature: + * void(int region, double time). Called for each enabled region. + */ + std::function region_func; + + /** + * @brief Global aggregation function + * + * Function pointer for global volume averaging across all regions. + * Signature: void(double time). Called once per output timestep. + */ + std::function global_func; + + /** + * @brief Global aggregation availability flag + * + * Indicates whether the global_func is available and should be called. + * True when global_func is not nullptr during registration. + */ + bool has_global_aggregation = true; + }; + + /** + * @brief Register a volume averaging calculation function + * + * @param calc_name Unique identifier for the calculation + * @param display_name Human-readable name for output + * @param region_func Function for per-region calculations + * @param global_func Function for global aggregation (optional) + * @param enabled Default enablement state + * + * Adds a new volume averaging calculation to the registered calculations list. + * The region_func is called for each enabled region, while global_func (if + * provided) is called once per timestep for global aggregation. + * + * Used internally by RegisterDefaultVolumeCalculations() to set up + * standard volume averaging operations like stress and strain averaging. + */ + void RegisterVolumeAverageFunction(const std::string& calc_name, + const std::string& display_name, + std::function region_func, + std::function global_func = nullptr, + bool enabled = true); + + /** + * @brief Execute global projection for a specific field + * + * @param field_name Name of the field to project globally + * + * Performs global aggregation of field data across all regions. + * Combines per-region data into unified global grid functions + * for visualization and analysis. The aggregation method depends + * on the field type (additive for extensive quantities, + * volume-weighted for intensive quantities). + * + * Used internally when aggregation mode includes GLOBAL_COMBINED. + */ + void ExecuteGlobalProjection(const std::string& field_name); + + /** + * @brief Combine region data into global grid function + * + * @param field_name Name of the field to combine + * + * Transfers data from region-specific grid functions to the corresponding + * global grid function using ParSubMesh::Transfer() operations. Accumulates + * contributions from all active regions to create unified global fields + * for visualization and analysis. + */ + void CombineRegionDataToGlobal(const std::string& field_name); + + /** + * @brief Initialize data collections for visualization output + * + * @param options Simulation options containing visualization settings + * + * Creates MFEM DataCollection objects (VisIt, ParaView, ADIOS2) based on + * ExaOptions visualization settings. Data collections are created for: + * - Each region when aggregation mode includes PER_REGION + * - Global fields when aggregation mode includes GLOBAL_COMBINED + * + * Configures output directories, file formats, and precision settings + * according to user preferences and registers all grid functions with + * appropriate data collections. + */ + void InitializeDataCollections(ExaOptions& options); + + /** + * @brief Initialize grid functions for all registered projections + * + * Creates ParGridFunction objects for all enabled projections based on + * the current aggregation mode. Grid functions are created for: + * - Per-region projections when mode includes PER_REGION + * - Global aggregated projections when mode includes GLOBAL_COMBINED + * + * Vector dimensions are determined from projection metadata and region + * compatibility. All grid functions are initialized to zero and registered + * with appropriate finite element spaces. + */ + void InitializeGridFunctions(); + + /** + * @brief Check if a region has the required quadrature function + * + * @param field_name Name of the field/quadrature function to check + * @param region Region index to query + * @return true if the region has the specified quadrature function + * + * Queries SimulationState to determine if a specific region contains + * the quadrature function data required for a projection or calculation. + * Used to validate data availability before attempting projections. + */ + bool RegionHasQuadratureFunction(const std::string& field_name, int region) const; + + /** + * @brief Get all active regions for a given field + * + * @param field_name Name of the field to query + * @return Vector of integers indicating which regions are active + * + * Returns a vector of boolean values (as integers) indicating which regions + * have grid functions created for the specified field. Used for global + * aggregation operations to determine which regions to combine. + */ + std::vector GetActiveRegionsForField(const std::string& field_name) const; + + /** + * @brief Clear the volume average cache + * + * Should be called at the beginning of each time step to ensure cache + * freshness and prevent stale data from affecting calculations. Also + * prevents unbounded memory growth over long simulation runs. + * + * @note This method should be called before any volume averaging operations + * in a new time step to ensure data consistency. + */ + void ClearVolumeAverageCache(); + + /** + * @brief Generic volume average calculation for region-specific output + * + * @param calc_type_str String identifier for calculation type + * @param region Region index to process + * @param time Current simulation time for output + * + * Unified interface for all region-specific volume averaging operations. + * This method: + * 1. Converts string type to enum for internal processing + * 2. Calculates or retrieves cached volume average data + * 3. Writes formatted output to appropriate file + * 4. Caches result for potential reuse in global calculations + * + * Supports all calculation types through a single, well-tested code path. + */ + void VolumeAverage(const std::string& calc_type_str, int region, double time); + + /** + * @brief Generic global volume average calculation + * + * @param calc_type_str String identifier for calculation type + * @param time Current simulation time for output + * + * Unified interface for all global volume averaging operations. + * This method: + * 1. Accumulates volume-weighted contributions from all regions + * 2. Uses cached data when available to avoid redundant calculations + * 3. Calculates missing region data on-demand + * 4. Normalizes by total volume to compute global average + * 5. Writes formatted output to global file + * + * The caching system makes this method highly efficient when region-specific + * calculations have already been performed in the same time step. + */ + void GlobalVolumeAverage(const std::string& calc_type_str, double time); + + /** + * @brief Calculate and output volume-averaged stress for a specific region + * + * @param region Region index for calculation + * @param time Current simulation time + * + * Computes volume-averaged Cauchy stress tensor (6 components) for the + * specified region using element-averaged stress values. The calculation + * performs true volume weighting by integrating stress over element volumes. + * + * Output format: Time, Total_Volume, Sxx, Syy, Szz, Sxy, Sxz, Syz + * + * Files are written only by MPI rank 0 to region-specific output files + * managed by PostProcessingFileManager. Headers are added automatically + * for new files. + */ + void VolumeAvgStress(const int region, const double time); + /** + * @brief Volume-averaged Euler strain calculation for a region + * + * @param region Region index for calculation + * @param time Current simulation time + * + * Computes volume-averaged Euler (engineering) strain tensor for the specified + * region. Euler strain is computed as E = 0.5*(F^T*F - I) where F is the + * deformation gradient. Output follows Voigt notation for symmetric tensors. + */ + void VolumeAvgEulerStrain(const int region, const double time); + /** + * @brief Calculate and output volume-averaged deformation gradient for a region + * + * @param region Region index for calculation + * @param time Current simulation time + * + * Computes volume-averaged deformation gradient tensor (9 components) for + * the specified region. The deformation gradient captures finite strain + * deformation including rotation and stretch components. + * + * Output format: Time, Total_Volume, F11, F12, F13, F21, F22, F23, F31, F32, F33 + * + * Essential for finite strain analysis and strain path tracking in + * large deformation simulations. + */ + void VolumeAvgDefGrad(const int region, const double time); + /** + * @brief Volume-averaged plastic work calculation for a region + * + * @param region Region index for calculation + * @param time Current simulation time + * + * Computes volume-averaged plastic work (scalar) for the specified region. + * Plastic work represents energy dissipated through irreversible deformation + * and is essential for energy balance analysis in thermomechanical problems. + */ + void VolumePlWork(const int region, const double time); + /** + * @brief Calculate and output volume-averaged equivalent plastic strain for a region + * + * @param region Region index for calculation + * @param time Current simulation time + * + * Computes volume-averaged equivalent plastic strain (scalar) for the + * specified region. Equivalent plastic strain provides a scalar measure + * of accumulated plastic deformation magnitude. + * + * Output format: Time, Total_Volume, Equivalent_Plastic_Strain + * + * Critical for strain-based failure criteria and plastic strain + * accumulation tracking in fatigue and damage analysis. + */ + void VolumeEPS(const int region, const double time); + /** + * @brief Calculate and output volume-averaged elastic strain for a region + * + * @param region Region index for calculation + * @param time Current simulation time + * + * Computes volume-averaged elastic strain tensor (6 components) for + * ExaCMech regions. Elastic strain represents recoverable deformation + * and is essential for stress-strain relationship analysis. + * + * Output format: Time, Total_Volume, Ee11, Ee22, Ee33, Ee23, Ee13, Ee12 + * + * Only available for ExaCMech material models that explicitly track + * elastic strain state variables. + */ + void VolumeAvgElasticStrain(const int region, const double time); + + /** + * @brief Calculate and output global volume-averaged stress + * + * @param time Current simulation time + * + * Computes global volume-averaged stress by combining contributions from + * all regions with proper volume weighting. Each region's contribution + * is weighted by its total volume before summing and normalizing. + * + * The global calculation provides homogenized stress response across + * all material regions for macroscopic analysis and comparison with + * experimental data. + */ + void GlobalVolumeAvgStress(const double time); + /** + * @brief Global volume-averaged Euler strain calculation + * + * @param time Current simulation time + * + * Computes global Euler strain by volume-weighted averaging across all regions. + * Provides macroscopic strain response for comparison with experimental data + * and validation of material model predictions. + */ + void GlobalVolumeAvgEulerStrain(const double time); + /** + * @brief Calculate and output global volume-averaged deformation gradient + * + * @param time Current simulation time + * + * Computes global volume-averaged deformation gradient by combining + * region contributions with volume weighting. The global deformation + * gradient represents overall specimen deformation for comparison + * with experimental displacement boundary conditions. + */ + void GlobalVolumeAvgDefGrad(const double time); + /** + * @brief Global volume-averaged plastic work calculation + * + * @param time Current simulation time + * + * Computes global plastic work by volume-weighted averaging across all regions. + * Provides total energy dissipation for specimen-level energy balance and + * thermomechanical analysis applications. + */ + void GlobalVolumePlWork(const double time); + /** + * @brief Calculate and output global volume-averaged equivalent plastic strain + * + * @param time Current simulation time + * + * Computes global equivalent plastic strain by volume-weighted averaging + * across all regions. Provides overall plastic strain accumulation for + * macroscopic material characterization and model validation. + */ + void GlobalVolumeEPS(const double time); + /** + * @brief Calculate and output global volume-averaged elastic strain + * + * @param time Current simulation time + * + * Computes global elastic strain by volume-weighted averaging across + * ExaCMech regions. Provides macroscopic elastic response for + * elasticity analysis and unloading behavior characterization. + */ + void GlobalVolumeAvgElasticStrain(const double time); + + /** + * @brief Calculate element-averaged values from partial quadrature function + * + * @param elemVal Output partial quadrature function for element averages + * @param qf Input partial quadrature function with quadrature point data + * + * Computes volume-weighted element averages from quadrature point data. + * The algorithm integrates quadrature point values over each element using + * integration weights and Jacobian determinants, then normalizes by element + * volume to produce true element averages. + * + * Essential for converting quadrature point data to element-constant data + * suitable for visualization and further processing operations. + */ + void CalcElementAvg(mfem::expt::PartialQuadratureFunction* elemVal, + const mfem::expt::PartialQuadratureFunction* qf); + + /** + * @brief Calculate global element averages across all regions + * + * @param elemVal Output global vector for element averages + * @param field_name Name of the field to average + * + * Computes element averages for a field across all regions and combines + * them into a single global vector. Each region's contribution is calculated + * using CalcElementAvg() and then mapped to the appropriate global element + * indices using partial-to-global mappings. + * + * Used for global aggregation operations and cross-region analysis. + */ + void CalcGlobalElementAvg(mfem::Vector* elemVal, const std::string& field_name); + + /** + * @brief Get the size of quadrature functions + * + * @return Size of quadrature functions in the simulation + * + * Returns the total number of quadrature points across all elements + * by querying one of the available quadrature functions. Used for + * memory allocation and loop bounds in global calculations. + */ + size_t GetQuadratureFunctionSize() const; + + /** + * @brief Generate standardized grid function names + * + * @param field_name Base field name + * @param region Region index (-1 for global) + * @return Formatted grid function name + * + * Creates consistent grid function names following the patterns: + * - "field_name_region_X" for region-specific functions + * - "field_name_global" for global aggregated functions + * + * Ensures consistent naming across all grid function operations + * and enables proper lookup in the m_map_gfs container. + */ + std::string GetGridFunctionName(const std::string& field_name, int region = -1) const; + + /** + * @brief Update all field projections for current time step + * + * @param step Current time step number + * @param time Current simulation time + * + * Executes all registered projections based on the current aggregation mode. + * Updates both per-region and global fields depending on configuration. + * This method handles the core projection pipeline including: + * - Element-averaged value computation from quadrature data + * - Region-specific projection execution + * - Global field aggregation when enabled + * + * @note Called internally by Update() before visualization updates + */ + void UpdateFields(const int step, const double time); + + /** + * @brief Register default set of projections based on material models + * + * Automatically registers standard projections based on the material model + * types present in each region. This includes: + * - Geometry projections (centroid, volume) for all regions + * - Stress projections (Cauchy, Von Mises, hydrostatic) for all regions + * - State variable projections for compatible material models + * - ECMech-specific projections for ExaCMech regions + * + * Registration is conditional on material model compatibility and + * availability of required quadrature data in each region. + */ + void RegisterDefaultProjections(); + + /** + * @brief Register default volume average calculations + * + * Sets up standard volume averaging operations including: + * - Stress tensor averaging (per-region and global) + * - Deformation gradient averaging + * - Plastic work averaging + * - Equivalent plastic strain averaging + * - Elastic strain averaging (ExaCMech only) + * + * Each calculation is registered with both per-region and global + * aggregation functions when applicable. + */ + void RegisterDefaultVolumeCalculations(); + + /** + * @brief Register a specific projection by field name + * + * @param field Name of the field to register for projection + * + * Dynamically adds a projection for the specified field name. + * The projection type is determined automatically based on: + * - Field name matching known projection types + * - Material model compatibility in each region + * - Availability of required quadrature data + * + * Supports both built-in projections and custom field names + * with automatic ECMech state variable detection. + */ + void RegisterProjection(const std::string& field); + + /** + * @brief Initialize LightUp analysis instances + * + * Creates and configures LightUp analysis objects based on the + * light_up_configs specified in ExaOptions. Each enabled LightUp + * configuration is instantiated with: + * - Specified HKL directions for lattice strain calculations + * - Distance tolerance for peak detection + * - Sample direction for orientation reference + * - Region-specific quadrature spaces and data + * + * LightUp instances are created only for regions with enabled + * configurations and compatible material models (typically ExaCMech). + */ + void InitializeLightUpAnalysis(); + /** + * @brief Update LightUp analysis for all configured instances + * + * Executes lattice strain calculations for all active LightUp instances. + * Each instance processes state variables and stress data for its + * assigned region to compute: + * - Lattice strains for specified HKL directions + * - Directional stiffness properties + * - Taylor factors and plastic strain rates + * + * Results are written to region-specific output files for post-processing + * and comparison with experimental diffraction data. + */ + void UpdateLightUpAnalysis(); + +private: + /** + * @brief Reference to simulation state for data access + * + * Provides access to all simulation data including quadrature functions, + * mesh information, material properties, and state variables across all regions. + */ + std::shared_ptr m_sim_state; + + /** + * @brief MPI rank of current process + * + * Used for controlling parallel I/O operations and ensuring only rank 0 + * performs file writing operations to avoid race conditions. + */ + int m_mpi_rank; + + /** + * @brief Total number of MPI processes + * + * Total count of parallel processes for coordination of distributed + * post-processing operations and resource allocation. + */ + int m_num_mpi_rank; + + /** + * @brief Material model types for each region + * + * Vector containing the material model type (EXACMECH, UMAT, etc.) for + * each material region. Used to determine projection compatibility and + * enable appropriate post-processing operations per region. + */ + std::vector m_region_model_types; + + /** + * @brief Total number of material regions + * + * Count of distinct material regions in the simulation. Determines the + * number of region-specific projections and volume averaging operations. + */ + size_t m_num_regions; + + /** + * @brief Current aggregation mode for multi-region processing + * + * Controls whether to process regions separately (PER_REGION), combine + * into global fields (GLOBAL_COMBINED), or both (BOTH). Affects which + * grid functions and data collections are created and updated. + */ + AggregationMode m_aggregation_mode; + + /** + * @brief Buffer for element-averaged values per region + * + * Vector of partial quadrature functions used as temporary storage for + * element-averaged calculations. One buffer per region plus one for + * global aggregation operations. + */ + std::vector> m_region_evec; + + /** + * @brief Global element vector for aggregated calculations + * + * MFEM Vector used for storing global element-averaged data when + * combining results from multiple regions. Provides unified storage + * for cross-region calculations and global aggregation operations. + */ + std::unique_ptr m_global_evec; + + /** + * @brief File manager for ExaOptions-compliant output + * + * Handles all file I/O operations including directory creation, filename + * generation, and output frequency control. Ensures consistent file + * organization and naming conventions across all post-processing output. + */ + std::unique_ptr m_file_manager; + + /** + * @brief Nested map for finite element spaces by region and vector dimension + * + * Two-level map: outer key is region index, inner key is vector dimension. + * Stores finite element spaces for different combinations of material regions + * and field vector dimensions. Enables efficient reuse of compatible spaces. + */ + std::map>> m_map_pfes; + + /** + * @brief Submesh storage for region-specific visualization + * + * Maps region index to corresponding ParSubMesh objects. Each submesh + * contains only the elements belonging to a specific material region, + * enabling region-specific visualization and data processing. + */ + std::map> m_map_submesh; + + /** + * @brief Mapping from partial quadrature space to submesh elements + * + * Maps region index to element index mapping arrays. Provides translation + * between local element indices in partial quadrature spaces and global + * element indices in the corresponding submesh. + */ + std::map> m_map_pqs2submesh; + + /** + * @brief Grid function storage for all projections + * + * Maps grid function names to ParGridFunction objects. Names follow the + * pattern "field_region_X" for region-specific or "field_global" for + * aggregated functions. Stores all projected data for visualization. + */ + std::map> m_map_gfs; + + /** + * @brief Data collection storage for visualization output + * + * Maps data collection keys to MFEM DataCollection objects (VisIt, ParaView, + * ADIOS2). Keys follow patterns like "visit_region_X" or "paraview_global". + * Manages all visualization output formats and their associated data. + */ + std::map> m_map_dcs; + + /** + * @brief Registered projection operations + * + * Vector of ProjectionRegistration structures containing metadata and + * instances for all registered projection operations. Enables dynamic + * projection management and execution based on field names and regions. + */ + std::vector m_registered_projections; + + /** + * @brief Registered volume averaging calculations + * + * Vector of VolumeAverageRegistration structures containing function + * pointers and metadata for volume averaging operations. Supports both + * per-region and global aggregated calculations. + */ + std::vector m_registered_volume_calcs; + + /** + * @brief Visualization enablement flag + * + * Controls whether visualization-related operations are performed. + * When false, grid functions and data collections are not created, + * reducing memory usage for simulations that only need volume averaging. + */ + bool m_enable_visualization; + + /** + * @brief Active LightUp analysis instances + * + * Vector of LightUp objects for lattice strain analysis. Each instance + * corresponds to an enabled LightUp configuration from ExaOptions, + * providing in-situ diffraction simulation capabilities. + */ + std::vector> m_light_up_instances; +}; diff --git a/src/postprocessing/postprocessing_file_manager.hpp b/src/postprocessing/postprocessing_file_manager.hpp new file mode 100644 index 0000000..f070029 --- /dev/null +++ b/src/postprocessing/postprocessing_file_manager.hpp @@ -0,0 +1,643 @@ +#pragma once + +#include "options/option_parser_v2.hpp" + +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +/** + * @brief Utility class for managing file paths and directories in PostProcessingDriver + * + * This class handles: + * 1. Proper file naming according to ExaOptions conventions + * 2. Directory creation when needed + * 3. Path resolution (relative vs absolute) + * 4. Error handling for file operations + */ +class PostProcessingFileManager { +public: + /** + * @brief Initialize file manager with ExaOptions + */ + PostProcessingFileManager(const ExaOptions& options); + + /** + * @brief Get the full file path for a volume average output + * + * @param calc_type Type of calculation (e.g., "stress", "def_grad") + * @param region Region index (-1 for global) + * @param region_name Optional region name (if available) + * @return Full file path + */ + std::string GetVolumeAverageFilePath(const std::string& calc_type, + int region = -1, + const std::string& region_name = "") const; + + /** + * @brief Get the output directory path + */ + fs::path GetOutputDirectory() const { + return m_output_directory; + } + /** + * @brief Get the visualization directory path + */ + fs::path GetVizDirectory() const { + return m_output_viz; + } + + /** + * @brief Get the base filename (without extension) + */ + std::string GetBaseFilename() const { + return m_base_filename; + } + + /** + * @brief Create output directory if it doesn't exist + * + * @return true if directory exists or was created successfully + * + * Ensures the main output directory exists before file operations. + * Creates the directory structure using filesystem operations with + * proper error handling. Only MPI rank 0 performs directory creation + * to avoid race conditions in parallel execution. + */ + bool EnsureOutputDirectoryExists(); + + /** + * @brief Create directory if it doesn't exist + * + * @param output_dir Directory path to create + * @param comm MPI communicator associated with a given region + * @return true if directory exists or was created successfully + * + * Generic directory creation utility with filesystem error handling. + * Used for both main output directory and subdirectory creation + * such as visualization output folders. + */ + bool EnsureDirectoryExists(fs::path& output_dir, MPI_Comm comm = MPI_COMM_WORLD); + + /** + * @brief Create and open an output file with proper error handling + * + * @param filepath Full path to the file + * @param append Whether to append to existing file + * @param comm MPI communicator associated with a given region + * @return Unique pointer to opened file stream + */ + std::unique_ptr + CreateOutputFile(const fs::path& filepath, bool append = true, MPI_Comm comm = MPI_COMM_WORLD); + + /** + * @brief Get column header string for volume average output files + * + * @param calc_type Type of calculation + * @return Header string with column descriptions + * + * Provides standardized column headers for volume average output files. + * Headers include time, volume, and appropriate component labels for + * each calculation type (tensor components, scalar values, etc.). + * + * Ensures consistent output format for post-processing tools and + * provides clear documentation of data organization in output files. + */ + std::string GetVolumeAverageHeader(const std::string& calc_type) const; + + /** + * @brief Check if output should occur at the current step + * + * @param step Current time step number + * @return true if output should occur, false otherwise + * + * Implements output frequency control based on ExaOptions configuration. + * Uses modulo operation to determine if current step matches the + * configured output frequency for volume averaging operations. + */ + bool ShouldOutputAtStep(int step) const; + + /** + * @brief Write time and volume with consistent formatting + * + * @param stream Output file stream + * @param time Current simulation time + * @param volume Total volume + * + * Provides consistent formatting for the first two columns + * that appear in all volume average output files. + */ + void WriteTimeAndVolume(std::ofstream& stream, double time, double volume) const { + stream << std::setw(COLUMN_WIDTH) << time << std::setw(COLUMN_WIDTH) << volume; + } + + /** + * @brief Write vector data with consistent column formatting + * + * @param stream Output file stream + * @param data Vector or array containing the data values + * @param size Number of elements to write + * + * Writes each element with proper column width alignment. + * Template allows use with mfem::Vector, std::vector, or C arrays. + */ + template + void WriteVectorData(std::ofstream& stream, const T& data, int size) const { + for (int i = 0; i < size; ++i) { + stream << std::setw(COLUMN_WIDTH) << data[i]; + } + } + + /** + * @brief Write single scalar value with consistent formatting + * + * @param stream Output file stream + * @param value Scalar value to write + * + * Writes a single value with proper column width alignment. + */ + template + void WriteScalarData(std::ofstream& stream, const T& value) const { + stream << std::setw(COLUMN_WIDTH) << value; + } + + /** + * @brief Safe template version that avoids deprecated conversions + */ + template + void WriteVolumeAverage(const std::string& calc_type, + int region, + const std::string& region_name, + double time, + double volume, + const T& data, + int data_size = -1, + MPI_Comm comm = MPI_COMM_WORLD) { + int rank; + MPI_Comm_rank(comm, &rank); + if (rank != 0) + return; + + auto filepath = GetVolumeAverageFilePath(calc_type, region, region_name); + + bool file_exists = fs::exists(filepath); + auto file = CreateOutputFile(filepath, true); + + if (file && file->is_open()) { + if (!file_exists) { + *file << GetVolumeAverageHeader(calc_type); + } + + WriteTimeAndVolume(*file, time, volume); + WriteDataSafe(*file, data, data_size); + *file << "\n" << std::flush; + } + } + +private: + // Column width for scientific notation with 12 digits: "-1.234567890123e-05" + static constexpr int COLUMN_WIDTH = 18; + /** + * @brief Get specific filename for a calculation type + * + * @param calc_type Type of calculation (e.g., "stress", "def_grad") + * @return Filename with extension from ExaOptions configuration + * + * Maps calculation type strings to configured filenames from ExaOptions. + * Supports standard calculation types (stress, deformation gradient, + * plastic work, strains) with fallback to default naming for custom types. + * + * Enables user customization of output filenames through configuration + * while maintaining consistent internal calculation type naming. + */ + std::string GetSpecificFilename(const std::string& calc_type) const; + + /** + * @brief Construct region-specific filename with proper formatting + * + * @param base_name Base filename without extension + * @param extension File extension (including dot) + * @param region Region index + * @param region_name Optional region name for descriptive filenames + * @return Formatted filename with region identifier + * + * Creates region-specific filenames using either region index or + * descriptive region name when available. Handles special formatting + * requirements and ensures consistent naming across all output files. + * + * Format examples: + * - "stress_region_0.txt" (index-based) + * - "stress_grain_austenite.txt" (name-based) + */ + std::string ConstructRegionFilename(const std::string& base_filename, + const std::string& extension, + int region, + const std::string& region_name) const; + +private: + /** + * @brief Configure stream for high-precision output + * + * @param stream Reference to output stream to configure + * @param precision Number of digits of precision (default 15) + * + * Centralizes precision configuration for all output streams. + * Uses scientific notation to ensure consistent formatting for + * small values that might be missed with default precision. + */ + void ConfigureStreamPrecision(std::ofstream& stream, int precision = 8) const { + stream.precision(precision); + stream.setf(std::ios::scientific, std::ios::floatfield); + // Optional: Set width for consistent column alignment + // stream.width(22); // Adjust based on your needs + } + + /** + * @brief Safe data writing that avoids deprecated conversions + */ + void WriteDataSafe(std::ofstream& stream, const mfem::Vector& data, int size) const { + int actual_size = (size > 0) ? size : data.Size(); + for (int i = 0; i < actual_size; ++i) { + stream << std::setw(COLUMN_WIDTH) << data[i]; // Use operator[] instead of conversion + } + } + + void WriteDataSafe(std::ofstream& stream, double data, int /*size*/) const { + stream << std::setw(COLUMN_WIDTH) << data; // Direct scalar value + } + + void WriteDataSafe(std::ofstream& stream, const double* data, int size) const { + for (size_t i = 0; i < static_cast(size); ++i) { + stream << std::setw(COLUMN_WIDTH) << data[i]; // Array access, no pointer dereferencing + } + } + + void WriteDataSafe(std::ofstream& stream, const std::vector& data, int size) const { + const size_t actual_size = (size > 0) ? static_cast(size) : data.size(); + for (size_t i = 0; i < actual_size; ++i) { + stream << std::setw(COLUMN_WIDTH) << data[i]; + } + } + + /** + * @brief Create a centered string within a fixed column width + * + * @param text Text to center + * @param width Total column width + * @return Centered string with padding + * + * Centers text within the specified width using spaces for padding. + * If the text is longer than the width, it will be truncated. + */ + std::string CenterText(const std::string& text, int width) const { + const size_t width_z = static_cast(width); + if (text.length() >= width_z) { + return text.substr(0, width_z); // Truncate if too long + } + + const size_t padding = width_z - text.length(); + const size_t left_pad = padding / 2; + const size_t right_pad = padding - left_pad; // Handle odd padding + + return std::string(left_pad, ' ') + text + std::string(right_pad, ' '); + } + + /** + * @brief Reference to ExaOptions configuration + * + * Provides access to user-specified configuration including output + * directories, filenames, and frequency settings. Used throughout + * the file manager for consistent configuration-driven behavior. + */ + const ExaOptions& m_options; + /** + * @brief Main output directory path + * + * Base directory for all postprocessing output files. Constructed + * from ExaOptions basename and output directory settings with + * proper path formatting and trailing slash handling. + */ + fs::path m_output_directory; + /** + * @brief Visualization output directory path + * + * Subdirectory for visualization files (VisIt, ParaView, ADIOS2). + * Created only when visualization output is enabled in ExaOptions. + * Provides organized separation of data files and visualization files. + */ + fs::path m_output_viz; + /** + * @brief Base filename without extension + * + * Core filename component used for all output files. Derived from + * ExaOptions basename setting and used as the foundation for + * region-specific and calculation-specific filename construction. + */ + std::string m_base_filename; + /** + * @brief Output frequency for volume averaging + * + * Timestep interval for volume average output. Copied from ExaOptions + * volume averaging configuration and used by ShouldOutputAtStep() + * for consistent output timing control. + */ + int m_output_frequency; + + /** + * @brief Cache of opened files to avoid reopening + * + * Weak pointer cache that tracks opened ofstream objects to prevent + * repeated file opening/closing operations. Uses weak_ptr to allow + * automatic cleanup when files are no longer referenced elsewhere. + * Improves performance for frequent output operations to the same files. + */ + mutable std::map> m_file_cache; +}; + +// Implementation + +inline PostProcessingFileManager::PostProcessingFileManager(const ExaOptions& options) + : m_options(options) { + // Use the basename from ExaOptions + m_base_filename = options.basename; + + // Get output directory from volume averages options + m_output_directory = options.post_processing.volume_averages.output_directory; + + // Get output frequency + m_output_frequency = options.post_processing.volume_averages.output_frequency; + + // If output directory is empty, use current directory + if (m_output_directory.empty()) { + m_output_directory = "."; + } + // Resolve symlinks and build path + m_output_directory = fs::weakly_canonical(m_output_directory) / m_base_filename; + + if (options.visualization.visit || options.visualization.paraview || + options.visualization.adios2) { + m_output_viz = m_output_directory / "visualizations"; + } +} + +inline std::string PostProcessingFileManager::GetVolumeAverageFilePath( + const std::string& calc_type, int region, const std::string& region_name) const { + // Get base filename with extension for this calculation type + fs::path specific_filename = GetSpecificFilename(calc_type); + + // Split into base and extension + fs::path extension = specific_filename.extension(); + fs::path base_name = specific_filename; + if (extension.string().empty()) { + extension = ".txt"; + } else { + base_name = base_name.stem(); + } + + fs::path filename; + if (region == -1) { + // Global file + filename = base_name.string() + "_global" + extension.string(); + } else { + // Region-specific file + filename = ConstructRegionFilename( + base_name.string(), extension.string(), region, region_name); + } + + return m_output_directory / filename; +} + +inline std::string +PostProcessingFileManager::GetSpecificFilename(const std::string& calc_type) const { + const auto& vol_opts = m_options.post_processing.volume_averages; + // Map calculation types to specific filenames from ExaOptions + if (calc_type == "stress") { + return vol_opts.avg_stress_fname; + } else if (calc_type == "def_grad") { + return vol_opts.avg_def_grad_fname; + } else if (calc_type == "plastic_work" || calc_type == "pl_work") { + return vol_opts.avg_pl_work_fname; + } else if (calc_type == "euler_strain") { + return vol_opts.avg_euler_strain_fname; + } else if (calc_type == "eps" || calc_type == "eq_pl_strain") { + return vol_opts.avg_eq_pl_strain_fname; + } else if (calc_type == "elastic_strain" || calc_type == "estrain") { + return vol_opts.avg_elastic_strain_fname; + } else { + // Default naming for custom calculation types + return "avg_" + calc_type + ".txt"; + } +} + +inline std::string +PostProcessingFileManager::ConstructRegionFilename(const std::string& base_filename, + const std::string& extension, + int region, + const std::string& region_name) const { + if (!region_name.empty()) { + // Use region name if available + return base_filename + "_region_" + region_name + extension; + } else { + // Use region index + return base_filename + "_region_" + std::to_string(region) + extension; + } +} + +inline bool PostProcessingFileManager::EnsureDirectoryExists(fs::path& output_dir, MPI_Comm comm) { + int rank; + MPI_Comm_rank(comm, &rank); + bool success = false; + if (rank == 0) { + try { + // Use weakly_canonical to resolve as much as possible + // This handles symlinks and normalizes the path + + // Example: output_dir = "./results/test_case1/visualizations" + // where ./results is a symlink to /storage/results + // but test_case1/visualizations doesn't exist yet + + // weakly_canonical will resolve to: + // /storage/results/test_case1/visualizations + fs::path canonical_path = fs::weakly_canonical(output_dir); + + // Now check if this canonical path exists + if (fs::exists(canonical_path)) { + if (!fs::is_directory(canonical_path)) { + std::cerr << "Error: Path exists but is not a directory: " << canonical_path + << std::endl; + success = false; + } else { + std::cout << "Using existing directory: " << canonical_path << std::endl; + output_dir = canonical_path; + success = true; + } + } else { + // Directory doesn't exist, create it + std::cout << "Creating output directory: " << canonical_path << std::endl; + success = fs::create_directories(canonical_path); + if (success) { + output_dir = canonical_path; + } else { + std::cerr << "Warning: Failed to create output directory: " << canonical_path + << std::endl; + } + } + + // Write test remains the same... + if (success) { + fs::path test_file = canonical_path / "test_write.tmp"; + std::ofstream test_stream(test_file); + if (!test_stream.is_open()) { + success = false; + std::cerr << "Warning: Output directory is not writable: " << canonical_path + << std::endl; + } else { + test_stream.close(); + fs::remove(test_file); + } + } + } catch (const fs::filesystem_error& ex) { + success = false; + std::cerr << "Filesystem error when creating directory " << output_dir << ": " + << ex.what() << std::endl; + } catch (const std::exception& ex) { + success = false; + std::cerr << "Error when creating directory " << output_dir << ": " << ex.what() + << std::endl; + } + } + + // Broadcast the potentially updated output_dir to all ranks + std::string path_str = output_dir.string(); + int dir_length = static_cast(path_str.length()); + MPI_Bcast(&dir_length, 1, MPI_INT, 0, comm); + path_str.resize(static_cast(dir_length)); + MPI_Bcast(&path_str[0], dir_length, MPI_CHAR, 0, comm); + output_dir = path_str; + + bool success_t = false; + MPI_Allreduce(&success, &success_t, 1, MPI_C_BOOL, MPI_LOR, comm); + return success_t; +} + +inline bool PostProcessingFileManager::EnsureOutputDirectoryExists() { + bool success = EnsureDirectoryExists(m_output_directory); + if (!m_output_viz.empty()) { + bool viz_success = EnsureDirectoryExists(m_output_viz); + success &= viz_success; + } + + return success; +} + +inline std::unique_ptr +PostProcessingFileManager::CreateOutputFile(const fs::path& filepath, bool append, MPI_Comm comm) { + int rank; + MPI_Comm_rank(comm, &rank); + try { + // Use weakly_canonical to resolve symlinks in the path + fs::path resolved_path = fs::weakly_canonical(filepath); + fs::path dir_path = resolved_path.parent_path(); + + // Ensure directory exists (only check and create if needed) + if (!dir_path.empty() && !fs::exists(dir_path)) { + if (rank == 0) { + std::cout << "Creating directory: " << dir_path << std::endl; + fs::create_directories(dir_path); + } + // Synchronize to ensure directory is created before all ranks proceed + MPI_Barrier(comm); + } + + // Open file + std::ios_base::openmode mode = std::ios_base::out; + if (append) { + mode |= std::ios_base::app; + } + + auto file = std::make_unique(resolved_path, mode); + + if (!file->is_open()) { + if (rank == 0) { + std::cerr << "Warning: Failed to open output file: " << resolved_path << std::endl; + } + return nullptr; + } + // Apply precision configuration + ConfigureStreamPrecision(*file); + return file; + + } catch (const fs::filesystem_error& ex) { + if (rank == 0) { + std::cerr << "Filesystem error when creating file " << filepath << ": " << ex.what() + << std::endl; + } + return nullptr; + } catch (const std::exception& ex) { + if (rank == 0) { + std::cerr << "Error when creating file " << filepath << ": " << ex.what() << std::endl; + } + return nullptr; + } +} + +// Updated GetVolumeAverageHeader method with proper alignment: +inline std::string +PostProcessingFileManager::GetVolumeAverageHeader(const std::string& calc_type) const { + std::ostringstream header; + + // Set formatting for header to match data columns + header << CenterText("# Time", COLUMN_WIDTH); + header << CenterText("Volume", COLUMN_WIDTH); + + if (calc_type == "stress") { + header << CenterText("Sxx", COLUMN_WIDTH); + header << CenterText("Syy", COLUMN_WIDTH); + header << CenterText("Szz", COLUMN_WIDTH); + header << CenterText("Sxy", COLUMN_WIDTH); + header << CenterText("Sxz", COLUMN_WIDTH); + header << CenterText("Syz", COLUMN_WIDTH); + } else if (calc_type == "def_grad") { + header << CenterText("F11", COLUMN_WIDTH); + header << CenterText("F12", COLUMN_WIDTH); + header << CenterText("F13", COLUMN_WIDTH); + header << CenterText("F21", COLUMN_WIDTH); + header << CenterText("F22", COLUMN_WIDTH); + header << CenterText("F23", COLUMN_WIDTH); + header << CenterText("F31", COLUMN_WIDTH); + header << CenterText("F32", COLUMN_WIDTH); + header << CenterText("F33", COLUMN_WIDTH); + } else if (calc_type == "euler_strain") { + header << CenterText("E11", COLUMN_WIDTH); + header << CenterText("E22", COLUMN_WIDTH); + header << CenterText("E33", COLUMN_WIDTH); + header << CenterText("E23", COLUMN_WIDTH); + header << CenterText("E13", COLUMN_WIDTH); + header << CenterText("E12", COLUMN_WIDTH); + } else if (calc_type == "plastic_work" || calc_type == "pl_work") { + header << CenterText("Plastic_Work", COLUMN_WIDTH); + } else if (calc_type == "elastic_strain") { + header << CenterText("Ee11", COLUMN_WIDTH); + header << CenterText("Ee22", COLUMN_WIDTH); + header << CenterText("Ee33", COLUMN_WIDTH); + header << CenterText("Ee23", COLUMN_WIDTH); + header << CenterText("Ee13", COLUMN_WIDTH); + header << CenterText("Ee12", COLUMN_WIDTH); + } else if (calc_type == "eps" || calc_type == "eq_pl_strain") { + header << CenterText("Equiv_Plastic_Strain", COLUMN_WIDTH); // Shortened to fit better + } else { + header << CenterText(calc_type, COLUMN_WIDTH); + } + + header << "\n"; + return header.str(); +} + +inline bool PostProcessingFileManager::ShouldOutputAtStep(int step) const { + return (step % m_output_frequency == 0); +} \ No newline at end of file diff --git a/src/postprocessing/projection_class.cpp b/src/postprocessing/projection_class.cpp new file mode 100644 index 0000000..28664d5 --- /dev/null +++ b/src/postprocessing/projection_class.cpp @@ -0,0 +1,333 @@ +#include "postprocessing/projection_class.hpp" + +#include "utilities/rotations.hpp" +#include "utilities/unified_logger.hpp" + +#include "ECMech_const.h" +#include "SNLS_linalg.h" + +//============================================================================= +// GEOMETRY PROJECTIONS +//============================================================================= +void CentroidProjection::ProjectGeometry(std::shared_ptr grid_function) { + auto* fes = grid_function->ParFESpace(); + auto* mesh = fes->GetMesh(); + const mfem::FiniteElement& el = *fes->GetFE(0); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + + const int nqpts = ir->GetNPoints(); + const int nelems = fes->GetNE(); + const int vdim = mesh->SpaceDimension(); + + const mfem::GeometricFactors* geom = mesh->GetGeometricFactors( + *ir, mfem::GeometricFactors::DETERMINANTS | mfem::GeometricFactors::COORDINATES); + + const double* W = ir->GetWeights().Read(); + const double* const detJ = geom->detJ.Read(); + const auto x_coords = mfem::Reshape(geom->X.Read(), nqpts, vdim, nelems); + + double* centroid_data = grid_function->ReadWrite(); + + // Calculate element centroids + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int ie) { + double vol = 0.0; + for (int iv = 0; iv < vdim; ++iv) { + centroid_data[ie * vdim + iv] = 0.0; + } + + for (int iq = 0; iq < nqpts; ++iq) { + const double wt = detJ[ie * nqpts + iq] * W[iq]; + vol += wt; + for (int iv = 0; iv < vdim; ++iv) { + const double coord = x_coords(iq, iv, ie); + centroid_data[ie * vdim + iv] += coord * wt; + } + } + + const double inv_vol = 1.0 / vol; + for (int iv = 0; iv < vdim; ++iv) { + centroid_data[ie * vdim + iv] *= inv_vol; + } + }); +} + +void VolumeProjection::ProjectGeometry(std::shared_ptr grid_function) { + auto* fes = grid_function->ParFESpace(); + auto* mesh = fes->GetMesh(); + const mfem::FiniteElement& el = *fes->GetFE(0); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + + const int nqpts = ir->GetNPoints(); + const int nelems = fes->GetNE(); + + const mfem::GeometricFactors* geom = mesh->GetGeometricFactors( + *ir, mfem::GeometricFactors::DETERMINANTS); + + const double* const W = ir->GetWeights().Read(); + const double* const detJ = geom->detJ.Read(); + + double* volume_data = grid_function->ReadWrite(); + + // Calculate element volumes + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int ie) { + double vol = 0.0; + for (int iq = 0; iq < nqpts; ++iq) { + vol += detJ[ie * nqpts + iq] * W[iq]; + } + volume_data[ie] = vol; + }); +} + +//============================================================================= +// STRESS-BASED PROJECTIONS +//============================================================================= + +void CauchyStressProjection::ProjectStress( + const std::shared_ptr stress_qf, + std::shared_ptr stress_gf, + mfem::Array& qpts2mesh) { + // Get stress data and compute Von Mises + const int nelems = stress_gf->ParFESpace()->GetNE(); + const auto part_quad_space = stress_qf->GetPartialSpaceShared(); + const int local_nelems = part_quad_space->GetNE(); + + const auto l2g = qpts2mesh.Read(); + const auto stress_data = mfem::Reshape(stress_qf->Read(), 6, local_nelems); + auto stress_gf_data = mfem::Reshape(stress_gf->Write(), 6, nelems); + + // Compute element-averaged Von Mises stress + mfem::forall(local_nelems, [=] MFEM_HOST_DEVICE(int ie) { + const int global_idx = l2g[ie]; + + stress_gf_data(0, global_idx) = stress_data(0, ie); + stress_gf_data(1, global_idx) = stress_data(1, ie); + stress_gf_data(2, global_idx) = stress_data(2, ie); + stress_gf_data(3, global_idx) = stress_data(3, ie); + stress_gf_data(4, global_idx) = stress_data(4, ie); + stress_gf_data(5, global_idx) = stress_data(5, ie); + }); +} + +void VonMisesStressProjection::ProjectStress( + const std::shared_ptr stress_qf, + std::shared_ptr von_mises, + mfem::Array& qpts2mesh) { + // Get stress data and compute Von Mises + const int nelems = von_mises->ParFESpace()->GetNE(); + const auto part_quad_space = stress_qf->GetPartialSpaceShared(); + const int local_nelems = part_quad_space->GetNE(); + + const auto l2g = qpts2mesh.Read(); + const auto stress_data = mfem::Reshape(stress_qf->Read(), 6, local_nelems); + auto von_mises_data = mfem::Reshape(von_mises->Write(), nelems); + + // Compute element-averaged Von Mises stress + mfem::forall(local_nelems, [=] MFEM_HOST_DEVICE(int ie) { + const int global_idx = l2g[ie]; + + double term1 = stress_data(0, ie) - stress_data(1, ie); + double term2 = stress_data(1, ie) - stress_data(2, ie); + double term3 = stress_data(2, ie) - stress_data(0, ie); + double term4 = stress_data(3, ie) * stress_data(3, ie) + + stress_data(4, ie) * stress_data(4, ie) + + stress_data(5, ie) * stress_data(5, ie); + + term1 *= term1; + term2 *= term2; + term3 *= term3; + term4 *= 6.0; + + von_mises_data(global_idx) = sqrt(0.5 * (term1 + term2 + term3 + term4)); + }); +} + +void HydrostaticStressProjection::ProjectStress( + const std::shared_ptr stress_qf, + std::shared_ptr hydro_static, + mfem::Array& qpts2mesh) { + // Get stress data and compute Von Mises + const int nelems = hydro_static->ParFESpace()->GetNE(); + const auto part_quad_space = stress_qf->GetPartialSpaceShared(); + const int local_nelems = part_quad_space->GetNE(); + + const auto l2g = qpts2mesh.Read(); + const auto stress_data = mfem::Reshape(stress_qf->Read(), 6, local_nelems); + auto hydro_static_data = mfem::Reshape(hydro_static->Write(), nelems); + + // Compute element-averaged Von Mises stress + mfem::forall(local_nelems, [=] MFEM_HOST_DEVICE(int ie) { + const int global_idx = l2g[ie]; + + hydro_static_data(global_idx) = ecmech::onethird * + (stress_data(0, ie) + stress_data(1, ie) + + stress_data(2, ie)); + }); +} + +//============================================================================= +// STATE VARIABLE PROJECTIONS +//============================================================================= + +void StateVariableProjection::Execute(std::shared_ptr sim_state, + std::shared_ptr state_gf, + mfem::Array& qpts2mesh, + int region) { + // Get state variable quadrature function for this region + auto state_qf = sim_state->GetQuadratureFunction("state_var_avg", region); + if (!state_qf) + return; // Region doesn't have state variables + + // Project the specific component(s) + const int nelems = state_gf->ParFESpace()->GetNE(); + const auto part_quad_space = state_qf->GetPartialSpaceShared(); + const int local_nelems = part_quad_space->GetNE(); + const int vdim = state_qf->GetVDim(); + m_component_length = (m_component_length == -1) ? vdim : m_component_length; + + if ((m_component_length + m_component_index) > vdim) { + MFEM_ABORT_0("StateVariableProjection provided a length and index that pushes us past the " + "state variable length"); + }; + + if (m_component_length > state_gf->VectorDim()) { + MFEM_ABORT_0("StateVariableProjection provided length is greater than the gridfunction " + "vector length"); + }; + + const auto l2g = qpts2mesh.Read(); + const auto state_qf_data = mfem::Reshape(state_qf->Read(), vdim, local_nelems); + auto state_gf_data = mfem::Reshape(state_gf->Write(), state_gf->VectorDim(), nelems); + + // Compute element-averaged Von Mises stress + const auto component_length = m_component_length; + const auto component_index = m_component_index; + mfem::forall(local_nelems, [=] MFEM_HOST_DEVICE(int ie) { + const int global_idx = l2g[ie]; + for (int j = 0; j < component_length; j++) { + state_gf_data(j, global_idx) = state_qf_data(j + component_index, ie); + } + }); + + // Apply any post-processing + PostProcessStateVariable(state_gf, part_quad_space, qpts2mesh); +} + +void NNegStateProjection::PostProcessStateVariable( + std::shared_ptr grid_function, + [[maybe_unused]] std::shared_ptr qspace, + [[maybe_unused]] mfem::Array& qpts2mesh) const { + auto data = grid_function->Write(); + const int local_nelems = grid_function->Size(); + + mfem::forall(local_nelems, [=] MFEM_HOST_DEVICE(int i) { + data[i] = fmax(data[i], 0.0); + }); +} + +void XtalOrientationProjection::PostProcessStateVariable( + std::shared_ptr grid_function, + std::shared_ptr qspace, + mfem::Array& qpts2mesh) const { + const int nelems = grid_function->ParFESpace()->GetNE(); + auto ori = mfem::Reshape(grid_function->Write(), grid_function->VectorDim(), nelems); + const auto l2g = qpts2mesh.Read(); + const int local_nelems = qspace->GetNE(); + + mfem::forall(local_nelems, [=] MFEM_HOST_DEVICE(int i) { + const int ie = l2g[i]; + const double inv_norm = 1.0 / (ori(0, ie) * ori(0, ie) + ori(1, ie) * ori(1, ie) + + ori(2, ie) * ori(2, ie) + ori(3, ie) * ori(3, ie)); + + ori(0, ie) = ori(0, ie) * inv_norm; + ori(1, ie) = ori(1, ie) * inv_norm; + ori(2, ie) = ori(2, ie) * inv_norm; + ori(3, ie) = ori(3, ie) * inv_norm; + }); +} + +void ElasticStrainProjection::Execute(std::shared_ptr sim_state, + std::shared_ptr elastic_strain_gf, + mfem::Array& qpts2mesh, + int region) { + // Get state variable quadrature function for this region + auto state_qf = sim_state->GetQuadratureFunction("state_var_avg", region); + if (!state_qf) + return; // Region doesn't have state variables + + const int nelems = elastic_strain_gf->ParFESpace()->GetNE(); + const auto part_quad_space = state_qf->GetPartialSpaceShared(); + + const auto l2g = qpts2mesh.Read(); + const int local_nelems = part_quad_space->GetNE(); + const int vdim = state_qf->GetVDim(); + const int gf_vdim = elastic_strain_gf->VectorDim(); + m_component_length = (m_component_length == -1) ? vdim : m_component_length; + + if ((m_component_length + m_component_index) > vdim) { + MFEM_ABORT_0("ElasticStrainProjection provided a length and index that pushes us past the " + "state variable length"); + }; + + if (m_component_length > elastic_strain_gf->VectorDim()) { + MFEM_ABORT_0("ElasticStrainProjection provided length is greater than the gridfunction " + "vector length"); + }; + + const int estrain_ind = + sim_state->GetQuadratureFunctionStatePair("elastic_strain", region).first; + const int quats_ind = sim_state->GetQuadratureFunctionStatePair("quats", region).first; + const int rel_vol_ind = + sim_state->GetQuadratureFunctionStatePair("relative_volume", region).first; + + auto state_vars = sim_state->GetQuadratureFunction("state_var_end", region)->Read(); + auto strain = mfem::Reshape(elastic_strain_gf->Write(), gf_vdim, nelems); + + mfem::forall(local_nelems, [=] MFEM_HOST_DEVICE(int ie) { + const int global_idx = l2g[ie]; + + const auto strain_lat = &state_vars[ie * vdim + estrain_ind]; + const auto quats = &state_vars[ie * vdim + quats_ind]; + const auto rel_vol = state_vars[ie * vdim + rel_vol_ind]; + { + double strainm[3 * 3] = {}; + double* strain_m[3] = {&strainm[0], &strainm[3], &strainm[6]}; + const double t1 = ecmech::sqr2i * strain_lat[0]; + const double t2 = ecmech::sqr6i * strain_lat[1]; + // + // Volume strain is ln(V^e_mean) term aka ln(relative volume) + // Our plastic deformation has a det(1) aka no change in volume change + const double elas_vol_strain = log(rel_vol); + // We output elastic strain formulation such that the relationship + // between V^e and \varepsilon is just V^e = I + \varepsilon + strain_m[0][0] = (t1 - t2) + elas_vol_strain; // 11 + strain_m[1][1] = (-t1 - t2) + elas_vol_strain; // 22 + strain_m[2][2] = ecmech::sqr2b3 * strain_lat[1] + elas_vol_strain; // 33 + strain_m[1][2] = ecmech::sqr2i * strain_lat[4]; // 23 + strain_m[2][0] = ecmech::sqr2i * strain_lat[3]; // 31 + strain_m[0][1] = ecmech::sqr2i * strain_lat[2]; // 12 + + strain_m[2][1] = strain_m[1][2]; + strain_m[0][2] = strain_m[2][0]; + strain_m[1][0] = strain_m[0][1]; + + double rmat[3 * 3] = {}; + double strain_samp[3 * 3] = {}; + + Quat2RMat(quats, rmat); + snls::linalg::rotMatrix<3, false>(strainm, rmat, strain_samp); + + strain_m[0] = &strain_samp[0]; + strain_m[1] = &strain_samp[3]; + strain_m[2] = &strain_samp[6]; + + strain(0, global_idx) = strain_m[0][0]; + strain(1, global_idx) = strain_m[1][1]; + strain(2, global_idx) = strain_m[2][2]; + strain(3, global_idx) = strain_m[1][2]; + strain(4, global_idx) = strain_m[0][2]; + strain(5, global_idx) = strain_m[0][1]; + } + }); +} \ No newline at end of file diff --git a/src/postprocessing/projection_class.hpp b/src/postprocessing/projection_class.hpp new file mode 100644 index 0000000..3485408 --- /dev/null +++ b/src/postprocessing/projection_class.hpp @@ -0,0 +1,822 @@ +#pragma once + +#include "sim_state/simulation_state.hpp" + +#include "mfem.hpp" + +#include +#include + +namespace ProjectionTraits { +/** + * @brief Model compatibility enumeration for projections + */ +enum class ModelCompatibility { + ALL_MODELS, ///< Compatible with all material models + EXACMECH_ONLY, ///< Only compatible with ExaCMech models + UMAT_ONLY ///< Only compatible with UMAT models +}; +} // namespace ProjectionTraits + +/** + * @brief Base projection interface for all projection types in ExaConstit + * + * ProjectionBase provides the fundamental interface that all projection classes + * must implement. It defines the common operations for converting quadrature + * function data to grid function data suitable for visualization and analysis. + * + * Key responsibilities: + * - Execute projection operations for specific material regions + * - Provide vector dimension information for grid function creation + * - Support material model compatibility checking + * - Enable global aggregation capabilities when appropriate + * + * The class uses the template method pattern where derived classes implement + * specific projection algorithms while the base class handles common interface + * requirements and material model compatibility checking. + * + * Material model compatibility is enforced through the ProjectionTraits::ModelCompatibility + * enumeration, allowing projections to specify whether they work with all material + * models, only ExaCMech models, or only UMAT models. + * + * @ingroup ExaConstit_projections + */ +class ProjectionBase { +public: + /** + * @brief Model compatibility type alias + * + * Shorthand for ProjectionTraits::ModelCompatibility enumeration, + * used throughout projection classes to specify material model + * compatibility requirements. + */ + using ptmc = ProjectionTraits::ModelCompatibility; + /** + * @brief Material model compatibility for this projection + * + * Specifies which material model types are compatible with this projection. + * Used during registration to ensure projections are only created for + * appropriate material regions. Defaults to ALL_MODELS for maximum compatibility. + */ + const ptmc model = ptmc::ALL_MODELS; + +public: + ProjectionBase() = default; + ProjectionBase(const ptmc mc) : model(mc) {}; + virtual ~ProjectionBase() = default; + + /** + * @brief Execute the projection for a specific region + * @param sim_state Reference to simulation state + * @param grid_function Target grid function to populate + * @param region Region index + */ + virtual void Execute(std::shared_ptr sim_state, + std::shared_ptr grid_function, + mfem::Array& qpts2mesh, + int region) = 0; + + /** + * @brief Get the vector dimension for this projection + */ + virtual int GetVectorDimension() const = 0; + + /** + * @brief Check if this projection can be aggregated globally across regions + */ + virtual bool CanAggregateGlobally() const { + return false; + } + + /** + * @brief Get a display name for this projection + */ + virtual std::string GetDisplayName() const = 0; +}; + +//============================================================================= +// GEOMETRY PROJECTIONS +//============================================================================= + +/** + * @brief Base class for geometry-based projections that operate directly on mesh data + * + * GeometryProjection specializes ProjectionBase for projections that compute + * geometric quantities directly from mesh topology and coordinates, without + * requiring material-specific quadrature function data. + * + * These projections are inherently region-independent since they depend only + * on mesh geometry rather than material state. Examples include element + * centroids, volumes, and geometric quality measures. + * + * Key characteristics: + * - Region-independent operation (same result regardless of material region) + * - Direct mesh geometry access through finite element spaces + * - No dependency on material model type or quadrature function data + * - Automatic global aggregation support for visualization + * + * Derived classes must implement ProjectGeometry() to perform the actual + * geometric calculations using MFEM's geometric factors and integration rules. + * + * @ingroup ExaConstit_projections_geometry + */ +class GeometryProjection : public ProjectionBase { +public: + GeometryProjection() = default; + ~GeometryProjection() {}; + /** + * @brief Execute geometry projection (region-independent) + * + * @param sim_state Reference to simulation state (unused for geometry) + * @param grid_function Target grid function to populate + * @param qpts2mesh Mapping array (unused for geometry) + * @param region Region index (unused for geometry) + * + * Executes geometry-based projection by calling the pure virtual + * ProjectGeometry() method. Geometry projections are region-independent + * since they depend only on mesh topology and element geometry. + */ + void Execute([[maybe_unused]] std::shared_ptr sim_state, + std::shared_ptr grid_function, + [[maybe_unused]] mfem::Array& qpts2mesh, + [[maybe_unused]] int region) override { + // Geometry projections don't depend on region-specific data + ProjectGeometry(grid_function); + } + + /** + * @brief Check if this projection can be aggregated globally across regions + */ + virtual bool CanAggregateGlobally() const override { + return true; + } + +protected: + /** + * @brief Pure virtual method for geometry calculations + * + * @param grid_function Target grid function to populate with geometry data + * + * Derived classes implement this method to compute geometry-based quantities + * such as element centroids or volumes. The method has direct access to + * mesh geometry through the grid function's finite element space. + */ + virtual void ProjectGeometry(std::shared_ptr grid_function) = 0; +}; + +/** + * @brief Element centroid calculation projection + * + * Computes geometric centroids of mesh elements by integrating coordinate + * positions over element volumes. Provides spatial location information + * for visualization and spatial analysis of simulation results. + * + * The centroid calculation uses numerical integration over each element + * with proper volume weighting to handle arbitrary element shapes and + * polynomial orders. Results are stored as 3D coordinate vectors. + * + * @ingroup ExaConstit_projections_geometry + */ +class CentroidProjection final : public GeometryProjection { +public: + CentroidProjection() = default; + ~CentroidProjection() {}; + + int GetVectorDimension() const override { + return 3; + } // Always 3D coordinates + std::string GetDisplayName() const override { + return "Element Centroids"; + } + +protected: + void ProjectGeometry(std::shared_ptr grid_function) override; +}; + +/** + * @brief Element volume projection for mesh analysis and visualization + * + * VolumeProjection computes the volume of each mesh element through numerical + * integration of the Jacobian determinant over the element domain. This provides + * essential geometric information for volume averaging operations, mesh quality + * assessment, and visualization scaling. + * + * The volume calculation uses MFEM's geometric factors to access pre-computed + * Jacobian determinants at integration points, which are then integrated using + * the appropriate quadrature weights to obtain accurate element volumes for + * arbitrary element shapes and polynomial orders. + * + * Key features: + * - Accurate volume calculation for arbitrary element geometries + * - Support for high-order finite elements through appropriate integration rules + * - Essential for volume-weighted averaging operations in post-processing + * - Useful for mesh quality assessment and adaptive refinement criteria + * + * The computed volumes are stored as scalar values (vector dimension = 1) and + * can be visualized directly or used internally for volume-weighted calculations + * in other post-processing operations. + * + * @ingroup ExaConstit_projections_geometry + */ +class VolumeProjection final : public GeometryProjection { +public: + VolumeProjection() = default; + ~VolumeProjection() {}; + + int GetVectorDimension() const override { + return 1; + } // Scalar volume + std::string GetDisplayName() const override { + return "Element Volumes"; + } + +protected: + void ProjectGeometry(std::shared_ptr grid_function) override; +}; + +//============================================================================= +// STRESS-BASED PROJECTIONS +//============================================================================= + +/** + * @brief Base class for stress-based projections using Cauchy stress tensor data + * + * StressProjection provides a specialized interface for projections that operate + * on stress tensor data from material constitutive models. It handles the common + * pattern of retrieving stress quadrature functions and delegating to derived + * classes for specific stress calculations. + * + * The class expects stress data in Voigt notation with 6 components representing + * the symmetric Cauchy stress tensor: [σ₁₁, σ₂₂, σ₃₃, σ₂₃, σ₁₃, σ₁₂]. + * + * Key features: + * - Automatic stress quadrature function retrieval from simulation state + * - Support for both element-averaged and quadrature point stress data + * - Global aggregation capabilities for multi-region stress analysis + * - Compatible with all material model types that provide stress output + * + * Derived classes implement ProjectStress() to perform specific calculations + * such as equivalent stress measures, stress invariants, or direct component + * extraction for visualization and post-processing. + * + * @ingroup ExaConstit_projections_stress + */ +class StressProjection : public ProjectionBase { +public: + StressProjection() = default; + ~StressProjection() {}; + + void Execute(std::shared_ptr sim_state, + std::shared_ptr grid_function, + mfem::Array& qpts2mesh, + int region) override { + // Get stress quadrature function for this region + auto stress_qf = sim_state->GetQuadratureFunction("cauchy_stress_avg", region); + if (!stress_qf) + return; // Region doesn't have stress data + + // Project the stress calculation + ProjectStress(stress_qf, grid_function, qpts2mesh); + } + + /** + * @brief Check if this projection can be aggregated globally across regions + */ + virtual bool CanAggregateGlobally() const override { + return true; + } + +protected: + /** + * @brief Pure virtual method for stress-specific calculations + * + * @param stress_qf Partial quadrature function containing stress tensor data + * @param grid_function Target grid function to populate with processed stress + * @param qpts2mesh Mapping from local partial space to global element indices + * + * Derived classes implement this method to perform specific stress calculations + * such as Von Mises equivalent stress, hydrostatic stress, or direct stress + * component extraction. The stress data is provided in Voigt notation with + * 6 components: [S11, S22, S33, S23, S13, S12]. + */ + virtual void + ProjectStress(const std::shared_ptr stress_qf, + std::shared_ptr grid_function, + mfem::Array& qpts2mesh) = 0; +}; + +/** + * @brief Full Cauchy stress tensor projection in Voigt notation + * + * CauchyStressProjection extracts and projects the complete Cauchy stress tensor + * from material constitutive models for visualization and analysis. The stress + * components are output in Voigt notation for efficient storage and compatibility + * with standard post-processing workflows. + * + * The projection preserves all six independent components of the symmetric stress + * tensor: [σ₁₁, σ₂₂, σ₃₃, σ₂₃, σ₁₃, σ₁₂], enabling detailed stress field analysis + * and validation of constitutive model predictions. + * + * Key applications: + * - Complete stress field visualization in finite element post-processors + * - Stress validation against analytical solutions or experimental data + * - Input for stress-based failure criteria and damage models + * - Principal stress calculations and stress invariant analysis + * - Multi-axial loading analysis and stress path characterization + * + * The projection is compatible with all material model types that provide + * Cauchy stress output and supports global aggregation for multi-material + * simulations with consistent stress field representation. + * + * @ingroup ExaConstit_projections_stress + */ +class CauchyStressProjection final : public StressProjection { +public: + CauchyStressProjection() = default; + ~CauchyStressProjection() {}; + + int GetVectorDimension() const override { + return 6; + } // Symmetric tensor in Voigt notation + std::string GetDisplayName() const override { + return "Cauchy Stress"; + } + +protected: + virtual void + ProjectStress(const std::shared_ptr stress_qf, + std::shared_ptr stress_gf, + mfem::Array& qpts2mesh) override; +}; + +/** + * @brief Von Mises equivalent stress projection for failure analysis + * + * VonMisesStressProjection computes the Von Mises equivalent stress from the + * Cauchy stress tensor, providing a scalar measure of stress intensity commonly + * used in plasticity theory and failure analysis. The Von Mises stress is + * calculated as the second invariant of the stress deviator tensor. + * + * Mathematical formulation: + * σᵥₘ = √(3/2 * sᵢⱼsᵢⱼ) = √(1/2 * [(σ₁₁-σ₂₂)² + (σ₂₂-σ₃₃)² + (σ₃₃-σ₁₁)² + 6(σ₁₂² + σ₁₃² + σ₂₃²)]) + * + * Key applications: + * - Yield criterion evaluation in metal plasticity + * - Fatigue analysis and life prediction + * - Stress concentration identification + * - Material failure assessment + * - Optimization of component design for stress reduction + * + * The Von Mises stress provides a material-independent measure of stress state + * that can be directly compared with material yield strength and used in + * plasticity models regardless of the specific loading configuration. + * + * @ingroup ExaConstit_projections_stress + */ +class VonMisesStressProjection final : public StressProjection { +public: + VonMisesStressProjection() = default; + ~VonMisesStressProjection() {}; + + int GetVectorDimension() const override { + return 1; + } // Scalar quantity + std::string GetDisplayName() const override { + return "Von Mises Stress"; + } + +protected: + virtual void + ProjectStress(const std::shared_ptr stress_qf, + std::shared_ptr von_mises, + mfem::Array& qpts2mesh) override; +}; + +/** + * @brief Hydrostatic stress projection for pressure analysis + * + * HydrostaticStressProjection computes the hydrostatic (mean normal) stress + * component from the Cauchy stress tensor. The hydrostatic stress represents + * the volumetric part of the stress state and is crucial for analyzing + * pressure-dependent material behavior and volumetric deformation. + * + * Mathematical formulation: + * σₕ = (σ₁₁ + σ₂₂ + σ₃₃) / 3 = (1/3) * tr(σ) + * + * Key applications: + * - Pressure-dependent plasticity models (Drucker-Prager, Mohr-Coulomb) + * - Volumetric strain analysis and compressibility studies + * - Geomechanics and soil mechanics applications + * - Phase transformation analysis under pressure + * - Cavitation and void nucleation studies + * - Bulk modulus validation and material characterization + * + * The hydrostatic stress is particularly important in materials that exhibit + * pressure-sensitive behavior, such as geological materials, polymers, and + * porous media, where the volumetric stress component significantly affects + * material response. + * + * @ingroup ExaConstit_projections_stress + */ +class HydrostaticStressProjection final : public StressProjection { +public: + HydrostaticStressProjection() = default; + ~HydrostaticStressProjection() {}; + + int GetVectorDimension() const override { + return 1; + } // Scalar quantity + std::string GetDisplayName() const override { + return "Hydrostatic Stress"; + } + +protected: + virtual void + ProjectStress(const std::shared_ptr stress_qf, + std::shared_ptr hydro_static, + mfem::Array& qpts2mesh) override; +}; + +//============================================================================= +// STATE VARIABLE PROJECTIONS +//============================================================================= + +/** + * @brief Base class for state variable projections from material constitutive models + * + * StateVariableProjection provides a framework for extracting and projecting + * specific components from material model state variable arrays. This enables + * visualization and analysis of internal material state evolution including + * plastic strains, hardening variables, crystal orientations, and other + * constitutive model-specific quantities. + * + * The class handles the common pattern of: + * 1. Retrieving state variable quadrature functions from simulation state + * 2. Extracting specific components based on index and length specifications + * 3. Copying data to grid functions with proper element mapping + * 4. Applying optional post-processing for data conditioning + * + * Key features: + * - Flexible component extraction with configurable start index and length + * - Automatic dimension validation against available data and target grid functions + * - Material model compatibility checking (ALL_MODELS, EXACMECH_ONLY, UMAT_ONLY) + * - Optional post-processing hook for derived classes (normalization, clamping, etc.) + * - Support for both scalar and vector state variable components + * + * The state variable array organization depends on the specific material model, + * but typically follows a consistent layout within each model type. ExaCMech + * models provide well-defined state variable mappings through SimulationState + * helper methods. + * + * @ingroup ExaConstit_projections_state_variables + */ +class StateVariableProjection : public ProjectionBase { +public: + StateVariableProjection(const std::string& state_var_name, + int component_index = 0, + int component_length = -1, + const std::string& display_name = "", + ptmc mc = ptmc::ALL_MODELS) + : ProjectionBase(mc), m_state_var_name(state_var_name), m_display_name(display_name), + m_component_index(component_index), m_component_length(component_length) {} + + ~StateVariableProjection() {}; + + void Execute(std::shared_ptr sim_state, + std::shared_ptr state_gf, + mfem::Array& qpts2mesh, + int region) override; + + std::string GetDisplayName() const override { + return m_display_name; + } + + int GetVectorDimension() const override { + return m_component_length; + } + +protected: + /** + * @brief Post-processing hook for derived classes + * + * @param grid_function Target grid function containing extracted state data + * @param qspace Partial quadrature space for the region + * @param qpts2mesh Mapping from local to global element indices + * + * Virtual method called after state variable extraction to allow derived + * classes to perform additional processing such as normalization, coordinate + * transformations, or value clamping. Default implementation does nothing. + */ + virtual void PostProcessStateVariable( + [[maybe_unused]] std::shared_ptr grid_function, + [[maybe_unused]] std::shared_ptr qspace, + [[maybe_unused]] mfem::Array& qpts2mesh) const {}; + + /** + * @brief State variable name for SimulationState lookup + * + * Key used to retrieve the appropriate state variable quadrature function + * from SimulationState. Must match the naming conventions used by the + * material model for proper data access. + */ + std::string m_state_var_name; + + /** + * @brief Display name for the state variable name + */ + std::string m_display_name; + + /** + * @brief Starting index of component within state variable vector + * + * Zero-based index indicating the first component to extract from the + * state variable vector at each quadrature point. For scalar quantities, + * this is the direct index. For multi-component data, this is the starting index. + */ + int m_component_index; + + /** + * @brief Number of components to extract (-1 for automatic detection) + * + * Specifies how many consecutive components to extract starting from + * m_component_index. When set to -1, the component length is determined + * automatically based on available data or target grid function dimensions. + */ + int m_component_length; +}; + +/** + * @brief Complete state variable array projection for debugging and analysis + * + * AllStateVariablesProjection extracts and projects the entire state variable + * array from material constitutive models, providing comprehensive access to + * all internal material state information. This projection is primarily used + * for debugging material model implementations and detailed analysis of + * constitutive model behavior. + * + * Key characteristics: + * - Projects all available state variables without filtering + * - Vector dimension determined automatically from material model + * - Useful for debugging constitutive model implementations + * - Enables detailed analysis of material state evolution + * - Cannot be aggregated globally due to variable interpretation complexity + * + * The interpretation of state variable components depends entirely on the + * specific material model implementation: + * - ExaCMech: Includes plastic strains, hardening variables, orientations, etc. + * - UMAT: User-defined state variables with model-specific meanings + * - Other models: Model-specific internal state representations + * + * This projection is typically used during material model development, + * validation, and debugging rather than for routine post-processing and + * visualization of simulation results. + * + * @note The output requires detailed knowledge of the material model's + * state variable organization for proper interpretation. + * + * @ingroup ExaConstit_projections_state_variables + */ +class AllStateVariablesProjection final : public StateVariableProjection { +public: + AllStateVariablesProjection() : StateVariableProjection("all_state_vars", 0, -1) {} + + AllStateVariablesProjection([[maybe_unused]] const std::string& state_var_name, + [[maybe_unused]] int component_index, + [[maybe_unused]] int component_length, + [[maybe_unused]] const std::string& display_name) + : StateVariableProjection("all_state_vars", 0, -1, "All State Variables") {} + + ~AllStateVariablesProjection() {}; + virtual bool CanAggregateGlobally() const override { + return false; + } +}; + +/** + * @brief Post-processing projection for physically non-negative state variables + * + * This class provides post-processing functionality for state variables that must be + * physically non-negative, ensuring numerical stability and physical consistency in + * finite element simulations. The projection applies a lower bound of zero to all + * computed values, preventing numerical artifacts from producing unphysical negative + * quantities. + * + * @details + * During finite element computations, numerical errors, interpolation artifacts, or + * convergence issues can occasionally produce small negative values for quantities that + * should be strictly non-negative (e.g., equivalent plastic strain, damage parameters, + * void fractions). This projection enforces the physical constraint by applying: + * + * \f$ \tilde{q} = \max(q, 0) \f$ + * + * where \f$q\f$ is the computed state variable and \f$\tilde{q}\f$ is the corrected value. + * + * @note Currently optimized for ExaCMech constitutive models but designed to be + * extensible to other material model frameworks requiring non-negative state variables. + * + * @par Typical Use Cases: + * - Equivalent plastic strain (\f$\varepsilon^p_{eq}\f$) + * - Damage variables (\f$D\f$) + * - Void fraction in porous materials (\f$f\f$) + * - Hardening variables (\f$\kappa\f$) + * - Any physically bounded scalar state variables + * + * @par Performance Characteristics: + * - Device-compatible (GPU/CPU) through MFEM forall + * - O(N) complexity where N is the number of degrees of freedom + * - Supports global aggregation for parallel post-processing + * + * @warning This projection modifies the computed field values. Ensure this is + * appropriate for your analysis before enabling. + * + * @ingroup ExaConstit_projections_state_variables + * @see StateVariableProjection for base class functionality + * @see PostProcessing.projections configuration options + */ +class NNegStateProjection final : public StateVariableProjection { +public: + /** + * @brief Construct a non-negative state variable projection + * + * @param state_var_name Name of the state variable in the material model + * @param component_index Index of the specific component (for tensor/vector state vars) + * @param component_length Total number of components in the state variable + * @param display_name Human-readable name for visualization and output + * + * @note The projection currently defaults to EXACMECH_ONLY compatibility but + * the implementation is model-agnostic and could be extended to other + * constitutive frameworks. + */ + NNegStateProjection(const std::string& state_var_name, + int component_index, + int component_length, + const std::string& display_name) + : StateVariableProjection(state_var_name, + component_index, + component_length, + display_name, + ptmc::EXACMECH_ONLY) {} + ~NNegStateProjection() = default; + + virtual bool CanAggregateGlobally() const override { + return true; + } + +protected: + /** + * @brief Apply non-negative constraint to state variable field + * + * Performs element-wise maximum operation with zero to ensure all field + * values satisfy the non-negative constraint. Uses MFEM's device-portable + * forall construct for optimal performance on both CPU and GPU architectures. + * + * @param grid_function Shared pointer to the MFEM grid function containing state data + * @param qspace Quadrature space (unused in this projection) + * @param qpts2mesh Quadrature point to mesh mapping (unused in this projection) + * + * @note The mathematical operation applied is: data[i] = max(data[i], 0.0) + * for all degrees of freedom i in the local element range. + */ + virtual void PostProcessStateVariable( + std::shared_ptr grid_function, + [[maybe_unused]] std::shared_ptr qspace, + [[maybe_unused]] mfem::Array& qpts2mesh) const override; +}; + +/** + * @brief Crystal orientation projection with quaternion normalization + * + * Projects crystal orientation quaternions from ExaCMech models with + * automatic normalization to ensure unit quaternions. Handles quaternion + * data extraction and post-processing for texture analysis applications. + * + * The post-processing step normalizes quaternions to correct for numerical + * drift during simulation and ensure valid rotation representations. + * Output quaternions follow the convention [q0, q1, q2, q3] where q0 + * is the scalar component. + * + * Only compatible with ExaCMech material models that provide quaternion + * orientation data in their state variable arrays. + * + * @ingroup ExaConstit_projections_crystal_plasticity + */ +class XtalOrientationProjection final : public StateVariableProjection { +public: + XtalOrientationProjection(const std::string& state_var_name, + int component_index, + int component_length, + const std::string& display_name) + : StateVariableProjection(state_var_name, + component_index, + component_length, + display_name, + ptmc::EXACMECH_ONLY) {} + ~XtalOrientationProjection() = default; + + virtual bool CanAggregateGlobally() const override { + return true; + } + +protected: + virtual void + PostProcessStateVariable(std::shared_ptr grid_function, + std::shared_ptr qspace, + mfem::Array& qpts2mesh) const override; +}; + +/** + * @brief Elastic strain tensor projection for ExaCMech models + * + * Projects elastic strain tensor components from ExaCMech state variables + * with coordinate system transformations. Performs conversion from lattice + * coordinates to sample coordinates using crystal orientation data. + * + * The projection involves: + * 1. Extraction of deviatoric elastic strain and relative volume + * 2. Reconstruction of full elastic strain tensor in lattice coordinates + * 3. Rotation to sample coordinates using quaternion orientations + * 4. Output in Voigt notation: [E11, E22, E33, E23, E13, E12] + * + * Only compatible with ExaCMech models that provide elastic strain + * state variables and crystal orientation data. + * + * @ingroup ExaConstit_projections_strain + */ +class ElasticStrainProjection final : public StateVariableProjection { +public: + ElasticStrainProjection(const std::string& state_var_name, + int component_index, + int component_length, + const std::string& display_name) + : StateVariableProjection(state_var_name, + component_index, + component_length, + display_name, + ptmc::EXACMECH_ONLY) { + m_component_length = 6; + } + /** + * @brief Execute elastic strain projection with coordinate transformation + * + * @param sim_state Reference to simulation state for data access + * @param elastic_strain_gf Target grid function for elastic strain output + * @param qpts2mesh Mapping from local to global element indices + * @param region Material region identifier + * + * Overrides the base StateVariableProjection::Execute() method to implement + * specialized processing for elastic strain data. The method: + * + * 1. Retrieves state variables including elastic strain, orientations, and volume + * 2. Reconstructs full 3x3 elastic strain tensor from deviatoric components + * 3. Applies coordinate transformation from crystal to sample coordinates + * 4. Outputs strain components in Voigt notation + * + * The coordinate transformation accounts for crystal orientation evolution + * and ensures strain components are expressed in the global reference frame + * for visualization and post-processing compatibility. + * + * @note This method bypasses the standard StateVariableProjection data flow + * due to the complex coordinate transformations required. + */ + void Execute(std::shared_ptr sim_state, + std::shared_ptr elastic_strain_gf, + mfem::Array& qpts2mesh, + int region) override; + + virtual bool CanAggregateGlobally() const override { + return true; + } +}; + +/** + * @brief Shear rate projection for crystal plasticity analysis + * + * Projects macroscopic shear rate data from ExaCMech state variables. + * Provides access to overall plastic deformation rates for rate-dependent + * analysis and comparison with experimental strain rate measurements. + * + * This projection extracts aggregate shear rate information rather than + * individual slip system rates, making it suitable for macroscopic + * deformation analysis and rate sensitivity studies. + * + * Only compatible with ExaCMech material models that compute and store + * macroscopic shear rate data. + * + * @ingroup ExaConstit_projections_crystal_plasticity + */ +class ShearingRateProjection final : public StateVariableProjection { +public: + ShearingRateProjection(const std::string& state_var_name, + int component_index, + int component_length, + const std::string& display_name) + : StateVariableProjection(state_var_name, + component_index, + component_length, + display_name, + ptmc::EXACMECH_ONLY) {} + + virtual bool CanAggregateGlobally() const override { + return false; + } +}; \ No newline at end of file diff --git a/src/sim_state/simulation_state.cpp b/src/sim_state/simulation_state.cpp new file mode 100644 index 0000000..0266248 --- /dev/null +++ b/src/sim_state/simulation_state.cpp @@ -0,0 +1,1301 @@ +#include "sim_state/simulation_state.hpp" + +namespace { + +void setupBoundaryConditions(ExaOptions& options) { + BCManager& bcm = BCManager::GetInstance(); + auto& bcs_opts = options.boundary_conditions; + bcm.Init(bcs_opts.time_info.cycles, + bcs_opts.map_ess_vel, + bcs_opts.map_ess_vgrad, + bcs_opts.map_ess_comp, + bcs_opts.map_ess_id); +} + +void setBdrConditions(mfem::Mesh& mesh) { + // modify MFEM auto cuboidal hex mesh generation boundary + // attributes to correspond to correct ExaConstit boundary conditions. + // Look at ../../mesh/mesh.cpp Make3D() to see how boundary attributes + // are set and modify according to ExaConstit convention + + // loop over boundary elements + for (int i = 0; i < mesh.GetNBE(); ++i) { + int bdrAttr = mesh.GetBdrAttribute(i); + + switch (bdrAttr) { + // note, srw wrote SetBdrAttribute() in ../../mesh/mesh.hpp + case 1: + mesh.SetBdrAttribute(i, 1); // bottom + break; + case 2: + mesh.SetBdrAttribute(i, 3); // front + break; + case 3: + mesh.SetBdrAttribute(i, 5); // right + break; + case 4: + mesh.SetBdrAttribute(i, 6); // back + break; + case 5: + mesh.SetBdrAttribute(i, 2); // left + break; + case 6: + mesh.SetBdrAttribute(i, 4); // top + break; + } + } + + return; +} + +void setElementGrainIDs(mfem::Mesh& mesh, const mfem::Vector& grainMap, int ncols, int offset) { + // after a call to reorderMeshElements, the elements in the serial + // MFEM mesh should be ordered the same as the input grainMap + // vector. Set the element attribute to the grain id. This vector + // has stride of 4 with the id in the 3rd position indexing from 0 + + const double* data = grainMap.HostRead(); + + // loop over elements + for (int i = 0; i < mesh.GetNE(); ++i) { + const int grainID = static_cast(data[ncols * i + offset]); + mesh.SetAttribute(i, grainID); + } + + return; +} + +// Projects the element attribute to GridFunction nodes +// This also assumes this the GridFunction is an L2 FE space +// void projectElemAttr2GridFunc(std::shared_ptr mesh, +// std::shared_ptr elem_attr) { +// // loop over elementsQ +// elem_attr->HostRead(); +// mfem::ParFiniteElementSpace *pfes = elem_attr->ParFESpace(); +// mfem::Array vdofs; +// for (int i = 0; i < mesh->GetNE(); ++i) { +// pfes->GetElementVDofs(i, vdofs); +// const double ea = static_cast(mesh->GetAttribute(i)); +// elem_attr->SetSubVector(vdofs, ea); +// } +// } + +std::shared_ptr makeMesh(ExaOptions& options, const int my_id) { + mfem::Mesh mesh; + if (options.mesh.mesh_type == MeshType::FILE) { + if (my_id == 0) { + std::cout << "Opening mesh file: " << options.mesh.mesh_file << std::endl; + } + + mesh = mfem::Mesh(options.mesh.mesh_file.string().c_str(), 1, 1, true); + } + // We're using the auto mesh generator + else { + if (options.mesh.nxyz[0] <= 0 || options.mesh.mxyz[0] <= 0) { + std::cerr << std::endl + << "Must input mesh geometry/discretization for hex_mesh_gen" << std::endl; + } + + if (my_id == 0) { + std::cout << "Using mfem's hex mesh generator" << std::endl; + } + + // use constructor to generate a 3D cuboidal mesh with 8 node hexes + // The false at the end is to tell the inline mesh generator to use the lexicographic + // ordering of the mesh The newer space-filling ordering option that was added in the + // pre-okina tag of MFEM resulted in a noticeable divergence of the material response for a + // monotonic tension test using symmetric boundary conditions out to 1% strain. + mesh = mfem::Mesh::MakeCartesian3D(options.mesh.nxyz[0], + options.mesh.nxyz[1], + options.mesh.nxyz[2], + mfem::Element::HEXAHEDRON, + options.mesh.mxyz[0], + options.mesh.mxyz[1], + options.mesh.mxyz[2], + false); + // read in the grain map if using a MFEM auto generated cuboidal mesh + if (options.grain_file) { + std::ifstream gfile(*options.grain_file); + if (!gfile && my_id == 0) { + std::cerr << std::endl + << "Cannot open grain map file: " << *options.grain_file << std::endl; + } + + const int gmap_size = mesh.GetNE(); + mfem::Vector gmap(gmap_size); + gmap.Load(gfile, gmap_size); + gfile.close(); + + // set grain ids as element attributes on the mesh + // The offset of where the grain index is located is + // location - 1. + ::setElementGrainIDs(mesh, gmap, 1, 0); + } + //// reorder elements to conform to ordering convention in grain map file + // No longer needed for the CA stuff. It's now ordered as X->Y->Z + // reorderMeshElements(mesh, &toml_opt.nxyz[0]); + + // reset boundary conditions from + ::setBdrConditions(mesh); + } + + // We need to check to see if our provided mesh has a different order than + // the order provided. If we see a difference we either increase our order seen + // in the options file or we increase the mesh ordering. I'm pretty sure this + // was causing a problem earlier with our auto-generated mesh and if we wanted + // to use a higher order FE space. + // So we can't really do the GetNodalFESpace it appears if we're given + // an initial mesh. It looks like NodalFESpace is initially set to + // NULL and only if we swap the mesh nodes does this actually + // get set... + // So, we're just going to set the mesh order to at least be 1. Although, + // I would like to see this change sometime in the future. + int mesh_order = 1; + if (mesh_order > options.mesh.order) { + options.mesh.order = mesh_order; + } + if (mesh_order <= options.mesh.order) { + if (my_id == 0) { + std::cout << "Increasing mesh order of the mesh to " << options.mesh.order << std::endl; + } + mesh_order = options.mesh.order; + mesh.SetCurvature(mesh_order); + } + + // mesh refinement if specified in input + for (int lev = 0; lev < options.mesh.ref_ser; lev++) { + mesh.UniformRefinement(); + } + + std::shared_ptr pmesh = std::make_shared(MPI_COMM_WORLD, mesh); + + for (int lev = 0; lev < options.mesh.ref_par; lev++) { + pmesh->UniformRefinement(); + } + pmesh->SetAttributes(); + + return pmesh; +} + +std::map create_grains_to_map(const ExaOptions& options, const mfem::Array& grains) { + std::map grain2regions; + + if (!options.region_mapping_file) { + for (const auto& item : grains) { + const int key = item; + grain2regions.emplace(key, 1); + } + } else { + std::ifstream file(*options.region_mapping_file); + + if (!file.is_open()) { + std::cerr << "Failed to open file: " << *options.region_mapping_file << std::endl; + } + + std::string line; + int key, value; + size_t lineNumber = 0; + + while (std::getline(file, line)) { + ++lineNumber; + if (line.empty()) { + continue; // Skip empty lines + } + std::istringstream iss(line); + if (!(iss >> key >> value)) { + std::cerr << "Error reading data on line " << lineNumber << " key " << key + << " line " << line << std::endl; + continue; + } + // Insert into the map + // Since keys are assumed to be unique, this won't overwrite any existing entry. + grain2regions.emplace(key, value); + } + file.close(); + } + + return grain2regions; +} + +/** + * @brief Helper function to initialize deformation gradient QuadratureFunction to identity + */ +void initializeDeformationGradientToIdentity(mfem::expt::PartialQuadratureFunction& defGrad) { + // This function would need to be implemented to properly initialize + // a 9-component QuadratureFunction representing 3x3 identity matrices + // at each quadrature point + + double* data = defGrad.HostReadWrite(); + const int npts = defGrad.Size() / defGrad.GetVDim(); + + // Initialize each 3x3 matrix to identity + for (int i = 0; i < npts; i++) { + double* mat = &data[i * 9]; + // Set to identity: [1,0,0,0,1,0,0,0,1] + mat[0] = 1.0; + mat[1] = 0.0; + mat[2] = 0.0; // first row + mat[3] = 0.0; + mat[4] = 1.0; + mat[5] = 0.0; // second row + mat[6] = 0.0; + mat[7] = 0.0; + mat[8] = 1.0; // third row + } +} + +} // end namespace + +TimeManagement::TimeManagement(ExaOptions& options) : time_type(options.time.time_type) { + if (time_type == TimeStepType::FIXED) { + dt = options.time.fixed_time->dt; + dt_fixed = dt; + dt_min = std::pow(dt_scale, max_failures) * dt; + time_final = options.time.fixed_time->t_final; + } else if (time_type == TimeStepType::AUTO) { + dt = options.time.auto_time->dt_start; + dt_min = options.time.auto_time->dt_min; + dt_max = options.time.auto_time->dt_max; + dt_scale = options.time.auto_time->dt_scale; + time_final = options.time.auto_time->t_final; + max_nr_steps = static_cast(options.solvers.nonlinear_solver.iter); + // insert logic to write out the first time step maybe? + } else if (time_type == TimeStepType::CUSTOM) { + // const auto dt_beg = options.time.custom_time->dt_values.begin(); + // const auto dt_end = options.time.custom_time->dt_values.end(); + custom_dt = options.time.custom_time->dt_values; + dt = custom_dt[0]; + dt_min = std::pow(dt_scale, max_failures) * + static_cast(*std::min_element(custom_dt.begin(), custom_dt.end())); + time_final = std::accumulate(custom_dt.begin(), custom_dt.end(), 0.0); + } + + prev_dt = dt; + // Set our first cycle to the initial dt value; + time = dt; + + const double tf_dt = std::abs(time_final - dt); + if (tf_dt <= std::abs(1e-3 * dt)) { + internal_tracker = TimeStep::FINAL; + } +} + +TimeStep TimeManagement::UpdateDeltaTime(const int nr_steps, const bool success) { + // If simulation failed we want to scale down our dt by some factor + if (!success) { + // If we were already sub-stepping through a simulation and encouter this just fail out + if (internal_tracker == TimeStep::SUBSTEP) { + return TimeStep::FAILED; + } + // For the very first failure we want to save off the initial guessed time step + if (num_failures == 0) { + dt_orig = dt; + } + // reset the time, update dt, and then update the time to correct time + ResetTime(); + dt *= dt_scale; + if (dt < dt_min) { + dt = dt_min; + } + UpdateTime(); + num_failures++; + num_sub_steps = 1; + if (internal_tracker == TimeStep::FINAL) { + const double tf_dt = std::abs(time - time_final); + if (tf_dt > std::abs(1e-3 * dt)) { + internal_tracker = TimeStep::RETRIAL; + } + } + // If we've failed too many times just give up at this point + if (num_failures > max_failures) { + return TimeStep::FAILED; + } + // else we need to let the simulation now it's retrying it's time step again + else { + return TimeStep::RETRIAL; + } + } + + old_time = time; + + if (internal_tracker == TimeStep::FINAL) { + internal_tracker = TimeStep::FINISHED; + return TimeStep::FINISHED; + } + // This means we had a successful time step but previously we failed + // Since we were using a fixed / custom dt here that means we need to substep + // to get our desired dt that the user was asking for + if (num_failures > 0) { + required_num_sub_steps = (time_type != TimeStepType::AUTO) + ? (static_cast(1.0 / std::pow(dt_scale, num_failures))) + : 0; + num_failures = 0; + } + // If sub-stepping through our original dt then need to update the time while we go along + if ((num_sub_steps < required_num_sub_steps) and (time_type != TimeStepType::AUTO)) { + num_sub_steps += 1; + UpdateTime(); + internal_tracker = TimeStep::SUBSTEP; + return TimeStep::SUBSTEP; + } + + prev_dt = dt; + simulation_cycle++; + // update our time based on the following logic + if (time_type == TimeStepType::AUTO) { + // update the dt + const double niter_scale = static_cast(max_nr_steps) * dt_scale; + const int nr_temp = (nr_steps == 0) ? 1 : nr_steps; + const double nr_iter = static_cast(nr_temp); + // Will approach dt_scale as nr_iter -> newton_iter + // dt increases as long as nr_iter > niter_scale + const double factor = niter_scale / nr_iter; + dt *= factor; + if (dt < dt_min) { + dt = dt_min; + } + if (dt > dt_max) { + dt = dt_max; + } + } else if (time_type == TimeStepType::CUSTOM) { + dt = custom_dt[simulation_cycle]; + } else { + dt = dt_fixed; + } + const double tnew = time + dt; + const double tf_dt = std::abs(tnew - time_final); + if (tf_dt <= std::abs(1e-3 * dt)) { + internal_tracker = TimeStep::FINAL; + time = tnew; + return TimeStep::FINAL; + } else if ((tnew - time_final) > 0) { + internal_tracker = TimeStep::FINAL; + dt = time_final - time; + time = time_final; + return TimeStep::FINAL; + } + time = tnew; + // We're back on a normal time stepping procedure + internal_tracker = TimeStep::NORMAL; + return TimeStep::NORMAL; +} + +bool TimeManagement::BCTime(const double desired_bc_time) { + // if time is already past the desired_bc_time before updating this then we're not going to + // update things to nail it + if (time > desired_bc_time) { + return false; + } + const double tnew = time + dt; + const double tf_dt = desired_bc_time - tnew; + // First check if we're when the radius when the next time step would be don't care about sign + // yet + if (std::abs(tf_dt) < std::abs(dt)) { + // Now only update the dt value if we're past the original value + if (tf_dt < 0.0) { + ResetTime(); + dt += tf_dt; + UpdateTime(); + return true; + } + } + return false; +} + +SimulationState::SimulationState(ExaOptions& options) + : m_time_manager(options), m_options(options), class_device(options.solvers.rtmodel) { + MPI_Comm_rank(MPI_COMM_WORLD, &my_id); + m_time_manager = TimeManagement(options); + m_mesh = ::makeMesh(options, my_id); + ::setupBoundaryConditions(options); + // m_bc_manager = BCManager::GetInstance(); + + // Set-up the mesh FEC and PFES + { + const int space_dim = m_mesh->SpaceDimension(); + std::string mesh_fec_str = "H1_" + std::to_string(space_dim) + "D_P" + + std::to_string(options.mesh.order); + m_map_fec[mesh_fec_str] = std::make_shared(options.mesh.order, + space_dim); + m_mesh_fes = std::make_shared( + m_mesh.get(), m_map_fec[mesh_fec_str].get(), space_dim); + } + + // Set-up our various mesh nodes / mesh QoI and + // primal variables + { + // Create our mesh nodes + m_mesh_nodes["mesh_current"] = std::make_shared(m_mesh_fes.get()); + // Create our mesh nodes + m_mesh_nodes["mesh_t_beg"] = std::make_shared(m_mesh_fes.get()); + // Create our mesh nodes + m_mesh_nodes["mesh_ref"] = std::make_shared(m_mesh_fes.get()); + + // Set them to the current default vaules + m_mesh->GetNodes(*m_mesh_nodes["mesh_current"]); + (*m_mesh_nodes["mesh_t_beg"]) = *m_mesh_nodes["mesh_current"]; + (*m_mesh_nodes["mesh_ref"]) = *m_mesh_nodes["mesh_current"]; + + { + mfem::GridFunction* nodes = + m_mesh_nodes["mesh_current"] + .get(); // set a nodes grid function to global current configuration + int owns_nodes = 0; + m_mesh->SwapNodes(nodes, owns_nodes); // m_mesh has current configuration nodes + delete nodes; + } + + m_mesh_qoi_nodes["displacement"] = std::make_shared( + m_mesh_fes.get()); + + m_mesh_qoi_nodes["velocity"] = std::make_shared(m_mesh_fes.get()); + + (*m_mesh_qoi_nodes["displacement"]) = 0.0; + (*m_mesh_qoi_nodes["velocity"]) = 0.0; + // This is our velocity field + m_primal_field = std::make_shared(m_mesh_fes->TrueVSize()); + m_primal_field->UseDevice(true); + m_primal_field_prev = std::make_shared(m_mesh_fes->TrueVSize()); + m_primal_field_prev->UseDevice(true); + (*m_primal_field) = 0.0; + (*m_primal_field_prev) = 0.0; + } + + { + const int space_dim = m_mesh->SpaceDimension(); + std::string l2_fec_str = "L2_" + std::to_string(space_dim) + "D_P" + std::to_string(0); + m_map_fec[l2_fec_str] = std::make_shared(0, space_dim); + } + + // Global QuadratureSpace Setup and QFs + const int int_order = 2 * options.mesh.order + 1; + { + mfem::Array global_index; + m_map_qs["global"] = std::make_shared( + m_mesh, int_order, global_index); + + m_map_qs["global_ord_0"] = std::make_shared( + m_mesh, 1, global_index); + + m_map_qfs["cauchy_stress_beg"] = std::make_shared( + m_map_qs["global"], 6, 0.0); + m_map_qfs["cauchy_stress_end"] = std::make_shared( + m_map_qs["global"], 6, 0.0); + m_map_qfs["cauchy_stress_avg"] = std::make_shared( + m_map_qs["global_ord_0"], 6, 0.0); + + m_map_qfs["cauchy_stress_beg"]->operator=(0.0); + m_map_qfs["cauchy_stress_end"]->operator=(0.0); + m_map_qfs["cauchy_stress_avg"]->operator=(0.0); + + m_model_update_qf_pairs.push_back(std::make_pair("cauchy_stress_beg", "cauchy_stress_end")); + + auto kinetic_grads_name = GetQuadratureFunctionMapName("kinetic_grads", -1); + m_map_qfs[kinetic_grads_name] = std::make_shared( + m_map_qs["global"], 9, 0.0); + ::initializeDeformationGradientToIdentity(*m_map_qfs[kinetic_grads_name]); + + auto tangent_stiffness_name = GetQuadratureFunctionMapName("tangent_stiffness", -1); + m_map_qfs[tangent_stiffness_name] = std::make_shared( + m_map_qs["global"], 36, 0.0); + m_map_qfs[tangent_stiffness_name]->operator=(0.0); + } + + // Material state variable and qspace setup + { + // Update our options file with the correct number of state variables now + UpdateExaOptionsWithOrientationCounts(); + // create our region map now + const int loc_nelems = m_mesh->GetNE(); + mfem::Array2D region_map(static_cast(options.materials.size()), loc_nelems); + region_map = false; + mfem::Array grains(loc_nelems); + + { + auto pfes = GetParFiniteElementSpace(1); + m_grains = std::make_shared(pfes.get()); + m_grains->HostWrite(); + for (int i = 0; i < loc_nelems; i++) { + grains.operator[](i) = m_mesh->GetAttribute(i); + m_grains->operator[](i) = m_mesh->GetAttribute(i); + } + } + + const auto grains2region = ::create_grains_to_map(options, grains); + + for (int i = 0; i < loc_nelems; i++) { + const int grain_id = grains.operator[](i); + const int region_id = grains2region.at(grain_id); + m_mesh->SetAttribute(i, region_id); + region_map(region_id - 1, i) = true; + } + + // update all of our attributes + m_mesh->SetAttributes(); + + for (auto matl : options.materials) { + const int region_id = matl.region_id - 1; + m_region_material_type.push_back(matl.mech_type); + m_material_name_region.push_back(std::make_pair(matl.material_name, region_id)); + std::string qspace_name = GetRegionName(region_id); + std::string qspace_name_0 = qspace_name + "_ord_0"; + + m_material_properties.emplace(qspace_name, matl.properties.properties); + mfem::Array loc_index(region_map.GetRow(region_id), loc_nelems, false); + // Check if this region is active on this rank + const int loc_num_elems = std::accumulate(loc_index.begin(), loc_index.end(), 0); + m_is_region_active[region_id] = (loc_num_elems > 0); + if (loc_num_elems == 0) { + continue; + } + + m_map_qs[qspace_name] = std::make_shared( + m_mesh, int_order, loc_index); + + m_map_qs[qspace_name_0] = std::make_shared( + m_mesh, 1, loc_index); + + auto state_var_beg_name = GetQuadratureFunctionMapName("state_var_beg", region_id); + auto state_var_end_name = GetQuadratureFunctionMapName("state_var_end", region_id); + auto state_var_avg_name = GetQuadratureFunctionMapName("state_var_avg", region_id); + auto cauchy_stress_beg_name = GetQuadratureFunctionMapName("cauchy_stress_beg", + region_id); + auto cauchy_stress_end_name = GetQuadratureFunctionMapName("cauchy_stress_end", + region_id); + auto cauchy_stress_avg_name = GetQuadratureFunctionMapName("cauchy_stress_avg", + region_id); + auto tangent_stiffness_name = GetQuadratureFunctionMapName("tangent_stiffness", + region_id); + + if (m_options.post_processing.volume_averages.def_grad || + m_options.post_processing.volume_averages.euler_strain || + m_options.post_processing.volume_averages.elastic_strain) { + auto def_grad = GetQuadratureFunctionMapName("kinetic_grads", region_id); + m_map_qfs[def_grad] = std::make_shared( + m_map_qs[qspace_name], 9, 0.0); + ::initializeDeformationGradientToIdentity(*m_map_qfs[def_grad]); + } + + if (m_options.post_processing.volume_averages.plastic_work || + m_options.post_processing.volume_averages.eq_pl_strain) { + auto scalar = GetQuadratureFunctionMapName("scalar", region_id); + m_map_qfs[scalar] = std::make_shared( + m_map_qs[qspace_name], 1, 0.0); + } + + m_map_qfs[state_var_beg_name] = std::make_shared( + m_map_qs[qspace_name], matl.state_vars.num_vars, 0.0); + m_map_qfs[state_var_end_name] = std::make_shared( + m_map_qs[qspace_name], matl.state_vars.num_vars, 0.0); + m_map_qfs[cauchy_stress_beg_name] = + std::make_shared( + m_map_qs[qspace_name], 6, 0.0); + m_map_qfs[cauchy_stress_end_name] = + std::make_shared( + m_map_qs[qspace_name], 6, 0.0); + m_map_qfs[tangent_stiffness_name] = + std::make_shared( + m_map_qs[qspace_name], 36, 0.0); + + m_map_qfs[cauchy_stress_avg_name] = + std::make_shared( + m_map_qs[qspace_name_0], 6, 0.0); + m_map_qfs[state_var_avg_name] = std::make_shared( + m_map_qs[qspace_name_0], matl.state_vars.num_vars, 0.0); + + m_map_qfs[state_var_beg_name]->operator=(0.0); + m_map_qfs[state_var_end_name]->operator=(0.0); + m_map_qfs[cauchy_stress_beg_name]->operator=(0.0); + m_map_qfs[cauchy_stress_end_name]->operator=(0.0); + m_map_qfs[tangent_stiffness_name]->operator=(0.0); + m_map_qfs[state_var_avg_name]->operator=(0.0); + m_map_qfs[cauchy_stress_avg_name]->operator=(0.0); + + if (matl.mech_type == MechType::UMAT) { + auto def_grad_name = GetQuadratureFunctionMapName("def_grad_beg", region_id); + m_map_qfs[def_grad_name] = std::make_shared( + m_map_qs[qspace_name], 9, 0.0); + ::initializeDeformationGradientToIdentity(*m_map_qfs[def_grad_name]); + } + + m_model_update_qf_pairs.push_back( + std::make_pair(state_var_beg_name, state_var_end_name)); + m_model_update_qf_pairs.push_back( + std::make_pair(cauchy_stress_beg_name, cauchy_stress_end_name)); + } + + CreateRegionCommunicators(); + InitializeStateVariables(grains2region); + } +} + +SimulationState::~SimulationState() { + for (auto& [region_id, comm] : m_region_communicators) { + if (comm != MPI_COMM_NULL) { + MPI_Comm_free(&comm); + } + } +} + +bool SimulationState::AddQuadratureFunction(const std::string_view& qf_name, + const int vdim, + const int region) { + std::string qf_name_mat = GetQuadratureFunctionMapName(qf_name, region); + if (m_map_qfs.find(qf_name_mat) == m_map_qfs.end()) { + std::string qspace_name = GetRegionName(region); + m_map_qfs.emplace(qf_name_mat, + std::make_shared( + m_map_qs[qspace_name], vdim, 0.0)); + return true; + } + return false; +} + +std::pair +SimulationState::GetQuadratureFunctionStatePair(const std::string_view& state_name, + const int region) const { + std::string mat_name = GetQuadratureFunctionMapName(state_name, region); + if (m_map_qf_mappings.find(mat_name) == m_map_qf_mappings.end()) { + return {-1, -1}; + } + const std::pair output = m_map_qf_mappings.at(mat_name); + return output; +} + +bool SimulationState::AddQuadratureFunctionStatePair(const std::string_view state_name, + std::pair state_pair, + const int region) { + std::string mat_name = GetQuadratureFunctionMapName(state_name, region); + if (m_map_qf_mappings.find(mat_name) == m_map_qf_mappings.end()) { + m_map_qf_mappings.emplace(mat_name, state_pair); + return true; + } + return false; +} + +void SimulationState::FinishCycle() { + (*m_primal_field_prev) = *m_primal_field; + (*m_mesh_qoi_nodes["displacement"]) = *m_mesh_nodes["mesh_current"]; + (*m_mesh_qoi_nodes["displacement"]) -= *m_mesh_nodes["mesh_ref"]; + m_mesh_qoi_nodes["velocity"]->Distribute(*m_primal_field); + // Code previously had beg time coords updated after the update model aspect of things + // UpdateModel(); + (*m_mesh_nodes["mesh_t_beg"]) = *m_mesh_nodes["mesh_current"]; +} + +std::shared_ptr +SimulationState::GetParFiniteElementSpace(const int vdim) { + if (m_map_pfes.find(vdim) == m_map_pfes.end()) { + const int space_dim = m_mesh->SpaceDimension(); + std::string l2_fec_str = "L2_" + std::to_string(space_dim) + "D_P" + std::to_string(0); + auto l2_fec = m_map_fec[l2_fec_str]; + auto value = std::make_shared( + m_mesh.get(), l2_fec.get(), vdim, mfem::Ordering::byVDIM); + m_map_pfes.emplace(vdim, std::move(value)); + } + return m_map_pfes[vdim]; +} + +std::string SimulationState::GetRegionDisplayName(const int region) const { + std::string raw_name = GetRegionName(region); + if (raw_name.empty()) + return raw_name; + + std::string display_name = raw_name; + + // Replace underscores with spaces + std::replace(display_name.begin(), display_name.end(), '_', ' '); + + // Capitalize first letter and letters after spaces + bool capitalize_next = true; + std::transform(display_name.begin(), + display_name.end(), + display_name.begin(), + [&capitalize_next](unsigned char c) -> char { // Explicitly specify return type + if (std::isspace(c)) { + capitalize_next = true; + return static_cast(c); + } else if (capitalize_next) { + capitalize_next = false; + return static_cast(std::toupper(c)); // No cast needed now + } + return static_cast(c); + }); + + return display_name; +} + +void SimulationState::CreateRegionCommunicators() { + int mpi_rank, mpi_size; + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); + MPI_Comm_size(MPI_COMM_WORLD, &mpi_size); + + // Get all unique region IDs across all materials + std::set all_region_ids; + for (const auto& mat : m_material_name_region) { + all_region_ids.insert(mat.second); + } + + // For each region, create a communicator containing only ranks with that region + for (int region_id : all_region_ids) { + // Each rank contributes whether it has this region + int has_region = m_is_region_active[region_id] ? 1 : 0; + std::vector all_has_region(static_cast(mpi_size)); + + MPI_Allgather(&has_region, 1, MPI_INT, all_has_region.data(), 1, MPI_INT, MPI_COMM_WORLD); + + // Build list of ranks that have this region + std::vector ranks_with_region; + for (int rank = 0; rank < mpi_size; ++rank) { + if (all_has_region[static_cast(rank)]) { + ranks_with_region.push_back(rank); + } + } + + // Create MPI group and communicator for this region + if (!ranks_with_region.empty()) { + m_region_root_rank[region_id] = ranks_with_region[0]; // First is lowest + if (!has_region) { + continue; + } + MPI_Group world_group, region_group; + MPI_Comm_group(MPI_COMM_WORLD, &world_group); + MPI_Group_incl(world_group, + static_cast(ranks_with_region.size()), + ranks_with_region.data(), + ®ion_group); + + MPI_Comm region_comm; + MPI_Comm_create_group(MPI_COMM_WORLD, region_group, 0, ®ion_comm); + + // Only store the communicator if this rank is part of it + m_region_communicators[region_id] = region_comm; + + MPI_Group_free(®ion_group); + MPI_Group_free(&world_group); + } + } +} + +// Modified InitializeStateVariables to load shared orientation data first +void SimulationState::InitializeStateVariables(const std::map& grains2region) { + // First, load shared orientation data if any material needs it + for (const auto& material : m_options.materials) { + if (material.grain_info.has_value() && material.grain_info->orientation_file.has_value()) { + if (!LoadSharedOrientationData(material.grain_info->orientation_file.value(), + material.grain_info->num_grains)) { + if (my_id == 0) { + std::cerr << "Failed to load shared orientation data from " + << material.grain_info->orientation_file.value() << std::endl; + } + return; + } + break; // Only need to load once since it's shared + } + } + + // Initialize state variables for each material region + for (size_t i = 0; i < m_options.materials.size(); ++i) { + if (!IsRegionActive(static_cast(i))) { + continue; + } + const auto& material = m_options.materials[i]; + const int region_id = material.region_id - 1; + InitializeRegionStateVariables(region_id, material, grains2region); + + auto state_var_beg_name = GetQuadratureFunctionMapName("state_var_beg", region_id); + auto state_var_qf_beg = m_map_qfs[state_var_beg_name]; + + auto state_var_end_name = GetQuadratureFunctionMapName("state_var_end", region_id); + auto state_var_qf_end = m_map_qfs[state_var_end_name]; + state_var_qf_end->operator=(*state_var_qf_beg.get()); + } + + // Clean up shared orientation data after all regions are initialized + CleanupSharedOrientationData(); +} + +// Refactored InitializeRegionStateVariables function +void SimulationState::InitializeRegionStateVariables(int region_id, + const MaterialOptions& material, + const std::map& grains2region) { + // Get the state variable QuadratureFunction for this region + auto state_var_beg_name = GetQuadratureFunctionMapName("state_var_beg", region_id); + auto state_var_qf = m_map_qfs[state_var_beg_name]; + + // Get the QuadratureSpace for this region + std::string qspace_name = GetRegionName(region_id); + auto qspace = m_map_qs[qspace_name]; + // Prepare orientation data for this region (convert from shared quaternions if needed) + OrientationConfig orientation_config = PrepareOrientationForRegion(material); + + // Calculate effective state variable count (includes orientations) + const int effective_state_var_size = material.state_vars.num_vars; + const int base_state_var_size = material.state_vars.num_vars - orientation_config.stride; + + // Load base state variable initial values + std::vector state_var_data; + if (!material.state_vars.initial_values.empty()) { + state_var_data = material.state_vars.initial_values; + } else if (!material.state_vars.state_file.empty()) { + // Load from file if not already loaded + std::ifstream file(material.state_vars.state_file); + if (!file.is_open()) { + if (my_id == 0) { + std::cerr << "Error: Cannot open state variables file: " + << material.state_vars.state_file << std::endl; + } + return; + } + + double value; + while (file >> value) { + state_var_data.push_back(value); + } + file.close(); + } + + // Validate state variable data size + if (state_var_data.size() != static_cast(base_state_var_size)) { + if (my_id == 0) { + std::cerr << "Warning: State variable data size (" << state_var_data.size() + << ") doesn't match expected size (" << base_state_var_size + << ") for material " << material.material_name << std::endl; + } + } + + // Get the data pointer for the QuadratureFunction + double* qf_data = state_var_qf->HostReadWrite(); + const int qf_vdim = state_var_qf->GetVDim(); + + // Validate that our total size matches + if (qf_vdim != effective_state_var_size) { + if (my_id == 0) { + std::cerr << "Error: QuadratureFunction vdim (" << qf_vdim + << ") doesn't match effective total size (" << effective_state_var_size + << ") for material " << material.material_name << std::endl; + } + return; + } + + // Get the local to global element mapping for this region + const auto& local2global = qspace->GetLocal2Global(); + const int num_local_elements = qspace->GetNumLocalElements(); + + // Loop over local elements in this region + for (int local_elem = 0; local_elem < num_local_elements; ++local_elem) { + const int global_elem = local2global[local_elem]; + + // Get the grain ID for this element (before region mapping) + const int grain_id = static_cast(m_grains->operator[](global_elem)); + + // Verify this element belongs to the current region + const int elem_region = grains2region.at(grain_id); + if (elem_region != (region_id + 1)) { // grains2region uses 1-based indexing + continue; // Skip elements that don't belong to this region + } + + // Get the integration rule for this element + const mfem::IntegrationRule* ir = &(state_var_qf->GetSpaceShared()->GetIntRule(local_elem)); + const int num_qpts = ir->GetNPoints(); + + // Loop over quadrature points in this element + for (int qpt = 0; qpt < num_qpts; ++qpt) { + // Calculate the base index for this quadrature point's data + const int qpt_base_index = (local_elem * num_qpts + qpt) * qf_vdim; + + // Fill state variables + size_t state_var_idx = 0; + for (int k = 0; k < qf_vdim; ++k) { + // Check if this component is NOT orientation data + if (orientation_config.is_valid && k > orientation_config.offset_start && + k < orientation_config.offset_end) { + // Skip orientation components - they'll be filled separately + continue; + } else { + // This is state variable data + double var_data = 0.0; + if (state_var_idx < state_var_data.size()) { + var_data = state_var_data[state_var_idx]; + } else if (my_id == 0) { + std::cerr << "Warning: Missing state variable data, component " + << state_var_idx << std::endl; + } + qf_data[qpt_base_index + k] = var_data; + state_var_idx++; + } + } + + // Fill orientation data (converted to format required by this material) + FillOrientationData(qf_data, qpt_base_index, qf_vdim, grain_id, orientation_config); + } + } + + if (my_id == 0) { + std::cout << "Initialized state variables for material " << material.material_name + << " (region " << region_id << ")" << std::endl; + if (orientation_config.is_valid) { + const auto& grain_info = material.grain_info.value(); + std::string format_name = "custom"; + if (grain_info.ori_type == OriType::QUAT) + format_name = "quaternions"; + else if (grain_info.ori_type == OriType::EULER) + format_name = "Euler angles"; + else if (grain_info.ori_type == OriType::CUSTOM && orientation_config.stride == 9) + format_name = "rotation matrices"; + + std::cout << " - Converted orientation data to " << format_name << " (stride " + << orientation_config.stride << ")" << std::endl; + } + } +} + +// Additional utility function to update ExaOptions with correct state variable counts +void SimulationState::UpdateExaOptionsWithOrientationCounts() { + for (auto& material : m_options.materials) { + int effective_count = CalculateEffectiveStateVarCount(material); + if (effective_count != material.state_vars.num_vars) { + if (my_id == 0) { + std::cout << "Updated state variable count for material " << material.material_name + << " from " << material.state_vars.num_vars << " to " << effective_count + << " (includes orientations)" << std::endl; + } + material.state_vars.num_vars = effective_count; + } + } +} + +int SimulationState::CalculateEffectiveStateVarCount(const MaterialOptions& material) { + int base_count = material.state_vars.num_vars; + + if (material.grain_info.has_value()) { + const auto& grain_info = material.grain_info.value(); + + // Add orientation variables based on what format this material needs + int orientation_vars = 0; + if (grain_info.ori_type == OriType::QUAT) { + orientation_vars = 4; // Quaternions + } else if (grain_info.ori_type == OriType::EULER) { + orientation_vars = 3; // Euler angles + } else if (grain_info.ori_type == OriType::CUSTOM) { + orientation_vars = grain_info.ori_stride; // Custom stride + } + + base_count += orientation_vars; + } + + return base_count; +} + +bool SimulationState::LoadSharedOrientationData(const std::string& orientation_file, + int num_grains) { + if (m_shared_orientation_data.is_loaded) { + // Already loaded, just verify grain count matches + if (m_shared_orientation_data.num_grains != num_grains) { + if (my_id == 0) { + std::cerr << "Error: Grain count mismatch. Expected " << num_grains + << " but shared data has " << m_shared_orientation_data.num_grains + << std::endl; + } + return false; + } + return true; + } + + std::ifstream orient_file(orientation_file); + if (!orient_file.is_open()) { + if (my_id == 0) { + std::cerr << "Error: Cannot open orientation file: " << orientation_file << std::endl; + } + return false; + } + + // Load unit quaternions (passive rotations from crystal to sample reference) + const size_t expected_size = 4 * static_cast( + num_grains); // Always 4 components per quaternion + m_shared_orientation_data.quaternions.reserve(expected_size); + + double value; + while (orient_file >> value && m_shared_orientation_data.quaternions.size() < expected_size) { + m_shared_orientation_data.quaternions.push_back(value); + } + orient_file.close(); + + if (m_shared_orientation_data.quaternions.size() != expected_size) { + if (my_id == 0) { + std::cerr << "Error: Orientation file size (" + << m_shared_orientation_data.quaternions.size() + << ") doesn't match expected size (" << expected_size << ") for " + << num_grains << " grains" << std::endl; + } + m_shared_orientation_data.quaternions.clear(); + return false; + } + + // Validate that quaternions are properly normalized + for (size_t i = 0; i < static_cast(num_grains); ++i) { + const size_t base_idx = i * 4; + const double w = m_shared_orientation_data.quaternions[base_idx]; + const double x = m_shared_orientation_data.quaternions[base_idx + 1]; + const double y = m_shared_orientation_data.quaternions[base_idx + 2]; + const double z = m_shared_orientation_data.quaternions[base_idx + 3]; + + const double norm = sqrt(w * w + x * x + y * y + z * z); + const double tolerance = 1e-6; + + if (fabs(norm - 1.0) > tolerance) { + if (my_id == 0) { + std::cerr << "Warning: Quaternion " << i << " is not normalized (norm = " << norm + << "). Normalizing..." << std::endl; + } + // Normalize the quaternion + m_shared_orientation_data.quaternions[base_idx] = w / norm; + m_shared_orientation_data.quaternions[base_idx + 1] = x / norm; + m_shared_orientation_data.quaternions[base_idx + 2] = y / norm; + m_shared_orientation_data.quaternions[base_idx + 3] = z / norm; + } + } + + m_shared_orientation_data.num_grains = num_grains; + m_shared_orientation_data.is_loaded = true; + + if (my_id == 0) { + std::cout << "Loaded shared orientation data: " << num_grains + << " unit quaternions (passive rotations)" << std::endl; + } + + return true; +} + +void SimulationState::CleanupSharedOrientationData() { + m_shared_orientation_data.quaternions.clear(); + m_shared_orientation_data.quaternions.shrink_to_fit(); + m_shared_orientation_data.num_grains = 0; + m_shared_orientation_data.is_loaded = false; + + if (my_id == 0) { + std::cout << "Cleaned up shared orientation data to free memory" << std::endl; + } +} + +std::vector +SimulationState::ConvertQuaternionsToEuler(const std::vector& quaternions, int num_grains) { + std::vector euler_angles; + euler_angles.reserve(static_cast(num_grains) * 3); + + auto bunge_func = [](const double* const quat, double* bunge_ang) { + // below is equivalent to std::sqrt(std::numeric_limits::epsilon); + constexpr double tol = 1.4901161193847656e-08; + const auto q03 = quat[0] * quat[0] + quat[3] * quat[3]; + const auto q12 = quat[1] * quat[1] + quat[2] * quat[2]; + const auto xi = std::sqrt(q03 * q12); + // We get to now go through all of the different cases that this might break down into + if (std::abs(xi) < tol && std::abs(q12) < tol) { + bunge_ang[0] = std::atan2(-2.0 * quat[0] * quat[3], + quat[0] * quat[0] - quat[3] * quat[3]); + // All of the other values are zero + } else if (std::abs(xi) < tol && std::abs(q03) < tol) { + bunge_ang[0] = std::atan2(2.0 * quat[1] * quat[2], + quat[1] * quat[1] - quat[2] * quat[2]); + bunge_ang[1] = 3.141592653589793; + // The other value is zero + } else { + const double inv_xi = 1.0 / xi; + // The atan2 terms are pretty long so we're breaking it down into a couple of temp terms + const double t1 = inv_xi * (quat[1] * quat[3] - quat[0] * quat[2]); + const double t2 = inv_xi * (-quat[0] * quat[1] - quat[2] * quat[3]); + // We can now assign the first two bunge angles + bunge_ang[0] = std::atan2(t1, t2); + bunge_ang[1] = std::atan2(2.0 * xi, q03 - q12); + // Once again these terms going into the atan2 term are pretty long + { + const double t1_ = inv_xi * (quat[0] * quat[2] + quat[1] * quat[3]); + const double t2_ = inv_xi * (quat[2] * quat[3] - quat[0] * quat[1]); + // We can finally find the final bunge angle + bunge_ang[2] = std::atan2(t1_, t2_); + } + } + }; + + for (int i = 0; i < num_grains; ++i) { + const int base_idx = i * 4; + const double* const quat = &(quaternions.data()[base_idx]); + double bunge[3] = {}; + + bunge_func(quat, bunge); + + euler_angles.push_back(bunge[0]); + euler_angles.push_back(bunge[1]); + euler_angles.push_back(bunge[2]); + } + + return euler_angles; +} + +std::vector +SimulationState::ConvertQuaternionsToMatrix(const std::vector& quaternions, + int num_grains) { + std::vector matrices; + matrices.reserve(static_cast(num_grains) * 9); + + for (int i = 0; i < num_grains; ++i) { + const int base_idx = i * 4; + const double* const quat = &(quaternions.data()[base_idx]); + + const double qbar = quat[0] * quat[0] - + (quat[1] * quat[1] + quat[2] * quat[2] + quat[3] * quat[3]); + + // Row-major order: [r11, r12, r13, r21, r22, r23, r31, r32, r33] + matrices.push_back(qbar + 2.0 * quat[1] * quat[1]); + matrices.push_back(2.0 * (quat[1] * quat[2] - quat[0] * quat[3])); + matrices.push_back(2.0 * (quat[1] * quat[3] + quat[0] * quat[2])); + + matrices.push_back(2.0 * (quat[1] * quat[2] + quat[0] * quat[3])); + matrices.push_back(qbar + 2.0 * quat[2] * quat[2]); + matrices.push_back(2.0 * (quat[2] * quat[3] - quat[0] * quat[1])); + + matrices.push_back(2.0 * (quat[1] * quat[3] - quat[0] * quat[2])); + matrices.push_back(2.0 * (quat[2] * quat[3] + quat[0] * quat[1])); + matrices.push_back(qbar + 2.0 * quat[3] * quat[3]); + } + + return matrices; +} + +SimulationState::OrientationConfig +SimulationState::PrepareOrientationForRegion(const MaterialOptions& material) { + OrientationConfig config; + + if (!material.grain_info.has_value() || !m_shared_orientation_data.is_loaded) { + return config; // Return invalid config + } + + const auto& grain_info = material.grain_info.value(); + + // Verify grain count consistency + if (m_shared_orientation_data.num_grains != grain_info.num_grains) { + if (my_id == 0) { + std::cerr << "Error: Grain count mismatch for material " << material.material_name + << ". Expected " << grain_info.num_grains << " but shared data has " + << m_shared_orientation_data.num_grains << std::endl; + } + return config; + } + + // Convert shared quaternions to the format required by this material + if (grain_info.ori_type == OriType::QUAT) { + // Material needs quaternions - use shared data directly + config.data = m_shared_orientation_data.quaternions; + config.stride = 4; + if (my_id == 0) { + std::cout << "Using quaternion format for material " << material.material_name + << std::endl; + } + } else if (grain_info.ori_type == OriType::EULER) { + // Material needs Euler angles - convert from quaternions + config.data = ConvertQuaternionsToEuler(m_shared_orientation_data.quaternions, + grain_info.num_grains); + config.stride = 3; + if (my_id == 0) { + std::cout << "Converted quaternions to Euler angles for material " + << material.material_name << std::endl; + } + } else if (grain_info.ori_type == OriType::CUSTOM) { + // Handle custom formats + if (grain_info.ori_stride == 9) { + // Assume custom format wants rotation matrices + config.data = ConvertQuaternionsToMatrix(m_shared_orientation_data.quaternions, + grain_info.num_grains); + config.stride = 9; + if (my_id == 0) { + std::cout << "Converted quaternions to rotation matrices for material " + << material.material_name << std::endl; + } + } else if (grain_info.ori_stride == 4) { + // Custom format wants quaternions + config.data = m_shared_orientation_data.quaternions; + config.stride = 4; + if (my_id == 0) { + std::cout << "Using quaternion format for custom material " + << material.material_name << std::endl; + } + } else { + // Unsupported custom stride + if (my_id == 0) { + std::cerr << "Error: Unsupported custom orientation stride (" + << grain_info.ori_stride << ") for material " << material.material_name + << std::endl; + } + return config; + } + } + + // Calculate placement offsets + auto offsets = CalculateOrientationOffsets(material, config.stride); + config.offset_start = offsets.first; + config.offset_end = offsets.second; + config.is_valid = true; + + return config; +} + +std::pair SimulationState::CalculateOrientationOffsets(const MaterialOptions& material, + int orientation_stride) { + if (!material.grain_info.has_value() || orientation_stride == 0) { + return {-1, 0}; + } + + const auto& grain_info = material.grain_info.value(); + const int state_var_size = material.state_vars.num_vars; + + int offset_start, offset_end; + + if (grain_info.ori_state_var_loc < 0) { + // Put orientation data at the end + if (my_id == 0) { + std::cout << "Note: Orientation data placed at end of state variable array " + << "for material " << material.material_name << std::endl; + } + offset_start = state_var_size - 1; + offset_end = state_var_size + orientation_stride; + } else if (grain_info.ori_state_var_loc == 0) { + // Put orientation data at the beginning + offset_start = -1; + offset_end = orientation_stride; + } else { + // Put orientation data at specified location + offset_start = grain_info.ori_state_var_loc - 1; + offset_end = grain_info.ori_state_var_loc + orientation_stride; + } + + return {offset_start, offset_end}; +} + +void SimulationState::FillOrientationData(double* qf_data, + int qpt_base_index, + int qf_vdim, + int grain_id, + const OrientationConfig& orientation_config) { + if (!orientation_config.is_valid || orientation_config.stride == 0) { + return; // No orientation data to fill + } + + for (int k = 0; k < qf_vdim; ++k) { + if (k > orientation_config.offset_start && k < orientation_config.offset_end) { + // This is orientation data + const int grain_idx = k - orientation_config.offset_start - 1; + const size_t orient_idx = static_cast( + orientation_config.stride * (grain_id - 1) + grain_idx); + + if (orient_idx < orientation_config.data.size()) { + qf_data[qpt_base_index + k] = orientation_config.data[orient_idx]; + } else { + qf_data[qpt_base_index + k] = 0.0; // Default value if data is missing + if (my_id == 0) { + std::cerr << "Warning: Missing orientation data for grain " << grain_id + << ", component " << grain_idx << std::endl; + } + } + } + } +} \ No newline at end of file diff --git a/src/sim_state/simulation_state.hpp b/src/sim_state/simulation_state.hpp new file mode 100644 index 0000000..30c2b92 --- /dev/null +++ b/src/sim_state/simulation_state.hpp @@ -0,0 +1,1324 @@ +#pragma once + +#include "boundary_conditions/BCManager.hpp" +#include "mfem_expt/partial_qfunc.hpp" +#include "mfem_expt/partial_qspace.hpp" +#include "options/option_parser_v2.hpp" + +#include "mfem.hpp" + +#include +#include +#include +#include +#include +#include + +/** + * @brief Enumeration for time step status and control + * + * @details Tracks the current state of time stepping algorithm and provides + * control flow information for adaptive time stepping, sub-stepping, and + * simulation completion detection. + */ +enum class TimeStep { + NORMAL, ///< Normal time stepping mode + RETRIAL, ///< Time step failed, retrying with smaller step + SUBSTEP, ///< Sub-stepping through an original time step + FAILED, ///< Time step failed completely, cannot continue + FINAL, ///< This is the final time step of the simulation + FINISHED ///< Simulation is completely finished +}; + +/** + * @brief Comprehensive time step management and adaptive time stepping for ExaConstit simulations + * + * @details This class handles all aspects of time management in ExaConstit simulations including: + * - **Multiple Time Step Types**: Fixed, automatic, and custom time step sequences + * - **Adaptive Time Stepping**: Automatic adjustment based on Newton-Raphson convergence behavior + * - **Sub-stepping Recovery**: Automatic sub-stepping when convergence fails + * - **Boundary Condition Synchronization**: Time step modification to hit exact BC change times + * - **Failure Handling**: Sophisticated retry logic with progressive time step reduction + * - **Final Time Detection**: Accurate detection of simulation completion + * - **Restart Capabilities**: Complete state save/restore for checkpoint/restart + * + * **Time Step Types Supported**: + * - **FIXED**: Constant time step throughout simulation (with failure handling) + * - **AUTO**: Automatic adjustment based on solver performance + * - **CUSTOM**: User-defined sequence of time steps from input + * + * **Adaptive Algorithm**: For AUTO mode, time step is adjusted using: + * factor = (max_nr_steps * dt_scale) / actual_nr_steps + * where fewer Newton iterations lead to larger time steps and vice versa. + */ +class TimeManagement { +private: + /** @brief Current simulation time */ + double time = 0.0; + + /** @brief Old simulation time */ + double old_time = 0.0; + + /** @brief Final simulation time (target end time) */ + double time_final = 0.0; + + /** @brief Current time step size */ + double dt = 1.0; + + /** @brief Original time step before failure/sub-stepping */ + double dt_orig = 1.0; + + /** @brief Previous time step size (for tracking changes) */ + double prev_dt = 1.0; + + /** @brief Minimum allowed time step size */ + double dt_min = 1.0; + + /** @brief Maximum allowed time step size (AUTO mode) */ + double dt_max = 1.0; + + /** @brief Scaling factor for time step reduction on failure */ + double dt_scale = 0.25; + + /** @brief Fixed time step size (FIXED mode) */ + double dt_fixed = 1.0; + + /** @brief Time stepping mode (FIXED, AUTO, CUSTOM, NOTYPE) */ + TimeStepType time_type = TimeStepType::NOTYPE; + + /** @brief Custom time step sequence (CUSTOM mode) */ + std::vector custom_dt = {}; + + /** @brief Current simulation cycle number */ + size_t simulation_cycle = 0; + + /** @brief Maximum Newton-Raphson steps for AUTO time step scaling */ + size_t max_nr_steps = 25; + + /** @brief Maximum number of consecutive failures before giving up */ + size_t max_failures = 4; + + /** @brief Current number of consecutive failures */ + size_t num_failures = 0; + + /** @brief Required number of sub-steps to complete original time step */ + size_t required_num_sub_steps = 0; + + /** @brief Current number of sub-steps completed */ + size_t num_sub_steps = 0; + + /** @brief Internal state tracker for time step status */ + TimeStep internal_tracker = TimeStep::NORMAL; + +public: + /** + * @brief Constructor - initialize time management from simulation options + * + * @param options Reference to ExaOptions containing time step configuration + * + * @details Sets up time management based on the specified time step type: + * + * **FIXED Mode**: + * - Uses constant time step from options.time.fixed_time->dt + * - Sets dt_min based on maximum allowed reductions + * - Configures final time from options + * + * **AUTO Mode**: + * - Uses starting time step from options.time.auto_time->dt_start + * - Configures min/max bounds and scaling parameters + * - Sets up automatic time step logging file + * - Links to Newton solver iteration limits + * + * **CUSTOM Mode**: + * - Loads user-defined time step sequence + * - Calculates total simulation time from sequence sum + * - Sets minimum based on smallest step in sequence + * + * Also performs initial time setup and final step detection. + */ + TimeManagement(ExaOptions& options); + + /** + * @brief Get current simulation time + * + * @return Current time value + */ + double GetTime() const { + return time; + } + + /** + * @brief Get actual simulation time if auto-time stepping used + * + * @return Actual time step value for a step + */ + double GetTrueCycleTime() const { + return old_time; + } + + /** + * @brief Get current time step size + * + * @return Current time step size + */ + double GetDeltaTime() const { + return dt; + } + + /** + * @brief Get current simulation cycle number + * + * @return Current cycle (time step) number + */ + size_t GetSimulationCycle() const { + return simulation_cycle; + } + + /** + * @brief Update time step based on solver performance and handle time advancement + * + * @param nr_steps Number of Newton-Raphson iterations required for convergence + * @param success Whether the previous time step converged successfully + * @return TimeStep status indicating the next action required + * + * @details This is the core time stepping algorithm that handles multiple scenarios: + * + * **Failure Handling** (success = false): + * 1. Checks if already sub-stepping (returns FAILED if so) + * 2. Saves original time step on first failure + * 3. Reduces time step by dt_scale factor + * 4. Enforces minimum time step limit + * 5. Increments failure counter and returns RETRIAL or FAILED + * + * **Success Cases**: + * - **Final Step**: Transitions FINAL -> FINISHED + * - **Sub-stepping Recovery**: Continues sub-steps until original dt is recovered + * - **Normal Advancement**: Updates cycle and computes next time step + * + * **Time Step Calculation by Mode**: + * - **AUTO**: factor = (max_nr_steps * dt_scale) / nr_steps; dt *= factor + * - **CUSTOM**: dt = custom_dt[simulation_cycle] + * - **FIXED**: dt = dt_fixed + * + * **Final Time Detection**: + * - Automatically adjusts final time step to land exactly on time_final + * - Handles floating-point precision issues with tolerance checking + * - Returns appropriate FINAL status when approaching end time + * + * @note This method is responsible for all time advancement logic and must be + * called after each Newton solver attempt to properly manage the simulation timeline. + */ + TimeStep UpdateDeltaTime(const int nr_steps, const bool success = true); + + /** + * @brief Adjust time step to hit a specific boundary condition time exactly + * + * @param desired_bc_time Target time for boundary condition change + * @return True if time step was adjusted, false if target time already passed + * + * @details This method ensures that the simulation hits specific times exactly + * when boundary conditions need to change. The algorithm: + * 1. Checks if the desired time hasn't already passed + * 2. Calculates if the next time step would overshoot the target + * 3. If overshoot detected, adjusts current time step to land exactly on target + * 4. Handles the time update internally using ResetTime()/UpdateTime() + * + * This is critical for simulations with time-dependent boundary conditions where + * exact timing is required for physical accuracy. + * + * @note Only modifies time step if it would overshoot the target time + */ + bool BCTime(const double desired_bc_time); + + /** + * @brief Advance simulation time by current time step + * + * @details Updates time = time + dt. Used after successful convergence + * to move to the next time step. Called internally by UpdateDeltaTime() + * and BCTime() methods. + */ + void UpdateTime() { + time += dt; + } + + /** + * @brief Revert time to previous value + * + * @details Updates time = time - dt. Used when a time step fails + * and needs to be retried with a smaller time step. Called internally + * by UpdateDeltaTime() and BCTime() methods. + */ + void ResetTime() { + time -= dt; + } + + /** + * @brief Restart simulation from a specific time and cycle + * + * @param time_restart Time to restart from + * @param dt_restart Time step size to use + * @param cycle Cycle number to restart from + * + * @details Used for simulation restarts from checkpoint data. + * Sets all time-related state to the specified restart values. + * Does not modify time step type or other configuration parameters. + */ + void RestartTimeState(const double time_restart, const double dt_restart, const size_t cycle) { + simulation_cycle = cycle; + time = time_restart; + dt = dt_restart; + } + + /** + * @brief Print retrial diagnostic information + * + * @details Outputs detailed information about cycle time step info including: + * - Original time step size before we reduced things down + * - Current time + * - Current cycle + * - Current time step size + * + * Used for debugging convergence issues and understanding when/why + * retrying a time step is required. + */ + void PrintRetrialStats() const { + std::cout << "[Cycle: " << (simulation_cycle + 1) << " , time: " << time + << "] Previous attempts to converge failed step: dt old was " << dt_orig + << " new dt is " << dt << std::endl; + } + + /** + * @brief Print sub-stepping diagnostic information + * + * @details Outputs detailed information about sub-stepping including: + * - Original time step size before sub-stepping began + * - Current sub-step size + * - Total number of sub-steps required to recover + * + * Used for debugging convergence issues and understanding when/why + * sub-stepping is being triggered. + */ + void PrintSubStepStats() const { + std::cout << "[Cycle: " << (simulation_cycle + 1) << " , time: " << time + << "] Previous attempts to converge failed but now starting sub-stepping of our " + "desired time step: desired dt old was " + << dt_orig << " sub-stepping dt is " << dt + << " and number of sub-steps required is " << required_num_sub_steps << std::endl; + } + + /** + * @brief Print time step change information + * + * @details Outputs information about time step changes including: + * - Current simulation time + * - Previous and current time step sizes + * - Factor by which time step changed + * + * Useful for monitoring adaptive time stepping behavior and understanding + * how the solver performance affects time step selection. + */ + void PrintTimeStats() const { + const double factor = dt / prev_dt; + std::cout << "Time " << time << " dt old was " << prev_dt << " dt has been updated to " + << dt << " and changed by a factor of " << factor << std::endl; + } + + /** + * @brief Check if this is the final time step + * + * @return True if simulation has reached final time and this is the last step + */ + bool IsLastStep() const { + return internal_tracker == TimeStep::FINAL; + } + + /** + * @brief Check if simulation is completely finished + * + * @return True if simulation has completed all time steps + */ + bool IsFinished() const { + return internal_tracker == TimeStep::FINISHED; + } +}; + +/** + * @brief Central simulation state manager for ExaConstit multi-material simulations + * + * @details This class serves as the central repository for all simulation data and provides + * a unified interface for accessing mesh, material properties, quadrature functions, and + * coordinate information across multiple material regions. + * + * **Key Architectural Features**: + * + * **Multi-Region Support**: + * - Manages data for multiple material regions with different models (ExaCMech, UMAT) + * - Region-aware naming scheme for all data structures + * - Independent material properties and model types per region + * - Seamless multi-material simulations with unified interface + * + * **Quadrature Function Management**: + * - Comprehensive storage and access for all simulation data + * - Region-specific automatic name resolution + * - State variable mapping for complex models like ExaCMech + * - Efficient begin/end step data management with O(1) swapping + * + * **Mesh and Coordinate Tracking**: + * - Multiple coordinate systems (reference, current, time-start) + * - Automatic mesh deformation based on velocity field + * - Displacement computation and tracking + * - Device-compatible coordinate management + * + * **Material Integration**: + * - Support for heterogeneous material models + * - Crystal plasticity grain management + * - Region-specific material properties + * - Material model type tracking per region + * + * **Time Management Integration**: + * - Embedded TimeManagement for comprehensive time control + * - Adaptive time stepping integration + * - Restart capability support + * + * **Device Compatibility**: + * All data structures support CPU/GPU execution with appropriate device memory management. + */ +class SimulationState { +private: + // All the various quantities related to our simulations + // aka the mesh, quadrature functions, finite element spaces, + // mesh nodes, and various things related to our material systems + + // We might eventually need to make this a map or have a LOR version + // if we decide to map our quadrature function data from a HOR set to a + // LOR version to make visualizations easier... + /** @brief Parallel mesh shared pointer */ + std::shared_ptr m_mesh; + // Get the PFES associated with the mesh + // The same as below goes for the above as well + /** @brief Finite element space for mesh coordinates and primary solution */ + std::shared_ptr m_mesh_fes; + // Map of the QuadratureSpaceBase associated with a given name + // These QuadratureSpaceBase might also be the PartialQuadratureSpace objects + /** @brief Map of quadrature functions by name (includes region-specific names) */ + std::map> m_map_qs; + // Map of the QuadratureFunction associated with a given name + // These QuadratureFunctions might also be a PartialQuadratureFunction class + // for when we have have multiple materials in a simulation + /** @brief Map of quadrature spaces by region name */ + std::map> m_map_qfs; + // Map of the ParallelFiniteElementSpace associated with a given vector dimension + /** @brief Map of finite element spaces by vector dimension */ + std::map> m_map_pfes; + // Map of the FiniteElementCollection associated with the typical FEC name + // Typically would be something like L2_3D_P2 (FECTYPE _ #SPACEDIM D_P #MESHORDER) + // The name is based on the name that MFEM prints out for along with any GridFunction that + // tells us what FiniteElementCollection it belongs to + /** @brief Map of finite element collections by type string */ + std::map> m_map_fec; + // Map of the mesh nodes associated with a given region maybe? + /** @brief Map of mesh coordinate grid functions (current, reference, time-start) */ + std::map> m_mesh_nodes; + // Map of the mesh nodes associated with a QoI aka x_nodes-> time_{0}, time_{i}, time_{i+1}, + // velocity, displacement + /** @brief Map of mesh quantities of interest (velocity, displacement) */ + std::map> m_mesh_qoi_nodes; + + // Our velocity field + /** @brief Current primal field (velocity degrees of freedom) */ + std::shared_ptr m_primal_field; + /** @brief Previous time step primal field for rollback capability */ + std::shared_ptr m_primal_field_prev; + /** @brief Grain ID array for crystal plasticity models */ + std::shared_ptr m_grains; + + // Map of the material properties associated with a given region name + /** @brief Material properties organized by region name */ + std::map> m_material_properties; + // Vector of the material region name and the region index associated with it + /** @brief Material name and region ID pairs for region management */ + std::vector> m_material_name_region; + /** @brief Material model type (EXACMECH, UMAT) by region index */ + std::vector m_region_material_type; + // Map of the quadrature function name to the potential offset in the quadrature function and + // the vector dimension associated with that quadrature function name. + // This variable is useful to obtain sub-mappings within a quadrature function used for all + // history variables such as how it's done with ECMech's models. + /** @brief Quadrature function state variable mappings for ExaCMech models */ + std::map> m_map_qf_mappings; + // Class devoted to updating our time based on various logic we might have. + /** @brief Time management instance for comprehensive time control */ + TimeManagement m_time_manager; + // Only need 1 instance of our boundary condition manager + // BCManager m_bc_manager; + + // Vector of the names of the quadrature function pairs that have their data ptrs + // swapped when UpdateModel() is called. + /** @brief Quadrature function pairs for efficient model updates (begin/end step swapping) */ + std::vector> m_model_update_qf_pairs; + /** @brief Reference to simulation options */ + ExaOptions& m_options; + +#if defined(EXACONSTIT_USE_AXOM) + // We want this to be something akin to a axom::sidre::MFEMSidreDataCollection + // However, we need it flexible enough to handle multiple different mesh topologies in it that + // we might due to different mfem::SubMesh objects that correspond to each + // PartialQuadraturePoint + /** @brief Simulation restart data store (optional Axom/Sidre support) */ + std::unique_ptr m_simulation_restart; +#endif + /** @brief MPI rank identifier */ + int my_id; + + /** @brief Map storing whether each region has elements on this MPI rank + * @details Key: region_id, Value: true if region has elements on this rank + */ + std::unordered_map m_is_region_active; + + /** @brief MPI communicators for each region containing only ranks with that region + * @details Key: region_id, Value: MPI communicator (MPI_COMM_NULL if region not on this rank) + */ + std::unordered_map m_region_communicators; + + /** @brief Map storing the root (lowest) MPI rank that has each region + * @details Key: region_id, Value: lowest rank with this region + */ + std::unordered_map m_region_root_rank; + +public: + /** @brief Runtime model for device execution (CPU/OpenMP/GPU) */ + RTModel class_device; + +public: + /** + * @brief Constructor - initializes complete simulation state from options + * + * @param options Reference to simulation options containing all configuration + * + * @details Sets up the complete simulation state including: + * - Mesh loading and finite element space creation + * - Material regions and properties setup + * - Quadrature functions for all regions and data types + * - Coordinate tracking grid functions (reference, current, time-start) + * - Time management initialization + * - Device memory configuration + * - Multi-material data structure organization + * - Crystal plasticity grain setup if applicable + */ + SimulationState(ExaOptions& options); + /** + * @brief Virtual destructor for proper cleanup + */ + virtual ~SimulationState(); + + // ========================================================================= + // INITIALIZATION METHODS + // ========================================================================= + + /** + * @brief Initialize state variables and grain orientation data for all material regions + * + * @details This method handles the complete initialization of material-specific data: + * 1. **Shared Orientation Loading**: Loads crystal orientation data once for all regions + * 2. **Region-Specific Initialization**: Calls InitializeRegionStateVariables for each region + * 3. **State Variable Setup**: Copies beginning-of-step to end-of-step data + * 4. **Memory Cleanup**: Frees shared orientation data after all regions are initialized + * + * Replaces the global setStateVarData function with a per-region approach that + * supports multiple material types and grain distributions. + */ + void InitializeStateVariables(const std::map& grains2region); + + // ========================================================================= + // QUADRATURE FUNCTION MANAGEMENT + // ========================================================================= + + /** + * @brief Generate region-specific quadrature function name + * + * @param qf_name Base quadrature function name + * @param region Region index (-1 for global/non-region-specific) + * @return Region-specific quadrature function name + * + * @details Creates region-specific names using the pattern: + * "base_name_materialname_regionid" + * + * Examples: + * - "cauchy_stress_beg" + region 0 (steel) -> "cauchy_stress_beg_steel_0" + * - "state_var_end" + region 1 (aluminum) -> "state_var_end_aluminum_1" + * - "def_grad_beg" + region -1 -> "def_grad_beg" (global) + * + * This naming scheme enables transparent multi-material support. + */ + std::string GetQuadratureFunctionMapName(const std::string_view& qf_name, + const int region = -1) const { + if (region < 0) { + return std::string(qf_name); + } + std::string mat_name = GetRegionName(region); + std::string qf_name_mat = std::string(qf_name) + "_" + mat_name; + return qf_name_mat; + } + + /** + * @brief Get quadrature function for specific region + * + * @param qf_name Quadrature function name + * @param region Region index (-1 for global) + * @return Shared pointer to the quadrature function + * @throws std::runtime_error if quadrature function doesn't exist + * + * @details Primary interface for accessing simulation data. Automatically resolves + * region-specific names and returns the appropriate quadrature function. + * + * **Common Usage Patterns**: + * ```cpp + * // Get stress for region 0 + * auto stress = sim_state.GetQuadratureFunction("cauchy_stress_beg", 0); + * + * // Get global deformation gradient + * auto def_grad = sim_state.GetQuadratureFunction("def_grad_beg"); + * + * // Get state variables for specific region + * auto state_vars = sim_state.GetQuadratureFunction("state_var_end", region_id); + * ``` + */ + std::shared_ptr + GetQuadratureFunction(const std::string_view& qf_name, const int region = -1) { + return m_map_qfs[GetQuadratureFunctionMapName(qf_name, region)]; + } + + /** + * @brief Add a new quadrature function for a specific region + * + * @param qf_name Base quadrature function name + * @param vdim Vector dimension of the quadrature function + * @param region Region index (-1 for global) + * @return True if successfully added, false if already exists + * + * @details Creates a new quadrature function with the specified vector dimension + * and associates it with the given region. The function is initialized with zeros + * and uses the appropriate quadrature space for the region. + * + * **Vector Dimensions for Common Data**: + * - Scalars (pressure, von Mises stress): vdim = 1 + * - Stress/strain tensors: vdim = 6 (Voigt notation) + * - Deformation gradients: vdim = 9 (3x3 matrix) + * - State variables: vdim = model-dependent + */ + bool AddQuadratureFunction(const std::string_view& qf_name, + const int vdim = 1, + const int region = -1); + + /** + * @brief Get state variable mapping for ExaCMech models + * + * @param state_name State variable name (e.g., "slip_rates", "hardness") + * @param region Region index (-1 for global) + * @return Pair containing (offset, length) within the state variable vector + * @throws std::out_of_range if mapping doesn't exist + * + * @details ExaCMech models store multiple state variables in a single large vector. + * This method returns the offset and length for a specific state variable within + * that vector, enabling efficient access to individual quantities. + * + * **Example Usage**: + * ```cpp + * auto [offset, length] = sim_state.GetQuadratureFunctionStatePair("slip_rates", 0); + * // slip_rates for region 0 starts at 'offset' and has 'length' components + * ``` + */ + std::pair GetQuadratureFunctionStatePair(const std::string_view& state_name, + const int region = -1) const; + + /** + * @brief Add state variable mapping for ExaCMech models + * + * @param state_name State variable name + * @param state_pair Pair containing (offset, length) within state vector + * @param region Region index + * @return True if successfully added, false if already exists + * + * @details Used by ExaCMech models during initialization to register the location + * of specific state variables within the large state variable vector. This enables + * efficient access without searching or string parsing during simulation. + */ + bool AddQuadratureFunctionStatePair(const std::string_view state_name, + std::pair state_pair, + const int region); + + // ========================================================================= + // MODEL UPDATE MANAGEMENT + // ========================================================================= + + /** + * @brief Register quadrature function pairs for model updates + * + * @param update_var_pair Pair of (beginning_step_name, end_step_name) + * + * @details Registers pairs of quadrature functions that need to have their + * data swapped when UpdateModel() is called. Typically used for begin/end + * step variables like: + * - ("cauchy_stress_beg", "cauchy_stress_end") + * - ("state_var_beg", "state_var_end") + * - ("def_grad_beg", "def_grad_end") + */ + void AddUpdateVariablePairNames(std::pair update_var_pair) { + std::string view1(update_var_pair.first); + std::string view2(update_var_pair.second); + m_model_update_qf_pairs.push_back({view1, view2}); + } + + /** + * @brief Update model variables by swapping begin/end step data + * + * @details Performs efficient O(1) pointer swaps between beginning and end time step + * variables for all registered quadrature function pairs. This moves end-of-step + * converged values to beginning-of-step for the next time step without data copying. + * + * Called after successful convergence to prepare for the next time step. + */ + void UpdateModel() { + for (auto [name_prev, name_cur] : m_model_update_qf_pairs) { + m_map_qfs[name_prev]->Swap(*m_map_qfs[name_cur]); + } + } + + /** + * @brief Setup model variables by copying begin to end step data + * + * @details Copies beginning-of-step data to end-of-step quadrature functions + * for all registered pairs. Used at the start of a time step to initialize + * end-step variables with beginning-step values before material model execution. + */ + void SetupModelVariables() { + for (auto [name_prev, name_cur] : m_model_update_qf_pairs) { + m_map_qfs[name_cur]->operator=(*m_map_qfs[name_prev]); + } + } + + // ========================================================================= + // MESH AND COORDINATE MANAGEMENT + // ========================================================================= + + /** + * @brief Get the simulation mesh + * + * @return Shared pointer to the parallel mesh + */ + std::shared_ptr GetMesh() { + return m_mesh; + } + + /** + * @brief Get current mesh coordinates + * + * @return Shared pointer to current coordinate grid function + * + * @details Returns the current deformed mesh coordinates. Updated after + * each converged time step based on the velocity field using: + * current_coords = time_start_coords + velocity * dt + */ + std::shared_ptr GetCurrentCoords() { + return m_mesh_nodes["mesh_current"]; + } + /** + * @brief Get beginning-of-time-step mesh coordinates + * + * @return Shared pointer to time step start coordinate grid function + * + * @details Coordinates at the beginning of the current time step, used as + * the reference for computing incremental deformation during the step. + */ + std::shared_ptr GetTimeStartCoords() { + return m_mesh_nodes["mesh_t_beg"]; + } + + /** + * @brief Get reference mesh coordinates + * + * @return Shared pointer to reference coordinate grid function + * + * @details Returns the undeformed reference configuration coordinates. + * Used for computing total deformation gradients and strains from the + * original configuration. + */ + std::shared_ptr GetRefCoords() { + return m_mesh_nodes["mesh_ref"]; + } + + /** + * @brief Get displacement field + * + * @return Shared pointer to displacement grid function + * + * @details Total displacement from reference configuration: + * displacement = current_coords - reference_coords + */ + std::shared_ptr GetDisplacement() { + return m_mesh_qoi_nodes["displacement"]; + } + + /** + * @brief Get velocity field + * + * @return Shared pointer to velocity grid function + * + * @details Current nodal velocity field, which is the primary unknown + * in ExaConstit's velocity-based formulation. + */ + std::shared_ptr GetVelocity() { + return m_mesh_qoi_nodes["velocity"]; + } + + /** + * @brief Get global visualization quadrature space + * + * @return Shared pointer to global quadrature space for visualization + */ + std::shared_ptr GetGlobalVizQuadSpace() { + return m_map_qs["global_ord_0"]; + } + + /** + * @brief Update nodal coordinates based on current velocity solution + * + * @details Updates mesh coordinates after Newton solver convergence using: + * 1. Distribute velocity solution to grid function + * 2. current_coords = time_start_coords + velocity * dt + * + * This implements the updated Lagrangian formulation by moving the mesh + * according to the computed velocity field. + */ + void UpdateNodalEndCoords() { + m_mesh_qoi_nodes["velocity"]->Distribute(*m_primal_field); + (*m_mesh_nodes["mesh_current"]) = *m_mesh_qoi_nodes["velocity"]; + (*m_mesh_nodes["mesh_current"]) *= GetDeltaTime(); + (*m_mesh_nodes["mesh_current"]) += *m_mesh_nodes["mesh_t_beg"]; + } + + /** + * @brief Restart cycle by reverting to previous time step state + * + * @details Reverts mesh coordinates and primal field to previous time step + * values when a time step fails and needs to be retried with a smaller + * time step size. Ensures simulation state consistency for adaptive stepping. + */ + void RestartCycle() { + m_mesh_qoi_nodes["velocity"]->Distribute(*m_primal_field_prev); + (*m_primal_field) = *m_primal_field_prev; + (*m_mesh_nodes["mesh_current"]) = (*m_mesh_nodes["mesh_t_beg"]); + } + + /** + * @brief Finalize current cycle after successful convergence + * + * @details Completes the current time step by: + * 1. Copying current primal field to previous (for next rollback if needed) + * 2. Computing displacement = current_coords - reference_coords + * 3. Distributing velocity solution to grid function + * 4. Updating time-start coordinates = current coordinates + * + * Prepares the simulation state for the next time step. + */ + void FinishCycle(); + + // ========================================================================= + // FINITE ELEMENT SPACE MANAGEMENT + // ========================================================================= + + /** + * @brief Get finite element space for specified vector dimension + * + * @param vdim Vector dimension required + * @return Shared pointer to finite element space + * + * @details Creates L2 discontinuous finite element spaces with specified vector + * dimension on demand. Spaces are cached for reuse. Uses L2 elements appropriate + * for quadrature data projection and visualization. + * + * **Common Vector Dimensions**: + * - vdim = 1: Scalar fields (pressure, temperature, von Mises stress) + * - vdim = 3: Vector fields (velocity, displacement) + * - vdim = 6: Symmetric tensors (stress, strain in Voigt notation) + * - vdim = 9: Full tensors (deformation gradient, velocity gradient) + */ + std::shared_ptr GetParFiniteElementSpace(const int vdim); + + /** + * @brief Get the main mesh finite element space + * + * @return Shared pointer to mesh finite element space + * + * @details Returns the finite element space used for mesh coordinates + * and primary solution fields. Typically uses H1 continuous elements + * for the velocity-based formulation. + */ + std::shared_ptr GetMeshParFiniteElementSpace() { + return m_mesh_fes; + } + + /** + * @brief Get finite element collection by string identifier + * + * @param fec_str String identifier for the finite element collection + * @return Shared pointer to finite element collection + * + * @details Retrieves or creates finite element collections based on string identifiers. + * The string format typically follows the pattern "ElementType_SpaceDim_Order" + * (e.g., "L2_3D_P0", "H1_2D_P1"). Collections are cached for reuse to avoid + * unnecessary memory allocation. + * + * **Common Collection Types**: + * - "L2_3D_P0": Discontinuous constant elements for 3D + * - "H1_3D_P1": Continuous linear elements for 3D + * - "L2_2D_P0": Discontinuous constant elements for 2D + * + * Used internally by GetParFiniteElementSpace() and other methods requiring + * specific finite element collections. + */ + std::shared_ptr + GetFiniteElementCollection(const std::string fec_str) { + return m_map_fec[fec_str]; + } + + // ========================================================================= + // MATERIAL AND REGION MANAGEMENT + // ========================================================================= + + /** + * @brief Get number of material regions + * + * @return Number of regions in the simulation + */ + size_t GetNumberOfRegions() const { + return m_material_name_region.size(); + } + + /** + * @brief Get material model type for a region + * + * @param idx Region index + * @return Material model type (EXACMECH, UMAT, etc.) + */ + MechType GetRegionModelType(const size_t idx) const { + return m_region_material_type[idx]; + } + + /** + * @brief Get region name string + * + * @param region Region index (-1 for global) + * @return Region name string + * + * @details Returns formatted region name as "material_name_region_id" + * (e.g., "steel_1", "aluminum_2") or "global" for region = -1. + */ + std::string GetRegionName(const int region) const { + if (region < 0) { + return "global"; + } + size_t region_idx = static_cast(region); + return m_material_name_region[region_idx].first + "_" + + std::to_string(m_material_name_region[region_idx].second + 1); + } + + /** + * @brief Get display region name string + * + * @param region Region index (-1 for global) + * @return Region name string + * + * @details Returns formatted region name as "material_name_region_id" + * (e.g., "Steel 1", "Aluminum 2") or "Global" for region = -1. + */ + std::string GetRegionDisplayName(const int region) const; + + /** + * @brief Get material properties for a specific region + * + * @param region Region index + * @return Const reference to material properties vector + * + * @details Material properties are stored as vectors of doubles containing + * model-specific parameters (elastic moduli, yield strengths, hardening + * parameters, etc.). + */ + const std::vector& GetMaterialProperties(const int region) const { + const auto region_name = GetRegionName(region); + return GetMaterialProperties(region_name); + } + + /** + * @brief Get material properties by region name + * + * @param region_name Name of the region + * @return Const reference to material properties vector + */ + const std::vector& GetMaterialProperties(const std::string& region_name) const { + return m_material_properties.at(region_name); + } + + /** + * @brief Get grain ID array + * + * @return Shared pointer to grain ID array for crystal plasticity + * + * @details Array mapping each element to its corresponding grain ID + * for crystal plasticity simulations. Used to assign orientations + * and track grain-specific behavior. + */ + std::shared_ptr GetGrains() { + return m_grains; + } + + /** @brief Check if a region has any elements on this MPI rank + * @param region_id The region identifier to check + * @return true if region has elements on this rank, false otherwise + */ + bool IsRegionActive(int region_id) const { + auto it = m_is_region_active.find(region_id); + return it != m_is_region_active.end() && it->second; + } + + /** @brief Get the MPI communicator for a specific region + * @param region_id The region identifier + * @return MPI communicator for the region, or MPI_COMM_NULL if region not on this rank + * @note Only ranks with elements in the region are part of the returned communicator + */ + MPI_Comm GetRegionCommunicator(int region_id) const { + auto it = m_region_communicators.find(region_id); + return (it != m_region_communicators.end()) ? it->second : MPI_COMM_NULL; + } + + /** @brief Get the root (lowest) MPI rank that has a specific region + * @param region_id The region identifier + * @return The lowest rank with this region, or -1 if region doesn't exist + */ + int GetRegionRootRank(int region_id) const { + auto it = m_region_root_rank.find(region_id); + return (it != m_region_root_rank.end()) ? it->second : -1; + } + + /** @brief Get the root (lowest) MPI rank mapping + * @return The root (lowest) MPI rank mapping + */ + const auto& GetRegionRootRankMapping() const { + return m_region_root_rank; + } + + /** @brief Check if this rank is responsible for I/O for a given region + * @param region_id The region identifier + * @return true if this rank should handle I/O for the region + */ + bool IsRegionIORoot(int region_id) const { + return GetRegionRootRank(region_id) == my_id; + } + + int GetMPIID() const { + return my_id; + } + + // ========================================================================= + // SOLUTION FIELD ACCESS + // ========================================================================= + + /** + * @brief Get current primal field (velocity DOFs) + * + * @return Shared pointer to current primal field vector + * + * @details The primal field contains velocity degrees of freedom in + * ExaConstit's velocity-based formulation. This is the primary unknown + * solved by the Newton-Raphson algorithm. + */ + std::shared_ptr GetPrimalField() { + return m_primal_field; + } + + /** + * @brief Get previous time step primal field + * + * @return Shared pointer to previous primal field vector + * + * @details Stores the converged primal field from the previous time step. + * Used for rollback when time step fails and for providing initial + * guesses in adaptive time stepping. + */ + std::shared_ptr GetPrimalFieldPrev() { + return m_primal_field_prev; + } + + // ========================================================================= + // SIMULATION CONTROL + // ========================================================================= + /** + * @brief Get simulation options + * + * @return Const reference to simulation options + */ + const ExaOptions& GetOptions() const { + return m_options; + } + + /** + * @brief Get current simulation time + * + * @return Current time value from TimeManagement + */ + double GetTime() const { + return m_time_manager.GetTime(); + } + + /** + * @brief Get actual simulation time for a given cycle as auto-time step might have changed + * things + * + * @return Current time value from TimeManagement + */ + double GetTrueCycleTime() const { + return m_time_manager.GetTrueCycleTime(); + } + + /** + * @brief Get current time step size + * + * @return Current time step size from TimeManagement + */ + double GetDeltaTime() const { + return m_time_manager.GetDeltaTime(); + } + + /** + * @brief Update time step based on solver performance + * + * @param nr_steps Number of Newton-Raphson iterations required + * @param failure Whether the time step failed to converge + * @return Updated time step status + * + * @details Delegates to TimeManagement for comprehensive adaptive time step control. + * See TimeManagement::UpdateDeltaTime() for detailed algorithm description. + */ + TimeStep UpdateDeltaTime(const int nr_steps, const bool failure = false) { + return m_time_manager.UpdateDeltaTime(nr_steps, failure); + } + + /** + * @brief Get current simulation cycle + * + * @return Current simulation cycle from TimeManagement + */ + size_t GetSimulationCycle() const { + return m_time_manager.GetSimulationCycle(); + } + + /** + * @brief Check if this is the last time step + * + * @return True if simulation has reached final time + */ + bool IsLastStep() const { + return m_time_manager.IsLastStep(); + } + + /** + * @brief Check if simulation is finished + * + * @return True if simulation is complete + */ + bool IsFinished() const { + return m_time_manager.IsFinished(); + } + + /** + * @brief Print time step statistics + * + * @details Outputs current time and time step information for monitoring + * adaptive time step behavior. Delegates to TimeManagement. + */ + void PrintTimeStats() const { + m_time_manager.PrintTimeStats(); + } + + /** + * @brief Print retrial time step statistics + * + * @details Outputs current time and time step information for monitoring + * adaptive time step behavior. Delegates to TimeManagement. + */ + void PrintRetrialTimeStats() const { + m_time_manager.PrintRetrialStats(); + } + +private: + /** @brief Create MPI communicators for each region containing only ranks with that region + * @details This prevents deadlocks in collective operations when some ranks have no + * elements for a region. Must be called after m_is_region_active is populated. + */ + void CreateRegionCommunicators(); + + /** + * @brief Initialize region-specific state variables + * + * @param region_id Region identifier + * @param material Material options for this region + * @param grains2region Mapping from grain IDs to regions + * + * @details Helper method that handles state variable initialization for + * a single material region, including grain orientation assignment and + * model-specific state variable setup. + */ + void InitializeRegionStateVariables(int region_id, + const MaterialOptions& material, + const std::map& grains2region); + + /** + * @brief Utility function to update the number of state variables count in our options if a + * model uses orientations + */ + void UpdateExaOptionsWithOrientationCounts(); + + /** + * @brief Shared crystal orientation data for multi-region crystal plasticity + * + * @details Temporary storage structure used during initialization to share + * crystal orientation data across multiple material regions. This avoids + * loading the same orientation file multiple times when several regions + * use the same grain structure. + * + * The data is loaded once, used by all regions that need it, then cleaned + * up to free memory. All quaternions are stored as unit quaternions representing + * passive rotations from the sample frame to the crystal frame. + */ + struct SharedOrientationData { + /** @brief Unit quaternions (w,x,y,z format) for passive rotations */ + std::vector quaternions; + /** @brief Number of grains loaded */ + int num_grains; + /** @brief Flag indicating if data has been successfully loaded */ + bool is_loaded; + /** + * @brief Default constructor + * + * @details Initializes an empty, unloaded state. + */ + SharedOrientationData() : num_grains(0), is_loaded(false) {} + }; + + /** + * @brief Per-region orientation configuration for crystal plasticity + * + * @details Stores orientation data that has been converted to the specific + * format required by a particular material region. Different material models + * may require different orientation representations (quaternions, Euler angles, + * rotation matrices), so this structure holds the converted data along with + * indexing information. + * + * Used during state variable initialization to efficiently assign orientations + * to elements based on their grain IDs and region membership. + */ + struct OrientationConfig { + /** @brief Orientation data in the format required by this region's material model */ + std::vector data; + /** @brief Number of components per orientation (4 for quaternions, 3 for Euler angles, + * etc.) */ + int stride; + /** @brief Starting index in the state variable vector for orientation data */ + int offset_start; + /** @brief Ending index in the state variable vector for orientation data */ + int offset_end; + /** @brief Flag indicating if this configuration is valid and ready for use */ + bool is_valid; + /** + * @brief Default constructor + * + * @details Initializes an invalid configuration that must be properly + * set up before use. + */ + OrientationConfig() : stride(0), offset_start(-1), offset_end(0), is_valid(false) {} + }; + + /** @brief Shared orientation data for crystal plasticity (temporary storage during + * initialization) */ + SharedOrientationData m_shared_orientation_data; + + /** + * @brief Load shared orientation data for crystal plasticity + * + * @param orientation_file Path to orientation data file + * @param num_grains Number of grains to load + * @return True if successful, false otherwise + * + * @details Loads crystal orientation data (quaternions) that can be shared + * across multiple material regions. Handles file I/O, quaternion normalization, + * and validation. + */ + bool LoadSharedOrientationData(const std::string& orientation_file, int num_grains); + + /** + * @brief Convert quaternions to Euler angles + * + * @param quaternions Vector of quaternion data (w,x,y,z format) + * @param num_grains Number of grains to convert + * @return Vector of Euler angles in Bunge convention + * + * @details Utility function for converting between orientation representations + * when different material models require different formats. + */ + std::vector ConvertQuaternionsToEuler(const std::vector& quaternions, + int num_grains); + + /** + * @brief Convert unit quaternions to rotation matrices + * @param quaternions Vector containing unit quaternions (stride 4) + * @param num_grains Number of grains + * @return Vector of 3x3 rotation matrices (stride 9) + */ + std::vector ConvertQuaternionsToMatrix(const std::vector& quaternions, + int num_grains); + + /** + * @brief Prepare orientation data for a specific region/material + * @param material Material options containing grain info and orientation requirements + * @return OrientationConfig with data converted to the format required by this material + */ + OrientationConfig PrepareOrientationForRegion(const MaterialOptions& material); + + /** + * @brief Calculate the effective state variable count including orientations + * @param material Material options + * @return Total count including orientation variables if present + */ + int CalculateEffectiveStateVarCount(const MaterialOptions& material); + + /** + * @brief Determine placement offsets for orientation data in state variable array + * @param material Material options + * @param orientation_stride Number of orientation components per grain + * @return Pair of (offset_start, offset_end) indices + */ + std::pair CalculateOrientationOffsets(const MaterialOptions& material, + int orientation_stride); + + /** + * @brief Fill orientation data into the state variable array at a specific quadrature point + * @param qf_data Pointer to QuadratureFunction data + * @param qpt_base_index Base index for current quadrature point + * @param qf_vdim Vector dimension of QuadratureFunction + * @param grain_id Grain ID for current element + * @param orientation_config Orientation configuration with data and offsets + */ + void FillOrientationData(double* qf_data, + int qpt_base_index, + int qf_vdim, + int grain_id, + const OrientationConfig& orientation_config); + + /** + * @brief Clean up shared orientation data after initialization + * + * @details Frees memory used by shared orientation data after all regions + * have been initialized. Helps reduce memory footprint for large simulations. + */ + void CleanupSharedOrientationData(); +}; \ No newline at end of file diff --git a/src/solvers/mechanics_solver.cpp b/src/solvers/mechanics_solver.cpp new file mode 100644 index 0000000..4b35bb0 --- /dev/null +++ b/src/solvers/mechanics_solver.cpp @@ -0,0 +1,352 @@ +#include "solvers/mechanics_solver.hpp" + +#include "utilities/mechanics_log.hpp" +#include "utilities/unified_logger.hpp" + +#include "mfem.hpp" +#include "mfem/general/globals.hpp" +#include "mfem/linalg/linalg.hpp" + +#include +#include +#include +#include + +/** + * @brief Set operator implementation for general Operator + * + * @details This implementation: + * 1. Stores the operator reference and extracts dimensions + * 2. Validates that the operator is square (required for Newton method) + * 3. Initializes residual and correction vectors with device memory + * 4. Configures vectors for GPU execution when available + */ +void ExaNewtonSolver::SetOperator(const mfem::Operator& op) { + oper = &op; + height = op.Height(); + width = op.Width(); + MFEM_ASSERT_0(height == width, "square Operator is required."); + + r.SetSize(width, mfem::Device::GetMemoryType()); + r.UseDevice(true); + c.SetSize(width, mfem::Device::GetMemoryType()); + c.UseDevice(true); +} + +/** + * @brief Set operator implementation for NonlinearForm + * + * @details This specialized implementation: + * 1. Stores both the NonlinearForm reference and base Operator interface + * 2. Enables specialized mechanics operations through oper_mech pointer + * 3. Provides same setup as general Operator version + * 4. Allows access to mechanics-specific functionality + */ +void ExaNewtonSolver::SetOperator(const std::shared_ptr op) { + oper_mech = op; + oper = op.get(); + height = op->Height(); + width = op->Width(); + MFEM_ASSERT_0(height == width, "square NonlinearForm is required."); + + r.SetSize(width, mfem::Device::GetMemoryType()); + r.UseDevice(true); + c.SetSize(width, mfem::Device::GetMemoryType()); + c.UseDevice(true); +} + +/** + * @brief Newton-Raphson iteration implementation + * + * @details The implementation includes several advanced features: + * + * **Adaptive Scaling**: Monitors convergence rate and automatically reduces + * step size when norm_ratio = norm_current/norm_previous > 0.5 + * + * **Device Compatibility**: All vector operations are device-aware for GPU execution + * + * **Convergence Criteria**: Uses combined absolute and relative tolerance: + * norm_max = max(rel_tol * norm_0, abs_tol) + * + * **Performance Monitoring**: Includes Caliper profiling scopes for: + * - Overall Newton solver performance ("NR_solver") + * - Individual Krylov solver calls ("krylov_solver") + * + * **Error Handling**: Validates finite residual norms and proper setup + */ +void ExaNewtonSolver::Mult(const mfem::Vector& b, mfem::Vector& x) const { + CALI_CXX_MARK_SCOPE("NR_solver"); + MFEM_ASSERT_0(oper_mech, "the Operator is not set (use SetOperator)."); + MFEM_ASSERT_0(prec_mech, "the Solver is not set (use SetSolver)."); + + int it; + double norm0, norm, norm_max; + double norm_prev, norm_ratio; + const bool have_b = (b.Size() == Height()); + + // Might want to use this to fix things later on for example when we have a + // large residual. We might also want to eventually try and find a converged + // relaxation factor which would mean resetting our solution vector a few times. + mfem::Vector x_prev(x.Size()); + x_prev.UseDevice(true); + + if (!iterative_mode) { + x = 0.0; + } + + x_prev = x; + + oper_mech->Mult(x, r); + if (have_b) { + r -= b; + } + + norm0 = norm = norm_prev = Norm(r); + norm_ratio = 1.0; + // Set the value for the norm that we'll exit on + norm_max = std::max(rel_tol * norm, abs_tol); + + prec_mech->iterative_mode = false; + double scale = 1.0; + + // x_{i+1} = x_i - [DF(x_i)]^{-1} [F(x_i)-b] + for (it = 0; true; it++) { + // Make sure the norm is finite + MFEM_ASSERT_0(mfem::IsFinite(norm), "norm = " << norm); + if (print_level >= 0) { + mfem::out << "Newton iteration " << std::setw(2) << it << " : ||r|| = " << norm; + if (it > 0) { + mfem::out << ", ||r||/||r_0|| = " << norm / norm0; + } + mfem::out << '\n'; + } + // See if our solution has converged and we can quit + if (norm <= norm_max) { + converged = 1; + break; + } + // See if we've gone over the max number of desired iterations + if (it >= max_iter) { + converged = 0; + break; + } + + prec_mech->SetOperator(oper_mech->GetGradient(x)); + CALI_MARK_BEGIN("krylov_solver"); + prec_mech->Mult(r, c); // c = [DF(x_i)]^{-1} [F(x_i)-b] + // ExaConstit may use GMRES here + + CALI_MARK_END("krylov_solver"); + const double c_scale = scale; + if (c_scale == 0.0) { + converged = 0; + break; + } + + add(x, -c_scale, c, x); // full update to the current config + // ExaConstit (srw) + + // We now get our new residual + oper_mech->Mult(x, r); + if (have_b) { + r -= b; + } + + // Find our new norm and save our previous time step value. + norm_prev = norm; + norm = Norm(r); + // We're going to more or less use a heuristic method here for now if + // our ratio is greater than 1e-1 then we'll set our scaling factor for + // the next iteration to 0.5. + // We want to do this since it's not uncommon for us to run into the case + // where our solution is oscillating over the one we actually want. + // Eventually, we'll fix this in our scaling factor function. + norm_ratio = norm / norm_prev; + + if (norm_ratio > 5.0e-1) { + scale = 0.5; + if (print_level >= 0) { + mfem::out << "The relaxation factor for the next iteration has been reduced to " + << scale << "\n"; + } + } else { + scale = 1.0; + } + } + + final_iter = it; + final_norm = norm; +} + +/** + * @brief Linear solver interface implementation + * + * @details Simple wrapper that: + * 1. Sets up the preconditioner with the current operator (typically Jacobian) + * 2. Applies the preconditioner to solve the linear system + * 3. Includes Caliper profiling for linear solver performance + * + * @note Despite the name "CGSolver", this method can use any linear solver + * (CG, GMRES, MINRES) depending on the solver configured via SetSolver() + */ +void ExaNewtonSolver::CGSolver(mfem::Operator& oper, const mfem::Vector& b, mfem::Vector& x) const { + prec_mech->SetOperator(oper); + CALI_MARK_BEGIN("krylov_solver"); + prec_mech->Mult(b, x); // c = [DF(x_i)]^{-1} [F(x_i)-b] + // ExaConstit may use GMRES here + + CALI_MARK_END("krylov_solver"); +} + +/** + * @brief Line search Newton implementation + * + * @details The line search algorithm implementation: + * + * **Quadratic Line Search Theory**: + * Given three points and their residual norms (q1, q2, q3), the algorithm + * fits a quadratic polynomial q(s) = as² + bs + c to find the minimum. + * The optimal step size is: ε = -b/(2a) = (3*q1 - 4*q2 + q3) / (4*(q1 - 2*q2 + q3)) + * + * **Robustness Checks**: + * - Validates quadratic fit: (q1 - 2*q2 + q3) > 0 (convex) + * - Bounds step size: 0 < ε < 1 (reasonable range) + * - Fallback logic when quadratic fit fails + * + * **Performance Profiling**: + * - "NRLS_solver" scope for overall line search Newton performance + * - "Line Search" scope specifically for step size computation + * - "krylov_solver" scope for linear solver calls + * + * **Memory Management**: + * - Uses device-compatible temporary vectors (x_prev, Jr) + * - Efficient vector operations with MFEM's device interface + * + * **Failure Handling**: + * - Scale factor of 0.0 triggers immediate convergence failure + * - Graceful degradation when line search produces invalid results + */ +void ExaNewtonLSSolver::Mult(const mfem::Vector& b, mfem::Vector& x) const { + CALI_CXX_MARK_SCOPE("NRLS_solver"); + MFEM_ASSERT_0(oper_mech, "the Operator is not set (use SetOperator)."); + MFEM_ASSERT_0(prec_mech, "the Solver is not set (use SetSolver)."); + + int it; + double norm0, norm, norm_max; + const bool have_b = (b.Size() == Height()); + + // Might want to use this to fix things later on for example when we have a + // large residual. We might also want to eventually try and find a converged + // relaxation factor which would mean resetting our solution vector a few times. + mfem::Vector x_prev(x.Size()); + mfem::Vector Jr(x.Size()); + Jr.UseDevice(true); + x_prev.UseDevice(true); + + if (!iterative_mode) { + x = 0.0; + } + + x_prev = x; + + oper_mech->Mult(x, r); + if (have_b) { + r -= b; + } + + norm0 = norm = Norm(r); + // Set the value for the norm that we'll exit on + norm_max = std::max(rel_tol * norm, abs_tol); + + prec_mech->iterative_mode = false; + double scale = 1.0; + + // x_{i+1} = x_i - [DF(x_i)]^{-1} [F(x_i)-b] + for (it = 0; true; it++) { + // Make sure the norm is finite + MFEM_ASSERT_0(mfem::IsFinite(norm), "norm = " << norm); + if (print_level >= 0) { + mfem::out << "Newton iteration " << std::setw(2) << it << " : ||r|| = " << norm; + if (it > 0) { + mfem::out << ", ||r||/||r_0|| = " << norm / norm0; + } + mfem::out << '\n'; + } + // See if our solution has converged and we can quit + if (norm <= norm_max) { + converged = 1; + break; + } + // See if we've gone over the max number of desired iterations + if (it >= max_iter) { + converged = 0; + break; + } + + prec_mech->SetOperator(oper_mech->GetGradient(x)); + CALI_MARK_BEGIN("krylov_solver"); + prec_mech->Mult(r, c); // c = [DF(x_i)]^{-1} [F(x_i)-b] + // ExaConstit may use GMRES here + CALI_MARK_END("krylov_solver"); + // This line search method is based on the quadratic variation of the norm + // of the residual line search described in this conference paper: + // https://doi.org/10.1007/978-3-642-01970-8_46 . We can probably do better + // than this one. + { + CALI_CXX_MARK_SCOPE("Line Search"); + x_prev = x; + add(x, -1.0, c, x); + oper_mech->Mult(x, r); + if (have_b) { + r -= b; + } + double q1 = norm; + double q3 = Norm(r); + x = x_prev; + add(x, -0.5, c, x); + oper_mech->Mult(x, r); + if (have_b) { + r -= b; + } + double q2 = Norm(r); + + double eps = (3.0 * q1 - 4.0 * q2 + q3) / (4.0 * (q1 - 2.0 * q2 + q3)); + + if ((q1 - 2.0 * q2 + q3) > 0 && eps > 0 && eps < 1) { + scale = eps; + } else if (q3 < q1) { + scale = 1.0; + } else { + // We should probably just quit if this is the case... + scale = 0.05; + } + + if (print_level >= 0) { + mfem::out << "The relaxation factor for this iteration is " << scale << std::endl; + } + + x = x_prev; + } + + const double c_scale = scale; + if (c_scale == 0.0) { + converged = 0; + break; + } + + add(x, -c_scale, c, x); // full update to the current config + // ExaConstit (srw) + + // We now get our new residual + oper_mech->Mult(x, r); + if (have_b) { + r -= b; + } + + // Find our new norm + norm = Norm(r); + } + + final_iter = it; + final_norm = norm; +} \ No newline at end of file diff --git a/src/solvers/mechanics_solver.hpp b/src/solvers/mechanics_solver.hpp new file mode 100644 index 0000000..7396c79 --- /dev/null +++ b/src/solvers/mechanics_solver.hpp @@ -0,0 +1,286 @@ + +#ifndef MECHANICS_SOLVER +#define MECHANICS_SOLVER + +#include "mfem.hpp" +#include "mfem/linalg/solvers.hpp" + +#include +/** + * @brief Newton-Raphson solver for nonlinear solid mechanics problems + * + * @details This class implements Newton's method for solving nonlinear systems of the form F(x) = b + * where F is a nonlinear operator. It extends MFEM's IterativeSolver to provide specialized + * functionality for ExaConstit's solid mechanics applications. + * + * The solver uses the Newton-Raphson iteration: + * x_{i+1} = x_i - [DF(x_i)]^{-1} [F(x_i) - b] + * + * Key features: + * - Device-compatible implementation for CPU/GPU execution + * - Integration with MFEM's operator and linear solver framework + * - Specialized handling for NonlinearForm operators in solid mechanics + * - Automatic scaling factor adjustment for convergence improvement + * - Caliper performance profiling integration + * + * The method GetGradient() must be implemented for the operator F. + * The preconditioner is used (in non-iterative mode) to evaluate + * the action of the inverse gradient of the operator. + */ +class ExaNewtonSolver : public mfem::IterativeSolver { +protected: + /** @brief Residual vector for Newton iterations */ + mutable mfem::Vector r; + + /** @brief Correction vector for Newton iterations */ + mutable mfem::Vector c; + + /** @brief Pointer to the mechanics nonlinear form operator */ + std::shared_ptr oper_mech; + + /** @brief Pointer to the preconditioner */ + std::shared_ptr prec_mech; + +public: + /** + * @brief Default constructor + * + * @details Creates an ExaNewtonSolver instance for single-processor execution. + * The operator and linear solver must be set separately using SetOperator() and SetSolver(). + */ + ExaNewtonSolver() {} + +#ifdef MFEM_USE_MPI + /** + * @brief MPI constructor + * + * @param _comm MPI communicator for parallel execution + * + * @details Creates an ExaNewtonSolver instance for parallel execution using the specified + * MPI communicator. This enables the solver to work with distributed finite element spaces + * and parallel linear solvers. + */ + ExaNewtonSolver(MPI_Comm _comm) : IterativeSolver(_comm) {} +#endif + /** + * @brief Set the nonlinear operator to be solved + * + * @param op The nonlinear operator representing F in F(x) = b + * + * @details Sets up the solver to work with the given operator. The operator must be square + * (height == width) and must implement the GetGradient() method for computing Jacobians. + * This method also initializes the internal residual and correction vectors with appropriate + * device memory settings. + * + * @pre The operator must be square (height == width) + * @post Internal vectors r and c are sized and configured for device execution + */ + virtual void SetOperator(const mfem::Operator& op); + + /** + * @brief Set the nonlinear form operator to be solved + * + * @param op The nonlinear form representing the mechanics problem + * + * @details Specialized version for MFEM NonlinearForm operators, which are commonly used + * in finite element mechanics problems. This method stores both the general operator + * interface and the specific NonlinearForm pointer for specialized mechanics operations. + * + * @pre The NonlinearForm must be square (height == width) + * @post Both oper and oper_mech pointers are set, internal vectors are initialized + */ + virtual void SetOperator(const std::shared_ptr op); + + /** + * @brief Set the linear solver for inverting the Jacobian + * + * @param solver Linear solver for the Newton correction equation + * + * @details This method is equivalent to calling SetPreconditioner(). The linear solver + * is used to solve the linearized system [DF(x_i)] c = [F(x_i) - b] at each Newton iteration. + * Common choices include: + * - CGSolver for symmetric positive definite systems + * - GMRESSolver for general nonsymmetric systems + * - MINRESSolver for symmetric indefinite systems + */ + virtual void SetSolver(mfem::Solver& solver) { + prec = &solver; + } + + /** + * @brief Set the linear solver for inverting the Jacobian + * + * @param solver Linear solver for the Newton correction equation + * + * @details This method is equivalent to calling SetPreconditioner(). The linear solver + * is used to solve the linearized system [DF(x_i)] c = [F(x_i) - b] at each Newton iteration. + * Common choices include: + * - CGSolver for symmetric positive definite systems + * - GMRESSolver for general nonsymmetric systems + * - MINRESSolver for symmetric indefinite systems + */ + virtual void SetSolver(std::shared_ptr solver) { + prec_mech = solver; + } + + /** + * @brief Solve the linearized Newton correction equation + * + * @param oper Linear operator (typically the Jacobian) + * @param b Right-hand side vector + * @param x Solution vector (output) + * + * @details This method solves the linearized Newton system using the configured linear solver. + * It sets up the preconditioner/solver with the given operator and applies it to compute + * the Newton correction. The method is marked with Caliper profiling for performance analysis. + * + * The operation performed is: x = [oper]^{-1} b + * + * @note This method may use different Krylov solvers (CG, GMRES, MINRES) depending on + * the configuration provided during solver setup. + */ + virtual void CGSolver(mfem::Operator& oper, const mfem::Vector& b, mfem::Vector& x) const; + + /** + * @brief Solve the nonlinear system F(x) = b using Newton-Raphson method + * + * @param b Right-hand side vector (if b.Size() != Height(), assumes b = 0) + * @param x Solution vector (input: initial guess, output: converged solution) + * + * @details Main solution method that implements the Newton-Raphson algorithm: + * + * 1. **Initialization**: Set up initial residual r = F(x) - b + * 2. **Newton Iteration Loop**: + * - Check convergence: ||r|| <= max(rel_tol * ||r_0||, abs_tol) + * - Compute Jacobian: J = DF(x_i) + * - Solve linear system: J * c = r + * - Apply scaling factor: x_{i+1} = x_i - scale * c + * - Update residual: r = F(x_{i+1}) - b + * - Adjust scaling factor if convergence stalls + * 3. **Convergence Check**: Exit when tolerance is met or max iterations reached + * + * **Adaptive Scaling**: The solver automatically reduces the scaling factor to 0.5 + * when the residual ratio exceeds 0.5, helping to stabilize convergence for + * difficult nonlinear problems. + * + * **Performance Profiling**: Includes Caliper markers for detailed performance analysis + * of Newton iterations and linear solver calls. + * + * @pre SetOperator() and SetSolver() must be called before Mult() + * @pre The operator must implement GetGradient() for Jacobian computation + * + * @post final_iter contains the number of Newton iterations performed + * @post final_norm contains the final residual norm + * @post converged flag indicates whether the solver converged + */ + virtual void Mult(const mfem::Vector& b, mfem::Vector& x) const; + + // We're going to comment this out for now. + /** @brief This method can be overloaded in derived classes to implement line + search algorithms. */ + /** The base class implementation (NewtonSolver) simply returns 1. A return + value of 0 indicates a failure, interrupting the Newton iteration. */ + // virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const + // { return 1.0; } +}; + +/** + * @brief Newton-Raphson solver with line search for enhanced convergence + * + * @details This class extends ExaNewtonSolver to include a line search algorithm that + * improves convergence robustness for highly nonlinear problems. The line search method + * uses a quadratic variation approach to find an optimal scaling factor for each Newton step. + * + * The line search algorithm: + * 1. Evaluates the residual at three points: x, x - 0.5*c, x - c + * 2. Fits a quadratic polynomial to these residual norms + * 3. Finds the minimum of the quadratic to determine optimal step size + * 4. Falls back to heuristic rules if the quadratic fit is invalid + * + * This approach is particularly useful for: + * - Large deformation problems with geometric nonlinearities + * - Material models with strong nonlinearities (e.g., plasticity, damage) + * - Problems where standard Newton-Raphson exhibits oscillatory behavior + * + * The method GetGradient() must be implemented for the operator F. + * The preconditioner is used (in non-iterative mode) to evaluate + * the action of the inverse gradient of the operator. + * + * Reference: Based on quadratic variation line search described in + * "Numerical Methods for Large Eigenvalue Problems" (https://doi.org/10.1007/978-3-642-01970-8_46) + */ +class ExaNewtonLSSolver : public ExaNewtonSolver { +public: + /** + * @brief Default constructor + * + * @details Creates an ExaNewtonLSSolver instance for single-processor execution. + * Inherits all functionality from ExaNewtonSolver and adds line search capabilities. + */ + ExaNewtonLSSolver() {} + +#ifdef MFEM_USE_MPI + /** + * @brief MPI constructor + * + * @param _comm MPI communicator for parallel execution + * + * @details Creates an ExaNewtonLSSolver instance for parallel execution using the specified + * MPI communicator. The line search algorithm works correctly in parallel environments. + */ + ExaNewtonLSSolver(MPI_Comm _comm) : ExaNewtonSolver(_comm) {} +#endif + /** @brief Use parent class SetOperator methods */ + using ExaNewtonSolver::SetOperator; + + /** @brief Use parent class SetSolver methods */ + using ExaNewtonSolver::SetSolver; + + /** @brief Use parent class CGSolver method */ + using ExaNewtonSolver::CGSolver; + + /** + * @brief Solve the nonlinear system F(x) = b using Newton-Raphson with line search + * + * @param b Right-hand side vector (if b.Size() != Height(), assumes b = 0) + * @param x Solution vector (input: initial guess, output: converged solution) + * + * @details Enhanced Newton-Raphson method with quadratic line search for improved robustness: + * + * 1. **Standard Newton Setup**: Compute residual and Jacobian as in standard Newton + * 2. **Line Search Algorithm**: + * - Store current state: x_prev = x + * - Evaluate residual at x - c: q3 = ||F(x - c) - b|| + * - Evaluate residual at x - 0.5*c: q2 = ||F(x - 0.5*c) - b|| + * - Current residual: q1 = ||F(x) - b|| + * - Fit quadratic: ε = (3*q1 - 4*q2 + q3) / (4*(q1 - 2*q2 + q3)) + * - Apply optimal step: x = x_prev - ε*c + * 3. **Fallback Strategy**: + * - If quadratic fit is invalid: ε = 1.0 (full Newton step) + * - If full step increases residual: ε = 0.05 (heavily damped step) + * - If algorithm fails completely: terminate with convergence failure + * + * **Line Search Benefits**: + * - Prevents divergence in highly nonlinear problems + * - Reduces oscillatory behavior near solution + * - Maintains quadratic convergence when possible + * - Provides automatic step size control + * + * **Performance Considerations**: + * - Requires 2 additional function evaluations per iteration + * - Includes Caliper profiling for line search performance analysis + * - May increase computational cost but improves robustness + * + * @pre SetOperator() and SetSolver() must be called before Mult() + * @pre The operator must implement GetGradient() for Jacobian computation + * + * @post final_iter contains the number of Newton iterations performed + * @post final_norm contains the final residual norm + * @post converged flag indicates whether the solver converged + * + * @note The line search algorithm prints the relaxation factor when print_level >= 0 + */ + virtual void Mult(const mfem::Vector& b, mfem::Vector& x) const; +}; + +#endif diff --git a/src/system_driver.cpp b/src/system_driver.cpp index 1b80983..15f4e2b 100644 --- a/src/system_driver.cpp +++ b/src/system_driver.cpp @@ -1,1095 +1,616 @@ -#include "mfem.hpp" -#include "mfem/general/forall.hpp" -#include "mechanics_log.hpp" + #include "system_driver.hpp" + +#include "boundary_conditions/BCData.hpp" +#include "boundary_conditions/BCManager.hpp" +#include "utilities/mechanics_kernels.hpp" +#include "utilities/mechanics_log.hpp" +#include "utilities/unified_logger.hpp" + +#include "ECMech_const.h" #include "RAJA/RAJA.hpp" -#include "mechanics_kernels.hpp" -#include "BCData.hpp" -#include "BCManager.hpp" -#include "mechanics_lightup.hpp" +#include "mfem.hpp" +#include "mfem/general/forall.hpp" #include #include -#include "ECMech_const.h" - -using namespace mfem; -void DirBdrFunc(int attr_id, Vector &y) -{ - BCManager & bcManager = BCManager::getInstance(); - BCData & bc = bcManager.GetBCInstance(attr_id); - - bc.setDirBCs(y); +/** + * @brief Dirichlet boundary condition function for MFEM integration + * + * @param attr_id Boundary attribute identifier from the mesh + * @param y Output vector where boundary condition values will be set + * + * @details This function serves as the interface between MFEM's boundary condition + * system and ExaConstit's boundary condition management. It is used as a callback + * function during finite element assembly to apply Dirichlet boundary conditions. + * + * The function: + * 1. Gets the singleton BCManager instance + * 2. Retrieves the appropriate BCData instance for the given boundary attribute + * 3. Applies the boundary condition values to the output vector + * + * This function is typically passed to MFEM's VectorFunctionRestrictedCoefficient + * or similar boundary condition mechanisms during system setup. + * + * @note The attr_id corresponds to mesh boundary attributes and must match the + * boundary IDs used during BCManager initialization. + */ +void DirBdrFunc(int attr_id, mfem::Vector& y) { + BCManager& bcManager = BCManager::GetInstance(); + BCData& bc = bcManager.GetBCInstance(attr_id); + + bc.SetDirBCs(y); } namespace { - // Once again NVCC is the bain of my existence for not allowing - // valid code to run... - template - void min_max_helper(const int space_dim, - const size_t nnodes, - const T& class_device, - mfem::Vector* const nodes, - mfem::Vector& origin) - { - // Our nodes are by default saved in xxx..., yyy..., zzz... ordering rather - // than xyz, xyz, ... - // So, the below should get us a device reference that can be used. - const auto X = mfem::Reshape(nodes->Read(), nnodes, space_dim); - mfem::Vector min_origin(space_dim); min_origin = std::numeric_limits::max(); - mfem::Vector max_origin(space_dim); max_origin = -std::numeric_limits::max(); - min_origin.HostReadWrite(); - max_origin.HostReadWrite(); - // We need to calculate the minimum point in the mesh to get the correct velocity gradient across - // the part. - RAJA::RangeSegment default_range(0, nnodes); - if (class_device == RTModel::CPU) { - for (int j = 0; j < space_dim; j++) { +/** + * @brief Helper function to find mesh bounding box for velocity gradient calculations + * + * @tparam T Device execution policy type (CPU/GPU) + * @param space_dim Spatial dimension of the problem (2D or 3D) + * @param nnodes Number of nodes in the mesh + * @param class_device Device execution policy instance + * @param nodes Pointer to mesh node coordinates vector + * @param origin Output vector containing min and max coordinates [min_x, min_y, min_z, max_x, + * max_y, max_z] + * + * @details Calculates the minimum and maximum coordinates of the mesh nodes across all + * spatial dimensions. This information is needed for velocity gradient boundary conditions + * that require knowledge of the mesh extent. + * + * The function: + * 1. Handles the MFEM node ordering (xxx..., yyy..., zzz... rather than xyz, xyz...) + * 2. Uses device-compatible reduction operations for GPU execution + * 3. Performs MPI reductions to find global min/max across all processes + * 4. Stores results in the origin vector with min values first, then max values + * + * @note This is a template function to support different device execution policies. + * The "NVCC is the bane of my existence" comment refers to CUDA compiler limitations + * that necessitated this template approach. + */ +template +void min_max_helper(const int space_dim, + const size_t nnodes, + const T& class_device, + mfem::Vector* const nodes, + mfem::Vector& origin) { + // Our nodes are by default saved in xxx..., yyy..., zzz... ordering rather + // than xyz, xyz, ... + // So, the below should get us a device reference that can be used. + const auto X = mfem::Reshape(nodes->Read(), nnodes, space_dim); + mfem::Vector min_origin(space_dim); + min_origin = std::numeric_limits::max(); + mfem::Vector max_origin(space_dim); + max_origin = -std::numeric_limits::max(); + + min_origin.HostReadWrite(); + max_origin.HostReadWrite(); + // We need to calculate the minimum point in the mesh to get the correct velocity gradient + // across the part. + RAJA::RangeSegment default_range(0, static_cast(nnodes)); + if (class_device == RTModel::CPU) { + for (int j = 0; j < space_dim; j++) { RAJA::ReduceMin seq_min(std::numeric_limits::max()); RAJA::ReduceMax seq_max(-std::numeric_limits::max()); - RAJA::forall(default_range, [ = ] (int i){ - seq_min.min(X(i, j)); - seq_max.max(X(i, j)); + RAJA::forall(default_range, [=](int i) { + seq_min.min(X(i, j)); + seq_max.max(X(i, j)); }); min_origin(j) = seq_min.get(); max_origin(j) = seq_max.get(); - } - } + } + } #if defined(RAJA_ENABLE_OPENMP) - if (class_device == RTModel::OPENMP) { - for (int j = 0; j < space_dim; j++) { - RAJA::ReduceMin omp_min(std::numeric_limits::max()); - RAJA::ReduceMax omp_max(-std::numeric_limits::max()); - RAJA::forall(default_range, [ = ] (int i){ - omp_min.min(X(i, j)); - omp_max.max(X(i, j)); + if (class_device == RTModel::OPENMP) { + for (int j = 0; j < space_dim; j++) { + RAJA::ReduceMin omp_min( + std::numeric_limits::max()); + RAJA::ReduceMax omp_max( + -std::numeric_limits::max()); + RAJA::forall(default_range, [=](int i) { + omp_min.min(X(i, j)); + omp_max.max(X(i, j)); }); min_origin(j) = omp_min.get(); max_origin(j) = omp_max.get(); - } - } + } + } #endif #if defined(RAJA_ENABLE_CUDA) || defined(RAJA_ENABLE_HIP) - if (class_device == RTModel::GPU) { + if (class_device == RTModel::GPU) { #if defined(RAJA_ENABLE_CUDA) - using gpu_reduce = RAJA::cuda_reduce; - using gpu_policy = RAJA::cuda_exec<1024>; + using gpu_reduce = RAJA::cuda_reduce; + using gpu_policy = RAJA::cuda_exec<1024>; #else - using gpu_reduce = RAJA::hip_reduce; - using gpu_policy = RAJA::hip_exec<1024>; + using gpu_reduce = RAJA::hip_reduce; + using gpu_policy = RAJA::hip_exec<1024>; #endif - for (int j = 0; j < space_dim; j++) { + for (int j = 0; j < space_dim; j++) { RAJA::ReduceMin gpu_min(std::numeric_limits::max()); RAJA::ReduceMax gpu_max(-std::numeric_limits::max()); - RAJA::forall(default_range, [ = ] RAJA_DEVICE(int i){ - gpu_min.min(X(i, j)); - gpu_max.max(X(i, j)); + RAJA::forall(default_range, [=] RAJA_DEVICE(int i) { + gpu_min.min(X(i, j)); + gpu_max.max(X(i, j)); }); min_origin(j) = gpu_min.get(); max_origin(j) = gpu_max.get(); - } - } + } + } #endif - MPI_Allreduce(min_origin.HostRead(), origin.HostReadWrite(), space_dim, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD); - MPI_Allreduce(max_origin.HostRead(), &origin.HostReadWrite()[space_dim], space_dim, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); - }// End of finding max and min locations + MPI_Allreduce(min_origin.HostRead(), + origin.HostReadWrite(), + space_dim, + MPI_DOUBLE, + MPI_MIN, + MPI_COMM_WORLD); + MPI_Allreduce(max_origin.HostRead(), + &origin.HostReadWrite()[space_dim], + space_dim, + MPI_DOUBLE, + MPI_MAX, + MPI_COMM_WORLD); +} // End of finding max and min locations +} // namespace + +bool is_vgrad_option_flag(const std::shared_ptr sim_state) { + const auto& bo = sim_state->GetOptions().boundary_conditions; + if (bo.vgrad_bcs.size() > 0) { + if (bo.vgrad_bcs[0].origin) { + return true; + } + } + return false; } -SystemDriver::SystemDriver(ParFiniteElementSpace &fes, - ExaOptions &options, - QuadratureFunction &q_matVars0, - QuadratureFunction &q_matVars1, - QuadratureFunction &q_sigma0, - QuadratureFunction &q_sigma1, - QuadratureFunction &q_matGrad, - QuadratureFunction &q_kinVars0, - QuadratureFunction &q_vonMises, - QuadratureFunction *q_evec, - ParGridFunction &ref_crds, - ParGridFunction &beg_crds, - ParGridFunction &end_crds, - Vector &matProps, - int nStateVars) - : fe_space(fes), mech_type(options.mech_type), class_device(options.rtmodel), - additional_avgs(options.additional_avgs), auto_time(options.dt_auto), - avg_stress_fname(options.avg_stress_fname), avg_pl_work_fname(options.avg_pl_work_fname), - avg_def_grad_fname(options.avg_def_grad_fname), - avg_euler_strain_fname(options.avg_euler_strain_fname), - avg_eps_fname(options.avg_eps_fname), - vgrad_origin_flag(options.vgrad_origin_flag), mono_def_flag(options.mono_def_flag), - def_grad(q_kinVars0), evec(q_evec) -{ - CALI_CXX_MARK_SCOPE("system_driver_init"); - - if (auto_time) { - dt_min = options.dt_min; - dt_max = options.dt_max; - dt_class = options.dt; - dt_scale = options.dt_scale; - auto_dt_fname = options.dt_file; - } - - const int space_dim = fe_space.GetParMesh()->SpaceDimension(); - // set the size of the essential boundary conditions attribute array - ess_bdr["total"] = mfem::Array(); - ess_bdr["total"].SetSize(fe_space.GetMesh()->bdr_attributes.Max()); - ess_bdr["total"] = 0; - ess_bdr["ess_vel"] = mfem::Array(); - ess_bdr["ess_vel"].SetSize(fe_space.GetMesh()->bdr_attributes.Max()); - ess_bdr["ess_vel"] = 0; - ess_bdr["ess_vgrad"] = mfem::Array(); - ess_bdr["ess_vgrad"].SetSize(fe_space.GetMesh()->bdr_attributes.Max()); - ess_bdr["ess_vgrad"] = 0; - - ess_bdr_component["total"] = mfem::Array2D(); - ess_bdr_component["total"].SetSize(fe_space.GetMesh()->bdr_attributes.Max(), space_dim); - ess_bdr_component["total"] = false; - ess_bdr_component["ess_vel"] = mfem::Array2D(); - ess_bdr_component["ess_vel"].SetSize(fe_space.GetMesh()->bdr_attributes.Max(), space_dim); - ess_bdr_component["ess_vel"] = false; - ess_bdr_component["ess_vgrad"] = mfem::Array2D(); - ess_bdr_component["ess_vgrad"].SetSize(fe_space.GetMesh()->bdr_attributes.Max(), space_dim); - ess_bdr_component["ess_vgrad"] = false; - - ess_bdr_scale.SetSize(fe_space.GetMesh()->bdr_attributes.Max(), space_dim); - ess_bdr_scale = 0.0; - ess_velocity_gradient.SetSize(space_dim * space_dim, mfem::Device::GetMemoryType()); ess_velocity_gradient.UseDevice(true); - - vgrad_origin.SetSize(space_dim, mfem::Device::GetMemoryType()); vgrad_origin.UseDevice(true); - if (vgrad_origin_flag) { - vgrad_origin.HostReadWrite(); - vgrad_origin(0) = options.vgrad_origin.at(0); - vgrad_origin(1) = options.vgrad_origin.at(1); - vgrad_origin(2) = options.vgrad_origin.at(2); - } - - // Set things to the initial step - BCManager::getInstance().getUpdateStep(1); - BCManager::getInstance().updateBCData(ess_bdr, ess_bdr_scale, ess_velocity_gradient, ess_bdr_component); - - mech_operator = new NonlinearMechOperator(fes, ess_bdr["total"], ess_bdr_component["total"], - options, q_matVars0, q_matVars1, - q_sigma0, q_sigma1, q_matGrad, - q_kinVars0, q_vonMises, ref_crds, - beg_crds, end_crds, matProps, - nStateVars); - model = mech_operator->GetModel(); - - if (options.light_up) { - light_up = new LightUpCubic(options.light_hkls, - options.light_dist_tol, - options.light_s_dir, - &fe_space, - def_grad.GetSpace(), - *model->GetQFMapping(), - options.rtmodel, - options.lattice_basename, - options.lattice_params); - } - - if (mono_def_flag) - { - const auto nodes = fe_space.GetParMesh()->GetNodes(); - const int space_dim = fe_space.GetParMesh()->SpaceDimension(); - const int nnodes = nodes->Size() / space_dim; - Vector origin(space_dim * 2, mfem::Device::GetMemoryType()); origin.UseDevice(true); origin = 0.0; - // Just scoping variable usage so we can reuse variables if we'd want to - // CUDA once again is limiting us from writing normal C++ - // code so had to move to a helper function for this part... - min_max_helper(space_dim, nnodes, class_device, nodes, origin); - - mfem::Array ess_vdofs, ess_tdofs, ess_true_dofs; - ess_vdofs.SetSize(fe_space.GetVSize()); - ess_vdofs = 0; - // We need to set the ess_vdofs doing something like ess_vdofs[i] = -1; - // However, the compiler thinks ess_vdofs is const when trying to do this in - // the later loop, so we turn to lambda fcns to do this so the compiler picks - // the right mfem::Array::operator[](int i) fcn. - auto f = [&ess_vdofs](int i) { ess_vdofs[i] = -1; }; - const auto X = mfem::Reshape(nodes->HostRead(), nnodes, space_dim); - // For this we would need to set up the true dofs at start of simulation - // before anything actually moves - // X's dofs would be at global min(x, z) - // Y's dofs would be at global min(x, y, z) - // Z's dofs would be at global min(z) | global max(z) - RAJA::RangeSegment default_range(0, nnodes); - RAJA::forall(default_range, [ = ] (int i) { - const double x_diff_min = std::abs(X(i, 0) - origin(0)); - const double y_diff_min = std::abs(X(i, 1) - origin(1)); - const double z_diff_min = std::abs(X(i, 2) - origin(2)); - const double z_diff_max = std::abs(X(i, 2) - origin(5)); - if (x_diff_min < 1e-12 && z_diff_min < 1e-12) { - auto dof = fe_space.DofToVDof(i, 0); - f(dof); - } - if (x_diff_min < 1e-12 && y_diff_min < 1e-12 && z_diff_min < 1e-12) { - auto dof = fe_space.DofToVDof(i, 1); - f(dof); - } - if (z_diff_min < 1e-12 || z_diff_max < 1e-12) { - auto dof = fe_space.DofToVDof(i, 2); - f(dof); - } - });//end loop over nodes - // Taken from mfem::FiniteElementSpace::GetEssentialTrueDofs(...) - fe_space.Synchronize(ess_vdofs); - fe_space.GetRestrictionMatrix()->BooleanMult(ess_vdofs, ess_tdofs); - fe_space.MarkerToList(ess_tdofs, ess_true_dofs); - mech_operator->UpdateEssTDofs(ess_true_dofs, mono_def_flag); - } - - - MPI_Comm_rank(MPI_COMM_WORLD, &myid); - - ess_bdr_func = new mfem::VectorFunctionRestrictedCoefficient(space_dim, DirBdrFunc, ess_bdr["ess_vel"], ess_bdr_scale); - - // Partial assembly we need to use a matrix free option instead for our preconditioner - // Everything else remains the same. - if (options.assembly != Assembly::FULL) { - J_prec = mech_operator->GetPAPreconditioner(); - } - else { - if (options.solver == KrylovSolver::GMRES || options.solver == KrylovSolver::PCG) { - HypreBoomerAMG *prec_amg = new HypreBoomerAMG(); - HYPRE_Solver h_amg = (HYPRE_Solver) * prec_amg; - HYPRE_Real st_val = 0.90; - HYPRE_Real rt_val = -10.0; - // HYPRE_Real om_val = 1.0; - // - [[maybe_unused]] int ml = HYPRE_BoomerAMGSetMaxLevels(h_amg, 30); - ml = HYPRE_BoomerAMGSetCoarsenType(h_amg, 0); - ml = HYPRE_BoomerAMGSetMeasureType(h_amg, 0); - ml = HYPRE_BoomerAMGSetStrongThreshold(h_amg, st_val); - ml = HYPRE_BoomerAMGSetNumSweeps(h_amg, 3); - ml = HYPRE_BoomerAMGSetRelaxType(h_amg, 8); - // int rwt = HYPRE_BoomerAMGSetRelaxWt(h_amg, rt_val); - // int ro = HYPRE_BoomerAMGSetOuterWt(h_amg, om_val); - // Dimensionality of our problem - ml = HYPRE_BoomerAMGSetNumFunctions(h_amg, 3); - ml = HYPRE_BoomerAMGSetSmoothType(h_amg, 6); - ml = HYPRE_BoomerAMGSetSmoothNumLevels(h_amg, 3); - ml = HYPRE_BoomerAMGSetSmoothNumSweeps(h_amg, 3); - ml = HYPRE_BoomerAMGSetVariant(h_amg, 0); - ml = HYPRE_BoomerAMGSetOverlap(h_amg, 0); - ml = HYPRE_BoomerAMGSetDomainType(h_amg, 1); - ml = HYPRE_BoomerAMGSetSchwarzRlxWeight(h_amg, rt_val); - - prec_amg->SetPrintLevel(0); - J_prec = prec_amg; - } - else { - HypreSmoother *J_hypreSmoother = new HypreSmoother; - J_hypreSmoother->SetType(HypreSmoother::l1Jacobi); - J_hypreSmoother->SetPositiveDiagonal(true); - J_prec = J_hypreSmoother; - } - } - if (options.solver == KrylovSolver::GMRES) { - GMRESSolver *J_gmres = new GMRESSolver(fe_space.GetComm()); - // These tolerances are currently hard coded while things are being debugged - // but they should eventually be moved back to being set by the options - // J_gmres->iterative_mode = false; - // The relative tolerance should be at this point or smaller - J_gmres->SetRelTol(options.krylov_rel_tol); - // The absolute tolerance could probably get even smaller then this - J_gmres->SetAbsTol(options.krylov_abs_tol); - J_gmres->SetMaxIter(options.krylov_iter); - J_gmres->SetPrintLevel(0); - J_gmres->SetPreconditioner(*J_prec); - J_solver = J_gmres; - } - else if (options.solver == KrylovSolver::PCG) { - CGSolver *J_pcg = new CGSolver(fe_space.GetComm()); - // These tolerances are currently hard coded while things are being debugged - // but they should eventually be moved back to being set by the options - // The relative tolerance should be at this point or smaller - J_pcg->SetRelTol(options.krylov_rel_tol); - // The absolute tolerance could probably get even smaller then this - J_pcg->SetAbsTol(options.krylov_abs_tol); - J_pcg->SetMaxIter(options.krylov_iter); - J_pcg->SetPrintLevel(0); - J_pcg->SetPreconditioner(*J_prec); - J_solver = J_pcg; - } - else { - MINRESSolver *J_minres = new MINRESSolver(fe_space.GetComm()); - J_minres->SetRelTol(options.krylov_rel_tol); - J_minres->SetAbsTol(options.krylov_abs_tol); - J_minres->SetMaxIter(options.krylov_iter); - J_minres->SetPrintLevel(-1); - J_minres->SetPreconditioner(*J_prec); - J_solver = J_minres; - } - // We might want to change our # iterations used in the newton solver - // for the 1st time step. We'll want to swap back to the old one after this - // step. - newton_iter = options.newton_iter; - if (options.nl_solver == NLSolver::NR) { - newton_solver = new ExaNewtonSolver(fes.GetComm()); - } - else if (options.nl_solver == NLSolver::NRLS) { - newton_solver = new ExaNewtonLSSolver(fes.GetComm()); - } - - // Set the newton solve parameters - newton_solver->iterative_mode = true; - newton_solver->SetSolver(*J_solver); - newton_solver->SetOperator(*mech_operator); - newton_solver->SetPrintLevel(1); - newton_solver->SetRelTol(options.newton_rel_tol); - newton_solver->SetAbsTol(options.newton_abs_tol); - newton_solver->SetMaxIter(options.newton_iter); - if (options.visit || options.conduit || options.paraview || options.adios2) { - postprocessing = true; - CalcElementAvg(evec, model->GetMatVars0()); - } else { - postprocessing = false; - } +bool is_expt_mono_flag(const std::shared_ptr sim_state) { + return sim_state->GetOptions().boundary_conditions.mono_def_bcs; } -const Array &SystemDriver::GetEssTDofList() -{ - return mech_operator->GetEssTDofList(); -} - -// Solve the Newton system -void SystemDriver::Solve(Vector &x) -{ - Vector zero; - - if (auto_time) { - // This would only happen on the last time step - if (solVars.GetLastStep()) { - dt_class = solVars.GetDTime(); - } - const double dt_old = dt_class; - Vector xprev(x); x.UseDevice(true); - // We provide an initial guess for what our current coordinates will look like - // based on what our last time steps solution was for our velocity field. - // The end nodes are updated before the 1st step of the solution here so we're good. - bool succeed_t = false; - bool succeed = false; - try{ - newton_solver->Mult(zero, x); - succeed_t = newton_solver->GetConverged(); - } - catch(const std::exception &exc) { - // catch anything thrown within try block that derives from std::exception - MFEM_WARNING(exc.what()); - succeed_t = false; - } - catch(...) { - MFEM_WARNING("An unknown exception was thrown in Krylov solver step"); - succeed_t = false; - } - MPI_Allreduce(&succeed_t, &succeed, 1, MPI_C_BOOL, MPI_LAND, MPI_COMM_WORLD); - - if (!succeed) - { - int iter = 0; - while (!succeed && (iter < 4)) { - if (myid == 0) { - MFEM_WARNING("Solution did not converge decreasing dt by input scale factor"); +SystemDriver::SystemDriver(std::shared_ptr sim_state) + : class_device(sim_state->GetOptions().solvers.rtmodel), + auto_time(sim_state->GetOptions().time.time_type == TimeStepType::AUTO), + vgrad_origin_flag(is_vgrad_option_flag(sim_state)), + mono_def_flag(is_expt_mono_flag(sim_state)), m_sim_state(sim_state) { + CALI_CXX_MARK_SCOPE("system_driver_init"); + + const auto& options = sim_state->GetOptions(); + + auto mesh = m_sim_state->GetMesh(); + auto fe_space = m_sim_state->GetMeshParFiniteElementSpace(); + const int space_dim = mesh->SpaceDimension(); + // set the size of the essential boundary conditions attribute array + ess_bdr["total"] = mfem::Array(); + ess_bdr["total"].SetSize(mesh->bdr_attributes.Max()); + ess_bdr["total"] = 0; + ess_bdr["ess_vel"] = mfem::Array(); + ess_bdr["ess_vel"].SetSize(mesh->bdr_attributes.Max()); + ess_bdr["ess_vel"] = 0; + ess_bdr["ess_vgrad"] = mfem::Array(); + ess_bdr["ess_vgrad"].SetSize(mesh->bdr_attributes.Max()); + ess_bdr["ess_vgrad"] = 0; + + ess_bdr_component["total"] = mfem::Array2D(); + ess_bdr_component["total"].SetSize(mesh->bdr_attributes.Max(), space_dim); + ess_bdr_component["total"] = false; + ess_bdr_component["ess_vel"] = mfem::Array2D(); + ess_bdr_component["ess_vel"].SetSize(mesh->bdr_attributes.Max(), space_dim); + ess_bdr_component["ess_vel"] = false; + ess_bdr_component["ess_vgrad"] = mfem::Array2D(); + ess_bdr_component["ess_vgrad"].SetSize(mesh->bdr_attributes.Max(), space_dim); + ess_bdr_component["ess_vgrad"] = false; + + ess_bdr_scale.SetSize(mesh->bdr_attributes.Max(), space_dim); + ess_bdr_scale = 0.0; + ess_velocity_gradient.SetSize(space_dim * space_dim, mfem::Device::GetMemoryType()); + ess_velocity_gradient.UseDevice(true); + + vgrad_origin.SetSize(space_dim, mfem::Device::GetMemoryType()); + vgrad_origin.UseDevice(true); + if (vgrad_origin_flag) { + vgrad_origin.HostReadWrite(); + vgrad_origin = 0.0; + // already checked if this exists + auto origin = sim_state->GetOptions().boundary_conditions.vgrad_bcs[0].origin; + vgrad_origin(0) = (*origin)[0]; + vgrad_origin(1) = (*origin)[1]; + vgrad_origin(2) = (*origin)[2]; + } + + // Set things to the initial step + BCManager::GetInstance().GetUpdateStep(1); + BCManager::GetInstance().UpdateBCData( + ess_bdr, ess_bdr_scale, ess_velocity_gradient, ess_bdr_component); + mech_operator = std::make_shared( + ess_bdr["total"], ess_bdr_component["total"], m_sim_state); + model = mech_operator->GetModel(); + + if (mono_def_flag) { + const auto nodes = mesh->GetNodes(); + const int nnodes = nodes->Size() / space_dim; + mfem::Vector origin(space_dim * 2, mfem::Device::GetMemoryType()); + origin.UseDevice(true); + origin = 0.0; + // Just scoping variable usage so we can reuse variables if we'd want to + // CUDA once again is limiting us from writing normal C++ + // code so had to move to a helper function for this part... + min_max_helper(space_dim, static_cast(nnodes), class_device, nodes, origin); + + mfem::Array ess_vdofs, ess_tdofs, ess_true_dofs; + ess_vdofs.SetSize(fe_space->GetVSize()); + ess_vdofs = 0; + // We need to set the ess_vdofs doing something like ess_vdofs[i] = -1; + // However, the compiler thinks ess_vdofs is const when trying to do this in + // the later loop, so we turn to lambda fcns to do this so the compiler picks + // the right mfem::Array::operator[](int i) fcn. + auto f = [&ess_vdofs](int i) { + ess_vdofs[i] = -1; + }; + const auto X = mfem::Reshape(nodes->HostRead(), nnodes, space_dim); + // For this we would need to set up the true dofs at start of simulation + // before anything actually moves + // X's dofs would be at global min(x, z) + // Y's dofs would be at global min(x, y, z) + // Z's dofs would be at global min(z) | global max(z) + RAJA::RangeSegment default_range(0, nnodes); + RAJA::forall(default_range, [=](int i) { + const double x_diff_min = std::abs(X(i, 0) - origin(0)); + const double y_diff_min = std::abs(X(i, 1) - origin(1)); + const double z_diff_min = std::abs(X(i, 2) - origin(2)); + const double z_diff_max = std::abs(X(i, 2) - origin(5)); + if (x_diff_min < 1e-12 && z_diff_min < 1e-12) { + auto dof = fe_space->DofToVDof(i, 0); + f(dof); } - x = xprev; - // Decrease it by a quarter and try again - dt_class *= dt_scale; - if (dt_class < dt_min) { dt_class = dt_min; } - SetDt(dt_class); - try{ - newton_solver->Mult(zero, x); - succeed_t = newton_solver->GetConverged(); + if (x_diff_min < 1e-12 && y_diff_min < 1e-12 && z_diff_min < 1e-12) { + auto dof = fe_space->DofToVDof(i, 1); + f(dof); } - catch (...) { - succeed_t = false; + if (z_diff_min < 1e-12 || z_diff_max < 1e-12) { + auto dof = fe_space->DofToVDof(i, 2); + f(dof); } - MPI_Allreduce(&succeed_t, &succeed, 1, MPI_C_BOOL, MPI_LAND, MPI_COMM_WORLD); - - iter += 1; - } // Do final converge check outside of this while loop - const double old_time = solVars.GetTime(); - const double new_time = old_time - dt_old + dt_class; - solVars.SetTime(new_time); - solVars.SetDt(dt_class); - } - - // Now we're going to save off the current dt value - if (myid == 0 && newton_solver->GetConverged()) { - std::ofstream file; - file.open(auto_dt_fname, std::ios_base::app); - file << std::setprecision(12) << dt_class << std::endl; - } + }); // end loop over nodes + // Taken from mfem::FiniteElementSpace::GetEssentialTrueDofs(...) + fe_space->Synchronize(ess_vdofs); + fe_space->GetRestrictionMatrix()->BooleanMult(ess_vdofs, ess_tdofs); + fe_space->MarkerToList(ess_tdofs, ess_true_dofs); + mech_operator->UpdateEssTDofs(ess_true_dofs, mono_def_flag); + } + + ess_bdr_func = std::make_unique( + space_dim, DirBdrFunc, ess_bdr["ess_vel"], ess_bdr_scale); + + // Partial assembly we need to use a matrix free option instead for our preconditioner + // Everything else remains the same. + auto& linear_solvers = options.solvers.linear_solver; + if (options.solvers.assembly != AssemblyType::FULL) { + J_prec = mech_operator->GetPAPreconditioner(); + } else { + if (linear_solvers.preconditioner == PreconditionerType::AMG) { + auto prec_amg = std::make_shared(); + HYPRE_Solver h_amg = static_cast(*prec_amg); + HYPRE_Real st_val = 0.90; + HYPRE_Real rt_val = -10.0; + // HYPRE_Real om_val = 1.0; + // + [[maybe_unused]] int ml = HYPRE_BoomerAMGSetMaxLevels(h_amg, 30); + ml = HYPRE_BoomerAMGSetCoarsenType(h_amg, 0); + ml = HYPRE_BoomerAMGSetMeasureType(h_amg, 0); + ml = HYPRE_BoomerAMGSetStrongThreshold(h_amg, st_val); + ml = HYPRE_BoomerAMGSetNumSweeps(h_amg, 3); + ml = HYPRE_BoomerAMGSetRelaxType(h_amg, 8); + // int rwt = HYPRE_BoomerAMGSetRelaxWt(h_amg, rt_val); + // int ro = HYPRE_BoomerAMGSetOuterWt(h_amg, om_val); + // Dimensionality of our problem + ml = HYPRE_BoomerAMGSetNumFunctions(h_amg, 3); + ml = HYPRE_BoomerAMGSetSmoothType(h_amg, 6); + ml = HYPRE_BoomerAMGSetSmoothNumLevels(h_amg, 3); + ml = HYPRE_BoomerAMGSetSmoothNumSweeps(h_amg, 3); + ml = HYPRE_BoomerAMGSetVariant(h_amg, 0); + ml = HYPRE_BoomerAMGSetOverlap(h_amg, 0); + ml = HYPRE_BoomerAMGSetDomainType(h_amg, 1); + ml = HYPRE_BoomerAMGSetSchwarzRlxWeight(h_amg, rt_val); + + prec_amg->SetPrintLevel(linear_solvers.print_level); + J_prec = prec_amg; + } else if (linear_solvers.preconditioner == PreconditionerType::ILU) { + auto J_hypreEuclid = std::make_shared(fe_space->GetComm()); + J_prec = J_hypreEuclid; + } else if (linear_solvers.preconditioner == PreconditionerType::L1GS) { + auto J_hypreSmoother = std::make_shared(); + J_hypreSmoother->SetType(mfem::HypreSmoother::l1GS); + J_hypreSmoother->SetPositiveDiagonal(true); + J_prec = J_hypreSmoother; + } else if (linear_solvers.preconditioner == PreconditionerType::CHEBYSHEV) { + auto J_hypreSmoother = std::make_shared(); + J_hypreSmoother->SetType(mfem::HypreSmoother::Chebyshev); + J_prec = J_hypreSmoother; + } else { + auto J_hypreSmoother = std::make_shared(); + J_hypreSmoother->SetType(mfem::HypreSmoother::l1Jacobi); + J_hypreSmoother->SetPositiveDiagonal(true); + J_prec = J_hypreSmoother; + } + } + + if (linear_solvers.solver_type == LinearSolverType::GMRES) { + J_solver = std::make_shared(fe_space->GetComm()); + } else if (linear_solvers.solver_type == LinearSolverType::CG) { + J_solver = std::make_shared(fe_space->GetComm()); + } else if (linear_solvers.solver_type == LinearSolverType::BICGSTAB) { + J_solver = std::make_shared(fe_space->GetComm()); + } else { + J_solver = std::make_shared(fe_space->GetComm()); + } + + // The relative tolerance should be at this point or smaller + J_solver->SetRelTol(linear_solvers.rel_tol); + // The absolute tolerance could probably get even smaller then this + J_solver->SetAbsTol(linear_solvers.abs_tol); + J_solver->SetMaxIter(linear_solvers.max_iter); + J_solver->SetPrintLevel(linear_solvers.print_level); + J_solver->SetPreconditioner(*J_prec); + + auto nonlinear_solver = options.solvers.nonlinear_solver; + newton_iter = nonlinear_solver.iter; + if (nonlinear_solver.nl_solver == NonlinearSolverType::NR) { + newton_solver = std::make_unique( + m_sim_state->GetMeshParFiniteElementSpace()->GetComm()); + } else if (nonlinear_solver.nl_solver == NonlinearSolverType::NRLS) { + newton_solver = std::make_unique( + m_sim_state->GetMeshParFiniteElementSpace()->GetComm()); + } + + // Set the newton solve parameters + newton_solver->iterative_mode = true; + newton_solver->SetSolver(J_solver); + newton_solver->SetOperator(mech_operator); + newton_solver->SetPrintLevel(1); + newton_solver->SetRelTol(nonlinear_solver.rel_tol); + newton_solver->SetAbsTol(nonlinear_solver.abs_tol); + newton_solver->SetMaxIter(nonlinear_solver.iter); +} - // update the dt - const double niter_scale = ((double) newton_iter) * dt_scale; - const double nr_iter = (double) newton_solver->GetNumIterations(); - // Will approach dt_scale as nr_iter -> newton_iter - // dt increases as long as nr_iter > niter_scale - const double factor = niter_scale / nr_iter; - dt_class *= factor; - if (dt_class < dt_min) { dt_class = dt_min; } - if (dt_class > dt_max) { dt_class = dt_max; } - if (myid == 0 && newton_solver->GetConverged()) { - std::cout << "Time "<< solVars.GetTime() << " dt old was " << solVars.GetDTime() << " dt has been updated to " << dt_class << " and changed by a factor of " << factor << std::endl; - } - } - else { - // We provide an initial guess for what our current coordinates will look like - // based on what our last time steps solution was for our velocity field. - // The end nodes are updated before the 1st step of the solution here so we're good. - newton_solver->Mult(zero, x); - } +const mfem::Array& SystemDriver::GetEssTDofList() { + return mech_operator->GetEssTDofList(); +} - // Just gotta be safe incase something in the solver wasn't playing nice and didn't swap things - // back to the current configuration... - // Once the system has finished solving, our current coordinates configuration are based on what our - // converged velocity field ended up being equal to. - MFEM_VERIFY(newton_solver->GetConverged(), "Newton Solver did not converge."); +// Solve the Newton system +void SystemDriver::Solve() { + mfem::Vector zero; + auto x = m_sim_state->GetPrimalField(); + if (auto_time) { + // This would only happen on the last time step + const auto x_prev = m_sim_state->GetPrimalFieldPrev(); + // Vector xprev(x); xprev.UseDevice(true); + // We provide an initial guess for what our current coordinates will look like + // based on what our last time steps solution was for our velocity field. + // The end nodes are updated before the 1st step of the solution here so we're good. + bool succeed_t = false; + bool succeed = false; + try { + newton_solver->Mult(zero, *x); + succeed_t = newton_solver->GetConverged(); + } catch (const std::exception& exc) { + // catch anything thrown within try block that derives from std::exception + MFEM_WARNING_0(exc.what()); + succeed_t = false; + } catch (...) { + MFEM_WARNING_0("An unknown exception was thrown in Krylov solver step"); + succeed_t = false; + } + MPI_Allreduce(&succeed_t, &succeed, 1, MPI_C_BOOL, MPI_LAND, MPI_COMM_WORLD); + TimeStep state = m_sim_state->UpdateDeltaTime(newton_solver->GetNumIterations(), succeed); + if (!succeed) { + while (state == TimeStep::RETRIAL) { + MFEM_WARNING_0("Solution did not converge decreasing dt by input scale factor"); + if (m_sim_state->GetMPIID() == 0) { + m_sim_state->PrintRetrialTimeStats(); + } + m_sim_state->RestartCycle(); + try { + newton_solver->Mult(zero, *x); + succeed_t = newton_solver->GetConverged(); + } catch (...) { + succeed_t = false; + } + MPI_Allreduce(&succeed_t, &succeed, 1, MPI_C_BOOL, MPI_LAND, MPI_COMM_WORLD); + state = m_sim_state->UpdateDeltaTime(newton_solver->GetNumIterations(), succeed); + } // Do final converge check outside of this while loop + } + } else { + // We provide an initial guess for what our current coordinates will look like + // based on what our last time steps solution was for our velocity field. + // The end nodes are updated before the 1st step of the solution here so we're good. + newton_solver->Mult(zero, *x); + m_sim_state->UpdateDeltaTime(newton_solver->GetNumIterations(), true); + } + + // Just gotta be safe incase something in the solver wasn't playing nice and didn't swap things + // back to the current configuration... + // Once the system has finished solving, our current coordinates configuration are based on what + // our converged velocity field ended up being equal to. + if (m_sim_state->GetMPIID() == 0 && newton_solver->GetConverged()) { + ess_bdr_func->SetTime(m_sim_state->GetTime()); + } + MFEM_VERIFY_0(newton_solver->GetConverged(), "Newton Solver did not converge."); } // Solve the Newton system for the 1st time step // It was found that for large meshes a ramp up to our desired applied BC might // be needed. -void SystemDriver::SolveInit(const Vector &xprev, Vector &x) const -{ - Vector b(x); b.UseDevice(true); - - Vector deltaF(x); deltaF.UseDevice(true); - b = 0.0; - // Want our vector for everything not on the Ess BCs to be 0 - // This means when we do K * diffF = b we're actually do the following: - // K_uc * (x - x_prev)_c = deltaF_u - { - deltaF = 0.0; - auto I = mech_operator->GetEssentialTrueDofs().Read(); - auto size = mech_operator->GetEssentialTrueDofs().Size(); - auto Y = deltaF.Write(); - auto XPREV = xprev.Read(); - auto X = x.Read(); - MFEM_FORALL(i, size, Y[I[i]] = X[I[i]] - XPREV[I[i]]; ); - } - mfem::Operator &oper = mech_operator->GetUpdateBCsAction(xprev, deltaF, b); - x = 0.0; - //This will give us our -change in velocity - //So, we want to add the previous velocity terms to it - newton_solver->CGSolver(oper, b, x); - auto X = x.ReadWrite(); - auto XPREV = xprev.Read(); - MFEM_FORALL(i, x.Size(), X[i] = -X[i] + XPREV[i]; ); +void SystemDriver::SolveInit() const { + const auto x = m_sim_state->GetPrimalField(); + const auto x_prev = m_sim_state->GetPrimalFieldPrev(); + mfem::Vector b(*x); + b.UseDevice(true); + + mfem::Vector deltaF(*x); + deltaF.UseDevice(true); + b = 0.0; + // Want our vector for everything not on the Ess BCs to be 0 + // This means when we do K * diffF = b we're actually do the following: + // K_uc * (x - x_prev)_c = deltaF_u + { + deltaF = 0.0; + auto I = mech_operator->GetEssentialTrueDofs().Read(); + auto size = mech_operator->GetEssentialTrueDofs().Size(); + auto Y = deltaF.Write(); + auto XPREV = x_prev->Read(); + auto X = x->Read(); + mfem::forall(size, [=] MFEM_HOST_DEVICE(int i) { + Y[I[i]] = X[I[i]] - XPREV[I[i]]; + }); + } + mfem::Operator& oper = mech_operator->GetUpdateBCsAction(*x_prev, deltaF, b); + x->operator=(0.0); + // This will give us our -change in velocity + // So, we want to add the previous velocity terms to it + newton_solver->CGSolver(oper, b, *x); + auto X = x->ReadWrite(); + auto XPREV = x_prev->Read(); + mfem::forall(x->Size(), [=] MFEM_HOST_DEVICE(int i) { + X[i] = -X[i] + XPREV[i]; + }); + m_sim_state->GetVelocity()->Distribute(*x); } void SystemDriver::UpdateEssBdr() { - if (!mono_def_flag) { - BCManager::getInstance().updateBCData(ess_bdr, ess_bdr_scale, ess_velocity_gradient, ess_bdr_component); - mech_operator->UpdateEssTDofs(ess_bdr["total"], mono_def_flag); - } + if (!mono_def_flag) { + BCManager::GetInstance().UpdateBCData( + ess_bdr, ess_bdr_scale, ess_velocity_gradient, ess_bdr_component); + mech_operator->UpdateEssTDofs(ess_bdr["total"], mono_def_flag); + } } // In the current form, we could honestly probably make use of velocity as our working array -void SystemDriver::UpdateVelocity(mfem::ParGridFunction &velocity, mfem::Vector &vel_tdofs) { - - if (ess_bdr["ess_vel"].Sum() > 0) { - // Now that we're doing velocity based we can just overwrite our data with the ess_bdr_func - velocity.ProjectBdrCoefficient(*ess_bdr_func); // don't need attr list as input - // pulled off the - // VectorFunctionRestrictedCoefficient - // populate the solution vector, v_sol, with the true dofs entries in v_cur. - velocity.GetTrueDofs(vel_tdofs); - } - - if (ess_bdr["ess_vgrad"].Sum() > 0) - { - // Just scoping variable usage so we can reuse variables if we'd want to - { - const auto nodes = fe_space.GetParMesh()->GetNodes(); - const int space_dim = fe_space.GetParMesh()->SpaceDimension(); - const int nnodes = nodes->Size() / space_dim; - - // Our nodes are by default saved in xxx..., yyy..., zzz... ordering rather - // than xyz, xyz, ... - // So, the below should get us a device reference that can be used. - const auto X = mfem::Reshape(nodes->Read(), nnodes, space_dim); - const auto VGRAD = mfem::Reshape(ess_velocity_gradient.Read(), space_dim, space_dim); - velocity = 0.0; - auto VT = mfem::Reshape(velocity.ReadWrite(), nnodes, space_dim); - - if (!vgrad_origin_flag) { - vgrad_origin.HostReadWrite(); - // We need to calculate the minimum point in the mesh to get the correct velocity gradient across - // the part. - RAJA::RangeSegment default_range(0, nnodes); - if (class_device == RTModel::CPU) { - for (int j = 0; j < space_dim; j++) { - RAJA::ReduceMin seq_min(std::numeric_limits::max()); - RAJA::forall(default_range, [ = ] (int i){ - seq_min.min(X(i, j)); - }); - vgrad_origin(j) = seq_min.get(); - } - } +void SystemDriver::UpdateVelocity() { + auto fe_space = m_sim_state->GetMeshParFiniteElementSpace(); + auto mesh = m_sim_state->GetMesh(); + auto velocity = m_sim_state->GetVelocity(); + auto vel_tdofs = m_sim_state->GetPrimalField(); + + if (ess_bdr["ess_vel"].Sum() > 0) { + // Now that we're doing velocity based we can just overwrite our data with the ess_bdr_func + velocity->ProjectBdrCoefficient(*ess_bdr_func); // don't need attr list as input + // pulled off the + // VectorFunctionRestrictedCoefficient + // populate the solution vector, v_sol, with the true dofs entries in v_cur. + velocity->GetTrueDofs(*vel_tdofs); + } + + if (ess_bdr["ess_vgrad"].Sum() > 0) { + // Just scoping variable usage so we can reuse variables if we'd want to + { + const auto nodes = mesh->GetNodes(); + const int space_dim = mesh->SpaceDimension(); + const int nnodes = nodes->Size() / space_dim; + + // Our nodes are by default saved in xxx..., yyy..., zzz... ordering rather + // than xyz, xyz, ... + // So, the below should get us a device reference that can be used. + const auto X = mfem::Reshape(nodes->Read(), nnodes, space_dim); + const auto VGRAD = mfem::Reshape(ess_velocity_gradient.Read(), space_dim, space_dim); + velocity->operator=(0.0); + auto VT = mfem::Reshape(velocity->ReadWrite(), nnodes, space_dim); + + if (!vgrad_origin_flag) { + vgrad_origin.HostReadWrite(); + // We need to calculate the minimum point in the mesh to get the correct velocity + // gradient across the part. + RAJA::RangeSegment default_range(0, nnodes); + if (class_device == RTModel::CPU) { + for (int j = 0; j < space_dim; j++) { + RAJA::ReduceMin seq_min( + std::numeric_limits::max()); + RAJA::forall(default_range, [=](int i) { + seq_min.min(X(i, j)); + }); + vgrad_origin(j) = seq_min.get(); + } + } #if defined(RAJA_ENABLE_OPENMP) - if (class_device == RTModel::OPENMP) { - for (int j = 0; j < space_dim; j++) { - RAJA::ReduceMin omp_min(std::numeric_limits::max()); - RAJA::forall(default_range, [ = ] (int i){ - omp_min.min(X(i, j)); - }); - vgrad_origin(j) = omp_min.get(); - } - } + if (class_device == RTModel::OPENMP) { + for (int j = 0; j < space_dim; j++) { + RAJA::ReduceMin omp_min( + std::numeric_limits::max()); + RAJA::forall(default_range, [=](int i) { + omp_min.min(X(i, j)); + }); + vgrad_origin(j) = omp_min.get(); + } + } #endif #if defined(RAJA_ENABLE_CUDA) || defined(RAJA_ENABLE_HIP) - if (class_device == RTModel::GPU) { + if (class_device == RTModel::GPU) { #if defined(RAJA_ENABLE_CUDA) - using gpu_reduce = RAJA::cuda_reduce; - using gpu_policy = RAJA::cuda_exec<1024>; + using gpu_reduce = RAJA::cuda_reduce; + using gpu_policy = RAJA::cuda_exec<1024>; #else - using gpu_reduce = RAJA::hip_reduce; - using gpu_policy = RAJA::hip_exec<1024>; + using gpu_reduce = RAJA::hip_reduce; + using gpu_policy = RAJA::hip_exec<1024>; #endif - for (int j = 0; j < space_dim; j++) { - RAJA::ReduceMin gpu_min(std::numeric_limits::max()); - RAJA::forall(default_range, [ = ] RAJA_DEVICE(int i){ - gpu_min.min(X(i, j)); - }); - vgrad_origin(j) = gpu_min.get(); - } - } + for (int j = 0; j < space_dim; j++) { + RAJA::ReduceMin gpu_min( + std::numeric_limits::max()); + RAJA::forall(default_range, [=] RAJA_DEVICE(int i) { + gpu_min.min(X(i, j)); + }); + vgrad_origin(j) = gpu_min.get(); + } + } #endif - } // End if vgrad_origin_flag - Vector origin(space_dim, mfem::Device::GetMemoryType()); origin.UseDevice(true); - MPI_Allreduce(vgrad_origin.HostRead(), origin.HostReadWrite(), space_dim, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD); - const double* dmin_x = origin.Read(); - // We've now found our minimum points so we can now go and calculate everything. - MFEM_FORALL(i, nnodes, { - for (int ii = 0; ii < space_dim; ii++) { - for (int jj = 0; jj < space_dim; jj++) { - // mfem::Reshape assumes Fortran memory layout - // which is why everything is the transpose down below... - VT(i, ii) += VGRAD(jj, ii) * (X(i, jj) - dmin_x[jj]); - } - } - }); - } - { - mfem::Vector vel_tdof_tmp(vel_tdofs); vel_tdof_tmp.UseDevice(true); vel_tdof_tmp = 0.0; - velocity.GetTrueDofs(vel_tdof_tmp); - - mfem::Array ess_tdofs(mech_operator->GetEssentialTrueDofs()); - if (!mono_def_flag) { - fe_space.GetEssentialTrueDofs(ess_bdr["ess_vgrad"], ess_tdofs, ess_bdr_component["ess_vgrad"]); - } - auto I = ess_tdofs.Read(); - auto size = ess_tdofs.Size(); - auto Y = vel_tdofs.ReadWrite(); - const auto X = vel_tdof_tmp.Read(); - // vel_tdofs should already have the current solution - MFEM_FORALL(i, size, Y[I[i]] = X[I[i]]; ); - } - } // end of if constant strain rate -} - -void SystemDriver::UpdateModel() -{ - const ParFiniteElementSpace *fes = GetFESpace(); - - model->UpdateModelVars(); - - // internally these two Update methods swap the internal data of the end step - // with the begginning step using a simple pointer swap. - // update the beginning step stress variable - model->UpdateStress(); - // update the beginning step state variables - if (model->numStateVars > 0) { - model->UpdateStateVars(); - } - - { - CALI_CXX_MARK_SCOPE("avg_stress_computation"); - // Here we're getting the average stress value - Vector stress(6); - stress = 0.0; - - const QuadratureFunction *qstress = model->GetStress0(); - - exaconstit::kernel::ComputeVolAvgTensor(fes, qstress, stress, 6, class_device); - - std::cout.setf(std::ios::fixed); - std::cout.setf(std::ios::showpoint); - std::cout.precision(8); - - int my_id; - MPI_Comm_rank(MPI_COMM_WORLD, &my_id); - // Now we're going to save off the average stress tensor to a file - if (my_id == 0) { - std::ofstream file; - - file.open(avg_stress_fname, std::ios_base::app); - - stress.Print(file, 6); - } - } - - if (mech_type == MechType::EXACMECH && additional_avgs) { - CALI_CXX_MARK_SCOPE("extra_avgs_computations"); - const QuadratureFunction *qstate_var = model->GetMatVars0(); - // Here we're getting the average stress value - Vector state_var(qstate_var->GetVDim()); - state_var = 0.0; - - exaconstit::kernel::ComputeVolAvgTensor(fes, qstate_var, state_var, state_var.Size(), class_device); - - mfem::Vector history(model->GetMatVars0()->GetVDim()); - exaconstit::kernel::ComputeVolAvgTensor(fes, model->GetMatVars0(), history, history.Size(), class_device); - - std::cout.setf(std::ios::fixed); - std::cout.setf(std::ios::showpoint); - std::cout.precision(8); - - int my_id; - MPI_Comm_rank(MPI_COMM_WORLD, &my_id); - // Now we're going to save off the average stress tensor to a file - if (my_id == 0) { - auto qf_mapping = model->GetQFMapping(); - { - std::string s_pl_work = "pl_work"; - auto pair = qf_mapping->find(s_pl_work)->second; - - std::ofstream file; - file.open(avg_pl_work_fname, std::ios_base::app); - file << state_var[pair.first] << std::endl; - } - - { - std::string s_eps = "shrEff"; - auto pair = qf_mapping->find(s_eps)->second; - - std::ofstream file; - file.open(avg_eps_fname, std::ios_base::app); - file << state_var[pair.first] << std::endl; - } - - } - } - - if (additional_avgs) - { - CALI_CXX_MARK_SCOPE("extra_avgs_def_grad_computation"); - mech_operator->CalculateDeformationGradient(def_grad); - const QuadratureFunction *qstate_var = &def_grad; - // Here we're getting the average stress value - Vector dgrad(qstate_var->GetVDim()); - dgrad = 0.0; - - exaconstit::kernel::ComputeVolAvgTensor(fes, qstate_var, dgrad, dgrad.Size(), class_device); - - std::cout.setf(std::ios::fixed); - std::cout.setf(std::ios::showpoint); - std::cout.precision(8); - - int my_id; - MPI_Comm_rank(MPI_COMM_WORLD, &my_id); - // Now we're going to save off the average stress tensor to a file - if (my_id == 0) { - std::ofstream file; - file.open(avg_def_grad_fname, std::ios_base::app); - dgrad.Print(file, dgrad.Size()); - } - // Eulerian strain calculation - mfem::DenseMatrix estrain(3, 3); - { - mfem::DenseMatrix def_grad(dgrad.HostReadWrite(), 3, 3); - // Would be nice if we could just do this but maybe we should create more kernels for users... - // ExaModel::CalcEulerianStrain(estrain, def_grad); - - /// Eulerian is simply e = 1/2(I - F^(-t)F^(-1)) - const int dim = 3; - mfem::DenseMatrix Finv(dim), Binv(dim); - double half = 1.0 / 2.0; - - CalcInverse(def_grad, Finv); - MultAtB(Finv, Finv, Binv); - - estrain = 0.0; - - for (int j = 0; j < dim; j++) { - for (int i = 0; i < dim; i++) { - estrain(i, j) -= half * Binv(i, j); + } // End if vgrad_origin_flag + mfem::Vector origin(space_dim, mfem::Device::GetMemoryType()); + origin.UseDevice(true); + MPI_Allreduce(vgrad_origin.HostRead(), + origin.HostReadWrite(), + space_dim, + MPI_DOUBLE, + MPI_MIN, + MPI_COMM_WORLD); + const double* dmin_x = origin.Read(); + // We've now found our minimum points so we can now go and calculate everything. + mfem::forall(nnodes, [=] MFEM_HOST_DEVICE(int i) { + for (int ii = 0; ii < space_dim; ii++) { + for (int jj = 0; jj < space_dim; jj++) { + // mfem::Reshape assumes Fortran memory layout + // which is why everything is the transpose down below... + VT(i, ii) += VGRAD(jj, ii) * (X(i, jj) - dmin_x[jj]); + } + } + }); + } + { + mfem::Vector vel_tdof_tmp(*vel_tdofs); + vel_tdof_tmp.UseDevice(true); + vel_tdof_tmp = 0.0; + velocity->GetTrueDofs(vel_tdof_tmp); + + mfem::Array ess_tdofs(mech_operator->GetEssentialTrueDofs()); + if (!mono_def_flag) { + fe_space->GetEssentialTrueDofs( + ess_bdr["ess_vgrad"], ess_tdofs, ess_bdr_component["ess_vgrad"]); } - estrain(j, j) += half; - } - } - - mfem::Vector euler_strain(6); - euler_strain(0) = estrain(0, 0); - euler_strain(1) = estrain(1, 1); - euler_strain(2) = estrain(2, 2); - euler_strain(3) = estrain(1, 2); - euler_strain(4) = estrain(0, 2); - euler_strain(5) = estrain(0, 1); - - // Now we're going to save off the average stress tensor to a file - if (my_id == 0) { - std::ofstream file; - file.open(avg_euler_strain_fname, std::ios_base::app); - euler_strain.Print(file, euler_strain.Size()); - } - } - - if(postprocessing) { - CalcElementAvg(evec, model->GetMatVars0()); - } - - if(light_up && (mech_type == MechType::EXACMECH)) { - light_up->calculate_lightup_data(*(model->GetMatVars0()), *(model->GetStress0())); - } -} - -void SystemDriver::CalcElementAvg(mfem::Vector *elemVal, const mfem::QuadratureFunction *qf) -{ - - Mesh *mesh = fe_space.GetMesh(); - const FiniteElement &el = *fe_space.GetFE(0); - const IntegrationRule *ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1));; - - const int nqpts = ir->GetNPoints(); - const int nelems = fe_space.GetNE(); - const int vdim = qf->GetVDim(); - - const double* W = ir->GetWeights().Read(); - const GeometricFactors *geom = mesh->GetGeometricFactors(*ir, GeometricFactors::DETERMINANTS); - - const int DIM2 = 2; - const int DIM3 = 3; - std::array perm2 {{ 1, 0 } }; - std::array perm3 {{2, 1, 0}}; - - RAJA::Layout layout_geom = RAJA::make_permuted_layout({{ nqpts, nelems } }, perm2); - RAJA::Layout layout_ev = RAJA::make_permuted_layout({{ vdim, nelems } }, perm2); - RAJA::Layout layout_qf = RAJA::make_permuted_layout({{vdim, nqpts, nelems}}, perm3); - - (*elemVal) = 0.0; - - RAJA::View > j_view(geom->detJ.Read(), layout_geom); - RAJA::View > qf_view(qf->Read(), layout_qf); - RAJA::View > ev_view(elemVal->ReadWrite(), layout_ev); - - MFEM_FORALL(i, nelems, { - double vol = 0.0; - for(int j = 0; j < nqpts; j++) { - const double wts = j_view(j, i) * W[j]; - vol += wts; - for(int k = 0; k < vdim; k++) { - ev_view(k, i) += qf_view(k, j, i) * wts; - } - } - const double ivol = 1.0 / vol; - for(int k = 0; k < vdim; k++) { - ev_view(k, i) *= ivol; - } - }); -} - -void SystemDriver::ProjectCentroid(ParGridFunction ¢roid) -{ - - Mesh *mesh = fe_space.GetMesh(); - const FiniteElement &el = *fe_space.GetFE(0); - const IntegrationRule *ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1));; - - const int nqpts = ir->GetNPoints(); - const int nelems = fe_space.GetNE(); - const int vdim = mesh->SpaceDimension(); - - const double* W = ir->GetWeights().Read(); - const GeometricFactors *geom = mesh->GetGeometricFactors(*ir, GeometricFactors::DETERMINANTS | GeometricFactors::COORDINATES); - - const int DIM2 = 2; - const int DIM3 = 3; - std::array perm2 {{ 1, 0 } }; - std::array perm3 {{2, 1, 0}}; - - RAJA::Layout layout_geom = RAJA::make_permuted_layout({{ nqpts, nelems } }, perm2); - RAJA::Layout layout_ev = RAJA::make_permuted_layout({{ vdim, nelems } }, perm2); - RAJA::Layout layout_qf = RAJA::make_permuted_layout({{nqpts, vdim, nelems}}, perm3); - - centroid = 0.0; - - RAJA::View > j_view(geom->detJ.Read(), layout_geom); - RAJA::View > x_view(geom->X.Read(), layout_qf); - RAJA::View > ev_view(centroid.ReadWrite(), layout_ev); - - MFEM_FORALL(i, nelems, { - double vol = 0.0; - for(int j = 0; j < nqpts; j++) { - const double wts = j_view(j, i) * W[j]; - vol += wts; - for(int k = 0; k < vdim; k++) { - ev_view(k, i) += x_view(j, k, i) * wts; - } - } - const double ivol = 1.0 / vol; - for(int k = 0; k < vdim; k++) { - ev_view(k, i) *= ivol; - } - }); -} - -void SystemDriver::ProjectVolume(ParGridFunction &vol) -{ - Mesh *mesh = fe_space.GetMesh(); - const FiniteElement &el = *fe_space.GetFE(0); - const IntegrationRule *ir = &(IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1));; - - const int nqpts = ir->GetNPoints(); - const int nelems = fe_space.GetNE(); - - const double* W = ir->GetWeights().Read(); - const GeometricFactors *geom = mesh->GetGeometricFactors(*ir, GeometricFactors::DETERMINANTS); - - const int DIM2 = 2; - std::array perm2 {{ 1, 0 } }; - RAJA::Layout layout_geom = RAJA::make_permuted_layout({{ nqpts, nelems } }, perm2); - - double *vol_data = vol.ReadWrite(); - RAJA::View > j_view(geom->detJ.Read(), layout_geom); - - MFEM_FORALL(i, nelems, { - vol_data[i] = 0.0; - for(int j = 0; j < nqpts; j++) { - vol_data[i] += j_view(j, i) * W[j]; - } - }); -} - -void SystemDriver::ProjectModelStress(ParGridFunction &s) -{ - CalcElementAvg(&s, model->GetStress0()); -} - -void SystemDriver::ProjectVonMisesStress(ParGridFunction &vm, const ParGridFunction &s) -{ - const int npts = vm.Size(); - - const int DIM2 = 2; - std::array perm2{{ 1, 0 } }; - - RAJA::Layout layout_stress = RAJA::make_permuted_layout({{ 6, npts } }, perm2); - RAJA::View > stress_view(s.Read(), layout_stress); - double *vm_data = vm.ReadWrite(); - - MFEM_FORALL(i, npts, { - double term1 = stress_view(0, i) - stress_view(1, i); - double term2 = stress_view(1, i) - stress_view(2, i); - double term3 = stress_view(2, i) - stress_view(0, i); - double term4 = stress_view(3, i) * stress_view(3, i) - + stress_view(4, i) * stress_view(4, i) - + stress_view(5, i) * stress_view(5, i); - - term1 *= term1; - term2 *= term2; - term3 *= term3; - term4 *= 6.0; - - vm_data[i] = sqrt(0.5 * (term1 + term2 + term3 + term4)); - }); - -} - -void SystemDriver::ProjectHydroStress(ParGridFunction &hss, const ParGridFunction &s) -{ - const int npts = hss.Size(); - - const int DIM2 = 2; - std::array perm2{{ 1, 0 } }; - - RAJA::Layout layout_stress = RAJA::make_permuted_layout({{ 6, npts } }, perm2); - RAJA::View > stress_view(s.Read(), layout_stress); - double* hydro = hss.ReadWrite(); - - const double one_third = 1.0 / 3.0; - - MFEM_FORALL(i, npts, { - hydro[i] = one_third * (stress_view(0, i) + stress_view(1, i) + stress_view(2, i)); - }); - - return; -} - -// These next group of Project* functions are only available with ExaCMech type models -// Need to figure out a smart way to get all of the indices that I want for down below -// that go with ExaModel -void SystemDriver::ProjectDpEff(ParGridFunction &dpeff) -{ - if (mech_type == MechType::EXACMECH) { - std::string s_shrateEff = "shrateEff"; - auto qf_mapping = model->GetQFMapping(); - auto pair = qf_mapping->find(s_shrateEff)->second; - - VectorQuadratureFunctionCoefficient qfvc(*evec); - qfvc.SetComponent(pair.first, pair.second); - dpeff.ProjectDiscCoefficient(qfvc, mfem::GridFunction::ARITHMETIC); - } - return; -} - -void SystemDriver::ProjectEffPlasticStrain(ParGridFunction &pleff) -{ - if (mech_type == MechType::EXACMECH) { - std::string s_shrEff = "shrEff"; - auto qf_mapping = model->GetQFMapping(); - auto pair = qf_mapping->find(s_shrEff)->second; - - VectorQuadratureFunctionCoefficient qfvc(*evec); - qfvc.SetComponent(pair.first, pair.second); - pleff.ProjectDiscCoefficient(qfvc, mfem::GridFunction::ARITHMETIC); - } - return; -} - -void SystemDriver::ProjectShearRate(ParGridFunction &gdot) -{ - if (mech_type == MechType::EXACMECH) { - std::string s_gdot = "gdot"; - auto qf_mapping = model->GetQFMapping(); - auto pair = qf_mapping->find(s_gdot)->second; - - VectorQuadratureFunctionCoefficient qfvc(*evec); - qfvc.SetComponent(pair.first, pair.second); - gdot.ProjectDiscCoefficient(qfvc, mfem::GridFunction::ARITHMETIC); - } - return; -} - -// This one requires that the orientations be made unit normals afterwards -void SystemDriver::ProjectOrientation(ParGridFunction &quats) -{ - if (mech_type == MechType::EXACMECH) { - std::string s_quats = "quats"; - auto qf_mapping = model->GetQFMapping(); - auto pair = qf_mapping->find(s_quats)->second; - - VectorQuadratureFunctionCoefficient qfvc(*evec); - qfvc.SetComponent(pair.first, pair.second); - quats.ProjectDiscCoefficient(qfvc, mfem::GridFunction::ARITHMETIC); - - // The below is normalizing the quaternion since it most likely was not - // returned normalized - int _size = quats.Size(); - int size = _size / 4; - - double norm = 0; - double inv_norm = 0; - int index = 0; - - for (int i = 0; i < size; i++) { - index = i * 4; - - norm = quats(index + 0) * quats(index + 0); - norm += quats(index + 1) * quats(index + 1); - norm += quats(index + 2) * quats(index + 2); - norm += quats(index + 3) * quats(index + 3); - - inv_norm = 1.0 / sqrt(norm); - - for (int j = 0; j < 4; j++) { - quats(index + j) *= inv_norm; - } - } - } - return; -} - -// Here this can be either the CRSS for a voce model or relative dislocation density -// value for the MTS model. -void SystemDriver::ProjectH(ParGridFunction &h) -{ - if (mech_type == MechType::EXACMECH) { - std::string s_hard = "hardness"; - auto qf_mapping = model->GetQFMapping(); - auto pair = qf_mapping->find(s_hard)->second; - - VectorQuadratureFunctionCoefficient qfvc(*evec); - qfvc.SetComponent(pair.first, pair.second); - h.ProjectDiscCoefficient(qfvc, mfem::GridFunction::ARITHMETIC); - } - return; -} - -// This one requires that the deviatoric strain be converted from 5d rep to 6d -// and have vol. contribution added. -void SystemDriver::ProjectElasticStrains(ParGridFunction &estrain) -{ - if (mech_type == MechType::EXACMECH) { - std::string s_estrain = "elas_strain"; - std::string s_rvol = "rel_vol"; - auto qf_mapping = model->GetQFMapping(); - auto espair = qf_mapping->find(s_estrain)->second; - auto rvpair = qf_mapping->find(s_rvol)->second; - - const int e_offset = espair.first; - const int rv_offset = rvpair.first; - - int _size = estrain.Size(); - int nelems = _size / 6; - - auto data_estrain = mfem::Reshape(estrain.HostReadWrite(), 6, nelems); - auto data_evec = mfem::Reshape(evec->HostReadWrite(), evec->GetVDim(), nelems); - // The below is outputting the full elastic strain in the crystal ref frame - // We'd only stored the 5d deviatoric elastic strain, so we need to convert - // it over to the 6d version and add in the volume elastic strain contribution. - for (int i = 0; i < nelems; i++) { - const double t1 = ecmech::sqr2i * data_evec(0 + e_offset, i); - const double t2 = ecmech::sqr6i * data_evec(1 + e_offset, i); - // - // Volume strain is ln(V^e_mean) term aka ln(relative volume) - // Our plastic deformation has a det(1) aka no change in volume change - const double elas_vol_strain = log(data_evec(rv_offset, i)); - // We output elastic strain formulation such that the relationship - // between V^e and \varepsilon is just V^e = I + \varepsilon - data_estrain(0, i) = (t1 - t2) + elas_vol_strain; // 11 - data_estrain(1, i) = (-t1 - t2) + elas_vol_strain ; // 22 - data_estrain(2, i) = ecmech::sqr2b3 * data_evec(1 + e_offset, i) + elas_vol_strain; // 33 - data_estrain(3, i) = ecmech::sqr2i * data_evec(4 + e_offset, i); // 23 - data_estrain(4, i) = ecmech::sqr2i * data_evec(3 + e_offset, i); // 31 - data_estrain(5, i) = ecmech::sqr2i * data_evec(2 + e_offset, i); // 12 - } - } - return; -} - -void SystemDriver::SetTime(const double t) -{ - solVars.SetTime(t); - model->SetModelTime(t); - // set the time for the nonzero Dirichlet BC function evaluation - ess_bdr_func->SetTime(t); - return; -} - -double SystemDriver::GetDt() -{ - return dt_class; + auto I = ess_tdofs.Read(); + auto size = ess_tdofs.Size(); + auto Y = vel_tdofs->ReadWrite(); + const auto X = vel_tdof_tmp.Read(); + // vel_tdofs should already have the current solution + mfem::forall(size, [=] MFEM_HOST_DEVICE(int i) { + Y[I[i]] = X[I[i]]; + }); + } + } // end of if constant strain rate } -void SystemDriver::SetDt(const double dt) -{ - solVars.SetDt(dt); - model->SetModelDt(dt); - return; -} +void SystemDriver::UpdateModel() { + model->UpdateModelVars(); + m_sim_state->UpdateModel(); + m_sim_state->SetupModelVariables(); -SystemDriver::~SystemDriver() -{ - delete ess_bdr_func; - delete J_solver; - if (J_prec != nullptr) { - delete J_prec; - } - if (light_up != nullptr) { - delete light_up; - } - delete newton_solver; - delete mech_operator; + auto def_grad = m_sim_state->GetQuadratureFunction("kinetic_grads"); + mech_operator->CalculateDeformationGradient(*def_grad.get()); } \ No newline at end of file diff --git a/src/system_driver.hpp b/src/system_driver.hpp index c235ee0..54729e1 100644 --- a/src/system_driver.hpp +++ b/src/system_driver.hpp @@ -1,161 +1,375 @@ #ifndef mechanics_system_driver_hpp #define mechanics_system_driver_hpp +#include "fem_operators/mechanics_operator.hpp" +#include "models/mechanics_model.hpp" +#include "options/option_parser_v2.hpp" +#include "sim_state/simulation_state.hpp" +#include "solvers/mechanics_solver.hpp" + #include "mfem.hpp" -#include "mechanics_model.hpp" -#include "mechanics_operator.hpp" -#include "mechanics_solver.hpp" -#include "option_parser.hpp" -#include - -class SimVars -{ - protected: - double time; - double dt; - bool last_step = false; - public: - double GetTime() const { return time; } - double GetDTime() const { return dt; } - bool GetLastStep() const { return last_step; } - - void SetTime(double t) { time = t; } - void SetDt(double dtime) { dt = dtime; } - void SetLastStep(bool last) { last_step = last; } -}; -class LatticeTypeCubic; -template -class LightUp; -using LightUpCubic = LightUp; - -// The NonlinearMechOperator class is what really drives the entire system. -// It's responsible for calling the Newton Rhapson solver along with several of -// our post-processing steps. It also contains all of the relevant information -// related to our Krylov iterative solvers. -class SystemDriver -{ - public: - SimVars solVars; - private: - mfem::ParFiniteElementSpace &fe_space; - /// Newton solver for the operator - ExaNewtonSolver* newton_solver; - /// Solver for the Jacobian solve in the Newton method - mfem::Solver *J_solver; - /// Preconditioner for the Jacobian - mfem::Solver *J_prec; - /// nonlinear model - ExaModel *model; - int newton_iter; - int myid; - /// Variable telling us if we should use the UMAT specific - /// stuff - MechType mech_type; - NonlinearMechOperator *mech_operator; - RTModel class_device; - bool postprocessing = false; - bool additional_avgs = false; - bool auto_time = false; - double dt_class = 0.0; - double dt_min = 0.0; - double dt_max = 0.0; - double dt_scale = 1.0; - - std::string avg_stress_fname; - std::string avg_pl_work_fname; - std::string avg_def_grad_fname; - std::string avg_euler_strain_fname; - std::string avg_eps_fname; - std::string auto_dt_fname; - - // define a boundary attribute array and initialize to 0 - std::unordered_map > ess_bdr; - mfem::Array2D ess_bdr_scale; - std::unordered_map > ess_bdr_component; - mfem::Vector ess_velocity_gradient; - // declare a VectorFunctionRestrictedCoefficient over the boundaries that have attributes - // associated with a Dirichlet boundary condition (ids provided in input) - mfem::VectorFunctionRestrictedCoefficient *ess_bdr_func; - - const bool vgrad_origin_flag = false; - mfem::Vector vgrad_origin; - const bool mono_def_flag = false; - - LightUpCubic* light_up = nullptr; - - mfem::QuadratureFunction &def_grad; - mfem::QuadratureFunction *evec; - - public: - SystemDriver(mfem::ParFiniteElementSpace &fes, - ExaOptions &options, - mfem::QuadratureFunction &q_matVars0, - mfem::QuadratureFunction &q_matVars1, - mfem::QuadratureFunction &q_sigma0, - mfem::QuadratureFunction &q_sigma1, - mfem::QuadratureFunction &q_matGrad, - mfem::QuadratureFunction &q_kinVars0, - mfem::QuadratureFunction &q_vonMises, - mfem::QuadratureFunction *q_evec, - mfem::ParGridFunction &ref_crds, - mfem::ParGridFunction &beg_crds, - mfem::ParGridFunction &end_crds, - mfem::Vector &matProps, - int nStateVars); - - /// Get FE space - const mfem::ParFiniteElementSpace *GetFESpace() { return &fe_space; } - - /// Get essential true dof list, if required - const mfem::Array &GetEssTDofList(); - - /// Driver for the newton solver - void Solve(mfem::Vector &x); - - /// Solve the Newton system for the 1st time step - /// It was found that for large meshes a ramp up to our desired applied BC might - /// be needed. It should be noted that this is no longer a const function since - /// we modify several values/objects held by our class. - void SolveInit(const mfem::Vector &xprev, mfem::Vector &x) const; - - /// routine to update beginning step model variables with converged end - /// step values - void UpdateModel(); - void UpdateEssBdr(); - void UpdateVelocity(mfem::ParGridFunction &velocity, mfem::Vector &vel_tdofs); - - void ProjectCentroid(mfem::ParGridFunction ¢roid); - void ProjectVolume(mfem::ParGridFunction &vol); - void ProjectModelStress(mfem::ParGridFunction &s); - void ProjectVonMisesStress(mfem::ParGridFunction &vm, const mfem::ParGridFunction &s); - void ProjectHydroStress(mfem::ParGridFunction &hss, const mfem::ParGridFunction &s); - - // These next group of Project* functions are only available with ExaCMech type models - void ProjectDpEff(mfem::ParGridFunction &dpeff); - void ProjectEffPlasticStrain(mfem::ParGridFunction &pleff); - void ProjectShearRate(mfem::ParGridFunction &gdot); - - // This one requires that the orientations be made unit normals afterwards - void ProjectOrientation(mfem::ParGridFunction &quats); - - // Here this can be either the CRSS for a voce model or relative dislocation density - // value for the MTS model. - void ProjectH(mfem::ParGridFunction &h); - - // This one requires that the deviatoric strain be converted from 5d rep to 6d - // and have vol. contribution added. - void ProjectElasticStrains(mfem::ParGridFunction &estrain); - - void SetTime(const double t); - void SetDt(const double dt); - double GetDt(); - void SetModelDebugFlg(const bool dbg); - - // Computes the element average of a quadrature function and stores it in a - // vector. This is meant to be a helper function for the Project* methods. - void CalcElementAvg(mfem::Vector *elemVal, const mfem::QuadratureFunction *qf); - virtual ~SystemDriver(); +#include +/** + * @brief Primary driver class for ExaConstit's velocity-based finite element simulations. + * + * SystemDriver orchestrates the entire nonlinear mechanics simulation workflow for ExaConstit, + * implementing a velocity-based, updated Lagrangian finite element framework for solid mechanics + * problems with emphasis on crystal plasticity and micromechanics modeling. The class manages + * the Newton-Raphson solution process, boundary condition enforcement, and model state updates + * throughout the simulation. + * + * The driver integrates multiple components: + * - Newton-Raphson nonlinear solver with optional line search + * - Krylov iterative linear solvers (GMRES, CG, MINRES) with preconditioning + * - Essential boundary condition management (velocity and velocity gradient BCs) + * - Constitutive model integration and state variable updates + * - Device-aware execution supporting CPU, OpenMP, and GPU backends + * + * Key simulation capabilities: + * - Multi-material nonlinear solid mechanics with large deformations + * - Crystal plasticity simulations with grain-specific material behavior + * - Complex loading conditions including velocity gradient boundary conditions + * - Automated time stepping with adaptive control + * - Parallel MPI execution with domain decomposition + * + * The class follows the MFEM finite element framework conventions while extending + * functionality for ExaConstit's specialized micromechanics applications. It serves + * as the central coordination point between mesh management, material models, + * solvers, and postprocessing systems. + * + * @ingroup ExaConstit_system + */ +class SystemDriver { +private: + /// @brief Newton-Raphson solver instance for nonlinear equation systems + /// Handles the main iterative solution process for F(x) = 0 using Newton's method or Newton + /// with line search + std::unique_ptr newton_solver; + + /// @brief Linear solver for Jacobian system solution within Newton iterations + /// Solves the linearized system J*dx = -F at each Newton step using Krylov methods + /// (GMRES/CG/MINRES) + std::shared_ptr J_solver; + + /// @brief Preconditioner for the Jacobian linear system to improve convergence + /// Typically algebraic multigrid (BoomerAMG) or Jacobi preconditioning for efficiency + std::shared_ptr J_prec; + + /// @brief Material model interface for constitutive relationship evaluation + /// Manages material property evaluation, state variable updates, and stress computation + std::shared_ptr model; + + /// @brief Nonlinear mechanics operator encapsulating the finite element discretization + /// Provides residual evaluation, Jacobian computation, and essential DOF management for the + /// mechanics problem + std::shared_ptr mech_operator; + + /// @brief Number of Newton iterations performed in current solve + int newton_iter; + + /// @brief Device execution model (CPU/OpenMP/GPU) for RAJA kernels + RTModel class_device; + + /// @brief Flag indicating automatic time stepping is enabled + bool auto_time = false; + + /// @brief Boundary condition attribute arrays organized by BC type + /// Keys: "total", "ess_vel", "ess_vgrad" for combined, velocity, and velocity gradient BCs + std::unordered_map> ess_bdr; + + /// @brief Scaling factors for boundary conditions per attribute and spatial component + mfem::Array2D ess_bdr_scale; + + /// @brief Component-wise BC flags indicating which spatial directions have essential BCs + /// Keys match ess_bdr: "total", "ess_vel", "ess_vgrad" + std::unordered_map> ess_bdr_component; + + /// @brief Current velocity gradient tensor for uniform deformation boundary conditions + /// Stored as flattened 3x3 or 2x2 tensor depending on spatial dimension + mfem::Vector ess_velocity_gradient; + + /// @brief MFEM coefficient function for applying Dirichlet boundary conditions + /// Restricted to specific boundary attributes with time-dependent scaling factors + std::unique_ptr ess_bdr_func; + + /// @brief Reference point for velocity gradient boundary condition calculations + /// Used as origin for computing position-dependent velocity in uniform deformation + const bool vgrad_origin_flag = false; + + /// @brief Reference point for velocity gradient boundary condition calculations + /// Used as origin for computing position-dependent velocity in uniform deformation + mfem::Vector vgrad_origin; + + /// @brief Flag enabling monolithic deformation mode with simplified boundary conditions + /// Used for special loading cases with constrained degrees of freedom + const bool mono_def_flag = false; + + /// @brief Reference to simulation state containing mesh, fields, and configuration data + std::shared_ptr m_sim_state; + +public: + /** + * @brief Construct SystemDriver with simulation state and initialize all components. + * + * @param sim_state Reference to simulation state containing mesh, options, and field data + * + * Initializes the complete finite element system for ExaConstit simulations including + * boundary condition management, linear and nonlinear solvers, and device configuration. + * The constructor performs extensive setup to prepare the system for time-stepping. + * + * Initialization process: + * 1. Configure device execution model from simulation options + * 2. Initialize boundary condition arrays and component flags + * 3. Set up velocity gradient and origin vectors for uniform deformation + * 4. Create and configure the nonlinear mechanics operator + * 5. Initialize linear solver (GMRES/CG/MINRES) with preconditioning + * 6. Configure Newton solver with convergence criteria + * 7. Handle special case initialization for monolithic deformation mode + * 8. Set up boundary condition coefficient functions + * + * Boundary condition setup: + * - Creates separate attribute arrays for total, velocity, and velocity gradient BCs + * - Initializes component-wise flags for spatial direction control + * - Configures scaling factors for time-dependent boundary conditions + * - Sets up restricted coefficient functions for MFEM integration + * + * Linear solver configuration: + * - Supports GMRES, Conjugate Gradient, and MINRES iterative solvers + * - Configures algebraic multigrid (BoomerAMG) or Jacobi preconditioning + * - Sets convergence tolerances and maximum iterations from options + * - Enables device-aware execution for GPU acceleration when available + * + * Nonlinear solver setup: + * - Creates Newton-Raphson solver with optional line search capability + * - Configures convergence criteria (relative/absolute tolerances) + * - Sets maximum iteration limits and printing levels + * - Links linear solver for Jacobian system solution + * + * Special handling for monolithic deformation mode: + * - Computes mesh bounding box for reference coordinate system + * - Identifies constrained degrees of freedom based on geometric constraints + * - Sets up essential DOF lists for simplified boundary condition enforcement + * + * @note Constructor performs significant computational work including mesh analysis + * @note Memory allocation is device-aware and respects the configured execution model + * @note All components are fully initialized and ready for time-stepping upon completion + * + * @throws std::runtime_error if critical initialization steps fail + * @throws MFEM_VERIFY errors for invalid configuration combinations + */ + SystemDriver(std::shared_ptr sim_state); + + /** + * @brief Get essential true degrees of freedom list from mechanics operator. + * + * @return Const reference to array of essential true DOF indices + * + * Retrieves the list of essential (constrained) true degrees of freedom from the + * underlying nonlinear mechanics operator. These DOFs correspond to nodes where + * Dirichlet boundary conditions are applied and represent constrained solution + * components that are not solved for in the linear system. + * + * The essential true DOF list is used by: + * - Linear solvers to identify constrained equations + * - Post-processing routines for proper field reconstruction + * - Boundary condition enforcement during assembly + * - Solution vector manipulation and constraint application + * + * True DOFs represent the actual degrees of freedom in the parallel distributed + * system after applying finite element space restrictions and MPI communication + * patterns. This differs from local DOFs which are process-specific. + * + * @note Returns reference to internal data - do not modify + * @note Valid only after mechanics operator initialization + * @note Used primarily for linear solver configuration + * + * @ingroup ExaConstit_boundary_conditions + */ + const mfem::Array& GetEssTDofList(); + + /** + * @brief Execute Newton-Raphson solver for current time step. + * + * Performs the main nonlinear solve for the current time step using Newton-Raphson + * iteration. This function orchestrates the complete solution process including + * initial guess setup, Newton iteration, convergence checking, and solution update. + * + * Solution process: + * 1. **Initial Guess Setup**: For automatic time stepping, uses previous solution + * 2. **Newton Iteration**: Repeatedly solves linearized system until convergence + * 3. **Convergence Check**: Verifies solution meets relative and absolute tolerances + * 4. **Solution Update**: Updates velocity field and distributes to parallel processes + * 5. **Boundary Condition Sync**: Updates time-dependent boundary conditions if converged + * + * Automatic time stepping mode: + * - Uses previous time step solution as initial guess for better convergence + * - Enables more aggressive time step sizes for improved efficiency + * - Particularly effective for quasi-static and dynamic problems + * + * The function handles both standard Newton-Raphson and line search variants + * depending on the solver type configured during initialization. Line search + * provides better robustness for highly nonlinear problems. + * + * Convergence criteria: + * - Relative tolerance: ||residual|| / ||initial_residual|| < rel_tol + * - Absolute tolerance: ||residual|| < abs_tol + * - Maximum iterations: Prevents infinite loops for non-convergent cases + * + * @note Throws MFEM_VERIFY error if Newton solver fails to converge + * @note Updates boundary condition time if solver converges successfully + * @note Critical function called once per time step in main simulation loop + * + * @throws MFEM_VERIFY if Newton solver does not converge within iteration limits + * + * @ingroup ExaConstit_solvers + */ + void Solve(); + + /** + * @brief Solve Newton system for first time step with boundary condition ramp-up. + * + * Performs a specialized solve for the first time step that addresses convergence + * issues with large meshes by implementing a boundary condition ramp-up strategy. + * This corrector step ensures the solver has better initial conditions for the + * main Newton iteration. + * + * **Algorithm:** + * 1. **Setup Phase**: Create residual and increment vectors + * 2. **BC Increment Calculation**: Compute difference between current and previous BCs + * 3. **Linear Solve**: Use mechanics operator's UpdateBCsAction for linearized correction + * 4. **CG Solution**: Apply conjugate gradient solver to correction equation + * 5. **Solution Update**: Apply correction and distribute to velocity field + * + * **Mathematical Framework:** + * The method solves a linearized correction equation: + * ``` + * K_uc * (x - x_prev)_c = deltaF_u + * ``` + * where: + * - K_uc: Unconstrained-constrained stiffness block + * - x, x_prev: Current and previous solution vectors + * - deltaF_u: Force increment from boundary condition changes + * + * **Ramp-up Strategy:** + * - Addresses numerical difficulties with sudden BC application + * - Provides smooth transition from previous solution state + * - Particularly important for large deformation problems + * - Improves convergence rates for subsequent Newton iterations + * + * **Usage Context:** + * Called automatically when BCManager detects boundary condition changes + * between time steps, enabling complex loading scenarios: + * - Multi-stage deformation processes + * - Cyclic loading applications + * - Time-dependent displacement prescriptions + * - Load path modifications during simulation + * + * @note Function marked const but modifies solution through sim_state references + * @note Uses mechanics operator's specialized BC update action + * @note Critical for robust handling of time-dependent boundary conditions + * + * @ingroup ExaConstit_solvers + */ + void SolveInit() const; + + /** + * @brief Update material model variables after converged time step. + * + * Synchronizes material model state variables and simulation data structures + * after a successful time step completion. This function ensures all internal + * variables, history-dependent data, and kinematic measures are properly + * updated for the next time step. + * + * **Update Sequence:** + * 1. **Model Variable Update**: Calls model->UpdateModelVars() to advance material state + * 2. **Simulation State Update**: Updates SimulationState internal data structures + * 3. **Model Variable Setup**: Reinitializes model variables for next step + * 4. **Deformation Gradient**: Computes current deformation gradient from solution + * + * **Material Model Updates:** + * - Advances internal state variables (plastic strains, damage, etc.) + * - Updates stress states based on converged deformation + * - Handles crystal plasticity orientation evolution + * - Manages history-dependent material properties + * + * **Kinematic Updates:** + * - Computes deformation gradient F from current nodal positions + * - Updates kinematic measures for large deformation analysis + * - Maintains consistency between geometric and material nonlinearities + * + * **State Management:** + * - Swaps "beginning" and "end" step quadrature function data + * - Prepares data structures for next time increment + * - Ensures proper history variable management + * - Maintains simulation restart capability + * + * **Critical for:** + * - Material model accuracy and stability + * - History-dependent constitutive behavior + * - Multi-physics coupling consistency + * - Simulation restart and checkpointing + * + * @note Must be called after each converged time step + * @note Order of operations is critical for simulation accuracy + * @note Handles both single and multi-material regions + * + * @ingroup ExaConstit_material_models + */ + void UpdateModel(); + + /** + * @brief Update essential boundary condition data for the current time step. + * + * Synchronizes boundary condition data with the BCManager to reflect any changes + * in applied boundary conditions for the current simulation step. Updates the + * essential true degrees of freedom in the mechanics operator to ensure proper + * constraint enforcement during the solve process. + * + * This function is called when boundary conditions change between time steps, + * enabling time-dependent loading scenarios such as cyclic loading, multi-stage + * deformation, or complex loading histories typical in materials testing. + * + * Updates performed: + * - Retrieves latest BC data from BCManager + * - Updates ess_bdr, ess_bdr_scale, and ess_bdr_component arrays + * - Refreshes essential true DOF list in mechanics operator + * - Ensures consistency between BC data and solver constraints + * + * @note Called automatically when BCManager detects BC changes for current step + * @note Only updates data if mono_def_flag is false (normal operation mode) + */ + void UpdateEssBdr(); + + /** + * @brief Update velocity field with current boundary condition values. + * + * Applies essential boundary conditions to the velocity field and updates the + * solution vector with the prescribed velocity values. Handles both direct + * velocity boundary conditions and velocity gradient boundary conditions that + * require position-dependent velocity calculations. + * + * The function performs different operations based on boundary condition type: + * - Direct velocity BCs: Projects boundary function onto velocity field + * - Velocity gradient BCs: Computes position-dependent velocities from gradients + * - Mixed BCs: Applies appropriate method for each boundary region + * + * For velocity gradient boundary conditions, the velocity is computed as: + * v(x) = ∇v · (x - x_origin) where ∇v is the prescribed velocity gradient + * and x_origin is the reference point for the deformation. + * + * Algorithm: + * 1. Check for direct velocity boundary conditions + * 2. Apply velocity boundary functions if present + * 3. Handle velocity gradient boundary conditions + * 4. Update solution vector with prescribed velocities + * 5. Ensure compatibility with essential DOF constraints + * + * @note Called before each solve to ensure current BC values are applied + * @note Critical for maintaining consistency between field values and constraints + */ + void UpdateVelocity(); + virtual ~SystemDriver() = default; }; #endif \ No newline at end of file diff --git a/src/umat_tests/userumat.cxx b/src/umat_tests/userumat.cxx deleted file mode 100755 index cdc7e48..0000000 --- a/src/umat_tests/userumat.cxx +++ /dev/null @@ -1,59 +0,0 @@ - -#include -#include -#include - -#include -#include -#include -#include - -#define real8 double - -extern "C" { - -#ifdef WIN32 -#define UMAT_API __declspec(dllexport) -#elif defined(__clang__) || defined(__INTEL_LLVM_COMPILER) -#define UMAT_API -#define UMAT umat -#else -#define UMAT_API -#define UMAT umat_ -#endif - - // A fortran function defined in umat.f - void UMAT(real8 *stress, real8 *statev, real8 *ddsdde, - real8 *sse, real8 *spd, real8 *scd, real8 *rpl, - real8 *ddsdt, real8 *drplde, real8 *drpldt, - real8 *stran, real8 *dstran, real8 *time, - real8 *deltaTime, real8 *tempk, real8 *dtemp, real8 *predef, - real8 *dpred, real8 *cmname, int *ndi, int *nshr, int *ntens, - int *nstatv, real8 *props, int *nprops, real8 *coords, - real8 *drot, real8 *pnewdt, real8 *celent, - real8 *dfgrd0, real8 *dfgrd1, int *noel, int *npt, - int *layer, int *kspt, int *kstep, int *kinc); - - // The C entry point function for my umat - UMAT_API void - umat_call(real8 *stress, real8 *statev, real8 *ddsdde, - real8 *sse, real8 *spd, real8 *scd, real8 *rpl, - real8 *ddsdt, real8 *drplde, real8 *drpldt, - real8 *stran, real8 *dstran, real8 *time, - real8 *deltaTime, real8 *tempk, real8 *dtemp, real8 *predef, - real8 *dpred, real8 *cmname, int *ndi, int *nshr, int *ntens, - int *nstatv, real8 *props, int *nprops, real8 *coords, - real8 *drot, real8 *pnewdt, real8 *celent, - real8 *dfgrd0, real8 *dfgrd1, int *noel, int *npt, - int *layer, int *kspt, int *kstep, int *kinc) - { - - UMAT(stress, statev, ddsdde, sse, spd, scd, rpl, - ddsdt, drplde, drpldt, stran, dstran, time, deltaTime, - tempk, dtemp, predef, dpred, cmname, ndi, nshr, ntens, - nstatv, props, nprops, coords, drot, pnewdt, celent, - dfgrd0, dfgrd1, noel, npt, layer, kspt, kstep, kinc); - - } - -} diff --git a/src/umat_tests/userumat.h b/src/umat_tests/userumat.h deleted file mode 100644 index 13b671c..0000000 --- a/src/umat_tests/userumat.h +++ /dev/null @@ -1,58 +0,0 @@ -// -// userumat.h -// -// -// Created by Carson, Robert Allen on 10/31/18. -// - -#ifndef userumat_h -#define userumat_h - -#include -#include -#include - -#include -#include -#include -#include - -#define real8 double - -extern "C" { - -#ifdef WIN32 -#define UMAT_API __declspec(dllexport) -#else -#define UMAT_API -#define UMATTEST umattest_ -#endif - - // A fortran function defined in umat.f - void UMATTEST(real8 *stress, real8 *statev, real8 *ddsdde, - real8 *sse, real8 *spd, real8 *scd, real8 *rpl, - real8 *ddsdt, real8 *drplde, real8 *drpldt, - real8 *stran, real8 *dstran, real8 *time, - real8 *deltaTime, real8 *tempk, real8 *dtemp, real8 *predef, - real8 *dpred, real8 *cmname, int *ndi, int *nshr, int *ntens, - int *nstatv, real8 *props, int *nprops, real8 *coords, - real8 *drot, real8 *pnewdt, real8 *celent, - real8 *dfgrd0, real8 *dfgrd1, int *noel, int *npt, - int *layer, int *kspt, int *kstep, int *kinc); - // The C entry point function for my umat - UMAT_API void - umat_call(real8 *stress, real8 *statev, real8 *ddsdde, - real8 *sse, real8 *spd, real8 *scd, real8 *rpl, - real8 *ddsdt, real8 *drplde, real8 *drpldt, - real8 *stran, real8 *dstran, real8 *time, - real8 *deltaTime, real8 *tempk, real8 *dtemp, real8 *predef, - real8 *dpred, real8 *cmname, int *ndi, int *nshr, int *ntens, - int *nstatv, real8 *props, int *nprops, real8 *coords, - real8 *drot, real8 *pnewdt, real8 *celent, - real8 *dfgrd0, real8 *dfgrd1, int *noel, int *npt, - int *layer, int *kspt, int *kstep, int *kinc); - -} - - -#endif /* userumat_h */ diff --git a/src/umat_tests/umat.cxx b/src/umats/umat.cxx similarity index 93% rename from src/umat_tests/umat.cxx rename to src/umats/umat.cxx index 920c822..600ddae 100644 --- a/src/umat_tests/umat.cxx +++ b/src/umats/umat.cxx @@ -25,7 +25,7 @@ void UMAT(real8 * /* stress */, real8 * /* statev */, real8 *ddsdde, real8 * /* ddsdt */, real8 *drplde, real8 *drpldt, real8 * /* stran */, real8 * /* dstran */, real8 * /* time */, real8 * /* deltaTime */, real8 * /* tempk */, real8 * /* dtemp */, real8 * /* predef */, - real8 * /* dpred */, real8 * /* cmname */, int * /* ndi */, int * /* nshr */, int * ntens, + real8 * /* dpred */, char * /* cmname */, int * /* ndi */, int * /* nshr */, int * ntens, int * /* nstatv */, real8 * /* props */, int * /* nprops */, real8 * /* coords */, real8 * /* drot */, real8 * /* pnewdt */, real8 * /* celent */, real8 * /* dfgrd0 */, real8 * /* dfgrd1 */, int * /* noel */, int * /* npt */, diff --git a/src/umat_tests/umat.f b/src/umats/umat.f similarity index 100% rename from src/umat_tests/umat.f rename to src/umats/umat.f diff --git a/src/umats/unified_umat_loader.hpp b/src/umats/unified_umat_loader.hpp new file mode 100644 index 0000000..0deffc4 --- /dev/null +++ b/src/umats/unified_umat_loader.hpp @@ -0,0 +1,138 @@ +#pragma once + +#include "umats/userumat.h" +#include "utilities/dynamic_function_loader.hpp" + +#include +#include + +namespace exaconstit { + +/** + * @brief Unified UMAT loader using the generic dynamic function loader framework + * + * This replaces both DynamicUmatLoader and UmatResolver with a single unified interface + * that leverages the templated DynamicFunctionLoader. + */ +class UnifiedUmatLoader { +public: + using Loader = DynamicFunctionLoader; + + /** + * @brief Load a UMAT function with automatic symbol resolution + * + * @param library_path Path to library (empty for built-in) + * @param function_name Specific function name to search for + * @param strategy Loading strategy + * @return UMAT function pointer or nullptr on failure + */ + static UmatFunction LoadUmat(const std::string& library_path, + LoadStrategy strategy = LoadStrategy::PERSISTENT, + const std::string& function_name = "umat_call") { + // Configure symbol search + SymbolConfig config; + + // Primary search name + if (!function_name.empty() && function_name != "auto") { + config.search_names.push_back(function_name); + } + + // Add default UMAT symbol variants + config.search_names.insert(config.search_names.end(), + {"umat_call", // Common C wrapper + "umat", // Standard Fortran name + "userumat", // Alternative name + "UMAT", // Sometimes uppercase + "USERUMAT"}); + + // Enable all search features for UMATs + config.enable_fortran_mangling = true; + config.enable_builtin_search = library_path.empty(); + config.case_sensitive = false; // Be flexible with case + + // Perform the load + auto result = Loader::load(library_path, config, strategy); + + if (result.success) { + // Log success if needed + if (std::getenv("EXACONSTIT_DEBUG_UMAT")) { + std::cout << "Successfully loaded UMAT" + << (library_path.empty() ? " (built-in)" : " from: " + library_path) + << " using symbol: " << result.resolved_symbol << std::endl; + } + + // Compatibility: warn if different symbol was used + if (!function_name.empty() && function_name != "auto" && + result.resolved_symbol != function_name) { + std::cerr << "Warning: Requested function '" << function_name + << "' not found, using '" << result.resolved_symbol << "' instead" + << std::endl; + } + } else { + // Log error + std::cerr << "Failed to load UMAT: " << result.error_message << std::endl; + } + + return result.function; + } + + /** + * @brief Unload a UMAT library + */ + static bool UnloadUmat(const std::string& library_path) { + // Use same config as load for consistency + SymbolConfig config; + config.search_names = {"umat_call", "umat", "userumat", "UMAT", "USERUMAT"}; + config.enable_fortran_mangling = true; + config.enable_builtin_search = library_path.empty(); + + return Loader::unload(library_path, config); + } + + /** + * @brief Get already-loaded UMAT without loading + */ + static UmatFunction GetUmat(const std::string& library_path) { + // Try to load with LAZY_LOAD strategy - if already loaded, just returns it + return LoadUmat(library_path, LoadStrategy::LAZY_LOAD); + } + + /** + * @brief Check if a UMAT library is loaded + */ + static bool IsLoaded(const std::string& library_path) { + SymbolConfig config; + config.search_names = {"umat_call", "umat", "userumat"}; + return Loader::validate(library_path, config); + } + + /** + * @brief Validate a UMAT library + */ + static bool ValidateLibrary(const std::string& library_path, + const std::string& function_name = "") { + SymbolConfig config; + if (!function_name.empty()) { + config.search_names.push_back(function_name); + } + config.search_names.insert(config.search_names.end(), {"umat_call", "umat", "userumat"}); + config.enable_fortran_mangling = true; + + return Loader::validate(library_path, config); + } + + /** + * @brief Get last error message + */ + static std::string GetLastError() { + return Loader::get_last_error(); + } + + /** + * @brief Clear all cached UMATs + */ + static void ClearCache() { + Loader::clear_cache(); + } +}; +} // namespace exaconstit \ No newline at end of file diff --git a/src/umats/userumat.h b/src/umats/userumat.h new file mode 100644 index 0000000..8a2d1dd --- /dev/null +++ b/src/umats/userumat.h @@ -0,0 +1,175 @@ +#pragma once + +/** + * @brief Function pointer type for UMAT subroutines. + * + * This typedef defines the signature for UMAT (User-defined Material) functions + * that follow the Abaqus UMAT interface standard. The function signature includes + * all the standard UMAT parameters for stress, state variables, material properties, + * and various control parameters. + * + * @param stress Array of stress components (input/output) + * @param statev Array of state variables (input/output) + * @param ddsdde Material tangent stiffness matrix (output) + * @param sse Specific strain energy (output) + * @param spd Specific plastic dissipation (output) + * @param scd Specific creep dissipation (output) + * @param rpl Volumetric heat generation (output) + * @param ddsdt Stress variation with temperature (output) + * @param drplde Energy dissipation variation with strain (output) + * @param drpldt Energy dissipation variation with temperature (output) + * @param stran Total strain array (input) + * @param dstran Strain increment array (input) + * @param time Step time and total time array (input) + * @param deltaTime Time increment for current step (input) + * @param tempk Temperature at start of increment (input) + * @param dtemp Temperature increment (input) + * @param predef Predefined field variables (input) + * @param dpred Predefined field variable increments (input) + * @param cmname Material name (input) + * @param ndi Number of direct stress components (input) + * @param nshr Number of shear stress components (input) + * @param ntens Total number of stress components (input) + * @param nstatv Number of state variables (input) + * @param props Material properties array (input) + * @param nprops Number of material properties (input) + * @param coords Coordinates of integration point (input) + * @param drot Rotation increment matrix (input) + * @param pnewdt Suggested new time increment (output) + * @param celent Characteristic element length (input) + * @param dfgrd0 Deformation gradient at start of increment (input) + * @param dfgrd1 Deformation gradient at end of increment (input) + * @param noel Element number (input) + * @param npt Integration point number (input) + * @param layer Layer number (input) + * @param kspt Section point number (input) + * @param kstep Step number (input) + * @param kinc Increment number (input) + */ +using UmatFunction = void (*)(double* stress, + double* statev, + double* ddsdde, + double* sse, + double* spd, + double* scd, + double* rpl, + double* ddsdt, + double* drplde, + double* drpldt, + double* stran, + double* dstran, + double* time, + double* deltaTime, + double* tempk, + double* dtemp, + double* predef, + double* dpred, + char* cmname, + int* ndi, + int* nshr, + int* ntens, + int* nstatv, + double* props, + int* nprops, + double* coords, + double* drot, + double* pnewdt, + double* celent, + double* dfgrd0, + double* dfgrd1, + int* noel, + int* npt, + int* layer, + int* kspt, + int* kstep, + int* kinc); + +#ifdef __cplusplus +extern "C" { +#endif + +// Default static UMAT (for testing/built-in materials) +// This will be linked from either umat.f or umat.cxx based on ENABLE_FORTRAN +void umat(double* stress, + double* statev, + double* ddsdde, + double* sse, + double* spd, + double* scd, + double* rpl, + double* ddsdt, + double* drplde, + double* drpldt, + double* stran, + double* dstran, + double* time, + double* deltaTime, + double* tempk, + double* dtemp, + double* predef, + double* dpred, + char* cmname, + int* ndi, + int* nshr, + int* ntens, + int* nstatv, + double* props, + int* nprops, + double* coords, + double* drot, + double* pnewdt, + double* celent, + double* dfgrd0, + double* dfgrd1, + int* noel, + int* npt, + int* layer, + int* kspt, + int* kstep, + int* kinc); + +#ifdef __cplusplus +} +#endif + +// #include +// #include + +// namespace exaconstit { + +// /** +// * @brief Universal UMAT resolver that handles both static and dynamic loading +// * +// * This class provides a unified interface for UMAT functions, supporting: +// * - Built-in/static UMATs compiled into the binary +// * - Dynamically loaded UMATs from shared libraries +// * - Runtime symbol resolution with Fortran name mangling handling +// */ +// class UmatResolver { +// public: +// /** +// * @brief Get UMAT function from library path or built-in +// * +// * @param library_path Path to shared library (empty for built-in) +// * @param function_name Name of the function to load (default: "umat_call") +// * @return Function pointer to UMAT, or nullptr on failure +// */ +// static UmatFunction GetUmat(const std::string& library_path = "", +// const std::string& function_name = "umat_call"); + +// /** +// * @brief Get diagnostic information about the last operation +// */ +// static std::string GetLastError(); + +// /** +// * @brief Check if a library provides a valid UMAT +// */ +// static bool ValidateLibrary(const std::string& library_path, +// const std::string& function_name = "umat_call"); + +// private: +// static thread_local std::string last_error_; +// }; + +// } // namespace ExaConstit diff --git a/src/userumat.h b/src/userumat.h deleted file mode 100644 index f21b104..0000000 --- a/src/userumat.h +++ /dev/null @@ -1,54 +0,0 @@ - -#ifndef userumat_h -#define userumat_h - -#include -#include -#include - -#include -#include -#include -#include - -#define real8 double - -extern "C" { -#ifdef WIN32 -#define UMAT_API __declspec(dllexport) -#elif defined(__clang__) || defined(__INTEL_LLVM_COMPILER) -#define UMAT_API -#define UMAT_FUNC umat -#else -#define UMAT_API -#define UMAT_FUNC umat_ -#endif - - // A fortran function defined in umat.f - void UMAT_FUNC(real8 *stress, real8 *statev, real8 *ddsdde, - real8 *sse, real8 *spd, real8 *scd, real8 *rpl, - real8 *ddsdt, real8 *drplde, real8 *drpldt, - real8 *stran, real8 *dstran, real8 *time, - real8 *deltaTime, real8 *tempk, real8 *dtemp, real8 *predef, - real8 *dpred, real8 *cmname, int *ndi, int *nshr, int *ntens, - int *nstatv, real8 *props, int *nprops, real8 *coords, - real8 *drot, real8 *pnewdt, real8 *celent, - real8 *dfgrd0, real8 *dfgrd1, int *noel, int *npt, - int *layer, int *kspt, int *kstep, int *kinc); - - // The C entry point function for my umat - UMAT_API void - umat_call(real8 *stress, real8 *statev, real8 *ddsdde, - real8 *sse, real8 *spd, real8 *scd, real8 *rpl, - real8 *ddsdt, real8 *drplde, real8 *drpldt, - real8 *stran, real8 *dstran, real8 *time, - real8 *deltaTime, real8 *tempk, real8 *dtemp, real8 *predef, - real8 *dpred, real8 *cmname, int *ndi, int *nshr, int *ntens, - int *nstatv, real8 *props, int *nprops, real8 *coords, - real8 *drot, real8 *pnewdt, real8 *celent, - real8 *dfgrd0, real8 *dfgrd1, int *noel, int *npt, - int *layer, int *kspt, int *kstep, int *kinc); -} - - -#endif /* userumat_h */ diff --git a/src/utilities/assembly_ops.hpp b/src/utilities/assembly_ops.hpp new file mode 100644 index 0000000..fac1aab --- /dev/null +++ b/src/utilities/assembly_ops.hpp @@ -0,0 +1,580 @@ +#pragma once + +#include "mfem_expt/partial_qfunc.hpp" + +#include "mfem.hpp" + +/** + * @brief Construct standard B-matrix for finite element strain-displacement relations. + * + * @param deriv_shapes Dense matrix containing shape function derivatives in physical coordinates + * (∂N/∂x) + * @param B Output B-matrix relating nodal displacements to strain components (modified in place) + * + * This function constructs the standard B-matrix used in finite element assembly + * operations for computing element stiffness matrices. The B-matrix relates nodal + * displacements to strain measures through the relationship: strain = B * nodal_displacements. + * + * The function generates the transpose of the traditional B-matrix to better match + * MFEM's internal memory layout and vectorization patterns. This organization enables + * efficient computation of the material tangent stiffness matrix: K = ∫ B^T * C * B dV. + * + * Matrix structure for 3D elements with symmetric material stiffness: + * - Input deriv_shapes: (dof × 3) matrix of shape function derivatives + * - Output B: (3*dof × 6) matrix in Voigt notation order + * - Strain ordering: [ε_xx, ε_yy, ε_zz, γ_xy, γ_xz, γ_yz] + * + * The B-matrix structure for each node i follows the pattern: + * ``` + * [∂N_i/∂x 0 0 ] <- x-displacement DOF + * [ 0 ∂N_i/∂y 0 ] <- y-displacement DOF + * [ 0 0 ∂N_i/∂z ] <- z-displacement DOF + * [ 0 ∂N_i/∂z ∂N_i/∂y ] <- xy-shear component + * [∂N_i/∂z 0 ∂N_i/∂x ] <- xz-shear component + * [∂N_i/∂y ∂N_i/∂x 0 ] <- yz-shear component + * ``` + * + * The function constructs the matrix in blocks corresponding to the three spatial + * dimensions, following MFEM's internal vector ordering: [x₀...xₙ, y₀...yₙ, z₀...zₙ]. + * + * @note This function assumes 3D elements and unrolls loops for performance. + * @note The deriv_shapes matrix should contain shape function derivatives in physical coordinates. + * @note The B matrix must be pre-sized to (3*dof, 6) before calling this function. + * @note For problems with symmetric material stiffness, this generates the standard B-matrix. + * + * @ingroup ExaConstit_utilities_assembly + */ +inline void GenerateGradMatrix(const mfem::DenseMatrix& deriv_shapes, mfem::DenseMatrix& B) { + int dof = deriv_shapes.Height(); + + // The B matrix generally has the following structure that is + // repeated for the number of dofs if we're dealing with something + // that results in a symmetric Cstiff. If we aren't then it's a different + // structure + // [deriv_shapes(i,0) 0 0] + // [0 deriv_shapes(i, 1) 0] + // [0 0 deriv_shapes(i, 2)] + // [0 deriv_shapes(i,2) deriv_shapes(i,1)] + // [deriv_shapes(i,2) 0 deriv_shapes(i,0)] + // [deriv_shapes(i,1) deriv_shapes(i,0) 0] + + // Just going to go ahead and make the assumption that + // this is for a 3D space. Should put either an assert + // or an error here if it isn't + // We should also put an assert if B doesn't have dimensions of + // (dim*dof, 6) + // fix_me + // We've rolled out the above B matrix in the comments + // This is definitely not the most efficient way of doing this memory wise. + // However, it might be fine for our needs. + // The ordering has now changed such that B matches up with mfem's internal + // ordering of vectors such that it's [x0...xn, y0...yn, z0...zn] ordering + + // The previous single loop has been split into 3 so the B matrix + // is constructed in chunks now instead of performing multiple striding + // operations in a single loop. + // x dofs + for (int i = 0; i < dof; i++) { + B(i, 0) = deriv_shapes(i, 0); + B(i, 1) = 0.0; + B(i, 2) = 0.0; + B(i, 3) = 0.0; + B(i, 4) = deriv_shapes(i, 2); + B(i, 5) = deriv_shapes(i, 1); + } + + // y dofs + for (int i = 0; i < dof; i++) { + B(i + dof, 0) = 0.0; + B(i + dof, 1) = deriv_shapes(i, 1); + B(i + dof, 2) = 0.0; + B(i + dof, 3) = deriv_shapes(i, 2); + B(i + dof, 4) = 0.0; + B(i + dof, 5) = deriv_shapes(i, 0); + } + + // z dofs + for (int i = 0; i < dof; i++) { + B(i + 2 * dof, 0) = 0.0; + B(i + 2 * dof, 1) = 0.0; + B(i + 2 * dof, 2) = deriv_shapes(i, 2); + B(i + 2 * dof, 3) = deriv_shapes(i, 1); + B(i + 2 * dof, 4) = deriv_shapes(i, 0); + B(i + 2 * dof, 5) = 0.0; + } +} + +/** + * @brief Construct B-bar matrix for selective reduced integration and volumetric locking + * mitigation. + * + * @param deriv_shapes Dense matrix containing shape function derivatives in physical coordinates + * (∂N/∂x) + * @param elem_deriv_shapes Dense matrix containing element-averaged shape function derivatives + * (∂N̄/∂x) + * @param B Output B-bar matrix relating nodal displacements to strain components (modified in + * place) + * + * This function constructs the B-bar matrix using the classical Hughes formulation for + * treating nearly incompressible materials. The B-bar method applies selective reduced + * integration by splitting the strain into volumetric and deviatoric components, then + * using element-averaged shape function derivatives for the volumetric part while + * retaining full integration for the deviatoric part. + * + * The B-bar matrix is constructed using the decomposition: + * B̄ = B_dev + B_vol + * + * where: + * - B_dev represents the deviatoric strain contribution (full integration) + * - B_vol represents the volumetric strain contribution (reduced integration via averaging) + * + * The volumetric modification is applied to the normal strain components through: + * B̄_ii = B_ii + (∂N̄/∂x_i - ∂N/∂x_i)/3 + * + * where the factor of 1/3 distributes the volumetric correction equally across the + * three normal strain components, ensuring proper treatment of the volumetric constraint + * for nearly incompressible materials. + * + * Matrix structure for 3D elements: + * - Input deriv_shapes: (dof × 3) matrix of shape function derivatives at integration point + * - Input elem_deriv_shapes: (dof × 3) matrix of element-averaged shape function derivatives + * - Output B: (3*dof × 6) matrix in Voigt notation order + * - Strain ordering: [ε_xx, ε_yy, ε_zz, γ_xy, γ_xz, γ_yz] + * + * The B-bar matrix structure for each node i follows the pattern: + * ``` + * [B̄₁ + ∂N_i/∂x B̄₁ B̄₁ 0 ∂N_i/∂z ∂N_i/∂y] <- x-displacement DOF + * [ B̄₂ B̄₂ + ∂N_i/∂y B̄₂ ∂N_i/∂z 0 ∂N_i/∂x] <- y-displacement DOF + * [ B̄₃ B̄₃ B̄₃ + ∂N_i/∂z ∂N_i/∂y ∂N_i/∂x 0 ] <- z-displacement DOF + * ``` + * + * where B̄ₖ = (∂N̄_i/∂x_k - ∂N_i/∂x_k)/3 for k = 1,2,3 + * + * Note that the shear strain components (columns 4-6) use the standard B-matrix + * formulation without volumetric correction, as they do not contribute to volumetric + * deformation. + * + * This formulation effectively prevents volumetric locking in low-order elements + * (such as linear hexahedra and tetrahedra) when analyzing nearly incompressible + * materials with Poisson's ratios approaching 0.5. + * + * @note This function assumes 3D elements and unrolls loops for performance. + * @note The elem_deriv_shapes matrix should contain element-averaged derivatives: ∂N̄/∂x = (1/V)∫_V + * ∂N/∂x dV. + * @note The B matrix must be pre-sized to (3*dof, 6) before calling this function. + * @note For compressible materials, this reduces to the standard B-matrix as elem_deriv_shapes → + * deriv_shapes. + * @note Follows MFEM's vector ordering: [x₀...xₙ, y₀...yₙ, z₀...zₙ]. + * + * @see T.J.R. Hughes, "The Finite Element Method: Linear Static and Dynamic Finite Element + * Analysis" + * + * @ingroup ExaConstit_utilities_assembly + */ +inline void GenerateGradBarMatrix(const mfem::DenseMatrix& deriv_shapes, + const mfem::DenseMatrix& elem_deriv_shapes, + mfem::DenseMatrix& B) { + int dof = deriv_shapes.Height(); + + for (int i = 0; i < dof; i++) { + const double B1 = (elem_deriv_shapes(i, 0) - deriv_shapes(i, 0)) / 3.0; + B(i, 0) = B1 + deriv_shapes(i, 0); + B(i, 1) = B1; + B(i, 2) = B1; + B(i, 3) = 0.0; + B(i, 4) = deriv_shapes(i, 2); + B(i, 5) = deriv_shapes(i, 1); + } + + // y dofs + for (int i = 0; i < dof; i++) { + const double B2 = (elem_deriv_shapes(i, 1) - deriv_shapes(i, 1)) / 3.0; + B(i + dof, 0) = B2; + B(i + dof, 1) = B2 + deriv_shapes(i, 1); + B(i + dof, 2) = B2; + B(i + dof, 3) = deriv_shapes(i, 2); + B(i + dof, 4) = 0.0; + B(i + dof, 5) = deriv_shapes(i, 0); + } + + // z dofs + for (int i = 0; i < dof; i++) { + const double B3 = (elem_deriv_shapes(i, 2) - deriv_shapes(i, 2)) / 3.0; + B(i + 2 * dof, 0) = B3; + B(i + 2 * dof, 1) = B3; + B(i + 2 * dof, 2) = B3 + deriv_shapes(i, 2); + B(i + 2 * dof, 3) = deriv_shapes(i, 1); + B(i + 2 * dof, 4) = deriv_shapes(i, 0); + B(i + 2 * dof, 5) = 0.0; + } +} + +/** + * @brief Construct geometric B-matrix for geometric stiffness operations. + * + * @param deriv_shapes Dense matrix containing shape function derivatives in physical coordinates + * (∂N/∂x) + * @param B_geom Output geometric B-matrix for nonlinear geometric stiffness computations + * + * This function constructs the geometric B-matrix used in finite element assembly + * for computing geometric stiffness contributions in nonlinear solid mechanics. + * The geometric B-matrix is essential for capturing nonlinear effects due to + * large deformations and finite rotations. + * + * The geometric B-matrix is used in operations of the form: + * K_geom = ∫ B_geom^T * Σ_bar * B_geom dV + * + * where Σ_bar is a block-diagonal stress tensor repeated for each spatial dimension: + * ``` + * Σ_bar = [σ 0 0 ] + * [0 σ 0 ] + * [0 0 σ ] + * ``` + * + * Matrix structure for 3D elements: + * - Input deriv_shapes: (dof × 3) matrix of shape function derivatives + * - Output B_geom: (3*dof × 9) matrix organized in spatial dimension blocks + * - Each block corresponds to x, y, z displacement components + * + * The geometric B-matrix structure repeats the shape function derivatives + * in each spatial direction: + * ``` + * Block structure (for node i): + * x-block: [∂N_i/∂x ∂N_i/∂y ∂N_i/∂z 0 0 0 0 0 0] + * y-block: [0 0 0 ∂N_i/∂x ∂N_i/∂y ∂N_i/∂z 0 0 0] + * z-block: [0 0 0 0 0 0 ∂N_i/∂x ∂N_i/∂y ∂N_i/∂z] + * ``` + * + * This formulation enables efficient computation of geometric stiffness terms + * that arise from the nonlinear strain-displacement relationships in updated + * Lagrangian finite element formulations. + * + * @note This function assumes 3D elements and is optimized for performance. + * @note The deriv_shapes matrix should contain shape function derivatives in physical coordinates. + * @note The B_geom matrix must be pre-sized to (3*dof, 9) before calling this function. + * @note The function follows MFEM's vector ordering: [x₀...xₙ, y₀...yₙ, z₀...zₙ]. + * + * @ingroup ExaConstit_utilities_assembly + */ +inline void GenerateGradGeomMatrix(const mfem::DenseMatrix& deriv_shapes, + mfem::DenseMatrix& B_geom) { + int dof = deriv_shapes.Height(); + // For a 3D mesh B_geom has the following shape: + // [deriv_shapes(i, 0), 0, 0] + // [deriv_shapes(i, 0), 0, 0] + // [deriv_shapes(i, 0), 0, 0] + // [0, deriv_shapes(i, 1), 0] + // [0, deriv_shapes(i, 1), 0] + // [0, deriv_shapes(i, 1), 0] + // [0, 0, deriv_shapes(i, 2)] + // [0, 0, deriv_shapes(i, 2)] + // [0, 0, deriv_shapes(i, 2)] + // We'll be returning the transpose of this. + // It turns out the Bilinear operator can't have this created using + // the dense gradient matrix, deriv_shapes. + // It can be used in the following: B_geom^T Sigma_bar B_geom + // where Sigma_bar is a block diagonal version of sigma repeated 3 times in 3D. + + // I'm assumming we're in 3D and have just unrolled the loop + // The ordering has now changed such that B_geom matches up with mfem's internal + // ordering of vectors such that it's [x0...xn, y0...yn, z0...zn] ordering + + // The previous single loop has been split into 3 so the B matrix + // is constructed in chunks now instead of performing multiple striding + // operations in a single loop. + + // x dofs + for (int i = 0; i < dof; i++) { + B_geom(i, 0) = deriv_shapes(i, 0); + B_geom(i, 1) = deriv_shapes(i, 1); + B_geom(i, 2) = deriv_shapes(i, 2); + B_geom(i, 3) = 0.0; + B_geom(i, 4) = 0.0; + B_geom(i, 5) = 0.0; + B_geom(i, 6) = 0.0; + B_geom(i, 7) = 0.0; + B_geom(i, 8) = 0.0; + } + + // y dofs + for (int i = 0; i < dof; i++) { + B_geom(i + dof, 0) = 0.0; + B_geom(i + dof, 1) = 0.0; + B_geom(i + dof, 2) = 0.0; + B_geom(i + dof, 3) = deriv_shapes(i, 0); + B_geom(i + dof, 4) = deriv_shapes(i, 1); + B_geom(i + dof, 5) = deriv_shapes(i, 2); + B_geom(i + dof, 6) = 0.0; + B_geom(i + dof, 7) = 0.0; + B_geom(i + dof, 8) = 0.0; + } + + // z dofs + for (int i = 0; i < dof; i++) { + B_geom(i + 2 * dof, 0) = 0.0; + B_geom(i + 2 * dof, 1) = 0.0; + B_geom(i + 2 * dof, 2) = 0.0; + B_geom(i + 2 * dof, 3) = 0.0; + B_geom(i + 2 * dof, 4) = 0.0; + B_geom(i + 2 * dof, 5) = 0.0; + B_geom(i + 2 * dof, 6) = deriv_shapes(i, 0); + B_geom(i + 2 * dof, 7) = deriv_shapes(i, 1); + B_geom(i + 2 * dof, 8) = deriv_shapes(i, 2); + } +} + +/** + * @brief Get quadrature function data at a specific element and integration point. + * + * @param elem_id Global element index + * @param int_point_num Integration point number within the element + * @param qfdata Output array to store the retrieved data + * @param qf Shared pointer to the PartialQuadratureFunction + * + * This function extracts data from a PartialQuadratureFunction at a specific + * element and integration point. It handles the indexing and memory layout + * automatically, providing a convenient interface for accessing quadrature + * point data during assembly operations. + * + * The function: + * 1. Computes the correct offset based on element ID and integration point + * 2. Accounts for the vector dimension of the quadrature function + * 3. Copies the data to the provided output array + * 4. Handles both full and partial quadrature spaces transparently + * + * Data layout assumptions: + * - Data is stored element-by-element + * - Within each element, data is stored point-by-point + * - Within each point, components are stored sequentially + * + * Usage example: + * @code + * double stress[6]; // For symmetric stress tensor + * GetQFData(elem_id, qp_id, stress, stress_qf); + * // stress[0] = σ_xx, stress[1] = σ_yy, etc. + * @endcode + * + * @note The qfdata array must be pre-allocated with size qf->GetVDim(). + * @note This function uses host-side memory access patterns. + * + * @ingroup ExaConstit_utilities_assembly + */ +inline void GetQFData(const int elem_id, + const int int_point_num, + double* qfdata, + std::shared_ptr qf) { + const auto data = qf->HostRead(); + const int qf_offset = qf->GetVDim(); + auto qspace = qf->GetSpaceShared(); + + const mfem::IntegrationRule* ir = &(qf->GetSpaceShared()->GetIntRule(elem_id)); + int elem_offset = qf_offset * ir->GetNPoints(); + + for (int i = 0; i < qf_offset; ++i) { + qfdata[i] = data[elem_id * elem_offset + int_point_num * qf_offset + i]; + } +} + +/** + * @brief Set quadrature function data at a specific element and integration point. + * + * @param elem_id Global element index + * @param int_point_num Integration point number within the element + * @param qfdata Input array containing the data to store + * @param qf Shared pointer to the PartialQuadratureFunction + * + * This function stores data into a PartialQuadratureFunction at a specific + * element and integration point. It provides the complementary operation to + * GetQFData(), enabling efficient storage of computed values during assembly. + * + * The function: + * 1. Computes the correct offset based on element ID and integration point + * 2. Accounts for the vector dimension of the quadrature function + * 3. Copies the data from the input array to the quadrature function + * 4. Handles both full and partial quadrature spaces transparently + * + * This function is commonly used to store: + * - Updated stress tensors after material model evaluation + * - Computed material tangent stiffness matrices + * - State variables and internal variables + * - Derived quantities like plastic strain + * + * Usage example: + * @code + * double new_stress[6] = {s11, s22, s33, s12, s13, s23}; + * SetQFData(elem_id, qp_id, new_stress, stress_qf); + * @endcode + * + * @note The qfdata array must contain qf->GetVDim() values. + * @note This function uses host-side memory access patterns. + * @note Data is written directly to the quadrature function's internal storage. + * + * @ingroup ExaConstit_utilities_assembly + */ +inline void SetQFData(const int elem_id, + const int int_point_num, + double* qfdata, + std::shared_ptr qf) { + auto data = qf->HostReadWrite(); + const int qf_offset = qf->GetVDim(); + auto qspace = qf->GetSpaceShared(); + + const mfem::IntegrationRule* ir = &(qf->GetSpaceShared()->GetIntRule(elem_id)); + int elem_offset = qf_offset * ir->GetNPoints(); + + for (int i = 0; i < qf_offset; ++i) { + data[elem_id * elem_offset + int_point_num * qf_offset + i] = qfdata[i]; + } +} + +/** + * @brief Transform material gradient to 4D layout for partial assembly. + * + * @param mat_grad Shared pointer to material gradient PartialQuadratureFunction + * @param mat_grad_PA Output vector with 4D layout for partial assembly + * + * This function transforms material gradient data (typically tangent stiffness + * matrices) from the standard quadrature function layout to a 4D layout + * optimized for MFEM's partial assembly operations. + * + * The transformation reorganizes data to enable efficient vectorized operations + * during partial assembly, where material properties are applied element-wise + * rather than globally assembled into a sparse matrix. + * + * Layout transformation: + * - Input: Standard QF layout with material gradients per quadrature point + * - Output: 4D RAJA view layout optimized for partial assembly kernels + * - Uses permuted layouts to optimize memory access patterns + * + * The function uses RAJA views with specific permutations to: + * 1. Optimize cache performance for the target architecture + * 2. Enable vectorization in assembly kernels + * 3. Support both CPU and GPU execution + * + * This transformation is essential for high-performance partial assembly + * operations in ExaConstit's finite element solver. + * + * @note The mat_grad_PA vector is resized automatically to accommodate the data. + * @note The function assumes 3D problems with 6x6 material tangent matrices. + * @note RAJA views use specific permutations for optimal performance. + * + * @ingroup ExaConstit_utilities_assembly + */ +inline void +TransformMatGradTo4D(const std::shared_ptr mat_grad, + mfem::Vector& mat_grad_PA) { + const int npts = mat_grad->Size() / mat_grad->GetVDim(); + + const int dim = 3; + const int dim2 = 6; + + const int DIM5 = 5; + const int DIM3 = 3; + std::array perm5{{4, 3, 2, 1, 0}}; + std::array perm3{{2, 1, 0}}; + + // bunch of helper RAJA views to make dealing with data easier down below in our kernel. + RAJA::Layout layout_4Dtensor = RAJA::make_permuted_layout({{dim, dim, dim, dim, npts}}, + perm5); + RAJA::View> cmat_4d(mat_grad_PA.ReadWrite(), + layout_4Dtensor); + + // bunch of helper RAJA views to make dealing with data easier down below in our kernel. + RAJA::Layout layout_2Dtensor = RAJA::make_permuted_layout({{dim2, dim2, npts}}, perm3); + RAJA::View> cmat(mat_grad->Read(), + layout_2Dtensor); + + // This sets up our 4D tensor to be the same as the 2D tensor which takes advantage of symmetry + // operations + mfem::forall(npts, [=] MFEM_HOST_DEVICE(int i) { + cmat_4d(0, 0, 0, 0, i) = cmat(0, 0, i); + cmat_4d(1, 1, 0, 0, i) = cmat(1, 0, i); + cmat_4d(2, 2, 0, 0, i) = cmat(2, 0, i); + cmat_4d(1, 2, 0, 0, i) = cmat(3, 0, i); + cmat_4d(2, 1, 0, 0, i) = cmat_4d(1, 2, 0, 0, i); + cmat_4d(2, 0, 0, 0, i) = cmat(4, 0, i); + cmat_4d(0, 2, 0, 0, i) = cmat_4d(2, 0, 0, 0, i); + cmat_4d(0, 1, 0, 0, i) = cmat(5, 0, i); + cmat_4d(1, 0, 0, 0, i) = cmat_4d(0, 1, 0, 0, i); + + cmat_4d(0, 0, 1, 1, i) = cmat(0, 1, i); + cmat_4d(1, 1, 1, 1, i) = cmat(1, 1, i); + cmat_4d(2, 2, 1, 1, i) = cmat(2, 1, i); + cmat_4d(1, 2, 1, 1, i) = cmat(3, 1, i); + cmat_4d(2, 1, 1, 1, i) = cmat_4d(1, 2, 1, 1, i); + cmat_4d(2, 0, 1, 1, i) = cmat(4, 1, i); + cmat_4d(0, 2, 1, 1, i) = cmat_4d(2, 0, 1, 1, i); + cmat_4d(0, 1, 1, 1, i) = cmat(5, 1, i); + cmat_4d(1, 0, 1, 1, i) = cmat_4d(0, 1, 1, 1, i); + + cmat_4d(0, 0, 2, 2, i) = cmat(0, 2, i); + cmat_4d(1, 1, 2, 2, i) = cmat(1, 2, i); + cmat_4d(2, 2, 2, 2, i) = cmat(2, 2, i); + cmat_4d(1, 2, 2, 2, i) = cmat(3, 2, i); + cmat_4d(2, 1, 2, 2, i) = cmat_4d(1, 2, 2, 2, i); + cmat_4d(2, 0, 2, 2, i) = cmat(4, 2, i); + cmat_4d(0, 2, 2, 2, i) = cmat_4d(2, 0, 2, 2, i); + cmat_4d(0, 1, 2, 2, i) = cmat(5, 2, i); + cmat_4d(1, 0, 2, 2, i) = cmat_4d(0, 1, 2, 2, i); + + cmat_4d(0, 0, 1, 2, i) = cmat(0, 3, i); + cmat_4d(1, 1, 1, 2, i) = cmat(1, 3, i); + cmat_4d(2, 2, 1, 2, i) = cmat(2, 3, i); + cmat_4d(1, 2, 1, 2, i) = cmat(3, 3, i); + cmat_4d(2, 1, 1, 2, i) = cmat_4d(1, 2, 1, 2, i); + cmat_4d(2, 0, 1, 2, i) = cmat(4, 3, i); + cmat_4d(0, 2, 1, 2, i) = cmat_4d(2, 0, 1, 2, i); + cmat_4d(0, 1, 1, 2, i) = cmat(5, 3, i); + cmat_4d(1, 0, 1, 2, i) = cmat_4d(0, 1, 1, 2, i); + + cmat_4d(0, 0, 2, 1, i) = cmat(0, 3, i); + cmat_4d(1, 1, 2, 1, i) = cmat(1, 3, i); + cmat_4d(2, 2, 2, 1, i) = cmat(2, 3, i); + cmat_4d(1, 2, 2, 1, i) = cmat(3, 3, i); + cmat_4d(2, 1, 2, 1, i) = cmat_4d(1, 2, 1, 2, i); + cmat_4d(2, 0, 2, 1, i) = cmat(4, 3, i); + cmat_4d(0, 2, 2, 1, i) = cmat_4d(2, 0, 1, 2, i); + cmat_4d(0, 1, 2, 1, i) = cmat(5, 3, i); + cmat_4d(1, 0, 2, 1, i) = cmat_4d(0, 1, 1, 2, i); + + cmat_4d(0, 0, 2, 0, i) = cmat(0, 4, i); + cmat_4d(1, 1, 2, 0, i) = cmat(1, 4, i); + cmat_4d(2, 2, 2, 0, i) = cmat(2, 4, i); + cmat_4d(1, 2, 2, 0, i) = cmat(3, 4, i); + cmat_4d(2, 1, 2, 0, i) = cmat_4d(1, 2, 2, 0, i); + cmat_4d(2, 0, 2, 0, i) = cmat(4, 4, i); + cmat_4d(0, 2, 2, 0, i) = cmat_4d(2, 0, 2, 0, i); + cmat_4d(0, 1, 2, 0, i) = cmat(5, 4, i); + cmat_4d(1, 0, 2, 0, i) = cmat_4d(0, 1, 2, 0, i); + + cmat_4d(0, 0, 0, 2, i) = cmat(0, 4, i); + cmat_4d(1, 1, 0, 2, i) = cmat(1, 4, i); + cmat_4d(2, 2, 0, 2, i) = cmat(2, 4, i); + cmat_4d(1, 2, 0, 2, i) = cmat(3, 4, i); + cmat_4d(2, 1, 0, 2, i) = cmat_4d(1, 2, 2, 0, i); + cmat_4d(2, 0, 0, 2, i) = cmat(4, 4, i); + cmat_4d(0, 2, 0, 2, i) = cmat_4d(2, 0, 2, 0, i); + cmat_4d(0, 1, 0, 2, i) = cmat(5, 4, i); + cmat_4d(1, 0, 0, 2, i) = cmat_4d(0, 1, 2, 0, i); + + cmat_4d(0, 0, 0, 1, i) = cmat(0, 5, i); + cmat_4d(1, 1, 0, 1, i) = cmat(1, 5, i); + cmat_4d(2, 2, 0, 1, i) = cmat(2, 5, i); + cmat_4d(1, 2, 0, 1, i) = cmat(3, 5, i); + cmat_4d(2, 1, 0, 1, i) = cmat_4d(1, 2, 0, 1, i); + cmat_4d(2, 0, 0, 1, i) = cmat(4, 5, i); + cmat_4d(0, 2, 0, 1, i) = cmat_4d(2, 0, 0, 1, i); + cmat_4d(0, 1, 0, 1, i) = cmat(5, 5, i); + cmat_4d(1, 0, 0, 1, i) = cmat_4d(0, 1, 0, 1, i); + + cmat_4d(0, 0, 1, 0, i) = cmat(0, 5, i); + cmat_4d(1, 1, 1, 0, i) = cmat(1, 5, i); + cmat_4d(2, 2, 1, 0, i) = cmat(2, 5, i); + cmat_4d(1, 2, 1, 0, i) = cmat(3, 5, i); + cmat_4d(2, 1, 1, 0, i) = cmat_4d(1, 2, 0, 1, i); + cmat_4d(2, 0, 1, 0, i) = cmat(4, 5, i); + cmat_4d(0, 2, 1, 0, i) = cmat_4d(2, 0, 0, 1, i); + cmat_4d(0, 1, 1, 0, i) = cmat(5, 5, i); + cmat_4d(1, 0, 1, 0, i) = cmat_4d(0, 1, 0, 1, i); + }); +} diff --git a/src/utilities/dynamic_function_loader.hpp b/src/utilities/dynamic_function_loader.hpp new file mode 100644 index 0000000..2f932f5 --- /dev/null +++ b/src/utilities/dynamic_function_loader.hpp @@ -0,0 +1,434 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Platform-specific includes +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace exaconstit { + +/** + * @brief Platform-specific library handle abstraction + */ +class LibraryHandle { +public: + LibraryHandle() = default; + explicit LibraryHandle(void* handle_) : handle(handle_) {} + + // Move-only semantics + LibraryHandle(const LibraryHandle&) = delete; + LibraryHandle& operator=(const LibraryHandle&) = delete; + LibraryHandle(LibraryHandle&& other) noexcept : handle(std::exchange(other.handle, nullptr)) {} + LibraryHandle& operator=(LibraryHandle&& other) noexcept { + if (this != &other) { + unload(); + handle = std::exchange(other.handle, nullptr); + } + return *this; + } + + ~LibraryHandle() { + unload(); + } + + void* get() const { + return handle; + } + explicit operator bool() const { + return handle != nullptr; + } + + void* release() { + return std::exchange(handle, nullptr); + } + +private: + void unload() { + if (handle) { +#ifdef _WIN32 + ::FreeLibrary(static_cast(handle)); +#else + ::dlclose(handle); +#endif + handle = nullptr; + } + } + + void* handle = nullptr; +}; + +/** + * @brief Loading strategies for dynamic libraries + */ +enum class LoadStrategy { + PERSISTENT, ///< Keep loaded for entire application lifetime + LOAD_ON_SETUP, ///< Load during setup, unload after each use + LAZY_LOAD ///< Load on first use, unload when refs drop to zero +}; + +/** + * @brief Symbol resolution configuration + */ +struct SymbolConfig { + std::vector search_names; ///< Symbol names to search for + bool enable_fortran_mangling = true; ///< Generate Fortran name variants + bool enable_builtin_search = true; ///< Search in main executable + bool case_sensitive = true; ///< Case-sensitive symbol search +}; + +/** + * @brief Information about a loaded function + */ +template +struct LoadedFunction { + std::string library_path; + std::string resolved_symbol; + FuncType function = nullptr; // Changed from FuncType* to FuncType + LoadStrategy strategy = LoadStrategy::PERSISTENT; + std::atomic reference_count{0}; + + // Default constructor + LoadedFunction() = default; + + // Move constructor (needed because std::atomic is not moveable) + LoadedFunction(LoadedFunction&& other) noexcept + : library_path(std::move(other.library_path)), + resolved_symbol(std::move(other.resolved_symbol)), function(other.function), + strategy(other.strategy), reference_count(other.reference_count.load()) { + other.function = nullptr; + } + + // Deleted copy constructor and assignment + LoadedFunction(const LoadedFunction&) = delete; + LoadedFunction& operator=(const LoadedFunction&) = delete; + LoadedFunction& operator=(LoadedFunction&&) = delete; +}; + +/** + * @brief Generic dynamic function loader with symbol resolution + * + * @tparam FuncType Function pointer type (e.g., UmatFunction) + */ +template +class DynamicFunctionLoader { + static_assert(std::is_function_v>, + "FuncType must be a function pointer type"); + +public: + /** + * @brief Result type for load operations + */ + struct LoadResult { + FuncType function = nullptr; // Changed from FuncType* to FuncType + std::string resolved_symbol; + std::string error_message; + bool success = false; + + operator bool() const { + return success; + } + }; + + /** + * @brief Load a function from a library + * + * @param library_path Path to the library (empty for built-in search) + * @param config Symbol resolution configuration + * @param strategy Loading strategy + * @return LoadResult containing function pointer and status + */ + static LoadResult load(const std::string& library_path, + const SymbolConfig& config, + LoadStrategy strategy = LoadStrategy::PERSISTENT) { + std::lock_guard lock(mutex_lock); + + // Check cache first + auto cache_key = make_cache_key(library_path, config); + auto it = loaded_libraries.find(cache_key); + if (it != loaded_libraries.end()) { + it->second.reference_count++; + return {it->second.function, it->second.resolved_symbol, "", true}; + } + + // Perform the load + LoadResult result; + + if (library_path.empty() && config.enable_builtin_search) { + result = load_builtin(config); + } else if (!library_path.empty()) { + result = load_from_library(library_path, config); + } else { + result.error_message = "No library path specified and built-in search disabled"; + } + + // Cache successful loads + if (result.success) { + LoadedFunction info; + info.library_path = library_path; + info.resolved_symbol = result.resolved_symbol; + info.function = result.function; + info.strategy = strategy; + info.reference_count = 1; + + loaded_libraries.emplace(cache_key, std::move(info)); + } + + return result; + } + + /** + * @brief Unload a previously loaded function + */ + static bool unload(const std::string& library_path, const SymbolConfig& config) { + std::lock_guard lock(mutex_lock); + + auto cache_key = make_cache_key(library_path, config); + auto it = loaded_libraries.find(cache_key); + if (it == loaded_libraries.end()) { + return false; + } + + if (--it->second.reference_count <= 0) { + if (it->second.strategy != LoadStrategy::PERSISTENT) { + auto lib_it = library_handles.find(library_path); + if (lib_it != library_handles.end()) { + library_handles.erase(lib_it); + } + loaded_libraries.erase(it); + } + } + + return true; + } + + /** + * @brief Check if a library provides a valid function + */ + static bool validate(const std::string& library_path, const SymbolConfig& config) { + auto result = load(library_path, config, LoadStrategy::LAZY_LOAD); + if (result.success) { + unload(library_path, config); + } + return result.success; + } + + /** + * @brief Get the last error message for the current thread + */ + static std::string get_last_error() { + return last_error; + } + + /** + * @brief Clear all cached libraries and force unload + */ + static void clear_cache() { + std::lock_guard lock(mutex_lock); + loaded_libraries.clear(); + library_handles.clear(); + } + +private: + // Static members + static std::unordered_map> loaded_libraries; + static std::unordered_map library_handles; + static std::mutex mutex_lock; + static thread_local std::string last_error; + + /** + * @brief Generate all possible symbol variants based on config + */ + static std::vector generate_symbol_variants(const SymbolConfig& config) { + std::vector variants; + + for (const auto& base_name : config.search_names) { + // Original name + variants.push_back(base_name); + + if (config.enable_fortran_mangling) { + // Common Fortran manglings + variants.push_back(base_name + "_"); // gfortran/flang default + variants.push_back(base_name + "__"); // g77 with underscores + variants.push_back("_" + base_name); // Leading underscore + variants.push_back("_" + base_name + "_"); // Both + + if (!config.case_sensitive) { + // Uppercase variants + std::string upper = base_name; + std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper); + variants.push_back(upper); + variants.push_back(upper + "_"); + + // Lowercase variants + std::string lower = base_name; + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + if (lower != base_name) { + variants.push_back(lower); + variants.push_back(lower + "_"); + } + } + } + } + + // Remove duplicates while preserving order + std::vector unique_variants; + std::unordered_set seen; + for (const auto& variant : variants) { + if (seen.insert(variant).second) { + unique_variants.push_back(variant); + } + } + variants = std::move(unique_variants); + + return variants; + } + + /** + * @brief Try to find a symbol in a handle + */ + static void* find_symbol(void* handle, const std::string& symbol) { +#ifdef _WIN32 + return ::GetProcAddress(static_cast(handle), symbol.c_str()); +#else + // Clear any existing error + ::dlerror(); + return ::dlsym(handle, symbol.c_str()); +#endif + } + + /** + * @brief Load from the main executable (built-in) + */ + static LoadResult load_builtin(const SymbolConfig& config) { + LoadResult result; + +#ifdef _WIN32 + void* handle = ::GetModuleHandle(nullptr); +#else + void* handle = RTLD_DEFAULT; +#endif + + auto variants = generate_symbol_variants(config); + for (const auto& symbol : variants) { + if (void* func = find_symbol(handle, symbol)) { + result.function = reinterpret_cast(func); + result.resolved_symbol = symbol; + result.success = true; + last_error = "Found built-in function: " + symbol; + return result; + } + } + + result.error_message = "No built-in symbol found. Searched: "; + for (const auto& sym : variants) { + result.error_message += sym + " "; + } + last_error = result.error_message; + return result; + } + + /** + * @brief Load from a specific library file + */ + static LoadResult load_from_library(const std::string& library_path, + const SymbolConfig& config) { + LoadResult result; + + // Get or create library handle + auto& handle = library_handles[library_path]; + if (!handle) { +#ifdef _WIN32 + void* h = ::LoadLibraryA(library_path.c_str()); + if (!h) { + DWORD error = ::GetLastError(); + char error_buf[256]; + ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + error_buf, + sizeof(error_buf), + NULL); + result.error_message = "Failed to load library: " + std::string(error_buf); + } +#else + void* h = ::dlopen(library_path.c_str(), RTLD_NOW | RTLD_LOCAL); + if (!h) { + const char* error = ::dlerror(); + result.error_message = "Failed to load library: " + + std::string(error ? error : "Unknown error"); + } +#endif + if (!h) { + last_error = result.error_message; + return result; + } + handle = LibraryHandle(h); + } + + // Search for symbols + auto variants = generate_symbol_variants(config); + for (const auto& symbol : variants) { + if (void* func = find_symbol(handle.get(), symbol)) { + result.function = reinterpret_cast(func); + result.resolved_symbol = symbol; + result.success = true; + last_error = "Found function '" + symbol + "' in library: " + library_path; + return result; + } + } + + // Symbol not found + result.error_message = "No symbol found in '" + library_path + "'. Searched: "; + for (const auto& sym : variants) { + result.error_message += sym + " "; + } + last_error = result.error_message; + + // Remove handle if we couldn't find the symbol + library_handles.erase(library_path); + + return result; + } + + /** + * @brief Create a cache key from library path and config + */ + static std::string make_cache_key(const std::string& library_path, const SymbolConfig& config) { + std::string key = library_path + "|"; + for (const auto& name : config.search_names) { + key += name + ","; + } + return key; + } +}; + +// Static member definitions +template +std::unordered_map> + DynamicFunctionLoader::loaded_libraries; + +template +std::unordered_map DynamicFunctionLoader::library_handles; + +template +std::mutex DynamicFunctionLoader::mutex_lock; + +template +thread_local std::string DynamicFunctionLoader::last_error; + +} // namespace exaconstit \ No newline at end of file diff --git a/src/utilities/mechanics_kernels.cpp b/src/utilities/mechanics_kernels.cpp new file mode 100644 index 0000000..3a81fbd --- /dev/null +++ b/src/utilities/mechanics_kernels.cpp @@ -0,0 +1,108 @@ +#include "utilities/mechanics_kernels.hpp" + +#include "mfem/general/forall.hpp" + +namespace exaconstit { +namespace kernel { + +// Updated implementation in mechanics_kernels.cpp +void GradCalc(const int nqpts, + const int nelems, + const int global_nelems, + const int nnodes, + const double* jacobian_data, + const double* loc_grad_data, + const double* field_data, + double* field_grad_array, + const int* const local2global) { + const int DIM4 = 4; + const int DIM3 = 3; + const int DIM2 = 2; + std::array perm4{{3, 2, 1, 0}}; + std::array perm3{{2, 1, 0}}; + std::array perm2{{1, 0}}; + + const int dim = 3; + const int space_dim2 = dim * dim; + + // Determine the size for input data views (global data) + const int input_nelems = local2global ? global_nelems : nelems; + + // Set up RAJA views for input data (sized for global elements) + RAJA::Layout layout_jacob_input = RAJA::make_permuted_layout( + {{dim, dim, nqpts, input_nelems}}, perm4); + RAJA::View> J_input(jacobian_data, + layout_jacob_input); + + RAJA::Layout layout_field_input = RAJA::make_permuted_layout( + {{nnodes, dim, input_nelems}}, perm3); + RAJA::View> field_input( + field_data, layout_field_input); + + RAJA::Layout layout_loc_grad = RAJA::make_permuted_layout({{nnodes, dim, nqpts}}, perm3); + RAJA::View> loc_grad_view( + loc_grad_data, layout_loc_grad); + + // Set up RAJA views for output data (sized for local elements) + RAJA::Layout layout_grad_output = RAJA::make_permuted_layout({{dim, dim, nqpts, nelems}}, + perm4); + RAJA::View> field_grad_view(field_grad_array, + layout_grad_output); + + RAJA::Layout layout_jinv = RAJA::make_permuted_layout({{dim, dim}}, perm2); + + // Process local elements (loop over nelems which is the local count) + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i_local_elem) { + // Map local element index to global element index for input data access + const int i_global_elem = local2global ? local2global[i_local_elem] : i_local_elem; + + for (int j_qpts = 0; j_qpts < nqpts; j_qpts++) { + // Access input data using global element index + const double J11 = J_input(0, 0, j_qpts, i_global_elem); + const double J21 = J_input(1, 0, j_qpts, i_global_elem); + const double J31 = J_input(2, 0, j_qpts, i_global_elem); + const double J12 = J_input(0, 1, j_qpts, i_global_elem); + const double J22 = J_input(1, 1, j_qpts, i_global_elem); + const double J32 = J_input(2, 1, j_qpts, i_global_elem); + const double J13 = J_input(0, 2, j_qpts, i_global_elem); + const double J23 = J_input(1, 2, j_qpts, i_global_elem); + const double J33 = J_input(2, 2, j_qpts, i_global_elem); + + const double detJ = J11 * (J22 * J33 - J32 * J23) - J21 * (J12 * J33 - J32 * J13) + + J31 * (J12 * J23 - J22 * J13); + const double c_detJ = 1.0 / detJ; + + // Calculate adjugate matrix (inverse * determinant) + const double A11 = c_detJ * ((J22 * J33) - (J23 * J32)); + const double A12 = c_detJ * ((J32 * J13) - (J12 * J33)); + const double A13 = c_detJ * ((J12 * J23) - (J22 * J13)); + const double A21 = c_detJ * ((J31 * J23) - (J21 * J33)); + const double A22 = c_detJ * ((J11 * J33) - (J13 * J31)); + const double A23 = c_detJ * ((J21 * J13) - (J11 * J23)); + const double A31 = c_detJ * ((J21 * J32) - (J31 * J22)); + const double A32 = c_detJ * ((J31 * J12) - (J11 * J32)); + const double A33 = c_detJ * ((J11 * J22) - (J12 * J21)); + + const double A[space_dim2] = {A11, A21, A31, A12, A22, A32, A13, A23, A33}; + RAJA::View> jinv_view( + &A[0], layout_jinv); + + // Calculate field gradient - access input field data with global index + // but write output data with local index + for (int t = 0; t < dim; t++) { + for (int s = 0; s < dim; s++) { + for (int r = 0; r < nnodes; r++) { + for (int q = 0; q < dim; q++) { + field_grad_view( + q, t, j_qpts, i_local_elem) += field_input(r, q, i_global_elem) * + loc_grad_view(r, s, j_qpts) * + jinv_view(s, t); + } + } + } + } + } + }); +} // end GradCalc +} // end namespace kernel +} // end namespace exaconstit \ No newline at end of file diff --git a/src/utilities/mechanics_kernels.hpp b/src/utilities/mechanics_kernels.hpp new file mode 100644 index 0000000..e7d139a --- /dev/null +++ b/src/utilities/mechanics_kernels.hpp @@ -0,0 +1,898 @@ +#ifndef MECHANICS_KERNELS +#define MECHANICS_KERNELS + +#include "mfem_expt/partial_qfunc.hpp" +#include "options/option_parser_v2.hpp" + +#include "RAJA/RAJA.hpp" +#include "mfem.hpp" +#include "mfem/general/forall.hpp" + +/** + * @brief ExaConstit computational kernels for finite element operations. + * + * This namespace contains high-performance computational kernels used throughout + * ExaConstit for finite element operations, particularly gradient calculations + * and field transformations. The kernels are designed to work with both full + * and partial element sets, supporting multi-material simulations with optimal + * performance. + * + * @ingroup ExaConstit_utilities + */ +namespace exaconstit { +namespace kernel { + +/** + * @brief Main gradient calculation function with partial element mapping support. + * + * @param nqpts Number of quadrature points per element + * @param nelems Number of local elements to process in the partial set + * @param global_nelems Total number of elements in global arrays (for input data sizing) + * @param nnodes Number of nodes per element (typically 8 for hexahedral elements) + * @param jacobian_data Global jacobian data array (sized for global_nelems) + * @param loc_grad_data Global local gradient data array (shape function derivatives) + * @param field_data Global field data array (velocity or displacement field, sized for + * global_nelems) + * @param field_grad_array Local output array for computed gradients (sized for nelems) + * @param local2global Optional mapping from local to global element indices + * + * This function computes field gradients (typically velocity gradients) at quadrature + * points for a subset of mesh elements. It supports both full mesh processing and + * partial element processing for multi-material simulations. + * + * The function performs the fundamental finite element operation: + * ∇u = ∑(N_i,α * u_i) where N_i,α are shape function derivatives and u_i are nodal values. + * + * Key features: + * - RAJA-based implementation for performance portability (CPU/GPU) + * - Support for partial element processing via local2global mapping + * - Efficient memory layout optimized for vectorization + * - Automatic handling of Jacobian inverse computation + * - Compatible with MFEM's FORALL construct for device execution + * + * The computation involves: + * 1. Mapping local element indices to global indices (if partial processing) + * 2. Computing Jacobian inverse at each quadrature point + * 3. Transforming shape function derivatives from reference to physical space + * 4. Computing field gradients using chain rule: ∇u = (∂N/∂ξ)(∂ξ/∂x)u + * + * @note The jacobian_data and loc_grad_data are sized for global elements, + * while field_grad_array is sized for local elements only. + * @note When local2global is nullptr, assumes nelems == global_nelems (full processing). + * @note All arrays must be properly sized and allocated before calling this function. + */ +void GradCalc(const int nqpts, + const int nelems, + const int global_nelems, + const int nnodes, + const double* jacobian_data, + const double* loc_grad_data, + const double* field_data, + double* field_grad_array, + const int* const local2global = nullptr); + +/** + * @brief Backward compatibility overload - assumes full element processing. + * + * @param nqpts Number of quadrature points per element + * @param nelems Number of elements to process + * @param nnodes Number of nodes per element + * @param jacobian_data Jacobian data array + * @param loc_grad_data Local gradient data array (shape function derivatives) + * @param field_data Field data array (velocity or displacement field) + * @param field_grad_array Output gradient array + * + * This overload provides backward compatibility for code that processes all + * elements in the mesh without partial element mapping. It internally calls + * the main GradCalc function with local2global = nullptr. + * + * This is equivalent to calling the main function with: + * - global_nelems = nelems + * - local2global = nullptr + * + * @deprecated Use the full signature with explicit global_nelems for clarity + * and better support of partial element processing. + */ +inline void GradCalc(const int nqpts, + const int nelems, + const int nnodes, + const double* jacobian_data, + const double* loc_grad_data, + const double* field_data, + double* field_grad_array) { + // Call the full version with no partial mapping (backward compatibility) + GradCalc(nqpts, + nelems, + nelems, + nnodes, + jacobian_data, + loc_grad_data, + field_data, + field_grad_array, + nullptr); +} + +/** + * @brief Compute volume-averaged tensor values from quadrature function data. + * + * @tparam vol_avg Boolean template parameter controlling averaging behavior + * @param fes Parallel finite element space defining the mesh and element structure + * @param qf Quadrature function containing the tensor data at quadrature points + * @param tensor Output vector for the volume-averaged tensor components + * @param size Number of tensor components per quadrature point + * @param class_device Runtime model for device execution policy + * + * This template function computes volume-averaged values of tensor quantities + * stored at quadrature points. It supports both simple averaging and proper + * volume-weighted averaging depending on the template parameter. + * + * The volume averaging computation follows: + * = (∫ T(x) dV) / (∫ dV) = (∑ T_qp * |J_qp| * w_qp) / (∑ |J_qp| * w_qp) + * + * where: + * - T(x) is the tensor field to be averaged + * - T_qp are the tensor values at quadrature points + * - |J_qp| are the Jacobian determinants at quadrature points + * - w_qp are the quadrature weights + * + * Algorithm steps: + * 1. Set up RAJA views for efficient memory access patterns + * 2. Loop over all elements and quadrature points + * 3. Accumulate weighted tensor values and total volume + * 4. Normalize by total volume if vol_avg is true + * + * Template behavior: + * - vol_avg = true: Performs proper volume averaging (tensor /= total_volume) + * - vol_avg = false: Returns volume-weighted sum without normalization + * + * This function is essential for: + * - Computing homogenized material properties + * - Extracting representative volume element (RVE) responses + * - Postprocessing stress and strain fields + * - Volume averaging for multiscale analysis + * + * @note The tensor vector is resized automatically to match the size parameter. + * @note RAJA views are used for performance portability across CPU/GPU. + * @note MPI parallelization requires additional reduction across processes. + * + * @ingroup ExaConstit_utilities_kernels + */ +template +void ComputeVolAvgTensor(const mfem::ParFiniteElementSpace* fes, + const mfem::QuadratureFunction* qf, + mfem::Vector& tensor, + int size, + RTModel& class_device) { + mfem::Mesh* mesh = fes->GetMesh(); + const mfem::FiniteElement& el = *fes->GetFE(0); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + ; + + const int nqpts = ir->GetNPoints(); + const int nelems = fes->GetNE(); + const int npts = nqpts * nelems; + + const double* W = ir->GetWeights().Read(); + const mfem::GeometricFactors* geom = mesh->GetGeometricFactors( + *ir, mfem::GeometricFactors::DETERMINANTS); + + double el_vol = 0.0; + int my_id; + MPI_Comm_rank(MPI_COMM_WORLD, &my_id); + mfem::Vector data(size); + + const int DIM2 = 2; + std::array perm2{{1, 0}}; + RAJA::Layout layout_geom = RAJA::make_permuted_layout({{nqpts, nelems}}, perm2); + + mfem::Vector wts(geom->detJ); + RAJA::View> wts_view(wts.ReadWrite(), + layout_geom); + RAJA::View> j_view(geom->detJ.Read(), + layout_geom); + + RAJA::RangeSegment default_range(0, npts); + + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i) { + const int nqpts_ = nqpts; + for (int j = 0; j < nqpts_; j++) { + wts_view(j, i) = j_view(j, i) * W[j]; + } + }); + + if (class_device == RTModel::CPU) { + const double* qf_data = qf->HostRead(); + const double* wts_data = wts.HostRead(); + for (int j = 0; j < size; j++) { + RAJA::ReduceSum seq_sum(0.0); + RAJA::ReduceSum vol_sum(0.0); + RAJA::forall(default_range, [=](int i_npts) { + const double* val = &(qf_data[i_npts * size]); + seq_sum += wts_data[i_npts] * val[j]; + vol_sum += wts_data[i_npts]; + }); + data[j] = seq_sum.get(); + el_vol = vol_sum.get(); + } + } +#if defined(RAJA_ENABLE_OPENMP) + if (class_device == RTModel::OPENMP) { + const double* qf_data = qf->HostRead(); + const double* wts_data = wts.HostRead(); + for (int j = 0; j < size; j++) { + RAJA::ReduceSum omp_sum(0.0); + RAJA::ReduceSum vol_sum(0.0); + RAJA::forall(default_range, [=](int i_npts) { + const double* val = &(qf_data[i_npts * size]); + omp_sum += wts_data[i_npts] * val[j]; + vol_sum += wts_data[i_npts]; + }); + data[j] = omp_sum.get(); + el_vol = vol_sum.get(); + } + } +#endif +#if defined(RAJA_ENABLE_CUDA) || defined(RAJA_ENABLE_HIP) + if (class_device == RTModel::GPU) { + const double* qf_data = qf->Read(); + const double* wts_data = wts.Read(); +#if defined(RAJA_ENABLE_CUDA) + using gpu_reduce = RAJA::cuda_reduce; + using gpu_policy = RAJA::cuda_exec<1024>; +#else + using gpu_reduce = RAJA::hip_reduce; + using gpu_policy = RAJA::hip_exec<1024>; +#endif + for (int j = 0; j < size; j++) { + RAJA::ReduceSum gpu_sum(0.0); + RAJA::ReduceSum vol_sum(0.0); + RAJA::forall(default_range, [=] RAJA_DEVICE(int i_npts) { + const double* val = &(qf_data[i_npts * size]); + gpu_sum += wts_data[i_npts] * val[j]; + vol_sum += wts_data[i_npts]; + }); + data[j] = gpu_sum.get(); + el_vol = vol_sum.get(); + } + } +#endif + + for (int i = 0; i < size; i++) { + tensor[i] = data[i]; + } + + MPI_Allreduce( + data.HostRead(), tensor.HostReadWrite(), size, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + if (vol_avg) { + double temp = el_vol; + + // Here we find what el_vol should be equal to + MPI_Allreduce(&temp, &el_vol, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + + // We meed to multiple by 1/V by our tensor values to get the appropriate + // average value for the tensor in the end. + double inv_vol = 1.0 / el_vol; + + for (int m = 0; m < size; m++) { + tensor[m] *= inv_vol; + } + } +} + +/** + * @brief Compute filtered volume-averaged tensor values from full QuadratureFunction. + * + * @tparam vol_avg Boolean template parameter controlling averaging behavior + * @param fes Parallel finite element space defining the mesh and element structure + * @param qf Quadrature function containing the tensor data at quadrature points + * @param filter Boolean array indicating which quadrature points to include + * @param tensor Output vector for the volume-averaged tensor components + * @param size Number of tensor components per quadrature point + * @param class_device Runtime model for device execution policy + * @return Total volume of the filtered region + * + * This template function provides filtering capability for full mesh + * QuadratureFunction data. It processes the entire mesh but includes only + * those quadrature points where the filter condition is satisfied. + * + * This function bridges the gap between: + * - Full mesh processing (ComputeVolAvgTensor) + * - Partial mesh processing (ComputeVolAvgTensorFromPartial) + * - Filtered partial processing (ComputeVolAvgTensorFilterFromPartial) + * + * Use cases include: + * - Legacy code integration with filtering requirements + * - Dynamic filtering where partial spaces are not pre-defined + * - Multi-criteria filtering across the entire domain + * - Exploratory data analysis on full simulation results + * + * Performance considerations: + * - Processes entire mesh but conditionally accumulates + * - More memory bandwidth than partial space alternatives + * - Suitable when filter changes frequently or is complex + * - RAJA views optimize memory access patterns + * + * The filter array organization follows standard QuadratureFunction layout: + * - Element-major ordering: filter[elem][qp] + * - Total size: nqpts * nelems + * - Boolean values minimize memory footprint + * + * Integration with ExaConstit workflows: + * - Supports all material models and integration rules + * - Compatible with existing postprocessing infrastructure + * - Enables gradual migration to partial space architectures + * - Returns volume for consistency with other averaging functions + * + * @note Filter array must cover all quadrature points in the mesh. + * @note Performance scales with total mesh size, not filtered size. + * @note Return volume enables multi-region volume fraction calculations. + * + * @ingroup ExaConstit_utilities_kernels + */ +template +double ComputeVolAvgTensorFilter(const mfem::ParFiniteElementSpace* fes, + const mfem::QuadratureFunction* qf, + const mfem::Array* filter, + mfem::Vector& tensor, + int size, + const RTModel& class_device) { + mfem::Mesh* mesh = fes->GetMesh(); + const mfem::FiniteElement& el = *fes->GetFE(0); + const mfem::IntegrationRule* ir = &( + mfem::IntRules.Get(el.GetGeomType(), 2 * el.GetOrder() + 1)); + ; + + const int nqpts = ir->GetNPoints(); + const int nelems = fes->GetNE(); + const int npts = nqpts * nelems; + + const double* W = ir->GetWeights().Read(); + const mfem::GeometricFactors* geom = mesh->GetGeometricFactors( + *ir, mfem::GeometricFactors::DETERMINANTS); + + double el_vol = 0.0; + int my_id; + MPI_Comm_rank(MPI_COMM_WORLD, &my_id); + mfem::Vector data(size); + + const int DIM2 = 2; + std::array perm2{{1, 0}}; + RAJA::Layout layout_geom = RAJA::make_permuted_layout({{nqpts, nelems}}, perm2); + + mfem::Vector wts(geom->detJ); + RAJA::View> wts_view(wts.ReadWrite(), + layout_geom); + RAJA::View> j_view(geom->detJ.Read(), + layout_geom); + + RAJA::RangeSegment default_range(0, npts); + + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i) { + const int nqpts_ = nqpts; + for (int j = 0; j < nqpts_; j++) { + wts_view(j, i) = j_view(j, i) * W[j]; + } + }); + + if (class_device == RTModel::CPU) { + const double* qf_data = qf->HostRead(); + const bool* filter_data = filter->HostRead(); + const double* wts_data = wts.HostRead(); + for (int j = 0; j < size; j++) { + RAJA::ReduceSum seq_sum(0.0); + RAJA::ReduceSum vol_sum(0.0); + RAJA::forall(default_range, [=](int i_npts) { + if (!filter_data[i_npts]) + return; + const double* val = &(qf_data[i_npts * size]); + seq_sum += wts_data[i_npts] * val[j]; + vol_sum += wts_data[i_npts]; + }); + data[j] = seq_sum.get(); + el_vol = vol_sum.get(); + } + } +#if defined(RAJA_ENABLE_OPENMP) + if (class_device == RTModel::OPENMP) { + const double* qf_data = qf->HostRead(); + const bool* filter_data = filter->HostRead(); + const double* wts_data = wts.HostRead(); + for (int j = 0; j < size; j++) { + RAJA::ReduceSum omp_sum(0.0); + RAJA::ReduceSum vol_sum(0.0); + RAJA::forall(default_range, [=](int i_npts) { + if (!filter_data[i_npts]) + return; + const double* val = &(qf_data[i_npts * size]); + omp_sum += wts_data[i_npts] * val[j]; + vol_sum += wts_data[i_npts]; + }); + data[j] = omp_sum.get(); + el_vol = vol_sum.get(); + } + } +#endif +#if defined(RAJA_ENABLE_CUDA) || defined(RAJA_ENABLE_HIP) + if (class_device == RTModel::GPU) { + const double* qf_data = qf->Read(); + const bool* filter_data = filter->Read(); + const double* wts_data = wts.Read(); +#if defined(RAJA_ENABLE_CUDA) + using gpu_reduce = RAJA::cuda_reduce; + using gpu_policy = RAJA::cuda_exec<1024>; +#else + using gpu_reduce = RAJA::hip_reduce; + using gpu_policy = RAJA::hip_exec<1024>; +#endif + for (int j = 0; j < size; j++) { + RAJA::ReduceSum gpu_sum(0.0); + RAJA::ReduceSum vol_sum(0.0); + RAJA::forall(default_range, [=] RAJA_DEVICE(int i_npts) { + if (!filter_data[i_npts]) + return; + const double* val = &(qf_data[i_npts * size]); + gpu_sum += wts_data[i_npts] * val[j]; + vol_sum += wts_data[i_npts]; + }); + data[j] = gpu_sum.get(); + el_vol = vol_sum.get(); + } + } +#endif + + for (int i = 0; i < size; i++) { + tensor[i] = data[i]; + } + + MPI_Allreduce( + data.HostRead(), tensor.HostReadWrite(), size, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + + double temp = el_vol; + // Here we find what el_vol should be equal to + MPI_Allreduce(&temp, &el_vol, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + + if (vol_avg) { + // We meed to multiple by 1/V by our tensor values to get the appropriate + // average value for the tensor in the end. + double inv_vol = (fabs(el_vol) > 1e-14) ? 1.0 / el_vol : 0.0; + + for (int m = 0; m < size; m++) { + tensor[m] *= inv_vol; + } + } + return el_vol; +} + +/** + * @brief Compute volume-averaged tensor values from PartialQuadratureFunction. + * + * @tparam vol_avg Boolean template parameter controlling averaging behavior + * @param pqf Partial quadrature function containing region-specific tensor data + * @param tensor Output vector for the volume-averaged tensor components + * @param size Number of tensor components per quadrature point + * @param class_device Runtime model for device execution policy + * @param region_comm MPI communicator associated with a given region + * @return Total volume of the region processed + * + * This template function computes volume-averaged values directly from a + * PartialQuadratureFunction, which contains data only for a specific material + * region or subdomain. This enables efficient region-specific postprocessing + * without processing the entire mesh. + * + * The function handles the complexity of partial element mapping: + * 1. Extracts local-to-global element mapping from PartialQuadratureSpace + * 2. Maps local data offsets to global geometric factors + * 3. Performs volume averaging over only the active elements + * 4. Returns the total volume of the processed region + * + * Key advantages over full mesh processing: + * - Reduced computational cost for region-specific calculations + * - Automatic handling of multi-material simulations + * - Efficient memory usage for sparse material distributions + * - Direct integration with PartialQuadratureFunction workflow + * + * Data layout handling: + * - Local data uses PartialQuadratureSpace offsets + * - Global geometric factors indexed via local-to-global mapping + * - Automatic optimization for full-space vs. partial-space cases + * + * The returned volume can be used for: + * - Volume fraction calculations in composites + * - Normalization of other regional quantities + * - Quality assurance and verification + * - Multi-scale homogenization procedures + * + * @note Debug builds include assertion checking for size/vdim consistency. + * @note The function assumes uniform element types within the region. + * @note Return value enables volume-based postprocessing workflows. + * + * @ingroup ExaConstit_utilities_kernels + */ +template +double ComputeVolAvgTensorFilterFromPartial(const mfem::expt::PartialQuadratureFunction* pqf, + const mfem::Array* filter, + mfem::Vector& tensor, + int size, + const RTModel& class_device, + MPI_Comm region_comm = MPI_COMM_WORLD) { + auto pqs = pqf->GetPartialSpaceShared(); + auto mesh = pqs->GetMeshShared(); + + // Get finite element and integration rule info + // Note: We need to get this from the global finite element space since + // the PartialQuadratureSpace doesn't have direct FE access + const int fe_order = pqs->GetOrder(); + mfem::Geometry::Type geom_type = mesh->GetElementBaseGeometry(0); // Assume uniform elements + const mfem::IntegrationRule* ir = &(mfem::IntRules.Get(geom_type, fe_order)); + + const int nqpts = ir->GetNPoints(); + const int local_nelems = pqs->GetNE(); // Number of elements in this partial space + const int nelems = mesh->GetNE(); + + // Verify size matches vdim +#if defined(MFEM_USE_DEBUG) + const int vdim = pqf->GetVDim(); + MFEM_ASSERT_0(size == vdim, "Size parameter must match quadrature function vector dimension"); +#endif + + const double* W = ir->GetWeights().Read(); + const mfem::GeometricFactors* geom = mesh->GetGeometricFactors( + *ir, mfem::GeometricFactors::DETERMINANTS); + + // Get the local-to-global element mapping and data layout info + auto l2g = pqs->GetLocal2Global().Read(); // Maps local element index to global element index + auto loc_offsets = pqs->getOffsets().Read(); // Offsets for local data layout + auto global_offsets = (pqs->GetGlobalOffset().Size() > 1) + ? pqs->GetGlobalOffset().Read() + : loc_offsets; // Offsets for global data layout + + double el_vol = 0.0; + mfem::Vector data(size); + + const int DIM2 = 2; + std::array perm2{{1, 0}}; + RAJA::Layout layout_geom = RAJA::make_permuted_layout({{nqpts, nelems}}, perm2); + + mfem::Vector wts(geom->detJ); + RAJA::View> wts_view(wts.ReadWrite(), + layout_geom); + RAJA::View> j_view(geom->detJ.Read(), + layout_geom); + + RAJA::RangeSegment default_range(0, local_nelems); + + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i) { + const int nqpts_ = nqpts; + for (int j = 0; j < nqpts_; j++) { + wts_view(j, i) = j_view(j, i) * W[j]; + } + }); + + if (class_device == RTModel::CPU) { + const double* qf_data = pqf->HostRead(); + const bool* filter_data = filter->HostRead(); + const double* wts_data = wts.HostRead(); + for (int j = 0; j < size; j++) { + RAJA::ReduceSum data_sum(0.0); + RAJA::ReduceSum vol_sum(0.0); + RAJA::forall(default_range, [=](int ie) { + const int global_elem = l2g[ie]; // Map local element to global element + const int local_offset = loc_offsets[ie]; // Offset into local data array + const int npts_elem = loc_offsets[ie + 1] - + local_offset; // Number of qpts for this element + const int global_offset = global_offsets[global_elem]; + + for (int k = 0; k < npts_elem; k++) { + if (!filter_data[local_offset + k]) + continue; + const double* val = &(qf_data[local_offset * size + k * size]); + data_sum += wts_data[global_offset + k] * val[j]; + vol_sum += wts_data[global_offset + k]; + } + }); + data[j] = data_sum.get(); + el_vol = vol_sum.get(); + } + } +#if defined(RAJA_ENABLE_OPENMP) + if (class_device == RTModel::OPENMP) { + const double* qf_data = pqf->HostRead(); + const bool* filter_data = filter->HostRead(); + const double* wts_data = wts.HostRead(); + for (int j = 0; j < size; j++) { + RAJA::ReduceSum data_sum(0.0); + RAJA::ReduceSum vol_sum(0.0); + RAJA::forall(default_range, [=](int ie) { + const int global_elem = l2g[ie]; // Map local element to global element + const int local_offset = loc_offsets[ie]; // Offset into local data array + const int npts_elem = loc_offsets[ie + 1] - + local_offset; // Number of qpts for this element + const int global_offset = global_offsets[global_elem]; + + for (int k = 0; k < npts_elem; k++) { + if (!filter_data[local_offset + k]) + continue; + const double* val = &(qf_data[local_offset * size + k * size]); + data_sum += wts_data[global_offset + k] * val[j]; + vol_sum += wts_data[global_offset + k]; + } + }); + data[j] = data_sum.get(); + el_vol = vol_sum.get(); + } + } +#endif +#if defined(RAJA_ENABLE_CUDA) || defined(RAJA_ENABLE_HIP) + if (class_device == RTModel::GPU) { + const double* qf_data = pqf->Read(); + const bool* filter_data = filter->Read(); + const double* wts_data = wts.Read(); +#if defined(RAJA_ENABLE_CUDA) + using gpu_reduce = RAJA::cuda_reduce; + using gpu_policy = RAJA::cuda_exec<1024>; +#else + using gpu_reduce = RAJA::hip_reduce; + using gpu_policy = RAJA::hip_exec<1024>; +#endif + for (int j = 0; j < size; j++) { + RAJA::ReduceSum data_sum(0.0); + RAJA::ReduceSum vol_sum(0.0); + RAJA::forall(default_range, [=] RAJA_DEVICE(int ie) { + const int global_elem = l2g[ie]; // Map local element to global element + const int local_offset = loc_offsets[ie]; // Offset into local data array + const int npts_elem = loc_offsets[ie + 1] - + local_offset; // Number of qpts for this element + const int global_offset = global_offsets[global_elem]; + + for (int k = 0; k < npts_elem; k++) { + if (!filter_data[local_offset + k]) + continue; + const double* val = &(qf_data[local_offset * size + k * size]); + data_sum += wts_data[global_offset + k] * val[j]; + vol_sum += wts_data[global_offset + k]; + } + }); + data[j] = data_sum.get(); + el_vol = vol_sum.get(); + } + } +#endif + + for (int i = 0; i < size; i++) { + tensor[i] = data[i]; + } + + MPI_Allreduce(data.HostRead(), tensor.HostReadWrite(), size, MPI_DOUBLE, MPI_SUM, region_comm); + + double temp = el_vol; + // Here we find what el_vol should be equal to + MPI_Allreduce(&temp, &el_vol, 1, MPI_DOUBLE, MPI_SUM, region_comm); + + if (vol_avg) { + // We meed to multiple by 1/V by our tensor values to get the appropriate + // average value for the tensor in the end. + double inv_vol = (fabs(el_vol) > 1e-14) ? 1.0 / el_vol : 0.0; + + for (int m = 0; m < size; m++) { + tensor[m] *= inv_vol; + } + } + return el_vol; +} + +/** + * @brief Compute filtered volume-averaged tensor values from PartialQuadratureFunction. + * + * @tparam vol_avg Boolean template parameter controlling averaging behavior + * @param pqf Partial quadrature function containing region-specific tensor data + * @param filter Boolean array indicating which quadrature points to include + * @param tensor Output vector for the volume-averaged tensor components + * @param size Number of tensor components per quadrature point + * @param class_device Runtime model for device execution policy + * @param region_comm MPI communicator associated with a given region + * @return Total volume of the filtered region + * + * This template function extends ComputeVolAvgTensorFromPartial by adding + * point-wise filtering capability. It computes volume averages over only + * those quadrature points where the filter array is true, enabling selective + * postprocessing based on material state, stress levels, or other criteria. + * + * Filtering applications: + * - Stress-based filtering (e.g., only plastic regions) + * - Phase-specific averaging in multiphase materials + * - Damage-based selective averaging + * - Grain-specific calculations in polycrystals + * - Temperature or strain-rate dependent processing + * + * Algorithm with filtering: + * 1. Loop over all local elements in the PartialQuadratureFunction + * 2. For each quadrature point, check the filter condition + * 3. Include only filtered points in volume and tensor accumulation + * 4. Normalize by filtered volume if vol_avg is true + * + * The filter array indexing must match the quadrature point layout: + * - One boolean value per quadrature point + * - Organized element-by-element, then point-by-point within elements + * - Size should equal nqpts * nelems for the partial space + * + * Memory efficiency considerations: + * - Filter array can be generated on-the-fly or cached + * - Boolean filter minimizes memory overhead + * - Processing only active points reduces computational cost + * + * Return value enables cascaded filtering operations and + * provides volume information for normalization in subsequent + * calculations or multiscale homogenization procedures. + * + * @note Filter array size must match total quadrature points in partial space. + * @note Filtering reduces computational cost but adds conditional overhead. + * @note Zero filtered volume will result in division by zero if vol_avg is true. + * + * @ingroup ExaConstit_utilities_kernels + */ +template +double ComputeVolAvgTensorFromPartial(const mfem::expt::PartialQuadratureFunction* pqf, + mfem::Vector& tensor, + int size, + const RTModel& class_device, + MPI_Comm region_comm = MPI_COMM_WORLD) { + auto pqs = pqf->GetPartialSpaceShared(); + auto mesh = pqs->GetMeshShared(); + + // Get finite element and integration rule info + // Note: We need to get this from the global finite element space since + // the PartialQuadratureSpace doesn't have direct FE access + const int fe_order = pqs->GetOrder(); + mfem::Geometry::Type geom_type = mesh->GetElementBaseGeometry(0); // Assume uniform elements + const mfem::IntegrationRule* ir = &(mfem::IntRules.Get(geom_type, fe_order)); + + const int nqpts = ir->GetNPoints(); + const int local_nelems = pqs->GetNE(); // Number of elements in this partial space + const int nelems = mesh->GetNE(); + + // Verify size matches vdim +#if defined(MFEM_USE_DEBUG) + const int vdim = pqf->GetVDim(); + MFEM_ASSERT_0(size == vdim, "Size parameter must match quadrature function vector dimension"); +#endif + + const double* W = ir->GetWeights().Read(); + const mfem::GeometricFactors* geom = mesh->GetGeometricFactors( + *ir, mfem::GeometricFactors::DETERMINANTS); + + // Get the local-to-global element mapping and data layout info + auto l2g = pqs->GetLocal2Global().Read(); // Maps local element index to global element index + auto loc_offsets = pqs->getOffsets().Read(); // Offsets for local data layout + auto global_offsets = (pqs->GetGlobalOffset().Size() > 1) + ? pqs->GetGlobalOffset().Read() + : loc_offsets; // Offsets for global data layout + + // Initialize output tensor and volume + tensor.SetSize(size); + tensor = 0.0; + double total_volume = 0.0; + mfem::Vector data(size); + + const int DIM2 = 2; + std::array perm2{{1, 0}}; + RAJA::Layout layout_geom = RAJA::make_permuted_layout({{nqpts, nelems}}, perm2); + + mfem::Vector wts(geom->detJ); + RAJA::View> wts_view(wts.ReadWrite(), + layout_geom); + RAJA::View> j_view(geom->detJ.Read(), + layout_geom); + + RAJA::RangeSegment default_range(0, local_nelems); + + mfem::forall(nelems, [=] MFEM_HOST_DEVICE(int i) { + const int nqpts_ = nqpts; + for (int j = 0; j < nqpts_; j++) { + wts_view(j, i) = j_view(j, i) * W[j]; + } + }); + + if (class_device == RTModel::CPU) { + const double* qf_data = pqf->HostRead(); + const double* wts_data = wts.HostRead(); + for (int j = 0; j < size; j++) { + RAJA::ReduceSum seq_sum(0.0); + RAJA::ReduceSum vol_sum(0.0); + RAJA::forall(default_range, [=](int ie) { + const int global_elem = l2g[ie]; // Map local element to global element + const int local_offset = loc_offsets[ie]; // Offset into local data array + const int npts_elem = loc_offsets[ie + 1] - + local_offset; // Number of qpts for this element + const int global_offset = global_offsets[global_elem]; + for (int k = 0; k < npts_elem; k++) { + const double* val = &(qf_data[local_offset * size + k * size]); + seq_sum += wts_data[global_offset + k] * val[j]; + vol_sum += wts_data[global_offset + k]; + } + }); + data[j] = seq_sum.get(); + total_volume = vol_sum.get(); + } + } +#if defined(RAJA_ENABLE_OPENMP) + if (class_device == RTModel::OPENMP) { + const double* qf_data = pqf->HostRead(); + const double* wts_data = wts.HostRead(); + for (int j = 0; j < size; j++) { + RAJA::ReduceSum omp_sum(0.0); + RAJA::ReduceSum vol_sum(0.0); + RAJA::forall(default_range, [=](int ie) { + const int global_elem = l2g[ie]; // Map local element to global element + const int local_offset = loc_offsets[ie]; // Offset into local data array + const int npts_elem = loc_offsets[ie + 1] - + local_offset; // Number of qpts for this element + const int global_offset = global_offsets[global_elem]; + for (int k = 0; k < npts_elem; k++) { + const double* val = &(qf_data[local_offset * size + k * size]); + omp_sum += wts_data[global_offset + k] * val[j]; + vol_sum += wts_data[global_offset + k]; + } + }); + data[j] = omp_sum.get(); + total_volume = vol_sum.get(); + } + } +#endif +#if defined(RAJA_ENABLE_CUDA) || defined(RAJA_ENABLE_HIP) + if (class_device == RTModel::GPU) { + const double* qf_data = pqf->Read(); + const double* wts_data = wts.Read(); +#if defined(RAJA_ENABLE_CUDA) + using gpu_reduce = RAJA::cuda_reduce; + using gpu_policy = RAJA::cuda_exec<1024>; +#else + using gpu_reduce = RAJA::hip_reduce; + using gpu_policy = RAJA::hip_exec<1024>; +#endif + for (int j = 0; j < size; j++) { + RAJA::ReduceSum gpu_sum(0.0); + RAJA::ReduceSum vol_sum(0.0); + RAJA::forall(default_range, [=] RAJA_DEVICE(int ie) { + const int global_elem = l2g[ie]; // Map local element to global element + const int local_offset = loc_offsets[ie]; // Offset into local data array + const int npts_elem = loc_offsets[ie + 1] - + local_offset; // Number of qpts for this element + const int global_offset = global_offsets[global_elem]; + for (int k = 0; k < npts_elem; k++) { + const double* val = &(qf_data[local_offset * size + k * size]); + gpu_sum += wts_data[global_offset + k] * val[j]; + vol_sum += wts_data[global_offset + k]; + } + }); + data[j] = gpu_sum.get(); + total_volume = vol_sum.get(); + } + } +#endif + + for (int i = 0; i < size; i++) { + tensor[i] = data[i]; + } + + MPI_Allreduce(data.HostRead(), tensor.HostReadWrite(), size, MPI_DOUBLE, MPI_SUM, region_comm); + + double temp = total_volume; + // Here we find what el_vol should be equal to + MPI_Allreduce(&temp, &total_volume, 1, MPI_DOUBLE, MPI_SUM, region_comm); + + if (vol_avg) { + // We meed to multiple by 1/V by our tensor values to get the appropriate + // average value for the tensor in the end. + double inv_vol = (fabs(total_volume) > 1e-14) ? 1.0 / total_volume : 0.0; + + for (int m = 0; m < size; m++) { + tensor[m] *= inv_vol; + } + } + return total_volume; +} + +} // namespace kernel +} // namespace exaconstit +#endif diff --git a/src/utilities/mechanics_log.hpp b/src/utilities/mechanics_log.hpp new file mode 100644 index 0000000..b7e3002 --- /dev/null +++ b/src/utilities/mechanics_log.hpp @@ -0,0 +1,96 @@ +/** + * @file mechanics_log.hpp + * @brief Conditional compilation macros for Caliper performance profiling. + * + * This header provides a unified interface for performance profiling using + * the Caliper performance analysis toolkit. When HAVE_CALIPER is defined, + * the macros expand to actual Caliper profiling calls. When not defined, + * they expand to empty statements, allowing code to be compiled without + * the Caliper dependency. + * + * Caliper provides low-overhead performance measurement and analysis for + * HPC applications, enabling detailed profiling of ExaConstit simulations. + * + * @ingroup ExaConstit_utilities + */ +#ifndef MECHANICS_LOG +#define MECHANICS_LOG + +#ifdef HAVE_CALIPER +#include "caliper/cali-mpi.h" +#include "caliper/cali.h" +/** + * @brief Initialize Caliper for MPI applications. + * + * This macro initializes both the MPI-aware Caliper profiling system and + * the standard Caliper profiling system. It should be called once at the + * beginning of the main function, after MPI_Init(). + * + * When HAVE_CALIPER is not defined, this expands to nothing. + */ +#define CALI_INIT \ + cali_mpi_init(); \ + cali_init(); +#else +#define CALI_INIT +/** + * @brief Mark a C++ function for profiling (disabled when Caliper unavailable). + * + * When HAVE_CALIPER is defined, this macro marks the current function for + * automatic profiling. When Caliper is not available, this expands to nothing. + */ +#define CALI_CXX_MARK_FUNCTION +/** + * @brief Begin a named profiling region (disabled when Caliper unavailable). + * + * @param name String literal name for the profiling region + * + * When HAVE_CALIPER is defined, this macro begins a named profiling region. + * Must be paired with CALI_MARK_END. When Caliper is not available, this + * expands to nothing. + * + * Example usage: + * @code + * CALI_MARK_BEGIN("matrix_assembly"); + * // ... matrix assembly code ... + * CALI_MARK_END("matrix_assembly"); + * @endcode + */ +#define CALI_MARK_BEGIN(name) +/** + * @brief End a named profiling region (disabled when Caliper unavailable). + * + * @param name String literal name for the profiling region (must match CALI_MARK_BEGIN) + * + * When HAVE_CALIPER is defined, this macro ends a named profiling region. + * Must be paired with CALI_MARK_BEGIN. When Caliper is not available, this + * expands to nothing. + */ +#define CALI_MARK_END(name) +/** + * @brief Mark a C++ scope for profiling (disabled when Caliper unavailable). + * + * @param name String literal name for the profiling scope + * + * When HAVE_CALIPER is defined, this macro marks the current scope for + * profiling using RAII (the profiling region ends when the scope exits). + * When Caliper is not available, this expands to nothing. + * + * Example usage: + * @code + * void myFunction() { + * CALI_CXX_MARK_SCOPE("myFunction"); + * // ... function body profiled automatically ... + * } // profiling region ends here + * @endcode + */ +#define CALI_CXX_MARK_SCOPE(name) +/** + * @brief Initialize Caliper (disabled when Caliper unavailable). + * + * When HAVE_CALIPER is not defined, this expands to nothing, allowing + * code to compile without the Caliper dependency. + */ +#endif + +#endif \ No newline at end of file diff --git a/src/utilities/rotations.hpp b/src/utilities/rotations.hpp new file mode 100644 index 0000000..02d0cc9 --- /dev/null +++ b/src/utilities/rotations.hpp @@ -0,0 +1,184 @@ +#pragma once + +#include "ECMech_gpu_portability.h" +#include "mfem.hpp" + +#include +#include + +/** + * @brief Convert a 3x3 rotation matrix to a unit quaternion representation. + * + * @param rmat Input rotation matrix (3x3, assumed to be orthogonal) + * @param quat Output quaternion vector (4 components: [w, x, y, z]) + * + * This function converts a 3x3 rotation matrix to its equivalent unit quaternion + * representation using a numerically stable algorithm. The conversion handles + * special cases and numerical precision issues that can arise with naive + * conversion methods. + * + * Algorithm details: + * 1. Computes the rotation angle φ from the trace of the rotation matrix + * 2. Handles the case where φ ≈ 0 (identity rotation) specially + * 3. Extracts the rotation axis from the skew-symmetric part of the matrix + * 4. Constructs the quaternion using the half-angle formulation + * + * The quaternion representation uses the convention: + * - quat[0] = cos(φ/2) (scalar part) + * - quat[1] = sin(φ/2) * axis_x (vector part x) + * - quat[2] = sin(φ/2) * axis_y (vector part y) + * - quat[3] = sin(φ/2) * axis_z (vector part z) + * + * This conversion is essential for: + * - Crystal plasticity simulations requiring orientation tracking + * - Rigid body kinematics and rotational mechanics + * - Interpolation between rotational states + * - Integration of rotational differential equations + * + * @note The input rotation matrix should be orthogonal (R^T * R = I). + * @note The output quaternion is automatically normalized to unit length. + * @note Special handling is provided for small rotation angles to avoid numerical issues. + * + * @ingroup ExaConstit_utilities_rotations + */ +inline void RMat2Quat(const mfem::DenseMatrix& rmat, mfem::Vector& quat) { + constexpr double inv2 = 0.5; + double phi = 0.0; + static const double eps = std::numeric_limits::epsilon(); + double tr_r = 0.0; + double inv_sin = 0.0; + double s = 0.0; + + quat = 0.0; + + tr_r = rmat(0, 0) + rmat(1, 1) + rmat(2, 2); + phi = inv2 * (tr_r - 1.0); + phi = std::min(phi, 1.0); + phi = std::max(phi, -1.0); + phi = std::acos(phi); + if (std::abs(phi) < eps) { + quat[3] = 1.0; + } else { + inv_sin = 1.0 / sin(phi); + quat[0] = phi; + quat[1] = inv_sin * inv2 * (rmat(2, 1) - rmat(1, 2)); + quat[2] = inv_sin * inv2 * (rmat(0, 2) - rmat(2, 0)); + quat[3] = inv_sin * inv2 * (rmat(1, 0) - rmat(0, 1)); + } + + s = std::sin(inv2 * quat[0]); + quat[0] = std::cos(quat[0] * inv2); + quat[1] = s * quat[1]; + quat[2] = s * quat[2]; + quat[3] = s * quat[3]; +} + +/** + * @brief Convert a unit quaternion to its corresponding 3x3 rotation matrix. + * + * @param quat Input unit quaternion (4 components: [w, x, y, z]) + * @param rmat Output rotation matrix (3x3, orthogonal) + * + * This function converts a unit quaternion to its equivalent 3x3 rotation matrix + * representation using the standard quaternion-to-matrix conversion formula. + * The conversion is numerically stable and efficient. + * + * The conversion uses the formula: + * R = (w² - x² - y² - z²)I + 2(vv^T) + 2w[v]× + * + * where: + * - w is the scalar part of the quaternion + * - v = [x, y, z] is the vector part + * - [v]× is the skew-symmetric matrix of v + * - I is the 3x3 identity matrix + * + * The resulting rotation matrix has the properties: + * - Orthogonal: R^T * R = I + * - Proper: det(R) = +1 + * - Preserves lengths and angles + * + * This conversion is widely used in: + * - Crystal plasticity for transforming between crystal and sample coordinates + * - Rigid body mechanics for applying rotational transformations + * - Computer graphics and robotics applications + * - Finite element simulations involving large rotations + * + * @note The input quaternion should be normalized (||q|| = 1). + * @note The output matrix is guaranteed to be a proper orthogonal matrix. + * @note This is the inverse operation of RMat2Quat(). + * + * @ingroup ExaConstit_utilities_rotations + */ +inline void Quat2RMat(const mfem::Vector& quat, mfem::DenseMatrix& rmat) { + double qbar = 0.0; + + qbar = quat[0] * quat[0] - (quat[1] * quat[1] + quat[2] * quat[2] + quat[3] * quat[3]); + + rmat(0, 0) = qbar + 2.0 * quat[1] * quat[1]; + rmat(1, 0) = 2.0 * (quat[1] * quat[2] + quat[0] * quat[3]); + rmat(2, 0) = 2.0 * (quat[1] * quat[3] - quat[0] * quat[2]); + + rmat(0, 1) = 2.0 * (quat[1] * quat[2] - quat[0] * quat[3]); + rmat(1, 1) = qbar + 2.0 * quat[2] * quat[2]; + rmat(2, 1) = 2.0 * (quat[2] * quat[3] + quat[0] * quat[1]); + + rmat(0, 2) = 2.0 * (quat[1] * quat[3] + quat[0] * quat[2]); + rmat(1, 2) = 2.0 * (quat[2] * quat[3] - quat[0] * quat[1]); + rmat(2, 2) = qbar + 2.0 * quat[3] * quat[3]; +} + +/** + * @brief Device-compatible quaternion to rotation matrix conversion. + * + * @param quat Input unit quaternion array (4 components: [w, x, y, z]) + * @param rmats Output rotation matrix array (9 components in row-major order) + * + * This function provides a device-compatible (CPU/GPU) version of quaternion + * to rotation matrix conversion. It's designed for use in GPU kernels and + * high-performance computing environments where the standard MFEM objects + * may not be suitable. + * + * Array layout: + * - Input quat: [w, x, y, z] (4 consecutive doubles) + * - Output rmats: [r11, r12, r13, r21, r22, r23, r31, r32, r33] (9 consecutive doubles) + * + * The function uses raw arrays instead of MFEM objects to ensure: + * - Compatibility with GPU execution (CUDA/HIP/OpenMP target) + * - Minimal memory overhead and optimal performance + * - Integration with ECMech material models + * - Use in vectorized and parallel operations + * + * This function is extensively used in: + * - Crystal plasticity material models running on GPU + * - Vectorized operations over multiple grains + * - Integration with external material libraries (ECMech) + * - High-performance lattice strain calculations + * + * The `__ecmech_hdev__` decorator ensures the function can be called from + * both host and device code, providing maximum flexibility for hybrid + * CPU/GPU simulations. + * + * @note This function assumes both input and output arrays are properly allocated. + * @note The quaternion should be normalized for correct results. + * @note Row-major storage order is used for the output matrix. + * + * @ingroup ExaConstit_utilities_rotations + */ +__ecmech_hdev__ inline void Quat2RMat(const double* const quat, double* const rmats) { + const double qbar = quat[0] * quat[0] - + (quat[1] * quat[1] + quat[2] * quat[2] + quat[3] * quat[3]); + + double* rmat[3] = {&rmats[0], &rmats[3], &rmats[6]}; + + rmat[0][0] = qbar + 2.0 * quat[1] * quat[1]; + rmat[1][0] = 2.0 * (quat[1] * quat[2] + quat[0] * quat[3]); + rmat[2][0] = 2.0 * (quat[1] * quat[3] - quat[0] * quat[2]); + + rmat[0][1] = 2.0 * (quat[1] * quat[2] - quat[0] * quat[3]); + rmat[1][1] = qbar + 2.0 * quat[2] * quat[2]; + rmat[2][1] = 2.0 * (quat[2] * quat[3] + quat[0] * quat[1]); + + rmat[0][2] = 2.0 * (quat[1] * quat[3] + quat[0] * quat[2]); + rmat[1][2] = 2.0 * (quat[2] * quat[3] - quat[0] * quat[1]); + rmat[2][2] = qbar + 2.0 * quat[3] * quat[3]; +} \ No newline at end of file diff --git a/src/utilities/strain_measures.hpp b/src/utilities/strain_measures.hpp new file mode 100644 index 0000000..90567a0 --- /dev/null +++ b/src/utilities/strain_measures.hpp @@ -0,0 +1,469 @@ +#pragma once + +#include "utilities/rotations.hpp" + +#include "mfem.hpp" + +#include + +/** + * @brief Compute polar decomposition of a 3x3 deformation gradient using stable rotation + * extraction. + * + * @param R Input deformation gradient matrix, output rotation matrix (3x3) + * @param U Output right stretch tensor (3x3) + * @param V Output left stretch tensor (3x3) + * @param err Convergence tolerance for iterative algorithm (default: 1e-12) + * + * This function computes the polar decomposition F = R*U = V*R of a 3x3 deformation + * gradient matrix using a fast and robust iterative algorithm proposed by Müller et al. + * The method is particularly well-suited for finite element applications where + * numerical stability and performance are critical. + * + * Polar decomposition separates the deformation into: + * - R: Rotation tensor (proper orthogonal matrix, det(R) = +1) + * - U: Right stretch tensor (symmetric positive definite) + * - V: Left stretch tensor (symmetric positive definite) + * + * Algorithm characteristics: + * - Based on iterative extraction of rotation from deformation gradient + * - Uses quaternion intermediate representation for numerical stability + * - Exponential mapping ensures rapid convergence + * - Robust handling of near-singular and large deformation cases + * - Maximum 500 iterations with configurable tolerance + * + * The algorithm performs these steps: + * 1. Extract initial rotation estimate using SVD-based quaternion method + * 2. Iteratively refine rotation using exponential mapping + * 3. Compute axial vector corrections for rotation updates + * 4. Apply exponential mapping to update rotation matrix + * 5. Converge when correction magnitude falls below tolerance + * 6. Compute stretch tensors: U = R^T * F, V = F * R^T + * + * Applications in solid mechanics: + * - Large deformation analysis requiring objective stress measures + * - Crystal plasticity with finite rotations + * - Hyperelastic material models using stretch-based formulations + * - Kinematic analysis of deforming structures + * + * Reference: "A Robust Method to Extract the Rotational Part of Deformations" + * by Müller et al., MIG 2016 + * + * @note The input matrix R is modified in place and becomes the rotation output. + * @note The algorithm assumes the input represents a valid deformation gradient (det(F) > 0). + * @note Convergence is typically achieved in 5-15 iterations for typical FE problems. + * @note The method is more stable than traditional SVD-based approaches for ill-conditioned cases. + * + * Usage example: + * @code + * mfem::DenseMatrix F(3), R(3), U(3), V(3); + * // ... populate F with deformation gradient ... + * R = F; // Copy F since it will be modified + * CalcPolarDecompDefGrad(R, U, V); + * // Now R contains rotation, U and V contain right and left stretch + * @endcode + * + * @ingroup ExaConstit_utilities_strain + */ +inline void CalcPolarDecompDefGrad(mfem::DenseMatrix& R, + mfem::DenseMatrix& U, + mfem::DenseMatrix& V, + double err = 1e-12) { + mfem::DenseMatrix omega_mat, temp; + mfem::DenseMatrix def_grad(R, 3); + + constexpr int dim = 3; + mfem::Vector quat; + + constexpr int max_iter = 500; + + double norm, inv_norm; + + double ac1[3], ac2[3], ac3[3]; + double w_top[3], w[3]; + double w_bot, w_norm, w_norm_inv2, w_norm_inv; + double cth, sth; + double r1da1, r2da2, r3da3; + + quat.SetSize(4); + omega_mat.SetSize(dim); + temp.SetSize(dim); + + quat = 0.0; + + RMat2Quat(def_grad, quat); + + norm = quat.Norml2(); + + inv_norm = 1.0 / norm; + + quat *= inv_norm; + + Quat2RMat(quat, R); + + ac1[0] = def_grad(0, 0); + ac1[1] = def_grad(1, 0); + ac1[2] = def_grad(2, 0); + ac2[0] = def_grad(0, 1); + ac2[1] = def_grad(1, 1); + ac2[2] = def_grad(2, 1); + ac3[0] = def_grad(0, 2); + ac3[1] = def_grad(1, 2); + ac3[2] = def_grad(2, 2); + + for (int i = 0; i < max_iter; i++) { + // The dot products that show up in the paper + r1da1 = R(0, 0) * ac1[0] + R(1, 0) * ac1[1] + R(2, 0) * ac1[2]; + r2da2 = R(0, 1) * ac2[0] + R(1, 1) * ac2[1] + R(2, 1) * ac2[2]; + r3da3 = R(0, 2) * ac3[0] + R(1, 2) * ac3[1] + R(2, 2) * ac3[2]; + + // The summed cross products that show up in the paper + w_top[0] = (-R(2, 0) * ac1[1] + R(1, 0) * ac1[2]) + (-R(2, 1) * ac2[1] + R(1, 1) * ac2[2]) + + (-R(2, 2) * ac3[1] + R(1, 2) * ac3[2]); + + w_top[1] = (R(2, 0) * ac1[0] - R(0, 0) * ac1[2]) + (R(2, 1) * ac2[0] - R(0, 1) * ac2[2]) + + (R(2, 2) * ac3[0] - R(0, 2) * ac3[2]); + + w_top[2] = (-R(1, 0) * ac1[0] + R(0, 0) * ac1[1]) + (-R(1, 1) * ac2[0] + R(0, 1) * ac2[1]) + + (-R(1, 2) * ac3[0] + R(0, 2) * ac3[1]); + + w_bot = (1.0 / (std::abs(r1da1 + r2da2 + r3da3) + err)); + // The axial vector that shows up in the paper + w[0] = w_top[0] * w_bot; + w[1] = w_top[1] * w_bot; + w[2] = w_top[2] * w_bot; + // The norm of the axial vector + w_norm = std::sqrt(w[0] * w[0] + w[1] * w[1] + w[2] * w[2]); + // If the norm is below our desired error we've gotten our solution + // So we can break out of the loop + if (w_norm < err) { + break; + } + // The exponential mapping for an axial vector + // The 3x3 case has been explicitly unrolled here + w_norm_inv2 = 1.0 / (w_norm * w_norm); + w_norm_inv = 1.0 / w_norm; + + sth = std::sin(w_norm) * w_norm_inv; + cth = (1.0 - std::cos(w_norm)) * w_norm_inv2; + + omega_mat(0, 0) = 1.0 - cth * (w[2] * w[2] + w[1] * w[1]); + omega_mat(1, 1) = 1.0 - cth * (w[2] * w[2] + w[0] * w[0]); + omega_mat(2, 2) = 1.0 - cth * (w[1] * w[1] + w[0] * w[0]); + + omega_mat(0, 1) = -sth * w[2] + cth * w[1] * w[0]; + omega_mat(0, 2) = sth * w[1] + cth * w[2] * w[0]; + + omega_mat(1, 0) = sth * w[2] + cth * w[0] * w[1]; + omega_mat(1, 2) = -sth * w[0] + cth * w[2] * w[1]; + + omega_mat(2, 0) = -sth * w[1] + cth * w[0] * w[2]; + omega_mat(2, 1) = sth * w[0] + cth * w[2] * w[1]; + + Mult(omega_mat, R, temp); + R = temp; + } + + // Now that we have the rotation portion of our deformation gradient + // the left and right stretch tensors are easy to find. + MultAtB(R, def_grad, U); + MultABt(def_grad, R, V); +} + +/** + * @brief Calculate the Lagrangian strain tensor from deformation gradient. + * + * @param E Output Lagrangian strain tensor (3x3, symmetric) + * @param F Input deformation gradient tensor (3x3) + * + * This function computes the Lagrangian strain tensor (also known as Green-Lagrange strain) + * using the standard definition: + * + * E = (1/2)(C - I) = (1/2)(F^T F - I) + * + * where: + * - F is the deformation gradient tensor + * - C = F^T F is the right Cauchy-Green deformation tensor + * - I is the 3x3 identity tensor + * + * The Lagrangian strain tensor provides a material description of strain that: + * - Is objective (frame-invariant) under rigid body rotations + * - Vanishes for rigid body motion (E = 0 when F = R) + * - Is symmetric by construction + * - Measures strain relative to the reference configuration + * + * Mathematical properties: + * - E_ij = (1/2)(∂u_i/∂X_j + ∂u_j/∂X_i + ∂u_k/∂X_i ∂u_k/∂X_j) + * - For small deformations: E ≈ (1/2)(∇u + ∇u^T) (linearized strain) + * - Principal strains are eigenvalues of E + * - Compatible with hyperelastic constitutive models + * + * Applications in continuum mechanics: + * - Nonlinear elasticity and hyperelasticity + * - Large deformation finite element analysis + * - Material point method and other Lagrangian formulations + * - Constitutive model implementation for finite strains + * + * The computation is efficient and involves: + * 1. Computing C = F^T * F using optimized matrix multiplication + * 2. Scaling by 1/2 and subtracting identity from diagonal terms + * 3. Ensuring symmetry of the result + * + * @note The output strain tensor E is automatically symmetric. + * @note For infinitesimal strains, this reduces to the linearized strain tensor. + * @note The function assumes F represents a valid deformation gradient. + * + * @ingroup ExaConstit_utilities_strain + */ +inline void CalcLagrangianStrain(mfem::DenseMatrix& E, const mfem::DenseMatrix& F) { + constexpr int dim = 3; + + // DenseMatrix F(Jpt, dim); + mfem::DenseMatrix C(dim); + + constexpr double half = 0.5; + + MultAtB(F, F, C); + + E = 0.0; + + for (int j = 0; j < dim; j++) { + for (int i = 0; i < dim; i++) { + E(i, j) += half * C(i, j); + } + + E(j, j) -= half; + } +} + +/** + * @brief Calculate the Eulerian strain tensor from deformation gradient. + * + * @param e Output Eulerian strain tensor (3x3, symmetric) + * @param F Input deformation gradient tensor (3x3) + * + * This function computes the Eulerian strain tensor (also known as Almansi strain) + * using the standard definition: + * + * e = (1/2)(I - B^(-1)) = (1/2)(I - F^(-T) F^(-1)) + * + * where: + * - F is the deformation gradient tensor + * - B^(-1) = F^(-T) F^(-1) is the inverse left Cauchy-Green deformation tensor + * - I is the 3x3 identity tensor + * + * The Eulerian strain tensor provides a spatial description of strain that: + * - Describes strain in the current (deformed) configuration + * - Is objective under rigid body rotations + * - Vanishes for rigid body motion + * - Complements the Lagrangian strain description + * + * Mathematical characteristics: + * - Measures strain relative to the current configuration + * - For small deformations: e ≈ (1/2)(∇u + ∇u^T) (same as Lagrangian) + * - Related to velocity gradient in rate form + * - Useful for spatial constitutive formulations + * + * Computational procedure: + * 1. Compute F^(-1) using matrix inversion + * 2. Calculate B^(-1) = F^(-T) F^(-1) + * 3. Compute e = (1/2)(I - B^(-1)) + * + * Applications: + * - Eulerian finite element formulations + * - Fluid-structure interaction problems + * - Updated Lagrangian formulations + * - Spatial constitutive model implementations + * + * Numerical considerations: + * - Requires matrix inversion which may be expensive + * - Numerical stability depends on conditioning of F + * - More sensitive to numerical errors than Lagrangian strain + * + * @note The function requires F to be invertible (det(F) > 0). + * @note Matrix inversion is performed using MFEM's CalcInverse function. + * @note For nearly incompressible materials, use with appropriate precautions. + * + * @ingroup ExaConstit_utilities_strain + */ +inline void CalcEulerianStrain(mfem::DenseMatrix& e, const mfem::DenseMatrix& F) { + constexpr int dim = 3; + + mfem::DenseMatrix Finv(dim), Binv(dim); + + constexpr double half = 0.5; + + CalcInverse(F, Finv); + + MultAtB(Finv, Finv, Binv); + + e = 0.0; + + for (int j = 0; j < dim; j++) { + for (int i = 0; i < dim; i++) { + e(i, j) -= half * Binv(i, j); + } + + e(j, j) += half; + } +} + +/** + * @brief Calculate the Biot strain tensor from deformation gradient. + * + * @param E Output Biot strain tensor (3x3, symmetric) + * @param F Input deformation gradient tensor (3x3) + * + * This function computes the Biot strain tensor using the definition: + * + * E = U - I (or alternatively E = V - I when R = I) + * + * where: + * - U is the right stretch tensor from polar decomposition F = RU + * - V is the left stretch tensor from polar decomposition F = VR + * - I is the 3x3 identity tensor + * - R is the rotation tensor + * + * The Biot strain tensor provides an intuitive measure of pure stretch: + * - Directly measures stretch ratios in principal directions + * - Vanishes for rigid body motion (E = 0 when U = I) + * - Symmetric by construction (since U and V are symmetric) + * - Physically represents "engineering strain" for principal directions + * + * Key properties: + * - E_ii = λ_i - 1 where λ_i are principal stretches + * - For small deformations: E ≈ linearized strain tensor + * - Simple interpretation: E_ii is the fractional change in length + * - Compatible with logarithmic strain for hyperelastic models + * + * Computational approach: + * 1. Perform polar decomposition F = RU to extract U + * 2. Compute E = U - I by subtracting identity + * 3. Result is automatically symmetric + * + * Applications in material modeling: + * - Hyperelastic constitutive relations + * - Crystal plasticity where stretch is separated from rotation + * - Biomechanics applications requiring intuitive strain measures + * - Damage mechanics based on principal stretches + * + * Advantages over other strain measures: + * - Direct physical interpretation as stretch ratios + * - Computationally efficient (single polar decomposition) + * - Natural for anisotropic material models + * - Separates pure deformation from rotation effects + * + * @note This function internally calls CalcPolarDecompDefGrad. + * @note The computation is more expensive than simple strain measures due to polar decomposition. + * @note For small deformations, Biot strain converges to linearized strain. + * + * @ingroup ExaConstit_utilities_strain + */ +inline void CalcBiotStrain(mfem::DenseMatrix& E, const mfem::DenseMatrix& F) { + constexpr int dim = 3; + + mfem::DenseMatrix rmat(F, dim); + mfem::DenseMatrix umat, vmat; + + umat.SetSize(dim); + vmat.SetSize(dim); + + CalcPolarDecompDefGrad(rmat, umat, vmat); + + E = umat; + E(0, 0) -= 1.0; + E(1, 1) -= 1.0; + E(2, 2) -= 1.0; +} + +/** + * @brief Calculate the logarithmic strain tensor (Hencky strain) from deformation gradient. + * + * @param E Output logarithmic strain tensor (3x3, symmetric) + * @param F Input deformation gradient tensor (3x3) + * + * This function computes the logarithmic strain tensor (also known as Hencky strain + * or true strain) using the spectral decomposition approach: + * + * E = ln(V) = (1/2) ln(B) = (1/2) ln(FF^T) + * + * where: + * - F is the deformation gradient tensor + * - V is the left stretch tensor from polar decomposition F = VR + * - B = FF^T is the left Cauchy-Green deformation tensor + * - ln denotes the matrix logarithm + * + * The logarithmic strain tensor is considered the most natural finite strain measure: + * - Objective under rigid body rotations + * - Additive for successive deformations + * - Vanishes for rigid body motion + * - Principal values are ln(λ_i) where λ_i are principal stretches + * + * Mathematical advantages: + * - E_ii = ln(λ_i) represents true strain in principal directions + * - For small deformations: E ≈ linearized strain tensor + * - Compatible with multiplicative decomposition in plasticity + * - Natural measure for hyperelastic models + * + * Computational procedure: + * 1. Compute B = F F^T (left Cauchy-Green tensor) + * 2. Calculate eigenvalue decomposition of B: B = Q Λ Q^T + * 3. Compute matrix logarithm: ln(B) = Q ln(Λ) Q^T + * 4. Scale by 1/2: E = (1/2) ln(B) + * + * The spectral decomposition enables efficient computation: + * - Eigenvalues λ_i of B are squares of principal stretches + * - ln(B) is computed as ln(λ_i) applied to eigenvalues + * - Eigenvectors provide principal directions + * + * Applications in nonlinear mechanics: + * - Hyperelastic material models (Neo-Hookean, Mooney-Rivlin) + * - Crystal plasticity with finite deformations + * - Multiplicative plasticity decomposition + * - Biomechanics and soft tissue modeling + * + * Performance characteristics: + * - More expensive than simple strain measures (eigenvalue decomposition) + * - Numerically stable for typical finite element applications + * - Handles large deformations robustly + * - Compatible with GPU acceleration via MFEM's eigen solver + * + * @note The function uses MFEM's CalcEigenvalues for spectral decomposition. + * @note Requires positive definite deformation gradient (det(F) > 0). + * @note For nearly incompressible materials, eigenvalue computation is well-conditioned. + * + * @ingroup ExaConstit_utilities_strain + */ +inline void CalcLogStrain(mfem::DenseMatrix& E, const mfem::DenseMatrix& F) { + // calculate current end step logorithmic strain (Hencky Strain) + // which is taken to be E = ln(U) = 1/2 ln(C), where C = (F_T)F. + // We have incremental F from MFEM, and store F0 (Jpt0) so + // F = F_hat*F0. With F, use a spectral decomposition on C to obtain a + // form where we only have to take the natural log of the + // eigenvalues + // UMAT uses the E = ln(V) approach instead + + mfem::DenseMatrix B; + + constexpr int dim = 3; + + B.SetSize(dim); + MultABt(F, F, B); + + // compute eigenvalue decomposition of B + double lambda[dim]; + double vec[dim * dim]; + B.CalcEigenvalues(&lambda[0], &vec[0]); + + // compute ln(V) using spectral representation + E = 0.0; + for (int i = 0; i < dim; ++i) { // outer loop for every eigenvalue/vector + for (int j = 0; j < dim; ++j) { // inner loops for diadic product of eigenvectors + for (int k = 0; k < dim; ++k) { + // Dense matrices are col. maj. representation, so the indices were + // reversed for it to be more cache friendly. + E(k, j) += 0.5 * log(lambda[i]) * vec[i * dim + j] * vec[i * dim + k]; + } + } + } +} \ No newline at end of file diff --git a/src/utilities/unified_logger.cpp b/src/utilities/unified_logger.cpp new file mode 100644 index 0000000..68f94ff --- /dev/null +++ b/src/utilities/unified_logger.cpp @@ -0,0 +1,681 @@ +#include "utilities/unified_logger.hpp" + +#include "postprocessing/postprocessing_file_manager.hpp" + +#include // For std::put_time + +#include // For select() - monitoring file descriptors + +namespace exaconstit { + +// ============================================================================ +// STATIC MEMBER INITIALIZATION +// ============================================================================ +// These must be defined in exactly one translation unit +std::unique_ptr UnifiedLogger::instance = nullptr; +std::mutex UnifiedLogger::instance_mutex; + +// ============================================================================ +// SINGLETON ACCESS +// ============================================================================ +UnifiedLogger& UnifiedLogger::get_instance() { + // Double-checked locking pattern for thread-safe lazy initialization + // First check without lock (fast path) + if (!instance) { + // Acquire lock and check again (slow path) + std::lock_guard lock(instance_mutex); + if (!instance) { + // Create instance - using private constructor via friendship + instance.reset(new UnifiedLogger()); + } + } + return *instance; +} + +// ============================================================================ +// INITIALIZATION +// ============================================================================ +void UnifiedLogger::initialize(const ExaOptions& options) { + PostProcessingFileManager file_manager(options); + // Step 1: Set up log directory path + // Use file manager's structure if available for consistency + fs::path base_dir = fs::weakly_canonical(file_manager.GetOutputDirectory()); + log_directory = base_dir / "logs"; + // Step 2: Create directory structure (handling symlinks properly) + if (mpi_rank == 0) { + std::error_code ec; + + // Check if the path exists after resolving symlinks + if (!fs::exists(log_directory)) { + fs::create_directories(log_directory, ec); + if (ec) { + std::cerr << "Warning: Failed to create log directory: " << ec.message() + << std::endl; + } + } else if (!fs::is_directory(log_directory)) { + std::cerr << "Error: Log path exists but is not a directory: " << log_directory + << std::endl; + } + } + + // Synchronize all ranks before proceeding + MPI_Barrier(MPI_COMM_WORLD); + + // Step 3: Set up main log file path + main_log_filename = log_directory / (options.basename + "_simulation.log"); + + // Step 4: Open main log file + // All ranks append to same file (OS handles concurrent writes) + main_log_file = std::make_unique(main_log_filename, std::ios::app); + + if (!main_log_file->is_open()) { + std::cerr << "Warning: Failed to open main log file: " << main_log_filename << std::endl; + return; + } + + // Step 5: Enable tee functionality + // From this point, all cout/cerr goes to both terminal and file + enable_main_logging(); + + // Step 6: Write header (rank 0 only to avoid duplication) + if (mpi_rank == 0) { + std::cout << "\n=== ExaConstit Simulation: " << options.basename << " ===" << std::endl; + std::cout << "MPI Ranks: " << mpi_size << std::endl; + std::cout << "Log Directory: " << log_directory << std::endl; + std::cout << "Main Log: " << main_log_filename.filename() << std::endl; + + // Add timestamp using C++17 time formatting + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + std::cout << "Start Time: " << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S") + << std::endl; + std::cout << "==========================================\n" << std::endl; + } +} + +// ============================================================================ +// TEE MODE - MAIN LOGGING +// ============================================================================ +void UnifiedLogger::enable_main_logging() { + if (!main_log_file || !main_log_file->is_open()) { + return; + } + + // Set up cout tee + if (!cout_guard) { + // Get cout's current streambuf (this is what writes to terminal) + std::streambuf* original_buf = std::cout.rdbuf(); + // Create guard to restore it later + cout_guard.emplace(std::cout); + // Create tee that writes to BOTH original buffer AND file + cout_tee = std::make_unique(original_buf, main_log_file.get()); + // Now replace cout's buffer with our tee + std::cout.rdbuf(cout_tee.get()); + } + + // Set up cerr tee + if (!cerr_guard) { + std::streambuf* original_buf = std::cerr.rdbuf(); + cerr_guard.emplace(std::cerr); + cerr_tee = std::make_unique(original_buf, main_log_file.get()); + std::cerr.rdbuf(cerr_tee.get()); + } + + // Set up mfem::out tee + if (!mfem_out_guard) { + // MFEM's out stream might be using cout's buffer or its own + std::streambuf* original_buf = mfem::out.rdbuf(); + if (original_buf) { // mfem::out might be disabled + mfem_out_guard.emplace(mfem::out); + mfem_out_tee = std::make_unique(original_buf, main_log_file.get()); + mfem::out.rdbuf(mfem_out_tee.get()); + } + } + + // Set up mfem::err tee + if (!mfem_err_guard) { + std::streambuf* original_buf = mfem::err.rdbuf(); + if (original_buf) { // mfem::err might be disabled + mfem_err_guard.emplace(mfem::err); + mfem_err_tee = std::make_unique(original_buf, main_log_file.get()); + mfem::err.rdbuf(mfem_err_tee.get()); + } + } +} + +void UnifiedLogger::disable_main_logging() { + // RAII guards automatically restore original buffers when reset + cout_guard.reset(); // Restores original cout buffer + cerr_guard.reset(); // Restores original cerr buffer + mfem_out_guard.reset(); + mfem_err_guard.reset(); + + // Clean up tee buffers + cout_tee.reset(); + cerr_tee.reset(); + mfem_out_tee.reset(); + mfem_err_tee.reset(); +} + +// ============================================================================ +// CAPTURE MODE - READER THREAD +// ============================================================================ +void UnifiedLogger::reader_thread_func(CaptureContext* ctx) { + // This function runs in a separate thread during capture + // Its job: read from pipe and accumulate in stringstream + + constexpr size_t BUFFER_SIZE = 65536; // 64KB buffer + std::vector buffer(BUFFER_SIZE); + + // Main reading loop + while (!ctx->stop_reading.load()) { + // Use select() for efficient I/O monitoring + // select() tells us when data is available without blocking + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(ctx->capture_pipe.read_end().get(), &read_fds); + + // Set timeout so we periodically check stop_reading + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 10000; // 10ms - more responsive than 50ms + + // Wait for data or timeout + int ret = select( + ctx->capture_pipe.read_end().get() + 1, &read_fds, nullptr, nullptr, &timeout); + + if (ret > 0 && FD_ISSET(ctx->capture_pipe.read_end().get(), &read_fds)) { + // Data is available - read as much as possible + while (true) { + ssize_t bytes_read = ::read( + ctx->capture_pipe.read_end().get(), buffer.data(), buffer.size() - 1); + + if (bytes_read > 0) { + // Successfully read data + buffer[static_cast(bytes_read)] = '\0'; // Null terminate + ctx->captured_output << buffer.data(); + ctx->has_content = true; + ctx->bytes_captured += static_cast(bytes_read); + } else if (bytes_read == 0) { + // EOF - pipe closed + break; + } else { + // Error or would block + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // No more data available right now + break; + } + // Real error - stop reading + break; + } + } + } + // If select times out, loop continues to check stop_reading + } + + // Final flush - read any remaining data + while (true) { + ssize_t bytes_read = ::read( + ctx->capture_pipe.read_end().get(), buffer.data(), buffer.size() - 1); + if (bytes_read <= 0) + break; + + buffer[static_cast(bytes_read)] = '\0'; + ctx->captured_output << buffer.data(); + ctx->has_content = true; + ctx->bytes_captured += static_cast(bytes_read); + } +} + +// ============================================================================ +// GPU OUTPUT SYNCHRONIZATION +// ============================================================================ +void UnifiedLogger::flush_gpu_output() { + // GPU printf uses device-side buffers that must be explicitly flushed + +#ifdef RAJA_ENABLE_CUDA + // CUDA: Force device synchronization + cudaError_t err = cudaDeviceSynchronize(); + if (err != cudaSuccess) { + // Don't print error - we might be capturing output + // Store error info in capture context if needed + } +#elif defined(RAJA_ENABLE_HIP) + // HIP: Force device synchronization + hipError_t err = hipDeviceSynchronize(); + if (err != hipSuccess) { + // Don't print error - we might be capturing output + } +#endif + + // Additional delay to ensure driver flushes printf buffers + // This is unfortunately necessary for reliable GPU printf capture + usleep(5000); // 5ms +} + +// ============================================================================ +// BEGIN CAPTURE +// ============================================================================ +void UnifiedLogger::begin_capture(const std::string& filename, bool suppress_non_zero_ranks) { + std::lock_guard lock(capture_mutex); + + // CRITICAL: Flush all C++ streams before redirecting + std::cout.flush(); + std::cerr.flush(); + mfem::out.flush(); + mfem::err.flush(); + + // Also flush C stdio buffers + fflush(stdout); + fflush(stderr); + + // Temporarily disable tee functionality during capture + if (!in_capture_mode && (cout_tee || cerr_tee || mfem_out_tee || mfem_err_tee)) { + in_capture_mode = true; + + // Use RAII guards to temporarily restore original buffers + if (cout_guard && cout_tee) { + temp_cout_guard.emplace(std::cout); + temp_cout_guard->set_buffer(cout_guard->get_original()); + } + if (cerr_guard && cerr_tee) { + temp_cerr_guard.emplace(std::cerr); + temp_cerr_guard->set_buffer(cerr_guard->get_original()); + } + if (mfem_out_guard && mfem_out_tee) { + temp_mfem_out_guard.emplace(mfem::out); + temp_mfem_out_guard->set_buffer(mfem_out_guard->get_original()); + } + if (mfem_err_guard && mfem_err_tee) { + temp_mfem_err_guard.emplace(mfem::err); + temp_mfem_err_guard->set_buffer(mfem_err_guard->get_original()); + } + } + + // Create new capture context + auto ctx = std::make_unique(); + ctx->output_filename = log_directory / filename; + ctx->suppress_non_zero_ranks = suppress_non_zero_ranks; + ctx->start_time = std::chrono::steady_clock::now(); + + // Special handling for rank suppression + if (suppress_non_zero_ranks && mpi_rank != 0) { + // For non-zero ranks, redirect both file descriptors AND C++ streams + + // Open /dev/null for file descriptors + FileDescriptor devnull(::open("/dev/null", O_WRONLY)); + if (!devnull) { + throw std::system_error(errno, std::system_category(), "Failed to open /dev/null"); + } + + // Redirect file descriptors + ctx->stdout_dup.emplace(STDOUT_FILENO); + ctx->stderr_dup.emplace(STDERR_FILENO); + ctx->stdout_dup->redirect_to(devnull.get()); + ctx->stderr_dup->redirect_to(devnull.get()); + + // Also redirect C++ streams to null stream using RAII + ctx->null_stream = std::make_unique("/dev/null"); + if (ctx->null_stream->is_open()) { + ctx->cout_null_guard.emplace(std::cout); + ctx->cout_null_guard->set_buffer(ctx->null_stream->rdbuf()); + + ctx->cerr_null_guard.emplace(std::cerr); + ctx->cerr_null_guard->set_buffer(ctx->null_stream->rdbuf()); + + ctx->mfem_out_null_guard.emplace(mfem::out); + ctx->mfem_out_null_guard->set_buffer(ctx->null_stream->rdbuf()); + + ctx->mfem_err_null_guard.emplace(mfem::err); + ctx->mfem_err_null_guard->set_buffer(ctx->null_stream->rdbuf()); + } + + capture_stack.push(std::move(ctx)); + return; + } + + // Normal capture setup (rest of the existing code) + try { + // Create pipe with non-blocking read + ctx->capture_pipe.set_non_blocking(true, false); + + // Save and redirect file descriptors + ctx->stdout_dup.emplace(STDOUT_FILENO); + ctx->stderr_dup.emplace(STDERR_FILENO); + ctx->stdout_dup->redirect_to(ctx->capture_pipe.write_end().get()); + ctx->stderr_dup->redirect_to(ctx->capture_pipe.write_end().get()); + + // Start reader thread + ctx->reader_thread = std::thread(&UnifiedLogger::reader_thread_func, this, ctx.get()); + + // Push onto stack + capture_stack.push(std::move(ctx)); + + } catch (const std::exception& e) { + // RAII will automatically restore everything + throw std::runtime_error(std::string("Failed to begin capture: ") + e.what()); + } +} + +// ============================================================================ +// END CAPTURE +// ============================================================================ +std::string UnifiedLogger::end_capture() { + std::lock_guard lock(capture_mutex); + + if (capture_stack.empty()) { + return ""; + } + + // Get current capture context + auto ctx = std::move(capture_stack.top()); + capture_stack.pop(); + + // Handle suppressed ranks + if (ctx->suppress_non_zero_ranks && mpi_rank != 0) { + // RAII automatically restores everything when ctx goes out of scope + + // Re-enable tee if this was the last capture + if (capture_stack.empty() && in_capture_mode) { + restore_tee_after_capture(); + } + + return ""; + } + + // Normal capture end (existing flush code) + flush_gpu_output(); + std::cout.flush(); + std::cerr.flush(); + mfem::out.flush(); + mfem::err.flush(); + fflush(stdout); + fflush(stderr); + + // RAII automatically restores stdout/stderr + ctx->stdout_dup.reset(); + ctx->stderr_dup.reset(); + + // Close write end and stop reader + ctx->capture_pipe.write_end().close(); + ctx->stop_reading.store(true); + if (ctx->reader_thread.joinable()) { + ctx->reader_thread.join(); + } + + // Re-enable tee if this was the last capture + if (capture_stack.empty() && in_capture_mode) { + restore_tee_after_capture(); + } + + // Step 5: Check capture duration for warnings + auto duration = std::chrono::steady_clock::now() - ctx->start_time; + if (duration > std::chrono::seconds(5)) { + std::cerr << "[Logger] Long capture duration: " + << std::chrono::duration_cast(duration).count() + << " seconds for " << ctx->output_filename.filename() << std::endl; + } + + // Step 6: Process captured content + std::string content = ctx->captured_output.str(); + + // Use string_view for efficient trimming (no copies) + std::string_view sv(content); + + // Remove leading whitespace + size_t start = sv.find_first_not_of(" \t\n\r"); + if (start == std::string_view::npos) { + // All whitespace - no content + stats_.total_captures++; + return ""; + } + sv.remove_prefix(start); + + // Remove trailing whitespace + size_t end = sv.find_last_not_of(" \t\n\r"); + if (end != std::string_view::npos) { + sv = sv.substr(0, end + 1); + } + + // Update statistics + stats_.total_captures++; + + // Step 7: Write file ONLY if content exists + if (!sv.empty() && ctx->has_content) { + // Resolve symlinks in the output path + fs::path output_path = fs::weakly_canonical(ctx->output_filename); + fs::path output_dir = output_path.parent_path(); + + // Ensure output directory exists (with symlink handling) + std::error_code ec; + if (!fs::exists(output_dir)) { + fs::create_directories(output_dir, ec); + if (ec) { + std::cerr << "Warning: Failed to create output directory: " << output_dir << ": " + << ec.message() << std::endl; + } + } + + // Open output file using resolved path + std::ofstream out_file(output_path, std::ios::app); + if (out_file.is_open()) { + // Write header with metadata + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + + out_file << "\n=== Capture Session ===" << std::endl; + out_file << "Timestamp: " << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S") + << std::endl; + out_file << "MPI Rank: " << mpi_rank << std::endl; + out_file << "Duration: " + << std::chrono::duration_cast(duration).count() + << " ms" << std::endl; + out_file << "Bytes Captured: " << ctx->bytes_captured << std::endl; + out_file << "------------------------" << std::endl; + out_file << sv; // Write actual content + out_file << "\n========================\n" << std::endl; + + // Update statistics + stats_.files_created++; + stats_.created_files.push_back(ctx->output_filename.string()); + + // Log to main that we created a file + if (main_log_file && main_log_file->is_open()) { + if (debugging_logging) { + *main_log_file + << "[Logger] Created output file: " << ctx->output_filename.filename() + << " (" << ctx->bytes_captured << " bytes)" << std::endl; + } + } + + return ctx->output_filename.string(); + } + } + + // No file created + return ""; +} + +void UnifiedLogger::restore_tee_after_capture() { + if (!in_capture_mode) + return; + + // Simply reset the temporary guards - RAII handles restoration + temp_cout_guard.reset(); + temp_cerr_guard.reset(); + temp_mfem_out_guard.reset(); + temp_mfem_err_guard.reset(); + + in_capture_mode = false; +} + +// ============================================================================ +// UTILITY METHODS +// ============================================================================ +std::string UnifiedLogger::get_material_log_filename(const std::string& model_type, + int region_id, + const std::string& context) { + std::stringstream ss; + ss << "material_" << model_type << "_region_" << region_id; + + if (!context.empty()) { + ss << "_" << context; + } + + ss << "_rank_" << mpi_rank << ".log"; + + return ss.str(); +} + +void UnifiedLogger::print_capture_statistics() { + // Gather statistics from all ranks + int local_captures = stats_.total_captures; + int total_captures = 0; + + // Use MPI to gather totals + MPI_Reduce(&local_captures, &total_captures, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); + + // Only rank 0 prints and scans directory + if (mpi_rank == 0) { + std::cout << "\n=== Material Output Summary ===" << std::endl; + std::cout << "Total capture sessions: " << total_captures << std::endl; + + // Scan the log directory for all material files + try { + std::map> files_by_type; + int actual_file_count = 0; + + // Look for all files matching material log patterns + for (const auto& entry : std::filesystem::directory_iterator(log_directory)) { + if (!entry.is_regular_file()) + continue; + + std::string filename = entry.path().filename().string(); + + // Skip the main simulation log + if (filename == main_log_filename.filename().string()) + continue; + + // Check if it matches material log pattern + if (filename.find("material_") == 0 && filename.find(".log") != std::string::npos) { + actual_file_count++; + + // Extract model type from filename + std::string type = "other"; + if (filename.find("ExaCMech") != std::string::npos || + filename.find("exacmech") != std::string::npos) { + type = "ExaCMech"; + } else if (filename.find("UMAT") != std::string::npos || + filename.find("umat") != std::string::npos) { + type = "UMAT"; + } + files_by_type[type].push_back(entry.path()); + } else if (filename.find("mfem") == 0 && + filename.find(".log") != std::string::npos) { + actual_file_count++; + files_by_type["mfem"].push_back(entry.path()); + } + } + + std::cout << "Files found in directory scan: " << actual_file_count << std::endl; + + if (actual_file_count > 0) { + std::cout << "\nFiles with captured output:" << std::endl; + + // Print grouped files + for (const auto& [type, files] : files_by_type) { + std::cout << "\n " << type << " outputs (" << files.size() + << " files):" << std::endl; + + // Group by region for cleaner output + std::map> by_region; + for (const auto& file : files) { + std::string fname = file.filename().string(); + // Extract region number + size_t region_pos = fname.find("region_"); + if (region_pos != std::string::npos) { + int region = std::stoi(fname.substr(region_pos + 7)); + by_region[region].push_back(file); + } else { + by_region[-1].push_back(file); // Unknown region + } + } + + // Print by region + for (const auto& [region, region_files] : by_region) { + if (region >= 0) { + std::cout << " Region " << region << ":" << std::endl; + for (const auto& file : region_files) { + // Get file size for additional info + auto size = std::filesystem::file_size(file); + std::cout << " - " << file.filename().string() << " (" << size + << " bytes)" << std::endl; + } + } + } + + // Print files without clear region + if (by_region.count(-1) > 0) { + std::cout << " MFEM:" << std::endl; + for (const auto& file : by_region[-1]) { + auto size = std::filesystem::file_size(file); + std::cout << " - " << file.filename().string() << " (" << size + << " bytes)" << std::endl; + } + } + } + } + + } catch (const std::filesystem::filesystem_error& e) { + std::cerr << "Warning: Could not scan log directory: " << e.what() << std::endl; + } + std::cout << std::endl; + } +} + +// ============================================================================ +// SHUTDOWN +// ============================================================================ +void UnifiedLogger::shutdown() { + // Step 1: End any active captures + while (!capture_stack.empty()) { + end_capture(); + } + + // Step 2: Print statistics + print_capture_statistics(); + + // Step 3: Write footer (rank 0 only) + if (mpi_rank == 0) { + auto now = std::chrono::system_clock::now(); + auto time_t = std::chrono::system_clock::to_time_t(now); + + std::cout << "\n=== Simulation Complete ===" << std::endl; + std::cout << "End Time: " << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S") + << std::endl; + std::cout << "Log files saved in: " << log_directory << std::endl; + } + + // Step 4: Disable tee functionality + disable_main_logging(); + + // Step 5: Close main log file + if (main_log_file) { + main_log_file->close(); + } +} + +// ============================================================================ +// SCOPED CAPTURE IMPLEMENTATION +// ============================================================================ +UnifiedLogger::ScopedCapture::ScopedCapture(const std::string& filename, bool suppress_non_zero) + : logger(UnifiedLogger::get_instance()) { + logger.begin_capture(filename, suppress_non_zero); +} + +UnifiedLogger::ScopedCapture::~ScopedCapture() { + // Save any created filename before end_capture + filename_created = logger.end_capture(); +} + +} // namespace exaconstit \ No newline at end of file diff --git a/src/utilities/unified_logger.hpp b/src/utilities/unified_logger.hpp new file mode 100644 index 0000000..24c010c --- /dev/null +++ b/src/utilities/unified_logger.hpp @@ -0,0 +1,754 @@ +#pragma once + +#include "mfem.hpp" + +#include +#include +#include +#include // For open(), pipe(), dup2() - POSIX file operations +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // For read(), write(), close() - POSIX I/O + +#ifdef RAJA_ENABLE_CUDA +#include +#elif defined(RAJA_ENABLE_HIP) +#include +#endif + +class PostProcessingFileManager; +class ExaOptions; + +namespace exaconstit { + +// ============================================================================ +// RAII WRAPPERS FOR SAFER POSIX OPERATIONS +// ============================================================================ + +/** + * @brief RAII wrapper for file descriptors + * + * @details Provides automatic cleanup of file descriptors and prevents + * resource leaks. File descriptors are move-only (can't be copied) to + * maintain single ownership semantics. + * + * Why we need this: + * - Raw file descriptors (int) don't clean themselves up + * - Easy to forget close() calls, especially with exceptions + * - Prevents accidental copying of file descriptors + * + * Usage: + * ```cpp + * FileDescriptor fd(open("file.txt", O_RDONLY)); + * if (!fd) throw std::runtime_error("Failed to open"); + * // fd automatically closed when out of scope + * ``` + */ +class FileDescriptor { +private: + int fd; // The underlying POSIX file descriptor (-1 = invalid) + +public: + // Default constructor creates invalid descriptor + FileDescriptor() : fd(-1) {} + + // Wrap an existing file descriptor + explicit FileDescriptor(int fd_) : fd(fd_) {} + + // Move constructor - transfers ownership + FileDescriptor(FileDescriptor&& other) noexcept : fd(other.fd) { + other.fd = -1; // Other no longer owns the descriptor + } + + // Move assignment - close current and transfer ownership + FileDescriptor& operator=(FileDescriptor&& other) noexcept { + if (this != &other) { + close(); // Close our current descriptor if valid + fd = other.fd; + other.fd = -1; + } + return *this; + } + + // Prevent copying - file descriptors should have single owner + FileDescriptor(const FileDescriptor&) = delete; + FileDescriptor& operator=(const FileDescriptor&) = delete; + + // Destructor ensures descriptor is closed + ~FileDescriptor() { + close(); + } + + // Explicitly close the descriptor + void close() { + if (fd >= 0) { + ::close(fd); // :: means global namespace (POSIX close) + fd = -1; + } + } + + // Getters + int get() const { + return fd; + } + bool is_valid() const { + return fd >= 0; + } + + // Release ownership without closing + int release() { + int temp = fd; + fd = -1; + return temp; + } + + // Convenience operators + operator int() const { + return fd; + } // Allow implicit conversion to int + explicit operator bool() const { + return is_valid(); + } // if (fd) {...} +}; + +/** + * @brief RAII wrapper for pipe creation + * + * @details Creates a pipe and manages both ends with automatic cleanup. + * A pipe is a unidirectional communication channel: + * - Data written to write_end can be read from read_end + * - Used for capturing stdout/stderr by redirecting them to write_end + * + * Why we need pipes: + * - C++ streams only capture C++ iostream output + * - Pipes capture ALL output: printf, fprintf, Fortran WRITE, etc. + * - Works at the OS level, not language level + */ +class Pipe { +private: + FileDescriptor read_end_var; // Where we read captured data from + FileDescriptor write_end_var; // Where stdout/stderr write to + +public: + // Constructor creates the pipe + Pipe() { + int pipe_fds[2]; + if (::pipe(pipe_fds) == -1) { + // Convert errno to C++ exception with descriptive message + throw std::system_error(errno, std::system_category(), "Failed to create pipe"); + } + // pipe() fills array: [0] = read end, [1] = write end + read_end_var = FileDescriptor(pipe_fds[0]); + write_end_var = FileDescriptor(pipe_fds[1]); + } + + // Access to pipe ends + FileDescriptor& read_end() { + return read_end_var; + } + FileDescriptor& write_end() { + return write_end_var; + } + const FileDescriptor& read_end() const { + return read_end_var; + } + const FileDescriptor& write_end() const { + return write_end_var; + } + + /** + * @brief Make pipe non-blocking for efficient reading + * + * @param read Make read end non-blocking + * @param write Make write end non-blocking + * + * @details Non-blocking is important for read end so our reader + * thread doesn't hang waiting for data. It can check for data + * and do other work if none is available. + */ + void set_non_blocking(bool read = true, bool write = false) { + if (read && read_end_var) { + int flags = fcntl(read_end_var.get(), F_GETFL, 0); + fcntl(read_end_var.get(), F_SETFL, flags | O_NONBLOCK); + } + if (write && write_end_var) { + int flags = fcntl(write_end_var.get(), F_GETFL, 0); + fcntl(write_end_var.get(), F_SETFL, flags | O_NONBLOCK); + } + } +}; + +/** + * @brief RAII wrapper for saving and restoring file descriptors + * + * @details When we redirect stdout/stderr, we need to: + * 1. Save the current descriptor so we can restore it later + * 2. Redirect to our pipe + * 3. Later restore the original descriptor + * + * This class handles that pattern safely with automatic restoration. + */ +class FileDescriptorDuplicator { +private: + FileDescriptor saved_fd; // Copy of original descriptor + int target_fd; // Which descriptor we're managing (1=stdout, 2=stderr) + +public: + // Constructor saves a copy of the current descriptor + FileDescriptorDuplicator(int fd_to_save) : saved_fd(::dup(fd_to_save)), target_fd(fd_to_save) { + if (!saved_fd) { + throw std::system_error( + errno, std::system_category(), "Failed to duplicate file descriptor"); + } + } + + // Destructor automatically restores original + ~FileDescriptorDuplicator() { + restore(); + } + + // Manually restore original descriptor + void restore() { + if (saved_fd) { + ::dup2(saved_fd.get(), target_fd); // Restore original + saved_fd.close(); // Close our saved copy + } + } + + // Redirect target to a new descriptor + void redirect_to(int new_fd) { + ::dup2(new_fd, target_fd); // target_fd now points to new_fd + } + + // Prevent copying + FileDescriptorDuplicator(const FileDescriptorDuplicator&) = delete; + FileDescriptorDuplicator& operator=(const FileDescriptorDuplicator&) = delete; +}; + +/** + * @brief RAII wrapper for C++ stream buffer management + * + * @details C++ streams (cout/cerr) use streambuf objects for actual I/O. + * By replacing the streambuf, we can redirect where the stream writes. + * This class safely saves and restores the original streambuf. + * + * Used for tee functionality where we want cout to write to both + * terminal and log file. + */ +class StreamBufferGuard { +private: + std::ostream* stream; // The stream we're managing (cout or cerr) + std::streambuf* original_buffer; // Original buffer to restore + bool active; // Whether we still need to restore + +public: + StreamBufferGuard(std::ostream& stream_) + : stream(&stream_), original_buffer(stream_.rdbuf()), // Save current buffer + active(true) {} + + ~StreamBufferGuard() { + restore(); // Ensure buffer is restored + } + + // Replace stream's buffer with a new one + void set_buffer(std::streambuf* new_buffer) { + if (active && stream) { + stream->rdbuf(new_buffer); + } + } + + // Restore original buffer + void restore() { + if (active && stream && original_buffer) { + stream->rdbuf(original_buffer); + active = false; // Don't restore twice + } + } + + std::streambuf* get_original() const { + return original_buffer; + } + + // Move-only semantics + StreamBufferGuard(StreamBufferGuard&& other) noexcept + : stream(other.stream), original_buffer(other.original_buffer), active(other.active) { + other.active = false; // Other shouldn't restore + } + + // No copy + StreamBufferGuard(const StreamBufferGuard&) = delete; + StreamBufferGuard& operator=(const StreamBufferGuard&) = delete; +}; + +// ============================================================================ +// UNIFIED LOGGER CLASS +// ============================================================================ + +/** + * @brief Unified logging system for ExaConstit + * + * @details Provides two main modes of operation: + * + * 1. **Tee Mode (Main Logging)**: + * - Active throughout simulation + * - All stdout/stderr goes to BOTH terminal AND log file + * - Uses custom streambuf to duplicate output + * + * 2. **Capture Mode (Material Logging)**: + * - Temporarily activated for specific code sections + * - All stdout/stderr goes ONLY to a specific file + * - Terminal sees nothing during capture + * - Files only created if content is actually captured + * + * Architecture: + * - Singleton pattern ensures single instance + * - RAII wrappers ensure exception safety + * - Thread-safe for parallel MPI execution + * - Captures all output types (C++, C, Fortran, GPU) + */ +class UnifiedLogger { +private: + // ======================================================================== + // SINGLETON PATTERN + // ======================================================================== + static std::unique_ptr instance; + static std::mutex instance_mutex; + + // ======================================================================== + // MPI INFORMATION + // ======================================================================== + int mpi_rank; // This process's rank + int mpi_size; // Total number of processes + bool debugging_logging = false; + + // ======================================================================== + // MAIN LOG FILE MANAGEMENT + // ======================================================================== + std::filesystem::path log_directory; // Where all logs are stored + std::filesystem::path main_log_filename; // Main simulation log + std::unique_ptr main_log_file; // File stream for main log + + // ======================================================================== + // TEE STREAMBUF FOR DUAL OUTPUT + // ======================================================================== + /** + * @brief Custom stream buffer that writes to two destinations + * + * @details Inherits from std::streambuf to intercept stream operations. + * When data is written to cout/cerr, this buffer writes it to both + * the terminal and a log file simultaneously. + * + * How it works: + * - overflow(): Called for single character output + * - xsputn(): Called for string output (more efficient) + * - Both methods write to terminal AND file + * - Thread-safe via mutex + */ + class TeeStreambuf : public std::streambuf { + protected: + std::streambuf* original_buf; // The ORIGINAL buffer (terminal) + std::ostream* file_stream; // The log file stream + std::mutex mutex_lock; + + protected: + virtual int overflow(int c) override { + int result = c; + if (c != EOF) { + char cchar = static_cast(c); + std::lock_guard lock(mutex_lock); + + // Write to original buffer first + if (original_buf && original_buf->sputc(cchar) == EOF) { + result = EOF; + } + + // Then write to file + if (file_stream) { + file_stream->put(cchar); + } + } + return result; + } + + virtual std::streamsize xsputn(const char* s, std::streamsize n) override { + std::lock_guard lock(mutex_lock); + + // Write to original buffer + std::streamsize result = n; + if (original_buf) { + result = original_buf->sputn(s, n); + } + + // Also write to file + if (file_stream && result > 0) { + file_stream->write(s, result); + } + + return result; + } + + // Critical: sync() must flush both destinations + virtual int sync() override { + std::lock_guard lock(mutex_lock); + int result = 0; + + if (original_buf) { + result = original_buf->pubsync(); + } + + if (file_stream) { + file_stream->flush(); + } + + return result; + } + + public: + TeeStreambuf(std::streambuf* terminal, std::ostream* file) + : original_buf(terminal), file_stream(file) {} + }; + + // ======================================================================== + // STREAM MANAGEMENT FOR TEE MODE + // ======================================================================== + std::optional cout_guard; // Manages cout buffer + std::optional cerr_guard; // Manages cerr buffer + std::unique_ptr cout_tee; // Tee buffer for cout + std::unique_ptr cerr_tee; // Tee buffer for cerr + + // Add a flag to track if we're in capture mode + bool in_capture_mode = false; + + // Store original streambufs when disabling tee temporarily + std::optional temp_cout_guard; + std::optional temp_cerr_guard; + std::optional temp_mfem_out_guard; + std::optional temp_mfem_err_guard; + + std::optional mfem_out_guard; + std::optional mfem_err_guard; + std::unique_ptr mfem_out_tee; + std::unique_ptr mfem_err_tee; + + // ======================================================================== + // CAPTURE CONTEXT FOR REDIRECTED OUTPUT + // ======================================================================== + /** + * @brief State for active output capture + * + * @details When capturing output to a file, we need to: + * 1. Save current stdout/stderr state + * 2. Create a pipe for capturing + * 3. Redirect stdout/stderr to the pipe + * 4. Read from pipe in separate thread + * 5. Restore everything when done + * + * This structure holds all that state with RAII for safety. + */ + struct CaptureContext { + // RAII wrappers for file descriptor management + std::optional stdout_dup; // Saves/restores stdout + std::optional stderr_dup; // Saves/restores stderr + Pipe capture_pipe; // Pipe for capturing output + + // Thread that reads from pipe + std::thread reader_thread; + + // Output accumulation + std::stringstream captured_output; // Where we store captured text + std::atomic stop_reading{false}; // Signal to stop reader thread + + // Metadata + std::filesystem::path output_filename; // Where to write captured output + bool suppress_non_zero_ranks; // Only capture on rank 0? + bool has_content{false}; // Did we capture anything? + + // Statistics + std::chrono::steady_clock::time_point start_time; // When capture started + size_t bytes_captured{0}; // Total bytes captured + + // Add these for C++ stream handling during suppression + std::unique_ptr null_stream; + std::optional cout_null_guard; + std::optional cerr_null_guard; + std::optional mfem_out_null_guard; + std::optional mfem_err_null_guard; + }; + + // Stack allows nested captures (capture within capture) + std::stack> capture_stack; + std::mutex capture_mutex; // Thread safety for capture operations + + // ======================================================================== + // STATISTICS TRACKING + // ======================================================================== + struct LogStatistics { + int total_captures = 0; // Total begin_capture calls + int files_created = 0; // Files actually written + std::vector created_files; // List of created files + } stats_; + + // ======================================================================== + // PRIVATE CONSTRUCTOR (SINGLETON) + // ======================================================================== + UnifiedLogger() { + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank); + MPI_Comm_size(MPI_COMM_WORLD, &mpi_size); + } + + // ======================================================================== + // PRIVATE HELPER METHODS + // ======================================================================== + + /** + * @brief Thread function that reads captured output from pipe + * + * @param ctx Capture context containing pipe and output buffer + * + * @details Runs in separate thread during capture. Continuously reads + * from the pipe where stdout/stderr are redirected and accumulates + * in a stringstream. Uses select() for efficient non-blocking I/O. + */ + void reader_thread_func(CaptureContext* ctx); + + /** + * @brief Ensure GPU printf buffers are flushed + * + * @details GPU kernels use device-side printf buffers that aren't + * automatically flushed. This forces synchronization to ensure + * all GPU output is captured before ending a capture session. + */ + void flush_gpu_output(); + + /** + * @brief Restores the tee after capture to ensure proper logging + */ + void restore_tee_after_capture(); + +public: + // Delete copy operations (singleton must not be copied) + UnifiedLogger(const UnifiedLogger&) = delete; + UnifiedLogger& operator=(const UnifiedLogger&) = delete; + + /** + * @brief Get the singleton instance + * + * @return Reference to the single UnifiedLogger instance + * + * @details Thread-safe lazy initialization. First call creates + * the instance, subsequent calls return the same instance. + */ + static UnifiedLogger& get_instance(); + + /** + * @brief Initialize the logging system + * + * @param options ExaOptions containing configuration + * + * @details Must be called once after MPI initialization. Sets up: + * - Log directory structure + * - Main simulation log file + * - Tee functionality for cout/cerr + * - Initial log headers with timestamp + */ + void initialize(const ExaOptions& options); + + /** + * @brief Enable tee output for main logging + * + * @details Activates custom streambuf that duplicates all output + * to both terminal and log file. Called automatically by initialize(). + * + * Implementation: + * - Saves original cout/cerr buffers + * - Creates TeeStreambuf instances + * - Redirects cout/cerr to use TeeStreambuf + */ + void enable_main_logging(); + + /** + * @brief Disable main logging tee + * + * @details Restores original cout/cerr buffers. Called by shutdown(). + * RAII guards ensure proper cleanup even if exceptions occur. + */ + void disable_main_logging(); + + /** + * @brief Start capturing output to a specific file + * + * @param filename Output filename relative to log directory + * @param suppress_non_zero_ranks If true, only rank 0 captures + * + * @details Redirects ALL output (stdout/stderr) to go ONLY to file. + * Terminal sees nothing until end_capture() is called. + * + * Process: + * 1. Create pipe for communication + * 2. Save current stdout/stderr descriptors + * 3. Redirect stdout/stderr to pipe's write end + * 4. Start thread to read from pipe's read end + * + * Supports nesting - previous capture is paused and resumes later. + */ + void begin_capture(const std::string& filename, bool suppress_non_zero_ranks = false); + + /** + * @brief End current capture and optionally write to file + * + * @return Filename if file was created, empty string otherwise + * + * @details Restores stdout/stderr and writes captured content to file + * ONLY if content exists (prevents empty log files). + * + * Process: + * 1. Flush GPU and C streams + * 2. Restore original stdout/stderr + * 3. Stop reader thread and collect output + * 4. Write to file if content exists + * 5. Update statistics + */ + std::string end_capture(); + + /** + * @brief RAII helper for automatic capture management + * + * @details Ensures capture is properly ended even if exceptions occur. + * + * Usage: + * ```cpp + * { + * UnifiedLogger::ScopedCapture capture("output.log"); + * // All output here goes only to output.log + * } // Automatically ends capture + * ``` + */ + class ScopedCapture { + private: + UnifiedLogger& logger; + std::string filename_created; + + public: + ScopedCapture(const std::string& filename, bool suppress_non_zero = false); + ~ScopedCapture(); + + bool file_was_created() const { + return !filename_created.empty(); + } + const std::string& get_created_filename() const { + return filename_created; + } + }; + + /** + * @brief Generate standardized material log filename + * + * @param model_type Type of model (e.g., "ExaCMech", "UMAT") + * @param region_id Material region index + * @param context Optional context (e.g., "step_50") + * @return Formatted filename like "material_ExaCMech_region_0_step_50_rank_3.log" + */ + std::string get_material_log_filename(const std::string& model_type, + int region_id, + const std::string& context = ""); + + /** + * @brief Print summary of capture statistics + * + * @details Shows how many captures were performed and which files + * were created. Only rank 0 prints to avoid duplication. + */ + void print_capture_statistics(); + + /** + * @brief Execute code with output suppressed on non-zero ranks + * + * @param func Function/lambda to execute + * + * @details On rank 0, executes normally. On other ranks, redirects + * all output to /dev/null during execution. + */ + template + void execute_on_rank_zero_only(Func&& func) { + if (mpi_rank == 0) { + // Rank 0: execute normally without any capture + func(); + } else { + // Non-zero ranks: suppress output + std::stringstream ss; + ss << "mfem_logging" << "_rank_" << mpi_rank << ".log"; + + ScopedCapture suppress(ss.str()); + func(); + } + } + + /** + * @brief Clean shutdown of logging system + * + * @details Call before MPI_Finalize(). Performs: + * - Ends any active captures + * - Prints statistics + * - Writes footer with timestamp + * - Disables tee functionality + * - Closes all files + */ + void shutdown(); +}; + +// ============================================================================ +// CONVENIENCE MACROS +// ============================================================================ + +/** + * @brief Create scoped capture with automatic cleanup + * + * Usage: SCOPED_CAPTURE("filename.log"); + * The capture lasts until end of current scope. + */ +#define SCOPED_CAPTURE(filename, ...) \ + exaconstit::UnifiedLogger::ScopedCapture _capture(filename, ##__VA_ARGS__) +} // namespace exaconstit + +/** + * @brief MFEM macros that only print on rank 0 + * + * These suppress output on non-zero ranks while still executing + * the underlying macro (preserving side effects like MPI_Abort). + */ +#define MFEM_WARNING_0(...) \ + exaconstit::UnifiedLogger::get_instance().execute_on_rank_zero_only([&]() { \ + MFEM_WARNING(__VA_ARGS__); \ + }) + +#define MFEM_ABORT_0(...) \ + exaconstit::UnifiedLogger::get_instance().execute_on_rank_zero_only([&]() { \ + MFEM_ABORT(__VA_ARGS__); \ + }) + +#define MFEM_VERIFY_0(condition, ...) \ + do { \ + if (!(condition)) { \ + exaconstit::UnifiedLogger::get_instance().execute_on_rank_zero_only([&]() { \ + MFEM_VERIFY(false, __VA_ARGS__); \ + }); \ + } \ + } while (0) + +#define MFEM_ASSERT_0(condition, ...) \ + do { \ + if (!(condition)) { \ + exaconstit::UnifiedLogger::get_instance().execute_on_rank_zero_only([&]() { \ + MFEM_ASSERT(false, __VA_ARGS__); \ + }); \ + } \ + } while (0) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 917ad10..c521415 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -39,14 +39,6 @@ list(APPEND EXACONSTIT_TEST_DEPENDS exaconstit_static) message("-- EXACONSTIT_TEST_DEPENDS: ${EXACONSTIT_TEST_DEPENDS}") -blt_add_executable(NAME test_pa - SOURCES mechanics_test.cpp - OUTPUT_DIR ${TEST_OUTPUT_DIR} - DEPENDS_ON ${EXACONSTIT_TEST_DEPENDS} gtest) - -blt_add_test(NAME test_partial_assembly - COMMAND test_pa) - blt_add_executable(NAME test_grad_oper SOURCES grad_test.cpp OUTPUT_DIR ${TEST_OUTPUT_DIR} @@ -115,7 +107,7 @@ endfunction(add_python_test) #################################### # Add Python Module Tests #################################### -set(PYTHON_MODULE_TESTS test_mechanics.py test_mechanics_const_strain_rate.py) +set(PYTHON_MODULE_TESTS test_mechanics.py) foreach(TEST ${PYTHON_MODULE_TESTS}) @@ -124,19 +116,11 @@ foreach(TEST ${PYTHON_MODULE_TESTS}) endforeach() # A hack more or less to get the test config files into the problem -add_custom_command(TARGET test_pa PRE_BUILD +add_custom_command(TARGET test_grad_oper PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_SOURCE_DIR}/test/data/ $/../test/) - -add_custom_command(TARGET test_pa POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_SOURCE_DIR}/test/test_mechanics.py $/../test/test_mechanics.py -) + ${CMAKE_SOURCE_DIR}/test/data/ $/../test/) -add_custom_command(TARGET test_pa POST_BUILD +add_custom_command(TARGET test_grad_oper POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_SOURCE_DIR}/test/test_mechanics_const_strain_rate.py $/../test/test_mechanics_const_strain_rate.py + ${CMAKE_SOURCE_DIR}/test/test_mechanics.py $/../test/test_mechanics.py ) - -#add_test(NAME test_python -# COMMAND ${PYTHON_EXECUTABLE} test_mechanics.py) diff --git a/test/data/mtsdd_bcc.toml b/test/data/mtsdd_bcc.toml index 9ede811..38fc780 100644 --- a/test/data/mtsdd_bcc.toml +++ b/test/data/mtsdd_bcc.toml @@ -85,8 +85,6 @@ Version = "0.6.0" visit = false conduit = false paraview = false - floc = "./exaconstit_p1" - avg_stress_fname = "test_mtsdd_bcc_stress.txt" [Solvers] # Option for how our assembly operation is conducted. Possible choices are # FULL, PA, EA diff --git a/test/data/mtsdd_bcc_stress.txt b/test/data/mtsdd_bcc_stress.txt deleted file mode 100644 index 6718992..0000000 --- a/test/data/mtsdd_bcc_stress.txt +++ /dev/null @@ -1,40 +0,0 @@ -1.1323e-14 1.14918e-14 0.00065299 -5.79514e-06 2.78435e-06 2.06205e-08 -2.37751e-15 6.09436e-15 0.0259026 -0.000231806 0.000110315 1.40251e-06 --1.25224e-13 6.92749e-14 0.0375399 -0.000335817 0.000167023 1.08296e-05 -9.27917e-15 -8.51265e-15 0.0475258 -0.000427146 0.000218913 2.59356e-05 --2.75871e-13 5.40614e-14 0.0560462 -0.000503037 0.000265293 4.49707e-05 -5.56964e-14 2.52416e-14 0.0633371 -0.000564547 0.000304326 6.56944e-05 -4.68658e-15 8.48001e-16 0.0696066 -0.000613966 0.000336447 8.62741e-05 --5.14991e-13 -4.51569e-14 0.0750264 -0.00065326 0.000362752 0.000105641 -8.75329e-12 -9.55152e-12 0.0797361 -0.00068425 0.000384289 0.000123353 -1.9529e-11 1.30723e-11 0.0838477 -0.000708752 0.000402017 0.000139275 -1.4271e-11 1.28188e-11 0.087452 -0.000728134 0.000416825 0.000153623 -1.23549e-11 1.29569e-11 0.0906233 -0.00074364 0.000429404 0.000166449 -1.25681e-11 1.51753e-11 0.0934231 -0.00075618 0.000440293 0.000177916 -1.61325e-11 1.61052e-11 0.0959022 -0.000766426 0.000449894 0.000188173 -1.55863e-11 1.25036e-11 0.0981033 -0.000774804 0.000458489 0.000197485 -1.16068e-11 1.17917e-11 0.100062 -0.000781713 0.00046617 0.000205956 -1.15989e-11 9.60918e-12 0.10181 -0.000787428 0.000473077 0.000213721 -9.94567e-12 9.27977e-12 0.103372 -0.000792179 0.000479401 0.000220867 -9.06081e-12 8.69569e-12 0.104771 -0.000796042 0.000485102 0.000227518 -8.59115e-12 8.25507e-12 0.106025 -0.000799233 0.000490289 0.000233629 -6.04864e-12 7.79599e-12 0.107153 -0.000801928 0.000495069 0.000239322 --2.76577e-14 -2.93797e-14 0.109 -0.000805963 0.000503377 0.000249265 -1.66787e-11 2.96146e-11 0.11052 -0.000809046 0.00051071 0.000258068 -2.62118e-11 1.93287e-11 0.111775 -0.000811552 0.000517336 0.00026598 -1.67938e-11 1.54893e-11 0.112817 -0.000813835 0.000523526 0.000273112 -9.36247e-12 1.59361e-11 0.113684 -0.000815952 0.000529354 0.000279642 -1.31351e-11 1.38656e-11 0.114408 -0.000817999 0.000534916 0.000285719 -1.00663e-15 2.40937e-14 0.115457 -0.000821942 0.000545195 0.000296211 --6.44917e-14 -4.68732e-14 0.116223 -0.000825714 0.000554909 0.000305187 --1.01393e-14 7.40971e-15 0.116788 -0.000829484 0.000564256 0.000313121 --2.35047e-14 -1.84784e-14 0.11721 -0.000833101 0.000573227 0.00032005 --1.6192e-12 3.48098e-12 0.117391 -0.000834908 0.00057762 0.000323314 --1.19524e-12 -1.40043e-13 0.117759 -0.000839955 0.000590008 0.000331717 --1.94501e-14 6.37544e-16 0.117982 -0.000843809 0.000599804 0.000337983 --3.831e-15 -1.64711e-14 0.118147 -0.000847273 0.000609293 0.000343518 --2.50239e-14 5.73782e-14 0.118313 -0.000851615 0.000622887 0.00035073 -1.76922e-13 -2.4821e-17 0.118427 -0.000855391 0.000635802 0.000356671 -3.31654e-14 1.43814e-13 0.118506 -0.000858525 0.000648081 0.000361597 --2.39525e-14 2.86717e-13 0.118561 -0.000861168 0.000659965 0.000365591 --3.18198e-13 -7.4779e-14 0.118606 -0.000864263 0.00067511 0.000369715 diff --git a/test/data/mtsdd_full.toml b/test/data/mtsdd_full.toml index 6278cd6..f7c29b2 100644 --- a/test/data/mtsdd_full.toml +++ b/test/data/mtsdd_full.toml @@ -88,8 +88,6 @@ Version = "0.6.0" visit = false conduit = false paraview = false - floc = "./exaconstit_p1" - avg_stress_fname = "test_mtsdd_full_stress.txt" [Solvers] # Option for how our assembly operation is conducted. Possible choices are # FULL, PA, EA diff --git a/test/data/mtsdd_full_auto.toml b/test/data/mtsdd_full_auto.toml index c36a8e6..d35770f 100644 --- a/test/data/mtsdd_full_auto.toml +++ b/test/data/mtsdd_full_auto.toml @@ -111,7 +111,7 @@ Version = "0.6.0" # dt scaling factor as used in algorithm discussed above # Note: This scaling factor needs to be between 0 and 1 # default value: 0.25 - dt_scale = 0.333333 + dt_scale = 0.12 # Final time step value that we are aiming to reach # default value: 1.0 t_final = 10.0 @@ -128,8 +128,6 @@ Version = "0.6.0" visit = false conduit = false paraview = false - floc = "./exaconstit_p1" - avg_stress_fname = "test_mtsdd_full_auto_stress.txt" [Solvers] # Option for how our assembly operation is conducted. Possible choices are # FULL, PA, EA diff --git a/test/data/mtsdd_full_auto_stress.txt b/test/data/mtsdd_full_auto_stress.txt deleted file mode 100644 index 473be79..0000000 --- a/test/data/mtsdd_full_auto_stress.txt +++ /dev/null @@ -1,71 +0,0 @@ --8.34244e-12 -8.43232e-12 -21.0723 0.16728 -0.076779 0.00362408 --1.59895e-06 -1.43264e-06 -106.916 0.917808 -0.318611 0.0326105 -1.67906e-08 1.48762e-08 -137.432 1.15981 -0.429794 0.0379226 --1.2976e-09 -1.29216e-09 -179.83 1.49591 -0.584262 0.0453289 --1.62267e-09 -1.46099e-09 -238.742 1.96271 -0.798887 0.0556702 --1.46042e-09 -1.48803e-09 -271.482 2.22205 -0.918151 0.0614447 --4.15488e-09 -4.16974e-09 -316.97 2.58223 -1.08385 0.0694979 --2.99688e-09 -2.58568e-09 -380.176 3.08256 -1.31405 0.0807272 -4.0207e-09 -6.45672e-09 -438.724 3.54702 -1.52486 0.0914416 --3.33201e-09 5.23614e-08 -461.918 3.74004 -1.60342 0.0966004 --6.64416e-08 -1.18533e-07 -489.38 3.98807 -1.69332 0.102627 --8.90339e-09 3.02835e-08 -502.821 4.11123 -1.73865 0.106629 --3.67035e-09 9.87492e-08 -515.162 4.2302 -1.77959 0.107364 -4.28538e-09 -1.74157e-07 -526.402 4.34595 -1.81265 0.106232 --1.56859e-07 -5.75973e-08 -537.845 4.46886 -1.84928 0.100894 -1.02428e-08 1.4168e-07 -549.429 4.59316 -1.88211 0.0963621 -9.36134e-08 2.1135e-07 -559.789 4.69888 -1.91661 0.0912753 --1.88195e-07 -9.15799e-08 -571.581 4.80754 -1.96585 0.0889428 --1.76617e-07 -3.88921e-08 -583.216 4.89428 -2.03141 0.0841613 --2.03562e-07 -1.58867e-07 -592.305 4.95363 -2.08549 0.0829521 --1.0475e-07 -2.07986e-07 -601.144 5.00006 -2.13997 0.0798045 -1.0221e-07 5.09345e-08 -609.689 5.04025 -2.20131 0.0746695 --1.26198e-07 4.58523e-08 -616.048 5.07234 -2.25589 0.0723244 --7.67962e-08 -1.46205e-08 -622.159 5.10656 -2.31454 0.0702993 -2.64731e-08 5.67873e-08 -627.545 5.15159 -2.37021 0.0649581 -3.10855e-08 8.84341e-08 -633.452 5.21534 -2.43086 0.0543248 --1.04577e-07 1.44952e-07 -639.959 5.30466 -2.49255 0.040557 -3.57056e-08 -1.12995e-07 -645.495 5.39485 -2.53615 0.0266396 --8.69591e-08 -7.50171e-08 -650.799 5.5029 -2.57 0.00491623 -1.95364e-09 -6.48647e-08 -655.877 5.63452 -2.59748 -0.027768 --9.71408e-08 -2.0306e-08 -660.751 5.77272 -2.61572 -0.0687901 -1.68034e-07 5.87619e-08 -665.405 5.9237 -2.6276 -0.115769 --7.07752e-08 -4.09643e-08 -669.377 6.06508 -2.63557 -0.163968 -1.33424e-08 2.61478e-08 -673.223 6.21599 -2.64172 -0.221465 --9.31628e-08 1.9614e-07 -677.492 6.39723 -2.64629 -0.291843 --4.81161e-08 6.55211e-08 -682.184 6.61004 -2.66351 -0.385428 --1.55193e-07 1.96604e-07 -686.733 6.8116 -2.67606 -0.501099 --9.35059e-09 -6.54657e-08 -689.932 6.95839 -2.68054 -0.592448 -1.62536e-07 -9.89095e-09 -693.015 7.10907 -2.68245 -0.68977 --2.99855e-09 1.00851e-08 -695.185 7.21551 -2.68525 -0.764102 -1.14368e-08 3.80018e-08 -698.04 7.35011 -2.69836 -0.866695 --1.02415e-08 -4.40908e-08 -701.234 7.50648 -2.71441 -0.987812 --2.07968e-08 -2.67328e-08 -704.383 7.63804 -2.7407 -1.10862 --2.51671e-08 7.75815e-08 -707.106 7.7509 -2.7667 -1.22442 -8.66543e-09 -7.45497e-08 -710.157 7.87639 -2.79644 -1.36474 --1.35286e-07 1.3147e-07 -713.211 8.00312 -2.83382 -1.50367 -2.24537e-08 9.44603e-08 -715.869 8.11384 -2.86396 -1.63205 --4.44108e-08 1.89458e-07 -718.795 8.2326 -2.88575 -1.7818 --4.99583e-08 4.76423e-08 -722.049 8.37279 -2.90413 -1.93977 --2.93438e-08 -1.70062e-08 -724.666 8.45967 -2.92479 -2.06624 -2.19189e-08 -1.5163e-08 -726.923 8.54356 -2.94451 -2.18429 -5.81968e-08 -6.08306e-08 -729.465 8.63587 -2.97783 -2.31889 -3.31901e-08 -5.93562e-08 -731.999 8.71498 -3.01823 -2.44667 --1.76499e-08 1.91958e-07 -734.477 8.78481 -3.06076 -2.57808 --3.49046e-08 2.59222e-08 -737.28 8.84771 -3.10757 -2.71623 --8.53035e-09 8.8163e-09 -740.123 8.91162 -3.14984 -2.85266 --6.46402e-08 6.7046e-08 -742.987 8.97405 -3.19023 -2.98717 -3.99481e-08 1.23537e-08 -745.522 9.02883 -3.23603 -3.10714 -1.12705e-08 3.16375e-09 -747.546 9.07425 -3.28165 -3.20781 --1.36347e-09 -1.08687e-09 -749.537 9.11358 -3.33636 -3.31011 -5.85035e-08 -7.59225e-09 -751.778 9.16929 -3.39467 -3.43043 --6.36941e-08 5.2214e-08 -754.352 9.24514 -3.44785 -3.56508 --3.32971e-08 -3.57214e-08 -757.022 9.31948 -3.50293 -3.68582 --3.23725e-08 -4.08988e-09 -759.452 9.36923 -3.56698 -3.79367 --1.97439e-08 -1.5918e-08 -761.84 9.42063 -3.61857 -3.8972 -1.40848e-08 4.6058e-08 -764.209 9.4618 -3.66236 -3.99664 -5.13846e-08 -3.3086e-08 -766.297 9.49289 -3.69396 -4.07545 -1.00131e-08 2.43169e-09 -768.367 9.51656 -3.72547 -4.14366 --7.61929e-09 1.27377e-08 -770.162 9.53422 -3.75408 -4.20315 -5.96074e-09 -6.02569e-08 -771.712 9.55573 -3.77569 -4.25017 --3.43852e-09 2.54737e-08 -773.13 9.57371 -3.7974 -4.29163 diff --git a/test/data/mtsdd_full_stress.txt b/test/data/mtsdd_full_stress.txt deleted file mode 100644 index 7f74090..0000000 --- a/test/data/mtsdd_full_stress.txt +++ /dev/null @@ -1,40 +0,0 @@ -1.1323e-14 1.14918e-14 0.00065299 -5.79514e-06 2.78435e-06 2.06205e-08 --3.63368e-12 -1.95715e-13 0.0250009 -0.000221945 0.000111181 3.51597e-06 --4.26903e-14 4.57801e-13 0.0340613 -0.000305058 0.000162338 2.99446e-05 -1.61623e-12 -8.71327e-12 0.0410149 -0.000371015 0.000200926 5.34644e-05 -3.64628e-13 -1.35782e-13 0.0465973 -0.000415189 0.000237446 7.87249e-05 --1.49695e-14 1.53957e-13 0.0511801 -0.000441297 0.000262254 9.92558e-05 -2.02989e-13 2.3883e-13 0.0549885 -0.000456891 0.000275147 0.000116344 -1.67074e-13 1.53881e-13 0.0581935 -0.000465317 0.000282327 0.000129712 -1.94085e-13 2.12123e-13 0.060923 -0.000468948 0.000286954 0.000139823 -1.23401e-13 1.28288e-13 0.0632719 -0.000471544 0.000289885 0.000147369 -6.47381e-14 7.51493e-14 0.0653084 -0.000471975 0.000291925 0.00015322 -4.88594e-14 5.94756e-15 0.0670855 -0.000469902 0.000293556 0.000157412 -4.08979e-15 2.46818e-14 0.0686443 -0.000466717 0.000295038 0.000160903 -9.52511e-15 3.57192e-14 0.0700179 -0.000463389 0.000296408 0.000163508 --3.59793e-15 5.44457e-15 0.071234 -0.000460057 0.000297646 0.000165911 -1.72235e-11 5.20954e-12 0.072314 -0.000457035 0.000298457 0.000168101 -1.8178e-11 -3.58123e-12 0.0732758 -0.000454513 0.000298978 0.000170023 -1.27153e-11 7.06894e-12 0.074135 -0.000452742 0.000299296 0.000171624 -8.60173e-12 7.62293e-12 0.0749048 -0.000451201 0.000299923 0.000173077 -1.02531e-11 2.38694e-12 0.0755968 -0.000449873 0.000300635 0.000174567 -5.8383e-12 5.01543e-12 0.0762203 -0.000448649 0.000301278 0.000176151 -1.52426e-11 1.69011e-11 0.0772502 -0.000446564 0.000302349 0.000179606 -7.11503e-12 1.67958e-11 0.0781022 -0.000445479 0.000304491 0.000183359 -1.28845e-11 1.58706e-11 0.078812 -0.000444175 0.000306995 0.000187236 -1.184e-11 1.79057e-11 0.0794054 -0.000442731 0.000309955 0.000190962 -1.28164e-11 1.24146e-11 0.0799038 -0.000441857 0.000313539 0.000194429 -3.74671e-12 7.01269e-12 0.0803246 -0.000441273 0.000317385 0.000197795 --2.84841e-13 -3.07898e-13 0.0809467 -0.000441365 0.000325177 0.000204605 --2.61727e-14 1.04285e-15 0.0814113 -0.000442331 0.000333123 0.00021032 --4.1877e-14 -3.62643e-14 0.0817626 -0.000443686 0.000341873 0.000215249 --5.54081e-14 -4.12068e-14 0.0820313 -0.000445039 0.000350759 0.000220078 -2.82599e-12 1.96314e-12 0.0821479 -0.000445682 0.000355232 0.00022241 --9.91446e-14 -9.2912e-14 0.0823948 -0.000447008 0.000368295 0.000228933 -1.09024e-13 1.2325e-13 0.0825501 -0.000447802 0.000378928 0.000234022 --5.71247e-14 -1.17756e-14 0.0826686 -0.000448395 0.000389341 0.000238872 --4.70368e-13 -2.25879e-13 0.0827928 -0.000448893 0.000404262 0.000246116 --4.34995e-14 9.08095e-14 0.0828813 -0.000449987 0.00041835 0.000252854 -6.18856e-13 3.4044e-13 0.0829449 -0.000451426 0.000431774 0.000258667 --1.51925e-13 1.16575e-13 0.0829914 -0.000452715 0.00044454 0.000263741 --1.32899e-12 2.19602e-14 0.0830306 -0.000454133 0.000461333 0.000269412 diff --git a/test/data/region_mapping.txt b/test/data/region_mapping.txt new file mode 100644 index 0000000..f4069b0 --- /dev/null +++ b/test/data/region_mapping.txt @@ -0,0 +1,125 @@ +1 1 +2 1 +3 1 +4 1 +5 1 +6 1 +7 1 +8 1 +9 1 +10 1 +11 1 +12 1 +13 1 +14 1 +15 1 +16 1 +17 1 +18 1 +19 1 +20 1 +21 1 +22 1 +23 1 +24 1 +25 1 +26 1 +27 1 +28 1 +29 1 +30 1 +31 1 +32 1 +33 1 +34 1 +35 1 +36 1 +37 1 +38 1 +39 1 +40 1 +41 1 +42 1 +43 1 +44 1 +45 1 +46 1 +47 1 +48 1 +49 1 +50 1 +51 1 +52 1 +53 1 +54 1 +55 1 +56 1 +57 1 +58 1 +59 1 +60 1 +61 1 +62 1 +63 1 +64 2 +65 2 +66 2 +67 2 +68 2 +69 2 +70 2 +71 2 +72 2 +73 2 +74 2 +75 2 +76 2 +77 2 +78 2 +79 2 +80 2 +81 2 +82 2 +83 2 +84 2 +85 2 +86 2 +87 2 +88 2 +89 2 +90 2 +91 2 +92 2 +93 2 +94 2 +95 2 +96 2 +97 2 +98 2 +99 2 +100 2 +101 2 +102 2 +103 2 +104 2 +105 2 +106 2 +107 2 +108 2 +109 2 +110 2 +111 2 +112 2 +113 2 +114 2 +115 2 +116 2 +117 2 +118 2 +119 2 +120 2 +121 2 +122 2 +123 2 +124 2 +125 2 \ No newline at end of file diff --git a/test/data/test_results/mtsdd_bcc/avg_stress_global.txt b/test/data/test_results/mtsdd_bcc/avg_stress_global.txt new file mode 100644 index 0000000..3d1d477 --- /dev/null +++ b/test/data/test_results/mtsdd_bcc/avg_stress_global.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.13447145e-14 1.14505311e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006302e+00 2.40144933e-15 6.11801124e-15 2.59026412e-02 -2.31806194e-04 1.10314700e-04 1.40251405e-06 + 3.00000000e-01 1.00009133e+00 -1.25342734e-13 6.91577456e-14 3.75398842e-02 -3.35816877e-04 1.67023221e-04 1.08296303e-05 + 4.00000000e-01 1.00011563e+00 9.28232045e-15 -8.50783490e-15 4.75257775e-02 -4.27145996e-04 2.18912800e-04 2.59356029e-05 + 5.00000000e-01 1.00013636e+00 -2.75903725e-13 5.40325480e-14 5.60462064e-02 -5.03037163e-04 2.65293074e-04 4.49707408e-05 + 6.00000000e-01 1.00015410e+00 5.52963090e-14 2.48389845e-14 6.33370544e-02 -5.64546699e-04 3.04325870e-04 6.56944065e-05 + 7.00000000e-01 1.00016936e+00 4.71483846e-15 8.74884296e-16 6.96065532e-02 -6.13966280e-04 3.36446723e-04 8.62741143e-05 + 8.00000000e-01 1.00018255e+00 -5.15071072e-13 -4.52334933e-14 7.50264311e-02 -6.53259675e-04 3.62751836e-04 1.05641136e-04 + 9.00000000e-01 1.00019402e+00 8.75318650e-12 -9.55162606e-12 7.97361170e-02 -6.84250292e-04 3.84289250e-04 1.23353451e-04 + 1.00000000e+00 1.00020403e+00 1.95290804e-11 1.30723895e-11 8.38476876e-02 -7.08751754e-04 4.02016705e-04 1.39274930e-04 + 1.10000000e+00 1.00021281e+00 1.42708425e-11 1.28186686e-11 8.74519654e-02 -7.28134241e-04 4.16825052e-04 1.53622722e-04 + 1.20000000e+00 1.00022053e+00 1.23551627e-11 1.29571832e-11 9.06233451e-02 -7.43640217e-04 4.29403604e-04 1.66449154e-04 + 1.30000000e+00 1.00022735e+00 1.25678684e-11 1.51751141e-11 9.34230837e-02 -7.56180451e-04 4.40292773e-04 1.77916459e-04 + 1.40000000e+00 1.00023339e+00 1.61325129e-11 1.61052076e-11 9.59021611e-02 -7.66426227e-04 4.49894436e-04 1.88172821e-04 + 1.50000000e+00 1.00023875e+00 1.55861634e-11 1.25033977e-11 9.81032680e-02 -7.74804497e-04 4.58489296e-04 1.97485301e-04 + 1.60000000e+00 1.00024352e+00 1.16068632e-11 1.17917402e-11 1.00062344e-01 -7.81712848e-04 4.66170151e-04 2.05956220e-04 + 1.70000000e+00 1.00024778e+00 1.15989441e-11 9.60926998e-12 1.01809850e-01 -7.87427518e-04 4.73076948e-04 2.13721143e-04 + 1.80000000e+00 1.00025159e+00 9.94546167e-12 9.27955300e-12 1.03371833e-01 -7.92178947e-04 4.79401321e-04 2.20867422e-04 + 1.90000000e+00 1.00025500e+00 9.06090526e-12 8.69579249e-12 1.04770570e-01 -7.96042179e-04 4.85102170e-04 2.27517799e-04 + 2.00000000e+00 1.00025806e+00 8.59119062e-12 8.25512306e-12 1.06025305e-01 -7.99233052e-04 4.90289341e-04 2.33629115e-04 + 2.10000000e+00 1.00026081e+00 6.04850298e-12 7.79584975e-12 1.07152663e-01 -8.01927970e-04 4.95069041e-04 2.39322278e-04 + 2.30000000e+00 1.00026534e+00 -2.74351971e-14 -2.91545380e-14 1.08999652e-01 -8.05963011e-04 5.03376912e-04 2.49264704e-04 + 2.50000000e+00 1.00026906e+00 1.66786578e-11 2.96145351e-11 1.10519527e-01 -8.09046021e-04 5.10709972e-04 2.58068134e-04 + 2.70000000e+00 1.00027215e+00 2.62116230e-11 1.93285048e-11 1.11775394e-01 -8.11551547e-04 5.17335813e-04 2.65980108e-04 + 2.90000000e+00 1.00027471e+00 1.67942087e-11 1.54896923e-11 1.12817002e-01 -8.13835412e-04 5.23525640e-04 2.73112339e-04 + 3.10000000e+00 1.00027685e+00 9.36234305e-12 1.59359641e-11 1.13684070e-01 -8.15951955e-04 5.29354334e-04 2.79642395e-04 + 3.30000000e+00 1.00027865e+00 1.31350656e-11 1.38655505e-11 1.14408225e-01 -8.17999357e-04 5.34916211e-04 2.85719104e-04 + 3.70000000e+00 1.00028132e+00 1.00225015e-15 2.40897249e-14 1.15456522e-01 -8.21942354e-04 5.45195093e-04 2.96210884e-04 + 4.10000000e+00 1.00028330e+00 -6.45015680e-14 -4.68823653e-14 1.16222719e-01 -8.25714187e-04 5.54908905e-04 3.05186804e-04 + 4.50000000e+00 1.00028480e+00 -1.00041827e-14 7.53815402e-15 1.16788269e-01 -8.29484099e-04 5.64256088e-04 3.13120566e-04 + 4.90000000e+00 1.00028594e+00 -2.33533314e-14 -1.83220061e-14 1.17209944e-01 -8.33101388e-04 5.73227479e-04 3.20049945e-04 + 5.10000000e+00 1.00028641e+00 -1.61927932e-12 3.48089164e-12 1.17390705e-01 -8.34908054e-04 5.77620179e-04 3.23313969e-04 + 5.70000000e+00 1.00028758e+00 -1.19543075e-12 -1.40221285e-13 1.17758916e-01 -8.39954855e-04 5.90007628e-04 3.31717437e-04 + 6.20000000e+00 1.00028831e+00 -1.93620913e-14 7.21545360e-16 1.17982332e-01 -8.43809029e-04 5.99804142e-04 3.37983117e-04 + 6.70000000e+00 1.00028889e+00 -3.95989801e-15 -1.65994355e-14 1.18147400e-01 -8.47273331e-04 6.09292518e-04 3.43518107e-04 + 7.45000000e+00 1.00028972e+00 -2.50898108e-14 5.73199075e-14 1.18313196e-01 -8.51615423e-04 6.22886955e-04 3.50729650e-04 + 8.20000000e+00 1.00029041e+00 1.77204767e-13 2.61975585e-16 1.18426732e-01 -8.55390889e-04 6.35801551e-04 3.56671028e-04 + 8.95000000e+00 1.00029102e+00 3.28924137e-14 1.43536520e-13 1.18505559e-01 -8.58524887e-04 6.48081019e-04 3.61596870e-04 + 9.70000000e+00 1.00029157e+00 -2.40080358e-14 2.86659704e-13 1.18560636e-01 -8.61168414e-04 6.59964817e-04 3.65590639e-04 + 1.07000000e+01 1.00029242e+00 -3.18422673e-13 -7.50025814e-14 1.18605665e-01 -8.64263450e-04 6.75110398e-04 3.69714655e-04 diff --git a/test/data/test_results/mtsdd_bcc/avg_stress_region_default_1.txt b/test/data/test_results/mtsdd_bcc/avg_stress_region_default_1.txt new file mode 100644 index 0000000..3d1d477 --- /dev/null +++ b/test/data/test_results/mtsdd_bcc/avg_stress_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.13447145e-14 1.14505311e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006302e+00 2.40144933e-15 6.11801124e-15 2.59026412e-02 -2.31806194e-04 1.10314700e-04 1.40251405e-06 + 3.00000000e-01 1.00009133e+00 -1.25342734e-13 6.91577456e-14 3.75398842e-02 -3.35816877e-04 1.67023221e-04 1.08296303e-05 + 4.00000000e-01 1.00011563e+00 9.28232045e-15 -8.50783490e-15 4.75257775e-02 -4.27145996e-04 2.18912800e-04 2.59356029e-05 + 5.00000000e-01 1.00013636e+00 -2.75903725e-13 5.40325480e-14 5.60462064e-02 -5.03037163e-04 2.65293074e-04 4.49707408e-05 + 6.00000000e-01 1.00015410e+00 5.52963090e-14 2.48389845e-14 6.33370544e-02 -5.64546699e-04 3.04325870e-04 6.56944065e-05 + 7.00000000e-01 1.00016936e+00 4.71483846e-15 8.74884296e-16 6.96065532e-02 -6.13966280e-04 3.36446723e-04 8.62741143e-05 + 8.00000000e-01 1.00018255e+00 -5.15071072e-13 -4.52334933e-14 7.50264311e-02 -6.53259675e-04 3.62751836e-04 1.05641136e-04 + 9.00000000e-01 1.00019402e+00 8.75318650e-12 -9.55162606e-12 7.97361170e-02 -6.84250292e-04 3.84289250e-04 1.23353451e-04 + 1.00000000e+00 1.00020403e+00 1.95290804e-11 1.30723895e-11 8.38476876e-02 -7.08751754e-04 4.02016705e-04 1.39274930e-04 + 1.10000000e+00 1.00021281e+00 1.42708425e-11 1.28186686e-11 8.74519654e-02 -7.28134241e-04 4.16825052e-04 1.53622722e-04 + 1.20000000e+00 1.00022053e+00 1.23551627e-11 1.29571832e-11 9.06233451e-02 -7.43640217e-04 4.29403604e-04 1.66449154e-04 + 1.30000000e+00 1.00022735e+00 1.25678684e-11 1.51751141e-11 9.34230837e-02 -7.56180451e-04 4.40292773e-04 1.77916459e-04 + 1.40000000e+00 1.00023339e+00 1.61325129e-11 1.61052076e-11 9.59021611e-02 -7.66426227e-04 4.49894436e-04 1.88172821e-04 + 1.50000000e+00 1.00023875e+00 1.55861634e-11 1.25033977e-11 9.81032680e-02 -7.74804497e-04 4.58489296e-04 1.97485301e-04 + 1.60000000e+00 1.00024352e+00 1.16068632e-11 1.17917402e-11 1.00062344e-01 -7.81712848e-04 4.66170151e-04 2.05956220e-04 + 1.70000000e+00 1.00024778e+00 1.15989441e-11 9.60926998e-12 1.01809850e-01 -7.87427518e-04 4.73076948e-04 2.13721143e-04 + 1.80000000e+00 1.00025159e+00 9.94546167e-12 9.27955300e-12 1.03371833e-01 -7.92178947e-04 4.79401321e-04 2.20867422e-04 + 1.90000000e+00 1.00025500e+00 9.06090526e-12 8.69579249e-12 1.04770570e-01 -7.96042179e-04 4.85102170e-04 2.27517799e-04 + 2.00000000e+00 1.00025806e+00 8.59119062e-12 8.25512306e-12 1.06025305e-01 -7.99233052e-04 4.90289341e-04 2.33629115e-04 + 2.10000000e+00 1.00026081e+00 6.04850298e-12 7.79584975e-12 1.07152663e-01 -8.01927970e-04 4.95069041e-04 2.39322278e-04 + 2.30000000e+00 1.00026534e+00 -2.74351971e-14 -2.91545380e-14 1.08999652e-01 -8.05963011e-04 5.03376912e-04 2.49264704e-04 + 2.50000000e+00 1.00026906e+00 1.66786578e-11 2.96145351e-11 1.10519527e-01 -8.09046021e-04 5.10709972e-04 2.58068134e-04 + 2.70000000e+00 1.00027215e+00 2.62116230e-11 1.93285048e-11 1.11775394e-01 -8.11551547e-04 5.17335813e-04 2.65980108e-04 + 2.90000000e+00 1.00027471e+00 1.67942087e-11 1.54896923e-11 1.12817002e-01 -8.13835412e-04 5.23525640e-04 2.73112339e-04 + 3.10000000e+00 1.00027685e+00 9.36234305e-12 1.59359641e-11 1.13684070e-01 -8.15951955e-04 5.29354334e-04 2.79642395e-04 + 3.30000000e+00 1.00027865e+00 1.31350656e-11 1.38655505e-11 1.14408225e-01 -8.17999357e-04 5.34916211e-04 2.85719104e-04 + 3.70000000e+00 1.00028132e+00 1.00225015e-15 2.40897249e-14 1.15456522e-01 -8.21942354e-04 5.45195093e-04 2.96210884e-04 + 4.10000000e+00 1.00028330e+00 -6.45015680e-14 -4.68823653e-14 1.16222719e-01 -8.25714187e-04 5.54908905e-04 3.05186804e-04 + 4.50000000e+00 1.00028480e+00 -1.00041827e-14 7.53815402e-15 1.16788269e-01 -8.29484099e-04 5.64256088e-04 3.13120566e-04 + 4.90000000e+00 1.00028594e+00 -2.33533314e-14 -1.83220061e-14 1.17209944e-01 -8.33101388e-04 5.73227479e-04 3.20049945e-04 + 5.10000000e+00 1.00028641e+00 -1.61927932e-12 3.48089164e-12 1.17390705e-01 -8.34908054e-04 5.77620179e-04 3.23313969e-04 + 5.70000000e+00 1.00028758e+00 -1.19543075e-12 -1.40221285e-13 1.17758916e-01 -8.39954855e-04 5.90007628e-04 3.31717437e-04 + 6.20000000e+00 1.00028831e+00 -1.93620913e-14 7.21545360e-16 1.17982332e-01 -8.43809029e-04 5.99804142e-04 3.37983117e-04 + 6.70000000e+00 1.00028889e+00 -3.95989801e-15 -1.65994355e-14 1.18147400e-01 -8.47273331e-04 6.09292518e-04 3.43518107e-04 + 7.45000000e+00 1.00028972e+00 -2.50898108e-14 5.73199075e-14 1.18313196e-01 -8.51615423e-04 6.22886955e-04 3.50729650e-04 + 8.20000000e+00 1.00029041e+00 1.77204767e-13 2.61975585e-16 1.18426732e-01 -8.55390889e-04 6.35801551e-04 3.56671028e-04 + 8.95000000e+00 1.00029102e+00 3.28924137e-14 1.43536520e-13 1.18505559e-01 -8.58524887e-04 6.48081019e-04 3.61596870e-04 + 9.70000000e+00 1.00029157e+00 -2.40080358e-14 2.86659704e-13 1.18560636e-01 -8.61168414e-04 6.59964817e-04 3.65590639e-04 + 1.07000000e+01 1.00029242e+00 -3.18422673e-13 -7.50025814e-14 1.18605665e-01 -8.64263450e-04 6.75110398e-04 3.69714655e-04 diff --git a/test/data/test_results/mtsdd_full/avg_stress_global.txt b/test/data/test_results/mtsdd_full/avg_stress_global.txt new file mode 100644 index 0000000..941c2be --- /dev/null +++ b/test/data/test_results/mtsdd_full/avg_stress_global.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.13447145e-14 1.14505311e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006083e+00 -3.63369312e-12 -1.95730246e-13 2.50008841e-02 -2.21944641e-04 1.11180992e-04 3.51597008e-06 + 3.00000000e-01 1.00008287e+00 -4.28535026e-14 4.57639360e-13 3.40612859e-02 -3.05057604e-04 1.62338038e-04 2.99445788e-05 + 4.00000000e-01 1.00009979e+00 1.61638067e-12 -8.71311750e-12 4.10149020e-02 -3.71015117e-04 2.00925683e-04 5.34643844e-05 + 5.00000000e-01 1.00011338e+00 3.64706225e-13 -1.35705232e-13 4.65972786e-02 -4.15188887e-04 2.37446344e-04 7.87248773e-05 + 6.00000000e-01 1.00012453e+00 -1.50253912e-14 1.53900382e-13 5.11801199e-02 -4.41296725e-04 2.62254411e-04 9.92558372e-05 + 7.00000000e-01 1.00013380e+00 2.03000917e-13 2.38842636e-13 5.49885269e-02 -4.56891462e-04 2.75146701e-04 1.16343908e-04 + 8.00000000e-01 1.00014161e+00 1.67009850e-13 1.53816288e-13 5.81934791e-02 -4.65317383e-04 2.82327320e-04 1.29712449e-04 + 9.00000000e-01 1.00014825e+00 1.93905959e-13 2.11940160e-13 6.09229645e-02 -4.68948075e-04 2.86953529e-04 1.39823057e-04 + 1.00000000e+00 1.00015398e+00 1.23341970e-13 1.28229609e-13 6.32718710e-02 -4.71544057e-04 2.89884732e-04 1.47368794e-04 + 1.10000000e+00 1.00015894e+00 6.46887780e-14 7.51016985e-14 6.53084180e-02 -4.71974875e-04 2.91925234e-04 1.53220317e-04 + 1.20000000e+00 1.00016327e+00 4.88996552e-14 5.98978117e-15 6.70855085e-02 -4.69901890e-04 2.93555636e-04 1.57412290e-04 + 1.30000000e+00 1.00016707e+00 4.04991224e-15 2.46434304e-14 6.86442985e-02 -4.66717161e-04 2.95037673e-04 1.60902673e-04 + 1.40000000e+00 1.00017042e+00 9.51131763e-15 3.57072092e-14 7.00179156e-02 -4.63389238e-04 2.96407501e-04 1.63508036e-04 + 1.50000000e+00 1.00017338e+00 -3.51210319e-15 5.52841834e-15 7.12339960e-02 -4.60057416e-04 2.97646423e-04 1.65910657e-04 + 1.60000000e+00 1.00017602e+00 1.72233574e-11 5.20940471e-12 7.23140144e-02 -4.57035090e-04 2.98456640e-04 1.68101265e-04 + 1.70000000e+00 1.00017836e+00 1.81782775e-11 -3.58095822e-12 7.32757986e-02 -4.54513106e-04 2.98977986e-04 1.70023043e-04 + 1.80000000e+00 1.00018046e+00 1.27153278e-11 7.06898287e-12 7.41349674e-02 -4.52742196e-04 2.99295865e-04 1.71623570e-04 + 1.90000000e+00 1.00018234e+00 8.60160263e-12 7.62280651e-12 7.49048056e-02 -4.51200987e-04 2.99922926e-04 1.73077285e-04 + 2.00000000e+00 1.00018403e+00 1.02532155e-11 2.38700995e-12 7.55967651e-02 -4.49872690e-04 3.00634564e-04 1.74567303e-04 + 2.10000000e+00 1.00018556e+00 5.83817199e-12 5.01530517e-12 7.62202572e-02 -4.48649353e-04 3.01277597e-04 1.76151058e-04 + 2.30000000e+00 1.00018809e+00 1.52427152e-11 1.69012371e-11 7.72502069e-02 -4.46564034e-04 3.02348643e-04 1.79605763e-04 + 2.50000000e+00 1.00019020e+00 7.11500432e-12 1.67957450e-11 7.81022347e-02 -4.45479208e-04 3.04491253e-04 1.83359065e-04 + 2.70000000e+00 1.00019195e+00 1.28846998e-11 1.58707241e-11 7.88119591e-02 -4.44175140e-04 3.06995369e-04 1.87235681e-04 + 2.90000000e+00 1.00019343e+00 1.18399671e-11 1.79056670e-11 7.94053997e-02 -4.42731368e-04 3.09955362e-04 1.90961935e-04 + 3.10000000e+00 1.00019467e+00 1.28160631e-11 1.24143526e-11 7.99038295e-02 -4.41857263e-04 3.13539351e-04 1.94429411e-04 + 3.30000000e+00 1.00019572e+00 3.74682400e-12 7.01280829e-12 8.03245876e-02 -4.41272927e-04 3.17385325e-04 1.97795459e-04 + 3.70000000e+00 1.00019735e+00 -2.84856968e-13 -3.07913798e-13 8.09466939e-02 -4.41364644e-04 3.25176528e-04 2.04605229e-04 + 4.10000000e+00 1.00019860e+00 -2.58613721e-14 1.35324923e-15 8.14112892e-02 -4.42330766e-04 3.33123458e-04 2.10319818e-04 + 4.50000000e+00 1.00019958e+00 -4.16548956e-14 -3.60381930e-14 8.17625810e-02 -4.43686044e-04 3.41873102e-04 2.15248829e-04 + 4.90000000e+00 1.00020035e+00 -5.51495874e-14 -4.09474789e-14 8.20312953e-02 -4.45039253e-04 3.50758827e-04 2.20077829e-04 + 5.10000000e+00 1.00020067e+00 2.82609961e-12 1.96324824e-12 8.21479278e-02 -4.45681973e-04 3.55232400e-04 2.22410394e-04 + 5.70000000e+00 1.00020154e+00 -9.86244093e-14 -9.23899371e-14 8.23948072e-02 -4.47008235e-04 3.68295471e-04 2.28932654e-04 + 6.20000000e+00 1.00020210e+00 1.09218672e-13 1.23439280e-13 8.25500687e-02 -4.47802058e-04 3.78927674e-04 2.34021647e-04 + 6.70000000e+00 1.00020257e+00 -5.71955578e-14 -1.18460327e-14 8.26686399e-02 -4.48394776e-04 3.89340999e-04 2.38871523e-04 + 7.45000000e+00 1.00020329e+00 -4.70271979e-13 -2.25779535e-13 8.27928158e-02 -4.48893189e-04 4.04262045e-04 2.46116397e-04 + 8.20000000e+00 1.00020393e+00 -4.35036937e-14 9.08025510e-14 8.28812509e-02 -4.49986953e-04 4.18350279e-04 2.52853726e-04 + 8.95000000e+00 1.00020450e+00 6.19037882e-13 3.40622390e-13 8.29449450e-02 -4.51426376e-04 4.31774262e-04 2.58667377e-04 + 9.70000000e+00 1.00020503e+00 -1.51925826e-13 1.16578464e-13 8.29913520e-02 -4.52715083e-04 4.44539636e-04 2.63741195e-04 + 1.07000000e+01 1.00020586e+00 -1.32901606e-12 2.19339361e-14 8.30305854e-02 -4.54132828e-04 4.61333229e-04 2.69412167e-04 diff --git a/test/data/test_results/mtsdd_full/avg_stress_region_default_1.txt b/test/data/test_results/mtsdd_full/avg_stress_region_default_1.txt new file mode 100644 index 0000000..941c2be --- /dev/null +++ b/test/data/test_results/mtsdd_full/avg_stress_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.13447145e-14 1.14505311e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006083e+00 -3.63369312e-12 -1.95730246e-13 2.50008841e-02 -2.21944641e-04 1.11180992e-04 3.51597008e-06 + 3.00000000e-01 1.00008287e+00 -4.28535026e-14 4.57639360e-13 3.40612859e-02 -3.05057604e-04 1.62338038e-04 2.99445788e-05 + 4.00000000e-01 1.00009979e+00 1.61638067e-12 -8.71311750e-12 4.10149020e-02 -3.71015117e-04 2.00925683e-04 5.34643844e-05 + 5.00000000e-01 1.00011338e+00 3.64706225e-13 -1.35705232e-13 4.65972786e-02 -4.15188887e-04 2.37446344e-04 7.87248773e-05 + 6.00000000e-01 1.00012453e+00 -1.50253912e-14 1.53900382e-13 5.11801199e-02 -4.41296725e-04 2.62254411e-04 9.92558372e-05 + 7.00000000e-01 1.00013380e+00 2.03000917e-13 2.38842636e-13 5.49885269e-02 -4.56891462e-04 2.75146701e-04 1.16343908e-04 + 8.00000000e-01 1.00014161e+00 1.67009850e-13 1.53816288e-13 5.81934791e-02 -4.65317383e-04 2.82327320e-04 1.29712449e-04 + 9.00000000e-01 1.00014825e+00 1.93905959e-13 2.11940160e-13 6.09229645e-02 -4.68948075e-04 2.86953529e-04 1.39823057e-04 + 1.00000000e+00 1.00015398e+00 1.23341970e-13 1.28229609e-13 6.32718710e-02 -4.71544057e-04 2.89884732e-04 1.47368794e-04 + 1.10000000e+00 1.00015894e+00 6.46887780e-14 7.51016985e-14 6.53084180e-02 -4.71974875e-04 2.91925234e-04 1.53220317e-04 + 1.20000000e+00 1.00016327e+00 4.88996552e-14 5.98978117e-15 6.70855085e-02 -4.69901890e-04 2.93555636e-04 1.57412290e-04 + 1.30000000e+00 1.00016707e+00 4.04991224e-15 2.46434304e-14 6.86442985e-02 -4.66717161e-04 2.95037673e-04 1.60902673e-04 + 1.40000000e+00 1.00017042e+00 9.51131763e-15 3.57072092e-14 7.00179156e-02 -4.63389238e-04 2.96407501e-04 1.63508036e-04 + 1.50000000e+00 1.00017338e+00 -3.51210319e-15 5.52841834e-15 7.12339960e-02 -4.60057416e-04 2.97646423e-04 1.65910657e-04 + 1.60000000e+00 1.00017602e+00 1.72233574e-11 5.20940471e-12 7.23140144e-02 -4.57035090e-04 2.98456640e-04 1.68101265e-04 + 1.70000000e+00 1.00017836e+00 1.81782775e-11 -3.58095822e-12 7.32757986e-02 -4.54513106e-04 2.98977986e-04 1.70023043e-04 + 1.80000000e+00 1.00018046e+00 1.27153278e-11 7.06898287e-12 7.41349674e-02 -4.52742196e-04 2.99295865e-04 1.71623570e-04 + 1.90000000e+00 1.00018234e+00 8.60160263e-12 7.62280651e-12 7.49048056e-02 -4.51200987e-04 2.99922926e-04 1.73077285e-04 + 2.00000000e+00 1.00018403e+00 1.02532155e-11 2.38700995e-12 7.55967651e-02 -4.49872690e-04 3.00634564e-04 1.74567303e-04 + 2.10000000e+00 1.00018556e+00 5.83817199e-12 5.01530517e-12 7.62202572e-02 -4.48649353e-04 3.01277597e-04 1.76151058e-04 + 2.30000000e+00 1.00018809e+00 1.52427152e-11 1.69012371e-11 7.72502069e-02 -4.46564034e-04 3.02348643e-04 1.79605763e-04 + 2.50000000e+00 1.00019020e+00 7.11500432e-12 1.67957450e-11 7.81022347e-02 -4.45479208e-04 3.04491253e-04 1.83359065e-04 + 2.70000000e+00 1.00019195e+00 1.28846998e-11 1.58707241e-11 7.88119591e-02 -4.44175140e-04 3.06995369e-04 1.87235681e-04 + 2.90000000e+00 1.00019343e+00 1.18399671e-11 1.79056670e-11 7.94053997e-02 -4.42731368e-04 3.09955362e-04 1.90961935e-04 + 3.10000000e+00 1.00019467e+00 1.28160631e-11 1.24143526e-11 7.99038295e-02 -4.41857263e-04 3.13539351e-04 1.94429411e-04 + 3.30000000e+00 1.00019572e+00 3.74682400e-12 7.01280829e-12 8.03245876e-02 -4.41272927e-04 3.17385325e-04 1.97795459e-04 + 3.70000000e+00 1.00019735e+00 -2.84856968e-13 -3.07913798e-13 8.09466939e-02 -4.41364644e-04 3.25176528e-04 2.04605229e-04 + 4.10000000e+00 1.00019860e+00 -2.58613721e-14 1.35324923e-15 8.14112892e-02 -4.42330766e-04 3.33123458e-04 2.10319818e-04 + 4.50000000e+00 1.00019958e+00 -4.16548956e-14 -3.60381930e-14 8.17625810e-02 -4.43686044e-04 3.41873102e-04 2.15248829e-04 + 4.90000000e+00 1.00020035e+00 -5.51495874e-14 -4.09474789e-14 8.20312953e-02 -4.45039253e-04 3.50758827e-04 2.20077829e-04 + 5.10000000e+00 1.00020067e+00 2.82609961e-12 1.96324824e-12 8.21479278e-02 -4.45681973e-04 3.55232400e-04 2.22410394e-04 + 5.70000000e+00 1.00020154e+00 -9.86244093e-14 -9.23899371e-14 8.23948072e-02 -4.47008235e-04 3.68295471e-04 2.28932654e-04 + 6.20000000e+00 1.00020210e+00 1.09218672e-13 1.23439280e-13 8.25500687e-02 -4.47802058e-04 3.78927674e-04 2.34021647e-04 + 6.70000000e+00 1.00020257e+00 -5.71955578e-14 -1.18460327e-14 8.26686399e-02 -4.48394776e-04 3.89340999e-04 2.38871523e-04 + 7.45000000e+00 1.00020329e+00 -4.70271979e-13 -2.25779535e-13 8.27928158e-02 -4.48893189e-04 4.04262045e-04 2.46116397e-04 + 8.20000000e+00 1.00020393e+00 -4.35036937e-14 9.08025510e-14 8.28812509e-02 -4.49986953e-04 4.18350279e-04 2.52853726e-04 + 8.95000000e+00 1.00020450e+00 6.19037882e-13 3.40622390e-13 8.29449450e-02 -4.51426376e-04 4.31774262e-04 2.58667377e-04 + 9.70000000e+00 1.00020503e+00 -1.51925826e-13 1.16578464e-13 8.29913520e-02 -4.52715083e-04 4.44539636e-04 2.63741195e-04 + 1.07000000e+01 1.00020586e+00 -1.32901606e-12 2.19339361e-14 8.30305854e-02 -4.54132828e-04 4.61333229e-04 2.69412167e-04 diff --git a/test/data/test_results/mtsdd_full_auto/avg_stress_global.txt b/test/data/test_results/mtsdd_full_auto/avg_stress_global.txt new file mode 100644 index 0000000..17e4947 --- /dev/null +++ b/test/data/test_results/mtsdd_full_auto/avg_stress_global.txt @@ -0,0 +1,21 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 1.00000000e-01 9.99962210e-01 -8.44668613e-12 -8.52989523e-12 -2.10722670e+01 1.67279793e-01 -7.67789506e-02 3.62408108e-03 + 2.50000000e-01 9.99905520e-01 -8.86678074e-11 -8.77426791e-11 -5.26883960e+01 4.18196137e-01 -1.91972835e-01 9.07568307e-03 + 4.75000000e-01 9.99820477e-01 -6.47799390e-10 -6.40215406e-10 -1.00129986e+02 7.94563048e-01 -3.64820919e-01 1.72879132e-02 + 8.12500000e-01 9.99692893e-01 -4.03928399e-09 -3.99329565e-09 -1.71331541e+02 1.35909604e+00 -6.24221801e-01 2.96846071e-02 + 1.31875000e+00 9.99501472e-01 -2.32518820e-08 -2.29976493e-08 -2.78222086e+02 2.20585555e+00 -1.01361257e+00 4.84558895e-02 + 2.07812500e+00 9.99214264e-01 -2.76446176e-10 -2.77251687e-10 -4.38742797e+02 3.47831404e+00 -1.59636823e+00 7.70341266e-02 + 2.83750000e+00 9.98959877e-01 -7.50423145e-07 -6.26482532e-07 -5.81025607e+02 5.04591777e+00 -1.98413686e+00 7.09310781e-02 + 3.59687500e+00 9.98852765e-01 -4.78829054e-09 -4.03069319e-08 -6.41104843e+02 6.18323872e+00 -2.24951272e+00 -2.81893544e-01 + 4.16640625e+00 9.98815106e-01 -7.25471762e-07 -5.11414690e-07 -6.62286654e+02 7.33565620e+00 -2.03720256e+00 -8.76383203e-01 + 4.73593750e+00 9.98790128e-01 -2.02406236e-07 -2.10860960e-07 -6.76383829e+02 8.07258638e+00 -2.01673313e+00 -1.54585290e+00 + 5.30546875e+00 9.98772020e-01 -1.02592491e-07 -5.06681209e-07 -6.86642862e+02 8.46252689e+00 -2.09087418e+00 -2.10640158e+00 + 5.87500000e+00 9.98758201e-01 8.07860776e-09 -2.90052263e-07 -6.94506405e+02 8.65089218e+00 -2.23618096e+00 -2.55328437e+00 + 6.44453125e+00 9.98747163e-01 -2.80920812e-08 -2.34228218e-07 -7.00815452e+02 8.78046182e+00 -2.42891146e+00 -2.91778925e+00 + 7.01406250e+00 9.98738060e-01 -3.99438067e-08 -3.56026476e-07 -7.06043538e+02 8.85103283e+00 -2.62706289e+00 -3.22817410e+00 + 7.58359375e+00 9.98730331e-01 -4.17057812e-08 -1.07519287e-07 -7.10503565e+02 8.91853061e+00 -2.82370030e+00 -3.49595501e+00 + 8.15312500e+00 9.98723663e-01 -3.98379242e-08 -1.49788697e-07 -7.14370951e+02 8.99311202e+00 -3.02180303e+00 -3.71374480e+00 + 8.72265625e+00 9.98717837e-01 -4.25947198e-08 -9.22025109e-08 -7.17767455e+02 9.06937805e+00 -3.20320033e+00 -3.89283913e+00 + 9.29218750e+00 9.98712659e-01 -3.33670276e-08 -4.04104239e-08 -7.20801445e+02 9.14736016e+00 -3.35731698e+00 -4.04712033e+00 + 9.86171875e+00 9.98708015e-01 -6.54958173e-08 -1.67581106e-08 -7.23537418e+02 9.22070533e+00 -3.48448212e+00 -4.17658048e+00 + 1.00000000e+01 9.98706893e-01 -2.33840801e-09 -3.78378826e-09 -7.24172563e+02 9.23795363e+00 -3.51333084e+00 -4.20521964e+00 diff --git a/test/data/test_results/mtsdd_full_auto/avg_stress_region_default_1.txt b/test/data/test_results/mtsdd_full_auto/avg_stress_region_default_1.txt new file mode 100644 index 0000000..17e4947 --- /dev/null +++ b/test/data/test_results/mtsdd_full_auto/avg_stress_region_default_1.txt @@ -0,0 +1,21 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 1.00000000e-01 9.99962210e-01 -8.44668613e-12 -8.52989523e-12 -2.10722670e+01 1.67279793e-01 -7.67789506e-02 3.62408108e-03 + 2.50000000e-01 9.99905520e-01 -8.86678074e-11 -8.77426791e-11 -5.26883960e+01 4.18196137e-01 -1.91972835e-01 9.07568307e-03 + 4.75000000e-01 9.99820477e-01 -6.47799390e-10 -6.40215406e-10 -1.00129986e+02 7.94563048e-01 -3.64820919e-01 1.72879132e-02 + 8.12500000e-01 9.99692893e-01 -4.03928399e-09 -3.99329565e-09 -1.71331541e+02 1.35909604e+00 -6.24221801e-01 2.96846071e-02 + 1.31875000e+00 9.99501472e-01 -2.32518820e-08 -2.29976493e-08 -2.78222086e+02 2.20585555e+00 -1.01361257e+00 4.84558895e-02 + 2.07812500e+00 9.99214264e-01 -2.76446176e-10 -2.77251687e-10 -4.38742797e+02 3.47831404e+00 -1.59636823e+00 7.70341266e-02 + 2.83750000e+00 9.98959877e-01 -7.50423145e-07 -6.26482532e-07 -5.81025607e+02 5.04591777e+00 -1.98413686e+00 7.09310781e-02 + 3.59687500e+00 9.98852765e-01 -4.78829054e-09 -4.03069319e-08 -6.41104843e+02 6.18323872e+00 -2.24951272e+00 -2.81893544e-01 + 4.16640625e+00 9.98815106e-01 -7.25471762e-07 -5.11414690e-07 -6.62286654e+02 7.33565620e+00 -2.03720256e+00 -8.76383203e-01 + 4.73593750e+00 9.98790128e-01 -2.02406236e-07 -2.10860960e-07 -6.76383829e+02 8.07258638e+00 -2.01673313e+00 -1.54585290e+00 + 5.30546875e+00 9.98772020e-01 -1.02592491e-07 -5.06681209e-07 -6.86642862e+02 8.46252689e+00 -2.09087418e+00 -2.10640158e+00 + 5.87500000e+00 9.98758201e-01 8.07860776e-09 -2.90052263e-07 -6.94506405e+02 8.65089218e+00 -2.23618096e+00 -2.55328437e+00 + 6.44453125e+00 9.98747163e-01 -2.80920812e-08 -2.34228218e-07 -7.00815452e+02 8.78046182e+00 -2.42891146e+00 -2.91778925e+00 + 7.01406250e+00 9.98738060e-01 -3.99438067e-08 -3.56026476e-07 -7.06043538e+02 8.85103283e+00 -2.62706289e+00 -3.22817410e+00 + 7.58359375e+00 9.98730331e-01 -4.17057812e-08 -1.07519287e-07 -7.10503565e+02 8.91853061e+00 -2.82370030e+00 -3.49595501e+00 + 8.15312500e+00 9.98723663e-01 -3.98379242e-08 -1.49788697e-07 -7.14370951e+02 8.99311202e+00 -3.02180303e+00 -3.71374480e+00 + 8.72265625e+00 9.98717837e-01 -4.25947198e-08 -9.22025109e-08 -7.17767455e+02 9.06937805e+00 -3.20320033e+00 -3.89283913e+00 + 9.29218750e+00 9.98712659e-01 -3.33670276e-08 -4.04104239e-08 -7.20801445e+02 9.14736016e+00 -3.35731698e+00 -4.04712033e+00 + 9.86171875e+00 9.98708015e-01 -6.54958173e-08 -1.67581106e-08 -7.23537418e+02 9.22070533e+00 -3.48448212e+00 -4.17658048e+00 + 1.00000000e+01 9.98706893e-01 -2.33840801e-09 -3.78378826e-09 -7.24172563e+02 9.23795363e+00 -3.51333084e+00 -4.20521964e+00 diff --git a/test/data/test_results/multi_material_test/avg_def_grad_global.txt b/test/data/test_results/multi_material_test/avg_def_grad_global.txt new file mode 100644 index 0000000..145f052 --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_def_grad_global.txt @@ -0,0 +1,41 @@ + # Time Volume F11 F12 F13 F21 F22 F23 F31 F32 F33 + 5.00000000e-03 1.00000159e+00 9.99998367e-01 -1.88826625e-07 -7.53880704e-08 1.74019841e-07 9.99998222e-01 -3.51738840e-08 -1.22636634e-07 3.73904224e-08 1.00000500e+00 + 2.00000000e-01 1.00006342e+00 9.99934654e-01 -7.57048434e-06 -3.16406369e-06 7.03431245e-06 9.99928791e-01 -1.40827900e-06 -4.80522784e-06 1.41614840e-06 1.00020000e+00 + 3.00000000e-01 1.00008461e+00 9.99898180e-01 -1.41765028e-05 -7.31794952e-06 1.32328203e-05 9.99886465e-01 -1.65818679e-06 -4.96308439e-06 4.44349886e-07 1.00030002e+00 + 4.00000000e-01 1.00009168e+00 9.99856823e-01 -2.40360881e-05 -1.27317551e-05 2.15990784e-05 9.99834907e-01 -3.88560668e-07 -4.38540570e-06 -3.75104592e-06 1.00040005e+00 + 5.00000000e-01 1.00009491e+00 9.99814573e-01 -3.55780111e-05 -1.83145788e-05 3.10797136e-05 9.99780415e-01 2.31872950e-06 -2.16009788e-06 -8.64715967e-06 1.00050009e+00 + 6.00000000e-01 1.00009698e+00 9.99772255e-01 -4.88716935e-05 -2.31888172e-05 4.20168514e-05 9.99724827e-01 4.22367932e-06 3.24380581e-07 -1.17719327e-05 1.00060014e+00 + 7.00000000e-01 1.00009854e+00 9.99729831e-01 -6.34721271e-05 -2.74374421e-05 5.42096652e-05 9.99668852e-01 5.34827831e-06 2.52870129e-06 -1.36272020e-05 1.00070020e+00 + 8.00000000e-01 1.00009984e+00 9.99687287e-01 -7.89110205e-05 -3.12767038e-05 6.74298824e-05 9.99612733e-01 5.94791294e-06 4.40666353e-06 -1.46600843e-05 1.00080027e+00 + 9.00000000e-01 1.00010097e+00 9.99644700e-01 -9.48941614e-05 -3.48188851e-05 8.14668865e-05 9.99556499e-01 6.17428447e-06 6.05380406e-06 -1.51380822e-05 1.00090035e+00 + 1.00000000e+00 1.00010200e+00 9.99602113e-01 -1.11268815e-04 -3.81359853e-05 9.61243038e-05 9.99500162e-01 6.12660471e-06 7.55624317e-06 -1.52072934e-05 1.00100044e+00 + 1.10000000e+00 1.00010296e+00 9.99559528e-01 -1.27891691e-04 -4.12138474e-05 1.11240188e-04 9.99443756e-01 5.82658034e-06 8.90423868e-06 -1.49126714e-05 1.00110054e+00 + 1.20000000e+00 1.00010386e+00 9.99516937e-01 -1.44680096e-04 -4.40353586e-05 1.26680600e-04 9.99387311e-01 5.33614140e-06 1.00577968e-05 -1.43630982e-05 1.00120065e+00 + 1.30000000e+00 1.00010473e+00 9.99474341e-01 -1.61593488e-04 -4.66186151e-05 1.42305705e-04 9.99330838e-01 4.70851639e-06 1.10293305e-05 -1.36426223e-05 1.00130077e+00 + 1.40000000e+00 1.00010557e+00 9.99431744e-01 -1.78604771e-04 -4.89864358e-05 1.58022664e-04 9.99274346e-01 3.97981590e-06 1.18307409e-05 -1.27945740e-05 1.00140090e+00 + 1.50000000e+00 1.00010638e+00 9.99389149e-01 -1.95694257e-04 -5.11454571e-05 1.73798759e-04 9.99217834e-01 3.19205420e-06 1.24545625e-05 -1.18647311e-05 1.00150104e+00 + 1.60000000e+00 1.00010719e+00 9.99346561e-01 -2.12841106e-04 -5.31126845e-05 1.89618522e-04 9.99161304e-01 2.37753673e-06 1.29069858e-05 -1.08864277e-05 1.00160119e+00 + 1.70000000e+00 1.00010798e+00 9.99303977e-01 -2.30035351e-04 -5.49216824e-05 2.05464154e-04 9.99104761e-01 1.54762811e-06 1.32135190e-05 -9.87293236e-06 1.00170135e+00 + 1.80000000e+00 1.00010875e+00 9.99261390e-01 -2.47241564e-04 -5.66113472e-05 2.21297082e-04 9.99048217e-01 7.08027944e-07 1.34128447e-05 -8.83345113e-06 1.00180152e+00 + 1.90000000e+00 1.00010952e+00 9.99218796e-01 -2.64436968e-04 -5.81993498e-05 2.37082567e-04 9.98991674e-01 -1.38489020e-07 1.35246889e-05 -7.76994989e-06 1.00190170e+00 + 2.00000000e+00 1.00011028e+00 9.99176205e-01 -2.81633836e-04 -5.96861725e-05 2.52813219e-04 9.98935126e-01 -9.92953525e-07 1.35463109e-05 -6.68023695e-06 1.00200189e+00 + 2.10000000e+00 1.00011104e+00 9.99133626e-01 -2.98837888e-04 -6.10728021e-05 2.68491884e-04 9.98878564e-01 -1.85520113e-06 1.34744414e-05 -5.56514716e-06 1.00210210e+00 + 2.30000000e+00 1.00011254e+00 9.99048538e-01 -3.33236984e-04 -6.34725915e-05 2.99652589e-04 9.98765388e-01 -3.61478430e-06 1.29939129e-05 -3.30104781e-06 1.00230252e+00 + 2.50000000e+00 1.00011402e+00 9.98963492e-01 -3.67557743e-04 -6.55616665e-05 3.30590926e-04 9.98652173e-01 -5.42188620e-06 1.22304624e-05 -1.08932054e-06 1.00250298e+00 + 2.70000000e+00 1.00011549e+00 9.98878486e-01 -4.01728526e-04 -6.73895794e-05 3.61308750e-04 9.98538920e-01 -7.28895404e-06 1.12008206e-05 1.07859705e-06 1.00270348e+00 + 2.90000000e+00 1.00011694e+00 9.98793523e-01 -4.35708587e-04 -6.90047413e-05 3.91803726e-04 9.98425632e-01 -9.21053894e-06 9.94105130e-06 3.28172706e-06 1.00290402e+00 + 3.10000000e+00 1.00011838e+00 9.98708614e-01 -4.69507633e-04 -7.04463142e-05 4.22057176e-04 9.98312299e-01 -1.11798129e-05 8.49461279e-06 5.57475837e-06 1.00310460e+00 + 3.30000000e+00 1.00011981e+00 9.98623770e-01 -5.03138149e-04 -7.17523548e-05 4.52071897e-04 9.98198911e-01 -1.31725765e-05 6.91570218e-06 7.96210558e-06 1.00330522e+00 + 3.70000000e+00 1.00012270e+00 9.98454408e-01 -5.69772741e-04 -7.39219633e-05 5.11375102e-04 9.97971935e-01 -1.71340179e-05 3.36196820e-06 1.30473233e-05 1.00370654e+00 + 4.10000000e+00 1.00012556e+00 9.98285370e-01 -6.35708759e-04 -7.56969953e-05 5.70088909e-04 9.97744687e-01 -2.09651247e-05 -5.69973904e-07 1.84013806e-05 1.00410803e+00 + 4.50000000e+00 1.00012840e+00 9.98116635e-01 -7.00817098e-04 -7.71514317e-05 6.28193682e-04 9.97517193e-01 -2.47032250e-05 -4.90018982e-06 2.40753285e-05 1.00450967e+00 + 4.90000000e+00 1.00013122e+00 9.97948151e-01 -7.65076350e-04 -7.83608862e-05 6.85690561e-04 9.97289507e-01 -2.83925447e-05 -9.61051895e-06 3.00257110e-05 1.00491148e+00 + 5.10000000e+00 1.00013259e+00 9.97863948e-01 -7.96990233e-04 -7.89104794e-05 7.14276296e-04 9.97175601e-01 -3.02445466e-05 -1.20436396e-05 3.30712422e-05 1.00511246e+00 + 5.70000000e+00 1.00013685e+00 9.97611884e-01 -8.91087550e-04 -8.00416596e-05 7.98664371e-04 9.96833717e-01 -3.59567786e-05 -1.96907356e-05 4.27683718e-05 1.00571553e+00 + 6.20000000e+00 1.00014034e+00 9.97402095e-01 -9.68485735e-04 -8.06851470e-05 8.68039794e-04 9.96548601e-01 -4.07872247e-05 -2.61452947e-05 5.10986682e-05 1.00621839e+00 + 6.70000000e+00 1.00014381e+00 9.97192583e-01 -1.04496128e-03 -8.11006830e-05 9.36569795e-04 9.96263314e-01 -4.56520371e-05 -3.26151939e-05 5.96328636e-05 1.00672150e+00 + 7.45000000e+00 1.00014910e+00 9.96878915e-01 -1.15790323e-03 -8.13995415e-05 1.03775278e-03 9.95835202e-01 -5.28609366e-05 -4.21681857e-05 7.26130302e-05 1.00747654e+00 + 8.20000000e+00 1.00015434e+00 9.96565680e-01 -1.26914104e-03 -8.14293756e-05 1.13739384e-03 9.95406897e-01 -5.99172828e-05 -5.16931199e-05 8.56976618e-05 1.00823215e+00 + 8.95000000e+00 1.00015955e+00 9.96252668e-01 -1.37883739e-03 -8.12074817e-05 1.23571200e-03 9.94978609e-01 -6.66804872e-05 -6.12598689e-05 9.87487979e-05 1.00898832e+00 + 9.70000000e+00 1.00016472e+00 9.95939776e-01 -1.48712303e-03 -8.07911839e-05 1.33284942e-03 9.94550445e-01 -7.31961411e-05 -7.08076777e-05 1.11744387e-04 1.00974507e+00 + 1.07000000e+01 1.00017172e+00 9.95522903e-01 -1.62896247e-03 -8.01050878e-05 1.46018796e-03 9.93979924e-01 -8.16949127e-05 -8.33850118e-05 1.29067757e-04 1.01075481e+00 diff --git a/test/data/test_results/multi_material_test/avg_def_grad_region_material_A_1.txt b/test/data/test_results/multi_material_test/avg_def_grad_region_material_A_1.txt new file mode 100644 index 0000000..4fb0fe3 --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_def_grad_region_material_A_1.txt @@ -0,0 +1,41 @@ + # Time Volume F11 F12 F13 F21 F22 F23 F31 F32 F33 + 5.00000000e-03 5.04000802e-01 9.99998399e-01 -2.38703733e-07 -1.25952301e-07 1.43052541e-07 9.99998234e-01 1.91389496e-08 -4.88996137e-07 4.48551065e-09 1.00000496e+00 + 2.00000000e-01 5.04032032e-01 9.99935956e-01 -9.52945681e-06 -5.10332460e-06 5.73646530e-06 9.99929371e-01 7.14915863e-07 -1.95617804e-05 1.46259278e-07 1.00019825e+00 + 3.00000000e-01 5.04042715e-01 9.99899551e-01 -1.67681992e-05 -9.44702165e-06 1.05911344e-05 9.99888955e-01 1.72106319e-06 -2.91336989e-05 -2.01144470e-06 1.00029630e+00 + 4.00000000e-01 5.04046152e-01 9.99858053e-01 -2.80500404e-05 -1.43779395e-05 1.62813313e-05 9.99841186e-01 4.36948449e-06 -3.58946413e-05 -8.48073376e-06 1.00039243e+00 + 5.00000000e-01 5.04047682e-01 9.99815140e-01 -4.15585294e-05 -1.92662592e-05 2.22544826e-05 9.99790254e-01 8.66388953e-06 -3.95541538e-05 -1.70315183e-05 1.00048937e+00 + 6.00000000e-01 5.04048696e-01 9.99771978e-01 -5.71573389e-05 -2.35165483e-05 2.99699130e-05 9.99737414e-01 1.22136643e-05 -4.30589195e-05 -2.44774551e-05 1.00058746e+00 + 7.00000000e-01 5.04049485e-01 9.99728684e-01 -7.39938767e-05 -2.72848559e-05 3.90522746e-05 9.99683746e-01 1.49607969e-05 -4.71565081e-05 -3.10212729e-05 1.00068608e+00 + 8.00000000e-01 5.04050148e-01 9.99685293e-01 -9.15110224e-05 -3.07588017e-05 4.91383528e-05 9.99629768e-01 1.70674310e-05 -5.20316248e-05 -3.67537691e-05 1.00078487e+00 + 9.00000000e-01 5.04050728e-01 9.99641860e-01 -1.09475832e-04 -3.40667428e-05 6.00490275e-05 9.99575638e-01 1.86353019e-05 -5.74823797e-05 -4.17861801e-05 1.00088371e+00 + 1.00000000e+00 5.04051253e-01 9.99598408e-01 -1.27773803e-04 -3.72880271e-05 7.15896154e-05 9.99521355e-01 1.97869616e-05 -6.32141504e-05 -4.62345231e-05 1.00098262e+00 + 1.10000000e+00 5.04051738e-01 9.99554963e-01 -1.46304574e-04 -4.03859351e-05 8.36019725e-05 9.99466929e-01 2.05941545e-05 -6.91471448e-05 -5.01836530e-05 1.00108160e+00 + 1.20000000e+00 5.04052196e-01 9.99511540e-01 -1.65004158e-04 -4.33090725e-05 9.59418892e-05 9.99412411e-01 2.11411759e-05 -7.53177197e-05 -5.37701896e-05 1.00118062e+00 + 1.30000000e+00 5.04052633e-01 9.99468139e-01 -1.83827522e-04 -4.60480512e-05 1.08446358e-04 9.99357833e-01 2.15054751e-05 -8.17034021e-05 -5.70938704e-05 1.00127964e+00 + 1.40000000e+00 5.04053056e-01 9.99424758e-01 -2.02753984e-04 -4.86114168e-05 1.21022161e-04 9.99303200e-01 2.17485850e-05 -8.82796667e-05 -6.02125472e-05 1.00137869e+00 + 1.50000000e+00 5.04053468e-01 9.99381409e-01 -2.21779707e-04 -5.09969197e-05 1.33641233e-04 9.99248516e-01 2.19288321e-05 -9.50353256e-05 -6.31805702e-05 1.00147775e+00 + 1.60000000e+00 5.04053873e-01 9.99338104e-01 -2.40893474e-04 -5.32081224e-05 1.46288535e-04 9.99193787e-01 2.20889864e-05 -1.01957066e-04 -6.60411758e-05 1.00157681e+00 + 1.70000000e+00 5.04054272e-01 9.99294841e-01 -2.60086726e-04 -5.52709194e-05 1.58942950e-04 9.99139019e-01 2.22383921e-05 -1.09013842e-04 -6.88155283e-05 1.00167587e+00 + 1.80000000e+00 5.04054666e-01 9.99251609e-01 -2.79336864e-04 -5.72221101e-05 1.71571381e-04 9.99084226e-01 2.23725007e-05 -1.16155983e-04 -7.15235942e-05 1.00177493e+00 + 1.90000000e+00 5.04055056e-01 9.99208403e-01 -2.98633006e-04 -5.90780622e-05 1.84144693e-04 9.99029412e-01 2.24867269e-05 -1.23362779e-04 -7.41752464e-05 1.00187399e+00 + 2.00000000e+00 5.04055442e-01 9.99165227e-01 -3.17982152e-04 -6.08398418e-05 1.96653243e-04 9.98974568e-01 2.25778675e-05 -1.30643453e-04 -7.67683335e-05 1.00197306e+00 + 2.10000000e+00 5.04055825e-01 9.99122091e-01 -3.37383983e-04 -6.25054852e-05 2.09098339e-04 9.98919687e-01 2.26455292e-05 -1.38011015e-04 -7.93003330e-05 1.00207213e+00 + 2.30000000e+00 5.04056587e-01 9.99035989e-01 -3.76364426e-04 -6.54517545e-05 2.33771551e-04 9.98809827e-01 2.26818857e-05 -1.53085410e-04 -8.41655078e-05 1.00227025e+00 + 2.50000000e+00 5.04057341e-01 9.98950024e-01 -4.15435010e-04 -6.80690965e-05 2.58233750e-04 9.98699925e-01 2.25878675e-05 -1.68429364e-04 -8.88915746e-05 1.00246830e+00 + 2.70000000e+00 5.04058086e-01 9.98864199e-01 -4.54493935e-04 -7.04173129e-05 2.82514241e-04 9.98589974e-01 2.23577987e-05 -1.83994784e-04 -9.35060075e-05 1.00266631e+00 + 2.90000000e+00 5.04058825e-01 9.98778512e-01 -4.93459283e-04 -7.25518519e-05 3.06615909e-04 9.98479938e-01 2.20269529e-05 -1.99733994e-04 -9.79821061e-05 1.00286432e+00 + 3.10000000e+00 5.04059559e-01 9.98692963e-01 -5.32313850e-04 -7.45084514e-05 3.30497086e-04 9.98369787e-01 2.16168087e-05 -2.15608401e-04 -1.02296505e-04 1.00306234e+00 + 3.30000000e+00 5.04060287e-01 9.98607540e-01 -5.71056027e-04 -7.63181536e-05 3.54139203e-04 9.98259506e-01 2.11651112e-05 -2.31592105e-04 -1.06472456e-04 1.00326042e+00 + 3.70000000e+00 5.04061758e-01 9.98437177e-01 -6.48002386e-04 -7.94926650e-05 4.00748245e-04 9.98038441e-01 2.02532218e-05 -2.63876681e-04 -1.14259924e-04 1.00365680e+00 + 4.10000000e+00 5.04063214e-01 9.98267286e-01 -7.24246318e-04 -8.22862845e-05 4.46953622e-04 9.97816799e-01 1.94836182e-05 -2.96441214e-04 -1.21371610e-04 1.00405349e+00 + 4.50000000e+00 5.04064657e-01 9.98097894e-01 -7.99727418e-04 -8.47185637e-05 4.92822511e-04 9.97594588e-01 1.88640838e-05 -3.29389198e-04 -1.27742532e-04 1.00445046e+00 + 4.90000000e+00 5.04066091e-01 9.97928962e-01 -8.74412353e-04 -8.68700443e-05 5.38351223e-04 9.97371931e-01 1.82842868e-05 -3.62719078e-04 -1.33405528e-04 1.00484764e+00 + 5.10000000e+00 5.04066791e-01 9.97844577e-01 -9.11547300e-04 -8.78879595e-05 5.61017858e-04 9.97260482e-01 1.79726730e-05 -3.79463170e-04 -1.36066792e-04 1.00504632e+00 + 5.70000000e+00 5.04068958e-01 9.97592151e-01 -1.02138692e-03 -9.03502526e-05 6.28103374e-04 9.96925547e-01 1.67892408e-05 -4.30125440e-04 -1.42818411e-04 1.00564271e+00 + 6.20000000e+00 5.04070733e-01 9.97382115e-01 -1.11187993e-03 -9.20266739e-05 6.83180996e-04 9.96646040e-01 1.56413479e-05 -4.72585793e-04 -1.47839345e-04 1.00614012e+00 + 6.70000000e+00 5.04072496e-01 9.97172361e-01 -1.20137398e-03 -9.34129227e-05 7.37456381e-04 9.96366239e-01 1.43545404e-05 -5.15189244e-04 -1.52306936e-04 1.00663789e+00 + 7.45000000e+00 5.04075184e-01 9.96858252e-01 -1.33370353e-03 -9.50987658e-05 8.17363230e-04 9.95946227e-01 1.22954939e-05 -5.79119830e-04 -1.58049369e-04 1.00738511e+00 + 8.20000000e+00 5.04077847e-01 9.96544580e-01 -1.46430144e-03 -9.64691547e-05 8.95902899e-04 9.95525987e-01 1.01469234e-05 -6.43170377e-04 -1.62884364e-04 1.00813289e+00 + 8.95000000e+00 5.04080491e-01 9.96231155e-01 -1.59339771e-03 -9.75313132e-05 9.73283398e-04 9.95105713e-01 8.13438856e-06 -7.07288822e-04 -1.67196414e-04 1.00888126e+00 + 9.70000000e+00 5.04083115e-01 9.95917843e-01 -1.72104904e-03 -9.83938760e-05 1.04968724e-03 9.94685471e-01 6.28183539e-06 -7.71273537e-04 -1.71165782e-04 1.00963028e+00 + 1.07000000e+01 5.04086670e-01 9.95500424e-01 -1.88856935e-03 -9.94633983e-05 1.14967912e-03 9.94125338e-01 3.88611262e-06 -8.56028789e-04 -1.75981090e-04 1.01062985e+00 diff --git a/test/data/test_results/multi_material_test/avg_def_grad_region_material_B_2.txt b/test/data/test_results/multi_material_test/avg_def_grad_region_material_B_2.txt new file mode 100644 index 0000000..c55d42c --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_def_grad_region_material_B_2.txt @@ -0,0 +1,41 @@ + # Time Volume F11 F12 F13 F21 F22 F23 F31 F32 F33 + 5.00000000e-03 4.96000786e-01 9.99998333e-01 -1.38145048e-07 -2.40082877e-08 2.05486615e-07 9.99998209e-01 -9.03627314e-08 2.49631896e-07 7.08260588e-08 1.00000504e+00 + 2.00000000e-01 4.96031390e-01 9.99933330e-01 -5.57991500e-06 -1.19352385e-06 8.35309297e-06 9.99928202e-01 -3.56571951e-06 1.01893376e-05 2.70651995e-06 1.00020178e+00 + 3.00000000e-01 4.96041896e-01 9.99896787e-01 -1.15430042e-05 -5.15453689e-06 1.59171148e-05 9.99883934e-01 -5.09194177e-06 1.95973858e-05 2.93975478e-06 1.00030380e+00 + 4.00000000e-01 4.96045528e-01 9.99855574e-01 -1.99573955e-05 -1.10590197e-05 2.70025945e-05 9.99828528e-01 -5.22334743e-06 2.76320364e-05 1.05492616e-06 1.00040780e+00 + 5.00000000e-01 4.96047229e-01 9.99813996e-01 -2.95010366e-05 -1.73475493e-05 4.00472815e-05 9.99770417e-01 -4.12876786e-06 3.58370646e-05 -1.27574693e-07 1.00051098e+00 + 6.00000000e-01 4.96048280e-01 9.99772536e-01 -4.04524148e-05 -2.28558004e-05 5.42580864e-05 9.99712036e-01 -3.89517052e-06 4.44073797e-05 1.13850830e-06 1.00061302e+00 + 7.00000000e-01 4.96049058e-01 9.99730996e-01 -5.27806796e-05 -2.75924893e-05 6.96115187e-05 9.99653718e-01 -4.41927384e-06 5.30152486e-05 4.04740578e-06 1.00071455e+00 + 8.00000000e-01 4.96049693e-01 9.99689314e-01 -6.61078017e-05 -3.18029588e-05 8.60164238e-05 9.99595423e-01 -5.35094448e-06 6.17552074e-05 7.78993494e-06 1.00081592e+00 + 9.00000000e-01 4.96050246e-01 9.99647586e-01 -8.00773118e-05 -3.55831582e-05 1.03230181e-04 9.99537052e-01 -6.48770888e-06 7.06147228e-05 1.19398061e-05 1.00091727e+00 + 1.00000000e+00 4.96050749e-01 9.99605878e-01 -9.44976279e-05 -3.89976196e-05 1.21054698e-04 9.99478627e-01 -7.75407191e-06 7.94680499e-05 1.63203558e-05 1.00101856e+00 + 1.10000000e+00 4.96051217e-01 9.99564167e-01 -1.09181838e-04 -4.20551126e-05 1.39324165e-04 9.99420211e-01 -9.17917147e-06 8.82144675e-05 2.09271753e-05 1.00111979e+00 + 1.20000000e+00 4.96051663e-01 9.99522420e-01 -1.24028239e-04 -4.47733586e-05 1.57915078e-04 9.99361807e-01 -1.07238035e-05 9.68102860e-05 2.56795676e-05 1.00122101e+00 + 1.30000000e+00 4.96052093e-01 9.99480644e-01 -1.39000854e-04 -4.71983814e-05 1.76711151e-04 9.99303408e-01 -1.23593509e-05 1.05257696e-04 3.05094260e-05 1.00132224e+00 + 1.40000000e+00 4.96052509e-01 9.99438842e-01 -1.54066070e-04 -4.93675032e-05 1.95619926e-04 9.99245026e-01 -1.40755355e-05 1.13555772e-04 3.53881764e-05 1.00142348e+00 + 1.50000000e+00 4.96052916e-01 9.99397013e-01 -1.69188090e-04 -5.12963901e-05 2.14603962e-04 9.99186657e-01 -1.58469185e-05 1.21678093e-04 4.02787517e-05 1.00152471e+00 + 1.60000000e+00 4.96053313e-01 9.99355154e-01 -1.84336298e-04 -5.30157074e-05 2.33647354e-04 9.99128298e-01 -1.76518276e-05 1.29623614e-04 4.51578798e-05 1.00162597e+00 + 1.70000000e+00 4.96053703e-01 9.99313261e-01 -1.99499294e-04 -5.45668127e-05 2.52735671e-04 9.99069951e-01 -1.94768455e-05 1.37412216e-04 5.00203153e-05 1.00172725e+00 + 1.80000000e+00 4.96054087e-01 9.99271329e-01 -2.14628617e-04 -5.59907336e-05 2.71824782e-04 9.99011626e-01 -2.13058591e-05 1.45071416e-04 5.48677863e-05 1.00182855e+00 + 1.90000000e+00 4.96054466e-01 9.99229358e-01 -2.29689401e-04 -5.73064651e-05 2.90874246e-04 9.98953328e-01 -2.31286146e-05 1.52619939e-04 5.97063613e-05 1.00192986e+00 + 2.00000000e+00 4.96054840e-01 9.99187360e-01 -2.44699278e-04 -5.85138962e-05 3.09878969e-04 9.98895049e-01 -2.49439356e-05 1.60061634e-04 6.45382729e-05 1.00203119e+00 + 2.10000000e+00 4.96055211e-01 9.99145347e-01 -2.59670103e-04 -5.96170120e-05 3.28843357e-04 9.98836779e-01 -2.67510909e-05 1.67403128e-04 6.93592747e-05 1.00213254e+00 + 2.30000000e+00 4.96055950e-01 9.99061290e-01 -2.89413961e-04 -6.14615075e-05 3.66596189e-04 9.98720233e-01 -3.03355801e-05 1.81751845e-04 7.88676344e-05 1.00233531e+00 + 2.50000000e+00 4.96056679e-01 9.98977177e-01 -3.18908286e-04 -6.30137953e-05 4.04115116e-04 9.98603651e-01 -3.38833959e-05 1.95804065e-04 8.81290542e-05 1.00253821e+00 + 2.70000000e+00 4.96057400e-01 9.98893004e-01 -3.48112088e-04 -6.43130129e-05 4.41374101e-04 9.98487043e-01 -3.74138659e-05 2.09544647e-04 9.71887141e-05 1.00274124e+00 + 2.90000000e+00 4.96058115e-01 9.98808777e-01 -3.77026456e-04 -6.54004209e-05 4.78365501e-04 9.98370451e-01 -4.09518471e-05 2.22997856e-04 1.06178802e-04 1.00294436e+00 + 3.10000000e+00 4.96058823e-01 9.98724518e-01 -4.05688438e-04 -6.63186604e-05 5.15094002e-04 9.98253883e-01 -4.45053981e-05 2.36212095e-04 1.15185834e-04 1.00314754e+00 + 3.30000000e+00 4.96059526e-01 9.98640262e-01 -4.34124849e-04 -6.71129160e-05 5.51584110e-04 9.98137338e-01 -4.80640841e-05 2.49270314e-04 1.24242340e-04 1.00335075e+00 + 3.70000000e+00 4.96060945e-01 9.98471917e-01 -4.90281355e-04 -6.82614134e-05 6.23786226e-04 9.97904356e-01 -5.51242649e-05 2.74910827e-04 1.42407870e-04 1.00375709e+00 + 4.10000000e+00 4.96062350e-01 9.98303746e-01 -5.45743199e-04 -6.90014290e-05 6.95210214e-04 9.97671412e-01 -6.20662550e-05 3.00073298e-04 1.60428734e-04 1.00416344e+00 + 4.50000000e+00 4.96063744e-01 9.98135678e-01 -6.00311474e-04 -6.94622509e-05 7.65748229e-04 9.97438549e-01 -6.89732223e-05 3.24822437e-04 1.78341829e-04 1.00456983e+00 + 4.90000000e+00 4.96065127e-01 9.97967649e-01 -6.53976882e-04 -6.97144850e-05 8.35406314e-04 9.97205754e-01 -7.58222201e-05 3.49193278e-04 1.96092909e-04 1.00497634e+00 + 5.10000000e+00 4.96065800e-01 9.97883631e-01 -6.80585487e-04 -6.97882026e-05 8.70006624e-04 9.97089351e-01 -7.92394565e-05 3.61301960e-04 2.04937285e-04 1.00517966e+00 + 5.70000000e+00 4.96067893e-01 9.97631935e-01 -7.58686584e-04 -6.95667996e-05 9.71976340e-04 9.96740406e-01 -8.95535371e-05 3.97363859e-04 2.31348479e-04 1.00578952e+00 + 6.20000000e+00 4.96069607e-01 9.97422398e-01 -8.22778731e-04 -6.91606923e-05 1.05588019e-03 9.96449591e-01 -9.81259360e-05 4.27495860e-04 2.53245361e-04 1.00629791e+00 + 6.70000000e+00 4.96071310e-01 9.97213130e-01 -8.86025788e-04 -6.85898579e-05 1.13889473e-03 9.96158728e-01 -1.06626467e-04 4.57742343e-04 2.74991063e-04 1.00680645e+00 + 7.45000000e+00 4.96073914e-01 9.96899910e-01 -9.79267421e-04 -6.74793597e-05 1.26169703e-03 9.95722387e-01 -1.19068288e-04 5.03444054e-04 3.06995827e-04 1.00756945e+00 + 8.20000000e+00 4.96076497e-01 9.96587119e-01 -1.07083284e-03 -6.61470159e-05 1.38277986e-03 9.95285885e-01 -1.31111573e-04 5.49324232e-04 3.38289134e-04 1.00833300e+00 + 8.95000000e+00 4.96079059e-01 9.96274528e-01 -1.16081634e-03 -6.46203574e-05 1.50237340e-03 9.94849455e-01 -1.42702078e-04 5.95189109e-04 3.68983532e-04 1.00909711e+00 + 9.70000000e+00 4.96081601e-01 9.95962062e-01 -1.24942392e-03 -6.29045705e-05 1.62057883e-03 9.94413241e-01 -1.53956052e-04 6.40956298e-04 3.99217735e-04 1.00986171e+00 + 1.07000000e+01 4.96085050e-01 9.95545744e-01 -1.36516825e-03 -6.04345368e-05 1.77570516e-03 9.93832164e-01 -1.68656320e-04 7.01721148e-04 4.39036899e-04 1.01088179e+00 diff --git a/test/data/test_results/multi_material_test/avg_elastic_strain_global.txt b/test/data/test_results/multi_material_test/avg_elastic_strain_global.txt new file mode 100644 index 0000000..af18667 --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_elastic_strain_global.txt @@ -0,0 +1,41 @@ + # Time Volume Ee11 Ee22 Ee33 Ee23 Ee13 Ee12 + 5.00000000e-03 1.00000159e+00 -6.41363482e-08 -7.61844749e-08 7.54388797e-07 -3.75468030e-10 1.34047497e-08 -6.98835229e-09 + 2.00000000e-01 1.00006342e+00 -2.57662734e-06 -3.05520514e-06 3.01154681e-05 -2.52556687e-08 5.32343516e-07 -2.70433445e-07 + 3.00000000e-01 1.00008461e+00 -3.36555848e-06 -4.43933564e-06 4.03454295e-05 -4.77230703e-08 7.04095565e-07 -3.20099820e-07 + 4.00000000e-01 1.00009168e+00 -3.39627340e-06 -5.14799020e-06 4.37610526e-05 -1.48751134e-07 7.50887395e-07 -2.18001047e-07 + 5.00000000e-01 1.00009491e+00 -3.22507313e-06 -5.51796280e-06 4.51854302e-05 -3.58740981e-07 7.61176584e-07 -1.19431292e-07 + 6.00000000e-01 1.00009698e+00 -3.00975774e-06 -5.73559884e-06 4.60463050e-05 -5.87107664e-07 7.62356810e-07 -9.31639134e-08 + 7.00000000e-01 1.00009854e+00 -2.83466781e-06 -5.88390406e-06 4.66956365e-05 -7.95230934e-07 7.38446679e-07 -1.05161685e-07 + 8.00000000e-01 1.00009984e+00 -2.72366150e-06 -5.99934846e-06 4.72319266e-05 -9.75213453e-07 6.91786942e-07 -1.34088312e-07 + 9.00000000e-01 1.00010097e+00 -2.65435835e-06 -6.09927610e-06 4.76939642e-05 -1.13267245e-06 6.30000690e-07 -1.65282751e-07 + 1.00000000e+00 1.00010200e+00 -2.60694804e-06 -6.18568902e-06 4.81127464e-05 -1.27122374e-06 5.65545304e-07 -1.91290772e-07 + 1.10000000e+00 1.00010296e+00 -2.57287665e-06 -6.25800190e-06 4.85054235e-05 -1.39211664e-06 5.05010798e-07 -2.10382385e-07 + 1.20000000e+00 1.00010386e+00 -2.54679281e-06 -6.32252192e-06 4.88816682e-05 -1.49640872e-06 4.52102509e-07 -2.25550527e-07 + 1.30000000e+00 1.00010473e+00 -2.52818144e-06 -6.38035483e-06 4.92453474e-05 -1.58750906e-06 4.05775177e-07 -2.36102709e-07 + 1.40000000e+00 1.00010557e+00 -2.51571870e-06 -6.43229840e-06 4.95989572e-05 -1.66808614e-06 3.64479537e-07 -2.42165151e-07 + 1.50000000e+00 1.00010638e+00 -2.50658527e-06 -6.47928468e-06 4.99438990e-05 -1.73971605e-06 3.27189316e-07 -2.45428241e-07 + 1.60000000e+00 1.00010719e+00 -2.50034300e-06 -6.52110506e-06 5.02819337e-05 -1.80281973e-06 2.93320925e-07 -2.47118034e-07 + 1.70000000e+00 1.00010798e+00 -2.49657594e-06 -6.55823390e-06 5.06147285e-05 -1.85769094e-06 2.62331098e-07 -2.47153210e-07 + 1.80000000e+00 1.00010875e+00 -2.49367255e-06 -6.59283841e-06 5.09433148e-05 -1.90627942e-06 2.33567699e-07 -2.45649217e-07 + 1.90000000e+00 1.00010952e+00 -2.49122321e-06 -6.62617924e-06 5.12683287e-05 -1.95027800e-06 2.06746504e-07 -2.43108237e-07 + 2.00000000e+00 1.00011028e+00 -2.48972424e-06 -6.65859976e-06 5.15901277e-05 -1.99057668e-06 1.81615424e-07 -2.40118877e-07 + 2.10000000e+00 1.00011104e+00 -2.48961745e-06 -6.69063659e-06 5.19092121e-05 -2.02783143e-06 1.57505448e-07 -2.37263411e-07 + 2.30000000e+00 1.00011254e+00 -2.49524465e-06 -6.75426325e-06 5.25407535e-05 -2.09387098e-06 1.11440102e-07 -2.31113067e-07 + 2.50000000e+00 1.00011402e+00 -2.50414512e-06 -6.81877574e-06 5.31673337e-05 -2.15234117e-06 6.88426192e-08 -2.25286625e-07 + 2.70000000e+00 1.00011549e+00 -2.51599357e-06 -6.88460526e-06 5.37897935e-05 -2.20455950e-06 2.92882350e-08 -2.22239057e-07 + 2.90000000e+00 1.00011694e+00 -2.53246576e-06 -6.95098250e-06 5.44093590e-05 -2.25149996e-06 -7.43484545e-09 -2.22196341e-07 + 3.10000000e+00 1.00011838e+00 -2.55500053e-06 -7.01706321e-06 5.50259158e-05 -2.29412803e-06 -4.10465371e-08 -2.23028215e-07 + 3.30000000e+00 1.00011981e+00 -2.58074574e-06 -7.08293035e-06 5.56395167e-05 -2.33396345e-06 -7.24252887e-08 -2.23365117e-07 + 3.70000000e+00 1.00012270e+00 -2.63456745e-06 -7.21941309e-06 5.68588351e-05 -2.41026544e-06 -1.29502326e-07 -2.24151156e-07 + 4.10000000e+00 1.00012556e+00 -2.68965334e-06 -7.35818845e-06 5.80739909e-05 -2.48315739e-06 -1.79867847e-07 -2.24163564e-07 + 4.50000000e+00 1.00012840e+00 -2.74806914e-06 -7.49327487e-06 5.92844259e-05 -2.55345360e-06 -2.25577391e-07 -2.22157851e-07 + 4.90000000e+00 1.00013122e+00 -2.80756435e-06 -7.62820366e-06 6.04895325e-05 -2.61986219e-06 -2.66710839e-07 -2.20200966e-07 + 5.10000000e+00 1.00013259e+00 -2.83611549e-06 -7.69799718e-06 6.10916299e-05 -2.65173590e-06 -2.86547310e-07 -2.20212717e-07 + 5.70000000e+00 1.00013685e+00 -2.90968155e-06 -7.92081790e-06 6.28850790e-05 -2.74339246e-06 -3.42444850e-07 -2.27989288e-07 + 6.20000000e+00 1.00014034e+00 -2.96916656e-06 -8.11126558e-06 6.43700420e-05 -2.81894963e-06 -3.86728487e-07 -2.37205631e-07 + 6.70000000e+00 1.00014381e+00 -3.02619174e-06 -8.30258391e-06 6.58468789e-05 -2.89278903e-06 -4.29430865e-07 -2.48753681e-07 + 7.45000000e+00 1.00014910e+00 -3.10699430e-06 -8.59015008e-06 6.80430615e-05 -2.99971712e-06 -4.93399113e-07 -2.68510074e-07 + 8.20000000e+00 1.00015434e+00 -3.19148307e-06 -8.87163941e-06 7.02232368e-05 -3.09682511e-06 -5.55451758e-07 -2.85727990e-07 + 8.95000000e+00 1.00015955e+00 -3.27926313e-06 -9.14645506e-06 7.23881410e-05 -3.18769835e-06 -6.14154739e-07 -2.98703303e-07 + 9.70000000e+00 1.00016472e+00 -3.36739546e-06 -9.41549126e-06 7.45376644e-05 -3.27590327e-06 -6.69136583e-07 -3.09566084e-07 + 1.07000000e+01 1.00017172e+00 -3.48059868e-06 -9.77200246e-06 7.73735973e-05 -3.38967669e-06 -7.38643674e-07 -3.24963766e-07 diff --git a/test/data/test_results/multi_material_test/avg_elastic_strain_region_material_A_1.txt b/test/data/test_results/multi_material_test/avg_elastic_strain_region_material_A_1.txt new file mode 100644 index 0000000..6d0ce16 --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_elastic_strain_region_material_A_1.txt @@ -0,0 +1,41 @@ + # Time Volume Ee11 Ee22 Ee33 Ee23 Ee13 Ee12 + 5.00000000e-03 5.04000802e-01 -6.01112210e-08 -6.58281752e-08 7.08580027e-07 2.25497044e-08 -1.84252060e-08 9.85225735e-09 + 2.00000000e-01 5.04032032e-01 -2.41872116e-06 -2.64931296e-06 2.82991186e-05 8.98527647e-07 -7.38255751e-07 3.93503589e-07 + 3.00000000e-01 5.04042715e-01 -2.66532404e-06 -4.15854023e-06 3.75995403e-05 1.15743943e-06 -1.03667844e-06 2.25206487e-07 + 4.00000000e-01 5.04046152e-01 -2.26588610e-06 -5.43147492e-06 4.04105100e-05 1.01390814e-06 -1.23204118e-06 3.89456863e-08 + 5.00000000e-01 5.04047682e-01 -1.90196849e-06 -6.15194105e-06 4.15546635e-05 6.57661167e-07 -1.29318653e-06 -2.31206435e-07 + 6.00000000e-01 5.04048696e-01 -1.47569904e-06 -6.51294788e-06 4.22560801e-05 2.72326010e-07 -1.30112992e-06 -5.59324423e-07 + 7.00000000e-01 5.04049485e-01 -1.12180200e-06 -6.74718770e-06 4.28199742e-05 -6.21174209e-08 -1.32049404e-06 -8.60870999e-07 + 8.00000000e-01 5.04050148e-01 -8.81570091e-07 -6.92483285e-06 4.33167402e-05 -3.45252014e-07 -1.36072783e-06 -1.11003045e-06 + 9.00000000e-01 5.04050728e-01 -7.31387718e-07 -7.06867863e-06 4.37664504e-05 -5.95719497e-07 -1.41537042e-06 -1.30793828e-06 + 1.00000000e+00 5.04051253e-01 -6.43166144e-07 -7.18012165e-06 4.41820742e-05 -8.21553641e-07 -1.47512413e-06 -1.46276920e-06 + 1.10000000e+00 5.04051738e-01 -5.94881704e-07 -7.26665757e-06 4.45703453e-05 -1.02092053e-06 -1.53454924e-06 -1.58287885e-06 + 1.20000000e+00 5.04052196e-01 -5.66039363e-07 -7.33874831e-06 4.49351116e-05 -1.19332305e-06 -1.58928706e-06 -1.67911122e-06 + 1.30000000e+00 5.04052633e-01 -5.47572909e-07 -7.40080217e-06 4.52819966e-05 -1.34433445e-06 -1.63917615e-06 -1.75887388e-06 + 1.40000000e+00 5.04053056e-01 -5.37093795e-07 -7.45403949e-06 4.56168733e-05 -1.47897306e-06 -1.68570731e-06 -1.82515137e-06 + 1.50000000e+00 5.04053468e-01 -5.30266565e-07 -7.50081270e-06 4.59428650e-05 -1.59989619e-06 -1.72925082e-06 -1.88016966e-06 + 1.60000000e+00 5.04053873e-01 -5.22872327e-07 -7.54284641e-06 4.62624880e-05 -1.70854742e-06 -1.77007724e-06 -1.92751186e-06 + 1.70000000e+00 5.04054272e-01 -5.13968178e-07 -7.58055954e-06 4.65768431e-05 -1.80527204e-06 -1.80850366e-06 -1.96861263e-06 + 1.80000000e+00 5.04054666e-01 -5.04312868e-07 -7.61465100e-06 4.68856607e-05 -1.89270240e-06 -1.84510235e-06 -2.00431395e-06 + 1.90000000e+00 5.04055056e-01 -4.94242600e-07 -7.64608368e-06 4.71889539e-05 -1.97328688e-06 -1.88005814e-06 -2.03635958e-06 + 2.00000000e+00 5.04055442e-01 -4.83572839e-07 -7.67631701e-06 4.74873415e-05 -2.04793150e-06 -1.91325174e-06 -2.06622936e-06 + 2.10000000e+00 5.04055825e-01 -4.72793050e-07 -7.70657508e-06 4.77820455e-05 -2.11709317e-06 -1.94501584e-06 -2.09424151e-06 + 2.30000000e+00 5.04056587e-01 -4.55705897e-07 -7.76693171e-06 4.83644229e-05 -2.23967444e-06 -2.00587598e-06 -2.14195633e-06 + 2.50000000e+00 5.04057341e-01 -4.43612669e-07 -7.82648821e-06 4.89420612e-05 -2.34895628e-06 -2.06318468e-06 -2.18429941e-06 + 2.70000000e+00 5.04058086e-01 -4.34569466e-07 -7.88678888e-06 4.95164506e-05 -2.44676555e-06 -2.11725971e-06 -2.22806963e-06 + 2.90000000e+00 5.04058825e-01 -4.29154729e-07 -7.94873203e-06 5.00890176e-05 -2.53516500e-06 -2.16916338e-06 -2.27419114e-06 + 3.10000000e+00 5.04059559e-01 -4.30878481e-07 -8.01027203e-06 5.06597131e-05 -2.61563192e-06 -2.21834885e-06 -2.31915295e-06 + 3.30000000e+00 5.04060287e-01 -4.35641685e-07 -8.07195125e-06 5.12279790e-05 -2.68979916e-06 -2.26541980e-06 -2.36087672e-06 + 3.70000000e+00 5.04061758e-01 -4.41106118e-07 -8.20651087e-06 5.23577302e-05 -2.82623962e-06 -2.35757639e-06 -2.43868149e-06 + 4.10000000e+00 5.04063214e-01 -4.44288308e-07 -8.34754708e-06 5.34866790e-05 -2.95263102e-06 -2.44736637e-06 -2.50808927e-06 + 4.50000000e+00 5.04064657e-01 -4.49854991e-07 -8.48214416e-06 5.46131369e-05 -3.07360512e-06 -2.53423634e-06 -2.56490426e-06 + 4.90000000e+00 5.04066091e-01 -4.55081605e-07 -8.61606002e-06 5.57356908e-05 -3.18736560e-06 -2.61776494e-06 -2.61606446e-06 + 5.10000000e+00 5.04066791e-01 -4.55473588e-07 -8.68789730e-06 5.62972383e-05 -3.24147541e-06 -2.65930070e-06 -2.64263045e-06 + 5.70000000e+00 5.04068958e-01 -4.40046452e-07 -8.92814076e-06 5.79698171e-05 -3.39210768e-06 -2.77909680e-06 -2.73113731e-06 + 6.20000000e+00 5.04070733e-01 -4.27042732e-07 -9.13887247e-06 5.93539549e-05 -3.51486619e-06 -2.87381871e-06 -2.80977882e-06 + 6.70000000e+00 5.04072496e-01 -4.14459406e-07 -9.35158205e-06 6.07307496e-05 -3.63315784e-06 -2.96429845e-06 -2.89124224e-06 + 7.45000000e+00 5.04075184e-01 -3.93450684e-07 -9.67351691e-06 6.27799323e-05 -3.80188153e-06 -3.09769867e-06 -3.01565660e-06 + 8.20000000e+00 5.04077847e-01 -3.83621196e-07 -9.98960068e-06 6.48166806e-05 -3.95200651e-06 -3.22620720e-06 -3.13728502e-06 + 8.95000000e+00 5.04080491e-01 -3.81977673e-07 -1.02973014e-05 6.68418339e-05 -4.09151823e-06 -3.35003488e-06 -3.25322649e-06 + 9.70000000e+00 5.04083115e-01 -3.81145234e-07 -1.05991365e-05 6.88552982e-05 -4.22718416e-06 -3.47009684e-06 -3.36642592e-06 + 1.07000000e+01 5.04086670e-01 -3.69800904e-07 -1.10058903e-05 7.15149377e-05 -4.40308510e-06 -3.62736128e-06 -3.51915872e-06 diff --git a/test/data/test_results/multi_material_test/avg_elastic_strain_region_material_B_2.txt b/test/data/test_results/multi_material_test/avg_elastic_strain_region_material_B_2.txt new file mode 100644 index 0000000..862ccd1 --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_elastic_strain_region_material_B_2.txt @@ -0,0 +1,41 @@ + # Time Volume Ee11 Ee22 Ee33 Ee23 Ee13 Ee12 + 5.00000000e-03 4.96000786e-01 -6.82263968e-08 -8.67078119e-08 8.00936419e-07 -2.36704015e-08 4.57480922e-08 -2.41005848e-08 + 2.00000000e-01 4.96031390e-01 -2.73708043e-06 -3.46764407e-06 3.19611141e-05 -9.63938968e-07 1.82343667e-06 -9.45079322e-07 + 3.00000000e-01 4.96041896e-01 -4.07708723e-06 -4.72466009e-06 4.31356081e-05 -1.27232403e-06 2.47294708e-06 -8.74201548e-07 + 4.00000000e-01 4.96045528e-01 -4.54489250e-06 -4.85993321e-06 4.71656356e-05 -1.33016272e-06 2.76579825e-06 -4.79092026e-07 + 5.00000000e-01 4.96047229e-01 -4.56951735e-06 -4.87375948e-06 4.88747554e-05 -1.39153608e-06 2.84867331e-06 -5.85339384e-09 + 6.00000000e-01 4.96048280e-01 -4.56855820e-06 -4.94571249e-06 4.98976599e-05 -1.46040254e-06 2.85912407e-06 3.80514973e-07 + 7.00000000e-01 4.96049058e-01 -4.57515923e-06 -5.00669712e-06 5.06338068e-05 -1.54016832e-06 2.83059461e-06 6.62735935e-07 + 8.00000000e-01 4.96049693e-01 -4.59546276e-06 -5.05893756e-06 5.12102585e-05 -1.61533512e-06 2.77740535e-06 8.57594148e-07 + 9.00000000e-01 4.96050246e-01 -4.60834337e-06 -5.11423870e-06 5.16848223e-05 -1.67828558e-06 2.70836030e-06 9.95801951e-07 + 1.00000000e+00 4.96050749e-01 -4.60240259e-06 -5.17521778e-06 5.21068141e-05 -1.72814629e-06 2.63912747e-06 1.10069457e-06 + 1.10000000e+00 4.96051217e-01 -4.58277353e-06 -5.23307821e-06 5.25039683e-05 -1.76929955e-06 2.57746571e-06 1.18425028e-06 + 1.20000000e+00 4.96051663e-01 -4.55949270e-06 -5.28990540e-06 5.28918766e-05 -1.80438269e-06 2.52641648e-06 1.25145381e-06 + 1.30000000e+00 4.96052093e-01 -4.54073407e-06 -5.34344928e-06 5.32726208e-05 -1.83460570e-06 2.48370835e-06 1.31122836e-06 + 1.40000000e+00 4.96052509e-01 -4.52625570e-06 -5.39407824e-06 5.36452659e-05 -1.86024932e-06 2.44773267e-06 1.36635215e-06 + 1.50000000e+00 4.96052916e-01 -4.51477888e-06 -5.44128102e-06 5.40094635e-05 -1.88179098e-06 2.41679659e-06 1.41567898e-06 + 1.60000000e+00 4.96053313e-01 -4.50970716e-06 -5.48288463e-06 5.43662066e-05 -1.89861250e-06 2.38999846e-06 1.46037790e-06 + 1.70000000e+00 4.96053703e-01 -4.51116005e-06 -5.51941976e-06 5.47177386e-05 -1.91095527e-06 2.36656518e-06 1.50207065e-06 + 1.80000000e+00 4.96054087e-01 -4.51511750e-06 -5.55454558e-06 5.50664124e-05 -1.92007542e-06 2.34576346e-06 1.54138004e-06 + 1.90000000e+00 4.96054466e-01 -4.52041203e-06 -5.58982532e-06 5.54134975e-05 -1.92689803e-06 2.32720807e-06 1.57906547e-06 + 2.00000000e+00 4.96054840e-01 -4.52823177e-06 -5.62446830e-06 5.57590856e-05 -1.93229681e-06 2.31026958e-06 1.61544397e-06 + 2.10000000e+00 4.96055211e-01 -4.53897014e-06 -5.65831255e-06 5.61029435e-05 -1.93713003e-06 2.29393720e-06 1.64966491e-06 + 2.30000000e+00 4.96055950e-01 -4.56767809e-06 -5.72526198e-06 5.67844421e-05 -1.94571592e-06 2.26290531e-06 1.71054922e-06 + 2.50000000e+00 4.96056679e-01 -4.59791091e-06 -5.79481036e-06 5.74607537e-05 -1.95255494e-06 2.23525637e-06 1.76532214e-06 + 2.70000000e+00 4.96057400e-01 -4.63098803e-06 -5.86625787e-06 5.81320593e-05 -1.95844702e-06 2.21045688e-06 1.81594265e-06 + 2.90000000e+00 4.96058115e-01 -4.66970020e-06 -5.93714069e-06 5.87993813e-05 -1.96325981e-06 2.18915930e-06 1.86289421e-06 + 3.10000000e+00 4.96058823e-01 -4.71338170e-06 -6.00783533e-06 5.94625392e-05 -1.96743873e-06 2.17137262e-06 1.90690408e-06 + 3.30000000e+00 4.96059526e-01 -4.76044739e-06 -6.07795790e-06 6.01222065e-05 -1.97238859e-06 2.15593922e-06 1.94862162e-06 + 3.70000000e+00 4.96060945e-01 -4.86340643e-06 -6.21639471e-06 6.14325369e-05 -1.98758214e-06 2.13450765e-06 2.02609665e-06 + 4.10000000e+00 4.96062350e-01 -4.97123328e-06 -6.35287270e-06 6.27352903e-05 -2.00611174e-06 2.12420258e-06 2.09659900e-06 + 4.50000000e+00 4.96063744e-01 -5.08335073e-06 -6.48845631e-06 6.40310571e-05 -2.02491267e-06 2.12031746e-06 2.15837425e-06 + 4.90000000e+00 4.96065127e-01 -5.19798995e-06 -6.62441432e-06 6.53200483e-05 -2.04320559e-06 2.12226308e-06 2.21430507e-06 + 5.10000000e+00 4.96065800e-01 -5.25515450e-06 -6.69213108e-06 6.59633497e-05 -2.05248455e-06 2.12447596e-06 2.24127593e-06 + 5.70000000e+00 4.96067893e-01 -5.41914931e-06 -6.89724795e-06 6.78796189e-05 -2.08421414e-06 2.13350778e-06 2.31553193e-06 + 6.20000000e+00 4.96069607e-01 -5.55229240e-06 -7.06708438e-06 6.94670337e-05 -2.11180862e-06 2.14047611e-06 2.37686069e-06 + 6.70000000e+00 4.96071310e-01 -5.68004898e-06 -7.23666637e-06 7.10455268e-05 -2.14047873e-06 2.14632186e-06 2.43635585e-06 + 7.45000000e+00 4.96073914e-01 -5.86430517e-06 -7.48930942e-06 7.33910808e-05 -2.18461445e-06 2.15290569e-06 2.52294570e-06 + 8.20000000e+00 4.96076497e-01 -6.04463371e-06 -7.73564625e-06 7.57169967e-05 -2.22785027e-06 2.15838101e-06 2.61182257e-06 + 8.95000000e+00 4.96079059e-01 -6.22327990e-06 -7.97704631e-06 7.80239064e-05 -2.26930045e-06 2.16585336e-06 2.70347441e-06 + 9.70000000e+00 4.96081601e-01 -6.40181221e-06 -8.21275445e-06 8.03116839e-05 -2.30927877e-06 2.17700157e-06 2.79659916e-06 + 1.07000000e+01 4.96085050e-01 -6.64157218e-06 -8.51821258e-06 8.33267543e-05 -2.35992248e-06 2.19666759e-06 2.92075206e-06 diff --git a/test/data/test_results/multi_material_test/avg_euler_strain_global.txt b/test/data/test_results/multi_material_test/avg_euler_strain_global.txt new file mode 100644 index 0000000..40f7ba3 --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_euler_strain_global.txt @@ -0,0 +1,41 @@ + # Time Volume E11 E22 E33 E23 E13 E12 + 5.00000000e-03 1.00000159e+00 -1.63348825e-06 -1.77847722e-06 4.99996254e-06 1.10837826e-09 -9.90119304e-08 -7.40343176e-09 + 2.00000000e-01 1.00006342e+00 -6.53528731e-05 -7.12165236e-05 1.99941049e-04 4.11230397e-09 -3.98395071e-06 -2.68144338e-07 + 3.00000000e-01 1.00008461e+00 -1.01835639e-04 -1.13554690e-04 2.99886087e-04 -6.06542461e-07 -6.13848794e-06 -4.72024707e-07 + 4.00000000e-01 1.00009168e+00 -1.43207413e-04 -1.65133197e-04 3.99810999e-04 -2.06953741e-06 -8.55426812e-06 -1.21921788e-06 + 5.00000000e-01 1.00009491e+00 -1.85478723e-04 -2.19657253e-04 4.99715927e-04 -3.16478724e-06 -1.02299576e-05 -2.25091048e-06 + 6.00000000e-01 1.00009698e+00 -2.27822483e-04 -2.75286123e-04 5.99600870e-04 -3.77561683e-06 -1.14213063e-05 -3.43082096e-06 + 7.00000000e-01 1.00009854e+00 -2.70277817e-04 -3.31310539e-04 6.99465797e-04 -4.14175101e-06 -1.24395637e-05 -4.63683357e-06 + 8.00000000e-01 1.00009984e+00 -3.12857850e-04 -3.87489910e-04 7.99310681e-04 -4.35901927e-06 -1.34159611e-05 -5.74883277e-06 + 9.00000000e-01 1.00010097e+00 -3.55487085e-04 -4.43792616e-04 8.99135520e-04 -4.48530064e-06 -1.43588937e-05 -6.72494072e-06 + 1.00000000e+00 1.00010200e+00 -3.98121015e-04 -5.00207575e-04 9.98940324e-04 -4.54402502e-06 -1.52613183e-05 -7.58695752e-06 + 1.10000000e+00 1.00010296e+00 -4.40758346e-04 -5.56701140e-04 1.09872510e-03 -4.54677492e-06 -1.61210907e-05 -8.34417613e-06 + 1.20000000e+00 1.00010386e+00 -4.83407186e-04 -6.13243430e-04 1.19848984e-03 -4.51703085e-06 -1.69497183e-05 -9.02221335e-06 + 1.30000000e+00 1.00010473e+00 -5.26064910e-04 -6.69823003e-04 1.29823456e-03 -4.47021703e-06 -1.77500832e-05 -9.67076687e-06 + 1.40000000e+00 1.00010557e+00 -5.68730744e-04 -7.26431343e-04 1.39795926e-03 -4.40995495e-06 -1.85276724e-05 -1.03227589e-05 + 1.50000000e+00 1.00010638e+00 -6.11398469e-04 -7.83068388e-04 1.49766394e-03 -4.33815628e-06 -1.92895726e-05 -1.09847173e-05 + 1.60000000e+00 1.00010719e+00 -6.54065056e-04 -8.39732611e-04 1.59734861e-03 -4.25536500e-06 -2.00412127e-05 -1.16539550e-05 + 1.70000000e+00 1.00010798e+00 -6.96731885e-04 -8.96419295e-04 1.69701327e-03 -4.16254335e-06 -2.07866198e-05 -1.23343997e-05 + 1.80000000e+00 1.00010875e+00 -7.39407972e-04 -9.53117277e-04 1.79665793e-03 -4.06144941e-06 -2.15258853e-05 -1.30276283e-05 + 1.90000000e+00 1.00010952e+00 -7.82095771e-04 -1.00982246e-03 1.89628260e-03 -3.95168295e-06 -2.22579838e-05 -1.37396401e-05 + 2.00000000e+00 1.00011028e+00 -8.24786400e-04 -1.06654235e-03 1.99588727e-03 -3.83266377e-06 -2.29845477e-05 -1.44803041e-05 + 2.10000000e+00 1.00011104e+00 -8.67470025e-04 -1.12328580e-03 2.09547196e-03 -3.70472880e-06 -2.37077241e-05 -1.52510780e-05 + 2.30000000e+00 1.00011254e+00 -9.52782403e-04 -1.23685284e-03 2.29457141e-03 -3.44909337e-06 -2.51358329e-05 -1.68880769e-05 + 2.50000000e+00 1.00011402e+00 -1.03807456e-03 -1.35049749e-03 2.49359096e-03 -3.24290533e-06 -2.65500219e-05 -1.85991755e-05 + 2.70000000e+00 1.00011549e+00 -1.12334663e-03 -1.46421671e-03 2.69253064e-03 -3.08806818e-06 -2.79667321e-05 -2.03475431e-05 + 2.90000000e+00 1.00011694e+00 -1.20859711e-03 -1.57800931e-03 2.89139050e-03 -2.94232628e-06 -2.93921387e-05 -2.21139192e-05 + 3.10000000e+00 1.00011838e+00 -1.29381411e-03 -1.69188560e-03 3.09017057e-03 -2.77490868e-06 -3.08240939e-05 -2.39125740e-05 + 3.30000000e+00 1.00011981e+00 -1.37898742e-03 -1.80585454e-03 3.28887089e-03 -2.57154464e-06 -3.22545117e-05 -2.57484084e-05 + 3.70000000e+00 1.00012270e+00 -1.54906914e-03 -2.03410736e-03 3.68599275e-03 -1.99614650e-06 -3.50923115e-05 -2.94760754e-05 + 4.10000000e+00 1.00012556e+00 -1.71890901e-03 -2.26278572e-03 4.08279611e-03 -1.21960007e-06 -3.79220254e-05 -3.31567151e-05 + 4.50000000e+00 1.00012840e+00 -1.88852786e-03 -2.49186526e-03 4.47928125e-03 -2.35023667e-07 -4.07906761e-05 -3.67350963e-05 + 4.90000000e+00 1.00013122e+00 -2.05797790e-03 -2.72129131e-03 4.87544841e-03 9.13831008e-07 -4.37269427e-05 -4.01996297e-05 + 5.10000000e+00 1.00013259e+00 -2.14269440e-03 -2.83612585e-03 5.07343262e-03 1.52047672e-06 -4.52064694e-05 -4.19079900e-05 + 5.70000000e+00 1.00013685e+00 -2.39642161e-03 -3.18102820e-03 5.66679035e-03 3.54591632e-06 -4.95614250e-05 -4.69053022e-05 + 6.20000000e+00 1.00014034e+00 -2.60773688e-03 -3.46892743e-03 6.16073465e-03 5.32687289e-06 -5.30828739e-05 -5.10477894e-05 + 6.70000000e+00 1.00014381e+00 -2.81890406e-03 -3.75724306e-03 6.65418386e-03 7.19575272e-06 -5.64989502e-05 -5.51626038e-05 + 7.45000000e+00 1.00014910e+00 -3.13529106e-03 -4.19035560e-03 7.39333823e-03 1.01378298e-05 -6.13866365e-05 -6.12752834e-05 + 8.20000000e+00 1.00015434e+00 -3.45153242e-03 -4.62421394e-03 8.13138169e-03 1.32141886e-05 -6.61275535e-05 -6.73308087e-05 + 8.95000000e+00 1.00015955e+00 -3.76783884e-03 -5.05860552e-03 8.86831596e-03 1.64249232e-05 -7.07654660e-05 -7.33000973e-05 + 9.70000000e+00 1.00016472e+00 -4.08431583e-03 -5.49342328e-03 9.60414281e-03 1.97361236e-05 -7.52986639e-05 -7.91768212e-05 + 1.07000000e+01 1.00017172e+00 -4.50641811e-03 -6.07367039e-03 1.05834035e-02 2.42513675e-05 -8.12031698e-05 -8.68643525e-05 diff --git a/test/data/test_results/multi_material_test/avg_euler_strain_region_material_A_1.txt b/test/data/test_results/multi_material_test/avg_euler_strain_region_material_A_1.txt new file mode 100644 index 0000000..1982162 --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_euler_strain_region_material_A_1.txt @@ -0,0 +1,41 @@ + # Time Volume E11 E22 E33 E23 E13 E12 + 5.00000000e-03 5.04000802e-01 -1.60090169e-06 -1.76633185e-06 4.95931375e-06 1.18121345e-08 -3.07473263e-07 -4.78258472e-08 + 2.00000000e-01 5.04032032e-01 -6.40500523e-05 -7.06367931e-05 1.98192520e-04 4.30444486e-07 -1.23310147e-05 -1.89689487e-06 + 3.00000000e-01 5.04042715e-01 -1.00464427e-04 -1.11063061e-04 2.96167747e-04 -1.45570368e-07 -1.92866395e-05 -3.08956124e-06 + 4.00000000e-01 5.04046152e-01 -1.41977741e-04 -1.58851421e-04 3.92198428e-04 -2.05676909e-06 -2.51297138e-05 -5.88714822e-06 + 5.00000000e-01 5.04047682e-01 -1.84912081e-04 -2.09811649e-04 4.89012870e-04 -4.18671712e-06 -2.94001857e-05 -9.65804537e-06 + 6.00000000e-01 5.04048696e-01 -2.28101359e-04 -2.62688143e-04 5.86944498e-04 -6.13693594e-06 -3.32737692e-05 -1.36043565e-05 + 7.00000000e-01 5.04049485e-01 -2.71427927e-04 -3.16401178e-04 6.85373786e-04 -8.03759450e-06 -3.72022933e-05 -1.74873460e-05 + 8.00000000e-01 5.04050148e-01 -3.14857286e-04 -3.70433828e-04 7.83944958e-04 -9.85293305e-06 -4.13718992e-05 -2.12099306e-05 + 9.00000000e-01 5.04050728e-01 -3.58334669e-04 -4.24627258e-04 8.82533190e-04 -1.15876169e-05 -4.57458052e-05 -2.47451024e-05 + 1.00000000e+00 5.04051253e-01 -4.01835952e-04 -4.78981117e-04 9.81165830e-04 -1.32383407e-05 -5.02163646e-05 -2.81329329e-05 + 1.10000000e+00 5.04051738e-01 -4.45336714e-04 -5.33488438e-04 1.07984564e-03 -1.48116241e-05 -5.47253756e-05 -3.14022982e-05 + 1.20000000e+00 5.04052196e-01 -4.88819705e-04 -5.88095537e-04 1.17852215e-03 -1.63336310e-05 -5.92653935e-05 -3.45933212e-05 + 1.30000000e+00 5.04052633e-01 -5.32287079e-04 -6.42771284e-04 1.27718269e-03 -1.78155289e-05 -6.38205397e-05 -3.77650536e-05 + 1.40000000e+00 5.04053056e-01 -5.75740291e-04 -6.97510263e-04 1.37583477e-03 -1.92555066e-05 -6.83828566e-05 -4.09538240e-05 + 1.50000000e+00 5.04053468e-01 -6.19166228e-04 -7.52309985e-04 1.47447020e-03 -2.06516185e-05 -7.29456635e-05 -4.41717775e-05 + 1.60000000e+00 5.04053873e-01 -6.62554436e-04 -8.07163679e-04 1.57307795e-03 -2.20041385e-05 -7.75041172e-05 -4.74208424e-05 + 1.70000000e+00 5.04054272e-01 -7.05905828e-04 -8.62064367e-04 1.67165576e-03 -2.33189856e-05 -8.20556486e-05 -5.07073255e-05 + 1.80000000e+00 5.04054666e-01 -7.49231470e-04 -9.16998749e-04 1.77020305e-03 -2.46084087e-05 -8.65938131e-05 -5.40365057e-05 + 1.90000000e+00 5.04055056e-01 -7.92536830e-04 -9.71963542e-04 1.86872294e-03 -2.58796297e-05 -9.11164439e-05 -5.74175510e-05 + 2.00000000e+00 5.04055442e-01 -8.35816749e-04 -1.02696676e-03 1.96721914e-03 -2.71331709e-05 -9.56287037e-05 -6.08588322e-05 + 2.10000000e+00 5.04055825e-01 -8.79063453e-04 -1.08201609e-03 2.06569253e-03 -2.83679665e-05 -1.00136136e-04 -6.43595633e-05 + 2.30000000e+00 5.04056587e-01 -9.65401903e-04 -1.19223878e-03 2.26251582e-03 -3.07877724e-05 -1.09127756e-04 -7.15622267e-05 + 2.50000000e+00 5.04057341e-01 -1.05162450e-03 -1.30253882e-03 2.45916764e-03 -3.32033306e-05 -1.18089080e-04 -7.89210174e-05 + 2.70000000e+00 5.04058086e-01 -1.13772938e-03 -1.41292279e-03 2.65565536e-03 -3.56311489e-05 -1.27025987e-04 -8.63702738e-05 + 2.90000000e+00 5.04058825e-01 -1.22371747e-03 -1.52342792e-03 2.85201860e-03 -3.80402518e-05 -1.35942346e-04 -9.38675059e-05 + 3.10000000e+00 5.04059559e-01 -1.30958822e-03 -1.63408358e-03 3.04828805e-03 -4.04082274e-05 -1.44836732e-04 -1.01425050e-04 + 3.30000000e+00 5.04060287e-01 -1.39535493e-03 -1.74490608e-03 3.24449494e-03 -4.27278918e-05 -1.53711705e-04 -1.09051482e-04 + 3.70000000e+00 5.04061758e-01 -1.56646830e-03 -1.96716280e-03 3.63677964e-03 -4.70898860e-05 -1.71396080e-04 -1.24389241e-04 + 4.10000000e+00 5.04063214e-01 -1.73719406e-03 -2.19014301e-03 4.02890562e-03 -5.10440095e-05 -1.89027405e-04 -1.39598111e-04 + 4.50000000e+00 5.04064657e-01 -1.90750335e-03 -2.41384119e-03 4.42084244e-03 -5.45539917e-05 -2.06667304e-04 -1.54613692e-04 + 4.90000000e+00 5.04066091e-01 -2.07743613e-03 -2.63813441e-03 4.81251277e-03 -5.76911034e-05 -2.24355249e-04 -1.69420697e-04 + 5.10000000e+00 5.04066791e-01 -2.16235224e-03 -2.75045643e-03 5.00826535e-03 -5.91856285e-05 -2.33208897e-04 -1.76776693e-04 + 5.70000000e+00 5.04068958e-01 -2.41649554e-03 -3.08823778e-03 5.59516684e-03 -6.31775441e-05 -2.59686609e-04 -1.98547555e-04 + 6.20000000e+00 5.04070733e-01 -2.62810574e-03 -3.37037350e-03 6.08386389e-03 -6.62828021e-05 -2.81681213e-04 -2.16617505e-04 + 6.70000000e+00 5.04072496e-01 -2.83956465e-03 -3.65303836e-03 6.57218628e-03 -6.91811299e-05 -3.03599394e-04 -2.34619807e-04 + 7.45000000e+00 5.04075184e-01 -3.15647290e-03 -4.07778693e-03 7.30386283e-03 -7.31142760e-05 -3.36287015e-04 -2.61477319e-04 + 8.20000000e+00 5.04077847e-01 -3.47323714e-03 -4.50328985e-03 8.03446592e-03 -7.66394225e-05 -3.68869905e-04 -2.88220714e-04 + 8.95000000e+00 5.04080491e-01 -3.79004807e-03 -4.92935494e-03 8.76400438e-03 -7.98373508e-05 -4.01326109e-04 -3.14860337e-04 + 9.70000000e+00 5.04083115e-01 -4.10704080e-03 -5.35591482e-03 9.49254713e-03 -8.27864367e-05 -4.33608986e-04 -3.41331739e-04 + 1.07000000e+01 5.04086670e-01 -4.52982555e-03 -5.92529233e-03 1.04622711e-02 -8.64453868e-05 -4.76322311e-04 -3.76326388e-04 diff --git a/test/data/test_results/multi_material_test/avg_euler_strain_region_material_B_2.txt b/test/data/test_results/multi_material_test/avg_euler_strain_region_material_B_2.txt new file mode 100644 index 0000000..de1f026 --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_euler_strain_region_material_B_2.txt @@ -0,0 +1,41 @@ + # Time Volume E11 E22 E33 E23 E13 E12 + 5.00000000e-03 4.96000786e-01 -1.66660040e-06 -1.79081849e-06 5.04126696e-06 -9.76801933e-09 1.12811683e-07 3.36709585e-08 + 2.00000000e-01 4.96031390e-01 -6.66767074e-05 -7.18056047e-05 2.01717782e-04 -4.29096320e-07 4.49774564e-06 1.38687681e-06 + 3.00000000e-01 4.96041896e-01 -1.03228968e-04 -1.16086507e-04 3.03664400e-04 -1.07494972e-06 7.22173444e-06 2.18773092e-06 + 4.00000000e-01 4.96045528e-01 -1.44456919e-04 -1.71516291e-04 4.07546353e-04 -2.08251166e-06 8.28851973e-06 3.52400061e-06 + 5.00000000e-01 4.96047229e-01 -1.86054504e-04 -2.29661652e-04 5.10591608e-04 -2.12637526e-06 9.24945584e-06 5.27568970e-06 + 6.00000000e-01 4.96048280e-01 -2.27539108e-04 -2.88087287e-04 6.12461368e-04 -1.37621364e-06 1.07835997e-05 6.90679646e-06 + 7.00000000e-01 4.96049058e-01 -2.69109158e-04 -3.46460363e-04 7.13785088e-04 -1.83074197e-07 1.27225465e-05 8.42093571e-06 + 8.00000000e-01 4.96049693e-01 -3.10826166e-04 -4.04821077e-04 8.14924228e-04 1.22350218e-06 1.49908596e-05 9.96162679e-06 + 9.00000000e-01 4.96050246e-01 -3.52593574e-04 -4.63267079e-04 9.16005618e-04 2.73156438e-06 1.75342374e-05 1.15858568e-05 + 1.00000000e+00 4.96050749e-01 -3.94346163e-04 -5.21776382e-04 1.01700149e-03 4.29051607e-06 2.02574970e-05 1.32903915e-05 + 1.10000000e+00 4.96051217e-01 -4.36106136e-04 -5.80288226e-04 1.11790906e-03 5.88363000e-06 2.31058202e-05 1.50858370e-05 + 1.20000000e+00 4.96051663e-01 -4.77907371e-04 -6.38796919e-04 1.21877958e-03 7.49015247e-06 2.60484422e-05 1.69613163e-05 + 1.30000000e+00 4.96052093e-01 -5.19742388e-04 -6.97311023e-04 1.31962597e-03 9.09033377e-06 2.90634175e-05 1.88766365e-05 + 1.40000000e+00 4.96052509e-01 -5.61608143e-04 -7.55818874e-04 1.42044059e-03 1.06750321e-05 3.21315974e-05 2.08023371e-05 + 1.50000000e+00 4.96052916e-01 -6.03505429e-04 -8.14322875e-04 1.52123176e-03 1.22384165e-05 3.52319066e-05 2.27375979e-05 + 1.60000000e+00 4.96053313e-01 -6.45438756e-04 -8.72826828e-04 1.62201071e-03 1.37796683e-05 3.83484781e-05 2.46897960e-05 + 1.70000000e+00 4.96053703e-01 -6.87409980e-04 -9.31328313e-04 1.72277975e-03 1.53028623e-05 4.14705824e-05 2.66574213e-05 + 1.80000000e+00 4.96054087e-01 -7.29426037e-04 -9.89818340e-04 1.82353949e-03 1.68169004e-05 4.45914869e-05 2.86426583e-05 + 1.90000000e+00 4.96054466e-01 -7.71486313e-04 -1.04829199e-03 1.92428675e-03 1.83299277e-05 4.77110567e-05 3.06427279e-05 + 2.00000000e+00 4.96054840e-01 -8.13578150e-04 -1.10675622e-03 2.02501777e-03 1.98436447e-05 5.08312469e-05 3.26462383e-05 + 2.10000000e+00 4.96055211e-01 -8.55689612e-04 -1.16522112e-03 2.12573168e-03 2.13562893e-05 5.39533612e-05 3.46494522e-05 + 2.30000000e+00 4.96055950e-01 -9.39959370e-04 -1.28218646e-03 2.32714400e-03 2.43305174e-05 6.02107540e-05 3.86678848e-05 + 2.50000000e+00 4.96056679e-01 -1.02430608e-03 -1.39922966e-03 2.52856947e-03 2.72007373e-05 6.64654258e-05 4.26955685e-05 + 2.70000000e+00 4.96057400e-01 -1.10873191e-03 -1.51633793e-03 2.73000066e-03 2.99798853e-05 7.26902046e-05 4.67400385e-05 + 2.90000000e+00 4.96058115e-01 -1.19323289e-03 -1.63347103e-03 2.93139741e-03 3.27216788e-05 7.88765721e-05 5.07969508e-05 + 3.10000000e+00 4.96058823e-01 -1.27778558e-03 -1.75061988e-03 3.13272860e-03 3.54653829e-05 8.50274090e-05 5.48500694e-05 + 3.30000000e+00 4.96059526e-01 -1.36235593e-03 -1.86778602e-03 3.33396257e-03 3.82324694e-05 9.11616200e-05 5.88982295e-05 + 3.70000000e+00 4.96060945e-01 -1.53138935e-03 -2.10213164e-03 3.73599959e-03 4.38248959e-05 1.03409858e-04 6.69679156e-05 + 4.10000000e+00 4.96062350e-01 -1.70032905e-03 -2.33660007e-03 4.13755578e-03 4.94084146e-05 1.15620494e-04 7.50014472e-05 + 4.50000000e+00 4.96063744e-01 -1.86924632e-03 -2.57114776e-03 4.53866261e-03 5.49600441e-05 1.27761343e-04 8.30447392e-05 + 4.90000000e+00 4.96065127e-01 -2.03820584e-03 -2.80578942e-03 4.93939914e-03 6.04639961e-05 1.39814692e-04 9.11056256e-05 + 5.10000000e+00 4.96065800e-01 -2.12271951e-03 -2.92317703e-03 5.13965096e-03 6.32057040e-05 1.45828229e-04 9.51359957e-05 + 5.70000000e+00 4.96067893e-01 -2.37602391e-03 -3.27531524e-03 5.73956907e-03 7.13455574e-05 1.63952862e-04 1.07182784e-04 + 6.20000000e+00 4.96069607e-01 -2.58703948e-03 -3.56907095e-03 6.23884527e-03 7.80915432e-05 1.79202537e-04 1.17192406e-04 + 6.70000000e+00 4.96071310e-01 -2.79791023e-03 -3.86312849e-03 6.73750400e-03 8.48045260e-05 1.94587002e-04 1.27189084e-04 + 7.45000000e+00 4.96073914e-01 -3.11376758e-03 -4.30473992e-03 7.48425680e-03 9.47327246e-05 2.17947663e-04 1.42155849e-04 + 8.20000000e+00 4.96076497e-01 -3.42947763e-03 -4.74708845e-03 8.22986065e-03 1.04517073e-04 2.41497810e-04 1.57121888e-04 + 8.95000000e+00 4.96079059e-01 -3.74527138e-03 -5.18994083e-03 8.97431003e-03 1.14239845e-04 2.65126904e-04 1.72156351e-04 + 9.70000000e+00 4.96081601e-01 -4.06122431e-03 -5.63314967e-03 9.71753847e-03 1.23912315e-04 2.88791000e-04 1.87206506e-04 + 1.07000000e+01 4.96085050e-01 -4.48263312e-03 -6.22444173e-03 1.07064897e-02 1.36733609e-04 3.20289059e-04 2.07266571e-04 diff --git a/test/data/test_results/multi_material_test/avg_pl_work_global.txt b/test/data/test_results/multi_material_test/avg_pl_work_global.txt new file mode 100644 index 0000000..30de579 --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_pl_work_global.txt @@ -0,0 +1,41 @@ + # Time Volume Plastic_Work + 5.00000000e-03 1.00000159e+00 0.00000000e+00 + 2.00000000e-01 1.00006342e+00 8.61647526e-09 + 3.00000000e-01 1.00008461e+00 1.06470917e-06 + 4.00000000e-01 1.00009168e+00 3.78251372e-06 + 5.00000000e-01 1.00009491e+00 7.08549953e-06 + 6.00000000e-01 1.00009698e+00 1.06322194e-05 + 7.00000000e-01 1.00009854e+00 1.43193796e-05 + 8.00000000e-01 1.00009984e+00 1.81061318e-05 + 9.00000000e-01 1.00010097e+00 2.19698255e-05 + 1.00000000e+00 1.00010200e+00 2.58973447e-05 + 1.10000000e+00 1.00010296e+00 2.98792589e-05 + 1.20000000e+00 1.00010386e+00 3.39089746e-05 + 1.30000000e+00 1.00010473e+00 3.79817652e-05 + 1.40000000e+00 1.00010557e+00 4.20942650e-05 + 1.50000000e+00 1.00010638e+00 4.62440405e-05 + 1.60000000e+00 1.00010719e+00 5.04291820e-05 + 1.70000000e+00 1.00010798e+00 5.46481655e-05 + 1.80000000e+00 1.00010875e+00 5.88999742e-05 + 1.90000000e+00 1.00010952e+00 6.31837993e-05 + 2.00000000e+00 1.00011028e+00 6.74989230e-05 + 2.10000000e+00 1.00011104e+00 7.18448265e-05 + 2.30000000e+00 1.00011254e+00 8.06540846e-05 + 2.50000000e+00 1.00011402e+00 8.95809468e-05 + 2.70000000e+00 1.00011549e+00 9.86238758e-05 + 2.90000000e+00 1.00011694e+00 1.07781028e-04 + 3.10000000e+00 1.00011838e+00 1.17051238e-04 + 3.30000000e+00 1.00011981e+00 1.26433635e-04 + 3.70000000e+00 1.00012270e+00 1.45633465e-04 + 4.10000000e+00 1.00012556e+00 1.65268713e-04 + 4.50000000e+00 1.00012840e+00 1.85335328e-04 + 4.90000000e+00 1.00013122e+00 2.05829746e-04 + 5.10000000e+00 1.00013259e+00 2.16185968e-04 + 5.70000000e+00 1.00013685e+00 2.48184603e-04 + 6.20000000e+00 1.00014034e+00 2.75502986e-04 + 6.70000000e+00 1.00014381e+00 3.03466363e-04 + 7.45000000e+00 1.00014910e+00 3.46835725e-04 + 8.20000000e+00 1.00015434e+00 3.91629751e-04 + 8.95000000e+00 1.00015955e+00 4.37836441e-04 + 9.70000000e+00 1.00016472e+00 4.85442784e-04 + 1.07000000e+01 1.00017172e+00 5.51359924e-04 diff --git a/test/data/test_results/multi_material_test/avg_pl_work_region_material_A_1.txt b/test/data/test_results/multi_material_test/avg_pl_work_region_material_A_1.txt new file mode 100644 index 0000000..956ff38 --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_pl_work_region_material_A_1.txt @@ -0,0 +1,41 @@ + # Time Volume Plastic_Work + 5.00000000e-03 5.04000802e-01 0.00000000e+00 + 2.00000000e-01 5.04032032e-01 2.01997913e-09 + 3.00000000e-01 5.04042715e-01 5.20642344e-07 + 4.00000000e-01 5.04046152e-01 1.82857164e-06 + 5.00000000e-01 5.04047682e-01 3.44194221e-06 + 6.00000000e-01 5.04048696e-01 5.20457119e-06 + 7.00000000e-01 5.04049485e-01 7.05024114e-06 + 8.00000000e-01 5.04050148e-01 8.94994837e-06 + 9.00000000e-01 5.04050728e-01 1.08888776e-05 + 1.00000000e+00 5.04051253e-01 1.28611250e-05 + 1.10000000e+00 5.04051738e-01 1.48619810e-05 + 1.20000000e+00 5.04052196e-01 1.68866365e-05 + 1.30000000e+00 5.04052633e-01 1.89325593e-05 + 1.40000000e+00 5.04053056e-01 2.09984199e-05 + 1.50000000e+00 5.04053468e-01 2.30827354e-05 + 1.60000000e+00 5.04053873e-01 2.51843362e-05 + 1.70000000e+00 5.04054272e-01 2.73026281e-05 + 1.80000000e+00 5.04054666e-01 2.94371677e-05 + 1.90000000e+00 5.04055056e-01 3.15876016e-05 + 2.00000000e+00 5.04055442e-01 3.37536469e-05 + 2.10000000e+00 5.04055825e-01 3.59350610e-05 + 2.30000000e+00 5.04056587e-01 4.03553096e-05 + 2.50000000e+00 5.04057341e-01 4.48320754e-05 + 2.70000000e+00 5.04058086e-01 4.93648384e-05 + 2.90000000e+00 5.04058825e-01 5.39536012e-05 + 3.10000000e+00 5.04059559e-01 5.85985910e-05 + 3.30000000e+00 5.04060287e-01 6.33003443e-05 + 3.70000000e+00 5.04061758e-01 7.29265892e-05 + 4.10000000e+00 5.04063214e-01 8.27760162e-05 + 4.50000000e+00 5.04064657e-01 9.28457695e-05 + 4.90000000e+00 5.04066091e-01 1.03131868e-04 + 5.10000000e+00 5.04066791e-01 1.08330208e-04 + 5.70000000e+00 5.04068958e-01 1.24399074e-04 + 6.20000000e+00 5.04070733e-01 1.38121341e-04 + 6.70000000e+00 5.04072496e-01 1.52170709e-04 + 7.45000000e+00 5.04075184e-01 1.73964581e-04 + 8.20000000e+00 5.04077847e-01 1.96471533e-04 + 8.95000000e+00 5.04080491e-01 2.19685617e-04 + 9.70000000e+00 5.04083115e-01 2.43602731e-04 + 1.07000000e+01 5.04086670e-01 2.76719845e-04 diff --git a/test/data/test_results/multi_material_test/avg_pl_work_region_material_B_2.txt b/test/data/test_results/multi_material_test/avg_pl_work_region_material_B_2.txt new file mode 100644 index 0000000..13df5ab --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_pl_work_region_material_B_2.txt @@ -0,0 +1,41 @@ + # Time Volume Plastic_Work + 5.00000000e-03 4.96000786e-01 0.00000000e+00 + 2.00000000e-01 4.96031390e-01 6.59649613e-09 + 3.00000000e-01 4.96041896e-01 5.44066827e-07 + 4.00000000e-01 4.96045528e-01 1.95394208e-06 + 5.00000000e-01 4.96047229e-01 3.64355732e-06 + 6.00000000e-01 4.96048280e-01 5.42764826e-06 + 7.00000000e-01 4.96049058e-01 7.26913847e-06 + 8.00000000e-01 4.96049693e-01 9.15618344e-06 + 9.00000000e-01 4.96050246e-01 1.10809480e-05 + 1.00000000e+00 4.96050749e-01 1.30362197e-05 + 1.10000000e+00 4.96051217e-01 1.50172778e-05 + 1.20000000e+00 4.96051663e-01 1.70223381e-05 + 1.30000000e+00 4.96052093e-01 1.90492059e-05 + 1.40000000e+00 4.96052509e-01 2.10958451e-05 + 1.50000000e+00 4.96052916e-01 2.31613051e-05 + 1.60000000e+00 4.96053313e-01 2.52448457e-05 + 1.70000000e+00 4.96053703e-01 2.73455374e-05 + 1.80000000e+00 4.96054087e-01 2.94628064e-05 + 1.90000000e+00 4.96054466e-01 3.15961977e-05 + 2.00000000e+00 4.96054840e-01 3.37452761e-05 + 2.10000000e+00 4.96055211e-01 3.59097655e-05 + 2.30000000e+00 4.96055950e-01 4.02987751e-05 + 2.50000000e+00 4.96056679e-01 4.47488715e-05 + 2.70000000e+00 4.96057400e-01 4.92590374e-05 + 2.90000000e+00 4.96058115e-01 5.38274264e-05 + 3.10000000e+00 4.96058823e-01 5.84526474e-05 + 3.30000000e+00 4.96059526e-01 6.31332908e-05 + 3.70000000e+00 4.96060945e-01 7.27068755e-05 + 4.10000000e+00 4.96062350e-01 8.24926967e-05 + 4.50000000e+00 4.96063744e-01 9.24895582e-05 + 4.90000000e+00 4.96065127e-01 1.02697878e-04 + 5.10000000e+00 4.96065800e-01 1.07855760e-04 + 5.70000000e+00 4.96067893e-01 1.23785529e-04 + 6.20000000e+00 4.96069607e-01 1.37381645e-04 + 6.70000000e+00 4.96071310e-01 1.51295653e-04 + 7.45000000e+00 4.96073914e-01 1.72871143e-04 + 8.20000000e+00 4.96076497e-01 1.95158218e-04 + 8.95000000e+00 4.96079059e-01 2.18150824e-04 + 9.70000000e+00 4.96081601e-01 2.41840053e-04 + 1.07000000e+01 4.96085050e-01 2.74640079e-04 diff --git a/test/data/test_results/multi_material_test/avg_stress_global.txt b/test/data/test_results/multi_material_test/avg_stress_global.txt new file mode 100644 index 0000000..2890b07 --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_stress_global.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.13447171e-14 1.14505164e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006342e+00 -1.74927103e-11 6.23473180e-11 2.60677388e-02 -2.37011020e-04 1.08523020e-04 8.92052816e-07 + 3.00000000e-01 1.00008461e+00 -3.09747148e-11 1.31959780e-09 3.47764131e-02 -3.34829790e-04 1.56622176e-04 2.36163900e-05 + 4.00000000e-01 1.00009168e+00 8.68671829e-12 1.05283016e-10 3.76792522e-02 -4.16312999e-04 1.45826096e-04 7.77878815e-05 + 5.00000000e-01 1.00009491e+00 -7.07611023e-14 2.48110796e-13 3.90050839e-02 -4.66039938e-04 1.40537384e-04 1.26975821e-04 + 6.00000000e-01 1.00009698e+00 -2.61247093e-14 -9.20348784e-13 3.98508208e-02 -4.87920814e-04 1.54224363e-04 1.63593789e-04 + 7.00000000e-01 1.00009854e+00 6.62421809e-10 7.28034680e-10 4.04919847e-02 -5.00516721e-04 1.74053484e-04 1.90223196e-04 + 8.00000000e-01 1.00009984e+00 3.56991374e-10 1.66183147e-10 4.10221015e-02 -5.11061376e-04 1.93411403e-04 2.08460594e-04 + 9.00000000e-01 1.00010097e+00 6.32680684e-11 3.71325633e-11 4.14852117e-02 -5.21826408e-04 2.09462082e-04 2.20470277e-04 + 1.00000000e+00 1.00010200e+00 -7.64861799e-12 -1.66033329e-11 4.19041766e-02 -5.31837549e-04 2.22681211e-04 2.28587596e-04 + 1.10000000e+00 1.00010296e+00 -9.71219331e-12 -3.48240784e-11 4.22933604e-02 -5.40683926e-04 2.33825867e-04 2.33658627e-04 + 1.20000000e+00 1.00010386e+00 -2.12153164e-11 -3.14698527e-11 4.26617094e-02 -5.48806420e-04 2.43563155e-04 2.36718609e-04 + 1.30000000e+00 1.00010473e+00 -1.85861367e-11 -2.55283377e-11 4.30150115e-02 -5.56317924e-04 2.52403762e-04 2.38979919e-04 + 1.40000000e+00 1.00010557e+00 -1.81743881e-11 -2.13183815e-11 4.33570886e-02 -5.63213219e-04 2.60588605e-04 2.41060225e-04 + 1.50000000e+00 1.00010638e+00 -1.64686259e-11 -1.63463695e-11 4.36905235e-02 -5.69575388e-04 2.68119221e-04 2.42996233e-04 + 1.60000000e+00 1.00010719e+00 -1.33757734e-11 -1.12769872e-11 4.40171905e-02 -5.75305432e-04 2.75034168e-04 2.44865227e-04 + 1.70000000e+00 1.00010798e+00 -1.16620555e-11 -9.36110689e-12 4.43384614e-02 -5.80383187e-04 2.81490485e-04 2.46829409e-04 + 1.80000000e+00 1.00010875e+00 -1.09777260e-11 -8.65086135e-12 4.46552194e-02 -5.84923902e-04 2.87596721e-04 2.48798746e-04 + 1.90000000e+00 1.00010952e+00 -9.86328975e-12 -7.74714992e-12 4.49681428e-02 -5.89112461e-04 2.93397403e-04 2.50705557e-04 + 2.00000000e+00 1.00011028e+00 -8.44751070e-12 -7.06430379e-12 4.52778055e-02 -5.93115882e-04 2.98881222e-04 2.52533346e-04 + 2.10000000e+00 1.00011104e+00 -7.64554167e-12 -6.91539639e-12 4.55846116e-02 -5.97006601e-04 3.03981260e-04 2.54328515e-04 + 2.30000000e+00 1.00011254e+00 -3.23517597e-11 -2.75291989e-11 4.61894125e-02 -6.04312101e-04 3.12860235e-04 2.57933035e-04 + 2.50000000e+00 1.00011402e+00 -3.51890878e-11 -3.76812595e-11 4.67864707e-02 -6.11298250e-04 3.21165532e-04 2.61126201e-04 + 2.70000000e+00 1.00011549e+00 -3.68775342e-11 -3.76491515e-11 4.73769927e-02 -6.18179467e-04 3.29237224e-04 2.63801920e-04 + 2.90000000e+00 1.00011694e+00 -3.39771750e-11 -3.30689347e-11 4.79621051e-02 -6.25027348e-04 3.36913825e-04 2.66000544e-04 + 3.10000000e+00 1.00011838e+00 -3.84319941e-11 -3.28283940e-11 4.85424830e-02 -6.31779736e-04 3.44063015e-04 2.67846254e-04 + 3.30000000e+00 1.00011981e+00 -3.80523057e-11 -2.90782861e-11 4.91186552e-02 -6.38354821e-04 3.50902544e-04 2.69510286e-04 + 3.70000000e+00 1.00012270e+00 -2.05387721e-10 -1.32659657e-10 5.02570907e-02 -6.50779326e-04 3.64306440e-04 2.72414865e-04 + 4.10000000e+00 1.00012556e+00 -1.49058320e-10 -8.56726719e-11 5.13839269e-02 -6.62281441e-04 3.76719820e-04 2.74882297e-04 + 4.50000000e+00 1.00012840e+00 -1.05695562e-10 -2.57902061e-11 5.25008081e-02 -6.72581701e-04 3.87986901e-04 2.77043864e-04 + 4.90000000e+00 1.00013122e+00 -8.47342932e-11 -6.24437929e-12 5.36088933e-02 -6.81702808e-04 3.98823660e-04 2.78638549e-04 + 5.10000000e+00 1.00013259e+00 -1.62737616e-11 -9.61761197e-12 5.41612290e-02 -6.86078170e-04 4.04183326e-04 2.79320846e-04 + 5.70000000e+00 1.00013685e+00 5.58316263e-13 8.90022968e-14 5.58010077e-02 -6.98760286e-04 4.19669477e-04 2.80993397e-04 + 6.20000000e+00 1.00014034e+00 -1.08358578e-10 -6.03779153e-11 5.71577155e-02 -7.09342032e-04 4.31484281e-04 2.82049662e-04 + 6.70000000e+00 1.00014381e+00 -9.48141026e-11 -3.94678958e-11 5.85053130e-02 -7.19763934e-04 4.42448459e-04 2.82978504e-04 + 7.45000000e+00 1.00014910e+00 -4.13768229e-10 -1.99801381e-10 6.05067689e-02 -7.35436121e-04 4.57699580e-04 2.84905676e-04 + 8.20000000e+00 1.00015434e+00 -3.23183409e-10 -2.26067162e-10 6.24898282e-02 -7.51174764e-04 4.71759080e-04 2.87475691e-04 + 8.95000000e+00 1.00015955e+00 -1.40939466e-10 -5.11124739e-11 6.44556099e-02 -7.67015445e-04 4.85886137e-04 2.91301203e-04 + 9.70000000e+00 1.00016472e+00 -1.66967226e-10 -6.94785377e-11 6.64049765e-02 -7.82963130e-04 5.00263279e-04 2.96139619e-04 + 1.07000000e+01 1.00017172e+00 -5.15055951e-10 -3.51937027e-10 6.89752132e-02 -8.04306655e-04 5.19269428e-04 3.03303469e-04 diff --git a/test/data/test_results/multi_material_test/avg_stress_region_material_A_1.txt b/test/data/test_results/multi_material_test/avg_stress_region_material_A_1.txt new file mode 100644 index 0000000..e72327d --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_stress_region_material_A_1.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 5.04000802e-01 9.24824827e-06 -8.90345975e-06 6.54323988e-04 -9.43446593e-06 5.76732184e-06 -2.72732474e-06 + 2.00000000e-01 5.04032032e-01 3.61725808e-04 -3.62606375e-04 2.61235368e-02 -3.81731653e-04 2.26103390e-04 -1.08478228e-04 + 3.00000000e-01 5.04042715e-01 5.12383563e-04 -5.08541894e-04 3.48311893e-02 -5.25261411e-04 2.35270914e-04 4.21781646e-06 + 4.00000000e-01 5.04046152e-01 5.55216562e-04 -6.43273165e-04 3.77232285e-02 -6.31056850e-04 1.42733676e-04 1.75078499e-04 + 5.00000000e-01 5.04047682e-01 5.63423578e-04 -7.33212286e-04 3.90505514e-02 -6.91380782e-04 9.16024462e-05 2.59565106e-04 + 6.00000000e-01 5.04048696e-01 5.85718007e-04 -7.79597624e-04 3.98989783e-02 -7.19017641e-04 7.55331180e-05 3.02336028e-04 + 7.00000000e-01 5.04049485e-01 6.11741548e-04 -8.08007672e-04 4.05420059e-02 -7.37324142e-04 7.22626907e-05 3.27193288e-04 + 8.00000000e-01 5.04050148e-01 6.38416408e-04 -8.29117716e-04 4.10737241e-02 -7.55103351e-04 7.28218983e-05 3.41847864e-04 + 9.00000000e-01 5.04050728e-01 6.62042814e-04 -8.47209982e-04 4.15385544e-02 -7.73883564e-04 7.35151660e-05 3.49870460e-04 + 1.00000000e+00 5.04051253e-01 6.81335653e-04 -8.62261020e-04 4.19591060e-02 -7.91798509e-04 7.41388028e-05 3.54717011e-04 + 1.10000000e+00 5.04051738e-01 6.97057932e-04 -8.75057738e-04 4.23494626e-02 -8.08116070e-04 7.46049746e-05 3.57094408e-04 + 1.20000000e+00 5.04052196e-01 7.10099095e-04 -8.87041214e-04 4.27185423e-02 -8.22864237e-04 7.54624375e-05 3.57633314e-04 + 1.30000000e+00 5.04052633e-01 7.21787548e-04 -8.98865143e-04 4.30723559e-02 -8.36172936e-04 7.74385392e-05 3.57770451e-04 + 1.40000000e+00 5.04053056e-01 7.32957649e-04 -9.10603739e-04 4.34148747e-02 -8.48219095e-04 8.04223296e-05 3.58361108e-04 + 1.50000000e+00 5.04053468e-01 7.43725072e-04 -9.22154725e-04 4.37489264e-02 -8.59096437e-04 8.37628662e-05 3.59463036e-04 + 1.60000000e+00 5.04053873e-01 7.54189913e-04 -9.33128109e-04 4.40765384e-02 -8.68793014e-04 8.70361871e-05 3.60902139e-04 + 1.70000000e+00 5.04054272e-01 7.64417941e-04 -9.43320786e-04 4.43990034e-02 -8.77457711e-04 9.01867985e-05 3.62681864e-04 + 1.80000000e+00 5.04054666e-01 7.74463242e-04 -9.52887484e-04 4.47170982e-02 -8.85425305e-04 9.32455801e-05 3.64592837e-04 + 1.90000000e+00 5.04055056e-01 7.84379475e-04 -9.62107125e-04 4.50314413e-02 -8.93082935e-04 9.62934643e-05 3.66477759e-04 + 2.00000000e+00 5.04055442e-01 7.94134274e-04 -9.71132563e-04 4.53425825e-02 -9.00639370e-04 9.93735376e-05 3.68338772e-04 + 2.10000000e+00 5.04055825e-01 8.03653958e-04 -9.79923327e-04 4.56509567e-02 -9.08140176e-04 1.02464659e-04 3.70303169e-04 + 2.30000000e+00 5.04056587e-01 8.21833694e-04 -9.96578643e-04 4.62591783e-02 -9.22659834e-04 1.08809094e-04 3.74588660e-04 + 2.50000000e+00 5.04057341e-01 8.39848280e-04 -1.01257439e-03 4.68596507e-02 -9.36794559e-04 1.15691656e-04 3.78711108e-04 + 2.70000000e+00 5.04058086e-01 8.57680833e-04 -1.02804372e-03 4.74535715e-02 -9.50691148e-04 1.22901331e-04 3.82450659e-04 + 2.90000000e+00 5.04058825e-01 8.74886953e-04 -1.04302060e-03 4.80420919e-02 -9.64350701e-04 1.30048203e-04 3.85833397e-04 + 3.10000000e+00 5.04059559e-01 8.91308104e-04 -1.05721053e-03 4.86259280e-02 -9.77741644e-04 1.36959457e-04 3.88896804e-04 + 3.30000000e+00 5.04060287e-01 9.07428716e-04 -1.07084593e-03 4.92056175e-02 -9.90712717e-04 1.43779105e-04 3.91845703e-04 + 3.70000000e+00 5.04061758e-01 9.39630244e-04 -1.09685674e-03 5.03511000e-02 -1.01547089e-03 1.58112854e-04 3.97746482e-04 + 4.10000000e+00 5.04063214e-01 9.70338360e-04 -1.12164919e-03 5.14847020e-02 -1.03909575e-03 1.72433849e-04 4.03932522e-04 + 4.50000000e+00 5.04064657e-01 9.99107783e-04 -1.14476614e-03 5.26082179e-02 -1.06138993e-03 1.86420099e-04 4.10785827e-04 + 4.90000000e+00 5.04066091e-01 1.02671968e-03 -1.16616865e-03 5.37229744e-02 -1.08216327e-03 2.00448104e-04 4.17649606e-04 + 5.10000000e+00 5.04066791e-01 1.04030181e-03 -1.17647551e-03 5.42786436e-02 -1.09224290e-03 2.07425537e-04 4.21041288e-04 + 5.70000000e+00 5.04068958e-01 1.07944619e-03 -1.20601159e-03 5.59280303e-02 -1.12101752e-03 2.28065459e-04 4.31028825e-04 + 6.20000000e+00 5.04070733e-01 1.11120536e-03 -1.22991494e-03 5.72925008e-02 -1.14459205e-03 2.43844896e-04 4.38957130e-04 + 6.70000000e+00 5.04072496e-01 1.14231783e-03 -1.25355181e-03 5.86476790e-02 -1.16739471e-03 2.58336218e-04 4.46678247e-04 + 7.45000000e+00 5.04075184e-01 1.18800555e-03 -1.28891147e-03 6.06602142e-02 -1.20066193e-03 2.77860445e-04 4.58787855e-04 + 8.20000000e+00 5.04077847e-01 1.23274090e-03 -1.32473855e-03 6.26538865e-02 -1.23279572e-03 2.95098636e-04 4.71358119e-04 + 8.95000000e+00 5.04080491e-01 1.27852555e-03 -1.36075414e-03 6.46302074e-02 -1.26407284e-03 3.11772308e-04 4.85035316e-04 + 9.70000000e+00 5.04083115e-01 1.32498396e-03 -1.39677516e-03 6.65901796e-02 -1.29462241e-03 3.28344407e-04 4.99815829e-04 + 1.07000000e+01 5.04086670e-01 1.38586055e-03 -1.44441483e-03 6.91746165e-02 -1.33477095e-03 3.49548421e-04 5.20639827e-04 diff --git a/test/data/test_results/multi_material_test/avg_stress_region_material_B_2.txt b/test/data/test_results/multi_material_test/avg_stress_region_material_B_2.txt new file mode 100644 index 0000000..1c18c22 --- /dev/null +++ b/test/data/test_results/multi_material_test/avg_stress_region_material_B_2.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 4.96000786e-01 -9.39741362e-06 9.04706404e-06 6.51634294e-04 -2.09711917e-06 -2.46725751e-07 2.81288740e-06 + 2.00000000e-01 4.96031390e-01 -3.67560229e-04 3.68455090e-04 2.60110407e-02 -8.99561451e-05 -1.09538388e-05 1.12026400e-04 + 3.00000000e-01 4.96041896e-01 -5.20648025e-04 5.16746991e-04 3.47207534e-02 -1.41326637e-04 7.67048867e-05 4.33278493e-05 + 4.00000000e-01 4.96045528e-01 -5.64171527e-04 6.53648608e-04 3.76345666e-02 -1.98105585e-04 1.48968394e-04 -2.10719183e-05 + 5.00000000e-01 4.96047229e-01 -5.72510704e-04 7.45037834e-04 3.89588831e-02 -2.37064705e-04 1.90261565e-04 -7.75191762e-06 + 6.00000000e-01 4.96048280e-01 -5.95164644e-04 7.92171208e-04 3.98018866e-02 -2.53096788e-04 2.34184763e-04 2.26138734e-05 + 7.00000000e-01 4.96049058e-01 -6.21606562e-04 8.21040928e-04 4.04411568e-02 -2.59889999e-04 2.77485989e-04 5.10440094e-05 + 8.00000000e-01 4.96049693e-01 -6.48712282e-04 8.42490338e-04 4.09696464e-02 -2.63083412e-04 3.15945815e-04 7.29220105e-05 + 9.00000000e-01 4.96050246e-01 -6.72720358e-04 8.60874173e-04 4.14310088e-02 -2.65703982e-04 3.47601599e-04 8.89830802e-05 + 1.00000000e+00 4.96050749e-01 -6.92324522e-04 8.76167875e-04 4.18483613e-02 -2.67683836e-04 3.73619371e-04 1.00423915e-04 + 1.10000000e+00 4.96051217e-01 -7.08300394e-04 8.89170965e-04 4.22363534e-02 -2.68938525e-04 3.95614740e-04 1.08232023e-04 + 1.20000000e+00 4.96051663e-01 -7.21551919e-04 9.01347729e-04 4.26039598e-02 -2.70328482e-04 4.14375073e-04 1.13853741e-04 + 1.30000000e+00 4.96052093e-01 -7.33428884e-04 9.13362374e-04 4.29567422e-02 -2.71949290e-04 4.30190898e-04 1.18273483e-04 + 1.40000000e+00 4.96052509e-01 -7.44779139e-04 9.25290302e-04 4.32983704e-02 -2.73610647e-04 4.43660679e-04 1.21867464e-04 + 1.50000000e+00 4.96052916e-01 -7.55720220e-04 9.37027596e-04 4.36311787e-02 -2.75384821e-04 4.55448954e-04 1.24651003e-04 + 1.60000000e+00 4.96053313e-01 -7.66353838e-04 9.48177975e-04 4.39568855e-02 -2.77084356e-04 4.66064261e-04 1.26956821e-04 + 1.70000000e+00 4.96053703e-01 -7.76746829e-04 9.58535053e-04 4.42769428e-02 -2.78517316e-04 4.75879601e-04 1.29108435e-04 + 1.80000000e+00 4.96054087e-01 -7.86954149e-04 9.68256057e-04 4.45923426e-02 -2.79575879e-04 4.85082442e-04 1.31137077e-04 + 1.90000000e+00 4.96054466e-01 -7.97030322e-04 9.77624409e-04 4.49038234e-02 -2.80239414e-04 4.93680323e-04 1.33066127e-04 + 2.00000000e+00 4.96054840e-01 -8.06942456e-04 9.86795425e-04 4.52119838e-02 -2.80632512e-04 5.01606659e-04 1.34860156e-04 + 2.10000000e+00 4.96055211e-01 -8.16615685e-04 9.95727981e-04 4.55171965e-02 -2.80854917e-04 5.08748016e-04 1.36483367e-04 + 2.30000000e+00 4.96055950e-01 -8.35088702e-04 1.01265190e-03 4.61185215e-02 -2.80829897e-04 5.20202414e-04 1.39395930e-04 + 2.50000000e+00 4.96056679e-01 -8.53393862e-04 1.02890564e-03 4.67121105e-02 -2.80552167e-04 5.29953399e-04 1.41644823e-04 + 2.70000000e+00 4.96057400e-01 -8.71514055e-04 1.04462450e-03 4.72991787e-02 -2.80304856e-04 5.38901016e-04 1.43239548e-04 + 2.90000000e+00 4.96058115e-01 -8.88997702e-04 1.05984297e-03 4.78808282e-02 -2.80231193e-04 5.47115896e-04 1.44234958e-04 + 3.10000000e+00 4.96058823e-01 -9.05683735e-04 1.07426180e-03 4.84576921e-02 -2.80237946e-04 5.54506864e-04 1.44843327e-04 + 3.30000000e+00 4.96059526e-01 -9.22064375e-04 1.08811715e-03 4.90302904e-02 -2.80313874e-04 5.61366600e-04 1.45201766e-04 + 3.70000000e+00 4.96060945e-01 -9.54785662e-04 1.11454733e-03 5.01615652e-02 -2.80205768e-04 5.73825658e-04 1.45061813e-04 + 4.10000000e+00 4.96062350e-01 -9.85989001e-04 1.13973981e-03 5.12815264e-02 -2.79389594e-04 5.84300668e-04 1.43750655e-04 + 4.50000000e+00 4.96063744e-01 -1.01522241e-03 1.16322979e-03 5.23916660e-02 -2.77502461e-04 5.92804734e-04 1.41144804e-04 + 4.90000000e+00 4.96065127e-01 -1.04327967e-03 1.18497760e-03 5.34929723e-02 -2.74783372e-04 6.00398787e-04 1.37385403e-04 + 5.10000000e+00 4.96065800e-01 -1.05708076e-03 1.19545073e-03 5.40419207e-02 -2.73362451e-04 6.04114599e-04 1.35314610e-04 + 5.70000000e+00 4.96067893e-01 -1.09685654e-03 1.22546332e-03 5.56719364e-02 -2.69692474e-04 6.14363870e-04 1.28538053e-04 + 6.20000000e+00 4.96069607e-01 -1.12912826e-03 1.24975216e-03 5.70207563e-02 -2.67071848e-04 6.22150110e-04 1.22611427e-04 + 6.70000000e+00 4.96071310e-01 -1.16074258e-03 1.27377040e-03 5.83606507e-02 -2.64913273e-04 6.29530266e-04 1.16638431e-04 + 7.45000000e+00 4.96073914e-01 -1.20716796e-03 1.30970017e-03 6.03508486e-02 -2.62706595e-04 6.40439375e-04 1.08218918e-04 + 8.20000000e+00 4.96076497e-01 -1.25262476e-03 1.34610515e-03 6.23231238e-02 -2.61785616e-04 6.51268927e-04 1.00627374e-04 + 8.95000000e+00 4.96079059e-01 -1.29914761e-03 1.38270212e-03 6.42781962e-02 -2.61940845e-04 6.62808309e-04 9.44422859e-05 + 9.70000000e+00 4.96081601e-01 -1.34635553e-03 1.41930421e-03 6.62167862e-02 -2.63051079e-04 6.74955106e-04 8.91782268e-05 + 1.07000000e+01 4.96085050e-01 -1.40821487e-03 1.46771185e-03 6.87725937e-02 -2.65286222e-04 6.91727956e-04 8.24615765e-05 diff --git a/test/data/test_results/voce_bcc/avg_stress_global.txt b/test/data/test_results/voce_bcc/avg_stress_global.txt new file mode 100644 index 0000000..85b37a8 --- /dev/null +++ b/test/data/test_results/voce_bcc/avg_stress_global.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.13447145e-14 1.14505311e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006342e+00 -1.74746693e-11 6.25472453e-11 2.60676103e-02 -2.37010300e-04 1.08522100e-04 8.91346090e-07 + 3.00000000e-01 1.00008461e+00 -3.07579285e-11 1.30165521e-09 3.47749506e-02 -3.34828688e-04 1.56659130e-04 2.36178116e-05 + 4.00000000e-01 1.00009167e+00 8.55736239e-12 1.04717290e-10 3.76768288e-02 -4.16273898e-04 1.45889628e-04 7.78691727e-05 + 5.00000000e-01 1.00009490e+00 -7.64141565e-14 2.44727338e-13 3.90019074e-02 -4.66017426e-04 1.40517116e-04 1.27251456e-04 + 6.00000000e-01 1.00009697e+00 -3.12721241e-14 -9.24387440e-13 3.98470003e-02 -4.87954655e-04 1.54086692e-04 1.64111247e-04 + 7.00000000e-01 1.00009853e+00 6.64684629e-10 7.30534094e-10 4.04875717e-02 -5.00603337e-04 1.73816983e-04 1.90998497e-04 + 8.00000000e-01 1.00009983e+00 3.54282870e-10 1.67524530e-10 4.10171454e-02 -5.11150605e-04 1.93097156e-04 2.09486720e-04 + 9.00000000e-01 1.00010096e+00 6.30180387e-11 3.61061800e-11 4.14797235e-02 -5.21931007e-04 2.09100655e-04 2.21740852e-04 + 1.00000000e+00 1.00010199e+00 -8.51349331e-12 -1.61643044e-11 4.18981201e-02 -5.31994017e-04 2.22284931e-04 2.30100684e-04 + 1.10000000e+00 1.00010294e+00 -1.01362137e-11 -3.45512156e-11 4.22866748e-02 -5.40914855e-04 2.33390749e-04 2.35434576e-04 + 1.20000000e+00 1.00010384e+00 -2.10806762e-11 -3.20757337e-11 4.26543404e-02 -5.49115642e-04 2.43083399e-04 2.38742267e-04 + 1.30000000e+00 1.00010471e+00 -1.87459341e-11 -2.55896693e-11 4.30069374e-02 -5.56710703e-04 2.51892137e-04 2.41241764e-04 + 1.40000000e+00 1.00010554e+00 -1.80239926e-11 -2.11314981e-11 4.33482837e-02 -5.63693268e-04 2.60082324e-04 2.43560288e-04 + 1.50000000e+00 1.00010636e+00 -1.66302949e-11 -1.62951908e-11 4.36809606e-02 -5.70158445e-04 2.67655659e-04 2.45722157e-04 + 1.60000000e+00 1.00010716e+00 -1.37822270e-11 -1.14013579e-11 4.40068639e-02 -5.76001277e-04 2.74635483e-04 2.47812566e-04 + 1.70000000e+00 1.00010795e+00 -1.21032940e-11 -9.54119145e-12 4.43273340e-02 -5.81208779e-04 2.81167789e-04 2.50013516e-04 + 1.80000000e+00 1.00010872e+00 -1.13401737e-11 -8.85706397e-12 4.46432312e-02 -5.85899359e-04 2.87342144e-04 2.52261319e-04 + 1.90000000e+00 1.00010949e+00 -1.01697390e-11 -7.82169339e-12 4.49552344e-02 -5.90264007e-04 2.93219003e-04 2.54446513e-04 + 2.00000000e+00 1.00011025e+00 -8.62531154e-12 -7.00519944e-12 4.52639400e-02 -5.94443761e-04 2.98796267e-04 2.56531650e-04 + 2.10000000e+00 1.00011100e+00 -7.60990972e-12 -6.70338905e-12 4.55697617e-02 -5.98487573e-04 3.03998410e-04 2.58579762e-04 + 2.30000000e+00 1.00011250e+00 -3.34583625e-11 -2.66773035e-11 4.61726594e-02 -6.06078886e-04 3.13112274e-04 2.62714294e-04 + 2.50000000e+00 1.00011397e+00 -3.55657510e-11 -3.89087149e-11 4.67676662e-02 -6.13407140e-04 3.21714004e-04 2.66485435e-04 + 2.70000000e+00 1.00011543e+00 -3.85634482e-11 -3.83560390e-11 4.73559564e-02 -6.20699524e-04 3.30096110e-04 2.69770383e-04 + 2.90000000e+00 1.00011688e+00 -3.32332573e-11 -3.26593012e-11 4.79386804e-02 -6.27978783e-04 3.38036196e-04 2.72578862e-04 + 3.10000000e+00 1.00011832e+00 -3.87354775e-11 -3.29284232e-11 4.85165880e-02 -6.35192779e-04 3.45498233e-04 2.75016499e-04 + 3.30000000e+00 1.00011974e+00 -3.77301200e-11 -2.96339808e-11 4.90902081e-02 -6.42289675e-04 3.52716042e-04 2.77277159e-04 + 3.70000000e+00 1.00012262e+00 -2.23416031e-10 -1.14103469e-10 5.02234808e-02 -6.55589275e-04 3.66722015e-04 2.81347245e-04 + 4.10000000e+00 1.00012547e+00 -1.75807103e-10 -7.74927013e-11 5.13445717e-02 -6.67752473e-04 3.79727654e-04 2.85056089e-04 + 4.50000000e+00 1.00012829e+00 -1.30843160e-10 2.06223556e-11 5.24550550e-02 -6.78676950e-04 3.91620565e-04 2.88427388e-04 + 4.90000000e+00 1.00013108e+00 -1.00747488e-10 1.99090922e-11 5.35561968e-02 -6.88385146e-04 4.03177218e-04 2.91181002e-04 + 5.10000000e+00 1.00013245e+00 -1.72801432e-11 -1.07979196e-11 5.41046214e-02 -6.93014810e-04 4.08894519e-04 2.92464357e-04 + 5.70000000e+00 1.00013668e+00 6.95013882e-13 2.60613738e-13 5.57327848e-02 -7.06639944e-04 4.25575238e-04 2.95839882e-04 + 6.20000000e+00 1.00014014e+00 -1.18361292e-10 -5.38668103e-11 5.70789280e-02 -7.18245635e-04 4.38722537e-04 2.98661320e-04 + 6.70000000e+00 1.00014357e+00 -8.97141176e-11 -1.84654165e-11 5.84153057e-02 -7.29843796e-04 4.51256987e-04 3.01495428e-04 + 7.45000000e+00 1.00014882e+00 3.62872171e-13 5.60514160e-15 6.03994060e-02 -7.47134886e-04 4.69004480e-04 3.06132483e-04 + 8.20000000e+00 1.00015401e+00 1.83408448e-13 3.05331093e-13 6.23636812e-02 -7.64532811e-04 4.85636101e-04 3.11902423e-04 + 8.95000000e+00 1.00015916e+00 2.29380729e-13 -1.32034810e-15 6.43090718e-02 -7.82262094e-04 5.01414998e-04 3.18953775e-04 + 9.70000000e+00 1.00016427e+00 -6.29741242e-13 -3.33759748e-13 6.62364103e-02 -8.00211697e-04 5.16786885e-04 3.27118764e-04 + 1.07000000e+01 1.00017118e+00 -2.61063745e-12 -1.61462778e-12 6.87754359e-02 -8.24434961e-04 5.36685779e-04 3.39412157e-04 diff --git a/test/data/test_results/voce_bcc/avg_stress_region_default_1.txt b/test/data/test_results/voce_bcc/avg_stress_region_default_1.txt new file mode 100644 index 0000000..85b37a8 --- /dev/null +++ b/test/data/test_results/voce_bcc/avg_stress_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.13447145e-14 1.14505311e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006342e+00 -1.74746693e-11 6.25472453e-11 2.60676103e-02 -2.37010300e-04 1.08522100e-04 8.91346090e-07 + 3.00000000e-01 1.00008461e+00 -3.07579285e-11 1.30165521e-09 3.47749506e-02 -3.34828688e-04 1.56659130e-04 2.36178116e-05 + 4.00000000e-01 1.00009167e+00 8.55736239e-12 1.04717290e-10 3.76768288e-02 -4.16273898e-04 1.45889628e-04 7.78691727e-05 + 5.00000000e-01 1.00009490e+00 -7.64141565e-14 2.44727338e-13 3.90019074e-02 -4.66017426e-04 1.40517116e-04 1.27251456e-04 + 6.00000000e-01 1.00009697e+00 -3.12721241e-14 -9.24387440e-13 3.98470003e-02 -4.87954655e-04 1.54086692e-04 1.64111247e-04 + 7.00000000e-01 1.00009853e+00 6.64684629e-10 7.30534094e-10 4.04875717e-02 -5.00603337e-04 1.73816983e-04 1.90998497e-04 + 8.00000000e-01 1.00009983e+00 3.54282870e-10 1.67524530e-10 4.10171454e-02 -5.11150605e-04 1.93097156e-04 2.09486720e-04 + 9.00000000e-01 1.00010096e+00 6.30180387e-11 3.61061800e-11 4.14797235e-02 -5.21931007e-04 2.09100655e-04 2.21740852e-04 + 1.00000000e+00 1.00010199e+00 -8.51349331e-12 -1.61643044e-11 4.18981201e-02 -5.31994017e-04 2.22284931e-04 2.30100684e-04 + 1.10000000e+00 1.00010294e+00 -1.01362137e-11 -3.45512156e-11 4.22866748e-02 -5.40914855e-04 2.33390749e-04 2.35434576e-04 + 1.20000000e+00 1.00010384e+00 -2.10806762e-11 -3.20757337e-11 4.26543404e-02 -5.49115642e-04 2.43083399e-04 2.38742267e-04 + 1.30000000e+00 1.00010471e+00 -1.87459341e-11 -2.55896693e-11 4.30069374e-02 -5.56710703e-04 2.51892137e-04 2.41241764e-04 + 1.40000000e+00 1.00010554e+00 -1.80239926e-11 -2.11314981e-11 4.33482837e-02 -5.63693268e-04 2.60082324e-04 2.43560288e-04 + 1.50000000e+00 1.00010636e+00 -1.66302949e-11 -1.62951908e-11 4.36809606e-02 -5.70158445e-04 2.67655659e-04 2.45722157e-04 + 1.60000000e+00 1.00010716e+00 -1.37822270e-11 -1.14013579e-11 4.40068639e-02 -5.76001277e-04 2.74635483e-04 2.47812566e-04 + 1.70000000e+00 1.00010795e+00 -1.21032940e-11 -9.54119145e-12 4.43273340e-02 -5.81208779e-04 2.81167789e-04 2.50013516e-04 + 1.80000000e+00 1.00010872e+00 -1.13401737e-11 -8.85706397e-12 4.46432312e-02 -5.85899359e-04 2.87342144e-04 2.52261319e-04 + 1.90000000e+00 1.00010949e+00 -1.01697390e-11 -7.82169339e-12 4.49552344e-02 -5.90264007e-04 2.93219003e-04 2.54446513e-04 + 2.00000000e+00 1.00011025e+00 -8.62531154e-12 -7.00519944e-12 4.52639400e-02 -5.94443761e-04 2.98796267e-04 2.56531650e-04 + 2.10000000e+00 1.00011100e+00 -7.60990972e-12 -6.70338905e-12 4.55697617e-02 -5.98487573e-04 3.03998410e-04 2.58579762e-04 + 2.30000000e+00 1.00011250e+00 -3.34583625e-11 -2.66773035e-11 4.61726594e-02 -6.06078886e-04 3.13112274e-04 2.62714294e-04 + 2.50000000e+00 1.00011397e+00 -3.55657510e-11 -3.89087149e-11 4.67676662e-02 -6.13407140e-04 3.21714004e-04 2.66485435e-04 + 2.70000000e+00 1.00011543e+00 -3.85634482e-11 -3.83560390e-11 4.73559564e-02 -6.20699524e-04 3.30096110e-04 2.69770383e-04 + 2.90000000e+00 1.00011688e+00 -3.32332573e-11 -3.26593012e-11 4.79386804e-02 -6.27978783e-04 3.38036196e-04 2.72578862e-04 + 3.10000000e+00 1.00011832e+00 -3.87354775e-11 -3.29284232e-11 4.85165880e-02 -6.35192779e-04 3.45498233e-04 2.75016499e-04 + 3.30000000e+00 1.00011974e+00 -3.77301200e-11 -2.96339808e-11 4.90902081e-02 -6.42289675e-04 3.52716042e-04 2.77277159e-04 + 3.70000000e+00 1.00012262e+00 -2.23416031e-10 -1.14103469e-10 5.02234808e-02 -6.55589275e-04 3.66722015e-04 2.81347245e-04 + 4.10000000e+00 1.00012547e+00 -1.75807103e-10 -7.74927013e-11 5.13445717e-02 -6.67752473e-04 3.79727654e-04 2.85056089e-04 + 4.50000000e+00 1.00012829e+00 -1.30843160e-10 2.06223556e-11 5.24550550e-02 -6.78676950e-04 3.91620565e-04 2.88427388e-04 + 4.90000000e+00 1.00013108e+00 -1.00747488e-10 1.99090922e-11 5.35561968e-02 -6.88385146e-04 4.03177218e-04 2.91181002e-04 + 5.10000000e+00 1.00013245e+00 -1.72801432e-11 -1.07979196e-11 5.41046214e-02 -6.93014810e-04 4.08894519e-04 2.92464357e-04 + 5.70000000e+00 1.00013668e+00 6.95013882e-13 2.60613738e-13 5.57327848e-02 -7.06639944e-04 4.25575238e-04 2.95839882e-04 + 6.20000000e+00 1.00014014e+00 -1.18361292e-10 -5.38668103e-11 5.70789280e-02 -7.18245635e-04 4.38722537e-04 2.98661320e-04 + 6.70000000e+00 1.00014357e+00 -8.97141176e-11 -1.84654165e-11 5.84153057e-02 -7.29843796e-04 4.51256987e-04 3.01495428e-04 + 7.45000000e+00 1.00014882e+00 3.62872171e-13 5.60514160e-15 6.03994060e-02 -7.47134886e-04 4.69004480e-04 3.06132483e-04 + 8.20000000e+00 1.00015401e+00 1.83408448e-13 3.05331093e-13 6.23636812e-02 -7.64532811e-04 4.85636101e-04 3.11902423e-04 + 8.95000000e+00 1.00015916e+00 2.29380729e-13 -1.32034810e-15 6.43090718e-02 -7.82262094e-04 5.01414998e-04 3.18953775e-04 + 9.70000000e+00 1.00016427e+00 -6.29741242e-13 -3.33759748e-13 6.62364103e-02 -8.00211697e-04 5.16786885e-04 3.27118764e-04 + 1.07000000e+01 1.00017118e+00 -2.61063745e-12 -1.61462778e-12 6.87754359e-02 -8.24434961e-04 5.36685779e-04 3.39412157e-04 diff --git a/test/data/test_results/voce_ea/avg_def_grad_global.txt b/test/data/test_results/voce_ea/avg_def_grad_global.txt new file mode 100644 index 0000000..d5e43d1 --- /dev/null +++ b/test/data/test_results/voce_ea/avg_def_grad_global.txt @@ -0,0 +1,41 @@ + # Time Volume F11 F12 F13 F21 F22 F23 F31 F32 F33 + 5.00000000e-03 1.00000159e+00 9.99998367e-01 -1.88826625e-07 -7.53880704e-08 1.74019841e-07 9.99998222e-01 -3.51738840e-08 -1.22636634e-07 3.73904224e-08 1.00000500e+00 + 2.00000000e-01 1.00006342e+00 9.99934654e-01 -7.57044467e-06 -3.16403984e-06 7.03427207e-06 9.99928791e-01 -1.40827224e-06 -4.80521001e-06 1.41614602e-06 1.00020000e+00 + 3.00000000e-01 1.00008461e+00 9.99898188e-01 -1.41746396e-05 -7.31696751e-06 1.32312152e-05 9.99886475e-01 -1.65817264e-06 -4.96304235e-06 4.44807391e-07 1.00030000e+00 + 4.00000000e-01 1.00009168e+00 9.99856845e-01 -2.40307433e-05 -1.27288105e-05 2.15946079e-05 9.99834935e-01 -3.89935238e-07 -4.38625022e-06 -3.74825402e-06 1.00040000e+00 + 5.00000000e-01 1.00009491e+00 9.99814611e-01 -3.55667768e-05 -1.83097301e-05 3.10704467e-05 9.99780465e-01 2.31649066e-06 -2.16249012e-06 -8.64354115e-06 1.00050000e+00 + 6.00000000e-01 1.00009697e+00 9.99772314e-01 -4.88520426e-05 -2.31824766e-05 4.20005166e-05 9.99724905e-01 4.22156758e-06 3.21106225e-07 -1.17686074e-05 1.00060000e+00 + 7.00000000e-01 1.00009854e+00 9.99729916e-01 -6.34419664e-05 -2.74292874e-05 5.41840704e-05 9.99668965e-01 5.34648837e-06 2.52464658e-06 -1.36243999e-05 1.00070000e+00 + 8.00000000e-01 1.00009984e+00 9.99687402e-01 -7.88686279e-05 -3.12666771e-05 6.73929744e-05 9.99612884e-01 5.94666037e-06 4.40199655e-06 -1.46580360e-05 1.00080000e+00 + 9.00000000e-01 1.00010097e+00 9.99644849e-01 -9.48375925e-05 -3.48067739e-05 8.14165691e-05 9.99556696e-01 6.17376242e-06 6.04838041e-06 -1.51370247e-05 1.00090000e+00 + 1.00000000e+00 1.00010200e+00 9.99602300e-01 -1.11196359e-04 -3.81216935e-05 9.60588391e-05 9.99500410e-01 6.12708856e-06 7.54992764e-06 -1.52076304e-05 1.00100000e+00 + 1.10000000e+00 1.00010295e+00 9.99559758e-01 -1.27801741e-04 -4.11977468e-05 1.11157784e-04 9.99444061e-01 5.82843180e-06 8.89753537e-06 -1.49148036e-05 1.00110000e+00 + 1.20000000e+00 1.00010385e+00 9.99517213e-01 -1.44570909e-04 -4.40176245e-05 1.26579845e-04 9.99387677e-01 5.33940749e-06 1.00509872e-05 -1.43669477e-05 1.00120000e+00 + 1.30000000e+00 1.00010472e+00 9.99474669e-01 -1.61463320e-04 -4.65993681e-05 1.42185463e-04 9.99331272e-01 4.71333087e-06 1.10226177e-05 -1.36483212e-05 1.00130000e+00 + 1.40000000e+00 1.00010556e+00 9.99432126e-01 -1.78451839e-04 -4.89658519e-05 1.57881555e-04 9.99274853e-01 3.98618817e-06 1.18244556e-05 -1.28021794e-05 1.00140000e+00 + 1.50000000e+00 1.00010637e+00 9.99389591e-01 -1.95516878e-04 -5.11238131e-05 1.73635213e-04 9.99218420e-01 3.19985488e-06 1.24491663e-05 -1.18741698e-05 1.00150000e+00 + 1.60000000e+00 1.00010717e+00 9.99347067e-01 -2.12637529e-04 -5.30900304e-05 1.89430980e-04 9.99161975e-01 2.38670367e-06 1.29027299e-05 -1.08977133e-05 1.00160000e+00 + 1.70000000e+00 1.00010796e+00 9.99304551e-01 -2.29803971e-04 -5.48978440e-05 2.05251287e-04 9.99105523e-01 1.55823911e-06 1.32103868e-05 -9.88616479e-06 1.00170000e+00 + 1.80000000e+00 1.00010874e+00 9.99262036e-01 -2.46981182e-04 -5.65861021e-05 2.21057960e-04 9.99049074e-01 7.20123926e-07 1.34106834e-05 -8.84872217e-06 1.00180000e+00 + 1.90000000e+00 1.00010951e+00 9.99219519e-01 -2.64145888e-04 -5.81728253e-05 2.36816072e-04 9.98992633e-01 -1.24788525e-07 1.35237244e-05 -7.78748955e-06 1.00190000e+00 + 2.00000000e+00 1.00011027e+00 9.99177008e-01 -2.81310076e-04 -5.96585988e-05 2.52517939e-04 9.98936192e-01 -9.77525393e-07 1.35469740e-05 -6.70026274e-06 1.00200000e+00 + 2.10000000e+00 1.00011102e+00 9.99134514e-01 -2.98479800e-04 -6.10443661e-05 2.68166428e-04 9.98879743e-01 -1.83792829e-06 1.34770679e-05 -5.58779995e-06 1.00210000e+00 + 2.30000000e+00 1.00011252e+00 9.99049604e-01 -3.32807437e-04 -6.34432113e-05 2.99264569e-04 9.98766805e-01 -3.59336076e-06 1.30011623e-05 -3.32820104e-06 1.00230000e+00 + 2.50000000e+00 1.00011399e+00 9.98964752e-01 -3.67051189e-04 -6.55315771e-05 3.30135207e-04 9.98653850e-01 -5.39558989e-06 1.22435385e-05 -1.12051887e-06 1.00250000e+00 + 2.70000000e+00 1.00011546e+00 9.98879957e-01 -4.01139779e-04 -6.73587644e-05 3.60780070e-04 9.98540880e-01 -7.25717557e-06 1.12206152e-05 1.04225499e-06 1.00270000e+00 + 2.90000000e+00 1.00011691e+00 9.98795222e-01 -4.35031934e-04 -6.89732637e-05 3.91197403e-04 9.98427898e-01 -9.17265231e-06 9.96838606e-06 3.23813203e-06 1.00290000e+00 + 3.10000000e+00 1.00011834e+00 9.98710557e-01 -4.68736956e-04 -7.04139116e-05 4.21368707e-04 9.98314893e-01 -1.11355378e-05 8.52953064e-06 5.52257361e-06 1.00310000e+00 + 3.30000000e+00 1.00011977e+00 9.98625973e-01 -5.02267713e-04 -7.17189107e-05 4.51296427e-04 9.98201856e-01 -1.31219058e-05 6.95835411e-06 7.90036462e-06 1.00330000e+00 + 3.70000000e+00 1.00012265e+00 9.98457163e-01 -5.68693178e-04 -7.38878302e-05 5.10414379e-04 9.97975630e-01 -1.70721914e-05 3.42221595e-06 1.29647022e-05 1.00370001e+00 + 4.10000000e+00 1.00012550e+00 9.98288742e-01 -6.34401017e-04 -7.56637440e-05 5.68923262e-04 9.97749225e-01 -2.08916486e-05 -4.87189014e-07 1.82938221e-05 1.00410001e+00 + 4.50000000e+00 1.00012833e+00 9.98120689e-01 -6.99262320e-04 -7.71186688e-05 6.26804308e-04 9.97522664e-01 -2.46168923e-05 -4.79026157e-06 2.39382483e-05 1.00450001e+00 + 4.90000000e+00 1.00013113e+00 9.97952953e-01 -7.63255931e-04 -7.83293699e-05 6.84060462e-04 9.97296003e-01 -2.82892715e-05 -9.47004224e-06 2.98546484e-05 1.00490001e+00 + 5.10000000e+00 1.00013250e+00 9.97869157e-01 -7.95026065e-04 -7.88796534e-05 7.12516423e-04 9.97182654e-01 -3.01312694e-05 -1.18873614e-05 3.28812789e-05 1.00510001e+00 + 5.70000000e+00 1.00013673e+00 9.97618361e-01 -8.88683744e-04 -8.00168785e-05 7.96510116e-04 9.96842510e-01 -3.58124001e-05 -1.94911791e-05 4.25192888e-05 1.00570001e+00 + 6.20000000e+00 1.00014020e+00 9.97409750e-01 -9.65676039e-04 -8.06667370e-05 8.65523564e-04 9.96559016e-01 -4.06130219e-05 -2.59070435e-05 5.07941029e-05 1.00620001e+00 + 6.70000000e+00 1.00014365e+00 9.97201517e-01 -1.04171535e-03 -8.10872116e-05 9.33663181e-04 9.96275491e-01 -4.54482150e-05 -3.23378402e-05 5.92697265e-05 1.00670001e+00 + 7.45000000e+00 1.00014890e+00 9.96889918e-01 -1.15396352e-03 -8.13942588e-05 1.03422500e-03 9.95850236e-01 -5.26150537e-05 -4.18315823e-05 7.21616845e-05 1.00745001e+00 + 8.20000000e+00 1.00015411e+00 9.96578983e-01 -1.26444491e-03 -8.14358301e-05 1.13318974e-03 9.95425100e-01 -5.96286760e-05 -5.12825700e-05 8.51475166e-05 1.00820001e+00 + 8.95000000e+00 1.00015927e+00 9.96268509e-01 -1.37331980e-03 -8.12288000e-05 1.23076669e-03 9.95000287e-01 -6.63516493e-05 -6.07699654e-05 9.80967230e-05 1.00895002e+00 + 9.70000000e+00 1.00016439e+00 9.95958381e-01 -1.48072193e-03 -8.08259800e-05 1.32710961e-03 9.94575913e-01 -7.28208319e-05 -7.02341181e-05 1.10979689e-04 1.00970002e+00 + 1.07000000e+01 1.00017133e+00 9.95545498e-01 -1.62133902e-03 -8.01510467e-05 1.45334494e-03 9.94010856e-01 -8.12409183e-05 -8.27019572e-05 1.28133523e-04 1.01070002e+00 diff --git a/test/data/test_results/voce_ea/avg_def_grad_region_default_1.txt b/test/data/test_results/voce_ea/avg_def_grad_region_default_1.txt new file mode 100644 index 0000000..d5e43d1 --- /dev/null +++ b/test/data/test_results/voce_ea/avg_def_grad_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume F11 F12 F13 F21 F22 F23 F31 F32 F33 + 5.00000000e-03 1.00000159e+00 9.99998367e-01 -1.88826625e-07 -7.53880704e-08 1.74019841e-07 9.99998222e-01 -3.51738840e-08 -1.22636634e-07 3.73904224e-08 1.00000500e+00 + 2.00000000e-01 1.00006342e+00 9.99934654e-01 -7.57044467e-06 -3.16403984e-06 7.03427207e-06 9.99928791e-01 -1.40827224e-06 -4.80521001e-06 1.41614602e-06 1.00020000e+00 + 3.00000000e-01 1.00008461e+00 9.99898188e-01 -1.41746396e-05 -7.31696751e-06 1.32312152e-05 9.99886475e-01 -1.65817264e-06 -4.96304235e-06 4.44807391e-07 1.00030000e+00 + 4.00000000e-01 1.00009168e+00 9.99856845e-01 -2.40307433e-05 -1.27288105e-05 2.15946079e-05 9.99834935e-01 -3.89935238e-07 -4.38625022e-06 -3.74825402e-06 1.00040000e+00 + 5.00000000e-01 1.00009491e+00 9.99814611e-01 -3.55667768e-05 -1.83097301e-05 3.10704467e-05 9.99780465e-01 2.31649066e-06 -2.16249012e-06 -8.64354115e-06 1.00050000e+00 + 6.00000000e-01 1.00009697e+00 9.99772314e-01 -4.88520426e-05 -2.31824766e-05 4.20005166e-05 9.99724905e-01 4.22156758e-06 3.21106225e-07 -1.17686074e-05 1.00060000e+00 + 7.00000000e-01 1.00009854e+00 9.99729916e-01 -6.34419664e-05 -2.74292874e-05 5.41840704e-05 9.99668965e-01 5.34648837e-06 2.52464658e-06 -1.36243999e-05 1.00070000e+00 + 8.00000000e-01 1.00009984e+00 9.99687402e-01 -7.88686279e-05 -3.12666771e-05 6.73929744e-05 9.99612884e-01 5.94666037e-06 4.40199655e-06 -1.46580360e-05 1.00080000e+00 + 9.00000000e-01 1.00010097e+00 9.99644849e-01 -9.48375925e-05 -3.48067739e-05 8.14165691e-05 9.99556696e-01 6.17376242e-06 6.04838041e-06 -1.51370247e-05 1.00090000e+00 + 1.00000000e+00 1.00010200e+00 9.99602300e-01 -1.11196359e-04 -3.81216935e-05 9.60588391e-05 9.99500410e-01 6.12708856e-06 7.54992764e-06 -1.52076304e-05 1.00100000e+00 + 1.10000000e+00 1.00010295e+00 9.99559758e-01 -1.27801741e-04 -4.11977468e-05 1.11157784e-04 9.99444061e-01 5.82843180e-06 8.89753537e-06 -1.49148036e-05 1.00110000e+00 + 1.20000000e+00 1.00010385e+00 9.99517213e-01 -1.44570909e-04 -4.40176245e-05 1.26579845e-04 9.99387677e-01 5.33940749e-06 1.00509872e-05 -1.43669477e-05 1.00120000e+00 + 1.30000000e+00 1.00010472e+00 9.99474669e-01 -1.61463320e-04 -4.65993681e-05 1.42185463e-04 9.99331272e-01 4.71333087e-06 1.10226177e-05 -1.36483212e-05 1.00130000e+00 + 1.40000000e+00 1.00010556e+00 9.99432126e-01 -1.78451839e-04 -4.89658519e-05 1.57881555e-04 9.99274853e-01 3.98618817e-06 1.18244556e-05 -1.28021794e-05 1.00140000e+00 + 1.50000000e+00 1.00010637e+00 9.99389591e-01 -1.95516878e-04 -5.11238131e-05 1.73635213e-04 9.99218420e-01 3.19985488e-06 1.24491663e-05 -1.18741698e-05 1.00150000e+00 + 1.60000000e+00 1.00010717e+00 9.99347067e-01 -2.12637529e-04 -5.30900304e-05 1.89430980e-04 9.99161975e-01 2.38670367e-06 1.29027299e-05 -1.08977133e-05 1.00160000e+00 + 1.70000000e+00 1.00010796e+00 9.99304551e-01 -2.29803971e-04 -5.48978440e-05 2.05251287e-04 9.99105523e-01 1.55823911e-06 1.32103868e-05 -9.88616479e-06 1.00170000e+00 + 1.80000000e+00 1.00010874e+00 9.99262036e-01 -2.46981182e-04 -5.65861021e-05 2.21057960e-04 9.99049074e-01 7.20123926e-07 1.34106834e-05 -8.84872217e-06 1.00180000e+00 + 1.90000000e+00 1.00010951e+00 9.99219519e-01 -2.64145888e-04 -5.81728253e-05 2.36816072e-04 9.98992633e-01 -1.24788525e-07 1.35237244e-05 -7.78748955e-06 1.00190000e+00 + 2.00000000e+00 1.00011027e+00 9.99177008e-01 -2.81310076e-04 -5.96585988e-05 2.52517939e-04 9.98936192e-01 -9.77525393e-07 1.35469740e-05 -6.70026274e-06 1.00200000e+00 + 2.10000000e+00 1.00011102e+00 9.99134514e-01 -2.98479800e-04 -6.10443661e-05 2.68166428e-04 9.98879743e-01 -1.83792829e-06 1.34770679e-05 -5.58779995e-06 1.00210000e+00 + 2.30000000e+00 1.00011252e+00 9.99049604e-01 -3.32807437e-04 -6.34432113e-05 2.99264569e-04 9.98766805e-01 -3.59336076e-06 1.30011623e-05 -3.32820104e-06 1.00230000e+00 + 2.50000000e+00 1.00011399e+00 9.98964752e-01 -3.67051189e-04 -6.55315771e-05 3.30135207e-04 9.98653850e-01 -5.39558989e-06 1.22435385e-05 -1.12051887e-06 1.00250000e+00 + 2.70000000e+00 1.00011546e+00 9.98879957e-01 -4.01139779e-04 -6.73587644e-05 3.60780070e-04 9.98540880e-01 -7.25717557e-06 1.12206152e-05 1.04225499e-06 1.00270000e+00 + 2.90000000e+00 1.00011691e+00 9.98795222e-01 -4.35031934e-04 -6.89732637e-05 3.91197403e-04 9.98427898e-01 -9.17265231e-06 9.96838606e-06 3.23813203e-06 1.00290000e+00 + 3.10000000e+00 1.00011834e+00 9.98710557e-01 -4.68736956e-04 -7.04139116e-05 4.21368707e-04 9.98314893e-01 -1.11355378e-05 8.52953064e-06 5.52257361e-06 1.00310000e+00 + 3.30000000e+00 1.00011977e+00 9.98625973e-01 -5.02267713e-04 -7.17189107e-05 4.51296427e-04 9.98201856e-01 -1.31219058e-05 6.95835411e-06 7.90036462e-06 1.00330000e+00 + 3.70000000e+00 1.00012265e+00 9.98457163e-01 -5.68693178e-04 -7.38878302e-05 5.10414379e-04 9.97975630e-01 -1.70721914e-05 3.42221595e-06 1.29647022e-05 1.00370001e+00 + 4.10000000e+00 1.00012550e+00 9.98288742e-01 -6.34401017e-04 -7.56637440e-05 5.68923262e-04 9.97749225e-01 -2.08916486e-05 -4.87189014e-07 1.82938221e-05 1.00410001e+00 + 4.50000000e+00 1.00012833e+00 9.98120689e-01 -6.99262320e-04 -7.71186688e-05 6.26804308e-04 9.97522664e-01 -2.46168923e-05 -4.79026157e-06 2.39382483e-05 1.00450001e+00 + 4.90000000e+00 1.00013113e+00 9.97952953e-01 -7.63255931e-04 -7.83293699e-05 6.84060462e-04 9.97296003e-01 -2.82892715e-05 -9.47004224e-06 2.98546484e-05 1.00490001e+00 + 5.10000000e+00 1.00013250e+00 9.97869157e-01 -7.95026065e-04 -7.88796534e-05 7.12516423e-04 9.97182654e-01 -3.01312694e-05 -1.18873614e-05 3.28812789e-05 1.00510001e+00 + 5.70000000e+00 1.00013673e+00 9.97618361e-01 -8.88683744e-04 -8.00168785e-05 7.96510116e-04 9.96842510e-01 -3.58124001e-05 -1.94911791e-05 4.25192888e-05 1.00570001e+00 + 6.20000000e+00 1.00014020e+00 9.97409750e-01 -9.65676039e-04 -8.06667370e-05 8.65523564e-04 9.96559016e-01 -4.06130219e-05 -2.59070435e-05 5.07941029e-05 1.00620001e+00 + 6.70000000e+00 1.00014365e+00 9.97201517e-01 -1.04171535e-03 -8.10872116e-05 9.33663181e-04 9.96275491e-01 -4.54482150e-05 -3.23378402e-05 5.92697265e-05 1.00670001e+00 + 7.45000000e+00 1.00014890e+00 9.96889918e-01 -1.15396352e-03 -8.13942588e-05 1.03422500e-03 9.95850236e-01 -5.26150537e-05 -4.18315823e-05 7.21616845e-05 1.00745001e+00 + 8.20000000e+00 1.00015411e+00 9.96578983e-01 -1.26444491e-03 -8.14358301e-05 1.13318974e-03 9.95425100e-01 -5.96286760e-05 -5.12825700e-05 8.51475166e-05 1.00820001e+00 + 8.95000000e+00 1.00015927e+00 9.96268509e-01 -1.37331980e-03 -8.12288000e-05 1.23076669e-03 9.95000287e-01 -6.63516493e-05 -6.07699654e-05 9.80967230e-05 1.00895002e+00 + 9.70000000e+00 1.00016439e+00 9.95958381e-01 -1.48072193e-03 -8.08259800e-05 1.32710961e-03 9.94575913e-01 -7.28208319e-05 -7.02341181e-05 1.10979689e-04 1.00970002e+00 + 1.07000000e+01 1.00017133e+00 9.95545498e-01 -1.62133902e-03 -8.01510467e-05 1.45334494e-03 9.94010856e-01 -8.12409183e-05 -8.27019572e-05 1.28133523e-04 1.01070002e+00 diff --git a/test/data/test_results/voce_ea/avg_elastic_strain_global.txt b/test/data/test_results/voce_ea/avg_elastic_strain_global.txt new file mode 100644 index 0000000..c9b1889 --- /dev/null +++ b/test/data/test_results/voce_ea/avg_elastic_strain_global.txt @@ -0,0 +1,41 @@ + # Time Volume Ee11 Ee22 Ee33 Ee23 Ee13 Ee12 + 5.00000000e-03 1.00000159e+00 -5.49512301e-08 -1.01222410e-07 7.26890046e-07 4.43489262e-09 -3.18156292e-08 8.63899603e-09 + 2.00000000e-01 1.00006342e+00 -2.21054934e-06 -4.05877277e-06 2.90293485e-05 1.75591296e-07 -1.27319890e-06 3.46545933e-07 + 3.00000000e-01 1.00008461e+00 -2.97553666e-06 -6.10835316e-06 3.90872844e-05 2.71334300e-07 -1.82698522e-06 3.59481459e-07 + 4.00000000e-01 1.00009168e+00 -3.19640414e-06 -7.41790886e-06 4.26942074e-05 1.63833888e-07 -2.14837328e-06 2.64527458e-07 + 5.00000000e-01 1.00009491e+00 -3.14188918e-06 -8.05774981e-06 4.42442372e-05 -3.44282033e-08 -2.30167300e-06 7.60145195e-08 + 6.00000000e-01 1.00009697e+00 -2.98513842e-06 -8.35267942e-06 4.51147061e-05 -2.26300007e-07 -2.39105412e-06 -1.48196953e-07 + 7.00000000e-01 1.00009854e+00 -2.86242247e-06 -8.53183570e-06 4.57291303e-05 -3.87006965e-07 -2.46187011e-06 -3.45561501e-07 + 8.00000000e-01 1.00009984e+00 -2.80285447e-06 -8.66310595e-06 4.62250247e-05 -5.23521370e-07 -2.52613414e-06 -5.03589238e-07 + 9.00000000e-01 1.00010097e+00 -2.79080097e-06 -8.76883470e-06 4.66542413e-05 -6.47715676e-07 -2.58772990e-06 -6.29886648e-07 + 1.00000000e+00 1.00010200e+00 -2.80632995e-06 -8.85287340e-06 4.70427100e-05 -7.61074853e-07 -2.64401835e-06 -7.31654470e-07 + 1.10000000e+00 1.00010295e+00 -2.83560854e-06 -8.92173923e-06 4.74028703e-05 -8.62649780e-07 -2.69317810e-06 -8.14616016e-07 + 1.20000000e+00 1.00010385e+00 -2.87047935e-06 -8.98151410e-06 4.77420309e-05 -9.53100598e-07 -2.73464688e-06 -8.83171588e-07 + 1.30000000e+00 1.00010472e+00 -2.90583300e-06 -9.03526531e-06 4.80661204e-05 -1.03470440e-06 -2.76965470e-06 -9.40975908e-07 + 1.40000000e+00 1.00010556e+00 -2.94076315e-06 -9.08500497e-06 4.83801258e-05 -1.10946498e-06 -2.80004930e-06 -9.90032611e-07 + 1.50000000e+00 1.00010637e+00 -2.97438472e-06 -9.13220076e-06 4.86871235e-05 -1.17870810e-06 -2.82721305e-06 -1.03211980e-06 + 1.60000000e+00 1.00010717e+00 -3.00590287e-06 -9.17768715e-06 4.89890891e-05 -1.24271454e-06 -2.85248997e-06 -1.06890986e-06 + 1.70000000e+00 1.00010796e+00 -3.03538372e-06 -9.22185394e-06 4.92872852e-05 -1.30125347e-06 -2.87677083e-06 -1.10136508e-06 + 1.80000000e+00 1.00010874e+00 -3.06372239e-06 -9.26483970e-06 4.95818392e-05 -1.35546094e-06 -2.90060859e-06 -1.13034235e-06 + 1.90000000e+00 1.00010951e+00 -3.09125938e-06 -9.30691507e-06 4.98728243e-05 -1.40630930e-06 -2.92404264e-06 -1.15680426e-06 + 2.00000000e+00 1.00011027e+00 -3.11766575e-06 -9.34865139e-06 5.01606220e-05 -1.45400515e-06 -2.94686241e-06 -1.18157038e-06 + 2.10000000e+00 1.00011102e+00 -3.14289458e-06 -9.39068019e-06 5.04459305e-05 -1.49878790e-06 -2.96906028e-06 -1.20487613e-06 + 2.30000000e+00 1.00011252e+00 -3.19137804e-06 -9.47501988e-06 5.10119639e-05 -1.58041457e-06 -3.01152137e-06 -1.24681523e-06 + 2.50000000e+00 1.00011399e+00 -3.23948032e-06 -9.55797842e-06 5.15755355e-05 -1.65675847e-06 -3.05188233e-06 -1.28585131e-06 + 2.70000000e+00 1.00011546e+00 -3.28742827e-06 -9.63919465e-06 5.21381105e-05 -1.72905186e-06 -3.09099940e-06 -1.32441943e-06 + 2.90000000e+00 1.00011691e+00 -3.33580865e-06 -9.71890241e-06 5.27005829e-05 -1.79741433e-06 -3.12932679e-06 -1.36296160e-06 + 3.10000000e+00 1.00011834e+00 -3.38611235e-06 -9.79688369e-06 5.32630896e-05 -1.86179047e-06 -3.16658376e-06 -1.40030070e-06 + 3.30000000e+00 1.00011977e+00 -3.43648663e-06 -9.87440319e-06 5.38248119e-05 -1.92280433e-06 -3.20312315e-06 -1.43500780e-06 + 3.70000000e+00 1.00012265e+00 -3.53377373e-06 -1.00329067e-05 5.49457971e-05 -2.03723347e-06 -3.27558036e-06 -1.49737485e-06 + 4.10000000e+00 1.00012550e+00 -3.63123035e-06 -1.01934127e-05 5.60666966e-05 -2.14361773e-06 -3.34702096e-06 -1.55335945e-06 + 4.50000000e+00 1.00012833e+00 -3.73074989e-06 -1.03498905e-05 5.71852029e-05 -2.24385726e-06 -3.41653198e-06 -1.60203883e-06 + 4.90000000e+00 1.00013113e+00 -3.83012512e-06 -1.05039785e-05 5.83003216e-05 -2.33682205e-06 -3.48360504e-06 -1.64675861e-06 + 5.10000000e+00 1.00013250e+00 -3.87862395e-06 -1.05823734e-05 5.88578575e-05 -2.38115855e-06 -3.51681523e-06 -1.66894887e-06 + 5.70000000e+00 1.00013673e+00 -4.01279784e-06 -1.08231945e-05 6.05233008e-05 -2.50418177e-06 -3.61330796e-06 -1.73677168e-06 + 6.20000000e+00 1.00014020e+00 -4.12063601e-06 -1.10295400e-05 6.19049757e-05 -2.60361004e-06 -3.69273029e-06 -1.79518373e-06 + 6.70000000e+00 1.00014365e+00 -4.22515219e-06 -1.12386066e-05 6.32803743e-05 -2.69863133e-06 -3.77165750e-06 -1.85427071e-06 + 7.45000000e+00 1.00014890e+00 -4.37450512e-06 -1.15562715e-05 6.53298269e-05 -2.83469668e-06 -3.89130059e-06 -1.94054618e-06 + 8.20000000e+00 1.00015411e+00 -4.52121356e-06 -1.18711053e-05 6.73697789e-05 -2.95894826e-06 -4.00974218e-06 -2.02331647e-06 + 8.95000000e+00 1.00015927e+00 -4.65921109e-06 -1.21840273e-05 6.94013838e-05 -3.07552365e-06 -4.12641811e-06 -2.10277570e-06 + 9.70000000e+00 1.00016439e+00 -4.78734675e-06 -1.24989666e-05 7.14232869e-05 -3.18671242e-06 -4.24209418e-06 -2.18276570e-06 + 1.07000000e+01 1.00017133e+00 -4.95208960e-06 -1.29194629e-05 7.40950378e-05 -3.32696868e-06 -4.39569918e-06 -2.28850021e-06 diff --git a/test/data/test_results/voce_ea/avg_elastic_strain_region_default_1.txt b/test/data/test_results/voce_ea/avg_elastic_strain_region_default_1.txt new file mode 100644 index 0000000..c9b1889 --- /dev/null +++ b/test/data/test_results/voce_ea/avg_elastic_strain_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Ee11 Ee22 Ee33 Ee23 Ee13 Ee12 + 5.00000000e-03 1.00000159e+00 -5.49512301e-08 -1.01222410e-07 7.26890046e-07 4.43489262e-09 -3.18156292e-08 8.63899603e-09 + 2.00000000e-01 1.00006342e+00 -2.21054934e-06 -4.05877277e-06 2.90293485e-05 1.75591296e-07 -1.27319890e-06 3.46545933e-07 + 3.00000000e-01 1.00008461e+00 -2.97553666e-06 -6.10835316e-06 3.90872844e-05 2.71334300e-07 -1.82698522e-06 3.59481459e-07 + 4.00000000e-01 1.00009168e+00 -3.19640414e-06 -7.41790886e-06 4.26942074e-05 1.63833888e-07 -2.14837328e-06 2.64527458e-07 + 5.00000000e-01 1.00009491e+00 -3.14188918e-06 -8.05774981e-06 4.42442372e-05 -3.44282033e-08 -2.30167300e-06 7.60145195e-08 + 6.00000000e-01 1.00009697e+00 -2.98513842e-06 -8.35267942e-06 4.51147061e-05 -2.26300007e-07 -2.39105412e-06 -1.48196953e-07 + 7.00000000e-01 1.00009854e+00 -2.86242247e-06 -8.53183570e-06 4.57291303e-05 -3.87006965e-07 -2.46187011e-06 -3.45561501e-07 + 8.00000000e-01 1.00009984e+00 -2.80285447e-06 -8.66310595e-06 4.62250247e-05 -5.23521370e-07 -2.52613414e-06 -5.03589238e-07 + 9.00000000e-01 1.00010097e+00 -2.79080097e-06 -8.76883470e-06 4.66542413e-05 -6.47715676e-07 -2.58772990e-06 -6.29886648e-07 + 1.00000000e+00 1.00010200e+00 -2.80632995e-06 -8.85287340e-06 4.70427100e-05 -7.61074853e-07 -2.64401835e-06 -7.31654470e-07 + 1.10000000e+00 1.00010295e+00 -2.83560854e-06 -8.92173923e-06 4.74028703e-05 -8.62649780e-07 -2.69317810e-06 -8.14616016e-07 + 1.20000000e+00 1.00010385e+00 -2.87047935e-06 -8.98151410e-06 4.77420309e-05 -9.53100598e-07 -2.73464688e-06 -8.83171588e-07 + 1.30000000e+00 1.00010472e+00 -2.90583300e-06 -9.03526531e-06 4.80661204e-05 -1.03470440e-06 -2.76965470e-06 -9.40975908e-07 + 1.40000000e+00 1.00010556e+00 -2.94076315e-06 -9.08500497e-06 4.83801258e-05 -1.10946498e-06 -2.80004930e-06 -9.90032611e-07 + 1.50000000e+00 1.00010637e+00 -2.97438472e-06 -9.13220076e-06 4.86871235e-05 -1.17870810e-06 -2.82721305e-06 -1.03211980e-06 + 1.60000000e+00 1.00010717e+00 -3.00590287e-06 -9.17768715e-06 4.89890891e-05 -1.24271454e-06 -2.85248997e-06 -1.06890986e-06 + 1.70000000e+00 1.00010796e+00 -3.03538372e-06 -9.22185394e-06 4.92872852e-05 -1.30125347e-06 -2.87677083e-06 -1.10136508e-06 + 1.80000000e+00 1.00010874e+00 -3.06372239e-06 -9.26483970e-06 4.95818392e-05 -1.35546094e-06 -2.90060859e-06 -1.13034235e-06 + 1.90000000e+00 1.00010951e+00 -3.09125938e-06 -9.30691507e-06 4.98728243e-05 -1.40630930e-06 -2.92404264e-06 -1.15680426e-06 + 2.00000000e+00 1.00011027e+00 -3.11766575e-06 -9.34865139e-06 5.01606220e-05 -1.45400515e-06 -2.94686241e-06 -1.18157038e-06 + 2.10000000e+00 1.00011102e+00 -3.14289458e-06 -9.39068019e-06 5.04459305e-05 -1.49878790e-06 -2.96906028e-06 -1.20487613e-06 + 2.30000000e+00 1.00011252e+00 -3.19137804e-06 -9.47501988e-06 5.10119639e-05 -1.58041457e-06 -3.01152137e-06 -1.24681523e-06 + 2.50000000e+00 1.00011399e+00 -3.23948032e-06 -9.55797842e-06 5.15755355e-05 -1.65675847e-06 -3.05188233e-06 -1.28585131e-06 + 2.70000000e+00 1.00011546e+00 -3.28742827e-06 -9.63919465e-06 5.21381105e-05 -1.72905186e-06 -3.09099940e-06 -1.32441943e-06 + 2.90000000e+00 1.00011691e+00 -3.33580865e-06 -9.71890241e-06 5.27005829e-05 -1.79741433e-06 -3.12932679e-06 -1.36296160e-06 + 3.10000000e+00 1.00011834e+00 -3.38611235e-06 -9.79688369e-06 5.32630896e-05 -1.86179047e-06 -3.16658376e-06 -1.40030070e-06 + 3.30000000e+00 1.00011977e+00 -3.43648663e-06 -9.87440319e-06 5.38248119e-05 -1.92280433e-06 -3.20312315e-06 -1.43500780e-06 + 3.70000000e+00 1.00012265e+00 -3.53377373e-06 -1.00329067e-05 5.49457971e-05 -2.03723347e-06 -3.27558036e-06 -1.49737485e-06 + 4.10000000e+00 1.00012550e+00 -3.63123035e-06 -1.01934127e-05 5.60666966e-05 -2.14361773e-06 -3.34702096e-06 -1.55335945e-06 + 4.50000000e+00 1.00012833e+00 -3.73074989e-06 -1.03498905e-05 5.71852029e-05 -2.24385726e-06 -3.41653198e-06 -1.60203883e-06 + 4.90000000e+00 1.00013113e+00 -3.83012512e-06 -1.05039785e-05 5.83003216e-05 -2.33682205e-06 -3.48360504e-06 -1.64675861e-06 + 5.10000000e+00 1.00013250e+00 -3.87862395e-06 -1.05823734e-05 5.88578575e-05 -2.38115855e-06 -3.51681523e-06 -1.66894887e-06 + 5.70000000e+00 1.00013673e+00 -4.01279784e-06 -1.08231945e-05 6.05233008e-05 -2.50418177e-06 -3.61330796e-06 -1.73677168e-06 + 6.20000000e+00 1.00014020e+00 -4.12063601e-06 -1.10295400e-05 6.19049757e-05 -2.60361004e-06 -3.69273029e-06 -1.79518373e-06 + 6.70000000e+00 1.00014365e+00 -4.22515219e-06 -1.12386066e-05 6.32803743e-05 -2.69863133e-06 -3.77165750e-06 -1.85427071e-06 + 7.45000000e+00 1.00014890e+00 -4.37450512e-06 -1.15562715e-05 6.53298269e-05 -2.83469668e-06 -3.89130059e-06 -1.94054618e-06 + 8.20000000e+00 1.00015411e+00 -4.52121356e-06 -1.18711053e-05 6.73697789e-05 -2.95894826e-06 -4.00974218e-06 -2.02331647e-06 + 8.95000000e+00 1.00015927e+00 -4.65921109e-06 -1.21840273e-05 6.94013838e-05 -3.07552365e-06 -4.12641811e-06 -2.10277570e-06 + 9.70000000e+00 1.00016439e+00 -4.78734675e-06 -1.24989666e-05 7.14232869e-05 -3.18671242e-06 -4.24209418e-06 -2.18276570e-06 + 1.07000000e+01 1.00017133e+00 -4.95208960e-06 -1.29194629e-05 7.40950378e-05 -3.32696868e-06 -4.39569918e-06 -2.28850021e-06 diff --git a/test/data/test_results/voce_ea/avg_eq_pl_strain_global.txt b/test/data/test_results/voce_ea/avg_eq_pl_strain_global.txt new file mode 100644 index 0000000..2f670f9 --- /dev/null +++ b/test/data/test_results/voce_ea/avg_eq_pl_strain_global.txt @@ -0,0 +1,41 @@ + # Time Volume Equiv_Plastic_Stra + 5.00000000e-03 1.00000159e+00 0.00000000e+00 + 2.00000000e-01 1.00006342e+00 3.46755596e-07 + 3.00000000e-01 1.00008461e+00 3.88769161e-05 + 4.00000000e-01 1.00009168e+00 1.30928916e-04 + 5.00000000e-01 1.00009491e+00 2.35451431e-04 + 6.00000000e-01 1.00009697e+00 3.42119589e-04 + 7.00000000e-01 1.00009854e+00 4.49015992e-04 + 8.00000000e-01 1.00009984e+00 5.55702697e-04 + 9.00000000e-01 1.00010097e+00 6.62109631e-04 + 1.00000000e+00 1.00010200e+00 7.68254755e-04 + 1.10000000e+00 1.00010295e+00 8.74186050e-04 + 1.20000000e+00 1.00010385e+00 9.79942153e-04 + 1.30000000e+00 1.00010472e+00 1.08554555e-03 + 1.40000000e+00 1.00010556e+00 1.19101261e-03 + 1.50000000e+00 1.00010637e+00 1.29636040e-03 + 1.60000000e+00 1.00010717e+00 1.40160352e-03 + 1.70000000e+00 1.00010796e+00 1.50675111e-03 + 1.80000000e+00 1.00010874e+00 1.61180875e-03 + 1.90000000e+00 1.00010951e+00 1.71678334e-03 + 2.00000000e+00 1.00011027e+00 1.82168094e-03 + 2.10000000e+00 1.00011102e+00 1.92650448e-03 + 2.30000000e+00 1.00011252e+00 2.13589307e-03 + 2.50000000e+00 1.00011399e+00 2.34501650e-03 + 2.70000000e+00 1.00011546e+00 2.55388684e-03 + 2.90000000e+00 1.00011691e+00 2.76252831e-03 + 3.10000000e+00 1.00011834e+00 2.97095809e-03 + 3.30000000e+00 1.00011977e+00 3.17919134e-03 + 3.70000000e+00 1.00012265e+00 3.59498410e-03 + 4.10000000e+00 1.00012550e+00 4.01013859e-03 + 4.50000000e+00 1.00012833e+00 4.42469838e-03 + 4.90000000e+00 1.00013113e+00 4.83870196e-03 + 5.10000000e+00 1.00013250e+00 5.04556199e-03 + 5.70000000e+00 1.00013673e+00 5.66508502e-03 + 6.20000000e+00 1.00014020e+00 6.18066249e-03 + 6.70000000e+00 1.00014365e+00 6.69560488e-03 + 7.45000000e+00 1.00014890e+00 7.46673667e-03 + 8.20000000e+00 1.00015411e+00 8.23662585e-03 + 8.95000000e+00 1.00015927e+00 9.00533261e-03 + 9.70000000e+00 1.00016439e+00 9.77293890e-03 + 1.07000000e+01 1.00017133e+00 1.07946466e-02 diff --git a/test/data/test_results/voce_ea/avg_eq_pl_strain_region_default_1.txt b/test/data/test_results/voce_ea/avg_eq_pl_strain_region_default_1.txt new file mode 100644 index 0000000..2f670f9 --- /dev/null +++ b/test/data/test_results/voce_ea/avg_eq_pl_strain_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Equiv_Plastic_Stra + 5.00000000e-03 1.00000159e+00 0.00000000e+00 + 2.00000000e-01 1.00006342e+00 3.46755596e-07 + 3.00000000e-01 1.00008461e+00 3.88769161e-05 + 4.00000000e-01 1.00009168e+00 1.30928916e-04 + 5.00000000e-01 1.00009491e+00 2.35451431e-04 + 6.00000000e-01 1.00009697e+00 3.42119589e-04 + 7.00000000e-01 1.00009854e+00 4.49015992e-04 + 8.00000000e-01 1.00009984e+00 5.55702697e-04 + 9.00000000e-01 1.00010097e+00 6.62109631e-04 + 1.00000000e+00 1.00010200e+00 7.68254755e-04 + 1.10000000e+00 1.00010295e+00 8.74186050e-04 + 1.20000000e+00 1.00010385e+00 9.79942153e-04 + 1.30000000e+00 1.00010472e+00 1.08554555e-03 + 1.40000000e+00 1.00010556e+00 1.19101261e-03 + 1.50000000e+00 1.00010637e+00 1.29636040e-03 + 1.60000000e+00 1.00010717e+00 1.40160352e-03 + 1.70000000e+00 1.00010796e+00 1.50675111e-03 + 1.80000000e+00 1.00010874e+00 1.61180875e-03 + 1.90000000e+00 1.00010951e+00 1.71678334e-03 + 2.00000000e+00 1.00011027e+00 1.82168094e-03 + 2.10000000e+00 1.00011102e+00 1.92650448e-03 + 2.30000000e+00 1.00011252e+00 2.13589307e-03 + 2.50000000e+00 1.00011399e+00 2.34501650e-03 + 2.70000000e+00 1.00011546e+00 2.55388684e-03 + 2.90000000e+00 1.00011691e+00 2.76252831e-03 + 3.10000000e+00 1.00011834e+00 2.97095809e-03 + 3.30000000e+00 1.00011977e+00 3.17919134e-03 + 3.70000000e+00 1.00012265e+00 3.59498410e-03 + 4.10000000e+00 1.00012550e+00 4.01013859e-03 + 4.50000000e+00 1.00012833e+00 4.42469838e-03 + 4.90000000e+00 1.00013113e+00 4.83870196e-03 + 5.10000000e+00 1.00013250e+00 5.04556199e-03 + 5.70000000e+00 1.00013673e+00 5.66508502e-03 + 6.20000000e+00 1.00014020e+00 6.18066249e-03 + 6.70000000e+00 1.00014365e+00 6.69560488e-03 + 7.45000000e+00 1.00014890e+00 7.46673667e-03 + 8.20000000e+00 1.00015411e+00 8.23662585e-03 + 8.95000000e+00 1.00015927e+00 9.00533261e-03 + 9.70000000e+00 1.00016439e+00 9.77293890e-03 + 1.07000000e+01 1.00017133e+00 1.07946466e-02 diff --git a/test/data/test_results/voce_ea/avg_euler_strain_global.txt b/test/data/test_results/voce_ea/avg_euler_strain_global.txt new file mode 100644 index 0000000..b8c4813 --- /dev/null +++ b/test/data/test_results/voce_ea/avg_euler_strain_global.txt @@ -0,0 +1,41 @@ + # Time Volume E11 E22 E33 E23 E13 E12 + 5.00000000e-03 1.00000159e+00 -1.63348823e-06 -1.77847722e-06 4.99996262e-06 1.10839246e-09 -9.90119337e-08 -7.40344508e-09 + 2.00000000e-01 1.00006342e+00 -6.53525140e-05 -7.12161657e-05 1.99940217e-04 4.13741756e-09 -3.98393415e-06 -2.68165999e-07 + 3.00000000e-01 1.00008461e+00 -1.01827211e-04 -1.13544449e-04 2.99865495e-04 -6.06249639e-07 -6.13795564e-06 -4.71953470e-07 + 4.00000000e-01 1.00009168e+00 -1.43185929e-04 -1.65105974e-04 3.99760717e-04 -2.06870814e-06 -8.55311251e-06 -1.21890826e-06 + 5.00000000e-01 1.00009491e+00 -1.85440239e-04 -2.19607111e-04 4.99625984e-04 -3.16388656e-06 -1.02284966e-05 -2.25015586e-06 + 6.00000000e-01 1.00009697e+00 -2.27762774e-04 -2.75207428e-04 5.99461326e-04 -3.77469124e-06 -1.14193852e-05 -3.42951399e-06 + 7.00000000e-01 1.00009854e+00 -2.70192358e-04 -3.31197886e-04 6.99266745e-04 -4.14080020e-06 -1.24369349e-05 -4.63503919e-06 + 8.00000000e-01 1.00009984e+00 -3.12742394e-04 -3.87337586e-04 7.99042251e-04 -4.35802649e-06 -1.34124711e-05 -5.74673116e-06 + 9.00000000e-01 1.00010097e+00 -3.55337467e-04 -4.43594812e-04 8.98787858e-04 -4.48426049e-06 -1.43544659e-05 -6.72262051e-06 + 1.00000000e+00 1.00010200e+00 -3.97933011e-04 -4.99958540e-04 9.98503579e-04 -4.54297386e-06 -1.52559312e-05 -7.58444146e-06 + 1.10000000e+00 1.00010295e+00 -4.40527616e-04 -5.56395241e-04 1.09818942e-03 -4.54570336e-06 -1.61146403e-05 -8.34156470e-06 + 1.20000000e+00 1.00010385e+00 -4.83129410e-04 -6.12875011e-04 1.19784540e-03 -4.51584609e-06 -1.69421144e-05 -9.01935068e-06 + 1.30000000e+00 1.00010472e+00 -5.25735818e-04 -6.69386356e-04 1.29747152e-03 -4.46888726e-06 -1.77412478e-05 -9.66735886e-06 + 1.40000000e+00 1.00010556e+00 -5.68346055e-04 -7.25920764e-04 1.39706779e-03 -4.40847228e-06 -1.85174924e-05 -1.03186136e-05 + 1.50000000e+00 1.00010637e+00 -6.10953951e-04 -7.82478125e-04 1.49663422e-03 -4.33651605e-06 -1.92779212e-05 -1.09797907e-05 + 1.60000000e+00 1.00010717e+00 -6.53556448e-04 -8.39056938e-04 1.59617083e-03 -4.25357178e-06 -2.00279565e-05 -1.16481644e-05 + 1.70000000e+00 1.00010796e+00 -6.96154833e-04 -8.95652575e-04 1.69567763e-03 -4.16057420e-06 -2.07716480e-05 -1.23276207e-05 + 1.80000000e+00 1.00010874e+00 -7.38758049e-04 -9.52253940e-04 1.79515462e-03 -4.05929515e-06 -2.15091314e-05 -1.30197394e-05 + 1.90000000e+00 1.00010951e+00 -7.81368728e-04 -1.00885676e-03 1.89460183e-03 -3.94936311e-06 -2.22393667e-05 -1.37303647e-05 + 2.00000000e+00 1.00011027e+00 -8.23978145e-04 -1.06546836e-03 1.99401927e-03 -3.83018883e-06 -2.29639352e-05 -1.44693699e-05 + 2.10000000e+00 1.00011102e+00 -8.66576435e-04 -1.12209765e-03 2.09340693e-03 -3.70207294e-06 -2.36850102e-05 -1.52383685e-05 + 2.30000000e+00 1.00011252e+00 -9.51709570e-04 -1.23542469e-03 2.29209301e-03 -3.44534713e-06 -2.51088667e-05 -1.68715629e-05 + 2.50000000e+00 1.00011399e+00 -1.03680542e-03 -1.34880644e-03 2.49066017e-03 -3.23732849e-06 -2.65181973e-05 -1.85787059e-05 + 2.70000000e+00 1.00011546e+00 -1.12186417e-03 -1.46223974e-03 2.68910852e-03 -3.08075738e-06 -2.79294795e-05 -2.03232111e-05 + 2.90000000e+00 1.00011691e+00 -1.20688453e-03 -1.57572317e-03 2.88743813e-03 -2.93387778e-06 -2.93489699e-05 -2.20852593e-05 + 3.10000000e+00 1.00011834e+00 -1.29185479e-03 -1.68926686e-03 3.08564913e-03 -2.76570351e-06 -3.07747514e-05 -2.38788254e-05 + 3.30000000e+00 1.00011977e+00 -1.37676478e-03 -1.80287972e-03 3.28374159e-03 -2.56191112e-06 -3.21987561e-05 -2.57091744e-05 + 3.70000000e+00 1.00012265e+00 -1.54628769e-03 -2.03037212e-03 3.67957129e-03 -1.98689659e-06 -3.50230787e-05 -2.94268183e-05 + 4.10000000e+00 1.00012550e+00 -1.71550289e-03 -2.25819577e-03 4.07492801e-03 -1.21193981e-06 -3.78372848e-05 -3.30978933e-05 + 4.50000000e+00 1.00012833e+00 -1.88442995e-03 -2.48632755e-03 4.46981249e-03 -2.30063539e-07 -4.06876447e-05 -3.66668397e-05 + 4.90000000e+00 1.00013113e+00 -2.05312102e-03 -2.71471282e-03 4.86422548e-03 9.16466634e-07 -4.36040386e-05 -4.01212812e-05 + 5.10000000e+00 1.00013250e+00 -2.13742436e-03 -2.82898082e-03 5.06125541e-03 1.52196828e-06 -4.50732894e-05 -4.18238781e-05 + 5.70000000e+00 1.00013673e+00 -2.38986349e-03 -3.17211127e-03 5.65164004e-03 3.54417238e-06 -4.94011314e-05 -4.68023896e-05 + 6.20000000e+00 1.00014020e+00 -2.59998057e-03 -3.45835767e-03 6.14282067e-03 5.32225007e-06 -5.28988339e-05 -5.09262437e-05 + 6.70000000e+00 1.00014365e+00 -2.80984536e-03 -3.74487392e-03 6.63326961e-03 7.18749990e-06 -5.62898030e-05 -5.50215639e-05 + 7.45000000e+00 1.00014890e+00 -3.12412341e-03 -4.17506693e-03 7.36757431e-03 1.01244548e-05 -6.11390913e-05 -6.11032350e-05 + 8.20000000e+00 1.00015411e+00 -3.43801792e-03 -4.60567838e-03 8.10024089e-03 1.31927161e-05 -6.58350470e-05 -6.71241694e-05 + 8.95000000e+00 1.00015927e+00 -3.75173133e-03 -5.03650399e-03 8.83127418e-03 1.63945314e-05 -7.04258759e-05 -7.30589724e-05 + 9.70000000e+00 1.00016439e+00 -4.06537985e-03 -5.46742560e-03 9.56067905e-03 1.96964266e-05 -7.49082158e-05 -7.88968510e-05 + 1.07000000e+01 1.00017133e+00 -4.48339277e-03 -6.04204313e-03 1.05306942e-02 2.42005991e-05 -8.07411683e-05 -8.65324645e-05 diff --git a/test/data/test_results/voce_ea/avg_euler_strain_region_default_1.txt b/test/data/test_results/voce_ea/avg_euler_strain_region_default_1.txt new file mode 100644 index 0000000..b8c4813 --- /dev/null +++ b/test/data/test_results/voce_ea/avg_euler_strain_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume E11 E22 E33 E23 E13 E12 + 5.00000000e-03 1.00000159e+00 -1.63348823e-06 -1.77847722e-06 4.99996262e-06 1.10839246e-09 -9.90119337e-08 -7.40344508e-09 + 2.00000000e-01 1.00006342e+00 -6.53525140e-05 -7.12161657e-05 1.99940217e-04 4.13741756e-09 -3.98393415e-06 -2.68165999e-07 + 3.00000000e-01 1.00008461e+00 -1.01827211e-04 -1.13544449e-04 2.99865495e-04 -6.06249639e-07 -6.13795564e-06 -4.71953470e-07 + 4.00000000e-01 1.00009168e+00 -1.43185929e-04 -1.65105974e-04 3.99760717e-04 -2.06870814e-06 -8.55311251e-06 -1.21890826e-06 + 5.00000000e-01 1.00009491e+00 -1.85440239e-04 -2.19607111e-04 4.99625984e-04 -3.16388656e-06 -1.02284966e-05 -2.25015586e-06 + 6.00000000e-01 1.00009697e+00 -2.27762774e-04 -2.75207428e-04 5.99461326e-04 -3.77469124e-06 -1.14193852e-05 -3.42951399e-06 + 7.00000000e-01 1.00009854e+00 -2.70192358e-04 -3.31197886e-04 6.99266745e-04 -4.14080020e-06 -1.24369349e-05 -4.63503919e-06 + 8.00000000e-01 1.00009984e+00 -3.12742394e-04 -3.87337586e-04 7.99042251e-04 -4.35802649e-06 -1.34124711e-05 -5.74673116e-06 + 9.00000000e-01 1.00010097e+00 -3.55337467e-04 -4.43594812e-04 8.98787858e-04 -4.48426049e-06 -1.43544659e-05 -6.72262051e-06 + 1.00000000e+00 1.00010200e+00 -3.97933011e-04 -4.99958540e-04 9.98503579e-04 -4.54297386e-06 -1.52559312e-05 -7.58444146e-06 + 1.10000000e+00 1.00010295e+00 -4.40527616e-04 -5.56395241e-04 1.09818942e-03 -4.54570336e-06 -1.61146403e-05 -8.34156470e-06 + 1.20000000e+00 1.00010385e+00 -4.83129410e-04 -6.12875011e-04 1.19784540e-03 -4.51584609e-06 -1.69421144e-05 -9.01935068e-06 + 1.30000000e+00 1.00010472e+00 -5.25735818e-04 -6.69386356e-04 1.29747152e-03 -4.46888726e-06 -1.77412478e-05 -9.66735886e-06 + 1.40000000e+00 1.00010556e+00 -5.68346055e-04 -7.25920764e-04 1.39706779e-03 -4.40847228e-06 -1.85174924e-05 -1.03186136e-05 + 1.50000000e+00 1.00010637e+00 -6.10953951e-04 -7.82478125e-04 1.49663422e-03 -4.33651605e-06 -1.92779212e-05 -1.09797907e-05 + 1.60000000e+00 1.00010717e+00 -6.53556448e-04 -8.39056938e-04 1.59617083e-03 -4.25357178e-06 -2.00279565e-05 -1.16481644e-05 + 1.70000000e+00 1.00010796e+00 -6.96154833e-04 -8.95652575e-04 1.69567763e-03 -4.16057420e-06 -2.07716480e-05 -1.23276207e-05 + 1.80000000e+00 1.00010874e+00 -7.38758049e-04 -9.52253940e-04 1.79515462e-03 -4.05929515e-06 -2.15091314e-05 -1.30197394e-05 + 1.90000000e+00 1.00010951e+00 -7.81368728e-04 -1.00885676e-03 1.89460183e-03 -3.94936311e-06 -2.22393667e-05 -1.37303647e-05 + 2.00000000e+00 1.00011027e+00 -8.23978145e-04 -1.06546836e-03 1.99401927e-03 -3.83018883e-06 -2.29639352e-05 -1.44693699e-05 + 2.10000000e+00 1.00011102e+00 -8.66576435e-04 -1.12209765e-03 2.09340693e-03 -3.70207294e-06 -2.36850102e-05 -1.52383685e-05 + 2.30000000e+00 1.00011252e+00 -9.51709570e-04 -1.23542469e-03 2.29209301e-03 -3.44534713e-06 -2.51088667e-05 -1.68715629e-05 + 2.50000000e+00 1.00011399e+00 -1.03680542e-03 -1.34880644e-03 2.49066017e-03 -3.23732849e-06 -2.65181973e-05 -1.85787059e-05 + 2.70000000e+00 1.00011546e+00 -1.12186417e-03 -1.46223974e-03 2.68910852e-03 -3.08075738e-06 -2.79294795e-05 -2.03232111e-05 + 2.90000000e+00 1.00011691e+00 -1.20688453e-03 -1.57572317e-03 2.88743813e-03 -2.93387778e-06 -2.93489699e-05 -2.20852593e-05 + 3.10000000e+00 1.00011834e+00 -1.29185479e-03 -1.68926686e-03 3.08564913e-03 -2.76570351e-06 -3.07747514e-05 -2.38788254e-05 + 3.30000000e+00 1.00011977e+00 -1.37676478e-03 -1.80287972e-03 3.28374159e-03 -2.56191112e-06 -3.21987561e-05 -2.57091744e-05 + 3.70000000e+00 1.00012265e+00 -1.54628769e-03 -2.03037212e-03 3.67957129e-03 -1.98689659e-06 -3.50230787e-05 -2.94268183e-05 + 4.10000000e+00 1.00012550e+00 -1.71550289e-03 -2.25819577e-03 4.07492801e-03 -1.21193981e-06 -3.78372848e-05 -3.30978933e-05 + 4.50000000e+00 1.00012833e+00 -1.88442995e-03 -2.48632755e-03 4.46981249e-03 -2.30063539e-07 -4.06876447e-05 -3.66668397e-05 + 4.90000000e+00 1.00013113e+00 -2.05312102e-03 -2.71471282e-03 4.86422548e-03 9.16466634e-07 -4.36040386e-05 -4.01212812e-05 + 5.10000000e+00 1.00013250e+00 -2.13742436e-03 -2.82898082e-03 5.06125541e-03 1.52196828e-06 -4.50732894e-05 -4.18238781e-05 + 5.70000000e+00 1.00013673e+00 -2.38986349e-03 -3.17211127e-03 5.65164004e-03 3.54417238e-06 -4.94011314e-05 -4.68023896e-05 + 6.20000000e+00 1.00014020e+00 -2.59998057e-03 -3.45835767e-03 6.14282067e-03 5.32225007e-06 -5.28988339e-05 -5.09262437e-05 + 6.70000000e+00 1.00014365e+00 -2.80984536e-03 -3.74487392e-03 6.63326961e-03 7.18749990e-06 -5.62898030e-05 -5.50215639e-05 + 7.45000000e+00 1.00014890e+00 -3.12412341e-03 -4.17506693e-03 7.36757431e-03 1.01244548e-05 -6.11390913e-05 -6.11032350e-05 + 8.20000000e+00 1.00015411e+00 -3.43801792e-03 -4.60567838e-03 8.10024089e-03 1.31927161e-05 -6.58350470e-05 -6.71241694e-05 + 8.95000000e+00 1.00015927e+00 -3.75173133e-03 -5.03650399e-03 8.83127418e-03 1.63945314e-05 -7.04258759e-05 -7.30589724e-05 + 9.70000000e+00 1.00016439e+00 -4.06537985e-03 -5.46742560e-03 9.56067905e-03 1.96964266e-05 -7.49082158e-05 -7.88968510e-05 + 1.07000000e+01 1.00017133e+00 -4.48339277e-03 -6.04204313e-03 1.05306942e-02 2.42005991e-05 -8.07411683e-05 -8.65324645e-05 diff --git a/test/data/test_results/voce_ea/avg_pl_work_global.txt b/test/data/test_results/voce_ea/avg_pl_work_global.txt new file mode 100644 index 0000000..11f0db6 --- /dev/null +++ b/test/data/test_results/voce_ea/avg_pl_work_global.txt @@ -0,0 +1,41 @@ + # Time Volume Plastic_Work + 5.00000000e-03 1.00000159e+00 0.00000000e+00 + 2.00000000e-01 1.00006342e+00 8.61564306e-09 + 3.00000000e-01 1.00008461e+00 1.06425647e-06 + 4.00000000e-01 1.00009168e+00 3.78093751e-06 + 5.00000000e-01 1.00009491e+00 7.08237024e-06 + 6.00000000e-01 1.00009697e+00 1.06271118e-05 + 7.00000000e-01 1.00009854e+00 1.43118515e-05 + 8.00000000e-01 1.00009984e+00 1.80957315e-05 + 9.00000000e-01 1.00010097e+00 2.19560941e-05 + 1.00000000e+00 1.00010200e+00 2.58798163e-05 + 1.10000000e+00 1.00010295e+00 2.98574635e-05 + 1.20000000e+00 1.00010385e+00 3.38824352e-05 + 1.30000000e+00 1.00010472e+00 3.79499979e-05 + 1.40000000e+00 1.00010556e+00 4.20567769e-05 + 1.50000000e+00 1.00010637e+00 4.62003313e-05 + 1.60000000e+00 1.00010717e+00 5.03787446e-05 + 1.70000000e+00 1.00010796e+00 5.45904843e-05 + 1.80000000e+00 1.00010874e+00 5.88345228e-05 + 1.90000000e+00 1.00010951e+00 6.31100457e-05 + 2.00000000e+00 1.00011027e+00 6.74163262e-05 + 2.10000000e+00 1.00011102e+00 7.17528367e-05 + 2.30000000e+00 1.00011252e+00 8.05419350e-05 + 2.50000000e+00 1.00011399e+00 8.94463680e-05 + 2.70000000e+00 1.00011546e+00 9.84645319e-05 + 2.90000000e+00 1.00011691e+00 1.07594518e-04 + 3.10000000e+00 1.00011834e+00 1.16835093e-04 + 3.30000000e+00 1.00011977e+00 1.26185318e-04 + 3.70000000e+00 1.00012265e+00 1.45314207e-04 + 4.10000000e+00 1.00012550e+00 1.64867784e-04 + 4.50000000e+00 1.00012833e+00 1.84841496e-04 + 4.90000000e+00 1.00013113e+00 2.05231323e-04 + 5.10000000e+00 1.00013250e+00 2.15530215e-04 + 5.70000000e+00 1.00013673e+00 2.47341191e-04 + 6.20000000e+00 1.00014020e+00 2.74480191e-04 + 6.70000000e+00 1.00014365e+00 3.02242943e-04 + 7.45000000e+00 1.00014890e+00 3.45272162e-04 + 8.20000000e+00 1.00015411e+00 3.89673608e-04 + 8.95000000e+00 1.00015927e+00 4.35432608e-04 + 9.70000000e+00 1.00016439e+00 4.82533381e-04 + 1.07000000e+01 1.00017133e+00 5.47684560e-04 diff --git a/test/data/test_results/voce_ea/avg_pl_work_region_default_1.txt b/test/data/test_results/voce_ea/avg_pl_work_region_default_1.txt new file mode 100644 index 0000000..11f0db6 --- /dev/null +++ b/test/data/test_results/voce_ea/avg_pl_work_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Plastic_Work + 5.00000000e-03 1.00000159e+00 0.00000000e+00 + 2.00000000e-01 1.00006342e+00 8.61564306e-09 + 3.00000000e-01 1.00008461e+00 1.06425647e-06 + 4.00000000e-01 1.00009168e+00 3.78093751e-06 + 5.00000000e-01 1.00009491e+00 7.08237024e-06 + 6.00000000e-01 1.00009697e+00 1.06271118e-05 + 7.00000000e-01 1.00009854e+00 1.43118515e-05 + 8.00000000e-01 1.00009984e+00 1.80957315e-05 + 9.00000000e-01 1.00010097e+00 2.19560941e-05 + 1.00000000e+00 1.00010200e+00 2.58798163e-05 + 1.10000000e+00 1.00010295e+00 2.98574635e-05 + 1.20000000e+00 1.00010385e+00 3.38824352e-05 + 1.30000000e+00 1.00010472e+00 3.79499979e-05 + 1.40000000e+00 1.00010556e+00 4.20567769e-05 + 1.50000000e+00 1.00010637e+00 4.62003313e-05 + 1.60000000e+00 1.00010717e+00 5.03787446e-05 + 1.70000000e+00 1.00010796e+00 5.45904843e-05 + 1.80000000e+00 1.00010874e+00 5.88345228e-05 + 1.90000000e+00 1.00010951e+00 6.31100457e-05 + 2.00000000e+00 1.00011027e+00 6.74163262e-05 + 2.10000000e+00 1.00011102e+00 7.17528367e-05 + 2.30000000e+00 1.00011252e+00 8.05419350e-05 + 2.50000000e+00 1.00011399e+00 8.94463680e-05 + 2.70000000e+00 1.00011546e+00 9.84645319e-05 + 2.90000000e+00 1.00011691e+00 1.07594518e-04 + 3.10000000e+00 1.00011834e+00 1.16835093e-04 + 3.30000000e+00 1.00011977e+00 1.26185318e-04 + 3.70000000e+00 1.00012265e+00 1.45314207e-04 + 4.10000000e+00 1.00012550e+00 1.64867784e-04 + 4.50000000e+00 1.00012833e+00 1.84841496e-04 + 4.90000000e+00 1.00013113e+00 2.05231323e-04 + 5.10000000e+00 1.00013250e+00 2.15530215e-04 + 5.70000000e+00 1.00013673e+00 2.47341191e-04 + 6.20000000e+00 1.00014020e+00 2.74480191e-04 + 6.70000000e+00 1.00014365e+00 3.02242943e-04 + 7.45000000e+00 1.00014890e+00 3.45272162e-04 + 8.20000000e+00 1.00015411e+00 3.89673608e-04 + 8.95000000e+00 1.00015927e+00 4.35432608e-04 + 9.70000000e+00 1.00016439e+00 4.82533381e-04 + 1.07000000e+01 1.00017133e+00 5.47684560e-04 diff --git a/test/data/test_results/voce_ea/avg_stress_global.txt b/test/data/test_results/voce_ea/avg_stress_global.txt new file mode 100644 index 0000000..1b8f52d --- /dev/null +++ b/test/data/test_results/voce_ea/avg_stress_global.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.14673178e-14 1.17530421e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006342e+00 -2.09401841e-11 6.60843137e-11 2.60676157e-02 -2.37009586e-04 1.08522635e-04 8.92032556e-07 + 3.00000000e-01 1.00008461e+00 -3.37196045e-11 1.31391641e-09 3.47754323e-02 -3.34815174e-04 1.56617170e-04 2.36076288e-05 + 4.00000000e-01 1.00009168e+00 1.07931168e-11 1.03791883e-10 3.76782543e-02 -4.16276022e-04 1.45834711e-04 7.77594717e-05 + 5.00000000e-01 1.00009491e+00 1.12621845e-12 -3.84421334e-13 3.90039956e-02 -4.66010980e-04 1.40530555e-04 1.26938781e-04 + 6.00000000e-01 1.00009697e+00 4.91754336e-14 -1.19265343e-12 3.98495687e-02 -4.87897510e-04 1.54198968e-04 1.63550487e-04 + 7.00000000e-01 1.00009854e+00 7.00416153e-10 5.71888405e-10 4.04905283e-02 -5.00492422e-04 1.74013396e-04 1.90179283e-04 + 8.00000000e-01 1.00009984e+00 3.92739558e-10 3.28502101e-11 4.10204084e-02 -5.11028549e-04 1.93364341e-04 2.08420837e-04 + 9.00000000e-01 1.00010097e+00 9.54836952e-11 -5.79257992e-11 4.14832514e-02 -5.21784874e-04 2.09412189e-04 2.20434773e-04 + 1.00000000e+00 1.00010200e+00 1.58195899e-12 -7.17510244e-11 4.19019180e-02 -5.31790918e-04 2.22628184e-04 2.28557241e-04 + 1.10000000e+00 1.00010295e+00 -1.14864205e-11 -6.66752574e-11 4.22907699e-02 -5.40632370e-04 2.33769758e-04 2.33635417e-04 + 1.20000000e+00 1.00010385e+00 -2.50410450e-11 -4.92728970e-11 4.26587542e-02 -5.48748448e-04 2.43502246e-04 2.36699111e-04 + 1.30000000e+00 1.00010472e+00 -1.97555635e-11 -4.17613150e-11 4.30116589e-02 -5.56254691e-04 2.52336968e-04 2.38959745e-04 + 1.40000000e+00 1.00010556e+00 -2.03794569e-11 -3.15636219e-11 4.33533077e-02 -5.63144621e-04 2.60516060e-04 2.41037651e-04 + 1.50000000e+00 1.00010637e+00 -1.87945296e-11 -2.15900107e-11 4.36862836e-02 -5.69502358e-04 2.68041999e-04 2.42972224e-04 + 1.60000000e+00 1.00010717e+00 -1.50226818e-11 -1.38411488e-11 4.40124606e-02 -5.75230341e-04 2.74952163e-04 2.44837343e-04 + 1.70000000e+00 1.00010796e+00 -1.37177679e-11 -1.03001000e-11 4.43332116e-02 -5.80306559e-04 2.81402607e-04 2.46797206e-04 + 1.80000000e+00 1.00010874e+00 -1.34359271e-11 -8.88819256e-12 4.46494223e-02 -5.84844931e-04 2.87502562e-04 2.48763572e-04 + 1.90000000e+00 1.00010951e+00 -1.21335924e-11 -7.58968434e-12 4.49617694e-02 -5.89028961e-04 2.93297264e-04 2.50667797e-04 + 2.00000000e+00 1.00011027e+00 -1.00772299e-11 -7.09554157e-12 4.52708278e-02 -5.93026044e-04 2.98776223e-04 2.52492951e-04 + 2.10000000e+00 1.00011102e+00 -8.99402885e-12 -7.45521951e-12 4.55770022e-02 -5.96910207e-04 3.03874003e-04 2.54283867e-04 + 2.30000000e+00 1.00011252e+00 -4.11553904e-11 -3.00679657e-11 4.61805777e-02 -6.04205327e-04 3.12747287e-04 2.57882448e-04 + 2.50000000e+00 1.00011399e+00 -4.84814377e-11 -4.03899866e-11 4.67762673e-02 -6.11176491e-04 3.21036974e-04 2.61074550e-04 + 2.70000000e+00 1.00011546e+00 -4.91842192e-11 -3.97375676e-11 4.73653081e-02 -6.18039634e-04 3.29092500e-04 2.63751219e-04 + 2.90000000e+00 1.00011691e+00 -4.01137746e-11 -3.81286855e-11 4.79488304e-02 -6.24868497e-04 3.36757794e-04 2.65950795e-04 + 3.10000000e+00 1.00011834e+00 -3.94975236e-11 -4.16942042e-11 4.85275129e-02 -6.31602660e-04 3.43894475e-04 2.67796092e-04 + 3.30000000e+00 1.00011977e+00 -3.70866465e-11 -3.78130814e-11 4.91018850e-02 -6.38160785e-04 3.50716893e-04 2.69457013e-04 + 3.70000000e+00 1.00012265e+00 -1.97592889e-10 -1.97056730e-10 5.02367220e-02 -6.50555872e-04 3.64082044e-04 2.72358412e-04 + 4.10000000e+00 1.00012550e+00 -1.41646748e-10 -1.82016008e-10 5.13595444e-02 -6.62034421e-04 3.76475491e-04 2.74824467e-04 + 4.50000000e+00 1.00012833e+00 -9.42934141e-11 -1.30568296e-10 5.24720005e-02 -6.72320921e-04 3.87711109e-04 2.76984234e-04 + 4.90000000e+00 1.00013113e+00 -7.38490228e-11 -8.83807076e-11 5.35752471e-02 -6.81421263e-04 3.98502512e-04 2.78587386e-04 + 5.10000000e+00 1.00013250e+00 -1.69319558e-11 -1.87672637e-11 5.41247476e-02 -6.85778059e-04 4.03837440e-04 2.79268188e-04 + 5.70000000e+00 1.00013673e+00 1.38289996e-12 -7.36244343e-13 5.57565133e-02 -6.98396245e-04 4.19272923e-04 2.80938272e-04 + 6.20000000e+00 1.00014020e+00 -1.33187032e-10 -1.34606108e-10 5.71058151e-02 -7.08920681e-04 4.31052721e-04 2.82000503e-04 + 6.70000000e+00 1.00014365e+00 -1.15009334e-10 -1.07217379e-10 5.84453806e-02 -7.19283771e-04 4.41976040e-04 2.82917074e-04 + 7.45000000e+00 1.00014890e+00 7.14115736e-13 -9.17040266e-13 6.04342482e-02 -7.34841499e-04 4.57166605e-04 2.84811589e-04 + 8.20000000e+00 1.00015411e+00 4.39363838e-13 -2.80313593e-13 6.24035785e-02 -7.50468361e-04 4.71149727e-04 2.87330168e-04 + 8.95000000e+00 1.00015927e+00 1.63558101e-13 1.40881235e-13 6.43543299e-02 -7.66173937e-04 4.85138254e-04 2.91061424e-04 + 9.70000000e+00 1.00016439e+00 -2.28143036e-13 -9.12763358e-14 6.62873957e-02 -7.81978191e-04 4.99387630e-04 2.95823487e-04 + 1.07000000e+01 1.00017133e+00 -6.61563055e-13 6.00342404e-14 6.88347131e-02 -8.03113176e-04 5.18231894e-04 3.02891266e-04 diff --git a/test/data/test_results/voce_ea/avg_stress_region_default_1.txt b/test/data/test_results/voce_ea/avg_stress_region_default_1.txt new file mode 100644 index 0000000..1b8f52d --- /dev/null +++ b/test/data/test_results/voce_ea/avg_stress_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.14673178e-14 1.17530421e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006342e+00 -2.09401841e-11 6.60843137e-11 2.60676157e-02 -2.37009586e-04 1.08522635e-04 8.92032556e-07 + 3.00000000e-01 1.00008461e+00 -3.37196045e-11 1.31391641e-09 3.47754323e-02 -3.34815174e-04 1.56617170e-04 2.36076288e-05 + 4.00000000e-01 1.00009168e+00 1.07931168e-11 1.03791883e-10 3.76782543e-02 -4.16276022e-04 1.45834711e-04 7.77594717e-05 + 5.00000000e-01 1.00009491e+00 1.12621845e-12 -3.84421334e-13 3.90039956e-02 -4.66010980e-04 1.40530555e-04 1.26938781e-04 + 6.00000000e-01 1.00009697e+00 4.91754336e-14 -1.19265343e-12 3.98495687e-02 -4.87897510e-04 1.54198968e-04 1.63550487e-04 + 7.00000000e-01 1.00009854e+00 7.00416153e-10 5.71888405e-10 4.04905283e-02 -5.00492422e-04 1.74013396e-04 1.90179283e-04 + 8.00000000e-01 1.00009984e+00 3.92739558e-10 3.28502101e-11 4.10204084e-02 -5.11028549e-04 1.93364341e-04 2.08420837e-04 + 9.00000000e-01 1.00010097e+00 9.54836952e-11 -5.79257992e-11 4.14832514e-02 -5.21784874e-04 2.09412189e-04 2.20434773e-04 + 1.00000000e+00 1.00010200e+00 1.58195899e-12 -7.17510244e-11 4.19019180e-02 -5.31790918e-04 2.22628184e-04 2.28557241e-04 + 1.10000000e+00 1.00010295e+00 -1.14864205e-11 -6.66752574e-11 4.22907699e-02 -5.40632370e-04 2.33769758e-04 2.33635417e-04 + 1.20000000e+00 1.00010385e+00 -2.50410450e-11 -4.92728970e-11 4.26587542e-02 -5.48748448e-04 2.43502246e-04 2.36699111e-04 + 1.30000000e+00 1.00010472e+00 -1.97555635e-11 -4.17613150e-11 4.30116589e-02 -5.56254691e-04 2.52336968e-04 2.38959745e-04 + 1.40000000e+00 1.00010556e+00 -2.03794569e-11 -3.15636219e-11 4.33533077e-02 -5.63144621e-04 2.60516060e-04 2.41037651e-04 + 1.50000000e+00 1.00010637e+00 -1.87945296e-11 -2.15900107e-11 4.36862836e-02 -5.69502358e-04 2.68041999e-04 2.42972224e-04 + 1.60000000e+00 1.00010717e+00 -1.50226818e-11 -1.38411488e-11 4.40124606e-02 -5.75230341e-04 2.74952163e-04 2.44837343e-04 + 1.70000000e+00 1.00010796e+00 -1.37177679e-11 -1.03001000e-11 4.43332116e-02 -5.80306559e-04 2.81402607e-04 2.46797206e-04 + 1.80000000e+00 1.00010874e+00 -1.34359271e-11 -8.88819256e-12 4.46494223e-02 -5.84844931e-04 2.87502562e-04 2.48763572e-04 + 1.90000000e+00 1.00010951e+00 -1.21335924e-11 -7.58968434e-12 4.49617694e-02 -5.89028961e-04 2.93297264e-04 2.50667797e-04 + 2.00000000e+00 1.00011027e+00 -1.00772299e-11 -7.09554157e-12 4.52708278e-02 -5.93026044e-04 2.98776223e-04 2.52492951e-04 + 2.10000000e+00 1.00011102e+00 -8.99402885e-12 -7.45521951e-12 4.55770022e-02 -5.96910207e-04 3.03874003e-04 2.54283867e-04 + 2.30000000e+00 1.00011252e+00 -4.11553904e-11 -3.00679657e-11 4.61805777e-02 -6.04205327e-04 3.12747287e-04 2.57882448e-04 + 2.50000000e+00 1.00011399e+00 -4.84814377e-11 -4.03899866e-11 4.67762673e-02 -6.11176491e-04 3.21036974e-04 2.61074550e-04 + 2.70000000e+00 1.00011546e+00 -4.91842192e-11 -3.97375676e-11 4.73653081e-02 -6.18039634e-04 3.29092500e-04 2.63751219e-04 + 2.90000000e+00 1.00011691e+00 -4.01137746e-11 -3.81286855e-11 4.79488304e-02 -6.24868497e-04 3.36757794e-04 2.65950795e-04 + 3.10000000e+00 1.00011834e+00 -3.94975236e-11 -4.16942042e-11 4.85275129e-02 -6.31602660e-04 3.43894475e-04 2.67796092e-04 + 3.30000000e+00 1.00011977e+00 -3.70866465e-11 -3.78130814e-11 4.91018850e-02 -6.38160785e-04 3.50716893e-04 2.69457013e-04 + 3.70000000e+00 1.00012265e+00 -1.97592889e-10 -1.97056730e-10 5.02367220e-02 -6.50555872e-04 3.64082044e-04 2.72358412e-04 + 4.10000000e+00 1.00012550e+00 -1.41646748e-10 -1.82016008e-10 5.13595444e-02 -6.62034421e-04 3.76475491e-04 2.74824467e-04 + 4.50000000e+00 1.00012833e+00 -9.42934141e-11 -1.30568296e-10 5.24720005e-02 -6.72320921e-04 3.87711109e-04 2.76984234e-04 + 4.90000000e+00 1.00013113e+00 -7.38490228e-11 -8.83807076e-11 5.35752471e-02 -6.81421263e-04 3.98502512e-04 2.78587386e-04 + 5.10000000e+00 1.00013250e+00 -1.69319558e-11 -1.87672637e-11 5.41247476e-02 -6.85778059e-04 4.03837440e-04 2.79268188e-04 + 5.70000000e+00 1.00013673e+00 1.38289996e-12 -7.36244343e-13 5.57565133e-02 -6.98396245e-04 4.19272923e-04 2.80938272e-04 + 6.20000000e+00 1.00014020e+00 -1.33187032e-10 -1.34606108e-10 5.71058151e-02 -7.08920681e-04 4.31052721e-04 2.82000503e-04 + 6.70000000e+00 1.00014365e+00 -1.15009334e-10 -1.07217379e-10 5.84453806e-02 -7.19283771e-04 4.41976040e-04 2.82917074e-04 + 7.45000000e+00 1.00014890e+00 7.14115736e-13 -9.17040266e-13 6.04342482e-02 -7.34841499e-04 4.57166605e-04 2.84811589e-04 + 8.20000000e+00 1.00015411e+00 4.39363838e-13 -2.80313593e-13 6.24035785e-02 -7.50468361e-04 4.71149727e-04 2.87330168e-04 + 8.95000000e+00 1.00015927e+00 1.63558101e-13 1.40881235e-13 6.43543299e-02 -7.66173937e-04 4.85138254e-04 2.91061424e-04 + 9.70000000e+00 1.00016439e+00 -2.28143036e-13 -9.12763358e-14 6.62873957e-02 -7.81978191e-04 4.99387630e-04 2.95823487e-04 + 1.07000000e+01 1.00017133e+00 -6.61563055e-13 6.00342404e-14 6.88347131e-02 -8.03113176e-04 5.18231894e-04 3.02891266e-04 diff --git a/test/data/test_results/voce_ea_cs/avg_def_grad_global.txt b/test/data/test_results/voce_ea_cs/avg_def_grad_global.txt new file mode 100644 index 0000000..9f23a31 --- /dev/null +++ b/test/data/test_results/voce_ea_cs/avg_def_grad_global.txt @@ -0,0 +1,41 @@ + # Time Volume F11 F12 F13 F21 F22 F23 F31 F32 F33 + 5.00000000e-03 1.00000159e+00 9.99998367e-01 -1.88826625e-07 -7.53880704e-08 1.74019841e-07 9.99998222e-01 -3.51738840e-08 -1.22636634e-07 3.73904224e-08 1.00000500e+00 + 2.00000000e-01 1.00006342e+00 9.99934654e-01 -7.57048412e-06 -3.16406370e-06 7.03431240e-06 9.99928791e-01 -1.40827898e-06 -4.80522780e-06 1.41614841e-06 1.00020000e+00 + 3.00000000e-01 1.00008461e+00 9.99898180e-01 -1.41765027e-05 -7.31794924e-06 1.32328198e-05 9.99886465e-01 -1.65818665e-06 -4.96308432e-06 4.44349143e-07 1.00030002e+00 + 4.00000000e-01 1.00009168e+00 9.99856823e-01 -2.40360882e-05 -1.27317550e-05 2.15990783e-05 9.99834907e-01 -3.88560939e-07 -4.38540578e-06 -3.75104570e-06 1.00040005e+00 + 5.00000000e-01 1.00009491e+00 9.99814573e-01 -3.55780111e-05 -1.83145788e-05 3.10797136e-05 9.99780415e-01 2.31872955e-06 -2.16009791e-06 -8.64715969e-06 1.00050009e+00 + 6.00000000e-01 1.00009698e+00 9.99772255e-01 -4.88716936e-05 -2.31888172e-05 4.20168514e-05 9.99724827e-01 4.22367931e-06 3.24380566e-07 -1.17719326e-05 1.00060014e+00 + 7.00000000e-01 1.00009854e+00 9.99729831e-01 -6.34721266e-05 -2.74374352e-05 5.42096582e-05 9.99668852e-01 5.34827149e-06 2.52869880e-06 -1.36271923e-05 1.00070020e+00 + 8.00000000e-01 1.00009984e+00 9.99687287e-01 -7.89110172e-05 -3.12767004e-05 6.74298738e-05 9.99612733e-01 5.94790702e-06 4.40666189e-06 -1.46600733e-05 1.00080027e+00 + 9.00000000e-01 1.00010097e+00 9.99644700e-01 -9.48941573e-05 -3.48188826e-05 8.14668777e-05 9.99556499e-01 6.17427974e-06 6.05380234e-06 -1.51380730e-05 1.00090035e+00 + 1.00000000e+00 1.00010200e+00 9.99602113e-01 -1.11268811e-04 -3.81359838e-05 9.61242968e-05 9.99500162e-01 6.12660146e-06 7.55624220e-06 -1.52072872e-05 1.00100044e+00 + 1.10000000e+00 1.00010296e+00 9.99559528e-01 -1.27891688e-04 -4.12138472e-05 1.11240183e-04 9.99443756e-01 5.82657852e-06 8.90423831e-06 -1.49126686e-05 1.00110054e+00 + 1.20000000e+00 1.00010386e+00 9.99516937e-01 -1.44680094e-04 -4.40353593e-05 1.26680597e-04 9.99387311e-01 5.33614103e-06 1.00577964e-05 -1.43630980e-05 1.00120065e+00 + 1.30000000e+00 1.00010473e+00 9.99474341e-01 -1.61593486e-04 -4.66186162e-05 1.42305703e-04 9.99330838e-01 4.70851651e-06 1.10293303e-05 -1.36426234e-05 1.00130077e+00 + 1.40000000e+00 1.00010557e+00 9.99431744e-01 -1.78604770e-04 -4.89864364e-05 1.58022662e-04 9.99274346e-01 3.97981590e-06 1.18307405e-05 -1.27945752e-05 1.00140090e+00 + 1.50000000e+00 1.00010638e+00 9.99389149e-01 -1.95694255e-04 -5.11454575e-05 1.73798758e-04 9.99217834e-01 3.19205415e-06 1.24545622e-05 -1.18647322e-05 1.00150104e+00 + 1.60000000e+00 1.00010719e+00 9.99346561e-01 -2.12841105e-04 -5.31126850e-05 1.89618521e-04 9.99161304e-01 2.37753663e-06 1.29069858e-05 -1.08864287e-05 1.00160119e+00 + 1.70000000e+00 1.00010798e+00 9.99303977e-01 -2.30035350e-04 -5.49216829e-05 2.05464153e-04 9.99104761e-01 1.54762796e-06 1.32135192e-05 -9.87293317e-06 1.00170135e+00 + 1.80000000e+00 1.00010875e+00 9.99261390e-01 -2.47241563e-04 -5.66113477e-05 2.21297082e-04 9.99048217e-01 7.08027785e-07 1.34128449e-05 -8.83345179e-06 1.00180152e+00 + 1.90000000e+00 1.00010952e+00 9.99218796e-01 -2.64436967e-04 -5.81993502e-05 2.37082566e-04 9.98991674e-01 -1.38489206e-07 1.35246892e-05 -7.76995043e-06 1.00190170e+00 + 2.00000000e+00 1.00011028e+00 9.99176205e-01 -2.81633835e-04 -5.96861728e-05 2.52813219e-04 9.98935126e-01 -9.92953732e-07 1.35463112e-05 -6.68023739e-06 1.00200189e+00 + 2.10000000e+00 1.00011104e+00 9.99133626e-01 -2.98837887e-04 -6.10728023e-05 2.68491884e-04 9.98878564e-01 -1.85520136e-06 1.34744417e-05 -5.56514756e-06 1.00210210e+00 + 2.30000000e+00 1.00011254e+00 9.99048538e-01 -3.33236984e-04 -6.34725921e-05 2.99652591e-04 9.98765388e-01 -3.61478497e-06 1.29939136e-05 -3.30104998e-06 1.00230252e+00 + 2.50000000e+00 1.00011402e+00 9.98963492e-01 -3.67557743e-04 -6.55616675e-05 3.30590928e-04 9.98652173e-01 -5.42188697e-06 1.22304641e-05 -1.08932337e-06 1.00250298e+00 + 2.70000000e+00 1.00011549e+00 9.98878486e-01 -4.01728526e-04 -6.73895806e-05 3.61308752e-04 9.98538920e-01 -7.28895531e-06 1.12008228e-05 1.07859495e-06 1.00270348e+00 + 2.90000000e+00 1.00011694e+00 9.98793523e-01 -4.35708587e-04 -6.90047423e-05 3.91803727e-04 9.98425632e-01 -9.21054066e-06 9.94105354e-06 3.28172602e-06 1.00290402e+00 + 3.10000000e+00 1.00011838e+00 9.98708614e-01 -4.69507632e-04 -7.04463148e-05 4.22057177e-04 9.98312299e-01 -1.11798148e-05 8.49461486e-06 5.57475813e-06 1.00310460e+00 + 3.30000000e+00 1.00011981e+00 9.98623770e-01 -5.03138148e-04 -7.17523553e-05 4.52071898e-04 9.98198911e-01 -1.31725784e-05 6.91570392e-06 7.96210582e-06 1.00330522e+00 + 3.70000000e+00 1.00012270e+00 9.98454408e-01 -5.69772736e-04 -7.39219689e-05 5.11375105e-04 9.97971935e-01 -1.71340192e-05 3.36197349e-06 1.30473187e-05 1.00370654e+00 + 4.10000000e+00 1.00012556e+00 9.98285370e-01 -6.35708749e-04 -7.56970052e-05 5.70088907e-04 9.97744687e-01 -2.09651208e-05 -5.69965397e-07 1.84013724e-05 1.00410803e+00 + 4.50000000e+00 1.00012840e+00 9.98116635e-01 -7.00817086e-04 -7.71514448e-05 6.28193673e-04 9.97517193e-01 -2.47032232e-05 -4.90017805e-06 2.40753249e-05 1.00450967e+00 + 4.90000000e+00 1.00013122e+00 9.97948151e-01 -7.65076340e-04 -7.83608964e-05 6.85690551e-04 9.97289507e-01 -2.83925432e-05 -9.61051138e-06 3.00257112e-05 1.00491148e+00 + 5.10000000e+00 1.00013259e+00 9.97863948e-01 -7.96990225e-04 -7.89104841e-05 7.14276290e-04 9.97175601e-01 -3.02445464e-05 -1.20436373e-05 3.30712450e-05 1.00511246e+00 + 5.70000000e+00 1.00013685e+00 9.97611884e-01 -8.91087545e-04 -8.00416630e-05 7.98664367e-04 9.96833717e-01 -3.59567786e-05 -1.96907346e-05 4.27683747e-05 1.00571553e+00 + 6.20000000e+00 1.00014034e+00 9.97402095e-01 -9.68485723e-04 -8.06851487e-05 8.68039790e-04 9.96548601e-01 -4.07872347e-05 -2.61452971e-05 5.10986882e-05 1.00621839e+00 + 6.70000000e+00 1.00014381e+00 9.97192583e-01 -1.04496127e-03 -8.11006820e-05 9.36569789e-04 9.96263314e-01 -4.56520509e-05 -3.26152005e-05 5.96328933e-05 1.00672150e+00 + 7.45000000e+00 1.00014910e+00 9.96878915e-01 -1.15790320e-03 -8.13995624e-05 1.03775276e-03 9.95835202e-01 -5.28609506e-05 -4.21681737e-05 7.26130745e-05 1.00747654e+00 + 8.20000000e+00 1.00015434e+00 9.96565680e-01 -1.26914099e-03 -8.14293987e-05 1.13739383e-03 9.95406897e-01 -5.99172762e-05 -5.16931099e-05 8.56977007e-05 1.00823215e+00 + 8.95000000e+00 1.00015955e+00 9.96252668e-01 -1.37883734e-03 -8.12075063e-05 1.23571199e-03 9.94978609e-01 -6.66804655e-05 -6.12598580e-05 9.87488242e-05 1.00898832e+00 + 9.70000000e+00 1.00016472e+00 9.95939776e-01 -1.48712298e-03 -8.07912091e-05 1.33284940e-03 9.94550445e-01 -7.31961178e-05 -7.08076651e-05 1.11744409e-04 1.00974507e+00 + 1.07000000e+01 1.00017172e+00 9.95522903e-01 -1.62896242e-03 -8.01051227e-05 1.46018792e-03 9.93979924e-01 -8.16948497e-05 -8.33849998e-05 1.29067762e-04 1.01075481e+00 diff --git a/test/data/test_results/voce_ea_cs/avg_def_grad_region_default_1.txt b/test/data/test_results/voce_ea_cs/avg_def_grad_region_default_1.txt new file mode 100644 index 0000000..9f23a31 --- /dev/null +++ b/test/data/test_results/voce_ea_cs/avg_def_grad_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume F11 F12 F13 F21 F22 F23 F31 F32 F33 + 5.00000000e-03 1.00000159e+00 9.99998367e-01 -1.88826625e-07 -7.53880704e-08 1.74019841e-07 9.99998222e-01 -3.51738840e-08 -1.22636634e-07 3.73904224e-08 1.00000500e+00 + 2.00000000e-01 1.00006342e+00 9.99934654e-01 -7.57048412e-06 -3.16406370e-06 7.03431240e-06 9.99928791e-01 -1.40827898e-06 -4.80522780e-06 1.41614841e-06 1.00020000e+00 + 3.00000000e-01 1.00008461e+00 9.99898180e-01 -1.41765027e-05 -7.31794924e-06 1.32328198e-05 9.99886465e-01 -1.65818665e-06 -4.96308432e-06 4.44349143e-07 1.00030002e+00 + 4.00000000e-01 1.00009168e+00 9.99856823e-01 -2.40360882e-05 -1.27317550e-05 2.15990783e-05 9.99834907e-01 -3.88560939e-07 -4.38540578e-06 -3.75104570e-06 1.00040005e+00 + 5.00000000e-01 1.00009491e+00 9.99814573e-01 -3.55780111e-05 -1.83145788e-05 3.10797136e-05 9.99780415e-01 2.31872955e-06 -2.16009791e-06 -8.64715969e-06 1.00050009e+00 + 6.00000000e-01 1.00009698e+00 9.99772255e-01 -4.88716936e-05 -2.31888172e-05 4.20168514e-05 9.99724827e-01 4.22367931e-06 3.24380566e-07 -1.17719326e-05 1.00060014e+00 + 7.00000000e-01 1.00009854e+00 9.99729831e-01 -6.34721266e-05 -2.74374352e-05 5.42096582e-05 9.99668852e-01 5.34827149e-06 2.52869880e-06 -1.36271923e-05 1.00070020e+00 + 8.00000000e-01 1.00009984e+00 9.99687287e-01 -7.89110172e-05 -3.12767004e-05 6.74298738e-05 9.99612733e-01 5.94790702e-06 4.40666189e-06 -1.46600733e-05 1.00080027e+00 + 9.00000000e-01 1.00010097e+00 9.99644700e-01 -9.48941573e-05 -3.48188826e-05 8.14668777e-05 9.99556499e-01 6.17427974e-06 6.05380234e-06 -1.51380730e-05 1.00090035e+00 + 1.00000000e+00 1.00010200e+00 9.99602113e-01 -1.11268811e-04 -3.81359838e-05 9.61242968e-05 9.99500162e-01 6.12660146e-06 7.55624220e-06 -1.52072872e-05 1.00100044e+00 + 1.10000000e+00 1.00010296e+00 9.99559528e-01 -1.27891688e-04 -4.12138472e-05 1.11240183e-04 9.99443756e-01 5.82657852e-06 8.90423831e-06 -1.49126686e-05 1.00110054e+00 + 1.20000000e+00 1.00010386e+00 9.99516937e-01 -1.44680094e-04 -4.40353593e-05 1.26680597e-04 9.99387311e-01 5.33614103e-06 1.00577964e-05 -1.43630980e-05 1.00120065e+00 + 1.30000000e+00 1.00010473e+00 9.99474341e-01 -1.61593486e-04 -4.66186162e-05 1.42305703e-04 9.99330838e-01 4.70851651e-06 1.10293303e-05 -1.36426234e-05 1.00130077e+00 + 1.40000000e+00 1.00010557e+00 9.99431744e-01 -1.78604770e-04 -4.89864364e-05 1.58022662e-04 9.99274346e-01 3.97981590e-06 1.18307405e-05 -1.27945752e-05 1.00140090e+00 + 1.50000000e+00 1.00010638e+00 9.99389149e-01 -1.95694255e-04 -5.11454575e-05 1.73798758e-04 9.99217834e-01 3.19205415e-06 1.24545622e-05 -1.18647322e-05 1.00150104e+00 + 1.60000000e+00 1.00010719e+00 9.99346561e-01 -2.12841105e-04 -5.31126850e-05 1.89618521e-04 9.99161304e-01 2.37753663e-06 1.29069858e-05 -1.08864287e-05 1.00160119e+00 + 1.70000000e+00 1.00010798e+00 9.99303977e-01 -2.30035350e-04 -5.49216829e-05 2.05464153e-04 9.99104761e-01 1.54762796e-06 1.32135192e-05 -9.87293317e-06 1.00170135e+00 + 1.80000000e+00 1.00010875e+00 9.99261390e-01 -2.47241563e-04 -5.66113477e-05 2.21297082e-04 9.99048217e-01 7.08027785e-07 1.34128449e-05 -8.83345179e-06 1.00180152e+00 + 1.90000000e+00 1.00010952e+00 9.99218796e-01 -2.64436967e-04 -5.81993502e-05 2.37082566e-04 9.98991674e-01 -1.38489206e-07 1.35246892e-05 -7.76995043e-06 1.00190170e+00 + 2.00000000e+00 1.00011028e+00 9.99176205e-01 -2.81633835e-04 -5.96861728e-05 2.52813219e-04 9.98935126e-01 -9.92953732e-07 1.35463112e-05 -6.68023739e-06 1.00200189e+00 + 2.10000000e+00 1.00011104e+00 9.99133626e-01 -2.98837887e-04 -6.10728023e-05 2.68491884e-04 9.98878564e-01 -1.85520136e-06 1.34744417e-05 -5.56514756e-06 1.00210210e+00 + 2.30000000e+00 1.00011254e+00 9.99048538e-01 -3.33236984e-04 -6.34725921e-05 2.99652591e-04 9.98765388e-01 -3.61478497e-06 1.29939136e-05 -3.30104998e-06 1.00230252e+00 + 2.50000000e+00 1.00011402e+00 9.98963492e-01 -3.67557743e-04 -6.55616675e-05 3.30590928e-04 9.98652173e-01 -5.42188697e-06 1.22304641e-05 -1.08932337e-06 1.00250298e+00 + 2.70000000e+00 1.00011549e+00 9.98878486e-01 -4.01728526e-04 -6.73895806e-05 3.61308752e-04 9.98538920e-01 -7.28895531e-06 1.12008228e-05 1.07859495e-06 1.00270348e+00 + 2.90000000e+00 1.00011694e+00 9.98793523e-01 -4.35708587e-04 -6.90047423e-05 3.91803727e-04 9.98425632e-01 -9.21054066e-06 9.94105354e-06 3.28172602e-06 1.00290402e+00 + 3.10000000e+00 1.00011838e+00 9.98708614e-01 -4.69507632e-04 -7.04463148e-05 4.22057177e-04 9.98312299e-01 -1.11798148e-05 8.49461486e-06 5.57475813e-06 1.00310460e+00 + 3.30000000e+00 1.00011981e+00 9.98623770e-01 -5.03138148e-04 -7.17523553e-05 4.52071898e-04 9.98198911e-01 -1.31725784e-05 6.91570392e-06 7.96210582e-06 1.00330522e+00 + 3.70000000e+00 1.00012270e+00 9.98454408e-01 -5.69772736e-04 -7.39219689e-05 5.11375105e-04 9.97971935e-01 -1.71340192e-05 3.36197349e-06 1.30473187e-05 1.00370654e+00 + 4.10000000e+00 1.00012556e+00 9.98285370e-01 -6.35708749e-04 -7.56970052e-05 5.70088907e-04 9.97744687e-01 -2.09651208e-05 -5.69965397e-07 1.84013724e-05 1.00410803e+00 + 4.50000000e+00 1.00012840e+00 9.98116635e-01 -7.00817086e-04 -7.71514448e-05 6.28193673e-04 9.97517193e-01 -2.47032232e-05 -4.90017805e-06 2.40753249e-05 1.00450967e+00 + 4.90000000e+00 1.00013122e+00 9.97948151e-01 -7.65076340e-04 -7.83608964e-05 6.85690551e-04 9.97289507e-01 -2.83925432e-05 -9.61051138e-06 3.00257112e-05 1.00491148e+00 + 5.10000000e+00 1.00013259e+00 9.97863948e-01 -7.96990225e-04 -7.89104841e-05 7.14276290e-04 9.97175601e-01 -3.02445464e-05 -1.20436373e-05 3.30712450e-05 1.00511246e+00 + 5.70000000e+00 1.00013685e+00 9.97611884e-01 -8.91087545e-04 -8.00416630e-05 7.98664367e-04 9.96833717e-01 -3.59567786e-05 -1.96907346e-05 4.27683747e-05 1.00571553e+00 + 6.20000000e+00 1.00014034e+00 9.97402095e-01 -9.68485723e-04 -8.06851487e-05 8.68039790e-04 9.96548601e-01 -4.07872347e-05 -2.61452971e-05 5.10986882e-05 1.00621839e+00 + 6.70000000e+00 1.00014381e+00 9.97192583e-01 -1.04496127e-03 -8.11006820e-05 9.36569789e-04 9.96263314e-01 -4.56520509e-05 -3.26152005e-05 5.96328933e-05 1.00672150e+00 + 7.45000000e+00 1.00014910e+00 9.96878915e-01 -1.15790320e-03 -8.13995624e-05 1.03775276e-03 9.95835202e-01 -5.28609506e-05 -4.21681737e-05 7.26130745e-05 1.00747654e+00 + 8.20000000e+00 1.00015434e+00 9.96565680e-01 -1.26914099e-03 -8.14293987e-05 1.13739383e-03 9.95406897e-01 -5.99172762e-05 -5.16931099e-05 8.56977007e-05 1.00823215e+00 + 8.95000000e+00 1.00015955e+00 9.96252668e-01 -1.37883734e-03 -8.12075063e-05 1.23571199e-03 9.94978609e-01 -6.66804655e-05 -6.12598580e-05 9.87488242e-05 1.00898832e+00 + 9.70000000e+00 1.00016472e+00 9.95939776e-01 -1.48712298e-03 -8.07912091e-05 1.33284940e-03 9.94550445e-01 -7.31961178e-05 -7.08076651e-05 1.11744409e-04 1.00974507e+00 + 1.07000000e+01 1.00017172e+00 9.95522903e-01 -1.62896242e-03 -8.01051227e-05 1.46018792e-03 9.93979924e-01 -8.16948497e-05 -8.33849998e-05 1.29067762e-04 1.01075481e+00 diff --git a/test/data/test_results/voce_ea_cs/avg_elastic_strain_global.txt b/test/data/test_results/voce_ea_cs/avg_elastic_strain_global.txt new file mode 100644 index 0000000..7c1f523 --- /dev/null +++ b/test/data/test_results/voce_ea_cs/avg_elastic_strain_global.txt @@ -0,0 +1,41 @@ + # Time Volume Ee11 Ee22 Ee33 Ee23 Ee13 Ee12 + 5.00000000e-03 1.00000159e+00 -5.49512301e-08 -1.01222410e-07 7.26890046e-07 4.43489262e-09 -3.18156292e-08 8.63899603e-09 + 2.00000000e-01 1.00006342e+00 -2.21056100e-06 -4.05879349e-06 2.90294865e-05 1.75592137e-07 -1.27320521e-06 3.46547695e-07 + 3.00000000e-01 1.00008461e+00 -2.97560241e-06 -6.10871125e-06 3.90884811e-05 2.71338660e-07 -1.82707497e-06 3.59469095e-07 + 4.00000000e-01 1.00009168e+00 -3.19645087e-06 -7.41837735e-06 4.26954250e-05 1.63746434e-07 -2.14849013e-06 2.64463795e-07 + 5.00000000e-01 1.00009491e+00 -3.14177473e-06 -8.05813533e-06 4.42454249e-05 -3.46053937e-08 -2.30177952e-06 7.58243584e-08 + 6.00000000e-01 1.00009698e+00 -2.98495924e-06 -8.35302642e-06 4.51159790e-05 -2.26534604e-07 -2.39117725e-06 -1.48479000e-07 + 7.00000000e-01 1.00009854e+00 -2.86228103e-06 -8.53219606e-06 4.57305757e-05 -3.87285574e-07 -2.46202004e-06 -3.45894086e-07 + 8.00000000e-01 1.00009984e+00 -2.80280951e-06 -8.66350086e-06 4.62266874e-05 -5.23857088e-07 -2.52632570e-06 -5.03947712e-07 + 9.00000000e-01 1.00010097e+00 -2.79085850e-06 -8.76925790e-06 4.66561644e-05 -6.48119282e-07 -2.58796132e-06 -6.30261867e-07 + 1.00000000e+00 1.00010200e+00 -2.80647509e-06 -8.85331367e-06 4.70449232e-05 -7.61534466e-07 -2.64427771e-06 -7.32040691e-07 + 1.10000000e+00 1.00010296e+00 -2.83582992e-06 -8.92220819e-06 4.74053972e-05 -8.63153293e-07 -2.69345344e-06 -8.15007109e-07 + 1.20000000e+00 1.00010386e+00 -2.87075286e-06 -8.98202236e-06 4.77448982e-05 -9.53645123e-07 -2.73492830e-06 -8.83569444e-07 + 1.30000000e+00 1.00010473e+00 -2.90615072e-06 -9.03581736e-06 4.80693636e-05 -1.03529365e-06 -2.76994542e-06 -9.41377314e-07 + 1.40000000e+00 1.00010557e+00 -2.94112400e-06 -9.08561143e-06 4.83837840e-05 -1.11010266e-06 -2.80035200e-06 -9.90435642e-07 + 1.50000000e+00 1.00010638e+00 -2.97477909e-06 -9.13286779e-06 4.86912292e-05 -1.17939458e-06 -2.82753484e-06 -1.03252654e-06 + 1.60000000e+00 1.00010719e+00 -3.00632475e-06 -9.17842169e-06 4.89936781e-05 -1.24343723e-06 -2.85283996e-06 -1.06932238e-06 + 1.70000000e+00 1.00010798e+00 -3.03583754e-06 -9.22265829e-06 4.92923829e-05 -1.30200816e-06 -2.87715653e-06 -1.10178263e-06 + 1.80000000e+00 1.00010875e+00 -3.06421917e-06 -9.26571672e-06 4.95874637e-05 -1.35625795e-06 -2.90103480e-06 -1.13076959e-06 + 1.90000000e+00 1.00010952e+00 -3.09179557e-06 -9.30787267e-06 4.98789981e-05 -1.40714869e-06 -2.92450737e-06 -1.15724966e-06 + 2.00000000e+00 1.00011028e+00 -3.11823525e-06 -9.34970428e-06 5.01673720e-05 -1.45488221e-06 -2.94736299e-06 -1.18203907e-06 + 2.10000000e+00 1.00011104e+00 -3.14349925e-06 -9.39184237e-06 5.04532947e-05 -1.49970317e-06 -2.96959952e-06 -1.20536521e-06 + 2.30000000e+00 1.00011254e+00 -3.19207680e-06 -9.47636473e-06 5.10205475e-05 -1.58142052e-06 -3.01212762e-06 -1.24733902e-06 + 2.50000000e+00 1.00011402e+00 -3.24029622e-06 -9.55951246e-06 5.15855072e-05 -1.65787770e-06 -3.05256392e-06 -1.28644845e-06 + 2.70000000e+00 1.00011549e+00 -3.28837002e-06 -9.64093038e-06 5.21496021e-05 -1.73029153e-06 -3.09177373e-06 -1.32511641e-06 + 2.90000000e+00 1.00011694e+00 -3.33691448e-06 -9.72084738e-06 5.27137185e-05 -1.79876428e-06 -3.13019274e-06 -1.36375698e-06 + 3.10000000e+00 1.00011838e+00 -3.38740380e-06 -9.79904994e-06 5.32779776e-05 -1.86324905e-06 -3.16754468e-06 -1.40115986e-06 + 3.30000000e+00 1.00011981e+00 -3.43791180e-06 -9.87685061e-06 5.38415498e-05 -1.92438932e-06 -3.20419475e-06 -1.43590996e-06 + 3.70000000e+00 1.00012270e+00 -3.53549428e-06 -1.00359651e-05 5.49663114e-05 -2.03906996e-06 -3.27689471e-06 -1.49839043e-06 + 4.10000000e+00 1.00012556e+00 -3.63335547e-06 -1.01970432e-05 5.60914035e-05 -2.14571783e-06 -3.34856933e-06 -1.55445336e-06 + 4.50000000e+00 1.00012840e+00 -3.73331675e-06 -1.03540692e-05 5.72145140e-05 -2.24623261e-06 -3.41832309e-06 -1.60319444e-06 + 4.90000000e+00 1.00013122e+00 -3.83306406e-06 -1.05089090e-05 5.83347029e-05 -2.33941323e-06 -3.48564576e-06 -1.64809018e-06 + 5.10000000e+00 1.00013259e+00 -3.88172345e-06 -1.05878159e-05 5.88952448e-05 -2.38387825e-06 -3.51902132e-06 -1.67039958e-06 + 5.70000000e+00 1.00013685e+00 -4.01636577e-06 -1.08300771e-05 6.05690716e-05 -2.50737856e-06 -3.61596065e-06 -1.73862205e-06 + 6.20000000e+00 1.00014034e+00 -4.12472049e-06 -1.10377247e-05 6.19584740e-05 -2.60723358e-06 -3.69579959e-06 -1.79742993e-06 + 6.70000000e+00 1.00014381e+00 -4.22975228e-06 -1.12481991e-05 6.33422766e-05 -2.70268711e-06 -3.77525029e-06 -1.85684104e-06 + 7.45000000e+00 1.00014910e+00 -4.37991972e-06 -1.15680615e-05 6.54049952e-05 -2.83940270e-06 -3.89570216e-06 -1.94362504e-06 + 8.20000000e+00 1.00015434e+00 -4.52759249e-06 -1.18849521e-05 6.74596118e-05 -2.96411378e-06 -4.01492891e-06 -2.02680724e-06 + 8.95000000e+00 1.00015955e+00 -4.66605827e-06 -1.22005541e-05 6.95074002e-05 -3.08138169e-06 -4.13250310e-06 -2.10691195e-06 + 9.70000000e+00 1.00016472e+00 -4.79505655e-06 -1.25183148e-05 7.15466892e-05 -3.19325843e-06 -4.24917489e-06 -2.18761809e-06 + 1.07000000e+01 1.00017172e+00 -4.96109842e-06 -1.29429426e-05 7.42427851e-05 -3.33452566e-06 -4.40431898e-06 -2.29427227e-06 diff --git a/test/data/test_results/voce_ea_cs/avg_elastic_strain_region_default_1.txt b/test/data/test_results/voce_ea_cs/avg_elastic_strain_region_default_1.txt new file mode 100644 index 0000000..7c1f523 --- /dev/null +++ b/test/data/test_results/voce_ea_cs/avg_elastic_strain_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Ee11 Ee22 Ee33 Ee23 Ee13 Ee12 + 5.00000000e-03 1.00000159e+00 -5.49512301e-08 -1.01222410e-07 7.26890046e-07 4.43489262e-09 -3.18156292e-08 8.63899603e-09 + 2.00000000e-01 1.00006342e+00 -2.21056100e-06 -4.05879349e-06 2.90294865e-05 1.75592137e-07 -1.27320521e-06 3.46547695e-07 + 3.00000000e-01 1.00008461e+00 -2.97560241e-06 -6.10871125e-06 3.90884811e-05 2.71338660e-07 -1.82707497e-06 3.59469095e-07 + 4.00000000e-01 1.00009168e+00 -3.19645087e-06 -7.41837735e-06 4.26954250e-05 1.63746434e-07 -2.14849013e-06 2.64463795e-07 + 5.00000000e-01 1.00009491e+00 -3.14177473e-06 -8.05813533e-06 4.42454249e-05 -3.46053937e-08 -2.30177952e-06 7.58243584e-08 + 6.00000000e-01 1.00009698e+00 -2.98495924e-06 -8.35302642e-06 4.51159790e-05 -2.26534604e-07 -2.39117725e-06 -1.48479000e-07 + 7.00000000e-01 1.00009854e+00 -2.86228103e-06 -8.53219606e-06 4.57305757e-05 -3.87285574e-07 -2.46202004e-06 -3.45894086e-07 + 8.00000000e-01 1.00009984e+00 -2.80280951e-06 -8.66350086e-06 4.62266874e-05 -5.23857088e-07 -2.52632570e-06 -5.03947712e-07 + 9.00000000e-01 1.00010097e+00 -2.79085850e-06 -8.76925790e-06 4.66561644e-05 -6.48119282e-07 -2.58796132e-06 -6.30261867e-07 + 1.00000000e+00 1.00010200e+00 -2.80647509e-06 -8.85331367e-06 4.70449232e-05 -7.61534466e-07 -2.64427771e-06 -7.32040691e-07 + 1.10000000e+00 1.00010296e+00 -2.83582992e-06 -8.92220819e-06 4.74053972e-05 -8.63153293e-07 -2.69345344e-06 -8.15007109e-07 + 1.20000000e+00 1.00010386e+00 -2.87075286e-06 -8.98202236e-06 4.77448982e-05 -9.53645123e-07 -2.73492830e-06 -8.83569444e-07 + 1.30000000e+00 1.00010473e+00 -2.90615072e-06 -9.03581736e-06 4.80693636e-05 -1.03529365e-06 -2.76994542e-06 -9.41377314e-07 + 1.40000000e+00 1.00010557e+00 -2.94112400e-06 -9.08561143e-06 4.83837840e-05 -1.11010266e-06 -2.80035200e-06 -9.90435642e-07 + 1.50000000e+00 1.00010638e+00 -2.97477909e-06 -9.13286779e-06 4.86912292e-05 -1.17939458e-06 -2.82753484e-06 -1.03252654e-06 + 1.60000000e+00 1.00010719e+00 -3.00632475e-06 -9.17842169e-06 4.89936781e-05 -1.24343723e-06 -2.85283996e-06 -1.06932238e-06 + 1.70000000e+00 1.00010798e+00 -3.03583754e-06 -9.22265829e-06 4.92923829e-05 -1.30200816e-06 -2.87715653e-06 -1.10178263e-06 + 1.80000000e+00 1.00010875e+00 -3.06421917e-06 -9.26571672e-06 4.95874637e-05 -1.35625795e-06 -2.90103480e-06 -1.13076959e-06 + 1.90000000e+00 1.00010952e+00 -3.09179557e-06 -9.30787267e-06 4.98789981e-05 -1.40714869e-06 -2.92450737e-06 -1.15724966e-06 + 2.00000000e+00 1.00011028e+00 -3.11823525e-06 -9.34970428e-06 5.01673720e-05 -1.45488221e-06 -2.94736299e-06 -1.18203907e-06 + 2.10000000e+00 1.00011104e+00 -3.14349925e-06 -9.39184237e-06 5.04532947e-05 -1.49970317e-06 -2.96959952e-06 -1.20536521e-06 + 2.30000000e+00 1.00011254e+00 -3.19207680e-06 -9.47636473e-06 5.10205475e-05 -1.58142052e-06 -3.01212762e-06 -1.24733902e-06 + 2.50000000e+00 1.00011402e+00 -3.24029622e-06 -9.55951246e-06 5.15855072e-05 -1.65787770e-06 -3.05256392e-06 -1.28644845e-06 + 2.70000000e+00 1.00011549e+00 -3.28837002e-06 -9.64093038e-06 5.21496021e-05 -1.73029153e-06 -3.09177373e-06 -1.32511641e-06 + 2.90000000e+00 1.00011694e+00 -3.33691448e-06 -9.72084738e-06 5.27137185e-05 -1.79876428e-06 -3.13019274e-06 -1.36375698e-06 + 3.10000000e+00 1.00011838e+00 -3.38740380e-06 -9.79904994e-06 5.32779776e-05 -1.86324905e-06 -3.16754468e-06 -1.40115986e-06 + 3.30000000e+00 1.00011981e+00 -3.43791180e-06 -9.87685061e-06 5.38415498e-05 -1.92438932e-06 -3.20419475e-06 -1.43590996e-06 + 3.70000000e+00 1.00012270e+00 -3.53549428e-06 -1.00359651e-05 5.49663114e-05 -2.03906996e-06 -3.27689471e-06 -1.49839043e-06 + 4.10000000e+00 1.00012556e+00 -3.63335547e-06 -1.01970432e-05 5.60914035e-05 -2.14571783e-06 -3.34856933e-06 -1.55445336e-06 + 4.50000000e+00 1.00012840e+00 -3.73331675e-06 -1.03540692e-05 5.72145140e-05 -2.24623261e-06 -3.41832309e-06 -1.60319444e-06 + 4.90000000e+00 1.00013122e+00 -3.83306406e-06 -1.05089090e-05 5.83347029e-05 -2.33941323e-06 -3.48564576e-06 -1.64809018e-06 + 5.10000000e+00 1.00013259e+00 -3.88172345e-06 -1.05878159e-05 5.88952448e-05 -2.38387825e-06 -3.51902132e-06 -1.67039958e-06 + 5.70000000e+00 1.00013685e+00 -4.01636577e-06 -1.08300771e-05 6.05690716e-05 -2.50737856e-06 -3.61596065e-06 -1.73862205e-06 + 6.20000000e+00 1.00014034e+00 -4.12472049e-06 -1.10377247e-05 6.19584740e-05 -2.60723358e-06 -3.69579959e-06 -1.79742993e-06 + 6.70000000e+00 1.00014381e+00 -4.22975228e-06 -1.12481991e-05 6.33422766e-05 -2.70268711e-06 -3.77525029e-06 -1.85684104e-06 + 7.45000000e+00 1.00014910e+00 -4.37991972e-06 -1.15680615e-05 6.54049952e-05 -2.83940270e-06 -3.89570216e-06 -1.94362504e-06 + 8.20000000e+00 1.00015434e+00 -4.52759249e-06 -1.18849521e-05 6.74596118e-05 -2.96411378e-06 -4.01492891e-06 -2.02680724e-06 + 8.95000000e+00 1.00015955e+00 -4.66605827e-06 -1.22005541e-05 6.95074002e-05 -3.08138169e-06 -4.13250310e-06 -2.10691195e-06 + 9.70000000e+00 1.00016472e+00 -4.79505655e-06 -1.25183148e-05 7.15466892e-05 -3.19325843e-06 -4.24917489e-06 -2.18761809e-06 + 1.07000000e+01 1.00017172e+00 -4.96109842e-06 -1.29429426e-05 7.42427851e-05 -3.33452566e-06 -4.40431898e-06 -2.29427227e-06 diff --git a/test/data/test_results/voce_ea_cs/avg_eq_pl_strain_global.txt b/test/data/test_results/voce_ea_cs/avg_eq_pl_strain_global.txt new file mode 100644 index 0000000..5697531 --- /dev/null +++ b/test/data/test_results/voce_ea_cs/avg_eq_pl_strain_global.txt @@ -0,0 +1,41 @@ + # Time Volume Equiv_Plastic_Stra + 5.00000000e-03 1.00000159e+00 0.00000000e+00 + 2.00000000e-01 1.00006342e+00 3.46788929e-07 + 3.00000000e-01 1.00008461e+00 3.88927914e-05 + 4.00000000e-01 1.00009168e+00 1.30979670e-04 + 5.00000000e-01 1.00009491e+00 2.35546047e-04 + 6.00000000e-01 1.00009698e+00 3.42267651e-04 + 7.00000000e-01 1.00009854e+00 4.49227424e-04 + 8.00000000e-01 1.00009984e+00 5.55987651e-04 + 9.00000000e-01 1.00010097e+00 6.62478340e-04 + 1.00000000e+00 1.00010200e+00 7.68717592e-04 + 1.10000000e+00 1.00010296e+00 8.74753420e-04 + 1.20000000e+00 1.00010386e+00 9.80624445e-04 + 1.30000000e+00 1.00010473e+00 1.08635311e-03 + 1.40000000e+00 1.00010557e+00 1.19195577e-03 + 1.50000000e+00 1.00010638e+00 1.29744956e-03 + 1.60000000e+00 1.00010719e+00 1.40284902e-03 + 1.70000000e+00 1.00010798e+00 1.50816327e-03 + 1.80000000e+00 1.00010875e+00 1.61339787e-03 + 1.90000000e+00 1.00010952e+00 1.71855976e-03 + 2.00000000e+00 1.00011028e+00 1.82365495e-03 + 2.10000000e+00 1.00011104e+00 1.92868634e-03 + 2.30000000e+00 1.00011254e+00 2.13851133e-03 + 2.50000000e+00 1.00011402e+00 2.34811174e-03 + 2.70000000e+00 1.00011549e+00 2.55750011e-03 + 2.90000000e+00 1.00011694e+00 2.76670064e-03 + 3.10000000e+00 1.00011838e+00 2.97573044e-03 + 3.30000000e+00 1.00011981e+00 3.18460458e-03 + 3.70000000e+00 1.00012270e+00 3.60176048e-03 + 4.10000000e+00 1.00012556e+00 4.01844145e-03 + 4.50000000e+00 1.00012840e+00 4.43469041e-03 + 4.90000000e+00 1.00013122e+00 4.85054560e-03 + 5.10000000e+00 1.00013259e+00 5.05841190e-03 + 5.70000000e+00 1.00013685e+00 5.68107587e-03 + 6.20000000e+00 1.00014034e+00 6.19957649e-03 + 6.70000000e+00 1.00014381e+00 6.71769493e-03 + 7.45000000e+00 1.00014910e+00 7.49396656e-03 + 8.20000000e+00 1.00015434e+00 8.26956227e-03 + 8.95000000e+00 1.00015955e+00 9.04454082e-03 + 9.70000000e+00 1.00016472e+00 9.81898514e-03 + 1.07000000e+01 1.00017172e+00 1.08505577e-02 diff --git a/test/data/test_results/voce_ea_cs/avg_eq_pl_strain_region_default_1.txt b/test/data/test_results/voce_ea_cs/avg_eq_pl_strain_region_default_1.txt new file mode 100644 index 0000000..5697531 --- /dev/null +++ b/test/data/test_results/voce_ea_cs/avg_eq_pl_strain_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Equiv_Plastic_Stra + 5.00000000e-03 1.00000159e+00 0.00000000e+00 + 2.00000000e-01 1.00006342e+00 3.46788929e-07 + 3.00000000e-01 1.00008461e+00 3.88927914e-05 + 4.00000000e-01 1.00009168e+00 1.30979670e-04 + 5.00000000e-01 1.00009491e+00 2.35546047e-04 + 6.00000000e-01 1.00009698e+00 3.42267651e-04 + 7.00000000e-01 1.00009854e+00 4.49227424e-04 + 8.00000000e-01 1.00009984e+00 5.55987651e-04 + 9.00000000e-01 1.00010097e+00 6.62478340e-04 + 1.00000000e+00 1.00010200e+00 7.68717592e-04 + 1.10000000e+00 1.00010296e+00 8.74753420e-04 + 1.20000000e+00 1.00010386e+00 9.80624445e-04 + 1.30000000e+00 1.00010473e+00 1.08635311e-03 + 1.40000000e+00 1.00010557e+00 1.19195577e-03 + 1.50000000e+00 1.00010638e+00 1.29744956e-03 + 1.60000000e+00 1.00010719e+00 1.40284902e-03 + 1.70000000e+00 1.00010798e+00 1.50816327e-03 + 1.80000000e+00 1.00010875e+00 1.61339787e-03 + 1.90000000e+00 1.00010952e+00 1.71855976e-03 + 2.00000000e+00 1.00011028e+00 1.82365495e-03 + 2.10000000e+00 1.00011104e+00 1.92868634e-03 + 2.30000000e+00 1.00011254e+00 2.13851133e-03 + 2.50000000e+00 1.00011402e+00 2.34811174e-03 + 2.70000000e+00 1.00011549e+00 2.55750011e-03 + 2.90000000e+00 1.00011694e+00 2.76670064e-03 + 3.10000000e+00 1.00011838e+00 2.97573044e-03 + 3.30000000e+00 1.00011981e+00 3.18460458e-03 + 3.70000000e+00 1.00012270e+00 3.60176048e-03 + 4.10000000e+00 1.00012556e+00 4.01844145e-03 + 4.50000000e+00 1.00012840e+00 4.43469041e-03 + 4.90000000e+00 1.00013122e+00 4.85054560e-03 + 5.10000000e+00 1.00013259e+00 5.05841190e-03 + 5.70000000e+00 1.00013685e+00 5.68107587e-03 + 6.20000000e+00 1.00014034e+00 6.19957649e-03 + 6.70000000e+00 1.00014381e+00 6.71769493e-03 + 7.45000000e+00 1.00014910e+00 7.49396656e-03 + 8.20000000e+00 1.00015434e+00 8.26956227e-03 + 8.95000000e+00 1.00015955e+00 9.04454082e-03 + 9.70000000e+00 1.00016472e+00 9.81898514e-03 + 1.07000000e+01 1.00017172e+00 1.08505577e-02 diff --git a/test/data/test_results/voce_ea_cs/avg_euler_strain_global.txt b/test/data/test_results/voce_ea_cs/avg_euler_strain_global.txt new file mode 100644 index 0000000..66ecdcc --- /dev/null +++ b/test/data/test_results/voce_ea_cs/avg_euler_strain_global.txt @@ -0,0 +1,41 @@ + # Time Volume E11 E22 E33 E23 E13 E12 + 5.00000000e-03 1.00000159e+00 -1.63348823e-06 -1.77847722e-06 4.99996262e-06 1.10839246e-09 -9.90119337e-08 -7.40344508e-09 + 2.00000000e-01 1.00006342e+00 -6.53528350e-05 -7.12165201e-05 1.99941192e-04 4.13524659e-09 -3.98395496e-06 -2.68165560e-07 + 3.00000000e-01 1.00008461e+00 -1.01835571e-04 -1.13554672e-04 2.99886451e-04 -6.06485709e-07 -6.13846711e-06 -4.72082779e-07 + 4.00000000e-01 1.00009168e+00 -1.43207327e-04 -1.65133112e-04 3.99811633e-04 -2.06941711e-06 -8.55416111e-06 -1.21934598e-06 + 5.00000000e-01 1.00009491e+00 -1.85478614e-04 -2.19657046e-04 4.99716830e-04 -3.16457718e-06 -1.02297217e-05 -2.25114087e-06 + 6.00000000e-01 1.00009698e+00 -2.27822331e-04 -2.75285776e-04 5.99602064e-04 -3.77529908e-06 -1.14209129e-05 -3.43117496e-06 + 7.00000000e-01 1.00009854e+00 -2.70277601e-04 -3.31310048e-04 6.99467330e-04 -4.14130606e-06 -1.24389740e-05 -4.63733055e-06 + 8.00000000e-01 1.00009984e+00 -3.12857554e-04 -3.87489253e-04 7.99312628e-04 -4.35842294e-06 -1.34151374e-05 -5.74948419e-06 + 9.00000000e-01 1.00010097e+00 -3.55486691e-04 -4.43791768e-04 8.99137965e-04 -4.48452676e-06 -1.43577914e-05 -6.72576041e-06 + 1.00000000e+00 1.00010200e+00 -3.98120501e-04 -5.00206511e-04 9.98943346e-04 -4.54304577e-06 -1.52598952e-05 -7.58795491e-06 + 1.10000000e+00 1.00010296e+00 -4.40757686e-04 -5.56699834e-04 1.09872877e-03 -4.54556104e-06 -1.61193084e-05 -8.34536057e-06 + 1.20000000e+00 1.00010386e+00 -4.83406356e-04 -6.13241859e-04 1.19849425e-03 -4.51555113e-06 -1.69475390e-05 -9.02359579e-06 + 1.30000000e+00 1.00010473e+00 -5.26063885e-04 -6.69821139e-04 1.29823977e-03 -4.46843979e-06 -1.77474690e-05 -9.67235894e-06 + 1.40000000e+00 1.00010557e+00 -5.68729499e-04 -7.26429160e-04 1.39796534e-03 -4.40784761e-06 -1.85245865e-05 -1.03245728e-05 + 1.50000000e+00 1.00010638e+00 -6.11396979e-04 -7.83065860e-04 1.49767097e-03 -4.33568567e-06 -1.92859800e-05 -1.09867667e-05 + 1.60000000e+00 1.00010719e+00 -6.54063295e-04 -8.39729711e-04 1.59735665e-03 -4.25249732e-06 -2.00370798e-05 -1.16562551e-05 + 1.70000000e+00 1.00010798e+00 -6.96729828e-04 -8.96415995e-04 1.69702240e-03 -4.15924403e-06 -2.07819136e-05 -1.23369662e-05 + 1.80000000e+00 1.00010875e+00 -7.39405593e-04 -9.53113549e-04 1.79666821e-03 -4.05768319e-06 -2.15205731e-05 -1.30304769e-05 + 1.90000000e+00 1.00010952e+00 -7.82093041e-04 -1.00981828e-03 1.89629410e-03 -3.94741343e-06 -2.22520328e-05 -1.37427863e-05 + 2.00000000e+00 1.00011028e+00 -8.24783291e-04 -1.06653767e-03 1.99590006e-03 -3.82785348e-06 -2.29779254e-05 -1.44837632e-05 + 2.10000000e+00 1.00011104e+00 -8.67466507e-04 -1.12328060e-03 2.09548609e-03 -3.69933924e-06 -2.37003983e-05 -1.52548650e-05 + 2.30000000e+00 1.00011254e+00 -9.52777980e-04 -1.23684652e-03 2.29458846e-03 -3.44242273e-06 -2.51270081e-05 -1.68925705e-05 + 2.50000000e+00 1.00011402e+00 -1.03806912e-03 -1.35048989e-03 2.49361119e-03 -3.23479718e-06 -2.65395734e-05 -1.86044438e-05 + 2.70000000e+00 1.00011549e+00 -1.12334005e-03 -1.46420770e-03 2.69255432e-03 -3.07836935e-06 -2.79545412e-05 -2.03536539e-05 + 2.90000000e+00 1.00011694e+00 -1.20858928e-03 -1.57799878e-03 2.89141789e-03 -2.93088587e-06 -2.93780935e-05 -2.21209386e-05 + 3.10000000e+00 1.00011838e+00 -1.29380491e-03 -1.69187341e-03 3.09020193e-03 -2.76157430e-06 -3.08080858e-05 -2.39205667e-05 + 3.30000000e+00 1.00011981e+00 -1.37897674e-03 -1.80584060e-03 3.28890647e-03 -2.55616033e-06 -3.22364317e-05 -2.57574368e-05 + 3.70000000e+00 1.00012270e+00 -1.54905518e-03 -2.03408958e-03 3.68603750e-03 -1.97619450e-06 -3.50697993e-05 -2.94873426e-05 + 4.10000000e+00 1.00012556e+00 -1.71889134e-03 -2.26276371e-03 4.08285100e-03 -1.19448157e-06 -3.78947265e-05 -3.31704459e-05 + 4.50000000e+00 1.00012840e+00 -1.88850608e-03 -2.49183869e-03 4.47934720e-03 -2.04134856e-07 -4.07582919e-05 -3.67515290e-05 + 4.90000000e+00 1.00013122e+00 -2.05795161e-03 -2.72125983e-03 4.87552636e-03 9.51092531e-07 -4.36892057e-05 -4.02189981e-05 + 5.10000000e+00 1.00013259e+00 -2.14266571e-03 -2.83609180e-03 5.07351692e-03 1.56114723e-06 -4.51659408e-05 -4.19289099e-05 + 5.70000000e+00 1.00013685e+00 -2.39638514e-03 -3.18098596e-03 5.66689510e-03 3.59771937e-06 -4.95121981e-05 -4.69311708e-05 + 6.20000000e+00 1.00014034e+00 -2.60769326e-03 -3.46887774e-03 6.16085812e-03 5.38900460e-06 -5.30258850e-05 -5.10781307e-05 + 6.70000000e+00 1.00014381e+00 -2.81885272e-03 -3.75718532e-03 6.65432754e-03 7.26916687e-06 -5.64337379e-05 -5.51977202e-05 + 7.45000000e+00 1.00014910e+00 -3.13522705e-03 -4.19028465e-03 7.39351500e-03 1.02299474e-05 -6.13082435e-05 -6.13180810e-05 + 8.20000000e+00 1.00015434e+00 -3.45145437e-03 -4.62412838e-03 8.13159494e-03 1.33271410e-05 -6.60349742e-05 -6.73819652e-05 + 8.95000000e+00 1.00015955e+00 -3.76774539e-03 -5.05850398e-03 8.86856899e-03 1.65608191e-05 -7.06577081e-05 -7.33603024e-05 + 9.70000000e+00 1.00016472e+00 -4.08420565e-03 -5.49330444e-03 9.60443882e-03 1.98969958e-05 -7.51747639e-05 -7.92467301e-05 + 1.07000000e+01 1.00017172e+00 -4.50628358e-03 -6.07352655e-03 1.05837616e-02 2.44486047e-05 -8.10563160e-05 -8.69481655e-05 diff --git a/test/data/test_results/voce_ea_cs/avg_euler_strain_region_default_1.txt b/test/data/test_results/voce_ea_cs/avg_euler_strain_region_default_1.txt new file mode 100644 index 0000000..66ecdcc --- /dev/null +++ b/test/data/test_results/voce_ea_cs/avg_euler_strain_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume E11 E22 E33 E23 E13 E12 + 5.00000000e-03 1.00000159e+00 -1.63348823e-06 -1.77847722e-06 4.99996262e-06 1.10839246e-09 -9.90119337e-08 -7.40344508e-09 + 2.00000000e-01 1.00006342e+00 -6.53528350e-05 -7.12165201e-05 1.99941192e-04 4.13524659e-09 -3.98395496e-06 -2.68165560e-07 + 3.00000000e-01 1.00008461e+00 -1.01835571e-04 -1.13554672e-04 2.99886451e-04 -6.06485709e-07 -6.13846711e-06 -4.72082779e-07 + 4.00000000e-01 1.00009168e+00 -1.43207327e-04 -1.65133112e-04 3.99811633e-04 -2.06941711e-06 -8.55416111e-06 -1.21934598e-06 + 5.00000000e-01 1.00009491e+00 -1.85478614e-04 -2.19657046e-04 4.99716830e-04 -3.16457718e-06 -1.02297217e-05 -2.25114087e-06 + 6.00000000e-01 1.00009698e+00 -2.27822331e-04 -2.75285776e-04 5.99602064e-04 -3.77529908e-06 -1.14209129e-05 -3.43117496e-06 + 7.00000000e-01 1.00009854e+00 -2.70277601e-04 -3.31310048e-04 6.99467330e-04 -4.14130606e-06 -1.24389740e-05 -4.63733055e-06 + 8.00000000e-01 1.00009984e+00 -3.12857554e-04 -3.87489253e-04 7.99312628e-04 -4.35842294e-06 -1.34151374e-05 -5.74948419e-06 + 9.00000000e-01 1.00010097e+00 -3.55486691e-04 -4.43791768e-04 8.99137965e-04 -4.48452676e-06 -1.43577914e-05 -6.72576041e-06 + 1.00000000e+00 1.00010200e+00 -3.98120501e-04 -5.00206511e-04 9.98943346e-04 -4.54304577e-06 -1.52598952e-05 -7.58795491e-06 + 1.10000000e+00 1.00010296e+00 -4.40757686e-04 -5.56699834e-04 1.09872877e-03 -4.54556104e-06 -1.61193084e-05 -8.34536057e-06 + 1.20000000e+00 1.00010386e+00 -4.83406356e-04 -6.13241859e-04 1.19849425e-03 -4.51555113e-06 -1.69475390e-05 -9.02359579e-06 + 1.30000000e+00 1.00010473e+00 -5.26063885e-04 -6.69821139e-04 1.29823977e-03 -4.46843979e-06 -1.77474690e-05 -9.67235894e-06 + 1.40000000e+00 1.00010557e+00 -5.68729499e-04 -7.26429160e-04 1.39796534e-03 -4.40784761e-06 -1.85245865e-05 -1.03245728e-05 + 1.50000000e+00 1.00010638e+00 -6.11396979e-04 -7.83065860e-04 1.49767097e-03 -4.33568567e-06 -1.92859800e-05 -1.09867667e-05 + 1.60000000e+00 1.00010719e+00 -6.54063295e-04 -8.39729711e-04 1.59735665e-03 -4.25249732e-06 -2.00370798e-05 -1.16562551e-05 + 1.70000000e+00 1.00010798e+00 -6.96729828e-04 -8.96415995e-04 1.69702240e-03 -4.15924403e-06 -2.07819136e-05 -1.23369662e-05 + 1.80000000e+00 1.00010875e+00 -7.39405593e-04 -9.53113549e-04 1.79666821e-03 -4.05768319e-06 -2.15205731e-05 -1.30304769e-05 + 1.90000000e+00 1.00010952e+00 -7.82093041e-04 -1.00981828e-03 1.89629410e-03 -3.94741343e-06 -2.22520328e-05 -1.37427863e-05 + 2.00000000e+00 1.00011028e+00 -8.24783291e-04 -1.06653767e-03 1.99590006e-03 -3.82785348e-06 -2.29779254e-05 -1.44837632e-05 + 2.10000000e+00 1.00011104e+00 -8.67466507e-04 -1.12328060e-03 2.09548609e-03 -3.69933924e-06 -2.37003983e-05 -1.52548650e-05 + 2.30000000e+00 1.00011254e+00 -9.52777980e-04 -1.23684652e-03 2.29458846e-03 -3.44242273e-06 -2.51270081e-05 -1.68925705e-05 + 2.50000000e+00 1.00011402e+00 -1.03806912e-03 -1.35048989e-03 2.49361119e-03 -3.23479718e-06 -2.65395734e-05 -1.86044438e-05 + 2.70000000e+00 1.00011549e+00 -1.12334005e-03 -1.46420770e-03 2.69255432e-03 -3.07836935e-06 -2.79545412e-05 -2.03536539e-05 + 2.90000000e+00 1.00011694e+00 -1.20858928e-03 -1.57799878e-03 2.89141789e-03 -2.93088587e-06 -2.93780935e-05 -2.21209386e-05 + 3.10000000e+00 1.00011838e+00 -1.29380491e-03 -1.69187341e-03 3.09020193e-03 -2.76157430e-06 -3.08080858e-05 -2.39205667e-05 + 3.30000000e+00 1.00011981e+00 -1.37897674e-03 -1.80584060e-03 3.28890647e-03 -2.55616033e-06 -3.22364317e-05 -2.57574368e-05 + 3.70000000e+00 1.00012270e+00 -1.54905518e-03 -2.03408958e-03 3.68603750e-03 -1.97619450e-06 -3.50697993e-05 -2.94873426e-05 + 4.10000000e+00 1.00012556e+00 -1.71889134e-03 -2.26276371e-03 4.08285100e-03 -1.19448157e-06 -3.78947265e-05 -3.31704459e-05 + 4.50000000e+00 1.00012840e+00 -1.88850608e-03 -2.49183869e-03 4.47934720e-03 -2.04134856e-07 -4.07582919e-05 -3.67515290e-05 + 4.90000000e+00 1.00013122e+00 -2.05795161e-03 -2.72125983e-03 4.87552636e-03 9.51092531e-07 -4.36892057e-05 -4.02189981e-05 + 5.10000000e+00 1.00013259e+00 -2.14266571e-03 -2.83609180e-03 5.07351692e-03 1.56114723e-06 -4.51659408e-05 -4.19289099e-05 + 5.70000000e+00 1.00013685e+00 -2.39638514e-03 -3.18098596e-03 5.66689510e-03 3.59771937e-06 -4.95121981e-05 -4.69311708e-05 + 6.20000000e+00 1.00014034e+00 -2.60769326e-03 -3.46887774e-03 6.16085812e-03 5.38900460e-06 -5.30258850e-05 -5.10781307e-05 + 6.70000000e+00 1.00014381e+00 -2.81885272e-03 -3.75718532e-03 6.65432754e-03 7.26916687e-06 -5.64337379e-05 -5.51977202e-05 + 7.45000000e+00 1.00014910e+00 -3.13522705e-03 -4.19028465e-03 7.39351500e-03 1.02299474e-05 -6.13082435e-05 -6.13180810e-05 + 8.20000000e+00 1.00015434e+00 -3.45145437e-03 -4.62412838e-03 8.13159494e-03 1.33271410e-05 -6.60349742e-05 -6.73819652e-05 + 8.95000000e+00 1.00015955e+00 -3.76774539e-03 -5.05850398e-03 8.86856899e-03 1.65608191e-05 -7.06577081e-05 -7.33603024e-05 + 9.70000000e+00 1.00016472e+00 -4.08420565e-03 -5.49330444e-03 9.60443882e-03 1.98969958e-05 -7.51747639e-05 -7.92467301e-05 + 1.07000000e+01 1.00017172e+00 -4.50628358e-03 -6.07352655e-03 1.05837616e-02 2.44486047e-05 -8.10563160e-05 -8.69481655e-05 diff --git a/test/data/test_results/voce_ea_cs/avg_pl_work_global.txt b/test/data/test_results/voce_ea_cs/avg_pl_work_global.txt new file mode 100644 index 0000000..8722169 --- /dev/null +++ b/test/data/test_results/voce_ea_cs/avg_pl_work_global.txt @@ -0,0 +1,41 @@ + # Time Volume Plastic_Work + 5.00000000e-03 1.00000159e+00 0.00000000e+00 + 2.00000000e-01 1.00006342e+00 8.61647521e-09 + 3.00000000e-01 1.00008461e+00 1.06470917e-06 + 4.00000000e-01 1.00009168e+00 3.78251372e-06 + 5.00000000e-01 1.00009491e+00 7.08549953e-06 + 6.00000000e-01 1.00009698e+00 1.06322194e-05 + 7.00000000e-01 1.00009854e+00 1.43193796e-05 + 8.00000000e-01 1.00009984e+00 1.81061318e-05 + 9.00000000e-01 1.00010097e+00 2.19698255e-05 + 1.00000000e+00 1.00010200e+00 2.58973448e-05 + 1.10000000e+00 1.00010296e+00 2.98792589e-05 + 1.20000000e+00 1.00010386e+00 3.39089746e-05 + 1.30000000e+00 1.00010473e+00 3.79817652e-05 + 1.40000000e+00 1.00010557e+00 4.20942650e-05 + 1.50000000e+00 1.00010638e+00 4.62440405e-05 + 1.60000000e+00 1.00010719e+00 5.04291820e-05 + 1.70000000e+00 1.00010798e+00 5.46481655e-05 + 1.80000000e+00 1.00010875e+00 5.88999742e-05 + 1.90000000e+00 1.00010952e+00 6.31837993e-05 + 2.00000000e+00 1.00011028e+00 6.74989230e-05 + 2.10000000e+00 1.00011104e+00 7.18448265e-05 + 2.30000000e+00 1.00011254e+00 8.06540846e-05 + 2.50000000e+00 1.00011402e+00 8.95809468e-05 + 2.70000000e+00 1.00011549e+00 9.86238758e-05 + 2.90000000e+00 1.00011694e+00 1.07781028e-04 + 3.10000000e+00 1.00011838e+00 1.17051238e-04 + 3.30000000e+00 1.00011981e+00 1.26433635e-04 + 3.70000000e+00 1.00012270e+00 1.45633465e-04 + 4.10000000e+00 1.00012556e+00 1.65268713e-04 + 4.50000000e+00 1.00012840e+00 1.85335328e-04 + 4.90000000e+00 1.00013122e+00 2.05829746e-04 + 5.10000000e+00 1.00013259e+00 2.16185968e-04 + 5.70000000e+00 1.00013685e+00 2.48184603e-04 + 6.20000000e+00 1.00014034e+00 2.75502987e-04 + 6.70000000e+00 1.00014381e+00 3.03466363e-04 + 7.45000000e+00 1.00014910e+00 3.46835725e-04 + 8.20000000e+00 1.00015434e+00 3.91629751e-04 + 8.95000000e+00 1.00015955e+00 4.37836442e-04 + 9.70000000e+00 1.00016472e+00 4.85442784e-04 + 1.07000000e+01 1.00017172e+00 5.51359925e-04 diff --git a/test/data/test_results/voce_ea_cs/avg_pl_work_region_default_1.txt b/test/data/test_results/voce_ea_cs/avg_pl_work_region_default_1.txt new file mode 100644 index 0000000..8722169 --- /dev/null +++ b/test/data/test_results/voce_ea_cs/avg_pl_work_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Plastic_Work + 5.00000000e-03 1.00000159e+00 0.00000000e+00 + 2.00000000e-01 1.00006342e+00 8.61647521e-09 + 3.00000000e-01 1.00008461e+00 1.06470917e-06 + 4.00000000e-01 1.00009168e+00 3.78251372e-06 + 5.00000000e-01 1.00009491e+00 7.08549953e-06 + 6.00000000e-01 1.00009698e+00 1.06322194e-05 + 7.00000000e-01 1.00009854e+00 1.43193796e-05 + 8.00000000e-01 1.00009984e+00 1.81061318e-05 + 9.00000000e-01 1.00010097e+00 2.19698255e-05 + 1.00000000e+00 1.00010200e+00 2.58973448e-05 + 1.10000000e+00 1.00010296e+00 2.98792589e-05 + 1.20000000e+00 1.00010386e+00 3.39089746e-05 + 1.30000000e+00 1.00010473e+00 3.79817652e-05 + 1.40000000e+00 1.00010557e+00 4.20942650e-05 + 1.50000000e+00 1.00010638e+00 4.62440405e-05 + 1.60000000e+00 1.00010719e+00 5.04291820e-05 + 1.70000000e+00 1.00010798e+00 5.46481655e-05 + 1.80000000e+00 1.00010875e+00 5.88999742e-05 + 1.90000000e+00 1.00010952e+00 6.31837993e-05 + 2.00000000e+00 1.00011028e+00 6.74989230e-05 + 2.10000000e+00 1.00011104e+00 7.18448265e-05 + 2.30000000e+00 1.00011254e+00 8.06540846e-05 + 2.50000000e+00 1.00011402e+00 8.95809468e-05 + 2.70000000e+00 1.00011549e+00 9.86238758e-05 + 2.90000000e+00 1.00011694e+00 1.07781028e-04 + 3.10000000e+00 1.00011838e+00 1.17051238e-04 + 3.30000000e+00 1.00011981e+00 1.26433635e-04 + 3.70000000e+00 1.00012270e+00 1.45633465e-04 + 4.10000000e+00 1.00012556e+00 1.65268713e-04 + 4.50000000e+00 1.00012840e+00 1.85335328e-04 + 4.90000000e+00 1.00013122e+00 2.05829746e-04 + 5.10000000e+00 1.00013259e+00 2.16185968e-04 + 5.70000000e+00 1.00013685e+00 2.48184603e-04 + 6.20000000e+00 1.00014034e+00 2.75502987e-04 + 6.70000000e+00 1.00014381e+00 3.03466363e-04 + 7.45000000e+00 1.00014910e+00 3.46835725e-04 + 8.20000000e+00 1.00015434e+00 3.91629751e-04 + 8.95000000e+00 1.00015955e+00 4.37836442e-04 + 9.70000000e+00 1.00016472e+00 4.85442784e-04 + 1.07000000e+01 1.00017172e+00 5.51359925e-04 diff --git a/test/data/test_results/voce_ea_cs/avg_stress_global.txt b/test/data/test_results/voce_ea_cs/avg_stress_global.txt new file mode 100644 index 0000000..14f9201 --- /dev/null +++ b/test/data/test_results/voce_ea_cs/avg_stress_global.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.14673178e-14 1.17530421e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006342e+00 -2.09365608e-11 6.59388065e-11 2.60677388e-02 -2.37011019e-04 1.08523023e-04 8.92061150e-07 + 3.00000000e-01 1.00008461e+00 -3.37400423e-11 1.33391709e-09 3.47764131e-02 -3.34829810e-04 1.56622189e-04 2.36163721e-05 + 4.00000000e-01 1.00009168e+00 1.08923940e-11 1.04074504e-10 3.76792522e-02 -4.16313004e-04 1.45826099e-04 7.77878751e-05 + 5.00000000e-01 1.00009491e+00 1.12470961e-12 -3.93624415e-13 3.90050839e-02 -4.66039938e-04 1.40537384e-04 1.26975821e-04 + 6.00000000e-01 1.00009698e+00 4.83951829e-14 -1.19166429e-12 3.98508208e-02 -4.87920815e-04 1.54224364e-04 1.63593789e-04 + 7.00000000e-01 1.00009854e+00 6.97475825e-10 5.73627420e-10 4.04919847e-02 -5.00516683e-04 1.74053639e-04 1.90223111e-04 + 8.00000000e-01 1.00009984e+00 3.93722619e-10 3.36213376e-11 4.10221015e-02 -5.11061283e-04 1.93411453e-04 2.08460556e-04 + 9.00000000e-01 1.00010097e+00 9.55827562e-11 -5.75646080e-11 4.14852117e-02 -5.21826331e-04 2.09462107e-04 2.20470258e-04 + 1.00000000e+00 1.00010200e+00 1.96978052e-12 -7.17349600e-11 4.19041766e-02 -5.31837513e-04 2.22681228e-04 2.28587585e-04 + 1.10000000e+00 1.00010296e+00 -1.13184894e-11 -6.67325508e-11 4.22933604e-02 -5.40683927e-04 2.33825858e-04 2.33658619e-04 + 1.20000000e+00 1.00010386e+00 -2.50406097e-11 -4.92478235e-11 4.26617094e-02 -5.48806431e-04 2.43563130e-04 2.36718604e-04 + 1.30000000e+00 1.00010473e+00 -1.97487615e-11 -4.17434675e-11 4.30150115e-02 -5.56317941e-04 2.52403737e-04 2.38979918e-04 + 1.40000000e+00 1.00010557e+00 -2.04217885e-11 -3.14528192e-11 4.33570886e-02 -5.63213234e-04 2.60588589e-04 2.41060227e-04 + 1.50000000e+00 1.00010638e+00 -1.88051437e-11 -2.14565374e-11 4.36905235e-02 -5.69575399e-04 2.68119210e-04 2.42996235e-04 + 1.60000000e+00 1.00010719e+00 -1.50449632e-11 -1.37333235e-11 4.40171905e-02 -5.75305440e-04 2.75034162e-04 2.44865228e-04 + 1.70000000e+00 1.00010798e+00 -1.37702576e-11 -1.02334478e-11 4.43384614e-02 -5.80383193e-04 2.81490481e-04 2.46829411e-04 + 1.80000000e+00 1.00010875e+00 -1.34720082e-11 -8.84172262e-12 4.46552194e-02 -5.84923906e-04 2.87596716e-04 2.48798748e-04 + 1.90000000e+00 1.00010952e+00 -1.21160080e-11 -7.56087546e-12 4.49681428e-02 -5.89112465e-04 2.93397400e-04 2.50705558e-04 + 2.00000000e+00 1.00011028e+00 -1.00493091e-11 -7.08829045e-12 4.52778055e-02 -5.93115885e-04 2.98881221e-04 2.52533347e-04 + 2.10000000e+00 1.00011104e+00 -8.97577896e-12 -7.44266982e-12 4.55846116e-02 -5.97006605e-04 3.03981259e-04 2.54328516e-04 + 2.30000000e+00 1.00011254e+00 -4.11599849e-11 -3.00115963e-11 4.61894125e-02 -6.04312126e-04 3.12860226e-04 2.57933042e-04 + 2.50000000e+00 1.00011402e+00 -4.81541348e-11 -4.02842644e-11 4.67864707e-02 -6.11298274e-04 3.21165527e-04 2.61126210e-04 + 2.70000000e+00 1.00011549e+00 -4.89283110e-11 -3.93918456e-11 4.73769927e-02 -6.18179481e-04 3.29237225e-04 2.63801928e-04 + 2.90000000e+00 1.00011694e+00 -4.00389383e-11 -3.82689871e-11 4.79621051e-02 -6.25027357e-04 3.36913831e-04 2.66000550e-04 + 3.10000000e+00 1.00011838e+00 -3.91659348e-11 -4.19890337e-11 4.85424830e-02 -6.31779743e-04 3.44063020e-04 2.67846259e-04 + 3.30000000e+00 1.00011981e+00 -3.64781613e-11 -3.80262106e-11 4.91186552e-02 -6.38354827e-04 3.50902545e-04 2.69510292e-04 + 3.70000000e+00 1.00012270e+00 -1.94812983e-10 -1.98701997e-10 5.02570907e-02 -6.50779371e-04 3.64306441e-04 2.72414905e-04 + 4.10000000e+00 1.00012556e+00 -1.32380151e-10 -1.82378958e-10 5.13839268e-02 -6.62281474e-04 3.76719813e-04 2.74882311e-04 + 4.50000000e+00 1.00012840e+00 -8.64888787e-11 -1.29442998e-10 5.25008081e-02 -6.72581734e-04 3.87986907e-04 2.77043842e-04 + 4.90000000e+00 1.00013122e+00 -6.56762548e-11 -8.89757024e-11 5.36088933e-02 -6.81702819e-04 3.98823659e-04 2.78638524e-04 + 5.10000000e+00 1.00013259e+00 -1.47628840e-11 -1.91078738e-11 5.41612290e-02 -6.86078169e-04 4.04183326e-04 2.79320843e-04 + 5.70000000e+00 1.00013685e+00 1.37825974e-12 -7.25613700e-13 5.58010077e-02 -6.98760285e-04 4.19669478e-04 2.80993397e-04 + 6.20000000e+00 1.00014034e+00 -1.07207605e-10 -1.40498036e-10 5.71577155e-02 -7.09341973e-04 4.31484290e-04 2.82049697e-04 + 6.70000000e+00 1.00014381e+00 -9.60832091e-11 -1.12027469e-10 5.85053130e-02 -7.19763847e-04 4.42448476e-04 2.82978529e-04 + 7.45000000e+00 1.00014910e+00 -5.09970293e-10 -2.36531502e-10 6.05067689e-02 -7.35435885e-04 4.57699651e-04 2.84905820e-04 + 8.20000000e+00 1.00015434e+00 -4.78645067e-10 -2.67900407e-10 6.24898282e-02 -7.51174513e-04 4.71759148e-04 2.87475900e-04 + 8.95000000e+00 1.00015955e+00 -2.20899135e-10 -1.48679008e-10 6.44556099e-02 -7.67015272e-04 4.85886210e-04 2.91301362e-04 + 9.70000000e+00 1.00016472e+00 -2.31115075e-10 -1.40562315e-10 6.64049765e-02 -7.82963008e-04 5.00263340e-04 2.96139718e-04 + 1.07000000e+01 1.00017172e+00 -6.45317698e-10 -5.38539913e-10 6.89752132e-02 -8.04306391e-04 5.19269488e-04 3.03303612e-04 diff --git a/test/data/test_results/voce_ea_cs/avg_stress_region_default_1.txt b/test/data/test_results/voce_ea_cs/avg_stress_region_default_1.txt new file mode 100644 index 0000000..14f9201 --- /dev/null +++ b/test/data/test_results/voce_ea_cs/avg_stress_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.14673178e-14 1.17530421e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006342e+00 -2.09365608e-11 6.59388065e-11 2.60677388e-02 -2.37011019e-04 1.08523023e-04 8.92061150e-07 + 3.00000000e-01 1.00008461e+00 -3.37400423e-11 1.33391709e-09 3.47764131e-02 -3.34829810e-04 1.56622189e-04 2.36163721e-05 + 4.00000000e-01 1.00009168e+00 1.08923940e-11 1.04074504e-10 3.76792522e-02 -4.16313004e-04 1.45826099e-04 7.77878751e-05 + 5.00000000e-01 1.00009491e+00 1.12470961e-12 -3.93624415e-13 3.90050839e-02 -4.66039938e-04 1.40537384e-04 1.26975821e-04 + 6.00000000e-01 1.00009698e+00 4.83951829e-14 -1.19166429e-12 3.98508208e-02 -4.87920815e-04 1.54224364e-04 1.63593789e-04 + 7.00000000e-01 1.00009854e+00 6.97475825e-10 5.73627420e-10 4.04919847e-02 -5.00516683e-04 1.74053639e-04 1.90223111e-04 + 8.00000000e-01 1.00009984e+00 3.93722619e-10 3.36213376e-11 4.10221015e-02 -5.11061283e-04 1.93411453e-04 2.08460556e-04 + 9.00000000e-01 1.00010097e+00 9.55827562e-11 -5.75646080e-11 4.14852117e-02 -5.21826331e-04 2.09462107e-04 2.20470258e-04 + 1.00000000e+00 1.00010200e+00 1.96978052e-12 -7.17349600e-11 4.19041766e-02 -5.31837513e-04 2.22681228e-04 2.28587585e-04 + 1.10000000e+00 1.00010296e+00 -1.13184894e-11 -6.67325508e-11 4.22933604e-02 -5.40683927e-04 2.33825858e-04 2.33658619e-04 + 1.20000000e+00 1.00010386e+00 -2.50406097e-11 -4.92478235e-11 4.26617094e-02 -5.48806431e-04 2.43563130e-04 2.36718604e-04 + 1.30000000e+00 1.00010473e+00 -1.97487615e-11 -4.17434675e-11 4.30150115e-02 -5.56317941e-04 2.52403737e-04 2.38979918e-04 + 1.40000000e+00 1.00010557e+00 -2.04217885e-11 -3.14528192e-11 4.33570886e-02 -5.63213234e-04 2.60588589e-04 2.41060227e-04 + 1.50000000e+00 1.00010638e+00 -1.88051437e-11 -2.14565374e-11 4.36905235e-02 -5.69575399e-04 2.68119210e-04 2.42996235e-04 + 1.60000000e+00 1.00010719e+00 -1.50449632e-11 -1.37333235e-11 4.40171905e-02 -5.75305440e-04 2.75034162e-04 2.44865228e-04 + 1.70000000e+00 1.00010798e+00 -1.37702576e-11 -1.02334478e-11 4.43384614e-02 -5.80383193e-04 2.81490481e-04 2.46829411e-04 + 1.80000000e+00 1.00010875e+00 -1.34720082e-11 -8.84172262e-12 4.46552194e-02 -5.84923906e-04 2.87596716e-04 2.48798748e-04 + 1.90000000e+00 1.00010952e+00 -1.21160080e-11 -7.56087546e-12 4.49681428e-02 -5.89112465e-04 2.93397400e-04 2.50705558e-04 + 2.00000000e+00 1.00011028e+00 -1.00493091e-11 -7.08829045e-12 4.52778055e-02 -5.93115885e-04 2.98881221e-04 2.52533347e-04 + 2.10000000e+00 1.00011104e+00 -8.97577896e-12 -7.44266982e-12 4.55846116e-02 -5.97006605e-04 3.03981259e-04 2.54328516e-04 + 2.30000000e+00 1.00011254e+00 -4.11599849e-11 -3.00115963e-11 4.61894125e-02 -6.04312126e-04 3.12860226e-04 2.57933042e-04 + 2.50000000e+00 1.00011402e+00 -4.81541348e-11 -4.02842644e-11 4.67864707e-02 -6.11298274e-04 3.21165527e-04 2.61126210e-04 + 2.70000000e+00 1.00011549e+00 -4.89283110e-11 -3.93918456e-11 4.73769927e-02 -6.18179481e-04 3.29237225e-04 2.63801928e-04 + 2.90000000e+00 1.00011694e+00 -4.00389383e-11 -3.82689871e-11 4.79621051e-02 -6.25027357e-04 3.36913831e-04 2.66000550e-04 + 3.10000000e+00 1.00011838e+00 -3.91659348e-11 -4.19890337e-11 4.85424830e-02 -6.31779743e-04 3.44063020e-04 2.67846259e-04 + 3.30000000e+00 1.00011981e+00 -3.64781613e-11 -3.80262106e-11 4.91186552e-02 -6.38354827e-04 3.50902545e-04 2.69510292e-04 + 3.70000000e+00 1.00012270e+00 -1.94812983e-10 -1.98701997e-10 5.02570907e-02 -6.50779371e-04 3.64306441e-04 2.72414905e-04 + 4.10000000e+00 1.00012556e+00 -1.32380151e-10 -1.82378958e-10 5.13839268e-02 -6.62281474e-04 3.76719813e-04 2.74882311e-04 + 4.50000000e+00 1.00012840e+00 -8.64888787e-11 -1.29442998e-10 5.25008081e-02 -6.72581734e-04 3.87986907e-04 2.77043842e-04 + 4.90000000e+00 1.00013122e+00 -6.56762548e-11 -8.89757024e-11 5.36088933e-02 -6.81702819e-04 3.98823659e-04 2.78638524e-04 + 5.10000000e+00 1.00013259e+00 -1.47628840e-11 -1.91078738e-11 5.41612290e-02 -6.86078169e-04 4.04183326e-04 2.79320843e-04 + 5.70000000e+00 1.00013685e+00 1.37825974e-12 -7.25613700e-13 5.58010077e-02 -6.98760285e-04 4.19669478e-04 2.80993397e-04 + 6.20000000e+00 1.00014034e+00 -1.07207605e-10 -1.40498036e-10 5.71577155e-02 -7.09341973e-04 4.31484290e-04 2.82049697e-04 + 6.70000000e+00 1.00014381e+00 -9.60832091e-11 -1.12027469e-10 5.85053130e-02 -7.19763847e-04 4.42448476e-04 2.82978529e-04 + 7.45000000e+00 1.00014910e+00 -5.09970293e-10 -2.36531502e-10 6.05067689e-02 -7.35435885e-04 4.57699651e-04 2.84905820e-04 + 8.20000000e+00 1.00015434e+00 -4.78645067e-10 -2.67900407e-10 6.24898282e-02 -7.51174513e-04 4.71759148e-04 2.87475900e-04 + 8.95000000e+00 1.00015955e+00 -2.20899135e-10 -1.48679008e-10 6.44556099e-02 -7.67015272e-04 4.85886210e-04 2.91301362e-04 + 9.70000000e+00 1.00016472e+00 -2.31115075e-10 -1.40562315e-10 6.64049765e-02 -7.82963008e-04 5.00263340e-04 2.96139718e-04 + 1.07000000e+01 1.00017172e+00 -6.45317698e-10 -5.38539913e-10 6.89752132e-02 -8.04306391e-04 5.19269488e-04 3.03303612e-04 diff --git a/test/data/test_results/voce_full/avg_stress_global.txt b/test/data/test_results/voce_full/avg_stress_global.txt new file mode 100644 index 0000000..2626b21 --- /dev/null +++ b/test/data/test_results/voce_full/avg_stress_global.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.13447145e-14 1.14505311e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006342e+00 -1.74928218e-11 6.24861065e-11 2.60676157e-02 -2.37009587e-04 1.08522632e-04 8.92024216e-07 + 3.00000000e-01 1.00008461e+00 -3.09727587e-11 1.29966969e-09 3.47754323e-02 -3.34815155e-04 1.56617156e-04 2.36076465e-05 + 4.00000000e-01 1.00009168e+00 8.57270117e-12 1.05013182e-10 3.76782543e-02 -4.16276016e-04 1.45834708e-04 7.77594780e-05 + 5.00000000e-01 1.00009491e+00 -7.00173481e-14 2.52260471e-13 3.90039956e-02 -4.66010980e-04 1.40530555e-04 1.26938781e-04 + 6.00000000e-01 1.00009697e+00 -2.88873216e-14 -9.23483947e-13 3.98495687e-02 -4.87897510e-04 1.54198967e-04 1.63550487e-04 + 7.00000000e-01 1.00009854e+00 6.65644300e-10 7.26008890e-10 4.04905283e-02 -5.00492459e-04 1.74013242e-04 1.90179368e-04 + 8.00000000e-01 1.00009984e+00 3.56297107e-10 1.65071200e-10 4.10204085e-02 -5.11028641e-04 1.93364291e-04 2.08420875e-04 + 9.00000000e-01 1.00010097e+00 6.32124122e-11 3.65665399e-11 4.14832514e-02 -5.21784950e-04 2.09412163e-04 2.20434793e-04 + 1.00000000e+00 1.00010200e+00 -7.96307192e-12 -1.68660685e-11 4.19019180e-02 -5.31790954e-04 2.22628168e-04 2.28557252e-04 + 1.10000000e+00 1.00010295e+00 -9.91643241e-12 -3.48688877e-11 4.22907699e-02 -5.40632368e-04 2.33769766e-04 2.33635426e-04 + 1.20000000e+00 1.00010385e+00 -2.12317734e-11 -3.15476059e-11 4.26587542e-02 -5.48748436e-04 2.43502271e-04 2.36699117e-04 + 1.30000000e+00 1.00010472e+00 -1.86449245e-11 -2.55519263e-11 4.30116589e-02 -5.56254674e-04 2.52336992e-04 2.38959746e-04 + 1.40000000e+00 1.00010556e+00 -1.82024090e-11 -2.13712357e-11 4.33533077e-02 -5.63144606e-04 2.60516077e-04 2.41037649e-04 + 1.50000000e+00 1.00010637e+00 -1.65202679e-11 -1.64229057e-11 4.36862836e-02 -5.69502347e-04 2.68042011e-04 2.42972222e-04 + 1.60000000e+00 1.00010717e+00 -1.34294439e-11 -1.13379916e-11 4.40124606e-02 -5.75230332e-04 2.74952170e-04 2.44837341e-04 + 1.70000000e+00 1.00010796e+00 -1.16846611e-11 -9.38761938e-12 4.43332116e-02 -5.80306553e-04 2.81402612e-04 2.46797204e-04 + 1.80000000e+00 1.00010874e+00 -1.10081878e-11 -8.68622050e-12 4.46494223e-02 -5.84844926e-04 2.87502566e-04 2.48763570e-04 + 1.90000000e+00 1.00010951e+00 -9.91379672e-12 -7.78922616e-12 4.49617694e-02 -5.89028957e-04 2.93297267e-04 2.50667796e-04 + 2.00000000e+00 1.00011027e+00 -8.48777636e-12 -7.10868694e-12 4.52708278e-02 -5.93026040e-04 2.98776224e-04 2.52492950e-04 + 2.10000000e+00 1.00011102e+00 -7.67831412e-12 -6.95825657e-12 4.55770022e-02 -5.96910204e-04 3.03874004e-04 2.54283866e-04 + 2.30000000e+00 1.00011252e+00 -3.23787359e-11 -2.76769775e-11 4.61805777e-02 -6.04205302e-04 3.12747297e-04 2.57882441e-04 + 2.50000000e+00 1.00011399e+00 -3.54498904e-11 -3.79732736e-11 4.67762673e-02 -6.11176467e-04 3.21036980e-04 2.61074542e-04 + 2.70000000e+00 1.00011546e+00 -3.69091115e-11 -3.82228745e-11 4.73653081e-02 -6.18039619e-04 3.29092499e-04 2.63751210e-04 + 2.90000000e+00 1.00011691e+00 -3.37826069e-11 -3.33750781e-11 4.79488304e-02 -6.24868488e-04 3.36757790e-04 2.65950788e-04 + 3.10000000e+00 1.00011834e+00 -3.83945660e-11 -3.31009755e-11 4.85275129e-02 -6.31602653e-04 3.43894471e-04 2.67796086e-04 + 3.30000000e+00 1.00011977e+00 -3.83121309e-11 -2.94195131e-11 4.91018850e-02 -6.38160779e-04 3.50716892e-04 2.69457007e-04 + 3.70000000e+00 1.00012265e+00 -2.05810780e-10 -1.34378717e-10 5.02367220e-02 -6.50555827e-04 3.64082046e-04 2.72358372e-04 + 4.10000000e+00 1.00012550e+00 -1.52481356e-10 -9.16589864e-11 5.13595444e-02 -6.62034387e-04 3.76475508e-04 2.74824451e-04 + 4.50000000e+00 1.00012833e+00 -1.07011392e-10 -3.21754322e-11 5.24720005e-02 -6.72320889e-04 3.87711115e-04 2.76984252e-04 + 4.90000000e+00 1.00013113e+00 -8.65478978e-11 -1.10858908e-11 5.35752472e-02 -6.81421253e-04 3.98502524e-04 2.78587408e-04 + 5.10000000e+00 1.00013250e+00 -1.75022418e-11 -1.05661382e-11 5.41247476e-02 -6.85778061e-04 4.03837440e-04 2.79268190e-04 + 5.70000000e+00 1.00013673e+00 5.70389588e-13 8.47338841e-14 5.57565133e-02 -6.98396247e-04 4.19272921e-04 2.80938273e-04 + 6.20000000e+00 1.00014020e+00 -1.23120502e-10 -6.92989916e-11 5.71058151e-02 -7.08920735e-04 4.31052738e-04 2.82000462e-04 + 6.70000000e+00 1.00014365e+00 -1.03261530e-10 -4.80733930e-11 5.84453807e-02 -7.19283857e-04 4.41976040e-04 2.82917044e-04 + 7.45000000e+00 1.00014890e+00 3.22265656e-13 -6.95933568e-14 6.04342482e-02 -7.34841497e-04 4.57166612e-04 2.84811591e-04 + 8.20000000e+00 1.00015411e+00 3.48440077e-13 3.62043987e-13 6.24035785e-02 -7.50468358e-04 4.71149729e-04 2.87330170e-04 + 8.95000000e+00 1.00015927e+00 1.75763679e-13 3.97055079e-14 6.43543299e-02 -7.66173934e-04 4.85138255e-04 2.91061425e-04 + 9.70000000e+00 1.00016439e+00 -2.23770106e-10 -1.15171566e-10 6.62873955e-02 -7.81978408e-04 4.99387485e-04 2.95823481e-04 + 1.07000000e+01 1.00017133e+00 -4.12529077e-13 -9.19749152e-13 6.88347130e-02 -8.03113157e-04 5.18231937e-04 3.02891269e-04 diff --git a/test/data/test_results/voce_full/avg_stress_region_default_1.txt b/test/data/test_results/voce_full/avg_stress_region_default_1.txt new file mode 100644 index 0000000..2626b21 --- /dev/null +++ b/test/data/test_results/voce_full/avg_stress_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.13447145e-14 1.14505311e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006342e+00 -1.74928218e-11 6.24861065e-11 2.60676157e-02 -2.37009587e-04 1.08522632e-04 8.92024216e-07 + 3.00000000e-01 1.00008461e+00 -3.09727587e-11 1.29966969e-09 3.47754323e-02 -3.34815155e-04 1.56617156e-04 2.36076465e-05 + 4.00000000e-01 1.00009168e+00 8.57270117e-12 1.05013182e-10 3.76782543e-02 -4.16276016e-04 1.45834708e-04 7.77594780e-05 + 5.00000000e-01 1.00009491e+00 -7.00173481e-14 2.52260471e-13 3.90039956e-02 -4.66010980e-04 1.40530555e-04 1.26938781e-04 + 6.00000000e-01 1.00009697e+00 -2.88873216e-14 -9.23483947e-13 3.98495687e-02 -4.87897510e-04 1.54198967e-04 1.63550487e-04 + 7.00000000e-01 1.00009854e+00 6.65644300e-10 7.26008890e-10 4.04905283e-02 -5.00492459e-04 1.74013242e-04 1.90179368e-04 + 8.00000000e-01 1.00009984e+00 3.56297107e-10 1.65071200e-10 4.10204085e-02 -5.11028641e-04 1.93364291e-04 2.08420875e-04 + 9.00000000e-01 1.00010097e+00 6.32124122e-11 3.65665399e-11 4.14832514e-02 -5.21784950e-04 2.09412163e-04 2.20434793e-04 + 1.00000000e+00 1.00010200e+00 -7.96307192e-12 -1.68660685e-11 4.19019180e-02 -5.31790954e-04 2.22628168e-04 2.28557252e-04 + 1.10000000e+00 1.00010295e+00 -9.91643241e-12 -3.48688877e-11 4.22907699e-02 -5.40632368e-04 2.33769766e-04 2.33635426e-04 + 1.20000000e+00 1.00010385e+00 -2.12317734e-11 -3.15476059e-11 4.26587542e-02 -5.48748436e-04 2.43502271e-04 2.36699117e-04 + 1.30000000e+00 1.00010472e+00 -1.86449245e-11 -2.55519263e-11 4.30116589e-02 -5.56254674e-04 2.52336992e-04 2.38959746e-04 + 1.40000000e+00 1.00010556e+00 -1.82024090e-11 -2.13712357e-11 4.33533077e-02 -5.63144606e-04 2.60516077e-04 2.41037649e-04 + 1.50000000e+00 1.00010637e+00 -1.65202679e-11 -1.64229057e-11 4.36862836e-02 -5.69502347e-04 2.68042011e-04 2.42972222e-04 + 1.60000000e+00 1.00010717e+00 -1.34294439e-11 -1.13379916e-11 4.40124606e-02 -5.75230332e-04 2.74952170e-04 2.44837341e-04 + 1.70000000e+00 1.00010796e+00 -1.16846611e-11 -9.38761938e-12 4.43332116e-02 -5.80306553e-04 2.81402612e-04 2.46797204e-04 + 1.80000000e+00 1.00010874e+00 -1.10081878e-11 -8.68622050e-12 4.46494223e-02 -5.84844926e-04 2.87502566e-04 2.48763570e-04 + 1.90000000e+00 1.00010951e+00 -9.91379672e-12 -7.78922616e-12 4.49617694e-02 -5.89028957e-04 2.93297267e-04 2.50667796e-04 + 2.00000000e+00 1.00011027e+00 -8.48777636e-12 -7.10868694e-12 4.52708278e-02 -5.93026040e-04 2.98776224e-04 2.52492950e-04 + 2.10000000e+00 1.00011102e+00 -7.67831412e-12 -6.95825657e-12 4.55770022e-02 -5.96910204e-04 3.03874004e-04 2.54283866e-04 + 2.30000000e+00 1.00011252e+00 -3.23787359e-11 -2.76769775e-11 4.61805777e-02 -6.04205302e-04 3.12747297e-04 2.57882441e-04 + 2.50000000e+00 1.00011399e+00 -3.54498904e-11 -3.79732736e-11 4.67762673e-02 -6.11176467e-04 3.21036980e-04 2.61074542e-04 + 2.70000000e+00 1.00011546e+00 -3.69091115e-11 -3.82228745e-11 4.73653081e-02 -6.18039619e-04 3.29092499e-04 2.63751210e-04 + 2.90000000e+00 1.00011691e+00 -3.37826069e-11 -3.33750781e-11 4.79488304e-02 -6.24868488e-04 3.36757790e-04 2.65950788e-04 + 3.10000000e+00 1.00011834e+00 -3.83945660e-11 -3.31009755e-11 4.85275129e-02 -6.31602653e-04 3.43894471e-04 2.67796086e-04 + 3.30000000e+00 1.00011977e+00 -3.83121309e-11 -2.94195131e-11 4.91018850e-02 -6.38160779e-04 3.50716892e-04 2.69457007e-04 + 3.70000000e+00 1.00012265e+00 -2.05810780e-10 -1.34378717e-10 5.02367220e-02 -6.50555827e-04 3.64082046e-04 2.72358372e-04 + 4.10000000e+00 1.00012550e+00 -1.52481356e-10 -9.16589864e-11 5.13595444e-02 -6.62034387e-04 3.76475508e-04 2.74824451e-04 + 4.50000000e+00 1.00012833e+00 -1.07011392e-10 -3.21754322e-11 5.24720005e-02 -6.72320889e-04 3.87711115e-04 2.76984252e-04 + 4.90000000e+00 1.00013113e+00 -8.65478978e-11 -1.10858908e-11 5.35752472e-02 -6.81421253e-04 3.98502524e-04 2.78587408e-04 + 5.10000000e+00 1.00013250e+00 -1.75022418e-11 -1.05661382e-11 5.41247476e-02 -6.85778061e-04 4.03837440e-04 2.79268190e-04 + 5.70000000e+00 1.00013673e+00 5.70389588e-13 8.47338841e-14 5.57565133e-02 -6.98396247e-04 4.19272921e-04 2.80938273e-04 + 6.20000000e+00 1.00014020e+00 -1.23120502e-10 -6.92989916e-11 5.71058151e-02 -7.08920735e-04 4.31052738e-04 2.82000462e-04 + 6.70000000e+00 1.00014365e+00 -1.03261530e-10 -4.80733930e-11 5.84453807e-02 -7.19283857e-04 4.41976040e-04 2.82917044e-04 + 7.45000000e+00 1.00014890e+00 3.22265656e-13 -6.95933568e-14 6.04342482e-02 -7.34841497e-04 4.57166612e-04 2.84811591e-04 + 8.20000000e+00 1.00015411e+00 3.48440077e-13 3.62043987e-13 6.24035785e-02 -7.50468358e-04 4.71149729e-04 2.87330170e-04 + 8.95000000e+00 1.00015927e+00 1.75763679e-13 3.97055079e-14 6.43543299e-02 -7.66173934e-04 4.85138255e-04 2.91061425e-04 + 9.70000000e+00 1.00016439e+00 -2.23770106e-10 -1.15171566e-10 6.62873955e-02 -7.81978408e-04 4.99387485e-04 2.95823481e-04 + 1.07000000e+01 1.00017133e+00 -4.12529077e-13 -9.19749152e-13 6.88347130e-02 -8.03113157e-04 5.18231937e-04 3.02891269e-04 diff --git a/test/data/test_results/voce_full_cyclic/avg_stress_global.txt b/test/data/test_results/voce_full_cyclic/avg_stress_global.txt new file mode 100644 index 0000000..02ea3d2 --- /dev/null +++ b/test/data/test_results/voce_full_cyclic/avg_stress_global.txt @@ -0,0 +1,71 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 1.00000000e-01 1.00003176e+00 9.33903989e-11 9.42336486e-11 1.30582253e-02 -1.15907460e-04 5.56804695e-05 4.14809553e-07 + 2.00000000e-01 1.00006344e+00 -7.66688784e-12 4.01583078e-11 2.60786212e-02 -2.36259453e-04 1.08905883e-04 8.37769994e-07 + 3.00000000e-01 1.00008460e+00 -3.10617875e-11 1.27689387e-09 3.47764667e-02 -3.34724399e-04 1.56679103e-04 2.36065150e-05 + 4.00000000e-01 1.00009167e+00 8.58544757e-12 1.03394727e-10 3.76787671e-02 -4.16199237e-04 1.45900571e-04 7.77615940e-05 + 5.00000000e-01 1.00009490e+00 -6.95988547e-14 2.51895187e-13 3.90044275e-02 -4.65930269e-04 1.40608398e-04 1.26940595e-04 + 6.00000000e-01 1.00009696e+00 -2.87314742e-14 -9.23009719e-13 3.98499671e-02 -4.87816870e-04 1.54281176e-04 1.63551187e-04 + 7.00000000e-01 1.00009853e+00 6.65557375e-10 7.25695215e-10 4.04909100e-02 -5.00412843e-04 1.74097099e-04 1.90178943e-04 + 8.00000000e-01 1.00009983e+00 3.56283940e-10 1.65064718e-10 4.10207792e-02 -5.10951020e-04 1.93447654e-04 2.08419256e-04 + 9.00000000e-01 1.00010096e+00 6.31967878e-11 3.65263929e-11 4.14836138e-02 -5.21708989e-04 2.09493862e-04 2.20432433e-04 + 1.00000000e+00 1.00010199e+00 -7.95739889e-12 -1.68790895e-11 4.19022744e-02 -5.31716071e-04 2.22708225e-04 2.28554591e-04 + 1.10000000e+00 1.00007026e+00 -4.70895000e-11 1.87646969e-11 2.88551953e-02 -4.15769772e-04 1.67077159e-04 2.28246466e-04 + 1.20000000e+00 1.00003853e+00 1.31070152e-11 1.37142772e-11 1.58060232e-02 -2.99830306e-04 1.11426827e-04 2.27940392e-04 + 1.30000000e+00 1.00000679e+00 -4.38480792e-11 -4.07800912e-11 2.75470901e-03 -1.83894715e-04 5.57686650e-05 2.27639855e-04 + 1.40000000e+00 9.99975075e-01 -3.12936971e-12 -8.62960938e-12 -1.02894752e-02 -6.61972480e-05 1.19198153e-06 2.27366464e-04 + 1.50000000e+00 9.99945596e-01 -7.80333897e-13 -1.13167785e-12 -2.24147298e-02 8.02589625e-05 -4.01282924e-05 2.23587784e-04 + 1.60000000e+00 9.99927198e-01 -3.82875486e-12 -1.52320803e-12 -2.99839930e-02 1.47697437e-04 -1.13396253e-04 2.00083032e-04 + 1.70000000e+00 9.99917649e-01 -1.94046840e-12 -5.90072337e-13 -3.39139357e-02 2.22092914e-04 -1.39488394e-04 1.55387485e-04 + 1.80000000e+00 9.99911935e-01 -1.46970090e-12 -7.43346672e-13 -3.62671911e-02 3.17874174e-04 -1.22787705e-04 9.64997342e-05 + 1.90000000e+00 9.99908017e-01 -1.65295043e-09 -4.63513373e-09 -3.78817479e-02 3.91670855e-04 -1.11432302e-04 3.68559249e-05 + 2.00000000e+00 9.99905066e-01 -8.34064357e-10 -2.29578027e-09 -3.90984930e-02 4.37279592e-04 -1.12821427e-04 -1.28955355e-05 + 2.10000000e+00 9.99902706e-01 -3.94131462e-10 -8.73057322e-10 -4.00722350e-02 4.64962816e-04 -1.25955303e-04 -5.47192252e-05 + 2.20000000e+00 9.99900724e-01 -4.28306836e-10 -3.88351707e-10 -4.08906330e-02 4.83376946e-04 -1.43925277e-04 -9.09867424e-05 + 2.30000000e+00 9.99899001e-01 -2.18566882e-10 -2.38576070e-10 -4.16022120e-02 4.96636500e-04 -1.63565944e-04 -1.22415085e-04 + 2.40000000e+00 9.99897470e-01 -9.46397175e-11 -1.40538124e-10 -4.22351973e-02 5.07341430e-04 -1.84063723e-04 -1.48703655e-04 + 2.50000000e+00 9.99896085e-01 -7.72129771e-11 -1.07858158e-10 -4.28078686e-02 5.16856674e-04 -2.04580456e-04 -1.69793309e-04 + 2.60000000e+00 9.99894817e-01 -7.48807322e-11 -8.61342600e-11 -4.33323976e-02 5.26535915e-04 -2.24120223e-04 -1.86941647e-04 + 2.70000000e+00 9.99893640e-01 -7.19549582e-11 -7.00073472e-11 -4.38196380e-02 5.36953037e-04 -2.41377404e-04 -2.00693203e-04 + 2.80000000e+00 9.99892536e-01 -7.12707302e-11 -4.34619842e-11 -4.42770302e-02 5.47491803e-04 -2.55852543e-04 -2.11619396e-04 + 2.90000000e+00 9.99891495e-01 -5.78199997e-11 -3.35999882e-11 -4.47082740e-02 5.57667632e-04 -2.68673752e-04 -2.20795229e-04 + 3.00000000e+00 9.99890510e-01 -4.38855080e-11 -3.74193595e-11 -4.51164347e-02 5.66918964e-04 -2.80139873e-04 -2.28799282e-04 + 3.10000000e+00 9.99922303e-01 -1.19218748e-10 -1.00021580e-10 -3.20433569e-02 4.51062037e-04 -2.24426753e-04 -2.28298000e-04 + 3.20000000e+00 9.99954092e-01 -1.38238923e-11 -1.45804256e-11 -1.89725121e-02 3.35206180e-04 -1.68709735e-04 -2.27788864e-04 + 3.30000000e+00 9.99985878e-01 3.11934661e-11 2.89591790e-11 -5.90381619e-03 2.19346941e-04 -1.12997456e-04 -2.27273423e-04 + 3.40000000e+00 1.00001766e+00 -6.28456081e-15 -8.16780123e-15 7.16261751e-03 1.03477917e-04 -5.73166905e-05 -2.26741105e-04 + 3.50000000e+00 1.00004894e+00 -1.06377523e-10 1.11229793e-09 2.00192221e-02 -2.70360910e-05 -7.24311661e-06 -2.26651216e-04 + 3.60000000e+00 1.00007280e+00 -1.31980188e-12 -1.23342068e-12 2.98286526e-02 -1.27973388e-04 6.53514277e-05 -2.19068059e-04 + 3.70000000e+00 1.00008554e+00 1.49250905e-11 -3.06148836e-12 3.50629341e-02 -1.91594812e-04 1.46903721e-04 -1.90981728e-04 + 3.80000000e+00 1.00009278e+00 -9.23757104e-13 -5.66267946e-13 3.80353640e-02 -2.89589889e-04 1.63311555e-04 -1.39197129e-04 + 3.90000000e+00 1.00009758e+00 1.54650338e-09 4.17475936e-09 4.00056115e-02 -3.78573673e-04 1.60623245e-04 -7.74280278e-05 + 4.00000000e+00 1.00010109e+00 5.50753957e-10 2.02032069e-09 4.14468061e-02 -4.41665720e-04 1.64929800e-04 -1.89337472e-05 + 4.10000000e+00 1.00010384e+00 3.40165997e-10 4.01570429e-10 4.25743913e-02 -4.81599526e-04 1.75615963e-04 3.02271024e-05 + 4.20000000e+00 1.00010610e+00 2.10476181e-10 1.25109270e-10 4.34996752e-02 -5.08158634e-04 1.91743634e-04 7.14198747e-05 + 4.30000000e+00 1.00010803e+00 1.16083055e-10 2.87935244e-11 4.42908126e-02 -5.25611868e-04 2.09613235e-04 1.05691985e-04 + 4.40000000e+00 1.00010973e+00 2.14133581e-11 5.06677352e-11 4.49868352e-02 -5.37775284e-04 2.27889967e-04 1.34929024e-04 + 4.50000000e+00 1.00011126e+00 -1.22854331e-11 4.50103296e-11 4.56104718e-02 -5.47472427e-04 2.47285044e-04 1.59148393e-04 + 4.60000000e+00 1.00011264e+00 -2.52394089e-11 1.97497877e-11 4.61774753e-02 -5.57259828e-04 2.66045217e-04 1.78489349e-04 + 4.70000000e+00 1.00011392e+00 -3.33002374e-11 -2.38356785e-12 4.66987138e-02 -5.67698557e-04 2.83628844e-04 1.94567330e-04 + 4.80000000e+00 1.00011510e+00 -3.21067141e-11 -1.47508441e-11 4.71833529e-02 -5.78186732e-04 2.99008292e-04 2.07623062e-04 + 4.90000000e+00 1.00011622e+00 -2.72759591e-11 -2.06448785e-11 4.76387139e-02 -5.88023912e-04 3.11811159e-04 2.18271206e-04 + 5.00000000e+00 1.00011727e+00 -1.11593373e-11 -2.78606838e-11 4.80685264e-02 -5.97146162e-04 3.22702340e-04 2.27415672e-04 + 5.10000000e+00 1.00008554e+00 -8.72471698e-12 3.47976547e-11 3.50218648e-02 -4.81181015e-04 2.67052670e-04 2.27091677e-04 + 5.20000000e+00 1.00005381e+00 4.19189662e-11 4.16050306e-11 2.19731702e-02 -3.65223502e-04 2.11396340e-04 2.26776798e-04 + 5.30000000e+00 1.00002208e+00 -1.86190746e-11 -1.69668159e-11 8.92233319e-03 -2.49269611e-04 1.55733426e-04 2.26467424e-04 + 5.40000000e+00 9.99990341e-01 -7.05851309e-11 -6.79348440e-11 -4.13064645e-03 -1.33319230e-04 1.00064375e-04 2.26163783e-04 + 5.50000000e+00 9.99958668e-01 -7.07518253e-12 -2.08580942e-11 -1.71578067e-02 -1.38503643e-05 4.54659823e-05 2.25718859e-04 + 5.60000000e+00 9.99930528e-01 -3.25506647e-09 -6.85581713e-09 -2.87328205e-02 1.11730716e-04 -1.16952498e-05 2.26253774e-04 + 5.70000000e+00 9.99913627e-01 -6.32242952e-12 2.94682059e-13 -3.56866068e-02 1.65478209e-04 -1.22084209e-04 2.13430784e-04 + 5.80000000e+00 9.99904408e-01 -6.07228118e-13 -9.22381826e-13 -3.94808760e-02 2.55608400e-04 -1.84069547e-04 1.74182781e-04 + 5.90000000e+00 9.99898594e-01 -3.45096409e-09 -5.61820476e-09 -4.18751732e-02 3.53871832e-04 -1.96124263e-04 1.16988318e-04 + 6.00000000e+00 9.99894445e-01 -1.32791882e-09 -3.77437175e-09 -4.35846975e-02 4.31815360e-04 -2.05381569e-04 5.47628829e-05 + 6.10000000e+00 9.99891270e-01 -8.85570877e-10 -1.20649810e-09 -4.48938411e-02 4.86445945e-04 -2.16926333e-04 -1.68086649e-06 + 6.20000000e+00 9.99888713e-01 -5.23545225e-10 -5.78936788e-10 -4.59486877e-02 5.22476184e-04 -2.31420526e-04 -4.89215276e-05 + 6.30000000e+00 9.99886573e-01 -3.72276944e-10 -2.89054042e-10 -4.68321251e-02 5.46395084e-04 -2.48669172e-04 -8.81574391e-05 + 6.40000000e+00 9.99884719e-01 -1.58207870e-10 -1.98398438e-10 -4.75977861e-02 5.61876592e-04 -2.66515100e-04 -1.20853801e-04 + 6.50000000e+00 9.99883074e-01 -1.06162797e-10 -1.37357172e-10 -4.82774780e-02 5.73163540e-04 -2.84328340e-04 -1.48506961e-04 + 6.60000000e+00 9.99881590e-01 -8.17670241e-11 -8.43477812e-11 -4.88908246e-02 5.82593709e-04 -3.02395698e-04 -1.71274436e-04 + 6.70000000e+00 9.99880236e-01 -5.99645545e-11 -7.42511226e-11 -4.94506684e-02 5.92613510e-04 -3.19429926e-04 -1.89929583e-04 + 6.80000000e+00 9.99878990e-01 -5.26689278e-11 -6.43886474e-11 -4.99665946e-02 6.03295531e-04 -3.35357126e-04 -2.05702898e-04 + 6.90000000e+00 9.99877831e-01 -6.51486474e-11 -5.54320840e-11 -5.04463441e-02 6.13572640e-04 -3.49239888e-04 -2.18866869e-04 + 7.00000000e+00 9.99876745e-01 -5.87100520e-11 -5.07803863e-11 -5.08959852e-02 6.22803780e-04 -3.60510203e-04 -2.29699609e-04 diff --git a/test/data/test_results/voce_full_cyclic/avg_stress_region_default_1.txt b/test/data/test_results/voce_full_cyclic/avg_stress_region_default_1.txt new file mode 100644 index 0000000..02ea3d2 --- /dev/null +++ b/test/data/test_results/voce_full_cyclic/avg_stress_region_default_1.txt @@ -0,0 +1,71 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 1.00000000e-01 1.00003176e+00 9.33903989e-11 9.42336486e-11 1.30582253e-02 -1.15907460e-04 5.56804695e-05 4.14809553e-07 + 2.00000000e-01 1.00006344e+00 -7.66688784e-12 4.01583078e-11 2.60786212e-02 -2.36259453e-04 1.08905883e-04 8.37769994e-07 + 3.00000000e-01 1.00008460e+00 -3.10617875e-11 1.27689387e-09 3.47764667e-02 -3.34724399e-04 1.56679103e-04 2.36065150e-05 + 4.00000000e-01 1.00009167e+00 8.58544757e-12 1.03394727e-10 3.76787671e-02 -4.16199237e-04 1.45900571e-04 7.77615940e-05 + 5.00000000e-01 1.00009490e+00 -6.95988547e-14 2.51895187e-13 3.90044275e-02 -4.65930269e-04 1.40608398e-04 1.26940595e-04 + 6.00000000e-01 1.00009696e+00 -2.87314742e-14 -9.23009719e-13 3.98499671e-02 -4.87816870e-04 1.54281176e-04 1.63551187e-04 + 7.00000000e-01 1.00009853e+00 6.65557375e-10 7.25695215e-10 4.04909100e-02 -5.00412843e-04 1.74097099e-04 1.90178943e-04 + 8.00000000e-01 1.00009983e+00 3.56283940e-10 1.65064718e-10 4.10207792e-02 -5.10951020e-04 1.93447654e-04 2.08419256e-04 + 9.00000000e-01 1.00010096e+00 6.31967878e-11 3.65263929e-11 4.14836138e-02 -5.21708989e-04 2.09493862e-04 2.20432433e-04 + 1.00000000e+00 1.00010199e+00 -7.95739889e-12 -1.68790895e-11 4.19022744e-02 -5.31716071e-04 2.22708225e-04 2.28554591e-04 + 1.10000000e+00 1.00007026e+00 -4.70895000e-11 1.87646969e-11 2.88551953e-02 -4.15769772e-04 1.67077159e-04 2.28246466e-04 + 1.20000000e+00 1.00003853e+00 1.31070152e-11 1.37142772e-11 1.58060232e-02 -2.99830306e-04 1.11426827e-04 2.27940392e-04 + 1.30000000e+00 1.00000679e+00 -4.38480792e-11 -4.07800912e-11 2.75470901e-03 -1.83894715e-04 5.57686650e-05 2.27639855e-04 + 1.40000000e+00 9.99975075e-01 -3.12936971e-12 -8.62960938e-12 -1.02894752e-02 -6.61972480e-05 1.19198153e-06 2.27366464e-04 + 1.50000000e+00 9.99945596e-01 -7.80333897e-13 -1.13167785e-12 -2.24147298e-02 8.02589625e-05 -4.01282924e-05 2.23587784e-04 + 1.60000000e+00 9.99927198e-01 -3.82875486e-12 -1.52320803e-12 -2.99839930e-02 1.47697437e-04 -1.13396253e-04 2.00083032e-04 + 1.70000000e+00 9.99917649e-01 -1.94046840e-12 -5.90072337e-13 -3.39139357e-02 2.22092914e-04 -1.39488394e-04 1.55387485e-04 + 1.80000000e+00 9.99911935e-01 -1.46970090e-12 -7.43346672e-13 -3.62671911e-02 3.17874174e-04 -1.22787705e-04 9.64997342e-05 + 1.90000000e+00 9.99908017e-01 -1.65295043e-09 -4.63513373e-09 -3.78817479e-02 3.91670855e-04 -1.11432302e-04 3.68559249e-05 + 2.00000000e+00 9.99905066e-01 -8.34064357e-10 -2.29578027e-09 -3.90984930e-02 4.37279592e-04 -1.12821427e-04 -1.28955355e-05 + 2.10000000e+00 9.99902706e-01 -3.94131462e-10 -8.73057322e-10 -4.00722350e-02 4.64962816e-04 -1.25955303e-04 -5.47192252e-05 + 2.20000000e+00 9.99900724e-01 -4.28306836e-10 -3.88351707e-10 -4.08906330e-02 4.83376946e-04 -1.43925277e-04 -9.09867424e-05 + 2.30000000e+00 9.99899001e-01 -2.18566882e-10 -2.38576070e-10 -4.16022120e-02 4.96636500e-04 -1.63565944e-04 -1.22415085e-04 + 2.40000000e+00 9.99897470e-01 -9.46397175e-11 -1.40538124e-10 -4.22351973e-02 5.07341430e-04 -1.84063723e-04 -1.48703655e-04 + 2.50000000e+00 9.99896085e-01 -7.72129771e-11 -1.07858158e-10 -4.28078686e-02 5.16856674e-04 -2.04580456e-04 -1.69793309e-04 + 2.60000000e+00 9.99894817e-01 -7.48807322e-11 -8.61342600e-11 -4.33323976e-02 5.26535915e-04 -2.24120223e-04 -1.86941647e-04 + 2.70000000e+00 9.99893640e-01 -7.19549582e-11 -7.00073472e-11 -4.38196380e-02 5.36953037e-04 -2.41377404e-04 -2.00693203e-04 + 2.80000000e+00 9.99892536e-01 -7.12707302e-11 -4.34619842e-11 -4.42770302e-02 5.47491803e-04 -2.55852543e-04 -2.11619396e-04 + 2.90000000e+00 9.99891495e-01 -5.78199997e-11 -3.35999882e-11 -4.47082740e-02 5.57667632e-04 -2.68673752e-04 -2.20795229e-04 + 3.00000000e+00 9.99890510e-01 -4.38855080e-11 -3.74193595e-11 -4.51164347e-02 5.66918964e-04 -2.80139873e-04 -2.28799282e-04 + 3.10000000e+00 9.99922303e-01 -1.19218748e-10 -1.00021580e-10 -3.20433569e-02 4.51062037e-04 -2.24426753e-04 -2.28298000e-04 + 3.20000000e+00 9.99954092e-01 -1.38238923e-11 -1.45804256e-11 -1.89725121e-02 3.35206180e-04 -1.68709735e-04 -2.27788864e-04 + 3.30000000e+00 9.99985878e-01 3.11934661e-11 2.89591790e-11 -5.90381619e-03 2.19346941e-04 -1.12997456e-04 -2.27273423e-04 + 3.40000000e+00 1.00001766e+00 -6.28456081e-15 -8.16780123e-15 7.16261751e-03 1.03477917e-04 -5.73166905e-05 -2.26741105e-04 + 3.50000000e+00 1.00004894e+00 -1.06377523e-10 1.11229793e-09 2.00192221e-02 -2.70360910e-05 -7.24311661e-06 -2.26651216e-04 + 3.60000000e+00 1.00007280e+00 -1.31980188e-12 -1.23342068e-12 2.98286526e-02 -1.27973388e-04 6.53514277e-05 -2.19068059e-04 + 3.70000000e+00 1.00008554e+00 1.49250905e-11 -3.06148836e-12 3.50629341e-02 -1.91594812e-04 1.46903721e-04 -1.90981728e-04 + 3.80000000e+00 1.00009278e+00 -9.23757104e-13 -5.66267946e-13 3.80353640e-02 -2.89589889e-04 1.63311555e-04 -1.39197129e-04 + 3.90000000e+00 1.00009758e+00 1.54650338e-09 4.17475936e-09 4.00056115e-02 -3.78573673e-04 1.60623245e-04 -7.74280278e-05 + 4.00000000e+00 1.00010109e+00 5.50753957e-10 2.02032069e-09 4.14468061e-02 -4.41665720e-04 1.64929800e-04 -1.89337472e-05 + 4.10000000e+00 1.00010384e+00 3.40165997e-10 4.01570429e-10 4.25743913e-02 -4.81599526e-04 1.75615963e-04 3.02271024e-05 + 4.20000000e+00 1.00010610e+00 2.10476181e-10 1.25109270e-10 4.34996752e-02 -5.08158634e-04 1.91743634e-04 7.14198747e-05 + 4.30000000e+00 1.00010803e+00 1.16083055e-10 2.87935244e-11 4.42908126e-02 -5.25611868e-04 2.09613235e-04 1.05691985e-04 + 4.40000000e+00 1.00010973e+00 2.14133581e-11 5.06677352e-11 4.49868352e-02 -5.37775284e-04 2.27889967e-04 1.34929024e-04 + 4.50000000e+00 1.00011126e+00 -1.22854331e-11 4.50103296e-11 4.56104718e-02 -5.47472427e-04 2.47285044e-04 1.59148393e-04 + 4.60000000e+00 1.00011264e+00 -2.52394089e-11 1.97497877e-11 4.61774753e-02 -5.57259828e-04 2.66045217e-04 1.78489349e-04 + 4.70000000e+00 1.00011392e+00 -3.33002374e-11 -2.38356785e-12 4.66987138e-02 -5.67698557e-04 2.83628844e-04 1.94567330e-04 + 4.80000000e+00 1.00011510e+00 -3.21067141e-11 -1.47508441e-11 4.71833529e-02 -5.78186732e-04 2.99008292e-04 2.07623062e-04 + 4.90000000e+00 1.00011622e+00 -2.72759591e-11 -2.06448785e-11 4.76387139e-02 -5.88023912e-04 3.11811159e-04 2.18271206e-04 + 5.00000000e+00 1.00011727e+00 -1.11593373e-11 -2.78606838e-11 4.80685264e-02 -5.97146162e-04 3.22702340e-04 2.27415672e-04 + 5.10000000e+00 1.00008554e+00 -8.72471698e-12 3.47976547e-11 3.50218648e-02 -4.81181015e-04 2.67052670e-04 2.27091677e-04 + 5.20000000e+00 1.00005381e+00 4.19189662e-11 4.16050306e-11 2.19731702e-02 -3.65223502e-04 2.11396340e-04 2.26776798e-04 + 5.30000000e+00 1.00002208e+00 -1.86190746e-11 -1.69668159e-11 8.92233319e-03 -2.49269611e-04 1.55733426e-04 2.26467424e-04 + 5.40000000e+00 9.99990341e-01 -7.05851309e-11 -6.79348440e-11 -4.13064645e-03 -1.33319230e-04 1.00064375e-04 2.26163783e-04 + 5.50000000e+00 9.99958668e-01 -7.07518253e-12 -2.08580942e-11 -1.71578067e-02 -1.38503643e-05 4.54659823e-05 2.25718859e-04 + 5.60000000e+00 9.99930528e-01 -3.25506647e-09 -6.85581713e-09 -2.87328205e-02 1.11730716e-04 -1.16952498e-05 2.26253774e-04 + 5.70000000e+00 9.99913627e-01 -6.32242952e-12 2.94682059e-13 -3.56866068e-02 1.65478209e-04 -1.22084209e-04 2.13430784e-04 + 5.80000000e+00 9.99904408e-01 -6.07228118e-13 -9.22381826e-13 -3.94808760e-02 2.55608400e-04 -1.84069547e-04 1.74182781e-04 + 5.90000000e+00 9.99898594e-01 -3.45096409e-09 -5.61820476e-09 -4.18751732e-02 3.53871832e-04 -1.96124263e-04 1.16988318e-04 + 6.00000000e+00 9.99894445e-01 -1.32791882e-09 -3.77437175e-09 -4.35846975e-02 4.31815360e-04 -2.05381569e-04 5.47628829e-05 + 6.10000000e+00 9.99891270e-01 -8.85570877e-10 -1.20649810e-09 -4.48938411e-02 4.86445945e-04 -2.16926333e-04 -1.68086649e-06 + 6.20000000e+00 9.99888713e-01 -5.23545225e-10 -5.78936788e-10 -4.59486877e-02 5.22476184e-04 -2.31420526e-04 -4.89215276e-05 + 6.30000000e+00 9.99886573e-01 -3.72276944e-10 -2.89054042e-10 -4.68321251e-02 5.46395084e-04 -2.48669172e-04 -8.81574391e-05 + 6.40000000e+00 9.99884719e-01 -1.58207870e-10 -1.98398438e-10 -4.75977861e-02 5.61876592e-04 -2.66515100e-04 -1.20853801e-04 + 6.50000000e+00 9.99883074e-01 -1.06162797e-10 -1.37357172e-10 -4.82774780e-02 5.73163540e-04 -2.84328340e-04 -1.48506961e-04 + 6.60000000e+00 9.99881590e-01 -8.17670241e-11 -8.43477812e-11 -4.88908246e-02 5.82593709e-04 -3.02395698e-04 -1.71274436e-04 + 6.70000000e+00 9.99880236e-01 -5.99645545e-11 -7.42511226e-11 -4.94506684e-02 5.92613510e-04 -3.19429926e-04 -1.89929583e-04 + 6.80000000e+00 9.99878990e-01 -5.26689278e-11 -6.43886474e-11 -4.99665946e-02 6.03295531e-04 -3.35357126e-04 -2.05702898e-04 + 6.90000000e+00 9.99877831e-01 -6.51486474e-11 -5.54320840e-11 -5.04463441e-02 6.13572640e-04 -3.49239888e-04 -2.18866869e-04 + 7.00000000e+00 9.99876745e-01 -5.87100520e-11 -5.07803863e-11 -5.08959852e-02 6.22803780e-04 -3.60510203e-04 -2.29699609e-04 diff --git a/test/data/test_results/voce_full_cyclic_cs/avg_stress_global.txt b/test/data/test_results/voce_full_cyclic_cs/avg_stress_global.txt new file mode 100644 index 0000000..f90e8bb --- /dev/null +++ b/test/data/test_results/voce_full_cyclic_cs/avg_stress_global.txt @@ -0,0 +1,71 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 1.00000000e-01 1.00003176e+00 9.33903989e-11 9.42336486e-11 1.30582253e-02 -1.15907460e-04 5.56804695e-05 4.14809553e-07 + 2.00000000e-01 1.00006344e+00 -7.74581662e-12 3.90245952e-11 2.60798923e-02 -2.36273896e-04 1.08910000e-04 8.37972147e-07 + 3.00000000e-01 1.00008460e+00 -3.11257918e-11 1.28903575e-09 3.47778684e-02 -3.34745439e-04 1.56686246e-04 2.36190385e-05 + 4.00000000e-01 1.00009167e+00 8.68957989e-12 1.03618195e-10 3.76799258e-02 -4.16242592e-04 1.45890474e-04 7.77951124e-05 + 5.00000000e-01 1.00009490e+00 -7.10611353e-14 2.46471546e-13 3.90056069e-02 -4.65962061e-04 1.40615860e-04 1.26981424e-04 + 6.00000000e-01 1.00009697e+00 -2.59613389e-14 -9.19692758e-13 3.98512843e-02 -4.87841564e-04 1.54308223e-04 1.63597322e-04 + 7.00000000e-01 1.00009853e+00 6.62270991e-10 7.27615715e-10 4.04924184e-02 -5.00438072e-04 1.74139191e-04 1.90224765e-04 + 8.00000000e-01 1.00009983e+00 3.56947297e-10 1.66158818e-10 4.10225166e-02 -5.10984722e-04 1.93496374e-04 2.08460293e-04 + 9.00000000e-01 1.00010096e+00 6.32378017e-11 3.70839668e-11 4.14856137e-02 -5.21751395e-04 2.09545084e-04 2.20468803e-04 + 1.00000000e+00 1.00010199e+00 -7.64273641e-12 -1.66180968e-11 4.19045693e-02 -5.31763514e-04 2.22762360e-04 2.28585517e-04 + 1.10000000e+00 1.00007023e+00 -4.72394826e-11 1.87571482e-11 2.88444411e-02 -4.15701236e-04 1.67075675e-04 2.28277170e-04 + 1.20000000e+00 1.00003847e+00 -2.53092171e-12 3.04903169e-12 1.57835217e-02 -2.99657431e-04 1.11375229e-04 2.27970896e-04 + 1.30000000e+00 1.00000671e+00 -2.17180531e-11 -1.33762362e-11 2.72176286e-03 -1.83629043e-04 5.56725915e-05 2.27670202e-04 + 1.40000000e+00 9.99974973e-01 -3.29470903e-12 -9.13364516e-12 -1.03313424e-02 -6.58092897e-05 1.07729686e-06 2.27397406e-04 + 1.50000000e+00 9.99945501e-01 -7.93508160e-13 -1.14918618e-12 -2.24539501e-02 8.07056118e-05 -4.02609651e-05 2.23572178e-04 + 1.60000000e+00 9.99927143e-01 -3.98483937e-12 -1.51105147e-12 -3.00067482e-02 1.47913748e-04 -1.13631901e-04 1.99989858e-04 + 1.70000000e+00 9.99917616e-01 -1.87787986e-12 -5.99042565e-13 -3.39278827e-02 2.22520450e-04 -1.39430632e-04 1.55163727e-04 + 1.80000000e+00 9.99911911e-01 -1.47817292e-12 -7.46311024e-13 -3.62769724e-02 3.18296984e-04 -1.22673485e-04 9.62323332e-05 + 1.90000000e+00 9.99907999e-01 -1.63358154e-09 -4.62674802e-09 -3.78892128e-02 3.91963791e-04 -1.11381629e-04 3.66017964e-05 + 2.00000000e+00 9.99905052e-01 -8.33489083e-10 -2.29198409e-09 -3.91044775e-02 4.37450846e-04 -1.12843431e-04 -1.31006461e-05 + 2.10000000e+00 9.99902694e-01 -3.94685357e-10 -8.74196626e-10 -4.00771877e-02 4.65071108e-04 -1.26018456e-04 -5.48923789e-05 + 2.20000000e+00 9.99900714e-01 -4.30620543e-10 -3.86889200e-10 -4.08948120e-02 4.83446425e-04 -1.44000047e-04 -9.11311915e-05 + 2.30000000e+00 9.99898993e-01 -2.18316140e-10 -2.37409690e-10 -4.16057488e-02 4.96686238e-04 -1.63641758e-04 -1.22528476e-04 + 2.40000000e+00 9.99897463e-01 -9.43845537e-11 -1.39935502e-10 -4.22381772e-02 5.07378708e-04 -1.84134924e-04 -1.48784814e-04 + 2.50000000e+00 9.99896079e-01 -7.71060621e-11 -1.07323579e-10 -4.28103344e-02 5.16886679e-04 -2.04641577e-04 -1.69846131e-04 + 2.60000000e+00 9.99894813e-01 -7.47458244e-11 -8.57164787e-11 -4.33343861e-02 5.26562794e-04 -2.24164828e-04 -1.86972733e-04 + 2.70000000e+00 9.99893637e-01 -7.16206119e-11 -6.96342528e-11 -4.38211742e-02 5.36974412e-04 -2.41403351e-04 -2.00705756e-04 + 2.80000000e+00 9.99892533e-01 -7.09064073e-11 -4.31918938e-11 -4.42781246e-02 5.47504728e-04 -2.55862617e-04 -2.11618421e-04 + 2.90000000e+00 9.99891493e-01 -5.74230731e-11 -3.33644747e-11 -4.47089346e-02 5.57671699e-04 -2.68672255e-04 -2.20784575e-04 + 3.00000000e+00 9.99890509e-01 -4.35255535e-11 -3.71767298e-11 -4.51166735e-02 5.66915177e-04 -2.80129631e-04 -2.28781692e-04 + 3.10000000e+00 9.99922270e-01 -1.19206026e-10 -1.00092693e-10 -3.20566658e-02 4.51174039e-04 -2.24472228e-04 -2.28280928e-04 + 3.20000000e+00 9.99954031e-01 1.01676233e-11 3.93465146e-12 -1.89975817e-02 3.35422473e-04 -1.68805295e-04 -2.27772290e-04 + 3.30000000e+00 9.99985792e-01 1.75335318e-11 9.31929645e-12 -5.93933708e-03 2.19655882e-04 -1.13137605e-04 -2.27257302e-04 + 3.40000000e+00 1.00001755e+00 7.66786542e-16 -1.53851009e-15 7.11795717e-03 1.03868109e-04 -5.74954051e-05 -2.26725558e-04 + 3.50000000e+00 1.00004882e+00 -1.04865416e-10 1.08827098e-09 1.99704439e-02 -2.64306589e-05 -7.41331864e-06 -2.26642455e-04 + 3.60000000e+00 1.00007272e+00 -1.33397046e-12 -1.25913650e-12 2.97969124e-02 -1.27770309e-04 6.48925472e-05 -2.19134183e-04 + 3.70000000e+00 1.00008550e+00 1.50512508e-11 -3.07517309e-12 3.50451980e-02 -1.91179139e-04 1.46696918e-04 -1.91185679e-04 + 3.80000000e+00 1.00009275e+00 -9.11128697e-13 -5.57105812e-13 3.80241885e-02 -2.89075403e-04 1.63359772e-04 -1.39498433e-04 + 3.90000000e+00 1.00009756e+00 1.56417568e-09 4.17127576e-09 3.99979032e-02 -3.78169739e-04 1.60652856e-04 -7.77585775e-05 + 4.00000000e+00 1.00010108e+00 5.51096669e-10 2.03452527e-09 4.14412386e-02 -4.41410360e-04 1.64926262e-04 -1.92223235e-05 + 4.10000000e+00 1.00010383e+00 3.41770832e-10 3.99210182e-10 4.25703008e-02 -4.81441934e-04 1.75572351e-04 2.99899668e-05 + 4.20000000e+00 1.00010609e+00 2.08738683e-10 1.25804902e-10 4.34966632e-02 -5.08067833e-04 1.91683401e-04 7.12290912e-05 + 4.30000000e+00 1.00010803e+00 1.17075921e-10 2.86678222e-11 4.42886384e-02 -5.25565676e-04 2.09553689e-04 1.05541381e-04 + 4.40000000e+00 1.00010973e+00 2.14627482e-11 5.11147256e-11 4.49853741e-02 -5.37753424e-04 2.27830759e-04 1.34810377e-04 + 4.50000000e+00 1.00011125e+00 -1.21908708e-11 4.52104109e-11 4.56096435e-02 -5.47460295e-04 2.47230926e-04 1.59066574e-04 + 4.60000000e+00 1.00011264e+00 -2.52359223e-11 2.00410073e-11 4.61772309e-02 -5.57250814e-04 2.66004708e-04 1.78435689e-04 + 4.70000000e+00 1.00011392e+00 -3.32848998e-11 -2.18427072e-12 4.66990154e-02 -5.67694515e-04 2.83605105e-04 1.94534735e-04 + 4.80000000e+00 1.00011511e+00 -3.21395076e-11 -1.45728278e-11 4.71841740e-02 -5.78191903e-04 2.99004061e-04 2.07610057e-04 + 4.90000000e+00 1.00011622e+00 -2.73083334e-11 -2.05462711e-11 4.76400402e-02 -5.88039220e-04 3.11823411e-04 2.18272270e-04 + 5.00000000e+00 1.00011728e+00 -1.11036382e-11 -2.78753930e-11 4.80703509e-02 -5.97170508e-04 3.22726725e-04 2.27427684e-04 + 5.10000000e+00 1.00008552e+00 -8.63454978e-12 3.49744180e-11 3.50106415e-02 -4.81089400e-04 2.67021412e-04 2.27103410e-04 + 5.20000000e+00 1.00005376e+00 8.46880270e-12 1.40304867e-11 2.19502004e-02 -3.65027562e-04 2.11314920e-04 2.26788274e-04 + 5.30000000e+00 1.00002200e+00 -1.42283642e-11 -6.46197564e-12 8.88891948e-03 -2.48980876e-04 1.55607497e-04 2.26478685e-04 + 5.40000000e+00 9.99990237e-01 -2.85938484e-11 -1.92813804e-11 -4.17320099e-03 -1.32949309e-04 9.98995062e-05 2.26174866e-04 + 5.50000000e+00 9.99958547e-01 -7.45679488e-12 -2.19346349e-11 -1.72075666e-02 -1.33539189e-05 4.52922054e-05 2.25735507e-04 + 5.60000000e+00 9.99930428e-01 -3.29955698e-09 -6.94565696e-09 -2.87739102e-02 1.12064014e-04 -1.20827559e-05 2.26260975e-04 + 5.70000000e+00 9.99913569e-01 -6.48798623e-12 3.15182912e-13 -3.57104555e-02 1.65801282e-04 -1.22540730e-04 2.13326000e-04 + 5.80000000e+00 9.99904372e-01 -6.04699768e-13 -9.14345256e-13 -3.94960300e-02 2.56126139e-04 -1.84194137e-04 1.73952629e-04 + 5.90000000e+00 9.99898568e-01 -3.43844585e-09 -5.61203925e-09 -4.18861260e-02 3.54336244e-04 -1.96158501e-04 1.16678602e-04 + 6.00000000e+00 9.99894425e-01 -1.32711063e-09 -3.77357219e-09 -4.35931487e-02 4.32165227e-04 -2.05434302e-04 5.44556224e-05 + 6.10000000e+00 9.99891254e-01 -8.84851083e-10 -1.20129042e-09 -4.49006096e-02 4.86677874e-04 -2.16990011e-04 -1.94433500e-06 + 6.20000000e+00 9.99888700e-01 -5.24963416e-10 -5.76388458e-10 -4.59542217e-02 5.22631325e-04 -2.31504038e-04 -4.91330965e-05 + 6.30000000e+00 9.99886561e-01 -3.73173493e-10 -2.87656663e-10 -4.68367215e-02 5.46491823e-04 -2.48755246e-04 -8.83255605e-05 + 6.40000000e+00 9.99884709e-01 -1.57776076e-10 -1.97662961e-10 -4.76016155e-02 5.61939872e-04 -2.66596032e-04 -1.20984681e-04 + 6.50000000e+00 9.99883066e-01 -1.05981203e-10 -1.36703900e-10 -4.82806398e-02 5.73208540e-04 -2.84402424e-04 -1.48607418e-04 + 6.60000000e+00 9.99881584e-01 -8.16624273e-11 -8.39502673e-11 -4.88933770e-02 5.82631643e-04 -3.02459679e-04 -1.71343714e-04 + 6.70000000e+00 9.99880232e-01 -5.97617422e-11 -7.38938140e-11 -4.94526490e-02 5.92647806e-04 -3.19478626e-04 -1.89976719e-04 + 6.80000000e+00 9.99878986e-01 -5.24704831e-11 -6.39273052e-11 -4.99680310e-02 6.03322279e-04 -3.35390642e-04 -2.05731524e-04 + 6.90000000e+00 9.99877829e-01 -6.48406298e-11 -5.51119092e-11 -5.04472539e-02 6.13589003e-04 -3.49256452e-04 -2.18879658e-04 + 7.00000000e+00 9.99876744e-01 -5.83331756e-11 -5.05422353e-11 -5.08963753e-02 6.22809929e-04 -3.60512564e-04 -2.29699785e-04 diff --git a/test/data/test_results/voce_full_cyclic_cs/avg_stress_region_default_1.txt b/test/data/test_results/voce_full_cyclic_cs/avg_stress_region_default_1.txt new file mode 100644 index 0000000..f90e8bb --- /dev/null +++ b/test/data/test_results/voce_full_cyclic_cs/avg_stress_region_default_1.txt @@ -0,0 +1,71 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 1.00000000e-01 1.00003176e+00 9.33903989e-11 9.42336486e-11 1.30582253e-02 -1.15907460e-04 5.56804695e-05 4.14809553e-07 + 2.00000000e-01 1.00006344e+00 -7.74581662e-12 3.90245952e-11 2.60798923e-02 -2.36273896e-04 1.08910000e-04 8.37972147e-07 + 3.00000000e-01 1.00008460e+00 -3.11257918e-11 1.28903575e-09 3.47778684e-02 -3.34745439e-04 1.56686246e-04 2.36190385e-05 + 4.00000000e-01 1.00009167e+00 8.68957989e-12 1.03618195e-10 3.76799258e-02 -4.16242592e-04 1.45890474e-04 7.77951124e-05 + 5.00000000e-01 1.00009490e+00 -7.10611353e-14 2.46471546e-13 3.90056069e-02 -4.65962061e-04 1.40615860e-04 1.26981424e-04 + 6.00000000e-01 1.00009697e+00 -2.59613389e-14 -9.19692758e-13 3.98512843e-02 -4.87841564e-04 1.54308223e-04 1.63597322e-04 + 7.00000000e-01 1.00009853e+00 6.62270991e-10 7.27615715e-10 4.04924184e-02 -5.00438072e-04 1.74139191e-04 1.90224765e-04 + 8.00000000e-01 1.00009983e+00 3.56947297e-10 1.66158818e-10 4.10225166e-02 -5.10984722e-04 1.93496374e-04 2.08460293e-04 + 9.00000000e-01 1.00010096e+00 6.32378017e-11 3.70839668e-11 4.14856137e-02 -5.21751395e-04 2.09545084e-04 2.20468803e-04 + 1.00000000e+00 1.00010199e+00 -7.64273641e-12 -1.66180968e-11 4.19045693e-02 -5.31763514e-04 2.22762360e-04 2.28585517e-04 + 1.10000000e+00 1.00007023e+00 -4.72394826e-11 1.87571482e-11 2.88444411e-02 -4.15701236e-04 1.67075675e-04 2.28277170e-04 + 1.20000000e+00 1.00003847e+00 -2.53092171e-12 3.04903169e-12 1.57835217e-02 -2.99657431e-04 1.11375229e-04 2.27970896e-04 + 1.30000000e+00 1.00000671e+00 -2.17180531e-11 -1.33762362e-11 2.72176286e-03 -1.83629043e-04 5.56725915e-05 2.27670202e-04 + 1.40000000e+00 9.99974973e-01 -3.29470903e-12 -9.13364516e-12 -1.03313424e-02 -6.58092897e-05 1.07729686e-06 2.27397406e-04 + 1.50000000e+00 9.99945501e-01 -7.93508160e-13 -1.14918618e-12 -2.24539501e-02 8.07056118e-05 -4.02609651e-05 2.23572178e-04 + 1.60000000e+00 9.99927143e-01 -3.98483937e-12 -1.51105147e-12 -3.00067482e-02 1.47913748e-04 -1.13631901e-04 1.99989858e-04 + 1.70000000e+00 9.99917616e-01 -1.87787986e-12 -5.99042565e-13 -3.39278827e-02 2.22520450e-04 -1.39430632e-04 1.55163727e-04 + 1.80000000e+00 9.99911911e-01 -1.47817292e-12 -7.46311024e-13 -3.62769724e-02 3.18296984e-04 -1.22673485e-04 9.62323332e-05 + 1.90000000e+00 9.99907999e-01 -1.63358154e-09 -4.62674802e-09 -3.78892128e-02 3.91963791e-04 -1.11381629e-04 3.66017964e-05 + 2.00000000e+00 9.99905052e-01 -8.33489083e-10 -2.29198409e-09 -3.91044775e-02 4.37450846e-04 -1.12843431e-04 -1.31006461e-05 + 2.10000000e+00 9.99902694e-01 -3.94685357e-10 -8.74196626e-10 -4.00771877e-02 4.65071108e-04 -1.26018456e-04 -5.48923789e-05 + 2.20000000e+00 9.99900714e-01 -4.30620543e-10 -3.86889200e-10 -4.08948120e-02 4.83446425e-04 -1.44000047e-04 -9.11311915e-05 + 2.30000000e+00 9.99898993e-01 -2.18316140e-10 -2.37409690e-10 -4.16057488e-02 4.96686238e-04 -1.63641758e-04 -1.22528476e-04 + 2.40000000e+00 9.99897463e-01 -9.43845537e-11 -1.39935502e-10 -4.22381772e-02 5.07378708e-04 -1.84134924e-04 -1.48784814e-04 + 2.50000000e+00 9.99896079e-01 -7.71060621e-11 -1.07323579e-10 -4.28103344e-02 5.16886679e-04 -2.04641577e-04 -1.69846131e-04 + 2.60000000e+00 9.99894813e-01 -7.47458244e-11 -8.57164787e-11 -4.33343861e-02 5.26562794e-04 -2.24164828e-04 -1.86972733e-04 + 2.70000000e+00 9.99893637e-01 -7.16206119e-11 -6.96342528e-11 -4.38211742e-02 5.36974412e-04 -2.41403351e-04 -2.00705756e-04 + 2.80000000e+00 9.99892533e-01 -7.09064073e-11 -4.31918938e-11 -4.42781246e-02 5.47504728e-04 -2.55862617e-04 -2.11618421e-04 + 2.90000000e+00 9.99891493e-01 -5.74230731e-11 -3.33644747e-11 -4.47089346e-02 5.57671699e-04 -2.68672255e-04 -2.20784575e-04 + 3.00000000e+00 9.99890509e-01 -4.35255535e-11 -3.71767298e-11 -4.51166735e-02 5.66915177e-04 -2.80129631e-04 -2.28781692e-04 + 3.10000000e+00 9.99922270e-01 -1.19206026e-10 -1.00092693e-10 -3.20566658e-02 4.51174039e-04 -2.24472228e-04 -2.28280928e-04 + 3.20000000e+00 9.99954031e-01 1.01676233e-11 3.93465146e-12 -1.89975817e-02 3.35422473e-04 -1.68805295e-04 -2.27772290e-04 + 3.30000000e+00 9.99985792e-01 1.75335318e-11 9.31929645e-12 -5.93933708e-03 2.19655882e-04 -1.13137605e-04 -2.27257302e-04 + 3.40000000e+00 1.00001755e+00 7.66786542e-16 -1.53851009e-15 7.11795717e-03 1.03868109e-04 -5.74954051e-05 -2.26725558e-04 + 3.50000000e+00 1.00004882e+00 -1.04865416e-10 1.08827098e-09 1.99704439e-02 -2.64306589e-05 -7.41331864e-06 -2.26642455e-04 + 3.60000000e+00 1.00007272e+00 -1.33397046e-12 -1.25913650e-12 2.97969124e-02 -1.27770309e-04 6.48925472e-05 -2.19134183e-04 + 3.70000000e+00 1.00008550e+00 1.50512508e-11 -3.07517309e-12 3.50451980e-02 -1.91179139e-04 1.46696918e-04 -1.91185679e-04 + 3.80000000e+00 1.00009275e+00 -9.11128697e-13 -5.57105812e-13 3.80241885e-02 -2.89075403e-04 1.63359772e-04 -1.39498433e-04 + 3.90000000e+00 1.00009756e+00 1.56417568e-09 4.17127576e-09 3.99979032e-02 -3.78169739e-04 1.60652856e-04 -7.77585775e-05 + 4.00000000e+00 1.00010108e+00 5.51096669e-10 2.03452527e-09 4.14412386e-02 -4.41410360e-04 1.64926262e-04 -1.92223235e-05 + 4.10000000e+00 1.00010383e+00 3.41770832e-10 3.99210182e-10 4.25703008e-02 -4.81441934e-04 1.75572351e-04 2.99899668e-05 + 4.20000000e+00 1.00010609e+00 2.08738683e-10 1.25804902e-10 4.34966632e-02 -5.08067833e-04 1.91683401e-04 7.12290912e-05 + 4.30000000e+00 1.00010803e+00 1.17075921e-10 2.86678222e-11 4.42886384e-02 -5.25565676e-04 2.09553689e-04 1.05541381e-04 + 4.40000000e+00 1.00010973e+00 2.14627482e-11 5.11147256e-11 4.49853741e-02 -5.37753424e-04 2.27830759e-04 1.34810377e-04 + 4.50000000e+00 1.00011125e+00 -1.21908708e-11 4.52104109e-11 4.56096435e-02 -5.47460295e-04 2.47230926e-04 1.59066574e-04 + 4.60000000e+00 1.00011264e+00 -2.52359223e-11 2.00410073e-11 4.61772309e-02 -5.57250814e-04 2.66004708e-04 1.78435689e-04 + 4.70000000e+00 1.00011392e+00 -3.32848998e-11 -2.18427072e-12 4.66990154e-02 -5.67694515e-04 2.83605105e-04 1.94534735e-04 + 4.80000000e+00 1.00011511e+00 -3.21395076e-11 -1.45728278e-11 4.71841740e-02 -5.78191903e-04 2.99004061e-04 2.07610057e-04 + 4.90000000e+00 1.00011622e+00 -2.73083334e-11 -2.05462711e-11 4.76400402e-02 -5.88039220e-04 3.11823411e-04 2.18272270e-04 + 5.00000000e+00 1.00011728e+00 -1.11036382e-11 -2.78753930e-11 4.80703509e-02 -5.97170508e-04 3.22726725e-04 2.27427684e-04 + 5.10000000e+00 1.00008552e+00 -8.63454978e-12 3.49744180e-11 3.50106415e-02 -4.81089400e-04 2.67021412e-04 2.27103410e-04 + 5.20000000e+00 1.00005376e+00 8.46880270e-12 1.40304867e-11 2.19502004e-02 -3.65027562e-04 2.11314920e-04 2.26788274e-04 + 5.30000000e+00 1.00002200e+00 -1.42283642e-11 -6.46197564e-12 8.88891948e-03 -2.48980876e-04 1.55607497e-04 2.26478685e-04 + 5.40000000e+00 9.99990237e-01 -2.85938484e-11 -1.92813804e-11 -4.17320099e-03 -1.32949309e-04 9.98995062e-05 2.26174866e-04 + 5.50000000e+00 9.99958547e-01 -7.45679488e-12 -2.19346349e-11 -1.72075666e-02 -1.33539189e-05 4.52922054e-05 2.25735507e-04 + 5.60000000e+00 9.99930428e-01 -3.29955698e-09 -6.94565696e-09 -2.87739102e-02 1.12064014e-04 -1.20827559e-05 2.26260975e-04 + 5.70000000e+00 9.99913569e-01 -6.48798623e-12 3.15182912e-13 -3.57104555e-02 1.65801282e-04 -1.22540730e-04 2.13326000e-04 + 5.80000000e+00 9.99904372e-01 -6.04699768e-13 -9.14345256e-13 -3.94960300e-02 2.56126139e-04 -1.84194137e-04 1.73952629e-04 + 5.90000000e+00 9.99898568e-01 -3.43844585e-09 -5.61203925e-09 -4.18861260e-02 3.54336244e-04 -1.96158501e-04 1.16678602e-04 + 6.00000000e+00 9.99894425e-01 -1.32711063e-09 -3.77357219e-09 -4.35931487e-02 4.32165227e-04 -2.05434302e-04 5.44556224e-05 + 6.10000000e+00 9.99891254e-01 -8.84851083e-10 -1.20129042e-09 -4.49006096e-02 4.86677874e-04 -2.16990011e-04 -1.94433500e-06 + 6.20000000e+00 9.99888700e-01 -5.24963416e-10 -5.76388458e-10 -4.59542217e-02 5.22631325e-04 -2.31504038e-04 -4.91330965e-05 + 6.30000000e+00 9.99886561e-01 -3.73173493e-10 -2.87656663e-10 -4.68367215e-02 5.46491823e-04 -2.48755246e-04 -8.83255605e-05 + 6.40000000e+00 9.99884709e-01 -1.57776076e-10 -1.97662961e-10 -4.76016155e-02 5.61939872e-04 -2.66596032e-04 -1.20984681e-04 + 6.50000000e+00 9.99883066e-01 -1.05981203e-10 -1.36703900e-10 -4.82806398e-02 5.73208540e-04 -2.84402424e-04 -1.48607418e-04 + 6.60000000e+00 9.99881584e-01 -8.16624273e-11 -8.39502673e-11 -4.88933770e-02 5.82631643e-04 -3.02459679e-04 -1.71343714e-04 + 6.70000000e+00 9.99880232e-01 -5.97617422e-11 -7.38938140e-11 -4.94526490e-02 5.92647806e-04 -3.19478626e-04 -1.89976719e-04 + 6.80000000e+00 9.99878986e-01 -5.24704831e-11 -6.39273052e-11 -4.99680310e-02 6.03322279e-04 -3.35390642e-04 -2.05731524e-04 + 6.90000000e+00 9.99877829e-01 -6.48406298e-11 -5.51119092e-11 -5.04472539e-02 6.13589003e-04 -3.49256452e-04 -2.18879658e-04 + 7.00000000e+00 9.99876744e-01 -5.83331756e-11 -5.05422353e-11 -5.08963753e-02 6.22809929e-04 -3.60512564e-04 -2.29699785e-04 diff --git a/test/data/test_results/voce_full_cyclic_csm/avg_stress_global.txt b/test/data/test_results/voce_full_cyclic_csm/avg_stress_global.txt new file mode 100644 index 0000000..f90e8bb --- /dev/null +++ b/test/data/test_results/voce_full_cyclic_csm/avg_stress_global.txt @@ -0,0 +1,71 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 1.00000000e-01 1.00003176e+00 9.33903989e-11 9.42336486e-11 1.30582253e-02 -1.15907460e-04 5.56804695e-05 4.14809553e-07 + 2.00000000e-01 1.00006344e+00 -7.74581662e-12 3.90245952e-11 2.60798923e-02 -2.36273896e-04 1.08910000e-04 8.37972147e-07 + 3.00000000e-01 1.00008460e+00 -3.11257918e-11 1.28903575e-09 3.47778684e-02 -3.34745439e-04 1.56686246e-04 2.36190385e-05 + 4.00000000e-01 1.00009167e+00 8.68957989e-12 1.03618195e-10 3.76799258e-02 -4.16242592e-04 1.45890474e-04 7.77951124e-05 + 5.00000000e-01 1.00009490e+00 -7.10611353e-14 2.46471546e-13 3.90056069e-02 -4.65962061e-04 1.40615860e-04 1.26981424e-04 + 6.00000000e-01 1.00009697e+00 -2.59613389e-14 -9.19692758e-13 3.98512843e-02 -4.87841564e-04 1.54308223e-04 1.63597322e-04 + 7.00000000e-01 1.00009853e+00 6.62270991e-10 7.27615715e-10 4.04924184e-02 -5.00438072e-04 1.74139191e-04 1.90224765e-04 + 8.00000000e-01 1.00009983e+00 3.56947297e-10 1.66158818e-10 4.10225166e-02 -5.10984722e-04 1.93496374e-04 2.08460293e-04 + 9.00000000e-01 1.00010096e+00 6.32378017e-11 3.70839668e-11 4.14856137e-02 -5.21751395e-04 2.09545084e-04 2.20468803e-04 + 1.00000000e+00 1.00010199e+00 -7.64273641e-12 -1.66180968e-11 4.19045693e-02 -5.31763514e-04 2.22762360e-04 2.28585517e-04 + 1.10000000e+00 1.00007023e+00 -4.72394826e-11 1.87571482e-11 2.88444411e-02 -4.15701236e-04 1.67075675e-04 2.28277170e-04 + 1.20000000e+00 1.00003847e+00 -2.53092171e-12 3.04903169e-12 1.57835217e-02 -2.99657431e-04 1.11375229e-04 2.27970896e-04 + 1.30000000e+00 1.00000671e+00 -2.17180531e-11 -1.33762362e-11 2.72176286e-03 -1.83629043e-04 5.56725915e-05 2.27670202e-04 + 1.40000000e+00 9.99974973e-01 -3.29470903e-12 -9.13364516e-12 -1.03313424e-02 -6.58092897e-05 1.07729686e-06 2.27397406e-04 + 1.50000000e+00 9.99945501e-01 -7.93508160e-13 -1.14918618e-12 -2.24539501e-02 8.07056118e-05 -4.02609651e-05 2.23572178e-04 + 1.60000000e+00 9.99927143e-01 -3.98483937e-12 -1.51105147e-12 -3.00067482e-02 1.47913748e-04 -1.13631901e-04 1.99989858e-04 + 1.70000000e+00 9.99917616e-01 -1.87787986e-12 -5.99042565e-13 -3.39278827e-02 2.22520450e-04 -1.39430632e-04 1.55163727e-04 + 1.80000000e+00 9.99911911e-01 -1.47817292e-12 -7.46311024e-13 -3.62769724e-02 3.18296984e-04 -1.22673485e-04 9.62323332e-05 + 1.90000000e+00 9.99907999e-01 -1.63358154e-09 -4.62674802e-09 -3.78892128e-02 3.91963791e-04 -1.11381629e-04 3.66017964e-05 + 2.00000000e+00 9.99905052e-01 -8.33489083e-10 -2.29198409e-09 -3.91044775e-02 4.37450846e-04 -1.12843431e-04 -1.31006461e-05 + 2.10000000e+00 9.99902694e-01 -3.94685357e-10 -8.74196626e-10 -4.00771877e-02 4.65071108e-04 -1.26018456e-04 -5.48923789e-05 + 2.20000000e+00 9.99900714e-01 -4.30620543e-10 -3.86889200e-10 -4.08948120e-02 4.83446425e-04 -1.44000047e-04 -9.11311915e-05 + 2.30000000e+00 9.99898993e-01 -2.18316140e-10 -2.37409690e-10 -4.16057488e-02 4.96686238e-04 -1.63641758e-04 -1.22528476e-04 + 2.40000000e+00 9.99897463e-01 -9.43845537e-11 -1.39935502e-10 -4.22381772e-02 5.07378708e-04 -1.84134924e-04 -1.48784814e-04 + 2.50000000e+00 9.99896079e-01 -7.71060621e-11 -1.07323579e-10 -4.28103344e-02 5.16886679e-04 -2.04641577e-04 -1.69846131e-04 + 2.60000000e+00 9.99894813e-01 -7.47458244e-11 -8.57164787e-11 -4.33343861e-02 5.26562794e-04 -2.24164828e-04 -1.86972733e-04 + 2.70000000e+00 9.99893637e-01 -7.16206119e-11 -6.96342528e-11 -4.38211742e-02 5.36974412e-04 -2.41403351e-04 -2.00705756e-04 + 2.80000000e+00 9.99892533e-01 -7.09064073e-11 -4.31918938e-11 -4.42781246e-02 5.47504728e-04 -2.55862617e-04 -2.11618421e-04 + 2.90000000e+00 9.99891493e-01 -5.74230731e-11 -3.33644747e-11 -4.47089346e-02 5.57671699e-04 -2.68672255e-04 -2.20784575e-04 + 3.00000000e+00 9.99890509e-01 -4.35255535e-11 -3.71767298e-11 -4.51166735e-02 5.66915177e-04 -2.80129631e-04 -2.28781692e-04 + 3.10000000e+00 9.99922270e-01 -1.19206026e-10 -1.00092693e-10 -3.20566658e-02 4.51174039e-04 -2.24472228e-04 -2.28280928e-04 + 3.20000000e+00 9.99954031e-01 1.01676233e-11 3.93465146e-12 -1.89975817e-02 3.35422473e-04 -1.68805295e-04 -2.27772290e-04 + 3.30000000e+00 9.99985792e-01 1.75335318e-11 9.31929645e-12 -5.93933708e-03 2.19655882e-04 -1.13137605e-04 -2.27257302e-04 + 3.40000000e+00 1.00001755e+00 7.66786542e-16 -1.53851009e-15 7.11795717e-03 1.03868109e-04 -5.74954051e-05 -2.26725558e-04 + 3.50000000e+00 1.00004882e+00 -1.04865416e-10 1.08827098e-09 1.99704439e-02 -2.64306589e-05 -7.41331864e-06 -2.26642455e-04 + 3.60000000e+00 1.00007272e+00 -1.33397046e-12 -1.25913650e-12 2.97969124e-02 -1.27770309e-04 6.48925472e-05 -2.19134183e-04 + 3.70000000e+00 1.00008550e+00 1.50512508e-11 -3.07517309e-12 3.50451980e-02 -1.91179139e-04 1.46696918e-04 -1.91185679e-04 + 3.80000000e+00 1.00009275e+00 -9.11128697e-13 -5.57105812e-13 3.80241885e-02 -2.89075403e-04 1.63359772e-04 -1.39498433e-04 + 3.90000000e+00 1.00009756e+00 1.56417568e-09 4.17127576e-09 3.99979032e-02 -3.78169739e-04 1.60652856e-04 -7.77585775e-05 + 4.00000000e+00 1.00010108e+00 5.51096669e-10 2.03452527e-09 4.14412386e-02 -4.41410360e-04 1.64926262e-04 -1.92223235e-05 + 4.10000000e+00 1.00010383e+00 3.41770832e-10 3.99210182e-10 4.25703008e-02 -4.81441934e-04 1.75572351e-04 2.99899668e-05 + 4.20000000e+00 1.00010609e+00 2.08738683e-10 1.25804902e-10 4.34966632e-02 -5.08067833e-04 1.91683401e-04 7.12290912e-05 + 4.30000000e+00 1.00010803e+00 1.17075921e-10 2.86678222e-11 4.42886384e-02 -5.25565676e-04 2.09553689e-04 1.05541381e-04 + 4.40000000e+00 1.00010973e+00 2.14627482e-11 5.11147256e-11 4.49853741e-02 -5.37753424e-04 2.27830759e-04 1.34810377e-04 + 4.50000000e+00 1.00011125e+00 -1.21908708e-11 4.52104109e-11 4.56096435e-02 -5.47460295e-04 2.47230926e-04 1.59066574e-04 + 4.60000000e+00 1.00011264e+00 -2.52359223e-11 2.00410073e-11 4.61772309e-02 -5.57250814e-04 2.66004708e-04 1.78435689e-04 + 4.70000000e+00 1.00011392e+00 -3.32848998e-11 -2.18427072e-12 4.66990154e-02 -5.67694515e-04 2.83605105e-04 1.94534735e-04 + 4.80000000e+00 1.00011511e+00 -3.21395076e-11 -1.45728278e-11 4.71841740e-02 -5.78191903e-04 2.99004061e-04 2.07610057e-04 + 4.90000000e+00 1.00011622e+00 -2.73083334e-11 -2.05462711e-11 4.76400402e-02 -5.88039220e-04 3.11823411e-04 2.18272270e-04 + 5.00000000e+00 1.00011728e+00 -1.11036382e-11 -2.78753930e-11 4.80703509e-02 -5.97170508e-04 3.22726725e-04 2.27427684e-04 + 5.10000000e+00 1.00008552e+00 -8.63454978e-12 3.49744180e-11 3.50106415e-02 -4.81089400e-04 2.67021412e-04 2.27103410e-04 + 5.20000000e+00 1.00005376e+00 8.46880270e-12 1.40304867e-11 2.19502004e-02 -3.65027562e-04 2.11314920e-04 2.26788274e-04 + 5.30000000e+00 1.00002200e+00 -1.42283642e-11 -6.46197564e-12 8.88891948e-03 -2.48980876e-04 1.55607497e-04 2.26478685e-04 + 5.40000000e+00 9.99990237e-01 -2.85938484e-11 -1.92813804e-11 -4.17320099e-03 -1.32949309e-04 9.98995062e-05 2.26174866e-04 + 5.50000000e+00 9.99958547e-01 -7.45679488e-12 -2.19346349e-11 -1.72075666e-02 -1.33539189e-05 4.52922054e-05 2.25735507e-04 + 5.60000000e+00 9.99930428e-01 -3.29955698e-09 -6.94565696e-09 -2.87739102e-02 1.12064014e-04 -1.20827559e-05 2.26260975e-04 + 5.70000000e+00 9.99913569e-01 -6.48798623e-12 3.15182912e-13 -3.57104555e-02 1.65801282e-04 -1.22540730e-04 2.13326000e-04 + 5.80000000e+00 9.99904372e-01 -6.04699768e-13 -9.14345256e-13 -3.94960300e-02 2.56126139e-04 -1.84194137e-04 1.73952629e-04 + 5.90000000e+00 9.99898568e-01 -3.43844585e-09 -5.61203925e-09 -4.18861260e-02 3.54336244e-04 -1.96158501e-04 1.16678602e-04 + 6.00000000e+00 9.99894425e-01 -1.32711063e-09 -3.77357219e-09 -4.35931487e-02 4.32165227e-04 -2.05434302e-04 5.44556224e-05 + 6.10000000e+00 9.99891254e-01 -8.84851083e-10 -1.20129042e-09 -4.49006096e-02 4.86677874e-04 -2.16990011e-04 -1.94433500e-06 + 6.20000000e+00 9.99888700e-01 -5.24963416e-10 -5.76388458e-10 -4.59542217e-02 5.22631325e-04 -2.31504038e-04 -4.91330965e-05 + 6.30000000e+00 9.99886561e-01 -3.73173493e-10 -2.87656663e-10 -4.68367215e-02 5.46491823e-04 -2.48755246e-04 -8.83255605e-05 + 6.40000000e+00 9.99884709e-01 -1.57776076e-10 -1.97662961e-10 -4.76016155e-02 5.61939872e-04 -2.66596032e-04 -1.20984681e-04 + 6.50000000e+00 9.99883066e-01 -1.05981203e-10 -1.36703900e-10 -4.82806398e-02 5.73208540e-04 -2.84402424e-04 -1.48607418e-04 + 6.60000000e+00 9.99881584e-01 -8.16624273e-11 -8.39502673e-11 -4.88933770e-02 5.82631643e-04 -3.02459679e-04 -1.71343714e-04 + 6.70000000e+00 9.99880232e-01 -5.97617422e-11 -7.38938140e-11 -4.94526490e-02 5.92647806e-04 -3.19478626e-04 -1.89976719e-04 + 6.80000000e+00 9.99878986e-01 -5.24704831e-11 -6.39273052e-11 -4.99680310e-02 6.03322279e-04 -3.35390642e-04 -2.05731524e-04 + 6.90000000e+00 9.99877829e-01 -6.48406298e-11 -5.51119092e-11 -5.04472539e-02 6.13589003e-04 -3.49256452e-04 -2.18879658e-04 + 7.00000000e+00 9.99876744e-01 -5.83331756e-11 -5.05422353e-11 -5.08963753e-02 6.22809929e-04 -3.60512564e-04 -2.29699785e-04 diff --git a/test/data/test_results/voce_full_cyclic_csm/avg_stress_region_default_1.txt b/test/data/test_results/voce_full_cyclic_csm/avg_stress_region_default_1.txt new file mode 100644 index 0000000..f90e8bb --- /dev/null +++ b/test/data/test_results/voce_full_cyclic_csm/avg_stress_region_default_1.txt @@ -0,0 +1,71 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 1.00000000e-01 1.00003176e+00 9.33903989e-11 9.42336486e-11 1.30582253e-02 -1.15907460e-04 5.56804695e-05 4.14809553e-07 + 2.00000000e-01 1.00006344e+00 -7.74581662e-12 3.90245952e-11 2.60798923e-02 -2.36273896e-04 1.08910000e-04 8.37972147e-07 + 3.00000000e-01 1.00008460e+00 -3.11257918e-11 1.28903575e-09 3.47778684e-02 -3.34745439e-04 1.56686246e-04 2.36190385e-05 + 4.00000000e-01 1.00009167e+00 8.68957989e-12 1.03618195e-10 3.76799258e-02 -4.16242592e-04 1.45890474e-04 7.77951124e-05 + 5.00000000e-01 1.00009490e+00 -7.10611353e-14 2.46471546e-13 3.90056069e-02 -4.65962061e-04 1.40615860e-04 1.26981424e-04 + 6.00000000e-01 1.00009697e+00 -2.59613389e-14 -9.19692758e-13 3.98512843e-02 -4.87841564e-04 1.54308223e-04 1.63597322e-04 + 7.00000000e-01 1.00009853e+00 6.62270991e-10 7.27615715e-10 4.04924184e-02 -5.00438072e-04 1.74139191e-04 1.90224765e-04 + 8.00000000e-01 1.00009983e+00 3.56947297e-10 1.66158818e-10 4.10225166e-02 -5.10984722e-04 1.93496374e-04 2.08460293e-04 + 9.00000000e-01 1.00010096e+00 6.32378017e-11 3.70839668e-11 4.14856137e-02 -5.21751395e-04 2.09545084e-04 2.20468803e-04 + 1.00000000e+00 1.00010199e+00 -7.64273641e-12 -1.66180968e-11 4.19045693e-02 -5.31763514e-04 2.22762360e-04 2.28585517e-04 + 1.10000000e+00 1.00007023e+00 -4.72394826e-11 1.87571482e-11 2.88444411e-02 -4.15701236e-04 1.67075675e-04 2.28277170e-04 + 1.20000000e+00 1.00003847e+00 -2.53092171e-12 3.04903169e-12 1.57835217e-02 -2.99657431e-04 1.11375229e-04 2.27970896e-04 + 1.30000000e+00 1.00000671e+00 -2.17180531e-11 -1.33762362e-11 2.72176286e-03 -1.83629043e-04 5.56725915e-05 2.27670202e-04 + 1.40000000e+00 9.99974973e-01 -3.29470903e-12 -9.13364516e-12 -1.03313424e-02 -6.58092897e-05 1.07729686e-06 2.27397406e-04 + 1.50000000e+00 9.99945501e-01 -7.93508160e-13 -1.14918618e-12 -2.24539501e-02 8.07056118e-05 -4.02609651e-05 2.23572178e-04 + 1.60000000e+00 9.99927143e-01 -3.98483937e-12 -1.51105147e-12 -3.00067482e-02 1.47913748e-04 -1.13631901e-04 1.99989858e-04 + 1.70000000e+00 9.99917616e-01 -1.87787986e-12 -5.99042565e-13 -3.39278827e-02 2.22520450e-04 -1.39430632e-04 1.55163727e-04 + 1.80000000e+00 9.99911911e-01 -1.47817292e-12 -7.46311024e-13 -3.62769724e-02 3.18296984e-04 -1.22673485e-04 9.62323332e-05 + 1.90000000e+00 9.99907999e-01 -1.63358154e-09 -4.62674802e-09 -3.78892128e-02 3.91963791e-04 -1.11381629e-04 3.66017964e-05 + 2.00000000e+00 9.99905052e-01 -8.33489083e-10 -2.29198409e-09 -3.91044775e-02 4.37450846e-04 -1.12843431e-04 -1.31006461e-05 + 2.10000000e+00 9.99902694e-01 -3.94685357e-10 -8.74196626e-10 -4.00771877e-02 4.65071108e-04 -1.26018456e-04 -5.48923789e-05 + 2.20000000e+00 9.99900714e-01 -4.30620543e-10 -3.86889200e-10 -4.08948120e-02 4.83446425e-04 -1.44000047e-04 -9.11311915e-05 + 2.30000000e+00 9.99898993e-01 -2.18316140e-10 -2.37409690e-10 -4.16057488e-02 4.96686238e-04 -1.63641758e-04 -1.22528476e-04 + 2.40000000e+00 9.99897463e-01 -9.43845537e-11 -1.39935502e-10 -4.22381772e-02 5.07378708e-04 -1.84134924e-04 -1.48784814e-04 + 2.50000000e+00 9.99896079e-01 -7.71060621e-11 -1.07323579e-10 -4.28103344e-02 5.16886679e-04 -2.04641577e-04 -1.69846131e-04 + 2.60000000e+00 9.99894813e-01 -7.47458244e-11 -8.57164787e-11 -4.33343861e-02 5.26562794e-04 -2.24164828e-04 -1.86972733e-04 + 2.70000000e+00 9.99893637e-01 -7.16206119e-11 -6.96342528e-11 -4.38211742e-02 5.36974412e-04 -2.41403351e-04 -2.00705756e-04 + 2.80000000e+00 9.99892533e-01 -7.09064073e-11 -4.31918938e-11 -4.42781246e-02 5.47504728e-04 -2.55862617e-04 -2.11618421e-04 + 2.90000000e+00 9.99891493e-01 -5.74230731e-11 -3.33644747e-11 -4.47089346e-02 5.57671699e-04 -2.68672255e-04 -2.20784575e-04 + 3.00000000e+00 9.99890509e-01 -4.35255535e-11 -3.71767298e-11 -4.51166735e-02 5.66915177e-04 -2.80129631e-04 -2.28781692e-04 + 3.10000000e+00 9.99922270e-01 -1.19206026e-10 -1.00092693e-10 -3.20566658e-02 4.51174039e-04 -2.24472228e-04 -2.28280928e-04 + 3.20000000e+00 9.99954031e-01 1.01676233e-11 3.93465146e-12 -1.89975817e-02 3.35422473e-04 -1.68805295e-04 -2.27772290e-04 + 3.30000000e+00 9.99985792e-01 1.75335318e-11 9.31929645e-12 -5.93933708e-03 2.19655882e-04 -1.13137605e-04 -2.27257302e-04 + 3.40000000e+00 1.00001755e+00 7.66786542e-16 -1.53851009e-15 7.11795717e-03 1.03868109e-04 -5.74954051e-05 -2.26725558e-04 + 3.50000000e+00 1.00004882e+00 -1.04865416e-10 1.08827098e-09 1.99704439e-02 -2.64306589e-05 -7.41331864e-06 -2.26642455e-04 + 3.60000000e+00 1.00007272e+00 -1.33397046e-12 -1.25913650e-12 2.97969124e-02 -1.27770309e-04 6.48925472e-05 -2.19134183e-04 + 3.70000000e+00 1.00008550e+00 1.50512508e-11 -3.07517309e-12 3.50451980e-02 -1.91179139e-04 1.46696918e-04 -1.91185679e-04 + 3.80000000e+00 1.00009275e+00 -9.11128697e-13 -5.57105812e-13 3.80241885e-02 -2.89075403e-04 1.63359772e-04 -1.39498433e-04 + 3.90000000e+00 1.00009756e+00 1.56417568e-09 4.17127576e-09 3.99979032e-02 -3.78169739e-04 1.60652856e-04 -7.77585775e-05 + 4.00000000e+00 1.00010108e+00 5.51096669e-10 2.03452527e-09 4.14412386e-02 -4.41410360e-04 1.64926262e-04 -1.92223235e-05 + 4.10000000e+00 1.00010383e+00 3.41770832e-10 3.99210182e-10 4.25703008e-02 -4.81441934e-04 1.75572351e-04 2.99899668e-05 + 4.20000000e+00 1.00010609e+00 2.08738683e-10 1.25804902e-10 4.34966632e-02 -5.08067833e-04 1.91683401e-04 7.12290912e-05 + 4.30000000e+00 1.00010803e+00 1.17075921e-10 2.86678222e-11 4.42886384e-02 -5.25565676e-04 2.09553689e-04 1.05541381e-04 + 4.40000000e+00 1.00010973e+00 2.14627482e-11 5.11147256e-11 4.49853741e-02 -5.37753424e-04 2.27830759e-04 1.34810377e-04 + 4.50000000e+00 1.00011125e+00 -1.21908708e-11 4.52104109e-11 4.56096435e-02 -5.47460295e-04 2.47230926e-04 1.59066574e-04 + 4.60000000e+00 1.00011264e+00 -2.52359223e-11 2.00410073e-11 4.61772309e-02 -5.57250814e-04 2.66004708e-04 1.78435689e-04 + 4.70000000e+00 1.00011392e+00 -3.32848998e-11 -2.18427072e-12 4.66990154e-02 -5.67694515e-04 2.83605105e-04 1.94534735e-04 + 4.80000000e+00 1.00011511e+00 -3.21395076e-11 -1.45728278e-11 4.71841740e-02 -5.78191903e-04 2.99004061e-04 2.07610057e-04 + 4.90000000e+00 1.00011622e+00 -2.73083334e-11 -2.05462711e-11 4.76400402e-02 -5.88039220e-04 3.11823411e-04 2.18272270e-04 + 5.00000000e+00 1.00011728e+00 -1.11036382e-11 -2.78753930e-11 4.80703509e-02 -5.97170508e-04 3.22726725e-04 2.27427684e-04 + 5.10000000e+00 1.00008552e+00 -8.63454978e-12 3.49744180e-11 3.50106415e-02 -4.81089400e-04 2.67021412e-04 2.27103410e-04 + 5.20000000e+00 1.00005376e+00 8.46880270e-12 1.40304867e-11 2.19502004e-02 -3.65027562e-04 2.11314920e-04 2.26788274e-04 + 5.30000000e+00 1.00002200e+00 -1.42283642e-11 -6.46197564e-12 8.88891948e-03 -2.48980876e-04 1.55607497e-04 2.26478685e-04 + 5.40000000e+00 9.99990237e-01 -2.85938484e-11 -1.92813804e-11 -4.17320099e-03 -1.32949309e-04 9.98995062e-05 2.26174866e-04 + 5.50000000e+00 9.99958547e-01 -7.45679488e-12 -2.19346349e-11 -1.72075666e-02 -1.33539189e-05 4.52922054e-05 2.25735507e-04 + 5.60000000e+00 9.99930428e-01 -3.29955698e-09 -6.94565696e-09 -2.87739102e-02 1.12064014e-04 -1.20827559e-05 2.26260975e-04 + 5.70000000e+00 9.99913569e-01 -6.48798623e-12 3.15182912e-13 -3.57104555e-02 1.65801282e-04 -1.22540730e-04 2.13326000e-04 + 5.80000000e+00 9.99904372e-01 -6.04699768e-13 -9.14345256e-13 -3.94960300e-02 2.56126139e-04 -1.84194137e-04 1.73952629e-04 + 5.90000000e+00 9.99898568e-01 -3.43844585e-09 -5.61203925e-09 -4.18861260e-02 3.54336244e-04 -1.96158501e-04 1.16678602e-04 + 6.00000000e+00 9.99894425e-01 -1.32711063e-09 -3.77357219e-09 -4.35931487e-02 4.32165227e-04 -2.05434302e-04 5.44556224e-05 + 6.10000000e+00 9.99891254e-01 -8.84851083e-10 -1.20129042e-09 -4.49006096e-02 4.86677874e-04 -2.16990011e-04 -1.94433500e-06 + 6.20000000e+00 9.99888700e-01 -5.24963416e-10 -5.76388458e-10 -4.59542217e-02 5.22631325e-04 -2.31504038e-04 -4.91330965e-05 + 6.30000000e+00 9.99886561e-01 -3.73173493e-10 -2.87656663e-10 -4.68367215e-02 5.46491823e-04 -2.48755246e-04 -8.83255605e-05 + 6.40000000e+00 9.99884709e-01 -1.57776076e-10 -1.97662961e-10 -4.76016155e-02 5.61939872e-04 -2.66596032e-04 -1.20984681e-04 + 6.50000000e+00 9.99883066e-01 -1.05981203e-10 -1.36703900e-10 -4.82806398e-02 5.73208540e-04 -2.84402424e-04 -1.48607418e-04 + 6.60000000e+00 9.99881584e-01 -8.16624273e-11 -8.39502673e-11 -4.88933770e-02 5.82631643e-04 -3.02459679e-04 -1.71343714e-04 + 6.70000000e+00 9.99880232e-01 -5.97617422e-11 -7.38938140e-11 -4.94526490e-02 5.92647806e-04 -3.19478626e-04 -1.89976719e-04 + 6.80000000e+00 9.99878986e-01 -5.24704831e-11 -6.39273052e-11 -4.99680310e-02 6.03322279e-04 -3.35390642e-04 -2.05731524e-04 + 6.90000000e+00 9.99877829e-01 -6.48406298e-11 -5.51119092e-11 -5.04472539e-02 6.13589003e-04 -3.49256452e-04 -2.18879658e-04 + 7.00000000e+00 9.99876744e-01 -5.83331756e-11 -5.05422353e-11 -5.08963753e-02 6.22809929e-04 -3.60512564e-04 -2.29699785e-04 diff --git a/test/data/test_results/voce_nl_full/avg_stress_global.txt b/test/data/test_results/voce_nl_full/avg_stress_global.txt new file mode 100644 index 0000000..5463161 --- /dev/null +++ b/test/data/test_results/voce_nl_full/avg_stress_global.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.13447145e-14 1.14505311e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006342e+00 -1.74928218e-11 6.24861065e-11 2.60676157e-02 -2.37009587e-04 1.08522632e-04 8.92024216e-07 + 3.00000000e-01 1.00008461e+00 -3.09727587e-11 1.29966969e-09 3.47754323e-02 -3.34815155e-04 1.56617156e-04 2.36076465e-05 + 4.00000000e-01 1.00009168e+00 8.57270876e-12 1.05013190e-10 3.76782543e-02 -4.16276016e-04 1.45834708e-04 7.77594780e-05 + 5.00000000e-01 1.00009491e+00 -7.00004176e-14 2.52276353e-13 3.90039956e-02 -4.66010980e-04 1.40530555e-04 1.26938781e-04 + 6.00000000e-01 1.00009697e+00 -2.88174492e-14 -9.23411205e-13 3.98495687e-02 -4.87897510e-04 1.54198967e-04 1.63550487e-04 + 7.00000000e-01 1.00009854e+00 6.65644478e-10 7.26009066e-10 4.04905283e-02 -5.00492459e-04 1.74013242e-04 1.90179368e-04 + 8.00000000e-01 1.00009984e+00 3.56296974e-10 1.65071068e-10 4.10204085e-02 -5.11028641e-04 1.93364291e-04 2.08420875e-04 + 9.00000000e-01 1.00010097e+00 6.32124577e-11 3.65665883e-11 4.14832514e-02 -5.21784950e-04 2.09412163e-04 2.20434793e-04 + 1.00000000e+00 1.00010200e+00 -7.96336308e-12 -1.68663610e-11 4.19019180e-02 -5.31790954e-04 2.22628168e-04 2.28557252e-04 + 1.10000000e+00 1.00010295e+00 -9.91668275e-12 -3.48691366e-11 4.22907699e-02 -5.40632368e-04 2.33769766e-04 2.33635426e-04 + 1.20000000e+00 1.00010385e+00 -2.12315383e-11 -3.15473701e-11 4.26587542e-02 -5.48748436e-04 2.43502271e-04 2.36699117e-04 + 1.30000000e+00 1.00010472e+00 -1.86449600e-11 -2.55519646e-11 4.30116589e-02 -5.56254674e-04 2.52336992e-04 2.38959746e-04 + 1.40000000e+00 1.00010556e+00 -1.82025346e-11 -2.13713592e-11 4.33533077e-02 -5.63144606e-04 2.60516077e-04 2.41037649e-04 + 1.50000000e+00 1.00010637e+00 -1.65201490e-11 -1.64227860e-11 4.36862836e-02 -5.69502347e-04 2.68042011e-04 2.42972222e-04 + 1.60000000e+00 1.00010717e+00 -1.34293044e-11 -1.13378558e-11 4.40124606e-02 -5.75230332e-04 2.74952170e-04 2.44837341e-04 + 1.70000000e+00 1.00010796e+00 -1.16849044e-11 -9.38786221e-12 4.43332116e-02 -5.80306553e-04 2.81402612e-04 2.46797204e-04 + 1.80000000e+00 1.00010874e+00 -1.10081927e-11 -8.68622711e-12 4.46494223e-02 -5.84844926e-04 2.87502566e-04 2.48763570e-04 + 1.90000000e+00 1.00010951e+00 -9.91370776e-12 -7.78913954e-12 4.49617694e-02 -5.89028957e-04 2.93297267e-04 2.50667796e-04 + 2.00000000e+00 1.00011027e+00 -8.48756258e-12 -7.10846839e-12 4.52708278e-02 -5.93026040e-04 2.98776224e-04 2.52492950e-04 + 2.10000000e+00 1.00011102e+00 -7.67797605e-12 -6.95792050e-12 4.55770022e-02 -5.96910204e-04 3.03874004e-04 2.54283866e-04 + 2.30000000e+00 1.00011252e+00 -3.23786672e-11 -2.76769122e-11 4.61805777e-02 -6.04205302e-04 3.12747297e-04 2.57882441e-04 + 2.50000000e+00 1.00011399e+00 -3.54501248e-11 -3.79735085e-11 4.67762673e-02 -6.11176467e-04 3.21036980e-04 2.61074542e-04 + 2.70000000e+00 1.00011546e+00 -3.69091606e-11 -3.82229222e-11 4.73653081e-02 -6.18039619e-04 3.29092499e-04 2.63751210e-04 + 2.90000000e+00 1.00011691e+00 -3.37828123e-11 -3.33752878e-11 4.79488304e-02 -6.24868488e-04 3.36757790e-04 2.65950788e-04 + 3.10000000e+00 1.00011834e+00 -3.83944548e-11 -3.31008668e-11 4.85275129e-02 -6.31602653e-04 3.43894471e-04 2.67796086e-04 + 3.30000000e+00 1.00011977e+00 -3.83118754e-11 -2.94192617e-11 4.91018850e-02 -6.38160779e-04 3.50716892e-04 2.69457007e-04 + 3.70000000e+00 1.00012265e+00 -2.05810793e-10 -1.34378728e-10 5.02367220e-02 -6.50555827e-04 3.64082046e-04 2.72358372e-04 + 4.10000000e+00 1.00012550e+00 -1.52481723e-10 -9.16593530e-11 5.13595444e-02 -6.62034387e-04 3.76475508e-04 2.74824451e-04 + 4.50000000e+00 1.00012833e+00 -1.07011302e-10 -3.21753394e-11 5.24720005e-02 -6.72320889e-04 3.87711115e-04 2.76984252e-04 + 4.90000000e+00 1.00013113e+00 -8.65478920e-11 -1.10858841e-11 5.35752472e-02 -6.81421253e-04 3.98502524e-04 2.78587408e-04 + 5.10000000e+00 1.00013250e+00 -1.75025778e-11 -1.05664721e-11 5.41247476e-02 -6.85778061e-04 4.03837440e-04 2.79268190e-04 + 5.70000000e+00 1.00013673e+00 5.70486058e-13 8.48273296e-14 5.57565133e-02 -6.98396247e-04 4.19272921e-04 2.80938273e-04 + 6.20000000e+00 1.00014020e+00 -1.23120552e-10 -6.92990405e-11 5.71058151e-02 -7.08920735e-04 4.31052738e-04 2.82000462e-04 + 6.70000000e+00 1.00014365e+00 -1.03261629e-10 -4.80734908e-11 5.84453807e-02 -7.19283857e-04 4.41976040e-04 2.82917044e-04 + 7.45000000e+00 1.00014890e+00 3.22341225e-13 -6.95186664e-14 6.04342482e-02 -7.34841497e-04 4.57166612e-04 2.84811591e-04 + 8.20000000e+00 1.00015411e+00 3.48645001e-13 3.62246268e-13 6.24035785e-02 -7.50468358e-04 4.71149729e-04 2.87330170e-04 + 8.95000000e+00 1.00015927e+00 1.75630036e-13 3.95742319e-14 6.43543299e-02 -7.66173934e-04 4.85138255e-04 2.91061425e-04 + 9.70000000e+00 1.00016439e+00 -2.23770142e-10 -1.15171599e-10 6.62873955e-02 -7.81978408e-04 4.99387485e-04 2.95823481e-04 + 1.07000000e+01 1.00017133e+00 -4.12559767e-13 -9.19776795e-13 6.88347130e-02 -8.03113157e-04 5.18231937e-04 3.02891269e-04 diff --git a/test/data/test_results/voce_nl_full/avg_stress_region_default_1.txt b/test/data/test_results/voce_nl_full/avg_stress_region_default_1.txt new file mode 100644 index 0000000..5463161 --- /dev/null +++ b/test/data/test_results/voce_nl_full/avg_stress_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.13447145e-14 1.14505311e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204733e-08 + 2.00000000e-01 1.00006342e+00 -1.74928218e-11 6.24861065e-11 2.60676157e-02 -2.37009587e-04 1.08522632e-04 8.92024216e-07 + 3.00000000e-01 1.00008461e+00 -3.09727587e-11 1.29966969e-09 3.47754323e-02 -3.34815155e-04 1.56617156e-04 2.36076465e-05 + 4.00000000e-01 1.00009168e+00 8.57270876e-12 1.05013190e-10 3.76782543e-02 -4.16276016e-04 1.45834708e-04 7.77594780e-05 + 5.00000000e-01 1.00009491e+00 -7.00004176e-14 2.52276353e-13 3.90039956e-02 -4.66010980e-04 1.40530555e-04 1.26938781e-04 + 6.00000000e-01 1.00009697e+00 -2.88174492e-14 -9.23411205e-13 3.98495687e-02 -4.87897510e-04 1.54198967e-04 1.63550487e-04 + 7.00000000e-01 1.00009854e+00 6.65644478e-10 7.26009066e-10 4.04905283e-02 -5.00492459e-04 1.74013242e-04 1.90179368e-04 + 8.00000000e-01 1.00009984e+00 3.56296974e-10 1.65071068e-10 4.10204085e-02 -5.11028641e-04 1.93364291e-04 2.08420875e-04 + 9.00000000e-01 1.00010097e+00 6.32124577e-11 3.65665883e-11 4.14832514e-02 -5.21784950e-04 2.09412163e-04 2.20434793e-04 + 1.00000000e+00 1.00010200e+00 -7.96336308e-12 -1.68663610e-11 4.19019180e-02 -5.31790954e-04 2.22628168e-04 2.28557252e-04 + 1.10000000e+00 1.00010295e+00 -9.91668275e-12 -3.48691366e-11 4.22907699e-02 -5.40632368e-04 2.33769766e-04 2.33635426e-04 + 1.20000000e+00 1.00010385e+00 -2.12315383e-11 -3.15473701e-11 4.26587542e-02 -5.48748436e-04 2.43502271e-04 2.36699117e-04 + 1.30000000e+00 1.00010472e+00 -1.86449600e-11 -2.55519646e-11 4.30116589e-02 -5.56254674e-04 2.52336992e-04 2.38959746e-04 + 1.40000000e+00 1.00010556e+00 -1.82025346e-11 -2.13713592e-11 4.33533077e-02 -5.63144606e-04 2.60516077e-04 2.41037649e-04 + 1.50000000e+00 1.00010637e+00 -1.65201490e-11 -1.64227860e-11 4.36862836e-02 -5.69502347e-04 2.68042011e-04 2.42972222e-04 + 1.60000000e+00 1.00010717e+00 -1.34293044e-11 -1.13378558e-11 4.40124606e-02 -5.75230332e-04 2.74952170e-04 2.44837341e-04 + 1.70000000e+00 1.00010796e+00 -1.16849044e-11 -9.38786221e-12 4.43332116e-02 -5.80306553e-04 2.81402612e-04 2.46797204e-04 + 1.80000000e+00 1.00010874e+00 -1.10081927e-11 -8.68622711e-12 4.46494223e-02 -5.84844926e-04 2.87502566e-04 2.48763570e-04 + 1.90000000e+00 1.00010951e+00 -9.91370776e-12 -7.78913954e-12 4.49617694e-02 -5.89028957e-04 2.93297267e-04 2.50667796e-04 + 2.00000000e+00 1.00011027e+00 -8.48756258e-12 -7.10846839e-12 4.52708278e-02 -5.93026040e-04 2.98776224e-04 2.52492950e-04 + 2.10000000e+00 1.00011102e+00 -7.67797605e-12 -6.95792050e-12 4.55770022e-02 -5.96910204e-04 3.03874004e-04 2.54283866e-04 + 2.30000000e+00 1.00011252e+00 -3.23786672e-11 -2.76769122e-11 4.61805777e-02 -6.04205302e-04 3.12747297e-04 2.57882441e-04 + 2.50000000e+00 1.00011399e+00 -3.54501248e-11 -3.79735085e-11 4.67762673e-02 -6.11176467e-04 3.21036980e-04 2.61074542e-04 + 2.70000000e+00 1.00011546e+00 -3.69091606e-11 -3.82229222e-11 4.73653081e-02 -6.18039619e-04 3.29092499e-04 2.63751210e-04 + 2.90000000e+00 1.00011691e+00 -3.37828123e-11 -3.33752878e-11 4.79488304e-02 -6.24868488e-04 3.36757790e-04 2.65950788e-04 + 3.10000000e+00 1.00011834e+00 -3.83944548e-11 -3.31008668e-11 4.85275129e-02 -6.31602653e-04 3.43894471e-04 2.67796086e-04 + 3.30000000e+00 1.00011977e+00 -3.83118754e-11 -2.94192617e-11 4.91018850e-02 -6.38160779e-04 3.50716892e-04 2.69457007e-04 + 3.70000000e+00 1.00012265e+00 -2.05810793e-10 -1.34378728e-10 5.02367220e-02 -6.50555827e-04 3.64082046e-04 2.72358372e-04 + 4.10000000e+00 1.00012550e+00 -1.52481723e-10 -9.16593530e-11 5.13595444e-02 -6.62034387e-04 3.76475508e-04 2.74824451e-04 + 4.50000000e+00 1.00012833e+00 -1.07011302e-10 -3.21753394e-11 5.24720005e-02 -6.72320889e-04 3.87711115e-04 2.76984252e-04 + 4.90000000e+00 1.00013113e+00 -8.65478920e-11 -1.10858841e-11 5.35752472e-02 -6.81421253e-04 3.98502524e-04 2.78587408e-04 + 5.10000000e+00 1.00013250e+00 -1.75025778e-11 -1.05664721e-11 5.41247476e-02 -6.85778061e-04 4.03837440e-04 2.79268190e-04 + 5.70000000e+00 1.00013673e+00 5.70486058e-13 8.48273296e-14 5.57565133e-02 -6.98396247e-04 4.19272921e-04 2.80938273e-04 + 6.20000000e+00 1.00014020e+00 -1.23120552e-10 -6.92990405e-11 5.71058151e-02 -7.08920735e-04 4.31052738e-04 2.82000462e-04 + 6.70000000e+00 1.00014365e+00 -1.03261629e-10 -4.80734908e-11 5.84453807e-02 -7.19283857e-04 4.41976040e-04 2.82917044e-04 + 7.45000000e+00 1.00014890e+00 3.22341225e-13 -6.95186664e-14 6.04342482e-02 -7.34841497e-04 4.57166612e-04 2.84811591e-04 + 8.20000000e+00 1.00015411e+00 3.48645001e-13 3.62246268e-13 6.24035785e-02 -7.50468358e-04 4.71149729e-04 2.87330170e-04 + 8.95000000e+00 1.00015927e+00 1.75630036e-13 3.95742319e-14 6.43543299e-02 -7.66173934e-04 4.85138255e-04 2.91061425e-04 + 9.70000000e+00 1.00016439e+00 -2.23770142e-10 -1.15171599e-10 6.62873955e-02 -7.81978408e-04 4.99387485e-04 2.95823481e-04 + 1.07000000e+01 1.00017133e+00 -4.12559767e-13 -9.19776795e-13 6.88347130e-02 -8.03113157e-04 5.18231937e-04 3.02891269e-04 diff --git a/test/data/test_results/voce_pa/avg_stress_global.txt b/test/data/test_results/voce_pa/avg_stress_global.txt new file mode 100644 index 0000000..40bad6a --- /dev/null +++ b/test/data/test_results/voce_pa/avg_stress_global.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.15380694e-14 1.18287025e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204734e-08 + 2.00000000e-01 1.00006342e+00 -1.76191834e-11 6.23787327e-11 2.60676157e-02 -2.37009587e-04 1.08522632e-04 8.92024202e-07 + 3.00000000e-01 1.00008461e+00 -3.06951226e-11 1.29946554e-09 3.47754323e-02 -3.34815155e-04 1.56617156e-04 2.36076465e-05 + 4.00000000e-01 1.00009168e+00 8.61236084e-12 1.04996221e-10 3.76782543e-02 -4.16276016e-04 1.45834708e-04 7.77594780e-05 + 5.00000000e-01 1.00009491e+00 -6.91412034e-14 2.51005464e-13 3.90039956e-02 -4.66010980e-04 1.40530555e-04 1.26938781e-04 + 6.00000000e-01 1.00009697e+00 -2.75322014e-14 -9.25781529e-13 3.98495687e-02 -4.87897510e-04 1.54198967e-04 1.63550487e-04 + 7.00000000e-01 1.00009854e+00 6.65551096e-10 7.25781517e-10 4.04905283e-02 -5.00492459e-04 1.74013241e-04 1.90179368e-04 + 8.00000000e-01 1.00009984e+00 3.56314525e-10 1.65203263e-10 4.10204085e-02 -5.11028641e-04 1.93364291e-04 2.08420875e-04 + 9.00000000e-01 1.00010097e+00 6.33339311e-11 3.66425315e-11 4.14832514e-02 -5.21784950e-04 2.09412163e-04 2.20434793e-04 + 1.00000000e+00 1.00010200e+00 -7.95172583e-12 -1.69263818e-11 4.19019180e-02 -5.31790954e-04 2.22628168e-04 2.28557252e-04 + 1.10000000e+00 1.00010295e+00 -9.85870252e-12 -3.48574897e-11 4.22907699e-02 -5.40632368e-04 2.33769766e-04 2.33635426e-04 + 1.20000000e+00 1.00010385e+00 -2.12365364e-11 -3.15785112e-11 4.26587542e-02 -5.48748436e-04 2.43502271e-04 2.36699117e-04 + 1.30000000e+00 1.00010472e+00 -1.86291055e-11 -2.55408972e-11 4.30116589e-02 -5.56254674e-04 2.52336992e-04 2.38959746e-04 + 1.40000000e+00 1.00010556e+00 -1.82067329e-11 -2.13789887e-11 4.33533077e-02 -5.63144606e-04 2.60516077e-04 2.41037649e-04 + 1.50000000e+00 1.00010637e+00 -1.65151350e-11 -1.64227947e-11 4.36862836e-02 -5.69502347e-04 2.68042011e-04 2.42972222e-04 + 1.60000000e+00 1.00010717e+00 -1.34241792e-11 -1.13356021e-11 4.40124606e-02 -5.75230332e-04 2.74952170e-04 2.44837341e-04 + 1.70000000e+00 1.00010796e+00 -1.16797625e-11 -9.38490190e-12 4.43332116e-02 -5.80306553e-04 2.81402612e-04 2.46797204e-04 + 1.80000000e+00 1.00010874e+00 -1.10042717e-11 -8.68546131e-12 4.46494223e-02 -5.84844926e-04 2.87502566e-04 2.48763570e-04 + 1.90000000e+00 1.00010951e+00 -9.90974421e-12 -7.78745824e-12 4.49617694e-02 -5.89028957e-04 2.93297267e-04 2.50667796e-04 + 2.00000000e+00 1.00011027e+00 -8.48205250e-12 -7.10308874e-12 4.52708278e-02 -5.93026040e-04 2.98776224e-04 2.52492950e-04 + 2.10000000e+00 1.00011102e+00 -7.67077835e-12 -6.95203036e-12 4.55770022e-02 -5.96910204e-04 3.03874004e-04 2.54283866e-04 + 2.30000000e+00 1.00011252e+00 -3.23781554e-11 -2.76761223e-11 4.61805777e-02 -6.04205302e-04 3.12747297e-04 2.57882441e-04 + 2.50000000e+00 1.00011399e+00 -3.54190877e-11 -3.79565554e-11 4.67762673e-02 -6.11176467e-04 3.21036980e-04 2.61074542e-04 + 2.70000000e+00 1.00011546e+00 -3.68970017e-11 -3.82196349e-11 4.73653081e-02 -6.18039619e-04 3.29092499e-04 2.63751210e-04 + 2.90000000e+00 1.00011691e+00 -3.37789273e-11 -3.33732079e-11 4.79488304e-02 -6.24868488e-04 3.36757790e-04 2.65950788e-04 + 3.10000000e+00 1.00011834e+00 -3.83915734e-11 -3.31003162e-11 4.85275129e-02 -6.31602653e-04 3.43894471e-04 2.67796086e-04 + 3.30000000e+00 1.00011977e+00 -3.83191244e-11 -2.94261226e-11 4.91018850e-02 -6.38160779e-04 3.50716892e-04 2.69457007e-04 + 3.70000000e+00 1.00012265e+00 -2.05841421e-10 -1.34424532e-10 5.02367220e-02 -6.50555827e-04 3.64082046e-04 2.72358372e-04 + 4.10000000e+00 1.00012550e+00 -1.52525925e-10 -9.17029224e-11 5.13595444e-02 -6.62034387e-04 3.76475508e-04 2.74824451e-04 + 4.50000000e+00 1.00012833e+00 -1.07025049e-10 -3.21800758e-11 5.24720005e-02 -6.72320889e-04 3.87711115e-04 2.76984252e-04 + 4.90000000e+00 1.00013113e+00 -8.65674840e-11 -1.11114546e-11 5.35752472e-02 -6.81421253e-04 3.98502524e-04 2.78587408e-04 + 5.10000000e+00 1.00013250e+00 -1.75038356e-11 -1.05667127e-11 5.41247476e-02 -6.85778061e-04 4.03837441e-04 2.79268190e-04 + 5.70000000e+00 1.00013673e+00 5.70171175e-13 8.44668816e-14 5.57565133e-02 -6.98396247e-04 4.19272921e-04 2.80938273e-04 + 6.20000000e+00 1.00014020e+00 -1.23139199e-10 -6.93074918e-11 5.71058151e-02 -7.08920735e-04 4.31052738e-04 2.82000462e-04 + 6.70000000e+00 1.00014365e+00 -1.03263838e-10 -4.80713937e-11 5.84453807e-02 -7.19283857e-04 4.41976040e-04 2.82917044e-04 + 7.45000000e+00 1.00014890e+00 3.22280395e-13 -6.96086418e-14 6.04342482e-02 -7.34841497e-04 4.57166612e-04 2.84811591e-04 + 8.20000000e+00 1.00015411e+00 3.48421691e-13 3.62176781e-13 6.24035785e-02 -7.50468358e-04 4.71149729e-04 2.87330170e-04 + 8.95000000e+00 1.00015927e+00 1.75452253e-13 3.91954720e-14 6.43543299e-02 -7.66173934e-04 4.85138255e-04 2.91061425e-04 + 9.70000000e+00 1.00016439e+00 -2.23758329e-10 -1.15124011e-10 6.62873955e-02 -7.81978409e-04 4.99387485e-04 2.95823481e-04 + 1.07000000e+01 1.00017133e+00 -4.12736397e-13 -9.19926064e-13 6.88347130e-02 -8.03113157e-04 5.18231937e-04 3.02891269e-04 diff --git a/test/data/test_results/voce_pa/avg_stress_region_default_1.txt b/test/data/test_results/voce_pa/avg_stress_region_default_1.txt new file mode 100644 index 0000000..40bad6a --- /dev/null +++ b/test/data/test_results/voce_pa/avg_stress_region_default_1.txt @@ -0,0 +1,41 @@ + # Time Volume Sxx Syy Szz Sxy Sxz Syz + 5.00000000e-03 1.00000159e+00 1.15380694e-14 1.18287025e-14 6.52989900e-04 -5.79514196e-06 2.78435425e-06 2.06204734e-08 + 2.00000000e-01 1.00006342e+00 -1.76191834e-11 6.23787327e-11 2.60676157e-02 -2.37009587e-04 1.08522632e-04 8.92024202e-07 + 3.00000000e-01 1.00008461e+00 -3.06951226e-11 1.29946554e-09 3.47754323e-02 -3.34815155e-04 1.56617156e-04 2.36076465e-05 + 4.00000000e-01 1.00009168e+00 8.61236084e-12 1.04996221e-10 3.76782543e-02 -4.16276016e-04 1.45834708e-04 7.77594780e-05 + 5.00000000e-01 1.00009491e+00 -6.91412034e-14 2.51005464e-13 3.90039956e-02 -4.66010980e-04 1.40530555e-04 1.26938781e-04 + 6.00000000e-01 1.00009697e+00 -2.75322014e-14 -9.25781529e-13 3.98495687e-02 -4.87897510e-04 1.54198967e-04 1.63550487e-04 + 7.00000000e-01 1.00009854e+00 6.65551096e-10 7.25781517e-10 4.04905283e-02 -5.00492459e-04 1.74013241e-04 1.90179368e-04 + 8.00000000e-01 1.00009984e+00 3.56314525e-10 1.65203263e-10 4.10204085e-02 -5.11028641e-04 1.93364291e-04 2.08420875e-04 + 9.00000000e-01 1.00010097e+00 6.33339311e-11 3.66425315e-11 4.14832514e-02 -5.21784950e-04 2.09412163e-04 2.20434793e-04 + 1.00000000e+00 1.00010200e+00 -7.95172583e-12 -1.69263818e-11 4.19019180e-02 -5.31790954e-04 2.22628168e-04 2.28557252e-04 + 1.10000000e+00 1.00010295e+00 -9.85870252e-12 -3.48574897e-11 4.22907699e-02 -5.40632368e-04 2.33769766e-04 2.33635426e-04 + 1.20000000e+00 1.00010385e+00 -2.12365364e-11 -3.15785112e-11 4.26587542e-02 -5.48748436e-04 2.43502271e-04 2.36699117e-04 + 1.30000000e+00 1.00010472e+00 -1.86291055e-11 -2.55408972e-11 4.30116589e-02 -5.56254674e-04 2.52336992e-04 2.38959746e-04 + 1.40000000e+00 1.00010556e+00 -1.82067329e-11 -2.13789887e-11 4.33533077e-02 -5.63144606e-04 2.60516077e-04 2.41037649e-04 + 1.50000000e+00 1.00010637e+00 -1.65151350e-11 -1.64227947e-11 4.36862836e-02 -5.69502347e-04 2.68042011e-04 2.42972222e-04 + 1.60000000e+00 1.00010717e+00 -1.34241792e-11 -1.13356021e-11 4.40124606e-02 -5.75230332e-04 2.74952170e-04 2.44837341e-04 + 1.70000000e+00 1.00010796e+00 -1.16797625e-11 -9.38490190e-12 4.43332116e-02 -5.80306553e-04 2.81402612e-04 2.46797204e-04 + 1.80000000e+00 1.00010874e+00 -1.10042717e-11 -8.68546131e-12 4.46494223e-02 -5.84844926e-04 2.87502566e-04 2.48763570e-04 + 1.90000000e+00 1.00010951e+00 -9.90974421e-12 -7.78745824e-12 4.49617694e-02 -5.89028957e-04 2.93297267e-04 2.50667796e-04 + 2.00000000e+00 1.00011027e+00 -8.48205250e-12 -7.10308874e-12 4.52708278e-02 -5.93026040e-04 2.98776224e-04 2.52492950e-04 + 2.10000000e+00 1.00011102e+00 -7.67077835e-12 -6.95203036e-12 4.55770022e-02 -5.96910204e-04 3.03874004e-04 2.54283866e-04 + 2.30000000e+00 1.00011252e+00 -3.23781554e-11 -2.76761223e-11 4.61805777e-02 -6.04205302e-04 3.12747297e-04 2.57882441e-04 + 2.50000000e+00 1.00011399e+00 -3.54190877e-11 -3.79565554e-11 4.67762673e-02 -6.11176467e-04 3.21036980e-04 2.61074542e-04 + 2.70000000e+00 1.00011546e+00 -3.68970017e-11 -3.82196349e-11 4.73653081e-02 -6.18039619e-04 3.29092499e-04 2.63751210e-04 + 2.90000000e+00 1.00011691e+00 -3.37789273e-11 -3.33732079e-11 4.79488304e-02 -6.24868488e-04 3.36757790e-04 2.65950788e-04 + 3.10000000e+00 1.00011834e+00 -3.83915734e-11 -3.31003162e-11 4.85275129e-02 -6.31602653e-04 3.43894471e-04 2.67796086e-04 + 3.30000000e+00 1.00011977e+00 -3.83191244e-11 -2.94261226e-11 4.91018850e-02 -6.38160779e-04 3.50716892e-04 2.69457007e-04 + 3.70000000e+00 1.00012265e+00 -2.05841421e-10 -1.34424532e-10 5.02367220e-02 -6.50555827e-04 3.64082046e-04 2.72358372e-04 + 4.10000000e+00 1.00012550e+00 -1.52525925e-10 -9.17029224e-11 5.13595444e-02 -6.62034387e-04 3.76475508e-04 2.74824451e-04 + 4.50000000e+00 1.00012833e+00 -1.07025049e-10 -3.21800758e-11 5.24720005e-02 -6.72320889e-04 3.87711115e-04 2.76984252e-04 + 4.90000000e+00 1.00013113e+00 -8.65674840e-11 -1.11114546e-11 5.35752472e-02 -6.81421253e-04 3.98502524e-04 2.78587408e-04 + 5.10000000e+00 1.00013250e+00 -1.75038356e-11 -1.05667127e-11 5.41247476e-02 -6.85778061e-04 4.03837441e-04 2.79268190e-04 + 5.70000000e+00 1.00013673e+00 5.70171175e-13 8.44668816e-14 5.57565133e-02 -6.98396247e-04 4.19272921e-04 2.80938273e-04 + 6.20000000e+00 1.00014020e+00 -1.23139199e-10 -6.93074918e-11 5.71058151e-02 -7.08920735e-04 4.31052738e-04 2.82000462e-04 + 6.70000000e+00 1.00014365e+00 -1.03263838e-10 -4.80713937e-11 5.84453807e-02 -7.19283857e-04 4.41976040e-04 2.82917044e-04 + 7.45000000e+00 1.00014890e+00 3.22280395e-13 -6.96086418e-14 6.04342482e-02 -7.34841497e-04 4.57166612e-04 2.84811591e-04 + 8.20000000e+00 1.00015411e+00 3.48421691e-13 3.62176781e-13 6.24035785e-02 -7.50468358e-04 4.71149729e-04 2.87330170e-04 + 8.95000000e+00 1.00015927e+00 1.75452253e-13 3.91954720e-14 6.43543299e-02 -7.66173934e-04 4.85138255e-04 2.91061425e-04 + 9.70000000e+00 1.00016439e+00 -2.23758329e-10 -1.15124011e-10 6.62873955e-02 -7.81978409e-04 4.99387485e-04 2.95823481e-04 + 1.07000000e+01 1.00017133e+00 -4.12736397e-13 -9.19926064e-13 6.88347130e-02 -8.03113157e-04 5.18231937e-04 3.02891269e-04 diff --git a/test/data/voce_bcc.toml b/test/data/voce_bcc.toml index 3bb14c6..4b8dba4 100644 --- a/test/data/voce_bcc.toml +++ b/test/data/voce_bcc.toml @@ -85,8 +85,6 @@ Version = "0.6.0" visit = false conduit = false paraview = false - floc = "./exaconstit_p1" - avg_stress_fname = "test_voce_bcc_stress.txt" [Solvers] # Option for how our assembly operation is conducted. Possible choices are # FULL, PA, EA diff --git a/test/data/voce_bcc_stress.txt b/test/data/voce_bcc_stress.txt deleted file mode 100644 index 1626210..0000000 --- a/test/data/voce_bcc_stress.txt +++ /dev/null @@ -1,40 +0,0 @@ -1.1323e-14 1.14918e-14 0.00065299 -5.79514e-06 2.78435e-06 2.06205e-08 --1.7475e-11 6.25469e-11 0.0260676 -0.00023701 0.000108522 8.91346e-07 --3.0758e-11 1.30166e-09 0.034775 -0.000334829 0.000156659 2.36178e-05 -8.5573e-12 1.04717e-10 0.0376768 -0.000416274 0.00014589 7.78692e-05 --7.64174e-14 2.44724e-13 0.0390019 -0.000466017 0.000140517 0.000127251 --3.12735e-14 -9.2439e-13 0.039847 -0.000487955 0.000154087 0.000164111 -6.64685e-10 7.30534e-10 0.0404876 -0.000500603 0.000173817 0.000190998 -3.54283e-10 1.67524e-10 0.0410171 -0.000511151 0.000193097 0.000209487 -6.30181e-11 3.61063e-11 0.0414797 -0.000521931 0.000209101 0.000221741 --8.51337e-12 -1.61642e-11 0.0418981 -0.000531994 0.000222285 0.000230101 --1.01358e-11 -3.45508e-11 0.0422867 -0.000540915 0.000233391 0.000235435 --2.10806e-11 -3.20756e-11 0.0426543 -0.000549116 0.000243083 0.000238742 --1.87458e-11 -2.55895e-11 0.0430069 -0.000556711 0.000251892 0.000241242 --1.8024e-11 -2.11315e-11 0.0433483 -0.000563693 0.000260082 0.00024356 --1.66302e-11 -1.62951e-11 0.043681 -0.000570158 0.000267656 0.000245722 --1.37824e-11 -1.14015e-11 0.0440069 -0.000576001 0.000274635 0.000247813 --1.21031e-11 -9.54101e-12 0.0443273 -0.000581209 0.000281168 0.000250014 --1.13406e-11 -8.85745e-12 0.0446432 -0.000585899 0.000287342 0.000252261 --1.01697e-11 -7.82164e-12 0.0449552 -0.000590264 0.000293219 0.000254447 --8.62578e-12 -7.00567e-12 0.0452639 -0.000594444 0.000298796 0.000256532 --7.60973e-12 -6.70321e-12 0.0455698 -0.000598488 0.000303998 0.00025858 --3.34582e-11 -2.66772e-11 0.0461727 -0.000606079 0.000313112 0.000262714 --3.55657e-11 -3.89087e-11 0.0467677 -0.000613407 0.000321714 0.000266485 --3.85633e-11 -3.83559e-11 0.047356 -0.0006207 0.000330096 0.00026977 --3.32334e-11 -3.26595e-11 0.0479387 -0.000627979 0.000338036 0.000272579 --3.87355e-11 -3.29285e-11 0.0485166 -0.000635193 0.000345498 0.000275016 --3.77302e-11 -2.96341e-11 0.0490902 -0.00064229 0.000352716 0.000277277 --2.23416e-10 -1.14103e-10 0.0502235 -0.000655589 0.000366722 0.000281347 --1.75807e-10 -7.74924e-11 0.0513446 -0.000667752 0.000379728 0.000285056 --1.30843e-10 2.06223e-11 0.0524551 -0.000678677 0.000391621 0.000288427 --1.00747e-10 1.99093e-11 0.0535562 -0.000688385 0.000403177 0.000291181 --1.728e-11 -1.07978e-11 0.0541046 -0.000693015 0.000408895 0.000292464 -6.94608e-13 2.60208e-13 0.0557328 -0.00070664 0.000425575 0.00029584 --1.18361e-10 -5.38669e-11 0.0570789 -0.000718246 0.000438723 0.000298661 --8.97142e-11 -1.84655e-11 0.0584153 -0.000729844 0.000451257 0.000301495 -3.63043e-13 5.78173e-15 0.0603994 -0.000747135 0.000469004 0.000306132 -1.83321e-13 3.05245e-13 0.0623637 -0.000764533 0.000485636 0.000311902 -2.2937e-13 -1.33238e-15 0.0643091 -0.000782262 0.000501415 0.000318954 --6.29697e-13 -3.33718e-13 0.0662364 -0.000800212 0.000516787 0.000327119 --2.61068e-12 -1.61467e-12 0.0687754 -0.000824435 0.000536686 0.000339412 diff --git a/test/data/voce_ea.toml b/test/data/voce_ea.toml index 75a74e9..c1161f4 100644 --- a/test/data/voce_ea.toml +++ b/test/data/voce_ea.toml @@ -85,21 +85,7 @@ Version = "0.6.0" visit = false conduit = false paraview = false - floc = "./exaconstit_p1" - avg_stress_fname = "test_voce_ea_stress.txt" - # Optional - additional volume averages or body values are calculated - # these values include the average deformation gradient and if a - # ExaCMech model is being used the plastic work is also calculated - # Default value is set to false additional_avgs = true - # Optional - the file name for our average deformation gradient file - avg_def_grad_fname = "test_voce_ea_def_grad.txt" - # Optional - the file name for our plastic work file - avg_pl_work_fname = "test_voce_ea_pl_work.txt" - # Optional - the file name for our average eulerian strain file - avg_euler_strain_fname = "test_voce_ea_euler_strain.txt" - # Optional - the file name for our average equivalent plastic strain file - avg_eps_fname = "test_voce_ea_eps.txt" [Solvers] # Option for how our assembly operation is conducted. Possible choices are # FULL, PA, EA diff --git a/test/data/voce_ea_cs.toml b/test/data/voce_ea_cs.toml index 76a5f80..c32c196 100644 --- a/test/data/voce_ea_cs.toml +++ b/test/data/voce_ea_cs.toml @@ -93,13 +93,7 @@ Version = "0.6.0" visit = false conduit = false paraview = false - floc = "./exaconstit_p1" - avg_stress_fname = "test_voce_ea_cs_stress.txt" additional_avgs = true - avg_def_grad_fname = "test_voce_ea_cs_def_grad.txt" - avg_pl_work_fname = "test_voce_ea_cs_pl_work.txt" - avg_euler_strain_fname = "test_voce_ea_cs_euler_strain.txt" - avg_eps_fname = "test_voce_ea_cs_eps.txt" [Solvers] # Option for how our assembly operation is conducted. Possible choices are # FULL, PA, EA diff --git a/test/data/voce_ea_cs_def_grad.txt b/test/data/voce_ea_cs_def_grad.txt deleted file mode 100644 index 1fcab95..0000000 --- a/test/data/voce_ea_cs_def_grad.txt +++ /dev/null @@ -1,40 +0,0 @@ -0.999998 -1.88827e-07 -7.53881e-08 1.7402e-07 0.999998 -3.51739e-08 -1.22637e-07 3.73904e-08 1.00001 -0.999935 -7.57048e-06 -3.16406e-06 7.03431e-06 0.999929 -1.40828e-06 -4.80523e-06 1.41615e-06 1.0002 -0.999898 -1.41765e-05 -7.31795e-06 1.32328e-05 0.999886 -1.65819e-06 -4.96308e-06 4.44349e-07 1.0003 -0.999857 -2.40361e-05 -1.27318e-05 2.15991e-05 0.999835 -3.88561e-07 -4.38541e-06 -3.75105e-06 1.0004 -0.999815 -3.5578e-05 -1.83146e-05 3.10797e-05 0.99978 2.31873e-06 -2.1601e-06 -8.64716e-06 1.0005 -0.999772 -4.88717e-05 -2.31888e-05 4.20169e-05 0.999725 4.22368e-06 3.24381e-07 -1.17719e-05 1.0006 -0.99973 -6.34721e-05 -2.74374e-05 5.42097e-05 0.999669 5.34827e-06 2.5287e-06 -1.36272e-05 1.0007 -0.999687 -7.8911e-05 -3.12767e-05 6.74299e-05 0.999613 5.94791e-06 4.40666e-06 -1.46601e-05 1.0008 -0.999645 -9.48942e-05 -3.48189e-05 8.14669e-05 0.999556 6.17428e-06 6.0538e-06 -1.51381e-05 1.0009 -0.999602 -0.000111269 -3.8136e-05 9.61243e-05 0.9995 6.1266e-06 7.55624e-06 -1.52073e-05 1.001 -0.99956 -0.000127892 -4.12138e-05 0.00011124 0.999444 5.82658e-06 8.90424e-06 -1.49127e-05 1.0011 -0.999517 -0.00014468 -4.40354e-05 0.000126681 0.999387 5.33614e-06 1.00578e-05 -1.43631e-05 1.0012 -0.999474 -0.000161593 -4.66186e-05 0.000142306 0.999331 4.70852e-06 1.10293e-05 -1.36426e-05 1.0013 -0.999432 -0.000178605 -4.89864e-05 0.000158023 0.999274 3.97982e-06 1.18307e-05 -1.27946e-05 1.0014 -0.999389 -0.000195694 -5.11455e-05 0.000173799 0.999218 3.19205e-06 1.24546e-05 -1.18647e-05 1.0015 -0.999347 -0.000212841 -5.31127e-05 0.000189619 0.999161 2.37754e-06 1.2907e-05 -1.08864e-05 1.0016 -0.999304 -0.000230035 -5.49217e-05 0.000205464 0.999105 1.54763e-06 1.32135e-05 -9.87293e-06 1.0017 -0.999261 -0.000247242 -5.66113e-05 0.000221297 0.999048 7.08028e-07 1.34128e-05 -8.83345e-06 1.0018 -0.999219 -0.000264437 -5.81994e-05 0.000237083 0.998992 -1.38489e-07 1.35247e-05 -7.76995e-06 1.0019 -0.999176 -0.000281634 -5.96862e-05 0.000252813 0.998935 -9.92954e-07 1.35463e-05 -6.68024e-06 1.002 -0.999134 -0.000298838 -6.10728e-05 0.000268492 0.998879 -1.8552e-06 1.34744e-05 -5.56515e-06 1.0021 -0.999049 -0.000333237 -6.34726e-05 0.000299653 0.998765 -3.61478e-06 1.29939e-05 -3.30105e-06 1.0023 -0.998963 -0.000367558 -6.55617e-05 0.000330591 0.998652 -5.42189e-06 1.22305e-05 -1.08932e-06 1.0025 -0.998878 -0.000401729 -6.73896e-05 0.000361309 0.998539 -7.28896e-06 1.12008e-05 1.07859e-06 1.0027 -0.998794 -0.000435709 -6.90047e-05 0.000391804 0.998426 -9.21054e-06 9.94105e-06 3.28173e-06 1.0029 -0.998709 -0.000469508 -7.04463e-05 0.000422057 0.998312 -1.11798e-05 8.49461e-06 5.57476e-06 1.0031 -0.998624 -0.000503138 -7.17524e-05 0.000452072 0.998199 -1.31726e-05 6.9157e-06 7.96211e-06 1.00331 -0.998454 -0.000569773 -7.3922e-05 0.000511375 0.997972 -1.7134e-05 3.36197e-06 1.30473e-05 1.00371 -0.998285 -0.000635709 -7.5697e-05 0.000570089 0.997745 -2.09651e-05 -5.69965e-07 1.84014e-05 1.00411 -0.998117 -0.000700817 -7.71514e-05 0.000628194 0.997517 -2.47032e-05 -4.90018e-06 2.40753e-05 1.00451 -0.997948 -0.000765076 -7.83609e-05 0.000685691 0.99729 -2.83925e-05 -9.61051e-06 3.00257e-05 1.00491 -0.997864 -0.00079699 -7.89105e-05 0.000714276 0.997176 -3.02445e-05 -1.20436e-05 3.30712e-05 1.00511 -0.997612 -0.000891088 -8.00417e-05 0.000798664 0.996834 -3.59568e-05 -1.96907e-05 4.27684e-05 1.00572 -0.997402 -0.000968486 -8.06851e-05 0.00086804 0.996549 -4.07872e-05 -2.61453e-05 5.10987e-05 1.00622 -0.997193 -0.00104496 -8.11007e-05 0.00093657 0.996263 -4.56521e-05 -3.26152e-05 5.96329e-05 1.00672 -0.996879 -0.0011579 -8.13996e-05 0.00103775 0.995835 -5.2861e-05 -4.21682e-05 7.26131e-05 1.00748 -0.996566 -0.00126914 -8.14294e-05 0.00113739 0.995407 -5.99173e-05 -5.16931e-05 8.56977e-05 1.00823 -0.996253 -0.00137884 -8.12075e-05 0.00123571 0.994979 -6.66805e-05 -6.12599e-05 9.87488e-05 1.00899 -0.99594 -0.00148712 -8.07912e-05 0.00133285 0.99455 -7.31961e-05 -7.08077e-05 0.000111744 1.00975 -0.995523 -0.00162896 -8.01051e-05 0.00146019 0.99398 -8.16948e-05 -8.3385e-05 0.000129068 1.01075 diff --git a/test/data/voce_ea_cs_eps.txt b/test/data/voce_ea_cs_eps.txt deleted file mode 100644 index 879d6da..0000000 --- a/test/data/voce_ea_cs_eps.txt +++ /dev/null @@ -1,40 +0,0 @@ -0 -3.46811e-07 -3.88961e-05 -0.000130992 -0.000235568 -0.000342301 -0.000449272 -0.000556043 -0.000662545 -0.000768796 -0.000874843 -0.000980726 -0.00108647 -0.00119208 -0.00129759 -0.001403 -0.00150833 -0.00161357 -0.00171875 -0.00182386 -0.0019289 -0.00213875 -0.00234838 -0.0025578 -0.00276702 -0.00297608 -0.00318499 -0.0036022 -0.00401895 -0.00443526 -0.00485118 -0.00505908 -0.00568185 -0.00620045 -0.00671866 -0.00749508 -0.00827084 -0.00904598 -0.0098206 -0.0108524 diff --git a/test/data/voce_ea_cs_euler_strain.txt b/test/data/voce_ea_cs_euler_strain.txt deleted file mode 100644 index f871595..0000000 --- a/test/data/voce_ea_cs_euler_strain.txt +++ /dev/null @@ -1,40 +0,0 @@ --1.63349e-06 -1.77848e-06 4.99996e-06 1.10839e-09 -9.90119e-08 -7.40345e-09 --6.53528e-05 -7.12165e-05 0.000199941 4.13525e-09 -3.98395e-06 -2.68166e-07 --0.000101836 -0.000113555 0.000299886 -6.06486e-07 -6.13847e-06 -4.72083e-07 --0.000143207 -0.000165133 0.000399812 -2.06942e-06 -8.55416e-06 -1.21935e-06 --0.000185479 -0.000219657 0.000499717 -3.16458e-06 -1.02297e-05 -2.25114e-06 --0.000227822 -0.000275286 0.000599602 -3.7753e-06 -1.14209e-05 -3.43117e-06 --0.000270278 -0.00033131 0.000699467 -4.14131e-06 -1.2439e-05 -4.63733e-06 --0.000312858 -0.000387489 0.000799313 -4.35842e-06 -1.34151e-05 -5.74948e-06 --0.000355487 -0.000443792 0.000899138 -4.48453e-06 -1.43578e-05 -6.72576e-06 --0.000398121 -0.000500207 0.000998943 -4.54305e-06 -1.52599e-05 -7.58795e-06 --0.000440758 -0.0005567 0.00109873 -4.54556e-06 -1.61193e-05 -8.34536e-06 --0.000483406 -0.000613242 0.00119849 -4.51555e-06 -1.69475e-05 -9.0236e-06 --0.000526064 -0.000669821 0.00129824 -4.46844e-06 -1.77475e-05 -9.67236e-06 --0.000568729 -0.000726429 0.00139797 -4.40785e-06 -1.85246e-05 -1.03246e-05 --0.000611397 -0.000783066 0.00149767 -4.33569e-06 -1.9286e-05 -1.09868e-05 --0.000654063 -0.00083973 0.00159736 -4.2525e-06 -2.00371e-05 -1.16563e-05 --0.00069673 -0.000896416 0.00169702 -4.15924e-06 -2.07819e-05 -1.2337e-05 --0.000739406 -0.000953114 0.00179667 -4.05768e-06 -2.15206e-05 -1.30305e-05 --0.000782093 -0.00100982 0.00189629 -3.94741e-06 -2.2252e-05 -1.37428e-05 --0.000824783 -0.00106654 0.0019959 -3.82785e-06 -2.29779e-05 -1.44838e-05 --0.000867467 -0.00112328 0.00209549 -3.69934e-06 -2.37004e-05 -1.52549e-05 --0.000952778 -0.00123685 0.00229459 -3.44242e-06 -2.5127e-05 -1.68926e-05 --0.00103807 -0.00135049 0.00249361 -3.2348e-06 -2.65396e-05 -1.86044e-05 --0.00112334 -0.00146421 0.00269255 -3.07837e-06 -2.79545e-05 -2.03537e-05 --0.00120859 -0.001578 0.00289142 -2.93089e-06 -2.93781e-05 -2.21209e-05 --0.0012938 -0.00169187 0.0030902 -2.76157e-06 -3.08081e-05 -2.39206e-05 --0.00137898 -0.00180584 0.00328891 -2.55616e-06 -3.22364e-05 -2.57574e-05 --0.00154906 -0.00203409 0.00368604 -1.97619e-06 -3.50698e-05 -2.94873e-05 --0.00171889 -0.00226276 0.00408285 -1.19448e-06 -3.78947e-05 -3.31704e-05 --0.00188851 -0.00249184 0.00447935 -2.04135e-07 -4.07583e-05 -3.67515e-05 --0.00205795 -0.00272126 0.00487553 9.51093e-07 -4.36892e-05 -4.0219e-05 --0.00214267 -0.00283609 0.00507352 1.56115e-06 -4.51659e-05 -4.19289e-05 --0.00239639 -0.00318099 0.0056669 3.59772e-06 -4.95122e-05 -4.69312e-05 --0.00260769 -0.00346888 0.00616086 5.389e-06 -5.30259e-05 -5.10781e-05 --0.00281885 -0.00375719 0.00665433 7.26917e-06 -5.64337e-05 -5.51977e-05 --0.00313523 -0.00419028 0.00739352 1.02299e-05 -6.13082e-05 -6.13181e-05 --0.00345145 -0.00462413 0.00813159 1.33271e-05 -6.6035e-05 -6.7382e-05 --0.00376775 -0.0050585 0.00886857 1.65608e-05 -7.06577e-05 -7.33603e-05 --0.00408421 -0.0054933 0.00960444 1.9897e-05 -7.51748e-05 -7.92467e-05 --0.00450628 -0.00607353 0.0105838 2.44486e-05 -8.10563e-05 -8.69482e-05 diff --git a/test/data/voce_ea_cs_pl_work.txt b/test/data/voce_ea_cs_pl_work.txt deleted file mode 100644 index 9f9d7bf..0000000 --- a/test/data/voce_ea_cs_pl_work.txt +++ /dev/null @@ -1,40 +0,0 @@ -0 -8.61648e-09 -1.06471e-06 -3.78251e-06 -7.0855e-06 -1.06322e-05 -1.43194e-05 -1.81061e-05 -2.19698e-05 -2.58973e-05 -2.98793e-05 -3.3909e-05 -3.79818e-05 -4.20943e-05 -4.6244e-05 -5.04292e-05 -5.46482e-05 -5.89e-05 -6.31838e-05 -6.74989e-05 -7.18448e-05 -8.06541e-05 -8.95809e-05 -9.86239e-05 -0.000107781 -0.000117051 -0.000126434 -0.000145633 -0.000165269 -0.000185335 -0.00020583 -0.000216186 -0.000248185 -0.000275503 -0.000303466 -0.000346836 -0.00039163 -0.000437836 -0.000485443 -0.00055136 diff --git a/test/data/voce_ea_cs_stress.txt b/test/data/voce_ea_cs_stress.txt deleted file mode 100644 index d7e97c7..0000000 --- a/test/data/voce_ea_cs_stress.txt +++ /dev/null @@ -1,40 +0,0 @@ -1.12859e-14 1.15709e-14 0.00065299 -5.79514e-06 2.78435e-06 2.06205e-08 --2.09368e-11 6.59385e-11 0.0260677 -0.000237011 0.000108523 8.92061e-07 --3.37398e-11 1.33392e-09 0.0347764 -0.00033483 0.000156622 2.36164e-05 -1.08924e-11 1.04074e-10 0.0376793 -0.000416313 0.000145826 7.77879e-05 -1.12445e-12 -3.93882e-13 0.0390051 -0.00046604 0.000140537 0.000126976 -4.82287e-14 -1.19183e-12 0.0398508 -0.000487921 0.000154224 0.000163594 -6.97476e-10 5.73628e-10 0.040492 -0.000500517 0.000174054 0.000190223 -3.93723e-10 3.36215e-11 0.0410221 -0.000511061 0.000193411 0.000208461 -9.55829e-11 -5.75645e-11 0.0414852 -0.000521826 0.000209462 0.00022047 -1.96951e-12 -7.17352e-11 0.0419042 -0.000531838 0.000222681 0.000228588 --1.13187e-11 -6.67327e-11 0.0422934 -0.000540684 0.000233826 0.000233659 --2.50408e-11 -4.9248e-11 0.0426617 -0.000548806 0.000243563 0.000236719 --1.97488e-11 -4.17435e-11 0.043015 -0.000556318 0.000252404 0.00023898 --2.04216e-11 -3.14526e-11 0.0433571 -0.000563213 0.000260589 0.00024106 --1.88052e-11 -2.14566e-11 0.0436905 -0.000569575 0.000268119 0.000242996 --1.50449e-11 -1.37333e-11 0.0440172 -0.000575305 0.000275034 0.000244865 --1.37704e-11 -1.02336e-11 0.0443385 -0.000580383 0.00028149 0.000246829 --1.34718e-11 -8.84149e-12 0.0446552 -0.000584924 0.000287597 0.000248799 --1.21159e-11 -7.56076e-12 0.0449681 -0.000589112 0.000293397 0.000250706 --1.00495e-11 -7.08852e-12 0.0452778 -0.000593116 0.000298881 0.000252533 --8.9757e-12 -7.44259e-12 0.0455846 -0.000597007 0.000303981 0.000254329 --4.11599e-11 -3.00115e-11 0.0461894 -0.000604312 0.00031286 0.000257933 --4.81545e-11 -4.02846e-11 0.0467865 -0.000611298 0.000321166 0.000261126 --4.89282e-11 -3.93917e-11 0.047377 -0.000618179 0.000329237 0.000263802 --4.00388e-11 -3.82688e-11 0.0479621 -0.000625027 0.000336914 0.000266001 --3.91661e-11 -4.19892e-11 0.0485425 -0.00063178 0.000344063 0.000267846 --3.64781e-11 -3.80262e-11 0.0491187 -0.000638355 0.000350903 0.00026951 --1.94813e-10 -1.98702e-10 0.0502571 -0.000650779 0.000364306 0.000272415 --1.3238e-10 -1.82379e-10 0.0513839 -0.000662281 0.00037672 0.000274882 --8.64889e-11 -1.29443e-10 0.0525008 -0.000672582 0.000387987 0.000277044 --6.56764e-11 -8.89759e-11 0.0536089 -0.000681703 0.000398824 0.000278639 --1.47628e-11 -1.91078e-11 0.0541612 -0.000686078 0.000404183 0.000279321 -1.37836e-12 -7.25508e-13 0.055801 -0.00069876 0.000419669 0.000280993 --1.07207e-10 -1.40498e-10 0.0571577 -0.000709342 0.000431484 0.00028205 --9.60835e-11 -1.12028e-10 0.0585053 -0.000719764 0.000442448 0.000282979 --5.09971e-10 -2.36532e-10 0.0605068 -0.000735436 0.0004577 0.000284906 --4.78645e-10 -2.67901e-10 0.0624898 -0.000751175 0.000471759 0.000287476 --2.20899e-10 -1.48679e-10 0.0644556 -0.000767015 0.000485886 0.000291301 --2.31115e-10 -1.40562e-10 0.066405 -0.000782963 0.000500263 0.00029614 --6.45318e-10 -5.3854e-10 0.0689752 -0.000804306 0.000519269 0.000303304 diff --git a/test/data/voce_ea_def_grad.txt b/test/data/voce_ea_def_grad.txt deleted file mode 100644 index 452265c..0000000 --- a/test/data/voce_ea_def_grad.txt +++ /dev/null @@ -1,40 +0,0 @@ -0.999998 -1.88827e-07 -7.53881e-08 1.7402e-07 0.999998 -3.51739e-08 -1.22637e-07 3.73904e-08 1.00001 -0.999935 -7.57044e-06 -3.16404e-06 7.03427e-06 0.999929 -1.40827e-06 -4.80521e-06 1.41615e-06 1.0002 -0.999898 -1.41746e-05 -7.31697e-06 1.32312e-05 0.999886 -1.65817e-06 -4.96304e-06 4.44807e-07 1.0003 -0.999857 -2.40307e-05 -1.27288e-05 2.15946e-05 0.999835 -3.89935e-07 -4.38625e-06 -3.74825e-06 1.0004 -0.999815 -3.55668e-05 -1.83097e-05 3.10704e-05 0.99978 2.31649e-06 -2.16249e-06 -8.64354e-06 1.0005 -0.999772 -4.8852e-05 -2.31825e-05 4.20005e-05 0.999725 4.22157e-06 3.21106e-07 -1.17686e-05 1.0006 -0.99973 -6.3442e-05 -2.74293e-05 5.41841e-05 0.999669 5.34649e-06 2.52465e-06 -1.36244e-05 1.0007 -0.999687 -7.88686e-05 -3.12667e-05 6.7393e-05 0.999613 5.94666e-06 4.402e-06 -1.4658e-05 1.0008 -0.999645 -9.48376e-05 -3.48068e-05 8.14166e-05 0.999557 6.17376e-06 6.04838e-06 -1.5137e-05 1.0009 -0.999602 -0.000111196 -3.81217e-05 9.60588e-05 0.9995 6.12709e-06 7.54993e-06 -1.52076e-05 1.001 -0.99956 -0.000127802 -4.11977e-05 0.000111158 0.999444 5.82843e-06 8.89754e-06 -1.49148e-05 1.0011 -0.999517 -0.000144571 -4.40176e-05 0.00012658 0.999388 5.33941e-06 1.0051e-05 -1.43669e-05 1.0012 -0.999475 -0.000161463 -4.65994e-05 0.000142185 0.999331 4.71333e-06 1.10226e-05 -1.36483e-05 1.0013 -0.999432 -0.000178452 -4.89659e-05 0.000157882 0.999275 3.98619e-06 1.18245e-05 -1.28022e-05 1.0014 -0.99939 -0.000195517 -5.11238e-05 0.000173635 0.999218 3.19985e-06 1.24492e-05 -1.18742e-05 1.0015 -0.999347 -0.000212638 -5.309e-05 0.000189431 0.999162 2.3867e-06 1.29027e-05 -1.08977e-05 1.0016 -0.999305 -0.000229804 -5.48978e-05 0.000205251 0.999106 1.55824e-06 1.32104e-05 -9.88616e-06 1.0017 -0.999262 -0.000246981 -5.65861e-05 0.000221058 0.999049 7.20124e-07 1.34107e-05 -8.84872e-06 1.0018 -0.99922 -0.000264146 -5.81728e-05 0.000236816 0.998993 -1.24789e-07 1.35237e-05 -7.78749e-06 1.0019 -0.999177 -0.00028131 -5.96586e-05 0.000252518 0.998936 -9.77525e-07 1.3547e-05 -6.70026e-06 1.002 -0.999135 -0.00029848 -6.10444e-05 0.000268166 0.99888 -1.83793e-06 1.34771e-05 -5.5878e-06 1.0021 -0.99905 -0.000332807 -6.34432e-05 0.000299265 0.998767 -3.59336e-06 1.30012e-05 -3.3282e-06 1.0023 -0.998965 -0.000367051 -6.55316e-05 0.000330135 0.998654 -5.39559e-06 1.22435e-05 -1.12052e-06 1.0025 -0.99888 -0.00040114 -6.73588e-05 0.00036078 0.998541 -7.25718e-06 1.12206e-05 1.04225e-06 1.0027 -0.998795 -0.000435032 -6.89733e-05 0.000391197 0.998428 -9.17265e-06 9.96839e-06 3.23813e-06 1.0029 -0.998711 -0.000468737 -7.04139e-05 0.000421369 0.998315 -1.11355e-05 8.52953e-06 5.52257e-06 1.0031 -0.998626 -0.000502268 -7.17189e-05 0.000451296 0.998202 -1.31219e-05 6.95835e-06 7.90036e-06 1.0033 -0.998457 -0.000568693 -7.38878e-05 0.000510414 0.997976 -1.70722e-05 3.42222e-06 1.29647e-05 1.0037 -0.998289 -0.000634401 -7.56637e-05 0.000568923 0.997749 -2.08916e-05 -4.87189e-07 1.82938e-05 1.0041 -0.998121 -0.000699262 -7.71187e-05 0.000626804 0.997523 -2.46169e-05 -4.79026e-06 2.39382e-05 1.0045 -0.997953 -0.000763256 -7.83294e-05 0.00068406 0.997296 -2.82893e-05 -9.47004e-06 2.98546e-05 1.0049 -0.997869 -0.000795026 -7.88797e-05 0.000712516 0.997183 -3.01313e-05 -1.18874e-05 3.28813e-05 1.0051 -0.997618 -0.000888684 -8.00169e-05 0.00079651 0.996843 -3.58124e-05 -1.94912e-05 4.25193e-05 1.0057 -0.99741 -0.000965676 -8.06667e-05 0.000865524 0.996559 -4.0613e-05 -2.5907e-05 5.07941e-05 1.0062 -0.997202 -0.00104172 -8.10872e-05 0.000933663 0.996275 -4.54482e-05 -3.23378e-05 5.92697e-05 1.0067 -0.99689 -0.00115396 -8.13943e-05 0.00103423 0.99585 -5.26151e-05 -4.18316e-05 7.21617e-05 1.00745 -0.996579 -0.00126444 -8.14358e-05 0.00113319 0.995425 -5.96287e-05 -5.12826e-05 8.51475e-05 1.0082 -0.996269 -0.00137332 -8.12288e-05 0.00123077 0.995 -6.63516e-05 -6.077e-05 9.80967e-05 1.00895 -0.995958 -0.00148072 -8.0826e-05 0.00132711 0.994576 -7.28208e-05 -7.02341e-05 0.00011098 1.0097 -0.995545 -0.00162134 -8.0151e-05 0.00145334 0.994011 -8.12409e-05 -8.2702e-05 0.000128134 1.0107 diff --git a/test/data/voce_ea_eps.txt b/test/data/voce_ea_eps.txt deleted file mode 100644 index c257e71..0000000 --- a/test/data/voce_ea_eps.txt +++ /dev/null @@ -1,40 +0,0 @@ -0 -3.46778e-07 -3.88802e-05 -0.000130941 -0.000235474 -0.000342153 -0.00044906 -0.000555758 -0.000662176 -0.000768333 -0.000874276 -0.000980044 -0.00108566 -0.00119114 -0.0012965 -0.00140175 -0.00150691 -0.00161198 -0.00171697 -0.00182188 -0.00192672 -0.00213613 -0.00234528 -0.00255418 -0.00276285 -0.00297131 -0.00317957 -0.00359543 -0.00401064 -0.00442527 -0.00483934 -0.00504623 -0.00566586 -0.00618153 -0.00669657 -0.00746785 -0.0082379 -0.00900677 -0.00977455 -0.0107965 diff --git a/test/data/voce_ea_euler_strain.txt b/test/data/voce_ea_euler_strain.txt deleted file mode 100644 index 5f5abec..0000000 --- a/test/data/voce_ea_euler_strain.txt +++ /dev/null @@ -1,40 +0,0 @@ --1.63349e-06 -1.77848e-06 4.99996e-06 1.10839e-09 -9.90119e-08 -7.40345e-09 --6.53525e-05 -7.12162e-05 0.00019994 4.13742e-09 -3.98393e-06 -2.68166e-07 --0.000101827 -0.000113544 0.000299865 -6.0625e-07 -6.13796e-06 -4.71953e-07 --0.000143186 -0.000165106 0.000399761 -2.06871e-06 -8.55311e-06 -1.21891e-06 --0.00018544 -0.000219607 0.000499626 -3.16389e-06 -1.02285e-05 -2.25016e-06 --0.000227763 -0.000275207 0.000599461 -3.77469e-06 -1.14194e-05 -3.42951e-06 --0.000270192 -0.000331198 0.000699267 -4.1408e-06 -1.24369e-05 -4.63504e-06 --0.000312742 -0.000387338 0.000799042 -4.35803e-06 -1.34125e-05 -5.74673e-06 --0.000355337 -0.000443595 0.000898788 -4.48426e-06 -1.43545e-05 -6.72262e-06 --0.000397933 -0.000499959 0.000998504 -4.54297e-06 -1.52559e-05 -7.58444e-06 --0.000440528 -0.000556395 0.00109819 -4.5457e-06 -1.61146e-05 -8.34156e-06 --0.000483129 -0.000612875 0.00119785 -4.51585e-06 -1.69421e-05 -9.01935e-06 --0.000525736 -0.000669386 0.00129747 -4.46889e-06 -1.77412e-05 -9.66736e-06 --0.000568346 -0.000725921 0.00139707 -4.40847e-06 -1.85175e-05 -1.03186e-05 --0.000610954 -0.000782478 0.00149663 -4.33652e-06 -1.92779e-05 -1.09798e-05 --0.000653556 -0.000839057 0.00159617 -4.25357e-06 -2.0028e-05 -1.16482e-05 --0.000696155 -0.000895653 0.00169568 -4.16057e-06 -2.07716e-05 -1.23276e-05 --0.000738758 -0.000952254 0.00179515 -4.0593e-06 -2.15091e-05 -1.30197e-05 --0.000781369 -0.00100886 0.0018946 -3.94936e-06 -2.22394e-05 -1.37304e-05 --0.000823978 -0.00106547 0.00199402 -3.83019e-06 -2.29639e-05 -1.44694e-05 --0.000866576 -0.0011221 0.00209341 -3.70207e-06 -2.3685e-05 -1.52384e-05 --0.00095171 -0.00123542 0.00229209 -3.44535e-06 -2.51089e-05 -1.68716e-05 --0.00103681 -0.00134881 0.00249066 -3.23733e-06 -2.65182e-05 -1.85787e-05 --0.00112186 -0.00146224 0.00268911 -3.08076e-06 -2.79295e-05 -2.03232e-05 --0.00120688 -0.00157572 0.00288744 -2.93388e-06 -2.9349e-05 -2.20853e-05 --0.00129185 -0.00168927 0.00308565 -2.7657e-06 -3.07748e-05 -2.38788e-05 --0.00137676 -0.00180288 0.00328374 -2.56191e-06 -3.21988e-05 -2.57092e-05 --0.00154629 -0.00203037 0.00367957 -1.9869e-06 -3.50231e-05 -2.94268e-05 --0.0017155 -0.0022582 0.00407493 -1.21194e-06 -3.78373e-05 -3.30979e-05 --0.00188443 -0.00248633 0.00446981 -2.30064e-07 -4.06876e-05 -3.66668e-05 --0.00205312 -0.00271471 0.00486423 9.16467e-07 -4.3604e-05 -4.01213e-05 --0.00213742 -0.00282898 0.00506126 1.52197e-06 -4.50733e-05 -4.18239e-05 --0.00238986 -0.00317211 0.00565164 3.54417e-06 -4.94011e-05 -4.68024e-05 --0.00259998 -0.00345836 0.00614282 5.32225e-06 -5.28988e-05 -5.09262e-05 --0.00280985 -0.00374487 0.00663327 7.1875e-06 -5.62898e-05 -5.50216e-05 --0.00312412 -0.00417507 0.00736757 1.01245e-05 -6.11391e-05 -6.11032e-05 --0.00343802 -0.00460568 0.00810024 1.31927e-05 -6.5835e-05 -6.71242e-05 --0.00375173 -0.0050365 0.00883127 1.63945e-05 -7.04259e-05 -7.3059e-05 --0.00406538 -0.00546743 0.00956068 1.96964e-05 -7.49082e-05 -7.88969e-05 --0.00448339 -0.00604204 0.0105307 2.42006e-05 -8.07412e-05 -8.65325e-05 diff --git a/test/data/voce_ea_pl_work.txt b/test/data/voce_ea_pl_work.txt deleted file mode 100644 index 5b27a16..0000000 --- a/test/data/voce_ea_pl_work.txt +++ /dev/null @@ -1,40 +0,0 @@ -0 -8.61564e-09 -1.06426e-06 -3.78094e-06 -7.08237e-06 -1.06271e-05 -1.43119e-05 -1.80957e-05 -2.19561e-05 -2.58798e-05 -2.98575e-05 -3.38824e-05 -3.795e-05 -4.20568e-05 -4.62003e-05 -5.03787e-05 -5.45905e-05 -5.88345e-05 -6.311e-05 -6.74163e-05 -7.17528e-05 -8.05419e-05 -8.94464e-05 -9.84645e-05 -0.000107595 -0.000116835 -0.000126185 -0.000145314 -0.000164868 -0.000184841 -0.000205231 -0.00021553 -0.000247341 -0.00027448 -0.000302243 -0.000345272 -0.000389674 -0.000435433 -0.000482533 -0.000547685 diff --git a/test/data/voce_ea_stress.txt b/test/data/voce_ea_stress.txt deleted file mode 100644 index 92b7f80..0000000 --- a/test/data/voce_ea_stress.txt +++ /dev/null @@ -1,40 +0,0 @@ -1.12859e-14 1.15709e-14 0.00065299 -5.79514e-06 2.78435e-06 2.06205e-08 --2.09402e-11 6.60843e-11 0.0260676 -0.00023701 0.000108523 8.92033e-07 --3.37196e-11 1.31392e-09 0.0347754 -0.000334815 0.000156617 2.36076e-05 -1.07929e-11 1.03792e-10 0.0376783 -0.000416276 0.000145835 7.77595e-05 -1.12605e-12 -3.84594e-13 0.039004 -0.000466011 0.000140531 0.000126939 -4.90498e-14 -1.19278e-12 0.0398496 -0.000487898 0.000154199 0.00016355 -7.00417e-10 5.71889e-10 0.0404905 -0.000500492 0.000174013 0.000190179 -3.9274e-10 3.28504e-11 0.0410204 -0.000511029 0.000193364 0.000208421 -9.54838e-11 -5.79257e-11 0.0414833 -0.000521785 0.000209412 0.000220435 -1.58167e-12 -7.17513e-11 0.0419019 -0.000531791 0.000222628 0.000228557 --1.14863e-11 -6.66752e-11 0.0422908 -0.000540632 0.00023377 0.000233635 --2.50411e-11 -4.92729e-11 0.0426588 -0.000548748 0.000243502 0.000236699 --1.97556e-11 -4.17614e-11 0.0430117 -0.000556255 0.000252337 0.00023896 --2.03796e-11 -3.15638e-11 0.0433533 -0.000563145 0.000260516 0.000241038 --1.87945e-11 -2.159e-11 0.0436863 -0.000569502 0.000268042 0.000242972 --1.50229e-11 -1.38414e-11 0.0440125 -0.00057523 0.000274952 0.000244837 --1.37175e-11 -1.02998e-11 0.0443332 -0.000580307 0.000281403 0.000246797 --1.34358e-11 -8.88811e-12 0.0446494 -0.000584845 0.000287503 0.000248764 --1.21333e-11 -7.58935e-12 0.0449618 -0.000589029 0.000293297 0.000250668 --1.00771e-11 -7.09538e-12 0.0452708 -0.000593026 0.000298776 0.000252493 --8.99392e-12 -7.45511e-12 0.045577 -0.00059691 0.000303874 0.000254284 --4.11555e-11 -3.00681e-11 0.0461806 -0.000604205 0.000312747 0.000257882 --4.84815e-11 -4.03901e-11 0.0467763 -0.000611176 0.000321037 0.000261075 --4.91844e-11 -3.97377e-11 0.0473653 -0.00061804 0.000329092 0.000263751 --4.01135e-11 -3.81285e-11 0.0479488 -0.000624868 0.000336758 0.000265951 --3.94973e-11 -4.1694e-11 0.0485275 -0.000631603 0.000343894 0.000267796 --3.70866e-11 -3.7813e-11 0.0491019 -0.000638161 0.000350717 0.000269457 --1.97593e-10 -1.97057e-10 0.0502367 -0.000650556 0.000364082 0.000272358 --1.41647e-10 -1.82016e-10 0.0513595 -0.000662034 0.000376475 0.000274824 --9.42938e-11 -1.30569e-10 0.052472 -0.000672321 0.000387711 0.000276984 --7.3849e-11 -8.83807e-11 0.0535752 -0.000681421 0.000398503 0.000278587 --1.69321e-11 -1.87674e-11 0.0541247 -0.000685778 0.000403837 0.000279268 -1.38314e-12 -7.36002e-13 0.0557565 -0.000698396 0.000419273 0.000280938 --1.33187e-10 -1.34606e-10 0.0571058 -0.000708921 0.000431053 0.000282001 --1.1501e-10 -1.07218e-10 0.0584454 -0.000719284 0.000441976 0.000282917 -7.14373e-13 -9.16784e-13 0.0604342 -0.000734841 0.000457167 0.000284812 -4.39119e-13 -2.80562e-13 0.0624036 -0.000750468 0.00047115 0.00028733 -1.63667e-13 1.40991e-13 0.0643543 -0.000766174 0.000485138 0.000291061 --2.28088e-13 -9.12196e-14 0.0662874 -0.000781978 0.000499388 0.000295823 --6.61302e-13 6.0289e-14 0.0688347 -0.000803113 0.000518232 0.000302891 diff --git a/test/data/voce_full.toml b/test/data/voce_full.toml index 3d922a1..657618c 100644 --- a/test/data/voce_full.toml +++ b/test/data/voce_full.toml @@ -85,8 +85,6 @@ Version = "0.6.0" visit = false conduit = false paraview = false - floc = "./exaconstit_p1" - avg_stress_fname = "test_voce_full_stress.txt" [Solvers] # Option for how our assembly operation is conducted. Possible choices are # FULL, PA, EA diff --git a/test/data/voce_full_cyclic.toml b/test/data/voce_full_cyclic.toml index 0f4746d..b746d79 100644 --- a/test/data/voce_full_cyclic.toml +++ b/test/data/voce_full_cyclic.toml @@ -108,8 +108,6 @@ Version = "0.6.0" visit = false conduit = false paraview = false - floc = "./exaconstit_p1" - avg_stress_fname = "test_voce_full_cyclic_stress.txt" [Solvers] # Option for how our assembly operation is conducted. Possible choices are # FULL, PA, EA diff --git a/test/data/voce_full_cyclic_cs.toml b/test/data/voce_full_cyclic_cs.toml index f1ec5dd..baae159 100644 --- a/test/data/voce_full_cyclic_cs.toml +++ b/test/data/voce_full_cyclic_cs.toml @@ -113,8 +113,6 @@ Version = "0.6.0" visit = false conduit = false paraview = false - floc = "./exaconstit_p1" - avg_stress_fname = "test_voce_full_cyclic_cs_stress.txt" [Solvers] # Option for how our assembly operation is conducted. Possible choices are # FULL, PA, EA diff --git a/test/data/voce_full_cyclic_cs_stress.txt b/test/data/voce_full_cyclic_cs_stress.txt deleted file mode 100644 index 4a76097..0000000 --- a/test/data/voce_full_cyclic_cs_stress.txt +++ /dev/null @@ -1,70 +0,0 @@ -9.33756e-11 9.4227e-11 0.0130582 -0.000115907 5.56805e-05 4.1481e-07 --7.7457e-12 3.90247e-11 0.0260799 -0.000236274 0.00010891 8.37972e-07 --3.11257e-11 1.28904e-09 0.0347779 -0.000334745 0.000156686 2.3619e-05 -8.68974e-12 1.03618e-10 0.0376799 -0.000416243 0.00014589 7.77951e-05 --7.10555e-14 2.46477e-13 0.0390056 -0.000465962 0.000140616 0.000126981 --2.59471e-14 -9.1968e-13 0.0398513 -0.000487842 0.000154308 0.000163597 -6.62271e-10 7.27616e-10 0.0404924 -0.000500438 0.000174139 0.000190225 -3.56947e-10 1.66159e-10 0.0410225 -0.000510985 0.000193496 0.00020846 -6.32376e-11 3.70838e-11 0.0414856 -0.000521751 0.000209545 0.000220469 --7.64258e-12 -1.66179e-11 0.0419046 -0.000531764 0.000222762 0.000228586 --4.72397e-11 1.87569e-11 0.0288444 -0.000415701 0.000167076 0.000228277 --2.53104e-12 3.04891e-12 0.0157835 -0.000299657 0.000111375 0.000227971 --2.17181e-11 -1.33763e-11 0.00272176 -0.000183629 5.56726e-05 0.00022767 --3.29478e-12 -9.13372e-12 -0.0103313 -6.58093e-05 1.0773e-06 0.000227397 --7.93755e-13 -1.14943e-12 -0.022454 8.07056e-05 -4.0261e-05 0.000223572 --3.9849e-12 -1.51111e-12 -0.0300067 0.000147914 -0.000113632 0.00019999 --1.87806e-12 -5.99228e-13 -0.0339279 0.00022252 -0.000139431 0.000155164 --1.47804e-12 -7.46175e-13 -0.036277 0.000318297 -0.000122673 9.62323e-05 --1.63358e-09 -4.62675e-09 -0.0378892 0.000391964 -0.000111382 3.66018e-05 --8.33489e-10 -2.29198e-09 -0.0391045 0.000437451 -0.000112843 -1.31006e-05 --3.94685e-10 -8.74197e-10 -0.0400772 0.000465071 -0.000126018 -5.48924e-05 --4.3062e-10 -3.86889e-10 -0.0408948 0.000483446 -0.000144 -9.11312e-05 --2.18316e-10 -2.3741e-10 -0.0416057 0.000496686 -0.000163642 -0.000122528 --9.43843e-11 -1.39935e-10 -0.0422382 0.000507379 -0.000184135 -0.000148785 --7.7106e-11 -1.07323e-10 -0.0428103 0.000516887 -0.000204642 -0.000169846 --7.4746e-11 -8.57166e-11 -0.0433344 0.000526563 -0.000224165 -0.000186973 --7.16206e-11 -6.96342e-11 -0.0438212 0.000536974 -0.000241403 -0.000200706 --7.0907e-11 -4.31925e-11 -0.0442781 0.000547505 -0.000255863 -0.000211618 --5.74232e-11 -3.33646e-11 -0.0447089 0.000557672 -0.000268672 -0.000220785 --4.35251e-11 -3.71763e-11 -0.0451167 0.000566915 -0.00028013 -0.000228782 --1.19206e-10 -1.00093e-10 -0.0320567 0.000451174 -0.000224472 -0.000228281 -1.01673e-11 3.93433e-12 -0.0189976 0.000335422 -0.000168805 -0.000227772 -1.75333e-11 9.31904e-12 -0.00593934 0.000219656 -0.000113138 -0.000227257 -5.99604e-16 -1.70542e-15 0.00711796 0.000103868 -5.74954e-05 -0.000226726 --1.04865e-10 1.08827e-09 0.0199704 -2.64307e-05 -7.41332e-06 -0.000226642 --1.33405e-12 -1.25921e-12 0.0297969 -0.00012777 6.48925e-05 -0.000219134 -1.50515e-11 -3.07492e-12 0.0350452 -0.000191179 0.000146697 -0.000191186 --9.11176e-13 -5.57155e-13 0.0380242 -0.000289075 0.00016336 -0.000139498 -1.56418e-09 4.17128e-09 0.0399979 -0.00037817 0.000160653 -7.77586e-05 -5.51097e-10 2.03453e-09 0.0414412 -0.00044141 0.000164926 -1.92223e-05 -3.41771e-10 3.9921e-10 0.0425703 -0.000481442 0.000175572 2.999e-05 -2.08739e-10 1.25805e-10 0.0434967 -0.000508068 0.000191683 7.12291e-05 -1.17076e-10 2.86677e-11 0.0442886 -0.000525566 0.000209554 0.000105541 -2.14628e-11 5.11148e-11 0.0449854 -0.000537753 0.000227831 0.00013481 --1.21912e-11 4.52101e-11 0.0456096 -0.00054746 0.000247231 0.000159067 --2.52358e-11 2.00411e-11 0.0461772 -0.000557251 0.000266005 0.000178436 --3.32848e-11 -2.18422e-12 0.046699 -0.000567695 0.000283605 0.000194535 --3.21394e-11 -1.45727e-11 0.0471842 -0.000578192 0.000299004 0.00020761 --2.73082e-11 -2.05462e-11 0.04764 -0.000588039 0.000311823 0.000218272 --1.11035e-11 -2.78752e-11 0.0480704 -0.000597171 0.000322727 0.000227428 --8.63459e-12 3.49744e-11 0.0350106 -0.000481089 0.000267021 0.000227103 -8.46883e-12 1.40305e-11 0.0219502 -0.000365028 0.000211315 0.000226788 --1.42285e-11 -6.46214e-12 0.00888892 -0.000248981 0.000155607 0.000226479 --2.8594e-11 -1.92816e-11 -0.0041732 -0.000132949 9.98995e-05 0.000226175 --7.45704e-12 -2.19349e-11 -0.0172076 -1.33539e-05 4.52922e-05 0.000225736 --3.29956e-09 -6.94566e-09 -0.0287739 0.000112064 -1.20828e-05 0.000226261 --6.48796e-12 3.15206e-13 -0.0357105 0.000165801 -0.000122541 0.000213326 --6.04584e-13 -9.1423e-13 -0.039496 0.000256126 -0.000184194 0.000173953 --3.43845e-09 -5.61204e-09 -0.0418861 0.000354336 -0.000196159 0.000116679 --1.32711e-09 -3.77357e-09 -0.0435931 0.000432165 -0.000205434 5.44556e-05 --8.84851e-10 -1.20129e-09 -0.0449006 0.000486678 -0.00021699 -1.94434e-06 --5.24963e-10 -5.76388e-10 -0.0459542 0.000522631 -0.000231504 -4.91331e-05 --3.73173e-10 -2.87656e-10 -0.0468367 0.000546492 -0.000248755 -8.83256e-05 --1.57776e-10 -1.97663e-10 -0.0476016 0.00056194 -0.000266596 -0.000120985 --1.05981e-10 -1.36704e-10 -0.0482806 0.000573209 -0.000284402 -0.000148607 --8.16621e-11 -8.395e-11 -0.0488934 0.000582632 -0.00030246 -0.000171344 --5.97621e-11 -7.38941e-11 -0.0494526 0.000592648 -0.000319479 -0.000189977 --5.24705e-11 -6.39273e-11 -0.049968 0.000603322 -0.000335391 -0.000205732 --6.48408e-11 -5.51121e-11 -0.0504473 0.000613589 -0.000349256 -0.00021888 --5.83329e-11 -5.0542e-11 -0.0508964 0.00062281 -0.000360513 -0.0002297 diff --git a/test/data/voce_full_cyclic_csm.toml b/test/data/voce_full_cyclic_csm.toml index b715c6c..b2158a9 100644 --- a/test/data/voce_full_cyclic_csm.toml +++ b/test/data/voce_full_cyclic_csm.toml @@ -68,12 +68,12 @@ Version = "0.6.0" [3, -1, -2, -3], [3, -1, -2, -3]] #Vector of vals to be applied for each attribute - #The length of this should be #ids * dim of problem - essential_vals = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001], - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.001], - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001], - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.001], - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001]] + #The length of this should be #essential val ids * dim of problem + essential_vals = [[0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.0]] # Optional - Velocity gradient to be applied on the essential components and boundary IDs # The length of this should be a 3x3 matrix as given below. # As an example, we're supplying a matrix that should equate to the same uni-axial loading @@ -120,8 +120,6 @@ Version = "0.6.0" visit = false conduit = false paraview = false - floc = "./exaconstit_p1" - avg_stress_fname = "test_voce_full_cyclic_csm_stress.txt" [Solvers] # Option for how our assembly operation is conducted. Possible choices are # FULL, PA, EA diff --git a/test/data/voce_full_cyclic_csm_stress.txt b/test/data/voce_full_cyclic_csm_stress.txt deleted file mode 100644 index 4a76097..0000000 --- a/test/data/voce_full_cyclic_csm_stress.txt +++ /dev/null @@ -1,70 +0,0 @@ -9.33756e-11 9.4227e-11 0.0130582 -0.000115907 5.56805e-05 4.1481e-07 --7.7457e-12 3.90247e-11 0.0260799 -0.000236274 0.00010891 8.37972e-07 --3.11257e-11 1.28904e-09 0.0347779 -0.000334745 0.000156686 2.3619e-05 -8.68974e-12 1.03618e-10 0.0376799 -0.000416243 0.00014589 7.77951e-05 --7.10555e-14 2.46477e-13 0.0390056 -0.000465962 0.000140616 0.000126981 --2.59471e-14 -9.1968e-13 0.0398513 -0.000487842 0.000154308 0.000163597 -6.62271e-10 7.27616e-10 0.0404924 -0.000500438 0.000174139 0.000190225 -3.56947e-10 1.66159e-10 0.0410225 -0.000510985 0.000193496 0.00020846 -6.32376e-11 3.70838e-11 0.0414856 -0.000521751 0.000209545 0.000220469 --7.64258e-12 -1.66179e-11 0.0419046 -0.000531764 0.000222762 0.000228586 --4.72397e-11 1.87569e-11 0.0288444 -0.000415701 0.000167076 0.000228277 --2.53104e-12 3.04891e-12 0.0157835 -0.000299657 0.000111375 0.000227971 --2.17181e-11 -1.33763e-11 0.00272176 -0.000183629 5.56726e-05 0.00022767 --3.29478e-12 -9.13372e-12 -0.0103313 -6.58093e-05 1.0773e-06 0.000227397 --7.93755e-13 -1.14943e-12 -0.022454 8.07056e-05 -4.0261e-05 0.000223572 --3.9849e-12 -1.51111e-12 -0.0300067 0.000147914 -0.000113632 0.00019999 --1.87806e-12 -5.99228e-13 -0.0339279 0.00022252 -0.000139431 0.000155164 --1.47804e-12 -7.46175e-13 -0.036277 0.000318297 -0.000122673 9.62323e-05 --1.63358e-09 -4.62675e-09 -0.0378892 0.000391964 -0.000111382 3.66018e-05 --8.33489e-10 -2.29198e-09 -0.0391045 0.000437451 -0.000112843 -1.31006e-05 --3.94685e-10 -8.74197e-10 -0.0400772 0.000465071 -0.000126018 -5.48924e-05 --4.3062e-10 -3.86889e-10 -0.0408948 0.000483446 -0.000144 -9.11312e-05 --2.18316e-10 -2.3741e-10 -0.0416057 0.000496686 -0.000163642 -0.000122528 --9.43843e-11 -1.39935e-10 -0.0422382 0.000507379 -0.000184135 -0.000148785 --7.7106e-11 -1.07323e-10 -0.0428103 0.000516887 -0.000204642 -0.000169846 --7.4746e-11 -8.57166e-11 -0.0433344 0.000526563 -0.000224165 -0.000186973 --7.16206e-11 -6.96342e-11 -0.0438212 0.000536974 -0.000241403 -0.000200706 --7.0907e-11 -4.31925e-11 -0.0442781 0.000547505 -0.000255863 -0.000211618 --5.74232e-11 -3.33646e-11 -0.0447089 0.000557672 -0.000268672 -0.000220785 --4.35251e-11 -3.71763e-11 -0.0451167 0.000566915 -0.00028013 -0.000228782 --1.19206e-10 -1.00093e-10 -0.0320567 0.000451174 -0.000224472 -0.000228281 -1.01673e-11 3.93433e-12 -0.0189976 0.000335422 -0.000168805 -0.000227772 -1.75333e-11 9.31904e-12 -0.00593934 0.000219656 -0.000113138 -0.000227257 -5.99604e-16 -1.70542e-15 0.00711796 0.000103868 -5.74954e-05 -0.000226726 --1.04865e-10 1.08827e-09 0.0199704 -2.64307e-05 -7.41332e-06 -0.000226642 --1.33405e-12 -1.25921e-12 0.0297969 -0.00012777 6.48925e-05 -0.000219134 -1.50515e-11 -3.07492e-12 0.0350452 -0.000191179 0.000146697 -0.000191186 --9.11176e-13 -5.57155e-13 0.0380242 -0.000289075 0.00016336 -0.000139498 -1.56418e-09 4.17128e-09 0.0399979 -0.00037817 0.000160653 -7.77586e-05 -5.51097e-10 2.03453e-09 0.0414412 -0.00044141 0.000164926 -1.92223e-05 -3.41771e-10 3.9921e-10 0.0425703 -0.000481442 0.000175572 2.999e-05 -2.08739e-10 1.25805e-10 0.0434967 -0.000508068 0.000191683 7.12291e-05 -1.17076e-10 2.86677e-11 0.0442886 -0.000525566 0.000209554 0.000105541 -2.14628e-11 5.11148e-11 0.0449854 -0.000537753 0.000227831 0.00013481 --1.21912e-11 4.52101e-11 0.0456096 -0.00054746 0.000247231 0.000159067 --2.52358e-11 2.00411e-11 0.0461772 -0.000557251 0.000266005 0.000178436 --3.32848e-11 -2.18422e-12 0.046699 -0.000567695 0.000283605 0.000194535 --3.21394e-11 -1.45727e-11 0.0471842 -0.000578192 0.000299004 0.00020761 --2.73082e-11 -2.05462e-11 0.04764 -0.000588039 0.000311823 0.000218272 --1.11035e-11 -2.78752e-11 0.0480704 -0.000597171 0.000322727 0.000227428 --8.63459e-12 3.49744e-11 0.0350106 -0.000481089 0.000267021 0.000227103 -8.46883e-12 1.40305e-11 0.0219502 -0.000365028 0.000211315 0.000226788 --1.42285e-11 -6.46214e-12 0.00888892 -0.000248981 0.000155607 0.000226479 --2.8594e-11 -1.92816e-11 -0.0041732 -0.000132949 9.98995e-05 0.000226175 --7.45704e-12 -2.19349e-11 -0.0172076 -1.33539e-05 4.52922e-05 0.000225736 --3.29956e-09 -6.94566e-09 -0.0287739 0.000112064 -1.20828e-05 0.000226261 --6.48796e-12 3.15206e-13 -0.0357105 0.000165801 -0.000122541 0.000213326 --6.04584e-13 -9.1423e-13 -0.039496 0.000256126 -0.000184194 0.000173953 --3.43845e-09 -5.61204e-09 -0.0418861 0.000354336 -0.000196159 0.000116679 --1.32711e-09 -3.77357e-09 -0.0435931 0.000432165 -0.000205434 5.44556e-05 --8.84851e-10 -1.20129e-09 -0.0449006 0.000486678 -0.00021699 -1.94434e-06 --5.24963e-10 -5.76388e-10 -0.0459542 0.000522631 -0.000231504 -4.91331e-05 --3.73173e-10 -2.87656e-10 -0.0468367 0.000546492 -0.000248755 -8.83256e-05 --1.57776e-10 -1.97663e-10 -0.0476016 0.00056194 -0.000266596 -0.000120985 --1.05981e-10 -1.36704e-10 -0.0482806 0.000573209 -0.000284402 -0.000148607 --8.16621e-11 -8.395e-11 -0.0488934 0.000582632 -0.00030246 -0.000171344 --5.97621e-11 -7.38941e-11 -0.0494526 0.000592648 -0.000319479 -0.000189977 --5.24705e-11 -6.39273e-11 -0.049968 0.000603322 -0.000335391 -0.000205732 --6.48408e-11 -5.51121e-11 -0.0504473 0.000613589 -0.000349256 -0.00021888 --5.83329e-11 -5.0542e-11 -0.0508964 0.00062281 -0.000360513 -0.0002297 diff --git a/test/data/voce_full_cyclic_stress.txt b/test/data/voce_full_cyclic_stress.txt deleted file mode 100644 index 55903c6..0000000 --- a/test/data/voce_full_cyclic_stress.txt +++ /dev/null @@ -1,70 +0,0 @@ -9.33756e-11 9.42271e-11 0.0130582 -0.000115907 5.56805e-05 4.1481e-07 --7.66705e-12 4.01581e-11 0.0260786 -0.000236259 0.000108906 8.3777e-07 --3.10617e-11 1.27689e-09 0.0347765 -0.000334724 0.000156679 2.36065e-05 -8.58554e-12 1.03395e-10 0.0376788 -0.000416199 0.000145901 7.77616e-05 --6.94712e-14 2.52023e-13 0.0390044 -0.00046593 0.000140608 0.000126941 --2.89049e-14 -9.23182e-13 0.03985 -0.000487817 0.000154281 0.000163551 -6.65558e-10 7.25695e-10 0.0404909 -0.000500413 0.000174097 0.000190179 -3.56284e-10 1.65065e-10 0.0410208 -0.000510951 0.000193448 0.000208419 -6.31969e-11 3.65265e-11 0.0414836 -0.000521709 0.000209494 0.000220432 --7.95712e-12 -1.68788e-11 0.0419023 -0.000531716 0.000222708 0.000228555 --4.70898e-11 1.87644e-11 0.0288552 -0.00041577 0.000167077 0.000228246 -1.31069e-11 1.37142e-11 0.015806 -0.00029983 0.000111427 0.00022794 --4.38482e-11 -4.07802e-11 0.00275471 -0.000183895 5.57687e-05 0.00022764 --3.12931e-12 -8.62955e-12 -0.0102895 -6.61972e-05 1.19198e-06 0.000227366 --7.8049e-13 -1.13183e-12 -0.0224147 8.0259e-05 -4.01283e-05 0.000223588 --3.82882e-12 -1.52325e-12 -0.029984 0.000147697 -0.000113396 0.000200083 --1.94058e-12 -5.90178e-13 -0.0339139 0.000222093 -0.000139488 0.000155387 --1.46959e-12 -7.43238e-13 -0.0362672 0.000317874 -0.000122788 9.64997e-05 --1.65295e-09 -4.63513e-09 -0.0378817 0.000391671 -0.000111432 3.68559e-05 --8.34065e-10 -2.29578e-09 -0.0390985 0.00043728 -0.000112821 -1.28955e-05 --3.94132e-10 -8.73058e-10 -0.0400722 0.000464963 -0.000125955 -5.47192e-05 --4.28307e-10 -3.88351e-10 -0.0408906 0.000483377 -0.000143925 -9.09867e-05 --2.18567e-10 -2.38576e-10 -0.0416022 0.000496637 -0.000163566 -0.000122415 --9.46397e-11 -1.40538e-10 -0.0422352 0.000507341 -0.000184064 -0.000148704 --7.72126e-11 -1.07858e-10 -0.0428079 0.000516857 -0.00020458 -0.000169793 --7.48811e-11 -8.61346e-11 -0.0433324 0.000526536 -0.00022412 -0.000186942 --7.19548e-11 -7.00072e-11 -0.0438196 0.000536953 -0.000241377 -0.000200693 --7.12707e-11 -4.3462e-11 -0.044277 0.000547492 -0.000255853 -0.000211619 --5.78201e-11 -3.36e-11 -0.0447083 0.000557668 -0.000268674 -0.000220795 --4.38858e-11 -3.74196e-11 -0.0451164 0.000566919 -0.00028014 -0.000228799 --1.19219e-10 -1.00021e-10 -0.0320434 0.000451062 -0.000224427 -0.000228298 --1.38239e-11 -1.45805e-11 -0.0189725 0.000335206 -0.00016871 -0.000227789 -3.11931e-11 2.89587e-11 -0.00590382 0.000219347 -0.000112997 -0.000227273 --5.94737e-15 -7.83078e-15 0.00716262 0.000103478 -5.73167e-05 -0.000226741 --1.06378e-10 1.1123e-09 0.0200192 -2.70361e-05 -7.24312e-06 -0.000226651 --1.31985e-12 -1.23347e-12 0.0298287 -0.000127973 6.53514e-05 -0.000219068 -1.49255e-11 -3.0611e-12 0.0350629 -0.000191595 0.000146904 -0.000190982 --9.23788e-13 -5.663e-13 0.0380354 -0.00028959 0.000163312 -0.000139197 -1.5465e-09 4.17476e-09 0.0400056 -0.000378574 0.000160623 -7.7428e-05 -5.50754e-10 2.02032e-09 0.0414468 -0.000441666 0.00016493 -1.89337e-05 -3.40166e-10 4.01571e-10 0.0425744 -0.0004816 0.000175616 3.02271e-05 -2.10476e-10 1.25109e-10 0.0434997 -0.000508159 0.000191744 7.14199e-05 -1.16083e-10 2.87936e-11 0.0442908 -0.000525612 0.000209613 0.000105692 -2.14138e-11 5.06682e-11 0.0449868 -0.000537775 0.00022789 0.000134929 --1.22856e-11 4.50102e-11 0.0456105 -0.000547472 0.000247285 0.000159148 --2.52396e-11 1.97496e-11 0.0461775 -0.00055726 0.000266045 0.000178489 --3.33006e-11 -2.38394e-12 0.0466987 -0.000567699 0.000283629 0.000194567 --3.21065e-11 -1.47506e-11 0.0471834 -0.000578187 0.000299008 0.000207623 --2.7276e-11 -2.06449e-11 0.0476387 -0.000588024 0.000311811 0.000218271 --1.11593e-11 -2.78606e-11 0.0480685 -0.000597146 0.000322702 0.000227416 --8.72493e-12 3.47974e-11 0.0350219 -0.000481181 0.000267053 0.000227092 -4.19189e-11 4.1605e-11 0.0219732 -0.000365224 0.000211396 0.000226777 --1.86194e-11 -1.69671e-11 0.00892233 -0.00024927 0.000155733 0.000226467 --7.0585e-11 -6.79347e-11 -0.00413065 -0.000133319 0.000100064 0.000226164 --7.0752e-12 -2.08581e-11 -0.0171578 -1.38504e-05 4.5466e-05 0.000225719 --3.25507e-09 -6.85582e-09 -0.0287328 0.000111731 -1.16952e-05 0.000226254 --6.32236e-12 2.94751e-13 -0.0356866 0.000165478 -0.000122084 0.000213431 --6.07032e-13 -9.22184e-13 -0.0394809 0.000255608 -0.00018407 0.000174183 --3.45096e-09 -5.6182e-09 -0.0418752 0.000353872 -0.000196124 0.000116988 --1.32792e-09 -3.77437e-09 -0.0435847 0.000431815 -0.000205382 5.47629e-05 --8.85571e-10 -1.2065e-09 -0.0448938 0.000486446 -0.000216926 -1.68087e-06 --5.23545e-10 -5.78937e-10 -0.0459487 0.000522476 -0.000231421 -4.89215e-05 --3.72277e-10 -2.89054e-10 -0.0468321 0.000546395 -0.000248669 -8.81574e-05 --1.58208e-10 -1.98399e-10 -0.0475978 0.000561877 -0.000266515 -0.000120854 --1.06163e-10 -1.37357e-10 -0.0482775 0.000573164 -0.000284328 -0.000148507 --8.1767e-11 -8.43478e-11 -0.0488908 0.000582594 -0.000302396 -0.000171274 --5.99645e-11 -7.42511e-11 -0.0494507 0.000592614 -0.00031943 -0.00018993 --5.2669e-11 -6.43887e-11 -0.0499666 0.000603296 -0.000335357 -0.000205703 --6.51486e-11 -5.54321e-11 -0.0504463 0.000613573 -0.00034924 -0.000218867 --5.87099e-11 -5.07803e-11 -0.050896 0.000622804 -0.00036051 -0.0002297 diff --git a/test/data/voce_full_multi.toml b/test/data/voce_full_multi.toml new file mode 100644 index 0000000..c49bafb --- /dev/null +++ b/test/data/voce_full_multi.toml @@ -0,0 +1,212 @@ +# Multi-material ExaConstit configuration using the new structured format +# This example defines two identical materials for testing multi-material functionality +# and demonstrates the new boundary condition format +# +# Key differences from single-material format: +# 1. Uses [[Materials]] array instead of single Properties/Model sections +# 2. Uses structured BCs with separate velocity_bcs and velocity_gradient_bcs +# 3. Requires region_mapping_file to map mesh elements to materials +# 4. Each material is self-contained with its own properties and configuration +# +# To test legacy format: comment out the new BCs structure and uncomment legacy format +Version = "0.8.0" + +# Base simulation name +basename = "multi_material_test" + +# Region mapping file - this maps mesh element attributes to material indices +# Format should map element attribute IDs to material region IDs (0, 1, etc.) +region_mapping_file = "region_mapping.txt" + +# For auto-generated mesh with multiple materials, grain file is required +grain_file = "grains.txt" + +# Define multiple materials using the new Materials array structure +[[Materials]] + name = "material_A" + mech_type = "exacmech" + temperature = 298.0 + + # Material properties for first material + [Materials.Properties] + floc = "props_cp_voce.txt" + num_props = 17 + + # State variables for first material + [Materials.State_Vars] + floc = "state_cp_voce.txt" + num_vars = 24 + + # Grain information for first material + [Materials.Grain] + ori_state_var_loc = 9 + ori_stride = 4 + ori_type = "quat" + num_grains = 500 + orientation_file = "voce_quats.ori" + + # Model configuration for first material + [Materials.Model] + crystal_plasticity = true + [Materials.Model.ExaCMech] + shortcut = "evptn_FCC_A" + +[[Materials]] + name = "material_B" + mech_type = "exacmech" + temperature = 298.0 + + # Material properties for second material (identical for testing) + [Materials.Properties] + floc = "props_cp_voce.txt" + num_props = 17 + + # State variables for second material (identical for testing) + [Materials.State_Vars] + floc = "state_cp_voce.txt" + num_vars = 24 + + # Grain information for second material (identical for testing) + [Materials.Grain] + ori_state_var_loc = 9 + ori_stride = 4 + ori_type = "quat" + num_grains = 500 + orientation_file = "voce_quats.ori" + + # Model configuration for second material (identical for testing) + [Materials.Model] + crystal_plasticity = true + [Materials.Model.ExaCMech] + shortcut = "evptn_FCC_A" + +# Boundary conditions using new structured format +[BCs] + # Modern structured boundary conditions - separates velocity and velocity gradient BCs + + # Velocity boundary conditions - directly specify velocity values + [[BCs.velocity_bcs]] + essential_ids = [1, 2, 3] + essential_comps = [3, 1, 2] # 3=xyz constrained, 1=x constrained, 2=y constrained + essential_vals = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # 3 components × 3 boundaries + + # Velocity gradient boundary conditions - specify velocity gradient tensor + [[BCs.velocity_gradient_bcs]] + essential_ids = [4] + essential_comps = [3] # xyz components for velocity gradient + velocity_gradient = [[0.0, 0.0, 0.0], + [0.0, 0.0, 0.0], + [0.0, 0.0, 0.001]] # 3x3 matrix as flat array + origin = [0.0, 0.0, 0.0] # Optional origin point for velocity gradient application + + # Time-dependent settings (optional) + [BCs.time_info] + cycle_dependent = true + cycles = [1] # Apply these BCs starting at cycle 1 + + # Legacy format (commented out for comparison/testing) + # essential_ids = [1, 2, 3, 4] + # essential_comps = [3, 1, 2, -3] # Note: negative comp (-3) would indicate vgrad BC in legacy format + # essential_vals = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001] + # essential_vel_grad = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.001]] + + # For time-dependent BCs in legacy format, you would use: + # changing_ess_bcs = true + # update_steps = [1, 11, 21] + # essential_ids = [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]] + # essential_comps = [[3, 1, 2, -3], [3, 1, 2, -3], [3, 1, 2, -3]] + # essential_vals = [[...], [...], [...]] + + # Example: Adding a third material (commented out) + # [[Materials]] + # name = "material_C" + # mech_type = "exacmech" + # temperature = 350.0 # Different temperature + # [Materials.Properties] + # floc = "props_cp_different.txt" # Different properties + # num_props = 17 + # [Materials.State_Vars] + # floc = "state_cp_different.txt" + # num_vars = 28 + # [Materials.Grain] + # ori_state_var_loc = 9 + # ori_stride = 4 + # ori_type = "quat" + # num_grains = 1000 # Different grain count + # orientation_file = "different_quats.ori" + # [Materials.Model] + # crystal_plasticity = true + # [Materials.Model.ExaCMech] + # xtal_type = "BCC" # Different crystal type + # slip_type = "PowerVoceNL" + + # Example: Time-dependent BCs in new format (commented out) + # [[BCs.velocity_bcs]] + # essential_ids = [1, 2] + # essential_comps = [3, 1] + # essential_vals = [0.0, 0.0, 0.001, 0.0, 0.0, 0.0] # Different loading + # [BCs.velocity_bcs.time_info] + # cycle_dependent = true + # cycles = [1, 10, 20] # Multiple time steps with different values + +# Time stepping options (unchanged from original) +[Time] + [Time.Auto] + dt_start = 0.1 + dt_min = 0.05 + dt_scale = 0.333333 + t_final = 10.0 + auto_dt_file = "auto_dt_out.txt" + [Time.Fixed] + dt = 0.1 + t_final = 7.1 + [Time.Custom] + nsteps = 40 + floc = "custom_dt.txt" + +# Visualization options (unchanged from original) +[Visualizations] + steps = 1 + visit = false + conduit = false + paraview = false + +# Post-processing options (unchanged from original) +[PostProcessing] + [PostProcessing.volume_averages] + enabled = true + stress = true + def_grad = true + euler_strain = true + plastic_work = true + elastic_strain = true + output_frequency = 1 + output_directory = "./results" + +# Solver options (unchanged from original) +[Solvers] + assembly = "FULL" + rtmodel = "CPU" + + [Solvers.NR] + iter = 25 + rel_tol = 5e-5 + abs_tol = 5e-10 + + [Solvers.Krylov] + iter = 1000 + rel_tol = 1e-7 + abs_tol = 1e-27 + solver = "CG" + +# Mesh options (unchanged from original) +[Mesh] + ref_ser = 1 + ref_par = 0 + p_refinement = 1 + floc = "../../data/cube-hex-ro.mesh" + type = "auto" + + [Mesh.Auto] + mxyz = [1.0, 1.0, 1.0] + nxyz = [5, 5, 5] \ No newline at end of file diff --git a/test/data/voce_full_stress.txt b/test/data/voce_full_stress.txt deleted file mode 100644 index 25217f9..0000000 --- a/test/data/voce_full_stress.txt +++ /dev/null @@ -1,40 +0,0 @@ -1.1323e-14 1.14918e-14 0.00065299 -5.79514e-06 2.78435e-06 2.06205e-08 --1.74929e-11 6.2486e-11 0.0260676 -0.00023701 0.000108523 8.92024e-07 --3.09727e-11 1.29967e-09 0.0347754 -0.000334815 0.000156617 2.36076e-05 -8.57249e-12 1.05013e-10 0.0376783 -0.000416276 0.000145835 7.77595e-05 --6.96835e-14 2.52595e-13 0.039004 -0.000466011 0.000140531 0.000126939 --2.88024e-14 -9.23399e-13 0.0398496 -0.000487898 0.000154199 0.00016355 -6.65644e-10 7.26009e-10 0.0404905 -0.000500492 0.000174013 0.000190179 -3.56297e-10 1.65071e-10 0.0410204 -0.000511029 0.000193364 0.000208421 -6.32123e-11 3.65665e-11 0.0414833 -0.000521785 0.000209412 0.000220435 --7.96317e-12 -1.68662e-11 0.0419019 -0.000531791 0.000222628 0.000228557 --9.91649e-12 -3.48689e-11 0.0422908 -0.000540632 0.00023377 0.000233635 --2.12318e-11 -3.15477e-11 0.0426588 -0.000548748 0.000243502 0.000236699 --1.86446e-11 -2.55516e-11 0.0430117 -0.000556255 0.000252337 0.00023896 --1.82025e-11 -2.13713e-11 0.0433533 -0.000563145 0.000260516 0.000241038 --1.65201e-11 -1.64228e-11 0.0436863 -0.000569502 0.000268042 0.000242972 --1.34295e-11 -1.13381e-11 0.0440125 -0.00057523 0.000274952 0.000244837 --1.16848e-11 -9.38774e-12 0.0443332 -0.000580307 0.000281403 0.000246797 --1.10083e-11 -8.68636e-12 0.0446494 -0.000584845 0.000287503 0.000248764 --9.91368e-12 -7.78912e-12 0.0449618 -0.000589029 0.000293297 0.000250668 --8.48772e-12 -7.10863e-12 0.0452708 -0.000593026 0.000298776 0.000252493 --7.67844e-12 -6.95838e-12 0.045577 -0.00059691 0.000303874 0.000254284 --3.23786e-11 -2.76768e-11 0.0461806 -0.000604205 0.000312747 0.000257882 --3.54496e-11 -3.7973e-11 0.0467763 -0.000611176 0.000321037 0.000261075 --3.69093e-11 -3.8223e-11 0.0473653 -0.00061804 0.000329092 0.000263751 --3.37826e-11 -3.3375e-11 0.0479488 -0.000624868 0.000336758 0.000265951 --3.83946e-11 -3.3101e-11 0.0485275 -0.000631603 0.000343894 0.000267796 --3.83119e-11 -2.94192e-11 0.0491019 -0.000638161 0.000350717 0.000269457 --2.05811e-10 -1.34379e-10 0.0502367 -0.000650556 0.000364082 0.000272358 --1.52482e-10 -9.16594e-11 0.0513595 -0.000662034 0.000376476 0.000274824 --1.07012e-10 -3.21756e-11 0.052472 -0.000672321 0.000387711 0.000276984 --8.65479e-11 -1.10858e-11 0.0535752 -0.000681421 0.000398503 0.000278587 --1.75021e-11 -1.0566e-11 0.0541247 -0.000685778 0.000403837 0.000279268 -5.7058e-13 8.49216e-14 0.0557565 -0.000698396 0.000419273 0.000280938 --1.2312e-10 -6.9299e-11 0.0571058 -0.000708921 0.000431053 0.000282 --1.03261e-10 -4.80733e-11 0.0584454 -0.000719284 0.000441976 0.000282917 -3.22126e-13 -6.97277e-14 0.0604342 -0.000734841 0.000457167 0.000284812 -3.485e-13 3.62102e-13 0.0624036 -0.000750468 0.00047115 0.00028733 -1.75547e-13 3.94911e-14 0.0643543 -0.000766174 0.000485138 0.000291061 --2.2377e-10 -1.15172e-10 0.0662874 -0.000781978 0.000499387 0.000295823 --4.12583e-13 -9.19805e-13 0.0688347 -0.000803113 0.000518232 0.000302891 diff --git a/test/data/voce_nl_full.toml b/test/data/voce_nl_full.toml index 5fdaaf4..af278be 100644 --- a/test/data/voce_nl_full.toml +++ b/test/data/voce_nl_full.toml @@ -85,8 +85,6 @@ Version = "0.6.0" visit = false conduit = false paraview = false - floc = "./exaconstit_p1" - avg_stress_fname = "test_voce_nl_full_stress.txt" [Solvers] # Option for how our assembly operation is conducted. Possible choices are # FULL, PA, EA diff --git a/test/data/voce_pa.toml b/test/data/voce_pa.toml index 2ea6c93..fc1cf7a 100644 --- a/test/data/voce_pa.toml +++ b/test/data/voce_pa.toml @@ -85,8 +85,6 @@ Version = "0.6.0" visit = false conduit = false paraview = false - floc = "./exaconstit_p1" - avg_stress_fname = "test_voce_pa_stress.txt" [Solvers] # Option for how our assembly operation is conducted. Possible choices are # FULL, PA, EA diff --git a/test/data/voce_pa_stress.txt b/test/data/voce_pa_stress.txt deleted file mode 100644 index a5a1092..0000000 --- a/test/data/voce_pa_stress.txt +++ /dev/null @@ -1,40 +0,0 @@ -1.15975e-14 1.18875e-14 0.00065299 -5.79514e-06 2.78435e-06 2.06205e-08 --1.76189e-11 6.2379e-11 0.0260676 -0.00023701 0.000108523 8.92024e-07 --3.0695e-11 1.29947e-09 0.0347754 -0.000334815 0.000156617 2.36076e-05 -8.61222e-12 1.04996e-10 0.0376783 -0.000416276 0.000145835 7.77595e-05 --6.89314e-14 2.51215e-13 0.039004 -0.000466011 0.000140531 0.000126939 --2.78742e-14 -9.26124e-13 0.0398496 -0.000487898 0.000154199 0.00016355 -6.65551e-10 7.25781e-10 0.0404905 -0.000500492 0.000174013 0.000190179 -3.56314e-10 1.65203e-10 0.0410204 -0.000511029 0.000193364 0.000208421 -6.33339e-11 3.66425e-11 0.0414833 -0.000521785 0.000209412 0.000220435 --7.95187e-12 -1.69265e-11 0.0419019 -0.000531791 0.000222628 0.000228557 --9.85872e-12 -3.48575e-11 0.0422908 -0.000540632 0.00023377 0.000233635 --2.12364e-11 -3.15784e-11 0.0426588 -0.000548748 0.000243502 0.000236699 --1.86288e-11 -2.55406e-11 0.0430117 -0.000556255 0.000252337 0.00023896 --1.82067e-11 -2.13789e-11 0.0433533 -0.000563145 0.000260516 0.000241038 --1.65155e-11 -1.64232e-11 0.0436863 -0.000569502 0.000268042 0.000242972 --1.34241e-11 -1.13355e-11 0.0440125 -0.00057523 0.000274952 0.000244837 --1.16797e-11 -9.38486e-12 0.0443332 -0.000580307 0.000281403 0.000246797 --1.10043e-11 -8.68552e-12 0.0446494 -0.000584845 0.000287503 0.000248764 --9.90976e-12 -7.78748e-12 0.0449618 -0.000589029 0.000293297 0.000250668 --8.4821e-12 -7.10313e-12 0.0452708 -0.000593026 0.000298776 0.000252493 --7.67077e-12 -6.95202e-12 0.045577 -0.00059691 0.000303874 0.000254284 --3.23782e-11 -2.76762e-11 0.0461806 -0.000604205 0.000312747 0.000257882 --3.5419e-11 -3.79564e-11 0.0467763 -0.000611176 0.000321037 0.000261075 --3.6897e-11 -3.82196e-11 0.0473653 -0.00061804 0.000329092 0.000263751 --3.3779e-11 -3.33733e-11 0.0479488 -0.000624868 0.000336758 0.000265951 --3.83917e-11 -3.31005e-11 0.0485275 -0.000631603 0.000343894 0.000267796 --3.83192e-11 -2.94262e-11 0.0491019 -0.000638161 0.000350717 0.000269457 --2.05841e-10 -1.34424e-10 0.0502367 -0.000650556 0.000364082 0.000272358 --1.52526e-10 -9.17027e-11 0.0513595 -0.000662034 0.000376476 0.000274824 --1.07025e-10 -3.21802e-11 0.052472 -0.000672321 0.000387711 0.000276984 --8.65673e-11 -1.11112e-11 0.0535752 -0.000681421 0.000398503 0.000278587 --1.75039e-11 -1.05668e-11 0.0541247 -0.000685778 0.000403837 0.000279268 -5.70071e-13 8.4366e-14 0.0557565 -0.000698396 0.000419273 0.000280938 --1.23139e-10 -6.93074e-11 0.0571058 -0.000708921 0.000431053 0.000282 --1.03264e-10 -4.80715e-11 0.0584454 -0.000719284 0.000441976 0.000282917 -3.22385e-13 -6.95084e-14 0.0604342 -0.000734841 0.000457167 0.000284812 -3.48625e-13 3.62374e-13 0.0624036 -0.000750468 0.00047115 0.00028733 -1.7556e-13 3.93054e-14 0.0643543 -0.000766174 0.000485138 0.000291061 --2.23758e-10 -1.15124e-10 0.0662874 -0.000781978 0.000499387 0.000295823 --4.12799e-13 -9.19989e-13 0.0688347 -0.000803113 0.000518232 0.000302891 diff --git a/test/grad_test.cpp b/test/grad_test.cpp index 27d44d5..2c919d1 100644 --- a/test/grad_test.cpp +++ b/test/grad_test.cpp @@ -1,9 +1,10 @@ +#include "utilities/mechanics_kernels.hpp" + #include "mfem.hpp" #include "mfem/general/forall.hpp" -#include #include "RAJA/RAJA.hpp" -#include "mechanics_kernels.hpp" +#include #include using namespace std; @@ -71,7 +72,7 @@ double test_main_body() } const int intOrder = 2 * order + 1; - mfem::QuadratureSpace *qspace = new mfem::QuadratureSpace(pmesh, intOrder); + std::shared_ptr qspace = std::make_shared(mfem::ptr_utils::borrow_ptr(pmesh), intOrder); mfem::QuadratureFunction raderiv(qspace, 9); mfem::QuadratureFunction rderiv(qspace, 9); @@ -85,7 +86,7 @@ double test_main_body() { auto coord = mfem::Reshape(raderiv.ReadWrite(), 3, 3, nqpts, nelems); // u_vec = (2x + 3y + 4z)i + (4x + 2y + 3z)j + (3x + 4y + 2z)k - mfem::MFEM_FORALL(i, nelems, { + mfem::forall(nelems, [=] MFEM_HOST_DEVICE (int i) { for(int j = 0; j < nqpts; j++) { coord(0, 0, j, i) = 3.0; coord(0, 1, j, i) = 3.0; @@ -179,7 +180,7 @@ double test_main_body() } } rderiv = 0.0; - exaconstit::kernel::grad_calc(nqpts, nelems, ndofs, el_jac.Read(), qpts_dshape.Read(), el_x.Read(), rderiv.ReadWrite()); + exaconstit::kernel::GradCalc(nqpts, nelems, ndofs, el_jac.Read(), qpts_dshape.Read(), el_x.Read(), rderiv.ReadWrite()); } raderiv -= rderiv; diff --git a/test/mechanics_test.cpp b/test/mechanics_test.cpp deleted file mode 100644 index fad807c..0000000 --- a/test/mechanics_test.cpp +++ /dev/null @@ -1,840 +0,0 @@ - -#include "mfem.hpp" -#include "mfem/general/forall.hpp" -#include "mechanics_integrators.hpp" -#include "mechanics_umat.hpp" -#include -#include -#include "RAJA/RAJA.hpp" - -#include - -using namespace std; -using namespace mfem; - -static int outputLevel = 0; - -class test_model : public ExaModel -{ - public: - - test_model(mfem::QuadratureFunction *q_stress0, mfem::QuadratureFunction *q_stress1, - mfem::QuadratureFunction *q_matGrad, mfem::QuadratureFunction *q_matVars0, - mfem::QuadratureFunction *q_matVars1, - mfem::ParGridFunction* _beg_coords, mfem::ParGridFunction* _end_coords, - mfem::Vector *props, int nProps, int nStateVars, Assembly _assembly) : - ExaModel(q_stress0, - q_stress1, q_matGrad, q_matVars0, - q_matVars1, - _beg_coords, _end_coords, - props, nProps, nStateVars, _assembly) {} - - virtual ~test_model() {} - - void UpdateModelVars() {} - - void ModelSetup(const int, const int, const int, - const int, const mfem::Vector &, - const mfem::Vector &, const mfem::Vector &) {} - virtual void calcDpMat(mfem::QuadratureFunction & /*DpMat*/) const {}; -}; - -// This function will either set our CMat array to all ones or something resembling a cubic symmetry like system. -template -void setCMat(QuadratureFunction &cmat_data); - -// This function compares the difference in the formation of the GetGradient operator and then multiplying it -// by the necessary vector, and the matrix-free partial assembly formulation which avoids forming the matrix. -// It's been tested on higher order elements and multiple elements. The difference in these two methods -// should be 0.0. -template -double ExaNLFIntegratorPATest() -{ - int dim = 3; - int order = 3; - mfem::ParMesh *pmesh = nullptr; - { - // Making this mesh and test real simple with 8 cubic element - mfem::Mesh mesh = Mesh::MakeCartesian3D(2, 2, 2, Element::HEXAHEDRON, 1.0, 1.0, 1.0, false); - mesh.SetCurvature(order); - pmesh = new mfem::ParMesh(MPI_COMM_WORLD, mesh); - } - H1_FECollection fec(order, dim); - - ParFiniteElementSpace fes(pmesh, &fec, dim); - - // All of these Quadrature function variables are needed to instantiate our material model - // We can just ignore this marked section - ///////////////////////////////////////////////////////////////////////////////////////// - // Define a quadrature space and material history variable QuadratureFunction. - int intOrder = 2 * order + 1; - QuadratureSpace qspace(pmesh, intOrder); // 3rd order polynomial for 2x2x2 quadrature - // for first order finite elements. - QuadratureFunction q_matVars0(&qspace, 1); - QuadratureFunction q_matVars1(&qspace, 1); - QuadratureFunction q_sigma0(&qspace, 1); - QuadratureFunction q_sigma1(&qspace, 1); - // We'll modify this before doing the partial assembly - // This is our stiffness matrix and is a 6x6 due to major and minor symmetry - // of the 4th order tensor which has dimensions 3x3x3x3. - QuadratureFunction q_matGrad(&qspace, 36); - QuadratureFunction q_kinVars0(&qspace, 9); - QuadratureFunction q_vonMises(&qspace, 1); - ParGridFunction beg_crds(&fes); - ParGridFunction end_crds(&fes); - // We'll want to update this later in case we do anything more complicated. - Vector matProps(1); - - end_crds = 1.0; - - ExaModel *model; - // This doesn't really matter and is just needed for the integrator class. - model = new AbaqusUmatModel(&q_sigma0, &q_sigma1, &q_matGrad, &q_matVars0, &q_matVars1, &q_kinVars0, - &beg_crds, &end_crds, &matProps, 1, 1, &fes, Assembly::PA); - // Model time needs to be set. - model->SetModelDt(1.0); - ///////////////////////////////////////////////////////////////////////////// - ExaNLFIntegrator* nlf_int; - - nlf_int = new ExaNLFIntegrator(dynamic_cast(model)); - - const FiniteElement &el = *fes.GetFE(0); - ElementTransformation *Ttr; - - // So, we're going to originally support non tensor-product type elements originally. - const ElementDofOrdering ordering = ElementDofOrdering::NATIVE; - // const ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC; - const Operator *elem_restrict_lex; - elem_restrict_lex = fes.GetElementRestriction(ordering); - // Set our field variable to a linear spacing so 1 ... ndofs in field - Vector xtrue(end_crds.Size()); - for (int i = 0; i < xtrue.Size(); i++) { - xtrue(i) = i + 1; - } - - // For multiple elements xtrue and local_x are differently sized - Vector local_x(elem_restrict_lex->Height()); - // All of our local global solution variables - Vector y_fa(end_crds.Size()); - Vector local_y_fa(elem_restrict_lex->Height()); - Vector local_y_pa(elem_restrict_lex->Height()); - Vector y_pa(end_crds.Size()); - // Initializing them all to 1. - y_fa = 0.0; - y_pa = 0.0; - local_y_pa = 0.0; - local_y_fa = 0.0; - // Get our local x values (element values) from the global vector - elem_restrict_lex->Mult(xtrue, local_x); - // Variables used to kinda mimic what the NonlinearForm::GetGradient does. - int ndofs = el.GetDof() * el.GetDim(); - Vector elfun(ndofs), elresults(ndofs); - DenseMatrix elmat; - - // Set our CMat array for the non-PA case - q_matGrad = 0.0; - setCMat(q_matGrad); - elfun.HostReadWrite(); - elresults.HostReadWrite(); - local_x.HostReadWrite(); - local_y_fa.HostReadWrite(); - for (int i = 0; i < fes.GetNE(); i++) { - Ttr = fes.GetElementTransformation(i); - for (int j = 0; j < ndofs; j++) { - elfun(j) = local_x((i * ndofs) + j); - } - - nlf_int->AssembleElementGrad(el, *Ttr, elfun, elmat); - // Getting out the local action of our gradient operator on - // the local x values and then saving the results off to the - // global variable. - elresults = 0.0; - elmat.AddMult(elfun, elresults); - for (int j = 0; j < ndofs; j++) { - local_y_fa((i * ndofs) + j) = elresults(j); - } - } - - // This takes our 2d cmat and transforms it into the 4d version - model->TransformMatGradTo4D(); - // Perform the setup and action operation of our PA operation - nlf_int->AssembleGradPA(fes); - nlf_int->AddMultGradPA(local_x, local_y_pa); - - // Take all of our multiple elements and go back to the L vector. - elem_restrict_lex->MultTranspose(local_y_fa, y_fa); - elem_restrict_lex->MultTranspose(local_y_pa, y_pa); - // Find out how different our solutions were from one another. - double mag = y_fa.Norml2(); - std::cout << "y_fa mag: " << mag << std::endl; - y_fa -= y_pa; - double difference = y_fa.Norml2(); - // Free up memory now. - delete nlf_int; - delete model; - delete pmesh; - - return difference / mag; -} - -// This function compares the difference in the formation of the Mult operator and then multiplying it -// by the necessary vector, and the matrix-free partial assembly formulation. -// It's been tested on higher order elements and multiple elements. The difference in these two methods -// should be 0.0. -double ExaNLFIntegratorPAVecTest() -{ - int dim = 3; - int order = 6; - mfem::ParMesh *pmesh = nullptr; - { - // Making this mesh and test real simple with 8 cubic element - mfem::Mesh mesh = Mesh::MakeCartesian3D(2, 2, 2, Element::HEXAHEDRON, 1.0, 1.0, 1.0, false); - mesh.SetCurvature(order); - pmesh = new mfem::ParMesh(MPI_COMM_WORLD, mesh); - } - - H1_FECollection fec(order, dim); - ParFiniteElementSpace fes(pmesh, &fec, dim); - - // All of these Quadrature function variables are needed to instantiate our material model - // We can just ignore this marked section - ///////////////////////////////////////////////////////////////////////////////////////// - // Define a quadrature space and material history variable QuadratureFunction. - int intOrder = 2 * order + 1; - QuadratureSpace qspace(pmesh, intOrder); // 3rd order polynomial for 2x2x2 quadrature - // for first order finite elements. - QuadratureFunction q_matVars0(&qspace, 1); - QuadratureFunction q_matVars1(&qspace, 1); - QuadratureFunction q_sigma0(&qspace, 6); - QuadratureFunction q_sigma1(&qspace, 6); - q_sigma1 = 1.0; - q_sigma0 = 1.0; - // We'll modify this before doing the partial assembly - // This is our stiffness matrix and is a 6x6 due to major and minor symmetry - // of the 4th order tensor which has dimensions 3x3x3x3. - QuadratureFunction q_matGrad(&qspace, 36); - QuadratureFunction q_kinVars0(&qspace, 9); - QuadratureFunction q_vonMises(&qspace, 1); - ParGridFunction beg_crds(&fes); - ParGridFunction end_crds(&fes); - // We'll want to update this later in case we do anything more complicated. - Vector matProps(1); - - end_crds = 1.0; - - ExaModel *model; - // This doesn't really matter and is just needed for the integrator class. - model = new test_model(&q_sigma0, &q_sigma1, &q_matGrad, &q_matVars0, &q_matVars1, - &beg_crds, &end_crds, &matProps, 1, 1, Assembly::PA); - // Model time needs to be set. - model->SetModelDt(1.0); - ///////////////////////////////////////////////////////////////////////////// - ExaNLFIntegrator* nlf_int; - - nlf_int = new ExaNLFIntegrator(dynamic_cast(model)); - - const FiniteElement &el = *fes.GetFE(0); - ElementTransformation *Ttr; - - // So, we're going to originally support non tensor-product type elements originally. - const ElementDofOrdering ordering = ElementDofOrdering::NATIVE; - // const ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC; - const Operator *elem_restrict_lex; - elem_restrict_lex = fes.GetElementRestriction(ordering); - // Set our field variable to a linear spacing so 1 ... ndofs in field - Vector xtrue(end_crds.Size()); - for (int i = 0; i < xtrue.Size(); i++) { - xtrue(i) = i + 1; - } - - // For multiple elements xtrue and local_x are differently sized - Vector local_x(elem_restrict_lex->Height()); - // All of our local global solution variables - Vector y_fa(end_crds.Size()); - Vector local_y_fa(elem_restrict_lex->Height()); - Vector local_y_pa(elem_restrict_lex->Height()); - Vector y_pa(end_crds.Size()); - // Initializing them all to 1. - y_fa = 0.0; - y_pa = 0.0; - local_y_pa = 0.0; - local_y_fa = 0.0; - // Get our local x values (element values) from the global vector - elem_restrict_lex->Mult(xtrue, local_x); - // Variables used to kinda mimic what the NonlinearForm::GetGradient does. - int ndofs = el.GetDof() * el.GetDim(); - Vector elfun(ndofs), elresults(ndofs); - DenseMatrix elmat; - elfun.HostReadWrite(); - elresults.HostReadWrite(); - local_x.HostReadWrite(); - local_y_fa.HostReadWrite(); - for (int i = 0; i < fes.GetNE(); i++) { - Ttr = fes.GetElementTransformation(i); - for (int j = 0; j < ndofs; j++) { - elfun(j) = local_x((i * ndofs) + j); - } - - elresults = 0.0; - nlf_int->AssembleElementVector(el, *Ttr, elfun, elresults); - for (int j = 0; j < ndofs; j++) { - local_y_fa((i * ndofs) + j) = elresults(j); - } - } - - // Perform the setup and action operation of our PA operation - nlf_int->AssemblePA(fes); - nlf_int->AddMultPA(local_x, local_y_pa); - - // Take all of our multiple elements and go back to the L vector. - elem_restrict_lex->MultTranspose(local_y_fa, y_fa); - elem_restrict_lex->MultTranspose(local_y_pa, y_pa); - // Find out how different our solutions were from one another. - double mag = y_fa.Norml2(); - std::cout << "y_fa mag: " << mag << std::endl; - y_fa -= y_pa; - double difference = y_fa.Norml2(); - // Free up memory now. - delete nlf_int; - delete model; - delete pmesh; - - return difference / mag; -} - -// This function compares the difference in the formation of the GetGradient operator and then multiplying it -// by the necessary vector, and the matrix-free partial assembly formulation which avoids forming the matrix. -// It's been tested on higher order elements and multiple elements. The difference in these two methods -// should be 0.0. -template -double ExaNLFIntegratorEATest() -{ - int dim = 3; - int order = 3; - mfem::ParMesh *pmesh = nullptr; - { - // Making this mesh and test real simple with 8 cubic element - mfem::Mesh mesh = Mesh::MakeCartesian3D(2, 2, 2, Element::HEXAHEDRON, 1.0, 1.0, 1.0, false); - mesh.SetCurvature(order); - pmesh = new mfem::ParMesh(MPI_COMM_WORLD, mesh); - } - - H1_FECollection fec(order, dim); - ParFiniteElementSpace fes(pmesh, &fec, dim); - - - // All of these Quadrature function variables are needed to instantiate our material model - // We can just ignore this marked section - ///////////////////////////////////////////////////////////////////////////////////////// - // Define a quadrature space and material history variable QuadratureFunction. - int intOrder = 2 * order + 1; - QuadratureSpace qspace(pmesh, intOrder); // 3rd order polynomial for 2x2x2 quadrature - // for first order finite elements. - QuadratureFunction q_matVars0(&qspace, 1); - QuadratureFunction q_matVars1(&qspace, 1); - QuadratureFunction q_sigma0(&qspace, 1); - QuadratureFunction q_sigma1(&qspace, 1); - // We'll modify this before doing the partial assembly - // This is our stiffness matrix and is a 6x6 due to major and minor symmetry - // of the 4th order tensor which has dimensions 3x3x3x3. - QuadratureFunction q_matGrad(&qspace, 36); - QuadratureFunction q_kinVars0(&qspace, 9); - QuadratureFunction q_vonMises(&qspace, 1); - ParGridFunction beg_crds(&fes); - ParGridFunction end_crds(&fes); - // We'll want to update this later in case we do anything more complicated. - Vector matProps(1); - - end_crds = 1.0; - - const int NE = fes.GetMesh()->GetNE(); - const int elemDofs = fes.GetFE(0)->GetDof() * fes.GetFE(0)->GetDim();; - - Vector ea_data; - - ea_data.SetSize(NE * elemDofs * elemDofs, Device::GetMemoryType()); - ea_data.UseDevice(true); - - ExaModel *model; - // This doesn't really matter and is just needed for the integrator class. - model = new AbaqusUmatModel(&q_sigma0, &q_sigma1, &q_matGrad, &q_matVars0, &q_matVars1, &q_kinVars0, - &beg_crds, &end_crds, &matProps, 1, 1, &fes, Assembly::PA); - // Model time needs to be set. - model->SetModelDt(1.0); - ///////////////////////////////////////////////////////////////////////////// - ExaNLFIntegrator* nlf_int; - - nlf_int = new ExaNLFIntegrator(dynamic_cast(model)); - - const FiniteElement &el = *fes.GetFE(0); - ElementTransformation *Ttr; - - // So, we're going to originally support non tensor-product type elements originally. - const ElementDofOrdering ordering = ElementDofOrdering::NATIVE; - // const ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC; - const Operator *elem_restrict_lex; - elem_restrict_lex = fes.GetElementRestriction(ordering); - // Set our field variable to a linear spacing so 1 ... ndofs in field - Vector xtrue(end_crds.Size()); - for (int i = 0; i < xtrue.Size(); i++) { - xtrue(i) = i + 1; - } - - // For multiple elements xtrue and local_x are differently sized - Vector local_x(elem_restrict_lex->Height()); - // All of our local global solution variables - Vector y_fa(end_crds.Size()); - Vector local_y_fa(elem_restrict_lex->Height()); - Vector local_y_ea(elem_restrict_lex->Height()); - Vector y_ea(end_crds.Size()); - // Initializing them all to 1. - y_fa = 0.0; - y_ea = 0.0; - local_y_ea = 0.0; - local_y_fa = 0.0; - // Get our local x values (element values) from the global vector - elem_restrict_lex->Mult(xtrue, local_x); - // Variables used to kinda mimic what the NonlinearForm::GetGradient does. - int ndofs = el.GetDof() * el.GetDim(); - Vector elfun(ndofs), elresults(ndofs); - DenseMatrix elmat; - - // Set our CMat array for the non-PA case - q_matGrad = 0.0; - setCMat(q_matGrad); - elfun.HostReadWrite(); - elresults.HostReadWrite(); - local_x.HostReadWrite(); - local_y_fa.HostReadWrite(); - for (int i = 0; i < fes.GetNE(); i++) { - Ttr = fes.GetElementTransformation(i); - for (int j = 0; j < ndofs; j++) { - elfun(j) = local_x((i * ndofs) + j); - } - - nlf_int->AssembleElementGrad(el, *Ttr, elfun, elmat); - // Getting out the local action of our gradient operator on - // the local x values and then saving the results off to the - // global variable. - elresults = 0.0; - elmat.AddMult(elfun, elresults); - for (int j = 0; j < ndofs; j++) { - local_y_fa((i * ndofs) + j) = elresults(j); - } - } - - // Perform the setup and action operation of our PA operation - nlf_int->AssembleEA(fes, ea_data); - - const bool useRestrict = true && elem_restrict_lex; - // Apply the Element Matrices - const int NDOFS = elemDofs; - auto X = Reshape(useRestrict?local_x.HostRead():xtrue.HostRead(), NDOFS, NE); - auto Y = Reshape(useRestrict?local_y_ea.HostReadWrite():y_ea.HostReadWrite(), NDOFS, NE); - auto A = Reshape(ea_data.HostRead(), NDOFS, NDOFS, NE); - for(int glob_j = 0; glob_j < NE*NDOFS; glob_j++) - { - const int e = glob_j/NDOFS; - const int j = glob_j%NDOFS; - double res = 0.0; - for (int i = 0; i < NDOFS; i++) - { - res += A(i, j, e)*X(i, e); - } - Y(j, e) += res; - } - - // Take all of our multiple elements and go back to the L vector. - elem_restrict_lex->MultTranspose(local_y_fa, y_fa); - elem_restrict_lex->MultTranspose(local_y_ea, y_ea); - // Find out how different our solutions were from one another. - double mag = y_fa.Norml2(); - std::cout << "y_fa mag: " << mag << std::endl; - y_fa -= y_ea; - double difference = y_fa.Norml2(); - // Free up memory now. - delete nlf_int; - delete model; - delete pmesh; - - return difference / mag; -} - -// This function compares the difference in the formation of the GetGradient operator and then multiplying it -// by the necessary vector, and the matrix-free partial assembly formulation which avoids forming the matrix. -// It's been tested on higher order elements and multiple elements. The difference in these two methods -// should be 0.0. -template -double ICExaNLFIntegratorEATest() -{ - int dim = 3; - int order = 3; - mfem::ParMesh *pmesh = nullptr; - { - // Making this mesh and test real simple with 8 cubic element - mfem::Mesh mesh = Mesh::MakeCartesian3D(2, 2, 2, Element::HEXAHEDRON, 1.0, 1.0, 1.0, false); - mesh.SetCurvature(order); - pmesh = new mfem::ParMesh(MPI_COMM_WORLD, mesh); - } - - H1_FECollection fec(order, dim); - ParFiniteElementSpace fes(pmesh, &fec, dim); - - // All of these Quadrature function variables are needed to instantiate our material model - // We can just ignore this marked section - ///////////////////////////////////////////////////////////////////////////////////////// - // Define a quadrature space and material history variable QuadratureFunction. - int intOrder = 2 * order + 1; - QuadratureSpace qspace(pmesh, intOrder); // 3rd order polynomial for 2x2x2 quadrature - // for first order finite elements. - QuadratureFunction q_matVars0(&qspace, 1); - QuadratureFunction q_matVars1(&qspace, 1); - QuadratureFunction q_sigma0(&qspace, 1); - QuadratureFunction q_sigma1(&qspace, 1); - // We'll modify this before doing the partial assembly - // This is our stiffness matrix and is a 6x6 due to major and minor symmetry - // of the 4th order tensor which has dimensions 3x3x3x3. - QuadratureFunction q_matGrad(&qspace, 36); - QuadratureFunction q_kinVars0(&qspace, 9); - QuadratureFunction q_vonMises(&qspace, 1); - ParGridFunction beg_crds(&fes); - ParGridFunction end_crds(&fes); - // We'll want to update this later in case we do anything more complicated. - Vector matProps(1); - - end_crds = 1.0; - - const int NE = fes.GetMesh()->GetNE(); - const int elemDofs = fes.GetFE(0)->GetDof() * fes.GetFE(0)->GetDim();; - - Vector ea_data; - - ea_data.SetSize(NE * elemDofs * elemDofs, Device::GetMemoryType()); - ea_data.UseDevice(true); - - ExaModel *model; - // This doesn't really matter and is just needed for the integrator class. - model = new AbaqusUmatModel(&q_sigma0, &q_sigma1, &q_matGrad, &q_matVars0, &q_matVars1, &q_kinVars0, - &beg_crds, &end_crds, &matProps, 1, 1, &fes, Assembly::PA); - // Model time needs to be set. - model->SetModelDt(1.0); - ///////////////////////////////////////////////////////////////////////////// - ExaNLFIntegrator* nlf_int; - - nlf_int = new ICExaNLFIntegrator(dynamic_cast(model)); - - const FiniteElement &el = *fes.GetFE(0); - ElementTransformation *Ttr; - - // So, we're going to originally support non tensor-product type elements originally. - const ElementDofOrdering ordering = ElementDofOrdering::NATIVE; - // const ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC; - const Operator *elem_restrict_lex; - elem_restrict_lex = fes.GetElementRestriction(ordering); - // Set our field variable to a linear spacing so 1 ... ndofs in field - Vector xtrue(end_crds.Size()); - for (int i = 0; i < xtrue.Size(); i++) { - xtrue(i) = i + 1; - } - - // For multiple elements xtrue and local_x are differently sized - Vector local_x(elem_restrict_lex->Height()); - // All of our local global solution variables - Vector y_fa(end_crds.Size()); - Vector local_y_fa(elem_restrict_lex->Height()); - Vector local_y_ea(elem_restrict_lex->Height()); - Vector y_ea(end_crds.Size()); - // Initializing them all to 1. - y_fa = 0.0; - y_ea = 0.0; - local_y_ea = 0.0; - local_y_fa = 0.0; - // Get our local x values (element values) from the global vector - elem_restrict_lex->Mult(xtrue, local_x); - // Variables used to kinda mimic what the NonlinearForm::GetGradient does. - int ndofs = el.GetDof() * el.GetDim(); - Vector elfun(ndofs), elresults(ndofs); - DenseMatrix elmat; - - // Set our CMat array for the non-PA case - q_matGrad = 0.0; - setCMat(q_matGrad); - elfun.HostReadWrite(); - elresults.HostReadWrite(); - local_x.HostReadWrite(); - local_y_fa.HostReadWrite(); - - for (int i = 0; i < fes.GetNE(); i++) { - Ttr = fes.GetElementTransformation(i); - for (int j = 0; j < ndofs; j++) { - elfun(j) = local_x((i * ndofs) + j); - } - - nlf_int->AssembleElementGrad(el, *Ttr, elfun, elmat); - // Getting out the local action of our gradient operator on - // the local x values and then saving the results off to the - // global variable. - elresults = 0.0; - elmat.AddMult(elfun, elresults); - for (int j = 0; j < ndofs; j++) { - local_y_fa((i * ndofs) + j) = elresults(j); - } - } - - // Perform the setup and action operation of our PA operation - nlf_int->AssemblePA(fes); - ea_data = 0.0; - nlf_int->AssembleEA(fes, ea_data); - - const bool useRestrict = true && elem_restrict_lex; - // Apply the Element Matrices - const int NDOFS = elemDofs; - auto X = Reshape(useRestrict?local_x.HostRead():xtrue.HostRead(), NDOFS, NE); - auto Y = Reshape(useRestrict?local_y_ea.HostReadWrite():y_ea.HostReadWrite(), NDOFS, NE); - auto A = Reshape(ea_data.HostRead(), NDOFS, NDOFS, NE); - for(int glob_j = 0; glob_j < NE*NDOFS; glob_j++) - { - const int e = glob_j/NDOFS; - const int j = glob_j%NDOFS; - double res = 0.0; - for (int i = 0; i < NDOFS; i++) - { - res += A(i, j, e)*X(i, e); - } - Y(j, e) += res; - } - - // Take all of our multiple elements and go back to the L vector. - elem_restrict_lex->MultTranspose(local_y_fa, y_fa); - elem_restrict_lex->MultTranspose(local_y_ea, y_ea); - // Find out how different our solutions were from one another. - double mag = y_fa.Norml2(); - std::cout << "y_fa mag: " << mag << std::endl; - y_fa -= y_ea; - double difference = y_fa.Norml2(); - // Free up memory now. - delete nlf_int; - delete model; - delete pmesh; - - return difference / mag; -} - -// This function compares the difference in the formation of the Mult operator and then multiplying it -// by the necessary vector, and the matrix-free partial assembly formulation. -// It's been tested on higher order elements and multiple elements. The difference in these two methods -// should be 0.0. -double ICExaNLFIntegratorPAVecTest() -{ - int dim = 3; - int order = 6; - mfem::ParMesh *pmesh = nullptr; - { - // Making this mesh and test real simple with 8 cubic element - mfem::Mesh mesh = Mesh::MakeCartesian3D(2, 2, 2, Element::HEXAHEDRON, 1.0, 1.0, 1.0, false); - mesh.SetCurvature(order); - pmesh = new mfem::ParMesh(MPI_COMM_WORLD, mesh); - } - - H1_FECollection fec(order, dim); - ParFiniteElementSpace fes(pmesh, &fec, dim); - - // All of these Quadrature function variables are needed to instantiate our material model - // We can just ignore this marked section - ///////////////////////////////////////////////////////////////////////////////////////// - // Define a quadrature space and material history variable QuadratureFunction. - int intOrder = 2 * order + 1; - QuadratureSpace qspace(pmesh, intOrder); // 3rd order polynomial for 2x2x2 quadrature - // for first order finite elements. - QuadratureFunction q_matVars0(&qspace, 1); - QuadratureFunction q_matVars1(&qspace, 1); - QuadratureFunction q_sigma0(&qspace, 6); - QuadratureFunction q_sigma1(&qspace, 6); - q_sigma1 = 1.0; - q_sigma0 = 1.0; - // We'll modify this before doing the partial assembly - // This is our stiffness matrix and is a 6x6 due to major and minor symmetry - // of the 4th order tensor which has dimensions 3x3x3x3. - QuadratureFunction q_matGrad(&qspace, 36); - QuadratureFunction q_kinVars0(&qspace, 9); - QuadratureFunction q_vonMises(&qspace, 1); - ParGridFunction beg_crds(&fes); - ParGridFunction end_crds(&fes); - // We'll want to update this later in case we do anything more complicated. - Vector matProps(1); - - end_crds = 1.0; - - ExaModel *model; - // This doesn't really matter and is just needed for the integrator class. - model = new test_model(&q_sigma0, &q_sigma1, &q_matGrad, &q_matVars0, &q_matVars1, - &beg_crds, &end_crds, &matProps, 1, 1, Assembly::PA); - // Model time needs to be set. - model->SetModelDt(1.0); - ///////////////////////////////////////////////////////////////////////////// - ExaNLFIntegrator* nlf_int; - - nlf_int = new ICExaNLFIntegrator(dynamic_cast(model)); - - const FiniteElement &el = *fes.GetFE(0); - ElementTransformation *Ttr; - - // So, we're going to originally support non tensor-product type elements originally. - const ElementDofOrdering ordering = ElementDofOrdering::NATIVE; - // const ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC; - const Operator *elem_restrict_lex; - elem_restrict_lex = fes.GetElementRestriction(ordering); - // Set our field variable to a linear spacing so 1 ... ndofs in field - Vector xtrue(end_crds.Size()); - for (int i = 0; i < xtrue.Size(); i++) { - xtrue(i) = i + 1; - } - - // For multiple elements xtrue and local_x are differently sized - Vector local_x(elem_restrict_lex->Height()); - // All of our local global solution variables - Vector y_fa(end_crds.Size()); - Vector local_y_fa(elem_restrict_lex->Height()); - Vector local_y_pa(elem_restrict_lex->Height()); - Vector y_pa(end_crds.Size()); - // Initializing them all to 1. - y_fa = 0.0; - y_pa = 0.0; - local_y_pa = 0.0; - local_y_fa = 0.0; - // Get our local x values (element values) from the global vector - elem_restrict_lex->Mult(xtrue, local_x); - // Variables used to kinda mimic what the NonlinearForm::GetGradient does. - int ndofs = el.GetDof() * el.GetDim(); - Vector elfun(ndofs), elresults(ndofs); - DenseMatrix elmat; - elfun.HostReadWrite(); - elresults.HostReadWrite(); - local_x.HostReadWrite(); - local_y_fa.HostReadWrite(); - for (int i = 0; i < fes.GetNE(); i++) { - Ttr = fes.GetElementTransformation(i); - for (int j = 0; j < ndofs; j++) { - elfun(j) = local_x((i * ndofs) + j); - } - - elresults = 0.0; - nlf_int->AssembleElementVector(el, *Ttr, elfun, elresults); - for (int j = 0; j < ndofs; j++) { - local_y_fa((i * ndofs) + j) = elresults(j); - } - } - - // Perform the setup and action operation of our PA operation - nlf_int->AssemblePA(fes); - nlf_int->AddMultPA(local_x, local_y_pa); - - // Take all of our multiple elements and go back to the L vector. - elem_restrict_lex->MultTranspose(local_y_fa, y_fa); - elem_restrict_lex->MultTranspose(local_y_pa, y_pa); - // Find out how different our solutions were from one another. - double mag = y_fa.Norml2(); - std::cout << "y_fa mag: " << mag << std::endl; - y_fa -= y_pa; - double difference = y_fa.Norml2(); - // Free up memory now. - delete nlf_int; - delete model; - delete pmesh; - - return difference / mag; -} - -template -void setCMat(QuadratureFunction &cmat_data) -{ - int npts = cmat_data.Size() / cmat_data.GetVDim(); - const int dim2 = 6; - - if (cmat_ones) { - cmat_data = 1.0; - } - else { - const int DIM3 = 3; - std::array perm3 {{ 2, 1, 0 } }; - // bunch of helper RAJA views to make dealing with data easier down below in our kernel. - RAJA::Layout layout_2Dtensor = RAJA::make_permuted_layout({{ dim2, dim2, npts } }, perm3); - RAJA::View > cmat(cmat_data.HostReadWrite(), layout_2Dtensor); - for (int i = 0; i < npts; i++) { - cmat(0, 0, i) = 100.; - cmat(1, 1, i) = 100.; - cmat(2, 2, i) = 100.; - cmat(0, 1, i) = 75.; - cmat(1, 0, i) = 75.; - cmat(0, 2, i) = 75.; - cmat(2, 0, i) = 75.; - cmat(1, 2, i) = 75.; - cmat(2, 1, i) = 75.; - cmat(3, 3, i) = 50.; - cmat(4, 4, i) = 50.; - cmat(5, 5, i) = 50.; - } // end of pa style set ups - } // end of cmat set 1 or as cubic material -} - -TEST(exaconstit, partial_assembly) -{ - double difference = ExaNLFIntegratorPATest(); - std::cout << difference << std::endl; - EXPECT_LT(fabs(difference), 1.0e-14) << "Did not get expected value for pa false"; - difference = ExaNLFIntegratorPATest(); - std::cout << difference << std::endl; - EXPECT_LT(fabs(difference), 1.0e-14) << "Did not get expected value for pa true"; - difference = ExaNLFIntegratorPAVecTest(); - std::cout << difference << std::endl; - EXPECT_LT(fabs(difference), 2e-14) << "Did not get expected value for pa vec"; -} - -TEST(exaconstit, ea_assembly) -{ - double difference = ExaNLFIntegratorEATest(); - std::cout << difference << std::endl; - EXPECT_LT(fabs(difference), 1.0e-14) << "Did not get expected value for ea false"; - difference = ExaNLFIntegratorEATest(); - std::cout << difference << std::endl; - EXPECT_LT(fabs(difference), 1.0e-14) << "Did not get expected value for ea true"; -} - -TEST(exaconstit, ic_ea_assembly) -{ - double difference = ICExaNLFIntegratorEATest(); - std::cout << difference << std::endl; - EXPECT_LT(fabs(difference), 1.0e-14) << "Did not get expected value for ea false"; - difference = ICExaNLFIntegratorEATest(); - std::cout << difference << std::endl; - EXPECT_LT(fabs(difference), 1.0e-14) << "Did not get expected value for ea true"; - difference = ICExaNLFIntegratorPAVecTest(); - std::cout << difference << std::endl; - EXPECT_LT(fabs(difference), 2e-14) << "Did not get expected value for pa vec"; -} - -int main(int argc, char *argv[]) -{ - // Initialize MPI. - int num_procs, myid; - MPI_Init(&argc, &argv); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - MPI_Comm_rank(MPI_COMM_WORLD, &myid); - // Testing the case for a dense CMat and then a sparser version of CMat - - Device device("debug"); - printf("\n"); - device.Print(); - - ::testing::InitGoogleTest(&argc, argv); - if (argc > 1) { - outputLevel = atoi(argv[1]); - } - std::cout << "got outputLevel : " << outputLevel << std::endl; - - int i = RUN_ALL_TESTS(); - - MPI_Finalize(); - - return i; -} \ No newline at end of file diff --git a/test/test_mechanics.py b/test/test_mechanics.py index fbfe9ce..1e5bc30 100644 --- a/test/test_mechanics.py +++ b/test/test_mechanics.py @@ -1,187 +1,406 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +""" +Improved ExaConstit Testing Framework +Automatically discovers TOML test cases and performs comprehensive validation +""" + import subprocess -import csv import os import multiprocessing import numpy as np +import pandas as pd import unittest +import glob +from pathlib import Path from sys import platform +import toml +from typing import List, Tuple, Dict, Set +import logging +from dataclasses import dataclass + +# Configure logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +@dataclass +class TestResult: + """Container for test results""" + test_name: str + passed: bool + missing_files: List[str] + file_differences: Dict[str, List[str]] + error_message: str = "" # Taken from https://github.com/orgs/community/discussions/49224 # but modified slightly as we don't need as strict of a req as the OP in that thread # import requests # -def is_on_github_actions(): +def is_on_github_actions() -> bool: + """Check if running on GitHub Actions CI""" if "CI" not in os.environ or not os.environ["CI"] or "GITHUB_RUN_ID" not in os.environ: return False - - # headers = {"Authorization": f"Bearer {os.environ['GITHUB_TOKEN']}"} - # url = f"https://api.github.com/repos/{os.environ['GITHUB_REPOSITORY']}/actions/runs/{os.environ['GITHUB_RUN_ID']}" - # response = requests.get(url, headers=headers) - - # return response.status_code == 200 and "workflow_runs" in response.json() return True -def check_stress(ans_pwd, test_pwd, test_case): - answers = [] - tests = [] - with open(ans_pwd) as csvfile: - readcsv = csv.reader(csvfile, delimiter=' ') - for row in readcsv: - answers.append(row) - with open(test_pwd) as csvfile: - readcsv = csv.reader(csvfile, delimiter=' ') - for row in readcsv: - tests.append(row) - err = 0.0 - i = 0 - for ans, test in zip(answers, tests): - i = i + 1 - for a, t in zip(ans, test): - err += abs(float(a) - float(t)) - err = err / i - if (err > 1.0e-10): - raise ValueError("The following test case failed: ", test_case, " error ", err) - return True +def extract_basename_from_toml(toml_file: str) -> str: + """ + Extract basename from TOML file, fallback to filename stem if not specified + + Args: + toml_file: Path to TOML configuration file + + Returns: + basename string for output directory naming + """ + try: + with open(toml_file, 'r') as f: + config = toml.load(f) + + # Check if basename is explicitly set in TOML + if 'basename' in config: + return config['basename'] + else: + # Fallback to file stem (filename without extension) + return Path(toml_file).stem + + except Exception as e: + logger.warning(f"Could not parse {toml_file} for basename: {e}") + return Path(toml_file).stem -def runSystemCommands(params): - test, ans = params - print("Now running test case: " + test) - result = subprocess.run('pwd', stdout=subprocess.PIPE) - pwd = result.stdout.decode('utf-8') - if not is_on_github_actions(): - cmd = 'mpirun -np 2 ' + pwd.rstrip() + '/../bin/mechanics -opt ' + test - else: - cmd = 'mpirun -np 1 ' + pwd.rstrip() + '/../bin/mechanics -opt ' + test - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - ans_pwd = pwd.rstrip() + '/' + ans - tresult = test.split(".")[0] - test_pwd = pwd.rstrip() + '/test_'+tresult+'_stress.txt' - check_stress(ans_pwd, test_pwd, test) - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_stress.txt' - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - return True +def discover_test_cases(test_dir: str = ".") -> List[Tuple[str, str]]: + """ + Automatically discover all TOML test cases in the test directory + + Args: + test_dir: Directory to search for TOML files + + Returns: + List of (toml_file, basename) tuples + """ + toml_files = glob.glob(os.path.join(test_dir, "*.toml")) + test_cases = [] + + for toml_file in toml_files: + basename = extract_basename_from_toml(toml_file) + test_cases.append((os.path.basename(toml_file), basename)) + logger.info(f"Discovered test case: {toml_file} -> basename: {basename}") + + if not test_cases: + logger.warning(f"No TOML files found in {test_dir}") + + return test_cases -def run(): - test_cases = ["voce_pa.toml", "voce_full.toml", "voce_nl_full.toml", - "voce_bcc.toml", "voce_full_cyclic.toml", "mtsdd_bcc.toml", "mtsdd_full.toml", "mtsdd_full_auto.toml"] +def load_data_file(file_path: str) -> np.ndarray: + """ + Load data file using pandas with automatic header detection + + Args: + file_path: Path to data file + + Returns: + numpy array of numeric data (excluding headers) + """ + try: + # Try to read with pandas, handling various formats + # Use sep='\s+' instead of deprecated delim_whitespace=True + df = pd.read_csv(file_path, sep=r'\s+', comment='#', + header=0, na_values=['nan', 'NaN', 'inf', '-inf']) + + # Convert to numeric, replacing non-numeric with NaN + df_numeric = df.apply(pd.to_numeric, errors='coerce') + + # Return as numpy array, dropping any rows with all NaN + data = df_numeric.dropna(how='all').values + + if data.size == 0: + raise ValueError("No numeric data found in file") + + return data + + except Exception as e: + # Fallback: try numpy loadtxt with skip_header + try: + return np.loadtxt(file_path, skiprows=1) + except Exception as e2: + raise ValueError(f"Could not load {file_path}: {e}, {e2}") - test_results = ["voce_pa_stress.txt", "voce_full_stress.txt", - "voce_full_stress.txt", "voce_bcc_stress.txt", "voce_full_cyclic_stress.txt", - "mtsdd_bcc_stress.txt", "mtsdd_full_stress.txt", "mtsdd_full_auto_stress.txt"] +def compare_files(baseline_file: str, result_file: str, rel_tolerance: float = 1e-8, + abs_tolerance: float = 1e-10) -> List[str]: + """ + Compare two data files with specified relative and absolute tolerances + + Args: + baseline_file: Path to baseline reference file + result_file: Path to test result file + rel_tolerance: Relative tolerance for comparison + abs_tolerance: Absolute tolerance for comparison (for small values) + + Returns: + List of difference descriptions (empty if files match) + """ + differences = [] + + try: + baseline_data = load_data_file(baseline_file) + result_data = load_data_file(result_file) + + # Check shape compatibility + if baseline_data.shape != result_data.shape: + differences.append(f"Shape mismatch: baseline {baseline_data.shape} vs result {result_data.shape}") + return differences + + # Calculate absolute differences + abs_diff = np.abs(baseline_data - result_data) + + # Calculate relative differences + # Handle case where baseline values might be zero + with np.errstate(divide='ignore', invalid='ignore'): + rel_diff = abs_diff / (np.abs(baseline_data) + 1e-16) + + # Determine adaptive absolute tolerance based on data magnitude + # Exclude first two columns if they exist (likely time and volume) + if baseline_data.shape[1] > 2: + data_for_scaling = baseline_data[:, 2:] # Skip first two columns + else: + data_for_scaling = baseline_data + + # Use the maximum magnitude in the dataset to scale absolute tolerance + max_magnitude = np.max(np.abs(data_for_scaling)) + if is_on_github_actions() and ("elastic_strain" in baseline_file): + # Currently running on 1 core leads to varying differences in the elastic strains + adaptive_abs_tolerance = max(5e-6, max_magnitude * 1e-7) + else: + adaptive_abs_tolerance = max(abs_tolerance, max_magnitude * rel_tolerance) - result = subprocess.run('pwd', stdout=subprocess.PIPE) - pwd = result.stdout.decode('utf-8') + + # A difference is acceptable if EITHER: + # 1. Relative difference is below tolerance, OR + # 2. Absolute difference is below the adaptive absolute tolerance + acceptable_diff = (rel_diff <= rel_tolerance) | (abs_diff <= adaptive_abs_tolerance) + + # Find locations where differences are NOT acceptable + diff_locations = np.where(~acceptable_diff) + + if len(diff_locations[0]) > 0: + # Report first few significant differences + max_reports = min(10, len(diff_locations[0])) # Limit to first 10 differences + + for i in range(max_reports): + row, col = diff_locations[0][i], diff_locations[1][i] + baseline_val = baseline_data[row, col] + result_val = result_data[row, col] + abs_diff_val = abs_diff[row, col] + rel_diff_val = rel_diff[row, col] + + differences.append( + f"Row {row+2}, Col {col+1}: baseline={baseline_val:.6e}, " + f"result={result_val:.6e}, abs_diff={abs_diff_val:.6e}, " + f"rel_diff={rel_diff_val:.6e} (tol: rel={rel_tolerance:.1e}, abs={adaptive_abs_tolerance:.1e})" + ) + + if len(diff_locations[0]) > max_reports: + differences.append(f"... and {len(diff_locations[0]) - max_reports} more differences") + + # Add summary of tolerance criteria + differences.insert(0, f"Using adaptive absolute tolerance: {adaptive_abs_tolerance:.1e} " + f"(based on max data magnitude: {max_magnitude:.1e})") + + except Exception as e: + differences.append(f"Error comparing files: {str(e)}") + + return differences - # Remove any stress file that might already be living in the test directory - for test in test_cases: - tresult = test.split(".")[0] - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_stress.txt' - result = subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) +def validate_test_results(test_name: str, baseline_dir: str, result_dir: str) -> TestResult: + """ + Validate test results by comparing all files in baseline vs result directories + + Args: + test_name: Name of the test case + baseline_dir: Directory containing baseline reference files + result_dir: Directory containing test result files + + Returns: + TestResult object with validation details + """ + missing_files = [] + file_differences = {} + + try: + if not os.path.exists(baseline_dir): + return TestResult(test_name, False, [], {}, f"Baseline directory not found: {baseline_dir}") + + if not os.path.exists(result_dir): + return TestResult(test_name, False, [], {}, f"Result directory not found: {result_dir}") + + # Get all files in baseline directory + baseline_files = set() + for root, _, files in os.walk(baseline_dir): + for file in files: + rel_path = os.path.relpath(os.path.join(root, file), baseline_dir) + baseline_files.add(rel_path) + + # Check for missing files in result directory + for baseline_file in baseline_files: + result_file_path = os.path.join(result_dir, baseline_file) + if not os.path.exists(result_file_path): + missing_files.append(baseline_file) + else: + # Compare files + baseline_file_path = os.path.join(baseline_dir, baseline_file) + differences = compare_files(baseline_file_path, result_file_path) + if differences: + file_differences[baseline_file] = differences + + # Test passes if no missing files and no significant differences + passed = len(missing_files) == 0 and len(file_differences) == 0 + + return TestResult(test_name, passed, missing_files, file_differences) + + except Exception as e: + return TestResult(test_name, False, [], {}, f"Validation error: {str(e)}") - params = zip(test_cases, test_results) +def run_single_test(params: Tuple[str, str]) -> TestResult: + """ + Run a single test case and validate results + + Args: + params: Tuple of (toml_file, basename) + + Returns: + TestResult object + """ + toml_file, basename = params + logger.info(f"Running test case: {toml_file} (basename: {basename})") + + try: + # Get current working directory + result = subprocess.run('pwd', stdout=subprocess.PIPE, text=True) + pwd = result.stdout.strip() + + # Determine number of MPI processes + if not is_on_github_actions(): + np_flag = '-np 2' + else: + np_flag = '-np 1' + + # Run the mechanics simulation + cmd = f'mpirun {np_flag} {pwd}/../bin/mechanics -opt {toml_file}' + result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + shell=True, text=True) + + if result.returncode != 0: + error_msg = f"Simulation failed: {result.stderr}" + logger.error(error_msg) + return TestResult(basename, False, [], {}, error_msg) + + # Validate results + baseline_dir = os.path.join(pwd, 'test_results', basename) + result_dir = os.path.join(pwd, 'results', basename) + + test_result = validate_test_results(basename, baseline_dir, result_dir) + + # Log results + if test_result.passed: + logger.info(f"✓ Test {basename} PASSED") + else: + logger.error(f"✗ Test {basename} FAILED") + if test_result.missing_files: + logger.error(f" Missing files: {test_result.missing_files}") + for file, diffs in test_result.file_differences.items(): + logger.error(f" Differences in {file}:") + for diff in diffs[:3]: # Show first 3 differences + logger.error(f" {diff}") + + return test_result + + except Exception as e: + error_msg = f"Test execution error: {str(e)}" + logger.error(error_msg) + return TestResult(basename, False, [], {}, error_msg) - # This returns the number of processors we have available to us - # We divide by 2 since we use 2 cores per MPI call - # However, this command only works on Unix machines since Windows - # hasn't added support for this command yet... - if platform == "linux" or platform == "linux2": - num_processes = int(len(os.sched_getaffinity(0)) / 2) - else: - num_processes = int(multiprocessing.cpu_count() / 2) - print(num_processes) - pool = multiprocessing.Pool(num_processes) - pool.map(runSystemCommands, params) - pool.close() - pool.join() - return True +def cleanup_result_files(test_cases: List[Tuple[str, str]], results_dir: str = "results"): + """ + Clean up any existing result files before running tests + + Args: + test_cases: List of (toml_file, basename) tuples + results_dir: Directory containing result subdirectories + """ + for _, basename in test_cases: + result_subdir = os.path.join(results_dir, basename) + if os.path.exists(result_subdir): + logger.info(f"Cleaning up existing results in {result_subdir}") + subprocess.run(f'rm -rf {result_subdir}', shell=True) -def runExtraSystemCommands(params): - test, ans = params - print("Now running test case: " + test) - result = subprocess.run('pwd', stdout=subprocess.PIPE) - pwd = result.stdout.decode('utf-8') - if not is_on_github_actions(): - cmd = 'mpirun -np 2 ' + pwd.rstrip() + '/../bin/mechanics -opt ' + test - else: - cmd = 'mpirun -np 1 ' + pwd.rstrip() + '/../bin/mechanics -opt ' + test - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - ans_pwd = pwd.rstrip() + '/' + ans[0] - tresult = test.split(".")[0] - test_pwd = pwd.rstrip() + '/test_'+tresult+'_stress.txt' - check_stress(ans_pwd, test_pwd, test) - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_stress.txt' - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - ans_pwd = pwd.rstrip() + '/' + ans[1] - tresult = test.split(".")[0] - test_pwd = pwd.rstrip() + '/test_'+tresult+'_def_grad.txt' - check_stress(ans_pwd, test_pwd, test) - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_def_grad.txt' - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - ans_pwd = pwd.rstrip() + '/' + ans[2] - tresult = test.split(".")[0] - test_pwd = pwd.rstrip() + '/test_'+tresult+'_pl_work.txt' - check_stress(ans_pwd, test_pwd, test) - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_pl_work.txt' - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - ans_pwd = pwd.rstrip() + '/' + ans[3] - tresult = test.split(".")[0] - test_pwd = pwd.rstrip() + '/test_'+tresult+'_euler_strain.txt' - check_stress(ans_pwd, test_pwd, test) - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_euler_strain.txt' - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - ans_pwd = pwd.rstrip() + '/' + ans[4] - tresult = test.split(".")[0] - test_pwd = pwd.rstrip() + '/test_'+tresult+'_eps.txt' - check_stress(ans_pwd, test_pwd, test) - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_eps.txt' - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - return True - -def runExtra(): - test_cases = ["voce_ea.toml"] - - test_results = [("voce_ea_stress.txt", "voce_ea_def_grad.txt", "voce_ea_pl_work.txt", "voce_ea_euler_strain.txt", "voce_ea_eps.txt")] - - result = subprocess.run('pwd', stdout=subprocess.PIPE) - - pwd = result.stdout.decode('utf-8') - - # Remove any stress file that might already be living in the test directory - for test in test_cases: - tresult = test.split(".")[0] - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_stress.txt ' + pwd.rstrip() \ - + '/test_'+tresult+'_pl_work.txt ' + pwd.rstrip() \ - + '/test_'+tresult+'_def_grad.txt' + pwd.rstrip() \ - + '/test_'+tresult+'_euler_strain.txt' + pwd.rstrip() \ - + '/test_'+tresult+'_eps.txt' + pwd.rstrip() - result = subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - - params = zip(test_cases, test_results) - - # This returns the number of processors we have available to us - # We divide by 2 since we use 2 cores per MPI call - # However, this command only works on Unix machines since Windows - # hasn't added support for this command yet... +def run_all_tests() -> bool: + """ + Discover and run all test cases in parallel + + Returns: + True if all tests pass, False otherwise + """ + # Discover test cases + test_cases = discover_test_cases() + + if not test_cases: + logger.error("No test cases found!") + return False + + # Clean up any existing result files + cleanup_result_files(test_cases) + + # Determine number of parallel processes if platform == "linux" or platform == "linux2": - num_processes = int(len(os.sched_getaffinity(0)) / 2) + num_processes = max(1, len(os.sched_getaffinity(0)) // 2) else: - num_processes = int(multiprocessing.cpu_count() / 2) - print(num_processes) - pool = multiprocessing.Pool(num_processes) - pool.map(runExtraSystemCommands, params) - pool.close() - pool.join() - return True + num_processes = max(1, multiprocessing.cpu_count() // 2) + + logger.info(f"Running {len(test_cases)} tests with {num_processes} parallel processes") + + # Run tests in parallel + with multiprocessing.Pool(num_processes) as pool: + results = pool.map(run_single_test, test_cases) + + # Summarize results + passed_tests = [r for r in results if r.passed] + failed_tests = [r for r in results if not r.passed] + + logger.info(f"\n{'='*60}") + logger.info(f"TEST SUMMARY: {len(passed_tests)}/{len(results)} tests passed") + logger.info(f"{'='*60}") + + if failed_tests: + logger.error("\nFAILED TESTS:") + for result in failed_tests: + logger.error(f"\n❌ {result.test_name}:") + if result.error_message: + logger.error(f" Error: {result.error_message}") + if result.missing_files: + logger.error(f" Missing files: {', '.join(result.missing_files)}") + for file, diffs in result.file_differences.items(): + logger.error(f" Differences in {file}:") + for diff in diffs[:3]: # Limit output + logger.error(f" {diff}") + if len(diffs) > 3: + logger.error(f" ... and {len(diffs)-3} more") + + return len(failed_tests) == 0 -class TestUnits(unittest.TestCase): +class TestExaConstit(unittest.TestCase): + """Unit test wrapper for ExaConstit validation""" + def test_all_cases(self): - actual = run() - actualExtra = runExtra() - self.assertTrue(actual) - self.assertTrue(actualExtra) + """Run all discovered test cases and validate results""" + success = run_all_tests() + self.assertTrue(success, "One or more ExaConstit tests failed") if __name__ == '__main__': - unittest.main() \ No newline at end of file + # If run directly, execute tests with detailed logging + if len(os.sys.argv) == 1: + # Run with detailed output + success = run_all_tests() + exit(0 if success else 1) + else: + # Run as unittest + unittest.main() \ No newline at end of file diff --git a/test/test_mechanics_const_strain_rate.py b/test/test_mechanics_const_strain_rate.py deleted file mode 100644 index fef1208..0000000 --- a/test/test_mechanics_const_strain_rate.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -import subprocess -import csv -import os -import multiprocessing -import numpy as np -import unittest -from sys import platform - -# Taken from https://github.com/orgs/community/discussions/49224 -# but modified slightly as we don't need as strict of a req as the OP in that thread -# import requests -# -def is_on_github_actions(): - if "CI" not in os.environ or not os.environ["CI"] or "GITHUB_RUN_ID" not in os.environ: - return False - - # headers = {"Authorization": f"Bearer {os.environ['GITHUB_TOKEN']}"} - # url = f"https://api.github.com/repos/{os.environ['GITHUB_REPOSITORY']}/actions/runs/{os.environ['GITHUB_RUN_ID']}" - # response = requests.get(url, headers=headers) - - # return response.status_code == 200 and "workflow_runs" in response.json() - return True - -def check_stress(ans_pwd, test_pwd, test_case): - answers = [] - tests = [] - with open(ans_pwd) as csvfile: - readcsv = csv.reader(csvfile, delimiter=' ') - for row in readcsv: - answers.append(row) - with open(test_pwd) as csvfile: - readcsv = csv.reader(csvfile, delimiter=' ') - for row in readcsv: - tests.append(row) - err = 0.0 - i = 0 - for ans, test in zip(answers, tests): - i = i + 1 - for a, t in zip(ans, test): - err += abs(float(a) - float(t)) - err = err / i - if (err > 1.0e-10): - raise ValueError("The following test case failed: ", test_case, " error ", err) - return True - -def runSystemCommands(params): - test, ans = params - print("Now running test case: " + test) - result = subprocess.run('pwd', stdout=subprocess.PIPE) - pwd = result.stdout.decode('utf-8') - if not is_on_github_actions(): - cmd = 'mpirun -np 2 ' + pwd.rstrip() + '/../bin/mechanics -opt ' + test - else: - cmd = 'mpirun -np 1 ' + pwd.rstrip() + '/../bin/mechanics -opt ' + test - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - ans_pwd = pwd.rstrip() + '/' + ans - tresult = test.split(".")[0] - test_pwd = pwd.rstrip() + '/test_'+tresult+'_stress.txt' - check_stress(ans_pwd, test_pwd, test) - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_stress.txt' - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - return True - -def run(): - test_cases = ["voce_full_cyclic_cs.toml", "voce_full_cyclic_csm.toml"] - - test_results = ["voce_full_cyclic_cs_stress.txt", "voce_full_cyclic_csm_stress.txt"] - - result = subprocess.run('pwd', stdout=subprocess.PIPE) - - pwd = result.stdout.decode('utf-8') - - # Remove any stress file that might already be living in the test directory - for test in test_cases: - tresult = test.split(".")[0] - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_stress.txt' - result = subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - - params = zip(test_cases, test_results) - - # This returns the number of processors we have available to us - # We divide by 2 since we use 2 cores per MPI call - # However, this command only works on Unix machines since Windows - # hasn't added support for this command yet... - if platform == "linux" or platform == "linux2": - num_processes = int(len(os.sched_getaffinity(0)) / 2) - else: - num_processes = int(multiprocessing.cpu_count() / 2) - - print(num_processes) - pool = multiprocessing.Pool(num_processes) - pool.map(runSystemCommands, params) - pool.close() - pool.join() - return True - -def runExtraSystemCommands(params): - test, ans = params - print("Now running test case: " + test) - result = subprocess.run('pwd', stdout=subprocess.PIPE) - pwd = result.stdout.decode('utf-8') - if not is_on_github_actions(): - cmd = 'mpirun -np 2 ' + pwd.rstrip() + '/../bin/mechanics -opt ' + test - else: - cmd = 'mpirun -np 1 ' + pwd.rstrip() + '/../bin/mechanics -opt ' + test - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - ans_pwd = pwd.rstrip() + '/' + ans[0] - tresult = test.split(".")[0] - test_pwd = pwd.rstrip() + '/test_'+tresult+'_stress.txt' - check_stress(ans_pwd, test_pwd, test) - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_stress.txt' - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - ans_pwd = pwd.rstrip() + '/' + ans[1] - tresult = test.split(".")[0] - test_pwd = pwd.rstrip() + '/test_'+tresult+'_def_grad.txt' - check_stress(ans_pwd, test_pwd, test) - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_def_grad.txt' - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - ans_pwd = pwd.rstrip() + '/' + ans[2] - tresult = test.split(".")[0] - test_pwd = pwd.rstrip() + '/test_'+tresult+'_pl_work.txt' - check_stress(ans_pwd, test_pwd, test) - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_pl_work.txt' - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - ans_pwd = pwd.rstrip() + '/' + ans[3] - tresult = test.split(".")[0] - test_pwd = pwd.rstrip() + '/test_'+tresult+'_euler_strain.txt' - check_stress(ans_pwd, test_pwd, test) - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_euler_strain.txt' - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - ans_pwd = pwd.rstrip() + '/' + ans[4] - tresult = test.split(".")[0] - test_pwd = pwd.rstrip() + '/test_'+tresult+'_eps.txt' - check_stress(ans_pwd, test_pwd, test) - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_eps.txt' - subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - return True - -def runExtra(): - test_cases = ["voce_ea_cs.toml"] - - test_results = [("voce_ea_cs_stress.txt", "voce_ea_cs_def_grad.txt", "voce_ea_cs_pl_work.txt", "voce_ea_cs_euler_strain.txt", "voce_ea_cs_eps.txt")] - - result = subprocess.run('pwd', stdout=subprocess.PIPE) - - pwd = result.stdout.decode('utf-8') - - # Remove any stress file that might already be living in the test directory - for test in test_cases: - tresult = test.split(".")[0] - cmd = 'rm ' + pwd.rstrip() + '/test_'+tresult+'_stress.txt ' + pwd.rstrip() \ - + '/test_'+tresult+'_pl_work.txt ' + pwd.rstrip() \ - + '/test_'+tresult+'_def_grad.txt' + pwd.rstrip() \ - + '/test_'+tresult+'_euler_strain.txt' + pwd.rstrip() \ - + '/test_'+tresult+'_eps.txt' + pwd.rstrip() - result = subprocess.run(cmd.rstrip(), stdout=subprocess.PIPE, shell=True) - - params = zip(test_cases, test_results) - - # This returns the number of processors we have available to us - # We divide by 2 since we use 2 cores per MPI call - # However, this command only works on Unix machines since Windows - # hasn't added support for this command yet... - if platform == "linux" or platform == "linux2": - num_processes = int(len(os.sched_getaffinity(0)) / 2) - else: - num_processes = int(multiprocessing.cpu_count() / 2) - print(num_processes) - pool = multiprocessing.Pool(num_processes) - pool.map(runExtraSystemCommands, params) - pool.close() - pool.join() - return True - -class TestUnits(unittest.TestCase): - def test_all_cases(self): - actual = run() - actualExtra = runExtra() - self.assertTrue(actualExtra) - - self.assertTrue(actual) - -if __name__ == '__main__': - unittest.main() \ No newline at end of file