Compare commits

...
Author SHA1 Message Date
Dylan Copeland 199560d0fe Small bug fix 2021-12-04 19:40:25 -08:00
Dylan Copeland ff43a53599 Fixing DiffusionIntegrator::ComputeFluxEnergy in the case with_coef = true. 2021-12-03 18:32:16 -08:00
Tzanio Kolev 539f663fe6 Merge pull request #2570 from mdeuse/complex_fem-improvement
Added AddDomainIntegrator with marker for (Par)ComplexLinearForm and (Par)SesquilinearForm
2021-11-30 14:21:05 -08:00
mdeuse 104abd56e1 Merge branch 'master' into complex_fem-improvement 2021-11-30 21:25:29 +01:00
Will Pazner 68b4445893 Merge remote-tracking branch 'origin/master' into complex_fem-improvement 2021-11-30 11:04:45 -08:00
Tzanio Kolev de9110300d Merge pull request #2383 from mfem/pr_autodiff
AD capabilities in mfem - autodiff
2021-11-30 09:59:27 -08:00
Will Pazner cbff87a616 Merge pull request #2639 from mfem/hyperparvec-move-dev
Add move constructors/assignment for Vector and HypreParVector [hypreparvec-move-dev]
2021-11-30 09:48:10 -08:00
blaz 139ce00846 new .gitignore 2021-11-24 22:11:45 -08:00
blaz 465459bc94 new .gitignore 2021-11-24 21:54:58 -08:00
blaz 0487599b56 .gitignore 2021-11-24 21:38:05 -08:00
blaz a85687d0cb move caliper configuration 2021-11-24 21:14:07 -08:00
blaz 03b9eb4b53 style 2021-11-24 17:45:20 -08:00
blaz 293acb99d9 small amendments 2021-11-24 17:32:44 -08:00
Josh Essman 8c5e3c4e08 Merge branch 'master' into hyperparvec-move-dev 2021-11-22 10:25:05 -06:00
Josh Essman 620b2590a3 tests: make sure that moved-from hpvec is empty 2021-11-22 10:24:10 -06:00
Tzanio 247818aaa6 More styling 2021-11-14 12:36:43 -08:00
Tzanio e5e4a7e753 Merge branch 'master' into pr_autodiff 2021-11-14 12:08:07 -08:00
Tzanio 20cb878f5f Styling 2021-11-14 12:07:15 -08:00
Tzanio 4cc03b4238 Small adjustments 2021-11-14 11:54:02 -08:00
Tzanio Kolev 0843a87d79 Merge pull request #2661 from mfem/gitlab-disable-rebaseline
In Gitlab CI, disable the `rebaseline` job for `master`
2021-11-12 08:11:57 -08:00
Tzanio Kolev 9c34c32ae6 Merge pull request #2652 from mfem/hypre-update
Update with latest changes in hypre
2021-11-12 08:10:42 -08:00
Tzanio Kolev 9a12cedd5e Merge pull request #2358 from mfem/yohann/mem/min-default-constr
Use `Reset()` in default constructor of `Memory<T>`.
2021-11-11 16:21:12 -08:00
Tzanio Kolev f9f3f11818 Merge pull request #2638 from mfem/diffusion-kernel-dev
More threads/block for SmemPADiffusionApply3D
2021-11-11 16:20:44 -08:00
Tzanio Kolev b2df200f3d Merge pull request #2659 from mfem/deps-flags
Allow for customizing compiler flags for generating dependencies
2021-11-11 15:39:52 -08:00
Veselin Dobrev 6d102971c9 In Gitlab CI, disable the rebaseline job for 'master' 2021-11-11 14:28:49 -08:00
Victor A. P. Magri ba1e213c19 Add check on hypre version 2021-11-11 11:05:03 -08:00
Will Pazner acbe45ea91 Allow for customizing compiler flags for generating dependencies
On Mac, by default, add the flag -Wno-unused-command-line-argument
2021-11-10 14:34:52 -08:00
Tzanio 0cc104331c Merge branch 'master' into yohann/mem/min-default-constr 2021-11-10 12:52:15 -08:00
Tzanio Kolev d7e87fda54 Merge pull request #2366 from xjrc/ciurej/feature/conduit-data-collection-adjsets
extend `ConduitDataCollection` to support `adjsets` exporting
2021-11-10 11:21:12 -08:00
Josh Essman bd1dbd3754 refactor: add CreateCompatibleVector method to HypreParVector 2021-11-09 15:45:39 -06:00
Tzanio Kolev d6924d1b3f Merge pull request #2649 from mfem/gh-actions-fix
Small fix in Github actions
2021-11-08 20:11:38 -08:00
Victor A. P. Magri 7a6758a53b Update prepocessor macros 2021-11-08 15:27:06 -08:00
Josh Essman 143b0378e5 cleanup: remove redundant code 2021-11-08 17:11:01 -06:00
Victor A. P. Magri 39b2f27dc2 Merge branch 'master' into hypre-update 2021-11-08 14:50:15 -08:00
Tzanio f9e9f5b22e minor 2021-11-08 10:36:31 -08:00
Cyrus Harrison 882883a650 Update CHANGELOG 2021-11-08 10:35:58 -08:00
Tzanio Kolev 2519a91d01 Merge pull request #2645 from mfem/omp-macos
Include `omp` header on macOS [omp-macos]
2021-11-08 07:39:10 -08:00
Josh Essman 12cc340afb cleanup: apply suggestions from @pazner 2021-11-08 08:08:30 -06:00
Veselin Dobrev 88a190e034 In Github CI, remove testing change 'v2.0-tweak' -> 'v2.0' 2021-11-06 18:51:36 -07:00
Veselin Dobrev 9f64008a55 Fix the 'gitignore' github action.
Test with the external github action (remove before merge):
   mfem/github-actions/build-mfem@v2.0-tweak
2021-11-06 17:30:26 -07:00
Tzanio Kolev 7127265fde Merge pull request #2634 from mfem/gitlab-ci-updates
Gitlab CI updates
2021-11-06 15:57:22 -07:00
mdeuse f6f702c522 Minor, updated comment 2021-11-06 14:48:56 +01:00
Veselin Dobrev 29fc4c45b6 In Gitlab CI, apply another set of tweaks 2021-11-05 12:16:09 -07:00
Veselin Dobrev f6f802e0cd In Gitlab CI, test another tweak 2021-11-05 11:26:22 -07:00
Tobias Duswald 0ccc63af04 Include omp headers on macOS 2021-11-05 08:43:30 +01:00
Veselin Dobrev 2ec5efc7d5 In Gitlab CI, in sub-pipeline definitions, explicitly pass
some global variables
2021-11-04 20:37:10 -07:00
Veselin Dobrev c8a8ab5cba In Gitlab CI, attempt to explicitly inherit global variables
in the sub-pipelines
2021-11-04 20:21:35 -07:00
Veselin Dobrev aeac01c130 In Gitlab CI, add some debug "echo" commands 2021-11-04 19:15:29 -07:00
Veselin Dobrev a25c9c575b In Gitlab CI, move the definitions of some global variables from
.gitlab/configs/common.yml to .gitlab-ci.yml
2021-11-04 18:48:38 -07:00
Will Pazner 2900a6ecd0 Remove testing data_dir symlink if it exists 2021-11-04 16:25:17 -07:00
Will Pazner 134780da77 Try to fix hipcc errors in shifted miniapp 2021-11-04 12:37:05 -07:00
Josh Essman c85f81f9b4 fix: update hypreparvec copy ctor to copy local data 2021-11-04 12:22:25 -05:00
Josh Essman 72331717c7 tests: add tests for new functionality and fix other hypreparvec test 2021-11-04 10:15:23 -05:00
Josh Essman e96e88d543 feat: add vector move ctor/assignment 2021-11-04 09:01:28 -05:00
Veselin Dobrev a0172dfeb3 In Gitlab CI, remove some debug output 2021-11-03 21:42:47 -07:00
Tom Stitt 2efaaaaffa remove added blank lines 2021-11-03 17:07:12 -07:00
Tom Stitt 70422ccf14 switch threading order 2021-11-03 17:04:57 -07:00
Tom Stitt 716df18263 Merge remote-tracking branch 'origin/master' into diffusion-kernel-dev 2021-11-03 16:23:50 -07:00
Josh Essman 2337137f33 feat: add move constructor for hypreparvec 2021-11-03 17:31:19 -05:00
Tzanio Kolev 167ff72edc Merge pull request #2406 from SCOREC/installExamples
cmake: install examples when enabled
2021-11-03 08:02:37 -07:00
Veselin Dobrev 3f036b943e Add a new Gitlab CI file forgotten in the previous commit 2021-11-03 02:32:28 -07:00
Veselin Dobrev 5565b5066f Update Gitlab CI to fix some issues:
- Use a single (per user) clone of the internal MFEM/autotests repo
- Use a single (per user) clone of the Github MFEM/data repo
- Properly set the location for the pipeline-common temporary directory,
  BUILD_ROOT; add a cleanup step for BUILD_ROOT
- Various other small tweaks and additions
2021-11-03 02:15:58 -07:00
blaz 269eb6b7d1 fix in the makefile configuration 2021-11-02 17:51:24 -07:00
Tom Stitt 2558593206 threads in z; seems like ~10% faster on v100... 2021-11-02 17:19:22 -07:00
Tzanio Kolev d09ab725b5 Merge pull request #2367 from mfem/yohann/mem/delete-reset
Make `Memory<T>::Delete()` reset the `Memory<T>` object.
2021-11-02 06:45:51 -07:00
Tzanio Kolev 41bb7c5d4e Merge pull request #2423 from mfem/additional-device-kernels
Additional Device Kernels
2021-11-02 06:44:46 -07:00
Tzanio Kolev a0166719e6 Merge pull request #2607 from mfem/size-mt-array-ctor
Add Size & MemoryType Array Constructor
2021-11-02 06:44:01 -07:00
Tzanio Kolev 26200dafed Merge pull request #2611 from mfem/hypre-par-vector-read
Add HypreParVector::Read
2021-11-02 06:43:24 -07:00
Tzanio b8b2d23087 Merge branch 'master' into ciurej/feature/conduit-data-collection-adjsets 2021-10-31 14:44:55 -07:00
Tzanio Kolev ef656f03e9 Merge pull request #2507 from mfem/coarse-fine-map-fix-dev
Proposed fix for GetCoarseFineMap() for Derefinement in parallel
2021-10-31 14:40:38 -07:00
Tzanio Kolev c1c98ca52c Merge pull request #2448 from mfem/shifted-dirichletplusneumann
Shifted boundary method - Neumann boundary condition + multiple level sets
2021-10-31 14:40:00 -07:00
Tzanio 405a6d2f0d Merge branch 'master' into yohann/mem/delete-reset 2021-10-29 18:16:09 -07:00
Veselin Dobrev ba73a40ce9 Merge pull request #2598 from mfem/ceed-int-rule-fix
Fix bug in CEED integration
2021-10-29 16:04:36 -07:00
Ketan Mittal fa2e6fa2f4 minor 2021-10-29 13:58:29 -07:00
blaz a42ee48079 interface modifications and fixes 2021-10-29 12:13:33 -07:00
blaz bfafa6c97e fixed kapa -> kappa 2021-10-29 11:24:44 -07:00
blaz eeb0ede002 Merge branch 'master' into pr_autodiff 2021-10-29 11:21:47 -07:00
blaz ef5cc5aa5d Merge branch 'pr_autodiff' of https://github.com/mfem/mfem into pr_autodiff 2021-10-29 11:11:07 -07:00
Ketan Mittal b8590d7c0f set level set if none is specified 2021-10-29 09:02:46 -07:00
Tzanio Kolev bf616ac6ec Merge pull request #2493 from mfem/vqfluxfix
Bug fix for DiffusionIntegrator
2021-10-29 08:17:55 -07:00
Tzanio Kolev 0526a67b92 Merge pull request #2612 from mfem/yohann/wrapwrap
Improved device compilation.
2021-10-29 08:05:25 -07:00
Tzanio Kolev 59a950e1c6 Merge pull request #2616 from mfem/additional-template-instances
additional template instances
2021-10-29 07:57:33 -07:00
Ketan Mittal 26fdb711bc remove default dirichlet level set 2021-10-27 16:45:13 -07:00
Joseph Ciurej 8d76e7462a fixed the wording in a code comment 2021-10-27 13:40:09 -07:00
mdeuse 92710559c0 Merge branch 'master' of https://github.com/mfem/mfem into complex_fem-improvement 2021-10-27 22:28:49 +02:00
Tzanio Kolev 17537c506f Merge pull request #1915 from mfem/tmop-align-surface
Initial surface alignment TMOP capability
2021-10-27 09:05:00 -07:00
Will Pazner d9eec2acd8 Merge pull request #2619 from mfem/PrintMatlab
Fixed function signature which creates PyMFEM conflict
2021-10-25 16:09:53 -07:00
Tzanio 7d5bf6e3e1 square01_tri.mesh -> square01-tri.mesh; other minor 2021-10-25 10:17:05 -07:00
Tzanio Kolev 4d6c90d4fc Merge pull request #2511 from mfem/contrib-doc-mod
Proposed changes to documentation requirements [contrib-doc-mod]
2021-10-24 20:04:32 -07:00
Tzanio Kolev efb97380e3 Merge pull request #2510 from mfem/nonlinearform-doftrans-dev
Adding DofTransformations NonlinearForm methods [nonlinearform-doftrans-dev]
2021-10-24 19:58:54 -07:00
Tzanio Kolev 1f7e9f0f23 Merge pull request #2433 from mfem/oscillation
Data oscillation refinement / mesh pre-processing
2021-10-24 19:57:12 -07:00
Tzanio Kolev ca612b872f Update ex30p.cpp 2021-10-24 19:52:46 -07:00
Tzanio Kolev f991e44f2e Update ex30.cpp 2021-10-24 19:51:52 -07:00
Tzanio Kolev 3b232cd0c1 Update CHANGELOG 2021-10-24 19:50:19 -07:00
Joseph Ciurej cecda6b411 fixed a few bugs w/ neighbor/rank distinction between MFEM and Blueprint 2021-10-24 11:52:17 -07:00
Joseph Ciurej 8145b4c377 removed invalid boundary topology and added a note about its exclusion 2021-10-21 12:29:36 -07:00
Joseph Ciurej 2c1a39031e extend 'ConduitDataCollection' adjacency information to include shared boundaries 2021-10-21 12:29:36 -07:00
Joseph Ciurej ded707375a add basic support to 'ConduitDataCollection' for outputting adjacency information 2021-10-21 12:29:35 -07:00
Keith be505c9af0 fixed function signature which creates PyMFEM conflict 2021-10-20 17:00:50 -07:00
Tom Stitt 4056c529a1 additional template instances 2021-10-19 15:06:15 -07:00
Tzanio Kolev 23f110f9b7 Adding Mark and Will as MFEM editors 2021-10-19 12:13:59 -07:00
Keith 1ed1a29e68 PR checklist 2021-10-18 12:07:22 -07:00
Tzanio Kolev f428d03583 Merge pull request #2609 from mfem/changelog-fix
Fix CHANGELOG entries
2021-10-17 09:01:56 -07:00
Yohann Dudouit ddf40b7909 Use static method. 2021-10-15 16:34:54 -07:00
Yohann Dudouit 21417add4b Dim -> DIM. 2021-10-15 16:32:35 -07:00
Yohann Dudouit 5f68f31a73 Wrap exec policies to avoid triple compilation. 2021-10-15 16:30:54 -07:00
Will Pazner 407a3321aa Add unit test for HypreParVector::Read 2021-10-15 13:25:23 -07:00
Will Pazner c76cbfa010 Add HypreParVector::Read 2021-10-15 13:03:13 -07:00
Veselin Dobrev 8a67918a24 Merge pull request #2577 from mfem/artv3/bugfix/hypre-amgx-host-device
Host/Device fix for Hypre-AMGX solver.
2021-10-14 17:55:00 -07:00
Veselin Dobrev 7f0b52ccc0 Merge pull request #2556 from mfem/par-get-deriv
Parallel implementation of GridFunction::GetDerivative()
2021-10-14 16:33:09 -07:00
Veselin Dobrev 6298a3ce47 Merge pull request #2508 from mfem/bugfix/lin-penta-dev
Fixing special cases in RT pyramids [bugfix/lin-penta-dev]
2021-10-14 16:31:25 -07:00
Veselin Dobrev 000d018d1a Merge pull request #2471 from mfem/coef-set-time-dev
Propagating Coefficient::time [coef-set-time-dev]
2021-10-14 16:27:18 -07:00
Veselin Dobrev b938ea0286 Merge pull request #2579 from mfem/br2-fix
Fix for BR2 integrator [br2-fix]
2021-10-14 16:25:03 -07:00
Veselin Dobrev b773b012c7 Merge pull request #2376 from mfem/lor-solvers-p-ref
LOR solvers: device and p-refinement [lor-solvers-p-ref]
2021-10-14 16:13:28 -07:00
Veselin Dobrev f1e503e70f Move or remove CHANGELOG entries in the v4.3 section which
were added/changed after v4.3.

Update the RAJA version requirement in INSTALL.
2021-10-14 15:59:58 -07:00
Tom Stitt 320bf0a980 add new ctor to array that takes a size and a memorytype 2021-10-14 10:48:43 -07:00
Stowell, Mark L 47008f92fb Adding statement about documenting pointer arguments/return values 2021-10-12 11:40:30 -07:00
Will Pazner d72cd3f5f7 Small modifications to CEED convection unit test 2021-10-09 09:32:23 -06:00
Julian AndrejandYohann 8bb634ffcb Update tests/unit/ceed/test_ceed.cpp
Co-authored-by: Yohann <dudouit1@llnl.gov>
2021-10-08 13:44:58 -07:00
Vladimir Z Tomov af6426d240 Addressed reviewer comments. 2021-10-08 13:09:17 -07:00
Julian Andrej 15b9085ad5 add unit test 2021-10-08 11:39:33 -07:00
Will Pazner cbc44b22b6 Fix bug in CEED integration
The requested integration rule was assumed to be GaussLegendre, but this is not
always the case.
2021-10-08 11:07:52 -07:00
Victor A. P. Magri 28369b9be2 Update BoomerAMGCoarseParms call 2021-10-07 13:54:25 -07:00
Will Pazner 31545fb3d7 Comments and fix for BR2 integrator.
Use SetAllIntPoints for the face transformation, re-add factor of 0.5
corresponding to average term in lifting operator. Also revert ex14p sample
run.
2021-10-06 13:25:13 -07:00
Will Pazner c0486fec51 Take average of coefficient across face in BR2 integrator 2021-10-05 10:25:07 -07:00
Will Pazner 5df3ef4b9e Pass fespace by reference to BR2 integrator in ex14 and ex14p 2021-10-04 16:56:43 -07:00
Will Pazner d9b1f2409c Merge remote-tracking branch 'origin/master' into br2-fix 2021-10-04 15:50:55 -07:00
Will Pazner 1441a6e636 Merge remote-tracking branch 'origin/master' into lor-solvers-p-ref 2021-10-04 15:50:34 -07:00
Will Pazner 2fe688deea Add version of BR2 integrator with coefficient 2021-10-04 15:45:55 -07:00
Will Pazner c7856f70ea Fix possible dereferencing of NULL pointer 2021-10-04 15:34:28 -07:00
Will Pazner ee8578e22a Take sqrt of BR2 factor 2021-09-30 17:37:44 -07:00
Will Pazner 2888c61e59 Increase maximum iterations in ex14p. 2021-09-30 08:28:07 -07:00
Tzanio 8f59bf3005 minor 2021-09-29 20:55:20 -07:00
Will Pazner 037855173e Use smaller problem for ex14 sample run 2021-09-29 15:32:13 -07:00
Keith d6a5eea4e7 const member function fix 2021-09-29 14:37:32 -07:00
Vladimir Z Tomov 092b5079fd Used HostReadWrite as suggested. 2021-09-29 13:12:08 -07:00
Keith 3b325a597c supress warnings by default 2021-09-29 13:02:24 -07:00
Vladimir Z Tomov 8880e17cd5 Unit test. 2021-09-29 12:48:39 -07:00
Vladimir Z Tomov 22a0fa48d3 Merge branch 'master' into par-get-deriv 2021-09-29 11:44:33 -07:00
Stowell, Mark L 98be69f851 Adding MatrixFunctionCoefficient::SetTime thanks to @vladotomov 2021-09-29 09:53:11 -07:00
Arturo Vargas 3f78a06411 add hypre read call 2021-09-29 09:44:32 -07:00
Stowell, Mark L d40305077c Merge remote-tracking branch 'origin/master' into bugfix/lin-penta-dev
# Conflicts:
#	fem/fe.cpp
2021-09-29 09:42:17 -07:00
Vladimir Z Tomov 1e5602f139 Removed the mesh curvature output, as it doesn't take
into account the user-selected mesh order.
2021-09-28 22:41:50 -07:00
Vladimir Z Tomov bb085f9866 Aborts for unsupported setups, mem leak. 2021-09-28 22:24:40 -07:00
Vladimir Z Tomov e7a7bfe3fb Removed unused function.
Better computation and output of fitting errors.
2021-09-28 18:11:45 -07:00
Vladimir Z Tomov 76617db79e Serial miniapp and some comments. 2021-09-28 17:24:23 -07:00
Tzanio f491fe30c3 Merge branch 'master' into lor-solvers-p-ref 2021-09-28 16:31:10 -07:00
Vladimir Z Tomov a1ba9f93c4 Reverted changes in pmesh.cpp. 2021-09-28 16:09:21 -07:00
Vladimir Z Tomov 211a221c50 Fixed a merge issue. 2021-09-28 16:00:10 -07:00
Vladimir Z Tomov a194cdacce Merge branch 'master' into tmop-align-surface 2021-09-28 15:54:35 -07:00
Vladimir Z Tomov 3829693441 Better triangular mesh, added parallel sample runs. 2021-09-28 15:35:26 -07:00
Tzanio Kolev 1e91b0bc06 Merge branch 'master' into coef-set-time-dev 2021-09-28 14:04:42 -07:00
Will Pazner e424d758be Remove factor of 0.5 in BR2 integrator 2021-09-28 12:40:48 -07:00
Arturo Vargas fc070abf6b make style 2021-09-28 11:35:01 -07:00
Arturo Vargas 7bac717364 Host/Device fix for Hypre-AMGX solver. 2021-09-28 10:58:28 -07:00
Keith 5231ec314a Socratis's comments 2021-09-27 15:01:01 -07:00
Keith a33f29549a Merge branch 'master' into oscillation 2021-09-27 15:00:11 -07:00
Stowell, Mark L 0e48fd5f94 Fixing a small typo 2021-09-27 09:29:20 -07:00
Tzanio 946ab2a893 Incorporate suggestion from Denis 2021-09-26 19:02:31 -07:00
Tzanio b34831a308 Incorporate Aaron suggestions 2021-09-26 18:48:00 -07:00
Tzanio c753e3bf8e Incorporater PR review rules in CONTRIBUTING.md 2021-09-26 18:35:02 -07:00
Tzanio f25aa615da Merge branch 'master' into contrib-doc-mod 2021-09-26 18:34:52 -07:00
Tzanio Kolev a303bcdf0a Merge branch 'master' into pr_autodiff 2021-09-26 17:28:25 -07:00
Tzanio f7b7c5388b make style 2021-09-26 17:26:44 -07:00
Tzanio b704fa15b1 Merge branch 'master' into additional-device-kernels 2021-09-26 17:25:53 -07:00
mdeuse e533a91560 Added AddDomainIntegrator with marker for (Par)ComplexLinearForm and (Par)SesquilinearForm 2021-09-26 22:14:01 +02:00
Tom Stitt b74049fff1 MultT -> MultTranspose 2021-09-24 14:46:12 -07:00
Tom StittandYohann 7e9dd26f57 fix empty mat check
Co-authored-by: Yohann <dudouit1@llnl.gov>
2021-09-24 14:45:09 -07:00
Vladimir Z Tomov 621a7842b1 AMR - sample run for the distance app, abort for the diffusion app. 2021-09-24 14:43:56 -07:00
Vladimir Z Tomov 7cf96edc2f Makefile improvements to avoid extra recompilation. 2021-09-24 14:23:22 -07:00
Vladimir Z Tomov e04fc27673 Moved the reproducer to tmp.cpp and reverted ex1p.cpp. 2021-09-23 13:27:31 -07:00
Vladimir Z Tomov c216a86943 Minor edits. 2021-09-21 18:06:44 -07:00
Vladimir Z Tomov 3562662d93 Revert ex1p. 2021-09-20 14:50:17 -07:00
Vladimir Z Tomov a13a1bf95c Merge branch 'par-get-deriv' into shifted-dirichletplusneumann 2021-09-20 14:48:32 -07:00
Vladimir Z Tomov 654663a221 Parallel implementation of GridFunction::GetDerivative(). 2021-09-20 14:22:48 -07:00
Jakub Červený 6708f9c19b Fix Doxygen error 2021-09-17 14:17:32 +02:00
Jakub Červený 4f0c54ff06 Updated derefinement test in test_derefine.cpp 2021-09-17 14:12:23 +02:00
Jakub Červený a53706b57c Simplified CoarseFineTransformations::MakeCoarseToFineTable. 2021-09-17 13:53:45 +02:00
Jakub Červený 61e9c368d7 DerefinementOperator: uses original GetCoarseToFineMap, now in fespace.cpp. 2021-09-17 12:52:20 +02:00
Jakub Červený a747244a6f WIP refactoring DerefinementOperator constructor 2021-09-14 13:08:58 +02:00
Jakub Červený 9b05700cb5 Initialization of Embedding::geom and ::ghost in NCMesh and Mesh. 2021-09-14 11:23:49 +02:00
Vladimir Z Tomov 27dfa26f0d Improved the marking algorithm. 2021-09-13 14:12:21 -07:00
Jakub Červený 83c11c4ea5 Refactoring coarse/fine: added MakeCoarseToFineTable, Embedding::geom/ghost. 2021-09-13 16:01:36 +02:00
Stowell, Mark L 09b5b1f184 Mention of new function usage 2021-09-09 14:48:58 -07:00
Stowell, Mark L 7b64d8739a Adjusting statements based on input obtained during MFEM developer meeting 2021-09-09 14:29:17 -07:00
Vladimir Z Tomov 36e2e4d37f Some additional tests. 2021-09-08 14:38:39 -07:00
Ketan Mittal 44d71cfdbf Merge branch 'master' of https://github.com/mfem/mfem into shifted-dirichletplusneumann 2021-09-08 13:58:37 -07:00
Ketan Mittal 4806441a03 make sure normal vector is pointing outside the domain 2021-09-08 13:58:20 -07:00
Tucker Babcock 5ad64b058b fix compile error, changed ComputeGradientBlocked doftrans usage to be similar to MixedBilinearForm::Assemble. 2021-09-07 14:35:00 -06:00
Tucker Babcock dd5f50bb40 store DofTransformations in Array for BlockNonlinearForm::MultBlocked and ::ComputeGradientBlocked since they need to be reused 2021-09-07 14:24:33 -06:00
Tucker Babcock 3a8908e6bf add DofTransformation to BlockNonlinearForm methods 2021-09-07 14:16:08 -06:00
Stowell, Mark L f53e14016d Separate statements for public and private entities. 2021-09-03 19:20:07 -07:00
Stowell, Mark L 70bb02a0c7 Proposed changes to documentation requirements 2021-09-03 17:37:03 -07:00
Tucker Babcock 091ff0c475 adding DofTransformation objects to NonlinearForm Mult, GetGridFunctionEnergy, and GetGradient methods similar to those used in BilinearForm::Assemble and LinearForm::Assemble 2021-09-03 18:13:19 -06:00
Robert W. Anderson 583024eaa7 keep track of # of nonghost elements in derefinement, use this to fix GetCoarseFineMap in the case of derefinement 2021-09-03 17:12:18 -07:00
Stowell, Mark L 1d5df46cdb Adding rescaling to special case at apex of RT pyramid 2021-09-03 15:01:25 -07:00
Stowell, Mark L 68fabe1cfa Adding GetVectorFieldValues check to GetVectorValue unit test 2021-09-03 15:00:55 -07:00
Dylan Copeland 3c82571b68 Small comment. 2021-09-03 09:55:32 -07:00
Dylan Copeland 3388160761 Fixing some unsupported coefficients in ComputeFluxEnergy. 2021-09-02 21:25:49 -07:00
Keith c8f6d1ad4f Socratis's comments 2021-09-02 12:40:36 -07:00
blaz 7b9d5d0084 Merge branch 'master' into pr_autodiff 2021-09-02 12:40:08 -07:00
blaz d41b7f7e83 Documentation 2021-09-02 12:37:18 -07:00
Vladimir Z Tomov 1459c4f5a6 Minor. 2021-09-01 18:14:25 -07:00
Will Pazner 1fce1b6306 Add device configuration to plor_solvers 2021-08-31 14:44:20 -07:00
Keith 6b33ad4abf Jakub's changes 2021-08-31 12:58:44 -07:00
Keith 5f6fda1bf3 Jakub's changes 2021-08-31 11:55:22 -07:00
Vladimir Z Tomov d0831c2f11 Minor edits in the miniapp. 2021-08-31 10:46:21 -07:00
Jakub Červený cfd1442180 Minor whitespace and typos. 2021-08-31 16:55:27 +02:00
Tzanio Kolev 0f7529465b Merge branch 'master' into coef-set-time-dev 2021-08-29 18:46:58 -07:00
Dylan Copeland 08715a83cf Fixing with_coef logic in flux calculation. 2021-08-28 12:54:47 -07:00
Dylan Copeland 4aaa441d70 Fixing a bug in DiffusionIntegrator in the case of a vector (diagonal matrix) coefficient. 2021-08-27 15:40:21 -07:00
Vladimir Z Tomov bba6e27f85 Minor edits. 2021-08-27 14:57:23 -07:00
Keith 4d48ddb566 fix unit tests after constructor redefinition 2021-08-26 13:53:47 -07:00
Keith 6bdd079b40 fix style 2021-08-26 13:37:07 -07:00
Keith 71c4967e80 added Coefficient & as argument in constructor 2021-08-26 13:34:08 -07:00
Keith 165f4301d8 make gf local 2021-08-26 13:09:40 -07:00
Keith 7d309b3d9e initialize gf 2021-08-26 12:30:58 -07:00
Keith 7246251b52 const Vector & GetLocalOscs() const 2021-08-26 12:24:34 -07:00
Keith 4d40b7536f make GetOsc at const member function 2021-08-26 11:47:40 -07:00
Keith eb195d1cb3 Addressing comments: Change default value 2021-08-26 09:30:52 -07:00
Brendan Keith ebddfa7114 Addressing comments: Added period for conformity. 2021-08-26 09:12:50 -07:00
Brendan Keith a5be9f36ed Addressing comments: Added period for conformity. 2021-08-26 09:09:47 -07:00
blaz 1c4602cf64 More documentation 2021-08-25 09:34:03 -07:00
blaz 2fd06ce867 documentation 2021-08-24 23:48:26 -07:00
blaz 435d542208 All functors are created as private objects 2021-08-24 20:32:06 -07:00
blaz bd155a672e FDual documentation 2021-08-24 18:07:18 -07:00
blaz afacaa7cad Documentation 2021-08-24 17:36:17 -07:00
blaz f9811d774c FDual name changed for FDualNumber 2021-08-24 16:58:25 -07:00
blaz 3e0b76a048 Changed names for the vector and the matrix classes 2021-08-24 16:53:16 -07:00
Stowell, Mark L 9dee99fbf1 Moving CHANGELOG entry (Oops) 2021-08-19 12:45:30 -07:00
Will Pazner ca01ef44bf Merge branch 'ads-cuda-use-jacobi' into lor-solvers-p-ref 2021-08-18 17:28:48 -07:00
Stowell, Mark L 4d4cf27264 Adding CHANGELOG entry 2021-08-17 10:45:37 -07:00
Stowell, Mark L 246f9bcec1 Making *Coefficient::SetTime virtual 2021-08-17 09:51:47 -07:00
blaz 59151dbe58 documentation and small changes 2021-08-13 20:13:40 -07:00
blaz 002514c2e0 Merge branch 'master' into pr_autodiff 2021-08-13 16:20:11 -07:00
Ketan Mittal b133ec754e minor 2021-08-06 14:31:48 -07:00
blaz 23cd7f75bb changed back miniapp CMakefile 2021-08-06 11:12:32 -07:00
blaz 09b67ab18d Merge branch 'master' into pr_autodiff 2021-08-06 11:01:22 -07:00
blaz 6327127cdc cosmetics 2021-08-06 10:56:23 -07:00
Ketan Mittal d4c102b94c enable high-order terms for Neumann 2021-08-05 17:02:11 -07:00
Ketan Mittal bbba4d863f minor fix for inhomogeneous Neumann 2021-08-04 10:05:49 -07:00
Ketan Mittal 9bf2e813b0 make style 2021-08-03 14:13:58 -07:00
Ketan Mittal 838ac0cf36 improved documentation and other misc changes 2021-08-03 14:13:34 -07:00
Keith 7ec56ca61f FIX: Removed too much in last commit 2021-08-03 09:17:55 -07:00
Keith 1cd7bfb28d remove unused variables 2021-08-03 09:12:50 -07:00
Keith 6a63b46bd4 typo in comment 2021-08-02 23:34:33 -07:00
Ketan Mittal 81aacd2c9e documentation 2021-08-02 15:28:13 -07:00
Ketan Mittal 2077b076c8 Merge branch 'master' of https://github.com/mfem/mfem into shifted-dirichletplusneumann 2021-08-02 15:08:35 -07:00
Ketan Mittal 93be96fcc0 make style 2021-08-02 14:25:31 -07:00
Ketan Mittal 2cb619d9cf minor change to how level sets are indicated 2021-08-02 14:22:19 -07:00
Keith 6f2c580774 fixed style 2021-08-02 12:54:54 -07:00
Keith c0912fe75e added support to return local oscs 2021-08-02 12:53:29 -07:00
Keith 5ffd03a2da updated unit tests 2021-08-02 12:13:56 -07:00
Keith ec09fe54e4 style 2021-07-30 23:13:23 -07:00
Keith 2a09088fb0 added some simple unit tests 2021-07-30 23:12:56 -07:00
Keith e93a3542c0 example files written 2021-07-30 21:14:04 -07:00
Keith bdcbfafb42 cleaning code 2021-07-30 20:39:02 -07:00
Keith b9eaa8c309 fix style 2021-07-30 19:45:52 -07:00
Keith 96a10dfa2e finalize serial examples 2021-07-30 19:44:54 -07:00
Keith e4cebc0f6a merged with bug fix 2021-07-30 17:20:52 -07:00
Keith 865b2facc2 verification with embedded meshes 2021-07-30 16:54:15 -07:00
Keith 8877f99da8 Fixed Mesh::GetElementSize(), which did not work for embedded meshes 2021-07-30 16:54:15 -07:00
Keith 8a61b3e27b adding extra features 2021-07-30 16:54:15 -07:00
psocratis 54676a17c2 Fixed bug for serial builds. Fixed comments 2021-07-30 16:54:15 -07:00
psocratis f47244859a fixing parallel implementation 2021-07-30 16:54:15 -07:00
Keith 2e6583650f debugging parallel implementation 2021-07-30 16:54:15 -07:00
Keith 2bc6f6146a cleaned up files 2021-07-30 16:54:15 -07:00
psocratis d2b8fa35bb Fixing setting intrule. Adding parallel example 2021-07-30 16:54:15 -07:00
Keith 1fdfa6d39a code compiles. Needs debugging 2021-07-30 16:54:15 -07:00
Tom Stitt f89d359d06 add MultAtB and MultT(vector)
and const qualifier to Mult(vector)
2021-07-22 15:58:54 -07:00
Ketan Mittal 31ce22595b work for multiple Dirichlet and Neumann conditions 2021-07-22 10:02:09 -07:00
Keith a6270b2582 update osc file 2021-07-19 19:52:33 -07:00
Ketan Mittal 1d84ae004c working dirichlet+neumann 2021-07-19 10:51:02 -07:00
Ketan Mittal 1f35f2580d WIP - initial commit 2021-07-14 12:47:29 -07:00
blaz 86a297b34f Merge branch 'pr_autodiff' of https://github.com/mfem/mfem into pr_autodiff 2021-07-14 12:02:07 -07:00
blaz a9d6514fb6 cleaned version 2021-07-14 11:59:52 -07:00
Cameron Smith 4d51a907cf install examples if enabled
I don't think this has other significant side effects...
2021-07-14 16:16:58 +00:00
Cameron Smith a10fb1f421 install examples to examples dir 2021-07-14 16:14:01 +00:00
Cameron Smith afee0acf50 exes are RUNTIME comps, cmake sets the prefix 2021-07-14 13:18:11 +00:00
Cameron Smith 1d5ff54f4c install examples 2021-07-13 20:08:24 +00:00
Will Pazner e20ae257e5 Don't need LORSolver permutation anymore 2021-07-07 14:25:56 -07:00
blaz 7fcd181280 modified INSTALL 2021-07-03 23:47:06 -07:00
blaz d7ae085678 license check 2021-07-02 16:10:14 -07:00
blaz e967c8a7ef copyright check 2021-07-02 15:52:26 -07:00
blaz 1a6c24c5f2 bug in makefile 2021-07-02 15:42:31 -07:00
blaz 340e3ed188 autodiff makefile 2021-07-02 15:30:46 -07:00
blaz 1382ed617a Merge remote-tracking branch 'origin/master' into pr_autodiff 2021-07-02 15:08:41 -07:00
blaz 4d834ca852 cleaned code 2021-07-02 15:05:28 -07:00
blaz c8a8562ea4 modification and examples 2021-07-02 11:21:48 -07:00
Will Pazner 06032d9340 Add HostRead in BilinearForm::EliminateVDofs 2021-06-30 18:30:59 -07:00
Will Pazner 74710220a1 Remove need (mostly) for LOR DOF permutations
Build the permutation into the P and R operators of the LOR space, so that the
true DOF numbering corresponds to the same true DOF number of the high-order
space. This means the LORSolver does not need to perform any permutation, since
it is incorporated into the RAP.
2021-06-30 18:30:59 -07:00
Will Pazner 6e43e93144 Handle better support of LOR for nonconforming spaces 2021-06-30 18:30:59 -07:00
Will Pazner 0a9b0c681d Handle LOR permutation construction in variable-order case 2021-06-30 18:30:59 -07:00
Will Pazner 2657585cfe Enable LOR for variable-order spaces (in serial) 2021-06-30 18:30:59 -07:00
Will Pazner c2d96bebba Refactor LOR AssembleSystem 2021-06-30 18:30:59 -07:00
Will Pazner b69e30c2ab Add accessor for LORBase object from LORSolver 2021-06-30 18:30:59 -07:00
Will Pazner ec011048a0 Minor Doxygen comments edits 2021-06-30 18:30:59 -07:00
Yohann Dudouit 330f67cedc Reset with current MemoryType. 2021-06-30 10:06:23 -07:00
Yohann Dudouit 6651c80b27 Make Delete reset the Memory object. 2021-06-28 17:35:25 -07:00
Yohann Dudouit 3ac9eff225 Improve documentation. 2021-06-28 17:06:42 -07:00
Yohann Dudouit 3d399d8eb3 Remove unnecessary Reset. 2021-06-25 11:16:09 -07:00
Yohann Dudouit 216e0dfe08 Use Reset() in default constructor. 2021-06-24 19:08:38 -07:00
blaz a32ca0cb89 Another Lambda example 2021-05-26 15:53:39 -07:00
blaz 2e92b44070 Lambda function differentiation 2021-05-26 15:11:05 -07:00
blaz 2aa374efe8 Example of auto diff 2021-05-25 21:00:57 -07:00
blaz 476e642935 added cmake and make files for Eigen and CoDiPack 2021-05-25 17:53:33 -07:00
blaz 0da14179f7 initial copy frm fadg branch 2021-05-25 16:38:32 -07:00
Ketan Mittal ff940b8254 Merge branch 'tmop-align-surface' of https://github.com/mfem/mfem into tmop-align-surface 2021-02-04 10:04:32 -08:00
Ketan Mittal ec9c967b88 adding 3D combo metrics 2021-02-04 10:03:43 -08:00
Vladimir Z Tomov 966071f5ce Minor. 2021-02-02 22:55:28 -08:00
Vladimir Z Tomov ff6dc50d7f Computation of surface fitting errors. Changed the normalization. 2021-02-02 22:51:03 -08:00
Ketan Mittal a39f7635f5 bug fix for writing attribute 2021-01-25 10:42:04 -08:00
Ketan Mittal 7d2ce53a63 write element attribute in mesh file 2021-01-25 09:26:28 -08:00
Vladimir Z Tomov feca2f59dc Minor. 2021-01-14 12:26:28 -08:00
Vladimir Z Tomov 5e296c39c6 Minor. 2021-01-12 09:48:57 -08:00
Vladimir Z Tomov ec306e7166 Merge branch 'master' into tmop-align-surface 2021-01-12 08:49:06 -08:00
Vladimir Z Tomov 7588076853 3D surface fitting test. 2021-01-10 17:06:42 -08:00
Vladimir Z Tomov fbcc7406a7 Another option to diffuse fields. 2021-01-10 16:36:24 -08:00
Vladimir Z Tomov 06b40d8f9e Added a 2D triangle mesh. 2021-01-05 15:10:55 -08:00
Vladimir Z Tomov 944de5d51c Merge branch 'master' into tmop-align-surface 2021-01-04 16:05:28 -08:00
Vladimir Z Tomov 438fd06639 Minor. 2021-01-01 23:28:50 -08:00
Vladimir Z Tomov be26f52385 Improved the output for initial-vs-final energy. 2020-12-31 11:42:30 -08:00
Vladimir Z Tomov 94e16907c9 Minor. 2020-12-30 22:45:51 -08:00
Vladimir Z Tomov dd1fe9e7c3 Added FD support for surface fitting. 2020-12-30 22:39:39 -08:00
Vladimir Z Tomov caeecc0e6d Improved function arguments. 2020-12-30 22:12:43 -08:00
Vladimir Z Tomov 69f70eca5e Found a bug. 2020-12-29 15:10:10 -08:00
Vladimir Z Tomov dbbbf1f443 Merge branch 'master' into tmop-align-surface 2020-12-18 18:35:42 -08:00
Vladimir Z Tomov 2d39d300d1 Updated the fitting method. 2020-12-18 18:34:36 -08:00
Vladimir Z Tomov be3fb993f1 Non-variational form of the fitting term. 2020-11-02 10:06:18 -08:00
Vladimir Z Tomov 6e88331f87 Objective function, derivatives, normalization. 2020-10-28 22:51:43 -07:00
Tomov 0e195e2f87 wip surface alignment. 2020-09-21 22:21:47 -07:00
124 changed files with 10508 additions and 1111 deletions
+2 -2
View File
@@ -82,9 +82,9 @@ jobs:
uses: mfem/github-actions/build-mfem@v2.0
with:
os: ${{ runner.os }}
target: optim
target: opt
codecov: NO
mpi: parallel
mpi: par
build-system: make
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
metis-dir: ${{ env.METIS_TOP_DIR }}
+10
View File
@@ -51,6 +51,8 @@ examples/ex1[04-9]
examples/ex1[0-9]p
examples/ex2[0-9]
examples/ex2[0-9]p
examples/ex30
examples/ex30p
examples/refined.mesh
examples/displaced.mesh
@@ -223,6 +225,14 @@ miniapps/mtop/ParHeat*
miniapps/mtop/seqheat
miniapps/mtop/SeqHeat*
miniapps/autodiff/paradiff
miniapps/autodiff/seqadiff
miniapps/autodiff/seqtest
miniapps/autodiff/par_example
miniapps/autodiff/seq_example
miniapps/autodiff/seq_test
miniapps/autodiff/Exampl*
miniapps/navier/navier_mms
miniapps/navier/navier_kovasznay
miniapps/navier/navier_kovasznay_vs
+36 -4
View File
@@ -29,12 +29,34 @@ stages:
variables:
CUSTOM_CI_BUILDS_DIR: "/usr/workspace/mfem/gitlab-runner"
USER_CI_TOP_DIR: "${CUSTOM_CI_BUILDS_DIR}/${GITLAB_USER_LOGIN}"
SHARED_REPOS_DIR: "${USER_CI_TOP_DIR}/repos"
AUTOTEST_ROOT: "${SHARED_REPOS_DIR}"
# MFEM_DATA_DIR is setup in '.gitlab/configs/setup-build-and-test.yml' and
# used in '.gitlab/configs/<machine>-config.yml':
MFEM_DATA_DIR: "${SHARED_REPOS_DIR}/mfem-data"
# Defines the default choice for updating the saved baseline results. By default
# the baseline can only be updated from the master branch. This variable offers
# the option to manually ask for rebaselining from another branch if necessary.
REBASELINE: "NO"
AUTOTEST: "NO"
# AUTOTEST_COMMIT: used only when AUTOTEST is set to YES.
# * If AUTOTEST_COMMIT is NOT set to NO, reporting jobs will commit their
# files to the MFEM/autotest repo.
# * If AUTOTEST_COMMIT is set to NO, reporting jobs will NOT commit their
# files to the MFEM/autotest repo. Instead they will just show the contents
# of the report files and remove them.
AUTOTEST_COMMIT: "YES"
# Trigger subpipelines:
quartz-build-and-test:
stage: sub-pipelines
variables:
_AUTOTEST: $AUTOTEST
# Explicitly pass down values that we want to be able to set when triggering
# pipelines manually or using scheduling
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/quartz-build-and-test.yml
strategy: depend
@@ -42,7 +64,11 @@ quartz-build-and-test:
quartz-baseline:
stage: sub-pipelines
variables:
_AUTOTEST: $AUTOTEST
# Explicitly pass down values that we want to be able to set when triggering
# pipelines manually or using scheduling
REBASELINE: "${REBASELINE}"
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/quartz-baseline.yml
strategy: depend
@@ -50,7 +76,10 @@ quartz-baseline:
lassen-build-and-test:
stage: sub-pipelines
variables:
_AUTOTEST: $AUTOTEST
# Explicitly pass down values that we want to be able to set when triggering
# pipelines manually or using scheduling
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/lassen-build-and-test.yml
strategy: depend
@@ -58,7 +87,10 @@ lassen-build-and-test:
corona-build-and-test:
stage: sub-pipelines
variables:
_AUTOTEST: $AUTOTEST
# Explicitly pass down values that we want to be able to set when triggering
# pipelines manually or using scheduling
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/corona-build-and-test.yml
strategy: depend
+1 -9
View File
@@ -18,19 +18,13 @@ variables:
# the pipeline, preventing any form of concurrency with other pipelines. This
# also means that the BUILD_ROOT directory will never be cleaned.
# TODO: add a clean-up mechanism
BUILD_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}/${CI_PROJECT_NAME}_${CI_COMMIT_REF_SLUG}_${CI_PIPELINE_ID}
BUILD_ROOT: ${USER_CI_TOP_DIR}/${CI_PROJECT_NAME}-${MACHINE_NAME}-pipeline-${CI_PIPELINE_ID}
# On LLNL's quartz, there is only one allocation shared among jobs in order to
# save time and resource. This allocation has to be uniquely named so that we
# are sure to retrieve it.
ALLOC_NAME: ${CI_PROJECT_NAME}_ci_${CI_PIPELINE_ID}
# Defines the default choice for updating the saved baseline results. By default
# the baseline can only be updated from the master branch. This variable offers
# the option to manually ask for rebaselining from another branch if necessary.
_REBASELINE: "NO"
_AUTOTEST: "NO"
# Git repositories used in the pipeline
TPLS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tpls.git
TESTS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tests.git
@@ -40,5 +34,3 @@ variables:
# Directory used to place artifacts.
ARTIFACTS_DIR: artifacts
SLURM_OVERLAP: 1
+10 -5
View File
@@ -26,17 +26,20 @@ variables:
- if: '$CI_COMMIT_BRANCH =~ /_cnone/ || $ON_CORONA != "ON"'
when: never
# Dont run autotest update if...
- if: '$CI_JOB_NAME =~ /report/ && $_AUTOTEST != "YES"'
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
when: never
# Report success on success status
- if: '$CI_JOB_NAME =~ /report_job_success/ && $_AUTOTEST == "YES"'
- if: '$CI_JOB_NAME =~ /report_job_success/ && $AUTOTEST == "YES"'
when: on_success
# Report failure on failure status
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $_AUTOTEST == "YES"'
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $AUTOTEST == "YES"'
when: on_failure
# Always release resource
- if: '$CI_JOB_NAME =~ /release_resource/'
when: always
# Always cleanup
- if: '$CI_JOB_NAME =~ /cleanup/'
when: always
# Default is to run if previous stage succeeded
- when: on_success
@@ -46,9 +49,11 @@ variables:
extends: [.on_corona]
stage: build_and_test
script:
# THREADS is used by 'tests/gitlab/build_and_test', run below
- export THREADS=12
- echo ${ALLOC_NAME}
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
- echo ${JOBID}
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 15 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data
- echo ${MFEM_DATA_DIR}
- echo ${SPEC}
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 15 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
+10 -4
View File
@@ -21,14 +21,17 @@ variables:
- if: '$CI_COMMIT_BRANCH =~ /_lnone/ || $ON_LASSEN == "OFF"' #run except if ...
when: never
# Don't run autotest update if...
- if: '$CI_JOB_NAME =~ /report/ && $_AUTOTEST != "YES"'
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
when: never
# Report success on success status
- if: '$CI_JOB_NAME =~ /report_job_success/ && $_AUTOTEST == "YES"'
- if: '$CI_JOB_NAME =~ /report_job_success/ && $AUTOTEST == "YES"'
when: on_success
# Report failure on failure status
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $_AUTOTEST == "YES"'
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $AUTOTEST == "YES"'
when: on_failure
# Always cleanup
- if: '$CI_JOB_NAME =~ /cleanup/'
when: always
- when: on_success
# Lassen uses a different job scheduler (spectrum lsf) that does not allow
@@ -39,5 +42,8 @@ variables:
extends: [.on_lassen]
stage: build_and_test
script:
- lalloc 1 -W 30 -q pdebug tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data
- echo ${MFEM_DATA_DIR}
- echo ${SPEC}
# Next script uses 'THREADS': leaving it empty --> it uses 'make all -j'
- lalloc 1 -W 30 -q pdebug tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
needs: [setup]
+10 -5
View File
@@ -22,17 +22,20 @@ variables:
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_QUARTZ == "OFF"'
when: never
# Don't run autotest update if...
- if: '$CI_JOB_NAME =~ /report/ && $_AUTOTEST != "YES"'
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
when: never
# Report success on success status
- if: '$CI_JOB_NAME =~ /report_job_success/ && $_AUTOTEST == "YES"'
- if: '$CI_JOB_NAME =~ /report_job_success/ && $AUTOTEST == "YES"'
when: on_success
# Report failure on failure status
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $_AUTOTEST == "YES"'
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $AUTOTEST == "YES"'
when: on_failure
# Always release resource
- if: '$CI_JOB_NAME =~ /release_resource/'
when: always
# Always cleanup
- if: '$CI_JOB_NAME =~ /cleanup/'
when: always
# Default is to run if previous stage succeeded
- when: on_success
@@ -42,9 +45,11 @@ variables:
extends: [.on_quartz]
stage: build_and_test
script:
# THREADS is used by 'tests/gitlab/build_and_test', run below
- export THREADS=12
- echo ${ALLOC_NAME}
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
- echo ${JOBID}
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 30 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --build-root "${BUILD_ROOT}" --data
- echo ${MFEM_DATA_DIR}
- echo ${SPEC}
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 30 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
+81
View File
@@ -0,0 +1,81 @@
# Copyright (c) 2010-2021, 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.
# Jobs report
.report_job_success:
script:
- echo ${MACHINE_NAME}
- echo ${AUTOTEST}
- echo ${AUTOTEST_COMMIT}
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
- cd ${AUTOTEST_ROOT}
- |
(
date
echo "Waiting to aquire lock on '$PWD/autotest.lock' ..."
# try to get an excusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
# command to hang indefinitely sometimes, so we use the timeout & retry
# as a workaround; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -w 5 9; do
true
done
echo "Aquired lock on '$PWD/autotest.lock'"
date
# Report SUCCESS while holding the file lock on 'autotest.lock'.
# The next script uses the following environment variables:
# - MACHINE_NAME, AUTOTEST_ROOT, AUTOTEST_COMMIT
# - CI_COMMIT_REF_SLUG, CI_PROJECT_DIR, CI_PIPELINE_URL
# It also calls the script '.gitlab/scripts/safe_create_rundir'.
${CI_PROJECT_DIR}/.gitlab/scripts/report_build_and_test_success
err=$?
# sleep for a period to allow NFS to propagate the above changes;
# clearly, there is no guarantee that other NFS clients will see the
# changes even after the timeout
sleep 10
exit $err
) 9> autotest.lock
.report_job_failure:
script:
- echo ${MACHINE_NAME}
- echo ${AUTOTEST}
- echo ${AUTOTEST_COMMIT}
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
- cd ${AUTOTEST_ROOT}
- |
(
date
echo "Waiting to aquire lock on '$PWD/autotest.lock' ..."
# try to get an excusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
# command to hang indefinitely sometimes, so we use the timeout & retry
# as a workaround; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -w 5 9; do
true
done
echo "Aquired lock on '$PWD/autotest.lock'"
date
# Report FAILURE while holding the file lock on 'autotest.lock'.
# The next script uses the following environment variables:
# - MACHINE_NAME, AUTOTEST_ROOT, AUTOTEST_COMMIT
# - CI_COMMIT_REF_SLUG, CI_PROJECT_DIR, CI_PIPELINE_URL
# It also calls the script '.gitlab/scripts/safe_create_rundir'.
${CI_PROJECT_DIR}/.gitlab/scripts/report_build_and_test_failure
err=$?
# sleep for a period to allow NFS to propagate the above changes;
# clearly, there is no guarantee that other NFS clients will see the
# changes even after the timeout
sleep 10
exit $err
) 9> autotest.lock
+39 -9
View File
@@ -9,13 +9,6 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# TPLS_DIR is used in .gitlab/scripts/baseline to provide the tpls location
# when call the runtest script in MFEM test repo.
# Note: the value must be consistent with what setup_baseline does.
variables:
TPLS_DIR: ${BUILD_ROOT}/tpls
AUTOTEST_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}_baseline
# The setup_baseline job doesn't rely on MFEM git repo. It prepares a
# pipeline-wide working directory downloading/updating external repos.
# TODO:
@@ -30,13 +23,50 @@ setup_baseline:
variables:
GIT_STRATEGY: none
script:
#
# Setup ${BUILD_ROOT}/tpls and ${BUILD_ROOT}/tests:
#
- echo "MACHINE_NAME = ${MACHINE_NAME}"
- echo "REBASELINE = ${REBASELINE}"
- echo "AUTOTEST = ${AUTOTEST}"
- echo "AUTOTEST_COMMIT = ${AUTOTEST_COMMIT}"
- echo "BUILD_ROOT ${BUILD_ROOT}"
- mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT}
- if [ ! -d "tpls" ]; then git clone ${TPLS_REPO}; fi
- if [ ! -d "tests" ]; then git clone ${TESTS_REPO}; fi
- cd tpls && git pull && cd ..
- cd tests && git pull origin && cd ..
#
# Setup ${AUTOTEST_ROOT}/autotest:
#
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
- mkdir -p ${AUTOTEST_ROOT} && cd ${AUTOTEST_ROOT}
- if [ ! -d "autotest" ]; then git clone ${AUTOTEST_REPO}; fi
- cd autotest && git pull && cd ..
- command -v flock || echo "Required command 'flock' not found"
- |
(
date
echo "Waiting to aquire lock on '$PWD/autotest.lock' ..."
# try to get an excusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
# command to hang indefinitely sometimes, so we use the timeout & retry
# as a workaround; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -w 5 9; do
true
done
echo "Aquired lock on '$PWD/autotest.lock'"
date
# clone/update the autotest repo while holding the file lock on
# 'autotest.lock'
err=0
if [[ ! -d "autotest" ]]; then
git clone ${AUTOTEST_REPO}
else
cd autotest && git pull && cd ..
fi || err=1
# sleep for a period to allow NFS to propagate the above changes;
# clearly, there is no guarantee that other NFS clients will see the
# changes even after the timeout
sleep 10
exit $err
) 9> autotest.lock
+73 -13
View File
@@ -9,13 +9,10 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
variables:
AUTOTEST_ROOT: ${CI_BUILDS_DIR}/MFEM_${MACHINE_NAME}_build_and_test
# setup clones the mfem/data repo in ${BUILD_ROOT}. The build_and_test script
# then symlinks the repo to the parent directory of the MFEM source directory.
# Unit tests that depend on the mfem/data repo will then detect that this
# directory is present and be enabled.
# Setup clones the mfem/data repo in ${SHARED_REPOS_DIR}. The build_and_test
# script then symlinks the repo to the parent directory of the MFEM source
# directory. Unit tests that depend on the mfem/data repo will then detect that
# this directory is present and be enabled.
setup:
tags:
- shell
@@ -24,11 +21,74 @@ setup:
variables:
GIT_STRATEGY: none
script:
- echo "BUILD_ROOT ${BUILD_ROOT}"
- mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT}
- if [ ! -d data ]; then git clone ${MFEM_DATA_REPO}; fi
#
# Setup MFEM_DATA_DIR=${SHARED_REPOS_DIR}/mfem-data, see '.gitlab-ci.yml'
# and '.gitlab/configs/<machine>-config.yml'
#
- echo "MACHINE_NAME = ${MACHINE_NAME}"
- echo "AUTOTEST = ${AUTOTEST}"
- echo "AUTOTEST_COMMIT = ${AUTOTEST_COMMIT}"
- echo "SHARED_REPOS_DIR ${SHARED_REPOS_DIR}"
- mkdir -p ${SHARED_REPOS_DIR} && cd ${SHARED_REPOS_DIR}
- command -v flock || echo "Required command 'flock' not found"
- |
(
date
echo "Waiting to aquire lock on '$PWD/mfem-data.lock' ..."
# try to get an excusive lock on fd 9 (mfem-data.lock) repeating the try
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
# command to hang indefinitely sometimes, so we use the timeout & retry
# as a workaround; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -w 5 9; do
true
done
echo "Aquired lock on '$PWD/mfem-data.lock'"
date
# clone/update the mfem/data repo while holding the file lock on
# 'mfem-data.lock'
err=0
if [[ ! -d "mfem-data" ]]; then
git clone ${MFEM_DATA_REPO} "mfem-data"
else
cd "mfem-data" && git pull && cd ..
fi || err=1
# sleep for a period to allow NFS to propagate the above changes;
# clearly, there is no guarantee that other NFS clients will see the
# changes even after the timeout
sleep 10
exit $err
) 9> mfem-data.lock
#
# Setup ${AUTOTEST_ROOT}/autotest:
#
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
- mkdir -p ${AUTOTEST_ROOT} && cd ${AUTOTEST_ROOT}
- if [ ! -d "autotest" ]; then git clone ${AUTOTEST_REPO}; fi
- cd autotest && git pull && cd ..
- |
(
date
echo "Waiting to aquire lock on '$PWD/autotest.lock' ..."
# try to get an excusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
# command to hang indefinitely sometimes, so we use the timeout & retry
# as a workaround; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -w 5 9; do
true
done
echo "Aquired lock on '$PWD/autotest.lock'"
date
# clone/update the autotest repo while holding the file lock on
# 'autotest.lock'
err=0
if [[ ! -d "autotest" ]]; then
git clone ${AUTOTEST_REPO}
else
cd autotest && git pull && cd ..
fi || err=1
# sleep for a period to allow NFS to propagate the above changes;
# clearly, there is no guarantee that other NFS clients will see the
# changes even after the timeout
sleep 10
exit $err
) 9> autotest.lock
+10 -6
View File
@@ -22,6 +22,7 @@ allocate_resource:
extends: .on_corona
stage: allocate_resource
script:
- echo ${ALLOC_NAME}
- salloc --exclusive --nodes=1 --partition=mi60 --time=30 --no-shell --job-name=${ALLOC_NAME}
timeout: 6h
needs: [setup]
@@ -40,24 +41,27 @@ release_resource:
extends: .on_corona
stage: release_resource_and_report
script:
- echo ${ALLOC_NAME}
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
- echo ${JOBID}
- ([[ -n "${JOBID}" ]] && scancel ${JOBID})
needs: [rocm_gcc_8.3.1]
# Jobs report
report_job_success:
extends: .on_corona
stage: release_resource_and_report
script:
- .gitlab/scripts/report_build_and_test_success
extends:
- .on_corona
- .report_job_success
report_job_failure:
extends: .on_corona
stage: release_resource_and_report
script:
- .gitlab/scripts/report_build_and_test_failure
extends:
- .on_corona
- .report_job_failure
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/corona-config.yml
- local: .gitlab/configs/setup-build-and-test.yml
- local: .gitlab/configs/report-build-and-test.yml
+7 -6
View File
@@ -21,18 +21,19 @@ opt_mpi_cuda_xl_16_1_1_8:
# Jobs report
report_job_success:
extends: .on_lassen
stage: report
script:
- .gitlab/scripts/report_build_and_test_success
extends:
- .on_lassen
- .report_job_success
report_job_failure:
extends: .on_lassen
stage: report
script:
- .gitlab/scripts/report_build_and_test_failure
extends:
- .on_lassen
- .report_job_failure
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/lassen-config.yml
- local: .gitlab/configs/setup-build-and-test.yml
- local: .gitlab/configs/report-build-and-test.yml
+84 -15
View File
@@ -16,12 +16,26 @@ stages:
- setup
- baseline_check
- baseline_report
- cleanup
- baseline_publish
baselinecheck_mfem_intel_quartz:
extends: [.on_quartz]
stage: baseline_check
variables:
# TPLS_DIR is used in .gitlab/scripts/baseline to provide the tpls location
# when call the runtest script in MFEM test repo.
# Note: the value must be consistent with the setup performed in
# .gitlab/configs/setup-baseline.yml.
TPLS_DIR: ${BUILD_ROOT}/tpls
script:
- echo ${BUILD_ROOT}
- echo ${TPLS_DIR}
# Used by the tests in MFEM/tests:
- export MFEM_TEST_NP=32
# The next script uses the following environment variables:
# * BASELINE_TEST, SYS_TYPE, CI_PROJECT_DIR, ARTIFACTS_DIR,
# * BUILD_ROOT, TPLS_DIR, MACHINE_NAME
- .gitlab/scripts/baseline
artifacts:
when: always
@@ -29,33 +43,88 @@ baselinecheck_mfem_intel_quartz:
- ${ARTIFACTS_DIR}
allow_failure: true
cleanup:
extends: .on_quartz
stage: cleanup
variables:
GIT_STRATEGY: none
script:
- echo "BUILD_ROOT=${BUILD_ROOT}"
- rm -rf "${BUILD_ROOT}" || true
report_baseline:
extends: [.on_quartz]
stage: baseline_report
script:
- cd ${AUTOTEST_ROOT}/autotest && git pull
- mkdir -p ${MACHINE_NAME}
- rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-${BASELINE_TEST}-${CI_COMMIT_REF_SLUG}"
- rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir ${rundir})
- cp ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/* ${rundir}
# We create an autotest-email.html file, because that's how we signal that there was a diff (temporary).
- echo ${MACHINE_NAME}
- echo ${AUTOTEST}
- echo ${AUTOTEST_COMMIT}
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
- cd ${AUTOTEST_ROOT}
- |
if [[ -f ${rundir}/*.err ]]
then
echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/*.err
cp ${rundir}/*.err ${rundir}/autotest-email.html
fi
- git add ${rundir}
- git commit -am "GitLab CI log for ${BASELINE_TEST} on ${MACHINE_NAME} with intel ($(date +%Y-%m-%d))"
- git push origin master
(
date
echo "Waiting to aquire lock on '$PWD/autotest.lock' ..."
# try to get an excusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; simply using no timeout, i.e. 'flock 9', causes the
# command to hang indefinitely sometimes, so we use the timeout & retry
# as a workaround; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -w 5 9; do
true
done
echo "Aquired lock on '$PWD/autotest.lock'"
date
# ----------------------
cd ${AUTOTEST_ROOT}/autotest || \
{ echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; }
mkdir -p ${MACHINE_NAME}
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-${BASELINE_TEST}-${CI_COMMIT_REF_SLUG}"
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir ${rundir})
cp ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/* ${rundir}
# We create an autotest-email.html file, because that's how we signal that there was a diff (temporary).
if [[ -f ${rundir}/${BASELINE_TEST}.err ]]; then
cp ${rundir}/${BASELINE_TEST}.err ${rundir}/autotest-email.html
fi
printf "%s\n" "" "Pipeline URL:" "$CI_PIPELINE_URL" \
>> ${rundir}/pipeline.txt
msg="GitLab CI log for ${BASELINE_TEST} on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then
git pull && \
git add ${rundir} && \
git commit -m "${msg}" && \
git push origin master
else
for file in ${rundir}/*; do
echo "------------------------------"
echo "Content of '$file'"
echo "******************************"
cat $file
echo "******************************"
done
rm -rf ${rundir} || true
fi
err=$?
# ----------------------
# sleep for a period to allow NFS to propagate the above changes;
# clearly, there is no guarantee that other NFS clients will see the
# changes even after the timeout
sleep 10
exit $err
) 9> autotest.lock
baselinepublish_mfem_quartz:
extends: [.on_quartz]
stage: baseline_publish
rules:
- if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
# - if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
- if: '$REBASELINE == "YES"'
when: manual
script:
- echo ${BUILD_ROOT}
- echo ${PWD}
- echo ${ARTIFACTS_DIR}
- ls -lA ${ARTIFACTS_DIR}
- .gitlab/scripts/rebaseline
include:
+10 -6
View File
@@ -22,6 +22,7 @@ allocate_resource:
extends: .on_quartz
stage: allocate_resource
script:
- echo ${ALLOC_NAME}
- salloc --exclusive --nodes=1 --partition=pdebug --time=30 --no-shell --job-name=${ALLOC_NAME}
timeout: 6h
@@ -73,23 +74,26 @@ release_resource:
extends: .on_quartz
stage: release_resource_and_report
script:
- echo ${ALLOC_NAME}
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
- echo ${JOBID}
- ([[ -n "${JOBID}" ]] && scancel ${JOBID})
# Jobs report
report_job_success:
extends: .on_quartz
stage: release_resource_and_report
script:
- .gitlab/scripts/report_build_and_test_success
extends:
- .on_quartz
- .report_job_success
report_job_failure:
extends: .on_quartz
stage: release_resource_and_report
script:
- .gitlab/scripts/report_build_and_test_failure
extends:
- .on_quartz
- .report_job_failure
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/quartz-config.yml
- local: .gitlab/configs/setup-build-and-test.yml
- local: .gitlab/configs/report-build-and-test.yml
+9 -1
View File
@@ -20,7 +20,8 @@ base_out=${base}.out
artifacts_path=${CI_PROJECT_DIR}/${ARTIFACTS_DIR}
# prepare
cd ${BUILD_ROOT}
cd ${BUILD_ROOT} || \
{ echo "Invalid BUILD_ROOT=$BUILD_ROOT"; exit 1; }
ln -snf ${CI_PROJECT_DIR} mfem
cd tests
[[ -d _${BASELINE_TEST} ]] && rm -rf _${BASELINE_TEST}
@@ -33,6 +34,9 @@ elif [[ ${MACHINE_NAME} == "corona" ]]; then
srun --nodes=1 -t 60 -p mi60 ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
elif [[ ${MACHINE_NAME} == "lassen" ]]; then
lalloc 1 -q pdebug ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
else
echo "Unknown machine: MACHINE_NAME=$MACHINE_NAME"
exit 1
fi
# post
@@ -60,6 +64,10 @@ then
cp ${base_out} ${artifacts_path}/${base_out}
fi
if [[ -f ${BASELINE_TEST}.out ]]; then
cp ${BASELINE_TEST}.out ${artifacts_path}
fi
# base_diff won't even exist if there is no difference.
if [[ -f ${base_diff} ]]
then
+20 -7
View File
@@ -13,20 +13,33 @@
echo "Runs if there was at least one failure on ${MACHINE_NAME}"
cd ${AUTOTEST_ROOT}/autotest && git pull
cd ${AUTOTEST_ROOT}/autotest || \
{ echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; }
mkdir -p ${MACHINE_NAME}
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-ci-${CI_COMMIT_REF_SLUG}"
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir $rundir)
echo "There was an error while running CI on ${MACHINE_NAME}" > ${rundir}/gitlab.err
echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/gitlab.err
printf "%s\n" "Some 'build-and-test' jobs on ${MACHINE_NAME} FAILED." \
"Pipeline URL:" "$CI_PIPELINE_URL" > ${rundir}/gitlab.err
msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
# Create 'autotest-email.html' to indicate failure:
cp ${rundir}/gitlab.err ${rundir}/autotest-email.html
git pull
git add ${rundir}
git commit -am "${msg}"
git push origin master
if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then
git pull && \
git add ${rundir} && \
git commit -m "${msg}" && \
git push origin master
else
for file in ${rundir}/*; do
echo "------------------------------"
echo "Content of '$file'"
echo "******************************"
cat $file
echo "******************************"
done
rm -rf ${rundir} || true
fi
+19 -7
View File
@@ -13,18 +13,30 @@
echo "Can only run if all the ${MACHINE_NAME} jobs passed"
cd ${AUTOTEST_ROOT}/autotest && git pull
cd ${AUTOTEST_ROOT}/autotest || \
{ echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; }
mkdir -p ${MACHINE_NAME}
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-ci-${CI_COMMIT_REF_SLUG}"
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir $rundir)
echo "The ${MACHINE_NAME} jobs were successful" > ${rundir}/gitlab.out
echo "See the pipeline here -> $CI_PIPELINE_URL" >> ${rundir}/gitlab.err
printf "%s\n" "The 'build-and-test' jobs on ${MACHINE_NAME} were SUCCESSFUL." \
"Pipeline URL:" "$CI_PIPELINE_URL" > ${rundir}/gitlab.out
msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
git pull
git add ${rundir}
git commit -am "${msg}"
git push origin master
if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then
git pull && \
git add ${rundir} && \
git commit -m "${msg}" && \
git push origin master
else
for file in ${rundir}/*; do
echo "------------------------------"
echo "Content of '$file'"
echo "******************************"
cat $file
echo "******************************"
done
rm -rf ${rundir} || true
fi
+28 -6
View File
@@ -10,6 +10,15 @@
Version 4.3.1 (development)
===========================
- Added support for automatic differentiation. Users can select between
native implementation and external library implementation at the
configuration phase. A parallel and two serial examples are implemented
in the autodiff miniapp directory.
- Added support for mesh preprocessing to resolve fine scale problem data
before simulation. This feature uses adaptive mesh refinement to control the
associated data oscillation error. See the new Example 30/30p.
- Switched from Artistic Style (astyle) version 2.05.1 to version 3.1 for code
formatting. See the "make style" target.
@@ -18,6 +27,9 @@ Version 4.3.1 (development)
- Added support for hr-adaptivity using TMOP-based error estimator.
- Coefficient::SetTime now propagates the new time into internally stored
Coefficient objects.
- Added initial support for google-benchmarks in the tests/benchmarks directory.
It can be enabled with MFEM_USE_BENCHMARK=YES.
@@ -47,11 +59,26 @@ Version 4.3.1 (development)
output format if no physical groups are defined) are now successfully loaded,
and elements are reassigned attribute number 1.
- Added new miniapps that use the ParELAG library, its hybrid smoothers, and the
hierarchy of spaces created by the element-based AMG (AMGe) methodology in
ParELAG to build multigrid solvers for H(curl) and H(div) forms. See the
miniapps/parelag directory for more details.
- Fixed several MinGW build issues on Windows.
- Remove the 'u' flag in the ar command, to update all files in the archive,
avoiding file name collisions from different subdirectories.
- Added initial TMOP-based capabilities for surface fitting and tangential
relaxation in the mesh-optimizer and pmesh-optimizer miniapps.
- Added ParMesh Adjaceny Set (adjset) creation support to the Conduit Mesh
Blueprint MFEM wrapper functions in ConduitDataCollection.
- `HypreParVector` and `Vector` now support move semantics, and the copy
constructor for `HypreParVector` now copies the local vector data.
Version 4.3, released on July 29, 2021
======================================
@@ -306,7 +333,7 @@ Miscellaneous
* HYPRE >= 2.22.0 for CUDA support
* libCEED >= 0.8
* PETSc >= 3.15.0 for CUDA support
* RAJA >= 0.14.0
* RAJA >= 0.13.0
see INSTALL for more details.
- Added a "scaled Jacobian" visualization option in the Mesh Explorer miniapp to
@@ -320,11 +347,6 @@ Miscellaneous
- Various other simplifications, extensions, and bugfixes in the code.
- Added new miniapps that use the ParELAG library, its hybrid smoothers, and the
hierarchy of spaces created by the element-based AMG (AMGe) methodology in
ParELAG to build multigrid solvers for H(curl) and H(div) forms. See the
miniapps/parelag directory for more details.
API changes
-----------
- Added an abstract interface `mfem::FaceRestriction` for `H1FaceRestriction`
+17 -2
View File
@@ -252,6 +252,11 @@ if (MFEM_USE_OPENMP OR MFEM_USE_LEGACY_OPENMP)
endif()
find_package(OpenMP REQUIRED)
set(OPENMP_LIBRARIES ${OpenMP_CXX_LIBRARIES})
if(APPLE)
# On macOS, the compiler needs additional help to find the <omp.h> header.
# See issue #2642 for more information.
include_directories(${OpenMP_CXX_INCLUDE_DIRS})
endif(APPLE)
endif()
# SuiteSparse (before SUNDIALS which may depend on KLU)
@@ -367,6 +372,12 @@ if (MFEM_USE_HIOP)
# find_package updates HIOP_FOUND, HIOP_INCLUDE_DIRS, HIOP_LIBRARIES
endif()
# CoDiPack package
if (MFEM_USE_CODIPACK)
find_package(CODIPACK REQUIRED)
# find_package updates CODIPACK_FOUND, CODIPACK_INCLUDE_DIRS, CODIPACK_LIBRARIES
endif()
# OCCA
if (MFEM_USE_OCCA)
find_package(OCCA REQUIRED)
@@ -446,7 +457,7 @@ endif()
set(MFEM_TPLS OPENMP HYPRE BLAS LAPACK SuperLUDist METIS SuiteSparse SUNDIALS PETSC
SLEPC MESQUITE MUMPS STRUMPACK AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB NETCDF
MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE ADIOS2
CUSPARSE MKL_CPARDISO AMGX CALIPER BENCHMARK PARELAG MPI_CXX)
CUSPARSE MKL_CPARDISO AMGX CALIPER CODIPACK BENCHMARK PARELAG MPI_CXX)
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
set(TPL_LIBRARIES "")
@@ -576,7 +587,11 @@ add_custom_target(${MFEM_EXEC_PREREQUISITES_TARGET_NAME})
# Create a target for all examples and, optionally, enable it.
set(MFEM_ALL_EXAMPLES_TARGET_NAME examples)
add_mfem_target(${MFEM_ALL_EXAMPLES_TARGET_NAME} ${MFEM_ENABLE_EXAMPLES})
add_subdirectory(examples EXCLUDE_FROM_ALL)
if (MFEM_ENABLE_EXAMPLES)
add_subdirectory(examples) #install examples if enabled
else()
add_subdirectory(examples EXCLUDE_FROM_ALL)
endif()
# Create a target for all miniapps and, optionally, enable it.
set(MFEM_ALL_MINIAPPS_TARGET_NAME miniapps)
+101 -5
View File
@@ -42,6 +42,7 @@ back to them before issuing pull requests:
- [New Feature Development](#new-feature-development)
- [Developer Guidelines](#developer-guidelines)
- [Pull Requests](#pull-requests)
- [MFEM PR Rules](#mfem-pr-rules)
- [Pull Request Checklist](#pull-request-checklist)
- [Master/Next Workflow](#masternext-workflow)
- [Releases](#releases)
@@ -67,8 +68,9 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
with regards to documentation and code styling.
- Please do not commit large/binary files to the central repository (use a fork
instead).
- Pull requests should be issued toward `mfem:master`. Make sure
to check the items off the [Pull Request Checklist](#pull-request-checklist).
- Pull requests should be issued toward `mfem:master`. Make sure
to check the items off the [Pull Request Checklist](#pull-request-checklist) and
follow the [MFEM PR Rules](#mfem-pr-rules).
- When your contribution is fully working and ready to be reviewed, add
the `ready-for-review` label.
- PRs are treated similarly to journal submission with an "editor" assigning two
@@ -121,6 +123,7 @@ The MFEM source code has the following structure:
├── mesh
├── miniapps
│ ├── adjoint
│ ├── autodiff
│ ├── common
│ ├── electromagnetics
│ ├── gslib
@@ -326,8 +329,12 @@ Before you can start, you need a GitHub account, here are a few suggestions:
change the code by default.
- Code specifics
- All significant new classes, methods and functions have Doxygen-style
documentation in source comments.
- All new public, protected, and private classes, methods, data members, and
functions have Doxygen-style documentation in source comments.
- In addition to arguments and functionality, documentation should include the
current limitations of the code, any background information that is
implicitly assumed in the implementation, and the ownership and lifetime
of data.
- Consistent code styling is enforced with `make style` in the top-level
directory. This requires [Artistic Style](http://astyle.sourceforge.net) (we
specifically use version 3.1). See also the file `config/mfem.astylerc`.
@@ -335,6 +342,9 @@ Before you can start, you need a GitHub account, here are a few suggestions:
internal library code. (You can use `std` in examples and miniapps.)
- When manually resolving conflicts during a merge, make sure to mention the
conflicted files in the commit message.
- All significant new features and changes should be documented in CHANGELOG.
- New examples and miniapps should have documentation on the MFEM webpage.
### Pull Requests
@@ -400,6 +410,83 @@ Before you can start, you need a GitHub account, here are a few suggestions:
- If triggered, track the status of the LLNL GitLab tests. If failing, ask
one of the _LLNL developers_ for details.
### MFEM PR Rules
The Pull Request (PR) approval process in MFEM is similar to the approval of papers in a peer-reviewed journal. In particular:
1. There is an MFEM board of "editors" that evaluates new PRs and assigns "reviewers" for each PR.
2. The assigned reviewers are responsible to carefully review and test the proposed PR.
3. A PR can be (manually) merged in the *next* branch only if 2 of the assigned reviewers have approved it and it has passed internal testing. This merge can be performed by any of the assigned reviewers or by any of the editors.
4. A PR can be merged in the *master* branch only if it has been tested successfully for a week in *next* and an editor has (optionally) taken a final look. This merge can be performed only by one of the editors.
#### Responsibilities of Editors
The current list of MFEM editors is:
- @v-dobrev (Veselin Dobrev)
- @tzanio (Tzanio Kolev)
- @pazner (Will Pazner)
- @mlstowell (Mark Stowell)
**The responsibilities of the editors are:**
1. To assign appropriate milestone and labels for new PRs, e.g. *bugfix*, *minor*, *api-change*, *high-impact*, etc.
2. To assign at least 2 reviewers for new PRs. An editor can also be a reviewer. The editor, reviewers, and author should be listed as "Assignees" on the GitHub PR page. After assignment, the `in-review` label should be added.
3. To complete the initial PR evaluation and assignments in a timely manner: 1 week from submission.
4. To assist reviewers when they need help with their reviews (but also to stay out of the way when they don't).
5. To remind the reviewers about timely completion of their review.
6. To take a final look and complete the PR merge in *master*. The final look step is optional and shouldn't take more than 3 days.
7. The assignment of bugfixes should be expedited proportional to their importance, e.g. in some cases the editor can assign much shorter review window.
#### Responsibilities of Reviewers
Everyone on the MFEM team can be asked to serve as a reviewer on a PR in their area of expertise.
**The responsibilities of the reviewers are:**
1. To let the editors know if the proposed assignment is not a good match for them.
2. To communicate with the PR author, provide feedback and work with them to resolve issues.
3. To ensure the quality of the PR by making sure that the code adheres to the [Developer Guidelines](#developer-guidelines), e.g. all methods, data members, and functions have documentation, including data ownership and lifetime, new examples/miniapps have a corresponding PR in mfem/web, major features have `CHANGELOG` entries, etc.
3. To seek help from the editors in case of difficulties.
4. To complete the review in a timely manner: 3 weeks from assignment.
5. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
6. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
7. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
#### Responsibilities of Authors
Authors should clearly indicate when a PR is ready for review (before that the PR should be marked as `Draft` or `[WIP]`).
**The responsibilities of the authors are:**
1. To follow the instructions and PR checklist in the `CONTRIBUTING.md` document in the MFEM repository.
2. To respond to reviewer feedback in a timely manner.
3. Authors are encouraged to perform testing and inform the reviewers about the results.
4. Authors can use the "Reviewers" section of the GitHub PR page to suggest reviewers, but the "Assignees" section will show who the editor has assigned to do the reviews.
5. To indicate when the PR is ready for review by adding the `ready-for-review` label.
### Pull Request Checklist
Before a PR can be merged, it should satisfy the following:
@@ -453,7 +540,9 @@ Before a PR can be merged, it should satisfy the following:
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
- [ ] New capability:
- [ ] All significant new classes, methods and functions have Doxygen-style documentation in source comments.
- [ ] All new public, protected, and private classes, methods, data members, and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, template parameters, and prerequisites for calling new functions.
- [ ] Pointer arguments and return values must specify whether ownership is being transferred or lent with the call.
- [ ] Any new functions should include descriptions of their intended use e.g. for internal use only, user-facing, etc., along with references to example code whenever possible/appropriate.
- [ ] Consider adding new sample runs in existing examples to highlight the new capability.
- [ ] Consider saving cool simulation pictures with the new capability in the Confluence gallery (LLNL only) or submitting them, via pull request, to the gallery section of the `mfem/web` repo.
- [ ] If this is a major new feature, consider mentioning it in the short summary inside `README` *(rare)*.
@@ -464,6 +553,7 @@ Before a PR can be merged, it should satisfy the following:
- [ ] (LLNL only) After merging:
- [ ] Update internal tests to include the new features.
### Master/Next Workflow
MFEM uses a `master`/`next`-branch workflow as described below:
@@ -555,8 +645,10 @@ MFEM uses a `master`/`next`-branch workflow as described below:
- Update version and shortlinks in `src/index.md` and `src/download.md`.
- Use [cloc-1.62.pl](http://cloc.sourceforge.net/) and `ls -lh` to estimate the SLOC and the tarball size in `src/download.md`.
## LLNL Workflow
### Mirroring on Bitbucket
- The GitHub `master` and `next` branches are mirrored to the LLNL institutional
@@ -576,6 +668,7 @@ MFEM uses a `master`/`next`-branch workflow as described below:
- `mfem:gh-next` -- Bleeding-edge development version, may be broken, use at
your own risk.
### Mirroring on GitLab
- MFEM repository is also mirrored on the LLNL GitLab instance, in a
@@ -598,6 +691,7 @@ In addition, developers can set local git hooks to run some quick checks on
commit or push, see the [README](config/githooks/README.md) in the `config/githooks`
directory.
### Linux and Mac smoke tests
We use GitHub Actions to drive the default tests on the `master` and `next`
branches. See the `.github/workflows` files and the logs at
@@ -609,6 +703,7 @@ constraint on jobs. Two virtual machines are configured - Mac (OS X) and Linux.
- Tests on the `master` branch are triggered whenever a PR is issued on this branch.
- Tests on the `next` branch are currently scheduled to run each night.
### Windows smoke test
We use Appveyor to test building with the MS Visual C++ compiler in a Windows
environment, as well as to test the CMake build. See the `.appveyor` file and the
@@ -618,6 +713,7 @@ build logs at
CMake is used to generate the MSVC Project files and drive the build. A release
and debug build is performed with a simple run of `ex1` to verify the executable.
### Tests at LLNL
- We mirror the `master` and `next` branches internally (to `gh-master` and
+18 -2
View File
@@ -467,6 +467,14 @@ MFEM_USE_HIOP = YES/NO
Enable the usage of HiOp (https://github.com/LLNL/hiop) in MFEM. HiOp is an
HPC solver for nonlinear optimization problems.
MFEM_USE_CODIPACK = YES/NO
Enable automatic differentiation using the CoDiPack library.
www.scicomp.uni-kl.de/codi/
MFEM_USE_ADFORWARD = YES/NO
Enable forward mode for AD packages. This option is valid
only if the AD package supports two modes (backward/forward).
MFEM_USE_CUDA = YES/NO
Enables support for CUDA devices in MFEM. CUDA is a parallel computing
platform and programming model for general computing on graphical processing
@@ -703,6 +711,11 @@ The specific libraries and their options are:
Options: HIOP_OPT, HIOP_LIB.
Versions: HIOP >= 0.4.6.
- CoDiPack (optiobal), used with MFEM_USE_CODIPACK = YES
URL: https://www.scicomp.uni-kl.de/codi/
Options: CODIPACK_OPT
Versions: 1.9.3
- GSLIB (optional), used when MFEM_USE_GSLIB = YES. The gslib library must be
built prior to the MFEM build, as follows: download gslib-1.0.7, untar it at
the same level as MFEM and create a symbolic link: "ln -s gslib-1.0.7 gslib".
@@ -739,10 +752,10 @@ The specific libraries and their options are:
Versions: libCEED >= 0.8.
- RAJA (optional), used when MFEM_USE_RAJA = YES.
Beginning with MFEM v4.3, only RAJA v0.13.0+ is supported.
Beginning with MFEM v4.3, only RAJA v0.14.0+ is supported.
URL: https://github.com/LLNL/RAJA
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
Versions: RAJA >= 0.13.0.
Versions: RAJA >= 0.14.0.
- Caliper (optional), used when MFEM_USE_CALIPER = YES.
URL: https://github.com/LLNL/Caliper
@@ -908,6 +921,8 @@ MFEM_USE_MPFR
MFEM_USE_ZLIB
MFEM_USE_PUMI
MFEM_USE_HIOP
MFEM_USE_CODIPACK
MFEM_USE_ADFORWARD
MFEM_USE_CUDA
MFEM_USE_OCCA
MFEM_USE_CEED
@@ -967,6 +982,7 @@ The CMake build system adds auto-detection for the following packages/libraries:
- POSIXCLOCKS
- PUMI
- HIOP
- CoDiPack
- OCCA
- RAJA
- UMPIRE
+2
View File
@@ -54,6 +54,8 @@ set(MFEM_USE_CEED @MFEM_USE_CEED@)
set(MFEM_USE_UMPIRE @MFEM_USE_UMPIRE@)
set(MFEM_USE_SIMD @MFEM_USE_SIMD@)
set(MFEM_USE_ADIOS2 @MFEM_USE_ADIOS2@)
set(MFEM_USE_CODIPACK @MFEM_USE_CODIPACK@)
set(MFEM_USE_ADFORWARD @MFEM_USE_ADFORWARD@)
set(MFEM_USE_CALIPER @MFEM_USE_CALIPER@)
set(MFEM_USE_BENCHMARK @MFEM_USE_BENCHMARK@)
set(MFEM_USE_PARELAG @MFEM_USE_PARELAG@)
+6
View File
@@ -175,6 +175,12 @@
// Enable interface to the MKL CPardiso library.
#cmakedefine MFEM_USE_MKL_CPARDISO
// Use forward mode for automatic differentiation
#cmakedefine MFEM_USE_ADFORWARD
// Enable the use of the CoDiPack library for AD
#cmakedefine MFEM_USE_CODIPACK
// Enable MFEM functionality based on the Google Benchmark library.
#cmakedefine MFEM_USE_BENCHMARK
+24
View File
@@ -0,0 +1,24 @@
# Copyright (c) 2010-2021, 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.
# Automatic differentiation using the CoDiPack library.
# www.scicomp.uni-kl.de/codi/
# Sets the following variables:
# - CODIPACK_FOUND
# - CODIPACK_INCLUDE_DIRS
# - CODIPACK_LIBRARIES
include(MfemCmakeUtilities)
mfem_find_package(CODIPACK CODIPACK CODIPACK_DIR
"include" "codi.h"
"lib" ""
"Paths to headers required by CODIPACK."
"Libraries required by CODIPACK.")
@@ -100,6 +100,8 @@ macro(add_mfem_examples EXE_SRCS)
string(REPLACE ".cpp" "" EXE_NAME "${EXE_PREFIX}${SRC_FILENAME}")
mfem_add_executable(${EXE_NAME} ${SRC_FILE})
install(TARGETS ${EXE_NAME}
RUNTIME DESTINATION examples)
add_dependencies(${MFEM_ALL_EXAMPLES_TARGET_NAME} ${EXE_NAME})
if (EXE_NEEDED_BY)
add_dependencies(${EXE_NEEDED_BY} ${EXE_NAME})
+6
View File
@@ -180,6 +180,12 @@
// Enable interface to the MKL CPardiso library.
// #define MFEM_USE_MKL_CPARDISO
// Use forward mode for automatic differentiation
// #define MFEM_USE_ADFORWARD
// Enable the use of the CoDiPack library for AD
// #define MFEM_USE_CODIPACK
// Enable functionality based on the Google Benchmark library.
// #define MFEM_USE_BENCHMARK
+2
View File
@@ -58,6 +58,8 @@ MFEM_USE_UMPIRE = @MFEM_USE_UMPIRE@
MFEM_USE_SIMD = @MFEM_USE_SIMD@
MFEM_USE_ADIOS2 = @MFEM_USE_ADIOS2@
MFEM_USE_MKL_CPARDISO = @MFEM_USE_MKL_CPARDISO@
MFEM_USE_ADFORWARD = @MFEM_USE_ADFORWARD@
MFEM_USE_CODIPACK = @MFEM_USE_CODIPACK@
MFEM_USE_BENCHMARK = @MFEM_USE_BENCHMARK@
MFEM_USE_PARELAG = @MFEM_USE_PARELAG@
+5
View File
@@ -58,6 +58,8 @@ option(MFEM_USE_SIMD "Enable use of SIMD intrinsics" OFF)
option(MFEM_USE_ADIOS2 "Enable ADIOS2" OFF)
option(MFEM_USE_CALIPER "Enable Caliper support" OFF)
option(MFEM_USE_MKL_CPARDISO "Enable MKL CPardiso" OFF)
option(MFEM_USE_ADFORWARD "Enable forward mode for AD" OFF)
option(MFEM_USE_CODIPACK "Enable automatic differentiation (AD) using CoDiPack" OFF)
option(MFEM_USE_BENCHMARK "Enable Google Benchmark" OFF)
option(MFEM_USE_PARELAG "Enable ParELAG" OFF)
@@ -243,6 +245,9 @@ set(BLAS_LIBRARIES "" CACHE STRING "The BLAS library.")
set(LAPACK_INCLUDE_DIRS "" CACHE STRING "Path to LAPACK headers.")
set(LAPACK_LIBRARIES "" CACHE STRING "The LAPACK library.")
set(CODIPACK_INCLUDE_DIRS "${MFEM_DIR}/../CoDiPack/inlude" CACHE STRING "Path to CoDiPack headers.")
set(CODIPACK_LIBRARIES "")
# Some useful variables:
set(CMAKE_SKIP_PREPROCESSED_SOURCE_RULES ON) # Skip *.i rules
set(CMAKE_SKIP_ASSEMBLY_SOURCE_RULES ON) # Skip *.s rules
+13
View File
@@ -59,6 +59,9 @@ HIP_FLAGS = --amdgpu-target=$(HIP_ARCH)
HIP_XCOMPILER =
HIP_XLINKER = -Wl,
# Flags for generating dependencies.
DEP_FLAGS = -MM -MT
ifneq ($(NOTMAC),)
AR = ar
ARFLAGS = crv
@@ -86,6 +89,9 @@ else
BUILD_RPATH = $(XLINKER)-undefined,dynamic_lookup
INSTALL_SOFLAGS = $(subst $1 ,,$(call MAKE_SOFLAGS,$(MFEM_LIB_DIR)))
INSTALL_RPATH = $(XLINKER)-undefined,dynamic_lookup
# Silence unused command line argument warnings when generating dependencies
# with mpicxx and clang
DEP_FLAGS := -Wno-unused-command-line-argument $(DEP_FLAGS)
endif
# Set CXXFLAGS to overwrite the default selection of DEBUG_FLAGS/OPTIM_FLAGS
@@ -151,6 +157,8 @@ MFEM_USE_UMPIRE = NO
MFEM_USE_SIMD = NO
MFEM_USE_ADIOS2 = NO
MFEM_USE_MKL_CPARDISO = NO
MFEM_USE_ADFORWARD = NO
MFEM_USE_CODIPACK = NO
MFEM_USE_BENCHMARK = NO
MFEM_USE_PARELAG = NO
@@ -408,6 +416,11 @@ HIOP_DIR = @MFEM_DIR@/../hiop/install
HIOP_OPT = -I$(HIOP_DIR)/include
HIOP_LIB = -L$(HIOP_DIR)/lib -lhiop $(LAPACK_LIB)
# CoDiPack
CODIPACK_DIR = @MFEM_DIR@/../CoDiPack
CODIPACK_OPT = -I$(CODIPACK_DIR)
CODIPACK_LIB =
# GSLIB library
GSLIB_DIR = @MFEM_DIR@/../gslib/build
GSLIB_OPT = -I$(GSLIB_DIR)/include
+1
View File
@@ -781,6 +781,7 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/miniapps/gslib \
@MFEM_SOURCE_DIR@/miniapps/meshing \
@MFEM_SOURCE_DIR@/miniapps/mtop \
@MFEM_SOURCE_DIR@/miniapps/autodiff \
@MFEM_SOURCE_DIR@/miniapps/navier \
@MFEM_SOURCE_DIR@/miniapps/nurbs \
@MFEM_SOURCE_DIR@/miniapps/performance \
+2
View File
@@ -194,6 +194,8 @@ namespace mfem {
* - <a class="el" href="parheat_8cpp_source.html">Optimization gradients</a>: Gradients of PDE-constrained function
* - <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
* - <a class="el" href="par__example_8cpp_source.html">Parallel pLaplacian example using AD</a>: Parallel pLaplacian example
* - <a class="el" href="seq__example_8cpp_source.html">Serial pLaplacian example using AD</a>: Serial pLaplacian example
*
* See also the <a class="el" href="https://mfem.org/examples/">examples documentation</a> online.
*/
+2
View File
@@ -37,6 +37,7 @@ list(APPEND ALL_EXE_SRCS
ex27.cpp
ex28.cpp
ex29.cpp
ex30.cpp
)
if (MFEM_USE_MPI)
@@ -70,6 +71,7 @@ if (MFEM_USE_MPI)
ex27p.cpp
ex28p.cpp
ex29p.cpp
ex30p.cpp
)
endif()
+2 -2
View File
@@ -135,8 +135,8 @@ int main(int argc, char *argv[])
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
if (eta > 0)
{
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
}
a->Assemble();
a->Finalize();
+4 -4
View File
@@ -199,8 +199,8 @@ int main(int argc, char *argv[])
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
if (eta > 0)
{
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(*fespace, eta));
}
a->Assemble();
a->Finalize();
@@ -221,7 +221,7 @@ int main(int argc, char *argv[])
{
HyprePCG pcg(*A);
pcg.SetTol(1e-12);
pcg.SetMaxIter(200);
pcg.SetMaxIter(500);
pcg.SetPrintLevel(2);
pcg.SetPreconditioner(*amg);
pcg.Mult(*B, *X);
@@ -232,7 +232,7 @@ int main(int argc, char *argv[])
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetAbsTol(0.0);
gmres.SetRelTol(1e-12);
gmres.SetMaxIter(200);
gmres.SetMaxIter(500);
gmres.SetKDim(10);
gmres.SetPrintLevel(1);
gmres.SetOperator(*A);
+195
View File
@@ -0,0 +1,195 @@
// MFEM Example 30
//
// Compile with: make ex30
//
// Sample runs: ex30 -m ../data/square-disc.mesh -o 1
// ex30 -m ../data/square-disc.mesh -o 2
// ex30 -m ../data/square-disc.mesh -o 2 -me 1e3
// ex30 -m ../data/square-disc-nurbs.mesh -o 2
// ex30 -m ../data/star.mesh -o 2 -eo 4
// ex30 -m ../data/fichera.mesh -o 2 -me 1e4
// ex30 -m ../data/disc-nurbs.mesh -o 2
// ex30 -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2 -me 1e4
// ex30 -m ../data/star-surf.mesh -o 2
// ex30 -m ../data/square-disc-surf.mesh -o 2
// ex30 -m ../data/amr-quad.mesh -l 2
//
// Description: This is an example of adaptive mesh refinement preprocessing
// which lowers the data oscillation [1] to a user-defined
// relative threshold. There is no PDE being solved.
//
// MFEM's capability to work with both conforming and
// nonconforming meshes is demonstrated in example 6. In some
// problems, the material data or loading data is not sufficiently
// resolved on the initial mesh. This missing fine scale data
// reduces the accuracy of the solution as well as the accuracy
// of some local error estimators. By preprocessing the mesh
// before solving the PDE, many issues can be avoided.
//
// [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000).
// Data oscillation and convergence of adaptive FEM. SIAM
// Journal on Numerical Analysis, 38(2), 466-488.
//
// [2] Mitchell, W. F. (2013). A collection of 2D elliptic
// problems for testing adaptive grid refinement algorithms.
// Applied mathematics and computation, 220, 350-364.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Piecewise-affine function which is sometimes mesh-conforming
double affine_function(const Vector &p)
{
double x = p(0), y = p(1);
if (x < 0.0)
{
return 1.0 + x + y;
}
else
{
return 1.0;
}
}
// Piecewise-constant function which is never mesh-conforming
double jump_function(const Vector &p)
{
if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1.0; }
return 5.0;
}
// Singular function derived from the Laplacian of the "steep wavefront"
// problem in [2].
double singular_function(const Vector &p)
{
double x = p(0), y = p(1);
double alpha = 1000.0;
double xc = 0.75, yc = 0.5;
double r0 = 0.7;
double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0));
double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) );
double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \
- 2 * pow(alpha,2) * r0 * r + 1.0 ),2);
denom = max(denom,1e-8);
return num / denom;
}
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
int order = 1;
int nc_limit = 1;
int max_elems = 1e5;
double double_max_elems = double(max_elems);
bool visualization = true;
double osc_threshold = 1e-3;
int enriched_order = 5;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&nc_limit, "-l", "--nc-limit",
"Maximum level of hanging nodes.");
args.AddOption(&double_max_elems, "-me", "--max-elems",
"Stop after reaching this many elements.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&osc_threshold, "-e", "--error",
"relative data oscillation threshold.");
args.AddOption(&enriched_order, "-eo", "--enriched_order",
"Enriched quadrature order.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
max_elems = int(double_max_elems);
Mesh mesh(mesh_file, 1, 1);
// 2. Since a NURBS mesh can currently only be refined uniformly, we need to
// convert it to a piecewise-polynomial curved mesh. First we refine the
// NURBS mesh a bit more and then project the curvature to quadratic Nodes.
if (mesh.NURBSext)
{
for (int i = 0; i < 2; i++)
{
mesh.UniformRefinement();
}
mesh.SetCurvature(2);
}
// 3. Define functions and refiner.
FunctionCoefficient affine_coeff(affine_function);
FunctionCoefficient jump_coeff(jump_function);
FunctionCoefficient singular_coeff(singular_function);
CoefficientRefiner coeffrefiner(affine_coeff, order);
// 4. Connect to GLVis.
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock;
if (visualization)
{
sol_sock.open(vishost, visport);
}
// 5. Define custom integration rule (optional).
const IntegrationRule *irs[Geometry::NumGeom];
int order_quad = 2*order + enriched_order;
for (int i = 0; i < Geometry::NumGeom; ++i)
{
irs[i] = &(IntRules.Get(i, order_quad));
}
// 6. Apply custom refiner settings.
coeffrefiner.SetIntRule(irs);
coeffrefiner.SetMaxElements(max_elems);
coeffrefiner.SetThreshold(osc_threshold);
coeffrefiner.SetNCLimit(nc_limit);
coeffrefiner.PrintWarnings();
// 7. Preprocess mesh to control osc (piecewise-affine function).
// This is mostly just a verification check. The oscillation should
// be zero if the function is mesh-conforming and order > 0.
coeffrefiner.PreprocessMesh(mesh);
mfem::out << "\n";
mfem::out << "Function 0 (affine) \n";
mfem::out << "Number of Elements " << mesh.GetNE() << "\n";
mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n";
// 8. Preprocess mesh to control osc (jump function).
coeffrefiner.ResetCoefficient(jump_coeff);
coeffrefiner.PreprocessMesh(mesh);
mfem::out << "\n";
mfem::out << "Function 1 (discontinuous) \n";
mfem::out << "Number of Elements " << mesh.GetNE() << "\n";
mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n";
// 9. Preprocess mesh to control osc (singular function).
coeffrefiner.ResetCoefficient(singular_coeff);
coeffrefiner.PreprocessMesh(mesh);
mfem::out << "\n";
mfem::out << "Function 2 (singular) \n";
mfem::out << "Number of Elements " << mesh.GetNE() << "\n";
mfem::out << "Osc error " << coeffrefiner.GetOsc() << "\n";
sol_sock.precision(8);
sol_sock << "mesh\n" << mesh << flush;
return 0;
}
+241
View File
@@ -0,0 +1,241 @@
// MFEM Example 30 - Parallel Version
//
// Compile with: make ex30p
//
// Sample runs: mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 1
// mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 2
// mpirun -np 4 ex30p -m ../data/square-disc.mesh -o 2 -me 1e3
// mpirun -np 4 ex30p -m ../data/square-disc-nurbs.mesh -o 2
// mpirun -np 4 ex30p -m ../data/star.mesh -o 2 -eo 4
// mpirun -np 4 oscp -m ../data/fichera.mesh -o 2 -me 1e4
// mpirun -np 4 ex30p -m ../data/disc-nurbs.mesh -o 2
// mpirun -np 4 ex30p -m ../data/ball-nurbs.mesh -o 2 -eo 3 -e 1e-2
// mpirun -np 4 ex30p -m ../data/star-surf.mesh -o 2
// mpirun -np 4 ex30p -m ../data/square-disc-surf.mesh -o 2
// mpirun -np 4 ex30p -m ../data/amr-quad.mesh -l 2
//
// Description: This is an example of adaptive mesh refinement preprocessing
// which lowers the data oscillation [1] to a user-defined
// relative threshold. There is no PDE being solved.
//
// MFEM's capability to work with both conforming and
// nonconforming meshes is demonstrated in example 6. In some
// problems, the material data or loading data is not sufficiently
// resolved on the initial mesh. This missing fine scale data
// reduces the accuracy of the solution as well as the accuracy
// of some local error estimators. By preprocessing the mesh
// before solving the PDE, many issues can be avoided.
//
// [1] Morin, P., Nochetto, R. H., & Siebert, K. G. (2000).
// Data oscillation and convergence of adaptive FEM. SIAM
// Journal on Numerical Analysis, 38(2), 466-488.
//
// [2] Mitchell, W. F. (2013). A collection of 2D elliptic
// problems for testing adaptive grid refinement algorithms.
// Applied mathematics and computation, 220, 350-364.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Piecewise-affine function which is sometimes mesh-conforming
double affine_function(const Vector &p)
{
double x = p(0), y = p(1);
if (x < 0.0)
{
return 1.0 + x + y;
}
else
{
return 1.0;
}
}
// Piecewise-constant function which is never mesh-conforming
double jump_function(const Vector &p)
{
if (p.Normlp(2.0) > 0.4 && p.Normlp(2.0) < 0.6) { return 1.0; }
return 5.0;
}
// Singular function derived from the Laplacian of the "steep wavefront"
// problem in [2].
double singular_function(const Vector &p)
{
double x = p(0), y = p(1);
double alpha = 1000.0;
double xc = 0.75, yc = 0.5;
double r0 = 0.7;
double r = sqrt(pow(x - xc,2.0) + pow(y - yc,2.0));
double num = - ( alpha - pow(alpha,3) * (pow(r,2) - pow(r0,2)) );
double denom = pow(r * ( pow(alpha,2) * pow(r0,2) + pow(alpha,2) * pow(r,2) \
- 2 * pow(alpha,2) * r0 * r + 1.0 ),2);
denom = max(denom,1e-8);
return num / denom;
}
int main(int argc, char *argv[])
{
// 0. 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);
// 1. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
int order = 1;
int nc_limit = 1;
int max_elems = 1e5;
double double_max_elems = double(max_elems);
bool visualization = true;
bool nc_simplices = true;
double osc_threshold = 1e-3;
int enriched_order = 5;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&nc_limit, "-l", "--nc-limit",
"Maximum level of hanging nodes.");
args.AddOption(&double_max_elems, "-me", "--max-elems",
"Stop after reaching this many elements.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&osc_threshold, "-e", "--error",
"relative data oscillation threshold.");
args.AddOption(&enriched_order, "-eo", "--enriched_order",
"Enriched quadrature order.");
args.AddOption(&nc_simplices, "-ns", "--nonconforming-simplices",
"-cs", "--conforming-simplices",
"For simplicial meshes, enable/disable nonconforming"
" refinement");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
max_elems = int(double_max_elems);
Mesh mesh(mesh_file, 1, 1);
// 2. Since a NURBS mesh can currently only be refined uniformly, we need to
// convert it to a piecewise-polynomial curved mesh. First we refine the
// NURBS mesh a bit more and then project the curvature to quadratic Nodes.
if (mesh.NURBSext)
{
for (int i = 0; i < 2; i++)
{
mesh.UniformRefinement();
}
mesh.SetCurvature(2);
}
// 3. Make sure the mesh is in the non-conforming mode to enable local
// refinement of quadrilaterals/hexahedra. Simplices can be refined
// either in conforming or in non-conforming mode. The conforming
// mode however does not support dynamic partitioning.
mesh.EnsureNCMesh(nc_simplices);
// 4. Define a parallel mesh by partitioning the serial mesh.
// Once the parallel mesh is defined, the serial mesh can be deleted.
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
// 5. Define functions and refiner.
FunctionCoefficient affine_coeff(affine_function);
FunctionCoefficient jump_coeff(jump_function);
FunctionCoefficient singular_coeff(singular_function);
CoefficientRefiner coeffrefiner(affine_coeff,order);
// 6. Connect to GLVis.
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock;
if (visualization)
{
sol_sock.open(vishost, visport);
}
// 7. Define custom integration rule (optional).
const IntegrationRule *irs[Geometry::NumGeom];
int order_quad = 2*order + enriched_order;
for (int i=0; i < Geometry::NumGeom; ++i)
{
irs[i] = &(IntRules.Get(i, order_quad));
}
// 8. Apply custom refiner settings.
coeffrefiner.SetIntRule(irs);
coeffrefiner.SetMaxElements(max_elems);
coeffrefiner.SetThreshold(osc_threshold);
coeffrefiner.SetNCLimit(nc_limit);
coeffrefiner.PrintWarnings();
// 9. Preprocess mesh to control osc (piecewise-affine function).
// This is mostly just a verification check. The oscillation should
// be zero if the function is mesh-conforming and order > 0.
coeffrefiner.PreprocessMesh(pmesh);
int globalNE = pmesh.GetGlobalNE();
double osc = coeffrefiner.GetOsc();
if (myid == 0)
{
mfem::out << "\n";
mfem::out << "Function 0 (affine) \n";
mfem::out << "Number of Elements " << globalNE << "\n";
mfem::out << "Osc error " << osc << "\n";
}
// 10. Preprocess mesh to control osc (jump function).
coeffrefiner.ResetCoefficient(jump_coeff);
coeffrefiner.PreprocessMesh(pmesh);
globalNE = pmesh.GetGlobalNE();
osc = coeffrefiner.GetOsc();
if (myid == 0)
{
mfem::out << "\n";
mfem::out << "Function 1 (discontinuous) \n";
mfem::out << "Number of Elements " << globalNE << "\n";
mfem::out << "Osc error " << osc << "\n";
}
// 11. Preprocess mesh to control osc (singular function).
coeffrefiner.ResetCoefficient(singular_coeff);
coeffrefiner.PreprocessMesh(pmesh);
globalNE = pmesh.GetGlobalNE();
osc = coeffrefiner.GetOsc();
if (myid == 0)
{
mfem::out << "\n";
mfem::out << "Function 2 (singular) \n";
mfem::out << "Number of Elements " << globalNE << "\n";
mfem::out << "Osc error " << osc << "\n";
}
sol_sock.precision(8);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock << "mesh\n" << pmesh << flush;
MPI_Finalize();
return 0;
}
+2 -2
View File
@@ -22,10 +22,10 @@ MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 ex30
PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \
ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \
ex25p ex26p ex27p ex28p ex29p
ex25p ex26p ex27p ex28p ex29p ex30p
SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex22 ex24 ex25 ex26
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex22p \
ex24p ex25p ex26p
+1
View File
@@ -969,6 +969,7 @@ void BilinearForm::EliminateVDofs(const Array<int> &vdofs,
const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy)
{
vdofs.HostRead();
for (int i = 0; i < vdofs.Size(); i++)
{
int vdof = vdofs[i];
+75 -31
View File
@@ -985,6 +985,8 @@ void DiffusionIntegrator::ComputeElementFlux
"Unexpected height for MatrixCoefficient");
}
MFEM_VERIFY(!SMQ, "SymmetricMatrixCoefficient not supported here");
#ifdef MFEM_THREAD_SAFE
DenseMatrix dshape(nd,dim), invdfdx(dim, spaceDim);
DenseMatrix M(MQ ? spaceDim : 0);
@@ -997,7 +999,7 @@ void DiffusionIntegrator::ComputeElementFlux
#endif
vec.SetSize(dim);
vecdxt.SetSize(spaceDim);
pointflux.SetSize(MQ ? spaceDim : 0);
pointflux.SetSize(MQ || VQ ? spaceDim : 0);
const IntegrationRule &ir = fluxelem.GetNodes();
fnd = ir.GetNPoints();
@@ -1013,36 +1015,45 @@ void DiffusionIntegrator::ComputeElementFlux
CalcInverse(Trans.Jacobian(), invdfdx);
invdfdx.MultTranspose(vec, vecdxt);
if (!MQ && !VQ)
if (with_coef)
{
if (Q && with_coef)
if (!MQ && !VQ)
{
vecdxt *= Q->Eval(Trans,ip);
if (Q)
{
vecdxt *= Q->Eval(Trans,ip);
}
for (j = 0; j < spaceDim; j++)
{
flux(fnd*j+i) = vecdxt(j);
}
}
for (j = 0; j < spaceDim; j++)
else
{
flux(fnd*j+i) = vecdxt(j);
if (MQ)
{
MQ->Eval(M, Trans, ip);
M.Mult(vecdxt, pointflux);
}
else
{
VQ->Eval(D, Trans, ip);
for (int j=0; j<spaceDim; ++j)
{
pointflux[j] = D[j] * vecdxt[j];
}
}
for (j = 0; j < spaceDim; j++)
{
flux(fnd*j+i) = pointflux(j);
}
}
}
else
{
if (MQ)
{
MQ->Eval(M, Trans, ip);
M.Mult(vecdxt, pointflux);
}
else
{
VQ->Eval(D, Trans, ip);
for (int j=0; j<spaceDim; ++j)
{
pointflux[j] = D[j] * vecdxt[j];
}
}
for (j = 0; j < spaceDim; j++)
{
flux(fnd*j+i) = pointflux(j);
flux(fnd*j+i) = vecdxt(j);
}
}
}
@@ -1050,7 +1061,7 @@ void DiffusionIntegrator::ComputeElementFlux
double DiffusionIntegrator::ComputeFluxEnergy
( const FiniteElement &fluxelem, ElementTransformation &Trans,
Vector &flux, Vector* d_energy)
Vector &flux, bool with_coef, Vector* d_energy)
{
int nd = fluxelem.GetDof();
int dim = fluxelem.GetDim();
@@ -1058,8 +1069,13 @@ double DiffusionIntegrator::ComputeFluxEnergy
#ifdef MFEM_THREAD_SAFE
DenseMatrix M;
Vector D(VQ ? VQ->GetVDim() : 0);
#else
D.SetSize(VQ ? VQ->GetVDim() : 0);
#endif
MFEM_VERIFY(!SMQ, "SymmetricMatrixCoefficient not supported here");
shape.SetSize(nd);
pointflux.SetSize(spaceDim);
if (d_energy) { vec.SetSize(spaceDim); }
@@ -1088,16 +1104,42 @@ double DiffusionIntegrator::ComputeFluxEnergy
Trans.SetIntPoint(&ip);
double w = Trans.Weight() * ip.weight;
if (!MQ)
if (MQ)
{
double e = (pointflux * pointflux);
if (Q) { e *= Q->Eval(Trans, ip); }
energy += w * e;
MQ->Eval(M, Trans, ip);
if (with_coef) { M.Invert(); }
energy += w * M.InnerProduct(pointflux, pointflux);
}
else if (VQ)
{
VQ->Eval(D, Trans, ip);
if (with_coef)
{
Vector Dinv(D.Size());
Dinv = 1.0;
Dinv /= D;
D = Dinv;
}
D *= pointflux;
energy += w * (D * pointflux);
}
else
{
MQ->Eval(M, Trans, ip);
energy += w * M.InnerProduct(pointflux, pointflux);
double e = (pointflux * pointflux);
if (Q)
{
if (with_coef)
{
e /= Q->Eval(Trans, ip);
}
else
{
e *= Q->Eval(Trans, ip);
}
}
energy += w * e;
}
if (d_energy)
@@ -1108,7 +1150,7 @@ double DiffusionIntegrator::ComputeFluxEnergy
{
(*d_energy)[k] += w * vec[k] * vec[k];
}
// TODO: Q, MQ
// TODO: Q, VQ, MQ
}
}
@@ -1936,7 +1978,8 @@ void CurlCurlIntegrator
double CurlCurlIntegrator::ComputeFluxEnergy(const FiniteElement &fluxelem,
ElementTransformation &Trans,
Vector &flux, Vector *d_energy)
Vector &flux, bool with_coef,
Vector *d_energy)
{
int nd = fluxelem.GetDof();
int dim = fluxelem.GetDim();
@@ -2829,7 +2872,8 @@ void ElasticityIntegrator::ComputeElementFlux(
double ElasticityIntegrator::ComputeFluxEnergy(const FiniteElement &fluxelem,
ElementTransformation &Trans,
Vector &flux, Vector *d_energy)
Vector &flux, bool with_coef,
Vector *d_energy)
{
const int dof = fluxelem.GetDof();
const int dim = fluxelem.GetDim();
+30 -9
View File
@@ -234,6 +234,8 @@ public:
position of the mesh element.
@param[in] flux "Flux" coefficients representing the expansion of the
"flux" function in the basis of @a fluxelem.
@param[in] wcoef If true, @a flux includes the coefficient of this
integrator.
@param[out] d_energy If not NULL, the given Vector should be set to
represent directional energy split that can be used
for anisotropic error estimation.
@@ -241,7 +243,8 @@ public:
*/
virtual double ComputeFluxEnergy(const FiniteElement &fluxelem,
ElementTransformation &Trans,
Vector &flux, Vector *d_energy = NULL)
Vector &flux, bool wcoef = true,
Vector *d_energy = NULL)
{ return 0.0; }
virtual ~BilinearFormIntegrator() { }
@@ -2033,7 +2036,8 @@ public:
virtual double ComputeFluxEnergy(const FiniteElement &fluxelem,
ElementTransformation &Trans,
Vector &flux, Vector *d_energy = NULL);
Vector &flux, bool wcoef = true,
Vector *d_energy = NULL);
using BilinearFormIntegrator::AssemblePA;
@@ -2452,7 +2456,8 @@ public:
virtual double ComputeFluxEnergy(const FiniteElement &fluxelem,
ElementTransformation &Trans,
Vector &flux, Vector *d_energy = NULL);
Vector &flux, bool wcoef = true,
Vector *d_energy = NULL);
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace &fes);
@@ -2785,7 +2790,8 @@ public:
s_yz in 3D. */
virtual double ComputeFluxEnergy(const FiniteElement &fluxelem,
ElementTransformation &Trans,
Vector &flux, Vector *d_energy = NULL);
Vector &flux, bool wcoef = true,
Vector *d_energy = NULL);
};
/** Integrator for the DG form:
@@ -2938,10 +2944,11 @@ public:
sum_e eta (r_e([u]), r_e([v]))
where r_e is the lifting operator defined on each edge e. The parameter eta
can be chosen to be one to obtain a stable discretization. The constructor
for this integrator requires the finite element space because the lifting
operator depends on the element-wise inverse mass matrix.
where r_e is the lifting operator defined on each edge e (potentially
weighted by a coefficient Q). The parameter eta can be chosen to be one to
obtain a stable discretization. The constructor for this integrator requires
the finite element space because the lifting operator depends on the
element-wise inverse mass matrix.
BR2 stands for the second method of Bassi and Rebay:
@@ -2964,14 +2971,28 @@ protected:
Array<int> ipiv;
Array<int> ipiv_offsets, Minv_offsets;
Coefficient *Q;
Vector shape1, shape2;
DenseMatrix R11, R12, R21, R22;
DenseMatrix MinvR11, MinvR12, MinvR21, MinvR22;
DenseMatrix Re, MinvRe;
/// Precomputes the inverses (LU factorizations) of the local mass matrices.
/** @a fes must be a DG space, so the mass matrix is block diagonal, and its
inverse can be computed locally. This is required for the computation of
the lifting operators @a r_e.
*/
void PrecomputeMassInverse(class FiniteElementSpace &fes);
public:
DGDiffusionBR2Integrator(class FiniteElementSpace *fes, double e = 1.0);
DGDiffusionBR2Integrator(class FiniteElementSpace &fes, double e = 1.0);
DGDiffusionBR2Integrator(class FiniteElementSpace &fes, Coefficient &Q_,
double e = 1.0);
MFEM_DEPRECATED DGDiffusionBR2Integrator(class FiniteElementSpace *fes,
double e = 1.0);
using BilinearFormIntegrator::AssembleFaceMatrix;
virtual void AssembleFaceMatrix(const FiniteElement &el1,
const FiniteElement &el2,
+40 -18
View File
@@ -16,20 +16,39 @@
namespace mfem
{
DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(FiniteElementSpace *fes,
double e) : eta(e)
DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(
FiniteElementSpace &fes, double e) : eta(e), Q(NULL)
{
PrecomputeMassInverse(fes);
}
DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(
FiniteElementSpace &fes, Coefficient &Q_, double e) : eta(e), Q(&Q_)
{
PrecomputeMassInverse(fes);
}
DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(
FiniteElementSpace *fes, double e) : eta(e), Q(NULL)
{
PrecomputeMassInverse(*fes);
}
void DGDiffusionBR2Integrator::PrecomputeMassInverse(FiniteElementSpace &fes)
{
MFEM_VERIFY(fes.IsDGSpace(),
"The BR2 integrator is only defined for DG spaces.");
// Precompute local mass matrix inverses needed for the lifting operators
// First compute offsets and total size needed (e.g. for mixed meshes or
// p-refinement)
int nel = fes->GetNE();
int nel = fes.GetNE();
Minv_offsets.SetSize(nel+1);
ipiv_offsets.SetSize(nel+1);
ipiv_offsets[0] = 0;
Minv_offsets[0] = 0;
for (int i=0; i<nel; ++i)
{
int dof = fes->GetFE(i)->GetDof();
int dof = fes.GetFE(i)->GetDof();
ipiv_offsets[i+1] = ipiv_offsets[i] + dof;
Minv_offsets[i+1] = Minv_offsets[i] + dof*dof;
}
@@ -37,7 +56,7 @@ DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(FiniteElementSpace *fes,
#ifdef MFEM_USE_MPI
// When running in parallel, we also need to compute the local mass matrices
// of face neighbor elements
ParFiniteElementSpace *pfes = dynamic_cast<ParFiniteElementSpace *>(fes);
ParFiniteElementSpace *pfes = dynamic_cast<ParFiniteElementSpace *>(&fes);
if (pfes != NULL)
{
ParMesh *pmesh = pfes->GetParMesh();
@@ -64,15 +83,15 @@ DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(FiniteElementSpace *fes,
{
const FiniteElement *fe = NULL;
ElementTransformation *tr = NULL;
if (i < fes->GetNE())
if (i < fes.GetNE())
{
fe = fes->GetFE(i);
tr = fes->GetElementTransformation(i);
fe = fes.GetFE(i);
tr = fes.GetElementTransformation(i);
}
else
{
#ifdef MFEM_USE_MPI
int inbr = i - fes->GetNE();
int inbr = i - fes.GetNE();
fe = pfes->GetFaceNbrFE(inbr);
tr = pfes->GetParMesh()->GetFaceNbrElementTransformation(inbr);
#endif
@@ -151,21 +170,24 @@ void DGDiffusionBR2Integrator::AssembleFaceMatrix(
for (int p = 0; p < ir->GetNPoints(); p++)
{
const IntegrationPoint &ip = ir->IntPoint(p);
IntegrationPoint eip1, eip2;
Trans.SetAllIntPoints(&ip);
Trans.Loc1.Transform(ip, eip1);
const IntegrationPoint &eip1 = Trans.Elem1->GetIntPoint();
el1.CalcShape(eip1, shape1);
double q = Q ? Q->Eval(*Trans.Elem1, eip1) : 1.0;
if (ndof2)
{
Trans.Loc2.Transform(ip, eip2);
const IntegrationPoint &eip2 = Trans.Elem2->GetIntPoint();
el2.CalcShape(eip2, shape2);
// Set coefficient value q to the average of the values on either side
if (Q) { q = 0.5*(q + Q->Eval(*Trans.Elem2, eip2)); }
}
double w = factor*sqrt(eta)*ip.weight*Trans.Face->Weight();
if (ndof2)
{
w /= 2;
}
// Take sqrt here because
// eta (r_e([u]), r_e([v])) = (sqrt(eta) r_e([u]), sqrt(eta) r_e([v]))
double w = sqrt((factor + 1)*eta*q)*ip.weight*Trans.Face->Weight();
// r_e is defined by, (r_e([u]), tau) = <[u], {tau}>, so we pick up a
// factor of 0.5 on interior faces from the average term.
if (ndof2) { w *= 0.5; }
for (int i = 0; i < ndof1; i++)
{
+152 -183
View File
@@ -903,9 +903,11 @@ static void PADiffusionAssembleDiagonal(const int dim,
{
switch ((D1D << 4 ) | Q1D)
{
case 0x22: return SmemPADiffusionDiagonal3D<2,2>(NE,symm,B,G,D,Y);
case 0x23: return SmemPADiffusionDiagonal3D<2,3>(NE,symm,B,G,D,Y);
case 0x34: return SmemPADiffusionDiagonal3D<3,4>(NE,symm,B,G,D,Y);
case 0x45: return SmemPADiffusionDiagonal3D<4,5>(NE,symm,B,G,D,Y);
case 0x46: return SmemPADiffusionDiagonal3D<4,6>(NE,symm,B,G,D,Y);
case 0x56: return SmemPADiffusionDiagonal3D<5,6>(NE,symm,B,G,D,Y);
case 0x67: return SmemPADiffusionDiagonal3D<6,7>(NE,symm,B,G,D,Y);
case 0x78: return SmemPADiffusionDiagonal3D<7,8>(NE,symm,B,G,D,Y);
@@ -1554,7 +1556,7 @@ static void SmemPADiffusionApply3D(const int NE,
auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, Q1D, Q1D, 1,
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -1583,118 +1585,102 @@ static void SmemPADiffusionApply3D(const int NE,
double (*QDD0)[MD1][MD1] = (double (*)[MD1][MD1]) (sm0+0);
double (*QDD1)[MD1][MD1] = (double (*)[MD1][MD1]) (sm0+1);
double (*QDD2)[MD1][MD1] = (double (*)[MD1][MD1]) (sm0+2);
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
MFEM_FOREACH_THREAD(dx,x,D1D)
{
X[dz][dy][dx] = x(dx,dy,dz,e);
}
}
MFEM_FOREACH_THREAD(qx,x,Q1D)
}
if (MFEM_THREAD_ID(z) == 0)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
const int i = qi(qx,dy,Q1D);
const int j = dj(qx,dy,D1D);
const int k = qk(qx,dy,Q1D);
const int l = dl(qx,dy,D1D);
B[i][j] = b(qx,dy);
G[k][l] = g(qx,dy) * sign(qx,dy);
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
const int i = qi(qx,dy,Q1D);
const int j = dj(qx,dy,D1D);
const int k = qk(qx,dy,Q1D);
const int l = dl(qx,dy,D1D);
B[i][j] = b(qx,dy);
G[k][l] = g(qx,dy) * sign(qx,dy);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
double u[D1D], v[D1D];
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; dz++) { u[dz] = v[dz] = 0.0; }
MFEM_UNROLL(MD1)
for (int dx = 0; dx < D1D; ++dx)
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
const int i = qi(qx,dx,Q1D);
const int j = dj(qx,dx,D1D);
const int k = qk(qx,dx,Q1D);
const int l = dl(qx,dx,D1D);
const double s = sign(qx,dx);
double u = 0.0, v = 0.0;
MFEM_UNROLL(MD1)
for (int dx = 0; dx < D1D; ++dx)
{
const int i = qi(qx,dx,Q1D);
const int j = dj(qx,dx,D1D);
const int k = qk(qx,dx,Q1D);
const int l = dl(qx,dx,D1D);
const double s = sign(qx,dx);
const double coords = X[dz][dy][dx];
u += coords * B[i][j];
v += coords * G[k][l] * s;
}
DDQ0[dz][dy][qx] = u;
DDQ1[dz][dy][qx] = v;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
double u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MD1)
for (int dy = 0; dy < D1D; ++dy)
{
const int i = qi(qy,dy,Q1D);
const int j = dj(qy,dy,D1D);
const int k = qk(qy,dy,Q1D);
const int l = dl(qy,dy,D1D);
const double s = sign(qy,dy);
u += DDQ1[dz][dy][qx] * B[i][j];
v += DDQ0[dz][dy][qx] * G[k][l] * s;
w += DDQ0[dz][dy][qx] * B[i][j];
}
DQQ0[dz][qy][qx] = u;
DQQ1[dz][qy][qx] = v;
DQQ2[dz][qy][qx] = w;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
double u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
{
const double coords = X[dz][dy][dx];
u[dz] += coords * B[i][j];
v[dz] += coords * G[k][l] * s;
}
}
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
{
DDQ0[dz][dy][qx] = u[dz];
DDQ1[dz][dy][qx] = v[dz];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
double u[D1D], v[D1D], w[D1D];
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; dz++) { u[dz] = v[dz] = w[dz] = 0.0; }
MFEM_UNROLL(MD1)
for (int dy = 0; dy < D1D; ++dy)
{
const int i = qi(qy,dy,Q1D);
const int j = dj(qy,dy,D1D);
const int k = qk(qy,dy,Q1D);
const int l = dl(qy,dy,D1D);
const double s = sign(qy,dy);
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; dz++)
{
u[dz] += DDQ1[dz][dy][qx] * B[i][j];
v[dz] += DDQ0[dz][dy][qx] * G[k][l] * s;
w[dz] += DDQ0[dz][dy][qx] * B[i][j];
}
}
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; dz++)
{
DQQ0[dz][qy][qx] = u[dz];
DQQ1[dz][qy][qx] = v[dz];
DQQ2[dz][qy][qx] = w[dz];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
double u[Q1D], v[Q1D], w[Q1D];
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; qz++) { u[qz] = v[qz] = w[qz] = 0.0; }
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
{
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; qz++)
{
const int i = qi(qz,dz,Q1D);
const int j = dj(qz,dz,D1D);
const int k = qk(qz,dz,Q1D);
const int l = dl(qz,dz,D1D);
const double s = sign(qz,dz);
u[qz] += DQQ0[dz][qy][qx] * B[i][j];
v[qz] += DQQ1[dz][qy][qx] * B[i][j];
w[qz] += DQQ2[dz][qy][qx] * G[k][l] * s;
u += DQQ0[dz][qy][qx] * B[i][j];
v += DQQ1[dz][qy][qx] * B[i][j];
w += DQQ2[dz][qy][qx] * G[k][l] * s;
}
}
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; qz++)
{
const double O11 = d(qx,qy,qz,0,e);
const double O12 = d(qx,qy,qz,1,e);
const double O13 = d(qx,qy,qz,2,e);
@@ -1704,9 +1690,9 @@ static void SmemPADiffusionApply3D(const int NE,
const double O31 = symmetric ? O13 : d(qx,qy,qz,6,e);
const double O32 = symmetric ? O23 : d(qx,qy,qz,7,e);
const double O33 = symmetric ? d(qx,qy,qz,5,e) : d(qx,qy,qz,8,e);
const double gX = u[qz];
const double gY = v[qz];
const double gZ = w[qz];
const double gX = u;
const double gY = v;
const double gZ = w;
QQQ0[qz][qy][qx] = (O11*gX) + (O12*gY) + (O13*gZ);
QQQ1[qz][qy][qx] = (O21*gX) + (O22*gY) + (O23*gZ);
QQQ2[qz][qy][qx] = (O31*gX) + (O32*gY) + (O33*gZ);
@@ -1714,112 +1700,94 @@ static void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(d,y,D1D)
if (MFEM_THREAD_ID(z) == 0)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
MFEM_FOREACH_THREAD(d,y,D1D)
{
const int i = qi(q,d,Q1D);
const int j = dj(q,d,D1D);
const int k = qk(q,d,Q1D);
const int l = dl(q,d,D1D);
Bt[j][i] = b(q,d);
Gt[l][k] = g(q,d) * sign(q,d);
MFEM_FOREACH_THREAD(q,x,Q1D)
{
const int i = qi(q,d,Q1D);
const int j = dj(q,d,D1D);
const int k = qk(q,d,Q1D);
const int l = dl(q,d,D1D);
Bt[j][i] = b(q,d);
Gt[l][k] = g(q,d) * sign(q,d);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
double u[Q1D], v[Q1D], w[Q1D];
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz) { u[qz] = v[qz] = w[qz] = 0.0; }
MFEM_UNROLL(MQ1)
for (int qx = 0; qx < Q1D; ++qx)
MFEM_FOREACH_THREAD(dx,x,D1D)
{
const int i = qi(qx,dx,Q1D);
const int j = dj(qx,dx,D1D);
const int k = qk(qx,dx,Q1D);
const int l = dl(qx,dx,D1D);
const double s = sign(qx,dx);
double u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MQ1)
for (int qx = 0; qx < Q1D; ++qx)
{
const int i = qi(qx,dx,Q1D);
const int j = dj(qx,dx,D1D);
const int k = qk(qx,dx,Q1D);
const int l = dl(qx,dx,D1D);
const double s = sign(qx,dx);
u += QQQ0[qz][qy][qx] * Gt[l][k] * s;
v += QQQ1[qz][qy][qx] * Bt[j][i];
w += QQQ2[qz][qy][qx] * Bt[j][i];
}
QQD0[qz][qy][dx] = u;
QQD1[qz][qy][dx] = v;
QQD2[qz][qy][dx] = w;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(Q1D)
for (int qy = 0; qy < Q1D; ++qy)
{
const int i = qi(qy,dy,Q1D);
const int j = dj(qy,dy,D1D);
const int k = qk(qy,dy,Q1D);
const int l = dl(qy,dy,D1D);
const double s = sign(qy,dy);
u += QQD0[qz][qy][dx] * Bt[j][i];
v += QQD1[qz][qy][dx] * Gt[l][k] * s;
w += QQD2[qz][qy][dx] * Bt[j][i];
}
QDD0[qz][dy][dx] = u;
QDD1[qz][dy][dx] = v;
QDD2[qz][dy][dx] = w;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz)
{
u[qz] += QQQ0[qz][qy][qx] * Gt[l][k] * s;
v[qz] += QQQ1[qz][qy][qx] * Bt[j][i];
w[qz] += QQQ2[qz][qy][qx] * Bt[j][i];
}
}
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz)
{
QQD0[qz][qy][dx] = u[qz];
QQD1[qz][qy][dx] = v[qz];
QQD2[qz][qy][dx] = w[qz];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double u[Q1D], v[Q1D], w[Q1D];
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz) { u[qz] = v[qz] = w[qz] = 0.0; }
MFEM_UNROLL(MQ1)
for (int qy = 0; qy < Q1D; ++qy)
{
const int i = qi(qy,dy,Q1D);
const int j = dj(qy,dy,D1D);
const int k = qk(qy,dy,Q1D);
const int l = dl(qy,dy,D1D);
const double s = sign(qy,dy);
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz)
{
u[qz] += QQD0[qz][qy][dx] * Bt[j][i];
v[qz] += QQD1[qz][qy][dx] * Gt[l][k] * s;
w[qz] += QQD2[qz][qy][dx] * Bt[j][i];
}
}
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz)
{
QDD0[qz][dy][dx] = u[qz];
QDD1[qz][dy][dx] = v[qz];
QDD2[qz][dy][dx] = w[qz];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double u[D1D], v[D1D], w[D1D];
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz) { u[dz] = v[dz] = w[dz] = 0.0; }
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz)
{
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
{
const int i = qi(qz,dz,Q1D);
const int j = dj(qz,dz,D1D);
const int k = qk(qz,dz,Q1D);
const int l = dl(qz,dz,D1D);
const double s = sign(qz,dz);
u[dz] += QDD0[qz][dy][dx] * Bt[j][i];
v[dz] += QDD1[qz][dy][dx] * Bt[j][i];
w[dz] += QDD2[qz][dy][dx] * Gt[l][k] * s;
u += QDD0[qz][dy][dx] * Bt[j][i];
v += QDD1[qz][dy][dx] * Bt[j][i];
w += QDD2[qz][dy][dx] * Gt[l][k] * s;
}
}
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
{
y(dx,dy,dz,e) += (u[dz] + v[dz] + w[dz]);
y(dx,dy,dz,e) += (u + v + w);
}
}
}
@@ -1877,6 +1845,7 @@ static void PADiffusionApply(const int dim,
{
switch (ID)
{
case 0x22: return SmemPADiffusionApply3D<2,2>(NE,symm,B,G,D,X,Y);
case 0x23: return SmemPADiffusionApply3D<2,3>(NE,symm,B,G,D,X,Y);
case 0x34: return SmemPADiffusionApply3D<3,4>(NE,symm,B,G,D,X,Y);
case 0x45: return SmemPADiffusionApply3D<4,5>(NE,symm,B,G,D,X,Y);
+2
View File
@@ -1203,8 +1203,10 @@ static void PAMassApply(const int dim,
{
switch (id)
{
case 0x22: return SmemPAMassApply3D<2,2>(NE,B,Bt,D,X,Y);
case 0x23: return SmemPAMassApply3D<2,3>(NE,B,Bt,D,X,Y);
case 0x24: return SmemPAMassApply3D<2,4>(NE,B,Bt,D,X,Y);
case 0x26: return SmemPAMassApply3D<2,6>(NE,B,Bt,D,X,Y);
case 0x34: return SmemPAMassApply3D<3,4>(NE,B,Bt,D,X,Y);
case 0x35: return SmemPAMassApply3D<3,5>(NE,B,Bt,D,X,Y);
case 0x36: return SmemPAMassApply3D<3,6>(NE,B,Bt,D,X,Y);
+7 -3
View File
@@ -186,14 +186,18 @@ static void InitTensorBasis(const mfem::FiniteElementSpace &fes,
const int ndofs = maps.ndof;
const int nqpts = maps.nqpt;
mfem::Vector qX(nqpts), qW(nqpts);
const mfem::IntegrationRule &ir1d =
IntRules.Get(Geometry::SEGMENT, ir.GetOrder());
// The x-coordinates of the first `nqpts` points of the integration rule are
// the points of the corresponding 1D rule. We also scale the weights
// accordingly.
double w_sum = 0.0;
for (int i = 0; i < nqpts; i++)
{
const mfem::IntegrationPoint &ip = ir1d.IntPoint(i);
const mfem::IntegrationPoint &ip = ir.IntPoint(i);
qX(i) = ip.x;
qW(i) = ip.weight;
w_sum += ip.weight;
}
qW *= 1.0/w_sum;
CeedBasisCreateTensorH1(ceed, mesh->Dimension(), fes.GetVDim(), ndofs,
nqpts, maps.Bt.GetData(),
maps.Gt.GetData(), qX.GetData(),
+190
View File
@@ -52,6 +52,13 @@ double GridFunctionCoefficient::Eval (ElementTransformation &T,
return GridF -> GetValue (T, ip, Component);
}
void TransformedCoefficient::SetTime(double t)
{
if (Q1) { Q1->SetTime(t); }
if (Q2) { Q2->SetTime(t); }
this->Coefficient::SetTime(t);
}
double TransformedCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -66,6 +73,12 @@ double TransformedCoefficient::Eval(ElementTransformation &T,
}
}
void DeltaCoefficient::SetTime(double t)
{
if (weight) { weight->SetTime(t); }
this->Coefficient::SetTime(t);
}
void DeltaCoefficient::SetDeltaCenter(const Vector& vcenter)
{
MFEM_VERIFY(vcenter.Size() <= 3,
@@ -87,6 +100,12 @@ double DeltaCoefficient::EvalDelta(ElementTransformation &T,
return weight ? weight->Eval(T, ip, GetTime())*w : w;
}
void RestrictedCoefficient::SetTime(double t)
{
if (c) { c->SetTime(t); }
this->Coefficient::SetTime(t);
}
void VectorCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationRule &ir)
{
@@ -134,6 +153,15 @@ VectorArrayCoefficient::VectorArrayCoefficient (int dim)
}
}
void VectorArrayCoefficient::SetTime(double t)
{
for (int i = 0; i < vdim; i++)
{
if (Coeff[i]) { Coeff[i]->SetTime(t); }
}
this->VectorCoefficient::SetTime(t);
}
void VectorArrayCoefficient::Set(int i, Coefficient *c, bool own)
{
if (ownCoeff[i]) { delete Coeff[i]; }
@@ -247,6 +275,12 @@ double DivergenceGridFunctionCoefficient::Eval(ElementTransformation &T,
return GridFunc->GetDivergence(T);
}
void VectorDeltaCoefficient::SetTime(double t)
{
d.SetTime(t);
this->VectorCoefficient::SetTime(t);
}
void VectorDeltaCoefficient::SetDirection(const Vector &d_)
{
dir = d_;
@@ -261,6 +295,12 @@ void VectorDeltaCoefficient::EvalDelta(
V *= d.EvalDelta(T, ip);
}
void VectorRestrictedCoefficient::SetTime(double t)
{
if (c) { c->SetTime(t); }
this->VectorCoefficient::SetTime(t);
}
void VectorRestrictedCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -291,6 +331,12 @@ void VectorRestrictedCoefficient::Eval(
}
}
void MatrixFunctionCoefficient::SetTime(double t)
{
if (Q) { Q->SetTime(t); }
this->MatrixCoefficient::SetTime(t);
}
void MatrixFunctionCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -371,6 +417,12 @@ void MatrixFunctionCoefficient::EvalSymmetric(Vector &K,
}
}
void SymmetricMatrixFunctionCoefficient::SetTime(double t)
{
if (Q) { Q->SetTime(t); }
this->SymmetricMatrixCoefficient::SetTime(t);
}
void SymmetricMatrixFunctionCoefficient::Eval(DenseSymmetricMatrix &K,
ElementTransformation &T,
const IntegrationPoint &ip)
@@ -413,6 +465,15 @@ MatrixArrayCoefficient::MatrixArrayCoefficient (int dim)
}
}
void MatrixArrayCoefficient::SetTime(double t)
{
for (int i=0; i < height*width; i++)
{
if (Coeff[i]) { Coeff[i]->SetTime(t); }
}
this->MatrixCoefficient::SetTime(t);
}
void MatrixArrayCoefficient::Set(int i, int j, Coefficient * c, bool own)
{
if (ownCoeff[i*width+j]) { delete Coeff[i*width+j]; }
@@ -441,6 +502,12 @@ void MatrixArrayCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
}
}
void MatrixRestrictedCoefficient::SetTime(double t)
{
if (c) { c->SetTime(t); }
this->MatrixCoefficient::SetTime(t);
}
void MatrixRestrictedCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -456,6 +523,33 @@ void MatrixRestrictedCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
}
}
void SumCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
if (b) { b->SetTime(t); }
this->Coefficient::SetTime(t);
}
void ProductCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
if (b) { b->SetTime(t); }
this->Coefficient::SetTime(t);
}
void RatioCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
if (b) { b->SetTime(t); }
this->Coefficient::SetTime(t);
}
void PowerCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
this->Coefficient::SetTime(t);
}
InnerProductCoefficient::InnerProductCoefficient(VectorCoefficient &A,
VectorCoefficient &B)
: a(&A), b(&B)
@@ -465,6 +559,13 @@ InnerProductCoefficient::InnerProductCoefficient(VectorCoefficient &A,
"Arguments have incompatible dimensions.");
}
void InnerProductCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
if (b) { b->SetTime(t); }
this->Coefficient::SetTime(t);
}
double InnerProductCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -482,6 +583,13 @@ VectorRotProductCoefficient::VectorRotProductCoefficient(VectorCoefficient &A,
"Arguments must have dimension equal to two.");
}
void VectorRotProductCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
if (b) { b->SetTime(t); }
this->Coefficient::SetTime(t);
}
double VectorRotProductCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -498,6 +606,12 @@ DeterminantCoefficient::DeterminantCoefficient(MatrixCoefficient &A)
"Argument must be a square matrix.");
}
void DeterminantCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
this->Coefficient::SetTime(t);
}
double DeterminantCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -546,6 +660,15 @@ VectorSumCoefficient::VectorSumCoefficient(VectorCoefficient &A_,
"Arguments must have the same dimension.");
}
void VectorSumCoefficient::SetTime(double t)
{
if (ACoef) { ACoef->SetTime(t); }
if (BCoef) { BCoef->SetTime(t); }
if (alphaCoef) { alphaCoef->SetTime(t); }
if (betaCoef) { betaCoef->SetTime(t); }
this->VectorCoefficient::SetTime(t);
}
void VectorSumCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -569,6 +692,13 @@ ScalarVectorProductCoefficient::ScalarVectorProductCoefficient(
: VectorCoefficient(B.GetVDim()), aConst(0.0), a(&A), b(&B)
{}
void ScalarVectorProductCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
if (b) { b->SetTime(t); }
this->VectorCoefficient::SetTime(t);
}
void ScalarVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -582,6 +712,12 @@ NormalizedVectorCoefficient::NormalizedVectorCoefficient(VectorCoefficient &A,
: VectorCoefficient(A.GetVDim()), a(&A), tol(tol_)
{}
void NormalizedVectorCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
this->VectorCoefficient::SetTime(t);
}
void NormalizedVectorCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -600,6 +736,13 @@ VectorCrossProductCoefficient::VectorCrossProductCoefficient(
"Arguments must have dimension equal to three.");
}
void VectorCrossProductCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
if (b) { b->SetTime(t); }
this->VectorCoefficient::SetTime(t);
}
void VectorCrossProductCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -621,6 +764,13 @@ MatrixVectorProductCoefficient::MatrixVectorProductCoefficient(
"Arguments have incompatible dimensions.");
}
void MatrixVectorProductCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
if (b) { b->SetTime(t); }
this->VectorCoefficient::SetTime(t);
}
void MatrixVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -650,6 +800,13 @@ MatrixSumCoefficient::MatrixSumCoefficient(MatrixCoefficient &A,
"Arguments must have the same dimensions.");
}
void MatrixSumCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
if (b) { b->SetTime(t); }
this->MatrixCoefficient::SetTime(t);
}
void MatrixSumCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -671,6 +828,13 @@ ScalarMatrixProductCoefficient::ScalarMatrixProductCoefficient(
: MatrixCoefficient(B.GetHeight(), B.GetWidth()), aConst(0.0), a(&A), b(&B)
{}
void ScalarMatrixProductCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
if (b) { b->SetTime(t); }
this->MatrixCoefficient::SetTime(t);
}
void ScalarMatrixProductCoefficient::Eval(DenseMatrix &M,
ElementTransformation &T,
const IntegrationPoint &ip)
@@ -684,6 +848,12 @@ TransposeMatrixCoefficient::TransposeMatrixCoefficient(MatrixCoefficient &A)
: MatrixCoefficient(A.GetWidth(), A.GetHeight()), a(&A)
{}
void TransposeMatrixCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
this->MatrixCoefficient::SetTime(t);
}
void TransposeMatrixCoefficient::Eval(DenseMatrix &M,
ElementTransformation &T,
const IntegrationPoint &ip)
@@ -700,6 +870,12 @@ InverseMatrixCoefficient::InverseMatrixCoefficient(MatrixCoefficient &A)
"Argument must be a square matrix.");
}
void InverseMatrixCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
this->MatrixCoefficient::SetTime(t);
}
void InverseMatrixCoefficient::Eval(DenseMatrix &M,
ElementTransformation &T,
const IntegrationPoint &ip)
@@ -714,6 +890,13 @@ OuterProductCoefficient::OuterProductCoefficient(VectorCoefficient &A,
va(A.GetVDim()), vb(B.GetVDim())
{}
void OuterProductCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
if (b) { b->SetTime(t); }
this->MatrixCoefficient::SetTime(t);
}
void OuterProductCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip)
{
@@ -740,6 +923,13 @@ CrossCrossCoefficient::CrossCrossCoefficient(Coefficient &A,
vk(K.GetVDim())
{}
void CrossCrossCoefficient::SetTime(double t)
{
if (a) { a->SetTime(t); }
if (k) { k->SetTime(t); }
this->MatrixCoefficient::SetTime(t);
}
void CrossCrossCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip)
{
+88 -4
View File
@@ -45,7 +45,7 @@ public:
Coefficient() { time = 0.; }
/// Set the time for time dependent coefficients
void SetTime(double t) { time = t; }
virtual void SetTime(double t) { time = t; }
/// Get the time for time dependent coefficients
double GetTime() { return time; }
@@ -217,6 +217,9 @@ public:
double (*F)(double,double))
: Q1(q1), Q2(q2), Transform2(F) { Transform1 = 0; }
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip);
};
@@ -269,6 +272,9 @@ public:
weight = NULL; sdim = 3; tdf = NULL;
}
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Set the center location of the delta function.
void SetDeltaCenter(const Vector& center);
@@ -333,6 +339,9 @@ public:
RestrictedCoefficient(Coefficient &c_, Array<int> &attr)
{ c = &c_; attr.Copy(active_attr); }
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip)
{ return active_attr[T.Attribute-1] ? c->Eval(T, ip, GetTime()) : 0.0; }
@@ -350,7 +359,7 @@ public:
VectorCoefficient(int vd) { vdim = vd; time = 0.; }
/// Set the time for time dependent coefficients
void SetTime(double t) { time = t; }
virtual void SetTime(double t) { time = t; }
/// Get the time for time dependent coefficients
double GetTime() { return time; }
@@ -456,6 +465,9 @@ public:
still need to be added with Set(). */
explicit VectorArrayCoefficient(int dim);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Returns i'th coefficient.
Coefficient* GetCoeff(int i) { return Coeff[i]; }
@@ -632,6 +644,9 @@ public:
double s)
: VectorCoefficient(dir_.Size()), dir(dir_), d(x,y,z,s) { }
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Replace the associated DeltaCoefficient with a new DeltaCoefficient.
/** The new DeltaCoefficient cannot have a specified weight Coefficient, i.e.
DeltaCoefficient::Weight() should return NULL. */
@@ -677,6 +692,9 @@ public:
: VectorCoefficient(vc.GetVDim())
{ c = &vc; attr.Copy(active_attr); }
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Evaluate the vector coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
@@ -708,7 +726,7 @@ public:
height(h), width(w), time(0.), symmetric(symm) { }
/// Set the time for time dependent coefficients
void SetTime(double t) { time = t; }
virtual void SetTime(double t) { time = t; }
/// Get the time for time dependent coefficients
double GetTime() { return time; }
@@ -817,6 +835,9 @@ public:
: MatrixCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
{ }
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
@@ -844,6 +865,9 @@ public:
actual coefficients still need to be added with Set(). */
explicit MatrixArrayCoefficient (int dim);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Get the coefficient located at (i,j) in the matrix.
Coefficient* GetCoeff (int i, int j) { return Coeff[i*width+j]; }
@@ -881,6 +905,9 @@ public:
: MatrixCoefficient(mc.GetHeight(), mc.GetWidth())
{ c = &mc; attr.Copy(active_attr); }
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
@@ -911,6 +938,9 @@ public:
double alpha_ = 1.0, double beta_ = 1.0)
: aConst(0.0), a(&A), b(&B), alpha(alpha_), beta(beta_) { }
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the first term in the linear combination as a constant
void SetAConst(double A) { a = NULL; aConst = A; }
/// Return the first term in the linear combination
@@ -959,7 +989,7 @@ public:
{ dim = dimension; time = 0.; }
/// Set the time for time dependent coefficients
void SetTime(double t) { time = t; }
virtual void SetTime(double t) { time = t; }
/// Get the time for time dependent coefficients
double GetTime() { return time; }
@@ -1037,6 +1067,9 @@ public:
: SymmetricMatrixCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
{ }
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseSymmetricMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
@@ -1063,6 +1096,9 @@ public:
ProductCoefficient(Coefficient &A, Coefficient &B)
: aConst(0.0), a(&A), b(&B) { }
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the first term in the product as a constant
void SetAConst(double A) { a = NULL; aConst = A; }
/// Return the first term in the product
@@ -1108,6 +1144,9 @@ public:
RatioCoefficient(Coefficient &A, double B)
: aConst(0.0), bConst(B), a(&A), b(NULL) { }
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the numerator in the ratio as a constant
void SetAConst(double A) { a = NULL; aConst = A; }
/// Return the numerator of the ratio
@@ -1151,6 +1190,9 @@ public:
PowerCoefficient(Coefficient &A, double p_)
: a(&A), p(p_) { }
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the base coefficient
void SetACoef(Coefficient &A) { a = &A; }
/// Return the base coefficient
@@ -1181,6 +1223,9 @@ public:
/// Construct with the two vector coefficients. Result is \f$ A \cdot B \f$.
InnerProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the first vector in the inner product
void SetACoef(VectorCoefficient &A) { a = &A; }
/// Return the first vector coefficient in the inner product
@@ -1210,6 +1255,9 @@ public:
/// Constructor with two vector coefficients. Result is \f$ A_x B_y - A_y * B_x; \f$.
VectorRotProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the first vector in the product
void SetACoef(VectorCoefficient &A) { a = &A; }
/// Return the first vector of the product
@@ -1237,6 +1285,9 @@ public:
/// Construct with the matrix.
DeterminantCoefficient(MatrixCoefficient &A);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
/// Return the matrix coefficient
@@ -1280,6 +1331,9 @@ public:
VectorSumCoefficient(VectorCoefficient &A_, VectorCoefficient &B_,
Coefficient &alpha_, Coefficient &beta_);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the first vector coefficient
void SetACoef(VectorCoefficient &A) { ACoef = &A; }
/// Return the first vector coefficient
@@ -1341,6 +1395,9 @@ public:
/// Constructor with two coefficients. Result is A * B.
ScalarVectorProductCoefficient(Coefficient &A, VectorCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the scalar factor as a constant
void SetAConst(double A) { a = NULL; aConst = A; }
/// Return the scalar factor
@@ -1379,6 +1436,9 @@ public:
*/
NormalizedVectorCoefficient(VectorCoefficient &A, double tol = 1e-6);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the vector coefficient
void SetACoef(VectorCoefficient &A) { a = &A; }
/// Return the vector coefficient
@@ -1404,6 +1464,9 @@ public:
/// Construct with the two coefficients. Result is A x B.
VectorCrossProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the first term in the product
void SetACoef(VectorCoefficient &A) { a = &A; }
/// Return the first term in the product
@@ -1435,6 +1498,9 @@ public:
/// Constructor with two coefficients. Result is A*B.
MatrixVectorProductCoefficient(MatrixCoefficient &A, VectorCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
/// Return the matrix coefficient
@@ -1487,6 +1553,9 @@ public:
MatrixSumCoefficient(MatrixCoefficient &A, MatrixCoefficient &B,
double alpha_ = 1.0, double beta_ = 1.0);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the first matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
/// Return the first matrix coefficient
@@ -1528,6 +1597,9 @@ public:
/// Constructor with two coefficients. Result is A*B.
ScalarMatrixProductCoefficient(Coefficient &A, MatrixCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the scalar factor as a constant
void SetAConst(double A) { a = NULL; aConst = A; }
/// Return the scalar factor
@@ -1558,6 +1630,9 @@ public:
/// Construct with the matrix coefficient. Result is \f$ A^T \f$.
TransposeMatrixCoefficient(MatrixCoefficient &A);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
/// Return the matrix coefficient
@@ -1578,6 +1653,9 @@ public:
/// Construct with the matrix coefficient. Result is \f$ A^{-1} \f$.
InverseMatrixCoefficient(MatrixCoefficient &A);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
/// Return the matrix coefficient
@@ -1602,6 +1680,9 @@ public:
/// Construct with two vector coefficients. Result is \f$ A B^T \f$.
OuterProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the first vector in the outer product
void SetACoef(VectorCoefficient &A) { a = &A; }
/// Return the first vector coefficient in the outer product
@@ -1637,6 +1718,9 @@ public:
CrossCrossCoefficient(double A, VectorCoefficient &K);
CrossCrossCoefficient(Coefficient &A, VectorCoefficient &K);
/// Set the time for internally stored coefficients
void SetTime(double t);
/// Reset the scalar factor as a constant
void SetAConst(double A) { a = NULL; aConst = A; }
/// Return the scalar factor
+34
View File
@@ -195,6 +195,15 @@ ComplexLinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi_real,
if ( lfi_imag ) { lfi->AddDomainIntegrator(lfi_imag); }
}
void
ComplexLinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag,
Array<int> &elem_attr_marker)
{
if ( lfi_real ) { lfr->AddDomainIntegrator(lfi_real, elem_attr_marker); }
if ( lfi_imag ) { lfi->AddDomainIntegrator(lfi_imag, elem_attr_marker); }
}
void
ComplexLinearForm::AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag)
@@ -317,6 +326,14 @@ void SesquilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
if (bfi_imag) { blfi->AddDomainIntegrator(bfi_imag); }
}
void SesquilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> & elem_marker)
{
if (bfi_real) { blfr->AddDomainIntegrator(bfi_real, elem_marker); }
if (bfi_imag) { blfi->AddDomainIntegrator(bfi_imag, elem_marker); }
}
void
SesquilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
@@ -879,6 +896,15 @@ ParComplexLinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi_real,
if ( lfi_imag ) { plfi->AddDomainIntegrator(lfi_imag); }
}
void
ParComplexLinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag,
Array<int> &elem_attr_marker)
{
if ( lfi_real ) { plfr->AddDomainIntegrator(lfi_real, elem_attr_marker); }
if ( lfi_imag ) { plfi->AddDomainIntegrator(lfi_imag, elem_attr_marker); }
}
void
ParComplexLinearForm::AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag)
@@ -1040,6 +1066,14 @@ void ParSesquilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
if (bfi_imag) { pblfi->AddDomainIntegrator(bfi_imag); }
}
void ParSesquilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> & elem_marker)
{
if (bfi_real) { pblfr->AddDomainIntegrator(bfi_real, elem_marker); }
if (bfi_imag) { pblfi->AddDomainIntegrator(bfi_imag, elem_marker); }
}
void
ParSesquilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag)
+20
View File
@@ -128,6 +128,11 @@ public:
void AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag);
/// Adds new Domain Integrator, restricted to the given attributes.
void AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag,
Array<int> &elem_attr_marker);
/// Adds new Boundary Integrator.
void AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag);
@@ -260,6 +265,11 @@ public:
void AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
/// Adds new Domain Integrator, restricted to the given attributes.
void AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> &elem_marker);
/// Adds new Boundary Integrator.
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
@@ -464,6 +474,11 @@ public:
void AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag);
/// Adds new Domain Integrator, restricted to specific attributes.
void AddDomainIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag,
Array<int> &elem_attr_marker);
/// Adds new Boundary Integrator.
void AddBoundaryIntegrator(LinearFormIntegrator *lfi_real,
LinearFormIntegrator *lfi_imag);
@@ -598,6 +613,11 @@ public:
void AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
/// Adds new Domain Integrator, restricted to specific attributes.
void AddDomainIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag,
Array<int> &elem_marker);
/// Adds new Boundary Integrator.
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi_real,
BilinearFormIntegrator *bfi_imag);
+79 -1
View File
@@ -645,7 +645,8 @@ ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
Node &n_mesh,
const std::string &coordset_name,
const std::string &main_topology_name,
const std::string &boundary_topology_name)
const std::string &boundary_topology_name,
const std::string &main_adjset_name)
{
int dim = mesh->SpaceDimension();
@@ -815,6 +816,83 @@ ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
bndry_att_vals[i] = mesh->GetBdrAttribute(i);
}
}
////////////////////////////////////////////
// Setup adjsets
////////////////////////////////////////////
#ifdef MFEM_USE_MPI
ParMesh *pmesh = dynamic_cast<ParMesh*>(mesh);
if (pmesh)
{
////////////////////////////////////////////
// Setup main adjset
////////////////////////////////////////////
Node &n_adjset = n_mesh["adjsets"][main_adjset_name];
n_adjset["association"] = "vertex";
n_adjset["topology"] = main_topology_name;
n_adjset["groups"].set(DataType::object());
const GroupTopology &pmesh_gtopo = pmesh->gtopo;
const int local_rank = pmesh->GetMyRank();
const int num_groups = pmesh_gtopo.NGroups();
// NOTE: skip the first group since its the local-only group
for (int i = 1; i < num_groups; i++)
{
const int num_group_nbrs = pmesh_gtopo.GetGroupSize(i);
const int *group_nbrs = pmesh_gtopo.GetGroup(i);
const int num_group_verts = pmesh->GroupNVertices(i);
// NOTE: 'neighbor' values are local to this processor, but Blueprint
// expects global domain identifiers, so we collapse this layer of
// indirection
Array<int> group_ranks(num_group_nbrs);
std::string group_name = "group";
{
for (int j = 0; j < num_group_nbrs; j++)
{
group_ranks[j] = pmesh_gtopo.GetNeighborRank(group_nbrs[j]);
}
group_ranks.Sort();
for (int j = 0; j < num_group_nbrs; j++)
{
group_name += "_" + std::to_string(group_ranks[j]);
}
// NOTE: Blueprint only wants remote ranks in its neighbor list,
// so we remove the local rank after the canonicalized Blueprint
// group name is formed
group_ranks.DeleteFirst(local_rank);
}
Node &n_group = n_adjset["groups"][group_name];
n_group["neighbors"].set(group_ranks.GetData(), group_ranks.Size());
n_group["values"].set(DataType::c_int(num_group_verts));
int_array group_vals = n_group["values"].value();
for (int j = 0; j < num_group_verts; j++)
{
group_vals[j] = pmesh->GroupVertex(i, j);
}
}
// NOTE: We don't create an adjset for face neighbor data because
// these faces aren't listed in the 'boundary_topology_name' topology
// (this topology only covers the faces between 'main_topology_name'
// elements and void). To include a face neighbor data adjset, this
// function would need to export a topology with either (1) all faces
// in the mesh topology or (2) all boundary faces, including neighbors.
////////////////////////////////////////////
// Setup distributed state
////////////////////////////////////////////
Node &n_domid = n_mesh["state/domain_id"];
n_domid.set(local_rank);
}
#endif
}
//---------------------------------------------------------------------------//
+2 -1
View File
@@ -166,7 +166,8 @@ public:
conduit::Node &out,
const std::string &coordset_name = "coords",
const std::string &main_topology_name = "main",
const std::string &boundary_topology_name = "boundary");
const std::string &boundary_topology_name = "boundary",
const std::string &main_adjset_name = "main_adjset");
/// Describes a MFEM grid function using the mesh blueprint
/** Sets up passed conduit::Node out to describe the given grid function
+9
View File
@@ -6030,6 +6030,15 @@ void RT0PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
shape(4,1) = - 0.5;
shape(4,2) = 1.0;
if (!rt0)
{
for (int i=1; i<5; i++)
for (int j=0; j<3; j++)
{
shape(i, j) *= 0.5;
}
}
return;
}
+134 -3
View File
@@ -106,8 +106,11 @@ void FiniteElementSpace::CopyProlongationAndRestriction(
SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
if (perm)
{
// Note: although n and fes.GetVSize() are typically equal, in
// variable-order spaces they may differ, since nonconforming edges/faces
// my have fictitious DOFs.
int n = perm->Size();
perm_mat = new SparseMatrix(n, n);
perm_mat = new SparseMatrix(n, fes.GetVSize());
for (int i=0; i<n; ++i)
{
double s;
@@ -124,11 +127,22 @@ void FiniteElementSpace::CopyProlongationAndRestriction(
else { cP = new SparseMatrix(*fes.GetConformingProlongation()); }
cP_is_set = true;
}
else if (perm != NULL)
{
cP = perm_mat;
cP_is_set = true;
perm_mat = NULL;
}
if (fes.GetConformingRestriction() != NULL)
{
if (perm) { cR = Mult(*fes.GetConformingRestriction(), *perm_mat_tr); }
else { cR = new SparseMatrix(*fes.GetConformingRestriction()); }
}
else if (perm != NULL)
{
cR = perm_mat_tr;
perm_mat_tr = NULL;
}
delete perm_mat;
delete perm_mat_tr;
@@ -1723,6 +1737,122 @@ void FiniteElementSpace::RefinementOperator
}
}
namespace internal
{
// Used in GetCoarseToFineMap() below.
struct RefType
{
Geometry::Type geom;
int num_children;
const Pair<int,int> *children;
RefType(Geometry::Type g, int n, const Pair<int,int> *c)
: geom(g), num_children(n), children(c) { }
bool operator<(const RefType &other) const
{
if (geom < other.geom) { return true; }
if (geom > other.geom) { return false; }
if (num_children < other.num_children) { return true; }
if (num_children > other.num_children) { return false; }
for (int i = 0; i < num_children; i++)
{
if (children[i].one < other.children[i].one) { return true; }
if (children[i].one > other.children[i].one) { return false; }
}
return false; // everything is equal
}
};
void GetCoarseToFineMap(const CoarseFineTransformations &cft,
const mfem::Mesh &fine_mesh,
Table &coarse_to_fine,
Array<int> &coarse_to_ref_type,
Table &ref_type_to_matrix,
Array<Geometry::Type> &ref_type_to_geom)
{
const int fine_ne = cft.embeddings.Size();
int coarse_ne = -1;
for (int i = 0; i < fine_ne; i++)
{
coarse_ne = std::max(coarse_ne, cft.embeddings[i].parent);
}
coarse_ne++;
coarse_to_ref_type.SetSize(coarse_ne);
coarse_to_fine.SetDims(coarse_ne, fine_ne);
Array<int> cf_i(coarse_to_fine.GetI(), coarse_ne+1);
Array<Pair<int,int> > cf_j(fine_ne);
cf_i = 0;
for (int i = 0; i < fine_ne; i++)
{
cf_i[cft.embeddings[i].parent+1]++;
}
cf_i.PartialSum();
MFEM_ASSERT(cf_i.Last() == cf_j.Size(), "internal error");
for (int i = 0; i < fine_ne; i++)
{
const Embedding &e = cft.embeddings[i];
cf_j[cf_i[e.parent]].one = e.matrix; // used as sort key below
cf_j[cf_i[e.parent]].two = i;
cf_i[e.parent]++;
}
std::copy_backward(cf_i.begin(), cf_i.end()-1, cf_i.end());
cf_i[0] = 0;
for (int i = 0; i < coarse_ne; i++)
{
std::sort(&cf_j[cf_i[i]], cf_j.GetData() + cf_i[i+1]);
}
for (int i = 0; i < fine_ne; i++)
{
coarse_to_fine.GetJ()[i] = cf_j[i].two;
}
using std::map;
using std::pair;
map<RefType,int> ref_type_map;
for (int i = 0; i < coarse_ne; i++)
{
const int num_children = cf_i[i+1]-cf_i[i];
MFEM_ASSERT(num_children > 0, "");
const int fine_el = cf_j[cf_i[i]].two;
// Assuming the coarse and the fine elements have the same geometry:
const Geometry::Type geom = fine_mesh.GetElementBaseGeometry(fine_el);
const RefType ref_type(geom, num_children, &cf_j[cf_i[i]]);
pair<map<RefType,int>::iterator,bool> res =
ref_type_map.insert(
pair<const RefType,int>(ref_type, (int)ref_type_map.size()));
coarse_to_ref_type[i] = res.first->second;
}
ref_type_to_matrix.MakeI((int)ref_type_map.size());
ref_type_to_geom.SetSize((int)ref_type_map.size());
for (map<RefType,int>::iterator it = ref_type_map.begin();
it != ref_type_map.end(); ++it)
{
ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children);
ref_type_to_geom[it->second] = it->first.geom;
}
ref_type_to_matrix.MakeJ();
for (map<RefType,int>::iterator it = ref_type_map.begin();
it != ref_type_map.end(); ++it)
{
const RefType &rt = it->first;
for (int j = 0; j < rt.num_children; j++)
{
ref_type_to_matrix.AddConnection(it->second, rt.children[j].one);
}
}
ref_type_to_matrix.ShiftUpI();
}
} // namespace internal
/// TODO: Implement DofTransformation support
FiniteElementSpace::DerefinementOperator::DerefinementOperator(
const FiniteElementSpace *f_fes, const FiniteElementSpace *c_fes,
@@ -1764,8 +1894,9 @@ FiniteElementSpace::DerefinementOperator::DerefinementOperator(
}
Table ref_type_to_matrix;
rtrans.GetCoarseToFineMap(*f_mesh, coarse_to_fine, coarse_to_ref_type,
ref_type_to_matrix, ref_type_to_geom);
internal::GetCoarseToFineMap(rtrans, *f_mesh, coarse_to_fine,
coarse_to_ref_type, ref_type_to_matrix,
ref_type_to_geom);
MFEM_ASSERT(coarse_to_fine.Size() == c_fes->GetNE(), "");
const int total_ref_types = ref_type_to_geom.Size();
+14 -9
View File
@@ -1341,21 +1341,20 @@ void GridFunction::ProjectVectorFieldOn(GridFunction &vec_field, int comp)
}
}
void GridFunction::GetDerivative(int comp, int der_comp, GridFunction &der)
void GridFunction::AccumulateAndCountDerivativeValues(int comp, int der_comp,
GridFunction &der,
Array<int> &zones_per_dof)
{
FiniteElementSpace * der_fes = der.FESpace();
ElementTransformation * transf;
Array<int> overlap(der_fes->GetVSize());
zones_per_dof.SetSize(der_fes->GetVSize());
Array<int> der_dofs, vdofs;
DenseMatrix dshape, inv_jac;
Vector pt_grad, loc_func;
int i, j, k, dim, dof, der_dof, ind;
double a;
for (i = 0; i < overlap.Size(); i++)
{
overlap[i] = 0;
}
zones_per_dof = 0;
der = 0.0;
comp--;
@@ -1390,11 +1389,17 @@ void GridFunction::GetDerivative(int comp, int der_comp, GridFunction &der)
a += inv_jac(j, der_comp) * pt_grad(j);
}
der(der_dofs[k]) += a;
overlap[der_dofs[k]]++;
zones_per_dof[der_dofs[k]]++;
}
}
}
for (i = 0; i < overlap.Size(); i++)
void GridFunction::GetDerivative(int comp, int der_comp, GridFunction &der)
{
Array<int> overlap;
AccumulateAndCountDerivativeValues(comp, der_comp, der, overlap);
for (int i = 0; i < overlap.Size(); i++)
{
der(i) /= overlap[i];
}
@@ -3998,7 +4003,7 @@ double ZZErrorEstimator(BilinearFormIntegrator &blfi,
fl -= fla;
double err = blfi.ComputeFluxEnergy(*ffes->GetFE(i), *Transf, fl,
(aniso_flags ? &d_xyz : NULL));
with_coeff, (aniso_flags ? &d_xyz : NULL));
error_estimates(i) = std::sqrt(err);
total_error += err;
+16
View File
@@ -310,6 +310,16 @@ public:
void ProjectVectorFieldOn(GridFunction &vec_field, int comp = 0);
/** @brief Compute a certain derivative of a function's component.
Derivatives of the function are computed at the DOF locations of @a der,
and averaged over overlapping DOFs. Thus this function projects the
derivative to the FiniteElementSpace of @a der.
@param[in] comp Index of the function's component to be differentiated.
The index is 1-based, i.e., use 1 for scalar functions.
@param[in] der_comp Use 0/1/2 for derivatives in x/y/z directions.
@param[out] der The resulting derivative (scalar function). The
FiniteElementSpace of this function must be set
before the call. */
void GetDerivative(int comp, int der_comp, GridFunction &der);
double GetDivergence(ElementTransformation &tr) const;
@@ -411,6 +421,12 @@ protected:
void AccumulateAndCountZones(VectorCoefficient &vcoeff, AvgType type,
Array<int> &zones_per_vdof);
/** @brief Used for the serial and parallel implementations of the
GetDerivative() method; see its documentation. */
void AccumulateAndCountDerivativeValues(int comp, int der_comp,
GridFunction &der,
Array<int> &zones_per_dof);
void AccumulateAndCountBdrValues(Coefficient *coeff[],
VectorCoefficient *vcoeff, Array<int> &attr,
Array<int> &values_counter);
+63 -43
View File
@@ -34,7 +34,8 @@ void LORBase::AddIntegratorsAndMarkers(BilinearForm &a_from,
BilinearForm &a_to,
GetIntegratorsFn get_integrators,
GetMarkersFn get_markers,
AddIntegratorMarkersFn add_integrator,
AddIntegratorMarkersFn add_integrator_marker,
AddIntegratorFn add_integrator,
const IntegrationRule *ir)
{
Array<BilinearFormIntegrator*> *integrators = (a_from.*get_integrators)();
@@ -42,7 +43,14 @@ void LORBase::AddIntegratorsAndMarkers(BilinearForm &a_from,
for (int i=0; i<integrators->Size(); ++i)
{
(a_to.*add_integrator)((*integrators)[i], *(*markers[i]));
if (*markers[i])
{
(a_to.*add_integrator_marker)((*integrators)[i], *(*markers[i]));
}
else
{
(a_to.*add_integrator)((*integrators)[i]);
}
ir_map[(*integrators)[i]] = ((*integrators)[i])->GetIntegrationRule();
if (ir) { ((*integrators)[i])->SetIntegrationRule(*ir); }
}
@@ -92,13 +100,29 @@ void LORBase::ConstructLocalDofPermutation(Array<int> &perm_) const
int dim = mesh_lor.Dimension();
const CoarseFineTransformations &cf_tr = mesh_lor.GetRefinementTransforms();
using GeomRef = std::pair<Geometry::Type, int>;
std::map<GeomRef, int> point_matrices_offsets;
perm_.SetSize(fes_lor.GetVSize());
Array<int> vdof_ho, vdof_lor;
for (int ilor=0; ilor<mesh_lor.GetNE(); ++ilor)
{
int iho = cf_tr.embeddings[ilor].parent;
int p = fes_ho.GetOrder(iho);
int lor_index = cf_tr.embeddings[ilor].matrix;
// We use the point matrix index to identify the local LOR element index
// within the high-order coarse element.
//
// In variable-order spaces, the point matrices for each order are
// concatenated sequentially, so for the given element order, we need to
// find the offset that will give us the point matrix index relative to
// the current element order only.
GeomRef id(mesh_lor.GetElementBaseGeometry(ilor), p);
if (point_matrices_offsets.find(id) == point_matrices_offsets.end())
{
point_matrices_offsets[id] = lor_index;
}
lor_index -= point_matrices_offsets[id];
fes_ho.GetElementVDofs(iho, vdof_ho);
fes_lor.GetElementVDofs(ilor, vdof_lor);
@@ -109,7 +133,6 @@ void LORBase::ConstructLocalDofPermutation(Array<int> &perm_) const
continue;
}
int p = fes_ho.GetOrder(iho);
int p1 = p+1;
int ndof_per_dim = (dim == 2) ? p*p1 : type == ND ? p*p1*p1 : p*p*p1;
@@ -181,7 +204,7 @@ void LORBase::ConstructLocalDofPermutation(Array<int> &perm_) const
void LORBase::ConstructDofPermutation() const
{
FESpaceType type = GetFESpaceType();
if (type == H1 || type == L2 || nonconforming)
if (type == H1 || type == L2)
{
// H1 and L2: no permutation necessary, return identity
perm.SetSize(fes->GetTrueVSize());
@@ -226,10 +249,10 @@ const Array<int> &LORBase::GetDofPermutation() const
return perm;
}
bool LORBase::RequiresDofPermutation() const
bool LORBase::HasSameDofNumbering() const
{
FESpaceType type = GetFESpaceType();
return (type == H1 || type == L2 || nonconforming) ? false : true;
return type == H1 || type == L2;
}
const OperatorHandle &LORBase::GetAssembledSystem() const
@@ -238,7 +261,7 @@ const OperatorHandle &LORBase::GetAssembledSystem() const
return A;
}
void LORBase::AssembleSystem(BilinearForm &a_ho, const Array<int> &ess_dofs)
void LORBase::AssembleSystem_(BilinearForm &a_ho, const Array<int> &ess_dofs)
{
a->UseExternalIntegrators();
AddIntegrators(a_ho, *a, &BilinearForm::GetDBFI,
@@ -247,40 +270,23 @@ void LORBase::AssembleSystem(BilinearForm &a_ho, const Array<int> &ess_dofs)
&BilinearForm::AddInteriorFaceIntegrator, ir_face);
AddIntegratorsAndMarkers(a_ho, *a, &BilinearForm::GetBBFI,
&BilinearForm::GetBBFI_Marker,
&BilinearForm::AddBoundaryIntegrator,
&BilinearForm::AddBoundaryIntegrator, ir_face);
AddIntegratorsAndMarkers(a_ho, *a, &BilinearForm::GetBFBFI,
&BilinearForm::GetBFBFI_Marker,
&BilinearForm::AddBdrFaceIntegrator,
&BilinearForm::AddBdrFaceIntegrator, ir_face);
a->Assemble();
if (RequiresDofPermutation())
{
const Array<int> &p = GetDofPermutation();
// Form inverse permutation: given high-order dof i, pi[i] is corresp. LO
Array<int> pi(p.Size());
for (int i=0; i<p.Size(); ++i)
{
pi[absdof(p[i])] = i;
}
Array<int> ess_dofs_perm(ess_dofs.Size());
for (int i=0; i<ess_dofs.Size(); ++i)
{
ess_dofs_perm[i] = pi[ess_dofs[i]];
}
a->FormSystemMatrix(ess_dofs_perm, A);
}
else
{
a->FormSystemMatrix(ess_dofs, A);
}
a->FormSystemMatrix(ess_dofs, A);
ResetIntegrationRules(&BilinearForm::GetDBFI);
ResetIntegrationRules(&BilinearForm::GetFBFI);
ResetIntegrationRules(&BilinearForm::GetBBFI);
ResetIntegrationRules(&BilinearForm::GetBFBFI);
}
void LORBase::SetupNonconforming()
void LORBase::SetupProlongationAndRestriction()
{
if (RequiresDofPermutation())
if (!HasSameDofNumbering())
{
Array<int> p;
ConstructLocalDofPermutation(p);
@@ -290,7 +296,6 @@ void LORBase::SetupNonconforming()
{
fes->CopyProlongationAndRestriction(fes_ho, NULL);
}
nonconforming = true;
}
template <typename FEC>
@@ -373,7 +378,6 @@ LORDiscretization::LORDiscretization(BilinearForm &a_ho_,
int ref_type)
: LORDiscretization(*a_ho_.FESpace(), ref_type)
{
a = new BilinearForm(fes);
AssembleSystem(a_ho_, ess_tdof_list);
}
@@ -382,23 +386,32 @@ LORDiscretization::LORDiscretization(FiniteElementSpace &fes_ho,
{
CheckBasisType(fes_ho);
// TODO: support variable-order spaces
MFEM_VERIFY(!fes_ho.IsVariableOrder(),
"Cannot construct LOR operators on variable-order spaces");
int order = fes_ho.GetMaxElementOrder();
if (GetFESpaceType() == L2) { ++order; }
Mesh &mesh_ho = *fes_ho.GetMesh();
mesh = new Mesh(Mesh::MakeRefined(mesh_ho, order, ref_type));
// For H1, ND and RT spaces, use refinement = element order, for DG spaces,
// use refinement = element order + 1 (since LOR is p = 0 in this case).
int increment = (GetFESpaceType() == L2) ? 1 : 0;
Array<int> refinements(mesh_ho.GetNE());
for (int i=0; i<refinements.Size(); ++i)
{
refinements[i] = fes_ho.GetOrder(i) + increment;
}
mesh = new Mesh(Mesh::MakeRefined(mesh_ho, refinements, ref_type));
fec = fes_ho.FEColl()->Clone(GetLOROrder());
fes = new FiniteElementSpace(mesh, fec);
if (fes_ho.Nonconforming()) { SetupNonconforming(); }
SetupProlongationAndRestriction();
A.SetType(Operator::MFEM_SPARSEMAT);
}
void LORDiscretization::AssembleSystem(BilinearForm &a_ho,
const Array<int> &ess_dofs)
{
delete a;
a = new BilinearForm(&GetFESpace());
AssembleSystem_(a_ho, ess_dofs);
}
SparseMatrix &LORDiscretization::GetAssembledMatrix() const
{
MFEM_VERIFY(a != NULL && A.Ptr() != NULL, "No LOR system assembled");
@@ -412,7 +425,6 @@ ParLORDiscretization::ParLORDiscretization(ParBilinearForm &a_ho_,
int ref_type)
: ParLORDiscretization(*a_ho_.ParFESpace(), ref_type)
{
a = new ParBilinearForm(static_cast<ParFiniteElementSpace*>(fes));
AssembleSystem(a_ho_, ess_tdof_list);
}
@@ -420,7 +432,7 @@ ParLORDiscretization::ParLORDiscretization(ParFiniteElementSpace &fes_ho,
int ref_type) : LORBase(fes_ho)
{
if (fes_ho.GetMyRank() == 0) { CheckBasisType(fes_ho); }
// TODO: support variable-order spaces
// TODO: support variable-order spaces in parallel
MFEM_VERIFY(!fes_ho.IsVariableOrder(),
"Cannot construct LOR operators on variable-order spaces");
@@ -434,11 +446,19 @@ ParLORDiscretization::ParLORDiscretization(ParFiniteElementSpace &fes_ho,
fec = fes_ho.FEColl()->Clone(GetLOROrder());
ParFiniteElementSpace *pfes = new ParFiniteElementSpace(pmesh, fec);
fes = pfes;
if (fes_ho.Nonconforming()) { SetupNonconforming(); }
SetupProlongationAndRestriction();
A.SetType(Operator::Hypre_ParCSR);
}
void ParLORDiscretization::AssembleSystem(ParBilinearForm &a_ho,
const Array<int> &ess_dofs)
{
delete a;
a = new ParBilinearForm(&GetParFESpace());
AssembleSystem_(a_ho, ess_dofs);
}
HypreParMatrix &ParLORDiscretization::GetAssembledMatrix() const
{
MFEM_VERIFY(a != NULL && A.Ptr() != NULL, "No LOR system assembled");
+37 -67
View File
@@ -35,7 +35,7 @@ private:
/// Adds all the integrators from the BilinearForm @a a_from to @a a_to. If
/// the mesh consists of tensor product elements, temporarily changes the
/// integration rules of the integrators to use collocated quadrature for
/// better conditioning of the %LOR system.
/// better conditioning of the LOR system.
void AddIntegrators(BilinearForm &a_from,
BilinearForm &a_to,
GetIntegratorsFn get_integrators,
@@ -49,11 +49,12 @@ private:
BilinearForm &a_to,
GetIntegratorsFn get_integrators,
GetMarkersFn get_markers,
AddIntegratorMarkersFn add_integrator,
AddIntegratorMarkersFn add_integrator_marker,
AddIntegratorFn add_integrator,
const IntegrationRule *ir);
/// Resets the integration rules of the integrators of @a a to their original
/// values (after temporarily changing them for %LOR assembly).
/// values (after temporarily changing them for LOR assembly).
void ResetIntegrationRules(GetIntegratorsFn get_integrators);
static inline int absdof(int i) { return i < 0 ? -1-i : i; }
@@ -68,37 +69,42 @@ protected:
BilinearForm *a;
OperatorHandle A;
mutable Array<int> perm;
bool nonconforming = false;
/// Constructs the local DOF (ldof) permutation. In parallel this is used as
/// an intermediate step in computing the DOF permutation (see
/// ConstructDofPermutation and GetDofPermutation).
void ConstructLocalDofPermutation(Array<int> &perm_) const;
/// Construct the permutation that maps %LOR DOFs to high-order DOFs. See
/// Construct the permutation that maps LOR DOFs to high-order DOFs. See
/// GetDofPermutation.
void ConstructDofPermutation() const;
/// Sets up the prolongation and restriction operators required for
/// nonconforming spaces.
void SetupNonconforming();
/// Returns true if the LOR space and HO space have the same DOF numbering
/// (H1 or L2 spaces), false otherwise (ND or RT spaces).
bool HasSameDofNumbering() const;
/// Sets up the prolongation and restriction operators required in the case
/// of different DOF numberings (ND or RT spaces) or nonconforming spaces.
void SetupProlongationAndRestriction();
/// Returns the type of finite element space: H1, ND, RT or L2.
FESpaceType GetFESpaceType() const;
/// Returns the order of the %LOR space. 1 for H1 or ND, 0 for L2 or RT.
/// Returns the order of the LOR space. 1 for H1 or ND, 0 for L2 or RT.
int GetLOROrder() const;
/// Assembles the LOR system (used internally by
/// LORDiscretization::AssembleSystem and
/// ParLORDiscretization::AssembleSystem).
void AssembleSystem_(BilinearForm &a_ho, const Array<int> &ess_dofs);
LORBase(FiniteElementSpace &fes_ho_);
public:
/// Returns the assembled %LOR system.
/// Returns the assembled LOR system.
const OperatorHandle &GetAssembledSystem() const;
/// Assembles the %LOR system.
void AssembleSystem(BilinearForm &a_ho, const Array<int> &ess_dofs);
/// @brief Returns the permutation that maps %LOR DOFs to high-order DOFs.
/// @brief Returns the permutation that maps LOR DOFs to high-order DOFs.
///
/// This permutation is constructed the first time it is requested, and then
/// is cached. For H1 and L2 finite element spaces (or for nonconforming
@@ -108,16 +114,9 @@ public:
///
/// For vector finite element spaces (ND and RT), the DOF permutation is
/// nontrivial. Returns an array @a perm such that, given an index @a i of a
/// %LOR dof, @a perm[i] is the index of the corresponding HO dof.
/// LOR dof, @a perm[i] is the index of the corresponding HO dof.
const Array<int> &GetDofPermutation() const;
/// Returns true if the %LOR spaces requires a DOF permutation (if the
/// corresponding %LOR and HO DOFs are numbered differently), false
/// otherwise. Note: permutations are not required in the case of
/// nonconforming spaces, since the DOF numbering is incorporated into the
/// prolongation operators.
bool RequiresDofPermutation() const;
/// Returns the low-order refined finite element space.
FiniteElementSpace &GetFESpace() const { return *fes; }
@@ -144,7 +143,10 @@ public:
LORDiscretization(FiniteElementSpace &fes_ho,
int ref_type=BasisType::GaussLobatto);
/// Return the assembled %LOR operator as a SparseMatrix.
/// Assembles the LOR system corresponding to @a a_ho.
void AssembleSystem(BilinearForm &a_ho, const Array<int> &ess_dofs);
/// Return the assembled LOR operator as a SparseMatrix.
SparseMatrix &GetAssembledMatrix() const;
};
@@ -170,10 +172,13 @@ public:
ParLORDiscretization(ParFiniteElementSpace &fes_ho,
int ref_type=BasisType::GaussLobatto);
/// Return the assembled %LOR operator as a HypreParMatrix.
/// Assembles the LOR system corresponding to @a a_ho.
void AssembleSystem(ParBilinearForm &a_ho, const Array<int> &ess_dofs);
/// Return the assembled LOR operator as a HypreParMatrix.
HypreParMatrix &GetAssembledMatrix() const;
/// Return the %LOR ParFiniteElementSpace.
/// Return the LOR ParFiniteElementSpace.
ParFiniteElementSpace &GetParFESpace() const;
};
@@ -192,12 +197,11 @@ class LORSolver : public Solver
protected:
LORBase *lor;
bool own_lor = true;
bool use_permutation = true;
SolverType solver;
mutable Vector px, py;
public:
/// @brief Create a solver of type @a SolverType, formed using the assembled
/// SparseMatrix of the %LOR version of @a a_ho. @see LORDiscretization
/// SparseMatrix of the LOR version of @a a_ho. @see LORDiscretization
LORSolver(BilinearForm &a_ho, const Array<int> &ess_tdof_list,
int ref_type=BasisType::GaussLobatto)
{
@@ -207,7 +211,7 @@ public:
#ifdef MFEM_USE_MPI
/// @brief Create a solver of type @a SolverType, formed using the assembled
/// HypreParMatrix of the %LOR version of @a a_ho. @see ParLORDiscretization
/// HypreParMatrix of the LOR version of @a a_ho. @see ParLORDiscretization
LORSolver(ParBilinearForm &a_ho, const Array<int> &ess_tdof_list,
int ref_type=BasisType::GaussLobatto)
{
@@ -218,8 +222,6 @@ public:
/// @brief Create a solver of type @a SolverType using Operator @a op and
/// arguments @a args.
///
/// The object @a lor_ will be used for DOF permutations.
template <typename... Args>
LORSolver(const Operator &op, LORBase &lor_, Args&&... args) : solver(args...)
{
@@ -228,7 +230,7 @@ public:
SetOperator(op);
}
/// @brief Create a solver of type @a SolverType using the assembled %LOR
/// @brief Create a solver of type @a SolverType using the assembled LOR
/// operator represented by @a lor_.
///
/// The given @a args will be used as arguments to the solver constructor.
@@ -243,42 +245,7 @@ public:
height = solver.Height();
}
void Mult(const Vector &x, Vector &y) const
{
if (use_permutation && lor->RequiresDofPermutation())
{
const Array<int> &p = lor->GetDofPermutation();
px.SetSize(x.Size());
py.SetSize(y.Size());
for (int i=0; i<x.Size(); ++i)
{ px[i] = p[i] < 0 ? -x[-1-p[i]] : x[p[i]]; }
solver.Mult(px, py);
for (int i=0; i<y.Size(); ++i)
{
int pi = p[i];
int s = pi < 0 ? -1 : 1;
y[pi < 0 ? -1-pi : pi] = s*py[i];
}
}
else
{
solver.Mult(x, y);
}
}
/// @brief Enable or disable the DOF permutation (enabled by default).
///
/// The corresponding %LOR and high-order DOFs may not have the same
/// numbering (for example, when using ND or RT spaces), and so a permutation
/// is required when applying the %LOR solver as a preconditioner for the
/// high-order problem. This permutation can be disabled (for example, in
/// order to precondition the low-order problem directly).
void UsePermutation(bool use_permutation_)
{
use_permutation = use_permutation_;
}
void Mult(const Vector &x, Vector &y) const { solver.Mult(x, y); }
/// Access the underlying solver.
SolverType &GetSolver() { return solver; }
@@ -286,6 +253,9 @@ public:
/// Access the underlying solver.
const SolverType &GetSolver() const { return solver; }
/// Access the LOR discretization object.
const LORBase &GetLOR() const { return *lor; }
~LORSolver() { if (own_lor) { delete lor; } }
};
+25 -6
View File
@@ -96,6 +96,7 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
Vector el_x;
const FiniteElement *fe;
ElementTransformation *T;
DofTransformation *doftrans;
double energy = 0.0;
if (dnfi.Size())
@@ -103,9 +104,10 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
for (int i = 0; i < fes->GetNE(); i++)
{
fe = fes->GetFE(i);
fes->GetElementVDofs(i, vdofs);
doftrans = fes->GetElementVDofs(i, vdofs);
T = fes->GetElementTransformation(i);
x.GetSubVector(vdofs, el_x);
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
for (int k = 0; k < dnfi.Size(); k++)
{
energy += dnfi[k]->GetElementEnergy(*fe, *T, el_x);
@@ -166,6 +168,7 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
Vector el_x, el_y;
const FiniteElement *fe;
ElementTransformation *T;
DofTransformation *doftrans;
Mesh *mesh = fes->GetMesh();
py = 0.0;
@@ -175,12 +178,14 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
for (int i = 0; i < fes->GetNE(); i++)
{
fe = fes->GetFE(i);
fes->GetElementVDofs(i, vdofs);
doftrans = fes->GetElementVDofs(i, vdofs);
T = fes->GetElementTransformation(i);
px.GetSubVector(vdofs, el_x);
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
for (int k = 0; k < dnfi.Size(); k++)
{
dnfi[k]->AssembleElementVector(*fe, *T, el_x, el_y);
if (doftrans) {doftrans->TransformDual(el_y); }
py.AddElementVector(vdofs, el_y);
}
}
@@ -302,6 +307,7 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
DenseMatrix elmat;
const FiniteElement *fe;
ElementTransformation *T;
DofTransformation *doftrans;
Mesh *mesh = fes->GetMesh();
const Vector &px = Prolongate(x);
@@ -319,12 +325,14 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
for (int i = 0; i < fes->GetNE(); i++)
{
fe = fes->GetFE(i);
fes->GetElementVDofs(i, vdofs);
doftrans = fes->GetElementVDofs(i, vdofs);
T = fes->GetElementTransformation(i);
px.GetSubVector(vdofs, el_x);
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
for (int k = 0; k < dnfi.Size(); k++)
{
dnfi[k]->AssembleElementGrad(*fe, *T, el_x, elmat);
if (doftrans) { doftrans->TransformDual(elmat); }
Grad->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros);
// Grad->AddSubMatrix(vdofs, vdofs, elmat, 1);
}
@@ -583,6 +591,7 @@ double BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
Array<const Vector *> el_x_const(fes.Size());
Array<const FiniteElement *> fe(fes.Size());
ElementTransformation *T;
DofTransformation *doftrans;
double energy = 0.0;
for (int i=0; i<fes.Size(); ++i)
@@ -598,8 +607,9 @@ double BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
for (int s=0; s<fes.Size(); ++s)
{
fe[s] = fes[s]->GetFE(i);
fes[s]->GetElementVDofs(i, *vdofs[s]);
doftrans = fes[s]->GetElementVDofs(i, *vdofs[s]);
bx.GetBlock(s).GetSubVector(*vdofs[s], *el_x[s]);
if (doftrans) {doftrans->InvTransformPrimal(*el_x[s]); }
}
for (int k = 0; k < dnfi.Size(); ++k)
@@ -645,6 +655,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
Array<const FiniteElement *> fe(fes.Size());
Array<const FiniteElement *> fe2(fes.Size());
ElementTransformation *T;
Array<DofTransformation *> doftrans(fes.Size()); doftrans = nullptr;
by.UseDevice(true);
by = 0.0;
@@ -664,9 +675,10 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
T = fes[0]->GetElementTransformation(i);
for (int s = 0; s < fes.Size(); ++s)
{
fes[s]->GetElementVDofs(i, *(vdofs[s]));
doftrans[s] = fes[s]->GetElementVDofs(i, *(vdofs[s]));
fe[s] = fes[s]->GetFE(i);
bx.GetBlock(s).GetSubVector(*(vdofs[s]), *el_x[s]);
if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); }
}
for (int k = 0; k < dnfi.Size(); ++k)
@@ -677,6 +689,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
for (int s=0; s<fes.Size(); ++s)
{
if (el_y[s]->Size() == 0) { continue; }
if (doftrans[s]) {doftrans[s]->TransformDual(*el_y[s]); }
by.GetBlock(s).AddElementVector(*(vdofs[s]), *el_y[s]);
}
}
@@ -844,6 +857,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
Array<const FiniteElement *>fe(fes.Size());
Array<const FiniteElement *>fe2(fes.Size());
ElementTransformation * T;
Array<DofTransformation *> doftrans(fes.Size()); doftrans = nullptr;
for (int i=0; i<fes.Size(); ++i)
{
@@ -880,8 +894,9 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
for (int s = 0; s < fes.Size(); ++s)
{
fe[s] = fes[s]->GetFE(i);
fes[s]->GetElementVDofs(i, *vdofs[s]);
doftrans[s] = fes[s]->GetElementVDofs(i, *vdofs[s]);
bx.GetBlock(s).GetSubVector(*vdofs[s], *el_x[s]);
if (doftrans[s]) {doftrans[s]->InvTransformPrimal(*el_x[s]); }
}
for (int k = 0; k < dnfi.Size(); ++k)
@@ -893,6 +908,10 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
for (int l=0; l<fes.Size(); ++l)
{
if (elmats(j,l)->Height() == 0) { continue; }
if (doftrans[j] || doftrans[l])
{
TransformDual(doftrans[j], doftrans[l], *elmats(j,l));
}
Grads(j,l)->AddSubMatrix(*vdofs[j], *vdofs[l],
*elmats(j,l), skip_zeros);
}
+19 -1
View File
@@ -3195,8 +3195,11 @@ void ParFiniteElementSpace::CopyProlongationAndRestriction(
SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
if (perm)
{
// Note: although n and fes.GetVSize() are typically equal, in
// variable-order spaces they may differ, since nonconforming edges/faces
// my have fictitious DOFs.
int n = perm->Size();
perm_mat = new SparseMatrix(n, n);
perm_mat = new SparseMatrix(n, fes.GetVSize());
for (int i=0; i<n; ++i)
{
double s;
@@ -3213,11 +3216,26 @@ void ParFiniteElementSpace::CopyProlongationAndRestriction(
else { P = new HypreParMatrix(*pfes->P); }
nonconf_P = true;
}
else if (perm != NULL)
{
HYPRE_BigInt glob_nrows = GlobalVSize();
HYPRE_BigInt glob_ncols = GlobalTrueVSize();
HYPRE_BigInt *col_starts = GetTrueDofOffsets();
HYPRE_BigInt *row_starts = GetDofOffsets();
P = new HypreParMatrix(MyComm, glob_nrows, glob_ncols, row_starts,
col_starts, perm_mat);
nonconf_P = true;
}
if (pfes->R != NULL)
{
if (perm) { R = Mult(*pfes->R, *perm_mat_tr); }
else { R = new SparseMatrix(*pfes->R); }
}
else if (perm != NULL)
{
R = perm_mat_tr;
perm_mat_tr = NULL;
}
delete perm_mat;
delete perm_mat_tr;
+21
View File
@@ -481,6 +481,27 @@ void ParGridFunction::GetVectorValue(ElementTransformation &T,
}
}
void ParGridFunction::GetDerivative(int comp, int der_comp,
ParGridFunction &der)
{
Array<int> overlap;
AccumulateAndCountDerivativeValues(comp, der_comp, der, overlap);
// Count the zones globally.
GroupCommunicator &gcomm = der.ParFESpace()->GroupComm();
gcomm.Reduce<int>(overlap, GroupCommunicator::Sum);
gcomm.Bcast(overlap);
// Accumulate for all dofs.
gcomm.Reduce<double>(der.HostReadWrite(), GroupCommunicator::Sum);
gcomm.Bcast<double>(der.HostReadWrite());
for (int i = 0; i < overlap.Size(); i++)
{
der(i) /= overlap[i];
}
}
void ParGridFunction::GetElementDofValues(int el, Vector &dof_vals) const
{
int ne = fes->GetNE();
+3
View File
@@ -226,6 +226,9 @@ public:
const IntegrationPoint &ip,
Vector &val, Vector *tr = NULL) const;
/// Parallel version of GridFunction::GetDerivative(); see its documentation.
void GetDerivative(int comp, int der_comp, ParGridFunction &der);
/** Sets the output vector @a dof_vals to the values of the degrees of
freedom of element @a el. If @a el is greater than or equal to the number
of local elements, it will be interpreted as a shifted index of a face
-2
View File
@@ -40,8 +40,6 @@ StaticCondensation::StaticCondensation(FiniteElementSpace *fespace)
#endif
S = S_e = NULL;
symm = false;
A_data.Reset();
A_ipiv.Reset();
Array<int> vdofs;
const int NE = fes->GetNE();
+323 -38
View File
@@ -2326,6 +2326,8 @@ TMOP_Integrator::~TMOP_Integrator()
{
delete lim_func;
delete zeta;
delete sigma;
delete sigma_bar;
for (int i = 0; i < ElemDer.Size(); i++)
{
delete ElemDer[i];
@@ -2393,6 +2395,87 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
}
#endif
void TMOP_Integrator::EnableSurfaceFitting(const GridFunction &s0,
const Array<bool> &smarker,
Coefficient &coeff,
AdaptivityEvaluator &ae)
{
delete sigma;
sigma = new GridFunction(s0);
sigma_marker = &smarker;
coeff_sigma = &coeff;
sigma_eval = &ae;
// Compute the restricted sigma.
delete sigma_bar;
sigma_bar = new GridFunction(*sigma);
for (int i = 0; i < sigma_marker->Size(); i++)
{
if ((*sigma_marker)[i] == false) { (*sigma_bar)(i) = 0.0; }
}
sigma_eval->SetSerialMetaInfo(*s0.FESpace()->GetMesh(),
*s0.FESpace()->FEColl(), 1);
sigma_eval->SetInitialField
(*sigma->FESpace()->GetMesh()->GetNodes(), *sigma);
}
#ifdef MFEM_USE_MPI
void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0,
const Array<bool> &smarker,
Coefficient &coeff,
AdaptivityEvaluator &ae)
{
delete sigma;
sigma = new GridFunction(s0);
sigma_marker = &smarker;
coeff_sigma = &coeff;
sigma_eval = &ae;
// Compute the restricted sigma.
delete sigma_bar;
sigma_bar = new GridFunction(*sigma);
for (int i = 0; i < sigma_marker->Size(); i++)
{
if ((*sigma_marker)[i] == false) { (*sigma_bar)(i) = 0.0; }
}
sigma_eval->SetParMetaInfo(*s0.ParFESpace()->GetParMesh(),
*s0.ParFESpace()->FEColl(), 1);
sigma_eval->SetInitialField
(*sigma->FESpace()->GetMesh()->GetNodes(), *sigma);
}
#endif
void TMOP_Integrator::GetSurfaceFittingErrors(double &err_avg, double &err_max)
{
MFEM_VERIFY(sigma, "Surface fitting has not been enabled.");
int loc_cnt = 0;
double loc_max = 0.0, loc_sum = 0.0;
for (int i = 0; i < sigma_marker->Size(); i++)
{
if ((*sigma_marker)[i] == true)
{
loc_cnt++;
loc_max = std::max(loc_max, std::abs((*sigma_bar)(i)));
loc_sum += std::abs((*sigma_bar)(i));
}
}
err_avg = loc_sum / loc_cnt;
err_max = loc_max;
#ifdef MFEM_USE_MPI
if (targetC->Parallel() == false) { return; }
int glob_cnt;
MPI_Comm comm = targetC->GetComm();
MPI_Allreduce(&loc_max, &err_max, 1, MPI_DOUBLE, MPI_MAX, comm);
MPI_Allreduce(&loc_cnt, &glob_cnt, 1, MPI_INT, MPI_SUM, comm);
MPI_Allreduce(&loc_sum, &err_avg, 1, MPI_DOUBLE, MPI_SUM, comm);
err_avg = err_avg / glob_cnt;
#endif
}
void TMOP_Integrator::UpdateAfterMeshTopologyChange()
{
if (zeta)
@@ -2419,16 +2502,19 @@ void TMOP_Integrator::ParUpdateAfterMeshTopologyChange()
}
#endif
double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
ElementTransformation &T,
const Vector &elfun)
{
const int dof = el.GetDof(), dim = el.GetDim();
const int el_id = T.ElementNo;
double energy;
// No adaptive limiting terms if this is a FD computation.
// No adaptive limiting / surface fitting terms if the function is called
// as part of a FD derivative computation (because we include the exact
// derivatives of these terms in FD computations).
const bool adaptive_limiting = (zeta && fd_call_flag == false);
const bool surface_fit = (sigma && fd_call_flag == false);
DSh.SetSize(dof, dim);
Jrt.SetSize(dim);
@@ -2440,7 +2526,7 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
energy = 0.0;
DenseTensor Jtr(dim, dim, ir.GetNPoints());
targetC->ComputeElementTargets(T.ElementNo, el, ir, elfun, Jtr);
targetC->ComputeElementTargets(el_id, el, ir, elfun, Jtr);
// Limited case.
Vector shape, p, p0, d_vals;
@@ -2453,11 +2539,11 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
pos0.SetSize(dof, dim);
Vector pos0V(pos0.Data(), dof * dim);
Array<int> pos_dofs;
nodes0->FESpace()->GetElementVDofs(T.ElementNo, pos_dofs);
nodes0->FESpace()->GetElementVDofs(el_id, pos_dofs);
nodes0->GetSubVector(pos_dofs, pos0V);
if (lim_dist)
{
lim_dist->GetValues(T.ElementNo, ir, d_vals);
lim_dist->GetValues(el_id, ir, d_vals);
}
else
{
@@ -2467,11 +2553,11 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
// Define ref->physical transformation, when a Coefficient is specified.
IsoparametricTransformation *Tpr = NULL;
if (coeff1 || coeff0 || adaptive_limiting)
if (coeff1 || coeff0 || adaptive_limiting || surface_fit)
{
Tpr = new IsoparametricTransformation;
Tpr->SetFE(&el);
Tpr->ElementNo = T.ElementNo;
Tpr->ElementNo = el_id;
Tpr->ElementType = ElementTransformation::ELEMENT;
Tpr->Attribute = T.Attribute;
Tpr->GetPointMat().Transpose(PMatI); // PointMat = PMatI^T
@@ -2487,13 +2573,17 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
Vector zeta_q, zeta0_q;
if (adaptive_limiting)
{
zeta->GetValues(T.ElementNo, ir, zeta_q);
zeta_0->GetValues(T.ElementNo, ir, zeta0_q);
zeta->GetValues(el_id, ir, zeta_q);
zeta_0->GetValues(el_id, ir, zeta0_q);
}
Vector sigma_bar_q;
if (surface_fit) { sigma_bar->GetValues(el_id, ir, sigma_bar_q); }
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
const DenseMatrix &Jtr_i = Jtr(i);
metric->SetTargetJacobian(Jtr_i);
CalcInverse(Jtr_i, Jrt);
@@ -2516,16 +2606,24 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
coeff0->Eval(*Tpr, ip);
}
// Contribution from the adaptive limiting term.
if (adaptive_limiting)
{
const double diff = zeta_q(i) - zeta0_q(i);
val += coeff_zeta->Eval(*Tpr, ip) * lim_normal * diff * diff;
}
// Contribution from the surface fitting term.
if (surface_fit)
{
val += coeff_sigma->Eval(*Tpr, ip) * sigma_normal *
sigma_bar_q(i) * sigma_bar_q(i);
}
energy += weight * val;
}
delete Tpr;
delete Tpr;
return energy;
}
@@ -2747,7 +2845,7 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
// Define ref->physical transformation, when a Coefficient is specified.
IsoparametricTransformation *Tpr = NULL;
if (coeff1 || coeff0 || zeta || exact_action)
if (coeff1 || coeff0 || zeta || sigma || exact_action)
{
Tpr = new IsoparametricTransformation;
Tpr->SetFE(&el);
@@ -2829,7 +2927,8 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
}
}
if (zeta) { AssembleElemVecAdaptLim(el, weights, *Tpr, ir, PMatO); }
if (zeta) { AssembleElemVecAdaptLim(el, *Tpr, ir, weights, PMatO); }
if (sigma) { AssembleElemVecSurfFit(el, *Tpr, ir, weights, PMatO); }
delete Tpr;
}
@@ -2881,7 +2980,7 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
// Define ref->physical transformation, when a Coefficient is specified.
IsoparametricTransformation *Tpr = NULL;
if (coeff1 || coeff0 || zeta)
if (coeff1 || coeff0 || zeta || sigma)
{
Tpr = new IsoparametricTransformation;
Tpr->SetFE(&el);
@@ -2935,21 +3034,20 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
}
}
if (zeta) { AssembleElemGradAdaptLim(el, weights, *Tpr, ir, elmat); }
if (zeta) { AssembleElemGradAdaptLim(el, *Tpr, ir, weights, elmat); }
if (sigma) { AssembleElemGradSurfFit(el, *Tpr, ir, weights, elmat); }
delete Tpr;
}
void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el,
const Vector &weights,
IsoparametricTransformation &Tpr,
const IntegrationRule &ir,
const Vector &weights,
DenseMatrix &mat)
{
if (zeta == NULL) { return; }
const int dof = el.GetDof(), dim = el.GetDim();
Vector shape(dof), zeta_e, zeta_q, zeta0_q;
const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size();
Vector shape(dof), zeta_e, zeta_q, zeta0_q(nqp);
Array<int> dofs;
zeta->FESpace()->GetElementDofs(Tpr.ElementNo, dofs);
@@ -2967,7 +3065,6 @@ void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el,
Vector zeta_grad_q(dim);
const int nqp = weights.Size();
for (int q = 0; q < nqp; q++)
{
const IntegrationPoint &ip = ir.IntPoint(q);
@@ -2980,15 +3077,13 @@ void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el,
}
void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el,
const Vector &weights,
IsoparametricTransformation &Tpr,
const IntegrationRule &ir,
const Vector &weights,
DenseMatrix &mat)
{
if (zeta == NULL) { return; }
const int dof = el.GetDof(), dim = el.GetDim();
Vector shape(dof), zeta_e, zeta_q, zeta0_q;
const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size();
Vector shape(dof), zeta_e, zeta_q, zeta0_q(nqp);
Array<int> dofs;
zeta->FESpace()->GetElementDofs(Tpr.ElementNo, dofs);
@@ -3014,7 +3109,6 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el,
Vector zeta_grad_q(dim);
DenseMatrix zeta_grad_grad_q(dim, dim);
const int nqp = weights.Size();
for (int q = 0; q < nqp; q++)
{
const IntegrationPoint &ip = ir.IntPoint(q);
@@ -3043,6 +3137,169 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el,
}
}
void TMOP_Integrator::AssembleElemVecSurfFit(const FiniteElement &el_x,
IsoparametricTransformation &Tpr,
const IntegrationRule &ir_quad,
const Vector &weights,
DenseMatrix &mat)
{
const int el_id = Tpr.ElementNo;
const FiniteElement &el_s = *sigma->FESpace()->GetFE(el_id);
const int dof_x = el_x.GetDof(), dim = el_x.GetDim(),
dof_s = el_s.GetDof(), nqp = ir_quad.GetNPoints();
Vector sigma_e, sigma_bar_e;
Vector sigma_bar_q;
Array<int> dofs;
sigma->FESpace()->GetElementDofs(el_id, dofs);
sigma->GetSubVector(dofs, sigma_e);
sigma_bar->GetSubVector(dofs, sigma_bar_e);
sigma_bar->GetValues(el_id, ir_quad, sigma_bar_q);
// Project the gradient of sigma in the same space.
// The FE coefficients of the gradient go in sigma_grad_e.
DenseMatrix sigma_grad_e(dof_s, dim);
DenseMatrix grad_phys; // This will be (dof x dim, dof).
el_s.ProjectGrad(el_s, Tpr, grad_phys);
Vector grad_ptr(sigma_grad_e.GetData(), dof_s * dim);
grad_phys.Mult(sigma_e, grad_ptr);
// Gradient of sigma_bar.
DenseMatrix sigma_bar_grad_e(dof_s, dim);
Vector ptr(sigma_bar_grad_e.GetData(), dof_s * dim);
grad_phys.Mult(sigma_bar_e, ptr);
Vector shape_x(dof_x), shape_s(dof_s), grad_q(dim);
for (int q = 0; q < nqp; q++)
{
const IntegrationPoint &ip = ir_quad.IntPoint(q);
Tpr.SetIntPoint(&ip);
el_s.CalcShape(ip, shape_s);
// Grad of sigma_bar at the current quad point.
sigma_bar_grad_e.MultTranspose(shape_s, grad_q);
for (int s = 0; s < dof_s; s++)
{
if ((*sigma_marker)[dofs[s]] == false) { continue; }
for (int d = 0; d < dim; d++)
{
// Grad of sigma must be taken at the active DOFs.
grad_q(d) += sigma_grad_e(s, d) * shape_s(s);
}
}
grad_q *= 2.0 * sigma_normal * coeff_sigma->Eval(Tpr, ip) *
weights(q) * sigma_bar_q(q);
el_x.CalcShape(ip, shape_x);
AddMultVWt(shape_x, grad_q, mat);
}
}
void TMOP_Integrator::AssembleElemGradSurfFit(const FiniteElement &el_x,
IsoparametricTransformation &Tpr,
const IntegrationRule &ir_quad,
const Vector &weights,
DenseMatrix &mat)
{
const int el_id = Tpr.ElementNo, nqp = ir_quad.GetNPoints();
const FiniteElement &el_s = *sigma->FESpace()->GetFE(el_id);
const int dof_x = el_x.GetDof(), dim = el_x.GetDim(),
dof_s = el_s.GetDof();
Vector sigma_e, sigma_bar_e;
Vector sigma_bar_q;
Array<int> dofs;
sigma->FESpace()->GetElementDofs(el_id, dofs);
sigma->GetSubVector(dofs, sigma_e);
sigma_bar->GetSubVector(dofs, sigma_bar_e);
sigma_bar->GetValues(el_id, ir_quad, sigma_bar_q);
// Project the gradient of sigma in the same space.
// The FE coefficients of the gradient go in sigma_grad_e.
DenseMatrix sigma_grad_e(dof_s, dim);
DenseMatrix grad_phys; // This will be (dof x dim, dof).
el_s.ProjectGrad(el_s, Tpr, grad_phys);
Vector grad_ptr(sigma_grad_e.GetData(), dof_s * dim);
grad_phys.Mult(sigma_e, grad_ptr);
// Gradient of sigma_bar.
DenseMatrix sigma_bar_grad_e(dof_s, dim);
Vector ptr(sigma_bar_grad_e.GetData(), dof_s * dim);
grad_phys.Mult(sigma_bar_e, ptr);
// Project the gradient of each gradient of sigma in the same space.
// The FE coefficients of the second derivatives go in sigma_grad_grad_e.
DenseMatrix sigma_grad_grad_e(dof_s * dim, dim);
Mult(grad_phys, sigma_grad_e, sigma_grad_grad_e);
// Project the gradient of each gradient of sigma in the same space.
// The FE coefficients of the second derivatives go in sigma_grad_grad_e.
DenseMatrix sigma_bar_grad_grad_e(dof_s * dim, dim);
Mult(grad_phys, sigma_bar_grad_e, sigma_bar_grad_grad_e);
// Reshape to be more convenient later (no change in the data).
sigma_bar_grad_grad_e.SetSize(dof_s, dim * dim);
DenseMatrix sigma_bar_grad_grad_q(dim, dim);
Vector shape_x(dof_x), shape_s(dof_s), sigma_bar_grad_q(dim);
DenseMatrix dshape_s(dof_s, dim);
for (int q = 0; q < nqp; q++)
{
const IntegrationPoint &ip = ir_quad.IntPoint(q);
Tpr.SetIntPoint(&ip);
el_s.CalcShape(ip, shape_s);
el_x.CalcShape(ip, shape_x);
// We could reuse grad_phys, but this is more accurate.
el_s.CalcPhysDShape(Tpr, dshape_s);
// Grad of sigma_bar at the current quad point.
sigma_bar_grad_e.MultTranspose(shape_s, sigma_bar_grad_q);
// Grad-grad of sigma_bar at the current quad point.
Vector gg_ptr(sigma_bar_grad_grad_q.GetData(), dim * dim);
sigma_bar_grad_grad_e.MultTranspose(shape_s, gg_ptr);
// Loops over the local matrix.
const double w = 2.0 * sigma_normal *
coeff_sigma->Eval(Tpr, ip) * weights(q);
for (int i = 0; i < dof_x * dim; i++)
{
const int idof = i % dof_x, idim = i / dof_x;
for (int j = 0; j <= i; j++)
{
const int jdof = j % dof_x, jdim = j / dof_x;
double Di = sigma_bar_grad_q(idim),
Dj = sigma_bar_grad_q(jdim),
DD = sigma_bar_grad_grad_q(idim, jdim);
for (int s = 0; s < dof_s; s++)
{
if ((*sigma_marker)[dofs[s]] == false) { continue; }
Di += sigma_grad_e(s, idim) * shape_s(s);
Dj += sigma_grad_e(s, jdim) * shape_s(s);
DD += sigma_grad_e(s, idim) * dshape_s(s, jdim) +
sigma_grad_grad_e(dof_s * idim + s, jdim) * shape_s(s) +
sigma_grad_e(s, jdim) * dshape_s(s, idim);
}
const double entry = w * (Di * Dj + sigma_bar_q(q) * DD) *
shape_x(idof) * shape_x(jdof);
mat(i, j) += entry;
if (i != j) { mat(j, i) += entry; }
}
}
}
}
double TMOP_Integrator::GetFDDerivative(const FiniteElement &el,
ElementTransformation &T,
Vector &elfun, const int dofidx,
@@ -3103,8 +3360,8 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el,
}
fd_call_flag = false;
// Contributions from adaptive limiting (exact derivatives).
if (zeta)
// Contributions from adaptive limiting, surface fitting (exact derivatives).
if (zeta || sigma)
{
const IntegrationRule &ir = ActionIntegrationRule(el);
const int nqp = ir.GetNPoints();
@@ -3125,7 +3382,8 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el,
}
PMatO.UseExternalData(elvect.GetData(), dof, dim);
AssembleElemVecAdaptLim(el, weights, Tpr, ir, PMatO);
if (zeta) { AssembleElemVecAdaptLim(el, Tpr, ir, weights, PMatO); }
if (sigma) { AssembleElemVecSurfFit(el, Tpr, ir, weights, PMatO); }
}
}
@@ -3200,7 +3458,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el,
fd_call_flag = false;
// Contributions from adaptive limiting.
if (zeta)
if (zeta || sigma)
{
const IntegrationRule &ir = GradientIntegrationRule(el);
const int nqp = ir.GetNPoints();
@@ -3220,35 +3478,41 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el,
weights(q) = ir.IntPoint(q).weight * Jtr(q).Det();
}
AssembleElemGradAdaptLim(el, weights, Tpr, ir, elmat);
if (zeta) { AssembleElemGradAdaptLim(el, Tpr, ir, weights, elmat); }
if (sigma) { AssembleElemGradSurfFit(el, Tpr, ir, weights, elmat); }
}
}
void TMOP_Integrator::EnableNormalization(const GridFunction &x)
{
ComputeNormalizationEnergies(x, metric_normal, lim_normal);
ComputeNormalizationEnergies(x, metric_normal, lim_normal, sigma_normal);
metric_normal = 1.0 / metric_normal;
lim_normal = 1.0 / lim_normal;
//if (sigma) { sigma_normal = 1.0 / sigma_normal; }
if (sigma) { sigma_normal = lim_normal; }
}
#ifdef MFEM_USE_MPI
void TMOP_Integrator::ParEnableNormalization(const ParGridFunction &x)
{
double loc[2];
ComputeNormalizationEnergies(x, loc[0], loc[1]);
double rdc[2];
MPI_Allreduce(loc, rdc, 2, MPI_DOUBLE, MPI_SUM, x.ParFESpace()->GetComm());
double loc[3];
ComputeNormalizationEnergies(x, loc[0], loc[1], loc[2]);
double rdc[3];
MPI_Allreduce(loc, rdc, 3, MPI_DOUBLE, MPI_SUM, x.ParFESpace()->GetComm());
metric_normal = 1.0 / rdc[0];
lim_normal = 1.0 / rdc[1];
// if (sigma) { sigma_normal = 1.0 / rdc[2]; }
if (sigma) { sigma_normal = lim_normal; }
}
#endif
void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
double &metric_energy,
double &lim_energy)
double &lim_energy,
double &sigma_energy)
{
Array<int> vdofs;
Vector x_vals;
Vector x_vals, sigma_bar_q;
const FiniteElementSpace* const fes = x.FESpace();
const int dim = fes->GetMesh()->Dimension();
@@ -3258,6 +3522,7 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
metric_energy = 0.0;
lim_energy = 0.0;
sigma_energy = 0.0;
for (int i = 0; i < fes->GetNE(); i++)
{
const FiniteElement *fe = fes->GetFE(i);
@@ -3273,6 +3538,8 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
targetC->ComputeElementTargets(i, *fe, ir, x_vals, Jtr);
if (sigma) { sigma_bar->GetValues(i, ir, sigma_bar_q); }
for (int q = 0; q < nqp; q++)
{
const IntegrationPoint &ip = ir.IntPoint(q);
@@ -3286,8 +3553,15 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
metric_energy += weight * metric->EvalW(Jpt);
lim_energy += weight;
// Normalization of the surface fitting term.
if (sigma)
{
sigma_energy += weight * sigma_bar_q(q) * sigma_bar_q(q);
}
}
}
if (targetC->ContainsVolumeInfo() == false)
{
// Special case when the targets don't contain volumetric information.
@@ -3336,6 +3610,17 @@ void TMOP_Integrator::UpdateAfterMeshPositionChange(const Vector &new_x)
}
// Update zeta if adaptive limiting is enabled.
if (zeta) { adapt_eval->ComputeAtNewPosition(new_x, *zeta); }
// Update sigma if surface fitting is enabled.
if (sigma)
{
sigma_eval->ComputeAtNewPosition(new_x, *sigma);
// Update the restricted sigma.
for (int i = 0; i < sigma_marker->Size(); i++)
{
(*sigma_bar)(i) = ((*sigma_marker)[i] == true) ? (*sigma)(i) : 0.0;
}
}
}
void TMOP_Integrator::ComputeFDh(const Vector &x, const FiniteElementSpace &fes)
+105 -12
View File
@@ -592,6 +592,27 @@ public:
virtual int Id() const { return 321; }
};
/// 3D barrier Shape+Size (VS) metric (polyconvex).
class TMOP_Metric_328 : public TMOP_Combo_QualityMetric
{
protected:
mutable InvariantsEvaluator2D<double> ie;
double gamma;
TMOP_QualityMetric *sh_metric, *sz_metric;
public:
TMOP_Metric_328(double gamma_) : gamma(gamma_),
sh_metric(new TMOP_Metric_301),
sz_metric(new TMOP_Metric_316)
{
// (1-gamma) mu_301 + gamma mu_316
AddQualityMetric(sh_metric, 1.-gamma_);
AddQualityMetric(sz_metric, gamma_);
}
virtual ~TMOP_Metric_328() { delete sh_metric; delete sz_metric; }
};
/// 3D barrier Shape+Size (VS) metric (polyconvex).
class TMOP_Metric_332 : public TMOP_Combo_QualityMetric
{
@@ -619,6 +640,7 @@ public:
class TMOP_Metric_333 : public TMOP_Combo_QualityMetric
{
protected:
mutable InvariantsEvaluator2D<double> ie;
double gamma;
TMOP_QualityMetric *sh_metric, *sz_metric;
@@ -632,12 +654,30 @@ public:
AddQualityMetric(sz_metric, gamma_);
}
virtual int Id() const { return 333; }
double GetGamma() const { return gamma; }
virtual ~TMOP_Metric_333() { delete sh_metric; delete sz_metric; }
};
/// 3D barrier Shape+Size (VS) metric (polyconvex).
class TMOP_Metric_334 : public TMOP_Combo_QualityMetric
{
protected:
mutable InvariantsEvaluator2D<double> ie;
double gamma;
TMOP_QualityMetric *sh_metric, *sz_metric;
public:
TMOP_Metric_334(double gamma_) : gamma(gamma_),
sh_metric(new TMOP_Metric_303),
sz_metric(new TMOP_Metric_316)
{
// (1-gamma) mu_303 + gamma mu_316
AddQualityMetric(sh_metric, 1.-gamma_);
AddQualityMetric(sz_metric, gamma_);
}
virtual ~TMOP_Metric_334() { delete sh_metric; delete sz_metric; }
};
/// Shifted barrier form of 3D metric 16 (volume, ideal barrier metric), 3D
class TMOP_Metric_352 : public TMOP_QualityMetric
{
@@ -897,9 +937,6 @@ protected:
#ifdef MFEM_USE_MPI
MPI_Comm comm;
bool Parallel() const { return (comm != MPI_COMM_NULL); }
#else
bool Parallel() const { return false; }
#endif
// should be called only if avg_volume == 0.0, i.e. avg_volume is not
@@ -936,6 +973,13 @@ public:
#endif
virtual ~TargetConstructor() { }
#ifdef MFEM_USE_MPI
bool Parallel() const { return (comm != MPI_COMM_NULL); }
MPI_Comm GetComm() const { return comm; }
#else
bool Parallel() const { return false; }
#endif
/** @brief Set the nodes to be used in the target-matrix construction.
This method should be called every time the target nodes are updated
@@ -1296,6 +1340,13 @@ protected:
Coefficient *coeff_zeta; // Not owned.
AdaptivityEvaluator *adapt_eval; // Not owned.
// Surface fitting.
GridFunction *sigma, *sigma_bar; // Owned. Updated by sigma_eval.
const Array<bool> *sigma_marker; // Not owned.
Coefficient *coeff_sigma; // Not owned.
AdaptivityEvaluator *sigma_eval; // Not owned.
double sigma_normal;
DiscreteAdaptTC *discr_tc;
// Parameters for FD-based Gradient & Hessian calculation.
@@ -1364,7 +1415,8 @@ protected:
} PA;
void ComputeNormalizationEnergies(const GridFunction &x,
double &metric_energy, double &lim_energy);
double &metric_energy, double &lim_energy,
double &sigma_energy);
void AssembleElementVectorExact(const FiniteElement &el,
ElementTransformation &T,
@@ -1383,12 +1435,25 @@ protected:
ElementTransformation &T,
const Vector &elfun, DenseMatrix &elmat);
void AssembleElemVecAdaptLim(const FiniteElement &el, const Vector &weights,
void AssembleElemVecAdaptLim(const FiniteElement &el,
IsoparametricTransformation &Tpr,
const IntegrationRule &ir, DenseMatrix &m);
void AssembleElemGradAdaptLim(const FiniteElement &el, const Vector &weights,
const IntegrationRule &ir,
const Vector &weights, DenseMatrix &mat);
void AssembleElemGradAdaptLim(const FiniteElement &el,
IsoparametricTransformation &Tpr,
const IntegrationRule &ir, DenseMatrix &m);
const IntegrationRule &ir,
const Vector &weights, DenseMatrix &m);
// First derivative of the surface fitting term.
void AssembleElemVecSurfFit(const FiniteElement &el_x,
IsoparametricTransformation &Tpr,
const IntegrationRule &ir_quad,
const Vector &weights, DenseMatrix &mat);
// Second derivative of the surface fitting term.
void AssembleElemGradSurfFit(const FiniteElement &el_x,
IsoparametricTransformation &Tpr,
const IntegrationRule &ir_quad,
const Vector &weights, DenseMatrix &mat);
double GetFDDerivative(const FiniteElement &el,
ElementTransformation &T,
@@ -1470,6 +1535,8 @@ public:
nodes0(NULL), coeff0(NULL),
lim_dist(NULL), lim_func(NULL), lim_normal(1.0),
zeta_0(NULL), zeta(NULL), coeff_zeta(NULL), adapt_eval(NULL),
sigma(NULL), sigma_bar(NULL), sigma_marker(NULL), coeff_sigma(NULL),
sigma_eval(NULL), sigma_normal(1.0),
discr_tc(dynamic_cast<DiscreteAdaptTC *>(tc)),
fdflag(false), dxscale(1.0e3), fd_call_flag(false), exact_action(false)
{ PA.enabled = false; }
@@ -1522,7 +1589,7 @@ public:
Adds the term @f$ \int c (z(x) - z_0(x_0))^2 @f$, where z0(x0) is a given
function on the starting mesh, and z(x) is its image on the new mesh.
Minimizing this, means that a node at x0 is allowed to move to a
Minimizing this term means that a node at x0 is allowed to move to a
position x(x0) only if z(x) ~ z0(x0).
Such term can be used for tangential mesh relaxation.
@@ -1537,6 +1604,32 @@ public:
AdaptivityEvaluator &ae);
#endif
/** @brief Fitting of certain DOFs to the zero level set of a function.
Having a level set function s0(x0) on the starting mesh, and a set of
marked nodes (or DOFs), we move these nodes to the zero level set of s0.
If s(x) is the image of s0(x0) on the current mesh, this function adds to
the TMOP functional the term @f$ \int c \bar{s}(x))^2 @f$, where
@f$\bar{s}(x)@f$ is the restriction of s(x) on the aligned DOFs.
Minimizing this term means that a marked node at x0 is allowed to move to
a position x(x0) only if s(x) ~ 0.
Such term can be used for surface fitting and tangential relaxation.
@param[in] s0 The level set function on the initial mesh.
@param[in] smarker Indicates which DOFs will be aligned.
@param[in] coeff Coefficient c for the above integral.
@param[in] ae AdaptivityEvaluator to compute s(x) from s0(x0). */
void EnableSurfaceFitting(const GridFunction &s0,
const Array<bool> &smarker, Coefficient &coeff,
AdaptivityEvaluator &ae);
#ifdef MFEM_USE_MPI
/// Parallel support for surface fitting.
void EnableSurfaceFitting(const ParGridFunction &s0,
const Array<bool> &smarker, Coefficient &coeff,
AdaptivityEvaluator &ae);
#endif
void GetSurfaceFittingErrors(double &err_avg, double &err_max);
/// Update the original/reference nodes used for limiting.
void SetLimitingNodes(const GridFunction &n0) { nodes0 = &n0; }
+8 -6
View File
@@ -394,12 +394,13 @@ bool TMOPDeRefinerEstimator::GetDerefineEnergyForIntegrator(
const CoarseFineTransformations &dtrans =
meshcopy.ncmesh->GetDerefinementTransforms();
Table coarse_to_fine;
dtrans.GetCoarseToFineMap(meshcopy, coarse_to_fine);
Table coarse_to_fine;
dtrans.MakeCoarseToFineTable(coarse_to_fine);
Array<int> tabrow;
for (int pe = 0; pe < coarse_to_fine.Size(); pe++)
{
Array<int> tabrow;
coarse_to_fine.GetRow(pe, tabrow);
int nchild = tabrow.Size();
double parent_energy = coarse_energy(pe);
@@ -446,12 +447,13 @@ bool TMOPDeRefinerEstimator::GetDerefineEnergyForIntegrator(
const CoarseFineTransformations &dtrans =
meshcopy.pncmesh->GetDerefinementTransforms();
Table coarse_to_fine;
dtrans.GetCoarseToFineMap(meshcopy, coarse_to_fine);
Table coarse_to_fine;
dtrans.MakeCoarseToFineTable(coarse_to_fine);
Array<int> tabrow;
for (int pe = 0; pe < meshcopy.GetNE(); pe++)
{
Array<int> tabrow;
coarse_to_fine.GetRow(pe, tabrow);
int nchild = tabrow.Size();
double parent_energy = coarse_energy(pe);
+4
View File
@@ -70,6 +70,10 @@ public:
explicit inline Array(int asize)
: size(asize) { asize > 0 ? data.New(asize) : data.Reset(); }
/// Creates array of @a asize elements with a given MemoryType
inline Array(int asize, MemoryType mt)
: size(asize) { asize > 0 ? data.New(asize, mt) : data.Reset(mt); }
/** @brief Creates array using an existing c-array of asize elements;
allocsize is set to -asize to indicate that the data will not
be deleted. */
+149 -12
View File
@@ -183,6 +183,42 @@ void RajaCuWrap3D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(cudaGetLastError());
}
template <int Dim>
struct RajaCuWrap;
template <>
struct RajaCuWrap<1>
{
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaCuWrap1D<BLCK>(N, d_body);
}
};
template <>
struct RajaCuWrap<2>
{
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaCuWrap2D(N, d_body, X, Y, Z);
}
};
template <>
struct RajaCuWrap<3>
{
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaCuWrap3D(N, d_body, X, Y, Z, G);
}
};
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP)
@@ -248,6 +284,43 @@ void RajaHipWrap3D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(hipGetLastError());
}
template <int Dim>
struct RajaHipWrap;
template <>
struct RajaHipWrap<1>
{
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaHipWrap1D<BLCK>(N, d_body);
}
};
template <>
struct RajaHipWrap<2>
{
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaHipWrap2D(N, d_body, X, Y, Z);
}
};
template <>
struct RajaHipWrap<3>
{
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaHipWrap3D(N, d_body, X, Y, Z, G);
}
};
#endif
/// RAJA OpenMP backend
@@ -333,6 +406,42 @@ void CuWrap3D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(cudaGetLastError());
}
template <int Dim>
struct CuWrap;
template <>
struct CuWrap<1>
{
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
CuWrap1D<BLCK>(N, d_body);
}
};
template <>
struct CuWrap<2>
{
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
CuWrap2D(N, d_body, X, Y, Z);
}
};
template <>
struct CuWrap<3>
{
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
CuWrap3D(N, d_body, X, Y, Z, G);
}
};
#endif // MFEM_USE_CUDA
@@ -392,6 +501,42 @@ void HipWrap3D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(hipGetLastError());
}
template <int Dim>
struct HipWrap;
template <>
struct HipWrap<1>
{
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
HipWrap1D<BLCK>(N, d_body);
}
};
template <>
struct HipWrap<2>
{
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
HipWrap2D(N, d_body, X, Y, Z);
}
};
template <>
struct HipWrap<3>
{
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
HipWrap3D(N, d_body, X, Y, Z, G);
}
};
#endif // MFEM_USE_HIP
@@ -413,9 +558,7 @@ inline void ForallWrap(const bool use_dev, const int N,
// If Backend::RAJA_CUDA is allowed, use it
if (Device::Allows(Backend::RAJA_CUDA))
{
if (DIM == 1) { return RajaCuWrap1D(N, d_body); }
if (DIM == 2) { return RajaCuWrap2D(N, d_body, X, Y, Z); }
if (DIM == 3) { return RajaCuWrap3D(N, d_body, X, Y, Z, G); }
return RajaCuWrap<DIM>::run(N, d_body, X, Y, Z, G);
}
#endif
@@ -423,9 +566,7 @@ inline void ForallWrap(const bool use_dev, const int N,
// If Backend::RAJA_HIP is allowed, use it
if (Device::Allows(Backend::RAJA_HIP))
{
if (DIM == 1) { return RajaHipWrap1D(N, d_body); }
if (DIM == 2) { return RajaHipWrap2D(N, d_body, X, Y, Z); }
if (DIM == 3) { return RajaHipWrap3D(N, d_body, X, Y, Z, G); }
return RajaHipWrap<DIM>::run(N, d_body, X, Y, Z, G);
}
#endif
@@ -433,9 +574,7 @@ inline void ForallWrap(const bool use_dev, const int N,
// If Backend::CUDA is allowed, use it
if (Device::Allows(Backend::CUDA))
{
if (DIM == 1) { return CuWrap1D(N, d_body); }
if (DIM == 2) { return CuWrap2D(N, d_body, X, Y, Z); }
if (DIM == 3) { return CuWrap3D(N, d_body, X, Y, Z, G); }
return CuWrap<DIM>::run(N, d_body, X, Y, Z, G);
}
#endif
@@ -443,9 +582,7 @@ inline void ForallWrap(const bool use_dev, const int N,
// If Backend::HIP is allowed, use it
if (Device::Allows(Backend::HIP))
{
if (DIM == 1) { return HipWrap1D(N, d_body); }
if (DIM == 2) { return HipWrap2D(N, d_body, X, Y, Z); }
if (DIM == 3) { return HipWrap3D(N, d_body, X, Y, Z, G); }
return HipWrap<DIM>::run(N, d_body, X, Y, Z, G);
}
#endif
+5 -4
View File
@@ -175,8 +175,9 @@ protected:
// Copy{From,To}, {ReadWrite,Read,Write}.
public:
/// Default constructor: no initialization.
Memory() { }
/** Default constructor, sets the host pointer to nullptr and the metadata to
meaningful default values. */
Memory() { Reset(); }
/// Copy constructor: default.
Memory(const Memory &orig) = default;
@@ -368,8 +369,7 @@ public:
be updated as described above. */
inline void SetDeviceMemoryType(MemoryType d_mt);
/** @brief Delete the owned pointers. The Memory is not reset by this method,
i.e. it will, generally, not be Empty() after this call. */
/** @brief Delete the owned pointers and reset the Memory object. */
inline void Delete();
/** @brief Delete the device pointer, if owned. If @a copy_to_host is true
@@ -986,6 +986,7 @@ inline void Memory<T>::Delete()
{
if (flags & OWNS_HOST) { delete [] h_ptr; }
}
Reset(h_mt);
}
template <typename T>
-4
View File
@@ -35,10 +35,6 @@ Table::Table(const Table &table)
I.CopyFrom(table.I, size+1);
J.CopyFrom(table.J, nnz);
}
else
{
I.Reset(); J.Reset();
}
}
Table& Table::operator=(const Table &rhs)
+2 -2
View File
@@ -53,7 +53,7 @@ protected:
public:
/// Creates an empty table
Table() { size = -1; I.Reset(); J.Reset(); }
Table() { size = -1; }
/// Copy constructor
Table(const Table &);
@@ -66,7 +66,7 @@ public:
/** Create a table from a list of connections, see MakeFromList(). */
Table(int nrows, Array<Connection> &list) : size(-1)
{ I.Reset(); J.Reset(); MakeFromList(nrows, list); }
{ MakeFromList(nrows, list); }
/** Create a table with one entry per row with column indices given
by 'partitioning'. */
+5
View File
@@ -604,11 +604,16 @@ void AmgXSolver::SetMatrix(const HypreParMatrix &A, const bool update_mat)
mfem_error("Hypre version 2.16+ is required when using AmgX \n");
#endif
// Ensure HypreParMatrix is on the host
A.HostRead();
hypre_ParCSRMatrix * A_ptr =
(hypre_ParCSRMatrix *)const_cast<HypreParMatrix&>(A);
hypre_CSRMatrix *A_csr = hypre_MergeDiagAndOffd(A_ptr);
A.HypreRead();
Array<double> loc_A(A_csr->data, (int)A_csr->num_nonzeros);
const Array<HYPRE_Int> loc_I(A_csr->i, (int)A_csr->num_rows+1);
+1 -20
View File
@@ -70,10 +70,7 @@ namespace mfem
using namespace std;
DenseMatrix::DenseMatrix() : Matrix(0)
{
data.Reset();
}
DenseMatrix::DenseMatrix() : Matrix(0) { }
DenseMatrix::DenseMatrix(const DenseMatrix &m) : Matrix(m.height, m.width)
{
@@ -84,10 +81,6 @@ DenseMatrix::DenseMatrix(const DenseMatrix &m) : Matrix(m.height, m.width)
data.New(hw);
std::memcpy(data, m.data, sizeof(double)*hw);
}
else
{
data.Reset();
}
}
DenseMatrix::DenseMatrix(int s) : Matrix(s)
@@ -98,10 +91,6 @@ DenseMatrix::DenseMatrix(int s) : Matrix(s)
data.New(s*s);
*this = 0.0; // init with zeroes
}
else
{
data.Reset();
}
}
DenseMatrix::DenseMatrix(int m, int n) : Matrix(m, n)
@@ -114,10 +103,6 @@ DenseMatrix::DenseMatrix(int m, int n) : Matrix(m, n)
data.New(capacity);
*this = 0.0; // init with zeroes
}
else
{
data.Reset();
}
}
DenseMatrix::DenseMatrix(const DenseMatrix &mat, char ch)
@@ -137,10 +122,6 @@ DenseMatrix::DenseMatrix(const DenseMatrix &mat, char ch)
}
}
}
else
{
data.Reset();
}
}
void DenseMatrix::SetSize(int h, int w)
-5
View File
@@ -753,7 +753,6 @@ public:
DenseTensor()
{
nk = 0;
tdata.Reset();
}
DenseTensor(int i, int j, int k)
@@ -787,10 +786,6 @@ public:
tdata.New(size, other.tdata.GetMemoryType());
tdata.CopyFrom(other.tdata, size);
}
else
{
tdata.Reset();
}
}
int SizeI() const { return Mk.Height(); }
+62 -11
View File
@@ -127,18 +127,19 @@ HypreParVector::HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size,
own_ParVector = 1;
}
HypreParVector::HypreParVector(const HypreParVector &y) : Vector()
// Call the move constructor on the "compatible" temp vector
HypreParVector::HypreParVector(const HypreParVector &y) : HypreParVector(
y.CreateCompatibleVector())
{
x = hypre_ParVectorCreate(y.x -> comm, y.x -> global_size,
y.x -> partitioning);
hypre_ParVectorInitialize(x);
#if MFEM_HYPRE_VERSION <= 22200
hypre_ParVectorSetPartitioningOwner(x,0);
#endif
hypre_ParVectorSetDataOwner(x,1);
hypre_SeqVectorSetDataOwner(hypre_ParVectorLocalVector(x),1);
_SetDataAndSize_();
own_ParVector = 1;
// Deep copy the local data
hypre_SeqVectorCopy(hypre_ParVectorLocalVector(y.x),
hypre_ParVectorLocalVector(x));
}
HypreParVector::HypreParVector(HypreParVector &&y)
{
own_ParVector = 0;
*this = std::move(y);
}
HypreParVector::HypreParVector(const HypreParMatrix &A,
@@ -178,6 +179,23 @@ HypreParVector::HypreParVector(ParFiniteElementSpace *pfes)
own_ParVector = 1;
}
HypreParVector HypreParVector::CreateCompatibleVector() const
{
HypreParVector result;
result.x = hypre_ParVectorCreate(x -> comm, x -> global_size,
x -> partitioning);
hypre_ParVectorInitialize(result.x);
#if MFEM_HYPRE_VERSION <= 22200
hypre_ParVectorSetPartitioningOwner(result.x,0);
#endif
hypre_ParVectorSetDataOwner(result.x,1);
hypre_SeqVectorSetDataOwner(hypre_ParVectorLocalVector(result.x),1);
result._SetDataAndSize_();
result.own_ParVector = 1;
return result;
}
void HypreParVector::WrapHypreParVector(hypre_ParVector *y, bool owner)
{
if (own_ParVector) { hypre_ParVectorDestroy(x); }
@@ -216,6 +234,18 @@ HypreParVector& HypreParVector::operator=(const HypreParVector &y)
return *this;
}
HypreParVector& HypreParVector::operator=(HypreParVector &&y)
{
// If the argument vector owns its data, then the calling vector will as well
WrapHypreParVector(static_cast<hypre_ParVector*>(y), y.own_ParVector);
// Either way the argument vector will no longer own its data
y.own_ParVector = 0;
y.x = nullptr;
y.data.Reset();
y.size = 0;
return *this;
}
void HypreParVector::SetData(double *data_)
{
hypre_VectorData(hypre_ParVectorLocalVector(x)) = data_;
@@ -303,6 +333,18 @@ void HypreParVector::Print(const char *fname) const
hypre_ParVectorPrint(x,fname);
}
void HypreParVector::Read(MPI_Comm comm, const char *fname)
{
if (own_ParVector)
{
hypre_ParVectorDestroy(x);
}
data.Delete();
x = hypre_ParVectorRead(comm, fname);
own_ParVector = true;
_SetDataAndSize_();
}
HypreParVector::~HypreParVector()
{
if (own_ParVector)
@@ -1561,9 +1603,16 @@ HypreParMatrix *HypreParMatrix::ExtractSubmatrix(const Array<int> &indices,
}
// Construct cpts_global array on hypre matrix structure
#if (MFEM_HYPRE_VERSION > 22300) || (MFEM_HYPRE_VERSION == 22300 && HYPRE_DEVELOP_NUMBER >=8)
HYPRE_BigInt cpts_global[2];
hypre_BoomerAMGCoarseParms(MPI_COMM_WORLD, local_num_vars, 1, NULL,
CF_marker, NULL, cpts_global);
#else
HYPRE_BigInt *cpts_global;
hypre_BoomerAMGCoarseParms(MPI_COMM_WORLD, local_num_vars, 1, NULL,
CF_marker, NULL, &cpts_global);
#endif
// Extract submatrix into *submat
#ifdef hypre_IntArrayData
@@ -1575,7 +1624,9 @@ HypreParMatrix *HypreParMatrix::ExtractSubmatrix(const Array<int> &indices,
"FF", &submat, threshold);
#endif
#if (MFEM_HYPRE_VERSION <= 22300) && !(MFEM_HYPRE_VERSION == 22300 && HYPRE_DEVELOP_NUMBER >=8)
mfem_hypre_TFree(cpts_global);
#endif
#ifdef hypre_IntArrayData
hypre_IntArrayDestroy(CF_marker);
#endif
+12 -1
View File
@@ -141,8 +141,10 @@ public:
allocated in the memory location HYPRE_MEMORY_DEVICE. */
HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size, double *data_,
HYPRE_BigInt *col, bool is_device_ptr = false);
/// Creates vector compatible with y
/// Creates a deep copy of @a y
HypreParVector(const HypreParVector &y);
/// Move constructor for HypreParVector. "Steals" data from its argument.
HypreParVector(HypreParVector&& other);
/// Creates vector compatible with (i.e. in the domain of) A or A^T
explicit HypreParVector(const HypreParMatrix &A, int transpose = 0);
/// Creates vector wrapping y
@@ -150,6 +152,10 @@ public:
/// Create a true dof parallel vector on a given ParFiniteElementSpace
explicit HypreParVector(ParFiniteElementSpace *pfes);
/// \brief Constructs a @p HypreParVector *compatible* with the calling vector
/// - meaning that it will be the same size and have the same partitioning.
HypreParVector CreateCompatibleVector() const;
/// MPI communicator
MPI_Comm GetComm() const { return x->comm; }
@@ -192,6 +198,8 @@ public:
HypreParVector& operator= (double d);
/// Define '=' for hypre vectors.
HypreParVector& operator= (const HypreParVector &y);
/// Move assignment
HypreParVector& operator= (HypreParVector &&y);
using Vector::Read;
@@ -252,6 +260,9 @@ public:
/// Prints the locally owned rows in parallel
void Print(const char *fname) const;
/// Reads a HypreParVector from files saved with HypreParVector::Print
void Read(MPI_Comm comm, const char *fname);
/// Calls hypre's destroy function
~HypreParVector();
+55 -2
View File
@@ -160,7 +160,7 @@ double Norml2(const int size, const T *data)
data of the input and output vectors. */
template<typename TA, typename TX, typename TY>
MFEM_HOST_DEVICE inline
void Mult(const int height, const int width, TA *data, const TX *x, TY *y)
void Mult(const int height, const int width, const TA *data, const TX *x, TY *y)
{
if (width == 0)
{
@@ -170,7 +170,7 @@ void Mult(const int height, const int width, TA *data, const TX *x, TY *y)
}
return;
}
TA *d_col = data;
const TA *d_col = data;
TX x_col = x[0];
for (int row = 0; row < height; row++)
{
@@ -188,6 +188,35 @@ void Mult(const int height, const int width, TA *data, const TX *x, TY *y)
}
}
/** @brief Matrix transpose vector multiplication: y = At x, where the matrix A
is of size @a height x @a width with given @a data, while @a x and @a y
specify the data of the input and output vectors. */
template<typename TA, typename TX, typename TY>
MFEM_HOST_DEVICE inline
void MultTranspose(const int height, const int width, const TA *data,
const TX *x, TY *y)
{
if (height == 0)
{
for (int row = 0; row < width; row++)
{
y[row] = 0.0;
}
return;
}
TY *y_off = y;
for (int i = 0; i < width; ++i)
{
TY val = 0.0;
for (int j = 0; j < height; ++j)
{
val += x[j] * data[i * height + j];
}
*y_off = val;
y_off++;
}
}
/// Symmetrize a square matrix with given @a size and @a data: A -> (A+A^T)/2.
template<typename T>
MFEM_HOST_DEVICE inline
@@ -353,6 +382,30 @@ void MultABt(const int Aheight, const int Awidth, const int Bheight,
}
}
/** @brief Multiply the transpose of a matrix of size @a Aheight x @a Awidth
and data @a Adata with a matrix of size @a Aheight x @a Bwidth and data @a
Bdata: At * B. Return the result in a matrix with data @a AtBdata. */
template<typename TA, typename TB, typename TC>
MFEM_HOST_DEVICE inline
void MultAtB(const int Aheight, const int Awidth, const int Bwidth,
const TA *Adata, const TB *Bdata, TC *AtBdata)
{
TC *c = AtBdata;
for (int i = 0; i < Bwidth; ++i)
{
for (int j = 0; j < Awidth; ++j)
{
TC val = 0.0;
for (int k = 0; k < Aheight; ++k)
{
val += Adata[j * Aheight + k] * Bdata[i * Aheight + k];
}
*c = val;
c++;
}
}
}
/// Compute the spectrum of the matrix of size dim with given @a data, returning
/// the eigenvalues in the array @a lambda and the eigenvectors in the array @a
/// vec (listed consecutively).
+1 -1
View File
@@ -242,7 +242,7 @@ public:
void FormDiscreteOperator(Operator* &A);
/// Prints operator with input size n and output size m in Matlab format.
void PrintMatlab(std::ostream & out, int n = 0, int m = 0) const;
void PrintMatlab(std::ostream & out, int n, int m = 0) const;
/// Prints operator in Matlab format.
virtual void PrintMatlab(std::ostream & out) const;
-4
View File
@@ -558,8 +558,6 @@ PetscParVector::PetscParVector(MPI_Comm comm, const Operator &op,
else /* Vector intended to be used with Place/ResetMemory calls */
{
size = loc;
pdata.Reset();
data.Reset();
}
}
@@ -581,8 +579,6 @@ PetscParVector::PetscParVector(const PetscParMatrix &A,
PetscInt n;
ierr = VecGetLocalSize(x,&n); PCHKERRQ(x,ierr);
size = n;
pdata.Reset();
data.Reset();
}
else
{
+6 -6
View File
@@ -84,9 +84,9 @@ SparseMatrix::SparseMatrix(int nrows, int ncols)
isSorted(false)
{
// We probably do not need to set the ownership flags here.
I.Reset(); I.SetHostPtrOwner(true);
J.Reset(); J.SetHostPtrOwner(true);
A.Reset(); A.SetHostPtrOwner(true);
I.SetHostPtrOwner(true);
J.SetHostPtrOwner(true);
A.SetHostPtrOwner(true);
for (int i = 0; i < nrows; i++)
{
@@ -229,9 +229,9 @@ SparseMatrix::SparseMatrix(const SparseMatrix &mat, bool copy_graph,
}
// We probably do not need to set the ownership flags here.
I.Reset(); I.SetHostPtrOwner(true);
J.Reset(); J.SetHostPtrOwner(true);
A.Reset(); A.SetHostPtrOwner(true);
I.SetHostPtrOwner(true);
J.SetHostPtrOwner(true);
A.SetHostPtrOwner(true);
}
current_row = -1;
+1 -8
View File
@@ -17,10 +17,7 @@
namespace mfem
{
DenseSymmetricMatrix::DenseSymmetricMatrix() : Matrix(0)
{
data.Reset();
}
DenseSymmetricMatrix::DenseSymmetricMatrix() : Matrix(0) { }
DenseSymmetricMatrix::DenseSymmetricMatrix(int s) : Matrix(s)
{
@@ -30,10 +27,6 @@ DenseSymmetricMatrix::DenseSymmetricMatrix(int s) : Matrix(s)
data.New((s*(s+1))/2);
*this = 0.0; // init with zeroes
}
else
{
data.Reset();
}
}
void DenseSymmetricMatrix::SetSize(int s)
+15 -6
View File
@@ -39,21 +39,21 @@ namespace mfem
Vector::Vector(const Vector &v)
{
const int s = v.Size();
size = s;
if (s > 0)
{
MFEM_ASSERT(!v.data.Empty(), "invalid source vector");
size = s;
data.New(s, v.data.GetMemoryType());
data.CopyFrom(v.data, s);
}
else
{
size = 0;
data.Reset();
}
UseDevice(v.UseDevice());
}
Vector::Vector(Vector &&v)
{
*this = std::move(v);
}
void Vector::Load(std::istream **in, int np, int *dim)
{
int i, j, s;
@@ -146,6 +146,15 @@ Vector &Vector::operator=(const Vector &v)
return *this;
}
Vector &Vector::operator=(Vector &&v)
{
data = std::move(v.data);
size = v.size;
v.data.Reset();
v.size = 0;
return *this;
}
Vector &Vector::operator=(double value)
{
const bool use_dev = UseDevice();
+11 -8
View File
@@ -66,12 +66,16 @@ protected:
public:
/// Default constructor for Vector. Sets size = 0 and data = NULL.
Vector() { data.Reset(); size = 0; }
/** Default constructor for Vector. Sets size = 0, and calls Memory::Reset on
data through Memory<double>'s default constructor. */
Vector(): size(0) { }
/// Copy constructor. Allocates a new data array and copies the data.
Vector(const Vector &);
/// Move constructor. "Steals" data from its argument.
Vector(Vector&& v);
/// @brief Creates vector of size s.
/// @warning Entries are not initialized to zero!
explicit Vector(int s);
@@ -278,6 +282,9 @@ public:
assignment operator. */
Vector &operator=(const Vector &v);
/// Move assignment
Vector &operator=(Vector&& v);
/// Redefine '=' for vector = constant.
Vector &operator=(double value);
@@ -503,16 +510,12 @@ inline int CheckFinite(const double *v, const int n)
inline Vector::Vector(int s)
{
MFEM_ASSERT(s>=0,"Unexpected negative size.");
size = s;
if (s > 0)
{
size = s;
data.New(s);
}
else
{
size = 0;
data.Reset();
}
}
inline void Vector::SetSize(int s)
+7 -5
View File
@@ -123,7 +123,7 @@ EXAMPLE_SUBDIRS = amgx caliper ginkgo hiop petsc pumi sundials superlu
EXAMPLE_DIRS := examples $(addprefix examples/,$(EXAMPLE_SUBDIRS))
EXAMPLE_TEST_DIRS := examples
MINIAPP_SUBDIRS = common electromagnetics meshing navier performance tools toys nurbs gslib adjoint solvers shifted mtop parelag
MINIAPP_SUBDIRS = common electromagnetics meshing navier performance tools toys nurbs gslib adjoint solvers shifted mtop parelag autodiff
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics meshing tools toys shifted)
@@ -274,7 +274,7 @@ endif
# List of MFEM dependencies, that require the *_LIB variable to be non-empty
MFEM_REQ_LIB_DEPS = SUPERLU MUMPS METIS FMS CONDUIT SIDRE LAPACK SUNDIALS MESQUITE\
SUITESPARSE STRUMPACK GINKGO GNUTLS NETCDF PETSC SLEPC MPFR PUMI HIOP GSLIB\
OCCA CEED RAJA UMPIRE MKL_CPARDISO AMGX CALIPER PARELAG BENCHMARK
OCCA CEED RAJA UMPIRE MKL_CPARDISO AMGX CALIPER PARELAG BENCHMARK
PETSC_ERROR_MSG = $(if $(PETSC_FOUND),,. PETSC config not found: $(PETSC_VARS))
SLEPC_ERROR_MSG = $(if $(SLEPC_FOUND),,. SLEPC config not found: $(SLEPC_VARS))
@@ -340,8 +340,8 @@ MFEM_DEFINES = MFEM_VERSION MFEM_VERSION_STRING MFEM_GIT_STRING MFEM_USE_MPI\
MFEM_USE_PUMI MFEM_USE_HIOP MFEM_USE_GSLIB MFEM_USE_CUDA MFEM_USE_HIP\
MFEM_USE_OCCA MFEM_USE_CEED MFEM_USE_RAJA MFEM_USE_UMPIRE MFEM_USE_SIMD\
MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_AMGX MFEM_USE_MUMPS\
MFEM_USE_CALIPER MFEM_USE_BENCHMARK MFEM_USE_PARELAG\
MFEM_SOURCE_DIR MFEM_INSTALL_DIR
MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_CALIPER MFEM_USE_BENCHMARK\
MFEM_USE_PARELAG MFEM_SOURCE_DIR MFEM_INSTALL_DIR
# List of makefile variables that will be written to config.mk:
MFEM_CONFIG_VARS = MFEM_CXX MFEM_HOST_CXX MFEM_CPPFLAGS MFEM_CXXFLAGS\
@@ -500,7 +500,7 @@ hpc:
deps:
rm -f $(BLD)deps.mk
for i in $(RELSRC_FILES:.cpp=); do \
$(DEP_CXX) $(MFEM_BUILD_FLAGS) -MM -MT $(BLD)$${i}.o $(SRC)$${i}.cpp\
$(DEP_CXX) $(MFEM_BUILD_FLAGS) $(DEP_FLAGS) $(BLD)$${i}.o $(SRC)$${i}.cpp\
>> $(BLD)deps.mk; done
check: lib
@@ -679,6 +679,8 @@ status info:
$(info MFEM_USE_SIMD = $(MFEM_USE_SIMD))
$(info MFEM_USE_ADIOS2 = $(MFEM_USE_ADIOS2))
$(info MFEM_USE_MKL_CPARDISO = $(MFEM_USE_MKL_CPARDISO))
$(info MFEM_USE_ADFORWARD = $(MFEM_USE_ADFORWARD))
$(info MFEM_USE_CODIPACK = $(MFEM_USE_CODIPACK))
$(info MFEM_USE_BENCHMARK = $(MFEM_USE_BENCHMARK))
$(info MFEM_USE_PARELAG = $(MFEM_USE_PARELAG))
$(info MFEM_CXX = $(value MFEM_CXX))
+11 -11
View File
@@ -75,7 +75,7 @@ void Mesh::GetElementCenter(int i, Vector &center)
double Mesh::GetElementSize(ElementTransformation *T, int type)
{
DenseMatrix J(spaceDim,Dim);
DenseMatrix J(spaceDim, Dim);
Geometry::Type geom = T->GetGeometryType();
T->SetIntPoint(&Geometries.GetCenter(geom));
@@ -102,7 +102,7 @@ double Mesh::GetElementSize(int i, int type)
double Mesh::GetElementSize(int i, const Vector &dir)
{
DenseMatrix J(spaceDim,Dim);
DenseMatrix J(spaceDim, Dim);
Vector d_hat(Dim);
GetElementJacobian(i, J);
J.MultTranspose(dir, d_hat);
@@ -8527,8 +8527,8 @@ void Mesh::LocalRefinement(const Array<int> &marked_el, int type)
elements[new_e] = new Segment(new_v, vert[1], attr);
vert[1] = new_v;
CoarseFineTr.embeddings[i] = Embedding(i, 1);
CoarseFineTr.embeddings[new_e] = Embedding(i, 2);
CoarseFineTr.embeddings[i] = Embedding(i, Geometry::SEGMENT, 1);
CoarseFineTr.embeddings[new_e] = Embedding(i, Geometry::SEGMENT, 2);
}
static double seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 };
@@ -9276,7 +9276,7 @@ void Mesh::Bisection(int i, const DSTable &v_to_v,
int coarse = FindCoarseElement(i);
CoarseFineTr.embeddings[i].parent = coarse;
CoarseFineTr.embeddings.Append(Embedding(coarse));
CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TRIANGLE));
// 3. edge1 and edge2 may have to be changed for the second triangle.
if (v[1][0] < v_to_v.NumberOfRows() && v[1][1] < v_to_v.NumberOfRows())
@@ -9396,7 +9396,7 @@ void Mesh::Bisection(int i, HashTable<Hashed2> &v_to_v)
int coarse = FindCoarseElement(i);
CoarseFineTr.embeddings[i].parent = coarse;
CoarseFineTr.embeddings.Append(Embedding(coarse));
CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TETRAHEDRON));
// 3. Set the bisection flag
switch (type)
@@ -9534,10 +9534,10 @@ void Mesh::UniformRefinement(int i, const DSTable &v_to_v,
// set parent indices
int coarse = FindCoarseElement(i);
CoarseFineTr.embeddings[i] = Embedding(coarse);
CoarseFineTr.embeddings.Append(Embedding(coarse));
CoarseFineTr.embeddings.Append(Embedding(coarse));
CoarseFineTr.embeddings.Append(Embedding(coarse));
CoarseFineTr.embeddings[i] = Embedding(coarse, Geometry::TRIANGLE);
CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TRIANGLE));
CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TRIANGLE));
CoarseFineTr.embeddings.Append(Embedding(coarse, Geometry::TRIANGLE));
NumOfElements += 3;
}
@@ -9555,7 +9555,7 @@ void Mesh::InitRefinementTransforms()
for (int i = 0; i < NumOfElements; i++)
{
elements[i]->ResetTransform(0);
CoarseFineTr.embeddings[i] = Embedding(i);
CoarseFineTr.embeddings[i] = Embedding(i, GetElementGeometry(i));
}
}
+136
View File
@@ -157,6 +157,142 @@ int ThresholdDerefiner::ApplyImpl(Mesh &mesh)
}
int CoefficientRefiner::ApplyImpl(Mesh &mesh)
{
int max_it = 1;
return PreprocessMesh(mesh, max_it);
}
int CoefficientRefiner::PreprocessMesh(Mesh &mesh, int max_it)
{
int rank = 0;
MFEM_VERIFY(max_it > 0, "max_it must be strictly positive")
int dim = mesh.Dimension();
L2_FECollection l2fec(order, dim);
FiniteElementSpace* l2fes = NULL;
bool par = false;
GridFunction *gf = NULL;
#ifdef MFEM_USE_MPI
ParMesh* pmesh = dynamic_cast<ParMesh*>(&mesh);
if (pmesh && pmesh->Nonconforming())
{
par = true;
l2fes = new ParFiniteElementSpace(pmesh, &l2fec);
gf = new ParGridFunction(static_cast<ParFiniteElementSpace*>(l2fes));
}
#endif
if (!par)
{
l2fes = new FiniteElementSpace(&mesh, &l2fec);
gf = new GridFunction(l2fes);
}
// If custom integration rule has not been set,
// then use the default integration rule
if (!irs)
{
int order_quad = 2*order + 3;
for (int i=0; i < Geometry::NumGeom; ++i)
{
ir_default[i] = &(IntRules.Get(i, order_quad));
}
irs = ir_default;
}
for (int i = 0; i < max_it; i++)
{
// Compute number of elements and L2-norm of f.
int NE = mesh.GetNE();
int globalNE = 0;
double norm_of_coeff = 0.0;
if (par)
{
#ifdef MFEM_USE_MPI
globalNE = pmesh->GetGlobalNE();
norm_of_coeff = ComputeGlobalLpNorm(2.0,*coeff,*pmesh,irs);
#endif
}
else
{
globalNE = NE;
norm_of_coeff = ComputeLpNorm(2.0,*coeff,mesh,irs);
}
// Compute average L2-norm of f
double av_norm_of_coeff = norm_of_coeff / sqrt(globalNE);
// Compute element-wise L2-norms of (I - Π) f
Vector element_norms_of_fine_scale(NE);
gf->SetSpace(l2fes);
gf->ProjectCoefficient(*coeff);
gf->ComputeElementL2Errors(*coeff,element_norms_of_fine_scale,irs);
// Define osc_K(f) := || h ⋅ (I - Π) f ||_K and select elements
// for refinement based on threshold. Also record relative osc(f).
global_osc = 0.0;
mesh_refinements.SetSize(0);
element_oscs.Destroy();
element_oscs.SetSize(NE);
element_oscs = 0.0;
for (int j = 0; j < NE; j++)
{
double h = mesh.GetElementSize(j);
double element_osc = h * element_norms_of_fine_scale(j);
if ( element_osc > threshold * av_norm_of_coeff )
{
mesh_refinements.Append(j);
}
element_oscs(j) = element_osc/(norm_of_coeff + 1e-10);
global_osc += element_osc*element_osc;
}
#ifdef MFEM_USE_MPI
if (par)
{
MPI_Comm comm = pmesh->GetComm();
MPI_Allreduce(MPI_IN_PLACE, &global_osc, 1, MPI_DOUBLE, MPI_SUM, comm);
MPI_Comm_rank(comm, &rank);
}
#endif
global_osc = sqrt(global_osc)/(norm_of_coeff + 1e-10);
// Exit if the global threshold or maximum number of elements is reached.
if (global_osc < threshold || globalNE > max_elements)
{
if (global_osc > threshold && globalNE > max_elements && rank == 0 &&
print_level)
{
MFEM_WARNING("Reached maximum number of elements "
"before resolving data to tolerance.");
}
delete l2fes;
delete gf;
return STOP;
}
// Refine elements.
mesh.GeneralRefinement(mesh_refinements, nonconforming, nc_limit);
l2fes->Update(false);
gf->Update();
}
delete l2fes;
delete gf;
return CONTINUE + REFINED;
}
void CoefficientRefiner::Reset()
{
element_oscs.Destroy();
global_osc = 0.0;
coeff = NULL;
irs = NULL;
}
int Rebalancer::ApplyImpl(Mesh &mesh)
{
#ifdef MFEM_USE_MPI
+112
View File
@@ -308,6 +308,118 @@ public:
};
/** @brief Refinement operator to control data oscillation.
This class computes osc_K(f) := || h (I - Π) f ||_K at each element K.
Here, Π is the L2-projection and ||||_K is the L2-norm, restricted to the
element K. All elements satisfying the inequality
\code
osc_K(f) > threshold ||f|| / sqrt(n_el),
\endcode
are refined. Here, threshold is a positive parameter, |||| is the L2-norm
over the entire domain Ω, and n_el is the number of elements in the mesh.
Note that if osc(f) = threshold ||f|| / sqrt(n_el) for each K, then
\code
osc(f) = sqrt(sum_K osc_K^2(f)) = threshold ||f||.
\endcode
This is the reason for the 1/sqrt(n_el) factor.
*/
class CoefficientRefiner : public MeshOperator
{
protected:
bool print_level = false;
int nc_limit = 1;
int nonconforming = -1;
int order;
long max_elements = std::numeric_limits<long>::max();
double threshold = 1.0e-2;
double global_osc = NAN;
Array<int> mesh_refinements;
Vector element_oscs;
Coefficient *coeff = NULL;
const IntegrationRule *ir_default[Geometry::NumGeom];
const IntegrationRule **irs = NULL;
/** @brief Apply the operator to the mesh once.
@return STOP if a stopping criterion is satisfied or no elements were
marked for refinement; REFINED + CONTINUE otherwise. */
virtual int ApplyImpl(Mesh &mesh);
public:
/// Constructor
CoefficientRefiner(Coefficient &coeff_, int order_)
{
// function f
coeff = &coeff_;
// order of the projection Π
order = order_;
}
/** @brief Apply the operator to the mesh max_it times or until tolerance
* achieved.
@return STOP if a stopping criterion is satisfied or no elements were
marked for refinement; REFINED + CONTINUE otherwise. */
virtual int PreprocessMesh(Mesh &mesh, int max_it);
int PreprocessMesh(Mesh &mesh)
{
int max_it = 10;
return PreprocessMesh(mesh, max_it);
}
/// Set the refinement threshold. The default value is 1.0e-2.
void SetThreshold(double threshold_) { threshold = threshold_; }
/** @brief Set the maximum number of elements stopping criterion: stop when
the input mesh has num_elements >= max_elem. The default value is
LONG_MAX. */
void SetMaxElements(long max_elements_) { max_elements = max_elements_; }
/// Reset the function f
void ResetCoefficient(Coefficient &coeff_)
{
element_oscs.Destroy();
global_osc = NAN;
coeff = &coeff_;
}
/// Reset the oscillation order
void SetOrder(double order_) { order = order_; }
/** @brief Set the maximum ratio of refinement levels of adjacent elements
(0 = unlimited). The default value is 1, which helps ensure appropriate
refinements in pathological situations where the default quadrature
order is too low. */
void SetNCLimit(int nc_limit_)
{
MFEM_ASSERT(nc_limit_ >= 0, "Invalid NC limit");
nc_limit = nc_limit_;
}
// Set a custom integration rule
void SetIntRule(const IntegrationRule *irs_[]) { irs = irs_; }
// Set print level
void PrintWarnings() { print_level = true; }
// Return the value of the global relative data oscillation
double GetOsc() const { return global_osc; }
// Return the local relative data oscillation errors
const Vector & GetLocalOscs() const
{
MFEM_ASSERT(element_oscs.Size() > 0,
"Local oscillations have not been computed yet")
return element_oscs;
}
/// Reset
virtual void Reset();
};
/** @brief ParMesh rebalancing operator.
If the mesh is a parallel mesh, perform rebalancing; otherwise, do nothing.
+28 -133
View File
@@ -1865,8 +1865,12 @@ void NCMesh::InitDerefTransforms()
transforms.embeddings.SetSize(nfine);
for (int i = 0; i < nfine; i++)
{
transforms.embeddings[i].parent = -1;
transforms.embeddings[i].matrix = 0;
Embedding &emb = transforms.embeddings[i];
emb.parent = -1;
emb.matrix = 0;
Element &el = elements[leaf_elements[i]];
emb.geom = el.Geom();
emb.ghost = IsGhost(el);
}
}
@@ -1879,7 +1883,7 @@ void NCMesh::SetDerefMatrixCodes(int parent, Array<int> &fine_coarse)
Element &ch = elements[prn.child[i]];
if (ch.index >= 0)
{
int code = (prn.ref_type << 8) | (i << 4) | prn.geom;
int code = (prn.ref_type << 4) | i;
transforms.embeddings[ch.index].matrix = code;
fine_coarse[ch.index] = parent;
}
@@ -4291,6 +4295,8 @@ void NCMesh::TraverseRefinements(int elem, int coarse_index,
Embedding &emb = transforms.embeddings[el.index];
emb.parent = coarse_index;
emb.matrix = matrix - 1;
emb.geom = el.Geom();
emb.ghost = IsGhost(el);
}
else
{
@@ -4378,15 +4384,14 @@ const CoarseFineTransformations& NCMesh::GetDerefinementTransforms()
// assign numbers to the different matrices used
for (int i = 0; i < transforms.embeddings.Size(); i++)
{
int code = transforms.embeddings[i].matrix;
Embedding &emb = transforms.embeddings[i];
int code = emb.matrix; // see SetDerefMatrixCodes()
if (code)
{
int geom = code & 0xf; // see SetDerefMatrixCodes()
int ref_type_child = code >> 4;
int &matrix = mat_no[emb.geom][code];
if (!matrix) { matrix = mat_no[emb.geom].size(); }
int &matrix = mat_no[geom][ref_type_child];
if (!matrix) { matrix = mat_no[geom].size(); }
transforms.embeddings[i].matrix = matrix - 1;
emb.matrix = matrix - 1;
}
}
@@ -4421,136 +4426,26 @@ const CoarseFineTransformations& NCMesh::GetDerefinementTransforms()
return transforms;
}
namespace internal
void CoarseFineTransformations::MakeCoarseToFineTable(Table &coarse_to_fine,
bool want_ghosts) const
{
Array<Connection> conn;
conn.Reserve(embeddings.Size());
// Used in CoarseFineTransformations::GetCoarseToFineMap() below.
struct RefType
{
Geometry::Type geom;
int num_children;
const Pair<int,int> *children;
RefType(Geometry::Type g, int n, const Pair<int,int> *c)
: geom(g), num_children(n), children(c) { }
bool operator<(const RefType &other) const
int max_parent = -1;
for (int i = 0; i < embeddings.Size(); i++)
{
if (geom < other.geom) { return true; }
if (geom > other.geom) { return false; }
if (num_children < other.num_children) { return true; }
if (num_children > other.num_children) { return false; }
for (int i = 0; i < num_children; i++)
const Embedding &emb = embeddings[i];
if ((emb.parent >= 0) &&
(!emb.ghost || want_ghosts))
{
if (children[i].one < other.children[i].one) { return true; }
if (children[i].one > other.children[i].one) { return false; }
}
return false; // everything is equal
}
};
} // namespace internal
void CoarseFineTransformations::GetCoarseToFineMap(
const mfem::Mesh &fine_mesh, Table &coarse_to_fine,
Array<int> &coarse_to_ref_type, Table &ref_type_to_matrix,
Array<mfem::Geometry::Type> &ref_type_to_geom,
bool get_coarse_to_fine_only) const
{
const int fine_ne = embeddings.Size();
int coarse_ne = -1;
for (int i = 0; i < fine_ne; i++)
{
coarse_ne = std::max(coarse_ne, embeddings[i].parent);
}
coarse_ne++;
coarse_to_ref_type.SetSize(coarse_ne);
coarse_to_fine.SetDims(coarse_ne, fine_ne);
Array<int> cf_i(coarse_to_fine.GetI(), coarse_ne+1);
Array<Pair<int,int> > cf_j(fine_ne);
cf_i = 0;
for (int i = 0; i < fine_ne; i++)
{
cf_i[embeddings[i].parent+1]++;
}
cf_i.PartialSum();
MFEM_ASSERT(cf_i.Last() == cf_j.Size(), "internal error");
for (int i = 0; i < fine_ne; i++)
{
const Embedding &e = embeddings[i];
cf_j[cf_i[e.parent]].one = e.matrix; // used as sort key below
cf_j[cf_i[e.parent]].two = i;
cf_i[e.parent]++;
}
std::copy_backward(cf_i.begin(), cf_i.end()-1, cf_i.end());
cf_i[0] = 0;
for (int i = 0; i < coarse_ne; i++)
{
std::sort(&cf_j[cf_i[i]], cf_j.GetData() + cf_i[i+1]);
}
for (int i = 0; i < fine_ne; i++)
{
coarse_to_fine.GetJ()[i] = cf_j[i].two;
}
if (get_coarse_to_fine_only) { return; }
MFEM_VERIFY(fine_mesh.GetLastOperation() != Mesh::Operation::DEREFINE,
"GetCoarseToFineMap is not fully supported for derefined meshes."
" Set 'get_coarse_to_fine_only=true'.")
using internal::RefType;
using std::map;
using std::pair;
map<RefType,int> ref_type_map;
for (int i = 0; i < coarse_ne; i++)
{
const int num_children = cf_i[i+1]-cf_i[i];
MFEM_ASSERT(num_children > 0, "");
const int fine_el = cf_j[cf_i[i]].two;
// Assuming the coarse and the fine elements have the same geometry:
const Geometry::Type geom = fine_mesh.GetElementBaseGeometry(fine_el);
const RefType ref_type(geom, num_children, &cf_j[cf_i[i]]);
pair<map<RefType,int>::iterator,bool> res =
ref_type_map.insert(
pair<const RefType,int>(ref_type, (int)ref_type_map.size()));
coarse_to_ref_type[i] = res.first->second;
}
ref_type_to_matrix.MakeI((int)ref_type_map.size());
ref_type_to_geom.SetSize((int)ref_type_map.size());
for (map<RefType,int>::iterator it = ref_type_map.begin();
it != ref_type_map.end(); ++it)
{
ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children);
ref_type_to_geom[it->second] = it->first.geom;
}
ref_type_to_matrix.MakeJ();
for (map<RefType,int>::iterator it = ref_type_map.begin();
it != ref_type_map.end(); ++it)
{
const RefType &rt = it->first;
for (int j = 0; j < rt.num_children; j++)
{
ref_type_to_matrix.AddConnection(it->second, rt.children[j].one);
conn.Append(Connection(emb.parent, i));
max_parent = std::max(emb.parent, max_parent);
}
}
ref_type_to_matrix.ShiftUpI();
}
void CoarseFineTransformations::GetCoarseToFineMap(const Mesh &fine_mesh,
Table &coarse_to_fine) const
{
Array<int> coarse_to_ref_type;
Table ref_type_to_matrix;
Array<mfem::Geometry::Type> ref_type_to_geom;
bool get_coarse_to_fine_only = true;
GetCoarseToFineMap(fine_mesh, coarse_to_fine, coarse_to_ref_type,
ref_type_to_matrix, ref_type_to_geom,
get_coarse_to_fine_only);
conn.Sort(); // NOTE: unique is not necessary
coarse_to_fine.MakeFromList(max_parent+1, conn);
}
void NCMesh::ClearTransforms()
@@ -4580,7 +4475,7 @@ bool CoarseFineTransformations::IsInitialized() const
void Swap(CoarseFineTransformations &a, CoarseFineTransformations &b)
{
for (int g=0; g<Geometry::NumGeom; ++g)
for (int g = 0; g < Geometry::NumGeom; ++g)
{
a.point_matrices[g].Swap(b.point_matrices[g]);
}
+26 -17
View File
@@ -38,45 +38,54 @@ struct Refinement
char ref_type; ///< refinement XYZ bit mask (7 = full isotropic)
Refinement() = default;
Refinement(int index, int type = 7) : index(index), ref_type(type) {}
};
/// Defines the position of a fine element within a coarse element.
struct Embedding
{
/// %Element index in the coarse mesh.
/// Coarse %Element index in the coarse mesh.
int parent;
/** @brief Index into the DenseTensor corresponding to the parent
Geometry::Type stored in CoarseFineTransformations::point_matrices. */
int matrix;
/** The (geom, matrix) pair determines the sub-element transformation for the
fine element: CoarseFineTransformations::point_matrices[geom](matrix) is
the point matrix of the region within the coarse element reference domain.*/
unsigned geom : 4;
unsigned matrix : 27;
/// For internal use: 0 if regular fine element, 1 if parallel ghost element.
unsigned ghost : 1;
Embedding() = default;
Embedding(int elem, int matrix = 0) : parent(elem), matrix(matrix) {}
Embedding(int elem, Geometry::Type geom, int matrix = 0, bool ghost = false)
: parent(elem), geom(geom), matrix(matrix), ghost(ghost) {}
};
/// Defines the coarse-fine transformations of all fine elements.
struct CoarseFineTransformations
{
/// Matrices for IsoparametricTransformation organized by Geometry::Type
DenseTensor point_matrices[Geometry::NumGeom];
/// Fine element positions in their parents.
Array<Embedding> embeddings;
void GetCoarseToFineMap(const Mesh &fine_mesh,
Table &coarse_to_fine,
Array<int> &coarse_to_ref_type,
Table &ref_type_to_matrix,
Array<Geometry::Type> &ref_type_to_geom,
bool get_coarse_to_fine_only = false) const;
/** A "dictionary" of matrices for IsoparametricTransformation. Use
Embedding::{geom,matrix} to access a fine element point matrix. */
DenseTensor point_matrices[Geometry::NumGeom];
void GetCoarseToFineMap(const Mesh &fine_mesh,
Table &coarse_to_fine) const;
/** Invert the 'embeddings' array: create a Table with coarse elements as
rows and fine elements as columns. If 'want_ghosts' is false, parallel
ghost fine elements are not included in the table. */
void MakeCoarseToFineTable(Table &coarse_to_fine,
bool want_ghosts = false) const;
void Clear();
bool IsInitialized() const;
long MemoryUsage() const;
MFEM_DEPRECATED
void GetCoarseToFineMap(const Mesh &fine_mesh, Table &coarse_to_fine) const
{ MakeCoarseToFineTable(coarse_to_fine, true); (void) fine_mesh; }
};
void Swap(CoarseFineTransformations &a, CoarseFineTransformations &b);
+2 -2
View File
@@ -3706,8 +3706,8 @@ void ParMesh::LocalRefinement(const Array<int> &marked_el, int type)
elements[new_e] = new Segment(new_v, vert[1], attr);
vert[1] = new_v;
CoarseFineTr.embeddings[i] = Embedding(i, 1);
CoarseFineTr.embeddings[new_e] = Embedding(i, 2);
CoarseFineTr.embeddings[i] = Embedding(i, Geometry::SEGMENT, 1);
CoarseFineTr.embeddings[new_e] = Embedding(i, Geometry::SEGMENT, 2);
}
static double seg_children[3*2] = { 0.0,1.0, 0.0,0.5, 0.5,1.0 };
+1
View File
@@ -29,4 +29,5 @@ add_subdirectory(gslib)
add_subdirectory(solvers)
add_subdirectory(shifted)
add_subdirectory(mtop)
add_subdirectory(autodiff)
add_subdirectory(parelag)
+57
View File
@@ -0,0 +1,57 @@
# Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
list(APPEND SEQADIFF_COMMON_SOURCES)
list(APPEND SEQADIFF_COMMON_HEADERS
fdual.hpp
tadvector.hpp
taddensemat.hpp
admfem.hpp)
convert_filenames_to_full_paths(SEQADIFF_COMMON_SOURCES)
convert_filenames_to_full_paths(SEQADIFF_COMMON_HEADERS)
set(SEQADIFF_COMMON_FILES
EXTRA_SOURCES ${SEQADIFF_COMMON_SOURCES}
EXTRA_HEADERS ${SEQADIFF_COMMON_HEADERS})
add_mfem_miniapp(seqadiff
MAIN seq_example.cpp
${SEQADIFF_COMMON_FILES}
LIBRARIES mfem)
add_mfem_miniapp(seqtest
MAIN seq_test.cpp
${SEQADIFF_COMMON_FILES}
LIBRARIES mfem)
if(MFEM_USE_MPI)
list(APPEND PARADIFF_COMMON_SOURCES)
list(APPEND PARADIFF_COMMON_HEADERS)
convert_filenames_to_full_paths(PARADIFF_COMMON_SOURCES)
convert_filenames_to_full_paths(PARADIFF_COMMON_HEADERS)
set(PARADIFF_COMMON_FILES
EXTRA_SOURCES ${PARADIFF_COMMON_SOURCES} ${SEQADIFF_COMMON_SOURCES}
EXTRA_HEADERS ${PARADIFF_COMMON_HEADERS} ${SEQADIFF_COMMON_HEADERS})
message(STATUS "PARADIFF_COMMON_FILES: ${PARADIFF_COMMON_FILES}")
message(STATUS "SEQADIFF_COMMON_FILES: ${SEQADIFF_COMMON_FILES}")
add_mfem_miniapp(paradiff
MAIN par_example.cpp
${PARADIFF_COMMON_FILES}
LIBRARIES mfem)
endif ()
+712
View File
@@ -0,0 +1,712 @@
// Copyright (c) 2010-2021, 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 ADMFEM_HPP
#define ADMFEM_HPP
#include "mfem.hpp"
#include "fdual.hpp"
#include "tadvector.hpp"
#include "taddensemat.hpp"
#ifdef MFEM_USE_CODIPACK
#include <codi.hpp>
namespace mfem
{
namespace ad
{
#ifdef MFEM_USE_ADFORWARD
/// Forward AD type declaration
typedef codi::RealForward ADFloatType;
/// Vector type for AD-numbers
typedef TAutoDiffVector<ADFloatType> ADVectorType;
/// Matrix type for AD-numbers
typedef TAutoDiffDenseMatrix<ADFloatType> ADMatrixType;
#else
/// Reverse AD type declaration
typedef codi::RealReverse ADFloatType;
/// Vector type for AD-numbers
typedef TAutoDiffVector<ADFloatType> ADVectorType;
/// Matrix type for AD-numbers
typedef TAutoDiffDenseMatrix<ADFloatType> ADMatrixType;
#endif
}
/// The class provides an evaluation of the Jacobian of a templated vector
/// function provided in the constructor. The Jacobian is evaluated with the
/// help of automatic differentiation (AD). The template parameters specify the
/// size of the return vector (vector_size), the size of the input vector
/// (state_size), and the size of the parameters supplied to the function.
template<int vector_size=1, int state_size=1, int param_size=0>
class VectorFuncAutoDiff
{
public:
/// F_ is user implemented function to be differentiated by
/// VectorFuncAutoDiff. The signature of the function is: F_(mfem::Vector&
/// parameters, ad::ADVectorType& state_vector, ad::ADVectorType& result).
/// The parameters vector should have size param_size. The state_vector
/// should have size state_size, and the result vector should have size
/// vector_size. All size parameters are teplate parameters in
/// VectorFuncAutoDiff.
VectorFuncAutoDiff(
std::function<void(mfem::Vector&, ad::ADVectorType&, ad::ADVectorType&)> F_)
{
F=F_;
}
/// Evaluates the Jacobian of the vector function F_ for a set of parameters
/// (vparam) and state vector vstate. The Jacobian (jac) has dimensions
/// [vector_size x state_size].
void Jacobian(mfem::Vector &vparam, mfem::Vector &vstate,
mfem::DenseMatrix &jac)
{
#ifdef MFEM_USE_ADFORWARD
// use forward mode
jac.SetSize(vector_size, state_size);
jac = 0.0;
{
ad::ADVectorType ad_state(state_size);
ad::ADVectorType ad_result(vector_size);
for (int i=0; i<state_size; i++)
{
ad_state[i].setValue(vstate[i]);
ad_state[i].setGradient(0.0);
}
for (int ii=0; ii<state_size; ii++)
{
ad_state[ii].setGradient(1.0);
F(vparam,ad_state,ad_result);
for (int jj=0; jj<vector_size; jj++)
{
jac(jj,ii)=ad_result[jj].getGradient();
}
ad_state[ii].setGradient(0.0);
}
}
#else // use reverse mode
jac.SetSize(vector_size, state_size);
jac = 0.0;
{
ad::ADVectorType ad_state(state_size);
ad::ADVectorType ad_result(vector_size);
for (int i=0; i<state_size; i++)
{
ad_state[i]=vstate[i];
}
ad::ADFloatType::TapeType& tape =ad::ADFloatType::getGlobalTape();
typename ad::ADFloatType::TapeType::Position pos=tape.getPosition();
tape.setActive();
for (int ii=0; ii<state_size; ii++) { tape.registerInput(ad_state[ii]); }
F(vparam,ad_state,ad_result);
for (int ii=0; ii<vector_size; ii++) { tape.registerOutput(ad_result[ii]); }
tape.setPassive();
for (int jj=0; jj<vector_size; jj++)
{
ad_result[jj].setGradient(1.0);
tape.evaluate();
for (int ii=0; ii<state_size; ii++)
{
jac(jj,ii)=ad_state[ii].getGradient();
}
tape.clearAdjoints();
ad_result[jj].setGradient(0.0);
}
tape.reset(pos);
}
#endif
}
private:
std::function<void(mfem::Vector&, ad::ADVectorType&, ad::ADVectorType&)> F;
}; // VectorFuncAutoDiff
/// The class provides an evaluation of the Jacobian of a templated vector
/// function provided as a functor TFunctor. The Jacobian is evaluated with the
/// help of automatic differentiation (AD). The template parameters specify the
/// size of the return vector (vector_size), the size of the input vector
/// (state_size), and the size of the parameters supplied to the function. The
/// TFunctor functor is a template class with parameters [Float data type],
/// [Vector type for the additional parameters], [Vector type for the state
/// vector and the return residual]. The integer template parameters are the
/// same ones passed to QVectorFuncAutoDiff.
template<template<typename, typename, typename, int, int, int> class TFunctor
, int vector_size=1, int state_size=1, int param_size=0>
class QVectorFuncAutoDiff
{
public:
/// Evaluates the vector function for given set of parameters and state
/// values in vector uu. The result is returned in vector rr.
void VectorFunc(const mfem::Vector &vparam, mfem::Vector &uu, mfem::Vector& rr)
{
rf(vparam,uu,rr);
}
/// Returns the gradient of TFunctor(...) in the dense matrix jac. The
/// dimensions of jac are vector_size x state_size, where state_size is the
/// length of vector uu.
void Jacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
#ifdef MFEM_USE_ADFORWARD
// use forward mode
jac.SetSize(vector_size, state_size);
jac = 0.0;
{
ad::ADVectorType aduu(state_size);
ad::ADVectorType rr(vector_size);
for (int i=0; i<state_size; i++)
{
aduu[i].setValue(uu[i]);
aduu[i].setGradient(0.0);
}
for (int ii=0; ii<state_size; ii++)
{
aduu[ii].setGradient(1.0);
tf(vparam,aduu,rr);
for (int jj=0; jj<vector_size; jj++)
{
jac(jj,ii)=rr[jj].getGradient();
}
aduu[ii].setGradient(0.0);
}
}
#else // end MFEM_USE_ADFORWARD
// use reverse mode
jac.SetSize(vector_size, state_size);
jac = 0.0;
{
ad::ADVectorType aduu(state_size);
ad::ADVectorType rr(vector_size);
for (int i=0; i<state_size; i++)
{
aduu[i]=uu[i];
}
ad::ADFloatType::TapeType& tape =ad::ADFloatType::getGlobalTape();
typename ad::ADFloatType::TapeType::Position pos=tape.getPosition();
tape.setActive();
for (int ii=0; ii<state_size; ii++) { tape.registerInput(aduu[ii]); }
tf(vparam,aduu,rr);
for (int ii=0; ii<vector_size; ii++) { tape.registerOutput(rr[ii]); }
tape.setPassive();
for (int jj=0; jj<vector_size; jj++)
{
rr[jj].setGradient(1.0);
tape.evaluate();
for (int ii=0; ii<state_size; ii++)
{
jac(jj,ii)=aduu[ii].getGradient();
}
tape.clearAdjoints();
rr[jj].setGradient(0.0);
}
tape.reset(pos);
}
#endif
}
private:
TFunctor<ad::ADFloatType, const Vector, ad::ADVectorType,
vector_size, state_size, param_size> tf;
TFunctor<double,const mfem::Vector, mfem::Vector,
vector_size, state_size, param_size> rf;
};
/// The class provides an evaluation of the first derivatives and the Hessian of
/// a templated scalar function provided as a functor TFunctor. Both the first
/// and the second derivatives are evaluated with the help of automatic
/// differentiation (AD). The template parameters specify the size of the input
/// vector (state_size) and the size of the parameters supplied to the
/// function. The TFunctor functor is a template class with parameters [Float
/// data type], [Vector type for the additional parameters], [Vector type for
/// the state vector and the return residual]. The integer template parameters
/// are the same ones passed to QFunctionAutoDiff.
template<template<typename, typename, typename, int, int> class TFunctor
, int state_size=1, int param_size=0>
class QFunctionAutoDiff
{
public:
/// Evaluates a function for arguments vparam and uu. The evaluation is
/// based on the operator() in the user provided functor TFunctor.
double Eval(const mfem::Vector &vparam, mfem::Vector &uu)
{
return rf(vparam,uu);
}
/// Provides the same functionality as Grad.
void VectorFunc(const mfem::Vector &vparam, mfem::Vector &uu, mfem::Vector &rr)
{
Grad(vparam,uu,rr);
}
/// Returns the first derivative of TFunctor(...) with respect to the active
/// arguments proved in vector uu. The length of rr is the same as for uu.
void Grad(const mfem::Vector &vparam, mfem::Vector &uu, mfem::Vector &rr)
{
#ifdef MFEM_USE_ADFORWARD
// use forward mode
rr.SetSize(state_size);
{
ad::ADVectorType aduu(state_size);
for (int i=0; i<state_size; i++)
{
aduu[i].setValue(uu[i]);
aduu[i].setGradient(0.0);
}
ad::ADFloatType rez;
for (int ii=0; ii<state_size; ii++)
{
aduu[ii].setGradient(1.0);
rez=tf(vparam,aduu);
rr[ii]=rez.getGradient();
aduu[ii].setGradient(0.0);
}
}
#else
{
ad::ADVectorType aduu(state_size);
ad::ADFloatType rez;
for (int i=0; i<state_size; i++)
{
aduu[i]=uu[i];
}
ad::ADFloatType::TapeType& tape =ad::ADFloatType::getGlobalTape();
typename ad::ADFloatType::TapeType::Position pos=tape.getPosition();
tape.setActive();
for (int ii=0; ii<state_size; ii++) { tape.registerInput(aduu[ii]); }
rez=tf(vparam,aduu);
tape.registerOutput(rez);
tape.setPassive();
rez.setGradient(1.0);
tape.evaluate();
for (int i=0; i<state_size; i++)
{
rr[i]=aduu[i].getGradient();
}
tape.reset(pos);
}
#endif
}
/// Provides same functionality as Hessian.
void Jacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
Hessian(vparam,uu,jac);
}
#ifdef MFEM_USE_ADFORWARD
// use forward-forward mode
typedef codi::RealForwardGen<double> ADFType;
typedef TAutoDiffVector<ADFType> ADFVector;
typedef TAutoDiffDenseMatrix<ADFType> ADFDenseMatrix;
typedef codi::RealForwardGen<ADFType> ADSType;
typedef TAutoDiffVector<ADSType> ADSVector;
typedef TAutoDiffDenseMatrix<ADSType> ADSDenseMatrix;
#else
//use mixed forward and reverse mode
typedef codi::RealForwardGen<double> ADFType;
typedef TAutoDiffVector<ADFType> ADFVector;
typedef TAutoDiffDenseMatrix<ADFType> ADFDenseMatrix;
typedef codi::RealReverseGen<ADFType> ADSType;
typedef TAutoDiffVector<ADSType> ADSVector;
typedef TAutoDiffDenseMatrix<ADSType> ADSDenseMatrix;
#endif
/// Returns the Hessian of TFunctor(...) in the dense matrix jac. The
/// dimensions of jac are state_size x state_size, where state_size is the
/// length of vector uu.
void Hessian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
#ifdef MFEM_USE_ADFORWARD
// use forward-forward mode
jac.SetSize(state_size);
jac=0.0;
{
ADSVector aduu(state_size);
for (int ii = 0; ii < state_size; ii++)
{
aduu[ii].value().value()=uu[ii];
aduu[ii].value().gradient()=0.0;
aduu[ii].gradient().value()=0.0;
aduu[ii].gradient().gradient()=0.0;
}
for (int ii = 0; ii < state_size; ii++)
{
aduu[ii].value().gradient()=1.0;
for (int jj=0; jj<(ii+1); jj++)
{
aduu[jj].gradient().value()=1.0;
ADSType rez=sf(vparam,aduu);
jac(ii,jj)=rez.gradient().gradient();
jac(jj,ii)=jac(ii,jj);
aduu[jj].gradient().value()=0.0;
}
aduu[ii].value().gradient()=0.0;
}
}
#else
// use mixed forward and reverse mode
jac.SetSize(state_size);
jac=0.0;
{
ADSVector aduu(state_size);
for (int ii=0; ii < state_size ; ii++)
{
aduu[ii].value().value()=uu[ii];
}
ADSType rez;
ADSType::TapeType& tape = ADSType::getGlobalTape();
typename ADSType::TapeType::Position pos;
for (int ii = 0; ii < state_size ; ii++)
{
pos=tape.getPosition();
tape.setActive();
for (int jj=0; jj < state_size; jj++)
{
if (jj==ii) {aduu[jj].value().gradient()=1.0;}
else {aduu[jj].value().gradient()=0.0;}
tape.registerInput(aduu[jj]);
}
rez=sf(vparam,aduu);
tape.registerOutput(rez);
tape.setPassive();
rez.gradient().value()=1.0;
tape.evaluate();
for (int jj=0; jj<(ii+1); jj++)
{
jac(ii,jj)=aduu[jj].gradient().gradient();
jac(jj,ii)=jac(ii,jj);
}
tape.reset(pos);
}
}
#endif
}
private:
TFunctor<double, const mfem::Vector,
mfem::Vector, state_size, param_size> rf;
TFunctor<ad::ADFloatType, const mfem::Vector,
ad::ADVectorType, state_size, param_size> tf;
TFunctor<ADSType, const mfem::Vector, ADSVector,
state_size, param_size> sf;
};
}
#else // end MFEM_USE_CODIPACK
// USE NATIVE IMPLEMENTATION
namespace mfem
{
namespace ad
{
/// MFEM native forward AD-type
typedef FDualNumber<double> ADFloatType;
/// Vector type for AD-type numbers
typedef TAutoDiffVector<ADFloatType> ADVectorType;
/// Matrix type for AD-type numbers
typedef TAutoDiffDenseMatrix<ADFloatType> ADMatrixType;
}
/// The class provides an evaluation of the Jacobian of a templated vector
/// function provided in the constructor. The Jacobian is evaluated with the
/// help of automatic differentiation (AD). The template parameters specify the
/// size of the return vector (vector_size), the size of the input vector
/// (state_size), and the size of the parameters supplied to the function.
template<int vector_size=1, int state_size=1, int param_size=0>
class VectorFuncAutoDiff
{
public:
/// F_ is user implemented function to be differentiated by
/// VectorFuncAutoDiff. The signature of the function is: F_(mfem::Vector&
/// parameters, ad::ADVectroType& state_vector, ad::ADVectorType& result).
/// The parameters vector should have size param_size. The state_vector
/// should have size state_size, and the result vector should have size
/// vector_size. All size parameters are teplate parameters in
/// VectorFuncAutoDiff.
VectorFuncAutoDiff(
std::function<void(mfem::Vector&, ad::ADVectorType&, ad::ADVectorType&)> F_)
{
F=F_;
}
/// Evaluates the Jacobian of the vector function F_ for a set of parameters
/// (vparam) and state vector uu. The Jacobian (jac) has dimensions
/// [vector_size x state_size].
void Jacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
jac.SetSize(vector_size, state_size);
jac = 0.0;
{
ad::ADVectorType aduu(uu); // all dual numbers are initialized to zero
ad::ADVectorType rr(vector_size);
for (int ii = 0; ii < state_size; ii++)
{
aduu[ii].dual(1.0);
F(vparam,aduu,rr);
for (int jj = 0; jj < vector_size; jj++)
{
jac(jj, ii) = rr[jj].dual();
}
aduu[ii].dual(0.0);
}
}
}
private:
std::function<void(mfem::Vector&, ad::ADVectorType&, ad::ADVectorType&)> F;
};
/// The class provides an evaluation of the Jacobian of a templated vector
/// function provided as a functor TFunctor. The Jacobian is evaluated with the
/// help of automatic differentiation (AD). The template parameters specify the
/// size of the return vector (vector_size), the size of the input vector
/// (state_size), and the size of the parameters supplied to the function. The
/// TFunctor functor is a template class with parameters [Float data type],
/// [Vector type for the additional parameters], [Vector type for the state
/// vector and the return residual].
/// The integer template parameters are the same ones
/// passed to QVectorFuncAutoDiff. \n
/// Example: f={sin(a*x*y), cos(b*x*y*z), x*x+y*x} \n
/// The vector function has vector_size=3, and state_size=3, i.e., it has
/// three arguments [x,y,z]. The parameters [a,b] size is 2.
/// The functor class will have the following form
/// \code{.cpp}
/// template<typename TDataType, typename TParamVector, typename TStateVector,
/// int residual_size, int state_size, int param_size>
/// class MyVectorFunction{
/// public:
/// TDataType operator() (TParamVector& vparam, TStateVector& uu, TStateVector& rr)
/// {
/// auto a=vparam[0];
/// auto b=vparam[1];
/// rr[0]=sin(a*uu[0]*uu[1]);
/// rr[1]=cos(b*uu[0]*uu[1]*uu[2]);
/// rr[2]=uu[0]*uu[0]+uu[0]*uu[1];
/// }
//
/// };
/// \endcode
template<template<typename, typename, typename, int, int, int> class TFunctor
, int vector_size=1, int state_size=1, int param_size=0>
class QVectorFuncAutoDiff
{
private:
/// MFEM native forward AD-type
typedef ad::FDualNumber<double> ADFType;
/// Vector type for AD-type numbers
typedef TAutoDiffVector<ADFType> ADFVector;
/// Matrix type for AD-type numbers
typedef TAutoDiffDenseMatrix<ADFType> ADFDenseMatrix;
public:
/// Returns a vector valued function rr for supplied passive arguments
/// vparam and active arguments uu. The evaluation is based on the user
/// supplied TFunctor template class.
void VectorFunc(const Vector &vparam, Vector &uu, Vector &rr)
{
func(vparam, uu, rr);
}
/// Returns the gradient of TFunctor(...) residual in the dense matrix jac.
/// The dimensions of jac are vector_size x state_size, where state_size is
/// the length of vector uu.
void Jacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
// use native AD package
jac.SetSize(vector_size, state_size);
jac = 0.0;
{
ADFVector aduu(uu); // all dual numbers are initialized to zero
ADFVector rr(vector_size);
for (int ii = 0; ii < state_size; ii++)
{
aduu[ii].dual(1.0);
Eval(vparam, aduu, rr);
for (int jj = 0; jj < vector_size; jj++)
{
jac(jj, ii) = rr[jj].dual();
}
aduu[ii].dual(0.0);
}
}
}
private:
/// Evaluates the residual from TFunctor(...).
/// Intended for internal use only.
void Eval(const Vector &vparam, ADFVector &uu, ADFVector &rr)
{
tf(vparam, uu, rr);
}
TFunctor<double, const Vector, Vector,
vector_size, state_size, param_size> func;
TFunctor<ADFType, const Vector, ADFVector,
vector_size, state_size, param_size> tf;
};
/// The class provides an evaluation of the first derivatives and the Hessian of
/// a templated scalar function provided as a functor TFunctor. Both the first
/// and the second derivatives are evaluated with the help of automatic
/// differentiation (AD). The template parameters specify the size of the input
/// vector (state_size) and the size of the parameters supplied to the
/// function. The TFunctor functor is a template class with parameters [Float
/// data type], [Vector type for the additional parameters], [Vector type for
/// the state vector and the return residual]. The integer template parameters
/// are the same ones passed to QFunctionAutoDiff. The class duplicates Grad and
/// Hessian, i.e., VectorFunc calls Grad, and Jacobian calls Hessian. The main
/// reason is to provide the same interface as the QVectorFuncAutoDiff class
/// used to differentiate vector functions. Such compatibility allows users to
/// start implementation of their problem based only on some energy or a weak
/// form. The gradients, computed with Grad/VectorFunc, of the function will
/// contribute to the FE residual. Computed with Hessian/Jacobian, the Hessian
/// will contribute to the tangent matrix in Newton's iterations. Once the
/// implementation is complete and tested, the users can start improving the
/// performance by replacing Grad/VectorFunc with a hand-coded version. The
/// gradient is a vector function and can be differentiated with the
/// functionality implemented in QVectorFuncAutoDiff. Thus, the user can
/// directly employ AD for computing the contributions to the global tangent
/// matrix. The main code will not require changes as the names Grad/VectorFunc
/// and Hessian/Jacobian are mirrored.
template<template<typename, typename, typename, int, int> class TFunctor
, int state_size=1, int param_size=0>
class QFunctionAutoDiff
{
private:
/// MFEM native AD-type for first derivatives
typedef ad::FDualNumber<double> ADFType;
/// Vector type for AD-numbers(first derivatives)
typedef TAutoDiffVector<ADFType> ADFVector;
/// Matrix type for AD-numbers(first derivatives)
typedef TAutoDiffDenseMatrix<ADFType> ADFDenseMatrix;
/// MFEM native AD-type for second derivatives
typedef ad::FDualNumber<ADFType> ADSType;
/// Vector type for AD-numbers (second derivatives)
typedef TAutoDiffVector<ADSType> ADSVector;
/// Vector type for AD-numbers (second derivatives)
typedef TAutoDiffDenseMatrix<ADSType> ADSDenseMatrix;
public:
/// Evaluates a function for arguments vparam and uu. The evaluation is
/// based on the operator() in the user provided functor TFunctor.
double Eval(const Vector &vparam, Vector &uu)
{
return tf(vparam,uu);
}
/// Provides the same functionality as Grad.
void VectorFunc(const Vector &vparam, Vector &uu, Vector &rr)
{
Grad(vparam,uu,rr);
}
/// Returns the first derivative of TFunctor(...) with respect to the active
/// arguments proved in vector uu. The length of rr is the same as for uu.
void Grad(const Vector &vparam, Vector &uu, Vector &rr)
{
int n = uu.Size();
rr.SetSize(n);
ADFVector aduu(uu);
ADFType rez;
for (int ii = 0; ii < n; ii++)
{
aduu[ii].dual(1.0);
rez = ff(vparam, aduu);
rr[ii] = rez.dual();
aduu[ii].dual(0.0);
}
}
/// Provides same functionality as Hessian.
void Jacobian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
Hessian(vparam,uu,jac);
}
/// Returns the Hessian of TFunctor(...) in the dense matrix jac. The
/// dimensions of jac are state_size x state_size, where state_size is the
/// length of vector uu.
void Hessian(mfem::Vector &vparam, mfem::Vector &uu, mfem::DenseMatrix &jac)
{
int n = uu.Size();
jac.SetSize(n);
jac = 0.0;
{
ADSVector aduu(n);
for (int ii = 0; ii < n; ii++)
{
aduu[ii].real(ADFType(uu[ii], 0.0));
aduu[ii].dual(ADFType(0.0, 0.0));
}
for (int ii = 0; ii < n; ii++)
{
aduu[ii].real(ADFType(uu[ii], 1.0));
for (int jj = 0; jj < (ii + 1); jj++)
{
aduu[jj].dual(ADFType(1.0, 0.0));
ADSType rez = sf(vparam, aduu);
jac(ii, jj) = rez.dual().dual();
jac(jj, ii) = rez.dual().dual();
aduu[jj].dual(ADFType(0.0, 0.0));
}
aduu[ii].real(ADFType(uu[ii], 0.0));
}
}
}
private:
TFunctor<double, const Vector, Vector, state_size, param_size> tf;
TFunctor<ADFType, const Vector, ADFVector, state_size, param_size> ff;
TFunctor<ADSType, const Vector, ADSVector, state_size, param_size> sf;
};
} // end namespace mfem
#endif // NATIVE
#endif // ADMFEM_HPP
+876
View File
@@ -0,0 +1,876 @@
// Copyright (c) 2010-2021, 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 ADEXAMPLE_HPP
#define ADEXAMPLE_HPP
#include "mfem.hpp"
#include "admfem.hpp"
#include <memory>
#include <iostream>
#include <fstream>
namespace mfem
{
/// Example: Implementation of the residual evaluation for p-Laplacian
/// problem. The residual is evaluated at the integration points for PDE
/// parameters vparam and state fields (derivatives with respect to x,y,z and
/// primal field) stored in vector uu.
template<typename TDataType, typename TParamVector, typename TStateVector,
int residual_size, int state_size, int param_size>
class MyResidualFunctor
{
public:
/// The operator returns the first derivative of the energy with respect to
/// all state variables. These are set in vector uu and consist of the
/// derivatives with respect to x,y,z and the primal field. The derivative is
/// stored in vector rr with length equal to the length of vector uu.
void operator()(TParamVector &vparam, TStateVector &uu, TStateVector &rr)
{
MFEM_ASSERT(residual_size==4,
"PLaplacianResidual residual_size should be equal to 4!");
double pp = vparam[0];
double ee = vparam[1];
double ff = vparam[2];
// The vector rr holds the gradients of the following expression:
// (u_x^2+u_y^2+u_z^2+\varepsilon^2)^(p/2)-f.u,
// where u_x,u_y,u_z are the gradients of the scalar field u.
// The state vector is defined as uu=[u_x,u_y,u_z,u].
TDataType norm2 = uu[0] * uu[0] + uu[1] * uu[1] + uu[2] * uu[2];
TDataType tvar = pow(ee * ee + norm2, (pp - 2.0) / 2.0);
rr[0] = tvar * uu[0];
rr[1] = tvar * uu[1];
rr[2] = tvar * uu[2];
rr[3] = -ff;
}
};
/// Defines template class (functor) for evaluating the energy of the
/// p-Laplacian problem. The input parameters vparam are: vparam[0] - the
/// p-Laplacian power, vparam[1] small value ensuring exciting of an unique
/// solution, and vparam[2] - the distributed external input to the PDE. The
/// template parameter TDataType will be replaced by the compiler with the
/// appropriate AD type for automatic differentiation. The TParamVector
/// represents the vector type used for the parameter vector, and TStateVector
/// the vector type used for the state vector. The template parameters
/// state_size and param_size provide information for the size of the state and
/// the parameters vectors.
template<typename TDataType, typename TParamVector, typename TStateVector
, int state_size, int param_size>
class MyEnergyFunctor
{
public:
/// Returns the energy of a p-Laplacian for state field input provided in
/// vector uu and parameters provided in vector vparam.
TDataType operator()(TParamVector &vparam, TStateVector &uu)
{
MFEM_ASSERT(state_size==4,"MyEnergyFunctor state_size should be equal to 4!");
MFEM_ASSERT(param_size==3,"MyEnergyFunctor param_size should be equal to 3!");
double pp = vparam[0];
double ee = vparam[1];
double ff = vparam[2];
TDataType u = uu[3];
TDataType norm2 = uu[0] * uu[0] + uu[1] * uu[1] + uu[2] * uu[2];
TDataType rez = pow(ee * ee + norm2, pp / 2.0) / pp - ff * u;
return rez;
}
};
/// Implements integrator for a p-Laplacian problem. The integrator is based on
/// a class QFunction utilized for evaluating the energy, the first derivative
/// (residual) and the Hessian of the energy (the Jacobian of the residual).
/// The template parameter CQVectAutoDiff represents the automatically
/// differentiated energy or residual implemented by the user.
/// CQVectAutoDiff::VectorFunc(Vector parameters, Vector state,Vector residual)
/// evaluates the residual at an integration point.
/// CQVectAutoDiff::Jacobian(Vector parameters, Vector state, Matrix hessian)
/// evaluates the Hessian of the energy(the Jacobian of the residual).
template<class CQVectAutoDiff>
class pLaplaceAD : public NonlinearFormIntegrator
{
protected:
Coefficient *pp;
Coefficient *coeff;
Coefficient *load;
CQVectAutoDiff rdf;
public:
pLaplaceAD()
{
coeff = nullptr;
pp = nullptr;
load = nullptr;
vparam.SetSize(3);
vparam[0] = 2.0; // default power
vparam[1] = 1e-8; // default epsilon
vparam[2] = 1.0; // default load
}
pLaplaceAD(Coefficient &pp_) : pp(&pp_), coeff(nullptr), load(nullptr)
{
vparam.SetSize(3);
vparam[0] = 2.0; // default power
vparam[1] = 1e-8; // default epsilon
vparam[2] = 1.0; // default load
}
pLaplaceAD(Coefficient &pp_, Coefficient &q, Coefficient &ld_)
: pp(&pp_), coeff(&q), load(&ld_)
{
vparam.SetSize(3);
vparam[0] = 2.0; // default power
vparam[1] = 1e-8; // default epsilon
vparam[2] = 1.0; // default load
}
virtual ~pLaplaceAD() {}
virtual double GetElementEnergy(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun)
{
double energy = 0.0;
const int ndof = el.GetDof();
const int ndim = el.GetDim();
const int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
Vector shapef(ndof);
// derivatives in isoparametric coordinates
DenseMatrix dshape_iso(ndof, ndim);
// derivatives in physical space
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector grad(spaceDim);
Vector uu(4); //[diff_x,diff_y,diff_z,u]
uu = 0.0;
// Calculates the functional/energy at an integration point.
MyEnergyFunctor<double,Vector,Vector,4,3> qfunc;
double w;
double detJ;
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// calculate the gradient
dshape_xyz.MultTranspose(elfun, grad);
// set the power
if (pp != nullptr)
{
vparam[0] = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
vparam[1] = coeff->Eval(trans, ip);
}
// add the contribution from the load
if (load != nullptr)
{
vparam[2] = load->Eval(trans, ip);
}
// fill the values of vector uu
for (int jj = 0; jj < spaceDim; jj++)
{
uu[jj] = grad[jj] / detJ;
}
uu[3] = shapef * elfun;
// the energy is taken directly from the templated function
energy = energy + w * qfunc(vparam,uu);
}
return energy;
}
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun,
Vector &elvect)
{
MFEM_PERF_BEGIN("AssembleElementVector");
const int ndof = el.GetDof();
const int ndim = el.GetDim();
const int spaceDim = trans.GetSpaceDim();
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector lvec(ndof);
elvect.SetSize(ndof);
elvect = 0.0;
DenseMatrix B(ndof, 4); // [diff_x,diff_y,diff_z, shape]
Vector uu(4); // [diff_x,diff_y,diff_z,u]
Vector du(4);
B = 0.0;
uu = 0.0;
double w;
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
Mult(dshape_iso, trans.InverseJacobian(), dshape_xyz);
// set the matrix B
for (int jj = 0; jj < spaceDim; jj++)
{
B.SetCol(jj, dshape_xyz.GetColumn(jj));
}
B.SetCol(3, shapef);
// set the power
if (pp != nullptr)
{
vparam[0] = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
vparam[1] = coeff->Eval(trans, ip);
}
// add the contribution from the load
if (load != nullptr)
{
vparam[2] = load->Eval(trans, ip);
}
// calculate uu
B.MultTranspose(elfun, uu);
// calculate derivative of the energy with respect to uu
rdf.VectorFunc(vparam,uu,du);
B.Mult(du, lvec);
elvect.Add(w, lvec);
} // end integration loop
MFEM_PERF_END("AssembleElementVector");
}
virtual void AssembleElementGrad(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun,
DenseMatrix &elmat)
{
MFEM_PERF_BEGIN("AssembleElementGrad");
const int ndof = el.GetDof();
const int ndim = el.GetDim();
const int spaceDim = trans.GetSpaceDim();
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
elmat.SetSize(ndof, ndof);
elmat = 0.0;
DenseMatrix B(ndof, 4); // [diff_x,diff_y,diff_z, shape]
DenseMatrix A(ndof, 4);
Vector uu(4); // [diff_x,diff_y,diff_z,u]
DenseMatrix duu(4, 4);
B = 0.0;
uu = 0.0;
double w;
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
Mult(dshape_iso, trans.InverseJacobian(), dshape_xyz);
// set the matrix B
for (int jj = 0; jj < spaceDim; jj++)
{
B.SetCol(jj, dshape_xyz.GetColumn(jj));
}
B.SetCol(3, shapef);
// set the power
if (pp != nullptr)
{
vparam[0] = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
vparam[1] = coeff->Eval(trans, ip);
}
// add the contribution from the load
if (load != nullptr)
{
vparam[2] = load->Eval(trans, ip);
}
// calculate uu
B.MultTranspose(elfun, uu);
// calculate derivative of the energy with respect to uu
rdf.Jacobian(vparam,uu,duu);
Mult(B, duu, A);
AddMult_a_ABt(w, A, B, elmat);
} // end integration loop
MFEM_PERF_END("AssembleElementGrad");
}
private:
Vector vparam; // [power, epsilon, load]
};
/// Implements hand-coded integrator for a p-Laplacian problem. Utilized as
/// alternative for the pLaplaceAD class based on automatic differentiation.
class pLaplace : public NonlinearFormIntegrator
{
protected:
Coefficient *pp;
Coefficient *coeff;
Coefficient *load;
public:
pLaplace()
{
coeff = nullptr;
pp = nullptr;
load = nullptr;
}
pLaplace(Coefficient &pp_) : pp(&pp_), coeff(nullptr), load(nullptr) {}
pLaplace(Coefficient &pp_, Coefficient &q, Coefficient &ld_)
: pp(&pp_), coeff(&q), load(&ld_)
{}
virtual ~pLaplace() {}
virtual double GetElementEnergy(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun)
{
double energy = 0.0;
const int ndof = el.GetDof();
const int ndim = el.GetDim();
const int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector grad(spaceDim);
double w;
double detJ;
double nrgrad2;
double ppp = 2.0;
double eee = 0.0;
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// calculate the gradient
dshape_xyz.MultTranspose(elfun, grad);
nrgrad2 = grad * grad / (detJ * detJ);
// set the power
if (pp != nullptr)
{
ppp = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
eee = coeff->Eval(trans, ip);
}
energy = energy + w * std::pow(nrgrad2 + eee * eee, ppp / 2.0) / ppp;
// add the contribution from the load
if (load != nullptr)
{
energy = energy - w * (shapef * elfun) * load->Eval(trans, ip);
}
}
return energy;
}
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun,
Vector &elvect)
{
MFEM_PERF_BEGIN("AssembleElementVector");
const int ndof = el.GetDof();
const int ndim = el.GetDim();
const int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector grad(spaceDim);
Vector lvec(ndof);
elvect.SetSize(ndof);
elvect = 0.0;
double w;
double detJ;
double nrgrad;
double aa;
double ppp = 2.0;
double eee = 0.0;
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// calculate the gradient
dshape_xyz.MultTranspose(elfun, grad);
nrgrad = grad.Norml2() / detJ;
// grad is not scaled so far, i.e., grad=grad/detJ
// set the power
if (pp != nullptr)
{
ppp = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
eee = coeff->Eval(trans, ip);
}
// compute (norm of the gradient)^2 + epsilon^2
aa = nrgrad * nrgrad + eee * eee;
aa = std::pow(aa, (ppp - 2.0) / 2.0);
dshape_xyz.Mult(grad, lvec);
elvect.Add(w * aa / (detJ * detJ), lvec);
// add loading
if (load != nullptr)
{
elvect.Add(-w * load->Eval(trans, ip), shapef);
}
} // end integration loop
MFEM_PERF_END("AssembleElementVector");
}
virtual void AssembleElementGrad(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun,
DenseMatrix &elmat)
{
MFEM_PERF_BEGIN("AssembleElementGrad");
const int ndof = el.GetDof();
const int ndim = el.GetDim();
const int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector grad(spaceDim);
Vector lvec(ndof);
// set the size of the element matrix
elmat.SetSize(ndof, ndof);
elmat = 0.0;
double w; // integration weight
double detJ;
double nrgrad; // norm of the gradient
double aa0; // original nonlinear diffusion coefficient
double aa1; // gradient of the above
double ppp = 2.0; // power in the P-Laplacian
double eee = 0.0; // regularization parameter
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// grad is not scaled so far,i.e., grad=grad/detJ
// set the power
if (pp != nullptr)
{
ppp = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
eee = coeff->Eval(trans, ip);
}
// calculate the gradient
dshape_xyz.MultTranspose(elfun, grad);
nrgrad = grad.Norml2() / detJ;
// (u_x^2+u_y^2+u_z^2+\varepsilon^2)
aa0 = nrgrad * nrgrad + eee * eee;
aa1 = std::pow(aa0, (ppp - 2.0) / 2.0);
aa0 = (ppp - 2.0) * std::pow(aa0, (ppp - 4.0) / 2.0);
dshape_xyz.Mult(grad, lvec);
w = w / (detJ * detJ);
AddMult_a_VVt(w * aa0 / (detJ * detJ), lvec, elmat);
AddMult_a_AAt(w * aa1, dshape_xyz, elmat);
} // end integration loop
MFEM_PERF_END("AssembleElementGrad");
}
};
/// Implements AD enabled integrator for a p-Laplacian problem. The tangent
/// matrix is computed using the residual of the element. The template argument
/// should be equal to the size of the residual vector (element vector), i.e.,
/// the user should specify the size to match the exact vector size for the
/// considered order of the shape functions.
template<int sizeres=10>
class pLaplaceSL : public NonlinearFormIntegrator
{
protected:
Coefficient *pp;
Coefficient *coeff;
Coefficient *load;
public:
pLaplaceSL()
{
coeff = nullptr;
pp = nullptr;
load = nullptr;
}
pLaplaceSL(Coefficient &pp_) : pp(&pp_), coeff(nullptr), load(nullptr) {}
pLaplaceSL(Coefficient &pp_, Coefficient &q, Coefficient &ld_)
: pp(&pp_), coeff(&q), load(&ld_)
{}
virtual ~pLaplaceSL() {}
virtual double GetElementEnergy(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun)
{
double energy = 0.0;
const int ndof = el.GetDof();
const int ndim = el.GetDim();
const int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector grad(spaceDim);
double w;
double detJ;
double nrgrad2;
double ppp = 2.0;
double eee = 0.0;
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// calculate the gradient
dshape_xyz.MultTranspose(elfun, grad);
nrgrad2 = grad * grad / (detJ * detJ);
// set the power
if (pp != nullptr)
{
ppp = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
eee = coeff->Eval(trans, ip);
}
energy = energy + w * std::pow(nrgrad2 + eee * eee, ppp / 2.0) / ppp;
// add the contribution from the load
if (load != nullptr)
{
energy = energy - w * (shapef * elfun) * load->Eval(trans, ip);
}
}
return energy;
}
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun,
Vector &elvect)
{
MFEM_PERF_BEGIN("AssembleElementVector");
const int ndof = el.GetDof();
const int ndim = el.GetDim();
const int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
Vector shapef(ndof);
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
Vector grad(spaceDim);
Vector lvec(ndof);
elvect.SetSize(ndof);
elvect = 0.0;
double w;
double detJ;
double nrgrad;
double aa;
double ppp = 2.0;
double eee = 0.0;
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w; //w;
el.CalcDShape(ip, dshape_iso);
el.CalcShape(ip, shapef);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// calculate the gradient
dshape_xyz.MultTranspose(elfun, grad);
nrgrad = grad.Norml2() / detJ;
// grad is not scaled so far, i.e., grad=grad/detJ
// set the power
if (pp != nullptr)
{
ppp = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
eee = coeff->Eval(trans, ip);
}
aa = nrgrad * nrgrad + eee * eee;
aa = std::pow(aa, (ppp - 2.0) / 2.0);
dshape_xyz.Mult(grad, lvec);
elvect.Add(w * aa / (detJ * detJ), lvec);
// add loading
if (load != nullptr)
{
elvect.Add(-w * load->Eval(trans, ip), shapef);
}
} // end integration loop
MFEM_PERF_END("AssembleElementVector");
}
virtual void AssembleElementGrad(const FiniteElement &el,
ElementTransformation &trans,
const Vector &elfun,
DenseMatrix &elmat)
{
MFEM_PERF_BEGIN("AssembleElementGrad");
const int ndof = el.GetDof();
const int ndim = el.GetDim();
const int spaceDim = trans.GetSpaceDim();
bool square = (ndim == spaceDim);
int order = 2 * el.GetOrder() + trans.OrderGrad(&el);
const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order));
DenseMatrix dshape_iso(ndof, ndim);
DenseMatrix dshape_xyz(ndof, spaceDim);
elmat.SetSize(ndof, ndof);
elmat = 0.0;
double w;
double detJ;
double ppp = 2.0;
double eee = 0.0;
mfem::Vector param(3); param=0.0;
// Computes the residual at an integration point. The implementation is a
// copy of the integration loop in AssembleElementVector.
auto resfun = [&](mfem::Vector& vparam, mfem::ad::ADVectorType& uu,
mfem::ad::ADVectorType& vres)
{
vres.SetSize(uu.Size()); vres=0.0;
mfem::ad::ADVectorType grad(spaceDim);
mfem::ad::ADFloatType nrgrad;
mfem::ad::ADFloatType aa;
mfem::ad::ADVectorType lvec(ndof);
for (int i = 0; i < ir.GetNPoints(); i++)
{
lvec=0.0;
const IntegrationPoint &ip = ir.IntPoint(i);
trans.SetIntPoint(&ip);
w = trans.Weight();
detJ = (square ? w : w * w);
w = ip.weight * w;
el.CalcDShape(ip, dshape_iso);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz);
// dshape_xyz should be divided by detJ for obtaining the real value
// grad is not scaled so far,i.e., grad=grad/detJ
// set the power
if (pp != nullptr)
{
ppp = pp->Eval(trans, ip);
}
// set the coefficient ensuring positiveness of the tangent matrix
if (coeff != nullptr)
{
eee = coeff->Eval(trans, ip);
}
grad=0.0;
// calculate the gradient
for (int i=0; i<spaceDim; i++)
{
for (int j=0; j<ndof; j++)
{
grad[i]= grad[i]+ dshape_xyz(j,i)*uu[j];
}
}
nrgrad= (grad*grad)/(detJ*detJ);
aa = nrgrad + eee * eee;
aa = pow(aa, (ppp - 2.0) / 2.0);
for (int i=0; i<spaceDim; i++)
{
for (int j=0; j<ndof; j++)
{
lvec[j] = lvec[j] + dshape_xyz(j,i) * grad[i];
}
}
for (int j=0; j<ndof; j++)
{
vres[j]=vres[j] + lvec[j] * (w*aa/(detJ*detJ));
}
}
};
mfem::Vector bla(elfun);
// calculate the gradient - only for a fixed ndof
mfem::VectorFuncAutoDiff<sizeres,sizeres,3> fdr(resfun);
fdr.Jacobian(param, bla, elmat);
MFEM_PERF_END("AssembleElementGrad");
}
};
} // namespace mfem
#endif
+650
View File
@@ -0,0 +1,650 @@
// Copyright (c) 2010-2021, 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 FDUAL_H
#define FDUAL_H
#include <cmath>
#include <type_traits>
namespace mfem
{
namespace ad
{
/** The FDualNumber template class provides forward automatic differentiation
(see https://en.wikipedia.org/wiki/Automatic_differentiation) implementation
based on dual numbers.
The derivative of an arbitrary function double f(double a) can be obtained
by replacing the double type for the return value and the argument a with
FDualNumber<double>, i.e., FDualNumber<double> f(FDualNumber<double> a). The
derivative is evaluated automatically by calling the function r=f(a). The
value of the function is stored in r.pr and the derivative in r.du. These
can be extracted by the corresponding methods real()/prim() and dual().
Internally, the function f can be composed of standard functions predefined
for FDualNumber type. These consist of a large set of functions replicating
the functionality of the standard math library, i.e., sin, cos, exp, log,
etc. New functions (non-member) can be equally added to the class. Example:
\code{.cpp}
template<typename tbase>
inline FDualNumber<tbase> cos(const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(cos(f.real()), -f.dual() * sin(f.real()));
}
\endcode
The real part of the return value consists of the standard real value of the
function, i.e., cos(f.real()).
The dual part of the return value consists of the first derivative of the
function with respect to the real part of the argument -sin(f.reaf)
multiplied with the dual part of the argument f.dual().
*/
template<typename tbase>
class FDualNumber
{
private:
/// Real value
tbase pr;
/// Dual value holding derivative information
tbase du;
public:
/// Standard constructor - both values are set to zero.
FDualNumber() : pr(0), du(0) {}
/// The constructor utilized in nested definition of dual numbers. It is
/// used for second and higher order derivatives.
template<class fltyp,
class = typename std::enable_if<std::is_arithmetic<fltyp>::value>::type>
FDualNumber(fltyp &f) : pr(f), du(0)
{}
/// The constructor utilized in nested definition of dual numbers. It is
/// used for second and higher order derivatives.
template<class fltyp,
class = typename std::enable_if<std::is_arithmetic<fltyp>::value>::type>
FDualNumber(const fltyp &f) : pr(f), du(0)
{}
/// Standard constructor with user supplied input for both parts of the dual
/// number.
FDualNumber(tbase &pr_, tbase &du_) : pr(pr_), du(du_) {}
/// Standard constructor with user supplied input for both parts of the dual
/// number.
FDualNumber(const tbase &pr_, const tbase &du_) : pr(pr_), du(du_) {}
/// Standard constructor with user supplied dual number.
FDualNumber(FDualNumber<tbase> &nm) : pr(nm.pr), du(nm.du) {}
/// Standard constructor with user supplied dual number.
FDualNumber(const FDualNumber<tbase> &nm) : pr(nm.pr), du(nm.du) {}
/// Return the real value of the dual number.
tbase prim() const { return pr; }
/// Same as prim(). Return the real value of the dual number.
tbase real() const { return pr; }
/// Return the dual value of the dual number.
tbase dual() const { return du; }
/// Set the primal and the dual values.
void set(const tbase &pr_, const tbase &du_)
{
pr = pr_;
du = du_;
}
/// Set the primal value.
void prim(const tbase &pr_) { pr = pr_; }
/// Set the primal value.
void real(const tbase &pr_) { pr = pr_; }
/// Set the dual value.
void dual(const tbase &du_) { du = du_; }
/// Set the primal value.
void setReal(const tbase &pr_) { pr = pr_; }
/// Set the dual value.
void setDual(const tbase &du_) { du = du_; }
/// operator =
FDualNumber<tbase> &operator=(tbase sc_)
{
pr = sc_;
du = tbase(0);
return *this;
}
/// operator +=
FDualNumber<tbase> &operator+=(tbase sc_)
{
pr = pr + sc_;
return *this;
}
/// operator -=
FDualNumber<tbase> &operator-=(tbase sc_)
{
pr = pr - sc_;
return *this;
}
/// operator *=
FDualNumber<tbase> &operator*=(tbase sc_)
{
pr = pr * sc_;
du = du * sc_;
return *this;
}
/// operator /=
FDualNumber<tbase> &operator/=(tbase sc_)
{
pr = pr / sc_;
du = du / sc_;
return *this;
}
/// operator =
FDualNumber<tbase> &operator=(const FDualNumber<tbase> &f)
{
pr = f.real();
du = f.dual();
return *this;
}
/// operator +=
FDualNumber<tbase> &operator+=(const FDualNumber<tbase> &f)
{
pr += f.real();
du += f.dual();
return *this;
}
/// operator -=
FDualNumber<tbase> &operator-=(const FDualNumber<tbase> &f)
{
pr -= f.real();
du -= f.dual();
return *this;
}
/// operator *=
FDualNumber<tbase> &operator*=(const FDualNumber<tbase> &f)
{
du = du * f.real();
du = du + pr * f.dual();
pr = pr * f.real();
return *this;
}
/// operator /=
FDualNumber<tbase> &operator/=(const FDualNumber<tbase> &f_)
{
pr = pr / f_.real();
du = du - pr * f_.dual();
du = du / f_.real();
return *this;
}
};
/// non-member functions
/// boolean operation ==
template<typename tbase>
inline bool operator==(const FDualNumber<tbase> &a1,
const FDualNumber<tbase> &a2)
{
return a1.real() == a2.real();
}
/// boolean operation ==
template<typename tbase>
inline bool operator==(tbase a, const FDualNumber<tbase> &f_)
{
return a == f_.real();
}
/// boolean operation ==
template<typename tbase>
inline bool operator==(const FDualNumber<tbase> &a, tbase b)
{
return a.real() == b;
}
/// boolean operation <
template<typename tbase>
inline bool operator<(const FDualNumber<tbase> &f1,
const FDualNumber<tbase> &f2)
{
return f1.real() < f2.real();
}
/// boolean operation <
template<typename tbase>
inline bool operator<(const FDualNumber<tbase> &f, tbase a)
{
return f.real() < a;
}
/// boolean operation <
template<typename tbase>
inline bool operator<(tbase a, const FDualNumber<tbase> &f)
{
return a < f.real();
}
/// boolean operation >
template<typename tbase>
inline bool operator>(const FDualNumber<tbase> &f1,
const FDualNumber<tbase> &f2)
{
return f1.real() > f2.real();
}
/// boolean operation >
template<typename tbase>
inline bool operator>(const FDualNumber<tbase> &f, tbase a)
{
return f.real() > a;
}
/// boolean operation >
template<typename tbase>
inline bool operator>(tbase a, const FDualNumber<tbase> &f)
{
return (a > f.real());
}
/// Negate the real and the dual parts.
template<typename tbase>
inline FDualNumber<tbase> operator-(const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(-f.real(), -f.dual());
}
/// [dual number] - [base number]
template<typename tbase>
inline FDualNumber<tbase> operator-(const FDualNumber<tbase> &f, tbase a)
{
return FDualNumber<tbase>(f.real() - a, f.dual());
}
/// [dual number<dual number>] - [base number]
template<typename tbase>
inline FDualNumber<FDualNumber<tbase>> operator-(const
FDualNumber<FDualNumber<tbase>> &f, tbase a)
{
return FDualNumber<FDualNumber<tbase>>(f.real() - a, f.dual());
}
/// [dual number] + [base number]
template<typename tbase>
inline FDualNumber<tbase> operator+(const FDualNumber<tbase> &f, tbase a)
{
return FDualNumber<tbase>(f.real() + a, f.dual());
}
/// [dual number<dual number>] + [base number]
template<typename tbase>
inline FDualNumber<FDualNumber<tbase>> operator+(const
FDualNumber<FDualNumber<tbase>> &f, tbase a)
{
return FDualNumber<FDualNumber<tbase>>(f.real() + a, f.dual());
}
/// [dual number] * [base number]
template<typename tbase>
inline FDualNumber<tbase> operator*(const FDualNumber<tbase> &f, tbase a)
{
return FDualNumber<tbase>(f.real() * a, f.dual() * a);
}
/// [dual number] / [base number]
template<typename tbase>
inline FDualNumber<tbase> operator/(const FDualNumber<tbase> &f, tbase a)
{
return FDualNumber<tbase>(f.real() / a, f.dual() / a);
}
/// [dual number<dual number>] / [base number]
template<typename tbase>
inline FDualNumber<FDualNumber<tbase>> operator/(const
FDualNumber<FDualNumber<tbase>> &f, tbase a)
{
return FDualNumber<FDualNumber<tbase>>(f.real() / a, f.dual() / a);
}
/// [base number] + [dual number]
template<typename tbase>
inline FDualNumber<tbase> operator+(tbase a, const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(a + f.real(), f.dual());
}
/// [base number] + [dual number<dual number>]
template<typename tbase>
inline FDualNumber<FDualNumber<tbase>> operator+(tbase a,
const FDualNumber<FDualNumber<tbase>> &f)
{
return FDualNumber<FDualNumber<tbase>>(a + f.real(), f.dual());
}
/// [base number] - [dual number]
template<typename tbase>
inline FDualNumber<tbase> operator-(tbase a, const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(a - f.real(), -f.dual());
}
/// [base number] - [dual number<dual number>]
template<typename tbase>
inline FDualNumber<FDualNumber<tbase>> operator-(tbase a,
const FDualNumber<FDualNumber<tbase>> &f)
{
return FDualNumber<FDualNumber<tbase>>(a - f.real(), -f.dual());
}
/// [base number] * [dual number]
template<typename tbase>
inline FDualNumber<tbase> operator*(tbase a, const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(f.real() * a, f.dual() * a);
}
/// [base number] * [dual number<dual number>]
template<typename tbase>
inline FDualNumber<FDualNumber<tbase>> operator*(tbase a,
const FDualNumber<FDualNumber<tbase>> &f)
{
return FDualNumber<FDualNumber<tbase>>(f.real() * a, f.dual() * a);
}
/// [base number] / [dual number]
template<typename tbase>
inline FDualNumber<tbase> operator/(tbase a, const FDualNumber<tbase> &f)
{
a = a / f.real();
return FDualNumber<tbase>(a, -a * f.dual() / f.real());
}
/// [dual number] + [dual number]
template<typename tbase>
inline FDualNumber<tbase> operator+(const FDualNumber<tbase> &f1,
const FDualNumber<tbase> &f2)
{
return FDualNumber<tbase>(f1.real() + f2.real(), f1.dual() + f2.dual());
}
/// [dual number] - [dual number]
template<typename tbase>
inline FDualNumber<tbase> operator-(const FDualNumber<tbase> &f1,
const FDualNumber<tbase> &f2)
{
return FDualNumber<tbase>(f1.real() - f2.real(), f1.dual() - f2.dual());
}
/// [dual number] * [dual number]
template<typename tbase>
inline FDualNumber<tbase> operator*(const FDualNumber<tbase> &f1,
const FDualNumber<tbase> &f2)
{
return FDualNumber<tbase>(f1.real() * f2.real(),
f1.real() * f2.dual() + f1.dual() * f2.real());
}
/// [dual number] / [dual number]
template<typename tbase>
inline FDualNumber<tbase> operator/(const FDualNumber<tbase> &f1,
const FDualNumber<tbase> &f2)
{
tbase a = tbase(1) / f2.real();
tbase b = f1.real() * a;
return FDualNumber<tbase>(b, (f1.dual() - f2.dual() * b) * a);
}
/// acos([dual number])
template<typename tbase>
inline FDualNumber<tbase> acos(const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(acos(f.real()),
-f.dual() / sqrt(tbase(1) - f.real() * f.real()));
}
/// acos([dual number<double>])
template<>
inline FDualNumber<double> acos(const FDualNumber<double> &f)
{
return FDualNumber<double>(std::acos(f.real()),
-f.dual() / std::sqrt(double(1) - f.real() * f.real()));
}
/// asin([dual number])
template<typename tbase>
inline FDualNumber<tbase> asin(const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(asin(f.real()),
f.dual() / sqrt(tbase(1) - f.real() * f.real()));
}
/// asin([dual number<double>])
template<>
inline FDualNumber<double> asin(const FDualNumber<double> &f)
{
return FDualNumber<double>(std::asin(f.real()),
f.dual() / std::sqrt(double(1) - f.real() * f.real()));
}
/// atan([dual number])
template<typename tbase>
inline FDualNumber<tbase> atan(const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(atan(f.real()),
f.dual() / (tbase(1) + f.real() * f.real()));
}
/// atan([dual number<double>])
template<>
inline FDualNumber<double> atan(const FDualNumber<double> &f)
{
return FDualNumber<double>(std::atan(f.real()),
f.dual() / (double(1) + f.real() * f.real()));
}
/// cos([dual number])
template<typename tbase>
inline FDualNumber<tbase> cos(const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(cos(f.real()), -f.dual() * sin(f.real()));
}
/// cos([dual number<double>])
template<>
inline FDualNumber<double> cos(const FDualNumber<double> &f)
{
return FDualNumber<double>(std::cos(f.real()), -f.dual() * std::sin(f.real()));
}
/// cosh([dual number])
template<typename tbase>
inline FDualNumber<tbase> cosh(const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(cosh(f.real()), f.dual() * sinh(f.real()));
}
/// cosh([dual number<double>])
template<>
inline FDualNumber<double> cosh(const FDualNumber<double> &f)
{
return FDualNumber<double>(std::cosh(f.real()), f.dual() * std::sinh(f.real()));
}
/// exp([dual number])
template<typename tbase>
inline FDualNumber<tbase> exp(const FDualNumber<tbase> &f)
{
tbase x = exp(f.real());
return FDualNumber<tbase>(x, f.dual() * x);
}
/// exp([dual number<double>])
template<>
inline FDualNumber<double> exp(const FDualNumber<double> &f)
{
double x = std::exp(f.real());
return FDualNumber<double>(x, f.dual() * x);
}
/// log([dual number])
template<typename tbase>
inline FDualNumber<tbase> log(const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(log(f.real()), f.dual() / f.real());
}
/// log([dual number<double>])
template<>
inline FDualNumber<double> log(const FDualNumber<double> &f)
{
return FDualNumber<double>(std::log(f.real()), f.dual() / f.real());
}
/// log10([dual number])
template<typename tbase>
inline FDualNumber<tbase> log10(const FDualNumber<tbase> &f)
{
return log(f) / log(tbase(10));
}
/// log10([dual number<double>])
template<>
inline FDualNumber<double> log10(const FDualNumber<double> &f)
{
return log(f) / std::log(double(10));
}
/// pow([dual number],[dual number])
template<typename tbase>
inline FDualNumber<tbase> pow(const FDualNumber<tbase> &a,
const FDualNumber<tbase> &b)
{
return exp(log(a) * b);
}
/// pow([dual number], [base number])
template<typename tbase, typename tbase1>
inline FDualNumber<tbase> pow(const FDualNumber<tbase> &a, const tbase1 &b)
{
return exp(log(a) * tbase(b));
}
/// pow([base number], [dual number])
template<typename tbase, typename tbase1>
inline FDualNumber<tbase> pow(const tbase1 &a, const FDualNumber<tbase> &b)
{
return exp(log(tbase(a)) * b);
}
/// pow([base number], [dual number<double>])
template<>
inline FDualNumber<double> pow(const double &a, const FDualNumber<double> &b)
{
return exp(std::log(a) * b);
}
/// sin([dual number])
template<typename tbase>
inline FDualNumber<tbase> sin(const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(sin(f.real()), f.dual() * cos(f.real()));
}
/// sin([dual number<double>])
template<>
inline FDualNumber<double> sin(const FDualNumber<double> &f)
{
return FDualNumber<double>(std::sin(f.real()), f.dual() * std::cos(f.real()));
}
/// sinh([dual number])
template<typename tbase>
inline FDualNumber<tbase> sinh(const FDualNumber<tbase> &f)
{
return FDualNumber<tbase>(sinh(f.real()), f.dual() * cosh(f.real()));
}
/// sinh([dual number<double>])
template<>
inline FDualNumber<double> sinh(const FDualNumber<double> &f)
{
return FDualNumber<double>(std::sinh(f.real()), f.dual() * std::cosh(f.real()));
}
/// sqrt([dual number])
template<typename tbase>
inline FDualNumber<tbase> sqrt(const FDualNumber<tbase> &f)
{
tbase a = sqrt(f.real());
return FDualNumber<tbase>(a, f.dual() / (tbase(2) * a));
}
/// sqrt([dual number<double>])
template<>
inline FDualNumber<double> sqrt(const FDualNumber<double> &f)
{
double a = std::sqrt(f.real());
return FDualNumber<double>(a, f.dual() / (double(2) * a));
}
/// tan([dual number])
template<typename tbase>
inline FDualNumber<tbase> tan(const FDualNumber<tbase> &f)
{
tbase a = tan(f.real());
return FDualNumber<tbase>(a, f.dual() * (tbase(1) + a * a));
}
/// tan([dual number<double>])
template<>
inline FDualNumber<double> tan(const FDualNumber<double> &f)
{
double a = std::tan(f.real());
return FDualNumber<double>(a, f.dual() * (double(1) + a * a));
}
/// tanh([dual number])
template<typename tbase>
inline FDualNumber<tbase> tanh(const FDualNumber<tbase> &f)
{
tbase a = tanh(f.real());
return FDualNumber<tbase>(a, f.dual() * (tbase(1) - a * a));
}
/// tanh([dual number<double>])
template<>
inline FDualNumber<double> tanh(const FDualNumber<double> &f)
{
double a = std::tanh(f.real());
return FDualNumber<double>(a, f.dual() * (double(1) - a * a));
}
} // namespace ad
} // namespace mfem
#endif
+79
View File
@@ -0,0 +1,79 @@
# Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/autodiff/,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
# Use the MFEM install directory
# MFEM_INSTALL_DIR = ../../mfem
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
# Include defaults.mk to get XLINKER
DEFAULTS_MK = $(MFEM_DIR)/config/defaults.mk
include $(DEFAULTS_MK)
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
ADIFF_COMMON_SRC =
ADIFF_COMMON_OBJ = $(ADIFF_COMMON_SRC:.cpp=.o)
SEQ_MINIAPPS = seq_example seq_test
PAR_MINIAPPS = par_example
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
else
MINIAPPS = $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean clean-build clean-exec
# Remove built-in rules
%: %.cpp
%.o: %.cpp
%: %.o $(ADIFF_COMMON_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) $^ -o $@ $(MFEM_LIBS)
%.o: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@
all: $(MINIAPPS)
MFEM_TESTS = MINIAPPS
include $(MFEM_TEST_MK)
# Testing: Parallel vs. serial runs
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
TEST_NAME := ADIFF miniapp
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI), $(TEST_NAME))
%-test-seq: %
@$(call mfem-test,$<,, $(TEST_NAME))
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_MINIAPPS) $(PAR_MINIAPPS)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -rf Example*
+553
View File
@@ -0,0 +1,553 @@
// Copyright (c) 2010-2021, 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.
//
// MFEM AD Example - Parallel Version
//
// Compile with: make par_example
//
// Sample runs: mpirun -np 2 par_example -m ../data/beam-quad.mesh -pp 3.8
// mpirun -np 2 par_example -m ../data/beam-tri.mesh -pp 7.2
// mpirun -np 2 par_example -m ../data/beam-hex.mesh
// mpirun -np 2 par_example -m ../data/beam-tet.mesh
// mpirun -np 2 par_example -m ../data/beam-wedge.mesh
//
// Description: This examples solves a quasi-static nonlinear p-Laplacian
// problem with zero Dirichlet boundary conditions applied on all
// defined boundaries
//
// The example demonstrates the use of nonlinear operators
// combined with automatic differentiation (AD). The integrators
// are defined in example.hpp. Selecting integrator = 0 will use
// the manually implemented integrator. Selecting integrator = 1
// or 2 will utilize one of the AD integrators.
//
// We recommend viewing examples 1 and 19, before viewing this
// example.
#include "example.hpp"
using namespace mfem;
enum IntegratorType
{
HandCodedIntegrator = 0,
ADJacobianIntegrator = 1,
ADHessianIntegrator = 2
};
/// Non-linear solver for the p-Laplacian problem.
class ParNLSolverPLaplacian
{
public:
/// Constructor Input: imesh - FE mesh, finite element space, power for the
/// p-Laplacian, external load (source, input), regularization parameter
ParNLSolverPLaplacian(MPI_Comm comm, ParMesh& imesh,
ParFiniteElementSpace& ifespace,
double powerp=2,
Coefficient* load=nullptr,
double regularizationp=1e-7)
{
lcomm = comm;
// default parameters for the Newton solver
newton_rtol = 1e-4;
newton_atol = 1e-8;
newton_iter = 10;
// linear solver
linear_rtol = 1e-7;
linear_atol = 1e-15;
linear_iter = 500;
print_level = 0;
// set the mesh
mesh=&imesh;
// set the fespace
fespace=&ifespace;
// set the parameters
plap_epsilon=new ConstantCoefficient(regularizationp);
plap_power=new ConstantCoefficient(powerp);
if (load==nullptr)
{
plap_input=new ConstantCoefficient(1.0);
input_ownership=true;
}
else
{
plap_input=load;
input_ownership=false;
}
nlform=nullptr;
nsolver=nullptr;
gmres=nullptr;
prec=nullptr;
// set the default integrator
integ=IntegratorType::HandCodedIntegrator;
}
~ParNLSolverPLaplacian()
{
delete nlform;
delete nsolver;
delete prec;
delete gmres;
if (input_ownership) { delete plap_input;}
delete plap_epsilon;
delete plap_power;
}
/// Set the integrator.
/// 0 - hand coded, 1 - AD based (compute only Hessian by AD),
/// 2 - AD based (compute residual and Hessian by AD)
void SetIntegrator(IntegratorType intr)
{
integ=intr;
}
// set relative tolerance for the Newton solver
void SetNRRTol(double rtol)
{
newton_rtol=rtol;
}
// set absolute tolerance for the Newton solver
void SetNRATol(double atol)
{
newton_atol=atol;
}
// set max iterations for the NR solver
void SetMaxNRIter(int miter)
{
newton_iter=miter;
}
void SetLSRTol(double rtol)
{
linear_rtol=rtol;
}
void SetLSATol(double atol)
{
linear_atol=atol;
}
// set max iterations for the linear solver
void SetMaxLSIter(int miter)
{
linear_iter=miter;
}
// set the print level
void SetPrintLevel(int plev)
{
print_level=plev;
}
/// The state vector is used as initial condition for the NR solver. On
/// return the statev holds the solution to the problem.
void Solve(Vector& statev)
{
if (nlform==nullptr)
{
AllocSolvers();
}
Vector b; // RHS is zero
nsolver->Mult(b, statev);
}
/// Compute the energy
double GetEnergy(Vector& statev)
{
if (nlform==nullptr)
{
// allocate the solvers
AllocSolvers();
}
return nlform->GetEnergy(statev);
}
private:
void AllocSolvers()
{
if (nlform!=nullptr) { delete nlform;}
if (nsolver!=nullptr) { delete nsolver;}
if (gmres!=nullptr) { delete gmres;}
if (prec!=nullptr) { delete prec;}
// Define the essential boundary attributes
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
nlform = new ParNonlinearForm(fespace);
if (integ==IntegratorType::HandCodedIntegrator)
{
nlform->AddDomainIntegrator(new pLaplace(*plap_power,*plap_epsilon,
*plap_input));
}
else if (integ==IntegratorType::ADJacobianIntegrator)
{
// The template integrator is based on automatic differentiation. For
// ADJacobianIntegrator the residual (vector function) at an
// integration point is implemented as a functor by MyResidualFunctor.
// The vector function has a return size of four(4), four state
// arguments, and three(3) parameters. MyResidualFunctor is a template
// argument to the actual template class performing the differentiation
// - in this case, QVectorFuncAutoDiff. The derivatives are used in the
// integration loop in the integrator pLaplaceAD.
nlform->AddDomainIntegrator(new
pLaplaceAD<mfem::QVectorFuncAutoDiff<MyResidualFunctor,4,4,3>>(*plap_power,
*plap_epsilon,*plap_input));
}
else if (integ==IntegratorType::ADHessianIntegrator)
{
// The main difference from the previous case is that the user has to
// implement only a functional evaluation at an integration point. The
// implementation is in MyEnergyFunctor, which takes four state
// arguments and three parameters. The residual vector is the first
// derivative of the energy/functional with respect to the state
// variables, and the Hessian is the second derivative. Automatic
// differentiation is used for evaluating both of them.
nlform->AddDomainIntegrator(new
pLaplaceAD<mfem::QFunctionAutoDiff<MyEnergyFunctor,4,3>>(*plap_power,
*plap_epsilon,*plap_input));
}
nlform->SetEssentialBC(ess_bdr);
prec = new HypreBoomerAMG();
prec->SetPrintLevel(print_level);
gmres = new GMRESSolver(lcomm);
gmres->SetAbsTol(linear_atol);
gmres->SetRelTol(linear_rtol);
gmres->SetMaxIter(linear_iter);
gmres->SetPrintLevel(print_level);
gmres->SetPreconditioner(*prec);
nsolver = new NewtonSolver(lcomm);
nsolver->iterative_mode = true;
nsolver->SetSolver(*gmres);
nsolver->SetOperator(*nlform);
nsolver->SetPrintLevel(print_level);
nsolver->SetRelTol(newton_rtol);
nsolver->SetAbsTol(newton_atol);
nsolver->SetMaxIter(newton_iter);
}
double newton_rtol;
double newton_atol;
int newton_iter;
double linear_rtol;
double linear_atol;
int linear_iter;
int print_level;
// power of the p-laplacian
Coefficient* plap_power;
// regularization parameter
Coefficient* plap_epsilon;
// load(input) parameter
Coefficient* plap_input;
// flag indicating the ownership of plap_input
bool input_ownership;
MPI_Comm lcomm;
ParMesh *mesh;
ParFiniteElementSpace *fespace;
ParNonlinearForm *nlform;
HypreBoomerAMG *prec;
GMRESSolver *gmres;
NewtonSolver *nsolver;
IntegratorType integ;
};
int main(int argc, char *argv[])
{
// 1. Initialize MPI
int num_procs, myrank;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
// Define Caliper ConfigManager
#ifdef MFEM_USE_CALIPER
cali::ConfigManager mgr;
#endif
// Caliper instrumentation
MFEM_PERF_FUNCTION;
// 2. Parse command-line options
const char *mesh_file = "../../data/beam-tet.mesh";
int ser_ref_levels = 3;
int par_ref_levels = 1;
int order = 1;
bool visualization = true;
double newton_rel_tol = 1e-4;
double newton_abs_tol = 1e-6;
int newton_iter = 10;
int print_level = 0;
double pp = 2.0; // p-Laplacian power
IntegratorType integrator = IntegratorType::ADHessianIntegrator;
int int_integrator = integrator;
// HandCodedIntegrator = 0 - do not use AD (hand coded)
// ADJacobianIntegrator = 1 - use AD for Hessian only
// ADHessianIntegrator = 2 - use AD for Residual and Hessian
const char* cali_config = "runtime-report";
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
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(&visualization,
"-vis",
"--visualization",
"-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&newton_rel_tol,
"-rel",
"--relative-tolerance",
"Relative tolerance for the Newton solve.");
args.AddOption(&newton_abs_tol,
"-abs",
"--absolute-tolerance",
"Absolute tolerance for the Newton solve.");
args.AddOption(&newton_iter,
"-it",
"--newton-iterations",
"Maximum iterations for the Newton solve.");
args.AddOption(&pp,
"-pp",
"--power-parameter",
"Power parameter (>=2.0) for the p-Laplacian.");
args.AddOption((&print_level), "-prt", "--print-level", "Print level.");
args.AddOption(&int_integrator,
"-int",
"--integrator",
"Integrator 0: standard; 1: AD for Hessian; 2: AD for residual and Hessian");
args.AddOption(&cali_config, "-p", "--caliper",
"Caliper configuration string.");
args.Parse();
if (!args.Good())
{
if (myrank == 0)
{
args.PrintUsage(std::cout);
}
MPI_Finalize();
return 1;
}
if (myrank == 0)
{
args.PrintOptions(std::cout);
}
integrator = static_cast<IntegratorType>(int_integrator);
StopWatch *timer = new StopWatch();
// Caliper configuration
#ifdef MFEM_USE_CALIPER
mgr.add(cali_config);
mgr.start();
#endif
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral and hexahedral meshes
// with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. 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.
for (int lev = 0; lev < ser_ref_levels; lev++)
{
mesh->UniformRefinement();
}
// 5. 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;
for (int lev = 0; lev < par_ref_levels; lev++)
{
pmesh->UniformRefinement();
}
// 6. Define the load for the p-Laplacian
ConstantCoefficient load(1.00);
// 7. Define the finite element spaces for the solution
H1_FECollection fec(order, dim);
ParFiniteElementSpace fespace(pmesh, &fec, 1, Ordering::byVDIM);
HYPRE_Int glob_size = fespace.GlobalTrueVSize();
if (myrank == 0)
{
std::cout << "Number of finite element unknowns: " << glob_size
<< std::endl;
}
// 8. 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;
HypreParVector *sv = x.GetTrueDofs();
// 9. Define ParaView DataCollection
ParaViewDataCollection *dacol = new ParaViewDataCollection("Example",
pmesh);
dacol->SetLevelsOfDetail(order);
dacol->RegisterField("sol", &x);
// 10. Define the NR solver
ParNLSolverPLaplacian* nr;
// 11. Start with linear diffusion - solvable for any initial guess
nr=new ParNLSolverPLaplacian(MPI_COMM_WORLD,*pmesh, fespace, 2.0, &load);
nr->SetIntegrator(integrator);
nr->SetMaxNRIter(newton_iter);
nr->SetNRATol(newton_abs_tol);
nr->SetNRRTol(newton_rel_tol);
nr->SetPrintLevel(print_level);
timer->Clear();
timer->Start();
nr->Solve(*sv);
timer->Stop();
if (myrank==0)
{
std::cout << "[pp=2] The solution time is: " << timer->RealTime()
<< std::endl;
}
// Compute the energy
double energy = nr->GetEnergy(*sv);
if (myrank==0)
{
std::cout << "[pp=2] The total energy of the system is E=" << energy
<< std::endl;
}
delete nr;
x.SetFromTrueDofs(*sv);
dacol->SetTime(2.0);
dacol->SetCycle(2);
dacol->Save();
// 12. Continue with powers higher than 2
for (int i = 3; i < pp; i++)
{
nr=new ParNLSolverPLaplacian(MPI_COMM_WORLD,*pmesh, fespace, (double)i, &load);
nr->SetIntegrator(integrator);
nr->SetMaxNRIter(newton_iter);
nr->SetNRATol(newton_abs_tol);
nr->SetNRRTol(newton_rel_tol);
nr->SetPrintLevel(print_level);
timer->Clear();
timer->Start();
nr->Solve(*sv);
timer->Stop();
if (myrank==0)
{
std::cout << "[pp="<<i<<"] The solution time is: " << timer->RealTime()
<< std::endl;
}
// Compute the energy
double energy = nr->GetEnergy(*sv);
if (myrank==0)
{
std::cout << "[pp="<<i<<"] The total energy of the system is E=" << energy
<< std::endl;
}
delete nr;
x.SetFromTrueDofs(*sv);
dacol->SetTime((double)i);
dacol->SetCycle(i);
dacol->Save();
}
// 13. Continue with the final power
if (std::abs(pp - 2.0) > std::numeric_limits<double>::epsilon())
{
nr=new ParNLSolverPLaplacian(MPI_COMM_WORLD,*pmesh, fespace, pp, &load);
nr->SetIntegrator(integrator);
nr->SetMaxNRIter(newton_iter);
nr->SetNRATol(newton_abs_tol);
nr->SetNRRTol(newton_rel_tol);
nr->SetPrintLevel(print_level);
timer->Clear();
timer->Start();
nr->Solve(*sv);
timer->Stop();
if (myrank==0)
{
std::cout << "[pp="<<pp<<"] The solution time is: " << timer->RealTime()
<< std::endl;
}
// Compute the energy
double energy = nr->GetEnergy(*sv);
if (myrank==0)
{
std::cout << "[pp="<<pp<<"] The total energy of the system is E=" << energy
<< std::endl;
}
delete nr;
x.SetFromTrueDofs(*sv);
dacol->SetTime(pp);
if (pp < 2.0)
{
dacol->SetCycle(std::floor(pp));
}
else
{
dacol->SetCycle(std::ceil(pp));
}
dacol->Save();
}
// 14. Free the used memory
delete dacol;
delete sv;
delete pmesh;
delete timer;
// Flush output before MPI_finalize
#ifdef MFEM_USE_CALIPER
mgr.flush();
#endif
MPI_Finalize();
return 0;
}
+478
View File
@@ -0,0 +1,478 @@
// Copyright (c) 2010-2021, 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.
//
// MFEM AD Example - Serial Version
//
// Compile with: make seq_example
//
// Sample runs: seq_example -m ../data/beam-quad.mesh -pp 3.5
// seq_example -m ../data/beam-tri.mesh -pp 4.6
// seq_example -m ../data/beam-hex.mesh
// seq_example -m ../data/beam-tet.mesh
// seq_example -m ../data/beam-wedge.mesh
//
// Description: This examples solves a quasi-static nonlinear p-Laplacian
// problem with zero Dirichlet boundary conditions applied on all
// defined boundaries
//
// The example demonstrates the use of nonlinear operators
// combined with automatic differentiation (AD). The integrators
// are defined in example.hpp. Selecting integrator = 0 will use
// the manually implemented integrator. Selecting integrator = 1
// or 2 will utilize one of the AD integrators.
//
// We recommend viewing examples 1 and 19, before viewing this
// example.
#include "example.hpp"
using namespace mfem;
enum IntegratorType
{
HandCodedIntegrator = 0,
ADJacobianIntegrator = 1,
ADHessianIntegrator = 2
};
/// Non-linear solver for the p-Laplacian problem.
class NLSolverPLaplacian
{
public:
/// Constructor Input: imesh - FE mesh, finite element space, power for the
/// p-Laplacian, external load (source, input), regularization parameter
NLSolverPLaplacian(Mesh& imesh, FiniteElementSpace& ifespace,
double powerp=2,
Coefficient* load=nullptr,
double regularizationp=1e-7)
{
// default parameters for the Newton solver
newton_rtol = 1e-4;
newton_atol = 1e-8;
newton_iter = 10;
// linear solver
linear_rtol = 1e-7;
linear_atol = 1e-15;
linear_iter = 500;
print_level = 0;
// set the mesh
mesh=&imesh;
// set the fespace
fespace=&ifespace;
// set the parameters
plap_epsilon=new ConstantCoefficient(regularizationp);
plap_power=new ConstantCoefficient(powerp);
if (load==nullptr)
{
plap_input=new ConstantCoefficient(1.0);
input_ownership=true;
}
else
{
plap_input=load;
input_ownership=false;
}
// set the nonlinear form
nlform=nullptr;
lsolver=nullptr;
prec=nullptr;
nsolver=nullptr;
// set the default integrator
integ=IntegratorType::HandCodedIntegrator; // hand coded
}
~NLSolverPLaplacian()
{
if (nlform!=nullptr) { delete nlform;}
if (nsolver!=nullptr) { delete nsolver;}
if (prec!=nullptr) { delete prec;}
if (lsolver!=nullptr) { delete lsolver;}
if (input_ownership) { delete plap_input;}
delete plap_epsilon;
delete plap_power;
}
/// Set the integrator.
/// 0 - hand coded, 1 - AD based (compute only Hessian by AD),
/// 2 - AD based (compute residual and Hessian by AD)
void SetIntegrator(IntegratorType intr)
{
integ=intr;
}
// set relative tolerance for the Newton solver
void SetNRRTol(double rtol)
{
newton_rtol=rtol;
}
// set absolute tolerance for the Newton solver
void SetNRATol(double atol)
{
newton_atol=atol;
}
// set max iterations for the NR solver
void SetMaxNRIter(int miter)
{
newton_iter=miter;
}
void SetLSRTol(double rtol)
{
linear_rtol=rtol;
}
void SetLSATol(double atol)
{
linear_atol=atol;
}
// set max iterations for the linear solver
void SetMaxLSIter(int miter)
{
linear_iter=miter;
}
// set the print level
void SetPrintLevel(int plev)
{
print_level=plev;
}
/// The state vector is used as initial condition for the NR solver. On
/// return the statev holds the solution to the problem.
void Solve(Vector& statev)
{
if (nlform==nullptr)
{
AllocSolvers();
}
Vector b; // RHS is zero
nsolver->Mult(b, statev);
}
/// Compute the energy
double GetEnergy(Vector& statev)
{
if (nlform==nullptr)
{
// allocate the solvers
AllocSolvers();
}
return nlform->GetEnergy(statev);
}
private:
void AllocSolvers()
{
if (nlform!=nullptr) { delete nlform;}
if (nsolver!=nullptr) {delete nsolver;}
if (prec!=nullptr) {delete prec;}
if (lsolver!=nullptr) { delete lsolver;}
// Define the essential boundary attributes
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
nlform = new NonlinearForm(fespace);
if (integ==IntegratorType::HandCodedIntegrator)
{
// standard hand coded integrator
nlform->AddDomainIntegrator(new pLaplace(*plap_power,*plap_epsilon,
*plap_input));
}
else if (integ==IntegratorType::ADJacobianIntegrator)
{
// The template integrator is based on automatic differentiation. For
// ADJacobianIntegrator the residual (vector function) at an
// integration point is implemented as a functor by MyVFunctor. The
// vector function has a return size of four(4), four state arguments,
// and three(3) parameters. MyVFunctor is a template argument to the
// actual template class performing the differentiation - in this case,
// QVectorFuncAutoDiff. The derivatives are used in the integration
// loop in the integrator pLaplaceAD.
nlform->AddDomainIntegrator(new
pLaplaceAD<mfem::QVectorFuncAutoDiff<MyResidualFunctor,4,4,3>>(*plap_power,
*plap_epsilon,*plap_input));
}
else // IntegratorType::ADHessianIntegrator
{
// The main difference from the previous case is that the user has to
// implement only a functional evaluation at an integration point. The
// implementation is in MyQFunctor, which takes four state arguments
// and three parameters. The residual vector is the first derivative of
// the energy/functional with respect to the state variables, and the
// Hessian is the second derivative. Automatic differentiation is used
// for evaluating both of them.
nlform->AddDomainIntegrator(new
pLaplaceAD<mfem::QFunctionAutoDiff<MyEnergyFunctor,4,3>>(*plap_power,
*plap_epsilon,*plap_input));
}
nlform->SetEssentialBC(ess_bdr);
#ifdef MFEM_USE_SUITESPARSE
prec = new UMFPackSolver();
#else
prec = new GSSmoother();
#endif
// allocate the linear solver
lsolver=new CGSolver();
lsolver->SetRelTol(linear_rtol);
lsolver->SetAbsTol(linear_atol);
lsolver->SetMaxIter(linear_iter);
lsolver->SetPrintLevel(print_level);
lsolver->SetPreconditioner(*prec);
// allocate the NR solver
nsolver = new NewtonSolver();
nsolver->iterative_mode = true;
nsolver->SetSolver(*lsolver);
nsolver->SetOperator(*nlform);
nsolver->SetPrintLevel(print_level);
nsolver->SetRelTol(newton_rtol);
nsolver->SetAbsTol(newton_atol);
nsolver->SetMaxIter(newton_iter);
}
double newton_rtol;
double newton_atol;
int newton_iter;
double linear_rtol;
double linear_atol;
int linear_iter;
int print_level;
// reference to the mesh
Mesh* mesh;
// reference to the fespace
FiniteElementSpace *fespace;
// nonlinear form for the p-laplacian
NonlinearForm *nlform;
CGSolver *lsolver; // linear solver
Solver *prec; // preconditioner for the linear solver
NewtonSolver *nsolver; // NR solver
IntegratorType integ;
// power of the p-laplacian
Coefficient* plap_power;
// regularization parameter
Coefficient* plap_epsilon;
// load(input) parameter
Coefficient* plap_input;
// flag indicating the ownership of plap_input
bool input_ownership;
};
int main(int argc, char *argv[])
{
// 1. Parse command-line options
const char *mesh_file = "../../data/beam-tet.mesh";
int ser_ref_levels = 3;
int order = 1;
bool visualization = true;
double newton_rel_tol = 1e-4;
double newton_abs_tol = 1e-6;
int newton_iter = 10;
int print_level = 0;
double pp = 2.0; // p-Laplacian power
IntegratorType integrator = IntegratorType::ADHessianIntegrator;
int int_integrator = integrator;
// HandCodedIntegrator = 0 - do not use AD (hand coded)
// ADJacobianIntegrator = 1 - use AD for Hessian only
// ADHessianIntegrator = 2 - use AD for Residual and Hessian
StopWatch *timer = new StopWatch();
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&ser_ref_levels,
"-rs",
"--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&order,
"-o",
"--order",
"Order (degree) of the finite elements.");
args.AddOption(&visualization,
"-vis",
"--visualization",
"-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&newton_rel_tol,
"-rel",
"--relative-tolerance",
"Relative tolerance for the Newton solve.");
args.AddOption(&newton_abs_tol,
"-abs",
"--absolute-tolerance",
"Absolute tolerance for the Newton solve.");
args.AddOption(&newton_iter,
"-it",
"--newton-iterations",
"Maximum iterations for the Newton solve.");
args.AddOption(&pp,
"-pp",
"--power-parameter",
"Power parameter (>=2.0) for the p-Laplacian.");
args.AddOption((&print_level), "-prt", "--print-level", "Print level.");
args.AddOption(&int_integrator,
"-int",
"--integrator",
"Integrator 0: standard; 1: AD for Hessian; 2: AD for residual and Hessian");
args.Parse();
if (!args.Good())
{
args.PrintUsage(std::cout);
return 1;
}
args.PrintOptions(std::cout);
integrator = static_cast<IntegratorType>(int_integrator);
// 2. Read the (serial) mesh from the given mesh file.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 3. 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.
for (int lev = 0; lev < ser_ref_levels; lev++)
{
mesh->UniformRefinement();
}
// 4. Define the load parameter for the p-Laplacian
ConstantCoefficient load(1.00);
// 5. Define the finite element spaces for the solution
H1_FECollection fec(order, dim);
FiniteElementSpace fespace(mesh, &fec, 1, Ordering::byVDIM);
int glob_size = fespace.GetTrueVSize();
std::cout << "Number of finite element unknowns: " << glob_size << std::endl;
// 6. Define the solution grid function
GridFunction x(&fespace);
x = 0.0;
// 7. Define the solution true vector
Vector sv(fespace.GetTrueVSize());
sv = 0.0;
// 8. Define ParaView DataCollection
ParaViewDataCollection *dacol = new ParaViewDataCollection("Example", mesh);
dacol->SetLevelsOfDetail(order);
dacol->RegisterField("sol", &x);
// 9. Define the nonlinear p-Laplacian solver
NLSolverPLaplacian* nr;
// 10. Start with linear diffusion - solvable for any initial guess
nr=new NLSolverPLaplacian(*mesh, fespace, 2.0, &load);
nr->SetIntegrator(integrator);
nr->SetMaxNRIter(newton_iter);
nr->SetNRATol(newton_abs_tol);
nr->SetNRRTol(newton_rel_tol);
timer->Clear();
timer->Start();
nr->Solve(sv);
timer->Stop();
std::cout << "[pp=2] The solution time is: " << timer->RealTime()
<< std::endl;
// Compute the energy
double energy = nr->GetEnergy(sv);
std::cout << "[pp=2] The total energy of the system is E=" << energy
<< std::endl;
delete nr;
x.SetFromTrueDofs(sv);
dacol->SetTime(2.0);
dacol->SetCycle(2);
dacol->Save();
// 11. Continue with powers higher than 2
for (int i = 3; i < pp; i++)
{
nr=new NLSolverPLaplacian(*mesh, fespace, (double)i, &load);
nr->SetIntegrator(integrator);
nr->SetMaxNRIter(newton_iter);
nr->SetNRATol(newton_abs_tol);
nr->SetNRRTol(newton_rel_tol);
timer->Clear();
timer->Start();
nr->Solve(sv);
timer->Stop();
std::cout << "[pp=" << i
<< "] The solution time is: " << timer->RealTime() << std::endl;
energy = nr->GetEnergy(sv);
std::cout << "[pp="<< i<<"] The total energy of the system is E=" << energy
<< std::endl;
delete nr;
x.SetFromTrueDofs(sv);
dacol->SetTime(i);
dacol->SetCycle(i);
dacol->Save();
}
// 12. Continue with the final power
if (std::abs(pp - 2.0) > std::numeric_limits<double>::epsilon())
{
nr=new NLSolverPLaplacian(*mesh, fespace, pp, &load);
nr->SetIntegrator(integrator);
nr->SetMaxNRIter(newton_iter);
nr->SetNRATol(newton_abs_tol);
nr->SetNRRTol(newton_rel_tol);
timer->Clear();
timer->Start();
nr->Solve(sv);
timer->Stop();
std::cout << "[pp=" << pp
<< "] The solution time is: " << timer->RealTime() << std::endl;
energy = nr->GetEnergy(sv);
std::cout << "[pp="<<pp<<"] The total energy of the system is E=" << energy
<< std::endl;
delete nr;
x.SetFromTrueDofs(sv);
dacol->SetTime(pp);
if (pp < 2.0)
{
dacol->SetCycle(std::floor(pp));
}
else
{
dacol->SetCycle(std::ceil(pp));
}
dacol->Save();
}
// 13. Free the memory
delete dacol;
delete mesh;
delete timer;
return 0;
}
+158
View File
@@ -0,0 +1,158 @@
// Copyright (c) 2010-2021, 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 "admfem.hpp"
#include "mfem.hpp"
template<typename TDataType, typename TParamVector, typename TStateVector
, int state_size, int param_size>
class DiffusionFunctional
{
public:
TDataType operator() (TParamVector& vparam, TStateVector& uu)
{
MFEM_ASSERT(state_size==4,"ExampleFunctor state_size should be equal to 4!");
MFEM_ASSERT(param_size==2,"ExampleFunctor param_size should be equal to 2!");
auto kappa = vparam[0]; // diffusion coefficient
auto load = vparam[1]; // volumetric influx
TDataType rez = kappa*(uu[0]*uu[0]+uu[1]*uu[1]+uu[2]*uu[2])/2.0 - load*uu[3];
return rez;
}
};
template<typename TDataType, typename TParamVector, typename TStateVector,
int residual_size, int state_size, int param_size>
class DiffusionResidual
{
public:
void operator ()(TParamVector& vparam, TStateVector& uu, TStateVector& rr)
{
MFEM_ASSERT(residual_size==4,
"DiffusionResidual residual_size should be equal to 4!");
MFEM_ASSERT(state_size==4,"ExampleFunctor state_size should be equal to 4!");
MFEM_ASSERT(param_size==2,"ExampleFunctor param_size should be equal to 2!");
auto kappa = vparam[0]; // diffusion coefficient
auto load = vparam[1]; // volumetric influx
rr[0] = kappa * uu[0];
rr[1] = kappa * uu[1];
rr[2] = kappa * uu[2];
rr[3] = -load;
}
};
int main(int argc, char *argv[])
{
#ifdef MFEM_USE_ADFORWARD
std::cout<<"MFEM_USE_ADFORWARD == true"<<std::endl;
#else
std::cout<<"MFEM_USE_ADFORWARD == false"<<std::endl;
#endif
#ifdef MFEM_USE_CALIPER
cali::ConfigManager mgr;
#endif
// Caliper instrumentation
MFEM_PERF_FUNCTION;
#ifdef MFEM_USE_CALIPER
const char* cali_config = "runtime-report";
mgr.add(cali_config);
mgr.start();
#endif
mfem::Vector param(2);
param[0]=3.0; // diffusion coefficient
param[1]=2.0; // volumetric influx
mfem::Vector state(4);
state[0]=1.0; // grad_x
state[1]=2.0; // grad_y
state[2]=3.0; // grad_z
state[3]=4.0; // state value
mfem::QFunctionAutoDiff<DiffusionFunctional,4,2> adf;
mfem::Vector rr0(4);
mfem::DenseMatrix hh0(4,4);
mfem::Vector rr1(4);
mfem::DenseMatrix hh1(4,4);
MFEM_PERF_BEGIN("Grad");
adf.Grad(param,state,rr0);
MFEM_PERF_END("Grad");
MFEM_PERF_BEGIN("Hessian");
adf.Hessian(param, state, hh0);
MFEM_PERF_END("Hessian");
// dump out the results
std::cout<<"FunctionAutoDiff"<<std::endl;
std::cout<< adf.Eval(param,state)<<std::endl;
rr0.Print(std::cout);
hh0.Print(std::cout);
mfem::QVectorFuncAutoDiff<DiffusionResidual,4,4,2> rdf;
MFEM_PERF_BEGIN("Jacobian");
rdf.Jacobian(param, state, hh1);
MFEM_PERF_END("Jacobian");
std::cout<<"ResidualAutoDiff"<<std::endl;
hh1.Print(std::cout);
// using lambda expression
auto func = [](mfem::Vector& vparam,
mfem::ad::ADVectorType& uu,
mfem::ad::ADVectorType& vres)
{
// auto func = [](auto& vparam, auto& uu, auto& vres) { //c++14
auto kappa = vparam[0]; // diffusion coefficient
auto load = vparam[1]; // volumetric influx
vres[0] = kappa * uu[0];
vres[1] = kappa * uu[1];
vres[2] = kappa * uu[2];
vres[3] = -load;
};
mfem::VectorFuncAutoDiff<4,4,2> fdr(func);
MFEM_PERF_BEGIN("JacobianV");
fdr.Jacobian(param,state,
hh1); // computes the gradient of func and stores the result in hh1
MFEM_PERF_END("JacobianV");
std::cout<<"LambdaAutoDiff"<<std::endl;
hh1.Print(std::cout);
double kappa = param[0];
double load = param[1];
// using lambda expression
auto func01 = [&kappa,&load](mfem::Vector& vparam,
mfem::ad::ADVectorType& uu,
mfem::ad::ADVectorType& vres)
{
// auto func = [](auto& vparam, auto& uu, auto& vres) { //c++14
vres[0] = kappa * uu[0];
vres[1] = kappa * uu[1];
vres[2] = kappa * uu[2];
vres[3] = -load;
};
mfem::VectorFuncAutoDiff<4,4,2> fdr01(func01);
MFEM_PERF_BEGIN("Jacobian1");
fdr01.Jacobian(param,state,hh1);
MFEM_PERF_END("Jacobian1");
std::cout<<"LambdaAutoDiff 01"<<std::endl;
hh1.Print(std::cout);
#ifdef MFEM_USE_CALIPER
mgr.flush();
#endif
}
+514
View File
@@ -0,0 +1,514 @@
// Copyright (c) 2010-2021, 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 TADDENSEMATRIX_H
#define TADDENSEMATRIX_H
#include "mfem.hpp"
#include "tadvector.hpp"
namespace mfem
{
/// Templated dense matrix data type.
/** The main goal of the TAutoDiffDenseMatrix class is to serve as a data
container for representing dense matrices in classes, methods, and functions
utilized with automatic differentiation (AD). The functionality/interface is
copied from the standard MFEM dense matrix mfem::DenseMatrix. The basic idea
is to utilize the templated vector class in combination with AD during the
development phase. The AD parts can be replaced with optimized code once the
initial development of the application is complete. The common interface
between TAutoDiffDenseMatrix and DenseMatrix will ease the transition from
AD to hand-optimized code as it does not require a change in the interface
or the code structure. TAutoDiffDenseMatrix is intended to be utilized for
dense serial matrices. The objects can be combined with TAutoDiffVector or
standard Vector.*/
template<typename dtype>
class TAutoDiffDenseMatrix
{
private:
int height; ///< Dimension of the output / number of rows in the matrix.
int width; ///< Dimension of the input / number of columns in the matrix.
dtype *data;
int capacity; // zero or negative capacity means we do not own the data.
public:
/// Get the height (size of output) of the Operator. Synonym with NumRows().
inline int Height() const { return height; }
/** @brief Get the number of rows (size of output) of the Operator. Synonym
with Height(). */
inline int NumRows() const { return height; }
/// Get the width (size of input) of the Operator. Synonym with NumCols().
inline int Width() const { return width; }
/** @brief Get the number of columns (size of input) of the Operator. Synonym
with Width(). */
inline int NumCols() const { return width; }
/** Default constructor for TAutoDiffDenseMatrix.
Sets data = NULL and height = width = 0. */
TAutoDiffDenseMatrix()
{
data = nullptr;
capacity = 0;
height = 0;
width = 0;
}
/// Copy constructor
template<typename idtype>
TAutoDiffDenseMatrix(const TAutoDiffDenseMatrix<idtype> &m)
{
height = m.GetHeight();
width = m.GetWidth();
const int hw = height * width;
if (hw > 0)
{
idtype *mdata = m.Data();
MFEM_ASSERT(mdata, "invalid source matrix");
data = new dtype[hw];
capacity = hw;
for (int i = 0; i < hw; i++)
{
data[i] = mdata[i];
}
}
else
{
data = nullptr;
capacity = 0;
width = 0;
height = 0;
}
}
/// Copy constructor using standard DenseMatrix
TAutoDiffDenseMatrix(const DenseMatrix &m)
{
height = m.Height();
width = m.Width();
const int hw = height * width;
if (hw > 0)
{
double *mdata = m.Data();
MFEM_ASSERT(mdata, "invalid source matrix");
data = new dtype[hw];
capacity = hw;
for (int i = 0; i < hw; i++)
{
data[i] = mdata[i];
}
}
else
{
data = nullptr;
capacity = 0;
width = 0;
height = 0;
}
}
/// Creates square matrix of size s.
explicit TAutoDiffDenseMatrix(int s)
{
MFEM_ASSERT(s >= 0, "invalid DenseMatrix size: " << s);
height = s;
width = s;
capacity = s * s;
if (capacity > 0)
{
data = new dtype[capacity](); // init with zeroes
}
else
{
data = nullptr;
}
}
/// Creates rectangular matrix of size m x n.
TAutoDiffDenseMatrix(int m, int n)
{
MFEM_ASSERT(m >= 0 && n >= 0,
"invalid DenseMatrix size: " << m << " x " << n);
height = m;
width = n;
capacity = m * n;
if (capacity > 0)
{
data = new dtype[capacity](); // init with zeroes
}
else
{
data = nullptr;
}
}
TAutoDiffDenseMatrix(const TAutoDiffDenseMatrix<dtype> &mat, char ch)
{
height = mat.Width();
width = mat.Height();
capacity = height * width;
if (capacity > 0)
{
data = new dtype[capacity];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
(*this)(i, j) = mat(j, i);
}
}
}
else
{
data = nullptr;
}
}
/// Change the size of the DenseMatrix to s x s.
void SetSize(int s) { SetSize(s, s); }
/// Change the size of the DenseMatrix to h x w.
void SetSize(int h, int w)
{
MFEM_ASSERT(h >= 0 && w >= 0,
"invalid DenseMatrix size: " << h << " x " << w);
if (Height() == h && Width() == w)
{
return;
}
height = h;
width = w;
const int hw = h * w;
if (hw > std::abs(capacity))
{
if (capacity > 0)
{
delete[] data;
}
capacity = hw;
data = new dtype[hw](); // init with zeroes
}
}
/// Returns the matrix data array.
inline dtype *Data() const { return data; }
/// Returns the matrix data array.
inline dtype *GetData() const { return data; }
inline bool OwnsData() const { return (capacity > 0); }
/// Returns reference to a_{ij}.
dtype &operator()(int i, int j)
{
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
return data[i + j * height];
}
const dtype &operator()(int i, int j) const
{
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
return data[i + j * height];
}
dtype &Elem(int i, int j) { return (*this)(i, j); }
const dtype &Elem(int i, int j) const { return (*this)(i, j); }
void Mult(const dtype *x, dtype *y) const
{
if (width == 0)
{
for (int row = 0; row < height; row++)
{
y[row] = 0.0;
}
return;
}
dtype *d_col = data;
dtype x_col = x[0];
for (int row = 0; row < height; row++)
{
y[row] = x_col * d_col[row];
}
d_col += height;
for (int col = 1; col < width; col++)
{
x_col = x[col];
for (int row = 0; row < height; row++)
{
y[row] += x_col * d_col[row];
}
d_col += height;
}
}
void Mult(const TAutoDiffVector<dtype> &x, TAutoDiffVector<dtype> &y) const
{
MFEM_ASSERT(height == y.Size() && width == x.Size(),
"incompatible dimensions");
Mult((const dtype *) x, (dtype *) y);
}
dtype operator*(const TAutoDiffDenseMatrix<dtype> &m) const
{
MFEM_ASSERT(Height() == m.Height() && Width() == m.Width(),
"incompatible dimensions");
const int hw = height * width;
dtype a = 0.0;
for (int i = 0; i < hw; i++)
{
a += data[i] * m.data[i];
}
return a;
}
void MultTranspose(const dtype *x, dtype *y) const
{
dtype *d_col = data;
for (int col = 0; col < width; col++)
{
double y_col = 0.0;
for (int row = 0; row < height; row++)
{
y_col += x[row] * d_col[row];
}
y[col] = y_col;
d_col += height;
}
}
void MultTranspose(const TAutoDiffVector<dtype> &x,
TAutoDiffVector<dtype> &y) const
{
MFEM_ASSERT(height == x.Size() && width == y.Size(),
"incompatible dimensions");
MultTranspose((const dtype *) x, (dtype *) y);
}
void Randomize(int seed)
{
// static unsigned int seed = time(0);
const double max = (double) (RAND_MAX) + 1.;
if (seed == 0)
{
seed = (int) time(0);
}
// srand(seed++);
srand((unsigned) seed);
for (int i = 0; i < capacity; i++)
{
data[i] = (dtype)(std::abs(rand() / max));
}
}
void RandomizeDiag(int seed)
{
// static unsigned int seed = time(0);
const double max = (double) (RAND_MAX) + 1.;
if (seed == 0)
{
seed = (int) time(0);
}
// srand(seed++);
srand((unsigned) seed);
for (int i = 0; i < std::min(height, width); i++)
{
Elem(i, i) = (dtype)(std::abs(rand() / max));
}
}
/// Creates n x n diagonal matrix with diagonal elements c
void Diag(dtype c, int n)
{
SetSize(n);
const int N = n * n;
for (int i = 0; i < N; i++)
{
data[i] = (dtype) 0.0;
}
for (int i = 0; i < n; i++)
{
data[i * (n + 1)] = c;
}
}
/// Creates n x n diagonal matrix with diagonal given by diag
template<typename itype>
void Diag(itype *diag, int n)
{
SetSize(n);
int i, N = n * n;
for (i = 0; i < N; i++)
{
data[i] = 0.0;
}
for (i = 0; i < n; i++)
{
data[i * (n + 1)] = (dtype) diag[i];
}
}
/// (*this) = (*this)^t
void Transpose()
{
int i, j;
dtype t;
if (Width() == Height())
{
for (i = 0; i < Height(); i++)
for (j = i + 1; j < Width(); j++)
{
t = (*this)(i, j);
(*this)(i, j) = (*this)(j, i);
(*this)(j, i) = t;
}
}
else
{
TAutoDiffDenseMatrix<dtype> T(*this, 't');
(*this) = T;
}
}
/// (*this) = A^t
template<typename itype>
void Transpose(const TAutoDiffDenseMatrix<itype> &A)
{
SetSize(A.Width(), A.Height());
for (int i = 0; i < Height(); i++)
for (int j = 0; j < Width(); j++)
{
(*this)(i, j) = (dtype) A(j, i);
}
}
/// (*this) = 1/2 ((*this) + (*this)^t)
void Symmetrize()
{
#ifdef MFEM_DEBUG
if (Width() != Height())
{
mfem_error("DenseMatrix::Symmetrize() : not a square matrix!");
}
#endif
for (int i = 0; i < Height(); i++)
for (int j = 0; j < i; j++)
{
dtype a = 0.5 * ((*this)(i, j) + (*this)(j, i));
(*this)(j, i) = (*this)(i, j) = a;
}
}
void Lump()
{
for (int i = 0; i < Height(); i++)
{
dtype L = 0.0;
for (int j = 0; j < Width(); j++)
{
L += (*this)(i, j);
(*this)(i, j) = (dtype) 0.0;
}
(*this)(i, i) = L;
}
}
};
template<typename dtype>
void CalcAdjugate(const TAutoDiffDenseMatrix<dtype> &a,
TAutoDiffDenseMatrix<dtype> &adja)
{
#ifdef MFEM_DEBUG
if (a.Width() > a.Height() || a.Width() < 1 || a.Height() > 3)
{
mfem_error("CalcAdjugate(...)");
}
if (a.Width() != adja.Height() || a.Height() != adja.Width())
{
mfem_error("CalcAdjugate(...)");
}
#endif
if (a.Width() < a.Height())
{
const dtype *d = a.Data();
dtype *ad = adja.Data();
if (a.Width() == 1)
{
// N x 1, N = 2,3
ad[0] = d[0];
ad[1] = d[1];
if (a.Height() == 3)
{
ad[2] = d[2];
}
}
else
{
// 3 x 2
double e, g, f;
e = d[0] * d[0] + d[1] * d[1] + d[2] * d[2];
g = d[3] * d[3] + d[4] * d[4] + d[5] * d[5];
f = d[0] * d[3] + d[1] * d[4] + d[2] * d[5];
ad[0] = d[0] * g - d[3] * f;
ad[1] = d[3] * e - d[0] * f;
ad[2] = d[1] * g - d[4] * f;
ad[3] = d[4] * e - d[1] * f;
ad[4] = d[2] * g - d[5] * f;
ad[5] = d[5] * e - d[2] * f;
}
return;
}
if (a.Width() == 1)
{
adja(0, 0) = (dtype) 1.0;
}
else if (a.Width() == 2)
{
adja(0, 0) = a(1, 1);
adja(0, 1) = -a(0, 1);
adja(1, 0) = -a(1, 0);
adja(1, 1) = a(0, 0);
}
else
{
adja(0, 0) = a(1, 1) * a(2, 2) - a(1, 2) * a(2, 1);
adja(0, 1) = a(0, 2) * a(2, 1) - a(0, 1) * a(2, 2);
adja(0, 2) = a(0, 1) * a(1, 2) - a(0, 2) * a(1, 1);
adja(1, 0) = a(1, 2) * a(2, 0) - a(1, 0) * a(2, 2);
adja(1, 1) = a(0, 0) * a(2, 2) - a(0, 2) * a(2, 0);
adja(1, 2) = a(0, 2) * a(1, 0) - a(0, 0) * a(1, 2);
adja(2, 0) = a(1, 0) * a(2, 1) - a(1, 1) * a(2, 0);
adja(2, 1) = a(0, 1) * a(2, 0) - a(0, 0) * a(2, 1);
adja(2, 2) = a(0, 0) * a(1, 1) - a(0, 1) * a(1, 0);
}
}
} // namespace mfem
#endif
+699
View File
@@ -0,0 +1,699 @@
// Copyright (c) 2010-2021, 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_TADVECTOR
#define MFEM_TADVECTOR
#include "mfem.hpp"
#include <cmath>
#include <iostream>
#include <limits>
#if defined(_MSC_VER) && (_MSC_VER < 1800)
#include <float.h>
#define isfinite _finite
#endif
namespace mfem
{
/// Templated vector data type.
/** The main goal of the TAutoDiffVector class is to serve as a data container
for representing vectors in classes, methods, and functions utilized with
automatic differentiation (AD). The functionality/interface is copied from
the standard MFEM dense vector mfem::Vector. The basic idea is to utilize
the templated vector class in combination with AD during the development
phase. The AD parts can be replaced with optimized code once the initial
development of the application is complete. The common interface between
TAutoDiffVector and Vector will ease the transition from AD to
hand-optimized code as it does not require a change in the interface or the
code structure. TAutoDiffVector is intended to be utilized for dense serial
vectors. */
template<typename dtype>
class TAutoDiffVector
{
protected:
dtype *data;
int size;
int capacity;
public:
/// Default constructor for Vector. Sets size = 0 and data = NULL.
TAutoDiffVector()
{
data = nullptr;
size = 0;
capacity = 0;
}
/// Copy constructor. Allocates a new data array and copies the data.
TAutoDiffVector(const TAutoDiffVector<dtype> &v)
{
const int s = v.Size();
if (s > 0)
{
size = s;
data = new dtype[s];
capacity = s;
for (int i = 0; i < s; i++)
{
data[i] = v[i];
}
}
else
{
size = 0;
capacity = 0;
data = nullptr;
}
}
TAutoDiffVector(const Vector &v)
{
const int s = v.Size();
if (s > 0)
{
size = s;
capacity = s;
data = new dtype[s];
for (int i = 0; i < s; i++)
{
data[i] = v[i];
}
}
else
{
size = 0;
capacity = 0;
data = nullptr;
}
}
/// @brief Creates vector of size s.
/// @warning Entries are not initialized to zero!
explicit TAutoDiffVector(int s)
{
if (s > 0)
{
size = s;
capacity = s;
data = new dtype[size];
}
else
{
size = 0;
capacity = 0;
data = nullptr;
}
}
/// Creates a vector referencing an array of doubles, owned by someone else.
/** The pointer @a _data can be NULL. The data array can be replaced later
with SetData(). */
TAutoDiffVector(dtype *_data, int _size)
{
if (capacity > 0)
{
delete[] data;
capacity = 0;
}
size = _size;
data = _data;
}
/// Reads a vector from multiple files
void Load(std::istream **in, int np, int *dim)
{
int i, j, s;
s = 0;
for (i = 0; i < np; i++)
{
s += dim[i];
}
SetSize(s);
int p = 0;
double tmpd;
for (i = 0; i < np; i++)
{
for (j = 0; j < dim[i]; j++)
{
*in[i] >> tmpd;
data[p++] = dtype(tmpd);
}
}
}
/// Load a vector from an input stream.
void Load(std::istream &in, int Size)
{
SetSize(Size);
double tmpd;
for (int i = 0; i < size; i++)
{
in >> tmpd;
data[i] = dtype(tmpd);
}
}
/// Load a vector from an input stream, reading the size from the stream.
void Load(std::istream &in)
{
int s;
in >> s;
Load(in, s);
}
/// @brief Resize the vector to size @a s.
/** If the new size is less than or equal to Capacity() then the internal
data array remains the same. Otherwise, the old array is deleted, if
owned, and a new array of size @a s is allocated without copying the
previous content of the Vector.
@warning In the second case above (new size greater than current one),
the vector will allocate new data array, even if it did not own the
original data! Also, new entries are not initialized! */
void SetSize(int s)
{
if (s == size)
{
return;
}
if (s <= capacity)
{
size = s;
return;
}
delete[] data;
data = new dtype[s];
size = s;
capacity = s;
}
/// Set the Vector data and size.
/** The Vector does not assume ownership of the new data. The new size is
@warning This method should be called only when OwnsData() is false.
@sa NewDataAndSize(). */
void SetDataAndSize(dtype *d, int s)
{
if (OwnsData())
{
delete[] data;
capacity = 0;
}
data = d;
size = s;
}
/// Set the Vector data and size, deleting the old data, if owned.
/** The Vector does not assume ownership of the new data. The new size is
also used as the new Capacity().
@sa SetDataAndSize(). */
void NewDataAndSize(dtype *d, int s) { SetDataAndSize(d, s); }
/// Reset the Vector to be a reference to a sub-vector of @a base.
inline void MakeRef(TAutoDiffVector<dtype> &base, int offset, int size_)
{
NewDataAndSize(base.GetData() + offset, size_);
}
/** @brief Reset the Vector to be a reference to a sub-vector of @a base
without changing its current size. */
inline void MakeRef(TAutoDiffVector<dtype> &base, int offset)
{
int tsiz = size;
NewDataAndSize(base.GetData() + offset, tsiz);
}
/// Destroy a vector
void Destroy()
{
size = 0;
capacity = 0;
delete[] data;
}
/// Returns the size of the vector.
inline int Size() const { return size; }
/// Return the size of the currently allocated data array.
/** It is always true that Capacity() >= Size(). */
inline int Capacity() const { return capacity; }
/// Return a pointer to the beginning of the Vector data.
/** @warning This method should be used with caution as it gives write access
to the data of const-qualified Vector%s. */
inline dtype *GetData() const
{
return const_cast<dtype *>((const dtype *) data);
}
/// Conversion to `double *`.
/** @note This conversion function makes it possible to use [] for indexing
in addition to the overloaded operator()(int). */
inline operator dtype *() { return data; }
/// Conversion to `const double *`.
/** @note This conversion function makes it possible to use [] for indexing
in addition to the overloaded operator()(int). */
inline operator const dtype *() const { return data; }
/// Read the Vector data (host pointer) ownership flag.
inline bool OwnsData() const { return (capacity > 0); }
/// Changes the ownership of the data; after the call the Vector is empty
inline void StealData(dtype **p)
{
*p = data;
delete[] data;
size = 0;
capacity = 0;
}
/// Changes the ownership of the data; after the call the Vector is empty
inline dtype *StealData()
{
dtype *p;
StealData(&p);
return p;
}
/// Access Vector entries. Index i = 0 .. size-1.
dtype &Elem(int i) { return operator()(i); }
/// Read only access to Vector entries. Index i = 0 .. size-1.
const dtype &Elem(int i) const { return operator()(i); }
/// Access Vector entries using () for 0-based indexing.
/** @note If MFEM_DEBUG is enabled, bounds checking is performed. */
inline dtype &operator()(int i)
{
MFEM_ASSERT(data && i >= 0 && i < size,
"index [" << i << "] is out of range [0," << size << ")");
return data[i];
}
/// Read only access to Vector entries using () for 0-based indexing.
/** @note If MFEM_DEBUG is enabled, bounds checking is performed. */
inline const dtype &operator()(int i) const
{
MFEM_ASSERT(data && i >= 0 && i < size,
"index [" << i << "] is out of range [0," << size << ")");
return data[i];
}
/// Dot product with a `dtype *` array.
dtype operator*(const dtype *v) const
{
dtype dot = 0.0;
for (int i = 0; i < size; i++)
{
dot += data[i] * v[i];
}
return dot;
}
/// Return the inner-product.
dtype operator*(const TAutoDiffVector<dtype> &v) const
{
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
dtype dot = 0.0;
for (int i = 0; i < size; i++)
{
dot += data[i] * v[i];
}
return dot;
}
dtype operator*(const Vector &v) const
{
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
dtype dot = 0.0;
for (int i = 0; i < size; i++)
{
dot += data[i] * v[i];
}
return dot;
}
/// Copy Size() entries from @a v.
TAutoDiffVector<dtype> &operator=(const dtype *v)
{
for (int i = 0; i < size; i++)
{
data[i] = v[i];
}
return *this;
}
/// Copy assignment.
/** @note Defining this method overwrites the implicitly defined copy
assignment operator. */
TAutoDiffVector<dtype> &operator=(const TAutoDiffVector<dtype> &v)
{
SetSize(v.Size());
for (int i = 0; i < size; i++)
{
data[i] = v[i];
}
return *this;
}
TAutoDiffVector<dtype> &operator=(const Vector &v)
{
SetSize(v.Size());
for (int i = 0; i < size; i++)
{
data[i] = v[i];
}
return *this;
}
/// Redefine '=' for vector = constant.
template<typename ivtype>
TAutoDiffVector &operator=(ivtype value)
{
for (int i = 0; i < size; i++)
{
data[i] = (dtype) value;
}
return *this;
}
template<typename ivtype>
TAutoDiffVector &operator*=(ivtype c)
{
for (int i = 0; i < size; i++)
{
data[i] = data[i] * c;
}
return *this;
}
template<typename ivtype>
TAutoDiffVector &operator/=(ivtype c)
{
for (int i = 0; i < size; i++)
{
data[i] = data[i] / c;
}
return *this;
}
TAutoDiffVector &operator-=(const TAutoDiffVector<dtype> &v)
{
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
for (int i = 0; i < size; i++)
{
data[i] = data[i] - v[i];
}
return *this;
}
template<typename ivtype>
TAutoDiffVector &operator-=(ivtype v)
{
for (int i = 0; i < size; i++)
{
data[i] = data[i] - v;
}
return *this;
}
TAutoDiffVector &operator+=(const TAutoDiffVector<dtype> &v)
{
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
for (int i = 0; i < size; i++)
{
data[i] = data[i] + v[i];
}
return *this;
}
template<typename ivtype>
TAutoDiffVector &operator+=(ivtype v)
{
for (int i = 0; i < size; i++)
{
data[i] = data[i] + v;
}
return *this;
}
/// (*this) += a * Va
template<typename ivtype, typename vtype>
TAutoDiffVector &Add(const ivtype a, const vtype &v)
{
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
for (int i = 0; i < size; i++)
{
data[i] = data[i] + a * v[i];
}
return *this;
}
/// (*this) = a * x
template<typename ivtype, typename vtype>
TAutoDiffVector &Set(const ivtype a, const vtype &v)
{
MFEM_ASSERT(size == v.Size(), "incompatible Vectors!");
for (int i = 0; i < size; i++)
{
data[i] = a * v[i];
}
return *this;
}
template<typename vtype>
void SetVector(const vtype &v, int offset)
{
MFEM_ASSERT(v.Size() + offset <= size, "invalid sub-vector");
for (int i = 0; i < size; i++)
{
data[i + offset] = v[i];
}
}
/// (*this) = -(*this)
void Neg()
{
for (int i = 0; i < size; i++)
{
data[i] = -data[i];
}
}
/// Swap the contents of two Vectors
inline void Swap(TAutoDiffVector<dtype> &other)
{
Swap(data, other.data);
Swap(size, other.size);
Swap(capacity, other.capacity);
}
/// Set v = v1 + v2.
template<typename vtype1, typename vtype2>
friend void add(const vtype1 &v1, const vtype2 &v2, TAutoDiffVector<dtype> &v)
{
MFEM_ASSERT(v1.Size() == v.Size(), "incompatible Vectors!");
MFEM_ASSERT(v2.Size() == v.Size(), "incompatible Vectors!");
for (int i = 0; i < v.Size(); i++)
{
v[i] = v1[i] + v2[i];
}
}
/// Set v = v1 + alpha * v2.
template<typename vtype1, typename vtype2>
friend void add(const vtype1 &v1,
dtype alpha,
const vtype2 &v2,
TAutoDiffVector<dtype> &v)
{
MFEM_ASSERT(v1.Size() == v.Size(), "incompatible Vectors!");
MFEM_ASSERT(v2.Size() == v.Size(), "incompatible Vectors!");
for (int i = 0; i < v.Size(); i++)
{
v[i] = v1[i] + alpha * v2[i];
}
}
template<typename vtype1, typename vtype2>
friend void add(const dtype a,
const vtype1 &x,
const dtype b,
const vtype2 &y,
TAutoDiffVector<dtype> &z)
{
MFEM_ASSERT(x.Size() == y.Size() && x.Size() == z.Size(),
"incompatible Vectors!");
for (int i = 0; i < z.Size(); i++)
{
z[i] = a * x[i] + b * y[i];
}
}
template<typename vtype1, typename vtype2>
friend void add(const dtype a,
const vtype1 &x,
const vtype2 &y,
TAutoDiffVector<dtype> &z)
{
MFEM_ASSERT(x.Size() == y.Size() && x.Size() == z.Size(),
"incompatible Vectors!");
for (int i = 0; i < z.Size(); i++)
{
z[i] = a * x[i] + y[i];
}
}
template<typename vtype1, typename vtype2>
friend void subtract(const vtype1 &x, const vtype2 &y,
TAutoDiffVector<dtype> &z)
{
MFEM_ASSERT(x.Size() == y.Size() && x.Size() == z.Size(),
"incompatible Vectors!");
for (int i = 0; i < z.Size(); i++)
{
z[i] = x[i] - y[i];
}
}
template<typename ivtype, typename vtype1, typename vtype2>
friend void subtract(const ivtype a,
const vtype1 &x,
const vtype2 &y,
TAutoDiffVector<dtype> &z)
{
MFEM_ASSERT(x.Size() == y.Size() && x.Size() == z.Size(),
"incompatible Vectors!");
for (int i = 0; i < z.Size(); i++)
{
z[i] = a * (x[i] - y[i]);
}
}
/// Destroys vector.
~TAutoDiffVector() { delete[] data; }
/// Prints vector to stream out.
void Print(std::ostream &out = mfem::out, int width = 8) const
{
if (!size)
{
return;
}
for (int i = 0; 1;)
{
out << data[i];
i++;
if (i == size)
{
break;
}
if (i % width == 0)
{
out << '\n';
}
else
{
out << ' ';
}
}
out << '\n';
}
/// Set random values in the vector.
void Randomize(int seed = 0)
{
// static unsigned int seed = time(0);
const double max = (double) (RAND_MAX) + 1.;
if (seed == 0)
{
seed = (int) time(0);
}
// srand(seed++);
srand((unsigned) seed);
for (int i = 0; i < size; i++)
{
data[i] = std::abs(rand() / max);
}
}
/// Returns the l2 norm of the vector.
dtype Norml2() const
{
// Scale entries of Vector on the fly, using algorithms from std::hypot()
// and LAPACK's drm2. This scaling ensures that the argument of each call
// to std::pow is <= 1 to avoid overflow.
if (0 == size)
{
return 0.0;
} // end if 0 == size
if (1 == size)
{
return abs(data[0]);
} // end if 1 == size
dtype scale = 0.0;
dtype sum = 0.0;
for (int i = 0; i < size; i++)
{
if (data[i] != 0.0)
{
const dtype absdata = abs(data[i]);
if (scale <= absdata)
{
const dtype sqr_arg = scale / absdata;
sum = 1.0 + sum * (sqr_arg * sqr_arg);
scale = absdata;
continue;
} // end if scale <= absdata
const dtype sqr_arg = absdata / scale;
sum += (sqr_arg * sqr_arg); // else scale > absdata
} // end if data[i] != 0
}
return scale * sqrt(sum);
}
/// Returns the l_infinity norm of the vector.
dtype Normlinf() const
{
dtype max = 0.0;
for (int i = 0; i < size; i++)
{
max = max(abs(data[i]), max);
}
return max;
}
/// Returns the l_1 norm of the vector.
dtype Norml1() const
{
dtype sum = 0.0;
for (int i = 0; i < size; i++)
{
sum += abs(data[i]);
}
return sum;
}
};
} // namespace mfem
#endif
+119 -16
View File
@@ -69,6 +69,10 @@
// Adaptive limiting through FD (requires GSLIB):
// * mesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5 -fd -ae 1
//
// Adaptive surface fitting:
// mesh-optimizer -m square01.mesh -o 3 -rs 1 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 5e4 -rtol 1e-5 -nor
// mesh-optimizer -m square01-tri.mesh -o 3 -rs 0 -mid 58 -tid 1 -ni 200 -vl 1 -sfc 1e4 -rtol 1e-5 -nor
//
// Blade shape:
// mesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 3 -art 1 -bnd -qt 1 -qo 8
// Blade shape with FD-based solver:
@@ -96,7 +100,6 @@
// mesh-optimizer -m jagged.mesh -o 2 -mid 22 -tid 1 -ni 50 -li 50 -qo 4 -fd -vl 1
// 3D untangling (the mesh is in the mfem/data GitHub repository):
// * mesh-optimizer -m ../../../mfem_data/cube-holes-inv.mesh -o 3 -mid 313 -tid 1 -rtol 1e-5 -li 50 -qo 4 -fd -vl 1
//
#include "mfem.hpp"
#include "../common/mfem-common.hpp"
@@ -117,7 +120,8 @@ int main(int argc, char *argv[])
int metric_id = 1;
int target_id = 1;
double lim_const = 0.0;
double adapt_lim_const = 0.0;
double adapt_lim_const = 0.0;
double surface_fit_const = 0.0;
int quad_type = 1;
int quad_order = 8;
int solver_type = 0;
@@ -195,6 +199,8 @@ int main(int argc, char *argv[])
args.AddOption(&lim_const, "-lc", "--limit-const", "Limiting constant.");
args.AddOption(&adapt_lim_const, "-alc", "--adapt-limit-const",
"Adaptive limiting coefficient constant.");
args.AddOption(&surface_fit_const, "-sfc", "--surface-fit-const",
"Surface preservation constant.");
args.AddOption(&quad_type, "-qt", "--quad-type",
"Quadrature rule type:\n\t"
"1: Gauss-Lobatto\n\t"
@@ -283,10 +289,6 @@ int main(int argc, char *argv[])
Mesh *mesh = new Mesh(mesh_file, 1, 1, false);
for (int lev = 0; lev < rs_levels; lev++) { mesh->UniformRefinement(); }
const int dim = mesh->Dimension();
cout << "Mesh curvature: ";
if (mesh->GetNodes()) { cout << mesh->GetNodes()->OwnFEC()->Name(); }
else { cout << "(NONE)"; }
cout << endl;
if (hradaptivity) { mesh->EnsureNCMesh(); }
@@ -722,8 +724,6 @@ int main(int argc, char *argv[])
<< irules->Get(Geometry::PRISM, quad_order).GetNPoints() << endl;
}
if (normalization) { he_nlf_integ->EnableNormalization(x0); }
// Limit the node movement.
// The limiting distances can be given by a general function of space.
FiniteElementSpace dist_fespace(mesh, fec); // scalar space
@@ -765,6 +765,77 @@ int main(int argc, char *argv[])
}
}
// Surface fitting.
L2_FECollection mat_coll(0, dim);
H1_FECollection sigma_fec(mesh_poly_deg, dim);
FiniteElementSpace sigma_fes(mesh, &sigma_fec);
FiniteElementSpace mat_fes(mesh, &mat_coll);
GridFunction mat(&mat_fes);
GridFunction marker_gf(&sigma_fes);
GridFunction ls_0(&sigma_fes);
Array<bool> marker(ls_0.Size());
ConstantCoefficient coef_ls(surface_fit_const);
AdaptivityEvaluator *adapt_surface = NULL;
if (surface_fit_const > 0.0)
{
MFEM_VERIFY(hradaptivity == false,
"Surface fitting with HR is not implemented yet.");
MFEM_VERIFY(pa == false,
"Surface fitting with PA is not implemented yet.");
FunctionCoefficient ls_coeff(surface_level_set);
ls_0.ProjectCoefficient(ls_coeff);
for (int i = 0; i < mesh->GetNE(); i++)
{
mat(i) = material_id(i, ls_0);
mesh->SetAttribute(i, mat(i) + 1);
}
GridFunctionCoefficient coeff_mat(&mat);
marker_gf.ProjectDiscCoefficient(coeff_mat, GridFunction::ARITHMETIC);
for (int j = 0; j < marker.Size(); j++)
{
if (marker_gf(j) > 0.1 && marker_gf(j) < 0.9)
{
marker[j] = true;
marker_gf(j) = 1.0;
}
else
{
marker[j] = false;
marker_gf(j) = 0.0;
}
}
if (adapt_eval == 0) { adapt_surface = new AdvectorCG; }
else if (adapt_eval == 1)
{
#ifdef MFEM_USE_GSLIB
adapt_surface = new InterpolatorFP;
#else
MFEM_ABORT("MFEM is not built with GSLIB support!");
#endif
}
else { MFEM_ABORT("Bad interpolation option."); }
he_nlf_integ->EnableSurfaceFitting(ls_0, marker, coef_ls, *adapt_surface);
if (visualization)
{
socketstream vis1, vis2, vis3;
common::VisualizeField(vis1, "localhost", 19916, ls_0, "Level Set 0",
300, 600, 300, 300);
common::VisualizeField(vis2, "localhost", 19916, mat, "Materials",
600, 600, 300, 300);
common::VisualizeField(vis3, "localhost", 19916, marker_gf, "Dofs to Move",
900, 600, 300, 300);
}
}
// Has to be after the enabling of the limiting / alignment, as it computes
// normalization factors for these terms as well.
if (normalization) { he_nlf_integ->EnableNormalization(x0); }
// 12. Setup the final NonlinearForm (which defines the integral of interest,
// its first and second derivatives). Here we can use a combination of
// metrics, i.e., optimize the sum of two integrals, where both are
@@ -855,6 +926,18 @@ int main(int argc, char *argv[])
// For HR tests, the energy is normalized by the number of elements.
const double init_energy = a.GetGridFunctionEnergy(x) /
(hradaptivity ? mesh->GetNE() : 1);
double init_metric_energy = init_energy;
if (lim_const > 0.0 || adapt_lim_const > 0.0 || surface_fit_const > 0.0)
{
lim_coeff.constant = 0.0;
coef_zeta.constant = 0.0;
coef_ls.constant = 0.0;
init_metric_energy = a.GetGridFunctionEnergy(x) /
(hradaptivity ? mesh->GetNE() : 1);
lim_coeff.constant = lim_const;
coef_zeta.constant = adapt_lim_const;
coef_ls.constant = surface_fit_const;
}
// Visualize the starting mesh and metric values.
// Note that for combinations of metrics, this only shows the first metric.
@@ -1012,23 +1095,26 @@ int main(int argc, char *argv[])
const double fin_energy = a.GetGridFunctionEnergy(x) /
(hradaptivity ? mesh->GetNE() : 1);
double metric_part = fin_energy;
double fin_metric_energy = fin_energy;
if (lim_const > 0.0 || adapt_lim_const > 0.0)
{
lim_coeff.constant = 0.0;
coef_zeta.constant = 0.0;
metric_part = a.GetGridFunctionEnergy(x) /
(hradaptivity ? mesh->GetNE() : 1);
coef_ls.constant = 0.0;
fin_metric_energy = a.GetGridFunctionEnergy(x) /
(hradaptivity ? mesh->GetNE() : 1);
lim_coeff.constant = lim_const;
coef_zeta.constant = adapt_lim_const;
coef_ls.constant = surface_fit_const;
}
std::cout << std::scientific << std::setprecision(4);
cout << "Initial strain energy: " << init_energy
<< " = metrics: " << init_energy
<< " + limiting term: " << 0.0 << endl;
<< " = metrics: " << init_metric_energy
<< " + extra terms: " << init_energy - init_metric_energy << endl;
cout << " Final strain energy: " << fin_energy
<< " = metrics: " << metric_part
<< " + limiting term: " << fin_energy - metric_part << endl;
cout << "The strain energy decreased by: " << setprecision(12)
<< " = metrics: " << fin_metric_energy
<< " + extra terms: " << fin_energy - fin_metric_energy << endl;
cout << "The strain energy decreased by: "
<< (init_energy - fin_energy) * 100.0 / init_energy << " %." << endl;
// 16. Visualize the final mesh and metric values.
@@ -1045,6 +1131,22 @@ int main(int argc, char *argv[])
600, 600, 300, 300);
}
if (surface_fit_const > 0.0)
{
if (visualization)
{
socketstream vis2, vis3;
common::VisualizeField(vis2, "localhost", 19916, mat, "Materials",
600, 900, 300, 300);
common::VisualizeField(vis3, "localhost", 19916, marker_gf, "Surface dof",
900, 900, 300, 300);
}
double err_avg, err_max;
he_nlf_integ->GetSurfaceFittingErrors(err_avg, err_max);
std::cout << "Avg fitting error: " << err_avg << std::endl
<< "Max fitting error: " << err_max << std::endl;
}
// 17. Visualize the mesh displacement.
if (visualization)
{
@@ -1066,6 +1168,7 @@ int main(int argc, char *argv[])
delete metric2;
delete coeff1;
delete adapt_evaluator;
delete adapt_surface;
delete target_c;
delete hr_adapt_coeff;
delete adapt_coeff;

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