Compare commits

...
Author SHA1 Message Date
Socratis Petrides 6744b7f895 fix path issue 2022-08-16 14:30:09 -07:00
psocratis 51d1876427 Merge branch 'dpg-complex-dev' of github.com:mfem/mfem into dpg-complex-dev 2022-08-15 17:12:29 -07:00
psocratis cf18624920 small print edits 2022-08-15 17:12:11 -07:00
Socratis Petrides ac5d8d5993 merge master 2022-08-15 16:27:04 -07:00
psocratis 5f38694913 several edits for comparison with DST runs 2022-07-27 06:51:45 -07:00
psocratis 967a024dbe setting up the dpg pml proble for comparison with DST 2022-07-27 01:45:43 -07:00
psocratis c8242b8e98 added pml terms to adjoint graph norm 2022-07-26 17:49:23 +03:00
psocratis c91875c5a9 fix pml coeff bug in uu_dpg maxwell 2022-07-25 17:08:10 +03:00
psocratis 94112d7a7a debugging uw pml maxwell 2022-07-25 15:33:57 +03:00
psocratis c275856ced dpg pml maxwell formulation 2022-07-25 13:14:51 +03:00
psocratis ad58472eb5 acoustics preconditioner edits 2022-07-22 13:00:46 +03:00
psocratis 041ffbd32f format print 2022-07-21 06:39:30 -07:00
psocratis 72683f55ca minor-sample run edit 2022-07-21 02:04:38 -07:00
Socratis Petrides 0de5f1f70b fichera 'oven' problem for maxwell 2022-05-20 18:40:09 -07:00
Socratis Petrides 18e7b9552c fix preconditioner 2022-05-19 14:12:18 -07:00
Socratis Petrides ee5cc572ce adding parallel maxwell example and pcg solver 2022-05-18 18:07:36 -07:00
Socratis Petrides 1314d0b7d3 use ComplexCholeskyFactors for G 2022-05-18 15:34:11 -07:00
Socratis Petrides 95be3d9c12 resolve conflict with master 2022-05-18 15:33:24 -07:00
Socratis Petrides 3d6f1694b3 fix integration order in CurlIntegrator 2022-05-18 15:33:05 -07:00
Socratis Petrides c454ddeb1c merge master 2022-05-18 12:23:35 -07:00
Socratis Petrides 878bfef86b minor fix in error calculation 2022-05-12 14:31:10 -07:00
Socratis Petrides bb0b0132b1 fixing graph norm in 2D 2022-05-12 12:28:50 -07:00
Socratis Petrides 623fc516f0 merging 2D and 3D uw maxwell 2022-05-06 19:57:59 -07:00
Socratis Petrides 24e142b6b4 fixing BC for E in 2D maxwell 2022-05-06 19:57:32 -07:00
Socratis Petrides 4e75435e20 merging ScalarCurlintegrator to CurlIntegrator 2022-05-06 19:57:11 -07:00
Socratis Petrides 364bc0faa4 2D maxwell uw_dpg with essential BC on H 2022-05-05 20:10:27 -07:00
Socratis Petrides 03f1f5e252 fix sign bug in densmat::GradToCurl 2022-05-05 20:08:28 -07:00
Socratis Petrides 6b69120856 adding ScalarCurlIntegrator (rotated gradient) for scalar H1 2022-05-05 20:07:48 -07:00
Socratis Petrides e591dbe8b0 fix sign bug 2022-05-04 20:35:58 -07:00
Socratis Petrides edd4bae030 uw_maxwell for linear solution 2022-05-04 19:15:34 -07:00
Socratis Petrides 6ea11d6e65 testing new CurlIntegrator, starting UW_maxwell 2022-05-03 19:37:34 -07:00
Socratis Petrides 6e2d4bd6b8 varying options for acoustics 2022-05-03 19:36:28 -07:00
Socratis Petrides 6b40a55988 adding CurlIntegrator for H(curl) vs vector L2 2022-05-03 19:35:54 -07:00
Socratis Petrides 7931e85395 EM-diffusion in 2D with primal DPG merging 2D and 3D integrators to TangentTraceIntegrator 2022-04-22 17:45:43 -07:00
Socratis Petrides 1190685292 adding LS maxwell in 2D 2022-04-21 17:42:23 -07:00
Socratis Petrides b0828aa597 fixing VectorFETraceTangentIntegrator, Adding AssempleMatrix2 to CurlCurlIntegrator 2022-04-21 17:42:00 -07:00
Socratis Petrides caa6085362 primal dpg EM diffusion 2022-04-20 17:33:29 -07:00
Socratis Petrides cef5e71448 starting primal dpg EM-diffusion example 2022-04-20 16:05:38 -07:00
Socratis Petrides 078603885e adding tangential trace integrator 2022-04-20 16:03:05 -07:00
Socratis Petrides 8123a2e211 solving complex-acoustics with CG 2022-04-20 16:02:36 -07:00
Socratis Petrides 63e3496131 block preconditioner for indefinite helmholtz (complex case) 2022-04-15 19:23:13 -07:00
Socratis Petrides dddef6be52 testing with block diagonal preconditioner, real indefinite 2022-04-15 17:59:00 -07:00
Socratis Petrides 8bdfcd231e finalizing unit tests for ComplexFactors 2022-03-25 17:54:58 -07:00
Socratis Petrides 5af0b1cf8a fixing some bugs in ComplexFactors 2022-03-25 17:54:34 -07:00
Socratis Petrides 4564b6dd5d unit tests ref matrices 2022-03-24 15:37:45 -07:00
Socratis Petrides 7f7394c991 adding cholesky and lu factors for complex systems. Started adding unit tests 2022-03-24 15:22:34 -07:00
psocratis e4bd8dd615 fix valgrind complaints for the parallel complex case with sc 2022-03-23 20:36:32 -07:00
psocratis 00f23ff6c7 minor valgrind issue on real example 2022-03-23 18:48:05 -07:00
psocratis 9c6553ce2e fixing valgrind issues for the serial case 2022-03-23 13:09:24 -07:00
Socratis Petrides a008739b3d minor 2022-03-22 16:01:13 -07:00
Socratis Petrides 673bb91447 merge with dpg-dev 2022-03-22 14:38:39 -07:00
Socratis Petrides d322228504 testing dpg residual with cholesky factors 2022-03-22 14:13:56 -07:00
Socratis Petrides 77ef3a2e92 Merge branch 'dpg-dev' into dpg-complex-dev 2022-03-22 12:09:23 -07:00
Socratis Petrides 2cc8c18250 fix repo check 2022-03-22 12:08:56 -07:00
Socratis Petrides 48c54c0bbc fix repo-check 2022-03-22 12:05:22 -07:00
Socratis Petrides 57c99323e7 fix repo check 2022-03-22 12:04:26 -07:00
Socratis Petrides 1b69590800 2022 2022-03-22 10:29:58 -07:00
Socratis Petrides fec64571ac Merge branch 'dpg-dev' into dpg-complex-dev 2022-03-22 10:27:47 -07:00
Socratis Petrides aa9a4887f7 change to 2022 2022-03-22 10:24:38 -07:00
Socratis Petrides 757369beb3 Merge branch 'master' into dpg-dev 2022-03-22 10:19:35 -07:00
Socratis Petrides 463b57205d Gaussian beam - Acoustics AMR parallel example 2022-03-07 20:35:41 -08:00
Socratis Petrides 0e9e25f893 adaptive complex example with sc, and parallel sc 2022-03-07 14:50:45 -08:00
Socratis Petrides 6598a8d90b adding parallel sc in the complex case 2022-03-07 12:34:02 -08:00
Socratis Petrides 91fabccd8b static cond for the complex case 2022-03-05 14:38:08 -08:00
Socratis Petrides 950fb937c7 Simplifying ComplexNormalEquations interface 2022-03-04 17:56:47 -08:00
Socratis Petrides ac9b14e8d7 Using OperatorHandle (for ComplexOperator) in FormSystemMatrix and FormLinearSystem 2022-03-04 15:53:23 -08:00
Socratis Petrides b58ac482d3 complex valued acoustics works 2022-03-03 19:58:36 -08:00
Socratis Petrides 1ebd603e62 fix signature in ComplexNormalEquations::EliminateVDofsInRHS 2022-03-03 19:57:52 -08:00
Socratis Petrides 6967342813 minor 2022-03-03 13:06:29 -08:00
Socratis Petrides fe16207d6a Merge branch 'dpg-dev' into dpg-complex-dev 2022-03-01 19:24:18 -08:00
Socratis Petrides ba94547c4d fixing residual computation 2022-03-01 19:20:44 -08:00
Socratis Petrides 02ef0f7f04 debugging complex-dpg 2022-02-28 11:01:11 -08:00
Socratis Petrides fa3a262608 starting complex acoustics/helmholtz examples for uw_dpg 2022-02-25 20:19:00 -08:00
Socratis Petrides 0f5b0eb57e place holder for par complex normal equations and sc 2022-02-25 20:15:53 -08:00
Socratis Petrides 8abd6992d8 DPG normal equations for complex case 2022-02-25 20:14:39 -08:00
Socratis Petrides 7349ece185 Merge branch 'dpg-dev' into dpg-complex-dev 2022-02-25 17:47:42 -08:00
Socratis Petrides 58f7aacce9 fix acoustics graph norm 2022-02-25 17:45:49 -08:00
Socratis Petrides af871ffa7b merge complex-linalg 2022-02-25 13:57:50 -08:00
Socratis Petrides 369d55c7b2 Merge branch 'dpg-dev' into dpg-complex-dev 2022-02-25 13:56:25 -08:00
Socratis Petrides 3876d111cf unit tests from complex dense matrix: Mult, MultAtB, Inverse, SystemMatrix 2022-02-23 18:17:38 -08:00
Socratis Petrides e3085a1182 adding ComplexDenseMatrix class 2022-02-23 18:16:44 -08:00
psocratis 66e0c79b3f small valgrind fix in the parallel case 2022-02-11 09:50:52 -08:00
psocratis 8b5f9f7720 resolving conflicts 2022-02-09 19:50:00 -08:00
psocratis 98739f8c09 fix valgrind complaints in the serial case 2022-02-09 19:41:48 -08:00
Socratis Petrides 84129a1f83 finished Block-sc in the parallel case, uw_dpgp passes initial tests 2022-02-09 17:04:01 -08:00
Socratis Petrides b6ea025795 cleanup, started on par sc case 2022-02-02 20:18:19 -08:00
Socratis Petrides d528e9d63e filling blockstaticcond destructor 2022-02-01 10:19:19 -08:00
Socratis Petrides d18f11e6c7 blockstatic-cond passes tests for primal-dpg 2022-01-31 17:56:37 -08:00
Socratis Petrides efeb400b27 Block sc FormSystemMatrix. Started on ReduceSystem 2022-01-28 20:01:07 -08:00
Socratis Petrides 1451c3c483 small bug-fix in assembly if doftrans is not null 2022-01-27 17:33:04 -08:00
Socratis Petrides f12d46bdf9 Shur complement assembly and conforming assembly 2022-01-27 17:31:55 -08:00
Socratis Petrides 6dfb809f97 changing GetSubMatrix to const 2022-01-27 17:30:38 -08:00
Socratis Petrides ef8785b852 computing indices for interior/interface local dofs for block-static-cond 2022-01-26 18:24:56 -08:00
Socratis Petrides efd182ec4c visualize mesh in amr for the l-shape problem 2022-01-26 18:23:12 -08:00
Socratis Petrides e7856b982a style 2022-01-26 10:59:38 -08:00
Socratis Petrides 4f4d1f9d2d adding function signatures in blockstaticcond 2022-01-26 10:59:19 -08:00
Socratis Petrides 1a89d5af02 started on static condensation for DPG (block) systems 2022-01-25 19:45:40 -08:00
Socratis Petrides c71b02a915 fix print format 2022-01-21 14:37:21 -08:00
Socratis Petrides 5576f3653e style 2022-01-21 13:45:29 -08:00
Socratis Petrides 7f316dd07b changing strong_dpg to use vdim>1 test space 2022-01-21 13:45:00 -08:00
Socratis Petrides 19b5ae6b9a parallel examples acoustics uw_dpg 2022-01-21 13:44:17 -08:00
Socratis Petrides 94cd8859b2 adding adjoint graphnorm in uw_dpg for acoustics 2022-01-21 13:43:56 -08:00
Socratis Petrides 8ea8b97fc8 adding vdim>1 for test spaces in normalequations 2022-01-21 13:43:00 -08:00
Socratis Petrides f9abc77026 clean up 2022-01-20 16:45:43 -08:00
Socratis Petrides 68e3f9aeb9 adding traditional fosls formulation for acoustics (real) 2022-01-20 16:34:51 -08:00
Socratis Petrides 08f018c299 adding uw dpg formulation for acoustics (real) 2022-01-20 16:33:58 -08:00
Socratis Petrides 69e57546d4 adding 'strong' dpg formulation for acoustics 2022-01-20 16:33:35 -08:00
Socratis Petrides 433a29c9e3 fixing comment in diffusion fosls 2022-01-20 16:32:47 -08:00
Socratis Petrides 92ee7099a4 reorganizing examples 2022-01-19 11:05:16 -08:00
Socratis Petrides 808c9cd2c4 Merge branch 'master' into dpg-dev 2022-01-19 10:10:05 -08:00
Socratis Petrides f3cd0a10e7 minor 2022-01-17 11:30:23 -08:00
Socratis Petrides e6bb8c8976 small bugfix in updating mesh dependent coefficient 2022-01-05 18:09:49 -08:00
Socratis Petrides 4718577810 bug-fix serial AMR in normalequations P/R Mult in RHS and Sol vector 2021-12-30 19:59:14 -08:00
Socratis Petrides 9e520183c8 Merge branch 'master' into dpg-dev 2021-12-30 09:26:52 -08:00
Socratis Petrides 95a40d7377 clean up convection-diffusion 2021-12-22 14:40:11 -08:00
Socratis Petrides 036846bcd6 adding parallel convection-diffusion with AMR example 2021-12-21 17:59:56 -08:00
Socratis Petrides 6c2968156f Merge branch 'master' into dpg-dev 2021-12-21 10:01:43 -08:00
Socratis Petrides 9ba650f2fd adding Erikson Johnson problem for convection-diffusion UW-DPG 2021-12-17 16:34:22 -08:00
Socratis Petrides ee6cb39ee0 Merge branch 'pncmesh-getessvdof-fix' into dpg-dev 2021-12-16 09:46:14 -08:00
Socratis Petrides 1701a6e84c Merge branch 'master' into dpg-dev 2021-12-16 09:45:02 -08:00
Socratis Petrides f500e5220a Parallel AMR for lshape 2021-12-15 18:28:31 -08:00
Socratis Petrides e078288ecb title fix 2021-12-15 12:40:42 -08:00
Socratis Petrides e46c7ac0e7 fix sample run and title in the example 2021-12-15 12:39:33 -08:00
Socratis Petrides c2b133f3c8 minor visualization edits 2021-12-15 12:37:46 -08:00
Socratis Petrides 373379fbdd lshape mesh 2021-12-15 12:20:41 -08:00
Socratis Petrides bfc3835a1d adding AMR l-shape example in diffusion_uwdpg 2021-12-15 12:20:08 -08:00
Socratis Petrides ab8578a728 add element residual computation to NormalEquations 2021-12-15 12:19:30 -08:00
Socratis Petrides fdaaacfdf3 Started residual based error estimator. Done some refactoring 2021-12-13 17:36:52 -08:00
Socratis Petrides 5d7ebbb51a fixed small size bug in DenseMatrix::GetSubMatrix 2021-12-13 17:35:26 -08:00
Socratis Petrides fe0821413a adding parallel test for uw diffusion 2021-12-09 15:16:49 -08:00
Socratis Petrides 695a5adcb2 ParNormalEquations cleanup 2021-12-09 15:16:15 -08:00
Socratis Petrides b2dfc76f1a minor changes in serial example 2021-12-09 15:13:54 -08:00
Socratis Petrides 76c700b41e adding BlockOperator and BlockMatrix in OpType 2021-12-09 15:13:05 -08:00
Socratis Petrides dc3f10fa63 Passing first tests in parallel for ParNormalEquations 2021-12-08 17:36:34 -08:00
Socratis Petrides 420bb73d6c minor change in normal equations constructor 2021-12-08 17:36:11 -08:00
Socratis Petrides 13648768ae removing unused code 2021-12-08 17:34:00 -08:00
Socratis Petrides 7bfda36bca Starting ParNormalEquations -> adding class methods signatures 2021-12-07 18:48:24 -08:00
Socratis Petrides 46fc3ce9c2 remove not used code 2021-12-07 18:47:20 -08:00
Socratis Petrides b3086ce754 Merge branch 'master' into dpg-dev 2021-12-07 13:05:01 -08:00
Socratis Petrides d1312d354a minor bug-fix in the adjoint graph norm 2021-12-03 20:04:49 -08:00
Socratis Petrides 16fcc3f970 Fixing P and R null diagonal blocks to act as Identity for L2 Space and AMR 2021-12-03 20:04:23 -08:00
Socratis Petrides a6caab3bed PartMult and AddPartMult for BlockMatrix 2021-12-03 19:53:27 -08:00
Socratis Petrides 172b5bdcc9 Modifying DPG examples to use block-diagonal preconditioners 2021-12-02 17:25:03 -08:00
Socratis Petrides 72f83c132d Changing normalequations to use only BlockMatrix 2021-12-02 17:24:25 -08:00
Socratis Petrides 911e394d80 adding EliminateRowCols with saving Ae to BlockMatrix class 2021-12-02 17:22:36 -08:00
Socratis Petrides 4ff29e40c9 Refactoring NormalEquations assemble to use BlockMatrix 2021-12-01 19:00:07 -08:00
Socratis Petrides dac7808875 style 2021-11-30 18:23:31 -08:00
Socratis Petrides 246fd8e6fe started element residual calculation for AMR 2021-11-30 18:23:09 -08:00
Socratis Petrides 5e243e01f8 clean up 2021-11-30 18:22:31 -08:00
Socratis Petrides 56b83ea538 UW-DPG with AMR: fixing Block Prolongation/Restriction Operators 2021-11-30 16:41:21 -08:00
Socratis Petrides cd7371896a removing no longer needed test 2021-11-29 17:30:01 -08:00
Socratis Petrides 3fdd3e2450 style 2021-11-29 17:28:42 -08:00
Socratis Petrides 303ccefd83 completing the descructor 2021-11-29 17:28:17 -08:00
Socratis Petrides 48bcc332f7 fix piola map bug in NormalTraceIntegrator 2021-11-29 17:19:05 -08:00
Socratis Petrides 0c23d23874 new diffusion uw-dpg with adjoint graph norm 2021-11-29 17:17:54 -08:00
Socratis Petrides e76cef225d UW-DPG for diffusion works on quad meshes 2021-11-28 15:03:34 -08:00
Socratis Petrides 6f49201e62 Ultraweak-DPG for poisson prototype 2021-11-27 14:23:24 -08:00
Socratis Petrides 1cb75f7730 adding diffusion UW-DPG 2021-11-24 16:56:15 -08:00
Socratis Petrides ef14d682c5 bug-fix ElementTrasformation 2021-11-24 16:55:45 -08:00
Socratis Petrides 54eb78e0df adding block primal test 2021-11-23 17:13:22 -08:00
Socratis Petrides cd1a3a4fc7 fixing minor bug in B^T G^-1 l 2021-11-23 17:12:02 -08:00
Socratis Petrides ffb50c2416 example tests cleanup 2021-11-23 10:05:44 -08:00
Socratis Petrides f568ca3c6e bug fix on accumulating domain integrators 2021-11-23 10:03:13 -08:00
Socratis Petrides c7fb058051 adding NormalTraceIntegrator and AssembleElementMatrix2 for DivDivIntegrator 2021-11-23 09:58:11 -08:00
Socratis Petrides a4d87d936b Adding NormalEquations Assembly for multiple spaces and integrators. Tested succesfully for primal DPG 2021-11-19 19:52:27 -08:00
Socratis Petrides f26bd4bea1 generalizing NormalEquations assembly for multiple fespaces and integrators 2021-11-18 17:20:30 -08:00
Socratis Petrides 6c001c3f9a adding example test for primal DPG 2021-11-15 17:57:09 -08:00
Socratis Petrides 82573af316 new Primal DPG reproduces ex8 2021-11-15 17:56:16 -08:00
Socratis Petrides 4bfb2d62d9 first primal dpg test runs 2021-11-15 13:41:57 -08:00
Socratis Petrides 0bf7a715db minor cleanup 2021-11-12 20:04:01 -08:00
Socratis Petrides ad68c8f245 Fixing doc complaints 2021-11-12 18:52:48 -08:00
Socratis Petrides 0b430fa5f6 Started NormalEquationsWeakFormulation::Assemble 2021-11-12 18:49:06 -08:00
Socratis Petrides 238d1921c7 adding TraceIntegrator and AssembleTraceFaceMatrix 2021-11-12 18:48:05 -08:00
Socratis Petrides a25783e3b4 Simplifying P and R in BlockBilinearForm 2021-11-04 09:43:40 -07:00
Socratis Petrides 3aa8d49ca8 started implementation of trace_elem_integrators for DPG 2021-11-03 18:29:08 -07:00
Socratis Petrides 4ed6a4a014 fixing minor bug in reference BlockFOSLS case 2021-11-03 15:11:20 -07:00
Socratis Petrides 08f910343b AMR for reference Block FOSLS 2021-11-03 11:16:59 -07:00
Socratis Petrides 9fb0340206 fixing minor bug in P/R for AMR with blockforms 2021-11-03 09:03:18 -07:00
Socratis Petrides db40e8125e Setting Prolongation/Restriction for BlockBilinearForm. Tested for 2D AMR 2021-11-02 18:56:07 -07:00
Socratis Petrides 3e46fce65f simplifying BlockBilinearFrom::Assembly() 2021-11-02 17:53:50 -07:00
Socratis Petrides f649ed6b62 investigating possible bug in high order 3D runs wrt to essential BC elimination (example 1 hexa mesh) 2021-11-01 17:34:59 -07:00
Socratis Petrides a1a3a51d86 simplifying offset calculation wrt neg orientation 2021-11-01 17:34:05 -07:00
Socratis Petrides 6728fc7eda make style 2021-11-01 16:44:38 -07:00
Socratis Petrides 71e9d4e1d9 poisson_fosls - reference implementation 2021-11-01 16:44:00 -07:00
Socratis Petrides 7feb560f9b testing blockbilinearform with FOSLS poisson 2021-11-01 16:43:28 -07:00
Socratis Petrides aa9eadfcdf fixing orientation (sign) bug in blockbilinearform assemble 2021-11-01 16:42:59 -07:00
Socratis Petrides 82122f05cf testing block integrator by borrowing (bi)linearIntegrators 2021-11-01 16:42:20 -07:00
Socratis Petrides 2d64fa2162 fix bug in blocklinearform assemble 2021-11-01 16:41:21 -07:00
Socratis Petrides 8041734755 adding DenseMatrix::SetSubMatrix functions 2021-11-01 16:40:24 -07:00
Socratis Petrides 523891f05f adding BlockLinearForm (assemble) with testblocklinearinteg 2021-10-29 18:32:31 -07:00
Socratis Petrides b3fc46b6f2 Merge branch 'master' into dpg-dev 2021-10-29 16:43:38 -07:00
Socratis Petrides 9271494723 Merge branch 'master' into dpg-dev 2021-10-27 08:45:53 -07:00
Socratis Petrides f2dbcecb92 debugging BlockBilinearForm::Assemble() 2021-10-25 16:47:32 -07:00
Socratis Petrides f2af9748c4 fixing offset computation in BlockBilinearForm::Assemble() 2021-10-22 19:39:51 -07:00
Socratis Petrides 9813dd7722 fix doxygen complaint 2021-10-22 19:28:52 -07:00
Socratis Petrides 3806e68ed1 adding new classes (block(bi)linearForms(integ) in support for DPG methods 2021-10-22 17:58:52 -07:00
63 changed files with 20520 additions and 22 deletions
@@ -0,0 +1,310 @@
// Example run: ./FOSLS2D_maxwell -ref 4 -o 3 -sol 1 -k 3.0
// ∇ × E - ω H = 0
// -ω E + ∇ × H = J
// --------------------------------------------------------------------------
// | | E | H | RHS |
// --------------------------------------------------------------------------
// | F | (∇ × E,∇ × F)+ ω^2 (E,F) | - ω (∇ × H,F) - ω (H,curF) | - ω (J,F) |
// | | | | |
// | G |-ω (E,∇ × G)-ω (∇ × E,G) | (∇ × H,∇ × G)+ ω^2(H,G) | (J,∇ × G) |
// for E in H1 (scalar) we have ∇ × E = [0 1;-1 0] ∇ E
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Define exact solution
double E_exact(const Vector &x);
void H_exact(const Vector &x, Vector &H);
double frhs(const Vector &x);
void fvrhs(const Vector &x, Vector &f);
void get_maxwell_solution(const Vector &x, double & E, Vector & curlE, double & curl2E);
int dim;
double omega;
int isol = 0;
int main(int argc, char *argv[])
{
StopWatch chrono;
// 1. Parse command-line options.
// geometry file
const char *mesh_file = "../data/star.mesh";
// finite element order of approximation
int order = 1;
// visualization flag
bool visualization = 1;
int ref = 1;
// number of wavelengths
double k = 0.6;
// optional command line inputs
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&ref, "-ref", "--init-refinements",
"Number of initial mesh refinements");
args.AddOption(&k, "-k", "--wavelengths",
"Number of wavelengths.");
args.AddOption(&isol, "-sol", "--solution",
"Exact Solution: 0) Polynomial, 1) Sinusoidal.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
omega = 2.0 * M_PI * k;
// Mesh mesh(1, 1, Element::QUADRILATERAL, true, 1.0, 1.0, false);
Mesh mesh(mesh_file, 1, 1);
dim = mesh.Dimension();
if (dim == 3) {MFEM_ABORT("This is 2D Maxwell")};
for (int i = 0; i < ref; i++)
{
mesh.UniformRefinement();
}
H1_FECollection H1fec(order,dim);
FiniteElementSpace H1fes(&mesh, &H1fec);
ND_FECollection NDfec(order, dim);
FiniteElementSpace NDfes(&mesh, &NDfec);
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (mesh.bdr_attributes.Size())
{
ess_bdr.SetSize(mesh.bdr_attributes.Max());
ess_bdr = 1;
// Essential BC on E. Nothing on H
H1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
Array<int> block_offsets(3);
block_offsets[0] = 0;
block_offsets[1] = H1fes.GetVSize();
block_offsets[2] = NDfes.GetVSize();
block_offsets.PartialSum();
BlockVector x(block_offsets), b(block_offsets);
x = 0.0;
b = 0.0;
FunctionCoefficient Eex(E_exact);
VectorFunctionCoefficient Hex(dim, H_exact);
GridFunction E_gf;
GridFunction H_gf;
E_gf.MakeRef(&H1fes, x.GetBlock(0));
E_gf.ProjectBdrCoefficient(Eex,ess_bdr);
H_gf.MakeRef(&NDfes, x.GetBlock(1));
FunctionCoefficient f(frhs);
ProductCoefficient f_E(-omega, f);
VectorFunctionCoefficient f_H(1,fvrhs);
LinearForm b_E;
b_E.Update(&H1fes, b.GetBlock(0), 0);
b_E.AddDomainIntegrator(new DomainLFIntegrator(f_E));
b_E.Assemble();
LinearForm b_H;
b_H.Update(&NDfes, b.GetBlock(1), 0);
b_H.AddDomainIntegrator(new VectorFEDomainLFCurlIntegrator(f_H));
b_H.Assemble();
// 7. Bilinear form a(.,.) on the finite element space
ConstantCoefficient one(1.0);
ConstantCoefficient omeg2(pow(omega, 2));
ConstantCoefficient negomega(-(omega));
DenseMatrix mat(2);
mat(0,0) = 0.; mat(0,1) = 1.;
mat(1,0) = -1.; mat(1,1) = 0.;
MatrixConstantCoefficient rot(mat);
BilinearForm a_EE(&H1fes);
a_EE.AddDomainIntegrator(new DiffusionIntegrator(one));
a_EE.AddDomainIntegrator(new MassIntegrator(omeg2));
a_EE.Assemble();
a_EE.EliminateEssentialBC(ess_bdr, x.GetBlock(0), b.GetBlock(0));
a_EE.Finalize();
SparseMatrix &A_EE = a_EE.SpMat();
ScalarMatrixProductCoefficient c1(-omega, rot);
MixedBilinearForm a_EH(&H1fes,&NDfes);
// - omega (rot grad E, G) - (omega E, curl G)
a_EH.AddDomainIntegrator(new MixedVectorGradientIntegrator(c1));
a_EH.AddDomainIntegrator(new MixedScalarWeakCurlIntegrator(negomega));
a_EH.Assemble();
a_EH.EliminateTrialDofs(ess_bdr, x.GetBlock(0), b.GetBlock(1));
a_EH.Finalize();
SparseMatrix &A_EH = a_EH.SpMat();
SparseMatrix * A_HE = Transpose(A_EH);
BilinearForm a_HH(&NDfes);
a_HH.AddDomainIntegrator(new CurlCurlIntegrator(one)); // one is the coeff
a_HH.AddDomainIntegrator(new VectorFEMassIntegrator(omeg2)); // one is the coeff
a_HH.Assemble();
a_HH.Finalize();
SparseMatrix &A_HH = a_HH.SpMat();
BlockMatrix LS_Maxwellop(block_offsets);
LS_Maxwellop.SetBlock(0, 0, &A_EE);
LS_Maxwellop.SetBlock(0, 1, A_HE);
LS_Maxwellop.SetBlock(1, 0, &A_EH);
LS_Maxwellop.SetBlock(1, 1, &A_HH);
UMFPackSolver invE;
invE.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
invE.SetOperator(LS_Maxwellop.GetBlock(0,0));
UMFPackSolver invH;
invH.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
invH.SetOperator(LS_Maxwellop.GetBlock(1,1));
BlockDiagonalPreconditioner prec(block_offsets);
prec.SetDiagonalBlock(0, &invE);
prec.SetDiagonalBlock(1, &invH);
int maxit(5000);
double rtol(1.e-16);
double atol(0.0);
CGSolver pcg;
pcg.SetAbsTol(atol);
pcg.SetRelTol(rtol);
pcg.SetMaxIter(maxit);
pcg.SetOperator(LS_Maxwellop);
pcg.SetPreconditioner(prec);
pcg.SetPrintLevel(3);
pcg.Mult(b, x);
int order_quad = max(2, 2 * order + 1);
const IntegrationRule *irs[Geometry::NumGeom];
for (int i = 0; i < Geometry::NumGeom; ++i)
{
irs[i] = &(IntRules.Get(i, order_quad));
}
double Error_E = E_gf.ComputeL2Error(Eex, irs);
double Error_H = H_gf.ComputeL2Error(Hex, irs);
cout << "|| E_h - E || = " << Error_E << "\n";
cout << "|| H_h - H || = " << Error_H << "\n";
cout << "Total error = " << sqrt(Error_H*Error_H+Error_E*Error_E) << "\n";
GridFunction E_exgf(&H1fes);
E_exgf.ProjectCoefficient(Eex);
GridFunction H_exgf(&NDfes);
H_exgf.ProjectCoefficient(Hex);
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
socketstream ex_sock(vishost, visport);
ex_sock.precision(8);
socketstream sol_sockH(vishost, visport);
sol_sockH.precision(8);
socketstream ex_sockH(vishost, visport);
ex_sockH.precision(8);
sol_sock << "solution\n"
<< mesh << E_gf << "window_title 'Numerical E'" << "keys rRljc\n"
<< flush;
ex_sock << "solution\n"
<< mesh << E_exgf << "window_title 'Exact E'" << "keys rRljc\n"
<< flush;
sol_sockH << "solution\n"
<< mesh << H_gf << "window_title 'Numerical H'" << "keys rRljc\n"
<< flush;
ex_sockH << "solution\n"
<< mesh << H_exgf << "window_title 'Exact H'" << "keys rRljc\n"
<< flush;
}
delete A_HE;
return 0;
}
double E_exact(const Vector &x)
{
double E, curl2E;
Vector curlE(2);
get_maxwell_solution(x, E, curlE, curl2E);
return E; //Scalar
}
//define exact solution
void H_exact(const Vector &x, Vector &H)
{
double E, curl2E;
Vector curlE(2);
get_maxwell_solution(x, E, curlE, curl2E);
H[0] = curlE[0]/omega;
H[1] = curlE[1]/omega;
}
double frhs(const Vector &x)
{
double E, curl2E;
Vector curlE(2);
get_maxwell_solution(x, E, curlE, curl2E);
// - omega E + curl H = f
// - omega E + curl (curl E) / omega = f
double f = - omega * E + curl2E / omega;
return f;
}
void fvrhs(const Vector &x, Vector &f)
{
double E, curl2E;
Vector curlE(2);
get_maxwell_solution(x, E, curlE, curl2E);
f[0] = - omega * E + curl2E / omega;
}
void get_maxwell_solution(const Vector &X, double & E, Vector & curlE, double & curl2E)
{
double x = X[0];
double y = X[1];
double Ex, Ey, Exx, Eyy;
if (isol == 0) // polynomial
{
E = x * (1.0 - x) * y * (1.0 - y);
Ex = (1.0 - 2.0 * x) * y * (1.0 - y);
Ey = x * (1.0 - x) * (1.0 - 2.0 * y);
Exx = -2.0 * y * (1.0 - y);
Eyy = -2.0 * x * (1.0 - x);
}
else
{
double s = omega * (y+x);
E = cos(s);
Ex = -omega * sin(s);
Ey = Ex;
Exx = - omega * omega * E;
Eyy = Exx;
}
curlE[0] = Ey;
curlE[1] = -Ex;
curl2E = -Exx - Eyy;
}
+61
View File
@@ -0,0 +1,61 @@
# Copyright (c) 2010-2022, 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)/examples/dpg_tests/EM-diffusion,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = primal_dpg
PAR_EXAMPLES =
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean clean-build clean-exec
# Remove built-in rule
%: %.cpp
# Replace the default implicit rule for *.cpp files
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
all: $(EXAMPLES)
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
# Testing: Parallel vs. serial runs
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
%-test-seq: %
@$(call mfem-test,$<,, Serial example)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
rm -rf ParaView
clean-exec:
@@ -0,0 +1,201 @@
// MFEM primal_dpg example
//
// Compile with: make primal_dpg
//
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
void E_exact(const Vector &, Vector &);
void f_exact(const Vector &, Vector &);
double freq = 1.0, kappa;
int dim;
int main(int argc, char *argv[])
{
// 1. Parse command line options
const char *mesh_file = "../../../data/star.mesh";
int order = 1;
bool static_cond = false;
int ref = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&order, "-o", "--order", "Finite element polynomial degree");
args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
" solution.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&ref, "-ref", "--refinements",
"Number of refinements.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
kappa = freq * M_PI;
// 2. Read the mesh from the given mesh file, and refine once uniformly.
Mesh mesh(mesh_file);
for (int i = 0; i<ref; i++)
{
mesh.UniformRefinement();
}
dim = mesh.Dimension();
int sdim = mesh.SpaceDimension();
// 3. Define a finite element space on the mesh. Here we use H1 continuous
// high-order Lagrange finite elements of the given order.
ND_FECollection fec(order, mesh.Dimension());
FiniteElementSpace NDfes(&mesh, &fec);
FiniteElementCollection * trace_fec = nullptr;
if (dim == 3)
{
trace_fec = new ND_Trace_FECollection(order,mesh.Dimension());
}
else
{
trace_fec = new H1_Trace_FECollection(order,mesh.Dimension());
}
FiniteElementSpace trace_fes(&mesh, trace_fec);
int test_order = order+1;
ND_FECollection test_fec(test_order,mesh.Dimension());
Array<FiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fecs;
trial_fes.Append(&NDfes);
trial_fes.Append(&trace_fes);
test_fecs.Append(&test_fec);
NormalEquations * a = new NormalEquations(trial_fes,test_fecs);
ConstantCoefficient one(1.0);
a->AddTrialIntegrator(new CurlCurlIntegrator(one),0,0);
a->AddTrialIntegrator(new VectorFEMassIntegrator(one),0,0);
a->AddTrialIntegrator(new TangentTraceIntegrator,1,0);
a->AddTestIntegrator(new CurlCurlIntegrator(one),0,0);
a->AddTestIntegrator(new VectorFEMassIntegrator(one),0,0);
VectorFunctionCoefficient f(sdim, f_exact);
a->AddDomainLFIntegrator(new VectorFEDomainLFIntegrator(f),0);
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (mesh.bdr_attributes.Size())
{
ess_bdr.SetSize(mesh.bdr_attributes.Max());
ess_bdr = 1;
NDfes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
Vector X,B;
OperatorPtr Ah;
VectorFunctionCoefficient E(sdim, E_exact);
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = NDfes.GetVSize();
offsets[2] = trace_fes.GetVSize();
offsets.PartialSum();
BlockVector x(offsets);
x = 0.;
GridFunction E_gf(&NDfes);
E_gf.MakeRef(&NDfes,x.GetBlock(0));
E_gf.ProjectBdrCoefficientTangent(E,ess_bdr);
E_gf.ProjectCoefficient(E);
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
BlockMatrix * A = (BlockMatrix *)(Ah.Ptr());
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
M->owns_blocks = 1;
for (int i=0; i<A->NumRowBlocks(); i++)
{
M->SetDiagonalBlock(i,new UMFPackSolver(A->GetBlock(i,i)));
}
GMRESSolver cg;
cg.SetRelTol(1e-8);
cg.SetMaxIter(2000);
cg.SetPrintLevel(3);
cg.SetPreconditioner(*M);
cg.SetOperator(*A);
cg.Mult(B, X);
delete M;
a->RecoverFEMSolution(X,x);
E_gf.MakeRef(&NDfes,x.GetData());
double L2Error = E_gf.ComputeL2Error(E);
mfem::out << "L2_error = " << L2Error << endl;
char vishost[] = "localhost";
int visport = 19916;
socketstream solu_sock(vishost, visport);
solu_sock.precision(8);
solu_sock << "solution\n" << mesh << E_gf <<
"window_title 'Numerical u' "
<< flush;
delete trace_fec;
return 0;
}
void E_exact(const Vector &x, Vector &E)
{
if (dim == 3)
{
E(0) = sin(kappa * x(1));
E(1) = sin(kappa * x(2));
E(2) = sin(kappa * x(0));
}
else
{
E(0) = sin(kappa * x(1));
E(1) = sin(kappa * x(0));
if (x.Size() == 3) { E(2) = 0.0; }
}
}
void f_exact(const Vector &x, Vector &f)
{
if (dim == 3)
{
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
f(1) = (1. + kappa * kappa) * sin(kappa * x(2));
f(2) = (1. + kappa * kappa) * sin(kappa * x(0));
}
else
{
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
if (x.Size() == 3) { f(2) = 0.0; }
}
}
@@ -0,0 +1,677 @@
// MFEM Ultraweak DPG acoustics example
//
// Compile with: make uw_dpg
//
// - Δ p - ω^2 p = f̃ , in Ω
// p = p_0, on ∂Ω
// First Order System
// ∇ p + i ω u = 0, in Ω
// ∇⋅u + i ω p = f, in Ω
// p = p_0, in ∂Ω
// where f:=f̃/(i ω)
// UW-DPG:
//
// p ∈ L^2(Ω), u ∈ (L^2(Ω))^dim
// p̂ ∈ H^1/2(Ω), û ∈ H^-1/2(Ω)
// -(p, ∇⋅v) + i ω (u , v) + < p̂, v⋅n> = 0, ∀ v ∈ H(div,Ω)
// -(u , ∇ q) + i ω (p , q) + < û, q > = (f,q) ∀ q ∈ H^1(Ω)
// p̂ = p_0 on ∂Ω
// Note:
// p̂ := p on Γ_h (skeleton)
// û := u on Γ_h
// -------------------------------------------------------------
// | | p | u | p̂ | û | RHS |
// -------------------------------------------------------------
// | v | -(p, ∇⋅v) | i ω (u,v) | < p̂, v⋅n> | | |
// | | | | | | |
// | q | i ω (p,q) |-(u , ∇ q) | | < û,q > | (f,q) |
// where (q,v) ∈ H^1(Ω) × H(div,Ω)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
void acoustics_solution(const Vector & X, complex<double> & p,
vector<complex<double>> &dp, complex<double> & d2p);
void acoustics_solution_r(const Vector & X, double & p,
Vector &dp, double & d2p);
void acoustics_solution_i(const Vector & X, double & p,
Vector &dp, double & d2p);
double p_exact_r(const Vector &x);
double p_exact_i(const Vector &x);
void u_exact_r(const Vector &x, Vector & u);
void u_exact_i(const Vector &x, Vector & u);
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 divu_exact_r(const Vector &x);
double divu_exact_i(const Vector &x);
double d2_exact_r(const Vector &x);
double d2_exact_i(const Vector &x);
double hatp_exact_r(const Vector & X);
double hatp_exact_i(const Vector & X);
void hatu_exact(const Vector & X, Vector & hatu);
void hatu_exact_r(const Vector & X, Vector & hatu);
void hatu_exact_i(const Vector & X, Vector & hatu);
int dim;
double omega;
enum prob_type
{
plane_wave,
gaussian_beam
};
prob_type prob;
int main(int argc, char *argv[])
{
const char *mesh_file = "../../../data/inline-quad.mesh";
int order = 1;
int delta_order = 1;
bool visualization = true;
double rnum=1.0;
int ref = 1;
double theta = 0.0;
bool adjoint_graph_norm = false;
bool static_cond = false;
int iprob = 0;
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(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"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(&theta, "-theta", "--theta",
"Theta parameter for AMR");
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
"-no-graph-norm", "--no-adjoint-graph-norm",
"Enable or disable Adjoint Graph Norm on the test space");
args.AddOption(&ref, "-ref", "--serial_ref",
"Number of serial refinements.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
if (iprob > 1) { iprob = 0; }
prob = (prob_type)iprob;
omega = 2.*M_PI*rnum;
Mesh mesh(mesh_file, 1, 1);
dim = mesh.Dimension();
// Define spaces
// L2 space for p
FiniteElementCollection *p_fec = new L2_FECollection(order-1,dim);
FiniteElementSpace *p_fes = new FiniteElementSpace(&mesh,p_fec);
// Vector L2 space for u
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
FiniteElementSpace *u_fes = new FiniteElementSpace(&mesh,u_fec, dim);
// H^1/2 space for p̂
FiniteElementCollection * hatp_fec = new H1_Trace_FECollection(order,dim);
FiniteElementSpace *hatp_fes = new FiniteElementSpace(&mesh,hatp_fec);
// H^-1/2 space for û
FiniteElementCollection * hatu_fec = new RT_Trace_FECollection(order-1,dim);
FiniteElementSpace *hatu_fes = new FiniteElementSpace(&mesh,hatu_fec);
// testspace fe collections
int test_order = order+delta_order;
FiniteElementCollection * q_fec = new H1_FECollection(test_order, dim);
FiniteElementCollection * v_fec = new RT_FECollection(test_order-1, dim);
mfem::out << "p_fes space true dofs = " << p_fes->GetTrueVSize() << endl;
mfem::out << "u_fes space true dofs = " << u_fes->GetTrueVSize() << endl;
mfem::out << "hatp_fes space true dofs = " << hatp_fes->GetTrueVSize() << endl;
mfem::out << "hatu_fes space true dofs = " << hatu_fes->GetTrueVSize() << endl;
// Coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient zero(0.0);
Vector vec0(dim); vec0 = 0.;
VectorConstantCoefficient vzero(vec0);
ConstantCoefficient negone(-1.0);
ConstantCoefficient omeg(omega);
ConstantCoefficient omeg2(omega*omega);
ConstantCoefficient negomeg(-omega);
// Normal equation weak formulation
Array<FiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(p_fes);
trial_fes.Append(u_fes);
trial_fes.Append(hatp_fes);
trial_fes.Append(hatu_fes);
test_fec.Append(q_fec);
test_fec.Append(v_fec);
ComplexNormalEquations * a = new ComplexNormalEquations(trial_fes,test_fec);
a->StoreMatrices();
// i ω (p,q)
a->AddTrialIntegrator(nullptr,new MixedScalarMassIntegrator(omeg),0,0);
// -(u , ∇ q)
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(negone)),nullptr,1,0);
// -(p, ∇⋅v)
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(one),nullptr,0,1);
// i ω (u,v)
a->AddTrialIntegrator(nullptr,new TransposeIntegrator(new VectorFEMassIntegrator(omeg)),1,1);
// < p̂, v⋅n>
a->AddTrialIntegrator(new NormalTraceIntegrator,nullptr,2,1);
// < û,q >
a->AddTrialIntegrator(new TraceIntegrator,nullptr,3,0);
// for impedence condition (only on the boundary)
// TODO
// a->AddTrialIntegrator(new TraceIntegrator,nullptr,2,0);
// test integrators
//space-induced norm for H(div) × H1
// (∇q,∇δq)
a->AddTestIntegrator(new DiffusionIntegrator(one),nullptr,0,0);
// (q,δq)
a->AddTestIntegrator(new MassIntegrator(one),nullptr,0,0);
// (∇⋅v,∇⋅δv)
a->AddTestIntegrator(new DivDivIntegrator(one),nullptr,1,1);
// (v,δv)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,1,1);
// additional integrators for the adjoint graph norm
if (adjoint_graph_norm)
{
// -i ω (∇q,δv)
a->AddTestIntegrator(nullptr,new MixedVectorGradientIntegrator(negomeg),0,1);
// i ω (v,∇ δq)
a->AddTestIntegrator(nullptr,new MixedVectorWeakDivergenceIntegrator(negomeg),1,0);
// ω^2 (v,δv)
a->AddTestIntegrator(new VectorFEMassIntegrator(omeg2),nullptr,1,1);
// - i ω (∇⋅v,δq)
a->AddTestIntegrator(nullptr,new VectorFEDivergenceIntegrator(negomeg),1,0);
// i ω (q,∇⋅v)
a->AddTestIntegrator(nullptr,new MixedScalarWeakGradientIntegrator(negomeg),0,1);
// ω^2 (q,δq)
a->AddTestIntegrator(new MassIntegrator(omeg2),nullptr,0,0);
}
// RHS
FunctionCoefficient f_rhs_r(rhs_func_r);
FunctionCoefficient f_rhs_i(rhs_func_i);
a->AddDomainLFIntegrator(new DomainLFIntegrator(f_rhs_r),new DomainLFIntegrator(f_rhs_i),0);
FunctionCoefficient hatpex_r(hatp_exact_r);
FunctionCoefficient hatpex_i(hatp_exact_i);
VectorFunctionCoefficient hatuex_r(dim,hatu_exact_r);
VectorFunctionCoefficient hatuex_i(dim,hatu_exact_i);
Array<int> elements_to_refine;
socketstream p_out_r;
socketstream p_out_i;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
p_out_r.open(vishost, visport);
p_out_i.open(vishost, visport);
}
double res0 = 0.;
double err0 = 0.;
int dof0;
mfem::out << " Refinement |"
<< " Dofs |"
<< " L2 Error |"
<< " Relative % |"
<< " Rate |"
<< " Residual |"
<< " Rate |" << endl;
mfem::out << " --------------------"
<< "-------------------"
<< "-------------------"
<< "-------------------" << endl;
for (int i = 0; i<ref; i++)
{
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (mesh.bdr_attributes.Size())
{
ess_bdr.SetSize(mesh.bdr_attributes.Max());
ess_bdr = 1;
// ess_bdr[1] = 0;
// ess_bdr[2] = 1;
hatp_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// hatu_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// shift the ess_tdofs
for (int j = 0; j < ess_tdof_list.Size(); j++)
{
ess_tdof_list[j] += p_fes->GetTrueVSize() + u_fes->GetTrueVSize();
// + hatp_fes->GetTrueVSize();
}
Array<int> offsets(5);
offsets[0] = 0;
offsets[1] = p_fes->GetVSize();
offsets[2] = u_fes->GetVSize();
offsets[3] = hatp_fes->GetVSize();
offsets[4] = hatu_fes->GetVSize();
offsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
double * xdata = x.GetData();
ComplexGridFunction hatp_gf(hatp_fes);
hatp_gf.real().MakeRef(hatp_fes,&xdata[offsets[2]]);
hatp_gf.imag().MakeRef(hatp_fes,&xdata[offsets.Last()+ offsets[2]]);
hatp_gf.ProjectBdrCoefficient(hatpex_r,hatpex_i, ess_bdr);
// ComplexGridFunction hatu_gf(hatu_fes);
// hatu_gf.real().MakeRef(hatu_fes,&xdata[offsets[3]]);
// hatu_gf.imag().MakeRef(hatu_fes,&xdata[offsets.Last()+ offsets[3]]);
// hatu_gf.ProjectBdrCoefficientNormal(hatuex_r,hatuex_i, ess_bdr);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
SparseMatrix * Ar = dynamic_cast<BlockMatrix *>(&Ahc->real())->CreateMonolithic();
SparseMatrix * Ai = dynamic_cast<BlockMatrix *>(&Ahc->imag())->CreateMonolithic();
ComplexSparseMatrix Ac(Ar,Ai,true,true);
SparseMatrix * A = Ac.GetSystemMatrix();
mfem::out << "Size of the linear system: " << A->Height() << std::endl;
UMFPackSolver umf(*A);
umf.Mult(B,X);
delete A;
a->RecoverFEMSolution(X,x);
Vector & residuals = a->ComputeResidual(x);
double residual = residuals.Norml2();
elements_to_refine.SetSize(0);
double max_resid = residuals.Max();
for (int iel = 0; iel<mesh.GetNE(); iel++)
{
if (residuals[iel] > theta * max_resid)
{
elements_to_refine.Append(iel);
}
}
ComplexGridFunction p(p_fes);
p.real().MakeRef(p_fes,x.GetData());
p.imag().MakeRef(p_fes,&x.GetData()[offsets.Last()]);
ComplexGridFunction pgf_ex(p_fes);
FunctionCoefficient p_ex_r(p_exact_r);
FunctionCoefficient p_ex_i(p_exact_i);
pgf_ex.ProjectCoefficient(p_ex_r, p_ex_i);
int dofs = X.Size()/2;
double p_err_r = p.real().ComputeL2Error(p_ex_r);
double p_err_i = p.imag().ComputeL2Error(p_ex_i);
double L2Error = sqrt(p_err_r*p_err_r + p_err_i*p_err_i);
double rate_err = (i) ? dim*log(err0/L2Error)/log((double)dof0/dofs) : 0.0;
double rate_res = (i) ? dim*log(res0/residual)/log((double)dof0/dofs) : 0.0;
err0 = L2Error;
res0 = residual;
dof0 = dofs;
mfem::out << std::right << std::setw(11) << i << " | "
<< std::setw(10) << dof0 << " | "
<< std::setprecision(3)
<< std::setw(10) << std::scientific << err0 << " | "
<< std::setprecision(3)
<< std::setw(10) << std::fixed << 0.0 << " | "
<< 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::resetiosflags(std::ios::showbase)
<< std::setw(10) << std::scientific
<< std::endl;
if (visualization)
{
p_out_r.precision(8);
p_out_r << "solution\n" << mesh << p.real() <<
"window_title 'Real Numerical presure' "
<< flush;
p_out_i.precision(8);
p_out_i << "solution\n" << mesh << p.imag() <<
"window_title 'Imag Numerical presure' "
<< flush;
}
if (i == ref)
break;
mesh.GeneralRefinement(elements_to_refine,1,1);
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
}
delete a;
delete q_fec;
delete v_fec;
delete hatp_fes;
delete hatp_fec;
delete hatu_fes;
delete hatu_fec;
delete u_fec;
delete p_fec;
delete u_fes;
delete p_fes;
return 0;
}
double p_exact_r(const Vector &x)
{
double p,d2p;
Vector dp;
acoustics_solution_r(x,p,dp,d2p);
return p;
}
double p_exact_i(const Vector &x)
{
double p,d2p;
Vector dp;
acoustics_solution_i(x,p,dp,d2p);
return p;
}
double hatp_exact_r(const Vector & X)
{
return p_exact_r(X);
}
double hatp_exact_i(const Vector & X)
{
return p_exact_i(X);
}
void gradp_exact_r(const Vector &x, Vector &grad)
{
grad.SetSize(x.Size());
double p,d2p;
acoustics_solution_r(x,p,grad,d2p);
}
void gradp_exact_i(const Vector &x, Vector &grad)
{
grad.SetSize(x.Size());
double p,d2p;
acoustics_solution_i(x,p,grad,d2p);
}
double d2_exact_r(const Vector &x)
{
double p,d2p;
Vector dp;
acoustics_solution_r(x,p,dp,d2p);
return d2p;
}
double d2_exact_i(const Vector &x)
{
double p,d2p;
Vector dp;
acoustics_solution_i(x,p,dp,d2p);
return d2p;
}
// u = - ∇ p / (i ω )
// = i (∇ p_r + i * ∇ p_i) / ω
// = - ∇ p_i / ω + i ∇ p_r / ω
void u_exact_r(const Vector &x, Vector & u)
{
gradp_exact_i(x,u);
u *= -1./omega;
}
void u_exact_i(const Vector &x, Vector & u)
{
gradp_exact_r(x,u);
u *= 1./omega;
}
void hatu_exact_r(const Vector & X, Vector & hatu)
{
u_exact_r(X,hatu);
}
void hatu_exact_i(const Vector & X, Vector & hatu)
{
u_exact_i(X,hatu);
}
// ∇⋅u = i Δ p / ω
// = i (Δ p_r + i * Δ p_i) / ω
// = - Δ p_i / ω + i Δ p_r / ω
double divu_exact_r(const Vector &x)
{
return -d2_exact_i(x)/omega;
}
double divu_exact_i(const Vector &x)
{
return d2_exact_r(x)/omega;
}
// f = ∇⋅u + i ω p
// f_r = ∇⋅u_r - ω p_i
double rhs_func_r(const Vector &x)
{
double p = p_exact_i(x);
double divu = divu_exact_r(x);
return divu - omega * p;
}
// f_i = ∇⋅u_i + ω p_r
double rhs_func_i(const Vector &x)
{
double p = p_exact_r(x);
double divu = divu_exact_i(x);
return divu + omega * p;
}
void acoustics_solution_r(const Vector & X, double & p,
Vector &dp, double & d2p)
{
complex<double> zp, d2zp;
vector<complex<double>> dzp;
acoustics_solution(X,zp,dzp,d2zp);
p = zp.real();
d2p = d2zp.real();
dp.SetSize(X.Size());
for (int i = 0; i<X.Size(); i++)
{
dp[i] = dzp[i].real();
}
}
void acoustics_solution_i(const Vector & X, double & p,
Vector &dp, double & d2p)
{
complex<double> zp, d2zp;
vector<complex<double>> dzp;
acoustics_solution(X,zp,dzp,d2zp);
p = zp.imag();
d2p = d2zp.imag();
dp.SetSize(X.Size());
for (int i = 0; i<X.Size(); i++)
{
dp[i] = dzp[i].imag();
}
}
void acoustics_solution(const Vector & X, complex<double> & p, vector<complex<double>> & dp,
complex<double> & d2p)
{
dp.resize(X.Size());
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();
p = exp(-alpha);
d2p = - dim * beta * beta * p;
for (int i = 0; i<X.Size(); i++)
{
dp[i] = - zi * beta * p;
}
}
break;
default:
{
double rk = omega;
double alpha = 45 * M_PI/180.;
double sina = sin(alpha);
double cosa = cos(alpha);
// shift the origin
double xprim=X(0) + 0.1;
double yprim=X(1) + 0.1;
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> zi = complex<double>(0., 1.);
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;
p = zp;
dp[0] = (zdpdx*dxdxprim + zdpdy*dydxprim);
dp[1] = (zdpdx*dxdyprim + zdpdy*dydyprim);
d2p = (zd2pdxdx*dxdxprim + zd2pdydx*dydxprim)*dxdxprim + (zd2pdxdy*dxdxprim + zd2pdydy*dydxprim)*dydxprim
+ (zd2pdxdx*dxdyprim + zd2pdydx*dydyprim)*dxdyprim + (zd2pdxdy*dxdyprim + zd2pdydy*dydyprim)*dydyprim;
}
break;
}
}
+294
View File
@@ -0,0 +1,294 @@
// MFEM FOSLS acoustics Example
//
// Compile with: make fosls
//
// Definite/Indefinite Helmholtz
// - Δ p ± ω^2 p = f̃ , in Ω
// p = p_0, on ∂Ω
// First Order System
// ∇ p - ω u = 0, in Ω
// - ∇⋅u ± ω p = f, in Ω
// p = p_0, in ∂Ω
// where f:=f̃/ω
// FOSLS:
// minimize 1/2(||∇p - ω u||^2 + ||-∇⋅u ± ω p - f||^2)
// (p,u) ∈ H^1(Ω) × H(div,Ω)
// -------------------------------------------------------------------
// | | p | u | RHS |
// -------------------------------------------------------------------
// | q | (∇ p,∇ q) + ω^2(p,q) | ∓ ω (∇⋅u,q) - ω (u, ∇ q) | ± ω(f,q) |
// | | | | |
// | v | ∓ ω (p,∇⋅v) - ω (∇ p,v)| (∇⋅u,∇⋅v) + ω^2 (u,v) | -(f,∇⋅v) |
// where (q,v) ∈ H^1(Ω) × H(div,Ω)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// #define DEFINITE
double p_exact(const Vector &x);
void u_exact(const Vector &x, Vector & u);
double rhs_func(const Vector &x);
void gradp_exact(const Vector &x, Vector &gradu);
double divu_exact(const Vector &x);
double d2_exact(const Vector &x);
int dim;
double omega;
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../../../data/inline-quad.mesh";
int order = 1;
bool visualization = true;
double rnum=1.0;
int sr = 1;
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(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&sr, "-sr", "--serial_ref",
"Number of serial refinements.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
omega = 2.0 * M_PI * rnum;
Mesh mesh(mesh_file, 1, 1);
dim = mesh.Dimension();
for (int i = 0; i < sr; i++ )
{
mesh.UniformRefinement();
}
FiniteElementCollection *H1fec = new H1_FECollection(order, dim);
FiniteElementCollection *RTfec = new RT_FECollection(order-1, dim);
FiniteElementSpace * H1fes = new FiniteElementSpace(&mesh, H1fec);
FiniteElementSpace * RTfes = new FiniteElementSpace(&mesh, RTfec);
Array<FiniteElementSpace *> fespaces(2);
fespaces[0] = H1fes;
fespaces[1] = RTfes;
Array<int> ess_bdr;
Array<int> ess_tdof_list;
if (mesh.bdr_attributes.Size())
{
ess_bdr.SetSize(mesh.bdr_attributes.Max());
ess_bdr = 1;
fespaces[0]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
BlockBilinearForm a(fespaces);
a.SetDiagonalPolicy(mfem::Operator::DIAG_KEEP);
cout << "H1 fespace = " << H1fes->GetTrueVSize() << endl;
cout << "RT fespace = " << RTfes->GetTrueVSize() << endl;
ConstantCoefficient one(1.0);
ConstantCoefficient negone(-1.0);
ConstantCoefficient omeg(omega);
ConstantCoefficient negomeg(-omega);
ConstantCoefficient omeg2(omega*omega);
Array2D<BilinearFormIntegrator * > blfi(2,2);
// blfi(0,0) = (∇ p,∇ q) + ω^2(p,q)
SumIntegrator * integ00 = new SumIntegrator();
integ00->AddIntegrator(new DiffusionIntegrator(one));
integ00->AddIntegrator(new MassIntegrator(omeg2));
blfi(0,0) = integ00;
// blfi(0,1) = ∓ ω (∇⋅u,q) - ω (u, ∇ q)
SumIntegrator * integ01 = new SumIntegrator();
#ifdef DEFINITE
// -ω (∇⋅u,q)
integ01->AddIntegrator(new MixedScalarDivergenceIntegrator(negomeg));
#else
// ω (∇⋅u,q)
integ01->AddIntegrator(new MixedScalarDivergenceIntegrator(omeg));
#endif
// - ω (u, ∇ q)
integ01->AddIntegrator(new MixedVectorWeakDivergenceIntegrator(omeg));
blfi(0,1) = integ01;
// blfi(1,0) = ∓ ω (p,∇⋅v) - ω (∇ p,v)
SumIntegrator * integ10 = new SumIntegrator();
#ifdef DEFINITE
// - ω (p,∇⋅v)
integ10->AddIntegrator(new MixedScalarWeakGradientIntegrator(omeg));
#else
// ω (p,∇⋅v)
integ10->AddIntegrator(new MixedScalarWeakGradientIntegrator(negomeg));
#endif
// - ω (∇ p,v)
integ10->AddIntegrator(new MixedVectorGradientIntegrator(negomeg));
blfi(1,0) = integ10;
// blfi(1,1) = (∇⋅u,∇⋅v) + ω^2 (u,v)
SumIntegrator * integ11 = new SumIntegrator();
integ11->AddIntegrator(new DivDivIntegrator(one));
integ11->AddIntegrator(new VectorFEMassIntegrator(omeg2));
blfi(1,1) = integ11;
BlockLinearForm b(fespaces);
Array<LinearFormIntegrator * > lfi(2);
// ± ω (f,q)
FunctionCoefficient f_rhs(rhs_func);
#ifdef DEFINITE
ProductCoefficient w_f(omeg,f_rhs);
#else
ProductCoefficient w_f(negomeg,f_rhs);
#endif
// lfi[0] = new DomainLFIntegrator(w_f);
lfi[0] = new DomainLFIntegrator(w_f);
// -(f,∇⋅v)
ProductCoefficient neg_f(negone,f_rhs);
// lfi[1] = new VectorFEDomainLFDivIntegrator(f_rhs);
lfi[1] = new VectorFEDomainLFDivIntegrator(neg_f);
TestBlockBilinearFormIntegrator * integ = new TestBlockBilinearFormIntegrator();
integ->SetIntegrators(blfi);
a.AddDomainIntegrator(integ);
a.Assemble();
TestBlockLinearFormIntegrator * lininteg = new TestBlockLinearFormIntegrator();
lininteg->SetIntegrators(lfi);
b.AddDomainIntegrator(lininteg);
b.Assemble();
int size = 0;
for (int i = 0; i<fespaces.Size(); i++)
{
size += fespaces[i]->GetVSize();
}
Vector x(size);
x = 0.0;
FunctionCoefficient p_ex(p_exact);
VectorFunctionCoefficient gradp_ex(dim,gradp_exact);
VectorFunctionCoefficient u_ex(dim,u_exact);
FunctionCoefficient divu_ex(divu_exact);
GridFunction p_gf, u_gf;
GridFunction pex_gf(H1fes);
p_gf.MakeRef(H1fes,x,0);
// p_gf.ProjectBdrCoefficient(p_ex,ess_bdr);
p_gf.ProjectCoefficient(p_ex);
pex_gf.ProjectCoefficient(p_ex);
u_gf.MakeRef(RTfes,x,H1fes->GetVSize());
u_gf = 0.;
OperatorPtr A;
Vector X,B;
a.FormLinearSystem(ess_tdof_list,x,b,A,X,B);
GSSmoother M((SparseMatrix&)(*A));
CGSolver cg;
cg.SetRelTol(1e-10);
cg.SetMaxIter(2000);
cg.SetPrintLevel(1);
cg.SetPreconditioner(M);
cg.SetOperator(*A);
cg.Mult(B, X);
a.RecoverFEMSolution(X,b,x);
p_gf.MakeRef(H1fes,x,0);
u_gf.MakeRef(RTfes,x,H1fes->GetVSize());
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream solu_sock(vishost, visport);
solu_sock.precision(8);
solu_sock << "solution\n" << mesh << p_gf <<
"window_title 'Numerical p' "
<< flush;
// socketstream sols_sock(vishost, visport);
// sols_sock.precision(8);
// sols_sock << "solution\n" << mesh << u_gf <<
// "window_title 'Numerical sigma' "
// << flush;
socketstream solex_sock(vishost, visport);
solex_sock.precision(8);
solex_sock << "solution\n" << mesh << pex_gf <<
"window_title 'Exact p' "
<< flush;
}
return 0;
}
double rhs_func(const Vector &x)
{
double p = p_exact(x);
double divu = divu_exact(x);
// f = - ∇⋅u ± ω p,
#ifdef DEFINITE
return -divu + omega * p;
#else
return -divu - omega * p;
#endif
}
double p_exact(const Vector &x)
{
return sin(omega*x.Sum());
}
void gradp_exact(const Vector &x, Vector &grad)
{
grad.SetSize(x.Size());
grad = omega * cos(omega * x.Sum());
}
void u_exact(const Vector &x, Vector & u)
{
gradp_exact(x,u);
u *= 1./omega;
}
double divu_exact(const Vector &x)
{
return d2_exact(x)/omega;
}
double d2_exact(const Vector &x)
{
return -dim * omega * omega * sin(omega*x.Sum());
}
+59
View File
@@ -0,0 +1,59 @@
# Copyright (c) 2010-2022, 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)/examples/dpg_tests/acoustics,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = fosls uw_dpg strong_dpg complex_uw_dpg
PAR_EXAMPLES = uw_dpgp
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean clean-build clean-exec
# Remove built-in rule
%: %.cpp
# Replace the default implicit rule for *.cpp files
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
all: $(EXAMPLES)
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
# Testing: Parallel vs. serial runs
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
%-test-seq: %
@$(call mfem-test,$<,, Serial example)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@@ -0,0 +1,837 @@
// MFEM Ultraweak DPG acoustics example
//
// Compile with: make pcomplex_uw_dpg
//
// sample runs
// ./pcomplex_uw_dpg -o 3 -m ../../../data/inline-quad.mesh -sref 2 -pref 3 -rnum 4.1 -prob 0 -sc -graph-norm
// - Δ p - ω^2 p = f̃ , in Ω
// p = p_0, on ∂Ω
// First Order System
// ∇ p + i ω u = 0, in Ω
// ∇⋅u + i ω p = f, in Ω
// p = p_0, in ∂Ω
// where f:=f̃/(i ω)
// UW-DPG:
//
// p ∈ L^2(Ω), u ∈ (L^2(Ω))^dim
// p̂ ∈ H^1/2(Ω), û ∈ H^-1/2(Ω)
// -(p, ∇⋅v) + i ω (u , v) + < p̂, v⋅n> = 0, ∀ v ∈ H(div,Ω)
// -(u , ∇ q) + i ω (p , q) + < û, q > = (f,q) ∀ q ∈ H^1(Ω)
// p̂ = p_0 on ∂Ω
// Note:
// p̂ := p on Γ_h (skeleton)
// û := u on Γ_h
// -------------------------------------------------------------
// | | p | u | p̂ | û | RHS |
// -------------------------------------------------------------
// | v | -(p, ∇⋅v) | i ω (u,v) | < p̂, v⋅n> | | |
// | | | | | | |
// | q | i ω (p,q) |-(u , ∇ q) | | < û,q > | (f,q) |
// where (q,v) ∈ H^1(Ω) × H(div,Ω)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
void acoustics_solution(const Vector & X, complex<double> & p,
vector<complex<double>> &dp, complex<double> & d2p);
void acoustics_solution_r(const Vector & X, double & p,
Vector &dp, double & d2p);
void acoustics_solution_i(const Vector & X, double & p,
Vector &dp, double & d2p);
double p_exact_r(const Vector &x);
double p_exact_i(const Vector &x);
void u_exact_r(const Vector &x, Vector & u);
void u_exact_i(const Vector &x, Vector & u);
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 divu_exact_r(const Vector &x);
double divu_exact_i(const Vector &x);
double d2_exact_r(const Vector &x);
double d2_exact_i(const Vector &x);
double hatp_exact_r(const Vector & X);
double hatp_exact_i(const Vector & X);
void hatu_exact(const Vector & X, Vector & hatu);
void hatu_exact_r(const Vector & X, Vector & hatu);
void hatu_exact_i(const Vector & X, Vector & hatu);
int dim;
double omega;
enum prob_type
{
plane_wave,
gaussian_beam
};
prob_type prob;
int main(int argc, char *argv[])
{
Mpi::Init();
int num_procs = Mpi::WorldSize();
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;
double theta = 0.0;
bool adjoint_graph_norm = false;
bool static_cond = false;
int iprob = 0;
int sr = 0;
int pr = 1;
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(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"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(&theta, "-theta", "--theta",
"Theta parameter for AMR");
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
"-no-graph-norm", "--no-adjoint-graph-norm",
"Enable or disable Adjoint Graph Norm on the 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.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
if (iprob > 1) { iprob = 0; }
prob = (prob_type)iprob;
omega = 2.*M_PI*rnum;
Mesh mesh(mesh_file, 1, 1);
for (int i = 0; i<sr; i++)
{
mesh.UniformRefinement();
}
dim = mesh.Dimension();
mesh.EnsureNCMesh();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
// Define spaces
// L2 space for p
FiniteElementCollection *p_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *p_fes = new ParFiniteElementSpace(&pmesh,p_fec);
// Vector L2 space for u
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *u_fes = new ParFiniteElementSpace(&pmesh,u_fec, dim);
// H^1/2 space for p̂
FiniteElementCollection * hatp_fec = new H1_Trace_FECollection(order,dim);
ParFiniteElementSpace *hatp_fes = new ParFiniteElementSpace(&pmesh,hatp_fec);
// H^-1/2 space for û
FiniteElementCollection * hatu_fec = new RT_Trace_FECollection(order-1,dim);
ParFiniteElementSpace *hatu_fes = new ParFiniteElementSpace(&pmesh,hatu_fec);
// testspace fe collections
int test_order = order+delta_order;
FiniteElementCollection * q_fec = new H1_FECollection(test_order, dim);
FiniteElementCollection * v_fec = new RT_FECollection(test_order-1, dim);
// if (myid == 0)
// {
// mfem::out << "p_fes space true dofs = " << p_fes->GetTrueVSize() << endl;
// mfem::out << "u_fes space true dofs = " << u_fes->GetTrueVSize() << endl;
// mfem::out << "hatp_fes space true dofs = " << hatp_fes->GetTrueVSize() << endl;
// mfem::out << "hatu_fes space true dofs = " << hatu_fes->GetTrueVSize() << endl;
// }
// Coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient zero(0.0);
Vector vec0(dim); vec0 = 0.;
VectorConstantCoefficient vzero(vec0);
ConstantCoefficient negone(-1.0);
ConstantCoefficient omeg(omega);
ConstantCoefficient omeg2(omega*omega);
ConstantCoefficient negomeg(-omega);
// Normal equation weak formulation
Array<ParFiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(p_fes);
trial_fes.Append(u_fes);
trial_fes.Append(hatp_fes);
trial_fes.Append(hatu_fes);
test_fec.Append(q_fec);
test_fec.Append(v_fec);
ComplexParNormalEquations * a = new ComplexParNormalEquations(trial_fes,test_fec);
a->StoreMatrices();
// i ω (p,q)
a->AddTrialIntegrator(nullptr,new MixedScalarMassIntegrator(omeg),0,0);
// -(u , ∇ q)
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(negone)),nullptr,1,0);
// -(p, ∇⋅v)
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(one),nullptr,0,1);
// i ω (u,v)
a->AddTrialIntegrator(nullptr,new TransposeIntegrator(new VectorFEMassIntegrator(omeg)),1,1);
// < p̂, v⋅n>
a->AddTrialIntegrator(new NormalTraceIntegrator,nullptr,2,1);
// < û,q >
a->AddTrialIntegrator(new TraceIntegrator,nullptr,3,0);
// test integrators
//space-induced norm for H(div) × H1
// (∇q,∇δq)
a->AddTestIntegrator(new DiffusionIntegrator(one),nullptr,0,0);
// (q,δq)
a->AddTestIntegrator(new MassIntegrator(one),nullptr,0,0);
// (∇⋅v,∇⋅δv)
a->AddTestIntegrator(new DivDivIntegrator(one),nullptr,1,1);
// (v,δv)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,1,1);
// additional integrators for the adjoint graph norm
if (adjoint_graph_norm)
{
// -i ω (∇q,δv)
a->AddTestIntegrator(nullptr,new MixedVectorGradientIntegrator(negomeg),0,1);
// i ω (v,∇ δq)
a->AddTestIntegrator(nullptr,new MixedVectorWeakDivergenceIntegrator(negomeg),1,0);
// ω^2 (v,δv)
a->AddTestIntegrator(new VectorFEMassIntegrator(omeg2),nullptr,1,1);
// - i ω (∇⋅v,δq)
a->AddTestIntegrator(nullptr,new VectorFEDivergenceIntegrator(negomeg),1,0);
// i ω (q,∇⋅v)
a->AddTestIntegrator(nullptr,new MixedScalarWeakGradientIntegrator(negomeg),0,1);
// ω^2 (q,δq)
a->AddTestIntegrator(new MassIntegrator(omeg2),nullptr,0,0);
}
// RHS
FunctionCoefficient f_rhs_r(rhs_func_r);
FunctionCoefficient f_rhs_i(rhs_func_i);
a->AddDomainLFIntegrator(new DomainLFIntegrator(f_rhs_r),new DomainLFIntegrator(f_rhs_i),0);
FunctionCoefficient hatpex_r(hatp_exact_r);
FunctionCoefficient hatpex_i(hatp_exact_i);
VectorFunctionCoefficient hatuex_r(dim,hatu_exact_r);
VectorFunctionCoefficient hatuex_i(dim,hatu_exact_i);
Array<int> elements_to_refine;
socketstream p_out_r;
socketstream p_out_i;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
p_out_r.open(vishost, visport);
p_out_i.open(vishost, visport);
}
double res0 = 0.;
double err0 = 0.;
int dof0;
if (myid == 0)
{
mfem::out << "\n Ref |"
<< " Mesh |"
<< " Dofs |"
<< " ω |"
<< " L2 Error |"
<< " Relative % |"
<< " Rate |"
<< " Residual |"
<< " Rate |"
<< " PCG it |"
<< " PCG time |" << endl;
mfem::out << " --------------------"
<< "---------------------"
<< "---------------------"
<< "---------------------"
<< "---------------------"
<< "-------------------" << endl;
}
for (int it = 0; it<pr; it++)
{
if (static_cond) { a->EnableStaticCondensation(); }
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;
// ess_bdr[1] = 0;
// ess_bdr[2] = 0;
hatp_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// hatu_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// shift the ess_tdofs
for (int j = 0; j < ess_tdof_list.Size(); j++)
{
ess_tdof_list[j] += p_fes->GetTrueVSize() + u_fes->GetTrueVSize();
// + hatp_fes->GetTrueVSize();
}
Array<int> offsets(5);
offsets[0] = 0;
offsets[1] = p_fes->GetVSize();
offsets[2] = u_fes->GetVSize();
offsets[3] = hatp_fes->GetVSize();
offsets[4] = hatu_fes->GetVSize();
offsets.PartialSum();
Vector x(2*offsets.Last());
x = 0.;
double * xdata = x.GetData();
ParComplexGridFunction hatp_gf(hatp_fes);
hatp_gf.real().MakeRef(hatp_fes,&xdata[offsets[2]]);
hatp_gf.imag().MakeRef(hatp_fes,&xdata[offsets.Last()+ offsets[2]]);
hatp_gf.ProjectBdrCoefficient(hatpex_r,hatpex_i, ess_bdr);
// ParComplexGridFunction hatu_gf(hatu_fes);
// hatu_gf.real().MakeRef(hatu_fes,&xdata[offsets[3]]);
// hatu_gf.imag().MakeRef(hatu_fes,&xdata[offsets.Last()+ offsets[3]]);
// hatu_gf.ProjectCoefficientNormal(hatuex_r,hatuex_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;
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.;
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(tdof_offsets);
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));
// amg->SetCycleNumSweeps(5, 5);
solver_hatp->SetPrintLevel(0);
HypreSolver * solver_hatu = nullptr;
if (dim == 2)
{
solver_hatu = new HypreAMS((HypreParMatrix &)BlockA_r->GetBlock(skip+1,skip+1),hatu_fes);
dynamic_cast<HypreAMS*>(solver_hatu)->SetPrintLevel(0);
}
else
{
solver_hatu = new HypreADS((HypreParMatrix &)BlockA_r->GetBlock(skip+1,skip+1), hatu_fes);
dynamic_cast<HypreAMS*>(solver_hatu)->SetPrintLevel(0);
}
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);
StopWatch chrono;
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-7);
cg.SetAbsTol(1e-7);
cg.SetMaxIter(10000);
cg.SetPrintLevel(0);
cg.SetPreconditioner(*M);
cg.SetOperator(blockA);
chrono.Clear();
chrono.Start();
cg.Mult(B, X);
chrono.Stop();
delete M;
int ne = pmesh.GetNE();
MPI_Allreduce(MPI_IN_PLACE,&ne,1,MPI_INT,MPI_SUM,MPI_COMM_WORLD);
int ne_x = (dim == 2) ? (int)sqrt(ne) : (int)cbrt(ne);
ostringstream oss;
double pcg_time = chrono.RealTime();
if (myid == 0)
{
if (dim == 2)
{
oss << ne_x << " x " << ne_x ;
}
else
{
oss << ne_x << " x " << ne_x << " x " << ne_x ;
}
}
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);
elements_to_refine.SetSize(0);
for (int iel = 0; iel<pmesh.GetNE(); iel++)
{
if (residuals[iel] > theta * maxresidual)
{
elements_to_refine.Append(iel);
}
}
ParComplexGridFunction p(p_fes);
p.real().MakeRef(p_fes,x.GetData());
p.imag().MakeRef(p_fes,&x.GetData()[offsets.Last()]);
ParComplexGridFunction u(u_fes);
u.real().MakeRef(u_fes,&x.GetData()[offsets[1]]);
u.imag().MakeRef(u_fes,&x.GetData()[offsets.Last()+offsets[1]]);
// Error in pressure
ParComplexGridFunction pgf_ex(p_fes);
FunctionCoefficient p_ex_r(p_exact_r);
FunctionCoefficient p_ex_i(p_exact_i);
pgf_ex.ProjectCoefficient(p_ex_r, p_ex_i);
double p_err_r = p.real().ComputeL2Error(p_ex_r);
double p_err_i = p.imag().ComputeL2Error(p_ex_i);
double p_error = sqrt(p_err_r*p_err_r + p_err_i*p_err_i);
double p_norm_r = pgf_ex.real().ComputeL2Error(zero);
double p_norm_i = pgf_ex.imag().ComputeL2Error(zero);
double p_norm = sqrt(p_norm_r*p_norm_r + p_norm_i*p_norm_i);
// Error in velocity
ParComplexGridFunction ugf_ex(u_fes);
VectorFunctionCoefficient u_ex_r(dim,u_exact_r);
VectorFunctionCoefficient u_ex_i(dim,u_exact_i);
double u_err_r = u.real().ComputeL2Error(u_ex_r);
double u_err_i = u.imag().ComputeL2Error(u_ex_i);
double u_error = sqrt(u_err_r*u_err_r + u_err_i*u_err_i);
double u_norm_r = pgf_ex.real().ComputeL2Error(vzero);
double u_norm_i = pgf_ex.imag().ComputeL2Error(vzero);
double u_norm = sqrt(u_norm_r*u_norm_r + u_norm_i*u_norm_i);
double L2Error = sqrt(p_error*p_error + u_error*u_error);
double L2norm = sqrt(p_norm*p_norm + u_norm*u_norm);
double rel_err = L2Error/L2norm;
int dofs = p_fes->GlobalTrueVSize()
+ u_fes->GlobalTrueVSize()
+ hatp_fes->GlobalTrueVSize()
+ hatu_fes->GlobalTrueVSize();
double rate_err = (it) ? dim*log(err0/rel_err)/log((double)dof0/dofs) : 0.0;
double rate_res = (it) ? dim*log(res0/globalresidual)/log((double)dof0/dofs) : 0.0;
err0 = rel_err;
res0 = globalresidual;
dof0 = dofs;
if (myid == 0)
{
mfem::out << std::right << std::setw(5) << it << " | "
<< std::setw(16) << oss.str() << " | "
<< std::setw(10) << dof0 << " | "
<< std::setprecision(0) << std::fixed
<< std::setw(2) << 2*rnum << " π | "
<< std::setprecision(3)
<< std::setw(10) << std::scientific << err0 << " | "
<< std::setprecision(3)
<< std::setw(10) << std::fixed << rel_err * 100. << " | "
<< 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 << num_iter << " | "
<< std::setprecision(5)
<< std::setw(8) << std::fixed << pcg_time << " | "
<< std::scientific
<< std::endl;
}
if (visualization)
{
p_out_r << "parallel " << num_procs << " " << myid << "\n";
p_out_r.precision(8);
p_out_r << "solution\n" << pmesh << p.real() <<
"window_title 'Real Numerical presure' "
<< flush;
p_out_i << "parallel " << num_procs << " " << myid << "\n";
p_out_i.precision(8);
p_out_i << "solution\n" << pmesh << p.imag() <<
"window_title 'Imag Numerical presure' "
<< flush;
}
if (it == pr)
break;
pmesh.GeneralRefinement(elements_to_refine,1,1);
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
}
delete a;
delete q_fec;
delete v_fec;
delete hatp_fes;
delete hatp_fec;
delete hatu_fes;
delete hatu_fec;
delete u_fec;
delete p_fec;
delete u_fes;
delete p_fes;
return 0;
}
double p_exact_r(const Vector &x)
{
double p,d2p;
Vector dp;
acoustics_solution_r(x,p,dp,d2p);
return p;
}
double p_exact_i(const Vector &x)
{
double p,d2p;
Vector dp;
acoustics_solution_i(x,p,dp,d2p);
return p;
}
double hatp_exact_r(const Vector & X)
{
return p_exact_r(X);
}
double hatp_exact_i(const Vector & X)
{
return p_exact_i(X);
}
void gradp_exact_r(const Vector &x, Vector &grad)
{
grad.SetSize(x.Size());
double p,d2p;
acoustics_solution_r(x,p,grad,d2p);
}
void gradp_exact_i(const Vector &x, Vector &grad)
{
grad.SetSize(x.Size());
double p,d2p;
acoustics_solution_i(x,p,grad,d2p);
}
double d2_exact_r(const Vector &x)
{
double p,d2p;
Vector dp;
acoustics_solution_r(x,p,dp,d2p);
return d2p;
}
double d2_exact_i(const Vector &x)
{
double p,d2p;
Vector dp;
acoustics_solution_i(x,p,dp,d2p);
return d2p;
}
// u = - ∇ p / (i ω )
// = i (∇ p_r + i * ∇ p_i) / ω
// = - ∇ p_i / ω + i ∇ p_r / ω
void u_exact_r(const Vector &x, Vector & u)
{
gradp_exact_i(x,u);
u *= -1./omega;
}
void u_exact_i(const Vector &x, Vector & u)
{
gradp_exact_r(x,u);
u *= 1./omega;
}
void hatu_exact_r(const Vector & X, Vector & hatu)
{
u_exact_r(X,hatu);
}
void hatu_exact_i(const Vector & X, Vector & hatu)
{
u_exact_i(X,hatu);
}
// ∇⋅u = i Δ p / ω
// = i (Δ p_r + i * Δ p_i) / ω
// = - Δ p_i / ω + i Δ p_r / ω
double divu_exact_r(const Vector &x)
{
return -d2_exact_i(x)/omega;
}
double divu_exact_i(const Vector &x)
{
return d2_exact_r(x)/omega;
}
// f = ∇⋅u + i ω p
// f_r = ∇⋅u_r - ω p_i
double rhs_func_r(const Vector &x)
{
double p = p_exact_i(x);
double divu = divu_exact_r(x);
return divu - omega * p;
}
// f_i = ∇⋅u_i + ω p_r
double rhs_func_i(const Vector &x)
{
double p = p_exact_r(x);
double divu = divu_exact_i(x);
return divu + omega * p;
}
void acoustics_solution_r(const Vector & X, double & p,
Vector &dp, double & d2p)
{
complex<double> zp, d2zp;
vector<complex<double>> dzp;
acoustics_solution(X,zp,dzp,d2zp);
p = zp.real();
d2p = d2zp.real();
dp.SetSize(X.Size());
for (int i = 0; i<X.Size(); i++)
{
dp[i] = dzp[i].real();
}
}
void acoustics_solution_i(const Vector & X, double & p,
Vector &dp, double & d2p)
{
complex<double> zp, d2zp;
vector<complex<double>> dzp;
acoustics_solution(X,zp,dzp,d2zp);
p = zp.imag();
d2p = d2zp.imag();
dp.SetSize(X.Size());
for (int i = 0; i<X.Size(); i++)
{
dp[i] = dzp[i].imag();
}
}
void acoustics_solution(const Vector & X, complex<double> & p, vector<complex<double>> & dp,
complex<double> & d2p)
{
dp.resize(X.Size());
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();
p = exp(-alpha);
d2p = - dim * beta * beta * p;
for (int i = 0; i<X.Size(); i++)
{
dp[i] = - zi * beta * p;
}
}
break;
default:
{
double rk = omega;
double alpha = 45 * M_PI/180.;
double sina = sin(alpha);
double cosa = cos(alpha);
// shift the origin
double xprim=X(0) + 0.1;
double yprim=X(1) + 0.1;
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;
p = zp;
dp[0] = (zdpdx*dxdxprim + zdpdy*dydxprim);
dp[1] = (zdpdx*dxdyprim + zdpdy*dydyprim);
d2p = (zd2pdxdx*dxdxprim + zd2pdydx*dydxprim)*dxdxprim + (zd2pdxdy*dxdxprim + zd2pdydy*dydxprim)*dydxprim
+ (zd2pdxdx*dxdyprim + zd2pdydx*dydyprim)*dxdyprim + (zd2pdxdy*dxdyprim + zd2pdydy*dydyprim)*dydyprim;
}
break;
}
}
+271
View File
@@ -0,0 +1,271 @@
// MFEM DPG_strong acoustics Example
//
// Compile with: make strong_dpg
//
// Definite/Indefinite Helmholtz
// - Δ p ± ω^2 p = f̃ , in Ω
// p = p_0, on ∂Ω
// First Order System
// ∇ p - ω u = 0, in Ω
// - ∇⋅u ± ω p = f, in Ω
// p = p_0, in ∂Ω
// where f:=f̃/ω
// Strong DPG formulation
// (p,u) ∈ H^1(Ω) × H(div,Ω)
//
// (∇ p, v) - ω (u,v) = 0, in Ω, ∀ v ∈ (L^2)^dim
// -(∇⋅u, q) ± ω (p,q) = (f,q), in Ω, ∀ q ∈ L^2
// p = p_0, in ∂Ω
//
// ------------------------------------
// | | p | u | RHS |
// ------------------------------------
// | q | ± ω (p,q) | -(∇⋅u,q) | (f,q) |
// | | | | |
// | v | (∇ p, v) | -ω (u,v) | |
// where (q,v) ∈ L^2 × (L^2)^dim
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// #define DEFINITE
double p_exact(const Vector &x);
void u_exact(const Vector &x, Vector & u);
double rhs_func(const Vector &x);
void gradp_exact(const Vector &x, Vector &gradu);
double divu_exact(const Vector &x);
double d2_exact(const Vector &x);
int dim;
double omega;
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../../../data/inline-quad.mesh";
int order = 1;
int delta_order = 1;
bool visualization = true;
double rnum=1.0;
int ref = 1;
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(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&delta_order, "-do", "--delta_order",
"Order enrichment for DPG test space.");
args.AddOption(&ref, "-ref", "--serial_ref",
"Number of serial refinements.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
omega = 2.0 * M_PI * rnum;
Mesh mesh(mesh_file, 1, 1);
dim = mesh.Dimension();
for (int i = 0; i < ref; i++ )
{
mesh.UniformRefinement();
}
// Define spaces
// H1 space for p
FiniteElementCollection *p_fec = new H1_FECollection(order, dim);
FiniteElementSpace * p_fes = new FiniteElementSpace(&mesh, p_fec);
// H(div) for u
FiniteElementCollection *u_fec = new RT_FECollection(order-1, dim);
FiniteElementSpace * u_fes = new FiniteElementSpace(&mesh, u_fec);
// testspace fe collections
int test_order = order+delta_order;
FiniteElementCollection * q_fec = new L2_FECollection(test_order-1, dim);
FiniteElementCollection * v_fec = new L2_FECollection(test_order-1, dim);
ConstantCoefficient one(1.0);
ConstantCoefficient negone(-1.0);
ConstantCoefficient omeg(omega);
ConstantCoefficient negomeg(-omega);
// Normal equation weak formulation
Array<FiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(p_fes);
trial_fes.Append(u_fes);
test_fec.Append(q_fec);
test_fec.Append(v_fec);
NormalEquations * a = new NormalEquations(trial_fes,test_fec);
a->SetTestFECollVdim(1,dim);
a->StoreMatrices(true);
// ± ω (p, q)
#ifdef DEFINITE
// ω (p, q)
a->AddTrialIntegrator(new MassIntegrator(omeg),0,0);
#else
// -ω (p, q)
a->AddTrialIntegrator(new MassIntegrator(negomeg),0,0);
#endif
// -(∇⋅u, q)
a->AddTrialIntegrator(new MixedScalarDivergenceIntegrator(negone),1,0);
// -ω (u,v)
a->AddTrialIntegrator(new VectorFEMassIntegrator(negomeg),1,1);
// (∇ p, v)
a->AddTrialIntegrator(new GradientIntegrator(one),0,1);
// (v,δv)
a->AddTestIntegrator(new VectorMassIntegrator(one),1,1);
// (q,δq)
a->AddTestIntegrator(new MassIntegrator(one),0,0);
FunctionCoefficient f_rhs(rhs_func);
a->AddDomainLFIntegrator(new DomainLFIntegrator(f_rhs),0);
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (mesh.bdr_attributes.Size())
{
ess_bdr.SetSize(mesh.bdr_attributes.Max());
ess_bdr = 1;
p_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
FunctionCoefficient p_ex(p_exact);
VectorFunctionCoefficient gradp_ex(dim,gradp_exact);
VectorFunctionCoefficient u_ex(dim,u_exact);
FunctionCoefficient divu_ex(divu_exact);
GridFunction p_gf, u_gf;
GridFunction pex_gf(p_fes);
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = p_fes->GetVSize();
offsets[2] = u_fes->GetVSize();
offsets.PartialSum();
BlockVector x(offsets);
x = 0.0;
p_gf.MakeRef(p_fes,x.GetBlock(0));
p_gf.ProjectBdrCoefficient(p_ex,ess_bdr);
u_gf.MakeRef(u_fes,x.GetBlock(1));
a->Assemble();
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
BlockMatrix * A = Ah.As<BlockMatrix>();
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
M->owns_blocks = 1;
for (int i=0; i<A->NumRowBlocks(); i++)
{
M->SetDiagonalBlock(i,new UMFPackSolver(A->GetBlock(i,i)));
}
CGSolver cg;
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(3);
cg.SetPreconditioner(*M);
cg.SetOperator(*A);
cg.Mult(B, X);
delete M;
a->RecoverFEMSolution(X,x);
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream p_out;
socketstream u_out;
p_out.open(vishost, visport);
u_out.open(vishost, visport);
p_out.precision(8);
p_out << "solution\n" << mesh << p_gf <<
"window_title 'Numerical p' "
<< flush;
u_out.precision(8);
u_out << "solution\n" << mesh << u_gf <<
"window_title 'Numerical flux' "
<< flush;
}
return 0;
}
double rhs_func(const Vector &x)
{
double p = p_exact(x);
double divu = divu_exact(x);
// f = - ∇⋅u ± ω p,
#ifdef DEFINITE
return -divu + omega * p;
#else
return -divu - omega * p;
#endif
}
double p_exact(const Vector &x)
{
return sin(omega*x.Sum());
}
void gradp_exact(const Vector &x, Vector &grad)
{
grad.SetSize(x.Size());
grad = omega * cos(omega * x.Sum());
}
void u_exact(const Vector &x, Vector & u)
{
gradp_exact(x,u);
u *= 1./omega;
}
double divu_exact(const Vector &x)
{
return d2_exact(x)/omega;
}
double d2_exact(const Vector &x)
{
return -dim * omega * omega * sin(omega*x.Sum());
}
+546
View File
@@ -0,0 +1,546 @@
// MFEM Ultraweak DPG acoustics example
//
// Compile with: make uw_dpg
//
// ./uw_dpg -m ../../../data/inline-quad.mesh -rnum 40 -theta 0.7 -prob 1 -graph-norm -ref 40 -o 3
// - Δ p ± ω^2 p = f̃ , in Ω
// p = p_0, on ∂Ω
// First Order System
// ∇ p - ω u = 0, in Ω
// - ∇⋅u ± ω p = f, in Ω
// p = p_0, in ∂Ω
// where f:=f̃/ω
// UW-DPG:
//
// p ∈ L^2(Ω), u ∈ (L^2(Ω))^dim
// p̂ ∈ H^1/2(Ω), û ∈ H^-1/2(Ω)
// -(p, ∇⋅v) - ω (u , v) + < p̂, v⋅n> = 0, ∀ v ∈ H(div,Ω)
// (u , ∇ q) ± ω (p , q) + < û, q > = (f,q) ∀ q ∈ H^1(Ω)
// p̂ = p_0 on ∂Ω
// Note:
// p̂ := p on Γ_h (skeleton)
// û := -u on Γ_h
// -------------------------------------------------------------
// | | p | u | p̂ | û | RHS |
// -------------------------------------------------------------
// | v | -(p, ∇⋅v) | - ω (u,v) | < p̂, v⋅n> | | |
// | | | | | | |
// | q | ± ω (p,q) | (u , ∇ q) | | < û,q > | (f,q) |
// where (q,v) ∈ H^1(Ω) × H(div,Ω)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// #define DEFINITE
void acoustics_solution(const Vector & X, double & p, Vector & dp, double & d2p);
double p_exact(const Vector &x);
void u_exact(const Vector &x, Vector & u);
double rhs_func(const Vector &x);
double divu_exact(const Vector &x);
double hatp_exact(const Vector & X);
void hatu_exact(const Vector & X, Vector & hatu);
int dim;
double omega;
enum prob_type
{
plane_wave,
gaussian_beam
};
prob_type prob;
int main(int argc, char *argv[])
{
const char *mesh_file = "../../../data/inline-quad.mesh";
int order = 1;
int delta_order = 1;
bool visualization = true;
double rnum=1.0;
int ref = 1;
double theta = 0.0;
bool adjoint_graph_norm = false;
int iprob = 0;
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(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&delta_order, "-do", "--delta_order",
"Order enrichment for DPG test space.");
args.AddOption(&theta, "-theta", "--theta",
"Theta parameter for AMR");
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
" 0: plane wave, 1: Gaussian beam");
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
"-no-graph-norm", "--no-adjoint-graph-norm",
"Enable or disable Adjoint Graph Norm on the test space");
args.AddOption(&ref, "-ref", "--serial_ref",
"Number of serial refinements.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
if (iprob > 1) { iprob = 0; }
prob = (prob_type)iprob;
omega = 2.0 * M_PI * rnum;
Mesh mesh(mesh_file, 1, 1);
dim = mesh.Dimension();
// Define spaces
// L2 space for p
FiniteElementCollection *p_fec = new L2_FECollection(order-1,dim);
FiniteElementSpace *p_fes = new FiniteElementSpace(&mesh,p_fec);
// Vector L2 space for u
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
FiniteElementSpace *u_fes = new FiniteElementSpace(&mesh,u_fec, dim);
// H^1/2 space for p̂
FiniteElementCollection * hatp_fec = new H1_Trace_FECollection(order,dim);
FiniteElementSpace *hatp_fes = new FiniteElementSpace(&mesh,hatp_fec);
// H^-1/2 space for û
FiniteElementCollection * hatu_fec = new RT_Trace_FECollection(order-1,dim);
FiniteElementSpace *hatu_fes = new FiniteElementSpace(&mesh,hatu_fec);
// testspace fe collections
int test_order = order+delta_order;
FiniteElementCollection * q_fec = new H1_FECollection(test_order, dim);
FiniteElementCollection * v_fec = new RT_FECollection(test_order-1, dim);
// Coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient zero(0.0);
Vector vec0(dim); vec0 = 0.;
VectorConstantCoefficient vzero(vec0);
ConstantCoefficient negone(-1.0);
ConstantCoefficient omeg(omega);
ConstantCoefficient omeg2(omega*omega);
ConstantCoefficient negomeg(-omega);
// Normal equation weak formulation
Array<FiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(p_fes);
trial_fes.Append(u_fes);
trial_fes.Append(hatp_fes);
trial_fes.Append(hatu_fes);
test_fec.Append(q_fec);
test_fec.Append(v_fec);
NormalEquations * a = new NormalEquations(trial_fes,test_fec);
a->StoreMatrices(true);
// ± ω (p,q)
#ifdef DEFINITE
a->AddTrialIntegrator(new MixedScalarMassIntegrator(omeg),0,0);
#else
a->AddTrialIntegrator(new MixedScalarMassIntegrator(negomeg),0,0);
#endif
// (u , ∇ q)
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(one)),1,0);
// -(p, ∇⋅v)
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(one),0,1);
// - ω (u,v)
a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(negomeg)),1,1);
// < p̂, v⋅n>
a->AddTrialIntegrator(new NormalTraceIntegrator,2,1);
// < û,q >
a->AddTrialIntegrator(new TraceIntegrator,3,0);
// test integrators
//space-induced norm for H(div) × H1
// (∇q,∇δq)
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
// (q,δq)
a->AddTestIntegrator(new MassIntegrator(one),0,0);
// (∇⋅v,∇⋅δv)
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
// (v,δv)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),1,1);
// additional integrators for the adjoint graph norm
if (adjoint_graph_norm)
{
// -ω (∇q,δv)
a->AddTestIntegrator(new MixedVectorGradientIntegrator(negomeg),0,1);
// -ω (v,δq)
a->AddTestIntegrator(new MixedVectorWeakDivergenceIntegrator(omeg),1,0);
// ω^2 (v,δv)
a->AddTestIntegrator(new VectorFEMassIntegrator(omeg2),1,1);
#ifdef DEFINITE
// - ω (∇⋅v,δq)
a->AddTestIntegrator(new VectorFEDivergenceIntegrator(negomeg),1,0);
// - ω (q,∇⋅v)
a->AddTestIntegrator(new MixedScalarWeakGradientIntegrator(omeg),0,1);
#else
// ω (∇⋅v,δq)
a->AddTestIntegrator(new VectorFEDivergenceIntegrator(omeg),1,0);
// ω (q,∇⋅v)
a->AddTestIntegrator(new MixedScalarWeakGradientIntegrator(negomeg),0,1);
#endif
// ω^2 (q,δq)
a->AddTestIntegrator(new MassIntegrator(omeg2),0,0);
}
// RHS
FunctionCoefficient f_rhs(rhs_func);
a->AddDomainLFIntegrator(new DomainLFIntegrator(f_rhs),0);
FunctionCoefficient hatpex(hatp_exact);
FunctionCoefficient pex(p_exact);
VectorFunctionCoefficient uex(dim,u_exact);
Array<int> elements_to_refine;
GridFunction hatp_gf;
socketstream p_out;
// socketstream u_out;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
p_out.open(vishost, visport);
// u_out.open(vishost, visport);
}
double res0 = 0.;
double err0 = 0.;
int dof0;
mfem::out << " Refinement |"
<< " Dofs |"
<< " L2 Error |"
<< " Relative % |"
<< " Rate |"
<< " Residual |"
<< " Rate |" << endl;
mfem::out << " --------------------"
<< "-------------------"
<< "-------------------"
<< "-------------------" << endl;
for (int i = 0; i<ref; i++)
{
a->Assemble();
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (mesh.bdr_attributes.Size())
{
ess_bdr.SetSize(mesh.bdr_attributes.Max());
ess_bdr = 1;
hatp_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// shift the ess_tdofs
for (int i = 0; i < ess_tdof_list.Size(); i++)
{
ess_tdof_list[i] += p_fes->GetTrueVSize() + u_fes->GetTrueVSize();
}
Array<int> offsets(5);
offsets[0] = 0;
offsets[1] = p_fes->GetVSize();
offsets[2] = u_fes->GetVSize();
offsets[3] = hatp_fes->GetVSize();
offsets[4] = hatu_fes->GetVSize();
offsets.PartialSum();
BlockVector x(offsets);
x = 0.0;
hatp_gf.MakeRef(hatp_fes,x.GetBlock(2));
hatp_gf.ProjectBdrCoefficient(hatpex,ess_bdr);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
BlockMatrix * A = Ah.As<BlockMatrix>();
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
M->owns_blocks = 1;
for (int i=0; i<A->NumRowBlocks(); i++)
{
M->SetDiagonalBlock(i,new UMFPackSolver(A->GetBlock(i,i)));
}
CGSolver cg;
cg.SetRelTol(1e-8);
cg.SetMaxIter(20000);
cg.SetPrintLevel(3);
cg.SetPreconditioner(*M);
cg.SetOperator(*A);
cg.Mult(B, X);
delete M;
a->RecoverFEMSolution(X,x);
Vector & residuals = a->ComputeResidual(x);
double residual = residuals.Norml2();
elements_to_refine.SetSize(0);
double max_resid = residuals.Max();
for (int iel = 0; iel<mesh.GetNE(); iel++)
{
if (residuals[iel] > theta * max_resid)
{
elements_to_refine.Append(iel);
}
}
GridFunction p_gf;
p_gf.MakeRef(p_fes,x.GetBlock(0));
GridFunction u_gf;
u_gf.MakeRef(u_fes,x.GetBlock(1));
GridFunction pex_gf(p_fes);
GridFunction uex_gf(u_fes);
pex_gf.ProjectCoefficient(pex);
uex_gf.ProjectCoefficient(uex);
// Error
int dofs = X.Size();
double p_err = p_gf.ComputeL2Error(pex);
double p_norm = pex_gf.ComputeL2Error(zero);
double u_err = u_gf.ComputeL2Error(uex);
double u_norm = uex_gf.ComputeL2Error(vzero);
double L2Error = sqrt(p_err*p_err + u_err*u_err);
double L2norm = sqrt(p_norm * p_norm + u_norm * u_norm);
double rel_error = L2Error/L2norm;
double rate_err = (i) ? dim*log(err0/L2Error)/log((double)dof0/dofs) : 0.0;
double rate_res = (i) ? dim*log(res0/residual)/log((double)dof0/dofs) : 0.0;
err0 = L2Error;
res0 = residual;
dof0 = dofs;
mfem::out << std::right << std::setw(11) << i << " | "
<< std::setw(10) << dof0 << " | "
<< std::setprecision(3)
<< std::setw(10) << std::scientific << err0 << " | "
<< std::setprecision(3)
<< std::setw(10) << std::fixed << rel_error * 100. << " | "
<< 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::resetiosflags(std::ios::showbase)
<< std::endl;
if (visualization)
{
p_out.precision(8);
p_out << "solution\n" << mesh << p_gf <<
"window_title 'Numerical presure' "
<< flush;
// u_out.precision(8);
// u_out << "solution\n" << mesh << u_gf <<
// "window_title 'Numerical velocity' "
// << flush;
}
if (i == ref)
break;
mesh.GeneralRefinement(elements_to_refine,1,1);
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
}
delete a;
delete q_fec;
delete v_fec;
delete hatp_fes;
delete hatp_fec;
delete hatu_fes;
delete hatu_fec;
delete u_fec;
delete p_fec;
delete u_fes;
delete p_fes;
return 0;
}
double rhs_func(const Vector &x)
{
double p = p_exact(x);
double divu = divu_exact(x);
// f = - ∇⋅u ± ω p,
#ifdef DEFINITE
return -divu + omega * p;
#else
return -divu - omega * p;
#endif
}
double p_exact(const Vector &x)
{
double p, d2p;
Vector dp;
acoustics_solution(x,p,dp,d2p);
return p;
}
void u_exact(const Vector &x, Vector & u)
{
double p, d2p;
acoustics_solution(x,p,u,d2p);
u *= 1./omega;
}
double divu_exact(const Vector &x)
{
double p, d2p;
Vector dp;
acoustics_solution(x,p,dp,d2p);
return d2p/omega;
}
double hatp_exact(const Vector & X)
{
return p_exact(X);
}
void hatu_exact(const Vector & X, Vector & hatu)
{
u_exact(X,hatu);
hatu *= -1.;
}
void acoustics_solution(const Vector & X, double & p, Vector & dp, double & d2p)
{
dp.SetSize(X.Size());
switch (prob)
{
case plane_wave:
{
p = sin(omega*X.Sum());
dp = omega * cos(omega * X.Sum());
d2p = -dim * omega * omega * sin(omega*X.Sum());
}
break;
default:
{
double rk = omega;
double alpha = 45 * M_PI/180.;
double sina = sin(alpha);
double cosa = cos(alpha);
// shift the origin
double xprim=X(0) + 0.1;
double yprim=X(1) + 0.1;
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> zi = complex<double>(0., 1.);
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;
p = zp.real();
dp[0] = (zdpdx*dxdxprim + zdpdy*dydxprim).real();
dp[1] = (zdpdx*dxdyprim + zdpdy*dydyprim).real();
d2p = ( (zd2pdxdx*dxdxprim + zd2pdydx*dydxprim)*dxdxprim + (zd2pdxdy*dxdxprim + zd2pdydy*dydxprim)*dydxprim
+ (zd2pdxdx*dxdyprim + zd2pdydx*dydyprim)*dxdyprim + (zd2pdxdy*dxdyprim + zd2pdydy*dydyprim)*dydyprim ).real();
}
break;
}
}
+525
View File
@@ -0,0 +1,525 @@
// MFEM Ultraweak DPG MPI acoustics (Helmholtz) example
//
// Compile with: make uw_dpgp
//
// - Δ p ± ω^2 p = f̃ , in Ω
// p = p_0, on ∂Ω
//
// First Order System
// ∇ p - ω u = 0, in Ω
// - ∇⋅u ± ω p = f, in Ω
// p = p_0, in ∂Ω
// where f:=f̃/ω
//
// UW-DPG:
//
// p ∈ L^2(Ω), u ∈ (L^2(Ω))^dim
// p̂ ∈ H^1/2(Ω), û ∈ H^-1/2(Ω)
// -(p, ∇⋅v) - ω (u , v) + < p̂, v⋅n> = 0, ∀ v ∈ H(div,Ω)
// (u , ∇ q) ± ω (p , q) + < û, q > = (f,q) ∀ q ∈ H^1(Ω)
// p̂ = p_0 on ∂Ω
// Note:
// p̂ := p on Γ_h (skeleton)
// û := -u on Γ_h
// -------------------------------------------------------------
// | | p | u | p̂ | û | RHS |
// -------------------------------------------------------------
// | v | -(p, ∇⋅v) | - ω (u,v) | < p̂, v⋅n> | | |
// | | | | | | |
// | q | ± ω (p,q) | (u , ∇ q) | | < û,q > | (f,q) |
// where (q,v) ∈ H^1(Ω) × H(div,Ω)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// #define DEFINITE
double p_exact(const Vector &x);
void u_exact(const Vector &x, Vector & u);
double rhs_func(const Vector &x);
void gradp_exact(const Vector &x, Vector &gradu);
double divu_exact(const Vector &x);
double d2_exact(const Vector &x);
double hatp_exact(const Vector & X);
void hatu_exact(const Vector & X, Vector & hatu);
int dim;
double omega;
int main(int argc, char *argv[])
{
Mpi::Init();
int num_procs = Mpi::WorldSize();
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;
bool visualization = true;
double rnum=1.0;
int sr = 0;
int pr = 1;
double theta = 0.0;
bool adjoint_graph_norm = false;
bool static_cond = 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(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&delta_order, "-do", "--delta_order",
"Order enrichment for DPG test space.");
args.AddOption(&theta, "-theta", "--theta",
"Theta parameter for AMR");
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
"-no-graph-norm", "--no-adjoint-graph-norm",
"Enable or disable Adjoint Graph Norm on the 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.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
omega = 2.0 * M_PI * rnum;
Mesh mesh(mesh_file, 1, 1);
for (int i = 0; i<sr; i++)
{
mesh.UniformRefinement();
}
dim = mesh.Dimension();
mesh.EnsureNCMesh();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
// Define spaces
// L2 space for p
FiniteElementCollection *p_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *p_fes = new ParFiniteElementSpace(&pmesh,p_fec);
// Vector L2 space for u
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *u_fes = new ParFiniteElementSpace(&pmesh,u_fec, dim);
// H^1/2 space for p̂
FiniteElementCollection * hatp_fec = new H1_Trace_FECollection(order,dim);
ParFiniteElementSpace *hatp_fes = new ParFiniteElementSpace(&pmesh,hatp_fec);
// H^-1/2 space for û
FiniteElementCollection * hatu_fec = new RT_Trace_FECollection(order-1,dim);
ParFiniteElementSpace *hatu_fes = new ParFiniteElementSpace(&pmesh,hatu_fec);
// testspace fe collections
int test_order = order+delta_order;
FiniteElementCollection * q_fec = new H1_FECollection(test_order, dim);
FiniteElementCollection * v_fec = new RT_FECollection(test_order-1, dim);
Array<ParFiniteElementSpace * > trial_fes;
trial_fes.Append(p_fes);
trial_fes.Append(u_fes);
trial_fes.Append(hatp_fes);
trial_fes.Append(hatu_fes);
Array<FiniteElementCollection * > test_fec;
test_fec.Append(q_fec);
test_fec.Append(v_fec);
ConstantCoefficient one(1.0);
ConstantCoefficient zero(0.0);
Vector vec0(dim); vec0 = 0.;
VectorConstantCoefficient vzero(vec0);
ConstantCoefficient negone(-1.0);
ConstantCoefficient omeg(omega);
ConstantCoefficient omeg2(omega*omega);
ConstantCoefficient negomeg(-omega);
ParNormalEquations * a = new ParNormalEquations(trial_fes,test_fec);
a->StoreMatrices(true);
// Integrators
// ± ω (p,q)
#ifdef DEFINITE
a->AddTrialIntegrator(new MixedScalarMassIntegrator(omeg),0,0);
#else
a->AddTrialIntegrator(new MixedScalarMassIntegrator(negomeg),0,0);
#endif
// (u , ∇ q)
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(one)),1,0);
// -(p, ∇⋅v)
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(one),0,1);
// - ω (u,v)
a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(negomeg)),1,1);
// < p̂, v⋅n>
a->AddTrialIntegrator(new NormalTraceIntegrator,2,1);
// < û,q >
a->AddTrialIntegrator(new TraceIntegrator,3,0);
// test integrators
//space-induced norm for H(div) × H1
// (∇q,∇δq)
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
// (q,δq)
a->AddTestIntegrator(new MassIntegrator(one),0,0);
// (∇⋅v,∇⋅δv)
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
// (v,δv)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),1,1);
// additional integrators for the adjoint graph norm
if (adjoint_graph_norm)
{
// -ω (∇q,δv)
a->AddTestIntegrator(new MixedVectorGradientIntegrator(negomeg),0,1);
// -ω (v,δq)
a->AddTestIntegrator(new MixedVectorWeakDivergenceIntegrator(omeg),1,0);
// ω^2 (v,δv)
a->AddTestIntegrator(new VectorFEMassIntegrator(omeg2),1,1);
#ifdef DEFINITE
// - ω (∇⋅v,δq)
a->AddTestIntegrator(new VectorFEDivergenceIntegrator(negomeg),1,0);
// - ω (q,∇⋅v)
a->AddTestIntegrator(new MixedScalarWeakGradientIntegrator(omeg),0,1);
#else
// ω (∇⋅v,δq)
a->AddTestIntegrator(new VectorFEDivergenceIntegrator(omeg),1,0);
// ω (q,∇⋅v)
a->AddTestIntegrator(new MixedScalarWeakGradientIntegrator(negomeg),0,1);
#endif
// ω^2 (q,δq)
a->AddTestIntegrator(new MassIntegrator(omeg2),0,0);
}
// RHS
FunctionCoefficient f_rhs(rhs_func);
a->AddDomainLFIntegrator(new DomainLFIntegrator(f_rhs),0);
FunctionCoefficient hatpex(hatp_exact);
FunctionCoefficient pex(p_exact);
VectorFunctionCoefficient uex(dim,u_exact);
Array<int> elements_to_refine;
ParGridFunction hatp_gf;
socketstream p_out;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
p_out.open(vishost, visport);
}
double res0 = 0.;
double err0 = 0.;
int dof0;
if (myid == 0)
{
mfem::out << "\n Refinement |"
<< " Dofs |"
<< " ω |"
<< " L2 Error |"
<< " Relative % |"
<< " Rate |"
<< " Residual |"
<< " Rate |"
<< " PCG it |" << endl;
mfem::out << " --------------------"
<< "---------------------"
<< "---------------------"
<< "---------------------"
<< "----------------" << endl;
}
for (int i = 0; i<pr; i++)
{
if (static_cond) { a->EnableStaticCondensation(); }
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;
hatp_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// shift the ess_tdofs
for (int j = 0; j < ess_tdof_list.Size(); j++)
{
ess_tdof_list[j] += p_fes->GetTrueVSize() + u_fes->GetTrueVSize();
}
Array<int> offsets(5);
offsets[0] = 0;
offsets[1] = p_fes->GetVSize();
offsets[2] = u_fes->GetVSize();
offsets[3] = hatp_fes->GetVSize();
offsets[4] = hatu_fes->GetVSize();
offsets.PartialSum();
BlockVector x(offsets);
x = 0.0;
hatp_gf.MakeRef(hatp_fes,x.GetBlock(2));
hatp_gf.ProjectBdrCoefficient(hatpex,ess_bdr);
Vector X,B;
OperatorPtr Ah;
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
BlockOperator * A = Ah.As<BlockOperator>();
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
M->owns_blocks = 1;
int skip = 0;
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);
// amg0->SetRelaxType(16);
// amg1->SetRelaxType(16);
M->SetDiagonalBlock(0,amg0);
M->SetDiagonalBlock(1,amg1);
skip = 2;
}
HypreBoomerAMG * amg2 = new HypreBoomerAMG((HypreParMatrix &)A->GetBlock(skip,skip));
amg2->SetPrintLevel(0);
// amg2->SetRelaxType(16);
M->SetDiagonalBlock(skip,amg2);
HypreSolver * prec;
if (dim == 2)
{
prec = new HypreAMS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatu_fes);
}
else
{
prec = new HypreADS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatu_fes);
}
M->SetDiagonalBlock(skip+1,prec);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-7);
cg.SetMaxIter(20000);
cg.SetPrintLevel(0);
cg.SetPreconditioner(*M);
cg.SetOperator(*A);
cg.Mult(B, X);
int num_iter = cg.GetNumIterations();
delete M;
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);
elements_to_refine.SetSize(0);
for (int iel = 0; iel<pmesh.GetNE(); iel++)
{
if (residuals[iel] > theta * maxresidual)
{
elements_to_refine.Append(iel);
}
}
ParGridFunction p_gf;
p_gf.MakeRef(p_fes,x.GetBlock(0));
ParGridFunction u_gf;
u_gf.MakeRef(u_fes,x.GetBlock(1));
ParGridFunction pex_gf(p_fes);
ParGridFunction uex_gf(u_fes);
pex_gf.ProjectCoefficient(pex);
uex_gf.ProjectCoefficient(uex);
int dofs = p_fes->GlobalTrueVSize()
+ u_fes->GlobalTrueVSize()
+ hatp_fes->GlobalTrueVSize()
+ hatu_fes->GlobalTrueVSize();
double p_err = p_gf.ComputeL2Error(pex);
double p_norm = pex_gf.ComputeL2Error(zero);
double u_err = u_gf.ComputeL2Error(uex);
double u_norm = uex_gf.ComputeL2Error(vzero);
double L2Error = sqrt(p_err*p_err + u_err*u_err);
double L2norm = sqrt(p_norm * p_norm + u_norm * u_norm);
double rel_error = L2Error/L2norm;
double rate_err = (i) ? dim*log(err0/L2Error)/log((double)dof0/dofs) : 0.0;
double rate_res = (i) ? dim*log(res0/globalresidual)/log((double)dof0/dofs) : 0.0;
err0 = L2Error;
res0 = globalresidual;
dof0 = dofs;
std::ios oldState(nullptr);
if (myid == 0)
{
mfem::out << std::right << std::setw(11) << i << " | "
<< std::setw(10) << dof0 << " | "
<< std::setprecision(0) << std::fixed
<< std::setw(2) << 2*rnum << " π | "
<< std::setprecision(3)
<< std::setw(10) << std::scientific << err0 << " | "
<< std::setprecision(3)
<< std::setw(10) << std::fixed << rel_error * 100. << " | "
<< 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 << num_iter << " | "
<< std::setprecision(5)
<< std::scientific
<< std::endl;
}
if (visualization)
{
p_out << "parallel " << num_procs << " " << myid << "\n";
p_out.precision(8);
p_out << "solution\n" << pmesh << p_gf <<
"window_title 'Numerical pressure' "
<< flush;
}
if (i == pr)
break;
pmesh.GeneralRefinement(elements_to_refine,1,1);
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
}
delete a;
delete q_fec;
delete v_fec;
delete hatp_fes;
delete hatp_fec;
delete hatu_fes;
delete hatu_fec;
delete u_fec;
delete p_fec;
delete u_fes;
delete p_fes;
return 0;
}
double rhs_func(const Vector &x)
{
double p = p_exact(x);
double divu = divu_exact(x);
// f = - ∇⋅u ± ω p,
#ifdef DEFINITE
return -divu + omega * p;
#else
return -divu - omega * p;
#endif
}
double p_exact(const Vector &x)
{
return sin(omega*x.Sum());
}
void gradp_exact(const Vector &x, Vector &grad)
{
grad.SetSize(x.Size());
grad = omega * cos(omega * x.Sum());
}
void u_exact(const Vector &x, Vector & u)
{
gradp_exact(x,u);
u *= 1./omega;
}
double divu_exact(const Vector &x)
{
return d2_exact(x)/omega;
}
double d2_exact(const Vector &x)
{
return -dim * omega * omega * sin(omega*x.Sum());
}
double hatp_exact(const Vector & X)
{
return p_exact(X);
}
void hatu_exact(const Vector & X, Vector & hatu)
{
u_exact(X,hatu);
hatu *= -1.;
}
@@ -0,0 +1,59 @@
# Copyright (c) 2010-2022, 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)/examples/dpg_tests/convection-diffusion,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = uw_dpg
PAR_EXAMPLES = uw_dpgp
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean clean-build clean-exec
# Remove built-in rule
%: %.cpp
# Replace the default implicit rule for *.cpp files
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
all: $(EXAMPLES)
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
# Testing: Parallel vs. serial runs
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
%-test-seq: %
@$(call mfem-test,$<,, Serial example)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@@ -0,0 +1,652 @@
// MFEM Ultraweak DPG example
//
// Compile with: make uw_dpg
//
// sample runs
// ./uw_dpg -m ../../../data/inline-quad.mesh -o 3 -ref 10 -test-norm 2 -do 1 -prob 1 -eps 1e-4
// - εΔu + ∇⋅(βu) = f, in Ω
// u = u_0, on ∂Ω
// First Order System
// - ∇⋅σ + ∇⋅(βu) = f, in Ω
// 1/ε σ - ∇u = 0, in Ω
// u = u_0, on ∂Ω
// UW-DPG:
//
// u ∈ L^2(Ω), σ ∈ (L^2(Ω))^dim
// û ∈ H^1/2, σ̂ ∈ H^-1/2
// -(βu , ∇v) + (σ , ∇v) + < f̂ , v > = (f,v), ∀ v ∈ H^1(Ω)
// (u , ∇⋅τ) + 1/ε (σ , τ) + < û , τ⋅n > = 0, ∀ τ ∈ H(div,Ω)
// û = u_0 on ∂Ω
// Note:
// f̂ := βu - σ
// û := -u
// -------------------------------------------------------------
// | | u | σ | û | f̂ | RHS |
// -------------------------------------------------------------
// | v |-(βu , ∇v) | (σ , ∇v) | | < f̂ ,v > | (f,v) |
// | | | | | | |
// | τ | (u ,∇⋅τ) | 1/ε(σ , τ)| <û,τ⋅n> | | 0 |
// where (v,τ) ∈ H^1(Ω_h) × H(div,Ω_h)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
enum prob_type
{
polynomial,
EJ,
general
};
enum test_norm_type
{
standard,
adjoint_graph,
robust
};
prob_type prob;
test_norm_type test_norm;
Vector beta;
double epsilon;
// Function returns the solution u, and gradient du and the Laplacian d2u
void solution(const Vector & x, double & u, Vector & du, double & d2u);
double exact_u(const Vector & X);
void exact_sigma(const Vector & X, Vector & sigma);
double exact_hatu(const Vector & X);
void exact_hatf(const Vector & X, Vector & hatf);
double f_exact(const Vector & X);
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../../../data/inline-quad.mesh";
int order = 1;
int delta_order = 1;
int ref = 1;
bool visualization = true;
int iprob = 0;
int itest_norm = 0;
double theta = 0.7;
epsilon = 1e0;
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(&epsilon, "-eps", "--epsilon",
"Epsilon coefficient");
args.AddOption(&ref, "-ref", "--num_refinements",
"Number of uniform refinements");
args.AddOption(&theta, "-theta", "--theta",
"Theta parameter for AMR");
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
" 0: polynomial, 1: EJ ,2: General");
args.AddOption(&itest_norm, "-test-norm", "--test-norm", "Choice of test norm"
" 0: Standard, 1: Adjoint Graph, 2: Robust");
args.AddOption(&beta, "-beta", "--beta",
"Vector Coefficient beta");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
if (iprob > 2) { iprob = 2; }
prob = (prob_type)iprob;
test_norm = (test_norm_type)itest_norm;
if (prob == prob_type::EJ)
{
mesh_file = "../../../data/inline-quad.mesh";
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
if (beta.Size() == 0)
{
beta.SetSize(dim);
beta[0] = 1.;
beta[1] = 0.;
}
// Define spaces
// L2 space for u
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
FiniteElementSpace *u_fes = new FiniteElementSpace(&mesh,u_fec);
// Vector L2 space for σ
FiniteElementCollection *sigma_fec = new L2_FECollection(order-1,dim);
FiniteElementSpace *sigma_fes = new FiniteElementSpace(&mesh,sigma_fec, dim);
// H^1/2 space for û
FiniteElementCollection * hatu_fec = new H1_Trace_FECollection(order,dim);
FiniteElementSpace *hatu_fes = new FiniteElementSpace(&mesh,hatu_fec);
// H^-1/2 space for σ̂
FiniteElementCollection * hatf_fec = new RT_Trace_FECollection(order-1,dim);
FiniteElementSpace *hatf_fes = new FiniteElementSpace(&mesh,hatf_fec);
// testspace fe collections
int test_order = order+delta_order;
FiniteElementCollection * v_fec = new H1_FECollection(test_order, dim);
FiniteElementCollection * tau_fec = new RT_FECollection(test_order-1, dim);
// Coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient negone(-1.0);
ConstantCoefficient eps(epsilon);
ConstantCoefficient eps1(1./epsilon);
ConstantCoefficient negeps1(-1./epsilon);
ConstantCoefficient eps2(1/(epsilon*epsilon));
ConstantCoefficient negeps(-epsilon);
VectorConstantCoefficient betacoeff(beta);
Vector negbeta = beta;
negbeta.Neg();
ConstantCoefficient zero(0.0);
Vector vec0(dim); vec0 = 0.;
VectorConstantCoefficient vzero(vec0);
DenseMatrix bbt(beta.Size());
MultVVt(beta, bbt);
MatrixConstantCoefficient bbtcoeff(bbt);
VectorConstantCoefficient negbetacoeff(negbeta);
// Normal equation weak formulation
Array<FiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(u_fes);
trial_fes.Append(sigma_fes);
trial_fes.Append(hatu_fes);
trial_fes.Append(hatf_fes);
test_fec.Append(v_fec);
test_fec.Append(tau_fec);
FiniteElementCollection *coeff_fec = new L2_FECollection(0,dim);
FiniteElementSpace *coeff_fes = new FiniteElementSpace(&mesh,coeff_fec);
GridFunction c1_gf, c2_gf;
GridFunctionCoefficient c1_coeff(&c1_gf);
GridFunctionCoefficient c2_coeff(&c2_gf);
NormalEquations * a = new NormalEquations(trial_fes,test_fec);
a->StoreMatrices(true);
//-(βu , ∇v)
a->AddTrialIntegrator(new MixedScalarWeakDivergenceIntegrator(betacoeff),0,0);
// (σ,∇ v)
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(one)),1,0);
// (u ,∇⋅τ)
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(negone),0,1);
// 1/ε (σ,τ)
a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(eps1)),1,1);
// <û,τ⋅n>
a->AddTrialIntegrator(new NormalTraceIntegrator,2,1);
// <f̂ ,v>
a->AddTrialIntegrator(new TraceIntegrator,3,0);
switch (test_norm)
{
case standard:
{
// (∇v,∇δv)
mfem::out << "\n Test norm: Standard" << endl;
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
// (v,δv)
a->AddTestIntegrator(new MassIntegrator(one),0,0);
// (∇⋅τ,∇⋅δτ)
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
// (τ,δτ)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),1,1);
}
break;
case adjoint_graph:
{
mfem::out << "\n Test norm: Adjoint Graph" << endl;
// (∇v,∇δv)
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
// (β⋅∇v, β⋅∇δv)
a->AddTestIntegrator(new DiffusionIntegrator(bbtcoeff), 0,0);
// (v,δv)
a->AddTestIntegrator(new MassIntegrator(one),0,0);
// (∇⋅τ,∇⋅δτ)
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
// (τ,δτ)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),1,1);
// 1/ε^2 (τ,δτ)
a->AddTestIntegrator(new VectorFEMassIntegrator(eps2),1,1);
// 1/ε (∇v, δτ)
a->AddTestIntegrator(new MixedVectorGradientIntegrator(eps1),0,1);
// - (β ⋅ ∇v,∇⋅δτ)
a->AddTestIntegrator(new MixedGradDivIntegrator(betacoeff),0,1);
// 1/ε (τ,∇δv)
a->AddTestIntegrator(new MixedVectorWeakDivergenceIntegrator(negeps1),1,0);
// -(β ∇⋅τ ,∇⋅δv)
a->AddTestIntegrator(new MixedDivGradIntegrator(betacoeff),1,0);
}
break;
default:
{
mfem::out << "\n Test norm: Robust" << endl;
c1_gf.SetSpace(coeff_fes);
c2_gf.SetSpace(coeff_fes);
Array<int> dofs;
for (int i =0; i < mesh.GetNE(); i++)
{
double volume = mesh.GetElementVolume(i);
double c1 = min(epsilon/volume, 1.);
double c2 = min(1./epsilon, 1./volume);
// double c2 = 1.;
coeff_fes->GetElementDofs(i,dofs);
c1_gf.SetSubVector(dofs,c1);
c2_gf.SetSubVector(dofs,c2);
}
// c1 (v,δv)
a->AddTestIntegrator(new MassIntegrator(c1_coeff),0,0);
// ε (∇v,∇δv)
a->AddTestIntegrator(new DiffusionIntegrator(eps),0,0);
// (β⋅∇v, β⋅∇δv)
a->AddTestIntegrator(new DiffusionIntegrator(bbtcoeff), 0,0);
// c2 (τ,δτ)
a->AddTestIntegrator(new VectorFEMassIntegrator(c2_coeff),1,1);
// (∇⋅τ,∇⋅δτ)
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
}
break;
}
FunctionCoefficient f(f_exact);
// if (prob != prob_type::EJ)
// {
a->AddDomainLFIntegrator(new DomainLFIntegrator(f),0);
// }
FunctionCoefficient hatuex(exact_hatu);
VectorFunctionCoefficient hatfex(dim,exact_hatf);
Array<int> elements_to_refine;
FunctionCoefficient uex(exact_u);
VectorFunctionCoefficient sigmaex(dim,exact_sigma);
GridFunction hatu_gf;
GridFunction hatf_gf;
// socketstream uex_out;
socketstream u_out;
// socketstream sigma_out;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
u_out.open(vishost, visport);
// uex_out.open(vishost, visport);
// sigma_out.open(vishost, visport);
}
double res0 = 0.;
double err0 = 0.;
int dof0;
mfem::out << " Refinement |"
<< " Dofs |"
<< " L2 Error |"
<< " Relative % |"
<< " Rate |"
<< " Residual |"
<< " Rate |" << endl;
mfem::out << " --------------------"
<< "-------------------"
<< "-------------------"
<< "-------------------" << endl;
for (int i = 0; i<=ref; i++)
{
a->Assemble();
Array<int> ess_tdof_list_uhat;
Array<int> ess_tdof_list_fhat;
Array<int> ess_bdr_uhat;
Array<int> ess_bdr_fhat;
if (mesh.bdr_attributes.Size())
{
ess_bdr_uhat.SetSize(mesh.bdr_attributes.Max());
ess_bdr_fhat.SetSize(mesh.bdr_attributes.Max());
// ess_bdr_uhat = 1;
// ess_bdr_fhat = 0;
ess_bdr_uhat = 0;
ess_bdr_fhat = 1;
ess_bdr_uhat[1] = 1;
ess_bdr_fhat[1] = 0;
hatu_fes->GetEssentialTrueDofs(ess_bdr_uhat, ess_tdof_list_uhat);
hatf_fes->GetEssentialTrueDofs(ess_bdr_fhat, ess_tdof_list_fhat);
}
// shift the ess_tdofs
int n = ess_tdof_list_uhat.Size();
int m = ess_tdof_list_fhat.Size();
Array<int> ess_tdof_list(n+m);
for (int j = 0; j < n; j++)
{
ess_tdof_list[j] = ess_tdof_list_uhat[j]
+ u_fes->GetTrueVSize()
+ sigma_fes->GetTrueVSize();
}
for (int j = 0; j < m; j++)
{
ess_tdof_list[j+n] = ess_tdof_list_fhat[j]
+ u_fes->GetTrueVSize()
+ sigma_fes->GetTrueVSize()
+ hatu_fes->GetTrueVSize();
}
Array<int> offsets(5);
offsets[0] = 0;
offsets[1] = u_fes->GetVSize();
offsets[2] = sigma_fes->GetVSize();
offsets[3] = hatu_fes->GetVSize();
offsets[4] = hatf_fes->GetVSize();
offsets.PartialSum();
BlockVector x(offsets);
x = 0.0;
hatu_gf.MakeRef(hatu_fes,x.GetBlock(2));
hatf_gf.MakeRef(hatf_fes,x.GetBlock(3));
hatu_gf.ProjectBdrCoefficient(hatuex,ess_bdr_uhat);
hatf_gf.ProjectBdrCoefficientNormal(hatfex,ess_bdr_fhat);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
BlockMatrix * A = Ah.As<BlockMatrix>();
// BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
// M->owns_blocks = 1;
// for (int i=0; i<A->NumRowBlocks(); i++)
// {
// M->SetDiagonalBlock(i,new UMFPackSolver(A->GetBlock(i,i)));
// }
CGSolver cg;
cg.SetRelTol(1e-12);
cg.SetMaxIter(200000);
cg.SetPrintLevel(0);
// cg.SetPreconditioner(*M);
cg.SetOperator(*A);
cg.Mult(B, X);
// delete M;
a->RecoverFEMSolution(X,x);
Vector & residuals = a->ComputeResidual(x);
double residual = residuals.Norml2();
elements_to_refine.SetSize(0);
double max_resid = residuals.Max();
for (int iel = 0; iel<mesh.GetNE(); iel++)
{
if (residuals[iel] > theta * max_resid)
{
elements_to_refine.Append(iel);
}
}
GridFunction uex_gf(u_fes);
uex_gf.ProjectCoefficient(uex);
GridFunction sigmaex_gf(sigma_fes);
sigmaex_gf.ProjectCoefficient(sigmaex);
GridFunction u_gf;
u_gf.MakeRef(u_fes,x.GetBlock(0));
GridFunction sigma_gf;
sigma_gf.MakeRef(sigma_fes,x.GetBlock(1));
int dofs = X.Size();
double u_err = u_gf.ComputeL2Error(uex);
double u_norm = uex_gf.ComputeL2Error(zero);
// mfem::out << "u_err = " << u_err << endl;
double sigma_err = sigma_gf.ComputeL2Error(sigmaex);
double sigma_norm = sigmaex_gf.ComputeL2Error(vzero);
// mfem::out << "sigma_err = " << sigma_err << endl;
double L2Error = sqrt(u_err*u_err + sigma_err*sigma_err);
double L2norm = sqrt(u_norm * u_norm + sigma_norm * sigma_norm);
double rel_error = L2Error/L2norm;
double rate_err = (i) ? dim*log(err0/L2Error)/log((double)dof0/dofs) : 0.0;
double rate_res = (i) ? dim*log(res0/residual)/log((double)dof0/dofs) : 0.0;
err0 = L2Error;
res0 = residual;
dof0 = dofs;
mfem::out << std::right << std::setw(11) << i << " | "
<< std::setw(10) << dof0 << " | "
<< std::setprecision(3)
<< std::setw(10) << std::scientific << err0 << " | "
<< std::setprecision(3)
<< std::setw(10) << std::fixed << rel_error * 100. << " | "
<< 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::resetiosflags(std::ios::showbase)
<< std::endl;
if (visualization)
{
// uex_out.precision(8);
// uex_out << "solution\n" << mesh << uex_gf <<
// "window_title 'Exact u' "
// << flush;
u_out.precision(8);
u_out << "solution\n" << mesh << u_gf <<
"window_title 'Numerical u' "
<< flush;
// sigma_out.precision(8);
// sigma_out << "solution\n" << mesh << sigma_gf <<
// "window_title 'Numerical flux' "
// << flush;
}
if (i == ref)
break;
mesh.GeneralRefinement(elements_to_refine,1,1);
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
if (test_norm == test_norm_type::robust)
{
coeff_fes->Update();
c1_gf.Update();
c2_gf.Update();
Array<int> dofs;
for (int i = 0; i < mesh.GetNE(); i++)
{
double volume = mesh.GetElementVolume(i);
double c1 = min(epsilon/volume, 1.);
double c2 = min(1./epsilon, 1./volume);
// double c2 = 1.;
coeff_fes->GetElementDofs(i,dofs);
c1_gf.SetSubVector(dofs,c1);
c2_gf.SetSubVector(dofs,c2);
}
}
}
delete coeff_fes;
delete coeff_fec;
delete a;
delete tau_fec;
delete v_fec;
delete hatf_fes;
delete hatf_fec;
delete hatu_fes;
delete hatu_fec;
delete sigma_fes;
delete sigma_fec;
delete u_fec;
delete u_fes;
return 0;
}
void solution(const Vector & X, double & u, Vector & du, double & d2u)
{
double x = X[0];
double y = X[1];
double z = 0.;
if (X.Size() == 3) z = X[2];
du.SetSize(X.Size());
du = 0.;
d2u = 0.;
switch(prob)
{
case polynomial:
{
int n=2;
int m=2;
u = pow(x,n)*pow(y,m);
du[0] = n * pow(x,n-1) * pow(y,m);
du[1] = m * pow(x,n) * pow(y,m-1);
d2u = n * (n-1) * pow(x,n-2) * pow(y,m)
+ m * (m-1) * pow(x,n) * pow(y,m-2);
}
break;
case EJ:
{
double alpha = sqrt(1. + 4. * epsilon * epsilon * M_PI * M_PI);
double r1 = (1. + alpha) / (2.*epsilon);
double r2 = (1. - alpha) / (2.*epsilon);
double denom = exp(-r2) - exp(-r1);
double g1 = exp(r2*(x-1.));
double g1_x = r2*g1;
double g1_xx = r2*g1_x;
double g2 = exp(r1*(x-1.));
double g2_x = r1*g2;
double g2_xx = r1*g2_x;
double g = g1-g2;
double g_x = g1_x - g2_x;
double g_xx = g1_xx - g2_xx;
u = g * cos(M_PI * y)/denom;
double u_x = g_x * cos(M_PI * y)/denom;
double u_xx = g_xx * cos(M_PI * y)/denom;
double u_y = -M_PI * g * sin(M_PI*y)/denom;
double u_yy = -M_PI * M_PI * u;
du[0] = u_x;
du[1] = u_y;
d2u = u_xx + u_yy;
}
break;
default:
{
double alpha = M_PI * (x + y + z);
u = sin(alpha);
du.SetSize(X.Size());
for (int i = 0; i<du.Size(); i++)
{
du[i] = M_PI * cos(alpha);
}
d2u = - M_PI*M_PI * u * du.Size();
}
break;
}
}
double exact_u(const Vector & X)
{
double u, d2u;
Vector du;
solution(X,u,du,d2u);
return u;
}
void exact_sigma(const Vector & X, Vector & sigma)
{
double u, d2u;
Vector du;
solution(X,u,du,d2u);
// σ = ε ∇ u
sigma = du;
sigma *= epsilon;
}
double exact_hatu(const Vector & X)
{
return -exact_u(X);
}
void exact_hatf(const Vector & X, Vector & hatf)
{
Vector sigma;
exact_sigma(X,sigma);
double u = exact_u(X);
hatf.SetSize(X.Size());
for (int i = 0; i<hatf.Size(); i++)
{
hatf[i] = beta[i] * u - sigma[i];
}
}
double f_exact(const Vector & X)
{
// f = - εΔu + ∇⋅(βu)
double u, d2u;
Vector du;
solution(X,u,du,d2u);
double s = 0;
for (int i = 0; i<du.Size(); i++)
{
s += beta[i] * du[i];
}
return -epsilon * d2u + s;
}
@@ -0,0 +1,704 @@
// MFEM Ultraweak DPG example
//
// Compile with: make uw_dpgp
//
// sample runs
// mpirun -np 6 ./uw_dpgp -m ../../../data/inline-quad.mesh -o 3 -ref 10 -test-norm 2 -do 1 -prob 1 -eps 1e-4
// - εΔu + ∇⋅(βu) = f, in Ω
// u = u_0, on ∂Ω
// First Order System
// - ∇⋅σ + ∇⋅(βu) = f, in Ω
// 1/ε σ - ∇u = 0, in Ω
// u = u_0, on ∂Ω
// UW-DPG:
//
// u ∈ L^2(Ω), σ ∈ (L^2(Ω))^dim
// û ∈ H^1/2, f̂ ∈ H^-1/2
// -(βu , ∇v) + (σ , ∇v) + < f̂ , v > = (f,v), ∀ v ∈ H^1(Ω)
// (u , ∇⋅τ) + 1/ε (σ , τ) + < û , τ⋅n > = 0, ∀ τ ∈ H(div,Ω)
// û = u_0 on ∂Ω
// Note:
// f̂ := βu - σ
// û := -u
// -------------------------------------------------------------
// | | u | σ | û | f̂ | RHS |
// -------------------------------------------------------------
// | v |-(βu , ∇v) | (σ , ∇v) | | < f̂ ,v > | (f,v) |
// | | | | | | |
// | τ | (u ,∇⋅τ) | 1/ε(σ , τ)| <û,τ⋅n> | | 0 |
// where (v,τ) ∈ H^1(Ω_h) × H(div,Ω_h)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
enum prob_type
{
polynomial,
EJ,
general
};
enum test_norm_type
{
standard,
adjoint_graph,
robust
};
prob_type prob;
test_norm_type test_norm;
Vector beta;
double epsilon;
// Function returns the solution u, and gradient du and the Laplacian d2u
void solution(const Vector & x, double & u, Vector & du, double & d2u);
double exact_u(const Vector & X);
void exact_sigma(const Vector & X, Vector & sigma);
double exact_hatu(const Vector & X);
void exact_hatf(const Vector & X, Vector & hatf);
double f_exact(const Vector & X);
int main(int argc, char *argv[])
{
Mpi::Init();
int num_procs = Mpi::WorldSize();
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 ref = 1;
bool visualization = true;
int iprob = 0;
int itest_norm = 0;
double theta = 0.7;
bool static_cond = false;
epsilon = 1e0;
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(&epsilon, "-eps", "--epsilon",
"Epsilon coefficient");
args.AddOption(&ref, "-ref", "--num_refinements",
"Number of uniform refinements");
args.AddOption(&theta, "-theta", "--theta",
"Theta parameter for AMR");
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
" 0: lshape, 1: General");
args.AddOption(&itest_norm, "-test-norm", "--test-norm", "Choice of test norm"
" 0: Standard, 1: Adjoint Graph, 2: Robust");
args.AddOption(&beta, "-beta", "--beta",
"Vector Coefficient beta");
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;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
if (iprob > 2) { iprob = 2; }
prob = (prob_type)iprob;
test_norm = (test_norm_type)itest_norm;
if (prob == prob_type::EJ)
{
mesh_file = "../../../data/inline-quad.mesh";
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
if (beta.Size() == 0)
{
beta.SetSize(dim);
beta[0] = 1.;
beta[1] = 0.;
}
mesh.EnsureNCMesh();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
// Define spaces
// L2 space for u
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *u_fes = new ParFiniteElementSpace(&pmesh,u_fec);
// Vector L2 space for σ
FiniteElementCollection *sigma_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *sigma_fes = new ParFiniteElementSpace(&pmesh,sigma_fec, dim);
// H^1/2 space for û
FiniteElementCollection * hatu_fec = new H1_Trace_FECollection(order,dim);
ParFiniteElementSpace *hatu_fes = new ParFiniteElementSpace(&pmesh,hatu_fec);
// H^-1/2 space for σ̂
FiniteElementCollection * hatf_fec = new RT_Trace_FECollection(order-1,dim);
ParFiniteElementSpace *hatf_fes = new ParFiniteElementSpace(&pmesh,hatf_fec);
// testspace fe collections
int test_order = order+delta_order;
FiniteElementCollection * v_fec = new H1_FECollection(test_order, dim);
FiniteElementCollection * tau_fec = new RT_FECollection(test_order-1, dim);
// Coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient negone(-1.0);
ConstantCoefficient eps(epsilon);
ConstantCoefficient eps1(1./epsilon);
ConstantCoefficient negeps1(-1./epsilon);
ConstantCoefficient eps2(1/(epsilon*epsilon));
ConstantCoefficient negeps(-epsilon);
VectorConstantCoefficient betacoeff(beta);
Vector negbeta = beta;
negbeta.Neg();
DenseMatrix bbt(beta.Size());
MultVVt(beta, bbt);
MatrixConstantCoefficient bbtcoeff(bbt);
VectorConstantCoefficient negbetacoeff(negbeta);
// Normal equation weak formulation
Array<ParFiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(u_fes);
trial_fes.Append(sigma_fes);
trial_fes.Append(hatu_fes);
trial_fes.Append(hatf_fes);
test_fec.Append(v_fec);
test_fec.Append(tau_fec);
ParNormalEquations * a = new ParNormalEquations(trial_fes,test_fec);
a->StoreMatrices(true);
//-(βu , ∇v)
a->AddTrialIntegrator(new MixedScalarWeakDivergenceIntegrator(betacoeff),0,0);
// (σ,∇ v)
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(one)),1,0);
// (u ,∇⋅τ)
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(negone),0,1);
// 1/ε (σ,τ)
a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(eps1)),1,1);
// <û,τ⋅n>
a->AddTrialIntegrator(new NormalTraceIntegrator,2,1);
// <f̂ ,v>
a->AddTrialIntegrator(new TraceIntegrator,3,0);
FiniteElementCollection *coeff_fec = new L2_FECollection(0,dim);
ParFiniteElementSpace *coeff_fes = new ParFiniteElementSpace(&pmesh,coeff_fec);
ParGridFunction c1_gf, c2_gf;
GridFunctionCoefficient c1_coeff(&c1_gf);
GridFunctionCoefficient c2_coeff(&c2_gf);
switch (test_norm)
{
case standard:
{
if (myid == 0)
{
mfem::out << "\n Test norm: Standard" << endl;
}
// (∇v,∇δv)
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
// (v,δv)
a->AddTestIntegrator(new MassIntegrator(one),0,0);
// (∇⋅τ,∇⋅δτ)
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
// (τ,δτ)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),1,1);
}
break;
case adjoint_graph:
{
if (myid == 0)
{
mfem::out << "\n Test norm: Adjoint Graph" << endl;
}
// (∇v,∇δv)
a->AddTestIntegrator(new DiffusionIntegrator(one),0,0);
// (β⋅∇v, β⋅∇δv)
a->AddTestIntegrator(new DiffusionIntegrator(bbtcoeff), 0,0);
// (v,δv)
a->AddTestIntegrator(new MassIntegrator(one),0,0);
// (∇⋅τ,∇⋅δτ)
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
// (τ,δτ)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),1,1);
// 1/ε^2 (τ,δτ)
a->AddTestIntegrator(new VectorFEMassIntegrator(eps2),1,1);
// 1/ε (∇v, δτ)
a->AddTestIntegrator(new MixedVectorGradientIntegrator(eps1),0,1);
// - (β ⋅ ∇v,∇⋅δτ)
a->AddTestIntegrator(new MixedGradDivIntegrator(betacoeff),0,1);
// 1/ε (τ,∇δv)
a->AddTestIntegrator(new MixedVectorWeakDivergenceIntegrator(negeps1),1,0);
// -(β ∇⋅τ ,∇⋅δv)
a->AddTestIntegrator(new MixedDivGradIntegrator(betacoeff),1,0);
}
break;
default:
{
if (myid == 0)
{
mfem::out << "\n Test norm: Robust" << endl;
}
c1_gf.SetSpace(coeff_fes);
c2_gf.SetSpace(coeff_fes);
Array<int> dofs;
for (int i =0; i < pmesh.GetNE(); i++)
{
double volume = pmesh.GetElementVolume(i);
double c1 = min(epsilon/volume, 1.);
double c2 = min(1./epsilon, 1./volume);
coeff_fes->GetElementDofs(i,dofs);
c1_gf.SetSubVector(dofs,c1);
c2_gf.SetSubVector(dofs,c2);
}
// c1 (v,δv)
a->AddTestIntegrator(new MassIntegrator(c1_coeff),0,0);
// ε (∇v,∇δv)
a->AddTestIntegrator(new DiffusionIntegrator(eps),0,0);
// (β⋅∇v, β⋅∇δv)
a->AddTestIntegrator(new DiffusionIntegrator(bbtcoeff), 0,0);
// c2 (τ,δτ)
a->AddTestIntegrator(new VectorFEMassIntegrator(c2_coeff),1,1);
// (∇⋅τ,∇⋅δτ)
a->AddTestIntegrator(new DivDivIntegrator(one),1,1);
}
break;
}
FunctionCoefficient f(f_exact);
// if (prob != prob_type::EJ)
// {
a->AddDomainLFIntegrator(new DomainLFIntegrator(f),0);
// }
FunctionCoefficient hatuex(exact_hatu);
VectorFunctionCoefficient hatfex(dim,exact_hatf);
Array<int> elements_to_refine;
FunctionCoefficient uex(exact_u);
VectorFunctionCoefficient sigmaex(dim,exact_sigma);
ParGridFunction hatu_gf;
ParGridFunction hatf_gf;
// socketstream uex_out;
socketstream u_out;
// socketstream sigma_out;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
u_out.open(vishost, visport);
// uex_out.open(vishost, visport);
// sigma_out.open(vishost, visport);
}
double res0 = 0.;
double err0 = 0.;
int dof0;
if (myid == 0)
{
mfem::out << " Refinement |"
<< " Dofs |"
<< " L2 Error |"
<< " Rate |"
<< " Residual |"
<< " Rate |"
<< " CG iter |" << endl;
mfem::out << " --------------------"
<< "-------------------"
<< "-------------------"
<< "-------------------" << endl;
}
for (int i = 0; i<ref; i++)
{
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
Array<int> ess_tdof_list_uhat;
Array<int> ess_tdof_list_fhat;
Array<int> ess_bdr_uhat;
Array<int> ess_bdr_fhat;
if (pmesh.bdr_attributes.Size())
{
ess_bdr_uhat.SetSize(pmesh.bdr_attributes.Max());
ess_bdr_fhat.SetSize(pmesh.bdr_attributes.Max());
// ess_bdr_uhat = 1;
// ess_bdr_fhat = 0;
ess_bdr_uhat = 0;
ess_bdr_fhat = 1;
ess_bdr_uhat[1] = 1;
ess_bdr_fhat[1] = 0;
hatu_fes->GetEssentialTrueDofs(ess_bdr_uhat, ess_tdof_list_uhat);
hatf_fes->GetEssentialTrueDofs(ess_bdr_fhat, ess_tdof_list_fhat);
}
// shift the ess_tdofs
int n = ess_tdof_list_uhat.Size();
int m = ess_tdof_list_fhat.Size();
Array<int> ess_tdof_list(n+m);
for (int j = 0; j < n; j++)
{
ess_tdof_list[j] = ess_tdof_list_uhat[j]
+ u_fes->GetTrueVSize()
+ sigma_fes->GetTrueVSize();
}
for (int j = 0; j < m; j++)
{
ess_tdof_list[j+n] = ess_tdof_list_fhat[j]
+ u_fes->GetTrueVSize()
+ sigma_fes->GetTrueVSize()
+ hatu_fes->GetTrueVSize();
}
Array<int> offsets(5);
offsets[0] = 0;
offsets[1] = u_fes->GetVSize();
offsets[2] = sigma_fes->GetVSize();
offsets[3] = hatu_fes->GetVSize();
offsets[4] = hatf_fes->GetVSize();
offsets.PartialSum();
BlockVector x(offsets);
x = 0.0;
hatu_gf.MakeRef(hatu_fes,x.GetBlock(2));
hatu_gf.ProjectBdrCoefficient(hatuex,ess_bdr_uhat);
hatf_gf.MakeRef(hatf_fes,x.GetBlock(3));
hatf_gf.ProjectBdrCoefficientNormal(hatfex,ess_bdr_fhat);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
BlockOperator * A = Ah.As<BlockOperator>();
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
M->owns_blocks = 1;
int skip = 0;
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)
{
prec = new HypreAMS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatf_fes);
}
else
{
prec = new HypreADS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatf_fes);
}
M->SetDiagonalBlock(skip+1,prec);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-6);
cg.SetMaxIter(200000);
cg.SetPrintLevel(-1);
cg.SetPreconditioner(*M);
cg.SetOperator(*A);
cg.Mult(B, X);
int num_iter = cg.GetNumIterations();
delete M;
a->RecoverFEMSolution(X,x);
Vector & residuals = a->ComputeResidual(x);
double residual = residuals.Norml2();
double maxresidual = residuals.Max();
double gresidual = residual * residual;
MPI_Allreduce(MPI_IN_PLACE,&maxresidual,1,MPI_DOUBLE,MPI_MAX,MPI_COMM_WORLD);
MPI_Allreduce(MPI_IN_PLACE,&gresidual,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
gresidual = sqrt(gresidual);
elements_to_refine.SetSize(0);
for (int iel = 0; iel<pmesh.GetNE(); iel++)
{
if (residuals[iel] > theta * maxresidual)
{
elements_to_refine.Append(iel);
}
}
ParGridFunction u_gf;
u_gf.MakeRef(u_fes,x.GetBlock(0));
ParGridFunction sigma_gf;
sigma_gf.MakeRef(sigma_fes,x.GetBlock(1));
int dofs = u_fes->GlobalTrueVSize()
+ sigma_fes->GlobalTrueVSize()
+ hatu_fes->GlobalTrueVSize()
+ hatf_fes->GlobalTrueVSize();
double u_err = u_gf.ComputeL2Error(uex);
double sigma_err = sigma_gf.ComputeL2Error(sigmaex);
double L2Error = sqrt(u_err*u_err + sigma_err*sigma_err);
double rate_err = (i) ? dim*log(err0/L2Error)/log((double)dof0/dofs) : 0.0;
double rate_res = (i) ? dim*log(res0/gresidual)/log((double)dof0/dofs) : 0.0;
err0 = L2Error;
res0 = gresidual;
dof0 = dofs;
if (myid == 0)
{
mfem::out << std::right << std::setw(11) << i << " | "
<< 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 << num_iter << " | "
<< std::resetiosflags(std::ios::showbase)
<< std::endl;
}
if (visualization)
{
// uex_out.precision(8);
// uex_out << "parallel " << num_procs << " " << myid << "\n";
// uex_out << "solution\n" << pmesh << uex_gf <<
// "window_title 'Exact u' "
// << flush;
u_out << "parallel " << num_procs << " " << myid << "\n";
u_out.precision(8);
u_out << "solution\n" << pmesh << u_gf <<
"window_title 'Numerical u' "
<< flush;
// sigma_out << "parallel " << num_procs << " " << myid << "\n";
// sigma_out.precision(8);
// sigma_out << "solution\n" << pmesh << sigma_gf <<
// "window_title 'Numerical flux' "
// << flush;
}
if (i == ref-1)
break;
pmesh.GeneralRefinement(elements_to_refine,1,1);
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
if (test_norm == test_norm_type::robust)
{
coeff_fes->Update();
c1_gf.Update();
c2_gf.Update();
Array<int> edofs;
for (int i = 0; i < pmesh.GetNE(); i++)
{
double volume = pmesh.GetElementVolume(i);
double c1 = min(epsilon/volume, 1.);
double c2 = min(1./epsilon, 1./volume);
coeff_fes->GetElementDofs(i,edofs);
c1_gf.SetSubVector(edofs,c1);
c2_gf.SetSubVector(edofs,c2);
}
}
}
delete coeff_fes;
delete coeff_fec;
delete a;
delete tau_fec;
delete v_fec;
delete hatf_fes;
delete hatf_fec;
delete hatu_fes;
delete hatu_fec;
delete sigma_fec;
delete sigma_fes;
delete u_fec;
delete u_fes;
return 0;
}
void solution(const Vector & X, double & u, Vector & du, double & d2u)
{
double x = X[0];
double y = X[1];
double z = 0.;
if (X.Size() == 3) z = X[2];
du.SetSize(X.Size());
du = 0.;
d2u = 0.;
switch(prob)
{
case polynomial:
{
int n=2;
int m=2;
u = pow(x,n)*pow(y,m);
du[0] = n * pow(x,n-1) * pow(y,m);
du[1] = m * pow(x,n) * pow(y,m-1);
d2u = n * (n-1) * pow(x,n-2) * pow(y,m)
+ m * (m-1) * pow(x,n) * pow(y,m-2);
}
break;
case EJ:
{
double alpha = sqrt(1. + 4. * epsilon * epsilon * M_PI * M_PI);
double r1 = (1. + alpha) / (2.*epsilon);
double r2 = (1. - alpha) / (2.*epsilon);
double denom = exp(-r2) - exp(-r1);
double g1 = exp(r2*(x-1.));
double g1_x = r2*g1;
double g1_xx = r2*g1_x;
double g2 = exp(r1*(x-1.));
double g2_x = r1*g2;
double g2_xx = r1*g2_x;
double g = g1-g2;
double g_x = g1_x - g2_x;
double g_xx = g1_xx - g2_xx;
u = g * cos(M_PI * y)/denom;
double u_x = g_x * cos(M_PI * y)/denom;
double u_xx = g_xx * cos(M_PI * y)/denom;
double u_y = -M_PI * g * sin(M_PI*y)/denom;
double u_yy = -M_PI * M_PI * u;
du[0] = u_x;
du[1] = u_y;
d2u = u_xx + u_yy;
}
break;
default:
{
double alpha = M_PI * (x + y + z);
u = sin(alpha);
du.SetSize(X.Size());
for (int i = 0; i<du.Size(); i++)
{
du[i] = M_PI * cos(alpha);
}
d2u = - M_PI*M_PI * u * du.Size();
}
break;
}
}
double exact_u(const Vector & X)
{
double u, d2u;
Vector du;
solution(X,u,du,d2u);
return u;
}
void exact_sigma(const Vector & X, Vector & sigma)
{
double u, d2u;
Vector du;
solution(X,u,du,d2u);
// σ = ε ∇ u
sigma = du;
sigma *= epsilon;
}
double exact_hatu(const Vector & X)
{
return -exact_u(X);
}
void exact_hatf(const Vector & X, Vector & hatf)
{
Vector sigma;
exact_sigma(X,sigma);
double u = exact_u(X);
hatf.SetSize(X.Size());
for (int i = 0; i<hatf.Size(); i++)
{
hatf[i] = beta[i] * u - sigma[i];
}
}
double f_exact(const Vector & X)
{
// f = - εΔu + ∇⋅(βu)
double u, d2u;
Vector du;
solution(X,u,du,d2u);
double s = 0;
for (int i = 0; i<du.Size(); i++)
{
s += beta[i] * du[i];
}
return -epsilon * d2u + s;
}
+203
View File
@@ -0,0 +1,203 @@
// MFEM Fosls 1
//
// Compile with: make blkfosls
//
// - Δ u = f, in Ω
// u = 0, on ∂Ω
// First Order System
// ∇ u - σ = 0, in Ω
// - ∇⋅σ = f, in Ω
// u = 0, in ∂Ω
// FOSLS:
// minimize 1/2(||∇u - σ||^2 + ||∇ ⋅ σ - f||^2)
// -------------------------------------------------
// | | u | σ | RHS |
// -------------------------------------------------
// | v | (∇u,∇v) | -(σ,∇v) | 0 |
// | | | | |
// | τ | -(∇u,τ) | (∇⋅σ, ∇⋅τ) + (σ,τ) | -(f,∇⋅τ ) |
// where (u,τ) ∈ H^1(Ω) × H(div,Ω)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../../../data/inline-quad.mesh";
int order = 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) or -1 for"
" isoparametric space.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
// 3. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
// the same code.
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
// 5. Define a finite element space on the mesh. Here we use continuous
// Lagrange finite elements of the specified order. If order < 1, we
// instead use an isoparametric/isogeometric space.
FiniteElementCollection *fec0 = new H1_FECollection(order, dim);
FiniteElementCollection *fec1 = new RT_FECollection(order-1, dim);
FiniteElementSpace fespace0(&mesh, fec0);
FiniteElementSpace fespace1(&mesh, fec1);
Array<FiniteElementSpace *> fespaces(2);
fespaces[0] = &fespace0;
fespaces[1] = &fespace1;
Array<int> ess_bdr;
Array<int> ess_tdof_list;
if (mesh.bdr_attributes.Size())
{
ess_bdr.SetSize(mesh.bdr_attributes.Max());
ess_bdr = 1;
fespaces[0]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
BlockBilinearForm a(fespaces);
a.SetDiagonalPolicy(mfem::Operator::DIAG_KEEP);
cout << "H1 fespace = " << fespace0.GetVSize() << endl;
cout << "RT fespace = " << fespace1.GetVSize() << endl;
FiniteElementCollection *fec2 = new RT_Trace_FECollection(order-1, dim);
FiniteElementSpace RT_trace_fes(&mesh, fec2);
cout << "RT trace = " << RT_trace_fes.GetVSize() << endl;
// for (int i = 0; i<mesh.GetNE(); i++)
// {
// // const FiniteElement * fe = fespace1.GetFE(i);
// // fespace1.GetTraceElement()
// Array<int> faces, ori;
// mesh.GetElementEdges(i, faces, ori);
// for (int f = 0; f<faces.Size(); f++)
// {
// const FiniteElement * fe_trace = RT_trace_fes.GetFaceElement(faces[f]);
// cout << fe_trace->GetDof() << endl;
// Array<int> face_dofs;
// RT_trace_fes.GetFaceDofs(faces[f],face_dofs);
// cout << "face dofs = " << endl;
// face_dofs.Print();
// }
// // cout << fe->GetGeomType() << endl;
// Array<int> vdofs;
// RT_trace_fes.GetElementVDofs(i, vdofs);
// cout << "trace dofs = " << endl;
// vdofs.Print();
// fespace1.GetElementVDofs(i, vdofs);
// cout << "elem dofs = " << endl;
// vdofs.Print();
// cin.get();
// }
ConstantCoefficient one(1.0);
ConstantCoefficient negone(-1.0);
Array2D<BilinearFormIntegrator * > blfi(2,2);
blfi(0,0) = new DiffusionIntegrator(one);
blfi(0,1) = new MixedVectorWeakDivergenceIntegrator(one);
blfi(1,0) = new MixedVectorGradientIntegrator(negone);
BilinearFormIntegrator * divdiv = new DivDivIntegrator(one);
BilinearFormIntegrator * mass = new VectorFEMassIntegrator(one);
SumIntegrator * suminteg = new SumIntegrator();
suminteg->AddIntegrator(divdiv);
suminteg->AddIntegrator(mass);
blfi(1,1) = suminteg;
TestBlockBilinearFormIntegrator * integ = new TestBlockBilinearFormIntegrator();
integ->SetIntegrators(blfi);
a.AddDomainIntegrator(integ);
a.Assemble();
BlockLinearForm b(fespaces);
TestBlockLinearFormIntegrator * lininteg = new TestBlockLinearFormIntegrator();
Array<LinearFormIntegrator * > lfi(2);
lfi[0] = nullptr;
lfi[1] = new VectorFEDomainLFDivIntegrator(negone);
lininteg->SetIntegrators(lfi);
b.AddDomainIntegrator(lininteg);
b.Assemble();
// need to implement blkgridfunction later but for now Vector would do
int size = 0;
for (int i = 0; i<fespaces.Size(); i++)
{
size += fespaces[i]->GetVSize();
}
Vector x(size);
x = 0.0;
OperatorPtr A;
Vector X,B;
a.FormLinearSystem(ess_tdof_list,x,b,A,X,B);
GSSmoother M((SparseMatrix&)(*A));
CGSolver cg;
cg.SetRelTol(1e-6);
cg.SetMaxIter(200);
cg.SetPrintLevel(1);
cg.SetPreconditioner(M);
cg.SetOperator(*A);
cg.Mult(B, X);
a.RecoverFEMSolution(X,b,x);
GridFunction u_gf, sigma_gf;
double *data = x.GetData();
u_gf.MakeRef(fespaces[0],&data[0]);
sigma_gf.MakeRef(fespaces[1],&data[fespaces[0]->GetVSize()]);
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream solu_sock(vishost, visport);
solu_sock.precision(8);
solu_sock << "solution\n" << mesh << u_gf <<
"window_title 'Numerical u' "
<< flush;
socketstream sols_sock(vishost, visport);
sols_sock.precision(8);
sols_sock << "solution\n" << mesh << sigma_gf <<
"window_title 'Numerical sigma' "
<< flush;
}
delete fec0;
return 0;
}
+223
View File
@@ -0,0 +1,223 @@
// MFEM Fosls example
//
// Compile with: make fosls
//
// - Δ u = f, in Ω
// u = 0, on ∂Ω
// First Order System
// ∇ u - σ = 0, in Ω
// - ∇⋅σ = f, in Ω
// u = 0, in ∂Ω
// FOSLS:
// minimize 1/2(||∇u - σ||^2 + ||∇ ⋅ σ - f||^2)
// -------------------------------------------------
// | | u | σ | RHS |
// -------------------------------------------------
// | v | (∇u,∇v) | -(σ,∇v) | 0 |
// | | | | |
// | τ | -(∇u,τ) | (∇⋅σ, ∇⋅τ) + (σ,τ) | -(f,∇⋅τ ) |
// where (u,τ) ∈ H^1(Ω) × H(div,Ω)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../../../data/inline-quad.mesh";
int order = 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) or -1 for"
" isoparametric space.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
// 3. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
// the same code.
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
FiniteElementCollection *H1fec = new H1_FECollection(order,dim);
FiniteElementSpace *H1fes = new FiniteElementSpace(&mesh, H1fec);
FiniteElementCollection *RTfec = new RT_FECollection(order-1,dim);
FiniteElementSpace *RTfes = new FiniteElementSpace(&mesh, RTfec);
// Coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient negone(-1.0);
// Linear forms
LinearForm b_0(H1fes);
// (f,∇⋅τ )
LinearForm b_1(RTfes);
b_1.AddDomainIntegrator(new VectorFEDomainLFDivIntegrator(negone));
// Bilinear forms
// (∇u,∇v)
BilinearForm a_00(H1fes);
a_00.AddDomainIntegrator(new DiffusionIntegrator(one));
// -(σ,∇v)
MixedBilinearForm a_01(RTfes, H1fes);
a_01.AddDomainIntegrator(new MixedVectorWeakDivergenceIntegrator(
one)); // (-1 is included)
// // -(∇u,τ)
// MixedBilinearForm()
MixedBilinearForm a_10(H1fes, RTfes);
a_10.AddDomainIntegrator(new MixedVectorGradientIntegrator(negone));
// (∇⋅σ, ∇⋅τ) + (σ,τ)
BilinearForm a_11(RTfes);
a_11.AddDomainIntegrator(new DivDivIntegrator(one));
a_11.AddDomainIntegrator(new VectorFEMassIntegrator(one));
Array<int> ess_bdr;
Array<int> ess_tdof_list;
if (mesh.bdr_attributes.Size())
{
ess_bdr.SetSize(mesh.bdr_attributes.Max());
ess_bdr = 1;
H1fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
Array<int> block_Toffsets(3);
block_Toffsets[0] = 0;
block_Toffsets[1] = H1fes->GetTrueVSize();
block_Toffsets[2] = RTfes->GetTrueVSize();
block_Toffsets.PartialSum();
Vector rhs_H1(H1fes->GetVSize()); rhs_H1 = 0.;
Vector rhs_RT(RTfes->GetVSize()); rhs_RT = 0.;
Vector x_H1(H1fes->GetVSize()); x_H1 = 0.;
Vector x_RT(RTfes->GetVSize()); x_RT = 0.;
Vector RHS_H1(H1fes->GetTrueVSize()); RHS_H1 = 0.0;
Vector RHS_RT(RTfes->GetTrueVSize()); RHS_RT = 0.0;
Vector X_H1(H1fes->GetTrueVSize()); X_H1 = 0.0;
Vector X_RT(RTfes->GetTrueVSize()); X_RT = 0.0;
b_0.Update(H1fes,rhs_H1,0);
b_0.Assemble();
b_1.Update(RTfes,rhs_RT,0);
b_1.Assemble();
// Assembly and BC
a_00.Assemble();
SparseMatrix A_00;
a_00.FormLinearSystem(ess_tdof_list,x_H1,rhs_H1,
A_00,X_H1,RHS_H1);
a_01.Assemble();
SparseMatrix A_01;
Array<int> empty;
a_01.FormRectangularSystemMatrix(empty, ess_tdof_list,A_01);
a_10.Assemble();
SparseMatrix A_10;
a_10.FormRectangularLinearSystem(ess_tdof_list,empty,x_H1,rhs_RT,
A_10,X_H1,RHS_RT);
a_11.Assemble();
SparseMatrix A_11;
a_11.FormSystemMatrix(empty,A_11);
BlockMatrix BlockA(block_Toffsets);
BlockA.SetBlock(0,0,&A_00);
BlockA.SetBlock(0,1,&A_01);
BlockA.SetBlock(1,0,&A_10);
BlockA.SetBlock(1,1,&A_11);
BlockVector RHS(block_Toffsets);
RHS.GetBlock(0) = RHS_H1;
RHS.GetBlock(1) = RHS_RT;
BlockVector X(block_Toffsets);
X.GetBlock(0) = X_H1;
X.GetBlock(1) = X_RT;
SparseMatrix * A = BlockA.CreateMonolithic();
GSSmoother M(*A);
CGSolver cg;
cg.SetRelTol(1e-6);
cg.SetMaxIter(2000);
cg.SetPrintLevel(1);
cg.SetPreconditioner(M);
cg.SetOperator(*A);
cg.Mult(RHS, X);
GridFunction u_gf(H1fes), sigma_gf(RTfes);
u_gf = 0.;
sigma_gf = 0.;
const SparseMatrix * P = H1fes->GetConformingProlongation();
if (P)
{
a_00.RecoverFEMSolution(X.GetBlock(0),rhs_H1,u_gf);
a_11.RecoverFEMSolution(X.GetBlock(1),rhs_RT,sigma_gf);
}
else
{
u_gf.MakeRef(X.GetBlock(0),0);
sigma_gf.MakeRef(X.GetBlock(1),0);
}
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream solu_sock(vishost, visport);
solu_sock.precision(8);
solu_sock << "solution\n" << mesh << u_gf <<
"window_title 'Numerical u' "
<< flush;
socketstream sols_sock(vishost, visport);
sols_sock.precision(8);
sols_sock << "solution\n" << mesh << sigma_gf <<
"window_title 'Numerical sigma' "
<< flush;
}
return 0;
}
+61
View File
@@ -0,0 +1,61 @@
# Copyright (c) 2010-2022, 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)/examples/dpg_tests/diffusion,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = blkfosls fosls primal_dpg \
uw_dpg
PAR_EXAMPLES = uw_dpgp
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean clean-build clean-exec
# Remove built-in rule
%: %.cpp
# Replace the default implicit rule for *.cpp files
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
all: $(EXAMPLES)
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
# Testing: Parallel vs. serial runs
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
%-test-seq: %
@$(call mfem-test,$<,, Serial example)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
rm -rf ParaView
clean-exec:
+179
View File
@@ -0,0 +1,179 @@
// MFEM primal_dpg example
//
// Compile with: make primal_dpg
//
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Parse command line options
const char *mesh_file = "../../../data/star.mesh";
int order = 1;
bool static_cond = false;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&order, "-o", "--order", "Finite element polynomial degree");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.ParseCheck();
// 2. Read the mesh from the given mesh file, and refine once uniformly.
Mesh mesh(mesh_file);
// mesh.UniformRefinement();
// 3. Define a finite element space on the mesh. Here we use H1 continuous
// high-order Lagrange finite elements of the given order.
H1_FECollection fec(order, mesh.Dimension());
FiniteElementSpace H1fes(&mesh, &fec);
RT_Trace_FECollection trace_fec(order-1, mesh.Dimension());
FiniteElementSpace RTtrace_fes(&mesh, &trace_fec);
int dim = mesh.Dimension();
int test_order = order;
if (dim == 2 && (order%2 == 0 || (mesh.MeshGenerator() & 2 && order > 1)))
{
test_order++;
}
test_order++;
H1_FECollection test_fec(test_order,mesh.Dimension());
Array<FiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fecs;
trial_fes.Append(&H1fes);
trial_fes.Append(&RTtrace_fes);
test_fecs.Append(&test_fec);
NormalEquations * a = new NormalEquations(trial_fes,test_fecs);
ConstantCoefficient one(1.0);
a->AddTrialIntegrator(new DiffusionIntegrator(one),0,0);
a->AddTrialIntegrator(new TraceIntegrator,1,0);
BilinearFormIntegrator * diffusion = new DiffusionIntegrator(one);
BilinearFormIntegrator * mass = new MassIntegrator(one);
a->AddTestIntegrator(diffusion,0,0);
a->AddTestIntegrator(mass,0,0);
a->AddDomainLFIntegrator(new DomainLFIntegrator(one),0);
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
Array<int> ess_tdof_list;
if (mesh.bdr_attributes.Size())
{
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 1;
H1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
Vector X,B;
OperatorPtr Ah;
int size = H1fes.GetVSize() + RTtrace_fes.GetVSize();
Vector x(size);
x = 0.0;
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
BlockMatrix * A = (BlockMatrix *)(Ah.Ptr());
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
M->owns_blocks = 1;
for (int i=0; i<A->NumRowBlocks(); i++)
{
M->SetDiagonalBlock(i,new UMFPackSolver(A->GetBlock(i,i)));
}
CGSolver cg;
cg.SetRelTol(1e-6);
cg.SetMaxIter(2000);
cg.SetPrintLevel(3);
cg.SetPreconditioner(*M);
cg.SetOperator(*A);
cg.Mult(B, X);
delete M;
a->RecoverFEMSolution(X,x);
GridFunction u_gf;
double *data = x.GetData();
u_gf.MakeRef(&H1fes,data);
GridFunction s_gf;
s_gf.MakeRef(&RTtrace_fes,&data[H1fes.GetVSize()]);
RT_FECollection RTfec(order-1, mesh.Dimension());
FiniteElementSpace RTfes(&mesh, &RTfec);
GridFunction sigma_gf(&RTfes);
sigma_gf = 0.0;
for (int i = 0; i<mesh.GetNE(); i++)
{
Array<int> strace_dofs;
Array<int> trace_dofs;
Vector dofs;
RTtrace_fes.GetElementDofs(i,trace_dofs);
strace_dofs.SetSize(trace_dofs.Size());
// shift dofs;
for (int j = 0; j< trace_dofs.Size(); j++)
{
int offset = trace_dofs[j] < 0 ? -H1fes.GetVSize() : H1fes.GetVSize();
strace_dofs[j] = offset + trace_dofs[j];
}
x.GetSubVector(strace_dofs, dofs);
sigma_gf.SetSubVector(trace_dofs,dofs);
}
ParaViewDataCollection paraview_dc("DPG_example", &mesh);
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("field",&u_gf);
paraview_dc.RegisterField("flux",&sigma_gf);
// paraview_dc.RegisterField("flux",&s_gf);
paraview_dc.Save();
char vishost[] = "localhost";
int visport = 19916;
socketstream solu_sock(vishost, visport);
solu_sock.precision(8);
solu_sock << "solution\n" << mesh << u_gf <<
"window_title 'Numerical u' "
<< flush;
socketstream soltrace_sock(vishost, visport);
soltrace_sock.precision(8);
soltrace_sock << "solution\n" << mesh << sigma_gf <<
"window_title 'Flux sigma_n' "
<< flush;
}
+403
View File
@@ -0,0 +1,403 @@
// MFEM Ultraweak DPG example
//
// Compile with: make uw_dpg
//
// sample runs
// ./uw_dpg -m ../lshape2.mesh -o 2 -ref 20 -graph-norm -do 1 -prob 0
// - Δ u = f, in Ω
// u = u_0, on ∂Ω
// First Order System
// ∇ u - σ = 0, in Ω
// - ∇⋅σ = f, in Ω
// u = 0, in ∂Ω
// UW-DPG:
//
// u ∈ L^2(Ω), σ ∈ (L^2(Ω))^dim
// û ∈ H^1/2, σ̂ ∈ H^-1/2
// -(u , ∇⋅τ) - (σ , τ) + < û, τ⋅n> = 0, ∀ τ ∈ H(div,Ω)
// (σ , ∇ v) + < σ̂, v > = (f,v) ∀ v ∈ H^1(Ω)
// û = 0 on ∂Ω
// Note:
// û := u
// σ̂ := -σ
// -------------------------------------------------------------
// | | u | σ | û | σ̂ | RHS |
// -------------------------------------------------------------
// | τ | -(u,∇⋅τ) | -(σ,τ) | < û, τ⋅n> | | 0 |
// | | | | | | |
// | v | | (σ,∇ v) | | <σ̂,v> | (f,v) |
// where (τ,v) ∈ H(div,Ω) × H^1(Ω)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
enum prob_type
{
lshape,
general
};
prob_type prob;
void solution(const Vector & X, double & u, Vector & du, double & d2u);
double exact_u(const Vector & X)
{
double u, d2u;
Vector du;
solution(X,u,du,d2u);
return u;
}
void exact_sigma(const Vector & X, Vector & sigma)
{
double u, d2u;
Vector du;
solution(X,u,du,d2u);
// σ = ∇ u
sigma = du;
}
double exact_hatu(const Vector & X)
{
return exact_u(X);
}
void exact_hatsigma(const Vector & X, Vector & hatsigma)
{
exact_sigma(X,hatsigma);
hatsigma *= -1.;
}
double f_exact(const Vector & X)
{
double u, d2u;
Vector du;
solution(X,u,du,d2u);
return -d2u;
}
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../../../data/inline-quad.mesh";
int order = 1;
int delta_order = 1;
int ref = 1;
bool adjoint_graph_norm = false;
bool visualization = true;
int iprob = 0;
bool static_cond = 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",
"Order enrichment for DPG test space.");
args.AddOption(&ref, "-ref", "--num_refinements",
"Number of uniform refinements");
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
"-no-graph-norm", "--no-adjoint-graph-norm",
"Enable or disable Adjoint Graph Norm on the test space");
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
" 0: lshape, 1: General");
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())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
if (iprob > 1) { iprob = 1; }
prob = (prob_type)iprob;
if (prob == prob_type::lshape)
{
mesh_file = "../lshape2.mesh";
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
mesh.UniformRefinement();
// Define spaces
// L2 space for u
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
FiniteElementSpace *u_fes = new FiniteElementSpace(&mesh,u_fec);
// Vector L2 space for σ
FiniteElementCollection *sigma_fec = new L2_FECollection(order-1,dim);
FiniteElementSpace *sigma_fes = new FiniteElementSpace(&mesh,sigma_fec, dim);
// H^1/2 space for û
FiniteElementCollection * hatu_fec = new H1_Trace_FECollection(order,dim);
FiniteElementSpace *hatu_fes = new FiniteElementSpace(&mesh,hatu_fec);
// H^-1/2 space for σ̂
FiniteElementCollection * hatsigma_fec = new RT_Trace_FECollection(order-1,dim);
FiniteElementSpace *hatsigma_fes = new FiniteElementSpace(&mesh,hatsigma_fec);
// testspace fe collections
int test_order = order+delta_order;
FiniteElementCollection * tau_fec = new RT_FECollection(test_order-1, dim);
FiniteElementCollection * v_fec = new H1_FECollection(test_order, dim);
// Coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient negone(-1.0);
// Normal equation weak formulation
Array<FiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(u_fes);
trial_fes.Append(sigma_fes);
trial_fes.Append(hatu_fes);
trial_fes.Append(hatsigma_fes);
test_fec.Append(tau_fec);
test_fec.Append(v_fec);
NormalEquations * a = new NormalEquations(trial_fes,test_fec);
a->StoreMatrices(true);
// -(u,∇⋅τ)
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(one),0,0);
// -(σ,τ)
a->AddTrialIntegrator(new TransposeIntegrator(new VectorFEMassIntegrator(negone)),1,0);
// (σ,∇ v)
a->AddTrialIntegrator(new TransposeIntegrator(new GradientIntegrator(one)),1,1);
// <û,τ⋅n>
a->AddTrialIntegrator(new NormalTraceIntegrator,2,0);
// <σ̂,v>
a->AddTrialIntegrator(new TraceIntegrator,3,1);
// test integrators (space-induced norm for H(div) × H1)
// (∇⋅τ,∇⋅δτ)
a->AddTestIntegrator(new DivDivIntegrator(one),0,0);
// (τ,δτ)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),0,0);
// (∇v,∇δv)
a->AddTestIntegrator(new DiffusionIntegrator(one),1,1);
// (v,δv)
a->AddTestIntegrator(new MassIntegrator(one),1,1);
// additional terms for adjoint graph norm
if (adjoint_graph_norm)
{
// -(∇v,δτ)
a->AddTestIntegrator(new MixedVectorGradientIntegrator(negone),1,0);
// -(τ,∇δv)
a->AddTestIntegrator(new MixedVectorWeakDivergenceIntegrator(one),0,1);
// (τ,δτ)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),0,0);
}
// RHS
FunctionCoefficient f(f_exact);
if (prob == prob_type::general)
{
a->AddDomainLFIntegrator(new DomainLFIntegrator(f),1);
}
FunctionCoefficient hatuex(exact_hatu);
Array<int> elements_to_refine;
GridFunction hatu_gf;
socketstream u_out;
// socketstream sigma_out;
socketstream mesh_out;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
u_out.open(vishost, visport);
// sigma_out.open(vishost, visport);
mesh_out.open(vishost, visport);
}
for (int iref = 0; iref<ref; iref++)
{
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (mesh.bdr_attributes.Size())
{
ess_bdr.SetSize(mesh.bdr_attributes.Max());
ess_bdr = 1;
hatu_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// shift the ess_tdofs
for (int i = 0; i < ess_tdof_list.Size(); i++)
{
ess_tdof_list[i] += u_fes->GetTrueVSize() + sigma_fes->GetTrueVSize();
}
Array<int> offsets(5);
offsets[0] = 0;
offsets[1] = u_fes->GetVSize();
offsets[2] = sigma_fes->GetVSize();
offsets[3] = hatu_fes->GetVSize();
offsets[4] = hatsigma_fes->GetVSize();
offsets.PartialSum();
BlockVector x(offsets);
x = 0.0;
hatu_gf.MakeRef(hatu_fes,x.GetBlock(2));
hatu_gf.ProjectBdrCoefficient(hatuex,ess_bdr);
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
BlockMatrix * A = Ah.As<BlockMatrix>();
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
M->owns_blocks = 1;
for (int i=0; i<A->NumRowBlocks(); i++)
{
M->SetDiagonalBlock(i,new GSSmoother(A->GetBlock(i,i)));
}
CGSolver cg;
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(3);
cg.SetPreconditioner(*M);
cg.SetOperator(*A);
cg.Mult(B, X);
delete M;
a->RecoverFEMSolution(X,x);
Vector & residuals = a->ComputeResidual(x);
double residual = residuals.Norml2();
cout << "Residual = " << residual << endl;
elements_to_refine.SetSize(0);
double max_resid = residuals.Max();
double theta = 0.7;
for (int iel = 0; iel<mesh.GetNE(); iel++)
{
if (residuals[iel] > theta * max_resid)
{
elements_to_refine.Append(iel);
}
}
GridFunction u_gf;
u_gf.MakeRef(u_fes,x.GetBlock(0));
GridFunction sigma_gf;
sigma_gf.MakeRef(sigma_fes,x.GetBlock(1));
if (visualization)
{
u_out.precision(8);
string keys = (iref == 0) ? "keys em\n" : "keys";
u_out << "solution\n" << mesh << u_gf
<< "window_title 'Numerical u' "
<< flush;
// sigma_out.precision(8);
// sigma_out << "solution\n" << mesh << sigma_gf <<
// "window_title 'Numerical flux' "
// << flush;
mesh_out.precision(8);
mesh_out << "mesh\n" << mesh
<< keys
<< "window_title 'Mesh' "
<< flush;
}
mesh.GeneralRefinement(elements_to_refine);
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
}
delete a;
delete tau_fec;
delete v_fec;
delete hatsigma_fes;
delete hatsigma_fec;
delete hatu_fes;
delete hatu_fec;
delete sigma_fec;
delete sigma_fes;
delete u_fec;
delete u_fes;
return 0;
}
void solution(const Vector & X, double & u, Vector & du, double & d2u)
{
double x = X[0];
double y = X[1];
double z = 0.;
if (X.Size() == 3) z = X[2];
du.SetSize(X.Size());
du = 0.;
d2u = 0.;
switch(prob)
{
case lshape:
{
double r = sqrt(x*x + y*y);
double alpha = 2./3.;
double theta = atan2(y,x);
if (theta < 0) theta += 2*M_PI;
u = pow(r,alpha) * sin(alpha * theta);
}
break;
default:
{
double alpha = M_PI * (x + y + z);
u = sin(alpha);
du.SetSize(X.Size());
for (int i = 0; i<du.Size(); i++)
{
du[i] = M_PI * cos(alpha);
}
d2u = - M_PI*M_PI * u * du.Size();
}
break;
}
}
+404
View File
@@ -0,0 +1,404 @@
// MFEM UW DPG parallel example
//
// Compile with: make poisson_fosls
//
// - Δ u = f, in Ω
// u = 0, on ∂Ω
// First Order System
// ∇ u - σ = 0, in Ω
// - ∇⋅σ = f, in Ω
// u = 0, in ∂Ω
// UW-DPG:
//
// u ∈ L^2(Ω), σ ∈ (L^2(Ω))^dim
// û ∈ H^1/2, σ̂ ∈ H^-1/2
// -(u , ∇⋅τ) + < û, τ⋅n> - (σ , τ) = 0, ∀ τ ∈ H(div,Ω)
// (σ , ∇ v) - < σ̂, v > = (f,v) ∀ v ∈ H^1(Ω)
// û = 0 on ∂Ω
// -------------------------------------------------------------
// | | u | σ | û | σ̂ | RHS |
// -------------------------------------------------------------
// | τ | -(u,∇⋅τ) | -(σ,τ) | < û, τ⋅n> | | 0 |
// | | | | | | |
// | v | | (σ,∇ v) | | -<σ̂,v> | (f,v) |
// where (τ,v) ∈ H(div,Ω) × H^1(Ω)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
enum prob_type
{
lshape,
general
};
prob_type prob;
double exact(const Vector & X)
{
double x = X[0];
double y = X[1];
double r = sqrt(x*x + y*y);
double alpha = 2./3.;
double theta = atan2(y,x);
if (theta < 0) theta += 2*M_PI;
return pow(r,alpha) * sin(alpha * theta);
}
void gradexact(const Vector & X, Vector & grad)
{
grad.SetSize(2);
double x = X[0];
double y = X[1];
double r = sqrt(x*x + y*y);
double alpha = 2./3.;
double theta = atan2(y,x);
if (theta < 0) theta += 2*M_PI;
double r_x = x/r;
double r_y = y/r;
double theta_x = - y / (r*r);
double theta_y = x / (r*r);
double beta = alpha * pow(r,alpha - 1.);
grad[0] = beta*(r_x * sin(alpha*theta) + r * theta_x * cos(alpha*theta));
grad[1] = beta*(r_y * sin(alpha*theta) + r * theta_y * cos(alpha*theta));
}
int main(int argc, char *argv[])
{
MPI_Session mpi;
int num_procs = mpi.WorldSize();
int myid = mpi.WorldRank();
// 1. Parse command-line options.
const char *mesh_file = "../../../data/inline-quad.mesh";
int order = 1;
int delta_order = 1;
int ref = 1;
bool adjoint_graph_norm = false;
bool visualization = true;
int iprob = 0;
bool static_cond = false;
double theta = 0.7;
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(&ref, "-ref", "--num_refinements",
"Number of uniform refinements");
args.AddOption(&theta, "-theta", "--theta_factor",
"Refinement factor");
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
"-no-graph-norm", "--no-adjoint-graph-norm",
"Enable or disable Adjoint Graph Norm on the test space");
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
" 0: lshape, 1: General");
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;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
if (iprob > 1) { iprob = 1; }
prob = (prob_type)iprob;
if (prob == prob_type::lshape)
{
mesh_file = "../lshape2.mesh";
}
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
mesh.UniformRefinement();
mesh.EnsureNCMesh();
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
// Define spaces
// L2 space for u
FiniteElementCollection *u_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *u_fes = new ParFiniteElementSpace(&pmesh,u_fec);
// Vector L2 space for σ
FiniteElementCollection *sigma_fec = new L2_FECollection(order-1,dim);
ParFiniteElementSpace *sigma_fes = new ParFiniteElementSpace(&pmesh,sigma_fec, dim);
// H^1/2 space for û
FiniteElementCollection * hatu_fec = new H1_Trace_FECollection(order,dim);
ParFiniteElementSpace *hatu_fes = new ParFiniteElementSpace(&pmesh,hatu_fec);
// H^-1/2 space for σ̂
FiniteElementCollection * hatsigma_fec = new RT_Trace_FECollection(order-1,dim);
ParFiniteElementSpace *hatsigma_fes = new ParFiniteElementSpace(&pmesh,hatsigma_fec);
// testspace fe collections
int test_order = order+delta_order;
FiniteElementCollection * tau_fec = new RT_FECollection(test_order-1, dim);
FiniteElementCollection * v_fec = new H1_FECollection(test_order, dim);
// Coefficients
ConstantCoefficient one(1.0);
ConstantCoefficient negone(-1.0);
// Normal equation weak formulation
Array<ParFiniteElementSpace * > trial_fes;
Array<FiniteElementCollection * > test_fec;
trial_fes.Append(u_fes);
trial_fes.Append(sigma_fes);
trial_fes.Append(hatu_fes);
trial_fes.Append(hatsigma_fes);
test_fec.Append(tau_fec);
test_fec.Append(v_fec);
ParNormalEquations * a = new ParNormalEquations(trial_fes,test_fec);
a->StoreMatrices(true);
// -(u,∇⋅τ)
a->AddTrialIntegrator(new MixedScalarWeakGradientIntegrator(one),0,0);
// -(σ,τ)
TransposeIntegrator * mass = new TransposeIntegrator(new VectorFEMassIntegrator(negone));
a->AddTrialIntegrator(mass,1,0);
// (σ,∇ v)
TransposeIntegrator * grad = new TransposeIntegrator(new GradientIntegrator(one));
a->AddTrialIntegrator(grad,1,1);
// <û,τ⋅n>
a->AddTrialIntegrator(new NormalTraceIntegrator,2,0);
// -<σ̂,v> (sign is included in σ̂)
a->AddTrialIntegrator(new TraceIntegrator,3,1);
// test integrators (space-induced norm for H(div) × H1)
// (∇⋅τ,∇⋅δτ)
a->AddTestIntegrator(new DivDivIntegrator(one),0,0);
// (τ,δτ)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),0,0);
// (∇v,∇δv)
a->AddTestIntegrator(new DiffusionIntegrator(one),1,1);
// (v,δv)
a->AddTestIntegrator(new MassIntegrator(one),1,1);
// additional terms for adjoint graph norm
if (adjoint_graph_norm)
{
// -(∇v,δτ)
a->AddTestIntegrator(new MixedVectorGradientIntegrator(negone),1,0);
// -(τ,∇δv)
a->AddTestIntegrator(new MixedVectorWeakDivergenceIntegrator(one),0,1);
// (τ,δτ)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),0,0);
}
// RHS
if (prob == prob_type::general)
{
a->AddDomainLFIntegrator(new DomainLFIntegrator(one),1);
}
FunctionCoefficient uex(exact);
Array<int> elements_to_refine;
ParGridFunction hatu_gf;
socketstream u_out;
socketstream sigma_out;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
u_out.open(vishost, visport);
sigma_out.open(vishost, visport);
}
for (int i = 0; i<ref; i++)
{
if (static_cond) { a->EnableStaticCondensation(); }
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;
hatu_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// shift the ess_tdofs
for (int i = 0; i < ess_tdof_list.Size(); i++)
{
ess_tdof_list[i] += u_fes->GetTrueVSize() + sigma_fes->GetTrueVSize();
}
Array<int> offsets(5);
offsets[0] = 0;
offsets[1] = u_fes->GetVSize();
offsets[2] = sigma_fes->GetVSize();
offsets[3] = hatu_fes->GetVSize();
offsets[4] = hatsigma_fes->GetVSize();
offsets.PartialSum();
BlockVector x(offsets);
x = 0.0;
if (prob == prob_type::lshape)
{
hatu_gf.MakeRef(hatu_fes,x.GetBlock(2));
hatu_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 = new BlockDiagonalPreconditioner(A->RowOffsets());
M->owns_blocks = 1;
int skip = 0;
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)
{
prec = new HypreAMS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatsigma_fes);
}
else
{
prec = new HypreADS((HypreParMatrix &)A->GetBlock(skip+1,skip+1), hatsigma_fes);
}
M->SetDiagonalBlock(skip+1,prec);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(3);
cg.SetPreconditioner(*M);
cg.SetOperator(*A);
cg.Mult(B, X);
delete M;
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);
if (myid == 0)
{
cout << "Global Residual = " << globalresidual << endl;
}
elements_to_refine.SetSize(0);
for (int iel = 0; iel<pmesh.GetNE(); iel++)
{
if (residuals[iel] > theta * maxresidual)
{
elements_to_refine.Append(iel);
}
}
ParGridFunction u_gf;
u_gf.MakeRef(u_fes,x.GetBlock(0));
ParGridFunction sigma_gf;
sigma_gf.MakeRef(sigma_fes,x.GetBlock(1));
if (visualization)
{
u_out << "parallel " << num_procs << " " << myid << "\n";
u_out.precision(8);
u_out << "solution\n" << pmesh << u_gf <<
"window_title 'Numerical u' "
<< flush;
sigma_out << "parallel " << num_procs << " " << myid << "\n";
sigma_out.precision(8);
sigma_out << "solution\n" << pmesh << sigma_gf <<
"window_title 'Numerical flux' "
<< flush;
}
if (i == ref-1)
{
break;
}
pmesh.GeneralRefinement(elements_to_refine);
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
a->Update();
}
delete a;
delete tau_fec;
delete v_fec;
delete hatsigma_fes;
delete hatsigma_fec;
delete hatu_fes;
delete hatu_fec;
delete sigma_fec;
delete sigma_fes;
delete u_fec;
delete u_fes;
return 0;
}
+59
View File
@@ -0,0 +1,59 @@
# Copyright (c) 2010-2022, 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)/examples/dpg_tests/grad-div,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = primal_dpg
PAR_EXAMPLES =
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean clean-build clean-exec
# Remove built-in rule
%: %.cpp
# Replace the default implicit rule for *.cpp files
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
all: $(EXAMPLES)
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
# Testing: Parallel vs. serial runs
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
%-test-seq: %
@$(call mfem-test,$<,, Serial example)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
+176
View File
@@ -0,0 +1,176 @@
// MFEM primal dpg example for grad-dic problem
//
// Compile with: make primal_dpg
//
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Exact solution, F, and r.h.s., f. See below for implementation.
void F_exact(const Vector &, Vector &);
void f_exact(const Vector &, Vector &);
double freq = 1.0, kappa;
int main(int argc, char *argv[])
{
// 1. Parse command line options
const char *mesh_file = "../../../data/star.mesh";
int order = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&order, "-o", "--order", "Finite element polynomial degree");
args.ParseCheck();
kappa = freq * M_PI;
// 2. Read the mesh from the given mesh file, and refine once uniformly.
Mesh mesh(mesh_file);
// mesh.UniformRefinement();
RT_FECollection fec(order-1, mesh.Dimension());
FiniteElementSpace RTfes(&mesh, &fec);
H1_Trace_FECollection trace_fec(order, mesh.Dimension());
FiniteElementSpace H1trace_fes(&mesh, &trace_fec);
int dim = mesh.Dimension();
int test_order = order;
if (dim == 2 && (order%2 == 0 || (mesh.MeshGenerator() & 2 && order > 1)))
{
test_order++;
}
test_order++;
RT_FECollection test_fec(test_order,mesh.Dimension());
Array<FiniteElementSpace *> trial_fes;
Array<FiniteElementCollection * > test_fecs;
trial_fes.Append(&RTfes);
trial_fes.Append(&H1trace_fes);
test_fecs.Append(&test_fec);
GridFunction rt_gf(&RTfes);
VectorFunctionCoefficient F(dim, F_exact);
rt_gf.ProjectCoefficient(F);
Vector x(RTfes.GetVSize()+H1trace_fes.GetVSize());
x = 0.;
x.SetVector(rt_gf,0);
ConstantCoefficient alpha(1.0);
ConstantCoefficient beta(1.0);
NormalEquations * a = new NormalEquations(trial_fes,test_fecs);
a->AddTrialIntegrator(new DivDivIntegrator(alpha),0,0);
a->AddTrialIntegrator(new VectorFEMassIntegrator(beta),0,0);
a->AddTrialIntegrator(new NormalTraceIntegrator,1,0);
a->AddTestIntegrator(new DivDivIntegrator(alpha),0,0);
a->AddTestIntegrator(new VectorFEMassIntegrator(beta),0,0);
VectorFunctionCoefficient f(dim, f_exact);
a->AddDomainLFIntegrator(new VectorFEDomainLFIntegrator(f),0);
a->Assemble();
Array<int> ess_tdof_list;
if (mesh.bdr_attributes.Size())
{
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 1;
RTfes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
Vector X,B;
OperatorPtr Ah;
a->FormLinearSystem(ess_tdof_list,x,Ah,X,B);
BlockMatrix * A = (BlockMatrix *)(Ah.Ptr());
BlockDiagonalPreconditioner * M = new BlockDiagonalPreconditioner(A->RowOffsets());
M->owns_blocks = 1;
for (int i=0; i<A->NumRowBlocks(); i++)
{
M->SetDiagonalBlock(i,new UMFPackSolver(A->GetBlock(i,i)));
}
CGSolver cg;
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(3);
cg.SetPreconditioner(*M);
cg.SetOperator(*A);
cg.Mult(B, X);
delete M;
a->RecoverFEMSolution(X,x);
// GridFunction u_gf;
double *data = x.GetData();
rt_gf.MakeRef(&RTfes,data);
GridFunction exact_gf(&RTfes);
exact_gf.ProjectCoefficient(F);
char vishost[] = "localhost";
int visport = 19916;
socketstream solu_sock(vishost, visport);
solu_sock.precision(8);
solu_sock << "solution\n" << mesh << rt_gf <<
"window_title 'Numerical u' "
<< flush;
socketstream soltrace_sock(vishost, visport);
soltrace_sock.precision(8);
soltrace_sock << "solution\n" << mesh << exact_gf <<
"window_title 'Exact' "
<< flush;
}
// The exact solution (for non-surface meshes)
void F_exact(const Vector &p, Vector &F)
{
int dim = p.Size();
double x = p(0);
double y = p(1);
// double z = (dim == 3) ? p(2) : 0.0; // Uncomment if F is changed to depend on z
F(0) = cos(kappa*x)*sin(kappa*y);
F(1) = cos(kappa*y)*sin(kappa*x);
if (dim == 3)
{
F(2) = 0.0;
}
}
// The right hand side
void f_exact(const Vector &p, Vector &f)
{
int dim = p.Size();
double x = p(0);
double y = p(1);
// double z = (dim == 3) ? p(2) : 0.0; // Uncomment if f is changed to depend on z
double temp = 1 + 2*kappa*kappa;
f(0) = temp*cos(kappa*x)*sin(kappa*y);
f(1) = temp*cos(kappa*y)*sin(kappa*x);
if (dim == 3)
{
f(2) = 0;
}
}
+51
View File
@@ -0,0 +1,51 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
#
dimension
2
elements
3
1 3 0 1 4 3
1 3 3 4 7 6
1 3 1 2 5 4
boundary
8
1 1 0 1
1 1 1 2
1 1 2 5
2 1 5 4
2 1 4 7
1 1 7 6
1 1 6 3
1 1 3 0
vertices
8
nodes
FiniteElementSpace
FiniteElementCollection: H1_2D_P1
VDim: 2
Ordering: 1
-1 1
-1 -0
-1 -1
0 1
0 -0
0 -1
1 1
1 -0
@@ -0,0 +1,907 @@
// MFEM Ultraweak DPG Maxwell example
//
// Compile with: make complex_uw_dpg
//
// ∇×(1/μ ∇×E) - ω^2 ϵ E = Ĵ , in Ω
// E×n = E_0, on ∂Ω
// First Order System
// i ω μ H + ∇ × E = 0, in Ω
// -i ω ϵ E + ∇ × H = J, in Ω
// E × n = E_0, on ∂Ω
// note: Ĵ = -iωJ
// 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 = A = [0 1; -1 0];
// UW-DPG:
//
// 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,Ω)
// -i ω ϵ (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 | -i ω ϵ (E,G) | (H,∇ × G) | | < n × Ĥ, G > | (J,G) |
// where (F,G) ∈ H(curl,Ω) × H(curl,Ω)
// in 2D
// E ∈ L^2(Ω)^2, H ∈ L^2(Ω)
// Ê ∈ H^-1/2(Ω)(Γ_h), Ĥ ∈ H^1/2(Γ_h)
// i ω μ (H,F) + (E, ∇ × F) + < AÊ, F > = 0, ∀ F ∈ H^1
// -i ω ϵ (E,G) + (H,∇ × G) + < Ĥ, G × n > = (J,G) ∀ G ∈ H(curl,Ω)
// Ê = E_0 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^1 × H(curl,Ω)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
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);
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 curlcurlE_exact_r(const Vector &x, Vector & curlcurlE_r);
void curlcurlE_exact_i(const Vector &x, Vector & curlcurlE_i);
void hatE_exact_r(const Vector & X, Vector & hatE_r);
void hatE_exact_i(const Vector & X, Vector & hatE_i);
void hatH_exact_r(const Vector & X, Vector & hatH_r);
void hatH_exact_i(const Vector & X, Vector & hatH_i);
double hatH_exact_scalar_r(const Vector & X);
double hatH_exact_scalar_i(const Vector & X);
void maxwell_solution(const Vector & X,
std::vector<complex<double>> &E,
std::vector<complex<double>> &curlE,
std::vector<complex<double>> &curlcurlE);
void maxwell_solution_r(const Vector & X, Vector &E_r,
Vector &curlE_r,
Vector &curlcurlE_r);
void maxwell_solution_i(const Vector & X, Vector &E_i,
Vector &curlE_i,
Vector &curlcurlE_i);
int dim;
int dimc;
double omega;
double mu = 1.0;
double epsilon = 1.0;
enum prob_type
{
polynomial,
plane_wave,
fichera_oven
};
prob_type prob;
int main(int argc, char *argv[])
{
const char *mesh_file = "../../../data/inline-hex.mesh";
int order = 1;
int delta_order = 1;
bool visualization = true;
double rnum=1.0;
int ref = 1;
double theta = 0.0;
bool adjoint_graph_norm = false;
bool static_cond = false;
int iprob = 0;
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(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
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(&iprob, "-prob", "--problem", "Problem case"
" 0: polynomial, 1: plane wave, 2: Gaussian beam");
args.AddOption(&delta_order, "-do", "--delta_order",
"Order enrichment for DPG test space.");
args.AddOption(&theta, "-theta", "--theta",
"Theta parameter for AMR");
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
"-no-graph-norm", "--no-adjoint-graph-norm",
"Enable or disable Adjoint Graph Norm on the test space");
args.AddOption(&ref, "-ref", "--serial_ref",
"Number of serial refinements.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
if (iprob > 2) { iprob = 0; }
prob = (prob_type)iprob;
omega = 2.*M_PI*rnum;
Mesh mesh(mesh_file, 1, 1);
dim = mesh.Dimension();
dimc = (dim == 3) ? 3 : 1;
int test_order = order+delta_order;
// Define spaces
// L2 space for E
FiniteElementCollection *E_fec = new L2_FECollection(order-1,dim);
FiniteElementSpace *E_fes = new FiniteElementSpace(&mesh,E_fec,dim);
// Vector L2 space for H
FiniteElementCollection *H_fec = new L2_FECollection(order-1,dim);
FiniteElementSpace *H_fes = new FiniteElementSpace(&mesh,H_fec, dimc);
// H^-1/2 (curl) space for Ê
FiniteElementCollection * hatE_fec = nullptr;
FiniteElementCollection * hatH_fec = nullptr;
FiniteElementCollection * F_fec = nullptr;
if (dim == 3)
{
hatE_fec = new ND_Trace_FECollection(order,dim);
hatH_fec = new ND_Trace_FECollection(order,dim);
F_fec = new ND_FECollection(test_order, dim);
}
else
{
hatE_fec = new RT_Trace_FECollection(order-1,dim);
hatH_fec = new H1_Trace_FECollection(order,dim);
F_fec = new H1_FECollection(test_order, dim);
}
FiniteElementSpace *hatE_fes = new FiniteElementSpace(&mesh,hatE_fec);
FiniteElementSpace *hatH_fes = new FiniteElementSpace(&mesh,hatH_fec);
FiniteElementCollection * G_fec = new ND_FECollection(test_order, dim);
mfem::out << "E_fes space true dofs = " << E_fes->GetTrueVSize() << endl;
mfem::out << "H_fes space true dofs = " << H_fes->GetTrueVSize() << endl;
mfem::out << "hatE_fes space true dofs = " << hatE_fes->GetTrueVSize() << endl;
mfem::out << "hatH_fes space true dofs = " << hatH_fes->GetTrueVSize() << endl;
// // Coefficients
Vector dim_zero(dim); dim_zero = 0.0;
Vector dimc_zero(dimc); dimc_zero = 0.0;
VectorConstantCoefficient E_zero(dim_zero);
VectorConstantCoefficient H_zero(dimc_zero);
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);
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);
ScalarMatrixProductCoefficient epsrot(epsomeg,rot);
ScalarMatrixProductCoefficient negepsrot(negepsomeg,rot);
// Normal equation weak formulation
Array<FiniteElementSpace * > 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);
ComplexNormalEquations * a = new ComplexNormalEquations(trial_fes,test_fec);
a->StoreMatrices();
// (E,∇ × F)
a->AddTrialIntegrator(new TransposeIntegrator(new CurlIntegrator(one)),nullptr,0,0);
// -i ω ϵ (E , G)
a->AddTrialIntegrator(nullptr,new TransposeIntegrator(new VectorFEMassIntegrator(negepsomeg)),0,1);
// i ω μ (H, F)
if (dim == 3)
{
a->AddTrialIntegrator(nullptr,new TransposeIntegrator(new VectorFEMassIntegrator(muomeg)),1,0);
}
else
{
a->AddTrialIntegrator(nullptr,new MixedScalarMassIntegrator(muomeg),1,0);
}
// (H,∇ × G)
a->AddTrialIntegrator(new TransposeIntegrator(new CurlIntegrator(one)),nullptr,1,1);
// < n×Ê,F>
if (dim == 3)
{
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,2,0);
}
else
{
a->AddTrialIntegrator(new TraceIntegrator,nullptr,2,0);
}
// < n×Ĥ ,G>
a->AddTrialIntegrator(new TangentTraceIntegrator,nullptr,3,1);
// test integrators
//space-induced norm for H(curl) × H(curl)
if (dim == 3)
{
// (∇×F,∇×δF)
a->AddTestIntegrator(new CurlCurlIntegrator(one),nullptr,0,0);
// (F,δF)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,0,0);
}
else
{
// (∇F,∇δF)
a->AddTestIntegrator(new DiffusionIntegrator(one),nullptr,0,0);
// (F,δF)
a->AddTestIntegrator(new MassIntegrator(one),nullptr,0,0);
}
// (∇×G ,∇× δG)
a->AddTestIntegrator(new CurlCurlIntegrator(one),nullptr,1,1);
// (G,δG)
a->AddTestIntegrator(new VectorFEMassIntegrator(one),nullptr,1,1);
// additional integrators for the adjoint graph norm
if (adjoint_graph_norm)
{
if(dim == 3)
{
// μ^2 ω^2 (F,δF)
a->AddTestIntegrator(new VectorFEMassIntegrator(mu2omeg2),nullptr,0,0);
// -i ω μ (F,∇ × δG) = (F, ω μ ∇ × δ G)
a->AddTestIntegrator(nullptr,new MixedVectorWeakCurlIntegrator(negmuomeg),0,1);
// -i ω ϵ (∇ × F, δG)
a->AddTestIntegrator(nullptr,new MixedVectorCurlIntegrator(negepsomeg),0,1);
// i ω μ (∇ × G,δF)
a->AddTestIntegrator(nullptr,new MixedVectorCurlIntegrator(epsomeg),1,0);
// i ω ϵ (G, ∇ × δF )
a->AddTestIntegrator(nullptr,new MixedVectorWeakCurlIntegrator(muomeg),1,0);
// ϵ^2 ω^2 (G,δG)
a->AddTestIntegrator(new VectorFEMassIntegrator(eps2omeg2),nullptr,1,1);
}
else
{
// μ^2 ω^2 (F,δF)
a->AddTestIntegrator(new MassIntegrator(mu2omeg2),nullptr,0,0);
// -i ω μ (F,∇ × δG) = i (F, -ω μ ∇ × δ G)
a->AddTestIntegrator(nullptr,
new TransposeIntegrator(new CurlIntegrator(negmuomeg)),0,1);
// -i ω ϵ (∇ × F, δG) = i (- ω ϵ A ∇ F,δG), A = [0 1; -1; 0]
a->AddTestIntegrator(nullptr,new MixedVectorGradientIntegrator(negepsrot),0,1);
// i ω μ (∇ × G,δF) = i (ω μ ∇ × G, δF )
a->AddTestIntegrator(nullptr,new CurlIntegrator(muomeg),1,0);
// i ω ϵ (G, ∇ × δF ) = i (ω ϵ G, A ∇ δF) = i ( G , ω ϵ A ∇ δF)
a->AddTestIntegrator(nullptr,
new TransposeIntegrator(new MixedVectorGradientIntegrator(epsrot)),1,0);
// or i ( ω ϵ A^t G, ∇ δF) = i (- ω ϵ A G, ∇ δF)
// a->AddTestIntegrator(nullptr,
// new MixedVectorWeakDivergenceIntegrator(epsrot),1,0);
// ϵ^2 ω^2 (G,δG)
a->AddTestIntegrator(new VectorFEMassIntegrator(eps2omeg2),nullptr,1,1);
}
}
// 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),1);
VectorFunctionCoefficient hatEex_r(dim,hatE_exact_r);
VectorFunctionCoefficient hatEex_i(dim,hatE_exact_i);
VectorFunctionCoefficient hatHex_r(dimc,hatH_exact_r);
VectorFunctionCoefficient hatHex_i(dimc,hatH_exact_i);
FunctionCoefficient hatH_2D_ex_r(hatH_exact_scalar_r);
FunctionCoefficient hatH_2D_ex_i(hatH_exact_scalar_i);
Array<int> elements_to_refine;
socketstream E_out_r;
socketstream Eex_out_r;
// socketstream E_out_i;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
E_out_r.open(vishost, visport);
Eex_out_r.open(vishost, visport);
// E_out_i.open(vishost, visport);
}
double res0 = 0.;
double err0 = 0.;
int dof0;
mfem::out << " Refinement |"
<< " Dofs |"
<< " L2 Error |"
<< " Relative % |"
<< " Rate |"
<< " Residual |"
<< " Rate |" << endl;
mfem::out << " --------------------"
<< "-------------------"
<< "-------------------"
<< "-------------------" << endl;
for (int i = 0; i<ref; i++)
{
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
Array<int> ess_tdof_list;
Array<int> ess_bdr;
if (mesh.bdr_attributes.Size())
{
ess_bdr.SetSize(mesh.bdr_attributes.Max());
ess_bdr = 1;
hatE_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// hatH_fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// shift the ess_tdofs
for (int j = 0; j < ess_tdof_list.Size(); j++)
{
ess_tdof_list[j] += E_fes->GetTrueVSize() + H_fes->GetTrueVSize();
// + hatE_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();
ComplexGridFunction hatE_gf(hatE_fes);
hatE_gf.real().MakeRef(hatE_fes,&xdata[offsets[2]]);
hatE_gf.imag().MakeRef(hatE_fes,&xdata[offsets.Last()+ offsets[2]]);
ComplexGridFunction hatH_gf(hatH_fes);
hatH_gf.real().MakeRef(hatH_fes,&xdata[offsets[3]]);
hatH_gf.imag().MakeRef(hatH_fes,&xdata[offsets.Last()+ offsets[3]]);
if (dim == 3)
{
hatE_gf.ProjectBdrCoefficientTangent(hatEex_r,hatEex_i, ess_bdr);
// hatH_gf.ProjectBdrCoefficientTangent(hatHex_r,hatHex_i, ess_bdr);
}
else
{
hatE_gf.ProjectBdrCoefficientNormal(hatEex_r,hatEex_i, ess_bdr);
// hatH_gf.ProjectBdrCoefficient(hatH_2D_ex_r,hatH_2D_ex_i, ess_bdr);
}
OperatorPtr Ah;
Vector X,B;
a->FormLinearSystem(ess_tdof_list,x,Ah, X,B);
ComplexOperator * Ahc = Ah.As<ComplexOperator>();
SparseMatrix * Ar = dynamic_cast<BlockMatrix *>(&Ahc->real())->CreateMonolithic();
SparseMatrix * Ai = dynamic_cast<BlockMatrix *>(&Ahc->imag())->CreateMonolithic();
ComplexSparseMatrix Ac(Ar,Ai,true,true);
SparseMatrix * A = Ac.GetSystemMatrix();
UMFPackSolver umf(*A);
umf.Mult(B,X);
delete A;
a->RecoverFEMSolution(X,x);
Vector & residuals = a->ComputeResidual(x);
double residual = residuals.Norml2();
elements_to_refine.SetSize(0);
double max_resid = residuals.Max();
for (int iel = 0; iel<mesh.GetNE(); iel++)
{
if (residuals[iel] > theta * max_resid)
{
elements_to_refine.Append(iel);
}
}
ComplexGridFunction E(E_fes);
E.real().MakeRef(E_fes,x.GetData());
E.imag().MakeRef(E_fes,&x.GetData()[offsets.Last()]);
VectorFunctionCoefficient E_ex_r(dim,E_exact_r);
VectorFunctionCoefficient E_ex_i(dim,E_exact_i);
ComplexGridFunction H(H_fes);
H.real().MakeRef(H_fes,&x.GetData()[offsets[1]]);
H.imag().MakeRef(H_fes,&x.GetData()[offsets.Last()+offsets[1]]);
VectorFunctionCoefficient H_ex_r(dimc,H_exact_r);
VectorFunctionCoefficient H_ex_i(dimc,H_exact_i);
int dofs = X.Size()/2;
double E_err_r = E.real().ComputeL2Error(E_ex_r);
double E_err_i = E.imag().ComputeL2Error(E_ex_i);
double H_err_r = H.real().ComputeL2Error(H_ex_r);
double H_err_i = H.imag().ComputeL2Error(H_ex_i);
double L2Error = sqrt( E_err_r*E_err_r + E_err_i*E_err_i
+ H_err_r*H_err_r + H_err_i*H_err_i );
ComplexGridFunction Egf_ex(E_fes);
ComplexGridFunction Hgf_ex(H_fes);
Egf_ex.ProjectCoefficient(E_ex_r, E_ex_i);
Hgf_ex.ProjectCoefficient(H_ex_r, H_ex_i);
double E_norm_r = Egf_ex.real().ComputeL2Error(E_zero);
double E_norm_i = Egf_ex.imag().ComputeL2Error(E_zero);
double H_norm_r = Hgf_ex.real().ComputeL2Error(H_zero);
double H_norm_i = Hgf_ex.imag().ComputeL2Error(H_zero);
double L2norm = sqrt( E_norm_r*E_norm_r + E_norm_i*E_norm_i
+ H_norm_r*H_norm_r + H_norm_i*H_norm_i );
double rel_err = L2Error/L2norm;
double rate_err = (i) ? dim*log(err0/rel_err)/log((double)dof0/dofs) : 0.0;
double rate_res = (i) ? dim*log(res0/residual)/log((double)dof0/dofs) : 0.0;
err0 = rel_err;
res0 = residual;
dof0 = dofs;
mfem::out << std::right << std::setw(11) << i << " | "
<< std::setw(10) << dof0 << " | "
<< std::setprecision(3)
<< std::setw(10) << std::scientific << err0 << " | "
<< std::setprecision(3)
<< std::setw(10) << std::fixed << rel_err*100 << " | "
<< 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::resetiosflags(std::ios::showbase)
<< std::setw(10) << std::scientific
<< std::endl;
if (visualization)
{
E_out_r.precision(8);
E_out_r << "solution\n" << mesh << E.real() <<
"window_title 'Real Numerical Electric field' "
<< flush;
// E_out_i.precision(8);
// E_out_i << "solution\n" << mesh << E.imag() <<
// "window_title 'Imag Numerical Electric field' "
// << flush;
Eex_out_r.precision(8);
Eex_out_r << "solution\n" << mesh << Egf_ex.real()
<< "window_title 'Real Exact Electric field' "
<< flush;
// socketstream E_i_sock(vishost, visport);
// E_i_sock.precision(8);
// E_i_sock << "solution\n" << mesh << Egf_ex.imag()
// << "window_title 'Imag Exact Electric field' "
// << flush;
}
if (i == ref-1)
break;
mesh.GeneralRefinement(elements_to_refine,1,1);
for (int i =0; i<trial_fes.Size(); i++)
{
trial_fes[i]->Update(false);
}
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;
}
void E_exact_r(const Vector &x, Vector & E_r)
{
Vector curlE_r;
Vector curlcurlE_r;
maxwell_solution_r(x,E_r,curlE_r,curlcurlE_r);
}
void E_exact_i(const Vector &x, Vector & E_i)
{
Vector curlE_i;
Vector curlcurlE_i;
maxwell_solution_i(x,E_i,curlE_i,curlcurlE_i);
}
void curlE_exact_r(const Vector &x, Vector &curlE_r)
{
Vector E_r;
Vector curlcurlE_r;
maxwell_solution_r(x,E_r,curlE_r,curlcurlE_r);
}
void curlE_exact_i(const Vector &x, Vector &curlE_i)
{
Vector E_i;
Vector curlcurlE_i;
maxwell_solution_i(x,E_i,curlE_i,curlcurlE_i);
}
void curlcurlE_exact_r(const Vector &x, Vector & curlcurlE_r)
{
Vector E_r;
Vector curlE_r;
maxwell_solution_r(x,E_r,curlE_r,curlcurlE_r);
}
void curlcurlE_exact_i(const Vector &x, Vector & curlcurlE_i)
{
Vector E_i;
Vector curlE_i;
maxwell_solution_i(x,E_i,curlE_i,curlcurlE_i);
}
void H_exact_r(const Vector &x, Vector & H_r)
{
// H = i ∇ × E / ω μ
// H_r = - ∇ × E_i / ω μ
Vector curlE_i;
curlE_exact_i(x,curlE_i);
H_r.SetSize(dimc);
for (int i = 0; i<dimc; i++)
{
H_r(i) = - curlE_i(i) / (omega * mu);
}
}
void H_exact_i(const Vector &x, Vector & H_i)
{
// H = i ∇ × E / ω μ
// H_i = ∇ × E_r / ω μ
Vector curlE_r;
curlE_exact_r(x,curlE_r);
H_i.SetSize(dimc);
for (int i = 0; i<dimc; i++)
{
H_i(i) = curlE_r(i) / (omega * mu);
}
}
void curlH_exact_r(const Vector &x,Vector &curlH_r)
{
// ∇ × H_r = - ∇ ×× E_i / ω μ
Vector curlcurlE_i;
curlcurlE_exact_i(x,curlcurlE_i);
curlH_r.SetSize(dim);
for (int i = 0; i<dim; i++)
{
curlH_r(i) = -curlcurlE_i(i) / (omega * mu);
}
}
void curlH_exact_i(const Vector &x,Vector &curlH_i)
{
// ∇ × H_i = ∇ ×× E_r / ω μ
Vector curlcurlE_r;
curlcurlE_exact_r(x,curlcurlE_r);
curlH_i.SetSize(dim);
for (int i = 0; i<dim; i++)
{
curlH_i(i) = curlcurlE_r(i) / (omega * mu);
}
}
void hatE_exact_r(const Vector & x, Vector & hatE_r)
{
if (dim == 3)
{
E_exact_r(x,hatE_r);
}
else
{
Vector E_r;
E_exact_r(x,E_r);
hatE_r.SetSize(hatE_r.Size());
// rotate E_hat
hatE_r[0] = E_r[1];
hatE_r[1] = -E_r[0];
}
}
void hatE_exact_i(const Vector & x, Vector & hatE_i)
{
if (dim == 3)
{
E_exact_i(x,hatE_i);
}
else
{
Vector E_i;
E_exact_i(x,E_i);
hatE_i.SetSize(hatE_i.Size());
// rotate E_hat
hatE_i[0] = E_i[1];
hatE_i[1] = -E_i[0];
}
}
void hatH_exact_r(const Vector & x, Vector & hatH_r)
{
H_exact_r(x,hatH_r);
}
void hatH_exact_i(const Vector & x, Vector & hatH_i)
{
H_exact_i(x,hatH_i);
}
double hatH_exact_scalar_r(const Vector & x)
{
Vector hatH_r;
H_exact_r(x,hatH_r);
return hatH_r[0];
}
double hatH_exact_scalar_i(const Vector & x)
{
Vector hatH_i;
H_exact_i(x,hatH_i);
return hatH_i[0];
}
// J = -i ω ϵ E + ∇ × H
// J_r + iJ_i = -i ω ϵ (E_r + i E_i) + ∇ × (H_r + i H_i)
void rhs_func_r(const Vector &x, Vector & J_r)
{
// J_r = ω ϵ E_i + ∇ × H_r
Vector E_i, curlH_r;
E_exact_i(x,E_i);
curlH_exact_r(x,curlH_r);
J_r.SetSize(dim);
for (int i = 0; i<dim; i++)
{
J_r(i) = omega * epsilon * E_i(i) + curlH_r(i);
}
}
void rhs_func_i(const Vector &x, Vector & J_i)
{
// J_i = - ω ϵ E_r + ∇ × H_i
Vector E_r, curlH_i;
E_exact_r(x,E_r);
curlH_exact_i(x,curlH_i);
J_i.SetSize(dim);
for (int i = 0; i<dim; i++)
{
J_i(i) = -omega * epsilon * E_r(i) + curlH_i(i);
}
}
void maxwell_solution(const Vector & X, std::vector<complex<double>> &E,
std::vector<complex<double>> &curlE,
std::vector<complex<double>> &curlcurlE)
{
double x = X(0);
double y = X(1);
double z;
if (dim == 3) z = X(2);
E.resize(dim);
curlE.resize(dimc);
curlcurlE.resize(dim);
switch (prob)
{
case prob_type::polynomial:
{
if (dim == 3)
{
E[0] = y * z * (1.0 - y) * (1.0 - z);
E[1] = x * y * z * (1.0 - x) * (1.0 - z);
E[2] = x * y * (1.0 - x) * (1.0 - y);
curlE[0] = (1.0 - x) * x * (y*(2.0*z-3.0)+1.0);
curlE[1] = 2.0*(1.0 - y)*y*(x-z);
curlE[2] = (z-1)*z*(1.0+y*(2.0*x-3.0));
curlcurlE[0] = 2.0 * y * (1.0 - y) - (2.0 * x - 3.0) * z * (1 - z);
curlcurlE[1] = 2.0 * y * (x * (1.0 - x) + (1.0 - z) * z);
curlcurlE[2] = 2.0 * y * (1.0 - y) + x * (3.0 - 2.0 * z) * (1.0 - x);
}
else
{
E[0] = y * (1.0 - y);
E[1] = x * y * (1.0 - x);
curlE[0] = y*(3.0 - 2*x) - 1.0;
curlcurlE[0] = 3.0 - 2*x;
curlcurlE[1] = 2.0*y;
}
}
break;
case prob_type::plane_wave:
{
std::complex<double> zi(0,1);
std::complex<double> pw = exp(-zi * omega * (X.Sum()));
E[0] = pw;
E[1] = 0.0;
if (dim == 3)
{
E[2] = 0.0;
curlE[0] = 0.0;
curlE[1] = -zi * omega * pw;
curlE[2] = zi * omega * pw;
curlcurlE[0] = 2.0 * omega * omega * pw;
curlcurlE[1] = - omega * omega * pw;
curlcurlE[2] = - omega * omega * pw;
}
else
{
curlE[0] = zi * omega * pw;
curlcurlE[0] = omega * omega * pw;
curlcurlE[1] = - omega * omega * pw ;
}
}
break;
default:
MFEM_ABORT("Fichera 'oven' problem not implemented yet");
break;
}
}
void maxwell_solution_r(const Vector & X, Vector &E_r,
Vector &curlE_r,
Vector &curlcurlE_r)
{
E_r.SetSize(dim);
curlE_r.SetSize(dimc);
curlcurlE_r.SetSize(dim);
std::vector<complex<double>> E;
std::vector<complex<double>> curlE;
std::vector<complex<double>> curlcurlE;
maxwell_solution(X,E,curlE,curlcurlE);
for (int i = 0; i<dim ; i++)
{
E_r(i) = E[i].real();
curlcurlE_r(i) = curlcurlE[i].real();
}
for (int i = 0; i<dimc; i++)
{
curlE_r(i) = curlE[i].real();
}
}
void maxwell_solution_i(const Vector & X, Vector &E_i,
Vector &curlE_i,
Vector &curlcurlE_i)
{
E_i.SetSize(dim);
curlE_i.SetSize(dimc);
curlcurlE_i.SetSize(dim);
std::vector<complex<double>> E;
std::vector<complex<double>> curlE;
std::vector<complex<double>> curlcurlE;
maxwell_solution(X,E,curlE,curlcurlE);
for (int i = 0; i<dim; i++)
{
E_i(i) = E[i].imag();
curlcurlE_i(i) = curlcurlE[i].imag();
}
for (int i = 0; i<dimc; i++)
{
curlE_i(i) = curlE[i].imag();
}
}
File diff suppressed because it is too large Load Diff
+59
View File
@@ -0,0 +1,59 @@
# Copyright (c) 2010-2022, 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)/examples/dpg_tests/acoustics,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = complex_uw_dpg complex_uw_dpg_2D
PAR_EXAMPLES = pcomplex_uw_dpg
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean clean-build clean-exec
# Remove built-in rule
%: %.cpp
# Replace the default implicit rule for *.cpp files
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
all: $(EXAMPLES)
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
# Testing: Parallel vs. serial runs
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI), Parallel example)
%-test-seq: %
@$(call mfem-test,$<,, Serial example)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,253 @@
// Test integrator
// (∇ × E, F)
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
void E_exact_r(const Vector &x, Vector & E_r);
void curlE_exact_r(const Vector &x, Vector &curlE_r);
void maxwell_solution(const Vector & X,
std::vector<complex<double>> &E,
std::vector<complex<double>> &curlE,
std::vector<complex<double>> &curlcurlE);
void maxwell_solution_r(const Vector & X, Vector &E_r,
Vector &curlE_r,
Vector &curlcurlE_r);
int dim;
int dimc;
double omega;
enum prob_type
{
polynomial,
plane_wave,
fichera_oven
};
prob_type prob;
int main(int argc, char *argv[])
{
const char *mesh_file = "../../../data/inline-hex.mesh";
int order = 1;
int delta_order = 1;
bool visualization = true;
double rnum=1.0;
int ref = 1;
double theta = 0.0;
bool adjoint_graph_norm = false;
bool static_cond = false;
int iprob = 0;
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(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&rnum, "-rnum", "--number_of_wavelenths",
"Number of wavelengths");
args.AddOption(&iprob, "-prob", "--problem", "Problem case"
" 0: polynomial, 1: plane wave, 2: Gaussian beam");
args.AddOption(&delta_order, "-do", "--delta_order",
"Order enrichment for DPG test space.");
args.AddOption(&theta, "-theta", "--theta",
"Theta parameter for AMR");
args.AddOption(&adjoint_graph_norm, "-graph-norm", "--adjoint-graph-norm",
"-no-graph-norm", "--no-adjoint-graph-norm",
"Enable or disable Adjoint Graph Norm on the test space");
args.AddOption(&ref, "-ref", "--serial_ref",
"Number of serial refinements.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
if (iprob > 2) { iprob = 0; }
prob = (prob_type)iprob;
omega = 2.*M_PI*rnum;
Mesh mesh(mesh_file, 1, 1);
dim = mesh.Dimension();
dimc = (dim == 3) ? 3 : 1;
// Define spaces
// L2 space for E
FiniteElementCollection *E_fec = new ND_FECollection(order,dim);
FiniteElementSpace *E_fes = new FiniteElementSpace(&mesh,E_fec);
FiniteElementCollection *curlE_fec = new L2_FECollection(order-1,dim);
FiniteElementSpace *curlE_fes = new FiniteElementSpace(&mesh,curlE_fec,dimc);
mfem::out << "E_fes space true dofs = " << E_fes->GetTrueVSize() << endl;
mfem::out << "curlE_fes space true dofs = " << curlE_fes->GetTrueVSize() << endl;
GridFunction E_gf(E_fes);
VectorFunctionCoefficient E_cf(dim,E_exact_r);
E_gf.ProjectCoefficient(E_cf);
GridFunction curlE_gf(curlE_fes);
VectorFunctionCoefficient curlE_cf(dimc,curlE_exact_r);
curlE_gf.ProjectCoefficient(curlE_cf);
char vishost[] = "localhost";
int visport = 19916;
socketstream E_sock(vishost, visport);
E_sock.precision(8);
E_sock << "solution\n"
<< mesh << E_gf
<< "window_title 'Exact E'" << flush;
socketstream curlE_sock(vishost, visport);
curlE_sock.precision(8);
curlE_sock << "solution\n"
<< mesh << curlE_gf
<< "window_title 'Exact curlE'" << flush;
MixedBilinearForm a(E_fes,curlE_fes);
a.AddDomainIntegrator(new CurlIntegrator());
a.Assemble();
Array<int> empty;
SparseMatrix A;
a.FormRectangularSystemMatrix(empty,empty,A);
Vector curl_load(A.Height());
A.Mult(E_gf,curl_load);
BilinearForm m(curlE_fes);
m.AddDomainIntegrator(new VectorMassIntegrator);
m.Assemble();
SparseMatrix M;
m.FormSystemMatrix(empty, M);
GSSmoother prec(M);
PCG(M, prec, curl_load, curlE_gf, 1, 200, 1e-12, 0.0);
socketstream curlE2_sock(vishost, visport);
curlE2_sock.precision(8);
curlE2_sock << "solution\n"
<< mesh << curlE_gf
<< "window_title 'Numerical curlE'" << flush;
delete E_fec;
delete E_fes;
delete curlE_fec;
delete curlE_fes;
return 0;
}
void E_exact_r(const Vector &x, Vector & E_r)
{
Vector curlE_r;
Vector curlcurlE_r;
maxwell_solution_r(x,E_r,curlE_r,curlcurlE_r);
}
void curlE_exact_r(const Vector &x, Vector &curlE_r)
{
Vector E_r;
Vector curlcurlE_r;
maxwell_solution_r(x,E_r,curlE_r,curlcurlE_r);
}
void maxwell_solution(const Vector & X, std::vector<complex<double>> &E,
std::vector<complex<double>> &curlE,
std::vector<complex<double>> &curlcurlE)
{
double x = X(0);
double y = X(1);
double z;
if (dim == 3)
{
z = X(2);
}
E.resize(dim);
curlE.resize(dimc);
curlcurlE.resize(dim);
if (dim == 3)
{
E[0] = y * z * (1.0 - y) * (1.0 - z);
E[1] = x * y * z * (1.0 - x) * (1.0 - z);
E[2] = x * y * (1.0 - x) * (1.0 - y);
curlE[0] = (1.0 - x) * x * (y*(2.0*z-3.0)+1.0);
curlE[1] = 2.0*(1.0 - y)*y*(x-z);
curlE[2] = (z-1)*z*(1.0+y*(2.0*x-3.0));
curlcurlE[0] = 2.0 * y * (1.0 - y) - (2.0 * x - 3.0) * z * (1 - z);
curlcurlE[1] = 2.0 * y * (x * (1.0 - x) + (1.0 - z) * z);
curlcurlE[2] = 2.0 * y * (1.0 - y) + x * (3.0 - 2.0 * z) * (1.0 - x);
}
else if (dim == 2)
{
double c = 2.0*M_PI;
E[0] = sin(c * y);
E[1] = sin(c * x);
curlE[0] = c * (cos(c*x) - cos(c*y));
curlcurlE[0] = c*c * sin(c*y);
curlcurlE[1] = c*c * sin(c*x);
}
else
{
MFEM_ABORT("Dimension cannot be 1");
}
}
void maxwell_solution_r(const Vector & X, Vector &E_r,
Vector &curlE_r,
Vector &curlcurlE_r)
{
E_r.SetSize(dim);
curlE_r.SetSize(dimc);
curlcurlE_r.SetSize(dim);
std::vector<complex<double>> E;
std::vector<complex<double>> curlE;
std::vector<complex<double>> curlcurlE;
maxwell_solution(X,E,curlE,curlcurlE);
for (int i = 0; i<dim; i++)
{
E_r(i) = E[i].real();
curlcurlE_r(i) = curlcurlE[i].real();
}
for (int i = 0; i<dimc; i++)
{
curlE_r(i) = curlE[i].real();
}
}
+27 -5
View File
@@ -72,8 +72,8 @@ int main(int argc, char *argv[])
// largest number that gives a final mesh with no more than 10,000
// elements.
{
int ref_levels =
(int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
int ref_levels = 1;
// (int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
@@ -147,6 +147,8 @@ int main(int argc, char *argv[])
F.AddDomainIntegrator(new DomainLFIntegrator(one));
F.Assemble();
// 7. Set up the mixed bilinear form for the primal trial unknowns, B0,
// the mixed bilinear form for the interfacial unknowns, Bhat,
// the inverse stiffness matrix on the discontinuous test space, Sinv,
@@ -187,10 +189,17 @@ int main(int argc, char *argv[])
// 8. Set up the 1x2 block Least Squares DPG operator, B = [B0 Bhat],
// the normal equation operator, A = B^t Sinv B, and
// the normal equation right-hand-size, b = B^t Sinv F.
BlockOperator B(offsets_test, offsets);
// BlockOperator B(offsets_test, offsets);
BlockMatrix B(offsets_test, offsets);
B.SetBlock(0,0,&matB0);
B.SetBlock(0,1,&matBhat);
RAPOperator A(B, matSinv, B);
SparseMatrix * Bh = B.CreateMonolithic();
SparseMatrix * A = RAP(*Bh, matSinv, *Bh);
// RAPOperator A(B, matSinv, B);
{
Vector SinvF(s_test);
matSinv.Mult(F,SinvF);
@@ -234,7 +243,20 @@ int main(int argc, char *argv[])
// 10. Solve the normal equation system using the PCG iterative solver.
// Check the weighted norm of residual for the DPG least square problem.
// Wrap the primal variable in a GridFunction for visualization purposes.
PCG(A, P, b, x, 1, 200, 1e-12, 0.0);
// PCG(*A, P, b, x, 1, 200, 1e-12, 0.0);
GSSmoother M(*A);
CGSolver cg;
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(3);
cg.SetPreconditioner(M);
cg.SetOperator(*A);
cg.Mult(b, x);
{
Vector LSres(s_test);
+423 -2
View File
@@ -144,6 +144,14 @@ void BilinearFormIntegrator::AssembleFaceMatrix (
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe, const FiniteElement &test_fe,
FaceElementTransformations &Trans, DenseMatrix &elmat)
{
mfem_error ("BilinearFormIntegrator::AssembleTraceFaceMatrix(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleFaceMatrix(
const FiniteElement &trial_face_fe, const FiniteElement &test_fe1,
const FiniteElement &test_fe2, FaceElementTransformations &Trans,
@@ -793,6 +801,84 @@ const IntegrationRule &GradientIntegrator::GetRule(const FiniteElement
}
void CurlIntegrator::AssembleElementMatrix2(
const FiniteElement &trial_fe, const FiniteElement &test_fe,
ElementTransformation &Trans, DenseMatrix &elmat)
{
int dim = trial_fe.GetDim();
int trial_dof = trial_fe.GetDof();
int test_dof = test_fe.GetDof();
int dimc = (dim == 3) ? 3 : 1;
MFEM_ASSERT(trial_fe.GetMapType() == mfem::FiniteElement::H_CURL ||
dim == 2 && trial_fe.GetMapType() == mfem::FiniteElement::VALUE,
"Trial finite element must be either 2D/3D H(Curl) or 2D H1");
MFEM_ASSERT(test_fe.GetMapType() == mfem::FiniteElement::VALUE ||
test_fe.GetMapType() == mfem::FiniteElement::INTEGRAL,
"Test finite element must be in H1/L2");
bool spaceH1 = (trial_fe.GetMapType() == mfem::FiniteElement::VALUE);
if (spaceH1)
{
dshape.SetSize(trial_dof,dim);
curlshape.SetSize(dim*trial_dof,1);
dimc = dim;
}
else
{
curlshape.SetSize(trial_dof,dimc);
elmat_comp.SetSize(test_dof, trial_dof);
}
elmat.SetSize(dimc * test_dof, trial_dof);
shape.SetSize(test_dof);
elmat = 0.0;
double c;
Vector d_col;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order = trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderJ();
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
}
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Trans.SetIntPoint(&ip);
if (spaceH1)
{
trial_fe.CalcPhysDShape(Trans, dshape);
dshape.GradToCurl(curlshape);
}
else
{
trial_fe.CalcPhysCurlShape(Trans, curlshape);
}
test_fe.CalcPhysShape(Trans, shape);
c = ip.weight*Trans.Weight();
if (Q)
{
c *= Q->Eval(Trans, ip);
}
shape *= c;
for (int d = 0; d < dimc; ++d)
{
double * 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];
}
}
}
}
}
void DiffusionIntegrator::AssembleElementMatrix
( const FiniteElement &el, ElementTransformation &Trans,
DenseMatrix &elmat )
@@ -2003,6 +2089,84 @@ void CurlCurlIntegrator::AssembleElementMatrix
}
}
void CurlCurlIntegrator::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 dimc = trial_fe.GetCurlDim();
double w;
#ifdef MFEM_THREAD_SAFE
Vector D;
DenseMatrix curlshape(tr_nd,dimc), curlshape_dFt(tr_nd,dimc), M;
DenseMatrix te_curlshape(te_nd,dimc), te_curlshape_dFt(te_nd,dimc), M;
#else
curlshape.SetSize(tr_nd,dimc);
curlshape_dFt.SetSize(tr_nd,dimc);
te_curlshape.SetSize(te_nd,dimc);
te_curlshape_dFt.SetSize(te_nd,dimc);
#endif
elmat.SetSize(te_nd, tr_nd);
if (MQ) { M.SetSize(dimc); }
if (DQ) { D.SetSize(dimc); }
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order;
if (trial_fe.Space() == FunctionSpace::Pk)
{
order = test_fe.GetOrder() + trial_fe.GetOrder() - 2;
}
else
{
order = test_fe.GetOrder() + trial_fe.GetOrder() + trial_fe.GetDim() - 1;
}
ir = &IntRules.Get(trial_fe.GetGeomType(), order);
}
elmat = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Trans.SetIntPoint(&ip);
w = ip.weight * Trans.Weight();
trial_fe.CalcPhysCurlShape(Trans, curlshape_dFt);
test_fe.CalcPhysCurlShape(Trans, te_curlshape_dFt);
if (MQ)
{
MQ->Eval(M, Trans, ip);
M *= w;
Mult(te_curlshape_dFt, M, te_curlshape);
AddMultABt(te_curlshape, curlshape_dFt, elmat);
}
else if (DQ)
{
DQ->Eval(D, Trans, ip);
D *= w;
AddMultADBt(te_curlshape_dFt,D,curlshape_dFt,elmat);
}
else
{
if (Q)
{
w *= Q->Eval(Trans, ip);
}
curlshape_dFt *= w;
AddMultABt(te_curlshape_dFt, curlshape_dFt, elmat);
}
}
}
void CurlCurlIntegrator
::ComputeElementFlux(const FiniteElement &el, ElementTransformation &Trans,
Vector &u, const FiniteElement &fluxelem, Vector &flux,
@@ -2586,6 +2750,55 @@ void DivDivIntegrator::AssembleElementMatrix(
}
}
void DivDivIntegrator::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();
double c;
#ifdef MFEM_THREAD_SAFE
Vector divshape(tr_nd);
Vector te_divshape(te_nd);
#else
divshape.SetSize(tr_nd);
te_divshape.SetSize(te_nd);
#endif
elmat.SetSize(te_nd,tr_nd);
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order = 2 * max(test_fe.GetOrder(),
trial_fe.GetOrder()) - 2; // <--- OK for RTk
ir = &IntRules.Get(test_fe.GetGeomType(), order);
}
elmat = 0.0;
for (int i = 0; i < ir -> GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
trial_fe.CalcDivShape(ip,divshape);
test_fe.CalcDivShape(ip,te_divshape);
Trans.SetIntPoint (&ip);
c = ip.weight / Trans.Weight();
if (Q)
{
c *= Q -> Eval (Trans, ip);
}
te_divshape *= c;
AddMultVWt(te_divshape, divshape, elmat);
}
}
void VectorDiffusionIntegrator::AssembleElementMatrix(
const FiniteElement &el,
@@ -3780,7 +3993,7 @@ void NormalTraceJumpIntegrator::AssembleFaceMatrix(
for (i = 0; i < ndof1; i++)
for (j = 0; j < face_ndof; j++)
{
elmat(i, j) -= shape1_n(i) * face_shape(j);
elmat(i, j) += shape1_n(i) * face_shape(j);
}
if (ndof2)
{
@@ -3788,12 +4001,220 @@ void NormalTraceJumpIntegrator::AssembleFaceMatrix(
for (i = 0; i < ndof2; i++)
for (j = 0; j < face_ndof; j++)
{
elmat(ndof1+i, j) += shape2_n(i) * face_shape(j);
elmat(ndof1+i, j) -= shape2_n(i) * face_shape(j);
}
}
}
}
void TraceIntegrator::AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations & Trans,
DenseMatrix &elmat)
{
int i, j, face_ndof, ndof;
int order;
face_ndof = trial_face_fe.GetDof();
ndof = test_fe.GetDof();
face_shape.SetSize(face_ndof);
shape.SetSize(ndof);
elmat.SetSize(ndof, face_ndof);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
order = test_fe.GetOrder();
order += trial_face_fe.GetOrder();
if (trial_face_fe.GetMapType() == FiniteElement::VALUE)
{
order += Trans.OrderW();
}
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;
for (i = 0; i < ndof; i++)
{
for (j = 0; j < face_ndof; j++)
{
elmat(i, j) += scale * shape(i) * face_shape(j);
}
}
}
}
void NormalTraceIntegrator::AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations &Trans,
DenseMatrix &elmat)
{
int i, j, face_ndof, ndof, dim;
int order;
MFEM_VERIFY(trial_face_fe.GetMapType() == FiniteElement::VALUE, "");
face_ndof = trial_face_fe.GetDof();
ndof = test_fe.GetDof();
dim = test_fe.GetDim();
face_shape.SetSize(face_ndof);
normal.SetSize(dim);
shape.SetSize(ndof,dim);
shape_n.SetSize(ndof);
elmat.SetSize(ndof, face_ndof);
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);
Trans.SetAllIntPoints(&ip);
trial_face_fe.CalcPhysShape(Trans, face_shape);
CalcOrtho(Trans.Jacobian(),normal);
ElementTransformation * etrans = (iel == elem) ? Trans.Elem1 : Trans.Elem2;
test_fe.CalcVShape(*etrans, shape);
shape.Mult(normal, shape_n);
face_shape *= ip.weight;
for (i = 0; i < ndof; i++)
{
for (j = 0; j < face_ndof; j++)
{
elmat(i, j) += scale * shape_n(i) * face_shape(j);
}
}
}
}
void TangentTraceIntegrator::AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations & Trans,
DenseMatrix &elmat)
{
MFEM_VERIFY(test_fe.GetMapType() == FiniteElement::H_CURL, "");
int face_ndof, ndof, dim;
int order;
dim = test_fe.GetDim();
if (dim == 3)
{
std::string msg =
"Trial space should be ND face trace and test space should be a ND vector field in 3D ";
MFEM_VERIFY(trial_face_fe.GetMapType() == FiniteElement::H_CURL &&
trial_face_fe.GetDim() == 2 && test_fe.GetDim() == 3, msg);
}
else
{
std::string msg =
"Trial space should be H1 edge trace and test space should be a ND vector field in 2D";
MFEM_VERIFY(trial_face_fe.GetMapType() == FiniteElement::VALUE &&
trial_face_fe.GetDim() == 1 && test_fe.GetDim() == 2, msg);
}
face_ndof = trial_face_fe.GetDof();
ndof = test_fe.GetDof();
int dimc = (dim == 3) ? 3 : 1;
face_shape.SetSize(face_ndof,dimc);
shape_n.SetSize(ndof,dimc);
shape.SetSize(ndof,dim);
normal.SetSize(dim);
DenseMatrix face_shape_n(face_ndof,dimc);
elmat.SetSize(ndof, face_ndof);
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
if (dim == 3)
{
trial_face_fe.CalcVShape(Trans,face_shape);
}
else
{
face_shape.GetColumnReference(0,temp);
trial_face_fe.CalcPhysShape(Trans,temp);
}
CalcOrtho(Trans.Jacobian(),normal);
ElementTransformation * eltrans = (iel == elem) ? Trans.Elem1 : Trans.Elem2;
test_fe.CalcVShape(*eltrans, shape);
// rotate
cross_product(normal, shape, shape_n);
const double w = scale*ip.weight;
AddMult_a_ABt(w,shape_n, face_shape, elmat);
}
}
void NormalInterpolator::AssembleElementMatrix2(
const FiniteElement &dom_fe, const FiniteElement &ran_fe,
+124 -1
View File
@@ -151,6 +151,12 @@ public:
FaceElementTransformations &Trans,
DenseMatrix &elmat);
virtual void AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations &Trans,
DenseMatrix &elmat);
/** Abstract method used for assembling TraceFaceIntegrators in a
MixedBilinearForm. */
virtual void AssembleFaceMatrix(const FiniteElement &trial_face_fe,
@@ -2069,6 +2075,31 @@ public:
ElementTransformation &Trans);
};
/** Class for integrating the bilinear form a(u,v) := (Q curl u, v) where Q is a
scalar coefficient, and v is a vector with components v_i in the L2 or H1 space.
u can be in H(curl) (2D or 3D) or it can be a scalar H1.
Note: If u is scalar H1 then curl u = [0 1; -1 0] grad u */
class CurlIntegrator : public BilinearFormIntegrator
{
protected:
Coefficient *Q;
private:
Vector shape;
DenseMatrix dshape;
DenseMatrix curlshape;
DenseMatrix elmat_comp;
public:
CurlIntegrator() : Q{NULL} { }
CurlIntegrator(Coefficient *q_) : Q{q_} { }
CurlIntegrator(Coefficient &q) : Q{&q} { }
virtual void AssembleElementMatrix2(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
};
/** Class for integrating the bilinear form a(u,v) := (Q grad u, grad v) where Q
can be a scalar or a matrix coefficient. */
class DiffusionIntegrator: public BilinearFormIntegrator
@@ -2524,6 +2555,7 @@ private:
#ifndef MFEM_THREAD_SAFE
Vector D;
DenseMatrix curlshape, curlshape_dFt, M;
DenseMatrix te_curlshape, te_curlshape_dFt;
DenseMatrix vshape, projcurl;
#endif
@@ -2557,6 +2589,11 @@ public:
ElementTransformation &Trans,
DenseMatrix &elmat);
virtual void AssembleElementMatrix2(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
virtual void ComputeElementFlux(const FiniteElement &el,
ElementTransformation &Trans,
Vector &u, const FiniteElement &fluxelem,
@@ -2725,7 +2762,7 @@ protected:
private:
#ifndef MFEM_THREAD_SAFE
Vector divshape;
Vector divshape, te_divshape;
#endif
// PA extension
@@ -2743,6 +2780,10 @@ public:
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
DenseMatrix &elmat);
virtual void AssembleElementMatrix2(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
const Coefficient *GetCoefficient() const { return Q; }
};
@@ -3259,6 +3300,88 @@ public:
DenseMatrix &elmat);
};
/** 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 i.e., v:=un normal trace of H(div))
and the test variable w is in an H1-conforming space. */
class TraceIntegrator : public BilinearFormIntegrator
{
private:
Vector face_shape, shape;
public:
TraceIntegrator() { }
void AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations &Trans,
DenseMatrix &elmat);
};
/** 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:
Vector face_shape, normal, shape_n;
DenseMatrix shape;
public:
NormalTraceIntegrator() { }
virtual void AssembleTraceFaceMatrix(int ielem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations &Trans,
DenseMatrix &elmat);
};
/** Integrator for the form: < v, w × n > over a face (the interface)
* In 3D the trial variable v is defined on the interface (H^-1/2(curl), trace of H(curl))
* In 2D it's defined on the interface (H^1/2, trace of H1)
* The test variable w is in an H(curl)-conforming space. */
class TangentTraceIntegrator : public BilinearFormIntegrator
{
private:
DenseMatrix face_shape, shape, shape_n;
Vector normal;
Vector temp;
void cross_product(const Vector & x, const DenseMatrix & Y, DenseMatrix & Z)
{
int dim = x.Size();
MFEM_VERIFY(Y.Width() == dim, "Size missmatch");
int dimc = dim == 3 ? dim : 1;
int h = Y.Height();
Z.SetSize(h,dimc);
if (dim == 3)
{
for (int i = 0; i<h; i++)
{
Z(i,0) = x(2) * Y(i,1) - x(1) * Y(i,2);
Z(i,1) = x(0) * Y(i,2) - x(2) * Y(i,0);
Z(i,2) = x(1) * Y(i,0) - x(0) * Y(i,1);
}
}
else
{
for (int i = 0; i<h; i++)
{
Z(i,0) = x(1) * Y(i,0) - x(0) * Y(i,1);
}
}
}
public:
TangentTraceIntegrator() { }
void AssembleTraceFaceMatrix(int elem,
const FiniteElement &trial_face_fe,
const FiniteElement &test_fe,
FaceElementTransformations &Trans,
DenseMatrix &elmat);
};
/** Abstract class to serve as a base for local interpolators to be used in the
DiscreteLinearOperator class. */
class DiscreteInterpolator : public BilinearFormIntegrator { };
+512
View File
@@ -0,0 +1,512 @@
// Copyright (c) 2010-2022, 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 "fem.hpp"
namespace mfem
{
BlockBilinearForm::BlockBilinearForm(Array<FiniteElementSpace *> & fespaces_) :
Matrix(0), fespaces(fespaces_)
{
height = 0;
nblocks = fespaces.Size();
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] = fespaces[i]->GetVSize();
tdof_offsets[i+1] = fespaces[i]->GetTrueVSize();
}
dof_offsets.PartialSum();
tdof_offsets.PartialSum();
height = dof_offsets[nblocks];
width = height;
mat = mat_e = NULL;
extern_bfs = 0;
element_matrices = NULL;
diag_policy = DIAG_KEEP;
}
// Allocate appropriate SparseMatrix and assign it to mat
void BlockBilinearForm::AllocMat()
{
mat = new SparseMatrix(height);
}
void BlockBilinearForm::BuildProlongation()
{
P = new BlockMatrix(dof_offsets, tdof_offsets);
R = new BlockMatrix(tdof_offsets, dof_offsets);
for (int i = 0; i<nblocks; i++)
{
const SparseMatrix *P_ = fespaces[i]->GetConformingProlongation();
const SparseMatrix *R_ = fespaces[i]->GetRestrictionMatrix();
P->SetBlock(i,i,const_cast<SparseMatrix*>(P_));
R->SetBlock(i,i,const_cast<SparseMatrix*>(R_));
}
}
void BlockBilinearForm::ConformingAssemble()
{
Finalize(0);
MFEM_ASSERT(mat, "the BilinearForm is not assembled");
if (!P) { BuildProlongation(); }
SparseMatrix * Pm = P->CreateMonolithic();
SparseMatrix *Pt = Transpose(*Pm);
SparseMatrix *PtA = mfem::Mult(*Pt, *mat);
delete mat;
if (mat_e)
{
SparseMatrix *PtAe = mfem::Mult(*Pt, *mat_e);
delete mat_e;
mat_e = PtAe;
}
delete Pt;
mat = mfem::Mult(*PtA, *Pm);
delete PtA;
if (mat_e)
{
SparseMatrix *PtAeP = mfem::Mult(*mat_e, *Pm);
delete mat_e;
mat_e = PtAeP;
}
delete Pm;
height = mat->Height();
width = mat->Width();
}
void BlockBilinearForm::Mult(const Vector &x, Vector &y) const
{
// TODO
}
double& BlockBilinearForm::Elem (int i, int j)
{
return mat -> Elem(i,j);
}
const double& BlockBilinearForm::Elem (int i, int j) const
{
return mat -> Elem(i,j);
}
MatrixInverse * BlockBilinearForm::Inverse() const
{
return mat -> Inverse();
}
void BlockBilinearForm::Finalize(int skip_zeros)
{
mat->Finalize(skip_zeros);
if (mat_e) { mat_e->Finalize(skip_zeros); }
}
/// Adds new Block Domain Integrator. Assumes ownership of @a bfi.
void BlockBilinearForm::AddDomainIntegrator(BlockBilinearFormIntegrator *bfi)
{
domain_integs.Append(bfi);
}
/// Assembles the form i.e. sums over all domain integrators.
void BlockBilinearForm::Assemble(int skip_zeros)
{
ElementTransformation *eltrans;
DofTransformation * doftrans_j, *doftrans_k;
Mesh *mesh = fespaces[0] -> GetMesh();
DenseMatrix elmat, *elmat_p;
int nblocks = fespaces.Size();
Array<const FiniteElement *> fe(nblocks);
Array<int> vdofs_j, vdofs_k;
Array<int> offsetvdofs_j;
Array<int> elementblockoffsets(nblocks+1);
elementblockoffsets[0] = 0;
Array<int> blockoffsets(nblocks+1);
blockoffsets[0] = 0;
for (int i =0; i<nblocks; i++)
{
blockoffsets[i+1] = fespaces[i]->GetVSize();
}
blockoffsets.PartialSum();
// mfem::out << "blockoffsets = " ; blockoffsets.Print();
if (mat == NULL)
{
AllocMat();
}
if (domain_integs.Size())
{
// loop through elements
for (int i = 0; i < mesh -> GetNE(); i++)
{
if (element_matrices)
{
elmat_p = &(*element_matrices)(i);
}
else
{
elmat.SetSize(0);
for (int k = 0; k < domain_integs.Size(); k++)
{
for (int j = 0; j<nblocks; j++)
{
fe[j] = fespaces[j]->GetFE(i);
elementblockoffsets[j+1] = fe[j]->GetDof();
}
elementblockoffsets.PartialSum();
eltrans = mesh->GetElementTransformation(i);
domain_integs[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
if (elmat.Size() == 0)
{
elmat = elemmat;
}
else
{
elmat += elemmat;
}
}
}
if (elmat.Size() == 0)
{
continue;
}
else
{
elmat_p = &elmat;
}
vdofs.SetSize(0);
for (int j = 0; j<nblocks; j++)
{
doftrans_j = fespaces[j]->GetElementVDofs(i, vdofs_j);
int jbeg = elementblockoffsets[j];
int jend = elementblockoffsets[j+1]-1;
int offset_j = blockoffsets[j];
offsetvdofs_j.SetSize(vdofs_j.Size());
for (int l = 0; l<vdofs_j.Size(); l++)
{
offsetvdofs_j[l] = vdofs_j[l]<0 ? -offset_j + vdofs_j[l]
: offset_j + vdofs_j[l];
}
vdofs.Append(offsetvdofs_j);
for (int k = 0; k<nblocks; k++)
{
doftrans_k = fespaces[k]->GetElementVDofs(i, vdofs_k);
if (doftrans_k || doftrans_j)
{
int kbeg = elementblockoffsets[k];
int kend = elementblockoffsets[k+1]-1;
DenseMatrix A;
elmat_p->GetSubMatrix(jbeg,jend,kbeg, kend, A);
TransformDual(doftrans_j, doftrans_k, A);
elmat_p->SetSubMatrix(jbeg,kbeg,A);
}
}
}
mat->AddSubMatrix(vdofs,vdofs,*elmat_p, skip_zeros);
}
}
}
void BlockBilinearForm::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);
if (!P)
{
EliminateVDofsInRHS(ess_tdof_list, x, b);
X.MakeRef(x, 0, x.Size());
B.MakeRef(b, 0, b.Size());
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
}
else // non conforming space
{
B.SetSize(P->Width());
P->MultTranspose(b, B);
X.SetSize(R->Height());
mfem::out << "R height, width = " << R->Height() <<" x "<< R->Width() <<
std::endl;
R->Mult(x, X);
EliminateVDofsInRHS(ess_tdof_list, X, B);
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
}
}
void BlockBilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A)
{
if (!mat_e)
{
const SparseMatrix *P_ = fespaces[0]->GetConformingProlongation();
if (P_) { ConformingAssemble(); }
EliminateVDofs(ess_tdof_list, diag_policy);
const int remove_zeros = 0;
Finalize(remove_zeros);
}
A.Reset(mat, false);
}
void BlockBilinearForm::RecoverFEMSolution(const Vector &X, const Vector &b,
Vector &x)
{
if (!P)
{
x.SyncMemory(X);
}
else
{
// Apply conforming prolongation
x.SetSize(P->Height());
P->Mult(X, x);
}
}
void BlockBilinearForm::ComputeElementMatrices()
{
MFEM_ABORT("BlockBilinearForm::ComputeElementMatrices:not implemented yet")
}
void BlockBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
{
if (element_matrices)
{
elmat.SetSize(element_matrices->SizeI(), element_matrices->SizeJ());
elmat = element_matrices->GetData(i);
return;
}
int nblocks = fespaces.Size();
Array<const FiniteElement *> fe(nblocks);
ElementTransformation *eltrans;
elmat.SetSize(0);
if (domain_integs.Size())
{
for (int j = 0; j<nblocks; j++)
{
fe[j] = fespaces[j]->GetFE(i);
}
eltrans = fespaces[0]->GetElementTransformation(i);
domain_integs[0]->AssembleElementMatrix(fe, *eltrans, elmat);
for (int k = 1; k < domain_integs.Size(); k++)
{
domain_integs[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
elmat += elemmat;
}
}
else
{
int matsize = 0;
for (int j = 0; j<nblocks; j++)
{
matsize += fespaces[j]->GetFE(i)->GetDof();
}
elmat.SetSize(matsize);
elmat = 0.0;
}
}
void BlockBilinearForm::EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy)
{
MFEM_ABORT("BlockBilinearForm::EliminateEssentialBC: not implemented yet");
// Array<int> ess_dofs, conf_ess_dofs;
// fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs);
// if (fes->GetVSize() == height)
// {
// EliminateEssentialBCFromDofs(ess_dofs, sol, rhs, dpolicy);
// }
// else
// {
// fes->GetRestrictionMatrix()->BooleanMult(ess_dofs, conf_ess_dofs);
// EliminateEssentialBCFromDofs(conf_ess_dofs, sol, rhs, dpolicy);
// }
}
void BlockBilinearForm::EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
DiagonalPolicy dpolicy)
{
MFEM_ABORT("BlockBilinearForm::EliminateEssentialBC: not implemented yet");
// Array<int> ess_dofs, conf_ess_dofs;
// fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs);
// if (fes->GetVSize() == height)
// {
// EliminateEssentialBCFromDofs(ess_dofs, dpolicy);
// }
// else
// {
// fes->GetRestrictionMatrix()->BooleanMult(ess_dofs, conf_ess_dofs);
// EliminateEssentialBCFromDofs(conf_ess_dofs, dpolicy);
// }
}
void BlockBilinearForm::EliminateEssentialBCDiag (const Array<int>
&bdr_attr_is_ess,
double value)
{
MFEM_ABORT("BlockBilinearForm::EliminateEssentialBCDiag: not implemented yet");
// Array<int> ess_dofs, conf_ess_dofs;
// fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs);
// if (fes->GetVSize() == height)
// {
// EliminateEssentialBCFromDofsDiag(ess_dofs, value);
// }
// else
// {
// fes->GetRestrictionMatrix()->BooleanMult(ess_dofs, conf_ess_dofs);
// EliminateEssentialBCFromDofsDiag(conf_ess_dofs, value);
// }
}
void BlockBilinearForm::EliminateVDofs(const Array<int> &vdofs,
const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy)
{
vdofs.HostRead();
for (int i = 0; i < vdofs.Size(); i++)
{
int vdof = vdofs[i];
if ( vdof >= 0 )
{
mat -> EliminateRowCol (vdof, sol(vdof), rhs, dpolicy);
}
else
{
mat -> EliminateRowCol (-1-vdof, sol(-1-vdof), rhs, dpolicy);
}
}
}
void BlockBilinearForm::EliminateVDofs(const Array<int> &vdofs,
DiagonalPolicy dpolicy)
{
if (mat_e == NULL)
{
mat_e = new SparseMatrix(height);
}
// mat -> EliminateCols(vdofs, *mat_e,)
for (int i = 0; i < vdofs.Size(); i++)
{
int vdof = vdofs[i];
if ( vdof >= 0 )
{
mat -> EliminateRowCol (vdof, *mat_e, dpolicy);
}
else
{
mat -> EliminateRowCol (-1-vdof, *mat_e, dpolicy);
}
}
}
void BlockBilinearForm::EliminateEssentialBCFromDofs(
const Array<int> &ess_dofs, const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy)
{
MFEM_ASSERT(ess_dofs.Size() == height, "incorrect dof Array size");
MFEM_ASSERT(sol.Size() == height, "incorrect sol Vector size");
MFEM_ASSERT(rhs.Size() == height, "incorrect rhs Vector size");
for (int i = 0; i < ess_dofs.Size(); i++)
{
if (ess_dofs[i] < 0)
{
mat -> EliminateRowCol (i, sol(i), rhs, dpolicy);
}
}
}
void BlockBilinearForm::EliminateEssentialBCFromDofs (const Array<int>
&ess_dofs,
DiagonalPolicy dpolicy)
{
MFEM_ASSERT(ess_dofs.Size() == height, "incorrect dof Array size");
for (int i = 0; i < ess_dofs.Size(); i++)
{
if (ess_dofs[i] < 0)
{
mat -> EliminateRowCol (i, dpolicy);
}
}
}
void BlockBilinearForm::EliminateEssentialBCFromDofsDiag (
const Array<int> &ess_dofs,
double value)
{
MFEM_ASSERT(ess_dofs.Size() == height, "incorrect dof Array size");
for (int i = 0; i < ess_dofs.Size(); i++)
{
if (ess_dofs[i] < 0)
{
mat -> EliminateRowColDiag (i, value);
}
}
}
void BlockBilinearForm::EliminateVDofsInRHS(
const Array<int> &vdofs, const Vector &x, Vector &b)
{
mat_e->AddMult(x, b, -1.);
mat->PartMult(vdofs, x, b);
}
BlockBilinearForm::~BlockBilinearForm()
{
delete mat_e;
delete mat;
delete element_matrices;
for (int k=0; k < domain_integs.Size(); k++)
{
delete domain_integs[k];
}
for (int k=0; k < trace_integs.Size(); k++)
{
delete trace_integs[k];
}
delete P;
delete R;
}
} // namespace mfem
+288
View File
@@ -0,0 +1,288 @@
// Copyright (c) 2010-2022, 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_BLOCKBILINEARFORM
#define MFEM_BLOCKBILINEARFORM
#include "../config/config.hpp"
#include "../linalg/linalg.hpp"
namespace mfem
{
/** @brief A "square matrix" operator for the associated FE space and
BLFIntegrators The sum of all the BLFIntegrators can be used form the matrix
M. */
class BlockBilinearForm : public Matrix
{
protected:
int nblocks;
Array<int> dof_offsets;
Array<int> tdof_offsets;
/// Sparse matrix \f$ M \f$ to be associated with the form. Owned.
SparseMatrix *mat;
/** @brief Sparse Matrix \f$ M_e \f$ used to store the eliminations
from the b.c. Owned.
\f$ M + M_e = M_{original} \f$ */
SparseMatrix *mat_e;
/// FE spaces on which the block form lives. Not owned.
Array<FiniteElementSpace * > fespaces;
/** @brief Indicates the Mesh::sequence corresponding to the current state of
the BilinearForm. */
long sequence;
/** @brief Indicates the BlockBilinearFormIntegrator%s stored in #domain_integs,
are owned by another BlockBilinearForm. */
int extern_bfs;
/// Set of Domain Integrators to be applied.
Array<BlockBilinearFormIntegrator * > domain_integs;
/// Trace integrators.
Array<BlockBilinearFormIntegrator * > trace_integs;
DenseMatrix elemmat;
Array<int> vdofs;
DenseTensor *element_matrices; ///< Owned.
BlockMatrix * P = nullptr; // Block Prolongation
BlockMatrix * R = nullptr; // Block Restriction
/** This data member allows one to specify what should be done to the
diagonal matrix entries and corresponding RHS values upon elimination of
the constrained DoFs. */
DiagonalPolicy diag_policy;
// Allocate appropriate SparseMatrix and assign it to mat
void AllocMat();
void ConformingAssemble();
void BuildProlongation();
private:
public:
/// Creates bilinear form associated with FE spaces @a *fespaces.
BlockBilinearForm(Array<FiniteElementSpace * > & fespaces_);
/// Get the size of the BilinearForm as a square matrix.
int Size() const { return height; }
/// Pre-allocate the internal SparseMatrix before assembly.
void AllocateMatrix() { if (mat == NULL) { AllocMat(); } }
/// Returns a reference to: \f$ M_{ij} \f$
const double &operator()(int i, int j) { return (*mat)(i,j); }
/// Matrix vector multiplication: \f$ y = M x \f$
virtual void Mult(const Vector &x, Vector &y) const;
/** @brief Matrix vector multiplication with the original uneliminated
matrix. The original matrix is \f$ M + M_e \f$ so we have:
\f$ y = M x + M_e x \f$ */
void FullMult(const Vector &x, Vector &y) const
{ mat->Mult(x, y); mat_e->AddMult(x, y); }
virtual double &Elem(int i, int j);
virtual const double &Elem(int i, int j) const;
virtual MatrixInverse *Inverse() const;
/// Finalizes the matrix initialization.
virtual void Finalize(int skip_zeros = 1);
/// Returns a const reference to the sparse matrix.
const SparseMatrix &SpMat() const
{
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
return *mat;
}
/// Returns a reference to the sparse matrix: \f$ M \f$
SparseMatrix &SpMat()
{
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
return *mat;
}
/// Returns a const reference to the sparse matrix of eliminated b.c.: \f$ M_e \f$
const SparseMatrix &SpMatElim() const
{
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
return *mat_e;
}
/// Returns a reference to the sparse matrix of eliminated b.c.: \f$ M_e \f$
SparseMatrix &SpMatElim()
{
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
return *mat_e;
}
/// Adds new Domain Integrator. Assumes ownership of @a bfi.
void AddDomainIntegrator(BlockBilinearFormIntegrator *bfi);
/// Adds new Trace Integrator. Assumes ownership of @a bfi.
void AddTraceIntegrator(BlockBilinearFormIntegrator *bfi);
/// Sets all sparse values of \f$ M \f$ and \f$ M_e \f$ to 'a'.
void operator=(const double a)
{
if (mat != NULL) { *mat = a; }
if (mat_e != NULL) { *mat_e = a; }
}
/// Assembles the form i.e. sums over all domain 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);
/** @brief Form the linear system A X = B, corresponding to this bilinear
form and the linear form @a b(.). */
/** Version of the method FormLinearSystem() where the system matrix is
returned in the variable @a A, of type OpType, holding a *reference* to
the system matrix (created with the method OpType::MakeRef()). The
reference will be invalidated when SetOperatorType(), Update(), or the
destructor is called. */
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);
/// Form the linear system matrix A, see FormLinearSystem() for details.
/** Version of the method FormSystemMatrix() where the system matrix is
returned in the variable @a A, of type OpType, holding a *reference* to
the system matrix (created with the method OpType::MakeRef()). The
reference will be invalidated when SetOperatorType(), Update(), or the
destructor is called. */
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);
}
virtual void RecoverFEMSolution(const Vector &X, const Vector &b, Vector &x);
void ComputeElementMatrices();
/// Free the memory used by the element matrices.
void FreeElementMatrices()
{ delete element_matrices; element_matrices = NULL; }
/// Compute the element matrix of the given element
/** The element matrix is computed by calling the domain integrators
or the one stored internally by a prior call of ComputeElementMatrices()
is returned when available.
*/
void ComputeElementMatrix(int i, DenseMatrix &elmat);
/// Eliminate essential boundary DOFs from the system.
/** The array @a bdr_attr_is_ess marks boundary attributes that constitute
the essential part of the boundary. By default, the diagonal at the
essential DOFs is set to 1.0. This behavior is controlled by the argument
@a dpolicy. */
void EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy = DIAG_ONE);
/// Eliminate essential boundary DOFs from the system matrix.
void EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
DiagonalPolicy dpolicy = DIAG_ONE);
/// Perform elimination and set the diagonal entry to the given value
void EliminateEssentialBCDiag(const Array<int> &bdr_attr_is_ess,
double value);
/// Eliminate the given @a vdofs.
/** NOTE: here, @a vdofs is a list of DOFs from all the fespaces
In this case the eliminations are applied to the internal \f$ M \f$
and @a rhs without storing the elimination matrix \f$ M_e \f$. */
void EliminateVDofs(const Array<int> &vdofs, const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy = DIAG_ONE);
/// Eliminate the given @a vdofs (all the fespaces), storing the eliminated part internally in \f$ M_e \f$.
/** This method works in conjunction with EliminateVDofsInRHS() and allows
elimination of boundary conditions in multiple right-hand sides. In this
method, @a vdofs is a list of DOFs. */
void EliminateVDofs(const Array<int> &vdofs,
DiagonalPolicy dpolicy = DIAG_ONE);
/** @brief Similar to
EliminateVDofs(const Array<int> &, const Vector &, Vector &, DiagonalPolicy)
but here @a ess_dofs is a marker (boolean) array on all vector-dofs
(@a ess_dofs[i] < 0 is true). */
void EliminateEssentialBCFromDofs(const Array<int> &ess_dofs, const Vector &sol,
Vector &rhs, DiagonalPolicy dpolicy = DIAG_ONE);
/** @brief Similar to EliminateVDofs(const Array<int> &, DiagonalPolicy) but
here @a ess_dofs is a marker (boolean) array on all vector-dofs
(@a ess_dofs[i] < 0 is true). */
void EliminateEssentialBCFromDofs(const Array<int> &ess_dofs,
DiagonalPolicy dpolicy = DIAG_ONE);
/// Perform elimination and set the diagonal entry to the given value
void EliminateEssentialBCFromDofsDiag(const Array<int> &ess_dofs,
double value);
/** @brief Use the stored eliminated part of the matrix (see
EliminateVDofs(const Array<int> &, DiagonalPolicy)) to modify the r.h.s.
@a b; @a vdofs is a list of DOFs (non-directional, i.e. >= 0). */
void EliminateVDofsInRHS(const Array<int> &vdofs, const Vector &x,
Vector &b);
/// 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(DiagonalPolicy policy)
{
diag_policy = policy;
}
/// Destroys bilinear form.
virtual ~BlockBilinearForm();
};
} // namespace mfem
#endif
+136
View File
@@ -0,0 +1,136 @@
// Copyright (c) 2010-2022, 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 "fem.hpp"
namespace mfem
{
void BlockBilinearFormIntegrator::AssembleElementMatrix(
const Array<const FiniteElement *> &el,
ElementTransformation &Trans,
DenseMatrix &elmat)
{
mfem_error ("BlockBilinearFormIntegrator::AssembleElementMatrix\n"
" is not implemented for this class.");
}
void BlockLinearFormIntegrator::AssembleRHSElementVect(
const Array<const FiniteElement *> &el,
ElementTransformation &Trans,
Vector &elvect)
{
mfem_error ("BlockLinearFormIntegrator::AssembleElementVector\n"
" is not implemented for this class.");
}
/** Given a particular Finite Element computes the element vector */
void TestBlockBilinearFormIntegrator::AssembleElementMatrix
(const Array<const FiniteElement *> &el,
ElementTransformation &Trans,
DenseMatrix &elmat)
{
int nd = 0;
int nblocks = el.Size();
Array<int> offsets(nblocks+1);
offsets[0] = 0;
for (int i = 0; i<nblocks; i++)
{
nd += el[i]->GetDof();
offsets[i+1] = el[i]->GetDof();
}
offsets.PartialSum();
elmat.SetSize(nd);
elmat = 0.0;
DenseMatrix dmat;
if (blfis.NumRows())
{
// Get the matrices directly from the existing BilinearFormIntegrators
for (int i = 0; i<nblocks; i++)
{
// mfem::out << "i = " << i << std::endl;
int offset_i = offsets[i];
const FiniteElement * fe_i = el[i];
for (int j = 0; j<nblocks; j++)
{
// mfem::out << "j = " << j << std::endl;
BilinearFormIntegrator * blfi = blfis(i,j);
if (!blfi) { continue; }
if (j == i)
{
blfi->AssembleElementMatrix(*fe_i,Trans,dmat);
// mfem::out << "j 1 = " << j << std::endl;
elmat.SetSubMatrix(offset_i,dmat);
}
else
{
const FiniteElement * fe_j = el[j];
blfi->AssembleElementMatrix2(*fe_j,*fe_i,Trans,dmat);
// mfem::out << "j 2 = " << j << std::endl;
int offset_j = offsets[j];
elmat.SetSubMatrix(offset_i,offset_j,dmat);
}
}
}
return;
}
// else compute the matrices
elmat = 25.0;
// TODO
}
/** Given a particular Finite Element computes the element vector */
void TestBlockLinearFormIntegrator::AssembleRHSElementVect
(const Array<const FiniteElement *> &el,
ElementTransformation &Trans,
Vector &elvector)
{
int nd = 0;
int nblocks = el.Size();
Array<int> offsets(nblocks+1);
offsets[0] = 0;
for (int i = 0; i<nblocks; i++)
{
nd += el[i]->GetDof();
offsets[i+1] = el[i]->GetDof();
}
offsets.PartialSum();
elvector.SetSize(nd);
elvector = 0.0;
Vector subvector;
if (lfis.Size())
{
// Get the matrices directly from the existing BilinearFormIntegrators
for (int i = 0; i<nblocks; i++)
{
int offset = offsets[i];
const FiniteElement * fe_i = el[i];
LinearFormIntegrator * lfi = lfis[i];
if (!lfi)
{
continue;
}
lfi->AssembleRHSElementVect(*fe_i,Trans,subvector);
elvector.SetVector(subvector,offset);
}
return;
}
// else, compute the block linear form integrator
// elvector = 1.0;
// TODO
}
} // namespace mfem
+147
View File
@@ -0,0 +1,147 @@
// Copyright (c) 2010-2022, 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_BLOCKINTEG
#define MFEM_BLOCKINTEG
#include "../config/config.hpp"
#include "fe.hpp"
#include "coefficient.hpp"
#include "fespace.hpp"
namespace mfem
{
/** The abstract base class BlockBilinearFormIntegrator is
a generalization of the BilinearFormIntegrator class suitable
for block formulations. */
class BlockBilinearFormIntegrator
{
protected:
const IntegrationRule *IntRule;
BlockBilinearFormIntegrator(const IntegrationRule *ir = NULL)
: IntRule(ir) { }
public:
/// Given a particular Finite Element computes the element matrix elmat.
virtual void AssembleElementMatrix(const Array<const FiniteElement *> &el,
ElementTransformation &Trans,
DenseMatrix &elmat);
virtual ~BlockBilinearFormIntegrator() { }
};
/** The abstract base class BlockBilinearFormIntegrator is
a generalization of the BilinearFormIntegrator class suitable
for block formulations. */
class BlockLinearFormIntegrator
{
protected:
const IntegrationRule *IntRule;
BlockLinearFormIntegrator(const IntegrationRule *ir = NULL)
: IntRule(ir) { }
public:
/// Given a particular Finite Element computes the element matrix elmat.
virtual void AssembleRHSElementVect(const Array<const FiniteElement *> &el,
ElementTransformation &Trans,
Vector &elvect);
virtual ~BlockLinearFormIntegrator() { }
};
class TestBlockBilinearFormIntegrator: public BlockBilinearFormIntegrator
{
protected:
Coefficient *Q;
Array<const FiniteElementSpace * > fespaces;
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq, dofs1D, quad1D;
Array2D<BilinearFormIntegrator *> blfis;
public:
TestBlockBilinearFormIntegrator(const IntegrationRule *ir = NULL)
: BlockBilinearFormIntegrator(ir), Q(NULL), maps(NULL), geom(NULL) { }
/// Construct a mass integrator with coefficient q
TestBlockBilinearFormIntegrator(Coefficient &q,
const IntegrationRule *ir = NULL)
: BlockBilinearFormIntegrator(ir), Q(&q), maps(NULL), geom(NULL) { }
TestBlockBilinearFormIntegrator(Array2D<BilinearFormIntegrator *> blfis_)
: BlockBilinearFormIntegrator(NULL), blfis(blfis_) { }
void SetIntegrators(Array2D<BilinearFormIntegrator *> blfis_)
{
blfis = blfis_;
}
/** Given a particular Finite Element computes the element matrix
elmat. */
virtual void AssembleElementMatrix(const Array<const FiniteElement *> &el,
ElementTransformation &Trans,
DenseMatrix &elmat);
virtual ~TestBlockBilinearFormIntegrator() { }
};
/** Class for local vector assembly */
class TestBlockLinearFormIntegrator: public BlockLinearFormIntegrator
{
protected:
Coefficient *Q;
Array<const FiniteElementSpace * > fespaces;
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq, dofs1D, quad1D;
Array<LinearFormIntegrator *> lfis;
public:
TestBlockLinearFormIntegrator(const IntegrationRule *ir = NULL)
: BlockLinearFormIntegrator(ir), Q(NULL), maps(NULL), geom(NULL) { }
/// Construct a test linear integrator with coefficient q
TestBlockLinearFormIntegrator(Coefficient &q, const IntegrationRule *ir = NULL)
: BlockLinearFormIntegrator(ir), Q(&q), maps(NULL), geom(NULL) { }
TestBlockLinearFormIntegrator(Array<LinearFormIntegrator *> lfis_)
: BlockLinearFormIntegrator(NULL), lfis(lfis_) { }
void SetIntegrators(Array<LinearFormIntegrator *> lfis_)
{
lfis = lfis_;
}
/** Given a particular Finite Element computes the element vector */
virtual void AssembleRHSElementVect(const Array<const FiniteElement *> &el,
ElementTransformation &Trans,
Vector &elvector);
};
} // namespace mfem
#endif
+123
View File
@@ -0,0 +1,123 @@
// Copyright (c) 2010-2022, 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 "fem.hpp"
namespace mfem
{
BlockLinearForm::BlockLinearForm(Array<FiniteElementSpace * > & fespaces_) :
Vector(0), fespaces(fespaces_)
{
int s = 0;
int nblocks = fespaces.Size();
for (int i =0; i<nblocks; i++)
{
s += fespaces[i]->GetVSize();
}
// mfem::out << "size = " << size << std::endl;
SetSize(s);
}
void BlockLinearForm::AddDomainIntegrator(BlockLinearFormIntegrator *lfi)
{
domain_integs.Append(lfi);
}
void BlockLinearForm::Assemble()
{
ElementTransformation *eltrans;
DofTransformation *doftrans;
Mesh *mesh = fespaces[0] -> GetMesh();
Vector subvect,elvect, *elvect_p;
int nblocks = fespaces.Size();
Array<const FiniteElement *> fe(nblocks);
Array<int> offsetvdofs;
Array<int> elementblockoffsets(nblocks+1);
elementblockoffsets[0] = 0;
Array<int> blockoffsets(nblocks+1);
blockoffsets[0] = 0;
for (int i =0; i<nblocks; i++)
{
blockoffsets[i+1] = fespaces[i]->GetVSize();
}
blockoffsets.PartialSum();
Vector::operator=(0.0);
if (domain_integs.Size())
{
// loop through elements
for (int i = 0; i < mesh -> GetNE(); i++)
{
elvect.SetSize(0);
for (int k = 0; k < domain_integs.Size(); k++)
{
for (int j = 0; j<nblocks; j++)
{
fe[j] = fespaces[j]->GetFE(i);
elementblockoffsets[j+1] = fe[j]->GetDof();
}
elementblockoffsets.PartialSum();
eltrans = mesh->GetElementTransformation(i);
domain_integs[k]->AssembleRHSElementVect(fe, *eltrans, elemvect);
if (elvect.Size() == 0)
{
elvect = elemvect;
}
else
{
elvect += elemvect;
}
}
if (elvect.Size() == 0)
{
continue;
}
else
{
elvect_p = &elvect;
}
double *data = elvect_p->GetData();
for (int j = 0; j<nblocks; j++)
{
doftrans = fespaces[j]->GetElementVDofs(i, vdofs);
int offset = blockoffsets[j];
offsetvdofs.SetSize(vdofs.Size());
for (int l = 0; l<vdofs.Size(); l++)
{
offsetvdofs[l] = vdofs[l]<0 ? -offset + vdofs[l]
: offset + vdofs[l];
}
int jbeg = elementblockoffsets[j];
int jend = elementblockoffsets[j+1]-1;
subvect.SetSize(jend-jbeg+1);
subvect.SetData(&data[jbeg]);
if (doftrans)
{
doftrans->TransformDual(subvect);
}
AddElementVector(offsetvdofs,subvect);
}
}
}
}
} // name space mfem
+49
View File
@@ -0,0 +1,49 @@
// Copyright (c) 2010-2022, 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_BLOCKLINEARFORM
#define MFEM_BLOCKLINEARFORM
#include "../config/config.hpp"
#include "../linalg/linalg.hpp"
namespace mfem
{
class BlockLinearForm : public Vector
{
protected:
/// FE spaces on which the LinearForm lives. Not owned.
Array<FiniteElementSpace * > fespaces;
/// Set of Domain Integrators to be applied.
Array<BlockLinearFormIntegrator*> domain_integs;
Vector elemvect;
Array<int> vdofs;
public:
BlockLinearForm(Array<FiniteElementSpace * > & fespaces_);
/// Adds new Domain Integrator. Assumes ownership of @a lfi.
void AddDomainIntegrator(BlockLinearFormIntegrator *lfi);
/// Assembles the block linear form i.e. sums over all domain integrators.
void Assemble();
};
} // namespace mfem
#endif
+969
View File
@@ -0,0 +1,969 @@
// Copyright (c) 2010-2022, 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 "blockstaticcond.hpp"
namespace mfem
{
BlockStaticCondensation::BlockStaticCondensation(Array<FiniteElementSpace *> &
fes_)
{
SetSpaces(fes_);
Array<int> rvdofs;
Array<int> vdofs;
Array<int> rdof_edof0;
for (int k = 0; k<nblocks; k++)
{
if (!tr_fes[k]) { continue; }
rdof_edof0.SetSize(tr_fes[k]->GetVSize());
for (int i = 0; i < mesh->GetNE(); i++)
{
fes[k]->GetElementVDofs(i, vdofs);
tr_fes[k]->GetElementVDofs(i, rvdofs);
const int vdim = fes[k]->GetVDim();
const int nsd = vdofs.Size()/vdim;
const int nsrd = rvdofs.Size()/vdim;
for (int vd = 0; vd < vdim; vd++)
{
for (int j = 0; j < nsrd; j++)
{
int rvdof = rvdofs[j+nsrd*vd];
int vdof = vdofs[j+nsd*vd];
if (rvdof < 0)
{
rvdof = -1-rvdof;
vdof = -1-vdof;
}
MFEM_ASSERT(vdof >= 0, "incompatible volume and trace FE spaces");
rdof_edof0[rvdof] = vdof + dof_offsets[k];
}
}
}
rdof_edof.Append(rdof_edof0);
}
}
void BlockStaticCondensation::SetSpaces(Array<FiniteElementSpace*> & fes_)
{
#ifdef MFEM_USE_MPI
ParMesh *pmesh = nullptr;
parallel = false;
if (dynamic_cast<ParFiniteElementSpace *>(fes_[0]))
{
parallel = true;
}
#else
parallel = false;
#endif
fes=fes_;
nblocks = fes.Size();
rblocks = 0;
tr_fes.SetSize(nblocks);
mesh = fes[0]->GetMesh();
IsTraceSpace.SetSize(nblocks);
const FiniteElementCollection * fec;
for (int i = 0; i < nblocks; i++)
{
fec = fes[i]->FEColl();
IsTraceSpace[i] =
(dynamic_cast<const H1_Trace_FECollection*>(fec) ||
dynamic_cast<const ND_Trace_FECollection*>(fec) ||
dynamic_cast<const RT_Trace_FECollection*>(fec));
#ifdef MFEM_USE_MPI
if (parallel)
{
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(),
fes[i]->GetOrdering());
}
else
{
tr_fes[i] = (fec->GetContType() == FiniteElementCollection::DISCONTINUOUS) ?
nullptr : (IsTraceSpace[i]) ? fes[i] :
new FiniteElementSpace(mesh, 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(),
fes[i]->GetOrdering());
#endif
if (tr_fes[i]) { rblocks++; }
}
if (parallel)
{
ess_tdofs.SetSize(rblocks);
for (int i = 0; i<rblocks; i++)
{
ess_tdofs[i] = new Array<int>();
}
}
Init();
}
void BlockStaticCondensation::ComputeOffsets()
{
dof_offsets.SetSize(nblocks+1);
tdof_offsets.SetSize(nblocks+1);
dof_offsets[0] = 0;
tdof_offsets[0] = 0;
rdof_offsets.SetSize(rblocks+1);
rtdof_offsets.SetSize(rblocks+1);
rdof_offsets[0] = 0;
rtdof_offsets[0] = 0;
int j=0;
for (int i =0; i<nblocks; i++)
{
dof_offsets[i+1] = fes[i]->GetVSize();
tdof_offsets[i+1] = fes[i]->GetTrueVSize();
if (tr_fes[i])
{
rdof_offsets[j+1] = tr_fes[i]->GetVSize();
rtdof_offsets[j+1] = tr_fes[i]->GetTrueVSize();
j++;
}
}
rdof_offsets.PartialSum();
rtdof_offsets.PartialSum();
dof_offsets.PartialSum();
tdof_offsets.PartialSum();
}
void BlockStaticCondensation::Init()
{
lmat.SetSize(mesh->GetNE());
lvec.SetSize(mesh->GetNE());
for (int i = 0; i < mesh->GetNE(); i++)
{
lmat[i] = nullptr;
lvec[i] = nullptr;
}
ComputeOffsets();
S = new BlockMatrix(rdof_offsets);
S->owns_blocks = 1;
for (int i = 0; i<S->NumRowBlocks(); i++)
{
int h = rdof_offsets[i+1] - rdof_offsets[i];
for (int j = 0; j<S->NumColBlocks(); j++)
{
int w = rdof_offsets[j+1] - rdof_offsets[j];
S->SetBlock(i,j,new SparseMatrix(h, w));
}
}
y = new BlockVector(rdof_offsets);
*y = 0.;
}
void BlockStaticCondensation::GetReduceElementIndicesAndOffsets(int el,
Array<int> & trace_ldofs,
Array<int> & interior_ldofs,
Array<int> & offsets) const
{
int dim = mesh->Dimension();
offsets.SetSize(tr_fes.Size()+1); offsets = 0;
Array<int> dofs;
Array<int> faces, ori;
if (dim == 1)
{
mesh->GetElementVertices(el, faces);
}
if (dim == 2)
{
mesh->GetElementEdges(el, faces, ori);
}
else //dim = 3
{
mesh->GetElementFaces(el,faces,ori);
}
int numfaces = faces.Size();
trace_ldofs.SetSize(0);
interior_ldofs.SetSize(0);
// construct Array of bubble dofs to be extracted
int skip=0;
Array<int> tr_dofs;
Array<int> int_dofs;
for (int i = 0; i<tr_fes.Size(); i++)
{
int td = 0;
int ndof;
// if it's an L2 space (bubbles)
if (!tr_fes[i])
{
ndof = fes[i]->GetVDim()*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();
}
ndof = td;
}
else
{
Array<int> trace_dofs;
ndof = fes[i]->GetVDim()*fes[i]->GetFE(el)->GetDof();
tr_fes[i]->GetElementVDofs(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++)
{
tr_dofs[j] = skip + j;
}
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);
}
offsets.PartialSum();
}
void BlockStaticCondensation::GetReduceElementVDofs(int el,
Array<int> & rdofs) const
{
Array<int> faces, ori;
int dim = mesh->Dimension();
if (dim == 1)
{
mesh->GetElementVertices(el, faces);
}
if (dim == 2)
{
mesh->GetElementEdges(el, faces, ori);
}
else //dim = 3
{
mesh->GetElementFaces(el,faces,ori);
}
int numfaces = faces.Size();
rdofs.SetSize(0);
int skip = 0;
for (int i = 0; i<tr_fes.Size(); i++)
{
if (!tr_fes[i]) { continue; }
Array<int> vdofs;
if (IsTraceSpace[i])
{
Array<int> face_vdofs;
for (int k = 0; k < numfaces; k++)
{
int iface = faces[k];
tr_fes[i]->GetFaceVDofs(iface, face_vdofs);
vdofs.Append(face_vdofs);
}
}
else
{
tr_fes[i]->GetElementVDofs(el, vdofs);
}
for (int j=0; j<vdofs.Size(); j++)
{
vdofs[j] = (vdofs[j]>=0) ? vdofs[j]+rdof_offsets[skip] :
vdofs[j]-rdof_offsets[skip];
}
skip++;
rdofs.Append(vdofs);
}
}
void BlockStaticCondensation::GetElementVDofs(int el, Array<int> & vdofs) const
{
Array<int> faces, ori;
int dim = mesh->Dimension();
if (dim == 1)
{
mesh->GetElementVertices(el, faces);
}
if (dim == 2)
{
mesh->GetElementEdges(el, faces, ori);
}
else //dim = 3
{
mesh->GetElementFaces(el,faces,ori);
}
int numfaces = faces.Size();
vdofs.SetSize(0);
for (int i = 0; i<tr_fes.Size(); i++)
{
Array<int> dofs;
if (IsTraceSpace[i])
{
Array<int> face_vdofs;
for (int k = 0; k < numfaces; k++)
{
int iface = faces[k];
fes[i]->GetFaceVDofs(iface, face_vdofs);
dofs.Append(face_vdofs);
}
}
else
{
fes[i]->GetElementVDofs(el, dofs);
}
for (int j=0; j<dofs.Size(); j++)
{
dofs[j] = (dofs[j]>=0) ? dofs[j]+dof_offsets[i] :
dofs[j]-dof_offsets[i];
}
vdofs.Append(dofs);
}
}
void BlockStaticCondensation::GetLocalShurComplement(int el,
const Array<int> & tr_idx, const Array<int> & int_idx,
const DenseMatrix & elmat, const Vector & elvect,
DenseMatrix & rmat, Vector & rvect)
{
int rdofs = tr_idx.Size();
int idofs = int_idx.Size();
MFEM_VERIFY(idofs != 0, "Number of interior dofs is zero");
MFEM_VERIFY(rdofs != 0, "Number of interface dofs is zero");
rmat.SetSize(rdofs);
rvect.SetSize(rdofs);
DenseMatrix A_tt, A_ti, A_it, A_ii;
Vector y_t, y_i;
elmat.GetSubMatrix(tr_idx,A_tt);
elmat.GetSubMatrix(tr_idx,int_idx, A_ti);
elmat.GetSubMatrix(int_idx, tr_idx, A_it);
elmat.GetSubMatrix(int_idx, A_ii);
elvect.GetSubVector(tr_idx, y_t);
elvect.GetSubVector(int_idx, y_i);
DenseMatrixInverse lu(A_ii);
lu.Factor();
lmat[el] = new DenseMatrix(idofs,rdofs);
lvec[el] = new Vector(idofs);
lu.Mult(A_it,*lmat[el]);
lu.Mult(y_i,*lvec[el]);
// LHS
mfem::Mult(A_ti,*lmat[el],rmat);
rmat.Neg();
rmat.Add(1., A_tt);
// RHS
A_ti.Mult(*lvec[el], rvect);
rvect.Neg();
rvect.Add(1., y_t);
}
void BlockStaticCondensation::AssembleReducedSystem(int el,
DenseMatrix &elmat,
Vector & elvect)
{
// Get Shur Complement
Array<int> tr_idx, int_idx;
Array<int> offsets;
// Get local element idx and offsets for global assembly
GetReduceElementIndicesAndOffsets(el, tr_idx,int_idx, offsets);
DenseMatrix rmat, *rmatptr;
Vector rvec, *rvecptr;
// Extract the reduced matrices based on tr_idx and int_idx
if (int_idx.Size()!=0)
{
GetLocalShurComplement(el,tr_idx,int_idx, elmat, elvect, rmat, rvec);
rmatptr = &rmat;
rvecptr = &rvec;
}
else
{
rmatptr = &elmat;
rvecptr = &elvect;
}
// Assemble global mat and rhs
DofTransformation * doftrans_i, *doftrans_j;
Array<int> faces, ori;
int dim = mesh->Dimension();
if (dim == 1)
{
mesh->GetElementVertices(el, faces);
}
if (dim == 2)
{
mesh->GetElementEdges(el, faces, ori);
}
else //dim = 3
{
mesh->GetElementFaces(el,faces,ori);
}
int numfaces = faces.Size();
int skip_i=0;
for (int i = 0; i<tr_fes.Size(); i++)
{
if (!tr_fes[i]) { continue; }
Array<int> vdofs_i;
doftrans_i = nullptr;
if (IsTraceSpace[i])
{
Array<int> face_vdofs;
for (int k = 0; k < numfaces; k++)
{
int iface = faces[k];
tr_fes[i]->GetFaceVDofs(iface, face_vdofs);
vdofs_i.Append(face_vdofs);
}
}
else
{
doftrans_i = tr_fes[i]->GetElementVDofs(el, vdofs_i);
}
int skip_j=0;
for (int j = 0; j<tr_fes.Size(); j++)
{
if (!tr_fes[j]) { continue; }
Array<int> vdofs_j;
doftrans_j = nullptr;
if (IsTraceSpace[j])
{
Array<int> face_vdofs;
for (int k = 0; k < numfaces; k++)
{
int iface = faces[k];
tr_fes[j]->GetFaceVDofs(iface, face_vdofs);
vdofs_j.Append(face_vdofs);
}
}
else
{
doftrans_j = tr_fes[j]->GetElementVDofs(el, vdofs_j);
}
DenseMatrix Ae;
rmatptr->GetSubMatrix(offsets[i],offsets[i+1],
offsets[j],offsets[j+1], Ae);
if (doftrans_i || doftrans_j)
{
TransformDual(doftrans_i, doftrans_j, Ae);
}
S->GetBlock(skip_i,skip_j).AddSubMatrix(vdofs_i,vdofs_j, Ae);
skip_j++;
}
// assemble rhs
double * data = rvecptr->GetData();
Vector vec1;
// ref subvector
vec1.SetDataAndSize(&data[offsets[i]],
offsets[i+1]-offsets[i]);
if (doftrans_i)
{
doftrans_i->TransformDual(vec1);
}
y->GetBlock(skip_i).AddElementVector(vdofs_i,vec1);
skip_i++;
}
}
void BlockStaticCondensation::BuildProlongation()
{
P = new BlockMatrix(rdof_offsets, rtdof_offsets);
R = new BlockMatrix(rtdof_offsets, rdof_offsets);
P->owns_blocks = 0;
R->owns_blocks = 0;
int skip = 0;
for (int i = 0; i<nblocks; i++)
{
if (!tr_fes[i]) { continue; }
const SparseMatrix *P_ = tr_fes[i]->GetConformingProlongation();
if (P_)
{
const SparseMatrix *R_ = tr_fes[i]->GetRestrictionMatrix();
P->SetBlock(skip,skip,const_cast<SparseMatrix*>(P_));
R->SetBlock(skip,skip,const_cast<SparseMatrix*>(R_));
}
skip++;
}
}
#ifdef MFEM_USE_MPI
void BlockStaticCondensation::BuildParallelProlongation()
{
MFEM_VERIFY(parallel, "BuildParallelProlongation: wrong code path");
pP = new BlockOperator(rdof_offsets, rtdof_offsets);
R = new BlockMatrix(rtdof_offsets, rdof_offsets);
pP->owns_blocks = 0;
R->owns_blocks = 0;
int skip = 0;
for (int i = 0; i<nblocks; i++)
{
if (!tr_fes[i]) { continue; }
const HypreParMatrix *P_ =
dynamic_cast<ParFiniteElementSpace *>(tr_fes[i])->Dof_TrueDof_Matrix();
if (P_)
{
const SparseMatrix *R_ = tr_fes[i]->GetRestrictionMatrix();
pP->SetBlock(skip,skip,const_cast<HypreParMatrix*>(P_));
R->SetBlock(skip,skip,const_cast<SparseMatrix*>(R_));
}
skip++;
}
}
void BlockStaticCondensation::ParallelAssemble(BlockMatrix *m)
{
if (!pP) { BuildParallelProlongation(); }
pS = new BlockOperator(rtdof_offsets);
pS_e = new BlockOperator(rtdof_offsets);
pS->owns_blocks = 1;
pS_e->owns_blocks = 1;
HypreParMatrix * A = nullptr;
HypreParMatrix * PtAP = nullptr;
int skip_i=0;
ParFiniteElementSpace * pfes_i = nullptr;
ParFiniteElementSpace * pfes_j = nullptr;
for (int i = 0; i<nblocks; i++)
{
if (!tr_fes[i]) { continue; }
pfes_i = dynamic_cast<ParFiniteElementSpace*>(fes[i]);
HypreParMatrix * Pi = (HypreParMatrix*)(&pP->GetBlock(skip_i,skip_i));
int skip_j=0;
for (int j = 0; j<nblocks; j++)
{
if (!tr_fes[j]) { continue; }
if (m->IsZeroBlock(skip_i,skip_j)) { continue; }
if (skip_i == skip_j)
{
// Make block diagonal square hypre matrix
A = new HypreParMatrix(pfes_i->GetComm(), pfes_i->GlobalVSize(),
pfes_i->GetDofOffsets(),&m->GetBlock(skip_i,skip_i));
PtAP = RAP(A,Pi);
delete A;
pS_e->SetBlock(skip_i,skip_i,PtAP->EliminateRowsCols(*ess_tdofs[skip_i]));
}
else
{
pfes_j = dynamic_cast<ParFiniteElementSpace*>(fes[j]);
HypreParMatrix * Pj = (HypreParMatrix*)(&pP->GetBlock(skip_j,skip_j));
A = new HypreParMatrix(pfes_i->GetComm(), pfes_i->GlobalVSize(),
pfes_j->GlobalVSize(), pfes_i->GetDofOffsets(),
pfes_j->GetDofOffsets(), &m->GetBlock(skip_i,skip_j));
PtAP = RAP(Pi,A,Pj);
delete A;
pS_e->SetBlock(skip_i,skip_j,PtAP->EliminateCols(*ess_tdofs[skip_j]));
PtAP->EliminateRows(*ess_tdofs[skip_i]);
}
pS->SetBlock(skip_i,skip_j,PtAP);
skip_j++;
}
skip_i++;
}
}
#endif
void BlockStaticCondensation::ConformingAssemble(int skip_zeros)
{
Finalize(0);
if (!P) { BuildProlongation(); }
BlockMatrix * Pt = Transpose(*P);
BlockMatrix * PtA = mfem::Mult(*Pt, *S);
delete S;
if (S_e)
{
BlockMatrix *PtAe = mfem::Mult(*Pt, *S_e);
delete S_e;
S_e = PtAe;
}
delete Pt;
S = mfem::Mult(*PtA, *P);
delete PtA;
if (S_e)
{
BlockMatrix *PtAeP = mfem::Mult(*S_e, *P);
S_e = PtAeP;
}
height = S->Height();
width = S->Width();
}
void BlockStaticCondensation::Finalize(int skip_zeros)
{
if (S) { S->Finalize(skip_zeros); }
if (S_e) { S_e->Finalize(skip_zeros); }
}
void BlockStaticCondensation::FormSystemMatrix(Operator::DiagonalPolicy
diag_policy)
{
if (parallel)
{
FillEssTdofLists(ess_rtdof_list);
if (S)
{
const int remove_zeros = 0;
Finalize(remove_zeros);
ParallelAssemble(S);
delete S;
S=nullptr;
delete S_e;
S_e = nullptr;
}
}
else
{
if (!S_e)
{
bool conforming = true;
for (int i = 0; i<nblocks; i++)
{
if (!tr_fes[i]) { continue; }
const SparseMatrix *P_ = tr_fes[i]->GetConformingProlongation();
if (P_)
{
conforming = false;
break;
}
}
if (!conforming) { ConformingAssemble(0); }
const int remove_zeros = 0;
EliminateReducedTrueDofs(ess_rtdof_list, diag_policy);
Finalize(remove_zeros);
}
}
}
void BlockStaticCondensation::ConvertMarkerToReducedTrueDofs(
Array<int> & tdof_marker,
Array<int> & rtdof_marker)
{
// convert tdof_marker to dof_marker
rtdof_marker.SetSize(0);
Array<int> tdof_marker0;
Array<int> dof_marker0;
Array<int> dof_marker;
int * data = tdof_marker.GetData();
for (int i = 0; i<nblocks; i++)
{
tdof_marker0.MakeRef(&data[tdof_offsets[i]],tdof_offsets[i+1]-tdof_offsets[i]);
const SparseMatrix * R = fes[i]->GetRestrictionMatrix();
if (!R)
{
dof_marker0.MakeRef(tdof_marker0);
}
else
{
dof_marker0.SetSize(fes[i]->GetVSize());
R->BooleanMultTranspose(tdof_marker0, dof_marker0);
}
dof_marker.Append(dof_marker0);
}
int rdofs = rdof_edof.Size();
Array<int> rdof_marker(rdofs);
for (int i = 0; i < rdofs; i++)
{
rdof_marker[i] = dof_marker[rdof_edof[i]];
}
// convert rdof_marker to rtdof_marker
Array<int> rtdof_marker0;
Array<int> rdof_marker0;
int * rdata = rdof_marker.GetData();
int k=0;
for (int i = 0; i<nblocks; i++)
{
if (!tr_fes[i]) { continue; }
rdof_marker0.MakeRef(&rdata[rdof_offsets[k]],rdof_offsets[k+1]-rdof_offsets[k]);
const SparseMatrix *tr_R = tr_fes[i]->GetRestrictionMatrix();
if (!tr_R)
{
rtdof_marker0.MakeRef(rdof_marker0);
}
else
{
rtdof_marker0.SetSize(tr_fes[i]->GetTrueVSize());
tr_R->BooleanMult(rdof_marker0, rtdof_marker0);
}
rtdof_marker.Append(rtdof_marker0);
k++;
}
}
void BlockStaticCondensation::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 < rblocks; j++)
{
if (rtdof_offsets[j+1] > tdof) { break; }
}
ess_tdofs[j]->Append(tdof-rtdof_offsets[j]);
}
}
void BlockStaticCondensation::SetEssentialTrueDofs(const Array<int>
&ess_tdof_list)
{
Array<int> tdof_marker;
Array<int> rtdof_marker;
FiniteElementSpace::ListToMarker(ess_tdof_list,tdof_offsets.Last(),tdof_marker);
ConvertMarkerToReducedTrueDofs(tdof_marker, rtdof_marker);
FiniteElementSpace::MarkerToList(rtdof_marker,ess_rtdof_list);
}
void BlockStaticCondensation::EliminateReducedTrueDofs(const Array<int>
&ess_rtdof_list,
Matrix::DiagonalPolicy dpolicy)
{
MFEM_VERIFY(!parallel, "EliminateReducedTrueDofs::Wrong Code path");
if (S_e == NULL)
{
Array<int> offsets;
offsets.MakeRef( (P) ? rtdof_offsets : rdof_offsets);
S_e = new BlockMatrix(offsets);
S_e->owns_blocks = 1;
for (int i = 0; i<S_e->NumRowBlocks(); i++)
{
int h = offsets[i+1] - offsets[i];
for (int j = 0; j<S_e->NumColBlocks(); j++)
{
int w = offsets[j+1] - offsets[j];
S_e->SetBlock(i,j,new SparseMatrix(h, w));
}
}
}
S->EliminateRowCols(ess_rtdof_list,S_e,dpolicy);
}
void BlockStaticCondensation::EliminateReducedTrueDofs(Matrix::DiagonalPolicy
dpolicy)
{
EliminateReducedTrueDofs(ess_rtdof_list, dpolicy);
}
void BlockStaticCondensation::ReduceSolution(const Vector &sol,
Vector &sc_sol) const
{
MFEM_ASSERT(sol.Size() == dof_offsets.Last(), "'sol' has incorrect size");
const int nrdofs = rdof_offsets.Last();
Vector sol_r;
if (!R)
{
sc_sol.SetSize(nrdofs);
sol_r.SetDataAndSize(sc_sol.GetData(), sc_sol.Size());
}
else
{
sol_r.SetSize(nrdofs);
}
for (int i = 0; i < nrdofs; i++)
{
sol_r(i) = sol(rdof_edof[i]);
}
if (R)
{
// wrap vector into a block vector
BlockVector blsol_r(sol_r,rdof_offsets);
sc_sol.SetSize(R->Height());
R->Mult(blsol_r, sc_sol);
}
}
void BlockStaticCondensation::ReduceSystem(Vector &x, Vector &X,
Vector &B,
int copy_interior) const
{
ReduceSolution(x, X);
if (parallel)
{
B.SetSize(pP->Width());
pP->MultTranspose(*y,B);
Vector tmp(B.Size());
pS_e->Mult(X,tmp);
B-=tmp;
for (int j = 0; j<rblocks; j++)
{
if (!ess_tdofs[j]->Size()) { continue; }
HypreParMatrix *Ah = (HypreParMatrix *)(&pS->GetBlock(j,j));
Vector diag;
Ah->GetDiag(diag);
for (int i = 0; i < ess_tdofs[j]->Size(); i++)
{
int tdof = (*ess_tdofs[j])[i];
int gdof = tdof + rtdof_offsets[j];
B(gdof) = diag(tdof)*X(gdof);
}
}
}
else
{
if (!P)
{
S_e->AddMult(X,*y,-1.);
S->PartMult(ess_rtdof_list,X,*y);
B.MakeRef(*y, 0, y->Size());
}
else
{
B.SetSize(P->Width());
P->MultTranspose(*y, B);
S_e->AddMult(X,B,-1.);
S->PartMult(ess_rtdof_list,X,B);
}
}
if (!copy_interior) { X.SetSubVectorComplement(ess_rtdof_list, 0.0); }
}
void BlockStaticCondensation::ComputeSolution(const Vector &sc_sol,
Vector &sol) const
{
const int nrdofs = rdof_offsets.Last();
const int nrtdofs = rtdof_offsets.Last();
MFEM_VERIFY(sc_sol.Size() == nrtdofs, "'sc_sol' has incorrect size");
Vector sol_r;
if (parallel)
{
sol_r.SetSize(nrdofs);
pP->Mult(sc_sol, sol_r);
}
else
{
if (!P)
{
sol_r.SetDataAndSize(sc_sol.GetData(), sc_sol.Size());
}
else
{
sol_r.SetSize(nrdofs);
P->Mult(sc_sol, sol_r);
}
}
if (rdof_offsets.Last() == dof_offsets.Last())
{
sol = sol_r;
return;
}
else
{
sol.SetSize(dof_offsets.Last());
}
Vector lsr; // element (local) sc solution vector
Vector lsi; // element (local) interior solution vector
const int NE = mesh->GetNE();
Array<int> trace_vdofs;
Array<int> vdofs;
Array<int> tr_offsets;
Vector lsol;
for (int iel = 0; iel < NE; iel++)
{
lsol.SetSize(lmat[iel]->Width() + lmat[iel]->Height());
// GetReduceElementIndicesAndOffsets(iel, trace_ldofs, interior_ldofs, tr_offsets);
GetReduceElementVDofs(iel, trace_vdofs);
lsr.SetSize(trace_vdofs.Size());
sol_r.GetSubVector(trace_vdofs, lsr);
// complete the interior dofs
lsi.SetSize(lmat[iel]->Height());
lmat[iel]->Mult(lsr,lsi);
lsi.Neg();
lsi+=*lvec[iel];
Array<int> tr_idx,int_idx,idx_offs;
GetReduceElementIndicesAndOffsets(iel,tr_idx, int_idx, idx_offs);
lsol.SetSubVector(tr_idx,lsr);
lsol.SetSubVector(int_idx,lsi);
GetElementVDofs(iel, vdofs);
sol.SetSubVector(vdofs,lsol);
}
}
BlockStaticCondensation::~BlockStaticCondensation()
{
delete S_e; S_e = nullptr;
delete S; S=nullptr;
delete y; y=nullptr;
if (P) { delete P; } P=nullptr;
if (R) { delete R; } R=nullptr;
if (parallel)
{
delete pS; pS=nullptr;
delete pS_e; pS_e=nullptr;
for (int i = 0; i<rblocks; i++)
{
delete ess_tdofs[i];
}
delete pP; pP=nullptr;
}
for (int i=0; i<lmat.Size(); i++)
{
delete lmat[i]; lmat[i] = nullptr;
delete lvec[i]; lvec[i] = nullptr;
}
}
}
+191
View File
@@ -0,0 +1,191 @@
// Copyright (c) 2010-2022, 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_BLOCK_STATIC_CONDENSATION
#define MFEM_BLOCK_STATIC_CONDENSATION
#include "../config/config.hpp"
#include "fespace.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
#endif
namespace mfem
{
class BlockStaticCondensation
{
int height, width;
int nblocks; // original number of blocks
int rblocks; // reduces number of blocks
Mesh * mesh = nullptr;
bool parallel = false;
// original set of Finite Element Spaces
Array<FiniteElementSpace *> fes;
// indicates if the original space is already a trace space
Array<bool> IsTraceSpace;
// New set of "reduced" Finite Element Spaces
// (after static condensation)
Array<FiniteElementSpace *> tr_fes;
Array<int> dof_offsets;
Array<int> tdof_offsets;
Array<int> rdof_offsets;
Array<int> rtdof_offsets;
// Schur complement matrix
// S = A_ii - A_ib (A_bb)^{-1} A_bi.
BlockMatrix * S = nullptr;
BlockMatrix * S_e = nullptr;
BlockVector * y = nullptr;
Array<DenseMatrix * > lmat;
Array<Vector * > lvec;
Array<int> rdof_edof; // Map from reduced dofs to exposed dofs
Array<int> ess_rtdof_list;
BlockMatrix * P = nullptr; // Block Prolongation
BlockMatrix * R = nullptr; // Block Restriction
#ifdef MFEM_USE_MPI
BlockOperator * pS = nullptr;
BlockOperator * pS_e = nullptr;
// Block HypreParMatrix for Prolongation
BlockOperator * pP = nullptr;
#endif
bool Parallel() const { return parallel; }
// tr_idx (trace dofs indices)
// int_idx (interior dof indices)
void GetReduceElementIndicesAndOffsets(int el, Array<int> & tr_idx,
Array<int> & int_idx,
Array<int> & offsets) const;
void GetReduceElementVDofs(int el, Array<int> & rdofs) const;
void GetElementVDofs(int el, Array<int> & vdofs) const;
// S = A_ii - A_ib (A_bb)^{-1} A_bi.
// y = y_i - A_ib (A_bb)^{-1} y_b
void GetLocalShurComplement(int el, const Array<int> & tr_idx,
const Array<int> & int_idx,
const DenseMatrix & elmat, const Vector & elvect,
DenseMatrix & rmat, Vector & rvect);
void ComputeOffsets();
void BuildProlongation();
#ifdef MFEM_USE_MPI
void BuildParallelProlongation();
#endif
// ess_tdof list for each space
Array<Array<int> *> ess_tdofs;
void FillEssTdofLists(const Array<int> & ess_tdof_list);
void ConformingAssemble(int skip_zeros);
/** Restrict a marker Array on the true FE spaces dofs to a marker Array on
the reduced/trace true FE spaces dofs. */
void ConvertMarkerToReducedTrueDofs(Array<int> & tdof_marker,
Array<int> & rtdof_marker);
public:
BlockStaticCondensation(Array<FiniteElementSpace *> & fes_);
~BlockStaticCondensation();
void SetSpaces(Array<FiniteElementSpace*> & fes_);
void Init();
/** Assemble the contribution to the Schur complement from the given
element matrix 'elmat'; save the other blocks internally: A_bb_inv, A_bi,
and A_bi. */
void AssembleReducedSystem(int el, DenseMatrix &elmat,
Vector & elvect);
/// Finalize the construction of the Schur complement matrix.
void Finalize(int skip_zeros = 0);
/// Determine and save internally essential reduced true dofs.
void SetEssentialTrueDofs(const Array<int> &ess_tdof_list);
/// Eliminate the given reduced true dofs from the Schur complement matrix S.
void EliminateReducedTrueDofs(const Array<int> &ess_rtdof_list,
Matrix::DiagonalPolicy dpolicy);
void EliminateReducedTrueDofs(Matrix::DiagonalPolicy dpolicy);
bool HasEliminatedBC() const
{
#ifndef MFEM_USE_MPI
return S_e;
#else
return S_e || pS_e;
#endif
}
/// Return the serial Schur complement matrix.
BlockMatrix &GetMatrix() { return *S; }
/// Return the eliminated part of the serial Schur complement matrix.
BlockMatrix &GetMatrixElim() { return *S_e; }
#ifdef MFEM_USE_MPI
/// Return the parallel Schur complement matrix.
BlockOperator &GetParallelMatrix() { return *pS; }
/// Return the eliminated part of the parallel Schur complement matrix.
BlockOperator &GetParallelMatrixElim() { return *pS_e; }
void ParallelAssemble(BlockMatrix *m);
#endif
void FormSystemMatrix(Operator::DiagonalPolicy diag_policy);
/** Restrict a solution vector on the full FE space dofs to a vector on the
reduced/trace true FE space dofs. */
void ReduceSolution(const Vector &sol, Vector &sc_sol) const;
/** @brief Set the reduced solution `X` and r.h.s `B` vectors from the full
linear system solution `x` and r.h.s. `b` vectors.
This method should be called after the internal reduced essential dofs
have been set using SetEssentialTrueDofs() and both the Schur complement
and its eliminated part have been finalized. */
void ReduceSystem(Vector &x, Vector &X, Vector &B,
int copy_interior = 0) const;
/** Restrict a list of true FE space dofs to a list of reduced/trace true FE
space dofs. */
void ConvertListToReducedTrueDofs(const Array<int> &ess_tdof_list,
Array<int> &ess_rtdof_list) const;
/** Given a solution of the reduced system 'sc_sol' and the RHS 'b' for the
full linear system, compute the solution of the full system 'sol'. */
void ComputeSolution(const Vector &sc_sol, Vector &sol) const;
};
}
#endif
File diff suppressed because it is too large Load Diff
+220
View File
@@ -0,0 +1,220 @@
// Copyright (c) 2010-2022, 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_BLOCK_STATIC_CONDENSATION
#define MFEM_COMPLEX_BLOCK_STATIC_CONDENSATION
#include "../config/config.hpp"
#include "fespace.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
#endif
namespace mfem
{
class ComplexBlockStaticCondensation
{
int height, width;
int nblocks; // original number of blocks
int rblocks; // reduces number of blocks
Mesh * mesh = nullptr;
bool parallel = false;
// original set of Finite Element Spaces
Array<FiniteElementSpace *> fes;
// indicates if the original space is already a trace space
Array<bool> IsTraceSpace;
// New set of "reduced" Finite Element Spaces
// (after static condensation)
Array<FiniteElementSpace *> tr_fes;
Array<int> dof_offsets;
Array<int> tdof_offsets;
Array<int> rdof_offsets;
Array<int> rtdof_offsets;
// Schur complement matrix
// S = A_ii - A_ib (A_bb)^{-1} A_bi.
BlockMatrix * S_r = nullptr;
BlockMatrix * S_i = nullptr;
BlockMatrix * S_e_r = nullptr;
BlockMatrix * S_e_i = nullptr;
ComplexOperator * S = nullptr;
BlockVector * y_r = nullptr;
BlockVector * y_i = nullptr;
Vector * y = nullptr;
Array<ComplexDenseMatrix * > lmat;
Array<Vector * > lvec;
Array<int> rdof_edof; // Map from reduced dofs to exposed dofs
Array<int> ess_rtdof_list;
BlockMatrix * P = nullptr; // Block Prolongation
BlockMatrix * R = nullptr; // Block Restriction
#ifdef MFEM_USE_MPI
BlockOperator * pS_r = nullptr;
BlockOperator * pS_e_r = nullptr;
BlockOperator * pS_i = nullptr;
BlockOperator * pS_e_i = nullptr;
// Block HypreParMatrix for Prolongation
BlockOperator * pP = nullptr;
#endif
bool Parallel() const { return parallel; }
// tr_idx (trace dofs indices)
// int_idx (interior dof indices)
void GetReduceElementIndicesAndOffsets(int el, Array<int> & tr_idx,
Array<int> & int_idx,
Array<int> & offsets) const;
void GetReduceElementVDofs(int el, Array<int> & rdofs) const;
void GetElementVDofs(int el, Array<int> & vdofs) const;
// S = A_ii - A_ib (A_bb)^{-1} A_bi.
// y = y_i - A_ib (A_bb)^{-1} y_b
ComplexDenseMatrix * GetLocalShurComplement(int el, const Array<int> & tr_idx,
const Array<int> & int_idx,
const ComplexDenseMatrix & elmat,
const Vector & elvect_r,
const Vector & elvect_i,
Vector & rvect_r,
Vector & rvect_i);
void ComputeOffsets();
void BuildProlongation();
#ifdef MFEM_USE_MPI
void BuildParallelProlongation();
#endif
// ess_tdof list for each space
Array<Array<int> *> ess_tdofs;
void FillEssTdofLists(const Array<int> & ess_tdof_list);
void ConformingAssemble(int skip_zeros);
/** Restrict a marker Array on the true FE spaces dofs to a marker Array on
the reduced/trace true FE spaces dofs. */
void ConvertMarkerToReducedTrueDofs(Array<int> & tdof_marker,
Array<int> & rtdof_marker);
public:
ComplexBlockStaticCondensation(Array<FiniteElementSpace *> & fes_);
~ComplexBlockStaticCondensation();
void SetSpaces(Array<FiniteElementSpace*> & fes_);
void Init();
/** Assemble the contribution to the Schur complement from the given
element matrix 'elmat'; save the other blocks internally: A_bb_inv, A_bi,
and A_bi. */
void AssembleReducedSystem(int el, ComplexDenseMatrix &elmat,
Vector & elvect_r, Vector & elvect_i);
/// Finalize the construction of the Schur complement matrix.
void Finalize(int skip_zeros = 0);
/// Determine and save internally essential reduced true dofs.
void SetEssentialTrueDofs(const Array<int> &ess_tdof_list);
/// Eliminate the given reduced true dofs from the Schur complement matrix S.
void EliminateReducedTrueDofs(const Array<int> &ess_rtdof_list,
Matrix::DiagonalPolicy dpolicy);
void EliminateReducedTrueDofs(Matrix::DiagonalPolicy dpolicy);
bool HasEliminatedBC() const
{
#ifndef MFEM_USE_MPI
return S_e_r;
#else
return S_e_r || pS_e_r;
#endif
}
/// Return the serial Schur complement matrix.
BlockMatrix &GetMatrix_r() { return *S_r; }
BlockMatrix &GetMatrix_i() { return *S_i; }
ComplexOperator &GetComplexOperator()
{
if (!S)
{
if (parallel)
{
S = new ComplexOperator(pS_r,pS_i,false,false);
}
else
{
S = new ComplexOperator(S_r,S_i,false,false);
}
}
return *S;
}
/// Return the eliminated part of the serial Schur complement matrix.
BlockMatrix &GetMatrixElim_r() { return *S_e_r; }
BlockMatrix &GetMatrixElim_i() { return *S_e_i; }
#ifdef MFEM_USE_MPI
/// Return the parallel Schur complement matrix.
BlockOperator &GetParallelMatrix_r() { return *pS_r; }
BlockOperator &GetParallelMatrix_i() { return *pS_i; }
/// Return the eliminated part of the parallel Schur complement matrix.
BlockOperator &GetParallelMatrixElim_r() { return *pS_e_r; }
BlockOperator &GetParallelMatrixElim_i() { return *pS_e_i; }
void ParallelAssemble(BlockMatrix *m_r, BlockMatrix*m_i);
#endif
void FormSystemMatrix(Operator::DiagonalPolicy diag_policy);
/** Restrict a solution vector on the full FE space dofs to a vector on the
reduced/trace true FE space dofs. */
void ReduceSolution(const Vector &sol, Vector &sc_sol) const;
/** @brief Set the reduced solution `X` and r.h.s `B` vectors from the full
linear system solution `x` and r.h.s. `b` vectors.
This method should be called after the internal reduced essential dofs
have been set using SetEssentialTrueDofs() and both the Schur complement
and its eliminated part have been finalized. */
void ReduceSystem(Vector &x, Vector &X, Vector &B,
int copy_interior = 0) const;
/** Restrict a list of true FE space dofs to a list of reduced/trace true FE
space dofs. */
void ConvertListToReducedTrueDofs(const Array<int> &ess_tdof_list,
Array<int> &ess_rtdof_list) const;
/** Given a solution of the reduced system 'sc_sol' and the RHS 'b' for the
full linear system, compute the solution of the full system 'sol'. */
void ComputeSolution(const Vector &sc_sol, Vector &sol) const;
};
}
#endif
File diff suppressed because it is too large Load Diff
+291
View File
@@ -0,0 +1,291 @@
// Copyright (c) 2010-2022, 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_NORMALEQUATIONS
#define MFEM_COMPLEX_NORMALEQUATIONS
#include "../config/config.hpp"
#include "../linalg/linalg.hpp"
#include "complex_blockstaticcond.hpp"
namespace mfem
{
/** @brief Class representing the whole weak formulation. (Convenient for DPG or
Complex Normal Equations) */
class ComplexNormalEquations
{
protected:
ComplexBlockStaticCondensation *static_cond; ///< Owned.
bool initialized = false;
Mesh * mesh = nullptr;
int height, width;
int nblocks;
Array<int> dof_offsets;
Array<int> tdof_offsets;
/// Block matrix \f$ M \f$ to be associated with the real/imag Block bilinear form. Owned.
BlockMatrix *mat_r = nullptr;
BlockMatrix *mat_i = nullptr;
ComplexOperator * mat = nullptr;
/// BlockVectors to be associated with the real/imag Block linear form
BlockVector * y_r = nullptr;
BlockVector * y_i = nullptr;
Vector * y = nullptr;
/** @brief Block Matrix \f$ M_e \f$ used to store the eliminations
from the b.c. Owned.
\f$ M + M_e = M_{original} \f$ */
BlockMatrix *mat_e_r = nullptr;
BlockMatrix *mat_e_i = nullptr;
// Trial FE spaces
Array<FiniteElementSpace * > trial_fes;
// Flags to determine if a FiniteElementSpace is Trace
Array<int> IsTraceFes;
// Test FE Collections (Broken)
Array<FiniteElementCollection *> test_fecols;
Array<int> test_fecols_vdims;
/// Set of Trial Integrators to be applied for matrix B
Array2D<Array<BilinearFormIntegrator * > * > trial_integs_r;
Array2D<Array<BilinearFormIntegrator * > * > trial_integs_i;
/// Set of Test Space (broken) Integrators to be applied for matrix G
Array2D<Array<BilinearFormIntegrator * > * > test_integs_r;
Array2D<Array<BilinearFormIntegrator * > * > test_integs_i;
/// Set of Liniear Froem Integrators to be applied.
Array<Array<LinearFormIntegrator * > * > lfis_r;
Array<Array<LinearFormIntegrator * > * > lfis_i;
BlockMatrix * P = nullptr; // Block Prolongation
BlockMatrix * R = nullptr; // Block Restriction
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();
bool store_matrices = false;
// // Store the matrices G ^-1, B and Vector l
// // for computing the residual
// Array<ComplexDenseMatrix * > Ginv;
// Array<ComplexDenseMatrix * > Bmat;
// Store the matrix L^-1 B and Vector L^-1 l
// where G = L L^t
Array<ComplexDenseMatrix * > Bmat;
Array<Vector * > fvec;
Vector residuals;
private:
public:
/// Creates bilinear form associated with FE spaces @a *fespaces.
ComplexNormalEquations()
{
height = 0.;
width = 0;
}
ComplexNormalEquations(Array<FiniteElementSpace* > & fes_,
Array<FiniteElementCollection *> & fecol_)
{
SetSpaces(fes_,fecol_);
}
void SetTestFECollVdim(int test_fec, int vdim)
{
test_fecols_vdims[test_fec] = vdim;
}
void SetSpaces(Array<FiniteElementSpace* > & fes_,
Array<FiniteElementCollection *> & fecol_)
{
trial_fes = fes_;
test_fecols = fecol_;
test_fecols_vdims.SetSize(test_fecols.Size());
test_fecols_vdims = 1;
nblocks = trial_fes.Size();
mesh = trial_fes[0]->GetMesh();
IsTraceFes.SetSize(nblocks);
// Initialize with False
IsTraceFes = false;
for (int i = 0; i < nblocks; i++)
{
IsTraceFes[i] =
(dynamic_cast<const H1_Trace_FECollection*>(trial_fes[i]->FEColl()) ||
dynamic_cast<const ND_Trace_FECollection*>(trial_fes[i]->FEColl()) ||
dynamic_cast<const RT_Trace_FECollection*>(trial_fes[i]->FEColl()));
}
Init();
}
// Get the size of the bilinear form of the ComplexNormalEquations
int Size() const { return height; }
// Pre-allocate the internal SparseMatrix before assembly.
void AllocateMatrix() { if (mat_r == NULL) { AllocMat(); } }
/// Finalizes the matrix initialization.
void Finalize(int skip_zeros = 1);
/// Returns a reference to the sparse matrix: \f$ M \f$
BlockMatrix &BlockMat_r()
{
MFEM_VERIFY(mat_r, "mat_r is NULL and can't be dereferenced");
return *mat_r;
}
BlockMatrix &BlockMat_i()
{
MFEM_VERIFY(mat_i, "mat_i is NULL and can't be dereferenced");
return *mat_i;
}
/// Returns a reference to the sparse matrix of eliminated b.c.: \f$ M_e \f$
BlockMatrix &BlockMatElim_r()
{
MFEM_VERIFY(mat_e_r, "mat_e is NULL and can't be dereferenced");
return *mat_e_r;
}
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.
void AddTrialIntegrator(BilinearFormIntegrator *bfi_r,
BilinearFormIntegrator *bfi_i,
int trial_fes,
int test_fes);
/// Adds new Test Integrator. Assumes ownership of @a bfi.
void AddTestIntegrator(BilinearFormIntegrator *bfi_r,
BilinearFormIntegrator *bfi_i,
int test_fes0,
int test_fes1);
/// Adds new Domain LF Integrator. Assumes ownership of @a bfi.
void AddDomainLFIntegrator(LinearFormIntegrator *bfi_r,
LinearFormIntegrator *bfi_i,
int test_fes);
/// 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, OperatorHandle & A,
Vector &X, Vector &B,
int copy_interior = 0);
template <typename OpType>
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, OpType &A,
Vector &X, Vector &B,
int copy_interior = 0)
{
OperatorHandle Ah;
FormLinearSystem(ess_tdof_list, x, 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();
void StoreMatrices(bool store_matrices_ = true)
{
store_matrices = store_matrices_;
if (Bmat.Size() == 0)
{
Bmat.SetSize(mesh->GetNE());
fvec.SetSize(mesh->GetNE());
for (int i =0; i<mesh->GetNE(); i++)
{
Bmat[i] = nullptr;
fvec[i] = nullptr;
}
}
}
void EnableStaticCondensation();
Vector & ComputeResidual(const Vector & x);
/// Destroys bilinear form.
virtual ~ComplexNormalEquations();
};
} // namespace mfem
#endif
+9
View File
@@ -22,6 +22,7 @@
#include "complex_fem.hpp"
#include "convergence.hpp"
#include "lininteg.hpp"
#include "blockinteg.hpp"
#include "nonlininteg.hpp"
#include "bilininteg.hpp"
#include "fespace.hpp"
@@ -43,6 +44,12 @@
#include "transfer.hpp"
#include "fespacehierarchy.hpp"
#include "multigrid.hpp"
#include "blocklinearform.hpp"
#include "blockbilinearform.hpp"
#include "normal_equations.hpp"
#include "blockstaticcond.hpp"
#include "complex_normal_equations.hpp"
#include "complex_blockstaticcond.hpp"
#include "ceed/solvers/algebraic.hpp"
#include "lor/lor.hpp"
@@ -52,6 +59,8 @@
#include "plinearform.hpp"
#include "pbilinearform.hpp"
#include "pnonlinearform.hpp"
#include "pnormal_equations.hpp"
#include "pcomplex_normal_equations.hpp"
#endif
#ifdef MFEM_USE_SIDRE
+824
View File
@@ -0,0 +1,824 @@
// Copyright (c) 2010-2022, 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 "fem.hpp"
namespace mfem
{
void NormalEquations::Init()
{
trial_integs.SetSize(trial_fes.Size(), test_fecols.Size());
for (int i = 0; i < trial_integs.NumRows(); i++)
{
for (int j = 0; j < trial_integs.NumCols(); j++)
{
trial_integs(i,j) = new Array<BilinearFormIntegrator * >();
}
}
test_integs.SetSize(test_fecols.Size(), test_fecols.Size());
for (int i = 0; i < test_integs.NumRows(); i++)
{
for (int j = 0; j < test_integs.NumCols(); j++)
{
test_integs(i,j) = new Array<BilinearFormIntegrator * >();
}
}
lfis.SetSize(test_fecols.Size());
for (int j = 0; j < lfis.Size(); j++)
{
lfis[j] = new Array<LinearFormIntegrator * >();
}
ComputeOffsets();
mat = mat_e = NULL;
diag_policy = mfem::Operator::DIAG_ONE;
height = dof_offsets[nblocks];
width = height;
initialized = true;
static_cond = nullptr;
if (store_matrices)
{
Bmat.SetSize(mesh->GetNE());
fvec.SetSize(mesh->GetNE());
}
}
void NormalEquations::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] = trial_fes[i]->GetVSize();
tdof_offsets[i+1] = trial_fes[i]->GetTrueVSize();
}
dof_offsets.PartialSum();
tdof_offsets.PartialSum();
}
// Allocate SparseMatrix and RHS
void NormalEquations::AllocMat()
{
if (static_cond) { return; }
mat = new BlockMatrix(dof_offsets);
mat->owns_blocks = 1;
for (int i = 0; i<mat->NumRowBlocks(); i++)
{
int h = dof_offsets[i+1] - dof_offsets[i];
for (int j = 0; j<mat->NumColBlocks(); j++)
{
int w = dof_offsets[j+1] - dof_offsets[j];
mat->SetBlock(i,j,new SparseMatrix(h, w));
}
}
y = new BlockVector(dof_offsets);
*y = 0.;
}
void NormalEquations::Finalize(int skip_zeros)
{
if (mat) { mat->Finalize(skip_zeros); }
if (mat_e) { mat_e->Finalize(skip_zeros); }
if (static_cond) { static_cond->Finalize(); }
}
/// Adds new Domain BF Integrator. Assumes ownership of @a bfi.
void NormalEquations::AddTrialIntegrator(
BilinearFormIntegrator *bfi, int trial_fes, int test_fes)
{
trial_integs(trial_fes,test_fes)->Append(bfi);
}
/// Adds new Domain BF Integrator. Assumes ownership of @a bfi.
void NormalEquations::AddTestIntegrator
(BilinearFormIntegrator *bfi, int test_fes0, int test_fes1)
{
test_integs(test_fes0,test_fes1)->Append(bfi);
}
/// Adds new Domain LF Integrator. Assumes ownership of @a bfi.
void NormalEquations::AddDomainLFIntegrator(
LinearFormIntegrator *lfi, int test_fes)
{
lfis[test_fes]->Append(lfi);
}
void NormalEquations::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_ = trial_fes[i]->GetConformingProlongation();
if (P_)
{
const SparseMatrix *R_ = trial_fes[i]->GetRestrictionMatrix();
P->SetBlock(i,i,const_cast<SparseMatrix*>(P_));
R->SetBlock(i,i,const_cast<SparseMatrix*>(R_));
}
else
{
// do nothing
}
}
}
void NormalEquations::ConformingAssemble()
{
Finalize(0);
if (!P) { BuildProlongation(); }
BlockMatrix * Pt = Transpose(*P);
BlockMatrix * PtA = mfem::Mult(*Pt, *mat);
mat->owns_blocks = 0;
for (int i = 0; i<nblocks; i++)
{
for (int j = 0; j<nblocks; j++)
{
SparseMatrix * tmp = &mat->GetBlock(i,j);
if (Pt->IsZeroBlock(i,i))
{
PtA->SetBlock(i,j,tmp);
}
else
{
delete tmp;
}
}
}
delete mat;
if (mat_e)
{
BlockMatrix *PtAe = mfem::Mult(*Pt, *mat_e);
mat_e->owns_blocks = 0;
for (int i = 0; i<nblocks; i++)
{
for (int j = 0; j<nblocks; j++)
{
SparseMatrix * tmp = &mat_e->GetBlock(i,j);
if (Pt->IsZeroBlock(i,i))
{
PtAe->SetBlock(i,j,tmp);
}
else
{
delete tmp;
}
}
}
delete mat_e;
mat_e = PtAe;
}
delete Pt;
mat = mfem::Mult(*PtA, *P);
PtA->owns_blocks = 0;
for (int i = 0; i<nblocks; i++)
{
for (int j = 0; j<nblocks; j++)
{
SparseMatrix * tmp = &PtA->GetBlock(j,i);
if (P->IsZeroBlock(i,i))
{
mat->SetBlock(j,i,tmp);
}
else
{
delete tmp;
}
}
}
delete PtA;
if (mat_e)
{
BlockMatrix *PtAeP = mfem::Mult(*mat_e, *P);
mat_e->owns_blocks = 0;
for (int i = 0; i<nblocks; i++)
{
for (int j = 0; j<nblocks; j++)
{
SparseMatrix * tmp = &mat_e->GetBlock(j,i);
if (P->IsZeroBlock(i,i))
{
PtAeP->SetBlock(j,i,tmp);
}
else
{
delete tmp;
}
}
}
delete mat_e;
mat_e = PtAeP;
}
height = mat->Height();
width = mat->Width();
}
/// Assembles the form i.e. sums over all domain integrators.
void NormalEquations::Assemble(int skip_zeros)
{
ElementTransformation *eltrans;
Array<int> faces, ori;
DofTransformation * doftrans_i, *doftrans_j;
if (mat == NULL)
{
AllocMat();
}
// loop through the elements
int dim = mesh->Dimension();
DenseMatrix B, Be, G, Ge, A;
Vector vec_e, vec, Gvec, b;
Array<int> vdofs;
// loop through elements
for (int iel = 0; iel < mesh -> GetNE(); iel++)
{
if (dim == 1)
{
mesh->GetElementVertices(iel, faces);
}
if (dim == 2)
{
mesh->GetElementEdges(iel, faces, ori);
}
else //dim = 3
{
mesh->GetElementFaces(iel,faces,ori);
}
int numfaces = faces.Size();
Array<int> test_offs(test_fecols.Size()+1); test_offs[0] = 0;
Array<int> trial_offs(trial_fes.Size()+1); trial_offs = 0;
eltrans = mesh->GetElementTransformation(iel);
for (int j = 0; j < test_fecols.Size(); j++)
{
int order = test_fecols[j]->GetOrder(); // assuming uniform order
test_offs[j+1] = test_fecols_vdims[j]*test_fecols[j]->GetFE(
eltrans->GetGeometryType(),
order)->GetDof();
}
for (int j = 0; j < trial_fes.Size(); j++)
{
if (IsTraceFes[j])
{
for (int ie = 0; ie<faces.Size(); ie++)
{
trial_offs[j+1] += trial_fes[j]->GetVDim()*trial_fes[j]->GetFaceElement(
faces[ie])->GetDof();
}
}
else
{
trial_offs[j+1] = trial_fes[j]->GetVDim() * trial_fes[j]->GetFE(
iel)->GetDof();
}
}
test_offs.PartialSum();
trial_offs.PartialSum();
G.SetSize(test_offs.Last()); G = 0.0;
vec.SetSize(test_offs.Last()); vec = 0.0;
B.SetSize(test_offs.Last(),trial_offs.Last()); B = 0.0;
for (int j = 0; j < test_fecols.Size(); j++)
{
int order = test_fecols[j]->GetOrder();
eltrans = mesh->GetElementTransformation(iel);
const FiniteElement & test_fe =
*test_fecols[j]->GetFE(eltrans->GetGeometryType(), order);
for (int k = 0; k < lfis[j]->Size(); k++)
{
(*lfis[j])[k]->AssembleRHSElementVect(test_fe,*eltrans,vec_e);
vec.AddSubVector(vec_e,test_offs[j]);
}
for (int i = 0; i < test_fecols.Size(); i++)
{
int order = test_fecols[i]->GetOrder();
eltrans = mesh->GetElementTransformation(iel);
const FiniteElement & test_fe_i =
*test_fecols[i]->GetFE(eltrans->GetGeometryType(), order);
for (int k = 0; k < test_integs(i,j)->Size(); k++)
{
if (i==j)
{
(*test_integs(i,j))[k]->AssembleElementMatrix(test_fe,*eltrans,Ge);
}
else
{
(*test_integs(i,j))[k]->AssembleElementMatrix2(test_fe_i,test_fe,*eltrans,
Ge);
}
G.AddSubMatrix(test_offs[j], test_offs[i], Ge);
}
}
for (int i = 0; i < trial_fes.Size(); i++)
{
if (IsTraceFes[i])
{
for (int k = 0; k < trial_integs(i,j)->Size(); k++)
{
int face_dof_offs = 0;
for (int ie = 0; ie < numfaces; ie++)
{
int iface = faces[ie];
FaceElementTransformations * ftr = mesh->GetFaceElementTransformations(iface);
const FiniteElement & tfe = *trial_fes[i]->GetFaceElement(iface);
(*trial_integs(i,j))[k]->AssembleTraceFaceMatrix(iel,tfe,test_fe,*ftr,Be);
B.AddSubMatrix(test_offs[j], trial_offs[i]+face_dof_offs, Be);
face_dof_offs+=Be.Width();
}
}
}
else
{
const FiniteElement & fe = *trial_fes[i]->GetFE(iel);
eltrans = mesh->GetElementTransformation(iel);
for (int k = 0; k < trial_integs(i,j)->Size(); k++)
{
(*trial_integs(i,j))[k]->AssembleElementMatrix2(fe,test_fe,*eltrans,Be);
B.AddSubMatrix(test_offs[j], trial_offs[i], Be);
}
}
}
}
// Form Normal Equations B^T G^-1 B = B^T G^-1 l
Gvec.SetSize(G.Height());
b.SetSize(B.Width());
A.SetSize(B.Width());
CholeskyFactors chol(G.GetData());
chol.Factor(G.Height());
chol.LSolve(B.Height(), B.Width(), B.GetData());
chol.LSolve(vec.Size(), 1, vec.GetData());
if (store_matrices)
{
Bmat[iel] = new DenseMatrix(B);
fvec[iel] = new Vector(vec);
}
mfem::MultAtB(B,B,A);
B.MultTranspose(vec,b);
if (static_cond)
{
static_cond->AssembleReducedSystem(iel,A,b);
}
else
{
// Assembly
for (int i = 0; i<trial_fes.Size(); i++)
{
Array<int> vdofs_i;
doftrans_i = nullptr;
if (IsTraceFes[i])
{
Array<int> face_vdofs;
for (int k = 0; k < numfaces; k++)
{
int iface = faces[k];
trial_fes[i]->GetFaceVDofs(iface, face_vdofs);
vdofs_i.Append(face_vdofs);
}
}
else
{
doftrans_i = trial_fes[i]->GetElementVDofs(iel, vdofs_i);
}
for (int j = 0; j<trial_fes.Size(); j++)
{
Array<int> vdofs_j;
doftrans_j = nullptr;
if (IsTraceFes[j])
{
Array<int> face_vdofs;
for (int k = 0; k < numfaces; k++)
{
int iface = faces[k];
trial_fes[j]->GetFaceVDofs(iface, face_vdofs);
vdofs_j.Append(face_vdofs);
}
}
else
{
doftrans_j = trial_fes[j]->GetElementVDofs(iel, vdofs_j);
}
DenseMatrix Ae;
A.GetSubMatrix(trial_offs[i],trial_offs[i+1],
trial_offs[j],trial_offs[j+1], Ae);
if (doftrans_i || doftrans_j)
{
TransformDual(doftrans_i, doftrans_j, Ae);
}
mat->GetBlock(i,j).AddSubMatrix(vdofs_i,vdofs_j, Ae);
}
// assemble rhs
double * data = b.GetData();
Vector vec1;
// ref subvector
vec1.SetDataAndSize(&data[trial_offs[i]],
trial_offs[i+1]-trial_offs[i]);
if (doftrans_i)
{
doftrans_i->TransformDual(vec1);
}
y->GetBlock(i).AddElementVector(vdofs_i,vec1);
}
}
}
}
void NormalEquations::FormLinearSystem(const Array<int>
&ess_tdof_list,
Vector &x,
OperatorHandle &A, Vector &X,
Vector &B, int copy_interior)
{
FormSystemMatrix(ess_tdof_list, A);
if (static_cond)
{
// Schur complement reduction to the exposed dofs
static_cond->ReduceSystem(x, X, B, copy_interior);
}
else if (!P)
{
EliminateVDofsInRHS(ess_tdof_list, x, *y);
X.MakeRef(x, 0, x.Size());
B.MakeRef(*y, 0, y->Size());
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
}
else // non conforming space
{
B.SetSize(P->Width());
P->MultTranspose(*y, B);
double *data = y->GetData();
Vector tmp;
for (int i = 0; i<nblocks; i++)
{
if (P->IsZeroBlock(i,i))
{
int offset = tdof_offsets[i];
tmp.SetDataAndSize(&data[offset],tdof_offsets[i+1]-tdof_offsets[i]);
B.SetVector(tmp,offset);
}
}
X.SetSize(R->Height());
R->Mult(x, X);
data = x.GetData();
for (int i = 0; i<nblocks; i++)
{
if (R->IsZeroBlock(i,i))
{
int offset = tdof_offsets[i];
tmp.SetDataAndSize(&data[offset],tdof_offsets[i+1]-tdof_offsets[i]);
X.SetVector(tmp,offset);
}
}
EliminateVDofsInRHS(ess_tdof_list, X, B);
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
}
}
void NormalEquations::FormSystemMatrix(const Array<int>
&ess_tdof_list,
OperatorHandle &A)
{
if (static_cond)
{
if (!static_cond->HasEliminatedBC())
{
static_cond->SetEssentialTrueDofs(ess_tdof_list);
static_cond->FormSystemMatrix(diag_policy);
}
A.Reset(&static_cond->GetMatrix(), false);
}
else
{
if (!mat_e)
{
bool conforming = true;
for (int i = 0; i<nblocks; i++)
{
const SparseMatrix *P_ = trial_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);
}
A.Reset(mat, false);
}
}
void NormalEquations::EliminateVDofsInRHS(
const Array<int> &vdofs, const Vector &x, Vector &b)
{
mat_e->AddMult(x,b,-1.);
mat->PartMult(vdofs,x,b);
}
void NormalEquations::EliminateVDofs(const Array<int> &vdofs,
Operator::DiagonalPolicy dpolicy)
{
if (mat_e == NULL)
{
Array<int> offsets;
offsets.MakeRef( (P) ? tdof_offsets : dof_offsets);
mat_e = new BlockMatrix(offsets);
mat_e->owns_blocks = 1;
for (int i = 0; i<mat_e->NumRowBlocks(); i++)
{
int h = offsets[i+1] - offsets[i];
for (int j = 0; j<mat_e->NumColBlocks(); j++)
{
int w = offsets[j+1] - offsets[j];
mat_e->SetBlock(i,j,new SparseMatrix(h, w));
}
}
}
mat->EliminateRowCols(vdofs,mat_e,diag_policy);
}
void NormalEquations::RecoverFEMSolution(const Vector &X,
Vector &x)
{
if (static_cond)
{
// Private dofs back solve
static_cond->ComputeSolution(X, x);
}
else if (!P)
{
x.SyncMemory(X);
}
else
{
x.SetSize(P->Height());
P->Mult(X, x);
double *data = X.GetData();
Vector tmp;
for (int i = 0; i<nblocks; i++)
{
if (P->IsZeroBlock(i,i))
{
int offset = tdof_offsets[i];
tmp.SetDataAndSize(&data[offset],tdof_offsets[i+1]-tdof_offsets[i]);
x.SetVector(tmp,offset);
}
}
}
}
void NormalEquations::ReleaseInitMemory()
{
if (initialized)
{
for (int k = 0; k< trial_integs.NumRows(); k++)
{
for (int l = 0; l<trial_integs.NumCols(); l++)
{
for (int i = 0; i<trial_integs(k,l)->Size(); i++)
{
delete (*trial_integs(k,l))[i];
}
delete trial_integs(k,l);
}
}
trial_integs.DeleteAll();
for (int k = 0; k < test_integs.NumRows(); k++)
{
for (int l = 0; l < test_integs.NumCols(); l++)
{
for (int i = 0; i < test_integs(k,l)->Size(); i++)
{
delete (*test_integs(k,l))[i];
}
delete test_integs(k,l);
}
}
test_integs.DeleteAll();
for (int k = 0; k < lfis.Size(); k++)
{
for (int i = 0; i < lfis[k]->Size(); i++)
{
delete (*lfis[k])[i];
}
delete lfis[k];
}
lfis.DeleteAll();
}
}
void NormalEquations::Update()
{
delete mat_e; mat_e = nullptr;
delete mat; mat = nullptr;
delete y; y = nullptr;
if (P)
{
delete P; P = nullptr;
delete R; R = nullptr;
}
delete static_cond;
static_cond = NULL;
ComputeOffsets();
diag_policy = mfem::Operator::DIAG_ONE;
height = dof_offsets[nblocks];
width = height;
initialized = true;
if (store_matrices)
{
for (int i = 0; i<Bmat.Size(); i++)
{
delete Bmat[i]; Bmat[i] = nullptr;
delete fvec[i]; fvec[i] = nullptr;
}
Bmat.SetSize(mesh->GetNE());
fvec.SetSize(mesh->GetNE());
for (int i = 0; i<Bmat.Size(); i++)
{
Bmat[i] = nullptr;
fvec[i] = nullptr;
}
}
}
void NormalEquations::EnableStaticCondensation()
{
static_cond = new BlockStaticCondensation(trial_fes);
}
Vector & NormalEquations::ComputeResidual(const BlockVector & x)
{
// Element vector of trial space size
Vector u;
Array<int> vdofs;
Array<int> faces, ori;
int dim = mesh->Dimension();
residuals.SetSize(mesh->GetNE());
// loop through elements
for (int iel = 0; iel < mesh -> GetNE(); iel++)
{
if (dim == 1)
{
mesh->GetElementVertices(iel, faces);
}
if (dim == 2)
{
mesh->GetElementEdges(iel, faces, ori);
}
else //dim = 3
{
mesh->GetElementFaces(iel,faces,ori);
}
int numfaces = faces.Size();
Array<int> trial_offs(trial_fes.Size()+1); trial_offs = 0;
for (int j = 0; j < trial_fes.Size(); j++)
{
if (IsTraceFes[j])
{
for (int ie = 0; ie<faces.Size(); ie++)
{
trial_offs[j+1] += trial_fes[j]->GetFaceElement(faces[ie])->GetDof();
}
}
else
{
trial_offs[j+1] = trial_fes[j]->GetVDim() * trial_fes[j]->GetFE(
iel)->GetDof();
}
}
trial_offs.PartialSum();
u.SetSize(trial_offs.Last());
double * data = u.GetData();
DofTransformation * doftrans = nullptr;
for (int i = 0; i<trial_fes.Size(); i++)
{
vdofs.SetSize(0);
doftrans = nullptr;
if (IsTraceFes[i])
{
Array<int> face_vdofs;
for (int k = 0; k < numfaces; k++)
{
int iface = faces[k];
trial_fes[i]->GetFaceVDofs(iface, face_vdofs);
vdofs.Append(face_vdofs);
}
}
else
{
doftrans = trial_fes[i]->GetElementVDofs(iel, vdofs);
}
Vector vec1;
vec1.SetDataAndSize(&data[trial_offs[i]],
trial_offs[i+1]-trial_offs[i]);
x.GetBlock(i).GetSubVector(vdofs,vec1);
if (doftrans)
{
doftrans->InvTransformPrimal(vec1);
}
} // end of loop through trial spaces
Vector v(Bmat[iel]->Height());
Bmat[iel]->Mult(u,v);
v -= *fvec[iel];
residuals[iel] = v.Norml2();
} // end of loop through elements
return residuals;
}
NormalEquations::~NormalEquations()
{
delete mat_e; mat_e = nullptr;
delete mat; mat = nullptr;
delete y; y = nullptr;
ReleaseInitMemory();
if (P)
{
delete P;
delete R;
}
delete static_cond;
if (store_matrices)
{
for (int i = 0; i<mesh->GetNE(); i++)
{
delete Bmat[i]; Bmat[i] = nullptr;
delete fvec[i]; fvec[i] = nullptr;
}
}
}
} // namespace mfem
+255
View File
@@ -0,0 +1,255 @@
// Copyright (c) 2010-2022, 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_NORMALEQUATIONS
#define MFEM_NORMALEQUATIONS
#include "../config/config.hpp"
#include "../linalg/linalg.hpp"
#include "blockstaticcond.hpp"
namespace mfem
{
/** @brief Class representing the whole weak formulation. (Convenient for DPG or
Normal Equations) */
class NormalEquations
{
protected:
BlockStaticCondensation *static_cond; ///< Owned.
bool initialized = false;
Mesh * mesh = nullptr;
int height, width;
int nblocks;
Array<int> dof_offsets;
Array<int> tdof_offsets;
/// Block matrix \f$ M \f$ to be associated with the Block bilinear form. Owned.
BlockMatrix *mat = nullptr;
/// BlockVector to be associated with the Block linear form
BlockVector * y = nullptr;
/** @brief Block Matrix \f$ M_e \f$ used to store the eliminations
from the b.c. Owned.
\f$ M + M_e = M_{original} \f$ */
BlockMatrix *mat_e = nullptr;
// Trial FE spaces
Array<FiniteElementSpace * > trial_fes;
// Flags to determine if a FiniteElementSpace is Trace
Array<int> IsTraceFes;
// Test FE Collections (Broken)
Array<FiniteElementCollection *> test_fecols;
Array<int> test_fecols_vdims;
/// Set of Trial Integrators to be applied for matrix B
Array2D<Array<BilinearFormIntegrator * > * > trial_integs;
/// Set of Test Space (broken) Integrators to be applied for matrix G
Array2D<Array<BilinearFormIntegrator * > * > test_integs;
/// Set of Liniear Froem Integrators to be applied.
Array<Array<LinearFormIntegrator * > * > lfis;
BlockMatrix * P = nullptr; // Block Prolongation
BlockMatrix * R = nullptr; // Block Restriction
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();
bool store_matrices = false;
// Store the matrix L^-1 B and Vector L^-1 l
// where G = L L^t
Array<DenseMatrix * > Bmat;
Array<Vector * > fvec;
Vector residuals;
private:
public:
/// Creates bilinear form associated with FE spaces @a *fespaces.
NormalEquations()
{
height = 0.;
width = 0;
}
NormalEquations(Array<FiniteElementSpace* > & fes_,
Array<FiniteElementCollection *> & fecol_)
{
SetSpaces(fes_,fecol_);
}
void SetTestFECollVdim(int test_fec, int vdim)
{
test_fecols_vdims[test_fec] = vdim;
}
void SetSpaces(Array<FiniteElementSpace* > & fes_,
Array<FiniteElementCollection *> & fecol_)
{
trial_fes = fes_;
test_fecols = fecol_;
test_fecols_vdims.SetSize(test_fecols.Size());
test_fecols_vdims = 1;
nblocks = trial_fes.Size();
mesh = trial_fes[0]->GetMesh();
IsTraceFes.SetSize(nblocks);
// Initialize with False
IsTraceFes = false;
for (int i = 0; i < nblocks; i++)
{
IsTraceFes[i] =
(dynamic_cast<const H1_Trace_FECollection*>(trial_fes[i]->FEColl()) ||
dynamic_cast<const ND_Trace_FECollection*>(trial_fes[i]->FEColl()) ||
dynamic_cast<const RT_Trace_FECollection*>(trial_fes[i]->FEColl()));
}
Init();
}
// Get the size of the bilinear form of the NormalEquations
int Size() const { return height; }
// Pre-allocate the internal SparseMatrix before assembly.
void AllocateMatrix() { if (mat == NULL) { AllocMat(); } }
/// Finalizes the matrix initialization.
void Finalize(int skip_zeros = 1);
/// Returns a reference to the sparse matrix: \f$ M \f$
BlockMatrix &BlockMat()
{
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
return *mat;
}
/// Returns a reference to the sparse matrix of eliminated b.c.: \f$ M_e \f$
BlockMatrix &BlockMatElim()
{
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
return *mat_e;
}
/// Adds new Trial Integrator. Assumes ownership of @a bfi.
void AddTrialIntegrator(BilinearFormIntegrator *bfi, int trial_fes,
int test_fes);
/// Adds new Test Integrator. Assumes ownership of @a bfi.
void AddTestIntegrator(BilinearFormIntegrator *bfi, int test_fes0,
int test_fes1);
/// Adds new Domain LF Integrator. Assumes ownership of @a bfi.
void AddDomainLFIntegrator(LinearFormIntegrator *bfi, int test_fes);
/// 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,
OperatorHandle &A, Vector &X,
Vector &B, int copy_interior = 0);
template <typename OpType>
void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
OpType &A, Vector &X, Vector &B,
int copy_interior = 0)
{
OperatorHandle Ah;
FormLinearSystem(ess_tdof_list, x, 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, Vector &b);
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();
void StoreMatrices(bool store_matrices_ = true)
{
store_matrices = store_matrices_;
if (Bmat.Size() == 0)
{
Bmat.SetSize(mesh->GetNE());
fvec.SetSize(mesh->GetNE());
for (int i =0; i<mesh->GetNE(); i++)
{
Bmat[i] = nullptr;
fvec[i] = nullptr;
}
}
}
void EnableStaticCondensation();
Vector & ComputeResidual(const BlockVector & x);
/// Destroys bilinear form.
virtual ~NormalEquations();
};
} // namespace mfem
#endif
+296
View File
@@ -0,0 +1,296 @@
// Copyright (c) 2010-2022, 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 "../config/config.hpp"
#ifdef MFEM_USE_MPI
#include "fem.hpp"
namespace mfem
{
void ComplexParNormalEquations::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 ComplexParNormalEquations::Assemble(int skip_zeros)
{
ComplexNormalEquations::Assemble(skip_zeros);
}
void ComplexParNormalEquations::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(trial_pfes[i]->GetComm(), trial_pfes[i]->GlobalVSize(),
trial_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(trial_pfes[i]->GetComm(), trial_pfes[i]->GlobalVSize(),
trial_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(trial_pfes[i]->GetComm(), trial_pfes[i]->GlobalVSize(),
trial_pfes[j]->GlobalVSize(), trial_pfes[i]->GetDofOffsets(),
trial_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(trial_pfes[i]->GetComm(), trial_pfes[i]->GlobalVSize(),
trial_pfes[j]->GlobalVSize(), trial_pfes[i]->GetDofOffsets(),
trial_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 ComplexParNormalEquations::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_ = trial_pfes[i]->Dof_TrueDof_Matrix();
P->SetBlock(i,i,P_);
const SparseMatrix * R_ = trial_pfes[i]->GetRestrictionMatrix();
R->SetBlock(i,i,const_cast<SparseMatrix*>(R_));
}
}
void ComplexParNormalEquations::FormLinearSystem(const Array<int>
&ess_tdof_list,
Vector &x,
OperatorHandle &A,
Vector &X, Vector &B,
int copy_interior)
{
FormSystemMatrix(ess_tdof_list, A);
if (static_cond)
{
static_cond->ReduceSystem(x, X, B, copy_interior);
}
else
{
int n = P->Width();
B.SetSize(2*n);
double * bdata = B.GetData();
Vector B_r(bdata,n);
Vector B_i(&bdata[n],n);
P->MultTranspose(*y_r,B_r);
P->MultTranspose(*y_i,B_i);
int m = R->Height();
X.SetSize(2*m);
double * Xdata = X.GetData();
Vector X_r(Xdata,m);
Vector X_i(&Xdata[m],m);
Vector x_r(x.GetData(),x.Size()/2);
Vector x_i(&x.GetData()[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 ComplexParNormalEquations::FormSystemMatrix(const Array<int>
&ess_tdof_list,
OperatorHandle &A)
{
if (static_cond)
{
if (!static_cond->HasEliminatedBC())
{
static_cond->SetEssentialTrueDofs(ess_tdof_list);
static_cond->FormSystemMatrix(Operator::DiagonalPolicy::DIAG_ONE);
}
A.Reset(&static_cond->GetComplexOperator(), false);
}
else
{
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 ComplexParNormalEquations::RecoverFEMSolution(const Vector &X,
Vector &x)
{
if (static_cond)
{
static_cond->ComputeSolution(X, x);
}
else
{
int n = P->Height();
int m = P->Width();
x.SetSize(2*n);
double * xdata = x.GetData();
double * Xdata = X.GetData();
Vector x_r(xdata,n);
Vector x_i(&xdata[n],n);
Vector X_r(Xdata,m);
Vector X_i(&Xdata[m],m);
P->Mult(X_r, x_r);
P->Mult(X_i, x_i);
}
}
void ComplexParNormalEquations::Update()
{
ComplexNormalEquations::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;
}
ComplexParNormalEquations::~ComplexParNormalEquations()
{
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
+121
View File
@@ -0,0 +1,121 @@
// Copyright (c) 2010-2022, 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_NORMALEQUATIONS
#define MFEM_PCOMPLEX_NORMALEQUATIONS
#include "../config/config.hpp"
#include "../linalg/linalg.hpp"
#ifdef MFEM_USE_MPI
#include <mpi.h>
#include "pfespace.hpp"
#include "complex_normal_equations.hpp"
namespace mfem
{
/** @brief Class representing the whole weak formulation. (Convenient for DPG or
Normal Equations) */
class ComplexParNormalEquations : public ComplexNormalEquations
{
protected:
// Trial FE spaces
Array<ParFiniteElementSpace * > trial_pfes;
// ess_tdof list for each space
Array<Array<int> *> ess_tdofs;
// // split ess_tdof_list give in global tdof (for all spaces)
// // to individual lists for each space
// // (this can be changed i.e., the lists to be given by the user)
void FillEssTdofLists(const Array<int> & ess_tdof_list);
// Block operator of HypreParMatrix
BlockOperator * P = nullptr; // Block Prolongation
BlockMatrix * R = nullptr; // Block Restriction
// // Block operator of HypreParMatrix
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();
private:
public:
ComplexParNormalEquations() {}
/// Creates bilinear form associated with FE spaces @a *fespaces.
ComplexParNormalEquations(Array<ParFiniteElementSpace* > & trial_pfes_,
Array<FiniteElementCollection* > & fecol_)
: ComplexNormalEquations()
{
SetParSpaces(trial_pfes_,fecol_);
}
void SetParSpaces(Array<ParFiniteElementSpace* > & trial_pfes_,
Array<FiniteElementCollection* > & fecol_)
{
trial_pfes = trial_pfes_;
ess_tdofs.SetSize(trial_pfes.Size());
Array<FiniteElementSpace * > trial_sfes(trial_pfes.Size());
for (int i = 0; i<trial_sfes.Size(); i++)
{
trial_sfes[i] = (FiniteElementSpace *)trial_pfes[i];
ess_tdofs[i] = new Array<int>();
}
SetSpaces(trial_sfes,fecol_);
}
/// 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,
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 ~ComplexParNormalEquations();
};
} // namespace mfem
#endif // MFEM_USE_MPI
#endif
+220
View File
@@ -0,0 +1,220 @@
// Copyright (c) 2010-2022, 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 "../config/config.hpp"
#ifdef MFEM_USE_MPI
#include "fem.hpp"
namespace mfem
{
void ParNormalEquations::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 ParNormalEquations::Assemble(int skip_zeros)
{
NormalEquations::Assemble(skip_zeros);
}
void ParNormalEquations::ParallelAssemble(BlockMatrix *m)
{
if (!P) { BuildProlongation(); }
p_mat = new BlockOperator(tdof_offsets);
p_mat_e = new BlockOperator(tdof_offsets);
p_mat->owns_blocks = 1;
p_mat_e->owns_blocks = 1;
HypreParMatrix * A = nullptr;
HypreParMatrix * PtAP = nullptr;
for (int i = 0; i<nblocks; i++)
{
HypreParMatrix * Pi = (HypreParMatrix*)(&P->GetBlock(i,i));
for (int j = 0; j<nblocks; j++)
{
if (m->IsZeroBlock(i,j)) { continue; }
if (i == j)
{
// Make block diagonal square hypre matrix
A = new HypreParMatrix(trial_pfes[i]->GetComm(), trial_pfes[i]->GlobalVSize(),
trial_pfes[i]->GetDofOffsets(),&m->GetBlock(i,i));
PtAP = RAP(A,Pi);
delete A;
p_mat_e->SetBlock(i,i,PtAP->EliminateRowsCols(*ess_tdofs[i]));
}
else
{
HypreParMatrix * Pj = (HypreParMatrix*)(&P->GetBlock(j,j));
A = new HypreParMatrix(trial_pfes[i]->GetComm(), trial_pfes[i]->GlobalVSize(),
trial_pfes[j]->GlobalVSize(), trial_pfes[i]->GetDofOffsets(),
trial_pfes[j]->GetDofOffsets(), &m->GetBlock(i,j));
PtAP = RAP(Pi,A,Pj);
delete A;
p_mat_e->SetBlock(i,j,PtAP->EliminateCols(*ess_tdofs[j]));
PtAP->EliminateRows(*ess_tdofs[i]);
}
p_mat->SetBlock(i,j,PtAP);
}
}
}
void ParNormalEquations::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_ = trial_pfes[i]->Dof_TrueDof_Matrix();
P->SetBlock(i,i,P_);
const SparseMatrix * R_ = trial_pfes[i]->GetRestrictionMatrix();
R->SetBlock(i,i,const_cast<SparseMatrix*>(R_));
}
}
void ParNormalEquations::FormLinearSystem(const Array<int>
&ess_tdof_list,
Vector &x,
OperatorHandle &A, Vector &X,
Vector &B, int copy_interior)
{
FormSystemMatrix(ess_tdof_list, A);
if (static_cond)
{
static_cond->ReduceSystem(x, X, B, copy_interior);
}
else
{
B.SetSize(P->Width());
P->MultTranspose(*y,B);
X.SetSize(R->Height());
R->Mult(x,X);
// eliminate tdof is RHS
// B -= Ae*X
Vector tmp(B.Size());
p_mat_e->Mult(X,tmp);
B-=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(gdof) = X(gdof); // diagonal policy in always one in parallel
}
}
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
}
}
void ParNormalEquations::FormSystemMatrix(const Array<int>
&ess_tdof_list,
OperatorHandle &A)
{
if (static_cond)
{
if (!static_cond->HasEliminatedBC())
{
static_cond->SetEssentialTrueDofs(ess_tdof_list);
static_cond->FormSystemMatrix(Operator::DiagonalPolicy::DIAG_ONE);
}
A.Reset(&static_cond->GetParallelMatrix(), false);
}
else
{
FillEssTdofLists(ess_tdof_list);
if (mat)
{
const int remove_zeros = 0;
Finalize(remove_zeros);
ParallelAssemble(mat);
delete mat;
mat = nullptr;
delete mat_e;
mat_e = nullptr;
}
A.Reset(p_mat,false);
}
}
void ParNormalEquations::RecoverFEMSolution(const Vector &X,
Vector &x)
{
if (static_cond)
{
static_cond->ComputeSolution(X, x);
}
else
{
x.SetSize(P->Height());
P->Mult(X, x);
}
}
void ParNormalEquations::Update()
{
NormalEquations::Update();
delete p_mat_e;
p_mat_e = 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;
}
ParNormalEquations::~ParNormalEquations()
{
delete p_mat_e;
p_mat_e = 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
+120
View File
@@ -0,0 +1,120 @@
// Copyright (c) 2010-2022, 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_PNORMALEQUATIONS
#define MFEM_PNORMALEQUATIONS
#include "../config/config.hpp"
#include "../linalg/linalg.hpp"
#ifdef MFEM_USE_MPI
#include <mpi.h>
#include "pfespace.hpp"
#include "normal_equations.hpp"
namespace mfem
{
/** @brief Class representing the whole weak formulation. (Convenient for DPG or
Normal Equations) */
class ParNormalEquations : public NormalEquations
{
protected:
// Trial FE spaces
Array<ParFiniteElementSpace * > trial_pfes;
// ess_tdof list for each space
Array<Array<int> *> ess_tdofs;
// split ess_tdof_list give in global tdof (for all spaces)
// to individual lists for each space
// (this can be changed i.e., the lists to be given by the user)
void FillEssTdofLists(const Array<int> & ess_tdof_list);
// Block operator of HypreParMatrix
BlockOperator * P = nullptr; // Block Prolongation
BlockMatrix * R = nullptr; // Block Restriction
// Block operator of HypreParMatrix
BlockOperator * p_mat = nullptr;
BlockOperator * p_mat_e = nullptr;
void BuildProlongation();
private:
public:
ParNormalEquations() {}
/// Creates bilinear form associated with FE spaces @a *fespaces.
ParNormalEquations(Array<ParFiniteElementSpace* > & trial_pfes_,
Array<FiniteElementCollection* > & fecol_)
: NormalEquations()
{
SetParSpaces(trial_pfes_,fecol_);
}
void SetParSpaces(Array<ParFiniteElementSpace* > & trial_pfes_,
Array<FiniteElementCollection* > & fecol_)
{
trial_pfes = trial_pfes_;
ess_tdofs.SetSize(trial_pfes.Size());
Array<FiniteElementSpace * > trial_sfes(trial_pfes.Size());
for (int i = 0; i<trial_sfes.Size(); i++)
{
trial_sfes[i] = (FiniteElementSpace *)trial_pfes[i];
ess_tdofs[i] = new Array<int>();
}
SetSpaces(trial_sfes,fecol_);
}
/// 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);
void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
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 ~ParNormalEquations();
};
} // namespace mfem
#endif // MFEM_USE_MPI
#endif
+2
View File
@@ -15,6 +15,7 @@ list(APPEND SRCS
blockoperator.cpp
blockvector.cpp
complex_operator.cpp
complex_densemat.cpp
constraints.cpp
densemat.cpp
symmat.cpp
@@ -34,6 +35,7 @@ list(APPEND HDRS
blockoperator.hpp
blockvector.hpp
complex_operator.hpp
complex_densemat.hpp
constraints.hpp
densemat.hpp
dinvariants.hpp
+90 -1
View File
@@ -15,7 +15,7 @@
#include "sparsemat.hpp"
#include "blockvector.hpp"
#include "blockmatrix.hpp"
#include "../fem/fespace.hpp"
namespace mfem
{
@@ -320,6 +320,55 @@ void BlockMatrix::EliminateRowCol(Array<int> & ess_bc_dofs, Vector & sol,
}
}
void BlockMatrix::EliminateRowCols(Array<int> vdofs, BlockMatrix *Ae,
DiagonalPolicy dpolicy)
{
MFEM_VERIFY(nRowBlocks == nColBlocks,
"BlockMatrix::EliminateRowCols supported only for"
"nRowBlocks = nColBlocks");
std::vector<Array<int>> cols(nRowBlocks);
std::vector<Array<int>> rows(nRowBlocks);
SparseMatrix * tmp = nullptr;
for (int k = 0; k < vdofs.Size(); k++)
{
int vdof = (vdofs[k]) >=0 ? vdofs[k] : -1 - vdofs[k];
// find block
int iblock, dof;
findGlobalCol(vdof,iblock,dof);
cols[iblock].Append(dof);
tmp = &GetBlock(iblock,iblock);
if (tmp)
{
tmp->EliminateRowCol(dof,Ae->GetBlock(iblock,iblock), dpolicy);
}
}
// Eliminate col from off-diagonal blocks
for (int j = 0; j<nColBlocks; j++)
{
if (!cols[j].Size()) { continue; }
Array<int> colmarker;
int blocksize = col_offsets[j+1] - col_offsets[j];
mfem::FiniteElementSpace::ListToMarker(cols[j],blocksize,colmarker);
for (int i = 0; i<nRowBlocks; i++)
{
if (i == j) { continue; }
tmp = &GetBlock(i,j);
if (tmp) { tmp->EliminateCols(colmarker,Ae->GetBlock(i,j)); }
for (int k = 0; k < cols[j].Size(); k++)
{
tmp = &GetBlock(j,i);
if (tmp) { tmp->EliminateRow(cols[j][k]); }
}
}
}
}
void BlockMatrix::EliminateZeroRows(const double threshold)
{
MFEM_VERIFY(nRowBlocks == nColBlocks, "not a square matrix");
@@ -471,6 +520,46 @@ void BlockMatrix::AddMultTranspose(const Vector & x, Vector & y,
}
}
void BlockMatrix::PartMult(const Array<int> &rows, const Vector &x,
Vector &y) const
{
Array<int> cols;
Vector srow;
for (int i = 0; i<rows.Size(); i++)
{
int dof = (rows[i]>=0) ? rows[i] : -1-rows[i];
GetRow(dof,cols,srow);
double s=0.0;
for (int k = 0; k <cols.Size(); k++)
{
s += srow[k] * x[cols[k]];
}
y[dof] = s;
}
}
void BlockMatrix::PartAddMult(const Array<int> &rows, const Vector &x,
Vector &y,
const double a) const
{
Array<int> cols;
Vector srow;
for (int i = 0; i<rows.Size(); i++)
{
int dof = (rows[i]>=0) ? rows[i] : -1-rows[i];
GetRow(dof,cols,srow);
double s=0.0;
for (int k = 0; k <cols.Size(); k++)
{
s += srow[k] * x[cols[k]];
}
y[dof] += a * s;
}
}
SparseMatrix * BlockMatrix::CreateMonolithic() const
{
int nnz = NumNonZeroElems();
+9
View File
@@ -71,6 +71,9 @@ public:
treated according to that policy. */
void EliminateRowCol(int rc, DiagonalPolicy dpolicy = DIAG_ONE);
void EliminateRowCols(Array<int> vdofs, BlockMatrix *Ae,
DiagonalPolicy dpolicy = DIAG_ONE);
//! Symmetric elimination of the marked degree of freedom.
/**
@param ess_bc_dofs marker of the degree of freedom to be eliminated
@@ -133,11 +136,17 @@ public:
const double val = 1.) const;
///@}
void PartMult(const Array<int> &rows, const Vector &x, Vector &y) const;
void PartAddMult(const Array<int> &rows, const Vector &x, Vector &y,
const double a=1.0) const;
//! Destructor
virtual ~BlockMatrix();
//! If owns_blocks the SparseMatrix objects Aij will be deallocated.
int owns_blocks;
virtual Type GetType() const { return MFEM_Block_Matrix; }
private:
//! Given a global row iglobal finds to which row iloc in block iblock belongs to.
inline void findGlobalRow(int iglobal, int & iblock, int & iloc) const;
+2
View File
@@ -113,6 +113,8 @@ public:
//! delete all blocks that are set (non-NULL); the default value is zero.
int owns_blocks;
virtual Type GetType() const { return MFEM_Block_Operator; }
private:
//! Number of block rows
int nRowBlocks;
File diff suppressed because it is too large Load Diff
+252
View File
@@ -0,0 +1,252 @@
// Copyright (c) 2010-2022, 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_DENSEMAT
#define MFEM_COMPLEX_DENSEMAT
#include "complex_operator.hpp"
#include <complex>
namespace mfem
{
/** @brief Specialization of the ComplexOperator built from a pair of Dense
Matrices.
The purpose of this specialization is to support the inverse of a
ComplexDenseMatrix and various MatMat operations
See ComplexOperator documentation for more information.
Note: Only the Hermitian convention is supported
*/
class ComplexDenseMatrix : public ComplexOperator
{
public:
ComplexDenseMatrix(DenseMatrix * A_Real, DenseMatrix * A_Imag,
bool ownReal, bool ownImag)
: ComplexOperator(A_Real, A_Imag, ownReal, ownImag)
{ }
virtual DenseMatrix & real();
virtual DenseMatrix & imag();
virtual const DenseMatrix & real() const;
virtual const DenseMatrix & imag() const;
/** Combine the blocks making up this complex operator into a single
DenseMatrix. Note that this combined operator requires roughly
twice the memory of the block structured operator. */
DenseMatrix * GetSystemMatrix() const;
virtual Type GetType() const { return Complex_DenseMat; }
ComplexDenseMatrix * ComputeInverse();
};
/// Matrix matrix multiplication. A = B * C.
ComplexDenseMatrix * Mult(const ComplexDenseMatrix &B,
const ComplexDenseMatrix &C);
/// Multiply the Complex transpose of a matrix A with a matrix B. A^H*B
ComplexDenseMatrix * MultAtB(const ComplexDenseMatrix &A,
const ComplexDenseMatrix &B);
/** Abstract class that can compute factorization of external data and perform various
operations with the factored data. */
class ComplexFactors
{
protected:
// returns a new complex array
std::complex<double> * RealToComplex(int m, const double * x_r,
const double * x_i) const;
// copies the given complex array to real and imag arrays
void ComplexToReal(int m, const std::complex<double> * x, double * x_r,
double * x_i) const;
public:
double *data_r = nullptr;
double *data_i = nullptr;
std::complex<double> * data = nullptr;
ComplexFactors() { }
ComplexFactors(double *data_r_, double *data_i_)
: data_r(data_r_), data_i(data_i_) { }
void SetComplexData(int m);
void ResetComplexData(int m)
{
delete [] data; data = nullptr;
SetComplexData(m);
}
virtual bool Factor(int m, double TOL = 0.0)
{
mfem_error("ComplexFactors::ComplexFactors(...)");
return false;
}
virtual std::complex<double> Det(int m) const
{
mfem_error("Factors::Det(...)");
return 0.;
}
virtual void Solve(int m, int n, double *X_r, double * X_i) const
{
mfem_error("Factors::Solve(...)");
}
virtual void GetInverseMatrix(int m, double *X_r, double * X_i) const
{
mfem_error("Factors::GetInverseMatrix(...)");
}
virtual ~ComplexFactors()
{
delete [] data; data = nullptr;
}
};
/** Class that computes factorization of external data and perform various
operations with the factored data. */
class ComplexLUFactors : public ComplexFactors
{
public:
int *ipiv;
#ifdef MFEM_USE_LAPACK
static const int ipiv_base = 1;
#else
static const int ipiv_base = 0;
#endif
/** With this constructor, the (public) data and ipiv members should be set
explicitly before calling class methods. */
ComplexLUFactors(): ComplexFactors() { }
ComplexLUFactors(double *data_r_,double * data_i, int *ipiv_)
: ComplexFactors(data_r_, data_i), ipiv(ipiv_) { }
/**
* @brief Compute the LU factorization of the current matrix
*
* Factorize the current matrix of size (m x m) overwriting it with the
* LU factors. The factorization is such that L.U = P.A, where A is the
* original matrix and P is a permutation matrix represented by ipiv.
*
* @param [in] m size of the square matrix
* @param [in] TOL optional fuzzy comparison tolerance. Defaults to 0.0.
*
* @return status set to true if successful, otherwise, false.
*/
virtual bool Factor(int m, double TOL = 0.0);
/** Assuming L.U = P.A factored data of size (m x m), compute |A|
from the diagonal values of U and the permutation information. */
virtual std::complex<double> Det(int m) const;
/** Assuming L.U = P.A factored data of size (m x m), compute X <- A X,
for a matrix X of size (m x n). */
void Mult(int m, int n, double *X_r, double * X_i) const;
void Mult(int m, int n, std::complex<double> *X) const;
/** Assuming L.U = P.A factored data of size (m x m), compute
X <- L^{-1} P X, for a matrix X of size (m x n). */
void LSolve(int m, int n, double *X_r, double *X_i) const;
/** Assuming L.U = P.A factored data of size (m x m), compute
X <- U^{-1} X, for a matrix X of size (m x n). */
void USolve(int m, int n, double *X_r, double *X_i) const;
/** Assuming L.U = P.A factored data of size (m x m), compute X <- A^{-1} X,
for a matrix X of size (m x n). */
virtual void Solve(int m, int n, double *X_r, double *X_i) const;
/** Assuming L.U = P.A factored data of size (m x m), compute X <- X A^{-1},
for a matrix X of size (n x m). */
void RightSolve(int m, int n, double *X_r, double *X_i) const;
/// Assuming L.U = P.A factored data of size (m x m), compute X <- A^{-1}.
virtual void GetInverseMatrix(int m, double *X_r, double * X_i) const;
};
/** Class that can compute Cholesky factorizations of external data of an
Hermitian PD matrix and perform various operations with the factored data. */
class ComplexCholeskyFactors : public ComplexFactors
{
public:
/** With this constructor, the (public) data should be set
explicitly before calling class methods. */
ComplexCholeskyFactors() : ComplexFactors() { }
ComplexCholeskyFactors(double *data_r_, double * data_i_)
: ComplexFactors(data_r_, data_i_) { }
/**
* @brief Compute the Cholesky factorization of the current matrix
*
* Factorize the current matrix of size (m x m) overwriting it with the
* Cholesky factors. The factorization is such that LL^H = A, where A is the
* original matrix
*
* @param [in] m size of the square matrix
* @param [in] TOL optional fuzzy comparison tolerance. Defaults to 0.0.
*
* @return status set to true if successful, otherwise, false.
*/
virtual bool Factor(int m, double TOL = 0.0);
/** Assuming LL^H = A factored data of size (m x m), compute |A|
from the diagonal values of L */
virtual std::complex<double> Det(int m) const;
/** Assuming L.L^H = A factored data of size (m x m), compute X <- L X,
for a matrix X of size (m x n). */
void LMult(int m, int n, double *X_r, double * X_i) const;
/** Assuming L.L^H = A factored data of size (m x m), compute X <- L^t X,
for a matrix X of size (m x n). */
void UMult(int m, int n, double *X_r, double *X_i) const;
/** Assuming L L^H = A factored data of size (m x m), compute
X <- L^{-1} X, for a matrix X of size (m x n). */
void LSolve(int m, int n, double *X_r, double * X_i) const;
/** Assuming L L^H = A factored data of size (m x m), compute
X <- L^{-t} X, for a matrix X of size (m x n). */
void USolve(int m, int n, double *X_r, double *X_i) const;
/** Assuming L.L^H = A factored data of size (m x m), compute X <- A^{-1} X,
for a matrix X of size (m x n). */
virtual void Solve(int m, int n, double *X_r, double * X_i) const;
/** Assuming L.L^H = A factored data of size (m x m), compute X <- X A^{-1},
for a matrix X of size (n x m). */
void RightSolve(int m, int n, double *X_r, double *X_i) const;
/// Assuming L.L^H = A factored data of size (m x m), compute X <- A^{-1}.
virtual void GetInverseMatrix(int m, double *X_r, double * X_i) const;
};
} // namespace mfem
#endif // MFEM_COMPLEX_DENSEMAT
+248 -2
View File
@@ -1450,10 +1450,10 @@ void DenseMatrix::GradToCurl(DenseMatrix &curl)
int j = i+n;
// curl of (Ui,0)
curl(i,0) = -y;
curl(i,0) = y;
// curl of (0,Ui)
curl(j,0) = x;
curl(j,0) = -x;
}
}
else
@@ -1718,6 +1718,233 @@ void DenseMatrix::AddMatrix(double a, const DenseMatrix &A, int ro, int co)
}
}
void DenseMatrix::GetSubMatrix(const Array<int> & idx, DenseMatrix & A) const
{
int k = idx.Size();
A.SetSize(k);
double * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = idx[i];
for (int j = 0; j<k; j++)
{
jj = idx[j];
adata[i+j*k] = this->data[ii+jj*height];
}
}
}
void DenseMatrix::GetSubMatrix(const Array<int> & idx_i,
const Array<int> & idx_j, DenseMatrix & A) const
{
int k = idx_i.Size();
int l = idx_j.Size();
A.SetSize(k,l);
double * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = idx_i[i];
for (int j = 0; j<l; j++)
{
jj = idx_j[j];
adata[i+j*k] = this->data[ii+jj*height];
}
}
}
void DenseMatrix::GetSubMatrix(int ibeg, int iend, DenseMatrix & A)
{
int k = iend - ibeg + 1;
A.SetSize(k);
double * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = ibeg + i;
for (int j = 0; j<k; j++)
{
jj = ibeg + j;
adata[i+j*k] = this->data[ii+jj*height];
}
}
}
void DenseMatrix::GetSubMatrix(int ibeg, int iend, int jbeg, int jend,
DenseMatrix & A)
{
int k = iend - ibeg;
int l = jend - jbeg;
A.SetSize(k,l);
double * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = ibeg + i;
for (int j = 0; j<l; j++)
{
jj = jbeg + j;
adata[i+j*k] = this->data[ii+jj*height];
}
}
}
void DenseMatrix::SetSubMatrix(const Array<int> & idx, const DenseMatrix & A)
{
int k = idx.Size();
MFEM_VERIFY(A.Height() == k &&
A.Width() == k, "DenseMatrix::SetSubMatrix:Inconsistent matrix dimensions");
double * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = idx[i];
for (int j = 0; j<k; j++)
{
jj = idx[j];
this->data[ii+jj*height] = adata[i+j*k];
}
}
}
void DenseMatrix::SetSubMatrix(const Array<int> & idx_i,
const Array<int> & idx_j, const DenseMatrix & A)
{
int k = idx_i.Size();
int l = idx_j.Size();
MFEM_VERIFY(k == A.Height() &&
l == A.Width(),"DenseMatrix::SetSubMatrix:Inconsistent matrix dimensions");
double * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = idx_i[i];
for (int j = 0; j<l; j++)
{
jj = idx_j[j];
this->data[ii+jj*height] = adata[i+j*k];
}
}
}
void DenseMatrix::SetSubMatrix(int ibeg, const DenseMatrix & A)
{
int k = A.Height();
double * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = ibeg + i;
for (int j = 0; j<k; j++)
{
jj = ibeg + j;
this->data[ii+jj*height] = adata[i+j*k];
}
}
}
void DenseMatrix::SetSubMatrix(int ibeg, int jbeg, const DenseMatrix & A)
{
int k = A.Height();
int l = A.Width();
double * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = ibeg + i;
for (int j = 0; j<l; j++)
{
jj = jbeg + j;
this->data[ii+jj*height] = adata[i+j*k];
}
}
}
void DenseMatrix::AddSubMatrix(const Array<int> & idx, const DenseMatrix & A)
{
int k = idx.Size();
MFEM_VERIFY(A.Height() == k &&
A.Width() == k, "DenseMatrix::SetSubMatrix:Inconsistent matrix dimensions");
double * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = idx[i];
for (int j = 0; j<k; j++)
{
jj = idx[j];
this->data[ii+jj*height] += adata[i+j*k];
}
}
}
void DenseMatrix::AddSubMatrix(const Array<int> & idx_i,
const Array<int> & idx_j, const DenseMatrix & A)
{
int k = idx_i.Size();
int l = idx_j.Size();
MFEM_VERIFY(k == A.Height() &&
l == A.Width(),"DenseMatrix::SetSubMatrix:Inconsistent matrix dimensions");
double * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = idx_i[i];
for (int j = 0; j<l; j++)
{
jj = idx_j[j];
this->data[ii+jj*height] += adata[i+j*k];
}
}
}
void DenseMatrix::AddSubMatrix(int ibeg, const DenseMatrix & A)
{
int k = A.Height();
double * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = ibeg + i;
for (int j = 0; j<k; j++)
{
jj = ibeg + j;
this->data[ii+jj*height] += adata[i+j*k];
}
}
}
void DenseMatrix::AddSubMatrix(int ibeg, int jbeg, const DenseMatrix & A)
{
int k = A.Height();
int l = A.Width();
double * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = ibeg + i;
for (int j = 0; j<l; j++)
{
jj = jbeg + j;
this->data[ii+jj*height] += adata[i+j*k];
}
}
}
void DenseMatrix::AddToVector(int offset, Vector &v) const
{
const int n = height * width;
@@ -2857,6 +3084,25 @@ void AddMult_a_VVt(const double a, const Vector &v, DenseMatrix &VVt)
}
}
void RAP(const DenseMatrix &A, const DenseMatrix &P, DenseMatrix & RAP)
{
DenseMatrix RA(P.Width(),A.Width());
MultAtB(P,A,RA);
RAP.SetSize(RA.Height(), P.Width());
Mult(RA,P, RAP);
}
/// General R^tAP with given R, A and P
void RAP(const DenseMatrix &Rt, const DenseMatrix &A,
const DenseMatrix &P, DenseMatrix & RAP)
{
DenseMatrix RA(Rt.Width(),A.Width());
MultAtB(Rt,A,RA);
RAP.SetSize(RA.Height(), P.Width());
Mult(RA,P, RAP);
}
bool LUFactors::Factor(int m, double TOL)
{
+29
View File
@@ -355,6 +355,29 @@ public:
/// Perform (ro+i,co+j)+=a*A(i,j) for 0<=i<A.Height, 0<=j<A.Width
void AddMatrix(double a, const DenseMatrix &A, int ro, int co);
/// GetSubMatrix
void GetSubMatrix(const Array<int> & idx, DenseMatrix & A) const;
void GetSubMatrix(const Array<int> & idx_i, const Array<int> & idx_j,
DenseMatrix & A) const;
// Get submatrix i,j ∈ [ibeg, iend)
void GetSubMatrix(int ibeg, int iend, DenseMatrix & A);
// Get submatrix i ∈ [ibeg, iend), j ∈ [jbeg, jend)
void GetSubMatrix(int ibeg, int iend, int jbeg, int jend, DenseMatrix & A);
void SetSubMatrix(const Array<int> & idx, const DenseMatrix & A);
void SetSubMatrix(const Array<int> & idx_i, const Array<int> & idx_j,
const DenseMatrix & A);
void SetSubMatrix(int ibeg, const DenseMatrix & A);
void SetSubMatrix(int ibeg, int jbeg, const DenseMatrix & A);
void AddSubMatrix(const Array<int> & idx, const DenseMatrix & A);
void AddSubMatrix(const Array<int> & idx_i, const Array<int> & idx_j,
const DenseMatrix & A);
void AddSubMatrix(int ibeg, const DenseMatrix & A);
void AddSubMatrix(int ibeg, int jbeg, const DenseMatrix & A);
/// Add the matrix 'data' to the Vector 'v' at the given 'offset'
void AddToVector(int offset, Vector &v) const;
/// Get the matrix 'data' from the Vector 'v' at the given 'offset'
@@ -523,6 +546,12 @@ void AddMult_a_VWt(const double a, const Vector &v, const Vector &w,
/// VVt += a * v v^t
void AddMult_a_VVt(const double a, const Vector &v, DenseMatrix &VVt);
/// Computes matrix P^t * A * P
void RAP(const DenseMatrix &A, const DenseMatrix &P, DenseMatrix & RAP);
/// Computes the matrix Rt^t * A * P
void RAP(const DenseMatrix &Rt, const DenseMatrix &A,
const DenseMatrix &P, DenseMatrix & RAP);
/** Abstract class that can compute factorization of external data and perform various
operations with the factored data. */
+1
View File
@@ -19,6 +19,7 @@
#include "matrix.hpp"
#include "sparsemat.hpp"
#include "complex_operator.hpp"
#include "complex_densemat.hpp"
#include "blockvector.hpp"
#include "blockmatrix.hpp"
#include "blockoperator.hpp"
+14 -11
View File
@@ -255,18 +255,21 @@ public:
/** This enumeration is primarily used with class OperatorHandle. */
enum Type
{
ANY_TYPE, ///< ID for the base class Operator, i.e. any type.
MFEM_SPARSEMAT, ///< ID for class SparseMatrix.
Hypre_ParCSR, ///< ID for class HypreParMatrix.
PETSC_MATAIJ, ///< ID for class PetscParMatrix, MATAIJ format.
PETSC_MATIS, ///< ID for class PetscParMatrix, MATIS format.
PETSC_MATSHELL, ///< ID for class PetscParMatrix, MATSHELL format.
PETSC_MATNEST, ///< ID for class PetscParMatrix, MATNEST format.
PETSC_MATHYPRE, ///< ID for class PetscParMatrix, MATHYPRE format.
PETSC_MATGENERIC, ///< ID for class PetscParMatrix, unspecified format.
Complex_Operator, ///< ID for class ComplexOperator.
ANY_TYPE, ///< ID for the base class Operator, i.e. any type.
MFEM_SPARSEMAT, ///< ID for class SparseMatrix.
Hypre_ParCSR, ///< ID for class HypreParMatrix.
PETSC_MATAIJ, ///< ID for class PetscParMatrix, MATAIJ format.
PETSC_MATIS, ///< ID for class PetscParMatrix, MATIS format.
PETSC_MATSHELL, ///< ID for class PetscParMatrix, MATSHELL format.
PETSC_MATNEST, ///< ID for class PetscParMatrix, MATNEST format.
PETSC_MATHYPRE, ///< ID for class PetscParMatrix, MATHYPRE format.
PETSC_MATGENERIC, ///< ID for class PetscParMatrix, unspecified format.
Complex_Operator, ///< ID for class ComplexOperator.
MFEM_ComplexSparseMat, ///< ID for class ComplexSparseMatrix.
Complex_Hypre_ParCSR ///< ID for class ComplexHypreParMatrix.
Complex_Hypre_ParCSR, ///< ID for class ComplexHypreParMatrix.
MFEM_Block_Matrix, ///< ID for class BlockMatrix.
MFEM_Block_Operator, ///< ID for the base class BlockOperator.
Complex_DenseMat ///< ID for class ComplexDenseMatrix
};
/// Return the type ID of the Operator class.
+13
View File
@@ -289,6 +289,19 @@ void Vector::SetVector(const Vector &v, int offset)
}
}
void Vector::AddSubVector(const Vector &v, int offset)
{
MFEM_ASSERT(v.Size() + offset <= size, "invalid sub-vector");
const int vs = v.Size();
const double *vp = v.data;
double *p = data + offset;
for (int i = 0; i < vs; i++)
{
p[i] += vp[i];
}
}
void Vector::Neg()
{
const bool use_dev = UseDevice();
+2
View File
@@ -321,6 +321,8 @@ public:
void SetVector(const Vector &v, int offset);
void AddSubVector(const Vector &v, int offset);
/// (*this) = -(*this)
void Neg();
+1
View File
@@ -23,6 +23,7 @@ set(UNIT_TESTS_SRCS
linalg/test_cg_indefinite.cpp
linalg/test_chebyshev.cpp
linalg/test_complex_operator.cpp
linalg/test_complex_dense_matrix.cpp
linalg/test_constrainedsolver.cpp
linalg/test_direct_solvers.cpp
linalg/test_hypre_ilu.cpp
@@ -0,0 +1,793 @@
// Copyright (c) 2010-2022, 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 "mfem.hpp"
#include "unit_tests.hpp"
using namespace mfem;
TEST_CASE("ComplexDenseMatrix", "[ComplexDenseMatrix]")
{
DenseMatrix A_r(
{
{
7.476973198773836e-01, 5.307752092411233e-01,
7.787714872353524e-01, 1.374015004967300e-01
},
{
5.912071572460218e-01, 7.793148354527577e-01,
8.614244090659824e-01, 3.664367109052707e-01
},
{
4.707552432065591e-02, 7.498226683758581e-01,
5.086949143815038e-02, 4.642421529285558e-01
},
{
2.153684312350395e-01, 8.995081931810892e-01,
2.838610147580374e-01, 3.462963875422466e-01
}
});
DenseMatrix A_i(
{
{
2.338532314577970e-01, 4.866770088208406e-01,
6.635456790536266e-01, 5.077509109627342e-01
},
{
5.921703125786201e-01, 9.612084289713472e-01,
6.886727004080341e-01, 2.105085744039327e-01
},
{
5.857974556931360e-02, 3.537664834416591e-01,
9.653908939745925e-01, 6.672005467737580e-01
},
{
1.883898354361578e-01, 6.475861201488613e-01,
6.396282633261788e-01, 7.482565383983398e-01
}
});
DenseMatrix B_r(
{
{
1.401452180742794e+00, 1.912351953807087e+00,
1.895130685031467e+00, 8.392549763377584e-01
},
{
1.912351953807087e+00, 3.965697621768416e+00,
3.118779584828659e+00, 2.188141653405000e+00
},
{
1.895130685031467e+00, 3.118779584828659e+00,
3.687368672616838e+00, 2.149180498127385e+00
},
{
8.392549763377584e-01, 2.188141653405000e+00,
2.149180498127385e+00, 1.795766264370507e+00
}
});
DenseMatrix B_i(
{
{
0.000000000000000e+00, 2.892905182471230e-01,
3.377969313208506e-01, 3.550991903463218e-01
},
{
-2.892905182471230e-01, 0.000000000000000e+00,
7.792703332501396e-01, 7.993167778330725e-01
},
{
-3.377969313208506e-01, -7.792703332501396e-01,
0.000000000000000e+00, -1.901030659985157e-01
},
{
-3.550991903463218e-01, -7.993167778330725e-01,
1.901030659985157e-01, 0.000000000000000e+00
}
});
DenseMatrix AB_r(
{
{
4.199316358694532, 7.119508739631666,
5.684500367417206, 3.363473388898064
},
{
4.844369905372942, 8.243147426694234,
6.525761183975376, 3.863145594383837
},
{
2.651290557124633, 5.506731777039958,
3.190755998317346, 2.503175360026021
},
{
3.519699773999353, 6.664121172538803,
4.293944745711777, 2.918008065054989
}
});
DenseMatrix AB_i(
{
{
2.476664090430751, 5.057308079927443,
6.191314340700356, 4.140779508119272
},
{
3.503306034281864, 6.759590101303163,
7.988514056402222, 5.127445401059480
},
{
2.749163864229835, 5.588627587485725,
6.896492887398851, 4.702581910360381
},
{
2.863512962181819, 6.124848339934783,
7.182928158705073, 5.034994005830734
}
});
DenseMatrix AtB_r(
{
{
2.190427107115792, 4.263902975830449,
4.473548538497790, 2.954315435185178
},
{
3.782581973223430, 7.759823841169083,
9.171024988758536, 6.251394254923715
},
{
2.320929711857948, 4.613604134364786,
5.842510922053375, 3.760150638280159
},
{
1.511766385570270, 2.950415646350681,
4.337120195387853, 2.758465978422851
}
});
DenseMatrix AtB_i(
{
{
-1.992705303802213, -3.383029459787039,
-2.156697737612761, -1.227098261518685
},
{
-4.532297784683165, -8.412647875423712,
-5.658770233055886, -3.766073955464488
},
{
-4.980449520661596, -8.451683973225684,
-7.351440910685274, -4.331797912622623
},
{
-3.392361165173878, -6.122776241469277,
-5.289337862384023, -3.410946995052299
}
});
DenseMatrix invA_r(
{
{
1.284125217026929e+00, 5.566676578220062e-01,
3.205235484188196e-01, -1.884823653328449e+00
},
{
-8.923029616469982e-01, 1.334042076588671e-01,
3.358025813557918e-01, 6.973560749734028e-01
},
{
2.366798350499296e-01, -5.172682567295234e-01,
-6.685771336109858e-01, 1.099184759019011e+00
},
{
-1.623347650066281e-02, 8.311589945672611e-01,
6.287242859617315e-02, -7.940924063262524e-01
}
});
DenseMatrix invA_i(
{
{
-2.049683956467907e-01, 3.816877789141072e-01,
2.326958896869482e+00, -2.208208959084148e+00
},
{
7.275898603082531e-01, -1.183531610937897e+00,
-5.227713373718834e-01, 7.379400884151782e-01
},
{
-1.255712097872317e-01, -1.944130914054951e-01,
-1.977900692062877e+00, 1.787591531313541e+00
},
{
-1.091544681586941e+00, 1.603416041973783e+00,
1.828734546571891e+00, -2.601914242111798e+00
},
});
DenseMatrix invB_r(
{
{
1.609277412199292e+01, -5.725193323656868e+00,
-1.086842033161421e+01, 1.279540669178842e+01
},
{
-5.725193323656868e+00, 4.161049651113045e+00,
2.753672305121771e+00, -5.975244397594217e+00
},
{
-1.086842033161421e+01, 2.753672305121771e+00,
9.139925521110435e+00, -9.791537312098491e+00
},
{
1.279540669178842e+01, -5.975244397594217e+00,
-9.791537312098491e+00, 1.520226505276132e+01
},
});
DenseMatrix invB_i(
{
{
0.000000000000000e+00, 7.582682740428248e-01,
4.380318627231539e-02, -2.760797691635214e+00
},
{
-7.582682740428248e-01, 0.000000000000000e+00,
1.276538947979396e+00, -1.601898857634354e+00
},
{
-4.380318627231539e-02, -1.276538947979396e+00,
0.000000000000000e+00, 3.466959403556459e+00
},
{
2.760797691635214e+00, 1.601898857634354e+00,
-3.466959403556459e+00, 0.000000000000000e+00
}
});
// L matrix where B = L * L^H (real part)
DenseMatrix BL_r(
{
{
1.183829455936451e+00, 0.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
1.615394805575562e+00, 1.138631338745275e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
1.600847719684717e+00, 4.066722657083971e-01,
6.996783780831184e-01, 0.000000000000000e+00
},
{
7.089323315357766e-01, 8.515800861725336e-01,
4.506517233907201e-01, 2.564754791774774e-01
}
});
// L matrix where B = L * L^H (imag part)
DenseMatrix BL_i(
{
{
0.000000000000000e+00, 0.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
-2.443684069495331e-01, 0.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
-2.853425631765863e-01, -6.231393970721737e-01,
0.000000000000000e+00, 0.000000000000000e+00
},
{
-2.999580628490323e-01, -4.285906501101729e-01,
1.595654676419282e-01, 0.000000000000000e+00
}
});
// L matrix where P * A = L * U (real part)
DenseMatrix AL_r(
{
{
1.000000000000000e+00, 0.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
8.929066668627371e-02, 1.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
8.290944409495822e-01, -6.632415969670290e-01,
1.000000000000000e+00, 0.000000000000000e+00
},
{
3.411726617549650e-01, 9.379298209889033e-01,
-3.855119154762174e-01, 1.000000000000000e+00
}
});
// L matrix where P * A = L * U (imag part)
DenseMatrix AL_i(
{
{
0.000000000000000e+00, 0.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
9.648840507821686e-03, 0.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
-4.348930483084801e-01, 2.920682426805858e-01,
0.000000000000000e+00, 0.000000000000000e+00
},
{
-2.307564472343243e-02, 1.354227054751071e-01,
-1.114174339166530e-01, 0.000000000000000e+00
}
});
// U matrix where P * A = L * U (real part)
DenseMatrix AU_r(
{
{
5.912071572460218e-01, 7.793148354527577e-01,
8.614244090659824e-01, 3.664367109052707e-01
},
{
0.000000000000000e+00, 6.895116739856960e-01,
-1.940277529885255e-02, 4.335539383734513e-01
},
{
0.000000000000000e+00, 0.000000000000000e+00,
1.377415198958651e-02, 2.179387335957125e-01
},
{
0.000000000000000e+00, 0.000000000000000e+00,
0.000000000000000e+00,-1.073019476366396e-01
}
});
// U matrix where P * A = L * U (imag part)
DenseMatrix AU_i(
{
{
5.921703125786201e-01, 9.612084289713472e-01,
6.886727004080341e-01, 2.105085744039327e-01
},
{
0.000000000000000e+00, 2.604200574416788e-01,
8.955871026939005e-01, 6.448684064423180e-01
},
{
0.000000000000000e+00, 0.000000000000000e+00,
1.066856013356064e+00, 7.936564152043436e-01
},
{
0.000000000000000e+00, 0.000000000000000e+00,
0.000000000000000e+00, 3.515843540851109e-01
}
});
// P matrix where P * A = L * U
DenseMatrix P_r(
{
{
0.0, 1.0, 0.0, 0.0
},
{
0.0, 0.0, 1.0, 0.0
},
{
1.0, 0.0, 0.0, 0.0
},
{
0.0, 0.0, 0.0, 1.0
}
});
DenseMatrix invBL_r(
{
{
8.447162680277831e-01, 0.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
-1.198412712810921e+00, 8.782473887482838e-01,
0.000000000000000e+00, 0.000000000000000e+00
},
{
-1.397598658079286e+00, -5.104614729030819e-01,
1.429228101545257e+00, 0.000000000000000e+00
},
{
3.281708062547134e+00, -1.532503670075513e+00,
-2.511289224004609e+00, 3.899008213989978e+00
}
});
DenseMatrix invBL_i(
{
{
0.000000000000000e+00, 0.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
1.812895550282085e-01, 0.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
-8.281949571012291e-01, 7.821744467853249e-01,
0.000000000000000e+00, 0.000000000000000e+00
},
{
7.080769108742154e-01, 4.108477771056253e-01,
-8.891900743160057e-01, 0.000000000000000e+00
}
});
DenseMatrix invAL_r(
{
{
1.000000000000000e+00, 0.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
-8.929066668627371e-02, 1.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
-8.911338452078618e-01, 6.632415969670290e-01,
1.000000000000000e+00, 0.000000000000000e+00
},
{
-6.529209723598358e-01, -6.497007883906214e-01,
3.855119154762174e-01, 1.000000000000000e+00
}
});
DenseMatrix invAL_i(
{
{
0.000000000000000e+00, 0.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
-9.648840507821686e-03, 0.000000000000000e+00,
0.000000000000000e+00, 0.000000000000000e+00
},
{
4.545725040280302e-01, -2.920682426805858e-01,
0.000000000000000e+00, 0.000000000000000e+00
},
{
1.201728340713420e-01, -1.741218163598231e-01,
1.114174339166530e-01, 0.000000000000000e+00
}
});
DenseMatrix invAU_r(
{
{
8.443505642555830e-01, -1.940087632374593e+00,
1.764714218183542e+00, -1.884823653328449e+00
},
{
0.000000000000000e+00, 1.269246346038117e+00,
-1.078922646843524e+00, 6.973560749734028e-01
},
{
0.000000000000000e+00, 0.000000000000000e+00,
1.209987444834138e-02, 1.099184759019011e+00
},
{
0.000000000000000e+00, 0.000000000000000e+00,
0.000000000000000e+00, -7.940924063262524e-01
}
});
DenseMatrix invAU_i(
{
{
-8.457261239702749e-01, 5.115617062678094e-01,
8.563246847807503e-01, -2.208208959084148e+00
},
{
0.000000000000000e+00, -4.793786948264843e-01,
3.654075389169029e-01, 7.379400884151782e-01
},
{
0.000000000000000e+00, 0.000000000000000e+00,
-9.371773903631724e-01, 1.787591531313541e+00
},
{
0.000000000000000e+00, 0.000000000000000e+00,
0.000000000000000e+00, -2.601914242111798e+00
}
});
ComplexDenseMatrix A(&A_r,&A_i,false,false);
ComplexDenseMatrix B(&B_r,&B_i,false,false);
SECTION("Mult")
{
ComplexDenseMatrix * AB = Mult(A,B);
AB_r -= AB->real();
AB_i -= AB->imag();
double norm_r = AB_r.MaxMaxNorm();
double norm_i = AB_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete AB;
}
SECTION("MultAtB")
{
ComplexDenseMatrix * AtB = MultAtB(A,B);
AtB_r -= AtB->real();
AtB_i -= AtB->imag();
double norm_r = AtB_r.MaxMaxNorm();
double norm_i = AtB_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete AtB;
}
SECTION("Inverse")
{
ComplexDenseMatrix * invA = A.ComputeInverse();
invA_r -= invA->real();
invA_i -= invA->imag();
double norm_r = invA_r.MaxMaxNorm();
double norm_i = invA_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete invA;
}
SECTION("SystemMatrix")
{
DenseMatrix * sA = A.GetSystemMatrix();
sA->Invert();
ComplexDenseMatrix * invA = A.ComputeInverse();
DenseMatrix * sinvA = invA->GetSystemMatrix();
*sA-=*sinvA;
double norm = sA->MaxMaxNorm();
REQUIRE(norm == MFEM_Approx(0.));
delete sinvA;
delete invA;
delete sA;
}
double norm_r = 0.;
double norm_i = 0.;
DenseMatrix diff_r;
DenseMatrix diff_i;
ComplexCholeskyFactors chol(B.real().Data(),B.imag().Data());
int m = B.real().Height();
chol.Factor(m);
SECTION("ComplexCholeskyFactors::Inverse")
{
DenseMatrix Binv_r(m);
DenseMatrix Binv_i(m);
chol.GetInverseMatrix(m,Binv_r.Data(), Binv_i.Data());
diff_r = invB_r; diff_r-=Binv_r;
diff_i = invB_i; diff_i-=Binv_i;
norm_r = diff_r.MaxMaxNorm();
norm_i = diff_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
}
SECTION("ComplexCholeskyFactors::LMult")
{
ComplexDenseMatrix exactL(&BL_r, &BL_i, false,false);
ComplexDenseMatrix A(&A_r, &A_i, false,false);
ComplexDenseMatrix * LA = mfem::Mult(exactL,A);
DenseMatrix LA_r(A_r);
DenseMatrix LA_i(A_i);
chol.LMult(m,m,LA_r.Data(),LA_i.Data());
diff_r = LA->real(); diff_r-=LA_r;
diff_i = LA->imag(); diff_i-=LA_i;
norm_r = diff_r.MaxMaxNorm();
norm_i = diff_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete LA;
}
SECTION("ComplexCholeskyFactors::UMult")
{
ComplexDenseMatrix exactL(&BL_r, &BL_i, false,false);
ComplexDenseMatrix * UA = mfem::MultAtB(exactL,A);
DenseMatrix LtA_r(A_r);
DenseMatrix LtA_i(A_i);
chol.UMult(m,m,LtA_r.Data(),LtA_i.Data());
diff_r = UA->real(); diff_r-=LtA_r;
diff_i = UA->imag(); diff_i-=LtA_i;
norm_r = diff_r.MaxMaxNorm();
norm_i = diff_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete UA;
}
SECTION("ComplexCholeskyFactors::LSolve")
{
ComplexDenseMatrix exactinvL(&invBL_r, &invBL_i, false,false);
ComplexDenseMatrix * invLA = mfem::Mult(exactinvL,A);
DenseMatrix invLA_r(A_r);
DenseMatrix invLA_i(A_i);
chol.LSolve(m,m,invLA_r.Data(),invLA_i.Data());
diff_r = invLA->real(); diff_r-=invLA_r;
diff_i = invLA->imag(); diff_i-=invLA_i;
norm_r = diff_r.MaxMaxNorm();
norm_i = diff_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete invLA;
}
SECTION("ComplexCholeskyFactors::USolve")
{
ComplexDenseMatrix exactinvL(&invBL_r, &invBL_i, false,false);
ComplexDenseMatrix * invUA = mfem::MultAtB(exactinvL,A);
DenseMatrix invUA_r(A_r);
DenseMatrix invUA_i(A_i);
chol.USolve(m,m,invUA_r.Data(),invUA_i.Data());
diff_r = invUA->real(); diff_r-=invUA_r;
diff_i = invUA->imag(); diff_i-=invUA_i;
norm_r = diff_r.MaxMaxNorm();
norm_i = diff_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete invUA;
}
SECTION("ComplexCholeskyFactors::Solve")
{
ComplexDenseMatrix invB(&invB_r,&invB_i,false,false);
ComplexDenseMatrix * invBA = mfem::Mult(invB,A);
DenseMatrix invBA_r(A_r);
DenseMatrix invBA_i(A_i);
chol.Solve(m,m,invBA_r.Data(),invBA_i.Data());
diff_r = invBA->real(); diff_r-=invBA_r;
diff_i = invBA->imag(); diff_i-=invBA_i;
norm_r = diff_r.MaxMaxNorm();
norm_i = diff_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete invBA;
}
SECTION("ComplexCholeskyFactors::RightSolve")
{
ComplexDenseMatrix invB(&invB_r,&invB_i,false,false);
ComplexDenseMatrix * AinvB = mfem::Mult(A,invB);
DenseMatrix AinvB_r(A_r);
DenseMatrix AinvB_i(A_i);
chol.RightSolve(m,m,AinvB_r.Data(),AinvB_i.Data());
diff_r = AinvB->real(); diff_r-=AinvB_r;
diff_i = AinvB->imag(); diff_i-=AinvB_i;
norm_r = diff_r.MaxMaxNorm();
norm_i = diff_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete AinvB;
}
int ipiv[m];
ComplexLUFactors lu(A.real().Data(),A.imag().Data(), ipiv);
lu.Factor(m);
SECTION("ComplexLUFactors::Inverse")
{
DenseMatrix Ainv_r(m);
DenseMatrix Ainv_i(m);
lu.GetInverseMatrix(m,Ainv_r.Data(), Ainv_i.Data());
diff_r = invA_r; diff_r-=Ainv_r;
diff_i = invA_i; diff_i-=Ainv_i;
norm_r = diff_r.MaxMaxNorm();
norm_i = diff_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
}
SECTION("ComplexLUFactors::LSolve")
{
ComplexDenseMatrix P(&P_r,nullptr, false,false);
ComplexDenseMatrix *PB = mfem::Mult(P,B);
ComplexDenseMatrix exactinvL(&invAL_r, &invAL_i, false,false);
ComplexDenseMatrix * invLB = mfem::Mult(exactinvL,*PB);
DenseMatrix invLB_r(B_r);
DenseMatrix invLB_i(B_i);
lu.LSolve(m,m,invLB_r.Data(),invLB_i.Data());
diff_r = invLB->real(); diff_r-=invLB_r;
diff_i = invLB->imag(); diff_i-=invLB_i;
norm_r = diff_r.MaxMaxNorm();
norm_i = diff_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete PB;
delete invLB;
}
SECTION("ComplexLUFactors::USolve")
{
ComplexDenseMatrix exactinvU(&invAU_r, &invAU_i, false,false);
ComplexDenseMatrix * invUB = mfem::Mult(exactinvU,B);
DenseMatrix invUB_r(B_r);
DenseMatrix invUB_i(B_i);
lu.USolve(m,m,invUB_r.Data(),invUB_i.Data());
diff_r = invUB->real(); diff_r-=invUB_r;
diff_i = invUB->imag(); diff_i-=invUB_i;
norm_r = diff_r.MaxMaxNorm();
norm_i = diff_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete invUB;
}
SECTION("ComplexLUFactors::Solve")
{
ComplexDenseMatrix invA(&invA_r,&invA_i,false,false);
ComplexDenseMatrix * invAB = mfem::Mult(invA,B);
DenseMatrix invAB_r(B_r);
DenseMatrix invAB_i(B_i);
lu.Solve(m,m,invAB_r.Data(),invAB_i.Data());
diff_r = invAB->real(); diff_r-=invAB_r;
diff_i = invAB->imag(); diff_i-=invAB_i;
norm_r = diff_r.MaxMaxNorm();
norm_i = diff_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete invAB;
}
SECTION("ComplexLUFactors::RightSolve")
{
ComplexDenseMatrix invA(&invA_r,&invA_i,false,false);
ComplexDenseMatrix * BinvA = mfem::Mult(B,invA);
DenseMatrix BinvA_r(B_r);
DenseMatrix BinvA_i(B_i);
lu.RightSolve(m,m,BinvA_r.Data(),BinvA_i.Data());
diff_r = BinvA->real(); diff_r-=BinvA_r;
diff_i = BinvA->imag(); diff_i-=BinvA_i;
norm_r = diff_r.MaxMaxNorm();
norm_i = diff_i.MaxMaxNorm();
REQUIRE(norm_r == MFEM_Approx(0.));
REQUIRE(norm_i == MFEM_Approx(0.));
delete BinvA;
}
}