Compare commits

...
Author SHA1 Message Date
Socratis Petrides 541d99667e pmg in lh driver 2026-03-10 10:50:10 -07:00
psocratis 1115c01253 minor 2026-03-08 21:43:23 -07:00
Socratis Petrides 6080aca98b fixing pmg non-SPD issue with relaxation parameter. Also added user-defined max-mg-levels 2026-03-03 18:32:21 -08:00
Socratis Petrides e3e8646f28 Merge branch 'hughcars/simplex-quadrature-dev' into dpg-plasma 2026-02-25 11:28:18 -08:00
Hugh Carson d5dec97d23 Fix memory leak 2026-02-25 11:46:21 -05:00
Hugh Carson 2d401bcb74 Add positive-weight simplex quadrature rules for orders 0-20
Triangle rules from Witherden & Vincent (2015), tet rules d=0-13
from Witherden & Vincent, tet rules d=14-20 from Chuluunbaatar et al.
(2022). All rules have strictly positive weights and interior points,
replacing the legacy rules which use negative weights at several
orders and fall back to Grundmann-Moller (negative weights, high
point counts) for tets at d>=9.
2026-02-24 20:21:49 -05:00
Tzanio Kolev 3e277808a9 Merge pull request #5210 from mfem/hughcars/array-move-assignment-bugfix
Fix Array non-owning move assignment
2026-02-24 08:41:48 -08:00
Tzanio Kolev 864fb1ce9e Merge pull request #5217 from mfem/quadrature-function-fix
Fix a bug in `QuadratureFunction::GetValues` that returns a `DenseMatrix` view
2026-02-24 08:40:53 -08:00
Veselin Dobrev 38030d4395 Merge pull request #5222 from mfem/bugfix/chapman39/rm-return-from-omp
Remove return from openmp section
2026-02-24 08:18:53 -08:00
Alex Tyler Chapman 2c96dc6a1f Merge branch 'master' into bugfix/chapman39/rm-return-from-omp 2026-02-23 11:53:58 -08:00
Tzanio Kolev 1f84ba036e Merge pull request #5179 from izaid/pfes
Fixed NULL pointer segfault in GetSurfaceFittingErrors
2026-02-21 12:52:33 -08:00
Tzanio Kolev 76d0312309 Merge branch 'master' into hughcars/array-move-assignment-bugfix 2026-02-21 12:52:11 -08:00
Tzanio Kolev c7ed339260 Merge pull request #5228 from mfem/mesh-3d-part-fix
Improve 3d mesh partitions
2026-02-21 12:43:49 -08:00
Tzanio Kolev f1b3a33fb2 Merge pull request #5234 from mfem/few-small-fixes
A few small fixes
2026-02-21 12:43:02 -08:00
Socratis Petrides d51bb332e8 fixing integ order 2026-02-20 22:17:20 -08:00
psocratis fde141210f fix boundary attr bug 2026-02-18 15:37:23 -08:00
Veselin Dobrev e3dedbbd5b Increase a relative tolerance due to failures on some machines 2026-02-18 10:27:44 -08:00
Socratis Petrides a56303a4a8 Merge branch 'trace-p-ref' into dpg-plasma 2026-02-17 09:27:59 -08:00
John Camier b386b2d6b6 Merge branch 'master' into bugfix/chapman39/rm-return-from-omp 2026-02-17 08:28:34 -08:00
Tzanio Kolev 6e98055eb7 Merge branch 'master' into quadrature-function-fix 2026-02-17 08:28:12 -08:00
Tzanio Kolev 5abd44f212 Merge pull request #5227 from mfem/add-constexpr
Add if constexpr to Kernel Specializations
2026-02-17 08:25:11 -08:00
Socratis Petrides 35ba876ecf CI fix 2026-02-14 15:15:31 -08:00
Veselin Dobrev 75012728db Formatting: re-wrap comment to 80 chars/line. 2026-02-14 13:10:30 -08:00
Socratis Petrides 2886c4b211 pass ess_bdr_marker in pmg 2026-02-13 17:11:10 -08:00
Socratis Petrides 9455ed83b2 add ess_tdof treatement in pmg 2026-02-13 17:10:44 -08:00
Veselin Dobrev 8acd5cd3a2 Fix a size bug in thread-safe mode in H1_TriangleElement::CalcHessian.
Fix use-after-delete bug in nurbs_ex10p.cpp.

Use relative tolerance in the "Collocated Derivative Kernels" unit test
to resolve failures in some setups with the original absolute tolerance.
2026-02-13 10:29:22 -08:00
chapman39 1f5bc1c3d8 wording 2026-02-13 10:03:00 -08:00
chapman39 3588d47ec1 Merge remote-tracking branch 'origin/master' into bugfix/chapman39/rm-return-from-omp 2026-02-13 09:59:31 -08:00
Andrew Ho e7f5996bdf fix warnings with some compilers 2026-02-13 09:28:16 -08:00
Tzanio Kolev 560ad1b5a3 Merge pull request #5157 from mfem/plbound-extremum
Estimate function minimum/maximum using recursion + piecewise linear bounds
2026-02-13 07:08:57 -08:00
psocratis 9757247504 setting up latest tokamak problem 2026-02-11 18:28:44 -08:00
Alex Tyler Chapman f0fe5b0ec0 Merge branch 'master' into bugfix/chapman39/rm-return-from-omp 2026-02-11 15:14:48 -08:00
chapman39 a92983051a style 2026-02-11 15:14:23 -08:00
psocratis c0b7058006 Merge branch 'complex-mumps-solver' into dpg-plasma 2026-02-11 14:11:45 -08:00
psocratis 9520e6c8ac dpg makefile 2026-02-11 14:10:45 -08:00
psocratis d16d25718f fix make issue when both real and complex mumps are enabled 2026-02-11 14:09:55 -08:00
Will Pazner 37843b050c Add one more 'if constexpr' 2026-02-11 11:33:51 -08:00
Socratis Petrides 74f611ec90 ci fix 2026-02-11 11:21:54 -08:00
camierjs 436714f5ef Revert unused included header 2026-02-11 11:18:02 -08:00
camierjs 1ceef4f786 Add few missing Kernel() constexpr 2026-02-11 11:11:35 -08:00
Socratis Petrides 635286cd1a ci fix 2026-02-11 10:16:10 -08:00
Socratis Petrides 071a99fb68 doc fix 2026-02-11 10:07:48 -08:00
Socratis Petrides c1173361b6 Merge branch 'complex-mumps-solver' into dpg-plasma 2026-02-11 10:05:30 -08:00
Socratis Petrides ca7230cc56 merge and resolve conflicts 2026-02-11 10:04:42 -08:00
Socratis Petrides 2b09b1fbb0 minor fixes 2026-02-11 10:02:50 -08:00
Tzanio Kolev b24663d168 Merge branch 'master' into complex-mumps-solver 2026-02-11 08:21:36 -08:00
Socratis Petrides b9c09659f0 merge with p-ref and resolve conflicts 2026-02-10 22:33:13 -08:00
Socratis Petrides 41fbb9bd9a merge with complex-mumps and resolve conflicts 2026-02-10 19:00:07 -08:00
Socratis PetridesandCopilot eacc170b2c Apply suggestion from @Copilot
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-10 18:33:07 -08:00
Vladimir Z Tomov 6bb6745c0e improve 3d partitions 2026-02-10 16:23:38 -08:00
Mittal, Ketan 60f47c287d CHANGELOG 2026-02-10 14:28:17 -08:00
Mittal, Ketan 164ee942c8 Merge branch 'plbound-extremum' of https://github.com/mfem/mfem into plbound-extremum 2026-02-10 10:59:44 -08:00
Mittal, Ketan 16c4fbdd29 reviewer comments 2026-02-10 10:59:33 -08:00
Socratis Petrides 505661eb5f minor 2026-02-09 19:25:36 -08:00
Ketan Mittal e28093274b Merge branch 'master' into plbound-extremum 2026-02-09 19:06:18 -08:00
psocratis e0165de06e fixing comments 2026-02-09 17:45:24 -08:00
Andrew Ho 87dd19e6c0 more constexprs 2026-02-09 17:35:17 -08:00
psocratis a3f2464a1e removing ParMETIS dependency from MUMPS 2026-02-09 17:19:45 -08:00
Andrew Ho 678f53c306 use constexpr to prevent unintended kernel Pinstantiations 2026-02-09 17:17:26 -08:00
psocratis bd54dd97c0 cmake minor 2026-02-09 12:05:10 -08:00
psocratis 689d3bd6ab fix make for linux 2026-02-09 10:11:29 -08:00
Socratis Petrides 69c1d0d15b changelog 2026-02-07 12:52:47 -08:00
Socratis Petrides 5f7d138aef style 2026-02-06 18:18:19 -08:00
Socratis Petrides 642b1f0c82 add a unit test 2026-02-06 18:17:00 -08:00
Socratis Petrides f78ce76bff fix the case where either real or imag part is null 2026-02-06 18:16:41 -08:00
Socratis Petrides 9ec17338a2 more refactoring 2026-02-06 17:04:03 -08:00
Socratis Petrides 12335855cb some refactoring 2026-02-06 15:18:39 -08:00
Socratis Petrides 69dca6c15d changelog 2026-02-06 12:01:05 -08:00
Socratis Petrides 853f421d6b fix cmake for complex mumps 2026-02-06 11:57:29 -08:00
Socratis Petrides ed18b85859 cmake build 2026-02-05 22:45:54 -08:00
Socratis Petrides 9a9c5163ee add COMPLEX_MUMPS to make build 2026-02-05 22:15:53 -08:00
Socratis Petrides 186ab106e6 complex mumps in ex25p 2026-02-05 22:09:13 -08:00
chapman39 32fb4bf244 remove return from openmp section 2026-02-05 14:56:40 -08:00
Socratis Petrides 2363ac5f40 refactor 2026-02-05 11:12:20 -08:00
Socratis Petrides 20da483cd3 merge with master 2026-02-05 11:08:47 -08:00
Socratis Petrides deae7e4997 minor 2026-02-05 10:33:10 -08:00
psocratis 6142015168 refactoring 2026-02-04 22:46:27 -08:00
psocratis 305a7f1e02 added pmg and cpmg options to dpg miniapps 2026-02-04 18:40:51 -08:00
psocratis 0bff07026c additional pmg and complex pmg options added including coarse solve option 2026-02-04 18:40:17 -08:00
psocratis 32c3e5dfd8 add complexoperator-->complexhypre 2026-02-04 18:39:05 -08:00
psocratis ee78679277 add blockoperator-->monolythic 2026-02-04 18:38:08 -08:00
psocratis 89a0f18b30 small leak 2026-02-04 11:14:18 -08:00
Socratis Petrides 4cbc345ba6 fix makefile and cmakelists.txt 2026-02-03 21:58:34 -08:00
Socratis Petrides abe3843712 typo 2026-02-03 21:47:04 -08:00
Socratis Petrides 3c2d1e4814 shadow var fix 2026-02-03 21:11:13 -08:00
Socratis Petrides a8c4ed3c79 expand p-MG to the complex case 2026-02-03 18:51:04 -08:00
Socratis Petrides 0aa73fa285 simplify precond construction in pmax 2026-02-03 15:12:01 -08:00
Socratis Petrides f1652b5ba0 add convenient functions for solver construction 2026-02-03 12:35:58 -08:00
Socratis Petrides fed0baf6b6 fix leak in complex case 2026-02-03 12:34:23 -08:00
Socratis Petrides 49a8ee5b54 fix possible leak in case of static_cond and add method to retrun trace_fes 2026-02-03 12:32:40 -08:00
Socratis Petrides c9569a0629 shadow var fix 2026-02-02 23:50:36 -08:00
Socratis Petrides a4e83b5836 p-mg cleanup 2026-02-02 22:30:02 -08:00
Socratis Petrides 8b285047bd some helper function for constructor order of fecols + Clone impl 2026-02-02 22:29:34 -08:00
Veselin Dobrev 2d021685de Fix a bug in QuadratureFunction::GetValues when getting a reference to
the data for one mesh element as a DenseMatrix.

Added new methods:
- Array<T>::MakeRef(Memory<T> &base, int offset, int size_)
- DenseMatrix::MakeRef(Memory<real_t> &base, int offset, int h, int w)
2026-02-02 08:42:58 -08:00
psocratis f999be0372 valgrind fixes for p-mg 2026-02-01 18:12:32 -08:00
psocratis e639cfd75b style 2026-02-01 15:49:29 -08:00
psocratis 2753692303 Merge branch 'master' into trace-p-ref 2026-02-01 15:42:07 -08:00
psocratis 2722e979fc fix mg-transpose issue 2026-02-01 15:41:26 -08:00
psocratis 144c0ce106 CI 2026-02-01 14:49:58 -08:00
psocratis b64172e12e remove no longer needed code 2026-02-01 13:27:06 -08:00
psocratis c0072fcb84 fix windows CI issue 2026-02-01 13:02:18 -08:00
psocratis 37cd34a0e1 fix shadow var 2026-01-30 22:50:44 -08:00
psocratis 005be84af4 valgrind fixes 2026-01-30 22:44:48 -08:00
Socratis Petrides 0ef682c156 fixing dim for AMSvsADS in smoother 2026-01-30 17:03:26 -08:00
Socratis Petrides 3faa838774 expanding unit tests to serial/parallel true transfer 2026-01-30 17:02:53 -08:00
Socratis Petrides f10c2793c9 adding NC case in serial 2026-01-30 17:02:18 -08:00
Hugh Carson d6ea262498 Address PR feedback
- Hoist src deletion
- Remove unneeded explicit cast given src is named
2026-01-30 14:01:59 -05:00
Socratis Petrides 9f4b83362a p-ref MG for dpg diffusion 2026-01-28 00:02:13 -08:00
Socratis Petrides 8ef423ca65 traces p-ref example for p-diffusion 2026-01-28 00:01:00 -08:00
Hugh Carson a0981cb363 Refactor test for showing equivalency of copy and move assignment 2026-01-27 14:15:08 -05:00
Hugh Carson 894de992da Move assignment of non-owned arrays must fallback to copy assignment 2026-01-27 14:01:23 -05:00
Socratis Petrides aff5173656 fix CI 2026-01-26 22:45:47 -08:00
Socratis Petrides 2164f07f01 starte p-mg for dpg-diffusion 2026-01-26 22:08:38 -08:00
Socratis Petrides fc0037da31 minor 2026-01-25 22:06:19 -08:00
Socratis Petrides 795a1a29a3 minor print fix 2026-01-25 00:11:44 -08:00
Socratis Petrides 476d9c5111 fix serial case 2026-01-25 00:06:18 -08:00
Socratis Petrides 64625f7333 tests for parallel trace/assemble prefoperator 2026-01-24 18:12:49 -08:00
Socratis Petrides 4ae8c1bc2b started on p-multigrid in serial 2026-01-24 18:12:11 -08:00
Socratis Petrides 4ae97f1834 hpp file changes for pref assembly 2026-01-24 18:11:37 -08:00
Socratis Petrides 816e8e4bea add option to get the preftransfer operator in parallel as a hypreparmatrix 2026-01-24 18:11:12 -08:00
Socratis Petrides c14d630f14 tet ND case fixed for assembled Transfer P 2026-01-24 00:03:09 -08:00
Socratis Petrides 899433f79f added PrerinementTransfer SparseMatrix. Still need to address the ND tet case wrt to doftrans 2026-01-23 18:54:42 -08:00
Socratis Petrides 16db883427 style 2026-01-22 22:11:46 -08:00
Socratis Petrides 1834268091 complete pref-tests 2026-01-22 22:11:25 -08:00
Socratis Petrides 947e25f769 clean up of ProjectTrace methods 2026-01-22 22:10:59 -08:00
Socratis Petrides 9fbe90527a fix trace pref prolongation 2026-01-22 22:09:35 -08:00
Socratis Petrides bfa672c644 error computation 2026-01-22 00:24:33 -08:00
Socratis Petrides afe388a573 style 2026-01-21 19:35:22 -08:00
Socratis Petrides 50a9cce9f4 first tests on project 2026-01-21 19:35:03 -08:00
Socratis Petrides 8ee2fdbfd9 Project coeff for trace/skeleton 2026-01-21 19:34:40 -08:00
Socratis Petrides d6b4594150 Pref mat-free for trace space 2026-01-21 19:34:06 -08:00
Ketan Mittal 5546250963 Merge branch 'master' into plbound-extremum 2026-01-21 11:00:02 -08:00
Ketan Mittal 7f4d7b8f4e Merge branch 'master' into plbound-extremum 2026-01-16 12:54:56 -08:00
Mittal, Ketan 6529372830 reviewer comments 2026-01-15 13:10:47 -08:00
Mittal, Ketan d66d799387 minor 2026-01-14 15:25:11 -08:00
Mittal, Ketan 51f205b273 reviewer comments 2026-01-14 14:14:31 -08:00
Ketan Mittal ad7cf12cd5 Merge branch 'master' into plbound-extremum 2026-01-13 12:24:55 -08:00
Ketan Mittal abdb023ae3 Merge branch 'master' into plbound-extremum 2026-01-12 10:12:14 -08:00
izaid 5cd3ec521b applied astyle 2026-01-08 23:19:16 +00:00
Mittal, Ketan d1a9c6e62d format miniapp output 2026-01-08 10:53:49 -08:00
Mittal, Ketan 8f0b57138b Merge branch 'master' of https://github.com/mfem/mfem into plbound-extremum 2026-01-08 10:37:40 -08:00
Mittal, Ketan 3167a1c98b remove default value from tol in pgridfunc.hpp 2026-01-08 10:31:19 -08:00
Socratis Petrides ca8e8b391c more fixes 2026-01-07 22:09:17 -08:00
Socratis Petrides 3ea74a3a40 further debugging and simplifications 2026-01-07 19:15:10 -08:00
Tzanio Kolev 9488637956 Merge branch 'master' into pfes 2026-01-07 14:20:49 -08:00
Mittal, Ketan f429737c12 change 0.0 to 0_r 2026-01-06 12:22:27 -08:00
Mittal, Ketan 04fd683e9c doxygen fix 2026-01-06 11:38:42 -08:00
Mittal, Ketan 4b9f46a6b0 Merge branch 'plbound-extremum' of https://github.com/mfem/mfem into plbound-extremum 2026-01-06 11:30:04 -08:00
Mittal, Ketan 793a5b6d60 minor 2026-01-06 11:29:47 -08:00
Mittal, Ketan 4e6e9a13b6 Merge branch 'master' of https://github.com/mfem/mfem into plbound-extremum 2026-01-06 11:15:53 -08:00
izaid 90353c437e Fixed bug in GetSurfaceFittingErrors 2026-01-01 23:09:09 +01:00
Socratis Petrides 8fc02b66ea style 2025-12-23 09:17:51 -08:00
Socratis Petrides 9c3f8f6a6d one more fix 2025-12-23 09:17:15 -08:00
Socratis Petrides 203524a35f debugging uw 2025-12-23 09:06:44 -08:00
Socratis Petrides 873c9920fd minor 2025-12-15 18:31:51 -08:00
Socratis Petrides 083bd886e8 adding fosls and full uw driver 2025-12-15 17:57:15 -08:00
Socratis Petrides a1f155a1c9 lh-utils 2025-12-15 17:55:48 -08:00
Socratis Petrides d1b4b8d31c zero block fix in block complexhypremat 2025-12-15 17:40:49 -08:00
Socratis Petrides 8c1cac84ee add default sample run in lh-eld-fem 2025-12-15 17:40:14 -08:00
Socratis Petrides c8334770bc minor fix complex_op 2025-12-15 17:39:48 -08:00
Ketan Mittal 5b917af59b Merge branch 'master' into plbound-extremum 2025-12-15 12:59:44 -08:00
Mittal, Ketan f956c6b2de function for pargridfunction 2025-12-14 16:33:38 -08:00
Mittal, Ketan 62dbc570b2 add functions to compute min/max over all elements 2025-12-14 15:57:05 -08:00
Mittal, Ketan c221f5a29d initial commit 2025-12-13 15:20:43 -08:00
Socratis Petrides 339bd4fb78 rearrange plasma related examples
adding convenience classes for complex block forms

simplified lh-fem so that coupling it's more easily added using the complex block form

added pmaxwell coupled fem for testing complexblockform

adding missing biliniteg

adding eld formulation

adding custom integrator for correct directionl diffusion

clean up the fem ELD formulation

started on uw-dpg for ELD formulation

lh-dpg debugging

trying weak coupling

cleaning up

added complex-mumps, started fosls-dpg example

added complex mumps, cont on fosls-dpg
2025-11-14 17:06:20 -08:00
psocratis 9f41cd63fa minor edits 2025-04-28 10:16:07 -07:00
Socratis Petrides 27fcac14b6 more clean up 2025-01-08 18:56:38 -08:00
Socratis Petrides a68f588066 adding the option of blocksymmetric prec 2025-01-08 17:45:51 -08:00
Socratis Petrides 237a04cb7b adding primal in 2D 2025-01-08 16:21:05 -08:00
Socratis Petrides b69a7edf81 Fixing issue with int rule. Cleaning up Paraview and parameter ouput 2025-01-08 16:19:40 -08:00
psocratis 73ed9be9d9 adding AMR 2025-01-05 22:10:15 -08:00
psocratis cdc4f9fcfe transpose coeff issue + tol adjustment 2025-01-03 17:31:50 -08:00
psocratis 616447e78b leftover print 2025-01-03 14:34:16 -08:00
psocratis 428acfec3b minor 2025-01-03 14:32:33 -08:00
Socratis Petrides 73daa5d1d6 marking and removing internal bdr 2025-01-03 11:42:58 -08:00
psocratis 600d484f0d fixing integration rule a 2025-01-02 17:54:47 -08:00
Socratis Petrides f325e7338f new 2D maxwell driver 2024-12-23 14:11:41 -08:00
Socratis Petrides 61ed91c994 master merge conflicts 2024-12-20 17:41:48 -08:00
Socratis Petrides 13583fe7fd uw maxwell coupled with primal vector diffusion in both 2D and 3D 2024-07-22 12:33:11 -07:00
Socratis Petrides e044979d06 makefile 2024-07-16 17:12:33 -07:00
Socratis Petrides 72b308a816 adding example of uw-maxwell coupled with primal vector diffusion in 3D 2024-07-16 17:11:59 -07:00
Socratis Petrides b5fc8e748a fix vdim in complex weak form resisual 2024-07-16 17:11:12 -07:00
Socratis Petrides ad574a171a fix vdim in complex static cond 2024-07-16 17:10:49 -07:00
Socratis Petrides 681a949ca1 vector-diff-dpg 2024-05-20 11:11:37 -07:00
Socratis Petrides c157ced8e2 diffusion and vector diffusion primal dpg 2024-05-02 22:17:12 -07:00
Socratis Petrides fce3d1288b fix vdim bug in weakform.cpp 2024-05-02 22:16:30 -07:00
Socratis Petrides 155dede4fe fix vdim bug in block static cond 2024-05-02 22:16:09 -07:00
Socratis Petrides bfcd9b45fc add AssembleElementMatrix2 to VectorDiffusion 2024-05-02 22:15:43 -07:00
psocratis 735a243ad1 minor 2024-01-12 13:48:34 -08:00
psocratis d4c82203fb primal-tokamak 2023-12-13 13:47:29 -08:00
Socratis Petrides 5e513c1c79 primal pmaxwell 2023-12-13 13:46:33 -08:00
Socratis Petrides 7c0f038e9b adding acoustics and maxwell primal 2023-12-06 20:41:00 -08:00
psocratis f310cb722d starting primal 2023-12-06 10:56:46 -08:00
Socratis Petrides 9c0df97d9e minor 2023-05-03 09:42:36 -07:00
Socratis Petrides 5eca435eca adding mumps 2023-05-02 16:02:41 -07:00
Socratis Petrides b617a03c20 fem and primal 2023-05-01 16:31:04 -07:00
Socratis 93aa70bb40 fixing valgrind issues for ComplexMUMPSSolver 2020-10-16 17:51:47 -07:00
psocratis fa4e1c37b2 adding ComplexMUMPSSolver 2020-10-16 15:50:06 -07:00
116 changed files with 25162 additions and 553 deletions
+34
View File
@@ -8,6 +8,24 @@
https://mfem.org
Version 4.10 (development)
==========================
Discretization improvements
---------------------------
- Added symmetric positive-weight quadrature rules for triangles and
tetrahedra (orders 0-20). These rules guarantee all-positive weights and
interior quadrature points, improving numerical stability over the previous
Grundmann-Moller rules. Selected via SimplexQuadrature::PositiveWeights
(now the default). The previous rules remain available as
SimplexQuadrature::Legacy.
Triangle rules: Witherden & Vincent, Comput. Math. Appl. 69(10):1232-1241,
2015.
Tet rules (d=1-13): Witherden & Vincent (ibid).
Tet rules (d=14-20): Chuluunbaatar et al., Comput. Math. Appl. 124:89-97,
2022.
Version 4.9.1 (development)
===========================
@@ -16,17 +34,33 @@ Discretization improvements
- Improved the gridfunction projection routines. Projections work for Scalar,
Vector and VectorFE, also NURBS versions. Optionally different types of
projections can be selected, default behaviour has not changed.
- Added gridfunction projection methods for trace spaces, i.e., project
coefficients on the mesh skeleton.
- Added methods to estimate function extremum using piecewise linear bounds +
recursive subdivision.
Meshing improvements
--------------------
- Improved support for 1D NURBS meshes with variable order, including using
the patches construct for 1D NURBS meshes.
Linear and nonlinear solvers
----------------------------
- Added interface to MUMPS direct solver for complex-valued problems.
Its usage is demonstrated in ex25p. See http://mumps.enseeiht.fr/ for more details.
Supported versions >= 5.1.1.
New and updated examples and miniapps
-------------------------------------
- Electromagnetics/lorentz miniapp has been updated to leverage the ParticleSet
capability.
- Added (Complex)PRefinementMultigrid solver option in DPG miniapps.
Linear and nonlinear solvers
----------------------------
- Added support for trace spaces in PRefinementTransferOperator. This is used in
PRefinement multigrid methods for problems posed on trace spaces (see DPG miniapps)
Version 4.9, released on Dec 11, 2025
=====================================
+6 -4
View File
@@ -76,7 +76,9 @@ set(XSDK_ENABLE_Fortran OFF)
# Check if we need to enable C or Fortran.
if (MFEM_USE_CONDUIT OR
MFEM_USE_SIDRE OR
MFEM_USE_PETSC)
MFEM_USE_PETSC OR
MFEM_USE_MUMPS OR
MFEM_USE_COMPLEX_MUMPS)
# This seems to be needed by:
# * find_package(BLAS REQUIRED) and
# * find_package(HDF5 REQUIRED) needed, in turn, by:
@@ -84,7 +86,7 @@ if (MFEM_USE_CONDUIT OR
# * find_package(PETSc REQUIRED)
set(XSDK_ENABLE_C ON)
endif()
if (MFEM_USE_STRUMPACK OR MFEM_USE_MUMPS)
if (MFEM_USE_STRUMPACK OR MFEM_USE_MUMPS OR MFEM_USE_COMPLEX_MUMPS)
# Just needed to find the MPI_Fortran libraries to link with
set(XSDK_ENABLE_Fortran ON)
endif()
@@ -342,7 +344,7 @@ if (MFEM_USE_MPI)
endif()
endif()
else()
set(PKGS_NEED_MPI SUPERLU MUMPS PETSC SLEPC STRUMPACK PUMI)
set(PKGS_NEED_MPI SUPERLU MUMPS COMPLEX_MUMPS PETSC SLEPC STRUMPACK PUMI)
foreach(PKG IN LISTS PKGS_NEED_MPI)
if (MFEM_USE_${PKG})
message(STATUS "Disabling package ${PKG} - requires MPI")
@@ -415,7 +417,7 @@ if (MFEM_USE_SUPERLU)
endif()
# MUMPS can only be enabled in parallel
if (MFEM_USE_MUMPS)
if (MFEM_USE_MUMPS OR MFEM_USE_COMPLEX_MUMPS)
if (MFEM_USE_MPI)
find_package(MUMPS REQUIRED mumps_common pord)
set(MFEM_MUMPS_VERSION ${MUMPS_VERSION})
+1
View File
@@ -34,6 +34,7 @@ set(MFEM_USE_SUNDIALS @MFEM_USE_SUNDIALS@)
set(MFEM_USE_SUITESPARSE @MFEM_USE_SUITESPARSE@)
set(MFEM_USE_SUPERLU @MFEM_USE_SUPERLU@)
set(MFEM_USE_MUMPS @MFEM_USE_MUMPS@)
set(MFEM_USE_COMPLEX_MUMPS @MFEM_USE_COMPLEX_MUMPS@)
set(MFEM_USE_STRUMPACK @MFEM_USE_STRUMPACK@)
set(MFEM_USE_GINKGO @MFEM_USE_GINKGO@)
set(MFEM_USE_AMGX @MFEM_USE_AMGX@)
+1
View File
@@ -103,6 +103,7 @@
// Enable MFEM functionality based on the MUMPS library.
#cmakedefine MFEM_USE_MUMPS
#cmakedefine MFEM_USE_COMPLEX_MUMPS
#cmakedefine MFEM_MUMPS_VERSION @MFEM_MUMPS_VERSION@
// Enable MFEM functionality based on the STRUMPACK library.
+52 -7
View File
@@ -17,13 +17,31 @@
include(MfemCmakeUtilities)
# Toggle which precision of MUMPS to use depending on the precision of MFEM.
# Decide headers/libs by MFEM precision
if (MFEM_USE_DOUBLE)
set(_mumps_header dmumps_c.h)
set(_mumps_lib dmumps)
elseif(MFEM_USE_SINGLE)
set(_mumps_header smumps_c.h)
set(_mumps_lib smumps)
set(_rmumps_header dmumps_c.h)
set(_rmumps_lib dmumps)
set(_cmumps_header zmumps_c.h)
set(_cmumps_lib zmumps)
elseif (MFEM_USE_SINGLE)
set(_rmumps_header smumps_c.h)
set(_rmumps_lib smumps)
set(_cmumps_header cmumps_c.h)
set(_cmumps_lib cmumps)
endif()
# Choose which header/lib mfem_find_package should use as the "primary" one.
# If both enabled, prefer the real one as primary (either is fine).
if (MFEM_USE_MUMPS)
set(_mumps_header ${_rmumps_header})
set(_mumps_lib ${_rmumps_lib})
elseif (MFEM_USE_COMPLEX_MUMPS)
set(_mumps_header ${_cmumps_header})
set(_mumps_lib ${_cmumps_lib})
else()
# Should not happen in practice because FindMUMPS is only called when enabled,
set(_mumps_header ${_rmumps_header})
set(_mumps_lib ${_rmumps_lib})
endif()
mfem_find_package(MUMPS MUMPS MUMPS_DIR
@@ -31,8 +49,35 @@ mfem_find_package(MUMPS MUMPS MUMPS_DIR
"Paths to headers required by MUMPS."
"Libraries required by MUMPS."
ADD_COMPONENT mumps_common "include" ${_mumps_header} "lib" mumps_common
ADD_COMPONENT pord "include" ${_mumps_header} "lib" pord)
ADD_COMPONENT pord "include" ${_mumps_header} "lib" pord)
# If BOTH real and complex are enabled, ensure BOTH solver libs are linked.
if (MUMPS_FOUND AND MFEM_USE_MUMPS AND MFEM_USE_COMPLEX_MUMPS)
# Find the "other" solver library and append it.
find_library(_mfem_other_mumps_solver
NAMES ${_cmumps_lib}
HINTS ${MUMPS_DIR}
PATH_SUFFIXES lib lib64
NO_DEFAULT_PATH)
if (NOT _mfem_other_mumps_solver)
# Fall back to system search
find_library(_mfem_other_mumps_solver NAMES ${_cmumps_lib})
endif()
if (NOT _mfem_other_mumps_solver)
message(FATAL_ERROR
"MFEM_USE_MUMPS=ON and MFEM_USE_COMPLEX_MUMPS=ON, but could not find "
"the complex solver library '${_cmumps_lib}' in MUMPS_DIR='${MUMPS_DIR}'.")
endif()
# Put solver libs first (important for static link order)
# MUMPS_LIBRARIES contains the primary solver already + common + pord.
# We prepend the other solver.
list(INSERT MUMPS_LIBRARIES 0 ${_mfem_other_mumps_solver})
endif()
# Version detection
if (MUMPS_FOUND AND (NOT MUMPS_VERSION))
try_run(MUMPS_VERSION_RUN_RESULT MUMPS_VERSION_COMPILE_RESULT
${CMAKE_CURRENT_BINARY_DIR}/config
@@ -871,13 +871,13 @@ function(mfem_export_mk_files)
MFEM_USE_SINGLE MFEM_USE_DOUBLE MFEM_DEBUG MFEM_USE_EXCEPTIONS
MFEM_USE_ZLIB MFEM_USE_LIBUNWIND MFEM_USE_LAPACK MFEM_THREAD_SAFE
MFEM_USE_LEGACY_OPENMP MFEM_USE_OPENMP MFEM_USE_MEMALLOC MFEM_USE_SUNDIALS
MFEM_USE_SUITESPARSE MFEM_USE_SUPERLU MFEM_USE_SUPERLU5 MFEM_USE_MUMPS
MFEM_USE_STRUMPACK MFEM_USE_GINKGO MFEM_USE_AMGX MFEM_USE_MAGMA
MFEM_USE_GNUTLS MFEM_USE_NETCDF MFEM_USE_PETSC MFEM_USE_SLEPC
MFEM_USE_MPFR MFEM_USE_SIDRE MFEM_USE_FMS MFEM_USE_CONDUIT MFEM_USE_PUMI
MFEM_USE_HIOP MFEM_USE_GSLIB MFEM_USE_CUDA MFEM_USE_HIP MFEM_USE_RAJA
MFEM_USE_OCCA MFEM_USE_CEED MFEM_USE_CALIPER MFEM_USE_UMPIRE MFEM_USE_SIMD
MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO
MFEM_USE_SUITESPARSE MFEM_USE_SUPERLU MFEM_USE_SUPERLU5 MFEM_USE_MUMPS
MFEM_USE_COMPLEX_MUMPS MFEM_USE_STRUMPACK MFEM_USE_GINKGO MFEM_USE_AMGX
MFEM_USE_MAGMA MFEM_USE_GNUTLS MFEM_USE_NETCDF MFEM_USE_PETSC
MFEM_USE_SLEPC MFEM_USE_MPFR MFEM_USE_SIDRE MFEM_USE_FMS MFEM_USE_CONDUIT
MFEM_USE_PUMI MFEM_USE_HIOP MFEM_USE_GSLIB MFEM_USE_CUDA MFEM_USE_HIP
MFEM_USE_RAJA MFEM_USE_OCCA MFEM_USE_CEED MFEM_USE_CALIPER MFEM_USE_UMPIRE
MFEM_USE_SIMD MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO
MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_BENCHMARK MFEM_USE_PARELAG
MFEM_USE_TRIBOL MFEM_USE_MOONOLITH MFEM_USE_ALGOIM MFEM_USE_ENZYME
MFEM_USE_HDF5)
+3
View File
@@ -140,6 +140,9 @@ constexpr real_t operator""_r(unsigned long long v)
#ifdef MFEM_USE_MUMPS
#error Building with MUMPS (MFEM_USE_MUMPS=YES) requires MPI (MFEM_USE_MPI=YES)
#endif
#ifdef MFEM_USE_COMPLEX_MUMPS
#error Building with COMPLEX_MUMPS (MFEM_USE_COMPLEX_MUMPS=YES) requires MPI (MFEM_USE_MPI=YES)
#endif
#ifdef MFEM_USE_STRUMPACK
#error Building with STRUMPACK (MFEM_USE_STRUMPACK=YES) requires MPI (MFEM_USE_MPI=YES)
#endif
+1
View File
@@ -103,6 +103,7 @@
// Enable MFEM functionality based on the MUMPS library.
// #define MFEM_USE_MUMPS
// #define MFEM_USE_COMPLEX_MUMPS
// #define MFEM_MUMPS_VERSION @MFEM_MUMPS_VERSION@
// Enable MFEM functionality based on the STRUMPACK library.
+2 -1
View File
@@ -37,6 +37,7 @@ option(MFEM_USE_SUITESPARSE "Enable SuiteSparse usage" OFF)
option(MFEM_USE_SUPERLU "Enable SuperLU_DIST usage" OFF)
option(MFEM_USE_SUPERLU5 "Use the old SuperLU_DIST 5.1 version" OFF)
option(MFEM_USE_MUMPS "Enable MUMPS usage" OFF)
option(MFEM_USE_COMPLEX_MUMPS "Enable COMPLEX_MUMPS usage" OFF)
option(MFEM_USE_STRUMPACK "Enable STRUMPACK usage" OFF)
option(MFEM_USE_GINKGO "Enable Ginkgo usage" OFF)
option(MFEM_USE_AMGX "Enable AmgX usage" OFF)
@@ -152,7 +153,7 @@ set(SuperLUDist_REQUIRED_PACKAGES "MPI" "ParMETIS" "METIS"
set(MUMPS_DIR "${MFEM_DIR}/../MUMPS_5.5.0" CACHE PATH
"Path to the MUMPS library.")
# MUMPS may also depend on "OpenMP", depending on how it was compiled.
set(MUMPS_REQUIRED_PACKAGES "MPI" "MPI_Fortran" "ParMETIS" "METIS"
set(MUMPS_REQUIRED_PACKAGES "MPI" "MPI_Fortran" "METIS"
"ScaLAPACK" "LAPACK" "BLAS" CACHE STRING
"Additional packages required by MUMPS.")
# If the MPI package does not find all required Fortran libraries:
+12 -5
View File
@@ -152,6 +152,7 @@ MFEM_USE_SUITESPARSE = NO
MFEM_USE_SUPERLU = NO
MFEM_USE_SUPERLU5 = NO
MFEM_USE_MUMPS = NO
MFEM_USE_COMPLEX_MUMPS = NO
MFEM_USE_STRUMPACK = NO
MFEM_USE_GINKGO = NO
MFEM_USE_AMGX = NO
@@ -248,7 +249,7 @@ ifeq (YES,$(MFEM_USE_HIP))
endif
# METIS library configuration
ifeq ($(MFEM_USE_SUPERLU)$(MFEM_USE_STRUMPACK)$(MFEM_USE_MUMPS),NONONO)
ifeq ($(MFEM_USE_SUPERLU)$(MFEM_USE_STRUMPACK)$(MFEM_USE_MUMPS)$(MFEM_USE_COMPLEX_MUMPS),NONONONO)
ifeq ($(MFEM_USE_METIS_5),NO)
METIS_DIR = @MFEM_DIR@/../metis-4.0
METIS_OPT =
@@ -352,13 +353,19 @@ MPI_FORTRAN_LIB = -lmpifort
# MUMPS library configuration
MUMPS_DIR = @MFEM_DIR@/../MUMPS_5.5.0
MUMPS_OPT = -I$(MUMPS_DIR)/include
MUMPS_LIB = $(XLINKER)-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib
COMPLEX_MUMPS_OPT = $(MUMPS_OPT)
MUMPS_COMMON_PATH = $(XLINKER)-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib
MUMPS_COMMON_LIB = -lmumps_common -lpord $(SCALAPACK_LIB) $(LAPACK_LIB) $(MPI_FORTRAN_LIB)
ifeq ($(MFEM_USE_SINGLE),YES)
MUMPS_LIB += -lsmumps
MUMPS_SOLVER_LIB = -lsmumps
COMPLEX_MUMPS_SOLVER_LIB = -lcmumps
else
MUMPS_LIB += -ldmumps
MUMPS_SOLVER_LIB = -ldmumps
COMPLEX_MUMPS_SOLVER_LIB = -lzmumps
endif
MUMPS_LIB += -lmumps_common -lpord $(SCALAPACK_LIB) $(LAPACK_LIB) $(MPI_FORTRAN_LIB)
MUMPS_LIB = $(MUMPS_COMMON_PATH) $(MUMPS_SOLVER_LIB) $(MUMPS_COMMON_LIB)
COMPLEX_MUMPS_LIB = $(MUMPS_COMMON_PATH) $(COMPLEX_MUMPS_SOLVER_LIB) $(MUMPS_COMMON_LIB)
# STRUMPACK library configuration
STRUMPACK_DIR = @MFEM_DIR@/../STRUMPACK-build
+8
View File
@@ -10,10 +10,18 @@
// CONTRIBUTING.md for details.
#ifdef MFEM_USE_SINGLE
#ifdef MFEM_USE_COMPLEX_MUMPS
#include "cmumps_c.h"
#else
#include "smumps_c.h"
#endif
#else
#ifdef MFEM_USE_COMPLEX_MUMPS
#include "zmumps_c.h"
#else
#include "dmumps_c.h"
#endif
#endif
#include <string>
#include <iostream>
#include <algorithm>
+1 -1
View File
@@ -42,7 +42,7 @@ GHV_FLAGS = $(MFEM_CXXFLAGS) $(subst @MFEM_DIR@,$(if $(MFEM_DIR),$(MFEM_DIR),..)
SMX = $(if $(MFEM_USE_PUMI:NO=),MFEM_USE_SIMMETRIX)
SMX_PATH = $(PUMI_DIR)/include/gmi_sim.h
SMX_FILE = $(subst @MFEM_DIR@,$(if $(MFEM_DIR),$(MFEM_DIR),..),$(SMX_PATH))
MUMPS = $(MFEM_USE_MUMPS:NO=)
MUMPS = $(if $(filter YES,$(MFEM_USE_MUMPS) $(MFEM_USE_COMPLEX_MUMPS)),YES,)
GMV_CXX ?= $(MFEM_CXX)
GMV = get_mumps_version
GMV_FLAGS = $(MFEM_CXXFLAGS) $(subst @MFEM_DIR@,$(if $(MFEM_DIR),$(MFEM_DIR),..),$(MUMPS_OPT))
+11 -3
View File
@@ -200,7 +200,7 @@ int main(int argc, char *argv[])
args.AddOption(&slu_solver, "-slu", "--superlu", "-no-slu",
"--no-superlu", "Use the SuperLU Solver.");
#endif
#ifdef MFEM_USE_MUMPS
#if defined(MFEM_USE_MUMPS) || defined(MFEM_USE_COMPLEX_MUMPS)
args.AddOption(&mumps_solver, "-mumps", "--mumps-solver", "-no-mumps",
"--no-mumps-solver", "Use the MUMPS Solver.");
#endif
@@ -502,16 +502,24 @@ int main(int argc, char *argv[])
delete A;
}
#endif
#ifdef MFEM_USE_MUMPS
#if defined(MFEM_USE_MUMPS) || defined(MFEM_USE_COMPLEX_MUMPS)
if (!pa && mumps_solver)
{
HypreParMatrix *A = Ah.As<ComplexHypreParMatrix>()->GetSystemMatrix();
ComplexHypreParMatrix *Ahc = Ah.As<ComplexHypreParMatrix>();
#ifdef MFEM_USE_COMPLEX_MUMPS
ComplexMUMPSSolver mumps(MPI_COMM_WORLD);
mumps.SetPrintLevel(0);
mumps.SetOperator(*Ahc);
mumps.Mult(B, X);
#else
HypreParMatrix *A = Ahc->GetSystemMatrix();
MUMPSSolver mumps(A->GetComm());
mumps.SetPrintLevel(0);
mumps.SetMatrixSymType(MUMPSSolver::MatType::UNSYMMETRIC);
mumps.SetOperator(*A);
mumps.Mult(B, X);
delete A;
#endif
}
#endif
// 16a. Set up the parallel Bilinear form a(.,.) for the preconditioner
+219 -6
View File
@@ -2592,6 +2592,22 @@ void MixedCurlIntegrator::AssembleElementMatrix2(
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
}
// Workspace for matrix-coefficient path
DenseMatrix M;
DenseMatrix Mcurl; // (dimc x trial_dof) when MQ is used
if (MQ)
{
// MQ only makes sense when curl(u) is vector-valued (case 1 or 3)
MFEM_VERIFY(dimc == dim,
"MixedCurlIntegrator: MatrixCoefficient requires vector-valued curl(u) "
"(3D H(curl) or 2D H1 rotated-grad case).");
M.SetSize(dimc);
Mcurl.SetSize(dimc, trial_dof);
}
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
@@ -2607,20 +2623,59 @@ void MixedCurlIntegrator::AssembleElementMatrix2(
}
test_fe.CalcPhysShape(Trans, shape);
c = ip.weight*Trans.Weight();
if (Q)
if (MQ)
{
// apply matrix coefficient to curl(u)
MQ->Eval(M, Trans, ip);
for (int d = 0; d < dimc; ++d)
{
for (int jj = 0; jj < trial_dof; ++jj)
{
real_t val = 0.0;
for (int k = 0; k < dimc; ++k)
{
const real_t *curl_k = &(curlshape.GetData())[k * trial_dof];
val += M(d, k) * curl_k[jj];
}
Mcurl(d, jj) = val;
}
}
}
else if (Q)
{
c *= Q->Eval(Trans, ip);
}
shape *= c;
for (int d = 0; d < dimc; ++d)
if (MQ)
{
real_t * curldata = &(curlshape.GetData())[d*trial_dof];
for (int jj = 0; jj < trial_dof; ++jj)
// use transformed curl
for (int d = 0; d < dimc; ++d)
{
for (int ii = 0; ii < test_dof; ++ii)
for (int jj = 0; jj < trial_dof; ++jj)
{
elmat(d * test_dof + ii, jj) += shape(ii) * curldata[jj];
const real_t cur_val = Mcurl(d, jj);
for (int ii = 0; ii < test_dof; ++ii)
{
elmat(d * test_dof + ii, jj) += shape(ii) * cur_val;
}
}
}
}
else
{
for (int d = 0; d < dimc; ++d)
{
real_t * curldata = &(curlshape.GetData())[d*trial_dof];
for (int jj = 0; jj < trial_dof; ++jj)
{
for (int ii = 0; ii < test_dof; ++ii)
{
elmat(d * test_dof + ii, jj) += shape(ii) * curldata[jj];
}
}
}
}
@@ -3109,6 +3164,94 @@ void VectorDiffusionIntegrator::AssembleElementMatrix(
}
}
void VectorDiffusionIntegrator::AssembleElementMatrix2(const FiniteElement
&trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat)
{
int tr_nd = trial_fe.GetDof();
int te_nd = test_fe.GetDof();
dim = trial_fe.GetDim();
int spaceDim = Trans.GetSpaceDim();
bool square = (dim == spaceDim);
vdim = (vdim <= 0) ? spaceDim : vdim;
if (VQ)
{
vcoeff.SetSize(vdim);
}
else if (MQ)
{
mcoeff.SetSize(vdim);
}
dshape.SetSize(tr_nd, dim);
dshapedxt.SetSize(tr_nd, spaceDim);
te_dshape.SetSize(te_nd, dim);
te_dshapedxt.SetSize(te_nd, spaceDim);
elmat.SetSize(vdim * te_nd, vdim*tr_nd);
pelmat.SetSize(te_nd, tr_nd);
const IntegrationRule *ir = IntRule ? IntRule : &DiffusionIntegrator::GetRule(
trial_fe, test_fe);
elmat = 0.0;
for (int i = 0; i < ir -> GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trial_fe.CalcDShape(ip, dshape);
test_fe.CalcDShape(ip, te_dshape);
Trans.SetIntPoint(&ip);
double w = Trans.Weight();
w = ip.weight / (square ? w : w*w*w);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape, Trans.AdjugateJacobian(), dshapedxt);
Mult(te_dshape, Trans.AdjugateJacobian(), te_dshapedxt);
if (VQ)
{
VQ->Eval(vcoeff, Trans, ip);
for (int k = 0; k < vdim; ++k)
{
pelmat = 0.0;
AddMult_a_ABt(w*vcoeff(k), te_dshapedxt, dshapedxt, pelmat);
elmat.AddMatrix(pelmat, te_nd*k, tr_nd*k);
}
}
else if (MQ)
{
MQ->Eval(mcoeff, Trans, ip);
for (int ii = 0; ii < vdim; ++ii)
{
for (int jj = 0; jj < vdim; ++jj)
{
pelmat = 0.0;
AddMult_a_ABt(w*mcoeff(ii,jj), te_dshapedxt, dshapedxt, pelmat);
elmat.AddMatrix(pelmat, te_nd*ii, tr_nd*jj);
}
}
}
else
{
if (Q) { w *= Q->Eval(Trans, ip); }
dshapedxt *= w;
MultABt(te_dshapedxt,dshapedxt, pelmat);
for (int k = 0; k < vdim; ++k)
{
elmat.AddMatrix(pelmat, te_nd*k, tr_nd*k);
}
}
}
}
void VectorDiffusionIntegrator::AssembleElementVector(
const FiniteElement &el, ElementTransformation &Tr,
const Vector &elfun, Vector &elvect)
@@ -4451,6 +4594,76 @@ void TraceIntegrator::AssembleTraceFaceMatrix(int elem,
}
}
void VectorTraceIntegrator::AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations & Trans,
DenseMatrix &elmat)
{
MFEM_VERIFY(test_fe.GetMapType() == FiniteElement::VALUE,
"TraceIntegrator::AssembleTraceFaceMatrix: Test space should be H1");
MFEM_VERIFY(trial_face_fe.GetMapType() == FiniteElement::INTEGRAL,
"TraceIntegrator::AssembleTraceFaceMatrix: Trial space should be RT trace");
int i, j, face_ndof, ndof;
int order;
int spaceDim = Trans.GetSpaceDim();
vdim = (vdim == -1) ? spaceDim : vdim;
face_ndof = trial_face_fe.GetDof();
ndof = test_fe.GetDof();
face_shape.SetSize(face_ndof);
shape.SetSize(ndof);
elmat.SetSize(ndof*vdim, face_ndof*vdim);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
order = test_fe.GetOrder();
order += trial_face_fe.GetOrder();
ir = &IntRules.Get(Trans.GetGeometryType(), order);
}
int iel = Trans.Elem1->ElementNo;
if (iel != elem)
{
MFEM_VERIFY(elem == Trans.Elem2->ElementNo, "Elem != Trans.Elem2->ElementNo");
}
double scale = 1.0;
if (iel != elem) { scale = -1.; }
for (int p = 0; p < ir->GetNPoints(); p++)
{
const IntegrationPoint &ip = ir->IntPoint(p);
// Set the integration point in the face and the neighboring elements
Trans.SetAllIntPoints(&ip);
// Trace finite element shape function
trial_face_fe.CalcPhysShape(Trans,face_shape);
// Finite element shape function
ElementTransformation * eltrans = (iel == elem) ? Trans.Elem1 : Trans.Elem2;
test_fe.CalcPhysShape(*eltrans, shape);
face_shape *= Trans.Weight()*ip.weight*scale;
for (int k = 0; k < vdim; k++)
{
for (i = 0; i < ndof; i++)
{
for (j = 0; j < face_ndof; j++)
{
elmat(i+k*ndof, j+k*face_ndof) += shape(i) * face_shape(j);
}
}
}
}
}
void NormalTraceIntegrator::AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
+46 -9
View File
@@ -2936,16 +2936,27 @@ class MixedCurlIntegrator : public BilinearFormIntegrator
{
protected:
Coefficient *Q;
MatrixCoefficient *MQ;
private:
Vector shape;
DenseMatrix dshape;
DenseMatrix curlshape;
DenseMatrix elmat_comp;
public:
MixedCurlIntegrator() : Q{NULL} { }
MixedCurlIntegrator(Coefficient *q_) : Q{q_} { }
MixedCurlIntegrator(Coefficient &q) : Q{&q} { }
MixedCurlIntegrator() : Q(nullptr), MQ(nullptr) { }
// Scalar coefficient
explicit MixedCurlIntegrator(Coefficient *q)
: Q(q), MQ(nullptr) { }
explicit MixedCurlIntegrator(Coefficient &q)
: Q(&q), MQ(nullptr) { }
// Matrix coefficient
explicit MixedCurlIntegrator(MatrixCoefficient *mq)
: Q(nullptr), MQ(mq) { }
explicit MixedCurlIntegrator(MatrixCoefficient &mq)
: Q(nullptr), MQ(&mq) { }
void AssembleElementMatrix2(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
@@ -3129,9 +3140,11 @@ public:
to be the spatial dimension (i.e. 2-dimension or 3-dimension). */
class VectorDiffusionIntegrator : public BilinearFormIntegrator
{
int vdim = -1;
private:
DenseMatrix dshape, dshapedxt, pelmat;
DenseMatrix mcoeff;
DenseMatrix te_dshape, te_dshapedxt;
int vdim = -1;
DenseMatrix mcoeff,dshapedxt_m;
Vector vcoeff;
protected:
@@ -3193,6 +3206,10 @@ public:
void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
DenseMatrix &elmat) override;
void AssembleElementMatrix2(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat) override;
void AssembleElementVector(const FiniteElement &el,
ElementTransformation &Tr,
const Vector &elfun, Vector &elvect) override;
@@ -3798,9 +3815,29 @@ public:
DenseMatrix &elmat);
};
/** Integrator for the form: $ \langle v, w \cdot n \rangle $ over a face (the interface) where
the trial variable $v$ is defined on the interface ($H^{1/2}$, i.e., trace of $H^1$)
and the test variable $w$ is in an $H(div)$-conforming space. */
/** Integrator for the DPG form: < v, w > over a face (the interface) where
the trial variable v is defined on the interface
((H^-1/2)^vdim i.e., vᵢ :=uᵢ⋅n (for normal trace of H(div)^vdim)
and the test variable w is in an dim copies of H1-conforming space. */
class VectorTraceIntegrator : public BilinearFormIntegrator
{
private:
Vector face_shape, shape;
int vdim = -1;
public:
VectorTraceIntegrator() { }
void AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations &Trans,
DenseMatrix &elmat);
void SetVDim(int vdim_) { vdim = vdim_; }
};
/** Integrator for the form: < v, w.n > over a face (the interface) where
the trial variable v is defined on the interface (H^1/2, i.e., trace of H1)
and the test variable w is in an H(div)-conforming space. */
class NormalTraceIntegrator : public BilinearFormIntegrator
{
private:
+67 -28
View File
@@ -39,8 +39,8 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
b_type = b_type_i;
cp_type = cp_type_i;
tol = tol_i;
lbound.SetSize(nb, ncp);
ubound.SetSize(nb, ncp);
lbound.SetSize(ncp, nb);
ubound.SetSize(ncp, nb);
nodes.SetSize(nb);
weights.SetSize(nb);
control_points.SetSize(ncp);
@@ -125,21 +125,25 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
{
if (j == 0)
{
lbound(i, j) = bv(i);
ubound(i, j) = bv(i);
lbound(j,i) = bv(i);
ubound(j,i) = bv(i);
}
else if (j == ncp-1)
{
lbound(i, j) = bv(i);
ubound(i, j) = bv(i);
lbound(j,i) = bv(i);
ubound(j,i) = bv(i);
}
else
{
vals(0) = bv(i);
vals(1) = bmv(i) + dm*bdmv(i);
vals(2) = bpv(i) + dp*bdpv(i);
lbound(i, j) = vals.Min()-tol; // tolerance for good measure
ubound(i, j) = vals.Max()+tol; // tolerance for good measure
lbound(j,i) = vals.Min()-tol; // tolerance for good measure
ubound(j,i) = vals.Max()+tol; // tolerance for good measure
if (b_type == 2)
{
lbound(j,i) = std::max(lbound(j,i),0_r);
}
}
}
}
@@ -273,8 +277,7 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
intmax.SetSize(ncp);
intmin = 0.0;
intmax = 0.0;
Vector coeffm(nb);
coeffm = 0.0;
Vector coeffm;
real_t a0 = 0.0;
real_t a1 = 0.0;
@@ -302,6 +305,8 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
// compute L2 projection for linear bases: a0 + a1*x
if (proj)
{
coeffm.SetSize(nb);
coeffm = 0.0;
for (int i = 0; i < nb; i++)
{
x = 2.0*nodes_int(i)-1;
@@ -342,8 +347,8 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
real_t c = coeffm(i);
for (int j = 0; j < ncp; j++)
{
intmin(j) += min(lbound(i,j)*c, ubound(i,j)*c);
intmax(j) += max(lbound(i,j)*c, ubound(i,j)*c);
intmin(j) += min(lbound(j,i)*c, ubound(j,i)*c);
intmax(j) += max(lbound(j,i)*c, ubound(j,i)*c);
}
}
}
@@ -474,10 +479,10 @@ void PLBound::Get2DBounds(const Vector &coeff, Vector &intmin,
real_t w1 = intmaxT(id2++);
for (int k = 0; k < ncp; k++) // kth row
{
vals(0) = w0*lbound(j,k);
vals(1) = w0*ubound(j,k);
vals(2) = w1*lbound(j,k);
vals(3) = w1*ubound(j,k);
vals(0) = w0*lbound(k,j);
vals(1) = w0*ubound(k,j);
vals(2) = w1*lbound(k,j);
vals(3) = w1*ubound(k,j);
intmin(k*ncp+i) += vals.Min();
intmax(k*ncp+i) += vals.Max();
}
@@ -553,17 +558,17 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
for (int i = 0; i < nb; i++)
{
x = 2.0*nodes(i)-1; // x-coordinate
minBounds(i) -= a0V(j) + a1V(j)*x;
maxBounds(i) -= a0V(j) + a1V(j)*x;
minNodalVals(i) -= a0V(j) + a1V(j)*x;
maxNodalVals(i) -= a0V(j) + a1V(j)*x;
}
// Compute Bernstein coefficients
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
lu.Solve(nb, 1, minBounds.GetData());
lu.Solve(nb, 1, maxBounds.GetData());
lu.Solve(nb, 1, minNodalVals.GetData());
lu.Solve(nb, 1, maxNodalVals.GetData());
for (int i = 0; i < nb; i++)
{
intminT(i*ncp2+j) = minBounds(i);
intmaxT(i*ncp2+j) = maxBounds(i);
intminT(i*ncp2+j) = minNodalVals(i);
intmaxT(i*ncp2+j) = maxNodalVals(i);
}
}
}
@@ -617,10 +622,10 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
real_t w1 = intmaxT(id2++);
for (int k = 0; k < ncp; k++) // kth slice
{
vals(0) = w0*lbound(j,k);
vals(1) = w0*ubound(j,k);
vals(2) = w1*lbound(j,k);
vals(3) = w1*ubound(j,k);
vals(0) = w0*lbound(k,j);
vals(1) = w0*ubound(k,j);
vals(2) = w1*lbound(k,j);
vals(3) = w1*ubound(k,j);
intmin(k*ncp2+i) += vals.Min();
intmax(k*ncp2+i) += vals.Max();
}
@@ -653,7 +658,8 @@ void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
Vector &nodesBern) const
{
const int nbern = nodesBern.Size();
L2_SegmentElement el(nbern-1, 2); // we use L2 to leverage lexicographic order
L2_SegmentElement el(nbern-1, 2);
// we use L2 to leverage lexicographic order
Array<int> ordering = el.GetLexicographicOrdering();
basisMat.SetSize(nbern, nbern);
Vector shape(nbern);
@@ -666,6 +672,39 @@ void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
}
}
DenseMatrix PLBound::GetBoundingMatrix(int dim, bool is_lower) const
{
if (dim > 1)
{
const int ncpd = static_cast<int>(std::pow(ncp, dim));
const int nbd = static_cast<int>(std::pow(nb, dim));
DenseMatrix boundND(ncpd, nbd);
Vector phimin, phimax, col;
Vector coeffs(nbd);
coeffs = 0.0;
for (int j = 0; j < nbd; j++)
{
coeffs(j) = 1.0;
boundND.GetColumnReference(j, col);
GetNDBounds(dim, coeffs, phimin, phimax);
col = is_lower ? phimin : phimax;
coeffs(j) = 0.0;
}
return boundND;
}
return is_lower ? lbound : ubound;
}
DenseMatrix PLBound::GetLowerBoundMatrix(int dim) const
{
return GetBoundingMatrix(dim, true);
}
DenseMatrix PLBound::GetUpperBoundMatrix(int dim) const
{
return GetBoundingMatrix(dim, false);
}
constexpr int PLBound::min_ncp_gl_x[2][11];
constexpr int PLBound::min_ncp_gll_x[2][11];
constexpr int PLBound::min_ncp_pos_x[2][11];
@@ -716,4 +755,4 @@ void PLBound::Print(std::ostream &outp) const
ubound.Print(outp);
}
}
}
+71 -20
View File
@@ -19,14 +19,18 @@ namespace mfem
{
/** @name Piecewise linear bounds of bases
\brief Piecewise linear bounds of bases can be used to compute bounds on the grid function in each element. The bounds for the bases are constructed based on the following parameters:
\brief Piecewise linear bounds of bases can be used to compute bounds on
the grid function in each element. The bounds for the bases are constructed
based on the following parameters:
(i) @b nb: number of bases/nodes in 1D (i.e. polynomial order+1),
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre
nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
2 - Positive/Bernstein bases on uniformly distributed nodes,
(iii) @b ncp: number of control points used to construct the piecewise linear bounds
(iii) @b ncp: number of control points used to construct the piecewise
linear bounds
(iv) @b cp_type: control point distribution. 0 - GL + end-points,
1 - Chebyshev.
@@ -35,7 +39,9 @@ namespace mfem
If the user does not specify @b ncp and @b cp_type, the minimum value of
@b ncp is used that would bound the bases for the @b cp_type. We default
to @b cp_type = 0 as it requires fewer number of points to bound the bases. Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and increasing @b ncp results in tighter bounds.
to @b cp_type = 0 as it requires fewer number of points to bound the bases.
Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and
increasing @b ncp results in tighter bounds.
Finally, only tensor-product elements are currently supported.
@@ -54,7 +60,7 @@ private:
bool proj = true; // Use linear projection to compute bounds.
real_t tol = 0.0; // offset bounds to avoid round-off errors
Vector nodes, weights, control_points;
DenseMatrix lbound, ubound; // nb x ncp matrices with bounds of all bases
DenseMatrix lbound, ubound; // ncp x nb matrices with bounds of all bases
// Some auxillary storage for computing the bounds with Bernstein
DenseMatrix basisMatNodes; // Bernstein bases at equispaced nodes
DenseMatrix basisMatInt; // Bernstein bases at GLL nodes
@@ -80,6 +86,9 @@ private:
{3,5,8,9,11,12,13,13,14,15,16}
};
/// Helper function to extract lower or upper bounding matrix
DenseMatrix GetBoundingMatrix(int dim, bool is_lower) const;
public:
// Constructor
PLBound(const int nb_i, const int ncp_i, const int b_type_i,
@@ -92,40 +101,82 @@ public:
PLBound(const FiniteElementSpace *fes,
const int ncp_i = -1, const int cp_type_i = 0);
// Get minimum number of control points needed to bound the given bases
/// Get minimum number of control points needed to bound the given bases
int GetMinimumPointsForGivenBases(int nb_i, int b_type_i,
int cp_type_i) const;
// Print information about the bounds
/// Print information about the bounds
void Print(std::ostream &outp = mfem::out) const;
// Enable (default) or disable linear projection before bounding.
// This projection increases the computational cost but results in tighter
// bounds.
/** @brief Enable (default) or disable linear projection before bounding.
*
* @details This projection increases the computational cost but results in
* tighter bounds.
*/
void SetProjectionFlagForBounding(bool proj_) { proj = proj_; }
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 1D/2D/3D.
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
* nodal coefficients in @a coeff in 1D/2D/3D.
*
* @param[in] rdim The spatial dimension of the element (1, 2, or 3).
* @param[in] coeff The vector of lexicographically-ordered coefficients.
* Should be of size nb^rdim, where nb is the number of
* bases/nodes in 1D. These coefficients must correspond
* to the bases type and number of bases, used in the
* constructor of PLBound.
*
* @param[out] intmin The vector of minimum bound for all control points.
* @param[out] intmax The vector of maximum bound for all control points.
* Both intmin and intmax are of size ncp^rdim, where
* ncp is the number of control points in 1D, and are
* ordered lexicographically.
*/
void GetNDBounds(const int rdim, const Vector &coeff,
Vector &intmin, Vector &intmax) const;
/// Get number of control points used to compute the bounds.
int GetNControlPoints() const { return ncp; }
/// Get 1D control point locations (lexicographic order) in [0,1].
const Vector &GetControlPoints() const { return control_points; }
/** @brief Get lower and upper bounding matrix (ncp^dim x nb^dim)
*
* @details The matrices can be used to compute the bounds at control points
* by a simple matrix-vector product with the
* lexicographically-ordered nodal coefficients.
* The resulting output is also lexicographically-ordered.
*
* @note These matrices do not account for the linear projection step that
* is optionally done in GetNDBounds before bounding the function.
*/
///@{
DenseMatrix GetLowerBoundMatrix(int dim = 1) const;
DenseMatrix GetUpperBoundMatrix(int dim = 1) const;
///@}
private:
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 1D.
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
* nodal coefficients in @a coeff in 1D.
* See GetNDBounds for details of the input and output parameters.
*/
void Get1DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 2D.
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
* nodal coefficients in @a coeff in 2D.
* See GetNDBounds for details of the input and output parameters.
*/
void Get2DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 3D.
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
* nodal coefficients in @a coeff in 3D.
* See GetNDBounds for details of the input and output parameters.
*/
void Get3DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
/// Setup matrix used to compute values at given 1D locations in [0,1]
/// for Bernstein bases.
/** @brief Setup matrix used to compute values at given 1D locations in [0,1]
* for Bernstein bases.
*/
void SetupBernsteinBasisMat(DenseMatrix &basisMat, Vector &nodesBern) const;
void Setup(const int nb_i, const int ncp_i, const int b_type_i,
+40 -39
View File
@@ -663,58 +663,59 @@ const
#pragma omp critical (DofToQuad)
#endif
{
// If the new Dof2Quad is already present, e.g. added in a previous call
// or added by another omp thread, return.
// Do not run if the new Dof2Quad is already present, e.g. added in a
// previous call or added by another omp thread.
if (DofToQuad::SearchArray(dof2quad_array, ir,
DofToQuad::LEXICOGRAPHIC_FULL))
{ return; }
// Undo the native ordering which is what FiniteElement::GetDofToQuad
// returns.
auto *d2q_new = new DofToQuad(d2q);
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
const int nqpt = ir.GetNPoints();
const int b_dim = (range_type == VECTOR) ? dim : 1;
for (int i = 0; i < nqpt; i++)
DofToQuad::LEXICOGRAPHIC_FULL) == nullptr)
{
for (int d = 0; d < b_dim; d++)
// Undo the native ordering which is what FiniteElement::GetDofToQuad
// returns.
auto *d2q_new = new DofToQuad(d2q);
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
const int nqpt = ir.GetNPoints();
const int b_dim = (range_type == VECTOR) ? dim : 1;
for (int i = 0; i < nqpt; i++)
{
for (int j = 0; j < dof; j++)
for (int d = 0; d < b_dim; d++)
{
const double val = d2q.B[i + nqpt*(d+b_dim*lex_ordering[j])];
d2q_new->B[i+nqpt*(d+b_dim*j)] = val;
d2q_new->Bt[j+dof*(i+nqpt*d)] = val;
for (int j = 0; j < dof; j++)
{
const double val = d2q.B[i + nqpt*(d+b_dim*lex_ordering[j])];
d2q_new->B[i+nqpt*(d+b_dim*j)] = val;
d2q_new->Bt[j+dof*(i+nqpt*d)] = val;
}
}
}
}
const int g_dim = [this]()
{
switch (deriv_type)
const int g_dim = [this]()
{
case GRAD: return dim;
case DIV: return 1;
case CURL: return cdim;
default: return 0;
}
}();
for (int i = 0; i < nqpt; i++)
{
for (int d = 0; d < g_dim; d++)
{
for (int j = 0; j < dof; j++)
switch (deriv_type)
{
const double val = d2q.G[i + nqpt*(d+g_dim*lex_ordering[j])];
d2q_new->G[i+nqpt*(d+g_dim*j)] = val;
d2q_new->Gt[j+dof*(i+nqpt*d)] = val;
case GRAD: return dim;
case DIV: return 1;
case CURL: return cdim;
default: return 0;
}
}();
for (int i = 0; i < nqpt; i++)
{
for (int d = 0; d < g_dim; d++)
{
for (int j = 0; j < dof; j++)
{
const double val = d2q.G[i + nqpt*(d+g_dim*lex_ordering[j])];
d2q_new->G[i+nqpt*(d+g_dim*j)] = val;
d2q_new->Gt[j+dof*(i+nqpt*d)] = val;
}
}
}
dof2quad_array.Append(d2q_new);
}
dof2quad_array.Append(d2q_new);
}
}
+1 -1
View File
@@ -589,7 +589,7 @@ void H1_TriangleElement::CalcHessian(const IntegrationPoint &ip,
Vector shape_x(p + 1), shape_y(p + 1), shape_l(p + 1);
Vector dshape_x(p + 1), dshape_y(p + 1), dshape_l(p + 1);
Vector ddshape_x(p + 1), ddshape_y(p + 1), ddshape_l(p + 1);
DenseMatrix ddu(dof, dim);
DenseMatrix ddu(dof, (dim*(dim+1))/2);
#endif
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x, ddshape_x);
+25
View File
@@ -250,6 +250,12 @@ public:
its GetOrder() method. */
virtual FiniteElementCollection *Clone(int p) const;
virtual int GetConstructorOrder() const
{
MFEM_ABORT("Collection " << Name() << " does not support GetConstructorOrder");
return -1;
}
protected:
const int base_p; ///< Order as returned by GetOrder().
@@ -314,6 +320,9 @@ public:
FiniteElementCollection *Clone(int p) const override
{ return new H1_FECollection(p, dim, b_type); }
int GetConstructorOrder() const override
{ return base_p; }
virtual ~H1_FECollection();
};
@@ -343,6 +352,10 @@ class H1_Trace_FECollection : public H1_FECollection
public:
H1_Trace_FECollection(const int p, const int dim,
const int btype = BasisType::GaussLobatto);
FiniteElementCollection *Clone(int p) const override
{ return new H1_Trace_FECollection(p, dim+1, b_type); }
};
/// Arbitrary order "L2-conforming" discontinuous finite elements.
@@ -396,6 +409,9 @@ public:
FiniteElementCollection *Clone(int p) const override
{ return new L2_FECollection(p, dim, b_type, m_type); }
int GetConstructorOrder() const override
{ return base_p; }
virtual ~L2_FECollection();
};
@@ -456,6 +472,9 @@ public:
FiniteElementCollection *Clone(int p) const override
{ return new RT_FECollection(p, dim, cb_type, ob_type); }
int GetConstructorOrder() const override
{ return base_p-1; }
virtual ~RT_FECollection();
};
@@ -536,6 +555,9 @@ public:
FiniteElementCollection *Clone(int p) const override
{ return new ND_FECollection(p, dim, cb_type, ob_type); }
int GetConstructorOrder() const override
{ return dim>1 ? base_p : base_p+1; }
virtual ~ND_FECollection();
};
@@ -548,6 +570,9 @@ public:
ND_Trace_FECollection(const int p, const int dim,
const int cb_type = BasisType::GaussLobatto,
const int ob_type = BasisType::GaussLegendre);
FiniteElementCollection *Clone(int p) const override
{ return new ND_Trace_FECollection(p, dim+1, cb_type, ob_type); }
};
/// Arbitrary order 3D H(curl)-conforming Nedelec finite elements in 1D.
+681
View File
@@ -30,6 +30,7 @@
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
namespace mfem
{
@@ -2261,6 +2262,80 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
}
}
void GridFunction::AccumulateAndCountTraceValues(
Coefficient *coeff[], VectorCoefficient *vcoeff,
Array<int> &values_counter)
{
Array<int> vdofs;
Vector vc;
values_counter.SetSize(Size());
values_counter = 0;
const int vdim = fes->GetVDim();
HostReadWrite();
for (int i = 0; i < fes->GetMesh()->GetNumFaces(); i++)
{
const FiniteElement *fe = fes->GetFaceElement(i);
const int fdof = fe->GetDof();
ElementTransformation *transf = fes->GetMesh()->GetFaceTransformation(i);
const IntegrationRule &ir = fe->GetNodes();
fes->GetFaceVDofs(i, vdofs);
for (int j = 0; j < fdof; j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
transf->SetIntPoint(&ip);
if (vcoeff) { vcoeff->Eval(vc, *transf, ip); }
for (int d = 0; d < vdim; d++)
{
if (!vcoeff && !coeff[d]) { continue; }
real_t val = vcoeff ? vc(d) : coeff[d]->Eval(*transf, ip);
int ind = vdofs[fdof*d+j];
if ( ind < 0 )
{
val = -val, ind = -1-ind;
}
if (++values_counter[ind] == 1)
{
(*this)(ind) = val;
}
else
{
(*this)(ind) += val;
}
}
}
}
}
void GridFunction::AccumulateAndCountTraceTangentValues(
VectorCoefficient &vcoeff, Array<int> &values_counter)
{
const FiniteElement *fe;
ElementTransformation *T;
Array<int> dofs;
Vector lvec;
values_counter.SetSize(Size());
values_counter = 0;
HostReadWrite();
for (int i = 0; i < fes->GetMesh()->GetNumFaces(); i++)
{
fe = fes->GetFaceElement(i);
T = fes->GetMesh()->GetFaceTransformation(i);
fes->GetFaceVDofs(i, dofs);
lvec.SetSize(fe->GetDof());
fe->Project(vcoeff, *T, lvec);
accumulate_dofs(dofs, lvec, *this, values_counter);
}
}
void GridFunction::ComputeMeans(AvgType type, Array<int> &zones_per_vdof)
{
switch (type)
@@ -2699,6 +2774,54 @@ void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
}
}
void GridFunction::ProjectTraceCoefficient(Coefficient *coeff[])
{
Array<int> values_counter;
AccumulateAndCountTraceValues(coeff, NULL, values_counter);
ComputeMeans(ARITHMETIC, values_counter);
}
void GridFunction::ProjectTraceCoefficient(VectorCoefficient &vcoeff)
{
Array<int> values_counter;
AccumulateAndCountTraceValues(NULL, &vcoeff, values_counter);
ComputeMeans(ARITHMETIC, values_counter);
}
void GridFunction::ProjectTraceCoefficientNormal(VectorCoefficient &vcoeff)
{
const FiniteElement *fe;
ElementTransformation *T;
Array<int> dofs;
int dim = vcoeff.GetVDim();
Vector vc(dim), nor(dim), lvec;
for (int i = 0; i < fes->GetMesh()->GetNumFaces(); i++)
{
fe = fes->GetFaceElement(i);
T = fes->GetMesh()->GetFaceTransformation(i);
const IntegrationRule &ir = fe->GetNodes();
lvec.SetSize(fe->GetDof());
for (int j = 0; j < ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
T->SetIntPoint(&ip);
vcoeff.Eval(vc, *T, ip);
CalcOrtho(T->Jacobian(), nor);
lvec(j) = (vc * nor);
}
fes->GetFaceVDofs(i, dofs);
SetSubVector(dofs, lvec);
}
}
void GridFunction::ProjectTraceCoefficientTangent(VectorCoefficient &vcoeff)
{
Array<int> values_counter;
AccumulateAndCountTraceTangentValues(vcoeff, values_counter);
ComputeMeans(ARITHMETIC, values_counter);
}
void GridFunction::ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
real_t rtol, int iter)
{
@@ -5117,6 +5240,103 @@ void GridFunction::GetElementBoundsAtControlPoints(const int elem,
}
}
void GridFunction::GetElementBoundsAtControlPoints(const int elem,
const PLBound &plb,
const Vector &ref_range,
const int vdim,
Vector &lower, Vector &upper,
Vector &control_pos) const
{
const FiniteElement *fe = fes->GetFE(elem);
const IntegrationRule ir_in = fe->GetNodes();
IntegrationRule ir_new(ir_in.GetNPoints());
const int dim = fes->GetMesh()->Dimension();
const L2_FECollection *l2fec = dynamic_cast<const L2_FECollection *>
(fes->FEColl());
const TensorBasisElement *tbe =
dynamic_cast<const TensorBasisElement *>(fe);
MFEM_VERIFY(tbe != NULL, "TensorBasis FiniteElement expected.");
const Array<int> &dof_map = tbe->GetDofMap();
bool lexico = (dof_map.Size() == 0);
bool bern = (tbe->GetBasisType() == BasisType::Positive);
bool h1 = (l2fec == nullptr);
Vector loc_data; // gridfunction values
// Construct an integration rule to evaluate the gridfunction in
// subinterval.
for (int i = 0; i < ir_in.GetNPoints(); i++)
{
IntegrationPoint &ip_new = ir_new.IntPoint(i);
const IntegrationPoint &ip_old =
ir_in.IntPoint((lexico || bern) ? i : dof_map[i]);
Vector ip_coord(dim);
ip_old.Get(ip_coord.GetData(), dim);
for (int d = 0; d < dim; d++)
{
ip_coord(d) = ref_range(d) +
(ref_range(dim+d) - ref_range(d)) * ip_coord(d);
}
ip_new.Set(ip_coord.GetData(), dim);
}
GetValues(elem, ir_new, loc_data, vdim);
// At this point, the loc_data contains function values ordered
// lexicographically, unless we are using Bernstein bases.
// For Bernstein, we need to project and get coefficients first.
// For bernstein, we get coefficients corresponding to these function values
if (bern)
{
int bt = 4; // BasisType::ClosedUniform
int o = fe->GetOrder();
DenseMatrix projmat;
NodalTensorFiniteElement *ntfe = nullptr;
if (dim == 1)
{
if (h1) { ntfe = new H1_SegmentElement(o, bt); }
else { ntfe = new L2_SegmentElement(o, bt); }
}
else if (dim == 2)
{
if (h1) { ntfe = new H1_QuadrilateralElement(o, bt); }
else { ntfe = new L2_QuadrilateralElement(o, bt); }
}
else if (dim == 3)
{
if (h1) { ntfe = new H1_HexahedronElement(o, bt); }
else { ntfe = new L2_HexahedronElement(o, bt); }
}
// projection matrix from H1 to Positive
ElementTransformation *eltran = fes->GetElementTransformation(elem);
fe->Project(*ntfe, *eltran, projmat);
Vector loc_data_temp(loc_data.Size());
projmat.Mult(loc_data, loc_data_temp);
for (int i = 0; i < dof_map.Size(); i++)
{
loc_data(i) = loc_data_temp(dof_map[i]);
}
if (dof_map.Size() == 0) { loc_data = loc_data_temp; }
delete ntfe;
}
// Get bounds at control points
plb.GetNDBounds(dim, loc_data, lower, upper);
// Save control point positions
int ncp = plb.GetNControlPoints();
control_pos.SetSize(dim * ncp);
const Vector control_pos_1D = plb.GetControlPoints();
for (int i = 0; i < ncp; i++)
{
for (int d = 0; d < dim; d++)
{
control_pos(i + d*ncp) =
ref_range(d) + (ref_range(dim+d)-ref_range(d))*control_pos_1D(i);
}
}
}
void GridFunction::GetElementBounds(const int elem, const PLBound &plb,
Vector &lower, Vector &upper,
const int vdim) const
@@ -5197,6 +5417,467 @@ PLBound GridFunction::GetBounds(Vector &lower, Vector &upper,
return plb;
}
struct IntervalNode
{
real_t val_min;
real_t val_max;
Array<IntervalNode *> child;
IntervalNode(real_t vmin, real_t vmax)
: val_min(vmin), val_max(vmax)
{
child.SetSize(0);
}
void AddChild(IntervalNode *ch) { child.Append(ch); }
real_t GetChildMinLower()
{
if (child.Size() == 0)
{
return val_min;
}
real_t valmin = numeric_limits<real_t>::max();
for (int i = 0; i < child.Size(); i++)
{
real_t candidate = child[i]->GetChildMinLower();
valmin = std::min(valmin, candidate);
}
return valmin;
}
real_t GetChildMinUpper()
{
if (child.Size() == 0)
{
return val_max;
}
real_t valmax = numeric_limits<real_t>::max();
for (int i = 0; i < child.Size(); i++)
{
real_t candidate = child[i]->GetChildMinUpper();
valmax = std::min(valmax, candidate);
}
return valmax;
}
real_t GetChildMaxLower()
{
if (child.Size() == 0)
{
return val_min;
}
real_t valmin = numeric_limits<real_t>::lowest();
for (int i = 0; i < child.Size(); i++)
{
real_t candidate = child[i]->GetChildMaxLower();
valmin = std::max(valmin, candidate);
}
return valmin;
}
real_t GetChildMaxUpper()
{
if (child.Size() == 0)
{
return val_max;
}
real_t valmax = numeric_limits<real_t>::lowest();
for (int i = 0; i < child.Size(); i++)
{
real_t candidate = child[i]->GetChildMaxUpper();
valmax = std::max(valmax, candidate);
}
return valmax;
}
void DeleteChildren()
{
for (int i = 0; i < child.Size(); i++)
{
child[i]->DeleteChildren();
delete child[i];
}
child.SetSize(0);
}
};
struct SearchInterval
{
Vector ref_range;
int depth;
IntervalNode *node;
SearchInterval(const Vector &ref_range_in, int d, IntervalNode *n)
: ref_range(ref_range_in), depth(d), node(n)
{ }
};
struct IntervalCompareMin
{
bool operator()(const SearchInterval *a, const SearchInterval *b) const
{
return a->node->val_min > b->node->val_min;
}
};
struct IntervalCompareMax
{
bool operator()(const SearchInterval *a, const SearchInterval *b) const
{
return a->node->val_max < b->node->val_max;
}
};
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
const int elem, const PLBound &plb, const int vdim,
const int max_depth, const real_t tol) const
{
real_t min_threshold = std::numeric_limits<real_t>::max();
return EstimateFunctionMinimum(elem, plb, vdim, max_depth, tol,
min_threshold);
}
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
const int elem, const PLBound &plb, const int vdim,
const int max_depth, const real_t tol, real_t &min_threshold) const
{
const int dim = this->FESpace()->GetMesh()->Dimension();
const int ncp = plb.GetNControlPoints();
Vector pos_range(2*dim); pos_range = 0.0;
for (int d = 0; d < dim; d++) { pos_range(d+dim) = 1.0; }
Vector lower, upper, cp_ref_loc;
GetElementBoundsAtControlPoints(elem, plb, lower, upper, vdim);
real_t val_min = lower.Min();
real_t val_max = upper.Min();
min_threshold = std::min(min_threshold, val_max);
// Pruning: if the element's lower bound is greater than the current global
// upper bound, this element cannot contain the global minimum.
if (val_min >= min_threshold)
{
return std::make_pair(val_min, val_max);
}
if (val_min == val_max || max_depth == 0)
{
min_threshold = std::min(min_threshold, val_min);
return std::make_pair(val_min, val_max);
}
real_t abs_tol = tol*(val_max-val_min);
IntervalNode *initial_node = new IntervalNode(val_min, val_max);
SearchInterval *initial_interval = new SearchInterval(pos_range, 0,
initial_node);
std::priority_queue<SearchInterval*,
std::vector<SearchInterval*>, IntervalCompareMin> pq;
pq.push(initial_interval);
real_t min_upper_bound = upper.Min();
real_t min_lower_bound = lower.Min();
while (!pq.empty())
{
SearchInterval *current = pq.top();
pq.pop();
int curr_depth = current->depth;
// Reached max depth or this interval cannot contain the global minimum
if (current->node->val_min >= min_threshold || curr_depth >= max_depth)
{
delete current;
continue;
}
min_lower_bound = initial_node->GetChildMinLower();
if (min_upper_bound - min_lower_bound < abs_tol)
{
delete current;
break;
}
// Subdivide the interval and get bounds on it
GetElementBoundsAtControlPoints(elem, plb, current->ref_range,
vdim, lower, upper, cp_ref_loc);
// process the bounds and create sub-intervals
for (int k = 0; k < (dim == 3 ? ncp-1 : 1); k++)
{
for (int j = 0; j < (dim >= 2 ? ncp-1 : 1); j++)
{
for (int i = 0; i < ncp-1; i++)
{
real_t lv = 0.0, uv = 0.0;
if (dim == 1)
{
lv = std::min(lower(i), lower(i+1));
uv = std::min(upper(i), upper(i+1));
}
else if (dim == 2)
{
lv = std::min({lower(i + j*ncp), lower((i+1) + j*ncp),
lower(i + (j+1)*ncp),
lower((i+1) + (j+1)*ncp)});
uv = std::min({upper(i + j*ncp), upper((i+1) + j*ncp),
upper(i + (j+1)*ncp),
upper((i+1) + (j+1)*ncp)});
}
else if (dim == 3)
{
lv = std::min({lower(i + j*ncp + k*ncp*ncp),
lower((i+1) + j*ncp + k*ncp*ncp),
lower(i + (j+1)*ncp + k*ncp*ncp),
lower((i+1) + (j+1)*ncp + k*ncp*ncp),
lower(i + j*ncp + (k+1)*ncp*ncp),
lower((i+1) + j*ncp + (k+1)*ncp*ncp),
lower(i + (j+1)*ncp + (k+1)*ncp*ncp),
lower((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
uv = std::min({upper(i + j*ncp + k*ncp*ncp),
upper((i+1) + j*ncp + k*ncp*ncp),
upper(i + (j+1)*ncp + k*ncp*ncp),
upper((i+1) + (j+1)*ncp + k*ncp*ncp),
upper(i + j*ncp + (k+1)*ncp*ncp),
upper((i+1) + j*ncp + (k+1)*ncp*ncp),
upper(i + (j+1)*ncp + (k+1)*ncp*ncp),
upper((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
}
IntervalNode *child_node = new IntervalNode(lv, uv);
current->node->AddChild(child_node);
if (lv < min_threshold)
{
min_upper_bound = std::min(min_upper_bound, uv);
min_threshold = std::min(min_threshold, uv);
if (curr_depth < max_depth)
{
pos_range(0) = cp_ref_loc(i);
pos_range(0+dim) = cp_ref_loc(i+1);
if (dim >= 2)
{
pos_range(1) = cp_ref_loc(ncp + j);
pos_range(1+dim) = cp_ref_loc(ncp + j+1);
}
if (dim == 3)
{
pos_range(2) = cp_ref_loc(2*ncp + k);
pos_range(2+dim) = cp_ref_loc(2*ncp + k+1);
}
SearchInterval *child_interval =
new SearchInterval(pos_range, curr_depth + 1,
child_node);
pq.push(child_interval);
}
}
}
}
}
delete current;
}
// clean up remaining intervals in queue
while (!pq.empty())
{
delete pq.top();
pq.pop();
}
min_lower_bound = initial_node->GetChildMinLower();
initial_node->DeleteChildren();
delete initial_node;
min_threshold = std::min(min_threshold, min_lower_bound);
return std::make_pair(min_lower_bound, min_upper_bound);
}
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
const int elem, const PLBound &plb, const int vdim,
const int max_depth, const real_t tol) const
{
real_t max_threshold = std::numeric_limits<real_t>::lowest();
return EstimateFunctionMaximum(elem, plb, vdim, max_depth, tol,
max_threshold);
}
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
const int elem, const PLBound &plb, const int vdim,
const int max_depth, const real_t tol, real_t &max_threshold) const
{
const int dim = this->FESpace()->GetMesh()->Dimension();
const int ncp = plb.GetNControlPoints();
Vector pos_range(2*dim); pos_range = 0.0;
for (int d = 0; d < dim; d++) { pos_range(d+dim) = 1.0; }
Vector lower, upper, cp_ref_loc;
GetElementBoundsAtControlPoints(elem, plb, lower, upper, vdim);
real_t val_min = lower.Max();
real_t val_max = upper.Max();
max_threshold = std::max(max_threshold, val_min);
// Pruning: if the element's upper bound is less than the current global
// lower bound, this element cannot contain the global maximum.
if (val_max <= max_threshold)
{
return std::make_pair(val_min, val_max);
}
if (val_min == val_max || max_depth == 0)
{
max_threshold = std::max(max_threshold, val_max);
return std::make_pair(val_min, val_max);
}
real_t abs_tol = tol*(val_max-val_min);
IntervalNode *initial_node = new IntervalNode(val_min, val_max);
SearchInterval *initial_interval = new SearchInterval(pos_range, 0,
initial_node);
std::priority_queue<SearchInterval*,
std::vector<SearchInterval*>, IntervalCompareMax> pq;
pq.push(initial_interval);
real_t max_lower_bound = val_min;
real_t max_upper_bound = val_max;
while (!pq.empty())
{
SearchInterval *current = pq.top();
pq.pop();
int curr_depth = current->depth;
// Reached max depth or this interval cannot contain the global maximum.
if (current->node->val_max <= max_threshold || curr_depth >= max_depth)
{
delete current;
continue;
}
max_upper_bound = initial_node->GetChildMaxUpper();
if (max_upper_bound - max_lower_bound < abs_tol)
{
delete current;
break;
}
// Subdivide the interval and get bounds on it
GetElementBoundsAtControlPoints(elem, plb, current->ref_range,
vdim, lower, upper, cp_ref_loc);
// process the bounds and create sub-intervals
for (int k = 0; k < (dim == 3 ? ncp-1 : 1); k++)
{
for (int j = 0; j < (dim >= 2 ? ncp-1 : 1); j++)
{
for (int i = 0; i < ncp-1; i++)
{
real_t lv = 0.0, uv = 0.0;
if (dim == 1)
{
lv = std::max(lower(i), lower(i+1));
uv = std::max(upper(i), upper(i+1));
}
else if (dim == 2)
{
lv = std::max({lower(i + j*ncp), lower((i+1) + j*ncp),
lower(i + (j+1)*ncp),
lower((i+1) + (j+1)*ncp)});
uv = std::max({upper(i + j*ncp), upper((i+1) + j*ncp),
upper(i + (j+1)*ncp),
upper((i+1) + (j+1)*ncp)});
}
else if (dim == 3)
{
lv = std::max({lower(i + j*ncp + k*ncp*ncp),
lower((i+1) + j*ncp + k*ncp*ncp),
lower(i + (j+1)*ncp + k*ncp*ncp),
lower((i+1) + (j+1)*ncp + k*ncp*ncp),
lower(i + j*ncp + (k+1)*ncp*ncp),
lower((i+1) + j*ncp + (k+1)*ncp*ncp),
lower(i + (j+1)*ncp + (k+1)*ncp*ncp),
lower((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
uv = std::max({upper(i + j*ncp + k*ncp*ncp),
upper((i+1) + j*ncp + k*ncp*ncp),
upper(i + (j+1)*ncp + k*ncp*ncp),
upper((i+1) + (j+1)*ncp + k*ncp*ncp),
upper(i + j*ncp + (k+1)*ncp*ncp),
upper((i+1) + j*ncp + (k+1)*ncp*ncp),
upper(i + (j+1)*ncp + (k+1)*ncp*ncp),
upper((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
}
IntervalNode *child_node = new IntervalNode(lv, uv);
current->node->AddChild(child_node);
if (uv > max_threshold)
{
max_lower_bound = std::max(max_lower_bound, lv);
max_threshold = std::max(max_threshold, lv);
if (curr_depth < max_depth)
{
pos_range(0) = cp_ref_loc(i);
pos_range(0+dim) = cp_ref_loc(i+1);
if (dim >= 2)
{
pos_range(1) = cp_ref_loc(ncp + j);
pos_range(1+dim) = cp_ref_loc(ncp + j+1);
}
if (dim == 3)
{
pos_range(2) = cp_ref_loc(2*ncp + k);
pos_range(2+dim) = cp_ref_loc(2*ncp + k+1);
}
SearchInterval *child_interval =
new SearchInterval(pos_range, curr_depth + 1,
child_node);
pq.push(child_interval);
}
}
}
}
}
delete current;
}
// clean up remaining intervals in queue
while (!pq.empty())
{
delete pq.top();
pq.pop();
}
max_upper_bound = initial_node->GetChildMaxUpper();
initial_node->DeleteChildren();
delete initial_node;
max_threshold = std::max(max_threshold, max_upper_bound);
return std::make_pair(max_lower_bound, max_upper_bound);
}
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
const int vdim, const PLBound &plb, const int max_depth,
const real_t tol) const
{
real_t global_min_lower = std::numeric_limits<real_t>::max();
real_t global_min_upper = std::numeric_limits<real_t>::max();
for (int i = 0; i < fes->GetNE(); i++)
{
std::pair<real_t, real_t> min_pair =
EstimateFunctionMinimum(i, plb, vdim, max_depth, tol,
global_min_lower);
global_min_upper = std::min(global_min_upper, min_pair.second);
}
return std::make_pair(global_min_lower, global_min_upper);
}
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
const int vdim, const PLBound &plb, const int max_depth,
const real_t tol) const
{
real_t global_max_lower = std::numeric_limits<real_t>::lowest();
real_t global_max_upper = std::numeric_limits<real_t>::lowest();
for (int i = 0; i < fes->GetNE(); i++)
{
std::pair<real_t, real_t> max_pair =
EstimateFunctionMaximum(i, plb, vdim, max_depth, tol,
global_max_upper);
global_max_lower = std::max(global_max_lower, max_pair.first);
}
return std::make_pair(global_max_lower, global_max_upper);
}
}
+139 -7
View File
@@ -557,6 +557,13 @@ protected:
const Array<int> &bdr_attr,
Array<int> &values_counter);
void AccumulateAndCountTraceValues(Coefficient *coeff[],
VectorCoefficient *vcoeff,
Array<int> &values_counter);
void AccumulateAndCountTraceTangentValues(VectorCoefficient &vcoeff,
Array<int> &values_counter);
// Complete the computation of averages; called e.g. after
// AccumulateAndCountZones().
void ComputeMeans(AvgType type, Array<int> &zones_per_vdof);
@@ -564,6 +571,70 @@ protected:
/// P-refinement version of Update().
void UpdatePRef();
/** @brief Estimate the minimum value of the GridFunction in element @a elem
* if it is below a certain @a min_threshold.
*
* @details For a given element \p elem and grid function component \p vdim
* an estimate of the function minimum is the minimum of the piecewise
* linear lower bound obtained using the given PLBound object. The actual
* minimum is between [minimum lower bound, minimum upper bound]. We
* improve the estimate of the function minimum by recursively
* subdividing the interval with the lowest lower bound, and computing
* bounds on the sub-intervals.
* This process continues until (i) the maximum recursion depth is reached
* or (ii) the difference between the minimum upper bound and minimum lower
* bound is less than a certain tolerance (\p tol * [initial maximum
* upper bound - initial minimum lower bound]).
* The function also terminates if the lowest minima estimate is found
* to be above the given threshold \p min_threshold. This is useful when
* we are interested in computing the global minimum of the function
* over all elements. In this case we can reject elements where the lowest
* bound is above the current global minimum. In case the function
* minimum on the element is below the global minimum, we update
* \p min_threshold.
*
* We return a pair of values that bracket the actual minimum, i.e.
* [min_lower_bound, min_upper_bound].
*/
std::pair<real_t,real_t> EstimateFunctionMinimum(const int elem,
const PLBound &plb,
const int vdim,
const int max_depth,
const real_t tol,
real_t &min_threshold)const;
/** @brief Estimate the maximum value of the GridFunction in element @a elem
* if it is below a certain @a max_threshold.
*
* @details For a given element \p elem and grid function component \p vdim
* an estimate of the function maximum is the maximum of the piecewise
* linear upper bound obtained using the given PLBound object. The actual
* maximum is between [maximum lower bound, maximum upper bound]. We
* improve the estimate of the function maximum by recursively
* subdividing the interval with the highest upper bound, and computing
* bounds on the sub-intervals.
* This process continues until (i) the maximum recursion depth is reached
* or (ii) the difference between the maximum upper bound and maximum lower
* bound is less than a certain tolerance (\p tol * [initial maximum
* upper bound - initial maximum lower bound]).
* The function also terminates if the highest maxima estimate is found
* to be below the given threshold \p max_threshold. This is useful when
* we are interested in computing the global maximum of the function
* over all elements. In this case we can reject elements where the upper
* bound is below the current global maximum. In case the function
* maximum on the element is above the global maximum, we update
* \p max_threshold.
*
* We return a pair of values that bracket the actual maximum, i.e.
* [max_lower_bound, max_upper_bound].
*/
std::pair<real_t,real_t> EstimateFunctionMaximum(const int elem,
const PLBound &plb,
const int vdim,
const int max_depth,
const real_t tol,
real_t &max_threshold)const;
public:
/** @brief For each vdof, counts how many elements contain the vdof,
as containment is determined by FiniteElementSpace::GetElementVDofs(). */
@@ -578,6 +649,21 @@ public:
ProjectBdrCoefficient(&coeff_p, attr);
}
/// Project a Coefficient on a GridFunction defined on H1 trace space
void ProjectTraceCoefficient(Coefficient *coeff[]);
void ProjectTraceCoefficient(Coefficient &coeff)
{
Coefficient *coeff_p = &coeff;
ProjectTraceCoefficient(&coeff_p);
}
/// Project a VectorCoefficient on a GridFunction defined on a Vector H1 trace space
void ProjectTraceCoefficient(VectorCoefficient &vcoeff);
/// Project a VectorCoefficient on a GridFunction defined on a RT trace space
void ProjectTraceCoefficientNormal(VectorCoefficient &vcoeff);
/// Project a VectorCoefficient on a GridFunction defined on a ND trace space
void ProjectTraceCoefficientTangent(VectorCoefficient &vcoeff);
/** @brief Project a VectorCoefficient on the GridFunction, modifying only
DOFs on the boundary associated with the boundary attributes marked in
the @a attr array. */
@@ -1662,21 +1748,21 @@ public:
*/
///@{
/// Computes the \ref PLBound for the gridfunction with number of control
/// points based on @a ref_factor, and returns the overall bounds for each
/// vdim (across all elements) in @b lower and @b upper. We also return the
/// points based on \p ref_factor, and returns the overall bounds for each
/// vdim (across all elements) in \p lower and \p upper. We also return the
/// PLBound object used to compute the bounds.
/// We compute the bounds for each vdim if @a vdim < 1.
/// We compute the bounds for each vdim if \p vdim < 1.
/// Note: For most cases, this method/interface will be sufficient.
virtual PLBound GetBounds(Vector &lower, Vector &upper,
const int ref_factor=1, const int vdim=-1) const;
/// Computes the \ref PLBound for the gridfunction with number of control
/// points based on @a ref_factor, and returns the bounds for each element
/// ordered byVDim:
/// points based on \p ref_factor, and returns the bounds for each element
/// ordered byNodes:
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}. We also return the
/// PLBound object used to compute the bounds.
/// We compute the bounds for each vdim if @a vdim < 1.
/// We compute the bounds for each vdim if \p vdim < 1.
PLBound GetElementBounds(Vector &lower, Vector &upper,
const int ref_factor=1, const int vdim=-1) const;
@@ -1687,6 +1773,18 @@ public:
Vector &lower, Vector &upper,
const int vdim = -1) const;
/** @brief Gets the bounds on given reference range inside an element.
*
* @details @a ref_range is a vector of size 2*dim that specifies the
* lower and upper limits in each dimension of the reference element.
* For example, in 2D, ref_range = [rmin, smin, rmax, smax].
*/
void GetElementBoundsAtControlPoints(const int elem, const PLBound &plb,
const Vector &ref_range,
const int vdim,
Vector &lower, Vector &upper,
Vector &control_pos) const;
/// Compute bounds on the grid function for the given element.
/// The bounds are stored in @b lower and @b upper.
void GetElementBounds(const int elem, const PLBound &plb,
@@ -1694,11 +1792,45 @@ public:
const int vdim = -1) const;
/// Compute bounds on the grid function for all the elements. The bounds
/// are returned in @b lower and @b upper, ordered byVDim:
/// are returned in @b lower and @b upper, ordered byNodes:
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}
void GetElementBounds(const PLBound &plb, Vector &lower, Vector &upper,
const int vdim=-1) const;
/** @brief Estimate the minimum value of the GridFunction in element @a elem.
*
* @details See the protected version of EstimateFunctionMinimum for
* details.
*/
std::pair<real_t, real_t> EstimateFunctionMinimum(const int elem,
const PLBound &plb,
const int vdim,
const int max_depth,
const real_t tol) const;
/** @brief Estimate the minimum value of the GridFunction in element @a elem.
*
* @details See the protected version of EstimateFunctionMaximum for
* details.
*/
std::pair<real_t, real_t> EstimateFunctionMaximum(const int elem,
const PLBound &plb,
const int vdim,
const int max_depth,
const real_t tol) const;
/** @brief Estimate the GridFunction minimum across all elements. */
virtual std::pair<real_t,real_t> EstimateFunctionMinimum(const int vdim,
const PLBound &plb,
const int max_depth,
const real_t tol) const;
/** @brief Estimate the GridFunction maximum across all elements. */
virtual std::pair<real_t,real_t> EstimateFunctionMaximum(const int vdim,
const PLBound &plb,
const int max_depth,
const real_t tol) const;
///@}
/// Destroys grid function.
+3 -3
View File
@@ -171,15 +171,15 @@ template<int DIM, int T_SDIM, int T_D1D, int T_Q1D>
VectorDiffusionIntegrator::ApplyKernelType
VectorDiffusionIntegrator::ApplyPAKernels::Kernel()
{
if (DIM == 2)
if constexpr (DIM == 2)
{
return internal::SmemPAVectorDiffusionApply2D<T_SDIM, T_D1D, T_Q1D>;
}
else if (DIM == 3)
else if constexpr (DIM == 3)
{
return internal::SmemPAVectorDiffusionApply3D<T_SDIM, T_D1D, T_Q1D>;
}
else { MFEM_ABORT("Unsupported kernel"); }
MFEM_ABORT("Unsupported kernel");
}
inline VectorDiffusionIntegrator::ApplyKernelType
+3 -3
View File
@@ -182,15 +182,15 @@ template<int DIM, int T_D1D, int T_Q1D>
VectorMassIntegrator::VectorMassAddMultPAType
VectorMassIntegrator::VectorMassAddMultPA::Kernel()
{
if (DIM == 2)
if constexpr (DIM == 2)
{
return internal::SmemPAVectorMassApply2D<T_D1D,T_Q1D>;
}
else if (DIM == 3)
else if constexpr (DIM == 3)
{
return internal::SmemPAVectorMassApply3D<T_D1D, T_Q1D>;
}
else { MFEM_ABORT("Unsupported kernel"); }
MFEM_ABORT("Unsupported kernel");
}
inline VectorMassIntegrator::VectorMassAddMultPAType
+6 -10
View File
@@ -301,18 +301,14 @@ template <int DIM, int T_D1D, int T_Q1D>
DomainLFIntegrator::AssembleKernelType
DomainLFIntegrator::AssembleKernels::Kernel()
{
switch (DIM)
{
case 1:
return DLFEvalAssemble1D<T_D1D, T_Q1D>;
case 2:
return DLFEvalAssemble2D<T_D1D, T_Q1D>;
case 3:
return DLFEvalAssemble3D<T_D1D, T_Q1D>;
}
if constexpr (DIM == 1) { return DLFEvalAssemble1D<T_D1D, T_Q1D>; }
if constexpr (DIM == 2) { return DLFEvalAssemble2D<T_D1D, T_Q1D>; }
if constexpr (DIM == 3) { return DLFEvalAssemble3D<T_D1D, T_Q1D>; }
MFEM_ABORT("");
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
#endif
#endif // MFEM_LININTEG_DOMAIN_KERNELS_HPP
+962 -2
View File
@@ -14,6 +14,18 @@
// Acknowledgment: Some of the high-precision triangular and tetrahedral
// quadrature rules below were obtained from the Encyclopaedia of Cubature
// Formulas at http://nines.cs.kuleuven.be/research/ecf/ecf.html
//
// Positive-weight simplex quadrature rules from:
//
// [1] F.D. Witherden, P.E. Vincent, "On the identification of symmetric
// quadrature rules for finite element methods", Computers & Mathematics
// with Applications, 69(10):1232-1241, 2015.
// Used for: triangles (d=0-20), tetrahedra (d=0-13).
//
// [2] G. Chuluunbaatar, O. Chuluunbaatar, A.A. Gusev, S.I. Vinitsky,
// "PI-type fully symmetric quadrature rules on the 3-,...,6-simplexes",
// Computers & Mathematics with Applications, 124:89-97, 2022.
// Used for: tetrahedra (d=14-20).
#include "fem.hpp"
#include "../mesh/nurbs.hpp"
@@ -959,8 +971,8 @@ IntegrationRules IntRules(0, Quadrature1D::GaussLegendre);
IntegrationRules RefinedIntRules(1, Quadrature1D::GaussLegendre);
IntegrationRules::IntegrationRules(int ref, int type)
: quad_type(type)
IntegrationRules::IntegrationRules(int ref, int type, SimplexQuadrature stype)
: quad_type(type), simplex_type(stype)
{
refined = ref;
@@ -1253,6 +1265,11 @@ IntegrationRule *IntegrationRules::SegmentIntegrationRule(int Order)
// Integration rules for reference triangle {[0,0],[1,0],[0,1]}
IntegrationRule *IntegrationRules::TriangleIntegrationRule(int Order)
{
if (simplex_type == SimplexQuadrature::PositiveWeights)
{
return TrianglePositiveIntegrationRule(Order);
}
IntegrationRule *ir = NULL;
// Note: Set TriangleIntRules[*] to ir only *after* ir is fully constructed.
// This is needed in multithreaded environment.
@@ -1675,6 +1692,11 @@ IntegrationRule *IntegrationRules::SquareIntegrationRule(int Order)
{[0,0,0],[1,0,0],[0,1,0],[0,0,1]} */
IntegrationRule *IntegrationRules::TetrahedronIntegrationRule(int Order)
{
if (simplex_type == SimplexQuadrature::PositiveWeights)
{
return TetrahedronPositiveIntegrationRule(Order);
}
IntegrationRule *ir;
// Note: Set TetrahedronIntRules[*] to ir only *after* ir is fully
// constructed. This is needed in multithreaded environment.
@@ -1788,6 +1810,944 @@ IntegrationRule *IntegrationRules::TetrahedronIntegrationRule(int Order)
}
}
// Triangle rules from Witherden & Vincent [1].
// Orbit data from PyFR (https://pyfr.org), licensed under CC-BY 4.0.
IntegrationRule *IntegrationRules::TrianglePositiveIntegrationRule(int Order)
{
IntegrationRule *ir = NULL;
switch (Order)
{
case 0:
case 1:
ir = new IntegrationRule(1);
ir->AddTriMidPoint(0, 5.00000000000000000000e-01);
ir->SetOrder(1);
TriangleIntRules[0] =
TriangleIntRules[1] = ir;
return ir;
case 2:
ir = new IntegrationRule(3);
ir->AddTriPoints3(0, 1.66666666666666740682e-01, 1.66666666666666657415e-01);
ir->SetOrder(2);
TriangleIntRules[2] = ir;
return ir;
case 3:
case 4:
ir = new IntegrationRule(6);
ir->AddTriPoints3(0, 4.45948490915964890213e-01, 1.11690794839005735906e-01);
ir->AddTriPoints3(3, 9.15762135097707430376e-02, 5.49758718276609353870e-02);
ir->SetOrder(4);
TriangleIntRules[3] =
TriangleIntRules[4] = ir;
return ir;
case 5:
ir = new IntegrationRule(7);
ir->AddTriMidPoint(0, 1.12500000000000002776e-01);
ir->AddTriPoints3(1, 1.01286507323456342888e-01, 6.29695902724135697648e-02);
ir->AddTriPoints3(4, 4.70142064105115109474e-01, 6.61970763942530959767e-02);
ir->SetOrder(5);
TriangleIntRules[5] = ir;
return ir;
case 6:
ir = new IntegrationRule(12);
ir->AddTriPoints3(0, 6.30890144915022266225e-02, 2.54224531851034094010e-02);
ir->AddTriPoints3(3, 2.49286745170910428726e-01, 5.83931378631896841336e-02);
ir->AddTriPoints6(6, 6.36502499121398668258e-01, 3.10352451033784393353e-01,
4.14255378091867854096e-02);
ir->SetOrder(6);
TriangleIntRules[6] = ir;
return ir;
case 7:
ir = new IntegrationRule(15);
ir->AddTriPoints3(0, 3.37306485545878498300e-02, 8.27252505539606552976e-03);
ir->AddTriPoints3(3, 2.41577382595403566956e-01, 6.39720856150777922311e-02);
ir->AddTriPoints3(6, 4.74309692504718327655e-01, 3.85433230929930342734e-02);
ir->AddTriPoints6(9, 7.54280040550053154647e-01, 1.98683314797351684433e-01,
2.79393664515998896292e-02);
ir->SetOrder(7);
TriangleIntRules[7] = ir;
return ir;
case 8:
ir = new IntegrationRule(16);
ir->AddTriMidPoint(0, 7.21578038388935860681e-02);
ir->AddTriPoints3(1, 4.59292588292723236165e-01, 4.75458171336423096598e-02);
ir->AddTriPoints3(4, 1.70569307751760268488e-01, 5.16086852673591223173e-02);
ir->AddTriPoints3(7, 5.05472283170309566458e-02, 1.62292488115990396480e-02);
ir->AddTriPoints6(10, 7.28492392955404244326e-01, 2.63112829634638112353e-01,
1.36151570872174963733e-02);
ir->SetOrder(8);
TriangleIntRules[8] = ir;
return ir;
case 9:
ir = new IntegrationRule(19);
ir->AddTriMidPoint(0, 4.85678981413994181882e-02);
ir->AddTriPoints3(1, 4.37089591492936690997e-01, 3.89137705023871391385e-02);
ir->AddTriPoints3(4, 1.88203535619032802373e-01, 3.98238694636051243636e-02);
ir->AddTriPoints3(7, 4.89682519198737620236e-01, 1.56673501135695357467e-02);
ir->AddTriPoints3(10, 4.47295133944527467662e-02, 1.27888378293490156262e-02);
ir->AddTriPoints6(13, 7.41198598784498008385e-01, 2.21962989160765733487e-01,
2.16417696886446880855e-02);
ir->SetOrder(9);
TriangleIntRules[9] = ir;
return ir;
case 10:
ir = new IntegrationRule(25);
ir->AddTriMidPoint(0, 4.08716645731429864541e-02);
ir->AddTriPoints3(1, 3.20553732169435168231e-02, 6.67648440657478327992e-03);
ir->AddTriPoints3(4, 1.42161101056564431744e-01, 2.29789818023723654838e-02);
ir->AddTriPoints6(7, 5.30054118927343997925e-01, 3.21812995288835446139e-01,
3.19524531982120219009e-02);
ir->AddTriPoints6(13, 6.01233328683459244957e-01, 3.69146781827810910315e-01,
1.70923240814797143539e-02);
ir->AddTriPoints6(19, 8.07930600922879049719e-01, 1.63701733737182442141e-01,
1.26488788536441923438e-02);
ir->SetOrder(10);
TriangleIntRules[10] = ir;
return ir;
case 11:
ir = new IntegrationRule(28);
ir->AddTriMidPoint(0, 4.28805898661121093207e-02);
ir->AddTriPoints3(1, 2.84854176143718995640e-02, 5.21593525644734826857e-03);
ir->AddTriPoints3(4, 2.10219956703178278978e-01, 3.52578420558582877886e-02);
ir->AddTriPoints3(7, 1.02635482712246428605e-01, 1.93153796185096607307e-02);
ir->AddTriPoints3(10, 4.95891900965890919384e-01, 8.30313652729268436570e-03);
ir->AddTriPoints3(13, 4.38465926764352253997e-01, 3.36580770397341480504e-02);
ir->AddTriPoints6(16, 8.43349783661853091843e-01, 1.49324788652082374174e-01,
5.14514478647663895533e-03);
ir->AddTriPoints6(22, 6.64408374196864159877e-01, 2.89581125637705882880e-01,
2.01662383202502772106e-02);
ir->SetOrder(11);
TriangleIntRules[11] = ir;
return ir;
case 12:
ir = new IntegrationRule(33);
ir->AddTriPoints3(0, 4.88203750945541581352e-01, 1.21334190407260157640e-02);
ir->AddTriPoints3(3, 1.09257827659354322947e-01, 1.42430260344387719235e-02);
ir->AddTriPoints3(6, 2.71462507014926135440e-01, 3.12706065979513822550e-02);
ir->AddTriPoints3(9, 2.46463634363356387524e-02, 3.96582125498681943576e-03);
ir->AddTriPoints3(12, 4.40111648658593201944e-01, 2.49591674640304711508e-02);
ir->AddTriPoints6(15, 6.85310163906391878186e-01, 2.91655679738340944951e-01,
1.08917925193037796322e-02);
ir->AddTriPoints6(21, 6.28249751683556123538e-01, 2.55454228638517299999e-01,
2.16136818297071042760e-02);
ir->AddTriPoints6(27, 8.51337792510240110033e-01, 1.27279717233589384495e-01,
7.54183878825571887144e-03);
ir->SetOrder(12);
TriangleIntRules[12] = ir;
return ir;
case 13:
ir = new IntegrationRule(37);
ir->AddTriMidPoint(0, 3.39800182934158201409e-02);
ir->AddTriPoints3(1, 4.89076946452539351728e-01, 1.19972009644473652512e-02);
ir->AddTriPoints3(4, 2.21372286291832920391e-01, 2.91392425595999905730e-02);
ir->AddTriPoints3(7, 4.26941414259800422482e-01, 2.78009837652266646180e-02);
ir->AddTriPoints3(10, 2.15096811088433259584e-02, 3.02616855176958583773e-03);
ir->AddTriPoints6(13, 7.48507115899952224503e-01, 1.63597401067850478640e-01,
1.20895199057969096601e-02);
ir->AddTriPoints6(19, 8.64707770295442768038e-01, 1.10922042803463405392e-01,
7.48270055258283377231e-03);
ir->AddTriPoints6(25, 6.23545995553675513889e-01, 3.08441760892117777804e-01,
1.73206380704241866275e-02);
ir->AddTriPoints6(31, 7.22357793124188019007e-01, 2.72515817773429591675e-01,
4.79534050177163155559e-03);
ir->SetOrder(13);
TriangleIntRules[13] = ir;
return ir;
case 14:
ir = new IntegrationRule(42);
ir->AddTriPoints3(0, 1.77205532412543442788e-01, 2.10812943684965080349e-02);
ir->AddTriPoints3(3, 4.17644719340453940415e-01, 1.63941767720626740967e-02);
ir->AddTriPoints3(6, 6.17998830908725871325e-02, 7.21684983488833382143e-03);
ir->AddTriPoints3(9, 4.88963910362178677538e-01, 1.09417906847144447147e-02);
ir->AddTriPoints3(12, 2.73477528308838646609e-01, 2.58870522536457925433e-02);
ir->AddTriPoints3(15, 1.93909612487010996063e-02, 2.46170180120004094063e-03);
ir->AddTriPoints6(18, 6.86980167808087793802e-01, 2.98372882136257788765e-01,
7.21815405676692022074e-03);
ir->AddTriPoints6(24, 7.70608554774996457049e-01, 1.72266687821355679588e-01,
1.23328766062818367955e-02);
ir->AddTriPoints6(30, 5.70222290846683188548e-01, 3.36861459796344964168e-01,
1.92857553935303419057e-02);
ir->AddTriPoints6(36, 8.79757171370171064950e-01, 1.18974497696956893478e-01,
2.50511441925033596229e-03);
ir->SetOrder(14);
TriangleIntRules[14] = ir;
return ir;
case 15:
ir = new IntegrationRule(49);
ir->AddTriMidPoint(0, 2.21676936910920364954e-02);
ir->AddTriPoints3(1, 4.05362214133975495844e-01, 2.13568907857302828224e-02);
ir->AddTriPoints3(4, 7.01735528999860580512e-02, 8.22236878131258133728e-03);
ir->AddTriPoints3(7, 4.74170681438019769871e-01, 8.69807400038170690226e-03);
ir->AddTriPoints3(10, 2.26378713420349653163e-01, 2.33916808643548149171e-02);
ir->AddTriPoints3(13, 4.94996956769126195130e-01, 4.78692309123004283711e-03);
ir->AddTriPoints3(16, 1.58117262509887002153e-02, 1.48038731895268772104e-03);
ir->AddTriPoints6(19, 6.66975644801868106093e-01, 3.14648242812450851247e-01,
7.80128641528798211224e-03);
ir->AddTriPoints6(25, 9.19912157726236134891e-01, 7.09486052364554087291e-02,
2.01492668600904969653e-03);
ir->AddTriPoints6(31, 7.15222356931450642392e-01, 1.90535589476393929509e-01,
1.43602934626006709801e-02);
ir->AddTriPoints6(37, 8.13292641049419229304e-01, 1.68068645222414381202e-01,
5.83631059078792285844e-03);
ir->AddTriPoints6(43, 5.65252664877114230357e-01, 3.38950611475277163720e-01,
1.56577381424846430458e-02);
ir->SetOrder(15);
TriangleIntRules[15] = ir;
return ir;
case 16:
ir = new IntegrationRule(55);
ir->AddTriMidPoint(0, 2.26322830369093952463e-02);
ir->AddTriPoints3(1, 2.45990070467141719313e-01, 2.05464615718494759966e-02);
ir->AddTriPoints3(4, 4.15584896885420551627e-01, 2.03559166562126796218e-02);
ir->AddTriPoints3(7, 8.53555665867003487968e-02, 7.39081734511220188322e-03);
ir->AddTriPoints3(10, 1.61918644191271221544e-01, 1.47092048494940497855e-02);
ir->AddTriPoints3(13, 5.00000000000000000000e-01, 2.20927315607528452004e-03);
ir->AddTriPoints3(16, 4.75280727545942083268e-01, 1.29871666491385793357e-02);
ir->AddTriPoints6(19, 7.54170061444767725334e-01, 1.91074763640529221576e-01,
9.46913623220784969603e-03);
ir->AddTriPoints6(25, 9.68244368030958701965e-01, 2.32034277688137335893e-02,
8.27233357417524097638e-04);
ir->AddTriPoints6(31, 6.49303698245446425652e-01, 3.31764523474147643434e-01,
7.50430089214290316213e-03);
ir->AddTriPoints6(37, 9.00273703270429548340e-01, 8.06961669858730079596e-02,
3.97379696669624901673e-03);
ir->AddTriPoints6(43, 5.89148840564247877616e-01, 3.08244969196354023921e-01,
1.59918050396850343342e-02);
ir->AddTriPoints6(49, 8.06621867499395683865e-01, 1.87441782483782071189e-01,
2.69559355842440570919e-03);
ir->SetOrder(16);
TriangleIntRules[16] = ir;
return ir;
case 17:
ir = new IntegrationRule(60);
ir->AddTriPoints3(0, 4.17103444361599295931e-01, 1.36554632640510532210e-02);
ir->AddTriPoints3(3, 1.47554916607539610141e-02, 1.38694378881882109979e-03);
ir->AddTriPoints3(6, 4.65597871618890324363e-01, 1.25097254752486782697e-02);
ir->AddTriPoints3(9, 1.80358116266370605008e-01, 1.31563152940089925225e-02);
ir->AddTriPoints3(12, 6.66540634795969033632e-02, 6.22950040115272107855e-03);
ir->AddTriPoints3(15, 2.85706502436586629035e-01, 1.88581185763976415248e-02);
ir->AddTriPoints6(18, 8.24790070165088096132e-01, 1.59192287472792681768e-01,
3.98915010296479674579e-03);
ir->AddTriPoints6(24, 6.26369030386452196879e-01, 3.06281591746186521164e-01,
1.12438862733455335191e-02);
ir->AddTriPoints6(30, 5.71294867944684092720e-01, 4.15475459295228999324e-01,
5.19921997791976831654e-03);
ir->AddTriPoints6(36, 7.53235145936458128091e-01, 1.68722513495259462957e-01,
1.02789491602272593102e-02);
ir->AddTriPoints6(42, 7.15072259110642427515e-01, 2.71791870055354878311e-01,
4.34610725050059605590e-03);
ir->AddTriPoints6(48, 9.15919353297816929427e-01, 7.25054707990024915887e-02,
2.29217420086793351869e-03);
ir->AddTriPoints6(54, 5.43275579596159796658e-01, 2.99218942476970228839e-01,
1.30858129676684944304e-02);
ir->SetOrder(17);
TriangleIntRules[17] = ir;
return ir;
case 18:
ir = new IntegrationRule(67);
ir->AddTriMidPoint(0, 1.81778676507133342410e-02);
ir->AddTriPoints3(1, 3.99955628067576229867e-01, 1.66522350166950668104e-02);
ir->AddTriPoints3(4, 4.87580301574869645620e-01, 6.02332381699985548729e-03);
ir->AddTriPoints3(7, 4.61809506406449243876e-01, 9.47458575338943308208e-03);
ir->AddTriPoints3(10, 2.42264702514271956790e-01, 1.82375447044718190515e-02);
ir->AddTriPoints3(13, 3.88302560886856218403e-02, 3.56466300985948522304e-03);
ir->AddTriPoints3(16, 9.19477421216432500017e-02, 8.27957997600162372287e-03);
ir->AddTriPoints6(19, 7.70372376214675247397e-01, 1.83822707925463957324e-01,
6.87980811747110256732e-03);
ir->AddTriPoints6(25, 6.70953985194234547862e-01, 2.06349257433837918185e-01,
1.18909554500764153007e-02);
ir->AddTriPoints6(31, 6.00418954634256873959e-01, 3.95683434332269712286e-01,
2.26526725112853252742e-03);
ir->AddTriPoints6(37, 8.78342189467521738955e-01, 1.08195793791033278985e-01,
3.42005505980359086893e-03);
ir->AddTriPoints6(43, 6.39988092004714625993e-01, 3.19751624525377309283e-01,
8.87374455101020212511e-03);
ir->AddTriPoints6(49, 7.58929479855198430016e-01, 2.35772184958191743931e-01,
2.50533043728986106261e-03);
ir->AddTriPoints6(55, 9.72360728962795684005e-01, 2.70909109951620319379e-02,
6.11474063480544911126e-04);
ir->AddTriPoints6(61, 5.45918775386194599086e-01, 3.33493529449880754534e-01,
1.27410876559122202695e-02);
ir->SetOrder(18);
TriangleIntRules[18] = ir;
return ir;
case 19:
ir = new IntegrationRule(73);
ir->AddTriMidPoint(0, 1.72346988520061666916e-02);
ir->AddTriPoints3(1, 5.25238903512089683190e-02, 3.55462829889906543543e-03);
ir->AddTriPoints3(4, 4.92512675041336889237e-01, 5.16087757147214078873e-03);
ir->AddTriPoints3(7, 1.11448873323021391268e-01, 7.61717554650914990128e-03);
ir->AddTriPoints3(10, 4.59194201039543670184e-01, 1.14917950133708035576e-02);
ir->AddTriPoints3(13, 4.03969722551901222474e-01, 1.57687674465774863020e-02);
ir->AddTriPoints3(16, 1.78170104781764315760e-01, 1.23259574240954274116e-02);
ir->AddTriPoints3(19, 1.16394611837894457196e-02, 8.82661388221423837477e-04);
ir->AddTriPoints3(22, 2.55161632913607716588e-01, 1.58765096830015377261e-02);
ir->AddTriPoints6(25, 8.30156464400275351245e-01, 1.30697676268032414448e-01,
4.84774224342752330097e-03);
ir->AddTriPoints6(31, 5.59369805720300927732e-01, 3.11317629809541251973e-01,
1.31731609886953666272e-02);
ir->AddTriPoints6(37, 6.33313293128784149388e-01, 3.64617780974611060962e-01,
1.64103827591790965915e-03);
ir->AddTriPoints6(43, 7.04004819966042139079e-01, 2.21434885432331141075e-01,
9.05397246560622585843e-03);
ir->AddTriPoints6(49, 8.52566954376889230005e-01, 1.42425757365756355810e-01,
1.46315755173510018451e-03);
ir->AddTriPoints6(55, 6.05083979068707922266e-01, 3.54028009735275261960e-01,
8.05108138201205379703e-03);
ir->AddTriPoints6(61, 7.43181368957436361278e-01, 2.41894578960579587079e-01,
4.22794374976824798712e-03);
ir->AddTriPoints6(67, 9.30137698876805085746e-01, 6.00862753223067036501e-02,
1.66360068142969402642e-03);
ir->SetOrder(19);
TriangleIntRules[19] = ir;
return ir;
case 20:
ir = new IntegrationRule(79);
ir->AddTriMidPoint(0, 1.39101107014531159140e-02);
ir->AddTriPoints3(1, 2.54579267673339160183e-01, 1.40832013075202471669e-02);
ir->AddTriPoints3(4, 1.09761410283977789426e-02, 7.98840791066619858654e-04);
ir->AddTriPoints3(7, 1.09383596711714603522e-01, 7.83023077607453328597e-03);
ir->AddTriPoints3(10, 1.86294997744540946627e-01, 9.17346297425291473671e-03);
ir->AddTriPoints3(13, 4.45551056955924895675e-01, 9.45239993323244813428e-03);
ir->AddTriPoints3(16, 3.73108805988847103130e-02, 2.16127541066557732688e-03);
ir->AddTriPoints3(19, 3.93425347817099924086e-01, 1.37880506290704585304e-02);
ir->AddTriPoints3(22, 4.76245611540499047543e-01, 7.10182530340844071076e-03);
ir->AddTriPoints6(25, 8.33295511838236246938e-01, 1.59133707657067247077e-01,
2.20289741855849742491e-03);
ir->AddTriPoints6(31, 7.54921502863547422280e-01, 1.98518132228788335425e-01,
5.98639857895469015836e-03);
ir->AddTriPoints6(37, 9.31054476783942153162e-01, 6.40905856084340586065e-02,
1.12986960212586558597e-03);
ir->AddTriPoints6(43, 6.11877703547425655373e-01, 3.33134817309587605294e-01,
8.66722556721933289070e-03);
ir->AddTriPoints6(49, 8.61684018936486717521e-01, 9.99522962881386756173e-02,
4.14571152761385782609e-03);
ir->AddTriPoints6(55, 6.78165737889635522606e-01, 2.15607057390094447591e-01,
7.72260782209923009323e-03);
ir->AddTriPoints6(61, 5.70144692890973359134e-01, 4.20023758816224113133e-01,
3.69568150025529782929e-03);
ir->AddTriPoints6(67, 5.42331804172428100230e-01, 3.17860123835772001577e-01,
1.16917457318277372147e-02);
ir->AddTriPoints6(73, 7.08681375720323636358e-01, 2.80581411423665327831e-01,
3.57820023845768515197e-03);
ir->SetOrder(20);
TriangleIntRules[20] = ir;
return ir;
default:
// Grundmann-Moller fallback for orders beyond tabulated rules
int i = (Order / 2) * 2 + 1; // closest odd >= Order
AllocIntRule(TriangleIntRules, i);
ir = new IntegrationRule;
ir->GrundmannMollerSimplexRule(i/2, 2);
if (!TriangleIntRules[i-1]) { TriangleIntRules[i-1] = ir; }
TriangleIntRules[i] = ir;
return ir;
}
}
// Tet rules d=0-13 from Witherden & Vincent [1], orbit data from PyFR, CC-BY 4.0.
// Tet rules d=14-20 from Chuluunbaatar et al. [2], supplementary data.
IntegrationRule *IntegrationRules::TetrahedronPositiveIntegrationRule(int Order)
{
IntegrationRule *ir = NULL;
switch (Order)
{
case 0:
case 1:
ir = new IntegrationRule(1);
ir->AddTetMidPoint(0, 1.66666666666666657415e-01);
ir->SetOrder(1);
TetrahedronIntRules[0] =
TetrahedronIntRules[1] = ir;
return ir;
case 2:
ir = new IntegrationRule(4);
ir->AddTetPoints4(0, 1.38196601125010531952e-01, 4.16666666666666643537e-02);
ir->SetOrder(2);
TetrahedronIntRules[2] = ir;
return ir;
case 3:
ir = new IntegrationRule(8);
ir->AddTetPoints4(0, 3.28163302516381705232e-01, 2.27029737561812265667e-02);
ir->AddTetPoints4(4, 1.08047249898428621151e-01, 1.89636929104854412564e-02);
ir->SetOrder(3);
TetrahedronIntRules[3] = ir;
return ir;
case 4:
case 5:
ir = new IntegrationRule(14);
ir->AddTetPoints4(0, 3.10885919263300669613e-01, 1.87813209530026427319e-02);
ir->AddTetPoints4(4, 9.27352503108912484819e-02, 1.22488405193936587129e-02);
ir->AddTetPoints6(8, 4.54496295874350364485e-01, 7.09100346284691120807e-03);
ir->SetOrder(5);
TetrahedronIntRules[4] =
TetrahedronIntRules[5] = ir;
return ir;
case 6:
ir = new IntegrationRule(24);
ir->AddTetPoints4(0, 4.06739585346113652342e-02, 1.67953517588677390775e-03);
ir->AddTetPoints4(4, 3.22337890142275540484e-01, 9.22619692394245453915e-03);
ir->AddTetPoints4(8, 2.14602871259152117034e-01, 6.65379170969458179352e-03);
ir->AddTetPoints12(12, 6.36610018750174977420e-02, 6.03005664791649187428e-01,
8.03571428571428492127e-03);
ir->SetOrder(6);
TetrahedronIntRules[6] = ir;
return ir;
case 7:
ir = new IntegrationRule(35);
ir->AddTetMidPoint(0, 1.59142149106884754628e-02);
ir->AddTetPoints4(1, 3.15701149778202794227e-01, 7.05493020166117132397e-03);
ir->AddTetPoints6(5, 4.49510177401603649994e-01, 5.31615463880959638471e-03);
ir->AddTetPoints12(11, 1.88833831026001153219e-01, 5.75171637586999962011e-01,
6.20118845472243662709e-03);
ir->AddTetPoints12(23, 2.12654725414832546093e-02, 8.10830241098548620826e-01,
1.35179513831722359664e-03);
ir->SetOrder(7);
TetrahedronIntRules[7] = ir;
return ir;
case 8:
ir = new IntegrationRule(46);
ir->AddTetPoints4(0, 1.07952724962210866444e-01, 4.40444181806813866292e-03);
ir->AddTetPoints4(4, 1.85109487782586568105e-01, 8.67195792728975463348e-03);
ir->AddTetPoints4(8, 4.23165436847673381848e-02, 1.25420935892336655841e-03);
ir->AddTetPoints4(12, 3.14181709124039088010e-01, 6.96063047615581593358e-03);
ir->AddTetPoints6(16, 4.35591328583830206256e-01, 6.04682171021813687217e-03);
ir->AddTetPoints12(22, 2.14339301271305737728e-02, 7.17464063426308307214e-01,
1.19281714847407210867e-03);
ir->AddTetPoints12(34, 2.04139333876029116510e-01, 5.83797378302144398532e-01,
2.57558102516005586052e-03);
ir->SetOrder(8);
TetrahedronIntRules[8] = ir;
return ir;
case 9:
ir = new IntegrationRule(59);
ir->AddTetMidPoint(0, 9.66842481874670943431e-03);
ir->AddTetPoints4(1, 6.19817086544571793638e-10, 1.07198802932093984424e-05);
ir->AddTetPoints4(5, 1.60774535395261597426e-01, 3.86222307707090968185e-03);
ir->AddTetPoints4(9, 3.22276521821420969260e-01, 4.92715205590488116577e-03);
ir->AddTetPoints4(13, 4.51089183454135844720e-02, 1.34399666326936377374e-03);
ir->AddTetPoints6(17, 3.87703453995623947836e-01, 6.35568001728374458448e-03);
ir->AddTetPoints12(23, 4.58871448752459276665e-01, 7.97025232620401369310e-02,
1.39740369971642539558e-03);
ir->AddTetPoints12(35, 3.37758706853386048152e-02, 7.18350326442074527122e-01,
1.70575989212422133613e-03);
ir->AddTetPoints12(47, 1.83641369809927956780e-01, 5.98301349801968918030e-01,
3.42081932799802312939e-03);
ir->SetOrder(9);
TetrahedronIntRules[9] = ir;
return ir;
case 10:
ir = new IntegrationRule(81);
ir->AddTetMidPoint(0, 7.89996225933678984654e-03);
ir->AddTetPoints4(1, 3.12250068695188676138e-01, 4.48950999871145037950e-03);
ir->AddTetPoints4(5, 1.14309653857346149586e-01, 1.64485995279889710662e-03);
ir->AddTetPoints12(9, 4.10430739218965501269e-01, 1.65486025619611065718e-01,
1.89898020336587186781e-03);
ir->AddTetPoints12(21, 6.13800882479076381770e-03, 9.42988767345204870196e-01,
6.03240573898756009806e-05);
ir->AddTetPoints12(33, 1.21050181145589408338e-01, 4.77190379904280370660e-01,
4.28995533007601147213e-03);
ir->AddTetPoints12(45, 3.27794682164426753879e-02, 5.94256269480006982242e-01,
1.68931194662596552945e-03);
ir->AddTetPoints12(57, 3.24852815648231096901e-02, 8.01177284658344368573e-01,
1.09602454617265063913e-03);
ir->AddTetPoints12(69, 1.74979342183939068356e-01, 6.28071845475365986289e-01,
2.15117263314366490706e-03);
ir->SetOrder(10);
TetrahedronIntRules[10] = ir;
return ir;
case 11:
ir = new IntegrationRule(96);
ir->AddTetPoints4(0, 2.71527207067321363354e-02, 3.30755017786941475644e-04);
ir->AddTetPoints4(4, 7.29513610462571016058e-02, 1.27724462275054500421e-03);
ir->AddTetPoints4(8, 1.16306248902001030388e-01, 2.22195840281977797376e-03);
ir->AddTetPoints4(12, 1.79873804986097840519e-01, 3.55549424791121128006e-03);
ir->AddTetPoints4(16, 2.90224794862315171873e-01, 4.27767411104971236741e-03);
ir->AddTetPoints4(20, 3.25420936748619160639e-01, 2.29560465583227143668e-03);
ir->AddTetPoints6(24, 4.99998725049884129579e-01, 1.95152894059845476377e-04);
ir->AddTetPoints6(30, 3.94300142842090972639e-01, 4.13762713030314983886e-03);
ir->AddTetPoints12(36, 1.53994139264412854828e-02, 8.20202176629804657892e-01,
3.44113207868302869216e-04);
ir->AddTetPoints12(48, 4.36843254717693696421e-02, 6.27516751622257062948e-01,
2.00540889524405963051e-03);
ir->AddTetPoints12(60, 1.32316796082697751835e-01, 7.35366407834604496330e-01,
5.92160675031106853265e-04);
ir->AddTetPoints12(72, 2.14430354900043917965e-01, 5.31595425719235903372e-01,
3.02373698028425954079e-03);
ir->AddTetPoints12(84, 4.39586615093850330283e-01, 1.15789732843376125260e-01,
1.10416876556284249307e-03);
ir->SetOrder(11);
TetrahedronIntRules[11] = ir;
return ir;
case 12:
ir = new IntegrationRule(123);
ir->AddTetMidPoint(0, 3.73841522662751247000e-03);
ir->AddTetPoints4(1, 1.87550512633127830497e-02, 1.42695998696545141987e-04);
ir->AddTetPoints4(5, 1.08129536920462676619e-01, 2.21101382522646480733e-03);
ir->AddTetPoints4(9, 2.00131676822545012673e-01, 1.02716311841773611131e-03);
ir->AddTetPoints4(13, 3.00854293538076578152e-01, 3.84627572131096594557e-03);
ir->AddTetPoints4(17, 3.33333333333333259318e-01, 3.93263480259983290444e-04);
ir->AddTetPoints6(21, 4.61659950214442116323e-01, 3.64372815936188636666e-04);
ir->AddTetPoints12(27, 1.44707549187619299857e-02, 8.12825119403836504617e-01,
2.78228183082975693598e-04);
ir->AddTetPoints12(39, 1.93543398987769954545e-02, 6.01428742996530152354e-01,
5.44744358748572190913e-04);
ir->AddTetPoints12(51, 7.79277628532308863640e-02, 8.27642519452021385717e-01,
4.96603607085240776955e-04);
ir->AddTetPoints12(63, 1.22055870746741623734e-01, 4.77806520042316273944e-01,
3.50252598711278933380e-03);
ir->AddTetPoints12(75, 2.47870739372197945727e-01, 4.77761799116294072487e-01,
1.92951947508030146987e-03);
ir->AddTetPoints12(87, 4.29731509588804683197e-01, 1.17747435901101149547e-01,
1.59458000732484511328e-03);
ir->AddTetPoints24(99, 6.53037808968305988344e-01, 2.26776739658831050228e-01,
9.77349816032284102185e-02, 1.25441443948160597475e-03);
ir->SetOrder(12);
TetrahedronIntRules[12] = ir;
return ir;
case 13:
ir = new IntegrationRule(145);
ir->AddTetMidPoint(0, 4.65163625751287973520e-03);
ir->AddTetPoints4(1, 1.83047574861928130652e-02, 1.11328544338301012079e-04);
ir->AddTetPoints4(5, 1.79015082630022803745e-01, 3.21788310144650391634e-03);
ir->AddTetPoints4(9, 3.29615853754448240309e-01, 1.11613482625956810662e-03);
ir->AddTetPoints6(13, 4.84258919196047465938e-01, 4.22777636660235864308e-04);
ir->AddTetPoints6(19, 4.37693799377281589358e-01, 1.42741910394635984974e-03);
ir->AddTetPoints12(25, 1.68580250819503341120e-02, 7.16622722155387803511e-01,
3.27226375772531215529e-04);
ir->AddTetPoints12(37, 2.35257182448596058322e-02, 8.50600927605402956644e-01,
4.21131904215649394228e-04);
ir->AddTetPoints12(49, 6.94418950495464537553e-02, 7.08574314820604622689e-01,
9.98940835257684659268e-04);
ir->AddTetPoints12(61, 9.37005272821476720146e-02, 5.63456727822489344959e-01,
1.88623398086488818989e-03);
ir->AddTetPoints12(73, 1.25885360164042447995e-01, 7.40105985667665167149e-01,
5.36847192210583730974e-04);
ir->AddTetPoints12(85, 2.17955270547737667286e-01, 5.35290625276012344003e-01,
1.79931440889047528954e-03);
ir->AddTetPoints12(97, 3.54663455472783883948e-01, 2.00014910210617791186e-01,
2.91862732938839245650e-03);
ir->AddTetPoints12(109, 4.16689287657038387458e-01, 1.52054854976777675812e-01,
9.62750257012783515476e-04);
ir->AddTetPoints24(121, 6.06652560730350010054e-01, 3.04158433676372519372e-01,
7.79465622318078477093e-02, 6.21649861415869242447e-04);
ir->SetOrder(13);
TetrahedronIntRules[13] = ir;
return ir;
case 14:
// Chuluunbaatar et al. 2022: 175 pts, 1xCent + 6xS31 + 1xS22 + 10xS211 + 1xS1111
ir = new IntegrationRule(175);
ir->AddTetMidPoint(0, 2.79630622899013732072e-03);
ir->AddTetPoints4(1, 3.33328696010048830534e-01, 1.46917540892973303920e-04);
ir->AddTetPoints4(5, 2.03700979179134489261e-01, 1.62781576883158380503e-03);
ir->AddTetPoints4(9, 4.23119120487503441730e-02, 3.41825853298758448786e-04);
ir->AddTetPoints4(13, 1.66911321524259963212e-02, 6.86936532495300726303e-05);
ir->AddTetPoints4(17, 1.64429779556425403886e-01, 2.04589809259575743788e-03);
ir->AddTetPoints4(21, 3.05243130480787605574e-01, 2.91353212326472864671e-03);
ir->AddTetPoints6(25, 3.64287147870284933049e-01, 2.86344364876423311192e-03);
ir->AddTetPoints12(31, 2.15024351638664623643e-01, 5.09377822427890203372e-01,
1.86604702493200981690e-03);
ir->AddTetPoints12(43, 4.08453557824531576781e-01, 2.80291884809145824820e-02,
1.37079721642533879263e-03);
ir->AddTetPoints12(55, 2.59921479331125596102e-02, 7.58920443676681433232e-01,
5.79340182017268356431e-04);
ir->AddTetPoints12(67, 1.49228115767079897586e-02, 6.12483481308898292106e-01,
3.15838010189281473503e-04);
ir->AddTetPoints12(79, 8.37923462693035414617e-02, 8.22678981568125355928e-01,
3.37570057080462679680e-04);
ir->AddTetPoints12(91, 2.67966909251860618824e-01, 1.21450733299050331326e-02,
7.33968440516890967273e-04);
ir->AddTetPoints12(103, 7.22614743275835913483e-02, 2.93829968686904419162e-01,
1.73940500456261186446e-03);
ir->AddTetPoints12(115, 4.61690356122462508548e-01, 6.24312988244191680032e-02,
5.50156605537168688809e-04);
ir->AddTetPoints12(127, 1.02556247843651599492e-05, 9.09652220362116237240e-01,
3.74818592914694638193e-05);
ir->AddTetPoints12(139, 1.30667193397036723868e-01, 6.88760930866849863108e-01,
1.25403778742792012396e-03);
ir->AddTetPoints24(151, 4.96082264783182565887e-03, 1.13153535288820022986e-01,
2.55337379104889128367e-01, 5.28969173366363918341e-04);
ir->SetOrder(14);
TetrahedronIntRules[14] = ir;
return ir;
case 15:
// Chuluunbaatar et al. 2022: 209 pts, 1xCent + 4xS31 + 2xS22 + 11xS211 + 2xS1111
ir = new IntegrationRule(209);
ir->AddTetMidPoint(0, 1.41781886024826123995e-03);
ir->AddTetPoints4(1, 3.28314281102506377863e-01, 8.78039594754075579386e-04);
ir->AddTetPoints4(5, 5.95315181460130682378e-02, 6.49096346642090296988e-04);
ir->AddTetPoints4(9, 1.79953296856689010097e-01, 2.51315014419990637520e-03);
ir->AddTetPoints4(13, 2.87467267398706316506e-01, 1.49510722611222407301e-03);
ir->AddTetPoints6(17, 1.63820429539269674102e-01, 1.59662635597863640340e-03);
ir->AddTetPoints6(23, 4.49691037286174599696e-01, 5.56101982388976408267e-04);
ir->AddTetPoints12(29, 4.00736540413628217205e-01, 1.44084693588290586180e-02,
7.41098096825775301023e-04);
ir->AddTetPoints12(41, 9.30917130105696349895e-02, 5.00389099764625755462e-01,
1.48911776902807633377e-03);
ir->AddTetPoints12(53, 8.58969713610300000806e-02, 6.54216648304909331735e-01,
1.11933741359922559432e-03);
ir->AddTetPoints12(65, 2.10555592437809635520e-01, 6.11819979966393701076e-02,
1.62698522585349981094e-03);
ir->AddTetPoints12(77, 8.52418251507089524965e-02, 9.51322614055337932581e-03,
1.79269162929391381043e-04);
ir->AddTetPoints12(89, 3.39062766530538045595e-02, 1.00939010271196565223e-03,
6.94170071688395131987e-05);
ir->AddTetPoints12(101, 3.61923170322900333851e-01, 6.94744848139675630350e-02,
1.59447426536825733780e-03);
ir->AddTetPoints12(113, 4.82786943073602314858e-01, 2.98248878093377127463e-02,
1.85989656618890347554e-04);
ir->AddTetPoints12(125, 1.67482762532157707092e-02, 8.28143120169573809797e-01,
2.46974837616148544094e-04);
ir->AddTetPoints12(137, 1.77918303634979659000e-02, 2.95058306317452390122e-01,
3.64978786118624204133e-04);
ir->AddTetPoints12(149, 2.22052218944333024098e-01, 5.47335431979886655185e-01,
5.97708840203148013097e-04);
ir->AddTetPoints24(161, 7.01737933129022994905e-01, 1.90468590405707266511e-01,
9.00527571062145620884e-02, 6.09486799192013577881e-04);
ir->AddTetPoints24(185, 9.30456155647334665071e-02, 3.39714197260826189506e-01,
1.75628396157984228987e-02, 7.07458692200529210524e-04);
ir->SetOrder(15);
TetrahedronIntRules[15] = ir;
return ir;
case 16:
// Chuluunbaatar et al. 2022: 248 pts, 8xS31 + 2xS22 + 11xS211 + 3xS1111
ir = new IntegrationRule(248);
ir->AddTetPoints4(0, 3.27237393634992601577e-01, 1.02720766161859349518e-03);
ir->AddTetPoints4(4, 1.70006239733430930539e-01, 1.65526299995553852033e-03);
ir->AddTetPoints4(8, 1.15524594427552973475e-01, 9.38857005487496389280e-04);
ir->AddTetPoints4(12, 2.91444830780401391290e-02, 6.51307095609853886316e-05);
ir->AddTetPoints4(16, 2.99333264802760234957e-01, 1.67595204112568215392e-03);
ir->AddTetPoints4(20, 3.08156348381425804206e-01, 5.31861976759345615219e-04);
ir->AddTetPoints4(24, 1.50096498602994791322e-02, 5.70730781338430579624e-05);
ir->AddTetPoints4(28, 2.15377318942399170743e-01, 1.98341225672248695419e-03);
ir->AddTetPoints6(32, 4.31794349434656055120e-01, 1.33750640425309239717e-03);
ir->AddTetPoints6(38, 3.51744151127164061954e-01, 1.76194827177588689456e-03);
ir->AddTetPoints12(44, 8.29503118270854405969e-03, 1.02752005688054329213e-01,
7.68034210054447698257e-05);
ir->AddTetPoints12(56, 4.80211290069074772657e-02, 1.31255041819827861227e-01,
6.11232548742599826781e-04);
ir->AddTetPoints12(68, 1.58330800578366723275e-02, 7.34396180497698725098e-01,
2.28071918651308634196e-04);
ir->AddTetPoints12(80, 2.33860521976982954628e-01, 5.24417475091189966285e-01,
5.40957583749601829924e-04);
ir->AddTetPoints12(92, 4.03140399383019043533e-01, 1.61507733387133249614e-02,
7.92724342585451077596e-04);
ir->AddTetPoints12(104, 4.62664186044103586948e-01, 8.10608626417101511830e-03,
2.79154547643383486085e-04);
ir->AddTetPoints12(116, 9.54740058566225929804e-02, 2.29727696324949964835e-01,
9.08138512826854073234e-04);
ir->AddTetPoints12(128, 5.22644355194657739272e-02, 2.65023714835432855352e-01,
5.22825831653034723938e-04);
ir->AddTetPoints12(140, 1.54579380484822833525e-02, 3.89592797271225033118e-01,
2.70158998322436651619e-04);
ir->AddTetPoints12(152, 6.51049846146104782552e-02, 1.06703843590993385781e-02,
1.88285026959176838299e-04);
ir->AddTetPoints12(164, 1.54103896531334966236e-01, 6.61715834360067312048e-01,
9.04088475565851504463e-04);
ir->AddTetPoints24(176, 3.06401667521507548031e-01, 9.60517630472854377910e-02,
5.82737849734082380415e-01, 5.64378019946093099565e-04);
ir->AddTetPoints24(200, 7.52465510383990981991e-02, 7.45338308409307703783e-01,
1.68928693209220324653e-04, 1.50966565378719797755e-04);
ir->AddTetPoints24(224, 6.67856954025341370551e-02, 1.77429946613237937703e-01,
4.69596074566060506239e-01, 1.47055596469915315222e-03);
ir->SetOrder(16);
TetrahedronIntRules[16] = ir;
return ir;
case 17:
// Chuluunbaatar et al. 2022: 284 pts, 8xS31 + 2xS22 + 14xS211 + 3xS1111
ir = new IntegrationRule(284);
ir->AddTetPoints4(0, 7.70317217555786387662e-02, 5.61182432136912863994e-04);
ir->AddTetPoints4(4, 3.33178098937441047322e-01, 1.00689770519544758830e-04);
ir->AddTetPoints4(8, 4.70791056455278841830e-02, 3.67647177915098600219e-05);
ir->AddTetPoints4(12, 3.04818016813530989761e-01, 1.65889979279151768450e-03);
ir->AddTetPoints4(16, 1.30944391509640850613e-01, 1.44088986921434188127e-03);
ir->AddTetPoints4(20, 1.92535395691919936079e-01, 9.15395452245713805300e-04);
ir->AddTetPoints4(24, 2.76657577444746005657e-01, 1.54901732083429399985e-03);
ir->AddTetPoints4(28, 1.25812395975189866837e-02, 3.37712309004839736381e-05);
ir->AddTetPoints6(32, 3.79480026881957605706e-03, 6.09736907157372445766e-05);
ir->AddTetPoints6(38, 6.63574479091031538269e-02, 9.84476156008183339238e-04);
ir->AddTetPoints12(44, 1.55274078991054307469e-02, 1.62632006416904367763e-01,
1.72804764846827806904e-04);
ir->AddTetPoints12(56, 1.50826606630984655366e-01, 2.85203367985520928052e-01,
1.47139558752681400000e-03);
ir->AddTetPoints12(68, 2.15452134252546806392e-01, 4.98848586166224794436e-01,
1.39306363547645069810e-03);
ir->AddTetPoints12(80, 8.76098177043343750992e-02, 5.57666868254345748923e-01,
1.10287787958478406859e-03);
ir->AddTetPoints12(92, 2.76970146665180327883e-01, 4.27355049241211759625e-01,
7.68445912031412610432e-04);
ir->AddTetPoints12(104, 7.84854004483451911378e-02, 8.28808438721931994841e-01,
2.88969312852391376975e-04);
ir->AddTetPoints12(116, 4.16031236751370603333e-01, 5.86692256093802096822e-03,
3.64077087845785364682e-04);
ir->AddTetPoints12(128, 3.11418578536613735799e-03, 2.87446864291519776913e-01,
3.72979694270385682886e-05);
ir->AddTetPoints12(140, 1.48006973486492082737e-01, 3.23952556344107162056e-02,
8.00305827281323160088e-04);
ir->AddTetPoints12(152, 1.43033359605475689225e-02, 6.71799737186767331742e-02,
1.04156882366980408738e-04);
ir->AddTetPoints12(164, 4.67432600299733047589e-01, 1.06651363448138844503e-02,
3.28661937749209721524e-04);
ir->AddTetPoints12(176, 3.84385292133539946402e-01, 1.72438767641856061097e-01,
1.30629277435961670649e-03);
ir->AddTetPoints12(188, 4.91933575124020999736e-02, 1.88887052205218147760e-01,
6.32377286180225181393e-04);
ir->AddTetPoints12(200, 2.12339226453523544080e-01, 4.66228370030305223209e-03,
2.79290741478308349437e-04);
ir->AddTetPoints24(212, 5.56903978599597615506e-01, 3.03486206973905936479e-01,
1.18763501187465259079e-01, 6.89373805061415279201e-04);
ir->AddTetPoints24(236, 7.72626757757003540528e-02, 7.24630018034681633310e-01,
1.98007306189710574618e-01, 1.50128885728721429014e-04);
ir->AddTetPoints24(260, 3.94151894733046209707e-02, 1.37686205384732439361e-02,
3.27821577026260191356e-01, 2.69135394730690453036e-04);
ir->SetOrder(17);
TetrahedronIntRules[17] = ir;
return ir;
case 18:
// Chuluunbaatar et al. 2022: 343 pts, 1xCent + 6xS31 + 1xS22 + 18xS211 + 4xS1111
ir = new IntegrationRule(343);
ir->AddTetMidPoint(0, 1.50320520665968271855e-03);
ir->AddTetPoints4(1, 1.48031283019549930735e-01, 1.29183701010426432026e-03);
ir->AddTetPoints4(5, 9.18424577295562372115e-02, 5.65165600131921114398e-04);
ir->AddTetPoints4(9, 1.21731006846268821620e-02, 2.96638661291863846498e-05);
ir->AddTetPoints4(13, 2.96287086243479214076e-01, 1.68021599975901742008e-03);
ir->AddTetPoints4(17, 3.26360945420223702573e-01, 5.68549421501223146459e-04);
ir->AddTetPoints4(21, 2.16789137320780644913e-01, 5.55087845531530756776e-04);
ir->AddTetPoints6(25, 4.02614199568341046831e-01, 9.23257682834909085973e-04);
ir->AddTetPoints12(31, 4.40000294606430919497e-01, 2.67115252330815747261e-02,
5.89236226881148018354e-04);
ir->AddTetPoints12(43, 4.12210360146149590310e-01, 1.74700018752273367184e-01,
2.20201645262970173086e-04);
ir->AddTetPoints12(55, 3.72351275734651154803e-01, 2.01036053559202482210e-01,
1.19114067751767893286e-03);
ir->AddTetPoints12(67, 2.69242251358920825499e-01, 4.49869807112305730712e-01,
4.91465288515259167076e-04);
ir->AddTetPoints12(79, 9.76627002277863226487e-02, 5.25608170470769464622e-01,
9.64486286268097844226e-04);
ir->AddTetPoints12(91, 7.95369840699704060138e-03, 9.21057037109250575924e-01,
3.50539800669945179434e-05);
ir->AddTetPoints12(103, 1.87267495112264620305e-01, 6.22654876406694146596e-01,
2.16974411183503681708e-04);
ir->AddTetPoints12(115, 1.11072676172302167719e-01, 7.68063140393386190041e-01,
2.34282910136374296871e-04);
ir->AddTetPoints12(127, 7.28479245819699250397e-02, 6.49722949416110195919e-01,
4.87753175028903700802e-04);
ir->AddTetPoints12(139, 4.35242030819264283381e-02, 1.17645467635705727738e-01,
3.42108745586317604826e-04);
ir->AddTetPoints12(151, 4.75773867838972852606e-01, 4.07808063375027368691e-02,
1.54370387724311262630e-04);
ir->AddTetPoints12(163, 1.34493684207502642303e-02, 7.10473230757141527292e-01,
1.53084245505138950502e-04);
ir->AddTetPoints12(175, 1.55188126953707594691e-01, 4.80062353992693063853e-02,
7.41762550201790354931e-04);
ir->AddTetPoints12(187, 9.79636346189126545891e-03, 3.98937047199835026490e-01,
9.70189072253606300464e-05);
ir->AddTetPoints12(199, 2.22972555180978554423e-01, 4.76498622927509052349e-01,
1.23606250864963739498e-03);
ir->AddTetPoints12(211, 4.21260289724883496554e-02, 5.74400719147259319897e-01,
5.42935360416797459064e-04);
ir->AddTetPoints12(223, 5.33405089760383491204e-02, 8.80640211270697026436e-01,
1.37609067910292992955e-04);
ir->AddTetPoints12(235, 1.61516953402295604381e-01, 3.97825734805866804145e-01,
1.38360407936932868454e-03);
ir->AddTetPoints24(247, 1.45407574632878761056e-01, 2.91164813093958863011e-01,
2.48113931665566757323e-02, 7.08660104525119741853e-04);
ir->AddTetPoints24(271, 3.05950727633923398596e-03, 8.24555339410965038027e-01,
2.68565028804892240444e-02, 7.44949831353088352858e-05);
ir->AddTetPoints24(295, 6.97926614756394281258e-01, 2.13661027507205059095e-01,
1.66604565123493510159e-02, 3.48774332060316084436e-04);
ir->AddTetPoints24(319, 7.44110286479428006956e-02, 3.30529031579615995007e-01,
5.94802303330229431566e-01, 1.27738536486342427233e-04);
ir->SetOrder(18);
TetrahedronIntRules[18] = ir;
return ir;
case 19:
// Chuluunbaatar et al. 2022: 383 pts, 1xCent + 7xS31 + 3xS22 + 18xS211 + 5xS1111
ir = new IntegrationRule(383);
ir->AddTetMidPoint(0, 1.63415516118113374362e-03);
ir->AddTetPoints4(1, 1.99329047511150186933e-01, 1.40358969707281050661e-03);
ir->AddTetPoints4(5, 3.19645815662209620278e-01, 7.59091701642911130359e-04);
ir->AddTetPoints4(9, 1.28431938718745111000e-02, 3.39837309564992975973e-05);
ir->AddTetPoints4(13, 4.48982322308715264825e-02, 2.64426237344559213801e-04);
ir->AddTetPoints4(17, 1.36956642483832574664e-01, 7.02856031175181001670e-04);
ir->AddTetPoints4(21, 2.90023140379987831583e-01, 1.36565197140369625796e-03);
ir->AddTetPoints4(25, 9.74727471850612287030e-02, 5.49967641751253539552e-04);
ir->AddTetPoints6(29, 9.71505706534206842084e-02, 8.76433367036966496331e-04);
ir->AddTetPoints6(35, 3.43201766155373844125e-01, 1.42791869569969145752e-03);
ir->AddTetPoints6(41, 4.81048682529186311108e-01, 2.14104659346860782621e-04);
ir->AddTetPoints12(47, 1.65183633185142647593e-01, 6.20343271948313068620e-01,
7.66624121398570487762e-04);
ir->AddTetPoints12(59, 6.34076230480007801971e-02, 1.00712056526788096511e-06,
5.83669782740338187726e-05);
ir->AddTetPoints12(71, 1.14358224224068777061e-03, 4.00863727471189756901e-01,
2.15179213304474853136e-05);
ir->AddTetPoints12(83, 1.72841898844304682481e-02, 2.80974353964125900252e-01,
2.11660243173845957895e-04);
ir->AddTetPoints12(95, 3.72064110603054443160e-01, 1.99705629942815821032e-01,
8.75485553265252145448e-04);
ir->AddTetPoints12(107, 5.20242736384851911513e-02, 1.37531998164526964024e-01,
2.41875978548309640689e-04);
ir->AddTetPoints12(119, 1.60391938988630439189e-01, 6.93856268828658799552e-03,
2.41421983084743403854e-04);
ir->AddTetPoints12(131, 2.04009311788170112981e-03, 1.85612033720361502276e-01,
2.06414634761428247161e-05);
ir->AddTetPoints12(143, 5.27148535043930888122e-02, 3.66275984765305206992e-01,
5.92721175866269185152e-04);
ir->AddTetPoints12(155, 2.43716736043213727525e-01, 4.92302726125094514131e-01,
5.87805221231469288319e-04);
ir->AddTetPoints12(167, 2.35953750535251915998e-01, 4.36147999140300668408e-01,
1.13870552683716466137e-03);
ir->AddTetPoints12(179, 3.76396525093455058819e-01, 2.39487568074092604942e-01,
3.83776606890474675932e-04);
ir->AddTetPoints12(191, 1.01345934656195998946e-01, 7.75724008450901503231e-01,
3.13399374751491786861e-04);
ir->AddTetPoints12(203, 1.03978576485678226443e-02, 9.15876371248755760668e-01,
5.00269118618190970684e-05);
ir->AddTetPoints12(215, 4.34825905512194077485e-01, 1.54748703679767566493e-02,
5.11025086732051357814e-04);
ir->AddTetPoints12(227, 1.21014927057761692564e-01, 5.16611295841134299245e-01,
1.01828426898903845119e-03);
ir->AddTetPoints12(239, 2.02221519510974792611e-02, 1.24848861415461573343e-01,
1.43839758373707761420e-04);
ir->AddTetPoints12(251, 6.45349753497565792326e-02, 6.58797428082353198064e-01,
5.65395165437599170333e-04);
ir->AddTetPoints24(263, 5.41751356566992220420e-02, 1.84132753560833639650e-01,
7.55314661874476711567e-01, 1.46482060260151955187e-04);
ir->AddTetPoints24(287, 8.81662316742434920558e-02, 6.34269181940578685719e-01,
1.71630199679625762565e-02, 3.35574461050504140036e-04);
ir->AddTetPoints24(311, 2.99876324799488391815e-01, 5.12011805461436986242e-01,
1.39945756017624101109e-01, 6.87105878169750922298e-04);
ir->AddTetPoints24(335, 5.43033048543508201078e-01, 3.98941499659789464149e-03,
2.96197429831241032527e-01, 1.95466436126630728823e-04);
ir->AddTetPoints24(359, 5.64748804926985426000e-01, 5.04233375578317308263e-02,
5.38328049657370907161e-03, 1.64230458281612438400e-04);
ir->SetOrder(19);
TetrahedronIntRules[19] = ir;
return ir;
case 20:
// Chuluunbaatar et al. 2022: 441 pts, 1xCent + 8xS31 + 4xS22 + 20xS211 + 6xS1111
ir = new IntegrationRule(441);
ir->AddTetMidPoint(0, 1.18189152746071531389e-03);
ir->AddTetPoints4(1, 1.44398440418483348102e-01, 9.66793543666131616025e-04);
ir->AddTetPoints4(5, 7.58537944731913719304e-03, 7.60496594911488061041e-06);
ir->AddTetPoints4(9, 2.92814880923072839991e-01, 1.15590090717039508002e-03);
ir->AddTetPoints4(13, 3.21283351882928780441e-01, 6.88636083704610908220e-04);
ir->AddTetPoints4(17, 1.99126561548209762842e-01, 1.13432796616464034133e-03);
ir->AddTetPoints4(21, 9.94395843777090698845e-02, 4.86016284904959409725e-04);
ir->AddTetPoints4(25, 5.64411054542164752901e-02, 3.22610846546138882538e-04);
ir->AddTetPoints4(29, 2.29655958568571322287e-02, 5.81298999656466752642e-05);
ir->AddTetPoints6(33, 1.79141319969986889671e-01, 1.09529243625682828553e-03);
ir->AddTetPoints6(39, 1.28302591207222288494e-01, 5.73999808941768881014e-04);
ir->AddTetPoints6(45, 1.45390267831490144212e-02, 1.87025723866918939901e-04);
ir->AddTetPoints6(51, 4.21353125034043096697e-01, 7.00971843927932197066e-04);
ir->AddTetPoints12(57, 2.31009838674045342444e-01, 4.40089079468850674637e-01,
9.76079610667369892280e-04);
ir->AddTetPoints12(69, 5.34968850305569260106e-03, 1.93845423004036843118e-01,
3.05362840831512903964e-05);
ir->AddTetPoints12(81, 1.24450062776632008887e-01, 2.68040213994591769442e-01,
8.86819612544235340128e-04);
ir->AddTetPoints12(93, 2.04325810970697151203e-02, 2.58308114426872181824e-01,
1.92969809694827680807e-04);
ir->AddTetPoints12(105, 4.82828231821251577238e-02, 5.80396926577266859815e-03,
6.19875387085781813859e-05);
ir->AddTetPoints12(117, 2.89149787325270579696e-01, 4.18198665244226719384e-01,
1.83280536557154684341e-04);
ir->AddTetPoints12(129, 4.46052961749180063022e-02, 1.63432451082933805075e-01,
3.33781952353497124181e-04);
ir->AddTetPoints12(141, 2.70746696244779198881e-03, 9.47473449934223443947e-01,
9.76902870004440518315e-06);
ir->AddTetPoints12(153, 1.81599434536722420530e-01, 5.60679256816125279328e-02,
7.16163033436471277611e-04);
ir->AddTetPoints12(165, 7.98241847716316815786e-02, 2.24797220241435974364e-01,
6.67198343081130593700e-04);
ir->AddTetPoints12(177, 1.46751424308700709198e-02, 1.05996716787954137207e-01,
1.02112432519835052438e-04);
ir->AddTetPoints12(189, 1.47988480867858707146e-01, 6.98383249617804735543e-01,
1.65331156535892409174e-04);
ir->AddTetPoints12(201, 4.52421884404454466289e-01, 8.04532263339534647884e-02,
3.75572061072812404120e-04);
ir->AddTetPoints12(213, 4.82988925439242506449e-03, 3.56408995372303527560e-01,
3.70372995907359520520e-05);
ir->AddTetPoints12(225, 3.96075105354866952023e-01, 1.93643489260524576112e-01,
4.20355142354041121707e-04);
ir->AddTetPoints12(237, 2.11473197416018027228e-01, 5.66485319771568907044e-01,
3.20737452814357003415e-04);
ir->AddTetPoints12(249, 2.56953469781508958558e-01, 4.54914546979866607490e-01,
5.44699807144207316482e-04);
ir->AddTetPoints12(261, 3.63762446007509787638e-01, 7.07704644682126682298e-02,
8.71724711789707298014e-04);
ir->AddTetPoints12(273, 4.34084693566413395982e-02, 5.61826818484091217165e-01,
4.66716966955154613419e-04);
ir->AddTetPoints12(285, 1.26096840063810999855e-01, 4.15826803180920218095e-02,
4.05374493922667372432e-04);
ir->AddTetPoints24(297, 3.19830665436792060952e-01, 4.36032333155288651105e-02,
5.02224495466759290885e-01, 7.04309544520175120040e-04);
ir->AddTetPoints24(321, 6.93104765295092647634e-04, 7.61588104432530443866e-01,
4.88127934219945436300e-02, 6.22004488693067759562e-05);
ir->AddTetPoints24(345, 3.01059509765821443905e-03, 4.68001522754562040984e-02,
5.92923309754523120141e-01, 1.11516752988944994277e-04);
ir->AddTetPoints24(369, 8.05046770737637640281e-01, 1.15008082676269607347e-01,
6.71446262920421810261e-02, 1.37946350993749402127e-04);
ir->AddTetPoints24(393, 3.27837344763098725853e-01, 1.41845459805815643506e-01,
3.73982332962941683638e-03, 1.75610557117139728066e-04);
ir->AddTetPoints24(417, 1.69431918115453827856e-02, 9.30651836894259287813e-02,
6.46141331822991715761e-01, 3.76832469454361519961e-04);
ir->SetOrder(20);
TetrahedronIntRules[20] = ir;
return ir;
default:
// Grundmann-Moller fallback for orders beyond tabulated rules
int i = (Order / 2) * 2 + 1; // closest odd >= Order
AllocIntRule(TetrahedronIntRules, i);
ir = new IntegrationRule;
ir->GrundmannMollerSimplexRule(i/2, 3);
if (!TetrahedronIntRules[i-1]) { TetrahedronIntRules[i-1] = ir; }
TetrahedronIntRules[i] = ir;
return ir;
}
}
// Integration rules for reference pyramid
IntegrationRule *IntegrationRules::PyramidIntegrationRule(int Order)
{
+51 -1
View File
@@ -219,6 +219,39 @@ private:
AddTetPoints6(off + 6, a, b, c, weight);
}
// add all 24 permutations of (a,b,c,d) where a+b+c+d = 1, all distinct
void AddTetPoints24(const int off, const real_t a, const real_t b,
const real_t c, const real_t weight)
{
const real_t d = 1. - a - b - c;
// all 24 permutations of 4 distinct barycentric coordinates
// permuting which coordinate goes to x, y, z (4th is 1-x-y-z)
IntPoint(off + 0).Set(a, b, c, weight);
IntPoint(off + 1).Set(a, b, d, weight);
IntPoint(off + 2).Set(a, c, b, weight);
IntPoint(off + 3).Set(a, c, d, weight);
IntPoint(off + 4).Set(a, d, b, weight);
IntPoint(off + 5).Set(a, d, c, weight);
IntPoint(off + 6).Set(b, a, c, weight);
IntPoint(off + 7).Set(b, a, d, weight);
IntPoint(off + 8).Set(b, c, a, weight);
IntPoint(off + 9).Set(b, c, d, weight);
IntPoint(off + 10).Set(b, d, a, weight);
IntPoint(off + 11).Set(b, d, c, weight);
IntPoint(off + 12).Set(c, a, b, weight);
IntPoint(off + 13).Set(c, a, d, weight);
IntPoint(off + 14).Set(c, b, a, weight);
IntPoint(off + 15).Set(c, b, d, weight);
IntPoint(off + 16).Set(c, d, a, weight);
IntPoint(off + 17).Set(c, d, b, weight);
IntPoint(off + 18).Set(d, a, b, weight);
IntPoint(off + 19).Set(d, a, c, weight);
IntPoint(off + 20).Set(d, b, a, weight);
IntPoint(off + 21).Set(d, b, c, weight);
IntPoint(off + 22).Set(d, c, a, weight);
IntPoint(off + 23).Set(d, c, b, weight);
}
public:
IntegrationRule() :
Array<IntegrationPoint>() { }
@@ -417,6 +450,14 @@ public:
static int CheckOpen(int type);
};
/// Simplex (triangle/tetrahedron) quadrature rule selection.
enum class SimplexQuadrature
{
Legacy = 0, ///< Original MFEM rules (may have negative weights)
PositiveWeights = 1, ///< Positive-weight interior-point rules for simplices
Default = PositiveWeights
};
/// Container class for integration rules
class IntegrationRules
{
@@ -426,6 +467,9 @@ private:
/// segment, square, and cube geometries
const int quad_type;
/// Determines which simplex (triangle/tetrahedron) quadrature rules to use
const SimplexQuadrature simplex_type;
int own_rules, refined;
Array<IntegrationRule *> PointIntRules;
@@ -471,11 +515,17 @@ private:
IntegrationRule *PrismIntegrationRule(int Order);
IntegrationRule *CubeIntegrationRule(int Order);
/// Witherden-Vincent triangle quadrature rules (all-positive weights)
IntegrationRule *TrianglePositiveIntegrationRule(int Order);
/// Witherden-Vincent and Chuluunbaatar tetrahedron quadrature rules
IntegrationRule *TetrahedronPositiveIntegrationRule(int Order);
public:
/// Sets initial sizes for the integration rule arrays, but rules
/// are defined the first time they are requested with the Get method.
explicit IntegrationRules(int ref = 0,
int type = Quadrature1D::GaussLegendre);
int type = Quadrature1D::GaussLegendre,
SimplexQuadrature stype = SimplexQuadrature::Default);
/// Returns an integration rule for given GeomType and Order.
const IntegrationRule &Get(int GeomType, int Order);
+33
View File
@@ -1568,6 +1568,39 @@ PLBound ParGridFunction::GetBounds(Vector &lower, Vector &upper,
return plb;
}
std::pair<real_t, real_t> ParGridFunction::EstimateFunctionMinimum(
const int vdim, const PLBound &plb, const int max_depth,
const real_t tol) const
{
std::pair<real_t, real_t> minmax =
GridFunction::EstimateFunctionMinimum(vdim, plb, max_depth, tol);
real_t glob_min_lower = minmax.first;
real_t glob_min_upper = minmax.second;
MPI_Allreduce(MPI_IN_PLACE, &glob_min_lower, 1,
MFEM_MPI_REAL_T, MPI_MIN, pfes->GetComm());
MPI_Allreduce(MPI_IN_PLACE, &glob_min_upper, 1,
MFEM_MPI_REAL_T, MPI_MIN, pfes->GetComm());
return std::make_pair(glob_min_lower, glob_min_upper);
}
std::pair<real_t, real_t> ParGridFunction::EstimateFunctionMaximum(
const int vdim, const PLBound &plb, const int max_depth,
const real_t tol) const
{
std::pair<real_t, real_t> minmax =
GridFunction::EstimateFunctionMaximum(vdim, plb, max_depth, tol);
real_t glob_max_lower = minmax.first;
real_t glob_max_upper = minmax.second;
MPI_Allreduce(MPI_IN_PLACE, &glob_max_lower, 1,
MFEM_MPI_REAL_T, MPI_MAX, pfes->GetComm());
MPI_Allreduce(MPI_IN_PLACE, &glob_max_upper, 1,
MFEM_MPI_REAL_T, MPI_MAX, pfes->GetComm());
return std::make_pair(glob_max_lower, glob_max_upper);
}
} // namespace mfem
#endif // MFEM_USE_MPI
+12
View File
@@ -609,6 +609,18 @@ public:
PLBound GetBounds(Vector &lower, Vector &upper,
const int ref_factor=1, const int vdim=-1) const override;
/** @brief Estimate the GridFunction minimum across all elements. */
std::pair<real_t, real_t> EstimateFunctionMinimum(const int vdim,
const PLBound &plb,
const int max_depth,
const real_t tol) const override;
/** @brief Estimate the GridFunction maximum across all elements. */
std::pair<real_t, real_t> EstimateFunctionMaximum(const int vdim,
const PLBound &plb,
const int max_depth,
const real_t tol) const override;
/** Save the local portion of the ParGridFunction. This differs from the
serial GridFunction::Save in that it takes into account the signs of
the local dofs. */
+1 -4
View File
@@ -271,10 +271,7 @@ inline void QuadratureFunction::GetValues(
const int s_offset = qspace->Offset(idx);
const int sl_size = qspace->Offset(idx + 1) - s_offset;
// Make the values matrix memory an alias of the quadrature function memory
Memory<real_t> &values_mem = values.GetMemory();
values_mem.Delete();
values_mem.MakeAlias(GetMemory(), vdim*s_offset, vdim*sl_size);
values.SetSize(vdim, sl_size);
values.MakeRef(GetMemory(), vdim*s_offset, vdim, sl_size);
}
inline void QuadratureFunction::GetValues(
+8 -9
View File
@@ -334,17 +334,16 @@ template<int DIM, int SDIM, int D1D, int Q1D>
QuadratureInterpolator::DetKernelType
QuadratureInterpolator::DetKernels::Kernel()
{
if (DIM == 1)
if constexpr (DIM == 1)
{
if (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
else if (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 2>; }
else if (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 3>; }
else { MFEM_ABORT(""); }
if constexpr (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
else if constexpr (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 2>; }
else if constexpr (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 3>; }
}
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
else if (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
else { MFEM_ABORT(""); }
else if constexpr (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
else if constexpr (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
MFEM_ABORT("");
}
/// @endcond
+4 -4
View File
@@ -203,10 +203,10 @@ template<int DIM, QVectorLayout Q_LAYOUT,
QuadratureInterpolator::TensorEvalKernelType
QuadratureInterpolator::TensorEvalKernels::Kernel()
{
if (DIM == 1) { return internal::quadrature_interpolator::Values1D<Q_LAYOUT>; }
else if (DIM == 2) { return internal::quadrature_interpolator::Values2D<Q_LAYOUT, VDIM, D1D, Q1D, NBZ>; }
else if (DIM == 3) { return internal::quadrature_interpolator::Values3D<Q_LAYOUT, VDIM, D1D, Q1D>; }
else { MFEM_ABORT(""); }
if constexpr (DIM == 1) { return internal::quadrature_interpolator::Values1D<Q_LAYOUT>; }
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::Values2D<Q_LAYOUT, VDIM, D1D, Q1D, NBZ>; }
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Values3D<Q_LAYOUT, VDIM, D1D, Q1D>; }
MFEM_ABORT("");
}
/// @endcond
+9 -2
View File
@@ -453,8 +453,15 @@ QuadratureInterpolator::TensorEvalHDivKernels::Kernel()
{
using namespace internal::quadrature_interpolator;
static_assert(DIM == 2 || DIM == 3, "only DIM=2 and DIM=3 are implemented!");
if (DIM == 2) { return EvalHDiv2D<Q_LAYOUT, FLAGS, D1D, Q1D>; }
return EvalHDiv3D<Q_LAYOUT, FLAGS, D1D, Q1D>;
if constexpr (DIM == 2)
{
return EvalHDiv2D<Q_LAYOUT, FLAGS, D1D, Q1D>;
}
else if constexpr (DIM == 3)
{
return EvalHDiv3D<Q_LAYOUT, FLAGS, D1D, Q1D>;
}
MFEM_ABORT("only DIM=2 and DIM=3 are implemented!");
}
/// @endcond
+8 -8
View File
@@ -592,10 +592,10 @@ template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
QuadratureInterpolator::GradKernelType
QuadratureInterpolator::GradKernels::Kernel()
{
if (DIM == 1) { return internal::quadrature_interpolator::Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
else if (DIM == 2) { return internal::quadrature_interpolator::Derivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D, NBZ>; }
else if (DIM == 3) { return internal::quadrature_interpolator::Derivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D>; }
else { MFEM_ABORT(""); }
if constexpr (DIM == 1) { return internal::quadrature_interpolator::Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::Derivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D, NBZ>; }
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Derivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D>; }
MFEM_ABORT("");
}
template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
@@ -603,10 +603,10 @@ template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
QuadratureInterpolator::CollocatedGradKernelType
QuadratureInterpolator::CollocatedGradKernels::Kernel()
{
if (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
else if (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
else if (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
else { MFEM_ABORT(""); }
if constexpr (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
MFEM_ABORT("");
}
/// @endcond
+4 -4
View File
@@ -752,10 +752,10 @@ template <int DIM, int VDIM, int ND, int NQ>
EvalKernel QuadratureInterpolator::EvalKernels::Kernel()
{
using namespace internal::quadrature_interpolator;
if (DIM == 1) { return Eval1D; }
else if (DIM == 2) { return Eval2D<VDIM,ND,NQ>; }
else if (DIM == 3) { return Eval3D<VDIM,ND,NQ>; }
else { MFEM_ABORT(""); }
if constexpr (DIM == 1) { return Eval1D; }
else if constexpr (DIM == 2) { return Eval2D<VDIM,ND,NQ>; }
else if constexpr (DIM == 3) { return Eval3D<VDIM,ND,NQ>; }
MFEM_ABORT("");
}
template <int DIM>
+5 -2
View File
@@ -4102,8 +4102,11 @@ void TMOP_Integrator::GetSurfaceFittingErrors(const Vector &d_loc,
#ifdef MFEM_USE_MPI
// Don't count the overlapping DOFs in parallel.
// The pfes might be ordered byVDIM, while the loop goes consecutively.
const int dof_i = pfes->DofToVDof(i, 0);
if (parallel && pfes->GetLocalTDofNumber(dof_i) < 0) { continue; }
if (parallel)
{
const int dof_i = pfes->DofToVDof(i, 0);
if (pfes->GetLocalTDofNumber(dof_i) < 0) { continue; }
}
#endif
dof_cnt++;
+281 -16
View File
@@ -2057,6 +2057,10 @@ TransferOperator::TransferOperator(const FiniteElementSpace& lFESpace_,
: Operator(hFESpace_.GetVSize(), lFESpace_.GetVSize())
{
bool isvar_order = lFESpace_.IsVariableOrder() || hFESpace_.IsVariableOrder();
bool is_trace_space =
(dynamic_cast<const H1_Trace_FECollection*>(lFESpace_.FEColl()) ||
dynamic_cast<const ND_Trace_FECollection*>(lFESpace_.FEColl()) ||
dynamic_cast<const RT_Trace_FECollection*>(lFESpace_.FEColl()));
if (lFESpace_.FEColl() == hFESpace_.FEColl() && !isvar_order)
{
OperatorPtr P(Operator::ANY_TYPE);
@@ -2066,6 +2070,7 @@ TransferOperator::TransferOperator(const FiniteElementSpace& lFESpace_,
}
else if (lFESpace_.GetVDim() == 1
&& hFESpace_.GetVDim() == 1
&& !is_trace_space
&& dynamic_cast<const TensorBasisElement*>(lFESpace_.GetTypicalFE())
&& dynamic_cast<const TensorBasisElement*>(hFESpace_.GetTypicalFE())
&& !isvar_order
@@ -2096,15 +2101,244 @@ void TransferOperator::MultTranspose(const Vector& x, Vector& y) const
PRefinementTransferOperator::PRefinementTransferOperator(
const FiniteElementSpace& lFESpace_, const FiniteElementSpace& hFESpace_)
const FiniteElementSpace& lFESpace_, const FiniteElementSpace& hFESpace_,
bool assemble_matrix)
: Operator(hFESpace_.GetVSize(), lFESpace_.GetVSize()), lFESpace(lFESpace_),
hFESpace(hFESpace_)
{
isvar_order = lFESpace_.IsVariableOrder() || hFESpace_.IsVariableOrder();
MFEM_VERIFY(lFESpace.FEColl()->GetContType() ==
hFESpace.FEColl()->GetContType(),
"Incompatible finite element space continuity types.");
is_trace_space =
(dynamic_cast<const H1_Trace_FECollection*>(lFESpace.FEColl()) ||
dynamic_cast<const ND_Trace_FECollection*>(lFESpace.FEColl()) ||
dynamic_cast<const RT_Trace_FECollection*>(lFESpace.FEColl()));
if (assemble_matrix) { AssembleMatrix(); }
}
void PRefinementTransferOperator::AssembleMatrix()
{
Mesh* mesh = hFESpace.GetMesh();
const int nL = lFESpace.GetVSize();
const int nH = hFESpace.GetVSize();
P.reset(new SparseMatrix(nH, nL));
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
DenseMatrix loc_prol;
Geometry::Type cached_geom = Geometry::INVALID;
const FiniteElement* h_fe = nullptr;
const FiniteElement* l_fe = nullptr;
IsoparametricTransformation T;
int vdim = lFESpace.GetVDim();
const int iend = (is_trace_space) ? mesh->GetNumFaces() : mesh->GetNE();
DofTransformation doftrans_h, doftrans_l;
Vector w(nH); w = 0.0;
for (int i = 0; i < iend; i++)
{
if (is_trace_space)
{
hFESpace.GetFaceDofs(i, h_dofs);
lFESpace.GetFaceDofs(i, l_dofs);
}
else
{
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
}
const Geometry::Type geom = (is_trace_space) ? mesh->GetFaceGeometry(i)
: mesh->GetElementBaseGeometry(i);
if (geom != cached_geom || isvar_order)
{
h_fe = (is_trace_space) ? hFESpace.GetFaceElement(i) : hFESpace.GetFE(i);
l_fe = (is_trace_space) ? lFESpace.GetFaceElement(i) : lFESpace.GetFE(i);
T.SetIdentityTransformation(h_fe->GetGeomType());
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
cached_geom = geom;
}
DenseMatrix Aeff(loc_prol);
TransformPrimal(doftrans_h, doftrans_l, Aeff);
for (int vd = 0; vd < vdim; vd++)
{
l_dofs.Copy(l_vdofs);
lFESpace.DofsToVDofs(vd, l_vdofs);
h_dofs.Copy(h_vdofs);
hFESpace.DofsToVDofs(vd, h_vdofs);
Aeff.AdjustDofDirection(h_vdofs, l_vdofs);
P->AddSubMatrix(h_vdofs, l_vdofs, Aeff);
for (int rr = 0; rr < h_vdofs.Size(); rr++)
{
w(h_vdofs[rr]) += 1.0;
}
}
}
P->Finalize();
Vector inv_w(nH);
for (int i = 0; i < nH; i++)
{
inv_w(i) = (w(i) > 0.0) ? (1.0 / w(i)) : 1.0;
}
P->ScaleRows(inv_w);
assembled = true;
}
std::unique_ptr<SparseMatrix>
PRefinementTransferOperator::BuildConformingTransferMatrix() const
{
MFEM_VERIFY(assembled && P, "Matrix path requires assembled P.");
const SparseMatrix *Pl = lFESpace.GetConformingProlongation();
const SparseMatrix *Rh = hFESpace.GetRestrictionMatrix();
if (Pl && Rh)
{
SparseMatrix *RhP = mfem::Mult(*Rh, *P);
SparseMatrix *RhPPl = mfem::Mult(*RhP, *Pl);
delete RhP;
return std::unique_ptr<SparseMatrix>(RhPPl);
}
else if (Pl)
{
return std::unique_ptr<SparseMatrix>(mfem::Mult(*P, *Pl));
}
else if (Rh)
{
return std::unique_ptr<SparseMatrix>(mfem::Mult(*Rh, *P));
}
else
{
return std::make_unique<SparseMatrix>(*P);
}
}
std::unique_ptr<Operator>
PRefinementTransferOperator::BuildConformingTransferOperator() const
{
const Operator *Pl = lFESpace.GetProlongationMatrix();
const Operator *Rh = hFESpace.GetRestrictionOperator();
if (Pl && Rh)
{
return std::make_unique<TripleProductOperator>(Rh,
const_cast<PRefinementTransferOperator*>(this), Pl,
false, false, false);
}
else if (Pl)
{
return std::make_unique<ProductOperator>
(const_cast<PRefinementTransferOperator*>(this), Pl,
false, false);
}
else if (Rh)
{
return std::make_unique<ProductOperator>(Rh,
const_cast<PRefinementTransferOperator*>(this),
false, false);
}
else
{
// return nullptr to mean "identity/no-op wrapper", i.e. use `this`
return nullptr;
}
}
Operator *
PRefinementTransferOperator::GetTrueTransferOperator()
{
if (tP) { return tP.get(); }
#ifdef MFEM_USE_MPI
const ParFiniteElementSpace* lpfes = dynamic_cast<const ParFiniteElementSpace*>
(&lFESpace);
const ParFiniteElementSpace* hpfes = dynamic_cast<const ParFiniteElementSpace*>
(&hFESpace);
bool parallel = (lpfes) && (hpfes);
if (parallel)
{
if (assembled)
{
HypreParMatrix * Pl = lpfes->Dof_TrueDof_Matrix();
const SparseMatrix * Rh = hpfes->GetRestrictionMatrix();
// Rh * P
SparseMatrix * RhP = mfem::Mult(*Rh, *P);
HypreParMatrix * RhPh = new HypreParMatrix(hpfes->GetComm(),
hpfes->GlobalTrueVSize(), lpfes->GlobalVSize(),
hpfes->GetTrueDofOffsets(), lpfes->GetDofOffsets(), RhP);
HypreStealOwnership(*RhPh, *RhP);
delete RhP;
HypreParMatrix * tmp = ParMult(RhPh, Pl, true);
delete RhPh;
tP.reset(tmp);
return tP.get();
}
else
{
auto Pl = lpfes->GetProlongationMatrix();
auto Rh = hpfes->GetRestrictionOperator();
tP = std::make_unique<TripleProductOperator>(Rh, this, Pl, false, false, false);
return tP.get();
}
}
else
{
if (assembled)
{
auto M = BuildConformingTransferMatrix();
tP.reset(M.release());
return tP.get();
}
else
{
tP = BuildConformingTransferOperator();
return tP ? tP.get() : this;
}
}
#else
{
if (assembled)
{
auto M = BuildConformingTransferMatrix();
tP.reset(M.release());
return tP.get();
}
else
{
tP = BuildConformingTransferOperator();
return tP ? tP.get() : this;
}
}
#endif
}
void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
{
y = 0.0;
if (assembled) { P->Mult(x, y); return; }
Mesh* mesh = hFESpace.GetMesh();
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
DenseMatrix loc_prol;
@@ -2117,19 +2351,31 @@ void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
int vdim = lFESpace.GetVDim();
y = 0.0;
DofTransformation doftrans_h, doftrans_l;
for (int i = 0; i < mesh->GetNE(); i++)
{
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
int iend = (is_trace_space) ? mesh->GetNumFaces() : mesh->GetNE();
for (int i = 0; i < iend; i++)
{
if (is_trace_space)
{
hFESpace.GetFaceDofs(i, h_dofs);
lFESpace.GetFaceDofs(i, l_dofs);
}
else
{
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
}
const Geometry::Type geom = (is_trace_space) ? mesh->GetFaceGeometry(i)
: mesh->GetElementBaseGeometry(i);
if (geom != cached_geom || isvar_order)
{
h_fe = hFESpace.GetFE(i);
l_fe = lFESpace.GetFE(i);
h_fe = (is_trace_space) ? hFESpace.GetFaceElement(i) : hFESpace.GetFE(i);
l_fe = (is_trace_space) ? lFESpace.GetFaceElement(i) : lFESpace.GetFE(i);
T.SetIdentityTransformation(h_fe->GetGeomType());
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
subY.SetSize(loc_prol.Height());
@@ -2144,6 +2390,7 @@ void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
hFESpace.DofsToVDofs(vd, h_vdofs);
x.GetSubVector(l_vdofs, subX);
doftrans_l.InvTransformPrimal(subX);
loc_prol.Mult(subX, subY);
doftrans_h.TransformPrimal(subY);
y.SetSubVector(h_vdofs, subY);
@@ -2156,6 +2403,12 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
{
y = 0.0;
if (assembled)
{
P->MultTranspose(x, y);
return;
}
Mesh* mesh = hFESpace.GetMesh();
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
DenseMatrix loc_prol;
@@ -2173,16 +2426,28 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
DofTransformation doftrans_h, doftrans_l;
for (int i = 0; i < mesh->GetNE(); i++)
{
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
int iend = (is_trace_space) ? mesh->GetNumFaces() : mesh->GetNE();
for (int i = 0; i < iend; i++)
{
if (is_trace_space)
{
hFESpace.GetFaceDofs(i, h_dofs);
lFESpace.GetFaceDofs(i, l_dofs);
}
else
{
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
}
const Geometry::Type geom = (is_trace_space) ? mesh->GetFaceGeometry(i)
: mesh->GetElementBaseGeometry(i);
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
if (geom != cached_geom || isvar_order)
{
h_fe = hFESpace.GetFE(i);
l_fe = lFESpace.GetFE(i);
h_fe = (is_trace_space) ? hFESpace.GetFaceElement(i) : hFESpace.GetFE(i);
l_fe = (is_trace_space) ? lFESpace.GetFaceElement(i) : lFESpace.GetFE(i);
T.SetIdentityTransformation(h_fe->GetGeomType());
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
loc_prol.Transpose();
+20 -1
View File
@@ -569,6 +569,13 @@ private:
const FiniteElementSpace& lFESpace;
const FiniteElementSpace& hFESpace;
bool isvar_order;
bool is_trace_space;
bool assembled = false;
std::unique_ptr<SparseMatrix> P;
mutable std::unique_ptr<Operator> tP;
std::unique_ptr<SparseMatrix> BuildConformingTransferMatrix() const;
std::unique_ptr<Operator> BuildConformingTransferOperator() const;
public:
/// @brief Constructs a transfer operator from \p lFESpace to \p hFESpace
@@ -577,11 +584,23 @@ public:
The underlying finite elements need to implement the GetTransferMatrix
methods. */
PRefinementTransferOperator(const FiniteElementSpace& lFESpace_,
const FiniteElementSpace& hFESpace_);
const FiniteElementSpace& hFESpace_,
bool assemble_matrix = false);
Operator * GetTrueTransferOperator();
const Operator * GetTrueTransferOperator() const
{
return const_cast<PRefinementTransferOperator*>(this)
->GetTrueTransferOperator();
}
/// Destructor
virtual ~PRefinementTransferOperator() { }
void AssembleMatrix();
/// @brief Interpolation or prolongation of a vector \p x corresponding to
/// the coarse space to the vector \p y corresponding to the fine space.
void Mult(const Vector& x, Vector& y) const override;
+24 -1
View File
@@ -114,10 +114,22 @@ public:
Array<T> &operator=(const Array<T> &src) { src.Copy(*this); return *this; }
/// Move assignment operator
/** If *this is a non-owning view (e.g., from MakeRef()), the data is copied
so that the base is also modified. */
Array<T> &operator=(Array<T> &&src)
{
if (this == &src) { return *this; }
Swap(src); // Swap does not use move assignment!
// If *this is a non-owning view (alias), and its capacity is sufficient
// to contain src, then copy into *this so that the alias's base memory is
// modified.
if (!OwnsData() && Capacity() >= src.Size())
{
*this = src; // Copy assignment.
}
else
{
Swap(src); // Swap the pointers only.
}
src.DeleteAll();
return *this;
}
@@ -251,6 +263,9 @@ public:
/// Make this Array a reference to 'master'.
inline void MakeRef(const Array &master);
/// Make this Array a reference to the given sub-Memory of @a base.
inline void MakeRef(Memory<T> &base, int offset, int size_);
/// Reset the Array to use the given external Memory @a mem and size @a s.
/** If @a own_mem is false, the Array will not own any of the pointers of
@a mem.
@@ -1073,6 +1088,14 @@ inline void Array<T>::MakeRef(const Array &master)
data.MakeAlias(master.GetMemory(), 0, size);
}
template <class T>
inline void Array<T>::MakeRef(Memory<T> &base, int offset, int size_)
{
data.Delete();
size = size_;
data.MakeAlias(base, offset, size_);
}
template <class T>
inline void Array<T>::NewMemoryAndSize(
const Memory<T> &mem, int s, bool own_mem)
+186 -1
View File
@@ -14,6 +14,7 @@
#include "blockvector.hpp"
#include "blockoperator.hpp"
namespace mfem
{
@@ -129,6 +130,33 @@ void BlockOperator::MultTranspose(const Vector &x, Vector &y) const
}
}
#ifdef MFEM_USE_MPI
HypreParMatrix * BlockOperator::GetMonolithicHypreParMatrix(
Array2D<real_t> *blockCoeff) const
{
Array2D<const HypreParMatrix*> blocks(nRowBlocks, nColBlocks);
for (int i = 0; i < nRowBlocks; ++i)
{
for (int j = 0; j < nColBlocks; ++j)
{
if (IsZeroBlock(i, j))
{
blocks(i, j) = nullptr;
}
else
{
auto mat = dynamic_cast<const HypreParMatrix*>(&GetBlock(i, j));
MFEM_VERIFY(mat,"BlockOperator block (" << i << "," << j
<< ") is not a HypreParMatrix.");
blocks(i, j) = mat;
}
}
}
return HypreParMatrixFromBlocks(blocks, blockCoeff);
}
#endif
BlockOperator::~BlockOperator()
{
if (owns_blocks)
@@ -381,4 +409,161 @@ BlockLowerTriangularPreconditioner::~BlockLowerTriangularPreconditioner()
}
}
} // namespace mfem
BlockTriangularSymmetricPreconditioner::BlockTriangularSymmetricPreconditioner(
const Array<int> & offsets_)
: Solver(offsets_.Last()),
owns_blocks(0),
nBlocks(offsets_.Size() - 1),
offsets(0),
ops(nBlocks, nBlocks),
coef(nBlocks, nBlocks)
{
ops = static_cast<Operator *>(NULL);
offsets.MakeRef(offsets_);
}
void BlockTriangularSymmetricPreconditioner::SetDiagonalBlock(int iblock,
Operator *op, real_t c)
{
MFEM_VERIFY(offsets[iblock+1] - offsets[iblock] == op->Height() &&
offsets[iblock+1] - offsets[iblock] == op->Width(),
"incompatible Operator dimensions");
SetBlock(iblock, iblock, op);
coef(iblock, iblock) = c;
}
void BlockTriangularSymmetricPreconditioner::SetBlock(int iRow, int iCol,
Operator *op, real_t c)
{
MFEM_VERIFY(offsets[iRow+1] - offsets[iRow] == op->NumRows() &&
offsets[iCol+1] - offsets[iCol] == op->NumCols(),
"incompatible Operator dimensions");
ops(iRow, iCol) = op;
coef(iRow, iCol) = c;
}
// Operator application
void BlockTriangularSymmetricPreconditioner::ForwardPass(const Vector & x,
Vector & y) const
{
// Forward sweep: Solve for y1, then y2
for (int iRow = 0; iRow < nBlocks; ++iRow)
{
tmp.SetSize(offsets[iRow + 1] - offsets[iRow]);
tmp2.SetSize(offsets[iRow + 1] - offsets[iRow]);
tmp2 = 0.0;
tmp2 += xblock.GetBlock(iRow); // tmp2 = xblock(iRow)
// Process the lower triangular part (jCol < iRow)
for (int jCol = 0; jCol < iRow; ++jCol)
{
if (ops(iRow, jCol))
{
ops(iRow, jCol)->Mult(yblock.GetBlock(jCol),
tmp); // tmp = A(iRow,jCol) * yblock(jCol)
tmp*= coef(iRow,jCol); // tmp *= c
tmp2 -= tmp; // tmp2 -= A(iRow, jCol) * yblock(jCol)
}
}
// Apply the diagonal block
if (ops(iRow, iRow))
{
ops(iRow, iRow)->Mult(tmp2,
yblock.GetBlock(iRow));
yblock.GetBlock(iRow) *= coef(iRow,
iRow); // yblock(iRow) = A(iRow,iRow)^-1 * tmp2
}
else
{
yblock.GetBlock(iRow) = tmp2; // If no diagonal operator, set yblock directly
}
}
}
void BlockTriangularSymmetricPreconditioner::BackwardPass(const Vector & x,
Vector & y) const
{
// Backward sweep: Adjust y1 based on y2
for (int iRow = nBlocks - 1; iRow >= 0; --iRow)
{
tmp.SetSize(offsets[iRow + 1] - offsets[iRow]);
tmp2.SetSize(offsets[iRow + 1] - offsets[iRow]);
tmp2 = 0.0;
tmp2 += xblock.GetBlock(iRow); // tmp2 = yblock(iRow) from forward sweep
// Process the upper triangular part (jCol > iRow)
for (int jCol = iRow + 1; jCol < nBlocks; ++jCol)
{
if (ops(iRow, jCol))
{
ops(iRow, jCol)->Mult(yblock.GetBlock(jCol),
tmp); // tmp = A(iRow,jCol) * yblock(jCol)
tmp *= coef(iRow,jCol); // tmp *= c
tmp2 -= tmp; // tmp2 -= A(iRow,jCol) * yblock(jCol)
}
}
// Reapply diagonal block to correct y1
if (ops(iRow, iRow))
{
ops(iRow, iRow)->Mult(tmp2,
yblock.GetBlock(iRow)); // Final correction for yblock(iRow)
yblock.GetBlock(iRow) *= coef(iRow,
iRow); // yblock(iRow) = A(iRow,iRow)^-1 * tmp2
}
else
{
yblock.GetBlock(iRow) = tmp2; // If no diagonal operator, set yblock directly
}
}
}
void BlockTriangularSymmetricPreconditioner::Mult(const Vector & x,
Vector & y) const
{
MFEM_ASSERT(x.Size() == width, "incorrect input Vector size");
MFEM_ASSERT(y.Size() == height, "incorrect output Vector size");
// Update block views of the vectors y and x using offsets
yblock.Update(y.GetData(), offsets);
xblock.Update(x.GetData(), offsets);
// Initialize y to zero
y = 0.0;
ForwardPass(x,y);
// Update Residual
r.SetSize(x.Size());
r = 0.0; r+=x;
Op->AddMult(y,r,-1.0);
Vector y1(y);
yblock.Update(y1.GetData(), offsets);
xblock.Update(r.GetData(), offsets);
BackwardPass(r,y1);
y+=y1;
}
BlockTriangularSymmetricPreconditioner::~BlockTriangularSymmetricPreconditioner()
{
if (owns_blocks)
{
for (int iRow=0; iRow < nBlocks; ++iRow)
{
for (int jCol=0; jCol < nBlocks; ++jCol)
{
delete ops(jCol,iRow);
}
}
}
}
}
+86
View File
@@ -16,6 +16,9 @@
#include "../general/array.hpp"
#include "operator.hpp"
#include "blockvector.hpp"
#ifdef MFEM_USE_MPI
#include "hypre.hpp"
#endif
namespace mfem
{
@@ -105,6 +108,13 @@ public:
/// Action of the transpose operator
void MultTranspose (const Vector & x, Vector & y) const override;
#ifdef MFEM_USE_MPI
/// Returns a monolithic HypreParMatrix formed by merging the blocks of
// this BlockOperator, assuming every block is a HypreParMatrix.
HypreParMatrix * GetMonolithicHypreParMatrix(Array2D<real_t> *blockCoeff=NULL)
const;
#endif
~BlockOperator();
//! Controls the ownership of the blocks: if nonzero, BlockOperator will
@@ -288,6 +298,82 @@ private:
mutable Vector tmp2;
};
class BlockTriangularSymmetricPreconditioner : public Solver
{
private:
const Operator * Op;
public:
//! Constructor for BlockTriangularSymmetricPreconditioners with the same
//! block-structure for rows and columns.
/**
* @param offsets Offsets that mark the start of each row/column block
* (size nBlocks+1).
*
* @note BlockTriangularSymmetricPreconditioner will not own/copy the data
* contained in @a offsets.
*/
BlockTriangularSymmetricPreconditioner(const Array<int> & offsets);
//! Add block op in the block-entry (iblock, iblock).
/**
* @param iblock The block will be inserted in location (iblock, iblock).
* @param op The Operator to be inserted.
* @param c The coefficient to be applied to the block.
*/
void SetDiagonalBlock(int iblock, Operator *op, real_t c = 1.0);
//! Add a block opt in the block-entry (iblock, jblock).
/**
* @param iRow, iCol The block will be inserted in location (iRow, iCol).
* @param op The Operator to be inserted.
* @param c The coefficient to be applied to the block.
*/
void SetBlock(int iRow, int iCol, Operator *op, real_t c = 1.0);
//! This method is present since required by the abstract base class Solver
virtual void SetOperator(const Operator &op) {Op = &op;}
//! Return the number of blocks
int NumBlocks() const { return nBlocks; }
//! Return a reference to block i,j.
Operator & GetBlock(int iblock, int jblock)
{ MFEM_VERIFY(ops(iblock,jblock), ""); return *ops(iblock,jblock); }
Operator & GetDiagonalBlock(int iblock)
{ MFEM_VERIFY(ops(iblock,iblock), ""); return *ops(iblock,iblock); }
//! Return the offsets for block starts
Array<int> & Offsets() { return offsets; }
/// Operator application
virtual void Mult (const Vector & x, Vector & y) const;
~BlockTriangularSymmetricPreconditioner();
//! Controls the ownership of the blocks: if nonzero,
//! BlockTriangularSymmetricPreconditioner will delete all blocks that are set
//! (non-NULL); the default value is zero.
int owns_blocks;
private:
//! Number of block rows/columns
int nBlocks;
//! Offsets for the starting position of each block
Array<int> offsets;
//! 2D array that stores each block of the operator.
Array2D<Operator *> ops;
Array2D<real_t> coef;
//! Temporary Vectors used to efficiently apply the Mult and MultTranspose
//! methods.
mutable BlockVector xblock;
mutable BlockVector yblock;
mutable Vector tmp;
mutable Vector tmp2;
mutable Vector r;
void ForwardPass(const Vector & x, Vector & y) const;
void BackwardPass(const Vector & x, Vector & y) const;
};
}
#endif /* MFEM_BLOCKOPERATOR */
+939 -1
View File
@@ -10,8 +10,15 @@
// CONTRIBUTING.md for details.
#include "complex_operator.hpp"
#include "../general/communication.hpp"
#ifdef MFEM_USE_MPI
#include "blockoperator.hpp"
#endif
#include <set>
#include <map>
#include <unordered_map>
#include <vector>
#include <limits>
namespace mfem
{
@@ -164,6 +171,51 @@ void ComplexOperator::MultTranspose(const Vector &x_r, const Vector &x_i,
}
}
#ifdef MFEM_USE_MPI
ComplexHypreParMatrix * ComplexOperator::AsComplexHypreParMatrix() const
{
HypreParMatrix *Ar = nullptr;
HypreParMatrix *Ai = nullptr;
bool own_r = false;
bool own_i = false;
if (auto *Ahr = dynamic_cast<const HypreParMatrix*>(&real()))
{
Ar = const_cast<HypreParMatrix*>(Ahr);
}
else if (auto *Br = dynamic_cast<const BlockOperator*>(&real()))
{
Ar = Br->GetMonolithicHypreParMatrix();
own_r = true;
}
else
{
MFEM_ABORT("Real part is neither HypreParMatrix nor BlockOperator.");
}
if (auto *Ahi = dynamic_cast<const HypreParMatrix*>(&imag()))
{
Ai = const_cast<HypreParMatrix*>(Ahi);
}
else if (auto *Bi = dynamic_cast<const BlockOperator*>(&imag()))
{
Ai = Bi->GetMonolithicHypreParMatrix();
own_i = true;
}
else
{
MFEM_ABORT("Imag part is neither HypreParMatrix nor BlockOperator.");
}
return new ComplexHypreParMatrix(Ar, Ai, own_r, own_i, GetConvention());
}
#endif
SparseMatrix & ComplexSparseMatrix::real()
{
@@ -877,6 +929,892 @@ ComplexHypreParMatrix::getColStartStop(const HypreParMatrix * A_r,
delete [] stat;
}
#endif // MFEM_USE_MPI
static Array<int> Twice(const Array<int> &offs)
{
Array<int> arrayout(offs.Size());
for (int i = 0; i < offs.Size(); i++) { arrayout[i] = 2 * offs[i]; }
return arrayout;
}
ComplexBlockOperator::ComplexBlockOperator(const ComplexOperator &A)
: BlockOperator(
Twice(dynamic_cast<const BlockOperator&>(A.real()).RowOffsets()),
Twice(dynamic_cast<const BlockOperator&>(A.real()).ColOffsets()))
{
const BlockOperator *Ar = dynamic_cast<const BlockOperator*>(&A.real());
const BlockOperator *Ai = dynamic_cast<const BlockOperator*>(&A.imag());
MFEM_VERIFY(Ar && Ai,
"ComplexBlockOperator: expected ComplexOperator with BlockOperator Re/Im.");
MFEM_VERIFY(Ar->NumRowBlocks() == Ai->NumRowBlocks() &&
Ar->NumColBlocks() == Ai->NumColBlocks(),
"ComplexBlockOperator: Re/Im block layouts mismatch.");
// Populate this BlockOperator (base) with ComplexOperator blocks.
for (int i = 0; i < Ar->NumRowBlocks(); ++i)
{
for (int j = 0; j < Ar->NumColBlocks(); ++j)
{
HypreParMatrix *Rij = nullptr;
if (!Ar->IsZeroBlock(i, j))
{
Rij = const_cast<HypreParMatrix*>
(dynamic_cast<const HypreParMatrix*>(&Ar->GetBlock(i, j)));
}
HypreParMatrix *Iij = nullptr;
if (!Ai->IsZeroBlock(i, j))
{
Iij = const_cast<HypreParMatrix*>
(dynamic_cast<const HypreParMatrix*>(&Ai->GetBlock(i, j)));
}
// MFEM_VERIFY((Rij && Iij) || (!Rij && !Iij),
// "ComplexBlockOperator: inconsistent sparsity at block ("
// << i << "," << j << ").");
if (Rij)
{
auto *Cij = new ComplexHypreParMatrix(Rij, Iij, false, false,
A.GetConvention());
SetBlock(i, j, Cij);
}
}
}
}
void ComplexBlockOperator::BlockComplexToComplexBlock(const Vector &xin,
Vector &xout) const
{
MFEM_VERIFY(xout.Size() == xin.Size(),
"BlockComplexToComplexBlock: size mismatch (xout != xin).");
MFEM_VERIFY(xin.Size() % 2 == 0,
"BlockComplexToComplexBlock: expected even-sized vector (2*N).");
// Decide whether xin is domain-sized or range-sized by matching doubled offsets.
const int twoNcols = ColOffsets().Last();
const int twoNrows = RowOffsets().Last();
const Array<int> *doffs = nullptr; // doubled offsets to use
if (xin.Size() == twoNcols) { doffs = &ColOffsets(); }
else if (xin.Size() == twoNrows) { doffs = &RowOffsets(); }
else
{
MFEM_ABORT("BlockComplexToComplexBlock: vector size does not match "
"either doubled domain or range size.");
}
// xin layout: [ Re(all 0..N-1), Im(all 0..N-1) ], where N = doffs->Last()/2
const int N = doffs->Last() / 2;
int pos = 0; // position in per-block layout (xout), but note xout is also size 2*N
for (int b = 0; b < doffs->Size() - 1; b++)
{
// Doubled block segment in this BlockOperator is [(*doffs)[b], (*doffs)[b+1])
// The *base* (undoubled) start/length are:
const int s_base = (*doffs)[b] / 2;
const int len = ((*doffs)[b+1] - (*doffs)[b]) / 2;
// Write per-block Re then Im, contiguous
for (int k = 0; k < len; k++) { xout[pos + k] = xin[s_base + k]; }
for (int k = 0; k < len; k++) { xout[pos + len + k] = xin[N + s_base + k]; }
pos += 2 * len;
}
}
void ComplexBlockOperator::ComplexBlockToBlockComplex(const Vector &xin,
Vector &xout) const
{
MFEM_VERIFY(xout.Size() == xin.Size(),
"ComplexBlockToBlockComplex: size mismatch (xout != xin).");
MFEM_VERIFY(xin.Size() % 2 == 0,
"ComplexBlockToBlockComplex: expected even-sized vector (2*N).");
const int Ncols = ColOffsets().Last();
const int Nrows = RowOffsets().Last();
const Array<int> *doffs = nullptr;
if (xin.Size() == Ncols) { doffs = &ColOffsets(); }
else if (xin.Size() == Nrows) { doffs = &RowOffsets(); }
else
{
MFEM_ABORT("ComplexBlockToBlockComplex: vector size does not match "
"either doubled domain or range size.");
}
const int N = doffs->Last() / 2;
int pos = 0; // position in per-block layout (xin)
for (int b = 0; b < doffs->Size() - 1; b++)
{
const int s_base = (*doffs)[b] / 2;
const int len = ((*doffs)[b+1] - (*doffs)[b]) / 2;
// Read per-block Re then Im, scatter to stacked [Re(all); Im(all)]
for (int k = 0; k < len; k++) { xout[s_base + k] = xin[pos + k]; }
for (int k = 0; k < len; k++) { xout[N + s_base + k] = xin[pos + len + k]; }
pos += 2 * len;
}
}
#ifdef MFEM_USE_COMPLEX_MUMPS
// Macro so indices match MUMPS documentation
#define MUMPS_ICNTL(I) icntl[(I) - 1]
#define MUMPS_CNTL(I) cntl[(I) - 1]
#define MUMPS_INFO(I) info[(I) - 1]
#define MUMPS_INFOG(I) infog[(I) - 1]
ComplexMUMPSSolver::ComplexMUMPSSolver(MPI_Comm comm_)
{
Init(comm_);
}
ComplexMUMPSSolver::ComplexMUMPSSolver(const Operator &op)
{
auto APtr = dynamic_cast<const ComplexHypreParMatrix *>(&op);
MFEM_VERIFY(APtr, "Not a compatible matrix type for ComplexMUMPSSolver");
SetOperator(op);
}
void ComplexMUMPSSolver::Init(MPI_Comm comm_)
{
comm = comm_;
MPI_Comm_size(comm, &numProcs);
MPI_Comm_rank(comm, &myid);
print_level = 2;
row_start = 0;
id = nullptr;
#if MFEM_MUMPS_VERSION >= 530
irhs_loc = nullptr;
isol_loc = nullptr;
rhs_loc = nullptr;
sol_loc = nullptr;
#else
global_num_rows = 0;
recv_counts = nullptr;
displs = nullptr;
rhs_glob = nullptr;
rhs_glob_r = nullptr;
rhs_glob_i = nullptr;
#endif
}
ComplexMUMPSSolver::~ComplexMUMPSSolver()
{
#if MFEM_MUMPS_VERSION >= 530
delete [] irhs_loc;
delete [] isol_loc;
delete [] rhs_loc;
delete [] sol_loc;
#else
delete [] recv_counts;
delete [] displs;
delete [] rhs_glob;
delete [] rhs_glob_r;
delete [] rhs_glob_i;
#endif
if (id)
{
id->job = -2;
mumps_call();
delete id;
id = nullptr;
}
}
void ComplexMUMPSSolver::SetOperator(const Operator &op)
{
auto APtr = dynamic_cast<const ComplexHypreParMatrix *>(&op);
MFEM_VERIFY(APtr, "Not compatible matrix type for ComplexMUMPSSolver");
height = op.Height();
width = op.Width();
const HypreParMatrix *Ar = (APtr->hasRealPart()) ? &APtr->real() : nullptr;
const HypreParMatrix *Ai = (APtr->hasImagPart()) ? &APtr->imag() : nullptr;
MFEM_VERIFY(Ar || Ai, "ComplexMUMPSSolver: both real and imag parts are null.");
// Pick communicator from the non-null part
MPI_Comm op_comm = (Ar ? Ar->GetComm() : Ai->GetComm());
// Comm setup/check
if (comm == MPI_COMM_NULL) { Init(op_comm); }
else
{
int cmp = MPI_UNEQUAL;
MPI_Comm_compare(comm, op_comm, &cmp);
MFEM_VERIFY(cmp != MPI_UNEQUAL, "MPI Comm mismatch");
}
// HostRead only if non-null
if (Ar) { Ar->HostRead(); }
if (Ai) { Ai->HostRead(); }
// hypre parcsr pointers
hypre_ParCSRMatrix *parcsr_op_r = nullptr;
hypre_ParCSRMatrix *parcsr_op_i = nullptr;
if (Ar) { parcsr_op_r = (hypre_ParCSRMatrix*) const_cast<HypreParMatrix&>(*Ar); }
if (Ai) { parcsr_op_i = (hypre_ParCSRMatrix*) const_cast<HypreParMatrix&>(*Ai); }
// Merge diag+offd for whichever exists
hypre_CSRMatrix *csr_op_r = nullptr;
hypre_CSRMatrix *csr_op_i = nullptr;
if (parcsr_op_r) { csr_op_r = hypre_MergeDiagAndOffd(parcsr_op_r); }
if (parcsr_op_i) { csr_op_i = hypre_MergeDiagAndOffd(parcsr_op_i); }
#if MFEM_HYPRE_VERSION >= 21600
if (csr_op_r) { hypre_CSRMatrixBigJtoJ(csr_op_r); }
if (csr_op_i) { hypre_CSRMatrixBigJtoJ(csr_op_i); }
#endif
// Determine local/global sizes and row_start from an existing part
const int n_loc = internal::to_int((csr_op_r ? csr_op_r->num_rows :
csr_op_i->num_rows));
row_start = internal::to_int((parcsr_op_r ? parcsr_op_r->first_row_index
: parcsr_op_i->first_row_index));
const int global_n = internal::to_int((parcsr_op_r ?
parcsr_op_r->global_num_rows
: parcsr_op_i->global_num_rows));
// Use nullptr checks
const int *Ir = csr_op_r ? csr_op_r->i : nullptr;
const int *Jr = csr_op_r ? csr_op_r->j : nullptr;
const real_t *Vr = csr_op_r ? (const real_t*)csr_op_r->data : nullptr;
const int *Ii = csr_op_i ? csr_op_i->i : nullptr;
const int *Ji = csr_op_i ? csr_op_i->j : nullptr;
const real_t *Vi = csr_op_i ? (const real_t*)csr_op_i->data : nullptr;
// Build union COO
std::vector<int> Icoo, Jcoo;
std::vector<mumps_complex_t> Zcoo;
size_t nnz_r = csr_op_r ? (size_t)csr_op_r->num_nonzeros : 0;
size_t nnz_i = csr_op_i ? (size_t)csr_op_i->num_nonzeros : 0;
Icoo.reserve(nnz_r + nnz_i);
Jcoo.reserve(nnz_r + nnz_i);
Zcoo.reserve(nnz_r + nnz_i);
BuildUnionCOO(n_loc, row_start, Ir, Jr, Vr, Ii, Ji, Vi, Icoo, Jcoo, Zcoo);
const int nnz = (int)Icoo.size();
int *I = new int[nnz];
int *J = new int[nnz];
mumps_complex_t *A = new mumps_complex_t[nnz];
std::copy(Icoo.begin(), Icoo.end(), I);
std::copy(Jcoo.begin(), Jcoo.end(), J);
std::copy(Zcoo.begin(), Zcoo.end(), A);
// New ComplexMUMPS object or reuse an existing one
if (!id || !reorder_reuse)
{
if (id)
{
id->job = -2;
mumps_call();
delete id;
id = nullptr;
}
#ifdef MFEM_USE_SINGLE
id = new CMUMPS_STRUC_C();
#else
id = new ZMUMPS_STRUC_C();
#endif
id->sym = 0; // general complex
id->par = 1;
id->comm_fortran = (MUMPS_INT)MPI_Comm_c2f(comm);
// Init
id->job = -1;
mumps_call();
// Set parameters
SetParameters();
// Attach matrix
id->n = global_n;
id->nnz_loc = nnz;
id->irn_loc = I;
id->jcn_loc = J;
id->a_loc = A;
// Analysis (ordering + symbolic)
id->job = 1;
mumps_call();
}
else
{
// Reuse symbolic factorization / ordering
MFEM_VERIFY(id->n == global_n,
"ReorderingReuse requires same global size (id->n mismatch)");
// Update matrix pointers (pattern is assumed compatible)
id->nnz_loc = nnz;
id->irn_loc = I;
id->jcn_loc = J;
id->a_loc = A;
}
// Factorization
id->job = 2;
{
const int mem_relax_lim = 200;
while (true)
{
mumps_call();
if (id->MUMPS_INFOG(1) < 0)
{
if (id->MUMPS_INFOG(1) == -8 || id->MUMPS_INFOG(1) == -9)
{
id->MUMPS_ICNTL(14) += 20;
MFEM_VERIFY(id->MUMPS_ICNTL(14) <= mem_relax_lim,
"Memory relaxation limit reached for MUMPS factorization");
if (myid == 0 && print_level > 0)
{
out << "Re-running MUMPS factorization with memory relaxation "
<< id->MUMPS_ICNTL(14) << '\n';
}
}
else
{
MFEM_ABORT("Error during MUMPS numerical factorization");
}
}
else { break; }
}
}
// Done with input storage
if (csr_op_r) { hypre_CSRMatrixDestroy(csr_op_r);}
if (csr_op_i) { hypre_CSRMatrixDestroy(csr_op_i);}
delete [] I;
delete [] J;
delete [] A;
// Post-factorization RHS/SOL setup
id->nrhs = -1;
#if MFEM_MUMPS_VERSION >= 530
// Distributed RHS/SOL sizes
id->nloc_rhs = n_loc;
id->lrhs_loc = n_loc;
id->lsol_loc = id->MUMPS_INFO(23);
delete [] irhs_loc;
irhs_loc = new int[id->lrhs_loc];
for (int i = 0; i < n_loc; i++)
{
irhs_loc[i] = row_start + i + 1;
}
id->irhs_loc = irhs_loc;
delete [] isol_loc;
isol_loc = new int[id->lsol_loc];
id->isol_loc = isol_loc;
row_starts.SetSize(numProcs);
MPI_Allgather(&row_start, 1, MPI_INT, row_starts, 1, MPI_INT, comm);
// Reset cached buffers
delete [] rhs_loc; rhs_loc = nullptr;
delete [] sol_loc; sol_loc = nullptr;
rhs1_buf.clear();
#else
// Centralized RHS/SOL on root
id->lrhs = id->n;
global_num_rows = id->n;
if (myid == 0)
{
delete [] recv_counts;
delete [] displs;
recv_counts = new int[numProcs];
displs = new int[numProcs];
delete [] rhs_glob; rhs_glob = nullptr;
delete [] rhs_glob_r; rhs_glob_r = nullptr;
delete [] rhs_glob_i; rhs_glob_i = nullptr;
}
MPI_Gather(&n_loc, 1, MPI_INT, recv_counts, 1, MPI_INT, 0, comm);
if (myid == 0)
{
displs[0] = 0;
int s = 0;
for (int k = 0; k < numProcs - 1; k++)
{
s += recv_counts[k];
displs[k+1] = s;
}
}
#endif
}
void ComplexMUMPSSolver::InitRhsSol(int nrhs) const
{
#if MFEM_MUMPS_VERSION >= 530
MFEM_VERIFY(id, "InitRhsSol called before SetOperator");
if (id->nrhs != nrhs)
{
delete [] rhs_loc;
delete [] sol_loc;
rhs_loc = new mumps_complex_t[(size_t)nrhs * (size_t)id->lrhs_loc];
sol_loc = new mumps_complex_t[(size_t)nrhs * (size_t)id->lsol_loc];
id->rhs_loc = rhs_loc;
id->sol_loc = sol_loc;
}
id->nrhs = nrhs;
#else
MFEM_VERIFY(id, "InitRhsSol called before SetOperator");
id->nrhs = nrhs;
id->lrhs = id->n;
if (myid == 0)
{
const size_t N = (size_t)nrhs * (size_t)global_num_rows;
delete [] rhs_glob;
delete [] rhs_glob_r;
delete [] rhs_glob_i;
rhs_glob = new mumps_complex_t[N];
rhs_glob_r = new real_t[N];
rhs_glob_i = new real_t[N];
id->rhs = rhs_glob;
}
#endif
}
void ComplexMUMPSSolver::Mult(const Vector &x, Vector &y) const
{
Array<const Vector *> X(1);
Array<Vector *> Y(1);
X[0] = &x;
Y[0] = &y;
ArrayMult(X, Y);
}
void ComplexMUMPSSolver::ArrayMult(const Array<const Vector *> &X,
Array<Vector *> &Y) const
{
MFEM_ASSERT(X.Size() == Y.Size(),
"Number of columns mismatch in ComplexMUMPSSolver::Mult!");
MFEM_VERIFY(id, "ComplexMUMPSSolver::ArrayMult called before SetOperator");
InitRhsSol(X.Size());
#if MFEM_MUMPS_VERSION >= 530
MFEM_VERIFY(irhs_loc && isol_loc, "RHS/SOL maps not initialized");
MFEM_VERIFY(rhs_loc && sol_loc, "RHS/SOL buffers not initialized");
const int n_loc = id->lrhs_loc;
const int nrhs = id->nrhs;
// Pack all RHS
for (int i = 0; i < nrhs; i++)
{
MFEM_ASSERT(X[i], "Missing Vector in Mult!");
X[i]->HostRead();
MFEM_VERIFY(X[i]->Size() == 2*n_loc, "RHS size mismatch");
const real_t *xdata = X[i]->GetData();
const real_t *xr = xdata;
const real_t *xi = xdata + n_loc;
mumps_complex_t *dst = rhs_loc + i * n_loc;
for (int j = 0; j < n_loc; j++)
{
dst[j].r = xr[j];
dst[j].i = xi[j];
}
}
id->rhs_loc = rhs_loc;
id->sol_loc = sol_loc;
id->irhs_loc = irhs_loc;
id->isol_loc = isol_loc;
// MUMPS solve
id->job = 3;
mumps_call();
const int lsol = id->lsol_loc;
// Redistribute each solution column into Y
for (int i = 0; i < nrhs; i++)
{
MFEM_ASSERT(Y[i], "Missing output Vector in Mult!");
Y[i]->HostWrite();
MFEM_VERIFY(Y[i]->Size() == 2*n_loc, "Output size mismatch");
const mumps_complex_t *xcol = sol_loc + i * lsol;
RedistributeSol(isol_loc, xcol, Y[i]->GetData(), n_loc, lsol);
}
#else // MFEM_MUMPS_VERSION < 530
const int nrhs = id->nrhs;
MFEM_VERIFY(X.Size() > 0 && X[0], "Missing RHS");
const int n_loc = X[0]->Size()/2;
for (int i = 0; i < nrhs; i++)
{
MFEM_ASSERT(X[i], "Missing Vector in Mult!");
X[i]->HostRead();
MFEM_VERIFY(X[i]->Size() == 2*n_loc, "RHS size mismatch");
}
// Gather each RHS column (real+imag separately) into root staging
for (int i = 0; i < nrhs; i++)
{
const real_t *xdata = X[i]->GetData();
MPI_Gatherv(xdata, n_loc, MPITypeMap<real_t>::mpi_type,
rhs_glob_r + i * global_num_rows,
recv_counts, displs, MPITypeMap<real_t>::mpi_type,
0, comm);
MPI_Gatherv(xdata + n_loc, n_loc, MPITypeMap<real_t>::mpi_type,
rhs_glob_i + i * global_num_rows,
recv_counts, displs, MPITypeMap<real_t>::mpi_type,
0, comm);
}
// Pack into MUMPS complex RHS on root: id->rhs is in-place
if (myid == 0)
{
for (int i = 0; i < nrhs; i++)
{
mumps_complex_t *dst = rhs_glob + i * global_num_rows;
const real_t *rr = rhs_glob_r + i * global_num_rows;
const real_t *ri = rhs_glob_i + i * global_num_rows;
for (int j = 0; j < global_num_rows; j++)
{
dst[j].r = rr[j];
dst[j].i = ri[j];
}
}
id->rhs = rhs_glob;
}
// Solve
id->job = 3;
mumps_call();
// Unpack to real/imag
if (myid == 0)
{
for (int i = 0; i < nrhs; i++)
{
const mumps_complex_t *src = rhs_glob + i * global_num_rows;
real_t *rr = rhs_glob_r + i * global_num_rows;
real_t *ri = rhs_glob_i + i * global_num_rows;
for (int j = 0; j < global_num_rows; j++)
{
rr[j] = src[j].r;
ri[j] = src[j].i;
}
}
}
// Scatter each RHS solution
for (int i = 0; i < nrhs; i++)
{
MFEM_ASSERT(Y[i], "Missing Vector in Mult!");
Y[i]->HostWrite();
MFEM_VERIFY(Y[i]->Size() == 2*n_loc, "Output size mismatch");
real_t *ydata = Y[i]->GetData();
MPI_Scatterv(rhs_glob_r + i * global_num_rows,
recv_counts, displs, MPITypeMap<real_t>::mpi_type,
ydata, n_loc, MPITypeMap<real_t>::mpi_type,
0, comm);
MPI_Scatterv(rhs_glob_i + i * global_num_rows,
recv_counts, displs, MPITypeMap<real_t>::mpi_type,
ydata + n_loc, n_loc, MPITypeMap<real_t>::mpi_type,
0, comm);
}
#endif
}
void ComplexMUMPSSolver::MultTranspose(const Vector &x, Vector &y) const
{
MFEM_VERIFY(id, "MultTranspose called before SetOperator");
// Transpose solve
id->MUMPS_ICNTL(9) = 0;
Mult(x, y);
id->MUMPS_ICNTL(9) = 1;
}
void ComplexMUMPSSolver::ArrayMultTranspose(const Array<const Vector *> &X,
Array<Vector *> &Y) const
{
MFEM_VERIFY(id, "ArrayMultTranspose called before SetOperator");
// Transpose solve
id->MUMPS_ICNTL(9) = 0;
ArrayMult(X, Y);
id->MUMPS_ICNTL(9) = 1;
}
void ComplexMUMPSSolver::SetParameters()
{
// Output stream for error messages
id->MUMPS_ICNTL(1) = 6;
// Output stream for diagnostic printing local to each proc
id->MUMPS_ICNTL(2) = 0;
// Output stream for global info
id->MUMPS_ICNTL(3) = 6;
// Level of error printing
id->MUMPS_ICNTL(4) = print_level;
// Input matrix format (assembled)
id->MUMPS_ICNTL(5) = 0;
// Use A or A^T
id->MUMPS_ICNTL(9) = 1;
// Iterative refinement (disabled)
id->MUMPS_ICNTL(10) = 0;
// Error analysis-statistics (disabled)
id->MUMPS_ICNTL(11) = 0;
// Use of ScaLAPACK (disabled)
id->MUMPS_ICNTL(13) = 0;
// Workspace relaxation (% increase)
id->MUMPS_ICNTL(14) = 20;
// OpenMP threads (default)
id->MUMPS_ICNTL(16) = 0;
// Matrix input format (distributed)
id->MUMPS_ICNTL(18) = 3;
// Schur complement (none)
id->MUMPS_ICNTL(19) = 0;
#if MFEM_MUMPS_VERSION >= 530
// Distributed RHS
id->MUMPS_ICNTL(20) = 10;
// Distributed Sol
id->MUMPS_ICNTL(21) = 1;
#else
// Centralized RHS
id->MUMPS_ICNTL(20) = 0;
// Centralized Sol
id->MUMPS_ICNTL(21) = 0;
#endif
// Out-of-core (disabled)
id->MUMPS_ICNTL(22) = 0;
// Max size of working memory (default)
id->MUMPS_ICNTL(23) = 0;
switch (reorder_method)
{
case ReorderingStrategy::AUTOMATIC:
id->MUMPS_ICNTL(28) = 0;
id->MUMPS_ICNTL(7) = 7;
id->MUMPS_ICNTL(29) = 0;
break;
case ReorderingStrategy::AMD:
id->MUMPS_ICNTL(28) = 1;
id->MUMPS_ICNTL(7) = 0;
break;
case ReorderingStrategy::AMF:
id->MUMPS_ICNTL(28) = 1;
id->MUMPS_ICNTL(7) = 2;
break;
case ReorderingStrategy::PORD:
id->MUMPS_ICNTL(28) = 1;
id->MUMPS_ICNTL(7) = 4;
break;
case ReorderingStrategy::METIS:
id->MUMPS_ICNTL(28) = 1;
id->MUMPS_ICNTL(7) = 5;
break;
case ReorderingStrategy::PARMETIS:
id->MUMPS_ICNTL(28) = 2;
id->MUMPS_ICNTL(29) = 2;
break;
case ReorderingStrategy::SCOTCH:
id->MUMPS_ICNTL(28) = 1;
id->MUMPS_ICNTL(7) = 3;
break;
case ReorderingStrategy::PTSCOTCH:
id->MUMPS_ICNTL(28) = 2;
id->MUMPS_ICNTL(29) = 1;
break;
default:
break; // This should be unreachable
}
}
void ComplexMUMPSSolver::BuildUnionCOO(const int n_loc,
const int row_start_,
const int *Ir, const int *Jr, const real_t *Vr,
const int *Ii, const int *Ji, const real_t *Vi,
std::vector<int> &Icoo,
std::vector<int> &Jcoo,
std::vector<mumps_complex_t> &Zcoo) const
{
for (int r = 0; r < n_loc; ++r)
{
std::unordered_map<int, std::pair<real_t, real_t>> row;
const int rr0 = Ir ? Ir[r] : 0;
const int rr1 = Ir ? Ir[r+1] : 0;
const int ii0 = Ii ? Ii[r] : 0;
const int ii1 = Ii ? Ii[r+1] : 0;
row.reserve((rr1 - rr0) + (ii1 - ii0));
if (Ir)
{
for (int p = rr0; p < rr1; ++p) { row[Jr[p]].first += Vr[p]; }
}
if (Ii)
{
for (int p = ii0; p < ii1; ++p) { row[Ji[p]].second += Vi[p]; }
}
for (const auto &kv : row)
{
Icoo.push_back(row_start_ + r + 1);
Jcoo.push_back(kv.first + 1);
Zcoo.push_back(mumps_complex_t{kv.second.first, kv.second.second});
}
}
}
#if MFEM_MUMPS_VERSION >= 530
int ComplexMUMPSSolver::GetRowRank(int i, const Array<int> &row_starts_) const
{
if (row_starts_.Size() == 1) { return 0; }
auto up = std::upper_bound(row_starts_.begin(), row_starts_.end(), i);
return (int)std::distance(row_starts_.begin(), up) - 1;
}
void ComplexMUMPSSolver::RedistributeSol(const int *row_map,
const mumps_complex_t *x,
real_t *y_ri,
int n_loc,
int lsol_loc) const
{
int *send_count = new int[numProcs]();
for (int i = 0; i < lsol_loc; i++)
{
const int j = row_map[i] - 1;
const int row_rank = GetRowRank(j, row_starts);
if (myid == row_rank) { continue; }
send_count[row_rank]++;
}
int *recv_count = new int[numProcs];
MPI_Alltoall(send_count, 1, MPI_INT, recv_count, 1, MPI_INT, comm);
int *send_displ = new int[numProcs]; send_displ[0] = 0;
int *recv_displ = new int[numProcs]; recv_displ[0] = 0;
int sbuff_size = send_count[numProcs-1];
int rbuff_size = recv_count[numProcs-1];
for (int k = 0; k < numProcs - 1; k++)
{
send_displ[k+1] = send_displ[k] + send_count[k];
recv_displ[k+1] = recv_displ[k] + recv_count[k];
sbuff_size += send_count[k];
rbuff_size += recv_count[k];
}
int *sendbuf_index = new int[sbuff_size];
real_t *sendbuf_r = new real_t[sbuff_size];
real_t *sendbuf_i = new real_t[sbuff_size];
int *soffs = new int[numProcs]();
for (int i = 0; i < lsol_loc; i++)
{
const int j = row_map[i] - 1;
const int row_rank = GetRowRank(j, row_starts);
const real_t xr = (real_t)x[i].r;
const real_t xi = (real_t)x[i].i;
if (myid == row_rank)
{
const int local_index = j - row_start;
y_ri[local_index] = xr;
y_ri[local_index+n_loc] = xi;
}
else
{
const int k = send_displ[row_rank] + soffs[row_rank];
sendbuf_index[k] = j;
sendbuf_r[k] = xr;
sendbuf_i[k] = xi;
soffs[row_rank]++;
}
}
int *recvbuf_index = new int[rbuff_size];
real_t *recvbuf_r = new real_t[rbuff_size];
real_t *recvbuf_i = new real_t[rbuff_size];
MPI_Alltoallv(sendbuf_index, send_count, send_displ, MPI_INT,
recvbuf_index, recv_count, recv_displ, MPI_INT, comm);
MPI_Alltoallv(sendbuf_r, send_count, send_displ, MPITypeMap<real_t>::mpi_type,
recvbuf_r, recv_count, recv_displ, MPITypeMap<real_t>::mpi_type, comm);
MPI_Alltoallv(sendbuf_i, send_count, send_displ, MPITypeMap<real_t>::mpi_type,
recvbuf_i, recv_count, recv_displ, MPITypeMap<real_t>::mpi_type, comm);
for (int i = 0; i < rbuff_size; i++)
{
const int local_index = recvbuf_index[i] - row_start;
y_ri[local_index] = recvbuf_r[i];
y_ri[local_index+n_loc] = recvbuf_i[i];
}
delete [] recvbuf_i;
delete [] recvbuf_r;
delete [] recvbuf_index;
delete [] soffs;
delete [] sendbuf_i;
delete [] sendbuf_r;
delete [] sendbuf_index;
delete [] recv_displ;
delete [] send_displ;
delete [] recv_count;
delete [] send_count;
}
#endif // MFEM_MUMPS_VERSION >= 530
#endif // MFEM_USE_COMPLEX_MUMPS
#endif // MFEM_USE_MPI
} // namespace mfem
+280
View File
@@ -13,6 +13,7 @@
#define MFEM_COMPLEX_OPERATOR
#include "operator.hpp"
#include "blockoperator.hpp"
#include "sparsemat.hpp"
#ifdef MFEM_USE_MPI
#include "hypre.hpp"
@@ -22,8 +23,20 @@
#include <umfpack.h>
#endif
#ifdef MFEM_USE_COMPLEX_MUMPS
#ifdef MFEM_USE_SINGLE
#include "cmumps_c.h"
#else
#include "zmumps_c.h"
#endif
#include <vector>
#endif
namespace mfem
{
#ifdef MFEM_USE_MPI
class ComplexHypreParMatrix; // forward declaration
#endif
/** @brief Mimic the action of a complex operator using two real operators.
@@ -118,6 +131,13 @@ public:
Convention GetConvention() const { return convention_; }
#ifdef MFEM_USE_MPI
/// Return a ComplexHypreParMatrix view:
/// - wraps if real/imag are HypreParMatrix
/// - merges if real/imag are BlockOperator of HypreParMatrix blocks
ComplexHypreParMatrix *AsComplexHypreParMatrix() const;
#endif
protected:
// Let this be hidden from the public interface since the implementation
// depends on internal members
@@ -242,6 +262,7 @@ public:
#endif
#ifdef MFEM_USE_MPI
/** @brief Specialization of the ComplexOperator built from a pair of
@@ -288,6 +309,265 @@ private:
int myid_;
int nranks_;
};
/// A BlockOperator whose blocks are ComplexOperator objects, constructed from
/// a ComplexOperator whose Real()/Imag() parts are BlockOperator objects.
/// It also provides layout conversions between:
/// • BlockComplex (stacked): [ Re(all); Im(all) ]
/// • ComplexBlock (per-block): [ Re(block_b); Im(block_b) ]
class ComplexBlockOperator : public BlockOperator
{
public:
/// Construct from a ComplexOperator whose Real()/Imag() parts are
/// BlockOperator objects.
ComplexBlockOperator(const ComplexOperator &A);
/// Convert vector from BlockComplex (stacked) -> ComplexBlock (per-block).
/// Sizes must match (xout.Size() == xin.Size() == 2*N).
void BlockComplexToComplexBlock(const Vector &xin,
Vector &xout) const;
/// Convert vector from ComplexBlock (per-block) -> BlockComplex (stacked).
/// Sizes must match (xout.Size() == xin.Size() == 2*N).
void ComplexBlockToBlockComplex(const Vector &xin,
Vector &xout) const;
private:
};
#ifdef MFEM_USE_COMPLEX_MUMPS
/**
* @brief Complex MUMPS: Parallel sparse direct solver for ComplexHypreParMatrix
*
* Notes:
* - Expects Operator to be a ComplexHypreParMatrix.
* - Complex vectors are assumed packed as [Re; Im] in a real Vector.
* - SetOperator(): analysis + factorization
* - Mult() : solve
*/
class ComplexMUMPSSolver : public Solver
{
public:
/// Specify the reordering strategy
enum ReorderingStrategy
{
/// Let MUMPS automatically decide the reordering strategy
AUTOMATIC = 0,
/// Approximate Minimum Degree with auto quasi-dense row detection is used
AMD,
/// Approximate Minimum Fill method will be used
AMF,
/// The PORD library will be used
PORD,
/// The METIS library will be used
METIS,
/// The ParMETIS library will be used
PARMETIS,
/// The Scotch library will be used
SCOTCH,
/// The PTScotch library will be used
PTSCOTCH
};
/**
* @brief Constructor with MPI_Comm parameter.
*/
ComplexMUMPSSolver(MPI_Comm comm_);
/**
* @brief Constructor with a ComplexHypreParMatrix Operator.
*/
ComplexMUMPSSolver(const Operator &op);
/**
* @brief Set the Operator and perform factorization
*
* @a op needs to be of type ComplexHypreParMatrix.
*
* @param op Operator used in factorization and solve
*/
void SetOperator(const Operator &op);
/**
* @brief Solve $ y = Op^{-1} x $
*
* @param x RHS vector
* @param y Solution vector
*/
void Mult(const Vector &x, Vector &y) const;
/**
* @brief Solve $ Y_i = Op^{-1} X_i $
*
* @param X Array of RHS vectors
* @param Y Array of Solution vectors
*/
void ArrayMult(const Array<const Vector *> &X, Array<Vector *> &Y) const;
/**
* @brief Transpose Solve $ y = Op^{-T} x $
* @note This is not a Hermitian/conjugate-transpose solve.
*
* @param x RHS vector
* @param y Solution vector
*/
void MultTranspose(const Vector &x, Vector &y) const;
/**
* @brief Transpose Solve $ Y_i = Op^{-T} X_i $
* @note This is not a Hermitian/conjugate-transpose solve.
*
* @param X Array of RHS vectors
* @param Y Array of Solution vectors
*/
void ArrayMultTranspose(const Array<const Vector *> &X,
Array<Vector *> &Y) const;
/**
* @brief Set the error print level for MUMPS
*
* Supported values are:
* - 0: No output printed
* - 1: Only errors printed
* - 2: Errors, warnings, and main stats printed
* - 3: Errors, warnings, main stats, and terse diagnostics printed
* - 4: Errors, warnings, main stats, diagnostics, and input/output printed
*
* @param print_lvl Print level, default is 2
*
* @note This method has to be called before SetOperator
*/
void SetPrintLevel(int print_lvl) { print_level = print_lvl;}
/**
* @brief Set the reordering strategy
*
* Supported reorderings are: ComplexMUMPSSolver::AUTOMATIC,
* ComplexMUMPSSolver::AMD, ComplexMUMPSSolver::AMF,
* ComplexMUMPSSolver::PORD, ComplexMUMPSSolver::METIS,
* ComplexMUMPSSolver::PARMETIS, ComplexMUMPSSolver::SCOTCH,
* and ComplexMUMPSSolver::PTSCOTCH
*
* @param method Reordering method
*
* @note This method has to be called before SetOperator
*/
void SetReorderingStrategy(ReorderingStrategy method) { reorder_method = method; }
/**
* @brief Set the flag controlling reuse of the symbolic factorization
* for multiple operators
*
* @param reuse Flag to reuse symbolic factorization
*
* @note This method has to be called before repeated calls to SetOperator
*/
void SetReorderingReuse(bool reuse) { reorder_reuse = reuse; }
~ComplexMUMPSSolver();
private:
// MPI communicator
MPI_Comm comm = MPI_COMM_NULL;
// Number of procs
int numProcs;
// MPI rank
int myid;
// Parameter controlling the printing level
int print_level = 0;
// Parameter controlling the reordering strategy
ReorderingStrategy reorder_method = ReorderingStrategy::AUTOMATIC;
// Parameter controlling whether or not to reuse the symbolic factorization
// for multiple calls to SetOperator
bool reorder_reuse = false;
// Local row offsets
int row_start;
// ComplexMUMPS object
#ifdef MFEM_USE_SINGLE
CMUMPS_STRUC_C *id = nullptr;
using mumps_complex_t = mumps_complex;
#else
ZMUMPS_STRUC_C *id = nullptr;
using mumps_complex_t = mumps_double_complex;
#endif
/// Method for initialization
void Init(MPI_Comm comm_);
/// Method for setting ComplexMUMPS internal parameters
void SetParameters();
/// Method for configuring storage for distributed/centralized
/// RHS and solution
void InitRhsSol(int nrhs) const;
/// Method for calling the single/double ComplexMUMPS solver
inline void mumps_call() const
{
#ifdef MFEM_USE_SINGLE
cmumps_c(id);
#else
zmumps_c(id);
#endif
}
/// Method for building the COO format of the combined complex operator
/// from the real and imaginary parts. This is particularly useful when
/// real and imaginary parts have different sparsity patterns.
void BuildUnionCOO(const int n_loc,
const int row_start,
const int *Ir, const int *Jr, const real_t *Vr,
const int *Ii, const int *Ji, const real_t *Vi,
std::vector<int> &Icoo,
std::vector<int> &Jcoo,
std::vector<mumps_complex_t> &Zcoo) const;
#if MFEM_MUMPS_VERSION >= 530
// Row offsets on all procs
Array<int> row_starts;
// Local RHS row indices
int *irhs_loc = nullptr;
// Local solution row map returned by MUMPS
int *isol_loc = nullptr;
// Cached buffers
mutable mumps_complex_t *rhs_loc = nullptr;
mutable mumps_complex_t *sol_loc = nullptr;
// RHS buffers
mutable std::vector<mumps_complex_t> rhs1_buf;
// These two methods are needed to distribute the local solution
// vectors returned by MUMPS to the original MFEM parallel partition
int GetRowRank(int i, const Array<int> &row_starts_) const;
void RedistributeSol(const int *row_map,
const mumps_complex_t *x,
real_t *y_ri,
int n_loc,
int lsol_loc) const;
#else
// Root-gather path
int global_num_rows;
// Arrays needed for MPI_Gatherv and MPI_Scatterv
int *recv_counts = nullptr;
int *displs = nullptr;
// Complex RHS/solution on root
mutable mumps_complex_t *rhs_glob = nullptr;
// Cached real/imag staging on root
mutable real_t *rhs_glob_r = nullptr;
mutable real_t *rhs_glob_i = nullptr;
#endif
};
#endif // MFEM_USE_COMPLEX_MUMPS
#endif // MFEM_USE_MPI
+39
View File
@@ -2185,6 +2185,45 @@ void DenseMatrix::AdjustDofDirection(Array<int> &dofs)
}
}
void DenseMatrix::AdjustDofDirection(Array<int> &row_dofs,
Array<int> &col_dofs)
{
const int nr = row_dofs.Size();
const int nc = col_dofs.Size();
MFEM_VERIFY(Height() == nr && Width() == nc,
"DenseMatrix::AdjustDofDirection: size mismatch.");
// Extract signs and convert to unsigned indices
Vector rsign(nr), csign(nc);
for (int i = 0; i < nr; i++)
{
const int d = row_dofs[i];
if (d >= 0) { rsign(i) = 1.0; }
else { rsign(i) = -1.0; row_dofs[i] = -d - 1; continue; }
row_dofs[i] = d;
}
for (int j = 0; j < nc; j++)
{
const int d = col_dofs[j];
if (d >= 0) { csign(j) = 1.0; }
else { csign(j) = -1.0; col_dofs[j] = -d - 1; continue; }
col_dofs[j] = d;
}
// Apply row/column signs
for (int i = 0; i < nr; i++)
{
const real_t rs = rsign(i);
for (int j = 0; j < nc; j++)
{
(*this)(i,j) *= rs * csign(j);
}
}
}
void DenseMatrix::SetRow(int row, real_t value)
{
for (int j = 0; j < Width(); j++)
+13
View File
@@ -82,6 +82,15 @@ public:
}
}
/// Make the DenseMatrix to reference the given sub-Memory of @a base.
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
not delete the @a base Memory. */
void MakeRef(Memory<real_t> &base, int offset, int h, int w)
{
data.MakeRef(base, offset, h*w);
height = h; width = w;
}
/// Change the data array and the size of the DenseMatrix.
/** The DenseMatrix does not assume ownership of the data array, i.e. it will
not delete the data array @a d. */
@@ -467,6 +476,10 @@ public:
then (*this)(i,j) = -(*this)(i,j). */
void AdjustDofDirection(Array<int> &dofs);
void AdjustDofDirection(Array<int> &row_dofs,
Array<int> &col_dofs);
/// Replace small entries, abs(a_ij) <= eps, with zero.
void Threshold(real_t eps);
+47 -2
View File
@@ -3443,6 +3443,51 @@ HypreParMatrix *HypreParMatrixFromBlocks(Array2D<const HypreParMatrix*> &blocks,
}
}
HypreParMatrix * GetSubHypreParMatrix(const Array<int> &tdofs,
const HypreParMatrix & A)
{
int nrows = tdofs.Size();
int gncols = A.GetGlobalNumCols();
SparseMatrix Sr(nrows, gncols);
for (int i = 0; i<nrows; i++)
{
int col = tdofs[i] + A.RowPart()[0];
Sr.Set(i,col,1.0);
}
Sr.Finalize();
int newrows[2];
int newcols[2];
int row_offset;
MPI_Scan(&nrows,&row_offset,1,MPI_INT,MPI_SUM,MPI_COMM_WORLD);
row_offset-=nrows;
newrows[0] = row_offset;
newrows[1] = row_offset+nrows;
newcols[0] = A.ColPart()[0];
newcols[1] = A.ColPart()[1];
int gnrows;
MPI_Allreduce(&nrows, &gnrows,1,MPI_INT,MPI_SUM,MPI_COMM_WORLD);
HypreParMatrix * Prt = new HypreParMatrix(MPI_COMM_WORLD, nrows, gnrows,
gncols, Sr.GetI(), Sr.GetJ(),
Sr.GetData(), newrows,newcols);
HypreParMatrix * Pr = Prt->Transpose();
delete Prt;
HypreParMatrix * tmp = RAP(&A,Pr);
delete Pr;
return tmp;
}
HypreParMatrix * HypreParMatrixFromBlocks(Array2D<HypreParMatrix*> &blocks,
Array2D<real_t> *blockCoeff)
{
@@ -5224,7 +5269,7 @@ void HypreBoomerAMG::SetDefaultOptions()
Pmax = 4; // max number of elements per row in P
// AMG relaxation options:
relax_type = 8; // 8 = l1-GS, 6 = symm. GS, 3 = GS, 18 = l1-Jacobi
relax_type = 88; // 8 = l1-GS, 6 = symm. GS, 3 = GS, 18 = l1-Jacobi
relax_sweeps = 1; // relaxation sweeps on each level
// Additional options:
@@ -5741,7 +5786,7 @@ void HypreAMS::MakeSolver(int sdim, int cycle_type)
const bool hypre_gpu = HypreUsingGPU();
int amg_coarsen_type = hypre_gpu ? 8 : 10;
int amg_agg_levels = hypre_gpu ? 0 : 1;
int amg_rlx_type = hypre_gpu ? 18 : 8;
int amg_rlx_type = hypre_gpu ? 18 : 88;
int rlx_type = hypre_gpu ? 1: 2;
real_t theta = 0.25;
int amg_interp_type = 6;
+5
View File
@@ -1063,6 +1063,11 @@ MFEM_DEPRECATED HypreParMatrix *HypreParMatrixFromBlocks(
Array2D<HypreParMatrix*> &blocks,
Array2D<real_t> *blockCoeff=NULL);
/** Extract a SubMatrix of a HypreParMatrix constructed
from the given rows and cols (local numbering) */
HypreParMatrix * GetSubHypreParMatrix(const Array<int> &dofs,
const HypreParMatrix & A);
/** @brief Eliminate essential BC specified by @a ess_dof_list from the solution
@a X to the r.h.s. @a B. */
/** Here @a A is a matrix with eliminated BC, while @a Ae is such that (A+Ae) is
+4 -4
View File
@@ -52,7 +52,7 @@ public:
/// Specify the reordering strategy for the MUMPS solver
enum ReorderingStrategy
{
/// Let MUMPS automatically decide the reording strategy
/// Let MUMPS automatically decide the reordering strategy
AUTOMATIC = 0,
/// Approximate Minimum Degree with auto quasi-dense row detection is used
AMD,
@@ -98,7 +98,7 @@ public:
void Mult(const Vector &x, Vector &y) const;
/**
* @brief Solve $ Y_i = Op^{-T} X_i $
* @brief Solve $ Y_i = Op^{-1} X_i $
*
* @param X Array of RHS vectors
* @param Y Array of Solution vectors
@@ -129,8 +129,8 @@ public:
* - 0: No output printed
* - 1: Only errors printed
* - 2: Errors, warnings, and main stats printed
* - 3: Errors, warning, main stats, and terse diagnostics printed
* - 4: Errors, warning, main stats, diagnostics, and input/output printed
* - 3: Errors, warnings, main stats, and terse diagnostics printed
* - 4: Errors, warnings, main stats, diagnostics, and input/output printed
*
* @param print_lvl Print level, default is 2
*
+2 -1
View File
@@ -466,10 +466,11 @@ RAPOperator::RAPOperator(const Operator &Rt_, const Operator &A_,
TripleProductOperator::TripleProductOperator(
const Operator *A, const Operator *B, const Operator *C,
bool ownA, bool ownB, bool ownC)
bool ownA, bool ownB, bool ownC, real_t alpha_)
: Operator(A->Height(), C->Width())
, A(A), B(B), C(C)
, ownA(ownA), ownB(ownB), ownC(ownC)
, alpha(alpha_)
{
MFEM_VERIFY(A->Width() == B->Height(),
"incompatible Operators: A->Width() = " << A->Width()
+5 -4
View File
@@ -1011,27 +1011,28 @@ public:
};
/// General triple product operator x -> A*B*C*x, with ownership of the factors.
/// General triple product operator x -> α A*B*C*x, with ownership of the factors.
class TripleProductOperator : public Operator
{
const Operator *A;
const Operator *B;
const Operator *C;
bool ownA, ownB, ownC;
const real_t alpha;
mutable Vector t1, t2;
MemoryClass mem_class;
public:
TripleProductOperator(const Operator *A, const Operator *B,
const Operator *C, bool ownA, bool ownB, bool ownC);
const Operator *C, bool ownA, bool ownB, bool ownC, real_t alpha_ = 1.0);
MemoryClass GetMemoryClass() const override { return mem_class; }
void Mult(const Vector &x, Vector &y) const override
{ C->Mult(x, t1); B->Mult(t1, t2); A->Mult(t2, y); }
{ C->Mult(x, t1); B->Mult(t1, t2); A->Mult(t2, y); y*=alpha; }
void MultTranspose(const Vector &x, Vector &y) const override
{ A->MultTranspose(x, t2); B->MultTranspose(t2, t1); C->MultTranspose(t1, y); }
{ A->MultTranspose(x, t2); B->MultTranspose(t2, t1); C->MultTranspose(t1, y); y*=alpha; }
virtual ~TripleProductOperator();
};
+5 -8
View File
@@ -683,8 +683,7 @@ void SLISolver::Mult(const Vector &b, Vector &x) const
oper->Mult(x, r); // r = A x
if (!zero_b) { subtract(b, r, r); } // r = b - A x
prec->Mult(r, z); // z = B r
if (!zero_b) { x += z; } // x = x + B (b - A x)
else { x -= z; } // x = x - B (A x)
add(x, omega, z, x); // x = x + ω B (b - A x)
}
converged = true;
final_iter = i;
@@ -701,7 +700,7 @@ void SLISolver::Mult(const Vector &b, Vector &x) const
oper->Mult(x, r); // r = A x
subtract(b, r, r); // r = b - A x
prec->Mult(r, z); // z = B r
x += z; // x = x + B (b - A x)
add(x, omega, z, x); // x = x + ω B (b - A x)
}
converged = true;
final_iter = i;
@@ -754,15 +753,13 @@ void SLISolver::Mult(const Vector &b, Vector &x) const
final_iter = max_iter;
for (i = 1; true; )
{
if (prec)
if (prec) // x = x + ω B (b - A x)
{
if (!zero_b) { x += z; } // x = x + B (b - A x)
else { x -= z; } // x = x - B (A x)
add(x, omega, z, x);
}
else
{
if (!zero_b) { x += r; } // x = x + (b - A x)
else { x -= r; } // x = x - (A x)
add(x, omega, r, x);
}
oper->Mult(x, r);
+5 -4
View File
@@ -586,19 +586,20 @@ private:
};
/// Stationary linear iteration: x <- x + B (b - A x)
/// Stationary linear iteration: x <- x + ω B (b - A x)
class SLISolver : public IterativeSolver
{
protected:
mutable Vector r, z;
real_t omega; ///< Relaxation parameter
void UpdateVectors();
public:
SLISolver() { }
SLISolver(real_t omega_ = 1.0) : omega(omega_) { }
#ifdef MFEM_USE_MPI
SLISolver(MPI_Comm comm_) : IterativeSolver(comm_) { }
SLISolver(MPI_Comm comm_, real_t omega_ = 1.0) : IterativeSolver(comm_),
omega(omega_) { }
#endif
void SetOperator(const Operator &op) override
+5 -4
View File
@@ -224,7 +224,7 @@ CXXFLAGS ?= $(OPTIM_FLAGS)
# MPI configuration
ifneq ($(MFEM_USE_MPI),YES)
HOST_CXX = $(CXX)
PKGS_NEED_MPI = SUPERLU MUMPS STRUMPACK PETSC PUMI SLEPC MKL_CPARDISO
PKGS_NEED_MPI = SUPERLU MUMPS COMPLEX_MUMPS STRUMPACK PETSC PUMI SLEPC MKL_CPARDISO
$(foreach mpidep,$(PKGS_NEED_MPI),$(if $(MFEM_USE_$(mpidep):NO=),\
$(warning *** [MPI is OFF] setting MFEM_USE_$(mpidep) = NO)\
$(eval override MFEM_USE_$(mpidep)=NO),))
@@ -299,7 +299,7 @@ ifeq ($(MFEM_USE_LEGACY_OPENMP),YES)
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\
MFEM_REQ_LIB_DEPS = SUPERLU MUMPS COMPLEX_MUMPS METIS FMS CONDUIT SIDRE LAPACK SUNDIALS\
SUITESPARSE STRUMPACK GINKGO GNUTLS HDF5 NETCDF SLEPC PETSC MPFR PUMI HIOP\
GSLIB OCCA CEED RAJA UMPIRE MKL_CPARDISO MKL_PARDISO AMGX MAGMA CALIPER PARELAG\
TRIBOL BENCHMARK MOONOLITH ALGOIM
@@ -369,7 +369,7 @@ 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_MOONOLITH MFEM_USE_CEED MFEM_USE_RAJA MFEM_USE_UMPIRE\
MFEM_USE_SIMD MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO MFEM_USE_AMGX\
MFEM_USE_MAGMA MFEM_USE_MUMPS MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_CALIPER\
MFEM_USE_MAGMA MFEM_USE_MUMPS MFEM_USE_COMPLEX_MUMPS MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_CALIPER\
MFEM_USE_BENCHMARK MFEM_USE_PARELAG MFEM_USE_TRIBOL MFEM_USE_ALGOIM MFEM_USE_ENZYME\
MFEM_SOURCE_DIR MFEM_INSTALL_DIR MFEM_SHARED_BUILD MFEM_USE_DOUBLE MFEM_USE_SINGLE
@@ -406,7 +406,7 @@ MFEM_INSTALL_DIR = $(abspath $(MFEM_PREFIX))
# If we have 'config' target, export variables used by config/makefile
ifneq (,$(filter config,$(MAKECMDGOALS)))
export $(MFEM_DEFINES) MFEM_DEFINES $(MFEM_CONFIG_VARS) MFEM_CONFIG_VARS
export VERBOSE HYPRE_OPT PUMI_DIR MUMPS_OPT GSLIB_OPT
export VERBOSE HYPRE_OPT PUMI_DIR MUMPS_OPT COMPLEX_MUMPS_OPT GSLIB_OPT
endif
# If we have 'install' target, export variables used by config/makefile
@@ -736,6 +736,7 @@ status info:
$(info MFEM_USE_SUPERLU = $(MFEM_USE_SUPERLU))
$(info MFEM_USE_SUPERLU5 = $(MFEM_USE_SUPERLU5))
$(info MFEM_USE_MUMPS = $(MFEM_USE_MUMPS))
$(info MFEM_USE_COMPLEX_MUMPS = $(MFEM_USE_COMPLEX_MUMPS))
$(info MFEM_USE_STRUMPACK = $(MFEM_USE_STRUMPACK))
$(info MFEM_USE_GINKGO = $(MFEM_USE_GINKGO))
$(info MFEM_USE_AMGX = $(MFEM_USE_AMGX))
+17 -8
View File
@@ -15751,9 +15751,18 @@ Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
{
MFEM_VERIFY(dim > 1, "Not implemented for 1D meshes.");
auto factor = [&](int N)
// Closest int divisor to the cubit root, going down.
auto factor3 = [](int N)
{
for (int i = static_cast<int>(sqrt(N)); i > 0; i--)
for (int i = static_cast<int>(round(cbrt(N))); i > 0; i--)
{ if (N % i == 0) { return i; } }
return 1;
};
// Closest int divisor to the square root, going down.
auto factor2 = [](int N)
{
for (int i = static_cast<int>(round(sqrt(N))); i > 0; i--)
{ if (N % i == 0) { return i; } }
return 1;
};
@@ -15777,22 +15786,22 @@ Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
int el0_x, el0_y, el0_z;
if (dim == 2)
{
mpi_x = factor(mpi_cnt);
mpi_x = factor2(mpi_cnt);
mpi_y = mpi_cnt / mpi_x;
// Switch order for better balance.
el0_y = factor(el0);
el0_y = factor2(el0);
el0_x = el0 / el0_y;
}
else
{
mpi_x = factor(mpi_cnt);
mpi_y = factor(mpi_cnt / mpi_x);
mpi_x = factor3(mpi_cnt);
mpi_y = factor2(mpi_cnt / mpi_x);
mpi_z = mpi_cnt / mpi_x / mpi_y;
// Switch order for better balance.
el0_z = factor(el0);
el0_y = factor(el0 / el0_z);
el0_z = factor3(el0);
el0_y = factor2(el0 / el0_z);
el0_x = el0 / el0_y / el0_z;
}
+4 -2
View File
@@ -14,14 +14,16 @@ util/weakform.cpp
util/complexweakform.cpp
util/blockstaticcond.cpp
util/complexstaticcond.cpp
util/pml.cpp)
util/pml.cpp
util/preconditioners.cpp)
list(APPEND DPG_HEADERS
util/weakform.hpp
util/complexweakform.hpp
util/blockstaticcond.hpp
util/complexstaticcond.hpp
util/pml.hpp)
util/pml.hpp
util/preconditioners.hpp)
if (MFEM_USE_MPI)
list(APPEND DPG_SOURCES
+60 -13
View File
@@ -12,20 +12,19 @@
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
MFEM_INSTALL_DIR ?= ../../mfem
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/dpg/,)
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/dpg,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
DPG_REAL_SEQ_SRC = util/weakform.cpp util/blockstaticcond.cpp
DPG_REAL_SEQ_SRC = util/weakform.cpp util/blockstaticcond.cpp util/utils.cpp util/preconditioners.cpp
DPG_REAL_PAR_SRC = $(DPG_REAL_SEQ_SRC) util/pweakform.cpp
DPG_REAL_OBJ = $(DPG_REAL_PAR_SRC:.cpp=.o)
DPG_COMPLEX_SEQ_SRC = util/complexweakform.cpp util/complexstaticcond.cpp util/pml.cpp
DPG_COMPLEX_PAR_SRC = $(DPG_COMPLEX_SEQ_SRC) util/pcomplexweakform.cpp
DPG_COMPLEX_SEQ_SRC = util/maxwell_utils.cpp util/utils.cpp util/complexblockform.cpp util/complexweakform.cpp util/complexstaticcond.cpp util/pml.cpp util/preconditioners.cpp
DPG_COMPLEX_PAR_SRC = $(DPG_COMPLEX_SEQ_SRC) util/pcomplexweakform.cpp util/pcomplexblockform.cpp
DPG_COMPLEX_OBJ = $(DPG_COMPLEX_PAR_SRC:.cpp=.o)
DIFFUSION_SRC = diffusion.cpp $(DPG_REAL_SEQ_SRC)
@@ -34,6 +33,12 @@ DIFFUSION_OBJ = $(DIFFUSION_SRC:.cpp=.o)
PDIFFUSION_SRC = pdiffusion.cpp $(DPG_REAL_PAR_SRC)
PDIFFUSION_OBJ = $(PDIFFUSION_SRC:.cpp=.o)
PDIFFUSIONPRIMAL_SRC = pdiffusion-primal.cpp $(DPG_REAL_PAR_SRC)
PDIFFUSIONPRIMAL_OBJ = $(PDIFFUSIONPRIMAL_SRC:.cpp=.o)
PVDIFFUSIONPRIMAL_SRC = pvectordiffusion-primal.cpp $(DPG_REAL_PAR_SRC)
PVDIFFUSIONPRIMAL_OBJ = $(PVDIFFUSIONPRIMAL_SRC:.cpp=.o)
CONVECTIONDIFFUSION_SRC = convection-diffusion.cpp $(DPG_REAL_SEQ_SRC)
CONVECTIONDIFFUSION_OBJ = $(CONVECTIONDIFFUSION_SRC:.cpp=.o)
@@ -46,14 +51,34 @@ ACOUSTICS_OBJ = $(ACOUSTICS_SRC:.cpp=.o)
PACOUSTICS_SRC = pacoustics.cpp $(DPG_COMPLEX_PAR_SRC)
PACOUSTICS_OBJ = $(PACOUSTICS_SRC:.cpp=.o)
PACOUSTICS_PRIMAL_SRC = pacoustics-primal.cpp $(DPG_COMPLEX_PAR_SRC)
PACOUSTICS_PRIMAL_OBJ = $(PACOUSTICS_PRIMAL_SRC:.cpp=.o)
MAXWELL_SRC = maxwell.cpp $(DPG_COMPLEX_SEQ_SRC)
MAXWELL_OBJ = $(MAXWELL_SRC:.cpp=.o)
PMAXWELL_SRC = pmaxwell.cpp $(DPG_COMPLEX_PAR_SRC)
PMAXWELL_OBJ = $(PMAXWELL_SRC:.cpp=.o)
PMAXWELL_V_SRC = pmaxwell-verify.cpp $(DPG_COMPLEX_PAR_SRC)
PMAXWELL_V_OBJ = $(PMAXWELL_V_SRC:.cpp=.o)
PMAXWELL_PRIMAL_SRC = pmaxwell-primal.cpp $(DPG_COMPLEX_PAR_SRC)
PMAXWELL_PRIMAL_OBJ = $(PMAXWELL_PRIMAL_SRC:.cpp=.o)
PMAXWELL_FEM_SRC = pmaxwell-fem.cpp $(DPG_COMPLEX_PAR_SRC)
PMAXWELL_FEM_OBJ = $(PMAXWELL_FEM_SRC:.cpp=.o)
PMAXWELL_UW_COUPLED_SRC = pmaxwell-coupled.cpp $(DPG_COMPLEX_PAR_SRC)
PMAXWELL_UW_COUPLED_OBJ = $(PMAXWELL_UW_COUPLED_SRC:.cpp=.o)
PMAXWELL_FEM_COUPLED_SRC = pmaxwell-coupled-fem.cpp $(DPG_COMPLEX_PAR_SRC)
PMAXWELL_FEM_COUPLED_OBJ = $(PMAXWELL_FEM_COUPLED_SRC:.cpp=.o)
SEQ_MINIAPPS = diffusion convection-diffusion acoustics maxwell
PAR_MINIAPPS = pdiffusion pconvection-diffusion pacoustics pmaxwell
PAR_MINIAPPS = pdiffusion pconvection-diffusion pacoustics pmaxwell pmaxwell-verify \
pacoustics-primal pmaxwell-primal pdiffusion-primal pvectordiffusion-primal \
pmaxwell-coupled pmaxwell-coupled-fem pmaxwell-fem
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
@@ -79,11 +104,6 @@ COMMON_LIB += $(if $(MFEM_SHARED:YES=),,\
$(CONFIG_MK) | lib-common
$(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@
util/%.o: $(SRC)util/%.cpp $(wildcard $(SRC)util/%.hpp) $(MFEM_LIB_FILE)\
$(CONFIG_MK) | lib-common
mkdir -p $(@D)
$(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@
all: $(MINIAPPS)
diffusion: $(DIFFUSION_OBJ)
@@ -95,6 +115,12 @@ convection-diffusion: $(CONVECTIONDIFFUSION_OBJ)
pdiffusion: $(PDIFFUSION_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PDIFFUSION_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pdiffusion-primal: $(PDIFFUSIONPRIMAL_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PDIFFUSIONPRIMAL_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pvectordiffusion-primal: $(PVDIFFUSIONPRIMAL_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PVDIFFUSIONPRIMAL_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pconvection-diffusion: $(PCONVECTIONDIFFUSION_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PCONVECTIONDIFFUSION_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
@@ -107,9 +133,30 @@ maxwell: $(MAXWELL_OBJ)
pacoustics: $(PACOUSTICS_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PACOUSTICS_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pacoustics-primal: $(PACOUSTICS_PRIMAL_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PACOUSTICS_PRIMAL_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pmaxwell: $(PMAXWELL_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pmaxwell-fem: $(PMAXWELL_FEM_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_FEM_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pmaxwell-verify: $(PMAXWELL_V_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_V_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pmaxwell-primal: $(PMAXWELL_PRIMAL_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_PRIMAL_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pmaxwell-primal-tokamak: $(PMAXWELL_PRIMAL_TOKAMAK_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_PRIMAL_TOKAMAK_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pmaxwell-coupled: $(PMAXWELL_UW_COUPLED_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_UW_COUPLED_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pmaxwell-coupled-fem: $(PMAXWELL_FEM_COUPLED_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_FEM_COUPLED_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
# Rule for building lib-common
lib-common:
+743
View File
@@ -0,0 +1,743 @@
// MFEM Ultraweak DPG acoustics example
//
// Compile with: make pacoustics
//
// sample runs
// mpirun -np 4 pacoustics-primal -o 3 -m ../../data/star.mesh -sref 1 -pref 2 -rnum 1.9 -sc -prob 0
// mpirun -np 4 pacoustics-primal -o 3 -m ../../data/inline-quad.mesh -sref 1 -pref 2 -rnum 5.2 -sc -prob 1
// mpirun -np 4 pacoustics-primal -o 4 -m ../../data/inline-tri.mesh -sref 1 -pref 2 -rnum 7.1 -sc -prob 1
// mpirun -np 4 pacoustics-primal -o 2 -m ../../data/inline-hex.mesh -sref 0 -pref 1 -rnum 1.9 -sc -prob 0
// Description:
// This example code demonstrates the use of MFEM to define and solve
// the "ultraweak" (UW) DPG formulation for the Helmholtz problem
// - Δ p - ω² p = f , in Ω
// p = p₀, on ∂Ω
// It solves the following kinds of problems
// a) f̃ = 0 and p₀ is a plane wave
// b) A manufactured solution problem where p_exact is a gaussian beam
// The DPG Primal deals with the Second Order Equation
// - Δ p - ω² p = f , in Ω (1)
// p = p₀, on ∂Ω
// The primal-DPG formulation is obtained by integration by parts of (1)
// and the introduction of a trace unknown on the mesh skeleton
// p ∈ H¹(Ω),
// p̂ ∈ H^-1/2(Ω)
// (∇p,∇q) + ω²(p,q) + <p̂,q> = 0, ∀ q ∈ H^1(Ω)
// p = p₀ on ∂Ω
#include "mfem.hpp"
#include "util/pcomplexweakform.hpp"
#include "util/pml.hpp"
#include "../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
using namespace mfem::common;
complex<double> acoustics_solution(const Vector & X);
void acoustics_solution_grad(const Vector & X,vector<complex<double>> &dp);
complex<double> acoustics_solution_laplacian(const Vector & X);
double p_exact_r(const Vector &x);
double p_exact_i(const Vector &x);
double rhs_func_r(const Vector &x);
double rhs_func_i(const Vector &x);
void gradp_exact_r(const Vector &x, Vector &gradu);
void gradp_exact_i(const Vector &x, Vector &gradu);
double d2_exact_r(const Vector &x);
double d2_exact_i(const Vector &x);
int dim;
double omega;
enum prob_type
{
plane_wave,
gaussian_beam
};
static const char *enum_str[] =
{
"plane_wave",
"gaussian_beam"
};
prob_type prob;
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "../../data/inline-quad.mesh";
int order = 1;
int delta_order = 1;
bool visualization = true;
double rnum=1.0;
bool static_cond = false;
int iprob = 0;
int sr = 0;
int pr = 0;
bool paraview = false;
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(&rnum, "-rnum", "--number-of-wavelengths",
"Number of wavelengths");
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
" 0: plane wave, 1: Gaussian beam");
args.AddOption(&delta_order, "-do", "--delta-order",
"Order enrichment for DPG test space.");
args.AddOption(&sr, "-sref", "--serial-ref",
"Number of parallel refinements.");
args.AddOption(&pr, "-pref", "--parallel-ref",
"Number of parallel refinements.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (iprob > 1) { iprob = 0; }
prob = (prob_type)iprob;
omega = 2.*M_PI*rnum;
if (myid == 0)
{
args.PrintOptions(cout);
}
Mesh mesh(mesh_file, 1, 1);
for (int i = 0; i<sr; i++)
{
mesh.UniformRefinement();
}
dim = mesh.Dimension();
MFEM_VERIFY(dim > 1, "Dimension = 1 is not supported in this example");
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
// Define spaces
enum TrialSpace
{
p_space = 0,
hatp_space = 1,
};
enum TestSpace
{
q_space = 0,
};
// H1 space for p
FiniteElementCollection *p_fec = new H1_FECollection(order,dim);
ParFiniteElementSpace *p_fes = new ParFiniteElementSpace(&pmesh,p_fec);
// H^-1/2 space for p̂
FiniteElementCollection * hatp_fec = new RT_Trace_FECollection(order-1,dim);
ParFiniteElementSpace *hatp_fes = new ParFiniteElementSpace(&pmesh,hatp_fec);
// testspace fe collections
int test_order = order+delta_order;
FiniteElementCollection * q_fec = new H1_FECollection(test_order, dim);
Array<ParFiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(p_fes);
trial_fes.Append(hatp_fes);
test_fec.Append(q_fec);
// Bilinear form Coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient negomeg2(-omega*omega);
ParComplexDPGWeakForm * a = new ParComplexDPGWeakForm(trial_fes,test_fec);
a->StoreMatrices(); // needed for AMR
// Trial itegrators
// (∇ p,∇ q)
a->AddTrialIntegrator(new DiffusionIntegrator(one),nullptr,
TrialSpace::p_space,TestSpace::q_space);
// ω² (p,q)
a->AddTrialIntegrator(new MixedScalarMassIntegrator(negomeg2), nullptr,
TrialSpace::p_space, TestSpace::q_space);
// < p̂,q >
a->AddTrialIntegrator(new TraceIntegrator,nullptr,
TrialSpace::hatp_space,TestSpace::q_space);
// test integrators
// (∇q,∇δq)
a->AddTestIntegrator(new DiffusionIntegrator(one),nullptr,
TestSpace::q_space, TestSpace::q_space);
// (q,δq)
a->AddTestIntegrator(new MassIntegrator(one),nullptr,
TestSpace::q_space, TestSpace::q_space);
// RHS
FunctionCoefficient f_rhs_r(rhs_func_r);
FunctionCoefficient f_rhs_i(rhs_func_i);
if (prob == prob_type::gaussian_beam)
{
a->AddDomainLFIntegrator(new DomainLFIntegrator(f_rhs_r),
new DomainLFIntegrator(f_rhs_i),
TestSpace::q_space);
}
socketstream p_out_r;
socketstream p_out_i;
if (myid == 0)
{
std::cout << "\n Ref |"
<< " Dofs |"
<< " ω |" ;
std::cout << " H¹ Error |"
<< " Rate |" ;
std::cout << " Residual |"
<< " Rate |"
<< " PCG it |" << endl;
std::cout << std::string(82,'-')
<< endl;
}
double res0 = 0.;
double err0 = 0.;
int dof0 = 0;
ParGridFunction p_r, p_i;
FunctionCoefficient pex_r(p_exact_r);
FunctionCoefficient pex_i(p_exact_i);
ParaViewDataCollection * paraview_dc = nullptr;
if (paraview)
{
paraview_dc = new ParaViewDataCollection(enum_str[prob], &pmesh);
paraview_dc->SetPrefixPath("ParaViewPrimal/Acoustics");
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("p_r",&p_r);
paraview_dc->RegisterField("p_i",&p_i);
}
if (static_cond) { a->EnableStaticCondensation(); }
for (int it = 0; it<=pr; it++)
{
a->Assemble();
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
p_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = p_fes->GetVSize();
offsets[2] = hatp_fes->GetVSize();
offsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
ParGridFunction p_gf_r(p_fes, x, offsets[0]);
ParGridFunction p_gf_i(p_fes, x, offsets.Last()+ offsets[0]);
p_gf_r.ProjectBdrCoefficient(pex_r, ess_bdr);
p_gf_i.ProjectBdrCoefficient(pex_i, ess_bdr);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
int num_blocks = BlockA_r->NumRowBlocks();
Array<int> tdof_offsets(2*num_blocks+1);
tdof_offsets[0] = 0;
for (int i=0; i<num_blocks; i++)
{
int h = BlockA_r->GetBlock(i,i).Height();
tdof_offsets[i+1] = h;
tdof_offsets[num_blocks+i+1] = h;
}
tdof_offsets.PartialSum();
BlockOperator blockA(tdof_offsets);
for (int i = 0; i<num_blocks; i++)
{
for (int j = 0; j<num_blocks; j++)
{
blockA.SetBlock(i,j,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i,j+num_blocks,&BlockA_i->GetBlock(i,j), -1.0);
blockA.SetBlock(i+num_blocks,j+num_blocks,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i+num_blocks,j,&BlockA_i->GetBlock(i,j));
}
}
X = 0.;
BlockDiagonalPreconditioner M(tdof_offsets);
M.owns_blocks=0;
HypreBoomerAMG * solver_p = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(0,0));
solver_p->SetPrintLevel(0);
M.SetDiagonalBlock(0,solver_p);
M.SetDiagonalBlock(num_blocks,solver_p);
HypreSolver * solver_hatp = nullptr;
if (dim == 2)
{
// AMS preconditioner for 2D H(div) (trace) space
solver_hatp = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(1,1),
hatp_fes);
dynamic_cast<HypreAMS*>(solver_hatp)->SetPrintLevel(0);
}
else
{
// ADS preconditioner for 3D H(div) (trace) space
solver_hatp = new HypreADS((HypreParMatrix &)BlockA_r->GetBlock(1,1),
hatp_fes);
dynamic_cast<HypreADS*>(solver_hatp)->SetPrintLevel(0);
}
M.SetDiagonalBlock(1,solver_hatp);
M.SetDiagonalBlock(num_blocks+1,solver_hatp);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-6);
cg.SetMaxIter(10000);
cg.SetPrintLevel(0);
cg.SetPreconditioner(M);
cg.SetOperator(blockA);
cg.Mult(B, X);
for (int i = 0; i<num_blocks; i++)
{
delete &M.GetDiagonalBlock(i);
}
int num_iter = cg.GetNumIterations();
a->RecoverFEMSolution(X,x);
Vector & residuals = a->ComputeResidual(x);
double residual = residuals.Norml2();
double maxresidual = residuals.Max();
double globalresidual = residual * residual;
MPI_Allreduce(MPI_IN_PLACE,&maxresidual,1,MPI_DOUBLE,MPI_MAX,MPI_COMM_WORLD);
MPI_Allreduce(MPI_IN_PLACE,&globalresidual,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
globalresidual = sqrt(globalresidual);
p_r.MakeRef(p_fes, x, 0);
p_i.MakeRef(p_fes, x, offsets.Last());
int dofs = 0;
for (int i = 0; i<trial_fes.Size(); i++)
{
dofs += trial_fes[i]->GlobalTrueVSize();
}
double H1Error = 0.0;
double rate_err = 0.0;
VectorFunctionCoefficient pex_grad_r(dim,gradp_exact_r);
VectorFunctionCoefficient pex_grad_i(dim,gradp_exact_i);
double p_err_r = p_r.ComputeH1Error(&pex_r,&pex_grad_r);
double p_err_i = p_i.ComputeH1Error(&pex_i,&pex_grad_i);
H1Error = sqrt(p_err_r*p_err_r + p_err_i*p_err_i);
rate_err = (it) ? dim*log(err0/H1Error)/log((double)dof0/dofs) : 0.0;
err0 = H1Error;
double rate_res = (it) ? dim*log(res0/globalresidual)/log((
double)dof0/dofs) : 0.0;
res0 = globalresidual;
dof0 = dofs;
if (myid == 0)
{
std::ios oldState(nullptr);
oldState.copyfmt(std::cout);
std::cout << std::right << std::setw(5) << it << " | "
<< std::setw(10) << dof0 << " | "
<< std::setprecision(1) << std::fixed
<< std::setw(4) << 2*rnum << " π | ";
std::cout << std::setprecision(3) << std::setw(10)
<< std::scientific << err0 << " | "
<< std::setprecision(2)
<< std::setw(6) << std::fixed << rate_err << " | " ;
std::cout << std::setprecision(3)
<< std::setw(10) << std::scientific << res0 << " | "
<< std::setprecision(2)
<< std::setw(6) << std::fixed << rate_res << " | "
<< std::setw(6) << std::fixed << num_iter << " | "
<< std::endl;
std::cout.copyfmt(oldState);
}
if (visualization)
{
const char * keys = (it == 0 && dim == 2) ? "jRcml\n" : nullptr;
char vishost[] = "localhost";
int visport = 19916;
VisualizeField(p_out_r,vishost, visport, p_r,
"Numerical presure (real part)", 0, 0, 500, 500, keys);
VisualizeField(p_out_i,vishost, visport, p_i,
"Numerical presure (imaginary part)", 501, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(it);
paraview_dc->SetTime((double)it);
paraview_dc->Save();
}
if (it == pr)
{
break;
}
pmesh.UniformRefinement();
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
}
if (paraview)
{
delete paraview_dc;
}
delete a;
delete q_fec;
delete hatp_fes;
delete hatp_fec;
delete p_fec;
delete p_fes;
return 0;
}
double p_exact_r(const Vector &x)
{
return acoustics_solution(x).real();
}
double p_exact_i(const Vector &x)
{
return acoustics_solution(x).imag();
}
void gradp_exact_r(const Vector &x, Vector &grad_r)
{
grad_r.SetSize(x.Size());
vector<complex<double>> grad;
acoustics_solution_grad(x,grad);
for (unsigned i = 0; i < grad.size(); i++)
{
grad_r[i] = grad[i].real();
}
}
void gradp_exact_i(const Vector &x, Vector &grad_i)
{
grad_i.SetSize(x.Size());
vector<complex<double>> grad;
acoustics_solution_grad(x,grad);
for (unsigned i = 0; i < grad.size(); i++)
{
grad_i[i] = grad[i].imag();
}
}
double d2_exact_r(const Vector &x)
{
return acoustics_solution_laplacian(x).real();
}
double d2_exact_i(const Vector &x)
{
return acoustics_solution_laplacian(x).imag();
}
// f = -Δ p - ω² p
double rhs_func_r(const Vector &x)
{
return -d2_exact_r(x) - omega * omega * p_exact_r(x);
}
// f = -Δ p - ω² p
double rhs_func_i(const Vector &x)
{
return -d2_exact_i(x) - omega * omega * p_exact_i(x);
}
complex<double> acoustics_solution(const Vector & X)
{
complex<double> zi = complex<double>(0., 1.);
switch (prob)
{
case plane_wave:
{
double beta = omega/std::sqrt((double)X.Size());
complex<double> alpha = beta * zi * X.Sum();
return exp(alpha);
}
break;
case gaussian_beam:
{
double rk = omega;
double degrees = 45;
double alpha = (180+degrees) * M_PI/180.;
double sina = sin(alpha);
double cosa = cos(alpha);
// shift the origin
double shift = 0.1;
double xprim=X(0) + shift;
double yprim=X(1) + shift;
double x = xprim*sina - yprim*cosa;
double y = xprim*cosa + yprim*sina;
//wavelength
double rl = 2.*M_PI/rk;
// beam waist radius
double w0 = 0.05;
// function w
double fact = rl/M_PI/(w0*w0);
double aux = 1. + (fact*y)*(fact*y);
double w = w0*sqrt(aux);
double phi0 = atan(fact*y);
double r = y + 1./y/(fact*fact);
// pressure
complex<double> ze = - x*x/(w*w) - zi*rk*y - zi * M_PI * x * x/rl/r +
zi*phi0/2.;
double pf = pow(2.0/M_PI/(w*w),0.25);
return pf*exp(ze);
}
break;
default:
MFEM_ABORT("Should be unreachable");
return 1;
break;
}
}
void acoustics_solution_grad(const Vector & X, vector<complex<double>> & dp)
{
dp.resize(X.Size());
complex<double> zi = complex<double>(0., 1.);
// initialize
for (int i = 0; i<X.Size(); i++) { dp[i] = 0.0; }
switch (prob)
{
case plane_wave:
{
double beta = omega/std::sqrt((double)X.Size());
complex<double> alpha = beta * zi * X.Sum();
complex<double> p = exp(alpha);
for (int i = 0; i<X.Size(); i++)
{
dp[i] = zi * beta * p;
}
}
break;
case gaussian_beam:
{
double rk = omega;
double degrees = 45;
double alpha = (180+degrees) * M_PI/180.;
double sina = sin(alpha);
double cosa = cos(alpha);
// shift the origin
double shift = 0.1;
double xprim=X(0) + shift;
double yprim=X(1) + shift;
double x = xprim*sina - yprim*cosa;
double y = xprim*cosa + yprim*sina;
double dxdxprim = sina, dxdyprim = -cosa;
double dydxprim = cosa, dydyprim = sina;
//wavelength
double rl = 2.*M_PI/rk;
// beam waist radius
double w0 = 0.05;
// function w
double fact = rl/M_PI/(w0*w0);
double aux = 1. + (fact*y)*(fact*y);
double w = w0*sqrt(aux);
double dwdy = w0*fact*fact*y/sqrt(aux);
double phi0 = atan(fact*y);
double dphi0dy = cos(phi0)*cos(phi0)*fact;
double r = y + 1./y/(fact*fact);
double drdy = 1. - 1./(y*y)/(fact*fact);
// pressure
complex<double> ze = - x*x/(w*w) - zi*rk*y - zi * M_PI * x * x/rl/r +
zi*phi0/2.;
complex<double> zdedx = -2.*x/(w*w) - 2.*zi*M_PI*x/rl/r;
complex<double> zdedy = 2.*x*x/(w*w*w)*dwdy - zi*rk + zi*M_PI*x*x/rl/
(r*r)*drdy + zi*dphi0dy/2.;
double pf = pow(2.0/M_PI/(w*w),0.25);
double dpfdy = -pow(2./M_PI/(w*w),-0.75)/M_PI/(w*w*w)*dwdy;
complex<double> zp = pf*exp(ze);
complex<double> zdpdx = zp*zdedx;
complex<double> zdpdy = dpfdy*exp(ze)+zp*zdedy;
dp[0] = (zdpdx*dxdxprim + zdpdy*dydxprim);
dp[1] = (zdpdx*dxdyprim + zdpdy*dydyprim);
}
break;
default:
MFEM_ABORT("Should be unreachable");
break;
}
}
complex<double> acoustics_solution_laplacian(const Vector & X)
{
complex<double> zi = complex<double>(0., 1.);
switch (prob)
{
case plane_wave:
{
double beta = omega/std::sqrt((double)X.Size());
complex<double> alpha = beta * zi * X.Sum();
return dim * beta * beta * exp(alpha);
}
break;
case gaussian_beam:
{
double rk = omega;
double degrees = 45;
double alpha = (180+degrees) * M_PI/180.;
double sina = sin(alpha);
double cosa = cos(alpha);
// shift the origin
double shift = 0.1;
double xprim=X(0) + shift;
double yprim=X(1) + shift;
double x = xprim*sina - yprim*cosa;
double y = xprim*cosa + yprim*sina;
double dxdxprim = sina, dxdyprim = -cosa;
double dydxprim = cosa, dydyprim = sina;
//wavelength
double rl = 2.*M_PI/rk;
// beam waist radius
double w0 = 0.05;
// function w
double fact = rl/M_PI/(w0*w0);
double aux = 1. + (fact*y)*(fact*y);
double w = w0*sqrt(aux);
double dwdy = w0*fact*fact*y/sqrt(aux);
double d2wdydy = w0*fact*fact*(1. - (fact*y)*(fact*y)/aux)/sqrt(aux);
double phi0 = atan(fact*y);
double dphi0dy = cos(phi0)*cos(phi0)*fact;
double d2phi0dydy = -2.*cos(phi0)*sin(phi0)*fact*dphi0dy;
double r = y + 1./y/(fact*fact);
double drdy = 1. - 1./(y*y)/(fact*fact);
double d2rdydy = 2./(y*y*y)/(fact*fact);
// pressure
complex<double> ze = - x*x/(w*w) - zi*rk*y - zi * M_PI * x * x/rl/r +
zi*phi0/2.;
complex<double> zdedx = -2.*x/(w*w) - 2.*zi*M_PI*x/rl/r;
complex<double> zdedy = 2.*x*x/(w*w*w)*dwdy - zi*rk + zi*M_PI*x*x/rl/
(r*r)*drdy + zi*dphi0dy/2.;
complex<double> zd2edxdx = -2./(w*w) - 2.*zi*M_PI/rl/r;
complex<double> zd2edxdy = 4.*x/(w*w*w)*dwdy + 2.*zi*M_PI*x/rl/(r*r)*drdy;
complex<double> zd2edydx = zd2edxdy;
complex<double> zd2edydy = -6.*x*x/(w*w*w*w)*dwdy*dwdy + 2.*x*x/
(w*w*w)*d2wdydy - 2.*zi*M_PI*x*x/rl/(r*r*r)*drdy*drdy
+ zi*M_PI*x*x/rl/(r*r)*d2rdydy + zi/2.*d2phi0dydy;
double pf = pow(2.0/M_PI/(w*w),0.25);
double dpfdy = -pow(2./M_PI/(w*w),-0.75)/M_PI/(w*w*w)*dwdy;
double d2pfdydy = -1./M_PI*pow(2./M_PI,-0.75)*(-1.5*pow(w,-2.5)
*dwdy*dwdy + pow(w,-1.5)*d2wdydy);
complex<double> zp = pf*exp(ze);
complex<double> zdpdx = zp*zdedx;
complex<double> zdpdy = dpfdy*exp(ze)+zp*zdedy;
complex<double> zd2pdxdx = zdpdx*zdedx + zp*zd2edxdx;
complex<double> zd2pdxdy = zdpdy*zdedx + zp*zd2edxdy;
complex<double> zd2pdydx = dpfdy*exp(ze)*zdedx + zdpdx*zdedy + zp*zd2edydx;
complex<double> zd2pdydy = d2pfdydy*exp(ze) + dpfdy*exp(
ze)*zdedy + zdpdy*zdedy + zp*zd2edydy;
return (zd2pdxdx*dxdxprim + zd2pdydx*dydxprim)*dxdxprim
+ (zd2pdxdy*dxdxprim + zd2pdydy*dydxprim)*dydxprim
+ (zd2pdxdx*dxdyprim + zd2pdydx*dydyprim)*dxdyprim
+ (zd2pdxdy*dxdyprim + zd2pdydy*dydyprim)*dydyprim;
}
break;
default:
MFEM_ABORT("Should be unreachable");
return 1;
break;
}
}
+60 -72
View File
@@ -121,6 +121,7 @@
#include "mfem.hpp"
#include "util/pcomplexweakform.hpp"
#include "util/pml.hpp"
#include "util/preconditioners.hpp"
#include "../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
@@ -192,6 +193,9 @@ int main(int argc, char *argv[])
int iprob = 0;
int sr = 0;
int pr = 0;
bool pmg = false;
int pmg_levels = -1;
real_t relax_factor = 2.0/3;
int visport = 19916;
bool exact_known = false;
bool with_pml = false;
@@ -216,6 +220,12 @@ int main(int argc, char *argv[])
"Number of parallel refinements.");
args.AddOption(&pr, "-pref", "--parallel-ref",
"Number of parallel refinements.");
args.AddOption(&pmg, "-pmg", "--p-refinement-multigrid", "-no-pmg",
"--no-p-refinement-multigrid", "Enable P-Refinement Multigrid.");
args.AddOption(&pmg_levels, "-pmgl","--p-refinement-multigrid-levels",
"Number of levels for P-Refinement Multigrid.");
args.AddOption(&relax_factor, "-rf", "--relaxation-factor",
"Relaxation factor for the p-multigrid smoother.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
@@ -536,7 +546,7 @@ int main(int argc, char *argv[])
<< " ω |" ;
if (exact_known)
{
std::cout << " L2 Error |"
std::cout << " L² Error |"
<< " Rate |" ;
}
std::cout << " Residual |"
@@ -618,91 +628,69 @@ int main(int argc, char *argv[])
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
int num_blocks = BlockA_r->NumRowBlocks();
Array<int> tdof_offsets(2*num_blocks+1);
tdof_offsets[0] = 0;
int skip = (static_cond) ? 0 : 2;
int k = (static_cond) ? 2 : 0;
for (int i=0; i<num_blocks; i++)
Array<ParFiniteElementSpace *> prec_fes;
if (static_cond)
{
tdof_offsets[i+1] = trial_fes[i+k]->GetTrueVSize();
tdof_offsets[num_blocks+i+1] = trial_fes[i+k]->GetTrueVSize();
}
tdof_offsets.PartialSum();
BlockOperator blockA(tdof_offsets);
for (int i = 0; i<num_blocks; i++)
{
for (int j = 0; j<num_blocks; j++)
{
blockA.SetBlock(i,j,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i,j+num_blocks,&BlockA_i->GetBlock(i,j), -1.0);
blockA.SetBlock(i+num_blocks,j+num_blocks,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i+num_blocks,j,&BlockA_i->GetBlock(i,j));
}
}
X = 0.;
BlockDiagonalPreconditioner M(tdof_offsets);
M.owns_blocks=0;
if (!static_cond)
{
HypreBoomerAMG * solver_p = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(0,0));
solver_p->SetPrintLevel(0);
solver_p->SetSystemsOptions(dim);
HypreBoomerAMG * solver_u = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(1,1));
solver_u->SetPrintLevel(0);
solver_u->SetSystemsOptions(dim);
M.SetDiagonalBlock(0,solver_p);
M.SetDiagonalBlock(1,solver_u);
M.SetDiagonalBlock(num_blocks,solver_p);
M.SetDiagonalBlock(num_blocks+1,solver_u);
}
HypreBoomerAMG * solver_hatp = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(skip,skip));
solver_hatp->SetPrintLevel(0);
HypreSolver * solver_hatu = nullptr;
if (dim == 2)
{
// AMS preconditioner for 2D H(div) (trace) space
solver_hatu = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(skip+1,skip+1),
hatu_fes);
dynamic_cast<HypreAMS*>(solver_hatu)->SetPrintLevel(0);
a->GetTraceFESpaces(prec_fes);
}
else
{
// ADS preconditioner for 3D H(div) (trace) space
solver_hatu = new HypreADS((HypreParMatrix &)BlockA_r->GetBlock(skip+1,skip+1),
hatu_fes);
dynamic_cast<HypreADS*>(solver_hatu)->SetPrintLevel(0);
prec_fes = trial_fes;
}
Solver * cprec = nullptr;
if (pmg)
{
#ifdef MFEM_USE_COMPLEX_MUMPS
bool mumps_coarse_solver = true;
#else
bool mumps_coarse_solver = false;
#endif
std::vector<Array<int>> ess_bdr_marker(prec_fes.Size());
for (int b = 0; b<prec_fes.Size(); b++)
{
if (pmesh.bdr_attributes.Size())
{
ess_bdr_marker[b].SetSize(pmesh.bdr_attributes.Max());
int ess_block = (static_cond) ? 0 : 2;
if (b == ess_block) // hatp
{
ess_bdr_marker[b] = ess_bdr;
}
else
{
ess_bdr_marker[b] = 0;
}
}
}
cprec = new ComplexPRefinementMultigrid(prec_fes, ess_bdr_marker, *Ahc,
pmg_levels, relax_factor, mumps_coarse_solver );
}
else
{
BlockDiagonalPreconditioner * real_prec = new BlockDiagonalPreconditioner(
BlockA_r->RowOffsets());
real_prec->owns_blocks = 1;
for (int i = 0; i<BlockA_r->NumRowBlocks(); i++)
{
auto prec = MakeFESpaceDefaultSolver(prec_fes[i],0);
prec->SetOperator(BlockA_r->GetBlock(i,i));
real_prec->SetDiagonalBlock(i,prec);
}
cprec = new ComplexPreconditioner(real_prec, true);
}
M.SetDiagonalBlock(skip,solver_hatp);
M.SetDiagonalBlock(skip+1,solver_hatu);
M.SetDiagonalBlock(skip+num_blocks,solver_hatp);
M.SetDiagonalBlock(skip+num_blocks+1,solver_hatu);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-6);
cg.SetMaxIter(10000);
cg.SetPrintLevel(0);
cg.SetPreconditioner(M);
cg.SetOperator(blockA);
cg.SetOperator(*Ahc);
cg.SetPreconditioner(*cprec);
cg.Mult(B, X);
for (int i = 0; i<num_blocks; i++)
{
delete &M.GetDiagonalBlock(i);
}
delete cprec;
int num_iter = cg.GetNumIterations();
+62 -26
View File
@@ -66,6 +66,7 @@
#include "mfem.hpp"
#include "util/pweakform.hpp"
#include "util/preconditioners.hpp"
#include "../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
@@ -121,6 +122,9 @@ int main(int argc, char *argv[])
real_t theta = 0.7;
bool static_cond = false;
epsilon = 1e0;
bool pmg = false;
int pmg_levels = -1;
real_t relax_factor = 2.0/3;
bool visualization = true;
int visport = 19916;
@@ -145,6 +149,12 @@ int main(int argc, char *argv[])
"Vector Coefficient beta");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pmg, "-pmg", "--p-refinement-multigrid", "-no-pmg",
"--no-p-refinement-multigrid", "Enable P-Refinement Multigrid.");
args.AddOption(&pmg_levels, "-pmgl","--p-refinement-multigrid-levels",
"Number of levels for P-Refinement Multigrid.");
args.AddOption(&relax_factor, "-rf", "--relaxation-factor",
"Relaxation factor for the p-multigrid smoother.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -453,44 +463,70 @@ int main(int argc, char *argv[])
BlockOperator * A = Ah.As<BlockOperator>();
BlockDiagonalPreconditioner M(A->RowOffsets());
M.owns_blocks = 1;
int skip = 0;
if (!static_cond)
Solver * preconditioner = nullptr;
Array<ParFiniteElementSpace *> prec_fes;
if (static_cond)
{
HypreBoomerAMG * amg0 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(0,0));
HypreBoomerAMG * amg1 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(1,1));
amg0->SetPrintLevel(0);
amg1->SetPrintLevel(0);
M.SetDiagonalBlock(0,amg0);
M.SetDiagonalBlock(1,amg1);
skip = 2;
}
HypreBoomerAMG * amg2 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(skip,
skip));
amg2->SetPrintLevel(0);
M.SetDiagonalBlock(skip,amg2);
HypreSolver * prec;
if (dim == 2)
{
// AMS preconditioner for 2D H(div) (trace) space
prec = new HypreAMS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatf_fes);
a->GetTraceFESpaces(prec_fes);
}
else
{
// ADS preconditioner for 3D H(div) (trace) space
prec = new HypreADS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatf_fes);
prec_fes = trial_fes;
}
if (pmg)
{
#ifdef MFEM_USE_MUMPS
bool mumps_coarse_solver = true;
#else
bool mumps_coarse_solver = false;
#endif
std::vector<Array<int>> ess_bdr_marker(prec_fes.Size());
for (int b = 0; b<prec_fes.Size(); b++)
{
if (pmesh.bdr_attributes.Size())
{
ess_bdr_marker[b].SetSize(pmesh.bdr_attributes.Max());
int ess_block = (static_cond) ? 0 : 2;
if (b == ess_block) // hatu space has essential bdr conditions
{
ess_bdr_marker[b] = ess_bdr_uhat;
}
else if (b == ess_block+1) // hatf space has essential bdr conditions
{
ess_bdr_marker[b] = ess_bdr_fhat;
}
else
{
ess_bdr_marker[b] = 0;
}
}
}
preconditioner = new PRefinementMultigrid(prec_fes, ess_bdr_marker, *A,
pmg_levels, relax_factor, mumps_coarse_solver);
}
else
{
preconditioner = new BlockDiagonalPreconditioner(A->RowOffsets());
auto block_diag = dynamic_cast<BlockDiagonalPreconditioner*>(preconditioner);
block_diag->owns_blocks = 1;
for (int i = 0; i<A->NumRowBlocks(); i++)
{
auto prec = MakeFESpaceDefaultSolver(prec_fes[i],0);
prec->SetOperator(A->GetBlock(i,i));
block_diag->SetDiagonalBlock(i,prec);
}
}
M.SetDiagonalBlock(skip+1,prec);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(0);
cg.SetPreconditioner(M);
cg.SetOperator(*A);
cg.SetPreconditioner(*preconditioner);
cg.Mult(B, X);
delete preconditioner;
int num_iter = cg.GetNumIterations();
a->RecoverFEMSolution(X,x);
+329
View File
@@ -0,0 +1,329 @@
// MFEM Primal DPG parallel example for diffusion
//
// Compile with: make pdiffusion-primal
//
// Sample runs
// mpirun -np 4 pdiffusion-primal -m ../../data/inline-quad.mesh -o 3 -sref 1 -pref 2
// - Δ u = f, in Ω
// u = u₀, on ∂Ω
// --------------------------------------
// | | u | σ̂ | RHS |
// --------------------------------------
// | v | (∇u,∇v) | -(σ̂ₙ,v) | (f,v) |
//
// u ∈ H¹(Ω), σ̂ₙ ∈ H^-1/2(Τ)
#include "mfem.hpp"
#include "util/pweakform.hpp"
#include "../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
using namespace mfem::common;
double exact_u(const Vector & X);
void exact_gradu(const Vector & X, Vector &gradu);
double exact_laplacian_u(const Vector & X);
void exact_hatsigma(const Vector & X, Vector & hatsigma);
double f_exact(const Vector & X);
int main(int argc, char *argv[])
{
// 0. Initialize MPI and HYPRE.
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
// 1. Parse command-line options.
const char *mesh_file = "../../data/inline-quad.mesh";
int order = 1;
int delta_order = 1;
int sref = 0; // initial uniform mesh refinements
int pref = 0; // parallel mesh refinements for AMR
bool static_cond = false;
bool visualization = true;
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(&delta_order, "-do", "--delta_order",
"Order enrichment for DPG test space.");
args.AddOption(&sref, "-sref", "--num-serial-refinements",
"Number of initial serial uniform refinements");
args.AddOption(&pref, "-pref", "--num-parallel-refinements",
"Number of AMR refinements");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
for (int i = 0; i<sref; i++)
{
mesh.UniformRefinement();
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
// H1 space for u
FiniteElementCollection *u_fec = new H1_FECollection(order,dim);
ParFiniteElementSpace *u_fes = new ParFiniteElementSpace(&pmesh,u_fec);
// H^-1/2 space for σ̂
FiniteElementCollection * hatsigma_fec = new RT_Trace_FECollection(order-1,dim);
ParFiniteElementSpace *hatsigma_fes = new ParFiniteElementSpace(&pmesh,
hatsigma_fec);
int test_order = order+delta_order;
FiniteElementCollection * v_fec = new H1_FECollection(test_order, dim);
Array<ParFiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(u_fes);
trial_fes.Append(hatsigma_fes);
test_fec.Append(v_fec);
ConstantCoefficient one(1.0);
FunctionCoefficient f(f_exact); // rhs for the manufactured solution problem
FunctionCoefficient uex(exact_u);
VectorFunctionCoefficient graduex(dim,exact_gradu);
ParDPGWeakForm * a = new ParDPGWeakForm(trial_fes,test_fec);
a->StoreMatrices(true); // this is needed for estimation of residual
// (∇u,∇v)
a->AddTrialIntegrator(new DiffusionIntegrator(one),0,0);
// -<σ̂,v> (sign is included in σ̂)
a->AddTrialIntegrator(new TraceIntegrator,1,0);
// (∇v,∇δv)
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
// (v,δv)
a->AddTestIntegrator(new MassIntegrator(one),0,0);
a->AddDomainLFIntegrator(new DomainLFIntegrator(f),0);
if (myid == 0)
{
std::cout << "\n Ref |"
<< " Dofs |"
<< " H1 Error |"
<< " Rate |"
<< " Residual |"
<< " Rate |"
<< " PCG it |" << endl;
std::cout << std::string(72,'-') << endl;
}
socketstream u_out;
double err0 = 0.;
int dof0=0.;
double res0=0.0;
ParGridFunction u_gf(u_fes);
u_gf = 0.0;
if (static_cond) { a->EnableStaticCondensation(); }
for (int it = 0; it<=pref; it++)
{
a->Assemble();
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
u_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = u_fes->GetVSize();
offsets[2] = hatsigma_fes->GetVSize();
offsets.PartialSum();
BlockVector x(offsets);
x = 0.0;
u_gf.MakeRef(u_fes,x.GetBlock(0),0);
u_gf.ProjectBdrCoefficient(uex,ess_bdr);
Vector X,B;
OperatorPtr Ah;
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
BlockOperator * A = Ah.As<BlockOperator>();
BlockDiagonalPreconditioner M(A->RowOffsets());
M.owns_blocks = 1;
HypreBoomerAMG * amg0 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(0,0));
amg0->SetPrintLevel(0);
M.SetDiagonalBlock(0,amg0);
HypreSolver * prec;
if (dim == 2)
{
// AMS preconditioner for 2D H(div) (trace) space
prec = new HypreAMS((HypreParMatrix &)A->GetBlock(1,1), hatsigma_fes);
}
else
{
// ADS preconditioner for 3D H(div) (trace) space
prec = new HypreADS((HypreParMatrix &)A->GetBlock(1,1), hatsigma_fes);
}
M.SetDiagonalBlock(1,prec);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(0);
cg.SetPreconditioner(M);
cg.SetOperator(*A);
cg.Mult(B, X);
a->RecoverFEMSolution(X,x);
Vector & residuals = a->ComputeResidual(x);
double residual = residuals.Norml2();
double maxresidual = residuals.Max();
double globalresidual = residual * residual;
MPI_Allreduce(MPI_IN_PLACE,&maxresidual,1,MPI_DOUBLE,MPI_MAX,MPI_COMM_WORLD);
MPI_Allreduce(MPI_IN_PLACE,&globalresidual,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
globalresidual = sqrt(globalresidual);
u_gf.MakeRef(u_fes,x.GetBlock(0),0);
int dofs = u_fes->GlobalTrueVSize() + hatsigma_fes->GlobalTrueVSize();
double u_err = u_gf.ComputeH1Error(&uex,&graduex);
double rate_err = (it) ? dim*log(err0/u_err)/log((double)dof0/dofs) : 0.0;
double rate_res = (it) ? dim*log(res0/globalresidual)/log((
double)dof0/dofs) : 0.0;
err0 = u_err;
res0 = globalresidual;
dof0 = dofs;
if (myid == 0)
{
std::ios oldState(nullptr);
oldState.copyfmt(std::cout);
std::cout << std::right << std::setw(5) << it << " | "
<< std::setw(10) << dof0 << " | "
<< std::setprecision(3)
<< std::setw(10) << std::scientific << err0 << " | "
<< std::setprecision(2)
<< std::setw(6) << std::fixed << rate_err << " | "
<< std::setprecision(3)
<< std::setw(10) << std::scientific << res0 << " | "
<< std::setprecision(2)
<< std::setw(6) << std::fixed << rate_res << " | "
<< std::setw(6) << std::fixed << cg.GetNumIterations() << " | "
<< std::endl;
std::cout.copyfmt(oldState);
}
if (visualization)
{
const char * keys = (it == 0 && dim == 2) ? "jRcm\n" : nullptr;
char vishost[] = "localhost";
int visport = 19916;
VisualizeField(u_out,vishost,visport,u_gf,
"Numerical u", 0,0,500,500,keys);
}
if (it == pref) { break; }
pmesh.UniformRefinement();
for (int i=0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
}
delete a;
delete v_fec;
delete hatsigma_fes;
delete hatsigma_fec;
delete u_fec;
delete u_fes;
return 0;
}
double exact_u(const Vector & X)
{
double alpha = M_PI * (X.Sum());
return sin(alpha);
}
void exact_gradu(const Vector & X, Vector & du)
{
du.SetSize(X.Size());
double alpha = M_PI * (X.Sum());
du.SetSize(X.Size());
for (int i = 0; i<du.Size(); i++)
{
du[i] = M_PI * cos(alpha);
}
}
double exact_laplacian_u(const Vector & X)
{
double alpha = M_PI * (X.Sum());
double u = sin(alpha);
return - M_PI*M_PI * u * X.Size();
}
void exact_hatsigma(const Vector & X, Vector & hatsigma)
{
exact_gradu(X,hatsigma);
hatsigma *= -1.;
}
double f_exact(const Vector & X)
{
return -exact_laplacian_u(X);
}
+54 -26
View File
@@ -70,6 +70,7 @@
#include "mfem.hpp"
#include "util/pweakform.hpp"
#include "util/preconditioners.hpp"
#include "../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
@@ -114,6 +115,9 @@ int main(int argc, char *argv[])
int sref = 0; // initial uniform mesh refinements
int pref = 0; // parallel mesh refinements for AMR
int iprob = 0;
bool pmg = false;
int pmg_levels = -1;
real_t relax_factor = 2.0/3;
bool static_cond = false;
real_t theta = 0.7;
bool visualization = true;
@@ -137,6 +141,12 @@ int main(int argc, char *argv[])
" 0: manufactured, 1: L-shape");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pmg, "-pmg", "--p-refinement-multigrid", "-no-pmg",
"--no-p-refinement-multigrid", "Enable P-Refinement Multigrid.");
args.AddOption(&pmg_levels, "-pmgl","--p-refinement-multigrid-levels",
"Number of levels for P-Refinement Multigrid.");
args.AddOption(&relax_factor, "-rf", "--relaxation-factor",
"Relaxation factor for the p-multigrid smoother.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -186,7 +196,6 @@ int main(int argc, char *argv[])
}
}
for (int i = 0; i<sref; i++)
{
mesh.UniformRefinement();
@@ -378,44 +387,63 @@ int main(int argc, char *argv[])
BlockOperator * A = Ah.As<BlockOperator>();
BlockDiagonalPreconditioner M(A->RowOffsets());
M.owns_blocks = 1;
int skip = 0;
if (!static_cond)
Solver * preconditioner = nullptr;
Array<ParFiniteElementSpace *> prec_fes;
if (static_cond)
{
HypreBoomerAMG * amg0 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(0,0));
HypreBoomerAMG * amg1 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(1,1));
amg0->SetPrintLevel(0);
amg1->SetPrintLevel(0);
M.SetDiagonalBlock(0,amg0);
M.SetDiagonalBlock(1,amg1);
skip=2;
}
HypreBoomerAMG * amg2 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(skip,
skip));
amg2->SetPrintLevel(0);
M.SetDiagonalBlock(skip,amg2);
HypreSolver * prec;
if (dim == 2)
{
// AMS preconditioner for 2D H(div) (trace) space
prec = new HypreAMS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatsigma_fes);
a->GetTraceFESpaces(prec_fes);
}
else
{
// ADS preconditioner for 3D H(div) (trace) space
prec = new HypreADS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatsigma_fes);
prec_fes = trial_fes;
}
if (pmg)
{
#ifdef MFEM_USE_MUMPS
bool mumps_coarse_solver = true;
#else
bool mumps_coarse_solver = false;
#endif
std::vector<Array<int>> ess_bdr_marker(prec_fes.Size());
for (int b = 0; b<prec_fes.Size(); b++)
{
ess_bdr_marker[b].SetSize(pmesh.bdr_attributes.Max());
int ess_block = (static_cond) ? 0 : 2;
if (b == ess_block)
{
ess_bdr_marker[b] = ess_bdr;
}
else
{
ess_bdr_marker[b] = 0;
}
}
preconditioner = new PRefinementMultigrid(prec_fes, ess_bdr_marker, *A,
pmg_levels, relax_factor,mumps_coarse_solver);
}
else
{
preconditioner = new BlockDiagonalPreconditioner(A->RowOffsets());
auto block_diag = dynamic_cast<BlockDiagonalPreconditioner*>(preconditioner);
block_diag->owns_blocks = 1;
for (int i = 0; i<A->NumRowBlocks(); i++)
{
auto prec = MakeFESpaceDefaultSolver(prec_fes[i],0);
prec->SetOperator(A->GetBlock(i,i));
block_diag->SetDiagonalBlock(i,prec);
}
}
M.SetDiagonalBlock(skip+1,prec);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(0);
cg.SetPreconditioner(M);
cg.SetOperator(*A);
cg.SetPreconditioner(*preconditioner);
cg.Mult(B, X);
delete preconditioner;
a->RecoverFEMSolution(X,x);
Vector & residuals = a->ComputeResidual(x);
File diff suppressed because it is too large Load Diff
+976
View File
@@ -0,0 +1,976 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
//
// MFEM Ultraweak DPG Maxwell parallel example
//
// Compile with: make lh-eld-dpg
//
// mpirun -np 8 ./lh-eld-dpg -o 3 -paraview -pr 0
// mpirun -np 8 ./lh-eld-dpg -o 3 -paraview -pr 1 -sc
// Electron Landau Damping
// Strong formulation:
// ∇×(1/μ₀∇×E) - ω² ϵ₀ ϵ E + i ω²ϵ₀(J₁ + J₂) = 0, in Ω
// - Δ∥ J₁ + c₁ J₁ - c₁ P(r) E∥ = 0, in Ω
// - Δ∥ J₂ + c₂ J₂ + c₂ P(r) E∥ = 0, in Ω
// E×n = E₀, on ∂Ω
// J₁ = 0, on ∂Ω
// J₂ = 0, on ∂Ω
// The DPG UW deals with the First Order System
// i ω μ₀ H + ∇ × E = 0, in Ω
// -i ω ϵ₀ϵ E + ∇ × H - ω ϵ₀ (J₁ + J₂) = 0, in Ω
// - Δ∥ J₁ + c₁ J₁ - c₁ P(r) E∥ = 0, in Ω
// - Δ∥ J₂ + c₂ J₂ + c₂ P(r) E∥ = 0, in Ω
// E×n = E₀, on ∂Ω
// J₁ = 0, on ∂Ω
// J₂ = 0, on ∂Ω
// in 2D
// E is vector valued and H is scalar.
// (∇ × E, δE) = (E, ∇ × δE ) + < n × E , δE >
// or (∇ ⋅ AE , δE) = (AE, ∇ δE ) + < AE ⋅ n, δE >
// where A = [0 1; -1 0];
// E ∈ (L²(Ω))² , H ∈ L²(Ω), J ∈ (H¹(Ω))²
// Ê ∈ H^-1/2(Γₕ), Ĥ ∈ H^1/2(Γₕ)
// iωμ₀ (H,δE) + (E,∇×δE) + < AÊ, δE > = 0, ∀ δE ∈ H¹(Ω)
// -i ωϵ₀ϵ (E,δH) + (H,∇×δH) + < Ĥ, δH×n > - ωϵ₀(J₁ + J₂,δH) = 0, ∀ δH ∈ H(curl,Ω)
// ( (b⋅∇)J₁,(b⋅∇) δJ₁ ) + c₁ (J₁,δJ₁) - c₁ (P(r) b⊗b E, δJ₁) = 0, ∀ δJ₁ ∈ (H¹(Ω))²
// ( (b⋅∇)J₂,(b⋅∇) δJ₂ ) + c₂ (J₂,δJ₂) + c₂ (P(r) b⊗b E, δJ₂) = 0, ∀ δJ₁ ∈ (H¹(Ω))²
// Ê = E₀, on ∂Ω
// J₁ = J₂ = 0, on ∂Ω
// ----------------------------------------------------------------------------------------------
// | | E | H | J₁ | J₂ | Ê | Ĥ | RHS |
// ----------------------------------------------------------------------------------------------
// |δE | (E,∇ × δE) |iωμ₀(H,δE)| | | <Ê,δE>| | 0 |
// | | | | | | | | |
// |δH | -iωϵ₀ϵ(E,δH) | (H,∇×δH) | -ωϵ₀ (J₁,δH) | -ωϵ₀ (J₂,δH) | |<Ĥ,δH×n>| 0 |
// | | | | | | | | |
// |δJ₁|-c₁(P(r)E,δJ₁)| |((b⋅∇)J₁,(b⋅∇)δJ₁)| | | | 0 |
// | | | | + c₁ (J₁,δJ₁)| | | | |
// |δJ₂| c₂(P(r)E,δJ₂)| | |((b⋅∇)J₂,(b⋅∇)δJ₂)| | | 0 |
// | | | | | + c₂ (J₂,δJ₂)| | | |
// where (δE,δH,δJ₁,δJ₂) ∈ H¹(Ω) × H(curl,Ω) × (H¹(Ω))² × (H¹(Ω))²
#include "mfem.hpp"
#include "../util/pcomplexweakform.hpp"
#include "../util/pcomplexblockform.hpp"
#include "../../common/mfem-common.hpp"
#include "../util/blockcomplexhypremat.hpp"
#include "../util/maxwell_utils.hpp"
#include "utils/lh_utils.hpp"
#include "../util/utils.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
using namespace mfem::common;
int main(int argc, char *argv[])
{
Mpi::Init();
Hypre::Init();
const char *mesh_file = "data/LH_hot.msh";
int order = 1;
int delta_order = 1;
int par_ref_levels = 0;
int ser_ref_levels = 0;
real_t rnum=1.5;
real_t mu = 1.257;
real_t eps0 = 8.8541878128;
real_t cfactor = 1e-6;
real_t balance_scale = 1.0;
bool enable_balance_scale = false;
bool visualization = false;
bool paraview = false;
bool debug = false;
bool mumps_solver = false;
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(&ser_ref_levels, "-sr", "--serial-refinement_levels",
"Number of serial refinement levels.");
args.AddOption(&par_ref_levels, "-pr", "--parallel-refinement_levels",
"Number of parallel refinement levels.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&a0, "-a0", "--a0", "P(r) first parameter.");
args.AddOption(&a1, "-a1", "--a1", "P(r) second parameter.");
args.AddOption(&delta, "-delta", "--delta", "stability parameter.");
args.AddOption(&mumps_solver, "-mumps", "--mumps", "-no-mumps",
"--no-mumps",
"Enable or disable MUMPS solver.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&debug, "-debug", "--debug", "-no-debug",
"--no-debug",
"Enable or disable debug mode (delta = 0.01 and no coupling).");
args.AddOption(&enable_balance_scale, "-ebs", "--enable-balance-scale", "-no-ebs",
"--no-ebs",
"Enable or disable balance scale.");
args.Parse();
if (!args.Good())
{
if (Mpi::Root())
{
args.PrintUsage(cout);
}
return 1;
}
if (Mpi::Root())
{
args.PrintOptions(cout);
}
// number of diffusion equations
int ndiffusionequations = 2;
Vector cvals(ndiffusionequations);
Vector csigns(ndiffusionequations);
cvals(0) = 25e6; cvals(1) = 1e6;
csigns(0) = -1.0; csigns(1) = 1.0;
cvals *= cfactor; // scale the coefficients
real_t omega = 2.*M_PI*rnum;
int test_order = order+delta_order;
balance_scale = (enable_balance_scale) ? omega * eps0 : 1.0;
if (!debug)
{
delta = 0.0; // disable delta if electron Landau damping is enabled
if (Mpi::Root())
{
cout << "Electron Landau damping enabled, delta set to 0.0." << endl;
}
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
MFEM_VERIFY(dim == 2, "Dimension != 2 is not supported in this example");
for (int i = 0; i < ser_ref_levels; i++)
{
mesh.UniformRefinement();
}
// mesh.RemoveInternalBoundaries();
Array<int> int_bdr_attr;
for (int i = 0; i < mesh.GetNBE(); i++)
{
if (mesh.FaceIsInterior(mesh.GetBdrElementFaceIndex(i)))
{
int_bdr_attr.Append(mesh.GetBdrAttribute(i));
}
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
for (int i = 0; i < par_ref_levels; i++)
{
pmesh.UniformRefinement();
}
int nattr = (pmesh.attributes.Size()) ? pmesh.attributes.Max() : 0;
Array<int> attr(nattr);
for (int i = 0; i<nattr; i++) { attr[i] = i+1; }
// Define coefficients
ConstantCoefficient muinv(1./mu);
ConstantCoefficient one_cf(1.0);
// ωμ₀
ConstantCoefficient omegamu_cf(omega*mu);
// -ω μ₀
ConstantCoefficient negomegamu_cf(-omega*mu);
// -ωϵ₀
real_t scale = (debug) ? 0.0 : 1.0;
ConstantCoefficient negomegeps0_cf(-omega*eps0 * scale);
ConstantCoefficient balancescaled_negomegeps0_cf( -omega*eps0/balance_scale * scale);
// μ₀² ω²
ConstantCoefficient mu2omeg2_cf((mu*mu*omega*omega));
Vector zero(dim); zero = 0.0;
Vector one_x(dim); one_x = 0.0; one_x(0) = 1.0;
Vector negone_x(dim); negone_x = 0.0; negone_x(0) = -1.0;
VectorConstantCoefficient zero_vcf(zero);
VectorConstantCoefficient one_x_cf(one_x);
VectorConstantCoefficient negone_x_cf(negone_x);
DenseMatrix Mone(dim);
Mone = 0.0; Mone(0,0) = Mone(1,1) = 1.0;
MatrixConstantCoefficient Mone_cf(Mone);
DenseMatrix Mzero(dim); Mzero = 0.0;
MatrixConstantCoefficient Mzero_cf(Mzero);
Array<MatrixCoefficient*> coefs_r(nattr);
Array<MatrixCoefficient*> coefs_i(nattr);
for (int i = 0; i < nattr-1; ++i)
{
coefs_r[i] = &Mone_cf;
coefs_i[i] = &Mzero_cf;
}
// S(r)
FunctionCoefficient S_cf_r(sfunc_r), S_cf_i(sfunc_i);
// P(r)
FunctionCoefficient P_cf_r(pfunc_r), P_cf_i(pfunc_i);
VectorFunctionCoefficient b_cf(dim,bfunc);// b
ScalarVectorProductCoefficient scaled_b_cf(sqrt(cfactor), b_cf);
MatrixFunctionCoefficient bb_cf(dim,bcrossb); // b⊗b
MatrixSumCoefficient oneminusbb(Mone_cf, bb_cf, 1.0, -1.0); // 1 - b⊗b
// S(r) (I - b⊗b)
ScalarMatrixProductCoefficient Soneminusbb_r(S_cf_r, oneminusbb), Soneminusbb_i(S_cf_i, oneminusbb);
// P(r) b⊗b
ScalarMatrixProductCoefficient P_cf_bb_r(P_cf_r, bb_cf), P_cf_bb_i(P_cf_i, bb_cf);
// ε = S(r) (I - b⊗b) + P(r) b⊗b
MatrixSumCoefficient eps_r(Soneminusbb_r, P_cf_bb_r, 1.0, 1.0);
MatrixSumCoefficient eps_i(Soneminusbb_i, P_cf_bb_i, 1.0, 1.0);
coefs_r[nattr-1] = &eps_r;
coefs_i[nattr-1] = &eps_i;
// for (int i = 0; i < nattr-1; ++i)
// {
// coefs_r[i] = &eps_r;
// coefs_i[i] = &eps_i;
// }
PWMatrixCoefficient eps_cf_r(dim, attr, coefs_r);
PWMatrixCoefficient eps_cf_i(dim, attr, coefs_i);
ConstantCoefficient eps0omeg(omega * eps0);
ConstantCoefficient negeps0omeg(-omega * eps0);
// ω ϵ₀ ϵᵣ
ScalarMatrixProductCoefficient eps0omeg_eps_r(eps0omeg, eps_cf_r);
// ω ϵ₀ ϵᵢ
ScalarMatrixProductCoefficient eps0omeg_eps_i(eps0omeg, eps_cf_i);
// -ω ϵ₀ ϵᵣ
ScalarMatrixProductCoefficient negeps0omeg_eps_r(negeps0omeg, eps_cf_r);
// -ω ϵ₀ ϵᵢ
ScalarMatrixProductCoefficient negeps0omeg_eps_i(eps0omeg, eps_cf_i);
// A = [0 1; -1 0]
DenseMatrix rot_mat(2);
rot_mat(0,0) = 0.; rot_mat(0,1) = 1.;
rot_mat(1,0) = -1.; rot_mat(1,1) = 0.;
MatrixConstantCoefficient rot(rot_mat);
// ω ϵ₀ ϵᵣ A
MatrixProductCoefficient eps0omeg_eps_r_rot(eps0omeg_eps_r, rot);
// ω ϵ₀ ϵᵢ A
MatrixProductCoefficient eps0omeg_eps_i_rot(eps0omeg_eps_i, rot);
// -ω ϵ₀ ϵᵣ A
MatrixProductCoefficient negeps0omeg_eps_r_rot(negeps0omeg_eps_r, rot);
// -ω ϵ₀ ϵᵢ A
MatrixProductCoefficient negeps0omeg_eps_i_rot(negeps0omeg_eps_i, rot);
// (ωϵ₀ϵ)(ωϵ₀ϵ)^* (δH, δH)
TransposeMatrixCoefficient eps0omeg_eps_r_t(eps0omeg_eps_r);
TransposeMatrixCoefficient eps0omeg_eps_i_t(eps0omeg_eps_i);
MatrixProductCoefficient MrMrt_cf(eps0omeg_eps_r, eps0omeg_eps_r_t);
MatrixProductCoefficient MiMit_cf(eps0omeg_eps_i, eps0omeg_eps_i_t);
MatrixProductCoefficient MiMrt_cf(eps0omeg_eps_i, eps0omeg_eps_r_t);
MatrixProductCoefficient MrMit_cf(eps0omeg_eps_r, eps0omeg_eps_i_t);
// (MᵣMᵣᵗ + MᵢMᵢᵗ) + i (MᵢMᵣᵗ - MᵣMᵢᵗ)
MatrixSumCoefficient Mreal_cf(MrMrt_cf,MiMit_cf);
MatrixSumCoefficient Mimag_cf(MiMrt_cf,MrMit_cf,1.0,-1.0);
// if ELD
Array<Vector *> c_arrays(ndiffusionequations);
Array<PWConstCoefficient *> pw_c_coeffs(ndiffusionequations);
Array<MatrixCoefficient *> cPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> cPibb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPibb_cf(ndiffusionequations);
Array<MatrixCoefficient *> balancescaled_signedcPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> balancescaled_signedcPibb_cf(ndiffusionequations);
Vector temp(nattr); temp=0.0;
Array<ConstantCoefficient *> c_coeffs(ndiffusionequations);
for (int i = 0; i<ndiffusionequations; i++)
{
temp[nattr-1] = cvals(i);
// temp = cvals(i);
pw_c_coeffs[i] = new PWConstCoefficient(temp);
c_coeffs[i] = new ConstantCoefficient(cvals(i));
cPrbb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_r);
cPibb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_i);
signedcPrbb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPrbb_cf[i]);
signedcPibb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPibb_cf[i]);
balancescaled_signedcPrbb_cf[i] = new ScalarMatrixProductCoefficient(balance_scale,*signedcPrbb_cf[i]);
balancescaled_signedcPibb_cf[i] = new ScalarMatrixProductCoefficient(balance_scale,*signedcPibb_cf[i]);
}
// ----------------------------------------------
// DPG UW formulation for Maxwell equations
// ----------------------------------------------
// Define DPG spaces for the Maxwell equations
// trial spaces for E, H, Ê, Ĥ
Array<FiniteElementCollection *> dpg_trial_fecols;
Array<FiniteElementCollection *> dpg_test_fecols;
Array<ParFiniteElementSpace *> dpg_pfes;
// Vector L2 space for E
dpg_trial_fecols.Append(new L2_FECollection(order-1, dim));
dpg_pfes.Append(new ParFiniteElementSpace(&pmesh, dpg_trial_fecols.Last(), dim));
// Scalar L2 space for H
dpg_trial_fecols.Append(new L2_FECollection(order-1, dim));
dpg_pfes.Append(new ParFiniteElementSpace(&pmesh, dpg_trial_fecols.Last()));
// Trial trace space for Ê
dpg_trial_fecols.Append(new RT_Trace_FECollection(order-1, dim));
dpg_pfes.Append(new ParFiniteElementSpace(&pmesh, dpg_trial_fecols.Last()));
// Trial trace space for Ĥ
dpg_trial_fecols.Append(new H1_Trace_FECollection(order, dim));
dpg_pfes.Append(new ParFiniteElementSpace(&pmesh, dpg_trial_fecols.Last()));
Array<HYPRE_BigInt> dpg_tdofs(dpg_pfes.Size());
for (int i = 0; i < dpg_pfes.Size(); ++i)
{
dpg_tdofs[i] = dpg_pfes[i]->GlobalTrueVSize();
if (Mpi::Root())
{
cout << "DPG ParFiniteElementSpace " << i << " has " << dpg_tdofs[i]
<< " true dofs." << endl;
}
}
if (Mpi::Root())
{
cout << "Total number of DPG true dofs: " << dpg_tdofs.Sum() << endl;
}
// test spaces for E and H
dpg_test_fecols.Append(new H1_FECollection(test_order, dim));
dpg_test_fecols.Append(new ND_FECollection(test_order, dim));
ParComplexDPGWeakForm * a_dpg = new ParComplexDPGWeakForm(dpg_pfes,dpg_test_fecols);
// (E,∇ × δE)
a_dpg->AddTrialIntegrator(new TransposeIntegrator(new MixedCurlIntegrator(one_cf)),
nullptr,0, 0);
// -i ω ϵ₀ (ϵE,δH) = - i ω ϵ₀(ϵᵣ + i ϵᵢ E, δH)
// = (ω ϵ₀ ϵᵢ E, δH) + i (-ω ϵ₀ϵᵣ E, δH)
a_dpg->AddTrialIntegrator(
new TransposeIntegrator(new VectorFEMassIntegrator(eps0omeg_eps_i)),
new TransposeIntegrator(new VectorFEMassIntegrator(negeps0omeg_eps_r)),
0, 1);
// iωμ₀(H,δE)
a_dpg->AddTrialIntegrator(nullptr, new MixedScalarMassIntegrator(omegamu_cf), 1, 0);
// a_dpg->AddTrialIntegrator(nullptr, new MassIntegrator(omegamu_cf), 1, 0);
// (H,∇ × δH)
a_dpg->AddTrialIntegrator(
new TransposeIntegrator(new MixedCurlIntegrator(one_cf)), nullptr, 1, 1);
// Trace integrators
// <Ê,δE>
a_dpg->AddTrialIntegrator(new TraceIntegrator,nullptr, 2, 0);
// <Ĥ,δH × n>
a_dpg->AddTrialIntegrator(new TangentTraceIntegrator,nullptr, 3, 1);
// test integrators
// (∇δE,∇δE)
a_dpg->AddTestIntegrator(new DiffusionIntegrator(one_cf),nullptr, 0, 0);
// (δE,δE)
a_dpg->AddTestIntegrator(new MassIntegrator(one_cf),nullptr, 0, 0);
// μ₀² ω² (δE,δE)
a_dpg->AddTestIntegrator(new MassIntegrator(mu2omeg2_cf),nullptr,0, 0);
// -i ω μ₀ (δE,∇ × δH) = i (δE, -ω μ₀ ∇ × δ H)
a_dpg->AddTestIntegrator(nullptr,
new TransposeIntegrator(new MixedCurlIntegrator(negomegamu_cf)),0, 1);
// -i ω ϵ₀ϵ(∇ × δE, δH) = -i (ωϵ₀(ϵᵣ+iϵᵢ) A ∇ δE,δE), A = [0 1; -1 0]
// = (ω ϵ₀ ϵᵢ A ∇ δE,δE) + i (-ω ϵ₀ ϵᵣ A ∇ δE,δE)
a_dpg->AddTestIntegrator(new MixedVectorGradientIntegrator(eps0omeg_eps_i_rot),
new MixedVectorGradientIntegrator(negeps0omeg_eps_r_rot),0, 1);
// i ω μ₀ (∇ × δH ,δE) = i (ω μ₀ ∇ × δH, δE )
a_dpg->AddTestIntegrator(nullptr,new MixedCurlIntegrator(omegamu_cf),
1, 0);
// i ω ϵ₀ϵ̄ (δH, ∇ × δE ) = i (ω ϵ₀(ϵᵣ -i ϵᵢ) δH, A ∇ δE)
// = ( δH, ω ϵ₀ ϵᵢ A ∇ δE) + i (δH, ω ϵ₀ ϵᵣ A ∇ δE)
a_dpg->AddTestIntegrator(
new TransposeIntegrator(new MixedVectorGradientIntegrator(eps0omeg_eps_i_rot)),
new TransposeIntegrator(new MixedVectorGradientIntegrator(eps0omeg_eps_r_rot)),1, 0);
// (ωϵ₀ϵ)(ωϵ₀ϵ)^* (δH, δH)
// (MᵣMᵣᵗ + MᵢMᵢᵗ) + i (MᵢMᵣᵗ - MᵣMᵢᵗ)
a_dpg->AddTestIntegrator(new VectorFEMassIntegrator(Mreal_cf),
new VectorFEMassIntegrator(Mimag_cf),1, 1);
// (∇×δH ,∇×δH)
a_dpg->AddTestIntegrator(new CurlCurlIntegrator(one_cf),nullptr,1,1);
// (δH,δH)
a_dpg->AddTestIntegrator(new VectorFEMassIntegrator(one_cf),nullptr,1,1);
a_dpg->Assemble();
// ----------------------------------------------
// Define FEM spaces for the diffusion equations
// ----------------------------------------------
Array<FiniteElementCollection *> fem_fecols;
Array<ParFiniteElementSpace *> fem_pfes;
for (int i = 0; i < ndiffusionequations; ++i)
{
fem_fecols.Append(new H1_FECollection(order, dim));
fem_pfes.Append(new ParFiniteElementSpace(&pmesh, fem_fecols.Last(), dim));
}
Array<HYPRE_BigInt> fem_tdofs(fem_pfes.Size());
for (int i = 0; i < fem_pfes.Size(); ++i)
{
fem_tdofs[i] = fem_pfes[i]->GlobalTrueVSize();
if (Mpi::Root())
{
cout << "FEM ParFiniteElementSpace " << i << " has " << fem_tdofs[i]
<< " true dofs." << endl;
}
}
if (Mpi::Root())
{
cout << "Total number of FEM true dofs: " << fem_tdofs.Sum() << endl;
}
ParComplexBlockForm *a_fem = new ParComplexBlockForm(fem_pfes);
for (int i = 0; i<ndiffusionequations; i++)
{
// ( (b⋅∇)Jᵢ , (b⋅∇) Gᵢ)
a_fem->AddDomainIntegrator(new DirectionalVectorDiffusionIntegrator(scaled_b_cf), nullptr, i, i);
// cᵢ (Jᵢ , Gᵢ)
a_fem->AddDomainIntegrator(new VectorMassIntegrator(*pw_c_coeffs[i]), nullptr, i, i);
}
a_fem->Assemble();
// ----------------------------------------------
// Cross term DPG-FEM coupling
// ----------------------------------------------
// -ωϵ₀ (J₁,δH)
// -ωϵ₀ (J₂,δH)
ParMixedBilinearForm * a_J1H = new ParMixedBilinearForm(fem_pfes[0], dpg_pfes[1]);
ParMixedBilinearForm * a_J2H = new ParMixedBilinearForm(fem_pfes[1], dpg_pfes[1]);
ParMixedBilinearForm * a_J1H_i = new ParMixedBilinearForm(fem_pfes[0], dpg_pfes[1]);
ParMixedBilinearForm * a_J2H_i = new ParMixedBilinearForm(fem_pfes[1], dpg_pfes[1]);
ConstantCoefficient dummy_cf(0.0);
a_J1H_i->AddDomainIntegrator(new VectorMassIntegrator(dummy_cf));
a_J2H_i->AddDomainIntegrator(new VectorMassIntegrator(dummy_cf));
a_J1H_i->Assemble(0);
a_J2H_i->Assemble(0);
// a_J1H->AddDomainIntegrator(new VectorMassIntegrator(negomegeps0_cf));
// a_J2H->AddDomainIntegrator(new VectorMassIntegrator(negomegeps0_cf));
a_J1H->AddDomainIntegrator(new VectorMassIntegrator(balancescaled_negomegeps0_cf));
a_J2H->AddDomainIntegrator(new VectorMassIntegrator(balancescaled_negomegeps0_cf));
a_J1H->Assemble(0);
a_J2H->Assemble(0);
// ±cᵢ(P(r) (b ⊗ b) E, δJᵢ))
ParMixedBilinearForm * a_EJ1 = new ParMixedBilinearForm(dpg_pfes[0], fem_pfes[0]);
ParMixedBilinearForm * a_EJ2 = new ParMixedBilinearForm(dpg_pfes[0], fem_pfes[1]);
ParMixedBilinearForm * a_EJ1_i = new ParMixedBilinearForm(dpg_pfes[0], fem_pfes[0]);
ParMixedBilinearForm * a_EJ2_i = new ParMixedBilinearForm(dpg_pfes[0], fem_pfes[1]);
// a_EJ1_i->AddDomainIntegrator(new VectorMassIntegrator(*signedcPibb_cf[0]));
// a_EJ2_i->AddDomainIntegrator(new VectorMassIntegrator(*signedcPibb_cf[1]));
a_EJ1_i->AddDomainIntegrator(new VectorMassIntegrator(*balancescaled_signedcPibb_cf[0]));
a_EJ2_i->AddDomainIntegrator(new VectorMassIntegrator(*balancescaled_signedcPibb_cf[1]));
a_EJ1_i->Assemble(0);
a_EJ2_i->Assemble(0);
// a_EJ1->AddDomainIntegrator(new VectorMassIntegrator(*signedcPrbb_cf[0]));
// a_EJ2->AddDomainIntegrator(new VectorMassIntegrator(*signedcPrbb_cf[1]));
a_EJ1->AddDomainIntegrator(new VectorMassIntegrator(*balancescaled_signedcPrbb_cf[0]));
a_EJ2->AddDomainIntegrator(new VectorMassIntegrator(*balancescaled_signedcPrbb_cf[1]));
a_EJ1->Assemble(0);
a_EJ2->Assemble(0);
for (int i = 0; i<ndiffusionequations; i++)
{
delete pw_c_coeffs[i];
delete c_coeffs[i];
delete cPrbb_cf[i];
delete cPibb_cf[i];
delete signedcPrbb_cf[i];
delete signedcPibb_cf[i];
}
// Assemble all the system and get the global matrix and right-hand side
Array<ParFiniteElementSpace *> all_pfes;
all_pfes.Append(dpg_pfes);
all_pfes.Append(fem_pfes);
int npfes = all_pfes.Size();
Array<int> all_offsets(npfes + 1); all_offsets[0] = 0;
Array<int> all_toffsets(npfes + 1); all_toffsets[0] = 0;
for (int i = 0; i < npfes; ++i)
{
all_offsets[i+1] = all_pfes[i]->GetVSize();
all_toffsets[i+1] = all_pfes[i]->GetTrueVSize();
}
all_offsets.PartialSum();
all_toffsets.PartialSum();
Array<int> empty;
OperatorPtr Ah_dpg, Ah_fem, Ah_J1H, Ah_J2H, Ah_EJ1, Ah_EJ2;
OperatorPtr Ah_J1H_i, Ah_J2H_i, Ah_EJ1_i, Ah_EJ2_i;
// 4x4 upper left block (E, H, Ê, Ĥ)
a_dpg->FormSystemMatrix(empty,Ah_dpg);
// 2x2 lower right block (J₁, J₂)
a_fem->FormSystemMatrix(empty,Ah_fem);
// cross term A₁₄
a_J1H->FormRectangularSystemMatrix(empty, empty, Ah_J1H);
a_J1H_i->FormRectangularSystemMatrix(empty, empty, Ah_J1H_i);
// cross term A₂₄
a_J2H->FormRectangularSystemMatrix(empty, empty, Ah_J2H);
a_J2H_i->FormRectangularSystemMatrix(empty, empty, Ah_J2H_i);
// cross term A₄₀
a_EJ1->FormRectangularSystemMatrix(empty, empty, Ah_EJ1);
a_EJ1_i->FormRectangularSystemMatrix(empty, empty, Ah_EJ1_i);
// cross term A₅₀
a_EJ2->FormRectangularSystemMatrix(empty, empty, Ah_EJ2);
a_EJ2_i->FormRectangularSystemMatrix(empty, empty, Ah_EJ2_i);
// put all the operators into a block operator
BlockOperator A_r(all_toffsets);
BlockOperator A_i(all_toffsets);
ComplexOperator * Ac_dpg = Ah_dpg.As<ComplexOperator>();
ComplexOperator * Ac_fem = Ah_fem.As<ComplexOperator>();
BlockOperator * BlockAdpg_r = dynamic_cast<BlockOperator *>(&Ac_dpg->real());
BlockOperator * BlockAdpg_i = dynamic_cast<BlockOperator *>(&Ac_dpg->imag());
BlockOperator * BlockAfem_r = dynamic_cast<BlockOperator *>(&Ac_fem->real());
BlockOperator * BlockAfem_i = dynamic_cast<BlockOperator *>(&Ac_fem->imag());
for (int i = 0; i < dpg_pfes.Size(); ++i)
{
for (int j = 0; j < dpg_pfes.Size(); ++j)
{
A_r.SetBlock(i, j, &BlockAdpg_r->GetBlock(i, j));
A_i.SetBlock(i, j, &BlockAdpg_i->GetBlock(i, j));
}
}
for (int i = 0; i < fem_pfes.Size(); ++i)
{
for (int j = 0; j < fem_pfes.Size(); ++j)
{
A_r.SetBlock(dpg_pfes.Size() + i, dpg_pfes.Size() + j, &BlockAfem_r->GetBlock(i, j));
A_i.SetBlock(dpg_pfes.Size() + i, dpg_pfes.Size() + j, &BlockAfem_i->GetBlock(i, j));
}
}
A_r.SetBlock(1, 4, Ah_J1H.Ptr());
A_r.SetBlock(1, 5, Ah_J2H.Ptr());
A_r.SetBlock(4, 0, Ah_EJ1.Ptr());
A_r.SetBlock(5, 0, Ah_EJ2.Ptr());
A_i.SetBlock(1, 4, Ah_J1H_i.Ptr());
A_i.SetBlock(1, 5, Ah_J2H_i.Ptr());
A_i.SetBlock(4, 0, Ah_EJ1_i.Ptr());
A_i.SetBlock(5, 0, Ah_EJ2_i.Ptr());
ComplexOperator * A = new ComplexOperator(&A_r, &A_i, false, false);
if (Mpi::Root())
{
mfem::out << "Complex Operator A finished successfully." << endl;
}
socketstream E_out_r;
Vector x(2*all_offsets.Last());
x = 0.;
Array<ParGridFunction *> pgf_r(npfes);
Array<ParGridFunction *> pgf_i(npfes);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i] = new ParGridFunction(all_pfes[i], x, all_offsets[i]);
pgf_i[i] = new ParGridFunction(all_pfes[i], x, all_offsets.Last() + all_offsets[i]);
}
L2_FECollection L2fec(order, dim);
ParFiniteElementSpace L2_fes(&pmesh, &L2fec);
ParGridFunction E_par_r(&L2_fes);
ParGridFunction E_par_i(&L2_fes);
ParaViewDataCollection * paraview_dc = nullptr;
std::string output_dir = "ParaView/UW-FEM/" + GetTimestamp();
if (paraview)
{
if (Mpi::Root()) { WriteParametersToFile(args, output_dir); }
std::ostringstream paraview_file_name;
std::string filename = GetFilename(mesh_file);
paraview_file_name << filename
<< "_par_ref_" << par_ref_levels
<< "_order_" << order
<< "_eld_1" ;
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
paraview_dc->SetPrefixPath(output_dir);
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",pgf_r[0]);
paraview_dc->RegisterField("E_i",pgf_i[0]);
paraview_dc->RegisterField("E_par_r",&E_par_r);
paraview_dc->RegisterField("E_par_i",&E_par_i);
paraview_dc->RegisterField("H_r",pgf_r[1]);
paraview_dc->RegisterField("H_i",pgf_i[1]);
paraview_dc->RegisterField("Jh_1_r",pgf_r[4]);
paraview_dc->RegisterField("Jh_1_i",pgf_i[4]);
paraview_dc->RegisterField("Jh_2_r",pgf_r[5]);
paraview_dc->RegisterField("Jh_2_i",pgf_i[5]);
}
Array<int> ess_tdof_list;
Array<int> ess_tdof_listJ;
Array<int> ess_bdr;
Array<int> one_r_bdr;
Array<int> one_i_bdr;
Array<int> negone_r_bdr;
Array<int> negone_i_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_r_bdr.SetSize(pmesh.bdr_attributes.Max());
one_i_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_r_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_i_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
// remove internal boundaries
for (int i = 0; i<int_bdr_attr.Size(); i++)
{
ess_bdr[int_bdr_attr[i]-1] = 0;
}
all_pfes[2]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
for (int j = 0; j < ess_tdof_list.Size(); j++)
{
ess_tdof_list[j] += all_toffsets[2];
}
ess_bdr = 1;
for (int i = 0; i<ndiffusionequations;i++)
{
ess_tdof_listJ.SetSize(0);
all_pfes[i+4]->GetEssentialTrueDofs(ess_bdr, ess_tdof_listJ);
for (int j = 0; j < ess_tdof_listJ.Size(); j++)
{
ess_tdof_listJ[j] += all_toffsets[i+4];
}
ess_tdof_list.Append(ess_tdof_listJ);
}
one_r_bdr = 0; one_i_bdr = 0;
negone_r_bdr = 0; negone_i_bdr = 0;
// attr = 30,2 (real)
one_r_bdr[30-1] = 1; one_r_bdr[2-1] = 1;
// attr = 26,6 (imag)
one_i_bdr[26-1] = 1; one_i_bdr[6-1] = 1;
// attr = 22,10 (real)
negone_r_bdr[22-1] = 1; negone_r_bdr[10-1] = 1;
// attr = 18,14 (imag)
negone_i_bdr[18-1] = 1; negone_i_bdr[14-1] = 1;
}
// rotate the vector
// (x,y) -> (y,-x)
Vector rot_one_x(dim); rot_one_x = 0.0; rot_one_x(1) = -1.0;
Vector rot_negone_x(dim); rot_negone_x = 0.0; rot_negone_x(1) = 1.0;
VectorConstantCoefficient rot_one_x_cf(rot_one_x);
VectorConstantCoefficient rot_negone_x_cf(rot_negone_x);
pgf_r[2]->ProjectBdrCoefficientNormal(rot_one_x_cf, one_r_bdr);
pgf_r[2]->ProjectBdrCoefficientNormal(rot_negone_x_cf, negone_r_bdr);
pgf_i[2]->ProjectBdrCoefficientNormal(rot_one_x_cf, one_i_bdr);
pgf_i[2]->ProjectBdrCoefficientNormal(rot_negone_x_cf, negone_i_bdr);
if (Mpi::Root())
{
mfem::out << "Boundary conditions finished." << endl;
}
BlockOperator * P = new BlockOperator(all_offsets, all_toffsets);
BlockMatrix * R = new BlockMatrix(all_toffsets, all_offsets);
P->owns_blocks = 0;
R->owns_blocks = 0;
for (int i = 0; i < npfes; i++)
{
HypreParMatrix * P_ = all_pfes[i]->Dof_TrueDof_Matrix();
P->SetBlock(i,i,P_);
const SparseMatrix * R_ = all_pfes[i]->GetRestrictionMatrix();
R->SetBlock(i, i, const_cast<SparseMatrix*>(R_));
}
if (Mpi::Root())
{
mfem::out << "Build prolongation finished" << endl;
}
int n = P->Width();
Vector B(2*n); B = 0.0;
Vector X(2*n);
Vector X_r(X, 0, n);
Vector X_i(X, n, n);
Vector x_r(x, 0, x.Size()/2);
Vector x_i(x, x.Size()/2, x.Size()/2);
R->Mult(x_r, X_r);
R->Mult(x_i, X_i);
ParBlockComplexSystem aa(A);
A = aa.EliminateBC(ess_tdof_list, X, B);
if (Mpi::Root())
{
mfem::out << "Eliminate BC finished successfully." << endl;
}
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&A->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&A->imag());
int nblocks = BlockA_r->NumRowBlocks();
Array2D<const HypreParMatrix*> A_r_matrices(nblocks, nblocks);
Array2D<const HypreParMatrix*> A_i_matrices(nblocks, nblocks);
for (int i = 0; i < nblocks; i++)
{
for (int j = 0; j < nblocks; j++)
{
if (!BlockA_r->IsZeroBlock(i,j))
{
A_r_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&BlockA_r->GetBlock(i,j));
}
else
{
A_r_matrices(i,j) = nullptr;
}
if (!BlockA_i->IsZeroBlock(i,j))
{
A_i_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&BlockA_i->GetBlock(i,j));
}
else
{
A_i_matrices(i,j) = nullptr;
}
}
}
HypreParMatrix * Ahr = HypreParMatrixFromBlocks(A_r_matrices);
HypreParMatrix * Ahi = HypreParMatrixFromBlocks(A_i_matrices);
ComplexHypreParMatrix * Ahc_hypre =
new ComplexHypreParMatrix(Ahr, Ahi,false, false);
if (Mpi::Root())
{
mfem::out << "Getting ready for solve." << endl;
}
#ifdef MFEM_USE_COMPLEX_MUMPS
if (mumps_solver)
{
auto solver = new ComplexMUMPSSolver(MPI_COMM_WORLD);
solver->SetPrintLevel(1);
solver->SetOperator(*Ahc_hypre);
solver->Mult(B,X);
delete solver;
delete Ahc_hypre;
}
#else
if (mumps_solver)
{
MFEM_WARNING("MFEM compiled without mumps. Switching to an iterative solver");
}
mumps_solver = false;
#endif
Array<int> tdof_offsets(nblocks*2+1);
tdof_offsets[0] = 0;
for (int i=0; i<nblocks; i++)
{
tdof_offsets[i+1] = A_r_matrices(i,i)->Height();
tdof_offsets[nblocks+i+1] = tdof_offsets[i+1];
}
tdof_offsets.PartialSum();
if (!mumps_solver)
{
BlockDiagonalPreconditioner M(tdof_offsets);
HypreBoomerAMG * solver_E = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(0,0));
solver_E->SetPrintLevel(0);
solver_E->SetSystemsOptions(dim);
HypreBoomerAMG * solver_H = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(1,1));
solver_H->SetPrintLevel(0);
M.SetDiagonalBlock(0,solver_E);
M.SetDiagonalBlock(1,solver_H);
M.SetDiagonalBlock(nblocks,solver_E);
M.SetDiagonalBlock(nblocks+1,solver_H);
HypreAMS * solver_hatE =
new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(2,2), dpg_pfes[2]);
HypreBoomerAMG * solver_hatH = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(3,3));
solver_hatE->SetPrintLevel(0);
solver_hatH->SetPrintLevel(0);
solver_hatH->SetRelaxType(88);
M.SetDiagonalBlock(2,solver_hatE);
M.SetDiagonalBlock(3,solver_hatH);
M.SetDiagonalBlock(2+nblocks,solver_hatE);
M.SetDiagonalBlock(3+nblocks,solver_hatH);
HypreBoomerAMG * solver_J1 = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(4,4));
solver_J1->SetPrintLevel(0);
solver_J1->SetSystemsOptions(dim);
solver_J1->SetRelaxType(88);
HypreBoomerAMG * solver_J2 = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(5,5));
solver_J2->SetPrintLevel(0);
solver_J2->SetSystemsOptions(dim);
solver_J2->SetRelaxType(88);
M.SetDiagonalBlock(4,solver_J1);
M.SetDiagonalBlock(5,solver_J2);
M.SetDiagonalBlock(4+nblocks,solver_J1);
M.SetDiagonalBlock(5+nblocks,solver_J2);
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetRelTol(1e-10);
gmres.SetMaxIter(1000);
gmres.SetPrintLevel(1);
gmres.SetPreconditioner(M);
gmres.SetOperator(*A);
gmres.Mult(B, X);
}
n = P->Height();
int m = P->Width();
x_r.MakeRef(x, 0, n);
x_i.MakeRef(x, n, n);
X_r.MakeRef(X, 0, m);
X_i.MakeRef(X, m, m);
P->Mult(X_r, x_r);
P->Mult(X_i, x_i);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i]->MakeRef(all_pfes[i], x, all_offsets[i]);
pgf_i[i]->MakeRef(all_pfes[i], x, all_offsets.Last() + all_offsets[i]);
}
ParallelECoefficient par_e_r(pgf_r[0]);
ParallelECoefficient par_e_i(pgf_i[0]);
E_par_r.ProjectCoefficient(par_e_r);
E_par_i.ProjectCoefficient(par_e_i);
if (visualization)
{
const char * keys = nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, *pgf_r[0],
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(0);
paraview_dc->SetTime((real_t)0);
paraview_dc->Save();
delete paraview_dc;
}
delete a_fem;
for (int i = 0; i < fem_fecols.Size(); ++i)
{
delete fem_fecols[i];
delete fem_pfes[i];
}
delete a_dpg;
for (int i = 0; i < dpg_trial_fecols.Size(); ++i)
{
delete dpg_trial_fecols[i];
delete dpg_pfes[i];
}
for (int i = 0; i< dpg_test_fecols.Size(); ++i)
{
delete dpg_test_fecols[i];
}
return 0;
}
+907
View File
@@ -0,0 +1,907 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
//
// MFEM Ultraweak DPG Maxwell parallel example
//
// Compile with: make lh-eld-dpg
//
// mpirun -np 8 ./lh-eld-dpg -o 4 -paraview -eld -m data/quad.msh -ebs -sc
// Electron Landau Damping
// Strong formulation:
// ∇×(1/μ₀∇×E) - ω² ϵ₀ ϵ E + i ω²ϵ₀(J₁ + J₂) = 0, in Ω
// - Δ∥ J₁ + c₁ J₁ - c₁ P(r) E∥ = 0, in Ω
// - Δ∥ J₂ + c₂ J₂ + c₂ P(r) E∥ = 0, in Ω
// E×n = E₀, on ∂Ω
// J₁ = 0, on ∂Ω
// J₂ = 0, on ∂Ω
// The DPG UW deals with the First Order System
// i ω μ₀ H + ∇ × E = 0, in Ω
// -i ω ϵ₀ϵ E + ∇ × H - ω ϵ₀ (J₁ + J₂) = 0, in Ω
// - Δ∥ J₁ + c₁ J₁ - c₁ P(r) E∥ = 0, in Ω
// - Δ∥ J₂ + c₂ J₂ + c₂ P(r) E∥ = 0, in Ω
// E×n = E₀, on ∂Ω
// J₁ = 0, on ∂Ω
// J₂ = 0, on ∂Ω
// in 2D
// E is vector valued and H is scalar.
// (∇ × E, δE) = (E, ∇ × δE ) + < n × E , δE >
// or (∇ ⋅ AE , δE) = (AE, ∇ δE ) + < AE ⋅ n, δE >
// where A = [0 1; -1 0];
// E ∈ (L²(Ω))² , H ∈ L²(Ω), J ∈ (H¹(Ω))²
// Ê ∈ H^-1/2(Γₕ), Ĥ ∈ H^1/2(Γₕ), Ĵ₁, Ĵ₂ ∈ (H^-1/2(Γₕ))²
// iωμ₀ (H,δE) + (E,∇×δE) + < AÊ, δE > = 0, ∀ δE ∈ H¹(Ω)
// -i ωϵ₀ϵ (E,δH) + (H,∇×δH) + < Ĥ, δH×n > - ω ϵ₀ (J₁ + J₂,δH) = 0, ∀ δH ∈ H(curl,Ω)
// ( (b⋅∇)J₁,(b⋅∇) δJ₁ ) + <Ĵ₁, δJ₁> + c₁ (J₁,δJ₁) - c₁ (P(r) b⊗b E, δJ₁) = 0, ∀ δJ₁ ∈ (H¹(Ω))²
// ( (b⋅∇)J₂,(b⋅∇) δJ₂ ) + <Ĵ₂, δJ₂> + c₂ (J₂,δJ₂) + c₂ (P(r) b⊗b E, δJ₂) = 0, ∀ δJ₂ ∈ (H¹(Ω))²
// Ê = E₀, on ∂Ω
// Ĵ₁ = Ĵ₂ = 0, on ∂Ω
// ----------------------------------------------------------------------------------------------------------------
// | | E | H | J₁ | J₂ | Ê | Ĥ | Ĵ₁ | Ĵ₂ | RHS |
// ----------------------------------------------------------------------------------------------------------------
// |δE | (E,∇ × δE) |iωμ₀(H,δE)| | | <Ê,δE>| | | | 0 |
// | | | | | | | | | | |
// |δH | -iωϵ₀ϵ(E,δH) | (H,∇×δH) | -ωϵ₀ (J₁,δH) | -ωϵ₀ (J₂,δH) | |<Ĥ,δH×n>| | | 0 |
// | | | | | | | | | | |
// |δJ₁|-c₁(P(r)E,δJ₁)| |((b⋅∇)J₁,(b⋅∇)δJ₁)| | | |<Ĵ₁,δJ₁>| | 0 |
// | | | | + c₁ (J₁,δJ₁)| | | | | | |
// |δJ₂| c₂(P(r)E,δJ₂)| | |((b⋅∇)J₂,(b⋅∇)δJ₂)| | | |<Ĵ₂,δJ₂>| 0 |
// | | | | | + c₂ (J₂,δJ₂)| | | | | |
// where (δE,δH,δJ₁,δJ₂) ∈ H¹(Ω) × H(curl,Ω) × (H¹(Ω))² × (H¹(Ω))²
#include "mfem.hpp"
#include "../util/pcomplexweakform.hpp"
#include "../../common/mfem-common.hpp"
#include "../util/maxwell_utils.hpp"
#include "../util/preconditioners.hpp"
#include "utils/lh_utils.hpp"
#include "../util/utils.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
using namespace mfem::common;
int main(int argc, char *argv[])
{
Mpi::Init();
Hypre::Init();
const char *mesh_file = "data/LH_hot.msh";
int order = 1;
int delta_order = 1;
int par_ref_levels = 0;
int ser_ref_levels = 0;
// real_t rnum=1.5e9;
// real_t mu = 1.257e-6;
// real_t eps0 = 8.8541878128e-12;
real_t rnum=1.5;
real_t mu = 1.257;
real_t eps0 = 8.8541878128;
real_t cfactor = 1e-6;
real_t balance_scale = 1.0;
bool enable_balance_scale = false;
bool eld = false; // enable/disable electron Landau damping
real_t delta_prec = 0.0;
bool static_cond = false;
bool visualization = false;
bool paraview = false;
bool debug = false;
bool mumps_solver = false;
bool pmg = false;
int pmg_levels = -1;
real_t relax_factor = 2.0/3;
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(&delta_order, "-do", "--delta-order",
"Finite element order for the test space");
args.AddOption(&ser_ref_levels, "-sr", "--serial-refinement_levels",
"Number of serial refinement levels.");
args.AddOption(&par_ref_levels, "-pr", "--parallel-refinement_levels",
"Number of parallel refinement levels.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&a0, "-a0", "--a0", "P(r) first parameter.");
args.AddOption(&a1, "-a1", "--a1", "P(r) second parameter.");
args.AddOption(&delta_prec, "-dp", "--delta-prec", "stability parameter for the preconditioner.");
args.AddOption(&eld, "-eld", "--eld", "-no-eld",
"--no-eld",
"Enable or disable electron Landau damping.");
args.AddOption(&mumps_solver, "-mumps", "--mumps", "-no-mumps",
"--no-mumps",
"Enable or disable MUMPS solver.");
args.AddOption(&pmg, "-pmg", "--p-refinement-multigrid", "-no-pmg",
"--no-p-refinement-multigrid", "Enable P-Refinement Multigrid.");
args.AddOption(&pmg_levels, "-pmgl","--p-refinement-multigrid-levels",
"Number of levels for P-Refinement Multigrid.");
args.AddOption(&relax_factor, "-rf", "--relaxation-factor",
"Relaxation factor for the p-multigrid smoother.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&debug, "-debug", "--debug", "-no-debug",
"--no-debug",
"Enable or disable debug mode (delta = 0.01 and no coupling).");
args.AddOption(&enable_balance_scale, "-ebs", "--enable-balance-scale", "-no-ebs",
"--no-ebs",
"Enable or disable balance scale.");
args.Parse();
if (!args.Good())
{
if (Mpi::Root())
{
args.PrintUsage(cout);
}
return 1;
}
if (Mpi::Root())
{
args.PrintOptions(cout);
}
// number of diffusion equations
int ndiffusionequations = (eld) ? 2 : 0;
Vector cvals(ndiffusionequations);
Vector csigns(ndiffusionequations);
if (eld)
{
cvals(0) = 25e6; cvals(1) = 1e6;
csigns(0) = -1.0; csigns(1) = 1.0;
}
cvals *= cfactor; // scale the coefficients
real_t omega = 2.*M_PI*rnum;
int test_order = order+delta_order;
balance_scale = (enable_balance_scale) ? omega * eps0 * omega : 1.0;
if (eld && !debug)
{
delta = 0.0; // disable delta if electron Landau damping is enabled
if (Mpi::Root())
{
cout << "Electron Landau damping enabled, delta set to 0.0." << endl;
}
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
MFEM_VERIFY(dim == 2, "Dimension != 2 is not supported in this example");
for (int i = 0; i < ser_ref_levels; i++)
{
mesh.UniformRefinement();
}
// mesh.RemoveInternalBoundaries();
Array<int> int_bdr_attr;
for (int i = 0; i < mesh.GetNBE(); i++)
{
if (mesh.FaceIsInterior(mesh.GetBdrElementFaceIndex(i)))
{
int_bdr_attr.Append(mesh.GetBdrAttribute(i));
}
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
for (int i = 0; i < par_ref_levels; i++)
{
pmesh.UniformRefinement();
}
int nattr = (pmesh.attributes.Size()) ? pmesh.attributes.Max() : 0;
Array<int> attr(nattr);
for (int i = 0; i<nattr; i++) { attr[i] = i+1; }
// Define coefficients
ConstantCoefficient muinv(1./mu);
ConstantCoefficient one_cf(1.0);
// ωμ₀
ConstantCoefficient omegamu_cf(omega*mu);
// -ω μ₀
ConstantCoefficient negomegamu_cf(-omega*mu);
// -ωϵ₀
real_t scale = (debug) ? 0.0 : 1.0;
ConstantCoefficient negomegeps0_cf(-omega*eps0 * scale);
ConstantCoefficient balancescaled_negomegeps0_cf( -omega*eps0/balance_scale * scale);
// μ₀² ω²
ConstantCoefficient mu2omeg2_cf((mu*mu*omega*omega));
Vector zero(dim); zero = 0.0;
Vector one_x(dim); one_x = 0.0; one_x(0) = 1.0;
Vector negone_x(dim); negone_x = 0.0; negone_x(0) = -1.0;
VectorConstantCoefficient zero_vcf(zero);
VectorConstantCoefficient one_x_cf(one_x);
VectorConstantCoefficient negone_x_cf(negone_x);
DenseMatrix Mone(dim);
Mone = 0.0; Mone(0,0) = Mone(1,1) = 1.0;
MatrixConstantCoefficient Mone_cf(Mone);
DenseMatrix Mzero(dim); Mzero = 0.0;
MatrixConstantCoefficient Mzero_cf(Mzero);
Array<MatrixCoefficient*> coefs_r(nattr);
Array<MatrixCoefficient*> coefs_i(nattr);
for (int i = 0; i < nattr-1; ++i)
{
coefs_r[i] = &Mone_cf;
coefs_i[i] = &Mzero_cf;
}
// S(r)
FunctionCoefficient S_cf_r(sfunc_r), S_cf_i(sfunc_i);
// P(r)
FunctionCoefficient P_cf_r(pfunc_r), P_cf_i(pfunc_i);
VectorFunctionCoefficient b_cf(dim,bfunc);// b
ScalarVectorProductCoefficient scaled_b_cf(sqrt(cfactor), b_cf);
ConstantCoefficient diff_coeff(cfactor);
MatrixFunctionCoefficient bb_cf(dim,bcrossb); // b⊗b
MatrixSumCoefficient oneminusbb(Mone_cf, bb_cf, 1.0, -1.0); // 1 - b⊗b
// S(r) (I - b⊗b)
ScalarMatrixProductCoefficient Soneminusbb_r(S_cf_r, oneminusbb), Soneminusbb_i(S_cf_i, oneminusbb);
// P(r) b⊗b
ScalarMatrixProductCoefficient P_cf_bb_r(P_cf_r, bb_cf), P_cf_bb_i(P_cf_i, bb_cf);
// ε = S(r) (I - b⊗b) + P(r) b⊗b
MatrixSumCoefficient eps_r(Soneminusbb_r, P_cf_bb_r, 1.0, 1.0);
MatrixSumCoefficient eps_i(Soneminusbb_i, P_cf_bb_i, 1.0, 1.0);
coefs_r[nattr-1] = &eps_r;
coefs_i[nattr-1] = &eps_i;
// for (int i = 0; i < nattr-1; ++i)
// {
// coefs_r[i] = &eps_r;
// coefs_i[i] = &eps_i;
// }
PWMatrixCoefficient eps_cf_r(dim, attr, coefs_r);
PWMatrixCoefficient eps_cf_i(dim, attr, coefs_i);
ConstantCoefficient eps0omeg(omega * eps0);
ConstantCoefficient negeps0omeg(-omega * eps0);
// ω ϵ₀ ϵᵣ
ScalarMatrixProductCoefficient eps0omeg_eps_r(eps0omeg, eps_cf_r);
// ω ϵ₀ ϵᵢ
ScalarMatrixProductCoefficient eps0omeg_eps_i(eps0omeg, eps_cf_i);
// -ω ϵ₀ ϵᵣ
ScalarMatrixProductCoefficient negeps0omeg_eps_r(negeps0omeg, eps_cf_r);
// -ω ϵ₀ ϵᵢ
ScalarMatrixProductCoefficient negeps0omeg_eps_i(eps0omeg, eps_cf_i);
// A = [0 1; -1 0]
DenseMatrix rot_mat(2);
rot_mat(0,0) = 0.; rot_mat(0,1) = 1.;
rot_mat(1,0) = -1.; rot_mat(1,1) = 0.;
MatrixConstantCoefficient rot(rot_mat);
TransposeMatrixCoefficient Rt(rot);
// ω ϵ₀ ϵᵣ A
MatrixProductCoefficient eps0omeg_eps_r_rot(eps0omeg_eps_r, rot);
// ω ϵ₀ ϵᵢ A
MatrixProductCoefficient eps0omeg_eps_i_rot(eps0omeg_eps_i, rot);
// ω ϵ₀ Aᵀ ϵᵢ
MatrixProductCoefficient eps0omeg_Rt_eps_i(Rt, eps0omeg_eps_i);
// ω ϵ₀ Aᵀ ϵᵣ
MatrixProductCoefficient eps0omeg_Rt_eps_r(Rt, eps0omeg_eps_r);
// -ω ϵ₀ ϵᵣ A
MatrixProductCoefficient negeps0omeg_eps_r_rot(negeps0omeg_eps_r, rot);
// -ω ϵ₀ ϵᵢ A
MatrixProductCoefficient negeps0omeg_eps_i_rot(negeps0omeg_eps_i, rot);
// (ωϵ₀ϵ)(ωϵ₀ϵ)^* (δH, δH)
TransposeMatrixCoefficient eps0omeg_eps_r_t(eps0omeg_eps_r);
TransposeMatrixCoefficient eps0omeg_eps_i_t(eps0omeg_eps_i);
MatrixProductCoefficient MrMrt_cf(eps0omeg_eps_r, eps0omeg_eps_r_t);
MatrixProductCoefficient MiMit_cf(eps0omeg_eps_i, eps0omeg_eps_i_t);
MatrixProductCoefficient MiMrt_cf(eps0omeg_eps_i, eps0omeg_eps_r_t);
MatrixProductCoefficient MrMit_cf(eps0omeg_eps_r, eps0omeg_eps_i_t);
// (MᵣMᵣᵗ + MᵢMᵢᵗ) + i (MᵢMᵣᵗ - MᵣMᵢᵗ)
MatrixSumCoefficient Mreal_cf(MrMrt_cf,MiMit_cf);
MatrixSumCoefficient Mimag_cf(MiMrt_cf,MrMit_cf,1.0,-1.0);
// if ELD
Array<Vector *> c_arrays(ndiffusionequations);
Array<PWConstCoefficient *> pw_c_coeffs(ndiffusionequations);
Array<MatrixCoefficient *> cPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> cPibb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPibb_cf(ndiffusionequations);
Array<MatrixCoefficient *> balancescaled_signedcPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> balancescaled_signedcPibb_cf(ndiffusionequations);
Vector temp(nattr); temp=0.0;
Array<ConstantCoefficient *> c_coeffs(ndiffusionequations);
for (int i = 0; i<ndiffusionequations; i++)
{
temp[nattr-1] = cvals(i);
// temp = cvals(i);
pw_c_coeffs[i] = new PWConstCoefficient(temp);
c_coeffs[i] = new ConstantCoefficient(cvals(i));
cPrbb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_r);
cPibb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_i);
signedcPrbb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPrbb_cf[i]);
signedcPibb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPibb_cf[i]);
balancescaled_signedcPrbb_cf[i] = new ScalarMatrixProductCoefficient(balance_scale,*signedcPrbb_cf[i]);
balancescaled_signedcPibb_cf[i] = new ScalarMatrixProductCoefficient(balance_scale,*signedcPibb_cf[i]);
}
// Define the spaces
Array<FiniteElementCollection *> trial_fecols;
Array<FiniteElementCollection *> test_fecols;
Array<ParFiniteElementSpace *> pfes;
// Vector L2 space for E
trial_fecols.Append(new L2_FECollection(order-1, dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last(), dim));
// Scalar L2 space for H
trial_fecols.Append(new L2_FECollection(order-1, dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last()));
// Vector H1 spaces for Jᵢ
for (int i = 0; i < ndiffusionequations; i++)
{
trial_fecols.Append(new H1_FECollection(order, dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last(), dim));
}
// Trial trace space for Ê
trial_fecols.Append(new RT_Trace_FECollection(order-1, dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last()));
// Trial trace space for Ĥ
trial_fecols.Append(new H1_Trace_FECollection(order, dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last()));
// Vector Trace spaces Ĵᵢ
for (int i = 0; i < ndiffusionequations; i++)
{
trial_fecols.Append(new RT_Trace_FECollection(order-1,dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last(),dim));
}
Array<HYPRE_BigInt> tdofs(pfes.Size());
for (int i = 0; i < pfes.Size(); ++i)
{
tdofs[i] = pfes[i]->GlobalTrueVSize();
if (Mpi::Root())
{
cout << "ParFiniteElementSpace " << i << " has " << tdofs[i]
<< " true dofs." << endl;
}
}
if (Mpi::Root())
{
cout << "Total number of true dofs: " << tdofs.Sum() << endl;
}
// test spaces for E and H
test_fecols.Append(new H1_FECollection(test_order, dim));
test_fecols.Append(new ND_FECollection(test_order, dim));
// Test spaces δJs
for (int i = 0; i < ndiffusionequations; i++)
{
test_fecols.Append(new H1_FECollection(test_order, dim));
// test_fecols.Append(new H1_FECollection(order, dim));
}
ParComplexDPGWeakForm * a = new ParComplexDPGWeakForm(pfes,test_fecols);
const IntegrationRule &ir_test = IntRules.Get(pmesh.GetElementGeometry(0),
2*test_order + 4);
const IntegrationRule &ir_trial = IntRules.Get(pmesh.GetElementGeometry(0),
order+test_order + 4);
a->SetTestIntegrationRule(ir_test);
a->SetTrialIntegrationRule(ir_trial);
for (int i = 0; i < ndiffusionequations; i++)
{
a->SetTestFECollVdim(i+2,dim);
}
// (E,∇ × δE)
a->AddTrialIntegrator(new TransposeIntegrator(new MixedCurlIntegrator(one_cf)),
nullptr,0, 0);
// -i ω ϵ₀ (ϵE,δH) = - i ω ϵ₀(ϵᵣ + i ϵᵢ E, δH)
// = (ω ϵ₀ ϵᵢ E, δH) + i (-ω ϵ₀ϵᵣ E, δH)
a->AddTrialIntegrator(
new TransposeIntegrator(new VectorFEMassIntegrator(eps0omeg_eps_i)),
new TransposeIntegrator(new VectorFEMassIntegrator(negeps0omeg_eps_r)),
0,1);
// iωμ₀(H,δE)
a->AddTrialIntegrator(nullptr,new MixedScalarMassIntegrator(omegamu_cf),1, 0);
// (H,∇ × δH)
a->AddTrialIntegrator(
new TransposeIntegrator(new MixedCurlIntegrator(one_cf)), nullptr,1, 1);
// Trace integrators
// <Ê,δE>
a->AddTrialIntegrator(new TraceIntegrator,nullptr, 2 + ndiffusionequations, 0);
// <Ĥ,δH × n>
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr, 2 + ndiffusionequations+1, 1);
if (eld)
{
for (int i = 0; i < ndiffusionequations; i++)
{
// ±cᵢ(P(r) (b ⊗ b) E, δJᵢ)
a->AddTrialIntegrator(new VectorMassIntegrator(*balancescaled_signedcPrbb_cf[i]),
new VectorMassIntegrator(*balancescaled_signedcPibb_cf[i]),
0, i+2);
// -ωϵ₀ (Jᵢ ,δH)
a->AddTrialIntegrator(
new TransposeIntegrator(new VectorFEMassIntegrator(balancescaled_negomegeps0_cf)),
// new TransposeIntegrator(new VectorFEMassIntegrator(negomegeps0_cf)),
nullptr,i+2, 1);
// ((b⋅∇)Jᵢ, (b⋅∇) δJᵢ)
a->AddTrialIntegrator(new DirectionalVectorDiffusionIntegrator(scaled_b_cf), nullptr,
// a->AddTrialIntegrator(new VectorDiffusionIntegrator(diff_coeff), nullptr,
i+2, i+2);
// cᵢ(Jᵢ, δJᵢ)
a->AddTrialIntegrator(new VectorMassIntegrator(*pw_c_coeffs[i]), nullptr,
i+2, i+2);
// // <Ĵᵢ,δJᵢ>
a->AddTrialIntegrator(new VectorTraceIntegrator,nullptr,
i + ndiffusionequations + 4, i+2);
}
}
// test integrators
// (∇δE,∇δE)
a->AddTestIntegrator(new DiffusionIntegrator(one_cf),nullptr, 0, 0);
// (δE,δE)
a->AddTestIntegrator(new MassIntegrator(one_cf),nullptr, 0, 0);
// μ₀² ω² (δE,δE)
a->AddTestIntegrator(new MassIntegrator(mu2omeg2_cf),nullptr,0, 0);
// -i ω μ₀ (δE,∇ × δH) = i (δE, -ω μ₀ ∇ × δ H)
a->AddTestIntegrator(nullptr,
new TransposeIntegrator(new MixedCurlIntegrator(negomegamu_cf)),0, 1);
// -i ω ϵ₀ϵ(∇ × δE, δH) = -i (ωϵ₀(ϵᵣ+iϵᵢ) A ∇ δE,δE), A = [0 1; -1 0]
// = (ω ϵ₀ ϵᵢ A ∇ δE,δE) + i (-ω ϵ₀ ϵᵣ A ∇ δE,δE)
a->AddTestIntegrator(new MixedVectorGradientIntegrator(eps0omeg_eps_i_rot),
new MixedVectorGradientIntegrator(negeps0omeg_eps_r_rot),0, 1);
// i ω μ₀ (∇ × δH ,δE) = i (ω μ₀ ∇ × δH, δE )
a->AddTestIntegrator(nullptr,new MixedCurlIntegrator(omegamu_cf),
1, 0);
// i ω ϵ₀ϵ̄ (δH, ∇ × δE ) = i (ω ϵ₀(ϵᵣ -i ϵᵢ) δH, A ∇ δE)
// = ( δH, ω ϵ₀ ϵᵢ A ∇ δE) + i (δH, ω ϵ₀ ϵᵣ A ∇ δE)
a->AddTestIntegrator(new TransposeIntegrator(new MixedVectorGradientIntegrator(eps0omeg_Rt_eps_i)),
new TransposeIntegrator(new MixedVectorGradientIntegrator(eps0omeg_Rt_eps_r)),1, 0);
// a->AddTestIntegrator(
// new TransposeIntegrator(new MixedVectorGradientIntegrator(eps0omeg_eps_i_rot)),
// new TransposeIntegrator(new MixedVectorGradientIntegrator(eps0omeg_eps_r_rot)),1, 0);
// (ωϵ₀ϵ)(ωϵ₀ϵ)^* (δH, δH)
// (MᵣMᵣᵗ + MᵢMᵢᵗ) + i (MᵢMᵣᵗ - MᵣMᵢᵗ)
a->AddTestIntegrator(new VectorFEMassIntegrator(Mreal_cf),
new VectorFEMassIntegrator(Mimag_cf),1, 1);
// (∇×δH ,∇×δH)
a->AddTestIntegrator(new CurlCurlIntegrator(one_cf),nullptr,1,1);
// (δH,δH)
a->AddTestIntegrator(new VectorFEMassIntegrator(one_cf),nullptr,1,1);
for (int i = 0; i < ndiffusionequations; i++)
{
// (∇δJ,∇δJ)
// a->AddTestIntegrator(new VectorDiffusionIntegrator(one_cf),nullptr,
// i+2,i+2);
// (b⋅∇δJ, b⋅∇δJ)
a->AddTestIntegrator(new DirectionalVectorDiffusionIntegrator(scaled_b_cf),nullptr,
// a->AddTestIntegrator(new DirectionalVectorDiffusionIntegrator(b_cf),nullptr,
i+2,i+2);
// (δJ,δJ)
// a->AddTestIntegrator(new VectorMassIntegrator(one_cf),nullptr,
a->AddTestIntegrator(new VectorMassIntegrator(*c_coeffs[i]),nullptr,
i+2,i+2);
}
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble(0);
// delta = delta_prec;
// ParComplexDPGWeakForm * a_prec = new ParComplexDPGWeakForm(pfes,test_fecols);
// for (int i = 0; i < ndiffusionequations; i++)
// {
// a_prec->SetTestFECollVdim(i+2,dim);
// }
// // (E,∇ × δE)
// a_prec->AddTrialIntegrator(new TransposeIntegrator(new MixedCurlIntegrator(one_cf)),
// nullptr,0, 0);
// // -i ω ϵ₀ (ϵE,δH) = - i ω ϵ₀(ϵᵣ + i ϵᵢ E, δH)
// // = (ω ϵ₀ ϵᵢ E, δH) + i (-ω ϵ₀ϵᵣ E, δH)
// a_prec->AddTrialIntegrator(
// new TransposeIntegrator(new VectorFEMassIntegrator(eps0omeg_eps_i)),
// new TransposeIntegrator(new VectorFEMassIntegrator(negeps0omeg_eps_r)),
// 0,1);
// // iωμ₀(H,δE)
// a_prec->AddTrialIntegrator(nullptr,new MixedScalarMassIntegrator(omegamu_cf),1, 0);
// // (H,∇ × δH)
// a_prec->AddTrialIntegrator(
// new TransposeIntegrator(new MixedCurlIntegrator(one_cf)), nullptr,1, 1);
// // Trace integrators
// // <Ê,δE>
// a_prec->AddTrialIntegrator(new TraceIntegrator,nullptr, 2 + ndiffusionequations, 0);
// // <Ĥ,δH × n>
// a_prec->AddTrialIntegrator(new TangentTraceIntegrator,nullptr, 2 + ndiffusionequations+1, 1);
// if (eld)
// {
// for (int i = 0; i < ndiffusionequations; i++)
// {
// // ±cᵢ(P(r) (b ⊗ b) E, δJᵢ)
// a_prec->AddTrialIntegrator(new VectorMassIntegrator(*balancescaled_signedcPrbb_cf[i]),
// new VectorMassIntegrator(*balancescaled_signedcPibb_cf[i]), 0, i+2);
// // a_prec->AddTrialIntegrator(
// // new TransposeIntegrator(new VectorFEMassIntegrator(balancescaled_negomegeps0_cf)),
// // nullptr,i+2, 1);
// // ((b⋅∇)Jᵢ, (b⋅∇) δJᵢ)
// a_prec->AddTrialIntegrator(new DirectionalVectorDiffusionIntegrator(scaled_b_cf), nullptr, i+2, i+2);
// // cᵢ(Jᵢ, δJᵢ)
// a_prec->AddTrialIntegrator(new VectorMassIntegrator(*pw_c_coeffs[i]), nullptr, i+2, i+2);
// // <Ĵᵢ,δJᵢ>
// // a_prec->AddTrialIntegrator(new VectorTraceIntegrator,nullptr, i + ndiffusionequations + 4, i+2);
// }
// }
// // test integrators
// // (∇δE,∇δE)
// a_prec->AddTestIntegrator(new DiffusionIntegrator(one_cf),nullptr, 0, 0);
// // (δE,δE)
// a_prec->AddTestIntegrator(new MassIntegrator(one_cf),nullptr, 0, 0);
// // μ₀² ω² (δE,δE)
// a_prec->AddTestIntegrator(new MassIntegrator(mu2omeg2_cf),nullptr,0, 0);
// // -i ω μ₀ (δE,∇ × δH) = i (δE, -ω μ₀ ∇ × δ H)
// a_prec->AddTestIntegrator(nullptr,
// new TransposeIntegrator(new MixedCurlIntegrator(negomegamu_cf)),0, 1);
// // -i ω ϵ₀ϵ(∇ × δE, δH) = -i (ωϵ₀(ϵᵣ+iϵᵢ) A ∇ δE,δE), A = [0 1; -1 0]
// // = (ω ϵ₀ ϵᵢ A ∇ δE,δE) + i (-ω ϵ₀ ϵᵣ A ∇ δE,δE)
// a_prec->AddTestIntegrator(new MixedVectorGradientIntegrator(eps0omeg_eps_i_rot),
// new MixedVectorGradientIntegrator(negeps0omeg_eps_r_rot),0, 1);
// // i ω μ₀ (∇ × δH ,δE) = i (ω μ₀ ∇ × δH, δE )
// a_prec->AddTestIntegrator(nullptr,new MixedCurlIntegrator(omegamu_cf),
// 1, 0);
// // i ω ϵ₀ϵ̄ (δH, ∇ × δE ) = i (ω ϵ₀(ϵᵣ -i ϵᵢ) δH, A ∇ δE)
// // = ( δH, ω ϵ₀ ϵᵢ A ∇ δE) + i (δH, ω ϵ₀ ϵᵣ A ∇ δE)
// a_prec->AddTestIntegrator(
// new TransposeIntegrator(new MixedVectorGradientIntegrator(eps0omeg_eps_i_rot)),
// new TransposeIntegrator(new MixedVectorGradientIntegrator(eps0omeg_eps_r_rot)),1, 0);
// // (ωϵ₀ϵ)(ωϵ₀ϵ)^* (δH, δH)
// // (MᵣMᵣᵗ + MᵢMᵢᵗ) + i (MᵢMᵣᵗ - MᵣMᵢᵗ)
// a_prec->AddTestIntegrator(new VectorFEMassIntegrator(Mreal_cf),
// new VectorFEMassIntegrator(Mimag_cf),1, 1);
// // (∇×δH ,∇×δH)
// a_prec->AddTestIntegrator(new CurlCurlIntegrator(one_cf),nullptr,1,1);
// // (δH,δH)
// a_prec->AddTestIntegrator(new VectorFEMassIntegrator(one_cf),nullptr,1,1);
// for (int i = 0; i < ndiffusionequations; i++)
// {
// // (b⋅∇δJ, b⋅∇δJ)
// a_prec->AddTestIntegrator(new DirectionalVectorDiffusionIntegrator(scaled_b_cf),nullptr,
// i+2,i+2);
// // (δJ,δJ)
// a_prec->AddTestIntegrator(new VectorMassIntegrator(*c_coeffs[i]),nullptr,
// i+2,i+2);
// }
// if (static_cond) { a_prec->EnableStaticCondensation(); }
// a_prec->Assemble();
for (int i = 0; i<ndiffusionequations; i++)
{
delete pw_c_coeffs[i];
delete c_coeffs[i];
delete cPrbb_cf[i];
delete cPibb_cf[i];
delete signedcPrbb_cf[i];
delete signedcPibb_cf[i];
}
socketstream E_out_r;
int npfes = pfes.Size();
Array<int> offsets(npfes+1); offsets[0] = 0;
Array<int> toffsets(npfes+1); toffsets[0] = 0;
for (int i = 0; i<npfes; i++)
{
offsets[i+1] = pfes[i]->GetVSize();
toffsets[i+1] = pfes[i]->TrueVSize();
}
offsets.PartialSum();
toffsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
Array<ParGridFunction *> pgf_r(npfes);
Array<ParGridFunction *> pgf_i(npfes);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i] = new ParGridFunction(pfes[i], x, offsets[i]);
pgf_i[i] = new ParGridFunction(pfes[i], x, offsets.Last() + offsets[i]);
}
L2_FECollection L2fec(order, dim);
ParFiniteElementSpace L2_fes(&pmesh, &L2fec);
ParGridFunction E_par_r(&L2_fes);
ParGridFunction E_par_i(&L2_fes);
ParaViewDataCollection * paraview_dc = nullptr;
std::string output_dir = "ParaView/UW/" + GetTimestamp();
if (paraview)
{
if (Mpi::Root()) { WriteParametersToFile(args, output_dir); }
std::ostringstream paraview_file_name;
std::string filename = GetFilename(mesh_file);
paraview_file_name << filename
<< "_par_ref_" << par_ref_levels
<< "_order_" << order
<< "_eld_" << eld;
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
paraview_dc->SetPrefixPath(output_dir);
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",pgf_r[0]);
paraview_dc->RegisterField("E_i",pgf_i[0]);
paraview_dc->RegisterField("E_par_r",&E_par_r);
paraview_dc->RegisterField("E_par_i",&E_par_i);
paraview_dc->RegisterField("H_r",pgf_r[1]);
paraview_dc->RegisterField("H_i",pgf_i[1]);
if (eld)
{
paraview_dc->RegisterField("Jh_1_r",pgf_r[2]);
paraview_dc->RegisterField("Jh_1_i",pgf_i[2]);
paraview_dc->RegisterField("Jh_2_r",pgf_r[3]);
paraview_dc->RegisterField("Jh_2_i",pgf_i[3]);
}
}
Array<int> ess_tdof_list;
Array<int> ess_tdof_listJ;
Array<int> ess_tdof_listJhat;
Array<int> ess_bdr;
Array<int> one_r_bdr;
Array<int> one_i_bdr;
Array<int> negone_r_bdr;
Array<int> negone_i_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_r_bdr.SetSize(pmesh.bdr_attributes.Max());
one_i_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_r_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_i_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
// remove internal boundaries
for (int i = 0; i<int_bdr_attr.Size(); i++)
{
ess_bdr[int_bdr_attr[i]-1] = 0;
}
pfes[2+ndiffusionequations]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
for (int j = 0; j < ess_tdof_list.Size(); j++)
{
ess_tdof_list[j] += toffsets[2+ndiffusionequations];
}
// ess_bdr=1;
for (int i = 0; i<ndiffusionequations;i++)
{
ess_tdof_listJ.SetSize(0);
ess_tdof_listJhat.SetSize(0);
pfes[i+2]->GetEssentialTrueDofs(ess_bdr, ess_tdof_listJ);
// pfes[i+6]->GetEssentialTrueDofs(ess_bdr, ess_tdof_listJhat);
for (int j = 0; j < ess_tdof_listJ.Size(); j++)
{
ess_tdof_listJ[j] += toffsets[i+2];
}
// for (int j = 0; j < ess_tdof_listJhat.Size(); j++)
// {
// ess_tdof_listJhat[j] += toffsets[i+6];
// }
ess_tdof_list.Append(ess_tdof_listJ);
// ess_tdof_list.Append(ess_tdof_listJhat);
}
one_r_bdr = 0; one_i_bdr = 0;
negone_r_bdr = 0; negone_i_bdr = 0;
// attr = 30,2 (real)
one_r_bdr[30-1] = 1; one_r_bdr[2-1] = 1;
// attr = 26,6 (imag)
one_i_bdr[26-1] = 1; one_i_bdr[6-1] = 1;
// attr = 22,10 (real)
negone_r_bdr[22-1] = 1; negone_r_bdr[10-1] = 1;
// attr = 18,14 (imag)
negone_i_bdr[18-1] = 1; negone_i_bdr[14-1] = 1;
}
// rotate the vector
// (x,y) -> (y,-x)
Vector rot_one_x(dim); rot_one_x = 0.0; rot_one_x(1) = -1.0;
Vector rot_negone_x(dim); rot_negone_x = 0.0; rot_negone_x(1) = 1.0;
VectorConstantCoefficient rot_one_x_cf(rot_one_x);
VectorConstantCoefficient rot_negone_x_cf(rot_negone_x);
pgf_r[2+ndiffusionequations]->ProjectBdrCoefficientNormal(rot_one_x_cf, one_r_bdr);
pgf_r[2+ndiffusionequations]->ProjectBdrCoefficientNormal(rot_negone_x_cf, negone_r_bdr);
pgf_i[2+ndiffusionequations]->ProjectBdrCoefficientNormal(rot_one_x_cf, one_i_bdr);
pgf_i[2+ndiffusionequations]->ProjectBdrCoefficientNormal(rot_negone_x_cf, negone_i_bdr);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
Array<ParFiniteElementSpace *> prec_fes;
if (static_cond)
{
a->GetTraceFESpaces(prec_fes);
}
else
{
prec_fes = pfes;
}
Solver * cprec = nullptr;
if (pmg)
{
#ifdef MFEM_USE_COMPLEX_MUMPS
bool mumps_coarse_solver = true;
#else
bool mumps_coarse_solver = false;
#endif
std::vector<Array<int>> ess_bdr_marker(prec_fes.Size());
for (int b = 0; b<prec_fes.Size(); b++)
{
if (pmesh.bdr_attributes.Size())
{
ess_bdr_marker[b].SetSize(pmesh.bdr_attributes.Max());
int ess_block_hatE = (static_cond) ? ndiffusionequations : ndiffusionequations + 2;
int ess_block_J1 = (static_cond) ? 0 : 2;
int ess_block_J2 = (static_cond) ? 1 : 3;
if (b == ess_block_hatE || b == ess_block_J1 || b == ess_block_J2) // hatE, J1, J2
{
ess_bdr_marker[b] = ess_bdr;
}
else
{
ess_bdr_marker[b] = 0;
}
}
}
cprec = new ComplexPRefinementMultigrid(prec_fes, ess_bdr_marker, *Ahc,
pmg_levels, relax_factor, mumps_coarse_solver);
}
else
{
BlockDiagonalPreconditioner * real_prec = new BlockDiagonalPreconditioner(BlockA_r->RowOffsets());
real_prec->owns_blocks = 1;
for (int i = 0; i<BlockA_r->NumRowBlocks(); i++)
{
auto prec = MakeFESpaceDefaultSolver(prec_fes[i],0);
prec->SetOperator(BlockA_r->GetBlock(i,i));
real_prec->SetDiagonalBlock(i,prec);
}
cprec = new ComplexPreconditioner(real_prec, true);
}
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-10);
cg.SetMaxIter(500);
cg.SetPrintLevel(1);
cg.SetOperator(*Ahc);
cg.SetPreconditioner(*cprec);
cg.Mult(B, X);
a->RecoverFEMSolution(X, x);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i]->MakeRef(pfes[i], x, offsets[i]);
pgf_i[i]->MakeRef(pfes[i], x, offsets.Last() + offsets[i]);
}
ParallelECoefficient par_e_r(pgf_r[0]);
ParallelECoefficient par_e_i(pgf_i[0]);
E_par_r.ProjectCoefficient(par_e_r);
E_par_i.ProjectCoefficient(par_e_i);
// rescale the J solutions
for (int i = 0; i < ndiffusionequations; ++i)
{
(*pgf_r[2+i]) /= balance_scale;
(*pgf_i[2+i]) /= balance_scale;
}
if (visualization)
{
const char * keys = nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, *pgf_r[0],
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(0);
paraview_dc->SetTime((real_t)0);
paraview_dc->Save();
delete paraview_dc;
}
delete a;
for (int i = 0; i < trial_fecols.Size(); ++i)
{
delete trial_fecols[i];
delete pfes[i];
}
for (int i = 0; i< test_fecols.Size(); ++i)
{
delete test_fecols[i];
}
return 0;
}
+522
View File
@@ -0,0 +1,522 @@
// MFEM FEM parallel example
//
// Electron Landau Damping
// ∇×(1/μ₀∇×E) - ω² ϵ₀ ϵᵣ E + i ω²ϵ₀(J₁ + J₂) = 0, in Ω
// - Δ∥ J₁ + c₁ J₁ - c₁ P(r) E∥ = 0, in Ω
// - Δ∥ J₂ + c₂ J₂ + c₂ P(r) E∥ = 0, in Ω
// Strong formulation:
// ∇×(1/μ₀∇×E) - ω² ϵ₀ ϵᵣ E + 1/2 i ω²ϵ₀(J₁ + J₂) - c₁ P(r)(b ⊗ b) J₃ + c₂ P(r)(b ⊗ b) J₄ = 0, in Ω
// - Δ∥ J₁ + c₁ J₁ - c₁ P(r) (b ⊗ b) E = 0, in Ω
// - Δ∥ J₂ + c₂ J₂ + c₂ P(r) (b ⊗ b) E = 0, in Ω
// - Δ∥ J₃ + c₁ J₃ + 1/2 i ω² ϵ₀ E = 0, in Ω
// - Δ∥ J₄ + c₂ J₄ + 1/2 i ω² ϵ₀ E = 0, in Ω
//
// E×n = E₀, on ∂Ω
// J₁ = 0, on ∂Ω
// J₂ = 0, on ∂Ω
// weak formulation:
// Find E ∈ H(curl,Ω), J₁ ∈ H¹(Ω), J₂ ∈ H¹(Ω), J₃ ∈ H¹(Ω), J₄ ∈ H¹(Ω) such that
// (1/μ₀ ∇×E, ∇ × F) - ω² ϵ₀ (ϵᵣ E, F) + i ω²ϵ₀(J₁ + J₂, F) - (c₁ P(r)(b ⊗ b) J₃, F) + (c₂ P(r)(b ⊗ b) J₄, F) = 0, ∀ F ∈ H(curl,Ω)
// ( (b⋅∇)J₁ , (b⋅∇) G) + c₁ (J₁ , G) - c₁ (P(r) (b ⊗ b) E, G) = 0, ∀ G ∈ (H¹(Ω))ᵈ
// ( (b⋅∇)J₂ , (b⋅∇) H) + c₂ (J₂ , H) + c₂ (P(r) (b ⊗ b) E, H) = 0, ∀ H ∈ (H¹(Ω))ᵈ
// ( (b⋅∇)J₃ , (b⋅∇) I) + c₁ (J₃ , I) - i ω² ϵ₀ (E, I) = 0, ∀ I ∈ (H¹(Ω))ᵈ
// ( (b⋅∇)J₄ , (b⋅∇) K) + c₂ (J₄ , K) - i ω² ϵ₀ (E, K) = 0, ∀ K ∈ (H¹(Ω))ᵈ
// | E | J₁ | J₂ | J₃ | J₄ |
// -----------------------------------------------------------------------------------------------------------
// δE |(1/μ₀∇×E,∇×δE)-ω²ϵ₀(ϵᵣE,δE)| i ω²ϵ₀(J₁,δE) | i ω²ϵ₀(J₂,δE) |-(c₁P(r)(b⊗b)J₃,δE)|c₂P(r)(b⊗b)J₄,δE)|
// δJ₁ | | ((b⋅∇)J₁,(b⋅∇)δJ₁)+c₁(J₁,δJ₁) | | | | |
// δJ₂ | | |((b⋅∇)J₂,(b⋅∇)δJ₂)+ c₂(J₂,δJ₂)| | |
// δJ₃ | | | | | |
// δJ₄ | | | | | |
#include "mfem.hpp"
#include "../util/pcomplexweakform.hpp"
#include "../util/pcomplexblockform.hpp"
#include "../util/blockcomplexhypremat.hpp"
#include "../util/utils.hpp"
#include "../util/maxwell_utils.hpp"
#include "utils/lh_utils.hpp"
#include "../../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
#include <cstring>
#include <filesystem>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "data/LH_hot.msh";
int order = 2;
int par_ref_levels = 0;
int ser_ref_levels = 0;
bool visualization = false;
// real_t rnum=4.6e9;
// real_t mu = 1.257e-6;
// real_t eps0 = 8.8541878128e-12*factor;
real_t rnum=1.5e9;
real_t mu = 1.257e-6;
real_t eps0 = 8.8541878128e-12;
bool eld = true; // enable/disable electron Landau damping
bool paraview = false;
bool debug = false;
bool mumps_solver = true;
real_t delta_prec = 0.01;
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(&ser_ref_levels, "-sr", "--serial-refinement_levels",
"Number of serial refinement levels.");
args.AddOption(&par_ref_levels, "-pr", "--parallel-refinement_levels",
"Number of parallel refinement levels.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&a0, "-a0", "--a0", "P(r) first parameter.");
args.AddOption(&a1, "-a1", "--a1", "P(r) second parameter.");
args.AddOption(&delta_prec, "-dp", "--delta-prec", "stability parameter for the preconditioner.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&eld, "-eld", "--eld", "-no-eld",
"--no-eld",
"Enable or disable electron Landau damping.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.AddOption(&mumps_solver, "-mumps", "--mumps", "-no-mumps",
"--no-mumps",
"Enable or disable MUMPS solver.");
args.AddOption(&debug, "-debug", "--debug", "-no-debug",
"--no-debug",
"Enable or disable debug mode (delta = 0.01 and no coupling).");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// number of diffusion equations
int ndiffusionequations = (eld) ? 2 : 0;
Vector cvals(ndiffusionequations);
Vector csigns(ndiffusionequations);
// real_t cfactor = 1e-6;
real_t cfactor = 1.0;
if (eld)
{
cvals(0) = 25e6; cvals(1) = 1e6;
csigns(0) = -1.0; csigns(1) = 1.0;
}
cvals *= cfactor; // scale the coefficients
real_t omega = 2.*M_PI*rnum;
if (eld && !debug)
{
delta = 0.0; // disable delta if electron Landau damping is enabled
if (Mpi::Root())
{
cout << "Electron Landau damping enabled, delta set to 0.0." << endl;
}
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
for (int i = 0; i < ser_ref_levels; i++)
{
mesh.UniformRefinement();
}
mesh.RemoveInternalBoundaries();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
int nattr = (pmesh.attributes.Size()) ? pmesh.attributes.Max() : 0;
Array<int> attr(nattr);
for (int i = 0; i<nattr; i++) { attr[i] = i+1; }
for (int i = 0; i<par_ref_levels; i++)
{
pmesh.UniformRefinement();
}
// Define the coefficients
ConstantCoefficient muinv(1./mu);
Vector zero(dim); zero = 0.0;
Vector one_x(dim); one_x = 0.0; one_x(0) = 1.0;
Vector negone_x(dim); negone_x = 0.0; negone_x(0) = -1.0;
VectorConstantCoefficient zero_vcf(zero);
VectorConstantCoefficient one_x_cf(one_x);
VectorConstantCoefficient negone_x_cf(negone_x);
DenseMatrix Mone(dim);
Mone = 0.0; Mone(0,0) = Mone(1,1) = 1.0;
MatrixConstantCoefficient Mone_cf(Mone);
DenseMatrix Mzero(dim); Mzero = 0.0;
MatrixConstantCoefficient Mzero_cf(Mzero);
Array<MatrixCoefficient*> coefs_r(nattr);
Array<MatrixCoefficient*> coefs_i(nattr);
for (int i = 0; i < nattr-1; ++i)
{
coefs_r[i] = &Mone_cf;
coefs_i[i] = &Mzero_cf;
}
// S(r)
FunctionCoefficient S_cf_r(sfunc_r), S_cf_i(sfunc_i);
// P(r)
FunctionCoefficient P_cf_r(pfunc_r), P_cf_i(pfunc_i);
VectorFunctionCoefficient b_cf(dim,bfunc);// b
ScalarVectorProductCoefficient scaledb_cf(sqrt(cfactor), b_cf);
MatrixFunctionCoefficient bb_cf(dim,bcrossb); // b⊗b
MatrixSumCoefficient oneminusbb(Mone_cf, bb_cf, 1.0, -1.0); // 1 - b⊗b
// S(r) (I - b⊗b)
ScalarMatrixProductCoefficient Soneminusbb_r(S_cf_r, oneminusbb), Soneminusbb_i(S_cf_i, oneminusbb);
// P(r) b⊗b
ScalarMatrixProductCoefficient P_cf_bb_r(P_cf_r, bb_cf), P_cf_bb_i(P_cf_i, bb_cf);
// εᵣ = S(r) (I - b⊗b) + P(r) b⊗b
MatrixSumCoefficient eps_r(Soneminusbb_r, P_cf_bb_r, 1.0, 1.0);
MatrixSumCoefficient eps_i(Soneminusbb_i, P_cf_bb_i, 1.0, 1.0);
coefs_r[nattr-1] = &eps_r;
coefs_i[nattr-1] = &eps_i;
PWMatrixCoefficient eps_cf_r(dim, attr, coefs_r);
PWMatrixCoefficient eps_cf_i(dim, attr, coefs_i);
real_t scale = (debug) ? 0.0 : 1.0;
ConstantCoefficient eps0omeg2(eps0 * omega * omega * scale);
ConstantCoefficient negeps0omeg2(-eps0 * omega * omega);
ScalarMatrixProductCoefficient m_cf_r(negeps0omeg2, eps_cf_r);
ScalarMatrixProductCoefficient m_cf_i(negeps0omeg2, eps_cf_i);
// if ELD
Array<Vector *> c_arrays(ndiffusionequations);
Array<PWConstCoefficient *> pw_c_coeffs(ndiffusionequations);
Array<MatrixCoefficient *> cPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> cPibb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPibb_cf(ndiffusionequations);
Vector temp(nattr); temp=0.0;
for (int i = 0; i<ndiffusionequations; i++)
{
temp[nattr-1] = cvals(i);
pw_c_coeffs[i] = new PWConstCoefficient(temp);
cPrbb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_r);
cPibb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_i);
signedcPrbb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPrbb_cf[i]);
signedcPibb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPibb_cf[i]);
}
Array<FiniteElementCollection *> fecols;
Array<ParFiniteElementSpace *> pfes;
fecols.Append(new ND_FECollection(order, dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, fecols[0]));
if (eld)
{
for (int i = 0; i < 2*ndiffusionequations; ++i)
{
fecols.Append(new H1_FECollection(order, dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, fecols[i+1], dim));
}
}
Array<HYPRE_BigInt> tdofs(pfes.Size());
for (int i = 0; i < pfes.Size(); ++i)
{
tdofs[i] = pfes[i]->GlobalTrueVSize();
if (Mpi::Root())
{
cout << "ParFiniteElementSpace " << i << " has " << tdofs[i]
<< " true dofs." << endl;
}
}
if (Mpi::Root())
{
cout << "Total number of true dofs: " << tdofs.Sum() << endl;
}
ParComplexBlockForm *a = new ParComplexBlockForm(pfes);
// (1/μ₀ ∇×E, ∇ × F)
a->AddDomainIntegrator(new CurlCurlIntegrator(muinv), nullptr, 0, 0);
// - ω² ϵ₀ (ϵᵣ E, F)
a->AddDomainIntegrator(new VectorFEMassIntegrator(m_cf_r),
new VectorFEMassIntegrator(m_cf_i), 0, 0);
ConstantCoefficient halfeps0omeg2(0.5*eps0 * omega * omega);
ConstantCoefficient neghalfeps0omeg2(-0.5*eps0 * omega * omega);
if (eld)
{
for (int i = 0; i<ndiffusionequations; i++)
{
// 1/2 i ω²ϵ₀((J₁+J₂),F)
a->AddDomainIntegrator(nullptr, new TransposeIntegrator(new VectorFEMassIntegrator(halfeps0omeg2)), i+1, 0);
// ( (b⋅∇)J₁ , (b⋅∇) G)
a->AddDomainIntegrator(new DirectionalVectorDiffusionIntegrator(scaledb_cf), nullptr, i+1, i+1);
// cᵢ (J₁ , G)
a->AddDomainIntegrator(new VectorMassIntegrator(*pw_c_coeffs[i]), nullptr, i+1, i+1);
// ±cᵢ(P(r) (b ⊗ b) E, G)
a->AddDomainIntegrator(new VectorFEMassIntegrator(*signedcPrbb_cf[i]),
new VectorFEMassIntegrator(*signedcPibb_cf[i]), 0, i+1);
//
// ±(cᵢ P(r)(b ⊗ b) Jᵢ₊₂,F)
a->AddDomainIntegrator(new TransposeIntegrator(
new VectorFEMassIntegrator(*signedcPrbb_cf[i])),
new TransposeIntegrator(
new VectorFEMassIntegrator(*signedcPibb_cf[i])), i+3, 0);
// ( (b⋅∇)J₃ , (b⋅∇) I) + c₁ (J₃ , I) - i ω² ϵ₀ (E, I) = 0, ∀ I ∈ (H¹(Ω))ᵈ
// ( (b⋅∇)J₄ , (b⋅∇) K) + c₂ (J₄ , K) - i ω² ϵ₀ (E, K) = 0, ∀ K ∈ (H¹(Ω))ᵈ
a->AddDomainIntegrator(new DirectionalVectorDiffusionIntegrator(scaledb_cf), nullptr, i+3, i+3);
a->AddDomainIntegrator(new VectorMassIntegrator(*pw_c_coeffs[i]), nullptr, i+3, i+3);
// + i ω² ϵ₀ (E, I)
a->AddDomainIntegrator(nullptr, new VectorFEMassIntegrator(halfeps0omeg2), 0, i+3);
}
}
a->Assemble();
for (int i = 0; i<ndiffusionequations; i++)
{
delete pw_c_coeffs[i];
delete cPrbb_cf[i];
delete cPibb_cf[i];
delete signedcPrbb_cf[i];
delete signedcPibb_cf[i];
}
socketstream E_out_r;
int npfes = pfes.Size();
Array<int> offsets(npfes+1); offsets[0] = 0;
Array<int> toffsets(npfes+1); toffsets[0] = 0;
for (int i = 0; i<npfes; i++)
{
offsets[i+1] = pfes[i]->GetVSize();
toffsets[i+1] = pfes[i]->TrueVSize();
}
offsets.PartialSum();
toffsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
Array<ParGridFunction *> pgf_r(npfes);
Array<ParGridFunction *> pgf_i(npfes);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i] = new ParGridFunction(pfes[i], x, offsets[i]);
pgf_i[i] = new ParGridFunction(pfes[i], x, offsets.Last() + offsets[i]);
}
ParComplexGridFunction maxwell_pgf(pfes[0]); maxwell_pgf = 0.0;
L2_FECollection L2fec(order, dim);
ParFiniteElementSpace L2_fes(&pmesh, &L2fec);
ParGridFunction E_par_r(&L2_fes);
ParGridFunction E_par_i(&L2_fes);
ParaViewDataCollection * paraview_dc = nullptr;
std::string output_dir = "ParaView/FEM/" + GetTimestamp();
if (paraview)
{
if (Mpi::Root()) { WriteParametersToFile(args, output_dir); }
std::ostringstream paraview_file_name;
std::string filename = GetFilename(mesh_file);
paraview_file_name << filename
<< "_par_ref_" << par_ref_levels
<< "_order_" << order;
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
paraview_dc->SetPrefixPath(output_dir);
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",pgf_r[0]);
paraview_dc->RegisterField("E_i",pgf_i[0]);
paraview_dc->RegisterField("MaxwellE_r",&maxwell_pgf.real());
paraview_dc->RegisterField("MaxwellE_i",&maxwell_pgf.imag());
paraview_dc->RegisterField("E_par_r",&E_par_r);
paraview_dc->RegisterField("E_par_i",&E_par_i);
if (eld)
{
paraview_dc->RegisterField("Jh_1_r",pgf_r[1]);
paraview_dc->RegisterField("Jh_1_i",pgf_i[1]);
paraview_dc->RegisterField("Jh_2_r",pgf_r[2]);
paraview_dc->RegisterField("Jh_2_i",pgf_i[2]);
paraview_dc->RegisterField("Jh_3_r",pgf_r[3]);
paraview_dc->RegisterField("Jh_3_i",pgf_i[3]);
paraview_dc->RegisterField("Jh_4_r",pgf_r[4]);
paraview_dc->RegisterField("Jh_4_i",pgf_i[4]);
}
}
Array<int> ess_tdof_list;
Array<int> ess_tdof_listJ;
Array<int> ess_bdr;
Array<int> one_r_bdr;
Array<int> one_i_bdr;
Array<int> negone_r_bdr;
Array<int> negone_i_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_r_bdr.SetSize(pmesh.bdr_attributes.Max());
one_i_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_r_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_i_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
pfes[0]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
for (int i = 0; i<2*ndiffusionequations;i++)
{
ess_tdof_listJ.SetSize(0);
pfes[i+1]->GetEssentialTrueDofs(ess_bdr, ess_tdof_listJ);
for (int j = 0; j < ess_tdof_listJ.Size(); j++)
{
ess_tdof_listJ[j] += toffsets[i+1];
}
ess_tdof_list.Append(ess_tdof_listJ);
}
one_r_bdr = 0; one_i_bdr = 0;
negone_r_bdr = 0; negone_i_bdr = 0;
// attr = 30,2 (real)
one_r_bdr[30-1] = 1; one_r_bdr[2-1] = 1;
// attr = 26,6 (imag)
one_i_bdr[26-1] = 1; one_i_bdr[6-1] = 1;
// attr = 22,10 (real)
negone_r_bdr[22-1] = 1; negone_r_bdr[10-1] = 1;
// attr = 18,14 (imag)
negone_i_bdr[18-1] = 1; negone_i_bdr[14-1] = 1;
}
pgf_r[0]->ProjectBdrCoefficientTangent(one_x_cf, one_r_bdr);
pgf_r[0]->ProjectBdrCoefficientTangent(negone_x_cf, negone_r_bdr);
pgf_i[0]->ProjectBdrCoefficientTangent(one_x_cf, one_i_bdr);
pgf_i[0]->ProjectBdrCoefficientTangent(negone_x_cf, negone_i_bdr);
OperatorPtr Ah;
Vector B, X;
Vector b(x.Size()); b = 0.0;
a->FormLinearSystem(ess_tdof_list, x, b, Ah, X, B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * Block_r = dynamic_cast<BlockOperator *>(&Ahc->real());
BlockOperator * Block_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
int nblocks = Block_r->NumRowBlocks();
Array2D<const HypreParMatrix*> r_matrices(nblocks, nblocks);
Array2D<const HypreParMatrix*> i_matrices(nblocks, nblocks);
for (int i = 0; i < nblocks; i++)
{
for (int j = 0; j < nblocks; j++)
{
r_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&Block_r->GetBlock(i,j));
i_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&Block_i->GetBlock(i,j));
}
}
HypreParMatrix * hr = HypreParMatrixFromBlocks(r_matrices);
HypreParMatrix * hi = HypreParMatrixFromBlocks(i_matrices);
ComplexHypreParMatrix * hc_hypre =
new ComplexHypreParMatrix(hr, hi,false, false);
auto P = new ComplexMUMPSSolver(MPI_COMM_WORLD);
P->SetPrintLevel(1);
P->SetOperator(*hc_hypre);
P->Mult(B, X);
a->RecoverFEMSolution(X, x);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i]->MakeRef(pfes[i], x, offsets[i]);
pgf_i[i]->MakeRef(pfes[i], x, offsets.Last() + offsets[i]);
}
ParallelECoefficient par_e_r(pgf_r[0]);
ParallelECoefficient par_e_i(pgf_i[0]);
E_par_r.ProjectCoefficient(par_e_r);
E_par_i.ProjectCoefficient(par_e_i);
if (visualization)
{
const char * keys = nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, *pgf_r[0],
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(0);
paraview_dc->SetTime((real_t)0);
paraview_dc->Save();
delete paraview_dc;
}
delete a;
for (int i = 0; i < fecols.Size(); ++i)
{
delete fecols[i];
delete pfes[i];
delete pgf_r[i];
delete pgf_i[i];
}
return 0;
}
+705
View File
@@ -0,0 +1,705 @@
// MFEM FEM parallel example
//
// Electron Landau Damping
// Strong formulation:
// ∇×(1/μ₀∇×E) - ω² ϵ₀ ϵᵣ E + i ω²ϵ₀(J₁ + J₂) = 0, in Ω
// - Δ∥ J₁ + c₁ J₁ - c₁ P(r) E∥ = 0, in Ω
// - Δ∥ J₂ + c₂ J₂ + c₂ P(r) E∥ = 0, in Ω
// E×n = E₀, on ∂Ω
// J₁ = 0, on ∂Ω
// J₂ = 0, on ∂Ω
// weak formulation:
// Find E ∈ H(curl,Ω), J₁ ∈ H¹(Ω), J₂ ∈ H¹(Ω) such that
// (1/μ₀ ∇×E, ∇ × F) - ω² ϵ₀ (ϵᵣ E, F) + i ω²ϵ₀(J₁ + J₂, F) = 0, ∀ F ∈ H(curl,Ω)
// ( (b⋅∇)J₁ , (b⋅∇) G) + c₁ (J₁ , G) - c₁ (P(r) (b ⊗ b) E, G) = 0, ∀ G ∈ (H¹(Ω))ᵈ
// ( (b⋅∇)J₂ , (b⋅∇) H) + c₂ (J₂ , H) + c₂ (P(r) (b ⊗ b) E, H) = 0, ∀ H ∈ (H¹(Ω))ᵈ
// mpirun -np 8 ./lh-eld-fem -o 4 -paraview -eld -m data/quad.msh
#include "mfem.hpp"
#include "../util/pcomplexweakform.hpp"
#include "../util/pcomplexblockform.hpp"
#include "../util/blockcomplexhypremat.hpp"
#include "../util/utils.hpp"
#include "../util/maxwell_utils.hpp"
#include "utils/lh_utils.hpp"
#include "../../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
#include <cstring>
#include <filesystem>
#ifndef MFEM_USE_COMPLEX_MUMPS
MFEM_ABORT("This example requires MFEM to be built with ComplexMUMPS.");
#endif
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "data/LH_hot.msh";
int order = 2;
int par_ref_levels = 0;
int ser_ref_levels = 0;
bool visualization = false;
// real_t rnum=4.6e9;
// real_t mu = 1.257e-6;
// real_t eps0 = 8.8541878128e-12*factor;
real_t rnum=1.5e9;
real_t mu = 1.257e-6;
real_t eps0 = 8.8541878128e-12;
bool eld = true; // enable/disable electron Landau damping
bool paraview = false;
bool debug = false;
bool monolithic_precond = true;
bool direct_solve = false;
bool triangular_precond = false;
bool use_amg = false;
real_t delta_prec = 0.01;
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(&ser_ref_levels, "-sr", "--serial-refinement_levels",
"Number of serial refinement levels.");
args.AddOption(&par_ref_levels, "-pr", "--parallel-refinement_levels",
"Number of parallel refinement levels.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&a0, "-a0", "--a0", "P(r) first parameter.");
args.AddOption(&a1, "-a1", "--a1", "P(r) second parameter.");
args.AddOption(&delta_prec, "-dp", "--delta-prec", "stability parameter for the preconditioner.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&eld, "-eld", "--eld", "-no-eld",
"--no-eld",
"Enable or disable electron Landau damping.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.AddOption(&direct_solve, "-direct", "--direct", "-no-direct",
"--no-direct",
"Enable or disable direct monolithic solver.");
args.AddOption(&monolithic_precond, "-monolithic", "--monolithic", "-no-monolithic",
"--no-monolithic",
"Enable or disable monolithic preconditioner.");
args.AddOption(&triangular_precond, "-triangular", "--triangular", "-no-triangular",
"--no-triangular",
"Enable or disable lower triangular preconditioner.");
args.AddOption(&use_amg, "-amg", "--amg", "-no-amg",
"--no-amg",
"Enable or disable AMG.");
args.AddOption(&debug, "-debug", "--debug", "-no-debug",
"--no-debug",
"Enable or disable debug mode (delta = 0.01 and no coupling).");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// number of diffusion equations
int ndiffusionequations = (eld) ? 2 : 0;
Vector cvals(ndiffusionequations);
Vector csigns(ndiffusionequations);
real_t cfactor = 1e-6;
if (eld)
{
cvals(0) = 25e6; cvals(1) = 1e6;
csigns(0) = -1.0; csigns(1) = 1.0;
}
cvals *= cfactor; // scale the coefficients
real_t omega = 2.*M_PI*rnum;
if (eld && !debug)
{
delta = 0.0; // disable delta if electron Landau damping is enabled
if (Mpi::Root())
{
cout << "Electron Landau damping enabled, delta set to 0.0." << endl;
}
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
for (int i = 0; i < ser_ref_levels; i++)
{
mesh.UniformRefinement();
}
mesh.RemoveInternalBoundaries();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
int nattr = (pmesh.attributes.Size()) ? pmesh.attributes.Max() : 0;
Array<int> attr(nattr);
for (int i = 0; i<nattr; i++) { attr[i] = i+1; }
for (int i = 0; i<par_ref_levels; i++)
{
pmesh.UniformRefinement();
}
// Define the coefficients
ConstantCoefficient muinv(1./mu);
Vector zero(dim); zero = 0.0;
Vector one_x(dim); one_x = 0.0; one_x(0) = 1.0;
Vector negone_x(dim); negone_x = 0.0; negone_x(0) = -1.0;
VectorConstantCoefficient zero_vcf(zero);
VectorConstantCoefficient one_x_cf(one_x);
VectorConstantCoefficient negone_x_cf(negone_x);
DenseMatrix Mone(dim);
Mone = 0.0; Mone(0,0) = Mone(1,1) = 1.0;
MatrixConstantCoefficient Mone_cf(Mone);
DenseMatrix Mzero(dim); Mzero = 0.0;
MatrixConstantCoefficient Mzero_cf(Mzero);
Array<MatrixCoefficient*> coefs_r(nattr);
Array<MatrixCoefficient*> coefs_i(nattr);
for (int i = 0; i < nattr-1; ++i)
{
coefs_r[i] = &Mone_cf;
coefs_i[i] = &Mzero_cf;
}
// S(r)
FunctionCoefficient S_cf_r(sfunc_r), S_cf_i(sfunc_i);
// P(r)
FunctionCoefficient P_cf_r(pfunc_r), P_cf_i(pfunc_i);
VectorFunctionCoefficient b_cf(dim,bfunc);// b
ScalarVectorProductCoefficient scaledb_cf(sqrt(cfactor), b_cf);
MatrixFunctionCoefficient bb_cf(dim,bcrossb); // b⊗b
MatrixSumCoefficient oneminusbb(Mone_cf, bb_cf, 1.0, -1.0); // 1 - b⊗b
// S(r) (I - b⊗b)
ScalarMatrixProductCoefficient Soneminusbb_r(S_cf_r, oneminusbb), Soneminusbb_i(S_cf_i, oneminusbb);
// P(r) b⊗b
ScalarMatrixProductCoefficient P_cf_bb_r(P_cf_r, bb_cf), P_cf_bb_i(P_cf_i, bb_cf);
// εᵣ = S(r) (I - b⊗b) + P(r) b⊗b
MatrixSumCoefficient eps_r(Soneminusbb_r, P_cf_bb_r, 1.0, 1.0);
MatrixSumCoefficient eps_i(Soneminusbb_i, P_cf_bb_i, 1.0, 1.0);
coefs_r[nattr-1] = &eps_r;
coefs_i[nattr-1] = &eps_i;
PWMatrixCoefficient eps_cf_r(dim, attr, coefs_r);
PWMatrixCoefficient eps_cf_i(dim, attr, coefs_i);
real_t scale = (debug) ? 0.0 : 1.0;
ConstantCoefficient eps0omeg2(eps0 * omega * omega * scale);
ConstantCoefficient negeps0omeg2(-eps0 * omega * omega);
ScalarMatrixProductCoefficient m_cf_r(negeps0omeg2, eps_cf_r);
ScalarMatrixProductCoefficient m_cf_i(negeps0omeg2, eps_cf_i);
// if ELD
Array<Vector *> c_arrays(ndiffusionequations);
Array<PWConstCoefficient *> pw_c_coeffs(ndiffusionequations);
Array<MatrixCoefficient *> cPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> cPibb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPibb_cf(ndiffusionequations);
Vector temp(nattr); temp=0.0;
for (int i = 0; i<ndiffusionequations; i++)
{
temp[nattr-1] = cvals(i);
pw_c_coeffs[i] = new PWConstCoefficient(temp);
cPrbb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_r);
cPibb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_i);
signedcPrbb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPrbb_cf[i]);
signedcPibb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPibb_cf[i]);
}
Array<FiniteElementCollection *> fecols;
Array<ParFiniteElementSpace *> pfes;
fecols.Append(new ND_FECollection(order, dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, fecols[0]));
if (eld)
{
for (int i = 0; i < ndiffusionequations; ++i)
{
fecols.Append(new H1_FECollection(order, dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, fecols[i+1], dim));
}
}
Array<HYPRE_BigInt> tdofs(pfes.Size());
for (int i = 0; i < pfes.Size(); ++i)
{
tdofs[i] = pfes[i]->GlobalTrueVSize();
if (Mpi::Root())
{
cout << "ParFiniteElementSpace " << i << " has " << tdofs[i]
<< " true dofs." << endl;
}
}
if (Mpi::Root())
{
cout << "Total number of true dofs: " << tdofs.Sum() << endl;
}
ParComplexBlockForm *a = new ParComplexBlockForm(pfes);
// (1/μ₀ ∇×E, ∇ × F)
a->AddDomainIntegrator(new CurlCurlIntegrator(muinv), nullptr, 0, 0);
// - ω² ϵ₀ (ϵᵣ E, F)
a->AddDomainIntegrator(new VectorFEMassIntegrator(m_cf_r),
new VectorFEMassIntegrator(m_cf_i), 0, 0);
if (eld)
{
for (int i = 0; i<ndiffusionequations; i++)
{
// i ω²ϵ₀((J₁+J₂),F)
a->AddDomainIntegrator(nullptr, new TransposeIntegrator(new VectorFEMassIntegrator(eps0omeg2)), i+1, 0);
// ( (b⋅∇)J₁ , (b⋅∇) G)
a->AddDomainIntegrator(new DirectionalVectorDiffusionIntegrator(scaledb_cf), nullptr, i+1, i+1);
// cᵢ (J₁ , G)
a->AddDomainIntegrator(new VectorMassIntegrator(*pw_c_coeffs[i]), nullptr, i+1, i+1);
// ±cᵢ(P(r) (b ⊗ b) E, G)
a->AddDomainIntegrator(new VectorFEMassIntegrator(*signedcPrbb_cf[i]), new VectorFEMassIntegrator(*signedcPibb_cf[i]), 0, i+1);
}
}
a->Assemble();
socketstream E_out_r;
int npfes = pfes.Size();
Array<int> offsets(npfes+1); offsets[0] = 0;
Array<int> toffsets(npfes+1); toffsets[0] = 0;
for (int i = 0; i<npfes; i++)
{
offsets[i+1] = pfes[i]->GetVSize();
toffsets[i+1] = pfes[i]->TrueVSize();
}
offsets.PartialSum();
toffsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
Array<ParGridFunction *> pgf_r(npfes);
Array<ParGridFunction *> pgf_i(npfes);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i] = new ParGridFunction(pfes[i], x, offsets[i]);
pgf_i[i] = new ParGridFunction(pfes[i], x, offsets.Last() + offsets[i]);
}
ParComplexGridFunction maxwell_pgf(pfes[0]); maxwell_pgf = 0.0;
L2_FECollection L2fec(order, dim);
ParFiniteElementSpace L2_fes(&pmesh, &L2fec);
ParGridFunction E_par_r(&L2_fes);
ParGridFunction E_par_i(&L2_fes);
ParaViewDataCollection * paraview_dc = nullptr;
std::string output_dir = "ParaView/FEM/" + GetTimestamp();
if (paraview)
{
if (Mpi::Root()) { WriteParametersToFile(args, output_dir); }
std::ostringstream paraview_file_name;
std::string filename = GetFilename(mesh_file);
paraview_file_name << filename
<< "_par_ref_" << par_ref_levels
<< "_order_" << order;
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
paraview_dc->SetPrefixPath(output_dir);
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",pgf_r[0]);
paraview_dc->RegisterField("E_i",pgf_i[0]);
paraview_dc->RegisterField("MaxwellE_r",&maxwell_pgf.real());
paraview_dc->RegisterField("MaxwellE_i",&maxwell_pgf.imag());
paraview_dc->RegisterField("E_par_r",&E_par_r);
paraview_dc->RegisterField("E_par_i",&E_par_i);
if (eld)
{
paraview_dc->RegisterField("Jh_1_r",pgf_r[1]);
paraview_dc->RegisterField("Jh_1_i",pgf_i[1]);
paraview_dc->RegisterField("Jh_2_r",pgf_r[2]);
paraview_dc->RegisterField("Jh_2_i",pgf_i[2]);
}
}
Array<int> ess_tdof_list;
Array<int> ess_tdof_listJ;
Array<int> ess_bdr;
Array<int> one_r_bdr;
Array<int> one_i_bdr;
Array<int> negone_r_bdr;
Array<int> negone_i_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_r_bdr.SetSize(pmesh.bdr_attributes.Max());
one_i_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_r_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_i_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
pfes[0]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
for (int i = 0; i<ndiffusionequations;i++)
{
ess_tdof_listJ.SetSize(0);
pfes[i+1]->GetEssentialTrueDofs(ess_bdr, ess_tdof_listJ);
for (int j = 0; j < ess_tdof_listJ.Size(); j++)
{
ess_tdof_listJ[j] += toffsets[i+1];
}
ess_tdof_list.Append(ess_tdof_listJ);
}
one_r_bdr = 0; one_i_bdr = 0;
negone_r_bdr = 0; negone_i_bdr = 0;
// attr = 30,2 (real)
one_r_bdr[30-1] = 1; one_r_bdr[2-1] = 1;
// attr = 26,6 (imag)
one_i_bdr[26-1] = 1; one_i_bdr[6-1] = 1;
// attr = 22,10 (real)
negone_r_bdr[22-1] = 1; negone_r_bdr[10-1] = 1;
// attr = 18,14 (imag)
negone_i_bdr[18-1] = 1; negone_i_bdr[14-1] = 1;
}
pgf_r[0]->ProjectBdrCoefficientTangent(one_x_cf, one_r_bdr);
pgf_r[0]->ProjectBdrCoefficientTangent(negone_x_cf, negone_r_bdr);
pgf_i[0]->ProjectBdrCoefficientTangent(one_x_cf, one_i_bdr);
pgf_i[0]->ProjectBdrCoefficientTangent(negone_x_cf, negone_i_bdr);
OperatorPtr Ah;
Vector B, X;
Vector b(x.Size()); b = 0.0;
a->FormLinearSystem(ess_tdof_list, x, b, Ah, X, B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
if (direct_solve)
{
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
int nblocks = BlockA_r->NumRowBlocks();
Array2D<const HypreParMatrix*> A_r_matrices(nblocks, nblocks);
Array2D<const HypreParMatrix*> A_i_matrices(nblocks, nblocks);
for (int i = 0; i < nblocks; i++)
{
for (int j = 0; j < nblocks; j++)
{
A_r_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&BlockA_r->GetBlock(i,j));
A_i_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&BlockA_i->GetBlock(i,j));
}
}
HypreParMatrix * Ahr = HypreParMatrixFromBlocks(A_r_matrices);
HypreParMatrix * Ahi = HypreParMatrixFromBlocks(A_i_matrices);
ComplexHypreParMatrix * Ahc_hypre =
new ComplexHypreParMatrix(Ahr, Ahi,true, true);
ComplexMUMPSSolver cmumps(MPI_COMM_WORLD);
cmumps.SetPrintLevel(0);
cmumps.SetOperator(*Ahc_hypre);
cmumps.Mult(B,X);
delete Ahc_hypre;
}
else
{
// Set up the preconditioner
delta = delta_prec;
ParComplexBlockForm a_prec(pfes);
// (1/μ₀ ∇×E, ∇ × F)
a_prec.AddDomainIntegrator(new CurlCurlIntegrator(muinv), nullptr, 0, 0);
// - ω² ϵ₀ (ϵᵣ E, F)
a_prec.AddDomainIntegrator(new VectorFEMassIntegrator(m_cf_r),
new VectorFEMassIntegrator(m_cf_i), 0, 0);
if (eld)
{
for (int i = 0; i<ndiffusionequations; i++)
{
// ( (b⋅∇)J₁ , (b⋅∇) G)
a_prec.AddDomainIntegrator(new DirectionalVectorDiffusionIntegrator(scaledb_cf), nullptr, i+1, i+1);
// cᵢ (J₁ , G)
a_prec.AddDomainIntegrator(new VectorMassIntegrator(*pw_c_coeffs[i]), nullptr, i+1, i+1);
// ±cᵢ(P(r) (b ⊗ b) E, G)
a_prec.AddDomainIntegrator(new VectorFEMassIntegrator(*signedcPrbb_cf[i]), new VectorFEMassIntegrator(*signedcPibb_cf[i]), 0, i+1);
}
}
a_prec.Assemble();
OperatorPtr Ahprec;
a_prec.FormSystemMatrix(ess_tdof_list, Ahprec);
ComplexOperator * Ahcprec = Ahprec.As<ComplexOperator>();
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetRelTol(1e-10);
gmres.SetMaxIter(2000);
gmres.SetPrintLevel(1);
Array<int> blk(3);
if (monolithic_precond)
{
BlockOperator * BlockPrec_r = dynamic_cast<BlockOperator *>(&Ahcprec->real());
BlockOperator * BlockPrec_i = dynamic_cast<BlockOperator *>(&Ahcprec->imag());
int nblocks = BlockPrec_r->NumRowBlocks();
Array2D<const HypreParMatrix*> prec_r_matrices(nblocks, nblocks);
Array2D<const HypreParMatrix*> prec_i_matrices(nblocks, nblocks);
for (int i = 0; i < nblocks; i++)
{
for (int j = 0; j < nblocks; j++)
{
prec_r_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&BlockPrec_r->GetBlock(i,j));
prec_i_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&BlockPrec_i->GetBlock(i,j));
}
}
HypreParMatrix * Prechr = HypreParMatrixFromBlocks(prec_r_matrices);
HypreParMatrix * Prechi = HypreParMatrixFromBlocks(prec_i_matrices);
ComplexHypreParMatrix * Prechc_hypre =
new ComplexHypreParMatrix(Prechr, Prechi,true, true);
ComplexMUMPSSolver cmumps(MPI_COMM_WORLD);
cmumps.SetPrintLevel(0);
cmumps.SetOperator(*Prechc_hypre);
gmres.SetOperator(*Ahc);
gmres.SetPreconditioner(cmumps);
gmres.Mult(B, X);
delete Prechc_hypre;
}
else
{
ComplexBlockOperator Ac(*Ahc);
ComplexBlockOperator Acprec(*Ahcprec);
Vector Xc(X.Size()); Xc = 0.0;
Vector Bc(B.Size());
Ac.BlockComplexToComplexBlock(B, Bc);
Solver * Mc = nullptr;
int nblocks = Ac.NumRowBlocks();
Array<Solver *> diag_solvers(nblocks);
diag_solvers[0] = new ComplexMUMPSSolver(MPI_COMM_WORLD);
dynamic_cast<ComplexMUMPSSolver*>(diag_solvers[0])->SetPrintLevel(0);
diag_solvers[0]->SetOperator(Acprec.GetBlock(0,0));
for (int i = 1; i < nblocks; ++i)
{
if (use_amg)
{
HypreBoomerAMG * amg1 = new HypreBoomerAMG();
HypreBoomerAMG * amg2 = new HypreBoomerAMG();
amg1->SetPrintLevel(0); amg2->SetPrintLevel(0);
amg1->SetSystemsOptions(dim); amg2->SetSystemsOptions(dim);
amg1->SetRelaxType(88); amg2->SetRelaxType(88);
auto op = dynamic_cast<ComplexHypreParMatrix*>(&Acprec.GetBlock(i,i));
blk[0]=0;
blk[1]=op->real().Height();
blk[2]=op->real().Height();
blk.PartialSum();
BlockDiagonalPreconditioner * bprec = new BlockDiagonalPreconditioner(blk);
bprec->owns_blocks=1;
amg1->SetOperator(op->real());
amg2->SetOperator(op->real());
bprec->SetDiagonalBlock(0, amg1);
bprec->SetDiagonalBlock(1, amg2);
diag_solvers[i] = bprec;
}
else
{
diag_solvers[i] = new ComplexMUMPSSolver(MPI_COMM_WORLD);
dynamic_cast<ComplexMUMPSSolver*>(diag_solvers[i])->SetPrintLevel(0);
diag_solvers[i]->SetOperator(Acprec.GetBlock(i,i));
}
}
if (triangular_precond)
{
Mc = new BlockLowerTriangularPreconditioner(Ac.RowOffsets());
auto Mclt = dynamic_cast<BlockLowerTriangularPreconditioner*>(Mc);
Mclt->SetBlock(1,0, &Acprec.GetBlock(1,0));
Mclt->SetBlock(2,0, &Acprec.GetBlock(2,0));
for (int i = 0; i < nblocks; ++i) { Mclt->SetDiagonalBlock(i, diag_solvers[i]); }
}
else
{
Mc = new BlockDiagonalPreconditioner(Ac.RowOffsets());
auto Mcdiag = dynamic_cast<BlockDiagonalPreconditioner*>(Mc);
for (int i = 0; i < nblocks; ++i) { Mcdiag->SetDiagonalBlock(i, diag_solvers[i]); }
}
gmres.SetPreconditioner(*Mc);
gmres.SetOperator(Ac);
gmres.Mult(Bc, Xc);
for (int i = 0; i < nblocks; ++i)
{
delete diag_solvers[i];
}
delete Mc;
Ac.ComplexBlockToBlockComplex(Xc, X);
}
}
for (int i = 0; i<ndiffusionequations; i++)
{
delete pw_c_coeffs[i];
delete cPrbb_cf[i];
delete cPibb_cf[i];
delete signedcPrbb_cf[i];
delete signedcPibb_cf[i];
}
a->RecoverFEMSolution(X, x);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i]->MakeRef(pfes[i], x, offsets[i]);
pgf_i[i]->MakeRef(pfes[i], x, offsets.Last() + offsets[i]);
}
ParallelECoefficient par_e_r(pgf_r[0]);
ParallelECoefficient par_e_i(pgf_i[0]);
E_par_r.ProjectCoefficient(par_e_r);
E_par_i.ProjectCoefficient(par_e_i);
if (visualization)
{
const char * keys = nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, *pgf_r[0],
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
}
delta = 0.0;
ParSesquilinearForm a_maxwell(pfes[0]);
// (1/μ₀ ∇×E, ∇ × F)
a_maxwell.AddDomainIntegrator(new CurlCurlIntegrator(muinv), nullptr);
// - ω² ϵ₀ (ϵᵣ E, F)
a_maxwell.AddDomainIntegrator(new VectorFEMassIntegrator(m_cf_r),
new VectorFEMassIntegrator(m_cf_i));
a_maxwell.Assemble();
VectorGridFunctionCoefficient J1_cf_r(pgf_r[1]);
VectorGridFunctionCoefficient J1_cf_i(pgf_i[1]);
VectorGridFunctionCoefficient J2_cf_r(pgf_r[2]);
VectorGridFunctionCoefficient J2_cf_i(pgf_i[2]);
// -iω²ϵ₀ (Jᵣ + i Jᵢ ,F) = ω² ϵ₀ (Jᵢ - i Jᵣ,F)
// = (ω² ϵ₀ Jᵢ, F) + i (-ω² ϵ₀Jᵢ,F)
ScalarVectorProductCoefficient omeg2_eps0_J1_cf_i(eps0*omega*omega, J1_cf_i);
ScalarVectorProductCoefficient omeg2_eps0_J2_cf_i(eps0*omega*omega, J2_cf_i);
ScalarVectorProductCoefficient negomeg2_eps0_J1_cf_r(-eps0*omega*omega, J1_cf_r);
ScalarVectorProductCoefficient negomeg2_eps0_J2_cf_r(-eps0*omega*omega, J2_cf_r);
ParComplexLinearForm b_maxwell(pfes[0]);
b_maxwell.AddDomainIntegrator(new VectorFEDomainLFIntegrator(omeg2_eps0_J1_cf_i),
new VectorFEDomainLFIntegrator(negomeg2_eps0_J1_cf_r));
b_maxwell.AddDomainIntegrator(new VectorFEDomainLFIntegrator(omeg2_eps0_J2_cf_i),
new VectorFEDomainLFIntegrator(negomeg2_eps0_J2_cf_r));
b_maxwell.Assemble();
// remove internal boundaries
ess_bdr = 1;
// for (int i = 0; i<int_bdr_attr.Size(); i++)
// {
// ess_bdr[int_bdr_attr[i]-1] = 0;
// }
pfes[0]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
maxwell_pgf.ProjectBdrCoefficientTangent(one_x_cf, zero_vcf, one_r_bdr);
maxwell_pgf.ProjectBdrCoefficientTangent(negone_x_cf, zero_vcf, negone_r_bdr);
maxwell_pgf.ProjectBdrCoefficientTangent(zero_vcf, one_x_cf, one_i_bdr);
maxwell_pgf.ProjectBdrCoefficientTangent(zero_vcf, negone_x_cf, negone_i_bdr);
OperatorPtr maxwell_Ah;
Vector maxwell_X,maxwell_B;
a_maxwell.FormLinearSystem(ess_tdof_list,maxwell_pgf,b_maxwell,maxwell_Ah, maxwell_X,maxwell_B);
ComplexHypreParMatrix *Maxwell_A = maxwell_Ah.As<ComplexHypreParMatrix>();
ComplexMUMPSSolver cmumps(MPI_COMM_WORLD);
cmumps.SetPrintLevel(0);
cmumps.SetOperator(*Maxwell_A);
cmumps.Mult(maxwell_B, maxwell_X);
a_maxwell.RecoverFEMSolution(maxwell_X, maxwell_B, maxwell_pgf);
if (paraview)
{
paraview_dc->SetCycle(0);
paraview_dc->SetTime((real_t)0);
paraview_dc->Save();
delete paraview_dc;
}
delete a;
for (int i = 0; i < fecols.Size(); ++i)
{
delete fecols[i];
delete pfes[i];
delete pgf_r[i];
delete pgf_i[i];
}
return 0;
}
+669
View File
@@ -0,0 +1,669 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
//
// MFEM Ultraweak DPG Maxwell parallel example
//
// Compile with: make lh-eld-fosls-dpg
//
// mpirun -np 8 ./lh-eld-fosls-dpg -o 4 -paraview -eld -m data/quad.msh -ebs -sc
// Electron Landau Damping
// Strong formulation:
// ∇×(1/μ₀∇×E) - ω² ϵ₀ ϵ E + i ω²ϵ₀(J₁ + J₂) = 0, in Ω
// λ₁ (- Δ∥ J₁ + c₁ J₁ - c₁ P(r) E∥) = 0, in Ω
// λ₂ (- Δ∥ J₂ + c₂ J₂ + c₂ P(r) E∥) = 0, in Ω
// E×n = E₀, on ∂Ω
// J₁ = 0, on ∂Ω
// J₂ = 0, on ∂Ω
// The DPG-FOSLS deals with the First Order System
// i ω μ₀ H + ∇ × E = 0, in Ω
// -i ω ϵ₀ϵ E + ∇ × H - ω ϵ₀ (J₁ + J₂) = 0, in Ω
// - λ₁ b⋅∇Q₁ + λ₁ c₁ J₁ - λ₁ c₁ P B E = 0, in Ω
// λ₁ Q₁ + λ₁ b⋅∇ J₁ = 0, in Ω
// - λ₂ b⋅∇Q₂ + λ₂ c₂ J₂ + λ₂ c₂ P B E = 0, in Ω
// λ₂ Q₂ + λ₂ b⋅∇ J₂ = 0, in Ω
// E×n = E₀, on ∂Ω
// J₁ = 0, on ∂Ω
// J₂ = 0, on ∂Ω
// in 2D E is vector valued and H is scalar and
// ∇ × E = ∇ ⋅ AE where A = [0 1; -1 0];
// E ∈ H(curl,Ω) , H ∈ H¹(Ω), Jᵢ, Qᵢ ∈ (H¹(Ω))²
// ( iωμ₀ H, F ) + ( ∇ × E, F) = 0, ∀ F ∈ L²(Ω)
// -(iωϵ₀ϵ E, R ) + ( ∇ × H, R) - ω ϵ₀ (J₁ + J₂, R) = 0, ∀ R ∈ (L²(Ω))²
// λ₁ (b ⋅ ∇Q₁, K₁) + λ₁ c₁(J₁, K₁) - λ₁ c₁ (P B E, K₁) = 0, ∀ K₁ ∈ (L²(Ω))²
// λ₁ (Q₁, L₁) + λ₁ (b ⋅ ∇ J₁, L₁) = 0, ∀ L₁ ∈ (L²(Ω))²
// λ₂ (b ⋅ ∇Q₂, K₂) + λ₂ c₂(J₂, K₂) + λ₂ c₂ (P B E, K₂) = 0, ∀ K₂ ∈ (L²(Ω))²
// λ₂ (Q₂, L₂) + λ₂ (b ⋅ ∇ J₂, L₂) = 0, ∀ L₂ ∈ (L²(Ω))²
// E = E₀, on ∂Ω
// J₁ = J₂ = 0, on ∂Ω
// ----------------------------------------------------------------------------------------------------------------
// | | E | H | J₁ | Q₁ | J₂ | Q₂ | RHS |
// ----------------------------------------------------------------------------------------------------------------
// | F | (∇ × E, F) | (iωμ₀H,F) | | | | | | | 0 |
// | | | | | | | | | | |
// | R | -iωϵ₀ϵ(E,R) |(∇ × H, R) | -ωϵ₀(J₁,R) | | -ωϵ₀(J₂, R)| | | | 0 |
// | | | | | | | | | | |
// |K₁ | -λ₁c₁(PBE,K₁)| | λ₁c₁(J₁, K₁)|(λ₁b⋅∇Q₁,K₁)| | | | | 0 |
// | | | | | | | | | | |
// |L₁ | | | λ₁(b⋅∇J₁,L₁)| λ₁(Q₁, L₁) | | | | | 0 |
// | | | | | | | | | | |
// |K₂ | λ₂c₂(PBE, K₂)| | | |λ₂c₂(J₂, K₂)|λ₂(b⋅∇Q₂,K₂)| | | 0 |
// | | | | | | | | | | |
// |L₂ | | | | |λ₂(b⋅∇J₂,L₂)| λ₂(Q₂, L₂) | | | 0 |
// | | | | | | | | | | |
// where (F,R,K₁,L₁,K₂,L₂) ∈ L²(Ω) × (L²(Ω))² × (L²(Ω))² × (L²(Ω))² × (L²(Ω))² × (L²(Ω))²
#include "mfem.hpp"
#include "../util/pcomplexweakform.hpp"
#include "../../common/mfem-common.hpp"
#include "../util/maxwell_utils.hpp"
#include "utils/lh_utils.hpp"
#include "../util/utils.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
using namespace mfem::common;
int main(int argc, char *argv[])
{
Mpi::Init();
Hypre::Init();
const char *mesh_file = "data/LH_hot.msh";
int order = 1;
int delta_order = 1;
int par_ref_levels = 0;
int ser_ref_levels = 0;
// real_t rnum=1.5e9;
// real_t mu = 1.257e-6;
// real_t eps0 = 8.8541878128e-12;
real_t rnum=1.5;
real_t mu = 1.257;
real_t eps0 = 8.8541878128;
real_t balance_scale = 1.0;
bool enable_balance_scale = false;
bool eld = false; // enable/disable electron Landau damping
real_t delta_prec = 0.0;
real_t lambda1 = 1.0;
real_t lambda2 = 1.0;
bool static_cond = false;
bool visualization = false;
bool paraview = false;
bool debug = false;
bool mumps_solver = false;
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(&delta_order, "-do", "--delta-order",
"Finite element order for the test space");
args.AddOption(&ser_ref_levels, "-sr", "--serial-refinement_levels",
"Number of serial refinement levels.");
args.AddOption(&par_ref_levels, "-pr", "--parallel-refinement_levels",
"Number of parallel refinement levels.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&a0, "-a0", "--a0", "P(r) first parameter.");
args.AddOption(&a1, "-a1", "--a1", "P(r) second parameter.");
args.AddOption(&lambda1, "-l1", "--lambda1", "Lambda 1 scaling parameter.");
args.AddOption(&lambda2, "-l2", "--lambda2", "Lambda 2 scaling parameter.");
args.AddOption(&delta_prec, "-dp", "--delta-prec", "stability parameter for the preconditioner.");
args.AddOption(&eld, "-eld", "--eld", "-no-eld",
"--no-eld",
"Enable or disable electron Landau damping.");
args.AddOption(&mumps_solver, "-mumps", "--mumps", "-no-mumps",
"--no-mumps",
"Enable or disable MUMPS solver.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&debug, "-debug", "--debug", "-no-debug",
"--no-debug",
"Enable or disable debug mode (delta = 0.01 and no coupling).");
args.AddOption(&enable_balance_scale, "-ebs", "--enable-balance-scale", "-no-ebs",
"--no-ebs",
"Enable or disable balance scale.");
args.Parse();
if (!args.Good())
{
if (Mpi::Root())
{
args.PrintUsage(cout);
}
return 1;
}
if (Mpi::Root())
{
args.PrintOptions(cout);
}
// number of diffusion equations
int ndiffusionequations = (eld) ? 2 : 0;
Vector cvals(ndiffusionequations);
Vector csigns(ndiffusionequations);
if (eld)
{
cvals(0) = 25e6; cvals(1) = 1e6;
csigns(0) = -1.0; csigns(1) = 1.0;
}
cvals(0) *= lambda1;
cvals(1) *= lambda2;
real_t omega = 2.*M_PI*rnum;
int test_order = order+delta_order;
balance_scale = (enable_balance_scale) ? eps0 * omega * omega : 1.0;
if (eld && !debug)
{
delta = 0.0; // disable delta if electron Landau damping is enabled
if (Mpi::Root())
{
cout << "Electron Landau damping enabled, delta set to 0.0." << endl;
}
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
MFEM_VERIFY(dim == 2, "Dimension != 2 is not supported in this example");
for (int i = 0; i < ser_ref_levels; i++)
{
mesh.UniformRefinement();
}
Array<int> int_bdr_attr;
for (int i = 0; i < mesh.GetNBE(); i++)
{
if (mesh.FaceIsInterior(mesh.GetBdrElementFaceIndex(i)))
{
int_bdr_attr.Append(mesh.GetBdrAttribute(i));
}
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
for (int i = 0; i < par_ref_levels; i++)
{
pmesh.UniformRefinement();
}
int nattr = (pmesh.attributes.Size()) ? pmesh.attributes.Max() : 0;
Array<int> attr(nattr);
for (int i = 0; i<nattr; i++) { attr[i] = i+1; }
real_t scale = (debug) ? 0.0 : 1.0;
// Define coefficients
ConstantCoefficient one_cf(1.0);
// ωμ₀
ConstantCoefficient omegamu_cf(omega*mu);
// -ωϵ₀
ConstantCoefficient negomegeps0_cf(-omega*eps0 * scale);
ConstantCoefficient balancescaled_negomegeps0_cf( -omega*eps0/balance_scale * scale);
Vector zero(dim); zero = 0.0;
Vector one_x(dim); one_x = 0.0; one_x(0) = 1.0;
Vector negone_x(dim); negone_x = 0.0; negone_x(0) = -1.0;
VectorConstantCoefficient zero_vcf(zero);
VectorConstantCoefficient one_x_cf(one_x);
VectorConstantCoefficient negone_x_cf(negone_x);
DenseMatrix Mone(dim);
Mone = 0.0; Mone(0,0) = Mone(1,1) = 1.0;
MatrixConstantCoefficient Mone_cf(Mone);
DenseMatrix Mzero(dim); Mzero = 0.0;
MatrixConstantCoefficient Mzero_cf(Mzero);
Array<MatrixCoefficient*> coefs_r(nattr);
Array<MatrixCoefficient*> coefs_i(nattr);
for (int i = 0; i < nattr-1; ++i)
{
coefs_r[i] = &Mone_cf;
coefs_i[i] = &Mzero_cf;
}
// S(r)
FunctionCoefficient S_cf_r(sfunc_r), S_cf_i(sfunc_i);
// P(r)
FunctionCoefficient P_cf_r(pfunc_r), P_cf_i(pfunc_i);
// b
VectorFunctionCoefficient b_cf(dim,bfunc);
ScalarVectorProductCoefficient scaled1_b_cf(sqrt(lambda1), b_cf);
ScalarVectorProductCoefficient scaled2_b_cf(sqrt(lambda2), b_cf);
// b⊗b
MatrixFunctionCoefficient bb_cf(dim,bcrossb);
// I - b⊗b
MatrixSumCoefficient oneminusbb(Mone_cf, bb_cf, 1.0, -1.0);
// S(r) (I - b⊗b)
ScalarMatrixProductCoefficient Soneminusbb_r(S_cf_r, oneminusbb), Soneminusbb_i(S_cf_i, oneminusbb);
// P(r) b⊗b
ScalarMatrixProductCoefficient P_cf_bb_r(P_cf_r, bb_cf), P_cf_bb_i(P_cf_i, bb_cf);
// ε = S(r) (I - b⊗b) + P(r) b⊗b
MatrixSumCoefficient eps_r(Soneminusbb_r, P_cf_bb_r, 1.0, 1.0);
MatrixSumCoefficient eps_i(Soneminusbb_i, P_cf_bb_i, 1.0, 1.0);
coefs_r[nattr-1] = &eps_r;
coefs_i[nattr-1] = &eps_i;
PWMatrixCoefficient eps_cf_r(dim, attr, coefs_r);
PWMatrixCoefficient eps_cf_i(dim, attr, coefs_i);
ConstantCoefficient eps0omeg(omega * eps0);
ConstantCoefficient negeps0omeg(-omega * eps0);
// - iωϵ₀ϵ = ωϵ₀ϵᵢ + i (-ωϵ₀ϵᵣ)
// ω ϵ₀ ϵᵢ
ScalarMatrixProductCoefficient eps0omeg_eps_i(eps0omeg, eps_cf_i);
// -ω ϵ₀ ϵᵣ
ScalarMatrixProductCoefficient negeps0omeg_eps_r(negeps0omeg, eps_cf_r);
// if ELD
Array<Vector *> c_arrays(ndiffusionequations);
Array<PWConstCoefficient *> pw_c_coeffs(ndiffusionequations);
Array<MatrixCoefficient *> cPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> cPibb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPibb_cf(ndiffusionequations);
Array<MatrixCoefficient *> balancescaled_signedcPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> balancescaled_signedcPibb_cf(ndiffusionequations);
Vector temp(nattr); temp=0.0;
Array<ConstantCoefficient *> c_coeffs(ndiffusionequations);
for (int i = 0; i<ndiffusionequations; i++)
{
temp[nattr-1] = cvals(i);
// temp = cvals(i);
pw_c_coeffs[i] = new PWConstCoefficient(temp);
c_coeffs[i] = new ConstantCoefficient(cvals(i));
cPrbb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_r);
cPibb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_i);
signedcPrbb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPrbb_cf[i]);
signedcPibb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPibb_cf[i]);
balancescaled_signedcPrbb_cf[i] = new ScalarMatrixProductCoefficient(balance_scale,*signedcPrbb_cf[i]);
balancescaled_signedcPibb_cf[i] = new ScalarMatrixProductCoefficient(balance_scale,*signedcPibb_cf[i]);
}
std::vector<std::string> variables = {" E ", " H ", " J₁ ", " J₂ ", " Q₁ ", " Q₂ "};
std::vector<std::string> spaces = {"H(curl,Ω) ", " H¹(Ω) ",
" (H¹(Ω))ᵈ", " (H¹(Ω))ᵈ",
" (H¹(Ω))ᵈ", " (H¹(Ω))ᵈ"};
// Define the spaces
Array<FiniteElementCollection *> trial_fecols;
Array<FiniteElementCollection *> test_fecols;
Array<ParFiniteElementSpace *> pfes;
// H(curl) space for E
trial_fecols.Append(new ND_FECollection(order, dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last()));
// Scalar H¹ space for H
trial_fecols.Append(new H1_FECollection(order, dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last()));
// Vector H¹ spaces for Jᵢ and Qᵢ
for (int i = 0; i < 2*ndiffusionequations; i++)
{
trial_fecols.Append(new H1_FECollection(order, dim));
pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last(), dim));
}
Array<HYPRE_BigInt> tdofs(pfes.Size());
for (int i = 0; i < pfes.Size(); ++i)
{
tdofs[i] = pfes[i]->GlobalTrueVSize();
if (Mpi::Root())
{
cout << "ParFiniteElementSpace " << spaces[i] << " for " << variables[i] << " has " << tdofs[i]
<< " true dofs." << endl;
}
}
if (Mpi::Root())
{
cout << "Total number of true dofs: " << tdofs.Sum() << endl;
}
// test spaces for F and G
test_fecols.Append(new L2_FECollection(test_order, dim));
test_fecols.Append(new L2_FECollection(test_order, dim));
// Test spaces Kᵢ and Lᵢ
for (int i = 0; i < 2*ndiffusionequations; i++)
{
test_fecols.Append(new L2_FECollection(test_order, dim));
}
ParComplexDPGWeakForm * a = new ParComplexDPGWeakForm(pfes,test_fecols);
for (int i = 0; i < test_fecols.Size(); i++)
{ // all but the first space are vector valued
if (i > 0) { a->SetTestFECollVdim(i,dim); }
}
// (∇ × E, F)
a->AddTrialIntegrator(new MixedCurlIntegrator(one_cf), nullptr, 0, 0);
// (i ω μ₀ H, F)
a->AddTrialIntegrator(nullptr, new MixedScalarMassIntegrator(omegamu_cf), 1, 0);
// -i ω ϵ₀ϵ (E, R) = ω ϵ₀ ϵᵢ (E, G) + i (-ω ϵ₀ ϵᵣ E, R)
a->AddTrialIntegrator(new VectorFEMassIntegrator(eps0omeg_eps_i),
new VectorFEMassIntegrator(negeps0omeg_eps_r), 0, 1);
// (∇ × H, R)
a->AddTrialIntegrator(new MixedCurlIntegrator(one_cf), nullptr, 1,1);
if (eld)
{
// - ω ϵ₀ (J₁, R)
a->AddTrialIntegrator(new VectorMassIntegrator(balancescaled_negomegeps0_cf), nullptr, 2, 1);
// - ω ϵ₀ (J₂, R)
a->AddTrialIntegrator(new VectorMassIntegrator(balancescaled_negomegeps0_cf), nullptr, 3, 1);
// -c₁(PBE, K₁)
a->AddTrialIntegrator(new VectorFEMassIntegrator(*balancescaled_signedcPrbb_cf[0]),
new VectorFEMassIntegrator(*balancescaled_signedcPibb_cf[0]), 0, 2);
// c₁(J₁, K₁)
a->AddTrialIntegrator(new VectorMassIntegrator(*pw_c_coeffs[0]), nullptr, 2, 2);
// (b⋅∇Q₁, K₁)
a->AddTrialIntegrator(new DirectionalVectorGradientIntegrator(scaled1_b_cf), nullptr, 3, 2);
// (b⋅∇J₁, L₁)
a->AddTrialIntegrator(new DirectionalVectorDiffusionIntegrator(scaled1_b_cf), nullptr, 2, 3);
// (Q₁, L₁)
a->AddTrialIntegrator(new VectorMassIntegrator(one_cf), nullptr, 3, 3);
// c₂(PBE, K₂)
a->AddTrialIntegrator(new VectorFEMassIntegrator(*balancescaled_signedcPrbb_cf[1]),
new VectorFEMassIntegrator(*balancescaled_signedcPibb_cf[1]), 0, 4);
// c₂(J₂, K₂)
a->AddTrialIntegrator(new VectorMassIntegrator(*pw_c_coeffs[1]), nullptr, 4, 4);
// (b⋅∇Q₂, K₂)
a->AddTrialIntegrator(new DirectionalVectorGradientIntegrator(scaled2_b_cf), nullptr, 5, 4);
// (b⋅∇J₂, L₂)
a->AddTrialIntegrator(new DirectionalVectorDiffusionIntegrator(scaled2_b_cf), nullptr, 4, 5);
// (Q₂, L₂)
a->AddTrialIntegrator(new VectorMassIntegrator(one_cf), nullptr, 5, 5);
}
// test integrators for test norm: ||v||² = ||F||² + ||R||² + ||K₁||² + ||L₁||² + ||K₂||² + ||L₂||²
// (F,δF), F, δF ∈ L²(Ω)
a->AddTestIntegrator(new MassIntegrator(one_cf),nullptr, 0, 0);
// (R,δR), R, δR ∈ (L²(Ω))²
a->AddTestIntegrator(new VectorMassIntegrator(one_cf),nullptr, 1, 1);
if (eld)
{
// (K₁,δK₁), K₁, δK₁ ∈ (L²(Ω))²
a->AddTestIntegrator(new VectorMassIntegrator(one_cf),nullptr, 2, 2);
// (L₁,δL₁), L₁, δL₁ ∈ (L²(Ω))²
a->AddTestIntegrator(new VectorMassIntegrator(one_cf),nullptr, 3, 3);
// (K₂,δK₂), K₂, δK₂ ∈ (L²(Ω))²
a->AddTestIntegrator(new VectorMassIntegrator(one_cf),nullptr, 4, 4);
// (L₂,δL₂), L₂, δL₂ ∈ (L²(Ω))²
a->AddTestIntegrator(new VectorMassIntegrator(one_cf),nullptr, 5, 5);
}
a->Assemble(0);
for (int i = 0; i<ndiffusionequations; i++)
{
delete pw_c_coeffs[i];
delete c_coeffs[i];
delete cPrbb_cf[i];
delete cPibb_cf[i];
delete signedcPrbb_cf[i];
delete signedcPibb_cf[i];
}
socketstream E_out_r;
int npfes = pfes.Size();
Array<int> offsets(npfes+1); offsets[0] = 0;
Array<int> toffsets(npfes+1); toffsets[0] = 0;
for (int i = 0; i<npfes; i++)
{
offsets[i+1] = pfes[i]->GetVSize();
toffsets[i+1] = pfes[i]->TrueVSize();
}
offsets.PartialSum();
toffsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
Array<ParGridFunction *> pgf_r(npfes);
Array<ParGridFunction *> pgf_i(npfes);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i] = new ParGridFunction(pfes[i], x, offsets[i]);
pgf_i[i] = new ParGridFunction(pfes[i], x, offsets.Last() + offsets[i]);
}
L2_FECollection L2fec(order, dim);
ParFiniteElementSpace L2_fes(&pmesh, &L2fec);
ParGridFunction E_par_r(&L2_fes);
ParGridFunction E_par_i(&L2_fes);
ParaViewDataCollection * paraview_dc = nullptr;
std::string output_dir = "ParaView/FOSLS/" + GetTimestamp();
if (paraview)
{
if (Mpi::Root()) { WriteParametersToFile(args, output_dir); }
std::ostringstream paraview_file_name;
std::string filename = GetFilename(mesh_file);
paraview_file_name << filename
<< "_par_ref_" << par_ref_levels
<< "_order_" << order
<< "_eld_" << eld;
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
paraview_dc->SetPrefixPath(output_dir);
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",pgf_r[0]);
paraview_dc->RegisterField("E_i",pgf_i[0]);
paraview_dc->RegisterField("E_par_r",&E_par_r);
paraview_dc->RegisterField("E_par_i",&E_par_i);
paraview_dc->RegisterField("H_r",pgf_r[1]);
paraview_dc->RegisterField("H_i",pgf_i[1]);
if (eld)
{
paraview_dc->RegisterField("J1_r",pgf_r[2]);
paraview_dc->RegisterField("J1_i",pgf_i[2]);
paraview_dc->RegisterField("Q1_r",pgf_r[3]);
paraview_dc->RegisterField("Q1_i",pgf_i[3]);
paraview_dc->RegisterField("J2_r",pgf_r[4]);
paraview_dc->RegisterField("J2_i",pgf_i[4]);
paraview_dc->RegisterField("Q2_r",pgf_r[5]);
paraview_dc->RegisterField("Q2_i",pgf_i[5]);
}
}
Array<int> ess_tdof_list;
Array<int> ess_tdof_listJ;
Array<int> ess_bdr;
Array<int> one_r_bdr;
Array<int> one_i_bdr;
Array<int> negone_r_bdr;
Array<int> negone_i_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_r_bdr.SetSize(pmesh.bdr_attributes.Max());
one_i_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_r_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_i_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
// remove internal boundaries
for (int i = 0; i<int_bdr_attr.Size(); i++)
{
ess_bdr[int_bdr_attr[i]-1] = 0;
}
pfes[0]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
ess_bdr=1;
for (int i = 0; i<ndiffusionequations;i++)
{
ess_tdof_listJ.SetSize(0);
pfes[2*i+2]->GetEssentialTrueDofs(ess_bdr, ess_tdof_listJ); // J₁ (2), J₂ (4)
for (int j = 0; j < ess_tdof_listJ.Size(); j++)
{
ess_tdof_listJ[j] += toffsets[2*i+2];
}
ess_tdof_list.Append(ess_tdof_listJ);
}
one_r_bdr = 0; one_i_bdr = 0;
negone_r_bdr = 0; negone_i_bdr = 0;
// attr = 30,2 (real)
one_r_bdr[30-1] = 1; one_r_bdr[2-1] = 1;
// attr = 26,6 (imag)
one_i_bdr[26-1] = 1; one_i_bdr[6-1] = 1;
// attr = 22,10 (real)
negone_r_bdr[22-1] = 1; negone_r_bdr[10-1] = 1;
// attr = 18,14 (imag)
negone_i_bdr[18-1] = 1; negone_i_bdr[14-1] = 1;
}
pgf_r[0]->ProjectBdrCoefficientTangent(one_x_cf, one_r_bdr);
pgf_r[0]->ProjectBdrCoefficientTangent(negone_x_cf, negone_r_bdr);
pgf_i[0]->ProjectBdrCoefficientTangent(one_x_cf, one_i_bdr);
pgf_i[0]->ProjectBdrCoefficientTangent(negone_x_cf, negone_i_bdr);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
bool direct_solve = true;
if (direct_solve)
{
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
int nblocks = BlockA_r->NumRowBlocks();
if(Mpi::Root())
{
mfem::out << "Number of blocks: " << nblocks << std::endl;
}
Array2D<const HypreParMatrix*> A_r_matrices(nblocks, nblocks);
Array2D<const HypreParMatrix*> A_i_matrices(nblocks, nblocks);
for (int i = 0; i < nblocks; i++)
{
for (int j = 0; j < nblocks; j++)
{
A_r_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&BlockA_r->GetBlock(i,j));
A_i_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&BlockA_i->GetBlock(i,j));
}
}
HypreParMatrix * Ahr = HypreParMatrixFromBlocks(A_r_matrices);
HypreParMatrix * Ahi = HypreParMatrixFromBlocks(A_i_matrices);
ComplexHypreParMatrix * Ahc_hypre =
new ComplexHypreParMatrix(Ahr, Ahi,true, true);
ComplexMUMPSSolver cmumps(MPI_COMM_WORLD);
cmumps.SetPrintLevel(0);
cmumps.SetOperator(*Ahc_hypre);
cmumps.Mult(B,X);
delete Ahc_hypre;
}
a->RecoverFEMSolution(X, x);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i]->MakeRef(pfes[i], x, offsets[i]);
pgf_i[i]->MakeRef(pfes[i], x, offsets.Last() + offsets[i]);
}
ParallelECoefficient par_e_r(pgf_r[0]);
ParallelECoefficient par_e_i(pgf_i[0]);
E_par_r.ProjectCoefficient(par_e_r);
E_par_i.ProjectCoefficient(par_e_i);
// // rescale the J solutions
for (int i = 0; i < 2*ndiffusionequations; ++i)
{
(*pgf_r[2+i]) /= balance_scale;
(*pgf_i[2+i]) /= balance_scale;
}
if (visualization)
{
const char * keys = nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, *pgf_r[0],
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(0);
paraview_dc->SetTime((real_t)0);
paraview_dc->Save();
delete paraview_dc;
}
delete a;
for (int i = 0; i < trial_fecols.Size(); ++i)
{
delete trial_fecols[i];
delete pfes[i];
}
for (int i = 0; i< test_fecols.Size(); ++i)
{
delete test_fecols[i];
}
return 0;
}
+897
View File
@@ -0,0 +1,897 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
//
// MFEM FOSLS-FEM Maxwell parallel example
//
// Compile with: make lh-eld-fosls-fem
//
// mpirun -np 8 ./lh-eld-fosls-fem -o 4 -paraview -m data/quad.msh -ebs -sc
// Electron Landau Damping
// Strong formulation:
// ∇×(1/μ₀∇×E) - ω² ϵ₀ ϵ E + i ω²ϵ₀(J₁ + J₂) = 0, in Ω
// subject to the constraints
// - Δ∥ J₁ + c₁ J₁ - c₁ P(r) E∥ = 0, in Ω
// - Δ∥ J₂ + c₂ J₂ + c₂ P(r) E∥ = 0, in Ω
// E×n = E₀, on ∂Ω
// J₁ = 0, on ∂Ω
// J₂ = 0, on ∂Ω
// The DPG-FOSLS deals with the First Order System
// i ω μ₀ H + ∇ × E = 0, in Ω
// -i ω ϵ₀ϵ E + ∇ × H - ω ϵ₀ (J₁ + J₂) = 0, in Ω
// subject to the constraints
// - Δ∥ J₁ + c₁ J₁ - c₁ P(r) E∥ = 0, in Ω
// - Δ∥ J₂ + c₂ J₂ + c₂ P(r) E∥ = 0, in Ω
// E×n = E₀, on ∂Ω
// J₁ = 0, on ∂Ω
// J₂ = 0, on ∂Ω
// in 2D E is vector valued and H is scalar and
// ∇ × E = ∇ ⋅ AE where A = [0 1; -1 0];
// E ∈ H(curl,Ω) , H ∈ H¹(Ω), Jᵢ, Qᵢ ∈ (H¹(Ω))²
// minimize the FOSLS functional:
// ( iωμ₀ H, F ) + ( ∇ × E, F) = 0, ∀ F ∈ L²(Ω)
// -(iωϵ₀ϵ E, R ) + ( ∇ × H, R) - ω ϵ₀ (J₁ + J₂, R) = 0, ∀ R ∈ (L²(Ω))²
// subject to the constraints
// (b⋅∇J₁, b ⋅ ∇K₁) + c₁(J₁, K₁) - c₁ (P B E, K₁) = 0, ∀ K₁ ∈ (H¹(Ω))²
// (b⋅∇J₂, b ⋅ ∇K₂) + c₂(J₂, K₂) + c₂ (P B E, K₂) = 0, ∀ K₂ ∈ (H¹(Ω))²
// E = E₀, on ∂Ω
// J₁ = J₂ = 0, on ∂Ω
// We formulate the problem as constrained minimization
// i.e, we minimize ||A U - F|| subject to B U = 0
// where U = [E, H, J₁, J₂]ᵀ
// A and B are given by:
// -----------------------------------------------
// A (FOSLS)
// ----------------------------------------------------------
// | | E | H | J₁ | J₂ |
// ----------------------------------------------------------
// | F | (∇ × E, F) | (iωμ₀H,F) | | |
// | | | | | |
// | R | -iωϵ₀ϵ(E,R) |(∇ × H, R) | -ωϵ₀(J₁,R) |-ωϵ₀(J₂, R)|
// B (Constraints)
// --------------------------------------------------------------------------------
// | | E | J₁ | J₂ |
// --------------------------------------------------------------------------------
// |K₁ | -c₁(PBE,K₁) | (b⋅∇J₁, b⋅∇K₁) + c₁(J₁, K₁) | |
// | | | | |
// |K₂ | c₂(PBE, K₂) | |(b⋅∇J₂, b⋅∇K₂) + c₂(J₂, K₂) |
// The saddle point system is then given by:
// | A B̄ᵀ | |U| = |F|
// | B 0 | |λ| |0|
#include "mfem.hpp"
#include "../util/pcomplexweakform.hpp"
#include "../util/pcomplexblockform.hpp"
#include "../util/blockcomplexhypremat.hpp"
#include "../../common/mfem-common.hpp"
#include "../util/maxwell_utils.hpp"
#include "utils/lh_utils.hpp"
#include "../util/utils.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
using namespace mfem::common;
int main(int argc, char *argv[])
{
Mpi::Init();
Hypre::Init();
const char *mesh_file = "data/LH_hot.msh";
int order = 1;
int delta_order = 1;
int par_ref_levels = 0;
int ser_ref_levels = 0;
// real_t rnum=1.5e9;
// real_t mu = 1.257e-6;
// real_t eps0 = 8.8541878128e-12;
real_t rnum=1.5;
real_t mu = 1.257;
real_t eps0 = 8.8541878128;
real_t cfactor = 1e-6;
real_t balance_scale = 1.0;
bool enable_balance_scale = false;
real_t delta_prec = 0.0;
bool static_cond = false;
bool visualization = false;
bool paraview = false;
bool debug = false;
bool mumps_solver = false;
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(&delta_order, "-do", "--delta-order",
"Finite element order for the test space");
args.AddOption(&ser_ref_levels, "-sr", "--serial-refinement_levels",
"Number of serial refinement levels.");
args.AddOption(&par_ref_levels, "-pr", "--parallel-refinement_levels",
"Number of parallel refinement levels.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&a0, "-a0", "--a0", "P(r) first parameter.");
args.AddOption(&a1, "-a1", "--a1", "P(r) second parameter.");
args.AddOption(&delta_prec, "-dp", "--delta-prec", "stability parameter for the preconditioner.");
args.AddOption(&mumps_solver, "-mumps", "--mumps", "-no-mumps",
"--no-mumps",
"Enable or disable MUMPS solver.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&debug, "-debug", "--debug", "-no-debug",
"--no-debug",
"Enable or disable debug mode (delta = 0.01 and no coupling).");
args.AddOption(&enable_balance_scale, "-ebs", "--enable-balance-scale", "-no-ebs",
"--no-ebs",
"Enable or disable balance scale.");
args.Parse();
if (!args.Good())
{
if (Mpi::Root())
{
args.PrintUsage(cout);
}
return 1;
}
if (Mpi::Root())
{
args.PrintOptions(cout);
}
// number of diffusion equations
int ndiffusionequations = 2;
Vector cvals(ndiffusionequations);
Vector csigns(ndiffusionequations);
cvals(0) = 25e6; cvals(1) = 1e6;
csigns(0) = -1.0; csigns(1) = 1.0;
cvals *= cfactor; // scale the coefficients
real_t omega = 2.*M_PI*rnum;
int test_order = order+delta_order;
balance_scale = (enable_balance_scale) ? eps0 : 1.0;
if (!debug)
{
delta = 0.0; // disable delta if electron Landau damping is enabled
if (Mpi::Root())
{
cout << "Electron Landau damping enabled, delta set to 0.0." << endl;
}
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
MFEM_VERIFY(dim == 2, "Dimension != 2 is not supported in this example");
for (int i = 0; i < ser_ref_levels; i++)
{
mesh.UniformRefinement();
}
Array<int> int_bdr_attr;
for (int i = 0; i < mesh.GetNBE(); i++)
{
if (mesh.FaceIsInterior(mesh.GetBdrElementFaceIndex(i)))
{
int_bdr_attr.Append(mesh.GetBdrAttribute(i));
}
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
for (int i = 0; i < par_ref_levels; i++)
{
pmesh.UniformRefinement();
}
int nattr = (pmesh.attributes.Size()) ? pmesh.attributes.Max() : 0;
Array<int> attr(nattr);
for (int i = 0; i<nattr; i++) { attr[i] = i+1; }
real_t scale = (debug) ? 0.0 : 1.0;
// Define coefficients
ConstantCoefficient one_cf(1.0);
// ωμ₀
ConstantCoefficient omegamu_cf(omega*mu);
// -ωϵ₀
ConstantCoefficient negomegeps0_cf(-omega*eps0 * scale);
ConstantCoefficient balancescaled_negomegeps0_cf( -omega*eps0/balance_scale * scale);
Vector zero(dim); zero = 0.0;
Vector one_x(dim); one_x = 0.0; one_x(0) = 1.0;
Vector negone_x(dim); negone_x = 0.0; negone_x(0) = -1.0;
VectorConstantCoefficient zero_vcf(zero);
VectorConstantCoefficient one_x_cf(one_x);
VectorConstantCoefficient negone_x_cf(negone_x);
DenseMatrix Mone(dim);
Mone = 0.0; Mone(0,0) = Mone(1,1) = 1.0;
MatrixConstantCoefficient Mone_cf(Mone);
DenseMatrix Mzero(dim); Mzero = 0.0;
MatrixConstantCoefficient Mzero_cf(Mzero);
Array<MatrixCoefficient*> coefs_r(nattr);
Array<MatrixCoefficient*> coefs_i(nattr);
for (int i = 0; i < nattr-1; ++i)
{
coefs_r[i] = &Mone_cf;
coefs_i[i] = &Mzero_cf;
}
// S(r)
FunctionCoefficient S_cf_r(sfunc_r), S_cf_i(sfunc_i);
// P(r)
FunctionCoefficient P_cf_r(pfunc_r), P_cf_i(pfunc_i);
// b
VectorFunctionCoefficient b_cf(dim,bfunc);
ScalarVectorProductCoefficient scaled_b_cf(sqrt(cfactor), b_cf);
ConstantCoefficient diff_coeff(cfactor);
// b⊗b
MatrixFunctionCoefficient bb_cf(dim,bcrossb);
// I - b⊗b
MatrixSumCoefficient oneminusbb(Mone_cf, bb_cf, 1.0, -1.0);
// S(r) (I - b⊗b)
ScalarMatrixProductCoefficient Soneminusbb_r(S_cf_r, oneminusbb), Soneminusbb_i(S_cf_i, oneminusbb);
// P(r) b⊗b
ScalarMatrixProductCoefficient P_cf_bb_r(P_cf_r, bb_cf), P_cf_bb_i(P_cf_i, bb_cf);
// ε = S(r) (I - b⊗b) + P(r) b⊗b
MatrixSumCoefficient eps_r(Soneminusbb_r, P_cf_bb_r, 1.0, 1.0);
MatrixSumCoefficient eps_i(Soneminusbb_i, P_cf_bb_i, 1.0, 1.0);
coefs_r[nattr-1] = &eps_r;
coefs_i[nattr-1] = &eps_i;
PWMatrixCoefficient eps_cf_r(dim, attr, coefs_r);
PWMatrixCoefficient eps_cf_i(dim, attr, coefs_i);
ConstantCoefficient eps0omeg(omega * eps0);
ConstantCoefficient negeps0omeg(-omega * eps0);
// - iωϵ₀ϵ = ωϵ₀ϵᵢ + i (-ωϵ₀ϵᵣ)
// ω ϵ₀ ϵᵢ
ScalarMatrixProductCoefficient eps0omeg_eps_i(eps0omeg, eps_cf_i);
// -ω ϵ₀ ϵᵣ
ScalarMatrixProductCoefficient negeps0omeg_eps_r(negeps0omeg, eps_cf_r);
// if ELD
Array<Vector *> c_arrays(ndiffusionequations);
Array<PWConstCoefficient *> pw_c_coeffs(ndiffusionequations);
Array<MatrixCoefficient *> cPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> cPibb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPibb_cf(ndiffusionequations);
Array<MatrixCoefficient *> balancescaled_signedcPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> balancescaled_signedcPibb_cf(ndiffusionequations);
Vector temp(nattr); temp=0.0;
Array<ConstantCoefficient *> c_coeffs(ndiffusionequations);
for (int i = 0; i<ndiffusionequations; i++)
{
temp[nattr-1] = cvals(i);
// temp = cvals(i);
pw_c_coeffs[i] = new PWConstCoefficient(temp);
c_coeffs[i] = new ConstantCoefficient(cvals(i));
cPrbb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_r);
cPibb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_i);
signedcPrbb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPrbb_cf[i]);
signedcPibb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPibb_cf[i]);
balancescaled_signedcPrbb_cf[i] = new ScalarMatrixProductCoefficient(balance_scale,*signedcPrbb_cf[i]);
balancescaled_signedcPibb_cf[i] = new ScalarMatrixProductCoefficient(balance_scale,*signedcPibb_cf[i]);
}
std::vector<std::string> fosls_variables = {" E ", " H ", " J₁ ", " J₂ "};
std::vector<std::string> fosls_spaces = {"H(curl,Ω) ", " H¹(Ω) ",
" (H¹(Ω))ᵈ", " (H¹(Ω))ᵈ"};
std::vector<std::string> constraints_variables = {" Q₁ ", " Q₂ "};
std::vector<std::string> constraints_spaces = {" (H¹(Ω))ᵈ", " (H¹(Ω))ᵈ"};
// Define the spaces
Array<FiniteElementCollection *> fosls_trial_fecols;
Array<FiniteElementCollection *> fosls_test_fecols;
Array<ParFiniteElementSpace *> fosls_pfes;
Array<FiniteElementCollection *> constraints_fecols;
Array<ParFiniteElementSpace *> constraints_pfes;
// H(curl) space for E
fosls_trial_fecols.Append(new ND_FECollection(order, dim));
fosls_pfes.Append(new ParFiniteElementSpace(&pmesh, fosls_trial_fecols.Last()));
// Scalar H¹ space for H
fosls_trial_fecols.Append(new H1_FECollection(order, dim));
fosls_pfes.Append(new ParFiniteElementSpace(&pmesh, fosls_trial_fecols.Last()));
// Vector H¹ spaces for Jᵢ
for (int i = 0; i < ndiffusionequations; i++)
{
fosls_trial_fecols.Append(new H1_FECollection(order, dim));
fosls_pfes.Append(new ParFiniteElementSpace(&pmesh, fosls_trial_fecols.Last(), dim));
constraints_fecols.Append(new H1_FECollection(order, dim));
constraints_pfes.Append(new ParFiniteElementSpace(&pmesh, constraints_fecols.Last(), dim));
}
Array<HYPRE_BigInt> tdofs(fosls_pfes.Size());
Array<HYPRE_BigInt> constraints_tdofs(constraints_pfes.Size());
for (int i = 0; i < fosls_pfes.Size(); ++i)
{
tdofs[i] = fosls_pfes[i]->GlobalTrueVSize();
if (Mpi::Root())
{
cout << " FOSLS FE Space " << fosls_spaces[i] << " for " << fosls_variables[i] << " has " << tdofs[i]
<< " true dofs." << endl;
}
}
for (int i = 0; i < constraints_pfes.Size(); ++i)
{
constraints_tdofs[i] = constraints_pfes[i]->GlobalTrueVSize();
if (Mpi::Root())
{
cout << " Constraint FE Space " << constraints_spaces[i] << " for " << constraints_variables[i] << " has " << constraints_tdofs[i]
<< " true dofs." << endl;
}
}
if (Mpi::Root())
{
cout << "Total number of true dofs: " << tdofs.Sum() << endl;
}
// test spaces for F and R
fosls_test_fecols.Append(new L2_FECollection(test_order, dim));
fosls_test_fecols.Append(new L2_FECollection(test_order, dim));
ParComplexDPGWeakForm * a = new ParComplexDPGWeakForm(fosls_pfes,fosls_test_fecols);
a->SetTestFECollVdim(1,dim);
// (∇ × E, F)
a->AddTrialIntegrator(new MixedCurlIntegrator(one_cf), nullptr, 0, 0);
// (i ω μ₀ H, F)
a->AddTrialIntegrator(nullptr, new MixedScalarMassIntegrator(omegamu_cf), 1, 0);
// -i ω ϵ₀ϵ (E, R) = ω ϵ₀ ϵᵢ (E, G) + i (-ω ϵ₀ ϵᵣ E, R)
a->AddTrialIntegrator(new VectorFEMassIntegrator(eps0omeg_eps_i),
new VectorFEMassIntegrator(negeps0omeg_eps_r), 0, 1);
// (∇ × H, R)
a->AddTrialIntegrator(new MixedCurlIntegrator(one_cf), nullptr, 1,1);
// - ω ϵ₀ (J₁, R)
a->AddTrialIntegrator(new VectorMassIntegrator(balancescaled_negomegeps0_cf), nullptr, 2, 1);
// - ω ϵ₀ (J₂, R)
a->AddTrialIntegrator(new VectorMassIntegrator(balancescaled_negomegeps0_cf), nullptr, 3, 1);
// test integrators for test norm: ||v||² = ||F||² + ||R||² + ||K₁||² + ||L₁||² + ||K₂||² + ||L₂||²
// (F,δF), F, δF ∈ L²(Ω)
a->AddTestIntegrator(new MassIntegrator(one_cf),nullptr, 0, 0);
// (R,δR), R, δR ∈ (L²(Ω))²
a->AddTestIntegrator(new VectorMassIntegrator(one_cf),nullptr, 1, 1);
a->Assemble(0);
// Constraint matrix B
ParMixedBilinearForm bEK1_r(fosls_pfes[0], constraints_pfes[0]);
ParMixedBilinearForm bEK1_i(fosls_pfes[0], constraints_pfes[0]);
// - c₁ (P B E, K₁)
bEK1_r.AddDomainIntegrator(new VectorFEMassIntegrator(*balancescaled_signedcPrbb_cf[0]));
bEK1_i.AddDomainIntegrator(new VectorFEMassIntegrator(*balancescaled_signedcPibb_cf[0]));
bEK1_r.Assemble();
bEK1_i.Assemble();
ParMixedBilinearForm bK2H_r(constraints_pfes[1], fosls_pfes[1]);
bK2H_r.Assemble();
ParMixedBilinearForm bEK2_r(fosls_pfes[0], constraints_pfes[1]);
ParMixedBilinearForm bEK2_i(fosls_pfes[0], constraints_pfes[1]);
// c₂(PBE, K₂)
bEK2_r.AddDomainIntegrator(new VectorFEMassIntegrator(*balancescaled_signedcPrbb_cf[1]));
bEK2_i.AddDomainIntegrator(new VectorFEMassIntegrator(*balancescaled_signedcPibb_cf[1]));
bEK2_r.Assemble();
bEK2_i.Assemble();
// (b⋅∇J₁, b⋅∇K₁) + c₁(J₁, K₁)
ParBilinearForm bJ1K1(constraints_pfes[0]);
bJ1K1.AddDomainIntegrator(new DirectionalVectorDiffusionIntegrator(scaled_b_cf));
bJ1K1.AddDomainIntegrator(new VectorMassIntegrator(*pw_c_coeffs[0]));
bJ1K1.Assemble();
ParBilinearForm bJ2K2(constraints_pfes[1]);
bJ2K2.AddDomainIntegrator(new DirectionalVectorDiffusionIntegrator(scaled_b_cf));
bJ2K2.AddDomainIntegrator(new VectorMassIntegrator(*pw_c_coeffs[1]));
bJ2K2.Assemble();
int fosls_npfes = fosls_pfes.Size();
Array<int> fosls_offsets(fosls_npfes+1); fosls_offsets[0] = 0;
Array<int> fosls_toffsets(fosls_npfes+1); fosls_toffsets[0] = 0;
for (int i = 0; i<fosls_npfes; i++)
{
fosls_offsets[i+1] = fosls_pfes[i]->GetVSize();
fosls_toffsets[i+1] = fosls_pfes[i]->TrueVSize();
}
fosls_offsets.PartialSum();
fosls_toffsets.PartialSum();
Array<int> empty;
OperatorPtr fosls_Aop;
a->FormSystemMatrix(empty, fosls_Aop);
ComplexOperator * fosls_Ac = fosls_Aop.As<ComplexOperator>();
OperatorPtr constraintOp_EK1_r, constraintOp_EK1_i;
bEK1_r.FormRectangularSystemMatrix(empty, empty, constraintOp_EK1_r);
bEK1_i.FormRectangularSystemMatrix(empty, empty, constraintOp_EK1_i);
OperatorPtr constraintOp_EK2_r, constraintOp_EK2_i;
bEK2_r.FormRectangularSystemMatrix(empty, empty, constraintOp_EK2_r);
bEK2_i.FormRectangularSystemMatrix(empty, empty, constraintOp_EK2_i);
OperatorPtr constraintOp_J1K1;
bJ1K1.FormSystemMatrix(empty, constraintOp_J1K1);
OperatorPtr constraintOp_J2K2;
bJ2K2.FormSystemMatrix(empty, constraintOp_J2K2);
for (int i = 0; i<ndiffusionequations; i++)
{
delete pw_c_coeffs[i];
delete c_coeffs[i];
delete cPrbb_cf[i];
delete cPibb_cf[i];
delete signedcPrbb_cf[i];
delete signedcPibb_cf[i];
}
// Assemble all the system and get the global matrix and right-hand side
Array<ParFiniteElementSpace *> all_pfes;
all_pfes.Append(fosls_pfes);
all_pfes.Append(constraints_pfes);
int npfes = all_pfes.Size();
Array<int> all_offsets(npfes + 1); all_offsets[0] = 0;
Array<int> all_toffsets(npfes + 1); all_toffsets[0] = 0;
for (int i = 0; i < npfes; ++i)
{
all_offsets[i+1] = all_pfes[i]->GetVSize();
all_toffsets[i+1] = all_pfes[i]->GetTrueVSize();
}
all_offsets.PartialSum();
all_toffsets.PartialSum();
// put all the operators into a block operator
BlockOperator A_r(all_toffsets);
BlockOperator A_i(all_toffsets);
BlockOperator * BlockAfosls_r = dynamic_cast<BlockOperator *>(&fosls_Ac->real());
BlockOperator * BlockAfosls_i = dynamic_cast<BlockOperator *>(&fosls_Ac->imag());
for (int i = 0; i < fosls_pfes.Size(); ++i)
{
for (int j = 0; j < fosls_pfes.Size(); ++j)
{
A_r.SetBlock(i, j, &BlockAfosls_r->GetBlock(i, j));
A_i.SetBlock(i, j, &BlockAfosls_i->GetBlock(i, j));
}
}
A_r.SetBlock(fosls_pfes.Size()+0, 0, constraintOp_EK1_r.Ptr());
A_i.SetBlock(fosls_pfes.Size()+0, 0, constraintOp_EK1_i.Ptr());
A_r.SetBlock(fosls_pfes.Size()+1, 0, constraintOp_EK2_r.Ptr());
A_i.SetBlock(fosls_pfes.Size()+1, 0, constraintOp_EK2_i.Ptr());
A_r.SetBlock(fosls_pfes.Size()+0, 2, constraintOp_J1K1.Ptr());
A_r.SetBlock(fosls_pfes.Size()+1, 3, constraintOp_J2K2.Ptr());
// Construct the adjoint operator B̄ᵀ
HypreParMatrix * constraintOp_EK1_r_t = constraintOp_EK1_r.As<HypreParMatrix>()->Transpose();
HypreParMatrix * constraintOp_EK1_i_t = constraintOp_EK1_i.As<HypreParMatrix>()->Transpose();
HypreParMatrix * constraintOp_EK2_r_t = constraintOp_EK2_r.As<HypreParMatrix>()->Transpose();
HypreParMatrix * constraintOp_EK2_i_t = constraintOp_EK2_i.As<HypreParMatrix>()->Transpose();
HypreParMatrix * constraintOp_J1K1_t = constraintOp_J1K1.As<HypreParMatrix>()->Transpose();
HypreParMatrix * constraintOp_J2K2_t = constraintOp_J2K2.As<HypreParMatrix>()->Transpose();
// We also need to scale the imaginary part of B̄ᵀ by -1;
*constraintOp_EK1_i_t *= -1.0;
*constraintOp_EK2_i_t *= -1.0;
A_r.SetBlock(0, fosls_pfes.Size()+0, constraintOp_EK1_r_t);
A_i.SetBlock(0, fosls_pfes.Size()+0, constraintOp_EK1_i_t);
A_r.SetBlock(0, fosls_pfes.Size()+1, constraintOp_EK2_r_t);
A_i.SetBlock(0, fosls_pfes.Size()+1, constraintOp_EK2_i_t);
A_r.SetBlock(2, fosls_pfes.Size()+0, constraintOp_J1K1_t);
A_r.SetBlock(3, fosls_pfes.Size()+1, constraintOp_J2K2_t);
// // dammies for other coupling terms in B
// ParMixedBilinearForm bHK1_r(fosls_pfes[1], constraints_pfes[0]); bHK1_r.Assemble();
// ParMixedBilinearForm bHK1_i(fosls_pfes[1], constraints_pfes[0]); bHK1_i.Assemble();
// OperatorPtr constraintOp_HK1_r, constraintOp_HK1_i;
// bHK1_r.FormRectangularSystemMatrix(empty, empty, constraintOp_HK1_r);
// bHK1_i.FormRectangularSystemMatrix(empty, empty, constraintOp_HK1_i);
// A_r.SetBlock(fosls_pfes.Size()+0, 1, constraintOp_HK1_r.Ptr());
// A_i.SetBlock(fosls_pfes.Size()+0, 1, constraintOp_HK1_i.Ptr());
// ParMixedBilinearForm bHK2_r(fosls_pfes[1], constraints_pfes[1]); bHK2_r.Assemble();
// ParMixedBilinearForm bHK2_i(fosls_pfes[1], constraints_pfes[1]); bHK2_i.Assemble();
// ParMixedBilinearForm bK1H_r(constraints_pfes[0], fosls_pfes[1]); bK1H_r.Assemble();
// ParMixedBilinearForm bK1H_i(constraints_pfes[0], fosls_pfes[1]); bK1H_i.Assemble();
// ParMixedBilinearForm bK2H_r(fosls_pfes[1], constraints_pfes[1]); bK2H_r.Assemble();
// ParMixedBilinearForm bK2H_i(fosls_pfes[1], constraints_pfes[0]); bK2H_i.Assemble();
// ParBilinearForm bJ1K2(constraints_pfes[0]);
ComplexOperator * A = new ComplexOperator(&A_r, &A_i, false, false);
if (Mpi::Root())
{
mfem::out << "Complex Operator A finished successfully." << endl;
}
socketstream E_out_r;
Vector x(2*all_offsets.Last());
x = 0.;
Array<ParGridFunction *> pgf_r(npfes);
Array<ParGridFunction *> pgf_i(npfes);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i] = new ParGridFunction(all_pfes[i], x, all_offsets[i]);
pgf_i[i] = new ParGridFunction(all_pfes[i], x, all_offsets.Last() + all_offsets[i]);
}
L2_FECollection L2fec(order, dim);
ParFiniteElementSpace L2_fes(&pmesh, &L2fec);
ParGridFunction E_par_r(&L2_fes);
ParGridFunction E_par_i(&L2_fes);
ParaViewDataCollection * paraview_dc = nullptr;
std::string output_dir = "ParaView/FOSLS-FEM/" + GetTimestamp();
if (paraview)
{
if (Mpi::Root()) { WriteParametersToFile(args, output_dir); }
std::ostringstream paraview_file_name;
std::string filename = GetFilename(mesh_file);
paraview_file_name << filename
<< "_par_ref_" << par_ref_levels
<< "_order_" << order
<< "_eld_1" ;
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
paraview_dc->SetPrefixPath(output_dir);
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("Er",pgf_r[0]);
paraview_dc->RegisterField("Ei",pgf_i[0]);
paraview_dc->RegisterField("E_par_r",&E_par_r);
paraview_dc->RegisterField("E_par_i",&E_par_i);
paraview_dc->RegisterField("Hr",pgf_r[1]);
paraview_dc->RegisterField("Hi",pgf_i[1]);
paraview_dc->RegisterField("J1r",pgf_r[2]);
paraview_dc->RegisterField("J1i",pgf_i[2]);
paraview_dc->RegisterField("J2r",pgf_r[3]);
paraview_dc->RegisterField("J2i",pgf_i[3]);
paraview_dc->RegisterField("Q1r",pgf_r[4]);
paraview_dc->RegisterField("Q1i",pgf_i[4]);
paraview_dc->RegisterField("Q2r",pgf_r[5]);
paraview_dc->RegisterField("Q2i",pgf_i[5]);
}
Array<int> ess_tdof_list;
Array<int> ess_tdof_listJ;
Array<int> ess_tdof_listQ;
Array<int> ess_bdr;
Array<int> one_r_bdr;
Array<int> one_i_bdr;
Array<int> negone_r_bdr;
Array<int> negone_i_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_r_bdr.SetSize(pmesh.bdr_attributes.Max());
one_i_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_r_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_i_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
// remove internal boundaries
for (int i = 0; i<int_bdr_attr.Size(); i++)
{
ess_bdr[int_bdr_attr[i]-1] = 0;
}
all_pfes[0]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
for (int j = 0; j < ess_tdof_list.Size(); j++)
{
ess_tdof_list[j] += all_toffsets[0];
}
ess_bdr = 1;
for (int i = 0; i<ndiffusionequations;i++)
{
ess_tdof_listJ.SetSize(0);
ess_tdof_listQ.SetSize(0);
all_pfes[i+2]->GetEssentialTrueDofs(ess_bdr, ess_tdof_listJ);
all_pfes[i+4]->GetEssentialTrueDofs(ess_bdr, ess_tdof_listQ);
for (int j = 0; j < ess_tdof_listJ.Size(); j++)
{
ess_tdof_listJ[j] += all_toffsets[i+2];
}
for (int j = 0; j < ess_tdof_listQ.Size(); j++)
{
ess_tdof_listQ[j] += all_toffsets[i+4];
}
ess_tdof_list.Append(ess_tdof_listJ);
ess_tdof_list.Append(ess_tdof_listQ);
}
one_r_bdr = 0; one_i_bdr = 0;
negone_r_bdr = 0; negone_i_bdr = 0;
// attr = 30,2 (real)
one_r_bdr[30-1] = 1; one_r_bdr[2-1] = 1;
// attr = 26,6 (imag)
one_i_bdr[26-1] = 1; one_i_bdr[6-1] = 1;
// attr = 22,10 (real)
negone_r_bdr[22-1] = 1; negone_r_bdr[10-1] = 1;
// attr = 18,14 (imag)
negone_i_bdr[18-1] = 1; negone_i_bdr[14-1] = 1;
}
pgf_r[0]->ProjectBdrCoefficientTangent(one_x_cf, one_r_bdr);
pgf_r[0]->ProjectBdrCoefficientTangent(negone_x_cf, negone_r_bdr);
pgf_i[0]->ProjectBdrCoefficientTangent(one_x_cf, one_i_bdr);
pgf_i[0]->ProjectBdrCoefficientTangent(negone_x_cf, negone_i_bdr);
BlockOperator * P = new BlockOperator(all_offsets, all_toffsets);
BlockMatrix * R = new BlockMatrix(all_toffsets, all_offsets);
P->owns_blocks = 0;
R->owns_blocks = 0;
for (int i = 0; i < npfes; i++)
{
HypreParMatrix * P_ = all_pfes[i]->Dof_TrueDof_Matrix();
P->SetBlock(i,i,P_);
const SparseMatrix * R_ = all_pfes[i]->GetRestrictionMatrix();
R->SetBlock(i, i, const_cast<SparseMatrix*>(R_));
}
if (Mpi::Root())
{
mfem::out << "Build prolongation finished" << endl;
}
int n = P->Width();
Vector B(2*n); B = 0.0;
Vector X(2*n);
Vector X_r(X, 0, n);
Vector X_i(X, n, n);
Vector x_r(x, 0, x.Size()/2);
Vector x_i(x, x.Size()/2, x.Size()/2);
R->Mult(x_r, X_r);
R->Mult(x_i, X_i);
ParBlockComplexSystem aa(A);
A = aa.EliminateBC(ess_tdof_list, X, B);
ess_tdof_list.Print(mfem::out);
if (Mpi::Root())
{
mfem::out << "Eliminate BC finished successfully." << endl;
}
bool direct_solve = true;
if (direct_solve)
{
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&A->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&A->imag());
int nblocks = BlockA_r->NumRowBlocks();
Array2D<const HypreParMatrix*> A_r_matrices(nblocks, nblocks);
Array2D<const HypreParMatrix*> A_i_matrices(nblocks, nblocks);
for (int i = 0; i < nblocks; i++)
{
for (int j = 0; j < nblocks; j++)
{
if (BlockA_r->IsZeroBlock(i,j))
{
A_r_matrices(i,j) = nullptr;
}
else
{
A_r_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&BlockA_r->GetBlock(i,j));
}
if (BlockA_i->IsZeroBlock(i,j))
{
A_i_matrices(i,j) = nullptr;
}
else
{
A_i_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&BlockA_i->GetBlock(i,j));
}
}
}
HypreParMatrix * Ahr = HypreParMatrixFromBlocks(A_r_matrices);
HypreParMatrix * Ahi = HypreParMatrixFromBlocks(A_i_matrices);
ComplexHypreParMatrix * Ahc_hypre =
new ComplexHypreParMatrix(Ahr, Ahi,true, true);
ComplexMUMPSSolver cmumps(MPI_COMM_WORLD);
cmumps.SetPrintLevel(1);
cmumps.SetOperator(*Ahc_hypre);
if (Mpi::Root())
{
mfem::out << "Setup CMUMPS solver finished successfully." << endl;
}
cmumps.Mult(B,X);
delete Ahc_hypre;
}
else
{
MFEM_ABORT("to be implemented: iterative solver with preconditioner");
}
if (Mpi::Root())
{
mfem::out << "Solve finished successfully." << endl;
}
n = P->Height();
int m = P->Width();
x_r.MakeRef(x, 0, n);
x_i.MakeRef(x, n, n);
X_r.MakeRef(X, 0, m);
X_i.MakeRef(X, m, m);
P->Mult(X_r, x_r);
P->Mult(X_i, x_i);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i]->MakeRef(all_pfes[i], x, all_offsets[i]);
pgf_i[i]->MakeRef(all_pfes[i], x, all_offsets.Last() + all_offsets[i]);
}
ParallelECoefficient par_e_r(pgf_r[0]);
ParallelECoefficient par_e_i(pgf_i[0]);
E_par_r.ProjectCoefficient(par_e_r);
E_par_i.ProjectCoefficient(par_e_i);
if (visualization)
{
const char * keys = nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, *pgf_r[0],
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(0);
paraview_dc->SetTime((real_t)0);
paraview_dc->Save();
delete paraview_dc;
}
return 0;
// a->RecoverFEMSolution(X, x);
// for (int i = 0; i < npfes; ++i)
// {
// pgf_r[i]->MakeRef(pfes[i], x, offsets[i]);
// pgf_i[i]->MakeRef(pfes[i], x, offsets.Last() + offsets[i]);
// }
// ParallelECoefficient par_e_r(pgf_r[0]);
// ParallelECoefficient par_e_i(pgf_i[0]);
// E_par_r.ProjectCoefficient(par_e_r);
// E_par_i.ProjectCoefficient(par_e_i);
// // // rescale the J solutions
// for (int i = 0; i < 2*ndiffusionequations; ++i)
// {
// (*pgf_r[2+i]) /= balance_scale;
// (*pgf_i[2+i]) /= balance_scale;
// }
// if (visualization)
// {
// const char * keys = nullptr;
// char vishost[] = "localhost";
// int visport = 19916;
// common::VisualizeField(E_out_r,vishost, visport, *pgf_r[0],
// "Numerical Electric field (real part)", 0, 0, 500, 500, keys);
// }
// if (paraview)
// {
// paraview_dc->SetCycle(0);
// paraview_dc->SetTime((real_t)0);
// paraview_dc->Save();
// delete paraview_dc;
// }
// delete a;
// for (int i = 0; i < trial_fecols.Size(); ++i)
// {
// delete trial_fecols[i];
// delete pfes[i];
// }
// for (int i = 0; i< test_fecols.Size(); ++i)
// {
// delete test_fecols[i];
// }
// return 0;
}
+980
View File
@@ -0,0 +1,980 @@
// MFEM UW parallel example
//
// Electron Landau Damping
//
// ∇×(1/μ₀∇×E) - ω² ϵ₀ ϵᵣ E + i ω²ϵ₀(J₁ + J₂) = 0, in Ω
// - (b⋅∇)²J₁ + c₁ J₁ - c₁ P b⊗b E = 0, in Ω
// - (b⋅∇)²J₂ + c₂ J₂ + c₂ P b⊗b E = 0, in Ω
// E×n = E₀, on ∂Ω
// J₁ = 0, on ∂Ω
// J₂ = 0, on ∂Ω
// First order system:
// i ω μ₀ H + ∇ × E = 0, in Ω
// -i ω ϵ₀ ϵᵣ E + ∇ × H - α ω ϵ₀ (J₁ + J₂) = 0, in Ω
// b ⋅ ∇Q₁ + c₁ J₁ - c₁ P B E = 0, in Ω
// Q₁ + b ⋅ ∇J₁ = 0, in Ω
// b ⋅ ∇Q₂ + c₂ J₂ + c₂ P B E = 0, in Ω
// Q₂ + b ⋅ ∇J₂ = 0, in Ω
// where B = b ⊗ b. Here α serves as a scaling factor. For α = 0 the system becomes
// lower trianular (Maxwell decouples from the diffusion equations).
// Note that in 2D we have the following 2 definitions of the curl.
// a) for a 2D vector E, ∇ × E:= ∇ ⋅ (R E) where R = [0 1; -1 0]
// b) for a scalar H, ∇ × H:= R ∇ H
// Define the group variables
// then the strong formulation reads:
// (A u, v) = (i ω μ₀ H, F) + (∇ × E, F)
// - (i ω ϵ₀ ϵᵣ E, W) + (∇ × H, W) - (α ω ϵ₀ (J₁ + J₂), W)
// + (b ⋅ ∇Q₁, K₁) + (c₁ J₁, K₁) - (c₁ P B E, K₁) + (Q₁, L₁) + (b ⋅ ∇J₁, L₁)
// + (b ⋅ ∇Q₂, K₂) + (c₂ J₂, K₂) + (c₂ P B E, K₂) + (Q₂, L₂) + (b ⋅ ∇J₂, L₂)
//
// and the adjoint operator is defined by:
// (u, A^⋆ v) = (E, ∇×F + i ω ϵ₀ ϵ⋆ᵣ W - c₁ P̄ B K₁ + c₂ P̄ B K₂)
// + (H, -i ω μ₀ F + ∇ × W)
// + (J₁, -α ω ϵ₀ W + c₁ K₁ - b ⋅ ∇ L₁)
// + (Q₁, - b ⋅ ∇ K₁ + L₁)
// + (J₂, -α ω ϵ₀ W + c₂ K₂ - b ⋅ ∇ L₂)
// + (Q₂, - b ⋅ ∇ K₂ + L₂)
//
// Define the group trial field and trace variables
// u:=(E,H,J₁,Q₁,J₂,Q₂), û:=(Ê, Ĥ, Ĵ₁, Q̂₁, Ĵ₂, Q̂₂) and
// the test variable v:=(F,W,K₁,L₁,K₂,L₂)
// The ultraweak variational formulation is then given by:
// Find u ∈ U=((L²(Ω))² × L²(Ω) × (L²(Ω))² × (L²(Ω))² × (L²(Ω))² × (L²(Ω))²),
// û ∈ Û=( H⁻¹/²(Γₕ) × H¹/²(Γₕ) × (H⁻¹/²(Γₕ))² × (H⁻¹/²(Γₕ))² × (H⁻¹/²(Γₕ))²× (H⁻¹/²(Γₕ))² )
// such that ∀ v ∈ V=(H¹(Ω) × H(curl,Ω) × (H¹(Ω))² × (H¹(Ω))² × (H¹(Ω))² × (H¹(Ω))²)
// (u, A^⋆ v) + b̂(û, v) = 0,
// or equivalently
//
// (i ω μ₀ H, F) + (E, ∇×F) + < R Ê, F > = 0, ∀ F ∈ H¹(Ω)
// -i ω ϵ₀ ϵᵣ (E, W) + (H, ∇ × W) + < Ĥ, W × n > - α ω ϵ₀ (J₁ + J₂, W) = 0, ∀ W ∈ H(curl,Ω)
// -(Q, b ⋅ ∇ K₁) + <Q̂₁, K₁> + (c₁ J₁, K₁) - c₁(P B E, K₁) = 0, ∀ K₁ ∈ (H¹(Ω))²
// (Q₁, L₁) - (J₁, b ⋅ ∇L₁) + <Ĵ₁, L₁> = 0, ∀ L₁ ∈ (H¹(Ω))²
// -(Q, b⋅ ∇ K₂) + <Q̂₂, K₂> + (c₂ J₂, K₂) + c₂(P B E, K₂) = 0, ∀ K₂ ∈ (H¹(Ω))²
// (Q₂, L₂) - (J₂, b ⋅ ∇L₂) + <Ĵ₂, L₂> = 0, ∀ L₂ ∈ (H¹(Ω))²
// with the adjoint graph test norm defined by:
// ‖v‖²_V = ‖A^⋆ v‖² + ‖v‖²
// = ‖∇×F + i ω ϵ₀ ϵ⋆ᵣ W - c₁ P̄ B K₁ + c₂ P̄ B K₂‖²
// + ‖-i ω μ₀ F + ∇ × W‖²
// + ‖-α ω ϵ₀ W + c₁ K₁ - b ⋅ ∇ L₁‖²
// + ‖- b ⋅ ∇ K₁ + L₁‖²
// + ‖-α ω ϵ₀ W + c₂ K₂ - b ⋅ ∇ L₂‖²
// + ‖- b ⋅ ∇ K₂ + L₂‖²
// + ‖F‖² + ‖W‖² + ‖K₁‖² + ‖L₁‖² + ‖K₂‖² + ‖L₂‖²
//
#include "mfem.hpp"
#include "../util/pcomplexweakform.hpp"
#include "../../common/mfem-common.hpp"
#include "../util/maxwell_utils.hpp"
#include "utils/lh_utils.hpp"
#include "../util/utils.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
using namespace mfem::common;
// Define spaces
enum TrialSpace
{
E_space = 0,
H_space = 1,
J1_space = 2,
Q1_space = 3,
J2_space = 4,
Q2_space = 5,
hatE_space = 6,
hatH_space = 7,
hatJ1_space = 8,
hatQ1_space = 9,
hatJ2_space = 10,
hatQ2_space = 11
};
enum TestSpace
{
F_space = 0,
W_space = 1,
K1_space = 2,
L1_space = 3,
K2_space = 4,
L2_space = 5
};
inline const char * ToString(TrialSpace ts)
{
switch (ts)
{
case E_space: return " L²(Ω)² for E ";
case H_space: return " L²(Ω) for H ";
case J1_space: return " L²(Ω)² for J₁ ";
case Q1_space: return " L²(Ω)² for Q₁ ";
case J2_space: return " L²(Ω)² for J₂ ";
case Q2_space: return " L²(Ω)² for Q₂ ";
case hatE_space: return " tr(RT) for Ê ";
case hatH_space: return " tr(H¹) for Ĥ ";
case hatJ1_space: return " tr(RT)² for Ĵ₁ ";
case hatQ1_space: return " tr(RT)² for Q̂₁ ";
case hatJ2_space: return " tr(RT)² for Ĵ₂ ";
case hatQ2_space: return " tr(RT)² for Q̂₂ ";
default: return "Unknown TrialSpace";
}
}
int main(int argc, char *argv[])
{
Mpi::Init();
Hypre::Init();
const char *mesh_file = "data/LH_hot.msh";
int order = 1;
int delta_order = 1;
int par_ref_levels = 0;
int ser_ref_levels = 0;
real_t rnum=1.5;
real_t mu = 1.257;
real_t eps0 = 8.8541878128;
real_t alpha = 1.0; // scaling factor for E, Js coupling
bool static_cond = false;
bool visualization = false;
bool paraview = false;
bool debug = false;
bool mumps_solver = false;
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(&delta_order, "-do", "--delta-order",
"Finite element order for the test space");
args.AddOption(&ser_ref_levels, "-sr", "--serial-refinement_levels",
"Number of serial refinement levels.");
args.AddOption(&par_ref_levels, "-pr", "--parallel-refinement_levels",
"Number of parallel refinement levels.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&a0, "-a0", "--a0", "P(r) first parameter.");
args.AddOption(&a1, "-a1", "--a1", "P(r) second parameter.");
args.AddOption(&alpha, "-alpha", "--alpha",
"Scaling factor for E, Js coupling.");
args.AddOption(&mumps_solver, "-mumps", "--mumps", "-no-mumps",
"--no-mumps",
"Enable or disable MUMPS solver.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&debug, "-debug", "--debug", "-no-debug",
"--no-debug",
"Enable or disable debug mode (delta = 0.01 and no coupling).");
args.Parse();
if (!args.Good())
{
if (Mpi::Root())
{
args.PrintUsage(cout);
}
return 1;
}
if (Mpi::Root())
{
args.PrintOptions(cout);
}
// number of diffusion equations
int ndiffusionequations = 2;
if (!debug)
{
delta = 0.0; // disable delta if electron Landau damping is enabled
if (Mpi::Root())
{
cout << "Electron Landau damping enabled, delta set to 0.0." << endl;
}
}
else
{
if (Mpi::Root())
{
alpha = 0.0;
cout << "Setting alpha = 0.0 to disable coupling between E and Js." << endl;
cout << "Debug mode enabled, delta = 0.01 and no coupling." << endl;
}
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
MFEM_VERIFY(dim == 2, "Dimension != 2 is not supported in this example");
for (int i = 0; i < ser_ref_levels; i++)
{
mesh.UniformRefinement();
}
Array<int> int_bdr_attr;
for (int i = 0; i < mesh.GetNBE(); i++)
{
if (mesh.FaceIsInterior(mesh.GetBdrElementFaceIndex(i)))
{
int_bdr_attr.Append(mesh.GetBdrAttribute(i));
}
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
for (int i = 0; i < par_ref_levels; i++)
{
pmesh.UniformRefinement();
}
int nattr = (pmesh.attributes.Size()) ? pmesh.attributes.Max() : 0;
Array<int> attr(nattr);
for (int i = 0; i<nattr; i++) { attr[i] = i+1; }
real_t omega = 2.*M_PI*rnum;
real_t neg_omega_eps0 = -omega * eps0;
real_t omega_eps0 = omega * eps0;
Vector cvals(ndiffusionequations);
Vector csigns(ndiffusionequations);
real_t cfactor = 1e-6;
cvals(0) = 25e6; cvals(1) = 1e6;
csigns(0) = -1.0; csigns(1) = 1.0;
cvals *= cfactor; // scale the coefficients
// List of coefficients
// Trial integrators coefficients
DenseMatrix R(dim), Rt(dim);
R(0,0) = 0.0; R(0,1) = 1.0;
R(1,0) = -1.0; R(1,1) = 0.0;
R.Transpose(Rt);
VectorFunctionCoefficient no_scale_bvec_cf(dim,bfunc);// b
real_t scaled_cfactor = std::sqrt(cfactor);
ScalarVectorProductCoefficient bvec_cf(scaled_cfactor,no_scale_bvec_cf); // scaled b
// VectorFunctionCoefficient bvec_cf(dim,bfunc);// b
ScalarVectorProductCoefficient neg_bvec_cf(-1.0,bvec_cf); // -b
ScalarVectorProductCoefficient alpha_omega_eps0_bvec_cf(eps0*omega*alpha,bvec_cf); // α ω ϵ₀ b
MatrixFunctionCoefficient B_cf(dim, bcrossb);
FunctionCoefficient p_r_cf(pfunc_r);
FunctionCoefficient p_i_cf(pfunc_i);
DenseMatrix Mone(dim);
Mone = 0.0; Mone(0,0) = Mone(1,1) = 1.0;
MatrixConstantCoefficient Mone_cf(Mone);
DenseMatrix Mzero(dim); Mzero = 0.0;
MatrixConstantCoefficient Mzero_cf(Mzero);
Array<MatrixCoefficient*> coefs_r(nattr);
Array<MatrixCoefficient*> coefs_i(nattr);
for (int i = 0; i < nattr-1; ++i)
{
coefs_r[i] = &Mone_cf;
coefs_i[i] = &Mzero_cf;
}
Array<Vector *> cf_arrays(ndiffusionequations);
Array<PWConstCoefficient *> c_cf(ndiffusionequations);
Array<ProductCoefficient *> neg_c_alpha_omega_eps0_cf(ndiffusionequations);
Array<ProductCoefficient *> c2_cf(ndiffusionequations);
Array<ProductCoefficient *> signed_c_cf(ndiffusionequations);
Array<MatrixCoefficient*> signed_PB_r_cf(ndiffusionequations);
Array<MatrixCoefficient*> signed_PB_i_cf(ndiffusionequations);
Array<ProductCoefficient*> neg_signed_c_omega_eps_cf(ndiffusionequations);
Array<ProductCoefficient*> signed_c_omega_eps_cf(ndiffusionequations);
Array<MatrixCoefficient*> signed_PBR_r_cf(ndiffusionequations);
Array<MatrixCoefficient*> signed_PBR_i_cf(ndiffusionequations);
Array<MatrixCoefficient *> c2_absP2BB_cf(ndiffusionequations);
Array<VectorCoefficient*> neg_c_bvec_cf(ndiffusionequations);
Vector zerovec(nattr); zerovec=0.0;
// ϵᵣ in plasma (real)
MatrixFunctionCoefficient eps_r_temp_cf(dim, epsilon_func_r);
// ϵᵣ in plasma(imag)
MatrixFunctionCoefficient eps_i_temp_cf(dim, epsilon_func_i);
coefs_r[nattr-1] = &eps_r_temp_cf;
coefs_i[nattr-1] = &eps_i_temp_cf;
// ωμ₀
ConstantCoefficient omegamu_cf(mu * omega);
// -ωμ₀
ConstantCoefficient neg_omegamu_cf(-mu * omega);
// ω² μ₀²
ConstantCoefficient omega2mu2_cf(mu * mu * omega * omega);
// R
MatrixConstantCoefficient R_cf(R);
// Rᵀ
MatrixConstantCoefficient Rt_cf(Rt);
// ϵᵣ (real)
PWMatrixCoefficient eps_r_cf(dim, attr, coefs_r);
// ϵᵣ (imag)
PWMatrixCoefficient eps_i_cf(dim, attr, coefs_i);
// -ω ϵ₀ ϵᵣ (real)
ScalarMatrixProductCoefficient neg_omega_eps0_eps_r_cf(neg_omega_eps0, eps_r_cf);
// ω ϵ₀ ϵᵣ (imag)
ScalarMatrixProductCoefficient omega_eps0_eps_i_cf(omega_eps0, eps_i_cf);
// ω ϵ₀ ϵᵣᵢ R
MatrixProductCoefficient omega_eps0_eps_i_R_cf(omega_eps0_eps_i_cf, R_cf);
// -ω ϵ₀ ϵᵣᵣ R
MatrixProductCoefficient neg_omega_eps0_eps_r_R_cf(neg_omega_eps0_eps_r_cf, R_cf);
//
// ω ϵ₀ Rᵀ ϵᵣᵢ
MatrixProductCoefficient omega_eps0_Rt_eps_i_cf(Rt_cf, omega_eps0_eps_i_cf);
// ω ϵ₀ ϵᵣ
ScalarMatrixProductCoefficient omega_eps0_eps_r_cf(omega_eps0, eps_r_cf);
// ω ϵ₀ Rᵀ ϵᵣᵣ
MatrixProductCoefficient omega_eps0_Rt_eps_r_cf(Rt_cf, omega_eps0_eps_r_cf);
// |ϵᵣ|² = ϵᵣᵣϵᵣᵣ + ϵᵣᵢϵᵣᵢ
MatrixProductCoefficient eps_r_eps_r_cf(eps_r_cf, eps_r_cf);
MatrixProductCoefficient eps_i_eps_i_cf(eps_i_cf, eps_i_cf);
MatrixSumCoefficient abseps2_cf(eps_r_eps_r_cf, eps_i_eps_i_cf);
// ω² ϵ₀²
ConstantCoefficient alpha2_omega2_eps02(alpha*alpha*omega * omega * eps0 * eps0);
// ω² ϵ₀² |ϵᵣ|²
ScalarMatrixProductCoefficient omega2_eps02_abseps2_cf(omega*omega*eps0*eps0, abseps2_cf);
// 1
ConstantCoefficient one_cf(1.0);
// -1
ConstantCoefficient neg_one_cf(-1.0);
// - ω ϵ₀
ConstantCoefficient neg_omega_eps0_cf(neg_omega_eps0);
// - α ω ϵ₀
ConstantCoefficient neg_alpha_omega_eps0_cf(alpha * neg_omega_eps0);
// P B
ScalarMatrixProductCoefficient PB_r_cf(p_r_cf, B_cf);
ScalarMatrixProductCoefficient PB_i_cf(p_i_cf, B_cf);
// BB
MatrixProductCoefficient BB_cf(B_cf, B_cf);
// Pᵣ²
ProductCoefficient P2_r_cf(p_r_cf, p_r_cf);
// Pᵢ²
ProductCoefficient P2_i_cf(p_i_cf, p_i_cf);
// |P|²
SumCoefficient absP2_cf(P2_r_cf, P2_i_cf);
// |P|² B B
ScalarMatrixProductCoefficient absP2_BB_cf(absP2_cf, BB_cf);
// cᵢ
for (int i = 0; i<ndiffusionequations; i++)
{
zerovec[nattr-1] = cvals(i);
c_cf[i] = new PWConstCoefficient(zerovec);
neg_c_alpha_omega_eps0_cf[i] = new ProductCoefficient(-omega_eps0*alpha, *c_cf[i]);
c2_cf[i] = new ProductCoefficient(*c_cf[i], *c_cf[i]);
signed_c_cf[i] = new ProductCoefficient(csigns(i), *c_cf[i]);
signed_PB_r_cf[i] = new ScalarMatrixProductCoefficient(*signed_c_cf[i], PB_r_cf);
signed_PB_i_cf[i] = new ScalarMatrixProductCoefficient(*signed_c_cf[i], PB_i_cf);
signed_c_omega_eps_cf[i] = new ProductCoefficient(omega_eps0, *signed_c_cf[i]);
neg_signed_c_omega_eps_cf[i] = new ProductCoefficient(neg_omega_eps0, *signed_c_cf[i]);
// cᵢ P B R
signed_PBR_r_cf[i] = new MatrixProductCoefficient(*signed_PB_r_cf[i], R_cf);
signed_PBR_i_cf[i] = new MatrixProductCoefficient(*signed_PB_i_cf[i], R_cf);
// cᵢ² |P|² B B
c2_absP2BB_cf[i] = new ScalarMatrixProductCoefficient(*c2_cf[i], absP2_BB_cf);
neg_c_bvec_cf[i] = new ScalarVectorProductCoefficient(-1.0 * cvals(i), bvec_cf);
}
ProductCoefficient neg_c1_c2_cf(*signed_c_cf[0], *signed_c_cf[1]);
// -c₁ c₂ |P|² B B
ScalarMatrixProductCoefficient neg_c1_c2_absP2BB_cf(neg_c1_c2_cf, absP2_BB_cf);
MatrixProductCoefficient PB_r_eps_r_cf(PB_r_cf, eps_r_cf);
MatrixProductCoefficient PB_r_eps_i_cf(PB_r_cf, eps_i_cf);
MatrixProductCoefficient PB_i_eps_r_cf(PB_i_cf, eps_r_cf);
MatrixProductCoefficient PB_i_eps_i_cf(PB_i_cf, eps_i_cf);
Array<FiniteElementCollection *> trial_fecols;
Array<ParFiniteElementSpace *> trial_pfes;
Array<FiniteElementCollection *> test_fecols;
// L²(Ω)² space for E
trial_fecols.Append(new L2_FECollection(order-1, dim));
trial_pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last(), dim));
// L²(Ω) space for H
trial_fecols.Append(new L2_FECollection(order-1, dim));
trial_pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last()));
// L²(Ω)² space for J₁, Q₁, J₂, Q₂
for (int i = 0; i < 2*ndiffusionequations; ++i)
{
trial_fecols.Append(new L2_FECollection(order-1, dim));
trial_pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last(), dim));
}
// H⁻¹/²(Γₕ) space for Ê
trial_fecols.Append(new RT_Trace_FECollection(order-1, dim));
trial_pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last()));
// H¹/²(Γₕ) space for Ĥ
trial_fecols.Append(new H1_Trace_FECollection(order, dim));
trial_pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last()));
// H⁻¹/²(Γₕ)² space for Ĵ₁, Q̂₁, Ĵ₂, Q̂₂
for (int i = 0; i < 2*ndiffusionequations; ++i)
{
trial_fecols.Append(new RT_Trace_FECollection(order-1, dim));
trial_pfes.Append(new ParFiniteElementSpace(&pmesh, trial_fecols.Last(), dim));
}
Array<HYPRE_BigInt> tdofs(trial_pfes.Size());
for (int i = 0; i < trial_pfes.Size(); ++i)
{
tdofs[i] = trial_pfes[i]->GlobalTrueVSize();
if (Mpi::Root())
{
cout << "ParFiniteElementSpace " << ToString(TrialSpace(i)) << " has " << tdofs[i]
<< " true dofs." << endl;
}
}
if (Mpi::Root())
{
cout << "Total number of true dofs: " << tdofs.Sum() << endl;
}
// Test spaces
// H¹(Ω) space for F
test_fecols.Append(new H1_FECollection(order, dim));
// H(curl,Ω) space for W
test_fecols.Append(new ND_FECollection(order, dim));
// Test spaces for K₁, L₁, K₂, L₂
for (int i = 0; i < 2*ndiffusionequations; ++i)
{
test_fecols.Append(new H1_FECollection(order, dim));
}
ParComplexDPGWeakForm * a = new ParComplexDPGWeakForm(trial_pfes,test_fecols);
for (int i = 0; i < 2*ndiffusionequations; ++i)
{
a->SetTestFECollVdim(i+2,dim);
}
// Trial integrators
// i(ω μ₀ H, F)
a->AddTrialIntegrator(nullptr,
new MixedScalarMassIntegrator(omegamu_cf),
TrialSpace::H_space,TestSpace::F_space);
// (E, ∇×F)
a->AddTrialIntegrator(new TransposeIntegrator(new MixedCurlIntegrator(one_cf)),
nullptr,
TrialSpace::E_space,TestSpace::F_space);
// -i ω ϵ₀ ϵᵣ (E, W) = -i(ω ϵ₀ (ϵᵣᵣ+i ϵᵣᵢ) E, W)
// = (ω ϵ₀ ϵᵣᵢ + i (-ωϵ₀ϵᵣᵣ) ) E, W)
a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(omega_eps0_eps_i_cf)),
new TransposeIntegrator(new VectorFEMassIntegrator(neg_omega_eps0_eps_r_cf)),
TrialSpace::E_space,TestSpace::W_space);
// (H, ∇ × W)
a->AddTrialIntegrator(new TransposeIntegrator(new MixedCurlIntegrator(one_cf)),
nullptr,
TrialSpace::H_space, TestSpace::W_space);
VectorFunctionCoefficient b_cf(dim, bfunc);
ScalarVectorProductCoefficient neg_b_cf(-1.0,b_cf);
for (int i = 0; i < ndiffusionequations; i++)
{
// - α ω ϵ₀ (Jᵢ,W)
a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(neg_alpha_omega_eps0_cf)),
nullptr,
TrialSpace::J1_space + 2*i,TestSpace::W_space);
// (Qᵢ, - b ⋅ ∇ Kᵢ)
a->AddTrialIntegrator(new TransposeIntegrator(new DirectionalVectorGradientIntegrator(neg_b_cf)),
nullptr,
TrialSpace::Q1_space + 2*i,TestSpace::K1_space+ 2*i);
// (cᵢ Jᵢ, Kᵢ)
a->AddTrialIntegrator(new VectorMassIntegrator(*c_cf[i]),
nullptr,
TrialSpace::J1_space + 2*i,TestSpace::K1_space + 2*i);
// (Qᵢ, Lᵢ)
a->AddTrialIntegrator(new VectorMassIntegrator(one_cf),
nullptr,
TrialSpace::Q1_space + 2*i,TestSpace::L1_space + 2*i);
// (Jᵢ, - b ⋅ ∇Lᵢ)
a->AddTrialIntegrator(new TransposeIntegrator(new DirectionalVectorGradientIntegrator(neg_b_cf)),
nullptr,
TrialSpace::J1_space + 2*i,TestSpace::L1_space + 2*i);
// - c₁(P B E, K₁) = (- c₁ Pᵣ B E, K₁) + i (-c₁ Pᵢ B E, K₁)
// c₂(P B E, K₂) = ( c₂ Pᵣ B E, K₂) + i ( c₂ Pᵢ B E, K₂)
a->AddTrialIntegrator(new VectorMassIntegrator(*signed_PB_r_cf[i]),
new VectorMassIntegrator(*signed_PB_i_cf[i]),
TrialSpace::E_space,TestSpace::K1_space + 2*i);
// Trace integrators
// <Q̂ᵢ, Kᵢ>
a->AddTrialIntegrator(new VectorTraceIntegrator,nullptr,
TrialSpace::hatQ1_space + 2*i,TestSpace::K1_space + 2*i);
// <Ĵᵢ, Lᵢ>
a->AddTrialIntegrator(new VectorTraceIntegrator,nullptr,
TrialSpace::hatJ1_space + 2*i,TestSpace::L1_space + 2*i);
}
// Trace integrators
// < R Ê, F > (we include R in the variable)
a->AddTrialIntegrator(new TraceIntegrator,
nullptr,
TrialSpace::hatE_space,TestSpace::F_space);
//< Ĥ, W × n >
a->AddTrialIntegrator(new TangentTraceIntegrator,
nullptr,
TrialSpace::hatH_space,TestSpace::W_space);
// -------------------------------------------------------------------------------
// Test integrators
// -------------------------------------------------------------------------------
// (∇×F,∇×δF) = (R ∇ F, R ∇δF) = (Rᵀ R ∇F, ∇δF) = (∇F, ∇δF)
a->AddTestIntegrator(new DiffusionIntegrator(one_cf),
nullptr,
TestSpace::F_space, TestSpace::F_space);
// i(-ω ϵ₀ ϵᵣ ∇ × F, δW) = i(-ω ϵ₀ (ϵᵣᵣ + i ϵᵣᵢ) ∇ × F, δW)
// = (ω ϵ₀ ϵᵣᵢ ∇ × F, δW) + i(-ω ϵ₀ ϵᵣᵣ ∇ × F, δW)
// = (ω ϵ₀ ϵᵣᵢ R ∇ F, δW) + i(-ω ϵ₀ ϵᵣᵣ R ∇ F, δW)
a->AddTestIntegrator(new MixedVectorGradientIntegrator(omega_eps0_eps_i_R_cf),
new MixedVectorGradientIntegrator(neg_omega_eps0_eps_r_R_cf),
TestSpace::F_space, TestSpace::W_space);
for (int i = 0; i<ndiffusionequations; i++)
{
// (-c₁ P B ∇ × F, δK₁) = (-c₁ Pᵣ B ∇ × F, δK₁) + i (-c₁ Pᵢ B ∇ × F, δK₁)
// ( c₂ P B ∇ × F, δK₂) = ( c₂ Pᵣ B ∇ × F, δK₂) + i ( c₂ Pᵢ B ∇ × F, δK₂)
a->AddTestIntegrator(new MixedCurlIntegrator(*signed_PB_r_cf[i]),
new MixedCurlIntegrator(*signed_PB_i_cf[i]),
TestSpace::F_space, TestSpace::K1_space+ 2*i);
}
// i(ω ϵ₀ ϵᵣ⋆ W, ∇×δF) = i(ω ϵ₀ (ϵᵣᵣ - i ϵᵣᵢ) W, ∇×δF) (ϵᵣᵣ & ϵᵣᵢ are symmetric)
// = (ω ϵ₀ ϵᵣᵢ W, ∇×δF) + i(ω ϵ₀ ϵᵣᵣ W, ∇×δF)
// = (ω ϵ₀ Rᵀ ϵᵣᵢ W, ∇δF) + i(ω ϵ₀ Rᵀ ϵᵣᵣ W, ∇δF)
a->AddTestIntegrator(new TransposeIntegrator(new MixedVectorGradientIntegrator(omega_eps0_Rt_eps_i_cf)),
new TransposeIntegrator(new MixedVectorGradientIntegrator(omega_eps0_Rt_eps_r_cf)),
TestSpace::W_space, TestSpace::F_space);
// (ω² ϵ²₀ ϵᵣϵᵣ⋆ W, δ W) = (ω² ϵ²₀ |ϵᵣ|² W, δW)
a->AddTestIntegrator(new VectorFEMassIntegrator(omega2_eps02_abseps2_cf),
nullptr,
TestSpace::W_space, TestSpace::W_space);
// Pᵢ B ϵᵣᵣ - Pᵣ B ϵᵣᵢ
MatrixSumCoefficient PB_i_eps_r_minus_PB_r_eps_i_cf(PB_i_eps_r_cf, PB_r_eps_i_cf, 1.0, -1.0);
// Pᵣ B ϵᵣᵣ + Pᵢ B ϵᵣᵢ)
MatrixSumCoefficient PB_r_eps_r_plus_PB_i_eps_i_cf(PB_r_eps_r_cf, PB_i_eps_i_cf, 1.0, 1.0);
// ω ϵ₀ c₁ (Pᵢ B ϵᵣᵣ - Pᵣ B ϵᵣᵢ) (negsigned)
// -ω ϵ₀ c₂ (Pᵢ B ϵᵣᵣ - Pᵣ B ϵᵣᵢ) (negsigned)
// - ω ϵ₀ c₁ (Pᵣ B ϵᵣᵣ + Pᵢ B ϵᵣᵢ) (signed)
// ω ϵ₀ c₂ (Pᵣ B ϵᵣᵣ + Pᵢ B ϵᵣᵢ) (signed)
Array<MatrixCoefficient*> neg_signed_c_PB_i_eps_r_minus_PB_r_eps_i_cf(ndiffusionequations);
Array<MatrixCoefficient*> signed_c_PB_i_eps_r_minus_PB_r_eps_i_cf(ndiffusionequations);
Array<MatrixCoefficient*> signed_c_PB_r_eps_r_plus_PB_i_eps_i_cf(ndiffusionequations);
Array<MatrixCoefficient*> neg_signed_c_PB_r_eps_r_plus_PB_i_eps_i_cf(ndiffusionequations);
for (int i = 0; i<ndiffusionequations; i++)
{
neg_signed_c_PB_i_eps_r_minus_PB_r_eps_i_cf[i] = new ScalarMatrixProductCoefficient(*neg_signed_c_omega_eps_cf[i], PB_i_eps_r_minus_PB_r_eps_i_cf);
signed_c_PB_r_eps_r_plus_PB_i_eps_i_cf[i] = new ScalarMatrixProductCoefficient(*signed_c_omega_eps_cf[i], PB_r_eps_r_plus_PB_i_eps_i_cf);
// i(-ω ϵ₀c₁ P B ϵᵣ⋆ W, δK₁) = ω ϵ₀ c₁ (Pᵢ B ϵᵣᵣ - Pᵣ B ϵᵣᵢ) + i (-ωϵ₀ c₁ (Pᵣ B ϵᵣᵣ + Pᵢ B ϵᵣᵢ))
// i( ω ϵ₀c₂ P B ϵᵣ⋆ W, δK₂) = -ω ϵ₀ c₂ (Pᵢ B ϵᵣᵣ - Pᵣ B ϵᵣᵢ) + i (ωϵ₀ c₂ (Pᵣ B ϵᵣᵣ + Pᵢ B ϵᵣᵢ))
// TestSpace K1_space and K2_space (2,4)
TestSpace tspace = static_cast<TestSpace>(2*i+2);
a->AddTestIntegrator(new VectorFEMassIntegrator(*neg_signed_c_PB_i_eps_r_minus_PB_r_eps_i_cf[i]),
new VectorFEMassIntegrator(*signed_c_PB_r_eps_r_plus_PB_i_eps_i_cf[i]),
TestSpace::W_space, tspace);
signed_c_PB_i_eps_r_minus_PB_r_eps_i_cf[i] = new ScalarMatrixProductCoefficient(*signed_c_omega_eps_cf[i], PB_i_eps_r_minus_PB_r_eps_i_cf);
neg_signed_c_PB_r_eps_r_plus_PB_i_eps_i_cf[i] = new ScalarMatrixProductCoefficient(*neg_signed_c_omega_eps_cf[i], PB_r_eps_r_plus_PB_i_eps_i_cf);
// Note that P is scalar and also ϵᵣ B = B ϵᵣ
// i( ω ϵ₀c₁ ϵᵣ P̄ B K₁, δW) = i (K₁, ω ϵ₀ c₁ P B ϵᵣ⋆ δW)
// i(-ω ϵ₀c₂ ϵᵣ P̄ B K₂, δW) = i (K₂, -ω ϵ₀ c₂ P B ϵᵣ⋆ δW)
a->AddTestIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(*signed_c_PB_i_eps_r_minus_PB_r_eps_i_cf[i])),
new TransposeIntegrator(new VectorFEMassIntegrator(*neg_signed_c_PB_r_eps_r_plus_PB_i_eps_i_cf[i])),
tspace, TestSpace::W_space);
// (c²₁ P P̄ B B K₁, δK₁)
// (c²₂ P P̄ B B K₂, δK₂)
a->AddTestIntegrator(new VectorMassIntegrator(*c2_absP2BB_cf[i]),
nullptr,
tspace, tspace);
// (c₁²K₁, δK₁)
// (c₂²K₂, δK₂)
a->AddTestIntegrator(new VectorMassIntegrator(*c2_cf[i]),
nullptr,
tspace, tspace);
// (-c₁ α ω ϵ₀ W, δK₁)
// (-c₂ α ω ϵ₀ W, δK₂)
a->AddTestIntegrator(new VectorFEMassIntegrator(*neg_c_alpha_omega_eps0_cf[i]),
nullptr,
TestSpace::W_space, tspace);
// (-c₁ α ω ϵ₀ K₁, δW)
// (-c₂ α ω ϵ₀ K₂, δW)
a->AddTestIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(*neg_c_alpha_omega_eps0_cf[i])),
nullptr,
tspace, TestSpace::W_space);
// (-c₁ b⋅∇L₁, δK₁)
// (-c₂ b⋅∇L₂, δK₂)
TestSpace Kspace = static_cast<TestSpace>(2*i+2);
TestSpace Lspace = static_cast<TestSpace>(2*i+3);
a->AddTestIntegrator(new DirectionalVectorGradientIntegrator(*neg_c_bvec_cf[i]),
nullptr,
Lspace, Kspace);
// (K₁,-c₁ b ⋅ ∇δL₁)
// (K₂,-c₂ b ⋅ ∇δL₂)
a->AddTestIntegrator(new TransposeIntegrator(new DirectionalVectorGradientIntegrator(*neg_c_bvec_cf[i])),
nullptr,
Kspace, Lspace);
// (L₁, δL₁)
// (L₂, δL₂)
a->AddTestIntegrator(new VectorMassIntegrator(one_cf),
nullptr,
Lspace, Lspace);
// (b⋅ ∇L₁, b ⋅ ∇δL₁)
// (b⋅ ∇L₂, b ⋅ ∇δL₂)
a->AddTestIntegrator(new DirectionalVectorDiffusionIntegrator(bvec_cf),
nullptr, Lspace, Lspace);
// (b ⋅ ∇K₁, b ⋅ ∇δK₁)
// (b ⋅ ∇K₂, b ⋅ ∇δK₂)
a->AddTestIntegrator(new DirectionalVectorDiffusionIntegrator(bvec_cf),
nullptr, Kspace, Kspace);
//-(b ⋅ ∇K₁, δL₁)
//-(b ⋅ ∇K₂, δL₂)
a->AddTestIntegrator(new DirectionalVectorGradientIntegrator(neg_b_cf),
nullptr, Kspace, Lspace);
//(L₁, - b ⋅ ∇δK₁)
//(L₂, - b ⋅ ∇δK₂)
a->AddTestIntegrator(new TransposeIntegrator(new DirectionalVectorGradientIntegrator(neg_b_cf)),
nullptr, Lspace, Kspace);
// (α ω ϵ₀ b⋅ ∇L₁, δW)
// (α ω ϵ₀ b⋅ ∇L₂, δW)
a->AddTestIntegrator(new MixedDirectionalVectorGradientIntegrator(alpha_omega_eps0_bvec_cf),
nullptr,
Lspace, TestSpace::W_space);
// (W, α ω ϵ₀ b ⋅ ∇δL₁)
// (W, α ω ϵ₀ b ⋅ ∇δL₂)
a->AddTestIntegrator(new TransposeIntegrator(new MixedDirectionalVectorGradientIntegrator(alpha_omega_eps0_bvec_cf)),
nullptr,
TestSpace::W_space, Lspace);
}
// (-c₁ P̄ B K₁, ∇×δF) = (K₁, -c₁ P B ∇×δF) = (K₁, -c₁ Pᵣ B ∇×δF) + i (K₁, -c₁ Pᵢ B ∇×δF)
a->AddTestIntegrator(new TransposeIntegrator(new MixedCurlIntegrator(*signed_PB_r_cf[0])),
new TransposeIntegrator(new MixedCurlIntegrator(*signed_PB_i_cf[0])),
TestSpace::K1_space, TestSpace::F_space);
// (c₂ P̄ B K₂, ∇×δF) = (K₂, c₂ P B ∇×δF) = (K₂, c₂ Pᵣ B ∇×δF) + i (K₂, c₂ Pᵢ B ∇×δF)
a->AddTestIntegrator(new TransposeIntegrator(new MixedCurlIntegrator(*signed_PB_r_cf[1])),
new TransposeIntegrator(new MixedCurlIntegrator(*signed_PB_i_cf[1])),
TestSpace::K2_space, TestSpace::F_space);
// (- c₁c₂ P P̄ B B K₁, δK₂)
// (- c₁c₂ P P̄ B B K₂, δK₁)
a->AddTestIntegrator(new VectorMassIntegrator(neg_c1_c2_absP2BB_cf),
nullptr,
TestSpace::K1_space, TestSpace::K2_space);
a->AddTestIntegrator(new VectorMassIntegrator(neg_c1_c2_absP2BB_cf),
nullptr,
TestSpace::K2_space, TestSpace::K1_space);
// (∇ × W, ∇ × δW)
a->AddTestIntegrator(new CurlCurlIntegrator(one_cf),
nullptr,
TestSpace::W_space, TestSpace::W_space);
// i(ω μ₀ ∇ × W, δF)
a->AddTestIntegrator(nullptr,
new MixedCurlIntegrator(omegamu_cf),
TestSpace::W_space, TestSpace::F_space);
// i(-ω μ₀ F, ∇ ×δW)
a->AddTestIntegrator(nullptr,
new TransposeIntegrator(new MixedCurlIntegrator(neg_omegamu_cf)),
TestSpace::F_space, TestSpace::W_space);
// (ω² μ₀² F, δF)
a->AddTestIntegrator(new MassIntegrator(omega2mu2_cf),
nullptr,
TestSpace::F_space, TestSpace::F_space);
// (α² ω² ϵ₀² W, δW)
a->AddTestIntegrator(new VectorFEMassIntegrator(alpha2_omega2_eps02),
nullptr,
TestSpace::W_space,TestSpace::W_space);
// (F, δF)
a->AddTestIntegrator(new MassIntegrator(one_cf),
nullptr,
TestSpace::F_space,TestSpace::F_space);
// (W, δW)
a->AddTestIntegrator(new VectorFEMassIntegrator(one_cf),
nullptr,
TestSpace::W_space,TestSpace::W_space);
// (K₁, δK₁)
a->AddTestIntegrator(new VectorMassIntegrator(one_cf),
nullptr,
TestSpace::K1_space,TestSpace::K1_space);
// (L₁, δL₁)
a->AddTestIntegrator(new VectorMassIntegrator(one_cf),
nullptr,
TestSpace::L1_space,TestSpace::L1_space);
// (K₂, δK₂)
a->AddTestIntegrator(new VectorMassIntegrator(one_cf),
nullptr,
TestSpace::K2_space,TestSpace::K2_space);
// (L₂, δL₂)
a->AddTestIntegrator(new VectorMassIntegrator(one_cf),
nullptr,
TestSpace::L2_space,TestSpace::L2_space);
a->Assemble();
for (int i = 0; i<ndiffusionequations; i++)
{
delete c_cf[i];
delete signed_c_cf[i];
delete signed_PB_r_cf[i];
delete signed_PB_i_cf[i];
delete signed_PBR_r_cf[i];
delete signed_PBR_i_cf[i];
}
socketstream E_out_r;
int npfes = trial_pfes.Size();
Array<int> offsets(npfes+1); offsets[0] = 0;
Array<int> toffsets(npfes+1); toffsets[0] = 0;
for (int i = 0; i<npfes; i++)
{
offsets[i+1] = trial_pfes[i]->GetVSize();
toffsets[i+1] = trial_pfes[i]->TrueVSize();
}
offsets.PartialSum();
toffsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
Array<ParGridFunction *> pgf_r(npfes);
Array<ParGridFunction *> pgf_i(npfes);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i] = new ParGridFunction(trial_pfes[i], x, offsets[i]);
pgf_i[i] = new ParGridFunction(trial_pfes[i], x, offsets.Last() + offsets[i]);
}
L2_FECollection L2fec(order, dim);
ParFiniteElementSpace L2_fes(&pmesh, &L2fec);
ParGridFunction E_par_r(&L2_fes);
ParGridFunction E_par_i(&L2_fes);
ParaViewDataCollection * paraview_dc = nullptr;
std::string output_dir = "ParaView/UW/" + GetTimestamp();
if (paraview)
{
if (Mpi::Root()) { WriteParametersToFile(args, output_dir); }
std::ostringstream paraview_file_name;
std::string filename = GetFilename(mesh_file);
paraview_file_name << filename
<< "_par_ref_" << par_ref_levels
<< "_order_" << order;
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
paraview_dc->SetPrefixPath(output_dir);
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",pgf_r[0]);
paraview_dc->RegisterField("E_i",pgf_i[0]);
paraview_dc->RegisterField("E_par_r",&E_par_r);
paraview_dc->RegisterField("E_par_i",&E_par_i);
paraview_dc->RegisterField("H_r",pgf_r[1]);
paraview_dc->RegisterField("H_i",pgf_i[1]);
paraview_dc->RegisterField("J_1_r",pgf_r[2]);
paraview_dc->RegisterField("J_1_i",pgf_i[2]);
paraview_dc->RegisterField("Q_1_r",pgf_r[3]);
paraview_dc->RegisterField("Q_1_i",pgf_i[3]);
paraview_dc->RegisterField("J_2_r",pgf_r[4]);
paraview_dc->RegisterField("J_2_i",pgf_i[4]);
paraview_dc->RegisterField("Q_2_r",pgf_r[5]);
paraview_dc->RegisterField("Q_2_i",pgf_i[5]);
}
Array<int> ess_tdof_list;
Array<int> ess_tdof_listJhat;
Array<int> ess_bdr;
Array<int> one_r_bdr;
Array<int> one_i_bdr;
Array<int> negone_r_bdr;
Array<int> negone_i_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_r_bdr.SetSize(pmesh.bdr_attributes.Max());
one_i_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_r_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_i_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
// remove internal boundaries
for (int i = 0; i<int_bdr_attr.Size(); i++)
{
ess_bdr[int_bdr_attr[i]-1] = 0;
}
trial_pfes[2+2*ndiffusionequations]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
for (int j = 0; j < ess_tdof_list.Size(); j++)
{
ess_tdof_list[j] += toffsets[2+2*ndiffusionequations];
}
// ess_bdr=1;
for (int i = 0; i<ndiffusionequations;i++)
{
ess_tdof_listJhat.SetSize(0);
trial_pfes[2*i+8]->GetEssentialTrueDofs(ess_bdr, ess_tdof_listJhat);
for (int j = 0; j < ess_tdof_listJhat.Size(); j++)
{
ess_tdof_listJhat[j] += toffsets[2*i+8];
}
ess_tdof_list.Append(ess_tdof_listJhat);
}
one_r_bdr = 0; one_i_bdr = 0;
negone_r_bdr = 0; negone_i_bdr = 0;
// attr = 30,2 (real)
one_r_bdr[30-1] = 1; one_r_bdr[2-1] = 1;
// attr = 26,6 (imag)
one_i_bdr[26-1] = 1; one_i_bdr[6-1] = 1;
// attr = 22,10 (real)
negone_r_bdr[22-1] = 1; negone_r_bdr[10-1] = 1;
// attr = 18,14 (imag)
negone_i_bdr[18-1] = 1; negone_i_bdr[14-1] = 1;
}
// rotate the vector
// (x,y) -> (y,-x)
Vector rot_one_x(dim); rot_one_x = 0.0; rot_one_x(1) = -1.0;
Vector rot_negone_x(dim); rot_negone_x = 0.0; rot_negone_x(1) = 1.0;
VectorConstantCoefficient rot_one_x_cf(rot_one_x);
VectorConstantCoefficient rot_negone_x_cf(rot_negone_x);
pgf_r[2+2*ndiffusionequations]->ProjectBdrCoefficientNormal(rot_one_x_cf, one_r_bdr);
pgf_r[2+2*ndiffusionequations]->ProjectBdrCoefficientNormal(rot_negone_x_cf, negone_r_bdr);
pgf_i[2+2*ndiffusionequations]->ProjectBdrCoefficientNormal(rot_one_x_cf, one_i_bdr);
pgf_i[2+2*ndiffusionequations]->ProjectBdrCoefficientNormal(rot_negone_x_cf, negone_i_bdr);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
int nblocks = BlockA_r->NumRowBlocks();
{
ComplexBlockOperator Ac(*Ahc);
Vector Xc(X.Size()); Xc = 0.0;
Vector Bc(B.Size());
Ac.BlockComplexToComplexBlock(B, Bc);
BlockDiagonalPreconditioner Mc(Ac.RowOffsets());
for (int i = 0; i < nblocks; ++i)
{
auto solver = new ComplexMUMPSSolver(MPI_COMM_WORLD);
solver->SetPrintLevel(0);
solver->SetOperator(Ac.GetBlock(i,i));
Mc.SetDiagonalBlock(i, solver);
}
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-5);
cg.SetMaxIter(500);
cg.SetPrintLevel(1);
cg.SetPreconditioner(Mc);
cg.SetOperator(Ac);
cg.Mult(Bc, Xc);
Ac.ComplexBlockToBlockComplex(Xc, X);
}
a->RecoverFEMSolution(X, x);
for (int i = 0; i < npfes; ++i)
{
pgf_r[i]->MakeRef(trial_pfes[i], x, offsets[i]);
pgf_i[i]->MakeRef(trial_pfes[i], x, offsets.Last() + offsets[i]);
}
ParallelECoefficient par_e_r(pgf_r[0]);
ParallelECoefficient par_e_i(pgf_i[0]);
E_par_r.ProjectCoefficient(par_e_r);
E_par_i.ProjectCoefficient(par_e_i);
if (visualization)
{
const char * keys = nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, *pgf_r[0],
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(0);
paraview_dc->SetTime((real_t)0);
paraview_dc->Save();
delete paraview_dc;
}
delete a;
for (int i = 0; i < trial_fecols.Size(); ++i)
{
delete trial_fecols[i];
delete trial_pfes[i];
}
for (int i = 0; i< test_fecols.Size(); ++i)
{
delete test_fecols[i];
}
return 0;
}
+711
View File
@@ -0,0 +1,711 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
//
// MFEM Maxwell-Vdiffusion coupling parallel example
//
// Compile with: make lh-eld-weak
//
// mpirun -np 8 ./lh-eld-weak -o 3 -paraview -pr 0
// mpirun -np 8 ./lh-eld-weak -o 3 -paraview -pr 1 -sc
// Electron Landau Damping
// Weak Coupling Formulation:
// Strong formulation:
// ∇×(1/μ₀∇×E) - ω² ϵ₀ ϵ E = i ω²ϵ₀(J₁ + J₂), in Ω
// E×n = E₀, on ∂Ω
// - Δ∥ J₁ + c₁ J₁ = c₁ P(r) E∥, in Ω
// - Δ∥ J₂ + c₂ J₂ = -c₂ P(r) E∥, in Ω
// J₁ = 0, on ∂Ω
// J₂ = 0, on ∂Ω
// Find E ∈ H(curl,Ω), J₁ ∈ H¹(Ω), J₂ ∈ H¹(Ω) such that
// (1/μ₀ ∇×E, ∇ × F) - ω² ϵ₀ (ϵᵣ E, F) = -i ω²ϵ₀(J₁ + J₂, F), ∀ F ∈ H(curl,Ω)
// ( (b⋅∇)J₁,(b⋅∇) δJ₁ ) + c₁ (J₁,δJ₁) = c₁(P(r) b⊗b E, δJ₁), ∀ δJ₁ ∈ (H¹(Ω))²
// ( (b⋅∇)J₂,(b⋅∇) δJ₂ ) + c₂ (J₂,δJ₂) = -c₂(P(r) b⊗b E, δJ₂), ∀ δJ₂ ∈ (H¹(Ω))²
// J₁ = J₂ = 0, on ∂Ω
// --------------------------------------------------------------------------------
// | J₁ | J₂ | RHS |
// --------------------------------------------------------------------------------
// |δJ₁|((b⋅∇)J₁,(b⋅∇)δJ₁)+c₁(J₁,δJ₁)| | c₁(P(r)E,δJ₁)|
// | | | | |
// |δJ₂| |((b⋅∇)J₂,(b⋅∇)δJ₂)+ c₂(J₂,δJ₂)|-c₂(P(r)E,δJ₂)|
// where (δE,δH,δJ₁,δJ₂) ∈ H¹(Ω) × H(curl,Ω) × (H¹(Ω))² × (H¹(Ω))²
#include "mfem.hpp"
#include "../util/pcomplexweakform.hpp"
#include "../util/pcomplexblockform.hpp"
#include "../../common/mfem-common.hpp"
#include "../util/maxwell_utils.hpp"
#include "../util/utils.hpp"
#include "utils/lh_utils.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
using namespace mfem::common;
int main(int argc, char *argv[])
{
Mpi::Init();
Hypre::Init();
const char *mesh_file = "data/LH_hot.msh";
int order = 1;
int par_ref_levels = 0;
int ser_ref_levels = 0;
real_t rnum=1.5;
real_t mu = 1.257;
real_t eps0 = 8.8541878128;
real_t cfactor = 1e-6;
bool static_cond = false;
bool visualization = false;
bool paraview = false;
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(&ser_ref_levels, "-sr", "--serial-refinement_levels",
"Number of serial refinement levels.");
args.AddOption(&par_ref_levels, "-pr", "--parallel-refinement_levels",
"Number of parallel refinement levels.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&a0, "-a0", "--a0", "P(r) first parameter.");
args.AddOption(&a1, "-a1", "--a1", "P(r) second parameter.");
args.AddOption(&delta, "-delta", "--delta", "stability parameter.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (Mpi::Root())
{
args.PrintUsage(cout);
}
return 1;
}
if (Mpi::Root())
{
args.PrintOptions(cout);
}
// number of diffusion equations
int ndiffusionequations = 2;
Vector cvals(ndiffusionequations);
Vector csigns(ndiffusionequations);
cvals(0) = 25e6; cvals(1) = 1e6;
csigns(0) = 1.0; csigns(1) = -1.0;
cvals *= cfactor; // scale the coefficients
real_t omega = 2.*M_PI*rnum;
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
MFEM_VERIFY(dim == 2, "Dimension != 2 is not supported in this example");
for (int i = 0; i < ser_ref_levels; i++)
{
mesh.UniformRefinement();
}
Array<int> int_bdr_attr;
for (int i = 0; i < mesh.GetNBE(); i++)
{
if (mesh.FaceIsInterior(mesh.GetBdrElementFaceIndex(i)))
{
int_bdr_attr.Append(mesh.GetBdrAttribute(i));
}
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
for (int i = 0; i < par_ref_levels; i++)
{
pmesh.UniformRefinement();
}
int nattr = (pmesh.attributes.Size()) ? pmesh.attributes.Max() : 0;
Array<int> attr(nattr);
for (int i = 0; i<nattr; i++) { attr[i] = i+1; }
// Define coefficients
ConstantCoefficient muinv(1./mu);
ConstantCoefficient one_cf(1.0);
ConstantCoefficient negomegeps0_cf(-omega*eps0);
Vector zero(dim); zero = 0.0;
Vector one_x(dim); one_x = 0.0; one_x(0) = 1.0;
Vector negone_x(dim); negone_x = 0.0; negone_x(0) = -1.0;
VectorConstantCoefficient zero_vcf(zero);
VectorConstantCoefficient one_x_cf(one_x);
VectorConstantCoefficient negone_x_cf(negone_x);
DenseMatrix Mone(dim);
Mone = 0.0; Mone(0,0) = Mone(1,1) = 1.0;
MatrixConstantCoefficient Mone_cf(Mone);
DenseMatrix Mzero(dim); Mzero = 0.0;
MatrixConstantCoefficient Mzero_cf(Mzero);
Array<MatrixCoefficient*> coefs_r(nattr);
Array<MatrixCoefficient*> coefs_i(nattr);
for (int i = 0; i < nattr-1; ++i)
{
coefs_r[i] = &Mone_cf;
coefs_i[i] = &Mzero_cf;
}
// S(r)
FunctionCoefficient S_cf_r(sfunc_r), S_cf_i(sfunc_i);
// P(r)
FunctionCoefficient P_cf_r(pfunc_r), P_cf_i(pfunc_i);
VectorFunctionCoefficient b_cf(dim,bfunc);// b
ScalarVectorProductCoefficient scaled_b_cf(sqrt(cfactor), b_cf);
ConstantCoefficient diff_coeff(cfactor);
MatrixFunctionCoefficient bb_cf(dim,bcrossb); // b⊗b
MatrixSumCoefficient oneminusbb(Mone_cf, bb_cf, 1.0, -1.0); // 1 - b⊗b
// S(r) (I - b⊗b)
ScalarMatrixProductCoefficient Soneminusbb_r(S_cf_r, oneminusbb), Soneminusbb_i(S_cf_i, oneminusbb);
// P(r) b⊗b
ScalarMatrixProductCoefficient P_cf_bb_r(P_cf_r, bb_cf), P_cf_bb_i(P_cf_i, bb_cf);
// ε = S(r) (I - b⊗b) + P(r) b⊗b
MatrixSumCoefficient eps_r(Soneminusbb_r, P_cf_bb_r, 1.0, 1.0);
MatrixSumCoefficient eps_i(Soneminusbb_i, P_cf_bb_i, 1.0, 1.0);
coefs_r[nattr-1] = &eps_r;
coefs_i[nattr-1] = &eps_i;
PWMatrixCoefficient eps_cf_r(dim, attr, coefs_r);
PWMatrixCoefficient eps_cf_i(dim, attr, coefs_i);
ConstantCoefficient eps0omeg(omega * eps0);
ConstantCoefficient negeps0omeg(-omega * eps0);
ConstantCoefficient negeps0omeg2(-omega * omega * eps0);
ConstantCoefficient eps0omeg2(omega * omega * eps0);
ScalarMatrixProductCoefficient m_cf_r(negeps0omeg2, eps_cf_r);
ScalarMatrixProductCoefficient m_cf_i(negeps0omeg2, eps_cf_i);
// ω ϵ₀ ϵᵣ
ScalarMatrixProductCoefficient eps0omeg_eps_r(eps0omeg, eps_cf_r);
// ω ϵ₀ ϵᵢ
ScalarMatrixProductCoefficient eps0omeg_eps_i(eps0omeg, eps_cf_i);
// -ω ϵ₀ ϵᵣ
ScalarMatrixProductCoefficient negeps0omeg_eps_r(negeps0omeg, eps_cf_r);
// -ω ϵ₀ ϵᵢ
ScalarMatrixProductCoefficient negeps0omeg_eps_i(eps0omeg, eps_cf_i);
// A = [0 1; -1 0]
DenseMatrix rot_mat(2);
rot_mat(0,0) = 0.; rot_mat(0,1) = 1.;
rot_mat(1,0) = -1.; rot_mat(1,1) = 0.;
MatrixConstantCoefficient rot(rot_mat);
// ω ϵ₀ ϵᵣ A
MatrixProductCoefficient eps0omeg_eps_r_rot(eps0omeg_eps_r, rot);
// ω ϵ₀ ϵᵢ A
MatrixProductCoefficient eps0omeg_eps_i_rot(eps0omeg_eps_i, rot);
// -ω ϵ₀ ϵᵣ A
MatrixProductCoefficient negeps0omeg_eps_r_rot(negeps0omeg_eps_r, rot);
// -ω ϵ₀ ϵᵢ A
MatrixProductCoefficient negeps0omeg_eps_i_rot(negeps0omeg_eps_i, rot);
Array<Vector *> c_arrays(ndiffusionequations);
Array<PWConstCoefficient *> pw_c_coeffs(ndiffusionequations);
Array<MatrixCoefficient *> cPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> cPibb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPrbb_cf(ndiffusionequations);
Array<MatrixCoefficient *> signedcPibb_cf(ndiffusionequations);
Vector temp(nattr); temp=0.0;
Array<ConstantCoefficient *> c_coeffs(ndiffusionequations);
for (int i = 0; i<ndiffusionequations; i++)
{
temp[nattr-1] = cvals(i);
// temp = cvals(i);
pw_c_coeffs[i] = new PWConstCoefficient(temp);
c_coeffs[i] = new ConstantCoefficient(cvals(i));
cPrbb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_r);
cPibb_cf[i] = new ScalarMatrixProductCoefficient(*pw_c_coeffs[i], P_cf_bb_i);
signedcPrbb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPrbb_cf[i]);
signedcPibb_cf[i] = new ScalarMatrixProductCoefficient(csigns[i], *cPibb_cf[i]);
}
// Define the spaces
Array<FiniteElementCollection *> fem_fecols;
Array<ParFiniteElementSpace *> fem_pfes;
ND_FECollection *nd_fec = new ND_FECollection(order, dim);
ParFiniteElementSpace *nd_pfes = new ParFiniteElementSpace(&pmesh, nd_fec);
// Vector H1 spaces for Jᵢ
for (int i = 0; i < ndiffusionequations; i++)
{
fem_fecols.Append(new H1_FECollection(order, dim));
fem_pfes.Append(new ParFiniteElementSpace(&pmesh, fem_fecols.Last(), dim));
}
HYPRE_BigInt nd_tdofs = nd_pfes->GlobalTrueVSize();
if (Mpi::Root())
{
cout << "ND ParFiniteElementSpace has " << nd_tdofs
<< " true dofs." << endl;
}
Array<HYPRE_BigInt> fem_tdofs(fem_pfes.Size());
for (int i = 0; i < fem_pfes.Size(); ++i)
{
fem_tdofs[i] = fem_pfes[i]->GlobalTrueVSize();
if (Mpi::Root())
{
cout << "FEM ParFiniteElementSpace " << i << " has " << fem_tdofs[i]
<< " true dofs." << endl;
}
}
if (Mpi::Root())
{
cout << "Total number of ND true dofs for Maxwell: " << nd_tdofs << endl;
cout << "Total number of FEM true dofs for VDiffusion: " << fem_tdofs.Sum() << endl;
}
int nfem_pfes = fem_pfes.Size();
Array<int> fem_offsets(nfem_pfes+1); fem_offsets[0] = 0;
Array<int> fem_toffsets(nfem_pfes+1); fem_toffsets[0] = 0;
for (int i = 0; i<nfem_pfes; i++)
{
fem_offsets[i+1] = fem_pfes[i]->GetVSize();
fem_toffsets[i+1] = fem_pfes[i]->TrueVSize();
}
fem_offsets.PartialSum();
fem_toffsets.PartialSum();
Vector nd_x(2*nd_pfes->GetVSize());
nd_x = 0.;
Vector fem_x(2*fem_offsets.Last());
fem_x = 0.;
Vector fem_b(2*fem_offsets.Last());
fem_b = 0.;
ParGridFunction * nd_pgf_r = new ParGridFunction(nd_pfes, nd_x, 0);
ParGridFunction * nd_pgf_i = new ParGridFunction(nd_pfes, nd_x, nd_x.Size()/2);
Array<ParGridFunction *> fem_pgf_r(nfem_pfes);
Array<ParGridFunction *> fem_pgf_i(nfem_pfes);
Array<ParGridFunction *> old_fem_pgf_r(nfem_pfes);
Array<ParGridFunction *> old_fem_pgf_i(nfem_pfes);
for (int i = 0; i < nfem_pfes; ++i)
{
fem_pgf_r[i] = new ParGridFunction(fem_pfes[i], fem_x, fem_offsets[i]);
fem_pgf_i[i] = new ParGridFunction(fem_pfes[i], fem_x, fem_offsets.Last() + fem_offsets[i]);
old_fem_pgf_r[i] = new ParGridFunction(fem_pfes[i]); *old_fem_pgf_r[i] = 0.0;
old_fem_pgf_i[i] = new ParGridFunction(fem_pfes[i]); *old_fem_pgf_i[i] = 0.0;
}
L2_FECollection L2fec(order, dim);
ParFiniteElementSpace L2_fes(&pmesh, &L2fec);
ParGridFunction E_par_r(&L2_fes);
ParGridFunction E_par_i(&L2_fes);
ParaViewDataCollection * paraview_dc = nullptr;
std::string output_dir = "ParaView/FEM/WeakCoupling/" + GetTimestamp();
if (paraview)
{
if (Mpi::Root()) { WriteParametersToFile(args, output_dir); }
std::ostringstream paraview_file_name;
std::string filename = GetFilename(mesh_file);
paraview_file_name << filename
<< "_par_ref_" << par_ref_levels
<< "_order_" << order;
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
paraview_dc->SetPrefixPath(output_dir);
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",nd_pgf_r);
paraview_dc->RegisterField("E_i",nd_pgf_i);
paraview_dc->RegisterField("E_par_r",&E_par_r);
paraview_dc->RegisterField("E_par_i",&E_par_i);
paraview_dc->RegisterField("Jh_1_r",fem_pgf_r[0]);
paraview_dc->RegisterField("Jh_1_i",fem_pgf_i[0]);
paraview_dc->RegisterField("Jh_2_r",fem_pgf_r[1]);
paraview_dc->RegisterField("Jh_2_i",fem_pgf_i[1]);
paraview_dc->Save();
}
Array<int> ess_tdof_list;
Array<int> ess_tdof_listJ;
Array<int> ess_bdr;
Array<int> one_r_bdr;
Array<int> one_i_bdr;
Array<int> negone_r_bdr;
Array<int> negone_i_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_r_bdr.SetSize(pmesh.bdr_attributes.Max());
one_i_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_r_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_i_bdr.SetSize(pmesh.bdr_attributes.Max());
one_r_bdr = 0; one_i_bdr = 0;
negone_r_bdr = 0; negone_i_bdr = 0;
// attr = 30,2 (real)
one_r_bdr[30-1] = 1; one_r_bdr[2-1] = 1;
// attr = 26,6 (imag)
one_i_bdr[26-1] = 1; one_i_bdr[6-1] = 1;
// attr = 22,10 (real)
negone_r_bdr[22-1] = 1; negone_r_bdr[10-1] = 1;
// attr = 18,14 (imag)
negone_i_bdr[18-1] = 1; negone_i_bdr[14-1] = 1;
}
int max_fixed_point_iter = 50;
// loop through fixed point iterations
for (int k = 0; k<max_fixed_point_iter; k++)
{
nd_x = 0.0;
// reset GridFunctions
*nd_pgf_r = 0.0;
*nd_pgf_i = 0.0;
delta = (k == 0) ? 0.01 : 0.01;
// delta = 0.01 * pow(0.5, 0.0);
delta = 0.01 * pow(0.9, k);
ParSesquilinearForm a_nd(nd_pfes);
// (1/μ₀ ∇×E, ∇ × F)
a_nd.AddDomainIntegrator(new CurlCurlIntegrator(muinv), nullptr);
// - ω² ϵ₀ (ϵᵣ E, F)
a_nd.AddDomainIntegrator(new VectorFEMassIntegrator(m_cf_r),
new VectorFEMassIntegrator(m_cf_i));
a_nd.Assemble();
VectorGridFunctionCoefficient J1_cf_r(fem_pgf_r[0]);
VectorGridFunctionCoefficient J1_cf_i(fem_pgf_i[0]);
VectorGridFunctionCoefficient J2_cf_r(fem_pgf_r[1]);
VectorGridFunctionCoefficient J2_cf_i(fem_pgf_i[1]);
// -iω²ϵ₀ (Jᵣ + i Jᵢ ,F) = ω² ϵ₀ (Jᵢ - i Jᵣ,F)
ScalarVectorProductCoefficient negomeg2eps0_J1_cf_i(negeps0omeg2, J1_cf_i);
ScalarVectorProductCoefficient negomeg2eps0_J2_cf_i(negeps0omeg2, J2_cf_i);
ScalarVectorProductCoefficient omeg2eps0_J1_cf_i(eps0omeg2, J1_cf_i);
ScalarVectorProductCoefficient omeg2eps0_J2_cf_i(eps0omeg2, J2_cf_i);
ScalarVectorProductCoefficient negomeg2eps0_J1_cf_r(negeps0omeg2, J1_cf_r);
ScalarVectorProductCoefficient negomeg2eps0_J2_cf_r(negeps0omeg2, J2_cf_r);
ScalarVectorProductCoefficient omeg2eps0_J1_cf_r(eps0omeg2, J1_cf_r);
ScalarVectorProductCoefficient omeg2eps0_J2_cf_r(eps0omeg2, J2_cf_r);
ParComplexLinearForm nd_b(nd_pfes);
// ω² ϵ₀ (Jᵢ - i Jᵣ,F) = (ω² ϵ₀ Jᵢ, F) + i (-ω² ϵ₀Jᵢ,F)
// nd_b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(omeg2eps0_J1_cf_i),
// new VectorFEDomainLFIntegrator(negomeg2eps0_J1_cf_r));
// nd_b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(omeg2eps0_J2_cf_i),
// new VectorFEDomainLFIntegrator(negomeg2eps0_J2_cf_r));
ScalarVectorProductCoefficient omeg2_eps0_J1_cf_i(eps0*omega*omega, J1_cf_i);
ScalarVectorProductCoefficient omeg2_eps0_J2_cf_i(eps0*omega*omega, J2_cf_i);
ScalarVectorProductCoefficient negomeg2_eps0_J1_cf_r(-eps0*omega*omega, J1_cf_r);
ScalarVectorProductCoefficient negomeg2_eps0_J2_cf_r(-eps0*omega*omega, J2_cf_r);
nd_b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(omeg2_eps0_J1_cf_i),
new VectorFEDomainLFIntegrator(negomeg2_eps0_J1_cf_r));
nd_b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(omeg2_eps0_J2_cf_i),
new VectorFEDomainLFIntegrator(negomeg2_eps0_J2_cf_r));
// nd_b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(negomeg2eps0_J1_cf_i), nullptr);
// ConstantCoefficient temp(1.0);
// nd_b.AddDomainIntegrator(nullptr,new VectorFEDomainLFIntegrator(J1_cf_r));
// nd_b.AddDomainIntegrator(nullptr,new VectorFEDomainLFIntegrator(omeg2eps0_J2_cf_r));
nd_b.Assemble();
// remove internal boundaries
ess_bdr = 1;
for (int i = 0; i<int_bdr_attr.Size(); i++)
{
ess_bdr[int_bdr_attr[i]-1] = 0;
}
nd_pfes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// if (k == 0)
// {
nd_pgf_r->ProjectBdrCoefficientTangent(one_x_cf, one_r_bdr);
nd_pgf_r->ProjectBdrCoefficientTangent(negone_x_cf, negone_r_bdr);
nd_pgf_i->ProjectBdrCoefficientTangent(one_x_cf, one_i_bdr);
nd_pgf_i->ProjectBdrCoefficientTangent(negone_x_cf, negone_i_bdr);
// }
OperatorPtr nd_Ah;
Vector nd_X,nd_B;
a_nd.FormLinearSystem(ess_tdof_list,nd_x,nd_b,nd_Ah, nd_X,nd_B);
ComplexHypreParMatrix *nd_A = nd_Ah.As<ComplexHypreParMatrix>();
ComplexMUMPSSolver nd_mumps(MPI_COMM_WORLD);
nd_mumps.SetPrintLevel(0);
nd_mumps.SetOperator(*nd_A);
nd_mumps.Mult(nd_B, nd_X);
a_nd.RecoverFEMSolution(nd_X, nd_b, nd_x);
nd_pgf_r->MakeRef(nd_pfes, nd_x, 0);
nd_pgf_i->MakeRef(nd_pfes, nd_x, nd_x.Size()/2);
fem_x = 0.0;
fem_b = 0.0;
for (int i = 0; i < nfem_pfes; ++i)
{
*fem_pgf_r[i] = 0.0;
*fem_pgf_i[i] = 0.0;
}
// Diffusion equations
ParComplexBlockForm a_fem(fem_pfes);
for (int i = 0; i < ndiffusionequations; i++)
{
// ((b⋅∇)Jᵢ, (b⋅∇) δJᵢ)
a_fem.AddDomainIntegrator(new DirectionalVectorDiffusionIntegrator(scaled_b_cf),
nullptr,i,i);
// cᵢ(Jᵢ, δJᵢ)
a_fem.AddDomainIntegrator(new VectorMassIntegrator(*pw_c_coeffs[i]),
nullptr,i,i);
}
// FEM RHS
// ±cᵢ(P(r) (b ⊗ b) E, δJᵢ)
real_t * fembdata = fem_b.GetData();
ParLinearForm b_J1_r(fem_pfes[0],fembdata);
ParLinearForm b_J2_r(fem_pfes[1],fembdata + fem_offsets[1]);
ParLinearForm b_J1_i(fem_pfes[0],fembdata + fem_offsets.Last());
ParLinearForm b_J2_i(fem_pfes[1],fembdata + fem_offsets.Last() + fem_offsets[1]);
VectorGridFunctionCoefficient E_r_cf(nd_pgf_r);
VectorGridFunctionCoefficient E_i_cf(nd_pgf_i);
// (a + i b) * (c + i d) = (ac - bd) + i (ad + bc)
MatrixVectorProductCoefficient c1_cf_rr(*signedcPrbb_cf[0], E_r_cf);
MatrixVectorProductCoefficient c1_cf_ii(*signedcPibb_cf[0], E_i_cf);
MatrixVectorProductCoefficient c1_cf_ri(*signedcPrbb_cf[0], E_i_cf);
MatrixVectorProductCoefficient c1_cf_ir(*signedcPibb_cf[0], E_r_cf);
VectorSumCoefficient c1_cf_r(c1_cf_rr, c1_cf_ii, 1.0, -1.0);
VectorSumCoefficient c1_cf_i(c1_cf_ri, c1_cf_ir, 1.0, 1.0);
MatrixVectorProductCoefficient c2_cf_rr(*signedcPrbb_cf[1], E_r_cf);
MatrixVectorProductCoefficient c2_cf_ii(*signedcPibb_cf[1], E_i_cf);
MatrixVectorProductCoefficient c2_cf_ri(*signedcPrbb_cf[1], E_i_cf);
MatrixVectorProductCoefficient c2_cf_ir(*signedcPibb_cf[1], E_r_cf);
VectorSumCoefficient c2_cf_r(c2_cf_rr, c2_cf_ii, 1.0, -1.0);
VectorSumCoefficient c2_cf_i(c2_cf_ri, c2_cf_ir, 1.0, 1.0);
b_J1_r.AddDomainIntegrator(new VectorDomainLFIntegrator(c1_cf_r));
b_J1_i.AddDomainIntegrator(new VectorDomainLFIntegrator(c1_cf_i));
b_J2_r.AddDomainIntegrator(new VectorDomainLFIntegrator(c2_cf_r));
b_J2_i.AddDomainIntegrator(new VectorDomainLFIntegrator(c2_cf_i));
b_J1_r.Assemble();
b_J1_i.Assemble();
b_J2_r.Assemble();
b_J2_i.Assemble();
a_fem.Assemble();
ess_bdr=1;
ess_tdof_list.SetSize(0);
for (int i = 0; i<ndiffusionequations;i++)
{
ess_tdof_listJ.SetSize(0);
fem_pfes[i]->GetEssentialTrueDofs(ess_bdr, ess_tdof_listJ);
for (int j = 0; j < ess_tdof_listJ.Size(); j++)
{
ess_tdof_listJ[j] += fem_toffsets[i];
}
ess_tdof_list.Append(ess_tdof_listJ);
}
OperatorPtr fem_Ah;
Vector fem_X,fem_B;
a_fem.FormLinearSystem(ess_tdof_list,fem_x,fem_b,fem_Ah, fem_X,fem_B);
ComplexOperator * fem_Ahc = fem_Ah.As<ComplexOperator>();
BlockOperator * fem_BlockA_r = dynamic_cast<BlockOperator *>(&fem_Ahc->real());
BlockOperator * fem_BlockA_i = dynamic_cast<BlockOperator *>(&fem_Ahc->imag());
int fem_nblocks = fem_BlockA_r->NumRowBlocks();
Array2D<const HypreParMatrix*> fem_A_r_matrices(fem_nblocks, fem_nblocks);
Array2D<const HypreParMatrix*> fem_A_i_matrices(fem_nblocks, fem_nblocks);
for (int i = 0; i < fem_nblocks; i++)
{
for (int j = 0; j < fem_nblocks; j++)
{
fem_A_r_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&fem_BlockA_r->GetBlock(i,j));
fem_A_i_matrices(i,j) = dynamic_cast<HypreParMatrix*>(&fem_BlockA_i->GetBlock(i,j));
}
}
HypreParMatrix * fem_Ahr = HypreParMatrixFromBlocks(fem_A_r_matrices);
HypreParMatrix * fem_Ahi = HypreParMatrixFromBlocks(fem_A_i_matrices);
ComplexHypreParMatrix * fem_Ahc_hypre =
new ComplexHypreParMatrix(fem_Ahr, fem_Ahi,false, false);
if (Mpi::Root())
{
mfem::out << "FEM Assembly finished successfully." << endl;
}
Array<int> fem_tdof_offsets(2*fem_nblocks+1);
fem_tdof_offsets[0] = 0;
for (int i=0; i<fem_nblocks; i++)
{
fem_tdof_offsets[i+1] = fem_A_r_matrices(i,i)->Height();
fem_tdof_offsets[fem_nblocks+i+1] = fem_tdof_offsets[i+1];
}
fem_tdof_offsets.PartialSum();
BlockDiagonalPreconditioner fem_M(fem_tdof_offsets);
HypreBoomerAMG * solver_J1 = new HypreBoomerAMG((HypreParMatrix &)
fem_BlockA_r->GetBlock(0,0));
solver_J1->SetPrintLevel(0);
solver_J1->SetSystemsOptions(dim);
HypreBoomerAMG * solver_J2 = new HypreBoomerAMG((HypreParMatrix &)
fem_BlockA_r->GetBlock(1,1));
solver_J2->SetPrintLevel(0);
fem_M.SetDiagonalBlock(0,solver_J1);
fem_M.SetDiagonalBlock(1,solver_J2);
fem_M.SetDiagonalBlock(fem_nblocks,solver_J1);
fem_M.SetDiagonalBlock(fem_nblocks+1,solver_J2);
CGSolver fem_cg(MPI_COMM_WORLD);
fem_cg.SetRelTol(1e-16);
fem_cg.SetMaxIter(1000);
fem_cg.SetPrintLevel(1);
fem_cg.SetPreconditioner(fem_M);
fem_cg.SetOperator(*fem_Ahc_hypre);
fem_cg.Mult(fem_B, fem_X);
a_fem.RecoverFEMSolution(fem_X, fem_x);
real_t alpha = (k == 0) ? 1.0 : 0.01;
for (int i = 0; i < nfem_pfes; ++i)
{
fem_pgf_r[i]->MakeRef(fem_pfes[i], fem_x, fem_offsets[i]);
fem_pgf_i[i]->MakeRef(fem_pfes[i], fem_x, fem_offsets.Last() + fem_offsets[i]);
(*fem_pgf_r[i])*= alpha;
(*fem_pgf_i[i])*= alpha;
fem_pgf_r[i]->Add(1.0-alpha, *old_fem_pgf_r[i]);
fem_pgf_i[i]->Add(1.0-alpha, *old_fem_pgf_i[i]);
*old_fem_pgf_r[i] = *fem_pgf_r[i];
*old_fem_pgf_i[i] = *fem_pgf_i[i];
}
ParallelECoefficient par_e_r(nd_pgf_r);
ParallelECoefficient par_e_i(nd_pgf_i);
E_par_r.ProjectCoefficient(par_e_r);
E_par_i.ProjectCoefficient(par_e_i);
if (paraview)
{
paraview_dc->SetCycle(k);
paraview_dc->SetTime((real_t)k);
paraview_dc->Save();
}
}
for (int i = 0; i<ndiffusionequations; i++)
{
delete pw_c_coeffs[i];
delete c_coeffs[i];
delete cPrbb_cf[i];
delete cPibb_cf[i];
delete signedcPrbb_cf[i];
delete signedcPibb_cf[i];
}
delete nd_fec;
delete nd_pfes;
for (int i = 0; i < fem_fecols.Size(); ++i)
{
delete fem_fecols[i];
delete fem_pfes[i];
}
if (paraview_dc)
{
delete paraview_dc;
}
return 0;
}
+479
View File
@@ -0,0 +1,479 @@
// srun -n 256 ./pmaxwell-primal-tokamak -o 3 -sc -rnum
// srun -n 448 ./pmaxwell-primal-tokamak -o 4 -sc -rnum 11.0 -paraview
// srun -n 448 ./pmaxwell-primal-tokamak -o 4 -do 0 -sc -paraview (with the new epsilon GridFunction coefficients)
// Description:
// This example code demonstrates the use of MFEM to define and solve
// the "ultraweak" (UW) DPG formulation for the Maxwell problem
// ∇×(1/μ ∇×E) - ω^2 ϵ₀ ϵ E = 0 , in Ω
// E×n = E_0, on ∂Ω
// The primal-DPG formulation is obtained by integration by parts
// and the introduction of trace unknowns on the mesh skeleton
// in 3D
// E ∈ H(curl)
// Ê ∈ H_0^1/2(Ω)(curl, Γ_h)
// 1/μ (∇×E , ∇×F) + (ω^2 ϵ₀ ϵ , F) + <Ê , F × n> = 0, ∀ F ∈ H(curl,Ω)
// Ê × n = E_0 on ∂Ω
#include "mfem.hpp"
#include "../util/utils.hpp"
#include "../util/maxwell_utils.hpp"
#include "utils/lh_utils.hpp"
#include "../util/pcomplexweakform.hpp"
#include "../../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "data/LH_hot.msh";
int order = 2;
int delta_order = 1;
int par_ref_levels = 0;
int amr_ref_levels = 0;
// real_t rnum=4.6e9;
// real_t mu = 1.257e-6/factor;
// real_t epsilon_scale = 8.8541878128e-12*factor;
real_t rnum=1.5;
real_t mu = 1.257;
real_t eps0 = 8.8541878128;
bool mumps_solver = false;
real_t theta = 0.0;
bool visualization = false;
bool static_cond = false;
bool paraview = false;
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(&par_ref_levels, "-pr", "--parallel-refinement-levels",
"Number of parallel refinement levels.");
args.AddOption(&amr_ref_levels, "-amr", "--parallel-amr-refinement-levels",
"Number of parallel AMR refinement levels.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&delta_order, "-do", "--delta_order",
"Order enrichment for DPG test space.");
args.AddOption(&theta, "-theta", "--theta",
"Theta parameter for AMR");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
#ifdef MFEM_USE_MUMPS
args.AddOption(&mumps_solver, "-mumps", "--mumps-solver", "-no-mumps",
"--no-mumps-solver", "Use the MUMPS Solver.");
#endif
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
double omega = 2.*M_PI*rnum;
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
mesh.RemoveInternalBoundaries();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
for (int i = 0; i<par_ref_levels; i++)
{
pmesh.UniformRefinement();
}
FiniteElementCollection *E_fec = new ND_FECollection(order,dim);
ParFiniteElementSpace *E_fes = new ParFiniteElementSpace(&pmesh,E_fec);
// H^-1/2 (curl) space for Ê (in 2D H1 trace, in 3D ND trace)
int test_order = order+delta_order;
FiniteElementCollection * hatE_fec = new H1_Trace_FECollection(order,dim);
FiniteElementCollection * F_fec = new ND_FECollection(test_order, dim);
ParFiniteElementSpace *hatE_fes = new ParFiniteElementSpace(&pmesh,hatE_fec);
Array<ParFiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(E_fes);
trial_fes.Append(hatE_fes);
test_fec.Append(F_fec);
int gdofs = 0;
for (int i = 0; i<trial_fes.Size(); i++)
{
gdofs += trial_fes[i]->GlobalTrueVSize();
}
if (Mpi::Root())
{
mfem::out << "Global number of dofs = " << gdofs << endl;
}
// Define the coefficients
ConstantCoefficient muinv(1./mu);
ConstantCoefficient one(1.0);
int nattr = (pmesh.attributes.Size()) ? pmesh.attributes.Max() : 0;
Array<int> attr(nattr);
for (int i = 0; i<nattr; i++) { attr[i] = i+1; }
Vector zero(dim); zero = 0.0;
Vector one_x(dim); one_x = 0.0; one_x(0) = 1.0;
Vector negone_x(dim); negone_x = 0.0; negone_x(0) = -1.0;
VectorConstantCoefficient zero_vcf(zero);
VectorConstantCoefficient one_x_cf(one_x);
VectorConstantCoefficient negone_x_cf(negone_x);
DenseMatrix Mone(dim);
Mone = 0.0; Mone(0,0) = Mone(1,1) = 1.0;
MatrixConstantCoefficient Mone_cf(Mone);
DenseMatrix Mzero(dim); Mzero = 0.0;
MatrixConstantCoefficient Mzero_cf(Mzero);
Array<MatrixCoefficient*> coefs_r(nattr);
Array<MatrixCoefficient*> coefs_i(nattr);
for (int i = 0; i < nattr-1; ++i)
{
coefs_r[i] = &Mone_cf;
coefs_i[i] = &Mzero_cf;
}
// S(r)
FunctionCoefficient S_cf_r(sfunc_r), S_cf_i(sfunc_i);
// P(r)
FunctionCoefficient P_cf_r(pfunc_r), P_cf_i(pfunc_i);
VectorFunctionCoefficient b_cf(dim,bfunc);// b
MatrixFunctionCoefficient bb_cf(dim,bcrossb); // b⊗b
MatrixSumCoefficient oneminusbb(Mone_cf, bb_cf, 1.0, -1.0); // 1 - b⊗b
// S(r) (I - b⊗b)
ScalarMatrixProductCoefficient Soneminusbb_r(S_cf_r, oneminusbb), Soneminusbb_i(S_cf_i, oneminusbb);
// P(r) b⊗b
ScalarMatrixProductCoefficient P_cf_bb_r(P_cf_r, bb_cf), P_cf_bb_i(P_cf_i, bb_cf);
// εᵣ = S(r) (I - b⊗b) + P(r) b⊗b
MatrixSumCoefficient eps_r(Soneminusbb_r, P_cf_bb_r, 1.0, 1.0);
MatrixSumCoefficient eps_i(Soneminusbb_i, P_cf_bb_i, 1.0, 1.0);
coefs_r[nattr-1] = &eps_r;
coefs_i[nattr-1] = &eps_i;
PWMatrixCoefficient eps_cf_r(dim, attr, coefs_r);
PWMatrixCoefficient eps_cf_i(dim, attr, coefs_i);
ConstantCoefficient negeps0omeg2(-eps0 * omega * omega);
ScalarMatrixProductCoefficient m_cf_r(negeps0omeg2, eps_cf_r);
ScalarMatrixProductCoefficient m_cf_i(negeps0omeg2, eps_cf_i);
ParComplexDPGWeakForm * a = new ParComplexDPGWeakForm(trial_fes,test_fec);
// (1/μ₀ ∇ × E,∇ × F)
a->AddTrialIntegrator(new CurlCurlIntegrator(muinv), nullptr,0,0);
// -(ω^2 ϵ₀ ϵ, F)
const IntegrationRule *irs[Geometry::NumGeom];
int order_quad = 2*order + 2;
for (int i = 0; i < Geometry::NumGeom; ++i)
{
irs[i] = &(IntRules.Get(i, order_quad));
}
const IntegrationRule &ir = IntRules.Get(pmesh.GetElementGeometry(0),
2*test_order + 2);
VectorFEMassIntegrator * integ_r = new VectorFEMassIntegrator(m_cf_r);
VectorFEMassIntegrator * integ_i = new VectorFEMassIntegrator(m_cf_i);
integ_r->SetIntegrationRule(ir);
integ_i->SetIntegrationRule(ir);
a->AddTrialIntegrator(integ_r,integ_i,0,0);
// < n×Ê,F>
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,1,0);
// test integrators
// (∇×F ,∇× δF)
a->AddTestIntegrator(new CurlCurlIntegrator(one),nullptr,0,0);
// (F,δF)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,0,0);
socketstream E_out_r;
socketstream E_theta_out_r;
ParComplexGridFunction E_gf(E_fes);
E_gf.real() = 0.0;
E_gf.imag() = 0.0;
L2_FECollection L2fec(order, dim);
ParFiniteElementSpace L2_fes(&pmesh, &L2fec);
ParGridFunction E_theta_r(&L2_fes);
ParGridFunction E_theta_i(&L2_fes);
ParGridFunction E_theta(&L2_fes);
E_theta = 0.0;
ParaViewDataCollection * paraview_dc = nullptr;
// Create ParaView directory and file
std::string output_dir = "ParaView/PrimalDPG/" + GetTimestamp();
if (Mpi::Root())
{
WriteParametersToFile(args, output_dir);
}
if (paraview)
{
std::ostringstream paraview_file_name;
std::string filename = GetFilename(mesh_file);
paraview_file_name << filename
<< "_par_ref_" << par_ref_levels
<< "_order_" << order;
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
paraview_dc->SetPrefixPath(output_dir);
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",&E_gf.real());
paraview_dc->RegisterField("E_i",&E_gf.imag());
paraview_dc->RegisterField("E_theta_r",&E_theta_r);
paraview_dc->RegisterField("E_theta_i",&E_theta_i);
}
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
Array<int> ess_tdof_list;
Array<int> ess_bdr;
Array<int> one_r_bdr;
Array<int> one_i_bdr;
Array<int> negone_r_bdr;
Array<int> negone_i_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_r_bdr.SetSize(pmesh.bdr_attributes.Max());
one_i_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_r_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_i_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
E_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
one_r_bdr = 0; one_i_bdr = 0;
negone_r_bdr = 0; negone_i_bdr = 0;
// attr = 30,2 (real)
one_r_bdr[30-1] = 1; one_r_bdr[2-1] = 1;
// attr = 26,6 (imag)
one_i_bdr[26-1] = 1; one_i_bdr[6-1] = 1;
// attr = 22,10 (real)
negone_r_bdr[22-1] = 1; negone_r_bdr[10-1] = 1;
// attr = 18,14 (imag)
negone_i_bdr[18-1] = 1; negone_i_bdr[14-1] = 1;
}
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = E_fes->GetVSize();
offsets[2] = hatE_fes->GetVSize();
offsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
double * xdata = x.GetData();
E_gf.real().MakeRef(E_fes,&xdata[0]);
E_gf.imag().MakeRef(E_fes,&xdata[offsets.Last()]);
E_gf.ProjectBdrCoefficientTangent(one_x_cf,zero_vcf, one_r_bdr);
E_gf.ProjectBdrCoefficientTangent(negone_x_cf,zero_vcf, negone_r_bdr);
E_gf.ProjectBdrCoefficientTangent(zero_vcf,one_x_cf, one_i_bdr);
E_gf.ProjectBdrCoefficientTangent(zero_vcf,negone_x_cf, negone_i_bdr);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
int num_blocks = BlockA_r->NumRowBlocks();
Array<int> tdof_offsets(2*num_blocks+1);
tdof_offsets[0] = 0;
for (int i=0; i<num_blocks; i++)
{
const int h = BlockA_r->GetBlock(i,i).Height();
tdof_offsets[i+1] = h;
tdof_offsets[num_blocks+i+1] = h;
}
tdof_offsets.PartialSum();
BlockOperator blockA(tdof_offsets);
for (int i = 0; i<num_blocks; i++)
{
for (int j = 0; j<num_blocks; j++)
{
blockA.SetBlock(i,j,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i,j+num_blocks,&BlockA_i->GetBlock(i,j), -1.0);
blockA.SetBlock(i+num_blocks,j+num_blocks,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i+num_blocks,j,&BlockA_i->GetBlock(i,j));
}
}
#ifdef MFEM_USE_MUMPS
if (mumps_solver)
{
// Monolithic real part
Array2D<const HypreParMatrix * > Ab_r(num_blocks,num_blocks);
// Monolithic imag part
Array2D<const HypreParMatrix * > Ab_i(num_blocks,num_blocks);
for (int i = 0; i<num_blocks; i++)
{
for (int j = 0; j<num_blocks; j++)
{
Ab_r(i,j) = &(HypreParMatrix &)BlockA_r->GetBlock(i,j);
Ab_i(i,j) = &(HypreParMatrix &)BlockA_i->GetBlock(i,j);
}
}
HypreParMatrix * A_r = HypreParMatrixFromBlocks(Ab_r);
HypreParMatrix * A_i = HypreParMatrixFromBlocks(Ab_i);
ComplexHypreParMatrix Acomplex(A_r, A_i,true,true);
HypreParMatrix * A = Acomplex.GetSystemMatrix();
MUMPSSolver mumps(MPI_COMM_WORLD);
mumps.SetPrintLevel(0);
mumps.SetMatrixSymType(MUMPSSolver::MatType::UNSYMMETRIC);
mumps.SetOperator(*A);
mumps.Mult(B,X);
delete A;
}
#else
if (mumps_solver)
{
MFEM_WARNING("MFEM compiled without mumps. Switching to an iterative solver");
}
mumps_solver = false;
#endif
if (!mumps_solver)
{
BlockDiagonalPreconditioner M(tdof_offsets);
ParFiniteElementSpace *ams_fes = nullptr;
if (static_cond)
{
ams_fes = new ParFiniteElementSpace(&pmesh,
E_fes->FEColl()->GetTraceCollection());
}
HypreAMS * solver_E = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(0,0),
(static_cond) ? ams_fes : E_fes);
solver_E->SetPrintLevel(0);
HypreBoomerAMG * solver_hatE = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(1,1));
solver_hatE->SetPrintLevel(0);
solver_hatE->SetRelaxType(88);
M.SetDiagonalBlock(0,solver_E);
M.SetDiagonalBlock(1,solver_hatE);
M.SetDiagonalBlock(2,solver_E);
M.SetDiagonalBlock(3,solver_hatE);
if (myid == 0)
{
std::cout << "PCG iterations" << endl;
}
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-8);
cg.SetMaxIter(1000);
cg.SetPrintLevel(1);
cg.SetPreconditioner(M);
cg.SetOperator(blockA);
cg.Mult(B, X);
for (int i = 0; i<num_blocks; i++)
{
delete &M.GetDiagonalBlock(i);
}
//int num_iter = cg.GetNumIterations();
}
a->RecoverFEMSolution(X,x);
E_gf.real().MakeRef(E_fes,x.GetData());
E_gf.imag().MakeRef(E_fes,&x.GetData()[offsets.Last()]);
AzimuthalECoefficient az_e_r(&E_gf.real());
AzimuthalECoefficient az_e_i(&E_gf.imag());
E_theta_r.ProjectCoefficient(az_e_r);
E_theta_i.ProjectCoefficient(az_e_i);
if (visualization)
{
const char * keys = nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, E_gf.real(),
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
common::VisualizeField(E_theta_out_r,vishost, visport, E_theta_r,
"Numerical Electric field (Azimuthal-real)", 501, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(0);
paraview_dc->SetTime(0.0);
paraview_dc->Save();
}
delete a;
delete F_fec;
delete hatE_fes;
delete hatE_fec;
delete E_fec;
delete E_fes;
return 0;
}
+145
View File
@@ -0,0 +1,145 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Use the MFEM build directory
MFEM_DIR ?= ../../..
MFEM_BUILD_DIR ?= ../../..
SRC = $(if $(MFEM_DIR:../../..=),$(MFEM_DIR)/miniapps/dpg/plasma,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/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)
DPG_COMPLEX_SEQ_SRC = ../util/utils.cpp ../util/complexweakform.cpp \
../util/complexstaticcond.cpp ../util/complexblockform.cpp ../util/preconditioners.cpp
DPG_COMPLEX_PAR_SRC = $(DPG_COMPLEX_SEQ_SRC) ../util/pcomplexweakform.cpp ../util/pcomplexblockform.cpp ../util/blockcomplexhypremat.cpp
DPG_COMPLEX_OBJ = $(DPG_COMPLEX_PAR_SRC:.cpp=.o)
LH_UTILS_SRC = utils/lh_utils.cpp ../util/maxwell_utils.cpp
LH_UTILS_OBJ = $(LH_UTILS_SRC:.cpp=.o)
LH_PRIMAL_SRC = lh-primal-dpg.cpp $(DPG_COMPLEX_PAR_SRC) $(LH_UTILS_SRC)
LH_PRIMAL_OBJ = $(LH_PRIMAL_SRC:.cpp=.o)
LH_ELD_FEM_SRC = lh-eld-fem.cpp $(DPG_COMPLEX_PAR_SRC) $(LH_UTILS_SRC)
LH_ELD_FEM_OBJ = $(LH_ELD_FEM_SRC:.cpp=.o)
LH_ELD_FEM_SYMM_SRC = lh-eld-fem-symm.cpp $(DPG_COMPLEX_PAR_SRC) $(LH_UTILS_SRC)
LH_ELD_FEM_SYMM_OBJ = $(LH_ELD_FEM_SYMM_SRC:.cpp=.o)
LH_ELD_DPG_SRC = lh-eld-dpg.cpp $(DPG_COMPLEX_PAR_SRC) $(LH_UTILS_SRC)
LH_ELD_DPG_OBJ = $(LH_ELD_DPG_SRC:.cpp=.o)
LH_ELD_FOSLS_DPG_SRC = lh-eld-fosls-dpg.cpp $(DPG_COMPLEX_PAR_SRC) $(LH_UTILS_SRC)
LH_ELD_FOSLS_DPG_OBJ = $(LH_ELD_FOSLS_DPG_SRC:.cpp=.o)
LH_ELD_FOSLS_FEM_SRC = lh-eld-fosls-fem.cpp $(DPG_COMPLEX_PAR_SRC) $(LH_UTILS_SRC)
LH_ELD_FOSLS_FEM_OBJ = $(LH_ELD_FOSLS_FEM_SRC:.cpp=.o)
LH_ELD_DPG_FEM_SRC = lh-eld-dpg-fem.cpp $(DPG_COMPLEX_PAR_SRC) $(LH_UTILS_SRC)
LH_ELD_DPG_FEM_OBJ = $(LH_ELD_DPG_FEM_SRC:.cpp=.o)
LH_ELD_WEAK_SRC = lh-eld-weak.cpp $(DPG_COMPLEX_PAR_SRC) $(LH_UTILS_SRC)
LH_ELD_WEAK_OBJ = $(LH_ELD_WEAK_SRC:.cpp=.o)
LH_ELD_DPG_FEM_WEAK_SRC = lh-eld-dpg-fem-weak.cpp $(DPG_COMPLEX_PAR_SRC) $(LH_UTILS_SRC)
LH_ELD_DPG_FEM_WEAK_OBJ = $(LH_ELD_DPG_FEM_WEAK_SRC:.cpp=.o)
LH_ELD_UW_SRC = lh-eld-uw.cpp $(DPG_COMPLEX_PAR_SRC) $(LH_UTILS_SRC)
LH_ELD_UW_OBJ = $(LH_ELD_UW_SRC:.cpp=.o)
SEQ_MINIAPPS =
PAR_MINIAPPS = lh-primal-dpg lh-eld-fem lh-eld-fem-symm lh-eld-dpg \
lh-eld-dpg-fem lh-eld-weak lh-eld-dpg-fem-weak lh-eld-fosls-dpg\
lh-eld-fosls-fem lh-eld-uw
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
else
MINIAPPS = $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
endif
COMMON_LIB = -L$(MFEM_BUILD_DIR)/miniapps/common -lmfem-common
# If MFEM_SHARED is set, add the ../common rpath
COMMON_LIB += $(if $(MFEM_SHARED:YES=),,\
$(if $(MFEM_USE_CUDA:YES=),$(CXX_XLINKER),$(CUDA_XLINKER))-rpath,$(abspath\
$(MFEM_BUILD_DIR)/miniapps/common))
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all lib-common clean clean-build clean-exec
# Remove built-in rule
%: %.cpp
%.o: %.cpp
%.o: $(SRC)%.cpp $(wildcard $(SRC)%.hpp) $(MFEM_LIB_FILE)\
$(CONFIG_MK) | lib-common
$(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@
all: $(MINIAPPS)
lh-primal-dpg: $(LH_PRIMAL_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(LH_PRIMAL_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
lh-eld-fem: $(LH_ELD_FEM_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(LH_ELD_FEM_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
lh-eld-fem-symm: $(LH_ELD_FEM_SYMM_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(LH_ELD_FEM_SYMM_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
lh-eld-dpg: $(LH_ELD_DPG_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(LH_ELD_DPG_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
lh-eld-fosls-dpg: $(LH_ELD_FOSLS_DPG_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(LH_ELD_FOSLS_DPG_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
lh-eld-fosls-fem: $(LH_ELD_FOSLS_FEM_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(LH_ELD_FOSLS_FEM_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
lh-eld-dpg-fem: $(LH_ELD_DPG_FEM_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(LH_ELD_DPG_FEM_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
lh-eld-weak: $(LH_ELD_WEAK_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(LH_ELD_WEAK_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
lh-eld-dpg-fem-weak: $(LH_ELD_DPG_FEM_WEAK_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(LH_ELD_DPG_FEM_WEAK_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
lh-eld-uw: $(LH_ELD_UW_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(LH_ELD_UW_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
# Rule for building lib-common
lib-common:
$(MAKE) -C $(MFEM_BUILD_DIR)/miniapps/common
MFEM_TESTS = MINIAPPS
include $(MFEM_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 *~ $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
rm -f $(DPG_COMPLEX_OBJ) $(LH_UTILS_OBJ)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -rf ParaView
+121
View File
@@ -0,0 +1,121 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Use the MFEM build directory
MFEM_DIR ?= ../../../..
MFEM_BUILD_DIR ?= ../../../..
SRC = $(if $(MFEM_DIR:../../../..=),$(MFEM_DIR)/miniapps/dpg/plasma/other,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/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)
DPG_COMPLEX_SEQ_SRC = ../../util/utils.cpp ../../util/complexweakform.cpp \
../../util/complexstaticcond.cpp ../../util/complexblockform.cpp \
../../util/preconditioners.cpp ../../util/maxwell_utils.cpp
DPG_COMPLEX_PAR_SRC = $(DPG_COMPLEX_SEQ_SRC) ../../util/pcomplexweakform.cpp ../../util/pcomplexblockform.cpp ../../util/blockcomplexhypremat.cpp
DPG_COMPLEX_OBJ = $(DPG_COMPLEX_PAR_SRC:.cpp=.o)
MAXWELL_SRC = ../../util/maxwell_utils.cpp
MAXWELL_OBJ = $(MAXWELL_SRC:.cpp=.o)
PMAXWELL_TOKAMAK_SRC = pmaxwell-tokamak.cpp $(DPG_COMPLEX_PAR_SRC)
PMAXWELL_TOKAMAK_OBJ = $(PMAXWELL_TOKAMAK_SRC:.cpp=.o)
PMAXWELL_FEM_TOKAMAK_SRC = pmaxwell-fem-tokamak.cpp $(DPG_COMPLEX_PAR_SRC)
PMAXWELL_FEM_TOKAMAK_OBJ = $(PMAXWELL_FEM_TOKAMAK_SRC:.cpp=.o)
PMAXWELL_PRIMAL_TOKAMAK_SRC = pmaxwell-primal-tokamak.cpp $(DPG_COMPLEX_PAR_SRC)
PMAXWELL_PRIMAL_TOKAMAK_OBJ = $(PMAXWELL_PRIMAL_TOKAMAK_SRC:.cpp=.o)
PMAXWELL_UW_TOKAMAK_SRC = pmaxwell-uw-tokamak.cpp $(DPG_COMPLEX_PAR_SRC)
PMAXWELL_UW_TOKAMAK_OBJ = $(PMAXWELL_UW_TOKAMAK_SRC:.cpp=.o)
PMAXWELL_UW_LH_SRC = pmaxwell-uw-lh.cpp $(DPG_COMPLEX_PAR_SRC) $(MAXWELL_SRC)
PMAXWELL_UW_LH_OBJ = $(PMAXWELL_UW_LH_SRC:.cpp=.o)
PMAXWELL_FEM_LH_SRC = pmaxwell-fem-lh.cpp $(DPG_COMPLEX_PAR_SRC) $(MAXWELL_SRC)
PMAXWELL_FEM_LH_OBJ = $(PMAXWELL_FEM_LH_SRC:.cpp=.o)
SEQ_MINIAPPS =
PAR_MINIAPPS = pmaxwell-tokamak pmaxwell-fem-tokamak \
pmaxwell-primal-tokamak pmaxwell-uw-tokamak \
pmaxwell-uw-lh pmaxwell-fem-lh
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
else
MINIAPPS = $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
endif
COMMON_LIB = -L$(MFEM_BUILD_DIR)/miniapps/common -lmfem-common
# If MFEM_SHARED is set, add the ../common rpath
COMMON_LIB += $(if $(MFEM_SHARED:YES=),,\
$(if $(MFEM_USE_CUDA:YES=),$(CXX_XLINKER),$(CUDA_XLINKER))-rpath,$(abspath\
$(MFEM_BUILD_DIR)/miniapps/common))
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all lib-common clean clean-build clean-exec
# Remove built-in rule
%: %.cpp
%.o: %.cpp
%.o: $(SRC)%.cpp $(wildcard $(SRC)%.hpp) $(MFEM_LIB_FILE)\
$(CONFIG_MK) | lib-common
$(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@
all: $(MINIAPPS)
pmaxwell-tokamak: $(PMAXWELL_TOKAMAK_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_TOKAMAK_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pmaxwell-fem-tokamak: $(PMAXWELL_FEM_TOKAMAK_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_FEM_TOKAMAK_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pmaxwell-primal-tokamak: $(PMAXWELL_PRIMAL_TOKAMAK_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_PRIMAL_TOKAMAK_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pmaxwell-uw-tokamak: $(PMAXWELL_UW_TOKAMAK_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_UW_TOKAMAK_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pmaxwell-uw-lh: $(PMAXWELL_UW_LH_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_UW_LH_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pmaxwell-fem-lh: $(PMAXWELL_FEM_LH_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PMAXWELL_FEM_LH_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
# Rule for building lib-common
lib-common:
$(MAKE) -C $(MFEM_BUILD_DIR)/miniapps/common
MFEM_TESTS = MINIAPPS
include $(MFEM_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 *~ $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
rm -f $(DPG_COMPLEX_OBJ) $(MAXWELL_OBJ)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -rf ParaView
@@ -0,0 +1,318 @@
// MFEM Ultraweak DPG Maxwell parallel example
//
// the "ultraweak" (UW) DPG formulation for the Maxwell problem
// ∇×(1/μ ∇×E) - ω² ϵ E = Ĵ , in Ω
// E×n = E₀ , on ∂Ω
#include "mfem.hpp"
#include "../../util/pcomplexweakform.hpp"
#include "../../util/utils.hpp"
#include "../../util/maxwell_utils.hpp"
#include "../../../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
#include <cstring>
#include <filesystem>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
// fine mesh (trianles)
// default mesh
const char *mesh_file = "data/mesh-tri34K.mesh";
// coarse mesh (triangles)
// const char *mesh_file = "data/mesh-tri11K.mesh";
// coarse mesh (quadrilaterals)
// const char *mesh_file = "data/mesh-quad5K.mesh";
// epsilon tensor
const char * eps_r_file = nullptr;
const char * eps_i_file = nullptr;
int order = 2;
int par_ref_levels = 0;
bool visualization = false;
// real_t rnum=4.6e9;
// real_t mu = 1.257e-6;
// real_t epsilon_scale = 8.8541878128e-12*factor;
real_t rnum=4.6;
real_t mu = 1.257;
real_t epsilon_scale = 8.8541878128;
bool paraview = false;
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(&par_ref_levels, "-pr", "--parallel-refinement_levels",
"Number of parallel refinement levels.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
if (strcmp(mesh_file, "data/mesh-tri34K.mesh") == 0)
{
eps_r_file = "data/eps-tri34K_r.gf";
eps_i_file = "data/eps-tri34K_i.gf";
}
else if (strcmp(mesh_file, "data/mesh-tri11K.mesh") == 0)
{
eps_r_file = "data/eps-tri11K_r.gf";
eps_i_file = "data/eps-tri11K_i.gf";
}
else if (strcmp(mesh_file, "data/mesh-quad5K.mesh") == 0)
{
eps_r_file = "data/eps-quad5K_r.gf";
eps_i_file = "data/eps-quad5K_i.gf";
}
else
{
MFEM_ABORT("Unknown mesh file: " + string(mesh_file));
}
real_t omega = 2.*M_PI*rnum;
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
Array<int> int_bdr_attr;
for (int i = 0; i < mesh.GetNBE(); i++)
{
if (mesh.FaceIsInterior(mesh.GetBdrElementFaceIndex(i)))
{
int_bdr_attr.Append(mesh.GetBdrAttribute(i));
}
}
int_bdr_attr.Sort();
int_bdr_attr.Unique();
// mesh.RemoveInternalBoundaries();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
EpsilonMatrixCoefficient eps_r_cf(eps_r_file,&mesh,&pmesh, epsilon_scale);
EpsilonMatrixCoefficient eps_i_cf(eps_i_file,&mesh,&pmesh, epsilon_scale);
for (int i = 0; i<par_ref_levels; i++)
{
pmesh.UniformRefinement();
eps_r_cf.Update();
eps_i_cf.Update();
}
FiniteElementCollection *fec = new ND_FECollection(order, dim);
ParFiniteElementSpace *E_fes = new ParFiniteElementSpace(&pmesh, fec);
// Bilinear form coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient muinv(1./mu);
ScalarMatrixProductCoefficient m_cf_r(-omega*omega, eps_r_cf);
ScalarMatrixProductCoefficient m_cf_i(-omega*omega, eps_i_cf);
ParComplexLinearForm *b = new ParComplexLinearForm(E_fes);
b->Vector::operator=(0.0);
ParSesquilinearForm *a = new ParSesquilinearForm(E_fes);
a->AddDomainIntegrator(new CurlCurlIntegrator(muinv),nullptr);
a->AddDomainIntegrator(new VectorFEMassIntegrator(m_cf_r),
new VectorFEMassIntegrator(m_cf_i));
socketstream E_out_r;
socketstream E_theta_out_r;
socketstream E_theta_out_i;
ParComplexGridFunction E_gf(E_fes);
E_gf.real() = 0.0;
E_gf.imag() = 0.0;
L2_FECollection L2fec(order, dim);
ParFiniteElementSpace L2_fes(&pmesh, &L2fec);
ParGridFunction E_theta_r(&L2_fes);
ParGridFunction E_theta_i(&L2_fes);
ParGridFunction E_theta(&L2_fes);
E_theta = 0.0;
ParaViewDataCollection * paraview_dc = nullptr;
// ParaViewDataCollection * paraview_tdc = nullptr;
std::string output_dir = "ParaView/FEM/" + GetTimestamp();
if (Mpi::Root())
{
WriteParametersToFile(args, output_dir);
}
if (paraview)
{
std::ostringstream paraview_file_name;
std::string filename = GetFilename(mesh_file);
paraview_file_name << filename
<< "_par_ref_" << par_ref_levels
<< "_order_" << order;
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
paraview_dc->SetPrefixPath(output_dir);
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",&E_gf.real());
paraview_dc->RegisterField("E_i",&E_gf.imag());
paraview_dc->RegisterField("E_theta_r",&E_theta_r);
paraview_dc->RegisterField("E_theta_i",&E_theta_i);
// paraview_tdc = new ParaViewDataCollection(mesh_file, &pmesh);
// paraview_tdc->SetPrefixPath("ParaViewFEM2D/TimeHarmonic");
// paraview_tdc->SetLevelsOfDetail(order);
// paraview_tdc->SetCycle(0);
// paraview_tdc->SetDataFormat(VTKFormat::BINARY);
// paraview_tdc->SetHighOrderOutput(true);
// paraview_tdc->SetTime(0.0); // set the time
// paraview_tdc->RegisterField("E_theta_t",&E_theta);
}
Array<int> ess_tdof_list;
Array<int> ess_bdr;
Array<int> one_r_bdr;
Array<int> one_i_bdr;
Array<int> negone_r_bdr;
Array<int> negone_i_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_r_bdr.SetSize(pmesh.bdr_attributes.Max());
one_i_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_r_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_i_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
// remove internal boundaries
for (int i = 0; i<int_bdr_attr.Size(); i++)
{
ess_bdr[int_bdr_attr[i]-1] = 0;
}
E_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
one_r_bdr = 0; one_i_bdr = 0;
negone_r_bdr = 0; negone_i_bdr = 0;
// attr = 30,2 (real)
one_r_bdr[30-1] = 1; one_r_bdr[2-1] = 1;
// attr = 26,6 (imag)
one_i_bdr[26-1] = 1; one_i_bdr[6-1] = 1;
// attr = 22,10 (real)
negone_r_bdr[22-1] = 1; negone_r_bdr[10-1] = 1;
// attr = 18,14 (imag)
negone_i_bdr[18-1] = 1; negone_i_bdr[14-1] = 1;
}
Vector zero(dim); zero = 0.0;
Vector one_x(dim); one_x = 0.0; one_x(0) = 1.0;
Vector negone_x(dim); negone_x = 0.0; negone_x(0) = -1.0;
VectorConstantCoefficient zero_cf(zero);
VectorConstantCoefficient one_x_cf(one_x);
VectorConstantCoefficient negone_x_cf(negone_x);
E_gf.ProjectBdrCoefficientTangent(one_x_cf,zero_cf, one_r_bdr);
E_gf.ProjectBdrCoefficientTangent(negone_x_cf,zero_cf, negone_r_bdr);
E_gf.ProjectBdrCoefficientTangent(zero_cf,one_x_cf, one_i_bdr);
E_gf.ProjectBdrCoefficientTangent(zero_cf,negone_x_cf, negone_i_bdr);
b->Assemble();
a->Assemble();
OperatorPtr Ah;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, E_gf, *b, Ah, X, B);
#ifdef MFEM_USE_MUMPS
HypreParMatrix *A = Ah.As<ComplexHypreParMatrix>()->GetSystemMatrix();
// auto cpardiso = new CPardisoSolver(A->GetComm());
auto solver = new MUMPSSolver(MPI_COMM_WORLD);
solver->SetMatrixSymType(MUMPSSolver::MatType::UNSYMMETRIC);
solver->SetPrintLevel(1);
solver->SetOperator(*A);
solver->Mult(B,X);
delete A;
delete solver;
#else
MFEM_ABORT("MFEM compiled without mumps");
#endif
a->RecoverFEMSolution(X, *b, E_gf);
AzimuthalECoefficient az_e_r(&E_gf.real());
AzimuthalECoefficient az_e_i(&E_gf.imag());
E_theta_r.ProjectCoefficient(az_e_r);
E_theta_i.ProjectCoefficient(az_e_i);
if (visualization)
{
const char * keys = nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, E_gf.real(),
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
common::VisualizeField(E_theta_out_r,vishost, visport, E_theta_r,
"Numerical Electric field (azimuthal)", 0, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(0);
paraview_dc->SetTime((real_t)0);
paraview_dc->Save();
delete paraview_dc;
// int num_frames = 32;
// for (int i = 0; i<num_frames; i++)
// {
// real_t t = (real_t)(i % num_frames) / num_frames;
// add(cos(real_t(2.0*M_PI)*t), E_theta_r,
// sin(real_t(2.0*M_PI)*t), E_theta_i, E_theta);
// paraview_tdc->SetCycle(i);
// paraview_tdc->SetTime(t);
// paraview_tdc->Save();
// }
// delete paraview_tdc;
}
delete a;
delete b;
delete E_fes;
delete fec;
return 0;
}
@@ -0,0 +1,307 @@
// srun -n 256 ./pmaxwell-fem-tokamak -o 3 -sc -rnum
// srun -n 448 ./pmaxwell-fem-tokamak -o 4 -sc -rnum 11.0 -sigma 2.0 -paraview
// srun -n 448 ./pmaxwell-fem-tokamak -o 4 -paraview
// Description:
// This example code demonstrates the use of MFEM to define and solve
// the standard FEM formulation for the Maxwell problem
// ∇×(1/μ ∇×E) - (ω^2 ϵ + i ω σ) E = J , in Ω
// E×n = E_0, on ∂Ω
#include "mfem.hpp"
#include "../../util/pcomplexweakform.hpp"
#include "../../../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
class EpsilonMatrixCoefficient : public MatrixArrayCoefficient
{
private:
Mesh * mesh = nullptr;
ParMesh * pmesh = nullptr;
Array<ParGridFunction * > pgfs;
Array<GridFunctionCoefficient * > gf_cfs;
GridFunction * vgf = nullptr;
int dim;
public:
EpsilonMatrixCoefficient(const char * filename, Mesh * mesh_, ParMesh * pmesh_,
double scale = 1.0)
: MatrixArrayCoefficient(mesh_->Dimension()), mesh(mesh_), pmesh(pmesh_),
dim(mesh_->Dimension())
{
std::filebuf fb;
fb.open(filename,std::ios::in);
std::istream is(&fb);
vgf = new GridFunction(mesh,is);
fb.close();
FiniteElementSpace * vfes = vgf->FESpace();
int vdim = vfes->GetVDim();
const FiniteElementCollection * fec = vfes->FEColl();
FiniteElementSpace * fes = new FiniteElementSpace(mesh, fec);
int num_procs = Mpi::WorldSize();
int * partitioning = mesh->GeneratePartitioning(num_procs);
double *data = vgf->GetData();
GridFunction gf;
pgfs.SetSize(vdim);
gf_cfs.SetSize(vdim);
for (int i = 0; i<dim; i++)
{
for (int j = 0; j<dim; j++)
{
int k = i*dim+j;
// int k = j*dim+i;
gf.MakeRef(fes,&data[k*fes->GetVSize()]);
pgfs[k] = new ParGridFunction(pmesh,&gf,partitioning);
(*pgfs[k])*=scale;
gf_cfs[k] = new GridFunctionCoefficient(pgfs[k]);
Set(i,j,gf_cfs[k], true);
}
}
}
~EpsilonMatrixCoefficient()
{
for (int i = 0; i<pgfs.Size(); i++)
{
delete pgfs[i];
}
pgfs.DeleteAll();
}
};
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "data/mesh_330k.mesh";
const char * eps_r_file = "data/eps_r_330k.gf";
const char * eps_i_file = "data/eps_i_330k.gf";
int order = 1;
bool visualization = false;
double rnum=50.0e6;
int sr = 0;
int pr = 0;
bool paraview = false;
double mu = 1.257e-6;
double epsilon = 1.0;
double epsilon_scale = 8.8541878128e-12;
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(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&epsilon, "-eps", "--permittivity",
"Permittivity of free space (or mass constant).");
args.AddOption(&sr, "-sref", "--serial_ref",
"Number of serial refinements.");
args.AddOption(&pr, "-pref", "--parallel_ref",
"Number of parallel refinements.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
socketstream E_out_r;
double omega = 2.*M_PI*rnum;
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
for (int i = 0; i<sr; i++)
{
mesh.UniformRefinement();
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
EpsilonMatrixCoefficient eps_r_cf(eps_r_file,&mesh,&pmesh, epsilon_scale);
EpsilonMatrixCoefficient eps_i_cf(eps_i_file,&mesh,&pmesh, epsilon_scale);
mesh.Clear();
FiniteElementCollection *fec = new ND_FECollection(order, dim);
ParFiniteElementSpace *E_fes = new ParFiniteElementSpace(&pmesh, fec);
// Bilinear form coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient muinv(1./mu);
if (myid == 0)
{
std::cout << "Assembling matrix" << endl;
}
ScalarMatrixProductCoefficient m_cf_r(-omega*omega, eps_r_cf);
ScalarMatrixProductCoefficient m_cf_i(-omega*omega, eps_i_cf);
ParComplexLinearForm *b = new ParComplexLinearForm(E_fes);
b->Vector::operator=(0.0);
ParSesquilinearForm *a = new ParSesquilinearForm(E_fes);
a->AddDomainIntegrator(new CurlCurlIntegrator(muinv),nullptr);
a->AddDomainIntegrator(new VectorFEMassIntegrator(m_cf_r),
new VectorFEMassIntegrator(m_cf_i));
ParComplexGridFunction E_gf(E_fes);
E_gf.real() = 0.0;
E_gf.imag() = 0.0;
ParaViewDataCollection * paraview_dc = nullptr;
if (paraview)
{
paraview_dc = new ParaViewDataCollection(mesh_file, &pmesh);
paraview_dc->SetPrefixPath("ParaView");
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",&E_gf.real());
paraview_dc->RegisterField("E_i",&E_gf.imag());
}
// internal bdr attributes
Array<int> internal_bdr({1, 3, 6, 9, 17, 157, 185, 75, 210, 211,
212, 213, 214, 215, 216, 217, 218, 219,
220, 221, 222, 223, 224, 225, 226, 227,
228, 229, 230, 231, 232, 233, 234, 125});
Array<int> ess_tdof_list;
Array<int> ess_bdr;
Array<int> one_bdr;
Array<int> negone_bdr;
if (myid == 0)
{
std::cout << "Attributes" << endl;
}
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
// need to exclude these attributes
for (int i = 0; i<internal_bdr.Size(); i++)
{
ess_bdr[internal_bdr[i]-1] = 0;
}
E_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
one_bdr = 0;
negone_bdr = 0;
one_bdr[234] = 1;
negone_bdr[235] = 1;
}
if (myid == 0)
{
std::cout << "Attributes 2" << endl;
}
Vector z_one(3); z_one = 0.0; z_one(2) = 1.0;
Vector zero(3); zero = 0.0;
Vector z_negone(3); z_negone = 0.0; z_negone(2) = -1.0;
VectorConstantCoefficient z_one_cf(z_one);
VectorConstantCoefficient z_negone_cf(z_negone);
VectorConstantCoefficient zero_cf(zero);
E_gf.real() = 0.0;
E_gf.imag() = 0.0;
E_gf.ProjectBdrCoefficientTangent(z_one_cf,zero_cf, one_bdr);
E_gf.ProjectBdrCoefficientTangent(z_negone_cf,zero_cf, negone_bdr);
if (myid == 0)
{
std::cout << "Assembly started" << endl;
}
b->Assemble();
a->Assemble();
OperatorPtr Ah;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, E_gf, *b, Ah, X, B);
if (myid == 0)
{
std::cout << "Assembly finished" << endl;
}
#ifdef MFEM_USE_MUMPS
HypreParMatrix *A = Ah.As<ComplexHypreParMatrix>()->GetSystemMatrix();
// auto cpardiso = new CPardisoSolver(A->GetComm());
auto solver = new MUMPSSolver(MPI_COMM_WORLD);
solver->SetMatrixSymType(MUMPSSolver::MatType::UNSYMMETRIC);
solver->SetPrintLevel(1);
solver->SetOperator(*A);
solver->Mult(B,X);
delete A;
delete solver;
#else
MFEM_ABORT("MFEM compiled without mumps");
#endif
a->RecoverFEMSolution(X, *b, E_gf);
if (visualization)
{
const char * keys = nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, E_gf.real(),
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(0);
paraview_dc->SetTime((double)0);
paraview_dc->Save();
}
if (paraview)
{
delete paraview_dc;
}
delete a;
delete b;
delete E_fes;
delete fec;
return 0;
}
@@ -0,0 +1,495 @@
// srun -n 256 ./pmaxwell-primal-tokamak -o 3 -sc -rnum
// srun -n 448 ./pmaxwell-primal-tokamak -o 4 -sc -rnum 11.0 -paraview
// srun -n 448 ./pmaxwell-primal-tokamak -o 4 -do 0 -sc -paraview (with the new epsilon GridFunction coefficients)
// Description:
// This example code demonstrates the use of MFEM to define and solve
// the "ultraweak" (UW) DPG formulation for the Maxwell problem
// ∇×(1/μ ∇×E) - ω^2 ϵ E = Ĵ , in Ω
// E×n = E_0, on ∂Ω
// The primal-DPG formulation is obtained by integration by parts
// and the introduction of trace unknowns on the mesh skeleton
// in 3D
// E ∈ H(curl)
// Ê ∈ H_0^1/2(Ω)(curl, Γ_h)
// 1/μ (∇×E , ∇×F) + (ω^2 ϵ , F) + <Ê , F × n> = 0, ∀ F ∈ H(curl,Ω)
// Ê × n = E_0 on ∂Ω
#include "mfem.hpp"
#include "../../util/pcomplexweakform.hpp"
#include "../../../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
class EpsilonMatrixCoefficient : public MatrixArrayCoefficient
{
private:
Mesh * mesh = nullptr;
ParMesh * pmesh = nullptr;
Array<ParGridFunction * > pgfs;
Array<GridFunctionCoefficient * > gf_cfs;
GridFunction * vgf = nullptr;
int dim;
public:
EpsilonMatrixCoefficient(const char * filename, Mesh * mesh_, ParMesh * pmesh_,
double scale = 1.0)
: MatrixArrayCoefficient(mesh_->Dimension()), mesh(mesh_), pmesh(pmesh_),
dim(mesh_->Dimension())
{
std::filebuf fb;
fb.open(filename,std::ios::in);
std::istream is(&fb);
vgf = new GridFunction(mesh,is);
fb.close();
FiniteElementSpace * vfes = vgf->FESpace();
int vdim = vfes->GetVDim();
const FiniteElementCollection * fec = vfes->FEColl();
FiniteElementSpace * fes = new FiniteElementSpace(mesh, fec);
int num_procs = Mpi::WorldSize();
int * partitioning = mesh->GeneratePartitioning(num_procs);
double *data = vgf->GetData();
GridFunction gf;
pgfs.SetSize(vdim);
gf_cfs.SetSize(vdim);
for (int i = 0; i<dim; i++)
{
for (int j = 0; j<dim; j++)
{
int k = i*dim+j;
// int k = j*dim+i;
gf.MakeRef(fes,&data[k*fes->GetVSize()]);
pgfs[k] = new ParGridFunction(pmesh,&gf,partitioning);
(*pgfs[k])*=scale;
gf_cfs[k] = new GridFunctionCoefficient(pgfs[k]);
Set(i,j,gf_cfs[k], true);
}
}
}
~EpsilonMatrixCoefficient()
{
for (int i = 0; i<pgfs.Size(); i++)
{
delete pgfs[i];
}
pgfs.DeleteAll();
}
};
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "data/mesh_330k.mesh";
const char * eps_r_file = "data/eps_r_330k.gf";
const char * eps_i_file = "data/eps_i_330k.gf";
int order = 1;
int delta_order = 1;
bool visualization = false;
// double rnum=50.0;
double rnum=50.0e6;
int sr = 0;
int pr = 0;
bool paraview = false;
double mu = 1.257e-6;
double epsilon = 1.0;
double epsilon_scale = 8.8541878128e-12;
// double epsilon_scale = 8.8541878128;
bool mumps_solver = false;
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(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&epsilon, "-eps", "--permittivity",
"Permittivity of free space (or mass constant).");
args.AddOption(&delta_order, "-do", "--delta_order",
"Order enrichment for DPG test space.");
args.AddOption(&sr, "-sref", "--serial_ref",
"Number of serial refinements.");
args.AddOption(&pr, "-pref", "--parallel_ref",
"Number of parallel refinements.");
#ifdef MFEM_USE_MUMPS
args.AddOption(&mumps_solver, "-mumps", "--mumps-solver", "-no-mumps",
"--no-mumps-solver", "Use the MUMPS Solver.");
#endif
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
double omega = 2.*M_PI*rnum;
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
for (int i = 0; i<sr; i++)
{
mesh.UniformRefinement();
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
EpsilonMatrixCoefficient eps_r_cf(eps_r_file,&mesh,&pmesh, epsilon_scale);
EpsilonMatrixCoefficient eps_i_cf(eps_i_file,&mesh,&pmesh, epsilon_scale);
mesh.Clear();
FiniteElementCollection *E_fec = new ND_FECollection(order,dim);
ParFiniteElementSpace *E_fes = new ParFiniteElementSpace(&pmesh,E_fec);
// H^-1/2 (curl) space for Ê
int test_order = order+delta_order;
FiniteElementCollection * hatE_fec = new ND_Trace_FECollection(order,dim);
FiniteElementCollection * F_fec = new ND_FECollection(test_order, dim);
ParFiniteElementSpace *hatE_fes = new ParFiniteElementSpace(&pmesh,hatE_fec);
Array<ParFiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(E_fes);
trial_fes.Append(hatE_fes);
test_fec.Append(F_fec);
// Bilinear form coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient invmu_cf(1./mu);
Vector z_one(3); z_one = 0.0; z_one(2) = 1.0;
Vector zero(3); zero = 0.0;
Vector z_negone(3); z_negone = 0.0; z_negone(2) = -1.0;
VectorConstantCoefficient z_one_cf(z_one);
VectorConstantCoefficient z_negone_cf(z_negone);
VectorConstantCoefficient zero_cf(zero);
if (myid == 0)
{
std::cout << "Assembling matrix" << endl;
}
ParComplexDPGWeakForm * a = new ParComplexDPGWeakForm(trial_fes,test_fec);
// (∇ × E,∇ × F)
a->AddTrialIntegrator(new MixedCurlCurlIntegrator(invmu_cf), nullptr,0,0);
// -(ω^2 ϵ, F)
ScalarMatrixProductCoefficient m_cf_r(-omega*omega, eps_r_cf);
ScalarMatrixProductCoefficient m_cf_i(-omega*omega, eps_i_cf);
const IntegrationRule *irs[Geometry::NumGeom];
int order_quad = 2*order + 2;
for (int i = 0; i < Geometry::NumGeom; ++i)
{
irs[i] = &(IntRules.Get(i, order_quad));
}
const IntegrationRule &ir = IntRules.Get(pmesh.GetElementGeometry(0),
2*test_order + 2);
VectorFEMassIntegrator * integ_r = new VectorFEMassIntegrator(m_cf_r);
VectorFEMassIntegrator * integ_i = new VectorFEMassIntegrator(m_cf_i);
integ_r->SetIntegrationRule(ir);
integ_i->SetIntegrationRule(ir);
a->AddTrialIntegrator(integ_r,integ_i,0,0);
// < n×Ê,F>
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,1,0);
// test integrators
// (∇×F ,∇× δF)
a->AddTestIntegrator(new CurlCurlIntegrator(one),nullptr,0,0);
// (F,δF)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,0,0);
socketstream E_out_r;
ParComplexGridFunction E_gf(E_fes);
E_gf.real() = 0.0;
E_gf.imag() = 0.0;
ParaViewDataCollection * paraview_dc = nullptr;
if (paraview)
{
paraview_dc = new ParaViewDataCollection(mesh_file, &pmesh);
paraview_dc->SetPrefixPath("ParaViewPrimalDPG");
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",&E_gf.real());
paraview_dc->RegisterField("E_i",&E_gf.imag());
}
// internal bdr attributes
Array<int> internal_bdr({1, 3, 6, 9, 17, 157, 185, 75, 210, 211,
212, 213, 214, 215, 216, 217, 218, 219,
220, 221, 222, 223, 224, 225, 226, 227,
228, 229, 230, 231, 232, 233, 234, 125});
for (int it = 0; it<=pr; it++)
{
Array<int> ess_tdof_list;
Array<int> ess_bdr;
Array<int> one_bdr;
Array<int> negone_bdr;
if (myid == 0)
{
std::cout << "Attributes" << endl;
}
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
// need to exclude these attributes
for (int i = 0; i<internal_bdr.Size(); i++)
{
ess_bdr[internal_bdr[i]-1] = 0;
}
E_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
one_bdr = 0;
negone_bdr = 0;
one_bdr[234] = 1;
negone_bdr[235] = 1;
}
if (myid == 0)
{
std::cout << "Attributes 2" << endl;
}
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = E_fes->GetVSize();
offsets[2] = hatE_fes->GetVSize();
offsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
double * xdata = x.GetData();
E_gf.real().MakeRef(E_fes,&xdata[0]);
E_gf.imag().MakeRef(E_fes,&xdata[offsets.Last()]);
E_gf.ProjectBdrCoefficientTangent(z_one_cf,zero_cf, one_bdr);
E_gf.ProjectBdrCoefficientTangent(z_negone_cf,zero_cf, negone_bdr);
if (myid == 0)
{
std::cout << "Assembly started" << endl;
}
a->Assemble();
if (myid == 0)
{
std::cout << "Assembly finished" << endl;
}
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
int num_blocks = BlockA_r->NumRowBlocks();
Array<int> tdof_offsets(2*num_blocks+1);
tdof_offsets[0] = 0;
for (int i=0; i<num_blocks; i++)
{
tdof_offsets[i+1] = trial_fes[i]->GetTrueVSize();
tdof_offsets[num_blocks+i+1] = trial_fes[i]->GetTrueVSize();
}
tdof_offsets.PartialSum();
BlockOperator blockA(tdof_offsets);
for (int i = 0; i<num_blocks; i++)
{
for (int j = 0; j<num_blocks; j++)
{
blockA.SetBlock(i,j,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i,j+num_blocks,&BlockA_i->GetBlock(i,j), -1.0);
blockA.SetBlock(i+num_blocks,j+num_blocks,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i+num_blocks,j,&BlockA_i->GetBlock(i,j));
}
}
#ifdef MFEM_USE_MUMPS
if (mumps_solver)
{
// Monolithic real part
Array2D <HypreParMatrix * > Ab_r(num_blocks,num_blocks);
// Monolithic imag part
Array2D <HypreParMatrix * > Ab_i(num_blocks,num_blocks);
for (int i = 0; i<num_blocks; i++)
{
for (int j = 0; j<num_blocks; j++)
{
Ab_r(i,j) = &(HypreParMatrix &)BlockA_r->GetBlock(i,j);
Ab_i(i,j) = &(HypreParMatrix &)BlockA_i->GetBlock(i,j);
}
}
HypreParMatrix * A_r = HypreParMatrixFromBlocks(Ab_r);
HypreParMatrix * A_i = HypreParMatrixFromBlocks(Ab_i);
ComplexHypreParMatrix Acomplex(A_r, A_i,true,true);
HypreParMatrix * A = Acomplex.GetSystemMatrix();
MUMPSSolver mumps(MPI_COMM_WORLD);
mumps.SetPrintLevel(0);
mumps.SetMatrixSymType(MUMPSSolver::MatType::UNSYMMETRIC);
mumps.SetOperator(*A);
mumps.Mult(B,X);
delete A;
}
#else
if (mumps_solver)
{
MFEM_WARNING("MFEM compiled without mumps. Switching to an iterative solver");
}
mumps_solver = false;
#endif
if (!mumps_solver)
{
BlockDiagonalPreconditioner M(tdof_offsets);
HypreAMS * solver_E = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(0,0),
E_fes);
HypreAMS * solver_hatE = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(1,1),
hatE_fes);
solver_E->SetPrintLevel(0);
solver_hatE->SetPrintLevel(0);
M.SetDiagonalBlock(0,solver_E);
M.SetDiagonalBlock(1,solver_hatE);
if (myid == 0)
{
std::cout << "PCG iterations" << endl;
}
double bl2norm = B.Norml2();
double xl2norm = X.Norml2();
MPI_Allreduce(MPI_IN_PLACE,&bl2norm,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
MPI_Allreduce(MPI_IN_PLACE,&xl2norm,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
if (myid == 0)
{
mfem::out << "X.Norm = " << xl2norm << endl;
mfem::out << "B.Norm = " << bl2norm << endl;
}
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-8);
cg.SetMaxIter(1000);
cg.SetPrintLevel(1);
cg.SetPreconditioner(M);
cg.SetOperator(blockA);
cg.Mult(B, X);
for (int i = 0; i<num_blocks; i++)
{
delete &M.GetDiagonalBlock(i);
}
int num_iter = cg.GetNumIterations();
}
a->RecoverFEMSolution(X,x);
E_gf.real().MakeRef(E_fes,x.GetData());
E_gf.imag().MakeRef(E_fes,&x.GetData()[offsets.Last()]);
int dofs = 0;
for (int i = 0; i<trial_fes.Size(); i++)
{
dofs += trial_fes[i]->GlobalTrueVSize();
}
if (visualization)
{
const char * keys = (it == 0 && dim == 2) ? "jRcml\n" : nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, E_gf.real(),
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(it);
paraview_dc->SetTime((double)it);
paraview_dc->Save();
}
if (it == pr)
{
break;
}
pmesh.UniformRefinement();
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
}
if (paraview)
{
delete paraview_dc;
}
delete a;
delete F_fec;
delete hatE_fes;
delete hatE_fec;
delete E_fec;
delete E_fes;
return 0;
}
@@ -0,0 +1,782 @@
// srun -n 256 ./pmaxwell-tokamak -o 3 -sc -rnum
// srun -n 448 ./pmaxwell-tokamak -o 4 -sc -rnum 11.0 -sigma 2.0 -paraview
// srun -n 448 ./pmaxwell-tokamak -o 4 -do 0 -sc -paraview (with the new epsilon GridFunction coefficients)
// TODO do > 0 fails with non SPD G matrix
// Description:
// This example code demonstrates the use of MFEM to define and solve
// the "ultraweak" (UW) DPG formulation for the Maxwell problem
// ∇×(1/μ ∇×E) - (ω^2 ϵ + i ω σ) E = Ĵ , in Ω
// E×n = E_0, on ∂Ω
// The DPG UW deals with the First Order System
// i ω μ H + ∇ × E = 0, in Ω (Faraday's law)
// M E + ∇ × H = J, in Ω (Ampere's law)
// E × n = E_0, on ∂Ω
// Note: Ĵ = -iωJ
// where M = -(i ω ϵI + σI)
// The ultraweak-DPG formulation is obtained by integration by parts of both
// equations and the introduction of trace unknowns on the mesh skeleton
// in 3D
// E,H ∈ (L^2(Ω))^3
// Ê ∈ H_0^1/2(Ω)(curl, Γ_h), Ĥ ∈ H^-1/2(curl, Γ_h)
// i ω μ (H,F) + (E,∇ × F) + < Ê, F × n > = 0, ∀ F ∈ H(curl,Ω)
// M (E,G) + (H,∇ × G) + < Ĥ, G × n > = (J,G) ∀ G ∈ H(curl,Ω)
// Ê × n = E_0 on ∂Ω
// -------------------------------------------------------------------------
// | | E | H | Ê | Ĥ | RHS |
// -------------------------------------------------------------------------
// | F | (E,∇ × F) | i ω μ (H,F) | < n × Ê, F > | | |
// | | | | | | |
// | G | (ME,G) | (H,∇ × G) | | < n × Ĥ, G > | (J,G) |
// where (F,G) ∈ H(curl,Ω) × H(curl,Ω)
// Here we use the "Adjoint Graph" norm on the test space i.e.,
// ||(F,G)||^2_V = ||A^*(F,G)||^2 + ||(F,G)||^2 where A is the
// maxwell operator defined by (1)
#include "mfem.hpp"
#include "../../util/pcomplexweakform.hpp"
#include "../../../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
class EpsilonMatrixCoefficient : public MatrixArrayCoefficient
{
private:
Mesh * mesh = nullptr;
ParMesh * pmesh = nullptr;
Array<ParGridFunction * > pgfs;
Array<GridFunctionCoefficient * > gf_cfs;
GridFunction * vgf = nullptr;
int dim;
public:
EpsilonMatrixCoefficient(const char * filename, Mesh * mesh_, ParMesh * pmesh_,
double scale = 1.0)
: MatrixArrayCoefficient(mesh_->Dimension()), mesh(mesh_), pmesh(pmesh_),
dim(mesh_->Dimension())
{
std::filebuf fb;
fb.open(filename,std::ios::in);
std::istream is(&fb);
vgf = new GridFunction(mesh,is);
fb.close();
FiniteElementSpace * vfes = vgf->FESpace();
int vdim = vfes->GetVDim();
const FiniteElementCollection * fec = vfes->FEColl();
FiniteElementSpace * fes = new FiniteElementSpace(mesh, fec);
int num_procs = Mpi::WorldSize();
int * partitioning = mesh->GeneratePartitioning(num_procs);
double *data = vgf->GetData();
GridFunction gf;
pgfs.SetSize(vdim);
gf_cfs.SetSize(vdim);
for (int i = 0; i<dim; i++)
{
for (int j = 0; j<dim; j++)
{
int k = i*dim+j;
// int k = j*dim+i;
gf.MakeRef(fes,&data[k*fes->GetVSize()]);
pgfs[k] = new ParGridFunction(pmesh,&gf,partitioning);
(*pgfs[k])*=scale;
gf_cfs[k] = new GridFunctionCoefficient(pgfs[k]);
Set(i,j,gf_cfs[k], true);
}
}
}
~EpsilonMatrixCoefficient()
{
for (int i = 0; i<pgfs.Size(); i++)
{
delete pgfs[i];
}
pgfs.DeleteAll();
}
};
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
// const char *mesh_file = "tokamak_100k.msh";
// const char *mesh_file = "tokamak_200k.msh";
// const char *mesh_file = "meshes/tokamak_100k.msh";
// const char *mesh_file = "meshes/tokamak_100k.msh";
const char *mesh_file = "data/mesh_330k.mesh";
const char * eps_r_file = "data/eps_r_330k.gf";
const char * eps_i_file = "data/eps_i_330k.gf";
// const char *mesh_file = "meshes/box.msh";
int order = 1;
int delta_order = 1;
bool visualization = false;
double rnum=50.0e6;
bool static_cond = false;
int sr = 0;
int pr = 0;
bool paraview = false;
double factor = 1.0;
double mu = 1.257e-6/factor;
// double mu = 1.0;
double epsilon = 1.0;
double sigma = 0.01*factor;
double epsilon_scale = 8.8541878128e-12*factor;
bool graph_norm = true;
bool mumps_solver = false;
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(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&epsilon, "-eps", "--permittivity",
"Permittivity of free space (or mass constant).");
args.AddOption(&sigma, "-sigma", "--sigma",
"conductivity");
args.AddOption(&delta_order, "-do", "--delta_order",
"Order enrichment for DPG test space.");
args.AddOption(&sr, "-sref", "--serial_ref",
"Number of serial refinements.");
args.AddOption(&pr, "-pref", "--parallel_ref",
"Number of parallel refinements.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&graph_norm, "-graph", "--graph-norm", "-no-gn",
"--no-graph-norm", "Enable adjoint graph norm.");
#ifdef MFEM_USE_MUMPS
args.AddOption(&mumps_solver, "-mumps", "--mumps-solver", "-no-mumps",
"--no-mumps-solver", "Use the MUMPS Solver.");
#endif
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
double omega = 2.*M_PI*rnum;
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
for (int i = 0; i<sr; i++)
{
mesh.UniformRefinement();
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
// Test first with identity matrix coefficient
DenseMatrix Id(dim); Id = 0.0;
Id(0,0) = 1; Id(0,1) = 0.0; Id(0,2) = 0.0;
Id(1,0) = 0.0; Id(1,1) = 1; Id(1,2) = 0.0;
Id(2,0) = 0.0; Id(2,1) = 0.0; Id(2,2) = 1;
DenseMatrix C(dim); C = 0.0;
C(0,0) = 1.0; C(1,1) = 1.0; C(2,2) = 1.0;
DenseMatrix zmat(dim); zmat = 0.0;
MatrixConstantCoefficient identity_cf(Id);
// FiniteElementCollection *H1_fec = new H1_FECollection(1,dim);
// ParFiniteElementSpace *H1_fes = new ParFiniteElementSpace(&pmesh,H1_fec);
// Array<ParGridFunction * > pgfs(dim*dim);
// Array<GridFunctionCoefficient * > pcfs(dim*dim);
// MatrixArrayCoefficient eps_r_cf(dim);
// MatrixArrayCoefficient eps_i_cf(dim);
// for (int i = 0; i<dim; i++)
// {
// for (int j = 0; j<dim; j++)
// {
// int k = i*dim + j;
// pgfs[k] = new ParGridFunction(H1_fes);
// if (i == j)
// {
// (*pgfs[k]) = 1.0;
// }
// else
// {
// (*pgfs[k]) = 0.0;
// }
// pcfs[k] = new GridFunctionCoefficient(pgfs[k]);
// eps_r_cf.Set(i,j,pcfs[k],false);
// eps_i_cf.Set(i,j,pcfs[k], false);
// }
// }
// MatrixConstantCoefficient eps_r_cf(C);
// MatrixConstantCoefficient eps_i_cf(zmat);
DenseMatrix mat_eps_r(dim);
mat_eps_r(0,0) = -3.18447132e+02; mat_eps_r(0,1) = -7.73308634e-11;
mat_eps_r(0,2) = 2.26832549e+02;
mat_eps_r(1,0) = -7.73030845e-11; mat_eps_r(1,1) = -1.26300045e+06;
mat_eps_r(1,2) = -7.73308634e-11;
mat_eps_r(2,0) = -2.26832549e+02; mat_eps_r(2,1) = -7.73030845e-11;
mat_eps_r(2,2) = -3.18447132e+02;
DenseMatrix mat_eps_i(dim);
mat_eps_i(0,0) = 3.06793840e+02; mat_eps_i(0,1) = 4.06300785e-11;
mat_eps_i(0,2) = 7.95645437e+02;
mat_eps_i(1,0) = 4.07275170e-11; mat_eps_i(1,1) = 6.64641976e+05;
mat_eps_i(1,2) = 4.06300785e-11;
mat_eps_i(2,0) = -7.95645437e+02; mat_eps_i(2,1) = 4.07275170e-11;
mat_eps_i(2,2) = 3.06793840e+02;
mat_eps_r *= epsilon_scale;
mat_eps_i *= epsilon_scale;
// MatrixConstantCoefficient eps_r_cf(mat_eps_r);
// MatrixConstantCoefficient eps_i_cf(mat_eps_i);
EpsilonMatrixCoefficient eps_r_cf(eps_r_file,&mesh,&pmesh, epsilon_scale);
EpsilonMatrixCoefficient eps_i_cf(eps_i_file,&mesh,&pmesh, epsilon_scale);
mesh.Clear();
// Matrix Coefficient (M = -i\omega \epsilon - \sigma I);
// M = -i * omega * (eps_r + i eps_i) - sigmaI
// = omega eps_i - sigma I + i (-omega eps_r)
MatrixSumCoefficient Mr_cf(eps_i_cf,identity_cf,omega,-sigma);
ScalarMatrixProductCoefficient Mi_cf(-omega,eps_r_cf);
// Matrix Coefficient eps_mat = -i\omega \epsilon
// = omega eps_mat_i + i (-omega eps_mat_r)
// ScalarMatrixProductCoefficient mat_eps_r_cf(omega,eps_i_cf);
// ScalarMatrixProductCoefficient mat_eps_i_cf(-omega,eps_r_cf);
// Define spaces
// L2 space for E
FiniteElementCollection *E_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *E_fes = new ParFiniteElementSpace(&pmesh,E_fec,dim);
// Vector L2 space for H
FiniteElementCollection *H_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *H_fes = new ParFiniteElementSpace(&pmesh,H_fec, dim);
// H^-1/2 (curl) space for Ê
FiniteElementCollection * hatE_fec = nullptr;
FiniteElementCollection * hatH_fec = nullptr;
FiniteElementCollection * F_fec = nullptr;
int test_order = order+delta_order;
hatE_fec = new ND_Trace_FECollection(order,dim);
hatH_fec = new ND_Trace_FECollection(order,dim);
F_fec = new ND_FECollection(test_order, dim);
ParFiniteElementSpace *hatE_fes = new ParFiniteElementSpace(&pmesh,hatE_fec);
ParFiniteElementSpace *hatH_fes = new ParFiniteElementSpace(&pmesh,hatH_fec);
FiniteElementCollection * G_fec = new ND_FECollection(test_order, dim);
Array<ParFiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(E_fes);
trial_fes.Append(H_fes);
trial_fes.Append(hatE_fes);
trial_fes.Append(hatH_fes);
test_fec.Append(F_fec);
test_fec.Append(G_fec);
// Bilinear form coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient eps2omeg2(epsilon*epsilon*omega*omega);
ConstantCoefficient mu2omeg2(mu*mu*omega*omega);
ConstantCoefficient muomeg(mu*omega);
ConstantCoefficient negepsomeg(-epsilon*omega);
ConstantCoefficient epsomeg(epsilon*omega);
ConstantCoefficient negmuomeg(-mu*omega);
ConstantCoefficient sigma_cf(sigma);
ConstantCoefficient negsigma_cf(-sigma);
ConstantCoefficient sigma2_cf(sigma*sigma);
Vector z_one(3); z_one = 0.0; z_one(2) = 1.0;
Vector zero(3); zero = 0.0;
Vector z_negone(3); z_negone = 0.0; z_negone(2) = -1.0;
VectorConstantCoefficient z_one_cf(z_one);
VectorConstantCoefficient z_negone_cf(z_negone);
VectorConstantCoefficient zero_cf(zero);
if (myid == 0)
{
std::cout << "Assembling matrix" << endl;
}
ParComplexDPGWeakForm * a = new ParComplexDPGWeakForm(trial_fes,test_fec);
// (E,∇ × F)
a->AddTrialIntegrator(new TransposeIntegrator(new MixedCurlIntegrator(one)),
nullptr,0,0);
// --------------------------------------------------------------------------
// -(i ω ϵ + σ) (E , G) = i (- ω ϵ E, G) - (σ E, G)
// a->AddTrialIntegrator(
// new TransposeIntegrator(new VectorFEMassIntegrator(negsigma_cf)),
// new TransposeIntegrator(new VectorFEMassIntegrator(negepsomeg)),0,1);
// a->AddTrialIntegrator(
// new TransposeIntegrator(new VectorFEMassIntegrator(negsigma_cf)), nullptr,0,1);
// a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(mat_eps_r_cf)),
// new TransposeIntegrator(new VectorFEMassIntegrator(mat_eps_i_cf)), 0,1);
// (M E , G) = (M_r E, G) + i (M_i E, G)
a->AddTrialIntegrator(
new TransposeIntegrator(new VectorFEMassIntegrator(Mr_cf)),
new TransposeIntegrator(new VectorFEMassIntegrator(Mi_cf)),0,1);
// --------------------------------------------------------------------------
// (H,∇ × G)
a->AddTrialIntegrator(new TransposeIntegrator(new MixedCurlIntegrator(one)),
nullptr,1,1);
// < n×Ĥ ,G>
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,3,1);
// test integrators
// (∇×G ,∇× δG)
a->AddTestIntegrator(new CurlCurlIntegrator(one),nullptr,1,1);
ConstantCoefficient l2weight(1.0);
// (G,δG)
a->AddTestIntegrator(new VectorFEMassIntegrator(l2weight),nullptr,1,1);
// i ω μ (H, F)
a->AddTrialIntegrator(nullptr,
new TransposeIntegrator(new VectorFEMassIntegrator(muomeg)),1,0);
// < n×Ê,F>
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,2,0);
// test integrators
// (∇×F,∇×δF)
a->AddTestIntegrator(new CurlCurlIntegrator(one),nullptr,0,0);
// (F,δF)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,0,0);
if (graph_norm)
{
// μ^2 ω^2 (F,δF)
a->AddTestIntegrator(new VectorFEMassIntegrator(mu2omeg2),nullptr,0,0);
// -i ω μ (F,∇ × δG) = i (F, -ω μ ∇ × δ G)
a->AddTestIntegrator(nullptr,new MixedVectorWeakCurlIntegrator(negmuomeg),0,1);
// --------------------------------------------------------------------------
// // -i ω ϵ (∇ × F, δG) - σ (∇ × F, δG)
// a->AddTestIntegrator(new MixedVectorCurlIntegrator(negsigma_cf),
// new MixedVectorCurlIntegrator(negepsomeg),0,1);
// a->AddTestIntegrator(new MixedVectorCurlIntegrator(negsigma_cf), nullptr, 0,1);
// a->AddTestIntegrator(new MixedVectorCurlIntegrator(mat_eps_r_cf),
// new MixedVectorCurlIntegrator(mat_eps_i_cf),0,1);
// (M ∇ × F, δG) = (M_r ∇ × F, δG) + i (M_i ∇ × F, δG)
a->AddTestIntegrator(new MixedVectorCurlIntegrator(Mr_cf),
new MixedVectorCurlIntegrator(Mi_cf),0,1);
// --------------------------------------------------------------------------
// i ω μ (∇ × G,δF)
a->AddTestIntegrator(nullptr,new MixedVectorCurlIntegrator(muomeg),1,0);
}
// --------------------------------------------------------------------------
// i ω ϵ (G, ∇ × δF ) - σ (G, ∇ × δF )
// a->AddTestIntegrator(new MixedVectorWeakCurlIntegrator(negsigma_cf),
// new MixedVectorWeakCurlIntegrator(epsomeg),1,0);
// // (Eps^* G, ∇ × δF ) - σ (G, ∇ × δF ) = (Eps_r^T G, ∇ × δF) - i (Eps_i^T G, ∇ × δF) - σ (G, ∇ × δF )
// TransposeMatrixCoefficient mat_eps_rt_cf(mat_eps_r_cf);
// TransposeMatrixCoefficient mat_eps_it_cf(mat_eps_i_cf);
// ScalarMatrixProductCoefficient neg_mat_eps_it_cf(-1.0,mat_eps_it_cf);
// a->AddTestIntegrator(new MixedVectorWeakCurlIntegrator(negsigma_cf), nullptr, 1,0);
// a->AddTestIntegrator(new MixedVectorWeakCurlIntegrator(mat_eps_rt_cf),
// new MixedVectorWeakCurlIntegrator(neg_mat_eps_it_cf), 1,0);
// (M^* G, ∇ × δF ) = (Mr^T G, ∇ × δF) - i (Mi^T G, ∇ × δF)
TransposeMatrixCoefficient Mrt_cf(Mr_cf);
TransposeMatrixCoefficient Mit_cf(Mi_cf);
ScalarMatrixProductCoefficient negMit_cf(-1.0,Mit_cf);
if (graph_norm)
{
a->AddTestIntegrator(new MixedVectorWeakCurlIntegrator(Mrt_cf),
new MixedVectorWeakCurlIntegrator(negMit_cf),1,0);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// ϵ^2 ω^2 (G,δG)
// a->AddTestIntegrator(new VectorFEMassIntegrator(eps2omeg2),nullptr,1,1);
// σ^2(G,δG)
// a->AddTestIntegrator(new VectorFEMassIntegrator(sigma2_cf),nullptr,1,1);
// MatrixProductCoefficient ErErt_cf(mat_eps_r_cf,mat_eps_rt_cf);
// MatrixProductCoefficient EiEit_cf(mat_eps_i_cf,mat_eps_it_cf);
// MatrixProductCoefficient EiErt_cf(mat_eps_i_cf,mat_eps_rt_cf);
// MatrixProductCoefficient ErEit_cf(mat_eps_r_cf,mat_eps_it_cf);
// MatrixSumCoefficient EEr_cf(ErErt_cf,EiEit_cf);
// MatrixSumCoefficient EEi_cf(EiErt_cf,ErEit_cf,1.0,-1.0);
// a->AddTestIntegrator(new VectorFEMassIntegrator(EEr_cf),
// new VectorFEMassIntegrator(EEi_cf),1,1);
// M*M^*(G,δG) = (MrMr^t + MiMi^t) + i (MiMr^t - MrMi^t)
MatrixProductCoefficient MrMrt_cf(Mr_cf,Mrt_cf);
MatrixProductCoefficient MiMit_cf(Mi_cf,Mit_cf);
MatrixProductCoefficient MiMrt_cf(Mi_cf,Mrt_cf);
MatrixProductCoefficient MrMit_cf(Mr_cf,Mit_cf);
MatrixSumCoefficient MMr_cf(MrMrt_cf,MiMit_cf);
MatrixSumCoefficient MMi_cf(MiMrt_cf,MrMit_cf,1.0,-1.0);
const IntegrationRule *irs[Geometry::NumGeom];
int order_quad = 2*order + 2;
for (int i = 0; i < Geometry::NumGeom; ++i)
{
irs[i] = &(IntRules.Get(i, order_quad));
}
const IntegrationRule &ir = IntRules.Get(pmesh.GetElementGeometry(0),
2*test_order + 2);
VectorFEMassIntegrator * integ_r = new VectorFEMassIntegrator(MMr_cf);
integ_r->SetIntegrationRule(ir);
VectorFEMassIntegrator * integ_i = new VectorFEMassIntegrator(MMi_cf);
integ_i->SetIntegrationRule(ir);
if (graph_norm)
{
a->AddTestIntegrator(integ_r,integ_i,1,1);
}
// --------------------------------------------------------------------------
socketstream E_out_r;
socketstream H_out_r;
ParComplexGridFunction E(E_fes);
ParComplexGridFunction H(H_fes);
E.real() = 0.0;
E.imag() = 0.0;
H.real() = 0.0;
H.imag() = 0.0;
ParaViewDataCollection * paraview_dc = nullptr;
if (paraview)
{
paraview_dc = new ParaViewDataCollection(mesh_file, &pmesh);
paraview_dc->SetPrefixPath("ParaViewUWDPG");
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",&E.real());
paraview_dc->RegisterField("E_i",&E.imag());
paraview_dc->RegisterField("H_r",&H.real());
paraview_dc->RegisterField("H_i",&H.imag());
}
// internal bdr attributes
Array<int> internal_bdr({1, 3, 6, 9, 17, 157, 185, 75, 210, 211,
212, 213, 214, 215, 216, 217, 218, 219,
220, 221, 222, 223, 224, 225, 226, 227,
228, 229, 230, 231, 232, 233, 234, 125});
for (int it = 0; it<=pr; it++)
{
Array<int> ess_tdof_list;
Array<int> ess_bdr;
Array<int> one_bdr;
Array<int> negone_bdr;
if (myid == 0)
{
std::cout << "Attributes" << endl;
}
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
// need to exclude these attributes
for (int i = 0; i<internal_bdr.Size(); i++)
{
ess_bdr[internal_bdr[i]-1] = 0;
}
hatE_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
one_bdr = 0;
negone_bdr = 0;
one_bdr[234] = 1;
negone_bdr[235] = 1;
// one_bdr[6] = 1;
// negone_bdr[7] = 1;
}
if (myid == 0)
{
std::cout << "Attributes 2" << endl;
}
// Set up bdr conditions
// shift the ess_tdofs
for (int j = 0; j < ess_tdof_list.Size(); j++)
{
ess_tdof_list[j] += E_fes->GetTrueVSize() + H_fes->GetTrueVSize();
}
Array<int> offsets(5);
offsets[0] = 0;
offsets[1] = E_fes->GetVSize();
offsets[2] = H_fes->GetVSize();
offsets[3] = hatE_fes->GetVSize();
offsets[4] = hatH_fes->GetVSize();
offsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
double * xdata = x.GetData();
ParComplexGridFunction hatE_gf(hatE_fes);
hatE_gf.real().MakeRef(hatE_fes,&xdata[offsets[2]]);
hatE_gf.imag().MakeRef(hatE_fes,&xdata[offsets.Last()+ offsets[2]]);
hatE_gf.ProjectBdrCoefficientTangent(z_one_cf,zero_cf, one_bdr);
hatE_gf.ProjectBdrCoefficientTangent(z_negone_cf,zero_cf, negone_bdr);
if (myid == 0)
{
std::cout << "Assembly started" << endl;
}
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
if (myid == 0)
{
std::cout << "Assembly finished" << endl;
}
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
int num_blocks = BlockA_r->NumRowBlocks();
Array<int> tdof_offsets(2*num_blocks+1);
tdof_offsets[0] = 0;
int skip = (static_cond) ? 0 : 2;
int k = (static_cond) ? 2 : 0;
for (int i=0; i<num_blocks; i++)
{
tdof_offsets[i+1] = trial_fes[i+k]->GetTrueVSize();
tdof_offsets[num_blocks+i+1] = trial_fes[i+k]->GetTrueVSize();
}
tdof_offsets.PartialSum();
BlockOperator blockA(tdof_offsets);
for (int i = 0; i<num_blocks; i++)
{
for (int j = 0; j<num_blocks; j++)
{
blockA.SetBlock(i,j,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i,j+num_blocks,&BlockA_i->GetBlock(i,j), -1.0);
blockA.SetBlock(i+num_blocks,j+num_blocks,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i+num_blocks,j,&BlockA_i->GetBlock(i,j));
}
}
X = 0.;
#ifdef MFEM_USE_MUMPS
if (mumps_solver)
{
// Monolithic real part
Array2D <HypreParMatrix * > Ab_r(num_blocks,num_blocks);
// Monolithic imag part
Array2D <HypreParMatrix * > Ab_i(num_blocks,num_blocks);
for (int i = 0; i<num_blocks; i++)
{
for (int j = 0; j<num_blocks; j++)
{
Ab_r(i,j) = &(HypreParMatrix &)BlockA_r->GetBlock(i,j);
Ab_i(i,j) = &(HypreParMatrix &)BlockA_i->GetBlock(i,j);
}
}
HypreParMatrix * A_r = HypreParMatrixFromBlocks(Ab_r);
HypreParMatrix * A_i = HypreParMatrixFromBlocks(Ab_i);
ComplexHypreParMatrix Acomplex(A_r, A_i,true,true);
HypreParMatrix * A = Acomplex.GetSystemMatrix();
MUMPSSolver mumps(MPI_COMM_WORLD);
mumps.SetPrintLevel(0);
mumps.SetMatrixSymType(MUMPSSolver::MatType::UNSYMMETRIC);
mumps.SetOperator(*A);
mumps.Mult(B,X);
delete A;
}
#else
if (mumps_solver)
{
MFEM_WARNING("MFEM compiled without mumps. Switching to an iterative solver");
}
mumps_solver = false;
#endif
if (!mumps_solver)
{
BlockDiagonalPreconditioner M(tdof_offsets);
if (!static_cond)
{
HypreBoomerAMG * solver_E = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(0,0));
solver_E->SetPrintLevel(0);
solver_E->SetSystemsOptions(dim);
HypreBoomerAMG * solver_H = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(1,1));
solver_H->SetPrintLevel(0);
solver_H->SetSystemsOptions(dim);
M.SetDiagonalBlock(0,solver_E);
M.SetDiagonalBlock(1,solver_H);
M.SetDiagonalBlock(num_blocks,solver_E);
M.SetDiagonalBlock(num_blocks+1,solver_H);
}
HypreAMS * solver_hatE = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(skip,
skip), hatE_fes);
HypreAMS * solver_hatH = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(
skip+1,skip+1), hatH_fes);
solver_hatE->SetPrintLevel(0);
solver_hatH->SetPrintLevel(0);
M.SetDiagonalBlock(skip,solver_hatE);
M.SetDiagonalBlock(skip+1,solver_hatH);
M.SetDiagonalBlock(skip+num_blocks,solver_hatE);
M.SetDiagonalBlock(skip+num_blocks+1,solver_hatH);
if (myid == 0)
{
std::cout << "PCG iterations" << endl;
}
CGSolver cg(MPI_COMM_WORLD);
// GMRESSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-6);
cg.SetMaxIter(500);
cg.SetPrintLevel(1);
cg.SetPreconditioner(M);
cg.SetOperator(blockA);
cg.Mult(B, X);
for (int i = 0; i<num_blocks; i++)
{
delete &M.GetDiagonalBlock(i);
}
int num_iter = cg.GetNumIterations();
}
a->RecoverFEMSolution(X,x);
E.real().MakeRef(E_fes,x.GetData());
E.imag().MakeRef(E_fes,&x.GetData()[offsets.Last()]);
H.real().MakeRef(H_fes,&x.GetData()[offsets[1]]);
H.imag().MakeRef(H_fes,&x.GetData()[offsets.Last()+offsets[1]]);
int dofs = 0;
for (int i = 0; i<trial_fes.Size(); i++)
{
dofs += trial_fes[i]->GlobalTrueVSize();
}
if (visualization)
{
const char * keys = (it == 0 && dim == 2) ? "jRcml\n" : nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, E.real(),
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
common::VisualizeField(H_out_r,vishost, visport, H.real(),
"Numerical Magnetic field (real part)", 501, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(it);
paraview_dc->SetTime((double)it);
paraview_dc->Save();
}
if (it == pr)
{
break;
}
pmesh.UniformRefinement();
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
}
if (paraview)
{
delete paraview_dc;
}
delete a;
delete F_fec;
delete G_fec;
delete hatH_fes;
delete hatH_fec;
delete hatE_fes;
delete hatE_fec;
delete H_fec;
delete E_fec;
delete H_fes;
delete E_fes;
return 0;
}
@@ -0,0 +1,833 @@
// MFEM Ultraweak DPG Maxwell parallel example
//
// Description:
// This example code demonstrates the use of MFEM to define and solve
// the "ultraweak" (UW) DPG formulation for the Maxwell problem
// ∇×(1/μ ∇×E) - ω² ϵ E = Ĵ , in Ω
// E×n = E₀ , on ∂Ω
// The DPG UW deals with the First Order System
// i ω μ H + ∇ × E = 0, in Ω
// -i ω ϵ E + ∇ × H = 0, in Ω
// E × n = E_0, on ∂Ω
// The ultraweak-DPG formulation is obtained by integration by parts of both
// equations and the introduction of trace unknowns on the mesh skeleton
// in 2D
// E is vector valued and H is scalar.
// (∇ × E, F) = (E, ∇ × F) + < n × E , F>
// or (∇ ⋅ AE , F) = (AE, ∇ F) + < AE ⋅ n, F>
// where A = [0 1; -1 0];
// E ∈ (L²(Ω))² , H ∈ L²(Ω)
// Ê ∈ H^-1/2(Γₕ), Ĥ ∈ H^1/2(Γₕ)
// i ω μ (H,F) + (E, ∇ × F) + < AÊ, F > = 0, ∀ F ∈ H¹
// -i ω ϵ (E,G) + (H,∇ × G) + < Ĥ, G × n > = (J,G) ∀ G ∈ H(curl,Ω)
// Ê = E₀ on ∂Ω
// -------------------------------------------------------------------------
// | | E | H | Ê | Ĥ | RHS |
// -------------------------------------------------------------------------
// | F | (E,∇ × F) | i ω μ (H,F) | < Ê, F > | | |
// | | | | | | |
// | G | -i ω ϵ (E,G) | (H,∇ × G) | | < Ĥ, G × n > | (J,G) |
// where (F,G) ∈ H¹ × H(curl,Ω)
#include "mfem.hpp"
#include "../../util/pcomplexweakform.hpp"
#include "../../util/utils.hpp"
#include "../../util/maxwell_utils.hpp"
#include "../../../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
#include <ctime>
#include <string>
#include <sstream>
#include <cstring>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
int num_procs = Mpi::WorldSize();
Hypre::Init();
// fine mesh (trianles)
// default mesh
const char *mesh_file = "data/mesh-tri34K.mesh";
// coarse mesh (triangles)
// const char *mesh_file = "data/mesh-tri11K.mesh";
// coarse mesh (quadrilaterals)
// const char *mesh_file = "data/mesh-quad5K.mesh";
// epsilon tensor
const char * eps_r_file = nullptr;
const char * eps_i_file = nullptr;
int order = 2;
int delta_order = 1;
int par_ref_levels = 0;
int amr_ref_levels = 0;
// real_t rnum=4.6e9;
// real_t mu = 1.257e-6/factor;
// real_t epsilon_scale = 8.8541878128e-12*factor;
real_t rnum=4.6;
real_t mu = 1.257;
real_t epsilon_scale = 8.8541878128;
// ∇×(1/μ ∇×E) - ω² ϵ E = Ĵ , in Ω
// 1/1.257e-6 - 2π*2π*4.6*4.6e18*8.8541878128e-12
// 1/1.257e-6 - 2π*2π*4.6*4.6*8.8541878128e6
// 1e6/1.257 - 2π*2π*4.6*4.6*8.8541878128e6
// 1/1.257 - 2π*2π*4.6*4.6*8.8541878128
bool visualization = false;
bool static_cond = false;
bool graph_norm = true;
bool mumps_solver = false;
real_t theta = 0.0;
bool paraview = false;
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(&par_ref_levels, "-pr", "--parallel-refinement-levels",
"Number of parallel refinement levels.");
args.AddOption(&amr_ref_levels, "-amr", "--parallel-amr-refinement-levels",
"Number of parallel AMR refinement levels.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&delta_order, "-do", "--delta_order",
"Order enrichment for DPG test space.");
args.AddOption(&theta, "-theta", "--theta",
"Theta parameter for AMR");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&graph_norm, "-graph", "--graph-norm", "-no-gn",
"--no-graph-norm", "Enable adjoint graph norm.");
#ifdef MFEM_USE_MUMPS
args.AddOption(&mumps_solver, "-mumps", "--mumps-solver", "-no-mumps",
"--no-mumps-solver", "Use the MUMPS Solver.");
#endif
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
if (strcmp(mesh_file, "data/mesh-tri34K.mesh") == 0)
{
eps_r_file = "data/eps-tri34K_r.gf";
eps_i_file = "data/eps-tri34K_i.gf";
}
else if (strcmp(mesh_file, "data/mesh-tri11K.mesh") == 0)
{
eps_r_file = "data/eps-tri11K_r.gf";
eps_i_file = "data/eps-tri11K_i.gf";
}
else if (strcmp(mesh_file, "data/mesh-quad5K.mesh") == 0)
{
eps_r_file = "data/eps-quad5K_r.gf";
eps_i_file = "data/eps-quad5K_i.gf";
}
else
{
MFEM_ABORT("Unknown mesh file: " + string(mesh_file));
}
real_t omega = 2.*M_PI*rnum;
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
Array<int> int_bdr_attr;
for (int i = 0; i < mesh.GetNBE(); i++)
{
if (mesh.FaceIsInterior(mesh.GetBdrElementFaceIndex(i)))
{
int_bdr_attr.Append(mesh.GetBdrAttribute(i));
}
}
// mesh.RemoveInternalBoundaries();
mesh.EnsureNCMesh(true);
int * partitioning = mesh.GeneratePartitioning(num_procs);
ParMesh pmesh(MPI_COMM_WORLD, mesh, partitioning);
EpsilonMatrixCoefficient eps_r_cf(eps_r_file,&mesh,&pmesh, epsilon_scale);
EpsilonMatrixCoefficient eps_i_cf(eps_i_file,&mesh,&pmesh, epsilon_scale);
mesh.Clear();
for (int i = 0; i<par_ref_levels; i++)
{
pmesh.UniformRefinement();
eps_r_cf.Update();
eps_i_cf.Update();
}
// eps_r_cf.VisualizeMatrixCoefficient();
// eps_i_cf.VisualizeMatrixCoefficient();
// return 0;
// Matrix Coefficient (M = -i\omega \epsilon);
// M = -i * omega * (eps_r + i eps_i)
// = omega eps_i + i (-omega eps_r)
ScalarMatrixProductCoefficient Mr_cf(omega,eps_i_cf);
ScalarMatrixProductCoefficient Mi_cf(-omega,eps_r_cf);
ConstantCoefficient one(1.0);
ConstantCoefficient muomeg(mu*omega);
ConstantCoefficient mu2omeg2(mu*mu*omega*omega);
ConstantCoefficient negmuomeg(-mu*omega);
DenseMatrix rot_mat(2);
rot_mat(0,0) = 0.; rot_mat(0,1) = 1.;
rot_mat(1,0) = -1.; rot_mat(1,1) = 0.;
MatrixConstantCoefficient rot(rot_mat);
MatrixProductCoefficient Mrot_r(Mr_cf,rot);
MatrixProductCoefficient Mrot_i(Mi_cf,rot);
ScalarMatrixProductCoefficient negMrot_i(-1.0,Mrot_i);
TransposeMatrixCoefficient Mrt_cf(Mr_cf);
TransposeMatrixCoefficient Mit_cf(Mi_cf);
MatrixProductCoefficient MrMrt_cf(Mr_cf,Mrt_cf);
MatrixProductCoefficient MiMit_cf(Mi_cf,Mit_cf);
MatrixProductCoefficient MiMrt_cf(Mi_cf,Mrt_cf);
MatrixProductCoefficient MrMit_cf(Mr_cf,Mit_cf);
MatrixSumCoefficient MMr_cf(MrMrt_cf,MiMit_cf);
MatrixSumCoefficient MMi_cf(MiMrt_cf,MrMit_cf,1.0,-1.0);
mesh.Clear();
// Define spaces
enum TrialSpace
{
E_space = 0,
H_space = 1,
hatE_space = 2,
hatH_space = 3
};
enum TestSpace
{
F_space = 0,
G_space = 1
};
int dimc = (dim == 3) ? 3 : 1;
int test_order = order+delta_order;
// Vector L2 L2 space for E
FiniteElementCollection *E_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *E_fes = new ParFiniteElementSpace(&pmesh,E_fec,dim);
// Vector L2 space for H
FiniteElementCollection *H_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *H_fes = new ParFiniteElementSpace(&pmesh,H_fec, dimc);
// H^-1/2 (curl) space for Ê
FiniteElementCollection * hatE_fec = new RT_Trace_FECollection(order-1,dim);
FiniteElementCollection * hatH_fec = new H1_Trace_FECollection(order,dim);
FiniteElementCollection * F_fec = new H1_FECollection(test_order, dim);
ParFiniteElementSpace *hatE_fes = new ParFiniteElementSpace(&pmesh,hatE_fec);
ParFiniteElementSpace *hatH_fes = new ParFiniteElementSpace(&pmesh,hatH_fec);
FiniteElementCollection * G_fec = new ND_FECollection(test_order, dim);
Array<ParFiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(E_fes);
trial_fes.Append(H_fes);
trial_fes.Append(hatE_fes);
trial_fes.Append(hatH_fes);
test_fec.Append(F_fec);
test_fec.Append(G_fec);
int gdofs = 0;
for (int i = 0; i<trial_fes.Size(); i++)
{
gdofs += trial_fes[i]->GlobalTrueVSize();
}
if (Mpi::Root())
{
mfem::out << "Global number of dofs = " << gdofs << endl;
}
ParComplexDPGWeakForm * a = new ParComplexDPGWeakForm(trial_fes,test_fec);
a->StoreMatrices(); // needed for AMR
const IntegrationRule *irs[Geometry::NumGeom];
int order_quad = 2*order + 2;
for (int i = 0; i < Geometry::NumGeom; ++i)
{
irs[i] = &(IntRules.Get(i, order_quad));
}
const IntegrationRule &ir = IntRules.Get(pmesh.GetElementGeometry(0),
2*test_order + 2);
// (E,∇ × F)
MixedCurlIntegrator * curl_integ = new MixedCurlIntegrator(one);
curl_integ->SetIntRule(&ir);
a->AddTrialIntegrator(new TransposeIntegrator(curl_integ), nullptr,
TrialSpace::E_space,
TestSpace::F_space);
// (M E , G) = (M_r E, G) + i (M_i E, G) = (E, M_rt G) + i (E, Mit G)
// = (M_rt G, E)^T + i (Mit G, E)^T
VectorFEMassIntegrator * Mrt_cf_integ = new VectorFEMassIntegrator(Mrt_cf);
VectorFEMassIntegrator * Mit_cf_integ = new VectorFEMassIntegrator(Mit_cf);
Mrt_cf_integ->SetIntegrationRule(ir);
Mit_cf_integ->SetIntegrationRule(ir);
a->AddTrialIntegrator(
new TransposeIntegrator(Mrt_cf_integ),
new TransposeIntegrator(Mit_cf_integ),
TrialSpace::E_space, TestSpace::G_space);
// (H,∇ × G)
MixedCurlIntegrator * curl_integ_H = new MixedCurlIntegrator(one);
curl_integ_H->SetIntRule(&ir);
a->AddTrialIntegrator(new TransposeIntegrator(curl_integ_H), nullptr,
TrialSpace::H_space, TestSpace::G_space);
// < n×Ĥ ,G>
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,
TrialSpace::hatH_space, TestSpace::G_space);
// i ω μ (H, F)
MixedScalarMassIntegrator * muomeg_integ = new MixedScalarMassIntegrator(
muomeg);
muomeg_integ->SetIntRule(&ir);
a->AddTrialIntegrator(nullptr,muomeg_integ,
TrialSpace::H_space, TestSpace::F_space);
// < n×Ê,F>
a->AddTrialIntegrator(new TraceIntegrator,nullptr,
TrialSpace::hatE_space, TestSpace::F_space);
// test integrators
// (∇×G ,∇× δG)
CurlCurlIntegrator * curlcurl_integ = new CurlCurlIntegrator(one);
curlcurl_integ->SetIntRule(&ir);
a->AddTestIntegrator(curlcurl_integ,nullptr,
TestSpace::G_space,TestSpace::G_space);
// (G,δG)
VectorFEMassIntegrator * vfemass_integ = new VectorFEMassIntegrator(one);
vfemass_integ->SetIntegrationRule(ir);
a->AddTestIntegrator(vfemass_integ,nullptr,
TestSpace::G_space,TestSpace::G_space);
// (∇F,∇δF)
DiffusionIntegrator * diff_integ = new DiffusionIntegrator(one);
diff_integ->SetIntRule(&ir);
a->AddTestIntegrator(diff_integ,nullptr,
TestSpace::F_space, TestSpace::F_space);
// (F,δF)
MassIntegrator * mass_integ = new MassIntegrator(one);
mass_integ->SetIntRule(&ir);
a->AddTestIntegrator(mass_integ,nullptr,
TestSpace::F_space, TestSpace::F_space);
if (graph_norm)
{
// μ^2 ω^2 (F,δF)
MassIntegrator * mu2omeg2_integ = new MassIntegrator(mu2omeg2);
mu2omeg2_integ->SetIntRule(&ir);
a->AddTestIntegrator(mu2omeg2_integ,nullptr,
TestSpace::F_space, TestSpace::F_space);
// -i ω μ (F,∇ × δG) = i (F, -ω μ ∇ × δ G)
MixedCurlIntegrator * negmuomeg_integ = new MixedCurlIntegrator(negmuomeg);
negmuomeg_integ->SetIntRule(&ir);
a->AddTestIntegrator(nullptr, new TransposeIntegrator(negmuomeg_integ),
TestSpace::F_space, TestSpace::G_space);
// (M ∇ × F, δG) = (M_r ∇ × F, δG) + i (M_i ∇ × F, δG)
// = (M_r A ∇ F, δG) + i (M_i A ∇ F, δG), A = [0 1; -1; 0]
MixedVectorGradientIntegrator * Mrot_r_integ = new
MixedVectorGradientIntegrator(Mrot_r);
MixedVectorGradientIntegrator * Mrot_i_integ = new
MixedVectorGradientIntegrator(Mrot_i);
Mrot_r_integ->SetIntRule(&ir);
Mrot_i_integ->SetIntRule(&ir);
a->AddTestIntegrator(Mrot_r_integ, Mrot_i_integ,
TestSpace::F_space, TestSpace::G_space);
// i ω μ (∇ × G,δF) = i (ω μ ∇ × G, δF )
MixedCurlIntegrator * muomeg_integ = new MixedCurlIntegrator(muomeg);
muomeg_integ->SetIntRule(&ir);
a->AddTestIntegrator(nullptr,muomeg_integ,
TestSpace::G_space, TestSpace::F_space);
// (M^* G, ∇ × δF ) = (G, Mr A ∇ δF) - i (G, Mi A ∇ δF)
MixedVectorGradientIntegrator * Mrot_r_integ2 = new
MixedVectorGradientIntegrator(Mrot_r);
MixedVectorGradientIntegrator * negMrot_i_integ = new
MixedVectorGradientIntegrator(negMrot_i);
Mrot_r_integ2->SetIntRule(&ir);
negMrot_i_integ->SetIntRule(&ir);
a->AddTestIntegrator(new TransposeIntegrator(Mrot_r_integ2),
new TransposeIntegrator(negMrot_i_integ),
TestSpace::G_space, TestSpace::F_space);
// M*M^*(G,δG) = (MrMr^t + MiMi^t) + i (MiMr^t - MrMi^t)
VectorFEMassIntegrator * MMr_integ = new VectorFEMassIntegrator(MMr_cf);
VectorFEMassIntegrator * MMi_integ = new VectorFEMassIntegrator(MMi_cf);
MMr_integ->SetIntegrationRule(ir);
MMi_integ->SetIntegrationRule(ir);
a->AddTestIntegrator(MMr_integ, MMi_integ,
TestSpace::G_space,
TestSpace::G_space);
}
socketstream E_out_r;
socketstream H_out_r;
socketstream E_theta_out_r;
ParComplexGridFunction E(E_fes);
ParComplexGridFunction H(H_fes);
E.real() = 0.0;
E.imag() = 0.0;
H.real() = 0.0;
H.imag() = 0.0;
L2_FECollection L2fec(order, dim);
ParFiniteElementSpace L2_fes(&pmesh, &L2fec);
ParGridFunction E_theta_r(&L2_fes);
ParGridFunction E_theta_i(&L2_fes);
ParGridFunction E_theta(&L2_fes);
E_theta = 0.0;
ParaViewDataCollection * paraview_dc = nullptr;
ParaViewDataCollection * paraview_tdc = nullptr;
// Create ParaView directory and file
std::string output_dir = "ParaView/UW/2D" + GetTimestamp();
if (Mpi::Root())
{
WriteParametersToFile(args, output_dir);
}
if (paraview)
{
std::ostringstream paraview_file_name;
std::string filename = GetFilename(mesh_file);
paraview_file_name << filename
<< "_par_ref_" << par_ref_levels
<< "_order_" << order;
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
paraview_dc->SetPrefixPath(output_dir);
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",&E.real());
paraview_dc->RegisterField("E_i",&E.imag());
paraview_dc->RegisterField("H_r",&H.real());
paraview_dc->RegisterField("H_i",&H.imag());
paraview_dc->RegisterField("E_theta_r",&E_theta_r);
paraview_dc->RegisterField("E_theta_i",&E_theta_i);
std::ostringstream paraview_file_name_th;
paraview_file_name_th << filename
<< "_par_ref_" << par_ref_levels
<< "_order_" << order
<< "th";
paraview_tdc = new ParaViewDataCollection(paraview_file_name_th.str(), &pmesh);
paraview_tdc->SetPrefixPath(output_dir);
paraview_tdc->SetLevelsOfDetail(order);
paraview_tdc->SetCycle(0);
paraview_tdc->SetDataFormat(VTKFormat::BINARY);
paraview_tdc->SetHighOrderOutput(true);
paraview_tdc->SetTime(0.0); // set the time
paraview_tdc->RegisterField("E_theta_t",&E_theta);
}
real_t res0 = 0.;
real_t err0 = 0.;
int dof0 = 0; // init to suppress gcc warning
Array<int> elements_to_refine;
if (static_cond) { a->EnableStaticCondensation(); }
for (int it = 0; it<=amr_ref_levels; it++)
{
a->Assemble();
Array<int> ess_tdof_list;
Array<int> ess_bdr;
Array<int> one_r_bdr;
Array<int> one_i_bdr;
Array<int> negone_r_bdr;
Array<int> negone_i_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_r_bdr.SetSize(pmesh.bdr_attributes.Max());
one_i_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_r_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_i_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
// remove internal boundaries
for (int i = 0; i<int_bdr_attr.Size(); i++)
{
ess_bdr[int_bdr_attr[i]-1] = 0;
}
hatE_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
one_r_bdr = 0; one_i_bdr = 0;
negone_r_bdr = 0; negone_i_bdr = 0;
// attr = 30,2 (real)
one_r_bdr[30-1] = 1; one_r_bdr[2-1] = 1;
// attr = 26,6 (imag)
one_i_bdr[26-1] = 1; one_i_bdr[6-1] = 1;
// attr = 22,10 (real)
negone_r_bdr[22-1] = 1; negone_r_bdr[10-1] = 1;
// attr = 18,14 (imag)
negone_i_bdr[18-1] = 1; negone_i_bdr[14-1] = 1;
}
// Set up bdr conditions
// shift the ess_tdofs
for (int j = 0; j < ess_tdof_list.Size(); j++)
{
ess_tdof_list[j] += E_fes->GetTrueVSize() + H_fes->GetTrueVSize();
}
Array<int> offsets(5);
offsets[0] = 0;
offsets[1] = E_fes->GetVSize();
offsets[2] = H_fes->GetVSize();
offsets[3] = hatE_fes->GetVSize();
offsets[4] = hatH_fes->GetVSize();
offsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
real_t * xdata = x.GetData();
ParComplexGridFunction hatE_gf(hatE_fes);
hatE_gf.real().MakeRef(hatE_fes,&xdata[offsets[2]]);
hatE_gf.imag().MakeRef(hatE_fes,&xdata[offsets.Last()+ offsets[2]]);
Vector zero(dim); zero = 0.0;
VectorConstantCoefficient zero_cf(zero);
// rotate the vector
// (x,y) -> (y,-x)
Vector rot_one_x(dim); rot_one_x = 0.0; rot_one_x(1) = -1.0;
Vector rot_negone_x(dim); rot_negone_x = 0.0; rot_negone_x(1) = 1.0;
VectorConstantCoefficient rot_one_x_cf(rot_one_x);
VectorConstantCoefficient rot_negone_x_cf(rot_negone_x);
hatE_gf.ProjectBdrCoefficientNormal(rot_one_x_cf,zero_cf, one_r_bdr);
hatE_gf.ProjectBdrCoefficientNormal(rot_negone_x_cf,zero_cf, negone_r_bdr);
hatE_gf.ProjectBdrCoefficientNormal(zero_cf,rot_one_x_cf, one_i_bdr);
hatE_gf.ProjectBdrCoefficientNormal(zero_cf,rot_negone_x_cf, negone_i_bdr);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
int num_blocks = BlockA_r->NumRowBlocks();
Array<int> tdof_offsets(2*num_blocks+1);
tdof_offsets[0] = 0;
int skip = (static_cond) ? 0 : 2;
int k = (static_cond) ? 2 : 0;
for (int i=0; i<num_blocks; i++)
{
tdof_offsets[i+1] = trial_fes[i+k]->GetTrueVSize();
tdof_offsets[num_blocks+i+1] = trial_fes[i+k]->GetTrueVSize();
}
tdof_offsets.PartialSum();
BlockOperator blockA(tdof_offsets);
for (int i = 0; i<num_blocks; i++)
{
for (int j = 0; j<num_blocks; j++)
{
blockA.SetBlock(i,j,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i,j+num_blocks,&BlockA_i->GetBlock(i,j), -1.0);
blockA.SetBlock(i+num_blocks,j+num_blocks,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i+num_blocks,j,&BlockA_i->GetBlock(i,j));
}
}
X = 0.;
#ifdef MFEM_USE_MUMPS
if (mumps_solver)
{
// Monolithic real part
Array2D<const HypreParMatrix * > Ab_r(num_blocks,num_blocks);
// Monolithic imag part
Array2D<const HypreParMatrix * > Ab_i(num_blocks,num_blocks);
for (int i = 0; i<num_blocks; i++)
{
for (int j = 0; j<num_blocks; j++)
{
Ab_r(i,j) = &(HypreParMatrix &)BlockA_r->GetBlock(i,j);
Ab_i(i,j) = &(HypreParMatrix &)BlockA_i->GetBlock(i,j);
}
}
HypreParMatrix * A_r = HypreParMatrixFromBlocks(Ab_r);
HypreParMatrix * A_i = HypreParMatrixFromBlocks(Ab_i);
ComplexHypreParMatrix Acomplex(A_r, A_i,true,true);
HypreParMatrix * A = Acomplex.GetSystemMatrix();
MUMPSSolver mumps(MPI_COMM_WORLD);
mumps.SetPrintLevel(0);
mumps.SetMatrixSymType(MUMPSSolver::MatType::UNSYMMETRIC);
mumps.SetOperator(*A);
mumps.Mult(B,X);
delete A;
}
#else
if (mumps_solver)
{
MFEM_WARNING("MFEM compiled without mumps. Switching to an iterative solver");
}
mumps_solver = false;
#endif
int num_iter = -1;
if (!mumps_solver)
{
BlockDiagonalPreconditioner M(tdof_offsets);
// BlockTriangularSymmetricPreconditioner M(tdof_offsets);
// M.SetOperator(blockA);
// int nblocks = blockA.NumRowBlocks();
// for (int i = 0; i<nblocks; i++)
// {
// for (int j = 0; j<nblocks; j++)
// {
// if (i != j)
// {
// M.SetBlock(i,j,&blockA.GetBlock(i,j));
// }
// }
// }
if (!static_cond)
{
HypreBoomerAMG * solver_E = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(0,0));
solver_E->SetPrintLevel(0);
solver_E->SetSystemsOptions(dim);
HypreBoomerAMG * solver_H = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(1,1));
solver_H->SetPrintLevel(0);
// solver_H->SetSystemsOptions(dim);
M.SetDiagonalBlock(0,solver_E);
M.SetDiagonalBlock(1,solver_H);
M.SetDiagonalBlock(num_blocks,solver_E);
M.SetDiagonalBlock(num_blocks+1,solver_H);
}
HypreAMS * solver_hatE = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(skip,
skip), hatE_fes);
HypreBoomerAMG * solver_hatH = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(
skip+1,skip+1));
solver_hatE->SetPrintLevel(0);
solver_hatH->SetPrintLevel(0);
solver_hatH->SetRelaxType(88);
M.SetDiagonalBlock(skip,solver_hatE);
M.SetDiagonalBlock(skip+1,solver_hatH);
M.SetDiagonalBlock(skip+num_blocks,solver_hatE);
M.SetDiagonalBlock(skip+num_blocks+1,solver_hatH);
if (myid == 0)
{
std::cout << "PCG iterations" << endl;
}
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-7);
cg.SetMaxIter(1500);
cg.SetPrintLevel(3);
cg.SetPreconditioner(M);
cg.SetOperator(blockA);
cg.Mult(B, X);
for (int i = 0; i<num_blocks; i++)
{
delete &M.GetDiagonalBlock(i);
}
num_iter = cg.GetNumIterations();
}
a->RecoverFEMSolution(X,x);
Vector & residuals = a->ComputeResidual(x);
real_t residual = residuals.Norml2();
real_t maxresidual = residuals.Max();
real_t globalresidual = residual * residual;
MPI_Allreduce(MPI_IN_PLACE, &maxresidual, 1, MPITypeMap<real_t>::mpi_type,
MPI_MAX, MPI_COMM_WORLD);
MPI_Allreduce(MPI_IN_PLACE, &globalresidual, 1,
MPITypeMap<real_t>::mpi_type, MPI_SUM, MPI_COMM_WORLD);
globalresidual = sqrt(globalresidual);
E.real().MakeRef(E_fes,x.GetData());
E.imag().MakeRef(E_fes,&x.GetData()[offsets.Last()]);
H.real().MakeRef(H_fes,&x.GetData()[offsets[1]]);
H.imag().MakeRef(H_fes,&x.GetData()[offsets.Last()+offsets[1]]);
int dofs = 0;
for (int i = 0; i<trial_fes.Size(); i++)
{
dofs += trial_fes[i]->GlobalTrueVSize();
}
real_t rate_res = (it) ? dim*log(res0/globalresidual)/log((
real_t)dof0/dofs) : 0.0;
res0 = globalresidual;
dof0 = dofs;
if (myid == 0)
{
std::ios oldState(nullptr);
oldState.copyfmt(std::cout);
std::cout << std::right << std::setw(5) << it << " | "
<< std::setw(10) << dof0 << " | "
<< std::setprecision(1) << std::fixed
<< std::setw(4) << 2.0*rnum << " π | "
<< std::setprecision(3);
std::cout << std::setprecision(3)
<< std::setw(10) << std::scientific << res0 << " | "
<< std::setprecision(2)
<< std::setw(6) << std::fixed << rate_res << " | "
<< std::setw(6) << std::fixed << num_iter << " | "
<< std::endl;
std::cout.copyfmt(oldState);
}
AzimuthalECoefficient az_e_r(&E.real());
AzimuthalECoefficient az_e_i(&E.imag());
E_theta_r.ProjectCoefficient(az_e_r);
E_theta_i.ProjectCoefficient(az_e_i);
if (visualization)
{
const char * keys = nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, E.real(),
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
common::VisualizeField(H_out_r,vishost, visport, H.real(),
"Numerical Magnetic field (real part)", 501, 0, 500, 500, keys);
common::VisualizeField(E_theta_out_r,vishost, visport, E_theta_r,
"Numerical Electric field (Azimuthal-real)", 501, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(it);
paraview_dc->SetTime((real_t)it);
paraview_dc->Save();
}
if (it == amr_ref_levels)
{
int num_frames = 32;
for (int i = 0; i<num_frames; i++)
{
real_t t = (real_t)(i % num_frames) / num_frames;
add(cos(real_t(2.0*M_PI)*t), E_theta_r,
sin(real_t(2.0*M_PI)*t), E_theta_i, E_theta);
paraview_tdc->SetCycle(i);
paraview_tdc->SetTime(t);
paraview_tdc->Save();
}
delete paraview_tdc;
break;
}
if (theta > 0.0)
{
elements_to_refine.SetSize(0);
for (int iel = 0; iel<pmesh.GetNE(); iel++)
{
if (residuals[iel] > theta * maxresidual)
{
elements_to_refine.Append(iel);
}
}
pmesh.GeneralRefinement(elements_to_refine,1,1);
}
else
{
pmesh.UniformRefinement();
}
eps_r_cf.Update();
eps_i_cf.Update();
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
L2_fes.Update();
E_theta_r.Update();
E_theta_i.Update();
E_theta.Update();
a->Update();
}
delete a;
delete F_fec;
delete G_fec;
delete hatH_fes;
delete hatH_fec;
delete hatE_fes;
delete hatE_fec;
delete H_fec;
delete E_fec;
delete H_fes;
delete E_fes;
return 0;
}
@@ -0,0 +1,676 @@
// srun -n 512 ./pmaxwell-uw-tokamak -o 4 -sc -prob 2 -paraview
// srun -n 512 ./pmaxwell-uw-tokamak -o 4 -sc -prob 4 -paraview
// Description:
// This example code demonstrates the use of MFEM to define and solve
// the "ultraweak" (UW) DPG formulation for the Maxwell problem
// ∇×(1/μ ∇×E) - (ω^2 ϵ + i ω σ) E = Ĵ , in Ω
// E×n = E_0, on ∂Ω
// The DPG UW deals with the First Order System
// i ω μ H + ∇ × E = 0, in Ω (Faraday's law)
// M E + ∇ × H = J, in Ω (Ampere's law)
// E × n = E_0, on ∂Ω
// Note: Ĵ = -iωJ
// where M = -(i ω ϵI + σI)
// The ultraweak-DPG formulation is obtained by integration by parts of both
// equations and the introduction of trace unknowns on the mesh skeleton
// in 3D
// E,H ∈ (L^2(Ω))^3
// Ê ∈ H_0^1/2(Ω)(curl, Γ_h), Ĥ ∈ H^-1/2(curl, Γ_h)
// i ω μ (H,F) + (E,∇ × F) + < Ê, F × n > = 0, ∀ F ∈ H(curl,Ω)
// M (E,G) + (H,∇ × G) + < Ĥ, G × n > = (J,G) ∀ G ∈ H(curl,Ω)
// Ê × n = E_0 on ∂Ω
// -------------------------------------------------------------------------
// | | E | H | Ê | Ĥ | RHS |
// -------------------------------------------------------------------------
// | F | (E,∇ × F) | i ω μ (H,F) | < n × Ê, F > | | |
// | | | | | | |
// | G | (ME,G) | (H,∇ × G) | | < n × Ĥ, G > | (J,G) |
// where (F,G) ∈ H(curl,Ω) × H(curl,Ω)
// Here we use the "Adjoint Graph" norm on the test space i.e.,
// ||(F,G)||^2_V = ||A^*(F,G)||^2 + ||(F,G)||^2 where A is the
// maxwell operator defined by (1)
#include "mfem.hpp"
#include "../../util/pcomplexweakform.hpp"
#include "../../util/preconditioners.hpp"
#include "../../../common/mfem-common.hpp"
#include "../../util/maxwell_utils.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
int order = 1;
int delta_order = 1;
bool visualization = false;
real_t rnum=5.0;
bool static_cond = false;
int pr = 0;
bool paraview = false;
real_t mu = 1.0;
real_t epsilon = 1.0;
real_t sigma = 0.1;
bool graph_norm = true;
int prob = 0;
bool pmg = false;
int pmg_levels = -1;
real_t relax_factor = 2.0/3;
OptionsParser args(argc, argv);
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree)");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&epsilon, "-eps", "--permittivity",
"Permittivity of free space (or mass constant).");
args.AddOption(&sigma, "-sigma", "--sigma",
"conductivity");
args.AddOption(&delta_order, "-do", "--delta_order",
"Order enrichment for DPG test space.");
args.AddOption(&pr, "-pref", "--parallel_ref",
"Number of parallel refinements.");
args.AddOption(&prob, "-prob", "--problem",
"Choice of problem: 0: tet mesh eps = 1, 1: hex mesh eps = 1 , 2: tet-mesh eps from file,",
"3: hex-mesh eps from file, 4: 400k-tet-mesh eps from file");
args.AddOption(&pmg, "-pmg", "--p-refinement-multigrid", "-no-pmg",
"--no-p-refinement-multigrid", "Enable P-Refinement Multigrid.");
args.AddOption(&pmg_levels, "-pmgl","--p-refinement-multigrid-levels",
"Number of levels for P-Refinement Multigrid.");
args.AddOption(&relax_factor, "-rf", "--relaxation-factor",
"Relaxation factor for the p-multigrid smoother.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&graph_norm, "-graph", "--graph-norm", "-no-gn",
"--no-graph-norm", "Enable adjoint graph norm.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
const char *mesh_file = nullptr;
if (prob == 0 || prob == 2)
{
mesh_file = "meshes/tokamak-tet.mesh";
if (prob == 2)
{
rnum=50.0e6;
mu = 1.257e-6;
sigma = 0.0;
}
}
else if (prob == 1 || prob == 3)
{
mesh_file = "meshes/tokamak-hex.mesh";
if (prob == 3)
{
rnum=50.0e6;
mu = 1.257e-6;
sigma = 0.0;
}
}
else if (prob == 4)
{
mesh_file = "meshes/tokamak-400K-tet-new.mesh";
rnum = 54.0e6;
mu = 1.257e-6;
sigma = 0.0;
}
else
{
MFEM_ABORT("No valid problem choice given");
}
real_t omega = 2.*M_PI*rnum;
Mesh mesh(mesh_file, 1, 1);
mesh.RemoveInternalBoundaries();
int dim = mesh.Dimension();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
DenseMatrix mat_eps_i(dim); mat_eps_i = 0.0;
DenseMatrix mat_eps_r(dim); mat_eps_r = 0.0;
mat_eps_r(0,0) = epsilon;
mat_eps_r(1,1) = epsilon;
mat_eps_r(2,2) = epsilon;
DenseMatrix I(dim); I = 0.0;
I(0,0) = 1.0;
I(1,1) = 1.0;
I(2,2) = 1.0;
MatrixConstantCoefficient identity_cf(I);
MatrixCoefficient * eps_r_cf = nullptr;
MatrixCoefficient * eps_i_cf = nullptr;
if (prob == 0 || prob == 1)
{
eps_r_cf = new MatrixConstantCoefficient(mat_eps_r);
eps_i_cf = new MatrixConstantCoefficient(mat_eps_i);
}
else
{
const char * eps_r_file = nullptr;
const char * eps_i_file = nullptr;
if (prob == 2)
{
eps_r_file = "data/tet-eps_r.gf";
eps_i_file = "data/tet-eps_i.gf";
}
else if (prob == 3)
{
eps_r_file = "data/hex-eps_r.gf";
eps_i_file = "data/hex-eps_i.gf";
}
else if (prob == 4)
{
eps_r_file = "data/tet-400k-eps_r.gf";
eps_i_file = "data/tet-400k-eps_i.gf";
}
real_t epsilon_scale = 8.8541878128e-12;
eps_r_cf = new EpsilonMatrixCoefficient(eps_r_file,&mesh,&pmesh, epsilon_scale);
eps_i_cf = new EpsilonMatrixCoefficient(eps_i_file,&mesh,&pmesh, epsilon_scale);
}
mesh.Clear();
for (int i = 0; i<pr; i++)
{
pmesh.UniformRefinement();
dynamic_cast<EpsilonMatrixCoefficient *>(eps_r_cf)->Update();
dynamic_cast<EpsilonMatrixCoefficient *>(eps_i_cf)->Update();
}
auto cf_r = dynamic_cast<EpsilonMatrixCoefficient *>(eps_r_cf);
auto cf_i = dynamic_cast<EpsilonMatrixCoefficient *>(eps_i_cf);
std::ostringstream cf_r_file_name;
cf_r_file_name << "epsilor_r" << "_prob_" << prob << "_order_" << order;
std::ostringstream cf_i_file_name;
cf_i_file_name << "epsilor_i" << "_prob_" << prob << "_order_" << order;
VisualizeMatrixArrayCoefficient(*cf_r, &pmesh, order, paraview,
cf_r_file_name.str().c_str());
VisualizeMatrixArrayCoefficient(*cf_i, &pmesh, order, paraview,
cf_i_file_name.str().c_str());
int ne = pmesh.GetNE();
MPI_Allreduce(MPI_IN_PLACE,&ne,1,MPI_INT,MPI_SUM,MPI_COMM_WORLD);
if (myid == 0)
{
mfem::out << "Number of elements = " << ne << endl;
}
// Matrix Coefficient (M = -i\omega \epsilon - \sigma I);
// M = -i * omega * (eps_r + i eps_i) - sigmaI
// = omega eps_i - sigma I + i (-omega eps_r)
MatrixSumCoefficient Mr_cf(*eps_i_cf,identity_cf,omega,-sigma);
ScalarMatrixProductCoefficient Mi_cf(-omega,*eps_r_cf);
// Define spaces
// L2 space for E
FiniteElementCollection *E_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *E_fes = new ParFiniteElementSpace(&pmesh,E_fec,dim);
// Vector L2 space for H
FiniteElementCollection *H_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *H_fes = new ParFiniteElementSpace(&pmesh,H_fec, dim);
// H^-1/2 (curl) space for Ê
FiniteElementCollection * hatE_fec = nullptr;
FiniteElementCollection * hatH_fec = nullptr;
FiniteElementCollection * F_fec = nullptr;
int test_order = order+delta_order;
hatE_fec = new ND_Trace_FECollection(order,dim);
hatH_fec = new ND_Trace_FECollection(order,dim);
F_fec = new ND_FECollection(test_order, dim);
ParFiniteElementSpace *hatE_fes = new ParFiniteElementSpace(&pmesh,hatE_fec);
ParFiniteElementSpace *hatH_fes = new ParFiniteElementSpace(&pmesh,hatH_fec);
FiniteElementCollection * G_fec = new ND_FECollection(test_order, dim);
Array<ParFiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(E_fes);
trial_fes.Append(H_fes);
trial_fes.Append(hatE_fes);
trial_fes.Append(hatH_fes);
test_fec.Append(F_fec);
test_fec.Append(G_fec);
int gdofs = 0;
for (int i = 0; i<trial_fes.Size(); i++)
{
gdofs += trial_fes[i]->GlobalTrueVSize();
}
if (myid == 0)
{
mfem::out << "Global number of dofs = " << gdofs << endl;
}
// Bilinear form coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient eps2omeg2(epsilon*epsilon*omega*omega);
ConstantCoefficient mu2omeg2(mu*mu*omega*omega);
ConstantCoefficient muomeg(mu*omega);
ConstantCoefficient negepsomeg(-epsilon*omega);
ConstantCoefficient epsomeg(epsilon*omega);
ConstantCoefficient negmuomeg(-mu*omega);
ConstantCoefficient sigma_cf(sigma);
ConstantCoefficient negsigma_cf(-sigma);
ConstantCoefficient sigma2_cf(sigma*sigma);
Vector z_one(3); z_one = 0.0; z_one(2) = 1.0;
Vector zero(3); zero = 0.0;
Vector z_negone(3); z_negone = 0.0; z_negone(2) = -1.0;
VectorConstantCoefficient z_one_cf(z_one);
VectorConstantCoefficient z_negone_cf(z_negone);
VectorConstantCoefficient zero_cf(zero);
if (myid == 0)
{
std::cout << "Assembling matrix" << endl;
}
ParComplexDPGWeakForm * a = new ParComplexDPGWeakForm(trial_fes,test_fec);
const IntegrationRule &ir_test = IntRules.Get(pmesh.GetElementGeometry(0),
2*test_order + 4);
const IntegrationRule &ir_trial = IntRules.Get(pmesh.GetElementGeometry(0),
order+test_order + 4);
a->SetTestIntegrationRule(ir_test);
// a->SetTrialIntegrationRule(ir_trial);
// (E,∇ × F)
a->AddTrialIntegrator(new TransposeIntegrator(new MixedCurlIntegrator(one)),
nullptr,0,0);
// --------------------------------------------------------------------------
// (M E , G) = (M_r E, G) + i (M_i E, G)
a->AddTrialIntegrator(
new TransposeIntegrator(new VectorFEMassIntegrator(Mr_cf)),
new TransposeIntegrator(new VectorFEMassIntegrator(Mi_cf)),0,1);
// --------------------------------------------------------------------------
// (H,∇ × G)
a->AddTrialIntegrator(new TransposeIntegrator(new MixedCurlIntegrator(one)),
nullptr,1,1);
// < n×Ĥ ,G>
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,3,1);
// test integrators
// (∇×G ,∇× δG)
a->AddTestIntegrator(new CurlCurlIntegrator(one),nullptr,1,1);
ConstantCoefficient l2weight(1.0);
// (G,δG)
a->AddTestIntegrator(new VectorFEMassIntegrator(l2weight),nullptr,1,1);
// i ω μ (H, F)
a->AddTrialIntegrator(nullptr,
new TransposeIntegrator(new VectorFEMassIntegrator(muomeg)),1,0);
// < n×Ê,F>
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,2,0);
// test integrators
// (∇×F,∇×δF)
a->AddTestIntegrator(new CurlCurlIntegrator(one),nullptr,0,0);
// (F,δF)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,0,0);
if (graph_norm)
{
// μ^2 ω^2 (F,δF)
a->AddTestIntegrator(new VectorFEMassIntegrator(mu2omeg2),nullptr,0,0);
// -i ω μ (F,∇ × δG) = i (F, -ω μ ∇ × δ G)
a->AddTestIntegrator(nullptr,new MixedVectorWeakCurlIntegrator(negmuomeg),0,1);
// --------------------------------------------------------------------------
// (M ∇ × F, δG) = (M_r ∇ × F, δG) + i (M_i ∇ × F, δG)
a->AddTestIntegrator(new MixedVectorCurlIntegrator(Mr_cf),
new MixedVectorCurlIntegrator(Mi_cf),0,1);
// --------------------------------------------------------------------------
// i ω μ (∇ × G,δF)
a->AddTestIntegrator(nullptr,new MixedVectorCurlIntegrator(muomeg),1,0);
}
// --------------------------------------------------------------------------
// (M^* G, ∇ × δF ) = (Mr^T G, ∇ × δF) - i (Mi^T G, ∇ × δF)
TransposeMatrixCoefficient Mrt_cf(Mr_cf);
TransposeMatrixCoefficient Mit_cf(Mi_cf);
ScalarMatrixProductCoefficient negMit_cf(-1.0,Mit_cf);
if (graph_norm)
{
a->AddTestIntegrator(new MixedVectorWeakCurlIntegrator(Mrt_cf),
new MixedVectorWeakCurlIntegrator(negMit_cf),1,0);
}
// --------------------------------------------------------------------------
// M*M^*(G,δG) = (MrMr^t + MiMi^t) + i (MiMr^t - MrMi^t)
MatrixProductCoefficient MrMrt_cf(Mr_cf,Mrt_cf);
MatrixProductCoefficient MiMit_cf(Mi_cf,Mit_cf);
MatrixProductCoefficient MiMrt_cf(Mi_cf,Mrt_cf);
MatrixProductCoefficient MrMit_cf(Mr_cf,Mit_cf);
MatrixSumCoefficient MMr_cf(MrMrt_cf,MiMit_cf);
MatrixSumCoefficient MMi_cf(MiMrt_cf,MrMit_cf,1.0,-1.0);
const IntegrationRule *irs[Geometry::NumGeom];
int order_quad = 2*order + 2;
for (int i = 0; i < Geometry::NumGeom; ++i)
{
irs[i] = &(IntRules.Get(i, order_quad));
}
const IntegrationRule &ir = IntRules.Get(pmesh.GetElementGeometry(0),
2*test_order + 2);
if (graph_norm)
{
a->AddTestIntegrator(new VectorFEMassIntegrator(MMr_cf),
new VectorFEMassIntegrator(MMi_cf),1,1);
}
// --------------------------------------------------------------------------
socketstream E_out_r;
socketstream H_out_r;
ParComplexGridFunction E(E_fes);
ParComplexGridFunction H(H_fes);
E.real() = 0.0;
E.imag() = 0.0;
H.real() = 0.0;
H.imag() = 0.0;
ParGridFunction Et(E_fes);
ParGridFunction Ht(H_fes);
ParaViewDataCollection * paraview_dc = nullptr;
ParaViewDataCollection * paraview_dct = nullptr;
std::ostringstream paraview_file_name;
paraview_file_name << "prob_" << prob
<< "_order_" << order
<< "_pref_" << pr
<< "_sc_" << (int)static_cond;
std::ostringstream paraview_file_namet;
paraview_file_namet << "tim-harmonin-prob_" << prob
<< "_order_" << order
<< "_pref_" << pr
<< "_sc_" << (int)static_cond;
if (paraview)
{
paraview_dc = new ParaViewDataCollection(paraview_file_name.str(), &pmesh);
paraview_dc->SetPrefixPath("ParaView/UW/3D");
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",&E.real());
paraview_dc->RegisterField("E_i",&E.imag());
paraview_dc->RegisterField("H_r",&H.real());
paraview_dc->RegisterField("H_i",&H.imag());
// paraview_dct = new ParaViewDataCollection(paraview_file_namet.str(), &pmesh);
// paraview_dct->SetPrefixPath("ParaView/UW3D");
// paraview_dct->SetLevelsOfDetail(order);
// paraview_dct->SetCycle(0);
// paraview_dct->SetDataFormat(VTKFormat::BINARY);
// paraview_dct->SetHighOrderOutput(true);
// paraview_dct->SetTime(0.0); // set the time
// paraview_dct->RegisterField("Et",&Et);
// paraview_dct->RegisterField("Ht",&Ht);
}
Array<int> ess_tdof_list;
Array<int> ess_bdr;
Array<int> one_bdr;
Array<int> negone_bdr;
if (myid == 0)
{
std::cout << "Setting up boundary attributes" << endl;
}
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
one_bdr.SetSize(pmesh.bdr_attributes.Max());
negone_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
hatE_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
one_bdr = 0;
negone_bdr = 0;
if (prob == 4)
{
one_bdr[232-1] = 1;
negone_bdr[231-1] = 1;
}
else
{
one_bdr[1] = 1;
negone_bdr[2] = 1;
}
}
if (myid == 0)
{
std::cout << "Setting up boundary conditions" << endl;
}
// Set up bdr conditions
// shift the ess_tdofs
for (int j = 0; j < ess_tdof_list.Size(); j++)
{
ess_tdof_list[j] += E_fes->GetTrueVSize() + H_fes->GetTrueVSize();
}
Array<int> offsets(5);
offsets[0] = 0;
offsets[1] = E_fes->GetVSize();
offsets[2] = H_fes->GetVSize();
offsets[3] = hatE_fes->GetVSize();
offsets[4] = hatH_fes->GetVSize();
offsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
real_t * xdata = x.GetData();
ParComplexGridFunction hatE_gf(hatE_fes);
hatE_gf.real().MakeRef(hatE_fes,&xdata[offsets[2]]);
hatE_gf.imag().MakeRef(hatE_fes,&xdata[offsets.Last()+ offsets[2]]);
hatE_gf.ProjectBdrCoefficientTangent(z_one_cf,zero_cf, one_bdr);
hatE_gf.ProjectBdrCoefficientTangent(z_negone_cf,zero_cf, negone_bdr);
if (myid == 0)
{
std::cout << "Assembly started" << endl;
}
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
if (myid == 0)
{
std::cout << "Assembly finished" << endl;
}
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
Array<ParFiniteElementSpace *> prec_fes;
if (static_cond)
{
a->GetTraceFESpaces(prec_fes);
}
else
{
prec_fes = trial_fes;
}
Solver * cprec = nullptr;
if (pmg)
{
#ifdef MFEM_USE_COMPLEX_MUMPS
bool mumps_coarse_solver = true;
#else
bool mumps_coarse_solver = false;
#endif
std::vector<Array<int>> ess_bdr_marker(prec_fes.Size());
for (int b = 0; b<prec_fes.Size(); b++)
{
if (pmesh.bdr_attributes.Size())
{
ess_bdr_marker[b].SetSize(pmesh.bdr_attributes.Max());
int ess_block = (static_cond) ? 0 : 2;
if (b == ess_block) // hatE
{
ess_bdr_marker[b] = ess_bdr;
}
else
{
ess_bdr_marker[b] = 0;
}
}
}
cprec = new ComplexPRefinementMultigrid(prec_fes, ess_bdr_marker, *Ahc,
pmg_levels, relax_factor, mumps_coarse_solver);
}
else
{
BlockDiagonalPreconditioner * real_prec = new BlockDiagonalPreconditioner(BlockA_r->RowOffsets());
real_prec->owns_blocks = 1;
for (int i = 0; i<BlockA_r->NumRowBlocks(); i++)
{
auto prec = MakeFESpaceDefaultSolver(prec_fes[i],0);
prec->SetOperator(BlockA_r->GetBlock(i,i));
real_prec->SetDiagonalBlock(i,prec);
}
cprec = new ComplexPreconditioner(real_prec, true);
}
if (myid == 0)
{
std::cout << "PCG iterations" << endl;
}
CGSolver cg(MPI_COMM_WORLD);
// GMRESSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-6);
cg.SetMaxIter(2000);
cg.SetPrintLevel(1);
cg.SetOperator(*Ahc);
cg.SetPreconditioner(*cprec);
cg.Mult(B, X);
delete cprec;
a->RecoverFEMSolution(X,x);
E.real().MakeRef(E_fes,x.GetData());
E.imag().MakeRef(E_fes,&x.GetData()[offsets.Last()]);
H.real().MakeRef(H_fes,&x.GetData()[offsets[1]]);
H.imag().MakeRef(H_fes,&x.GetData()[offsets.Last()+offsets[1]]);
if (visualization)
{
const char * keys = (dim == 2) ? "jRcml\n" : nullptr;
char vishost[] = "localhost";
int visport = 19916;
common::VisualizeField(E_out_r,vishost, visport, E.real(),
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
common::VisualizeField(H_out_r,vishost, visport, H.real(),
"Numerical Magnetic field (real part)", 501, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(0);
paraview_dc->SetTime(0.0);
paraview_dc->Save();
// Et = E.real();
// int num_frames = 32;
// for (int it = 0; it<num_frames; it++)
// {
// real_t t = (real_t)(it % num_frames) / num_frames;
// add(cos(2.0*M_PI*t), E.real(), sin(2.0*M_PI*t), E.imag(), Et);
// paraview_dct->SetCycle(it);
// paraview_dct->SetTime((real_t)it);
// paraview_dct->Save();
// }
}
if (paraview)
{
delete paraview_dc;
}
delete eps_r_cf;
delete eps_i_cf;
delete a;
delete F_fec;
delete G_fec;
delete hatH_fes;
delete hatH_fec;
delete hatE_fes;
delete hatE_fec;
delete H_fec;
delete E_fec;
delete H_fes;
delete E_fes;
return 0;
}
+96
View File
@@ -0,0 +1,96 @@
#include "lh_utils.hpp"
real_t delta = 0.01;
real_t a0 = -1.0;
real_t a1 = 5.0;
void bfunc(const Vector &x, Vector &b)
{
real_t r = std::sqrt(x(0) * x(0) + x(1) * x(1));
int dim = x.Size();
b.SetSize(dim); b = 0.0;
b(0) = -x(1) / r;
b(1) = x(0) / r;
if (dim == 3) b(2) = 0.0;
}
void bcrossb(const Vector &x, DenseMatrix &bb)
{
Vector b;
bfunc(x, b);
bb.SetSize(b.Size());
MultVVt(b, bb);
}
std::complex<real_t> pfunc(const Vector &x)
{
real_t r = std::sqrt(x(0) * x(0) + x(1) * x(1));
return std::complex<real_t>(a0 + a1 *(r-0.9), delta);
}
std::complex<real_t> sfunc(const Vector &x)
{
return std::complex<real_t>(1.0, delta);
}
real_t pfunc_r(const Vector &x) { return pfunc(x).real(); }
real_t pfunc_i(const Vector &x) { return pfunc(x).imag(); }
real_t sfunc_r(const Vector &x) { return sfunc(x).real(); }
real_t sfunc_i(const Vector &x) { return sfunc(x).imag(); }
void epsilon_func_r(const Vector &x, DenseMatrix &eps)
{
std::complex<real_t> p = pfunc(x);
std::complex<real_t> s = sfunc(x);
DenseMatrix B;
bcrossb(x, B);
int dim = x.Size();
eps.SetSize(dim);
for (int i = 0; i < dim; i++)
{
for (int j = 0; j < dim; j++)
{
if (i == j)
{
eps(i, j) = (s * (1.0 - B(i, j)) + p * B(i, j)).real();
}
else
{
eps(i, j) = ((p - s) * B(i, j)).real();
}
}
}
}
void epsilon_func_i(const Vector &x, DenseMatrix &eps)
{
int dim = x.Size();
eps.SetSize(dim);
std::complex<real_t> p = pfunc(x);
std::complex<real_t> s = sfunc(x);
DenseMatrix B;
bcrossb(x, B);
for (int i = 0; i < dim; i++)
{
for (int j = 0; j < dim; j++)
{
if (i == j)
{
eps(i, j) = (s * (1.0 - B(i, j)) + p * B(i, j)).imag();
}
else
{
eps(i, j) = ((p - s) * B(i, j)).imag();
}
}
}
}
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include "mfem.hpp"
using namespace mfem;
using namespace std;
extern double delta;
extern double a0;
extern double a1;
std::complex<real_t> pfunc(const Vector &x);
std::complex<real_t> sfunc(const Vector &x);
real_t pfunc_r(const Vector &x);
real_t pfunc_i(const Vector &x);
real_t sfunc_r(const Vector &x);
real_t sfunc_i(const Vector &x);
void bfunc(const Vector &x, Vector &b);
void bcrossb(const Vector &x, DenseMatrix &bb);
void epsilon_func_r(const Vector &x, DenseMatrix &eps);
void epsilon_func_i(const Vector &x, DenseMatrix &eps);
+642
View File
@@ -0,0 +1,642 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
//
// MFEM Ultraweak DPG Maxwell parallel example
//
// Compile with: make pmaxwell-coupled
//
// mpirun -np 4 ./pmaxwell-coupled -sref 1 -pref 2 -o 2 -rnum 0.5 -m ../../data/ref-cube.mesh -sc
// ∇×(1/μ ∇×E) - ω² ϵ E + J = F̃ , in Ω
// -ΔJ + α² J + c² E = G , in Ω
// E×n = E₀ , on ∂Ω
// J = J₀ , on ∂Ω
#include "mfem.hpp"
#include "util/pcomplexweakform.hpp"
#include "util/pcomplexblockform.hpp"
#include "../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
using namespace mfem::common;
void maxwell_solution(const Vector &x, std::vector<complex<double>> &E);
void maxwell_solution_curl(const Vector &x,
std::vector<complex<double>> &curlE);
void maxwell_solution_curlcurl(const Vector &x,
std::vector<complex<double>> &curlcurlE);
void J_solution(const Vector &x,std::vector<complex<double>> &J);
void J_solution_grad(const Vector &x,
std::vector<std::vector<complex<double>>> &gradJ);
void J_solution_laplace(const Vector &x,
std::vector<complex<double>> &laplaceJ);
void E_exact_r(const Vector &x, Vector & E_r);
void E_exact_i(const Vector &x, Vector & E_i);
void H_exact_r(const Vector &x, Vector & H_r);
void H_exact_i(const Vector &x, Vector & H_i);
void J_exact_r(const Vector &x, Vector & J_r);
void J_exact_i(const Vector &x, Vector & J_i);
void curlE_exact_r(const Vector &x, Vector &curlE_r);
void curlE_exact_i(const Vector &x, Vector &curlE_i);
void curlH_exact_r(const Vector &x,Vector &curlH_r);
void curlH_exact_i(const Vector &x,Vector &curlH_i);
void gradJ_exact_r(const Vector &x, DenseMatrix &gradJ_r);
void gradJ_exact_i(const Vector &x, DenseMatrix &gradJ_i);
void curlcurlE_exact_r(const Vector &x, Vector & curlcurlE_r);
void curlcurlE_exact_i(const Vector &x, Vector & curlcurlE_i);
void LaplaceJ_exact_r(const Vector &x, Vector & d2J_r);
void LaplaceJ_exact_i(const Vector &x, Vector & d2J_i);
void rhs1_func_r(const Vector &x, Vector & rhs1_r);
void rhs1_func_i(const Vector &x, Vector & rhs1_i);
void rhs2_func_r(const Vector &x, Vector & rhs2_r);
void rhs2_func_i(const Vector &x, Vector & rhs2_i);
int dim;
int dimc;
double omega;
double mu = 1.0;
double epsilon = 1.0;
double alpha = 1.0;
double c = 1.0;
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "../../data/inline-square.mesh";
int order = 1;
double rnum=1.0;
int sr = 0;
int pr = 1;
bool visualization = true;
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(&rnum, "-rnum", "--number-of-wavelengths",
"Number of wavelengths");
args.AddOption(&sr, "-sref", "--serial-ref",
"Number of parallel refinements.");
args.AddOption(&pr, "-pref", "--parallel-ref",
"Number of parallel refinements.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
omega = 2.*M_PI*rnum;
if (myid == 0)
{
args.PrintOptions(cout);
}
Mesh mesh(mesh_file, 1, 1);
dim = mesh.Dimension();
MFEM_VERIFY(dim > 1, "Dimension = 1 is not supported in this example");
dimc = (dim == 3) ? 3 : 1;
for (int i = 0; i<sr; i++)
{
mesh.UniformRefinement();
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
// Define spaces
enum TrialSpace
{
E_space = 0,
J_space = 1
};
// Vector L2 space for E
FiniteElementCollection *E_fec = new ND_FECollection(order,dim);
ParFiniteElementSpace *E_fes = new ParFiniteElementSpace(&pmesh,E_fec);
// Vector H1 space for J
FiniteElementCollection *J_fec = new H1_FECollection(order,dim);
ParFiniteElementSpace *J_fes = new ParFiniteElementSpace(&pmesh,J_fec, dim);
Array<ParFiniteElementSpace * > trial_fes;
trial_fes.Append(E_fes);
trial_fes.Append(J_fes);
ParComplexBlockForm * a = new ParComplexBlockForm(trial_fes);
// // Bilinear form coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient muinv(1./mu);
ConstantCoefficient rec_omega(1.0/omega);
ConstantCoefficient muomeg_cf(mu*omega);
ConstantCoefficient mu2omeg2_cf(mu*mu*omega*omega);
ConstantCoefficient eps2omeg2_cf(epsilon*epsilon*omega*omega);
ConstantCoefficient negepsomeg_cf(-epsilon*omega);
ConstantCoefficient epsomeg_cf(epsilon*omega);
ConstantCoefficient negmuomeg_cf(-mu*omega);
ConstantCoefficient c2_cf(c*c);
ConstantCoefficient a2_cf(alpha*alpha);
ConstantCoefficient negepsomeg2_cf(-epsilon*omega*omega);
// (1/μ ∇ × E,∇ × δE)
a->AddDomainIntegrator(new CurlCurlIntegrator(muinv), nullptr,
TrialSpace::E_space, TrialSpace::E_space);
// (- ω² ϵ E, δE)
a->AddDomainIntegrator(new VectorFEMassIntegrator(negepsomeg2_cf), nullptr,
TrialSpace::E_space, TrialSpace::E_space);
// // (J, δE)
a->AddDomainIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(one)),
nullptr,
TrialSpace::J_space, TrialSpace::E_space);
// // (∇ J, ∇ δJ)
a->AddDomainIntegrator(new VectorDiffusionIntegrator(one), nullptr,
TrialSpace::J_space, TrialSpace::J_space);
// // (α²J, δJ)
a->AddDomainIntegrator(new VectorMassIntegrator(a2_cf), nullptr,
TrialSpace::J_space, TrialSpace::J_space);
// // (c²E, δJ)
a->AddDomainIntegrator(new VectorFEMassIntegrator(c2_cf), nullptr,
TrialSpace::E_space, TrialSpace::J_space);
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = E_fes->GetVSize();
offsets[2] = J_fes->GetVSize();
offsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
Vector b(2*offsets.Last());
real_t * bdata = b.GetData();
ParLinearForm bE_r(E_fes, bdata);
ParLinearForm bE_i(E_fes, &bdata[offsets.Last()]);
ParLinearForm bJ_r(J_fes, &bdata[offsets[1]]);
ParLinearForm bJ_i(J_fes, &bdata[offsets[2]]);
VectorFunctionCoefficient f_rhs1_r(dim,rhs1_func_r);
VectorFunctionCoefficient f_rhs1_i(dim,rhs1_func_i);
bE_r.AddDomainIntegrator(new VectorFEDomainLFIntegrator(f_rhs1_r));
bE_i.AddDomainIntegrator(new VectorFEDomainLFIntegrator(f_rhs1_i));
VectorFunctionCoefficient f_rhs2_r(dim,rhs2_func_r);
VectorFunctionCoefficient f_rhs2_i(dim,rhs2_func_i);
bJ_r.AddDomainIntegrator(new VectorDomainLFIntegrator(f_rhs2_r));
bJ_i.AddDomainIntegrator(new VectorDomainLFIntegrator(f_rhs2_i));
bE_r.Assemble();
bE_i.Assemble();
bJ_r.Assemble();
bJ_i.Assemble();
socketstream E_out_r, E_out_i, Eex_out_r, Eex_out_i;
socketstream J_out_r, J_out_i, Jex_out_r, Jex_out_i;
ParGridFunction E_r(E_fes), E_i(E_fes);
ParGridFunction J_r(J_fes), J_i(J_fes);
ParGridFunction Eex_r(E_fes), Eex_i(E_fes);
ParGridFunction Jex_r(J_fes), Jex_i(J_fes);
a->Assemble();
// cin.get();
Array<int> ess_tdof_list;
Array<int> ess_tdof_listE;
Array<int> ess_tdof_listJ;
Array<int> ess_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
E_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_listE);
J_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_listJ);
}
// shift the ess_tdofs
for (int j = 0; j < ess_tdof_listJ.Size(); j++)
{
ess_tdof_listJ[j] += E_fes->GetTrueVSize();
}
ess_tdof_list.Append(ess_tdof_listE);
ess_tdof_list.Append(ess_tdof_listJ);
ParGridFunction E_gf_r(E_fes, x, offsets[0]); E_gf_r = 0.0;
ParGridFunction E_gf_i(E_fes, x, offsets.Last()); E_gf_i = 0.0;
VectorFunctionCoefficient Eex_cf_r(dim,E_exact_r);
VectorFunctionCoefficient Eex_cf_i(dim,E_exact_i);
E_gf_r.ProjectBdrCoefficientTangent(Eex_cf_r, ess_bdr);
E_gf_i.ProjectBdrCoefficientTangent(Eex_cf_i, ess_bdr);
VectorFunctionCoefficient Ecf_r(dim,E_exact_r);
VectorFunctionCoefficient Ecf_i(dim,E_exact_i);
VectorFunctionCoefficient Jcf_r(dim,J_exact_r);
VectorFunctionCoefficient Jcf_i(dim,J_exact_i);
ParGridFunction J_gf_r(J_fes, x, offsets[1]); J_gf_r = 0.0;
ParGridFunction J_gf_i(J_fes, x, offsets.Last() + offsets[1]); J_gf_i = 0.0;
J_gf_r.ProjectBdrCoefficient(Jcf_r,ess_bdr);
J_gf_i.ProjectBdrCoefficient(Jcf_i,ess_bdr);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,b, Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
int nblocks = BlockA_r->NumRowBlocks();
Array2D<const HypreParMatrix*> A_r_matrices(nblocks, nblocks);
Array2D<const HypreParMatrix*> A_i_matrices(nblocks, nblocks);
for (int i = 0; i < nblocks; i++)
{
for (int j = 0; j < nblocks; j++)
{
A_r_matrices(i,j) = dynamic_cast<const HypreParMatrix*>(&BlockA_r->GetBlock(i,
j));
A_i_matrices(i,j) = dynamic_cast<const HypreParMatrix*>(&BlockA_i->GetBlock(i,
j));
}
}
HypreParMatrix * Ahr = HypreParMatrixFromBlocks(A_r_matrices);
HypreParMatrix * Ahi = HypreParMatrixFromBlocks(A_i_matrices);
ComplexHypreParMatrix * Ahc_hypre =
new ComplexHypreParMatrix(Ahr, Ahi,false, false);
if (Mpi::Root())
{
mfem::out << "Assembly finished successfully." << endl;
}
#ifdef MFEM_USE_COMPLEX_MUMPS
auto solver = new ComplexMUMPSSolver(MPI_COMM_WORLD);
solver->SetPrintLevel(1);
solver->SetOperator(*Ahc_hypre);
solver->Mult(B,X);
delete solver;
delete Ahc_hypre;
#else
MFEM_ABORT("MFEM compiled without mumps");
#endif
a->RecoverFEMSolution(X, x);
E_r.MakeRef(E_fes,x, 0);
E_i.MakeRef(E_fes,x, offsets.Last());
J_r.MakeRef(J_fes,x, offsets[1]);
J_i.MakeRef(J_fes,x, offsets.Last()+offsets[1]);
if (visualization)
{
// const char * keys = (it == 0 && dim == 2) ? "jRcml\n" : nullptr;
const char * keys = nullptr;
char vishost[] = "localhost";
int visport = 19916;
VisualizeField(E_out_r,vishost, visport, E_r,
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
VisualizeField(J_out_r,vishost, visport, J_r,
"Numerical J field (real part)", 0, 0, 500, 500, keys);
}
delete a;
return 0;
}
void maxwell_solution(const Vector & X, std::vector<complex<double>> &E)
{
complex<double> zi = complex<double>(0., 1.);
E.resize(dim);
for (int i = 0; i < dim; ++i)
{
E[i] = 0.0;
}
E[0] = exp(zi * omega * (X.Sum()));
}
void maxwell_solution_curl(const Vector & X,
std::vector<complex<double>> &curlE)
{
complex<double> zi = complex<double>(0., 1.);
curlE.resize(dimc);
for (int i = 0; i < dimc; ++i)
{
curlE[i] = 0.0;
}
std::complex<double> pw = exp(zi * omega * (X.Sum()));
if (dim == 3)
{
curlE[0] = 0.0;
curlE[1] = zi * omega * pw;
curlE[2] = -zi * omega * pw;
}
else
{
curlE[0] = -zi * omega * pw;
}
}
void maxwell_solution_curlcurl(const Vector & X,
std::vector<complex<double>> &curlcurlE)
{
complex<double> zi = complex<double>(0., 1.);
curlcurlE.resize(dim);
for (int i = 0; i < dim; ++i)
{
curlcurlE[i] = 0.0;;
}
std::complex<double> pw = exp(zi * omega * (X.Sum()));
if (dim == 3)
{
curlcurlE[0] = 2.0 * omega * omega * pw;
curlcurlE[1] = - omega * omega * pw;
curlcurlE[2] = - omega * omega * pw;
}
else
{
curlcurlE[0] = omega * omega * pw;
curlcurlE[1] = -omega * omega * pw;
}
}
void J_solution(const Vector &x,std::vector<complex<double>> &J)
{
J.resize(dim);
for (int i = 0; i < dim; ++i)
{
J[i] = 0.0;
}
J[0] = x[0] * x[0];
}
void J_solution_grad(const Vector &x,
std::vector<std::vector<complex<double>>> &gradJ)
{
gradJ.resize(dim);
for (int i = 0; i < dim; ++i)
{
gradJ[i].resize(dim);
for (int j = 0; j < dim; ++j)
{
gradJ[i][j] = 0.0;
}
}
gradJ[0][0] = 2*x[0];
}
void J_solution_laplace(const Vector &x, std::vector<complex<double>> &laplaceJ)
{
laplaceJ.resize(dim);
for (int i = 0; i < dim; ++i)
{
laplaceJ[i] = 0.0;
}
laplaceJ[0] = 2.0;
}
void E_exact_r(const Vector &x, Vector & E_r)
{
std::vector<std::complex<double>> E;
maxwell_solution(x,E);
E_r.SetSize(E.size());
for (unsigned i = 0; i < E.size(); i++)
{
E_r[i]= E[i].real();
}
}
void E_exact_i(const Vector &x, Vector & E_i)
{
std::vector<std::complex<double>> E;
maxwell_solution(x, E);
E_i.SetSize(E.size());
for (unsigned i = 0; i < E.size(); i++)
{
E_i[i]= E[i].imag();
}
}
void J_exact_r(const Vector &x, Vector & J_r)
{
std::vector<std::complex<double>> J;
J_solution(x,J);
J_r.SetSize(J.size());
for (unsigned i = 0; i < J.size(); i++)
{
J_r[i]= J[i].real();
}
}
void J_exact_i(const Vector &x, Vector & J_i)
{
std::vector<std::complex<double>> J;
J_solution(x,J);
J_i.SetSize(J.size());
for (unsigned i = 0; i < J.size(); i++)
{
J_i[i]= J[i].imag();
}
}
void curlE_exact_r(const Vector &x, Vector &curlE_r)
{
std::vector<std::complex<double>> curlE;
maxwell_solution_curl(x, curlE);
curlE_r.SetSize(curlE.size());
for (unsigned i = 0; i < curlE.size(); i++)
{
curlE_r[i]= curlE[i].real();
}
}
void curlE_exact_i(const Vector &x, Vector &curlE_i)
{
std::vector<std::complex<double>> curlE;
maxwell_solution_curl(x, curlE);
curlE_i.SetSize(curlE.size());
for (unsigned i = 0; i < curlE.size(); i++)
{
curlE_i[i]= curlE[i].imag();
}
}
void gradJ_exact_r(const Vector &x, DenseMatrix &gradJ_r)
{
std::vector<std::vector<std::complex<double>>> gradJ;
J_solution_grad(x, gradJ);
gradJ_r.SetSize(gradJ.size());
for (unsigned i = 0; i < gradJ.size(); i++)
{
for (unsigned j = 0; j < gradJ.size(); j++)
{
gradJ_r(i,j)= gradJ[i][j].real();
}
}
}
void gradJ_exact_i(const Vector &x, DenseMatrix &gradJ_i)
{
std::vector<std::vector<std::complex<double>>> gradJ;
J_solution_grad(x, gradJ);
gradJ_i.SetSize(gradJ.size());
for (unsigned i = 0; i < gradJ.size(); i++)
{
for (unsigned j = 0; j < gradJ.size(); j++)
{
gradJ_i(i,j)= gradJ[i][j].imag();
}
}
}
void curlcurlE_exact_r(const Vector &x, Vector & curlcurlE_r)
{
std::vector<std::complex<double>> curlcurlE;
maxwell_solution_curlcurl(x, curlcurlE);
curlcurlE_r.SetSize(curlcurlE.size());
for (unsigned i = 0; i < curlcurlE.size(); i++)
{
curlcurlE_r[i]= curlcurlE[i].real();
}
}
void curlcurlE_exact_i(const Vector &x, Vector & curlcurlE_i)
{
std::vector<std::complex<double>> curlcurlE;
maxwell_solution_curlcurl(x, curlcurlE);
curlcurlE_i.SetSize(curlcurlE.size());
for (unsigned i = 0; i < curlcurlE.size(); i++)
{
curlcurlE_i[i]= curlcurlE[i].imag();
}
}
void LaplaceJ_exact_r(const Vector &x, Vector & d2J_r)
{
std::vector<std::complex<double>> d2J;
J_solution_laplace(x, d2J);
d2J_r.SetSize(d2J.size());
for (unsigned i = 0; i < d2J.size(); i++)
{
d2J_r[i]= d2J[i].real();
}
}
void LaplaceJ_exact_i(const Vector &x, Vector & d2J_i)
{
std::vector<std::complex<double>> d2J;
J_solution_laplace(x, d2J);
d2J_i.SetSize(d2J.size());
for (unsigned i = 0; i < d2J.size(); i++)
{
d2J_i[i]= d2J[i].imag();
}
}
// F = ∇×(1/μ ∇×E) - ω² ϵ E + J
void rhs1_func_r(const Vector &x, Vector & F_r)
{
Vector E_r, curlcurlE_r, J_r;
E_exact_r(x,E_r);
curlcurlE_exact_r(x,curlcurlE_r);
J_exact_r(x,J_r);
F_r.SetSize(dim);
for (int i = 0; i<dim; i++)
{
F_r(i) = 1.0/mu * curlcurlE_r(i)
- omega * omega * epsilon * E_r(i)
+ J_r(i);
}
}
void rhs1_func_i(const Vector &x, Vector & F_i)
{
Vector E_i, curlcurlE_i, J_i;
E_exact_i(x,E_i);
curlcurlE_exact_i(x,curlcurlE_i);
J_exact_i(x,J_i);
F_i.SetSize(dim);
for (int i = 0; i<dim; i++)
{
F_i(i) = 1.0/mu * curlcurlE_i(i)
- omega * omega * epsilon * E_i(i)
+ J_i(i);
}
}
// G = -ΔJ + α² J + c² E
// G_r + i G_i = - Δ (J_r + i J_i) + α² (J_r + i J_i) + c² (E_r + i E_i)
void rhs2_func_r(const Vector &x, Vector & G_r)
{
// G_r = - Δ J_r + α² J_r + c² E_r
Vector E_r, J_r, d2J_r;
E_exact_r(x,E_r);
J_exact_r(x,J_r);
LaplaceJ_exact_r(x,d2J_r);
G_r.SetSize(dim);
for (int i = 0; i<dim; i++)
{
G_r(i) = -d2J_r[i] + alpha*alpha*J_r[i] + c*c * E_r[i];
}
}
void rhs2_func_i(const Vector &x, Vector & G_i)
{
// G_i = - Δ J_i + α² J_i + c² E_i
Vector E_i, J_i, d2J_i;
E_exact_i(x,E_i);
J_exact_i(x,J_i);
LaplaceJ_exact_i(x,d2J_i);
G_i.SetSize(dim);
for (int i = 0; i<dim; i++)
{
G_i(i) = -d2J_i[i] + alpha*alpha*J_i[i] + c*c * E_i[i];
}
}
File diff suppressed because it is too large Load Diff
+595
View File
@@ -0,0 +1,595 @@
// MFEM Ultraweak DPG Maxwell parallel example
//
// Compile with: make pmaxwell
//
// sample run
// mpirun -np 4 pmaxwell -m ../../data/star.mesh -o 2 -sref 0 -pref 3 -rnum 1.0
// mpirun -np 4 pmaxwell -m ../../data/inline-quad.mesh -o 3 -sref 0 -pref 3 -rnum 4.8 -sc
// mpirun -np 4 pmaxwell -m ../../data/inline-hex.mesh -o 2 -sref 0 -pref 1 -rnum 0.8 -sc
// Description:
// This example code demonstrates the use of MFEM to define and solve
// the "ultraweak" (UW) DPG formulation for the Maxwell problem
// ∇×(1/μ ∇×E) - ω² ϵ E = J , in Ω
// E×n = E₀ , on ∂Ω
// It solves the following kinds of problems
// 1) Known exact solutions with error convergence rates
// a) A manufactured solution problem where E is a plane wave
#include "mfem.hpp"
#include "util/pcomplexweakform.hpp"
#include "../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
using namespace mfem::common;
void E_exact_r(const Vector &x, Vector & E_r);
void E_exact_i(const Vector &x, Vector & E_i);
void rhs_func_r(const Vector &x, Vector & J_r);
void rhs_func_i(const Vector &x, Vector & J_i);
void curlE_exact_r(const Vector &x, Vector &curlE_r);
void curlE_exact_i(const Vector &x, Vector &curlE_i);
void curlcurlE_exact_r(const Vector &x, Vector & curlcurlE_r);
void curlcurlE_exact_i(const Vector &x, Vector & curlcurlE_i);
void maxwell_solution(const Vector & X,
std::vector<complex<double>> &E);
void maxwell_solution_curl(const Vector & X,
std::vector<complex<double>> &curlE);
void maxwell_solution_curlcurl(const Vector & X,
std::vector<complex<double>> &curlcurlE);
int dim;
int dimc;
double omega;
double mu = 1.0;
double epsilon = 1.0;
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "../../data/inline-quad.mesh";
int order = 1;
int delta_order = 1;
double rnum=1.0;
bool static_cond = false;
int sr = 0;
int pr = 1;
bool visualization = true;
bool paraview = false;
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(&rnum, "-rnum", "--number-of-wavelengths",
"Number of wavelengths");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&epsilon, "-eps", "--permittivity",
"Permittivity of free space (or mass constant).");
args.AddOption(&delta_order, "-do", "--delta-order",
"Order enrichment for DPG test space.");
args.AddOption(&sr, "-sref", "--serial-ref",
"Number of parallel refinements.");
args.AddOption(&pr, "-pref", "--parallel-ref",
"Number of parallel refinements.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
omega = 2.*M_PI*rnum;
if (myid == 0)
{
args.PrintOptions(cout);
}
Mesh mesh(mesh_file, 1, 1);
dim = mesh.Dimension();
MFEM_VERIFY(dim > 1, "Dimension = 1 is not supported in this example");
dimc = (dim == 3) ? 3 : 1;
for (int i = 0; i<sr; i++)
{
mesh.UniformRefinement();
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
// Define spaces
enum TrialSpace { E_space = 0, hatE_space = 1 };
enum TestSpace { F_space = 0 };
// H(curl) space for E
FiniteElementCollection *E_fec = new ND_FECollection(order,dim);
ParFiniteElementSpace *E_fes = new ParFiniteElementSpace(&pmesh,E_fec);
// H^-1/2 (curl) space for Ê
FiniteElementCollection * hatE_fec = nullptr;
int test_order = order+delta_order;
if (dim == 2)
{
hatE_fec = new H1_Trace_FECollection(order,dim);
}
else
{
hatE_fec = new ND_Trace_FECollection(order,dim);
}
ParFiniteElementSpace *hatE_fes = new ParFiniteElementSpace(&pmesh,hatE_fec);
FiniteElementCollection * F_fec = new ND_FECollection(test_order, dim);
Array<ParFiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(E_fes);
trial_fes.Append(hatE_fes);
test_fec.Append(F_fec);
// Bilinear form coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient negepsomeg2(-epsilon*omega*omega);
ConstantCoefficient muinv(1./mu);
// for the 2D case
ParComplexDPGWeakForm * a = new ParComplexDPGWeakForm(trial_fes,test_fec);
a->StoreMatrices(); // needed for AMR
// (∇ × E,∇ × F)
a->AddTrialIntegrator(new CurlCurlIntegrator(muinv), nullptr,
TrialSpace::E_space, TestSpace::F_space);
// -ω² ϵ (E , F)
a->AddTrialIntegrator(new VectorFEMassIntegrator(negepsomeg2), nullptr,
TrialSpace::E_space,TestSpace::F_space);
// < n×Ê ,F>
if (dim == 3)
{
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,
TrialSpace::hatE_space, TestSpace::F_space);
}
else
{
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,
TrialSpace::hatE_space, TestSpace::F_space);
}
// test integrators
// (∇×F ,∇×δF)
a->AddTestIntegrator(new CurlCurlIntegrator(one),nullptr,
TestSpace::F_space,TestSpace::F_space);
// (F,δF)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,
TestSpace::F_space,TestSpace::F_space);
// RHS
VectorFunctionCoefficient f_rhs_r(dim,rhs_func_r);
VectorFunctionCoefficient f_rhs_i(dim,rhs_func_i);
a->AddDomainLFIntegrator(new VectorFEDomainLFIntegrator(f_rhs_r),
new VectorFEDomainLFIntegrator(f_rhs_i),
TestSpace::F_space);
VectorFunctionCoefficient Eex_r(dim,E_exact_r);
VectorFunctionCoefficient Eex_i(dim,E_exact_i);
socketstream E_out_r;
if (myid == 0)
{
std::cout << "\n Ref |"
<< " Dofs |"
<< " ω |" ;
std::cout << " H(curl) Error |"
<< " Rate |" ;
std::cout << " Residual |"
<< " Rate |"
<< " PCG it |" << endl;
std::cout << std::string(87,'-')
<< endl;
}
double res0 = 0.;
double err0 = 0.;
int dof0;
ParGridFunction E_r, E_i;
ParaViewDataCollection * paraview_dc = nullptr;
if (paraview)
{
paraview_dc = new ParaViewDataCollection("Plane", &pmesh);
paraview_dc->SetPrefixPath("ParaViewPrimal/Maxwell");
paraview_dc->SetLevelsOfDetail(order);
paraview_dc->SetCycle(0);
paraview_dc->SetDataFormat(VTKFormat::BINARY);
paraview_dc->SetHighOrderOutput(true);
paraview_dc->SetTime(0.0); // set the time
paraview_dc->RegisterField("E_r",&E_r);
paraview_dc->RegisterField("E_i",&E_i);
}
if (static_cond) { a->EnableStaticCondensation(); }
for (int it = 0; it<=pr; it++)
{
a->Assemble();
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
E_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = E_fes->GetVSize();
offsets[2] = hatE_fes->GetVSize();
offsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
ParGridFunction E_gf_r(E_fes, x, offsets[0]);
ParGridFunction E_gf_i(E_fes, x, offsets.Last() + offsets[0]);
E_gf_r.ProjectBdrCoefficientTangent(Eex_r, ess_bdr);
E_gf_i.ProjectBdrCoefficientTangent(Eex_i, ess_bdr);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
int num_blocks = BlockA_r->NumRowBlocks();
Array<int> tdof_offsets(2*num_blocks+1);
tdof_offsets[0] = 0;
for (int i=0; i<num_blocks; i++)
{
const int h = BlockA_r->GetBlock(i,i).Height();
tdof_offsets[i+1] = h;
tdof_offsets[num_blocks+i+1] = h;
}
tdof_offsets.PartialSum();
BlockOperator blockA(tdof_offsets);
for (int i = 0; i<num_blocks; i++)
{
for (int j = 0; j<num_blocks; j++)
{
blockA.SetBlock(i,j,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i,j+num_blocks,&BlockA_i->GetBlock(i,j), -1.0);
blockA.SetBlock(i+num_blocks,j+num_blocks,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i+num_blocks,j,&BlockA_i->GetBlock(i,j));
}
}
X = 0.;
BlockDiagonalPreconditioner M(tdof_offsets);
ParFiniteElementSpace *ams_fes = nullptr;
if (static_cond)
{
ams_fes = new ParFiniteElementSpace(&pmesh,
E_fes->FEColl()->GetTraceCollection());
}
HypreAMS * solver_E = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(0,0),
(static_cond) ? ams_fes : E_fes);
solver_E->SetPrintLevel(0);
HypreSolver * solver_hatE = nullptr;
if (dim == 2)
{
solver_hatE = new HypreBoomerAMG((HypreParMatrix &)BlockA_r->GetBlock(1,1));
dynamic_cast<HypreBoomerAMG*>(solver_hatE)->SetPrintLevel(0);
}
else
{
solver_hatE = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(1,1),
hatE_fes);
dynamic_cast<HypreAMS*>(solver_hatE)->SetPrintLevel(0);
}
M.SetDiagonalBlock(0,solver_E);
M.SetDiagonalBlock(1,solver_hatE);
M.SetDiagonalBlock(num_blocks,solver_E);
M.SetDiagonalBlock(num_blocks+1,solver_hatE);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(10000);
cg.SetPrintLevel(0);
cg.SetPreconditioner(M);
cg.SetOperator(blockA);
cg.Mult(B, X);
delete ams_fes;
for (int i = 0; i<num_blocks; i++)
{
delete &M.GetDiagonalBlock(i);
}
int num_iter = cg.GetNumIterations();
a->RecoverFEMSolution(X,x);
Vector & residuals = a->ComputeResidual(x);
double residual = residuals.Norml2();
double maxresidual = residuals.Max();
double globalresidual = residual * residual;
MPI_Allreduce(MPI_IN_PLACE,&maxresidual,1,MPI_DOUBLE,MPI_MAX,MPI_COMM_WORLD);
MPI_Allreduce(MPI_IN_PLACE,&globalresidual,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
globalresidual = sqrt(globalresidual);
E_r.MakeRef(E_fes,x, 0);
E_i.MakeRef(E_fes,x, offsets.Last());
int dofs = 0;
for (int i = 0; i<trial_fes.Size(); i++)
{
dofs += trial_fes[i]->GlobalTrueVSize();
}
double HcurlError = 0.0;
double rate_err = 0.0;
VectorFunctionCoefficient curlEex_r(dim,curlE_exact_r);
VectorFunctionCoefficient curlEex_i(dim,curlE_exact_i);
double E_err_r = E_r.ComputeHCurlError(&Eex_r,&curlEex_r);
double E_err_i = E_i.ComputeHCurlError(&Eex_i,&curlEex_i);
HcurlError = sqrt( E_err_r*E_err_r + E_err_i*E_err_i);
rate_err = (it) ? dim*log(err0/HcurlError)/log((double)dof0/dofs) : 0.0;
err0 = HcurlError;
double rate_res =
(it) ? dim*log(res0/globalresidual)/log((double)dof0/dofs) : 0.0;
res0 = globalresidual;
dof0 = dofs;
if (myid == 0)
{
std::ios oldState(nullptr);
oldState.copyfmt(std::cout);
std::cout << std::right << std::setw(5) << it << " | "
<< std::setw(10) << dof0 << " | "
<< std::setprecision(1) << std::fixed
<< std::setw(4) << 2.0*rnum << " π | "
<< std::setprecision(3);
std::cout << std::setw(15) << std::scientific << err0 << " | "
<< std::setprecision(2)
<< std::setw(6) << std::fixed << rate_err << " | " ;
std::cout << std::setprecision(3)
<< std::setw(10) << std::scientific << res0 << " | "
<< std::setprecision(2)
<< std::setw(6) << std::fixed << rate_res << " | "
<< std::setw(6) << std::fixed << num_iter << " | "
<< std::endl;
std::cout.copyfmt(oldState);
}
if (visualization)
{
const char * keys = (it == 0 && dim == 2) ? "jRcml\n" : nullptr;
char vishost[] = "localhost";
int visport = 19916;
VisualizeField(E_out_r,vishost, visport, E_r,
"Numerical Electric field (real part)", 0, 0, 500, 500, keys);
}
if (paraview)
{
paraview_dc->SetCycle(it);
paraview_dc->SetTime((double)it);
paraview_dc->Save();
}
if (it == pr)
{
break;
}
pmesh.UniformRefinement();
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
}
if (paraview)
{
delete paraview_dc;
}
delete a;
delete F_fec;
delete hatE_fes;
delete hatE_fec;
delete E_fec;
delete E_fes;
return 0;
}
void E_exact_r(const Vector &x, Vector & E_r)
{
std::vector<std::complex<double>> E;
maxwell_solution(x,E);
E_r.SetSize(E.size());
for (unsigned i = 0; i < E.size(); i++)
{
E_r[i]= E[i].real();
}
}
void E_exact_i(const Vector &x, Vector & E_i)
{
std::vector<std::complex<double>> E;
maxwell_solution(x, E);
E_i.SetSize(E.size());
for (unsigned i = 0; i < E.size(); i++)
{
E_i[i]= E[i].imag();
}
}
void curlE_exact_r(const Vector &x, Vector &curlE_r)
{
std::vector<std::complex<double>> curlE;
maxwell_solution_curl(x, curlE);
curlE_r.SetSize(curlE.size());
for (unsigned i = 0; i < curlE.size(); i++)
{
curlE_r[i]= curlE[i].real();
}
}
void curlE_exact_i(const Vector &x, Vector &curlE_i)
{
std::vector<std::complex<double>> curlE;
maxwell_solution_curl(x, curlE);
curlE_i.SetSize(curlE.size());
for (unsigned i = 0; i < curlE.size(); i++)
{
curlE_i[i]= curlE[i].imag();
}
}
void curlcurlE_exact_r(const Vector &x, Vector & curlcurlE_r)
{
std::vector<std::complex<double>> curlcurlE;
maxwell_solution_curlcurl(x, curlcurlE);
curlcurlE_r.SetSize(curlcurlE.size());
for (unsigned i = 0; i < curlcurlE.size(); i++)
{
curlcurlE_r[i]= curlcurlE[i].real();
}
}
void curlcurlE_exact_i(const Vector &x, Vector & curlcurlE_i)
{
std::vector<std::complex<double>> curlcurlE;
maxwell_solution_curlcurl(x, curlcurlE);
curlcurlE_i.SetSize(curlcurlE.size());
for (unsigned i = 0; i < curlcurlE.size(); i++)
{
curlcurlE_i[i]= curlcurlE[i].imag();
}
}
void rhs_func_r(const Vector &x, Vector & J_r)
{
Vector E_r, curlcurlE_r;
E_exact_r(x,E_r);
curlcurlE_exact_r(x,curlcurlE_r);
J_r.SetSize(dim);
for (int i = 0; i<dim; i++)
{
J_r(i) = 1./mu*curlcurlE_r(i) - omega *omega * epsilon * E_r(i);
}
}
void rhs_func_i(const Vector &x, Vector & J_i)
{
Vector E_i, curlcurlE_i;
E_exact_i(x,E_i);
curlcurlE_exact_i(x,curlcurlE_i);
J_i.SetSize(dim);
for (int i = 0; i<dim; i++)
{
J_i(i) = 1./mu*curlcurlE_i(i) - omega *omega * epsilon * E_i(i);
}
}
void maxwell_solution(const Vector & X, std::vector<complex<double>> &E)
{
complex<double> zi = complex<double>(0., 1.);
E.resize(dim);
for (int i = 0; i < dim; ++i)
{
E[i] = 0.0;
}
E[0] = exp(zi * omega * (X.Sum()));
}
void maxwell_solution_curl(const Vector & X,
std::vector<complex<double>> &curlE)
{
complex<double> zi = complex<double>(0., 1.);
curlE.resize(dimc);
for (int i = 0; i < dimc; ++i)
{
curlE[i] = 0.0;
}
std::complex<double> pw = exp(zi * omega * (X.Sum()));
if (dim == 3)
{
curlE[0] = 0.0;
curlE[1] = zi * omega * pw;
curlE[2] = -zi * omega * pw;
}
else
{
curlE[0] = -zi * omega * pw;
}
}
void maxwell_solution_curlcurl(const Vector & X,
std::vector<complex<double>> &curlcurlE)
{
complex<double> zi = complex<double>(0., 1.);
curlcurlE.resize(dim);
for (int i = 0; i < dim; ++i)
{
curlcurlE[i] = 0.0;;
}
std::complex<double> pw = exp(zi * omega * (X.Sum()));
if (dim == 3)
{
curlcurlE[0] = 2.0 * omega * omega * pw;
curlcurlE[1] = - omega * omega * pw;
curlcurlE[2] = - omega * omega * pw;
}
else
{
curlcurlE[0] = omega * omega * pw;
curlcurlE[1] = -omega * omega * pw;
}
}
+80 -80
View File
@@ -44,7 +44,7 @@
// The DPG UW deals with the First Order System
// i ω μ H + ∇ × E = 0, in Ω
// -i ω ϵ E + ∇ × H = J, in Ω
// E × n = E_0, on ∂Ω
// E × n = E, on ∂Ω
// Note: Ĵ = -iωJ
// The ultraweak-DPG formulation is obtained by integration by parts of both
@@ -57,7 +57,7 @@
// where A = [0 1; -1 0];
// E ∈ (L²(Ω))² , H ∈ L²(Ω)
// Ê ∈ H^-1/2(Ω)(Γₕ), Ĥ ∈ H^1/2(Γₕ)
// Ê ∈ H^-1/2(Γₕ), Ĥ ∈ H^1/2(Γₕ)
// i ω μ (H,F) + (E, ∇ × F) + < AÊ, F > = 0, ∀ F ∈ H¹
// -i ω ϵ (E,G) + (H,∇ × G) + < Ĥ, G × n > = (J,G) ∀ G ∈ H(curl,Ω)
// Ê = E₀ on ∂Ω
@@ -71,7 +71,7 @@
// in 3D
// E,H ∈ (L^2(Ω))³
// Ê ∈ H_0^1/2(Ω)(curl, Γₕ), Ĥ ∈ H^-1/2(curl, Γₕ)
// Ê ∈ H\_0^1/2(Ω)(curl, Γₕ), Ĥ ∈ H^-1/2(curl, Γₕ)
// i ω μ (H,F) + (E,∇ × F) + < Ê, F × n > = 0, ∀ F ∈ H(curl,Ω)
// -i ω ϵ (E,G) + (H,∇ × G) + < Ĥ, G × n > = (J,G) ∀ G ∈ H(curl,Ω)
// Ê × n = E₀ on ∂Ω
@@ -106,7 +106,7 @@
// in 2D
// E ∈ (L²(Ω))² , H ∈ L²(Ω)
// Ê ∈ H^-1/2(Ω)(Γₕ), Ĥ ∈ H^1/2(Γₕ)
// i ω μ (α⁻¹ H,F) + (E, ∇ × F) + < AÊ, F > = 0, ∀ F ∈ H¹
// i ω μ (α⁻¹ H,F) + (E, ∇ × F) + < AÊ, F > = 0, ∀ F ∈ H¹
// -i ω ϵ (β E,G) + (H,∇ × G) + < Ĥ, G × n > = (J,G) ∀ G ∈ H(curl,Ω)
// Ê = E₀ on ∂Ω
// ---------------------------------------------------------------------------------
@@ -121,10 +121,10 @@
//
// in 3D
// E,H ∈ (L^2(Ω))³
// Ê ∈ H_0^1/2(Ω)(curl, Γ_h), Ĥ ∈ H^-1/2(curl, Γₕ)
// Ê ∈ H_0^1/2(Ω)(curl, Γ), Ĥ ∈ H^-1/2(curl, Γₕ)
// i ω μ (α⁻¹ H,F) + (E,∇ × F) + < Ê, F × n > = 0, ∀ F ∈ H(curl,Ω)
// -i ω ϵ (β E,G) + (H,∇ × G) + < Ĥ, G × n > = (J,G) ∀ G ∈ H(curl,Ω)
// × n = E_0 on ∂Ω
// -i ω ϵ (β E,G) + (H,∇ × G) + < Ĥ, G × n > = (J,G) ∀ G ∈ H(curl,Ω)
// Ê × n = E on ∂Ω
// -------------------------------------------------------------------------------
// | | E | H | Ê | Ĥ | RHS |
// -------------------------------------------------------------------------------
@@ -138,6 +138,7 @@
#include "mfem.hpp"
#include "util/pcomplexweakform.hpp"
#include "util/pml.hpp"
#include "util/preconditioners.hpp"
#include "../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
@@ -146,13 +147,13 @@ using namespace std;
using namespace mfem;
using namespace mfem::common;
void E_exact_r(const Vector &x, Vector & E_r);
void E_exact_i(const Vector &x, Vector & E_i);
void H_exact_r(const Vector &x, Vector & H_r);
void H_exact_i(const Vector &x, Vector & H_i);
void rhs_func_r(const Vector &x, Vector & J_r);
void rhs_func_i(const Vector &x, Vector & J_i);
@@ -221,6 +222,9 @@ int main(int argc, char *argv[])
int delta_order = 1;
real_t rnum=1.0;
real_t theta = 0.0;
bool pmg = false;
int pmg_levels = -1;
real_t relax_factor = 2.0/3;
bool static_cond = false;
int iprob = 0;
int sr = 0;
@@ -255,6 +259,12 @@ int main(int argc, char *argv[])
"Number of parallel refinements.");
args.AddOption(&pr, "-pref", "--parallel-ref",
"Number of parallel refinements.");
args.AddOption(&pmg, "-pmg", "--p-refinement-multigrid", "-no-pmg",
"--no-p-refinement-multigrid", "Enable P-Refinement Multigrid.");
args.AddOption(&pmg_levels, "-pmgl","--p-refinement-multigrid-levels",
"Number of levels for P-Refinement Multigrid.");
args.AddOption(&relax_factor, "-rf", "--relaxation-factor",
"Relaxation factor for the p-multigrid smoother.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
@@ -345,7 +355,7 @@ int main(int argc, char *argv[])
F_space = 0,
G_space = 1
};
// L2 space for E
// Vector L2 L2 space for E
FiniteElementCollection *E_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *E_fes = new ParFiniteElementSpace(&pmesh,E_fec,dim);
@@ -503,7 +513,16 @@ int main(int argc, char *argv[])
*negepsomeg_detJ_Jt_J_inv_r_rot, attrPML);
}
pmesh.EnsureNodes();
int meshorder = pmesh.GetNodalFESpace()->FEColl()->GetOrder();
const IntegrationRule &test_ir = IntRules.Get(pmesh.GetElementGeometry(0),
2*test_order + 5 + meshorder);
const IntegrationRule &trial_ir = IntRules.Get(pmesh.GetElementGeometry(0),
order+test_order + 5 + meshorder);
ParComplexDPGWeakForm * a = new ParComplexDPGWeakForm(trial_fes,test_fec);
// a->SetTrialIntegrationRule(trial_ir);
// a->SetTestIntegrationRule(test_ir);
a->StoreMatrices(); // needed for AMR
// (E,∇ × F)
@@ -821,89 +840,70 @@ int main(int argc, char *argv[])
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
BlockOperator * BlockA_r = dynamic_cast<BlockOperator *>(&Ahc->real());
BlockOperator * BlockA_i = dynamic_cast<BlockOperator *>(&Ahc->imag());
int num_blocks = BlockA_r->NumRowBlocks();
Array<int> tdof_offsets(2*num_blocks+1);
tdof_offsets[0] = 0;
int skip = (static_cond) ? 0 : 2;
int k = (static_cond) ? 2 : 0;
for (int i=0; i<num_blocks; i++)
Array<ParFiniteElementSpace *> prec_fes;
if (static_cond)
{
tdof_offsets[i+1] = trial_fes[i+k]->GetTrueVSize();
tdof_offsets[num_blocks+i+1] = trial_fes[i+k]->GetTrueVSize();
}
tdof_offsets.PartialSum();
BlockOperator blockA(tdof_offsets);
for (int i = 0; i<num_blocks; i++)
{
for (int j = 0; j<num_blocks; j++)
{
blockA.SetBlock(i,j,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i,j+num_blocks,&BlockA_i->GetBlock(i,j), -1.0);
blockA.SetBlock(i+num_blocks,j+num_blocks,&BlockA_r->GetBlock(i,j));
blockA.SetBlock(i+num_blocks,j,&BlockA_i->GetBlock(i,j));
}
}
X = 0.;
BlockDiagonalPreconditioner M(tdof_offsets);
if (!static_cond)
{
HypreBoomerAMG * solver_E = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(0,0));
solver_E->SetPrintLevel(0);
solver_E->SetSystemsOptions(dim);
HypreBoomerAMG * solver_H = new HypreBoomerAMG((HypreParMatrix &)
BlockA_r->GetBlock(1,1));
solver_H->SetPrintLevel(0);
solver_H->SetSystemsOptions(dim);
M.SetDiagonalBlock(0,solver_E);
M.SetDiagonalBlock(1,solver_H);
M.SetDiagonalBlock(num_blocks,solver_E);
M.SetDiagonalBlock(num_blocks+1,solver_H);
}
HypreSolver * solver_hatH = nullptr;
HypreAMS * solver_hatE = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(skip,
skip),
hatE_fes);
solver_hatE->SetPrintLevel(0);
if (dim == 2)
{
solver_hatH = new HypreBoomerAMG((HypreParMatrix &)BlockA_r->GetBlock(skip+1,
skip+1));
dynamic_cast<HypreBoomerAMG*>(solver_hatH)->SetPrintLevel(0);
a->GetTraceFESpaces(prec_fes);
}
else
{
solver_hatH = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(skip+1,skip+1),
hatH_fes);
dynamic_cast<HypreAMS*>(solver_hatH)->SetPrintLevel(0);
prec_fes = trial_fes;
}
Solver * cprec = nullptr;
if (pmg)
{
#ifdef MFEM_USE_COMPLEX_MUMPS
bool mumps_coarse_solver = true;
#else
bool mumps_coarse_solver = false;
#endif
std::vector<Array<int>> ess_bdr_marker(prec_fes.Size());
for (int b = 0; b<prec_fes.Size(); b++)
{
if (pmesh.bdr_attributes.Size())
{
ess_bdr_marker[b].SetSize(pmesh.bdr_attributes.Max());
int ess_block = (static_cond) ? 0 : 2;
if (b == ess_block) // hatE
{
ess_bdr_marker[b] = ess_bdr;
}
else
{
ess_bdr_marker[b] = 0;
}
}
}
cprec = new ComplexPRefinementMultigrid(prec_fes, ess_bdr_marker, *Ahc,
pmg_levels, relax_factor, mumps_coarse_solver);
}
else
{
BlockDiagonalPreconditioner * real_prec = new BlockDiagonalPreconditioner(
BlockA_r->RowOffsets());
real_prec->owns_blocks = 1;
for (int i = 0; i<BlockA_r->NumRowBlocks(); i++)
{
auto prec = MakeFESpaceDefaultSolver(prec_fes[i],0);
prec->SetOperator(BlockA_r->GetBlock(i,i));
real_prec->SetDiagonalBlock(i,prec);
}
cprec = new ComplexPreconditioner(real_prec, true);
}
M.SetDiagonalBlock(skip,solver_hatE);
M.SetDiagonalBlock(skip+1,solver_hatH);
M.SetDiagonalBlock(skip+num_blocks,solver_hatE);
M.SetDiagonalBlock(skip+num_blocks+1,solver_hatH);
CGSolver cg(MPI_COMM_WORLD);
// SLISolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-6);
cg.SetMaxIter(10000);
cg.SetPrintLevel(0);
cg.SetPreconditioner(M);
cg.SetOperator(blockA);
cg.SetOperator(*Ahc);
cg.SetPreconditioner(*cprec);
cg.Mult(B, X);
for (int i = 0; i<num_blocks; i++)
{
delete &M.GetDiagonalBlock(i);
}
delete cprec;
int num_iter = cg.GetNumIterations();
@@ -1185,7 +1185,7 @@ void hatE_exact_r(const Vector & x, Vector & hatE_r)
{
Vector E_r;
E_exact_r(x,E_r);
hatE_r.SetSize(hatE_r.Size());
hatE_r.SetSize(E_r.Size());
// rotate E_hat
hatE_r[0] = E_r[1];
hatE_r[1] = -E_r[0];
@@ -1202,7 +1202,7 @@ void hatE_exact_i(const Vector & x, Vector & hatE_i)
{
Vector E_i;
E_exact_i(x,E_i);
hatE_i.SetSize(hatE_i.Size());
hatE_i.SetSize(E_i.Size());
// rotate E_hat
hatE_i[0] = E_i[1];
hatE_i[1] = -E_i[0];
+390
View File
@@ -0,0 +1,390 @@
// MFEM Primal DPG parallel example for diffusion
//
// Compile with: make pdiffusion-primal
//
// Sample runs
// mpirun -np 4 pdiffusion-primal -m ../../data/inline-quad.mesh -o 3 -sref 1 -pref 2
// - Δ u = f, in Ω
// u = u₀, on ∂Ω
// --------------------------------------
// | | u | σ̂ | RHS |
// --------------------------------------
// | v | (∇u,∇v) | -(σ̂ₙ,v) | (f,v) |
//
// u ∈ H¹(Ω), σ̂ₙ ∈ H^-1/2(Τ)
#include "mfem.hpp"
#include "util/pweakform.hpp"
#include "../common/mfem-common.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
using namespace mfem::common;
void exact_u(const Vector & X, Vector & u);
void exact_gradu(const Vector & X, DenseMatrix &du);
void exact_laplacian_u(const Vector & X, Vector & d2u);
void f_exact(const Vector & X, Vector & f);
int main(int argc, char *argv[])
{
// 0. Initialize MPI and HYPRE.
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
// 1. Parse command-line options.
const char *mesh_file = "../../data/inline-quad.mesh";
int order = 1;
int delta_order = 1;
int sref = 0; // initial uniform mesh refinements
int pref = 0; // parallel mesh refinements for AMR
bool static_cond = false;
bool visualization = true;
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(&delta_order, "-do", "--delta_order",
"Order enrichment for DPG test space.");
args.AddOption(&sref, "-sref", "--num-serial-refinements",
"Number of initial serial uniform refinements");
args.AddOption(&pref, "-pref", "--num-parallel-refinements",
"Number of AMR refinements");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
for (int i = 0; i<sref; i++)
{
mesh.UniformRefinement();
}
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
// H1 space for u
FiniteElementCollection *u_fec = new H1_FECollection(order,dim);
ParFiniteElementSpace *u_fes = new ParFiniteElementSpace(&pmesh,u_fec,dim);
// H^-1/2 space for σ̂
FiniteElementCollection * hatsigma_fec = new RT_Trace_FECollection(order-1,dim);
ParFiniteElementSpace *hatsigma_fes = new ParFiniteElementSpace(&pmesh,
hatsigma_fec,dim);
ParFiniteElementSpace *hatsigma1_fes = new ParFiniteElementSpace(&pmesh,
hatsigma_fec);
int test_order = order+delta_order;
FiniteElementCollection * v_fec = new H1_FECollection(test_order, dim);
Array<ParFiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(u_fes);
trial_fes.Append(hatsigma_fes);
test_fec.Append(v_fec);
ConstantCoefficient one(1.0);
VectorFunctionCoefficient f(dim,
f_exact); // rhs for the manufactured solution problem
VectorFunctionCoefficient uex(dim,exact_u);
MatrixFunctionCoefficient graduex(dim,exact_gradu);
ParDPGWeakForm * a = new ParDPGWeakForm(trial_fes,test_fec);
a->SetTestFECollVdim(0,dim);
a->StoreMatrices(true); // this is needed for estimation of residual
// (∇u,∇v)
a->AddTrialIntegrator(new VectorDiffusionIntegrator(one),0,0);
// // -<σ̂,v> (sign is included in σ̂)
a->AddTrialIntegrator(new VectorTraceIntegrator,1,0);
// // (∇v,∇δv)
a->AddTestIntegrator(new VectorDiffusionIntegrator(one),0,0);
// // (v,δv)
a->AddTestIntegrator(new VectorMassIntegrator(one),0,0);
a->AddDomainLFIntegrator(new VectorDomainLFIntegrator(f),0);
if (myid == 0)
{
std::cout << "\n Ref |"
<< " Dofs |"
<< " H1 Error |"
<< " Rate |"
<< " Residual |"
<< " Rate |"
<< " PCG it |" << endl;
std::cout << std::string(72,'-') << endl;
}
socketstream u_out, ex_out;
double err0 = 0.;
int dof0=0.;
double res0=0.0;
ParGridFunction u_gf(u_fes);
ParGridFunction exact_gf(u_fes);
u_gf = 0.0;
if (static_cond) { a->EnableStaticCondensation(); }
for (int it = 0; it<=pref; it++)
{
a->Assemble();
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (pmesh.bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh.bdr_attributes.Max());
ess_bdr = 1;
u_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = u_fes->GetVSize();
offsets[2] = hatsigma_fes->GetVSize();
offsets.PartialSum();
BlockVector x(offsets);
x = 0.0;
u_gf.MakeRef(u_fes,x.GetBlock(0),0);
u_gf.ProjectBdrCoefficient(uex,ess_bdr);
exact_gf.ProjectCoefficient(uex);
Vector X,B;
OperatorPtr Ah;
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
BlockOperator * A = Ah.As<BlockOperator>();
// BlockDiagonalPreconditioner M(A->RowOffsets());
BlockDiagonalPreconditioner M(A->RowOffsets());
M.owns_blocks = 1;
// HypreBoomerAMG * amg0 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(0,0));
// amg0->SetSystemsOptions(dim);
#ifdef MFEM_USE_MUMPS
MUMPSSolver * amg0 = new MUMPSSolver(MPI_COMM_WORLD);
amg0->SetOperator((HypreParMatrix &)A->GetBlock(0,0));
MUMPSSolver * prec = new MUMPSSolver(MPI_COMM_WORLD);
prec->SetOperator((HypreParMatrix&)A->GetBlock(1,1));
#else
HypreBoomerAMG * amg0 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(0,0));
amg0->SetSystemsOptions(dim);
HypreBoomerAMG * prec = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(1,1));
prec->SetSystemsOptions(dim);
#endif
M.SetDiagonalBlock(0,amg0);
M.SetDiagonalBlock(1,prec);
// Array<int> toffsets(4);
// toffsets[0] = 0;
// toffsets[1] = A->RowOffsets()[1];
// toffsets[2] = (A->RowOffsets()[2]-A->RowOffsets()[1])/dim;
// toffsets[3] = (A->RowOffsets()[2]-A->RowOffsets()[1])/dim;
// toffsets.PartialSum();
// BlockDiagonalPreconditioner M(toffsets);
// M.owns_blocks = 1;
// HypreBoomerAMG * amg0 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(0,0));
// amg0->SetSystemsOptions(dim);
// HypreParMatrix & A11 = (HypreParMatrix &)A->GetBlock(1,1);
// Array<int> tdofs1(A11.Height()/2);
// Array<int> tdofs2(A11.Height()/2);
// for (int i = 0; i<tdofs1.Size(); i++)
// {
// tdofs1[i] = i;
// tdofs2[i] = tdofs1.Size() + i;
// }
// HypreParMatrix * S1 = GetSubHypreParMatrix(tdofs1,A11);
// HypreParMatrix * S2 = GetSubHypreParMatrix(tdofs2,A11);
// MUMPSSolver * prec1 = new MUMPSSolver(MPI_COMM_WORLD);
// prec1->SetOperator(*S1);
// MUMPSSolver * prec2 = new MUMPSSolver(MPI_COMM_WORLD);
// prec2->SetOperator(*S2);
// HypreSolver * prec1;
// HypreSolver * prec2;
// if (dim == 2)
// {
// // AMS preconditioner for 2D H(div) (trace) space
// prec1 = new HypreAMS(*S1, hatsigma1_fes);
// prec2 = new HypreAMS(*S2, hatsigma1_fes);
// }
// else
// {
// // ADS preconditioner for 3D H(div) (trace) space
// prec1 = new HypreADS(*S1, hatsigma1_fes);
// prec2 = new HypreADS(*S2, hatsigma1_fes);
// }
// M.SetDiagonalBlock(1,prec1);
// M.SetDiagonalBlock(2,prec2);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(0);
cg.SetPreconditioner(M);
cg.SetOperator(*A);
cg.Mult(B, X);
a->RecoverFEMSolution(X,x);
Vector & residuals = a->ComputeResidual(x);
// Vector residuals;
double residual = residuals.Norml2();
double maxresidual = residuals.Max();
double globalresidual = residual * residual;
MPI_Allreduce(MPI_IN_PLACE,&maxresidual,1,MPI_DOUBLE,MPI_MAX,MPI_COMM_WORLD);
MPI_Allreduce(MPI_IN_PLACE,&globalresidual,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
globalresidual = sqrt(globalresidual);
u_gf.MakeRef(u_fes,x.GetBlock(0),0);
int dofs = u_fes->GlobalTrueVSize() + hatsigma_fes->GlobalTrueVSize();
double u_err = u_gf.ComputeL2Error(uex);
double rate_err = (it) ? dim*log(err0/u_err)/log((double)dof0/dofs) : 0.0;
double rate_res = (it) ? dim*log(res0/globalresidual)/log((
double)dof0/dofs) : 0.0;
err0 = u_err;
res0 = globalresidual;
dof0 = dofs;
if (myid == 0)
{
std::ios oldState(nullptr);
oldState.copyfmt(std::cout);
std::cout << std::right << std::setw(5) << it << " | "
<< std::setw(10) << dof0 << " | "
<< std::setprecision(3)
<< std::setw(10) << std::scientific << err0 << " | "
<< std::setprecision(2)
<< std::setw(6) << std::fixed << rate_err << " | "
<< std::setprecision(3)
<< std::setw(10) << std::scientific << res0 << " | "
<< std::setprecision(2)
<< std::setw(6) << std::fixed << rate_res << " | "
<< std::setw(6) << std::fixed << cg.GetNumIterations() << " | "
<< std::endl;
std::cout.copyfmt(oldState);
}
if (visualization)
{
const char * keys = (it == 0 && dim == 2) ? "jRcm\n" : nullptr;
char vishost[] = "localhost";
int visport = 19916;
VisualizeField(u_out,vishost,visport,u_gf,
"Numerical u", 0,0,500,500,keys);
VisualizeField(ex_out,vishost,visport,exact_gf,
"Exact u", 0,0,500,500,keys);
}
if (it == pref) { break; }
pmesh.UniformRefinement();
for (int i=0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
hatsigma1_fes->Update(false);
a->Update();
exact_gf.Update();
}
delete a;
delete v_fec;
delete hatsigma_fes;
delete hatsigma_fec;
delete u_fec;
delete u_fes;
return 0;
}
void exact_u(const Vector & X, Vector & u)
{
int size = X.Size();
for (int i = 0; i<size; i++)
{
u(i) = sin(M_PI*X(i));
}
}
void exact_gradu(const Vector & X, DenseMatrix & du)
{
du.SetSize(X.Size());
for (int i = 0; i<du.Size(); i++)
{
du(i,i) = M_PI * cos(M_PI * X(i));
}
}
void exact_laplacian_u(const Vector & X, Vector & du)
{
int size = X.Size();
du.SetSize(size);
for (int i = 0; i<size; i++)
{
du(i) = -M_PI*M_PI * sin(M_PI*X(i));
}
}
void f_exact(const Vector & X, Vector & f)
{
f.SetSize(X.Size());
exact_laplacian_u(X,f);
f.Neg();
}
+120
View File
@@ -0,0 +1,120 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "blockcomplexhypremat.hpp"
#ifdef MFEM_USE_MPI
namespace mfem
{
void ParBlockComplexSystem::FillEssTdofLists(const Array<int> &
ess_tdof_list)
{
for (int i = 0; i < ess_tdofs.Size(); i++)
{
delete ess_tdofs[i];
ess_tdofs[i] = new Array<int>();
}
int j;
for (int i = 0; i < ess_tdof_list.Size(); i++)
{
int tdof = ess_tdof_list[i];
for (j = 0; j < nblocks; j++)
{
if (toffsets[j+1] > tdof) { break; }
}
ess_tdofs[j]->Append(tdof-toffsets[j]);
}
}
ComplexOperator * ParBlockComplexSystem::EliminateBC(const Array<int>
ess_tdof_list, Vector &X, Vector & B)
{
FillEssTdofLists(ess_tdof_list);
delete op_e_r;
delete op_e_i;
op_e_r = new BlockOperator(toffsets);
op_e_i = new BlockOperator(toffsets);
op_e_r->owns_blocks = 1;
op_e_i->owns_blocks = 1;
for (int i = 0; i < nblocks; i++)
{
for (int j = 0; j < nblocks; j++)
{
if (op_r->IsZeroBlock(i,j)) { continue; }
if (i == j)
{
auto mat_r = &(HypreParMatrix &)op_r->GetBlock(i,i);
op_e_r->SetBlock(i, i, mat_r->EliminateRowsCols(*ess_tdofs[i]));
if (!op_i->IsZeroBlock(i,j))
{
auto mat_i = &(HypreParMatrix &)op_i->GetBlock(i,i);
op_e_i->SetBlock(i, i, mat_i->EliminateCols(*ess_tdofs[i]));
mat_i->EliminateRows(*ess_tdofs[i]);
}
}
else
{
auto mat_r = &(HypreParMatrix &)op_r->GetBlock(i,j);
op_e_r->SetBlock(i, j, mat_r->EliminateCols(*ess_tdofs[j]));
mat_r->EliminateRows(*ess_tdofs[i]);
if (!op_i->IsZeroBlock(i,j))
{
auto mat_i = &(HypreParMatrix &)op_i->GetBlock(i,j);
op_e_i->SetBlock(i, j, mat_i->EliminateCols(*ess_tdofs[j]));
mat_i->EliminateRows(*ess_tdofs[i]);
}
}
}
}
int n = B.Size()/2;
Vector B_r(B, 0, n);
Vector B_i(B, n, n);
Vector X_r(X, 0, n);
Vector X_i(X, n, n);
// eliminate tdof is RHS
// B_r -= Ae_r*X_r + Ae_i X_i
// B_i -= Ae_i*X_r + Ae_r X_i
Vector tmp(B_r.Size());
op_e_r->Mult(X_r, tmp); B_r-=tmp;
op_e_i->Mult(X_i, tmp); B_r+=tmp;
op_e_i->Mult(X_r, tmp); B_i-=tmp;
op_e_r->Mult(X_i, tmp); B_i-=tmp;
for (int j = 0; j < nblocks; j++)
{
if (!ess_tdofs[j]->Size()) { continue; }
for (int i = 0; i < ess_tdofs[j]->Size(); i++)
{
int tdof = (*ess_tdofs[j])[i];
int gdof = tdof + toffsets[j];
B_r(gdof) = X_r(gdof); // diagonal policy is always one in parallel
B_i(gdof) = X_i(gdof); // diagonal policy is always one in parallel
}
}
X_r.SetSubVectorComplement(ess_tdof_list, 0.0);
X_i.SetSubVectorComplement(ess_tdof_list, 0.0);
return op;
}
} // namespace mfem
#endif
@@ -0,0 +1,75 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_BLOCKCOMPLEX
#define MFEM_BLOCKCOMPLEX
#include "mfem.hpp"
#ifdef MFEM_USE_MPI
namespace mfem
{
class ParBlockComplexSystem
{
protected:
/// ess_tdof list for each space
Array<Array<int> *> ess_tdofs;
Array<int> toffsets;
int nblocks;
/** split ess_tdof_list given in global tdof (for all spaces)
to individual lists for each space */
void FillEssTdofLists(const Array<int> & ess_tdof_list);
// Block Prolongation
BlockOperator * P = nullptr;
// Block Restriction
BlockMatrix * R = nullptr;
ComplexOperator * op = nullptr;
BlockOperator * op_r = nullptr;
BlockOperator * op_i = nullptr;
BlockOperator * op_e_r = nullptr;
BlockOperator * op_e_i = nullptr;
public:
ParBlockComplexSystem() {}
/// Creates bilinear form associated with FE spaces @a trial_pfes_.
ParBlockComplexSystem(ComplexOperator * op_)
: op(op_)
{
op_r = dynamic_cast<BlockOperator *>(&op->real());
op_i = dynamic_cast<BlockOperator *>(&op->imag());
toffsets = op_r->RowOffsets(); // (assumes square blockoperator)
nblocks = toffsets.Size() - 1;
ess_tdofs.SetSize(nblocks);
}
ComplexOperator * EliminateBC(const Array<int> ess_tdof_list, Vector &X,
Vector & B);
virtual ~ParBlockComplexSystem() {}
};
} // namespace mfem
#endif // MFEM_USE_MPI
#endif
+35 -17
View File
@@ -69,12 +69,14 @@ void BlockStaticCondensation::SetSpaces(Array<FiniteElementSpace*> & fes_)
nblocks = fes.Size();
rblocks = 0;
tr_fes.SetSize(nblocks);
tr_fec.SetSize(nblocks);
mesh = fes[0]->GetMesh();
IsTraceSpace.SetSize(nblocks);
const FiniteElementCollection * fec;
for (int i = 0; i < nblocks; i++)
{
tr_fec[i] = nullptr;
fec = fes[i]->FEColl();
IsTraceSpace[i] =
(dynamic_cast<const H1_Trace_FECollection*>(fec) ||
@@ -86,21 +88,24 @@ void BlockStaticCondensation::SetSpaces(Array<FiniteElementSpace*> & fes_)
pmesh = dynamic_cast<ParMesh *>(mesh);
tr_fes[i] = (fec->GetContType() == FiniteElementCollection::DISCONTINUOUS) ?
nullptr : (IsTraceSpace[i]) ? fes[i] :
new ParFiniteElementSpace(pmesh, fec->GetTraceCollection(), fes[i]->GetVDim(),
new ParFiniteElementSpace(pmesh, tr_fec[i] = fec->GetTraceCollection(),
fes[i]->GetVDim(),
fes[i]->GetOrdering());
}
else
{
tr_fes[i] = (fec->GetContType() == FiniteElementCollection::DISCONTINUOUS) ?
nullptr : (IsTraceSpace[i]) ? fes[i] :
new FiniteElementSpace(mesh, fec->GetTraceCollection(), fes[i]->GetVDim(),
new FiniteElementSpace(mesh, tr_fec[i] = fec->GetTraceCollection(),
fes[i]->GetVDim(),
fes[i]->GetOrdering());
}
#else
// skip if it's an L2 space (no trace space to construct)
tr_fes[i] = (fec->GetContType() == FiniteElementCollection::DISCONTINUOUS) ?
nullptr : (IsTraceSpace[i]) ? fes[i] :
new FiniteElementSpace(mesh, fec->GetTraceCollection(), fes[i]->GetVDim(),
new FiniteElementSpace(mesh, tr_fec[i] = fec->GetTraceCollection(),
fes[i]->GetVDim(),
fes[i]->GetOrdering());
#endif
if (tr_fes[i]) { rblocks++; }
@@ -213,39 +218,43 @@ void BlockStaticCondensation::GetReducedElementIndicesAndOffsets(int el,
{
int td = 0;
int ndof;
int vdim = fes[i]->GetVDim();
// if it's an L2 space (bubbles)
if (!tr_fes[i])
{
ndof = fes[i]->GetVDim()*fes[i]->GetFE(el)->GetDof();
ndof = fes[i]->GetFE(el)->GetDof();
td = 0;
}
else if (IsTraceSpace[i])
{
for (int iface = 0; iface < numfaces; iface++)
{
td += fes[i]->GetVDim()*fes[i]->GetFaceElement(faces[iface])->GetDof();
td += fes[i]->GetFaceElement(faces[iface])->GetDof();
}
ndof = td;
}
else
{
Array<int> trace_dofs;
ndof = fes[i]->GetVDim()*fes[i]->GetFE(el)->GetDof();
tr_fes[i]->GetElementVDofs(el, trace_dofs);
ndof = fes[i]->GetFE(el)->GetDof();
tr_fes[i]->GetElementDofs(el, trace_dofs);
td = trace_dofs.Size(); // number of trace dofs
}
offsets[i+1] = td;
tr_dofs.SetSize(td);
int_dofs.SetSize(ndof - td);
for (int j = 0; j<td; j++)
offsets[i+1] = td*vdim;
tr_dofs.SetSize(td*vdim);
int_dofs.SetSize(vdim*(ndof - td));
for (int k = 0; k<vdim; k++)
{
tr_dofs[j] = skip + j;
for (int j = 0; j<td; j++)
{
tr_dofs[j+k*td] = skip + j;
}
for (int j = 0; j<ndof-td; j++)
{
int_dofs[j+k*(ndof-td)] = skip + td + j;
}
skip+=ndof;
}
for (int j = 0; j<ndof-td; j++)
{
int_dofs[j] = skip + td + j;
}
skip+=ndof;
trace_ldofs.Append(tr_dofs);
interior_ldofs.Append(int_dofs);
@@ -976,6 +985,15 @@ BlockStaticCondensation::~BlockStaticCondensation()
delete lmat[i]; lmat[i] = nullptr;
delete lvec[i]; lvec[i] = nullptr;
}
for (int i = 0; i<tr_fes.Size(); i++)
{
if (tr_fec[i])
{
delete tr_fes[i];
delete tr_fec[i];
}
}
}
} // namespace mfem
+6
View File
@@ -40,6 +40,7 @@ class BlockStaticCondensation
// New set of "reduced" Finite Element Spaces
// (after static condensation)
Array<FiniteElementSpace *> tr_fes;
Array<FiniteElementCollection *> tr_fec;
Array<int> dof_offsets;
Array<int> tdof_offsets;
@@ -185,6 +186,11 @@ public:
full linear system, compute the solution of the full system 'sol'. */
void ComputeSolution(const Vector &sc_sol, Vector &sol) const;
void GetTraceFESpaces(Array<FiniteElementSpace *> & trace_fes) const
{
trace_fes = tr_fes;
}
};
}
+577
View File
@@ -0,0 +1,577 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "complexblockform.hpp"
namespace mfem
{
void ComplexBlockForm::Init()
{
integs_r.SetSize(fes.Size(), fes.Size());
integs_i.SetSize(fes.Size(), fes.Size());
for (int i = 0; i < integs_r.NumRows(); i++)
{
for (int j = 0; j < integs_r.NumCols(); j++)
{
integs_r(i,j) = new Array<BilinearFormIntegrator * >();
integs_i(i,j) = new Array<BilinearFormIntegrator * >();
}
}
ComputeOffsets();
mat_r = mat_e_r = NULL;
mat_i = mat_e_i = NULL;
diag_policy = mfem::Operator::DIAG_ONE;
height = dof_offsets[nblocks];
width = height;
initialized = true;
}
void ComplexBlockForm::ComputeOffsets()
{
dof_offsets.SetSize(nblocks+1);
tdof_offsets.SetSize(nblocks+1);
dof_offsets[0] = 0;
tdof_offsets[0] = 0;
for (int i =0; i<nblocks; i++)
{
dof_offsets[i+1] = fes[i]->GetVSize();
tdof_offsets[i+1] = fes[i]->GetTrueVSize();
}
dof_offsets.PartialSum();
tdof_offsets.PartialSum();
}
// Allocate SparseMatrix and RHS
void ComplexBlockForm::AllocMat()
{
mat_r = new BlockMatrix(dof_offsets);
mat_r->owns_blocks = 1;
mat_i = new BlockMatrix(dof_offsets);
mat_i->owns_blocks = 1;
for (int i = 0; i < mat_r->NumRowBlocks(); i++)
{
int h = dof_offsets[i+1] - dof_offsets[i];
for (int j = 0; j < mat_r->NumColBlocks(); j++)
{
int w = dof_offsets[j+1] - dof_offsets[j];
mat_r->SetBlock(i,j,new SparseMatrix(h, w));
mat_i->SetBlock(i,j,new SparseMatrix(h, w));
}
}
}
void ComplexBlockForm::Finalize(int skip_zeros)
{
if (mat_r)
{
mat_r->Finalize(skip_zeros);
mat_i->Finalize(skip_zeros);
}
if (mat_e_r)
{
mat_e_r->Finalize(skip_zeros);
mat_e_i->Finalize(skip_zeros);
}
}
/// Adds new Domain BF Integrator. Assumes ownership of @a bfi.
void ComplexBlockForm::AddDomainIntegrator(
BilinearFormIntegrator *bfi_r,
BilinearFormIntegrator *bfi_i,
int n, int m)
{
MFEM_VERIFY(n < fes.Size(),
"ComplexBlockFrom::AddDomainIntegrator: fespace row index out of bounds");
MFEM_VERIFY(m < fes.Size(),
"ComplexBlockFrom::AddDomainIntegrator: fespace col index out of bounds");
if (bfi_r) { integs_r(n,m)->Append(bfi_r); }
if (bfi_i) { integs_i(n,m)->Append(bfi_i); }
}
void ComplexBlockForm::BuildProlongation()
{
P = new BlockMatrix(dof_offsets, tdof_offsets);
R = new BlockMatrix(tdof_offsets, dof_offsets);
P->owns_blocks = 0;
R->owns_blocks = 0;
for (int i = 0; i<nblocks; i++)
{
const SparseMatrix *P_ = fes[i]->GetConformingProlongation();
if (P_)
{
const SparseMatrix *R_ = fes[i]->GetRestrictionMatrix();
P->SetBlock(i, i, const_cast<SparseMatrix*>(P_));
R->SetBlock(i, i, const_cast<SparseMatrix*>(R_));
}
}
}
void ComplexBlockForm::ConformingAssemble()
{
Finalize(0);
if (!P) { BuildProlongation(); }
BlockMatrix * Pt = Transpose(*P);
BlockMatrix * PtA_r = mfem::Mult(*Pt, *mat_r);
BlockMatrix * PtA_i = mfem::Mult(*Pt, *mat_i);
mat_r->owns_blocks = 0;
mat_i->owns_blocks = 0;
for (int i = 0; i < nblocks; i++)
{
for (int j = 0; j < nblocks; j++)
{
SparseMatrix * tmp_r = &mat_r->GetBlock(i,j);
SparseMatrix * tmp_i = &mat_i->GetBlock(i,j);
if (Pt->IsZeroBlock(i, i))
{
PtA_r->SetBlock(i, j, tmp_r);
PtA_i->SetBlock(i, j, tmp_i);
}
else
{
delete tmp_r;
delete tmp_i;
}
}
}
delete mat_r;
delete mat_i;
if (mat_e_r)
{
BlockMatrix *PtAe_r = mfem::Mult(*Pt, *mat_e_r);
BlockMatrix *PtAe_i = mfem::Mult(*Pt, *mat_e_i);
mat_e_r->owns_blocks = 0;
mat_e_i->owns_blocks = 0;
for (int i = 0; i<nblocks; i++)
{
for (int j = 0; j<nblocks; j++)
{
SparseMatrix * tmp_r = &mat_e_r->GetBlock(i, j);
SparseMatrix * tmp_i = &mat_e_i->GetBlock(i, j);
if (Pt->IsZeroBlock(i, i))
{
PtAe_r->SetBlock(i, j, tmp_r);
PtAe_i->SetBlock(i, j, tmp_i);
}
else
{
delete tmp_r;
delete tmp_i;
}
}
}
delete mat_e_r;
delete mat_e_i;
mat_e_r = PtAe_r;
mat_e_i = PtAe_i;
}
delete Pt;
mat_r = mfem::Mult(*PtA_r, *P);
mat_i = mfem::Mult(*PtA_i, *P);
PtA_r->owns_blocks = 0;
PtA_i->owns_blocks = 0;
for (int i = 0; i < nblocks; i++)
{
for (int j = 0; j < nblocks; j++)
{
SparseMatrix * tmp_r = &PtA_r->GetBlock(j, i);
SparseMatrix * tmp_i = &PtA_i->GetBlock(j, i);
if (P->IsZeroBlock(i, i))
{
mat_r->SetBlock(j, i, tmp_r);
mat_i->SetBlock(j, i, tmp_i);
}
else
{
delete tmp_r;
delete tmp_i;
}
}
}
delete PtA_r;
delete PtA_i;
if (mat_e_r)
{
BlockMatrix *PtAeP_r = mfem::Mult(*mat_e_r, *P);
BlockMatrix *PtAeP_i = mfem::Mult(*mat_e_i, *P);
mat_e_r->owns_blocks = 0;
mat_e_i->owns_blocks = 0;
for (int i = 0; i < nblocks; i++)
{
for (int j = 0; j < nblocks; j++)
{
SparseMatrix * tmp_r = &mat_e_r->GetBlock(j, i);
SparseMatrix * tmp_i = &mat_e_i->GetBlock(j, i);
if (P->IsZeroBlock(i, i))
{
PtAeP_r->SetBlock(j, i, tmp_r);
PtAeP_i->SetBlock(j, i, tmp_i);
}
else
{
delete tmp_r;
delete tmp_i;
}
}
}
delete mat_e_r;
delete mat_e_i;
mat_e_r = PtAeP_r;
mat_e_i = PtAeP_i;
}
height = 2*mat_r->Height();
width = 2*mat_r->Width();
}
/// Assembles the form i.e. sums over all domain integrators.
void ComplexBlockForm::Assemble(int skip_zeros)
{
ElementTransformation *eltrans;
Array<int> faces, ori;
DofTransformation doftrans_i, doftrans_j;
if (mat_r == NULL)
{
AllocMat();
}
// loop through the elements
DenseMatrix A_r, Ae_r;
DenseMatrix A_i, Ae_i;
Array<int> vdofs;
// loop through elements
for (int iel = 0; iel < mesh -> GetNE(); iel++)
{
Array<int> offs(fes.Size()+1); offs = 0;
eltrans = mesh->GetElementTransformation(iel);
for (int j = 0; j < fes.Size(); j++)
{
offs[j+1] = fes[j]->GetVDim() * fes[j]->GetFE(iel)->GetDof();
}
offs.PartialSum();
A_r.SetSize(offs.Last(),offs.Last()); A_r = 0.0;
A_i.SetSize(offs.Last(),offs.Last()); A_i = 0.0;
for (int j = 0; j < fes.Size(); j++)
{
const FiniteElement & fe_j = *fes[j]->GetFE(iel);
for (int i = 0; i < fes.Size(); i++)
{
const FiniteElement & fe_i = *fes[i]->GetFE(iel);
// real integrators
for (int k = 0; k < integs_r(i,j)->Size(); k++)
{
if (i == j)
{
(*integs_r(i,j))[k]->AssembleElementMatrix(fe_i,*eltrans,Ae_r);
}
else
{
(*integs_r(i,j))[k]->AssembleElementMatrix2(fe_i,fe_j,*eltrans,Ae_r);
}
A_r.AddSubMatrix(offs[j], offs[i], Ae_r);
}
// imag integrators
for (int k = 0; k < integs_i(i,j)->Size(); k++)
{
if (i == j)
{
(*integs_i(i,j))[k]->AssembleElementMatrix(fe_i,*eltrans,Ae_i);
}
else
{
(*integs_i(i,j))[k]->AssembleElementMatrix2(fe_i, fe_j, *eltrans, Ae_i);
}
A_i.AddSubMatrix(offs[j], offs[i], Ae_i);
}
}
}
ComplexDenseMatrix A(&A_r, &A_i, false, false);
// Assembly
for (int i = 0; i<fes.Size(); i++)
{
Array<int> vdofs_i;
doftrans_i.SetDofTransformation(nullptr);
fes[i]->GetElementVDofs(iel, vdofs_i,doftrans_i);
for (int j = 0; j < fes.Size(); j++)
{
Array<int> vdofs_j;
doftrans_j.SetDofTransformation(nullptr);
fes[j]->GetElementVDofs(iel, vdofs_j,doftrans_j);
A.real().GetSubMatrix(offs[i],offs[i+1],
offs[j],offs[j+1], Ae_r);
A.imag().GetSubMatrix(offs[i],offs[i+1],
offs[j],offs[j+1], Ae_i);
TransformDual(doftrans_i, doftrans_j, Ae_r);
TransformDual(doftrans_i, doftrans_j, Ae_i);
mat_r->GetBlock(i,j).AddSubMatrix(vdofs_i,vdofs_j, Ae_r);
mat_i->GetBlock(i,j).AddSubMatrix(vdofs_i,vdofs_j, Ae_i);
}
}
} // end of loop through elements
}
void ComplexBlockForm::FormLinearSystem(const Array<int>
&ess_tdof_list,
Vector &x,
Vector &b,
OperatorHandle &A,
Vector &X,
Vector &B,
int copy_interior)
{
FormSystemMatrix(ess_tdof_list, A);
Vector x_r(x, 0, x.Size()/2);
Vector x_i(x, x.Size()/2, x.Size()/2);
Vector b_r(b, 0, b.Size()/2);
Vector b_i(b, b.Size()/2, b.Size()/2);
if (!P)
{
EliminateVDofsInRHS(ess_tdof_list, x_r,x_i, b_r, b_i);
if (!copy_interior)
{
x_r.SetSubVectorComplement(ess_tdof_list, 0.0);
x_i.SetSubVectorComplement(ess_tdof_list, 0.0);
}
X.MakeRef(x, 0, x.Size());
B.MakeRef(b, 0, b.Size());
}
else // non conforming space
{
B.SetSize(2*P->Width());
Vector B_r(B, 0, P->Width());
Vector B_i(B, P->Width(),P->Width());
P->MultTranspose(b_r, B_r);
P->MultTranspose(b_i, B_i);
Vector tmp_r,tmp_i;
for (int i = 0; i<nblocks; i++)
{
if (P->IsZeroBlock(i,i))
{
int offset = tdof_offsets[i];
tmp_r.MakeRef(b_r, offset,tdof_offsets[i+1]-tdof_offsets[i]);
tmp_i.MakeRef(b_i, offset,tdof_offsets[i+1]-tdof_offsets[i]);
B_r.SetVector(tmp_r,offset);
B_i.SetVector(tmp_i,offset);
}
}
X.SetSize(2*R->Height());
Vector X_r(X, 0, X.Size()/2);
Vector X_i(X, X.Size()/2, X.Size()/2);
R->Mult(x_r, X_r);
R->Mult(x_i, X_i);
for (int i = 0; i<nblocks; i++)
{
if (R->IsZeroBlock(i,i))
{
int offset = tdof_offsets[i];
tmp_r.MakeRef(x_r, offset, tdof_offsets[i+1]-tdof_offsets[i]);
tmp_i.MakeRef(x_i, offset, tdof_offsets[i+1]-tdof_offsets[i]);
X_r.SetVector(tmp_r,offset);
X_i.SetVector(tmp_i,offset);
}
}
EliminateVDofsInRHS(ess_tdof_list, X_r, X_i, B_r, B_i);
if (!copy_interior)
{
X_r.SetSubVectorComplement(ess_tdof_list, 0.0);
X_i.SetSubVectorComplement(ess_tdof_list, 0.0);
}
}
}
void ComplexBlockForm::FormSystemMatrix(const Array<int>
&ess_tdof_list,
OperatorHandle &A)
{
if (!mat_e_r)
{
bool conforming = true;
for (int i = 0; i<nblocks; i++)
{
const SparseMatrix *P_ = fes[i]->GetConformingProlongation();
if (P_)
{
conforming = false;
break;
}
}
if (!conforming) { ConformingAssemble(); }
const int remove_zeros = 0;
EliminateVDofs(ess_tdof_list, diag_policy);
Finalize(remove_zeros);
}
mat = new ComplexOperator(mat_r,mat_i,false,false);
A.Reset(mat,false);
}
void ComplexBlockForm::EliminateVDofsInRHS(
const Array<int> &vdofs, const Vector &x_r, const Vector & x_i,
Vector &b_r, Vector & b_i)
{
mat_e_r->AddMult(x_r,b_r,-1.);
mat_e_i->AddMult(x_i,b_r,1.);
mat_e_r->AddMult(x_i,b_i,-1.);
mat_e_i->AddMult(x_r,b_i,-1.);
mat_r->PartMult(vdofs,x_r,b_r);
mat_r->PartMult(vdofs,x_i,b_i);
}
void ComplexBlockForm::EliminateVDofs(const Array<int> &vdofs,
Operator::DiagonalPolicy dpolicy)
{
if (mat_e_r == NULL)
{
Array<int> offsets;
offsets.MakeRef( (P) ? tdof_offsets : dof_offsets);
mat_e_r = new BlockMatrix(offsets);
mat_e_r->owns_blocks = 1;
mat_e_i = new BlockMatrix(offsets);
mat_e_i->owns_blocks = 1;
for (int i = 0; i < mat_e_r->NumRowBlocks(); i++)
{
int h = offsets[i+1] - offsets[i];
for (int j = 0; j < mat_e_r->NumColBlocks(); j++)
{
int w = offsets[j+1] - offsets[j];
mat_e_r->SetBlock(i, j, new SparseMatrix(h, w));
mat_e_i->SetBlock(i, j, new SparseMatrix(h, w));
}
}
}
mat_r->EliminateRowCols(vdofs, mat_e_r, diag_policy);
mat_i->EliminateRowCols(vdofs, mat_e_i, Operator::DiagonalPolicy::DIAG_ZERO);
}
void ComplexBlockForm::RecoverFEMSolution(const Vector &X, Vector &x)
{
if (!P)
{
x.SyncMemory(X);
}
else
{
x.SetSize(2*P->Height());
Vector X_r(const_cast<Vector &>(X), 0, X.Size()/2);
Vector X_i(const_cast<Vector &>(X), X.Size()/2, X.Size()/2);
Vector x_r(x, 0, x.Size()/2);
Vector x_i(x, x.Size()/2, x.Size()/2);
P->Mult(X_r, x_r);
P->Mult(X_i, x_i);
Vector tmp_r, tmp_i;
for (int i = 0; i<nblocks; i++)
{
if (P->IsZeroBlock(i,i))
{
int offset = tdof_offsets[i];
tmp_r.MakeRef(X_r, offset, tdof_offsets[i+1]-tdof_offsets[i]);
tmp_i.MakeRef(X_i, offset, tdof_offsets[i+1]-tdof_offsets[i]);
x_r.SetVector(tmp_r,offset);
x_i.SetVector(tmp_i,offset);
}
}
}
}
void ComplexBlockForm::ReleaseInitMemory()
{
if (initialized)
{
for (int k = 0; k < integs_r.NumRows(); k++)
{
for (int l = 0; l < integs_r.NumCols(); l++)
{
for (int i = 0; i < integs_r(k,l)->Size(); i++)
{
delete (*integs_r(k,l))[i];
}
delete integs_r(k,l);
for (int i = 0; i < integs_i(k,l)->Size(); i++)
{
delete (*integs_i(k,l))[i];
}
delete integs_i(k,l);
}
}
integs_r.DeleteAll();
integs_i.DeleteAll();
}
}
void ComplexBlockForm::Update()
{
delete mat_e_r; mat_e_r = nullptr;
delete mat_e_i; mat_e_i = nullptr;
delete mat; mat = nullptr;
delete mat_r; mat_r = nullptr;
delete mat_i; mat_i = nullptr;
if (P)
{
delete P; P = nullptr;
delete R; R = nullptr;
}
ComputeOffsets();
diag_policy = mfem::Operator::DIAG_ONE;
height = dof_offsets[nblocks];
width = height;
initialized = true;
}
ComplexBlockForm::~ComplexBlockForm()
{
delete mat_e_r; mat_e_r = nullptr;
delete mat_e_i; mat_e_i = nullptr;
delete mat; mat = nullptr;
delete mat_r; mat_r = nullptr;
delete mat_i; mat_i = nullptr;
ReleaseInitMemory();
if (P)
{
delete P;
delete R;
}
}
} // namespace mfem
+199
View File
@@ -0,0 +1,199 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_COMPLEX_BLOCKFORM
#define MFEM_COMPLEX_BLOCKFORM
#include "mfem.hpp"
#include "complexstaticcond.hpp"
namespace mfem
{
class ComplexBlockForm
{
protected:
bool initialized = false;
Mesh * mesh = nullptr;
int height, width;
int nblocks;
Array<int> dof_offsets;
Array<int> tdof_offsets;
/// Block matrix $ M $ to be associated with the real/imag Block bilinear form. Owned.
BlockMatrix *mat_r = nullptr;
BlockMatrix *mat_i = nullptr;
ComplexOperator * mat = nullptr;
/** @brief Block Matrix $ M_e $ used to store the eliminations
from the b.c. Owned.
$ M + M_e = M_{original} $ */
BlockMatrix *mat_e_r = nullptr;
BlockMatrix *mat_e_i = nullptr;
/// FE spaces
Array<FiniteElementSpace * > fes;
/// Set of Trial Integrators to be applied for matrix A
Array2D<Array<BilinearFormIntegrator * > * > integs_r;
Array2D<Array<BilinearFormIntegrator * > * > integs_i;
/// Block Prolongation
BlockMatrix * P = nullptr;
/// Block Restriction
BlockMatrix * R = nullptr;
mfem::Operator::DiagonalPolicy diag_policy;
void Init();
void ReleaseInitMemory();
// Allocate appropriate SparseMatrix and assign it to mat
void AllocMat();
void ConformingAssemble();
void ComputeOffsets();
virtual void BuildProlongation();
private:
public:
ComplexBlockForm()
{
height = 0;
width = 0;
}
/// Creates bilinear form associated with FE spaces @a fes_.
ComplexBlockForm(Array<FiniteElementSpace* > & fes_)
{
SetSpaces(fes_);
}
void SetSpaces(Array<FiniteElementSpace* > & fes_)
{
fes = fes_;
nblocks = fes.Size();
mesh = fes[0]->GetMesh();
Init();
}
// Get the size of the bilinear form of the ComplexBlockForm
int Size() const { return height; }
// Pre-allocate the internal real and imag BlockMatrix before assembly.
void AllocateMatrix() { if (mat_r == nullptr) { AllocMat(); } }
/// Finalizes the matrix initialization.
void Finalize(int skip_zeros = 1);
/// Returns a reference to the BlockMatrix: $ M_r $
BlockMatrix &BlockMat_r()
{
MFEM_VERIFY(mat_r, "mat_r is NULL and can't be dereferenced");
return *mat_r;
}
/// Returns a reference to the BlockMatrix: $ M_i $
BlockMatrix &BlockMat_i()
{
MFEM_VERIFY(mat_i, "mat_i is NULL and can't be dereferenced");
return *mat_i;
}
/// Returns a reference to the BlockMatrix of eliminated b.c.: $ M_e_r $
BlockMatrix &BlockMatElim_r()
{
MFEM_VERIFY(mat_e_r, "mat_e is NULL and can't be dereferenced");
return *mat_e_r;
}
/// Returns a reference to the BlockMatrix of eliminated b.c.: $ M_e_i $
BlockMatrix &BlockMatElim_i()
{
MFEM_VERIFY(mat_e_i, "mat_e is NULL and can't be dereferenced");
return *mat_e_i;
}
/** Adds new Trial Integrator. Assumes ownership of @a bfi_r and @a bfi_i.
@a n and @a m correspond to the trial FESpace and test FEColl
respectively */
void AddDomainIntegrator(BilinearFormIntegrator *bfi_r,
BilinearFormIntegrator *bfi_i,
int n, int m);
/// Assembles the form i.e. sums over all integrators.
void Assemble(int skip_zeros = 1);
virtual void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b, OperatorHandle & A,
Vector &X, Vector &B, int copy_interior = 0);
template <typename OpType>
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b, OpType &A,
Vector &X, Vector &B, int copy_interior = 0)
{
OperatorHandle Ah;
FormLinearSystem(ess_tdof_list, x, b, Ah, X, B, copy_interior);
OpType *A_ptr = Ah.Is<OpType>();
MFEM_VERIFY(A_ptr, "invalid OpType used");
A.MakeRef(*A_ptr);
}
virtual void FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A);
template <typename OpType>
void FormSystemMatrix(const Array<int> &ess_tdof_list, OpType &A)
{
OperatorHandle Ah;
FormSystemMatrix(ess_tdof_list, Ah);
OpType *A_ptr = Ah.Is<OpType>();
MFEM_VERIFY(A_ptr, "invalid OpType used");
A.MakeRef(*A_ptr);
}
void EliminateVDofs(const Array<int> &vdofs,
Operator::DiagonalPolicy dpolicy = Operator::DIAG_ONE);
void EliminateVDofsInRHS(const Array<int> &vdofs,
const Vector &x_r, const Vector & x_i,
Vector &b_r, Vector & b_i);
virtual void RecoverFEMSolution(const Vector &X, Vector &x);
/// Sets diagonal policy used upon construction of the linear system.
/** Policies include:
- DIAG_ZERO (Set the diagonal values to zero)
- DIAG_ONE (Set the diagonal values to one)
- DIAG_KEEP (Keep the diagonal values)
*/
void SetDiagonalPolicy(Operator::DiagonalPolicy policy)
{
diag_policy = policy;
}
virtual void Update();
/// Destroys bilinear form.
virtual ~ComplexBlockForm();
};
} // namespace mfem
#endif
+36 -18
View File
@@ -71,12 +71,14 @@ void ComplexBlockStaticCondensation::SetSpaces(Array<FiniteElementSpace*> &
nblocks = fes.Size();
rblocks = 0;
tr_fes.SetSize(nblocks);
tr_fec.SetSize(nblocks);
mesh = fes[0]->GetMesh();
IsTraceSpace.SetSize(nblocks);
const FiniteElementCollection * fec;
for (int i = 0; i < nblocks; i++)
{
tr_fec[i] = nullptr;
fec = fes[i]->FEColl();
IsTraceSpace[i] =
(dynamic_cast<const H1_Trace_FECollection*>(fec) ||
@@ -88,21 +90,24 @@ void ComplexBlockStaticCondensation::SetSpaces(Array<FiniteElementSpace*> &
pmesh = dynamic_cast<ParMesh *>(mesh);
tr_fes[i] = (fec->GetContType() == FiniteElementCollection::DISCONTINUOUS) ?
nullptr : (IsTraceSpace[i]) ? fes[i] :
new ParFiniteElementSpace(pmesh, fec->GetTraceCollection(), fes[i]->GetVDim(),
new ParFiniteElementSpace(pmesh, tr_fec[i] = fec->GetTraceCollection(),
fes[i]->GetVDim(),
fes[i]->GetOrdering());
}
else
{
tr_fes[i] = (fec->GetContType() == FiniteElementCollection::DISCONTINUOUS) ?
nullptr : (IsTraceSpace[i]) ? fes[i] :
new FiniteElementSpace(mesh, fec->GetTraceCollection(), fes[i]->GetVDim(),
new FiniteElementSpace(mesh, tr_fec[i] = fec->GetTraceCollection(),
fes[i]->GetVDim(),
fes[i]->GetOrdering());
}
#else
// skip if it's an L2 space (no trace space to construct)
tr_fes[i] = (fec->GetContType() == FiniteElementCollection::DISCONTINUOUS) ?
nullptr : (IsTraceSpace[i]) ? fes[i] :
new FiniteElementSpace(mesh, fec->GetTraceCollection(), fes[i]->GetVDim(),
new FiniteElementSpace(mesh, tr_fec[i] = fec->GetTraceCollection(),
fes[i]->GetVDim(),
fes[i]->GetOrdering());
#endif
if (tr_fes[i]) { rblocks++; }
@@ -220,40 +225,43 @@ void ComplexBlockStaticCondensation::GetReduceElementIndicesAndOffsets(int el,
{
int td = 0;
int ndof;
int vdim = fes[i]->GetVDim();
// if it's an L2 space (bubbles)
if (!tr_fes[i])
{
ndof = fes[i]->GetVDim()*fes[i]->GetFE(el)->GetDof();
ndof = fes[i]->GetFE(el)->GetDof();
td = 0;
}
else if (IsTraceSpace[i])
{
for (int iface = 0; iface < numfaces; iface++)
{
td += fes[i]->GetVDim()*fes[i]->GetFaceElement(faces[iface])->GetDof();
td += fes[i]->GetFaceElement(faces[iface])->GetDof();
}
ndof = td;
}
else
{
Array<int> trace_dofs;
ndof = fes[i]->GetVDim()*fes[i]->GetFE(el)->GetDof();
tr_fes[i]->GetElementVDofs(el, trace_dofs);
ndof = fes[i]->GetFE(el)->GetDof();
tr_fes[i]->GetElementDofs(el, trace_dofs);
td = trace_dofs.Size(); // number of trace dofs
}
offsets[i+1] = td;
tr_dofs.SetSize(td);
int_dofs.SetSize(ndof - td);
for (int j = 0; j<td; j++)
offsets[i+1] = td*vdim;
tr_dofs.SetSize(td*vdim);
int_dofs.SetSize(vdim*(ndof - td));
for (int k=0; k<vdim; k++)
{
tr_dofs[j] = skip + j;
for (int j = 0; j<td; j++)
{
tr_dofs[j+k*td] = skip + j;
}
for (int j = 0; j<ndof-td; j++)
{
int_dofs[j+k*(ndof-td)] = skip + td + j;
}
skip+=ndof;
}
for (int j = 0; j<ndof-td; j++)
{
int_dofs[j] = skip + td + j;
}
skip+=ndof;
trace_ldofs.Append(tr_dofs);
interior_ldofs.Append(int_dofs);
}
@@ -1157,6 +1165,16 @@ ComplexBlockStaticCondensation::~ComplexBlockStaticCondensation()
delete lmat[i]; lmat[i] = nullptr;
delete lvec[i]; lvec[i] = nullptr;
}
for (int i = 0; i<tr_fes.Size(); i++)
{
if (tr_fec[i])
{
delete tr_fes[i];
delete tr_fec[i];
}
}
}
}
+6
View File
@@ -36,6 +36,7 @@ class ComplexBlockStaticCondensation
// New set of "reduced" Finite Element Spaces
// (after static condensation)
Array<FiniteElementSpace *> tr_fes;
Array<FiniteElementCollection *> tr_fec;
Array<int> dof_offsets;
Array<int> tdof_offsets;
@@ -214,6 +215,11 @@ public:
full linear system, compute the solution of the full system 'sol'. */
void ComputeSolution(const Vector &sc_sol, Vector &sol) const;
void GetTraceFESpaces(Array<FiniteElementSpace *> & trace_fes) const
{
trace_fes = tr_fes;
}
};
}
+45 -3
View File
@@ -314,9 +314,48 @@ void ComplexDPGWeakForm::ConformingAssemble()
width = 2*mat_r->Width();
}
void ComplexDPGWeakForm::SetIntegrationRules()
{
if (trial_ir)
{
for (int i = 0; i < trial_integs_r.NumRows(); i++)
{
for (int j = 0; j < trial_integs_r.NumCols(); j++)
{
for (int k = 0; k < trial_integs_r(i,j)->Size(); k++)
{
(*trial_integs_r(i,j))[k]->SetIntRule(trial_ir);
}
for (int k = 0; k < trial_integs_i(i,j)->Size(); k++)
{
(*trial_integs_i(i,j))[k]->SetIntRule(trial_ir);
}
}
}
}
if (test_ir)
{
for (int i = 0; i < test_integs_r.NumRows(); i++)
{
for (int j = 0; j < test_integs_r.NumCols(); j++)
{
for (int k = 0; k < test_integs_r(i,j)->Size(); k++)
{
(*test_integs_r(i,j))[k]->SetIntRule(test_ir);
}
for (int k = 0; k < test_integs_i(i,j)->Size(); k++)
{
(*test_integs_i(i,j))[k]->SetIntRule(test_ir);
}
}
}
}
}
/// Assembles the form i.e. sums over all domain integrators.
void ComplexDPGWeakForm::Assemble(int skip_zeros)
{
this->SetIntegrationRules();
ElementTransformation *eltrans;
Array<int> faces, ori;
@@ -505,8 +544,10 @@ void ComplexDPGWeakForm::Assemble(int skip_zeros)
}
ComplexCholeskyFactors chol(G_r.GetData(), G_i.GetData());
int h = G_r.Height();
chol.Factor(h);
bool info = chol.Factor(h);
MFEM_VERIFY(info, "Complex Cholesky factorization of G failed");
int w = B_r.Width();
chol.LSolve(h,w,B_r.GetData(), B_i.GetData());
@@ -919,7 +960,7 @@ void ComplexDPGWeakForm::EnableStaticCondensation()
Vector & ComplexDPGWeakForm::ComputeResidual(const Vector & x)
{
MFEM_VERIFY(store_matrices,
"Matrices needed for the residual are not store. Call ComplexDPGWeakForm::StoreMatrices()")
"Matrices needed for the residual are not stored. Call ComplexDPGWeakForm::StoreMatrices()")
// wrap vector in a blockvector
int n = x.Size()/2;
@@ -962,7 +1003,8 @@ Vector & ComplexDPGWeakForm::ComputeResidual(const Vector & x)
{
for (int ie = 0; ie < faces.Size(); ie++)
{
trial_offs[j+1] += trial_fes[j]->GetFaceElement(faces[ie])->GetDof();
trial_offs[j+1] += trial_fes[j]->GetVDim()*trial_fes[j]->GetFaceElement(
faces[ie])->GetDof();
}
}
else
+28 -1
View File
@@ -69,6 +69,10 @@ protected:
Array2D<Array<BilinearFormIntegrator * > * > test_integs_r;
Array2D<Array<BilinearFormIntegrator * > * > test_integs_i;
/// Integration rules for the test and trial integrators
const IntegrationRule *trial_ir = nullptr;
const IntegrationRule *test_ir = nullptr;
/// Set of LinearForm Integrators to be applied.
Array<Array<LinearFormIntegrator * > * > lfis_r;
Array<Array<LinearFormIntegrator * > * > lfis_i;
@@ -101,7 +105,8 @@ protected:
Vector residuals;
private:
/// Enforces a unique integration rule for all trial/test integrators
void SetIntegrationRules();
public:
ComplexDPGWeakForm()
@@ -198,6 +203,11 @@ public:
LinearFormIntegrator *lfi_i,
int n);
/// Sets the same integration rules for all trial integrators
void SetTrialIntegrationRule(const IntegrationRule &ir) { trial_ir = &ir; }
/// Sets the same integration rules for all test integrators
void SetTestIntegrationRule(const IntegrationRule &ir) { test_ir = &ir; }
/// Assembles the form i.e. sums over all integrators.
void Assemble(int skip_zeros = 1);
@@ -273,6 +283,23 @@ public:
Vector & ComputeResidual(const Vector & x);
void GetTraceFESpaces(Array<FiniteElementSpace *> & trace_fes) const
{
trace_fes.SetSize(0);
Array<FiniteElementSpace *> trace_fes_all;
if (static_cond)
{
static_cond->GetTraceFESpaces(trace_fes_all);
for (int i = 0; i < trace_fes_all.Size(); i++)
{
if (trace_fes_all[i])
{
trace_fes.Append(trace_fes_all[i]);
}
}
}
}
/// Destroys bilinear form.
virtual ~ComplexDPGWeakForm();
+562
View File
@@ -0,0 +1,562 @@
#include "maxwell_utils.hpp"
real_t AzimuthalECoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
Vector X, E;
vgf->GetVectorValue(T,ip,E);
T.Transform(ip, X);
real_t x = X(0);
real_t y = X(1);
real_t r = sqrt(x*x + y*y);
real_t val = -x*E[1] + y*E[0];
return val/r;
}
real_t ParallelECoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
Vector X, E;
vgf->GetVectorValue(T,ip,E);
T.Transform(ip, X);
Vector b;
ComputeB(X, b);
return E*b;
}
EpsilonMatrixCoefficient::EpsilonMatrixCoefficient(const char * filename,
Mesh * mesh_, ParMesh * pmesh_,
real_t scale)
: MatrixArrayCoefficient(mesh_->Dimension()), mesh(mesh_), pmesh(pmesh_),
dim(mesh->Dimension())
{
std::filebuf fb;
fb.open(filename,std::ios::in);
std::istream is(&fb);
vgf = new GridFunction(mesh,is);
fb.close();
FiniteElementSpace * vfes = vgf->FESpace();
int vdim = vfes->GetVDim();
const FiniteElementCollection * fec = vfes->FEColl();
FiniteElementSpace * fes = new FiniteElementSpace(mesh, fec);
int * partitioning = mesh->GeneratePartitioning(num_procs);
double *data = vgf->GetData();
GridFunction gf;
pgfs.SetSize(vdim);
gf_cfs.SetSize(vdim);
sdim = sqrt(vdim);
for (int i = 0; i<sdim; i++)
{
for (int j = 0; j<sdim; j++)
{
int k = i*sdim+j;
gf.MakeRef(fes,&data[k*fes->GetVSize()]);
pgfs[k] = new ParGridFunction(pmesh,&gf,partitioning);
(*pgfs[k])*=scale;
gf_cfs[k] = new GridFunctionCoefficient(pgfs[k]);
if (i<dim && j<dim)
{
Set(i,j,gf_cfs[k], true);
}
}
}
}
void EpsilonMatrixCoefficient::VisualizeMatrixCoefficient()
{
Array<socketstream *> sol_sock(pgfs.Size());
for (int k = 0; k<pgfs.Size(); k++)
{
if (Mpi::Root()) { mfem::out << "Visualizing component " << k << endl; }
char vishost[] = "localhost";
int visport = 19916;
sol_sock[k] = new socketstream(vishost, visport);
sol_sock[k]->precision(8);
*sol_sock[k] << "parallel " << num_procs << " " << myid << "\n";
int i = k/sdim;
int j = k%sdim;
*sol_sock[k] << "solution\n" << *pmesh << *pgfs[k]
<< "window_title 'Epsilon Matrix Coefficient Component (" << i << "," << j <<
")'" << flush;
}
}
void EpsilonMatrixCoefficient::Update()
{
pgfs[0]->ParFESpace()->Update();
for (int k = 0; k<pgfs.Size(); k++)
{
pgfs[k]->Update();
}
}
EpsilonMatrixCoefficient::~EpsilonMatrixCoefficient()
{
for (int i = 0; i<pgfs.Size(); i++)
{
delete pgfs[i];
}
pgfs.DeleteAll();
}
DielectricTensorComponentCoefficient::DielectricTensorComponentCoefficient(
real_t delta_, real_t a0_, real_t a1_,
int row_, int col_,
bool use_imag_)
: delta(delta_), a0(a0_), a1(a1_), row(row_), col(col_), use_imag(use_imag_) { }
real_t DielectricTensorComponentCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
Vector x;
T.Transform(ip, x);
return use_imag ? ComputeImagPart(x) : ComputeRealPart(x);
}
real_t DielectricTensorComponentCoefficient::ComputeRealPart(const Vector &x)
{
Vector b;
ComputeB(x, b);
real_t r = std::sqrt(x(0)*x(0) + x(1)*x(1));
real_t S = 1.0;
real_t P = a0 + a1 * (r - 0.9);
real_t bb_ij = b(row) * b(col);
return S * (row == col) + (P - S) * bb_ij;
}
real_t DielectricTensorComponentCoefficient::ComputeImagPart(const Vector &x)
{
return (row == col) ? delta : 0.0;
}
void VisualizeMatrixArrayCoefficient(MatrixArrayCoefficient &mc, ParMesh *pmesh,
int order, bool paraview, const char *name)
{
MFEM_VERIFY(pmesh != nullptr, "ParMesh pointer must not be null.");
int dim = mc.GetVDim();
if (Mpi::Root())
{
mfem::out << "Visualizing matrix coefficient with dimension: " << dim << endl;
mfem::out << "order = " << order << endl;
mfem::out << "pmesh dimension = " << pmesh->Dimension() << endl;
}
auto fec = new H1_FECollection(order, pmesh->Dimension());
auto pfes = new ParFiniteElementSpace(pmesh, fec);
Array<ParGridFunction *> pgfs(dim * dim);
Array<GridFunctionCoefficient *> gf_cfs(dim * dim);
ParaViewDataCollection * pvdc = nullptr;
std::ostringstream label;
if (name)
{
label << name;
}
else
{
label << "eps";
}
if (paraview)
{
pvdc = new ParaViewDataCollection(label.str(), pmesh);
pvdc->SetPrefixPath("ParaView");
pvdc->SetLevelsOfDetail(order);
pvdc->SetCycle(0);
pvdc->SetDataFormat(VTKFormat::BINARY);
}
for (int i = 0; i < dim; ++i)
{
for (int j = 0; j < dim; ++j)
{
Coefficient *c_ij = mc.GetCoeff(i, j);
if (!c_ij) { continue; }
pgfs[i*dim + j] = new ParGridFunction(pfes);
*pgfs[i*dim + j] = 0.0;
pgfs[i*dim + j]->ProjectCoefficient(*c_ij);
if (Mpi::Root())
{
mfem::out << "Projected component (" << i << "," << j << ")" << endl;
}
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << pmesh->GetNRanks() << " " << pmesh->GetMyRank() <<
"\n";
sol_sock << "solution\n" << * pmesh << *pgfs[i*dim + j];
sol_sock << "window_title '" << label.str() <<"_" << i << j << "'\n";
sol_sock << flush;
if (paraview)
{
pvdc->RegisterField(label.str() + std::to_string(i) + std::to_string(j),
pgfs[i*dim + j]);
}
}
}
if (paraview)
{
pvdc->Save();
delete pvdc;
for (int i = 0; i < dim * dim; ++i)
{
delete pgfs[i];
}
}
delete pfes;
delete fec;
}
void ComputeB(const Vector &x, Vector &b)
{
real_t x0 = x(0), x1 = x(1);
real_t r = std::sqrt(x0 * x0 + x1 * x1);
int dim = x.Size();
b.SetSize(dim); b = 0.0;
b(0) = -x1 / r;
b(1) = x0 / r;
if (dim == 3) { b(2) = 0.0; }
}
void DirectionalVectorDiffusionIntegrator::AssembleElementMatrix(
const FiniteElement &el,
ElementTransformation &Trans,
DenseMatrix &elmat)
{
int dof = el.GetDof();
int dim = el.GetDim();
int vdim = Trans.GetSpaceDim();
elmat.SetSize(dof * vdim, dof * vdim);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order = 2 * el.GetOrder(); // Integration order
ir = &IntRules.Get(el.GetGeomType(), order);
}
// Get shape functions and their derivatives
DenseMatrix dshape(dof, dim);
Vector vec(dim);
for (int k = 0; k < ir->GetNPoints(); k++)
{
const IntegrationPoint &ip = ir->IntPoint(k);
Trans.SetIntPoint(&ip);
double w = ip.weight * Trans.Weight();
VQ->Eval(vec, Trans, ip);
el.CalcPhysDShape(Trans, dshape);
// Compute (vq·∇)φ for each basis function
Vector vq_grad_phi(dof); vq_grad_phi = 0.0;
for (int j = 0; j < dof; j++)
{
for (int d = 0; d < dim; d++)
{
vq_grad_phi(j) += vec(d) * dshape(j, d);
}
}
for (int comp = 0; comp < vdim; comp++)
{
int offset = comp * dof;
for (int j = 0; j < dof; j++)
{
int jj = j + offset;
for (int i = 0; i < dof; i++)
{
int ii = i + offset;
elmat(jj, ii) += w * vq_grad_phi(j)
* vq_grad_phi(i);
}
}
}
}
}
void DirectionalVectorDiffusionIntegrator::AssembleElementMatrix2(
const FiniteElement &trial_fe, const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat)
{
int trial_dof = trial_fe.GetDof();
int test_dof = test_fe.GetDof();
int dim = trial_fe.GetDim();
int vdim = Trans.GetSpaceDim();
elmat.SetSize(test_dof * vdim, trial_dof * vdim);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order = trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderW() - 1;
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
}
Vector vec(dim);
DenseMatrix trial_dshape(trial_dof, dim);
DenseMatrix test_dshape(test_dof, dim);
for (int k = 0; k < ir->GetNPoints(); k++)
{
const IntegrationPoint &ip = ir->IntPoint(k);
Trans.SetIntPoint(&ip);
double w = ip.weight * Trans.Weight();
VQ->Eval(vec, Trans, ip);
trial_fe.CalcPhysDShape(Trans, trial_dshape);
test_fe.CalcPhysDShape(Trans, test_dshape);
// Compute (vq·∇)φ for each basis function
Vector vq_grad_phi_trial(trial_dof); vq_grad_phi_trial = 0.0;
for (int j = 0; j < trial_dof; j++)
{
for (int d = 0; d < dim; d++)
{
vq_grad_phi_trial(j) += vec(d) * trial_dshape(j, d);
}
}
Vector vq_grad_phi_test(test_dof); vq_grad_phi_test = 0.0;
for (int j = 0; j < test_dof; j++)
{
for (int d = 0; d < dim; d++)
{
vq_grad_phi_test(j) += vec(d) * test_dshape(j, d);
}
}
for (int trial_comp = 0; trial_comp < vdim; trial_comp++)
{
int offset_trial = trial_comp * trial_dof;
int offset_test = trial_comp * test_dof;
for (int j = 0; j < test_dof; j++)
{
int jj = j + offset_test;
for (int i = 0; i < trial_dof; i++)
{
int ii = i + offset_trial;
elmat(jj, ii) += w * vq_grad_phi_test(j) * vq_grad_phi_trial(i);
}
}
}
}
}
void DirectionalVectorGradientIntegrator::AssembleElementMatrix(
const FiniteElement &el,
ElementTransformation &Trans,
DenseMatrix &elmat)
{
int dof = el.GetDof();
int sdim = Trans.GetSpaceDim();
elmat.SetSize(dof * sdim, dof * sdim);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order = 2 * el.GetOrder(); // Integration order
ir = &IntRules.Get(el.GetGeomType(), order);
}
// Get shape functions and their derivatives
DenseMatrix dshape(dof, sdim);
Vector shape(dof);
Vector vec(sdim);
Vector vq_grad_phi(dof);
for (int k = 0; k < ir->GetNPoints(); k++)
{
const IntegrationPoint &ip = ir->IntPoint(k);
Trans.SetIntPoint(&ip);
double w = ip.weight * Trans.Weight();
VQ->Eval(vec, Trans, ip);
// gradient of trial (u) in physical space
el.CalcPhysDShape(Trans, dshape);
// value of test (v) in physical space
el.CalcPhysShape(Trans, shape);
// (vq·∇)φ_i on the trial side
vq_grad_phi = 0.0;
for (int j = 0; j < dof; j++)
{
for (int d = 0; d < sdim; d++)
{
vq_grad_phi(j) += vec(d) * dshape(j, d);
}
}
for (int comp = 0; comp < sdim; comp++)
{
int offset = comp * dof;
for (int j = 0; j < dof; j++)
{
int jj = j + offset;
for (int i = 0; i < dof; i++)
{
int ii = i + offset;
elmat(jj, ii) += w * shape(j) * vq_grad_phi(i);
}
}
}
}
}
void DirectionalVectorGradientIntegrator::AssembleElementMatrix2(
const FiniteElement &trial_fe, const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat)
{
int trial_dof = trial_fe.GetDof();
int test_dof = test_fe.GetDof();
int dim = trial_fe.GetDim();
int sdim = Trans.GetSpaceDim();
elmat.SetSize(test_dof * sdim, trial_dof * sdim);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order = trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderW();
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
}
Vector vec(sdim);
DenseMatrix trial_dshape(trial_dof, sdim);
Vector test_shape(test_dof);
Vector vq_grad_phi_trial(trial_dof);
for (int k = 0; k < ir->GetNPoints(); k++)
{
const IntegrationPoint &ip = ir->IntPoint(k);
Trans.SetIntPoint(&ip);
double w = ip.weight * Trans.Weight();
VQ->Eval(vec, Trans, ip);
// gradient of trial (u) in physical space
trial_fe.CalcPhysDShape(Trans, trial_dshape);
// value of test (v) in physical space
test_fe.CalcPhysShape(Trans, test_shape);
// (vq·∇)φ_i on the trial side
vq_grad_phi_trial = 0.0;
for (int i = 0; i < trial_dof; i++)
{
for (int d = 0; d < dim; d++)
{
vq_grad_phi_trial(i) += vec(d) * trial_dshape(i, d);
}
}
for (int comp = 0; comp < sdim; comp++)
{
int offset_trial = comp * trial_dof;
int offset_test = comp * test_dof;
for (int j = 0; j < test_dof; j++)
{
int jj = j + offset_test;
for (int i = 0; i < trial_dof; i++)
{
int ii = i + offset_trial;
elmat(jj, ii) += w * test_shape(j) * vq_grad_phi_trial(i);
}
}
}
}
}
void MixedDirectionalVectorGradientIntegrator::AssembleElementMatrix2(
const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat)
{
const int trial_dof = trial_fe.GetDof();
const int test_dof = test_fe.GetDof();
const int sdim = Trans.GetSpaceDim();
MFEM_VERIFY(test_fe.GetMapType() == mfem::FiniteElement::H_CURL ||
test_fe.GetMapType() == mfem::FiniteElement::H_DIV,
"MixedDirectionalVectorGradientIntegrator requires "
"H(curl) or H(div) test space.");
const int vdim = sdim;
// Test dofs already correspond to vector basis functions
elmat.SetSize(test_dof, trial_dof * vdim);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
const int order =
trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderW();
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
}
Vector q(vdim);
DenseMatrix trial_dshape(trial_dof, vdim);
DenseMatrix test_vshape(test_dof, vdim);
Vector q_dot_grad_phi(trial_dof);
for (int k = 0; k < ir->GetNPoints(); k++)
{
const IntegrationPoint &ip = ir->IntPoint(k);
Trans.SetIntPoint(&ip);
const double w = ip.weight * Trans.Weight();
// q in physical space
VQ->Eval(q, Trans, ip);
// ∇phi_i in physical space
trial_fe.CalcPhysDShape(Trans, trial_dshape);
// vector-valued test shape
test_fe.CalcVShape(Trans, test_vshape);
// (q · ∇) phi_i
q_dot_grad_phi = 0.0;
for (int i = 0; i < trial_dof; i++)
{
double val = 0.0;
for (int d = 0; d < vdim; d++)
{
val += q(d) * trial_dshape(i, d);
}
q_dot_grad_phi(i) = val;
}
// Assemble: v_j · (q·∇phi_i)
for (int j = 0; j < test_dof; j++)
{
for (int comp = 0; comp < vdim; comp++)
{
const double vj = test_vshape(j, comp);
const int offset_trial = comp * trial_dof;
for (int i = 0; i < trial_dof; i++)
{
elmat(j, offset_trial + i) +=
w * vj * q_dot_grad_phi(i);
}
}
}
}
}
+135
View File
@@ -0,0 +1,135 @@
#include "mfem.hpp"
using namespace mfem;
using namespace std;
class AzimuthalECoefficient : public Coefficient
{
private:
const GridFunction * vgf;
public:
AzimuthalECoefficient(const GridFunction * vgf_)
: Coefficient(), vgf(vgf_) {}
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
class ParallelECoefficient : public Coefficient
{
private:
const GridFunction * vgf;
public:
ParallelECoefficient(const GridFunction * vgf_)
: Coefficient(), vgf(vgf_) {}
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
class EpsilonMatrixCoefficient : public MatrixArrayCoefficient
{
private:
Mesh * mesh = nullptr;
ParMesh * pmesh = nullptr;
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Array<ParGridFunction * > pgfs;
Array<GridFunctionCoefficient * > gf_cfs;
GridFunction * vgf = nullptr;
int dim;
int sdim;
public:
EpsilonMatrixCoefficient(const char * filename, Mesh * mesh_, ParMesh * pmesh_,
real_t scale = 1.0);
// Visualize the components of the matrix coefficient
// in separate GLVis windows for each component
void VisualizeMatrixCoefficient();
// Update the Gridfunctions after mesh refinement
void Update();
~EpsilonMatrixCoefficient();
};
class DielectricTensorComponentCoefficient : public Coefficient
{
private:
real_t delta, a0, a1;
int row, col;
bool use_imag;
public:
/// Constructor
/// @param delta_ Imaginary scaling factor
/// @param a0_ Constant in P(r)
/// @param a1_ Linear term in P(r)
/// @param row_ row index of the dielectric tensor
/// @param col_ column index of the dielectric tensor
/// @param use_imag_ If true, returns imaginary part; otherwise real part
DielectricTensorComponentCoefficient(real_t delta_, real_t a0_, real_t a1_,
int row_, int col_, bool use_imag_ = false);
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip);
private:
real_t ComputeRealPart(const Vector &x);
real_t ComputeImagPart(const Vector &x);
};
void VisualizeMatrixArrayCoefficient(MatrixArrayCoefficient &mc, ParMesh *pmesh,
int order, bool paraview = false, const char *name = nullptr);
void ComputeB(const Vector &x, Vector &b);
// Integrator for (vq·∇) u · (vq·∇) v where u,v ∈ (H¹(Ω))ᵈ
class DirectionalVectorDiffusionIntegrator : public BilinearFormIntegrator
{
private:
VectorCoefficient *VQ;
public:
DirectionalVectorDiffusionIntegrator(VectorCoefficient &q) : VQ(&q) { }
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
DenseMatrix &elmat);
virtual void AssembleElementMatrix2(const FiniteElement &tr_el,
const FiniteElement &te_el,
ElementTransformation &Trans,
DenseMatrix &elmat);
};
// Integrator for (vq·∇) u · v where u ∈ (H¹(Ω))ᵈ, v ∈ (H¹(Ω))ᵈ or (L²(Ω))ᵈ
class DirectionalVectorGradientIntegrator : public BilinearFormIntegrator
{
private:
VectorCoefficient *VQ;
public:
DirectionalVectorGradientIntegrator(VectorCoefficient &q) : VQ(&q) { }
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
DenseMatrix &elmat);
virtual void AssembleElementMatrix2(const FiniteElement &tr_el,
const FiniteElement &te_el,
ElementTransformation &Trans,
DenseMatrix &elmat);
};
// Integrator for (vq·∇) u · v where u ∈ (H¹(Ω))ᵈ, v ∈ H(curl) or H(div)
class MixedDirectionalVectorGradientIntegrator : public BilinearFormIntegrator
{
private:
VectorCoefficient *VQ;
public:
MixedDirectionalVectorGradientIntegrator(VectorCoefficient &q) : VQ(&q) { }
virtual void AssembleElementMatrix2(const FiniteElement &tr_el,
const FiniteElement &te_el,
ElementTransformation &Trans,
DenseMatrix &elmat);
};
+265
View File
@@ -0,0 +1,265 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "pcomplexblockform.hpp"
#ifdef MFEM_USE_MPI
namespace mfem
{
void ParComplexBlockForm::FillEssTdofLists(const Array<int> &
ess_tdof_list)
{
int j;
for (int i = 0; i < ess_tdof_list.Size(); i++)
{
int tdof = ess_tdof_list[i];
for (j = 0; j < nblocks; j++)
{
if (tdof_offsets[j+1] > tdof) { break; }
}
ess_tdofs[j]->Append(tdof-tdof_offsets[j]);
}
}
void ParComplexBlockForm::Assemble(int skip_zeros)
{
ComplexBlockForm::Assemble(skip_zeros);
}
void ParComplexBlockForm::ParallelAssemble(BlockMatrix *m_r,
BlockMatrix *m_i)
{
if (!P) { BuildProlongation(); }
p_mat_r = new BlockOperator(tdof_offsets);
p_mat_i = new BlockOperator(tdof_offsets);
p_mat_e_r = new BlockOperator(tdof_offsets);
p_mat_e_i = new BlockOperator(tdof_offsets);
p_mat_r->owns_blocks = 1;
p_mat_i->owns_blocks = 1;
p_mat_e_r->owns_blocks = 1;
p_mat_e_i->owns_blocks = 1;
HypreParMatrix * A_r = nullptr;
HypreParMatrix * A_i = nullptr;
HypreParMatrix * PtAP_r = nullptr;
HypreParMatrix * PtAP_i = nullptr;
for (int i = 0; i < nblocks; i++)
{
HypreParMatrix * Pi = (HypreParMatrix*)(&P->GetBlock(i,i));
for (int j = 0; j<nblocks; j++)
{
if (m_r->IsZeroBlock(i,j)) { continue; }
if (i == j)
{
// Make block diagonal square hypre matrix
A_r = new HypreParMatrix(pfes[i]->GetComm(), pfes[i]->GlobalVSize(),
pfes[i]->GetDofOffsets(), &m_r->GetBlock(i,i));
PtAP_r = RAP(A_r,Pi);
delete A_r;
p_mat_e_r->SetBlock(i, i, PtAP_r->EliminateRowsCols(*ess_tdofs[i]));
A_i = new HypreParMatrix(pfes[i]->GetComm(), pfes[i]->GlobalVSize(),
pfes[i]->GetDofOffsets(), &m_i->GetBlock(i,i));
PtAP_i = RAP(A_i,Pi);
delete A_i;
p_mat_e_i->SetBlock(i, i, PtAP_i->EliminateCols(*ess_tdofs[i]));
PtAP_i->EliminateRows(*ess_tdofs[i]);
}
else
{
HypreParMatrix * Pj = (HypreParMatrix*)(&P->GetBlock(j,j));
A_r = new HypreParMatrix(pfes[i]->GetComm(), pfes[i]->GlobalVSize(),
pfes[j]->GlobalVSize(), pfes[i]->GetDofOffsets(),
pfes[j]->GetDofOffsets(), &m_r->GetBlock(i,j));
PtAP_r = RAP(Pi,A_r,Pj);
delete A_r;
p_mat_e_r->SetBlock(i, j, PtAP_r->EliminateCols(*ess_tdofs[j]));
PtAP_r->EliminateRows(*ess_tdofs[i]);
A_i = new HypreParMatrix(pfes[i]->GetComm(), pfes[i]->GlobalVSize(),
pfes[j]->GlobalVSize(), pfes[i]->GetDofOffsets(),
pfes[j]->GetDofOffsets(), &m_i->GetBlock(i,j));
PtAP_i = RAP(Pi,A_i,Pj);
delete A_i;
p_mat_e_i->SetBlock(i, j, PtAP_i->EliminateCols(*ess_tdofs[j]));
PtAP_i->EliminateRows(*ess_tdofs[i]);
}
p_mat_r->SetBlock(i, j, PtAP_r);
p_mat_i->SetBlock(i, j, PtAP_i);
}
}
}
void ParComplexBlockForm::BuildProlongation()
{
P = new BlockOperator(dof_offsets, tdof_offsets);
R = new BlockMatrix(tdof_offsets, dof_offsets);
P->owns_blocks = 0;
R->owns_blocks = 0;
for (int i = 0; i < nblocks; i++)
{
HypreParMatrix * P_ = pfes[i]->Dof_TrueDof_Matrix();
P->SetBlock(i,i,P_);
const SparseMatrix * R_ = pfes[i]->GetRestrictionMatrix();
R->SetBlock(i, i, const_cast<SparseMatrix*>(R_));
}
}
void ParComplexBlockForm::FormLinearSystem(const Array<int>
&ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A,
Vector &X, Vector &B,
int copy_interior)
{
FormSystemMatrix(ess_tdof_list, A);
int n = P->Width();
B.SetSize(2*n);
Vector B_r(B, 0, n);
Vector B_i(B, n, n);
Vector b_r(b, 0, b.Size()/2);
Vector b_i(b, b.Size()/2, b.Size()/2);
P->MultTranspose(b_r, B_r);
P->MultTranspose(b_i, B_i);
int m = R->Height();
X.SetSize(2*m);
Vector X_r(X, 0, m);
Vector X_i(X, m, m);
Vector x_r(x, 0, x.Size()/2);
Vector x_i(x, x.Size()/2, x.Size()/2);
R->Mult(x_r, X_r);
R->Mult(x_i, X_i);
// eliminate tdof is RHS
// B_r -= Ae_r*X_r + Ae_i X_i
// B_i -= Ae_i*X_r + Ae_r X_i
Vector tmp(B_r.Size());
p_mat_e_r->Mult(X_r, tmp); B_r-=tmp;
p_mat_e_i->Mult(X_i, tmp); B_r+=tmp;
p_mat_e_i->Mult(X_r, tmp); B_i-=tmp;
p_mat_e_r->Mult(X_i, tmp); B_i-=tmp;
for (int j = 0; j < nblocks; j++)
{
if (!ess_tdofs[j]->Size()) { continue; }
for (int i = 0; i < ess_tdofs[j]->Size(); i++)
{
int tdof = (*ess_tdofs[j])[i];
int gdof = tdof + tdof_offsets[j];
B_r(gdof) = X_r(gdof); // diagonal policy is always one in parallel
B_i(gdof) = X_i(gdof); // diagonal policy is always one in parallel
}
}
if (!copy_interior)
{
X_r.SetSubVectorComplement(ess_tdof_list, 0.0);
X_i.SetSubVectorComplement(ess_tdof_list, 0.0);
}
}
void ParComplexBlockForm::FormSystemMatrix(const Array<int>
&ess_tdof_list,
OperatorHandle &A)
{
FillEssTdofLists(ess_tdof_list);
if (mat_r)
{
const int remove_zeros = 0;
Finalize(remove_zeros);
ParallelAssemble(mat_r, mat_i);
delete mat_r;
delete mat_i;
mat_r = nullptr;
mat_i = nullptr;
delete mat_e_r;
delete mat_e_i;
mat_e_r = nullptr;
mat_e_i = nullptr;
}
p_mat = new ComplexOperator(p_mat_r, p_mat_i, false, false);
A.Reset(p_mat, false);
}
void ParComplexBlockForm::RecoverFEMSolution(const Vector &X,
Vector &x)
{
int n = P->Height();
int m = P->Width();
x.SetSize(2*n);
Vector x_r(x,0,n);
Vector x_i(x,n,n);
Vector X_r(const_cast<Vector&>(X), 0, m);
Vector X_i(const_cast<Vector&>(X), m, m);
P->Mult(X_r, x_r);
P->Mult(X_i, x_i);
}
void ParComplexBlockForm::Update()
{
ComplexBlockForm::Update();
delete p_mat_e_r;
delete p_mat_e_i;
p_mat_e_r = nullptr;
p_mat_e_i = nullptr;
delete p_mat_r;
delete p_mat_i;
p_mat_r = nullptr;
p_mat_i = nullptr;
delete p_mat;
p_mat = nullptr;
for (int i = 0; i < nblocks; i++)
{
delete ess_tdofs[i];
ess_tdofs[i] = new Array<int>();
}
delete P;
P = nullptr;
delete R;
R = nullptr;
}
ParComplexBlockForm::~ParComplexBlockForm()
{
delete p_mat_e_r;
delete p_mat_e_i;
p_mat_e_r = nullptr;
p_mat_e_i = nullptr;
delete p_mat_r;
delete p_mat_i;
p_mat_r = nullptr;
p_mat_i = nullptr;
delete p_mat;
p_mat = nullptr;
for (int i = 0; i < nblocks; i++)
{
delete ess_tdofs[i];
}
delete P;
delete R;
}
} // namespace mfem
#endif
+108
View File
@@ -0,0 +1,108 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_PCOMPLEX_BLOCKFORM
#define MFEM_PCOMPLEX_BLOCKFORM
#include "mfem.hpp"
#include "complexblockform.hpp"
#ifdef MFEM_USE_MPI
namespace mfem
{
/** @brief Class representing the parallel weak formulation.
(Convenient for DPG Equations) */
class ParComplexBlockForm : public ComplexBlockForm
{
protected:
/// Trial FE spaces
Array<ParFiniteElementSpace * > pfes;
/// ess_tdof list for each space
Array<Array<int> *> ess_tdofs;
/** split ess_tdof_list given in global tdof (for all spaces)
to individual lists for each space */
void FillEssTdofLists(const Array<int> & ess_tdof_list);
// Block Prolongation
BlockOperator * P = nullptr;
// Block Restriction
BlockMatrix * R = nullptr;
ComplexOperator * p_mat = nullptr;
BlockOperator * p_mat_r = nullptr;
BlockOperator * p_mat_i = nullptr;
BlockOperator * p_mat_e_r = nullptr;
BlockOperator * p_mat_e_i = nullptr;
void BuildProlongation();
public:
ParComplexBlockForm() {}
/// Creates bilinear form associated with FE spaces @a trial_pfes_.
ParComplexBlockForm(Array<ParFiniteElementSpace* > & pfes_)
: ComplexBlockForm() { SetParSpaces(pfes_); }
void SetParSpaces(Array<ParFiniteElementSpace* > & pfes_)
{
pfes = pfes_;
ess_tdofs.SetSize(pfes.Size());
Array<FiniteElementSpace * > sfes(pfes.Size());
for (int i = 0; i<sfes.Size(); i++)
{
sfes[i] = (FiniteElementSpace *)pfes[i];
ess_tdofs[i] = new Array<int>();
}
SetSpaces(sfes);
}
/// Assembles the form i.e. sums over all domain integrators.
void Assemble(int skip_zeros = 1);
/// Returns the matrix assembled on the true dofs, i.e. P^t A P.
/** The returned matrix has to be deleted by the caller. */
void ParallelAssemble(BlockMatrix *mat_r, BlockMatrix *mat_i);
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A,
Vector &X, Vector &B,
int copy_interior = 0);
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A);
/** Call this method after solving a linear system constructed using the
FormLinearSystem method to recover the solution as a ParGridFunction-size
vector in x. Use the same arguments as in the FormLinearSystem call. */
virtual void RecoverFEMSolution(const Vector &X, Vector &x);
virtual void Update();
/// Destroys bilinear form.
virtual ~ParComplexBlockForm();
};
} // namespace mfem
#endif // MFEM_USE_MPI
#endif

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