Compare commits

...
Author SHA1 Message Date
Tucker Babcock 4adfa0fc86 updting io benchmark 2020-05-05 15:44:44 -04:00
Tucker Babcock 857a24f6c4 Merge branch 'PCFinalProject' of github.com:mfem/mfem into PCFinalProject 2020-05-04 11:14:22 -07:00
Tucker Babcock 1ae22c7c69 adding io benchmark 2020-05-04 11:13:42 -07:00
Tucker Babcock 161ebff2a1 Merge branch 'PCFinalProject' of https://github.com/mfem/mfem into PCFinalProject 2020-05-04 12:42:18 -04:00
Tucker Babcock 3548f2cb83 adding num ranks printing 2020-05-04 12:42:13 -04:00
Tucker Babcock 41a7730048 adding barriers ahead of timings and averaging timing over all ranks 2020-05-04 09:40:57 -07:00
Tucker Babcock b638fb8960 adding all of the operator testing to one file 2020-05-03 22:02:26 -07:00
Tucker Babcock dc80f42710 Merge branch 'PCFinalProject' of https://github.com/mfem/mfem into PCFinalProject 2020-05-04 00:51:51 -04:00
Tucker Babcock 4414a3fc01 adding test to mfem examples 2020-05-04 00:50:16 -04:00
Tucker Babcock 2f683f80fa Merge branch 'mpiio-gf-dev' into PCFinalProject 2020-04-30 14:09:33 -07:00
Tucker Babcock 6ea2f7bf55 Merge branch 'mpiio-gf-dev' of github.com:mfem/mfem into mpiio-gf-dev 2020-04-30 14:06:16 -07:00
Tucker Babcock 581cafa7a7 updating documentation 2020-04-30 14:06:10 -07:00
Tucker Babcock c5bab73f9a Merge branch 'mpiio-gf-dev' into PCFinalProject 2020-04-30 15:45:12 -04:00
Tucker Babcock ac033e4406 added PA support to ParBilinearForm::TrueAddMult 2020-04-30 15:44:14 -04:00
Tucker Babcock b02eb71967 adding number of files printing control to example 1 2020-04-30 15:39:19 -04:00
Tucker Babcock d236571e4a cleaned up code in pgridfunc and added printing to example two. 2020-04-28 21:04:11 -07:00
Tucker Babcock 8d444d7f92 ordering by nodes appears to work now as well 2020-04-28 16:28:07 -07:00
Tucker Babcock 71937096f8 ordering by vdim works with high order 2020-04-28 16:26:30 -07:00
Tucker Babcock 4e0978cf3b can save and load files correctly for p = 1, errors otherwise. 2020-04-28 15:10:12 -07:00
Tucker Babcock e52fdd205a initial commit adding MPI-IO writing of GridFunction supporting writing to arbitrary number of files. Reading support to come 2020-04-27 22:41:28 -07:00
Tzanio Kolev 7690eca850 Merge pull request #1395 from mfem/bdr-int-order-fix
Boundary integral order bug [bdr-int-order-fix]
2020-04-26 10:17:17 -07:00
Veselin Dobrev 99cf263ee4 Merge pull request #1330 from mfem/navier-miniapp
Navier-Stokes miniapp
2020-04-23 20:26:50 -07:00
Veselin Dobrev d61f19726e Merge pull request #1357 from mfem/bc-example-dev
Adding example to illustrate boundary conditions [bc-example-dev]
2020-04-23 17:38:59 -07:00
Jean-Sylvain CAMIER 8e251ca987 Navier miniapp compilation with nvcc (#1433)
* navier_solver FORALL scope

* MFEM_FLAGS to MFEM_LINK_FLAGS

* Fext Write => ReadWrite
2020-04-22 08:27:03 -07:00
Stowell, Mark L eaeee4862a Merge remote-tracking branch 'origin/master' into bc-example-dev
# Conflicts:
#	CHANGELOG
#	examples/CMakeLists.txt
#	examples/makefile
2020-04-21 11:41:29 -07:00
Tzanio Kolev 8fd3957bfc Merge pull request #1263 from mfem/minimal-surf-dev-rewritten
Minimal surface example [minimal-surf-dev-rewritten]
2020-04-21 10:44:18 -07:00
Tzanio 2eba787d3b minor 2020-04-21 10:40:21 -07:00
Tzanio 004294f2fe Merge branch 'master' into minimal-surf-dev-rewritten
Conflicts:
	CHANGELOG
2020-04-21 10:17:51 -07:00
Tzanio Kolev 47d5689376 Merge pull request #1394 from mfem/tmop-combos
Combinations of TMOPIntegrators
2020-04-21 10:09:20 -07:00
Tzanio Kolev dcd2ed5c41 Merge pull request #1097 from mfem/drzisga/multigrid
Multigrid support [drzisga/multigrid]
2020-04-18 18:13:55 -07:00
Tzanio 7abd1bcb95 minor 2020-04-18 18:07:43 -07:00
HennesHajduk 1701ce9208 updated mesh name. 2020-04-18 10:52:50 +02:00
Tzanio Kolev 579de51a37 Merge pull request #1419 from mfem/deprecate-warn-dev
Resolve deprecation warnings in miniapps [deprecate-warn-dev]
2020-04-16 12:06:12 -07:00
camierjs 2254bce20f Move the CG out of the for-loop body for problem 1 2020-04-15 16:21:30 -07:00
Tzanio KolevandK10 b47c1751c6 Apply suggestions from code review
Co-Authored-By: K10 <ketan.mittal@gmail.com>
2020-04-15 14:48:24 -07:00
Stowell, Mark L cdcc519f6e Replacing error measure with a proper l2 error and average in one function 2020-04-15 13:23:27 -07:00
Stowell, Mark L a874fd8bdd Making ParFiniteElementSpace::GetParMesh a const method to match the serial variant 2020-04-15 13:22:38 -07:00
Veselin Dobrev 4093d575d7 In ex27.cpp, add a function for computing Lp boundary errors.
It supports error computations for Dirichlet, Neumann, and
Robin boundary conditions.
2020-04-14 20:15:54 -07:00
camierjs f18b128f0e Sync tau option in the parallel version 2020-04-14 17:34:18 -07:00
camierjs ee63ce045e Remove unsuported parallel mesh vis option 2020-04-14 17:15:00 -07:00
camierjs f031a1f98d Keep elmat initialization to zero 2020-04-14 17:11:07 -07:00
camierjs 8836399006 Meld serial/parallel 2020-04-14 17:03:49 -07:00
camierjs 9279273589 Style w/ new-lines and std fmin/fmax/fabs 2020-04-14 16:56:07 -07:00
camierjs 81ffd37614 Merge branch 'master' into minimal-surf-dev-rewritten 2020-04-14 16:35:07 -07:00
Stowell, Mark L 1ad22eff51 Removing deprecated flag from NonlinearForm::SetEssentialVdofs but left a gentler suggestion to use another method if possible. 2020-04-14 14:38:33 -07:00
Daniel Drzisga 2cadc23da4 Updated documention according to @v-dobrev 2020-04-14 22:24:01 +02:00
Stowell, Mark L 20f658d360 Updating Joule miniapp to avoid deprecation warning 2020-04-14 13:09:50 -07:00
Tzanio Kolev 278a27db5e Merge pull request #1411 from mfem/tpl-versions
Add version requirements for some external libraries.
2020-04-13 22:22:55 -07:00
Tzanio b49a5b782f Edits in CONTRIBUTING.md 2020-04-13 22:19:52 -07:00
Tzanio d827d51de5 Merge branch 'augment-release-checklist-dev' into tpl-versions
Conflicts:
	CONTRIBUTING.md
2020-04-13 22:14:47 -07:00
Tzanio Kolev c7a1e65362 Merge pull request #1342 from mfem/setpointmat-dev
Add IsoparametricTransformation::SetPointMat, remove FinalizeTransformation [setpointmat-dev]
2020-04-13 22:02:33 -07:00
Daniel Drzisga 293a68cb48 Added example 26/26p to the documentation 2020-04-13 18:56:11 +02:00
Daniel Drzisga 34b225d664 Switched from /** ... */ to /// ... comment 2020-04-13 18:43:05 +02:00
Jakub Červený 62117e96dd Added MFEM_DEPRECATED to functions already marked with ///@deprecated 2020-04-11 20:53:35 +02:00
Veselin Dobrev dd782e931d In example 7/7p, when using a non-conforming mesh, ensure it remains
continuous after snapping the nodes to the shere.
2020-04-10 20:03:56 -07:00
Tzanio Kolev 109a9dbdce Merge pull request #1391 from mfem/barker29/cg-converged-bugfix
[barker29/cg-converged-bugfix] add checks for indefinite preconditioner in CGSolver
2020-04-09 17:11:00 -07:00
Tzanio 5a8c53343d minor 2020-04-09 17:09:55 -07:00
Daniel Drzisga dac3abfe4b Moved PowerMethod to operator.* 2020-04-09 21:20:42 +02:00
Daniel Drzisga 09c4be18d3 Added descriptions and changed some formatting 2020-04-09 20:25:38 +02:00
Daniel DrzisgaandTzanio Kolev dacfcbcff6 Update examples/ex26.cpp
Co-Authored-By: Tzanio Kolev <tzanio@llnl.gov>
2020-04-09 19:47:17 +02:00
Julian Andrej af07cea014 Merge branch 'master' into navier-miniapp 2020-04-09 08:26:31 -07:00
Veselin Dobrev f9b8eae266 In INSTALL, add version requirements for some external libraries. 2020-04-08 19:39:48 -07:00
Julian Andrej bc521aa981 make style 2020-04-07 15:11:54 -07:00
Julian Andrej ce9a94c8c2 Address remaining issues 2020-04-07 15:10:27 -07:00
Tzanio 24818bf0d9 Merge branch 'master' into setpointmat-dev 2020-04-07 13:34:09 -07:00
Julian Andrej cfb7d3f071 refine NI comment 2020-04-07 12:29:58 -07:00
Julian Andrej 7b9a39131a correct TGV example 2020-04-07 12:29:49 -07:00
Julian Andrej f018de40cc Remove MPI finalize from error check 2020-04-07 12:29:31 -07:00
Julian Andrej 4f61db9f88 Provide mesh file in MFEM format for 3dfoc 2020-04-07 12:28:45 -07:00
camierjs 7eac26bb5b Merge branch 'master' into minimal-surf-dev-rewritten 2020-04-06 15:19:59 -07:00
Julian Andrej 98ef41c928 fix delete call for full assembly 2020-04-06 11:00:41 -07:00
Andrew T. Barker 72c754b8fa Add test_cg_indefinite.cpp to cmake system. 2020-04-06 09:32:32 -07:00
Andrew T. Barker 8560b69431 Address PR comments from @dylan-copeland 2020-04-06 09:31:01 -07:00
Julian Andrej 7c01bfedb1 update comments in OrthoSolver 2020-04-06 08:52:12 -07:00
Julian Andrej 6a3effeaee Typo in example documentation 2020-04-06 06:38:34 -07:00
Julian Andrej c5f6862844 Make valgrind happy 2020-04-06 06:38:19 -07:00
Julian Andrej 9aeb164514 fix ortho_solver typo 2020-04-04 15:02:13 -07:00
Julian Andrej bf9c0ca811 clear todos 2020-04-04 15:01:58 -07:00
Julian Andrej 182dfcf407 add version macro 2020-04-04 15:01:32 -07:00
Julian Andrej 0d945edbdf documentation update 2020-04-04 14:49:46 -07:00
Julian Andrej 90aa9eac5c missed delete for f_form in navier solver 2020-04-04 14:49:17 -07:00
Stowell, Mark L 764784e597 Switching error measure to l1 norm 2020-04-04 13:21:55 -07:00
Stowell, Mark L 26096315a0 Adding normalization factor to helper function 2020-04-04 12:56:25 -07:00
Stowell, Mark L b07f2963a6 Fixing boundary element orientations 2020-04-04 10:46:09 -07:00
Daniel Drzisga cecf5a8ebd Fixed CMake build 2020-04-04 12:26:46 +02:00
Daniel Drzisga 6f047ab99a * Moved TransferOperators (Prolongations) to FiniteElementSpaceHierarchy
* Merged MultigridSolver into Multigrid class
* Moved as much as possible in to the Multigrid class in order to make DiffusionMultigrid smaller
* Changed interfaces such that they look like in the mockup from the 4/1/20 telecon
2020-04-04 12:05:19 +02:00
Veselin Dobrev 260f1c40a3 Some small edits in comments and documentation. 2020-04-03 23:57:36 -07:00
Veselin Dobrev 1d3ed0af19 With CMake, use the same options for the ex27/ex27p tests as with
GNU make.
2020-04-03 23:55:03 -07:00
Tomov c678274e49 Valgrind error. 2020-04-03 17:18:01 -07:00
Tomov 5c36d2ff58 Removed a friend declaration that's not needed anymore. 2020-04-03 16:45:01 -07:00
Tomov 70dbab49bb Moved some implementations to tmop.cpp; cleanup. 2020-04-03 16:32:45 -07:00
Stowell, Mark L 46b7e7ddf1 make style 2020-04-03 11:32:38 -07:00
Stowell, Mark L 14b3173a38 Adding unit test to catch this error 2020-04-03 10:16:38 -07:00
Daniel Drzisga 17be017974 Renamed SpaceHierarchy to FiniteElementSpaceHierarchy 2020-04-03 16:33:55 +02:00
Daniel Drzisga f1e81513fe Switched to new implementation of RefinementOperator::Mult 2020-04-03 16:16:58 +02:00
Daniel Drzisga 043172bd1a Merge remote-tracking branch 'origin/master' into drzisga/multigrid 2020-04-03 15:44:25 +02:00
Tomov 75bf91490c Minor. 2020-04-02 18:11:58 -07:00
Tomov e36553970c Minor. 2020-04-02 17:49:03 -07:00
Tomov 696ade8f55 Updated the miniapps. Added a new sample run. 2020-04-02 17:47:54 -07:00
Tomov 500aa60807 New interface for combination of TMOP metrics. Correct normalization.
TMOPDescentNewtonSolver now inherits TMOPNewtonSolver.
Added flags to DiscreteAdaptTC to avoid extra computations in a combo.
2020-04-02 15:06:38 -07:00
camierjs ec08270b17 Remove unsued alpha option 2020-04-02 11:49:28 -07:00
camierjs 7fde6bfdfe Rename miniapp to minimal-surface
Add serial snapshot
Costa tau shift & scale
2020-04-02 11:36:45 -07:00
Stowell, Mark L 7bb1ec3744 Only use bbfi_[0] if the corresponding marker is on 2020-04-01 22:38:03 -07:00
Stowell, Mark L 41e7758098 Adding comments to highlight the need for diffusion coefficient in Neumann and Robin BCs 2020-04-01 22:26:06 -07:00
Stowell, Mark L 6b9c607961 Removing unneeded spaces in argument list for consistency 2020-04-01 22:25:24 -07:00
Andrew T. Barker 1effb0b7de CGSolver: add indefinite preconditioner check 2020-04-01 12:03:44 -07:00
Andrew T. Barker c387f4b084 Add (failing) unit test to demonstrate CGSolver bug with indefinite preconditioner. 2020-04-01 10:58:24 -07:00
camierjs 99b7878de7 Remove Print warnings 2020-04-01 10:51:46 -07:00
camierjs ff4a3eaff5 Update changelog 2020-04-01 10:33:12 -07:00
camierjs 19677d4c29 Parallel print 2020-04-01 10:31:03 -07:00
camierjs 06790cad03 Add print option 2020-04-01 10:02:46 -07:00
camierjs b8ef1f7c57 minimal-surf makefile update 2020-03-31 18:03:34 -07:00
camierjs f942298aaf miniapps/meshing/pmesh-minimal-surface sample runs 2020-03-31 18:00:04 -07:00
camierjs b4da619f57 minimal surface miniapp sample runs update 2020-03-31 17:53:13 -07:00
camierjs 843995cefb Sample runs for {,p}mesh-minimal-surface 2020-03-31 17:45:16 -07:00
camierjs adb2fb9859 D1D argument for OCCA fix 2020-03-31 17:25:55 -07:00
camierjs b4f691ba50 general/cuda.cpp revert 2020-03-31 17:06:54 -07:00
camierjs 52ab17aac4 fem/bilininteg_diffusion warnings fix 2020-03-31 16:53:12 -07:00
camierjs 0a8d5244b4 include path fix 2020-03-31 16:20:23 -07:00
camierjs 19efd596ff vecdiffusion cleanup and std::isnan fix 2020-03-31 15:17:02 -07:00
camierjs bf3d249ba5 Cleanup, doc & sync 2020-03-31 14:49:41 -07:00
Tzanio Kolev 4134e9496a Merge pull request #1305 from mfem/paradd_pr
Change Add and ParAdd for hypre matrices to use hypre parcsradd
2020-03-31 12:19:34 -07:00
Tzanio Kolev d8e05f8cfa Merge pull request #1387 from mfem/tmop-adapt-bugfix
Bug in TMOP's AdvectorCG.
2020-03-31 12:18:01 -07:00
Stowell, Mark L 56fb7323eb Adding comments to explain integration order 2020-03-30 23:18:53 -07:00
Stowell, Mark L 3354359f86 make style 2020-03-30 23:12:03 -07:00
bslazarov 32f7e1f88f add delete mesh;
modified:   ex27.cpp
2020-03-30 21:32:02 -07:00
Tomov 5d31bd1347 Fixed a parallel communication bug in tmop's AdvectorCG. 2020-03-30 14:57:50 -07:00
camierjs 39b8d3348b Merge branch 'master' into minimal-surf-dev-rewritten 2020-03-30 10:04:15 -07:00
Tzanio Kolev a1f7d70ff2 Merge pull request #1188 from mfem/adios2-dev
ADIOS2 output with ParaView visualization, adios2stream and ADIOS2DataCollection
2020-03-29 13:12:39 -07:00
Stowell, Mark L 8a56de18f3 Renumbering example 26 to example 27 2020-03-29 12:51:00 -07:00
Stowell, Mark L 1fd3984bda Adding comments to CHANGELOG 2020-03-29 12:42:58 -07:00
Tzanio 661a52077d Updated examples 2020-03-29 12:37:43 -07:00
Stowell, Mark L bb034ea0fa Renumbering example 29 to example 26 2020-03-29 12:25:04 -07:00
Veselin Dobrev 550dbf0877 For testing purposes, copy the implementation of the method
void FiniteElementSpace::RefinementOperator::Mult
from the master branch, while keeping the new implementation in
a commented out block.
2020-03-26 15:07:44 -07:00
Veselin Dobrev 000dea1e87 Merge branch 'master' into drzisga/multigrid
Resolved conflicts:
   CHANGELOG
   examples/CMakeLists.txt
   examples/makefile
2020-03-26 14:54:29 -07:00
Tzanio 0ef6573c30 small edits 2020-03-25 21:46:07 -07:00
Tzanio 7e3272a9ff Merge branch 'master' into adios2-dev 2020-03-24 08:22:06 -07:00
Tzanio b5655c5f56 Renamed example 25 to 26 2020-03-23 13:19:56 -07:00
Tzanio cd12f44814 Merge branch 'master' into drzisga/multigrid
Conflicts:
	CHANGELOG
2020-03-23 11:36:53 -07:00
ben-s-southworth b5b174b625 Merge w/ master 2020-03-22 14:54:43 -07:00
Daniel Drzisga 84f71ca2ee Added transfer tests for triangle and tetrahedral elements 2020-03-21 17:24:36 +01:00
Daniel Drzisga fb34914939 Fixed formatting 2020-03-21 16:53:51 +01:00
Daniel Drzisga d7acde34f6 Fixed formatting 2020-03-21 16:36:34 +01:00
Daniel Drzisga ddb3882c1d Visualize solutions on finest mesh of the space hierarchy 2020-03-21 16:35:13 +01:00
Julian Andrej 22e7887659 Update CFL computation 2020-03-20 18:04:04 -07:00
camierjs 83b9883b8a Merge branch 'master' into minimal-surf-dev-rewritten 2020-03-20 14:45:55 -07:00
Julian Andrej 3079a8b55f Add CFL computation 2020-03-20 10:30:08 -07:00
Julian Andrej 1939e40fcf Fix typo 2020-03-20 08:29:33 -07:00
ben-s-southworth 6f55674efb Added flag based on hypre version 2020-03-20 08:22:09 -07:00
Julian Andrej cf8cd1602e Update test tolerance for kovasznay 2020-03-19 17:11:18 -07:00
Julian Andrej 58b81e922b Do not get an FESpace from a nullptr 2020-03-19 16:58:01 -07:00
Julian Andrej 71b87dc822 Changes for kovasznay 2020-03-19 14:26:58 -07:00
Daniel Drzisga e1ba2cd4bb Reverted parallel transfers back to usage of RestrictionMatrix and fixed GPU support by building transpose 2020-03-19 20:55:28 +01:00
Tzanio 4376a2f456 Small edits 2020-03-18 16:00:03 -07:00
William F Godoy b44df9c005 License, style, and CHANGELOG changes 2020-03-18 18:20:37 -04:00
Daniel Drzisga e947e42693 Removed duplicate space character in license headers 2020-03-18 22:35:39 +01:00
Daniel Drzisga 0d0d0a5ab5 Merged diffs to master 2020-03-18 22:31:06 +01:00
Tzanio KolevandRobert Carson 40e9f7eb82 Update CHANGELOG
Co-Authored-By: Robert Carson <rac428@cornell.edu>
2020-03-18 14:02:11 -07:00
Daniel Drzisga 5bfe7e7304 Merge remote-tracking branch 'origin/master' into drzisga/multigrid 2020-03-18 20:23:22 +01:00
Julian Andrej e33b91c649 Documentation updates. 2020-03-18 08:54:49 -07:00
Julian Andrej 9907ef6667 Update documentation. 2020-03-18 08:44:51 -07:00
William F Godoy 11afcc6321 Requested changes from PR review
Keep a few examples
Correct function signature in gridfunc and pgridfunc
2020-03-18 09:42:04 -04:00
William F Godoy 9c379891bf Resolving conflicts with latest master 2020-03-18 09:41:46 -04:00
Daniel Drzisga 5f299e7811 Added tests for parallel grid transfers 2020-03-18 11:30:36 +01:00
Daniel Drzisga 3e927c13ea Replaced coarse solver by PCG with AMG preconditioner 2020-03-18 11:27:44 +01:00
Daniel Drzisga 9a96aebe20 Changed loglevel 2020-03-18 11:24:38 +01:00
Daniel Drzisga 3c9d716284 Reverting back to functioning matrix-based transposed of parallel restriction 2020-03-18 11:23:36 +01:00
Stowell, Mark L 1a0fcbb135 Initializing vectors to zero before iterative solve 2020-03-17 16:32:17 -07:00
Julian Andrej ed3ff2fd5b Merge branch 'master' into navier-miniapp 2020-03-17 15:51:07 -07:00
Stowell, Mark L 12ce0428f5 Moving boundary condition integrals into functions to reduce code duplication 2020-03-17 15:17:42 -07:00
Julian Andrej acb1ca9123 Typo in gitignore 2020-03-17 14:28:38 -07:00
Julian Andrej 617da26974 Generalize output to glob in clean target 2020-03-17 14:12:57 -07:00
Julian Andrej 8a672dd8ab add output to clean target and gitignore 2020-03-17 14:11:49 -07:00
Julian Andrej b7ce8d912e make style 2020-03-17 13:58:54 -07:00
Julian Andrej fff989837f Documentation updates 2020-03-17 13:58:27 -07:00
Julian Andrej e17bdcdc3f Update for makefile test system 2020-03-17 13:50:03 -07:00
Julian Andrej a6cfca37c2 Documentation updates 2020-03-17 13:22:08 -07:00
Julian Andrej 87924fcb20 Documentation update 2020-03-17 12:14:36 -07:00
Stowell, Mark L c844a724c0 More changes to comments 2020-03-17 11:40:04 -07:00
Stowell, Mark L 21c5f3433d Switching to ProjectBdrCoefficient for clarity 2020-03-17 11:06:30 -07:00
Stowell, Mark L 404d6423c6 Changing test run 2020-03-16 20:42:59 -07:00
Stowell, Mark L c6685674ce make style 2020-03-16 17:25:29 -07:00
Stowell, Mark L ffa4f0f58c Rewriting comments 2020-03-16 17:22:18 -07:00
Julian Andrej b597a1811a makefile update 2020-03-16 17:01:49 -07:00
Julian Andrej 471fbebf13 makefile update 2020-03-16 16:48:56 -07:00
Julian Andrej 397706e16a make style 2020-03-16 16:36:20 -07:00
Julian Andrej ce80c54b72 Add makefile 2020-03-16 15:45:30 -07:00
Stowell, Mark L 443b16588e Changing example names so that .gitignore will ignore them 2020-03-16 12:38:42 -07:00
Stowell, Mark L 954b7f331b Undoing an unintentional formatting change 2020-03-16 11:54:46 -07:00
Stowell, Mark L 9e6106cd39 make style 2020-03-16 11:53:09 -07:00
Stowell, Mark L aa3f96cd04 Adding serial example 2020-03-16 11:52:45 -07:00
Stowell, Mark L 0f959ddcb4 Removing h1 and dg examples in favor of a combined example 2020-03-16 11:26:55 -07:00
Stowell, Mark L f1d8f53c98 Merge remote-tracking branch 'origin/master' into bc-example-dev 2020-03-16 11:25:27 -07:00
Julian Andrej 877dc1f939 Add license 2020-03-16 10:11:30 -07:00
camierjs ea4087fda2 Cleanup & Tidy 2020-03-16 09:34:31 -07:00
Julian Andrej 4dab299a67 updates 2020-03-16 09:03:28 -07:00
camierjs 15f747e2dc Merge branch 'master' into minimal-surf-dev-rewritten 2020-03-15 14:40:50 -07:00
Daniel Drzisga f5deb38554 Implemented matrix-free L-dof to true-dof restriction 2020-03-15 17:03:22 +01:00
Daniel Drzisga 9ec3aec2f5 Restructured example 25 2020-03-14 16:09:23 +01:00
Daniel Drzisga d75b144cbf Fix for parallel transfer operators on CPU 2020-03-14 16:05:15 +01:00
Daniel Drzisga 43e0b2cdc1 Merge remote-tracking branch 'origin/master' into drzisga/multigrid 2020-03-14 14:41:23 +01:00
Stowell, Mark L adafac64c0 make style 2020-03-13 19:33:22 -07:00
camierjs 0a6de2eb72 Add problem 1 for z=f(x,y) surfaces, using Picard iterations 2020-03-13 17:30:09 -07:00
Stowell, Mark L 23b5971c9e make style 2020-03-13 16:18:39 -07:00
Stowell, Mark L 87f47fb66d Adding an example which can use either H1 or DG for Laplace with a variety of BCs 2020-03-13 16:18:28 -07:00
Stowell, Mark L 4c7262177f Merge remote-tracking branch 'origin/master' into bc-example-dev 2020-03-13 14:44:43 -07:00
Daniel DrzisgaandWill Pazner 89458cdfd5 Update examples/ex25p.cpp
Co-Authored-By: Will Pazner <11493037+pazner@users.noreply.github.com>
2020-03-13 10:50:21 +01:00
camierjs 13367f385a Merge branch 'master' into minimal-surf-dev-rewritten 2020-03-12 21:44:49 -07:00
camierjs 39af243bd4 MPI Cleanup 2020-03-12 18:52:28 -07:00
camierjs 90cf1b0e1a All 10 surfaces w/ MPI, CUDA & AMR 2020-03-12 18:21:26 -07:00
camierjs 62be534bda Sync miniapps/meshing/pmesh-minimal-surface.cpp 2020-03-12 17:55:32 -07:00
camierjs 5f690bf386 FullPeach BC fix 2020-03-12 17:49:51 -07:00
camierjs d8611f1972 Clean parallel minimal surface miniapp 2020-03-12 09:27:05 -07:00
camierjs 3981b45cec Cleanup templates in serial 2020-03-11 21:24:11 -07:00
camierjs 117a3d2b01 Trying w/o crtp 2020-03-11 18:13:31 -07:00
camierjs 59c2bbff1c Initial options and sync parallel version 2020-03-11 08:51:57 -07:00
camierjs 36c2282668 Merge branch 'master' into minimal-surf-dev-rewritten 2020-03-11 08:04:33 -07:00
Daniel DrzisgaandAndrew T. Barker 465ddfd070 Update fem/spacehierarchy.hpp
Co-Authored-By: Andrew T. Barker <barker29@llnl.gov>
2020-03-10 09:04:34 +01:00
camierjs 1d8af409c2 Cleanup 2020-03-09 10:18:09 -07:00
Daniel Drzisga 2e50768899 Added support for geometric h-refinements in example 25 2020-03-08 18:05:34 +01:00
Daniel Drzisga 5a3a33fbf3 Updated CHANGELOG 2020-03-08 17:41:51 +01:00
Daniel Drzisga aec494db67 Added comments 2020-03-08 16:15:54 +01:00
Daniel Drzisga 08b494b41e Merge remote-tracking branch 'origin/master' into drzisga/multigrid 2020-03-08 15:36:59 +01:00
Daniel Drzisga 238fad13c6 Fixed bug in parallel 2020-03-08 15:03:59 +01:00
Daniel Drzisga 230eaccb82 Added option to estimate the largest eigenvalue within the ChebyshevSmoother constructor 2020-03-08 13:16:01 +01:00
Daniel Drzisga cb7114ee76 Removed duplicate DecodeDof method 2020-03-08 12:09:38 +01:00
Daniel Drzisga b556da5884 Removed (Par)MultigridBilinearForm and moved them into example 25 2020-03-08 11:41:26 +01:00
Jakub Červený 7d1fa2c336 Added the macro MFEM_DEPRECATED. 2020-03-07 19:31:25 +01:00
Daniel Drzisga 1a378f090f Reverted addition of SetMaxIter in HypreBoomerAMG 2020-03-07 12:19:42 +01:00
Daniel Drzisga 1df3169947 Changed license headers 2020-03-07 10:27:38 +01:00
Daniel Drzisga ab322b5938 Merge remote-tracking branch 'origin/master' into drzisga/multigrid 2020-03-07 10:17:06 +01:00
Daniel Drzisga b331b21038 Merge remote-tracking branch 'origin/master' into drzisga/multigrid 2020-03-07 10:16:48 +01:00
camierjs b19e56404f Cleanup and debug AMR cuda 2020-03-06 19:10:34 -08:00
camierjs 5ac5153686 Before cleaning min/maxJ vs min/maxW 2020-03-06 15:36:23 -08:00
Daniel Drzisga 621761de5c Added tests for geometric prolongation and vector spaces without tensor product structure 2020-03-06 21:50:15 +01:00
Jakub Červený 513fdc2692 Added IsoparamTransformation::SetPointMat, removed FinalizeTransformation. 2020-03-06 21:15:10 +01:00
camierjs b648c9a972 Parallel AMR 2020-03-05 16:43:55 -08:00
camierjs 5da7f2a432 Sanity fix with debug device 2020-03-05 09:56:50 -08:00
camierjs bb9158ae9a Fix merge with MAX_VDIM2D 2020-03-05 09:14:53 -08:00
camierjs 036d399856 WIP Merge branch 'master' into minimal-surf-dev-rewritten 2020-03-05 08:56:51 -08:00
camierjs 9d4695a34a Update headers 2020-03-04 20:54:43 -08:00
camierjs 5ef984ee94 Merge branch 'master' into minimal-surf-dev-rewritten 2020-03-04 20:53:20 -08:00
camierjs a822710800 Serial AMR within solve 2020-03-04 19:56:38 -08:00
camierjs cde6362bbe WIP Amr 2020-03-04 18:17:17 -08:00
Julian Andrej 2db3f744b6 Merge branch 'master' into navier-miniapp 2020-03-04 14:50:40 -08:00
camierjs 6f79edc6a3 pmesh-minimal-surface update 2020-03-04 13:32:41 -08:00
camierjs 65b5db7d39 Cleanup and option logic for component solver 2020-03-04 13:15:35 -08:00
Julian Andrej a63e32ef06 change cmake file for windows 2020-03-04 12:54:38 -08:00
camierjs 42f39415e8 Cleanup and lambda/radial update 2020-03-04 12:41:24 -08:00
camierjs d7a5465ae3 Alpha Costa 2020-03-03 21:08:57 -08:00
camierjs 650da1ee12 Cleanup 2020-03-03 17:31:38 -08:00
Julian Andrej 2acc5d886f disable 3dfoc example for now 2020-03-03 16:21:40 -08:00
Julian Andrej 6eba50f4eb fine tune kovasznay example 2020-03-03 16:21:08 -08:00
camierjs da9a2610bc Random AMR option 2020-03-03 11:39:04 -08:00
camierjs 3e58124860 Merge branch 'master' into minimal-surf-dev-rewritten 2020-03-03 10:48:04 -08:00
Julian Andrej 54ec17f546 navier stokes miniapp draft 2020-03-02 16:09:51 -08:00
Daniel Drzisga 28421d2b8f Added license header to eigenvalue.hpp 2020-03-02 00:29:05 +01:00
camierjs cbda3fc4cc Merge branch 'master' into minimal-surf-dev-rewritten 2020-03-01 12:22:30 -08:00
Julian Andrej 98f3ea3945 Merge branch 'master' into navier-miniapp 2020-02-28 15:50:51 -08:00
camierjs a734a01961 Merge branch 'master' into minimal-surf-dev-rewritten 2020-02-25 17:10:13 -08:00
camierjs e7ba9c2fe1 x & nodes HostRead to get norm 2020-02-25 17:07:33 -08:00
camierjs 4ed2e067cf Odd/even Costa holes fix 2020-02-25 16:42:58 -08:00
Julian Andrej dd92afe66d Merge branch 'master' into navier-miniapp 2020-02-24 11:06:12 -08:00
camierjs db41a8ce14 gitignore and cmake headers fix 2020-02-23 16:56:56 -08:00
camierjs 37383544e9 Cleanup and pmeld 2020-02-23 16:11:01 -08:00
Daniel Drzisga cbced4052c Removed TimedMultigridOperator class 2020-02-23 22:20:15 +01:00
Daniel Drzisga 6ba5a136fa Reversed order of deletions 2020-02-23 22:10:56 +01:00
Daniel Drzisga 7884798b25 Added missing newline 2020-02-23 21:52:08 +01:00
Daniel Drzisga 4cbf753481 Added missing newlines at the end of files 2020-02-23 21:47:57 +01:00
camierjs 32a8f8951b Half plane transform & converge 2020-02-22 21:12:46 -08:00
camierjs b7a30ee155 Before boundaries cleanup 2020-02-22 13:34:46 -08:00
camierjs 6cf20ca5fd Cleanup 2020-02-21 18:24:20 -08:00
camierjs cc1e96349a Before removing corners 2020-02-21 17:51:44 -08:00
camierjs b71ab0fae6 Stable but corners 2020-02-21 16:59:17 -08:00
camierjs 69b7978fff Before creating from scratch 2020-02-21 16:25:54 -08:00
camierjs e6ac1a9ff5 Added surroundings 2020-02-21 12:01:58 -08:00
camierjs 44b339739f Scale, but shouldn't remove vertices 2020-02-20 21:57:05 -08:00
camierjs b8f2a42838 Snap w/o midpoint 2020-02-20 16:00:57 -08:00
camierjs 701018b2e1 Cleanup 2020-02-20 08:35:17 -08:00
camierjs b98b0502bc Before snaping nodes 2020-02-19 17:27:26 -08:00
Ben Southworth b44653ad0e Change Add and ParAdd for hypre matrices to use hypre parcsradd 2020-02-19 14:41:27 -08:00
camierjs 84134cfd9d Costa complex stand-alone 2020-02-19 12:45:32 -08:00
camierjs d192b64737 Jacobi theta 1,2,3,4 2020-02-18 18:06:34 -08:00
camierjs bf3cbcbffb Elliptic with series 2020-02-17 19:15:51 -08:00
camierjs f1ba920c99 Costa wip 2020-02-17 17:52:14 -08:00
Tzanio Kolev 49b195effa Merge branch 'master' into drzisga/multigrid 2020-02-16 11:50:05 -08:00
camierjs 5d93ad8c25 Remove dbg in mesh-minimal-surface 2020-02-12 20:02:25 -08:00
camierjs 5b3a200af7 Parallel pmesh-minimal-surface 2020-02-12 18:06:15 -08:00
camierjs fb38d0862c miniapps/meshing/pmesh-minimal-surface.cpp 2020-02-12 17:25:17 -08:00
camierjs 61f3bb7f39 Splitted back mesh-minimal-surface and pmesh-minimal-surface 2020-02-12 16:59:23 -08:00
camierjs 548858ce20 Merge branch 'master' into minimal-surf-dev-rewritten 2020-02-11 08:27:43 -08:00
camierjs a57fe0c7ff Merge branch 'master' into minimal-surf-dev-rewritten 2020-02-09 20:31:30 -08:00
camierjs 9d9f760acd Add radial and lambda options 2020-02-09 16:59:29 -08:00
Daniel Drzisga 451e669eb1 Merge branch 'master' into drzisga/multigrid 2020-02-09 23:03:39 +01:00
Daniel Drzisga f0dc46c269 Updated CHANGELOG 2020-02-09 22:45:13 +01:00
Daniel Drzisga c9c19147fd Updated description of examples 2020-02-09 22:45:02 +01:00
Daniel Drzisga c05dd1f957 Removed todos and added checks 2020-02-09 22:44:42 +01:00
camierjs 1921297186 Incorporate TK addons 2020-02-08 21:31:02 -08:00
camierjs f5fc032281 Merge branch 'master' into minimal-surf-dev-rewritten 2020-02-07 16:31:31 -08:00
Daniel Drzisga ec6f9cf763 Ran make style with correction version of astyle (2.05.1) 2020-02-08 01:16:13 +01:00
Daniel Drzisga 671a9a2ad2 Added example 23 to CMakeLists 2020-02-08 00:56:16 +01:00
Daniel Drzisga 3b775f00e5 Removed mg miniapp from CMakeLists 2020-02-08 00:54:29 +01:00
Julian Andrej 958b4f9fe3 Merge remote-tracking branch 'origin/parmixedbilinearform-rectop' into navier-miniapp 2020-02-07 13:48:41 -08:00
Daniel Drzisga 7b4dbbbb0a Removed mg miniapp and added examples instead 2020-02-02 17:50:36 +01:00
Daniel Drzisga d2d684b590 Merge branch 'master' into drzisga/multigrid 2020-02-02 14:21:09 +01:00
camierjs 64ef442633 Modified miniapps/meshing/makefile 2020-01-27 16:30:53 -08:00
camierjs 79835b755f Cleanup miniapps/meshing mesh-minimal-surface 2020-01-27 16:19:33 -08:00
camierjs e1349e712a Renamed mesh-minimal -> mesh-minimal-surface 2020-01-27 15:18:44 -08:00
camierjs 5c7032a68e Move examples/ex23 to miniapps/meshing 2020-01-27 15:16:55 -08:00
camierjs 0cc8de027e Factoring ex23 & ex23p 2020-01-27 15:14:39 -08:00
camierjs f9edcc9987 Cleanup ex23p and fix pmesh leak 2020-01-27 13:27:11 -08:00
camierjs 024d95d059 Merge branch 'master' into minimal-surf-dev-rewritten 2020-01-27 11:46:46 -08:00
camierjs c3f8e6a533 Update fem/CMakeLists.txt 2020-01-27 11:41:50 -08:00
camierjs 0b6b595556 Style and bring back non-square VectorDiffusionIntegrator::AssembleElementVector 2020-01-27 11:36:45 -08:00
camierjs e61223a594 meld & merge toward master, still fa sdim errors 2020-01-24 18:27:31 -08:00
camierjs 735740f24e ex23p cleanup 2020-01-24 17:12:41 -08:00
camierjs 9925d3985a Merge branch 'master' into minimal-surf-dev-rewritten 2020-01-24 14:24:32 -08:00
camierjs 5608d9e0fe Merge branch 'master' into minimal-surf-dev 2020-01-23 23:44:53 -08:00
camierjs 49585acf79 Merge fix 2020-01-23 23:44:52 -08:00
camierjs c159efc9ab Merge branch 'master' into minimal-surf-dev 2020-01-23 23:44:36 -08:00
camierjs 19a69cedaa ByVector SurfaceSolver 2020-01-23 23:42:29 -08:00
camierjs 8c9697c7a9 Turned SlottedSphere to a Surface 2020-01-23 23:42:29 -08:00
camierjs 19a6358cf6 Parallel cleanup 2020-01-23 23:42:29 -08:00
camierjs 09b386b6d6 Parallel surfaces 2020-01-23 23:42:29 -08:00
camierjs 3fab6973d8 Parallel mobius-strip 2020-01-23 23:42:29 -08:00
camierjs 12decde31b Undeclared 'and' identifier fix 2020-01-23 23:42:29 -08:00
camierjs 9cfeccaa2c QuadratureInterpolator::Mult (vdim == 3 and dim == 2) case fix 2020-01-23 23:42:29 -08:00
camierjs 51b9ae07b3 Add missing file in fem/CMakeLists.txt 2020-01-23 23:42:29 -08:00
camierjs 0f29feab4e Add ex23 in CMakeLists.txt 2020-01-23 23:42:29 -08:00
camierjs adea89cf64 Cleanup and vdiffusion fix 2020-01-23 23:42:29 -08:00
camierjs 8d66715281 make style 2020-01-23 23:42:29 -08:00
camierjs add421be30 Merge branch 'master' into minimal-surf-dev 2020-01-23 23:42:27 -08:00
camierjs 2f1a4e1073 Merge branch 'master' into minimal-surf-dev 2020-01-23 23:38:57 -08:00
camierjs 9e4f062967 Merge branch 'master' into minimal-surf-dev 2020-01-23 23:37:04 -08:00
camierjs 351f6646a8 Merge branch 'master' into minimal-surf-dev 2020-01-23 23:26:42 -08:00
Will Pazner b3510944ec Add slotted sphere mesh (still degenerates) 2020-01-23 23:18:32 -08:00
camierjs dfd5766386 Incorporate SnapNodes into FPeach 2020-01-23 23:18:32 -08:00
camierjs 30fed48306 Forall for Set/Get components 2020-01-23 23:18:32 -08:00
camierjs f73d90fd06 Reorder surfaces and re-enable components, partial assembly solves 2020-01-23 23:18:32 -08:00
camierjs bf5887d3ef BC tries for full Peach 2020-01-23 23:18:32 -08:00
camierjs 57bb75439b Add the DeleteGeometricFactors after update 2020-01-23 23:18:32 -08:00
Adrien M. Bernede 3c03649d7b Initial options and revert visualization to mesh 2020-01-23 23:18:26 -08:00
camierjs eb7a46592b Revert style changes 2020-01-23 23:16:48 -08:00
camierjs 1c216db921 Cleanup 2020-01-23 23:16:48 -08:00
camierjs cae6dfbdc7 Refactor & Cleanup 2020-01-23 23:16:48 -08:00
camierjs f7b59cd181 BC fix 2020-01-23 23:16:48 -08:00
camierjs d1c82eb08d Cleanup + Full Peach 7th surface 2020-01-23 23:16:48 -08:00
camierjs 891195888b Warning message 2020-01-23 23:16:48 -08:00
camierjs 80ae995b5b Merge branch 'minimal-surf-dev' of github.com:mfem/mfem into minimal-surf-dev 2020-01-23 23:16:43 -08:00
camierjs 4e7be2cc90 ex4 with param 7 2020-01-23 23:15:07 -08:00
camierjs 9f5ee9c4be Cleanup Surface and Solver classes 2020-01-23 23:15:07 -08:00
camierjs 0dcd903dcf Visualization 2020-01-23 23:15:07 -08:00
camierjs f01991bf4d Fallback for Eval2D<3> 2020-01-23 23:15:07 -08:00
camierjs 53ea097637 Cleanup with static parameterization surfaces 2020-01-23 23:15:01 -08:00
Daniel Drzisga 7cbb0d4848 Merge branch 'master' into drzisga/multigrid 2020-01-06 21:06:23 +01:00
Daniel Drzisga aa39d82053 make style 2019-12-29 21:22:56 +01:00
Daniel Drzisga b843a613bb Merge branch 'feature/okina-jacobi' into drzisga/multigrid 2019-12-29 21:18:22 +01:00
Daniel Drzisga 90ed3f67ef Merge branch 'master' into drzisga/multigrid 2019-12-27 21:24:18 +01:00
Stowell, Mark L b9bba72e16 Cleaning up and preparing to combine new examples 2019-12-08 21:46:32 -08:00
Stowell, Mark L 76dd40f675 Adding examples that demonstrate Dirichlet, Neumann, and Robin BCs in H1 and DG 2019-12-08 17:19:40 -08:00
Stowell, Mark L cbe42b8606 Implementing BoundaryLFIntegrator::AssembleRHSElementVector for use with DG basis 2019-12-08 16:38:21 -08:00
Daniel Drzisga 637361e716 Use assembled coarse matrix with BoomerAMG for any coarse FE order 2019-11-25 10:35:47 +01:00
Daniel Drzisga 7502661539 Merge branch 'master' into drzisga/multigrid 2019-11-22 22:01:32 +01:00
Tzanio eb9de64810 make style 2019-11-11 09:18:44 -08:00
Daniel Drzisga f451d835ad Merge branch 'feature/okina-jacobi' into drzisga/multigrid 2019-11-08 11:05:10 -08:00
Daniel Drzisga 9483fcb723 Possible fix for TrueTransferOperator on GPU 2019-11-07 11:07:34 -08:00
Daniel Drzisga 1d1435282c Fixed memleaks 2019-11-06 16:05:28 -08:00
Daniel Drzisga 2e2492a5b4 Added documentation to TimedMultigridOperator 2019-11-06 14:52:22 -08:00
Daniel Drzisga 70d37f0cbe Fixed sign warnings 2019-11-06 14:45:35 -08:00
Daniel Drzisga c8e99c1387 Minor cleanup 2019-11-06 14:30:50 -08:00
Daniel Drzisga 693c7dcac6 Moved free functions 2019-11-06 13:17:18 -08:00
Daniel Drzisga 6df7a7671f Cleaned up transfer classes and added more documentation 2019-11-06 13:14:59 -08:00
Daniel Drzisga 413381424e Added documentation to BooleanMask method 2019-11-06 11:16:40 -08:00
Daniel Drzisga 148a799fb9 Implemented 3D transfers for GPU 2019-11-06 11:01:22 -08:00
Daniel Drzisga 6bcea6555d Fixed GPU support for 2D transfers 2019-11-06 10:28:15 -08:00
Daniel Drzisga 29ba36538d Moved kernels to free functions 2019-11-05 16:50:19 -08:00
Daniel Drzisga bd7ddf62ed Fixed device lambda compilation error #2 2019-11-05 16:26:31 -08:00
Daniel Drzisga fb801bcd02 Fixed device lambda compilation error 2019-11-05 16:25:15 -08:00
Daniel Drzisga aa4c638696 WIP: Adding support for transfers on GPU 2019-11-05 16:18:17 -08:00
Daniel Drzisga cf5978d922 Added p-multigrid to ex1p 2019-10-30 11:22:25 -07:00
Daniel Drzisga 4af713050c Updated cmake files 2019-10-29 14:01:55 -07:00
Daniel Drzisga 7401af384a Merge branch 'drzisga/pa_coeff' into drzisga/multigrid 2019-10-28 15:52:45 -07:00
Daniel Drzisga 8d7cf94f9f Added ParMultigridBilinearForm 2019-10-28 15:52:18 -07:00
Daniel Drzisga af91548dad Added support for vector spaces in FiniteElementSpace::H2L_GlobalRestrictionMatrix 2019-10-25 09:25:50 -07:00
Daniel Drzisga 54f61b2210 Added p-multigrid solver to example 1 2019-10-24 15:30:24 -07:00
Daniel Drzisga 7ecd564594 Fixed bug in PowerMethod where serial execution failed when compiled with MPI 2019-10-24 14:13:21 -07:00
Daniel Drzisga 5b2641c955 Fixed mistake in counting of operator applications 2019-10-23 15:36:00 -07:00
Daniel Drzisga 8d3fca168b Added missing includes 2019-10-14 16:16:58 -07:00
Daniel Drzisga 280874b41d Added test for transfer operators 2019-10-14 15:56:24 -07:00
Daniel Drzisga 8c6213ab75 Renamed p refinement operators 2019-10-14 15:56:05 -07:00
Daniel Drzisga e470906223 Added support for vector spaces in tensor product transfers 2019-10-14 14:44:59 -07:00
Daniel Drzisga 30c2609915 Implemented restriction between FE spaces exploiting tensor product structure 2019-10-14 14:13:11 -07:00
Daniel Drzisga 0e3b2e3323 Implemented interpolation between FE spaces exploiting tensor product structure 2019-10-14 13:09:01 -07:00
Daniel Drzisga 28f9c878c1 Added simple timing support for multigrid 2019-10-10 10:35:21 -07:00
Daniel Drzisga 2c9ee23a19 Exploiting memory layout for better performance in matrix-free restriction 2019-10-10 08:21:47 -07:00
Daniel Drzisga f4cebec7cc Added mg-test-par target to makefile 2019-10-09 13:57:30 -07:00
Daniel Drzisga 63fe1a98b7 Fixed merge 2019-10-09 13:51:14 -07:00
Daniel Drzisga 6afcf71c28 Merge branch 'drzisga/pa_coeff' into drzisga/multigrid 2019-10-09 13:25:45 -07:00
camierjs ad6559f673 Vector functions coeffs 2019-10-09 11:35:58 -07:00
Daniel Drzisga 53d0f3ab27 Added multigrid miniapp to makefile 2019-10-07 14:28:58 -07:00
Daniel Drzisga e2e244dd37 Removed templates from SpaceHierarchy 2019-10-07 14:23:29 -07:00
Daniel Drzisga 30dc561773 Added reference to Chebyshev accelerated smoother implementation 2019-10-07 09:25:24 -07:00
Daniel Drzisga 7a34a0a7f8 Added some documentation to transfer operator classes 2019-10-07 08:33:11 -07:00
Daniel Drzisga 0376eb004c Renamed AddCoarseLevel to AddCoarsestLevel in MultigridOperator 2019-10-07 07:58:27 -07:00
Daniel Drzisga 92c099e89f Renamed Eval to EvalPhysicalSpace in FunctionCoefficient and added documentation 2019-10-07 07:28:29 -07:00
Daniel Drzisga 215ae01687 Fixed serial build 2019-10-03 10:42:08 -07:00
Daniel Drzisga c6c8cbd72c Fixed bug when using FE spaces with encoded dofs 2019-10-03 09:49:44 -07:00
Daniel Drzisga a976bdec75 Changed transfer operator to support more FE spaces 2019-10-02 14:16:25 -07:00
Daniel Drzisga 23d8cc87e0 Fixed warning 2019-10-02 09:19:23 -07:00
Daniel Drzisga 49ca555842 Added parameter to enable jump in coefficient 2019-10-02 09:15:13 -07:00
Daniel Drzisga 7a7d231469 Added initial support for variable coefficients in diffusion PA 2019-10-02 09:13:55 -07:00
Daniel Drzisga a2bbea0bbe Formatting 2019-09-30 17:45:07 -07:00
Daniel Drzisga 2e74896f48 Added support for H1 vector FE spaces in matrix-free transfer operators 2019-09-30 17:42:36 -07:00
Daniel Drzisga 822e74c11d Merge remote-tracking branch 'origin/feature/okina-jacobi' into drzisga/multigrid 2019-09-30 16:28:18 -07:00
Daniel Drzisga 0555eb5577 More refactoring 2019-09-30 14:23:58 -07:00
Daniel Drzisga 6bdf9c8812 Formatting 2019-09-30 13:35:30 -07:00
Daniel Drzisga 992d2d7f51 Added option to use PCG on coarsest level 2019-09-30 13:21:32 -07:00
Daniel Drzisga d25705954f Separated multigrid related code into header and cpp 2019-09-30 11:06:51 -07:00
Daniel Drzisga a633b95e5d Added parameter for parallel refinements 2019-09-27 12:19:25 -07:00
Daniel Drzisga b23949851c Fixed bug where possibly uninitialized vector was used 2019-09-26 12:14:53 -07:00
Daniel Drzisga 2115637982 Reordered initialization of member variables 2019-09-26 12:12:11 -07:00
Daniel Drzisga 828a2cb677 Added more parameters for study 2019-09-24 11:29:01 -07:00
Daniel Drzisga 504bd65e13 Removed memory leaks 2019-09-24 08:47:43 -07:00
Daniel Drzisga fa5356ca20 Implemented comparison of different preconditioners 2019-09-23 18:16:36 -07:00
Daniel Drzisga 06e4103fdd Switched to using AMG without PCG on the coarse grid 2019-09-20 08:54:13 -07:00
Daniel Drzisga ee072014a5 Changed the preconditioner to LOR matrix 2019-09-18 15:41:44 -07:00
Daniel Drzisga c9d0e643f1 Added parameter to compare BoomerAMG to MultigridSolver 2019-09-18 13:40:06 -07:00
Daniel Drzisga a737c32318 Added more support for vector spaces 2019-09-18 13:38:59 -07:00
Daniel Drzisga c59aafa832 Created PoissonMultigridOperator class 2019-09-18 10:18:14 -07:00
Daniel Drzisga b3097c958d Changed residual computations to use square roots 2019-09-18 08:56:56 -07:00
Daniel Drzisga 24da00302f Got rid of SLI solver used for smoothing. 2019-09-18 08:47:06 -07:00
Daniel Drzisga 9b81686fd4 Got rid of unnecessary copies at finest level 2019-09-17 15:09:39 -07:00
Daniel Drzisga 19b5479921 Reformatted file 2019-09-17 14:26:15 -07:00
Daniel Drzisga 7b82d4b917 Implemented some convenience methods in MultigridSolver 2019-09-17 14:23:50 -07:00
Daniel Drzisga 578d44db47 Removed memory leaks 2019-09-17 13:38:56 -07:00
Daniel Drzisga 62ce484d54 Moved transfer operator to its own object 2019-09-17 10:40:30 -07:00
Daniel Drzisga 66a3060ca3 Added more options to CLI 2019-09-16 17:06:35 -07:00
Daniel Drzisga f4c724a977 Changed coarse solver to BoomerAMG and prepared support for variable smoothing steps 2019-09-16 16:45:31 -07:00
Daniel Drzisga e6749a0766 Switched to Chebyshev smoother when using partial assembly 2019-09-16 14:39:11 -07:00
Daniel Drzisga 79b7272908 Fixed some bug occuring in parallel where the local instead of the global dot product was used. 2019-09-16 14:00:31 -07:00
Daniel Drzisga 556671a491 Refactored classes and added initial support for global p refinements 2019-09-12 13:12:45 -07:00
Daniel Drzisga 9f4b7cab1a Fixed vector FE spaces in prolongation 2019-09-10 17:49:59 -07:00
Daniel Drzisga 90a3cd1c1b Removed timing in prolongations and restrictions 2019-09-10 16:30:53 -07:00
Daniel Drzisga a47e0b55c3 Reimplemented matrix-free geometric restriction with support for vector spaces 2019-09-10 16:27:31 -07:00
Daniel Drzisga 29ad446bc9 Rewrote RefinementOperator::Mult using subvectors 2019-09-10 15:03:42 -07:00
Daniel Drzisga 25137b3bf9 Switched from templated HPC operator to ordinary operators with PA 2019-09-10 12:50:28 -07:00
Daniel Drzisga d9dbf4fed2 Improved formatting of v-cycle iterations 2019-09-05 09:50:57 -07:00
Daniel Drzisga fc8076632f Switched prolongation/restriction operators to the matrix-free version. 2019-09-05 09:29:38 -07:00
Daniel Drzisga d24547de84 Added matrix-free geometric restriction operator by @barker29 2019-09-05 09:19:09 -07:00
Daniel Drzisga f5f193ba67 Added first version of matrix-free Chebyshev smoother. 2019-09-05 08:39:05 -07:00
Daniel Drzisga cd1dabb051 Added estimation of optimal weight for Jacobi method 2019-09-04 10:20:15 -07:00
Daniel Drzisga a8cc799e32 Added parallel support for multigrid 2019-08-30 11:59:54 -07:00
Daniel Drzisga d48bba1e0c Initial multigrid class with basic geometric multigrid solver using partially assembled operators. 2019-08-29 11:26:42 -07:00
Geoffrey M Oxberry ba72856e29 CONTRIBUTING: also update CI config files 2019-06-28 11:14:11 -07:00
Geoffrey M Oxberry c2aaa18df3 CONTRIBUTING: add update dependency versions tasks 2019-06-28 10:50:21 -07:00
camierjs 0e6665b1fd SurfaceMesh refactor wip 2019-06-11 15:55:33 -07:00
camierjs 1d5d42ea35 ex1 with star-surf mesh with PA and on GPU 2019-06-11 13:50:58 -07:00
camierjs 372f023056 fem/bilininteg_vdiffusion 2D action kernel 2019-06-11 11:26:39 -07:00
camierjs 7752769810 VectorDiffusionIntegrator assemble 2019-06-11 10:05:47 -07:00
camierjs 6dd44b8614 Diffusion kernel weight fix & cleanup 2019-06-10 12:22:43 -07:00
camierjs db6ac1970e Peach 1/4 && Cleanup 2019-06-09 21:23:11 -07:00
camierjs 4b26fa7574 Cleanup & SurfaceSolver class 2019-06-09 13:52:37 -07:00
camierjs bcce24f6f1 ex24 with PA 2019-06-08 19:30:59 -07:00
camierjs 6cc7aa1baf Add fem/bilininteg_vdiffusion.cpp 2019-06-07 17:34:40 -07:00
camierjs 3e6d242b03 WIP non-square J for sdim=3 and dim=2 2019-06-07 16:00:19 -07:00
camierjs 86f8591cc8 shell model 2019-06-07 13:15:55 -07:00
camierjs 9cfd2a670f Toward ex24 with pa: both scalar and vector method 2019-06-06 18:30:23 -07:00
camierjs d51a6c9667 Catenoid posfix and default mesh file 2019-06-05 21:46:10 -07:00
camierjs 7774fce406 Can work on input mesh again 2019-06-05 15:02:23 -07:00
camierjs fb77a95dff Remove data/cylinder.mesh 2019-06-04 21:04:25 -07:00
camierjs bf3c5555e2 Cleanup & remove old files 2019-06-04 21:02:43 -07:00
camierjs 242b533920 Scherk, Enneper, Catenoid & Helicoid 2019-06-04 18:10:02 -07:00
camierjs e7f0bcc56f ex24 in Vector style 2019-06-03 16:03:26 -07:00
camierjs 254cee7ebb Small warning fixes 2019-06-01 17:47:23 -07:00
camierjs 890f4f9d40 Add support w/o SUITESPARSE 2019-05-31 18:32:42 -07:00
Will Pazner 2b64de7f5a Clean up 2019-05-31 16:24:18 -07:00
Will Pazner f0cf4dd3cc First try at computing minimal surfaces 2019-05-31 16:06:53 -07:00
125 changed files with 15201 additions and 454 deletions
+14
View File
@@ -72,6 +72,10 @@ examples/deformed.*
examples/velocity.*
examples/elastic_energy.*
examples/mode_*
examples/ex5-p-*.bp
examples/ex9-p-*.bp
examples/ex12-p-*.bp
examples/ex16-p-*.bp
examples/ex16.mesh
examples/ex16-mesh.*
examples/ex16-init.*
@@ -165,6 +169,8 @@ miniapps/meshing/shaper
miniapps/meshing/extruder
miniapps/meshing/mesh-optimizer
miniapps/meshing/pmesh-optimizer
miniapps/meshing/minimal-surface
miniapps/meshing/pminimal-surface
miniapps/meshing/mobius-strip.mesh
miniapps/meshing/klein-bottle.mesh
@@ -225,6 +231,14 @@ miniapps/gslib/field-diff
miniapps/gslib/findpts
miniapps/gslib/pfindpts
miniapps/navier/navier_mms
miniapps/navier/navier_kovasznay
miniapps/navier/navier_tgv
miniapps/navier/navier_shear
miniapps/navier/navier_3dfoc
miniapps/navier/tgv_out*.txt
miniapps/navier/*_output
# Unit test binary and outputs
tests/unit/output_meshes
tests/unit/unit_tests
+41 -7
View File
@@ -23,19 +23,47 @@ Meshing improvements
Hessian for r-adaptivity using discrete fields, and allows use of skewness
and orientation based metrics.
Improved GPU capabilities
-------------------------
- Added support for Chebyshev accelerated polynomial smoother on GPU.
Discretization improvements
---------------------------
- Added support for matrix-free interpolation and restriction operators between
continuous H1 finite element spaces of different order on the same mesh or
with the same order on uniformly refined meshes.
- Added support for simplices in GSLIB-FindPoints.
Linear and nonlinear solvers
----------------------------
- Added power method to iteratively estimate the largest eigenvalue and the
corresponding eigenvector of an operator.
- Added initial support for h- and p-multigrid solvers and preconditioners for
matrix-based and matrix-free discretizations with basic GPU capability.
New and updated examples and miniapps
-------------------------------------
- Adding a simple meshing miniapp, Twist, which demonstrates MFEM's strategy of
stitching together opposite surfaces of a mesh to create a topologically
periodic mesh.
- Added a new example, Example 25/25p, to demonstrate the use of a Perfectly
Matched Layer (PML) for the simulation of electromagnetic wave propagation.
The example defines and solves several indefinite Maxwell problems.
Discretization improvements
---------------------------
- Added support for simplices in GSLIB-FindPoints.
- Added a new Example 26/26p to demonstrate the construction of a matrix-free
geometric and p-multigrid preconditioner for the Laplace problem.
- Added a new example, Example 27/27p, to demonstrate the enforcement of
various boundary conditions with the Laplace operator. The example shows the
procedures for applying Dirichlet, Neumann (both homogeneous and
inhomogeneous), Robin, and periodic boundary conditions with either H1 or DG
discretizations.
- Added a simple meshing miniapp, Twist, which demonstrates MFEM's strategy of
stitching together opposite surfaces of a mesh to create a topologically
periodic mesh.
- Added a new meshing miniapp, Minimal Surface, which solves Plateau's problem:
the Dirichlet problem for the minimal surface equation.
Improved testing
----------------
@@ -49,6 +77,12 @@ Miscellaneous
- In SLISolver, changed the residual inner product from (Br,r) to (Br,Br) so the
solver can work with non-SPD preconditioner B.
- Added support for ADIOS2 for parallel I/O with ParaView visualization. The
classes adios2stream and ADIOS2DataCollection are introduced in mfem as the
interfaces to generate ADIOS2 Binary Pack (BP4) directory datasets for the
entire spatial and temporal data. In addition, ADIOS2 allows for setting a
user-defined number of data substreams/subfiles. See examples 5, 9, 12, 16.
Version 4.1, released on March 10, 2020
=======================================
+6 -1
View File
@@ -323,6 +323,11 @@ if (MFEM_USE_UMPIRE)
find_package(UMPIRE REQUIRED)
endif()
# ADIOS2 for parallel I/O
if (MFEM_USE_ADIOS2)
find_package(ADIOS2 REQUIRED)
endif()
# MFEM_TIMER_TYPE
if (NOT DEFINED MFEM_TIMER_TYPE)
if (APPLE)
@@ -348,7 +353,7 @@ endif()
# be before SuiteSparse.
set(MFEM_TPLS MPI_CXX OPENMP BLAS LAPACK METIS HYPRE SuiteSparse SUNDIALS PETSC
MESQUITE SuperLUDist STRUMPACK AXOM CONDUIT Ginkgo GNUTLS GSLIB NETCDF
MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE)
MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE ADIOS2)
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
set(TPL_LIBRARIES "")
set(TPL_INCLUDE_DIRS "")
+9 -1
View File
@@ -383,9 +383,13 @@ Before a PR can be merged, it should satisfy the following:
- [ ] Is this a new feature users need to be aware of? New or updated example or miniapp?
- [ ] Does it make sense to create a new section in the `CHANGELOG` to group with other related features?
- [ ] Update `INSTALL`:
- [ ] Had a new optional library been added? (*Make sure the external library is compatible with our BSD license, e.g. it is not licensed under GPL!*)
- [ ] Had a new optional library been added? If so, what range of versions of this library are required? (*Make sure the external library is compatible with our BSD license, e.g. it is not licensed under GPL!*)
- [ ] Have the version ranges for any required or optional libraries changed?
- [ ] Does `make` or `cmake` have a new target?
- [ ] Did the requirements or the installation process change? *(rare)*
- [ ] Update continuous integration server configurations if necessary (e.g. with new version requirements for each of MFEM's dependencies)
- [ ] `.travis.yml`
- [ ] `.appveyor.yml`
- [ ] Update `.gitignore`:
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
- [ ] Add new patterns (just for the new files above) and re-run the above test.
@@ -499,6 +503,10 @@ MFEM uses a `master`/`next`-branch workflow as described below:
- [ ] `makefile`
- [ ] `CMakeLists.txt`
- [ ] `doc/CodeDocumentation.conf.in`
- [ ] Check that version requirements for each of MFEM's dependencies are documented in `INSTALL` and up-to-date
- [ ] Check that continuous integration server configurations reflect the dependency version requirements of the new release
- [ ] `.travis.yml`
- [ ] `.appveyor.yml`
- [ ] (LLNL only) Make sure all `README.html` files in the source repo are up to date.
- [ ] Tag the repository:
+26
View File
@@ -403,6 +403,11 @@ MFEM_USE_CONDUIT = YES/NO
an installation of Conduit. If Conduit was built with HDF5 support, it also
requires an installation of HDF5 (see also MFEM_USE_NETCDF).
MFEM_USE_ADIOS2 = YES/NO
Enables support for ADIOS2, version 2 of the adaptable input output system
for scientific data management. In MFEM, ADIOS2 provides parallel I/O with
ParaView visualization.
MFEM_USE_ZLIB = YES/NO
Enables use of on-the-fly gzip compressed streams. With this feature enabled
(YES), MFEM can compress its output files on-the-fly. In addition, it can
@@ -492,11 +497,13 @@ The specific libraries and their options are:
- HYPRE, required for the parallel build, i.e. when MFEM_USE_MPI = YES.
URL: https://github.com/hypre-space/hypre and https://www.llnl.gov/casc/hypre
Options: HYPRE_OPT, HYPRE_LIB.
Versions: HYPRE >= 2.10.0b.
- METIS, used when MFEM_USE_METIS = YES. If using METIS 5, set
MFEM_USE_METIS_5 = YES (default is to use METIS 4).
URL: http://glaros.dtc.umn.edu/gkhome/metis/metis/overview
Options: METIS_OPT, METIS_LIB.
Versions: METIS 4.0.3 or 5.1.0.
- LAPACK (optional), used when MFEM_USE_LAPACK = YES. Alternative, optimized
implementations can also be used, e.g. the ATLAS project.
@@ -520,6 +527,7 @@ The specific libraries and their options are:
both MPI and hypre.
URL: http://computation.llnl.gov/projects/sundials/sundials-software
Options: SUNDIALS_OPT, SUNDIALS_LIB.
Versions: SUNDIALS >= 5.0.0.
- Mesquite (optional), used when MFEM_USE_MESQUITE = YES.
URL: http://trilinos.org/oldsite/packages/mesquite
@@ -528,6 +536,7 @@ The specific libraries and their options are:
- SuiteSparse (optional), used when MFEM_USE_SUITESPARSE = YES.
URL: http://faculty.cse.tamu.edu/davis/suitesparse.html
Options: SUITESPARSE_OPT, SUITESPARSE_LIB.
Versions: SuiteSparse >= 4.5.4, older versions may work too.
- SuperLU_DIST (optional), used when MFEM_USE_SUPERLU = YES. Note that
SuperLU_DIST requires ParMETIS, which includes METIS 5 in its distribution.
@@ -535,6 +544,7 @@ The specific libraries and their options are:
same location.
URL: http://crd-legacy.lbl.gov/~xiaoye/SuperLU
Options: SUPERLU_OPT, SUPERLU_LIB.
Versions: SuperLU_DIST >= 5.1.0.
- STRUMPACK (optional), used when MFEM_USE_STRUMPACK = YES. Note that STRUMPACK
requires the PT-Scotch and Scalapack libraries as well as ParMETIS, which
@@ -544,6 +554,7 @@ The specific libraries and their options are:
2.0.0 or later.
URL: http://portal.nersc.gov/project/sparse/strumpack
Options: STRUMPACK_OPT, STRUMPACK_LIB.
Versions: STRUMPACK >= 3.0.0, requires HYPRE < 2.16.0.
- Ginkgo (optional), used when MFEM_USE_GINKGO = YES. Note that Ginkgo needs a
C++ compiler that supports the C++-11 standard. For additional requirements
@@ -556,6 +567,7 @@ The specific libraries and their options are:
one can get the library through the Homebrew package manager (http://brew.sh).
URL: http://gnutls.org
Options: GNUTLS_OPT, GNUTLS_LIB.
Versions: GnuTLS >= 2.12.0, older versions may work too.
- NetCDF (optional), used when MFEM_USE_NETCDF = YES, required for reading Cubit
mesh files. Also requires installation of HDF5 and ZLIB, as explained at the
@@ -563,6 +575,7 @@ The specific libraries and their options are:
don't need the C++ or parallel versions.
URL: www.unidata.ucar.edu/software/netcdf
Options: NETCDF_OPT, NETCDF_LIB.
Versions: NetCDF >= 4.4.0.
- PETSc (optional), used when MFEM_USE_PETSC = YES. Version 3.8 or higher of
the PETSC dev branch is required. The MFEM and PETSc builds can share common
@@ -574,6 +587,7 @@ The specific libraries and their options are:
--with-shared-libraries=0
URL: https://www.mcs.anl.gov/petsc
Options: PETSC_OPT, PETSC_LIB.
Versions: PETSc >= 3.8.0.
- Sidre (optional), part of LLNL's axom project, used when MFEM_USE_SIDRE = YES.
Starting with MFEM v4.1, Axom version 0.3.1 or later is required.
@@ -581,16 +595,22 @@ The specific libraries and their options are:
https://github.com/LLNL/conduit (Conduit)
https://support.hdfgroup.org/HDF5 (HDF5)
Options: SIDRE_OPT, SIDRE_LIB.
Versions: Axom >= 0.3.1.
- Conduit (optional), used when MFEM_USE_CONDUIT = YES. Conduit Mesh Blueprint
support requires Conduit >= v0.3.1 and VisIt >= v2.13.1 to read the output.
URL: https://github.com/LLNL/conduit (Conduit)
https://support.hdfgroup.org/HDF5 (HDF5)
Options: CONDUIT_OPT, CONDUIT_LIB.
Versions: Conduit >= 0.3.1.
- ADIOS2 (optional) used when MFEM_USE_ADIOS2 = YES.
URL: https://adios2.readthedocs.io/
- PUMI (optional), used when MFEM_USE_PUMI = YES.
URL: https://scorec.rpi.edu/pumi
Options: PUMI_OPT, PUMI_LIB.
Versions: PUMI >= 2.2.0.
- HiOp (optional), used when MFEM_USE_HIOP = YES.
URL: https://github.com/LLNL/hiop
@@ -604,10 +624,12 @@ The specific libraries and their options are:
MFEM_USE_GSLIB=YES.
URL: https://github.com/gslib/gslib/archive/v1.0.5.tar.gz
Options: GSLIB_OPT, GSLIB_LIB.
Versions: GSLIB >= 1.0.5.
- CUDA (optional), used when MFEM_USE_CUDA = YES.
URL: https://developer.nvidia.com/cuda-toolkit
Options: CUDA_CXX, CUDA_ARCH, CUDA_OPT, CUDA_LIB.
Versions: CUDA >= 9.1, older versions may work too.
- HIP (optional), used when MFEM_USE_HIP = YES.
URL: https://rocm.github.io/ROCmInstall.html
@@ -616,21 +638,25 @@ The specific libraries and their options are:
- OCCA (optional), used when MFEM_USE_OCCA = YES.
URL: https://libocca.org
Options: OCCA_DIR, OCCA_OPT, OCCA_LIB.
Versions: OCCA >= 1.0.9.
- libCEED (optional), used when MFEM_USE_CEED = YES. Requires libCEED v0.6
or later version, specifically, git-hash 3d05795 or later.
URL: https://github.com/CEED/libCEED
https://ceed.exascaleproject.org/libceed
Options: CEED_DIR, CEED_OPT, CEED_LIB.
Versions: libCEED >= 0.6.
- RAJA (optional), used when MFEM_USE_RAJA = YES.
Beginning with MFEM v4.1, only RAJA v0.10.0+ is supported.
URL: https://github.com/LLNL/RAJA
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
Versions: RAJA >= 0.10.0.
- Umpire, used when MFEM_USE_UMPIRE = YES.
URL: https://github.com/LLNL/Umpire
Options: UMPIRE_DIR, UMPIRE_OPT, UMPIRE_LIB.
Versions: Umpire >= 2.0.0.
- MPFR (optional), used when MFEM_USE_MPFR = YES.
URL: http://mpfr.org, it depends on the GMP library: https://gmplib.org
+1
View File
@@ -47,6 +47,7 @@ set(MFEM_USE_OCCA @MFEM_USE_OCCA@)
set(MFEM_USE_RAJA @MFEM_USE_RAJA@)
set(MFEM_USE_CEED @MFEM_USE_CEED@)
set(MFEM_USE_UMPIRE @MFEM_USE_UMPIRE@)
set(MFEM_USE_ADIOS2 @MFEM_USE_ADIOS2@)
set(MFEM_CXX_COMPILER "@CMAKE_CXX_COMPILER@")
set(MFEM_CXX_FLAGS "@CMAKE_CXX_FLAGS@")
+3
View File
@@ -132,6 +132,9 @@
// Enable MFEM functionality based on the Umpire library
#cmakedefine MFEM_USE_UMPIRE
// Enable MFEM functionality based on the ADIOS2 library
#cmakedefine MFEM_USE_ADIOS2
// Which library functions to use in class StopWatch for measuring time.
// For a list of the available options, see INSTALL.
// If not defined, an option is selected automatically.
+52
View File
@@ -0,0 +1,52 @@
#------------------------------------------------------------------------------#
# Distributed under the OSI-approved Apache License, Version 2.0. See
# accompanying file Copyright.txt for details.
#------------------------------------------------------------------------------#
#
# FindADIOS2
# -----------
#
# Try to find the ADIOS2 library
#
# This module defines the following variables:
#
# ADIOS2_FOUND - System has ADIOS2
# ADIOS2_INCLUDE_DIRS - The ADIOS2 include directory
# ADIOS2_LIBRARIES - Link these to use ADIOS2
#
# and the following imported targets:
# ADIOS2::ADIOS2 - The ADIOS2 compression library target
#
# You can also set the following variable to help guide the search:
# ADIOS2_DIR - The install prefix for ADIOS2 containing the
# include and lib folders
# Note: this can be set as a CMake variable or an
# environment variable. If specified as a CMake
# variable, it will override any setting specified
# as an environment variable.
if(NOT ADIOS2_FOUND)
if((NOT ADIOS2_DIR) AND (NOT (ENV{ADIOS2_DIR} STREQUAL "")))
set(ADIOS2_DIR "$ENV{ADIOS2_DIR}")
endif()
if(ADIOS2_DIR)
set(ADIOS2_INCLUDE_OPTS HINTS ${ADIOS2_DIR}/include NO_DEFAULT_PATHS)
set(ADIOS2_LIBRARY_OPTS
HINTS ${ADIOS2_DIR}/lib ${ADIOS2_DIR}/lib64
NO_DEFAULT_PATHS
)
endif()
find_path(ADIOS2_INCLUDE_DIR adios2.h ${ADIOS2_INCLUDE_OPTS})
find_library(ADIOS2_LIBRARY NAMES adios2 ${ADIOS2_LIBRARY_OPTS})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ADIOS2
FOUND_VAR ADIOS2_FOUND
REQUIRED_VARS ADIOS2_LIBRARY ADIOS2_INCLUDE_DIR
)
if(ADIOS2_FOUND)
set(ADIOS2_INCLUDE_DIRS ${ADIOS2_INCLUDE_DIR})
set(ADIOS2_LIBRARIES ${ADIOS2_LIBRARY})
endif()
endif()
+3
View File
@@ -147,6 +147,9 @@
// Enable functionality based on the Umpire library.
// #define MFEM_USE_UMPIRE
// Enable IO functionality based on the ADIOS2 library.
// #define MFEM_USE_ADIOS2
// Version of HYPRE used for building MFEM.
// #define MFEM_HYPRE_VERSION @MFEM_HYPRE_VERSION@
+1
View File
@@ -49,6 +49,7 @@ MFEM_USE_RAJA = @MFEM_USE_RAJA@
MFEM_USE_OCCA = @MFEM_USE_OCCA@
MFEM_USE_CEED = @MFEM_USE_CEED@
MFEM_USE_UMPIRE = @MFEM_USE_UMPIRE@
MFEM_USE_ADIOS2 = @MFEM_USE_ADIOS2@
# Compiler, compile options, and link options
MFEM_CXX = @MFEM_CXX@
+1
View File
@@ -49,6 +49,7 @@ option(MFEM_USE_OCCA "Enable OCCA" OFF)
option(MFEM_USE_RAJA "Enable RAJA" OFF)
option(MFEM_USE_CEED "Enable CEED" OFF)
option(MFEM_USE_UMPIRE "Enable Umpire" OFF)
option(MFEM_USE_ADIOS2 "Enable ADIOS2" OFF)
set(MFEM_MPI_NP 4 CACHE STRING "Number of processes used for MPI tests")
+1
View File
@@ -137,6 +137,7 @@ MFEM_USE_RAJA = NO
MFEM_USE_OCCA = NO
MFEM_USE_CEED = NO
MFEM_USE_UMPIRE = NO
MFEM_USE_ADIOS2 = NO
# Compile and link options for zlib.
ZLIB_DIR =
+3 -3
View File
@@ -47,7 +47,7 @@ groups_serial=(
"Meshing miniapps:"
"miniapps/meshing"
"mobius-strip.cpp klein-bottle.cpp extruder.cpp toroid.cpp
mesh-optimizer.cpp"'
mesh-optimizer.cpp minimal-surface.cpp"'
)
# Parallel groups
groups_parallel=(
@@ -72,7 +72,7 @@ groups_parallel=(
'"meshing"
"Meshing miniapps:"
"miniapps/meshing"
"pmesh-optimizer.cpp"'
"pmesh-optimizer.cpp pminimal-surface.cpp"'
'"electromagnetics"
"Electromagnetics miniapps:"
"miniapps/electromagnetics"
@@ -101,7 +101,7 @@ groups_all=(
"Meshing miniapps:"
"miniapps/meshing"
"mobius-strip.cpp klein-bottle.cpp extruder.cpp toroid.cpp
{,p}mesh-optimizer.cpp"'
{,p}mesh-optimizer.cpp {,p}minimal-surface.cpp"'
'"electromagnetics"
"Electromagnetics miniapps:"
"miniapps/electromagnetics"
+1
View File
@@ -774,6 +774,7 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
@MFEM_SOURCE_DIR@/miniapps/gslib \
@MFEM_SOURCE_DIR@/miniapps/meshing \
@MFEM_SOURCE_DIR@/miniapps/navier \
@MFEM_SOURCE_DIR@/miniapps/nurbs \
@MFEM_SOURCE_DIR@/miniapps/performance \
@MFEM_SOURCE_DIR@/miniapps/tools \
+4
View File
@@ -88,6 +88,8 @@ namespace mfem {
* - <a class="el" href="ex24p_8cpp_source.html">Example 24p</a>: parallel mixed finite element spaces and interpolators
* - <a class="el" href="ex25_8cpp_source.html">Example 25</a>: simulation of electromagnetic wave propagation using a Perfectly Matched Layer (PML)
* - <a class="el" href="ex25p_8cpp_source.html">Example 25p</a>: parallel simulation of electromagnetic wave propagation using a Perfectly Matched Layer (PML)
* - <a class="el" href="ex26_8cpp_source.html">Example 26</a>: multigrid preconditioner for the Laplace problem using nodal H1 FEM
* - <a class="el" href="ex26p_8cpp_source.html">Example 26p</a>: parallel multigrid preconditioner for the Laplace problem using nodal H1 FEM
*
* <H4>SUNDIALS Examples</H4>
* - Variants of Examples
@@ -142,6 +144,7 @@ namespace mfem {
* - <a class="el" href="klein-bottle_8cpp_source.html">Klein Bottle</a>: generate three types of Klein bottle surfaces
* - <a class="el" href="toroid_8cpp_source.html">Toroid</a>: generate simple toroidal meshes
* - <a class="el" href="twist_8cpp_source.html">Twist</a>: generate simple periodic meshes
* - <a class="el" href="minimal-surface_8cpp_source.html">Minimal Surface</a>: compute minimal surfaces, <a class="el" href="minimal-surface_8cpp_source.html">serial</a> and <a class="el" href="pminimal-surface_8cpp_source.html">parallel</a> versions
* - <a class="el" href="shaper_8cpp_source.html">Shaper</a>: resolve material interfaces by mesh refinement
* - <a class="el" href="extruder_8cpp_source.html">Extruder</a>: extrude a low-dimensional mesh into a higher dimension
* - <a class="el" href="mesh-explorer_8cpp_source.html">Mesh Explorer</a>: visualize and manipulate meshes
@@ -153,6 +156,7 @@ namespace mfem {
* - <a class="el" href="lor-transfer_8cpp_source.html">LOR Transfer</a>: map functions between high-order and low-order refined spaces
* - <a class="el" href="findpts_8cpp_source.html">Find Points</a>: evaluate grid function in physical space, <a class="el" href="findpts_8cpp_source.html">serial</a> and <a class="el" href="pfindpts_8cpp_source.html">parallel</a> versions
* - <a class="el" href="field-diff_8cpp_source.html">Field Diff</a>: compare grid functions on different meshes
* - <a class="el" href="classmfem_1_1navier_1_1NavierSolver.html">Navier</a>: solve the transient incompressible Navier-Stokes equations
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Laplace problem
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Laplace problem
*
+8
View File
@@ -32,6 +32,8 @@ list(APPEND ALL_EXE_SRCS
ex23.cpp
ex24.cpp
ex25.cpp
ex26.cpp
ex27.cpp
)
if (MFEM_USE_MPI)
@@ -60,6 +62,10 @@ if (MFEM_USE_MPI)
ex22p.cpp
ex24p.cpp
ex25p.cpp
ex26p.cpp
ex27p.cpp
pa_oper.cpp
io_benchmark.cpp
)
endif()
@@ -79,6 +85,8 @@ foreach(SRC_FILE ${ALL_EXE_SRCS})
list(APPEND THIS_TEST_OPTIONS "-tf" "5")
elseif(${TEST_NAME} MATCHES "ex15p*")
list(APPEND THIS_TEST_OPTIONS "-e" "1")
elseif(${TEST_NAME} MATCHES "ex27p*")
list(APPEND THIS_TEST_OPTIONS "-dg")
endif()
if (NOT (${TEST_NAME} MATCHES ".*p$"))
+29 -3
View File
@@ -33,7 +33,8 @@
// The example highlights the use of the LOBPCG eigenvalue solver
// together with the BoomerAMG preconditioner in HYPRE. Reusing a
// single GLVis visualization window for multiple eigenfunctions
// is also illustrated.
// and optional saving with ADIOS2 (adios2.readthedocs.io) streams
// are also illustrated.
//
// We recommend viewing examples 2 and 11 before viewing this
// example.
@@ -60,6 +61,7 @@ int main(int argc, char *argv[])
int seed = 66;
bool visualization = 1;
bool amg_elast = 0;
bool adios2 = false;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -77,6 +79,9 @@ int main(int argc, char *argv[])
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&adios2, "-adios2", "--adios2-streams", "-no-adios2",
"--no-adios2-streams",
"Save data using adios2 streams.");
args.Parse();
if (!args.Good())
{
@@ -286,7 +291,28 @@ int main(int argc, char *argv[])
}
}
// 13. Send the above data by socket to a GLVis server. Use the "n" and "b"
// 13. Optionally output a BP (binary pack) file using ADIOS2. This can be
// visualized with the ParaView VTX reader.
#ifdef MFEM_USE_ADIOS2
if (adios2)
{
std::string postfix(mesh_file);
postfix.erase(0, std::string("../data/").size() );
postfix += "_o" + std::to_string(order);
adios2stream adios2output("ex12-p-" + postfix + ".bp",
adios2stream::openmode::out, MPI_COMM_WORLD);
pmesh->Print(adios2output);
for (int i=0; i<nev; i++)
{
x = lobpcg->GetEigenvector(i);
// x is a temporary that must be saved immediately
x.Save(adios2output, "mode_" + std::to_string(i));
}
}
#endif
// 14. Send the above data by socket to a GLVis server. Use the "n" and "b"
// keys in GLVis to visualize the displacements.
if (visualization)
{
@@ -326,7 +352,7 @@ int main(int argc, char *argv[])
mode_sock.close();
}
// 14. Free the used memory.
// 15. Free the used memory.
delete lobpcg;
delete amg;
delete M;
+43 -1
View File
@@ -24,7 +24,8 @@
// class ConductionOperator defining C(u)), as well as their
// implicit time integration. Note that implementing the method
// ConductionOperator::ImplicitSolve is the only requirement for
// high-order implicit (SDIRK) time integration.
// high-order implicit (SDIRK) time integration. Optional saving
// with ADIOS2 (adios2.readthedocs.io) is also illustrated.
//
// We recommend viewing examples 2, 9 and 10 before viewing this
// example.
@@ -108,6 +109,7 @@ int main(int argc, char *argv[])
bool visualization = true;
bool visit = false;
int vis_steps = 5;
bool adios2 = false;
int precision = 8;
cout.precision(precision);
@@ -140,6 +142,9 @@ int main(int argc, char *argv[])
"Save data files for VisIt (visit.llnl.gov) visualization.");
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
"Visualize every n-th timestep.");
args.AddOption(&adios2, "-adios2", "--adios2-streams", "-no-adios2",
"--no-adios2-streams",
"Save data using adios2 streams.");
args.Parse();
if (!args.Good())
{
@@ -248,6 +253,27 @@ int main(int argc, char *argv[])
visit_dc.Save();
}
// Optionally output a BP (binary pack) file using ADIOS2. This can be
// visualized with the ParaView VTX reader.
#ifdef MFEM_USE_ADIOS2
ADIOS2DataCollection* adios2_dc = NULL;
if (adios2)
{
std::string postfix(mesh_file);
postfix.erase(0, std::string("../data/").size() );
postfix += "_o" + std::to_string(order);
postfix += "_solver" + std::to_string(ode_solver_type);
const std::string collection_name = "ex16-p-" + postfix + ".bp";
adios2_dc = new ADIOS2DataCollection(MPI_COMM_WORLD, collection_name, pmesh);
adios2_dc->SetParameter("SubStreams", std::to_string(num_procs/2) );
adios2_dc->RegisterField("temperature", &u_gf);
adios2_dc->SetCycle(0);
adios2_dc->SetTime(0.0);
adios2_dc->Save();
}
#endif
socketstream sout;
if (visualization)
{
@@ -317,10 +343,26 @@ int main(int argc, char *argv[])
visit_dc.SetTime(t);
visit_dc.Save();
}
#ifdef MFEM_USE_ADIOS2
if (adios2)
{
adios2_dc->SetCycle(ti);
adios2_dc->SetTime(t);
adios2_dc->Save();
}
#endif
}
oper.SetParameters(u);
}
#ifdef MFEM_USE_ADIOS2
if (adios2)
{
delete adios2_dc;
}
#endif
// 11. Save the final solution in parallel. This output can be viewed later
// using GLVis: "glvis -np <np> -m ex16-mesh -g ex16-final".
{
+41 -7
View File
@@ -70,6 +70,7 @@ int main(int argc, char *argv[])
bool pa = false;
const char *device_config = "cpu";
bool visualization = true;
int nfiles = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -86,6 +87,7 @@ int main(int argc, char *argv[])
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&nfiles, "-nf", "--num-files", "Number of files to write.");
args.Parse();
if (!args.Good())
{
@@ -158,7 +160,7 @@ int main(int argc, char *argv[])
{
fec = new H1_FECollection(order = 1, dim);
}
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec, 1, 0);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
@@ -237,20 +239,52 @@ int main(int argc, char *argv[])
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
std::string filename("nranks_");
filename += to_string(num_procs);
filename += ".gf";
{
double t1;
t1 = MPI_Wtime();
x.Save(filename.c_str(), nfiles);
double t2 = MPI_Wtime();
if (myid == 0)
{
err << "elapsed write time: " << t2 - t1 << endl;
}
}
{
double t1;
t1 = MPI_Wtime();
ParGridFunction new_x(fespace, filename.c_str());
double t2 = MPI_Wtime();
if (myid == 0)
{
err << "elapsed read time: " << t2 - t1 << endl;
}
// new_x -= x;
// out << "GF difference: " << new_x.Norml1() << endl;
}
// 15. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
//mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << num_procs << setfill('0') << setw(6) << myid;
//ofstream mesh_ofs(mesh_name.str().c_str());
//mesh_ofs.precision(8);
//pmesh->Print(mesh_ofs);
double t1 = MPI_Wtime();
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
x.Save(sol_ofs);
double t2 = MPI_Wtime();
if (myid == 0)
{
err << t2 - t1 << endl;
}
}
// 16. Send the solution by socket to a GLVis server.
+255
View File
@@ -0,0 +1,255 @@
// MFEM Example 26
//
// Compile with: make ex26
//
// Sample runs: ex26 -m ../data/star.mesh
// ex26 -m ../data/fichera.mesh
// ex26 -m ../data/beam-hex.mesh
//
// Device sample runs:
// ex26 -d cuda
// ex26 -d raja-cuda
// ex26 -d occa-cuda
// ex26 -d raja-omp
// ex26 -d occa-omp
// ex26 -d ceed-cpu
// ex26 -d ceed-cuda
// ex26 -m ../data/beam-hex.mesh -d cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions
// as in Example 1.
//
// It highlights on the creation of a hierarchy of discretization
// spaces with partial assembly and the construction of an
// efficient multigrid preconditioner for the iterative solver.
//
// We recommend viewing Example 1 before viewing this example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Class for constructing a multigrid preconditioner for the diffusion operator.
// This example multigrid preconditioner class demonstrates the creation of the
// diffusion bilinear forms and operators using partial assembly for all spaces
// in the FiniteElementSpaceHierarchy. The preconditioner uses a CG solver on
// the coarsest level and second order Chebyshev accelerated smoothers on the
// other levels.
class DiffusionMultigrid : public Multigrid
{
private:
ConstantCoefficient one;
public:
// Constructs a diffusion multigrid for the given FiniteElementSpaceHierarchy
// and the array of essential boundaries
DiffusionMultigrid(FiniteElementSpaceHierarchy& fespaces, Array<int>& ess_bdr)
: Multigrid(fespaces), one(1.0)
{
ConstructCoarseOperatorAndSolver(fespaces.GetFESpaceAtLevel(0), ess_bdr);
for (int level = 1; level < fespaces.GetNumLevels(); ++level)
{
ConstructOperatorAndSmoother(fespaces.GetFESpaceAtLevel(level), ess_bdr);
}
}
private:
void ConstructBilinearForm(FiniteElementSpace& fespace, Array<int>& ess_bdr)
{
BilinearForm* form = new BilinearForm(&fespace);
form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
form->AddDomainIntegrator(new DiffusionIntegrator(one));
form->Assemble();
bfs.Append(form);
essentialTrueDofs.Append(new Array<int>());
fespace.GetEssentialTrueDofs(ess_bdr, *essentialTrueDofs.Last());
}
void ConstructCoarseOperatorAndSolver(FiniteElementSpace& coarse_fespace,
Array<int>& ess_bdr)
{
ConstructBilinearForm(coarse_fespace, ess_bdr);
OperatorPtr opr;
opr.SetType(Operator::ANY_TYPE);
bfs.Last()->FormSystemMatrix(*essentialTrueDofs.Last(), opr);
opr.SetOperatorOwner(false);
CGSolver* pcg = new CGSolver();
pcg->SetPrintLevel(-1);
pcg->SetMaxIter(200);
pcg->SetRelTol(sqrt(1e-4));
pcg->SetAbsTol(0.0);
pcg->SetOperator(*opr.Ptr());
AddLevel(opr.Ptr(), pcg, true, true);
}
void ConstructOperatorAndSmoother(FiniteElementSpace& fespace,
Array<int>& ess_bdr)
{
ConstructBilinearForm(fespace, ess_bdr);
OperatorPtr opr;
opr.SetType(Operator::ANY_TYPE);
bfs.Last()->FormSystemMatrix(*essentialTrueDofs.Last(), opr);
opr.SetOperatorOwner(false);
Vector diag(fespace.GetTrueVSize());
bfs.Last()->AssembleDiagonal(diag);
Solver* smoother = new OperatorChebyshevSmoother(opr.Ptr(), diag,
*essentialTrueDofs.Last(), 2);
AddLevel(opr.Ptr(), smoother, true, true);
}
};
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
int geometric_refinements = 0;
int order_refinements = 2;
const char *device_config = "cpu";
bool visualization = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&geometric_refinements, "-gr", "--geometric-refinements",
"Number of geometric refinements done prior to order refinements.");
args.AddOption(&order_refinements, "-or", "--order-refinements",
"Number of order refinements. Finest level in the hierarchy has order 2^{or}.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
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);
// 2. 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);
device.Print();
// 3. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
// the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Refine the mesh 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 50,000
// elements.
{
int ref_levels =
(int)floor(log(5000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 5. Define a finite element space hierarchy on the mesh. Here we use
// continuous Lagrange finite elements. We start with order 1 on the
// coarse level and geometrically refine the spaces by the specified
// amount. Afterwards, we increase the order of the finite elements
// by a factor of 2 for each additional level.
FiniteElementCollection *fec = new H1_FECollection(1, dim);
FiniteElementSpace *coarse_fespace = new FiniteElementSpace(mesh, fec);
FiniteElementSpaceHierarchy fespaces(mesh, coarse_fespace, true, true);
Array<FiniteElementCollection*> collections;
collections.Append(fec);
for (int level = 0; level < geometric_refinements; ++level)
{
fespaces.AddUniformlyRefinedLevel();
}
for (int level = 0; level < order_refinements; ++level)
{
collections.Append(new H1_FECollection(std::pow(2, level+1), dim));
fespaces.AddOrderRefinedLevel(collections.Last());
}
cout << "Number of finite element unknowns: "
<< fespaces.GetFinestFESpace().GetTrueVSize() << endl;
// 6. Set up the linear form b(.) which corresponds to the right-hand side of
// the FEM linear system, which in this case is (1,phi_i) where phi_i are
// the basis functions in the finite element fespace.
LinearForm *b = new LinearForm(&fespaces.GetFinestFESpace());
ConstantCoefficient one(1.0);
b->AddDomainIntegrator(new DomainLFIntegrator(one));
b->Assemble();
// 7. Define the solution vector x as a finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
GridFunction x(&fespaces.GetFinestFESpace());
x = 0.0;
// 8. Create the multigrid operator using the previously created
// FiniteElementSpaceHierarchy and additional boundary information. This operator
// is then used to create the MultigridSolver as a preconditioner in the
// iterative solver.
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
DiffusionMultigrid M(fespaces, ess_bdr);
M.SetCycleType(Multigrid::CycleType::VCYCLE, 1, 1);
OperatorPtr A;
Vector B, X;
M.FormFineLinearSystem(x, *b, A, X, B);
cout << "Size of linear system: " << A->Height() << endl;
// 9. Solve the linear system A X = B.
PCG(*A, M, B, X, 1, 2000, 1e-12, 0.0);
// 10. Recover the solution as a finite element grid function.
M.RecoverFineFEMSolution(X, *b, x);
// 11. Save the refined mesh and the solution. This output can be viewed later
// using GLVis: "glvis -m refined.mesh -g sol.gf".
ofstream mesh_ofs("refined.mesh");
mesh_ofs.precision(8);
fespaces.GetFinestFESpace().GetMesh()->Print(mesh_ofs);
ofstream sol_ofs("sol.gf");
sol_ofs.precision(8);
x.Save(sol_ofs);
// 12. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << *fespaces.GetFinestFESpace().GetMesh() << x <<
flush;
}
// 13. Free the used memory.
delete b;
for (int level = 0; level < collections.Size(); ++level)
{
delete collections[level];
}
return 0;
}
+317
View File
@@ -0,0 +1,317 @@
// MFEM Example 26 - Parallel Version
//
// Compile with: make ex26p
//
// Sample runs: mpirun -np 4 ex26p -m ../data/star.mesh
// mpirun -np 4 ex26p -m ../data/fichera.mesh
// mpirun -np 4 ex26p -m ../data/beam-hex.mesh
//
// Device sample runs:
// mpirun -np 4 ex26p -d cuda
// mpirun -np 4 ex26p -d occa-cuda
// mpirun -np 4 ex26p -d raja-omp
// mpirun -np 4 ex26p -d ceed-cpu
// mpirun -np 4 ex26p -d ceed-cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions
// as in Example 1.
//
// It highlights on the creation of a hierarchy of discretization
// spaces with partial assembly and the construction of an
// efficient multigrid preconditioner for the iterative solver.
//
// We recommend viewing Example 1 before viewing this example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Class for constructing a multigrid preconditioner for the diffusion operator.
// This example multigrid preconditioner class demonstrates the creation of the
// parallel diffusion bilinear forms and operators using partial assembly for
// all spaces except the coarsest one in the ParFiniteElementSpaceHierarchy.
// The multigrid uses a PCG solver preconditioned with AMG on the coarsest level
// and second order Chebyshev accelerated smoothers on the other levels.
class DiffusionMultigrid : public Multigrid
{
private:
ConstantCoefficient one;
HypreBoomerAMG* amg;
public:
// Constructs a diffusion multigrid for the ParFiniteElementSpaceHierarchy
// and the array of essential boundaries
DiffusionMultigrid(ParFiniteElementSpaceHierarchy& fespaces,
Array<int>& ess_bdr)
: Multigrid(fespaces), one(1.0)
{
ConstructCoarseOperatorAndSolver(fespaces.GetFESpaceAtLevel(0), ess_bdr);
for (int level = 1; level < fespaces.GetNumLevels(); ++level)
{
ConstructOperatorAndSmoother(fespaces.GetFESpaceAtLevel(level), ess_bdr);
}
}
virtual ~DiffusionMultigrid()
{
delete amg;
}
private:
void ConstructBilinearForm(ParFiniteElementSpace& fespace, Array<int>& ess_bdr,
bool partial_assembly)
{
ParBilinearForm* form = new ParBilinearForm(&fespace);
if (partial_assembly)
{
form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
form->AddDomainIntegrator(new DiffusionIntegrator(one));
form->Assemble();
bfs.Append(form);
essentialTrueDofs.Append(new Array<int>());
fespace.GetEssentialTrueDofs(ess_bdr, *essentialTrueDofs.Last());
}
void ConstructCoarseOperatorAndSolver(ParFiniteElementSpace& coarse_fespace,
Array<int>& ess_bdr)
{
ConstructBilinearForm(coarse_fespace, ess_bdr, false);
HypreParMatrix* hypreCoarseMat = new HypreParMatrix();
bfs.Last()->FormSystemMatrix(*essentialTrueDofs.Last(), *hypreCoarseMat);
amg = new HypreBoomerAMG(*hypreCoarseMat);
amg->SetPrintLevel(-1);
CGSolver* pcg = new CGSolver(MPI_COMM_WORLD);
pcg->SetPrintLevel(-1);
pcg->SetMaxIter(10);
pcg->SetRelTol(sqrt(1e-4));
pcg->SetAbsTol(0.0);
pcg->SetOperator(*hypreCoarseMat);
pcg->SetPreconditioner(*amg);
AddLevel(hypreCoarseMat, pcg, true, true);
}
void ConstructOperatorAndSmoother(ParFiniteElementSpace& fespace,
Array<int>& ess_bdr)
{
ConstructBilinearForm(fespace, ess_bdr, true);
OperatorPtr opr;
opr.SetType(Operator::ANY_TYPE);
bfs.Last()->FormSystemMatrix(*essentialTrueDofs.Last(), opr);
opr.SetOperatorOwner(false);
Vector diag(fespace.GetTrueVSize());
bfs.Last()->AssembleDiagonal(diag);
Solver* smoother = new OperatorChebyshevSmoother(opr.Ptr(), diag,
*essentialTrueDofs.Last(), 2, fespace.GetParMesh()->GetComm());
AddLevel(opr.Ptr(), smoother, true, true);
}
};
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
int geometric_refinements = 0;
int order_refinements = 2;
const char *device_config = "cpu";
bool visualization = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&geometric_refinements, "-gr", "--geometric-refinements",
"Number of geometric refinements done prior to order refinements.");
args.AddOption(&order_refinements, "-or", "--order-refinements",
"Number of order refinements. Finest level in the hierarchy has order 2^{or}.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
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);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// 3. 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 (myid == 0) { device.Print(); }
// 4. 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 = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 5. 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 1,000 elements.
{
int ref_levels =
(int)floor(log(1000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 6. 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 = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
}
// 7. Define a parallel finite element space hierarchy on the parallel mesh.
// Here we use continuous Lagrange finite elements. We start with order 1
// on the coarse level and geometrically refine the spaces by the specified
// amount. Afterwards, we increase the order of the finite elements by a
// factor of 2 for each additional level.
FiniteElementCollection *fec = new H1_FECollection(1, dim);
ParFiniteElementSpace *coarse_fespace = new ParFiniteElementSpace(pmesh, fec);
Array<FiniteElementCollection*> collections;
collections.Append(fec);
ParFiniteElementSpaceHierarchy* fespaces = new ParFiniteElementSpaceHierarchy(
pmesh, coarse_fespace, true, true);
for (int level = 0; level < geometric_refinements; ++level)
{
fespaces->AddUniformlyRefinedLevel();
}
for (int level = 0; level < order_refinements; ++level)
{
collections.Append(new H1_FECollection(std::pow(2, level+1), dim));
fespaces->AddOrderRefinedLevel(collections.Last());
}
HYPRE_Int size = fespaces->GetFinestFESpace().GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 8. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (1,phi_i) where phi_i are the basis functions in fespace.
ParLinearForm *b = new ParLinearForm(&fespaces->GetFinestFESpace());
ConstantCoefficient one(1.0);
b->AddDomainIntegrator(new DomainLFIntegrator(one));
b->Assemble();
// 9. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
ParGridFunction x(&fespaces->GetFinestFESpace());
x = 0.0;
// 10. Create the multigrid operator using the previously created parallel
// FiniteElementSpaceHierarchy and additional boundary information. This operator
// is then used to create the MultigridSolver as a preconditioner in the
// iterative solver.
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
if (pmesh->bdr_attributes.Size())
{
ess_bdr = 1;
}
DiffusionMultigrid* M = new DiffusionMultigrid(*fespaces, ess_bdr);
M->SetCycleType(Multigrid::CycleType::VCYCLE, 1, 1);
OperatorPtr A;
Vector X, B;
M->FormFineLinearSystem(x, *b, A, X, B);
// 11. Solve the linear system A X = B.
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(1);
cg.SetOperator(*A);
cg.SetPreconditioner(*M);
cg.Mult(B, X);
// 12. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
M->RecoverFineFEMSolution(X, *b, x);
// 13. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
fespaces->GetFinestFESpace().GetParMesh()->Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
x.Save(sol_ofs);
}
// 14. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << *fespaces->GetFinestFESpace().GetParMesh()
<< x << flush;
}
// 15. Free the used memory.
delete M;
delete b;
delete fespaces;
for (int level = 0; level < collections.Size(); ++level)
{
delete collections[level];
}
MPI_Finalize();
return 0;
}
+736
View File
@@ -0,0 +1,736 @@
// MFEM Example 27 - Serial Version
//
// Compile with: make ex27
//
// Sample runs: ex27
// ex27 -dg
// ex27 -dg -dbc 8 -nbc -2
// ex27 -rbc-a 1 -rbc-b 8
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// -Delta u = 0 with a variety of boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order using a continuous or discontinuous space. We then
// apply Dirichlet, Neumann (both homogeneous and inhomogeneous),
// Robin, and Periodic boundary conditions on different portions
// of a predefined mesh.
//
// The predefined mesh consists of a rectangle with two
// holes removed (see below). The narrow ends of the
// mesh are connected to form a Periodic boundary
// condition. The lower edge (tagged with attribute 1)
// receives an inhomogeneous Neumann boundary condition.
// A Robin boundary condition is applied to upper edge
// (attribute 2). The circular hole on the left
// (attribute 3) enforces a Dirichlet boundary
// condition. Finally, a natural boundary condition, or
// homogeneous Neumann BC, is applied to the circular
// hole on the right (attribute 4).
//
// Attribute 3 ^ y Attribute 2
// \ | /
// +-----------+-----------+
// | \_ | _ |
// | / \ | / \ |
// <--+---+---+---+---+---+---+--> x
// | \_/ | \_/ |
// | | \ |
// +-----------+-----------+ (hole radii are
// / | \ adjustable)
// Attribute 1 v Attribute 4
//
// The boundary conditions are defined as (where u is
// the solution field):
// Dirichlet: u = d
// Neumann: n.Grad(u) = g
// Robin: n.Grad(u) + a u = b
//
// The user can adjust the values of 'd', 'g', 'a', and
// 'b' with command line options.
//
// This example highlights the differing implementations of
// boundary conditions with continuous and discontinuous Galerkin
// formulations of the Laplace problem.
//
// We recommend viewing examples 1 and 14 before viewing this
// example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
static double a_ = 0.2;
// Normal to hole with boundary attribute 4
void n4Vec(const Vector &x, Vector &n) { n = x; n[0] -= 0.5; n /= -n.Norml2(); }
Mesh * GenerateSerialMesh(int ref);
// Compute the average value of alpha*n.Grad(sol) + beta*sol over the boundary
// attributes marked in bdr_marker. Also computes the L2 norm of
// alpha*n.Grad(sol) + beta*sol - gamma over the same boundary.
double IntegrateBC(const GridFunction &sol, const Array<int> &bdr_marker,
double alpha, double beta, double gamma,
double &err);
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
int ser_ref_levels = 2;
int order = 1;
double sigma = -1.0;
double kappa = -1.0;
bool h1 = true;
bool visualization = true;
double mat_val = 1.0;
double dbc_val = 0.0;
double nbc_val = 1.0;
double rbc_a_val = 1.0; // du/dn + a * u = b
double rbc_b_val = 1.0;
OptionsParser args(argc, argv);
args.AddOption(&h1, "-h1", "--continuous", "-dg", "--discontinuous",
"Select continuous \"H1\" or discontinuous \"DG\" basis.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&sigma, "-s", "--sigma",
"One of the two DG penalty parameters, typically +1/-1."
" See the documentation of class DGDiffusionIntegrator.");
args.AddOption(&kappa, "-k", "--kappa",
"One of the two DG penalty parameters, should be positive."
" Negative values are replaced with (order+1)^2.");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&mat_val, "-mat", "--material-value",
"Constant value for material coefficient "
"in the Laplace operator.");
args.AddOption(&dbc_val, "-dbc", "--dirichlet-value",
"Constant value for Dirichlet Boundary Condition.");
args.AddOption(&nbc_val, "-nbc", "--neumann-value",
"Constant value for Neumann Boundary Condition.");
args.AddOption(&rbc_a_val, "-rbc-a", "--robin-a-value",
"Constant 'a' value for Robin Boundary Condition: "
"du/dn + a * u = b.");
args.AddOption(&rbc_b_val, "-rbc-b", "--robin-b-value",
"Constant 'b' value for Robin Boundary Condition: "
"du/dn + a * u = b.");
args.AddOption(&a_, "-a", "--radius",
"Radius of holes in the mesh.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(mfem::out);
return 1;
}
if (kappa < 0 && !h1)
{
kappa = (order+1)*(order+1);
}
args.PrintOptions(mfem::out);
if (a_ < 0.01)
{
mfem::out << "Hole radius too small, resetting to 0.01.\n";
a_ = 0.01;
}
if (a_ > 0.49)
{
mfem::out << "Hole radius too large, resetting to 0.49.\n";
a_ = 0.49;
}
// 2. Construct the (serial) mesh and refine it if requested.
Mesh *mesh = GenerateSerialMesh(ser_ref_levels);
int dim = mesh->Dimension();
// 3. Define a finite element space on the serial mesh. Here we
// use either continuous Lagrange finite elements or discontinuous
// Galerkin finite elements of the specified order.
FiniteElementCollection *fec =
h1 ? (FiniteElementCollection*)new H1_FECollection(order, dim) :
(FiniteElementCollection*)new DG_FECollection(order, dim);
FiniteElementSpace fespace(mesh, fec);
int size = fespace.GetTrueVSize();
mfem::out << "Number of finite element unknowns: " << size << endl;
// 4. Create "marker arrays" to define the portions of the boundary
// associated with each type of boundary condition. These arrays
// have an entry corresponding to each boundary attribute.
// Placing a '1' in entry i marks attribute i+1 as being
// active, '0' is inactive.
Array<int> nbc_bdr(mesh->bdr_attributes.Max());
Array<int> rbc_bdr(mesh->bdr_attributes.Max());
Array<int> dbc_bdr(mesh->bdr_attributes.Max());
nbc_bdr = 0; nbc_bdr[0] = 1;
rbc_bdr = 0; rbc_bdr[1] = 1;
dbc_bdr = 0; dbc_bdr[2] = 1;
Array<int> ess_tdof_list(0);
if (h1 && mesh->bdr_attributes.Size())
{
// For a continuous basis the linear system must be modifed to enforce
// an essential (Dirichlet) boundary condition. In the DG case this is
// not necessary as the boundary condition will only be enforced weakly.
fespace.GetEssentialTrueDofs(dbc_bdr, ess_tdof_list);
}
// 5. Setup the various coefficients needed for the Laplace operator and
// the various boundary conditions. In general these coefficients could
// be functions of position but here we use only constants.
ConstantCoefficient matCoef(mat_val);
ConstantCoefficient dbcCoef(dbc_val);
ConstantCoefficient nbcCoef(nbc_val);
ConstantCoefficient rbcACoef(rbc_a_val);
ConstantCoefficient rbcBCoef(rbc_b_val);
// Since the n.Grad(u) terms arise by integrating -Div(m Grad(u)) by parts
// we must introduce the coefficient 'm' into the boundary conditions.
// Therefore, in the case of the Neumann BC, we actually enforce
// m n.Grad(u) = m g rather than simply n.Grad(u) = g.
ProductCoefficient m_nbcCoef(matCoef, nbcCoef);
ProductCoefficient m_rbcACoef(matCoef, rbcACoef);
ProductCoefficient m_rbcBCoef(matCoef, rbcBCoef);
// 6. Define the solution vector u as a finite element grid function
// corresponding to fespace. Initialize u with initial guess of zero.
GridFunction u(&fespace);
u = 0.0;
// 7. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator.
BilinearForm a(&fespace);
a.AddDomainIntegrator(new DiffusionIntegrator(matCoef));
if (h1)
{
// Add a Mass integrator on the Robin boundary
a.AddBoundaryIntegrator(new MassIntegrator(m_rbcACoef), rbc_bdr);
}
else
{
// Add the interfacial portion of the Lapalce operator
a.AddInteriorFaceIntegrator(new DGDiffusionIntegrator(matCoef,
sigma, kappa));
// Counteract the n.Grad(u) term on the Dirichlet portion of the boundary
a.AddBdrFaceIntegrator(new DGDiffusionIntegrator(matCoef, sigma, kappa),
dbc_bdr);
// Augment the n.Grad(u) term with a*u on the Robin portion of boundary
a.AddBdrFaceIntegrator(new BoundaryMassIntegrator(m_rbcACoef),
rbc_bdr);
}
a.Assemble();
// 8. Assemble the linear form for the right hand side vector.
LinearForm b(&fespace);
if (h1)
{
// Set the Dirchlet values in the solution vector
u.ProjectBdrCoefficient(dbcCoef, dbc_bdr);
// Add the desired value for n.Grad(u) on the Neumann boundary
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(m_nbcCoef), nbc_bdr);
// Add the desired value for n.Grad(u) + a*u on the Robin boundary
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(m_rbcBCoef), rbc_bdr);
}
else
{
// Add the desired value for the Dirchlet boundary
b.AddBdrFaceIntegrator(new DGDirichletLFIntegrator(dbcCoef, matCoef,
sigma, kappa),
dbc_bdr);
// Add the desired value for n.Grad(u) on the Neumann boundary
b.AddBdrFaceIntegrator(new BoundaryLFIntegrator(m_nbcCoef),
nbc_bdr);
// Add the desired value for n.Grad(u) + a*u on the Robin boundary
b.AddBdrFaceIntegrator(new BoundaryLFIntegrator(m_rbcBCoef),
rbc_bdr);
}
b.Assemble();
// 9. Construct the linear system.
OperatorPtr A;
Vector B, X;
a.FormLinearSystem(ess_tdof_list, u, b, A, X, B);
#ifndef MFEM_USE_SUITESPARSE
// 10. Define a simple symmetric Gauss-Seidel preconditioner and use it to
// solve the system AX=B with PCG in the symmetric case, and GMRES in the
// non-symmetric one.
{
GSSmoother M((SparseMatrix&)(*A));
if (sigma == -1.0)
{
PCG(*A, M, B, X, 1, 500, 1e-12, 0.0);
}
else
{
GMRES(*A, M, B, X, 1, 500, 10, 1e-12, 0.0);
}
}
#else
// 11. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the
// system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(*A);
umf_solver.Mult(B, X);
#endif
// 12. Recover the grid function corresponding to U. This is the
// local finite element solution.
a.RecoverFEMSolution(X, b, u);
// 13. Build a mass matrix to help solve for n.Grad(u) where 'n' is
// a surface normal.
BilinearForm m(&fespace);
m.AddDomainIntegrator(new MassIntegrator);
m.Assemble();
ess_tdof_list.SetSize(0);
OperatorPtr M;
m.FormSystemMatrix(ess_tdof_list, M);
// 14. Compute the various boundary integrals.
mfem::out << endl
<< "Verifying boundary conditions" << endl
<< "=============================" << endl;
{
// Integrate the solution on the Dirichlet boundary and compare
// to the expected value.
double err, avg = IntegrateBC(u, dbc_bdr, 0.0, 1.0, dbc_val, err);
bool hom_dbc = (dbc_val == 0.0);
err /= hom_dbc ? 1.0 : fabs(dbc_val);
mfem::out << "Average of solution on Gamma_dbc:\t"
<< avg << ", \t"
<< (hom_dbc ? "absolute" : "relative")
<< " error " << err << endl;
}
{
// Integrate n.Grad(u) on the inhomogeneous Neumann boundary and
// compare to the expected value.
double err, avg = IntegrateBC(u, nbc_bdr, 1.0, 0.0, nbc_val, err);
bool hom_nbc = (nbc_val == 0.0);
err /= hom_nbc ? 1.0 : fabs(nbc_val);
mfem::out << "Average of n.Grad(u) on Gamma_nbc:\t"
<< avg << ", \t"
<< (hom_nbc ? "absolute" : "relative")
<< " error " << err << endl;
}
{
// Integrate n.Grad(u) on the homogeneous Neumann boundary and compare
// to the expected value of zero.
Array<int> nbc0_bdr(mesh->bdr_attributes.Max());
nbc0_bdr = 0;
nbc0_bdr[3] = 1;
double err, avg = IntegrateBC(u, nbc0_bdr, 1.0, 0.0, 0.0, err);
bool hom_nbc = true;
mfem::out << "Average of n.Grad(u) on Gamma_nbc0:\t"
<< avg << ", \t"
<< (hom_nbc ? "absolute" : "relative")
<< " error " << err << endl;
}
{
// Integrate n.Grad(u) + a * u on the Robin boundary and compare to
// the expected value.
double err, avg = IntegrateBC(u, rbc_bdr, 1.0, rbc_a_val, rbc_b_val, err);
bool hom_rbc = (rbc_b_val == 0.0);
err /= hom_rbc ? 1.0 : fabs(rbc_b_val);
mfem::out << "Average of n.Grad(u)+a*u on Gamma_rbc:\t"
<< avg << ", \t"
<< (hom_rbc ? "absolute" : "relative")
<< " error " << err << endl;
}
// 15. Save the refined mesh and the solution. This output can be viewed
// later using GLVis: "glvis -m refined.mesh -g sol.gf".
{
ofstream mesh_ofs("refined.mesh");
mesh_ofs.precision(8);
mesh->Print(mesh_ofs);
ofstream sol_ofs("sol.gf");
sol_ofs.precision(8);
u.Save(sol_ofs);
}
// 16. Send the solution by socket to a GLVis server.
if (visualization)
{
string title_str = h1 ? "H1" : "DG";
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << *mesh << u
<< "window_title '" << title_str << " Solution'"
<< " keys 'mmc'" << flush;
}
// 17. Free the used memory.
delete fec;
delete mesh;
return 0;
}
void quad_trans(double u, double v, double &x, double &y, bool log = false)
{
double a = a_; // Radius of disc
double d = 4.0 * a * (M_SQRT2 - 2.0 * a) * (1.0 - 2.0 * v);
double v0 = (1.0 + M_SQRT2) * (M_SQRT2 * a - 2.0 * v) *
((4.0 - 3 * M_SQRT2) * a +
(8.0 * (M_SQRT2 - 1.0) * a - 2.0) * v) / d;
double r = 2.0 * ((M_SQRT2 - 1.0) * a * a * (1.0 - 4.0 *v) +
2.0 * (1.0 + M_SQRT2 *
(1.0 + 2.0 * (2.0 * a - M_SQRT2 - 1.0) * a)) * v * v
) / d;
double t = asin(v / r) * u / v;
if (log)
{
mfem::out << "u, v, r, v0, t "
<< u << " " << v << " " << r << " " << v0 << " " << t
<< endl;
}
x = r * sin(t);
y = r * cos(t) - v0;
}
void trans(const Vector &u, Vector &x)
{
double tol = 1e-4;
if (u[1] > 0.5 - tol || u[1] < -0.5 + tol)
{
x = u;
return;
}
if (u[0] > 1.0 - tol || u[0] < -1.0 + tol || fabs(u[0]) < tol)
{
x = u;
return;
}
if (u[0] > 0.0)
{
if (u[1] > fabs(u[0] - 0.5))
{
quad_trans(u[0] - 0.5, u[1], x[0], x[1]);
x[0] += 0.5;
return;
}
if (u[1] < -fabs(u[0] - 0.5))
{
quad_trans(u[0] - 0.5, -u[1], x[0], x[1]);
x[0] += 0.5;
x[1] *= -1.0;
return;
}
if (u[0] - 0.5 > fabs(u[1]))
{
quad_trans(u[1], u[0] - 0.5, x[1], x[0]);
x[0] += 0.5;
return;
}
if (u[0] - 0.5 < -fabs(u[1]))
{
quad_trans(u[1], 0.5 - u[0], x[1], x[0]);
x[0] *= -1.0;
x[0] += 0.5;
return;
}
}
else
{
if (u[1] > fabs(u[0] + 0.5))
{
quad_trans(u[0] + 0.5, u[1], x[0], x[1]);
x[0] -= 0.5;
return;
}
if (u[1] < -fabs(u[0] + 0.5))
{
quad_trans(u[0] + 0.5, -u[1], x[0], x[1]);
x[0] -= 0.5;
x[1] *= -1.0;
return;
}
if (u[0] + 0.5 > fabs(u[1]))
{
quad_trans(u[1], u[0] + 0.5, x[1], x[0]);
x[0] -= 0.5;
return;
}
if (u[0] + 0.5 < -fabs(u[1]))
{
quad_trans(u[1], -0.5 - u[0], x[1], x[0]);
x[0] *= -1.0;
x[0] -= 0.5;
return;
}
}
x = u;
}
Mesh * GenerateSerialMesh(int ref)
{
Mesh * mesh = new Mesh(2, 29, 16, 24, 2);
int vi[4];
for (int i=0; i<2; i++)
{
int o = 13 * i;
vi[0] = o + 0; vi[1] = o + 3; vi[2] = o + 4; vi[3] = o + 1;
mesh->AddQuad(vi);
vi[0] = o + 1; vi[1] = o + 4; vi[2] = o + 5; vi[3] = o + 2;
mesh->AddQuad(vi);
vi[0] = o + 5; vi[1] = o + 8; vi[2] = o + 9; vi[3] = o + 2;
mesh->AddQuad(vi);
vi[0] = o + 8; vi[1] = o + 12; vi[2] = o + 15; vi[3] = o + 9;
mesh->AddQuad(vi);
vi[0] = o + 11; vi[1] = o + 14; vi[2] = o + 15; vi[3] = o + 12;
mesh->AddQuad(vi);
vi[0] = o + 10; vi[1] = o + 13; vi[2] = o + 14; vi[3] = o + 11;
mesh->AddQuad(vi);
vi[0] = o + 6; vi[1] = o + 13; vi[2] = o + 10; vi[3] = o + 7;
mesh->AddQuad(vi);
vi[0] = o + 0; vi[1] = o + 6; vi[2] = o + 7; vi[3] = o + 3;
mesh->AddQuad(vi);
}
vi[0] = 0; vi[1] = 6; mesh->AddBdrSegment(vi, 1);
vi[0] = 6; vi[1] = 13; mesh->AddBdrSegment(vi, 1);
vi[0] = 13; vi[1] = 19; mesh->AddBdrSegment(vi, 1);
vi[0] = 19; vi[1] = 26; mesh->AddBdrSegment(vi, 1);
vi[0] = 28; vi[1] = 22; mesh->AddBdrSegment(vi, 2);
vi[0] = 22; vi[1] = 15; mesh->AddBdrSegment(vi, 2);
vi[0] = 15; vi[1] = 9; mesh->AddBdrSegment(vi, 2);
vi[0] = 9; vi[1] = 2; mesh->AddBdrSegment(vi, 2);
for (int i=0; i<2; i++)
{
int o = 13 * i;
vi[0] = o + 7; vi[1] = o + 3; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 10; vi[1] = o + 7; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 11; vi[1] = o + 10; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 12; vi[1] = o + 11; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 8; vi[1] = o + 12; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 5; vi[1] = o + 8; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 4; vi[1] = o + 5; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 3; vi[1] = o + 4; mesh->AddBdrSegment(vi, 3 + i);
}
double d[2];
double a = a_ / M_SQRT2;
d[0] = -1.0; d[1] = -0.5; mesh->AddVertex(d);
d[0] = -1.0; d[1] = 0.0; mesh->AddVertex(d);
d[0] = -1.0; d[1] = 0.5; mesh->AddVertex(d);
d[0] = -0.5 - a; d[1] = -a; mesh->AddVertex(d);
d[0] = -0.5 - a; d[1] = 0.0; mesh->AddVertex(d);
d[0] = -0.5 - a; d[1] = a; mesh->AddVertex(d);
d[0] = -0.5; d[1] = -0.5; mesh->AddVertex(d);
d[0] = -0.5; d[1] = -a; mesh->AddVertex(d);
d[0] = -0.5; d[1] = a; mesh->AddVertex(d);
d[0] = -0.5; d[1] = 0.5; mesh->AddVertex(d);
d[0] = -0.5 + a; d[1] = -a; mesh->AddVertex(d);
d[0] = -0.5 + a; d[1] = 0.0; mesh->AddVertex(d);
d[0] = -0.5 + a; d[1] = a; mesh->AddVertex(d);
d[0] = 0.0; d[1] = -0.5; mesh->AddVertex(d);
d[0] = 0.0; d[1] = 0.0; mesh->AddVertex(d);
d[0] = 0.0; d[1] = 0.5; mesh->AddVertex(d);
d[0] = 0.5 - a; d[1] = -a; mesh->AddVertex(d);
d[0] = 0.5 - a; d[1] = 0.0; mesh->AddVertex(d);
d[0] = 0.5 - a; d[1] = a; mesh->AddVertex(d);
d[0] = 0.5; d[1] = -0.5; mesh->AddVertex(d);
d[0] = 0.5; d[1] = -a; mesh->AddVertex(d);
d[0] = 0.5; d[1] = a; mesh->AddVertex(d);
d[0] = 0.5; d[1] = 0.5; mesh->AddVertex(d);
d[0] = 0.5 + a; d[1] = -a; mesh->AddVertex(d);
d[0] = 0.5 + a; d[1] = 0.0; mesh->AddVertex(d);
d[0] = 0.5 + a; d[1] = a; mesh->AddVertex(d);
d[0] = 1.0; d[1] = -0.5; mesh->AddVertex(d);
d[0] = 1.0; d[1] = 0.0; mesh->AddVertex(d);
d[0] = 1.0; d[1] = 0.5; mesh->AddVertex(d);
mesh->FinalizeTopology();
mesh->SetCurvature(1, true);
// Stitch the ends of the stack together
{
Array<int> v2v(mesh->GetNV());
for (int i = 0; i < v2v.Size() - 3; i++)
{
v2v[i] = i;
}
// identify vertices on the narrow ends of the rectangle
v2v[v2v.Size() - 3] = 0;
v2v[v2v.Size() - 2] = 1;
v2v[v2v.Size() - 1] = 2;
// renumber elements
for (int i = 0; i < mesh->GetNE(); i++)
{
Element *el = mesh->GetElement(i);
int *v = el->GetVertices();
int nv = el->GetNVertices();
for (int j = 0; j < nv; j++)
{
v[j] = v2v[v[j]];
}
}
// renumber boundary elements
for (int i = 0; i < mesh->GetNBE(); i++)
{
Element *el = mesh->GetBdrElement(i);
int *v = el->GetVertices();
int nv = el->GetNVertices();
for (int j = 0; j < nv; j++)
{
v[j] = v2v[v[j]];
}
}
mesh->RemoveUnusedVertices();
mesh->RemoveInternalBoundaries();
}
mesh->SetCurvature(3, true);
for (int l = 0; l < ref; l++)
{
mesh->UniformRefinement();
}
mesh->Transform(trans);
return mesh;
}
double IntegrateBC(const GridFunction &x, const Array<int> &bdr,
double alpha, double beta, double gamma,
double &err)
{
double nrm = 0.0;
double avg = 0.0;
err = 0.0;
const bool a_is_zero = alpha == 0.0;
const bool b_is_zero = beta == 0.0;
const FiniteElementSpace &fes = *x.FESpace();
MFEM_ASSERT(fes.GetVDim() == 1, "");
Mesh &mesh = *fes.GetMesh();
Vector shape, loc_dofs, w_nor;
DenseMatrix dshape;
Array<int> dof_ids;
for (int i = 0; i < mesh.GetNBE(); i++)
{
if (bdr[mesh.GetBdrAttribute(i)-1] == 0) { continue; }
FaceElementTransformations *FTr = mesh.GetBdrFaceTransformations(i);
if (FTr == nullptr) { continue; }
const FiniteElement &fe = *fes.GetFE(FTr->Elem1No);
MFEM_ASSERT(fe.GetMapType() == FiniteElement::VALUE, "");
const int int_order = 2*fe.GetOrder() + 3;
const IntegrationRule &ir = IntRules.Get(FTr->FaceGeom, int_order);
fes.GetElementDofs(FTr->Elem1No, dof_ids);
x.GetSubVector(dof_ids, loc_dofs);
if (!a_is_zero)
{
const int sdim = FTr->Face->GetSpaceDim();
w_nor.SetSize(sdim);
dshape.SetSize(fe.GetDof(), sdim);
}
if (!b_is_zero)
{
shape.SetSize(fe.GetDof());
}
for (int j = 0; j < ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
IntegrationPoint eip;
FTr->Loc1.Transform(ip, eip);
FTr->Face->SetIntPoint(&ip);
double face_weight = FTr->Face->Weight();
double val = 0.0;
if (!a_is_zero)
{
FTr->Elem1->SetIntPoint(&eip);
fe.CalcPhysDShape(*FTr->Elem1, dshape);
CalcOrtho(FTr->Face->Jacobian(), w_nor);
val += alpha * dshape.InnerProduct(w_nor, loc_dofs) / face_weight;
}
if (!b_is_zero)
{
fe.CalcShape(eip, shape);
val += beta * (shape * loc_dofs);
}
// Measure the length of the boundary
nrm += ip.weight * face_weight;
// Integrate alpha * n.Grad(x) + beta * x
avg += val * ip.weight * face_weight;
// Integrate |alpha * n.Grad(x) + beta * x - gamma|^2
val -= gamma;
err += (val*val) * ip.weight * face_weight;
}
}
// Normalize by the length of the boundary
if (std::abs(nrm) > 0.0)
{
err /= nrm;
avg /= nrm;
}
// Compute l2 norm of the error in the boundary condition
// (negative quadrature weights may produce negative 'err')
err = (err >= 0.0) ? sqrt(err) : -sqrt(-err);
// Return the average value of alpha * n.Grad(x) + beta * x
return avg;
}
+773
View File
@@ -0,0 +1,773 @@
// MFEM Example 27 - Parallel Version
//
// Compile with: make ex27p
//
// Sample runs: mpirun -np 4 ex27p
// mpirun -np 4 ex27p -dg
// mpirun -np 4 ex27p -dg -dbc 8 -nbc -2
// mpirun -np 4 ex27p -rbc-a 1 -rbc-b 8
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// -Delta u = 0 with a variety of boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order using a continuous or discontinuous space. We then
// apply Dirichlet, Neumann (both homogeneous and inhomogeneous),
// Robin, and Periodic boundary conditions on different portions
// of a predefined mesh.
//
// The predefined mesh consists of a rectangle with two
// holes removed (see below). The narrow ends of the
// mesh are connected to form a Periodic boundary
// condition. The lower edge (tagged with attribute 1)
// receives an inhomogeneous Neumann boundary condition.
// A Robin boundary condition is applied to upper edge
// (attribute 2). The circular hole on the left
// (attribute 3) enforces a Dirichlet boundary
// condition. Finally, a natural boundary condition, or
// homogeneous Neumann BC, is applied to the circular
// hole on the right (attribute 4).
//
// Attribute 3 ^ y Attribute 2
// \ | /
// +-----------+-----------+
// | \_ | _ |
// | / \ | / \ |
// <--+---+---+---+---+---+---+--> x
// | \_/ | \_/ |
// | | \ |
// +-----------+-----------+ (hole radii are
// / | \ adjustable)
// Attribute 1 v Attribute 4
//
// The boundary conditions are defined as (where u is
// the solution field):
// Dirichlet: u = d
// Neumann: n.Grad(u) = g
// Robin: n.Grad(u) + a u = b
//
// The user can adjust the values of 'd', 'g', 'a', and
// 'b' with command line options.
//
// This example highlights the differing implementations of
// boundary conditions with continuous and discontinuous Galerkin
// formulations of the Laplace problem.
//
// We recommend viewing examples 1 and 14 before viewing this
// example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
static double a_ = 0.2;
// Normal to hole with boundary attribute 4
void n4Vec(const Vector &x, Vector &n) { n = x; n[0] -= 0.5; n /= -n.Norml2(); }
Mesh * GenerateSerialMesh(int ref);
// Compute the average value of alpha*n.Grad(sol) + beta*sol over the boundary
// attributes marked in bdr_marker. Also computes the L2 norm of
// alpha*n.Grad(sol) + beta*sol - gamma over the same boundary.
double IntegrateBC(const ParGridFunction &sol, const Array<int> &bdr_marker,
double alpha, double beta, double gamma,
double &err);
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
MPI_Session mpi;
if (!mpi.Root()) { mfem::out.Disable(); mfem::err.Disable(); }
// 2. Parse command-line options.
int ser_ref_levels = 2;
int par_ref_levels = 1;
int order = 1;
double sigma = -1.0;
double kappa = -1.0;
bool h1 = true;
bool visualization = true;
double mat_val = 1.0;
double dbc_val = 0.0;
double nbc_val = 1.0;
double rbc_a_val = 1.0; // du/dn + a * u = b
double rbc_b_val = 1.0;
OptionsParser args(argc, argv);
args.AddOption(&h1, "-h1", "--continuous", "-dg", "--discontinuous",
"Select continuous \"H1\" or discontinuous \"DG\" basis.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&sigma, "-s", "--sigma",
"One of the two DG penalty parameters, typically +1/-1."
" See the documentation of class DGDiffusionIntegrator.");
args.AddOption(&kappa, "-k", "--kappa",
"One of the two DG penalty parameters, should be positive."
" Negative values are replaced with (order+1)^2.");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
"Number of times to refine the mesh uniformly in parallel.");
args.AddOption(&mat_val, "-mat", "--material-value",
"Constant value for material coefficient "
"in the Laplace operator.");
args.AddOption(&dbc_val, "-dbc", "--dirichlet-value",
"Constant value for Dirichlet Boundary Condition.");
args.AddOption(&nbc_val, "-nbc", "--neumann-value",
"Constant value for Neumann Boundary Condition.");
args.AddOption(&rbc_a_val, "-rbc-a", "--robin-a-value",
"Constant 'a' value for Robin Boundary Condition: "
"du/dn + a * u = b.");
args.AddOption(&rbc_b_val, "-rbc-b", "--robin-b-value",
"Constant 'b' value for Robin Boundary Condition: "
"du/dn + a * u = b.");
args.AddOption(&a_, "-a", "--radius",
"Radius of holes in the mesh.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(mfem::out);
return 1;
}
if (kappa < 0 && !h1)
{
kappa = (order+1)*(order+1);
}
args.PrintOptions(mfem::out);
if (a_ < 0.01)
{
mfem::out << "Hole radius too small, resetting to 0.01.\n";
a_ = 0.01;
}
if (a_ > 0.49)
{
mfem::out << "Hole radius too large, resetting to 0.49.\n";
a_ = 0.49;
}
// 3. Construct the (serial) mesh and refine it if requested.
Mesh *mesh = GenerateSerialMesh(ser_ref_levels);
int dim = mesh->Dimension();
// 4. 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);
delete mesh;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh.UniformRefinement();
}
// 5. Define a parallel finite element space on the parallel mesh. Here we
// use either continuous Lagrange finite elements or discontinuous
// Galerkin finite elements of the specified order.
FiniteElementCollection *fec =
h1 ? (FiniteElementCollection*)new H1_FECollection(order, dim) :
(FiniteElementCollection*)new DG_FECollection(order, dim);
ParFiniteElementSpace fespace(&pmesh, fec);
HYPRE_Int size = fespace.GlobalTrueVSize();
mfem::out << "Number of finite element unknowns: " << size << endl;
// 6. Create "marker arrays" to define the portions of the boundary
// associated with each type of boundary condition. These arrays
// have an entry corresponding to each boundary attribute.
// Placing a '1' in entry i marks attribute i+1 as being
// active, '0' is inactive.
Array<int> nbc_bdr(pmesh.bdr_attributes.Max());
Array<int> rbc_bdr(pmesh.bdr_attributes.Max());
Array<int> dbc_bdr(pmesh.bdr_attributes.Max());
nbc_bdr = 0; nbc_bdr[0] = 1;
rbc_bdr = 0; rbc_bdr[1] = 1;
dbc_bdr = 0; dbc_bdr[2] = 1;
Array<int> ess_tdof_list(0);
if (h1 && pmesh.bdr_attributes.Size())
{
// For a continuous basis the linear system must be modifed to enforce
// an essential (Dirichlet) boundary condition. In the DG case this is
// not necessary as the boundary condition will only be enforced weakly.
fespace.GetEssentialTrueDofs(dbc_bdr, ess_tdof_list);
}
// 7. Setup the various coefficients needed for the Laplace operator and
// the various boundary conditions. In general these coefficients could
// be functions of position but here we use only constants.
ConstantCoefficient matCoef(mat_val);
ConstantCoefficient dbcCoef(dbc_val);
ConstantCoefficient nbcCoef(nbc_val);
ConstantCoefficient rbcACoef(rbc_a_val);
ConstantCoefficient rbcBCoef(rbc_b_val);
// Since the n.Grad(u) terms arise by integrating -Div(m Grad(u)) by parts
// we must introduce the coefficient 'm' into the boundary conditions.
// Therefore, in the case of the Neumann BC, we actually enforce
// m n.Grad(u) = m g rather than simply n.Grad(u) = g.
ProductCoefficient m_nbcCoef(matCoef, nbcCoef);
ProductCoefficient m_rbcACoef(matCoef, rbcACoef);
ProductCoefficient m_rbcBCoef(matCoef, rbcBCoef);
// 8. Define the solution vector u as a parallel finite element grid function
// corresponding to fespace. Initialize u with initial guess of zero.
ParGridFunction u(&fespace);
u = 0.0;
// 9. Set up the parallel bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator.
ParBilinearForm a(&fespace);
a.AddDomainIntegrator(new DiffusionIntegrator(matCoef));
if (h1)
{
// Add a Mass integrator on the Robin boundary
a.AddBoundaryIntegrator(new MassIntegrator(m_rbcACoef), rbc_bdr);
}
else
{
// Add the interfacial portion of the Lapalce operator
a.AddInteriorFaceIntegrator(new DGDiffusionIntegrator(matCoef,
sigma, kappa));
// Counteract the n.Grad(u) term on the Dirichlet portion of the boundary
a.AddBdrFaceIntegrator(new DGDiffusionIntegrator(matCoef, sigma, kappa),
dbc_bdr);
// Augment the n.Grad(u) term with a*u on the Robin portion of boundary
a.AddBdrFaceIntegrator(new BoundaryMassIntegrator(m_rbcACoef),
rbc_bdr);
}
a.Assemble();
// 10. Assemble the parallel linear form for the right hand side vector.
ParLinearForm b(&fespace);
if (h1)
{
// Set the Dirchlet values in the solution vector
u.ProjectBdrCoefficient(dbcCoef, dbc_bdr);
// Add the desired value for n.Grad(u) on the Neumann boundary
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(m_nbcCoef), nbc_bdr);
// Add the desired value for n.Grad(u) + a*u on the Robin boundary
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(m_rbcBCoef), rbc_bdr);
}
else
{
// Add the desired value for the Dirchlet boundary
b.AddBdrFaceIntegrator(new DGDirichletLFIntegrator(dbcCoef, matCoef,
sigma, kappa),
dbc_bdr);
// Add the desired value for n.Grad(u) on the Neumann boundary
b.AddBdrFaceIntegrator(new BoundaryLFIntegrator(m_nbcCoef),
nbc_bdr);
// Add the desired value for n.Grad(u) + a*u on the Robin boundary
b.AddBdrFaceIntegrator(new BoundaryLFIntegrator(m_rbcBCoef),
rbc_bdr);
}
b.Assemble();
// 11. Construct the linear system.
OperatorPtr A;
Vector B, X;
a.FormLinearSystem(ess_tdof_list, u, b, A, X, B);
// 12. Solve the linear system A X = B.
HypreSolver *amg = new HypreBoomerAMG;
if (h1 || sigma == -1.0)
{
HyprePCG pcg(MPI_COMM_WORLD);
pcg.SetTol(1e-12);
pcg.SetMaxIter(200);
pcg.SetPrintLevel(2);
pcg.SetPreconditioner(*amg);
pcg.SetOperator(*A);
pcg.Mult(B, X);
}
else
{
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetAbsTol(0.0);
gmres.SetRelTol(1e-12);
gmres.SetMaxIter(200);
gmres.SetKDim(10);
gmres.SetPrintLevel(1);
gmres.SetPreconditioner(*amg);
gmres.SetOperator(*A);
gmres.Mult(B, X);
}
delete amg;
// 13. Recover the parallel grid function corresponding to U. This is the
// local finite element solution on each processor.
a.RecoverFEMSolution(X, b, u);
// 14. Build a mass matrix to help solve for n.Grad(u) where 'n' is
// a surface normal.
ParBilinearForm m(&fespace);
m.AddDomainIntegrator(new MassIntegrator);
m.Assemble();
ess_tdof_list.SetSize(0);
OperatorPtr M;
m.FormSystemMatrix(ess_tdof_list, M);
// 15. Compute the various boundary integrals.
mfem::out << endl
<< "Verifying boundary conditions" << endl
<< "=============================" << endl;
{
// Integrate the solution on the Dirichlet boundary and compare
// to the expected value.
double err, avg = IntegrateBC(u, dbc_bdr, 0.0, 1.0, dbc_val, err);
bool hom_dbc = (dbc_val == 0.0);
err /= hom_dbc ? 1.0 : fabs(dbc_val);
mfem::out << "Average of solution on Gamma_dbc:\t"
<< avg << ", \t"
<< (hom_dbc ? "absolute" : "relative")
<< " error " << err << endl;
}
{
// Integrate n.Grad(u) on the inhomogeneous Neumann boundary and
// compare to the expected value.
double err, avg = IntegrateBC(u, nbc_bdr, 1.0, 0.0, nbc_val, err);
bool hom_nbc = (nbc_val == 0.0);
err /= hom_nbc ? 1.0 : fabs(nbc_val);
mfem::out << "Average of n.Grad(u) on Gamma_nbc:\t"
<< avg << ", \t"
<< (hom_nbc ? "absolute" : "relative")
<< " error " << err << endl;
}
{
// Integrate n.Grad(u) on the homogeneous Neumann boundary and compare
// to the expected value of zero.
Array<int> nbc0_bdr(pmesh.bdr_attributes.Max());
nbc0_bdr = 0;
nbc0_bdr[3] = 1;
double err, avg = IntegrateBC(u, nbc0_bdr, 1.0, 0.0, 0.0, err);
bool hom_nbc = true;
mfem::out << "Average of n.Grad(u) on Gamma_nbc0:\t"
<< avg << ", \t"
<< (hom_nbc ? "absolute" : "relative")
<< " error " << err << endl;
}
{
// Integrate n.Grad(u) + a * u on the Robin boundary and compare to
// the expected value.
double err, avg = IntegrateBC(u, rbc_bdr, 1.0, rbc_a_val, rbc_b_val, err);
bool hom_rbc = (rbc_b_val == 0.0);
err /= hom_rbc ? 1.0 : fabs(rbc_b_val);
mfem::out << "Average of n.Grad(u)+a*u on Gamma_rbc:\t"
<< avg << ", \t"
<< (hom_rbc ? "absolute" : "relative")
<< " error " << err << endl;
}
// 16. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << mpi.WorldRank();
sol_name << "sol." << setfill('0') << setw(6) << mpi.WorldRank();
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh.Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
u.Save(sol_ofs);
}
// 17. Send the solution by socket to a GLVis server.
if (visualization)
{
string title_str = h1 ? "H1" : "DG";
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << mpi.WorldSize()
<< " " << mpi.WorldRank() << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << pmesh << u
<< "window_title '" << title_str << " Solution'"
<< " keys 'mmc'" << flush;
}
// 18. Free the used memory.
delete fec;
return 0;
}
void quad_trans(double u, double v, double &x, double &y, bool log = false)
{
double a = a_; // Radius of disc
double d = 4.0 * a * (M_SQRT2 - 2.0 * a) * (1.0 - 2.0 * v);
double v0 = (1.0 + M_SQRT2) * (M_SQRT2 * a - 2.0 * v) *
((4.0 - 3 * M_SQRT2) * a +
(8.0 * (M_SQRT2 - 1.0) * a - 2.0) * v) / d;
double r = 2.0 * ((M_SQRT2 - 1.0) * a * a * (1.0 - 4.0 *v) +
2.0 * (1.0 + M_SQRT2 *
(1.0 + 2.0 * (2.0 * a - M_SQRT2 - 1.0) * a)) * v * v
) / d;
double t = asin(v / r) * u / v;
if (log)
{
mfem::out << "u, v, r, v0, t "
<< u << " " << v << " " << r << " " << v0 << " " << t
<< endl;
}
x = r * sin(t);
y = r * cos(t) - v0;
}
void trans(const Vector &u, Vector &x)
{
double tol = 1e-4;
if (u[1] > 0.5 - tol || u[1] < -0.5 + tol)
{
x = u;
return;
}
if (u[0] > 1.0 - tol || u[0] < -1.0 + tol || fabs(u[0]) < tol)
{
x = u;
return;
}
if (u[0] > 0.0)
{
if (u[1] > fabs(u[0] - 0.5))
{
quad_trans(u[0] - 0.5, u[1], x[0], x[1]);
x[0] += 0.5;
return;
}
if (u[1] < -fabs(u[0] - 0.5))
{
quad_trans(u[0] - 0.5, -u[1], x[0], x[1]);
x[0] += 0.5;
x[1] *= -1.0;
return;
}
if (u[0] - 0.5 > fabs(u[1]))
{
quad_trans(u[1], u[0] - 0.5, x[1], x[0]);
x[0] += 0.5;
return;
}
if (u[0] - 0.5 < -fabs(u[1]))
{
quad_trans(u[1], 0.5 - u[0], x[1], x[0]);
x[0] *= -1.0;
x[0] += 0.5;
return;
}
}
else
{
if (u[1] > fabs(u[0] + 0.5))
{
quad_trans(u[0] + 0.5, u[1], x[0], x[1]);
x[0] -= 0.5;
return;
}
if (u[1] < -fabs(u[0] + 0.5))
{
quad_trans(u[0] + 0.5, -u[1], x[0], x[1]);
x[0] -= 0.5;
x[1] *= -1.0;
return;
}
if (u[0] + 0.5 > fabs(u[1]))
{
quad_trans(u[1], u[0] + 0.5, x[1], x[0]);
x[0] -= 0.5;
return;
}
if (u[0] + 0.5 < -fabs(u[1]))
{
quad_trans(u[1], -0.5 - u[0], x[1], x[0]);
x[0] *= -1.0;
x[0] -= 0.5;
return;
}
}
x = u;
}
Mesh * GenerateSerialMesh(int ref)
{
Mesh * mesh = new Mesh(2, 29, 16, 24, 2);
int vi[4];
for (int i=0; i<2; i++)
{
int o = 13 * i;
vi[0] = o + 0; vi[1] = o + 3; vi[2] = o + 4; vi[3] = o + 1;
mesh->AddQuad(vi);
vi[0] = o + 1; vi[1] = o + 4; vi[2] = o + 5; vi[3] = o + 2;
mesh->AddQuad(vi);
vi[0] = o + 5; vi[1] = o + 8; vi[2] = o + 9; vi[3] = o + 2;
mesh->AddQuad(vi);
vi[0] = o + 8; vi[1] = o + 12; vi[2] = o + 15; vi[3] = o + 9;
mesh->AddQuad(vi);
vi[0] = o + 11; vi[1] = o + 14; vi[2] = o + 15; vi[3] = o + 12;
mesh->AddQuad(vi);
vi[0] = o + 10; vi[1] = o + 13; vi[2] = o + 14; vi[3] = o + 11;
mesh->AddQuad(vi);
vi[0] = o + 6; vi[1] = o + 13; vi[2] = o + 10; vi[3] = o + 7;
mesh->AddQuad(vi);
vi[0] = o + 0; vi[1] = o + 6; vi[2] = o + 7; vi[3] = o + 3;
mesh->AddQuad(vi);
}
vi[0] = 0; vi[1] = 6; mesh->AddBdrSegment(vi, 1);
vi[0] = 6; vi[1] = 13; mesh->AddBdrSegment(vi, 1);
vi[0] = 13; vi[1] = 19; mesh->AddBdrSegment(vi, 1);
vi[0] = 19; vi[1] = 26; mesh->AddBdrSegment(vi, 1);
vi[0] = 28; vi[1] = 22; mesh->AddBdrSegment(vi, 2);
vi[0] = 22; vi[1] = 15; mesh->AddBdrSegment(vi, 2);
vi[0] = 15; vi[1] = 9; mesh->AddBdrSegment(vi, 2);
vi[0] = 9; vi[1] = 2; mesh->AddBdrSegment(vi, 2);
for (int i=0; i<2; i++)
{
int o = 13 * i;
vi[0] = o + 7; vi[1] = o + 3; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 10; vi[1] = o + 7; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 11; vi[1] = o + 10; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 12; vi[1] = o + 11; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 8; vi[1] = o + 12; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 5; vi[1] = o + 8; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 4; vi[1] = o + 5; mesh->AddBdrSegment(vi, 3 + i);
vi[0] = o + 3; vi[1] = o + 4; mesh->AddBdrSegment(vi, 3 + i);
}
double d[2];
double a = a_ / M_SQRT2;
d[0] = -1.0; d[1] = -0.5; mesh->AddVertex(d);
d[0] = -1.0; d[1] = 0.0; mesh->AddVertex(d);
d[0] = -1.0; d[1] = 0.5; mesh->AddVertex(d);
d[0] = -0.5 - a; d[1] = -a; mesh->AddVertex(d);
d[0] = -0.5 - a; d[1] = 0.0; mesh->AddVertex(d);
d[0] = -0.5 - a; d[1] = a; mesh->AddVertex(d);
d[0] = -0.5; d[1] = -0.5; mesh->AddVertex(d);
d[0] = -0.5; d[1] = -a; mesh->AddVertex(d);
d[0] = -0.5; d[1] = a; mesh->AddVertex(d);
d[0] = -0.5; d[1] = 0.5; mesh->AddVertex(d);
d[0] = -0.5 + a; d[1] = -a; mesh->AddVertex(d);
d[0] = -0.5 + a; d[1] = 0.0; mesh->AddVertex(d);
d[0] = -0.5 + a; d[1] = a; mesh->AddVertex(d);
d[0] = 0.0; d[1] = -0.5; mesh->AddVertex(d);
d[0] = 0.0; d[1] = 0.0; mesh->AddVertex(d);
d[0] = 0.0; d[1] = 0.5; mesh->AddVertex(d);
d[0] = 0.5 - a; d[1] = -a; mesh->AddVertex(d);
d[0] = 0.5 - a; d[1] = 0.0; mesh->AddVertex(d);
d[0] = 0.5 - a; d[1] = a; mesh->AddVertex(d);
d[0] = 0.5; d[1] = -0.5; mesh->AddVertex(d);
d[0] = 0.5; d[1] = -a; mesh->AddVertex(d);
d[0] = 0.5; d[1] = a; mesh->AddVertex(d);
d[0] = 0.5; d[1] = 0.5; mesh->AddVertex(d);
d[0] = 0.5 + a; d[1] = -a; mesh->AddVertex(d);
d[0] = 0.5 + a; d[1] = 0.0; mesh->AddVertex(d);
d[0] = 0.5 + a; d[1] = a; mesh->AddVertex(d);
d[0] = 1.0; d[1] = -0.5; mesh->AddVertex(d);
d[0] = 1.0; d[1] = 0.0; mesh->AddVertex(d);
d[0] = 1.0; d[1] = 0.5; mesh->AddVertex(d);
mesh->FinalizeTopology();
mesh->SetCurvature(1, true);
// Stitch the ends of the stack together
{
Array<int> v2v(mesh->GetNV());
for (int i = 0; i < v2v.Size() - 3; i++)
{
v2v[i] = i;
}
// identify vertices on the narrow ends of the rectangle
v2v[v2v.Size() - 3] = 0;
v2v[v2v.Size() - 2] = 1;
v2v[v2v.Size() - 1] = 2;
// renumber elements
for (int i = 0; i < mesh->GetNE(); i++)
{
Element *el = mesh->GetElement(i);
int *v = el->GetVertices();
int nv = el->GetNVertices();
for (int j = 0; j < nv; j++)
{
v[j] = v2v[v[j]];
}
}
// renumber boundary elements
for (int i = 0; i < mesh->GetNBE(); i++)
{
Element *el = mesh->GetBdrElement(i);
int *v = el->GetVertices();
int nv = el->GetNVertices();
for (int j = 0; j < nv; j++)
{
v[j] = v2v[v[j]];
}
}
mesh->RemoveUnusedVertices();
mesh->RemoveInternalBoundaries();
}
mesh->SetCurvature(3, true);
for (int l = 0; l < ref; l++)
{
mesh->UniformRefinement();
}
mesh->Transform(trans);
return mesh;
}
double IntegrateBC(const ParGridFunction &x, const Array<int> &bdr,
double alpha, double beta, double gamma,
double &glb_err)
{
double loc_vals[3];
double &nrm = loc_vals[0];
double &avg = loc_vals[1];
double &err = loc_vals[2];
nrm = 0.0;
avg = 0.0;
err = 0.0;
const bool a_is_zero = alpha == 0.0;
const bool b_is_zero = beta == 0.0;
const ParFiniteElementSpace &fes = *x.ParFESpace();
MFEM_ASSERT(fes.GetVDim() == 1, "");
ParMesh &mesh = *fes.GetParMesh();
Vector shape, loc_dofs, w_nor;
DenseMatrix dshape;
Array<int> dof_ids;
for (int i = 0; i < mesh.GetNBE(); i++)
{
if (bdr[mesh.GetBdrAttribute(i)-1] == 0) { continue; }
FaceElementTransformations *FTr = mesh.GetBdrFaceTransformations(i);
if (FTr == nullptr) { continue; }
const FiniteElement &fe = *fes.GetFE(FTr->Elem1No);
MFEM_ASSERT(fe.GetMapType() == FiniteElement::VALUE, "");
const int int_order = 2*fe.GetOrder() + 3;
const IntegrationRule &ir = IntRules.Get(FTr->FaceGeom, int_order);
fes.GetElementDofs(FTr->Elem1No, dof_ids);
x.GetSubVector(dof_ids, loc_dofs);
if (!a_is_zero)
{
const int sdim = FTr->Face->GetSpaceDim();
w_nor.SetSize(sdim);
dshape.SetSize(fe.GetDof(), sdim);
}
if (!b_is_zero)
{
shape.SetSize(fe.GetDof());
}
for (int j = 0; j < ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
IntegrationPoint eip;
FTr->Loc1.Transform(ip, eip);
FTr->Face->SetIntPoint(&ip);
double face_weight = FTr->Face->Weight();
double val = 0.0;
if (!a_is_zero)
{
FTr->Elem1->SetIntPoint(&eip);
fe.CalcPhysDShape(*FTr->Elem1, dshape);
CalcOrtho(FTr->Face->Jacobian(), w_nor);
val += alpha * dshape.InnerProduct(w_nor, loc_dofs) / face_weight;
}
if (!b_is_zero)
{
fe.CalcShape(eip, shape);
val += beta * (shape * loc_dofs);
}
// Measure the length of the boundary
nrm += ip.weight * face_weight;
// Integrate alpha * n.Grad(x) + beta * x
avg += val * ip.weight * face_weight;
// Integrate |alpha * n.Grad(x) + beta * x - gamma|^2
val -= gamma;
err += (val*val) * ip.weight * face_weight;
}
}
double glb_vals[3];
MPI_Allreduce(loc_vals, glb_vals, 3, MPI_DOUBLE, MPI_SUM, fes.GetComm());
double glb_nrm = glb_vals[0];
double glb_avg = glb_vals[1];
glb_err = glb_vals[2];
// Normalize by the length of the boundary
if (std::abs(glb_nrm) > 0.0)
{
glb_err /= glb_nrm;
glb_avg /= glb_nrm;
}
// Compute l2 norm of the error in the boundary condition
// (negative quadrature weights may produce negative 'err')
glb_err = (glb_err >= 0.0) ? sqrt(glb_err) : -sqrt(-glb_err);
// Return the average value of alpha * n.Grad(x) + beta * x
return glb_avg;
}
+7
View File
@@ -274,6 +274,13 @@ int main(int argc, char *argv[])
pmesh->SetNodalFESpace(fespace);
}
{
x.Save("ex2p.gf", 1);
ParGridFunction new_x(fespace, "ex2p.gf");
new_x -= x;
out << "GF difference: " << new_x.Norml1() << endl;
}
// 16. Save in parallel the displaced mesh and the inverted solution (which
// gives the backward displacements to the original grid). This output
// can be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
+28 -2
View File
@@ -22,6 +22,8 @@
// The example demonstrates the use of the BlockMatrix class, as
// well as the collective saving of several grid functions in
// VisIt (visit.llnl.gov) and ParaView (paraview.org) formats.
// Optional saving with ADIOS2 (adios2.readthedocs.io) streams is
// also illustrated.
//
// We recommend viewing examples 1-4 before viewing this example.
@@ -55,6 +57,7 @@ int main(int argc, char *argv[])
int order = 1;
bool par_format = false;
bool visualization = 1;
bool adios2 = false;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -67,6 +70,9 @@ int main(int argc, char *argv[])
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&adios2, "-adios2", "--adios2-streams", "-no-adios2",
"--no-adios2-streams",
"Save data using adios2 streams.");
args.Parse();
if (!args.Good())
{
@@ -337,7 +343,27 @@ int main(int argc, char *argv[])
paraview_dc.RegisterField("pressure",p);
paraview_dc.Save();
// 17. Send the solution by socket to a GLVis server.
// 17. Optionally output a BP (binary pack) file using ADIOS2. This can be
// visualized with the ParaView VTX reader.
#ifdef MFEM_USE_ADIOS2
if (adios2)
{
std::string postfix(mesh_file);
postfix.erase(0, std::string("../data/").size() );
postfix += "_o" + std::to_string(order);
const std::string collection_name = "ex5-p_" + postfix + ".bp";
ADIOS2DataCollection adios2_dc(MPI_COMM_WORLD, collection_name, pmesh);
adios2_dc.SetLevelsOfDetail(1);
adios2_dc.SetCycle(1);
adios2_dc.SetTime(0.0);
adios2_dc.RegisterField("velocity",u);
adios2_dc.RegisterField("pressure",p);
adios2_dc.Save();
}
#endif
// 18. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
@@ -357,7 +383,7 @@ int main(int argc, char *argv[])
<< endl;
}
// 18. Free the used memory.
// 19. Free the used memory.
delete fform;
delete gform;
delete u;
+7
View File
@@ -279,4 +279,11 @@ void SnapNodes(Mesh &mesh)
nodes(nodes.FESpace()->DofToVDof(i, d)) = node(d);
}
}
if (mesh.Nonconforming())
{
// Snap hanging nodes to the master side.
Vector tnodes;
nodes.GetTrueDofs(tnodes);
nodes.SetFromTrueDofs(tnodes);
}
}
+7
View File
@@ -348,4 +348,11 @@ void SnapNodes(Mesh &mesh)
nodes(nodes.FESpace()->DofToVDof(i, d)) = node(d);
}
}
if (mesh.Nonconforming())
{
// Snap hanging nodes to the master side.
Vector tnodes;
nodes.GetTrueDofs(tnodes);
nodes.SetFromTrueDofs(tnodes);
}
}
+46 -3
View File
@@ -31,9 +31,10 @@
// and explicit ODE time integrators, the definition of periodic
// boundary conditions through periodic meshes, as well as the use
// of GLVis for persistent visualization of a time-evolving
// solution. The saving of time-dependent data files for external
// visualization with VisIt (visit.llnl.gov) and ParaView
// (paraview.org) is also illustrated.
// solution. Saving of time-dependent data files for visualization
// with VisIt (visit.llnl.gov) and ParaView (paraview.org), as
// well as the optional saving with ADIOS2 (adios2.readthedocs.io)
// are also illustrated.
#include "mfem.hpp"
#include <fstream>
@@ -167,6 +168,7 @@ int main(int argc, char *argv[])
bool visualization = true;
bool visit = false;
bool paraview = false;
bool adios2 = false;
bool binary = false;
int vis_steps = 5;
@@ -208,6 +210,9 @@ int main(int argc, char *argv[])
args.AddOption(&paraview, "-paraview", "--paraview-datafiles", "-no-paraview",
"--no-paraview-datafiles",
"Save data files for ParaView (paraview.org) visualization.");
args.AddOption(&adios2, "-adios2", "--adios2-streams", "-no-adios2",
"--no-adios2-streams",
"Save data using adios2 streams.");
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
"--ascii-datafiles",
"Use binary (Sidre) or ascii format for VisIt data files.");
@@ -394,6 +399,28 @@ int main(int argc, char *argv[])
pd->Save();
}
// Optionally output a BP (binary pack) file using ADIOS2. This can be
// visualized with the ParaView VTX reader.
#ifdef MFEM_USE_ADIOS2
ADIOS2DataCollection *adios2_dc = NULL;
if (adios2)
{
std::string postfix(mesh_file);
postfix.erase(0, std::string("../data/").size() );
postfix += "_o" + std::to_string(order);
const std::string collection_name = "ex9-p-" + postfix + ".bp";
adios2_dc = new ADIOS2DataCollection(MPI_COMM_WORLD, collection_name, pmesh);
// output data substreams are half the number of mpi processes
adios2_dc->SetParameter("SubStreams", std::to_string(num_procs/2) );
// adios2_dc->SetLevelsOfDetail(2);
adios2_dc->RegisterField("solution", u);
adios2_dc->SetCycle(0);
adios2_dc->SetTime(0.0);
adios2_dc->Save();
}
#endif
socketstream sout;
if (visualization)
{
@@ -472,6 +499,16 @@ int main(int argc, char *argv[])
pd->SetTime(t);
pd->Save();
}
#ifdef MFEM_USE_ADIOS2
// transient solutions can be visualized with ParaView
if (adios2)
{
adios2_dc->SetCycle(ti);
adios2_dc->SetTime(t);
adios2_dc->Save();
}
#endif
}
}
@@ -497,6 +534,12 @@ int main(int argc, char *argv[])
delete pmesh;
delete ode_solver;
delete pd;
#ifdef MFEM_USE_ADIOS2
if (adios2)
{
delete adios2_dc;
}
#endif
delete dc;
MPI_Finalize();
+292
View File
@@ -0,0 +1,292 @@
// MFEM Example 1 - Parallel Version
//
// Compile with: make ex1p
//
// Sample runs: mpirun -np 4 ex1p -m ../data/square-disc.mesh
// mpirun -np 4 ex1p -m ../data/star.mesh
// mpirun -np 4 ex1p -m ../data/star-mixed.mesh
// mpirun -np 4 ex1p -m ../data/escher.mesh
// mpirun -np 4 ex1p -m ../data/fichera.mesh
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../data/star-mixed-p2.mesh -o 2
// mpirun -np 4 ex1p -m ../data/disc-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../data/pipe-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../data/ball-nurbs.mesh -o 2
// mpirun -np 4 ex1p -m ../data/fichera-mixed-p2.mesh -o 2
// mpirun -np 4 ex1p -m ../data/star-surf.mesh
// mpirun -np 4 ex1p -m ../data/square-disc-surf.mesh
// mpirun -np 4 ex1p -m ../data/inline-segment.mesh
// mpirun -np 4 ex1p -m ../data/amr-quad.mesh
// mpirun -np 4 ex1p -m ../data/amr-hex.mesh
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
//
// Device sample runs:
// mpirun -np 4 ex1p -pa -d cuda
// mpirun -np 4 ex1p -pa -d occa-cuda
// mpirun -np 4 ex1p -pa -d raja-omp
// mpirun -np 4 ex1p -pa -d ceed-cpu
// mpirun -np 4 ex1p -pa -d ceed-cuda
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
// NURBS mesh, etc.)
//
// The example highlights the use of mesh refinement, finite
// element grid functions, as well as linear and bilinear forms
// corresponding to the left-hand side and right-hand side of the
// discrete linear system. We also cover the explicit elimination
// of essential boundary conditions, static condensation, and the
// optional connection to the GLVis tool for visualization.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "mpi.h"
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
// const char *mesh_file = "../data/star.mesh";
const char *mesh_file = "../data/square-disc.mesh";
int order = 1;
bool static_cond = false;
bool pa = false;
const char *device_config = "cpu";
bool visualization = false;
int nfiles = 1;
// const char *out_file = "0_0.gf";
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&nfiles, "-nf", "--num-files", "Number of files to write.");
// args.AddOption(&out_file, "-o", "--outfile",
// "Name of file to write.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// 3. 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 (myid == 0) { device.Print(); }
// 4. 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 = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 5. 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 10,000 elements.
{
int ref_levels =
(int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 6. 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 = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
}
// 7. Define a parallel finite element space on the parallel mesh. Here we
// use continuous Lagrange finite elements of the specified order. If
// order < 1, we instead use an isoparametric/isogeometric space.
FiniteElementCollection *fec;
if (order > 0)
{
fec = new H1_FECollection(order, dim);
}
else if (pmesh->GetNodes())
{
fec = pmesh->GetNodes()->OwnFEC();
if (myid == 0)
{
cout << "Using isoparametric FEs: " << fec->Name() << endl;
}
}
else
{
fec = new H1_FECollection(order = 1, dim);
}
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec, 1, 0);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 8. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
if (pmesh->bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 9. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (1,phi_i) where phi_i are the basis functions in fespace.
ParLinearForm *b = new ParLinearForm(fespace);
ConstantCoefficient one(1.0);
b->AddDomainIntegrator(new DomainLFIntegrator(one));
b->Assemble();
// 10. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
ParGridFunction x(fespace);
x = 0.0;
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator.
ParBilinearForm *a = new ParBilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a->AddDomainIntegrator(new DiffusionIntegrator(one));
// 12. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
OperatorPtr A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
// 13. Solve the linear system A X = B.
// * With full assembly, use the BoomerAMG preconditioner from hypre.
// * With partial assembly, use Jacobi smoothing, for now.
Solver *prec = NULL;
if (pa)
{
if (UsesTensorBasis(*fespace))
{
prec = new OperatorJacobiSmoother(*a, ess_tdof_list);
}
}
else
{
prec = new HypreBoomerAMG;
}
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(1);
if (prec) { cg.SetPreconditioner(*prec); }
cg.SetOperator(*A);
cg.Mult(B, X);
delete prec;
// 14. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
std::string filename = to_string(num_procs) + "_" + to_string(nfiles) + "_";
{
double t1;
t1 = MPI_Wtime();
x.Save(filename.c_str(), nfiles);
double t2 = MPI_Wtime();
double write_time = t2 - t1;
double average_write_time;
MPI_Reduce(&write_time, &average_write_time, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
{
std::cout << "Average write time: " << average_write_time / num_procs << " for "
<< nfiles << " files and " << num_procs << " ranks\n";
}
}
{
double t1;
t1 = MPI_Wtime();
ParGridFunction temp_gf(fespace, filename.c_str());
double t2 = MPI_Wtime();
double read_time = t2 - t1;
double average_read_time;
MPI_Reduce(&read_time, &average_read_time, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
{
std::cout << "Average read time: " << average_read_time / num_procs << " for "
<< nfiles << " files and " << num_procs << " ranks\n";
}
}
// 17. Free the used memory.
delete a;
delete b;
delete fespace;
if (order > 0) { delete fec; }
delete pmesh;
MPI_Finalize();
return 0;
}
+8 -2
View File
@@ -22,9 +22,10 @@ MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 ex17\
ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25
ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27
PAR_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p ex12p\
ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p ex25p
ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p ex25p\
ex26p ex27p
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
@@ -103,6 +104,10 @@ ex15-test-seq: ex15
@$(call mfem-test,$<,, Serial example,-e 1)
ex15p-test-par: ex15p
@$(call mfem-test,$<, $(RUN_MPI), Parallel example,-e 1)
ex27-test-seq: ex27
@$(call mfem-test,$<,, Serial example,-dg)
ex27p-test-par: ex27p
@$(call mfem-test,$<, $(RUN_MPI), Parallel example,-dg)
# Testing: optional tests
ifeq ($(MFEM_USE_STRUMPACK),YES)
ex11p-test-strumpack: ex11p
@@ -128,6 +133,7 @@ clean-exec:
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.*
@rm -f ex9.mesh ex9-mesh.* ex9-init.* ex9-final.*
@rm -f deformed.* velocity.* elastic_energy.* mode_*
@rm -f ex5-p-*.bp ex9-p-*.bp ex12-p-*.bp ex16-p-*.bp
@rm -f ex16.mesh ex16-mesh.* ex16-init.* ex16-final.*
@rm -f vortex-mesh.* vortex.mesh vortex-?-init.* vortex-?-final.*
@rm -f deformation.* pressure.*
+906
View File
@@ -0,0 +1,906 @@
// MFEM Example 9
//
// Compile with: make serial_nogpu
//
// Description: This code solves the time-dependent advection-diffusion
// equation:
// \frac(\partial u}{\partial t}
// = \mathbf{a} \cdot \Nabla u - \nu \Nabla^2 u
// where a is a given advection velocity, \nu is the diffusion
// parameter, and u0(x) = u(0,x) is a given initial condition.
//
// The demonstrates explicit time marching with H1 elements of
// arbitrary order. Periodic boundary conditions are used through
// periodic meshes. GLVis can be used for visualization of a
// time-evolving solution.
#include <fstream>
#include <iostream>
#include <algorithm>
#include "mfem.hpp"
#include "mpi.h"
using namespace std;
using namespace mfem;
/** A time-dependent operator for the right-hand side of the ODE. The weak
form of du/dt = -a.grad(u) + nu Delta(u) is M du/dt = K u + b, where M and
K are the mass and advection-diffusion matrices, and b describes the flow
on the boundary. This can be written as a general ODE,
du/dt = M^{-1} (K u + b), and this class is used to evaluate the right-hand
side. */
class AdvectionDiffusionEvolution : public mfem::TimeDependentOperator
{
public:
/// \param[in] M - bilinear form for mass matrix
/// \param[in] K - bilinear form for stiffness matrix
/// \param[in] b - load vector
AdvectionDiffusionEvolution(mfem::BilinearForm &M, mfem::BilinearForm &K,
const mfem::Vector &b);
/// Perform the action of the operator: y = k = f(x, t), where k solves
/// Compute k = M^-1(Kx + l)
void Mult(const mfem::Vector &x, mfem::Vector &y) const override;
/// Solve the implicit equation: k = f(x + dt k, t), for the unknown k at
/// the current time t.
void ImplicitSolve(const double dt, const mfem::Vector &x,
mfem::Vector &k) override;
virtual ~AdvectionDiffusionEvolution();
private:
mfem::BilinearForm &M, &K;
const mfem::Vector &b;
/// solver for inverting mass matrix for explicit time-marching
std::unique_ptr<mfem::Solver> M_prec;
mfem::CGSolver M_solver;
/// solver for implicit time-marching
mfem::GSSmoother prec;
mfem::GMRESSolver linear_solver;
mfem::NewtonSolver newton;
mutable mfem::Vector z;
/// pointer-to-implementation idiom
/// Hides implementation details of this operator
class SystemOperator;
/// Operator that combines the linear spatial discretization with
/// the load vector into one operator used for implicit solves
std::unique_ptr<SystemOperator> combined_oper;
/// sets the state and dt for the combined operator
/// \param[in] dt - time increment
/// \param[in] x - the current state
void setOperParameters(double dt, const mfem::Vector *x);
};
class PAJacobianOperator : public mfem::Operator
{
public:
PAJacobianOperator(mfem::ParBilinearForm &_mass,
mfem::ParBilinearForm &_stiff);
/// Compute r = J@k = M@k + dt*K@k
/// \param[in] k - dx/dt
/// \param[out] r - J@k = M@k + dt*K@k
void Mult(const mfem::Vector &k, mfem::Vector &r) const override;
/// Set current dt values - needed to compute action of Jacobian.
void setParameters(double dt);
private:
mfem::ParBilinearForm &mass;
mfem::ParBilinearForm &stiff;
double dt;
};
class ParSystemOperator : public mfem::Operator
{
public:
/// Nonlinear operator of the form that combines the mass, res, stiff,
/// and load elements for implicit/explicit ODE integration
/// \param[in] ess_bdr - array of boundaries attributes marked essential
/// \param[in] mass - bilinear form for mass matrix (not owned)
/// \param[in] res - nonlinear residual operator (not owned)
/// \param[in] stiff - bilinear form for stiffness matrix (not owned)
/// \param[in] load - load vector (not owned)
/// \param[in] a - used to move the spatial residual to the rhs
ParSystemOperator(mfem::ParBilinearForm &_mass,
mfem::ParBilinearForm &_stiff);
/// Compute r = M@k + K@(x+dt*k)
/// (with `@` denoting matrix-vector multiplication)
/// \param[in] k - dx/dt
/// \param[out] r - the residual
/// \note the signs on each operator must be accounted for elsewhere
void Mult(const mfem::Vector &k, mfem::Vector &r) const override;
/// Compute J = M + dt * K
/// \param[in] k - dx/dt
mfem::Operator &GetGradient(const mfem::Vector &k) const override;
/// Set current dt and x values - needed to compute action and Jacobian.
void setParameters(double _dt, const mfem::Vector *_x);
~ParSystemOperator();
private:
mfem::ParBilinearForm &mass;
mfem::ParBilinearForm &stiff;
mutable mfem::HypreParMatrix *jacobian, *stiff_jacobian;
double dt;
const mfem::Vector *x;
mutable mfem::Vector work, work2;
std::unique_ptr<PAJacobianOperator> pa_jac;
};
/** A time-dependent operator for the right-hand side of the ODE. The weak
form of du/dt = -a.grad(u) + nu Delta(u) is M du/dt = K u + b, where M and
K are the mass and advection-diffusion matrices, and b describes the flow
on the boundary. This can be written as a general ODE,
du/dt = M^{-1} (K u + b), and this class is used to evaluate the right-hand
side. */
class ParAdvectionDiffusionEvolution : public mfem::TimeDependentOperator
{
public:
/// \param[in] M - parallel bilinear form for mass matrix
/// \param[in] K - parallel bilinear form for stiffness matrix
ParAdvectionDiffusionEvolution(mfem::ParBilinearForm &M,
mfem::ParBilinearForm &K);
/// Perform the action of the operator: y = k = f(x, t), where k solves
/// Compute k = M^-1(Kx + l)
void Mult(const mfem::Vector &x, mfem::Vector &y) const override;
/// Solve the implicit equation: k = f(x + dt k, t), for the unknown k at
/// the current time t.
void ImplicitSolve(const double dt, const mfem::Vector &x,
mfem::Vector &k) override;
virtual ~ParAdvectionDiffusionEvolution();
private:
mfem::OperatorHandle M_;
mfem::ParBilinearForm &M, &K;
/// solver for inverting mass matrix for explicit time-marching
std::unique_ptr<mfem::Solver> M_prec;
mfem::CGSolver M_solver;
/// solver for implicit time-marching
mfem::Solver *prec;
mfem::GMRESSolver linear_solver;
mfem::NewtonSolver newton;
mfem::Vector diag;
mutable mfem::Vector z, work, work2;
/// pointer-to-implementation idiom
/// Hides implementation details of this operator
/// Operator that combines the linear spatial discretization with
/// the load vector into one operator used for implicit solves
std::unique_ptr<ParSystemOperator> combined_oper;
/// sets the state and dt for the combined operator
/// \param[in] dt - time increment
/// \param[in] x - the current state
void setOperParameters(double dt, const mfem::Vector *x);
};
// Choice for the problem setup. The fluid velocity, initial condition and
// inflow boundary condition are chosen based on this parameter.
int problem;
// Velocity coefficient
void velocity_function(const Vector &X, Vector &v);
// Initial condition
double u0_function(const Vector &X);
// Inflow boundary condition
double inflow_function(const Vector &X, const double t);
// Mesh bounding box
Vector bb_min, bb_max;
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
problem = 3;
const char *mesh_file = "../data/periodic-square.mesh";
int ser_ref_levels = 0;
int par_ref_levels = 0;
int order = 3;
const char *device_config = "cpu";
int ode_solver_type = 22;
double t_final = 3 * 2*M_PI;
double dt = 0.01;
bool glvis = false;
bool paraview = false;
int vis_steps = 5;
double nu_val = 0.001;
int precision = 8;
cout.precision(precision);
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&problem, "-p", "--problem",
"Problem setup to use. See options in velocity_function().");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
"Number of times to refine the mesh uniformly in parallel.");
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver: 1 - Forward Euler,\n\t"
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6.");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
"Time step.");
args.AddOption(&glvis, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&paraview, "-paraview", "--paraview-datafiles", "-no-paraview",
"--no-paraview-datafiles",
"Save data files for ParaView (paraview.org) visualization.");
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
"Visualize every n-th timestep.");
args.AddOption(&nu_val, "-nu", "--nu-value",
"Value for \nu, the parameter that controls diffusion.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
std::cout << "Num ranks: " << num_procs << "\n";
args.PrintOptions(cout);
}
Device device(device_config);
if (myid == 0) { device.Print(); }
// 3. Read the serial mesh from the given mesh file on all processors. We can
// handle geometrically periodic meshes in this code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 5. Refine the mesh in serial to increase the resolution. In this example
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
// a command-line parameter. If the mesh is of NURBS type, we convert it
// to a (piecewise-polynomial) high-order mesh.
for (int lev = 0; lev < ser_ref_levels; lev++)
{
mesh->UniformRefinement();
}
mesh->GetBoundingBox(bb_min, bb_max, max(order, 1));
// 6. Define the 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 = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
for (int lev = 0; lev < par_ref_levels; lev++)
{
pmesh->UniformRefinement();
}
// 7. Define the finite element space of the given
// polynomial order on the refined mesh.
H1_FECollection fec(order, dim, BasisType::GaussLobatto);
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, &fec);
HYPRE_Int global_vSize = fes->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of unknowns: " << global_vSize << endl;
}
// 8. Set up and assemble the bilinear and linear forms corresponding to the
// CG discretization.
/// negative to move the diffusion terms to the right side
ConstantCoefficient nu(-nu_val);
ConstantCoefficient one(1.0);
VectorFunctionCoefficient velocity(dim, velocity_function);
FunctionCoefficient u0(u0_function);
ParBilinearForm *m_pa = new ParBilinearForm(fes);
ParBilinearForm *k_pa = new ParBilinearForm(fes);
m_pa->SetAssemblyLevel(AssemblyLevel::PARTIAL);
k_pa->SetAssemblyLevel(AssemblyLevel::PARTIAL);
/// create mass matrix
m_pa->AddDomainIntegrator(new MassIntegrator(one));
/// add advection terms to stiffness matrix
k_pa->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
/// add diffusion terms to stiffness matrix
k_pa->AddDomainIntegrator(new DiffusionIntegrator(nu));
m_pa->Assemble();
int skip_zeros = 0;
k_pa->Assemble(skip_zeros);
m_pa->Finalize();
k_pa->Finalize(skip_zeros);
ParBilinearForm *m = new ParBilinearForm(fes);
ParBilinearForm *k = new ParBilinearForm(fes);
/// create mass matrix
m->AddDomainIntegrator(new MassIntegrator);
/// add advection terms to stiffness matrix
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
/// add diffusion terms to stiffness matrix
k->AddDomainIntegrator(new DiffusionIntegrator(nu));
m->Assemble();
k->Assemble(skip_zeros);
m->Finalize();
k->Finalize(skip_zeros);
ParGridFunction *u = new ParGridFunction(fes);
u->UseDevice(true);
u->ProjectCoefficient(u0);
HypreParVector *U = u->GetTrueDofs();
ParSystemOperator pso(*m, *k);
ParSystemOperator pso_pa(*m_pa, *k_pa);
pso.setParameters(dt, U);
pso_pa.setParameters(dt, U);
MPI_Barrier(MPI_COMM_WORLD);
mfem::Vector pso_r(U->Size());
double t1 = MPI_Wtime();
pso.Mult(*U, pso_r);
double t2 = MPI_Wtime();
double fa_mult_time = t2 - t1;
double average_fa_mult_time;
MPI_Reduce(&fa_mult_time, &average_fa_mult_time, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
std::cout << "FA Mult time: " << average_fa_mult_time / num_procs << endl;
MPI_Barrier(MPI_COMM_WORLD);
mfem::Vector pso_pa_r(U->Size());
double t3 = MPI_Wtime();
pso_pa.Mult(*U, pso_pa_r);
double t4 = MPI_Wtime();
double pa_mult_time = t4 - t3;
double average_pa_mult_time;
MPI_Reduce(&pa_mult_time, &average_pa_mult_time, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
std::cout << "FA Mult time: " << average_pa_mult_time / num_procs << endl;
double local_mult_speedup = (t2-t1) / (t4-t3);
double global_mult_speedup;
MPI_Reduce(&local_mult_speedup, &global_mult_speedup, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
std::cout << "PA mult speedup: " << global_mult_speedup / num_procs << endl;
mfem::Vector diff_r(pso_pa_r);
diff_r -= pso_r;
// std::cout << "r diff: " << diff_r.Norml2() << std::endl;
mfem::Operator &pso_jac = pso.GetGradient(*U);
mfem::Operator &pso_pa_jac = pso_pa.GetGradient(*U);
MPI_Barrier(MPI_COMM_WORLD);
mfem::Vector pso_jac_r(U->Size());
double t5 = MPI_Wtime();
pso_jac.Mult(*U, pso_jac_r);
double t6 = MPI_Wtime();
double fa_jac_mult_time = t6-t5;
double average_fa_jac_time;
MPI_Reduce(&fa_jac_mult_time, &average_fa_jac_time, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
std::cout << "FA Jac Mult time: " << average_fa_jac_time / num_procs << endl;
MPI_Barrier(MPI_COMM_WORLD);
mfem::Vector pso_pa_jac_r(U->Size());
double t7 = MPI_Wtime();
pso_pa_jac.Mult(*U, pso_pa_jac_r);
double t8 = MPI_Wtime();
double pa_jac_mult_time = t8-t7;
double average_pa_jac_time;
MPI_Reduce(&pa_jac_mult_time, &average_pa_jac_time, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
std::cout << "PA Jac Mult time: " << average_pa_jac_time / num_procs << endl;
double local_jac_speedup = (t6-t5) / (t8-t7);
double global_jac_speedup;
MPI_Reduce(&local_jac_speedup, &global_jac_speedup, 1,
MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (myid == 0)
std::cout << "PA Jac mult speedup: " << global_jac_speedup / num_procs << endl;
// 13. Free the used memory.
delete U;
delete u;
delete k;
delete m;
delete fes;
delete pmesh;
MPI_Finalize();
return 0;
}
// Velocity coefficient
void velocity_function(const Vector &x, Vector &v)
{
int dim = x.Size();
// map to the reference [-1,1] domain
Vector X(dim);
for (int i = 0; i < dim; i++)
{
double center = (bb_min[i] + bb_max[i]) * 0.5;
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
}
switch (problem)
{
case 3:
{
// Translations in 1D, 2D, and 3D
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
break;
}
break;
}
case 1:
case 2:
{
// Clockwise rotation in 2D around the origin
const double w = M_PI/2;
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
}
break;
}
case 0:
{
// Clockwise twisting rotation in 2D around the origin
const double w = M_PI/2;
double d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
d = d*d;
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
}
break;
}
}
}
// Initial condition
double u0_function(const Vector &x)
{
int dim = x.Size();
// map to the reference [-1,1] domain
Vector X(dim);
for (int i = 0; i < dim; i++)
{
double center = (bb_min[i] + bb_max[i]) * 0.5;
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
}
switch (problem)
{
case 0:
case 1:
{
switch (dim)
{
case 1:
return exp(-40.*pow(X(0)-0.5,2));
case 2:
case 3:
{
double rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
if (dim == 3)
{
const double s = (1. + 0.25*cos(2*M_PI*X(2)));
rx *= s;
ry *= s;
}
return ( erfc(w*(X(0)-cx-rx))*erfc(-w*(X(0)-cx+rx)) *
erfc(w*(X(1)-cy-ry))*erfc(-w*(X(1)-cy+ry)) )/16;
}
}
}
case 2:
{
double x_ = X(0), y_ = X(1), rho, phi;
rho = hypot(x_, y_);
phi = atan2(y_, x_);
return pow(sin(M_PI*rho),2)*sin(3*phi);
}
case 3:
{
const double f = M_PI;
return sin(f*X(0))*sin(f*X(1));
}
}
return 0.0;
}
// Inflow boundary condition (zero for the problems considered in this example)
double inflow_function(const Vector &x, const double t)
{
switch (problem)
{
case 0:
case 1:
case 2:
case 3: return 0.0;
}
return 0.0;
}
class AdvectionDiffusionEvolution::SystemOperator : public mfem::Operator
{
public:
/// Nonlinear operator of the form that combines the mass, res, stiff,
/// and load elements for implicit/explicit ODE integration
/// \param[in] mass - bilinear form for mass matrix (not owned)
/// \param[in] res - nonlinear residual operator (not owned)
/// \param[in] stiff - bilinear form for stiffness matrix (not owned)
/// \param[in] load - load vector (not owned)
/// \param[in] a - used to move the spatial residual to the rhs
SystemOperator(BilinearForm &_mass, BilinearForm &_stiff,
const mfem::Vector &b)
: Operator(_mass.Height()), mass(_mass), stiff(_stiff),
load(b), Jacobian(NULL), dt(0.0), x(NULL), work(height)
{ }
/// Compute r = M@k + K@(x+dt*k) + l
/// (with `@` denoting matrix-vector multiplication)
/// \param[in] k - dx/dt
/// \param[out] r - the residual
/// \note the signs on each operator must be accounted for elsewhere
void Mult(const mfem::Vector &k, mfem::Vector &r) const override
{
/// work = x+dt*k = x+dt*dx/dt = x+dx
add(1.0, *x, dt, k, work);
r = 0.0;
stiff.AddMult(work, r);
r += load;
mass.AddMult(k, r, -1.0);
}
/// Compute J = M + dt * K
/// \param[in] k - dx/dt
mfem::Operator &GetGradient(const mfem::Vector &k) const override
{
delete Jacobian;
Jacobian = Add(-1.0, mass.SpMat(), dt, stiff.SpMat());
return *Jacobian;
}
/// Set current dt and x values - needed to compute action and Jacobian.
void setParameters(double _dt, const mfem::Vector *_x)
{
dt = _dt;
x = _x;
};
~SystemOperator() {delete Jacobian;};
private:
BilinearForm &mass;
BilinearForm &stiff;
const mfem::Vector &load;
mutable mfem::SparseMatrix *Jacobian;
double dt;
const mfem::Vector *x;
mutable mfem::Vector work, work2;
};
AdvectionDiffusionEvolution::AdvectionDiffusionEvolution(
BilinearForm &_M, BilinearForm &_K, const Vector &_b)
: TimeDependentOperator(_M.Height()), M(_M), K(_K), b(_b),
z(_M.Height())
{
bool pa = M.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
Array<int> ess_tdof_list;
if (pa)
{
M_prec.reset(new OperatorJacobiSmoother(M, ess_tdof_list));
M_solver.SetOperator(M);
}
else
{
M_prec.reset(new DSmoother(M.SpMat()));
M_solver.SetOperator(M.SpMat());
}
combined_oper.reset(new SystemOperator(_M, _K, _b));
M_solver.SetPreconditioner(*M_prec);
M_solver.iterative_mode = false;
M_solver.SetRelTol(1e-9);
M_solver.SetAbsTol(0.0);
M_solver.SetMaxIter(100);
M_solver.SetPrintLevel(0);
linear_solver.iterative_mode = true;
linear_solver.SetRelTol(1e-12);
linear_solver.SetAbsTol(0.0);
linear_solver.SetMaxIter(100);
linear_solver.SetPrintLevel(0);
linear_solver.SetPreconditioner(prec);
newton.iterative_mode = false;
newton.SetRelTol(1e-9);
newton.SetAbsTol(0.0);
newton.SetMaxIter(100);
newton.SetPrintLevel(-1);
newton.SetSolver(linear_solver);
newton.SetOperator(*combined_oper);
}
void AdvectionDiffusionEvolution::Mult(const Vector &x, Vector &y) const
{
// y = M^{-1} (K x + b)
K.Mult(x, z);
z += b;
M_solver.Mult(z, y);
}
void AdvectionDiffusionEvolution::ImplicitSolve(const double dt,
const Vector &x,
Vector &k)
{
setOperParameters(dt, &x);
Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
newton.Mult(zero, k);
MFEM_VERIFY(newton.GetConverged(), "Newton solver did not converge!");
}
void AdvectionDiffusionEvolution::setOperParameters(double dt,
const mfem::Vector *x)
{
combined_oper->setParameters(dt, x);
}
AdvectionDiffusionEvolution::~AdvectionDiffusionEvolution() {}
PAJacobianOperator::PAJacobianOperator(ParBilinearForm &_mass, ParBilinearForm &_stiff)
: Operator(_mass.ParFESpace()->GetTrueVSize()), mass(_mass), stiff(_stiff),
dt(0.0) { }
void PAJacobianOperator::Mult(const mfem::Vector &k, mfem::Vector &r) const
{
r.UseDevice(true);
r = 0.0;
stiff.TrueAddMult(k, r, dt);
mass.TrueAddMult(k, r, -1.0);
}
void PAJacobianOperator::setParameters(const double _dt)
{
dt = _dt;
};
ParSystemOperator::ParSystemOperator(ParBilinearForm &_mass, ParBilinearForm &_stiff)
: Operator(_mass.ParFESpace()->GetTrueVSize()), mass(_mass), stiff(_stiff),
jacobian(NULL), stiff_jacobian(NULL), dt(0.0), x(NULL),
work(height)
{
pa_jac.reset(new PAJacobianOperator(mass, stiff));
}
/// Compute r = M@k + K@(x+dt*k)
/// (with `@` denoting matrix-vector multiplication)
/// \param[in] k - dx/dt
/// \param[out] r - the residual
/// \note the signs on each operator must be accounted for elsewhere
void ParSystemOperator::Mult(const mfem::Vector &k, mfem::Vector &r) const
{
r = 0.0;
work.UseDevice(true);
work = 0.0;
/// work = x+dt*k = x+dt*dx/dt = x+dx
if (x)
{
add(1.0, *x, dt, k, work);
}
stiff.TrueAddMult(work, r);
mass.TrueAddMult(k, r, -1.0);
}
/// Compute J = M + dt * K
/// \param[in] k - dx/dt
mfem::Operator &ParSystemOperator::GetGradient(const mfem::Vector &k) const
{
bool mass_pa = mass.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
bool stiff_pa = stiff.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
if (mass_pa && stiff_pa)
{
return *pa_jac.get();
}
else
{
delete stiff_jacobian;
delete jacobian;
jacobian = mass.ParallelAssemble();
*jacobian *= -1.0; //alpha;
stiff_jacobian = stiff.ParallelAssemble();
jacobian->Add(dt, *stiff_jacobian);
return *jacobian;
}
}
/// Set current dt and x values - needed to compute action and Jacobian.
void ParSystemOperator::setParameters(const double _dt, const mfem::Vector *_x)
{
dt = _dt;
x = _x;
pa_jac->setParameters(_dt);
};
ParSystemOperator::~ParSystemOperator()
{
delete jacobian;
delete stiff_jacobian;
};
ParAdvectionDiffusionEvolution::ParAdvectionDiffusionEvolution(
ParBilinearForm &_M, ParBilinearForm &_K)
: TimeDependentOperator(_M.ParFESpace()->GetTrueVSize()), M(_M), K(_K), z(_M.Height())
{
bool mass_pa = M.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
bool stiff_pa = K.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
Array<int> ess_tdof_list;
M_solver = CGSolver(MPI_COMM_WORLD);
if (mass_pa)
{
M_prec.reset(new OperatorJacobiSmoother(M, ess_tdof_list));
M_solver.SetOperator(M);
}
else
{
M_.Reset(_M.ParallelAssemble(), true);
// M_prec.reset(new HypreSmoother());
// M_solver.SetOperator(M.As<HypreParMatrix>());
HypreParMatrix &M_mat = *M_.As<HypreParMatrix>();
// HypreParMatrix &K_mat = *K.As<HypreParMatrix>();
M_prec.reset(new HypreSmoother(M_mat, HypreSmoother::Jacobi));
}
combined_oper.reset(new ParSystemOperator(_M, _K));
M_solver.SetPreconditioner(*M_prec);
M_solver.iterative_mode = false;
M_solver.SetRelTol(1e-9);
M_solver.SetAbsTol(0.0);
M_solver.SetMaxIter(100);
M_solver.SetPrintLevel(0);
if (mass_pa && stiff_pa)
{
diag.UseDevice(true);
diag.SetSize(M.ParFESpace()->GetTrueVSize());
diag = 0.0;
work.UseDevice(true);
work2.UseDevice(true);
work.SetSize(M.ParFESpace()->GetTrueVSize());
work2.SetSize(M.ParFESpace()->GetTrueVSize());
work = 0.0;
work2 = 0.0;
M.AssembleDiagonal(work);
ParBilinearForm k(M.ParFESpace());
ConstantCoefficient nu(-0.01);
k.AddDomainIntegrator(new mfem::DiffusionIntegrator(nu));
k.SetAssemblyLevel(AssemblyLevel::PARTIAL);
k.Assemble(0);
k.Finalize(0);
k.AssembleDiagonal(work2);
double dt = 0.1;
add(-1.0, work, dt, work2, diag);
prec = new OperatorChebyshevSmoother(combined_oper.get(), diag,
ess_tdof_list, 5,
M.ParFESpace()->GetComm());
}
else
{
prec = new HypreSmoother();
}
linear_solver = GMRESSolver(MPI_COMM_WORLD);
linear_solver.iterative_mode = true;
linear_solver.SetRelTol(1e-12);
linear_solver.SetAbsTol(0.0);
linear_solver.SetMaxIter(2000);
linear_solver.SetPrintLevel(0);
linear_solver.SetPreconditioner(*prec);
linear_solver.SetKDim(2000);
newton.iterative_mode = true;
newton.SetRelTol(1e-9);
newton.SetAbsTol(0.0);
newton.SetMaxIter(10);
newton.SetPrintLevel(-1);
newton.SetSolver(linear_solver);
newton.SetOperator(*combined_oper);
}
void ParAdvectionDiffusionEvolution::Mult(const Vector &x, Vector &y) const
{
// y = M^{-1} (K x + b)
K.Mult(x, z);
M_solver.Mult(z, y);
}
void ParAdvectionDiffusionEvolution::ImplicitSolve(const double dt,
const Vector &x,
Vector &k)
{
setOperParameters(dt, &x);
Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
newton.Mult(zero, k);
MFEM_VERIFY(newton.GetConverged(), "Newton solver did not converge!");
}
void ParAdvectionDiffusionEvolution::setOperParameters(const double dt,
const mfem::Vector *x)
{
combined_oper->setParameters(dt, x);
}
ParAdvectionDiffusionEvolution::~ParAdvectionDiffusionEvolution() {delete prec;}
+11
View File
@@ -36,9 +36,11 @@ set(SRCS
intrules.cpp
linearform.cpp
lininteg.cpp
multigrid.cpp
nonlinearform.cpp
nonlinearform_ext.cpp
nonlininteg.cpp
fespacehierarchy.cpp
nonlininteg_vectorconvection.cpp
quadinterpolator.cpp
quadinterpolator_face.cpp
@@ -47,6 +49,7 @@ set(SRCS
tmop.cpp
tmop_tools.cpp
gslib.cpp
transfer.cpp
)
set(HDRS
@@ -68,12 +71,14 @@ set(HDRS
intrules.hpp
linearform.hpp
lininteg.hpp
multigrid.hpp
nonlinearform.hpp
nonlinearform_ext.hpp
nonlininteg.hpp
quadinterpolator.hpp
quadinterpolator_face.hpp
restriction.hpp
fespacehierarchy.hpp
staticcond.hpp
tbilinearform.hpp
tbilininteg.hpp
@@ -86,6 +91,7 @@ set(HDRS
tmop.hpp
tmop_tools.hpp
gslib.hpp
transfer.hpp
)
if (MFEM_USE_SIDRE)
@@ -98,6 +104,11 @@ if (MFEM_USE_CONDUIT)
list(APPEND HDRS conduitdatacollection.hpp)
endif()
if (MFEM_USE_ADIOS2)
list(APPEND SRCS adios2datacollection.cpp)
list(APPEND HDRS adios2datacollection.hpp)
endif()
if (MFEM_USE_MPI)
list(APPEND SRCS
pbilinearform.cpp
+90
View File
@@ -0,0 +1,90 @@
// Copyright (c) 2010-2020, 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.
//
// Created on: Jan 7, 2020
// Author: William F Godoy godoywf@ornl.gov
// adios2: Adaptable Input/Output System https://github.com/ornladios/ADIOS2
#include "adios2datacollection.hpp"
namespace mfem
{
#ifdef MFEM_USE_MPI
ADIOS2DataCollection::ADIOS2DataCollection(MPI_Comm comm,
const std::string& collection_name, Mesh* mesh,
const std::string engine_type) : DataCollection(collection_name, mesh),
stream( new adios2stream(name, adios2stream::openmode::out, comm, engine_type) )
{
SetMesh(mesh);
}
#else
ADIOS2DataCollection::ADIOS2DataCollection(
const std::string& collection_name, Mesh* mesh,
const std::string engine_type): DataCollection(collection_name, mesh),
stream( new adios2stream(name, adios2stream::openmode::out, engine_type) )
{
SetMesh(mesh);
}
#endif
ADIOS2DataCollection::~ADIOS2DataCollection()
{
stream->Close();
}
void ADIOS2DataCollection::Save()
{
stream->BeginStep();
// only save mesh once (moving mesh, not yet supported)
if (stream->CurrentStep() == 0)
{
if (mesh == nullptr)
{
const std::string error_message =
"MFEM ADIOS2DataCollection Save error: Mesh is null. Please call SetMesh before Save\n";
mfem_error(error_message.c_str());
}
stream->Print(*mesh);
}
// reduce footprint
if (myid == 0)
{
stream->SetTime(time);
stream->SetCycle(cycle);
}
for (const auto& field : field_map)
{
const std::string& variable_name = field.first;
field.second->Save(*stream.get(), variable_name);
}
stream->EndStep();
}
void ADIOS2DataCollection::SetParameter(const std::string key,
const std::string value) noexcept
{
stream->SetParameter(key, value);
}
void ADIOS2DataCollection::SetLevelsOfDetail(const int levels_of_detail)
noexcept
{
stream->SetRefinementLevel(levels_of_detail);
}
} //end namespace mfem
+88
View File
@@ -0,0 +1,88 @@
// Copyright (c) 2010-2020, 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.
//
// Created on: Jan 7, 2020
// Author: William F Godoy godoywf@ornl.gov
// adios2: Adaptable Input/Output System https://github.com/ornladios/ADIOS2
#ifndef MFEM_ADIOS2DATACOLLECTION
#define MFEM_ADIOS2DATACOLLECTION
#include "../config/config.hpp"
#include "../general/adios2stream.hpp"
#include "datacollection.hpp"
#include <memory> // std::unique_ptr
#include <string>
namespace mfem
{
class ADIOS2DataCollection : public DataCollection
{
public:
#ifdef MFEM_USE_MPI
/**
* Parallel constructor. Important: scope of this object must be within
* MPI_Init and MPI_Finalize otherwise. The destructor will call the Close
* function. Either object must live in a try/catch block (inside try) or use
* raw pointers calling delete before MPI_Finalize.
* @param comm MPI communicator setting the datacollection domain
* @param collection_name unique name for saving data
* @param mesh can be set at the constructor level or later by calling
* SetMesh()
* @param engine_type adios2 engine type
*/
ADIOS2DataCollection(MPI_Comm comm, const std::string& collection_name,
Mesh* mesh = nullptr,
const std::string engine_type = "BPFile");
#else
/**
* Serial constructor
* @param collection_name unique name for saving data
* @param mesh can be set at the constructor level or later by calling
* SetMesh()
* @param engine_type adios2 engine type
* @throws std::invalid_argument (user input error) or std::runtime_error
* (system error)
*/
ADIOS2DataCollection(const std::string& collection_name, Mesh* mesh = nullptr,
const std::string engine_type = "BPFile");
#endif
virtual ~ADIOS2DataCollection();
/** Save the collection */
virtual void Save();
/**
* Pass a parameter unique to adios2datacollection
* For available parameters:
* See https://adios2.readthedocs.io/en/latest/engines/engines.html
* The most common is: key=SubStreams value=1 to nprocs (MPI processes)
* @param key parameter key
* @param value parameter value
*/
void SetParameter(const std::string key, const std::string value) noexcept;
/**
* Sets the levels of detail for the global grid refinement
* @param levels_of_detail (default = 1)
*/
void SetLevelsOfDetail(const int levels_of_detail) noexcept;
private:
std::unique_ptr<adios2stream> stream;
};
} // namespace mfem
#endif /* MFEM_ADIOS2DATACOLLECTION */
+11 -2
View File
@@ -467,8 +467,17 @@ void BilinearForm::Assemble(int skip_zeros)
const FiniteElement &be = *fes->GetBE(i);
fes -> GetBdrElementVDofs (i, vdofs);
eltrans = fes -> GetBdrElementTransformation (i);
bbfi[0]->AssembleElementMatrix(be, *eltrans, elmat);
for (int k = 1; k < bbfi.Size(); k++)
int k = 0;
for (; k < bbfi.Size(); k++)
{
if (bbfi_marker[k] &&
(*bbfi_marker[k])[bdr_attr-1] == 0) { continue; }
bbfi[k]->AssembleElementMatrix(be, *eltrans, elmat);
k++;
break;
}
for (; k < bbfi.Size(); k++)
{
if (bbfi_marker[k] &&
(*bbfi_marker[k])[bdr_attr-1] == 0) { continue; }
+3 -3
View File
@@ -151,8 +151,8 @@ public:
/** This method must be called before assembly. */
void SetAssemblyLevel(AssemblyLevel assembly_level);
/// Get the assembly level
AssemblyLevel GetAssemblyLevel() {return assembly;}
/// Returns the assembly level
AssemblyLevel GetAssemblyLevel() const { return assembly; }
/** Enable the use of static condensation. For details see the description
for class StaticCondensation in fem/staticcond.hpp This method should be
@@ -530,7 +530,7 @@ public:
/// (DEPRECATED) Return the FE space associated with the BilinearForm.
/** @deprecated Use FESpace() instead. */
FiniteElementSpace *GetFES() { return fes; }
MFEM_DEPRECATED FiniteElementSpace *GetFES() { return fes; }
/// Return the FE space associated with the BilinearForm.
FiniteElementSpace *FESpace() { return fes; }
+25 -31
View File
@@ -2135,17 +2135,17 @@ void VectorDiffusionIntegrator::AssembleElementMatrix(
ElementTransformation &Trans,
DenseMatrix &elmat)
{
int dim = el.GetDim();
int dof = el.GetDof();
const int dim = el.GetDim();
const int dof = el.GetDof();
const int sdim = Trans.GetSpaceDim();
const bool square = (dim == sdim);
double w;
double norm;
elmat.SetSize(sdim * dof);
elmat.SetSize (dim * dof);
Jinv. SetSize (dim);
dshape.SetSize (dof, dim);
gshape.SetSize (dof, dim);
pelmat.SetSize (dof);
dshape.SetSize(dof, dim);
dshapedxt.SetSize(dof, sdim);
pelmat.SetSize(dof);
const IntegrationRule *ir = IntRule;
if (ir == NULL)
@@ -2163,35 +2163,29 @@ void VectorDiffusionIntegrator::AssembleElementMatrix(
}
elmat = 0.0;
pelmat = 0.0;
for (int i = 0; i < ir -> GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
el.CalcDShape (ip, dshape);
Trans.SetIntPoint (&ip);
norm = ip.weight * Trans.Weight();
CalcInverse (Trans.Jacobian(), Jinv);
Mult (dshape, Jinv, gshape);
MultAAt (gshape, pelmat);
if (Q)
w = Trans.Weight();
w = ip.weight / (square ? w : w*w*w);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape, Trans.AdjugateJacobian(), dshapedxt);
if (Q) { w *= Q -> Eval (Trans, ip); }
AddMult_a_AAt(w, dshapedxt, pelmat);
}
for (int d = 0; d < sdim; d++)
{
for (int k = 0; k < dof; k++)
{
norm *= Q -> Eval (Trans, ip);
}
pelmat *= norm;
for (int d = 0; d < dim; d++)
{
for (int k = 0; k < dof; k++)
for (int l = 0; l < dof; l++)
{
elmat (dof*d+k, dof*d+l) += pelmat (k, l);
}
for (int l = 0; l < dof; l++)
{
elmat(dof*d+k, dof*d+l) = pelmat(k, l);
}
}
}
}
+13 -5
View File
@@ -453,6 +453,16 @@ public:
ElementTransformation &Trans,
DenseMatrix &elmat);
/// Support for use in BilinearForm. Can be used only when appropriate.
/** Appropriate use cases are classes derived from
MixedScalarVectorIntegrator where the trial and test spaces can be the
same. Examples of such classes are: MixedVectorDivergenceIntegrator,
MixedScalarWeakDivergenceIntegrator, etc. */
virtual void AssembleElementMatrix(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &elmat)
{ AssembleElementMatrix2(fe, fe, Trans, elmat); }
protected:
MixedScalarVectorIntegrator(VectorCoefficient &vq, bool _transpose = false,
@@ -2405,14 +2415,12 @@ protected:
// PA extension
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, dofs1D, quad1D;
int dim, sdim, ne, dofs1D, quad1D;
Vector pa_data;
private:
DenseMatrix Jinv;
DenseMatrix dshape;
DenseMatrix gshape;
DenseMatrix pelmat;
DenseMatrix dshape, dshapedxt, pelmat;
DenseMatrix Jinv, gshape;
public:
VectorDiffusionIntegrator() { Q = NULL; }
+78 -25
View File
@@ -81,12 +81,20 @@ static void OccaPADiffusionSetup3D(const int D1D,
#endif // MFEM_USE_OCCA
// PA Diffusion Assemble 2D kernel
template<const int T_SDIM>
static void PADiffusionSetup2D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
Vector &d);
template<>
void PADiffusionSetup2D<2>(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
{
const int NQ = Q1D*Q1D;
const bool const_c = c.Size() == 1;
@@ -112,6 +120,48 @@ static void PADiffusionSetup2D(const int Q1D,
});
}
// PA Diffusion Assemble 2D kernel with 3D node coords
template<>
void PADiffusionSetup2D<3>(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
{
constexpr int DIM = 2;
constexpr int SDIM = 3;
const int NQ = Q1D*Q1D;
const bool const_c = c.Size() == 1;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, SDIM, DIM, NE);
auto C = const_c ? Reshape(c.Read(), 1, 1) : Reshape(c.Read(), NQ, NE);
auto D = Reshape(d.Write(), NQ, 3, NE);
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
{
const double wq = W[q];
const double J11 = J(q,0,0,e);
const double J21 = J(q,1,0,e);
const double J31 = J(q,2,0,e);
const double J12 = J(q,0,1,e);
const double J22 = J(q,1,1,e);
const double J32 = J(q,2,1,e);
const double E = J11*J11 + J21*J21 + J31*J31;
const double G = J12*J12 + J22*J22 + J32*J32;
const double F = J11*J12 + J21*J22 + J31*J32;
const double iw = 1.0 / sqrt(E*G - F*F);
const double coeff = const_c ? C(0,0) : C(q,e);
const double alpha = wq * coeff * iw;
D(q,0,e) = alpha * G; // 1,1
D(q,1,e) = -alpha * F; // 1,2
D(q,2,e) = alpha * E; // 2,2
}
});
}
// PA Diffusion Assemble 3D kernel
static void PADiffusionSetup3D(const int Q1D,
const int NE,
@@ -166,6 +216,7 @@ static void PADiffusionSetup3D(const int Q1D,
}
static void PADiffusionSetup(const int dim,
const int sdim,
const int D1D,
const int Q1D,
const int NE,
@@ -183,8 +234,11 @@ static void PADiffusionSetup(const int dim,
OccaPADiffusionSetup2D(D1D, Q1D, NE, W, J, C, D);
return;
}
#else
MFEM_CONTRACT_VAR(D1D);
#endif // MFEM_USE_OCCA
PADiffusionSetup2D(Q1D, NE, W, J, C, D);
if (sdim == 2) { PADiffusionSetup2D<2>(Q1D, NE, W, J, C, D); }
if (sdim == 3) { PADiffusionSetup2D<3>(Q1D, NE, W, J, C, D); }
}
if (dim == 3)
{
@@ -217,6 +271,8 @@ void DiffusionIntegrator::SetupPA(const FiniteElementSpace &fes,
InitCeedCoeff(Q, ptr);
return CeedPADiffusionAssemble(fes, *ir, *ptr);
}
#else
MFEM_CONTRACT_VAR(force);
#endif
const int dims = el.GetDim();
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
@@ -224,6 +280,7 @@ void DiffusionIntegrator::SetupPA(const FiniteElementSpace &fes,
dim = mesh->Dimension();
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
const int sdim = mesh->SpaceDimension();
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
@@ -252,8 +309,8 @@ void DiffusionIntegrator::SetupPA(const FiniteElementSpace &fes,
}
}
}
PADiffusionSetup(dim, dofs1D, quad1D, ne, ir->GetWeights(), geom->J, coeff,
pa_data);
PADiffusionSetup(dim, sdim, dofs1D, quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
@@ -910,8 +967,6 @@ template<int T_D1D = 0, int T_Q1D = 0, int T_NBZ = 0>
static void SmemPADiffusionApply2D(const int NE,
const Array<double> &b_,
const Array<double> &g_,
const Array<double> &bt_,
const Array<double> &gt_,
const Vector &d_,
const Vector &x_,
Vector &y_,
@@ -1257,8 +1312,6 @@ template<int T_D1D = 0, int T_Q1D = 0>
static void SmemPADiffusionApply3D(const int NE,
const Array<double> &b_,
const Array<double> &g_,
const Array<double> &bt_,
const Array<double> &gt_,
const Vector &d_,
const Vector &x_,
Vector &y_,
@@ -1525,14 +1578,14 @@ static void PADiffusionApply(const int dim,
{
switch ((D1D << 4 ) | Q1D)
{
case 0x22: return SmemPADiffusionApply2D<2,2,16>(NE,B,G,Bt,Gt,D,X,Y);
case 0x33: return SmemPADiffusionApply2D<3,3,16>(NE,B,G,Bt,Gt,D,X,Y);
case 0x44: return SmemPADiffusionApply2D<4,4,8>(NE,B,G,Bt,Gt,D,X,Y);
case 0x55: return SmemPADiffusionApply2D<5,5,8>(NE,B,G,Bt,Gt,D,X,Y);
case 0x66: return SmemPADiffusionApply2D<6,6,4>(NE,B,G,Bt,Gt,D,X,Y);
case 0x77: return SmemPADiffusionApply2D<7,7,4>(NE,B,G,Bt,Gt,D,X,Y);
case 0x88: return SmemPADiffusionApply2D<8,8,2>(NE,B,G,Bt,Gt,D,X,Y);
case 0x99: return SmemPADiffusionApply2D<9,9,2>(NE,B,G,Bt,Gt,D,X,Y);
case 0x22: return SmemPADiffusionApply2D<2,2,16>(NE,B,G,D,X,Y);
case 0x33: return SmemPADiffusionApply2D<3,3,16>(NE,B,G,D,X,Y);
case 0x44: return SmemPADiffusionApply2D<4,4,8>(NE,B,G,D,X,Y);
case 0x55: return SmemPADiffusionApply2D<5,5,8>(NE,B,G,D,X,Y);
case 0x66: return SmemPADiffusionApply2D<6,6,4>(NE,B,G,D,X,Y);
case 0x77: return SmemPADiffusionApply2D<7,7,4>(NE,B,G,D,X,Y);
case 0x88: return SmemPADiffusionApply2D<8,8,2>(NE,B,G,D,X,Y);
case 0x99: return SmemPADiffusionApply2D<9,9,2>(NE,B,G,D,X,Y);
default: return PADiffusionApply2D(NE,B,G,Bt,Gt,D,X,Y,D1D,Q1D);
}
}
@@ -1540,15 +1593,15 @@ static void PADiffusionApply(const int dim,
{
switch ((D1D << 4 ) | Q1D)
{
case 0x23: return SmemPADiffusionApply3D<2,3>(NE,B,G,Bt,Gt,D,X,Y);
case 0x34: return SmemPADiffusionApply3D<3,4>(NE,B,G,Bt,Gt,D,X,Y);
case 0x45: return SmemPADiffusionApply3D<4,5>(NE,B,G,Bt,Gt,D,X,Y);
case 0x46: return SmemPADiffusionApply3D<4,6>(NE,B,G,Bt,Gt,D,X,Y);
case 0x56: return SmemPADiffusionApply3D<5,6>(NE,B,G,Bt,Gt,D,X,Y);
case 0x58: return SmemPADiffusionApply3D<5,8>(NE,B,G,Bt,Gt,D,X,Y);
case 0x67: return SmemPADiffusionApply3D<6,7>(NE,B,G,Bt,Gt,D,X,Y);
case 0x78: return SmemPADiffusionApply3D<7,8>(NE,B,G,Bt,Gt,D,X,Y);
case 0x89: return SmemPADiffusionApply3D<8,9>(NE,B,G,Bt,Gt,D,X,Y);
case 0x23: return SmemPADiffusionApply3D<2,3>(NE,B,G,D,X,Y);
case 0x34: return SmemPADiffusionApply3D<3,4>(NE,B,G,D,X,Y);
case 0x45: return SmemPADiffusionApply3D<4,5>(NE,B,G,D,X,Y);
case 0x46: return SmemPADiffusionApply3D<4,6>(NE,B,G,D,X,Y);
case 0x56: return SmemPADiffusionApply3D<5,6>(NE,B,G,D,X,Y);
case 0x58: return SmemPADiffusionApply3D<5,8>(NE,B,G,D,X,Y);
case 0x67: return SmemPADiffusionApply3D<6,7>(NE,B,G,D,X,Y);
case 0x78: return SmemPADiffusionApply3D<7,8>(NE,B,G,D,X,Y);
case 0x89: return SmemPADiffusionApply3D<8,9>(NE,B,G,D,X,Y);
default: return PADiffusionApply3D(NE,B,G,Bt,Gt,D,X,Y,D1D,Q1D);
}
}
+84 -49
View File
@@ -101,7 +101,6 @@ static void PAVectorDiffusionSetup3D(const int Q1D,
}
static void PAVectorDiffusionSetup(const int dim,
const int D1D,
const int Q1D,
const int NE,
const Array<double> &W,
@@ -134,6 +133,7 @@ void VectorDiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
sdim = mesh->SpaceDimension();
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
@@ -147,46 +147,83 @@ void VectorDiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
MFEM_VERIFY(cQ != NULL, "only ConstantCoefficient is supported!");
coeff = cQ->constant;
}
PAVectorDiffusionSetup(dim, dofs1D, quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
const Array<double> &w = ir->GetWeights();
const Vector &j = geom->J;
Vector &d = pa_data;
if (dim == 1) { MFEM_ABORT("dim==1 not supported in PAVectorDiffusionSetup"); }
if (dim == 2 && sdim == 3)
{
constexpr int DIM = 2;
constexpr int SDIM = 3;
const int NQ = quad1D*quad1D;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, SDIM, DIM, ne);
auto D = Reshape(d.Write(), NQ, SDIM, ne);
MFEM_FORALL(e, ne,
{
for (int q = 0; q < NQ; ++q)
{
const double wq = W[q];
const double J11 = J(q,0,0,e);
const double J21 = J(q,1,0,e);
const double J31 = J(q,2,0,e);
const double J12 = J(q,0,1,e);
const double J22 = J(q,1,1,e);
const double J32 = J(q,2,1,e);
const double E = J11*J11 + J21*J21 + J31*J31;
const double G = J12*J12 + J22*J22 + J32*J32;
const double F = J11*J12 + J21*J22 + J31*J32;
const double iw = 1.0 / sqrt(E*G - F*F);
const double alpha = wq * coeff * iw;
D(q,0,e) = alpha * G; // 1,1
D(q,1,e) = -alpha * F; // 1,2
D(q,2,e) = alpha * E; // 2,2
}
});
}
else
{
PAVectorDiffusionSetup(dim, quad1D, ne, w, j, coeff, d);
}
}
// PA Diffusion Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0> static
template<int T_D1D = 0, int T_Q1D = 0, int T_VDIM = 0> static
void PAVectorDiffusionApply2D(const int NE,
const Array<double> &b,
const Array<double> &g,
const Array<double> &bt,
const Array<double> &gt,
const Vector &_op,
const Vector &_x,
Vector &_y,
const Vector &d_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
const int q1d = 0,
const int vdim = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 2;
const int VDIM = T_VDIM ? T_VDIM : vdim;
MFEM_VERIFY(D1D <= MAX_D1D, "");
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto Bt = Reshape(bt.Read(), D1D, Q1D);
auto Gt = Reshape(gt.Read(), D1D, Q1D);
auto op = Reshape(_op.Read(), Q1D*Q1D, 3, NE);
auto x = Reshape(_x.Read(), D1D, D1D, VDIM, NE);
auto y = Reshape(_y.ReadWrite(), D1D, D1D, VDIM, NE);
auto D = Reshape(d_.Read(), Q1D*Q1D, 3, NE);
auto x = Reshape(x_.Read(), D1D, D1D, VDIM, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, VDIM, NE);
MFEM_FORALL(e, NE,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
// the following variables are evaluated at compile time
const int VDIM = T_VDIM ? T_VDIM : vdim;
constexpr int max_D1D = T_D1D ? T_D1D : MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : MAX_Q1D;
for (int c = 0; c < VDIM; ++ c)
double grad[max_Q1D][max_Q1D][2];
for (int c = 0; c < VDIM; c++)
{
double grad[max_Q1D][max_Q1D][2];
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
@@ -229,14 +266,11 @@ void PAVectorDiffusionApply2D(const int NE,
for (int qx = 0; qx < Q1D; ++qx)
{
const int q = qx + qy * Q1D;
const double O11 = op(q,0,e);
const double O12 = op(q,1,e);
const double O22 = op(q,2,e);
const double O11 = D(q,0,e);
const double O12 = D(q,1,e);
const double O22 = D(q,2,e);
const double gradX = grad[qy][qx][0];
const double gradY = grad[qy][qx][1];
grad[qy][qx][0] = (O11 * gradX) + (O12 * gradY);
grad[qy][qx][1] = (O12 * gradX) + (O22 * gradY);
}
@@ -246,8 +280,8 @@ void PAVectorDiffusionApply2D(const int NE,
double gradX[max_D1D][2];
for (int dx = 0; dx < D1D; ++dx)
{
gradX[dx][0] = 0;
gradX[dx][1] = 0;
gradX[dx][0] = 0.0;
gradX[dx][1] = 0.0;
}
for (int qx = 0; qx < Q1D; ++qx)
{
@@ -469,35 +503,36 @@ void PAVectorDiffusionApply3D(const int NE,
});
}
static void PAVectorDiffusionApply(const int dim,
const int D1D,
const int Q1D,
const int NE,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
const Array<double> &Gt,
const Vector &op,
const Vector &x,
Vector &y)
{
if (dim == 2)
{
return PAVectorDiffusionApply2D(NE,B,G,Bt,Gt,op,x,y,D1D,Q1D);
}
if (dim == 3)
{
return PAVectorDiffusionApply3D(NE,B,G,Bt,Gt,op,x,y,D1D,Q1D);
}
MFEM_ABORT("Unknown kernel.");
}
// PA Diffusion Apply kernel
void VectorDiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
PAVectorDiffusionApply(dim, dofs1D, quad1D, ne,
maps->B, maps->G, maps->Bt, maps->Gt,
pa_data, x, y);
const int D1D = dofs1D;
const int Q1D = quad1D;
const Array<double> &B = maps->B;
const Array<double> &G = maps->G;
const Array<double> &Bt = maps->Bt;
const Array<double> &Gt = maps->Gt;
const Vector &D = pa_data;
if (dim == 2 && sdim == 3)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x22: return PAVectorDiffusionApply2D<2,2,3>(ne,B,G,Bt,Gt,D,x,y);
case 0x33: return PAVectorDiffusionApply2D<3,3,3>(ne,B,G,Bt,Gt,D,x,y);
case 0x44: return PAVectorDiffusionApply2D<4,4,3>(ne,B,G,Bt,Gt,D,x,y);
case 0x55: return PAVectorDiffusionApply2D<5,5,3>(ne,B,G,Bt,Gt,D,x,y);
default:
return PAVectorDiffusionApply2D(ne,B,G,Bt,Gt,D,x,y,D1D,Q1D,sdim);
}
}
if (dim == 2 && sdim == 2)
{ return PAVectorDiffusionApply2D(ne,B,G,Bt,Gt,D,x,y,D1D,Q1D,sdim); }
if (dim == 3 && sdim == 3)
{ return PAVectorDiffusionApply3D(ne,B,G,Bt,Gt,D,x,y,D1D,Q1D); }
MFEM_ABORT("Unknown kernel.");
}
template<int T_D1D = 0, int T_Q1D = 0>
+18 -1
View File
@@ -174,7 +174,24 @@ void VectorGridFunctionCoefficient::SetGridFunction(GridFunction *gf)
void VectorGridFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
GridFunc->GetVectorValue(T.ElementNo, ip, V);
Mesh *mesh = GridFunc->FESpace()->GetMesh();
if (mesh->Dimension() == T.GetDimension())
{
GridFunc->GetVectorValue(T.ElementNo, ip, V);
}
else // Assuming T is a boundary element transformation
{
int el_id, el_info;
mesh->GetBdrElementAdjacentElement(T.ElementNo, el_id, el_info);
IntegrationPointTransformation loc_T;
mesh->GetLocalFaceTransformation(mesh->GetBdrElementType(T.ElementNo),
mesh->GetElementType(el_id),
loc_T.Transf,
el_info);
IntegrationPoint eip;
loc_T.Transform(ip, eip);
GridFunc->GetVectorValue(el_id, eip, V);
}
}
void VectorGridFunctionCoefficient::Eval(
+2 -2
View File
@@ -138,7 +138,7 @@ public:
/// (DEPRECATED) Define a time-independent coefficient from a C-function
/** @deprecated Use the method where the C-function, @a f, uses a const
Vector argument instead of Vector. */
FunctionCoefficient(double (*f)(Vector &))
MFEM_DEPRECATED FunctionCoefficient(double (*f)(Vector &))
{
Function = reinterpret_cast<double(*)(const Vector&)>(f);
TDFunction = NULL;
@@ -147,7 +147,7 @@ public:
/// (DEPRECATED) Define a time-dependent coefficient from a C-function
/** @deprecated Use the method where the C-function, @a tdf, uses a const
Vector argument instead of Vector. */
FunctionCoefficient(double (*tdf)(Vector &, double))
MFEM_DEPRECATED FunctionCoefficient(double (*tdf)(Vector &, double))
{
Function = NULL;
TDFunction = reinterpret_cast<double(*)(const Vector&,double)>(tdf);
+8 -13
View File
@@ -391,14 +391,10 @@ void IsoparametricTransformation::SetIdentityTransformation(
nodes.IntPoint(j).Get(&PointMat(0,j), dim);
}
geom = GeomType;
space_dim = dim;
}
const DenseMatrix &IsoparametricTransformation::EvalJacobian()
{
MFEM_ASSERT(space_dim == PointMat.Height(),
"the IsoparametricTransformation has not been finalized;"
" call FinilizeTransformation() after setup");
MFEM_ASSERT((EvalState & JACOBIAN_MASK) == 0, "");
dshape.SetSize(FElem->GetDof(), FElem->GetDim());
@@ -415,9 +411,6 @@ const DenseMatrix &IsoparametricTransformation::EvalJacobian()
const DenseMatrix &IsoparametricTransformation::EvalHessian()
{
MFEM_ASSERT(space_dim == PointMat.Height(),
"the IsoparametricTransformation has not been finalized;"
" call FinilizeTransformation() after setup");
MFEM_ASSERT((EvalState & HESSIAN_MASK) == 0, "");
int Dim = FElem->GetDim();
@@ -433,7 +426,7 @@ const DenseMatrix &IsoparametricTransformation::EvalHessian()
return d2Fdx2;
}
int IsoparametricTransformation::OrderJ()
int IsoparametricTransformation::OrderJ() const
{
switch (FElem->Space())
{
@@ -442,12 +435,12 @@ int IsoparametricTransformation::OrderJ()
case FunctionSpace::Qk:
return (FElem->GetOrder());
default:
mfem_error("IsoparametricTransformation::OrderJ()");
MFEM_ABORT("unsupported finite element");
}
return 0;
}
int IsoparametricTransformation::OrderW()
int IsoparametricTransformation::OrderW() const
{
switch (FElem->Space())
{
@@ -456,12 +449,12 @@ int IsoparametricTransformation::OrderW()
case FunctionSpace::Qk:
return (FElem->GetOrder() * FElem->GetDim() - 1);
default:
mfem_error("IsoparametricTransformation::OrderW()");
MFEM_ABORT("unsupported finite element");
}
return 0;
}
int IsoparametricTransformation::OrderGrad(const FiniteElement *fe)
int IsoparametricTransformation::OrderGrad(const FiniteElement *fe) const
{
if (FElem->Space() == fe->Space())
{
@@ -474,9 +467,11 @@ int IsoparametricTransformation::OrderGrad(const FiniteElement *fe)
return ((k-1)*(d-1)+(l-1));
case FunctionSpace::Qk:
return (k*(d-1)+(l-1));
default:
MFEM_ABORT("unsupported finite element");
}
}
mfem_error("IsoparametricTransformation::OrderGrad(...)");
MFEM_ABORT("incompatible finite elements");
return 0;
}
+21 -14
View File
@@ -37,7 +37,6 @@ protected:
HESSIAN_MASK = 16
};
Geometry::Type geom;
int space_dim;
// Evaluate the Jacobian of the transformation at the IntPoint and store it
// in dFdx.
@@ -82,11 +81,11 @@ public:
const DenseMatrix &InverseJacobian()
{ return (EvalState & INVERSE_MASK) ? invJ : EvalInverseJ(); }
virtual int Order() = 0;
virtual int OrderJ() = 0;
virtual int OrderW() = 0;
virtual int Order() const = 0;
virtual int OrderJ() const = 0;
virtual int OrderW() const = 0;
/// Order of adj(J)^t.grad(fi)
virtual int OrderGrad(const FiniteElement *fe) = 0;
virtual int OrderGrad(const FiniteElement *fe) const = 0;
/// Return the Geometry::Type of the reference element.
Geometry::Type GetGeometryType() const { return geom; }
@@ -97,7 +96,7 @@ public:
/// Get the dimension of the target (physical) space.
/** We support 2D meshes embedded in 3D; in this case the function will
return "3". */
int GetSpaceDim() const { return space_dim; }
virtual int GetSpaceDim() const = 0;
/** @brief Transform a point @a pt from physical space to a point @a ip in
reference space. */
@@ -307,19 +306,23 @@ public:
void SetFE(const FiniteElement *FE) { FElem = FE; geom = FE->GetGeomType(); }
const FiniteElement* GetFE() const { return FElem; }
/** @brief Read and write access to the underlying point matrix describing
the transformation. */
/// @brief Set the underlying point matrix describing the transformation.
/** The dimensions of the matrix are space-dim x dof. The transformation is
defined as
x=F(xh)=P.phi(xh),
x = F(xh) = P . phi(xh),
where xh (x hat) is the reference point, x is the corresponding physical
point, P is the point matrix, and phi(xh) is the column-vector of all
basis functions evaluated at xh. The columns of P represent the control
points in physical space defining the transformation. */
void SetPointMat(const DenseMatrix &pm) { PointMat = pm; }
/// Return the stored point matrix.
const DenseMatrix &GetPointMat() const { return PointMat; }
/// Write access to the stored point matrix. Use with caution.
DenseMatrix &GetPointMat() { return PointMat; }
void FinalizeTransformation() { space_dim = PointMat.Height(); }
void SetIdentityTransformation(Geometry::Type GeomType);
@@ -327,10 +330,12 @@ public:
virtual void Transform(const IntegrationRule &, DenseMatrix &);
virtual void Transform(const DenseMatrix &matrix, DenseMatrix &result);
virtual int Order() { return FElem->GetOrder(); }
virtual int OrderJ();
virtual int OrderW();
virtual int OrderGrad(const FiniteElement *fe);
virtual int Order() const { return FElem->GetOrder(); }
virtual int OrderJ() const;
virtual int OrderW() const;
virtual int OrderGrad(const FiniteElement *fe) const;
virtual int GetSpaceDim() const { return PointMat.Height(); }
virtual int TransformBack(const Vector & v, IntegrationPoint & ip)
{
@@ -339,6 +344,8 @@ public:
}
virtual ~IsoparametricTransformation() { }
MFEM_DEPRECATED void FinalizeTransformation() {}
};
class IntegrationPointTransformation
+7
View File
@@ -37,6 +37,9 @@
#include "restriction.hpp"
#include "quadinterpolator.hpp"
#include "quadinterpolator_face.hpp"
#include "transfer.hpp"
#include "fespacehierarchy.hpp"
#include "multigrid.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
@@ -54,4 +57,8 @@
#include "conduitdatacollection.hpp"
#endif
#ifdef MFEM_USE_ADIOS2
#include "adios2datacollection.hpp"
#endif
#endif
+80 -33
View File
@@ -495,9 +495,10 @@ FiniteElementSpace::H2L_GlobalRestrictionMatrix (FiniteElementSpace *lfes)
{
SparseMatrix *R;
DenseMatrix loc_restr;
Array<int> l_dofs, h_dofs;
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
R = new SparseMatrix (lfes -> GetNDofs(), ndofs);
int vdim = lfes->GetVDim();
R = new SparseMatrix (vdim * lfes -> GetNDofs(), vdim * ndofs);
Geometry::Type cached_geom = Geometry::INVALID;
const FiniteElement *h_fe = NULL;
@@ -520,7 +521,16 @@ FiniteElementSpace::H2L_GlobalRestrictionMatrix (FiniteElementSpace *lfes)
cached_geom = geom;
}
R -> SetSubMatrix (l_dofs, h_dofs, loc_restr, 1);
for (int vd = 0; vd < vdim; vd++)
{
l_dofs.Copy(l_vdofs);
lfes->DofsToVDofs(vd, l_vdofs);
h_dofs.Copy(h_vdofs);
this->DofsToVDofs(vd, h_vdofs);
R -> SetSubMatrix (l_vdofs, h_vdofs, loc_restr, 1);
}
}
R -> Finalize();
@@ -670,7 +680,6 @@ void FiniteElementSpace::BuildConformingInterpolation() const
if (!slave_dofs.Size()) { continue; }
slave.OrientedPointMatrix(T.GetPointMat());
T.FinalizeTransformation();
fe->GetLocalInterpolation(T, I);
// make each slave DOF dependent on all master DOFs
@@ -1026,8 +1035,7 @@ void FiniteElementSpace::GetLocalRefinementMatrices(
localP.SetSize(ldof, ldof, nmat);
for (int i = 0; i < nmat; i++)
{
isotr.GetPointMat() = pmats(i);
isotr.FinalizeTransformation();
isotr.SetPointMat(pmats(i));
fe->GetLocalInterpolation(isotr, localP(i));
}
}
@@ -1096,46 +1104,90 @@ void FiniteElementSpace::RefinementOperator
Mesh* mesh = fespace->GetMesh();
const CoarseFineTransformations &rtrans = mesh->GetRefinementTransforms();
Array<int> dofs, old_dofs, old_vdofs;
Array<char> processed(fespace->GetVSize());
processed = 0;
Array<int> dofs, vdofs, old_dofs, old_vdofs;
int vdim = fespace->GetVDim();
int old_ndofs = width / vdim;
Vector subY, subX;
for (int k = 0; k < mesh->GetNE(); k++)
{
const Embedding &emb = rtrans.embeddings[k];
const Geometry::Type geom = mesh->GetElementBaseGeometry(k);
const DenseMatrix &lP = localP[geom](emb.matrix);
subY.SetSize(lP.Height());
fespace->GetElementDofs(k, dofs);
old_elem_dof->GetRow(emb.parent, old_dofs);
for (int vd = 0; vd < vdim; vd++)
{
dofs.Copy(vdofs);
fespace->DofsToVDofs(vd, vdofs);
old_dofs.Copy(old_vdofs);
fespace->DofsToVDofs(vd, old_vdofs, old_ndofs);
x.GetSubVector(old_vdofs, subX);
lP.Mult(subX, subY);
y.SetSubVector(vdofs, subY);
}
}
}
for (int i = 0; i < dofs.Size(); i++)
void FiniteElementSpace::RefinementOperator
::MultTranspose(const Vector &x, Vector &y) const
{
y = 0.0;
Mesh* mesh = fespace->GetMesh();
const CoarseFineTransformations &rtrans = mesh->GetRefinementTransforms();
Array<char> processed(fespace->GetVSize());
processed = 0;
Array<int> f_dofs, c_dofs, f_vdofs, c_vdofs;
int vdim = fespace->GetVDim();
int old_ndofs = width / vdim;
Vector subY, subX;
for (int k = 0; k < mesh->GetNE(); k++)
{
const Embedding &emb = rtrans.embeddings[k];
const Geometry::Type geom = mesh->GetElementBaseGeometry(k);
const DenseMatrix &lP = localP[geom](emb.matrix);
fespace->GetElementDofs(k, f_dofs);
old_elem_dof->GetRow(emb.parent, c_dofs);
subY.SetSize(lP.Width());
for (int vd = 0; vd < vdim; vd++)
{
f_dofs.Copy(f_vdofs);
fespace->DofsToVDofs(vd, f_vdofs);
c_dofs.Copy(c_vdofs);
fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
x.GetSubVector(f_vdofs, subX);
for (int p = 0; p < f_dofs.Size(); ++p)
{
double rsign, osign;
int r = fespace->DofToVDof(dofs[i], vd);
r = DecodeDof(r, rsign);
if (!processed[r])
if (processed[DecodeDof(f_dofs[p])])
{
double value = 0.0;
for (int j = 0; j < old_vdofs.Size(); j++)
{
int o = DecodeDof(old_vdofs[j], osign);
value += x[o] * lP(i, j) * osign;
}
y[r] = value * rsign;
processed[r] = 1;
subX[p] = 0.0;
}
}
lP.MultTranspose(subX, subY);
y.AddElementVector(c_vdofs, subY);
}
for (int p = 0; p < f_dofs.Size(); ++p)
{
processed[DecodeDof(f_dofs[p])] = 1;
}
}
}
@@ -1171,8 +1223,7 @@ FiniteElementSpace::DerefinementOperator::DerefinementOperator(
emb_tr.SetIdentityTransformation(geom);
for (int i = 0; i < pmats.SizeK(); i++)
{
emb_tr.GetPointMat() = pmats(i);
emb_tr.FinalizeTransformation();
emb_tr.SetPointMat(pmats(i));
// Get the local interpolation matrix for this refinement type
fine_fe->GetTransferMatrix(*coarse_fe, emb_tr, lP(i));
// Get the local mass matrix for this refinement type
@@ -1292,9 +1343,7 @@ void FiniteElementSpace::GetLocalDerefinementMatrices(Geometry::Type geom,
localR.SetSize(ldof, ldof, nmat);
for (int i = 0; i < nmat; i++)
{
isotr.GetPointMat() = pmats(i);
isotr.FinalizeTransformation();
isotr.SetPointMat(pmats(i));
fe->GetLocalRestriction(isotr, localR(i));
}
}
@@ -1392,8 +1441,7 @@ void FiniteElementSpace::GetLocalRefinementMatrices(
localP.SetSize(fine_fe->GetDof(), coarse_fe->GetDof(), nmat);
for (int i = 0; i < nmat; i++)
{
isotr.GetPointMat() = pmats(i);
isotr.FinalizeTransformation();
isotr.SetPointMat(pmats(i));
fine_fe->GetTransferMatrix(*coarse_fe, isotr, localP(i));
}
}
@@ -2650,8 +2698,7 @@ L2ProjectionGridTransfer::L2Projection::L2Projection(
// Create the transformation that embeds the fine low-order element
// within the coarse high-order element in reference space
emb_tr.GetPointMat() = pmats(cf_tr.embeddings[ilor].matrix);
emb_tr.FinalizeTransformation();
emb_tr.SetPointMat(pmats(cf_tr.embeddings[ilor].matrix));
int order = fe_lor->GetOrder() + fe_ho->GetOrder() + el_tr->OrderW();
const IntegrationRule *ir = &IntRules.Get(geom, order);
+8 -1
View File
@@ -87,6 +87,7 @@ class FaceQuadratureInterpolator;
class FiniteElementSpace
{
friend class InterpolationGridTransfer;
friend class PRefinementTransferOperator;
protected:
/// The mesh that FE space lives on (not owned).
@@ -158,7 +159,12 @@ protected:
void BuildElementToDofTable() const;
/// Helper to remove encoded sign from a DOF
/// Helpers to remove encoded sign from a DOF
static inline int DecodeDof(int dof)
{
return (dof >= 0) ? dof : (-1 - dof);
}
static inline int DecodeDof(int dof, double& sign)
{ return (dof >= 0) ? (sign = 1, dof) : (sign = -1, (-1 - dof)); }
@@ -196,6 +202,7 @@ protected:
RefinementOperator(const FiniteElementSpace *fespace,
const FiniteElementSpace *coarse_fes);
virtual void Mult(const Vector &x, Vector &y) const;
virtual void MultTranspose(const Vector &x, Vector &y) const;
virtual ~RefinementOperator();
};
+189
View File
@@ -0,0 +1,189 @@
// Copyright (c) 2010-2020, 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 "fespacehierarchy.hpp"
#include "transfer.hpp"
namespace mfem
{
FiniteElementSpaceHierarchy::FiniteElementSpaceHierarchy(Mesh* mesh,
FiniteElementSpace* fespace,
bool ownM, bool ownFES)
{
meshes.Append(mesh);
fespaces.Append(fespace);
ownedMeshes.Append(ownM);
ownedFES.Append(ownFES);
}
FiniteElementSpaceHierarchy::~FiniteElementSpaceHierarchy()
{
for (int i = 0; i < meshes.Size(); ++i)
{
if (ownedFES[i])
{
delete fespaces[i];
}
if (ownedMeshes[i])
{
delete meshes[i];
}
}
for (int i = 0; i < prolongations.Size(); ++i)
{
if (ownedProlongations[i])
{
delete prolongations[i];
}
}
fespaces.DeleteAll();
meshes.DeleteAll();
prolongations.DeleteAll();
}
int FiniteElementSpaceHierarchy::GetNumLevels() const { return meshes.Size(); }
int FiniteElementSpaceHierarchy::GetFinestLevelIndex() const { return GetNumLevels() - 1; }
void FiniteElementSpaceHierarchy::AddLevel(Mesh* mesh,
FiniteElementSpace* fespace,
Operator* prolongation,
bool ownM, bool ownFES,
bool ownP)
{
meshes.Append(mesh);
fespaces.Append(fespace);
prolongations.Append(prolongation);
ownedMeshes.Append(ownM);
ownedFES.Append(ownFES);
ownedProlongations.Append(ownP);
}
void FiniteElementSpaceHierarchy::AddUniformlyRefinedLevel(int dim,
int ordering)
{
MFEM_VERIFY(GetNumLevels() > 0, "There is no level which can be refined");
Mesh* mesh = new Mesh(*GetFinestFESpace().GetMesh());
mesh->UniformRefinement();
FiniteElementSpace& coarseFEspace = GetFinestFESpace();
FiniteElementSpace* fineFEspace =
new FiniteElementSpace(mesh, coarseFEspace.FEColl(), dim, ordering);
Operator* P = new TransferOperator(coarseFEspace, *fineFEspace);
AddLevel(mesh, fineFEspace, P, true, true, true);
}
void FiniteElementSpaceHierarchy::AddOrderRefinedLevel(FiniteElementCollection*
fec, int dim,
int ordering)
{
MFEM_VERIFY(GetNumLevels() > 0, "There is no level which can be refined");
Mesh* mesh = GetFinestFESpace().GetMesh();
FiniteElementSpace* newFEspace =
new FiniteElementSpace(mesh, fec, dim, ordering);
Operator* P = new TransferOperator(GetFinestFESpace(), *newFEspace);
AddLevel(mesh, newFEspace, P, false, true, true);
}
const FiniteElementSpace& FiniteElementSpaceHierarchy::GetFESpaceAtLevel(
int level) const
{
MFEM_ASSERT(level < fespaces.Size(),
"FE space at given level does not exist.");
return *fespaces[level];
}
FiniteElementSpace& FiniteElementSpaceHierarchy::GetFESpaceAtLevel(int level)
{
MFEM_ASSERT(level < fespaces.Size(),
"FE space at given level does not exist.");
return *fespaces[level];
}
const FiniteElementSpace& FiniteElementSpaceHierarchy::GetFinestFESpace() const
{
return GetFESpaceAtLevel(GetFinestLevelIndex());
}
FiniteElementSpace& FiniteElementSpaceHierarchy::GetFinestFESpace()
{
return GetFESpaceAtLevel(GetFinestLevelIndex());
}
Operator* FiniteElementSpaceHierarchy::GetProlongationAtLevel(int level) const
{
MFEM_ASSERT(level < prolongations.Size(),
"Prolongation at given level does not exist.");
return prolongations[level];
}
#ifdef MFEM_USE_MPI
ParFiniteElementSpaceHierarchy::ParFiniteElementSpaceHierarchy(ParMesh* mesh,
ParFiniteElementSpace* fespace,
bool ownM,
bool ownFES)
: FiniteElementSpaceHierarchy(mesh, fespace, ownM, ownFES)
{
}
void ParFiniteElementSpaceHierarchy::AddUniformlyRefinedLevel(int dim,
int ordering)
{
ParMesh* mesh = new ParMesh(*GetFinestFESpace().GetParMesh());
mesh->UniformRefinement();
ParFiniteElementSpace& coarseFEspace = GetFinestFESpace();
ParFiniteElementSpace* fineFEspace =
new ParFiniteElementSpace(mesh, coarseFEspace.FEColl(), dim, ordering);
Operator* P = new TrueTransferOperator(coarseFEspace, *fineFEspace);
AddLevel(mesh, fineFEspace, P, true, true, true);
}
void ParFiniteElementSpaceHierarchy::AddOrderRefinedLevel(
FiniteElementCollection* fec,
int dim, int ordering)
{
ParMesh* mesh = GetFinestFESpace().GetParMesh();
ParFiniteElementSpace* newFEspace =
new ParFiniteElementSpace(mesh, fec, dim, ordering);
Operator* P = new TrueTransferOperator(GetFinestFESpace(), *newFEspace);
AddLevel(mesh, newFEspace, P, false, true, true);
}
const ParFiniteElementSpace&
ParFiniteElementSpaceHierarchy::GetFESpaceAtLevel(int level) const
{
return static_cast<const ParFiniteElementSpace&>(
FiniteElementSpaceHierarchy::GetFESpaceAtLevel(level));
}
ParFiniteElementSpace& ParFiniteElementSpaceHierarchy::GetFESpaceAtLevel(
int level)
{
return static_cast<ParFiniteElementSpace&>(
FiniteElementSpaceHierarchy::GetFESpaceAtLevel(level));
}
const ParFiniteElementSpace& ParFiniteElementSpaceHierarchy::GetFinestFESpace()
const
{
return GetFESpaceAtLevel(GetFinestLevelIndex());
}
ParFiniteElementSpace& ParFiniteElementSpaceHierarchy::GetFinestFESpace()
{
return GetFESpaceAtLevel(GetFinestLevelIndex());
}
#endif
} // namespace mfem
+123
View File
@@ -0,0 +1,123 @@
// Copyright (c) 2010-2020, 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_FESPACEHIERARCHY
#define MFEM_FESPACEHIERARCHY
#include "fespace.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
#endif
namespace mfem
{
/// Class bundling a hierarchy finite element spaces together with the
/// corresponding prolongation operators
class FiniteElementSpaceHierarchy
{
protected:
Array<Mesh*> meshes;
Array<FiniteElementSpace*> fespaces;
Array<Operator*> prolongations;
Array<bool> ownedMeshes;
Array<bool> ownedFES;
Array<bool> ownedProlongations;
public:
/// @brief Constructs a space hierarchy with the given mesh and space on the
/// coarsest level.
/** The ownership of the mesh and space may be transferred to the
FiniteElementSpaceHierarchy by setting the according boolean variables. */
FiniteElementSpaceHierarchy(Mesh* mesh, FiniteElementSpace* fespace, bool ownM,
bool ownFES);
/// Destructor deleting all meshes and spaces that are owned
virtual ~FiniteElementSpaceHierarchy();
/// Returns the number of levels in the hierarchy
int GetNumLevels() const;
/// Returns the index of the finest level
int GetFinestLevelIndex() const;
/// Adds one level to the hierarchy
void AddLevel(Mesh* mesh, FiniteElementSpace* fespace, Operator* prolongation,
bool ownM, bool ownFES, bool ownP);
/// @brief Adds one level to the hierarchy by uniformly refining the mesh on the
/// previous level
virtual void AddUniformlyRefinedLevel(int dim = 1,
int ordering = Ordering::byVDIM);
/// @brief Adds one level to the hierarchy by using a different finite element
/// order defined through FiniteElementCollection
virtual void AddOrderRefinedLevel(FiniteElementCollection* fec, int dim = 1,
int ordering = Ordering::byVDIM);
/// Returns the finite element space at the given level
virtual const FiniteElementSpace& GetFESpaceAtLevel(int level) const;
/// Returns the finite element space at the given level
virtual FiniteElementSpace& GetFESpaceAtLevel(int level);
/// Returns the finite element space at the finest level
virtual const FiniteElementSpace& GetFinestFESpace() const;
/// Returns the finite element space at the finest level
virtual FiniteElementSpace& GetFinestFESpace();
/// @brief Returns the prolongation operator from the finite element space at
/// level to the finite element space at level + 1
Operator* GetProlongationAtLevel(int level) const;
};
#ifdef MFEM_USE_MPI
class ParFiniteElementSpaceHierarchy : public FiniteElementSpaceHierarchy
{
public:
/// @brief Constructs a parallel space hierarchy with the given mesh and spaces
/// on level zero.
/** The ownership of the mesh and space may be transferred to the
ParFiniteElementSpaceHierarchy by setting the according boolean variables. */
ParFiniteElementSpaceHierarchy(ParMesh* mesh, ParFiniteElementSpace* fespace,
bool ownM,
bool ownFES);
/// @brief Adds one level to the hierarchy by uniformly refining the mesh on the
/// previous level
void AddUniformlyRefinedLevel(int dim = 1,
int ordering = Ordering::byVDIM) override;
/// @brief Adds one level to the hierarchy by using a different finite element
/// order defined through FiniteElementCollection
void AddOrderRefinedLevel(FiniteElementCollection* fec, int dim = 1,
int ordering = Ordering::byVDIM) override;
/// Returns the finite element space at the given level
const ParFiniteElementSpace& GetFESpaceAtLevel(int level) const override;
/// Returns the finite element space at the given level
ParFiniteElementSpace& GetFESpaceAtLevel(int level) override;
/// Returns the finite element space at the finest level
const ParFiniteElementSpace& GetFinestFESpace() const override;
/// Returns the finite element space at the finest level
ParFiniteElementSpace& GetFinestFESpace() override;
};
#endif
} // namespace mfem
#endif
-3
View File
@@ -188,7 +188,6 @@ Geometry::Geometry()
IsoparametricTransformation tri_T;
tri_T.SetFE(&TriangleFE);
GetPerfPointMat (TRIANGLE, tri_T.GetPointMat());
tri_T.FinalizeTransformation();
tri_T.SetIntPoint(&GeomCenter[TRIANGLE]);
*GeomToPerfGeomJac[TRIANGLE] = tri_T.Jacobian();
CalcInverse(tri_T.Jacobian(), *PerfGeomToGeomJac[TRIANGLE]);
@@ -198,7 +197,6 @@ Geometry::Geometry()
IsoparametricTransformation tet_T;
tet_T.SetFE(&TetrahedronFE);
GetPerfPointMat (TETRAHEDRON, tet_T.GetPointMat());
tet_T.FinalizeTransformation();
tet_T.SetIntPoint(&GeomCenter[TETRAHEDRON]);
*GeomToPerfGeomJac[TETRAHEDRON] = tet_T.Jacobian();
CalcInverse(tet_T.Jacobian(), *PerfGeomToGeomJac[TETRAHEDRON]);
@@ -208,7 +206,6 @@ Geometry::Geometry()
IsoparametricTransformation pri_T;
pri_T.SetFE(&WedgeFE);
GetPerfPointMat (PRISM, pri_T.GetPointMat());
pri_T.FinalizeTransformation();
pri_T.SetIntPoint(&GeomCenter[PRISM]);
*GeomToPerfGeomJac[PRISM] = pri_T.Jacobian();
CalcInverse(pri_T.Jacobian(), *PerfGeomToGeomJac[PRISM]);
+9
View File
@@ -2753,6 +2753,15 @@ void GridFunction::Save(std::ostream &out) const
out.flush();
}
#ifdef MFEM_USE_ADIOS2
void GridFunction::Save(adios2stream &out,
const std::string& variable_name,
const adios2stream::data_type type) const
{
out.Save(*this, variable_name, type);
}
#endif
void GridFunction::SaveVTK(std::ostream &out, const std::string &field_name,
int ref)
{
+10
View File
@@ -16,6 +16,9 @@
#include "fespace.hpp"
#include "coefficient.hpp"
#include "bilininteg.hpp"
#ifdef MFEM_USE_ADIOS2
#include "../general/adios2stream.hpp"
#endif
#include <limits>
#include <ostream>
#include <string>
@@ -486,6 +489,13 @@ public:
/// Save the GridFunction to an output stream.
virtual void Save(std::ostream &out) const;
#ifdef MFEM_USE_ADIOS2
/// Save the GridFunction to a binary output stream using adios2 bp format.
virtual void Save(adios2stream &out, const std::string& variable_name,
const adios2stream::data_type
type = adios2stream::data_type::point_data) const;
#endif
/** Write the GridFunction in VTK format. Note that Mesh::PrintVTK must be
called first. The parameter ref > 0 must match the one used in
Mesh::PrintVTK. */
+1 -1
View File
@@ -100,7 +100,7 @@ public:
/// (DEPRECATED) Return the FE space associated with the LinearForm.
/** @deprecated Use FESpace() instead. */
FiniteElementSpace *GetFES() { return fes; }
MFEM_DEPRECATED FiniteElementSpace *GetFES() { return fes; }
/// Read+write access to the associated FiniteElementSpace.
FiniteElementSpace *FESpace() { return fes; }
+31
View File
@@ -93,6 +93,37 @@ void BoundaryLFIntegrator::AssembleRHSElementVect(
}
}
void BoundaryLFIntegrator::AssembleRHSElementVect(
const FiniteElement &el, FaceElementTransformations &Tr, Vector &elvect)
{
int dof = el.GetDof();
shape.SetSize(dof); // vector of size dof
elvect.SetSize(dof);
elvect = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int intorder = oa * el.GetOrder() + ob; // <------ user control
ir = &IntRules.Get(Tr.FaceGeom, intorder); // of integration order
}
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
IntegrationPoint eip;
Tr.Loc1.Transform(ip, eip);
Tr.Face->SetIntPoint (&ip);
double val = Tr.Face->Weight() * ip.weight * Q.Eval(*Tr.Face, ip);
el.CalcShape(eip, shape);
add(elvect, val, shape, elvect);
}
}
void BoundaryNormalLFIntegrator::AssembleRHSElementVect(
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
{
+5 -3
View File
@@ -126,7 +126,8 @@ class BoundaryLFIntegrator : public LinearFormIntegrator
Coefficient &Q;
int oa, ob;
public:
/// Constructs a boundary integrator with a given Coefficient QG
/** @brief Constructs a boundary integrator with a given Coefficient @a QG.
Integration order will be @a a * basis_order + @a b. */
BoundaryLFIntegrator(Coefficient &QG, int a = 1, int b = 1)
: Q(QG), oa(a), ob(b) { }
@@ -135,8 +136,9 @@ public:
virtual void AssembleRHSElementVect(const FiniteElement &el,
ElementTransformation &Tr,
Vector &elvect);
using LinearFormIntegrator::AssembleRHSElementVect;
virtual void AssembleRHSElementVect(const FiniteElement &el,
FaceElementTransformations &Tr,
Vector &elvect);
};
/// Class for boundary integration \f$ L(v) = (g \cdot n, v) \f$
+206
View File
@@ -0,0 +1,206 @@
// Copyright (c) 2010-2020, 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 "multigrid.hpp"
namespace mfem
{
Multigrid::Multigrid(const FiniteElementSpaceHierarchy& fespaces_)
: fespaces(fespaces_), cycleType(CycleType::VCYCLE), preSmoothingSteps(1),
postSmoothingSteps(1)
{}
Multigrid::~Multigrid()
{
for (int i = 0; i < operators.Size(); ++i)
{
if (ownedOperators[i])
{
delete operators[i];
}
if (ownedSmoothers[i])
{
delete smoothers[i];
}
delete X[i];
delete Y[i];
delete R[i];
delete Z[i];
}
operators.DeleteAll();
smoothers.DeleteAll();
X.DeleteAll();
Y.DeleteAll();
R.DeleteAll();
Z.DeleteAll();
for (int i = 0; i < bfs.Size(); ++i)
{
delete bfs[i];
}
bfs.DeleteAll();
for (int i = 0; i < essentialTrueDofs.Size(); ++i)
{
delete essentialTrueDofs[i];
}
essentialTrueDofs.DeleteAll();
}
void Multigrid::AddLevel(Operator* opr, Solver* smoother, bool ownOperator,
bool ownSmoother)
{
operators.Append(opr);
smoothers.Append(smoother);
ownedOperators.Append(ownOperator);
ownedSmoothers.Append(ownSmoother);
width = opr->Width();
height = opr->Height();
X.Append(new Vector(height));
*X.Last() = 0.0;
Y.Append(new Vector(height));
*Y.Last() = 0.0;
R.Append(new Vector(height));
*R.Last() = 0.0;
Z.Append(new Vector(height));
*Z.Last() = 0.0;
}
int Multigrid::NumLevels() const { return operators.Size(); }
int Multigrid::GetFinestLevelIndex() const { return NumLevels() - 1; }
const Operator* Multigrid::GetOperatorAtLevel(int level) const
{
return operators[level];
}
Operator* Multigrid::GetOperatorAtLevel(int level)
{
return operators[level];
}
const Operator* Multigrid::GetOperatorAtFinestLevel() const
{
return GetOperatorAtLevel(operators.Size() - 1);
}
Operator* Multigrid::GetOperatorAtFinestLevel()
{
return GetOperatorAtLevel(operators.Size() - 1);
}
Solver* Multigrid::GetSmootherAtLevel(int level) const
{
return smoothers[level];
}
Solver* Multigrid::GetSmootherAtLevel(int level)
{
return smoothers[level];
}
void Multigrid::SetCycleType(CycleType cycleType_, int preSmoothingSteps_,
int postSmoothingSteps_)
{
cycleType = cycleType_;
preSmoothingSteps = preSmoothingSteps_;
postSmoothingSteps = postSmoothingSteps_;
}
void Multigrid::Mult(const Vector& x, Vector& y) const
{
MFEM_ASSERT(NumLevels() > 0, "");
*X.Last() = x;
*Y.Last() = 0.0;
Cycle(GetFinestLevelIndex());
y = *Y.Last();
}
void Multigrid::SetOperator(const Operator& op)
{
MFEM_ABORT("SetOperator not supported in Multigrid");
}
void Multigrid::SmoothingStep(int level) const
{
GetOperatorAtLevel(level)->Mult(*Y[level], *R[level]); // r = A x
subtract(*X[level], *R[level], *R[level]); // r = b - A x
GetSmootherAtLevel(level)->Mult(*R[level], *Z[level]); // z = S r
add(*Y[level], 1.0, *Z[level], *Y[level]); // x = x + S (b - A x)
}
void Multigrid::Cycle(int level) const
{
if (level == 0)
{
GetSmootherAtLevel(level)->Mult(*X[level], *Y[level]);
return;
}
for (int i = 0; i < preSmoothingSteps; i++)
{
SmoothingStep(level);
}
// Compute residual
GetOperatorAtLevel(level)->Mult(*Y[level], *R[level]);
subtract(*X[level], *R[level], *R[level]);
// Restrict residual
fespaces.GetProlongationAtLevel(level - 1)->MultTranspose(*R[level],
*X[level - 1]);
// Init zeros
*Y[level - 1] = 0.0;
// Corrections
int corrections = 1;
if (cycleType == CycleType::WCYCLE)
{
corrections = 2;
}
for (int correction = 0; correction < corrections; ++correction)
{
Cycle(level - 1);
}
// Prolongate
fespaces.GetProlongationAtLevel(level - 1)->Mult(*Y[level - 1], *R[level]);
// Add update
*Y[level] += *R[level];
// Post-smooth
for (int i = 0; i < postSmoothingSteps; i++)
{
SmoothingStep(level);
}
}
void Multigrid::FormFineLinearSystem(Vector& x, Vector& b, OperatorHandle& A,
Vector& X, Vector& B)
{
bfs.Last()->FormLinearSystem(*essentialTrueDofs.Last(), x, b, A, X, B);
}
void Multigrid::RecoverFineFEMSolution(const Vector& X, const Vector& b,
Vector& x)
{
bfs.Last()->RecoverFEMSolution(X, b, x);
}
} // namespace mfem
+119
View File
@@ -0,0 +1,119 @@
// Copyright (c) 2010-2020, 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_MULTIGRID
#define MFEM_MULTIGRID
#include "fespacehierarchy.hpp"
#include "bilinearform.hpp"
#include "../linalg/operator.hpp"
#include "../linalg/handle.hpp"
namespace mfem
{
/// Multigrid solver class
class Multigrid : public Solver
{
public:
enum class CycleType
{
VCYCLE,
WCYCLE
};
protected:
const FiniteElementSpaceHierarchy& fespaces;
Array<Array<int>*> essentialTrueDofs;
Array<BilinearForm*> bfs;
private:
Array<Operator*> operators;
Array<Solver*> smoothers;
Array<bool> ownedOperators;
Array<bool> ownedSmoothers;
CycleType cycleType;
int preSmoothingSteps;
int postSmoothingSteps;
mutable Array<Vector*> X;
mutable Array<Vector*> Y;
mutable Array<Vector*> R;
mutable Array<Vector*> Z;
public:
/// Constructs an empty multigrid for the given FiniteElementSpaceHierarchy
Multigrid(const FiniteElementSpaceHierarchy& fespaces_);
/// Destructor
virtual ~Multigrid();
/// Adds a level to the multigrid operator hierarchy.
/** The ownership of the operators and solvers/smoothers may be transferred
to the Multigrid by setting the according boolean variables. */
void AddLevel(Operator* opr, Solver* smoother, bool ownOperator,
bool ownSmoother);
/// Returns the number of levels
int NumLevels() const;
/// Returns the index of the finest level
int GetFinestLevelIndex() const;
/// Returns operator at given level
const Operator* GetOperatorAtLevel(int level) const;
/// Returns operator at given level
Operator* GetOperatorAtLevel(int level);
/// Returns operator at finest level
const Operator* GetOperatorAtFinestLevel() const;
/// Returns operator at finest level
Operator* GetOperatorAtFinestLevel();
/// Returns smoother at given level
Solver* GetSmootherAtLevel(int level) const;
/// Returns smoother at given level
Solver* GetSmootherAtLevel(int level);
/// Set the cycle type and number of pre- and post-smoothing steps used by Mult
void SetCycleType(CycleType cycleType_, int preSmoothingSteps_,
int postSmoothingSteps_);
/// Application of the multigrid as a preconditioner
virtual void Mult(const Vector& x, Vector& y) const override;
/// Not supported for multigrid
virtual void SetOperator(const Operator& op) override;
/// Form the linear system A X = B, corresponding to the operator on the finest level
void FormFineLinearSystem(Vector& x, Vector& b, OperatorHandle& A, Vector& X,
Vector& B);
/// Recover the solution of a linear system formed with FormFineLinearSystem()
void RecoverFineFEMSolution(const Vector& X, const Vector& b, Vector& x);
private:
/// Application of a smoothing step at particular level
void SmoothingStep(int level) const;
/// Application of a cycle at particular level
void Cycle(int level) const;
};
} // namespace mfem
#endif
+2 -2
View File
@@ -111,8 +111,8 @@ public:
have zero entries at the essential true dofs. */
void SetEssentialBC(const Array<int> &bdr_attr_is_ess, Vector *rhs = NULL);
/// (DEPRECATED) Specify essential boundary conditions.
/** @deprecated Use either SetEssentialBC() or SetEssentialTrueDofs(). */
/// Specify essential boundary conditions.
/** Use either SetEssentialBC() or SetEssentialTrueDofs() if possible. */
void SetEssentialVDofs(const Array<int> &ess_vdofs_list);
/// Specify essential boundary conditions.
+8 -1
View File
@@ -283,7 +283,14 @@ const
}
X.Distribute(&x);
mat->Mult(X, Y);
if (ext)
{
ext->Mult(X, Y);
}
else
{
mat->Mult(X, Y);
}
pfes->Dof_TrueDof_Matrix()->MultTranspose(a, Y, 1.0, y);
}
-1
View File
@@ -2029,7 +2029,6 @@ int ParFiniteElementSpace
if (!slave_dofs.Size()) { continue; }
sf.OrientedPointMatrix(T.GetPointMat());
T.FinalizeTransformation();
fe->GetLocalInterpolation(T, I);
// make each slave DOF dependent on all master DOFs
+1 -1
View File
@@ -240,7 +240,7 @@ public:
int GetNRanks() const { return NRanks; }
int GetMyRank() const { return MyRank; }
inline ParMesh *GetParMesh() { return pmesh; }
inline ParMesh *GetParMesh() const { return pmesh; }
int GetDofSign(int i)
{ return NURBSext || Nonconforming() ? 1 : ldof_sign[VDofToDof(i)]; }
+439
View File
@@ -16,6 +16,7 @@
#include "fem.hpp"
#include <iostream>
#include <limits>
#include <string>
#include "../general/forall.hpp"
using namespace std;
@@ -78,6 +79,229 @@ ParGridFunction::ParGridFunction(ParMesh *pmesh, std::istream &input)
fes = pfes;
}
ParGridFunction::ParGridFunction(ParFiniteElementSpace *pf,
const char *_filename)
: GridFunction(pf), pfes(pf)
{
MPI_Comm fes_comm;
int fes_rank, n_fes_ranks;
fes_comm = pfes->GetComm();
MPI_Comm_size(fes_comm, &n_fes_ranks);
MPI_Comm_rank(fes_comm, &fes_rank);
std::string filename(_filename);
std::string file_prefix;
std::string file_ext;
{
size_t i = filename.rfind('.', filename.length());
if (i != string::npos)
{
file_prefix = (filename.substr(0, i));
file_ext = (filename.substr(i, filename.length() - i));
}
}
int nfiles = 1;
if (fes_rank == 0)
{
int n_rfes_ranks;
int tmp[2];
std::string mpi_filename;
size_t i = filename.rfind('.', filename.length());
if (i != string::npos)
{
mpi_filename = file_prefix + to_string(0) + file_ext;
}
else
{
mpi_filename = filename + to_string(0);
}
MPI_File fh;
MPI_File_open(MPI_COMM_SELF, mpi_filename.c_str(), MPI_MODE_RDONLY,
MPI_INFO_NULL, &fh);
MPI_File_read_at(fh, 0, tmp, 2, MPI_INT, MPI_STATUS_IGNORE);
MPI_File_close(&fh);
n_rfes_ranks = tmp[0];
nfiles = tmp[1];
MFEM_ASSERT(n_fes_ranks == n_rfes_ranks,
"ParGridFunction::ParGridFunction(ParFiniteElementSpace *pf,"
" const char *_filename):\n"
"\tThe number of MPI ranks used to save the GridFunction is\n"
"\tnot the same as the number used to load it!");
}
MPI_Bcast(&nfiles, 1, MPI_INT, 0, fes_comm);
int color = fes_rank * nfiles / n_fes_ranks;
MPI_Comm file_comm;
MPI_Comm_split(fes_comm, color, fes_rank, &file_comm);
int file_rank, n_file_ranks;
MPI_Comm_size(file_comm, &n_file_ranks);
MPI_Comm_rank(file_comm, &file_rank);
std::string mpi_filename;
{
size_t i = filename.rfind('.', filename.length());
if (i != string::npos) {
mpi_filename = file_prefix + std::to_string(color) + file_ext;
}
else
{
mpi_filename = filename + std::to_string(color);
}
}
MPI_File fh;
MPI_File_open(file_comm, mpi_filename.c_str(), MPI_MODE_RDONLY,
MPI_INFO_NULL, &fh);
int *dof_counts = new int[5*n_file_ranks];
int **nv = new int*[n_file_ranks];
int **nvdofs = new int*[n_file_ranks];
int **nedofs = new int*[n_file_ranks];
int **nfdofs = new int*[n_file_ranks];
int **nrdofs = new int*[n_file_ranks];
for (int i = 0; i < n_file_ranks; ++i)
{
nv[i] = &dof_counts[i*5+0];
nvdofs[i] = &dof_counts[i*5+1];
nedofs[i] = &dof_counts[i*5+2];
nfdofs[i] = &dof_counts[i*5+3];
nrdofs[i] = &dof_counts[i*5+4];
}
*nv[file_rank] = pfes->GetVSize();
*nvdofs[file_rank] = pfes->GetNVDofs();
*nedofs[file_rank] = pfes->GetNEDofs();
*nfdofs[file_rank] = pfes->GetNFDofs();
int vdim = pfes->GetVDim();
*nrdofs[file_rank] = *nv[file_rank] / vdim - *nvdofs[file_rank] -
*nedofs[file_rank] - *nfdofs[file_rank];
MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, &dof_counts[0], 5,
MPI_INT, file_comm);
double *data_ = HostWrite();
MPI_Offset header_offset = 0;
header_offset += 2 * sizeof(int);
MPI_Offset v_offset, e_offset, f_offset, r_offset;
int total_vdofs = 0, total_edofs = 0, total_fdofs = 0, total_rdofs = 0;
int total_scalar_dofs = 0;
for (int i = 0; i < n_file_ranks; ++i)
{
total_vdofs += *nvdofs[i];
total_edofs += *nedofs[i];
total_fdofs += *nfdofs[i];
total_rdofs += *nrdofs[i];
total_scalar_dofs += *nv[i];
}
total_scalar_dofs /= vdim;
if (pfes->GetOrdering() == Ordering::byNODES)
{
for (int d = 0; d < vdim; ++d)
{
int v_data_offset = 0 + *nv[file_rank] * d / vdim ;
int e_data_offset = v_data_offset + *nvdofs[file_rank];
int f_data_offset = e_data_offset + *nedofs[file_rank];
int r_data_offset = f_data_offset + *nfdofs[file_rank];
v_offset = header_offset;
e_offset = header_offset;
f_offset = header_offset;
r_offset = header_offset;
v_offset += total_scalar_dofs * d * sizeof(double);
e_offset += (total_vdofs + total_scalar_dofs * d) * sizeof(double);
f_offset += (total_vdofs + total_edofs +
total_scalar_dofs * d) * sizeof(double);
r_offset += (total_vdofs + total_edofs + total_fdofs +
total_scalar_dofs * d) * sizeof(double);
for (int i = 0; i < file_rank; ++i)
{
v_offset += *nvdofs[i] * sizeof(double);
e_offset += *nedofs[i] * sizeof(double);
f_offset += *nfdofs[i] * sizeof(double);
r_offset += *nrdofs[i] * sizeof(double);
}
MPI_File_read_at_all(fh, v_offset, &data_[v_data_offset],
*nvdofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_read_at_all(fh, e_offset, &data_[e_data_offset],
*nedofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_read_at_all(fh, f_offset, &data_[f_data_offset],
*nfdofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_read_at_all(fh, r_offset, &data_[r_data_offset],
*nrdofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
}
}
else
{
v_offset = header_offset;
e_offset = v_offset + total_vdofs * vdim * sizeof(double);
f_offset = e_offset + total_edofs * vdim * sizeof(double);
r_offset = f_offset + total_fdofs * vdim * sizeof(double);
for (int i = 0; i < file_rank; ++i)
{
v_offset += *nvdofs[i] * sizeof(double) * vdim;
e_offset += *nedofs[i] * sizeof(double) * vdim;
f_offset += *nfdofs[i] * sizeof(double) * vdim;
r_offset += *nrdofs[i] * sizeof(double) * vdim;
}
int v_data_offset = 0;
int e_data_offset = v_data_offset + *nvdofs[file_rank] * vdim;
int f_data_offset = e_data_offset + *nedofs[file_rank] * vdim;
int r_data_offset = f_data_offset + *nfdofs[file_rank] * vdim;
MPI_File_read_at_all(fh, v_offset, &data_[v_data_offset],
*nvdofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_read_at_all(fh, e_offset, &data_[e_data_offset],
*nedofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_read_at_all(fh, f_offset, &data_[f_data_offset],
*nfdofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_read_at_all(fh, r_offset, &data_[r_data_offset],
*nrdofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
}
MPI_File_close(&fh);
MPI_Comm_free(&file_comm);
for (int i = 0; i < size; i++)
{
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
}
delete[] dof_counts;
delete[] nv;
delete[] nvdofs;
delete[] nedofs;
delete[] nfdofs;
delete[] nrdofs;
}
void ParGridFunction::Update()
{
face_nbr_data.Destroy();
@@ -498,6 +722,221 @@ void ParGridFunction::Save(std::ostream &out) const
}
}
#ifdef MFEM_USE_ADIOS2
void ParGridFunction::Save(adios2stream &out,
const std::string& variable_name,
const adios2stream::data_type type) const
{
double *data_ = const_cast<double*>(HostRead());
for (int i = 0; i < size; i++)
{
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
}
GridFunction::Save(out, variable_name, type);
for (int i = 0; i < size; i++)
{
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
}
}
#endif
void ParGridFunction::Save(const char *_filename, const int nfiles)
{
MPI_Comm fes_comm;
int fes_rank, n_fes_ranks;
fes_comm = pfes->GetComm();
MPI_Comm_size(fes_comm, &n_fes_ranks);
MPI_Comm_rank(fes_comm, &fes_rank);
int color = fes_rank * nfiles / n_fes_ranks;
MPI_Comm file_comm;
MPI_Comm_split(fes_comm, color, fes_rank, &file_comm);
int file_rank, n_file_ranks;
MPI_Comm_size(file_comm, &n_file_ranks);
MPI_Comm_rank(file_comm, &file_rank);
std::string filename(_filename);
std::string file_prefix;
std::string file_ext;
std::string mpi_filename;
{
size_t i = filename.rfind('.', filename.length());
if (i != string::npos)
{
file_prefix = (filename.substr(0, i));
file_ext = (filename.substr(i, filename.length() - i));
mpi_filename = file_prefix + std::to_string(color) + file_ext;
}
else
{
mpi_filename = filename + std::to_string(color);
}
}
MPI_File fh;
MPI_File_open(file_comm, mpi_filename.c_str(), MPI_MODE_CREATE |
MPI_MODE_WRONLY,
MPI_INFO_NULL, &fh);
int *dof_counts = new int[5*n_file_ranks];
int **nv = new int*[n_file_ranks];
int **nvdofs = new int*[n_file_ranks];
int **nedofs = new int*[n_file_ranks];
int **nfdofs = new int*[n_file_ranks];
int **nrdofs = new int*[n_file_ranks];
for (int i = 0; i < n_file_ranks; ++i)
{
nv[i] = &dof_counts[i*5+0];
nvdofs[i] = &dof_counts[i*5+1];
nedofs[i] = &dof_counts[i*5+2];
nfdofs[i] = &dof_counts[i*5+3];
nrdofs[i] = &dof_counts[i*5+4];
}
*nv[file_rank] = pfes->GetVSize();
*nvdofs[file_rank] = pfes->GetNVDofs();
*nedofs[file_rank] = pfes->GetNEDofs();
*nfdofs[file_rank] = pfes->GetNFDofs();
int vdim = pfes->GetVDim();
*nrdofs[file_rank] = *nv[file_rank] / vdim - *nvdofs[file_rank] -
*nedofs[file_rank] - *nfdofs[file_rank];
MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, &dof_counts[0], 5,
MPI_INT, file_comm);
double *data_ = const_cast<double*>(HostRead());
for (int i = 0; i < size; i++)
{
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
}
MPI_Offset header_offset = 0;
if (file_rank == 0)
{
int tmp[] = {n_fes_ranks, nfiles};
MPI_File_write_at(fh, header_offset, &tmp, 2, MPI_INT,
MPI_STATUS_IGNORE);
}
header_offset += 2 * sizeof(int);
MPI_Offset v_offset, e_offset, f_offset, r_offset;
int total_vdofs = 0, total_edofs = 0, total_fdofs = 0, total_rdofs = 0;
int total_scalar_dofs = 0;
for (int i = 0; i < n_file_ranks; ++i)
{
total_vdofs += *nvdofs[i];
total_edofs += *nedofs[i];
total_fdofs += *nfdofs[i];
total_rdofs += *nrdofs[i];
total_scalar_dofs += *nv[i];
}
total_scalar_dofs /= vdim;
if (pfes->GetOrdering() == Ordering::byNODES)
{
for (int d = 0; d < vdim; ++d)
{
int v_data_offset = 0 + *nv[file_rank] * d / vdim ;
int e_data_offset = v_data_offset + *nvdofs[file_rank];
int f_data_offset = e_data_offset + *nedofs[file_rank];
int r_data_offset = f_data_offset + *nfdofs[file_rank];
v_offset = header_offset;
e_offset = header_offset;
f_offset = header_offset;
r_offset = header_offset;
v_offset += total_scalar_dofs * d * sizeof(double);
e_offset += (total_vdofs + total_scalar_dofs * d) * sizeof(double);
f_offset += (total_vdofs + total_edofs +
total_scalar_dofs * d) * sizeof(double);
r_offset += (total_vdofs + total_edofs + total_fdofs +
total_scalar_dofs * d) * sizeof(double);
for (int i = 0; i < file_rank; ++i)
{
v_offset += *nvdofs[i] * sizeof(double);
e_offset += *nedofs[i] * sizeof(double);
f_offset += *nfdofs[i] * sizeof(double);
r_offset += *nrdofs[i] * sizeof(double);
}
MPI_File_write_at_all(fh, v_offset, &data_[v_data_offset],
*nvdofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_write_at_all(fh, e_offset, &data_[e_data_offset],
*nedofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_write_at_all(fh, f_offset, &data_[f_data_offset],
*nfdofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_write_at_all(fh, r_offset, &data_[r_data_offset],
*nrdofs[file_rank], MPI_DOUBLE,
MPI_STATUS_IGNORE);
}
}
else
{
v_offset = header_offset;
e_offset = v_offset + total_vdofs * vdim * sizeof(double);
f_offset = e_offset + total_edofs * vdim * sizeof(double);
r_offset = f_offset + total_fdofs * vdim * sizeof(double);
for (int i = 0; i < file_rank; ++i)
{
v_offset += *nvdofs[i] * sizeof(double) * vdim;
e_offset += *nedofs[i] * sizeof(double) * vdim;
f_offset += *nfdofs[i] * sizeof(double) * vdim;
r_offset += *nrdofs[i] * sizeof(double) * vdim;
}
int v_data_offset = 0;
int e_data_offset = v_data_offset + *nvdofs[file_rank] * vdim;
int f_data_offset = e_data_offset + *nedofs[file_rank] * vdim;
int r_data_offset = f_data_offset + *nfdofs[file_rank] * vdim;
MPI_File_write_at_all(fh, v_offset, &data_[v_data_offset],
*nvdofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_write_at_all(fh, e_offset, &data_[e_data_offset],
*nedofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_write_at_all(fh, f_offset, &data_[f_data_offset],
*nfdofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
MPI_File_write_at_all(fh, r_offset, &data_[r_data_offset],
*nrdofs[file_rank] * vdim, MPI_DOUBLE,
MPI_STATUS_IGNORE);
}
MPI_File_close(&fh);
MPI_Comm_free(&file_comm);
for (int i = 0; i < size; i++)
{
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
}
delete[] dof_counts;
delete[] nv;
delete[] nvdofs;
delete[] nedofs;
delete[] nfdofs;
delete[] nrdofs;
}
void ParGridFunction::SaveAsOne(std::ostream &out)
{
int i, p;
+31 -1
View File
@@ -83,6 +83,13 @@ public:
constructed. The new ParGridFunction assumes ownership of both. */
ParGridFunction(ParMesh *pmesh, std::istream &input);
/// Construct a ParGridFunction by loading a ParGridFunction saved using
/// ParGridFunction::Save(char *filename, int nfiles).
/** The parallel space @a *pf and the space used by the GridFunction saved
in @a *filename should match. The number of ranks used when loading the
ParGridFunction must be the same as when it was saved. */
ParGridFunction(ParFiniteElementSpace *pf, const char *filename);
/// Copy assignment. Only the data of the base class Vector is copied.
/** It is assumed that this object and @a rhs use ParFiniteElementSpace%s
that have the same size.
@@ -310,11 +317,34 @@ public:
GridFunction &flux,
bool wcoef = true, int subdomain = -1);
/** Save the local portion of the ParGridFunction. It differs from the
/** Save the local portion of the ParGridFunction. This differs from the
serial GridFunction::Save in that it takes into account the signs of
the local dofs. */
virtual void Save(std::ostream &out) const;
#ifdef MFEM_USE_ADIOS2
/** Save the local portion of the ParGridFunction. This differs from the
serial GridFunction::Save in that it takes into account the signs of
the local dofs. */
virtual void Save(
adios2stream &out, const std::string &variable_name,
const adios2stream::data_type type = adios2stream::data_type::point_data) const;
#endif
/** Save the local grid functions to n number of files, where each file will
contain the grid functions from potentially multiple ranks. This is
similar to the syncIO approach from "Fu, Jing, et al. 'Scalable parallel
I/O alternatives for massively parallel partitioned solver systems.'
2010 IEEE International Symposium on Parallel & Distributed Processing,
Workshops and Phd Forum (IPDPSW). IEEE, 2010."
@param[in] filename - filename for output files with extension
@param[in] nfiles - number of files to write using MPI-IO
@note - takes into account the signs of the local dofs.
@note - writes a binary file without the FESpace header; the saved file
should only be loaded by the accompanying constructor:
ParGridFunction(ParFiniteElementSpace *pf, const char *filename) */
void Save(const char *filename, const int nfiles = 1);
/// Merge the local grid functions
void SaveAsOne(std::ostream &out = mfem::out);
+27
View File
@@ -319,6 +319,33 @@ void QuadratureInterpolator::Mult(
}
}
}
else if (vdim == 3 && dim == 2)
{
switch (100*nd + nq)
{
// Q0
case 101: eval_func = &Eval2D<3,1,1>; break;
case 104: eval_func = &Eval2D<3,1,4>; break;
// Q1
case 404: eval_func = &Eval2D<3,4,4>; break;
case 409: eval_func = &Eval2D<3,4,9>; break;
// Q2
case 904: eval_func = &Eval2D<3,9,4>; break;
case 909: eval_func = &Eval2D<3,9,9>; break;
case 916: eval_func = &Eval2D<3,9,16>; break;
case 925: eval_func = &Eval2D<3,9,25>; break;
// Q3
case 1616: eval_func = &Eval2D<3,16,16>; break;
case 1625: eval_func = &Eval2D<3,16,25>; break;
case 1636: eval_func = &Eval2D<3,16,36>; break;
// Q4
case 2525: eval_func = &Eval2D<3,25,25>; break;
case 2536: eval_func = &Eval2D<3,25,36>; break;
case 2549: eval_func = &Eval2D<3,25,49>; break;
case 2564: eval_func = &Eval2D<3,25,64>; break;
default: eval_func = &Eval2D<3>;
}
}
else if (vdim == dim)
{
if (dim == 2)
+1 -1
View File
@@ -47,7 +47,7 @@ protected:
static const int MAX_NQ2D = 100;
static const int MAX_ND2D = 100;
static const int MAX_VDIM2D = 2;
static const int MAX_VDIM2D = 3;
static const int MAX_NQ3D = 1000;
static const int MAX_ND3D = 1000;
+37
View File
@@ -223,6 +223,43 @@ void ElementRestriction::MultTransposeUnsigned(const Vector& x, Vector& y) const
});
}
void ElementRestriction::BooleanMask(Vector& y) const
{
// Assumes all elements have the same number of dofs
const int nd = dof;
const int vd = vdim;
const bool t = byvdim;
Array<char> processed(vd * ndofs);
processed = 0;
auto d_offsets = offsets.HostRead();
auto d_indices = indices.HostRead();
auto d_x = Reshape(processed.HostReadWrite(), t?vd:ndofs, t?ndofs:vd);
auto d_y = Reshape(y.HostWrite(), nd, vd, ne);
for (int i = 0; i < ndofs; ++i)
{
const int offset = d_offsets[i];
const int nextOffset = d_offsets[i+1];
for (int c = 0; c < vd; ++c)
{
for (int j = offset; j < nextOffset; ++j)
{
const int idx_j = d_indices[j];
if (d_x(t?c:i,t?i:c))
{
d_y(idx_j % nd, c, idx_j / nd) = 0.0;
}
else
{
d_y(idx_j % nd, c, idx_j / nd) = 1.0;
d_x(t?c:i,t?i:c) = 1;
}
}
}
}
}
/// Return the face degrees of freedom returned in Lexicographic order.
void GetFaceDofs(const int dim, const int face_id,
const int dof1d, Array<int> &faceMap)
+8
View File
@@ -49,6 +49,14 @@ public:
/// Compute MultTranspose without applying signs based on DOF orientations.
void MultTransposeUnsigned(const Vector &x, Vector &y) const;
/// @brief Fills the E-vector y with `boolean` values 0.0 and 1.0 such that each
/// each entry of the L-vector is uniquely represented in `y`.
/** This means, the sum of the E-vector `y` is equal to the sum of the
corresponding L-vector filled with ones. The boolean mask is required to
emulate SetSubVector and its transpose on GPUs. This method is running on
the host, since the `processed` array requires a large shared memory. */
void BooleanMask(Vector& y) const;
};
/// Operator that converts L2 FiniteElementSpace L-vectors to E-vectors.
+126 -19
View File
@@ -993,17 +993,21 @@ void DiscreteAdaptTC::SetSerialDiscreteTargetSpec(GridFunction &tspec_)
tspec_sav = tspec;
}
void DiscreteAdaptTC::UpdateTargetSpecification(const Vector &new_x)
void DiscreteAdaptTC::UpdateTargetSpecification(const Vector &new_x,
bool use_flag)
{
if (use_flag && good_tspec) { return; }
MFEM_VERIFY(tspec.Size() > 0, "Target specification is not set!");
adapt_eval->ComputeAtNewPosition(new_x, tspec);
tspec_sav = tspec;
good_tspec = use_flag;
}
void DiscreteAdaptTC::UpdateTargetSpecification(Vector &new_x,
Vector &IntData)
{
MFEM_VERIFY(tspec.Size() > 0, "Target specification is not set!");
adapt_eval->ComputeAtNewPosition(new_x, IntData);
}
@@ -1070,14 +1074,17 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
}
void DiscreteAdaptTC::UpdateGradientTargetSpecification(const Vector &x,
const double dx)
const double dx,
bool use_flag)
{
if (use_flag && good_tspec_grad) { return; }
const int dim = tspec_fes->GetFE(0)->GetDim();
const int cnt = x.Size()/dim;
if (tspec_perth.Size() != x.Size())
if (tspec_pert1h.Size() != x.Size())
{
tspec_perth.SetSize(x.Size());
tspec_pert1h.SetSize(x.Size());
}
Vector TSpecTemp;
@@ -1086,16 +1093,20 @@ void DiscreteAdaptTC::UpdateGradientTargetSpecification(const Vector &x,
{
for (int i = 0; i < cnt; i++) { xtemp(j*cnt+i) += dx; }
TSpecTemp.SetDataAndSize(tspec_perth.GetData() + j*cnt, cnt);
TSpecTemp.SetDataAndSize(tspec_pert1h.GetData() + j*cnt, cnt);
UpdateTargetSpecification(xtemp, TSpecTemp);
for (int i = 0; i < cnt; i++) { xtemp(j*cnt+i) -= dx; }
}
good_tspec_grad = use_flag;
}
void DiscreteAdaptTC::UpdateHessianTargetSpecification(const Vector &x,
const double dx)
double dx, bool use_flag)
{
if (use_flag && good_tspec_hess) { return; }
const int dim = tspec_fes->GetFE(0)->GetDim();
const int cnt = x.Size()/dim;
@@ -1142,6 +1153,8 @@ void DiscreteAdaptTC::UpdateHessianTargetSpecification(const Vector &x,
idx++;
}
}
good_tspec_hess = use_flag;
}
void AdaptivityEvaluator::SetSerialMetaInfo(const Mesh &m,
@@ -1180,19 +1193,8 @@ void TMOP_Integrator::EnableLimiting(const GridFunction &n0,
const GridFunction &dist, Coefficient &w0,
TMOP_LimiterFunction *lfunc)
{
nodes0 = &n0;
coeff0 = &w0;
EnableLimiting(n0, w0, lfunc);
lim_dist = &dist;
delete lim_func;
if (lfunc)
{
lim_func = lfunc;
}
else
{
lim_func = new TMOP_QuadraticLimiter;
}
}
void TMOP_Integrator::EnableLimiting(const GridFunction &n0, Coefficient &w0,
TMOP_LimiterFunction *lfunc)
@@ -1810,6 +1812,111 @@ void TMOP_Integrator::EnableFiniteDifferences(const ParGridFunction &x)
}
#endif
void TMOPComboIntegrator::EnableLimiting(const GridFunction &n0,
const GridFunction &dist,
Coefficient &w0,
TMOP_LimiterFunction *lfunc)
{
MFEM_VERIFY(tmopi.Size() > 0, "No TMOP_Integrators were added.");
tmopi[0]->EnableLimiting(n0, dist, w0, lfunc);
for (int i = 1; i < tmopi.Size(); i++) { tmopi[i]->DisableLimiting(); }
}
void TMOPComboIntegrator::EnableLimiting(const GridFunction &n0,
Coefficient &w0,
TMOP_LimiterFunction *lfunc)
{
MFEM_VERIFY(tmopi.Size() > 0, "No TMOP_Integrators were added.");
tmopi[0]->EnableLimiting(n0, w0, lfunc);
for (int i = 1; i < tmopi.Size(); i++) { tmopi[i]->DisableLimiting(); }
}
void TMOPComboIntegrator::SetLimitingNodes(const GridFunction &n0)
{
MFEM_VERIFY(tmopi.Size() > 0, "No TMOP_Integrators were added.");
tmopi[0]->SetLimitingNodes(n0);
for (int i = 1; i < tmopi.Size(); i++) { tmopi[i]->DisableLimiting(); }
}
double TMOPComboIntegrator::GetElementEnergy(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun)
{
double energy= 0.0;
for (int i = 0; i < tmopi.Size(); i++)
{
energy += tmopi[i]->GetElementEnergy(el, T, elfun);
}
return energy;
}
void TMOPComboIntegrator::AssembleElementVector(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun,
Vector &elvect)
{
MFEM_VERIFY(tmopi.Size() > 0, "No TMOP_Integrators were added.");
tmopi[0]->AssembleElementVector(el, T, elfun, elvect);
for (int i = 1; i < tmopi.Size(); i++)
{
Vector elvect_i;
tmopi[i]->AssembleElementVector(el, T, elfun, elvect_i);
elvect += elvect_i;
}
}
void TMOPComboIntegrator::AssembleElementGrad(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun,
DenseMatrix &elmat)
{
MFEM_VERIFY(tmopi.Size() > 0, "No TMOP_Integrators were added.");
tmopi[0]->AssembleElementGrad(el, T, elfun, elmat);
for (int i = 1; i < tmopi.Size(); i++)
{
DenseMatrix elmat_i;
tmopi[i]->AssembleElementGrad(el, T, elfun, elmat_i);
elmat += elmat_i;
}
}
void TMOPComboIntegrator::EnableNormalization(const GridFunction &x)
{
const int cnt = tmopi.Size();
double total_integral = 0.0;
for (int i = 0; i < cnt; i++)
{
tmopi[i]->EnableNormalization(x);
total_integral += 1.0 / tmopi[i]->metric_normal;
}
for (int i = 0; i < cnt; i++)
{
tmopi[i]->metric_normal = 1.0 / total_integral;
}
}
#ifdef MFEM_USE_MPI
void TMOPComboIntegrator::ParEnableNormalization(const ParGridFunction &x)
{
const int cnt = tmopi.Size();
double total_integral = 0.0;
for (int i = 0; i < cnt; i++)
{
tmopi[i]->ParEnableNormalization(x);
total_integral += 1.0 / tmopi[i]->metric_normal;
}
for (int i = 0; i < cnt; i++)
{
tmopi[i]->metric_normal = 1.0 / total_integral;
}
}
#endif
void InterpolateTMOP_QualityMetric(TMOP_QualityMetric &metric,
const TargetConstructor &tc,
+84 -16
View File
@@ -710,7 +710,7 @@ protected:
// Data is owned, updated by UpdateTargetSpecification.
Vector tspec; //eta(x)
Vector tspec_sav;
Vector tspec_perth; //eta(x+h)
Vector tspec_pert1h; //eta(x+h)
Vector tspec_pert2h; //eta(x+2*h)
Vector tspec_pertmix; //eta(x+h,y+h)
@@ -718,6 +718,10 @@ protected:
// positions corresponding to the values of tspec.
const FiniteElementSpace *tspec_fes;
// These flags can be used by outside functions to avoid recomputing
// the tspec and tspec_perth fields again on the same mesh.
bool good_tspec, good_tspec_grad, good_tspec_hess;
// Evaluation of the discrete target specification on different meshes.
// Owned.
AdaptivityEvaluator *adapt_eval;
@@ -725,7 +729,10 @@ protected:
public:
DiscreteAdaptTC(TargetType ttype)
: TargetConstructor(ttype),
tspec(), tspec_fes(NULL), adapt_eval(NULL) { }
tspec(), tspec_sav(), tspec_pert1h(), tspec_pert2h(), tspec_pertmix(),
tspec_fes(NULL),
good_tspec(false), good_tspec_grad(false), good_tspec_hess(false),
adapt_eval(NULL) { }
virtual ~DiscreteAdaptTC() { delete adapt_eval; }
@@ -734,9 +741,14 @@ public:
virtual void SetParDiscreteTargetSpec(ParGridFunction &tspec_);
#endif
/// Used in combination with the Update methods to avoid extra computations.
void ResetUpdateFlags()
{ good_tspec = good_tspec_grad = good_tspec_hess = false; }
/** Used to update the target specification after the mesh has changed. The
new mesh positions are given by new_x. */
void UpdateTargetSpecification(const Vector &new_x);
new mesh positions are given by new_x. If @a use_flags is true, repeated
calls won't do anything until ResetUpdateFlags() is called. */
void UpdateTargetSpecification(const Vector &new_x, bool use_flag = false);
void UpdateTargetSpecification(Vector &new_x, Vector &IntData);
@@ -747,12 +759,17 @@ public:
void RestoreTargetSpecificationAtNode(ElementTransformation &T, int nodenum);
/** Used for finite-difference based computations. Computes the target
specifications after a mesh perturbation in x or y direction. */
void UpdateGradientTargetSpecification(const Vector &x, const double dx);
specifications after a mesh perturbation in x or y direction.
If @a use_flags is true, repeated calls won't do anything until
ResetUpdateFlags() is called. */
void UpdateGradientTargetSpecification(const Vector &x, double dx,
bool use_flag = false);
/** Used for finite-difference based computations. Computes the target
specifications after two mesh perturbations in x and/or y direction. */
void UpdateHessianTargetSpecification(const Vector &x, const double dx);
specifications after two mesh perturbations in x and/or y direction.
If @a use_flags is true, repeated calls won't do anything until
ResetUpdateFlags() is called. */
void UpdateHessianTargetSpecification(const Vector &x, double dx,
bool use_flag = false);
void SetAdaptivityEvaluator(AdaptivityEvaluator *ae)
{
@@ -760,7 +777,7 @@ public:
adapt_eval = ae;
}
const Vector &GetTspecPert1H() { return tspec_perth; }
const Vector &GetTspecPert1H() { return tspec_pert1h; }
const Vector &GetTspecPert2H() { return tspec_pert2h; }
const Vector &GetTspecPertMixH() { return tspec_pertmix; }
@@ -776,7 +793,6 @@ public:
};
class TMOPNewtonSolver;
class TMOPDescentNewtonSolver;
/** @brief A TMOP integrator class based on any given TMOP_QualityMetric and
TargetConstructor.
@@ -789,7 +805,8 @@ class TMOP_Integrator : public NonlinearFormIntegrator
{
protected:
friend class TMOPNewtonSolver;
friend class TMOPDescentNewtonSolver;
friend class TMOPComboIntegrator;
TMOP_QualityMetric *metric; // not owned
const TargetConstructor *targetC; // not owned
@@ -865,6 +882,11 @@ protected:
#endif
void ComputeMinJac(const Vector &x, const FiniteElementSpace &fes);
void DisableLimiting()
{
nodes0 = NULL; coeff0 = NULL; lim_dist = NULL; lim_func = NULL;
}
public:
/** @param[in] m TMOP_QualityMetric that will be integrated (not owned).
@param[in] tc Target-matrix construction algorithm to use (not owned). */
@@ -911,8 +933,8 @@ public:
/** @brief Adds a limiting term to the integrator with limiting distance
function (@a dist in the general version of the method) equal to 1. */
void EnableLimiting(const GridFunction &n0,
Coefficient &w0, TMOP_LimiterFunction *lfunc = NULL);
void EnableLimiting(const GridFunction &n0, Coefficient &w0,
TMOP_LimiterFunction *lfunc = NULL);
/// Update the original/reference nodes used for limiting.
void SetLimitingNodes(const GridFunction &n0) { nodes0 = &n0; }
@@ -933,7 +955,7 @@ public:
ElementTransformation &T,
const Vector &elfun, DenseMatrix &elmat);
DiscreteAdaptTC *GetDiscreteAdaptTC() { return discr_tc; }
DiscreteAdaptTC *GetDiscreteAdaptTC() const { return discr_tc; }
/** @brief Computes the normalization factors of the metric and limiting
integrals using the mesh position given by @a x. */
@@ -953,13 +975,59 @@ public:
double GetFDh() const { return dx; }
};
class TMOPComboIntegrator : public NonlinearFormIntegrator
{
protected:
// Integrators in the combination. Owned.
Array<TMOP_Integrator *> tmopi;
public:
TMOPComboIntegrator() : tmopi(0) { }
~TMOPComboIntegrator()
{
for (int i = 0; i < tmopi.Size(); i++) { delete tmopi[i]; }
}
/// Adds a new TMOP_Integrator to the combination.
void AddTMOPIntegrator(TMOP_Integrator *ti) { tmopi.Append(ti); }
Array<TMOP_Integrator *> GetTMOPIntegrators() const { return tmopi; }
/// Adds the limiting term to the first integrator. Disables it for the rest.
void EnableLimiting(const GridFunction &n0, const GridFunction &dist,
Coefficient &w0, TMOP_LimiterFunction *lfunc = NULL);
/** @brief Adds the limiting term to the first integrator. Disables it for
the rest (@a dist in the general version of the method) equal to 1. */
void EnableLimiting(const GridFunction &n0, Coefficient &w0,
TMOP_LimiterFunction *lfunc = NULL);
/// Update the original/reference nodes used for limiting.
void SetLimitingNodes(const GridFunction &n0);
virtual double GetElementEnergy(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun);
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun, Vector &elvect);
virtual void AssembleElementGrad(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun, DenseMatrix &elmat);
/// Normalization factor that considers all integrators in the combination.
void EnableNormalization(const GridFunction &x);
#ifdef MFEM_USE_MPI
void ParEnableNormalization(const ParGridFunction &x);
#endif
};
/// Interpolates the @a metric's values at the nodes of @a metric_gf.
/** Assumes that @a metric_gf's FiniteElementSpace is initialized. */
void InterpolateTMOP_QualityMetric(TMOP_QualityMetric &metric,
const TargetConstructor &tc,
const Mesh &mesh, GridFunction &metric_gf);
}
#endif
+87 -103
View File
@@ -61,10 +61,10 @@ void AdvectorCG::ComputeAtNewPosition(const Vector &new_nodes,
ode_solver.Init(*oper);
// Compute some time step [mesh_size / speed].
double min_h = std::numeric_limits<double>::infinity();
double h_min = std::numeric_limits<double>::infinity();
for (int i = 0; i < m->GetNE(); i++)
{
min_h = std::min(min_h, m->GetElementSize(i));
h_min = std::min(h_min, m->GetElementSize(i));
}
double v_max = 0.0;
const int s = u.FESpace()->GetVSize() / 2;
@@ -73,26 +73,28 @@ void AdvectorCG::ComputeAtNewPosition(const Vector &new_nodes,
const double vel = u(i) * u(i) + u(i+s) * u(i+s);
v_max = std::max(v_max, vel);
}
if (v_max == 0.0)
{
// No need to change the field.
return;
}
v_max = std::sqrt(v_max);
double dt = 0.5 * min_h / v_max;
double glob_dt = dt;
#ifdef MFEM_USE_MPI
if (pfes)
{
MPI_Allreduce(&dt, &glob_dt, 1, MPI_DOUBLE, MPI_MIN, pfes->GetComm());
double v_loc = v_max, h_loc = h_min;
MPI_Allreduce(&v_loc, &v_max, 1, MPI_DOUBLE, MPI_MAX, pfes->GetComm());
MPI_Allreduce(&h_loc, &h_min, 1, MPI_DOUBLE, MPI_MIN, pfes->GetComm());
}
#endif
if (v_max == 0.0)
{
// No mesh motion --> no need to change the field.
delete oper;
return;
}
v_max = std::sqrt(v_max);
double dt = dt_scale * h_min / v_max;
double t = 0.0;
bool last_step = false;
for (int ti = 1; !last_step; ti++)
{
if (t + glob_dt >= 1.0)
if (t + dt >= 1.0)
{
#ifdef MFEM_DEBUG
if (myid == 0)
@@ -100,10 +102,10 @@ void AdvectorCG::ComputeAtNewPosition(const Vector &new_nodes,
mfem::out << "Remap took " << ti << " steps." << std::endl;
}
#endif
glob_dt = 1.0 - t;
dt = 1.0 - t;
last_step = true;
}
ode_solver.Step(new_field, t, glob_dt);
ode_solver.Step(new_field, t, dt);
}
// Trim the overshoots and undershoots.
@@ -410,28 +412,58 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
{
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
// Reset the update flags of all TargetConstructors.
// This is done to avoid repeated updates of shared TargetConstructors.
TMOP_Integrator *ti = NULL;
TMOPComboIntegrator *co = NULL;
DiscreteAdaptTC *dtc = NULL;
for (int i = 0; i < integs.Size(); i++)
{
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
if (ti)
{
dtc = ti->GetDiscreteAdaptTC();
if (dtc) { dtc->ResetUpdateFlags(); }
}
co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
if (co)
{
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
for (int j = 0; j < ati.Size(); j++)
{
dtc = ati[j]->GetDiscreteAdaptTC();
if (dtc) { dtc->ResetUpdateFlags(); }
}
}
}
if (parallel)
{
#ifdef MFEM_USE_MPI
const ParNonlinearForm *nlf =
dynamic_cast<const ParNonlinearForm *>(oper);
const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
const ParFiniteElementSpace *pfesc = nlf->ParFESpace();
Vector x_loc(pfesc->GetVSize());
pfesc->GetProlongationMatrix()->Mult(x, x_loc);
for (int i=0; i<integs.Size(); i++)
for (int i = 0; i < integs.Size(); i++)
{
TMOP_Integrator *tmopi = dynamic_cast<TMOP_Integrator *>(integs[i]);
DiscreteAdaptTC *discrtc = tmopi->GetDiscreteAdaptTC();
tmopi->ComputeFDh(x_loc, *pfesc);
if (discrtc)
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
if (ti)
{
discrtc->UpdateTargetSpecification(x_loc);
double dx = tmopi->GetFDh();
if (tmopi->GetFDFlag())
ti->ComputeFDh(x_loc, *pfesc);
UpdateDiscreteTC(*ti, x_loc);
}
co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
if (co)
{
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
for (int j = 0; j < ati.Size(); j++)
{
discrtc->UpdateGradientTargetSpecification(x_loc, dx);
discrtc->UpdateHessianTargetSpecification(x_loc, dx);
ati[j]->ComputeFDh(x_loc, *pfesc);
UpdateDiscreteTC(*ati[j], x_loc);
}
}
}
@@ -439,9 +471,6 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
}
else
{
const NonlinearForm *nlf =
dynamic_cast<const NonlinearForm *>(oper);
const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
const FiniteElementSpace *fesc = nlf->FESpace();
const Operator *P = nlf->GetProlongation();
Vector x_loc;
@@ -454,25 +483,45 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
{
x_loc = x;
}
for (int i=0; i<integs.Size(); i++)
for (int i = 0; i < integs.Size(); i++)
{
TMOP_Integrator *tmopi = dynamic_cast<TMOP_Integrator *>(integs[i]);
DiscreteAdaptTC *discrtc = tmopi->GetDiscreteAdaptTC();
tmopi->ComputeFDh(x_loc, *fesc);
if (discrtc)
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
if (ti)
{
discrtc->UpdateTargetSpecification(x);
double dx = tmopi->GetFDh();
if (tmopi->GetFDFlag())
ti->ComputeFDh(x_loc, *fesc);
UpdateDiscreteTC(*ti, x_loc);
}
co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
if (co)
{
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
for (int j = 0; j < ati.Size(); j++)
{
discrtc->UpdateGradientTargetSpecification(x_loc, dx);
discrtc->UpdateHessianTargetSpecification(x_loc, dx);
ati[j]->ComputeFDh(x_loc, *fesc);
UpdateDiscreteTC(*ati[j], x_loc);
}
}
}
}
}
void TMOPNewtonSolver::UpdateDiscreteTC(const TMOP_Integrator &ti,
const Vector &x_new) const
{
const bool update_flag = true;
DiscreteAdaptTC *discrtc = ti.GetDiscreteAdaptTC();
if (discrtc)
{
discrtc->UpdateTargetSpecification(x_new, update_flag);
if (ti.GetFDFlag())
{
double dx = ti.GetFDh();
discrtc->UpdateGradientTargetSpecification(x_new, dx, update_flag);
discrtc->UpdateHessianTargetSpecification(x_new, dx, update_flag);
}
}
}
double TMOPDescentNewtonSolver::ComputeScalingFactor(const Vector &x,
const Vector &b) const
{
@@ -572,71 +621,6 @@ double TMOPDescentNewtonSolver::ComputeScalingFactor(const Vector &x,
return scale;
}
void TMOPDescentNewtonSolver::ProcessNewState(const Vector &x) const
{
if (parallel)
{
#ifdef MFEM_USE_MPI
const ParNonlinearForm *nlf =
dynamic_cast<const ParNonlinearForm *>(oper);
const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
const ParFiniteElementSpace *pfesc = nlf->ParFESpace();
Vector x_loc(pfesc->GetVSize());
pfesc->GetProlongationMatrix()->Mult(x, x_loc);
for (int i=0; i<integs.Size(); i++)
{
TMOP_Integrator *tmopi = dynamic_cast<TMOP_Integrator *>(integs[i]);
DiscreteAdaptTC *discrtc = tmopi->GetDiscreteAdaptTC();
tmopi->ComputeFDh(x_loc, *pfesc);
if (discrtc)
{
discrtc->UpdateTargetSpecification(x_loc);
double dx = tmopi->GetFDh();
if (tmopi->GetFDFlag())
{
discrtc->UpdateGradientTargetSpecification(x_loc, dx);
discrtc->UpdateHessianTargetSpecification(x_loc, dx);
}
}
}
#endif
}
else
{
const NonlinearForm *nlf =
dynamic_cast<const NonlinearForm *>(oper);
const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
const FiniteElementSpace *fesc = nlf->FESpace();
const Operator *P = nlf->GetProlongation();
Vector x_loc;
if (P)
{
x_loc.SetSize(P->Height());
P->Mult(x,x_loc);
}
else
{
x_loc = x;
}
for (int i=0; i<integs.Size(); i++)
{
TMOP_Integrator *tmopi = dynamic_cast<TMOP_Integrator *>(integs[i]);
DiscreteAdaptTC *discrtc = tmopi->GetDiscreteAdaptTC();
tmopi->ComputeFDh(x_loc, *fesc);
if (discrtc)
{
discrtc->UpdateTargetSpecification(x);
double dx = tmopi->GetFDh();
if (tmopi->GetFDFlag())
{
discrtc->UpdateGradientTargetSpecification(x_loc, dx);
discrtc->UpdateHessianTargetSpecification(x_loc, dx);
}
}
}
}
}
#ifdef MFEM_USE_MPI
// Metric values are visualized by creating an L2 finite element functions and
// computing the metric values at the nodes.
+11 -13
View File
@@ -28,8 +28,12 @@ private:
Vector nodes0;
Vector field0;
const double dt_scale;
public:
AdvectorCG() : AdaptivityEvaluator(), ode_solver(), nodes0(), field0() { }
AdvectorCG(double timestep_scale = 0.5)
: AdaptivityEvaluator(),
ode_solver(), nodes0(), field0(), dt_scale(timestep_scale) { }
virtual void SetInitialField(const Vector &init_nodes,
const Vector &init_field);
@@ -105,12 +109,14 @@ public:
class TMOPNewtonSolver : public NewtonSolver
{
private:
protected:
bool parallel;
// Quadrature points that are checked for negative Jacobians etc.
const IntegrationRule &ir;
void UpdateDiscreteTC(const TMOP_Integrator &ti, const Vector &x_new) const;
public:
#ifdef MFEM_USE_MPI
TMOPNewtonSolver(MPI_Comm comm, const IntegrationRule &irule)
@@ -125,25 +131,17 @@ public:
};
/// Allows negative Jacobians. Used for untangling.
class TMOPDescentNewtonSolver : public NewtonSolver
class TMOPDescentNewtonSolver : public TMOPNewtonSolver
{
private:
bool parallel;
// Quadrature points that are checked for negative Jacobians etc.
const IntegrationRule &ir;
public:
#ifdef MFEM_USE_MPI
TMOPDescentNewtonSolver(MPI_Comm comm, const IntegrationRule &irule)
: NewtonSolver(comm), parallel(true), ir(irule) { }
: TMOPNewtonSolver(comm, irule) { }
#endif
TMOPDescentNewtonSolver(const IntegrationRule &irule)
: NewtonSolver(), parallel(false), ir(irule) { }
: TMOPNewtonSolver(irule) { }
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const;
virtual void ProcessNewState(const Vector &x) const;
};
void vis_tmop_metric_s(int order, TMOP_QualityMetric &qm,
+553
View File
@@ -0,0 +1,553 @@
// Copyright (c) 2010-2020, 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 "transfer.hpp"
#include "../general/forall.hpp"
namespace mfem
{
TransferOperator::TransferOperator(const FiniteElementSpace& lFESpace_,
const FiniteElementSpace& hFESpace_)
: Operator(hFESpace_.GetVSize(), lFESpace_.GetVSize())
{
if (lFESpace_.FEColl() == hFESpace_.FEColl())
{
OperatorPtr P(Operator::ANY_TYPE);
hFESpace_.GetTransferOperator(lFESpace_, P);
P.SetOperatorOwner(false);
opr = P.Ptr();
}
else if (lFESpace_.GetMesh()->GetNE() > 0
&& hFESpace_.GetMesh()->GetNE() > 0
&& dynamic_cast<const TensorBasisElement*>(lFESpace_.GetFE(0))
&& dynamic_cast<const TensorBasisElement*>(hFESpace_.GetFE(0)))
{
opr = new TensorProductPRefinementTransferOperator(lFESpace_, hFESpace_);
}
else
{
opr = new PRefinementTransferOperator(lFESpace_, hFESpace_);
}
}
TransferOperator::~TransferOperator() { delete opr; }
void TransferOperator::Mult(const Vector& x, Vector& y) const
{
opr->Mult(x, y);
}
void TransferOperator::MultTranspose(const Vector& x, Vector& y) const
{
opr->MultTranspose(x, y);
}
PRefinementTransferOperator::PRefinementTransferOperator(
const FiniteElementSpace& lFESpace_, const FiniteElementSpace& hFESpace_)
: Operator(hFESpace_.GetVSize(), lFESpace_.GetVSize()), lFESpace(lFESpace_),
hFESpace(hFESpace_)
{
}
PRefinementTransferOperator::~PRefinementTransferOperator() {}
void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
{
Mesh* mesh = hFESpace.GetMesh();
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
DenseMatrix loc_prol;
Vector subY, subX;
Geometry::Type cached_geom = Geometry::INVALID;
const FiniteElement* h_fe = NULL;
const FiniteElement* l_fe = NULL;
IsoparametricTransformation T;
int vdim = lFESpace.GetVDim();
for (int i = 0; i < mesh->GetNE(); i++)
{
hFESpace.GetElementDofs(i, h_dofs);
lFESpace.GetElementDofs(i, l_dofs);
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
if (geom != cached_geom)
{
h_fe = hFESpace.GetFE(i);
l_fe = lFESpace.GetFE(i);
T.SetIdentityTransformation(h_fe->GetGeomType());
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
subY.SetSize(loc_prol.Height());
cached_geom = geom;
}
for (int vd = 0; vd < vdim; vd++)
{
l_dofs.Copy(l_vdofs);
lFESpace.DofsToVDofs(vd, l_vdofs);
h_dofs.Copy(h_vdofs);
hFESpace.DofsToVDofs(vd, h_vdofs);
x.GetSubVector(l_vdofs, subX);
loc_prol.Mult(subX, subY);
y.SetSubVector(h_vdofs, subY);
}
}
}
void PRefinementTransferOperator::MultTranspose(const Vector& x,
Vector& y) const
{
y = 0.0;
Mesh* mesh = hFESpace.GetMesh();
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
DenseMatrix loc_prol;
Vector subY, subX;
Array<char> processed(hFESpace.GetVSize());
processed = 0;
Geometry::Type cached_geom = Geometry::INVALID;
const FiniteElement* h_fe = NULL;
const FiniteElement* l_fe = NULL;
IsoparametricTransformation T;
int vdim = lFESpace.GetVDim();
for (int i = 0; i < mesh->GetNE(); i++)
{
hFESpace.GetElementDofs(i, h_dofs);
lFESpace.GetElementDofs(i, l_dofs);
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
if (geom != cached_geom)
{
h_fe = hFESpace.GetFE(i);
l_fe = lFESpace.GetFE(i);
T.SetIdentityTransformation(h_fe->GetGeomType());
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
loc_prol.Transpose();
subY.SetSize(loc_prol.Height());
cached_geom = geom;
}
for (int vd = 0; vd < vdim; vd++)
{
l_dofs.Copy(l_vdofs);
lFESpace.DofsToVDofs(vd, l_vdofs);
h_dofs.Copy(h_vdofs);
hFESpace.DofsToVDofs(vd, h_vdofs);
x.GetSubVector(h_vdofs, subX);
for (int p = 0; p < h_dofs.Size(); ++p)
{
if (processed[lFESpace.DecodeDof(h_dofs[p])])
{
subX[p] = 0.0;
}
}
loc_prol.Mult(subX, subY);
y.AddElementVector(l_vdofs, subY);
}
for (int p = 0; p < h_dofs.Size(); ++p)
{
processed[lFESpace.DecodeDof(h_dofs[p])] = 1;
}
}
}
TensorProductPRefinementTransferOperator::
TensorProductPRefinementTransferOperator(
const FiniteElementSpace& lFESpace_,
const FiniteElementSpace& hFESpace_)
: Operator(hFESpace_.GetVSize(), lFESpace_.GetVSize()), lFESpace(lFESpace_),
hFESpace(hFESpace_)
{
// Assuming the same element type
Mesh* mesh = lFESpace.GetMesh();
dim = mesh->Dimension();
if (mesh->GetNE() == 0)
{
return;
}
const FiniteElement& el = *lFESpace.GetFE(0);
const TensorBasisElement* ltel =
dynamic_cast<const TensorBasisElement*>(&el);
MFEM_VERIFY(ltel, "Low order FE space must be tensor product space");
const TensorBasisElement* htel =
dynamic_cast<const TensorBasisElement*>(hFESpace.GetFE(0));
MFEM_VERIFY(htel, "High order FE space must be tensor product space");
const Array<int>& hdofmap = htel->GetDofMap();
const IntegrationRule& ir = hFESpace.GetFE(0)->GetNodes();
IntegrationRule irLex = ir;
// The quadrature points, or equivalently, the dofs of the high order space
// must be sorted in lexicographical order
for (int i = 0; i < ir.GetNPoints(); ++i)
{
irLex.IntPoint(i) = ir.IntPoint(hdofmap[i]);
}
NE = lFESpace.GetNE();
const DofToQuad& maps = el.GetDofToQuad(irLex, DofToQuad::TENSOR);
D1D = maps.ndof;
Q1D = maps.nqpt;
B = maps.B;
Bt = maps.Bt;
elem_restrict_lex_l =
lFESpace.GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
MFEM_VERIFY(elem_restrict_lex_l,
"Low order ElementRestriction not available");
elem_restrict_lex_h =
hFESpace.GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
MFEM_VERIFY(elem_restrict_lex_h,
"High order ElementRestriction not available");
localL.SetSize(elem_restrict_lex_l->Height(), Device::GetMemoryType());
localH.SetSize(elem_restrict_lex_h->Height(), Device::GetMemoryType());
localL.UseDevice(true);
localH.UseDevice(true);
MFEM_VERIFY(dynamic_cast<const ElementRestriction*>(elem_restrict_lex_h),
"High order element restriction is of unsupported type");
mask.SetSize(localH.Size(), Device::GetMemoryType());
static_cast<const ElementRestriction*>(elem_restrict_lex_h)
->BooleanMask(mask);
mask.UseDevice(true);
}
namespace TransferKernels
{
void Prolongation2D(const int NE, const int D1D, const int Q1D,
const Vector& localL, Vector& localH,
const Array<double>& B, const Vector& mask)
{
auto x_ = Reshape(localL.Read(), D1D, D1D, NE);
auto y_ = Reshape(localH.ReadWrite(), Q1D, Q1D, NE);
auto B_ = Reshape(B.Read(), Q1D, D1D);
auto m_ = Reshape(mask.Read(), Q1D, Q1D, NE);
localH = 0.0;
MFEM_FORALL(e, NE,
{
for (int dy = 0; dy < D1D; ++dy)
{
double sol_x[MAX_Q1D];
for (int qy = 0; qy < Q1D; ++qy)
{
sol_x[qy] = 0.0;
}
for (int dx = 0; dx < D1D; ++dx)
{
const double s = x_(dx, dy, e);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_x[qx] += B_(qx, dx) * s;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const double d2q = B_(qy, dy);
for (int qx = 0; qx < Q1D; ++qx)
{
y_(qx, qy, e) += d2q * sol_x[qx];
}
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
y_(qx, qy, e) *= m_(qx, qy, e);
}
}
});
}
void Prolongation3D(const int NE, const int D1D, const int Q1D,
const Vector& localL, Vector& localH,
const Array<double>& B, const Vector& mask)
{
auto x_ = Reshape(localL.Read(), D1D, D1D, D1D, NE);
auto y_ = Reshape(localH.ReadWrite(), Q1D, Q1D, Q1D, NE);
auto B_ = Reshape(B.Read(), Q1D, D1D);
auto m_ = Reshape(mask.Read(), Q1D, Q1D, Q1D, NE);
localH = 0.0;
MFEM_FORALL(e, NE,
{
for (int dz = 0; dz < D1D; ++dz)
{
double sol_xy[MAX_Q1D][MAX_Q1D];
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] = 0.0;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
double sol_x[MAX_Q1D];
for (int qx = 0; qx < Q1D; ++qx)
{
sol_x[qx] = 0;
}
for (int dx = 0; dx < D1D; ++dx)
{
const double s = x_(dx, dy, dz, e);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_x[qx] += B_(qx, dx) * s;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const double wy = B_(qy, dy);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] += wy * sol_x[qx];
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
const double wz = B_(qz, dz);
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
y_(qx, qy, qz, e) += wz * sol_xy[qy][qx];
}
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
y_(qx, qy, qz, e) *= m_(qx, qy, qz, e);
}
}
}
});
}
void Restriction2D(const int NE, const int D1D, const int Q1D,
const Vector& localH, Vector& localL,
const Array<double>& Bt, const Vector& mask)
{
auto x_ = Reshape(localH.Read(), Q1D, Q1D, NE);
auto y_ = Reshape(localL.ReadWrite(), D1D, D1D, NE);
auto Bt_ = Reshape(Bt.Read(), D1D, Q1D);
auto m_ = Reshape(mask.Read(), Q1D, Q1D, NE);
localL = 0.0;
MFEM_FORALL(e, NE,
{
for (int qy = 0; qy < Q1D; ++qy)
{
double sol_x[MAX_D1D];
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] = 0.0;
}
for (int qx = 0; qx < Q1D; ++qx)
{
const double s = m_(qx, qy, e) * x_(qx, qy, e);
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] += Bt_(dx, qx) * s;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
const double q2d = Bt_(dy, qy);
for (int dx = 0; dx < D1D; ++dx)
{
y_(dx, dy, e) += q2d * sol_x[dx];
}
}
}
});
}
void Restriction3D(const int NE, const int D1D, const int Q1D,
const Vector& localH, Vector& localL,
const Array<double>& Bt, const Vector& mask)
{
auto x_ = Reshape(localH.Read(), Q1D, Q1D, Q1D, NE);
auto y_ = Reshape(localL.ReadWrite(), D1D, D1D, D1D, NE);
auto Bt_ = Reshape(Bt.Read(), D1D, Q1D);
auto m_ = Reshape(mask.Read(), Q1D, Q1D, Q1D, NE);
localL = 0.0;
MFEM_FORALL(e, NE,
{
for (int qz = 0; qz < Q1D; ++qz)
{
double sol_xy[MAX_D1D][MAX_D1D];
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
sol_xy[dy][dx] = 0;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
double sol_x[MAX_D1D];
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] = 0;
}
for (int qx = 0; qx < Q1D; ++qx)
{
const double s = m_(qx, qy, qz, e) * x_(qx, qy, qz, e);
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] += Bt_(dx, qx) * s;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
const double wy = Bt_(dy, qy);
for (int dx = 0; dx < D1D; ++dx)
{
sol_xy[dy][dx] += wy * sol_x[dx];
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
const double wz = Bt_(dz, qz);
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
y_(dx, dy, dz, e) += wz * sol_xy[dy][dx];
}
}
}
}
});
}
} // namespace TransferKernels
TensorProductPRefinementTransferOperator::
~TensorProductPRefinementTransferOperator()
{
}
void TensorProductPRefinementTransferOperator::Mult(const Vector& x,
Vector& y) const
{
if (lFESpace.GetMesh()->GetNE() == 0)
{
return;
}
elem_restrict_lex_l->Mult(x, localL);
if (dim == 2)
{
TransferKernels::Prolongation2D(NE, D1D, Q1D, localL, localH, B, mask);
}
else if (dim == 3)
{
TransferKernels::Prolongation3D(NE, D1D, Q1D, localL, localH, B, mask);
}
else
{
MFEM_ABORT("TensorProductPRefinementTransferOperator::Mult not "
"implemented for dim = "
<< dim);
}
elem_restrict_lex_h->MultTranspose(localH, y);
}
void TensorProductPRefinementTransferOperator::MultTranspose(const Vector& x,
Vector& y) const
{
if (lFESpace.GetMesh()->GetNE() == 0)
{
return;
}
elem_restrict_lex_h->Mult(x, localH);
if (dim == 2)
{
TransferKernels::Restriction2D(NE, D1D, Q1D, localH, localL, Bt, mask);
}
else if (dim == 3)
{
TransferKernels::Restriction3D(NE, D1D, Q1D, localH, localL, Bt, mask);
}
else
{
MFEM_ABORT("TensorProductPRefinementTransferOperator::MultTranspose not "
"implemented for dim = "
<< dim);
}
elem_restrict_lex_l->MultTranspose(localL, y);
}
#ifdef MFEM_USE_MPI
TrueTransferOperator::TrueTransferOperator(const
ParFiniteElementSpace& lFESpace_,
const ParFiniteElementSpace& hFESpace_)
: lFESpace(lFESpace_), hFESpace(hFESpace_)
{
localTransferOperator = new TransferOperator(lFESpace_, hFESpace_);
tmpL.SetSize(lFESpace_.GetVSize());
tmpH.SetSize(hFESpace_.GetVSize());
hFESpace.GetRestrictionMatrix()->BuildTranspose();
}
TrueTransferOperator::~TrueTransferOperator()
{
delete localTransferOperator;
}
void TrueTransferOperator::Mult(const Vector& x, Vector& y) const
{
lFESpace.GetProlongationMatrix()->Mult(x, tmpL);
localTransferOperator->Mult(tmpL, tmpH);
hFESpace.GetRestrictionMatrix()->Mult(tmpH, y);
}
void TrueTransferOperator::MultTranspose(const Vector& x, Vector& y) const
{
hFESpace.GetRestrictionMatrix()->MultTranspose(x, tmpH);
localTransferOperator->MultTranspose(tmpH, tmpL);
lFESpace.GetProlongationMatrix()->MultTranspose(tmpL, y);
}
#endif
} // namespace mfem
+162
View File
@@ -0,0 +1,162 @@
// Copyright (c) 2010-2020, 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_TRANSFER_HPP
#define MFEM_TRANSFER_HPP
#include "../linalg/linalg.hpp"
#include "fespace.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
#endif
namespace mfem
{
/// Matrix-free transfer operator between finite element spaces
class TransferOperator : public Operator
{
private:
Operator* opr;
public:
/// Constructs a transfer operator from \p lFESpace to \p hFESpace.
/** No matrices are assembled, only the action to a vector is being computed.
If both spaces' FE collection pointers are pointing to the same collection
we assume that the grid was refined while keeping the order constant. If
the FE collections are different, it is assumed that both spaces have are
using the same mesh. If the first element of the high-order space is a
`TensorBasisElement`, the optimized tensor-product transfers are used. If
not, the general transfers used. */
TransferOperator(const FiniteElementSpace& lFESpace,
const FiniteElementSpace& hFESpace);
/// Destructor
virtual ~TransferOperator();
/// @brief Interpolation or prolongation of a vector \p x corresponding to the
/// coarse space to the vector \p y corresponding to the fine space.
virtual void Mult(const Vector& x, Vector& y) const override;
/// Restriction by applying the transpose of the Mult method.
/** The vector \p x corresponding to the fine space is restricted to the vector
\p y corresponding to the coarse space. */
virtual void MultTranspose(const Vector& x, Vector& y) const override;
};
/// Matrix-free transfer operator between finite element spaces on the same mesh
class PRefinementTransferOperator : public Operator
{
private:
const FiniteElementSpace& lFESpace;
const FiniteElementSpace& hFESpace;
public:
/// @brief Constructs a transfer operator from \p lFESpace to \p hFESpace
/// which have different FE collections.
/** No matrices are assembled, only the action to a vector is being computed.
The underlying finite elements need to implement the GetTransferMatrix
methods. */
PRefinementTransferOperator(const FiniteElementSpace& lFESpace_,
const FiniteElementSpace& hFESpace_);
/// Destructor
virtual ~PRefinementTransferOperator();
/// @brief Interpolation or prolongation of a vector \p x corresponding to the
/// coarse space to the vector \p y corresponding to the fine space.
virtual void Mult(const Vector& x, Vector& y) const override;
/// Restriction by applying the transpose of the Mult method.
/** The vector \p x corresponding to the fine space is restricted to the vector
\p y corresponding to the coarse space. */
virtual void MultTranspose(const Vector& x, Vector& y) const override;
};
/// @brief Matrix-free transfer operator between finite element spaces on the same
/// mesh exploiting the tensor product structure of the finite elements
class TensorProductPRefinementTransferOperator : public Operator
{
private:
const FiniteElementSpace& lFESpace;
const FiniteElementSpace& hFESpace;
int dim;
int NE;
int D1D;
int Q1D;
Array<double> B;
Array<double> Bt;
const Operator* elem_restrict_lex_l;
const Operator* elem_restrict_lex_h;
Vector mask;
mutable Vector localL;
mutable Vector localH;
public:
/// @brief Constructs a transfer operator from \p lFESpace to \p hFESpace which
/// have different FE collections.
/** No matrices are assembled, only the action to a vector is being computed.
The underlying finite elements need to be of the type `TensorBasisElement`. It
is also assumed that all the elements in the spaces are of the same type. */
TensorProductPRefinementTransferOperator(
const FiniteElementSpace& lFESpace_,
const FiniteElementSpace& hFESpace_);
/// Destructor
virtual ~TensorProductPRefinementTransferOperator();
/// @brief Interpolation or prolongation of a vector \p x corresponding to the
/// coarse space to the vector \p y corresponding to the fine space.
virtual void Mult(const Vector& x, Vector& y) const override;
/// Restriction by applying the transpose of the Mult method.
/** The vector \p x corresponding to the fine space is restricted to the vector
\p y corresponding to the coarse space. */
virtual void MultTranspose(const Vector& x, Vector& y) const override;
};
#ifdef MFEM_USE_MPI
/// @brief Matrix-free transfer operator between finite element spaces working on
/// true degrees of freedom
class TrueTransferOperator : public Operator
{
private:
const ParFiniteElementSpace& lFESpace;
const ParFiniteElementSpace& hFESpace;
TransferOperator* localTransferOperator;
mutable Vector tmpL;
mutable Vector tmpH;
public:
/// @brief Constructs a transfer operator working on true degrees of freedom from
/// from \p lFESpace to \p hFESpace
TrueTransferOperator(const ParFiniteElementSpace& lFESpace_,
const ParFiniteElementSpace& hFESpace_);
/// Destructor
~TrueTransferOperator();
/// @brief Interpolation or prolongation of a true dof vector \p x to a true dof
/// vector \p y.
/** The true dof vector \p x corresponding to the coarse space is restricted to
the true dof vector \p y corresponding to the fine space. */
virtual void Mult(const Vector& x, Vector& y) const override;
/// Restriction by applying the transpose of the Mult method.
/** The true dof vector \p x corresponding to the fine space is restricted to
the true dof vector \p y corresponding to the coarse space. */
virtual void MultTranspose(const Vector& x, Vector& y) const override;
};
#endif
} // namespace mfem
#endif
+5
View File
@@ -65,6 +65,11 @@ if (MFEM_USE_MPI)
list(APPEND HDRS communication.hpp)
endif()
if (MFEM_USE_ADIOS2)
list(APPEND SRCS adios2stream.cpp)
list(APPEND HDRS adios2stream.hpp)
endif()
convert_filenames_to_full_paths(SRCS)
convert_filenames_to_full_paths(HDRS)
+752
View File
@@ -0,0 +1,752 @@
// Copyright (c) 2010-2020, 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.
//
// Created on: Jan 22, 2019
// Author: William F Godoy godoywf@ornl.gov
// adios2: Adaptable Input/Output System https://github.com/ornladios/ADIOS2
#include "adios2stream.hpp"
#include "../fem/geom.hpp"
#include "../general/array.hpp"
#include "../mesh/element.hpp"
#include "../mesh/mesh.hpp"
#include "../fem/gridfunc.hpp"
#include <algorithm>
namespace mfem
{
namespace
{
// these functions might be included in adios2 upstream next release
template <class T>
adios2::Variable<T> SafeDefineVariable(adios2::IO io,
const std::string& variable_name,
const adios2::Dims& shape = adios2::Dims(),
const adios2::Dims& start = adios2::Dims(),
const adios2::Dims& count = adios2::Dims())
{
adios2::Variable<T> variable = io.InquireVariable<T>(variable_name);
if (variable)
{
if (variable.Count() != count &&
variable.ShapeID() == adios2::ShapeID::LocalArray)
{
variable.SetSelection({start, count});
}
}
else
{
variable = io.DefineVariable<T>(variable_name, shape, start, count);
}
return variable;
}
template <class T>
adios2::Attribute<T> SafeDefineAttribute(adios2::IO io,
const std::string& attribute_name,
const T& value,
const std::string& variable_name = "",
const std::string separator = "/")
{
adios2::Attribute<T> attribute = io.InquireAttribute<T>(attribute_name);
if (attribute)
{
return attribute;
}
return io.DefineAttribute<T>(attribute_name, value, variable_name, separator );
}
template <class T>
adios2::Attribute<T> SafeDefineAttribute(adios2::IO io,
const std::string& attribute_name,
const T* values, const size_t size,
const std::string& variable_name = "",
const std::string separator = "/")
{
adios2::Attribute<T> attribute = io.InquireAttribute<T>(attribute_name);
if (attribute)
{
return attribute;
}
return io.DefineAttribute<T>(attribute_name, values, size, variable_name,
separator );
}
bool SetBoolParameter(const std::string key,
const std::map<std::string, std::string>& parameters,
const bool default_value) noexcept
{
auto it = parameters.find(key);
if (it != parameters.end())
{
std::string value = it->second;
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
if (value == "on" || value == "true")
{
return true;
}
else if ( value == "off" || value == "false")
{
return false;
}
}
return default_value;
}
} //end empty namespace
// PUBLIC
#ifdef MFEM_USE_MPI
adios2stream::adios2stream(const std::string& name, const openmode mode,
MPI_Comm comm, const std::string engineType)
: name(name),
adios2_openmode(mode),
adios(new adios2::ADIOS(comm)),
io(adios->DeclareIO(name))
{
io.SetEngine(engineType);
}
#else
adios2stream::adios2stream(const std::string& name, const openmode mode,
const std::string engineType)
: name(name),
adios2_openmode(mode),
adios(new adios2::ADIOS()),
io(adios->DeclareIO(name))
{
io.SetEngine(engineType);
}
#endif
adios2stream::~adios2stream()
{
if (engine)
{
SafeDefineAttribute<std::string>(io, "vtk.xml", VTKSchema() );
engine.Close();
}
}
void adios2stream::SetParameters(
const std::map<std::string, std::string>& parameters)
{
io.SetParameters(parameters);
refine = SetBoolParameter("RefinedData", parameters, true);
}
void adios2stream::SetParameter(const std::string key,
const std::string value) noexcept
{
io.SetParameter(key, value);
if (key == "RefinedData")
{
refine = SetBoolParameter("RefinedData", io.Parameters(), true);
}
}
void adios2stream::BeginStep()
{
if (!engine)
{
engine = io.Open(name, adios2::Mode::Write);
}
engine.BeginStep();
active_step = true;
}
void adios2stream::EndStep()
{
if (!engine || !active_step)
{
const std::string message = "MFEM adios2stream error: calling EndStep "
"on uninitialized step (need BeginStep)";
mfem_error(message.c_str());
}
SafeDefineAttribute<std::string>(io, "vtk.xml", VTKSchema() );
engine.EndStep();
active_step = false;
}
void adios2stream::SetTime(const double time)
{
adios2::Variable<double> var_time = SafeDefineVariable<double>(io, "TIME");
engine.Put(var_time, time);
transient = true;
}
void adios2stream::SetCycle(const int cycle)
{
adios2::Variable<int> var_cycle = SafeDefineVariable<int>(io,"CYCLE");
engine.Put(var_cycle, cycle);
}
void adios2stream::SetRefinementLevel(const int level) noexcept
{
refinement_level = level;
}
size_t adios2stream::CurrentStep() const
{
return engine.CurrentStep();
}
void adios2stream::Close()
{
if (engine)
{
if (!active_step)
{
SafeDefineAttribute<std::string>(io, "vtk.xml", VTKSchema() );
}
engine.Close();
}
if (adios)
{
adios.reset();
}
}
// PROTECTED (accessible by friend class Mesh)
void adios2stream::Print(const Mesh& mesh, const mode print_mode)
{
auto lf_DefineMeshMetadata = [this](Mesh& mesh)
{
// check types are constant
if (!IsConstantElementType(mesh.elements))
{
throw std::invalid_argument("MFEM::adios2stream ERROR: non-constant "
" element types not yet implemented\n");
}
// format info
SafeDefineAttribute<std::string>(io, "format", "MFEM ADIOS2 BP v0.1" );
SafeDefineAttribute<std::string>(io, "format/version", "0.1" );
std::string mesh_type = "Unknown";
std::vector<std::string> viz_tools;
viz_tools.reserve(2); //for now
if (mesh.NURBSext)
{
mesh_type = "MFEM NURBS";
viz_tools.push_back("NONE");
}
else if (mesh.ncmesh)
{
mesh_type = "MFEM mesh v1.1";
viz_tools.push_back("NONE");
}
else
{
mesh_type = "MFEM mesh v1.0";
viz_tools.push_back("Paraview: ADIOS2VTXReader");
viz_tools.push_back("VTK: vtkADIOS2VTXReader.h");
}
SafeDefineAttribute<std::string>(io, "format/mfem_mesh", mesh_type );
SafeDefineAttribute<std::string>(io, "format/viz_tools", viz_tools.data(),
viz_tools.size() );
// elements
const uint32_t dimension = static_cast<int32_t>(mesh.Dimension());
SafeDefineAttribute<uint32_t>(io, "dimension", dimension);
SafeDefineVariable<uint32_t>(io,"NumOfElements", {adios2::LocalValueDim});
SafeDefineVariable<uint32_t>(io, "types");
size_t nelements = 0;
size_t element_nvertices = 0;
size_t nvertices = 0;
if (refine)
{
for (int i = 0; i < mesh.GetNE(); ++i)
{
const Geometry::Type type = mesh.GetElementBaseGeometry(i);
RefinedGeometry* refined_geometry = GlobGeometryRefiner.Refine(type,
refinement_level, 1);
if (refined_geometry == nullptr)
{
mfem_error("ERROR: could not refine geometry in call to Save with adios2stream \n");
}
element_nvertices = static_cast<size_t>(Geometries.GetVertices(
type)->GetNPoints());
nelements += refined_geometry->RefGeoms.Size() / element_nvertices;
nvertices += refined_geometry->RefPts.GetNPoints();
}
refined_mesh_nelements = nelements;
refined_mesh_nvertices = nvertices;
}
else
{
nelements = static_cast<size_t>(mesh.GetNE());
element_nvertices = static_cast<size_t>(mesh.elements[0]->GetNVertices());
}
SafeDefineVariable<uint64_t>(io, "connectivity", {}, {}, {nelements, element_nvertices+1});
// vertices
SafeDefineVariable<uint32_t>(io,"NumOfVertices", {adios2::LocalValueDim});
if (refine)
{
SafeDefineVariable<double>( io, "vertices", {}, {}, {nvertices, static_cast<size_t>(dimension)});
}
else
{
const GridFunction* grid_function = mesh.GetNodes();
if (grid_function == nullptr)
{
const size_t nVertices = static_cast<size_t>(mesh.GetNV());
const size_t spaceDim = static_cast<size_t>(mesh.SpaceDimension());
// similar to Ordering::byVDIM
SafeDefineVariable<double>( io, "vertices", {}, {}, {nVertices, spaceDim});
}
else
{
const size_t size = static_cast<size_t>(grid_function->Size());
const FiniteElementSpace* fes = grid_function->FESpace();
const size_t components = static_cast<size_t>(fes->GetVDim());
const size_t tuples = size /components;
SafeDefineVariable<double>(io, "vertices", {}, {}, {tuples, components} );
if (fes->GetOrdering() == Ordering::byNODES)
{
ordering_by_node = true;
}
}
}
};
auto lf_PrintRefinedMeshData = [this](Mesh& mesh)
{
// elements and vertices
engine.Put("NumOfElements", static_cast<uint32_t>(refined_mesh_nelements));
engine.Put("NumOfVertices", static_cast<uint32_t>(refined_mesh_nvertices));
const uint32_t vtkType =
GLVISToVTKType(static_cast<int>(mesh.elements[0]->GetGeometryType()));
engine.Put("types", vtkType);
adios2::Variable<double> var_vertices = io.InquireVariable<double>("vertices");
adios2::Variable<double>::Span span_vertices = engine.Put<double>(var_vertices);
adios2::Variable<uint64_t> var_connectivity =
io.InquireVariable<uint64_t>("connectivity");
adios2::Variable<uint64_t>::Span span_connectivity = engine.Put<uint64_t>
(var_connectivity);
size_t span_vertices_offset = 0;
size_t span_connectivity_offset = 0;
// use for setting absolute node id for each element
size_t point_id = 0;
DenseMatrix pmatrix;
for (int e = 0; e < mesh.GetNE(); ++e)
{
const Geometry::Type type = mesh.GetElementBaseGeometry(e);
RefinedGeometry* refined_geometry = GlobGeometryRefiner.Refine(type,
refinement_level, 1);
// vertices
mesh.GetElementTransformation(e)->Transform(refined_geometry->RefPts, pmatrix);
for (int i = 0; i < pmatrix.Width(); ++i)
{
for (int j = 0; j < pmatrix.Height(); ++j)
{
span_vertices[span_vertices_offset + i*pmatrix.Height() + j] = pmatrix(j, i);
}
}
span_vertices_offset += static_cast<size_t>(pmatrix.Width()*pmatrix.Height());
// connectivity
const int nv = Geometries.GetVertices(type)->GetNPoints();
const Array<int> &element_vertices = refined_geometry->RefGeoms;
for (int v = 0; v < element_vertices.Size();)
{
span_connectivity[span_connectivity_offset] = static_cast<uint64_t>(nv);
++span_connectivity_offset;
for (int k =0; k < nv; k++, v++ )
{
span_connectivity[span_connectivity_offset] = static_cast<uint64_t>
(point_id + element_vertices[v]);
++span_connectivity_offset;
}
}
point_id += static_cast<size_t>(refined_geometry->RefPts.GetNPoints());
}
for (int e = 0; e < mesh.GetNE(); ++e)
{
const Geometry::Type type = mesh.GetElementBaseGeometry(e);
RefinedGeometry* refined_geometry = GlobGeometryRefiner.Refine(type,
refinement_level, 1);
}
};
auto lf_PrintMeshData = [&](Mesh& mesh)
{
if (refine)
{
lf_PrintRefinedMeshData(mesh);
return;
}
// elements
engine.Put("NumOfElements", static_cast<uint32_t>(mesh.GetNE()));
const uint32_t vtkType =
GLVISToVTKType(static_cast<int>(mesh.elements[0]->GetGeometryType()));
engine.Put("types", vtkType);
adios2::Variable<uint64_t> varConnectivity =
io.InquireVariable<uint64_t>("connectivity");
// zero-copy access to adios2 buffer to put non-contiguous to contiguous memory
adios2::Variable<uint64_t>::Span spanConnectivity =
engine.Put<uint64_t>(varConnectivity);
size_t elementPosition = 0;
for (int e = 0; e < mesh.GetNE(); ++e)
{
const int nVertices = mesh.elements[e]->GetNVertices();
spanConnectivity[elementPosition] = nVertices;
for (int v = 0; v < nVertices; ++v)
{
spanConnectivity[elementPosition + v + 1] =
mesh.elements[e]->GetVertices()[v];
}
elementPosition += nVertices + 1;
}
// vertices
engine.Put("NumOfVertices", static_cast<uint32_t>(mesh.GetNV()));
if (mesh.GetNodes() == nullptr)
{
adios2::Variable<double> varVertices = io.InquireVariable<double>("vertices");
// zero-copy access to adios2 buffer to put non-contiguous to contiguous memory
adios2::Variable<double>::Span spanVertices = engine.Put(varVertices);
for (int v = 0; v < mesh.GetNV(); ++v)
{
const int space_dim = mesh.SpaceDimension();
for (int coord = 0; coord < space_dim; ++coord)
{
spanVertices[v * space_dim + coord] = mesh.vertices[v](coord);
}
}
}
else
{
const GridFunction* grid_function = mesh.GetNodes();
if (ordering_by_node)
{
adios2::Variable<double> varVertices = io.InquireVariable<double>("vertices");
// zero-copy access to adios2 buffer to put non-contiguous to contiguous memory
adios2::Variable<double>::Span spanVertices = engine.Put(varVertices);
const size_t size = static_cast<size_t>(grid_function->Size());
const FiniteElementSpace* fes = grid_function->FESpace();
const size_t components = static_cast<size_t>(fes->GetVDim());
const size_t tuples = size /components;
const double* data = grid_function->GetData();
for (size_t i = 0; i < tuples; ++i)
{
for (size_t j = 0; j < components; ++j)
{
spanVertices[i*components + j] = data[j*tuples + i];
}
}
}
else
{
grid_function->Print(*this, "vertices");
}
}
};
// BODY OF FUNCTION STARTS HERE
try
{
Mesh ref_mesh(mesh);
lf_DefineMeshMetadata(ref_mesh);
if (!engine) // if Engine is closed
{
engine = io.Open(name, adios2::Mode::Write);
}
lf_PrintMeshData(ref_mesh);
if (print_mode == mode::sync)
{
engine.PerformPuts();
}
}
catch (std::exception& e)
{
const std::string warning =
"MFEM: adios2stream exception caught, invalid bp dataset: " + name +
"," + e.what();
mfem_warning( warning.c_str());
}
}
void adios2stream::Save(const GridFunction& grid_function,
const std::string& variable_name, const data_type type)
{
auto lf_SafeDefine = [&](const std::string& variable_name,
const size_t tuples, const size_t components,
const Ordering::Type ordering,
const std::string& fespace_name)
{
adios2::Variable<double> var = io.InquireVariable<double>(variable_name);
if (!var)
{
if (components == 1 && type == adios2stream::data_type::point_data)
{
io.DefineVariable<double>(variable_name, {}, {}, {tuples*components});
}
else
{
const adios2::Dims count = (ordering == Ordering::byNODES) ?
adios2::Dims{components, tuples} :
adios2::Dims{tuples, components};
io.DefineVariable<double>(variable_name, {}, {}, count);
}
SafeDefineAttribute<std::string>(io, "FiniteElementSpace",
fespace_name, variable_name);
}
};
// BODY OF FUNCTION STARTS HERE
const std::map<std::string, std::string> parameters = io.Parameters();
const bool full_data = SetBoolParameter("FullData", parameters, false);
if (!full_data && !refine)
{
return;
}
const FiniteElementSpace* fes = grid_function.FESpace();
if (refine)
{
const Mesh *mesh = fes->GetMesh();
const size_t components = static_cast<size_t>(grid_function.VectorDim());
// const size_t tuples = static_cast<size_t>(mesh->GetNV());
const size_t tuples = refined_mesh_nvertices;
lf_SafeDefine(variable_name, tuples, components,
Ordering::byVDIM, std::string(fes->FEColl()->Name()));
if (type == adios2stream::data_type::point_data)
{
point_data_variables.insert(variable_name);
}
RefinedGeometry* refined_geometry;
DenseMatrix transform;
// zero-copy access to adios2 buffer to put non-contiguous to contiguous memory
adios2::Variable<double> variable = io.InquireVariable<double>(variable_name);
adios2::Variable<double>::Span span = engine.Put<double>(variable);
size_t offset = 0;
if (components == 1)
{
Vector scalar;
const int nelements = mesh->GetNE();
for (int e = 0; e < nelements; ++e)
{
refined_geometry = GlobGeometryRefiner.Refine(
mesh->GetElementBaseGeometry(e), refinement_level, 1);
grid_function.GetValues(e, refined_geometry->RefPts, scalar, transform);
const int size = scalar.Size();
for (int i = 0; i < size; ++i)
{
const double value = scalar(i);
span.at(offset+i) = value;
}
offset += static_cast<size_t>(size);
}
}
else
{
DenseMatrix vector;
for (int e = 0; e < mesh->GetNE(); ++e)
{
refined_geometry = GlobGeometryRefiner.Refine(
mesh->GetElementBaseGeometry(e), refinement_level, 1);
grid_function.GetVectorValues(e, refined_geometry->RefPts, vector, transform);
for (int i = 0; i < vector.Width(); ++i)
{
for (int j = 0; j < vector.Height(); ++j)
{
span[offset + i*vector.Height() + j] = vector(j, i);
}
}
offset += static_cast<size_t>(vector.Width()*vector.Height());
}
}
}
if (full_data)
{
const size_t size = static_cast<size_t>(grid_function.Size());
const size_t components = static_cast<size_t>(fes->GetVDim());
const size_t tuples = size /components;
lf_SafeDefine(variable_name +"/full", tuples, components,
fes->GetOrdering(),
std::string(fes->FEColl()->Name()) );
// calls Vector::Print
grid_function.Print(*this, variable_name+"/full");
if (!refine && type == adios2stream::data_type::point_data)
{
point_data_variables.insert(variable_name+"/full");
}
}
}
// PRIVATE
int32_t adios2stream::GLVISToVTKType(
const int glvisType) const noexcept
{
uint32_t vtkType = 0;
switch (glvisType)
{
case Geometry::Type::POINT:
vtkType = 1;
break;
case Geometry::Type::SEGMENT:
vtkType = 3;
break;
case Geometry::Type::TRIANGLE:
vtkType = 5;
break;
case Geometry::Type::SQUARE:
// vtkType = 8;
vtkType = 9;
break;
case Geometry::Type::TETRAHEDRON:
vtkType = 10;
break;
case Geometry::Type::CUBE:
// vtkType = 11;
vtkType = 12;
break;
case Geometry::Type::PRISM:
vtkType = 13;
break;
default:
vtkType = 0;
break;
}
return vtkType;
}
bool adios2stream::IsConstantElementType(const Array<Element*>& elements ) const
noexcept
{
bool isConstType = true;
const Geometry::Type type = elements[0]->GetGeometryType();
for (int e = 1; e < elements.Size(); ++e)
{
if (type != elements[e]->GetGeometryType())
{
isConstType = false;
break;
}
}
return isConstType;
}
std::string adios2stream::VTKSchema() const noexcept
{
std::string vtkSchema = R"(
<?xml version="1.0"?>
<VTKFile type="UnstructuredGrid" version="0.1" byte_order="LittleEndian">
<UnstructuredGrid>
<Piece NumberOfPoints="NumOfVertices" NumberOfCells="NumOfElements">
<Points>
<DataArray Name="vertices" />)";
vtkSchema += R"(
</Points>
<Cells>
<DataArray Name="connectivity" />
<DataArray Name="types" />
</Cells>
<PointData>)";
if (point_data_variables.empty())
{
vtkSchema += "\n";
}
else
{
for (const std::string& point_datum : point_data_variables )
{
vtkSchema += " <DataArray Name=\"" + point_datum +"\"/>\n";
}
}
if (transient)
{
vtkSchema += " <DataArray Name=\"TIME\">\n";
vtkSchema += " TIME\n";
vtkSchema += " </DataArray>\n";
}
vtkSchema += R"(
</PointData>
</Piece>
</UnstructuredGrid>
</VTKFile>)";
return vtkSchema;
}
adios2::Mode adios2stream::ToADIOS2Mode(const adios2stream::openmode mode) const
noexcept
{
adios2::Mode adios2Mode = adios2::Mode::Undefined;
switch (mode)
{
case adios2stream::openmode::out:
adios2Mode = adios2::Mode::Write;
break;
case adios2stream::openmode::in:
adios2Mode = adios2::Mode::Read;
break;
default:
const std::string message = "MFEM adios2stream ERROR: only "
"openmode::out and openmode::in "
" are valid, in call to adios2stream constructor";
mfem_error(message.c_str());
}
return adios2Mode;
}
} // end namespace mfem
+233
View File
@@ -0,0 +1,233 @@
// Copyright (c) 2010-2020, 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.
//
// Created on: Jan 22, 2019
// Author: William F Godoy godoywf@ornl.gov
// adios2: Adaptable Input/Output System https://github.com/ornladios/ADIOS2
#ifndef MFEM_ADIOS2STREAM
#define MFEM_ADIOS2STREAM
#include "../config/config.hpp"
#include <map>
#include <memory> // std::unique_ptr
#include <string>
#include <set>
#ifdef MFEM_USE_MPI
#include <mpi.h>
#endif
#include <adios2.h>
namespace mfem
{
// forward declaring classes to avoid circular references
class Vector;
class GridFunction;
class Mesh;
class ADIOS2DataCollection;
template <class T>
class Array;
class Element;
class adios2stream
{
friend class Vector;
friend class GridFunction;
friend class Mesh;
friend class ADIOS2DataCollection;
public:
/**
* Open modes for adios2stream (from std::fstream)
* out: write
* in: read
* app: append
*/
enum class openmode { out, in, app };
/** Print and Save modes, deferred is done at Close or EndStep, sync is immediate */
enum class mode {sync, deferred};
enum class data_type {none, point_data, cell_data};
#ifdef MFEM_USE_MPI
/**
* adios2stream MPI constructor, allows for passing parameters in source
* code (compile-time) only.
* @param name stream name
* @param mode adios2stream::openmode::in (Read), adios2stream::openmode::out
* (Write)
* @param comm MPI communicator establishing domain for fstream
* @param engine_type available adios2 engine, default is BPFile
* see https://adios2.readthedocs.io/en/latest/engines/engines.html
* @throws std::invalid_argument (user input error) or std::runtime_error
* (system error)
*/
adios2stream(const std::string& name, const openmode mode, MPI_Comm comm,
const std::string engine_type = "BPFile");
#else
/**
* adios2stream Non-MPI serial constructor, allows for passing parameters in
* source code (compile-time) only.
* @param name stream name
* @param mode adios2stream::openmode::in (Read), adios2stream::openmode::out
* (Write)
* @param engine_type available adios2 engine, default is BPFile
* @throws std::invalid_argument (user input error) or std::runtime_error
* (system error)
*/
adios2stream(const std::string& name, const openmode mode,
const std::string engine_type = "BPFile");
#endif
/** calls Close if stream is valid basically follows C++ RAII **/
virtual ~adios2stream();
/**
* Set parameters for a particular adios2stream Engine
* See https://adios2.readthedocs.io/en/latest/engines/engines.html
* @param parameters map of key/value string elements
*/
void SetParameters(const std::map<std::string, std::string>& parameters =
std::map<std::string, std::string>());
/**
* Single parameter version of SetParameters passing a key/value pair
* See https://adios2.readthedocs.io/en/latest/engines/engines.html
* @param key input parameter key
* @param value input parameter value
*/
void SetParameter(const std::string key, const std::string value) noexcept;
/** Begins an I/O step */
void BeginStep();
/** Ends the current step, by default transports the data */
void EndStep();
/**
* Associates a physical time with the current I/O step as TIME variable
* @param time input physical time
*/
void SetTime(const double time);
/**
* Associates a current time step (cycle) with the current I/O step as CYCLE variable
* @param cycle physical time
*/
void SetCycle(const int cycle);
/**
* Input to the Global Geometry Refiner
* @param level input level
*/
void SetRefinementLevel(const int level) noexcept;
/** Return the current step between BeginStep and EndStep */
size_t CurrentStep() const;
/** Finished interaction with adios2stream and flushes the data */
void Close();
protected:
/**
* Called from friend class Mesh (which is called from ParMesh)
* @param mesh input Mesh object to print
* @param print_mode sync: one at a time, deferred: collected (pre-fetch)
*/
void Print(const Mesh& mesh, const adios2stream::mode print_mode = mode::sync);
void Save(const GridFunction& grid_function, const std::string& variable_name,
const data_type type);
private:
/** placeholder for engine name */
const std::string name;
/** placeholder for engine openmode */
const openmode adios2_openmode;
/** main adios2 object that owns all the io and engine components */
std::unique_ptr<adios2::ADIOS> adios;
/** io object to set parameters, variables and engines */
adios2::IO io;
/** heavy object doing system-level I/O operations */
adios2::Engine engine;
/** true: transient problem (SetTime is called) */
bool transient = false;
/** true : engine step is active after engine.BeginStep(),
* false: inactive after engine.EndStep() */
bool active_step = false;
/** true: mesh is defined, false: not yet */
bool is_mesh_defined = false;
/** ordering of the nodes to be passed to the schema as an attribute
* true: XXX YYY ZZZ, false: XYZ, XYZ, XYZ
* if true it must swap the vertices to Ordering::byDIM*/
bool ordering_by_node = false;
/** true: refine solution at Save */
bool refine = true;
/** refinement level at Save and Print */
int refinement_level = 1;
/** save for point data */
size_t refined_mesh_nvertices = 0;
/** save for cell data */
size_t refined_mesh_nelements = 0;
/** saves the variable names representing point data */
std::set<std::string> point_data_variables;
/**
* Map glvis element types to VTK element types
* @param glvisType input
* @return VTK element type
*/
int32_t GLVISToVTKType(const int glvisType) const noexcept;
/** sets the current vtk_schema from point data arrays to be parsed
* in VTK for Paraview visualization */
std::string VTKSchema() const noexcept;
/**
* Checks if array of elements contains only constant types
* @param elements array input to check
* @return true: types are constant, false: mixed types
*/
bool IsConstantElementType(const Array<Element*>& elements ) const noexcept;
/**
* Maps to appropriate adios2::Mode from out, in to write, read
* @param mode
* @return
*/
adios2::Mode ToADIOS2Mode(const adios2stream::openmode mode) const noexcept;
};
} // end namespace mfem
#endif /* MFEM_ADIOS2STREAM */
+12
View File
@@ -114,4 +114,16 @@ void SetGlobalMPI_Comm(MPI_Comm comm);
#define MFEM_THREAD_LOCAL thread_local
// MFEM_DEPRECATED macro to mark obsolete functions and methods
// see https://stackoverflow.com/questions/295120/c-mark-as-deprecated
#if defined(__GNUC__) || defined(__clang__)
#define MFEM_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define MFEM_DEPRECATED __declspec(deprecated)
#else
#pragma message("WARNING: You need to implement MFEM_DEPRECATED for this compiler")
#define MFEM_DEPRECATED
#endif
#endif
+3
View File
@@ -147,6 +147,9 @@ const char *GetConfigStr()
#endif
#ifdef MFEM_USE_OCCA
"MFEM_USE_OCCA\n"
#endif
#ifdef MFEM_USE_ADIOS2
"MFEM_USE_ADIOS2\n"
#endif
"MFEM_TIMER_TYPE = " EXPAND_AND_QUOTE(MFEM_TIMER_TYPE)
;
+35 -9
View File
@@ -1590,6 +1590,8 @@ void HypreParMatrix::Destroy()
}
}
#if MFEM_HYPRE_VERSION < 21400
HypreParMatrix *Add(double alpha, const HypreParMatrix &A,
double beta, const HypreParMatrix &B)
{
@@ -1607,6 +1609,39 @@ HypreParMatrix *Add(double alpha, const HypreParMatrix &A,
return C;
}
HypreParMatrix * ParAdd(const HypreParMatrix *A, const HypreParMatrix *B)
{
hypre_ParCSRMatrix * C = internal::hypre_ParCSRMatrixAdd(*A,*B);
hypre_MatvecCommPkgCreate(C);
return new HypreParMatrix(C);
}
#else
HypreParMatrix *Add(double alpha, const HypreParMatrix &A,
double beta, const HypreParMatrix &B)
{
hypre_ParCSRMatrix *C;
hypre_ParcsrAdd(alpha, A, beta, B, &C);
hypre_MatvecCommPkgCreate(C);
return new HypreParMatrix(C);
}
HypreParMatrix * ParAdd(const HypreParMatrix *A, const HypreParMatrix *B)
{
hypre_ParCSRMatrix *C;
hypre_ParcsrAdd(1.0, *A, 1.0, *B, &C);
hypre_MatvecCommPkgCreate(C);
return new HypreParMatrix(C);
}
#endif
HypreParMatrix * ParMult(const HypreParMatrix *A, const HypreParMatrix *B,
bool own_matrix)
{
@@ -1624,15 +1659,6 @@ HypreParMatrix * ParMult(const HypreParMatrix *A, const HypreParMatrix *B,
return C;
}
HypreParMatrix * ParAdd(const HypreParMatrix *A, const HypreParMatrix *B)
{
hypre_ParCSRMatrix * C = internal::hypre_ParCSRMatrixAdd(*A,*B);
hypre_MatvecCommPkgCreate(C);
return new HypreParMatrix(C);
}
HypreParMatrix * RAP(const HypreParMatrix *A, const HypreParMatrix *P)
{
HYPRE_Int P_owns_its_col_starts =
+55
View File
@@ -543,4 +543,59 @@ void RectangularConstrainedOperator::Mult(const Vector &x, Vector &y) const
}
}
double PowerMethod::EstimateLargestEigenvalue(Operator& opr, Vector& v0,
int numSteps, double tolerance, int seed)
{
v1.SetSize(v0.Size());
v0.Randomize(seed);
double eigenvalue = 1.0;
for (int iter = 0; iter < numSteps; ++iter)
{
double normV0;
#ifdef MFEM_USE_MPI
if (comm != MPI_COMM_NULL)
{
normV0 = InnerProduct(comm, v0, v0);
}
else
{
normV0 = InnerProduct(v0, v0);
}
#else
normV0 = InnerProduct(v0, v0);
#endif
v0 /= sqrt(normV0);
opr.Mult(v0, v1);
double eigenvalueNew;
#ifdef MFEM_USE_MPI
if (comm != MPI_COMM_NULL)
{
eigenvalueNew = InnerProduct(comm, v0, v1);
}
else
{
eigenvalueNew = InnerProduct(v0, v1);
}
#else
eigenvalueNew = InnerProduct(v0, v1);
#endif
double diff = std::abs((eigenvalueNew - eigenvalue) / eigenvalue);
eigenvalue = eigenvalueNew;
std::swap(v0, v1);
if (diff < tolerance)
{
break;
}
}
return eigenvalue;
}
}
+33
View File
@@ -762,6 +762,39 @@ public:
virtual ~RectangularConstrainedOperator() { if (own_A) { delete A; } }
};
/** @brief PowerMethod helper class to estimate the largest eigenvalue of an
operator using the iterative power method. */
class PowerMethod
{
Vector v1;
#ifdef MFEM_USE_MPI
MPI_Comm comm;
#endif
public:
#ifdef MFEM_USE_MPI
PowerMethod() : comm(MPI_COMM_NULL) {}
#else
PowerMethod() {}
#endif
#ifdef MFEM_USE_MPI
PowerMethod(MPI_Comm _comm) : comm(_comm) {}
#endif
/// @brief Returns an estimate of the largest eigenvalue of the operator \p opr
/// using the iterative power method.
/** \p v0 is being used as the vector for the iterative process and will contain
the eigenvector corresponding to the largest eigenvalue after convergence.
The maximum number of iterations may set with \p numSteps, the relative
tolerance with \p tolerance and the seed of the random initialization of
\p v0 with \p seed. */
double EstimateLargestEigenvalue(Operator& opr, Vector& v0,
int numSteps = 10, double tolerance = 1e-8,
int seed = 12345);
};
}
#endif
+191 -1
View File
@@ -170,6 +170,174 @@ void OperatorJacobiSmoother::Mult(const Vector &x, Vector &y) const
MFEM_FORALL(i, N, Y[i] += DI[i] * R[i]; );
}
OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
const Vector &d,
const Array<int>& ess_tdofs,
int order_, double max_eig_estimate_)
:
Solver(d.Size()),
order(order_),
max_eig_estimate(max_eig_estimate_),
N(d.Size()),
dinv(N),
diag(d),
coeffs(order),
ess_tdof_list(ess_tdofs),
residual(N),
oper(oper_) { Setup(); }
#ifdef MFEM_USE_MPI
OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
const Vector &d,
const Array<int>& ess_tdofs,
int order_, MPI_Comm comm, int power_iterations, double power_tolerance)
#else
OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
const Vector &d,
const Array<int>& ess_tdofs,
int order_, int power_iterations, double power_tolerance)
#endif
: Solver(d.Size()),
order(order_),
N(d.Size()),
dinv(N),
diag(d),
coeffs(order),
ess_tdof_list(ess_tdofs),
residual(N),
oper(oper_)
{
OperatorJacobiSmoother invDiagOperator(diag, ess_tdofs, 1.0);
ProductOperator diagPrecond(&invDiagOperator, oper, false, false);
#ifdef MFEM_USE_MPI
PowerMethod powerMethod(comm);
#else
PowerMethod powerMethod;
#endif
Vector ev(oper->Width());
max_eig_estimate = powerMethod.EstimateLargestEigenvalue(diagPrecond, ev,
power_iterations, power_tolerance);
Setup();
}
void OperatorChebyshevSmoother::Setup()
{
// Invert diagonal
residual.UseDevice(true);
auto D = diag.Read();
auto X = dinv.Write();
MFEM_FORALL(i, N, X[i] = 1.0 / D[i]; );
auto I = ess_tdof_list.Read();
MFEM_FORALL(i, ess_tdof_list.Size(), X[I[i]] = 1.0; );
// Set up Chebyshev coefficients
// For reference, see e.g., Parallel multigrid smoothing: polynomial versus
// Gauss-Seidel by Adams et al.
double upper_bound = 1.2 * max_eig_estimate;
double lower_bound = 0.3 * max_eig_estimate;
double theta = 0.5 * (upper_bound + lower_bound);
double delta = 0.5 * (upper_bound - lower_bound);
switch (order-1)
{
case 0:
{
coeffs[0] = 1.0 / theta;
break;
}
case 1:
{
double tmp_0 = 1.0/(pow(delta, 2) - 2*pow(theta, 2));
coeffs[0] = -4*theta*tmp_0;
coeffs[1] = 2*tmp_0;
break;
}
case 2:
{
double tmp_0 = 3*pow(delta, 2);
double tmp_1 = pow(theta, 2);
double tmp_2 = 1.0/(-4*pow(theta, 3) + theta*tmp_0);
coeffs[0] = tmp_2*(tmp_0 - 12*tmp_1);
coeffs[1] = 12/(tmp_0 - 4*tmp_1);
coeffs[2] = -4*tmp_2;
break;
}
case 3:
{
double tmp_0 = pow(delta, 2);
double tmp_1 = pow(theta, 2);
double tmp_2 = 8*tmp_0;
double tmp_3 = 1.0/(pow(delta, 4) + 8*pow(theta, 4) - tmp_1*tmp_2);
coeffs[0] = tmp_3*(32*pow(theta, 3) - 16*theta*tmp_0);
coeffs[1] = tmp_3*(-48*tmp_1 + tmp_2);
coeffs[2] = 32*theta*tmp_3;
coeffs[3] = -8*tmp_3;
break;
}
case 4:
{
double tmp_0 = 5*pow(delta, 4);
double tmp_1 = pow(theta, 4);
double tmp_2 = pow(theta, 2);
double tmp_3 = pow(delta, 2);
double tmp_4 = 60*tmp_3;
double tmp_5 = 20*tmp_3;
double tmp_6 = 1.0/(16*pow(theta, 5) - pow(theta, 3)*tmp_5 + theta*tmp_0);
double tmp_7 = 160*tmp_2;
double tmp_8 = 1.0/(tmp_0 + 16*tmp_1 - tmp_2*tmp_5);
coeffs[0] = tmp_6*(tmp_0 + 80*tmp_1 - tmp_2*tmp_4);
coeffs[1] = tmp_8*(tmp_4 - tmp_7);
coeffs[2] = tmp_6*(-tmp_5 + tmp_7);
coeffs[3] = -80*tmp_8;
coeffs[4] = 16*tmp_6;
break;
}
default:
MFEM_ABORT("Chebyshev smoother not implemented for order = " << order);
}
}
void OperatorChebyshevSmoother::Mult(const Vector& x, Vector &y) const
{
if (iterative_mode)
{
MFEM_ABORT("Chebyshev smoother not implemented for iterative mode");
}
if (!oper)
{
MFEM_ABORT("Chebyshev smoother requires operator");
}
residual = x;
helperVector.SetSize(x.Size());
y.UseDevice(true);
y = 0.0;
for (int k = 0; k < order; ++k)
{
// Apply
if (k > 0)
{
oper->Mult(residual, helperVector);
residual = helperVector;
}
// Scale residual by inverse diagonal
const int n = N;
auto Dinv = dinv.Read();
auto R = residual.ReadWrite();
MFEM_FORALL(i, n, R[i] *= Dinv[i]; );
// Add weighted contribution to y
auto Y = y.ReadWrite();
auto C = coeffs.Read();
MFEM_FORALL(i, n, Y[i] += C[k] * R[i]; );
}
}
void SLISolver::UpdateVectors()
{
@@ -382,13 +550,24 @@ void CGSolver::Mult(const Vector &b, Vector &x) const
}
nom0 = nom = Dot(d, r);
MFEM_ASSERT(IsFinite(nom), "nom = " << nom);
if (print_level == 1 || print_level == 3)
{
mfem::out << " Iteration : " << setw(3) << 0 << " (B r, r) = "
<< nom << (print_level == 3 ? " ...\n" : "\n");
}
if (nom < 0.0)
{
if (print_level >= 0)
{
mfem::out << "PCG: The preconditioner is not positive definite. (Br, r) = "
<< nom << '\n';
}
converged = 0;
final_iter = 0;
final_norm = nom;
return;
}
r0 = std::max(nom*rel_tol*rel_tol, abs_tol*abs_tol);
if (nom <= r0)
{
@@ -436,6 +615,17 @@ void CGSolver::Mult(const Vector &b, Vector &x) const
betanom = Dot(r, r);
}
MFEM_ASSERT(IsFinite(betanom), "betanom = " << betanom);
if (betanom < 0.0)
{
if (print_level >= 0)
{
mfem::out << "PCG: The preconditioner is not positive definite. (Br, r) = "
<< betanom << '\n';
}
converged = 0;
final_iter = i;
break;
}
if (print_level == 1)
{
+59
View File
@@ -115,6 +115,65 @@ private:
const Operator *oper;
};
/// Chebyshev accelerated smoothing with given vector, no matrix necessary
/** Potentially useful with tensorized operators, for example. This is just a
very basic Chebyshev iteration, if you want tolerances, iteration control,
etc. wrap this with SLISolver. */
class OperatorChebyshevSmoother : public Solver
{
public:
/** Application is by *inverse* of the given vector. It is assumed the
underlying operator acts as the identity on entries in ess_tdof_list,
corresponding to (assembled) DIAG_ONE policy or ConstrainedOperator in
the matrix-free setting. The estimated largest eigenvalue of the
diagonally preconditoned operator must be provided via
max_eig_estimate. */
OperatorChebyshevSmoother(Operator* oper_, const Vector &d,
const Array<int>& ess_tdof_list,
int order, double max_eig_estimate);
/** Application is by *inverse* of the given vector. It is assumed the
underlying operator acts as the identity on entries in ess_tdof_list,
corresponding to (assembled) DIAG_ONE policy or ConstrainedOperator in
the matrix-free setting. The largest eigenvalue of the diagonally
preconditoned operator is estimated internally via a power method. The
accuracy of the estimated eigenvalue may be controlled via
power_iterations and power_tolerance. */
#ifdef MFEM_USE_MPI
OperatorChebyshevSmoother(Operator* oper_, const Vector &d,
const Array<int>& ess_tdof_list,
int order, MPI_Comm comm = MPI_COMM_NULL, int power_iterations = 10,
double power_tolerance = 1e-8);
#else
OperatorChebyshevSmoother(Operator* oper_, const Vector &d,
const Array<int>& ess_tdof_list,
int order, int power_iterations = 10, double power_tolerance = 1e-8);
#endif
~OperatorChebyshevSmoother() {}
void Mult(const Vector&x, Vector &y) const;
void SetOperator(const Operator &op_)
{
oper = &op_;
}
void Setup();
private:
const int order;
double max_eig_estimate;
const int N;
Vector dinv;
const Vector &diag;
Array<double> coeffs;
const Array<int>& ess_tdof_list;
mutable Vector residual;
mutable Vector helperVector;
const Operator* oper;
};
/// Stationary linear iteration: x <- x + B (b - A x)
class SLISolver : public IterativeSolver
+10
View File
@@ -670,6 +670,16 @@ void Vector::Print(std::ostream &out, int width) const
out << '\n';
}
#ifdef MFEM_USE_ADIOS2
void Vector::Print(adios2stream &out,
const std::string& variable_name) const
{
if (!size) { return; }
data.Read(MemoryClass::HOST, size);
out.engine.Put(variable_name, &data[0] );
}
#endif
void Vector::Print_HYPRE(std::ostream &out) const
{
int i;
+10
View File
@@ -13,6 +13,9 @@
#define MFEM_VECTOR
#include "../general/array.hpp"
#ifdef MFEM_USE_ADIOS2
#include "../general/adios2stream.hpp"
#endif
#include "../general/globals.hpp"
#include "../general/mem_manager.hpp"
#include "../general/device.hpp"
@@ -298,6 +301,13 @@ public:
/// Prints vector to stream out.
void Print(std::ostream &out = mfem::out, int width = 8) const;
#ifdef MFEM_USE_ADIOS2
/// Prints vector to stream out.
/// @param out adios2stream output
/// @param variable_name variable name associated with current Vector
void Print(adios2stream & out, const std::string& variable_name) const;
#endif
/// Prints vector to stream out in HYPRE_Vector format.
void Print_HYPRE(std::ostream &out) const;
+4 -1
View File
@@ -117,7 +117,7 @@ EXAMPLE_SUBDIRS = sundials petsc pumi hiop ginkgo
EXAMPLE_DIRS := examples $(addprefix examples/,$(EXAMPLE_SUBDIRS))
EXAMPLE_TEST_DIRS := examples
MINIAPP_SUBDIRS = common electromagnetics meshing performance tools toys nurbs gslib
MINIAPP_SUBDIRS = common electromagnetics meshing navier performance tools toys nurbs gslib
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics tools toys)
@@ -392,6 +392,9 @@ endif
# Source dirs in logical order
DIRS = general linalg mesh fem fem/libceed
SOURCE_FILES = $(foreach dir,$(DIRS),$(wildcard $(SRC)$(dir)/*.cpp))
ADIOS2_FILES = $(SRC)general/adios2stream.h $(SRC)general/adios2stream.cpp \
$(SRC)fem/adios2datacollection.hpp $(SRC)fem/adios2datacollection.cpp
SOURCE_FILES := $(filter-out $(ADIOS2_FILES),$(SOURCE_FILES))
RELSRC_FILES = $(patsubst $(SRC)%,%,$(SOURCE_FILES))
OBJECT_FILES = $(patsubst $(SRC)%,$(BLD)%,$(SOURCE_FILES:.cpp=.o))
OKL_DIRS = fem
+1 -1
View File
@@ -75,7 +75,7 @@ public:
virtual const int *GetEdgeVertices(int) const = 0;
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const = 0;
MFEM_DEPRECATED virtual int GetNFaces(int &nFaceVertices) const = 0;
virtual int GetNFaces() const = 0;
+1 -1
View File
@@ -52,7 +52,7 @@ public:
{ return geom_t::Edges[ei]; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const
MFEM_DEPRECATED virtual int GetNFaces(int &nFaceVertices) const
{ nFaceVertices = 4; return 6; }
virtual int GetNFaces() const { return 6; }
+12 -15
View File
@@ -360,7 +360,6 @@ void Mesh::GetElementTransformation(int i, IsoparametricTransformation *ElTr)
}
ElTr->SetFE(Nodes->FESpace()->GetFE(i));
}
ElTr->FinalizeTransformation();
}
void Mesh::GetElementTransformation(int i, const Vector &nodes,
@@ -402,7 +401,6 @@ void Mesh::GetElementTransformation(int i, const Vector &nodes,
}
ElTr->SetFE(Nodes->FESpace()->GetFE(i));
}
ElTr->FinalizeTransformation();
}
ElementTransformation *Mesh::GetElementTransformation(int i)
@@ -470,7 +468,6 @@ void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
ElTr->SetFE(face_el);
}
}
ElTr->FinalizeTransformation();
}
void Mesh::GetFaceTransformation(int FaceNo, IsoparametricTransformation *FTr)
@@ -535,7 +532,6 @@ void Mesh::GetFaceTransformation(int FaceNo, IsoparametricTransformation *FTr)
FTr->SetFE(face_el);
}
}
FTr->FinalizeTransformation();
}
ElementTransformation *Mesh::GetFaceTransformation(int FaceNo)
@@ -597,7 +593,6 @@ void Mesh::GetEdgeTransformation(int EdgeNo, IsoparametricTransformation *EdTr)
MFEM_ABORT("Not implemented.");
}
}
EdTr->FinalizeTransformation();
}
ElementTransformation *Mesh::GetEdgeTransformation(int EdgeNo)
@@ -619,7 +614,6 @@ void Mesh::GetLocalPtToSegTransformation(
locpm(0, 0) = SegVert->IntPoint(i/64).x;
// (i/64) is the local face no. in the segment
// (i%64) is the orientation of the point (not used)
Transf.FinalizeTransformation();
}
void Mesh::GetLocalSegToTriTransformation(
@@ -639,7 +633,6 @@ void Mesh::GetLocalSegToTriTransformation(
locpm(0, so[j]) = TriVert->IntPoint(tv[j]).x;
locpm(1, so[j]) = TriVert->IntPoint(tv[j]).y;
}
Transf.FinalizeTransformation();
}
void Mesh::GetLocalSegToQuadTransformation(
@@ -659,7 +652,6 @@ void Mesh::GetLocalSegToQuadTransformation(
locpm(0, so[j]) = QuadVert->IntPoint(qv[j]).x;
locpm(1, so[j]) = QuadVert->IntPoint(qv[j]).y;
}
Transf.FinalizeTransformation();
}
void Mesh::GetLocalTriToTetTransformation(
@@ -683,7 +675,6 @@ void Mesh::GetLocalTriToTetTransformation(
locpm(1, j) = vert.y;
locpm(2, j) = vert.z;
}
Transf.FinalizeTransformation();
}
void Mesh::GetLocalTriToWdgTransformation(
@@ -709,7 +700,6 @@ void Mesh::GetLocalTriToWdgTransformation(
locpm(1, j) = vert.y;
locpm(2, j) = vert.z;
}
Transf.FinalizeTransformation();
}
void Mesh::GetLocalQuadToHexTransformation(
@@ -731,7 +721,6 @@ void Mesh::GetLocalQuadToHexTransformation(
locpm(1, j) = vert.y;
locpm(2, j) = vert.z;
}
Transf.FinalizeTransformation();
}
void Mesh::GetLocalQuadToWdgTransformation(
@@ -755,7 +744,6 @@ void Mesh::GetLocalQuadToWdgTransformation(
locpm(1, j) = vert.y;
locpm(2, j) = vert.z;
}
Transf.FinalizeTransformation();
}
const GeometricFactors* Mesh::GetGeometricFactors(const IntegrationRule& ir,
@@ -939,8 +927,7 @@ void Mesh::ApplyLocalSlaveTransformation(IsoparametricTransformation &transf,
#endif
MFEM_ASSERT(fi.NCFace >= 0, "");
transf.Transform(*nc_faces_info[fi.NCFace].PointMatrix, composition);
transf.GetPointMat() = composition;
transf.FinalizeTransformation();
transf.SetPointMat(composition);
}
FaceElementTransformations *Mesh::GetBdrFaceTransformations(int BdrElemNo)
@@ -4846,10 +4833,12 @@ void Mesh::GetPointMatrix(int i, DenseMatrix &pointmat) const
pointmat.SetSize(spaceDim, nv);
for (k = 0; k < spaceDim; k++)
{
for (j = 0; j < nv; j++)
{
pointmat(k, j) = vertices[v[j]](k);
}
}
}
void Mesh::GetBdrPointMatrix(int i,DenseMatrix &pointmat) const
@@ -8616,6 +8605,13 @@ void Mesh::PrintTopo(std::ostream &out,const Array<int> &e_to_k) const
out << "\nvertices\n" << NumOfVertices << '\n';
}
#ifdef MFEM_USE_ADIOS2
void Mesh::Print(adios2stream &out) const
{
out.Print(*this);
}
#endif
void Mesh::PrintVTK(std::ostream &out)
{
out <<
@@ -10428,6 +10424,7 @@ GeometricFactors::GeometricFactors(const Mesh *mesh, const IntegrationRule &ir,
const GridFunction *nodes = mesh->GetNodes();
const FiniteElementSpace *fespace = nodes->FESpace();
const FiniteElement *fe = fespace->GetFE(0);
const int dim = fe->GetDim();
const int vdim = fespace->GetVDim();
const int NE = fespace->GetNE();
const int ND = fe->GetDof();
@@ -10445,7 +10442,7 @@ GeometricFactors::GeometricFactors(const Mesh *mesh, const IntegrationRule &ir,
}
if (flags & GeometricFactors::JACOBIANS)
{
J.SetSize(vdim*vdim*NQ*NE);
J.SetSize(dim*vdim*NQ*NE);
eval_flags |= QuadratureInterpolator::DERIVATIVES;
}
if (flags & GeometricFactors::DETERMINANTS)
+11
View File
@@ -23,6 +23,9 @@
#include "../fem/eltrans.hpp"
#include "../fem/coefficient.hpp"
#include "../general/zstr.hpp"
#ifdef MFEM_USE_ADIOS2
#include "../general/adios2stream.hpp"
#endif
#include <iostream>
namespace mfem
@@ -55,6 +58,10 @@ class Mesh
friend class NCMesh;
friend class NURBSExtension;
#ifdef MFEM_USE_ADIOS2
friend class adios2stream;
#endif
protected:
int Dim;
int spaceDim;
@@ -1195,6 +1202,10 @@ public:
/// \see mfem::ofgzstream() for on-the-fly compression of ascii outputs
virtual void Print(std::ostream &out = mfem::out) const { Printer(out); }
/// Print the mesh to the given stream using the adios2 bp format
#ifdef MFEM_USE_ADIOS2
virtual void Print(adios2stream &out) const;
#endif
/// Print the mesh in VTK format (linear and quadratic meshes only).
/// \see mfem::ofgzstream() for on-the-fly compression of ascii outputs
void PrintVTK(std::ostream &out);
+7 -2
View File
@@ -1695,7 +1695,6 @@ void ParMesh::GetFaceNbrElementTransformation(
MFEM_ABORT("Nodes are not ParGridFunction!");
}
}
ElTr->FinalizeTransformation();
}
void ParMesh::DeleteFaceNbrData()
@@ -2355,7 +2354,6 @@ ElementTransformation* ParMesh::GetGhostFaceTransformation(
#endif
FaceTransformation.SetFE(face_el);
}
FaceTransformation.FinalizeTransformation();
return &FaceTransformation;
}
@@ -4223,6 +4221,13 @@ void ParMesh::Print(std::ostream &out) const
}
}
#ifdef MFEM_USE_ADIOS2
void ParMesh::Print(adios2stream &out) const
{
Mesh::Print(out);
}
#endif
static void dump_element(const Element* elem, Array<int> &data)
{
data.Append(elem->GetGeometryType());
+10
View File
@@ -309,6 +309,12 @@ public:
as boundary (for visualization purposes) using the mfem v1.0 format. */
virtual void Print(std::ostream &out = mfem::out) const;
#ifdef MFEM_USE_ADIOS2
/** Print the part of the mesh in the calling processor using adios2 bp
format. */
virtual void Print(adios2stream &out) const;
#endif
/** Print the part of the mesh in the calling processor adding the interface
as boundary (for visualization purposes) using Netgen/Truegrid format .*/
virtual void PrintXG(std::ostream &out = mfem::out) const;
@@ -348,6 +354,10 @@ public:
#ifdef MFEM_USE_PUMI
friend class ParPumiMesh;
#endif
#ifdef MFEM_USE_ADIOS2
friend class adios2stream;
#endif
};
}
+1 -1
View File
@@ -47,7 +47,7 @@ public:
virtual const int *GetEdgeVertices(int ei) const { return NULL; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const
MFEM_DEPRECATED virtual int GetNFaces(int &nFaceVertices) const
{ nFaceVertices = 0; return 0; }
virtual int GetNFaces() const { return 0; }
+1 -1
View File
@@ -54,7 +54,7 @@ public:
{ return geom_t::Edges[ei]; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const
MFEM_DEPRECATED virtual int GetNFaces(int &nFaceVertices) const
{ nFaceVertices = 0; return 0; }
virtual int GetNFaces() const { return 0; }
+1 -1
View File
@@ -53,7 +53,7 @@ public:
virtual const int *GetEdgeVertices(int ei) const { return NULL; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const
MFEM_DEPRECATED virtual int GetNFaces(int &nFaceVertices) const
{ nFaceVertices = 0; return 0; }
virtual int GetNFaces() const { return 0; }
+1 -1
View File
@@ -101,7 +101,7 @@ public:
{ return geom_t::Edges[ei]; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const
MFEM_DEPRECATED virtual int GetNFaces(int &nFaceVertices) const
{ nFaceVertices = 3; return 4; }
virtual int GetNFaces() const { return 4; }
+1 -1
View File
@@ -81,7 +81,7 @@ public:
{ return geom_t::Edges[ei]; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const
MFEM_DEPRECATED virtual int GetNFaces(int &nFaceVertices) const
{ nFaceVertices = 0; return 0; }
virtual int GetNFaces() const { return 0; }
+2 -1
View File
@@ -13,6 +13,7 @@
#define MFEM_VERTEX
#include "../config/config.hpp"
#include "../general/globals.hpp"
namespace mfem
{
@@ -44,7 +45,7 @@ public:
/// (DEPRECATED) Set the coordinates of the Vertex.
/** @deprecated This old version of SetCoords is not always memory safe. */
void SetCoords(const double *p)
MFEM_DEPRECATED void SetCoords(const double *p)
{ coord[0] = p[0]; coord[1] = p[1]; coord[2] = p[2]; }
/// Sets vertex location based on given point p
+1 -1
View File
@@ -55,7 +55,7 @@ public:
{ return geom_t::Edges[ei]; }
/// @deprecated Use GetNFaces(void) and GetNFaceVertices(int) instead.
virtual int GetNFaces(int &nFaceVertices) const;
MFEM_DEPRECATED virtual int GetNFaces(int &nFaceVertices) const;
virtual int GetNFaces() const { return 5; }
+3
View File
@@ -24,6 +24,9 @@
#include "general/stable3d.hpp"
#include "general/table.hpp"
#include "general/tic_toc.hpp"
#ifdef MFEM_USE_ADIOS2
#include "general/adios2stream.hpp"
#endif
#include "general/isockstream.hpp"
#include "general/osockstream.hpp"
#include "general/socketstream.hpp"

Some files were not shown because too many files have changed in this diff Show More