Compare commits

..
Author SHA1 Message Date
Will Pazner cc7ebccc54 Fix signed char issue in Device::GetUUID 2026-04-17 11:38:22 -04:00
adam-sim-dev 9e423f2f8e Fixed missing parenthesis in the comment 2026-04-17 11:38:22 -04:00
Veselin Dobrev c14938cd1c Adjust seed values in sample runs in ex12p to ensure LOBPCG convergence in
older hypre versions.
2026-04-17 11:38:22 -04:00
Hugh Carson 419758be1e Address PR feedback
- Use [IntegrationRules] test tag instead of [PositiveWeightRules]
- Remove redundant case 21: (default branch handles it via the overwrite guard)
- Remove trailing blank line
2026-04-17 11:38:22 -04:00
Hugh Carson 4444d8ed70 Remove unused private helper methods from IntegrationRule
AddTriPoints3R, AddTetPoints4b, and AddTetPoints12bc are no longer
called after the legacy simplex rules were removed.
2026-04-17 11:38:22 -04:00
Hugh Carson 48670b9f87 Use exact fractions for trivial quadrature weights and coordinates
For rules where the mathematical value is an exact simple fraction
(midpoint weights, equal-weight symmetric rules), use the fraction
directly rather than the Polyquad decimal expansion. Cleaner to read
and avoids any rounding from decimal-to-double conversion.
2026-04-17 11:38:22 -04:00
Hugh Carson bddb52ace5 Restore original function order in intrules.cpp
Move TriangleIntegrationRule before SquareIntegrationRule to match
the original file layout, reducing diff noise against master.
2026-04-17 11:38:22 -04:00
Hugh Carson 2d99e1e2de Remove legacy simplex rules; positive-weight rules are now the default
The positive-weight rules now cover the full tabulated range for both
triangles (0-25) and tetrahedra (0-20), so the old rules with negative
weights are no longer needed. Remove the SimplexQuadrature enum,
simplex_type member, and legacy rule functions — all simplex quadrature
now uses positive-weight rules by default, with Grundmann-Moller
fallback for higher orders.
2026-04-17 11:38:22 -04:00
Hugh Carson fdad993654 Add existing order 21-25 triangle rule to positive-weight rules
The 126-point degree-25 rule already has all positive weights.
Copy it into TrianglePositiveIntegrationRule so the positive-weight
path covers orders 0-25.
2026-04-17 11:38:22 -04:00
Hugh Carson 7f51024345 Fix memory leak 2026-04-17 11:38:22 -04:00
Hugh Carson 0d00e79cd5 Add positive-weight simplex quadrature rules for orders 0-20
Triangle rules from Witherden & Vincent (2015), tet rules d=0-13
from Witherden & Vincent, tet rules d=14-20 from Chuluunbaatar et al.
(2022). All rules have strictly positive weights and interior points,
replacing the legacy rules which use negative weights at several
orders and fall back to Grundmann-Moller (negative weights, high
point counts) for tets at d>=9.
2026-04-17 11:38:22 -04:00
Gabriele Bozzola b406cdaf79 Improve error message for gmsh versions != 2.2
I am a new user of [palace](https://github.com/awslabs/palace). As I was
trying to set a simple mesh up (with gmsh), I kept getting indexing
errors that I could not decipher. I eventually
[learned](https://mfem.org/mesh-formats/) that supported version for
gmsh meshes is 2.2.

This commit catches this and adds an informative error.
2026-04-17 11:38:22 -04:00
chapman39 8adba4e1bb add comments showing each modulus replacement 2026-04-17 11:38:22 -04:00
chapman39 2b86c7300b added comment 2026-04-17 11:38:22 -04:00
chapman39 7357a9b4bf eliminate usage of modulus to avoid llvm backend bug 2026-04-17 11:38:22 -04:00
Veselin Dobrev b44728af9e Update the action actions/cache/restore to v5 2026-04-17 11:38:22 -04:00
Veselin Dobrev 8d002d09c8 Updated the github/codeql-action/* actions to the latest, v4 2026-04-17 11:38:22 -04:00
Veselin Dobrev fed8e6bc1b Updated actions/checkout to the latest major version, v6 2026-04-17 11:38:22 -04:00
Veselin Dobrev 0aa0ac0637 Update actions/{checkout,cache} to v5
Update github/codeql-action/* to v3
2026-04-17 11:38:22 -04:00
Jan Nikl 77ef843c2e Added scalar unit test of ProjectBdrCoefficientNormal(). 2026-04-17 11:38:22 -04:00
Jan Nikl 51bc8037d4 Added a unit test for vector ProjectBdrCoefficientNormal(). 2026-04-17 11:38:22 -04:00
Jan Nikl 523c208d87 Made the ProjectBdrCoefficientNormal check non-debug. 2026-04-17 11:38:22 -04:00
Jan Nikl 90e0e8e289 Minor unification of docstrings. 2026-04-17 11:38:22 -04:00
Jan Nikl 3f41665e4f Generalized RT normal projection. 2026-04-17 11:38:22 -04:00
Jan Nikl 6f07de9114 Removed unused code. 2026-04-17 11:38:22 -04:00
Jan Nikl 9f74ee130a Fixed vis of the initial exact solution. 2026-04-17 11:38:22 -04:00
Jan Nikl 0a7eb2c39e Fixed visulization in ex22p. 2026-04-17 11:38:22 -04:00
Jan Nikl d85723ce29 Fixed spelling of transverse. 2026-04-17 11:38:22 -04:00
Jan Nikl b33340edab Added documentation and checks to the extrusion classes. 2026-04-17 11:38:22 -04:00
Jan Nikl 9c1bf9704d Added extrusion of vector 1D grid functions. 2026-04-17 11:38:22 -04:00
Stowell, Mark L. c58816905d Adding bugfix and unit test which would have caught the bug 2026-04-17 11:38:22 -04:00
Wouter Tonnon ded65cf364 extended to MixedBilinearForm 2026-04-17 11:38:22 -04:00
Wouter Tonnon 422f42ec0d added missing face orientation 2026-04-17 11:38:22 -04:00
Veselin Dobrev acac245260 Small change in error message + formatting. 2026-04-17 11:38:22 -04:00
thartland ed0b39b732 VERIFY instead of ASSERT 2026-04-17 11:38:22 -04:00
Tucker Hartland d609bee2cc style 2026-04-17 11:38:22 -04:00
thartland 421f3f03ba adding a check to make sure that each process owns at least one entry of the HypreParVector prior to calling GlobalVector 2026-04-17 11:38:22 -04:00
Will Pazner 842f88a7b3 Use constexpr in unit test 2026-04-17 11:38:22 -04:00
Will Pazner abf97587d4 Add comment about the shape of FaceNbrData 2026-04-17 11:38:22 -04:00
Will Pazner 355a2cd570 Add unit test for parallel L2 face restriction with vdim > 1 2026-04-17 11:38:22 -04:00
Will Pazner e7e184a24d Fix bug in ParL2FaceRestriction with vdim > 1
The layout of the FaceNbrData vector was not handled properly
2026-04-17 11:38:22 -04:00
Stowell, Mark L. 616eaec18e Updating unit tests 2026-04-17 11:38:22 -04:00
Stowell, Mark L. 5c0711f334 Using new MapType entries and implementing new GetPhys*Dim methods 2026-04-17 11:38:22 -04:00
Stowell, Mark L. 999e4c4f46 Adding new MapType entries for R2D and R1D classes 2026-04-17 11:38:22 -04:00
214750edc8 Update to use PetscCtxRt from (3,25,0), and cleanup duplicate code
Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
Co-authored-by: Satish Balay <balay@mcs.anl.gov>
2026-04-17 11:38:22 -04:00
Satish Balay 4cf708d4fa update KSPMonitorFn usage for < (3,24,0) 2026-04-17 11:38:22 -04:00
Satish Balay 91a40a1d1a update PetscCtxDestroyFn usage for < (3,23,0) 2026-04-17 11:38:22 -04:00
Satish Balay dffe36f382 rework PetscContainerSetCtxDestroy() usage for < (3,23,0) 2026-04-17 11:38:22 -04:00
chapman39 49363859ee 80 chars/ line 2026-04-17 11:38:22 -04:00
chapman39 9f47892f62 dfem integrate: use mfem abort kernel in device code 2026-04-17 11:38:22 -04:00
Stowell, Mark L. 1c286184be Changing copyright date to pass CI checks 2026-04-17 11:38:22 -04:00
Stowell, Mark L. 6657cf2760 Adding miniapps/plasma subdirectory to build system 2026-04-17 11:38:22 -04:00
Stowell, Mark L. 731224d5e8 Adding plasma miniapp directory 2026-04-17 11:38:22 -04:00
Jan Nikl a8a85c68fb Minor docstring correction. 2026-04-17 11:38:22 -04:00
Jan Nikl 591cc1ca41 Fixed complex grid function copy assignment. 2026-04-17 11:38:22 -04:00
Andrew Ho 6b1c2644e6 comment on why TPL_LIBRARIES is reversed twice 2026-04-17 11:38:22 -04:00
Andrew HoandNuno Nobre 7742ad8355 Update config/cmake/modules/MfemCmakeUtilities.cmake
Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
2026-04-17 11:38:22 -04:00
Andrew HoandNuno Nobre 275e98264c Update config/cmake/modules/MfemCmakeUtilities.cmake
Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
2026-04-17 11:38:21 -04:00
Andrew Ho cd4d7c292f move cudart to MFEM_EXT_LIBS 2026-04-17 11:38:21 -04:00
Andrew Ho e130ae7dd8 fixed wrong dir being marked as system 2026-04-17 11:38:21 -04:00
Andrew HoandNuno Nobre e026e15fe3 Update config/cmake/modules/MfemCmakeUtilities.cmake
Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
2026-04-17 11:38:21 -04:00
Andrew Ho 493b5a942e MFEM_EXPORT_GPU_CONFIG should export CPU config.mk when set to off 2026-04-17 11:38:21 -04:00
Andrew Ho f27a13cbad revert change, updated comment to why libdl gets special treatment 2026-04-17 11:38:21 -04:00
Andrew Ho 559d0e42c7 suggestions from Veselin 2026-04-17 11:38:21 -04:00
Andrew Ho 5ba64a774e missed one old unsetting of shared_link_flag 2026-04-17 11:38:21 -04:00
Andrew Ho 896e251d3a review suggestions 2026-04-17 11:38:21 -04:00
Andrew Ho 9b0c9d3f6e fixes for hip 2026-04-17 11:38:21 -04:00
Andrew Ho c90d6f9d60 remove debug printout 2026-04-17 11:38:21 -04:00
Andrew Ho 6f2b8b82d1 seems to be building external laghos now 2026-04-17 11:38:21 -04:00
Andrew Ho 90cf6af2bb improving config.mk file generated by cmake to work with hip/cuda
Still need to export compiler flags
2026-04-17 11:38:21 -04:00
jdongg cd6bfc7de8 fix clang compiler warnings from origin/catch-tests 2026-03-06 14:07:34 -08:00
Will Pazner b42ad0a57c Merge remote-tracking branch 'origin/master' into bubble
# Conflicts:
#	fem/fe_coll.hpp
2026-03-01 16:30:46 -08:00
Dohyun Kim 8e67185297 Merge branch 'master' into bubble 2026-01-03 01:58:22 +09:00
Will Pazner 456c236cc5 Small fixes
Add local variables in thread-safe mode
Fix MFEM_VERIFY message
Fix trace collection order
2025-12-05 11:10:57 -08:00
Will Pazner f80902b776 Re-add assertion; skip check for bubble spaces 2025-12-05 10:14:06 -08:00
Will Pazner 4c9f6edef0 Improve Doxygen 2025-12-05 10:14:06 -08:00
Will Pazner 45ec9d451d Support "H1Bubble@" in FiniteElementCollection::New 2025-12-05 10:14:06 -08:00
Dohyun Kim 988ab81e5f FEColl::New 2025-12-05 10:14:06 -08:00
Will Pazner 3a7b1d7c67 Use bubble elements in ex36 and ex36p 2025-12-05 10:14:06 -08:00
Will Pazner 411ffcfef6 Fix DOF orderings in bubble elements 2025-12-05 10:14:06 -08:00
Will Pazner 27d79fc463 Revert "Return nullptr for H1Bubble_FECollection::DofOrderForOrientation"
This reverts commit 58e7bb6e6e213eb90839feb67de7d0e03b5799da.
2025-12-05 10:14:06 -08:00
Will Pazner 898337f772 Return nullptr for H1Bubble_FECollection::DofOrderForOrientation
Some features (e.g. node reordering) won't be supported; this could be added
later.
2025-12-05 10:14:06 -08:00
Will Pazner d3063a0982 Add bubble tets and hexes 2025-12-05 10:14:06 -08:00
Will Pazner 15853215ff Disable check that FE and FEC orders are the same
With enriched bubble elements, the orders could be different.

For example, linear triangle enriched with bubble has max total degree 3, but
the linear quadrilateral enriched with bubble has max degree 2 in each variable
(and max total degree 4).
2025-12-05 10:14:06 -08:00
Will Pazner 8b0e9ff064 Move bubble elements to their own file 2025-12-05 10:14:06 -08:00
Will Pazner 03973ad244 Add quad bubble element, change meaning of q 2025-12-05 09:57:09 -08:00
Will Pazner 88c04a6e45 Add H1 bubble triangle element and collection 2025-12-05 09:57:09 -08:00
100 changed files with 3339 additions and 11648 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ runs:
steps:
- uses: ./.github/actions/sanitize/config
- uses: actions/cache@v4
- uses: actions/cache@v5
if: ${{env.DEBUG == 'true'}}
id: debug
with:
+1 -1
View File
@@ -36,7 +36,7 @@ runs:
steps:
- uses: ./.github/actions/sanitize/config
- uses: actions/cache@v4
- uses: actions/cache@v5
if: ${{env.DEBUG == 'true' && inputs.cache-skip != 'true'}}
id: debug
with:
+5 -5
View File
@@ -23,7 +23,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/cache/restore@v4 # Cache for LLVM libcxx
- uses: actions/cache/restore@v5 # Cache for LLVM libcxx
with:
path: ${{env.LLVM_DIR}}
fail-on-cache-miss: true
@@ -32,14 +32,14 @@ runs:
- uses: ./.github/actions/sanitize/mpi
if: ${{inputs.par == 'true'}}
- uses: actions/cache/restore@v4 # Cache for Hypre
- uses: actions/cache/restore@v5 # Cache for Hypre
if: ${{inputs.par == 'true'}}
with:
path: ${{env.HYPRE_DIR}}
fail-on-cache-miss: true
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
- uses: actions/cache/restore@v4 # Cache for Metis
- uses: actions/cache/restore@v5 # Cache for Metis
if: ${{inputs.par == 'true'}}
with:
path: ${{env.METIS_DIR}}
@@ -51,13 +51,13 @@ runs:
run: ln -s -f ${{env.HYPRE_DIR}} hypre && ln -s -f ${{env.METIS_DIR}} metis-4.0
shell: bash
- uses: actions/cache/restore@v4 # Cache for LSAN suppression file
- uses: actions/cache/restore@v5 # Cache for LSAN suppression file
with:
path: ${{env.LSAN_DIR}}
fail-on-cache-miss: true
key: build-lsan-suppression-file
- uses: actions/checkout@v4 # Checkout the repository
- uses: actions/checkout@v6 # Checkout the repository
with:
path: mfem
# ref: ${{env.BRANCH}}
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
remove-docker-images: 'true'
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
# It's easier to reference named variables than indexes of the matrix
- name: Set Environment
+4 -4
View File
@@ -153,7 +153,7 @@ jobs:
# /home/runner/work/mfem/mfem/mfem
# Note: Done now to access "install-hypre" and "install-metis" actions.
- name: checkout mfem
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
path: ${{ env.MFEM_TOP_DIR }}
# Fetch the complete history for codecov to access commits ID
@@ -225,7 +225,7 @@ jobs:
- name: cache hypre
id: hypre-cache
if: matrix.mpi == 'par'
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
@@ -255,7 +255,7 @@ jobs:
- name: cache metis
id: metis-cache
if: matrix.mpi == 'par' && matrix.os != 'windows-latest'
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ env.METIS_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
@@ -270,7 +270,7 @@ jobs:
- name: cache vcpkg (Windows)
id: vcpkg-cache
if: matrix.os == 'windows-latest'
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: vcpkg_cache
key: ${{ runner.os }}-${{ matrix.mpi }}-vcpkg-v1
+4 -4
View File
@@ -40,11 +40,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -57,7 +57,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v4
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -70,4 +70,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v4
+3 -3
View File
@@ -39,7 +39,7 @@ jobs:
steps:
- name: checkout MFEM
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
path: mfem
@@ -50,7 +50,7 @@ jobs:
- name: Cache Hypre Install
id: hypre-cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-v2.5
@@ -65,7 +65,7 @@ jobs:
- name: Cache Metis Install
id: metis-cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ env.METIS_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
+4 -4
View File
@@ -38,7 +38,7 @@ jobs:
github.event.pull_request.head.repo.full_name != github.repository)
steps:
- name: checkout mfem
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: copyright check
id: copyright
@@ -93,7 +93,7 @@ jobs:
github.event.pull_request.head.repo.full_name != github.repository)
steps:
- name: checkout mfem
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: get astyle
run: |
@@ -110,7 +110,7 @@ jobs:
github.event.pull_request.head.repo.full_name != github.repository)
steps:
- name: checkout mfem
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: get doxygen and graphviz
run: |
@@ -135,7 +135,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout mfem
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
+2 -2
View File
@@ -17,11 +17,11 @@ jobs:
runs-on: ubuntu-latest
name: 2.19.0
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{env.HYPRE_DIR}}
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
+2 -2
View File
@@ -27,13 +27,13 @@ jobs:
llvm_use_sanitizer: "Undefined"
name: ${{matrix.sanitizer}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/config
with:
NO_FLAGS: true
- name: Cache
id: cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{env.LLVM_DIR}}
key: build-libcxx-${{env.LLVM_VER}}-${{matrix.sanitizer}}
+2 -2
View File
@@ -17,11 +17,11 @@ jobs:
runs-on: ubuntu-latest
name: lsan.supp
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{env.LSAN_DIR}}
key: build-lsan-suppression-file
+2 -2
View File
@@ -17,11 +17,11 @@ jobs:
runs-on: ubuntu-latest
name: 4.0.3
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{env.METIS_DIR}}
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
+7 -7
View File
@@ -28,7 +28,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/mfem
with:
par: ${{inputs.par}}
@@ -40,7 +40,7 @@ jobs:
env:
ex: ${{inputs.par && 'ex1p' || 'ex1'}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/restore
id: restore
with:
@@ -58,7 +58,7 @@ jobs:
env:
exclude: ${{inputs.par && '-E "_ser"' || ''}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/restore
id: restore
with:
@@ -82,7 +82,7 @@ jobs:
env:
exclude: ${{inputs.par && '-E "_ser"' || ''}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/restore
id: restore
with:
@@ -107,7 +107,7 @@ jobs:
run: ${{inputs.par && '-R "_cpu_np"' || ''}}
exclude: ${{inputs.par && '"unit_tests|debug"' || '"^unit_tests$|debug"'}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/restore
id: restore
with:
@@ -131,7 +131,7 @@ jobs:
env:
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/restore
id: restore
with:
@@ -165,7 +165,7 @@ jobs:
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
np: ${{inputs.par && '_np=2' || ''}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/restore
id: restore
with:
+16
View File
@@ -8,6 +8,22 @@
https://mfem.org
Version 4.10 (development)
==========================
Discretization improvements
---------------------------
- Replaced legacy simplex quadrature rules with symmetric positive-weight
rules for triangles (orders 0-25) and tetrahedra (orders 0-20). These
rules guarantee all-positive weights and interior quadrature points,
improving numerical stability. Higher orders fall back to Grundmann-Moller.
Triangle rules: Witherden & Vincent, Comput. Math. Appl. 69(10):1232-1241,
2015.
Tet rules (d=1-13): Witherden & Vincent (ibid).
Tet rules (d=14-20): Chuluunbaatar et al., Comput. Math. Appl. 124:89-97,
2022.
Version 4.9.1 (development)
===========================
+5 -1
View File
@@ -652,6 +652,8 @@ foreach(TPL IN LISTS MFEM_TPLS)
endif()
endforeach(TPL)
# reverse to remove the first instance of entries in TPL_LIBRARIES
# so later duplicates are kept (for dependency ordering)
list(REVERSE TPL_LIBRARIES)
list(REMOVE_DUPLICATES TPL_LIBRARIES)
list(REVERSE TPL_LIBRARIES)
@@ -1015,5 +1017,7 @@ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data
# Create 'config.mk' from 'config.mk.in' for the build and install locations and
# define install rules for 'config.mk' and 'test.mk'
#-------------------------------------------------------------------------------
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
option(MFEM_EXPORT_GPU_CONFIG "Export config.mk for GPU-enabled downstream packages" ON)
endif()
mfem_export_mk_files()
+89 -17
View File
@@ -701,7 +701,6 @@ endfunction(mfem_find_library)
# Extract compile and link options needed by the given target.
#
function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
if (NOT TARGET ${Target})
return()
endif()
@@ -799,7 +798,12 @@ function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
# message(STATUS "Lib = ${Lib}")
# Filter-out generator expressions
if (NOT ("${Lib}" MATCHES "^\\$"))
list(APPEND LinkOpts "${Lib}")
if(NOT ("${Lib}" STREQUAL "dl"))
list(APPEND LinkOpts "${Lib}")
else()
# for some reason libdl doesn't include the "-l"
list(APPEND LinkOpts "-ldl")
endif()
endif()
else()
mfem_get_target_options(${Lib} COpts LOpts)
@@ -888,9 +892,18 @@ function(mfem_export_mk_files)
set(${var} NO)
endif()
endforeach()
# TODO: Add support for MFEM_USE_CUDA=YES
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
set(MFEM_HOST_CXX ${MFEM_CXX})
if (MFEM_USE_CUDA AND MFEM_EXPORT_GPU_CONFIG)
set(MFEM_CXX ${CMAKE_CUDA_COMPILER})
if(MFEM_CUDA_COMPILER_IS_NVCC)
set(MFEM_HOST_CXX ${CMAKE_CUDA_HOST_COMPILER})
else()
set(MFEM_HOST_CXX ${CMAKE_CXX_COMPILER})
endif()
else()
# mfem doesn't use enable_language(HIP)
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
set(MFEM_HOST_CXX ${CMAKE_CXX_COMPILER})
endif()
set(MFEM_CPPFLAGS "")
get_target_property(cxx_std mfem CXX_STANDARD)
# For now, we ignore the setting of the CXX_EXTENSIONS property. If this
@@ -900,6 +913,50 @@ function(mfem_export_mk_files)
string(STRIP
"${cxx_std_flag} ${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
MFEM_CXXFLAGS)
if(MFEM_EXPORT_GPU_CONFIG)
if (MFEM_USE_CUDA)
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} ${CMAKE_CUDA_FLAGS}")
if (MFEM_CUDA_COMPILER_IS_NVCC)
set(MFEM_CXXFLAGS "-x=cu ${MFEM_CXXFLAGS} -ccbin ${CMAKE_CXX_COMPILER} --forward-unknown-to-host-compiler")
# The following intentionally hides CUDA deprecation warnings
foreach(ENTRY IN LISTS CUDAToolkit_INCLUDE_DIRS)
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -isystem ${ENTRY}")
endforeach()
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0)
# architecture flags not part of CMAKE_CUDA_FLAGS
if ("all" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
OR "native" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
OR "all-major" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}")
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -arch=${CMAKE_CUDA_ARCHITECTURES}")
else()
foreach (ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
set(MFEM_CXXFLAGS
"${MFEM_CXXFLAGS} -gencode arch=compute_${ENTRY},code=sm_${ENTRY}")
endforeach()
endif()
endif()
else()
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -xcuda --cuda-path=${CUDAToolkit_LIBRARY_ROOT}")
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0)
# architecture flags not part of CMAKE_CUDA_FLAGS
if ("all" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
OR "native" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
OR "all-major" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}")
# TODO: not supported
else()
foreach(ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
set(MFEM_CXXFLAGS "-cuda-gpu-arch=sm_${ENTRY} ${MFEM_CXXFLAGS}")
endforeach()
endif()
endif()
endif()
elseif (MFEM_USE_HIP)
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -xhip")
foreach(ENTRY IN LISTS CMAKE_HIP_ARCHITECTURES)
set(MFEM_CXXFLAGS "--offload-arch=${ENTRY} ${MFEM_CXXFLAGS}")
endforeach()
endif()
endif()
set(MFEM_TPLFLAGS "")
foreach(dir ${TPL_INCLUDE_DIRS})
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} -I${dir}")
@@ -930,6 +987,9 @@ function(mfem_export_mk_files)
set(MFEM_SHARED NO)
set(MFEM_STATIC YES)
endif()
if (MFEM_USE_CUDA)
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} -lcudart")
endif()
set(MFEM_BUILD_TAG "${CMAKE_SYSTEM}")
set(MFEM_PREFIX "${CMAKE_INSTALL_PREFIX}")
# For the next 4 variables, these are the values for the build-tree version of
@@ -938,8 +998,15 @@ function(mfem_export_mk_files)
set(MFEM_LIB_DIR "${PROJECT_BINARY_DIR}")
set(MFEM_TEST_MK "${PROJECT_SOURCE_DIR}/config/test.mk")
set(MFEM_CONFIG_EXTRA "MFEM_BUILD_DIR ?= ${PROJECT_BINARY_DIR}")
# TODO: CUDA/HIP support:
set(MFEM_XLINKER "${CMAKE_CXX_LINKER_WRAPPER_FLAG}")
if (MFEM_USE_CUDA AND MFEM_EXPORT_GPU_CONFIG)
if (MFEM_CUDA_COMPILER_IS_NVCC)
set(MFEM_XLINKER "-Xlinker=")
else()
set(MFEM_XLINKER "${CMAKE_CUDA_LINKER_WRAPPER_FLAG}")
endif()
else()
set(MFEM_XLINKER "${CMAKE_CXX_LINKER_WRAPPER_FLAG}")
endif()
set(MFEM_MPIEXEC ${MPIEXEC})
if (NOT MFEM_MPIEXEC)
set(MFEM_MPIEXEC "mpirun")
@@ -987,16 +1054,21 @@ function(mfem_export_mk_files)
# handle interfaces (e.g., SCOREC::apf)
if ("${lib}" MATCHES "SCOREC::.*" OR "${lib}" MATCHES "Ginkgo::.*" OR "${lib}" MATCHES "ParMoonolith::.*")
elseif (TARGET "${lib}")
mfem_get_target_options(${lib} CompileOpts LinkOpts)
mfem_get_target_options(${lib} CompileOpts2 LinkOpts2)
# remove generator expressions
string(GENEX_STRIP "${CompileOpts2}" CompileOpts)
string(GENEX_STRIP "${LinkOpts2}" LinkOpts)
# Removing duplicates may lead to issues:
# list(REMOVE_DUPLICATES CompileOpts)
# list(REMOVE_DUPLICATES LinkOpts)
string(REPLACE ";" " " COpts "${CompileOpts}")
string(REPLACE ";" " " LOpts "${LinkOpts}")
# message(STATUS "${lib}[COpts]: '${COpts}'")
# message(STATUS "${lib}[LOpts]: '${LOpts}'")
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} ${COpts}")
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${LOpts}")
# message(WARNING "${lib}[LinkOpts]: ${LinkOpts}")
# message(WARNING "${lib}[CompileOpts]: ${CompileOpts}")
foreach(LOpt IN LISTS LinkOpts)
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${LOpt}")
endforeach()
foreach(COpt IN LISTS CompileOpts)
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} ${COpt}")
endforeach()
# message(FATAL_ERROR "***** interface lib found ... exiting *****")
# handle static and shared libs
elseif ("${suffix}" STREQUAL "${CMAKE_SHARED_LIBRARY_SUFFIX}")
@@ -1004,7 +1076,7 @@ function(mfem_export_mk_files)
get_filename_component(fullLibName ${lib} NAME_WE)
string(REGEX REPLACE "^lib" "" libname ${fullLibName})
set(MFEM_EXT_LIBS
"${MFEM_EXT_LIBS} ${shared_link_flag}${dir} -L${dir} -l${libname}")
"${MFEM_EXT_LIBS} ${shared_link_flag}${dir} -L${dir} -l${libname}")
else()
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${lib}")
endif()
@@ -1013,7 +1085,7 @@ function(mfem_export_mk_files)
# Create the build-tree version of 'config.mk'
configure_file(
"${PROJECT_SOURCE_DIR}/config/config.mk.in"
"${PROJECT_BINARY_DIR}/config/config.mk")
"${PROJECT_BINARY_DIR}/config/config.mk" @ONLY)
# Copy 'test.mk' from the source-tree to the build-tree
configure_file(
"${PROJECT_SOURCE_DIR}/config/test.mk"
@@ -1031,7 +1103,7 @@ function(mfem_export_mk_files)
# Create the install-tree version of 'config.mk'
configure_file(
"${PROJECT_SOURCE_DIR}/config/config.mk.in"
"${PROJECT_BINARY_DIR}/config/config-install.mk")
"${PROJECT_BINARY_DIR}/config/config-install.mk" @ONLY)
# Install rules for 'config.mk' and 'test.mk'
install(FILES ${PROJECT_SOURCE_DIR}/config/test.mk
+2 -2
View File
@@ -5,9 +5,9 @@
// Sample runs:
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 462 -n 10 -o 2 -elast
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 464 -n 10 -o 2 -elast
// mpirun -np 4 ex12p -m ../data/beam-hex.mesh -s 3878
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 81
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 82
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh -s 3877 -o 2 -sys
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh -s 4544 -n 6 -o 3 -elast
// mpirun -np 4 ex12p -m ../data/beam-quad-nurbs.mesh
+27 -9
View File
@@ -302,15 +302,21 @@ int main(int argc, char *argv[])
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
<< "window_title 'Exact: Real Part'" << flush;
// Make sure all ranks have sent their real solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
socketstream sol_sock_i(vishost, visport);
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i.precision(8);
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
<< "window_title 'Exact: Imaginary Part'" << flush;
// Make sure all ranks have sent their imaginary solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
}
// 11. Set up the parallel sesquilinear form a(.,.) on the finite element
@@ -534,15 +540,21 @@ int main(int argc, char *argv[])
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u.real()
<< "window_title 'Solution: Real Part'" << flush;
// Make sure all ranks have sent their real solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
socketstream sol_sock_i(vishost, visport);
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i.precision(8);
sol_sock_i << "solution\n" << *pmesh << u.imag()
<< "window_title 'Solution: Imaginary Part'" << flush;
// Make sure all ranks have sent their imaginary solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
}
if (visualization && exact_sol)
{
@@ -551,15 +563,21 @@ int main(int argc, char *argv[])
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
<< "window_title 'Error: Real Part'" << flush;
// Make sure all ranks have sent their real solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
socketstream sol_sock_i(vishost, visport);
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i.precision(8);
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
<< "window_title 'Error: Imaginary Part'" << flush;
// Make sure all ranks have sent their imaginary solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
}
if (visualization)
{
+2 -8
View File
@@ -97,13 +97,7 @@ int main(int argc, char *argv[])
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
args.ParseCheck();
// 2. Read the mesh from the mesh file.
const char *mesh_file = "../data/disc-nurbs.mesh";
@@ -128,7 +122,7 @@ int main(int argc, char *argv[])
*nodes /= scale;
// 4. Define the necessary finite element spaces on the mesh.
H1_FECollection H1fec(order+1, dim);
H1Bubble_FECollection H1fec(order, order - 1, dim);
FiniteElementSpace H1fes(&mesh, &H1fec);
L2_FECollection L2fec(order-1, dim);
+2 -14
View File
@@ -103,19 +103,7 @@ int main(int argc, char *argv[])
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
args.ParseCheck();
// 2. Read the mesh from the mesh file.
const char *mesh_file = "../data/disc-nurbs.mesh";
@@ -143,7 +131,7 @@ int main(int argc, char *argv[])
mesh.Clear();
// 4. Define the necessary finite element spaces on the mesh.
H1_FECollection H1fec(order+1, dim);
H1Bubble_FECollection H1fec(order, order - 1, dim);
ParFiniteElementSpace H1fes(&pmesh, &H1fec);
L2_FECollection L2fec(order-1, dim);
+3 -1
View File
@@ -73,6 +73,7 @@ set(SRCS
fe/fe_base.cpp
fe/fe_fixed_order.cpp
fe/fe_h1.cpp
fe/fe_h1_bubble.cpp
fe/fe_l2.cpp
fe/fe_nd.cpp
fe/fe_nurbs.cpp
@@ -133,7 +134,7 @@ set(SRCS
tmop/assemble/diag2.cpp
tmop/assemble/grad2_limit.cpp
tmop/assemble/grad2.cpp
tmop/assemble/diag3_limit.cpp
tmop/assemble/diag3_limit.cpp
tmop/assemble/diag3.cpp
tmop/assemble/grad3_limit.cpp
tmop/assemble/grad3.cpp
@@ -221,6 +222,7 @@ set(HDRS
fe/fe_base.hpp
fe/fe_fixed_order.hpp
fe/fe_h1.hpp
fe/fe_h1_bubble.hpp
fe/fe_l2.hpp
fe/fe_nd.hpp
fe/fe_nurbs.hpp
+7 -3
View File
@@ -729,7 +729,8 @@ void BilinearForm::Assemble(int skip_zeros)
tr = mesh -> GetBdrFaceTransformations (i);
if (tr != NULL)
{
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
mfem::DofTransformation doftrans;
fes -> GetElementVDofs (tr -> Elem1No, vdofs, doftrans);
fe1 = fes -> GetFE (tr -> Elem1No);
// The fe2 object is really a dummy and not used on the boundaries,
// but we can't dereference a NULL pointer, and we don't want to
@@ -743,6 +744,7 @@ void BilinearForm::Assemble(int skip_zeros)
boundary_face_integs[k] -> AssembleFaceMatrix (*fe1, *fe2, *tr,
elemmat);
doftrans.TransformDual(elemmat);
mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros);
}
}
@@ -1723,6 +1725,7 @@ void MixedBilinearForm::Assemble(int skip_zeros)
}
}
DofTransformation dom_dof_trans, ran_dof_trans;
for (int i = 0; i < trial_fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
@@ -1731,8 +1734,8 @@ void MixedBilinearForm::Assemble(int skip_zeros)
ftr = mesh -> GetBdrFaceTransformations (i);
if (ftr != NULL)
{
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs);
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs);
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs, dom_dof_trans);
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs, ran_dof_trans);
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
test_fe1 = test_fes->GetFE(ftr->Elem1No);
// The test_fe2 object is really a dummy and not used on the
@@ -1748,6 +1751,7 @@ void MixedBilinearForm::Assemble(int skip_zeros)
boundary_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
*test_fe2,
*ftr, elemmat);
TransformDual(ran_dof_trans, dom_dof_trans, elemmat);
mat->AddSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
}
}
+1 -1
View File
@@ -2710,7 +2710,7 @@ public:
/** Integrator for $(-Q u, \nabla v)$ for Nedelec ($u$) and $H^1$ ($v$) elements.
This is equivalent to a weak divergence of the $H(curl$ basis functions. */
This is equivalent to a weak divergence of the $H(curl)$ basis functions. */
class VectorFEWeakDivergenceIntegrator: public BilinearFormIntegrator
{
protected:
+19
View File
@@ -82,6 +82,25 @@ public:
/// underlying #fes
int VectorDim() const;
/// Copy assignment. Only the data of the base class Vector is copied.
/** It is assumed that this object and @a rhs use FiniteElementSpace%s that
have the same size.
@note Defining this method overwrites the implicitly defined copy
assignment operator. */
ComplexGridFunction &operator=(const ComplexGridFunction &rhs)
{ return operator=((const Vector &)rhs); }
/// Copy the data from @a v.
/** The size of @a v must be equal to double of the size of the associated
FiniteElementSpace #fes. */
ComplexGridFunction &operator=(const Vector &v)
{
MFEM_ASSERT(fes && v.Size() == 2*fes->GetVSize(), "");
Vector::operator=(v);
return *this;
}
/// Assign constant values to the ComplexGridFunction data.
ComplexGridFunction &operator=(const std::complex<real_t> & value)
{ *gfr = value.real(); *gfi = value.imag(); return *this; }
+41 -215
View File
@@ -84,33 +84,29 @@ public:
const std::vector<derivative_action_t> &derivative_actions,
const FieldDescriptor &direction,
const int &daction_l_size,
const int &derivative_action_tr_l_size,
const std::vector<derivative_action_t> &derivative_tr_actions,
const std::vector<derivative_action_t> &derivative_actions_transpose,
const FieldDescriptor &transpose_direction,
const int &daction_transpose_l_size,
const std::vector<Vector *> &solutions_l,
const std::vector<Vector *> &parameters_l,
const restriction_callback_t &restriction_callback,
const std::function<void(Vector &, Vector &)> &prolongation_transpose,
const std::function<void(Vector &, Vector &)> &tr_prolongation_transpose,
const std::vector<assemble_derivative_sparsematrix_callback_t>
&assemble_derivative_sparsematrix_callbacks,
const assemble_derivative_hypreparmatrix_callback_t
&assemble_derivative_hypreparmatrix_callback) :
const std::vector<assemble_derivative_hypreparmatrix_callback_t>
&assemble_derivative_hypreparmatrix_callbacks) :
Operator(height, width),
derivative_actions(derivative_actions),
direction(direction),
daction_l(daction_l_size),
daction_l_size(daction_l_size),
derivative_action_tr_l_size(derivative_action_tr_l_size),
derivative_tr_actions(derivative_tr_actions),
derivative_actions_transpose(derivative_actions_transpose),
transpose_direction(transpose_direction),
prolongation_transpose(prolongation_transpose),
tr_prolongation_transpose(tr_prolongation_transpose),
assemble_derivative_sparsematrix_callbacks(
assemble_derivative_sparsematrix_callbacks),
assemble_derivative_hypreparmatrix_callback(
assemble_derivative_hypreparmatrix_callback)
assemble_derivative_hypreparmatrix_callbacks(
assemble_derivative_hypreparmatrix_callbacks)
{
std::vector<Vector> s_l(solutions_l.size());
for (size_t i = 0; i < s_l.size(); i++)
@@ -160,18 +156,18 @@ public:
/// direction_t on T-dofs.
void MultTranspose(const Vector &direction_t, Vector &result_t) const override
{
MFEM_ASSERT(!derivative_tr_actions.empty(),
MFEM_ASSERT(!derivative_actions_transpose.empty(),
"derivative can't be used to be multiplied in transpose mode");
daction_l.SetSize(derivative_action_tr_l_size);
daction_l.SetSize(width);
daction_l = 0.0;
prolongation(transpose_direction, direction_t, direction_l);
for (const auto &f : derivative_tr_actions)
for (const auto &f : derivative_actions_transpose)
{
f(fields_e, direction_l, daction_l);
}
tr_prolongation_transpose(daction_l, result_t);
prolongation_transpose(daction_l, result_t);
};
/// @brief Assemble the derivative operator into a SparseMatrix.
@@ -187,10 +183,6 @@ public:
{
f(fields_e, A);
}
// SparseMatrix A is finalized after all callbacks have contributed to
// it.
A->Finalize();
}
/// @brief Assemble the derivative operator into a HypreParMatrix.
@@ -199,7 +191,13 @@ public:
/// be an uninitialized object.
void Assemble(HypreParMatrix *&A)
{
assemble_derivative_hypreparmatrix_callback(fields_e, A);
MFEM_ASSERT(!assemble_derivative_hypreparmatrix_callbacks.empty(),
"derivative can't be assembled into a HypreParMatrix");
for (const auto &f : assemble_derivative_hypreparmatrix_callbacks)
{
f(fields_e, A);
}
}
private:
@@ -214,12 +212,10 @@ private:
const int daction_l_size;
const int derivative_action_tr_l_size;
/// Transpose Derivative action callbacks. Depending on the requested
/// derivatives in DifferentiableOperator the callbacks represent certain
/// combinations of actions of derivatives of the forward operator.
std::vector<derivative_action_t> derivative_tr_actions;
std::vector<derivative_action_t> derivative_actions_transpose;
FieldDescriptor transpose_direction;
@@ -229,15 +225,13 @@ private:
std::function<void(Vector &, Vector &)> prolongation_transpose;
std::function<void(Vector &, Vector &)> tr_prolongation_transpose;
/// Callbacks that assemble derivatives into a SparseMatrix.
std::vector<assemble_derivative_sparsematrix_callback_t>
assemble_derivative_sparsematrix_callbacks;
/// Callbacks that assemble derivatives into a HypreParMatrix.
assemble_derivative_hypreparmatrix_callback_t
assemble_derivative_hypreparmatrix_callback;
std::vector<assemble_derivative_hypreparmatrix_callback_t>
assemble_derivative_hypreparmatrix_callbacks;
};
/// Class representing a differentiable operator which acts on solution and
@@ -463,10 +457,7 @@ public:
dir_l = s_l[derivative_idx];
}
for (size_t i = 0; i < derivative_setup_callbacks[derivative_id].size(); i++)
{
derivative_setup_callbacks[derivative_id][i](fields_e, dir_l);
}
derivative_setup_callbacks[derivative_id][0](fields_e, dir_l);
return std::make_shared<DerivativeOperator>(
height,
@@ -474,17 +465,15 @@ public:
derivative_action_callbacks[derivative_id],
fields[derivative_idx],
residual_l.Size(),
derivative_action_tr_l_size[derivative_id],
derivative_action_tr_callbacks[derivative_id],
daction_transpose_callbacks[derivative_id],
fields[test_space_field_idx],
GetVSize(fields[test_space_field_idx]),
sol_l,
par_l,
restriction_callback,
prolongation_transpose,
derivative_tr_prolongation_transpose[derivative_id],
assemble_derivative_sparsematrix_callbacks[derivative_id],
assemble_derivative_hypreparmatrix_callback[derivative_id]);
assemble_derivative_hypreparmatrix_callbacks[derivative_id]);
}
private:
@@ -497,15 +486,13 @@ private:
std::map<size_t,
std::vector<derivative_action_t>> derivative_action_callbacks;
std::map<size_t,
std::vector<derivative_action_t>> derivative_action_tr_callbacks;
std::map<size_t,
std::function<void(Vector &, Vector &)>> derivative_tr_prolongation_transpose;
std::map<size_t, int> derivative_action_tr_l_size;
std::vector<derivative_action_t>> daction_transpose_callbacks;
std::map<size_t,
std::vector<assemble_derivative_sparsematrix_callback_t>>
assemble_derivative_sparsematrix_callbacks;
std::map<size_t, assemble_derivative_hypreparmatrix_callback_t>
assemble_derivative_hypreparmatrix_callback;
std::map<size_t,
std::vector<assemble_derivative_hypreparmatrix_callback_t>>
assemble_derivative_hypreparmatrix_callbacks;
std::vector<FieldDescriptor> solutions;
std::vector<FieldDescriptor> parameters;
@@ -523,7 +510,7 @@ private:
std::function<void(Vector &, Vector &)> output_restriction_transpose;
restriction_callback_t restriction_callback;
std::map<size_t, std::vector<Vector>> derivative_qp_caches;
std::map<size_t, Vector> derivative_qp_caches;
std::map<size_t, size_t> assembled_vector_sizes;
@@ -782,10 +769,9 @@ void DifferentiableOperator::AddIntegrator(
auto input_size_on_qp =
get_input_size_on_qp(inputs, std::make_index_sequence<num_inputs> {});
// printf("calculate shmem action info\n");
auto action_shmem_info =
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
(input_dtq_maps, output_dtq_maps, fields, num_entities, num_qp,
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
input_size_on_qp, residual_size_on_qp, element_dof_ordering);
Vector shmem_cache(action_shmem_info.total_size);
@@ -908,10 +894,9 @@ void DifferentiableOperator::AddIntegrator(
const int da_size_on_qp =
GetSizeOnQP<entity_t>(output_fop, fields[test_space_field_idx]);
// printf("calculate shmem derivative action info\n");
auto shmem_info =
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>(
input_dtq_maps, output_dtq_maps, fields, num_entities,
input_dtq_maps, output_dtq_maps, fields, num_entities, inputs,
num_qp, input_size_on_qp, residual_size_on_qp,
element_dof_ordering, d_field_idx);
@@ -983,12 +968,9 @@ void DifferentiableOperator::AddIntegrator(
// Quadrature point local derivative cache for each element, with data
// layout:
// [test_vdim, test_op_dim, trial_vdim, trial_op_dim, qp, num_entities].
derivative_qp_caches[derivative_id].push_back(
Vector(test_vdim * test_op_dim * trial_vdim * total_trial_op_dim * num_qp *
num_entities));
const int cache_index = this->derivative_qp_caches[derivative_id].size() - 1;
derivative_qp_caches[derivative_id] = Vector(test_vdim * test_op_dim *
trial_vdim *
total_trial_op_dim * num_qp * num_entities);
// Create local references for MSVC lambda capture compatibility
auto& fields_ref = this->fields;
auto& derivative_qp_caches_ref = this->derivative_qp_caches[derivative_id];
@@ -1030,8 +1012,6 @@ void DifferentiableOperator::AddIntegrator(
trial_vdim,
inputs_trial_op_dim,
qpdc_idx = cache_index,
// capture by ref:
&qpdc_mem = derivative_qp_caches_ref
](std::vector<Vector> &f_e, const Vector &dir_l) mutable
@@ -1044,7 +1024,7 @@ void DifferentiableOperator::AddIntegrator(
shmem_info.direction_size,
num_entities);
auto qpdc = Reshape(qpdc_mem[qpdc_idx].ReadWrite(), test_vdim, test_op_dim,
auto qpdc = Reshape(qpdc_mem.ReadWrite(), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp, num_entities);
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
@@ -1115,7 +1095,6 @@ void DifferentiableOperator::AddIntegrator(
inputs_trial_op_dim,
total_trial_op_dim,
trial_vdim,
qpdc_idx = cache_index,
// capture by ref:
&qpdc_mem = derivative_qp_caches_ref,
&or_transpose
@@ -1133,7 +1112,7 @@ void DifferentiableOperator::AddIntegrator(
shmem_info.direction_size,
num_entities);
auto qpdc = Reshape(qpdc_mem[qpdc_idx].Read(), test_vdim, test_op_dim,
auto qpdc = Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp, num_entities);
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
@@ -1178,154 +1157,6 @@ void DifferentiableOperator::AddIntegrator(
or_transpose(derivative_action_e, der_action_l);
});
// This prevents Sum/Identity. These are invalid
// as input FieldOperators anyways.
constexpr auto dummy_fop = Value<0> {};
auto [input_rt,
input_e_sz] = get_restriction_transpose<entity_t>
(fields[d_field_idx],
element_dof_ordering, dummy_fop);
const auto input_restriction_transpose = input_rt;
derivative_tr_prolongation_transpose[derivative_id] =
get_prolongation_transpose(
fields[d_field_idx], dummy_fop, mesh.GetComm());
const auto d_tr_field_idx = test_space_field_idx;
const auto direction_tr = fields[d_tr_field_idx];
auto output_size_on_qp =
get_input_size_on_qp(outputs, std::make_index_sequence<num_outputs> {});
const int residual_tr_size_on_qp = trial_vdim * total_trial_op_dim;
auto shmem_tr_info =
get_shmem_info<entity_t, num_fields, num_outputs, num_inputs>(
output_dtq_maps, input_dtq_maps, fields, num_entities,
num_qp, output_size_on_qp, residual_tr_size_on_qp,
element_dof_ordering, test_space_field_idx);
// print_shared_memory_info(shmem_tr_info);
// TODO: this is a hack to extend the shared memory with a known
// offset for a temp variable
Vector shmem_tr_cache(shmem_tr_info.total_size + residual_tr_size_on_qp *
num_qp);
Vector direction_tr_e(get_restriction<entity_t>(
fields[test_space_field_idx],
element_dof_ordering)->Height());
derivative_action_tr_l_size[derivative_id] =
get_restriction<entity_t>(fields[d_field_idx],
element_dof_ordering)->Width();
Vector derivative_action_tr_e(input_e_sz);
derivative_action_tr_e = 0.0;
derivative_action_tr_callbacks[derivative_id].push_back(
[
// capture by copy:
dimension, // int
num_entities, // int
num_trial_dof, // int
num_qp, // int
q1d, // int
test_vdim, // int (= output_fop.vdim)
test_op_dim, // int (derived from output_fop)
inputs, // mfem::future::tuple
outputs, // mfem::future::tuple
attributes, // Array<int>
ir_weights, // DeviceTensor
use_sum_factorization, // bool
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
// output_fop, // class derived from FieldOperator
thread_blocks, // ThreadBlocks
shmem_tr_cache, // Vector (local)
shmem_tr_info, // SharedMemoryInfo
// TODO: make this Array<int> a member of the DifferentiableOperator
// and capture it by ref.
elem_attributes, // Array<int>
input_is_dependent,
direction_tr, // FieldDescriptor
direction_tr_e, // Vector
derivative_action_tr_e, // Vector
element_dof_ordering, // ElementDofOrdering
inputs_trial_op_dim,
total_trial_op_dim,
trial_vdim,
input_restriction_transpose,
qpdc_idx = cache_index,
// capture by ref:
&qpdc_mem = derivative_qp_caches_ref
](
std::vector<Vector> &f_e, const Vector &dir_tr_l,
Vector &derivative_action_tr_l) mutable
{
restriction<entity_t>(direction_tr, dir_tr_l, direction_tr_e,
element_dof_ordering);
auto ye = Reshape(derivative_action_tr_e.ReadWrite(), num_trial_dof,
trial_vdim, num_entities);
auto wrapped_fields_e = wrap_fields(f_e, shmem_tr_info.field_sizes,
num_entities);
auto wrapped_direction_e = Reshape(direction_tr_e.ReadWrite(),
shmem_tr_info.direction_size,
num_entities);
auto qpdc = Reshape(qpdc_mem[qpdc_idx].Read(), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp, num_entities);
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
const bool has_attr = attributes.Size() > 0;
const auto d_attr = attributes.Read();
const auto d_elem_attr = elem_attributes->Read();
derivative_action_tr_e = 0.0;
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
{
if (has_attr && !d_attr[d_elem_attr[e] - 1]) { return; }
auto [output_dtq_shmem, input_dtq_shmem, fields_shmem,
direction_shmem, input_shmem,
shadow_shmem_, residual_shmem,
scratch_shmem] =
unpack_shmem(shmem, shmem_tr_info, output_dtq_maps, input_dtq_maps,
wrapped_fields_e, wrapped_direction_e, num_qp, e);
auto &shadow_shmem = shadow_shmem_;
std::array<bool, num_outputs> all_true{true};
map_direction_to_quadrature_data_conditional(
shadow_shmem, direction_shmem, output_dtq_shmem, outputs,
ir_weights, scratch_shmem, all_true, dimension,
use_sum_factorization);
auto fhat = Reshape(&residual_shmem(0, 0), trial_vdim,
total_trial_op_dim, num_qp);
auto qpdce = Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp);
constexpr bool transpose = true;
apply_qpdc(fhat, shadow_shmem, qpdce, itod, q1d, dimension,
use_sum_factorization, transpose);
auto y = Reshape(&ye(0, 0, e), num_trial_dof, trial_vdim);
auto fi_shmem = Reshape(shmem + shmem_tr_info.total_size, trial_vdim,
total_trial_op_dim, num_qp);
map_quadrature_data_to_fields_conditional(
y, fhat, inputs, itod, input_dtq_shmem, scratch_shmem, fi_shmem,
input_is_dependent, dimension, use_sum_factorization);
}, num_entities, thread_blocks, shmem_tr_info.total_size,
shmem_tr_cache.ReadWrite());
input_restriction_transpose(derivative_action_tr_e, derivative_action_tr_l);
});
assemble_derivative_sparsematrix_callbacks[derivative_id].push_back(
[
// capture by copy:
@@ -1359,7 +1190,7 @@ void DifferentiableOperator::AddIntegrator(
inputs_trial_op_dim,
Ae_mem,
output_to_field,
qpdc_idx = cache_index,
// capture by ref:
&qpdc_mem = derivative_qp_caches_ref,
&fields = fields_ref
@@ -1371,7 +1202,7 @@ void DifferentiableOperator::AddIntegrator(
shmem_info.direction_size,
num_entities);
auto qpdc = Reshape(qpdc_mem[qpdc_idx].Read(), test_vdim, test_op_dim,
auto qpdc = Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp, num_entities);
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
@@ -1419,10 +1250,7 @@ void DifferentiableOperator::AddIntegrator(
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
(&fields[output_to_field[0]].data);
if (A == nullptr)
{
A = new SparseMatrix(test_fes->GetVSize(), trial_fes->GetVSize());
}
A = new SparseMatrix(test_fes->GetVSize(), trial_fes->GetVSize());
auto tmp = Reshape(Ae_mem.HostReadWrite(), num_test_dof * test_vdim,
num_trial_dof * trial_vdim, num_entities);
@@ -1493,15 +1321,14 @@ void DifferentiableOperator::AddIntegrator(
A->AddSubMatrix(test_vdofs, trial_vdofs, Aee, 1);
}
}
// Don't finalize here since multiple callbacks might contribute to the same matrix
// A->Finalize() will be called after all callbacks have contributed
A->Finalize();
});
// Create local references for MSVC lambda capture compatibility
auto& assemble_derivative_sparsematrix_callbacks_ref =
this->assemble_derivative_sparsematrix_callbacks[derivative_id];
assemble_derivative_hypreparmatrix_callback[derivative_id] =
assemble_derivative_hypreparmatrix_callbacks[derivative_id].push_back(
[
input_is_dependent,
input_to_field,
@@ -1515,7 +1342,6 @@ void DifferentiableOperator::AddIntegrator(
{
f(f_e, spmat);
}
spmat->Finalize();
if (spmat == nullptr)
{
@@ -1569,7 +1395,7 @@ void DifferentiableOperator::AddIntegrator(
trial_fes->Dof_TrueDof_Matrix());
}
delete spmat;
};
});
}, derivative_ids);
}
}
+11 -69
View File
@@ -90,8 +90,8 @@ void map_quadrature_data_to_fields_impl(
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor");
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
" for this field descriptor");
}
}
@@ -169,8 +169,9 @@ void map_quadrature_data_to_fields_tensor_impl_1d(
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor with sum factorization on tensor product elements");
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
"for this field descriptor with sum factorization on"
" tensor product elements");
}
}
@@ -306,8 +307,9 @@ void map_quadrature_data_to_fields_tensor_impl_2d(
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor with sum factorization on tensor product elements");
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
" for this field descriptor with sum factorization on"
" tensor product elements");
}
}
@@ -492,8 +494,9 @@ void map_quadrature_data_to_fields_tensor_impl_3d(
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor with sum factorization on tensor product elements");
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
" for this field descriptor with sum factorization on"
" tensor product elements");
}
}
@@ -530,65 +533,4 @@ void map_quadrature_data_to_fields(
}
}
template <size_t N, typename field_operator_ts>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields_conditional(
DeviceTensor<2, real_t> &y,
const DeviceTensor<3, real_t> &f,
const field_operator_ts &fops,
const DeviceTensor<1, const real_t> &op_dims,
const std::array<DofToQuadMap, N> &dtqmaps,
std::array<DeviceTensor<1>, 6> &scratch_mem,
const DeviceTensor<3> &fi_shmem,
const std::array<bool, N> &conditions,
const int &dimension,
const bool &use_sum_factorization)
{
int offset = 0;
for_constexpr<N>([&](auto i)
{
if (conditions[i])
{
[[maybe_unused]] const auto [K, unused, M] = f.GetShape();
const int L = static_cast<int>(op_dims(static_cast<size_t>(i)));
auto fi = Reshape(&fi_shmem(0, 0, 0), K, L, M);
for (int k = 0; k < K; k++)
{
for (int l = 0; l < L; l++)
{
for (int m = 0; m < M; m++)
{
fi(k, l, m) = f(k, l + offset, m);
}
}
}
if (use_sum_factorization)
{
if (dimension == 1)
{
map_quadrature_data_to_fields_tensor_impl_1d(
y, fi, get<i>(fops), dtqmaps[i], scratch_mem);
}
else if (dimension == 2)
{
map_quadrature_data_to_fields_tensor_impl_2d(
y, fi, get<i>(fops), dtqmaps[i], scratch_mem);
}
else if (dimension == 3)
{
map_quadrature_data_to_fields_tensor_impl_3d(
y, fi, get<i>(fops), dtqmaps[i], scratch_mem);
}
else { MFEM_ABORT_KERNEL("dimension not supported"); }
}
else
{
map_quadrature_data_to_fields_impl(y, fi, get<i>(fops), dtqmaps[i]);
}
offset += L;
}
});
}
} // namespace mfem::future
+17 -12
View File
@@ -505,13 +505,13 @@ void map_field_to_quadrature_data(
}
}
template <typename field_operator_ts, size_t N, size_t M>
template <typename field_operator_ts, size_t num_inputs, size_t num_fields>
MFEM_HOST_DEVICE inline
void map_fields_to_quadrature_data(
std::array<DeviceTensor<2>, N> &fields_qp,
const std::array<DeviceTensor<1>, M> &fields_e,
const std::array<DofToQuadMap, N> &dtqmaps,
const std::array<size_t, N> &input_to_field,
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
const std::array<DeviceTensor<1>, num_fields> &fields_e,
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
const std::array<size_t, num_inputs> &input_to_field,
const field_operator_ts &fops,
const DeviceTensor<1, const real_t> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
@@ -523,7 +523,7 @@ void map_fields_to_quadrature_data(
// attached to them and we create a dummy field which is not accessed
// inside the functions it is passed to.
const auto dummy_field_weight = DeviceTensor<1>(nullptr, 0);
for_constexpr<N>([&](auto i)
for_constexpr<num_inputs>([&](auto i)
{
const DeviceTensor<1> &field_e =
(input_to_field[i] == SIZE_MAX) ? dummy_field_weight :
@@ -549,7 +549,12 @@ void map_fields_to_quadrature_data(
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
integration_weights, scratch_mem);
}
else { MFEM_ABORT_KERNEL("unsupported dimension"); }
else
{
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
MFEM_ABORT("unsupported dimension");
#endif
}
}
else
{
@@ -622,20 +627,20 @@ void map_fields_to_quadrature_data_conditional(
});
}
template <size_t N, typename field_operator_ts>
template <size_t num_inputs, typename field_operator_ts>
MFEM_HOST_DEVICE
void map_direction_to_quadrature_data_conditional(
std::array<DeviceTensor<2>, N> &directions_qp,
std::array<DeviceTensor<2>, num_inputs> &directions_qp,
const DeviceTensor<1> &direction_e,
const std::array<DofToQuadMap, N> &dtqmaps,
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
field_operator_ts fops,
const DeviceTensor<1, const real_t> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
const std::array<bool, N> &conditions,
const std::array<bool, num_inputs> &conditions,
const int &dimension,
const bool &use_sum_factorization)
{
for_constexpr<N>([&](auto i)
for_constexpr<num_inputs>([&](auto i)
{
if (conditions[i])
{
+1 -1
View File
@@ -82,7 +82,7 @@ protected:
};
/// @brief Uniform parameter space
class UniformParameterSpace final : public ParameterSpace
class UniformParameterSpace : public ParameterSpace
{
public:
/// @brief Constructor for a uniform parameter space
+31 -81
View File
@@ -379,102 +379,54 @@ namespace detail
/// @param shadow_shmem the shadow shared memory.
/// @param qpdc the quadrature point data cache holding the resulting
/// Jacobians on each quadrature point.
/// @param op_dims operator dimensions.
/// If an operator is dependent, the value corresponds to the spatial dimension.
/// Otherwise a zero indicates indepence on the variable.
/// @param itod inputs trial operator dimension.
/// If input is dependent the value corresponds to the spatial dimension, otherwise
/// a zero indicates non-dependence on the variable.
/// @param q the current quadrature point index.
/// @param transpose switch to use transpose action.
template <size_t N>
template <size_t num_fields>
MFEM_HOST_DEVICE inline
void apply_qpdc(
DeviceTensor<3> &fhat,
const std::array<DeviceTensor<2>, N> &shadow_shmem,
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
const DeviceTensor<5, const real_t> &qpdc,
const DeviceTensor<1, const real_t> &op_dims,
const int &q,
bool transpose)
const DeviceTensor<1, const real_t> &itod,
const int &q)
{
const size_t num_ops = op_dims.GetShape()[0];
const int test_vdim = qpdc.GetShape()[0];
const int test_op_dim = qpdc.GetShape()[1];
const int trial_vdim = qpdc.GetShape()[2];
const int total_trial_op_dim = qpdc.GetShape()[3];
const int num_qp = qpdc.GetShape()[4];
const size_t num_inputs = itod.GetShape()[0];
if (transpose)
for (int i = 0; i < test_vdim; i++)
{
for (int j = 0; j < trial_vdim; j++)
for (int k = 0; k < test_op_dim; k++)
{
for (int m = 0; m < total_trial_op_dim; m++)
real_t sum = 0.0;
int m_offset = 0;
for (size_t s = 0; s < num_inputs; s++)
{
fhat(j, m, q) = 0.0;
}
}
// Since we don't support more than output space right now
// shadow_shmem will always be of size 1.
constexpr int shadow_idx_tr = 0;
auto d_qp = Reshape(&(shadow_shmem[shadow_idx_tr])[0], test_vdim, test_op_dim,
num_qp);
int m_offset = 0;
for (size_t s = 0; s < num_ops; s++)
{
const int trial_op_dim = static_cast<int>(op_dims(s));
if (trial_op_dim == 0) { continue; }
for (int j = 0; j < trial_vdim; j++)
{
for (int m = 0; m < trial_op_dim; m++)
const int trial_op_dim = static_cast<int>(itod(s));
if (trial_op_dim == 0)
{
real_t sum = 0.0;
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
const real_t contrib = qpdc(i, k, j, m + m_offset, q) * d_qp(i, k, q);
sum += contrib;
}
}
fhat(j, m + m_offset, q) += sum;
continue;
}
}
m_offset += trial_op_dim;
}
}
else
{
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
real_t sum = 0.0;
int m_offset = 0;
for (size_t s = 0; s < num_ops; s++)
const auto d_qp =
Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
for (int j = 0; j < trial_vdim; j++)
{
const int trial_op_dim = static_cast<int>(op_dims(s));
if (trial_op_dim == 0) { continue; }
const auto d_qp =
Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
for (int j = 0; j < trial_vdim; j++)
for (int m = 0; m < trial_op_dim; m++)
{
for (int m = 0; m < trial_op_dim; m++)
{
sum += qpdc(i, k, j, m + m_offset, q) * d_qp(j, m, q);
}
sum += qpdc(i, k, j, m + m_offset, q) * d_qp(j, m, q);
}
m_offset += trial_op_dim;
}
fhat(i, k, q) = sum;
m_offset += trial_op_dim;
}
fhat(i, k, q) = sum;
}
}
}
} // namespace detail
}
/// @brief Apply the quadrature point data cache (qpdc) to a vector
/// (usually a direction).
@@ -493,18 +445,16 @@ void apply_qpdc(
/// @param q1d number of quadrature points in 1D.
/// @param dimension spatial dimension.
/// @param use_sum_factorization whether to use sum factorization.
/// @param T switch to use transpose application.
template <size_t N>
template <size_t num_fields>
MFEM_HOST_DEVICE inline
void apply_qpdc(
DeviceTensor<3> &fhat,
const std::array<DeviceTensor<2>, N> &shadow_shmem,
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
const DeviceTensor<5, const real_t> &qpdc,
const DeviceTensor<1, const real_t> &itod,
const int &q1d,
const int &dimension,
const bool &use_sum_factorization,
const bool T = false)
const bool &use_sum_factorization)
{
if (use_sum_factorization)
{
@@ -512,7 +462,7 @@ void apply_qpdc(
{
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
{
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
}
}
else if (dimension == 2)
@@ -522,7 +472,7 @@ void apply_qpdc(
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
{
const int q = qx + q1d * qy;
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
}
}
}
@@ -535,7 +485,7 @@ void apply_qpdc(
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
}
}
}
@@ -550,7 +500,7 @@ void apply_qpdc(
const int num_qp = qpdc.GetShape()[4];
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
{
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q, T);
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
}
}
}
+16 -29
View File
@@ -243,35 +243,6 @@ void process_qf_arg(
}
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1, T> &u,
const DeviceTensor<1, T> &v,
tensor<T, n> &arg)
{
for (int i = 0; i < n; i++)
{
arg(i) = u(i);
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1, T> &u,
const DeviceTensor<1, T> &v,
tensor<T, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i) = u((i * n) + j);
}
}
}
template <typename arg_type>
MFEM_HOST_DEVICE inline
void process_qf_arg(const DeviceTensor<2> &u, arg_type &arg, int qp)
@@ -356,4 +327,20 @@ void process_qf_result(
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_qf_arg(
const DeviceTensor<1, T> &u,
const DeviceTensor<1, T> &v,
tensor<T, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i) = u((i * n) + j);
}
}
}
} // namespace mfem::future
+3 -9
View File
@@ -1202,14 +1202,7 @@ std::function<void(const Vector&, Vector&)> get_prolongation_transpose(
const Operator *P = get_prolongation(f);
auto PT = [=](const Vector &r_local, Vector &y)
{
if (P)
{
P->MultTranspose(r_local, y);
}
else
{
y = r_local;
}
P->MultTranspose(r_local, y);
};
return PT;
}
@@ -1587,13 +1580,14 @@ struct SharedMemoryInfo
std::array<int, 6> temp_sizes;
};
template <typename entity_t, std::size_t num_fields, std::size_t num_inputs, std::size_t num_outputs>
template <typename entity_t, std::size_t num_fields, std::size_t num_inputs, std::size_t num_outputs, typename input_t>
SharedMemoryInfo<num_fields, num_inputs, num_outputs>
get_shmem_info(
const std::array<DofToQuadMap, num_inputs> &input_dtq_maps,
const std::array<DofToQuadMap, num_outputs> &output_dtq_maps,
const std::vector<FieldDescriptor> &fields,
const int &num_entities,
const input_t &inputs,
const int &num_qp,
const std::vector<int> &input_size_on_qp,
const int &residual_size_on_qp,
+1
View File
@@ -20,6 +20,7 @@
#include "fe/fe_base.hpp"
#include "fe/fe_fixed_order.hpp"
#include "fe/fe_h1.hpp"
#include "fe/fe_h1_bubble.hpp"
#include "fe/fe_nd.hpp"
#include "fe/fe_rt.hpp"
#include "fe/fe_l2.hpp"
+82 -5
View File
@@ -1044,9 +1044,50 @@ void VectorFiniteElement::SetDerivMembers()
switch (map_type)
{
case H_DIV:
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
switch (dim)
{
case 3: // div: 3D H_DIV -> 3D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
case 2: // div: 2D H_DIV -> 2D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_DIV_R2D:
switch (dim)
{
case 2: // div: 2D H_DIV_R2D -> 2D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
case 1: // div: 1D H_DIV_R2D -> 1D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_DIV_R1D:
switch (dim)
{
case 1: // div: 1D H_DIV_R1D -> 1D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_CURL:
switch (dim)
@@ -1064,13 +1105,49 @@ void VectorFiniteElement::SetDerivMembers()
break;
case 1:
deriv_type = NONE;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
deriv_range_type = UNKNOWN_RANGE_TYPE;
deriv_map_type = UNKNOWN_MAP_TYPE;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_CURL_R2D:
switch (dim)
{
case 2:
// curl: 2D H_CURL_R2D -> H_DIV_R2D
deriv_type = CURL;
deriv_range_type = VECTOR;
deriv_map_type = H_DIV_R2D;
break;
case 1:
// curl: 1D H_CURL_R2D -> H_DIV_R2D
deriv_type = CURL;
deriv_range_type = VECTOR;
deriv_map_type = H_DIV_R2D;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_CURL_R1D:
switch (dim)
{
case 1:
// curl: 1D H_CURL_R1D -> H_DIV_R1D
deriv_type = CURL;
deriv_range_type = VECTOR;
deriv_map_type = H_DIV_R1D;
break;
case 0:
deriv_type = NONE;
deriv_range_type = UNKNOWN_RANGE_TYPE;
deriv_map_type = UNKNOWN_MAP_TYPE;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
default:
MFEM_ABORT("Invalid MapType = " << map_type);
}
+31 -3
View File
@@ -295,10 +295,20 @@ public:
$ u(x) = (1/w) \hat u(\hat x) $ */
H_DIV, /**< For vector fields; preserves surface integrals of the
normal component $ u(x) = (J/w) \hat u(\hat x) $ */
H_CURL /**< For vector fields; preserves line integrals of the
H_CURL, /**< For vector fields; preserves line integrals of the
tangential component
$ u(x) = J^{-t} \hat u(\hat x) $ (square J),
$ u(x) = J(J^t J)^{-1} \hat u(\hat x) $ (general J) */
H_DIV_R2D, /**< For 3-component vector fields in 2D; equivalent to a
direct sum of an H_DIV basis and an INTEGRAL basis */
H_CURL_R2D,/**< For 3-component vector fields in 2D; equivalent to a
direct sum of an H_CURL basis and a VALUE basis */
H_DIV_R1D, /**< For 3-component vector fields in 1D; equivalent to a
direct sum of a VALUE basis and a pair of INTEGRAL
bases */
H_CURL_R1D /**< For 3-component vector fields in 1D; equivalent to a
direct sum of an INTEGRAL basis and a pair of VALUE
bases */
};
/** @brief Enumeration for DerivType: defines which derivative method
@@ -330,12 +340,28 @@ public:
int GetDim() const { return dim; }
/** @brief Returns the vector dimension for vector-valued finite elements,
which is also the dimension of the interpolation operation. */
which is also the dimension of the interpolation operation and the
width of the DenseMatrix argument in
CalcVShape(const IntegrationPoint &ip, DenseMatrix &shape). */
int GetRangeDim() const { return vdim; }
/// Returns the dimension of the curl for vector-valued finite elements.
/** @brief Returns the vector dimension, in physical space, for
vector-valued finite elements, which is also the width of the
DenseMatrix argument in
CalcPhysVShape(ElementTransformation &Trans, DenseMatrix &shape). */
int GetPhysRangeDim(int /* space_dim */) const { return vdim; }
/** Returns the dimension of the curl for vector-valued finite elements,
which is also the width of the DenseMatrix argument in
CalcCurlShape(const IntegrationPoint &ip, DenseMatrix &curl_shape). */
int GetCurlDim() const { return cdim; }
/** Returns the dimension, in physical space, of the curl for vector-valued
finite elements, which is also the width of the DenseMatrix argument in
CalcPhysCurlShape(ElementTransformation &Trans, DenseMatrix &curl_shape).
*/
int GetPhysCurlDim(int /* space_dim */) const { return cdim; }
/// Returns the Geometry::Type of the reference element.
Geometry::Type GetGeomType() const { return geom_type; }
@@ -990,6 +1016,8 @@ protected:
public:
VectorFiniteElement(int D, Geometry::Type G, int Do, int O, int M,
int F = FunctionSpace::Pk);
int GetPhysRangeDim(int space_dim) const { return space_dim; }
};
/// @brief Class for computing 1D special polynomials and their associated basis
+973
View File
@@ -0,0 +1,973 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
// H1 Finite Element classes
#include "fe_h1_bubble.hpp"
namespace mfem
{
using namespace std;
H1Bubble_TriangleElement::H1Bubble_TriangleElement(int p, int q, int btype)
: NodalFiniteElement(2, Geometry::TRIANGLE, 3*p + ((q+1)*(q+2))/2,
max(p, 3 + q), FunctionSpace::Pk),
base_order(p), bubble_order(q)
{
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
const real_t *cp2 = poly1d.ClosedPoints(
q + 3, VerifyNodal(VerifyClosed(btype)));
const int n1d = max(p + 1, q + 1);
const int npq = ((p+1)*(p+2))/2 + ((q+1)*(q+2))/2;
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(n1d);
shape_y.SetSize(n1d);
shape_l.SetSize(n1d);
dshape_x.SetSize(n1d);
dshape_y.SetSize(n1d);
dshape_l.SetSize(n1d);
u.SetSize(npq);
du.SetSize(npq, dim);
#endif
// vertices
Nodes.IntPoint(0).Set2(cp[0], cp[0]);
Nodes.IntPoint(1).Set2(cp[p], cp[0]);
Nodes.IntPoint(2).Set2(cp[0], cp[p]);
// edges
int o = 3;
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[i], cp[0]);
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[p-i], cp[i]);
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[0], cp[p-i]);
}
// Interior P_{q+3} nodes
for (int j = 1; j < q + 3; j++)
{
for (int i = 1; i + j < q + 3; i++)
{
const real_t w = cp2[i] + cp2[j] + cp2[q+3-i-j];
Nodes.IntPoint(o++).Set2(cp2[i]/w, cp2[j]/w);
}
}
#ifdef MFEM_THREAD_SAFE
Vector shape_x(n1d), shape_y(n1d), shape_l(n1d);
#endif
DenseMatrix Tt(dof, npq);
for (int k = 0; k < dof; ++k)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
poly1d.CalcBasis(p, 1. - ip.x - ip.y, shape_l);
o = 0;
for (int j = 0; j <= p; j++)
{
for (int i = 0; i + j <= p; i++)
{
Tt(k, o++) = shape_x[i]*shape_y[j]*shape_l[p-i-j];
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
poly1d.CalcBasis(q, 1. - ip.x - ip.y, shape_l);
const real_t b_T = ip.x * ip.y * (1 - ip.x - ip.y);
for (int j = 0; j <= q; j++)
{
for (int i = 0; i + j <= q; i++)
{
Tt(k, o++) = b_T*shape_x[i]*shape_y[j]*shape_l[q-i-j];
}
}
}
// Compute left inverse of T (given Tt = T^T).
DenseMatrix TtT(dof, dof);
MultAAt(Tt, TtT);
DenseMatrixInverse TtT_inv(TtT);
T_pinv.SetSize(dof, dof);
TtT_inv.Mult(Tt, T_pinv);
}
void H1Bubble_TriangleElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = ((p+1)*(p+2))/2 + ((q+1)*(q+2))/2;
Vector shape_x(n1d), shape_y(n1d), shape_l(n1d), u(npq);
#endif
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
poly1d.CalcBasis(p, 1. - ip.x - ip.y, shape_l);
int o = 0;
for (int j = 0; j <= p; j++)
{
for (int i = 0; i + j <= p; i++)
{
u(o++) = shape_x[i]*shape_y[j]*shape_l[p-i-j];
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
poly1d.CalcBasis(q, 1. - ip.x - ip.y, shape_l);
const real_t b_T = ip.x * ip.y * (1 - ip.x - ip.y);
for (int j = 0; j <= q; j++)
{
for (int i = 0; i + j <= q; i++)
{
u(o++) = b_T*shape_x[i]*shape_y[j]*shape_l[q-i-j];
}
}
T_pinv.Mult(u, shape);
}
void H1Bubble_TriangleElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = ((p+1)*(p+2))/2 + ((q+1)*(q+2))/2;
Vector shape_x(n1d), shape_y(n1d), shape_l(n1d);
Vector dshape_x(n1d), dshape_y(n1d), dshape_l(n1d);
DenseMatrix du(npq, dim);
#endif
const real_t lambda = 1.0 - ip.x - ip.y;
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(p, ip.y, shape_y, dshape_y);
poly1d.CalcBasis(p, lambda, shape_l, dshape_l);
int o = 0;
for (int j = 0; j <= p; j++)
{
for (int i = 0; i + j <= p; i++)
{
int k = p - i - j;
du(o,0) = (dshape_x[i]*shape_l[k] - shape_x[i]*dshape_l[k])*shape_y[j];
du(o,1) = (dshape_y[j]* shape_l[k] - shape_y[j]*dshape_l[k])*shape_x[i];
o++;
}
}
poly1d.CalcBasis(q, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(q, ip.y, shape_y, dshape_y);
poly1d.CalcBasis(q, lambda, shape_l, dshape_l);
const real_t b_T = ip.x * ip.y * lambda;
const real_t dxb_T = ip.y * (lambda - ip.x);
const real_t dyb_T = ip.x * (lambda - ip.y);
for (int j = 0; j <= q; j++)
{
for (int i = 0; i + j <= q; i++)
{
int k = q - i - j;
du(o,0) = shape_y[j]*(dxb_T*shape_x[i]*shape_l[k]
+ b_T*dshape_x[i]*shape_l[k]
- b_T*shape_x[i]*dshape_l[k]);
du(o,1) = shape_x[i]*(dyb_T*shape_y[j]*shape_l[k]
+ b_T*dshape_y[j]*shape_l[k]
- b_T*shape_y[j]*dshape_l[k]);
o++;
}
}
Mult(T_pinv, du, dshape);
}
H1Bubble_QuadrilateralElement::H1Bubble_QuadrilateralElement(
int p, int q, int btype)
: NodalFiniteElement(2, Geometry::SQUARE, 4*p + (q+1)*(q+1),
max(p, 2 + q), FunctionSpace::Qk),
base_order(p), bubble_order(q)
{
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
const real_t *cp2 = poly1d.ClosedPoints(
q + 2, VerifyNodal(VerifyClosed(btype)));
const int n1d = max(p + 1, q + 1);
const int npq = (p+1)*(p+1) + (q+1)*(q+1);
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(n1d);
shape_y.SetSize(n1d);
dshape_x.SetSize(n1d);
dshape_y.SetSize(n1d);
u.SetSize(npq);
du.SetSize(npq, dim);
#endif
// vertices
Nodes.IntPoint(0).Set2(cp[0], cp[0]);
Nodes.IntPoint(1).Set2(cp[p], cp[0]);
Nodes.IntPoint(2).Set2(cp[p], cp[p]);
Nodes.IntPoint(3).Set2(cp[0], cp[p]);
// edges
int o = 4;
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[i], cp[0]);
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[p], cp[i]);
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[p-i], cp[p]);
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[0], cp[p-i]);
}
// interior P_{q+2} nodes
for (int j = 1; j < q+2; j++)
{
for (int i = 1; i < q+2; i++)
{
Nodes.IntPoint(o++).Set2(cp2[i], cp2[j]);
}
}
#ifdef MFEM_THREAD_SAFE
Vector shape_x(n1d), shape_y(n1d);
#endif
DenseMatrix Tt(dof, npq);
for (int k = 0; k < dof; ++k)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
o = 0;
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++)
{
Tt(k, o++) = shape_x[i]*shape_y[j];
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y);
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= q; i++)
{
Tt(k, o++) = b_T*shape_x[i]*shape_y[j];
}
}
}
// Compute left inverse of T (given Tt = T^T).
DenseMatrix TtT(dof, dof);
MultAAt(Tt, TtT);
DenseMatrixInverse TtT_inv(TtT);
T_pinv.SetSize(dof, dof);
TtT_inv.Mult(Tt, T_pinv);
}
void H1Bubble_QuadrilateralElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = (p+1)*(p+1) + (q+1)*(q+1);
Vector shape_x(n1d), shape_y(n1d), u(npq);
#endif
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
int o = 0;
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++)
{
u(o++) = shape_x[i]*shape_y[j];
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y);
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= q; i++)
{
u(o++) = b_T*shape_x[i]*shape_y[j];
}
}
T_pinv.Mult(u, shape);
}
void H1Bubble_QuadrilateralElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = (p+1)*(p+1) + (q+1)*(q+1);
Vector shape_x(n1d), shape_y(n1d), dshape_x(n1d), dshape_y(n1d);
DenseMatrix du(npq, dim);
#endif
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(p, ip.y, shape_y, dshape_y);
int o = 0;
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++)
{
du(o,0) = dshape_x[i]*shape_y[j];
du(o,1) = shape_x[i]*dshape_y[j];
o += 1;
}
}
poly1d.CalcBasis(q, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(q, ip.y, shape_y, dshape_y);
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y);
const real_t dxb_T = (1.0 - 2*ip.x)*ip.y*(1.0 - ip.y);
const real_t dyb_T = ip.x*(1.0 - ip.x)*(1.0 - 2*ip.y);
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= q; i++)
{
du(o,0) = (dxb_T*shape_x[i] + b_T*dshape_x[i])*shape_y[j];
du(o,1) = (dyb_T*shape_y[j] + b_T*dshape_y[j])*shape_x[i];
o += 1;
}
}
Mult(T_pinv, du, dshape);
}
H1Bubble_TetrahedronElement::H1Bubble_TetrahedronElement(
int p, int q, int btype)
: NodalFiniteElement(3, Geometry::TETRAHEDRON,
2*(p*p + 1) + ((q+1)*(q+2)*(q+3))/6,
max(p, 4 + q), FunctionSpace::Pk),
base_order(p), bubble_order(q)
{
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
const real_t *cp2 = poly1d.ClosedPoints(
q + 4, VerifyNodal(VerifyClosed(btype)));
const int n1d = max(p+1, q+1);
const int npq = ((p+1)*(p+2)*(p+3))/6 + ((q+1)*(q+2)*(q+3))/6;
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(n1d);
shape_y.SetSize(n1d);
shape_z.SetSize(n1d);
shape_l.SetSize(n1d);
dshape_x.SetSize(n1d);
dshape_y.SetSize(n1d);
dshape_z.SetSize(n1d);
dshape_l.SetSize(n1d);
u.SetSize(npq);
du.SetSize(npq, dim);
#else
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), shape_l(n1d);
#endif
// vertices
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
Nodes.IntPoint(2).Set3(cp[0], cp[p], cp[0]);
Nodes.IntPoint(3).Set3(cp[0], cp[0], cp[p]);
// edges (see Tetrahedron::edges in mesh/tetrahedron.cpp)
int o = 4;
for (int i = 1; i < p; i++) // (0,1)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]);
}
for (int i = 1; i < p; i++) // (0,2)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (0,3)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (1,2)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (1,3)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (2,3)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[i]);
}
// faces (see Mesh::GenerateFaces in mesh/mesh.cpp)
for (int j = 1; j < p; j++)
{
for (int i = 1; i + j < p; i++) // (1,2,3)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[p-i-j]/w, cp[i]/w, cp[j]/w);
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i + j < p; i++) // (0,3,2)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[0], cp[j]/w, cp[i]/w);
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i + j < p; i++) // (0,1,3)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[0], cp[j]/w);
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i + j < p; i++) // (0,2,1)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[j]/w, cp[i]/w, cp[0]);
}
}
// Interior P_{q+4} nodes
for (int k = 1; k < q + 4; k++)
{
for (int j = 1; j + k < q + 4; j++)
{
for (int i = 1; i + j + k < q + 4; i++)
{
real_t w = cp2[i] + cp2[j] + cp2[k] + cp2[q+4-i-j-k];
Nodes.IntPoint(o++).Set3(cp2[i]/w, cp2[j]/w, cp2[k]/w);
}
}
}
DenseMatrix Tt(dof, npq);
for (int m = 0; m < dof; ++m)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
poly1d.CalcBasis(p, ip.z, shape_z);
poly1d.CalcBasis(p, 1. - ip.x - ip.y - ip.z, shape_l);
o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j + k <= p; j++)
{
for (int i = 0; i + j + k <= p; i++)
{
Tt(m, o++) = shape_x[i]*shape_y[j]*shape_z[k]*shape_l[p-i-j-k];
}
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
poly1d.CalcBasis(q, ip.z, shape_z);
poly1d.CalcBasis(q, 1. - ip.x - ip.y - ip.z, shape_l);
const real_t b_T = ip.x * ip.y * ip.z * (1 - ip.x - ip.y - ip.z);
for (int k = 0; k <= q; k++)
{
for (int j = 0; j + k <= q; j++)
{
for (int i = 0; i + j + k <= q; i++)
{
Tt(m, o++) = b_T*shape_x[i]*shape_y[j]*shape_z[k]*shape_l[q-i-j-k];
}
}
}
}
// Compute left inverse of T (given Tt = T^T).
DenseMatrix TtT(dof, dof);
MultAAt(Tt, TtT);
DenseMatrixInverse TtT_inv(TtT);
T_pinv.SetSize(dof, dof);
TtT_inv.Mult(Tt, T_pinv);
}
void H1Bubble_TetrahedronElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = ((p+1)*(p+2)*(p+3))/6 + ((q+1)*(q+2)*(q+3))/6;
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), shape_l(n1d), u(npq);
#endif
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
poly1d.CalcBasis(p, ip.z, shape_z);
poly1d.CalcBasis(p, 1. - ip.x - ip.y - ip.z, shape_l);
int o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j + k <= p; j++)
{
for (int i = 0; i + j + k <= p; i++)
{
u[o++] = shape_x[i]*shape_y[j]*shape_z[k]*shape_l[p-i-j-k];
}
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
poly1d.CalcBasis(q, ip.z, shape_z);
poly1d.CalcBasis(q, 1. - ip.x - ip.y - ip.z, shape_l);
const real_t b_T = ip.x * ip.y * ip.z * (1 - ip.x - ip.y - ip.z);
for (int k = 0; k <= q; k++)
{
for (int j = 0; j + k <= q; j++)
{
for (int i = 0; i + j + k <= q; i++)
{
u(o++) = b_T*shape_x[i]*shape_y[j]*shape_z[k]*shape_l[q-i-j-k];
}
}
}
T_pinv.Mult(u, shape);
}
void H1Bubble_TetrahedronElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p+1, q+1);
const int npq = ((p+1)*(p+2)*(p+3))/6 + ((q+1)*(q+2)*(q+3))/6;
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), shape_l(n1d);
Vector dshape_x(n1d), dshape_y(n1d), dshape_z(n1d), dshape_l(n1d);
DenseMatrix du(npq, dim);
#endif
const real_t lambda = 1.0 - ip.x - ip.y - ip.z;
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(p, ip.y, shape_y, dshape_y);
poly1d.CalcBasis(p, ip.z, shape_z, dshape_z);
poly1d.CalcBasis(p, lambda, shape_l, dshape_l);
int o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j + k <= p; j++)
{
for (int i = 0; i + j + k <= p; i++)
{
int l = p - i - j - k;
du(o,0) = (dshape_x[i]*shape_l[l] - shape_x[i]*dshape_l[l])
*shape_y[j]*shape_z[k];
du(o,1) = (dshape_y[j]*shape_l[l] - shape_y[j]*dshape_l[l])
*shape_x[i]*shape_z[k];
du(o,2) = (dshape_z[k]*shape_l[l] - shape_z[k]*dshape_l[l])
*shape_x[i]*shape_y[j];
o++;
}
}
}
poly1d.CalcBasis(q, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(q, ip.y, shape_y, dshape_y);
poly1d.CalcBasis(q, ip.z, shape_z, dshape_z);
poly1d.CalcBasis(q, lambda, shape_l, dshape_l);
const real_t b_T = ip.x * ip.y * ip.z * (1 - ip.x - ip.y - ip.z);
const real_t dxb_T = ip.y * ip.z * (lambda - ip.x);
const real_t dyb_T = ip.x * ip.z * (lambda - ip.y);
const real_t dzb_T = ip.x * ip.y * (lambda - ip.z);
for (int k = 0; k <= q; k++)
{
for (int j = 0; j + k <= q; j++)
{
for (int i = 0; i + j + k <= q; i++)
{
int l = q - i - j - k;
du(o,0) = shape_y[j]*shape_z[k]*(dxb_T*shape_x[i]*shape_l[l]
+ b_T*dshape_x[i]*shape_l[l]
- b_T*shape_x[i]*dshape_l[l]);
du(o,1) = shape_x[i]*shape_z[k]*(dyb_T*shape_y[j]*shape_l[l]
+ b_T*dshape_y[j]*shape_l[l]
- b_T*shape_y[j]*dshape_l[l]);
du(o,2) = shape_x[i]*shape_y[j]*(dzb_T*shape_z[k]*shape_l[l]
+ b_T*dshape_z[k]*shape_l[l]
- b_T*shape_z[k]*dshape_l[l]);
o++;
}
}
}
Mult(T_pinv, du, dshape);
}
H1Bubble_HexahedronElement::H1Bubble_HexahedronElement(
int p, int q, int btype)
: NodalFiniteElement(3, Geometry::CUBE, (2 + 6*p*p) + (q+1)*(q+1)*(q+1),
max(p, 2 + q), FunctionSpace::Qk),
base_order(p), bubble_order(q)
{
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
const real_t *cp2 = poly1d.ClosedPoints(
q + 2, VerifyNodal(VerifyClosed(btype)));
const int n1d = max(p + 1, q + 1);
const int npq = (p+1)*(p+1)*(p+1) + (q+1)*(q+1)*(q+1);
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(n1d);
shape_y.SetSize(n1d);
shape_z.SetSize(n1d);
dshape_x.SetSize(n1d);
dshape_y.SetSize(n1d);
dshape_z.SetSize(n1d);
u.SetSize(npq);
du.SetSize(npq, dim);
#endif
// vertices
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
Nodes.IntPoint(2).Set3(cp[p], cp[p], cp[0]);
Nodes.IntPoint(3).Set3(cp[0], cp[p], cp[0]);
Nodes.IntPoint(4).Set3(cp[0], cp[0], cp[p]);
Nodes.IntPoint(5).Set3(cp[p], cp[0], cp[p]);
Nodes.IntPoint(6).Set3(cp[p], cp[p], cp[p]);
Nodes.IntPoint(7).Set3(cp[0], cp[p], cp[p]);
int o = 8;
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]); // (0,1)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[0]); // (1,2)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[p], cp[0]); // (3,2)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]); // (0,3)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[p]); // (4,5)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[p]); // (5,6)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[p], cp[p]); // (7,6)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[p]); // (4,7)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]); // (0,4)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[0], cp[i]); // (1,5)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[p], cp[i]); // (2,6)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[p], cp[i]); // (3,7)
}
// faces
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[p-j], cp[0]); // (3,2,1,0)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[j]); // (0,1,5,4)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[j]); // (1,2,6,5)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[p], cp[j]); // (2,3,7,6)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[j]); // (3,0,4,7)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[j], cp[p]); // (4,5,6,7)
}
}
// interior P_{q+2} nodes
for (int k = 1; k < q+2; k++)
{
for (int j = 1; j < q+2; j++)
{
for (int i = 1; i < q+2; i++)
{
Nodes.IntPoint(o++).Set3(cp2[i], cp2[j], cp2[k]);
}
}
}
#ifdef MFEM_THREAD_SAFE
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d);
#endif
DenseMatrix Tt(dof, npq);
for (int m = 0; m < dof; ++m)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
poly1d.CalcBasis(p, ip.z, shape_z);
o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++)
{
Tt(m, o++) = shape_x[i]*shape_y[j]*shape_z[k];
}
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
poly1d.CalcBasis(q, ip.z, shape_z);
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y)*ip.z*(1.0 - ip.z);
for (int k = 0; k <= q; k++)
{
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= q; i++)
{
Tt(m, o++) = b_T*shape_x[i]*shape_y[j]*shape_z[k];
}
}
}
}
// Compute left inverse of T (given Tt = T^T).
DenseMatrix TtT(dof, dof);
MultAAt(Tt, TtT);
DenseMatrixInverse TtT_inv(TtT);
T_pinv.SetSize(dof, dof);
TtT_inv.Mult(Tt, T_pinv);
}
void H1Bubble_HexahedronElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = (p+1)*(p+1)*(p+1) + (q+1)*(q+1)*(q+1);
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), u(npq);
#endif
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
poly1d.CalcBasis(p, ip.z, shape_z);
int o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++)
{
u(o++) = shape_x[i]*shape_y[j]*shape_z[k];
}
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
poly1d.CalcBasis(q, ip.z, shape_z);
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y)*ip.z*(1.0 - ip.z);
for (int k = 0; k <= q; k++)
{
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= q; i++)
{
u(o++) = b_T*shape_x[i]*shape_y[j]*shape_z[k];
}
}
}
T_pinv.Mult(u, shape);
}
void H1Bubble_HexahedronElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = (p+1)*(p+1)*(p+1) + (q+1)*(q+1)*(q+1);
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), dshape_x(n1d),
dshape_y(n1d), dshape_z(n1d);
DenseMatrix du(npq, dim);
#endif
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(p, ip.y, shape_y, dshape_y);
poly1d.CalcBasis(p, ip.z, shape_z, dshape_z);
int o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++)
{
du(o,0) = dshape_x[i]*shape_y[j]*shape_z[k];
du(o,1) = shape_x[i]*dshape_y[j]*shape_z[k];
du(o,2) = shape_x[i]*shape_y[j]*dshape_z[k];
o += 1;
}
}
}
poly1d.CalcBasis(q, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(q, ip.y, shape_y, dshape_y);
poly1d.CalcBasis(q, ip.z, shape_z, dshape_z);
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y)*ip.z*(1.0 - ip.z);
const real_t dxb_T = (1.0 - 2*ip.x)*ip.y*(1.0 - ip.y)*ip.z*(1.0 - ip.z);
const real_t dyb_T = ip.x*(1.0 - ip.x)*(1.0 - 2*ip.y)*ip.z*(1.0 - ip.z);
const real_t dzb_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y)*(1.0 - 2*ip.z);
for (int k = 0; k <= q; k++)
{
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= q; i++)
{
du(o,0) = (dxb_T*shape_x[i] + b_T*dshape_x[i])*shape_y[j]*shape_z[k];
du(o,1) = (dyb_T*shape_y[j] + b_T*dshape_y[j])*shape_x[i]*shape_z[k];
du(o,2) = (dzb_T*shape_z[k] + b_T*dshape_z[k])*shape_x[i]*shape_y[j];
o += 1;
}
}
}
Mult(T_pinv, du, dshape);
}
}
+109
View File
@@ -0,0 +1,109 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_FE_H1_BUBBLE
#define MFEM_FE_H1_BUBBLE
#include "fe_base.hpp"
namespace mfem
{
/// Arbitrary order H1 plus bubble elements in 2D on a triangle
class H1Bubble_TriangleElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_l, dshape_x, dshape_y, dshape_l, u;
mutable DenseMatrix du;
#endif
int base_order;
int bubble_order;
DenseMatrix T_pinv;
public:
/// @brief Construct the triangular bubble element with degree-p polynomials,
/// enriched with cubic bubble times degree q polynomial.
H1Bubble_TriangleElement(int p, int q, int btype = BasisType::GaussLobatto);
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
};
/// Arbitrary order H1 plus bubble elements in 2D on a quadrilateral
class H1Bubble_QuadrilateralElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, dshape_x, dshape_y, u;
mutable DenseMatrix du;
#endif
int base_order;
int bubble_order;
DenseMatrix T_pinv;
public:
/// @brief Construct the quadrilateral bubble element with degree-p
/// polynomials, enriched with biquadratic bubble times degree q polynomial.
H1Bubble_QuadrilateralElement(
int p, int q, int btype = BasisType::GaussLobatto);
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
};
/// Arbitrary order H1 plus bubble elements in 3D on a tetrahedron
class H1Bubble_TetrahedronElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_z, shape_l;
mutable Vector dshape_x, dshape_y, dshape_z, dshape_l, u;
mutable DenseMatrix du;
#endif
int base_order;
int bubble_order;
DenseMatrix T_pinv;
public:
/// @brief Construct the tetrahedral bubble element with degree-p
/// polynomials, enriched with quartic bubble times degree q polynomial.
H1Bubble_TetrahedronElement(int p, int q, int btype = BasisType::GaussLobatto);
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
};
/// Arbitrary order H1 plus bubble elements in 3D on a hexahedron
class H1Bubble_HexahedronElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_z;
mutable Vector dshape_x, dshape_y, dshape_z, u;
mutable DenseMatrix du;
#endif
int base_order;
int bubble_order;
DenseMatrix T_pinv;
public:
/// @brief Construct the hexahedral bubble element with degree-p polynomials,
/// enriched with triquadratic bubble times degree q polynomial.
H1Bubble_HexahedronElement(int p, int q, int btype = BasisType::GaussLobatto);
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
};
} // namespace mfem
#endif
+4 -4
View File
@@ -2531,7 +2531,7 @@ void ND_FuentesPyramidElement::calcCurlBasis(const int p,
ND_R1D_PointElement::ND_R1D_PointElement(int p)
: VectorFiniteElement(1, Geometry::POINT, 2, p,
H_CURL, FunctionSpace::Pk)
H_CURL_R1D, FunctionSpace::Pk)
{
// VectorFiniteElement::SetDerivMembers doesn't support 0D H_CURL elements
// so we mimic a 1D element and then correct the dimension here.
@@ -2562,7 +2562,7 @@ ND_R1D_SegmentElement::ND_R1D_SegmentElement(const int p,
const int cb_type,
const int ob_type)
: VectorFiniteElement(1, Geometry::SEGMENT, 3 * p + 2, p,
H_CURL, FunctionSpace::Pk),
H_CURL_R1D, FunctionSpace::Pk),
dof2tk(dof),
cbasis1d(poly1d.GetBasis(p, VerifyClosed(cb_type))),
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(ob_type)))
@@ -2839,7 +2839,7 @@ ND_R2D_SegmentElement::ND_R2D_SegmentElement(const int p,
const int cb_type,
const int ob_type)
: VectorFiniteElement(1, Geometry::SEGMENT, 2 * p + 1, p,
H_CURL, FunctionSpace::Pk),
H_CURL_R2D, FunctionSpace::Pk),
dof2tk(dof),
cbasis1d(poly1d.GetBasis(p, VerifyClosed(cb_type))),
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(ob_type)))
@@ -3023,7 +3023,7 @@ void ND_R2D_SegmentElement::Project(VectorCoefficient &vc,
ND_R2D_FiniteElement::ND_R2D_FiniteElement(int p, Geometry::Type G, int Do,
const real_t *tk_fe)
: VectorFiniteElement(2, G, Do, p,
H_CURL, FunctionSpace::Pk),
H_CURL_R2D, FunctionSpace::Pk),
tk(tk_fe),
dof_map(dof),
dof2tk(dof)
+6
View File
@@ -663,6 +663,9 @@ public:
const int cb_type = BasisType::GaussLobatto,
const int ob_type = BasisType::GaussLegendre);
int GetPhysRangeDim(int space_dim) const { return 2; }
int GetPhysCurlDim(int space_dim) const { return 1; }
void CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const override;
@@ -705,6 +708,9 @@ private:
DenseMatrix &I) const;
public:
int GetPhysRangeDim(int space_dim) const { return 3; }
int GetPhysCurlDim(int space_dim) const { return 3; }
using FiniteElement::CalcVShape;
using FiniteElement::CalcPhysCurlShape;
+3 -3
View File
@@ -2006,7 +2006,7 @@ RT_R1D_SegmentElement::RT_R1D_SegmentElement(const int p,
const int cb_type,
const int ob_type)
: VectorFiniteElement(1, Geometry::SEGMENT, 3 * p + 4, p + 1,
H_DIV, FunctionSpace::Pk),
H_DIV_R1D, FunctionSpace::Pk),
dof2nk(dof),
cbasis1d(poly1d.GetBasis(p + 1, VerifyClosed(cb_type))),
obasis1d(poly1d.GetBasis(p, VerifyOpen(ob_type)))
@@ -2281,7 +2281,7 @@ const real_t RT_R2D_SegmentElement::nk[2] = { 0.,1.};
RT_R2D_SegmentElement::RT_R2D_SegmentElement(const int p,
const int ob_type)
: VectorFiniteElement(1, Geometry::SEGMENT, p + 1, p + 1,
H_DIV, FunctionSpace::Pk),
H_DIV_R2D, FunctionSpace::Pk),
dof2nk(dof),
obasis1d(poly1d.GetBasis(p, VerifyOpen(ob_type)))
{
@@ -2392,7 +2392,7 @@ void RT_R2D_SegmentElement::LocalInterpolation(const VectorFiniteElement &cfe,
RT_R2D_FiniteElement::RT_R2D_FiniteElement(int p, Geometry::Type G, int Do,
const real_t *nk_fe)
: VectorFiniteElement(2, G, Do, p + 1,
H_DIV, FunctionSpace::Pk),
H_DIV_R2D, FunctionSpace::Pk),
nk(nk_fe),
dof_map(dof),
dof2nk(dof)
+6
View File
@@ -510,6 +510,9 @@ public:
RT_R2D_SegmentElement(const int p,
const int ob_type = BasisType::GaussLegendre);
int GetPhysRangeDim(int space_dim) const { return 2; }
int GetPhysCurlDim(int space_dim) const { return 0; }
void CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const override;
@@ -547,6 +550,9 @@ private:
DenseMatrix &I) const;
public:
int GetPhysRangeDim(int space_dim) const { return 3; }
int GetPhysCurlDim(int space_dim) const { return 0; }
using FiniteElement::CalcVShape;
void CalcVShape(ElementTransformation &Trans,
+177
View File
@@ -243,11 +243,21 @@ FiniteElementCollection *FiniteElementCollection::New(const char *name)
{
fec = new H1Ser_FECollection(atoi(name + 10), atoi(name + 6));
}
else if (!strncmp(name, "H1Bubble_", 9))
{
fec = new H1Bubble_FECollection(atoi(name + 13), atoi(name + 16),
atoi(name + 9));
}
else if (!strncmp(name, "H1@", 3))
{
fec = new H1_FECollection(atoi(name + 9), atoi(name + 5),
BasisType::GetType(name[3]));
}
else if (!strncmp(name, "H1Bubble@", 9))
{
fec = new H1Bubble_FECollection(atoi(name + 15), atoi(name + 18),
atoi(name + 11), BasisType::GetType(name[9]));
}
else if (!strncmp(name, "L2_T", 4))
fec = new L2_FECollection(atoi(name + 10), atoi(name + 6),
atoi(name + 4));
@@ -2122,6 +2132,173 @@ H1_FECollection::~H1_FECollection()
}
}
static int GetBubbleSpaceOrder(int p, int q, int dim)
{
switch (dim)
{
case 0: return 0;
case 1: return std::max(p, q + 2);
case 2: return std::max(p, q + 3);
case 3: return std::max(p, q + 4);
}
MFEM_ABORT("Unsupported dimension.");
}
H1Bubble_FECollection::H1Bubble_FECollection(const int p, const int q,
const int dim, const int btype)
: FiniteElementCollection(GetBubbleSpaceOrder(p, q, dim)),
dim(dim),
b_type(BasisType::Check(btype)),
h1_order(p),
bubble_order(q)
{
MFEM_VERIFY(p >= 1, "H1Bubble_FECollection requires order >= 1.");
MFEM_VERIFY(dim >= 0 && dim <= 3, "Unsupported dimension.");
switch (btype)
{
case BasisType::GaussLobatto:
{
snprintf(fec_name, 32, "H1Bubble_%dD_P%d_P%d", dim, p, q);
break;
}
default:
{
const int pt_type = BasisType::GetQuadrature1D(btype);
MFEM_VERIFY(Quadrature1D::CheckClosed(pt_type) != Quadrature1D::Invalid,
"unsupported BasisType: " << BasisType::Name(btype));
snprintf(fec_name, 32, "H1Bubble@%c_%dD_P%d_P%d",
(int)BasisType::GetChar(btype), dim, p, q);
}
}
dofs[Geometry::POINT] = 1;
elements[Geometry::POINT] = make_unique<PointFiniteElement>();
if (dim >= 1)
{
dofs[Geometry::SEGMENT] = p - 1;
elements[Geometry::SEGMENT] = make_unique<H1_SegmentElement>(p, btype);
}
if (dim == 2)
{
dofs[Geometry::TRIANGLE] = ((q+1)*(q+2))/2;
dofs[Geometry::SQUARE] = (q+1)*(q+1);
elements[Geometry::TRIANGLE] =
make_unique<H1Bubble_TriangleElement>(p, q, btype);
elements[Geometry::SQUARE] =
make_unique<H1Bubble_QuadrilateralElement>(p, q, btype);
}
if (dim == 3)
{
dofs[Geometry::TRIANGLE] = ((p-1)*(p-2))/2;
dofs[Geometry::SQUARE] = (p-1)*(p-1);
dofs[Geometry::TETRAHEDRON] = ((q+1)*(q+2)*(q+3))/6;
dofs[Geometry::CUBE] = (q+1)*(q+1)*(q+1);
elements[Geometry::TRIANGLE] = make_unique<H1_TriangleElement>(p, btype);
elements[Geometry::SQUARE] = make_unique<H1_QuadrilateralElement>(p, btype);
elements[Geometry::TETRAHEDRON] =
make_unique<H1Bubble_TetrahedronElement>(p, q, btype);
elements[Geometry::CUBE] =
make_unique<H1Bubble_HexahedronElement>(p, q, btype);
}
// DOF orderings. Need only for lower-dimensional entities.
// Segment DOF orderings in 2D.
if (dim >= 2)
{
seg_dof_ord[0].resize(p - 1);
seg_dof_ord[1].resize(p - 1);
for (int i = 0; i < p - 1; i++)
{
seg_dof_ord[0][i] = i;
seg_dof_ord[1][i] = p - 2 - i;
}
}
// Face (triangle or quadrilateral) DOF orderings in 3D.
if (dim == 3)
{
const int n_tri_dof = dofs[Geometry::TRIANGLE];
for (int i = 0; i < 6; i++)
{
tri_dof_ord[i].resize(n_tri_dof);
}
// see Mesh::GetTriOrientation in mesh/mesh.cpp
const int pm1 = p - 1;
const int pm2 = p - 2;
for (int j = 0; j < pm2; j++)
{
for (int i = 0; i + j < pm2; i++)
{
int o = n_tri_dof - ((pm1 - j)*(pm2 - j))/2 + i;
int k = (p - 3) - j - i;
tri_dof_ord[0][o] = o; // (0,1,2)
tri_dof_ord[1][o] = n_tri_dof - ((pm1-j)*(pm2-j))/2 + k; // (1,0,2)
tri_dof_ord[2][o] = n_tri_dof - ((pm1-i)*(pm2-i))/2 + k; // (2,0,1)
tri_dof_ord[3][o] = n_tri_dof - ((pm1-k)*(pm2-k))/2 + i; // (2,1,0)
tri_dof_ord[4][o] = n_tri_dof - ((pm1-k)*(pm2-k))/2 + j; // (1,2,0)
tri_dof_ord[5][o] = n_tri_dof - ((pm1-i)*(pm2-i))/2 + j; // (0,2,1)
}
}
const int n_quad_dof = dofs[Geometry::SQUARE];
for (int i = 0; i < 8; i++)
{
quad_dof_ord[i].resize(n_quad_dof);
}
for (int j = 0; j < pm1; j++)
{
for (int i = 0; i < pm1; i++)
{
int o = i + j*pm1;
quad_dof_ord[0][o] = i + j*pm1; // (0,1,2,3)
quad_dof_ord[1][o] = j + i*pm1; // (0,3,2,1)
quad_dof_ord[2][o] = j + (pm2 - i)*pm1; // (1,2,3,0)
quad_dof_ord[3][o] = (pm2 - i) + j*pm1; // (1,0,3,2)
quad_dof_ord[4][o] = (pm2 - i) + (pm2 - j)*pm1; // (2,3,0,1)
quad_dof_ord[5][o] = (pm2 - j) + (pm2 - i)*pm1; // (2,1,0,3)
quad_dof_ord[6][o] = (pm2 - j) + i*pm1; // (3,0,1,2)
quad_dof_ord[7][o] = i + (pm2 - j)*pm1; // (3,2,1,0)
}
}
}
}
const FiniteElement *
H1Bubble_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
{
return elements[GeomType].get();
}
const int *H1Bubble_FECollection::DofOrderForOrientation(
Geometry::Type GeomType, int Or) const
{
if (GeomType == Geometry::SEGMENT)
{
return (Or > 0) ? seg_dof_ord[0].data() : seg_dof_ord[1].data();
}
else if (GeomType == Geometry::TRIANGLE)
{
return tri_dof_ord[Or%6].data();
}
else if (GeomType == Geometry::SQUARE)
{
return quad_dof_ord[Or%8].data();
}
return nullptr;
}
FiniteElementCollection *H1Bubble_FECollection::GetTraceCollection() const
{
return (dim < 0) ? NULL : new H1_Trace_FECollection(h1_order, dim, b_type);
}
H1_Trace_FECollection::H1_Trace_FECollection(const int p, const int dim,
const int btype)
+55
View File
@@ -111,6 +111,8 @@ public:
| :------: | :---: | :---: | :-------: | :-----: | :---: |
| H1_[DIM]_[ORDER] | H1 | * | 1 | VALUE | H1 nodal elements |
| H1@[BTYPE]_[DIM]_[ORDER] | H1 | * | * | VALUE | H1 nodal elements |
| H1Bubble_[DIM]_[ORDER]_[BUBBLE_ORDER] | H1 | * | 1 | VALUE | H1 nodal elements enriched with bubble functions |
| H1Bubble@[BTYPE]_[DIM]_[ORDER]_[BUBBLE_ORDER] | H1 | * | 1 | VALUE | H1 nodal elements enriched with bubble functions |
| H1Pos_[DIM]_[ORDER] | H1 | * | 2 | VALUE | H1 nodal elements |
| H1Pos_Trace_[DIM]_[ORDER] | H^{1/2} | * | 2 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
| H1_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
@@ -317,6 +319,59 @@ public:
virtual ~H1_FECollection();
};
/// @brief Arbitrary order $H^1$-conforming (continuous) finite elements
/// enriched with bubble functions.
///
/// The bubble space consists of the standard $P_p$ or $Q_p$ space, enriched
/// with bubble functions, which are degree-$q$ polynomials times $b$, where $b$
/// is the lowest-order bubble function.
///
/// The traces are the same as the standard $H^1$ traces.
class H1Bubble_FECollection : public FiniteElementCollection
{
protected:
int dim;
int b_type;
int h1_order;
int bubble_order;
char fec_name[32];
std::array<int, Geometry::NumGeom> dofs{}; // zero initialize
std::array<std::unique_ptr<FiniteElement>, Geometry::NumGeom> elements;
std::array<std::vector<int>, 2> seg_dof_ord;
std::array<std::vector<int>, 6> tri_dof_ord;
std::array<std::vector<int>, 8> quad_dof_ord;
std::array<std::vector<int>, 24> tet_dof_ord;
public:
/// Construct the $H^1$ bubble collection consisting of degree-$p$
/// polynomials enriched with the bubble function times degree-$q$
/// polynomials.
explicit H1Bubble_FECollection(const int p, const int q, const int dim = 3,
const int btype = BasisType::GaussLobatto);
const FiniteElement *
FiniteElementForGeometry(Geometry::Type GeomType) const override;
int DofForGeometry(Geometry::Type GeomType) const override
{ return dofs[GeomType]; }
const int *DofOrderForOrientation(Geometry::Type GeomType,
int Or) const override;
const char *Name() const override { return fec_name; }
int GetContType() const override { return CONTINUOUS; }
int GetBasisType() const { return b_type; }
FiniteElementCollection *GetTraceCollection() const override;
FiniteElementCollection *Clone(int p) const override
{ return new H1Bubble_FECollection(p, bubble_order, dim, b_type); }
};
/** @brief Arbitrary order H1-conforming (continuous) finite elements with
positive basis functions. */
class H1Pos_FECollection : public H1_FECollection
+6 -3
View File
@@ -3877,9 +3877,12 @@ const FiniteElement *FiniteElementSpace::GetFE(int i) const
else
{
#ifdef MFEM_DEBUG
// consistency check: fec->GetOrder() and FE->GetOrder() should return
// the same value (for standard, constant-order spaces)
if (!IsVariableOrder() && FE->GetDim() > 0)
// Consistency check: fec->GetOrder() and FE->GetOrder() should return
// the same value (for standard, constant-order spaces). Skip this check
// even for constant-order bubble spaces, since the bubble functions on
// different geometries have different orders.
if (!IsVariableOrder() && FE->GetDim() > 0 &&
dynamic_cast<const H1Bubble_FECollection*>(fec) == nullptr)
{
MFEM_ASSERT(FE->GetOrder() == fec->GetOrder(),
"internal error: " <<
+42 -43
View File
@@ -3137,52 +3137,29 @@ void GridFunction::ProjectBdrCoefficient(Coefficient *coeff[],
}
void GridFunction::ProjectBdrCoefficientNormal(
VectorCoefficient &vcoeff, const Array<int> &bdr_attr)
Coefficient *coeff, VectorCoefficient *vcoeff, const Array<int> &bdr_attr)
{
#if 0
// implementation for the case when the face dofs are integrals of the
// normal component.
const FiniteElement *fe;
ElementTransformation *T;
Array<int> dofs;
int dim = vcoeff.GetVDim();
Vector vc(dim), nor(dim), lvec, shape;
for (int i = 0; i < fes->GetNBE(); i++)
if (fes->GetNBE() > 0)
{
if (bdr_attr[fes->GetBdrAttribute(i)-1] == 0)
{
continue;
}
fe = fes->GetBE(i);
T = fes->GetBdrElementTransformation(i);
int intorder = 2*fe->GetOrder(); // !!!
const IntegrationRule &ir = IntRules.Get(fe->GetGeomType(), intorder);
int nd = fe->GetDof();
lvec.SetSize(nd);
shape.SetSize(nd);
lvec = 0.0;
for (int j = 0; j < ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
T->SetIntPoint(&ip);
vcoeff.Eval(vc, *T, ip);
CalcOrtho(T->Jacobian(), nor);
fe->CalcShape(ip, shape);
lvec.Add(ip.weight * (vc * nor), shape);
}
fes->GetBdrElementDofs(i, dofs);
SetSubVector(dofs, lvec);
// TODO: Replace this by GetTypicalBdrElement() once implemented
const FiniteElement *be = fes->GetBE(0);
MFEM_VERIFY(be->GetRangeType() == FiniteElement::SCALAR &&
be->GetMapType() == FiniteElement::INTEGRAL, "Not an RT FE space!");
}
#else
// implementation for the case when the face dofs are scaled point
// values of the normal component.
const FiniteElement *fe;
ElementTransformation *T;
Array<int> dofs;
int dim = vcoeff.GetVDim();
Vector vc(dim), nor(dim), lvec;
Vector vc, nor, lvec;
DofTransformation doftrans;
if (vcoeff)
{
const int dim = vcoeff->GetVDim();
vc.SetSize(dim);
nor.SetSize(dim);
}
for (int i = 0; i < fes->GetNBE(); i++)
{
@@ -3198,15 +3175,22 @@ void GridFunction::ProjectBdrCoefficientNormal(
{
const IntegrationPoint &ip = ir.IntPoint(j);
T->SetIntPoint(&ip);
vcoeff.Eval(vc, *T, ip);
CalcOrtho(T->Jacobian(), nor);
lvec(j) = (vc * nor);
if (coeff)
{
const real_t c = coeff->Eval(*T, ip);
lvec(j) = c * T->Weight();
}
else if (vcoeff)
{
vcoeff->Eval(vc, *T, ip);
CalcOrtho(T->Jacobian(), nor);
lvec(j) = (vc * nor);
}
}
fes->GetBdrElementDofs(i, dofs, doftrans);
doftrans.TransformPrimal(lvec);
SetSubVector(dofs, lvec);
}
#endif
}
void GridFunction::ProjectBdrCoefficientTangent(
@@ -5007,6 +4991,14 @@ real_t ExtrudeCoefficient::Eval(ElementTransformation &T,
return sol_in.Eval(*T_in, ip);
}
void VectorExtrudeCoefficient::Eval(Vector &v, ElementTransformation &T,
const IntegrationPoint &ip)
{
ElementTransformation *T_in =
mesh_in->GetElementTransformation(T.ElementNo / n);
T_in->SetIntPoint(&ip);
sol_in.Eval(v, *T_in, ip);
}
GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
GridFunction *sol, const int ny)
@@ -5057,10 +5049,17 @@ GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
return NULL;
}
FiniteElementSpace *solfes2d;
// assuming sol is scalar
solfes2d = new FiniteElementSpace(mesh2d, solfec2d);
const int vdim = sol->FESpace()->GetVDim();
solfes2d = new FiniteElementSpace(mesh2d, solfec2d, vdim);
sol2d = new GridFunction(solfes2d);
sol2d->MakeOwner(solfec2d);
if (vdim > 1)
{
VectorGridFunctionCoefficient vcsol(sol);
VectorExtrudeCoefficient vc2d(mesh, vcsol, ny);
sol2d->ProjectCoefficient(vc2d);
}
else
{
GridFunctionCoefficient csol(sol);
ExtrudeCoefficient c2d(mesh, csol, ny);
+62 -9
View File
@@ -532,6 +532,9 @@ public:
std::unique_ptr<GridFunction> ProlongateToMaxOrder() const;
protected:
void ProjectBdrCoefficientNormal(Coefficient *coeff, VectorCoefficient *vcoeff,
const Array<int> &attr);
/** @brief Accumulates (depending on @a type) the values of @a coeff at all
shared vdofs and counts in how many zones each vdof appears. */
void AccumulateAndCountZones(Coefficient &coeff, AvgType type,
@@ -656,15 +659,26 @@ public:
virtual void ProjectBdrCoefficient(Coefficient *coeff[],
const Array<int> &attr);
/** Project the normal component of the given VectorCoefficient on
the boundary. Only boundary attributes that are marked in
'bdr_attr' are projected. Assumes RT-type VectorFE GridFunction. */
/** @brief Project the normal component of the given VectorCoefficient on
the boundary. */
/** Only boundary attributes that are marked in @a bdr_attr are
projected. Assumes RT-type vector finite element GridFunction. */
void ProjectBdrCoefficientNormal(VectorCoefficient &vcoeff,
const Array<int> &bdr_attr);
const Array<int> &bdr_attr)
{ ProjectBdrCoefficientNormal(NULL, &vcoeff, bdr_attr); }
/** @brief Project the given Coefficient in the normal direction on the
boundary. */
/** Only boundary attributes that are marked in @a bdr_attr are projected.
Assumes RT-type vector finite element GridFunction. */
void ProjectBdrCoefficientNormal(Coefficient &coeff,
const Array<int> &bdr_attr)
{ ProjectBdrCoefficientNormal(&coeff, NULL, bdr_attr); }
/** @brief Project the tangential components of the given VectorCoefficient
on the boundary. Only boundary attributes that are marked in @a bdr_attr
are projected. Assumes ND-type VectorFE GridFunction. */
on the boundary. */
/** Only boundary attributes that are marked in @a bdr_attr
are projected. Assumes ND-type vector finite element GridFunction. */
virtual void ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
const Array<int> &bdr_attr);
@@ -1914,7 +1928,7 @@ real_t ComputeElementLpDistance(real_t p, int i,
GridFunction& gf1, GridFunction& gf2);
/// Class used for extruding scalar GridFunctions
/// Class used for extruding a scalar coefficient
class ExtrudeCoefficient : public Coefficient
{
private:
@@ -1922,13 +1936,52 @@ private:
Mesh *mesh_in;
Coefficient &sol_in;
public:
/// Constructs an instance of VectorExtrudeCoefficient
/**
* @param m 1D mesh
* @param s 1D vector coefficient
* @param n_ number of transverse elements of the extruded mesh
*/
ExtrudeCoefficient(Mesh *m, Coefficient &s, int n_)
: n(n_), mesh_in(m), sol_in(s) { }
: n(n_), mesh_in(m), sol_in(s)
{ MFEM_VERIFY(n > 0, "Number of transverse elements must be positive!"); }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
virtual ~ExtrudeCoefficient() { }
};
/// Extrude a scalar 1D GridFunction, after extruding the mesh with Extrude1D.
/// Class used for extruding a vector coefficient
class VectorExtrudeCoefficient : public VectorCoefficient
{
private:
int n;
Mesh *mesh_in;
VectorCoefficient &sol_in;
public:
/// Constructs an instance of VectorExtrudeCoefficient
/**
* @param m 1D mesh
* @param s 1D vector coefficient
* @param n_ number of transverse elements of the extruded mesh
*/
VectorExtrudeCoefficient(Mesh *m, VectorCoefficient &s, int n_)
: VectorCoefficient(s.GetVDim()), n(n_), mesh_in(m), sol_in(s)
{ MFEM_VERIFY(n > 0, "Number of transverse elements must be positive!"); }
void Eval(Vector &v, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual ~VectorExtrudeCoefficient() { }
};
/// Extrude a 1D GridFunction, after extruding the mesh with Extrude1D()
/**
* @param mesh 1D mesh
* @param mesh2d extruded mesh
* @param sol grid function
* @param ny number of transverse elements of the extruded mesh
*/
GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
GridFunction *sol, const int ny);
+18 -8
View File
@@ -197,15 +197,21 @@ static void EAHdivAssemble3D(const int NE,
// Assemble (one row per thread)
MFEM_FOREACH_THREAD(idx_i, x, NDOF)
{
// NOTE: due to an llvm backend bug, usage of the modulus operator
// has been removed from this foreach section.
const int ic = idx_i / NDOF_C;
const int idx_ii = idx_i % NDOF_C;
const int idx_ii = idx_i - ic * NDOF_C; // idx_i % NDOF_C
const int nx_i = (ic == 0) ? D1D : D1D-1;
const int ny_i = (ic == 1) ? D1D : D1D-1;
const int ix = idx_ii % nx_i;
const int iy = (idx_ii / nx_i) % ny_i;
const int iz = (idx_ii / nx_i) / ny_i;
const int qx_i = idx_ii / nx_i;
const int ix = idx_ii - qx_i * nx_i; // idx_ii % nx_i
const int qy_i = qx_i / ny_i;
const int iy = qx_i - qy_i * ny_i; // (idx_ii / nx_i) % ny_i
const int iz = qy_i; // (idx_ii / nx_i) / ny_i
const real_t (&Bi1)[MQ1][MD1] = (ic == 0) ? r_Bc : r_Bo;
const real_t (&Bi2)[MQ1][MD1] = (ic == 1) ? r_Bc : r_Bo;
@@ -214,14 +220,18 @@ static void EAHdivAssemble3D(const int NE,
for (int idx_j = 0; idx_j < NDOF; ++idx_j)
{
const int jc = idx_j / NDOF_C;
const int idx_jj = idx_j % NDOF_C;
const int idx_jj = idx_j - jc * NDOF_C; // idx_j % NDOF_C
const int nx_j = (jc == 0) ? D1D : D1D-1;
const int ny_j = (jc == 1) ? D1D : D1D-1;
const int jx = idx_jj % nx_j;
const int jy = (idx_jj / nx_j) % ny_j;
const int jz = (idx_jj / nx_j) / ny_j;
const int qx_j = idx_jj / nx_j;
const int jx = idx_jj - qx_j * nx_j; // idx_jj % nx_j
const int qy_j = qx_j / ny_j;
const int jy = qx_j - qy_j * ny_j; // (idx_jj / nx_j) % ny_j
const int jz = qy_j; // (idx_jj / nx_j) / ny_j
const real_t (&Bj1)[MQ1][MD1] = (jc == 0) ? r_Bc : r_Bo;
const real_t (&Bj2)[MQ1][MD1] = (jc == 1) ? r_Bc : r_Bo;
+811 -327
View File
File diff suppressed because it is too large Load Diff
+30 -27
View File
@@ -125,18 +125,6 @@ private:
void AddTriPoints3b(const int off, const real_t b, const real_t weight)
{ AddTriPoints3(off, (1. - b)/2., b, weight); }
void AddTriPoints3R(const int off, const real_t a, const real_t b,
const real_t c, const real_t weight)
{
IntPoint(off + 0).Set2w(a, b, weight);
IntPoint(off + 1).Set2w(c, a, weight);
IntPoint(off + 2).Set2w(b, c, weight);
}
void AddTriPoints3R(const int off, const real_t a, const real_t b,
const real_t weight)
{ AddTriPoints3R(off, a, b, 1. - a - b, weight); }
void AddTriPoints6(const int off, const real_t a, const real_t b,
const real_t c, const real_t weight)
{
@@ -183,14 +171,6 @@ private:
AddTetPoints3(off + 1, a, 1. - 3.*a, weight);
}
// given b, add the permutations of (a,a,a,b), where 3*a + b = 1
void AddTetPoints4b(const int off, const real_t b, const real_t weight)
{
const real_t a = (1. - b)/3.;
IntPoint(off).Set(a, a, a, weight);
AddTetPoints3(off + 1, a, b, weight);
}
// add the permutations of (a,a,b,b), 2*(a + b) = 1
void AddTetPoints6(const int off, const real_t a, const real_t weight)
{
@@ -209,14 +189,37 @@ private:
AddTetPoints6(off + 6, a, bc, cb, weight);
}
// given (b,c), add the permutations of (a,a,b,c), 2*a + b + c = 1
void AddTetPoints12bc(const int off, const real_t b, const real_t c,
const real_t weight)
// add all 24 permutations of (a,b,c,d) where a+b+c+d = 1, all distinct
void AddTetPoints24(const int off, const real_t a, const real_t b,
const real_t c, const real_t weight)
{
const real_t a = (1. - b - c)/2.;
AddTetPoints3(off, a, b, weight);
AddTetPoints3(off + 3, a, c, weight);
AddTetPoints6(off + 6, a, b, c, weight);
const real_t d = 1. - a - b - c;
// all 24 permutations of 4 distinct barycentric coordinates
// permuting which coordinate goes to x, y, z (4th is 1-x-y-z)
IntPoint(off + 0).Set(a, b, c, weight);
IntPoint(off + 1).Set(a, b, d, weight);
IntPoint(off + 2).Set(a, c, b, weight);
IntPoint(off + 3).Set(a, c, d, weight);
IntPoint(off + 4).Set(a, d, b, weight);
IntPoint(off + 5).Set(a, d, c, weight);
IntPoint(off + 6).Set(b, a, c, weight);
IntPoint(off + 7).Set(b, a, d, weight);
IntPoint(off + 8).Set(b, c, a, weight);
IntPoint(off + 9).Set(b, c, d, weight);
IntPoint(off + 10).Set(b, d, a, weight);
IntPoint(off + 11).Set(b, d, c, weight);
IntPoint(off + 12).Set(c, a, b, weight);
IntPoint(off + 13).Set(c, a, d, weight);
IntPoint(off + 14).Set(c, b, a, weight);
IntPoint(off + 15).Set(c, b, d, weight);
IntPoint(off + 16).Set(c, d, a, weight);
IntPoint(off + 17).Set(c, d, b, weight);
IntPoint(off + 18).Set(d, a, b, weight);
IntPoint(off + 19).Set(d, a, c, weight);
IntPoint(off + 20).Set(d, b, a, weight);
IntPoint(off + 21).Set(d, b, c, weight);
IntPoint(off + 22).Set(d, c, a, weight);
IntPoint(off + 23).Set(d, c, b, weight);
}
public:
+3 -1
View File
@@ -297,7 +297,8 @@ void LinearForm::Assemble()
tr = mesh->GetBdrFaceTransformations(i);
if (tr != NULL)
{
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
mfem::DofTransformation doftrans;
fes -> GetElementVDofs (tr -> Elem1No, vdofs, doftrans);
for (int k = 0; k < boundary_face_integs.Size(); k++)
{
if (boundary_face_integs_marker[k] &&
@@ -307,6 +308,7 @@ void LinearForm::Assemble()
boundary_face_integs[k]->
AssembleRHSElementVect(*fes->GetFE(tr->Elem1No),
*tr, elemvect);
doftrans.TransformDual(elemvect);
AddElementVector (vdofs, elemvect);
}
}
+11 -5
View File
@@ -321,12 +321,17 @@ void ParL2FaceRestriction::DoubleValuedConformingMult(
const int vd = vdim;
const bool t = byvdim;
const int threshold = ndofs;
const int nsdofs = pfes.GetFaceNbrVSize();
const int nsdofs = pfes.GetFaceNbrVSize() / vd;
auto d_indices1 = scatter_indices1.Read();
auto d_indices2 = scatter_indices2.Read();
auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
auto d_x_shared = Reshape(face_nbr_data.Read(),
t?vd:nsdofs, t?nsdofs:vd);
const int ne_shared = nsdofs / elem_dofs;
const int nedof = elem_dofs;
// Note: the shape of face_nbr_data, as determined by
// ParFiniteElementSpace::ExchangeFaceNbrData, is (elem_dofs, vdim,
// ne_shared), independent of the ordering (byNODES or byVDIM) of the finite
// element space.
auto d_x_shared = Reshape(face_nbr_data.Read(), elem_dofs, vd, ne_shared);
auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
{
@@ -346,8 +351,9 @@ void ParL2FaceRestriction::DoubleValuedConformingMult(
}
else if (idx2>=threshold) // shared boundary
{
d_y(dof, c, 1, face) = d_x_shared(t?c:(idx2-threshold),
t?(idx2-threshold):c);
const int e_shared = (idx2 - threshold) / nedof;
const int i_shared = (idx2 - threshold) % nedof;
d_y(dof, c, 1, face) = d_x_shared(i_shared,c,e_shared);
}
else // true boundary
{
+3 -6
View File
@@ -1398,20 +1398,17 @@ void L2FaceRestriction::PermuteAndSetSharedFaceDofsScatterIndices2(
const int dim = fes.GetMesh()->Dimension();
const int dof1d = fes.GetTypicalFE()->GetOrder()+1;
fes.GetTypicalFE()->GetFaceMap(face_id2, face_map);
Array<int> face_nbr_dofs;
const ParFiniteElementSpace &pfes =
static_cast<const ParFiniteElementSpace&>(this->fes);
pfes.GetFaceNbrElementVDofs(elem_index, face_nbr_dofs);
for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
{
const int face_dof_elem2 = PermuteFaceL2(dim, face_id1, face_id2,
orientation, dof1d, face_dof_elem1);
const int volume_dof_elem2 = face_map[face_dof_elem2];
const int global_dof_elem2 = face_nbr_dofs[volume_dof_elem2];
// Encode the volume DOF index and element index
const int global_dof_elem2 = elem_index*elem_dofs + volume_dof_elem2;
const int restriction_dof_elem2 = face_dofs*face_index + face_dof_elem1;
// Trick to differentiate dof location inter/shared
scatter_indices2[restriction_dof_elem2] = ndofs+global_dof_elem2;
scatter_indices2[restriction_dof_elem2] = ndofs + global_dof_elem2;
}
#endif
}
+12 -3
View File
@@ -278,9 +278,18 @@ void ArraysByName<T>::Load(std::istream &in)
q1 = ArrayLine.find(' ');
ArrayName = ArrayLine.substr(0,q1-1);
}
// Ignore the remainder of the line which may contain explanatory comments
data[ArrayName].Load(in, 0);
if (q1+2 < ArrayLine.size())
{
// Read the remainder of the line which contains the array data
std::istringstream ArrayDataStream(ArrayLine.substr(q1+2,
ArrayLine.size()));
data[ArrayName].Load(ArrayDataStream, 0);
}
else
{
// Read the array data starting on the next line
data[ArrayName].Load(in, 0);
}
}
}
+4 -4
View File
@@ -726,16 +726,16 @@ std::string Device::GetUUID(const int device_id)
MFEM_GPU_CHECK(cudaGetDeviceProperties(&prop, device_id));
for (int i = 0; i < 16; ++i)
{
res << std::setfill('0') << std::setw(2) << std::hex
<< static_cast<unsigned>(prop.uuid.bytes[i]);
const unsigned b = static_cast<unsigned char>(prop.uuid.bytes[i]);
res << std::setfill('0') << std::setw(2) << std::hex << b;
}
#elif defined(MFEM_USE_HIP)
hipUUID uuid;
MFEM_GPU_CHECK(hipDeviceGetUuid(&uuid, device_id));
for (int i = 0; i < 16; ++i)
{
res << std::setfill('0') << std::setw(2) << std::hex
<< static_cast<unsigned>(uuid.bytes[i]);
const unsigned b = static_cast<unsigned char>(uuid.bytes[i]);
res << std::setfill('0') << std::setw(2) << std::hex << b;
}
#endif
return res.str();
+1 -14
View File
@@ -22,14 +22,6 @@
//#define _WIN32
//#define _aligned_malloc(s,a) malloc(s)
#ifdef NVTX_DEBUG_HPP
#undef NVTX_COLOR
#define NVTX_COLOR ::nvtx::kGold
#include NVTX_DEBUG_HPP
#else
#define dbg(...)
#endif
#ifndef _WIN32
#include <unistd.h>
#include <signal.h>
@@ -768,12 +760,7 @@ private:
switch (mt)
{
case MT::HOST_DEBUG:
if (GetEnv("MFEM_MMU_STD"))
{
dbg("Using STD memory space for debug device!");
return new StdHostMemorySpace();
}
dbg("Using MMU memory space for debug device!");
if (GetEnv("MFEM_MMU_STD")) { return new StdHostMemorySpace(); }
return new MmuHostMemorySpace();
#ifdef MFEM_USE_UMPIRE
case MT::HOST_UMPIRE:
-8
View File
@@ -200,10 +200,6 @@ void OptionsParser::Parse()
isValid = isValidAsInt(argv[i]);
*(int *)(options[j].var_ptr) = atoi(argv[i++]);
break;
case LONG:
isValid = isValidAsInt(argv[i]);
*(long long *)(options[j].var_ptr) = atoi(argv[i++]);
break;
case DOUBLE:
isValid = isValidAsDouble(argv[i]);
*(real_t *)(options[j].var_ptr) = atof(argv[i++]);
@@ -282,10 +278,6 @@ void OptionsParser::WriteValue(const Option &opt, std::ostream &os)
case INT:
os << *(int *)(opt.var_ptr);
break;
case LONG:
os << *(long long *)(opt.var_ptr);
break;
case DOUBLE:
os << *(real_t *)(opt.var_ptr);
+1 -9
View File
@@ -31,7 +31,7 @@ class Vector;
class OptionsParser
{
public:
enum OptionType { INT, DOUBLE, STRING, STD_STRING, ENABLE, DISABLE, ARRAY, VECTOR , LONG };
enum OptionType { INT, DOUBLE, STRING, STD_STRING, ENABLE, DISABLE, ARRAY, VECTOR };
private:
struct Option
@@ -98,14 +98,6 @@ public:
required));
}
/// Add a long integer option and set 'var' to receive the value.
void AddOption(long long *var, const char *short_name, const char *long_name,
const char *description, bool required = false)
{
options.Append(Option(LONG, var, short_name, long_name, description,
required));
}
/// Add a double option and set 'var' to receive the value.
void AddOption(real_t *var, const char *short_name, const char *long_name,
const char *description, bool required = false)
+3
View File
@@ -317,6 +317,9 @@ void HypreParVector::WrapHypreParVector(hypre_ParVector *y, bool owner)
Vector * HypreParVector::GlobalVector() const
{
MFEM_VERIFY(size > 0,
"GlobalVector method can only be called on vectors wherein each "
"process owns one or more entries");
hypre_Vector *hv = hypre_ParVectorToVectorAll(*this);
Vector *v = new Vector(hv->data, internal::to_int(hv->size));
v->MakeDataOwner();
+44 -78
View File
@@ -38,6 +38,13 @@
#if PETSC_VERSION_LT(3,19,0)
#define PETSC_SUCCESS 0
#endif
#if PETSC_VERSION_LT(3,23,0)
#define PetscContainerSetCtxDestroy(A,B) PetscContainerSetUserDestroy(A,B)
typedef PetscErrorCode (PetscCtxDestroyFn)(void**);
#endif
#if PETSC_VERSION_LT(3,24,0)
typedef PetscErrorCode KSPMonitorFn(KSP,PetscInt,PetscReal,void*);
#endif
#include <fstream>
#include <iomanip>
@@ -77,13 +84,17 @@ static PetscErrorCode __mfem_mat_shell_apply_transpose(Mat,Vec,Vec);
static PetscErrorCode __mfem_mat_shell_destroy(Mat);
static PetscErrorCode __mfem_mat_shell_copy(Mat,Mat,MatStructure);
#if PETSC_VERSION_LT(3,23,0)
static PetscErrorCode __mfem_array_container_destroy(void*);
static PetscErrorCode __mfem_matarray_container_destroy(void *);
#else
static PetscErrorCode __mfem_array_container_destroy(void**);
static PetscErrorCode __mfem_matarray_container_destroy(void**);
typedef void *PetscCtxRt;
#elif PETSC_VERSION_LT(3,25,0)
typedef void **PetscCtxRt;
#endif
static PetscErrorCode __mfem_array_container_destroy(PetscCtxRt);
static PetscErrorCode __mfem_matarray_container_destroy(PetscCtxRt);
#if PETSC_VERSION_LT(3,23,0)
static PetscErrorCode __mfem_monitor_ctx_destroy(void**);
#else
static PetscErrorCode __mfem_monitor_ctx_destroy(PetscCtxRt);
#endif
// auxiliary functions
static PetscErrorCode Convert_Array_IS(MPI_Comm,bool,const mfem::Array<int>*,
@@ -1317,11 +1328,7 @@ BlockDiagonalConstructor(MPI_Comm comm,
ierr = PetscContainerCreate(comm,&c); CCHKERRQ(comm,ierr);
ierr = PetscContainerSetPointer(c,ptrs[i]); CCHKERRQ(comm,ierr);
#if PETSC_VERSION_LT(3,23,0)
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
#else
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
#endif
CCHKERRQ(comm,ierr);
ierr = PetscObjectCompose((PetscObject)A,names[i],(PetscObject)c);
CCHKERRQ(comm,ierr);
@@ -1648,11 +1655,7 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
PetscContainer c;
ierr = PetscContainerCreate(comm,&c); CCHKERRQ(comm,ierr);
ierr = PetscContainerSetPointer(c,vmatsl2l); PCHKERRQ(c,ierr);
#if PETSC_VERSION_LT(3,23,0)
ierr = PetscContainerSetUserDestroy(c,__mfem_matarray_container_destroy);
#else
ierr = PetscContainerSetCtxDestroy(c,__mfem_matarray_container_destroy);
#endif
PCHKERRQ(c,ierr);
ierr = PetscObjectCompose((PetscObject)(*A),"_MatIS_PtAP_l2l",(PetscObject)c);
PCHKERRQ((*A),ierr);
@@ -1748,11 +1751,7 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
ierr = PetscContainerCreate(PETSC_COMM_SELF,&c); PCHKERRQ(B,ierr);
ierr = PetscContainerSetPointer(c,ptrs[i]); PCHKERRQ(B,ierr);
#if PETSC_VERSION_LT(3,23,0)
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
#else
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
#endif
PCHKERRQ(B,ierr);
ierr = PetscObjectCompose((PetscObject)(B),names[i],(PetscObject)c);
PCHKERRQ(B,ierr);
@@ -2198,11 +2197,7 @@ PetscParMatrix * RAP(PetscParMatrix *Rt, PetscParMatrix *A, PetscParMatrix *P)
ierr = PetscContainerCreate(PetscObjectComm((PetscObject)B),&c);
PCHKERRQ(B,ierr);
ierr = PetscContainerSetPointer(c,vmatsl2l); PCHKERRQ(c,ierr);
#if PETSC_VERSION_LT(3,23,0)
ierr = PetscContainerSetUserDestroy(c,__mfem_matarray_container_destroy);
#else
ierr = PetscContainerSetCtxDestroy(c,__mfem_matarray_container_destroy);
#endif
PCHKERRQ(c,ierr);
ierr = PetscObjectCompose((PetscObject)B,"_MatIS_PtAP_l2l",(PetscObject)c);
PCHKERRQ(B,ierr);
@@ -2485,7 +2480,6 @@ void PetscSolver::SetMaxIter(int max_iter)
void PetscSolver::SetPrintLevel(int plev)
{
typedef PetscErrorCode (*myPetscFunc)(void**);
PetscViewerAndFormat *vf = NULL;
PetscViewer viewer = PETSC_VIEWER_STDOUT_(PetscObjectComm(obj));
@@ -2498,7 +2492,6 @@ void PetscSolver::SetPrintLevel(int plev)
{
// there are many other options, see the function KSPSetFromOptions() in
// src/ksp/ksp/interface/itcl.c
typedef PetscErrorCode (*myMonitor)(KSP,PetscInt,PetscReal,void*);
KSP ksp = (KSP)obj;
if (plev >= 0)
{
@@ -2507,29 +2500,29 @@ void PetscSolver::SetPrintLevel(int plev)
if (plev == 1)
{
#if PETSC_VERSION_LT(3,15,0)
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorDefault,vf,
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorDefault,vf,
#else
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorResidual,vf,
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorResidual,vf,
#endif
(myPetscFunc)PetscViewerAndFormatDestroy);
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
PCHKERRQ(ksp,ierr);
}
else if (plev > 1)
{
ierr = KSPSetComputeSingularValues(ksp,PETSC_TRUE); PCHKERRQ(ksp,ierr);
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorSingularValue,vf,
(myPetscFunc)PetscViewerAndFormatDestroy);
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorSingularValue,vf,
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
PCHKERRQ(ksp,ierr);
if (plev > 2)
{
ierr = PetscViewerAndFormatCreate(viewer,PETSC_VIEWER_DEFAULT,&vf);
PCHKERRQ(viewer,ierr);
#if PETSC_VERSION_LT(3,15,0)
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorTrueResidualNorm,vf,
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorTrueResidualNorm,vf,
#else
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorTrueResidual,vf,
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorTrueResidual,vf,
#endif
(myPetscFunc)PetscViewerAndFormatDestroy);
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
PCHKERRQ(ksp,ierr);
}
}
@@ -2545,7 +2538,7 @@ void PetscSolver::SetPrintLevel(int plev)
if (plev > 0)
{
ierr = SNESMonitorSet(snes,(myMonitor)SNESMonitorDefault,vf,
(myPetscFunc)PetscViewerAndFormatDestroy);
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
PCHKERRQ(snes,ierr);
}
}
@@ -5329,21 +5322,27 @@ static PetscErrorCode __mfem_pc_shell_destroy(PC pc)
PetscFunctionReturn(PETSC_SUCCESS);
}
static PetscErrorCode __mfem_array_container_destroy(PetscCtxRt ptr)
{
PetscErrorCode ierr;
PetscFunctionBeginUser;
#if PETSC_VERSION_LT(3,23,0)
static PetscErrorCode __mfem_array_container_destroy(void *ptr)
{
PetscErrorCode ierr;
PetscFunctionBeginUser;
ierr = PetscFree(ptr); CHKERRQ(ierr);
#else
ierr = PetscFree(*(void**)ptr); CHKERRQ(ierr);
#endif
PetscFunctionReturn(PETSC_SUCCESS);
}
static PetscErrorCode __mfem_matarray_container_destroy(void *ptr)
static PetscErrorCode __mfem_matarray_container_destroy(PetscCtxRt ptr)
{
#if PETSC_VERSION_LT(3,23,0)
mfem::Array<Mat> *a = (mfem::Array<Mat>*)ptr;
PetscErrorCode ierr;
#else
mfem::Array<Mat> *a = *(mfem::Array<Mat>**)ptr;
#endif
PetscErrorCode ierr;
PetscFunctionBeginUser;
for (int i=0; i<a->Size(); i++)
@@ -5356,41 +5355,16 @@ static PetscErrorCode __mfem_matarray_container_destroy(void *ptr)
PetscFunctionReturn(PETSC_SUCCESS);
}
#if PETSC_VERSION_LT(3,23,0)
static PetscErrorCode __mfem_monitor_ctx_destroy(void **ctx)
#else
static PetscErrorCode __mfem_array_container_destroy(void **ptr)
static PetscErrorCode __mfem_monitor_ctx_destroy(PetscCtxRt ctx)
#endif
{
PetscErrorCode ierr;
PetscFunctionBeginUser;
ierr = PetscFree(*ptr); CHKERRQ(ierr);
PetscFunctionReturn(PETSC_SUCCESS);
}
static PetscErrorCode __mfem_matarray_container_destroy(void **ptr)
{
mfem::Array<Mat> *a = (mfem::Array<Mat>*)*ptr;
PetscErrorCode ierr;
PetscFunctionBeginUser;
for (int i=0; i<a->Size(); i++)
{
Mat M = (*a)[i];
MPI_Comm comm = PetscObjectComm((PetscObject)M);
ierr = MatDestroy(&M); CCHKERRQ(comm,ierr);
}
delete a;
PetscFunctionReturn(PETSC_SUCCESS);
}
#endif
static PetscErrorCode __mfem_monitor_ctx_destroy(void **ctx)
{
PetscErrorCode ierr;
PetscFunctionBeginUser;
ierr = PetscFree(*ctx); CHKERRQ(ierr);
ierr = PetscFree(*(void**)ctx); CHKERRQ(ierr);
PetscFunctionReturn(PETSC_SUCCESS);
}
@@ -5635,11 +5609,7 @@ static PetscErrorCode MatConvert_hypreParCSR_AIJ(hypre_ParCSRMatrix* hA,Mat* pA)
ierr = PetscContainerCreate(comm,&c); CHKERRQ(ierr);
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
#if PETSC_VERSION_LT(3,23,0)
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
#else
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
#endif
CHKERRQ(ierr);
ierr = PetscObjectCompose((PetscObject)(*pA),names[i],(PetscObject)c);
CHKERRQ(ierr);
@@ -5733,11 +5703,7 @@ static PetscErrorCode MatConvert_hypreParCSR_IS(hypre_ParCSRMatrix* hA,Mat* pA)
ierr = PetscContainerCreate(PETSC_COMM_SELF,&c); CHKERRQ(ierr);
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
#if PETSC_VERSION_LT(3,23,0)
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
#else
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
#endif
CHKERRQ(ierr);
ierr = PetscObjectCompose((PetscObject)lA,names[i],(PetscObject)c);
CHKERRQ(ierr);
+2 -2
View File
@@ -126,11 +126,11 @@ EXAMPLE_TEST_DIRS := examples
MINIAPP_SUBDIRS = common electromagnetics meshing performance tools \
toys nurbs gslib adjoint solvers shifted mtop parelag tribol autodiff dfem \
hooke multidomain dpg hdiv-linear-solver spde diag-smoothers contact \
fluids/navier fluids/schrodinger-flow
fluids/navier fluids/schrodinger-flow plasma
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics meshing tools \
toys shifted dpg diag-smoothers fluids/navier)
toys shifted dpg diag-smoothers fluids/navier plasma)
EM_DIRS = $(EXAMPLE_DIRS) $(MINIAPP_DIRS)
+12
View File
@@ -3206,10 +3206,22 @@ public:
/// Extrude a 1D mesh
/**
* @param mesh 1D mesh
* @param ny number of transverse elements of the extruded mesh
* @param sy physical size in the direction of extrusion
* @param closed if false, only the original boundaries are extruded,
* otherwise boundaries are generated all around the domain
*/
Mesh *Extrude1D(Mesh *mesh, const int ny, const real_t sy,
const bool closed = false);
/// Extrude a 2D mesh
/**
* @param mesh 2D mesh
* @param nz number of transverse elements of the extruded mesh
* @param sz physical size in the direction of extrusion
*/
Mesh *Extrude2D(Mesh *mesh, const int nz, const real_t sz);
/** @brief Constructs the smallest possible [0,1]^dim serial mesh that can be
+6 -3
View File
@@ -1516,12 +1516,15 @@ void Mesh::ReadInlineMesh(std::istream &input, bool generate_edges)
void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
{
string buff;
real_t version;
string version;
int binary, dsize;
input >> version >> binary >> dsize;
if (version < 2.2)
if (version != "2.2")
{
MFEM_ABORT("Gmsh file version < 2.2");
MFEM_ABORT("Gmsh file version must be 2.2, found version "
<< version << ".\n"
"To convert your mesh to the required format, use:\n"
" gmsh -format msh22 -save -o output.msh input.msh");
}
if (dsize != sizeof(double))
{
+1 -2
View File
@@ -31,12 +31,11 @@ add_subdirectory(hdiv-linear-solver)
add_subdirectory(hooke)
add_subdirectory(meshing)
add_subdirectory(mtop)
add_subdirectory(mtop/chpt)
add_subdirectory(mtop/examples)
add_subdirectory(multidomain)
add_subdirectory(nurbs)
add_subdirectory(parelag)
add_subdirectory(performance)
add_subdirectory(plasma)
add_subdirectory(shifted)
add_subdirectory(solvers)
add_subdirectory(spde)
File diff suppressed because it is too large Load Diff
-65
View File
@@ -1,65 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
list(APPEND CHPT_SOURCES )
list(APPEND CHPT_HEADERS )
convert_filenames_to_full_paths(CHPT_SOURCES)
convert_filenames_to_full_paths(CHPT_HEADERS)
set(CHPT_COMMON_FILES
EXTRA_SOURCES ${CHPT_SOURCES}
EXTRA_HEADERS ${CHPT_HEADERS})
add_mfem_miniapp(mtop_test_scalar_chpt
MAIN tst_scalar_field.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_vector_chpt
MAIN tst_vector_field.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_fixedslot
MAIN tst_fixedslot_scalar_vector.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_segmentio
MAIN tst_segmentio_scalar_vector_mfem.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_file
MAIN tst_fileio_scalar_vector_mfem.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_revolve
MAIN revolve.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_dyn
MAIN tst_dynamic_chpt.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_rev_fixedslot
MAIN tst_revolve_chpt_fixedslot.cpp
${CHPT_COMMON_FILES}
LIBRARIES mfem)
@@ -1,309 +0,0 @@
#ifndef MFEM_DYNAMIC_CHECKPOINTING_HPP
#define MFEM_DYNAMIC_CHECKPOINTING_HPP
#include "mfem.hpp"
#include <map>
#include <memory>
#include <limits>
#include <vector>
#include <utility>
namespace mfem
{
template <typename Snapshot>
class InMemoryCheckpointStorage
{
public:
using Handle = int;
Handle InvalidHandle() const { return -1; }
bool IsValid(const Handle &h) const { return h >= 0; }
Handle Store(Snapshot &&snap)
{
Handle id = InvalidHandle();
if (!free_.empty())
{
id = free_.back();
free_.pop_back();
if (slots_[id])
{
// reuse existing allocation
*slots_[id] = std::move(snap);
}
else
{
slots_[id].reset(new Snapshot(std::move(snap)));
}
}
else
{
id = (Handle) slots_.size();
slots_.push_back(std::unique_ptr<Snapshot>(new Snapshot(std::move(snap))));
}
return id;
}
template <typename Func>
void Read(const Handle &h, Func &&f) const
{
MFEM_VERIFY(IsValid(h), "InMemoryCheckpointStorage: invalid handle.");
MFEM_VERIFY(h < (Handle) slots_.size(), "InMemoryCheckpointStorage: handle out of range.");
MFEM_VERIFY(slots_[h].get() != nullptr, "InMemoryCheckpointStorage: empty slot.");
f(*slots_[h]);
}
void Erase(Handle &h)
{
if (!IsValid(h)) { h = InvalidHandle(); return; }
MFEM_VERIFY(h < (Handle) slots_.size(), "InMemoryCheckpointStorage: handle out of range.");
slots_[h].reset();
free_.push_back(h);
h = InvalidHandle();
}
private:
std::vector<std::unique_ptr<Snapshot>> slots_;
std::vector<Handle> free_;
};
/**
* Dynamic checkpointing manager (WangMoinIaccarino 2009), with pluggable storage.
*
* Snapshot: checkpointed object (often the primal State itself).
* Storage : must provide:
* using Handle = ...
* Handle InvalidHandle() const;
* bool IsValid(const Handle&) const;
* Handle Store(Snapshot&&);
* template<class F> void Read(const Handle&, F&&) const; // calls f(const Snapshot&)
* void Erase(Handle&);
*/
template <typename Snapshot,
typename Storage = InMemoryCheckpointStorage<Snapshot>>
class DynamicCheckpointing
{
public:
using Step = long long;
using Handle = typename Storage::Handle;
struct Checkpoint
{
int level = 0;
Handle h; // InvalidHandle => placeholder
Checkpoint() = default;
Checkpoint(int lvl, const Handle &inv) : level(lvl), h(inv) {}
};
explicit DynamicCheckpointing(int s)
: s_(s),
owned_storage_(new Storage()),
storage_(owned_storage_.get())
{
MFEM_VERIFY(s_ > 0, "DynamicCheckpointing: s must be > 0.");
Reset();
}
DynamicCheckpointing(int s, Storage &external_storage)
: s_(s),
owned_storage_(nullptr),
storage_(&external_storage)
{
MFEM_VERIFY(s_ > 0, "DynamicCheckpointing: s must be > 0.");
Reset();
}
~DynamicCheckpointing() { ReleaseAll(); }
void Reset()
{
ReleaseAll();
cps_.emplace(Step(0), Checkpoint(InfLevel(), storage_->InvalidHandle()));
}
struct CheckpointInfo
{
Step step;
int level;
bool stored;
};
std::vector<CheckpointInfo> GetCheckpointInfo() const
{
std::vector<CheckpointInfo> out;
out.reserve(cps_.size());
for (const auto &kv : cps_)
{
out.push_back({kv.first, kv.second.level, storage_->IsValid(kv.second.h)});
}
return out;
}
Step GetMaxStep() const
{
MFEM_VERIFY(!cps_.empty(), "DynamicCheckpointing: checkpoint map is empty.");
return cps_.rbegin()->first;
}
template <typename State, typename PrimalStepFn, typename MakeSnapshotFn>
void ForwardStep(const Step i,
State &u_i_inout,
PrimalStepFn &&primal_step,
MakeSnapshotFn &&make_snapshot)
{
AllocateCheckpointForNextStep(i);
auto it = cps_.find(i);
if (it != cps_.end())
{
storage_->Erase(it->second.h);
Snapshot snap = make_snapshot(u_i_inout);
it->second.h = storage_->Store(std::move(snap));
}
primal_step(u_i_inout, i);
}
template <typename State,
typename AdjState,
typename PrimalStepFn,
typename AdjointStepFn,
typename MakeSnapshotFn,
typename RestoreSnapshotFn>
void BackwardStep(const Step i,
AdjState &q_ip1_inout,
State &u_work_inout,
PrimalStepFn &&primal_step,
AdjointStepFn &&adjoint_step,
MakeSnapshotFn &&make_snapshot,
RestoreSnapshotFn &&restore_snapshot)
{
// remove placeholder at i+1
const Step ph = i + 1;
auto it_ph = cps_.find(ph);
MFEM_VERIFY(it_ph != cps_.end(),
"DynamicCheckpointing: expected checkpoint at i+1 before BackwardStep.");
storage_->Erase(it_ph->second.h);
cps_.erase(it_ph);
MFEM_ASSERT(GetMaxStep() <= i,
"DynamicCheckpointing: found a checkpoint beyond current adjoint step.");
auto restore_from_handle = [&](Handle &h)
{
storage_->Read(h, [&](const Snapshot &snap)
{
restore_snapshot(snap, u_work_inout);
});
storage_->Erase(h); // retrieved => placeholder (Algorithm 4 semantics)
};
if (GetMaxStep() == i)
{
Handle h = TakeHandleMakePlaceholder(i);
restore_from_handle(h);
}
else
{
const Step k = GetMaxStep();
Handle hk = TakeHandleMakePlaceholder(k);
restore_from_handle(hk);
for (Step t = k; t < i; ++t)
{
ForwardStep(t, u_work_inout, primal_step, make_snapshot);
}
}
adjoint_step(q_ip1_inout, u_work_inout, i);
}
private:
int s_ = 0;
std::unique_ptr<Storage> owned_storage_;
Storage *storage_ = nullptr;
std::map<Step, Checkpoint> cps_;
static int InfLevel() { return std::numeric_limits<int>::max(); }
void ReleaseAll()
{
if (!storage_) { cps_.clear(); return; }
for (auto &kv : cps_) { storage_->Erase(kv.second.h); }
cps_.clear();
}
bool FindDispensableLargestStep(Step &out_step) const
{
int max_level_seen = std::numeric_limits<int>::min();
for (auto it = cps_.rbegin(); it != cps_.rend(); ++it)
{
const Step step = it->first;
const int lvl = it->second.level;
if (max_level_seen > lvl) { out_step = step; return true; }
max_level_seen = (lvl > max_level_seen) ? lvl : max_level_seen;
}
return false;
}
void AllocateCheckpointForNextStep(const Step i)
{
const Step new_step = i + 1;
MFEM_VERIFY(cps_.find(new_step) == cps_.end(),
"DynamicCheckpointing: checkpoint at i+1 already exists.");
const Handle inv = storage_->InvalidHandle();
// allow growth to s+1 entries (incl. placeholder)
if ((int)cps_.size() <= s_)
{
cps_.emplace(new_step, Checkpoint(0, inv));
return;
}
Step disp = -1;
if (FindDispensableLargestStep(disp))
{
auto it = cps_.find(disp);
MFEM_ASSERT(it != cps_.end(), "Internal error: dispensable checkpoint not found.");
storage_->Erase(it->second.h);
cps_.erase(it);
cps_.emplace(new_step, Checkpoint(0, inv));
return;
}
auto it_i = cps_.find(i);
MFEM_VERIFY(it_i != cps_.end(),
"DynamicCheckpointing: promotion expected checkpoint at step i but none found.");
MFEM_VERIFY(i != 0, "DynamicCheckpointing: attempted to remove step 0 checkpoint.");
const int l = it_i->second.level;
storage_->Erase(it_i->second.h);
cps_.erase(it_i);
cps_.emplace(new_step, Checkpoint(l + 1, inv));
}
Handle TakeHandleMakePlaceholder(const Step i)
{
auto it = cps_.find(i);
MFEM_VERIFY(it != cps_.end(),
"DynamicCheckpointing: TakeHandle requested a non-existent checkpoint.");
MFEM_VERIFY(storage_->IsValid(it->second.h),
"DynamicCheckpointing: TakeHandle requested a checkpoint with no snapshot.");
Handle h = std::move(it->second.h);
it->second.h = storage_->InvalidHandle();
return h;
}
};
} // namespace mfem
#endif // MFEM_DYNAMIC_CHECKPOINTING_HPP
@@ -1,291 +0,0 @@
#ifndef MFEM_FILE_CHECKPOINT_STORAGE_HPP
#define MFEM_FILE_CHECKPOINT_STORAGE_HPP
#include "mfem.hpp"
#include <cstdint>
#include <cstdio> // std::remove, std::rename
#include <cstring>
#include <string>
#include <vector>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <type_traits>
#if __has_include(<filesystem>)
#include <filesystem>
namespace mfem_fs = std::filesystem;
#define MFEM_HAVE_FILESYSTEM 1
#else
#define MFEM_HAVE_FILESYSTEM 0
#endif
namespace mfem
{
/**
* @brief Default binary serializer for checkpoint snapshots.
*
* Contract:
* - static void Write(std::ostream&, const Snapshot&)
* - static Snapshot Read (std::istream&)
*
* Default implementation supports trivially-copyable POD types.
* Specialization for mfem::Vector is provided below.
*
* Portability note:
* This binary format is NOT portable across endianness or differing sizeof(real_t).
* If you need portability, provide a custom SnapshotIO (e.g., text, XDR, HDF5).
*/
template <typename Snapshot, typename Enable = void>
struct DefaultCheckpointBinaryIO
{
static void Write(std::ostream &, const Snapshot &)
{
static_assert(sizeof(Snapshot) == 0,
"DefaultCheckpointBinaryIO: no implementation for this Snapshot type. "
"Provide a custom SnapshotIO with Write/Read.");
}
static Snapshot Read(std::istream &)
{
static_assert(sizeof(Snapshot) == 0,
"DefaultCheckpointBinaryIO: no implementation for this Snapshot type. "
"Provide a custom SnapshotIO with Write/Read.");
return Snapshot();
}
};
// POD / trivially-copyable types (double, int, structs of POD, etc.)
template <typename Snapshot>
struct DefaultCheckpointBinaryIO<
Snapshot,
typename std::enable_if<std::is_trivially_copyable<Snapshot>::value>::type>
{
static void Write(std::ostream &os, const Snapshot &x)
{
os.write(reinterpret_cast<const char*>(&x), sizeof(Snapshot));
MFEM_VERIFY(os.good(), "DefaultCheckpointBinaryIO: failed to write POD snapshot.");
}
static Snapshot Read(std::istream &is)
{
Snapshot x;
is.read(reinterpret_cast<char*>(&x), sizeof(Snapshot));
MFEM_VERIFY(is.good(), "DefaultCheckpointBinaryIO: failed to read POD snapshot.");
return x;
}
};
// Specialization for mfem::Vector
template <>
struct DefaultCheckpointBinaryIO<mfem::Vector, void>
{
static void Write(std::ostream &os, const mfem::Vector &v)
{
const std::int64_t n = (std::int64_t) v.Size();
os.write(reinterpret_cast<const char*>(&n), sizeof(n));
MFEM_VERIFY(os.good(), "VectorBinaryIO: failed to write vector size.");
if (n > 0)
{
const mfem::real_t *data = v.GetData();
os.write(reinterpret_cast<const char*>(data),
(std::streamsize)(n * (std::int64_t)sizeof(mfem::real_t)));
MFEM_VERIFY(os.good(), "VectorBinaryIO: failed to write vector data.");
}
}
static mfem::Vector Read(std::istream &is)
{
std::int64_t n = 0;
is.read(reinterpret_cast<char*>(&n), sizeof(n));
MFEM_VERIFY(is.good(), "VectorBinaryIO: failed to read vector size.");
MFEM_VERIFY(n >= 0, "VectorBinaryIO: invalid negative vector size.");
mfem::Vector v((int)n);
if (n > 0)
{
mfem::real_t *data = v.GetData();
is.read(reinterpret_cast<char*>(data),
(std::streamsize)(n * (std::int64_t)sizeof(mfem::real_t)));
MFEM_VERIFY(is.good(), "VectorBinaryIO: failed to read vector data.");
}
return v;
}
};
/**
* @brief File-based checkpoint storage backend.
*
* Stores each snapshot in a separate file:
* <directory>/<prefix><id><extension>
*
* Handle is a monotonically-increasing integer id (reused if keep_files==false).
*
* Template parameters:
* - Snapshot : stored snapshot type
* - SnapshotIO : serializer with static Write/Read methods (see DefaultCheckpointBinaryIO)
*
* Threading:
* - Not thread-safe. Typical adjoint/checkpointing usage is single-threaded control flow.
*/
template <typename Snapshot,
typename SnapshotIO = DefaultCheckpointBinaryIO<Snapshot>>
class FileCheckpointStorage
{
public:
using Handle = std::int64_t;
/**
* @param directory directory where checkpoint files live (created if create_dir==true)
* @param prefix filename prefix (e.g. "ckpt_")
* @param extension filename extension (e.g. ".bin")
* @param create_dir create directory if missing (requires <filesystem>)
* @param keep_files if true, Erase() will NOT delete files (useful for debugging),
* and ids are NOT reused.
*/
FileCheckpointStorage(const std::string &directory,
const std::string &prefix = "ckpt_",
const std::string &extension = ".bin",
bool create_dir = true,
bool keep_files = false)
: dir_(directory),
prefix_(prefix),
ext_(extension),
keep_files_(keep_files)
{
MFEM_VERIFY(!dir_.empty(), "FileCheckpointStorage: directory must be non-empty.");
MFEM_VERIFY(!prefix_.empty(), "FileCheckpointStorage: prefix must be non-empty.");
MFEM_VERIFY(!ext_.empty(), "FileCheckpointStorage: extension must be non-empty.");
if (create_dir)
{
#if MFEM_HAVE_FILESYSTEM
std::error_code ec;
mfem_fs::create_directories(mfem_fs::path(dir_), ec);
MFEM_VERIFY(!ec, "FileCheckpointStorage: failed to create directory: " << dir_);
#else
MFEM_ABORT("FileCheckpointStorage: create_dir=true requires <filesystem> support.");
#endif
}
}
Handle InvalidHandle() const { return (Handle)-1; }
bool IsValid(const Handle &h) const { return h >= 0; }
/**
* @brief Store snapshot to file, return handle.
*
* Uses atomic-ish pattern:
* - write to "<path>.tmp"
* - rename to "<path>"
*/
Handle Store(Snapshot &&snap)
{
const Handle id = AllocateId_();
const std::string path = Path_(id);
const std::string tmp = path + ".tmp";
{
std::ofstream os(tmp, std::ios::binary | std::ios::trunc);
MFEM_VERIFY(os.is_open(), "FileCheckpointStorage: failed to open for write: " << tmp);
// Write payload
SnapshotIO::Write(os, snap);
MFEM_VERIFY(os.good(), "FileCheckpointStorage: write failed for: " << tmp);
}
// Rename tmp -> final
const int rc = std::rename(tmp.c_str(), path.c_str());
MFEM_VERIFY(rc == 0, "FileCheckpointStorage: rename failed: " << tmp << " -> " << path);
return id;
}
/**
* @brief Read snapshot from file and pass it to callback f(const Snapshot&).
*
* The reference passed to f is valid only during the call.
*/
template <typename Func>
void Read(const Handle &h, Func &&f) const
{
MFEM_VERIFY(IsValid(h), "FileCheckpointStorage: Read called with invalid handle.");
const std::string path = Path_(h);
std::ifstream is(path, std::ios::binary);
MFEM_VERIFY(is.is_open(), "FileCheckpointStorage: failed to open for read: " << path);
Snapshot snap = SnapshotIO::Read(is);
MFEM_VERIFY(is.good(), "FileCheckpointStorage: read failed for: " << path);
f(snap);
}
/**
* @brief Erase snapshot (delete file unless keep_files==true), set handle invalid.
*
* Id reuse policy:
* - if keep_files_ == false: deleted ids are reused to avoid creating huge numbers of files
* - if keep_files_ == true : ids are not reused (avoid overwriting old debug files)
*/
void Erase(Handle &h)
{
if (!IsValid(h)) { h = InvalidHandle(); return; }
if (!keep_files_)
{
const std::string path = Path_(h);
const int rc = std::remove(path.c_str());
MFEM_VERIFY(rc == 0, "FileCheckpointStorage: failed to remove file: " << path);
free_.push_back(h);
}
h = InvalidHandle();
}
private:
std::string dir_;
std::string prefix_;
std::string ext_;
bool keep_files_ = false;
mutable Handle next_id_ = 0;
mutable std::vector<Handle> free_;
Handle AllocateId_()
{
if (!keep_files_ && !free_.empty())
{
const Handle id = free_.back();
free_.pop_back();
return id;
}
return next_id_++;
}
std::string Path_(Handle id) const
{
MFEM_VERIFY(IsValid(id), "FileCheckpointStorage: Path_ called with invalid id.");
std::ostringstream oss;
oss << prefix_ << std::setw(12) << std::setfill('0') << id << ext_;
#if MFEM_HAVE_FILESYSTEM
mfem_fs::path p = mfem_fs::path(dir_) / mfem_fs::path(oss.str());
return p.string();
#else
// Fallback: simple concatenation; assumes dir_ ends without trailing slash if needed.
return dir_ + "/" + oss.str();
#endif
}
};
} // namespace mfem
#endif // MFEM_FILE_CHECKPOINT_STORAGE_HPP
@@ -1,385 +0,0 @@
#ifndef MFEM_FIXED_SLOT_CHECKPOINT_STORAGE_HPP
#define MFEM_FIXED_SLOT_CHECKPOINT_STORAGE_HPP
#include "mfem.hpp"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include <fstream>
#include <limits>
#include <type_traits>
namespace mfem
{
// ------------------------------------------------------------
// Packer 1: trivially-copyable snapshots (memcpy)
// ------------------------------------------------------------
template <typename Snapshot>
class TrivialFixedPacker
{
public:
TrivialFixedPacker()
{
static_assert(std::is_trivially_copyable<Snapshot>::value,
"TrivialFixedPacker requires Snapshot to be trivially copyable.");
}
std::size_t SlotBytes() const { return sizeof(Snapshot); }
void Pack(const Snapshot &snap, void *dst) const
{
std::memcpy(dst, &snap, sizeof(Snapshot));
}
void Unpack(const void *src, Snapshot &out) const
{
std::memcpy(&out, src, sizeof(Snapshot));
}
};
// ------------------------------------------------------------
// Packer 2: mfem::Vector of fixed length n
// (Serialized size is fixed: n*sizeof(real_t))
// ------------------------------------------------------------
class FixedVectorPacker
{
public:
explicit FixedVectorPacker(int n) : n_(n)
{
MFEM_VERIFY(n_ > 0, "FixedVectorPacker: n must be > 0.");
}
std::size_t SlotBytes() const
{
return (std::size_t)n_ * sizeof(mfem::real_t);
}
void Pack(const mfem::Vector &v, void *dst) const
{
MFEM_VERIFY(v.Size() == n_, "FixedVectorPacker: vector size mismatch.");
std::memcpy(dst, v.GetData(), SlotBytes());
}
void Unpack(const void *src, mfem::Vector &out) const
{
out.SetSize(n_);
std::memcpy(out.GetData(), src, SlotBytes());
}
int Size() const { return n_; }
private:
int n_ = 0;
};
// ------------------------------------------------------------
// Fixed-slot MEMORY storage: one big RAM block
// ------------------------------------------------------------
template <typename Snapshot, typename Packer = TrivialFixedPacker<Snapshot>>
class FixedSlotMemoryCheckpointStorage
{
public:
using Handle = int;
FixedSlotMemoryCheckpointStorage(int max_slots, const Packer &packer = Packer())
: max_slots_(max_slots), packer_(packer)
{
MFEM_VERIFY(max_slots_ > 0, "FixedSlotMemoryCheckpointStorage: max_slots must be > 0.");
slot_bytes_ = packer_.SlotBytes();
MFEM_VERIFY(slot_bytes_ > 0, "FixedSlotMemoryCheckpointStorage: SlotBytes must be > 0.");
// Single contiguous block
bytes_.resize((std::size_t)max_slots_ * slot_bytes_);
in_use_.assign(max_slots_, 0);
free_.reserve(max_slots_);
for (int i = 0; i < max_slots_; ++i) { free_.push_back(i); }
}
Handle InvalidHandle() const { return -1; }
bool IsValid(const Handle &h) const { return h >= 0; }
int MaxSlots() const { return max_slots_; }
std::size_t SlotBytes() const { return slot_bytes_; }
Handle Store(Snapshot &&snap)
{
MFEM_VERIFY(!free_.empty(),
"FixedSlotMemoryCheckpointStorage: out of slots (increase max_slots).");
const int slot = free_.back();
free_.pop_back();
in_use_[slot] = 1;
void *dst = SlotPtr_(slot);
packer_.Pack(snap, dst);
return slot;
}
template <typename Func>
void Read(const Handle &h, Func &&f) const
{
MFEM_VERIFY(IsValid(h), "FixedSlotMemoryCheckpointStorage: Read invalid handle.");
MFEM_VERIFY(h < max_slots_, "FixedSlotMemoryCheckpointStorage: Read handle out of range.");
MFEM_VERIFY(in_use_[h] == 1, "FixedSlotMemoryCheckpointStorage: Read from free slot.");
Snapshot tmp;
const void *src = SlotPtrConst_(h);
packer_.Unpack(src, tmp);
f(tmp);
}
void Erase(Handle &h)
{
if (!IsValid(h)) { h = InvalidHandle(); return; }
MFEM_VERIFY(h < max_slots_, "FixedSlotMemoryCheckpointStorage: Erase handle out of range.");
MFEM_VERIFY(in_use_[h] == 1, "FixedSlotMemoryCheckpointStorage: double-free / invalid erase.");
in_use_[h] = 0;
free_.push_back(h);
h = InvalidHandle();
}
/// Optional: return all slots to the free list (does not zero memory).
void Reset()
{
free_.clear();
for (int i = 0; i < max_slots_; ++i) { in_use_[i] = 0; free_.push_back(i); }
}
private:
int max_slots_ = 0;
std::size_t slot_bytes_ = 0;
Packer packer_;
std::vector<unsigned char> bytes_; // single block
std::vector<unsigned char> in_use_;
std::vector<int> free_;
void *SlotPtr_(int slot)
{
return (void*)(&bytes_[(std::size_t)slot * slot_bytes_]);
}
const void *SlotPtrConst_(int slot) const
{
return (const void*)(&bytes_[(std::size_t)slot * slot_bytes_]);
}
};
// ------------------------------------------------------------
// Fixed-slot FILE storage: one single pre-sized file
// ------------------------------------------------------------
template <typename Snapshot, typename Packer = TrivialFixedPacker<Snapshot>>
class FixedSlotFileCheckpointStorage
{
public:
using Handle = int;
struct Header
{
char magic[8]; // "MFCKPTFS"
std::uint64_t version; // 1
std::uint64_t slot_bytes;
std::uint64_t max_slots;
std::uint64_t reserved[4]; // future use / padding
};
FixedSlotFileCheckpointStorage(const std::string &path,
int max_slots,
const Packer &packer = Packer(),
bool truncate = true,
bool flush_on_store = false)
: path_(path),
max_slots_(max_slots),
packer_(packer),
flush_on_store_(flush_on_store)
{
MFEM_VERIFY(!path_.empty(), "FixedSlotFileCheckpointStorage: empty file path.");
MFEM_VERIFY(max_slots_ > 0, "FixedSlotFileCheckpointStorage: max_slots must be > 0.");
slot_bytes_ = packer_.SlotBytes();
MFEM_VERIFY(slot_bytes_ > 0, "FixedSlotFileCheckpointStorage: SlotBytes must be > 0.");
Open_(truncate);
in_use_.assign(max_slots_, 0);
free_.reserve(max_slots_);
for (int i = 0; i < max_slots_; ++i) { free_.push_back(i); }
scratch_.resize(slot_bytes_);
}
~FixedSlotFileCheckpointStorage()
{
if (file_.is_open()) { file_.close(); }
}
Handle InvalidHandle() const { return -1; }
bool IsValid(const Handle &h) const { return h >= 0; }
int MaxSlots() const { return max_slots_; }
std::size_t SlotBytes() const { return slot_bytes_; }
const std::string &Path() const { return path_; }
Handle Store(Snapshot &&snap)
{
MFEM_VERIFY(!free_.empty(),
"FixedSlotFileCheckpointStorage: out of slots (increase max_slots).");
const int slot = free_.back();
free_.pop_back();
in_use_[slot] = 1;
// Pack into scratch buffer then write into fixed slot offset
packer_.Pack(snap, scratch_.data());
const std::uint64_t off = SlotOffset_(slot);
file_.seekp((std::streamoff)off, std::ios::beg);
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: seekp failed.");
file_.write(reinterpret_cast<const char*>(scratch_.data()),
(std::streamsize)slot_bytes_);
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: write failed.");
if (flush_on_store_) { file_.flush(); }
return slot;
}
template <typename Func>
void Read(const Handle &h, Func &&f) const
{
MFEM_VERIFY(IsValid(h), "FixedSlotFileCheckpointStorage: Read invalid handle.");
MFEM_VERIFY(h < max_slots_, "FixedSlotFileCheckpointStorage: Read handle out of range.");
MFEM_VERIFY(in_use_[h] == 1, "FixedSlotFileCheckpointStorage: Read from free slot.");
const std::uint64_t off = SlotOffset_(h);
file_.seekg((std::streamoff)off, std::ios::beg);
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: seekg failed.");
file_.read(reinterpret_cast<char*>(scratch_.data()),
(std::streamsize)slot_bytes_);
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: read failed.");
Snapshot tmp;
packer_.Unpack(scratch_.data(), tmp);
f(tmp);
}
void Erase(Handle &h)
{
if (!IsValid(h)) { h = InvalidHandle(); return; }
MFEM_VERIFY(h < max_slots_, "FixedSlotFileCheckpointStorage: Erase handle out of range.");
MFEM_VERIFY(in_use_[h] == 1, "FixedSlotFileCheckpointStorage: double-free / invalid erase.");
// No file deletion; just return slot to free list.
in_use_[h] = 0;
free_.push_back(h);
h = InvalidHandle();
}
/// Optional: return all slots to free list (file contents remain).
void Reset()
{
free_.clear();
for (int i = 0; i < max_slots_; ++i) { in_use_[i] = 0; free_.push_back(i); }
}
private:
std::string path_;
int max_slots_ = 0;
std::size_t slot_bytes_ = 0;
Packer packer_;
bool flush_on_store_ = false;
// mutable because Read() is const but needs to seek/read
mutable std::fstream file_;
mutable std::vector<unsigned char> scratch_;
std::vector<unsigned char> in_use_;
std::vector<int> free_;
static Header MakeHeader_(std::uint64_t slot_bytes, std::uint64_t max_slots)
{
Header h;
std::memset(&h, 0, sizeof(h));
h.magic[0] = 'M'; h.magic[1] = 'F'; h.magic[2] = 'C'; h.magic[3] = 'K';
h.magic[4] = 'P'; h.magic[5] = 'T'; h.magic[6] = 'F'; h.magic[7] = 'S';
h.version = 1;
h.slot_bytes = slot_bytes;
h.max_slots = max_slots;
return h;
}
void Open_(bool truncate)
{
const std::ios::openmode mode =
std::ios::binary | std::ios::in | std::ios::out | (truncate ? std::ios::trunc : (std::ios::openmode)0);
file_.open(path_.c_str(), mode);
MFEM_VERIFY(file_.is_open(), "FixedSlotFileCheckpointStorage: failed to open file.");
const Header expected = MakeHeader_((std::uint64_t)slot_bytes_, (std::uint64_t)max_slots_);
if (truncate)
{
// Write header
file_.seekp(0, std::ios::beg);
file_.write(reinterpret_cast<const char*>(&expected), sizeof(expected));
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: header write failed.");
// Pre-size file to: header + max_slots*slot_bytes
const std::uint64_t total = (std::uint64_t)sizeof(Header)
+ (std::uint64_t)max_slots_ * (std::uint64_t)slot_bytes_;
MFEM_VERIFY(total > 0, "FixedSlotFileCheckpointStorage: invalid total file size.");
file_.seekp((std::streamoff)(total - 1), std::ios::beg);
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: seekp for resize failed.");
const char zero = 0;
file_.write(&zero, 1);
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: resize write failed.");
file_.flush();
}
else
{
// Validate existing header
Header got;
file_.seekg(0, std::ios::beg);
file_.read(reinterpret_cast<char*>(&got), sizeof(got));
MFEM_VERIFY(file_.good(), "FixedSlotFileCheckpointStorage: header read failed.");
MFEM_VERIFY(std::memcmp(got.magic, expected.magic, 8) == 0,
"FixedSlotFileCheckpointStorage: magic mismatch.");
MFEM_VERIFY(got.version == expected.version,
"FixedSlotFileCheckpointStorage: version mismatch.");
MFEM_VERIFY(got.slot_bytes == expected.slot_bytes,
"FixedSlotFileCheckpointStorage: slot_bytes mismatch.");
MFEM_VERIFY(got.max_slots == expected.max_slots,
"FixedSlotFileCheckpointStorage: max_slots mismatch.");
}
}
std::uint64_t SlotOffset_(int slot) const
{
return (std::uint64_t)sizeof(Header) + (std::uint64_t)slot * (std::uint64_t)slot_bytes_;
}
};
} // namespace mfem
#endif // MFEM_FIXED_SLOT_CHECKPOINT_STORAGE_HPP
-694
View File
@@ -1,694 +0,0 @@
#include "mfem.hpp"
#include <cstdint>
#include <cstring>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using mfem::out;
// -------------------------------
// Fixed-slot storage: Memory
// -------------------------------
class FixedSlotMemoryStorage
{
private:
int max_slots_ = 0;
size_t slot_bytes_ = 0;
std::vector<uint8_t> data_;
public:
FixedSlotMemoryStorage(int max_slots, size_t slot_bytes)
: max_slots_(max_slots), slot_bytes_(slot_bytes),
data_(size_t(max_slots)*slot_bytes, 0)
{
MFEM_VERIFY(max_slots_ > 0, "max_slots must be > 0");
MFEM_VERIFY(slot_bytes_ > 0, "slot_bytes must be > 0");
}
int MaxSlots() const { return max_slots_; }
size_t SlotBytes() const { return slot_bytes_; }
void Save(int slot, const uint8_t *src, size_t bytes)
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Save: slot out of range");
MFEM_VERIFY(bytes == slot_bytes_, "Save: bytes mismatch");
MFEM_VERIFY(src != nullptr, "Save: src is null");
std::memcpy(data_.data() + size_t(slot)*slot_bytes_, src, slot_bytes_);
}
void Load(int slot, uint8_t *dst, size_t bytes) const
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Load: slot out of range");
MFEM_VERIFY(bytes == slot_bytes_, "Load: bytes mismatch");
MFEM_VERIFY(dst != nullptr, "Load: dst is null");
std::memcpy(dst, data_.data() + size_t(slot)*slot_bytes_, slot_bytes_);
}
};
// -------------------------------
// Fixed-slot storage: Single file
// -------------------------------
class FixedSlotFileStorage
{
private:
int max_slots_ = 0;
size_t slot_bytes_ = 0;
std::string filename_;
mutable std::fstream file_;
std::streamoff Offset(int slot) const
{
return std::streamoff(size_t(slot) * slot_bytes_);
}
public:
FixedSlotFileStorage(const std::string &filename,
int max_slots,
size_t slot_bytes)
: max_slots_(max_slots), slot_bytes_(slot_bytes), filename_(filename)
{
MFEM_VERIFY(max_slots_ > 0, "max_slots must be > 0");
MFEM_VERIFY(slot_bytes_ > 0, "slot_bytes must be > 0");
MFEM_VERIFY(!filename_.empty(), "filename must not be empty");
// Create/truncate file and size it.
{
std::ofstream ofs(filename_, std::ios::binary | std::ios::trunc);
MFEM_VERIFY(ofs.good(), "Failed to create checkpoint file");
const size_t total = size_t(max_slots_) * slot_bytes_;
if (total > 0)
{
ofs.seekp(std::streamoff(total - 1));
char zero = 0;
ofs.write(&zero, 1);
}
}
file_.open(filename_, std::ios::binary | std::ios::in | std::ios::out);
MFEM_VERIFY(file_.good(), "Failed to open checkpoint file");
}
~FixedSlotFileStorage()
{
if (file_.is_open()) { file_.close(); }
}
int MaxSlots() const { return max_slots_; }
size_t SlotBytes() const { return slot_bytes_; }
void Save(int slot, const uint8_t *src, size_t bytes)
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Save: slot out of range");
MFEM_VERIFY(bytes == slot_bytes_, "Save: bytes mismatch");
MFEM_VERIFY(src != nullptr, "Save: src is null");
file_.seekp(Offset(slot));
MFEM_VERIFY(file_.good(), "Save: seekp failed");
file_.write(reinterpret_cast<const char*>(src), std::streamsize(slot_bytes_));
MFEM_VERIFY(file_.good(), "Save: write failed");
file_.flush();
}
void Load(int slot, uint8_t *dst, size_t bytes) const
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Load: slot out of range");
MFEM_VERIFY(bytes == slot_bytes_, "Load: bytes mismatch");
MFEM_VERIFY(dst != nullptr, "Load: dst is null");
file_.seekg(Offset(slot));
MFEM_VERIFY(file_.good(), "Load: seekg failed");
file_.read(reinterpret_cast<char*>(dst), std::streamsize(slot_bytes_));
MFEM_VERIFY(file_.good(), "Load: read failed");
}
};
// -------------------------------
// REVOLVE controller (actions)
// (Transcribed from revolve.c used in ADOL-C; Algorithm 799.)
// -------------------------------
enum class RevolveAction
{
advance,
takeshot,
restore,
firsturn,
youturn,
terminate
};
class RevolveController
{
private:
int snaps_ = 0;
int check_ = -1;
int capo_ = 0;
int fine_ = 0;
int turn_ = 0;
int oldfine_ = 0;
int oldsnaps_ = 0;
// Stack of checkpoint times, indexed by 'check_'.
std::vector<int> ch_;
public:
RevolveController() = default;
RevolveController(int snaps, int capo0, int fine0)
: snaps_(snaps), check_(-1), capo_(capo0), fine_(fine0),
turn_(0), oldfine_(fine0), oldsnaps_(snaps), ch_(snaps, 0)
{
MFEM_VERIFY(snaps_ > 0, "REVOLVE snaps must be > 0");
MFEM_VERIFY(capo_ <= fine_, "REVOLVE: capo must be <= fine");
// Match revolve.c initialization behavior.
if (check_ == -1 && capo_ < fine_)
{
turn_ = 0;
ch_[0] = capo_ - 1;
}
}
int Snaps() const { return snaps_; }
int Check() const { return check_; }
int Capo() const { return capo_; }
int Fine() const { return fine_; }
const std::vector<int>& CheckpointTimes() const { return ch_; }
RevolveAction Next()
{
MFEM_VERIFY(!(check_ < -1), "REVOLVE: check < -1");
MFEM_VERIFY(!(capo_ > fine_), "REVOLVE: capo > fine");
if ((check_ == -1) && (capo_ < fine_))
{
turn_ = 0;
ch_[0] = capo_ - 1;
}
const int diff = fine_ - capo_;
switch (diff)
{
case 0:
{
// Terminate or restore to next checkpoint on the stack.
if (check_ == -1 || capo_ == ch_[0])
{
check_ -= 1; // mirror revolve.c behavior
return RevolveAction::terminate;
}
else
{
capo_ = ch_[check_];
oldfine_ = fine_;
return RevolveAction::restore;
}
}
case 1:
{
// One adjoint step available.
fine_ -= 1;
if (check_ >= 0 && ch_[check_] == capo_) { check_ -= 1; }
if (turn_ == 0)
{
turn_ = 1;
oldfine_ = fine_;
return RevolveAction::firsturn;
}
else
{
oldfine_ = fine_;
return RevolveAction::youturn;
}
}
default:
{
// diff > 1
if (check_ == -1 || ch_[check_] != capo_)
{
// Take a new checkpoint at current capo.
check_ += 1;
MFEM_VERIFY(check_ + 1 <= snaps_, "REVOLVE: exceeded snaps");
ch_[check_] = capo_;
oldfine_ = fine_;
return RevolveAction::takeshot;
}
else
{
// Advance capo forward within (capo, fine).
// This follows the binomial logic in revolve.c.
MFEM_VERIFY(!((oldfine_ < fine_) && (snaps_ == check_ + 1)),
"REVOLVE: fine increased unexpectedly with full stack");
const int oldcapo = capo_;
const int ds = snaps_ - check_;
MFEM_VERIFY(ds >= 1, "REVOLVE: ds < 1");
int reps = 0;
long long range = 1;
while (range < (fine_ - capo_))
{
reps += 1;
// range = range*(reps+ds)/reps (integer arithmetic)
range = range * (reps + ds) / reps;
}
MFEM_VERIFY(reps >= 1, "REVOLVE: reps < 1");
// Binomial helper values (integer)
const long long bino1 = range * reps / (ds + reps);
const long long bino2 = (ds > 1) ? (bino1 * ds / (ds + reps - 1)) : 1;
const long long bino3 =
(ds == 1) ? 0 :
(ds > 2) ? (bino2 * (ds - 1) / (ds + reps - 2)) : 1;
const long long bino4 = bino2 * (reps - 1) / ds;
const long long bino5 =
(ds < 3) ? 0 :
(ds > 3) ? (bino3 * (ds - 2) / reps) : 1;
// Kowarz "new version": keep l^ as small as possible
const long long bino6 = bino1 * ds / reps;
const long long gap = fine_ - capo_;
if (gap <= bino1 + bino3)
{
capo_ += int(bino4);
}
else if (gap < bino1 + bino2)
{
capo_ = fine_ - int(bino2 + bino3);
}
else if (gap <= bino1 + bino2 + bino5)
{
capo_ += int(bino1 - bino3);
}
else
{
capo_ = fine_ - int(bino6);
}
if (capo_ == oldcapo) { capo_ = oldcapo + 1; }
oldfine_ = fine_;
return RevolveAction::advance;
}
}
}
}
};
// -------------------------------
// Fixed-step REVOLVE checkpointing
// -------------------------------
template <typename StorageT>
class FixedStepRevolveCheckpointing
{
public:
struct Shot
{
int time = 0; // state index
int slot = 0; // checkpoint slot index
};
private:
int num_steps_ = 0; // total number of primal steps (0..num_steps)
int num_checkpoints_ = 0; // REVOLVE "snaps"
size_t snapshot_bytes_ = 0;
StorageT *storage_ = nullptr;
std::vector<Shot> forward_shots_;
int forward_shot_cursor_ = 0;
// Controller state at the beginning of the reverse sweep (pre-firsturn).
RevolveController ctrl_init_;
RevolveController ctrl_;
// Two scratch buffers for (de)serialization.
std::vector<uint8_t> io_buf_;
std::vector<uint8_t> prefinal_buf_;
bool prefinal_valid_ = false;
// Reverse sweep bookkeeping.
bool reverse_started_ = false;
int u_work_time_ = -1;
public:
FixedStepRevolveCheckpointing(int num_steps,
int num_checkpoints,
size_t snapshot_bytes,
StorageT &storage)
: num_steps_(num_steps),
num_checkpoints_(num_checkpoints),
snapshot_bytes_(snapshot_bytes),
storage_(&storage),
io_buf_(snapshot_bytes, 0),
prefinal_buf_(snapshot_bytes, 0)
{
MFEM_VERIFY(num_steps_ >= 0, "num_steps must be >= 0");
MFEM_VERIFY(num_checkpoints_ > 0, "num_checkpoints must be > 0");
MFEM_VERIFY(snapshot_bytes_ > 0, "snapshot_bytes must be > 0");
MFEM_VERIFY(storage_->MaxSlots() == num_checkpoints_,
"Storage MaxSlots() must match num_checkpoints");
MFEM_VERIFY(storage_->SlotBytes() == snapshot_bytes_,
"Storage SlotBytes() must match snapshot_bytes");
BuildForwardPlanAndInitialControllerState();
Reset();
}
void Reset()
{
forward_shot_cursor_ = 0;
prefinal_valid_ = false;
reverse_started_ = false;
u_work_time_ = -1;
ctrl_ = ctrl_init_;
}
int NumSteps() const { return num_steps_; }
int NumCheckpoints() const { return num_checkpoints_; }
size_t SnapshotBytes() const { return snapshot_bytes_; }
// ForwardStep: called for i=0..num_steps-1
template <typename State, typename PrimalStep, typename MakeSnapshot>
void ForwardStep(int i,
State &u,
PrimalStep &&primal_step,
MakeSnapshot &&make_snapshot)
{
MFEM_VERIFY(0 <= i && i < num_steps_, "ForwardStep: i out of range");
// Take any planned shots at time i (before advancing).
while (forward_shot_cursor_ < (int)forward_shots_.size() &&
forward_shots_[forward_shot_cursor_].time == i)
{
const int slot = forward_shots_[forward_shot_cursor_].slot;
make_snapshot(u, io_buf_.data(), snapshot_bytes_);
storage_->Save(slot, io_buf_.data(), snapshot_bytes_);
forward_shot_cursor_++;
}
// Cache u_{num_steps-1} so reverse can start there (REVOLVE expects capo=num_steps-1).
if (i == num_steps_ - 1)
{
make_snapshot(u, prefinal_buf_.data(), snapshot_bytes_);
prefinal_valid_ = true;
}
// Advance one step.
primal_step(i, u);
}
// BackwardStep: called for i=num_steps-1..0
template <typename State, typename AdjointState,
typename PrimalStep, typename AdjointStep,
typename MakeSnapshot, typename RestoreSnapshot>
void BackwardStep(int i,
AdjointState &lambda,
State &u_work,
PrimalStep &&primal_step,
AdjointStep &&adjoint_step,
MakeSnapshot &&make_snapshot,
RestoreSnapshot &&restore_snapshot)
{
MFEM_VERIFY(0 <= i && i < num_steps_, "BackwardStep: i out of range");
if (!reverse_started_)
{
MFEM_VERIFY(prefinal_valid_ || num_steps_ == 0,
"Reverse started but prefinal state was not captured. "
"Did you run the forward loop through i=num_steps-1?");
ctrl_ = ctrl_init_;
reverse_started_ = true;
if (num_steps_ > 0)
{
// Restore u_{num_steps-1} into u_work.
restore_snapshot(u_work, prefinal_buf_.data(), snapshot_bytes_);
u_work_time_ = num_steps_ - 1;
}
}
// Execute controller actions until we perform exactly one adjoint step.
for (;;)
{
RevolveAction act = ctrl_.Next();
switch (act)
{
case RevolveAction::takeshot:
{
const int slot = ctrl_.Check();
MFEM_VERIFY(u_work_time_ == ctrl_.Capo(),
"takeshot: u_work_time must equal capo");
make_snapshot(u_work, io_buf_.data(), snapshot_bytes_);
storage_->Save(slot, io_buf_.data(), snapshot_bytes_);
break;
}
case RevolveAction::restore:
{
const int slot = ctrl_.Check();
storage_->Load(slot, io_buf_.data(), snapshot_bytes_);
restore_snapshot(u_work, io_buf_.data(), snapshot_bytes_);
u_work_time_ = ctrl_.Capo();
break;
}
case RevolveAction::advance:
{
const int target = ctrl_.Capo();
MFEM_VERIFY(u_work_time_ >= 0, "advance: u_work_time not initialized");
MFEM_VERIFY(target >= u_work_time_, "advance: target < current time");
for (int t = u_work_time_; t < target; ++t)
{
primal_step(t, u_work);
}
u_work_time_ = target;
break;
}
case RevolveAction::firsturn:
case RevolveAction::youturn:
{
// After firsturn/youturn, ctrl_.Fine() has been decremented and equals ctrl_.Capo().
const int step = ctrl_.Fine();
MFEM_VERIFY(step == ctrl_.Capo(), "youturn: fine != capo");
MFEM_VERIFY(step == u_work_time_, "youturn: u_work_time != step");
MFEM_VERIFY(step == i, "BackwardStep called with i that doesn't match REVOLVE schedule");
// One adjoint step.
adjoint_step(step, u_work, lambda);
return;
}
case RevolveAction::terminate:
{
MFEM_ABORT("REVOLVE terminated early: BackwardStep called after completion?");
break;
}
}
}
}
private:
void BuildForwardPlanAndInitialControllerState()
{
forward_shots_.clear();
RevolveController sim(num_checkpoints_, /*capo=*/0, /*fine=*/num_steps_);
// Forward-plan phase: only TAKESHOT/ADVANCE should occur while fine-capo>1.
while (sim.Fine() - sim.Capo() > 1)
{
RevolveAction a = sim.Next();
if (a == RevolveAction::takeshot)
{
forward_shots_.push_back({sim.Capo(), sim.Check()});
}
else if (a == RevolveAction::advance)
{
// nothing to record; capo moved forward inside sim
}
else
{
MFEM_ABORT("Unexpected REVOLVE action during forward planning phase");
}
}
// At this point, sim is in the pre-firsturn state (capo = num_steps-1, fine = num_steps)
// for num_steps>=1. For num_steps<=1, fine-capo<=1 from the start.
ctrl_init_ = sim;
}
};
// ============================================================
// Mini example 1: scalar state (double)
// u_{n+1} = factor * u_n
// J = 0.5 (u_N - target)^2 => lambda_N = (u_N - target)
// lambda_n = factor * lambda_{n+1}
// ============================================================
static void RunScalarExample(int Nsteps, int Ncheck, double factor)
{
out << "\n--- Scalar REVOLVE example ---\n";
out << "Nsteps=" << Nsteps << ", Ncheckpoints=" << Ncheck << ", factor=" << factor << "\n";
const size_t snap_bytes = sizeof(double);
FixedSlotMemoryStorage storage(Ncheck, snap_bytes);
FixedStepRevolveCheckpointing<FixedSlotMemoryStorage> ckpt(Nsteps, Ncheck, snap_bytes, storage);
auto make_snapshot = [](const double &u, uint8_t *outb, size_t bytes)
{
MFEM_VERIFY(bytes == sizeof(double), "scalar snapshot size mismatch");
std::memcpy(outb, &u, sizeof(double));
};
auto restore_snapshot = [](double &u, const uint8_t *inb, size_t bytes)
{
MFEM_VERIFY(bytes == sizeof(double), "scalar snapshot size mismatch");
std::memcpy(&u, inb, sizeof(double));
};
auto primal_step = [factor](int /*step*/, double &u)
{
u *= factor;
};
auto adjoint_step = [factor](int /*step*/, const double & /*u_step*/, double &lambda)
{
lambda *= factor;
};
const double target = 2.0;
// Forward
double u = 1.0;
for (int i = 0; i < Nsteps; ++i)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
}
const double uN = u;
// Init adjoint at final state
double lambda = (uN - target);
// Reverse
double u_work = 0.0; // will be overwritten by restore_snapshot
for (int i = Nsteps - 1; i >= 0; --i)
{
ckpt.BackwardStep(i, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
}
// Analytic lambda_0 = factor^Nsteps * (uN - target)
double factorN = 1.0;
for (int k = 0; k < Nsteps; ++k) { factorN *= factor; }
const double lambda0_exact = factorN * (uN - target);
out << "uN = " << uN << "\n";
out << "lambda0 (computed) = " << lambda << "\n";
out << "lambda0 (exact) = " << lambda0_exact << "\n";
}
// ============================================================
// Mini example 2: mfem::Vector state
// u_{n+1} = factor * u_n (elementwise scalar multiply)
// J = 0.5 ||u_N - target||^2 => lambda_N = (u_N - target)
// lambda_n = factor * lambda_{n+1}
// ============================================================
static void RunVectorExample(int Nsteps, int Ncheck, int dim, double factor)
{
out << "\n--- mfem::Vector REVOLVE example ---\n";
out << "Nsteps=" << Nsteps << ", Ncheckpoints=" << Ncheck
<< ", dim=" << dim << ", factor=" << factor << "\n";
MFEM_VERIFY(dim > 0, "dim must be > 0");
const size_t snap_bytes = sizeof(double) * size_t(dim);
FixedSlotMemoryStorage storage(Ncheck, snap_bytes);
FixedStepRevolveCheckpointing<FixedSlotMemoryStorage> ckpt(Nsteps, Ncheck, snap_bytes, storage);
auto make_snapshot = [](const mfem::Vector &u, uint8_t *outb, size_t bytes)
{
MFEM_VERIFY(bytes == sizeof(double) * size_t(u.Size()), "Vector snapshot size mismatch");
std::memcpy(outb, u.GetData(), bytes);
};
auto restore_snapshot = [](mfem::Vector &u, const uint8_t *inb, size_t bytes)
{
MFEM_VERIFY(bytes == sizeof(double) * size_t(u.Size()), "Vector snapshot size mismatch");
std::memcpy(u.GetData(), inb, bytes);
};
auto primal_step = [factor](int /*step*/, mfem::Vector &u)
{
u *= factor;
};
auto adjoint_step = [factor](int /*step*/, const mfem::Vector & /*u_step*/, mfem::Vector &lambda)
{
lambda *= factor;
};
mfem::Vector u(dim);
for (int k = 0; k < dim; ++k) { u[k] = 1.0 + 0.1*k; }
mfem::Vector target(dim);
target = 2.0;
// Forward
for (int i = 0; i < Nsteps; ++i)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
}
mfem::Vector uN(u); // copy for reporting
// Init adjoint: lambda_N = uN - target
mfem::Vector lambda(dim);
lambda = u;
lambda -= target;
// Reverse
mfem::Vector u_work(dim);
u_work = 0.0;
for (int i = Nsteps - 1; i >= 0; --i)
{
ckpt.BackwardStep(i, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
}
out << "||uN||_2 = " << uN.Norml2() << "\n";
out << "||lambda0||_2 = " << lambda.Norml2() << "\n";
}
int main(int argc, char *argv[])
{
mfem::Device device("cpu");
device.Print();
int Nsteps = 20;
int Ncheck = 3;
int dim = 5;
double factor = 1.05;
mfem::OptionsParser args(argc, argv);
args.AddOption(&Nsteps, "-n", "--num-steps", "Number of primal steps.");
args.AddOption(&Ncheck, "-s", "--num-checkpoints", "Number of checkpoints (snaps).");
args.AddOption(&dim, "-d", "--dim", "Vector dimension for the mfem::Vector example.");
args.AddOption(&factor, "-f", "--factor", "Scalar factor in the toy update u_{n+1}=f*u_n.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(out);
return 1;
}
args.PrintOptions(out);
RunScalarExample(Nsteps, Ncheck, factor);
RunVectorExample(Nsteps, Ncheck, dim, factor);
return 0;
}
@@ -1,535 +0,0 @@
#ifndef MFEM_REVOLVE_CHECKPOINTING_HPP
#define MFEM_REVOLVE_CHECKPOINTING_HPP
#include "mfem.hpp"
#include <cstdint>
#include <cstring>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using mfem::out;
// -------------------------------
// Fixed-slot storage: Memory
// -------------------------------
class FixedSlotMemoryStorage
{
private:
int max_slots_ = 0;
size_t slot_bytes_ = 0;
std::vector<uint8_t> data_;
public:
FixedSlotMemoryStorage(int max_slots, size_t slot_bytes)
: max_slots_(max_slots), slot_bytes_(slot_bytes),
data_(size_t(max_slots)*slot_bytes, 0)
{
MFEM_VERIFY(max_slots_ > 0, "max_slots must be > 0");
MFEM_VERIFY(slot_bytes_ > 0, "slot_bytes must be > 0");
}
int MaxSlots() const { return max_slots_; }
size_t SlotBytes() const { return slot_bytes_; }
void Save(int slot, const uint8_t *src, size_t bytes)
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Save: slot out of range");
MFEM_VERIFY(bytes == slot_bytes_, "Save: bytes mismatch");
MFEM_VERIFY(src != nullptr, "Save: src is null");
std::memcpy(data_.data() + size_t(slot)*slot_bytes_, src, slot_bytes_);
}
void Load(int slot, uint8_t *dst, size_t bytes) const
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Load: slot out of range");
MFEM_VERIFY(bytes == slot_bytes_, "Load: bytes mismatch");
MFEM_VERIFY(dst != nullptr, "Load: dst is null");
std::memcpy(dst, data_.data() + size_t(slot)*slot_bytes_, slot_bytes_);
}
};
// -------------------------------
// Fixed-slot storage: Single file
// -------------------------------
class FixedSlotFileStorage
{
private:
int max_slots_ = 0;
size_t slot_bytes_ = 0;
std::string filename_;
mutable std::fstream file_;
std::streamoff Offset(int slot) const
{
return std::streamoff(size_t(slot) * slot_bytes_);
}
public:
FixedSlotFileStorage(const std::string &filename,
int max_slots,
size_t slot_bytes)
: max_slots_(max_slots), slot_bytes_(slot_bytes), filename_(filename)
{
MFEM_VERIFY(max_slots_ > 0, "max_slots must be > 0");
MFEM_VERIFY(slot_bytes_ > 0, "slot_bytes must be > 0");
MFEM_VERIFY(!filename_.empty(), "filename must not be empty");
// Create/truncate file and size it.
{
std::ofstream ofs(filename_, std::ios::binary | std::ios::trunc);
MFEM_VERIFY(ofs.good(), "Failed to create checkpoint file");
const size_t total = size_t(max_slots_) * slot_bytes_;
if (total > 0)
{
ofs.seekp(std::streamoff(total - 1));
char zero = 0;
ofs.write(&zero, 1);
}
}
file_.open(filename_, std::ios::binary | std::ios::in | std::ios::out);
MFEM_VERIFY(file_.good(), "Failed to open checkpoint file");
}
~FixedSlotFileStorage()
{
if (file_.is_open()) { file_.close(); }
}
int MaxSlots() const { return max_slots_; }
size_t SlotBytes() const { return slot_bytes_; }
void Save(int slot, const uint8_t *src, size_t bytes)
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Save: slot out of range");
MFEM_VERIFY(bytes == slot_bytes_, "Save: bytes mismatch");
MFEM_VERIFY(src != nullptr, "Save: src is null");
file_.seekp(Offset(slot));
MFEM_VERIFY(file_.good(), "Save: seekp failed");
file_.write(reinterpret_cast<const char*>(src), std::streamsize(slot_bytes_));
MFEM_VERIFY(file_.good(), "Save: write failed");
file_.flush();
}
void Load(int slot, uint8_t *dst, size_t bytes) const
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Load: slot out of range");
MFEM_VERIFY(bytes == slot_bytes_, "Load: bytes mismatch");
MFEM_VERIFY(dst != nullptr, "Load: dst is null");
file_.seekg(Offset(slot));
MFEM_VERIFY(file_.good(), "Load: seekg failed");
file_.read(reinterpret_cast<char*>(dst), std::streamsize(slot_bytes_));
MFEM_VERIFY(file_.good(), "Load: read failed");
}
};
// -------------------------------
// REVOLVE controller (actions)
// (Transcribed from revolve.c used in ADOL-C; Algorithm 799.)
// -------------------------------
enum class RevolveAction
{
advance,
takeshot,
restore,
firsturn,
youturn,
terminate
};
class RevolveController
{
private:
int snaps_ = 0;
int check_ = -1;
int capo_ = 0;
int fine_ = 0;
int turn_ = 0;
int oldfine_ = 0;
int oldsnaps_ = 0;
// Stack of checkpoint times, indexed by 'check_'.
std::vector<int> ch_;
public:
RevolveController() = default;
RevolveController(int snaps, int capo0, int fine0)
: snaps_(snaps), check_(-1), capo_(capo0), fine_(fine0),
turn_(0), oldfine_(fine0), oldsnaps_(snaps), ch_(snaps, 0)
{
MFEM_VERIFY(snaps_ > 0, "REVOLVE snaps must be > 0");
MFEM_VERIFY(capo_ <= fine_, "REVOLVE: capo must be <= fine");
// Match revolve.c initialization behavior.
if (check_ == -1 && capo_ < fine_)
{
turn_ = 0;
ch_[0] = capo_ - 1;
}
}
int Snaps() const { return snaps_; }
int Check() const { return check_; }
int Capo() const { return capo_; }
int Fine() const { return fine_; }
const std::vector<int>& CheckpointTimes() const { return ch_; }
RevolveAction Next()
{
MFEM_VERIFY(!(check_ < -1), "REVOLVE: check < -1");
MFEM_VERIFY(!(capo_ > fine_), "REVOLVE: capo > fine");
if ((check_ == -1) && (capo_ < fine_))
{
turn_ = 0;
ch_[0] = capo_ - 1;
}
const int diff = fine_ - capo_;
switch (diff)
{
case 0:
{
// Terminate or restore to next checkpoint on the stack.
if (check_ == -1 || capo_ == ch_[0])
{
check_ -= 1; // mirror revolve.c behavior
return RevolveAction::terminate;
}
else
{
capo_ = ch_[check_];
oldfine_ = fine_;
return RevolveAction::restore;
}
}
case 1:
{
// One adjoint step available.
fine_ -= 1;
if (check_ >= 0 && ch_[check_] == capo_) { check_ -= 1; }
if (turn_ == 0)
{
turn_ = 1;
oldfine_ = fine_;
return RevolveAction::firsturn;
}
else
{
oldfine_ = fine_;
return RevolveAction::youturn;
}
}
default:
{
// diff > 1
if (check_ == -1 || ch_[check_] != capo_)
{
// Take a new checkpoint at current capo.
check_ += 1;
MFEM_VERIFY(check_ + 1 <= snaps_, "REVOLVE: exceeded snaps");
ch_[check_] = capo_;
oldfine_ = fine_;
return RevolveAction::takeshot;
}
else
{
// Advance capo forward within (capo, fine).
// This follows the binomial logic in revolve.c.
MFEM_VERIFY(!((oldfine_ < fine_) && (snaps_ == check_ + 1)),
"REVOLVE: fine increased unexpectedly with full stack");
const int oldcapo = capo_;
const int ds = snaps_ - check_;
MFEM_VERIFY(ds >= 1, "REVOLVE: ds < 1");
int reps = 0;
long long range = 1;
while (range < (fine_ - capo_))
{
reps += 1;
// range = range*(reps+ds)/reps (integer arithmetic)
range = range * (reps + ds) / reps;
}
MFEM_VERIFY(reps >= 1, "REVOLVE: reps < 1");
// Binomial helper values (integer)
const long long bino1 = range * reps / (ds + reps);
const long long bino2 = (ds > 1) ? (bino1 * ds / (ds + reps - 1)) : 1;
const long long bino3 =
(ds == 1) ? 0 :
(ds > 2) ? (bino2 * (ds - 1) / (ds + reps - 2)) : 1;
const long long bino4 = bino2 * (reps - 1) / ds;
const long long bino5 =
(ds < 3) ? 0 :
(ds > 3) ? (bino3 * (ds - 2) / reps) : 1;
// Kowarz "new version": keep l^ as small as possible
const long long bino6 = bino1 * ds / reps;
const long long gap = fine_ - capo_;
if (gap <= bino1 + bino3)
{
capo_ += int(bino4);
}
else if (gap < bino1 + bino2)
{
capo_ = fine_ - int(bino2 + bino3);
}
else if (gap <= bino1 + bino2 + bino5)
{
capo_ += int(bino1 - bino3);
}
else
{
capo_ = fine_ - int(bino6);
}
if (capo_ == oldcapo) { capo_ = oldcapo + 1; }
oldfine_ = fine_;
return RevolveAction::advance;
}
}
}
}
};
// -------------------------------
// Fixed-step REVOLVE checkpointing
// -------------------------------
template <typename StorageT>
class FixedStepRevolveCheckpointing
{
public:
struct Shot
{
int time = 0; // state index
int slot = 0; // checkpoint slot index
};
private:
int num_steps_ = 0; // total number of primal steps (0..num_steps)
int num_checkpoints_ = 0; // REVOLVE "snaps"
size_t snapshot_bytes_ = 0;
StorageT *storage_ = nullptr;
std::vector<Shot> forward_shots_;
int forward_shot_cursor_ = 0;
// Controller state at the beginning of the reverse sweep (pre-firsturn).
RevolveController ctrl_init_;
RevolveController ctrl_;
// Two scratch buffers for (de)serialization.
std::vector<uint8_t> io_buf_;
std::vector<uint8_t> prefinal_buf_;
bool prefinal_valid_ = false;
// Reverse sweep bookkeeping.
bool reverse_started_ = false;
int u_work_time_ = -1;
public:
FixedStepRevolveCheckpointing(int num_steps,
int num_checkpoints,
size_t snapshot_bytes,
StorageT &storage)
: num_steps_(num_steps),
num_checkpoints_(num_checkpoints),
snapshot_bytes_(snapshot_bytes),
storage_(&storage),
io_buf_(snapshot_bytes, 0),
prefinal_buf_(snapshot_bytes, 0)
{
MFEM_VERIFY(num_steps_ >= 0, "num_steps must be >= 0");
MFEM_VERIFY(num_checkpoints_ > 0, "num_checkpoints must be > 0");
MFEM_VERIFY(snapshot_bytes_ > 0, "snapshot_bytes must be > 0");
MFEM_VERIFY(storage_->MaxSlots() == num_checkpoints_,
"Storage MaxSlots() must match num_checkpoints");
MFEM_VERIFY(storage_->SlotBytes() == snapshot_bytes_,
"Storage SlotBytes() must match snapshot_bytes");
BuildForwardPlanAndInitialControllerState();
Reset();
}
void Reset()
{
forward_shot_cursor_ = 0;
prefinal_valid_ = false;
reverse_started_ = false;
u_work_time_ = -1;
ctrl_ = ctrl_init_;
}
int NumSteps() const { return num_steps_; }
int NumCheckpoints() const { return num_checkpoints_; }
size_t SnapshotBytes() const { return snapshot_bytes_; }
// ForwardStep: called for i=0..num_steps-1
template <typename State, typename PrimalStep, typename MakeSnapshot>
void ForwardStep(int i,
State &u,
PrimalStep &&primal_step,
MakeSnapshot &&make_snapshot)
{
MFEM_VERIFY(0 <= i && i < num_steps_, "ForwardStep: i out of range");
// Take any planned shots at time i (before advancing).
while (forward_shot_cursor_ < (int)forward_shots_.size() &&
forward_shots_[forward_shot_cursor_].time == i)
{
const int slot = forward_shots_[forward_shot_cursor_].slot;
make_snapshot(u, io_buf_.data(), snapshot_bytes_);
storage_->Save(slot, io_buf_.data(), snapshot_bytes_);
forward_shot_cursor_++;
}
// Cache u_{num_steps-1} so reverse can start there (REVOLVE expects capo=num_steps-1).
if (i == num_steps_ - 1)
{
make_snapshot(u, prefinal_buf_.data(), snapshot_bytes_);
prefinal_valid_ = true;
}
// Advance one step.
primal_step(i, u);
}
// BackwardStep: called for i=num_steps-1..0
template <typename State, typename AdjointState,
typename PrimalStep, typename AdjointStep,
typename MakeSnapshot, typename RestoreSnapshot>
void BackwardStep(int i,
AdjointState &lambda,
State &u_work,
PrimalStep &&primal_step,
AdjointStep &&adjoint_step,
MakeSnapshot &&make_snapshot,
RestoreSnapshot &&restore_snapshot)
{
MFEM_VERIFY(0 <= i && i < num_steps_, "BackwardStep: i out of range");
if (!reverse_started_)
{
MFEM_VERIFY(prefinal_valid_ || num_steps_ == 0,
"Reverse started but prefinal state was not captured. "
"Did you run the forward loop through i=num_steps-1?");
ctrl_ = ctrl_init_;
reverse_started_ = true;
if (num_steps_ > 0)
{
// Restore u_{num_steps-1} into u_work.
restore_snapshot(u_work, prefinal_buf_.data(), snapshot_bytes_);
u_work_time_ = num_steps_ - 1;
}
}
// Execute controller actions until we perform exactly one adjoint step.
for (;;)
{
RevolveAction act = ctrl_.Next();
switch (act)
{
case RevolveAction::takeshot:
{
const int slot = ctrl_.Check();
MFEM_VERIFY(u_work_time_ == ctrl_.Capo(),
"takeshot: u_work_time must equal capo");
make_snapshot(u_work, io_buf_.data(), snapshot_bytes_);
storage_->Save(slot, io_buf_.data(), snapshot_bytes_);
break;
}
case RevolveAction::restore:
{
const int slot = ctrl_.Check();
storage_->Load(slot, io_buf_.data(), snapshot_bytes_);
restore_snapshot(u_work, io_buf_.data(), snapshot_bytes_);
u_work_time_ = ctrl_.Capo();
break;
}
case RevolveAction::advance:
{
const int target = ctrl_.Capo();
MFEM_VERIFY(u_work_time_ >= 0, "advance: u_work_time not initialized");
MFEM_VERIFY(target >= u_work_time_, "advance: target < current time");
for (int t = u_work_time_; t < target; ++t)
{
primal_step(t, u_work);
}
u_work_time_ = target;
break;
}
case RevolveAction::firsturn:
case RevolveAction::youturn:
{
// After firsturn/youturn, ctrl_.Fine() has been decremented and equals ctrl_.Capo().
const int step = ctrl_.Fine();
MFEM_VERIFY(step == ctrl_.Capo(), "youturn: fine != capo");
MFEM_VERIFY(step == u_work_time_, "youturn: u_work_time != step");
MFEM_VERIFY(step == i, "BackwardStep called with i that doesn't match REVOLVE schedule");
// One adjoint step.
adjoint_step(step, u_work, lambda);
return;
}
case RevolveAction::terminate:
{
MFEM_ABORT("REVOLVE terminated early: BackwardStep called after completion?");
break;
}
}
}
}
private:
void BuildForwardPlanAndInitialControllerState()
{
forward_shots_.clear();
RevolveController sim(num_checkpoints_, /*capo=*/0, /*fine=*/num_steps_);
// Forward-plan phase: only TAKESHOT/ADVANCE should occur while fine-capo>1.
while (sim.Fine() - sim.Capo() > 1)
{
RevolveAction a = sim.Next();
if (a == RevolveAction::takeshot)
{
forward_shots_.push_back({sim.Capo(), sim.Check()});
}
else if (a == RevolveAction::advance)
{
// nothing to record; capo moved forward inside sim
}
else
{
MFEM_ABORT("Unexpected REVOLVE action during forward planning phase");
}
}
// At this point, sim is in the pre-firsturn state (capo = num_steps-1, fine = num_steps)
// for num_steps>=1. For num_steps<=1, fine-capo<=1 from the start.
ctrl_init_ = sim;
}
};
#endif //MFEM_REVOLVE_CHECKPOINTING_HPP
-357
View File
@@ -1,357 +0,0 @@
#ifndef MFEM_REVOLVE_SEPARATE_FILE_STORAGE_HPP
#define MFEM_REVOLVE_SEPARATE_FILE_STORAGE_HPP
#include "mfem.hpp"
#include <cstdint>
#include <cstdio> // std::remove
#include <fstream>
#include <iomanip>
#include <limits>
#include <sstream>
#include <string>
#include <vector>
#if __cplusplus >= 201703L
#include <filesystem>
#include <system_error>
#else
#if defined(_WIN32)
#include <direct.h> // _mkdir
#else
#include <sys/stat.h> // mkdir
#include <sys/types.h>
#include <errno.h>
#endif
#endif
namespace mfem
{
/**
* @brief Separate-file checkpoint storage for REVOLVE: one file per checkpoint slot.
*
* This storage is "slot indexed":
* slot = 0..max_slots-1
*
* File naming:
* <dir>/<prefix><slot as zero-padded integer><ext>
*
* Example:
* dir="ckpt", prefix="rev_", ext=".bin", max_slots=8
* -> ckpt/rev_00.bin ... ckpt/rev_07.bin
*
* Intended REVOLVE interface:
* int MaxSlots() const;
* size_t SlotBytes() const;
* void Save(int slot, const uint8_t *src, size_t bytes);
* void Load(int slot, uint8_t *dst, size_t bytes) const;
*
* Notes:
* - Slot files are pre-created and resized on construction if truncate_files=true.
* - Save() overwrites the full slot file content from offset 0.
* - Load() reads exactly SlotBytes() from offset 0.
* - If keep_files=false, destructor deletes the slot files.
* - If keep_open=true, keeps N files open (faster, but uses file descriptors).
*/
class RevolveSeparateFileCheckpointStorage
{
public:
RevolveSeparateFileCheckpointStorage(const std::string &directory,
const std::string &prefix,
const std::string &ext,
int max_slots,
std::size_t slot_bytes,
bool create_dir = true,
bool truncate_files = true,
bool keep_files = true,
bool keep_open = false,
bool flush_on_save = false)
: dir_(directory),
prefix_(prefix),
ext_(ext),
max_slots_(max_slots),
slot_bytes_(slot_bytes),
keep_files_(keep_files),
keep_open_(keep_open),
flush_on_save_(flush_on_save)
{
MFEM_VERIFY(max_slots_ > 0, "RevolveSeparateFileCheckpointStorage: max_slots must be > 0.");
MFEM_VERIFY(slot_bytes_ > 0, "RevolveSeparateFileCheckpointStorage: slot_bytes must be > 0.");
MFEM_VERIFY(!prefix_.empty(), "RevolveSeparateFileCheckpointStorage: prefix must not be empty.");
if (create_dir) { EnsureDirectory_(dir_); }
// Precompute slot paths.
slot_paths_.resize((std::size_t)max_slots_);
for (int s = 0; s < max_slots_; ++s)
{
slot_paths_[(std::size_t)s] = MakeSlotPath_(s);
}
if (truncate_files)
{
PrecreateAll_();
}
else
{
// Optional light sanity check: try opening one file. (User may want to reuse existing.)
// We'll rely on Save/Load verification otherwise.
}
if (keep_open_)
{
OpenAll_();
}
}
~RevolveSeparateFileCheckpointStorage()
{
CloseAll_();
if (!keep_files_)
{
RemoveAllFiles_();
}
}
int MaxSlots() const { return max_slots_; }
std::size_t SlotBytes() const { return slot_bytes_; }
/// Return the full path for a given slot (useful for debugging).
const std::string &SlotPath(int slot) const
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "SlotPath: slot out of range.");
return slot_paths_[(std::size_t)slot];
}
/**
* @brief Save a checkpoint image into slot file.
*
* Requirements:
* - slot in [0, MaxSlots())
* - bytes == SlotBytes()
* - src != nullptr
*/
void Save(int slot, const std::uint8_t *src, std::size_t bytes)
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Save: slot out of range.");
MFEM_VERIFY(bytes == slot_bytes_, "Save: bytes mismatch.");
MFEM_VERIFY(src != nullptr, "Save: src is null.");
if (keep_open_)
{
std::fstream &f = files_[(std::size_t)slot];
MFEM_VERIFY(f.is_open(), "Save: file not open (keep_open).");
f.clear();
f.seekp(0, std::ios::beg);
MFEM_VERIFY(f.good(), "Save: seekp failed (keep_open).");
f.write(reinterpret_cast<const char*>(src), (std::streamsize)slot_bytes_);
MFEM_VERIFY(f.good(), "Save: write failed (keep_open).");
if (flush_on_save_) { f.flush(); }
return;
}
// Open on demand
std::fstream f(SlotPath(slot).c_str(),
std::ios::binary | std::ios::in | std::ios::out);
if (!f.is_open())
{
// If missing, create it sized correctly, then reopen.
PrecreateOne_(slot);
f.open(SlotPath(slot).c_str(), std::ios::binary | std::ios::in | std::ios::out);
}
MFEM_VERIFY(f.is_open(), "Save: failed to open slot file.");
f.seekp(0, std::ios::beg);
MFEM_VERIFY(f.good(), "Save: seekp failed.");
f.write(reinterpret_cast<const char*>(src), (std::streamsize)slot_bytes_);
MFEM_VERIFY(f.good(), "Save: write failed.");
if (flush_on_save_) { f.flush(); }
}
/**
* @brief Load a checkpoint image from slot file.
*
* Requirements:
* - slot in [0, MaxSlots())
* - bytes == SlotBytes()
* - dst != nullptr
*/
void Load(int slot, std::uint8_t *dst, std::size_t bytes) const
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "Load: slot out of range.");
MFEM_VERIFY(bytes == slot_bytes_, "Load: bytes mismatch.");
MFEM_VERIFY(dst != nullptr, "Load: dst is null.");
if (keep_open_)
{
std::fstream &f = files_[(std::size_t)slot];
MFEM_VERIFY(f.is_open(), "Load: file not open (keep_open).");
f.clear();
f.seekg(0, std::ios::beg);
MFEM_VERIFY(f.good(), "Load: seekg failed (keep_open).");
f.read(reinterpret_cast<char*>(dst), (std::streamsize)slot_bytes_);
MFEM_VERIFY(f.good(), "Load: read failed (keep_open).");
return;
}
std::ifstream f(SlotPath(slot).c_str(), std::ios::binary);
MFEM_VERIFY(f.is_open(), "Load: failed to open slot file.");
f.read(reinterpret_cast<char*>(dst), (std::streamsize)slot_bytes_);
MFEM_VERIFY(f.good(), "Load: read failed.");
}
private:
std::string dir_;
std::string prefix_;
std::string ext_;
int max_slots_ = 0;
std::size_t slot_bytes_ = 0;
bool keep_files_ = true;
bool keep_open_ = false;
bool flush_on_save_ = false;
std::vector<std::string> slot_paths_;
mutable std::vector<std::fstream> files_; // only used if keep_open_==true
static std::string JoinPath_(const std::string &dir, const std::string &file)
{
if (dir.empty()) { return file; }
const char last = dir.back();
if (last == '/' || last == '\\') { return dir + file; }
return dir + "/" + file;
}
int SlotDigits_() const
{
int x = max_slots_ - 1;
int d = 1;
while (x >= 10) { x /= 10; ++d; }
return d;
}
std::string MakeSlotFilename_(int slot) const
{
std::ostringstream os;
os << prefix_
<< std::setw(SlotDigits_()) << std::setfill('0') << slot
<< ext_;
return os.str();
}
std::string MakeSlotPath_(int slot) const
{
return JoinPath_(dir_, MakeSlotFilename_(slot));
}
void PrecreateOne_(int slot) const
{
MFEM_VERIFY(0 <= slot && slot < max_slots_, "PrecreateOne: slot out of range.");
// Create/truncate and set file size to slot_bytes_ by writing last byte.
std::ofstream ofs(SlotPath(slot).c_str(), std::ios::binary | std::ios::trunc);
MFEM_VERIFY(ofs.is_open(), "PrecreateOne: failed to create slot file.");
const std::uint64_t sb = (std::uint64_t)slot_bytes_;
MFEM_VERIFY(sb <= (std::uint64_t)std::numeric_limits<std::streamoff>::max(),
"PrecreateOne: slot_bytes too large for streamoff.");
if (sb > 0)
{
ofs.seekp((std::streamoff)(sb - 1), std::ios::beg);
MFEM_VERIFY(ofs.good(), "PrecreateOne: seekp failed.");
const char zero = 0;
ofs.write(&zero, 1);
MFEM_VERIFY(ofs.good(), "PrecreateOne: size write failed.");
}
}
void PrecreateAll_() const
{
for (int slot = 0; slot < max_slots_; ++slot)
{
PrecreateOne_(slot);
}
}
void OpenAll_()
{
files_.resize((std::size_t)max_slots_);
for (int slot = 0; slot < max_slots_; ++slot)
{
std::fstream &f = files_[(std::size_t)slot];
f.open(SlotPath(slot).c_str(), std::ios::binary | std::ios::in | std::ios::out);
if (!f.is_open())
{
PrecreateOne_(slot);
f.open(SlotPath(slot).c_str(), std::ios::binary | std::ios::in | std::ios::out);
}
MFEM_VERIFY(f.is_open(), "OpenAll: failed to open slot file.");
}
}
void CloseAll_()
{
if (!files_.empty())
{
for (auto &f : files_) { if (f.is_open()) { f.close(); } }
files_.clear();
}
}
void RemoveAllFiles_() const
{
for (int slot = 0; slot < max_slots_; ++slot)
{
// Ignore remove errors (e.g., already removed), but you can tighten if desired.
std::remove(SlotPath(slot).c_str());
}
}
static void EnsureDirectory_(const std::string &dir)
{
if (dir.empty()) { return; }
#if __cplusplus >= 201703L
namespace fs = std::filesystem;
std::error_code ec;
if (!fs::exists(dir, ec))
{
fs::create_directories(dir, ec);
}
MFEM_VERIFY(!ec, "EnsureDirectory: failed to create directory: " + dir);
#else
#if defined(_WIN32)
const int rc = _mkdir(dir.c_str());
if (rc != 0)
{
// If directory already exists, _mkdir fails. We accept that.
// There's no reliable portable "exists" check in pre-C++17 without more code.
}
#else
const int rc = mkdir(dir.c_str(), 0755);
if (rc != 0 && errno != EEXIST)
{
MFEM_ABORT("EnsureDirectory: failed to create directory: " + dir);
}
#endif
#endif
}
};
} // namespace mfem
#endif // MFEM_REVOLVE_SEPARATE_FILE_STORAGE_HPP
@@ -1,273 +0,0 @@
#ifndef MFEM_SEGMENT_CHECKPOINT_STORAGE_HPP
#define MFEM_SEGMENT_CHECKPOINT_STORAGE_HPP
#include "mfem.hpp"
#include "file_checkpoint_storage.hpp" // reuses DefaultCheckpointBinaryIO
#include <cstdint>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <set>
#if __has_include(<filesystem>)
#include <filesystem>
namespace mfem_fs = std::filesystem;
#define MFEM_HAVE_FILESYSTEM 1
#else
#define MFEM_HAVE_FILESYSTEM 0
#endif
namespace mfem
{
/**
* @brief Segment/range file storage: one file per handle-id range + in-file offsets.
*
* Segment file = <dir>/<prefix><segment_id><ext>
* Where segment_id = handle / records_per_file.
*
* Pros:
* - Much fewer files than file-per-snapshot
* - No per-snapshot file create/delete
*
* Cons:
* - Append-only: Erase() does not reclaim file space (records remain)
* - Index is in-memory (not restartable across processes unless you persist it)
*/
template <typename Snapshot,
typename SnapshotIO = DefaultCheckpointBinaryIO<Snapshot> >
class SegmentedFileCheckpointStorage
{
public:
using Handle = std::int64_t;
SegmentedFileCheckpointStorage(const std::string &directory,
std::int64_t records_per_file = 4096,
const std::string &prefix = "seg_",
const std::string &extension = ".bin",
bool create_dir = true,
bool keep_files = false)
: dir_(directory),
prefix_(prefix),
ext_(extension),
keep_files_(keep_files),
records_per_file_(records_per_file)
{
MFEM_VERIFY(records_per_file_ > 0, "SegmentedFileCheckpointStorage: records_per_file must be > 0.");
MFEM_VERIFY(!dir_.empty(), "SegmentedFileCheckpointStorage: empty directory.");
if (create_dir)
{
#if MFEM_HAVE_FILESYSTEM
std::error_code ec;
mfem_fs::create_directories(mfem_fs::path(dir_), ec);
MFEM_VERIFY(!ec, "SegmentedFileCheckpointStorage: failed to create directory.");
#else
MFEM_ABORT("SegmentedFileCheckpointStorage: create_dir=true requires <filesystem> support.");
#endif
}
}
Handle InvalidHandle() const { return (Handle)-1; }
bool IsValid(const Handle &h) const { return h >= 0; }
Handle Store(Snapshot &&snap)
{
const Handle id = AllocateId_();
EnsureMetaSize_(id);
const std::int64_t seg = SegmentId_(id);
const std::string path = SegmentPath_(seg);
const std::uint64_t offset = AppendRecord_(path, snap);
const std::uint64_t bytes = last_payload_bytes_;
meta_[id].valid = true;
meta_[id].seg_id = seg;
meta_[id].offset = offset;
meta_[id].bytes = bytes;
touched_segments_.insert(seg);
return id;
}
template <typename Func>
void Read(const Handle &h, Func &&f) const
{
MFEM_VERIFY(IsValid(h), "SegmentedFileCheckpointStorage: Read invalid handle.");
MFEM_VERIFY(h < (Handle)meta_.size(), "SegmentedFileCheckpointStorage: Read out-of-range handle.");
MFEM_VERIFY(meta_[h].valid, "SegmentedFileCheckpointStorage: Read on erased handle.");
const std::string path = SegmentPath_(meta_[h].seg_id);
std::ifstream is(path, std::ios::binary);
MFEM_VERIFY(is.is_open(), "SegmentedFileCheckpointStorage: open-for-read failed.");
// Seek to record start
is.seekg((std::streamoff)meta_[h].offset, std::ios::beg);
MFEM_VERIFY(is.good(), "SegmentedFileCheckpointStorage: seekg failed.");
std::uint64_t payload_bytes = 0;
is.read(reinterpret_cast<char*>(&payload_bytes), sizeof(payload_bytes));
MFEM_VERIFY(is.good(), "SegmentedFileCheckpointStorage: read header failed.");
MFEM_VERIFY(payload_bytes == meta_[h].bytes, "SegmentedFileCheckpointStorage: payload size mismatch.");
Snapshot snap = SnapshotIO::Read(is);
MFEM_VERIFY(is.good(), "SegmentedFileCheckpointStorage: payload read failed.");
f(snap);
}
void Erase(Handle &h)
{
if (!IsValid(h)) { h = InvalidHandle(); return; }
MFEM_VERIFY(h < (Handle)meta_.size(), "SegmentedFileCheckpointStorage: Erase out-of-range handle.");
meta_[h].valid = false;
if (!keep_files_)
{
free_.push_back(h); // allow handle reuse
}
h = InvalidHandle();
}
/**
* @brief Optional cleanup helper (NOT part of the required storage interface).
*
* Deletes all segment files touched by this storage object.
*/
void PurgeAllFiles()
{
#if MFEM_HAVE_FILESYSTEM
for (auto seg : touched_segments_)
{
const std::string path = SegmentPath_(seg);
std::error_code ec;
mfem_fs::remove(mfem_fs::path(path), ec);
// Best-effort cleanup:
MFEM_VERIFY(!ec, "SegmentedFileCheckpointStorage: failed to remove segment file.");
}
touched_segments_.clear();
#else
MFEM_ABORT("SegmentedFileCheckpointStorage: PurgeAllFiles requires <filesystem> support.");
#endif
}
private:
struct Meta
{
bool valid = false;
std::int64_t seg_id = 0;
std::uint64_t offset = 0;
std::uint64_t bytes = 0;
};
std::string dir_, prefix_, ext_;
bool keep_files_ = false;
std::int64_t records_per_file_ = 4096;
mutable Handle next_id_ = 0;
mutable std::vector<Handle> free_;
std::vector<Meta> meta_;
mutable std::uint64_t last_payload_bytes_ = 0;
std::set<std::int64_t> touched_segments_;
void EnsureMetaSize_(Handle id)
{
if ((std::size_t)id >= meta_.size())
{
meta_.resize((std::size_t)id + 1);
}
}
Handle AllocateId_()
{
if (!keep_files_ && !free_.empty())
{
const Handle id = free_.back();
free_.pop_back();
return id;
}
return next_id_++;
}
std::int64_t SegmentId_(Handle id) const
{
return (std::int64_t)(id / records_per_file_);
}
std::string SegmentPath_(std::int64_t seg) const
{
std::ostringstream oss;
oss << prefix_ << std::setw(8) << std::setfill('0') << seg << ext_;
#if MFEM_HAVE_FILESYSTEM
mfem_fs::path p = mfem_fs::path(dir_) / mfem_fs::path(oss.str());
return p.string();
#else
return dir_ + "/" + oss.str();
#endif
}
static void EnsureFileExists_(const std::string &path)
{
// Try open for read/write; if missing, create.
std::fstream fs(path, std::ios::binary | std::ios::in | std::ios::out);
if (!fs.is_open())
{
std::ofstream create(path, std::ios::binary | std::ios::out);
MFEM_VERIFY(create.is_open(), "SegmentedFileCheckpointStorage: file create failed.");
}
}
// Append framed record to file. Returns offset of record start. Updates last_payload_bytes_.
std::uint64_t AppendRecord_(const std::string &path, const Snapshot &snap)
{
EnsureFileExists_(path);
std::fstream fs(path, std::ios::binary | std::ios::in | std::ios::out);
MFEM_VERIFY(fs.is_open(), "SegmentedFileCheckpointStorage: open-for-append failed.");
fs.seekp(0, std::ios::end);
MFEM_VERIFY(fs.good(), "SegmentedFileCheckpointStorage: seekp(end) failed.");
const std::streamoff begin = (std::streamoff)fs.tellp();
MFEM_VERIFY(begin >= 0, "SegmentedFileCheckpointStorage: tellp failed.");
// placeholder for payload bytes
std::uint64_t payload_bytes = 0;
fs.write(reinterpret_cast<const char*>(&payload_bytes), sizeof(payload_bytes));
MFEM_VERIFY(fs.good(), "SegmentedFileCheckpointStorage: write header failed.");
// payload
SnapshotIO::Write(fs, snap);
MFEM_VERIFY(fs.good(), "SegmentedFileCheckpointStorage: write payload failed.");
const std::streamoff end = (std::streamoff)fs.tellp();
MFEM_VERIFY(end >= begin, "SegmentedFileCheckpointStorage: tellp end failed.");
payload_bytes = (std::uint64_t)(end - begin - (std::streamoff)sizeof(std::uint64_t));
last_payload_bytes_ = payload_bytes;
// backpatch payload size
fs.seekp(begin, std::ios::beg);
MFEM_VERIFY(fs.good(), "SegmentedFileCheckpointStorage: seekp(begin) failed.");
fs.write(reinterpret_cast<const char*>(&payload_bytes), sizeof(payload_bytes));
MFEM_VERIFY(fs.good(), "SegmentedFileCheckpointStorage: backpatch failed.");
return (std::uint64_t)begin;
}
};
} // namespace mfem
#endif // MFEM_SEGMENT_CHECKPOINT_STORAGE_HPP
-701
View File
@@ -1,701 +0,0 @@
#include "mfem.hpp"
#include "dynamic_checkpointing.hpp"
#include "fixed_slot_checkpoint_storage.hpp"
#include "file_checkpoint_storage.hpp"
#include <cmath>
#include <iomanip>
#include <string>
using namespace mfem;
/************************************************************************
* The forward state consists of vector and several additional values.
* The goal is to demontrate how different storages can be used together
* with DynamicCheckpointing in order to avoid unnecessary memory
* allocations, data copies, and deallocations.
* *********************************************************************/
struct State
{
mfem::real_t time = 0.0;
mfem::real_t obj = 0.0;
mfem::Vector v;
};
// ---------------------------
// Snapshot type used by storage:
// a lightweight view (non-owning).
// ---------------------------
struct StateSnapshotView
{
mfem::real_t time = 0.0;
mfem::real_t obj = 0.0;
// Points to n*sizeof(real_t) bytes.
// - during Store(): points to State::v host data
// - during Read(): points into storage's internal scratch buffer
const unsigned char *v_bytes = nullptr;
};
// ---------------------------
// Packer for fixed-slot storage
// Layout in slot bytes:
// [ time | obj | v[0..n-1] ]
// ---------------------------
class StateSnapshotViewPacker
{
public:
explicit StateSnapshotViewPacker(int n) : n_(n)
{
MFEM_VERIFY(n_ > 0, "StateSnapshotViewPacker: n must be > 0.");
}
std::size_t SlotBytes() const
{
return (std::size_t)(2 + n_) * sizeof(mfem::real_t);
}
void Pack(const StateSnapshotView &s, void *dst) const
{
MFEM_VERIFY(dst != nullptr, "Pack: dst is null.");
MFEM_VERIFY(s.v_bytes != nullptr, "Pack: v_bytes is null.");
unsigned char *b = static_cast<unsigned char*>(dst);
std::memcpy(b + 0*sizeof(mfem::real_t), &s.time, sizeof(mfem::real_t));
std::memcpy(b + 1*sizeof(mfem::real_t), &s.obj, sizeof(mfem::real_t));
std::memcpy(b + 2*sizeof(mfem::real_t),
s.v_bytes,
(std::size_t)n_ * sizeof(mfem::real_t));
}
// IMPORTANT:
// Unpack produces a view into the provided src buffer.
// The resulting pointer is only valid as long as src remains unchanged.
void Unpack(const void *src, StateSnapshotView &out) const
{
MFEM_VERIFY(src != nullptr, "Unpack: src is null.");
const unsigned char *b = static_cast<const unsigned char*>(src);
std::memcpy(&out.time, b + 0*sizeof(mfem::real_t), sizeof(mfem::real_t));
std::memcpy(&out.obj, b + 1*sizeof(mfem::real_t), sizeof(mfem::real_t));
out.v_bytes = b + 2*sizeof(mfem::real_t);
}
int N() const { return n_; }
private:
int n_ = 0;
};
/*****************************************************************************
* Provides a recipe for runing dynamic checkpointing with memory storage
* alocated as one single big block.
* **************************************************************************/
void run_fixed_slot_memory_storage( mfem::real_t Tfinal /*Final time*/,
mfem::real_t dtime /*time step*/,
int s /* checkpoint budget (real checkpoints) */,
int n /*State vector size*/,
bool print=true)
{
StateSnapshotViewPacker packer(n);
using Storage = mfem::FixedSlotMemoryCheckpointStorage<StateSnapshotView,
StateSnapshotViewPacker>;
Storage storage(s, packer);
// Snapshot type is StateSnapshotView
using Checkpointing = mfem::DynamicCheckpointing<StateSnapshotView, Storage>;
Checkpointing ckpt(s, storage);
// Returns view of the State and avoids data transfer
auto make_snapshot = [&](const State &u) -> StateSnapshotView
{
MFEM_VERIFY(u.v.Size() == n, "make_snapshot: State.v size changed!");
const mfem::real_t *vh = u.v.HostRead(); // host pointer
StateSnapshotView snap;
snap.time = u.time;
snap.obj = u.obj;
snap.v_bytes = reinterpret_cast<const unsigned char*>(vh);
return snap;
};
//Transfers data from the snaphot view to the State u_out.
auto restore_snapshot = [&](const StateSnapshotView &snap, State &u_out)
{
u_out.time = snap.time;
u_out.obj = snap.obj;
if (u_out.v.Size() != n) { u_out.v.SetSize(n); }
mfem::real_t *vh = u_out.v.HostWrite();
std::memcpy(vh,
snap.v_bytes,
(std::size_t)n * sizeof(mfem::real_t));
};
using Step = Checkpointing::Step;
State u;
u.v.SetSize(n); u.v=0.0;
auto primal_step = [&](State &u_inout, Step i)
{
// advance u_inout.v, u_inout.time, u_inout.obj
if(print)
std::cout<<"Forward step: "<<i<<" time="<<u_inout.time<<" obj="<<u_inout.obj<<std::endl;
u_inout.time=i*0.1;
u_inout.obj=i*0.2;
};
struct AdjointState { /* ... */ };
AdjointState q;
auto adjoint_step = [&](AdjointState &q_ip1_inout, const State &u_i, Step i)
{
// use u_i.time/u_i.obj/u_i.v to update adjoint
if(print)
std::cout<<"Adjoint step: "<<i<<" time="<<u_i.time<<" obj="<<u_i.obj<<std::endl;
};
// Forward sweep (unknown number of steps)
mfem::real_t t=0.0;
Step i=0;
while(t<Tfinal)
{
// Store snapshot of u_i when manager decides; then primal_step u_i -> u_{i+1}
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t+=dtime;
++i;
}
mfem::out << "Total number of steps m="<<i-1<<"\n";
mfem::out << "\nBackward sweep.\n";
const Step m=i;
// Backward sweep
State u_work;
u_work.v.SetSize(n); // allocate once
for (Step i = m - 1; i >= 0; --i)
{
ckpt.BackwardStep(i, q, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
if (i == 0) { break; }
}
}
/****************************************************************************
* Provides a recipe for runing dynamic checkpointing with file storage
* alocated as one single big file.
****************************************************************************/
/****************************************************************************
* The dynamic algorithm conceptually maintains s + 1 checkpoints, but the
* last one is a placeholder checkpoint that stores no solution and takes
* little memory; only s are real stored solutions.
* So FixedSlotFileCheckpointStorage needs only s slots.
* File behavior for FixedSlotFileCheckpointStorage:
- Writes a small header + s * slot_bytes payload region,
- Erase(handle) just returns the slot to the free list
(file contents remain, will be overwritten later),
no filesystem metadata churn.
* Device memory: Runs on GPU -packing to a host file requires a host pointer.
Using:
u.v.HostRead() for packing,
u_out.v.HostWrite() for restore,
keeps memmory access correct.
* Snapshot pointer lifetime: In the read path, StateSnapshotViewPacker::Unpack
sets snap.v_bytes to point into the storages internal read buffer. That
pointer is only valid until the storage performs another Read/Store
(and in general, one should treat it as valid only during restore_snapshot).
*****************************************************************************/
void run_fixed_slot_file_storage(mfem::real_t Tfinal /*Final time*/,
mfem::real_t dtime /*time step*/,
int s /* checkpoint budget (real checkpoints) */,
int n /*State vector size*/,
const std::string &file_path,
bool print=true)
{
// s = number of REAL checkpoints (placeholder doesn't store a snapshot)
MFEM_VERIFY(s > 0, "Need s > 0.");
MFEM_VERIFY(n > 0, "Need n > 0.");
StateSnapshotViewPacker packer(n);
// Fixed-slot FILE storage:
// - single file (file_path)
// - max_slots = s (real checkpoints)
// - slot_bytes = packer.SlotBytes()
// - truncate=true to start fresh
// - flush_on_store=false for performance (set true if you want extra safety)
using Storage = mfem::FixedSlotFileCheckpointStorage<StateSnapshotView, StateSnapshotViewPacker>;
Storage storage(file_path,
/*max_slots=*/s,
/*packer=*/packer,
/*truncate=*/true,
/*flush_on_store=*/false);
// Dynamic checkpointing manager using this storage.
using Checkpointing = mfem::DynamicCheckpointing<StateSnapshotView, Storage>;
Checkpointing ckpt(s, storage);
// ---------------------------
// Callbacks
// ---------------------------
// Convert State -> SnapshotView (no allocation).
auto make_snapshot = [&](const State &u) -> StateSnapshotView
{
MFEM_VERIFY(u.v.Size() == n, "make_snapshot: State.v size changed!");
// Ensure host pointer valid even when MFEM is using device memory.
const mfem::real_t *vh = u.v.HostRead();
StateSnapshotView snap;
snap.time = u.time;
snap.obj = u.obj;
snap.v_bytes = reinterpret_cast<const unsigned char*>(vh);
return snap;
};
// Convert SnapshotView -> State (must COPY out of v_bytes).
auto restore_snapshot = [&](const StateSnapshotView &snap, State &u_out)
{
u_out.time = snap.time;
u_out.obj = snap.obj;
if (u_out.v.Size() != n) { u_out.v.SetSize(n); }
mfem::real_t *vh = u_out.v.HostWrite();
std::memcpy(vh,
snap.v_bytes,
(std::size_t)n * sizeof(mfem::real_t));
};
// Example primal/adjoint step signatures (replace with your own):
using Step = Checkpointing::Step;
auto primal_step = [&](State &u_inout, Step i)
{
if(print)
std::cout<<"Forward step: "<<i<<" time="<<u_inout.time<<" obj="<<u_inout.obj<<std::endl;
u_inout.time=i*0.1;
u_inout.obj=i*0.2;
// update u_inout.time, u_inout.obj, u_inout.v
};
struct AdjointState
{
// your adjoint variables, e.g., mfem::Vector lambda;
};
auto adjoint_step = [&](AdjointState &q_ip1_inout, const State &u_i, Step i)
{
// update adjoint using u_i
if(print)
std::cout<<"Adjoint step: "<<i<<" time="<<u_i.time<<" obj="<<u_i.obj<<std::endl;
};
// ---------------------------
// Forward/backward skeleton
// ---------------------------
State u;
u.v.SetSize(n);
u.time = 0.0;
u.obj = 0.0;
u.v = 0.0;
// Forward sweep (unknown number of steps)
mfem::real_t t=0.0;
Step i=0;
while(t<Tfinal)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t+=dtime;
++i;
}
mfem::out << "Total number of steps m="<<i-1<<"\n";
mfem::out << "\nBackward sweep.\n";
// Backward sweep
AdjointState q;
const Step m=i;
State u_work;
u_work.v.SetSize(n);
for (Step i = m - 1; i >= 0; --i)
{
ckpt.BackwardStep(i, q, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
if (i == 0) { break; }
}
}
/******************************************************************************
The following classes implement the IO and the data necessery for dynamic
checkpointing with mfem::FileCheckpointStorage (one file per stored snapshot).
With FileCheckpointStorage, the Snapshot must own the data which will be
written, i.e. it cannot be a view containing pointers into State::v
as those pointers would be meaningless when read back.
*******************************************************************************/
// The StateSnapshot is just a copy of the State
using StateSnapshot=State;
struct StateSnapshotBinaryIO
{
static void Write(std::ostream &os, const StateSnapshot &s)
{
os.write(reinterpret_cast<const char*>(&s.time), sizeof(mfem::real_t));
os.write(reinterpret_cast<const char*>(&s.obj), sizeof(mfem::real_t));
MFEM_VERIFY(os.good(), "StateSnapshotBinaryIO: write time/obj failed.");
const std::int64_t n = (std::int64_t)s.v.Size();
os.write(reinterpret_cast<const char*>(&n), sizeof(n));
MFEM_VERIFY(os.good(), "StateSnapshotBinaryIO: write vector size failed.");
if (n > 0)
{
const mfem::real_t *vh = s.v.HostRead(); // ensure host pointer
os.write(reinterpret_cast<const char*>(vh),
(std::streamsize)(n * (std::int64_t)sizeof(mfem::real_t)));
MFEM_VERIFY(os.good(), "StateSnapshotBinaryIO: write vector payload failed.");
}
}
static StateSnapshot Read(std::istream &is)
{
StateSnapshot s;
is.read(reinterpret_cast<char*>(&s.time), sizeof(mfem::real_t));
is.read(reinterpret_cast<char*>(&s.obj), sizeof(mfem::real_t));
MFEM_VERIFY(is.good(), "StateSnapshotBinaryIO: read time/obj failed.");
std::int64_t n = 0;
is.read(reinterpret_cast<char*>(&n), sizeof(n));
MFEM_VERIFY(is.good(), "StateSnapshotBinaryIO: read vector size failed.");
MFEM_VERIFY(n >= 0, "StateSnapshotBinaryIO: invalid negative vector size.");
s.v.SetSize((int)n);
if (n > 0)
{
mfem::real_t *vh = s.v.HostWrite(); // ensure host pointer
is.read(reinterpret_cast<char*>(vh),
(std::streamsize)(n * (std::int64_t)sizeof(mfem::real_t)));
MFEM_VERIFY(is.good(), "StateSnapshotBinaryIO: read vector payload failed.");
}
return s;
}
};
/***************************************************************************
Important differences vs FixedSlotFileCheckpointStorage
Metadata overhead
FileCheckpointStorage typically:
*creates a new file for each Store() (plus a temp file rename),
*deletes a file on each Erase() (unless keep_files=true).
Dynamic checkpointing can perform many store/erase operations during
forward and during recomputation in reverse, so this can hammer filesystem
metadata on parallel filesystems.
If thats a concern, prefer:
*fixed-slot single-file storage, or
*segmented range file storage (few files + offsets).
Correctness note about pointers:
This is why we used an owning snapshot (StateSnapshot): storing a view
with v_bytes into file storage would store pointer values, which are
meaningless when read back.
*****************************************************************************/
void run_file_storage(mfem::real_t Tfinal /*Final time*/,
mfem::real_t dtime /*time step*/,
int s /* checkpoint budget (real checkpoints) */,
int n /*State vector size*/,
const std::string &directory,
bool print=true)
{
MFEM_VERIFY(s > 0, "Need s > 0.");
MFEM_VERIFY(n > 0, "Need n > 0.");
// One file per snapshot (create_dir=true). keep_files=false means Erase() removes files.
using Storage = mfem::FileCheckpointStorage<StateSnapshot, StateSnapshotBinaryIO>;
Storage storage(directory, "ckpt_", ".bin", /*create_dir=*/true, /*keep_files=*/false);
// Snapshot type = StateSnapshot (owning)
using Checkpointing = mfem::DynamicCheckpointing<StateSnapshot, Storage>;
Checkpointing ckpt(s, storage);
using Step = Checkpointing::Step;
// ---- Callbacks ----
// Make an owning snapshot from the current State (deep copy of vector payload)
auto make_snapshot = [&](const State &u) -> StateSnapshot
{
MFEM_VERIFY(u.v.Size() == n, "make_snapshot: State.v size changed!");
StateSnapshot snap;
snap.time = u.time;
snap.obj = u.obj;
snap.v.SetSize(n);
const mfem::real_t *src = u.v.HostRead();
mfem::real_t *dst = snap.v.HostWrite();
std::memcpy(dst, src, (std::size_t)n * sizeof(mfem::real_t));
return snap; // move into storage
};
// Restore from snapshot into an actual State (copy payload)
auto restore_snapshot = [&](const StateSnapshot &snap, State &u_out)
{
u_out.time = snap.time;
u_out.obj = snap.obj;
MFEM_VERIFY(snap.v.Size() == n, "restore_snapshot: snapshot vector size mismatch.");
if (u_out.v.Size() != n) { u_out.v.SetSize(n); }
const mfem::real_t *src = snap.v.HostRead();
mfem::real_t *dst = u_out.v.HostWrite();
std::memcpy(dst, src, (std::size_t)n * sizeof(mfem::real_t));
};
// Example primal step signature (replace with your integrator)
auto primal_step = [&](State &u_inout, Step i)
{
// advance u_inout.time, u_inout.obj, u_inout.v
if(print)
std::cout<<"Forward step: "<<i<<" time="<<u_inout.time<<" obj="<<u_inout.obj<<std::endl;
u_inout.time=i*0.1;
u_inout.obj=i*0.2;
};
// Example adjoint state and adjoint step signature (replace with yours)
struct AdjointState
{
// e.g., mfem::Vector lambda;
};
auto adjoint_step = [&](AdjointState &q_ip1_inout, const State &u_i, Step i)
{
// update adjoint using u_i
if(print)
std::cout<<"Adjoint step: "<<i<<" time="<<u_i.time<<" obj="<<u_i.obj<<std::endl;
};
// ---- Forward/backward skeleton ----
State u;
u.v.SetSize(n);
u.time = 0.0;
u.obj = 0.0;
u.v = 0.0;
// Forward sweep (unknown number of steps)
mfem::real_t t=0.0;
Step i=0;
while(t<Tfinal)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t+=dtime;
++i;
}
mfem::out << "Total number of steps m="<<i-1<<"\n";
mfem::out << "\nBackward sweep.\n";
// backward
const Step m=i;
AdjointState q;
State u_work;
u_work.v.SetSize(n);
for (Step i = m - 1; i >= 0; --i)
{
ckpt.BackwardStep(i, q, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
if (i == 0) { break; }
}
}
/****************************************************************************
* Using standard in memory storage. Works with variable snaphot sizes.
* The size of the vector (n) is provided in order to compare the run to
* the other implementations.
***************************************************************************/
void run_in_memory_storage(mfem::real_t Tfinal /*Final time*/,
mfem::real_t dtime /*time step*/,
int s /* checkpoint budget (real checkpoints) */,
int n /*State vector size*/,
bool print=true)
{
InMemoryCheckpointStorage<StateSnapshot> storage;
using Checkpointing = mfem::DynamicCheckpointing<StateSnapshot,
InMemoryCheckpointStorage<StateSnapshot>>;
Checkpointing ckpt(s, storage);
auto make_snapshot = [&](const State &u) -> StateSnapshot
{
StateSnapshot snap;
snap.time = u.time;
snap.obj = u.obj;
snap.v = u.v; // deep copy (MFEM handles memory)
return snap; // moved into storage by Store()
};
auto restore_snapshot = [&](const StateSnapshot &snap, State &u_out)
{
u_out.time = snap.time;
u_out.obj = snap.obj;
u_out.v = snap.v; // deep copy back into working state
};
using Step = Checkpointing::Step;
// Primal step: u_i -> u_{i+1}
auto primal_step = [&](State &u, Step i)
{
// ... update u.time, u.obj, u.v ...
if(print)
std::cout<<"Forward step: "<<i<<" time="<<u.time<<" obj="<<u.obj<<std::endl;
u.time=i*0.1;
u.obj=i*0.2;
};
// Adjoint step: q_{i+1} -> q_i using u_i
struct AdjointState
{
// e.g. mfem::Vector lambda;
};
auto adjoint_step = [&](AdjointState &q, const State &u_i, Step i)
{
// ... update q using u_i ...
if(print)
std::cout<<"Adjoint step: "<<i<<" time="<<u_i.time<<" obj="<<u_i.obj<<std::endl;
};
State u;
u.v.SetSize(n);
u.v=0.0;
u.time = 0.0;
u.obj = 0.0;
// Forward sweep (unknown number of steps)
mfem::real_t t=0.0;
Step i=0;
while(t<Tfinal)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t+=dtime;
++i;
}
mfem::out << "Total number of steps m="<<i-1<<"\n";
mfem::out << "\nBackward sweep.\n";
// Backward
const Step m=i;
AdjointState q;
State u_work; // used to restore/recompute primal state at step i
u_work.v.SetSize(u.v.Size());
for (Step i = m - 1; i >= 0; --i)
{
ckpt.BackwardStep(i, q, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
if (i == 0) { break; }
}
}
int main(int argc, char *argv[])
{
int s = 5/* checkpoint budget (real checkpoints) */;
int n = 30 /* fixed State.v size */;
std::string filepath="dynamic_ckpts.bin"; /*file name for fixed slot single file*/
std::string directory="dyn_ckpts";
mfem::real_t Tfinal=1;
mfem::real_t dt=0.1;
int print=true;
OptionsParser args(argc, argv);
args.AddOption(&s, "-s",
"--checkpoints", "Checkpoint budget s (real checkpoints).");
args.AddOption(&n, "-n",
"--size", "Size of the state vector.");
args.AddOption(&Tfinal, "-T",
"--tfinal", "Terminate when accumulated time reaches Tfinal.");
args.AddOption(&dt, "-dt", "--dt", "Time step.");
args.AddOption(&filepath, "-file", "--file",
"File name for storing the checkpoints.");
args.AddOption(&directory, "-dir", "--dir",
"Directory for storing the checkpoints.");
args.AddOption(&print, "-print", "--print",
"Turn on/off printing." );
args.Parse();
if (!args.Good())
{
args.PrintUsage(mfem::out);
return 1;
}
args.PrintOptions(mfem::out);
double run_time;
mfem::out<<"\nFixedSlotMemoryStorage Start\n";
mfem::tic();
run_fixed_slot_memory_storage(Tfinal, dt, s, n, print);
run_time=mfem::toc();
mfem::out<<"FixedSlotMemoryStorage time= "<<run_time<<std::endl;
mfem::out<<"\nFixedSlotFileStorage Start\n";
mfem::tic();
run_fixed_slot_file_storage(Tfinal,dt, s,n, filepath,print);
run_time=mfem::toc();
mfem::out<<"FixedSlotFileStorage time= "<<run_time<<std::endl;
mfem::out<<"\nFileStorage Start\n";
mfem::tic();
run_file_storage(Tfinal,dt, s,n, directory, print);
run_time=mfem::toc();
mfem::out<<"FileStorage time= "<<run_time<<std::endl;
mfem::out<<"\nInMemoryStorage\n";
mfem::tic();
run_in_memory_storage(Tfinal,dt, s,n, print);
run_time=mfem::toc();
mfem::out<<"InMemoryStorage time= "<<run_time<<std::endl;
mfem::out << "\nDone.\n";
return 0;
}
@@ -1,300 +0,0 @@
#include "mfem.hpp"
#include "dynamic_checkpointing.hpp"
#include "file_checkpoint_storage.hpp"
#include <cmath>
#include <iomanip>
using namespace mfem;
static double LogisticStep(double u, double alpha, double dt)
{
return u + dt * alpha * u * (1.0 - u);
}
static double LogisticJac(double u, double alpha, double dt)
{
// d/du [u + dt*alpha*u*(1-u)] = 1 + dt*alpha*(1 - 2u)
return 1.0 + dt * alpha * (1.0 - 2.0*u);
}
#if MFEM_HAVE_FILESYSTEM
static void PurgeDir(const std::string &dir)
{
std::error_code ec;
mfem_fs::remove_all(mfem_fs::path(dir), ec);
mfem_fs::create_directories(mfem_fs::path(dir), ec);
MFEM_VERIFY(!ec, "Failed to purge/create directory.");
}
#endif
int main(int argc, char *argv[])
{
// ---------- shared options ----------
int s = 3;
double dt0 = 0.02;
double omega = 0.2;
double Tfinal = 1.0;
bool keep_files = false;
bool purge_dirs = true;
// scalar problem params
double alpha = 2.0;
double u0 = 0.2;
double target_s = 0.7;
double eps = 1e-7;
// vector problem params
int n = 16;
double target_v = 0.7;
std::string dir_scalar = "chk_scalar_files";
std::string dir_vector = "chk_vector_files";
OptionsParser args(argc, argv);
args.AddOption(&s, "-s", "--checkpoints", "Checkpoint budget s (real checkpoints).");
args.AddOption(&dt0, "-dt0", "--dt0", "Base dt for dt(i)=dt0*(1+0.5*sin(omega*i)).");
args.AddOption(&omega, "-om", "--omega", "Omega for dt(i)=dt0*(1+0.5*sin(omega*i)).");
args.AddOption(&Tfinal, "-T", "--tfinal", "Stop when accumulated time reaches Tfinal.");
args.AddOption(&keep_files, "-k", "--keep-files", "-nk", "--no-keep-files",
"Keep checkpoint files (debug).");
args.AddOption(&purge_dirs, "-p", "--purge-dirs", "-np", "--no-purge-dirs",
"Purge checkpoint directories at start (recommended).");
args.AddOption(&alpha, "-a", "--alpha", "Logistic growth alpha.");
args.AddOption(&u0, "-u0", "--u0", "Scalar initial u0.");
args.AddOption(&target_s, "-ts", "--target-scalar", "Scalar target.");
args.AddOption(&eps, "-eps", "--fd-eps", "FD epsilon (scalar and vector directional).");
args.AddOption(&n, "-n", "--size", "Vector dimension.");
args.AddOption(&target_v, "-tv", "--target-vector", "Vector target value per component.");
args.AddOption(&dir_scalar, "-ds", "--dir-scalar", "Directory for scalar checkpoint files.");
args.AddOption(&dir_vector, "-dv", "--dir-vector", "Directory for vector checkpoint files.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(mfem::out);
return 1;
}
args.PrintOptions(mfem::out);
MFEM_VERIFY(s > 0, "Need s > 0.");
MFEM_VERIFY(dt0 > 0.0, "Need dt0 > 0.");
MFEM_VERIFY(Tfinal > 0.0, "Need Tfinal > 0.");
#if MFEM_HAVE_FILESYSTEM
if (purge_dirs)
{
PurgeDir(dir_scalar);
PurgeDir(dir_vector);
}
#else
MFEM_VERIFY(!purge_dirs, "purge_dirs requires <filesystem> support.");
#endif
using StepS = mfem::DynamicCheckpointing<double, mfem::FileCheckpointStorage<double>>::Step;
auto dt_func = [&](StepS i)
{
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
return dt;
};
mfem::out << std::setprecision(15);
// ============================================================
// A) Scalar run (double) with FileCheckpointStorage
// ============================================================
{
mfem::FileCheckpointStorage<double> storage(dir_scalar, "ckpt_", ".bin", true, keep_files);
mfem::DynamicCheckpointing<double, mfem::FileCheckpointStorage<double>> ckpt(s, storage);
auto make_snapshot = [](const double &u) { return u; };
auto restore_snapshot = [](const double &snap, double &out) { out = snap; };
auto primal_step = [&](double &u, StepS i)
{
const double dt = dt_func(i);
u = LogisticStep(u, alpha, dt);
};
auto adjoint_step = [&](double &lambda, const double &u_i, StepS i)
{
const double dt = dt_func(i);
const double dF = LogisticJac(u_i, alpha, dt);
lambda *= dF;
};
double u = u0;
double t_phys = 0.0;
StepS i = 0;
while (t_phys < Tfinal)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t_phys += dt_func(i);
++i;
}
const StepS m = i;
const double u_m = u;
const double J = 0.5 * (u_m - target_s) * (u_m - target_s);
double lambda = (u_m - target_s); // terminal adjoint
double u_work = 0.0;
for (StepS j = m - 1; j >= 0; --j)
{
ckpt.BackwardStep(j, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
if (j == 0) { break; }
}
const double dJ_du0_adj = lambda;
// FD check
auto forward_only_J = [&](double u_init)
{
double uu = u_init;
double tt = 0.0;
StepS k = 0;
while (tt < Tfinal)
{
uu = LogisticStep(uu, alpha, dt_func(k));
tt += dt_func(k);
++k;
}
const double r = uu - target_s;
return 0.5 * r * r;
};
const double Jp = forward_only_J(u0 + eps);
const double Jm = forward_only_J(u0 - eps);
const double dJ_du0_fd = (Jp - Jm) / (2.0 * eps);
mfem::out << "\n[FileIO][Scalar]\n";
mfem::out << " m steps = " << m << "\n";
mfem::out << " u_m = " << u_m << "\n";
mfem::out << " J = " << J << "\n";
mfem::out << " dJ/du0 adjoint = " << dJ_du0_adj << "\n";
mfem::out << " dJ/du0 FD = " << dJ_du0_fd << "\n";
mfem::out << " abs err = " << std::abs(dJ_du0_adj - dJ_du0_fd) << "\n";
}
// ============================================================
// B) Vector run (mfem::Vector) with FileCheckpointStorage
// ============================================================
{
mfem::FileCheckpointStorage<mfem::Vector> storage(dir_vector, "ckpt_", ".bin", true, keep_files);
mfem::DynamicCheckpointing<mfem::Vector, mfem::FileCheckpointStorage<mfem::Vector>> ckpt(s, storage);
using StepV = mfem::DynamicCheckpointing<mfem::Vector, mfem::FileCheckpointStorage<mfem::Vector>>::Step;
auto dt_func_v = [&](StepV i)
{
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
return dt;
};
auto make_snapshot = [](const mfem::Vector &u) { return u; };
auto restore_snapshot = [](const mfem::Vector &snap, mfem::Vector &out) { out = snap; };
auto primal_step = [&](mfem::Vector &u, StepV i)
{
const double dt = dt_func_v(i);
for (int j = 0; j < u.Size(); ++j)
{
u[j] = LogisticStep(u[j], alpha, dt);
}
};
auto adjoint_step = [&](mfem::Vector &lambda, const mfem::Vector &u_i, StepV i)
{
const double dt = dt_func_v(i);
MFEM_ASSERT(lambda.Size() == u_i.Size(), "Size mismatch.");
for (int j = 0; j < lambda.Size(); ++j)
{
lambda[j] *= LogisticJac(u_i[j], alpha, dt);
}
};
mfem::Vector u0v(n), ut(n);
for (int j = 0; j < n; ++j) { u0v[j] = 0.2 + 0.05 * std::cos(0.7*(j+1)); }
ut = target_v;
mfem::Vector u = u0v;
double t_phys = 0.0;
StepV i = 0;
while (t_phys < Tfinal)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t_phys += dt_func_v(i);
++i;
}
const StepV m = i;
const mfem::Vector u_m = u;
mfem::Vector diff(u_m);
diff -= ut;
const double J = 0.5 * mfem::InnerProduct(diff, diff);
// backward
mfem::Vector lambda = diff; // terminal adjoint
mfem::Vector u_work(n);
for (StepV j = m - 1; j >= 0; --j)
{
ckpt.BackwardStep(j, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
if (j == 0) { break; }
}
const mfem::Vector &grad_u0 = lambda;
// directional FD check
mfem::Vector v(n);
for (int j = 0; j < n; ++j) { v[j] = std::sin(0.3*(j+1)) + 0.1; }
auto forward_only_J = [&](const mfem::Vector &u_init)
{
mfem::Vector uu = u_init;
double tt = 0.0;
StepV k = 0;
while (tt < Tfinal)
{
primal_step(uu, k);
tt += dt_func_v(k);
++k;
}
mfem::Vector dd(uu);
dd -= ut;
return 0.5 * mfem::InnerProduct(dd, dd);
};
mfem::Vector u_plus(u0v), u_minus(u0v);
u_plus.Add(eps, v);
u_minus.Add(-eps, v);
const double Jp = forward_only_J(u_plus);
const double Jm = forward_only_J(u_minus);
const double dJ_dir_fd = (Jp - Jm) / (2.0 * eps);
const double dJ_dir_adj = mfem::InnerProduct(grad_u0, v);
mfem::out << "\n[FileIO][Vector]\n";
mfem::out << " m steps = " << m << "\n";
mfem::out << " J = " << J << "\n";
mfem::out << " v·grad adjoint = " << dJ_dir_adj << "\n";
mfem::out << " FD directional = " << dJ_dir_fd << "\n";
mfem::out << " abs err = " << std::abs(dJ_dir_adj - dJ_dir_fd) << "\n";
mfem::out << " ||u_m||_2 = " << u_m.Norml2() << "\n";
mfem::out << " ||u_m - u_target||2 = " << diff.Norml2() << "\n";
}
mfem::out << "\nDone.\n";
return 0;
}
@@ -1,375 +0,0 @@
#include "mfem.hpp"
#include "dynamic_checkpointing.hpp"
#include "fixed_slot_checkpoint_storage.hpp"
#include <cmath>
#include <iomanip>
#include <string>
using namespace mfem;
// Logistic ODE (scalar or componentwise):
// du/dt = alpha*u*(1-u)
// Explicit Euler:
// u_{i+1} = u_i + dt_i * alpha*u_i*(1-u_i)
//
// Objective:
// Scalar: J = 0.5*(u_m - target)^2
// Vector: J = 0.5*||u_m - u_target||^2
//
// Discrete adjoint:
// dF/du = 1 + dt_i*alpha*(1 - 2*u_i)
// lambda_i = (dF/du at u_i) * lambda_{i+1}
static inline double LogisticStep(const double u, const double alpha, const double dt)
{
return u + dt * alpha * u * (1.0 - u);
}
static inline double LogisticJac(const double u, const double alpha, const double dt)
{
return 1.0 + dt * alpha * (1.0 - 2.0*u);
}
// --------------------------
// Scalar run (double)
// --------------------------
template <typename Storage>
static void RunScalarFixedSlot(int s,
Storage &storage,
double alpha,
double dt0,
double omega,
double Tfinal,
double u0,
double target,
double eps)
{
using CKPT = mfem::DynamicCheckpointing<double, Storage>;
using Step = typename CKPT::Step;
CKPT ckpt(s, storage);
auto dt_func = [&](Step i)
{
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
return dt;
};
auto make_snapshot = [](const double &u) { return u; };
auto restore_snapshot = [](const double &snap, double &out) { out = snap; };
auto primal_step = [&](double &u, Step i)
{
const double dt = dt_func(i);
u = LogisticStep(u, alpha, dt);
};
auto adjoint_step = [&](double &lambda, const double &u_i, Step i)
{
const double dt = dt_func(i);
lambda *= LogisticJac(u_i, alpha, dt);
};
// Forward sweep (unknown m; stop on accumulated physical time)
double u = u0;
double t_phys = 0.0;
Step i = 0;
while (t_phys < Tfinal)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t_phys += dt_func(i);
++i;
}
const Step m = i;
const double u_m = u;
const double J = 0.5 * (u_m - target) * (u_m - target);
// Backward sweep
MFEM_VERIFY(m > 0, "Scalar run produced m=0 steps.");
double lambda = (u_m - target); // terminal condition dJ/du_m
double u_work = 0.0;
for (Step j = m - 1; j >= 0; --j)
{
ckpt.BackwardStep(j, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
if (j == 0) { break; }
}
const double dJ_du0_adj = lambda;
// FD check
auto forward_only_J = [&](double u_init)
{
double uu = u_init;
double tt = 0.0;
Step k = 0;
while (tt < Tfinal)
{
uu = LogisticStep(uu, alpha, dt_func(k));
tt += dt_func(k);
++k;
}
const double r = uu - target;
return 0.5 * r * r;
};
const double Jp = forward_only_J(u0 + eps);
const double Jm = forward_only_J(u0 - eps);
const double dJ_du0_fd = (Jp - Jm) / (2.0 * eps);
mfem::out << "\n[FixedSlot][Scalar]\n";
mfem::out << " m steps = " << m << "\n";
mfem::out << " t_phys = " << t_phys << "\n";
mfem::out << " u_m = " << u_m << "\n";
mfem::out << " J = " << J << "\n";
mfem::out << " dJ/du0 adjoint = " << dJ_du0_adj << "\n";
mfem::out << " dJ/du0 FD = " << dJ_du0_fd << "\n";
mfem::out << " abs err = " << std::abs(dJ_du0_adj - dJ_du0_fd) << "\n";
}
// --------------------------
// Vector run (mfem::Vector)
// --------------------------
template <typename Storage>
static void RunVectorFixedSlot(int s,
Storage &storage,
int n,
double alpha,
double dt0,
double omega,
double Tfinal,
double target_val,
double eps)
{
using CKPT = mfem::DynamicCheckpointing<mfem::Vector, Storage>;
using Step = typename CKPT::Step;
CKPT ckpt(s, storage);
auto dt_func = [&](Step i)
{
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
return dt;
};
auto make_snapshot = [](const mfem::Vector &u) { return u; };
auto restore_snapshot = [](const mfem::Vector &snap, mfem::Vector &out) { out = snap; };
auto primal_step = [&](mfem::Vector &u, Step i)
{
const double dt = dt_func(i);
for (int j = 0; j < u.Size(); ++j)
{
u[j] = LogisticStep(u[j], alpha, dt);
}
};
auto adjoint_step = [&](mfem::Vector &lambda, const mfem::Vector &u_i, Step i)
{
const double dt = dt_func(i);
MFEM_ASSERT(lambda.Size() == u_i.Size(), "lambda and u_i size mismatch.");
for (int j = 0; j < lambda.Size(); ++j)
{
lambda[j] *= LogisticJac(u_i[j], alpha, dt);
}
};
// Initial condition and target
mfem::Vector u0(n), u_target(n);
for (int j = 0; j < n; ++j)
{
u0[j] = 0.2 + 0.05 * std::cos(0.7 * (j + 1));
}
u_target = target_val;
// Forward sweep
mfem::Vector u = u0;
double t_phys = 0.0;
Step i = 0;
while (t_phys < Tfinal)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t_phys += dt_func(i);
++i;
}
const Step m = i;
const mfem::Vector u_m = u;
mfem::Vector diff(u_m);
diff -= u_target;
const double J = 0.5 * mfem::InnerProduct(diff, diff);
// Backward sweep
MFEM_VERIFY(m > 0, "Vector run produced m=0 steps.");
mfem::Vector lambda = diff; // terminal adjoint = dJ/du_m
mfem::Vector u_work(n);
for (Step j = m - 1; j >= 0; --j)
{
ckpt.BackwardStep(j, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
if (j == 0) { break; }
}
const mfem::Vector &grad_u0 = lambda;
// Directional FD check
mfem::Vector v(n);
for (int j = 0; j < n; ++j) { v[j] = std::sin(0.3 * (j + 1)) + 0.1; }
auto forward_only_J = [&](const mfem::Vector &u_init)
{
mfem::Vector uu = u_init;
double tt = 0.0;
Step k = 0;
while (tt < Tfinal)
{
primal_step(uu, k);
tt += dt_func(k);
++k;
}
mfem::Vector dd(uu);
dd -= u_target;
return 0.5 * mfem::InnerProduct(dd, dd);
};
mfem::Vector u_plus(u0), u_minus(u0);
u_plus.Add(eps, v);
u_minus.Add(-eps, v);
const double Jp = forward_only_J(u_plus);
const double Jm = forward_only_J(u_minus);
const double dJ_dir_fd = (Jp - Jm) / (2.0 * eps);
const double dJ_dir_adj = mfem::InnerProduct(grad_u0, v);
mfem::out << "\n[FixedSlot][Vector]\n";
mfem::out << " n = " << n << "\n";
mfem::out << " m steps = " << m << "\n";
mfem::out << " t_phys = " << t_phys << "\n";
mfem::out << " J = " << J << "\n";
mfem::out << " ||u_m||_2 = " << u_m.Norml2() << "\n";
mfem::out << " ||u_m-u_target|| = " << diff.Norml2() << "\n";
mfem::out << " v·grad adjoint = " << dJ_dir_adj << "\n";
mfem::out << " FD directional = " << dJ_dir_fd << "\n";
mfem::out << " abs err = " << std::abs(dJ_dir_adj - dJ_dir_fd) << "\n";
}
int main(int argc, char *argv[])
{
// Backend selection:
// 0 = fixed-slot memory (single RAM block)
// 1 = fixed-slot file (single file with fixed offsets)
int backend = 0;
// Common parameters
int s = 8; // number of REAL stored checkpoints
double alpha = 2.0;
double dt0 = 0.02;
double omega = 0.2;
double Tfinal = 1.0;
double eps = 1e-7;
// Scalar parameters
double u0 = 0.2;
double target_s = 0.7;
// Vector parameters
int n = 64; // must stay fixed for fixed-slot vector packing
double target_v = 0.7;
// File backend parameters
std::string scalar_file = "scalar_fixedslots.bin";
std::string vector_file = "vector_fixedslots.bin";
bool truncate_files = true;
bool flush_on_store = true; // safer for demo correctness with iostreams
OptionsParser args(argc, argv);
args.AddOption(&backend, "-b", "--backend",
"Backend: 0=memory fixed-slots, 1=file fixed-slots (single file).");
args.AddOption(&s, "-s", "--checkpoints", "Checkpoint budget s (real checkpoints).");
args.AddOption(&alpha, "-a", "--alpha", "Logistic growth alpha.");
args.AddOption(&dt0, "-dt0", "--dt0", "Base dt for dt(i)=dt0*(1+0.5*sin(omega*i)).");
args.AddOption(&omega, "-om", "--omega", "Omega for dt(i)=dt0*(1+0.5*sin(omega*i)).");
args.AddOption(&Tfinal, "-T", "--tfinal", "Stop when accumulated time reaches Tfinal.");
args.AddOption(&eps, "-eps", "--fd-eps", "FD epsilon.");
args.AddOption(&u0, "-u0", "--u0", "Scalar initial condition u0.");
args.AddOption(&target_s, "-ts", "--target-scalar", "Scalar target.");
args.AddOption(&n, "-n", "--size", "Vector dimension (fixed).");
args.AddOption(&target_v, "-tv", "--target-vector", "Vector target value per component.");
args.AddOption(&scalar_file, "-sf", "--scalar-file", "File for scalar fixed-slot storage.");
args.AddOption(&vector_file, "-vf", "--vector-file", "File for vector fixed-slot storage.");
args.AddOption(&truncate_files, "-tr", "--truncate", "-ntr", "--no-truncate",
"Truncate checkpoint files on startup.");
args.AddOption(&flush_on_store, "-fl", "--flush", "-nfl", "--no-flush",
"Flush on each Store() (demo-safety; slower).");
args.Parse();
if (!args.Good())
{
args.PrintUsage(mfem::out);
return 1;
}
args.PrintOptions(mfem::out);
MFEM_VERIFY(s > 0, "Need s > 0.");
MFEM_VERIFY(n > 0, "Need n > 0.");
MFEM_VERIFY(dt0 > 0.0, "Need dt0 > 0.");
MFEM_VERIFY(Tfinal > 0.0, "Need Tfinal > 0.");
mfem::out << std::setprecision(15);
if (backend == 0)
{
mfem::out << "\nUsing fixed-slot MEMORY backend (single RAM block)\n";
// Scalar: fixed-size POD => trivial packer
mfem::FixedSlotMemoryCheckpointStorage<double> stor_s(s);
RunScalarFixedSlot(s, stor_s, alpha, dt0, omega, Tfinal, u0, target_s, eps);
// Vector: fixed-size packing (n must remain constant)
mfem::FixedVectorPacker packer(n);
mfem::FixedSlotMemoryCheckpointStorage<mfem::Vector, mfem::FixedVectorPacker> stor_v(s, packer);
RunVectorFixedSlot(s, stor_v, n, alpha, dt0, omega, Tfinal, target_v, eps);
}
else if (backend == 1)
{
mfem::out << "\nUsing fixed-slot FILE backend (single file with fixed offsets)\n";
// Scalar file
mfem::FixedSlotFileCheckpointStorage<double> stor_s(scalar_file, s,
mfem::TrivialFixedPacker<double>(),
truncate_files,
flush_on_store);
RunScalarFixedSlot(s, stor_s, alpha, dt0, omega, Tfinal, u0, target_s, eps);
// Vector file (fixed-size packing with n)
mfem::FixedVectorPacker packer(n);
mfem::FixedSlotFileCheckpointStorage<mfem::Vector, mfem::FixedVectorPacker>
stor_v(vector_file, s, packer, truncate_files, flush_on_store);
RunVectorFixedSlot(s, stor_v, n, alpha, dt0, omega, Tfinal, target_v, eps);
}
else
{
MFEM_ABORT("Unknown backend. Use -b 0 (memory) or -b 1 (file).");
}
mfem::out << "\nDone.\n";
return 0;
}
@@ -1,178 +0,0 @@
#include "mfem.hpp"
#include "revolve_checkpointing.hpp"
#include <cmath>
#include <iomanip>
#include <string>
using namespace mfem;
// the forward state consists of vector and
// several additional values
struct State
{
mfem::real_t time = 0.0;
mfem::real_t obj = 0.0;
mfem::Vector v;
};
/// class to manage the Snaphot Layout
class StateSnapshotLayout
{
public:
// Constructor: n - size of the vector in the state
explicit StateSnapshotLayout(int n) : n_(n)
{
MFEM_VERIFY(n_ > 0, "StateSnapshotLayout: n must be > 0.");
}
// returns the total size of the snapshot in mfem::real_t
int N() const { return n_; }
// returns the total number of bytes necessary to store the state
int Bytes() const
{
return (2 + n_) * (int)sizeof(mfem::real_t);
}
// Pack State -> bytes (host bytes)
void Pack(const State &s, unsigned char *dst, int bytes) const
{
MFEM_VERIFY(dst != nullptr, "Pack: dst is null.");
MFEM_VERIFY(bytes == Bytes(), "Pack: snapshot byte size mismatch.");
MFEM_VERIFY(s.v.Size() == n_, "Pack: State.v size mismatch.");
// Ensure we read from host even if MFEM uses device memory.
const mfem::real_t *vh = s.v.HostRead();
std::memcpy(dst + 0*sizeof(mfem::real_t), &s.time, sizeof(mfem::real_t));
std::memcpy(dst + 1*sizeof(mfem::real_t), &s.obj, sizeof(mfem::real_t));
std::memcpy(dst + 2*sizeof(mfem::real_t),
vh, (std::size_t)n_ * sizeof(mfem::real_t));
}
// Unpack bytes -> State (host write)
void Unpack(const unsigned char *src, int bytes, State &out) const
{
MFEM_VERIFY(src != nullptr, "Unpack: src is null.");
MFEM_VERIFY(bytes == Bytes(), "Unpack: snapshot byte size mismatch.");
if (out.v.Size() != n_) { out.v.SetSize(n_); }
mfem::real_t *vh = out.v.HostWrite();
std::memcpy(&out.time, src + 0*sizeof(mfem::real_t), sizeof(mfem::real_t));
std::memcpy(&out.obj, src + 1*sizeof(mfem::real_t), sizeof(mfem::real_t));
std::memcpy(vh,
src + 2*sizeof(mfem::real_t),
(std::size_t)n_ * sizeof(mfem::real_t));
}
private:
int n_ = 0;
};
/* The REVOLVE manager (as provided earlier) uses this callback style:
* make_snapshot(const State&, uint8_t* out, size_t bytes)
* restore_snapshot(State&, const uint8_t* in, size_t bytes)
* primal_step(int step, State&)
* adjoint_step(int step, const State&, AdjointState&)
and a storage backend with:
* Save(slot, bytes)
* Load(slot, bytes)
REVOLVE manager will:
*request storing snapshots into checkpoint slots (takeshot)
*request restoring a checkpoint slot (restore)
*request recomputation forward (advance)
*then request the next adjoint step (firsturn / youturn)
all while using only (Ncheck) stored checkpoints.
Fixed-step REVOLVE manager:
* Snapshot is raw bytes packed/unpacked by your callbacks.
* Storage is a fixed indexed array (Save(slot) / Load(slot)),
because REVOLVE addresses checkpoints by slot index.
Best use when Nsteps is known in the begining of the simulation.
*/
int main(int argc, char *argv[])
{
// Backend selection:
// 0 = fixed-slot memory (single RAM block)
// 1 = fixed-slot file (single file with fixed offsets)
int backend = 0;
const int n = 100/* fixed State.v size */;
StateSnapshotLayout layout(n);
const std::size_t snapshot_bytes = (std::size_t)layout.Bytes();
const int Nsteps = 20 /* known number of time steps */;
const int Ncheck = 5 /* number of checkpoints (snaps) */;
// Memory backend (single block)
// FixedSlotMemoryStorage storage(Ncheck, snapshot_bytes);
// FixedStepRevolveCheckpointing<FixedSlotMemoryStorage>
// cktp(Nsteps, Ncheck, snapshot_bytes, storage);
// or file backend (single file)
FixedSlotFileStorage storage("revolve_ckpts.bin", Ncheck, snapshot_bytes);
FixedStepRevolveCheckpointing<FixedSlotFileStorage>
cktp(Nsteps, Ncheck, snapshot_bytes, storage);
auto make_snapshot = [&](const State &s, uint8_t *outb, std::size_t bytes)
{
MFEM_VERIFY(bytes == snapshot_bytes, "make_snapshot: byte size mismatch");
layout.Pack(s, reinterpret_cast<unsigned char*>(outb), (int)bytes);
};
auto restore_snapshot = [&](State &s, const uint8_t *inb, std::size_t bytes)
{
MFEM_VERIFY(bytes == snapshot_bytes, "restore_snapshot: byte size mismatch");
layout.Unpack(reinterpret_cast<const unsigned char*>(inb), (int)bytes, s);
};
// set the work state
State u_work;
u_work.v.SetSize(n); u_work.v=0.0;
State u;
u.v.SetSize(n); u.v=0.0; u.obj=0.0; u.time=0.0;
// init u.time, u.obj, u.v ...
auto primal_step = [&](int step, State &u_inout)
{
(void)step;
// advance u_inout -> u_{step+1}
};
struct AdjointState { /* ... */ };
AdjointState lambda;
auto adjoint_step = [&](int step, const State &u_step, AdjointState &lambda_inout)
{
(void)step;
// update lambda_{step+1} -> lambda_step using u_step
};
// Forward: i = 0..Nsteps-1
for (int i = 0; i < Nsteps; ++i)
{
cktp.ForwardStep(i, u, primal_step, make_snapshot);
}
// Reverse: i = Nsteps-1..0
for (int i = Nsteps - 1; i >= 0; --i)
{
cktp.BackwardStep(i, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
}
mfem::out << "\nDone.\n";
return 0;
};
-163
View File
@@ -1,163 +0,0 @@
#include "mfem.hpp"
#include "dynamic_checkpointing.hpp"
#include <cmath>
#include <iomanip>
using namespace mfem;
// Scalar logistic ODE:
// du/dt = alpha*u*(1-u)
// Explicit Euler with variable dt(i):
// u_{i+1} = u_i + dt_i*alpha*u_i*(1-u_i)
// Objective:
// J = 0.5*(u_m - target)^2
// Discrete adjoint (scalar):
// lambda_m = (u_m - target)
// lambda_i = (dF/du at u_i)^T * lambda_{i+1}
// dF/du = 1 + dt_i*alpha*(1 - 2*u_i)
int main(int argc, char *argv[])
{
int s = 3;
double alpha = 2.0;
double dt0 = 0.02;
double omega = 0.2;
double Tfinal = 1.0;
double u0 = 0.2;
double target = 0.7;
double eps = 1e-7;
OptionsParser args(argc, argv);
args.AddOption(&s, "-s", "--checkpoints", "Checkpoint budget s (real checkpoints).");
args.AddOption(&alpha, "-a", "--alpha", "Logistic growth alpha.");
args.AddOption(&dt0, "-dt0", "--dt0", "Base dt for dt(i)=dt0*(1+0.5*sin(omega*i)).");
args.AddOption(&omega, "-om", "--omega", "Omega in dt(i)=dt0*(1+0.5*sin(omega*i)).");
args.AddOption(&Tfinal, "-T", "--tfinal", "Terminate when accumulated time reaches Tfinal.");
args.AddOption(&u0, "-u0", "--u0", "Initial scalar state u0.");
args.AddOption(&target, "-ut", "--target", "Target value in J=0.5*(u_m-target)^2.");
args.AddOption(&eps, "-eps", "--fd-eps", "Finite-difference epsilon.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(mfem::out);
return 1;
}
args.PrintOptions(mfem::out);
MFEM_VERIFY(s > 0, "Need s > 0.");
MFEM_VERIFY(dt0 > 0.0, "Need dt0 > 0.");
MFEM_VERIFY(Tfinal > 0.0, "Need Tfinal > 0.");
using Step = mfem::DynamicCheckpointing<double>::Step;
auto dt_func = [&](Step i)
{
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
return dt;
};
// Checkpoint manager:
// State = double
// Snapshot= double
mfem::DynamicCheckpointing<double> ckpt(s);
auto make_snapshot = [](const double &u) -> double { return u; };
auto restore_snapshot = [](const double &snap, double &out) { out = snap; };
auto primal_step = [&](double &u, Step i)
{
const double dt = dt_func(i);
u = u + dt * alpha * u * (1.0 - u);
};
auto adjoint_step = [&](double &lambda, const double &u_i, Step i)
{
const double dt = dt_func(i);
const double dF_du = 1.0 + dt * alpha * (1.0 - 2.0 * u_i);
lambda = dF_du * lambda;
};
// ---------------- Forward sweep (unknown m) ----------------
double u = u0;
double t_phys = 0.0;
Step i = 0;
while (t_phys < Tfinal)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t_phys += dt_func(i);
++i;
}
const Step m = i;
const double u_m = u;
const double J = 0.5 * (u_m - target) * (u_m - target);
mfem::out << std::setprecision(15);
mfem::out << "\n[Scalar] Forward finished:\n";
mfem::out << " m (steps) = " << m << "\n";
mfem::out << " t_phys = " << t_phys << "\n";
mfem::out << " u_m = " << u_m << "\n";
mfem::out << " J = " << J << "\n\n";
mfem::out << "[Scalar] Checkpoint set after forward sweep (step, level, stored):\n";
for (const auto &cp : ckpt.GetCheckpointInfo())
{
mfem::out << " step=" << cp.step
<< ", level=" << cp.level
<< ", stored=" << (cp.stored ? "yes" : "no")
<< (cp.stored ? "" : " (placeholder)")
<< "\n";
}
mfem::out << "\n";
MFEM_VERIFY(m > 0, "Forward produced m=0 steps; nothing to do.");
// ---------------- Backward sweep (adjoint) ----------------
double lambda = (u_m - target); // terminal adjoint = dJ/du_m
double u_work = 0.0; // scratch primal state u_i
for (Step j = m - 1; j >= 0; --j)
{
ckpt.BackwardStep(j, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
if (j == 0) { break; } // avoid signed underflow
}
const double dJ_du0_adjoint = lambda;
// ---------------- Finite-difference gradient check ----------------
auto forward_only_J = [&](double u_init)
{
double uu = u_init;
double tt = 0.0;
Step k = 0;
while (tt < Tfinal)
{
primal_step(uu, k);
tt += dt_func(k);
++k;
}
const double r = (uu - target);
return 0.5 * r * r;
};
const double Jp = forward_only_J(u0 + eps);
const double Jm = forward_only_J(u0 - eps);
const double dJ_du0_fd = (Jp - Jm) / (2.0 * eps);
const double abs_err = std::abs(dJ_du0_adjoint - dJ_du0_fd);
const double rel_err = abs_err / (std::abs(dJ_du0_fd) + 1e-30);
mfem::out << "[Scalar] Gradient check (dJ/du0):\n";
mfem::out << " adjoint = " << dJ_du0_adjoint << "\n";
mfem::out << " FD = " << dJ_du0_fd << "\n";
mfem::out << " abs err = " << abs_err << "\n";
mfem::out << " rel err = " << rel_err << "\n\n";
return 0;
}
@@ -1,306 +0,0 @@
#include "mfem.hpp"
#include "dynamic_checkpointing.hpp"
#include "segment_checkpoint_storage.hpp"
#include <cmath>
#include <iomanip>
using namespace mfem;
static double LogisticStep(double u, double alpha, double dt)
{
return u + dt * alpha * u * (1.0 - u);
}
static double LogisticJac(double u, double alpha, double dt)
{
return 1.0 + dt * alpha * (1.0 - 2.0*u);
}
#if MFEM_HAVE_FILESYSTEM
static void PurgeDir(const std::string &dir)
{
std::error_code ec;
mfem_fs::remove_all(mfem_fs::path(dir), ec);
mfem_fs::create_directories(mfem_fs::path(dir), ec);
MFEM_VERIFY(!ec, "Failed to purge/create directory.");
}
#endif
int main(int argc, char *argv[])
{
int s = 3;
double dt0 = 0.02;
double omega = 0.2;
double Tfinal = 1.0;
// scalar params
double alpha = 2.0;
double u0 = 0.2;
double target_s = 0.7;
double eps = 1e-7;
// vector params
int n = 16;
double target_v = 0.7;
// segmented storage params
std::string dir_scalar = "chk_scalar_segments";
std::string dir_vector = "chk_vector_segments";
long long records_per_file = 4096;
bool keep_files = false;
bool purge_dirs = true;
OptionsParser args(argc, argv);
args.AddOption(&s, "-s", "--checkpoints", "Checkpoint budget s (real checkpoints).");
args.AddOption(&dt0, "-dt0", "--dt0", "Base dt for dt(i)=dt0*(1+0.5*sin(omega*i)).");
args.AddOption(&omega, "-om", "--omega", "Omega for dt(i)=dt0*(1+0.5*sin(omega*i)).");
args.AddOption(&Tfinal, "-T", "--tfinal", "Stop when accumulated time reaches Tfinal.");
args.AddOption(&alpha, "-a", "--alpha", "Logistic growth alpha.");
args.AddOption(&u0, "-u0", "--u0", "Scalar initial u0.");
args.AddOption(&target_s, "-ts", "--target-scalar", "Scalar target.");
args.AddOption(&eps, "-eps", "--fd-eps", "FD epsilon (scalar and vector directional).");
args.AddOption(&n, "-n", "--size", "Vector dimension.");
args.AddOption(&target_v, "-tv", "--target-vector", "Vector target value per component.");
args.AddOption(&dir_scalar, "-ds", "--dir-scalar", "Directory for scalar segment files.");
args.AddOption(&dir_vector, "-dv", "--dir-vector", "Directory for vector segment files.");
args.AddOption(&records_per_file, "-rpf", "--records-per-file",
"How many handles belong to one segment file (range size).");
args.AddOption(&keep_files, "-k", "--keep-files", "-nk", "--no-keep-files",
"Keep segment files (debug).");
args.AddOption(&purge_dirs, "-p", "--purge-dirs", "-np", "--no-purge-dirs",
"Purge checkpoint directories at start (recommended).");
args.Parse();
if (!args.Good())
{
args.PrintUsage(mfem::out);
return 1;
}
args.PrintOptions(mfem::out);
MFEM_VERIFY(s > 0, "Need s > 0.");
MFEM_VERIFY(records_per_file > 0, "Need records_per_file > 0.");
#if MFEM_HAVE_FILESYSTEM
if (purge_dirs)
{
PurgeDir(dir_scalar);
PurgeDir(dir_vector);
}
#else
MFEM_VERIFY(!purge_dirs, "purge_dirs requires <filesystem> support.");
#endif
using StepS = mfem::DynamicCheckpointing<
double,
mfem::SegmentedFileCheckpointStorage<double>>::Step;
auto dt_func = [&](StepS i)
{
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
return dt;
};
mfem::out << std::setprecision(15);
// ============================================================
// A) Scalar with segmented storage
// ============================================================
mfem::SegmentedFileCheckpointStorage<double> stor_s(dir_scalar, records_per_file,
"seg_", ".bin", true, keep_files);
{
mfem::DynamicCheckpointing<double, mfem::SegmentedFileCheckpointStorage<double>> ckpt(s, stor_s);
auto make_snapshot = [](const double &u) { return u; };
auto restore_snapshot = [](const double &snap, double &out) { out = snap; };
auto primal_step = [&](double &u, StepS i)
{
u = LogisticStep(u, alpha, dt_func(i));
};
auto adjoint_step = [&](double &lambda, const double &u_i, StepS i)
{
lambda *= LogisticJac(u_i, alpha, dt_func(i));
};
double u = u0;
double t_phys = 0.0;
StepS i = 0;
while (t_phys < Tfinal)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t_phys += dt_func(i);
++i;
}
const StepS m = i;
const double u_m = u;
const double J = 0.5 * (u_m - target_s) * (u_m - target_s);
double lambda = (u_m - target_s);
double u_work = 0.0;
for (StepS j = m - 1; j >= 0; --j)
{
ckpt.BackwardStep(j, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
if (j == 0) { break; }
}
const double dJ_du0_adj = lambda;
auto forward_only_J = [&](double u_init)
{
double uu = u_init;
double tt = 0.0;
StepS k = 0;
while (tt < Tfinal)
{
uu = LogisticStep(uu, alpha, dt_func(k));
tt += dt_func(k);
++k;
}
const double r = uu - target_s;
return 0.5 * r * r;
};
const double Jp = forward_only_J(u0 + eps);
const double Jm = forward_only_J(u0 - eps);
const double dJ_du0_fd = (Jp - Jm) / (2.0 * eps);
mfem::out << "\n[SegmentIO][Scalar]\n";
mfem::out << " m steps = " << m << "\n";
mfem::out << " J = " << J << "\n";
mfem::out << " dJ/du0 adjoint = " << dJ_du0_adj << "\n";
mfem::out << " dJ/du0 FD = " << dJ_du0_fd << "\n";
mfem::out << " abs err = " << std::abs(dJ_du0_adj - dJ_du0_fd) << "\n";
}
// ============================================================
// B) Vector with segmented storage
// ============================================================
mfem::SegmentedFileCheckpointStorage<mfem::Vector> stor_v(dir_vector, records_per_file,
"seg_", ".bin", true, keep_files);
{
mfem::DynamicCheckpointing<mfem::Vector, mfem::SegmentedFileCheckpointStorage<mfem::Vector>>
ckpt(s, stor_v);
using StepV = mfem::DynamicCheckpointing<
mfem::Vector,
mfem::SegmentedFileCheckpointStorage<mfem::Vector>>::Step;
auto dt_func_v = [&](StepV i)
{
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
return dt;
};
auto make_snapshot = [](const mfem::Vector &u) { return u; };
auto restore_snapshot = [](const mfem::Vector &snap, mfem::Vector &out) { out = snap; };
auto primal_step = [&](mfem::Vector &u, StepV i)
{
const double dt = dt_func_v(i);
for (int j = 0; j < u.Size(); ++j)
{
u[j] = LogisticStep(u[j], alpha, dt);
}
};
auto adjoint_step = [&](mfem::Vector &lambda, const mfem::Vector &u_i, StepV i)
{
const double dt = dt_func_v(i);
MFEM_ASSERT(lambda.Size() == u_i.Size(), "Size mismatch.");
for (int j = 0; j < lambda.Size(); ++j)
{
lambda[j] *= LogisticJac(u_i[j], alpha, dt);
}
};
mfem::Vector u0v(n), ut(n);
for (int j = 0; j < n; ++j) { u0v[j] = 0.2 + 0.05 * std::cos(0.7*(j+1)); }
ut = target_v;
mfem::Vector u = u0v;
double t_phys = 0.0;
StepV i = 0;
while (t_phys < Tfinal)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t_phys += dt_func_v(i);
++i;
}
const StepV m = i;
mfem::Vector diff(u);
diff -= ut;
const double J = 0.5 * mfem::InnerProduct(diff, diff);
mfem::Vector lambda = diff;
mfem::Vector u_work(n);
for (StepV j = m - 1; j >= 0; --j)
{
ckpt.BackwardStep(j, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
if (j == 0) { break; }
}
const mfem::Vector &grad_u0 = lambda;
mfem::Vector v(n);
for (int j = 0; j < n; ++j) { v[j] = std::sin(0.3*(j+1)) + 0.1; }
auto forward_only_J = [&](const mfem::Vector &u_init)
{
mfem::Vector uu = u_init;
double tt = 0.0;
StepV k = 0;
while (tt < Tfinal)
{
primal_step(uu, k);
tt += dt_func_v(k);
++k;
}
mfem::Vector dd(uu);
dd -= ut;
return 0.5 * mfem::InnerProduct(dd, dd);
};
mfem::Vector u_plus(u0v), u_minus(u0v);
u_plus.Add(eps, v);
u_minus.Add(-eps, v);
const double Jp = forward_only_J(u_plus);
const double Jm = forward_only_J(u_minus);
const double dJ_dir_fd = (Jp - Jm) / (2.0 * eps);
const double dJ_dir_adj = mfem::InnerProduct(grad_u0, v);
mfem::out << "\n[SegmentIO][Vector]\n";
mfem::out << " m steps = " << m << "\n";
mfem::out << " J = " << J << "\n";
mfem::out << " v·grad adjoint = " << dJ_dir_adj << "\n";
mfem::out << " FD directional = " << dJ_dir_fd << "\n";
mfem::out << " abs err = " << std::abs(dJ_dir_adj - dJ_dir_fd) << "\n";
mfem::out << " ||u - u_target||2 = " << diff.Norml2() << "\n";
}
// Optional cleanup: delete segment files this run touched
if (!keep_files)
{
stor_s.PurgeAllFiles();
stor_v.PurgeAllFiles();
}
mfem::out << "\nDone.\n";
return 0;
}
-193
View File
@@ -1,193 +0,0 @@
#include "mfem.hpp"
#include "dynamic_checkpointing.hpp"
#include <cmath>
#include <iomanip>
using namespace mfem;
// Vector logistic ODE (componentwise):
// du/dt = alpha*u*(1-u) (applied to each component)
// Explicit Euler:
// u_{i+1}[j] = u_i[j] + dt_i*alpha*u_i[j]*(1-u_i[j])
// Objective:
// J = 0.5*||u_m - u_target||^2
// Terminal adjoint:
// lambda_m = u_m - u_target
// Adjoint step (componentwise):
// lambda_i[j] = (1 + dt_i*alpha*(1 - 2*u_i[j])) * lambda_{i+1}[j]
int main(int argc, char *argv[])
{
int s = 3;
int n = 16;
double alpha = 2.0;
double dt0 = 0.02;
double omega = 0.2;
double Tfinal = 1.0;
double target_val = 0.7;
double eps = 1e-7;
OptionsParser args(argc, argv);
args.AddOption(&s, "-s", "--checkpoints", "Checkpoint budget s (real checkpoints).");
args.AddOption(&n, "-n", "--size", "Vector dimension n.");
args.AddOption(&alpha, "-a", "--alpha", "Logistic growth alpha.");
args.AddOption(&dt0, "-dt0", "--dt0", "Base dt for dt(i)=dt0*(1+0.5*sin(omega*i)).");
args.AddOption(&omega, "-om", "--omega", "Omega in dt(i)=dt0*(1+0.5*sin(omega*i)).");
args.AddOption(&Tfinal, "-T", "--tfinal", "Terminate when accumulated time reaches Tfinal.");
args.AddOption(&target_val,"-tv", "--target", "Target value for each component.");
args.AddOption(&eps, "-eps", "--fd-eps", "Finite-difference epsilon (directional).");
args.Parse();
if (!args.Good())
{
args.PrintUsage(mfem::out);
return 1;
}
args.PrintOptions(mfem::out);
MFEM_VERIFY(s > 0, "Need s > 0.");
MFEM_VERIFY(n > 0, "Need n > 0.");
MFEM_VERIFY(dt0 > 0.0, "Need dt0 > 0.");
MFEM_VERIFY(Tfinal > 0.0, "Need Tfinal > 0.");
using Step = mfem::DynamicCheckpointing<mfem::Vector>::Step;
auto dt_func = [&](Step i)
{
const double dt = dt0 * (1.0 + 0.5 * std::sin(omega * double(i)));
MFEM_VERIFY(dt > 0.0, "dt_func produced non-positive dt.");
return dt;
};
// Checkpoint manager:
// State = mfem::Vector
// Snapshot= mfem::Vector
mfem::DynamicCheckpointing<mfem::Vector> ckpt(s);
auto make_snapshot = [](const mfem::Vector &u) -> mfem::Vector { return u; };
auto restore_snapshot = [](const mfem::Vector &snap, mfem::Vector &out) { out = snap; };
auto primal_step = [&](mfem::Vector &u, Step i)
{
const double dt = dt_func(i);
for (int j = 0; j < u.Size(); ++j)
{
const double uj = u[j];
u[j] = uj + dt * alpha * uj * (1.0 - uj);
}
};
auto adjoint_step = [&](mfem::Vector &lambda, const mfem::Vector &u_i, Step i)
{
const double dt = dt_func(i);
MFEM_ASSERT(lambda.Size() == u_i.Size(), "lambda and u_i size mismatch");
for (int j = 0; j < lambda.Size(); ++j)
{
const double dF_du = 1.0 + dt * alpha * (1.0 - 2.0 * u_i[j]);
lambda[j] *= dF_du;
}
};
// Initial condition and target
mfem::Vector u0(n), u_target(n);
for (int j = 0; j < n; ++j)
{
u0[j] = 0.2 + 0.05 * std::cos(0.7 * (j + 1));
}
u_target = target_val;
// ---------------- Forward sweep (unknown m) ----------------
mfem::Vector u = u0;
double t_phys = 0.0;
Step i = 0;
while (t_phys < Tfinal)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t_phys += dt_func(i);
++i;
}
const Step m = i;
const mfem::Vector u_m = u;
mfem::Vector diff(u_m);
diff -= u_target;
const double J = 0.5 * mfem::InnerProduct(diff, diff);
mfem::out << std::setprecision(15);
mfem::out << "\n[Vector] Forward finished:\n";
mfem::out << " m (steps) = " << m << "\n";
mfem::out << " t_phys = " << t_phys << "\n";
mfem::out << " J = " << J << "\n";
mfem::out << " ||u_m|| = " << u_m.Norml2() << "\n";
mfem::out << " ||u_m-ut||= " << diff.Norml2() << "\n\n";
mfem::out << "[Vector] Checkpoint set after forward sweep (step, level, stored):\n";
for (const auto &cp : ckpt.GetCheckpointInfo())
{
mfem::out << " step=" << cp.step
<< ", level=" << cp.level
<< ", stored=" << (cp.stored ? "yes" : "no")
<< (cp.stored ? "" : " (placeholder)")
<< "\n";
}
mfem::out << "\n";
MFEM_VERIFY(m > 0, "Forward produced m=0 steps; nothing to do.");
// ---------------- Backward sweep (adjoint) ----------------
mfem::Vector lambda = diff; // terminal = dJ/du_m
mfem::Vector u_work(n); // scratch primal u_i
for (Step j = m - 1; j >= 0; --j)
{
ckpt.BackwardStep(j, lambda, u_work,
primal_step, adjoint_step,
make_snapshot, restore_snapshot);
if (j == 0) { break; }
}
const mfem::Vector &grad_u0 = lambda;
// ---------------- Directional FD check ----------------
mfem::Vector v(n);
for (int j = 0; j < n; ++j) { v[j] = std::sin(0.3 * (j + 1)) + 0.1; }
auto forward_only_J = [&](const mfem::Vector &u_init)
{
mfem::Vector uu = u_init;
double tt = 0.0;
Step k = 0;
while (tt < Tfinal)
{
primal_step(uu, k);
tt += dt_func(k);
++k;
}
mfem::Vector dd(uu);
dd -= u_target;
return 0.5 * mfem::InnerProduct(dd, dd);
};
mfem::Vector u_plus(u0), u_minus(u0);
u_plus.Add(eps, v);
u_minus.Add(-eps, v);
const double Jp = forward_only_J(u_plus);
const double Jm = forward_only_J(u_minus);
const double dJ_dir_fd = (Jp - Jm) / (2.0 * eps);
const double dJ_dir_adj = mfem::InnerProduct(grad_u0, v);
const double abs_err = std::abs(dJ_dir_adj - dJ_dir_fd);
const double rel_err = abs_err / (std::abs(dJ_dir_fd) + 1e-30);
mfem::out << "[Vector] Directional derivative check:\n";
mfem::out << " v·grad adjoint = " << dJ_dir_adj << "\n";
mfem::out << " FD directional = " << dJ_dir_fd << "\n";
mfem::out << " abs err = " << abs_err << "\n";
mfem::out << " rel err = " << rel_err << "\n\n";
return 0;
}
-37
View File
@@ -1,37 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
if(MFEM_USE_MPI)
list(APPEND EX_SOURCES linear_elasticity.hpp)
list(APPEND EX_HEADERS linear_elasticity.cpp)
convert_filenames_to_full_paths(EX_SOURCES)
convert_filenames_to_full_paths(EX_HEADERS)
set(EX_COMMON_FILES
EXTRA_SOURCES ${EX_SOURCES}
EXTRA_HEADERS ${EX_HEADERS})
add_mfem_miniapp(mtop_test_forward_le
MAIN test_forward_le.cpp
${EX_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(mtop_test_adjoint_le
MAIN test_adjoint_le.cpp
${EX_COMMON_FILES}
LIBRARIES mfem)
endif (MFEM_USE_MPI)
-555
View File
@@ -1,555 +0,0 @@
$MeshFormat
2.2 0 8
$EndMeshFormat
$Nodes
267
1 -5 -5 0
2 5 -5 0
3 5 0 0
4 3 0 0
5 2.5 0 0
6 0.25 0 0
7 -0.25 0 0
8 -2.5 0 0
9 -3 0 0
10 -5 0 0
11 -4.5 -5 0
12 -4 -5 0
13 -3.5 -5 0
14 -3 -5 0
15 -2.5 -5 0
16 -2 -5 0
17 -1.5 -5 0
18 -1 -5 0
19 -0.5 -5 0
20 0 -5 0
21 0.5 -5 0
22 1 -5 0
23 1.5 -5 0
24 2 -5 0
25 2.5 -5 0
26 3 -5 0
27 3.5 -5 0
28 4 -5 0
29 4.5 -5 0
30 5 -4.5 0
31 5 -4 0
32 5 -3.5 0
33 5 -3 0
34 5 -2.5 0
35 5 -2 0
36 5 -1.5 0
37 5 -1 0
38 5 -0.5 0
39 4.5 0 0
40 4 0 0
41 3.5 0 0
42 2.75 0 0
43 2.125 0 0
44 1.75 0 0
45 1.375 0 0
46 1 0 0
47 0.625 0 0
48 0 0 0
49 -0.625 0 0
50 -1 0 0
51 -1.375 0 0
52 -1.75 0 0
53 -2.125 0 0
54 -2.75 0 0
55 -3.5 0 0
56 -4 0 0
57 -4.5 0 0
58 -5 -0.5 0
59 -5 -1 0
60 -5 -1.5 0
61 -5 -2 0
62 -5 -2.5 0
63 -5 -3 0
64 -5 -3.5 0
65 -5 -4 0
66 -5 -4.5 0
67 3.147692481109132e-08 -2.865638549497702 0
68 0.7411835869316199 -1.439094140802561 0
69 -0.7411835350444509 -1.43909394139115 0
70 4.165134723562585e-08 -1.07528086308038 0
71 0.6217064619398043 -3.063797051940143 0
72 1.152592324474185 -3.183434480740315 0
73 1.65143516768565 -3.26705431245132 0
74 2.136365371184163 -3.328666155776893 0
75 2.615028312537451 -3.375240988133206 0
76 3.09130721617413 -3.411145687677366 0
77 3.567287338332451 -3.439448189733019 0
78 4.043963758935188 -3.462490104151034 0
79 4.521595026340442 -3.482145270350558 0
80 4.53664432455652 -1.978600022703204 0
81 4.07646042959763 -1.955552774759151 0
82 3.622473657533698 -1.929092238665446 0
83 3.177227845469996 -1.897332899989072 0
84 2.741992072859029 -1.858107555374913 0
85 2.315943299837527 -1.808659406432935 0
86 1.898361004221733 -1.745502414617874 0
87 1.49145086264214 -1.664634388017854 0
88 1.101775323220076 -1.562562613115749 0
89 0.353776448027755 -2.251637070635797 0
90 0.5817531180500853 -1.81803370667541 0
91 5.893865315616141e-08 -3.688498271511504 0
92 4.16589230598604e-08 -4.361309501422365 0
93 -0.6217063447015869 -3.063796759607709 0
94 -1.152592159125127 -3.183434038351562 0
95 -1.651434963884719 -3.267053810340575 0
96 -2.136365141575386 -3.328665661557225 0
97 -2.61502807124587 -3.375240540152028 0
98 -3.091306983305333 -3.411145327000243 0
99 -3.567287131691835 -3.439447930570905 0
100 -4.043963594137694 -3.462489938776664 0
101 -4.521594918346396 -3.482145167593328 0
102 -0.3537764846052657 -2.25163666228703 0
103 -0.5817531278520051 -1.818033637502388 0
104 -4.536644079337935 -1.978599647807268 0
105 -4.076459846340027 -1.955551693229819 0
106 -3.62247296968287 -1.929090872966037 0
107 -3.177227175417933 -1.897331476171343 0
108 -2.741991512175473 -1.858106255217367 0
109 -2.315942871426326 -1.808658299363782 0
110 -1.898360705274605 -1.745501533735374 0
111 -1.491450676964205 -1.664633739553477 0
112 -1.101775219860154 -1.562562160656453 0
113 0.4257768789585238 -1.302334772873867 0
114 0.1721672606205888 -1.17264922719834 0
115 -0.4257768283604875 -1.302334697557239 0
116 -0.1721671949129244 -1.172649169444562 0
117 5.039529297540552e-08 -0.8795314788631696 0
118 4.399248353403805e-08 -0.6140653765645073 0
119 2.391773920807489e-08 -0.3141333775040666 0
120 -0.8478847531712174 -1.078320812844798 0
121 -0.9182559238223678 -0.7209963330169558 0
122 -0.9651087839812744 -0.3619977550022322 0
123 0.8478848525935928 -1.078321055602277 0
124 0.9182560388765455 -0.7209965569029586 0
125 0.9651088678468086 -0.3619978973464902 0
126 0.8006710117138355 -2.47676624955276 0
127 0.9687509599230363 -1.991490373658575 0
128 1.267199118780179 -2.624331559216512 0
129 1.386971133363275 -2.122717577561095 0
130 1.733121009957757 -2.726002099497514 0
131 1.819410451523041 -2.220141767339956 0
132 2.196724740979399 -2.79935877814003 0
133 2.258923990363529 -2.292999983602781 0
134 2.659538340537992 -2.854063574089099 0
135 2.703484150533682 -2.348367764794451 0
136 3.123294379466398 -2.895966051131377 0
137 3.153232485979804 -2.391299851091255 0
138 3.589167673689383 -2.928956418122387 0
139 3.608560261518269 -2.425426828148212 0
140 4.057538435196422 -2.955867592895155 0
141 4.069039111160356 -2.453487152690021 0
142 4.528077399266859 -2.978924099167553 0
143 4.533425113711518 -2.477698347634654 0
144 0.5529057974518461 -3.729502166598679 0
145 1.08019255813471 -3.784879970197416 0
146 1.588026898054561 -3.835138377533823 0
147 2.08405323877662 -3.876448947726767 0
148 2.573544992310209 -3.909301557064001 0
149 3.059798426647599 -3.93522570140068 0
150 3.544781312614329 -3.955866580867792 0
151 4.029558163742809 -3.972704995723558 0
152 4.514598481900658 -3.98704568763109 0
153 0.5207429539874712 -4.372050944340369 0
154 1.034280493174569 -4.392852449659338 0
155 1.539989110360686 -4.415218583983825 0
156 2.039762094695762 -4.435343846034065 0
157 2.535732622385146 -4.452137363307919 0
158 3.029570711380306 -4.465715840583259 0
159 3.522401092498168 -4.476640912585935 0
160 4.014893345389466 -4.485580909772104 0
161 4.507386261505489 -4.493183790689629 0
162 -0.5529056692413425 -3.729501971224406 0
163 -0.5207428615254834 -4.372050820213267 0
164 -1.080192412465082 -3.784879670455047 0
165 -1.034280383041777 -4.392852235768746 0
166 -1.588026734554675 -3.835138038639476 0
167 -1.539988984956153 -4.4152183290159 0
168 -2.084053063491914 -3.876448618727172 0
169 -2.039761962697679 -4.435343595363031 0
170 -2.57354481360955 -3.909301269424566 0
171 -2.535732489384031 -4.452137137805948 0
172 -3.059798253458965 -3.935225467144031 0
173 -3.02957058029087 -4.465715643196338 0
174 -3.544781151325922 -3.955866397178196 0
175 -3.52240096857565 -4.476640744295789 0
176 -4.029558025213573 -3.97270485120735 0
177 -4.014893239369709 -4.485580776942857 0
178 -4.514598383857045 -3.987045581044113 0
179 -4.507386188672238 -4.493183701962657 0
180 -0.8006709540149735 -2.476765813610716 0
181 -1.267198951523049 -2.624330912705657 0
182 -1.733120766602745 -2.726001387393099 0
183 -2.196724444010224 -2.799358049188522 0
184 -2.659538010646273 -2.854062873235562 0
185 -3.123294047769952 -2.895965440056678 0
186 -3.589167377515512 -2.928955953414885 0
187 -4.057538220541448 -2.955867327310949 0
188 -4.528077277925624 -2.978923981788693 0
189 -0.9687509231976139 -1.991490069922028 0
190 -1.386970993902387 -2.122717016201259 0
191 -1.819410201177847 -2.220141006425682 0
192 -2.258923647273578 -2.292999103541908 0
193 -2.703483736760897 -2.348366833255585 0
194 -3.153232042234145 -2.391298962173866 0
195 -3.608559855392381 -2.425426109249888 0
196 -4.069038820050712 -2.453486730829595 0
197 -4.533424988551208 -2.47769824111193 0
198 -1.272476666753393e-08 -1.924359482524905 0
199 -0.2533343974762239 -1.606443202429349 0
200 0.2533344229837241 -1.606443132326576 0
201 2.53249852051541e-08 -1.388593389582041 0
202 -0.5272934750247175 -0.9926764285952894 0
203 -0.5858028437947259 -0.6707709234636405 0
204 -0.6157803404632994 -0.3382179577638268 0
205 -0.2456717774258411 -0.9190691202974864 0
206 -0.2802909995762338 -0.6309898154192547 0
207 -0.2874733536873217 -0.3203514901649924 0
208 0.5272935606350887 -0.9926765509995065 0
209 0.2456718710348613 -0.9190692014700954 0
210 0.5858029352058564 -0.6707710264887236 0
211 0.2802910897441391 -0.6309898869990653 0
212 0.6157804025422635 -0.3382180183964293 0
213 0.2874734069247128 -0.3203515208733445 0
214 -4.536347854639426 -1.481553666129692 0
215 -4.530849447494669 -0.9863227548585151 0
216 -4.518663750100603 -0.4925991230550557 0
217 -4.076965269089723 -1.461824093896926 0
218 -4.066822817654915 -0.9717378202251115 0
219 -4.041844794305193 -0.4846493859382576 0
220 -3.626631555456245 -1.439400263472837 0
221 -3.614767853477897 -0.9552829042038593 0
222 -3.577234059246457 -0.4756361204437503 0
223 -3.190066245774652 -1.412949304825956 0
224 -3.183592242159449 -0.9362633794796755 0
225 -3.139804799022206 -0.465366666148178 0
226 -2.770239834557847 -1.381149002386727 0
227 -2.78361605771829 -0.9144286373168963 0
228 -2.777303799227714 -0.454899765247956 0
229 -2.363294730094991 -1.342156913093806 0
230 -2.400494437457917 -0.8884888000889807 0
231 -2.434740206505228 -0.4429058760892239 0
232 -1.964720377034965 -1.293667708472078 0
233 -2.01933996144247 -0.8565871950067101 0
234 -2.068624514239478 -0.4275156525307307 0
235 -1.575416930632774 -1.233626257664419 0
236 -1.64166735878701 -0.8176813599726269 0
237 -1.697012105595388 -0.4086248381798738 0
238 -1.200429025084152 -1.161147932262043 0
239 -1.272506329260207 -0.7718009641075847 0
240 -1.327401030882088 -0.386422544456637 0
241 4.536348463925029 -1.481554877027547 0
242 4.076966575441157 -1.461826747345742 0
243 3.626632758299004 -1.439402719821685 0
244 3.190067289609639 -1.412951514178275 0
245 2.770240603008739 -1.381150766970896 0
246 2.363295268598255 -1.342158273522384 0
247 1.964720745715605 -1.29366873340107 0
248 1.575417171293567 -1.233626983666225 0
249 1.200429174776261 -1.161148389883658 0
250 4.530851405729067 -0.9863270776419871 0
251 4.066826269903716 -0.9717448686110475 0
252 3.614769893474459 -0.9552868751328539 0
253 3.183593918032332 -0.9362666689585752 0
254 2.783617050253483 -0.9144308326138045 0
255 2.400495065388643 -0.8884903509228984 0
256 2.019340386429411 -0.856588326996137 0
257 1.641667642233049 -0.8176821443214574 0
258 1.272506501026669 -0.771801400260968 0
259 4.518663549413066 -0.4925982624014307 0
260 4.04184693132522 -0.4846532618590358 0
261 3.577237250381986 -0.4756414769154902 0
262 3.139808324752386 -0.4653725616385991 0
263 2.777304908988505 -0.454902208732857 0
264 2.434740771273721 -0.4429073253123982 0
265 2.068624882856776 -0.4275166319116776 0
266 1.697012350109042 -0.4086254890422338 0
267 1.327401164359004 -0.3864228466778788 0
$EndNodes
$Elements
279
1 1 2 1 10 1 11
2 1 2 1 10 11 12
3 1 2 1 10 12 13
4 1 2 1 10 13 14
5 1 2 1 10 14 15
6 1 2 1 10 15 16
7 1 2 1 10 16 17
8 1 2 1 10 17 18
9 1 2 1 10 18 19
10 1 2 1 10 19 20
11 1 2 1 10 20 21
12 1 2 1 10 21 22
13 1 2 1 10 22 23
14 1 2 1 10 23 24
15 1 2 1 10 24 25
16 1 2 1 10 25 26
17 1 2 1 10 26 27
18 1 2 1 10 27 28
19 1 2 1 10 28 29
20 1 2 1 10 29 2
21 1 2 2 11 2 30
22 1 2 2 11 30 31
23 1 2 2 11 31 32
24 1 2 2 11 32 33
25 1 2 2 11 33 34
26 1 2 2 11 34 35
27 1 2 2 11 35 36
28 1 2 2 11 36 37
29 1 2 2 11 37 38
30 1 2 2 11 38 3
31 1 2 3 13 4 42
32 1 2 3 13 42 5
33 1 2 4 15 6 48
34 1 2 4 15 48 7
35 1 2 5 17 8 54
36 1 2 5 17 54 9
37 1 2 6 19 10 58
38 1 2 6 19 58 59
39 1 2 6 19 59 60
40 1 2 6 19 60 61
41 1 2 6 19 61 62
42 1 2 6 19 62 63
43 1 2 6 19 63 64
44 1 2 6 19 64 65
45 1 2 6 19 65 66
46 1 2 6 19 66 1
47 3 2 1 1 67 71 126 89
48 3 2 1 1 89 126 127 90
49 3 2 1 1 90 127 88 68
50 3 2 1 1 71 72 128 126
51 3 2 1 1 126 128 129 127
52 3 2 1 1 127 129 87 88
53 3 2 1 1 72 73 130 128
54 3 2 1 1 128 130 131 129
55 3 2 1 1 129 131 86 87
56 3 2 1 1 73 74 132 130
57 3 2 1 1 130 132 133 131
58 3 2 1 1 131 133 85 86
59 3 2 1 1 74 75 134 132
60 3 2 1 1 132 134 135 133
61 3 2 1 1 133 135 84 85
62 3 2 1 1 75 76 136 134
63 3 2 1 1 134 136 137 135
64 3 2 1 1 135 137 83 84
65 3 2 1 1 76 77 138 136
66 3 2 1 1 136 138 139 137
67 3 2 1 1 137 139 82 83
68 3 2 1 1 77 78 140 138
69 3 2 1 1 138 140 141 139
70 3 2 1 1 139 141 81 82
71 3 2 1 1 78 79 142 140
72 3 2 1 1 140 142 143 141
73 3 2 1 1 141 143 80 81
74 3 2 1 1 79 32 33 142
75 3 2 1 1 142 33 34 143
76 3 2 1 1 143 34 35 80
77 3 2 1 1 67 91 144 71
78 3 2 1 1 71 144 145 72
79 3 2 1 1 72 145 146 73
80 3 2 1 1 73 146 147 74
81 3 2 1 1 74 147 148 75
82 3 2 1 1 75 148 149 76
83 3 2 1 1 76 149 150 77
84 3 2 1 1 77 150 151 78
85 3 2 1 1 78 151 152 79
86 3 2 1 1 79 152 31 32
87 3 2 1 1 91 92 153 144
88 3 2 1 1 144 153 154 145
89 3 2 1 1 145 154 155 146
90 3 2 1 1 146 155 156 147
91 3 2 1 1 147 156 157 148
92 3 2 1 1 148 157 158 149
93 3 2 1 1 149 158 159 150
94 3 2 1 1 150 159 160 151
95 3 2 1 1 151 160 161 152
96 3 2 1 1 152 161 30 31
97 3 2 1 1 92 20 21 153
98 3 2 1 1 153 21 22 154
99 3 2 1 1 154 22 23 155
100 3 2 1 1 155 23 24 156
101 3 2 1 1 156 24 25 157
102 3 2 1 1 157 25 26 158
103 3 2 1 1 158 26 27 159
104 3 2 1 1 159 27 28 160
105 3 2 1 1 160 28 29 161
106 3 2 1 1 161 29 2 30
107 3 2 1 1 67 93 162 91
108 3 2 1 1 91 162 163 92
109 3 2 1 1 92 163 19 20
110 3 2 1 1 93 94 164 162
111 3 2 1 1 162 164 165 163
112 3 2 1 1 163 165 18 19
113 3 2 1 1 94 95 166 164
114 3 2 1 1 164 166 167 165
115 3 2 1 1 165 167 17 18
116 3 2 1 1 95 96 168 166
117 3 2 1 1 166 168 169 167
118 3 2 1 1 167 169 16 17
119 3 2 1 1 96 97 170 168
120 3 2 1 1 168 170 171 169
121 3 2 1 1 169 171 15 16
122 3 2 1 1 97 98 172 170
123 3 2 1 1 170 172 173 171
124 3 2 1 1 171 173 14 15
125 3 2 1 1 98 99 174 172
126 3 2 1 1 172 174 175 173
127 3 2 1 1 173 175 13 14
128 3 2 1 1 99 100 176 174
129 3 2 1 1 174 176 177 175
130 3 2 1 1 175 177 12 13
131 3 2 1 1 100 101 178 176
132 3 2 1 1 176 178 179 177
133 3 2 1 1 177 179 11 12
134 3 2 1 1 101 64 65 178
135 3 2 1 1 178 65 66 179
136 3 2 1 1 179 66 1 11
137 3 2 1 1 67 102 180 93
138 3 2 1 1 93 180 181 94
139 3 2 1 1 94 181 182 95
140 3 2 1 1 95 182 183 96
141 3 2 1 1 96 183 184 97
142 3 2 1 1 97 184 185 98
143 3 2 1 1 98 185 186 99
144 3 2 1 1 99 186 187 100
145 3 2 1 1 100 187 188 101
146 3 2 1 1 101 188 63 64
147 3 2 1 1 102 103 189 180
148 3 2 1 1 180 189 190 181
149 3 2 1 1 181 190 191 182
150 3 2 1 1 182 191 192 183
151 3 2 1 1 183 192 193 184
152 3 2 1 1 184 193 194 185
153 3 2 1 1 185 194 195 186
154 3 2 1 1 186 195 196 187
155 3 2 1 1 187 196 197 188
156 3 2 1 1 188 197 62 63
157 3 2 1 1 103 69 112 189
158 3 2 1 1 189 112 111 190
159 3 2 1 1 190 111 110 191
160 3 2 1 1 191 110 109 192
161 3 2 1 1 192 109 108 193
162 3 2 1 1 193 108 107 194
163 3 2 1 1 194 107 106 195
164 3 2 1 1 195 106 105 196
165 3 2 1 1 196 105 104 197
166 3 2 1 1 197 104 61 62
167 3 2 1 1 67 89 198 102
168 3 2 1 1 102 198 199 103
169 3 2 1 1 103 199 115 69
170 3 2 1 1 89 90 200 198
171 3 2 1 1 198 200 201 199
172 3 2 1 1 199 201 116 115
173 3 2 1 1 90 68 113 200
174 3 2 1 1 200 113 114 201
175 3 2 1 1 201 114 70 116
176 3 2 1 1 69 115 202 120
177 3 2 1 1 120 202 203 121
178 3 2 1 1 121 203 204 122
179 3 2 1 1 122 204 49 50
180 3 2 1 1 115 116 205 202
181 3 2 1 1 202 205 206 203
182 3 2 1 1 203 206 207 204
183 3 2 1 1 204 207 7 49
184 3 2 1 1 116 70 117 205
185 3 2 1 1 205 117 118 206
186 3 2 1 1 206 118 119 207
187 3 2 1 1 207 119 48 7
188 3 2 1 1 68 123 208 113
189 3 2 1 1 113 208 209 114
190 3 2 1 1 114 209 117 70
191 3 2 1 1 123 124 210 208
192 3 2 1 1 208 210 211 209
193 3 2 1 1 209 211 118 117
194 3 2 1 1 124 125 212 210
195 3 2 1 1 210 212 213 211
196 3 2 1 1 211 213 119 118
197 3 2 1 1 125 46 47 212
198 3 2 1 1 212 47 6 213
199 3 2 1 1 213 6 48 119
200 3 2 1 1 61 104 214 60
201 3 2 1 1 60 214 215 59
202 3 2 1 1 59 215 216 58
203 3 2 1 1 58 216 57 10
204 3 2 1 1 104 105 217 214
205 3 2 1 1 214 217 218 215
206 3 2 1 1 215 218 219 216
207 3 2 1 1 216 219 56 57
208 3 2 1 1 105 106 220 217
209 3 2 1 1 217 220 221 218
210 3 2 1 1 218 221 222 219
211 3 2 1 1 219 222 55 56
212 3 2 1 1 106 107 223 220
213 3 2 1 1 220 223 224 221
214 3 2 1 1 221 224 225 222
215 3 2 1 1 222 225 9 55
216 3 2 1 1 107 108 226 223
217 3 2 1 1 223 226 227 224
218 3 2 1 1 224 227 228 225
219 3 2 1 1 225 228 54 9
220 3 2 1 1 108 109 229 226
221 3 2 1 1 226 229 230 227
222 3 2 1 1 227 230 231 228
223 3 2 1 1 228 231 8 54
224 3 2 1 1 109 110 232 229
225 3 2 1 1 229 232 233 230
226 3 2 1 1 230 233 234 231
227 3 2 1 1 231 234 53 8
228 3 2 1 1 110 111 235 232
229 3 2 1 1 232 235 236 233
230 3 2 1 1 233 236 237 234
231 3 2 1 1 234 237 52 53
232 3 2 1 1 111 112 238 235
233 3 2 1 1 235 238 239 236
234 3 2 1 1 236 239 240 237
235 3 2 1 1 237 240 51 52
236 3 2 1 1 112 69 120 238
237 3 2 1 1 238 120 121 239
238 3 2 1 1 239 121 122 240
239 3 2 1 1 240 122 50 51
240 3 2 1 1 35 36 241 80
241 3 2 1 1 80 241 242 81
242 3 2 1 1 81 242 243 82
243 3 2 1 1 82 243 244 83
244 3 2 1 1 83 244 245 84
245 3 2 1 1 84 245 246 85
246 3 2 1 1 85 246 247 86
247 3 2 1 1 86 247 248 87
248 3 2 1 1 87 248 249 88
249 3 2 1 1 88 249 123 68
250 3 2 1 1 36 37 250 241
251 3 2 1 1 241 250 251 242
252 3 2 1 1 242 251 252 243
253 3 2 1 1 243 252 253 244
254 3 2 1 1 244 253 254 245
255 3 2 1 1 245 254 255 246
256 3 2 1 1 246 255 256 247
257 3 2 1 1 247 256 257 248
258 3 2 1 1 248 257 258 249
259 3 2 1 1 249 258 124 123
260 3 2 1 1 37 38 259 250
261 3 2 1 1 250 259 260 251
262 3 2 1 1 251 260 261 252
263 3 2 1 1 252 261 262 253
264 3 2 1 1 253 262 263 254
265 3 2 1 1 254 263 264 255
266 3 2 1 1 255 264 265 256
267 3 2 1 1 256 265 266 257
268 3 2 1 1 257 266 267 258
269 3 2 1 1 258 267 125 124
270 3 2 1 1 38 3 39 259
271 3 2 1 1 259 39 40 260
272 3 2 1 1 260 40 41 261
273 3 2 1 1 261 41 4 262
274 3 2 1 1 262 4 42 263
275 3 2 1 1 263 42 5 264
276 3 2 1 1 264 5 43 265
277 3 2 1 1 265 43 44 266
278 3 2 1 1 266 44 45 267
279 3 2 1 1 267 45 46 125
$EndElements
-841
View File
@@ -1,841 +0,0 @@
$MeshFormat
2.2 0 8
$EndMeshFormat
$Nodes
284
1 -5 -5 0
2 5 -5 0
3 5 0 0
4 3 0 0
5 2.5 0 0
6 0.25 0 0
7 -0.25 0 0
8 -2.5 0 0
9 -3 0 0
10 -5 0 0
11 -4.5 -5 0
12 -4 -5 0
13 -3.5 -5 0
14 -3 -5 0
15 -2.5 -5 0
16 -2 -5 0
17 -1.5 -5 0
18 -1 -5 0
19 -0.5 -5 0
20 0 -5 0
21 0.5 -5 0
22 1 -5 0
23 1.5 -5 0
24 2 -5 0
25 2.5 -5 0
26 3 -5 0
27 3.5 -5 0
28 4 -5 0
29 4.5 -5 0
30 5 -4.5 0
31 5 -4 0
32 5 -3.5 0
33 5 -3 0
34 5 -2.5 0
35 5 -2 0
36 5 -1.5 0
37 5 -1 0
38 5 -0.5 0
39 4.5 0 0
40 4 0 0
41 3.5 0 0
42 2.05 0 0
43 1.6 0 0
44 1.15 0 0
45 0.7 0 0
46 -0.7 0 0
47 -1.15 0 0
48 -1.6 0 0
49 -2.05 0 0
50 -3.5 0 0
51 -4 0 0
52 -4.5 0 0
53 -5 -0.5 0
54 -5 -1 0
55 -5 -1.5 0
56 -5 -2 0
57 -5 -2.5 0
58 -5 -3 0
59 -5 -3.5 0
60 -5 -4 0
61 -5 -4.5 0
62 2.281579424776069 -0.3939927528378067 0
63 0.9247461078594561 -0.4010584732281242 0
64 -2.251733651700584 -0.4058752540090222 0
65 -0.9249999999999999 -0.3860346567053154 0
66 -0.25 -4.566987298107781 0
67 -1.75 -4.566987298107781 0
68 1.757138123938968 -4.561388469215611 0
69 -4.57663533780044 -2.749996588117569 0
70 4.574218761575537 -2.744193863029555 0
71 -3.261047720525324 -4.633492158122827 0
72 3.233878916273093 -4.572861684713071 0
73 3.249983936371643 -0.4138167322607993 0
74 -3.22796127783195 -0.4745514342555692 0
75 0 -0.3897114317029975 0
76 0.7668085767464438 -4.554291553533576 0
77 4.524628320503154 -1.71259283597443 0
78 -4.57425038881721 -1.740509953296104 0
79 4.657280536213934 -3.745352555127771 0
80 -4.561557444591958 -3.743801811882471 0
81 4.247947141075545 -0.413931595411861 0
82 -4.25 -0.4330127018922192 0
83 -4.231971247749111 -4.526000871317382 0
84 4.247882011655604 -4.578053652047846 0
85 0.459240288050884 -0.4011062233317819 0
86 0.2191981222082625 -0.7894052414358377 0
87 -0.2550018029202653 -0.7696812455915785 0
88 -0.03974194597926101 -1.173522830121104 0
89 0.4346364211584371 -1.192775566160588 0
90 0.1864311373004761 -1.586413475031659 0
91 0.6507331103481842 -1.602794886574206 0
92 -0.2962063372658213 -1.560534711371811 0
93 0.4052226136760305 -1.998915829261101 0
94 0.8747101368415163 -2.014294995428312 0
95 0.6254442827334447 -2.414070730277764 0
96 1.096931360307092 -2.429431309425559 0
97 1.346300816143899 -2.027816295003789 0
98 1.569865887277439 -2.444122401305898 0
99 1.819907460974127 -2.038729916981179 0
100 2.044972612829793 -2.458848886499749 0
101 0.1536392683070031 -2.397215233401116 0
102 2.296563295216953 -2.050400407953151 0
103 2.523676371701205 -2.474026874448667 0
104 2.775089986948031 -2.064883565181495 0
105 0.3753978099944599 -2.816445321672421 0
106 -0.101594819363816 -2.799164055357317 0
107 1.321674174422592 -2.846267815879844 0
108 2.270294778623243 -2.882886829258818 0
109 2.752962034536527 -2.899399230379248 0
110 1.594627579321902 -1.620863532351973 0
111 2.496530594790508 -3.311620345179397 0
112 2.983571088917511 -3.327819126997308 0
113 0.1137225221607727 -3.227426914851572 0
114 -0.3711032418985809 -3.207832251592655 0
115 -0.5828344401467249 -2.781198932956502 0
116 -0.8353259116793657 -3.185936459987706 0
117 -1.059979017425433 -2.762083576904867 0
118 -1.314767961260485 -3.167209797744215 0
119 -1.538368860989399 -2.742671230707596 0
120 -1.793679531768132 -3.151042894839176 0
121 -2.01924352863926 -2.723529808724752 0
122 -1.762667957890364 -2.316647156105704 0
123 -2.242107472177446 -2.296152128033975 0
124 -2.502564281105353 -2.703893235067956 0
125 -2.725331546445376 -2.27365855104896 0
126 -2.987889362306672 -2.683287954718313 0
127 -1.983692675350436 -1.892945890847701 0
128 -2.763007610522606 -3.115691622644485 0
129 -3.251879284051252 -3.095617007538864 0
130 -3.21286057170471 -2.250678390947021 0
131 -2.949339074017358 -1.839974815980651 0
132 3.239407644843084 -2.914212985735978 0
133 2.547241608329427 -1.641063282475632 0
134 3.028142281477624 -1.652825101445818 0
135 -1.510406177790435 -1.913844107670896 0
136 -1.730236214842076 -1.505967151038945 0
137 -1.091336732540506 -3.593626264859387 0
138 -3.421854185011473 -1.808744480608429 0
139 3.472898669730996 -3.34330809310189 0
140 -1.256990758306386 -1.517443982643851 0
141 -3.024894304094996 -3.530456033168236 0
142 -3.516854524302131 -3.510343830944479 0
143 -1.03266707250538 -1.934584786094152 0
144 -2.201596179944664 -1.480657719683199 0
145 0.9021516016281497 -1.202776512596341 0
146 2.725588881466229 -3.738813882257535 0
147 -2.53516560562837 -3.550136689533193 0
148 -1.44581323125898 -1.123897963936046 0
149 2.235681770010328 -3.726033843511675 0
150 3.259277130723387 -2.077185219746537 0
151 3.513246676438002 -1.662021998760507 0
152 3.758409946183188 -2.082693761823018 0
153 0.5991111801411881 -3.240104427542418 0
154 0.3369992297204003 -3.654513295976306 0
155 0.8290255688225511 -3.666471451892976 0
156 -3.722265329818222 -2.240608173907041 0
157 -3.171684897998753 -1.403346357255416 0
158 -3.644197255924033 -1.372619186245074 0
159 2.794592285631932 -1.233756575634245 0
160 2.318227283889231 -1.220216315858479 0
161 -0.9783790307780148 -1.130197912479716 0
162 -3.713876128115184 -3.079033658054799 0
163 3.742324371372884 -2.928999279490477 0
164 2.485083477357409 -4.14729896608471 0
165 -2.778145642709027 -3.956644572916525 0
166 -2.304612039225867 -3.9863489431438 0
167 3.966968926084867 -3.336303597578833 0
168 3.706083502027171 -3.750175369113853 0
169 -4.077410641740634 -3.501439180006514 0
170 -3.76933168214444 -3.914132682209269 0
171 -2.568167113352794 -4.403628334893856 0
172 3.999000211194053 -1.669214607229196 0
173 3.757262029468758 -1.246079492069315 0
174 4.257624135642195 -1.243021671724434 0
175 -3.406533611304276 -0.962718897978591 0
176 -2.86976350923752 -0.9741763965199361 0
177 -3.887783979793185 -0.9468337725874192 0
178 -4.151590072554236 -1.433655682692203 0
179 2.567375777521253 -0.8162946823393982 0
180 -2.710501565840532 -0.449194289380908 0
181 -2.361189298792369 -0.8166861741006153 0
182 -1.992682834578591 -0.6868709027668873 0
183 0.5758835407852954 -4.076507624142248 0
184 1.063684706252736 -4.093379787905304 0
185 1.307846713632664 -3.678677225448682 0
186 0.06794786130557443 -4.080395137507568 0
187 -2.678398828890455 -1.460184324491551 0
188 -0.8061658045868053 -2.358239065659463 0
189 -4.417190667034111 -0.9530981038866508 0
190 -3.73616854160164 -0.4667243592037824 0
191 2.068162362263069 -1.631550568057341 0
192 1.835382136049844 -1.217564469998849 0
193 2.076433566739975 -0.8129459415658435 0
194 1.61110337936101 -0.8034909695554286 0
195 1.370865405519029 -1.205639238805478 0
196 1.266721313972774 -4.554232785919753 0
197 1.152132432870027 -0.7985062781329273 0
198 3.748778721192141 -0.4141047359259749 0
199 -0.3239021359339606 -2.378890209329921 0
200 0.2607890790366596 -4.560067219429725 0
201 -0.4362762851344742 -4.076426953781812 0
202 -0.7421904022893758 -4.524993181824712 0
203 -1.253691857241304 -4.549910310088686 0
204 -1.488540618385916 -4.054529834302866 0
205 1.541536749303611 -4.112783475173385 0
206 -0.5479417186259372 -1.958200565306824 0
207 -0.7713233681764451 -1.536925116417346 0
208 -0.5067754141866345 -1.151982463020497 0
209 -2.463410962804289 -1.87392890501434 0
210 -1.285472530570352 -2.338305660158975 0
211 1.378798590411674 -0.3978505940829993 0
212 4.568566441173747 -2.253723730329266 0
213 4.088512427247694 -2.504191622867672 0
214 -4.571120642477398 -2.248470981336947 0
215 -4.173258424046725 -2.492769868254391 0
216 -2.047412941544885 -3.538687166079191 0
217 1.125824650205117 -1.614348493757459 0
218 -4.567172963194309 -3.249678418636941 0
219 4.501762177208068 -3.291802327374498 0
220 2.734404034710506 -4.574544109781256 0
221 3.737632412102343 -4.577017677939905 0
222 -3.747757947362715 -4.608888775351113 0
223 0.6858343131338018 -0.7970681284062564 0
224 2.015439853666996 -3.2910097832621 0
225 -1.370883691544074 -0.3644617263994698 0
226 -0.4732994730374375 -0.3853059645196071 0
227 -4.170675295358781 -1.983559227818775 0
228 -2.277544785346445 -3.130935475978483 0
229 -3.263718927452331 -3.912011456955563 0
230 -0.1542685254652809 -3.640226549090094 0
231 -3.454561268417778 -2.67774506671365 0
232 3.214928094260674 -3.743707595789564 0
233 3.007108075975173 -2.488409339011342 0
234 2.750375637050162 -0.4010587521556839 0
235 3.269904368436389 -1.242599673877227 0
236 0.8478171295042514 -2.831115790911858 0
237 -0.0704595287570349 -1.980028337283739 0
238 1.793648233980072 -2.86032311009413 0
239 3.515839932724234 -2.499282034779171 0
240 1.07506868241603 -3.253368514666169 0
241 -1.567508338488392 -3.57138356920831 0
242 -0.9414895224228255 -4.065708547614685 0
243 1.832985826881454 -0.4013800430070131 0
244 1.547665348273904 -3.266345147264329 0
245 2.24775968641297 -4.567837723912959 0
246 3.024151650683252 -0.8281976201130985 0
247 3.975810147196631 -4.172641805166976 0
248 3.503728254202536 -0.8279311588352849 0
249 4.019169941690009 -0.8158447756611453 0
250 -4.60633771632638 -4.233723530566535 0
251 4.538799499276811 -4.205405374535824 0
252 4.562305311857926 -0.7843140484770377 0
253 2.972986107264538 -4.155124335587413 0
254 1.778207995839603 -3.692155396798268 0
255 -0.6383000365235054 -3.628292837821056 0
256 2.007567967143815 -4.134582979116102 0
257 3.473553196520742 -4.16192141138513 0
258 -0.7147426201537252 -0.7593778023334516 0
259 -1.806767054514615 -0.3633765595208169 0
260 -4.167097947097267 -2.975013458090675 0
261 4.175407739112876 -2.963681551184706 0
262 -1.168353453833206 -0.7478369226339197 0
263 -4.628606225681111 -1.325452747974991 0
264 -1.955754170223746 -1.109359237158393 0
265 -2.181031249640995 -4.584142759217013 0
266 4.182701247812142 -2.054804320229848 0
267 -4.210523009401651 -3.998563903983795 0
268 4.199775238004059 -3.750658239585041 0
269 -1.916827765372219 -4.194625820537434 0
270 -1.592821679170089 -0.7265436064032613 0
271 4.67022823811257 -1.25 0
272 -3.831566914438898 -1.745340707425962 0
273 -4.63781179938241 -0.6091765096601387 0
274 -3.039941547144599 -4.302681803322779 0
275 -3.505011562055658 -4.271356754644082 0
276 -4.633974596215562 -4.633974596215562 0
277 4.633974596215562 -4.633974596215562 0
278 4.636520367722471 -0.3634796322775284 0
279 -2.418900170891447 -1.187862709206192 0
280 -2.873831276204544 -4.667960459267892 0
281 -3.842482079912878 -2.704892577303744 0
282 -3.908734077731807 -4.261830041615932 0
283 -1.821328914771955 -3.849262003031382 0
284 -4.705859493625653 -0.2869977523880989 0
$EndNodes
$Elements
548
1 1 2 1 10 1 11
2 1 2 1 10 11 12
3 1 2 1 10 12 13
4 1 2 1 10 13 14
5 1 2 1 10 14 15
6 1 2 1 10 15 16
7 1 2 1 10 16 17
8 1 2 1 10 17 18
9 1 2 1 10 18 19
10 1 2 1 10 19 20
11 1 2 1 10 20 21
12 1 2 1 10 21 22
13 1 2 1 10 22 23
14 1 2 1 10 23 24
15 1 2 1 10 24 25
16 1 2 1 10 25 26
17 1 2 1 10 26 27
18 1 2 1 10 27 28
19 1 2 1 10 28 29
20 1 2 1 10 29 2
21 1 2 2 11 2 30
22 1 2 2 11 30 31
23 1 2 2 11 31 32
24 1 2 2 11 32 33
25 1 2 2 11 33 34
26 1 2 2 11 34 35
27 1 2 2 11 35 36
28 1 2 2 11 36 37
29 1 2 2 11 37 38
30 1 2 2 11 38 3
31 1 2 3 13 4 5
32 1 2 4 15 6 7
33 1 2 5 17 8 9
34 1 2 6 19 10 53
35 1 2 6 19 53 54
36 1 2 6 19 54 55
37 1 2 6 19 55 56
38 1 2 6 19 56 57
39 1 2 6 19 57 58
40 1 2 6 19 58 59
41 1 2 6 19 59 60
42 1 2 6 19 60 61
43 1 2 6 19 61 1
44 2 2 1 1 137 241 204
45 2 2 1 1 204 242 137
46 2 2 1 1 175 176 74
47 2 2 1 1 176 180 74
48 2 2 1 1 201 255 242
49 2 2 1 1 167 268 219
50 2 2 1 1 213 239 163
51 2 2 1 1 9 180 8
52 2 2 1 1 152 239 213
53 2 2 1 1 169 218 80
54 2 2 1 1 78 227 178
55 2 2 1 1 174 252 249
56 2 2 1 1 74 190 175
57 2 2 1 1 251 268 247
58 2 2 1 1 219 268 79
59 2 2 1 1 186 201 66
60 2 2 1 1 8 180 64
61 2 2 1 1 79 268 251
62 2 2 1 1 66 200 186
63 2 2 1 1 177 190 82
64 2 2 1 1 19 202 18
65 2 2 1 1 77 174 172
66 2 2 1 1 82 189 177
67 2 2 1 1 227 272 178
68 2 2 1 1 175 190 177
69 2 2 1 1 265 269 166
70 2 2 1 1 242 255 137
71 2 2 1 1 171 265 166
72 2 2 1 1 186 200 183
73 2 2 1 1 189 273 54
74 2 2 1 1 80 267 169
75 2 2 1 1 183 200 76
76 2 2 1 1 54 263 189
77 2 2 1 1 74 180 9
78 2 2 1 1 88 208 92
79 2 2 1 1 202 203 18
80 2 2 1 1 169 260 218
81 2 2 1 1 201 242 202
82 2 2 1 1 219 261 167
83 2 2 1 1 249 252 81
84 2 2 1 1 76 184 183
85 2 2 1 1 62 193 179
86 2 2 1 1 113 230 154
87 2 2 1 1 148 270 264
88 2 2 1 1 66 202 19
89 2 2 1 1 92 208 207
90 2 2 1 1 174 271 252
91 2 2 1 1 76 196 184
92 2 2 1 1 15 265 171
93 2 2 1 1 264 270 182
94 2 2 1 1 229 275 274
95 2 2 1 1 181 264 182
96 2 2 1 1 176 181 180
97 2 2 1 1 7 75 6
98 2 2 1 1 234 246 73
99 2 2 1 1 87 208 88
100 2 2 1 1 274 275 71
101 2 2 1 1 193 243 194
102 2 2 1 1 84 251 247
103 2 2 1 1 77 271 174
104 2 2 1 1 171 280 15
105 2 2 1 1 51 190 50
106 2 2 1 1 163 261 213
107 2 2 1 1 247 257 221
108 2 2 1 1 68 205 196
109 2 2 1 1 180 181 64
110 2 2 1 1 114 230 113
111 2 2 1 1 196 205 184
112 2 2 1 1 181 279 264
113 2 2 1 1 50 190 74
114 2 2 1 1 154 230 186
115 2 2 1 1 172 266 77
116 2 2 1 1 92 207 206
117 2 2 1 1 62 243 193
118 2 2 1 1 201 202 66
119 2 2 1 1 89 223 86
120 2 2 1 1 195 197 145
121 2 2 1 1 140 207 161
122 2 2 1 1 188 206 143
123 2 2 1 1 179 193 160
124 2 2 1 1 63 211 44
125 2 2 1 1 202 242 203
126 2 2 1 1 45 85 63
127 2 2 1 1 206 207 143
128 2 2 1 1 160 193 192
129 2 2 1 1 4 234 73
130 2 2 1 1 82 190 51
131 2 2 1 1 203 242 204
132 2 2 1 1 179 234 62
133 2 2 1 1 131 209 187
134 2 2 1 1 187 209 144
135 2 2 1 1 176 279 181
136 2 2 1 1 145 217 195
137 2 2 1 1 143 207 140
138 2 2 1 1 7 226 75
139 2 2 1 1 179 246 234
140 2 2 1 1 44 211 43
141 2 2 1 1 199 237 206
142 2 2 1 1 143 210 188
143 2 2 1 1 85 223 63
144 2 2 1 1 160 192 191
145 2 2 1 1 186 230 201
146 2 2 1 1 195 217 110
147 2 2 1 1 17 203 67
148 2 2 1 1 197 211 63
149 2 2 1 1 6 85 45
150 2 2 1 1 220 245 25
151 2 2 1 1 86 223 85
152 2 2 1 1 99 191 110
153 2 2 1 1 197 223 145
154 2 2 1 1 207 208 161
155 2 2 1 1 206 237 92
156 2 2 1 1 18 203 17
157 2 2 1 1 63 223 197
158 2 2 1 1 191 192 110
159 2 2 1 1 102 191 99
160 2 2 1 1 203 204 67
161 2 2 1 1 156 227 215
162 2 2 1 1 181 182 64
163 2 2 1 1 23 196 22
164 2 2 1 1 145 223 89
165 2 2 1 1 192 195 110
166 2 2 1 1 83 267 250
167 2 2 1 1 168 257 247
168 2 2 1 1 75 226 87
169 2 2 1 1 193 194 192
170 2 2 1 1 22 196 76
171 2 2 1 1 68 196 23
172 2 2 1 1 75 85 6
173 2 2 1 1 159 160 133
174 2 2 1 1 245 256 68
175 2 2 1 1 188 210 117
176 2 2 1 1 125 209 131
177 2 2 1 1 157 176 175
178 2 2 1 1 4 73 41
179 2 2 1 1 50 74 9
180 2 2 1 1 164 245 220
181 2 2 1 1 159 179 160
182 2 2 1 1 216 228 147
183 2 2 1 1 194 243 211
184 2 2 1 1 94 217 91
185 2 2 1 1 40 198 81
186 2 2 1 1 157 187 176
187 2 2 1 1 122 210 135
188 2 2 1 1 91 217 145
189 2 2 1 1 135 210 143
190 2 2 1 1 120 228 216
191 2 2 1 1 101 199 106
192 2 2 1 1 122 135 127
193 2 2 1 1 133 191 102
194 2 2 1 1 135 136 127
195 2 2 1 1 160 191 133
196 2 2 1 1 73 198 41
197 2 2 1 1 140 148 136
198 2 2 1 1 101 237 199
199 2 2 1 1 20 200 66
200 2 2 1 1 135 140 136
201 2 2 1 1 178 263 78
202 2 2 1 1 140 161 148
203 2 2 1 1 122 127 123
204 2 2 1 1 115 188 117
205 2 2 1 1 106 199 115
206 2 2 1 1 131 187 157
207 2 2 1 1 99 110 97
208 2 2 1 1 199 206 188
209 2 2 1 1 5 234 4
210 2 2 1 1 119 122 121
211 2 2 1 1 115 199 188
212 2 2 1 1 230 255 201
213 2 2 1 1 76 200 21
214 2 2 1 1 97 217 94
215 2 2 1 1 169 267 170
216 2 2 1 1 104 133 102
217 2 2 1 1 56 214 78
218 2 2 1 1 77 212 35
219 2 2 1 1 119 210 122
220 2 2 1 1 212 213 70
221 2 2 1 1 17 67 16
222 2 2 1 1 20 66 19
223 2 2 1 1 22 76 21
224 2 2 1 1 24 68 23
225 2 2 1 1 69 215 214
226 2 2 1 1 117 210 119
227 2 2 1 1 194 197 195
228 2 2 1 1 194 211 197
229 2 2 1 1 215 227 214
230 2 2 1 1 136 144 127
231 2 2 1 1 110 217 97
232 2 2 1 1 21 200 20
233 2 2 1 1 36 77 35
234 2 2 1 1 56 78 55
235 2 2 1 1 115 117 116
236 2 2 1 1 194 195 192
237 2 2 1 1 41 198 40
238 2 2 1 1 134 235 159
239 2 2 1 1 34 212 70
240 2 2 1 1 69 214 57
241 2 2 1 1 208 258 161
242 2 2 1 1 213 266 152
243 2 2 1 1 106 115 114
244 2 2 1 1 35 212 34
245 2 2 1 1 57 214 56
246 2 2 1 1 135 143 140
247 2 2 1 1 123 209 125
248 2 2 1 1 134 159 133
249 2 2 1 1 26 220 25
250 2 2 1 1 32 219 79
251 2 2 1 1 80 218 59
252 2 2 1 1 34 70 33
253 2 2 1 1 58 69 57
254 2 2 1 1 58 218 69
255 2 2 1 1 70 219 33
256 2 2 1 1 131 157 138
257 2 2 1 1 147 228 128
258 2 2 1 1 91 145 89
259 2 2 1 1 27 221 72
260 2 2 1 1 40 81 39
261 2 2 1 1 52 82 51
262 2 2 1 1 12 222 83
263 2 2 1 1 14 71 13
264 2 2 1 1 27 72 26
265 2 2 1 1 71 222 13
266 2 2 1 1 84 221 28
267 2 2 1 1 28 221 27
268 2 2 1 1 33 219 32
269 2 2 1 1 59 218 58
270 2 2 1 1 32 79 31
271 2 2 1 1 60 80 59
272 2 2 1 1 13 222 12
273 2 2 1 1 12 83 11
274 2 2 1 1 29 84 28
275 2 2 1 1 125 131 130
276 2 2 1 1 96 97 94
277 2 2 1 1 166 216 147
278 2 2 1 1 117 119 118
279 2 2 1 1 184 185 155
280 2 2 1 1 72 220 26
281 2 2 1 1 157 175 158
282 2 2 1 1 141 147 128
283 2 2 1 1 122 123 121
284 2 2 1 1 214 227 78
285 2 2 1 1 165 166 147
286 2 2 1 1 115 116 114
287 2 2 1 1 117 118 116
288 2 2 1 1 131 138 130
289 2 2 1 1 98 99 97
290 2 2 1 1 151 235 134
291 2 2 1 1 241 283 204
292 2 2 1 1 104 134 133
293 2 2 1 1 111 149 146
294 2 2 1 1 75 86 85
295 2 2 1 1 108 224 111
296 2 2 1 1 169 170 142
297 2 2 1 1 172 173 151
298 2 2 1 1 111 224 149
299 2 2 1 1 119 121 120
300 2 2 1 1 141 165 147
301 2 2 1 1 54 273 53
302 2 2 1 1 129 162 142
303 2 2 1 1 184 205 185
304 2 2 1 1 96 98 97
305 2 2 1 1 112 132 109
306 2 2 1 1 127 209 123
307 2 2 1 1 42 62 5
308 2 2 1 1 8 64 49
309 2 2 1 1 45 63 44
310 2 2 1 1 47 65 46
311 2 2 1 1 46 226 7
312 2 2 1 1 157 158 138
313 2 2 1 1 48 225 47
314 2 2 1 1 165 171 166
315 2 2 1 1 126 128 124
316 2 2 1 1 162 169 142
317 2 2 1 1 125 130 126
318 2 2 1 1 75 87 86
319 2 2 1 1 139 163 132
320 2 2 1 1 119 120 118
321 2 2 1 1 149 164 146
322 2 2 1 1 139 168 167
323 2 2 1 1 129 141 128
324 2 2 1 1 138 156 130
325 2 2 1 1 123 125 124
326 2 2 1 1 108 238 224
327 2 2 1 1 129 142 141
328 2 2 1 1 161 262 148
329 2 2 1 1 68 256 205
330 2 2 1 1 126 129 128
331 2 2 1 1 172 174 173
332 2 2 1 1 139 167 163
333 2 2 1 1 125 126 124
334 2 2 1 1 112 139 132
335 2 2 1 1 175 177 158
336 2 2 1 1 236 240 107
337 2 2 1 1 93 101 95
338 2 2 1 1 154 186 183
339 2 2 1 1 101 106 105
340 2 2 1 1 177 189 178
341 2 2 1 1 106 114 113
342 2 2 1 1 88 92 90
343 2 2 1 1 152 172 151
344 2 2 1 1 170 229 142
345 2 2 1 1 88 89 86
346 2 2 1 1 183 184 155
347 2 2 1 1 90 91 89
348 2 2 1 1 154 155 153
349 2 2 1 1 113 153 105
350 2 2 1 1 103 104 102
351 2 2 1 1 100 108 103
352 2 2 1 1 111 146 112
353 2 2 1 1 100 102 99
354 2 2 1 1 98 100 99
355 2 2 1 1 177 178 158
356 2 2 1 1 93 94 91
357 2 2 1 1 111 112 109
358 2 2 1 1 95 96 94
359 2 2 1 1 150 152 151
360 2 2 1 1 150 151 134
361 2 2 1 1 118 137 116
362 2 2 1 1 121 228 120
363 2 2 1 1 108 111 109
364 2 2 1 1 185 240 155
365 2 2 1 1 96 107 98
366 2 2 1 1 129 231 162
367 2 2 1 1 141 229 165
368 2 2 1 1 128 228 124
369 2 2 1 1 153 240 236
370 2 2 1 1 123 124 121
371 2 2 1 1 108 109 103
372 2 2 1 1 142 229 141
373 2 2 1 1 87 88 86
374 2 2 1 1 100 103 102
375 2 2 1 1 106 113 105
376 2 2 1 1 101 105 95
377 2 2 1 1 154 183 155
378 2 2 1 1 65 226 46
379 2 2 1 1 88 90 89
380 2 2 1 1 90 93 91
381 2 2 1 1 113 154 153
382 2 2 1 1 156 231 130
383 2 2 1 1 130 231 126
384 2 2 1 1 124 228 121
385 2 2 1 1 93 95 94
386 2 2 1 1 47 225 65
387 2 2 1 1 126 231 129
388 2 2 1 1 132 233 109
389 2 2 1 1 144 209 127
390 2 2 1 1 173 235 151
391 2 2 1 1 112 232 139
392 2 2 1 1 139 232 168
393 2 2 1 1 104 150 134
394 2 2 1 1 146 232 112
395 2 2 1 1 103 233 104
396 2 2 1 1 211 243 43
397 2 2 1 1 109 233 103
398 2 2 1 1 104 233 150
399 2 2 1 1 132 239 233
400 2 2 1 1 93 237 101
401 2 2 1 1 92 237 90
402 2 2 1 1 233 239 150
403 2 2 1 1 96 236 107
404 2 2 1 1 153 236 105
405 2 2 1 1 95 236 96
406 2 2 1 1 216 241 120
407 2 2 1 1 163 239 132
408 2 2 1 1 72 253 220
409 2 2 1 1 162 260 169
410 2 2 1 1 43 243 42
411 2 2 1 1 105 236 95
412 2 2 1 1 90 237 93
413 2 2 1 1 100 238 108
414 2 2 1 1 98 238 100
415 2 2 1 1 25 245 24
416 2 2 1 1 107 238 98
417 2 2 1 1 187 279 176
418 2 2 1 1 258 262 161
419 2 2 1 1 120 241 118
420 2 2 1 1 83 282 267
421 2 2 1 1 150 239 152
422 2 2 1 1 173 249 248
423 2 2 1 1 155 240 153
424 2 2 1 1 118 241 137
425 2 2 1 1 221 257 72
426 2 2 1 1 31 251 30
427 2 2 1 1 61 250 60
428 2 2 1 1 174 249 173
429 2 2 1 1 173 248 235
430 2 2 1 1 62 234 5
431 2 2 1 1 185 244 240
432 2 2 1 1 235 246 159
433 2 2 1 1 220 253 164
434 2 2 1 1 42 243 62
435 2 2 1 1 24 245 68
436 2 2 1 1 164 253 146
437 2 2 1 1 238 244 224
438 2 2 1 1 87 258 208
439 2 2 1 1 114 255 230
440 2 2 1 1 148 264 136
441 2 2 1 1 69 260 215
442 2 2 1 1 116 255 114
443 2 2 1 1 72 257 253
444 2 2 1 1 185 254 244
445 2 2 1 1 198 249 81
446 2 2 1 1 107 244 238
447 2 2 1 1 159 246 179
448 2 2 1 1 84 247 221
449 2 2 1 1 240 244 107
450 2 2 1 1 164 256 245
451 2 2 1 1 182 259 64
452 2 2 1 1 73 248 198
453 2 2 1 1 235 248 246
454 2 2 1 1 226 258 87
455 2 2 1 1 248 249 198
456 2 2 1 1 244 254 224
457 2 2 1 1 55 263 54
458 2 2 1 1 64 259 49
459 2 2 1 1 205 254 185
460 2 2 1 1 48 259 225
461 2 2 1 1 60 250 80
462 2 2 1 1 79 251 31
463 2 2 1 1 205 256 254
464 2 2 1 1 16 265 15
465 2 2 1 1 77 266 212
466 2 2 1 1 38 252 37
467 2 2 1 1 262 270 148
468 2 2 1 1 36 271 77
469 2 2 1 1 146 253 232
470 2 2 1 1 246 248 73
471 2 2 1 1 70 261 219
472 2 2 1 1 224 254 149
473 2 2 1 1 137 255 116
474 2 2 1 1 254 256 149
475 2 2 1 1 215 281 156
476 2 2 1 1 166 283 216
477 2 2 1 1 11 276 1
478 2 2 1 1 1 276 61
479 2 2 1 1 2 277 29
480 2 2 1 1 30 277 2
481 2 2 1 1 259 270 225
482 2 2 1 1 156 272 227
483 2 2 1 1 149 256 164
484 2 2 1 1 138 272 156
485 2 2 1 1 178 272 158
486 2 2 1 1 3 278 38
487 2 2 1 1 39 278 3
488 2 2 1 1 213 261 70
489 2 2 1 1 229 274 165
490 2 2 1 1 253 257 232
491 2 2 1 1 232 257 168
492 2 2 1 1 49 259 48
493 2 2 1 1 65 258 226
494 2 2 1 1 218 260 69
495 2 2 1 1 156 281 231
496 2 2 1 1 52 284 82
497 2 2 1 1 252 271 37
498 2 2 1 1 167 261 163
499 2 2 1 1 65 262 258
500 2 2 1 1 225 262 65
501 2 2 1 1 189 263 178
502 2 2 1 1 78 263 55
503 2 2 1 1 136 264 144
504 2 2 1 1 212 266 213
505 2 2 1 1 67 265 16
506 2 2 1 1 269 283 166
507 2 2 1 1 182 270 259
508 2 2 1 1 204 269 67
509 2 2 1 1 152 266 172
510 2 2 1 1 67 269 265
511 2 2 1 1 168 268 167
512 2 2 1 1 260 281 215
513 2 2 1 1 15 280 14
514 2 2 1 1 71 275 222
515 2 2 1 1 247 268 168
516 2 2 1 1 264 279 144
517 2 2 1 1 37 271 36
518 2 2 1 1 250 267 80
519 2 2 1 1 144 279 187
520 2 2 1 1 82 273 189
521 2 2 1 1 225 270 262
522 2 2 1 1 14 280 71
523 2 2 1 1 82 284 273
524 2 2 1 1 222 282 83
525 2 2 1 1 158 272 138
526 2 2 1 1 251 277 30
527 2 2 1 1 61 276 250
528 2 2 1 1 81 278 39
529 2 2 1 1 83 276 11
530 2 2 1 1 29 277 84
531 2 2 1 1 165 274 171
532 2 2 1 1 170 275 229
533 2 2 1 1 53 284 10
534 2 2 1 1 10 284 52
535 2 2 1 1 231 281 162
536 2 2 1 1 162 281 260
537 2 2 1 1 250 276 83
538 2 2 1 1 84 277 251
539 2 2 1 1 216 283 241
540 2 2 1 1 252 278 81
541 2 2 1 1 38 278 252
542 2 2 1 1 204 283 269
543 2 2 1 1 71 280 274
544 2 2 1 1 267 282 170
545 2 2 1 1 170 282 275
546 2 2 1 1 275 282 222
547 2 2 1 1 274 280 171
548 2 2 1 1 273 284 53
$EndElements
@@ -1,926 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "linear_elasticity.hpp"
#ifdef NVTX_DEBUG_HPP
#undef NVTX_COLOR
#define NVTX_COLOR ::nvtx::kCornflower
#include NVTX_DEBUG_HPP
#else
#define db1(...)
#define dbg(...)
#endif
using namespace mfem;
using mfem::future::dual;
using mfem::future::tuple;
using mfem::future::tensor;
using mfem::future::Weight;
using mfem::future::Gradient;
using mfem::future::Identity;
///////////////////////////////////////////////////////////////////////////////
LinearElasticityTimeDependentOperator::LinearElasticityTimeDependentOperator(
ParMesh &mesh_, int vorder)
: TimeDependentOperator(),
mesh(mesh_),
order(vorder)
{
dbg();
mesh.EnsureNodes();
dim = mesh.Dimension();
space_dim = mesh.SpaceDimension();
fec = std::make_unique<H1_FECollection>(order, dim);
fespace = std::make_unique<ParFiniteElementSpace>(&mesh, fec.get(), dim,
Ordering::byNODES);
nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
mfes = nodes->ParFESpace();
domain_attributes.SetSize(mesh_.attributes.Max());
const mfem::FiniteElement *fe= fespace->GetFE(0);
ir = &(IntRules.Get(fe->GetGeomType(),
fe->GetOrder() + fe->GetOrder() + fe->GetDim() - 1));
qs.reset(new QuadratureSpace(mesh, *ir));
fqs.reset(new FaceQuadratureSpace(mesh, order+1, FaceType::Boundary));
ups.reset(new future::UniformParameterSpace(
mesh, *ir, 1, false /* used_in_tensor_product */));
if (mesh.attributes.Size() > 0)
{
domain_attributes.SetSize(mesh.attributes.Max());
domain_attributes = 1;
}
//set the block sizes for the solution, rhs and tmp vectors
block_true_offsets.SetSize(3);
block_true_offsets[0] = 0;
block_true_offsets[1] = fespace->TrueVSize();
block_true_offsets[2] = fespace->TrueVSize();
block_true_offsets.PartialSum();
sol.Update(block_true_offsets); sol=0.0; sol.UseDevice(true);
rhs.Update(block_true_offsets); rhs=0.0; rhs.UseDevice(true);
tmp.Update(block_true_offsets); tmp=0.0; tmp.UseDevice(true);
res.SetSize(fespace->GetTrueVSize()); res=0.0; res.UseDevice(true);
displ.SetSpace(fespace.get()); displ=0.0;
displ.SetTrueVector();
displ.GetTrueVector().UseDevice(true);
veloc.SetSpace(fespace.get()); veloc=0.0;
veloc.SetTrueVector();
veloc.GetTrueVector().UseDevice(true);
this->width = 2*fespace->TrueVSize();
this->height = 2*fespace->TrueVSize();
MPI_Comm_rank(mesh.GetComm(),&myrank);
vol_force_mem.SetSize(10);
vol_force_mem.UseDevice(true);
vol_force_mem(0) = 0.0; // time
vol_force_mem(1) = 1.0; // period
vol_force_mem(2) = 0.0; // amplitude
vol_force_mem(3) = 0.5; // radius
vol_force_mem(4) = 0.0; // x coordinate of the center
vol_force_mem(5) = 0.0; // y coordinate of the center
vol_force_mem(6) = 0.0; // z coordinate of the center
vol_force_mem(7) = 5*vol_force_mem(1); // total train length
vol_force_mem(8) = vol_force_mem(7)/2.0;
vol_force_mem(9) = 2.0;
bdr_force_mem.SetSize(3);
bdr_force_mem.UseDevice(true);
bdr_force_mem(0) = 0.0; // time
bdr_force_mem(1) = 1.0; // period
bdr_force_mem(2) = 0.0; // amplitude
obj.reset();
}
///////////////////////////////////////////////////////////////////////////////
void LinearElasticityTimeDependentOperator::SetObjective(
std::shared_ptr<Operator> op_)
{
dbg();
if (op_.get() != nullptr)
{
obj = op_;
dbg("set the new objective and readjust the size of the operator and the state");
block_true_offsets.SetSize(4);
block_true_offsets[0] = 0;
block_true_offsets[1] = fespace->TrueVSize();
block_true_offsets[2] = fespace->TrueVSize();
block_true_offsets[3] = op_->Height();
block_true_offsets.PartialSum();
sol.Update(block_true_offsets); sol=0.0; sol.UseDevice(true);
rhs.Update(block_true_offsets); rhs=0.0; rhs.UseDevice(true);
tmp.Update(block_true_offsets); tmp=0.0; tmp.UseDevice(true);
this->width = block_true_offsets[3];
this->height = block_true_offsets[3];
}
else
{
dbg("New Objective");
obj.reset();
block_true_offsets.SetSize(3);
block_true_offsets[0] = 0;
block_true_offsets[1] = fespace->TrueVSize();
block_true_offsets[2] = fespace->TrueVSize();
block_true_offsets.PartialSum();
sol.Update(block_true_offsets); sol.UseDevice(true); sol = 0.0;
rhs.Update(block_true_offsets); rhs.UseDevice(true); rhs = 0.0;
tmp.Update(block_true_offsets); tmp.UseDevice(true); tmp = 0.0;
this->width = 2*fespace->TrueVSize();
this->height = 2*fespace->TrueVSize();
}
}
///////////////////////////////////////////////////////////////////////////////
template <int DI, typename scalar_t=real_t> struct QElasticityFunction
{
using vec_t = tensor<real_t, DI>;
using vecd_t = tensor<scalar_t, DI>;
using mat_t = tensor<real_t, DI, DI>;
using matd_t = tensor<scalar_t, DI, DI>;
struct Mass
{
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
const real_t &dens1,
const real_t &dens2,
const scalar_t &density,
const matd_t &J,
const real_t &w) const
{
const auto dens = density*dens2 + (1.0-density)*dens1;
const auto detJ = mfem::future::det(J);
return tuple{dens * u * detJ * w};
}
};
struct Elasticity
{
MFEM_HOST_DEVICE inline auto operator()(const matd_t &dudxi,
const real_t &L1,
const real_t &M1,
const real_t &L2,
const real_t &M2,
const scalar_t &density,
const matd_t &J,
const real_t &w) const
{
const matd_t JxW = transpose(inv(J)) * det(J) * w;
constexpr auto I = mfem::future::IsotropicIdentity<DI>();
const auto eps = mfem::future::sym(dudxi * mfem::future::inv(J));
const auto L = density*L2 + (1.0-density)*L1;
const auto M = density*M2 + (1.0-density)*M1;
return tuple{(L * tr(eps) * I + 2.0 * M * eps) * JxW};
}
};
struct DynamicBdrForce
{
// real_t time=0.0;
// real_t period=1.0;
// mfem::Vector* time_mem;
const real_t* time_mem;
//mfem::Memory<int> alt_time; check the documentation about Memory class for more details
DynamicBdrForce(mfem::Vector &tm)
// the Read method should be called on the vector passed as tm
// before calling the Mult on the differentiable operator when
// the time is changing, i.e., the values between the host
// and device have to be synchronized.
{
time_mem = tm.Read(); //get the device pointer
}
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
const matd_t &J,
const real_t &w) const
{
const real_t time = time_mem[0]; // (*time_mem)(0);
const real_t period = time_mem[1]; // (*time_mem)(1);
const real_t amplitude = time_mem[2]; // (*time_mem)(2);
const auto detJ = mfem::future::det(J);
// time dependent force in x direction
const real_t force_amplitude = (time > 0.0) ? amplitude*sin(
M_PI*time/period) : 0.0;
vecd_t force {0};//= vecd_t::Zero();
force(0) = force_amplitude;
return tuple{force * detJ * w};
}
};
struct DynamicVolForce
{
const real_t* time_mem;
DynamicVolForce(mfem::Vector& tm)
{
time_mem = tm.Read(); //get the device pointer
}
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
const vec_t &x,
const matd_t &J,
const real_t &w) const
{
const real_t time = *(time_mem+0);
const real_t period = *(time_mem+1);
const real_t amplitude = *(time_mem+2);
const real_t radius = *(time_mem+3);
const real_t L=*(time_mem+7);
const real_t t0=*(time_mem+8);
const real_t n=*(time_mem+9);
const real_t envelope_ampl= (time< L) ?pow(cos(M_PI*(time-t0)/L),n) : 0.0;
const real_t force_amplitude = (time > 0.0) ? amplitude*sin(
2.0*M_PI*time/period) : 0.0;
vecd_t force {0};
// time dependent force in x direction
force(0) = force_amplitude*envelope_ampl;
//compute the distance from the center of the force application
real_t dist_sq = 0.0;
for (int i = 0; i < DI; i++)
{
const real_t diff = x(i) - *(time_mem+4+i);
dist_sq += diff * diff;
}
// apply the force only within the specified radius
if (dist_sq > radius*radius)
{
force(0) = 0.0;
}
const auto detJ = mfem::future::det(J);
return tuple{force * detJ * w};
}
struct Objective
{
/* data */
const real_t* obj_mem;
Objective(mfem::Vector& tm)
{
obj_mem = tm.Read(); // get the device pointer
}
// takes velocity and returns squared velocity
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
const vec_t &x,
const mat_t &J,
const real_t &w
) const
{
// const real_t time = *(obj_mem+0);
const real_t radius = *(obj_mem+1);
//compute the distance from the center of the objective circle/sphere
real_t dist_sq = 0.0;
scalar_t obj = 0.0;
for (int i = 0; i < DI; i++)
{
const real_t diff = x(i) - *(obj_mem+2+i);
dist_sq += diff * diff;
obj += u(i) * u(i);
}
// apply the obj only within the specified radius
if (dist_sq > radius*radius)
{
obj = 0.0;
}
const auto detJ = mfem::future::det(J);
return tuple{obj * detJ * w};
}
};
struct ObjectiveGrad
{
/* data */
const real_t* obj_mem;
ObjectiveGrad(mfem::Vector& tm)
{
obj_mem = tm.Read(); // get the device pointer
}
// takes velocity and returns squared velocity
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
const vecd_t &x,
const matd_t &J,
const real_t &w) const
{
// const real_t time = *(obj_mem+0);
const real_t radius = *(obj_mem+1);
//compute the distance from the center of the objective circle/sphere
scalar_t dist_sq = 0.0;
vecd_t obj_grad;
real_t objc = 1.0;
for (int i = 0; i < DI; i++)
{
const real_t diff = x(i) - *(obj_mem+2+i);
dist_sq += diff * diff;
obj_grad(i) = 2.0 * u(i);
}
// apply the obj only within the specified radius
if (dist_sq > radius*radius)
{
objc = 0.0;
}
const auto detJ = mfem::future::det(J);
return tuple{objc* obj_grad * detJ * w};
}
};
};
};
///////////////////////////////////////////////////////////////////////////////
class InterpolatedCoefficient : public mfem::Coefficient
{
public:
InterpolatedCoefficient(mfem::Coefficient &c1, mfem::Coefficient &c2,
mfem::Coefficient &c3)
: coeff1(c1), coeff2(c2), coeff3(c3) {}
double Eval(mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip) override
{
real_t c1=coeff1.Eval(T, ip);
real_t c2=coeff2.Eval(T, ip);
real_t dens=coeff3.Eval(T, ip);
return c2*dens + (1.0-dens)*c1;
}
private:
mfem::Coefficient &coeff1, &coeff2, &coeff3;
};
///////////////////////////////////////////////////////////////////////////////
void LinearElasticityTimeDependentOperator::AssembleExplicit()
{
dbg();
// define the mass differentiable operator
{
dfem_mass_op = std::make_unique<mfem::future::DifferentiableOperator>(
std::vector<mfem::future::FieldDescriptor> { {FDispl, fespace.get()} },
std::vector<mfem::future::FieldDescriptor>
{
{Dens1, ups.get()},
{Dens2, ups.get()},
{Density, ups.get()},
{Coords, mfes}
},
mesh);
dfem_mass_op->SetParameters({ dens1.get(), dens2.get(), density.get(), nodes });
const auto minputs =
mfem::future::tuple
{
mfem::future::Value<FDispl>{},
mfem::future::Identity<Dens1>{},
mfem::future::Identity<Dens2>{},
mfem::future::Identity<Density>{},
mfem::future::Gradient<Coords>{},
mfem::future::Weight{}
};
const auto moutputs = mfem::future::tuple
{
mfem::future::Value<FDispl>{}
};
if (2 == space_dim)
{
typename QElasticityFunction<2>::Mass mass_func;
dfem_mass_op->AddDomainIntegrator(mass_func, minputs, moutputs, *ir,
domain_attributes);
}
else if (3 == space_dim)
{
typename QElasticityFunction<3>::Mass mass_func;
dfem_mass_op->AddDomainIntegrator(mass_func, minputs, moutputs, *ir,
domain_attributes);
}
}
// define the damp differentiable operator
{
dfem_damp_op = std::make_unique<mfem::future::DifferentiableOperator>(
std::vector<mfem::future::FieldDescriptor> { {FVeloc, fespace.get()} },
std::vector<mfem::future::FieldDescriptor>
{
{CMass1, ups.get()},
{CMass2, ups.get()},
{Density, ups.get()},
{Coords, mfes}
},
mesh);
dfem_damp_op->SetParameters({ cm1.get(), cm2.get(), density.get(), nodes });
const auto dinputs =
mfem::future::tuple
{
mfem::future::Value<FVeloc>{},
mfem::future::Identity<CMass1>{},
mfem::future::Identity<CMass2>{},
mfem::future::Identity<Density>{},
mfem::future::Gradient<Coords>{},
mfem::future::Weight{}
};
const auto doutputs = mfem::future::tuple
{
mfem::future::Value<FVeloc>{}
};
if (2 == space_dim)
{
typename QElasticityFunction<2>::Mass damp_func;
dfem_damp_op->AddDomainIntegrator(damp_func, dinputs, doutputs, *ir,
domain_attributes);
}
else if (3 == space_dim)
{
typename QElasticityFunction<3>::Mass damp_func;
dfem_damp_op->AddDomainIntegrator(damp_func, dinputs, doutputs, *ir,
domain_attributes);
}
}
// define the volumetric force differentiable operator
{
dfem_vol_force_op = std::make_unique<mfem::future::DifferentiableOperator>(
std::vector<mfem::future::FieldDescriptor> { {FDispl, fespace.get()} },
std::vector<mfem::future::FieldDescriptor>
{
{Coords, mfes}
},
mesh);
dfem_vol_force_op->SetParameters({ nodes });
const auto finputs =
mfem::future::tuple
{
mfem::future::Value<FDispl>{},
mfem::future::Value<Coords>{},
mfem::future::Gradient<Coords>{},
mfem::future::Weight{}
};
const auto foutputs =
mfem::future::tuple
{
mfem::future::Value<FDispl>{}
};
if (2 == space_dim)
{
typename QElasticityFunction<2>::DynamicVolForce vol_force_func(vol_force_mem);
dfem_vol_force_op->AddDomainIntegrator(vol_force_func, finputs, foutputs, *ir,
domain_attributes);
}
else if (3 == space_dim)
{
typename QElasticityFunction<3>::DynamicVolForce vol_force_func(vol_force_mem);
dfem_vol_force_op->AddDomainIntegrator(vol_force_func, finputs, foutputs, *ir,
domain_attributes);
}
}
// define the linear elasticity differentiable operator
{
dfem_forward_op = std::make_unique<mfem::future::DifferentiableOperator>(
std::vector<mfem::future::FieldDescriptor> { {FDispl, fespace.get()} },
std::vector<mfem::future::FieldDescriptor>
{
{Lambda1, ups.get()},
{Mu1, ups.get()},
{Lambda2, ups.get()},
{Mu2, ups.get()},
{Density, ups.get()},
{Coords, mfes}
},
mesh);
dfem_forward_op->SetParameters({ l1.get(), m1.get(), l2.get(), m2.get(), density.get(), nodes });
const auto finputs = mfem::future::tuple
{
mfem::future::Gradient<FDispl>{},
mfem::future::Identity<Lambda1>{},
mfem::future::Identity<Mu1>{},
mfem::future::Identity<Lambda2>{},
mfem::future::Identity<Mu2>{},
mfem::future::Identity<Density>{},
mfem::future::Gradient<Coords>{},
mfem::future::Weight{}
};
const auto foutputs = mfem::future::tuple
{
mfem::future::Gradient<FDispl>{}
};
if (2 == space_dim)
{
typename QElasticityFunction<2>::Elasticity elasticity_func;
dfem_forward_op->AddDomainIntegrator(elasticity_func, finputs, foutputs, *ir,
domain_attributes);
}
else if (3 == space_dim)
{
typename QElasticityFunction<3>::Elasticity elasticity_func;
dfem_forward_op->AddDomainIntegrator(elasticity_func, finputs, foutputs, *ir,
domain_attributes);
}
}
// Spectral mass-matrix
{
InterpolatedCoefficient interp_dens1(*cdens1, *cdens2, *cdensity);
IntegrationRules gll_rules(0, Quadrature1D::GaussLobatto);
const IntegrationRule &ir_ni = gll_rules.Get(mesh.GetTypicalElementGeometry(),
2 * order - 1);
ParBilinearForm bf_lor(fespace.get());
auto *mv_blfi = new VectorMassIntegrator(interp_dens1);
mv_blfi->SetIntRule(&ir_ni);
//bf_lor.AddDomainIntegrator(new LumpedIntegrator(mv_blfi));
bf_lor.AddDomainIntegrator(mv_blfi);
bf_lor.Assemble();
bf_lor.Finalize();
M_lor.reset(bf_lor.ParallelAssemble());
}
// allocate the AMG preconditioner and CG solver
// for the mass matrix
{
amg = std::make_unique<HypreBoomerAMG>();
amg->SetPrintLevel(1);
amg->SetOperator(*M_lor);
cg = std::make_unique<CGSolver>(mesh.GetComm());
cg->SetRelTol(1e-7);
cg->SetAbsTol(1e-12);
cg->SetMaxIter(500);
cg->SetPrintLevel(0);
cg->SetOperator(*dfem_mass_op);
//cg->SetOperator(*M_lor);
cg->SetPreconditioner(*amg);
cg->iterative_mode=false;
}
//set the zero bdr conditions
{
Array<int> bdr_attr; bdr_attr.SetSize(mesh.bdr_attributes.Max());
bdr_attr=0;
for (const auto &it:zero_bdrs)
{
bdr_attr[it-1]=1.0;
}
fespace->GetEssentialTrueDofs(bdr_attr,ess_tdof_list);
}
}
///////////////////////////////////////////////////////////////////////////////
void LinearElasticityTimeDependentOperator::Mult(const Vector &x,
Vector &y) const
{
db1();
real_t time = this->GetTime();
BlockVector bx(const_cast<Vector&>(x), block_true_offsets);
BlockVector by(y, block_true_offsets);
displ.GetTrueVector().Set(1.0,bx.GetBlock(0));
veloc.GetTrueVector().Set(1.0,bx.GetBlock(1));
//set zero BC
{
int N = ess_tdof_list.Size();
real_t *dp=displ.GetTrueVector().ReadWrite();
real_t *vp=veloc.GetTrueVector().ReadWrite();
const int *ep = ess_tdof_list.Read();
mfem::forall(N, [=] MFEM_HOST_DEVICE(int i)
{
dp[ep[i]] = 0.0;
vp[ep[i]] = 0.0;
});
}
//displ.SetFromTrueVector();
//veloc.SetFromTrueVector();
by.GetBlock(0).Set(1.0, veloc.GetTrueVector()); // dx/dt = velocity
// compute the residual
// 1) add external volumetric forces
real_t* pvol_force_mem=vol_force_mem.HostReadWrite(); //get the host pointer
pvol_force_mem[0]=time; //set the current time to be pass to the integrator
vol_force_mem.Read(); //copy force_mem from host to device
// call the kernel computing f_ext
// dfem_vol_force_op->SetParameters({nodes}); // it is already set
dfem_vol_force_op->Mult(veloc.GetTrueVector(),res);
// 2) compute the mass proportional viscous damping term
// dfem_damp_op->SetParameters({ cm1.get(), cm2.get(), density.get(), nodes });
dfem_damp_op->Mult(veloc.GetTrueVector(), tmp.GetBlock(1));
res -= tmp.GetBlock(1);
// 3) add the stiffness proportional viscous damping term
// 4) add the elastic force term
dfem_forward_op->Mult(displ.GetTrueVector(),tmp.GetBlock(0));
res-= tmp.GetBlock(0);
//dfem_mass_op->SetParameters({dens1.get(), dens2.get(), density.get(), nodes});
cg->Mult(res, by.GetBlock(1)); // solve for acceleration
//check if objective is valid
if (obj.get()!=nullptr)
{
//evaluate the objective contribution
obj->Mult(x,by.GetBlock(2));
}
}
///////////////////////////////////////////////////////////////////////////////
// implements the adjoint reverse time integration
// i.e. x=[l_q,l_v, L_\rho]^T y=x' - i.e. the derivative with respect to \tau=T-t
// before calling MultTranspose one should set the sol vector with the
// solution for the forward problem at time t
void LinearElasticityTimeDependentOperator::AdjointMult(const Vector &x,
Vector &y) const
{
dbg();
BlockVector bx(const_cast<Vector&>(x), block_true_offsets);
BlockVector by(y, block_true_offsets);
y=0.0;
}
///////////////////////////////////////////////////////////////////////////////
void LinearElasticityTimeDependentOperator::ImplicitSolve(
const real_t dt,
const Vector &x,
Vector &k)
{
}
///////////////////////////////////////////////////////////////////////////////
template <int DI, typename scalar_t=real_t> struct QObjectiveFunction
{
using matd_t = tensor<scalar_t, DI, DI>;
using vecd_t = tensor<scalar_t, DI>;
using vec_t = tensor<real_t, DI>;
using mat_t = tensor<real_t, DI, DI>;
struct Objective1
{
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
const real_t &co,
const matd_t &J,
const real_t &w) const
{
scalar_t rez {0.0};
for (int i=0; i<DI; i++) { rez = rez + u(i)*u(i); }
rez = rez*co;
const auto detJ = mfem::future::det(J);
return tuple{rez * detJ * w};
}
};
struct Objective2
{
const real_t s1, s2;
Objective2(real_t s1 = 1.0, real_t s2 = 1.0): s1(s1), s2(s2) { }
MFEM_HOST_DEVICE inline auto operator()(const vecd_t &u,
const vecd_t &v,
const real_t &co,
const matd_t &J,
const real_t &w) const
{
scalar_t rez = 0.0;
for (int i=0; i<DI; i++)
{
rez = rez + u(i)*u(i)*s1 + v(i)*v(i)*s2;
}
const auto detJ = mfem::future::det(J);
return tuple{rez * detJ * w};
}
};
};
///////////////////////////////////////////////////////////////////////////////
ExampleObjectiveIntegrand::ExampleObjectiveIntegrand(ParFiniteElementSpace*
fes_,
std::shared_ptr<mfem::Coefficient> objc_)
{
dbg();
fes=fes_;
fes->GetParMesh()->EnsureNodes();
disp.SetSpace(fes); disp=0.0;
velo.SetSpace(fes); velo=0.0;
this->width=2*fes->GetTrueVSize(); //disp.Size() + veloc.Size()
this->height=1; //returns 3 objectives
grad=nullptr;
//set the block sizes for the solution, rhs and tmp vectors
block_true_offsets.SetSize(3);
block_true_offsets[0] = 0;
block_true_offsets[1] = fes->TrueVSize();
block_true_offsets[2] = fes->TrueVSize();
block_true_offsets.PartialSum();
const mfem::FiniteElement *fe= fes->GetFE(0);
ir = &(IntRules.Get(fe->GetGeomType(),
fe->GetOrder() + fe->GetOrder() + fe->GetDim() - 1));
qs.reset(new QuadratureSpace(*(fes->GetParMesh()), *ir));
ups.reset(new future::UniformParameterSpace(
*(fes->GetParMesh()), *ir, 1, false /* used_in_tensor_product */));
if (fes->GetParMesh()->attributes.Size() > 0)
{
domain_attributes.SetSize(fes->GetParMesh()->attributes.Max());
domain_attributes = 1;
}
nodes = static_cast<ParGridFunction *>(fes->GetParMesh()->GetNodes());
mfes = nodes->ParFESpace();
SetCoefficients(objc_);
}
///////////////////////////////////////////////////////////////////////////////
void ExampleObjectiveIntegrand::SetCoefficients(
std::shared_ptr<mfem::Coefficient> objc)
{
dbg();
co=objc;
if (co.get()!=nullptr)
{
//project the coefficient
density.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
density->Project(*co);
}
else
{
density.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
density->SetConstant(1.0);
}
res.SetSize(density->Size());
//allocate the differentiable operator
{
obj = std::make_unique<mfem::future::DifferentiableOperator>(
std::vector<mfem::future::FieldDescriptor> { {FDispl, fes} },
std::vector<mfem::future::FieldDescriptor>
{
{Density, ups.get()},
{Coords, mfes}
},
*(fes->GetParMesh())
);
obj->SetParameters( {density.get(), nodes} );
const auto finputs =
mfem::future::tuple
{
mfem::future::Value<FDispl>{},
mfem::future::Identity<Density>{},
mfem::future::Gradient<Coords>{},
mfem::future::Weight{}
};
const auto foutputs =
mfem::future::tuple
{
mfem::future::Identity<Density>{}
};
int space_dim=fes->GetParMesh()->SpaceDimension();
if (2 == space_dim)
{
using mfem::future::dual;
using dual_t = dual<real_t, real_t>;
typename QObjectiveFunction<2,dual_t>::Objective1 obj_func;
auto derivatives = std::integer_sequence<size_t, FDispl, Coords> {};
obj->AddDomainIntegrator(obj_func, finputs, foutputs, *ir, domain_attributes,
derivatives);
}
else if ( 3 == space_dim)
{
using mfem::future::dual;
using dual_t = dual<real_t, real_t>;
typename QObjectiveFunction<3,dual_t>::Objective1 obj_func;
auto derivatives = std::integer_sequence<size_t, FDispl, Coords> {};
obj->AddDomainIntegrator(obj_func, finputs, foutputs, *ir, domain_attributes,
derivatives);
}
}
}
///////////////////////////////////////////////////////////////////////////////
void ExampleObjectiveIntegrand::Mult(const Vector &x, Vector &y) const
{
db1();
mfem::Array<int> lblock_true_offsets;
lblock_true_offsets.SetSize(4);
lblock_true_offsets[0] = 0;
lblock_true_offsets[1] = fes->TrueVSize();
lblock_true_offsets[2] = fes->TrueVSize();
lblock_true_offsets[3] = x.Size()-2*fes->TrueVSize();
lblock_true_offsets.PartialSum();
BlockVector bx(const_cast<Vector&>(x), lblock_true_offsets);
obj->Mult(bx.GetBlock(0),res);
// sum up the weighted values
real_t lp = mfem::InnerProduct(fes->GetComm(), res, *density);
y[0]=lp;
}
///////////////////////////////////////////////////////////////////////////////
void ExampleObjectiveIntegrand::EvalGradient(const Vector &x,
Vector &grad_y) const
{
dbg();
mfem::Array<int> lblock_true_offsets;
lblock_true_offsets.SetSize(4);
lblock_true_offsets[0] = 0;
lblock_true_offsets[1] = fes->TrueVSize();
lblock_true_offsets[2] = fes->TrueVSize();
lblock_true_offsets[3] = x.Size()-2*fes->TrueVSize();
lblock_true_offsets.PartialSum();
BlockVector bx(const_cast<Vector&>(x), lblock_true_offsets);
BlockVector by(grad_y, lblock_true_offsets); by=0.0;
disp.SetFromTrueDofs(bx.GetBlock(0));
std::shared_ptr<mfem::future::DerivativeOperator> dobj_du;
dobj_du=obj->GetDerivative(FDispl, {&disp}, {density.get(), nodes});
if (Mpi::Root())
{
std::cout << "Op size: " << dobj_du->Height()<<" "<<dobj_du->Width()<<
std::endl;
std::cout << " disp size:"<< bx.GetBlock(0).Size()<<std::endl;
std::cout << " dens size:"<< density->Size()<<std::endl;
}
dobj_du->MultTranspose(*density,by.GetBlock(0));
}
@@ -1,329 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef LINEAR_ELASTICITY_HPP
#define LINEAR_ELASTICITY_HPP
#include "mfem.hpp"
///////////////////////////////////////////////////////////////////////////////
class LinearElasticityTimeDependentOperator: public mfem::TimeDependentOperator
{
public:
LinearElasticityTimeDependentOperator(mfem::ParMesh &mesh_, int vorder =1);
virtual ~LinearElasticityTimeDependentOperator() { }
void Mult(const mfem::Vector &x, mfem::Vector &y) const override;
virtual void AdjointMult(const mfem::Vector &x, mfem::Vector &y) const;
void ImplicitSolve(const mfem::real_t dt,
const mfem::Vector &x,
mfem::Vector &k) override;
// sets the objective integraand which will be integrated with the state of the system
void SetObjective(std::shared_ptr<mfem::Operator> op_);
// Assemble the explicit operators
// must be called after setting all material coefficients
// and before time stepping
void AssembleExplicit();
void SetDensity(mfem::Coefficient &rho)
{
density = std::make_shared<mfem::CoefficientVector>(*qs,
mfem::CoefficientStorage::FULL);
cdensity = &rho;
density->Project(rho);
}
// l1, m1 are the Lame parameters for material 1
// l2, m2 are the Lame parameters for material 2
void SetElasticityCoefficients(mfem::Coefficient& l1_,
mfem::Coefficient& m1_,
mfem::Coefficient& l2_,
mfem::Coefficient& m2_)
{
l1.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
l2.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
m1.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
m2.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
l1->Project(l1_);
m1->Project(m1_);
l2->Project(l2_);
m2->Project(m2_);
}
// rho1 and rho2 are the density coefficients for material 1 and 2
void SetDensityMaterialCoefficients(mfem::Coefficient& rho1_,
mfem::Coefficient& rho2_)
{
dens1.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
dens2.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
cdens1 = &rho1_;
cdens2 = &rho2_;
dens1->Project(rho1_);
dens2->Project(rho2_);
}
// c1 and c2 are velocity proportional damping coefficients for material 1 and 2
// both of them are considered to be time dependent
void SetDampingMaterialCoefficients(mfem::Coefficient& c1_,
mfem::Coefficient& c2_)
{
cm1.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
cm2.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
cm1->Project(c1_);
cm2->Project(c2_);
}
// dl1, dm1 are the strain velocity proportional damping coefficients
// for material 1 and dl2, dm2 for material 2
// all of them are considered to be time dependent
void SetDampingMaterialCoefficients(mfem::Coefficient& dl1_,
mfem::Coefficient& dm1_,
mfem::Coefficient& dl2_,
mfem::Coefficient& dm2_)
{
dl1.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
dl2.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
dm1.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
dm2.reset(new mfem::CoefficientVector(*qs, mfem::CoefficientStorage::FULL));
dl1->Project(dl1_);
dm1->Project(dm1_);
dl2->Project(dl2_);
dm2->Project(dm2_);
}
mfem::ParGridFunction& GetDisplacement() { return displ; }
mfem::ParGridFunction& GetVelocity() { return veloc; }
mfem::Array<int>& GetTrueBlockOffsets() { return block_true_offsets;}
mfem::Vector& GetState() {return sol;}
const mfem::ParFiniteElementSpace* GetFESpace() { return fespace.get();}
void SetZeroBdr(int bdr_attr)
{
zero_bdrs.insert(bdr_attr);
}
void SetBdrLoad(int attr)
{
bdr_loads_markers.insert(attr);
}
void SetVolForce(mfem::real_t period, mfem::real_t amplitude, mfem::real_t rad,
mfem::real_t xc=0.0, mfem::real_t yc=0.0, mfem::real_t zc=0.0,
mfem::real_t L=5.0, mfem::real_t t0=0.0, mfem::real_t n=2.0)
{
// copy data to the host
mfem::real_t* pvol_force_mem = vol_force_mem.HostReadWrite();
pvol_force_mem[1] = period;
pvol_force_mem[2] = amplitude;
pvol_force_mem[3] = rad;
pvol_force_mem[4] = xc;
pvol_force_mem[5] = yc;
pvol_force_mem[6] = zc;
pvol_force_mem[7] = L;
pvol_force_mem[8] = t0;
pvol_force_mem[9] = n;
// copy data to the device
vol_force_mem.Read();
}
void AddState(mfem::real_t t, mfem::Vector& state)
{
int cind=adjoint_data.ind % adjoint_data.max_states;
adjoint_data.states[cind]=state;
adjoint_data.times[cind]=t;
adjoint_data.ind++;
}
private:
mfem::ParMesh &mesh;
int order;
std::unique_ptr<mfem::FiniteElementCollection> fec;
std::unique_ptr<mfem::ParFiniteElementSpace> fespace;
int dim;
int space_dim;
int myrank;
mfem::ParGridFunction *nodes;
mfem::ParFiniteElementSpace *mfes;
mfem::Array<int> domain_attributes;
const mfem::IntegrationRule *ir;
mutable mfem::ParGridFunction displ;
mutable mfem::ParGridFunction veloc;
mutable mfem::ParGridFunction accel;
mutable mfem::BlockVector sol;
mutable mfem::BlockVector rhs;
mutable mfem::BlockVector tmp;
mutable mfem::Vector res;
mfem::Array<int> block_true_offsets;
std::unique_ptr<mfem::future::UniformParameterSpace> ups;
std::unique_ptr<mfem::QuadratureSpace> qs;
std::unique_ptr<mfem::FaceQuadratureSpace> fqs;
// linear elasticty coefficients in dFEM form
// l1, m1 - material 1
// l2, m2 - material 2
std::unique_ptr<mfem::CoefficientVector> l1, l2;
std::unique_ptr<mfem::CoefficientVector> m1, m2;
// linear elasticity damping coefficients in dFEM form
// strain proportional damping for material 1 and 2
std::unique_ptr<mfem::CoefficientVector> dl1, dl2;
std::unique_ptr<mfem::CoefficientVector> dm1, dm2;
// damping mass coefficients in dFEM form
// (velocity proportional damping) for material 1 and 2
std::unique_ptr<mfem::CoefficientVector> cm1, cm2;
// density coefficients in dFEM form for material 1 and 2
std::unique_ptr<mfem::CoefficientVector> dens1, dens2;
mfem::Coefficient *cdens1, *cdens2;
// density coefficient for topology optimization
std::shared_ptr<mfem::CoefficientVector> density;
mfem::Coefficient *cdensity;
static constexpr int FDispl = 0; //grid function displacement
static constexpr int FVeloc = 1; //grid function velocity
// elasticity Coefficient Vectors
static constexpr int Lambda1 = 2, Lambda2 = 3, Mu1 = 4, Mu2 = 5;
// damping Coefficient Vectors
static constexpr int DLambda1 = 6, DMu1 = 7, DLambda2 = 8, DMu2 = 9;
static constexpr int CMass1 = 10, CMass2 = 11; // damping mass coeff vectors
static constexpr int Dens1 = 12, Dens2 = 13; // density Coefficient Vectors
// density for topology optimization
static constexpr int Density = 14; // coefficient vector
static constexpr int Coords = 15; // coordinates grid function
// DFEM forward related definitions
std::unique_ptr<mfem::future::DifferentiableOperator> dfem_forward_op;
std::unique_ptr<mfem::future::DifferentiableOperator> dfem_mass_op;
std::unique_ptr<mfem::future::DifferentiableOperator> dfem_damp_op;
std::unique_ptr<mfem::future::DifferentiableOperator> dfem_vol_force_op;
std::unique_ptr<mfem::HypreParMatrix> M_lor;
std::unique_ptr<mfem::CGSolver> cg;
std::unique_ptr<mfem::HypreBoomerAMG> amg;
// zero bdrs
std::set<int> zero_bdrs;
// time dependent memory vector for dynamic force
// the force is applied on all boundary attributes in bdr_loads_markers
std::set<int> bdr_loads_markers;
mutable mfem::Vector bdr_force_mem; // [0] - time, [1] - period, [2] - amplitude
// volumetric force parameters
// A*sin(2*pi*t/T)*cos^n (pi (t-t_0)/L)
// [0] - time, [1] - period, [2] - amplitude, [3] - radius
// [4],[5],[6] - point coordinates of the center of the force application
// [7] - L total train length - could be proportional to the period [1]
// [8] - t_0 center of the train
// [9] - n the envelope power
mutable mfem::Vector vol_force_mem;
// zero bdr dofs - constructed during the corrsponding Assemble calls
mfem::Array<int> ess_tdof_list;
//objective/constraints integrand
//obj->Mult(x,y)
//takes state vector s and returns y which consists of multiple objectives/constraints
std::shared_ptr<mfem::Operator> obj;
struct
{
mfem::real_t Tfinal;
mfem::Vector states[2];
mfem::real_t times[2];
int ind=0;
int max_states=2;
} adjoint_data;
};
///////////////////////////////////////////////////////////////////////////////
class ExampleObjectiveIntegrand: public mfem::Operator
{
public:
ExampleObjectiveIntegrand(mfem::ParFiniteElementSpace* fes_,
std::shared_ptr<mfem::Coefficient> objc);
void SetCoefficients( std::shared_ptr<mfem::Coefficient> objc);
//evaluates the QoIs y[1] for a given state x[2 x fes_->GetTrueVSize()]
virtual void Mult(const mfem::Vector &x, mfem::Vector &y) const override;
mfem::real_t EvalScalar(const mfem::Vector &x) const
{
mfem::Vector y(1);
Mult(x, y);
return y[0];
}
void EvalGradient(const mfem::Vector &x, mfem::Vector &grad) const;
private:
std::shared_ptr<mfem::Coefficient> co;
mfem::ParFiniteElementSpace* fes;
mutable mfem::ParGridFunction disp;
mutable mfem::ParGridFunction velo;
mfem::Operator* grad;
static constexpr int FDispl = 0; //grid function displacement
static constexpr int FVeloc = 1; //grid function velocity
static constexpr int Density = 14; // coefficient vector
static constexpr int Coords = 15; // coordinates grid function
// DFEM related definitions (3 objectives)
std::unique_ptr<mfem::future::DifferentiableOperator> obj;
// density coefficient for computing the objective function
std::shared_ptr<mfem::CoefficientVector> density;
mfem::Array<int> block_true_offsets;
//uniform parameter space
std::unique_ptr<mfem::future::UniformParameterSpace> ups;
//quadrature space for the coefficient
std::unique_ptr<mfem::QuadratureSpace> qs;
mfem::ParGridFunction *nodes;
mfem::ParFiniteElementSpace *mfes;
mfem::Array<int> domain_attributes;
const mfem::IntegrationRule *ir;
mutable mfem::Vector res;
};
#endif // LINEAR_ELASTICITY_HPP
-473
View File
@@ -1,473 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "linear_elasticity.hpp"
#include "../chpt/dynamic_checkpointing.hpp"
#include "../chpt/fixed_slot_checkpoint_storage.hpp"
#include <cmath>
#include <cstring>
using namespace mfem;
using namespace std;
#ifdef NVTX_DEBUG_HPP
#undef NVTX_COLOR
#define NVTX_COLOR ::nvtx::kSalmon
#include NVTX_DEBUG_HPP
#else
#define dbg(...)
#endif
#define MESH_PATH MFEM_SOURCE_DIR "/miniapps/mtop/examples/"
constexpr auto MESH_TRI = MESH_PATH "dyn_hex2d_tri.msh";
constexpr auto MESH_QUAD = MESH_PATH "dyn_hex2d_quad.msh";
///////////////////////////////////////////////////////////////////////////////
struct State
{
mfem::real_t time = 0.0; // time of the state
mfem::real_t dt = 0.0;
mfem::real_t obj = 0.0; // accumulated objective
mfem::Vector v; // state of the system
};
// Snapshot = *view* (non-owning) used only during Store() packing and Read() callback
struct StateSnapshotView
{
mfem::real_t time = 0.0;
mfem::real_t dt = 0.0;
mfem::real_t obj = 0.0;
// Points to n*sizeof(real_t) bytes:
// - during Store(): points to current State::v data (host)
// - during Read(): points into the storage slot bytes (valid only during callback)
const std::byte *v_bytes = nullptr;
};
///////////////////////////////////////////////////////////////////////////////
class StateSnapshotViewPacker
{
size_t n = 0;
public:
explicit StateSnapshotViewPacker(size_t n) : n(n)
{
MFEM_VERIFY(n > 0, "StateSnapshotViewPacker: n must be > 0.");
}
std::size_t SlotBytes() const
{
return (size_t)(3 + n) * sizeof(mfem::real_t);
}
void Pack(const StateSnapshotView &s, void *dst) const
{
MFEM_VERIFY(dst != nullptr, "Pack: dst is null.");
MFEM_VERIFY(s.v_bytes != nullptr, "Pack: snapshot v_bytes is null.");
auto *b = static_cast<std::byte*>(dst);
std::memcpy(b + 0*sizeof(mfem::real_t), &s.time, sizeof(mfem::real_t));
std::memcpy(b + 1*sizeof(mfem::real_t), &s.dt, sizeof(mfem::real_t));
std::memcpy(b + 2*sizeof(mfem::real_t), &s.obj, sizeof(mfem::real_t));
std::memcpy(b + 3*sizeof(mfem::real_t), s.v_bytes, n * sizeof(mfem::real_t));
}
// Important: Unpack returns a *view* pointing into src bytes (no allocation).
void Unpack(const void *src, StateSnapshotView &ssv) const
{
MFEM_VERIFY(src != nullptr, "Unpack: src is null.");
const auto *b = static_cast<const std::byte*>(src);
std::memcpy(&ssv.time, b + 0*sizeof(mfem::real_t), sizeof(mfem::real_t));
std::memcpy(&ssv.dt, b + 1*sizeof(mfem::real_t), sizeof(mfem::real_t));
std::memcpy(&ssv.obj, b + 2*sizeof(mfem::real_t), sizeof(mfem::real_t));
ssv.v_bytes = b + 3*sizeof(mfem::real_t);
}
size_t VectorSize() const { return n; }
};
///////////////////////////////////////////////////////////////////////////////
// struct AdjState
// {
// mfem::real_t time;
// mfem::real_t obj;
// mfem::Vector adj;
// mfem::Vector grd;
// };
///////////////////////////////////////////////////////////////////////////////
/// \brief The IsoElasticyLambdaCoeff class converts E modulus of elasticity
/// and Poisson's ratio to Lame's lambda coefficient
class IsoElasticyLambdaCoeff : public mfem::Coefficient
{
mfem::Coefficient *E, *nu;
public:
/// Constructor - takes as inputs E modulus and Poisson's ratio
IsoElasticyLambdaCoeff(mfem::Coefficient *E,
mfem::Coefficient *nu):
E(E), nu(nu) { }
/// Evaluates the Lame's lambda coefficient
real_t Eval(mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip) override
{
const real_t EE = E->Eval(T, ip);
const real_t nn = nu->Eval(T, ip);
constexpr auto Lambda = [](const real_t E, const real_t nu)
{
return E * nu / (1.0 + nu) / (1.0 - 2.0 * nu);
};
return Lambda(EE, nn);
}
};
///////////////////////////////////////////////////////////////////////////////
/// \brief The IsoElasticySchearCoeff class converts E modulus of elasticity
/// and Poisson's ratio to Shear coefficient
///
class IsoElasticySchearCoeff : public mfem::Coefficient
{
mfem::Coefficient *E, *nu;
public:
/// Constructor - takes as inputs E modulus and Poisson's ratio
IsoElasticySchearCoeff(mfem::Coefficient *E_, mfem::Coefficient *nu_):
E(E_), nu(nu_) { }
/// Evaluates the shear coefficient coefficient
real_t Eval(mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip) override
{
const real_t EE = E->Eval(T, ip);
const real_t nn = nu->Eval(T, ip);
constexpr auto Schear = [](const real_t E, const real_t nu)
{
return E / (2.0 * (1.0 + nu));
};
return Schear(EE, nn);
}
};
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
dbg();
// Initialize MPI and HYPRE.
Mpi::Init();
Hypre::Init();
// Parse command-line options.
const char *mesh_file = MESH_QUAD;
const char *device_config = "cpu";
int order = 3;
bool mesh_tri = false;
bool mesh_quad = false;
int par_ref_levels = 1;
// int max_steps = 1000;
bool paraview = false;
bool visualization = true;
int ode_solver_type = 4;
real_t Tfinal = 1.0;
real_t dt = 0.005;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&mesh_tri, "-tri", "--triangular", "-no-tri",
"--no-triangular", "Enable or not triangular mesh.");
args.AddOption(&mesh_quad, "-quad", "--quadrilateral", "-no-quad",
"--no-quadrilateral", "Enable or not quadrilateral mesh.");
args.AddOption(&par_ref_levels, "-prl", "--par-ref-levels",
"Number of parallel mesh refinement levels.");
// args.AddOption(&max_steps, "-ms", "--max-steps",
// "Maximum number of time steps.");
args.AddOption(&paraview, "-pa", "--paraview", "-no-pa",
"--no-paraview", "Enable or not Paraview output.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization", "Enable or not visualization.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
ODESolver::Types.c_str());
args.AddOption(&Tfinal, "-T", "--tfinal",
"Terminate when accumulated time reaches Tfinal.");
args.AddOption(&dt, "-dt", "--dt", "Time step.");
args.ParseCheck();
// Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (Mpi::Root()) { device.Print(); }
// Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh mesh(mesh_tri ? MESH_TRI : mesh_quad ? MESH_QUAD : mesh_file, 1, 1);
const int dim = mesh.Dimension();
// Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 1000 elements.
{
const int ref_levels =
(int)floor(log(1000. / mesh.GetNE()) / log(2.) / dim);
for (int l = 0; l < ref_levels; l++) { mesh.UniformRefinement(); }
}
if (Mpi::Root())
{
std::cout << "Number of elements: " << mesh.GetNE() << std::endl;
}
// Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
for (int l = 0; l < par_ref_levels; l++) { pmesh.UniformRefinement(); }
// Allocate the time dependent linear elasticity operator
LinearElasticityTimeDependentOperator lin_elasticity_op(pmesh, order);
// Set the material coefficients
// density coefficient for topology optimization
ConstantCoefficient rho_coef(0.5);
// Set elasticity coefficients for material 1 and 2
ConstantCoefficient E1(0.1);
ConstantCoefficient E2(1.0);
ConstantCoefficient nu1(0.3);
ConstantCoefficient nu2(0.3);
//Lame coefficients
IsoElasticyLambdaCoeff lambda1(&E1, &nu1);
IsoElasticySchearCoeff mu1(&E1, &nu1);
IsoElasticyLambdaCoeff lambda2(&E2, &nu2);
IsoElasticySchearCoeff mu2(&E2, &nu2);
// Set density coefficients for material 1 and 2
ConstantCoefficient dens1_coef(0.5);
ConstantCoefficient dens2_coef(1.0);
// Set damping coefficients
ProductCoefficient cm1_coef(0.02, dens1_coef);
ProductCoefficient cm2_coef(0.02, dens2_coef);
ProductCoefficient cl1_coef(0.01, lambda1);
ProductCoefficient cmu1_coef(0.01, mu1);
ProductCoefficient cl2_coef(0.01, lambda2);
ProductCoefficient cmu2_coef(0.01, mu2);
lin_elasticity_op.SetElasticityCoefficients(lambda1, mu1, lambda2, mu2);
lin_elasticity_op.SetDensityMaterialCoefficients(dens1_coef, dens2_coef);
lin_elasticity_op.SetDampingMaterialCoefficients(cm1_coef, cm2_coef);
lin_elasticity_op.SetDampingMaterialCoefficients(cl1_coef, cmu1_coef,
cl2_coef, cmu2_coef);
lin_elasticity_op.SetDensity(rho_coef);
// set bottom bdr to zero (both the velocities and the displacements)
lin_elasticity_op.SetZeroBdr(1);
lin_elasticity_op.SetVolForce(1.0 /*period*/, 1.0 /*amplitude*/, 0.2 /*radius*/,
0.0 /*x center*/, 0.0 /*y center*/, 0.0 /*z center*/,
5.0 /* train length*/, 2.5 /*center of the train*/, 2.0 /*power*/);
lin_elasticity_op.AssembleExplicit();
// test mult explicit
{
BlockVector tst;
tst.UseDevice(true);
tst.Update(lin_elasticity_op.GetTrueBlockOffsets());
tst = 0.0; // tst.Randomize();
tst.Read();
BlockVector grd;
grd.Update(lin_elasticity_op.GetTrueBlockOffsets());
grd = 0.0;
lin_elasticity_op.Mult(tst, grd);
lin_elasticity_op.GetVelocity().SetFromTrueDofs(grd.GetBlock(1));
}
auto obj = std::make_shared<ExampleObjectiveIntegrand>
(lin_elasticity_op.GetDisplacement().ParFESpace(),
std::shared_ptr<Coefficient>());
// set the objective for the integration process
lin_elasticity_op.SetObjective(obj);
ParaViewDataCollection paraview_dc("isoel", &pmesh);
if (paraview)
{
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(order);
paraview_dc.SetDataFormat(VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.RegisterField("disp", &(lin_elasticity_op.GetDisplacement()));
paraview_dc.RegisterField("velo", &(lin_elasticity_op.GetVelocity()));
}
// 4. Define the ODE solver used for time integration.
// Several explicit Runge-Kutta methods are available.
auto ode_solver = ODESolver::Select(ode_solver_type);
lin_elasticity_op.SetTime(0.0);
ode_solver->Init(lin_elasticity_op);
// Forward computations
{
// number of snapshots to be stored by the checkpointing process
const int s = 10;
// define the packer object
const int max_slots = lin_elasticity_op.GetState().Size();
StateSnapshotViewPacker packer(max_slots);
// storage stores StateSnapshotView snapshots using fixed-size slots
using Storage = mfem::FixedSlotMemoryCheckpointStorage<
/* Snapshot */ StateSnapshotView,
/* Packer */ StateSnapshotViewPacker>;
Storage storage(s, packer);
// Snapshot type is StateSnapshotView
using Checkpointing = mfem::DynamicCheckpointing<
/* Snapshot */ StateSnapshotView,
/* Storage */ Storage>;
Checkpointing ckpt(s, storage);
// Returns view of the State and avoids data transfer
auto make_snapshot = [&](const State &u) -> StateSnapshotView
{
MFEM_VERIFY(u.v.Size() == max_slots, "make_snapshot: State.v size changed!");
// Ensure host access if MFEM device is in use:
const mfem::real_t *vh = u.v.HostRead();
StateSnapshotView snap;
snap.time = u.time;
snap.obj = u.obj;
snap.v_bytes = reinterpret_cast<const std::byte*>(vh);
return snap;
};
// Transfers data from the snaphot view to the State u_out.
// auto restore_snapshot = [&](const StateSnapshotView &snap, State &u_out)
// {
// u_out.time = snap.time;
// u_out.obj = snap.obj;
// if (u_out.v.Size() != max_slots) { u_out.v.SetSize(max_slots); }
// mfem::real_t *vh = u_out.v.HostWrite();
// std::memcpy(vh,
// snap.v_bytes,
// (std::size_t)max_slots * sizeof(mfem::real_t));
// //make sure that the date is on the device
// u_out.v.Read(true);
// };
using Step = mfem::DynamicCheckpointing<
/* Snapshot */ StateSnapshotView,
/* Storage */ Storage>::Step;
// execute one integration step
auto primal_step = [&](State &u_st, Step i)
{
// begin with curent state u_st
real_t t = u_st.time;
real_t ldt = dt;
real_t obj = u_st.obj;
// make sure the integration does not overjump Tfinal
if ((t+ldt)>Tfinal)
{
ldt = Tfinal - t;
}
// advance u_st
ode_solver->Step(u_st.v, t, ldt);
// TO-DO update objective
obj = u_st.v[u_st.v.Size()-1];
// return updated u_st
u_st.dt = t - u_st.time;
u_st.time = t;
u_st.obj = obj;
if (Mpi::Root())
{
mfem::out<<"t: "<<u_st.time<<" dt="<<u_st.dt<<" obj:"<<obj<<"\n";
}
};
State u;
u.v.SetSize(lin_elasticity_op.GetState().Size());
u.obj = 0.0;
u.time = 0.0;
u.dt = 0.0;
// set initial state to 0
u.v = 0.0;
dbg("Forward sweep (unknown number of steps)");
real_t t = 0.0;
Step i = 0;
while (t<Tfinal)
{
ckpt.ForwardStep(i, u, primal_step, make_snapshot);
t = u.time;
++i;
}
}
dbg("test objective gradients");
{
Vector state; state.SetSize(lin_elasticity_op.GetState().Size());
state.Randomize();
Vector dx(state); dx.Randomize();
Vector tmp(state);
Vector grd(state);
const real_t ro = obj->EvalScalar(state);
obj->EvalGradient(state,grd);
const real_t dp = InnerProduct(MPI_COMM_WORLD, grd, dx);
// const real_t np=InnerProduct(MPI_COMM_WORLD,dx,dx);
real_t sca = 10.0;
for (int i=0; i<10; i++)
{
sca = sca / 10.0;
tmp.Set(sca, dx);
tmp.Add(1.0, state);
const real_t rc = obj->EvalScalar(tmp);
if (Mpi::Root())
{
std::cout<<" obj="<<ro<<" true drv="<<dp<<" fd drv="<<(rc-ro)/(sca)<<std::endl;
}
}
}
return EXIT_SUCCESS;
}
-508
View File
@@ -1,508 +0,0 @@
#include "linear_elasticity.hpp"
// #include "../chpt/dynamic_checkpointing.hpp"
// #include "../chpt/fixed_slot_checkpoint_storage.hpp"
#include <cmath>
#include <cstring>
using namespace mfem;
using namespace std;
#ifdef NVTX_DEBUG_HPP
#undef NVTX_COLOR
#define NVTX_COLOR ::nvtx::kCyan
#include NVTX_DEBUG_HPP
#else
#define dbg(...)
#endif
constexpr auto MESH_TRI = MFEM_SOURCE_DIR
"/miniapps/mtop/examples/dyn_hex2d_tri.msh";
constexpr auto MESH_QUAD = MFEM_SOURCE_DIR
"/miniapps/mtop/examples/dyn_hex2d_quad.msh";
struct State
{
mfem::real_t time = 0.0;
mfem::real_t obj = 0.0;
mfem::Vector v;
};
// Snapshot = *view* (non-owning) used only during Store() packing and Read() callback
struct StateSnapshotView
{
mfem::real_t time = 0.0;
mfem::real_t obj = 0.0;
// Points to n*sizeof(real_t) bytes:
// - during Store(): points to current State::v data (host)
// - during Read(): points into the storage slot bytes (valid only during callback)
const unsigned char *v_bytes = nullptr;
};
class StateSnapshotViewPacker
{
public:
explicit StateSnapshotViewPacker(int n) : n_(n)
{
MFEM_VERIFY(n_ > 0, "StateSnapshotViewPacker: n must be > 0.");
}
std::size_t SlotBytes() const
{
return (std::size_t)(2 + n_) * sizeof(mfem::real_t);
}
void Pack(const StateSnapshotView &s, void *dst) const
{
MFEM_VERIFY(dst != nullptr, "Pack: dst is null.");
MFEM_VERIFY(s.v_bytes != nullptr, "Pack: snapshot v_bytes is null.");
unsigned char *b = static_cast<unsigned char*>(dst);
std::memcpy(b, &s.time, sizeof(mfem::real_t));
std::memcpy(b + sizeof(mfem::real_t), &s.obj, sizeof(mfem::real_t));
std::memcpy(b + 2*sizeof(mfem::real_t),
s.v_bytes,
(std::size_t)n_ * sizeof(mfem::real_t));
}
// Important: Unpack returns a *view* pointing into src bytes (no allocation).
void Unpack(const void *src, StateSnapshotView &out) const
{
MFEM_VERIFY(src != nullptr, "Unpack: src is null.");
const unsigned char *b = static_cast<const unsigned char*>(src);
std::memcpy(&out.time, b, sizeof(mfem::real_t));
std::memcpy(&out.obj, b + sizeof(mfem::real_t), sizeof(mfem::real_t));
out.v_bytes = b + 2*sizeof(mfem::real_t);
}
int Size() const { return n_; }
private:
int n_ = 0;
};
struct AdjState
{
mfem::real_t time;
mfem::real_t obj;
mfem::Vector adj;
mfem::Vector grd;
};
///////////////////////////////////////////////////////////////////////////////
/// \brief The IsoElasticyLambdaCoeff class converts E modulus of elasticity
/// and Poisson's ratio to Lame's lambda coefficient
class IsoElasticyLambdaCoeff : public mfem::Coefficient
{
mfem::Coefficient *E, *nu;
public:
/// Constructor - takes as inputs E modulus and Poisson's ratio
IsoElasticyLambdaCoeff(mfem::Coefficient *E,
mfem::Coefficient *nu):
E(E), nu(nu) { }
/// Evaluates the Lame's lambda coefficient
real_t Eval(mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip) override
{
const real_t EE = E->Eval(T, ip);
const real_t nn = nu->Eval(T, ip);
constexpr auto Lambda = [](const real_t E, const real_t nu)
{
return E * nu / (1.0 + nu) / (1.0 - 2.0 * nu);
};
return Lambda(EE, nn);
}
};
///////////////////////////////////////////////////////////////////////////////
/// \brief The IsoElasticySchearCoeff class converts E modulus of elasticity
/// and Poisson's ratio to Shear coefficient
///
class IsoElasticySchearCoeff : public mfem::Coefficient
{
mfem::Coefficient *E, *nu;
public:
/// Constructor - takes as inputs E modulus and Poisson's ratio
IsoElasticySchearCoeff(mfem::Coefficient *E_, mfem::Coefficient *nu_):
E(E_), nu(nu_) { }
/// Evaluates the shear coefficient coefficient
real_t Eval(mfem::ElementTransformation &T,
const mfem::IntegrationPoint &ip) override
{
const real_t EE = E->Eval(T, ip);
const real_t nn = nu->Eval(T, ip);
constexpr auto Schear = [](const real_t E, const real_t nu)
{
return E / (2.0 * (1.0 + nu));
};
return Schear(EE, nn);
}
};
int main(int argc, char *argv[])
{
dbg();
// Initialize MPI and HYPRE.
Mpi::Init();
Hypre::Init();
// Parse command-line options.
const char *mesh_file = MESH_QUAD;
const char *device_config = "cpu";
int order = 3;
bool mesh_tri = false;
bool mesh_quad = false;
int par_ref_levels = 1;
int max_steps = 1000;
bool paraview = false;
bool visualization = true;
int ode_solver_type = 4;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&mesh_tri, "-tri", "--triangular", "-no-tri",
"--no-triangular", "Enable or not triangular mesh.");
args.AddOption(&mesh_quad, "-quad", "--quadrilateral", "-no-quad",
"--no-quadrilateral", "Enable or not quadrilateral mesh.");
args.AddOption(&par_ref_levels, "-prl", "--par-ref-levels",
"Number of parallel mesh refinement levels.");
args.AddOption(&max_steps, "-ms", "--max-steps",
"Maximum number of time steps.");
args.AddOption(&paraview, "-pa", "--paraview", "-no-pa",
"--no-paraview", "Enable or not Paraview output.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization", "Enable or not visualization.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
ODESolver::Types.c_str());
args.ParseCheck();
dbg("mesh_file: {}", mesh_file);
// Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (Mpi::Root()) { device.Print(); }
// Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh mesh(mesh_tri ? MESH_TRI : mesh_quad ? MESH_QUAD : mesh_file, 1, 1);
const int dim = mesh.Dimension();
// Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 1000 elements.
{
const int ref_levels =
(int)floor(log(1000. / mesh.GetNE()) / log(2.) / dim);
for (int l = 0; l < ref_levels; l++) { mesh.UniformRefinement(); }
}
if (Mpi::Root())
{
std::cout << "Number of elements: " << mesh.GetNE() << std::endl;
dbg("Number of elements: {}", mesh.GetNE());
}
// Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
for (int l = 0; l < par_ref_levels; l++) { pmesh.UniformRefinement(); }
// Allocate the time dependent linear elasticity operator
LinearElasticityTimeDependentOperator lin_elasticity_op(pmesh, order);
// Set the material coefficients
ConstantCoefficient rho_coef(
0.5); // density coefficient for topology optimization
// Set elasticity coefficients for material 1 and 2
ConstantCoefficient E1(0.1);
ConstantCoefficient E2(1.0);
ConstantCoefficient nu1(0.3);
ConstantCoefficient nu2(0.3);
//Lame coefficients
IsoElasticyLambdaCoeff lambda1(&E1, &nu1);
IsoElasticySchearCoeff mu1(&E1, &nu1);
IsoElasticyLambdaCoeff lambda2(&E2, &nu2);
IsoElasticySchearCoeff mu2(&E2, &nu2);
// Set density coefficients for material 1 and 2
ConstantCoefficient dens1_coef(0.5);
ConstantCoefficient dens2_coef(1.0);
// Set damping coefficients
ProductCoefficient cm1_coef(0.02, dens1_coef);
ProductCoefficient cm2_coef(0.02, dens2_coef);
ProductCoefficient cl1_coef(0.01, lambda1);
ProductCoefficient cmu1_coef(0.01, mu1);
ProductCoefficient cl2_coef(0.01, lambda2);
ProductCoefficient cmu2_coef(0.01, mu2);
lin_elasticity_op.SetElasticityCoefficients(lambda1, mu1, lambda2, mu2);
lin_elasticity_op.SetDensityMaterialCoefficients(dens1_coef, dens2_coef);
lin_elasticity_op.SetDampingMaterialCoefficients(cm1_coef, cm2_coef);
lin_elasticity_op.SetDampingMaterialCoefficients(cl1_coef, cmu1_coef,
cl2_coef, cmu2_coef);
lin_elasticity_op.SetDensity(rho_coef);
//set bottom bdr to zero (both the velocities and the displacements)
lin_elasticity_op.SetZeroBdr(1);
lin_elasticity_op.SetVolForce(1.0 /*period*/, 1.0 /*amplitude*/, 0.2 /*radius*/,
0.0 /*x center*/, 0.0 /*y center*/, 0.0 /*z center*/,
5.0 /* train length*/, 2.5 /*center of the train*/, 2.0 /*power*/);
lin_elasticity_op.AssembleExplicit();
// test mult explicit
{
BlockVector tst; tst.Update(lin_elasticity_op.GetTrueBlockOffsets());
tst=0.0; //tst.Randomize();
tst.UseDevice(true); tst.Read();
BlockVector grd; grd.Update(lin_elasticity_op.GetTrueBlockOffsets());
grd=0.0;
lin_elasticity_op.Mult(tst,grd);
lin_elasticity_op.GetVelocity().SetFromTrueDofs(grd.GetBlock(1));
}
ParaViewDataCollection paraview_dc("isoel", &pmesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(order);
paraview_dc.SetDataFormat(VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.RegisterField("disp", &(lin_elasticity_op.GetDisplacement()));
paraview_dc.RegisterField("velo", &(lin_elasticity_op.GetVelocity()));
/*
{
int s=10;
StateSnapshotViewPacker packer(lin_elasticity_op.GetState().Size());
// storage stores StateSnapshotView snapshots using fixed-size slots
using Storage=mfem::FixedSlotMemoryCheckpointStorage<StateSnapshotView, StateSnapshotViewPacker>;
Storage storage(s, packer);
auto make_snapshot = [&](const State &u) -> StateSnapshotView
{
MFEM_VERIFY(u.v.Size() == n, "make_snapshot: State.v size changed!");
// Ensure host access if MFEM device is in use:
const mfem::real_t *vh = u.v.HostRead();
StateSnapshotView snap;
snap.time = u.time;
snap.obj = u.obj;
snap.v_bytes = reinterpret_cast<const unsigned char*>(vh);
return snap;
};
auto restore_snapshot = [&](const StateSnapshotView &snap, State &u_out)
{
u_out.time = snap.time;
u_out.obj = snap.obj;
if (u_out.v.Size() != n) { u_out.v.SetSize(n); }
mfem::real_t *vh = u_out.v.HostWrite();
std::memcpy(vh,
snap.v_bytes,
(std::size_t)n * sizeof(mfem::real_t));
};
using Step = mfem::DynamicCheckpointing<StateSnapshotView, Storage>::Step;
State u;
u.v.SetSize(lin_elasticity_op.GetState().Size());
auto primal_step = [&](StateCheckPoint &u, Step i)
{
if (Mpi::Root()){
std::cout<<"Primal step: time= "<<u.time<<" obj= "<<u.obj;
}
const double dt = 0.01;
u.obj=dt*i;
u.time=dt*i;
u.v=(mfem::real_t)i;
if (Mpi::Root()){
std::cout<<" out Step: "<<i<<" time="<<u.time<<" obj="<<u.obj<<std::endl;
}
};
auto adjoint_step = [&](AdjState &lambda, const StateCheckPoint &u_i, Step i)
{
const double dt = 0.01;
MFEM_ASSERT(lambda.adj.Size() == u_i.state.Size(), "lambda and u_i size mismatch.");
if (Mpi::Root()){
std::cout<<"Adj step: time= "<<u_i.time<<" obj= "<<u_i.obj;
std::cout<<" adj time= "<<lambda.time<<" adj obj="<<lambda.obj<<std::endl;
}
lambda.obj=-u_i.obj;
lambda.time=lambda.time-dt;
};
// Initial condition
StateCheckPoint spt; spt.obj=-1.0; spt.time=-1.0; spt.state=(lin_elasticity_op.GetState());
Step i=0;
mfem::real_t t=0.0;
mfem::real_t dt=0.01;
while(t<0.2)
{
ckpt.ForwardStep(i, spt, primal_step, make_snapshot);
t=t+dt;
++i;
}
const Step m = i;
if (Mpi::Root()){
std::cout<<" Total number of steps="<<m<<std::endl;
}
AdjState ast; ast.obj=1.0; ast.time=spt.time;
ast.adj=(lin_elasticity_op.GetState());
ast.grd=(lin_elasticity_op.GetState());
for (Step j = m - 1; j >= 0; --j)
{
if (Mpi::Root()){
std::cout<<" Outer steps="<<j<<std::endl;
}
ckpt.BackwardStep(j, ast, spt, primal_step, adjoint_step, make_snapshot, restore_snapshot);
if (j == 0) { break; }
}
}
*/
//test time integration
{
real_t t = 0.0;
BlockVector tsol; tsol.Update(lin_elasticity_op.GetTrueBlockOffsets());
//set initial conditions at time t
tsol=0.0;
// 4. Define the ODE solver used for time integration. Several explicit
// Runge-Kutta methods are available.
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
lin_elasticity_op.SetTime(t);
ode_solver->Init(lin_elasticity_op);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
lin_elasticity_op.GetVelocity().SetFromTrueDofs(tsol.GetBlock(1));
lin_elasticity_op.GetDisplacement().SetFromTrueDofs(tsol.GetBlock(0));
paraview_dc.Save();
real_t dt_real = 0.005;
//ode_solver->Run(tsol, t, dt_real, 1.0);
for (int i=0; i< std::min(max_steps, 6000); i++)
{
dbg("#{}", i);
ode_solver->Step(tsol, t, dt_real);
if (Mpi::Root())
{
std::cout << "t: " << t << std::endl;
}
if (paraview && (i%5)==0)
{
dbg("Paraview output at step {} time {}", i, t);
paraview_dc.SetCycle(i+1);
paraview_dc.SetTime(t);
lin_elasticity_op.GetVelocity().SetFromTrueDofs(tsol.GetBlock(1));
lin_elasticity_op.GetDisplacement().SetFromTrueDofs(tsol.GetBlock(0));
paraview_dc.Save();
}
if (socketstream glvis; visualization && (i%10)==0 &&
((glvis.open("localhost", 19916)),
(glvis.is_open() ? glvis.precision(8) : 0),
(glvis.is_open() ? (glvis << "mgjR" << std::endl, 0) : 0),
(glvis.is_open())))
{
dbg("GLVis output at step {} time {}", i, t);
glvis << "parallel " << Mpi::WorldSize() << " " << Mpi::WorldRank() << "\n";
lin_elasticity_op.GetVelocity().SetFromTrueDofs(tsol.GetBlock(1));
ParGridFunction velocity(lin_elasticity_op.GetVelocity());
// lin_elasticity_op.GetDisplacement().SetFromTrueDofs(tsol.GetBlock(0));
// ParGridFunction displacement(lin_elasticity_op.GetDisplacement());
glvis << "solution\n" << pmesh << velocity << std::flush;
glvis << "keys g\n" << std::flush; // pause
}
}
}
/*
if (paraview)
{
ParaViewDataCollection paraview_dc("isoel", &pmesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(order);
paraview_dc.SetDataFormat(VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
//paraview_dc.RegisterField("disp", &sol);
paraview_dc.RegisterField("disp", &(lin_elasticity_op.GetDisplacement()));
paraview_dc.RegisterField("velo", &(lin_elasticity_op.GetVelocity()));
paraview_dc.Save();
}
*/
return EXIT_SUCCESS;
}
@@ -9,24 +9,17 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
if(MFEM_USE_MPI)
if (MFEM_USE_MPI)
list(APPEND PLASMA_COMMON_SOURCES)
list(APPEND AN_SOURCES linear_anisotropic_elasticity.hpp)
list(APPEND AN_HEADERS linear_anisotropic_elasticity.cpp)
list(APPEND PLASMA_COMMON_HEADERS
plasma.hpp)
convert_filenames_to_full_paths(AN_SOURCES)
convert_filenames_to_full_paths(AN_HEADERS)
set(EX_COMMON_FILES
EXTRA_SOURCES ${AN_SOURCES}
EXTRA_HEADERS ${AN_HEADERS})
add_mfem_miniapp(mtop_test_anisotropic_le
MAIN test_anisotropic_le.cpp
${EX_COMMON_FILES}
LIBRARIES mfem)
endif (MFEM_USE_MPI)
convert_filenames_to_full_paths(PLASMA_COMMON_SOURCES)
convert_filenames_to_full_paths(PLASMA_COMMON_HEADERS)
set(PLASMA_COMMON_FILES
EXTRA_SOURCES ${PLASMA_COMMON_SOURCES}
EXTRA_HEADERS ${PLASMA_COMMON_HEADERS})
endif()
+85
View File
@@ -0,0 +1,85 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/plasma/,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
# Use the MFEM install directory
# MFEM_INSTALL_DIR = ../../mfem
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_MINIAPPS =
PAR_MINIAPPS =
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
else
MINIAPPS = $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all lib-common clean clean-build clean-exec
.PRECIOUS: %.o
COMMON_LIB = -L$(MFEM_BUILD_DIR)/miniapps/common -lmfem-common
# If MFEM_SHARED is set, add the ../common rpath
COMMON_LIB += $(if $(MFEM_SHARED:YES=),,\
$(if $(MFEM_USE_CUDA:YES=),$(CXX_XLINKER),$(CUDA_XLINKER))-rpath,$(abspath\
$(MFEM_BUILD_DIR)/miniapps/common))
COMMON_O=
# Remove built-in rules
%: %.cpp
%.o: %.cpp
all: $(MINIAPPS)
# Rules for building the miniapps
%: $(SRC)%.cpp $(COMMON_O) $(MFEM_LIB_FILE) $(CONFIG_MK) | lib-common
$(MFEM_CXX) $(MFEM_LINK_FLAGS) $< -o $@ $(COMMON_O) $(COMMON_LIB) \
$(MFEM_LIBS)
# Rules for compiling miniapp dependencies
$(COMMON_O) $(addsuffix _solver.o,$(MINIAPPS)): \
%.o: $(SRC)%.cpp $(SRC)%.hpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $(<) -o $(@)
# Rule for building lib-common
lib-common:
$(MAKE) -C $(MFEM_BUILD_DIR)/miniapps/common
MFEM_TESTS = MINIAPPS
include $(MFEM_TEST_MK)
# Testing: Specific execution options
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_MINIAPPS) $(PAR_MINIAPPS)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
+62
View File
@@ -0,0 +1,62 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_PLASMA_HPP
#define MFEM_PLASMA_HPP
#include <cmath>
#include <complex>
namespace mfem
{
namespace plasma
{
// Physical Constants
// Permittivity of Free Space (units F/m)
static const real_t epsilon0_ = 8.8541878176e-12;
// Permeability of Free Space (units H/m)
static const real_t mu0_ = 4.0e-7 * M_PI;
// Speed of light in Free Space (units m/s)
static const real_t c0_ = 1.0 / sqrt(epsilon0_ * mu0_);
// Impedance of Free Space (units Ohm)
static const real_t Z0_ = sqrt(mu0_ / epsilon0_);
static const real_t q_ = 1.602176634e-19; // Elementary charge in coulombs
static const real_t eV_ = 1.602176634e-19; // 1 eV in Joules
static const real_t amu_ = 1.660539040e-27; // Atomic mass unit in kilograms
static const real_t me_kg_ = 9.10938356e-31; // Mass of electron in kilograms
static const real_t me_u_ = 5.4857990907e-4; // Mass of electron in a.m.u
/**
Returns the cyclotron frequency in radians/second
m is the mass in a.m.u
q is the charge in units of elementary electric charge
B is the magnetic field magnitude in tesla
*/
inline real_t cyclotronFrequency(real_t B, real_t m, real_t q)
{
return fabs(q * q_ * B / (m * amu_));
}
typedef std::complex<real_t> complex_t;
} // namespace plasma
} // namespace mfem
#endif // MFEM_PLASMA_HPP
-2
View File
@@ -39,8 +39,6 @@ set(UNIT_TESTS_SRCS
dfem/test_divergence.cpp
dfem/test_lvector_interface.cpp
dfem/test_mass.cpp
dfem/test_transpose.cpp
dfem/test_multiple_integrators.cpp
general/test_array.cpp
general/test_scan.cpp
general/test_arrays_by_name.cpp
+5
View File
@@ -295,8 +295,13 @@ namespace Catch {
// Otherwise all supported compilers support COUNTER macro,
// but user still might want to turn it off
#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
#if ( !(defined(__clang__) && __clang_major__ >= 22 ) )
// don't use __COUNTER__ if compiling with clang 22+ to avoid compiler warning
// https://github.com/llvm/llvm-project/pull/162662
// TODO: can enable if building with C2y
#define CATCH_INTERNAL_CONFIG_COUNTER
#endif
#endif
////////////////////////////////////////////////////////////////////////////////
+1 -1
View File
@@ -218,7 +218,7 @@ template <int DIM> void mass_mat_mixed(const char* filename, int p)
SECTION("spmat")
{
SparseMatrix *A = nullptr;
SparseMatrix *A;
ddopdu->Assemble(A);
TestSameMatrices(*A, blf.SpMat());
delete A;
@@ -1,202 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../unit_tests.hpp"
#include "../linalg/test_same_matrices.hpp"
#include "mfem.hpp"
#include "fem/dfem/doperator.hpp"
#ifdef MFEM_USE_MPI
using namespace mfem;
using namespace mfem::future;
using mfem::future::tensor;
#ifdef MFEM_USE_ENZYME
using dscalar_t = real_t;
#else
using mfem::future::dual;
using dscalar_t = dual<real_t, real_t>;
#endif
template <int DIM>
void mult_integ(const char *filename, int p)
{
CAPTURE(filename, DIM, p);
Mesh smesh(filename);
ParMesh pmesh(MPI_COMM_WORLD, smesh);
pmesh.EnsureNodes();
auto* nodes = static_cast<ParGridFunction*>(pmesh.GetNodes());
p = std::max(p, pmesh.GetNodalFESpace()->GetMaxElementOrder());
smesh.Clear();
H1_FECollection fec(p, DIM);
ParFiniteElementSpace fes(&pmesh, &fec);
ParGridFunction x(&fes), y(&fes), z(&fes);
Vector X(fes.GetTrueVSize()), Y(fes.GetTrueVSize()), Z(fes.GetTrueVSize());
X.Randomize(1);
x.SetFromTrueDofs(X);
const auto *ir = &IntRules.Get(pmesh.GetTypicalElementGeometry(), 2 * p);
Array<int> all_domain_attr;
if (pmesh.attributes.Size() > 0)
{
all_domain_attr.SetSize(pmesh.attributes.Max());
all_domain_attr = 1;
}
ParBilinearForm blf(&fes);
blf.AddDomainIntegrator(new DiffusionIntegrator(ir));
blf.AddDomainIntegrator(new MassIntegrator(ir));
blf.SetAssemblyLevel(AssemblyLevel::PARTIAL);
blf.Assemble();
blf.Mult(x, y);
fes.GetProlongationMatrix()->MultTranspose(y, Y);
const auto mass_qf =
[] MFEM_HOST_DEVICE(
const dscalar_t &u,
const tensor<real_t, DIM, DIM> &J,
const real_t &w)
{
return tuple{u * w * det(J)};
};
const auto diffusion_qf =
[] MFEM_HOST_DEVICE(
const tensor<dscalar_t, DIM> &dudxi,
const tensor<real_t, DIM, DIM> &J,
const real_t &w)
{
return tuple{(dudxi * inv(J)) * transpose(inv(J)) * w * det(J)};
};
static constexpr int U = 0, Coords = 1;
const auto sol = std::vector{ FieldDescriptor{ U, &fes } };
DifferentiableOperator dop(sol, {{Coords, nodes->ParFESpace()}}, pmesh);
auto derivatives = std::integer_sequence<size_t, U> {};
dop.AddDomainIntegrator(diffusion_qf,
tuple{ Gradient<U>{}, Gradient<Coords>{}, Weight{} },
tuple{ Gradient<U>{} },
*ir, all_domain_attr, derivatives);
dop.AddDomainIntegrator(mass_qf,
tuple{ Value<U>{}, Gradient<Coords>{}, Weight{} },
tuple{ Value<U>{} },
*ir, all_domain_attr, derivatives);
SECTION("action")
{
dop.SetParameters({ nodes });
fes.GetRestrictionMatrix()->Mult(x, X);
dop.Mult(X, Z);
Y -= Z;
real_t norm_g, norm_l = Y.Normlinf();
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, pmesh.GetComm());
REQUIRE(norm_g == MFEM_Approx(0.0));
MPI_Barrier(MPI_COMM_WORLD);
}
SECTION("linearized action")
{
auto ddopdu = dop.GetDerivative(U, {&x}, {nodes});
fes.GetRestrictionMatrix()->Mult(x, X);
ddopdu->Mult(X, Z);
Y -= Z;
real_t norm_g, norm_l = Y.Normlinf();
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, pmesh.GetComm());
REQUIRE(norm_g == MFEM_Approx(0.0));
MPI_Barrier(MPI_COMM_WORLD);
}
SECTION("linearized assembled SparseMatrix")
{
auto ddopdu = dop.GetDerivative(U, {&x}, {nodes});
SparseMatrix *A = nullptr;
ddopdu->Assemble(A);
A->Mult(x, z);
fes.GetProlongationMatrix()->MultTranspose(z, Z);
Y -= Z;
real_t norm_g, norm_l = Y.Normlinf();
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, pmesh.GetComm());
REQUIRE(norm_g == MFEM_Approx(0.0));
MPI_Barrier(MPI_COMM_WORLD);
delete A;
}
SECTION("linearized assembled HypreParMatrix")
{
auto ddopdu = dop.GetDerivative(U, {&x}, {nodes});
HypreParMatrix *A = nullptr;
ddopdu->Assemble(A);
fes.GetRestrictionMatrix()->Mult(x, X);
A->Mult(X, Z);
Y -= Z;
real_t norm_g, norm_l = Y.Normlinf();
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, pmesh.GetComm());
REQUIRE(norm_g == MFEM_Approx(0.0));
MPI_Barrier(MPI_COMM_WORLD);
delete A;
}
}
// no GPU tag to avoid failing 'hypre parallel mat' section
TEST_CASE("dFEM Multiple Integrators", "[Parallel][dFEM][XXX]")
{
const bool all_tests = launch_all_non_regression_tests;
const auto p = !all_tests ? 2 : GENERATE(1, 2, 3);
SECTION("2d")
{
const auto filename2d =
GENERATE(
"../../data/star.mesh",
"../../data/star-q3.mesh",
"../../data/rt-2d-q3.mesh",
"../../data/inline-quad.mesh",
"../../data/periodic-square.mesh"
);
mult_integ<2>(filename2d, p);
}
SECTION("3d")
{
const auto filename3d =
GENERATE(
"../../data/fichera.mesh",
"../../data/fichera-q3.mesh",
"../../data/inline-hex.mesh",
"../../data/toroid-hex.mesh",
"../../data/periodic-cube.mesh"
);
mult_integ<3>(filename3d, p);
}
}
#endif // MFEM_USE_MPI
-409
View File
@@ -1,409 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../unit_tests.hpp"
#include "mfem.hpp"
#include "fem/dfem/doperator.hpp" // TODO: remove before merge
#ifdef MFEM_USE_MPI
using namespace mfem;
using namespace mfem::future;
using mfem::future::tensor;
#ifdef MFEM_USE_ENZYME
using dscalar_t = real_t;
#else
using mfem::future::dual;
using dscalar_t = dual<real_t, real_t>;
#endif
template <int DIM>
void transpose(const char *filename, int p)
{
CAPTURE(filename, DIM, p);
Mesh serial_mesh(filename);
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
serial_mesh.Clear();
mesh.EnsureNodes();
auto* nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
p = std::max(p, mesh.GetNodalFESpace()->GetMaxElementOrder());
Array<int> all_domain_attr;
if (mesh.attributes.Size() > 0)
{
all_domain_attr.SetSize(mesh.attributes.Max());
all_domain_attr = 1;
}
H1_FECollection fec(p, DIM);
ParFiniteElementSpace scalar_fes(&mesh, &fec);
ParFiniteElementSpace vector_fes(&mesh, &fec, DIM);
ParGridFunction sgf(&scalar_fes);
auto f0 = [](const Vector &x)
{
if constexpr (DIM == 3)
{
return M_PI*cos(M_PI*x[0]) * sin(M_PI*x[1]) * sin(M_PI*x[2]);
}
return M_PI*cos(M_PI*x[0]) * sin(M_PI*x[1]);
};
FunctionCoefficient f0_coeff(f0);
sgf.ProjectCoefficient(f0_coeff);
ParGridFunction vgf(&vector_fes);
auto gradf1 = [](const Vector &x, Vector &u)
{
if constexpr (DIM == 3)
{
u(0) = M_PI*cos(M_PI*x[0]) * sin(M_PI*x[1]) * sin(M_PI*x[2]);
u(1) = M_PI*sin(M_PI*x[0]) * cos(M_PI*x[1]) * sin(M_PI*x[2]);
u(2) = M_PI*sin(M_PI*x[0]) * sin(M_PI*x[1]) * cos(M_PI*x[2]);
return;
}
u(0) = M_PI*cos(M_PI*x[0]) * sin(M_PI*x[1]);
u(1) = M_PI*sin(M_PI*x[0]) * cos(M_PI*x[1]);
};
VectorFunctionCoefficient gradf1_coeff(DIM, gradf1);
vgf.ProjectCoefficient(gradf1_coeff);
const auto* ir = &IntRules.Get(mesh.GetTypicalElementGeometry(), 2 * p);
SECTION("Mass Transpose Action")
{
ParBilinearForm Mblf(&scalar_fes);
auto mass_integ = new MassIntegrator;
mass_integ->SetIntegrationRule(*ir);
Mblf.AddDomainIntegrator(mass_integ);
Mblf.Assemble();
Mblf.Finalize();
auto Mmat = Mblf.ParallelAssemble();
static constexpr int SCALAR = 0, COORDINATES = 1;
const auto sol = std::vector{FieldDescriptor{SCALAR, &scalar_fes}};
const auto par = std::vector{FieldDescriptor{COORDINATES, nodes->ParFESpace()}};
DifferentiableOperator dop(sol, par, mesh);
const auto gradient_qf = [] MFEM_HOST_DEVICE(
const dscalar_t &u,
const tensor<real_t, DIM, DIM> &J,
const real_t &w)
{
return tuple{u * w * det(J)};
};
auto derivatives = std::integer_sequence<size_t, SCALAR> {};
dop.AddDomainIntegrator(gradient_qf,
tuple{Value<SCALAR>{}, Gradient<COORDINATES>{}, Weight{}},
tuple{Value<SCALAR>{}},
*ir, all_domain_attr, derivatives);
dop.SetParameters({nodes});
Vector S, T, U;
S.SetSize(scalar_fes.GetTrueVSize());
T.SetSize(scalar_fes.GetTrueVSize());
U.SetSize(scalar_fes.GetTrueVSize());
sgf.GetTrueDofs(S);
Mmat->MultTranspose(S, T);
auto ddop = dop.GetDerivative(SCALAR, {&sgf}, {nodes});
ddop->MultTranspose(S, U);
T -= U;
real_t norm_g, norm_l = T.Normlinf();
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, mesh.GetComm());
REQUIRE(norm_g == MFEM_Approx(0.0));
delete Mmat;
}
SECTION("Vector Mass Transpose Action")
{
ParBilinearForm Mvblf(&vector_fes);
auto mass_integ = new VectorMassIntegrator;
mass_integ->SetIntegrationRule(*ir);
Mvblf.AddDomainIntegrator(mass_integ);
Mvblf.Assemble();
Mvblf.Finalize();
auto Mvmat = Mvblf.ParallelAssemble();
static constexpr int VECTOR = 0, COORDINATES = 1;
const auto sol = std::vector{FieldDescriptor{VECTOR, &vector_fes}};
const auto par = std::vector{FieldDescriptor{COORDINATES, nodes->ParFESpace()}};
DifferentiableOperator dop(sol, par, mesh);
const auto gradient_qf = [] MFEM_HOST_DEVICE(
const tensor<dscalar_t, DIM> &u,
const tensor<real_t, DIM, DIM> &J,
const real_t &w)
{
return tuple{u * w * det(J)};
};
auto derivatives = std::integer_sequence<size_t, VECTOR> {};
dop.AddDomainIntegrator(gradient_qf,
tuple{Value<VECTOR>{}, Gradient<COORDINATES>{}, Weight{}},
tuple{Value<VECTOR>{}},
*ir, all_domain_attr, derivatives);
dop.SetParameters({nodes});
Vector V, W, Z;
V.SetSize(vector_fes.GetTrueVSize());
W.SetSize(vector_fes.GetTrueVSize());
Z.SetSize(vector_fes.GetTrueVSize());
vgf.GetTrueDofs(V);
Mvmat->MultTranspose(V, W);
auto ddop = dop.GetDerivative(VECTOR, {&vgf}, {nodes});
ddop->MultTranspose(V, Z);
W -= Z;
real_t norm_g, norm_l = W.Normlinf();
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, mesh.GetComm());
REQUIRE(norm_g == MFEM_Approx(0.0));
delete Mvmat;
}
SECTION("Discrete Gradient Transpose Action")
{
ParMixedBilinearForm Gblf(&scalar_fes, &vector_fes);
auto grad_integ = new GradientIntegrator;
grad_integ->SetIntegrationRule(*ir);
Gblf.AddDomainIntegrator(grad_integ);
Gblf.Assemble();
Gblf.Finalize();
auto Gmat = Gblf.ParallelAssemble();
static constexpr int SCALAR = 0, VECTOR = 2, COORDINATES = 1;
const auto sol = std::vector{FieldDescriptor{SCALAR, &scalar_fes}};
const auto par = std::vector
{
FieldDescriptor{VECTOR, &vector_fes},
FieldDescriptor{COORDINATES, nodes->ParFESpace()}
};
DifferentiableOperator dop(sol, par, mesh);
const auto gradient_qf = [] MFEM_HOST_DEVICE(
const tensor<dscalar_t, DIM> &dudxi,
const tensor<real_t, DIM, DIM> &J,
const real_t &w)
{
const auto dudx = dudxi * inv(J);
return tuple{dudx * w * det(J)};
};
auto derivatives = std::integer_sequence<size_t, SCALAR> {};
dop.AddDomainIntegrator(gradient_qf,
tuple{Gradient<SCALAR>{}, Gradient<COORDINATES>{}, Weight{}},
tuple{Value<VECTOR>{}},
*ir, all_domain_attr, derivatives);
dop.SetParameters({&vgf, nodes});
Vector S, T, V;
S.SetSize(scalar_fes.GetTrueVSize());
T.SetSize(scalar_fes.GetTrueVSize());
vgf.GetTrueDofs(V);
Gmat->MultTranspose(V, S);
auto ddop = dop.GetDerivative(SCALAR, {&sgf}, {&vgf, nodes});
ddop->MultTranspose(V, T);
S -= T;
real_t norm_g, norm_l = S.Normlinf();
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, mesh.GetComm());
REQUIRE(norm_g == MFEM_Approx(0.0));
delete Gmat;
}
SECTION("Scalar Convection Transpose Action")
{
auto b_func = [](const Vector &x, Vector &b)
{
b(0) = cos(x[0] * 2.0 * M_PI);
b(1) = 1.0 + cos(x[1] * 2.0 * M_PI);
if constexpr (DIM == 3)
{
b(2) = 2.0 + cos(x[2] * 2.0 * M_PI);
}
};
VectorFunctionCoefficient b_coeff(DIM, b_func);
ParBilinearForm Gblf(&scalar_fes);
auto conv_integ = new ConvectionIntegrator(b_coeff);
conv_integ->SetIntegrationRule(*ir);
Gblf.AddDomainIntegrator(conv_integ);
Gblf.Assemble();
Gblf.Finalize();
auto Gmat = Gblf.ParallelAssemble();
static constexpr int SCALAR = 0, COORDINATES = 1;
const auto sol = std::vector{FieldDescriptor{SCALAR, &scalar_fes}};
const auto par = std::vector
{
FieldDescriptor{COORDINATES, nodes->ParFESpace()}
};
DifferentiableOperator dop(sol, par, mesh);
const auto convection_qf =
[] MFEM_HOST_DEVICE(
const tensor<dscalar_t, DIM> &dudxi,
const tensor<real_t, DIM> &x,
const tensor<real_t, DIM, DIM> &J,
const real_t &w)
{
const auto dudx = dudxi * inv(J);
tensor<dscalar_t, DIM> b{};
b(0) = cos(x[0] * 2.0 * M_PI);
b(1) = 1.0 + cos(x[1] * 2.0 * M_PI);
if constexpr (DIM == 3)
{
b(2) = 2.0 + cos(x[2] * 2.0 * M_PI);
}
return tuple{dot(b, dudx) * w * det(J)};
};
auto derivatives = std::integer_sequence<size_t, SCALAR> {};
dop.AddDomainIntegrator(convection_qf,
tuple{Gradient<SCALAR>{}, Value<COORDINATES>{}, Gradient<COORDINATES>{}, Weight{}},
tuple{Value<SCALAR>{}},
*ir, all_domain_attr, derivatives);
dop.SetParameters({nodes});
Vector S, T, U;
S.SetSize(scalar_fes.GetTrueVSize());
T.SetSize(scalar_fes.GetTrueVSize());
U.SetSize(scalar_fes.GetTrueVSize());
U.Randomize(1);
{
Gmat->MultTranspose(U, S);
auto ddop = dop.GetDerivative(SCALAR, {&sgf}, {nodes});
ddop->MultTranspose(U, T);
S -= T;
real_t norm_g, norm_l = S.Normlinf();
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, mesh.GetComm());
REQUIRE(norm_g == MFEM_Approx(0.0));
}
delete Gmat;
}
SECTION("Nonlinear VectorConvection Transpose Action")
{
auto b_func = [](const Vector &x, Vector &b)
{
b(0) = cos(x[0]) * sin(x[0]) * x[1];
b(1) = cos(x[1]) * sin(x[1]) * x[0];
if constexpr (DIM == 3)
{
b(2) = cos(x[2]) * sin(x[2]) * x[0];
}
};
VectorFunctionCoefficient b_coeff(DIM, b_func);
ParGridFunction ugf(&vector_fes);
ugf.ProjectCoefficient(b_coeff);
Vector U(vector_fes.GetTrueVSize());
ugf.GetTrueDofs(U);
ParNonlinearForm nlf(&vector_fes);
const auto vcinteg = new VectorConvectionNLFIntegrator();
vcinteg->SetIntegrationRule(*ir);
nlf.AddDomainIntegrator(vcinteg);
HypreParMatrix &Nmat = dynamic_cast<HypreParMatrix&>(nlf.GetGradient(U));
static constexpr int VELOCITY = 0, COORDINATES = 1;
const auto sol = std::vector{FieldDescriptor{VELOCITY, &vector_fes}};
const auto par = std::vector
{
FieldDescriptor{COORDINATES, nodes->ParFESpace()}
};
DifferentiableOperator dop(sol, par, mesh);
const auto nlconvection_qf =
[] MFEM_HOST_DEVICE(
const tensor<dscalar_t, DIM> &u,
const tensor<dscalar_t, DIM, DIM> &dudxi,
const tensor<real_t, DIM, DIM> &J,
const real_t &w)
{
const auto invJ = inv(J);
const auto dudx = dudxi * invJ;
return tuple{dot(dudx, u) * w * det(J)};
};
auto derivatives = std::integer_sequence<size_t, VELOCITY> {};
dop.AddDomainIntegrator(nlconvection_qf,
tuple{Value<VELOCITY>{}, Gradient<VELOCITY>{}, Gradient<COORDINATES>{}, Weight{}},
tuple{Value<VELOCITY>{}},
*ir, all_domain_attr, derivatives);
dop.SetParameters({nodes});
auto ddop = dop.GetDerivative(VELOCITY, {&ugf}, {nodes});
Vector S(U.Size()), T(U.Size()), Se(vector_fes.GetVSize());
Nmat.MultTranspose(U, S);
ddop->MultTranspose(U, T);
S -= T;
real_t norm_g, norm_l = S.Normlinf();
MPI_Allreduce(&norm_l, &norm_g, 1, MPI_DOUBLE, MPI_MAX, mesh.GetComm());
REQUIRE(norm_g == MFEM_Approx(0.0));
}
}
TEST_CASE("dFEM Transpose", "[Parallel][dFEM][XXX]")
{
const bool all_tests = launch_all_non_regression_tests;
const auto p = !all_tests ? 2 : GENERATE(1, 2, 3);
SECTION("2d")
{
const auto filename2d =
GENERATE(
"../../data/star.mesh",
"../../data/star-q3.mesh",
"../../data/rt-2d-q3.mesh",
"../../data/inline-quad.mesh"
);
transpose<2>(filename2d, p);
}
SECTION("3d")
{
const auto filename3d =
GENERATE(
"../../data/fichera.mesh",
"../../data/fichera-q3.mesh",
"../../data/inline-hex.mesh",
"../../data/toroid-hex.mesh"
);
transpose<3>(filename3d, p);
}
}
#endif
+49
View File
@@ -117,3 +117,52 @@ TEST_CASE("Vector FE Face Restriction", "[FaceRestriction]")
gf2 -= gf;
REQUIRE(gf2.Normlinf() == MFEM_Approx(0.0));
}
#ifdef MFEM_USE_MPI
TEST_CASE("L2 Face Restriction", "[FaceRestriction][Parallel]")
{
const int dim = GENERATE(2, 3);
constexpr int nx = 3;
constexpr int order = 2;
constexpr int vdim = 2;
const Ordering::Type ordering = GENERATE(Ordering::byNODES, Ordering::byVDIM);
Mesh serial_mesh = MakeCartesianMesh(nx, dim);
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
L2_FECollection fec(order, dim, BasisType::GaussLobatto);
ParFiniteElementSpace fes(&mesh, &fec, vdim, ordering);
auto *R = fes.GetFaceRestriction(ElementDofOrdering::LEXICOGRAPHIC,
FaceType::Interior);
Vector vals({1.0, 2.0});
VectorConstantCoefficient coeff(vals);
ParGridFunction gf(&fes);
gf.ProjectCoefficient(coeff);
Vector face_vec(R->Height());
R->Mult(gf, face_vec);
const int nf = mesh.GetNFbyType(FaceType::Interior);
const int face_dofs = fes.GetTypicalTraceElement()->GetDof();
auto h_face_vec = Reshape(face_vec.HostRead(), face_dofs, vdim, 2, nf);
for (int f = 0; f < nf; ++f)
{
for (int m = 0; m < 2; ++m)
{
for (int c = 0; c < vdim; ++c)
{
for (int i = 0; i < face_dofs; ++i)
{
REQUIRE(h_face_vec(i, c, m, f) == vals[c]);
}
}
}
}
}
#endif
+4 -2
View File
@@ -281,8 +281,10 @@ TEST_CASE("Nedelec Segment Finite Element",
REQUIRE( fe.GetRangeType() == (int) FiniteElement::VECTOR );
REQUIRE( fe.GetMapType() == (int) FiniteElement::H_CURL );
REQUIRE( fe.GetDerivType() == (int) FiniteElement::NONE );
REQUIRE( fe.GetDerivRangeType() == (int) FiniteElement::SCALAR );
REQUIRE( fe.GetDerivMapType() == (int) FiniteElement::INTEGRAL);
REQUIRE( fe.GetDerivRangeType() ==
(int) FiniteElement::UNKNOWN_RANGE_TYPE);
REQUIRE( fe.GetDerivMapType() ==
(int) FiniteElement::UNKNOWN_MAP_TYPE);
}
}
SECTION("Sizes for p = " + std::to_string(p))
+47 -7
View File
@@ -105,39 +105,39 @@ TEST_CASE("Integration rule order initialization", "[IntegrationRules]")
SECTION("Segment rule constructed by accessing square rule")
{
auto &quad5_ir = intrules.Get(Geometry::SQUARE, 5);
REQUIRE(quad5_ir.GetOrder() == 5);
REQUIRE(quad5_ir.GetOrder() >= 5);
// The segment integration rule of order 5 is lazy constructed when we get
// the square integration rule of order 5. Make sure its order was
// properly set:
auto &line5_ir = intrules.Get(Geometry::SEGMENT, 5);
REQUIRE(line5_ir.GetOrder() == 5);
REQUIRE(line5_ir.GetOrder() >= 5);
}
SECTION("Segment rule constructed by accessing cube rule")
{
auto &hex7_ir = intrules.Get(Geometry::CUBE, 7);
REQUIRE(hex7_ir.GetOrder() == 7);
REQUIRE(hex7_ir.GetOrder() >= 7);
// The segment integration rule of order 7 is lazy constructed when we get
// the cube integration rule of order 7. Make sure its order was properly
// set:
auto &line7_ir = intrules.Get(Geometry::SEGMENT, 7);
REQUIRE(line7_ir.GetOrder() == 7);
REQUIRE(line7_ir.GetOrder() >= 7);
}
SECTION("Segment and triangle rules constructed by accessing prism rule")
{
auto &prism3_ir = intrules.Get(Geometry::PRISM, 3);
REQUIRE(prism3_ir.GetOrder() == 3);
REQUIRE(prism3_ir.GetOrder() >= 3);
// The segment integration rule of order 3 is lazy constructed when we get
// the prism integration rule of order 3. Make sure its order was properly
// set:
auto &line3_ir = intrules.Get(Geometry::SEGMENT, 3);
REQUIRE(line3_ir.GetOrder() == 3);
REQUIRE(line3_ir.GetOrder() >= 3);
// The triangle integration rule of order 3 is lazy constructed when we
// get the prism integration rule of order 3. Make sure its order was
// properly set:
auto &tri3_ir = intrules.Get(Geometry::TRIANGLE, 3);
REQUIRE(tri3_ir.GetOrder() == 3);
REQUIRE(tri3_ir.GetOrder() >= 3);
}
}
@@ -271,3 +271,43 @@ TEST_CASE("Simplex integration rules", "[SimplexRules]")
}
}
}
// Monomial exactness is tested by [SimplexRules] above, which now uses
// positive-weight rules by default. The tests below verify properties
// specific to the positive-weight rules: weight positivity, stability,
// and interior point placement.
TEST_CASE("Simplex rule positivity", "[IntegrationRules]")
{
IntegrationRules rules;
SECTION("triangle rules have all positive weights for orders 0-25")
{
for (int order = 0; order <= 25; order++)
{
const IntegrationRule &ir = rules.Get(Geometry::TRIANGLE, order);
for (int i = 0; i < ir.GetNPoints(); i++)
{
INFO("order=" << order << ", point=" << i);
REQUIRE(ir.IntPoint(i).weight > 0.0);
}
}
}
SECTION("tet rules have all positive weights for orders 0-20")
{
for (int order = 0; order <= 20; order++)
{
const IntegrationRule &ir =
rules.Get(Geometry::TETRAHEDRON, order);
for (int i = 0; i < ir.GetNPoints(); i++)
{
INFO("order=" << order << ", point=" << i);
REQUIRE(ir.IntPoint(i).weight > 0.0);
}
}
}
}
+190 -4
View File
@@ -25,15 +25,201 @@ void Func_3D_lin(const Vector &x, Vector &v)
v[2] = -2.572 * x[0] + 1.321 * x[1] + 3.234 * x[2];
}
TEST_CASE("3D ProjectBdrCoefficientNormal Vector",
"[GridFunction]"
"[VectorGridFunctionCoefficient]")
{
const int n = 1;
const int dim = 3;
const int order = 1;
const double tol = 1e-6;
for (int type = (int)Element::TETRAHEDRON;
type <= (int)Element::HEXAHEDRON; type++)
{
Mesh mesh = Mesh::MakeCartesian3D(
n, n, n, (Element::Type)type, 2.0, 3.0, 5.0);
VectorFunctionCoefficient funcCoef(dim, Func_3D_lin);
SECTION("3D GetVectorValue tests for element type " +
std::to_string(type))
{
RT_FECollection rt_fec(order+1, dim);
FiniteElementSpace rt_fespace(&mesh, &rt_fec);
GridFunction rt_x( &rt_fespace);
VectorGridFunctionCoefficient rt_xCoef( &rt_x);
Array<int> bdr_marker(6);
Vector normal(dim);
Vector f_val(dim);
Vector rt_val(dim);
for (int b = 1; b<=6; b++)
{
bdr_marker = 0;
bdr_marker[b-1] = 1;
rt_x = 0.0;
rt_x.ProjectBdrCoefficientNormal(funcCoef, bdr_marker);
for (int be = 0; be < mesh.GetNBE(); be++)
{
Element *e = mesh.GetBdrElement(be);
if (e->GetAttribute() != b) { continue; }
ElementTransformation *T = mesh.GetBdrElementTransformation(be);
const FiniteElement *fe = rt_fespace.GetBE(be);
const IntegrationRule &ir = IntRules.Get(fe->GetGeomType(),
2*order + 2);
double rt_err = 0.0;
for (int j=0; j<ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
T->SetIntPoint(&ip);
CalcOrtho(T->Jacobian(), normal);
funcCoef.Eval(f_val, *T, ip);
rt_xCoef.Eval(rt_val, *T, ip);
rt_val -= f_val;
double rt_dist = rt_val * normal;
rt_err += rt_dist;
if (verbose_tests && rt_dist > tol)
{
mfem::out << be << ":" << j << " rt ("
<< f_val[0] << "," << f_val[1] << "," << f_val[2]
<< ") vs. ("
<< rt_val[0] << "," << rt_val[1] << ","
<< rt_val[2] << ") " << rt_dist << std::endl;
}
}
rt_err /= ir.GetNPoints();
REQUIRE( rt_err == MFEM_Approx(0.0));
}
}
}
}
}
TEST_CASE("3D ProjectBdrCoefficientNormal Scalar",
"[GridFunction]"
"[VectorGridFunctionCoefficient]")
{
const int n = 1;
const int dim = 3;
const int order = 1;
const double tol = 1e-6;
const char bdrs_axis[] = {2, 1, 0, 1, 0, 2};
const char bdrs_sign[] = {-1, -1, +1, +1, -1, +1};
for (int type = (int)Element::TETRAHEDRON;
type <= (int)Element::HEXAHEDRON; type++)
{
Mesh mesh = Mesh::MakeCartesian3D(
n, n, n, (Element::Type)type, 2.0, 3.0, 5.0);
VectorFunctionCoefficient funcCoef(dim, Func_3D_lin);
SECTION("3D GetVectorValue tests for element type " +
std::to_string(type))
{
RT_FECollection rt_fec(order+1, dim);
FiniteElementSpace rt_fespace(&mesh, &rt_fec);
GridFunction rt_x( &rt_fespace);
VectorGridFunctionCoefficient rt_xCoef( &rt_x);
Array<int> bdr_marker(6);
Vector normal(dim);
Vector f_val(dim);
Vector rt_val(dim);
for (int b = 1; b<=6; b++)
{
bdr_marker = 0;
bdr_marker[b-1] = 1;
rt_x = 0.0;
normal = 0.;
normal(bdrs_axis[b-1]) = (bdrs_sign[b-1] > 0)?(+1.):(-1.);
VectorConstantCoefficient normCoef(normal);
InnerProductCoefficient prodCoef(funcCoef, normCoef);
rt_x.ProjectBdrCoefficientNormal(prodCoef, bdr_marker);
for (int be = 0; be < mesh.GetNBE(); be++)
{
Element *e = mesh.GetBdrElement(be);
if (e->GetAttribute() != b) { continue; }
ElementTransformation *T = mesh.GetBdrElementTransformation(be);
const FiniteElement *fe = rt_fespace.GetBE(be);
const IntegrationRule &ir = IntRules.Get(fe->GetGeomType(),
2*order + 2);
double rt_err = 0.0;
for (int j=0; j<ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
T->SetIntPoint(&ip);
CalcOrtho(T->Jacobian(), normal);
funcCoef.Eval(f_val, *T, ip);
rt_xCoef.Eval(rt_val, *T, ip);
rt_val -= f_val;
double rt_dist = rt_val * normal;
rt_err += rt_dist;
if (verbose_tests && rt_dist > tol)
{
mfem::out << be << ":" << j << " rt ("
<< f_val[0] << "," << f_val[1] << "," << f_val[2]
<< ") vs. ("
<< rt_val[0] << "," << rt_val[1] << ","
<< rt_val[2] << ") " << rt_dist << std::endl;
}
}
rt_err /= ir.GetNPoints();
REQUIRE( rt_err == MFEM_Approx(0.0));
}
}
}
}
}
TEST_CASE("3D ProjectBdrCoefficientTangent",
"[GridFunction]"
"[VectorGridFunctionCoefficient]")
{
int n = 1;
int dim = 3;
int order = 1;
const int n = 1;
const int dim = 3;
const int order = 1;
double tol = 1e-6;
const double tol = 1e-6;
for (int type = (int)Element::TETRAHEDRON;
type <= (int)Element::HEXAHEDRON; type++)
@@ -200,3 +200,39 @@ TEST_CASE("ArraysByName Sort/Unique Methods", "[ArraysByName]")
}
}
}
TEST_CASE("ArraysByName Print/Load Methods", "[ArraysByName]")
{
ArraysByName<int> abn;
FillArraysByName(abn);
// Print object to string using default format
std::ostringstream oss1;
abn.Print(oss1);
// Load new object from printed output
ArraysByName<int> abn_load1;
std::istringstream iss1(oss1.str());
abn_load1.Load(iss1);
REQUIRE(abn == abn_load1);
// Print object to string using one line per array
std::ostringstream oss2;
oss2 << abn.Size() << '\n';
for (auto a : abn)
{
oss2 << '"' << a.first << "\" " << a.second.Size();
for (auto d : a.second)
{
oss2 << ' ' << d;
}
oss2 << '\n';
}
// Load new object from printed output
ArraysByName<int> abn_load2;
std::istringstream iss2(oss2.str());
abn_load2.Load(iss2);
REQUIRE(abn == abn_load2);
}