Compare commits

..
Author SHA1 Message Date
Dylan Copeland aea515ea68 Adding BlockGSPreconditioner. 2020-12-09 18:16:27 -08:00
Dylan Copeland 4ac64167c7 Supporting MatrixCoefficient for beta in MatrixFreeAMS. Fixing some bugs. 2020-10-27 10:09:44 -07:00
Dylan Copeland 60891d88ff Implemented the option to use HypreAMS in full assembly case for the Galerkin formulation. 2020-10-14 19:21:42 -07:00
Dylan Copeland f3f6f092d4 Added the option to use AmgX in MatrixFreeAMS. 2020-10-12 09:12:34 -07:00
Dylan Copeland 0851795de7 Merge branch 'feature/artv3/amgx' of github.com:mfem/mfem into complex-strumpack-dev-dylan 2020-10-09 11:28:13 -07:00
Dylan Copeland e7269a42f4 More of the merge 2020-10-08 15:56:26 -07:00
Dylan Copeland 469b2ebfb0 Merged hypre-cuda-dev into this branch. Generalized to allow for DEVICE vectors to be used by hypre, making memory references to them from MANAGED vectors. Added more hypre big_j support in DD code. 2020-10-07 21:19:34 -07:00
Dylan Copeland bff5ca20d1 Added timing output for MatrixFreeAMS and the option to use a direct Pi solver. 2020-10-02 13:16:05 -07:00
Dylan Copeland b3f1b3b210 Generalized MatrixFreeAMS to allow more types of coefficients and an input MPI_Comm, and fixed a bug when ess_bdr is empty. Added the option to use MatrixFreeAMS in FOSLSSolver. 2020-09-30 09:20:28 -07:00
Dylan Copeland d1824cc755 Merge branch 'barker29/matrix-free-ams' of github.com:mfem/mfem into complex-strumpack-dev-dylan 2020-09-28 11:18:20 -07:00
Dylan Copeland c77006832f Added capability to run on input mesh with attribute defining subdomains. Added MINRES solver option in FOSLS solver. 2020-09-24 15:19:33 -07:00
Andrew T. Barker 7e3d262c63 Clean up some debug information and std::cout stuff. 2020-09-24 09:38:28 -07:00
Andrew T. Barker 61bd7dcc8d A bit of cleanup (still needs more). 2020-09-22 09:27:42 -07:00
Andrew T. Barker 222d13eabf Boundary condition tweak.
This now matches results from old 2D templated code, used for the NLA paper.
2020-09-22 09:15:29 -07:00
Andrew T. Barker 3dbbfdbdd6 Merge remote-tracking branch 'origin/pa-id-interp' into barker29/matrix-free-ams 2020-09-22 08:09:43 -07:00
Dylan Copeland 60369fec8f Fixed the new PA interpolators to work on device and added the CUDA flag to the unit tests, for inclusion in cunit_tests. 2020-09-21 14:36:41 -07:00
Dylan Copeland 7a9f2f966f Corrected the 3D PA kernels for IdentityInterpolator, and made a 3D unit test with a hexahedral mesh not aligned with coordinate axes. Fixed a bug in integration point ordering that reduces the error in the 2D tests. 2020-09-21 13:03:37 -07:00
Dylan Copeland 20134f9213 Corrected the 2D PA kernels for IdentityInterpolator, for general quadrilateral meshes not aligned with coordinate axes. 2020-09-18 21:25:52 -07:00
Andrew T. Barker cd3745046d Add unit tests for PA gradient and identity interpolators using star mesh. 2020-09-18 15:57:11 -07:00
Dylan Copeland 2c7f6300e0 Merge branch 'master' of github.com:mfem/mfem into pa-id-interp 2020-09-17 18:32:09 -07:00
Andrew T. Barker 573e1ab7f4 WIP: Ugly hack to try to get around zero row problem with hypre AMG initialization 2020-09-16 09:46:43 -07:00
Dylan Copeland c0ca09165f Remove unused variable. 2020-09-16 08:58:29 -07:00
Dylan Copeland 17142d2b36 Cleaned up some things. Added order 1 test for PA gradient interpolator. Made some optimizations for the GradientInterpolator PA kernels. 2020-09-15 21:07:31 -07:00
Andrew T. Barker bbe9a15202 Cleaner includes/build. 2020-09-15 14:41:32 -07:00
Andrew T. Barker 0f6555e9ae Begin implementing matrix-free PA AMS cycle, does not work yet. 2020-09-15 14:18:39 -07:00
Dylan Copeland 1bac4f7c19 Moving new code to the bottom of bilininteg_hcurl.cpp for simpler comparison to master. 2020-09-15 09:39:55 -07:00
Dylan Copeland cc21811d1a Implemented 2D version of IdentityInterpolator PA kernels. 2020-09-14 19:57:56 -07:00
Dylan Copeland a0615bbaef Fix merge. 2020-09-14 14:27:37 -07:00
Dylan Copeland df0a751dc9 Merge branch 'master' of github.com:mfem/mfem into pa-id-interp 2020-09-14 14:05:58 -07:00
Dylan Copeland 38eeac6cb3 Implemented 3D version of IdentityInterpolator::AddMultTransposePA. Optimized the kernel loops. 2020-09-14 09:54:18 -07:00
Dylan Copeland e9b865f2a0 Debugged 3D IdentityInterpolator::Mult for H(curl)-H1^3. 2020-09-11 16:44:36 -07:00
Dylan Copeland 8fe9ecf433 Initial implementation (not working correctly yet) of PA identity interpolation for H(curl) and vector H1. 2020-09-02 09:55:10 -07:00
Dylan Copeland b2c83ee3ba make style 2020-08-27 10:47:44 -07:00
Dylan Copeland 8b66629a13 Adding Jacobi and block AMS preconditioners for CG in FOSLS. Debugged the case of multiple subdomains per MPI task. 2020-08-27 09:58:01 -07:00
Dylan Copeland 29c00972fc Adding block AMS coarse solver for GMG. 2020-08-21 13:01:08 -07:00
Dylan Copeland abb79e409c Adding cusparse ILU solver. 2020-08-20 14:27:20 -07:00
Dylan Copeland 3eae815aa0 Adding cusparse incomplete Cholesky solver. 2020-08-18 16:07:16 -07:00
Dylan Copeland d519d9eed4 Adding Jacobi preconditioner for GMG coarse CG solver. 2020-08-14 07:49:32 -07:00
Dylan Copeland a11018d36d Copying from feature/artv3/cusparse-Spmv to support SpMV with cusparse. 2020-08-13 13:26:01 -07:00
Dylan Copeland 7df51bf66c Debugged BlockMGPASolver in the case of more than 2 levels. Added the flag COARSE_PA for using PA block operator in the coarse space. 2020-08-13 09:51:40 -07:00
Dylan Copeland c3a2b981fe Removed copying of data in BlockOperator. Debugged BlockOperator with empty blocks on some processes. 2020-08-10 19:17:42 -07:00
Andrew T. Barker f1dff5b830 ElementRestriction: minor cleanup 2020-08-10 13:33:42 -07:00
Andrew T. Barker 88c70ecb61 Cleanup pbilinearform.hpp, pbilinearform.cpp a bit. 2020-08-10 13:29:42 -07:00
Andrew T. Barker 356c3034c4 Remove some cruft related to Nedelec dof orientations.
(some of this might possibly be useful when you want to implement the Pi interpolation)
2020-08-10 13:15:33 -07:00
Andrew T. Barker 78c5229b2a A bit of cleanup, improved comments. 2020-08-10 13:09:40 -07:00
Andrew T. Barker b280a5c1bc Fix std::cout and a little style. 2020-08-06 13:05:11 -07:00
Andrew T. Barker 30914c9001 Style 2020-08-05 10:18:00 -07:00
Andrew T. Barker 372409764a Merge remote-tracking branch 'origin/master' into matrix-free-grad 2020-08-05 09:45:58 -07:00
Andrew T. Barker dc33b2f048 Clean up unused functions / warnings. 2020-08-03 08:41:56 -07:00
Andrew T. Barker 9624d9de6f Clean up some debug output. 2020-08-03 08:30:04 -07:00
Andrew T. Barker 9ba4ce9312 Fix parallel communication issues in parallel PAGradient and transpose. 2020-08-03 08:24:31 -07:00
Dylan Copeland 3f97d9ed8d Merge branch 'master' of github.com:mfem/mfem into complex-strumpack-dev-dylan 2020-07-31 09:11:02 -07:00
Dylan Copeland 2f80d148ec Merge branch 'pacurlsmem' of github.com:mfem/mfem into complex-strumpack-dev-dylan 2020-07-30 17:27:46 -07:00
Dylan Copeland 98f91fc3c4 Merge branch 'master' of github.com:mfem/mfem into complex-strumpack-dev-dylan 2020-07-20 20:47:15 -07:00
Dylan Copeland 5eaf398112 Adding PA kernel versions on host for kernels that use a lot of memory. Temporary fixes for BlockOperator and SparseMatrix on device. 2020-07-20 13:36:49 -07:00
Andrew T. Barker 5260e5b971 WIP: working on parlllel matrix-free gradient
The problem here is we need some kind of parallel multiplicity scaling
2020-07-16 13:57:20 -07:00
Andrew T. Barker 9f544e448e PAGradient: fix orientation / restriction issue, transpose looks good in 3D 2020-07-13 13:47:32 -07:00
Andrew T. Barker 902889abe5 WIP: Draft a kernel for 3D transpose, does not give correct answer 2020-07-13 11:49:13 -07:00
Andrew T. Barker 30fde8d98c Multi-element gradient transpose appears to work in 2D (fails in 3D). 2020-07-13 10:50:55 -07:00
Andrew T. Barker bbc29bcf9b PAGradient: 2D transpose kernel works for one element but not multiple elements 2020-07-06 10:31:31 -07:00
Andrew T. Barker fd59cceda3 GradientInterpolator: add failing test for PA transpose 2020-07-01 13:07:43 -07:00
Andrew T. Barker 9d9b126cc8 Draft a 3D PA Gradient kernel - seems to work in simple cases. 2020-07-01 12:12:32 -07:00
Andrew T. Barker 11e5037e3c MultTransposeOverwrite() -> MultLeftInverse() 2020-07-01 11:36:29 -07:00
Andrew T. Barker b968557873 WIP: a little cleanup, mock up a 3D kernel, add 3D tests (failing) 2020-06-26 13:04:22 -07:00
Andrew T. Barker dca9990bb9 WIP: implement ElementRestriction::MultTranspposeOverwrite(), looks good so far?
(lots of debug prints etc in this code)
2020-06-26 12:08:20 -07:00
Andrew T. Barker 685f274044 WIP: start thinking about orientations of Nedelec dofs 2020-06-26 10:12:01 -07:00
Dylan Copeland b5ad749856 In BlockMGPASolver, moved the memory allocation to the constructor and added the option for a CG coarse solver. 2020-05-28 11:23:27 -07:00
Dylan Copeland 9409815be3 Implemented SparseMatrix prolongation/restriction in GMG for speed on GPU. 2020-05-27 16:35:50 -07:00
Dylan Copeland 36008de757 More UseDevice. 2020-05-27 10:24:32 -07:00
Dylan Copeland 5a88330602 Setting UseDevice(true) in various operators. Adding more timings. 2020-05-26 11:00:19 -07:00
Dylan Copeland e8b7afd381 Changed PABilinearFormExtension to use MFEM_FORALL for boundary integrator restriction. Added more timing for profiling. 2020-05-24 10:42:42 -07:00
Andrew T. Barker 5c437ce96c WIP: test PAGradient in multi-element setting, where it does not work (orientations of dofs) 2020-05-19 14:53:41 -07:00
Andrew T. Barker b98932091c WIP: 2D PA gradient kernel appears to work in simple cases 2020-05-19 14:10:42 -07:00
Andrew T. Barker 1d8cc71777 2D H1 to H(curl) PA gradient kernel drafted, is kinda close but not correct. 2020-05-19 13:27:03 -07:00
Dylan Copeland b558f1015f Added subdomain solver timing and changed iterative mode to false. 2020-05-19 12:46:35 -07:00
Dylan Copeland 3921efe595 Device compatibility for SDFOSLS. Set MAX_D1D and MAX_Q1D in some H(curl) PA kernels to prevent the GPU from running out of memory. 2020-05-15 22:17:44 -07:00
Dylan Copeland c22e2a0052 Commenting out unused variables. 2020-05-13 21:01:34 -07:00
Dylan Copeland e2b4541d5b More device compatibility. 2020-05-13 20:21:28 -07:00
Dylan Copeland 5357e7afd9 More device compatibility, including BlockOperator. 2020-05-09 17:24:04 -07:00
Dylan Copeland 9db9e511c1 Extended more code to be device compatible, including UMFPackSolver and some uses of GridFunctions. 2020-05-07 11:36:19 -07:00
Andrew T. Barker 4f5b6a7495 GradientInterpolator: rearrange and rename data structures here to make more sense 2020-05-04 16:10:17 -07:00
Andrew T. Barker 5f7462ec8d Better mapsotherC, mapsotherO, that have data closer to what I want for the actual kernel.
(now how do we actually get that stuff into the kernel and use it correctly?)
2020-05-04 15:49:13 -07:00
Dylan Copeland 40fed07111 Replaced strumpack with umfpack for the direct subdomain solver version, if MFEM_USE_STRUMPACK is not defined. 2020-05-01 18:07:24 -07:00
Dylan Copeland 1ee1968174 Updated ex3p_cfosls to work with recent changes to mfem. Restored the capability to use order 4 ND elements. 2020-04-29 10:20:38 -07:00
Andrew T. Barker 1f85594b4f WIP: thinking and exploring, this compiles, much to do. 2020-04-27 16:02:58 -07:00
Dylan Copeland 1793c17912 Fixed the Jacobian for the PA vector FE mass boundary integrator. 2020-04-24 12:49:45 -07:00
Dylan Copeland 75f4736edb Implemented use of boundary attribute markers in boundary integrators with PA. 2020-04-22 12:57:21 -07:00
Dylan Copeland 1389e439fe Completed the PA implementation for FOSLS-DD by implementing PA for boundary integrators. 2020-04-14 13:04:56 -07:00
Dylan Copeland d65300ffb2 Restored implementation of 2D PA in 3D space, which was removed by a pull from master. Made first attempt to implement FOSLS-DD-PA. 2020-04-07 18:47:31 -07:00
Dylan Copeland 797201707d Merge branch 'curlpa-dev' of github.com:mfem/mfem into complex-strumpack-dev-dylan 2020-04-01 10:13:39 -07:00
Dylan Copeland b51732c827 Extended PA for some vector mass and curl integrators to handle diagonal matrix coefficients. Added unit tests. 2020-03-31 11:56:02 -07:00
Dylan Copeland d838c6b33b Implemented PA for MixedVectorCurlIntegrator and MixedVectorWeakCurlIntegrator. Added unit tests for these integrators. 2020-03-30 18:02:46 -07:00
Dylan Copeland a5deda92cf Merge branch 'master' of github.com:mfem/mfem into complex-strumpack-dev-dylan 2020-03-28 15:32:56 -07:00
Dylan Copeland e03d6b4922 Added a block multigrid solver for block PA operators. Implemented a PA version of FOSLS-DD, but some integrators used by FOSLS still need PA implementations. 2020-03-27 20:23:33 -07:00
Dylan Copeland e304db295b Debugged FOSLS-DD. 2020-03-25 20:12:25 -07:00
Andrew T. Barker 08c7b33344 WIP: begin thinking about actual gradient kernels 2020-03-23 16:47:35 -07:00
Andrew T. Barker 15ae2763d3 WIP: this compiles, runs, and gives the wrong answer 2020-03-23 15:56:23 -07:00
Dylan Copeland 09bf7f5836 Debugged FOSLS-DD in the case of 2 subdomains. 2020-03-20 13:54:53 -07:00
Andrew T. Barker a407d30cfa WIP: PADiscreteLinearOperatorExtension: try to get this to at least compile 2020-03-13 15:56:27 -07:00
Andrew T. Barker 1b2e165c89 WIP: thinking about how to implement a PADiscreteLinearOperator 2020-03-13 13:35:42 -07:00
Andrew T. Barker 60350c6ebb unittest: write a draft test for a partially assembled gradient operator
which does not yet exist, so of course the test fails
2020-03-11 14:40:51 -07:00
Dylan Copeland c1d73ee268 Mostly implemented FOSLS-DD, but still debugging. 2020-02-28 09:52:56 -08:00
Dylan Copeland 76f1e67d8c Corrected the rotated tangential boundary integrator. Implemented an example FOSLS solver for complex indefinite Maxwell with complex impedance boundary condition, preconditioned by block multigrid. 2020-02-13 19:12:38 -08:00
Dylan Copeland 3fd02be664 Successfully completed the implementation of FOSLS with complex impedance BC, and tested for exact solutions 3 and 4. 2020-02-03 16:36:57 -08:00
Dylan Copeland 0d868b82e6 Partial implementation of FOSLS test with complex impedance BC, with extension of BlockMGSolver to an arbitrary number of blocks. 2020-02-03 12:10:55 -08:00
Dylan Copeland b9ebfcfe41 Adapted BlockMGSolver from gmg-solver branch, using Strumpack for coarse inversion. 2020-01-29 10:31:39 -08:00
Dylan Copeland 67d42627bd Adapted FOSLS example from gmg-solver branch, without a preconditioner. 2020-01-28 09:46:45 -08:00
Dylan Copeland 39cd50d4c6 Debugged geometric multigrid for PA and added more testing for it on subdomains. 2020-01-24 15:55:47 -08:00
Dylan Copeland afaccd48d9 Extended 2D PA to work on 2D meshes in 3D space. Implemented DD version with PA. 2020-01-14 10:03:55 -08:00
Dylan Copeland d3452e1f83 Implemented partial assembly version of complex GMG solver. Partially implemented option to use PA for subdomain solvers. Matrix construction still needs to be removed. Refactored CreateInterfaceMeshes to create a coarse-level DD operator for coarse matrix construction. 2020-01-03 16:51:05 -08:00
Dylan Copeland c0b9a5c372 Merge branch 'ndpa-dev' of github.com:mfem/mfem into complex-strumpack-dev-dylan 2019-12-23 10:54:21 -08:00
Dylan Copeland 8e7b51a5e4 Debugged FE orders 3 and 4. 2019-12-05 15:41:47 -08:00
Dylan Copeland 8072e4d4b6 Fixed a bug in DDAuxSolver. Deleted the subdomain matrix to reduce memory. 2019-12-04 12:50:07 -08:00
Dylan Copeland 7b89b3f994 Implemented an iterative solver for the interface blocks. 2019-12-03 10:24:40 -08:00
Dylan Copeland 8d05afbb39 Changed the way MPI_Comm_split is used to define subdomain communicators, so that only one MPI_Comm_split call is done. 2019-11-27 16:15:35 -08:00
Dylan Copeland 9c66df42e0 Fixed some bugs for hexahedral DOF mappings. Added new code to create a map between finite element spaces defined on different hexahedral meshes coincide geometrically (allowing for different entity orderings). The map is now included as a factor of the fine level P operator for GMG. 2019-11-27 13:50:24 -08:00
Dylan Copeland 835d26e38d Added sign flips for mapping hexahedral DOF's between interfaces and subdomains, and made some other generalizations for hexahedra. 2019-11-26 08:48:19 -08:00
Dylan Copeland c47045777d Merge branch 'master' of github.com:mfem/mfem into complex-strumpack-dev-dylan 2019-11-22 16:51:58 -08:00
Dylan Copeland 4e00e207b8 Copied GMGSolver from branch gmg-solver and added some tests. 2019-11-22 11:28:44 -08:00
Dylan Copeland 96d41b4991 Copied ComplexGMGSolver from gmg-solver branch. Implemented an iterative solver for the subdomain block operators. 2019-11-21 10:49:53 -08:00
Dylan Copeland da7bc9dd62 Deleted some unnecessary memory. 2019-11-19 10:09:14 -08:00
Dylan Copeland f7dffba81b Reduced memory usage by no longer constructing subdomain operator (just matrices) and deleting some matrices when they become unnecessary. Made the solution recovery more scalable. 2019-11-18 10:34:39 -08:00
Dylan Copeland acd3c0afde Added more strumpack solver options. Added more timings. Added the option to use a hypre Euclid preconditioned GMRES solver for subdomains. Removed unnecessary MPI barriers for scalability. 2019-11-16 19:50:49 -08:00
Dylan Copeland 5ff021d698 Copied HypreEuclid class from master. Added more timings for coarse profiling. Removed the global FEM system assembly. Made the subdomain setup loop in the DDM constructor more scalable. 2019-11-13 13:38:32 -08:00
Dylan Copeland fcfd1184ec More implementation and debugging for SERIAL_INTERFACES version. 2019-11-12 09:58:23 -08:00
Dylan Copeland 81d7160ab2 More implementation of the SERIAL_INTERFACES version. 2019-11-09 09:40:11 -08:00
Dylan Copeland ee0aa40fc1 Implemented the option to use serial meshes and finite element spaces on interfaces. 2019-10-31 10:48:04 -07:00
Dylan Copeland bf8e88374f Fixed some bugs in GPWD. Added the option use HSS preconditioning in strumpack. Implemented a Gauss-Seidel preconditioner for the DD system. 2019-10-25 12:25:16 -07:00
Dylan Copeland c1638392f1 Implemented GPWD from Peng and Lee 2012. 2019-10-18 16:53:54 -07:00
Dylan Copeland d6f6021849 Implemented the option to use the Rawat and Lee 2010 variational formulation. 2019-10-15 14:25:08 -07:00
Dylan Copeland d184cd1344 Implemented the option to define the parallel Cartesian partition with coarse blocks, to ensure each subdomain is confined to a node. 2019-10-14 11:45:49 -07:00
Dylan Copeland d01a84a817 Changed the ordering of subdomain matrix factorizations for better scalability. 2019-10-11 13:07:57 -07:00
Dylan Copeland 8ef1a14021 Debugged the Airy function test for the DD solver. Generalized the DD system for nonzero Dirichlet BC. Debugged the recovery of the global solution from the DD solution. Added a global FEM system residual check for the recovered solution. 2019-10-09 15:03:51 -07:00
Dylan Copeland d9473e6c57 Debugged the Airy test with strumpack direct solver. Added new Orthomin linear solver. 2019-10-08 14:36:51 -07:00
Dylan Copeland d92f63fc10 Adding new Airy function test version of ex3p. 2019-10-07 14:28:08 -07:00
Dylan Copeland 29aa034488 Implemented Airy function test. Generalized interface creator for subdomain partitioning in multiple dimensions. 2019-10-04 12:19:00 -07:00
Dylan Copeland e624c1b2da Generalized the code for first or second order Nedelec elements. 2019-10-02 10:25:15 -07:00
Dylan Copeland 720d2e4f4e Added more options for transmission condition parameters. Fixed some potential bugs in injection operators by initializing y=0. Added subdomain matrix inversion tests. 2019-09-27 12:35:30 -07:00
Dylan Copeland e5baff78f1 Fixed a bug in the case of multiple interfaces per subdomain, in which sdNDcoef prevented interface operators from being summed in AddSubdomainMatrixAndInterfaceMatrix. Fixed a bug, in which the global source was being restricted to subdomain DOF's, by now computing local sources on subdomains. Added more debugging diagnostic code. 2019-09-20 15:41:15 -07:00
Dylan Copeland ac55e5f579 Added the option to use Robin TC. Fixed a bug in the case that some processes have empty matrices in AddSubdomainMatrixAndInterfaceMatrix. 2019-09-03 16:15:32 -07:00
Dylan Copeland 16ff142435 Added more useful output. Added more debugging tests. Debugged the global error computation. 2019-08-30 14:50:12 -07:00
Dylan Copeland d9224ab5f9 More generalization for arbitrary parallel partitioning. 2019-08-28 16:17:42 -07:00
Dylan Copeland 9ef176efa7 Fixed some bugs and made more generalizations for different parallel partitioning of the subdomains and interfaces. 2019-08-26 10:26:56 -07:00
Dylan Copeland 7bc5c5e7ed Added more exact solutions for testing. Generalized the ddoper code to allow for subdomains and interfaces to have different communicators on different sets of processes. 2019-08-16 22:18:17 -07:00
Dylan Copeland 1a7b2f6a02 Extended the code to run for multiple interfaces per subdomain (non-intersecting for now), but results are dubious. Now allowing for imaginary sources. Added more useful error output. 2019-08-09 14:36:31 -07:00
Dylan Copeland 18c97f8f41 Moved ddoper code to a cpp file for faster building. Added the option to use Cartesian parallel partitioning rather than unstructured, which avoids some bugs in the DD code. 2019-08-05 14:21:44 -07:00
Dylan Copeland f03ebe6596 Fixed a Nedelec edge DOF orientation bug in the mapping from global to subdomain DOF's. 2019-08-02 16:58:39 -07:00
Dylan Copeland 1c9d802b2c Debugged InjectionOperator so that the interface operator in parallel matches the one in serial. Added debugging output and tests. Debugged the essential boundary conditions for subdomain matrices. 2019-07-26 11:26:32 -07:00
Dylan Copeland 127389f384 Fixed the interface generation so that interface meshes on different processes do not overlap (except on edges and vertices). Generalized the adding of interface and subdomain matrices to handle cases where interfaces and subdomains are partitioned such that a process may have interface DOF's but not subdomain DOF's for the same edge. 2019-07-05 16:00:11 -07:00
Dylan Copeland 9d5eb79ad8 Extended the code that constructs the map from interface to subdomain DOF's, to handle cases where a subdomain locally (on a process) intersects the interface on only an edge. 2019-06-28 16:01:43 -07:00
Dylan Copeland c5d084583e Fixed some parallel bugs. 2019-06-20 15:25:31 -07:00
Dylan Copeland e97bf9ae57 Debugged the solver option that uses Strumpack to solve subdomain block matrices, assembled from Hypre blocks into a single Hypre matrix for each subdomain. 2019-06-17 09:38:23 -07:00
Dylan Copeland 8b3135bce3 Implemented option for direct strumpack solver for entire block subdomain matrices. There is still at least one bug. 2019-05-24 16:09:27 -07:00
Dylan Copeland c83f9b05bf Made some minor changes for testing. 2019-05-13 13:25:56 -07:00
Dylan Copeland 93f4c33484 Implemented the option to use STRUMPACK as a solver for each entire complex block subdomain operator. Implemented essential boundary conditions on the exterior boundary on each subdomain. Implemented subdomain solution recovery from the interface solution. Added L2 error computation and VisIt output for subdomain solutions. 2019-05-09 21:18:54 -07:00
Dylan Copeland ed1176b8a4 Implemented parameters from Rawat and Lee, 2010. 2019-05-06 15:44:28 -07:00
Dylan Copeland 9e85397743 Implemented the addition of a subdomain matrix and an interface matrix in parallel. Added some options to equate redundant interface variables. 2019-05-02 08:59:12 -07:00
Dylan Copeland d12db40c6b Added essential boundary conditions for f and rho on the interfaces. Added a testing function. 2019-04-24 14:25:36 -07:00
Dylan Copeland 81e05d3138 Fixed some bugs. The most noteworthy is that iterative_mode needs to be false for IterativeSolver instances used in a ProductOperator. 2019-04-12 14:52:48 -07:00
Dylan Copeland ce66e804eb Added a subdomain preconditioner for the complex case. 2019-04-10 13:05:13 -07:00
Dylan Copeland ec66fe6cad Extended the DD solver to complex arithmetic. 2019-04-04 15:14:26 -07:00
Dylan Copeland 73e289c0db Fixed some bugs caused by offset arrays being reused for multiple block operators. 2019-03-29 15:43:46 -07:00
Dylan Copeland 494202870e Adding a file. 2019-03-29 12:23:51 -07:00
Dylan Copeland bd904b6fe1 Fixed some bugs (e.g. interface operators were not being constructed correctly in parallel). Implemented a block diagonal preconditioner for the subdomain matrices, with Strumpack inversion for each block. Implemented the reduction of the source to the DD system on the interfaces, and the recovery of the domain solution from the DD solution. Implemented the construction of a single Strumpack matrix from an arbitrary array of HypreParMatrix blocks, but this may not be necessary and has not been tested. 2019-03-29 12:22:29 -07:00
Dylan Copeland c1f5a27954 Debugged the DDMInterfaceOperator constructor. 2019-03-21 20:45:58 -07:00
Dylan Copeland 07da8fbf68 Implemented mappings between interfaces and subdomain surfaces. 2019-03-20 13:26:54 -07:00
Dylan Copeland 914ad33136 Completed the implementation of the reduced subdomain surface DDM operator. 2019-03-08 11:44:45 -08:00
Dylan Copeland 533df613e9 More implementation of the DD operator, including the interface to subdomain surface map. 2019-03-06 11:05:07 -08:00
Dylan Copeland 7f887bf5a1 Added SumOperator class to linalg/operator*. Implemented some parts of the DD interface operator. 2019-02-08 20:38:27 -08:00
Dylan Copeland 48a375f002 Implemented parallel subdomain interface mesh generation, with testing. 2019-02-04 13:27:00 -08:00
Dylan Copeland 4de50a2f77 Implemented parallel subdomain mesh construction and testing. 2019-02-01 11:58:44 -08:00
Dylan Copeland 1976aa81d4 Refactored HypreIAMS. Changed the Helmholtz boundary condition. 2019-01-25 09:47:43 -08:00
Dylan Copeland 1c327c9092 Added Maxwell CSL correction and geometry-dependent boundary conditions for component-wise Helmholtz solvers. 2019-01-18 09:13:50 -08:00
Dylan Copeland 892648db73 Added the option to use an AMS variant with component-wise Helmholtz direct solvers. 2019-01-10 16:01:30 -08:00
Dylan Copeland 612499efde Added an option to test the complex shifted Laplace preconditioner, using STRUMPACK for exact inversion of the preconditioner, for Helmholtz and Maxwell tests. 2018-12-28 15:48:00 -08:00
Dylan Copeland 24c26bb2a4 Added an iterative solver for the normal equation. 2018-12-14 11:31:41 -08:00
Dylan Copeland 2f38d4f481 In ex3p_sp, added options to use the definite matrix (for preconditioning) or the square of the matrix. Added a Helmholtz test. 2018-11-19 12:59:44 -08:00
Dylan Copeland 71f016b23b Implemented additive and multiplicative versions of IAMS. 2018-11-02 08:40:05 -07:00
Dylan Copeland 0d963e367b Finished implementing indefinite Maxwell solver with hypre and strumpack for auxiliary space direct solvers. 2018-10-29 16:05:23 -07:00
Dylan Copeland 30b328cbd3 Partial implementation of an experimental indefinite Maxwell solver. 2018-10-19 16:04:07 -07:00
Dylan Copeland 4c746bd831 Added solver timer. 2018-10-17 09:08:30 -07:00
Dylan Copeland 98b26dba79 Adding strumpack version of ex3p. 2018-10-15 10:11:48 -07:00
Stowell, Mark L 0d1ca9dc79 Merge remote-tracking branch 'origin/complex-mfem-dev' into complex-strumpack-dev 2018-10-10 21:09:42 -07:00
Stowell, Mark L d0a58f0b3d This functionality seems to have vanished from the latest STRUMPACK 2018-09-25 16:52:19 -07:00
Stowell, Mark L 82fdc3d4ce Merge remote-tracking branch 'origin/complex-mfem-dev' into complex-strumpack-dev 2018-09-25 13:19:27 -07:00
Stowell, Mark L 76f0d6a956 Merge remote-tracking branch 'origin/complex-mfem-dev' into complex-strumpack-dev 2018-09-08 14:56:47 -07:00
Mark L. Stowell 72bf549085 Small changes to assist debugging 2018-08-31 14:34:35 -07:00
Stowell, Mark L 63ee675bd4 Avoiding template instanitations each time strumpack header is included 2018-08-24 19:39:07 -07:00
Stowell, Mark L 42a509538d Adding STRUMPACK support to example 21 2018-08-23 15:04:09 -07:00
Stowell, Mark L ca7cb115b1 CSRMatrixMPI does not _borrow_ the data array, it copies it so this should avoid a large memory leak 2018-08-23 14:44:56 -07:00
Stowell, Mark L 0dfa567ce3 styling changes 2018-08-23 14:44:46 -07:00
Stowell, Mark L 837e2abed4 Adding wrappers for STRUMPACK's complex sparse matrix and solver 2018-08-23 14:44:08 -07:00
167 changed files with 37253 additions and 7749 deletions
-61
View File
@@ -1,61 +0,0 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 30
# Number of days of inactivity before an Issue or Pull Request with the stale
# label is closed. Set to false to disable. If disabled, issues still need to
# be closed manually, but will remain marked as stale.
daysUntilClose: 7
# Only issues or pull requests with all of these labels are check if stale.
# Defaults to `[]` (disabled)
onlyLabels: []
# Issues or Pull Requests with these labels will never be considered stale. Set
# to `[]` to disable
exemptLabels:
- bug
- WIP
- ready-for-review
- in-review
- in-next
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false
# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: false
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
:warning: This issue or PR has been automatically marked as stale because it has not
had any activity in the last month. *If no activity occurs in the next week, it will
be automatically closed.* Thank you for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Limit to only `issues` or `pulls`
# only: issues
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
# pulls:
# daysUntilStale: 30
# markComment: >
# This pull request has been automatically marked as stale because it has not had
# recent activity. It will be closed if no further activity occurs. Thank you
# for your contributions.
# issues:
# exemptLabels:
# - confirmed
+53 -67
View File
@@ -102,29 +102,28 @@ examples/ex25.mesh
examples/ex25-*.gf
examples/ex25p-*.*
examples/amgx/ex1
examples/amgx/ex1p
examples/amgx/.logamgx
examples/amgx/refined.mesh
examples/amgx/sol.gf
examples/amgx/mesh.*
examples/amgx/sol.*
examples/sundials/ex9
examples/sundials/ex1[06]
examples/sundials/ex9p
examples/sundials/ex1[06]p
examples/gingko/ex1
examples/gingko/refined.mesh
examples/gingko/sol.gf
examples/gingko/mesh.*
examples/gingko/sol.*
examples/hiop/ex9
examples/hiop/ex9p
examples/hiop/ex9.mesh
examples/hiop/ex9-mesh.*
examples/hiop/ex9-init.*
examples/hiop/ex9-final.*
examples/sundials/ex9.mesh
examples/sundials/ex9-mesh.*
examples/sundials/ex9-init.*
examples/sundials/ex9-final.*
examples/sundials/Example9*
examples/sundials/deformed.*
examples/sundials/velocity.*
examples/sundials/elastic_energy.*
examples/sundials/ex16.mesh
examples/sundials/ex16-mesh.*
examples/sundials/ex16-init.*
examples/sundials/ex16-final.*
examples/sundials/Example16*
examples/petsc/ex[1-69]p
examples/petsc/ex1[0-1]p
examples/petsc/mesh.*
examples/petsc/sol.*
examples/petsc/sol_p.*
@@ -142,51 +141,28 @@ examples/petsc/mode_*
examples/pumi/ex1
examples/pumi/ex[126]p
examples/hiop/ex9.mesh
examples/hiop/ex9-mesh.*
examples/hiop/ex9-init.*
examples/hiop/ex9-final.*
examples/pumi/refined.mesh
examples/pumi/sol.gf
examples/pumi/mesh.*
examples/pumi/sol.*
examples/pumi/displaced.mesh
examples/sundials/ex9
examples/sundials/ex1[06]
examples/sundials/ex9p
examples/sundials/ex1[06]p
examples/sundials/ex9.mesh
examples/sundials/ex9-mesh.*
examples/sundials/ex9-init.*
examples/sundials/ex9-final.*
examples/sundials/Example9*
examples/sundials/deformed.*
examples/sundials/velocity.*
examples/sundials/elastic_energy.*
examples/sundials/ex16.mesh
examples/sundials/ex16-mesh.*
examples/sundials/ex16-init.*
examples/sundials/ex16-final.*
examples/sundials/Example16*
examples/superlu/ex1p
examples/superlu/mesh.*
examples/superlu/sol.*
miniapps/adjoint/cvsRoberts_ASAi_dns
miniapps/adjoint/adjoint_advection_diffusion
miniapps/electromagnetics/volta
miniapps/electromagnetics/tesla
miniapps/electromagnetics/maxwell
miniapps/electromagnetics/joule
miniapps/electromagnetics/Volta-AMR*
miniapps/electromagnetics/Tesla-AMR*
miniapps/electromagnetics/Maxwell-Parallel*
miniapps/electromagnetics/Joule_*
miniapps/gslib/field-diff
miniapps/gslib/field-interp
miniapps/gslib/findpts
miniapps/gslib/pfindpts
miniapps/meshing/mobius-strip
miniapps/meshing/klein-bottle
miniapps/meshing/toroid
@@ -200,6 +176,7 @@ miniapps/meshing/pmesh-optimizer
miniapps/meshing/minimal-surface
miniapps/meshing/pminimal-surface
miniapps/meshing/polar-nc
miniapps/meshing/mobius-strip.mesh
miniapps/meshing/klein-bottle.mesh
miniapps/meshing/toroid-*.mesh
@@ -213,25 +190,9 @@ miniapps/meshing/optimized*
miniapps/meshing/perturbed*
miniapps/meshing/polar-nc.mesh
miniapps/navier/navier_mms
miniapps/navier/navier_kovasznay
miniapps/navier/navier_tgv
miniapps/navier/navier_shear
miniapps/navier/navier_3dfoc
miniapps/navier/tgv_out*.txt
miniapps/navier/*_output
miniapps/nurbs/nurbs_ex1
miniapps/nurbs/nurbs_ex1p
miniapps/nurbs/nurbs_ex11p
miniapps/nurbs/refined.mesh
miniapps/nurbs/mesh.*
miniapps/nurbs/sol.*
miniapps/nurbs/mode_*
miniapps/nurbs/Example1*
miniapps/performance/ex1
miniapps/performance/ex1p
miniapps/performance/refined.mesh
miniapps/performance/mesh.*
miniapps/performance/sol.*
@@ -249,6 +210,7 @@ miniapps/toys/rubik
miniapps/toys/snake
miniapps/toys/lissajous
miniapps/toys/mondrian
miniapps/toys/snake-init.mesh
miniapps/toys/snake-user.mesh
miniapps/toys/snake-joined.mesh
@@ -263,13 +225,37 @@ miniapps/toys/lissajous.mesh
miniapps/toys/lissajous.gf
miniapps/toys/mondrian.mesh
miniapps/nurbs/nurbs_ex1
miniapps/nurbs/nurbs_ex1p
miniapps/nurbs/nurbs_ex11p
miniapps/nurbs/refined.mesh
miniapps/nurbs/mesh.*
miniapps/nurbs/sol.*
miniapps/nurbs/mode_*
miniapps/nurbs/Example1*
miniapps/gslib/field-diff
miniapps/gslib/field-interp
miniapps/gslib/findpts
miniapps/gslib/pfindpts
miniapps/navier/navier_mms
miniapps/navier/navier_kovasznay
miniapps/navier/navier_tgv
miniapps/navier/navier_shear
miniapps/navier/navier_3dfoc
miniapps/navier/tgv_out*.txt
miniapps/navier/*_output
miniapps/adjoint/cvsRoberts_ASAi_dns
miniapps/adjoint/adjoint_advection_diffusion
# Unit test binary and outputs
tests/unit/output_meshes
tests/unit/unit_tests
tests/unit/punit_tests
tests/unit/sedov_tests_*
tests/unit/psedov_tests_*
tests/unit/ceed_tests
# Test script output
tests/scripts/*.err
+1 -1
View File
@@ -37,7 +37,7 @@ build_mfem_debug_ser_lassen:
- mkdir -p ${BUILD_PATH}
- cp -r ${CI_PROJECT_DIR} ${BUILD_PATH}/${CI_PROJECT_NAME}_lassen_ser_debug
- cd ${BUILD_PATH}/${CI_PROJECT_NAME}_lassen_ser_debug
- lalloc 1 -W 5 -q pdebug make -j cuda MFEM_DEBUG="YES" CPPFLAGS=-O2 CUDA_ARCH=sm_70
- lalloc 1 -W 5 -q pdebug make -j cuda MFEM_DEBUG="YES" CUDA_ARCH=sm_70
# Sanity check
sanitycheck_mfem_ser_lassen:
+2 -62
View File
@@ -11,16 +11,6 @@
Version 4.1.1 (development)
===========================
libCEED integration improvements
--------------------------------
- Add support for all types of (scalar) Coefficient.
- Add support for VectorMassIntegrator and VectorDiffusionIntegrator.
- Add support for AssemblyLevel::NONE for MassIntegrator, DiffusionIntegrator,
VectorMassIntegrator, and VectorDiffusionIntegrator. This level of assembly
fully applies on the fly the finite element operator.
Meshing improvements
--------------------
- The graph linear ordering library Gecko, previously an external dependency, is
@@ -66,30 +56,10 @@ Performance improvements
Improved GPU capabilities
-------------------------
- Added a new solver class for simple integration with NVIDIA's multigrid
library, AmgX. The AmgX class is designed to work as a standalone solver or
preconditioner for existing MFEM solvers. It uses MFEM's sparse matrix format
for serial runs and the HypreParMatrix format for parallel runs.
The new solver may be configured to run with one GPU per MPI rank or with more
MPI ranks than GPUs. In the latter case, matrices and vectors are consolidated
to ranks communicating with the GPUs and the solution is then broadcasted.
Although CUDA is required to build, the AmgX support is compatible with the
MFEM CPU device configuration. The examples/amgx folder illustrates how to
integrate AmgX in existing MFEM applications.
The AmgX solver class is partially based on: "AmgXWrapper: An interface
between PETSc and the NVIDIA AmgX library", by Pi-Yueh Chuang and Lorena
A. Barba, doi:10.21105/joss.00280.
- Added support for Chebyshev accelerated polynomial smoother on GPU.
- Optimized AMD/HIP kernel support.
- Enabled HIP support in the libCEED integration, which is now available via the
"ceed-hip" device backend.
- Added a Full Assembly mode compatible with Device kernel execution. This
assembly level builds on top of the current Element Assembly kernels to
compute a global sparse matrix. All integrators supported by element assembly
@@ -97,15 +67,14 @@ Improved GPU capabilities
- Added CUDA support for sparse matrix-vector multiplication with cuSPARSE.
- Added AmgX solver class for simple integration with NVIDIA's multigrid library.
- Added support for BlockOperator on GPU. See the updated Example 5.
- Added partial assembly and GPU support for complex operators, including the
classes ComplexOperator, [Par]ComplexGridFunction, [Par]ComplexLinearForm, and
[Par]SesquilinearForm. See the updated Example 22.
- Added CUDA support for SUNDIALS ODE integrators. See the updated SUNDIALS
modification of Example 9/9p.
Discretization improvements
---------------------------
- Added support for matrix-free interpolation and restriction operators between
@@ -132,9 +101,6 @@ Discretization improvements
Additionally, new LinearForm integrators were also added which make use of
these new QuadratureFunction coefficient classes.
- Non-conforming meshes are now supported with block nonlinear forms. See the
updated Example 19/19p.
- Added support face integrals on the boundaries of NURBS meshes.
- Added support for interpolation of functions in L2, H(div) and H(curl)
@@ -145,12 +111,6 @@ Discretization improvements
member methods in GridFunction and ParGridFunction. See the rates.cpp file in
the tests/convergence directory for sample usage.
- The C-function based coefficient classes (FunctionCoefficient,
VectorFunctionCoefficient, and MatrixFunctionCoefficient) now use the more
general std::function class template. This allows the classes to be backward
compatible (i.e. they can still work with C-functions) and, in addition,
support any "callable", e.g. lambda functions.
Linear and nonlinear solvers
----------------------------
- Added power method to iteratively estimate the largest eigenvalue and the
@@ -159,9 +119,6 @@ Linear and nonlinear solvers
- Added initial support for h- and p-multigrid solvers and preconditioners for
matrix-based and matrix-free discretizations with basic GPU capability.
- Added wrappers for Hypre's flexible GMRES solver and the new parallel ILU
preconditioner. The latter requires hypre version 2.19.0 or later.
- Added a new IterativeSolverMonitor class that allows to monitor the residual
and solution during the solving process of an IterativeSolver after every
iteration.
@@ -182,15 +139,6 @@ Linear and nonlinear solvers
- Added partially assembled convergent diagonal preconditioner for adaptively
refined meshes (i.e. non-conforming finite element spaces), see Example 6/6p.
- Upgraded SuperLU interface to use SuperLU_DIST 6.3.1. Added a simple SuperLU
example in the new directory examples/superlu.
- Extended the KINSOL (SUNDIALS) nonlinear solver interface to support the
Jacobian-free Newton-Krylov method. A usage example is shown in Example 10p.
- Added an interface to the MKL CPardiso solver -- an MPI-parallel sparse direct
solver developed by Intel. See Example 11p for an illustration of its usage.
New and updated examples and miniapps
-------------------------------------
- Added a new example, Example 25/25p, to demonstrate the use of a Perfectly
@@ -265,8 +213,6 @@ Improved testing
triggered only by LLNL developers, see .gitlab-ci.yml, the .gitlab directory
and the updated CONTRIBUTING.md file.
- Add tests for the libCEED integration in MFEM.
- Added testing of the parallel mesh format in tests/par-mesh-format.
Miscellaneous
@@ -279,9 +225,6 @@ Miscellaneous
ParaView v5.8.1. In addition, ADIOS2 allows for setting a user-defined number
of data substreams/subfiles at scale. See examples 5, 9, 12, 16.
- Added VTU output of boundary elements and attributes and parallel VTU (PVTU)
output of parallel meshes for visualization using ParaView.
- The integration order used in the ComputeLpError and ComputeElementLpError
methods of class GridFunction has been increased.
@@ -290,9 +233,6 @@ Miscellaneous
- Renamed "Backend::DEBUG" to "Backend::DEBUG_DEVICE" to avoid conflicts,
as DEBUG is sometimes used as a macro.
- Change the IntegrationRule inside VectorDiffusionIntegrator to use the same
quadrature as DiffusionIntegrator.
Version 4.1, released on March 10, 2020
=======================================
+8 -15
View File
@@ -240,14 +240,12 @@ endif()
# SUNDIALS
if (MFEM_USE_SUNDIALS)
set(SUNDIALS_COMPONENTS CVODES ARKODE KINSOL NVector_Serial)
if (MFEM_USE_MPI)
list(APPEND SUNDIALS_COMPONENTS NVector_Parallel NVector_MPIPlusX)
if (NOT MFEM_USE_MPI)
find_package(SUNDIALS REQUIRED NVector_Serial CVODES ARKODE KINSOL)
else()
find_package(SUNDIALS REQUIRED
NVector_Serial NVector_Parallel NVector_ParHyp CVODES ARKODE KINSOL)
endif()
if (MFEM_USE_CUDA)
list(APPEND SUNDIALS_COMPONENTS NVector_Cuda)
endif()
find_package(SUNDIALS REQUIRED ${SUNDIALS_COMPONENTS})
endif()
# Mesquite
@@ -298,7 +296,8 @@ if (MFEM_USE_CEED)
endif()
if (MFEM_USE_AMGX)
find_package(AMGX REQUIRED)
include_directories(${AMGX_DIR}/include)
link_directories(${AMGX_DIR}/lib64 -L${AMGX_DIR}/lib -Xlinker=-rpath=${CUDA_HOME}/lib64)
endif()
if (MFEM_USE_CONDUIT)
@@ -352,12 +351,6 @@ if (MFEM_USE_ADIOS2)
find_package(ADIOS2 REQUIRED)
endif()
if (MFEM_USE_MKL_CPARDISO)
if (MFEM_USE_MPI)
find_package(MKL_CPARDISO REQUIRED MKL_SEQUENTIAL MKL_LP64 MKL_MPI_WRAPPER)
endif()
endif()
# MFEM_TIMER_TYPE
if (NOT DEFINED MFEM_TIMER_TYPE)
if (APPLE)
@@ -384,7 +377,7 @@ endif()
set(MFEM_TPLS MPI_CXX OPENMP BLAS LAPACK METIS HYPRE SuiteSparse SUNDIALS PETSC
SLEPC MESQUITE SuperLUDist STRUMPACK AXOM CONDUIT Ginkgo GNUTLS GSLIB NETCDF
MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE ADIOS2
CUSPARSE MKL_CPARDISO AMGX)
CUSPARSE AMGX)
# Add all *_FOUND libraries in the variable TPL_LIBRARIES.
set(TPL_LIBRARIES "")
set(TPL_INCLUDE_DIRS "")
-6
View File
@@ -98,18 +98,15 @@ The MFEM source code has the following structure:
│ └── web
│ └── examples
├── examples
│ ├── amgx
│ ├── ginkgo
│ ├── hiop
│ ├── petsc
│ ├── pumi
│ └── sundials
| └── superlu
├── fem
│ └── libceed
├── general
├── linalg
│ └── simd
├── mesh
├── miniapps
│ ├── adjoint
@@ -117,14 +114,11 @@ The MFEM source code has the following structure:
│ ├── electromagnetics
│ ├── gslib
│ ├── meshing
│ ├── navier
│ ├── nurbs
│ ├── performance
│ ├── tools
│ └── toys
└── tests
├── convergence
├── par-mesh-format
├── scripts
├── unit
│ ├── ...
+16 -35
View File
@@ -344,27 +344,24 @@ MFEM_USE_SUPERLU = YES/NO
SuperLURowLocMatrix a distributed CSR matrix class needed by SuperLU. When
enabled, this option uses the SUPERLU_* library options, see below.
MFEM_USE_SUPERLU5 = YES/NO
If SuperLU functionality is enabled, use the older 5.1.0 version rather than
the more recent 6+ versions.
MFEM_USE_STRUMPACK = YES/NO
Enable MFEM functionality based on the STRUMPACK sparse direct solver and
preconditioner through the STRUMPACKSolver and STRUMPACKRowLocMatrix
classes. When enabled, this option uses the STRUMPACK_* library options, see
below.
MFEM_USE_AMGX = YES/NO
Enable MFEM functionality based on the AMGX multigrid library from
NVIDIA. When enabled, this options enables the user to use SparseMatrices
and HypreParMatrices to solve linear systems using routines from the AMGX
library.
MFEM_USE_GINKGO = YES/NO
Enable MFEM functionality based on the Ginkgo library, which provides
iterative linear solvers and preconditioners with OpenMP, CUDA backends, see
https://github.com/ginkgo-project/ginkgo. When enabled, the user can use
Ginkgo's solvers and preconditioners as shown in examples/ginkgo/.
MFEM_USE_AMGX = YES/NO
Enable MFEM functionality based on the AmgX multigrid library from NVIDIA.
Allows the user to use SparseMatrices and HypreParMatrices to solve linear
systems with the routines from the AmgX library.
MFEM_USE_GNUTLS = YES/NO
Enable secure socket support in class socketstream, using the auxiliary
GnuTLS_* classes, based on the GnuTLS library. This option may be useful in
@@ -495,13 +492,6 @@ MFEM_USE_CEED = YES/NO
library for performant high-order operator evaluation developed by the Center
for Efficient Exascale Discretizations in the Exascale Computing Project.
MFEM_USE_MKL_CPARDISO = YES/NO
Enables the interface to MKL CPardiso: the Intel MKL Parallel Direct Sparse
Solver for Clusters. Make sure to set the correct values for MKL_MPI_WRAPPER
and MKL_LIBRARY_SUBDIR as shown in defaults.mk. If you configure MFEM with
MFEM_USE_LAPACK=YES, verify that the MKL LAPACK libraries are used. The
OpenMP capabilities are disabled at link time.
MFEM_BUILD_TAG = (any value)
An optional tag to characterize the build. Exported to config/config.mk.
Can be used to identify the MFEM build from other makefiles.
@@ -551,14 +541,11 @@ The specific libraries and their options are:
Beginning with MFEM v3.3, SUNDIALS v2.7.0 is supported.
Beginning with MFEM v3.3.2, SUNDIALS v3.0.0 is also supported.
Beginning with MFEM v4.1, only SUNDIALS v5.0.0+ is supported.
When MFEM_USE_CUDA is enabled, only SUNDIALS v5.4.0+ is supported.
If MFEM_USE_MPI is enabled, we expect that SUNDIALS is built with support for
both MPI and hypre.
If MFEM_USE_CUDA is enabled, we expect that SUNDIALS is built with support
for CUDA.
URL: http://computation.llnl.gov/projects/sundials/sundials-software
Options: SUNDIALS_OPT, SUNDIALS_LIB.
Versions: SUNDIALS >= 5.0.0, SUNDIALS >= 5.4.0 for CUDA suppport.
Versions: SUNDIALS >= 5.0.0.
- Mesquite (optional), used when MFEM_USE_MESQUITE = YES.
URL: http://trilinos.org/oldsite/packages/mesquite
@@ -593,11 +580,6 @@ The specific libraries and their options are:
URL: https://ginkgo-project.github.io
Options: GINKGO_OPT (Not used), GINKGO_LIB.
- AmgX (optional), used when MFEM_USE_AMGX = YES.
URL: https://github.com/NVIDIA/AMGX
Options: AMGX_OPT, AMGX_LIB.
Versions: AmgX >= 2.1, older versions may work too.
- GnuTLS (optional), used when MFEM_USE_GNUTLS = YES. On most Linux systems,
GnuTLS is available as a development package, e.g. gnutls-devel. On Mac OS X,
one can get the library through the Homebrew package manager (http://brew.sh).
@@ -669,16 +651,16 @@ The specific libraries and their options are:
Options: GSLIB_OPT, GSLIB_LIB.
Versions: GSLIB >= 1.0.5.
- MKL CPardiso (optional), used when MFEM_USE_MKL_CPARDISO = YES.
URL: https://software.intel.com/content/www/us/en/develop/tools/math-kernel-library.html
Options: MKL_CPARDISO_OPT, MKL_CPARDISO_LIB.
Versions: Intel MKL >= 2020.
- CUDA (optional), used when MFEM_USE_CUDA = YES.
URL: https://developer.nvidia.com/cuda-toolkit
Options: CUDA_CXX, CUDA_ARCH, CUDA_OPT, CUDA_LIB.
Versions: CUDA >= 9.1, older versions may work too.
- AMGX (optional), used when MFEM_USE_AMGX = YES.
URL: https://github.com/NVIDIA/AMGX
Options: AMGX_OPT, AMGX_LIB.
Versions: AMGX >= 2.1, older versions may work too.
- HIP (optional), used when MFEM_USE_HIP = YES.
URL: https://rocm.github.io/ROCmInstall.html
Options: HIP_CXX, HIP_ARCH, HIP_OPT, HIP_LIB.
@@ -686,13 +668,13 @@ The specific libraries and their options are:
- OCCA (optional), used when MFEM_USE_OCCA = YES.
URL: https://libocca.org
Options: OCCA_DIR, OCCA_OPT, OCCA_LIB.
Versions: OCCA >= 1.1.0.
Versions: OCCA >= 1.0.9.
- libCEED (optional), used when MFEM_USE_CEED = YES.
URL: https://github.com/CEED/libCEED
https://ceed.exascaleproject.org/libceed
Options: CEED_DIR, CEED_OPT, CEED_LIB.
Versions: libCEED >= 0.7.
Versions: libCEED > 0.6, git-hash bdfed75.
- RAJA (optional), used when MFEM_USE_RAJA = YES.
Beginning with MFEM v4.1, only RAJA v0.10.0+ is supported.
@@ -835,8 +817,8 @@ MFEM_USE_MESQUITE
MFEM_USE_SUITESPARSE
MFEM_USE_SUPERLU
MFEM_USE_STRUMPACK
MFEM_USE_GINKGO
MFEM_USE_AMGX
MFEM_USE_GINKGO
MFEM_USE_GNUTLS
MFEM_USE_NETCDF
MFEM_USE_MPFR
@@ -885,12 +867,11 @@ The CMake build system adds auto-detection for the following packages/libraries:
- HYPRE
- METIS - The option MFEM_USE_METIS_5 is auto-detected.
- ParMETIS
- MESQUITE
- SuiteSparse
- SuperLUDist, STRUMPACK
- ParMETIS
- Ginkgo
- AMGX
- GNUTLS - Extends the built-in CMake support, to search GNUTLS_DIR as well.
- NETCDF
- MPFR
+1 -2
View File
@@ -25,8 +25,7 @@ This project distributes the sources of several external software products with
their own respective licenses which can be found in their code and attached
license files. These software products and their licenses are as follows:
* AmgXWrapper (linalg/amgxsolver.{hpp,cpp}) -- MIT license
* Catch++ (tests/unit/catch.hpp) -- Boost 1.0 license
* Gecko (general/gecko.{cpp,hpp}) -- BSD 3-clause license
* Picojson (fem/picojson.h) -- Custom 2-clause license
* Catch++ (tests/unit/catch.hpp) -- Boost 1.0 license
* Zstr (general/zstr.hpp) -- MIT license
-1
View File
@@ -34,7 +34,6 @@ set(MFEM_USE_SUITESPARSE @MFEM_USE_SUITESPARSE@)
set(MFEM_USE_SUPERLU @MFEM_USE_SUPERLU@)
set(MFEM_USE_STRUMPACK @MFEM_USE_STRUMPACK@)
set(MFEM_USE_GINKGO @MFEM_USE_GINKGO@)
set(MFEM_USE_AMGX @MFEM_USE_AMGX@)
set(MFEM_USE_GNUTLS @MFEM_USE_GNUTLS@)
set(MFEM_USE_GSLIB @MFEM_USE_GSLIB@)
set(MFEM_USE_NETCDF @MFEM_USE_NETCDF@)
+6 -9
View File
@@ -74,9 +74,6 @@
// [Deprecated] Enable experimental OpenMP support. Requires MFEM_THREAD_SAFE.
#cmakedefine MFEM_USE_LEGACY_OPENMP
// Internal MFEM option: enable group/batch allocation for some small objects.
#cmakedefine MFEM_USE_MEMALLOC
// Enable MFEM functionality based on the Mesquite library.
#cmakedefine MFEM_USE_MESQUITE
@@ -89,12 +86,12 @@
// Enable MFEM functionality based on the STRUMPACK library.
#cmakedefine MFEM_USE_STRUMPACK
// Internal MFEM option: enable group/batch allocation for some small objects.
#cmakedefine MFEM_USE_MEMALLOC
// Enable functionality based on the Ginkgo library
#cmakedefine MFEM_USE_GINKGO
// Enable MFEM functionality based on the AmgX library
#cmakedefine MFEM_USE_AMGX
// Enable MFEM functionality based on the GnuTLS library
#cmakedefine MFEM_USE_GNUTLS
@@ -129,6 +126,9 @@
// Requires a CUDA compiler (nvcc).
#cmakedefine MFEM_USE_CUDA
// Enable MFEM functionality based on the AMGX library
#cmakedefine MFEM_USE_AMGX
// Enable MFEM functionality based on the RAJA library
#cmakedefine MFEM_USE_RAJA
@@ -159,7 +159,4 @@
// library.
#cmakedefine MFEM_USE_SIMMETRIX
// Enable interface to the MKL CPardiso library.
#cmakedefine MFEM_USE_MKL_CPARDISO
#endif // MFEM_CONFIG_HEADER
-20
View File
@@ -1,20 +0,0 @@
# Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Defines the following variables:
# - AMGX_FOUND
# - AMGX_LIBRARIES
# - AMGX_INCLUDE_DIRS
include(MfemCmakeUtilities)
set(AMGX_REQUIRED_LIBRARIES cusparse cusolver cublas nvToolsExt)
mfem_find_package(AMGX AMGX AMGX_DIR "include" "amgx_c.h" "lib" "amgx"
"Paths to headers required by AMGX." "Libraries required by AMGX.")
@@ -1,32 +0,0 @@
# Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Defines the following variables:
# - MKL_CPARDISO_FOUND
# - MKL_CPARDISO_LIBRARIES
# - MKL_CPARDISO_INCLUDE_DIRS
if(NOT MKL_MPI_WRAPPER_LIB)
message(FATAL_ERROR "MKL CPardiso enabled but no MKL MPI Wrapper lib specified")
endif()
if(NOT MKL_LIBRARY_DIR)
message(WARNING "Using default MKL library path. Double check the variable MKL_LIBRARY_DIR")
set(MKL_LIBRARY_DIR "lib")
endif()
include(MfemCmakeUtilities)
mfem_find_package(MKL_CPARDISO MKL_CPARDISO
MKL_CPARDISO_DIR "include" mkl_cluster_sparse_solver.h ${MKL_LIBRARY_DIR} mkl_core
"Paths to headers required by MKL CPardiso." "Libraries required by MKL CPARDISO."
ADD_COMPONENT MKL_LP64 "include" "" ${MKL_LIBRARY_DIR} mkl_intel_lp64
ADD_COMPONENT MKL_SEQUENTIAL "include" "" ${MKL_LIBRARY_DIR} mkl_sequential
ADD_COMPONENT MKL_MPI_WRAPPER "include" "" ${MKL_LIBRARY_DIR} ${MKL_MPI_WRAPPER_LIB})
+2 -6
View File
@@ -20,14 +20,10 @@ mfem_find_package(SUNDIALS SUNDIALS SUNDIALS_DIR
"Paths to headers required by SUNDIALS." "Libraries required by SUNDIALS."
ADD_COMPONENT NVector_Serial
"include" nvector/nvector_serial.h "lib" sundials_nvecserial
ADD_COMPONENT NVector_Cuda
"include" nvector/nvector_cuda.h "lib" sundials_nveccuda
ADD_COMPONENT NVector_ParHyp
"include" nvector/nvector_parhyp.h "lib" sundials_nvecparhyp
ADD_COMPONENT NVector_Parallel
"include" nvector/nvector_parallel.h "lib" sundials_nvecparallel
ADD_COMPONENT NVector_MPIPlusX
"include" nvector/nvector_mpiplusx.h "lib" sundials_nvecmpiplusx
ADD_COMPONENT NVector_ParHyp
"include" nvector/nvector_parhyp.h "lib" sundials_nvecparhyp
ADD_COMPONENT CVODE "include" cvode/cvode.h "lib" sundials_cvode
ADD_COMPONENT CVODES "include" cvodes/cvodes.h "lib" sundials_cvodes
ADD_COMPONENT ARKODE "include" arkode/arkode.h "lib" sundials_arkode
@@ -738,11 +738,10 @@ function(mfem_export_mk_files)
MFEM_DEBUG MFEM_USE_EXCEPTIONS MFEM_USE_ZLIB MFEM_USE_LIBUNWIND
MFEM_USE_LAPACK MFEM_THREAD_SAFE MFEM_USE_OPENMP MFEM_USE_LEGACY_OPENMP
MFEM_USE_MEMALLOC MFEM_USE_SUNDIALS MFEM_USE_MESQUITE MFEM_USE_SUITESPARSE
MFEM_USE_SUPERLU MFEM_USE_STRUMPACK MFEM_USE_GINKGO MFEM_USE_AMGX
MFEM_USE_GNUTLS MFEM_USE_GSLIB MFEM_USE_NETCDF MFEM_USE_PETSC
MFEM_USE_SLEPC MFEM_USE_MPFR MFEM_USE_SIDRE MFEM_USE_CONDUIT MFEM_USE_PUMI
MFEM_USE_CUDA MFEM_USE_OCCA MFEM_USE_RAJA MFEM_USE_UMPIRE MFEM_USE_SIMD
MFEM_USE_ADIOS2)
MFEM_USE_SUPERLU MFEM_USE_STRUMPACK MFEM_USE_GNUTLS
MFEM_USE_GSLIB MFEM_USE_NETCDF MFEM_USE_PETSC MFEM_USE_SLEPC MFEM_USE_MPFR MFEM_USE_SIDRE
MFEM_USE_CONDUIT MFEM_USE_PUMI MFEM_USE_CUDA MFEM_USE_OCCA MFEM_USE_RAJA
MFEM_USE_UMPIRE MFEM_USE_SIMD MFEM_USE_ADIOS2)
foreach(var ${CONFIG_MK_BOOL_VARS})
if (${var})
set(${var} YES)
-3
View File
@@ -45,9 +45,6 @@
#ifdef MFEM_USE_STRUMPACK
#error Building with STRUMPACK (MFEM_USE_STRUMPACK=YES) requires MPI (MFEM_USE_MPI=YES)
#endif
#ifdef MFEM_USE_MKL_CPARDISO
#error Building with MKL CPARDISO (MFEM_USE_MKL_CPARDISO=YES) requires MPI (MFEM_USE_MPI=YES)
#endif
#ifdef MFEM_USE_PETSC
#error Building with PETSc (MFEM_USE_PETSC=YES) requires MPI (MFEM_USE_MPI=YES)
#endif
+3 -7
View File
@@ -93,7 +93,6 @@
// Enable MFEM functionality based on the SuperLU library.
// #define MFEM_USE_SUPERLU
// #define MFEM_USE_SUPERLU5
// Enable MFEM functionality based on the STRUMPACK library.
// #define MFEM_USE_STRUMPACK
@@ -101,9 +100,6 @@
// Enable MFEM features based on the Ginkgo library
// #define MFEM_USE_GINKGO
// Enable MFEM functionality based on the AmgX library.
// #define MFEM_USE_AMGX
// Enable secure socket streams based on the GNUTLS library
// #define MFEM_USE_GNUTLS
@@ -141,6 +137,9 @@
// Requires a CUDA compiler (nvcc).
// #define MFEM_USE_CUDA
// Enable MFEM functionality based on the AMGX library.
// #define MFEM_USE_AMGX
// Build the AMD GPU/HIP-enabled version of the MFEM library.
// Requires a HIP compiler (hipcc).
// #define MFEM_USE_HIP
@@ -167,7 +166,4 @@
// library.
// #define MFEM_USE_SIMMETRIX
// Enable interface to the MKL CPardiso library.
// #define MFEM_USE_MKL_CPARDISO
#endif // MFEM_CONFIG_HEADER
+1 -3
View File
@@ -32,10 +32,8 @@ MFEM_USE_SUNDIALS = @MFEM_USE_SUNDIALS@
MFEM_USE_MESQUITE = @MFEM_USE_MESQUITE@
MFEM_USE_SUITESPARSE = @MFEM_USE_SUITESPARSE@
MFEM_USE_SUPERLU = @MFEM_USE_SUPERLU@
MFEM_USE_SUPERLU5 = @MFEM_USE_SUPERLU5@
MFEM_USE_STRUMPACK = @MFEM_USE_STRUMPACK@
MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
MFEM_USE_AMGX = @MFEM_USE_AMGX@
MFEM_USE_GNUTLS = @MFEM_USE_GNUTLS@
MFEM_USE_NETCDF = @MFEM_USE_NETCDF@
MFEM_USE_PETSC = @MFEM_USE_PETSC@
@@ -47,6 +45,7 @@ MFEM_USE_PUMI = @MFEM_USE_PUMI@
MFEM_USE_HIOP = @MFEM_USE_HIOP@
MFEM_USE_GSLIB = @MFEM_USE_GSLIB@
MFEM_USE_CUDA = @MFEM_USE_CUDA@
MFEM_USE_AMGX = @MFEM_USE_AMGX@
MFEM_USE_HIP = @MFEM_USE_HIP@
MFEM_USE_RAJA = @MFEM_USE_RAJA@
MFEM_USE_OCCA = @MFEM_USE_OCCA@
@@ -54,7 +53,6 @@ MFEM_USE_CEED = @MFEM_USE_CEED@
MFEM_USE_UMPIRE = @MFEM_USE_UMPIRE@
MFEM_USE_SIMD = @MFEM_USE_SIMD@
MFEM_USE_ADIOS2 = @MFEM_USE_ADIOS2@
MFEM_USE_MKL_CPARDISO = @MFEM_USE_MKL_CPARDISO@
# Compiler, compile options, and link options
MFEM_CXX = @MFEM_CXX@
+8 -11
View File
@@ -33,10 +33,8 @@ option(MFEM_USE_SUNDIALS "Enable SUNDIALS usage" OFF)
option(MFEM_USE_MESQUITE "Enable MESQUITE usage" OFF)
option(MFEM_USE_SUITESPARSE "Enable SuiteSparse usage" OFF)
option(MFEM_USE_SUPERLU "Enable SuperLU_DIST usage" OFF)
option(MFEM_USE_SUPERLU5 "Use the old SuperLU_DIST 5.1 version" OFF)
option(MFEM_USE_STRUMPACK "Enable STRUMPACK usage" OFF)
option(MFEM_USE_GINKGO "Enable Ginkgo usage" OFF)
option(MFEM_USE_AMGX "Enable AmgX usage" OFF)
option(MFEM_USE_GNUTLS "Enable GNUTLS usage" OFF)
option(MFEM_USE_GSLIB "Enable GSLIB usage" OFF)
option(MFEM_USE_NETCDF "Enable NETCDF usage" OFF)
@@ -54,7 +52,6 @@ option(MFEM_USE_CEED "Enable CEED" OFF)
option(MFEM_USE_UMPIRE "Enable Umpire" OFF)
option(MFEM_USE_SIMD "Enable use of SIMD intrinsics" OFF)
option(MFEM_USE_ADIOS2 "Enable ADIOS2" OFF)
option(MFEM_USE_MKL_CPARDISO "Enable MKL CPardiso" OFF)
set(MFEM_MPI_NP 4 CACHE STRING "Number of processes used for MPI tests")
@@ -86,12 +83,17 @@ set(HYPRE_DIR "${MFEM_DIR}/../hypre/src/hypre" CACHE PATH
# If hypre was compiled to depend on BLAS and LAPACK:
# set(HYPRE_REQUIRED_PACKAGES "BLAS" "LAPACK" CACHE STRING
# "Packages that HYPRE depends on.")
if (MFEM_USE_CUDA)
# This is only necessary when hypre is built with cuda:
set(HYPRE_REQUIRED_LIBRARIES "-lcusparse" "-lcurand" CACHE STRING
"Libraries that HYPRE depends on.")
endif()
set(METIS_DIR "${MFEM_DIR}/../metis-4.0" CACHE PATH "Path to the METIS library.")
set(LIBUNWIND_DIR "" CACHE PATH "Path to Libunwind.")
# For sundials_nvecmpiplusx and nvecparallel remember to build with MPI_ENABLE=ON
# For sundials_nvecparhyp and nvecparallel remember to build with MPI_ENABLED=ON
# and modify cmake variables for hypre for sundials
set(SUNDIALS_DIR "${MFEM_DIR}/../sundials-5.0.0/instdir" CACHE PATH
"Path to the SUNDIALS library.")
@@ -112,7 +114,7 @@ set(ParMETIS_DIR "${MFEM_DIR}/../parmetis-4.0.3" CACHE PATH
set(ParMETIS_REQUIRED_PACKAGES "METIS" CACHE STRING
"Additional packages required by ParMETIS.")
set(SuperLUDist_DIR "${MFEM_DIR}/../SuperLU_DIST_6.3.1" CACHE PATH
set(SuperLUDist_DIR "${MFEM_DIR}/../SuperLU_DIST_5.1.0" CACHE PATH
"Path to the SuperLU_DIST library.")
# SuperLU_DIST may also depend on "OpenMP", depending on how it was compiled.
set(SuperLUDist_REQUIRED_PACKAGES "MPI" "BLAS" "ParMETIS" CACHE STRING
@@ -148,8 +150,6 @@ set(ScaLAPACK_TARGET_NAMES scalapack)
set(Ginkgo_DIR "${MFEM_DIR}/../ginkgo" CACHE PATH "Path to the Ginkgo library.")
set(AMGX_DIR "${MFEM_DIR}/../amgx" CACHE PATH "Path to AmgX")
set(GNUTLS_DIR "" CACHE PATH "Path to the GnuTLS library.")
set(GSLIB_DIR "" CACHE PATH "Path to the GSLIB library.")
@@ -185,12 +185,9 @@ set(HIOP_DIR "${MFEM_DIR}/../hiop/install" CACHE STRING
set(HIOP_REQUIRED_PACKAGES "BLAS" "LAPACK" CACHE STRING
"Packages that HiOp depends on.")
set(MKL_CPARDISO_DIR "" CACHE STRING "MKL installation path.")
set(MKL_MPI_WRAPPER_LIB "mkl_blacs_mpich_lp64" CACHE STRING "MKL MPI wrapper library")
set(MKL_LIBRARY_DIR "" CACHE STRING "Custom library subdirectory")
set(OCCA_DIR "${MFEM_DIR}/../occa" CACHE PATH "Path to OCCA")
set(RAJA_DIR "${MFEM_DIR}/../raja" CACHE PATH "Path to RAJA")
set(AMGX_DIR "${MFEM_DIR}/../amgx" CACHE PATH "Path to AMGX")
set(CEED_DIR "${MFEM_DIR}/../libCEED" CACHE PATH "Path to libCEED")
set(UMPIRE_DIR "${MFEM_DIR}/../umpire" CACHE PATH "Path to Umpire")
+18 -36
View File
@@ -120,10 +120,8 @@ MFEM_USE_SUNDIALS = NO
MFEM_USE_MESQUITE = NO
MFEM_USE_SUITESPARSE = NO
MFEM_USE_SUPERLU = NO
MFEM_USE_SUPERLU5 = NO
MFEM_USE_STRUMPACK = NO
MFEM_USE_GINKGO = NO
MFEM_USE_AMGX = NO
MFEM_USE_GNUTLS = NO
MFEM_USE_NETCDF = NO
MFEM_USE_PETSC = NO
@@ -135,6 +133,7 @@ MFEM_USE_PUMI = NO
MFEM_USE_HIOP = NO
MFEM_USE_GSLIB = NO
MFEM_USE_CUDA = NO
MFEM_USE_AMGX = NO
MFEM_USE_HIP = NO
MFEM_USE_RAJA = NO
MFEM_USE_OCCA = NO
@@ -142,7 +141,6 @@ MFEM_USE_CEED = NO
MFEM_USE_UMPIRE = NO
MFEM_USE_SIMD = NO
MFEM_USE_ADIOS2 = NO
MFEM_USE_MKL_CPARDISO = NO
# Compile and link options for zlib.
ZLIB_DIR =
@@ -157,6 +155,10 @@ LIBUNWIND_LIB = $(if $(NOTMAC),-lunwind -ldl,)
HYPRE_DIR = @MFEM_DIR@/../hypre/src/hypre
HYPRE_OPT = -I$(HYPRE_DIR)/include
HYPRE_LIB = -L$(HYPRE_DIR)/lib -lHYPRE
ifeq (YES,$(MFEM_USE_CUDA))
# This is only necessary when hypre is built with cuda:
HYPRE_LIB += -lcusparse -lcurand
endif
# METIS library configuration
ifeq ($(MFEM_USE_SUPERLU)$(MFEM_USE_STRUMPACK),NONO)
@@ -192,19 +194,15 @@ OPENMP_LIB =
POSIX_CLOCKS_LIB = -lrt
# SUNDIALS library configuration
# For sundials_nvecmpiplusx and nvecparallel remember to build with MPI_ENABLE=ON
# For sundials_nvecparhyp and nvecparallel remember to build with MPI_ENABLED=ON
# and modify cmake variables for hypre for sundials
SUNDIALS_DIR = @MFEM_DIR@/../sundials-5.0.0/instdir
SUNDIALS_OPT = -I$(SUNDIALS_DIR)/include
SUNDIALS_LIBDIR = $(wildcard $(SUNDIALS_DIR)/lib*)
SUNDIALS_LIB = $(XLINKER)-rpath,$(SUNDIALS_LIBDIR) -L$(SUNDIALS_LIBDIR)\
SUNDIALS_DIR = @MFEM_DIR@/../sundials-5.0.0/instdir
SUNDIALS_OPT = -I$(SUNDIALS_DIR)/include
SUNDIALS_LIB = -Wl,-rpath,$(SUNDIALS_DIR)/lib64 -L$(SUNDIALS_DIR)/lib64\
-lsundials_arkode -lsundials_cvodes -lsundials_nvecserial -lsundials_kinsol
ifeq ($(MFEM_USE_MPI),YES)
SUNDIALS_LIB += -lsundials_nvecparallel -lsundials_nvecmpiplusx
endif
ifeq ($(MFEM_USE_CUDA),YES)
SUNDIALS_LIB += -lsundials_nveccuda
SUNDIALS_LIB += -lsundials_nvecparhyp -lsundials_nvecparallel
endif
# If SUNDIALS was built with KLU:
# MFEM_USE_SUITESPARSE = YES
@@ -223,15 +221,9 @@ SUITESPARSE_LIB = -Wl,-rpath,$(SUITESPARSE_DIR)/lib -L$(SUITESPARSE_DIR)/lib\
-lsuitesparseconfig $(LIB_RT) $(METIS_LIB) $(LAPACK_LIB)
# SuperLU library configuration
ifeq ($(MFEM_USE_SUPERLU5),YES)
SUPERLU_DIR = @MFEM_DIR@/../SuperLU_DIST_5.1.0
SUPERLU_OPT = -I$(SUPERLU_DIR)/include
SUPERLU_LIB = -Wl,-rpath,$(SUPERLU_DIR)/lib -L$(SUPERLU_DIR)/lib -lsuperlu_dist_5.1.0
else
SUPERLU_DIR = @MFEM_DIR@/../SuperLU_DIST_6.3.1
SUPERLU_OPT = -I$(SUPERLU_DIR)/include
SUPERLU_LIB = -Wl,-rpath,$(SUPERLU_DIR)/lib64 -L$(SUPERLU_DIR)/lib64 -lsuperlu_dist -lblas
endif
SUPERLU_DIR = @MFEM_DIR@/../SuperLU_DIST_5.1.0
SUPERLU_OPT = -I$(SUPERLU_DIR)/SRC
SUPERLU_LIB = -Wl,-rpath,$(SUPERLU_DIR)/lib -L$(SUPERLU_DIR)/lib -lsuperlu_dist_5.1.0
# SCOTCH library configuration (required by STRUMPACK <= v2.1.0, optional in
# STRUMPACK >= v2.2.0)
@@ -264,13 +256,7 @@ STRUMPACK_LIB = -L$(STRUMPACK_DIR)/lib -lstrumpack $(MPI_FORTRAN_LIB)\
# Ginkgo library configuration (currently not needed)
GINKGO_DIR = @MFEM_DIR@/../ginkgo/install
GINKGO_OPT = -isystem $(GINKGO_DIR)/include
GINKGO_LIB = $(XLINKER)-rpath,$(GINKGO_DIR)/lib -L$(GINKGO_DIR)/lib -lginkgo\
-lginkgo_omp -lginkgo_cuda -lginkgo_reference
# AmgX library configuration
AMGX_DIR = @MFEM_DIR@/../amgx
AMGX_OPT = -I$(AMGX_DIR)/include
AMGX_LIB = -lcusparse -lcusolver -lcublas -lnvToolsExt -L$(AMGX_DIR)/lib -lamgx
GINKGO_LIB = $(XLINKER)-rpath,$(GINKGO_DIR)/lib -L$(GINKGO_DIR)/lib -lginkgo -lginkgo_omp -lginkgo_cuda -lginkgo_reference
# GnuTLS library configuration
GNUTLS_OPT =
@@ -391,14 +377,10 @@ UMPIRE_DIR = @MFEM_DIR@/../umpire
UMPIRE_OPT = -I$(UMPIRE_DIR)/include
UMPIRE_LIB = -L$(UMPIRE_DIR)/lib -lumpire
# MKL CPardiso library configuration
MKL_CPARDISO_DIR ?=
MKL_MPI_WRAPPER ?= mkl_blacs_mpich_lp64
MKL_LIBRARY_SUBDIR ?= lib
MKL_CPARDISO_OPT = -I$(MKL_CPARDISO_DIR)/include
MKL_CPARDISO_LIB = -Wl,-rpath,$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR)\
-L$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR) -l$(MKL_MPI_WRAPPER)\
-lmkl_intel_lp64 -lmkl_sequential -lmkl_core
# AMGX library configuration
AMGX_DIR = @MFEM_DIR@/../amgx
AMGX_OPT = -I$(AMGX_DIR)/include
AMGX_LIB = -L$(CUDA_HOME)/lib64 -lcusparse -lcusolver -lcublas -lnvToolsExt -L$(AMGX_DIR)/lib -lamgx -Xlinker=-rpath=$(CUDA_HOME)/lib64
# If YES, enable some informational messages
VERBOSE = NO
+1 -5
View File
@@ -127,11 +127,6 @@ if (MFEM_USE_GINKGO)
add_subdirectory(ginkgo)
endif()
# Include the examples/amgx directory if AmgX is enabled
if (MFEM_USE_AMGX)
add_subdirectory(amgx)
endif()
# Include the examples/petsc directory if PETSc is enabled.
if (MFEM_USE_PETSC)
add_subdirectory(petsc)
@@ -145,3 +140,4 @@ endif()
if (MFEM_USE_HIOP)
add_subdirectory(hiop)
endif()
-79
View File
@@ -1,79 +0,0 @@
# Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
set(AMGX_EXAMPLES_SRCS)
list(APPEND AMGX_EXAMPLES_SRCS
ex1.cpp
)
if (MFEM_USE_MPI)
list(APPEND AMGX_EXAMPLES_SRCS
ex1p.cpp
)
endif()
set(AMGX_JSON_FILES amg_pcg.json multi_gs.json precon.json)
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
include_directories(BEFORE ${PROJECT_BINARY_DIR})
# Add targets to copy *.json files from the source directory
foreach(JSON_FILE ${AMGX_JSON_FILES})
add_custom_command(OUTPUT ${JSON_FILE}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/${JSON_FILE} ${JSON_FILE}
COMMENT "copy ${JSON_FILE}")
endforeach()
add_custom_target(copy_amgx_json_files DEPENDS ${AMGX_JSON_FILES}
COMMENT "Copying AMGX example json files ...")
# Add "test_amgx" target, see below.
add_custom_target(test_amgx
${CMAKE_CTEST_COMMAND} -R amgx USES_TERMINAL)
# Add one executable per cpp file, adding "amgx_" as prefix. Sets
# "copy_amgx_json_files" as a prerequisite for the given examples. Also, sets
# "test_amgx" as a target that depends on the given examples.
set(PFX amgx_)
add_mfem_examples(AMGX_EXAMPLES_SRCS ${PFX} copy_amgx_json_files test_amgx)
# Testing.
# The AMGX tests can be run separately using the target "test_amgx"
# which builds the examples and runs:
# ctest -R amgx
# Command line options for the tests.
# Example 1/1p:
set(EX1_TEST_OPTS)
set(EX1P_TEST_OPTS)
# Add the tests: one test per source file.
foreach(SRC_FILE ${AMGX_EXAMPLES_SRCS})
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
set(TEST_NAME ${PFX}${TEST_NAME})
set(THIS_TEST_OPTIONS "-no-vis")
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
if (NOT (${TEST_NAME} MATCHES ".*p$"))
add_test(NAME ${TEST_NAME}_ser
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
else()
add_test(NAME ${TEST_NAME}_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
${MPIEXEC_POSTFLAGS})
endif()
endforeach()
-18
View File
@@ -1,18 +0,0 @@
Finite Element Discretization Library
__
_ __ ___ / _| ___ _ __ ___
| '_ ` _ \ | |_ / _ \| '_ ` _ \
| | | | | || _|| __/| | | | | |
|_| |_| |_||_| \___||_| |_| |_|
https://mfem.org
This directory contains modifications of the example codes that illustrate the
use of MFEM features based on NVIDIA's multigrid library AmgX.
To build these examples, make sure that MFEM is configured with the option
"MFEM_USE_AMGX = YES", see the top-level INSTALL file for details (version
2.1 of AmgX is recommended, though older versions may work too.
We recommend comparing the original example codes with the corresponding files
in the current directory.
+1 -1
View File
@@ -17,7 +17,7 @@
"interpolator": "D2",
"max_row_sum" : 0.9,
"strength_threshold" : 0.25,
"max_iters": 2,
"max_iters": 1,
"monitor_residual": 0,
"store_res_history": 0,
"scope": "amg",
+61 -45
View File
@@ -1,15 +1,50 @@
// MFEM Example 1
// AmgX Modification
//
// Compile with: make ex1
//
// Sample runs: ex1 -m ../../data/square-disc.mesh
// ex1 -m ../../data/star.mesh
// ex1 -m ../../data/star-mixed.mesh
// ex1 -m ../../data/escher.mesh
// ex1 -m ../../data/fichera.mesh
// ex1 -m ../../data/fichera-mixed.mesh
// ex1 -m ../../data/toroid-wedge.mesh
// ex1 -m ../../data/periodic-annulus-sector.msh
// ex1 -m ../../data/periodic-torus-sector.msh
// ex1 -m ../../data/square-disc-p2.vtk -o 2
// ex1 -m ../../data/square-disc-p3.mesh -o 3
// ex1 -m ../../data/square-disc-nurbs.mesh -o -1
// ex1 -m ../../data/star-mixed-p2.mesh -o 2
// ex1 -m ../../data/disc-nurbs.mesh -o -1
// ex1 -m ../../data/pipe-nurbs.mesh -o -1
// ex1 -m ../../data/fichera-mixed-p2.mesh -o 2
// ex1 -m ../../data/star-surf.mesh
// ex1 -m ../../data/square-disc-surf.mesh
// ex1 -m ../../data/inline-segment.mesh
// ex1 -m ../../data/amr-quad.mesh
// ex1 -m ../../data/amr-hex.mesh
// ex1 -m ../../data/fichera-amr.mesh
// ex1 -m ../../data/mobius-strip.mesh
// ex1 -m ../../data/mobius-strip.mesh -o -1 -sc
//
// Device sample runs:
// ex1 -pa -d cuda
// ex1 -pa -d raja-cuda
// ex1 -pa -d occa-cuda
// ex1 -pa -d raja-omp
// ex1 -pa -d occa-omp
// ex1 -pa -d ceed-cpu
// * ex1 -pa -d ceed-cuda
// ex1 -pa -d ceed-cuda:/gpu/cuda/shared
// ex1 -m ../../data/beam-hex.mesh -pa -d cuda
// ex1 -m ../../data/beam-tet.mesh -pa -d ceed-cpu
// ex1 -m ../../data/beam-tet.mesh -pa -d ceed-cuda:/gpu/cuda/ref
//
// AmgX sample runs:
// ex1
// ex1 -d cuda
// ex1 --amgx-file multi_gs.json --amgx-solver
// ex1 --amgx-file precon.json --amgx-preconditioner
// ex1 --amgx-file multi_gs.json --amgx-solver -d cuda
// ex1 --amgx-file precon.json --amgx-preconditioner -d cuda
// ./ex1 --amgx-file multi_gs.json --amgx-solver
// ./ex1 --amgx-file precon.json --amgx-preconditioner
// ./ex1 --amgx-file multi_gs.json --amgx-solver -d cuda
// ./ex1 --amgx-file precon.json --amgx-preconditioner -d cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
@@ -42,9 +77,8 @@ int main(int argc, char *argv[])
bool pa = false;
const char *device_config = "cpu";
bool visualization = true;
bool amgx_lib = true;
bool amgx_solver = true;
const char* amgx_json_file = ""; // JSON file for AmgX
const char* amgx_json_file = ""; // jason file for amgx
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -56,12 +90,11 @@ int main(int argc, char *argv[])
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&amgx_lib, "-amgx", "--amgx-lib", "-no-amgx",
"--no-amgx-lib", "Use AmgX in example.");
args.AddOption(&amgx_json_file, "--amgx-file", "--amgx-file",
"AMGX solver config file (overrides --amgx-solver, --amgx-verbose)");
args.AddOption(&amgx_solver, "--amgx-solver", "--amgx-solver",
"--amgx-preconditioner", "--amgx-preconditioner",
"--amgx-preconditioner",
"--amgx-preconditioner",
"Configure AMGX as solver or preconditioner.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
@@ -76,6 +109,9 @@ int main(int argc, char *argv[])
}
args.PrintOptions(cout);
MFEM_VERIFY(!pa && strcmp(amgx_json_file,"") != 0,
"An AmgX json file is needed for this example \n");
// 2. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
@@ -172,29 +208,10 @@ int main(int argc, char *argv[])
cout << "Size of linear system: " << A->Height() << endl;
// 11. Solve the linear system A X = B.
if (pa)
{
// Jacobi preconditioning in partial assembly mode
if (UsesTensorBasis(fespace))
{
OperatorJacobiSmoother M(a, ess_tdof_list);
PCG(*A, M, B, X, 1, 400, 1e-12, 0.0);
}
else
{
CG(*A, B, X, 1, 400, 1e-12, 0.0);
}
}
else if (amgx_lib && strcmp(amgx_json_file,"") == 0)
{
bool amgx_verbose = false;
AmgXSolver amgx(AmgXSolver::PRECONDITIONER, amgx_verbose);
amgx.SetOperator(*A.As<SparseMatrix>());
PCG(*A, amgx, B, X, 1, 200, 1e-12, 0.0);
}
else if (amgx_lib && strcmp(amgx_json_file,"") != 0)
if (!pa)
{
AmgXSolver amgx;
amgx.ReadParameters(amgx_json_file, AmgXSolver::EXTERNAL);
amgx.InitSerial();
amgx.SetOperator(*A.As<SparseMatrix>());
@@ -207,20 +224,19 @@ int main(int argc, char *argv[])
{
PCG(*A.As<SparseMatrix>(), amgx, B, X, 3, 40, 1e-12, 0.0);
}
}
else
else // Jacobi preconditioning in partial assembly mode
{
#ifndef MFEM_USE_SUITESPARSE
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
GSSmoother M((SparseMatrix&)(*A));
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
#else
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(*A);
umf_solver.Mult(B, X);
#endif
if (UsesTensorBasis(fespace))
{
OperatorJacobiSmoother M(a, ess_tdof_list);
PCG(*A, M, B, X, 1, 400, 1e-12, 0.0);
}
else
{
CG(*A, B, X, 1, 400, 1e-12, 0.0);
}
}
// 12. Recover the solution as a finite element grid function.
+69 -68
View File
@@ -1,13 +1,45 @@
// MFEM Example 1 - Parallel Version
// AmgX Modification
//
// Compile with: make ex1p
//
// Sample runs: mpirun -np 4 ex1p -m ../../data/square-disc.mesh
// mpirun -np 4 ex1p -m ../../data/star.mesh
// mpirun -np 4 ex1p -m ../../data/star-mixed.mesh
// mpirun -np 4 ex1p -m ../../data/escher.mesh
// mpirun -np 4 ex1p -m ../../data/fichera.mesh
// mpirun -np 4 ex1p -m ../../data/fichera-mixed.mesh
// mpirun -np 4 ex1p -m ../../data/toroid-wedge.mesh
// mpirun -np 4 ex1p -m ../../data/periodic-annulus-sector.msh
// mpirun -np 4 ex1p -m ../../data/periodic-torus-sector.msh
// mpirun -np 4 ex1p -m ../../data/square-disc-p2.vtk -o 2
// mpirun -np 4 ex1p -m ../../data/square-disc-p3.mesh -o 3
// mpirun -np 4 ex1p -m ../../data/square-disc-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../../data/star-mixed-p2.mesh -o 2
// mpirun -np 4 ex1p -m ../../data/disc-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../../data/pipe-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../../data/ball-nurbs.mesh -o 2
// mpirun -np 4 ex1p -m ../../data/fichera-mixed-p2.mesh -o 2
// mpirun -np 4 ex1p -m ../../data/star-surf.mesh
// mpirun -np 4 ex1p -m ../../data/square-disc-surf.mesh
// mpirun -np 4 ex1p -m ../../data/inline-segment.mesh
// mpirun -np 4 ex1p -m ../../data/amr-quad.mesh
// mpirun -np 4 ex1p -m ../../data/amr-hex.mesh
// mpirun -np 4 ex1p -m ../../data/mobius-strip.mesh
// mpirun -np 4 ex1p -m ../../data/mobius-strip.mesh -o -1 -sc
//
// Device sample runs:
// mpirun -np 4 ex1p -pa -d cuda
// mpirun -np 4 ex1p -pa -d occa-cuda
// mpirun -np 4 ex1p -pa -d raja-omp
// mpirun -np 4 ex1p -pa -d ceed-cpu
// * mpirun -np 4 ex1p -pa -d ceed-cuda
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared
// mpirun -np 4 ex1p -m ../../data/beam-tet.mesh -pa -d ceed-cpu
//
// AmgX sample runs:
// mpirun -np 4 ex1p
// mpirun -np 4 ex1p -d cuda
// mpirun -np 10 ex1p --amgx-file amg_pcg.json --amgx-mpi-teams
// mpirun -np 4 ex1p --amgx-file amg_pcg.json
//
// mpirun -n 40 ./ex1p --amgx-file amg_pcg.json
// lrun -n 4 ./ex1p --amgx-file amg_pcg.json --amgx-mpi-gpu-exclusive
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
@@ -46,9 +78,8 @@ int main(int argc, char *argv[])
bool pa = false;
const char *device_config = "cpu";
bool visualization = true;
bool amgx_lib = true;
bool amgx_mpi_teams = false;
const char* amgx_json_file = ""; // JSON file for AmgX
bool amgx_mpi_teams = true;
const char* amgx_json_file = ""; // jason file for amgx
int ndevices = 1;
OptionsParser args(argc, argv);
@@ -61,20 +92,17 @@ int main(int argc, char *argv[])
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&amgx_lib, "-amgx", "--amgx-lib", "-no-amgx",
"--no-amgx-lib", "Use AmgX in example.");
args.AddOption(&amgx_json_file, "--amgx-file", "--amgx-file",
"AMGX solver config file (overrides --amgx-solver, --amgx-verbose)");
args.AddOption(&amgx_mpi_teams, "--amgx-mpi-teams", "--amgx-mpi-teams",
"--amgx-mpi-gpu-exclusive", "--amgx-mpi-gpu-exclusive",
"Create MPI teams when using AmgX to load balance between ranks and GPUs.");
"Create MPI teams when using AMGX.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&ndevices, "-nd","--gpus-per-node-in-teams-mode",
"Number of GPU devices per node (Only used if amgx_mpi_teams is true).");
args.AddOption(&ndevices, "-nd","--nd","Number of GPU devices.");
args.Parse();
if (!args.Good())
@@ -89,6 +117,9 @@ int main(int argc, char *argv[])
if (myid == 0)
{
args.PrintOptions(cout);
MFEM_VERIFY(!pa == true && strcmp(amgx_json_file,"") != 0,
"An AmgX json file is needed for this example \n");
}
// 3. Enable hardware devices such as GPUs, and programming models such as
@@ -205,11 +236,34 @@ int main(int argc, char *argv[])
// 13. Solve the linear system A X = B.
// * With full assembly, use the BoomerAMG preconditioner from hypre.
// * If AmgX is available solve using amg preconditioner.
// * With partial assembly, use Jacobi smoothing, for now.
Solver *prec = NULL;
if (pa)
if (!pa)
{
AmgXSolver amgx;
amgx.ReadParameters(amgx_json_file, AmgXSolver::EXTERNAL);
if (amgx_mpi_teams)
{
//Forms MPI teams to load balance between mpi ranks and gpus
amgx.InitMPITeams(MPI_COMM_WORLD, ndevices);
}
else
{
//Assumes MPI == number of devices
amgx.InitExclusiveGPU(MPI_COMM_WORLD);
}
amgx.SetOperator(*A.As<HypreParMatrix>());
amgx.Mult(B, X);
}
else
{
if (UsesTensorBasis(fespace))
{
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
@@ -223,60 +277,7 @@ int main(int argc, char *argv[])
cg.SetOperator(*A);
cg.Mult(B, X);
delete prec;
}
else if (amgx_lib && strcmp(amgx_json_file,"") == 0)
{
MFEM_VERIFY(!amgx_mpi_teams,
"Please add JSON file to try AmgX with MPI teams mode");
bool amgx_verbose = false;
prec = new AmgXSolver(MPI_COMM_WORLD, AmgXSolver::PRECONDITIONER,
amgx_verbose);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(1);
if (prec) { cg.SetPreconditioner(*prec); }
cg.SetOperator(*A);
cg.Mult(B, X);
delete prec;
}
else if (amgx_lib && strcmp(amgx_json_file,"") != 0)
{
AmgXSolver amgx;
amgx.ReadParameters(amgx_json_file, AmgXSolver::EXTERNAL);
if (amgx_mpi_teams)
{
// Forms MPI teams to load balance between MPI ranks and GPUs
amgx.InitMPITeams(MPI_COMM_WORLD, ndevices);
}
else
{
// Assumes each MPI rank is paired with a GPU
amgx.InitExclusiveGPU(MPI_COMM_WORLD);
}
amgx.SetOperator(*A.As<HypreParMatrix>());
amgx.Mult(B, X);
// Release MPI communicators and resources created by AmgX
amgx.Finalize();
}
else
{
prec = new HypreBoomerAMG;
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(1);
if (prec) { cg.SetPreconditioner(*prec); }
cg.SetOperator(*A);
cg.Mult(B, X);
delete prec;
}
// 14. Recover the parallel grid function corresponding to X. This is the
-3
View File
@@ -70,6 +70,3 @@ clean: clean-build
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -f .logamgx refined.mesh sol.gf mesh.* sol.*
+1211
View File
File diff suppressed because it is too large Load Diff
+14321
View File
File diff suppressed because it is too large Load Diff
+3286
View File
File diff suppressed because it is too large Load Diff
+28 -14
View File
@@ -35,7 +35,6 @@
// ex1 -pa -d occa-omp
// ex1 -pa -d ceed-cpu
// * ex1 -pa -d ceed-cuda
// * ex1 -pa -d ceed-hip
// ex1 -pa -d ceed-cuda:/gpu/cuda/shared
// ex1 -m ../data/beam-hex.mesh -pa -d cuda
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cpu
@@ -72,6 +71,7 @@ int main(int argc, char *argv[])
bool pa = false;
const char *device_config = "cpu";
bool visualization = true;
bool amgx = false;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -83,6 +83,8 @@ int main(int argc, char *argv[])
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&amgx, "-amgx", "--amgx-precon", "-no-amgx",
"--no-amgx-precon", "Use AmgX V-cycle as preconditioner for CG.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
@@ -192,7 +194,31 @@ int main(int argc, char *argv[])
cout << "Size of linear system: " << A->Height() << endl;
// 11. Solve the linear system A X = B.
if (!pa)
if (pa)
{
// Jacobi preconditioning in partial assembly mode
if (UsesTensorBasis(fespace))
{
OperatorJacobiSmoother M(a, ess_tdof_list);
PCG(*A, M, B, X, 1, 400, 1e-12, 0.0);
}
else
{
CG(*A, B, X, 1, 400, 1e-12, 0.0);
}
}
else if (amgx)
{
#if defined(MFEM_USE_AMGX)
bool amgx_verbose = false;
AmgXSolver amgx(AmgXSolver::PRECONDITIONER, amgx_verbose);
amgx.SetOperator(*A.As<SparseMatrix>());
PCG(*A, amgx, B, X, 1, 200, 1e-12, 0.0);
#else
mfem_error("MFEM not configured with AMGX \n");
#endif
}
else
{
#ifndef MFEM_USE_SUITESPARSE
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
@@ -206,18 +232,6 @@ int main(int argc, char *argv[])
umf_solver.Mult(B, X);
#endif
}
else // Jacobi preconditioning in partial assembly mode
{
if (UsesTensorBasis(fespace))
{
OperatorJacobiSmoother M(a, ess_tdof_list);
PCG(*A, M, B, X, 1, 400, 1e-12, 0.0);
}
else
{
CG(*A, B, X, 1, 400, 1e-12, 0.0);
}
}
// 12. Recover the solution as a finite element grid function.
a.RecoverFEMSolution(X, b, x);
+2 -16
View File
@@ -72,7 +72,6 @@ int main(int argc, char *argv[])
int seed = 75;
bool slu_solver = false;
bool sp_solver = false;
bool cpardiso_solver = false;
bool visualization = 1;
OptionsParser args(argc, argv);
@@ -96,10 +95,6 @@ int main(int argc, char *argv[])
#ifdef MFEM_USE_STRUMPACK
args.AddOption(&sp_solver, "-sp", "--strumpack", "-no-sp",
"--no-strumpack", "Use the STRUMPACK Solver.");
#endif
#ifdef MFEM_USE_MKL_CPARDISO
args.AddOption(&cpardiso_solver, "-cpardiso", "--cpardiso", "-no-cpardiso",
"--no-cpardiso", "Use the MKL CPardiso Solver.");
#endif
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
@@ -241,7 +236,7 @@ int main(int argc, char *argv[])
// preconditioner for A to be used within the solver. Set the matrices
// which define the generalized eigenproblem A x = lambda M x.
Solver * precond = NULL;
if (!slu_solver && !sp_solver && !cpardiso_solver)
if (!slu_solver && !sp_solver)
{
HypreBoomerAMG * amg = new HypreBoomerAMG(*A);
amg->SetPrintLevel(0);
@@ -273,19 +268,10 @@ int main(int argc, char *argv[])
strumpack->SetFromCommandLine();
precond = strumpack;
}
#endif
#ifdef MFEM_USE_MKL_CPARDISO
if (cpardiso_solver)
{
auto cpardiso = new CPardisoSolver(A->GetComm());
cpardiso->SetMatrixType(CPardisoSolver::MatType::REAL_STRUCTURE_SYMMETRIC);
cpardiso->SetPrintLevel(1);
cpardiso->SetOperator(*A);
precond = cpardiso;
}
#endif
}
HypreLOBPCG * lobpcg = new HypreLOBPCG(MPI_COMM_WORLD);
lobpcg->SetNumModes(nev);
lobpcg->SetRandomSeed(seed);
+27 -37
View File
@@ -8,7 +8,6 @@
// ex19 -m ../data/beam-hex.mesh
// ex19 -m ../data/beam-tet.mesh
// ex19 -m ../data/beam-wedge.mesh
// ex19 -m ../data/beam-quad-amr.mesh
//
// Description: This examples solves a quasi-static incompressible nonlinear
// elasticity problem of the form 0 = H(x), where H is an
@@ -97,7 +96,7 @@ protected:
Array<FiniteElementSpace *> spaces;
// Offsets for extracting block vector segments
Array<int> &block_trueOffsets;
Array<int> &block_offsets;
// Jacobian for block access
BlockOperator *jacobian;
@@ -153,7 +152,7 @@ protected:
Coefficient &mu;
// Block offsets for variable access
Array<int> &block_trueOffsets;
Array<int> &block_offsets;
public:
RubberOperator(Array<FiniteElementSpace *> &fes, Array<Array<int> *>&ess_bdr,
@@ -247,8 +246,8 @@ int main(int argc, char *argv[])
spaces[0] = &R_space;
spaces[1] = &W_space;
int R_size = R_space.GetTrueVSize();
int W_size = W_space.GetTrueVSize();
int R_size = R_space.GetVSize();
int W_size = W_space.GetVSize();
// 6. Define the Dirichlet conditions (set to boundary attribute 1 and 2)
Array<Array<int> *> ess_bdr(2);
@@ -272,13 +271,13 @@ int main(int argc, char *argv[])
std::cout << "***********************************************************\n";
// 8. Define the block structure of the solution vector (u then p)
Array<int> block_trueOffsets(3);
block_trueOffsets[0] = 0;
block_trueOffsets[1] = R_space.GetTrueVSize();
block_trueOffsets[2] = W_space.GetTrueVSize();
block_trueOffsets.PartialSum();
Array<int> block_offsets(3);
block_offsets[0] = 0;
block_offsets[1] = R_space.GetVSize();
block_offsets[2] = W_space.GetVSize();
block_offsets.PartialSum();
BlockVector xp(block_trueOffsets);
BlockVector xp(block_offsets);
// 9. Define grid functions for the current configuration, reference
// configuration, final deformation, and pressure
@@ -287,8 +286,8 @@ int main(int argc, char *argv[])
GridFunction x_def(&R_space);
GridFunction p_gf(&W_space);
x_gf.MakeTRef(&R_space, xp.GetBlock(0), 0);
p_gf.MakeTRef(&W_space, xp.GetBlock(1), 0);
x_gf.MakeRef(&R_space, xp.GetBlock(0), 0);
p_gf.MakeRef(&W_space, xp.GetBlock(1), 0);
VectorFunctionCoefficient deform(dim, InitialDeformation);
VectorFunctionCoefficient refconfig(dim, ReferenceConfiguration);
@@ -297,19 +296,14 @@ int main(int argc, char *argv[])
x_ref.ProjectCoefficient(refconfig);
p_gf = 0.0;
x_gf.SetTrueVector();
p_gf.SetTrueVector();
// 10. Initialize the incompressible neo-Hookean operator
RubberOperator oper(spaces, ess_bdr, block_trueOffsets,
RubberOperator oper(spaces, ess_bdr, block_offsets,
newton_rel_tol, newton_abs_tol, newton_iter, c_mu);
// 11. Solve the Newton system
oper.Solve(xp);
// 12. Compute the final deformation
x_gf.SetFromTrueVector();
p_gf.SetFromTrueVector();
subtract(x_gf, x_ref, x_def);
// 13. Visualize the results if requested
@@ -355,7 +349,7 @@ int main(int argc, char *argv[])
JacobianPreconditioner::JacobianPreconditioner(Array<FiniteElementSpace *> &fes,
SparseMatrix &mass,
Array<int> &offsets)
: Solver(offsets[2]), block_trueOffsets(offsets), pressure_mass(&mass)
: Solver(offsets[2]), block_offsets(offsets), pressure_mass(&mass)
{
fes.Copy(spaces);
@@ -387,18 +381,18 @@ JacobianPreconditioner::JacobianPreconditioner(Array<FiniteElementSpace *> &fes,
void JacobianPreconditioner::Mult(const Vector &k, Vector &y) const
{
// Extract the blocks from the input and output vectors
Vector disp_in(k.GetData() + block_trueOffsets[0],
block_trueOffsets[1]-block_trueOffsets[0]);
Vector pres_in(k.GetData() + block_trueOffsets[1],
block_trueOffsets[2]-block_trueOffsets[1]);
Vector disp_in(k.GetData() + block_offsets[0],
block_offsets[1]-block_offsets[0]);
Vector pres_in(k.GetData() + block_offsets[1],
block_offsets[2]-block_offsets[1]);
Vector disp_out(y.GetData() + block_trueOffsets[0],
block_trueOffsets[1]-block_trueOffsets[0]);
Vector pres_out(y.GetData() + block_trueOffsets[1],
block_trueOffsets[2]-block_trueOffsets[1]);
Vector disp_out(y.GetData() + block_offsets[0],
block_offsets[1]-block_offsets[0]);
Vector pres_out(y.GetData() + block_offsets[1],
block_offsets[2]-block_offsets[1]);
Vector temp(block_trueOffsets[1]-block_trueOffsets[0]);
Vector temp2(block_trueOffsets[1]-block_trueOffsets[0]);
Vector temp(block_offsets[1]-block_offsets[0]);
Vector temp2(block_offsets[1]-block_offsets[0]);
// Perform the block elimination for the preconditioner
mass_pcg->Mult(pres_in, pres_out);
@@ -453,9 +447,9 @@ RubberOperator::RubberOperator(Array<FiniteElementSpace *> &fes,
double abs_tol,
int iter,
Coefficient &c_mu)
: Operator(fes[0]->GetTrueVSize() + fes[1]->GetTrueVSize()),
: Operator(fes[0]->GetVSize() + fes[1]->GetVSize()),
newton_solver(), newton_monitor("Newton", 1),
j_monitor(" GMRES", 3), mu(c_mu), block_trueOffsets(offsets)
j_monitor(" GMRES", 3), mu(c_mu), block_offsets(offsets)
{
Array<Vector *> rhs(2);
rhs = NULL; // Set all entries in the array
@@ -477,16 +471,12 @@ RubberOperator::RubberOperator(Array<FiniteElementSpace *> &fes,
a->AddDomainIntegrator(new MassIntegrator(one));
a->Assemble();
a->Finalize();
OperatorPtr op;
Array<int> p_ess_tdofs;
a->FormSystemMatrix(p_ess_tdofs, op);
pressure_mass = a->LoseMat();
delete a;
// Initialize the Jacobian preconditioner
JacobianPreconditioner *jac_prec =
new JacobianPreconditioner(fes, *pressure_mass, block_trueOffsets);
new JacobianPreconditioner(fes, *pressure_mass, block_offsets);
j_prec = jac_prec;
// Set up the Jacobian solver
+8 -8
View File
@@ -8,7 +8,6 @@
// mpirun -np 2 ex19p -m ../data/beam-hex.mesh
// mpirun -np 2 ex19p -m ../data/beam-tet.mesh
// mpirun -np 2 ex19p -m ../data/beam-wedge.mesh
// mpirun -np 2 ex19p -m ../data/beam-quad-amr.mesh
//
// Description: This examples solves a quasi-static incompressible nonlinear
// elasticity problem of the form 0 = H(x), where H is an
@@ -197,8 +196,10 @@ void InitialDeformation(const Vector &x, Vector &y);
int main(int argc, char *argv[])
{
// 1. Initialize MPI
MPI_Session mpi;
const int myid = mpi.WorldRank();
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options
const char *mesh_file = "../data/beam-tet.mesh";
@@ -238,6 +239,7 @@ int main(int argc, char *argv[])
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
@@ -397,6 +399,8 @@ int main(int argc, char *argv[])
// 19. Free the used memory
delete pmesh;
MPI_Finalize();
return 0;
}
@@ -470,11 +474,7 @@ void JacobianPreconditioner::SetOperator(const Operator &op)
{
HypreBoomerAMG *stiff_prec_amg = new HypreBoomerAMG();
stiff_prec_amg->SetPrintLevel(0);
if (!spaces[0]->GetParMesh()->Nonconforming())
{
stiff_prec_amg->SetElasticityOptions(spaces[0]);
}
stiff_prec_amg->SetElasticityOptions(spaces[0]);
stiff_prec = stiff_prec_amg;
+14 -1
View File
@@ -33,7 +33,6 @@
// mpirun -np 4 ex1p -pa -d raja-omp
// mpirun -np 4 ex1p -pa -d ceed-cpu
// * mpirun -np 4 ex1p -pa -d ceed-cuda
// * mpirun -np 4 ex1p -pa -d ceed-hip
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
//
@@ -74,6 +73,7 @@ int main(int argc, char *argv[])
bool pa = false;
const char *device_config = "cpu";
bool visualization = true;
bool amgx = false;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -85,6 +85,8 @@ int main(int argc, char *argv[])
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&amgx, "-amgx", "--amgx-precon", "-no-amgx",
"--no-amgx-precon", "Use AmgX V-cycle as preconditioner for CG.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
@@ -219,6 +221,7 @@ int main(int argc, char *argv[])
// 13. Solve the linear system A X = B.
// * With full assembly, use the BoomerAMG preconditioner from hypre.
// * If AmgX is available solve using amg preconditioner.
// * With partial assembly, use Jacobi smoothing, for now.
Solver *prec = NULL;
if (pa)
@@ -228,6 +231,16 @@ int main(int argc, char *argv[])
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
}
}
else if (amgx)
{
#if defined(MFEM_USE_AMGX)
bool amgx_verbose = false;
prec = new AmgXSolver(MPI_COMM_WORLD, AmgXSolver::PRECONDITIONER,
amgx_verbose);
#else
mfem_error("MFEM not configured with AMGX \n");
#endif
}
else
{
prec = new HypreBoomerAMG;
+503
View File
@@ -0,0 +1,503 @@
// MFEM Example 1 - Parallel Version
//
// Compile with: make ex1p
//
// Sample runs: mpirun -np 4 ex1p -m ../data/square-disc.mesh
// mpirun -np 4 ex1p -m ../data/star.mesh
// mpirun -np 4 ex1p -m ../data/star-mixed.mesh
// mpirun -np 4 ex1p -m ../data/escher.mesh
// mpirun -np 4 ex1p -m ../data/fichera.mesh
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../data/star-mixed-p2.mesh -o 2
// mpirun -np 4 ex1p -m ../data/disc-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../data/pipe-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../data/ball-nurbs.mesh -o 2
// mpirun -np 4 ex1p -m ../data/fichera-mixed-p2.mesh -o 2
// mpirun -np 4 ex1p -m ../data/star-surf.mesh
// mpirun -np 4 ex1p -m ../data/square-disc-surf.mesh
// mpirun -np 4 ex1p -m ../data/inline-segment.mesh
// mpirun -np 4 ex1p -m ../data/amr-quad.mesh
// mpirun -np 4 ex1p -m ../data/amr-hex.mesh
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
// NURBS mesh, etc.)
//
// The example highlights the use of mesh refinement, finite
// element grid functions, as well as linear and bilinear forms
// corresponding to the left-hand side and right-hand side of the
// discrete linear system. We also cover the explicit elimination
// of essential boundary conditions, static condensation, and the
// optional connection to the GLVis tool for visualization.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
double u_exact(const Vector &x);
double f_exact(const Vector &x);
// #define FORM_DEFINITE
#define USE_GMRES
#define USE_CSL
#define K2 250.0
int dim;
double kappa;
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
const char *mesh_file = "../data/beam-tet.mesh";
int order = 1;
bool static_cond = false;
bool visualization = 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) or -1 for"
" isoparametric space.");
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);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
dim = mesh->Dimension();
kappa = 2.0 * M_PI;
// 4. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 10,000 elements.
{
int ref_levels =
(int)floor(log(100000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
}
{
double minsize = pmesh->GetElementSize(0);
double maxsize = minsize;
for (int i=1; i<pmesh->GetNE(); ++i)
{
const double size_i = pmesh->GetElementSize(i);
minsize = std::min(minsize, size_i);
maxsize = std::max(maxsize, size_i);
}
cout << myid << ": Element size range: (" << minsize << ", " << maxsize << ")"
<< endl;
}
// 6. Define a parallel finite element space on the parallel mesh. Here we
// use continuous Lagrange finite elements of the specified order. If
// order < 1, we instead use an isoparametric/isogeometric space.
FiniteElementCollection *fec;
if (order > 0)
{
fec = new H1_FECollection(order, dim);
}
else if (pmesh->GetNodes())
{
fec = pmesh->GetNodes()->OwnFEC();
if (myid == 0)
{
cout << "Using isoparametric FEs: " << fec->Name() << endl;
}
}
else
{
fec = new H1_FECollection(order = 1, dim);
}
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 7. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
if (pmesh->bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 8. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (1,phi_i) where phi_i are the basis functions in fespace.
ParLinearForm *b = new ParLinearForm(fespace);
//ConstantCoefficient bcoef(1.0);
FunctionCoefficient bcoef(f_exact);
ConstantCoefficient one(1.0);
ConstantCoefficient zero(0.0);
ConstantCoefficient neg(-K2);
ConstantCoefficient pos(K2);
b->AddDomainIntegrator(new DomainLFIntegrator(bcoef));
b->Assemble();
// 9. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
ParGridFunction x(fespace);
x = 0.0;
// 10. Set up the parallel bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator.
ParBilinearForm *a = new ParBilinearForm(fespace);
a->AddDomainIntegrator(new DiffusionIntegrator(one));
a->AddDomainIntegrator(new MassIntegrator(neg));
#ifdef FORM_DEFINITE
ParBilinearForm *adef = new ParBilinearForm(fespace);
adef->AddDomainIntegrator(new DiffusionIntegrator(one));
adef->AddDomainIntegrator(new MassIntegrator(pos));
if (static_cond) { adef->EnableStaticCondensation(); }
adef->Assemble();
ParGridFunction xdef(fespace);
xdef = 0.0;
ParLinearForm *bdef = new ParLinearForm(fespace);
bdef->AddDomainIntegrator(new DomainLFIntegrator(bcoef));
bdef->Assemble();
HypreParMatrix Adef;
Vector Bdef, Xdef;
adef->FormLinearSystem(ess_tdof_list, xdef, *bdef, Adef, Xdef, Bdef);
#endif
// 11. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
HypreParMatrix A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
if (myid == 0)
{
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
}
//A.Print("helmholtz");
// 12. Define and apply a parallel PCG solver for AX=B with the BoomerAMG
// preconditioner from hypre.
#ifdef FORM_DEFINITE
HypreSolver *amg = new HypreBoomerAMG(Adef);
#else
HypreSolver *amg = new HypreBoomerAMG(A);
#endif
const bool fullDirect = true;
if (fullDirect)
{
#ifdef USE_CSL
Vector Bdef, Xdef;
ParBilinearForm *Mform = new ParBilinearForm(fespace);
Mform->AddDomainIntegrator(new MassIntegrator(pos));
Mform->Assemble();
HypreParMatrix Mmat, Smat, Mcopy;
Mform->FormLinearSystem(ess_tdof_list, x, *b, Mmat, Xdef, Bdef);
Mform->FormLinearSystem(ess_tdof_list, x, *b, Mcopy, Xdef,
Bdef); // There must be a better way than creating two identical matrices.
ParBilinearForm *Sform = new ParBilinearForm(fespace);
Sform->AddDomainIntegrator(new DiffusionIntegrator(one));
Sform->Assemble();
Sform->FormLinearSystem(ess_tdof_list, x, *b, Smat, Xdef, Bdef);
const double beta1 = 1.0;
const double beta2 = 1.0;
Mmat *= -beta1;
HypreParMatrix * cslRe = ParAdd(&Smat, &Mmat);
Mcopy *= beta2;
ComplexHypreParMatrix chpm(cslRe, &Mcopy, false, false);
HypreParMatrix *cSysMat = chpm.GetSystemMatrix();
Array<int> block_trueOffsets(3); // number of variables + 1
block_trueOffsets[0] = 0;
block_trueOffsets[1] = fespace->TrueVSize();
block_trueOffsets[2] = fespace->TrueVSize();
block_trueOffsets.PartialSum();
// Note that B is of true size.
BlockVector trueY(block_trueOffsets), trueX(block_trueOffsets),
trueRhs(block_trueOffsets);
trueRhs.GetBlock(0) = B;
trueRhs.GetBlock(1) = 0.0;
Operator * Arow = new STRUMPACKRowLocMatrix(*cSysMat);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
// strumpack->SetSymmetricPattern(true);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
//Solver * precond = strumpack;
// strumpack->Mult(B, X);
BlockOperator blockDiagA(block_trueOffsets);
for (int i=0; i<2; ++i)
{
blockDiagA.SetDiagonalBlock(i, &A);
}
ProductOperator prod(&blockDiagA, strumpack, false, false);
//GMRESSolver *gmres = new GMRESSolver(fespace->GetComm());
BiCGSTABSolver *gmres = new BiCGSTABSolver(fespace->GetComm());
gmres->SetOperator(prod);
gmres->SetRelTol(1e-8);
gmres->SetMaxIter(10000);
gmres->SetPrintLevel(1);
gmres->Mult(trueRhs, trueY);
strumpack->Mult(trueY, trueX);
X = trueX.GetBlock(0);
double xim2 = trueX.GetBlock(1).Norml2();
xim2 *= xim2;
double sumxim2 = 0.0;
MPI_Allreduce(&xim2, &sumxim2, 1, MPI_DOUBLE, MPI_SUM, fespace->GetComm());
if (myid == 0)
{
cout << myid << ": norm of Xim " << trueX.GetBlock(1).Norml2() << ", global " <<
sqrt(sumxim2) << endl;
}
delete gmres;
delete strumpack;
delete Arow;
#else
Operator * Arow = new STRUMPACKRowLocMatrix(A);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
// strumpack->SetSymmetricPattern(true);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
//Solver * precond = strumpack;
strumpack->Mult(B, X);
delete strumpack;
delete Arow;
#endif
}
else
{
#ifdef USE_GMRES
HypreGMRES *gmres = new HypreGMRES(A);
gmres->SetTol(1e-12);
gmres->SetMaxIter(1000);
gmres->SetPrintLevel(10);
gmres->SetPreconditioner(*amg);
gmres->Mult(B, X);
delete gmres;
#else
HyprePCG *pcg = new HyprePCG(A);
pcg->SetTol(1e-12);
pcg->SetMaxIter(100);
pcg->SetPrintLevel(2);
pcg->SetPreconditioner(*amg);
pcg->Mult(B, X);
#endif
}
/*
HYPRE_ParCSRMatrix* amgP = amg->Get_Restriction();
HypreParMatrix P0(amgP[0], false);
HypreParMatrix P1(amgP[1], false);
HypreParMatrix P2(amgP[2], false);
//HypreParMatrix P3(amgP[3], false);
P0.Print("P0");
*/
// 13. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
// Compute and print the L^2 norm of the error.
{
FunctionCoefficient uex(u_exact);
double err = x.ComputeL2Error(uex);
double xnrm = x.ComputeL2Error(zero);
ParGridFunction zerogf(fespace);
zerogf = 0.0;
double normE = zerogf.ComputeL2Error(uex);
if (myid == 0)
{
cout << "|| E_h - E ||_{L^2} = " << err << endl;
cout << "|| E_h ||_{L^2} = " << xnrm << endl;
cout << "|| E ||_{L^2} = " << normE << endl;
}
}
// 14. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
x.Save(sol_ofs);
}
// 15. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << *pmesh << x << flush;
}
// 16. Free the used memory.
//delete pcg;
delete amg;
delete a;
delete b;
delete fespace;
if (order > 0) { delete fec; }
delete pmesh;
MPI_Finalize();
return 0;
}
double u_exact(const Vector & x)
{
double xi(x(0));
double yi(x(1));
double zi(1.0);
if (x.Size() == 3)
{
zi = x(2);
}
return sin(kappa*xi)*sin(kappa*yi)*sin(kappa*zi);
}
double f_exact(const Vector &x)
{
double xi(x(0));
double yi(x(1));
double zi(1.0);
if (x.Size() == 3)
{
zi = x(2);
}
const double s = 1.0;
return ((3.0*kappa*kappa) - (s*K2)) * sin(kappa*xi)*sin(kappa*yi)*sin(
kappa*zi) / s;
}
+23 -5
View File
@@ -158,9 +158,9 @@ int main(int argc, char *argv[])
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
if (pmesh->bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
@@ -207,18 +207,36 @@ int main(int argc, char *argv[])
// 13. Solve the system AX=B using PCG with the AMS preconditioner from hypre
// (in the full assembly case) or CG with Jacobi preconditioner (in the
// partial assembly case).
if (pa) // Jacobi preconditioning in partial assembly mode
if (pa) // matrix-free auxiliary space solver with PA
{
OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
StopWatch sw;
sw.Clear();
sw.Start();
MatrixFreeAMS *ams = new MatrixFreeAMS(*a, *A, *fespace, muinv, sigma, NULL,
ess_bdr); //, 2, 2);
//OperatorJacobiSmoother jacobi(*a, ess_tdof_list);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(1000);
cg.SetPrintLevel(1);
cg.SetOperator(*A);
cg.SetPreconditioner(Jacobi);
cg.SetPreconditioner(*ams);
//cg.SetPreconditioner(jacobi);
//cg.iterative_mode = false;
sw.Stop();
cout << myid << ": MatrixFreeAMS-CG setup time " << sw.RealTime() << endl;
sw.Clear();
sw.Start();
cg.Mult(B, X);
sw.Stop();
cout << myid << ": MatrixFreeAMS-CG solve time " << sw.RealTime() << endl;
ams->PrintTimings(myid);
}
else
{
+425
View File
@@ -0,0 +1,425 @@
// MFEM Example 3 - Parallel Version
//
// Compile with: make ex3p
//
// Sample runs: mpirun -np 4 ex3p -m ../data/star.mesh
// mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh
// mpirun -np 4 ex3p -m ../data/escher.mesh
// mpirun -np 4 ex3p -m ../data/escher.mesh -o 2
// mpirun -np 4 ex3p -m ../data/fichera.mesh
// mpirun -np 4 ex3p -m ../data/fichera-q2.vtk
// mpirun -np 4 ex3p -m ../data/fichera-q3.mesh
// mpirun -np 4 ex3p -m ../data/square-disc-nurbs.mesh
// mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
// mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
// mpirun -np 4 ex3p -m ../data/amr-hex.mesh
// mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
// mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
// mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
//
// Description: This example code solves a simple electromagnetic diffusion
// problem corresponding to the second order definite Maxwell
// equation curl curl E + E = f with boundary condition
// E x n = <given tangential field>. Here, we use a given exact
// solution E and compute the corresponding r.h.s. f.
// We discretize with Nedelec finite elements in 2D or 3D.
//
// The example demonstrates the use of H(curl) finite element
// spaces with the curl-curl and the (vector finite element) mass
// bilinear form, as well as the computation of discretization
// error when the exact solution is known. Static condensation is
// also illustrated.
//
// We recommend viewing examples 1-2 before viewing this example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
#define TEST_AIRY
#ifdef TEST_AIRY
#include "gsl_sf_airy.h"
#define XSHIFT 0.0 // 0.25
#endif
// Exact solution, E, and r.h.s., f. See below for implementation.
void E_exact(const Vector &, Vector &);
void f_exact(const Vector &, Vector &);
double freq = 1.0, kappa;
int dim;
//#define K2_VALUE 11.9
//#define K2_VALUE 2.1
//#define K2_VALUE 1500.9
#define K2_VALUE 10981.41589009910 // 104.792251097584^2 or 5 GHz
void test_Airy_epsilon(const Vector &x, Vector &e)
{
e(0) = 1.0;
e(1) = 1.0;
e(2) = (4.0 * (x(0) + XSHIFT)) - 1.0;
e *= -K2_VALUE;
}
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
//const char *mesh_file = "../data/beam-tet.mesh";
const char *mesh_file = "../data/inline-tetHalf.mesh";
//const char *mesh_file = "inline-tetSlab.mesh";
//const char *mesh_file = "../data/inline-hexHalf.mesh";
//const char *mesh_file = "../data/inline-tet.mesh";
int order = 2;
bool static_cond = false;
bool visualization = 1;
#ifdef MFEM_USE_STRUMPACK
bool use_strumpack = false;
#endif
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(&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(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
#ifdef MFEM_USE_STRUMPACK
args.AddOption(&use_strumpack, "-strumpack", "--strumpack-solver",
"-no-strumpack", "--no-strumpack-solver",
"Use STRUMPACK's double complex linear solver.");
#endif
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
cout << "Using k2 " << K2_VALUE << endl;
}
kappa = freq * M_PI;
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
// 4. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 1,000 elements.
{
int ref_levels =
(int)floor(log(100000./mesh->GetNE())/log(2.)/dim);
ref_levels = 0;
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
// meshes need to be reoriented before we can define high-order Nedelec
// spaces on them.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
}
pmesh->ReorientTetMesh();
// 6. Define a parallel finite element space on the parallel mesh. Here we
// use the Nedelec finite elements of the specified order.
FiniteElementCollection *fec = new ND_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
long globalNE = pmesh->GetGlobalNE();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
cout << "Number of mesh elements: " << globalNE << endl;
}
// 7. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
if (pmesh->bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 8. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (f,phi_i) where f is given by the function f_exact and phi_i are the
// basis functions in the finite element fespace.
VectorFunctionCoefficient f(sdim, f_exact);
ParLinearForm *b = new ParLinearForm(fespace);
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
b->Assemble();
// 9. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x by projecting the exact
// solution. Note that only values from the boundary edges will be used
// when eliminating the non-homogeneous boundary condition to modify the
// r.h.s. vector b.
ParGridFunction x(fespace);
ParGridFunction solproj(fespace);
VectorFunctionCoefficient E(sdim, E_exact);
x.ProjectCoefficient(E);
solproj = x;
// 10. Set up the parallel bilinear form corresponding to the EM diffusion
// operator curl muinv curl + sigma I, by adding the curl-curl and the
// mass domain integrators.
Coefficient *muinv = new ConstantCoefficient(1.0);
Coefficient *sigma = new ConstantCoefficient(-K2_VALUE);
ParBilinearForm *a = new ParBilinearForm(fespace);
a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
#ifdef TEST_AIRY
VectorFunctionCoefficient epsilon(3, test_Airy_epsilon);
a->AddDomainIntegrator(new VectorFEMassIntegrator(epsilon));
#else
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
#endif
// 11. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
HypreParMatrix A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
if (myid == 0)
{
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
}
StopWatch chrono;
chrono.Clear();
chrono.Start();
#ifdef MFEM_USE_STRUMPACK
if (use_strumpack)
{
Operator * Arow = new STRUMPACKRowLocMatrix(A);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
// strumpack->SetSymmetricPattern(true);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
//Solver * precond = strumpack;
strumpack->Mult(B, X);
//X = 0.0;
{
// Check residual
Vector res(X.Size());
Vector ssol(X.Size());
ssol = X;
const double Bnrm = B.Norml2();
const double Bnrm2 = Bnrm*Bnrm;
A.Mult(ssol, res);
res -= B;
const double Rnrm = res.Norml2();
const double Rnrm2 = Rnrm*Rnrm;
double sumBnrm2 = 0.0;
double sumRnrm2 = 0.0;
MPI_Allreduce(&Bnrm2, &sumBnrm2, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
MPI_Allreduce(&Rnrm2, &sumRnrm2, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
if (myid == 0)
{
cout << myid << ": STRUMPACK residual norm " << sqrt(sumRnrm2) << ", B norm " <<
sqrt(sumBnrm2) << endl;
}
}
delete strumpack;
delete Arow;
}
else
#endif
{
// 12. Define and apply a parallel PCG solver for AX=B with the AMS
// preconditioner from hypre.
ParFiniteElementSpace *prec_fespace =
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
HypreSolver *ams = new HypreAMS(A, prec_fespace);
HyprePCG *pcg = new HyprePCG(A);
pcg->SetTol(1e-12);
pcg->SetMaxIter(500);
pcg->SetPrintLevel(2);
pcg->SetPreconditioner(*ams);
pcg->Mult(B, X);
delete pcg;
delete ams;
}
chrono.Stop();
if (myid == 0)
{
cout << "Solver time " << chrono.RealTime() << endl;
}
// 13. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
// 14. Compute and print the L^2 norm of the error.
{
double err = x.ComputeL2Error(E);
ParGridFunction x0(fespace);
x0 = 0.0;
double solnrm = x0.ComputeL2Error(E);
if (myid == 0)
{
cout << "\n|| E_h - E ||_{L^2} = " << err << ", relative error " << err / solnrm
<< endl;
}
}
// 15. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
x.Save(sol_ofs);
//solproj.Save(sol_ofs);
}
// 16. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << *pmesh << x << flush;
}
// 17. Free the used memory.
delete a;
delete sigma;
delete muinv;
delete b;
delete fespace;
delete fec;
delete pmesh;
MPI_Finalize();
return 0;
}
void E_exact(const Vector &x, Vector &E)
{
if (dim == 3)
{
#ifdef TEST_AIRY
const double y = (4.0 * (x(0) + XSHIFT)) - 1.0;
const double k = sqrt(K2_VALUE);
const double beta = pow(0.25 * k, 2.0/3.0);
E(0) = 0.0;
E(1) = 0.0;
E(2) = gsl_sf_airy_Ai(-beta * y, GSL_PREC_DOUBLE);
#else
E(0) = sin(kappa * x(1));
E(1) = sin(kappa * x(2));
E(2) = sin(kappa * x(0));
#endif
}
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)
{
#ifdef TEST_AIRY
f = 0.0;
#else
f(0) = (-K2_VALUE + kappa * kappa) * sin(kappa * x(1));
f(1) = (-K2_VALUE + kappa * kappa) * sin(kappa * x(2));
f(2) = (-K2_VALUE + kappa * kappa) * sin(kappa * x(0));
#endif
}
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; }
}
}
+848
View File
@@ -0,0 +1,848 @@
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "gsl_sf_airy.h"
#include "multigrid.hpp"
using namespace std;
using namespace mfem;
//#define DIRECT_SOLVER
// Define exact solution
void E_exact(const Vector & x, Vector & E);
void H_exact(const Vector & x, Vector & H);
void f_exact_H(const Vector & x, Vector & f_H);
void get_maxwell_solution(const Vector & x, double E[], double curlE[],
double curl2E[]);
void epsilon_func(const Vector &x, Vector &M);
void epsilon2_func(const Vector &x, Vector &M);
void epsilon_func_mat(const Vector &x, DenseMatrix &M);
int dim;
double omega;
int sol = 3;
class FOSLSSolver : public Solver
{
public:
FOSLSSolver(ParFiniteElementSpace *fespace_, std::vector<HypreParMatrix*>& P,
const int myid_)
: Solver(2 * fespace_->GetTrueVSize()), M_inv(MPI_COMM_WORLD),
fespace(fespace_),
n(fespace_->GetTrueVSize()), nfull(fespace_->GetVSize()), LSpcg(MPI_COMM_WORLD),
myid(myid_)
{
z.SetSize(n);
Minv_x.SetSize(n);
ParMesh *pmesh = fespace->GetParMesh();
int dim = pmesh->Dimension();
int sdim = pmesh->SpaceDimension();
VectorFunctionCoefficient epsilon(dim, epsilon_func);
VectorFunctionCoefficient epsilonT(epsilon); // transpose of epsilon
VectorFunctionCoefficient epsilon2(dim,epsilon2_func);
ConstantCoefficient pos(omega);
ConstantCoefficient sigma(omega*omega);
ScalarVectorProductCoefficient coeff(pos,epsilon);
ScalarVectorProductCoefficient coeffT(pos,epsilonT);
ScalarVectorProductCoefficient coeff2(sigma,epsilon2);
bM = new ParBilinearForm(fespace);
bM->AddDomainIntegrator(new VectorFEMassIntegrator());
bM->Assemble();
bM->Finalize();
bM_eps = new ParBilinearForm(fespace);
bM_eps->AddDomainIntegrator(new VectorFEMassIntegrator(epsilonT));
bM_eps->Assemble();
bM_eps->Finalize();
bM_curl = new ParMixedBilinearForm(fespace,fespace);
bM_curl->AddDomainIntegrator(new MixedVectorWeakCurlIntegrator());
bM_curl->Assemble();
bM_curl->Finalize();
Array<int> ess_tdof_list; // empty
bM->FormSystemMatrix(ess_tdof_list, M);
bM_eps->FormSystemMatrix(ess_tdof_list, M_eps);
//bM_curl->FormColSystemMatrix(ess_tdof_list, M_curl);
{
OperatorPtr M_curl_ptr;
bM_curl->FormRectangularSystemMatrix(ess_tdof_list, ess_tdof_list, M_curl_ptr);
M_curl = M_curl_ptr.As<HypreParMatrix>();
}
M_inv.SetAbsTol(1.0e-12);
M_inv.SetRelTol(1.0e-12);
M_inv.SetMaxIter(100);
M_inv.SetOperator(M);
M_inv.SetPrintLevel(0);
block_trueOffsets.SetSize(5);
block_trueOffsets[0] = 0;
block_trueOffsets[1] = n;
block_trueOffsets[2] = n;
block_trueOffsets[3] = n;
block_trueOffsets[4] = n;
block_trueOffsets.PartialSum();
trueRhs = new BlockVector(block_trueOffsets);
trueSol = new BlockVector(block_trueOffsets);
// _ _ _ _ _ _
// | | | | | |
// | A00 A01 | | E | |F_E |
// | | | | = | |
// | A10 A11 | | H | |F_G |
// |_ _| |_ _| |_ _|
//
// A00 = (curl E, curl F) + \omega^2 (E,F)
// A01 = - \omega *( (curl E, F) + (E,curl F)
// A10 = - \omega *( (curl H, G) + (H,curl G)
// A11 = (curl H, curl G) + \omega^2 (H,G)
ParBilinearForm *a_EE = new ParBilinearForm(fespace);
a_EE->AddDomainIntegrator(new CurlCurlIntegrator());
a_EE->AddDomainIntegrator(new VectorFEMassIntegrator(coeff2));
a_EE->AddBoundaryIntegrator(new VectorFEMassIntegrator());
a_EE->Assemble();
a_EE->Finalize();
HypreParMatrix *A_EE = new HypreParMatrix;
a_EE->FormSystemMatrix(ess_tdof_list, *A_EE);
ParBilinearForm *a_HH = new ParBilinearForm(fespace);
a_HH->AddDomainIntegrator(new CurlCurlIntegrator());
a_HH->AddDomainIntegrator(new VectorFEMassIntegrator(sigma));
a_HH->AddBoundaryIntegrator(new VectorFEMassIntegrator());
a_HH->Assemble();
a_HH->Finalize();
HypreParMatrix *A_HH = new HypreParMatrix;
a_HH->FormSystemMatrix(ess_tdof_list, *A_HH);
ParBilinearForm *a_tang = new ParBilinearForm(fespace);
a_tang->AddBoundaryIntegrator(new VectorFEBoundaryTangentIntegrator(1.0));
a_tang->Assemble();
a_tang->Finalize();
OperatorHandle A_tang_ptr;
a_tang->FormSystemMatrix(ess_tdof_list, A_tang_ptr);
HypreParMatrix *A_tang = A_tang_ptr.As<HypreParMatrix>();
// (k curl u, eps v) + (k u, curl v)
ParMixedBilinearForm *a_mix1 = new ParMixedBilinearForm(fespace,fespace);
a_mix1->AddDomainIntegrator(new MixedVectorCurlIntegrator(coeffT));
a_mix1->AddDomainIntegrator(new MixedVectorWeakCurlIntegrator(pos));
a_mix1->Assemble();
a_mix1->Finalize();
HypreParMatrix *A_mix1 = NULL; // new HypreParMatrix;
//a_mix1->FormColSystemMatrix(ess_tdof_list, *A_mix1);
{
OperatorPtr A_mix1_ptr;
a_mix1->FormRectangularSystemMatrix(ess_tdof_list, ess_tdof_list, A_mix1_ptr);
A_mix1 = A_mix1_ptr.As<HypreParMatrix>();
}
// (k curl u, v) + (k eps u, curl v)
ParMixedBilinearForm *a_mix2 = new ParMixedBilinearForm(fespace,fespace);
a_mix2->AddDomainIntegrator(new MixedVectorCurlIntegrator(pos));
a_mix2->AddDomainIntegrator(new MixedVectorWeakCurlIntegrator(coeff));
a_mix2->Assemble();
a_mix2->Finalize();
HypreParMatrix *A_mix2 = NULL; // new HypreParMatrix;
//a_mix2->FormColSystemMatrix(ess_tdof_list, *A_mix2);
{
OperatorPtr A_mix2_ptr;
a_mix2->FormRectangularSystemMatrix(ess_tdof_list, ess_tdof_list, A_mix2_ptr);
A_mix2 = A_mix2_ptr.As<HypreParMatrix>();
}
BlockOperator *LS_Maxwellop = new BlockOperator(block_trueOffsets);
const int numBlocks = 4;
LS_Maxwellop->SetBlock(0, 0, A_EE);
LS_Maxwellop->SetBlock(1, 0, A_mix2, -1.0); // no bc
LS_Maxwellop->SetBlock(3, 0, A_tang, -1.0);
LS_Maxwellop->SetBlock(0, 1, A_mix1, -1.0); // no bc
LS_Maxwellop->SetBlock(1, 1, A_HH);
LS_Maxwellop->SetBlock(2, 1, A_tang, -1.0); // other rotation
LS_Maxwellop->SetBlock(1, 2, A_tang);
LS_Maxwellop->SetBlock(2, 2, A_EE);
LS_Maxwellop->SetBlock(3, 2, A_mix2, -1.0); // no bc
LS_Maxwellop->SetBlock(0, 3, A_tang); // other rotation
LS_Maxwellop->SetBlock(2, 3, A_mix1, -1.0); // no bc
LS_Maxwellop->SetBlock(3, 3, A_HH);
// Set up the preconditioner
Array2D<HypreParMatrix*> blockA(numBlocks, numBlocks);
Array2D<double> blockAcoef(numBlocks, numBlocks);
for (int i=0; i<numBlocks; ++i)
{
for (int j=0; j<numBlocks; ++j)
{
if (LS_Maxwellop->IsZeroBlock(i,j) == 0)
{
blockA(i,j) = static_cast<HypreParMatrix *>(&LS_Maxwellop->GetBlock(i,j));
blockAcoef(i,j) = LS_Maxwellop->GetBlockCoef(i,j);
}
else
{
blockA(i,j) = NULL;
blockAcoef(i,j) = 1.0;
}
}
}
LSpcg.SetAbsTol(1.0e-12);
LSpcg.SetRelTol(1.0e-8);
LSpcg.SetMaxIter(2000);
LSpcg.SetOperator(*LS_Maxwellop);
LSpcg.SetPrintLevel(1);
BlockMGSolver * precMG = NULL;
#ifdef DIRECT_SOLVER
std::vector<std::vector<int> > blockProcOffsets(numBlocks);
std::vector<std::vector<int> > all_block_num_loc_rows(numBlocks);
Array2D<SparseMatrix*> Asp;
Asp.SetSize(numBlocks,numBlocks);
{
int nprocs, rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
std::vector<int> allnumrows(nprocs);
const int blockNumRows = n;
MPI_Allgather(&blockNumRows, 1, MPI_INT, allnumrows.data(), 1, MPI_INT,
MPI_COMM_WORLD);
for (int b=0; b<numBlocks; ++b)
{
blockProcOffsets[b].resize(nprocs);
all_block_num_loc_rows[b].resize(nprocs);
for (int j=0; j<numBlocks; ++j)
{
Asp(b,j) = NULL;
}
}
blockProcOffsets[0][0] = 0;
for (int i=0; i<nprocs-1; ++i)
{
blockProcOffsets[0][i+1] = blockProcOffsets[0][i] + allnumrows[i];
}
for (int i=0; i<nprocs; ++i)
{
for (int b=0; b<numBlocks; ++b)
{
all_block_num_loc_rows[b][i] = allnumrows[i];
}
for (int b=1; b<numBlocks; ++b)
{
blockProcOffsets[b][i] = blockProcOffsets[0][i];
}
}
}
LSH = CreateHypreParMatrixFromBlocks(MPI_COMM_WORLD, block_trueOffsets, blockA,
Asp,
blockAcoef, blockProcOffsets, all_block_num_loc_rows);
invLSH = CreateStrumpackSolver(new STRUMPACKRowLocMatrix(*LSH), MPI_COMM_WORLD);
#else
precMG = new BlockMGSolver(LS_Maxwellop->Height(), LS_Maxwellop->Width(),
blockA, blockAcoef, P);
precMG->SetTheta(0.5);
LSpcg.SetPreconditioner(*precMG);
#endif
}
void SetOperator(const Operator &op) { }
void Mult(const Vector &x, Vector &y) const
{
// Solve (curl E, curl v) - k^2 (eps E, v) + ik <pi(u), pi(v)> = (x, v), with no BC,
// where x is complex, using FOSLS. This is the Galerkin discretization of
// curl curl u - k^2 eps u = x, with ik n x u x n - n x curl u = 0 on the boundary.
MFEM_VERIFY(x.Size() == 2*n, "");
(*trueRhs) = 0.0;
for (int i=0; i<n; ++i)
{
z[i] = x[i]; // Set z = x_Re
}
M_inv.Mult(z, Minv_x);
M_eps.Mult(Minv_x, z);
trueRhs->GetBlock(0) -= z;
M_curl->Mult(Minv_x, z);
z *= 1.0 / omega;
trueRhs->GetBlock(1) = z;
for (int i=0; i<n; ++i)
{
z[i] = x[n + i]; // Set z = x_Im
}
M_inv.Mult(z, Minv_x);
M_eps.Mult(Minv_x, z);
trueRhs->GetBlock(2) -= z;
M_curl->Mult(Minv_x, z);
z *= 1.0 / omega;
trueRhs->GetBlock(3) += z;
#ifdef DIRECT_SOLVER
invLSH->Mult(*trueRhs, *trueSol);
#else
LSpcg.Mult(*trueRhs, *trueSol);
#endif
for (int i=0; i<n; ++i)
{
y[i] = trueSol->GetBlock(0)[i]; // Set y_Re = E_Re
}
for (int i=0; i<n; ++i)
{
y[n + i] = trueSol->GetBlock(2)[i]; // Set y_Im = E_Im
}
}
void ComplexFOSLSTest()
{
Array<int> block_offsets;
block_offsets.SetSize(5);
block_offsets[0] = 0;
block_offsets[1] = nfull;
block_offsets[2] = nfull;
block_offsets[3] = nfull;
block_offsets[4] = nfull;
block_offsets.PartialSum();
BlockVector rhs(block_offsets);
BlockVector rhsIm(block_offsets);
rhs = 0.0;
rhsIm = 0.0;
const double ci = 0.0; // 3.3;
// Exact complex solution: E = Er + i Ei = Epw + ci i Epw, where Epw is E_exact.
// Set up the linear form with the real part Fr only.
const int sdim = 3;
VectorFunctionCoefficient Eex(sdim, E_exact);
VectorFunctionCoefficient Hex(sdim, H_exact);
ConstantCoefficient negOne(-1.0);
VectorFunctionCoefficient mEex(sdim, E_exact, &negOne);
VectorFunctionCoefficient mHex(sdim, H_exact, &negOne);
ConstantCoefficient neg(-omega);
ConstantCoefficient pos(omega);
VectorFunctionCoefficient f_H(3,f_exact_H); // f / omega
ScalarVectorProductCoefficient sf_H(neg,f_H);
ScalarVectorProductCoefficient spf_H(pos,f_H);
ScalarVectorProductCoefficient mf_H(negOne,f_H);
VectorFunctionCoefficient epsilon(3, epsilon_func);
VectorFunctionCoefficient epsilonT(3, epsilon_func); // transpose of epsilon
MatrixFunctionCoefficient epsilonTmat(3,
epsilon_func_mat); // transpose of epsilon
MatVecCoefficient epsT_spf_H(epsilonTmat, spf_H);
MatVecCoefficient epsT_sf_H(epsilonTmat, sf_H);
ParLinearForm *b_E = new ParLinearForm;
b_E->Update(fespace, rhs.GetBlock(0), 0);
b_E->AddDomainIntegrator(new VectorFEDomainLFIntegrator(
epsT_sf_H)); // (k^{-1} Fr, -k eps Qr)
b_E->AddBoundaryIntegrator(new VectorFEDomainLFIntegrator(
Eex)); // <n x E_Re x n, n x Q_Re x n>
b_E->Assemble();
ParLinearForm *b_H = new ParLinearForm;
b_H->Update(fespace, rhs.GetBlock(1), 0);
b_H->AddDomainIntegrator(new VectorFEDomainLFCurlIntegrator(
f_H)); // (k^{-1} Fr, curl Rr)
b_H->AddBoundaryIntegrator(new VectorFEDomainLFIntegrator(
Hex)); // <n x H_Re, n x R_Re>
b_H->Assemble();
ParLinearForm *b_E_Im = new ParLinearForm;
b_E_Im->Update(fespace, rhs.GetBlock(2), 0);
b_E_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(
mHex)); // <n x H_Re, n x Q_Im x n>
b_E_Im->Assemble();
ParLinearForm *b_H_Im = new ParLinearForm;
b_H_Im->Update(fespace, rhs.GetBlock(3), 0);
b_H_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(
mEex)); // -<n x E_Re x n, n x R_Im>
b_H_Im->Assemble();
// Add the imaginary part Fi.
ParLinearForm *b_Ei = new ParLinearForm;
b_Ei->Update(fespace, rhsIm.GetBlock(0), 0);
b_Ei->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(
Hex)); // -<n x H_Im, n x Q_Re x n>
b_Ei->Assemble();
ParLinearForm *b_Hi = new ParLinearForm;
b_Hi->Update(fespace, rhsIm.GetBlock(1), 0);
b_Hi->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(
Eex)); // <n x E_Im x n, n x R_Re>
b_Hi->Assemble();
ParLinearForm *b_Ei_Im = new ParLinearForm;
b_Ei_Im->Update(fespace, rhsIm.GetBlock(2), 0);
b_Ei_Im->AddDomainIntegrator(new VectorFEDomainLFIntegrator(
epsT_sf_H)); // -(k^{-1} Fi, k eps Qi)
b_Ei_Im->AddBoundaryIntegrator(new VectorFEDomainLFIntegrator(
Eex)); // <n x E_Im x n, n x Q_Im x n>
b_Ei_Im->Assemble();
ParLinearForm *b_Hi_Im = new ParLinearForm;
b_Hi_Im->Update(fespace, rhsIm.GetBlock(3), 0);
b_Hi_Im->AddDomainIntegrator(new VectorFEDomainLFCurlIntegrator(
f_H)); // (k^{-1} Fi, curl Ri)
b_Hi_Im->AddBoundaryIntegrator(new VectorFEDomainLFIntegrator(
Hex)); // <n x H_Im, n x R_Im>
b_Hi_Im->Assemble();
rhsIm *= ci;
rhs += rhsIm;
for (int i=0; i<4; ++i)
{
fespace->GetProlongationMatrix()->MultTranspose(rhs.GetBlock(i),
trueRhs->GetBlock(i));
}
#ifdef DIRECT_SOLVER
invLSH->Mult(*trueRhs, *trueSol);
#else
LSpcg.Mult(*trueRhs, *trueSol);
#endif
// Check error
ParGridFunction E_gf(fespace);
int order = 2;
int order_quad = std::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));
}
ParMesh *pmesh = fespace->GetParMesh();
// Check error of real part
E_gf.SetFromTrueDofs(trueSol->GetBlock(0));
double Error_E = E_gf.ComputeL2Error(Eex, irs);
double norm_E = ComputeGlobalLpNorm(2, Eex, *pmesh, irs);
cout << myid << ": real error " << Error_E << " relative to " << norm_E << endl;
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
E_gf.Save(sol_ofs);
}
// Check error of imaginary part
E_gf.SetFromTrueDofs(trueSol->GetBlock(2));
const double iml2 = E_gf.Norml2();
E_gf *= ci == 0.0 ? 0.0 : (1.0 / ci);
Error_E = E_gf.ComputeL2Error(Eex, irs);
cout << myid << ": imag error " << Error_E << " relative to " << norm_E <<
", l2 norm " << iml2 << endl;
}
private:
BlockVector *trueRhs, *trueSol;
Array<int> block_trueOffsets;
ParBilinearForm *bM, *bM_eps;
ParMixedBilinearForm *bM_curl;
HypreParMatrix M, M_eps;
HypreParMatrix *M_curl;
CGSolver M_inv;
const int n;
const int nfull;
const int myid;
mutable Vector z, Minv_x;
CGSolver LSpcg;
STRUMPACKSolver *invLSH;
HypreParMatrix *LSH;
ParFiniteElementSpace *fespace;
};
int main(int argc, char *argv[])
{
StopWatch chrono;
// 1. Initialize MPI
int num_procs, myid;
MPI_Init(&argc, &argv); // Initialize MPI
MPI_Comm_size(MPI_COMM_WORLD,
&num_procs); //total number of processors available
MPI_Comm_rank(MPI_COMM_WORLD, &myid); // Determine process identifier
// 1. Parse command-line options.
// geometry file
const char *mesh_file = "../data/inline-hex.mesh";
// finite element order of approximation
int order = 1;
// static condensation flag
bool static_cond = false;
// visualization flag
bool visualization = 1;
// number of wavelengths
double k = 1.0;
// number of mg levels
int ref_levels = 1;
// number of initial ref
int initref = 1;
// 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(&k, "-k", "--wavelengths",
"Number of wavelengths.");
args.AddOption(&ref_levels, "-ref", "--ref_levels",
"Number of Refinements.");
args.AddOption(&initref, "-initref", "--initref",
"Number of initial refinements.");
args.AddOption(&sol, "-sol", "--exact",
"Exact solution flag - "
" 1:sinusoidal, 2: point source, 3: plane wave");
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();
// check if the inputs are correct
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// Angular frequency
//omega = 2.0*k*M_PI;
omega = k;
// 2. Read the mesh from the given mesh file.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
if (sol == 4)
{
mesh->EnsureNodes();
GridFunction *nodes = mesh->GetNodes();
(*nodes) *= 0.5;
}
dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
// 3. Executing uniform h-refinement
for (int i = 0; i < initref; i++ )
{
mesh->UniformRefinement();
}
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
// 4. Define a finite element space on the mesh.
FiniteElementCollection *fec = new ND_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
std::vector<ParFiniteElementSpace * > fespaces(ref_levels+1);
std::vector<ParMesh * > ParMeshes(ref_levels+1);
std::vector<HypreParMatrix*> P(ref_levels);
for (int i = 0; i < ref_levels; i++)
{
ParMeshes[i] =new ParMesh(*pmesh);
fespaces[i] = new ParFiniteElementSpace(*fespace, *ParMeshes[i]);
pmesh->UniformRefinement();
// Update fespace
fespace->Update();
OperatorHandle Tr(Operator::Hypre_ParCSR);
fespace->GetTrueTransferOperator(*fespaces[i], Tr);
Tr.SetOperatorOwner(false);
Tr.Get(P[i]);
}
fespaces[ref_levels] = new ParFiniteElementSpace(*fespace);
FOSLSSolver fosls(fespace, P, myid);
fosls.ComplexFOSLSTest();
for (auto p: ParMeshes) { delete p; }
for (auto p: fespaces) { delete p; }
for (auto p: P) { delete p; }
ParMeshes.clear();
fespaces.clear();
P.clear();
delete fec;
delete fespace;
delete pmesh;
MPI_Finalize();
return 0;
}
//define exact solution
void E_exact(const Vector &x, Vector &E)
{
double curlE[3], curl2E[3];
get_maxwell_solution(x, E, curlE, curl2E);
}
void H_exact(const Vector &x, Vector &H)
{
double E[3], curlE[3], curl2E[3];
get_maxwell_solution(x, E, curlE, curl2E);
for (int i = 0; i<3; i++) { H(i) = curlE[i]/omega; }
}
void f_exact_H(const Vector &x, Vector &f)
{
// curl H - omega E = f
// = curl (curl E / omega) - omega E
f = 0.0;
if (sol !=4)
{
double E[3], curlE[3], curl2E[3];
get_maxwell_solution(x, E, curlE, curl2E);
f(0) = curl2E[0] / omega - omega * E[0];
f(1) = curl2E[1] / omega - omega * E[1];
f(2) = curl2E[2] / omega - omega * E[2];
}
}
void get_maxwell_solution(const Vector &X, double E[], double curlE[],
double curl2E[])
{
double x = X[0];
double y = X[1];
double z = X[2];
if (sol ==-1)
{
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] = -(x-1.0) * x * (y*(2.0*z-3.0)+1.0);
curlE[1] = -2.0*(y-1.0)*y*(x-z);
curlE[2] = (z-1)*z*(1.0+y*(2.0*x-3.0));
curl2E[0] = 2.0 * y * (1.0 - y) - (2.0 * x - 3.0) * z * (1 - z);
curl2E[1] = 2.0 * y * (x * (1.0 - x) + (1.0 - z) * z);
curl2E[2] = 2.0 * y * (1.0 - y) + x * (3.0 - 2.0 * z) * (1.0 - x);
}
else if (sol == 0) // polynomial
{
// Polynomial vanishing on the boundary
E[0] = y * z * (1.0 - y) * (1.0 - z);
E[1] = (1.0 - x) * x * y * (1.0 - z) * z;
E[2] = (1.0 - x) * x * (1.0 - y) * y;
//
curlE[0] = -(-1.0 + x) * x * (1.0 + y * (-3.0 + 2.0 * z));
curlE[1] = -2.0 * (-1.0 + y) * y * (x - z);
curlE[2] = (1.0 + (-3.0 + 2.0 * x) * y) * (-1.0 + z) * z;
curl2E[0] = -2.0 * (-1.0 + y) * y + (-3.0 + 2.0 * x) * (-1.0 + z) * z;
curl2E[1] = -2.0 * y * (-x + x * x + (-1.0 + z) * z);
curl2E[2] = -2.0 * (-1.0 + y) * y + (-1.0 + x) * x * (-3.0 + 2.0 * z);
}
else if (sol == 1) // sinusoidal
{
E[0] = sin(omega * y);
E[1] = sin(omega * z);
E[2] = sin(omega * x);
curlE[0] = -omega * cos(omega * z);
curlE[1] = -omega * cos(omega * x);
curlE[2] = -omega * cos(omega * y);
curl2E[0] = omega * omega * E[0];
curl2E[1] = omega * omega * E[1];
curl2E[2] = omega * omega * E[2];
}
else if (sol == 2) // point source
{
// shift to avoid singularity
double x0 = x + 0.1;
double x1 = y + 0.1;
double x2 = z + 0.1;
double r = sqrt(x0 * x0 + x1 * x1 + x2 * x2);
E[0] = cos(omega * r);
E[1] = 0.0;
E[2] = 0.0;
double r_x = x0 / r;
double r_y = x1 / r;
double r_z = x2 / r;
double r_xy = -(r_x / r) * r_y;
double r_xz = -(r_x / r) * r_z;
double r_yx = r_xy;
double r_yy = (1.0 / r) * (1.0 - r_y * r_y);
double r_zx = r_xz;
double r_zz = (1.0 / r) * (1.0 - r_z * r_z);
curlE[0] = 0.0;
curlE[1] = -omega * r_z * sin(omega * r);
curlE[2] = omega * r_y * sin(omega * r);
curl2E[0] = omega * ((r_yy + r_zz) * sin(omega * r) +
(omega * r_y * r_y + omega * r_z * r_z) * cos(omega * r));
curl2E[1] = -omega * (r_yx * sin(omega * r) + omega * r_y * r_x * cos(
omega * r));
curl2E[2] = -omega * (r_zx * sin(omega * r) + omega * r_z * r_x * cos(
omega * r));
}
else if (sol == 3) // plane wave
{
double coeff = omega / sqrt(3.0);
E[0] = cos(coeff * (x + y + z));
E[1] = 0.0;
E[2] = 0.0;
curlE[0] = 0.0;
curlE[1] = -coeff * sin(coeff * (x + y + z));
curlE[2] = coeff * sin(coeff * (x + y + z));
curl2E[0] = 2.0 * coeff * coeff * E[0];
curl2E[1] = -coeff * coeff * E[0];
curl2E[2] = -coeff * coeff * E[0];
}
else if (sol == -1)
{
E[0] = cos(omega * y);
E[1] = 0.0;
curlE[0] = 0.0;
curlE[1] = 0.0;
curlE[2] = -omega * sin(omega * y);
curl2E[0] = omega*omega * cos(omega*y);
curl2E[1] = 0.0;
curl2E[2] = 0.0;
}
else if (sol == 4) // Airy function
{
E[0] = 0;
E[1] = 0;
// double b = -pow(omega/4.0,2.0/3.0)*(4.0*x(0)-1.0);
double b = -pow(omega/4.0,2.0/3.0)*(4.0*x-1.0);
//E[2] = boost::math::airy_ai(b);
E[2] = gsl_sf_airy_Ai(b, GSL_PREC_DOUBLE);
curlE[0] = 0.0;
curlE[1] = 4.0 * pow(omega/4.0,2.0/3.0) * gsl_sf_airy_Ai_deriv(b,
GSL_PREC_DOUBLE);
curlE[2] = 0.0;
// not used
curl2E[0] = 0.0;
curl2E[1] = 0.0;
curl2E[2] = 0.0;
}
}
void epsilon_func(const Vector &x, Vector &M)
{
M.SetSize(3);
M = 1.0;
if (sol == 4)
{
M[2] = 4.0*x(0)-1.0;
}
}
void epsilon2_func(const Vector &x, Vector &M)
{
M.SetSize(3);
M = 1.0;
if (sol == 4)
{
M[2] = (4.0*x(0)-1.0) * (4.0*x(0)-1.0);
}
}
void epsilon_func_mat(const Vector &x, DenseMatrix &M)
{
M.SetSize(3);
M = 0.0;
M(0,0) = 1.0;
M(1,1) = 1.0;
if (sol != 4)
{
M(2,2) = 1.0;
}
else
{
M(2,2) = 4.0*x(0)-1.0;
}
}
+3293
View File
File diff suppressed because it is too large Load Diff
+764
View File
@@ -0,0 +1,764 @@
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "gsl_sf_airy.h"
#include "multigrid.hpp"
using namespace std;
using namespace mfem;
#define COMPLEX_IMPEDANCE // Whether to use impedance BC and solve a complex system
// Define exact solution
void E_exact(const Vector & x, Vector & E);
void H_exact(const Vector & x, Vector & H);
void f_exact_H(const Vector & x, Vector & f_H);
void get_maxwell_solution(const Vector & x, double E[], double curlE[],
double curl2E[]);
void epsilon_func(const Vector &x, DenseMatrix &M);
void epsilon2_func(const Vector &x, DenseMatrix &M);
int dim;
double omega;
int sol = 4;
int main(int argc, char *argv[])
{
StopWatch chrono;
// 1. Initialize MPI
int num_procs, myid;
MPI_Init(&argc, &argv); // Initialize MPI
MPI_Comm_size(MPI_COMM_WORLD,
&num_procs); //total number of processors available
MPI_Comm_rank(MPI_COMM_WORLD, &myid); // Determine process identifier
// 1. Parse command-line options.
// geometry file
const char *mesh_file = "../data/inline-hex.mesh";
// finite element order of approximation
int order = 1;
// static condensation flag
bool static_cond = false;
// visualization flag
bool visualization = 1;
// number of wavelengths
double k = 1.0;
// number of mg levels
int ref_levels = 1;
// number of initial ref
int initref = 1;
// 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(&k, "-k", "--wavelengths",
"Number of wavelengths.");
args.AddOption(&ref_levels, "-ref", "--ref_levels",
"Number of Refinements.");
args.AddOption(&initref, "-initref", "--initref",
"Number of initial refinements.");
args.AddOption(&sol, "-sol", "--exact",
"Exact solution flag - "
" 1:sinusoidal, 2: point source, 3: plane wave");
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();
// check if the inputs are correct
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// Angular frequency
//omega = 2.0*k*M_PI;
omega = k;
// 2. Read the mesh from the given mesh file.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
if (sol == 4)
{
mesh->EnsureNodes();
GridFunction *nodes = mesh->GetNodes();
(*nodes) *= 0.5;
}
dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
// 3. Executing uniform h-refinement
for (int i = 0; i < initref; i++ )
{
mesh->UniformRefinement();
}
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
// 4. Define a finite element space on the mesh.
FiniteElementCollection *fec = new ND_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
std::vector<ParFiniteElementSpace * > fespaces(ref_levels+1);
std::vector<ParMesh * > ParMeshes(ref_levels+1);
std::vector<HypreParMatrix*> P(ref_levels);
for (int i = 0; i < ref_levels; i++)
{
ParMeshes[i] =new ParMesh(*pmesh);
fespaces[i] = new ParFiniteElementSpace(*fespace, *ParMeshes[i]);
pmesh->UniformRefinement();
// Update fespace
fespace->Update();
OperatorHandle Tr(Operator::Hypre_ParCSR);
fespace->GetTrueTransferOperator(*fespaces[i], Tr);
Tr.SetOperatorOwner(false);
Tr.Get(P[i]);
}
fespaces[ref_levels] = new ParFiniteElementSpace(*fespace);
Array<int> ess_tdof_listE;
Array<int> ess_tdof_listH;
Array<int> ess_bdrE(pmesh->bdr_attributes.Max());
Array<int> ess_bdrH(pmesh->bdr_attributes.Max());
#ifdef COMPLEX_IMPEDANCE
ess_bdrE = 0;
ess_bdrH = 0; // Neumann
#else
ess_bdrE = 1;
ess_bdrH = 0; // Neumann
#endif
fespace->GetEssentialTrueDofs(ess_bdrE, ess_tdof_listE);
fespace->GetEssentialTrueDofs(ess_bdrH, ess_tdof_listH);
#ifdef COMPLEX_IMPEDANCE
Array<int> bdr_attr(pmesh->bdr_attributes.Max());
bdr_attr = 1;
ConstantCoefficient impedance(omega);
RestrictedCoefficient imp_rest(impedance, bdr_attr);
Array<int> block_offsets(5);
Array<int> block_trueOffsets(5);
#else
Array<int> block_offsets(3);
Array<int> block_trueOffsets(3);
#endif
block_offsets[0] = 0;
block_offsets[1] = fespace->GetVSize();
block_offsets[2] = fespace->GetVSize();
#ifdef COMPLEX_IMPEDANCE
block_offsets[3] = fespace->GetVSize();
block_offsets[4] = fespace->GetVSize();
#endif
block_offsets.PartialSum();
block_trueOffsets[0] = 0;
block_trueOffsets[1] = fespace->TrueVSize();
block_trueOffsets[2] = fespace->TrueVSize();
#ifdef COMPLEX_IMPEDANCE
block_trueOffsets[3] = fespace->TrueVSize();
block_trueOffsets[4] = fespace->TrueVSize();
#endif
block_trueOffsets.PartialSum();
// _ _ _ _ _ _
// | | | | | |
// | A00 A01 | | E | |F_E |
// | | | | = | |
// | A10 A11 | | H | |F_G |
// |_ _| |_ _| |_ _|
//
// A00 = (curl E, curl F) + \omega^2 (E,F)
// A01 = - \omega *( (curl E, F) + (E,curl F)
// A10 = - \omega *( (curl H, G) + (H,curl G)
// A11 = (curl H, curl G) + \omega^2 (H,G)
BlockVector x(block_offsets), rhs(block_offsets);
BlockVector trueX(block_trueOffsets), trueRhs(block_trueOffsets);
x = 0.0;
rhs = 0.0;
trueX = 0.0;
trueRhs = 0.0;
VectorFunctionCoefficient Eex(sdim, E_exact);
ConstantCoefficient negOne(-1.0);
VectorFunctionCoefficient mEex(sdim, E_exact, &negOne);
ParGridFunction * E_gf = new ParGridFunction;
ParGridFunction * Exact_gf = new ParGridFunction(fespace);
E_gf->MakeRef(fespace, x.GetBlock(0));
E_gf->ProjectCoefficient(Eex);
Exact_gf->ProjectCoefficient(Eex);
VectorFunctionCoefficient Hex(sdim, H_exact);
ParGridFunction * H_gf = new ParGridFunction;
H_gf->MakeRef(fespace, x.GetBlock(1));
H_gf->ProjectCoefficient(Hex);
ConstantCoefficient one(1.0);
ConstantCoefficient sigma(pow(omega, 2));
ConstantCoefficient neg(-abs(omega));
ConstantCoefficient pos(abs(omega));
MatrixFunctionCoefficient epsilon(dim,epsilon_func);
TransposeMatrixCoefficient epsilonT(epsilon); // transpose of epsilon
MatrixFunctionCoefficient epsilon2(dim,epsilon2_func);
ScalarMatrixProductCoefficient coeff(neg,epsilon);
ScalarMatrixProductCoefficient coeff2(sigma,epsilon2);
// 6. Set up the linear form
VectorFunctionCoefficient f_H(sdim,f_exact_H);
ScalarVectorProductCoefficient sf_H(neg,f_H);
MatVecCoefficient epsT_sf_H(epsilonT, sf_H);
ParLinearForm *b_E = new ParLinearForm;
b_E->Update(fespace, rhs.GetBlock(0), 0);
b_E->AddDomainIntegrator(new VectorFEDomainLFIntegrator(epsT_sf_H));
#ifdef COMPLEX_IMPEDANCE
b_E->AddBoundaryIntegrator(new VectorFEDomainLFIntegrator(
Eex)); // <g_Im, n x Q x n> = <n x E_Re x n, n x Q x n>
ParLinearForm *b_E_Im = new ParLinearForm;
b_E_Im->Update(fespace, rhs.GetBlock(2), 0);
b_E_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(
Hex)); // -<g_Re, n x Q x n> = <n x H_Re, n x Q x n>
b_E_Im->Assemble();
#endif
b_E->Assemble();
ParLinearForm *b_H = new ParLinearForm;
b_H->Update(fespace, rhs.GetBlock(1), 0);
b_H->AddDomainIntegrator(new VectorFEDomainLFCurlIntegrator(f_H));
#ifdef COMPLEX_IMPEDANCE
b_H->AddBoundaryIntegrator(new VectorFEDomainLFIntegrator(
Hex)); // -<g_Re, n x R> = <n x H_Re, n x R>
ParLinearForm *b_H_Im = new ParLinearForm;
b_H_Im->Update(fespace, rhs.GetBlock(3), 0);
b_H_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(
mEex)); // -<g_Im, n x R> = -<n x E_Re x n, n x R>
b_H_Im->Assemble();
#endif
b_H->Assemble();
#ifdef COMPLEX_IMPEDANCE
fespace->GetProlongationMatrix()->MultTranspose(rhs.GetBlock(2),
trueRhs.GetBlock(2));
fespace->GetProlongationMatrix()->MultTranspose(rhs.GetBlock(3),
trueRhs.GetBlock(3));
#endif
// 7. Bilinear form a(.,.) on the finite element space
ParBilinearForm *a_EE = new ParBilinearForm(fespace);
a_EE->AddDomainIntegrator(new CurlCurlIntegrator(one));
a_EE->AddDomainIntegrator(new VectorFEMassIntegrator(coeff2));
#ifdef COMPLEX_IMPEDANCE
a_EE->AddBoundaryIntegrator(new VectorFEMassIntegrator());
//a_EE->AddBoundaryIntegrator(new VectorFEMassIntegrator(imp_rest));
//a_EE->AddBoundaryIntegrator(new BoundaryMassIntegrator(imp_rest));
//a_EE->AddBdrFaceIntegrator(new BoundaryMassIntegrator(imp_rest));
#endif
a_EE->Assemble();
a_EE->Finalize();
HypreParMatrix *A_EE = new HypreParMatrix;
a_EE->FormLinearSystem(ess_tdof_listE, x.GetBlock(0), rhs.GetBlock(0), *A_EE,
trueX.GetBlock(0), trueRhs.GetBlock(0));
ParBilinearForm *a_HH = new ParBilinearForm(fespace);
a_HH->AddDomainIntegrator(new CurlCurlIntegrator(one)); // one is the coeff
a_HH->AddDomainIntegrator(new VectorFEMassIntegrator(sigma));
#ifdef COMPLEX_IMPEDANCE
a_HH->AddBoundaryIntegrator(new VectorFEMassIntegrator());
//a_HH->AddBoundaryIntegrator(new VectorFEMassIntegrator(imp_rest));
//a_HH->AddBoundaryIntegrator(new BoundaryMassIntegrator(imp_rest));
#endif
a_HH->Assemble();
a_HH->Finalize();
HypreParMatrix *A_HH = new HypreParMatrix;
a_HH->FormLinearSystem(ess_tdof_listH, x.GetBlock(1), rhs.GetBlock(1), *A_HH,
trueX.GetBlock(1), trueRhs.GetBlock(1));
ParMixedBilinearForm *a_HE = new ParMixedBilinearForm(fespace,fespace);
a_HE->AddDomainIntegrator(new MixedVectorCurlIntegrator(neg));
a_HE->AddDomainIntegrator(new MixedVectorWeakCurlIntegrator(coeff));
a_HE->Assemble();
a_HE->Finalize();
HypreParMatrix *A_HE = new HypreParMatrix;
a_HE->FormColLinearSystem(ess_tdof_listE,x.GetBlock(0),rhs.GetBlock(1),*A_HE,
trueX.GetBlock(0),trueRhs.GetBlock(1));
HypreParMatrix *A_EH = A_HE->Transpose();
#ifdef COMPLEX_IMPEDANCE
ParBilinearForm *a_EH_Im = new ParBilinearForm(fespace);
//a_EH_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentIntegrator(imp_rest));
//a_EH_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentIntegrator(impedance));
//a_EH_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentIntegrator(omega));
a_EH_Im->AddBoundaryIntegrator(new VectorFEBoundaryTangentIntegrator());
a_EH_Im->Assemble();
a_EH_Im->Finalize();
OperatorHandle A_EH_Im_ptr;
a_EH_Im->FormSystemMatrix(ess_tdof_listE,
A_EH_Im_ptr); // empty ess_tdof_list for impedance
HypreParMatrix *A_EH_Im = A_EH_Im_ptr.As<HypreParMatrix>();
HypreParMatrix *A_HE_Im = A_EH_Im->Transpose();
#endif
BlockOperator *LS_Maxwellop = new BlockOperator(block_trueOffsets);
#ifdef COMPLEX_IMPEDANCE
const int numBlocks = 4;
#else
const int numBlocks = 2;
#endif
LS_Maxwellop->SetBlock(0, 0, A_EE);
LS_Maxwellop->SetBlock(0, 1, A_EH);
LS_Maxwellop->SetBlock(1, 0, A_HE);
LS_Maxwellop->SetBlock(1, 1, A_HH);
#ifdef COMPLEX_IMPEDANCE
LS_Maxwellop->SetBlock(0, 3, A_EH_Im);
LS_Maxwellop->SetBlock(1, 2, A_HE_Im, -1.0);
LS_Maxwellop->SetBlock(2, 1, A_EH_Im, -1.0);
LS_Maxwellop->SetBlock(3, 0, A_HE_Im);
LS_Maxwellop->SetBlock(2, 2, A_EE);
LS_Maxwellop->SetBlock(2, 3, A_EH);
LS_Maxwellop->SetBlock(3, 2, A_HE);
LS_Maxwellop->SetBlock(3, 3, A_HH);
#endif
if (myid == 0)
{
cout << "Size of fine grid system: "
<< 2.0 * A_EE->GetGlobalNumRows() << " x " << 2.0* A_EE->GetGlobalNumCols() <<
endl;
}
// Set up the preconditioner
Array2D<HypreParMatrix*> blockA(numBlocks, numBlocks);
Array2D<double> blockAcoef(numBlocks, numBlocks);
for (int i=0; i<numBlocks; ++i)
{
for (int j=0; j<numBlocks; ++j)
{
if (LS_Maxwellop->IsZeroBlock(i,j) == 0)
{
blockA(i,j) = static_cast<HypreParMatrix *>(&LS_Maxwellop->GetBlock(i,j));
blockAcoef(i,j) = LS_Maxwellop->GetBlockCoef(i,j);
}
else
{
blockA(i,j) = NULL;
blockAcoef(i,j) = 1.0;
}
}
}
// // double nnz = A_HH->NNZ();
// // double ndof = A_HH->GetGlobalNumRows();
// // double est_mem_b = nnz*12.0 + (ndof+1.0)*4;
// // double gb = est_mem_b*4.0/pow(1024.0,3);
// // mfem::out << "Estimated memory taken by the global matrix: " << gb << endl;
int maxit(2000);
double rtol(1.e-8);
double atol(1.e-12);
// trueX = 0.0;
CGSolver pcg(MPI_COMM_WORLD);
pcg.SetAbsTol(atol);
pcg.SetRelTol(rtol);
pcg.SetMaxIter(maxit);
pcg.SetOperator(*LS_Maxwellop);
pcg.SetPrintLevel(1);
chrono.Clear();
chrono.Start();
BlockMGSolver * precMG = new BlockMGSolver(LS_Maxwellop->Height(),
LS_Maxwellop->Width(), blockA, blockAcoef, P);
//precMG->SetTheta(0.5);
// // int lv_coarse = min(ref_levels,ref_levels-1);
// // int levels = ref_levels - lv_coarse;
// // BlkParSchwarzSmoother * precAS = new BlkParSchwarzSmoother(fespaces[lv_coarse]->GetParMesh(),levels,fespaces[ref_levels],LS_Maxwellop);
chrono.Stop();
if (myid == 0)
{
cout << "MG Setup time: " << chrono.RealTime() << endl;
}
// Randomize H RHS
//trueRhs = 1.0;
/*
for (int i=block_trueOffsets[1]; i<block_trueOffsets[2]; ++i)
trueRhs[i] = i % 53;
*/
chrono.Clear();
chrono.Start();
pcg.SetPreconditioner(*precMG);
// // pcg.SetPreconditioner(*precAS);
pcg.Mult(trueRhs, trueX);
chrono.Stop();
delete precMG;
// // delete precAS;
// // trueX = 0.0;
// // invA->Mult(trueRhs,trueX);
if (myid == 0)
{
cout << "MG Solution time time: " << chrono.RealTime() << endl;
}
// // cin.get();
// // if(myid == 0)
// // cout << "MG prec Solution time: " << chrono.RealTime() << endl;
// // chrono.Clear();
// // chrono.Start();
// // Block_AMSSolver * precAMS = new Block_AMSSolver(block_trueOffsets,fespaces);
// // precAMS->SetSmootherType(Block_AMS::BlkSmootherType::SCHWARZ);
// // precAMS->SetSmootherType(Block_AMS::BlkSmootherType::HYPRE);
// // precAMS->SetOperator(LS_Maxwellop);
// // precAMS->SetTheta(1.0/5.0);
// // // 0-Smoother, 1-Grad, 2,3,4-Pix,Piy,Piz
// // precAMS->SetCycleType("023414320");
// // precAMS->SetNumberofCycles(1);
// // chrono.Stop();
// // if(myid == 0)
// // cout << "BlkAMS Setup time: " << chrono.RealTime() << endl;
// // // resolve with block AMS
// // trueX = 0;
// // chrono.Clear();
// // chrono.Start();
// // pcg.SetPreconditioner(*precAMS);
// // pcg.Mult(trueRhs, trueX);
// // chrono.Stop();
// // delete precAMS;
// // if(myid == 0)
// // cout << "BlockAMS Solution time: " << chrono.RealTime() << endl;
a_EE->RecoverFEMSolution(trueX.GetBlock(0), *b_E, *E_gf);
a_HH->RecoverFEMSolution(trueX.GetBlock(1), *b_H, *H_gf);
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 norm_E = ComputeGlobalLpNorm(2, Eex, *pmesh, irs);
double Error_H = H_gf->ComputeL2Error(Hex, irs);
double norm_H = ComputeGlobalLpNorm(2, Hex , *pmesh, irs);
if (myid == 0)
{
cout << "|| E_h - E || = " << Error_E << "\n";
cout << "|| E_h - E ||/||E|| = " << Error_E/norm_E << "\n";
cout << "|| H_h - H || = " << Error_H << "\n";
cout << "|| H_h - H ||/||H|| = " << Error_H/norm_H << "\n";
cout << "Total error = " << setprecision(15) << sqrt(Error_H*Error_H
+Error_E*Error_E) << "\n";
}
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
E_gf->Save(sol_ofs);
}
//ParGridFunction ExactE(fespace);
/*
if (visualization)
{
// 8. Connect to GLVis.
char vishost[] = "localhost";
int visport = 19916;
// socketstream E_sock(vishost, visport);
// E_sock << "parallel " << num_procs << " " << myid << "\n";
// E_sock.precision(8);
// E_sock << "solution\n" << *pmesh << *E_gf << "window_title 'Electric field'" << endl;
socketstream Exact_sock(vishost, visport);
Exact_sock << "parallel " << num_procs << " " << myid << "\n";
Exact_sock.precision(8);
Exact_sock << "solution\n" << *pmesh << *Exact_gf << "window_title 'Electric field'" << endl;
// MPI_Barrier(pmesh->GetComm());
// socketstream Eex_sock(vishost, visport);
// Eex_sock << "parallel " << num_procs << " " << myid << "\n";
// Eex_sock.precision(8);
// Eex_sock << "solution\n" << *pmesh << *Exact_gf << "window_title 'Exact Electric field'" << endl;
}
*/
// delete A_EE;
// delete A_HE;
// delete A_EH;
// delete A_HH;
// delete LS_Maxwellop;
// delete a_EE;
// delete a_HE;
// delete a_HH;
// delete b_E;
// delete b_H;
delete E_gf;
delete Exact_gf;
for (auto p: ParMeshes) { delete p; }
for (auto p: fespaces) { delete p; }
for (auto p: P) { delete p; }
ParMeshes.clear();
fespaces.clear();
P.clear();
delete fec;
delete fespace;
delete pmesh;
// cout << "Freed memory: " << endl;
// cin.get();
MPI_Finalize();
return 0;
}
//define exact solution
void E_exact(const Vector &x, Vector &E)
{
double curlE[3], curl2E[3];
get_maxwell_solution(x, E, curlE, curl2E);
}
void H_exact(const Vector &x, Vector &H)
{
double E[3], curlE[3], curl2E[3];
get_maxwell_solution(x, E, curlE, curl2E);
for (int i = 0; i<3; i++) { H(i) = curlE[i]/omega; }
}
void f_exact_H(const Vector &x, Vector &f)
{
// curl H - omega E = f
// = curl (curl E / omega) - omega E
f = 0.0;
if (sol !=4)
{
double E[3], curlE[3], curl2E[3];
get_maxwell_solution(x, E, curlE, curl2E);
f(0) = curl2E[0] / omega - omega * E[0];
f(1) = curl2E[1] / omega - omega * E[1];
f(2) = curl2E[2] / omega - omega * E[2];
}
}
void get_maxwell_solution(const Vector &X, double E[], double curlE[],
double curl2E[])
{
double x = X[0];
double y = X[1];
double z = X[2];
if (sol ==-1)
{
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] = -(x-1.0) * x * (y*(2.0*z-3.0)+1.0);
curlE[1] = -2.0*(y-1.0)*y*(x-z);
curlE[2] = (z-1)*z*(1.0+y*(2.0*x-3.0));
curl2E[0] = 2.0 * y * (1.0 - y) - (2.0 * x - 3.0) * z * (1 - z);
curl2E[1] = 2.0 * y * (x * (1.0 - x) + (1.0 - z) * z);
curl2E[2] = 2.0 * y * (1.0 - y) + x * (3.0 - 2.0 * z) * (1.0 - x);
}
else if (sol == 0) // polynomial
{
// Polynomial vanishing on the boundary
E[0] = y * z * (1.0 - y) * (1.0 - z);
E[1] = (1.0 - x) * x * y * (1.0 - z) * z;
E[2] = (1.0 - x) * x * (1.0 - y) * y;
//
curlE[0] = -(-1.0 + x) * x * (1.0 + y * (-3.0 + 2.0 * z));
curlE[1] = -2.0 * (-1.0 + y) * y * (x - z);
curlE[2] = (1.0 + (-3.0 + 2.0 * x) * y) * (-1.0 + z) * z;
curl2E[0] = -2.0 * (-1.0 + y) * y + (-3.0 + 2.0 * x) * (-1.0 + z) * z;
curl2E[1] = -2.0 * y * (-x + x * x + (-1.0 + z) * z);
curl2E[2] = -2.0 * (-1.0 + y) * y + (-1.0 + x) * x * (-3.0 + 2.0 * z);
}
else if (sol == 1) // sinusoidal
{
E[0] = sin(omega * y);
E[1] = sin(omega * z);
E[2] = sin(omega * x);
curlE[0] = -omega * cos(omega * z);
curlE[1] = -omega * cos(omega * x);
curlE[2] = -omega * cos(omega * y);
curl2E[0] = omega * omega * E[0];
curl2E[1] = omega * omega * E[1];
curl2E[2] = omega * omega * E[2];
}
else if (sol == 2) // point source
{
// shift to avoid singularity
double x0 = x + 0.1;
double x1 = y + 0.1;
double x2 = z + 0.1;
//
double r = sqrt(x0 * x0 + x1 * x1 + x2 * x2);
E[0] = cos(omega * r);
E[1] = 0.0;
E[2] = 0.0;
double r_x = x0 / r;
double r_y = x1 / r;
double r_z = x2 / r;
double r_xy = -(r_x / r) * r_y;
double r_xz = -(r_x / r) * r_z;
double r_yx = r_xy;
double r_yy = (1.0 / r) * (1.0 - r_y * r_y);
double r_zx = r_xz;
double r_zz = (1.0 / r) * (1.0 - r_z * r_z);
curlE[0] = 0.0;
curlE[1] = -omega * r_z * sin(omega * r);
curlE[2] = omega * r_y * sin(omega * r);
curl2E[0] = omega * ((r_yy + r_zz) * sin(omega * r) +
(omega * r_y * r_y + omega * r_z * r_z) * cos(omega * r));
curl2E[1] = -omega * (r_yx * sin(omega * r) + omega * r_y * r_x * cos(
omega * r));
curl2E[2] = -omega * (r_zx * sin(omega * r) + omega * r_z * r_x * cos(
omega * r));
}
else if (sol == 3) // plane wave
{
double coeff = omega / sqrt(3.0);
E[0] = cos(coeff * (x + y + z));
E[1] = 0.0;
E[2] = 0.0;
curlE[0] = 0.0;
curlE[1] = -coeff * sin(coeff * (x + y + z));
curlE[2] = coeff * sin(coeff * (x + y + z));
curl2E[0] = 2.0 * coeff * coeff * E[0];
curl2E[1] = -coeff * coeff * E[0];
curl2E[2] = -coeff * coeff * E[0];
}
else if (sol == -1)
{
E[0] = cos(omega * y);
E[1] = 0.0;
curlE[0] = 0.0;
curlE[1] = 0.0;
curlE[2] = -omega * sin(omega * y);
curl2E[0] = omega*omega * cos(omega*y);
curl2E[1] = 0.0;
curl2E[2] = 0.0;
}
else if (sol == 4) // Airy function
{
E[0] = 0;
E[1] = 0;
// double b = -pow(omega/4.0,2.0/3.0)*(4.0*x(0)-1.0);
double b = -pow(omega/4.0,2.0/3.0)*(4.0*x-1.0);
//E[2] = boost::math::airy_ai(b);
E[2] = gsl_sf_airy_Ai(b, GSL_PREC_DOUBLE);
curlE[0] = 0.0;
curlE[1] = 4.0 * pow(omega/4.0,2.0/3.0) * gsl_sf_airy_Ai_deriv(b,
GSL_PREC_DOUBLE);
curlE[2] = 0.0;
// not used
curl2E[0] = 0.0;
curl2E[1] = 0.0;
curl2E[2] = 0.0;
}
}
void epsilon_func(const Vector &x, DenseMatrix &M)
{
M.SetSize(3);
M = 0.0;
M(0,0) = 1.0;
M(1,1) = 1.0;
if (sol != 4)
{
M(2,2) = 1.0;
}
else
{
M(2,2) = 4.0*x(0)-1.0;
// M(2,2) = 2.0;
}
}
void epsilon2_func(const Vector &x, DenseMatrix &M)
{
M.SetSize(3);
M = 0.0;
M(0,0) = 1.0;
M(1,1) = 1.0;
if (sol != 4)
{
M(2,2) = 1.0;
}
else
{
M(2,2) = (4.0*x(0)-1.0) * (4.0*x(0)-1.0);
// M(2,2) = 4.0;
}
}
+915
View File
@@ -0,0 +1,915 @@
// MFEM Example 3 - Parallel Version
//
// Compile with: make ex3p
//
// Sample runs: mpirun -np 4 ex3p -m ../data/star.mesh
// mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh
// mpirun -np 4 ex3p -m ../data/escher.mesh
// mpirun -np 4 ex3p -m ../data/escher.mesh -o 2
// mpirun -np 4 ex3p -m ../data/fichera.mesh
// mpirun -np 4 ex3p -m ../data/fichera-q2.vtk
// mpirun -np 4 ex3p -m ../data/fichera-q3.mesh
// mpirun -np 4 ex3p -m ../data/square-disc-nurbs.mesh
// mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
// mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
// mpirun -np 4 ex3p -m ../data/amr-hex.mesh
// mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
// mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
// mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
//
// Description: This example code solves a simple electromagnetic diffusion
// problem corresponding to the second order definite Maxwell
// equation curl curl E + E = f with boundary condition
// E x n = <given tangential field>. Here, we use a given exact
// solution E and compute the corresponding r.h.s. f.
// We discretize with Nedelec finite elements in 2D or 3D.
//
// The example demonstrates the use of H(curl) finite element
// spaces with the curl-curl and the (vector finite element) mass
// bilinear form, as well as the computation of discretization
// error when the exact solution is known. Static condensation is
// also illustrated.
//
// We recommend viewing examples 1-2 before viewing this example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Exact solution, E, and r.h.s., f. See below for implementation.
void E_exact(const Vector &, Vector &);
void f_exact(const Vector &, Vector &);
double freq = 1.0, kappa;
int dim;
#define SIGMAVAL -250.0
//#define FORM_DEFINITE
//#define SOLVE_A2
//#define ITER_A2
//#define USE_CSL
//#define USE_HELMHOLTZ
//#define TEST_MULTIPLE_SP
#ifdef USE_HELMHOLTZ
void GetHelmholtzMatrix(ParMesh *pmesh, const int dir, HypreParMatrix *A)
{
const int order = 1;
FiniteElementCollection *fec;
fec = new H1_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
Array<int> ess_tdof_list;
const bool homogeneousBCeverywhere = false;
if (homogeneousBCeverywhere)
{
if (pmesh->bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
}
else
{
// Set boundary conditions, depending on dir.
MFEM_VERIFY(dim == 3, "");
for (int i=0; i<pmesh->GetNBE(); ++i)
{
Element *elem = pmesh->GetBdrElement(i);
MFEM_VERIFY(elem->GetNVertices() >= 3, "");
const int *vertices = elem->GetVertices();
double *v[3];
for (int j=0; j<3; ++j)
{
v[j] = pmesh->GetVertex(vertices[j]);
}
double u[3];
double w[3];
for (int j=0; j<3; ++j)
{
u[j] = v[1][j] - v[0][j]; // An edge tangent
w[j] = v[2][j] - v[1][j]; // Another edge tangent, not parallel to u.
}
double n[3]; // normal vector, taken as the cross product u x v
n[0] = (u[1]*w[2]) - (u[2]*w[1]);
n[1] = (u[2]*w[0]) - (u[0]*w[2]);
n[2] = (u[0]*w[1]) - (u[1]*w[0]);
double t = sqrt((n[0]*n[0]) + (n[1]*n[1]) + (n[2]*n[2]));
int d = -1;
for (int j=0; j<3; ++j)
{
n[j] /= t; // normalize
if (fabs(fabs(n[j]) - 1.0) < 1.0e-8)
{
d = j;
}
}
MFEM_VERIFY(d >= 0, "");
if (d != dir) // face has essential BC at all DOF's.
{
elem->SetAttribute(1);
}
else
{
elem->SetAttribute(0);
}
}
Array<int> ess_bdr(2);
ess_bdr = 0;
ess_bdr[1] = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
ParBilinearForm *a = new ParBilinearForm(fespace);
ConstantCoefficient one(1.0);
ConstantCoefficient neg(SIGMAVAL);
a->AddDomainIntegrator(new DiffusionIntegrator(one));
a->AddDomainIntegrator(new MassIntegrator(neg));
ParLinearForm *b = new ParLinearForm(fespace);
ConstantCoefficient zero(0.0);
b->AddDomainIntegrator(new DomainLFIntegrator(zero));
b->Assemble();
bool static_cond = false;
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
ParGridFunction x(fespace);
x = 0.0;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, *A, X, B);
}
#endif
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
const char *mesh_file = "../data/beam-tet.mesh";
int order = 2;
bool static_cond = false;
bool visualization = 1;
#ifdef MFEM_USE_STRUMPACK
bool use_strumpack = false;
#endif
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(&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(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
#ifdef MFEM_USE_STRUMPACK
args.AddOption(&use_strumpack, "-strumpack", "--strumpack-solver",
"-no-strumpack", "--no-strumpack-solver",
"Use STRUMPACK's double complex linear solver.");
#endif
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
kappa = freq * M_PI;
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
// 4. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 1,000 elements.
{
int ref_levels =
(int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
//(int)floor(log(100000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
// meshes need to be reoriented before we can define high-order Nedelec
// spaces on them.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
}
pmesh->ReorientTetMesh();
{
double minsize = pmesh->GetElementSize(0);
double maxsize = minsize;
for (int i=1; i<pmesh->GetNE(); ++i)
{
const double size_i = pmesh->GetElementSize(i);
minsize = std::min(minsize, size_i);
maxsize = std::max(maxsize, size_i);
}
cout << myid << ": Element size range: (" << minsize << ", " << maxsize << ")"
<< endl;
}
// 6. Define a parallel finite element space on the parallel mesh. Here we
// use the Nedelec finite elements of the specified order.
FiniteElementCollection *fec = new ND_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
long globalNE = pmesh->GetGlobalNE();
if (myid == 0)
{
cout << "Number of mesh elements: " << globalNE << endl;
cout << "Number of finite element unknowns: " << size << endl;
cout << "Root local number of finite element unknowns: " << fespace->TrueVSize()
<< endl;
}
// 7. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
if (pmesh->bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 8. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (f,phi_i) where f is given by the function f_exact and phi_i are the
// basis functions in the finite element fespace.
VectorFunctionCoefficient f(sdim, f_exact);
ParLinearForm *b = new ParLinearForm(fespace);
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
b->Assemble();
// 9. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x by projecting the exact
// solution. Note that only values from the boundary edges will be used
// when eliminating the non-homogeneous boundary condition to modify the
// r.h.s. vector b.
ParGridFunction x(fespace);
VectorFunctionCoefficient E(sdim, E_exact);
x.ProjectCoefficient(E);
// 10. Set up the parallel bilinear form corresponding to the EM diffusion
// operator curl muinv curl + sigma I, by adding the curl-curl and the
// mass domain integrators.
Coefficient *muinv = new ConstantCoefficient(1.0);
Coefficient *sigma = new ConstantCoefficient(SIGMAVAL);
Coefficient *sigmaAbs = new ConstantCoefficient(fabs(SIGMAVAL));
ParBilinearForm *a = new ParBilinearForm(fespace);
a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
//cout << myid << ": NBE " << pmesh->GetNBE() << endl;
#ifdef FORM_DEFINITE
ParBilinearForm *adef = new ParBilinearForm(fespace);
adef->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
adef->AddDomainIntegrator(new VectorFEMassIntegrator(*sigmaAbs));
if (static_cond) { adef->EnableStaticCondensation(); }
adef->Assemble();
HypreParMatrix Adef;
Vector Bdef, Xdef;
adef->FormLinearSystem(ess_tdof_list, x, *b, Adef, Xdef, Bdef);
#endif
#ifdef USE_CSL
Vector Bdef, Xdef;
ParBilinearForm *Mform = new ParBilinearForm(fespace);
Mform->AddDomainIntegrator(new VectorFEMassIntegrator(*sigmaAbs));
Mform->Assemble();
// Mform->Finalize();
HypreParMatrix Mmat, Smat, Mcopy;
Mform->FormLinearSystem(ess_tdof_list, x, *b, Mmat, Xdef, Bdef);
Mform->FormLinearSystem(ess_tdof_list, x, *b, Mcopy, Xdef,
Bdef); // There must be a better way than creating two identical matrices.
ParBilinearForm *Sform = new ParBilinearForm(fespace);
Sform->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
Sform->Assemble();
Sform->FormLinearSystem(ess_tdof_list, x, *b, Smat, Xdef, Bdef);
ParBilinearForm *agrad = new ParBilinearForm(fespace);
//agrad->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
agrad->AddDomainIntegrator(new VectorFEMassIntegrator(*muinv));
if (static_cond) { agrad->EnableStaticCondensation(); }
agrad->Assemble();
HypreParMatrix Agrad;
agrad->FormLinearSystem(ess_tdof_list, x, *b, Agrad, Xdef, Bdef);
#endif
#ifdef ITER_A2
Vector Bdef, Xdef;
ParBilinearForm *Mform = new ParBilinearForm(fespace);
Mform->AddDomainIntegrator(new VectorFEMassIntegrator(*sigmaAbs));
Mform->Assemble();
Mform->Finalize();
HypreParMatrix Mmat, Mcopy;
Mform->FormLinearSystem(ess_tdof_list, x, *b, Mmat, Xdef, Bdef);
Mform->FormLinearSystem(ess_tdof_list, x, *b, Mcopy, Xdef,
Bdef); // There must be a better way to implement M^2.
/*
HypreParMatrix *Mmat = Mform->ParallelAssemble();
HypreParMatrix *Mcopy = Mform->ParallelAssemble(); // There must be a better way to implement M^2.
*/
ParBilinearForm *Sform = new ParBilinearForm(fespace);
Sform->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
Sform->Assemble();
HypreParMatrix Smat, Scopy;
Sform->FormLinearSystem(ess_tdof_list, x, *b, Smat, Xdef, Bdef);
Sform->FormLinearSystem(ess_tdof_list, x, *b, Scopy, Xdef, Bdef);
#endif
// 11. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
HypreParMatrix A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
#ifdef SOLVE_A2
HypreParMatrix Acopy;
{
Vector Bdum, Xdum;
a->FormLinearSystem(ess_tdof_list, x, *b, Acopy, Xdum, Bdum);
}
#endif
if (myid == 0)
{
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
}
StopWatch chrono;
chrono.Clear();
chrono.Start();
//A.Print("maxwell1000_2");
#ifdef MFEM_USE_STRUMPACK
if (use_strumpack)
{
const bool fullDirect = true;
#ifdef USE_CSL
const double beta1 = 1.0;
const double beta2 = 0.5;
Mmat *= -beta1;
// HypreParMatrix *cslRe = Add(1.0, Smat, -beta1, Mmat);
HypreParMatrix * cslRe = ParAdd(&Smat, &Mmat);
Mcopy *= beta2;
//ComplexHypreParMatrix chpm(cslRe, &Mcopy, false, false);
ComplexHypreParMatrix chpm(&A, &Mcopy, false,
false); // For the case beta1 = 1.
HypreParMatrix *cSysMat = chpm.GetSystemMatrix();
Array<int> block_offsets(3); // number of variables + 1
block_offsets[0] = 0;
block_offsets[1] = fespace->GetVSize();
block_offsets[2] = fespace->GetVSize();
block_offsets.PartialSum();
Array<int> block_trueOffsets(3); // number of variables + 1
block_trueOffsets[0] = 0;
block_trueOffsets[1] = fespace->TrueVSize();
block_trueOffsets[2] = fespace->TrueVSize();
block_trueOffsets.PartialSum();
//cout << myid << ": V size " << fespace->GetVSize() << ", true " << fespace->TrueVSize() << ", global true " << size << ", B size "
//<< B.Size() << ", X size " << X.Size() << endl;
// Note that B is of true size.
BlockVector trueY(block_trueOffsets), trueX(block_trueOffsets),
trueRhs(block_trueOffsets);
trueRhs.GetBlock(0) = B;
trueRhs.GetBlock(1) = 0.0;
Operator * Arow = new STRUMPACKRowLocMatrix(*cSysMat);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
// strumpack->SetSymmetricPattern(true);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
#endif
if (fullDirect)
{
#ifdef USE_CSL
//Solver * precond = strumpack;
// strumpack->Mult(B, X);
BlockOperator blockDiagA(block_trueOffsets);
for (int i=0; i<2; ++i)
{
blockDiagA.SetDiagonalBlock(i, &A);
}
ParFiniteElementSpace *prec_fespace =
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
HypreSolver *amsgrad = new HypreAMS(Agrad, prec_fespace);
#ifdef HYPRE_DYLAN
{
Vector Xtmp(X);
amsgrad->Mult(B,
Xtmp); // Just a hack to get ams to run its setup function. There should be a better way.
}
HypreAMSG *amsg = new HypreAMSG((HypreAMS*) amsgrad, argc, argv);
BlockOperator blockDiagP(block_trueOffsets);
for (int i=0; i<2; ++i)
{
blockDiagP.SetDiagonalBlock(i, amsg);
}
TripleProductOperator strumpackProj(&blockDiagP, strumpack, &blockDiagP, false,
false, false);
ProductOperator prod(&blockDiagA, &strumpackProj, false, false);
#else
ProductOperator prod(&blockDiagA, strumpack, false, false);
#endif
GMRESSolver *gmres = new GMRESSolver(fespace->GetComm());
//BiCGSTABSolver *gmres = new BiCGSTABSolver(fespace->GetComm());
gmres->SetOperator(prod);
gmres->SetRelTol(1e-12);
gmres->SetMaxIter(1000);
gmres->SetPrintLevel(1);
gmres->Mult(trueRhs, trueY);
strumpack->Mult(trueY, trueX);
X = trueX.GetBlock(0);
double xim2 = trueX.GetBlock(1).Norml2();
xim2 *= xim2;
double sumxim2 = 0.0;
MPI_Allreduce(&xim2, &sumxim2, 1, MPI_DOUBLE, MPI_SUM, fespace->GetComm());
if (myid == 0)
{
cout << myid << ": norm of Xim " << trueX.GetBlock(1).Norml2() << ", global " <<
sqrt(sumxim2) << endl;
}
delete gmres;
delete strumpack;
delete Arow;
#else
cout << "Solving with STRUMPACK" << endl;
#ifdef TEST_MULTIPLE_SP
const int Ns = 2;
std::vector<Operator*> Arows(Ns);
std::vector<STRUMPACKSolver*> strumpacks(Ns);
//Operator * Arow = new STRUMPACKRowLocMatrix(A);
for (int m=0; m<Ns; ++m)
{
Arows[m] = new STRUMPACKRowLocMatrix(A);
//STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
strumpacks[m] = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
strumpacks[m]->SetPrintFactorStatistics(true);
strumpacks[m]->SetPrintSolveStatistics(false);
strumpacks[m]->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpacks[m]->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
// strumpack->SetSymmetricPattern(true);
strumpacks[m]->SetOperator(*Arows[m]);
strumpacks[m]->SetFromCommandLine();
//Solver * precond = strumpack;
strumpacks[m]->Mult(B, X);
//delete strumpack;
//delete Arow;
}
#else
Operator * Arow = new STRUMPACKRowLocMatrix(A);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
// strumpack->SetMC64Job(strumpack::MC64Job::NONE);
// strumpack->SetSymmetricPattern(true);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
//Solver * precond = strumpack;
cout << "Solving with strumpack one time" << endl;
strumpack->Mult(B, X);
delete strumpack;
delete Arow;
#endif
#endif
}
else
{
ParFiniteElementSpace *prec_fespace =
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
HypreSolver *ams = new HypreAMS(A, prec_fespace);
#ifdef HYPRE_DYLAN
{
Vector Xtmp(X);
ams->Mult(B,
Xtmp); // Just a hack to get ams to run its setup function. There should be a better way.
}
HypreParMatrix H[3];
#ifdef USE_HELMHOLTZ
for (int i=0; i<3; ++i)
{
GetHelmholtzMatrix(pmesh, i, &(H[i]));
}
#endif
#ifdef USE_CSL
HypreIAMS *iams = new HypreIAMS(A, H, strumpack, &trueX, &trueY,
(HypreAMS*) ams, argc, argv);
#else
HypreIAMS *iams = new HypreIAMS(A, H, strumpack, NULL, NULL, (HypreAMS*) ams,
argc, argv);
#endif
GMRESSolver *gmres = new GMRESSolver(fespace->GetComm());
//FGMRESSolver *gmres = new FGMRESSolver(fespace->GetComm());
//BiCGSTABSolver *gmres = new BiCGSTABSolver(fespace->GetComm());
//MINRESSolver *gmres = new MINRESSolver(fespace->GetComm());
gmres->SetOperator(A);
gmres->SetRelTol(1e-16);
gmres->SetMaxIter(1000);
gmres->SetPrintLevel(1);
#ifdef SOLVE_A2
{
StopWatch chronoA2;
chronoA2.Clear();
chronoA2.Start();
HypreParMatrix * A2 = ParMult(&A, &Acopy);
chronoA2.Stop();
cout << "A2 setup time " << chronoA2.RealTime() << endl;
Vector AB(B);
A.Mult(B, AB);
gmres->SetOperator(*A2);
HypreSolver *ams2 = new HypreAMS(A, prec_fespace);
{
Vector Xtmp(X);
ams2->Mult(B,
Xtmp); // Just a hack to get ams to run its setup function. There should be a better way.
}
#ifdef ITER_A2
// Iteratively solve 0.5 (A^2 + S^2 + M^2) u^{k+1} = 0.5 (SM + MS) u^k + Ab
StopWatch chronoIterA2;
chronoIterA2.Clear();
chronoIterA2.Start();
HypreParMatrix * M2 = ParMult(&Mmat, &Mcopy);
HypreParMatrix * S2 = ParMult(&Smat, &Scopy);
HypreParMatrix * MS = ParMult(&Mmat, &Scopy);
HypreParMatrix * SM = ParMult(&Smat, &Mcopy);
HypreParMatrix * Bmat = ParAdd(SM, MS);
(*Bmat) *= 0.5;
// TODO: there must be a better way to form a sum of three matrices. Of course, we could define an operator that does 3 mat-vecs.
//HypreParMatrix * S2M2 = ParAdd(S2, M2);
//HypreParMatrix * iterMat = ParAdd(A2, S2M2);
HypreParMatrix * iterMat = ParAdd(A2, Bmat);
chronoIterA2.Stop();
cout << "Iter A2 setup time " << chronoIterA2.RealTime() << endl;
/*
HypreSolver *ams3 = new HypreAMS(*iterMat, prec_fespace);
{
Vector Xtmp(X);
ams3->Mult(B, Xtmp); // Just a hack to get ams to run its setup function. There should be a better way.
}
*/
/*
// GMRES
gmres->SetOperator(*iterMat);
gmres->SetPreconditioner(*ams2);
*/
//HypreBoomerAMG *amg = new HypreBoomerAMG(*iterMat);
HypreBoomerAMG *amg = new HypreBoomerAMG(*A2);
// PCG
HyprePCG *pcg = new HyprePCG(*iterMat);
//HyprePCG *pcg = new HyprePCG(*A2);
pcg->SetTol(1e-12);
pcg->SetMaxIter(10);
pcg->SetPrintLevel(2);
pcg->SetPreconditioner(*amg);
/*
// Strumpack linear solver
Operator * Arow = new STRUMPACKRowLocMatrix(*iterMat);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
*/
Vector iterRHS(AB);
Vector iterU(AB);
Vector iterU0(AB);
iterU = 0.0;
iterU0 = 0.0;
bool iterate = true;
int numIter = 0;
while (iterate)
{
iterRHS = iterU;
iterRHS.Add(-1.0, iterU0);
cout << "Iteration " << numIter + 1 << ": diff norm " << iterRHS.Norml2() <<
endl;
iterU0 = iterU;
Bmat->Mult(iterU0, iterRHS);
//iterRHS.Add(2.0, AB);
iterRHS.Add(1.0, AB);
//gmres->Mult(iterRHS, iterU);
pcg->Mult(iterRHS, iterU);
//strumpack->Mult(iterRHS, iterU);
numIter++;
if (numIter > 100)
{
iterate = false;
}
}
//delete strumpack;
//delete Arow;
delete pcg;
X = iterU;
#else
//HypreIAMS *iams2 = new HypreIAMS(*A2, (HypreAMS*) ams2, argc, argv);
//gmres->SetPreconditioner(*iams2);
cout << myid << ": Solving" << endl;
gmres->SetPreconditioner(*ams2);
gmres->Mult(AB, X);
cout << myid << ": Solved" << endl;
return 3;
#endif
}
#else
gmres->SetPreconditioner(*iams);
gmres->Mult(B, X);
#endif
#else
HypreGMRES *gmres = new HypreGMRES(A);
gmres->SetTol(1e-12);
gmres->SetMaxIter(100);
gmres->SetPrintLevel(10);
#ifdef FORM_DEFINITE
HypreSolver *amsdef = new HypreAMS(Adef, prec_fespace);
gmres->SetPreconditioner(*amsdef);
#else
gmres->SetPreconditioner(*ams);
#endif
gmres->Mult(B, X);
#endif
delete gmres;
//delete iams;
//delete ams;
}
}
else
#endif
{
// 12. Define and apply a parallel PCG solver for AX=B with the AMS
// preconditioner from hypre.
ParFiniteElementSpace *prec_fespace =
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
HypreSolver *ams = new HypreAMS(A, prec_fespace);
HyprePCG *pcg = new HyprePCG(A);
pcg->SetTol(1e-12);
pcg->SetMaxIter(500);
pcg->SetPrintLevel(2);
pcg->SetPreconditioner(*ams);
pcg->Mult(B, X);
delete pcg;
delete ams;
}
chrono.Stop();
cout << myid << ": Solver time " << chrono.RealTime() << endl;
// 13. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
// 14. Compute and print the L^2 norm of the error.
{
double err = x.ComputeL2Error(E);
Vector zeroVec(3);
zeroVec = 0.0;
VectorConstantCoefficient vzero(zeroVec);
ParGridFunction zerogf(fespace);
zerogf = 0.0;
double normE = zerogf.ComputeL2Error(E);
double normX = x.ComputeL2Error(vzero);
if (myid == 0)
{
cout << "|| E_h - E ||_{L^2} = " << err << endl;
cout << "|| E_h ||_{L^2} = " << normX << endl;
cout << "|| E ||_{L^2} = " << normE << endl;
}
}
// 15. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
x.Save(sol_ofs);
}
// 16. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << *pmesh << x << flush;
}
// 17. Free the used memory.
delete a;
delete sigma;
delete muinv;
delete b;
delete fespace;
delete fec;
delete pmesh;
MPI_Finalize();
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) = (SIGMAVAL + kappa * kappa) * sin(kappa * x(1));
f(1) = (SIGMAVAL + kappa * kappa) * sin(kappa * x(2));
f(2) = (SIGMAVAL + 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; }
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
| | | | | || _|| __/| | | | | |
|_| |_| |_||_| \___||_| |_| |_|
https://mfem.org
http://mfem.org
This directory contains modifications of the example codes that illustrate the
use of MFEM features based on the Ginkgo high-performance linear algebra library
+1 -1
View File
@@ -5,7 +5,7 @@
| | | | | || _|| __/| | | | | |
|_| |_| |_||_| \___||_| |_| |_|
https://mfem.org
http://mfem.org
This directory contains modifications of the example codes that illustrate the
use of MFEM for solving nonlinear constrained optimization problems, including
-11
View File
@@ -48,12 +48,6 @@ endif
ifeq ($(MFEM_USE_GINKGO),YES)
SUBDIRS += ginkgo
endif
ifeq ($(MFEM_USE_AMGX),YES)
SUBDIRS += amgx
endif
ifeq ($(MFEM_USE_SUPERLU),YES)
SUBDIRS += superlu
endif
SUBDIRS_ALL = $(addsuffix /all,$(SUBDIRS))
SUBDIRS_TEST = $(addsuffix /test,$(SUBDIRS))
@@ -125,11 +119,6 @@ ex11p-test-superlu: ex11p
@$(call mfem-test,$<, $(RUN_MPI), SuperLU_DIST example,--superlu)
test-par-YES: ex11p-test-superlu
endif
ifeq ($(MFEM_USE_MKL_CPARDISO),YES)
ex11p-test-cpardiso: ex11p
@$(call mfem-test,$<, $(RUN_MPI), MKL_CPARDISO example,--cpardiso)
test-par-YES: ex11p-test-cpardiso
endif
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
+517
View File
@@ -0,0 +1,517 @@
#pragma once
#include "mfem.hpp"
using namespace std;
namespace mfem
{
STRUMPACKSolver* CreateStrumpackSolver(Operator *Arow, MPI_Comm comm)
{
//STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, comm);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(0, NULL, comm);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
return strumpack;
}
hypre_CSRMatrix* GetHypreParMatrixData(const HypreParMatrix & hypParMat)
{
// First cast the parameter to a hypre_ParCSRMatrix
hypre_ParCSRMatrix * parcsr_op =
(hypre_ParCSRMatrix *)const_cast<HypreParMatrix&>(hypParMat);
MFEM_ASSERT(parcsr_op != NULL,"STRUMPACK: const_cast failed in SetOperator");
// Create the CSRMatrixMPI A_ by borrowing the internal data from a hypre_CSRMatrix.
return hypre_MergeDiagAndOffd(parcsr_op);
}
// Row and column offsets are assumed to be the same, for each process.
// Array offsets stores process-local offsets with respect to the blocks. Process offsets are not included.
HypreParMatrix* CreateHypreParMatrixFromBlocks(MPI_Comm comm,
Array<int> const& offsets, Array2D<HypreParMatrix*> const& blocks,
Array2D<SparseMatrix*> const& blocksSp,
Array2D<double> const& coefficient,
std::vector<std::vector<int> > const& blockProcOffsets,
std::vector<std::vector<int> > const& all_block_num_loc_rows)
{
const int numBlocks = offsets.Size() - 1;
const int num_loc_rows = offsets[numBlocks];
int nprocs, rank;
MPI_Comm_rank(comm, &rank);
MPI_Comm_size(comm, &nprocs);
std::vector<int> all_num_loc_rows(nprocs);
std::vector<int> procOffsets(nprocs);
std::vector<std::vector<int> > procBlockOffsets(nprocs);
MPI_Allgather(&num_loc_rows, 1, MPI_INT, all_num_loc_rows.data(), 1, MPI_INT,
comm);
int first_loc_row = 0;
int glob_nrows = 0;
procOffsets[0] = 0;
for (int i=0; i<nprocs; ++i)
{
glob_nrows += all_num_loc_rows[i];
if (i < rank)
{
first_loc_row += all_num_loc_rows[i];
}
if (i < nprocs-1)
{
procOffsets[i+1] = procOffsets[i] + all_num_loc_rows[i];
}
if (numBlocks > 0)
{
procBlockOffsets[i].resize(numBlocks);
procBlockOffsets[i][0] = 0;
}
for (int j=1; j<numBlocks; ++j)
{
procBlockOffsets[i][j] = procBlockOffsets[i][j-1] + all_block_num_loc_rows[j
-1][i];
}
}
const int glob_ncols = glob_nrows;
std::vector<int> opI(num_loc_rows+1);
std::vector<int> cnt(num_loc_rows);
for (int i=0; i<num_loc_rows; ++i)
{
opI[i] = 0;
cnt[i] = 0;
}
opI[num_loc_rows] = 0;
Array2D<hypre_CSRMatrix*> csr_blocks(numBlocks, numBlocks);
// Loop over all blocks, to determine nnz for each row.
for (int i=0; i<numBlocks; ++i)
{
for (int j=0; j<numBlocks; ++j)
{
if (blocks(i, j) == NULL)
{
csr_blocks(i, j) = NULL;
if (blocksSp(i, j) != NULL)
{
const int nrows = blocksSp(i, j)->Height();
for (int k=0; k<nrows; ++k)
{
const int rowg = offsets[i] + k;
opI[rowg + 1] += blocksSp(i, j)->GetI()[k+1] - blocksSp(i, j)->GetI()[k];
}
}
}
else
{
MFEM_VERIFY(blocksSp(i, j) == NULL, "");
csr_blocks(i, j) = GetHypreParMatrixData(*(blocks(i, j)));
const int nrows = csr_blocks(i, j)->num_rows;
for (int k=0; k<nrows; ++k)
{
const int rowg = offsets[i] + k;
//(*(leftInjection(i, j)))[k]
opI[rowg + 1] += csr_blocks(i, j)->i[k+1] - csr_blocks(i, j)->i[k];
}
}
}
}
// Now opI[i] is nnz for row i-1. Do a partial sum to get offsets.
for (int i=0; i<num_loc_rows; ++i)
{
opI[i+1] += opI[i];
}
const int nnz = opI[num_loc_rows];
std::vector<HYPRE_Int> opJ(nnz);
std::vector<double> data(nnz);
// Loop over all blocks, to set matrix data.
for (int i=0; i<numBlocks; ++i)
{
for (int j=0; j<numBlocks; ++j)
{
if (csr_blocks(i, j) != NULL || blocksSp(i, j) != NULL)
{
const bool useCSR = (csr_blocks(i, j) != NULL);
const int nrows = useCSR ? csr_blocks(i, j)->num_rows : blocksSp(i,
j)->Height();
const double coef = coefficient(i, j);
int *Iarray = useCSR ? csr_blocks(i, j)->i : blocksSp(i, j)->GetI();
//const bool failure = (nrows != offsets[i+1] - offsets[i]);
MFEM_VERIFY(nrows == offsets[i+1] - offsets[i], "");
for (int k=0; k<nrows; ++k)
{
const int rowg = offsets[i] + k; // process-local row
const int nnz_k = Iarray[k+1] - Iarray[k];
const int osk = Iarray[k];
for (int l=0; l<nnz_k; ++l)
{
// Find the column process offset for the block.
const int bcol = useCSR ? csr_blocks(i, j)->j[osk + l] : blocksSp(i,
j)->GetJ()[osk + l];
int bcolproc = 0;
for (int p=1; p<nprocs; ++p)
{
if (blockProcOffsets[j][p] > bcol)
{
bcolproc = p-1;
break;
}
}
if (blockProcOffsets[j][nprocs - 1] <= bcol)
{
bcolproc = nprocs - 1;
}
const int colg = procOffsets[bcolproc] + procBlockOffsets[bcolproc][j] +
(bcol - blockProcOffsets[j][bcolproc]);
if (colg < 0)
{
cout << "BUG, negative global column index" << endl;
}
opJ[opI[rowg] + cnt[rowg]] = colg;
data[opI[rowg] + cnt[rowg]] = useCSR ? coef * csr_blocks(i,
j)->data[osk + l] : coef * blocksSp(i, j)->GetData()[osk + l];
cnt[rowg]++;
}
}
}
}
}
bool cntCheck = true;
for (int i=0; i<num_loc_rows; ++i)
{
if (cnt[i] != opI[i+1] - opI[i])
{
cntCheck = false;
}
}
MFEM_VERIFY(cntCheck, "");
for (int i=0; i<numBlocks; ++i)
{
for (int j=0; j<numBlocks; ++j)
{
if (csr_blocks(i, j) != NULL)
{
hypre_CSRMatrixDestroy(csr_blocks(i, j));
}
}
}
std::vector<HYPRE_Int> rowStarts2(2);
rowStarts2[0] = first_loc_row;
rowStarts2[1] = first_loc_row + all_num_loc_rows[rank];
if (nnz > 0)
{
HYPRE_Int minJ = opJ[0];
HYPRE_Int maxJ = opJ[0];
for (int i=0; i<nnz; ++i)
{
minJ = std::min(minJ, opJ[i]);
maxJ = std::max(maxJ, opJ[i]);
if (opJ[i] >= glob_ncols)
{
cout << "Column indices out of range" << endl;
}
}
}
HypreParMatrix *hmat = new HypreParMatrix(comm, num_loc_rows, glob_nrows,
glob_ncols, (int*) opI.data(), (HYPRE_Int*) opJ.data(), (double*) data.data(),
(HYPRE_Int*) rowStarts2.data(), (HYPRE_Int*) rowStarts2.data());
return hmat;
}
class BlockMGSolver : public Solver
{
private:
/// The linear system matrix
Array2D<HypreParMatrix *>&
Af; // TODO: remove this, as it is used only in the constructor
Array2D<double>&
Acoef; // TODO: remove this, as it is used only in the constructor
vector<Array<int>> Aoffsets;
vector<Array<int>> Poffsets_i;
vector<Array<int>> Poffsets_j;
std::vector<Array2D<HypreParMatrix *>> A;
std::vector<HypreParMatrix *>& P;
std::vector<BlockOperator *> BlkP;
std::vector<BlockOperator *> BlkA;
std::vector<BlockOperator *> S;
HypreParMatrix * Ac;
int numGrids, numBlocks;
STRUMPACKSolver *invAc = nullptr;
double theta = 0.5;
public:
BlockMGSolver(const int height, const int width, Array2D<HypreParMatrix *>& Af_,
Array2D<double>& Acoef_, std::vector<HypreParMatrix *>& P_);
virtual void SetOperator(const Operator &op) {}
virtual void SetTheta(const double a) { theta = a; }
virtual void Mult(const Vector &r, Vector &z) const;
virtual ~BlockMGSolver();
};
BlockMGSolver::BlockMGSolver(const int height, const int width,
Array2D<HypreParMatrix *>& Af_, Array2D<double>& Acoef_,
std::vector<HypreParMatrix *>& P_)
: Solver(height, width), Af(Af_), Acoef(Acoef_), P(P_)
{
numBlocks = Af.NumRows();
MFEM_VERIFY(Af.NumCols() == numBlocks, "");
numGrids = P.size();
BlkP.resize(numGrids);
BlkA.resize(numGrids+1);
S.resize(numGrids);
A.resize(numGrids + 1);
A[numGrids] = Af;
Aoffsets.resize(numGrids+1);
Poffsets_i.resize(numGrids);
Poffsets_j.resize(numGrids);
// Construct Bilinear form Matrices on each level
for (int k = numGrids ; k > 0; k--)
{
A[k - 1].SetSize(numBlocks,numBlocks);
Aoffsets[k].SetSize(numBlocks+1); Aoffsets[k][0] = 0;
for (int i=0; i<numBlocks; i++)
{
Aoffsets[k][i+1] = A[k](i,i)->Height();
}
Aoffsets[k].PartialSum();
BlkA[k] = new BlockOperator(Aoffsets[k]);
S[k-1] = new BlockOperator(Aoffsets[k]); // Smoother
for (int i=0; i<numBlocks; i++)
{
for (int j=0; j<numBlocks; j++)
{
if (A[k](i,j) == NULL)
{
A[k - 1](i,j) = NULL;
}
else
{
A[k - 1](i,j) = RAP(A[k](i,j), P[k - 1]);
BlkA[k]->SetBlock(i, j, A[k](i,j), Acoef(i,j));
}
}
HypreSmoother *S_i = new HypreSmoother;
S_i->SetType(HypreSmoother::Jacobi);
S_i->SetOperator(*(A[k](i,i)));
S[k - 1]->SetBlock(i,i,S_i);
}
Poffsets_i[k-1].SetSize(numBlocks+1); Poffsets_i[k-1][0] = 0;
Poffsets_j[k-1].SetSize(numBlocks+1); Poffsets_j[k-1][0] = 0;
for (int i=0; i<numBlocks; i++)
{
Poffsets_i[k-1][i+1] = P[k-1]->Height();
Poffsets_j[k-1][i+1] = P[k-1]->Width();
}
Poffsets_i[k-1].PartialSum();
Poffsets_j[k-1].PartialSum();
BlkP[k-1] = new BlockOperator(Poffsets_i[k-1],Poffsets_j[k-1]);
for (int i=0; i<numBlocks; i++)
{
BlkP[k-1]->SetBlock(i,i,P[k-1]);
}
}
// Set up coarse solve operator
// Convert the coarse grid blockmatrix to a HypreParMatrix
Array<int> offsets(numBlocks+1);
offsets[0]=0;
for (int i=0; i<numBlocks; i++)
{
offsets[i+1]=A[0](i,i)->Height();
}
offsets.PartialSum();
BlkA[0] = new BlockOperator(offsets);
Array2D<SparseMatrix*> Asp;
//Array2D<double> Acoef;
Asp.SetSize(numBlocks,numBlocks);
//Acoef.SetSize(numBlocks,numBlocks);
for (int i=0; i<numBlocks; i++)
{
for (int j=0; j<numBlocks; j++)
{
if (A[0](i,j) != NULL)
{
BlkA[0]->SetBlock(i, j, A[0](i,j), Acoef(i,j));
}
Asp(i,j) = NULL;
//Acoef(i,j) = 1.0;
}
}
// Convert to HypreParMatrix
HypreParMatrix * Ac;
std::vector<std::vector<int> > blockProcOffsets(numBlocks);
std::vector<std::vector<int> > all_block_num_loc_rows(numBlocks);
{
int nprocs, rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
std::vector<int> allnumrows(nprocs);
const int blockNumRows = A[0](0,0)->Height();
MPI_Allgather(&blockNumRows, 1, MPI_INT, allnumrows.data(), 1, MPI_INT,
MPI_COMM_WORLD);
for (int b=0; b<numBlocks; ++b)
{
blockProcOffsets[b].resize(nprocs);
all_block_num_loc_rows[b].resize(nprocs);
}
blockProcOffsets[0][0] = 0;
for (int i=0; i<nprocs-1; ++i)
{
blockProcOffsets[0][i+1] = blockProcOffsets[0][i] + allnumrows[i];
}
for (int i=0; i<nprocs; ++i)
{
for (int b=0; b<numBlocks; ++b)
{
all_block_num_loc_rows[b][i] = allnumrows[i];
}
for (int b=1; b<numBlocks; ++b)
{
blockProcOffsets[b][i] = blockProcOffsets[0][i];
}
}
}
Ac = CreateHypreParMatrixFromBlocks(MPI_COMM_WORLD, offsets, A[0], Asp,
Acoef, blockProcOffsets, all_block_num_loc_rows);
invAc = CreateStrumpackSolver(new STRUMPACKRowLocMatrix(*Ac), MPI_COMM_WORLD);
delete Ac;
}
void BlockMGSolver::Mult(const Vector &r, Vector &z) const
{
// Residual vectors
std::vector<Vector> rv(numGrids + 1);
// correction vectors
std::vector<Vector> zv(numGrids + 1);
// allocation
for (int i = 0; i <= numGrids ; i++)
{
int n = (i==0) ? invAc->Height(): BlkA[i]->Width();
rv[i].SetSize(n);
zv[i].SetSize(n);
}
// Initial residual
rv[numGrids] = r;
// smooth and update residuals down to the coarsest level
for (int i = numGrids; i > 0 ; i--)
{
// Pre smooth
S[i - 1]->Mult(rv[i], zv[i]); zv[i] *= theta;
// compute residual
int n = BlkA[i]->Width();
Vector w(n);
BlkA[i]->Mult(zv[i], w);
rv[i] -= w;
// Restrict
BlkP[i - 1]->MultTranspose(rv[i], rv[i - 1]);
}
// Coarse grid Solve
invAc->Mult(rv[0], zv[0]);
//
for (int i = 1; i <= numGrids ; i++)
{
// Prolong correction
Vector u(BlkP[i - 1]->Height());
BlkP[i - 1]->Mult(zv[i - 1], u);
// Update correction
zv[i] += u;
// Update residual
Vector v(BlkA[i]->Height());
BlkA[i]->Mult(u, v); rv[i] -= v;
// Post smooth
S[i - 1]->Mult(rv[i], v); v *= theta;
// Update correction
zv[i] += v;
}
z = zv[numGrids];
}
BlockMGSolver::~BlockMGSolver()
{
for (int i = numGrids - 1; i >= 0 ; i--)
{
delete S[i];
delete BlkP[i];
delete BlkA[i];
for (int j=0; j<numBlocks; j++)
{
for (int k=0; k<numBlocks; k++)
{
delete A[i](j,k);
}
}
A[i].DeleteAll();
}
delete BlkA[numGrids];
delete invAc;
A.clear();
}
} // namespace mfem
+869
View File
@@ -0,0 +1,869 @@
#ifndef BGMULTIGRID
#define BGMULTIGRID
#include "mfem.hpp"
using namespace std;
//namespace mfem {
namespace blockgmg
{
// Row and column offsets are assumed to be the same, for each process.
// Array offsets stores process-local offsets with respect to the blocks. Process offsets are not included.
HypreParMatrix* CreateHypreParMatrixFromBlocks2(MPI_Comm comm,
Array<int> const& offsets, Array2D<HypreParMatrix*> const& blocks,
Array2D<SparseMatrix*> const& blocksSp,
Array2D<double> const& coefficient,
std::vector<std::vector<int> > const& blockProcOffsets,
std::vector<std::vector<int> > const& all_block_num_loc_rows);
class BlockMGSolver : public Solver
{
private:
/// The linear system matrix
Array2D<HypreParMatrix *>&
Af; // TODO: remove this, as it is used only in the constructor
Array2D<double>&
Acoef; // TODO: remove this, as it is used only in the constructor
vector<Array<int>> Aoffsets;
vector<Array<int>> Poffsets_i;
vector<Array<int>> Poffsets_j;
std::vector<Array2D<HypreParMatrix *>> A;
std::vector<HypreParMatrix *> const& P;
std::vector<BlockOperator *> BlkP;
std::vector<BlockOperator *> BlkA;
std::vector<BlockOperator *> S;
HypreParMatrix * Ac;
SparseMatrix AcSp;
int numGrids, numBlocks;
//STRUMPACKSolver *invAc = nullptr;
Operator *invAc = nullptr;
double theta = 0.5;
public:
BlockMGSolver(MPI_Comm comm, const int height, const int width,
Array2D<HypreParMatrix *>& Af_,
Array2D<double>& Acoef_, std::vector<HypreParMatrix *> const& P_)
: Solver(height, width), Af(Af_), Acoef(Acoef_), P(P_)
{
numBlocks = Af.NumRows();
MFEM_VERIFY(Af.NumCols() == numBlocks, "");
numGrids = P.size();
BlkP.resize(numGrids);
BlkA.resize(numGrids+1);
S.resize(numGrids);
A.resize(numGrids + 1);
A[numGrids] = Af;
Aoffsets.resize(numGrids+1);
Poffsets_i.resize(numGrids);
Poffsets_j.resize(numGrids);
// Construct Bilinear form Matrices on each level
for (int k = numGrids ; k > 0; k--)
{
A[k - 1].SetSize(numBlocks,numBlocks);
Aoffsets[k].SetSize(numBlocks+1); Aoffsets[k][0] = 0;
for (int i=0; i<numBlocks; i++)
{
Aoffsets[k][i+1] = A[k](i,i)->Height();
}
Aoffsets[k].PartialSum();
BlkA[k] = new BlockOperator(Aoffsets[k]);
S[k-1] = new BlockOperator(Aoffsets[k]); // Smoother
for (int i=0; i<numBlocks; i++)
{
for (int j=0; j<numBlocks; j++)
{
if (A[k](i,j) == NULL)
{
A[k - 1](i,j) = NULL;
}
else
{
A[k - 1](i,j) = RAP(A[k](i,j), P[k - 1]);
BlkA[k]->SetBlock(i, j, A[k](i,j), Acoef(i,j));
}
}
HypreSmoother *S_i = new HypreSmoother;
S_i->SetType(HypreSmoother::Jacobi);
S_i->SetOperator(*(A[k](i,i)));
S[k - 1]->SetBlock(i,i,S_i);
}
Poffsets_i[k-1].SetSize(numBlocks+1); Poffsets_i[k-1][0] = 0;
Poffsets_j[k-1].SetSize(numBlocks+1); Poffsets_j[k-1][0] = 0;
for (int i=0; i<numBlocks; i++)
{
Poffsets_i[k-1][i+1] = P[k-1]->Height();
Poffsets_j[k-1][i+1] = P[k-1]->Width();
}
Poffsets_i[k-1].PartialSum();
Poffsets_j[k-1].PartialSum();
BlkP[k-1] = new BlockOperator(Poffsets_i[k-1],Poffsets_j[k-1]);
for (int i=0; i<numBlocks; i++)
{
BlkP[k-1]->SetBlock(i,i,P[k-1]);
}
}
// Set up coarse solve operator
// Convert the coarse grid blockmatrix to a HypreParMatrix
Array<int> offsets(numBlocks+1);
offsets[0]=0;
for (int i=0; i<numBlocks; i++)
{
offsets[i+1]=A[0](i,i)->Height();
}
offsets.PartialSum();
BlkA[0] = new BlockOperator(offsets);
Array2D<SparseMatrix*> Asp;
//Array2D<double> Acoef;
Asp.SetSize(numBlocks,numBlocks);
//Acoef.SetSize(numBlocks,numBlocks);
for (int i=0; i<numBlocks; i++)
{
for (int j=0; j<numBlocks; j++)
{
if (A[0](i,j) != NULL)
{
BlkA[0]->SetBlock(i, j, A[0](i,j), Acoef(i,j));
}
Asp(i,j) = NULL;
//Acoef(i,j) = 1.0;
}
}
// Convert to HypreParMatrix
//HypreParMatrix * Ac;
std::vector<std::vector<int> > blockProcOffsets(numBlocks);
std::vector<std::vector<int> > all_block_num_loc_rows(numBlocks);
{
int nprocs, rank;
MPI_Comm_rank(comm, &rank);
MPI_Comm_size(comm, &nprocs);
std::vector<int> allnumrows(nprocs);
const int blockNumRows = A[0](0,
0)->Height(); // TODO: Not valid if blocks are of different size
MPI_Allgather(&blockNumRows, 1, MPI_INT, allnumrows.data(), 1, MPI_INT, comm);
for (int b=0; b<numBlocks; ++b)
{
blockProcOffsets[b].resize(nprocs);
all_block_num_loc_rows[b].resize(nprocs);
}
blockProcOffsets[0][0] = 0;
for (int i=0; i<nprocs-1; ++i)
{
blockProcOffsets[0][i+1] = blockProcOffsets[0][i] + allnumrows[i];
}
for (int i=0; i<nprocs; ++i)
{
for (int b=0; b<numBlocks; ++b)
{
all_block_num_loc_rows[b][i] = allnumrows[i];
}
for (int b=1; b<numBlocks; ++b)
{
blockProcOffsets[b][i] = blockProcOffsets[0][i];
}
}
}
Ac = CreateHypreParMatrixFromBlocks2(comm, offsets, A[0], Asp,
Acoef, blockProcOffsets, all_block_num_loc_rows);
#ifdef MFEM_USE_STRUMPACK
invAc = CreateStrumpackSolver(new STRUMPACKRowLocMatrix(*Ac), comm);
delete Ac;
#else
Ac->GetDiag(AcSp); // AcSp does not own the data
UMFPackSolver *umf_solver = new UMFPackSolver();
umf_solver->Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver->SetOperator(AcSp);
invAc = umf_solver;
#endif
}
virtual void SetOperator(const Operator &op) {}
virtual void SetTheta(const double a) { theta = a; }
virtual void Mult(const Vector &r, Vector &z) const
{
// Residual vectors
std::vector<Vector> rv(numGrids + 1);
// correction vectors
std::vector<Vector> zv(numGrids + 1);
// allocation
for (int i = 0; i <= numGrids ; i++)
{
int n = (i==0) ? invAc->Height(): BlkA[i]->Width();
rv[i].SetSize(n);
zv[i].SetSize(n);
}
// Initial residual
rv[numGrids] = r;
// smooth and update residuals down to the coarsest level
for (int i = numGrids; i > 0 ; i--)
{
// Pre smooth
S[i - 1]->Mult(rv[i], zv[i]); zv[i] *= theta;
// compute residual
int n = BlkA[i]->Width();
Vector w(n);
BlkA[i]->Mult(zv[i], w);
rv[i] -= w;
// Restrict
BlkP[i - 1]->MultTranspose(rv[i], rv[i - 1]);
}
// Coarse grid Solve
invAc->Mult(rv[0], zv[0]);
//
for (int i = 1; i <= numGrids ; i++)
{
// Prolong correction
Vector u(BlkP[i - 1]->Height());
BlkP[i - 1]->Mult(zv[i - 1], u);
// Update correction
zv[i] += u;
// Update residual
Vector v(BlkA[i]->Height());
BlkA[i]->Mult(u, v); rv[i] -= v;
// Post smooth
S[i - 1]->Mult(rv[i], v); v *= theta;
// Update correction
zv[i] += v;
}
z = zv[numGrids];
}
virtual ~BlockMGSolver()
{
for (int i = numGrids - 1; i >= 0 ; i--)
{
delete S[i];
delete BlkP[i];
delete BlkA[i];
for (int j=0; j<numBlocks; j++)
{
for (int k=0; k<numBlocks; k++)
{
delete A[i](j,k);
}
}
A[i].DeleteAll();
}
delete BlkA[numGrids];
delete invAc;
A.clear();
}
#ifdef MFEM_USE_STRUMPACK
STRUMPACKSolver* CreateStrumpackSolver(Operator *Arow, MPI_Comm comm)
{
//STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, comm);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(0, NULL, comm);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
return strumpack;
}
#endif
};
#define SWTIMING
#define SERIAL_PROLONGATION
#define ITERATIVE_COARSE_SOLVE
#define SPARSE_JACOBI
//#define SPARSE_ICHOLESKY
//#define SPARSE_ILU
//#define COARSE_PA
//#define BLOCK_DIAG
//#define COARSE_AMS
class BlockMGPASolver : public Solver
{
private:
/// The linear system matrix
Array2D<Operator*>&
Af; // TODO: remove this, as it is used only in the constructor
Array2D<double>&
Acoef; // TODO: remove this, as it is used only in the constructor
vector<Array<int>> Aoffsets;
vector<Array<int>> Poffsets_i;
vector<Array<int>> Poffsets_j;
Array<int> Coffsets;
std::vector<Array2D<Operator*>> A;
#ifdef SERIAL_PROLONGATION
std::vector<SparseMatrix *> P;
#else
std::vector<HypreParMatrix *> const& P;
#endif
std::vector<Vector*> const& diag;
std::vector<Operator*> Pt;
std::vector<BlockOperator *> BlkP;
std::vector<BlockOperator *> BlkA;
std::vector<BlockOperator *> S;
std::vector<OperatorJacobiSmoother*> Jacobi;
HypreParMatrix * Ac;
SparseMatrix AcSp;
int numGrids, numBlocks;
//STRUMPACKSolver *invAc = nullptr;
Operator *invAc = nullptr;
double theta = 0.5;
mutable std::vector<Vector> rv, zv;
mutable Vector u, v, w;
Vector diagAc;
Array<int> emptyEssDof;
OperatorJacobiSmoother *JacobiAc;
#ifdef SPARSE_ICHOLESKY
Solver *iCholAc;
#endif
#ifdef SPARSE_ILU
Solver *iluAc;
#endif
#ifdef BLOCK_DIAG
HypreParMatrix *AcDiag;
SparseMatrix AcDiagSp;
#endif
public:
BlockMGPASolver(MPI_Comm comm, const int height, const int width,
Array2D<Operator*>& Af_, Array2D<double>& Acoef_,
#ifdef COARSE_PA
BlockOperator *BlkAc,
#else
Array2D<HypreParMatrix*> const& BlkAc,
#endif
#ifdef COARSE_AMS
ParFiniteElementSpace* coarseFespace,
#endif
std::vector<HypreParMatrix *> const& P_, std::vector<Vector*> const& diag_,
Array<int>& ess_tdof_list)
#ifdef SERIAL_PROLONGATION
: Solver(height, width), Af(Af_), Acoef(Acoef_), diag(diag_)
#else
: Solver(height, width), Af(Af_), Acoef(Acoef_), P(P_), diag(diag_)
#endif
{
timeMult = 0.0;
timeMultAc = 0.0;
timeMultPresmooth = 0.0;
timeMultResidual = 0.0;
timeMultRestrict = 0.0;
#ifdef SERIAL_PROLONGATION
P.resize(P_.size());
for (int i=0; i<P_.size(); ++i)
{
P[i] = new SparseMatrix();
P_[i]->GetDiag(*P[i]);
P[i]->BuildTranspose();
}
#endif
numBlocks = Af.NumRows();
MFEM_VERIFY(Af.NumCols() == numBlocks, "");
#ifndef COARSE_PA
MFEM_VERIFY(BlkAc.NumCols() == numBlocks && BlkAc.NumRows() == numBlocks, "");
#endif
numGrids = P.size();
MFEM_VERIFY(diag.size() == numBlocks, "");
BlkP.resize(numGrids);
BlkA.resize(numGrids+1);
Pt.resize(numGrids);
S.resize(numGrids);
A.resize(numGrids + 1);
A[numGrids] = Af;
Aoffsets.resize(numGrids+1);
Poffsets_i.resize(numGrids);
Poffsets_j.resize(numGrids);
Jacobi.resize(numBlocks);
for (int i=0; i<numBlocks; ++i)
{
Jacobi[i] = new OperatorJacobiSmoother(*(diag[i]), ess_tdof_list, 1.0);
}
// Construct Bilinear form Matrices on each level
for (int k = numGrids ; k > 0; k--)
{
A[k - 1].SetSize(numBlocks,numBlocks);
Aoffsets[k].SetSize(numBlocks+1); Aoffsets[k][0] = 0;
for (int i=0; i<numBlocks; i++)
{
Aoffsets[k][i+1] = A[k](i,i)->Height();
}
Aoffsets[k].PartialSum();
BlkA[k] = new BlockOperator(Aoffsets[k]);
S[k-1] = new BlockOperator(Aoffsets[k]); // Smoother
Pt[k - 1] = new TransposeOperator(P[k - 1]);
for (int i=0; i<numBlocks; i++)
{
for (int j=0; j<numBlocks; j++)
{
if (A[k](i,j) == NULL)
{
A[k - 1](i,j) = NULL;
}
else
{
//A[k - 1](i,j) = RAP(A[k](i,j), P[k - 1]);
A[k - 1](i,j) = new TripleProductOperator(Pt[k - 1], A[k](i,j), P[k - 1], false,
false, false);
BlkA[k]->SetBlock(i, j, A[k](i,j), Acoef(i,j));
}
}
if (k == numGrids) // finest level
{
S[k - 1]->SetBlock(i,i,Jacobi[i]);
}
else
{
S[k - 1]->SetBlock(i,i, new TripleProductOperator(Pt[k], &(S[k]->GetBlock(i,i)),
P[k], false,
false, false)); // TODO: get a diagonal on each level
}
}
Poffsets_i[k-1].SetSize(numBlocks+1); Poffsets_i[k-1][0] = 0;
Poffsets_j[k-1].SetSize(numBlocks+1); Poffsets_j[k-1][0] = 0;
for (int i=0; i<numBlocks; i++)
{
Poffsets_i[k-1][i+1] = P[k-1]->Height();
Poffsets_j[k-1][i+1] = P[k-1]->Width();
}
Poffsets_i[k-1].PartialSum();
Poffsets_j[k-1].PartialSum();
BlkP[k-1] = new BlockOperator(Poffsets_i[k-1],Poffsets_j[k-1]);
for (int i=0; i<numBlocks; i++)
{
BlkP[k-1]->SetBlock(i,i,P[k-1]);
}
}
// Set up coarse solve operator
// Convert the coarse grid blockmatrix to a HypreParMatrix
Coffsets.SetSize(numBlocks+1);
Coffsets[0]=0;
for (int i=0; i<numBlocks; i++)
{
Coffsets[i+1]=A[0](i,i)->Height();
#ifndef COARSE_PA
MFEM_VERIFY(BlkAc(i,i)->Height() == A[0](i,i)->Height(), "");
MFEM_VERIFY(BlkAc(i,i)->Width() == A[0](i,i)->Width(), "");
#endif
}
Coffsets.PartialSum();
#ifdef COARSE_PA
BlkA[0] = BlkAc;
#else
BlkA[0] = new BlockOperator(Coffsets);
#endif
Array2D<SparseMatrix*> Asp;
//Array2D<double> Acoef;
Asp.SetSize(numBlocks,numBlocks);
//Acoef.SetSize(numBlocks,numBlocks);
for (int i=0; i<numBlocks; i++)
{
for (int j=0; j<numBlocks; j++)
{
#ifndef COARSE_PA
MFEM_VERIFY((A[0](i,j) == NULL) == (BlkAc(i,j) == NULL), "");
if (BlkAc(i,j) != NULL)
{
BlkA[0]->SetBlock(i, j, BlkAc(i,j), Acoef(i,j));
}
#endif
Asp(i,j) = NULL;
//Acoef(i,j) = 1.0;
}
}
#ifdef COARSE_PA
CGSolver *cg_solver = new CGSolver();
cg_solver->SetAbsTol(1.0e-6);
cg_solver->SetRelTol(1.0e-6);
cg_solver->SetMaxIter(1000);
cg_solver->SetOperator(*BlkAc);
cg_solver->SetPrintLevel(0);
cg_solver->iterative_mode = false;
invAc = cg_solver;
#else
// Convert to HypreParMatrix
HypreParMatrix * Ac;
std::vector<std::vector<int> > blockProcOffsets(numBlocks);
std::vector<std::vector<int> > all_block_num_loc_rows(numBlocks);
{
int nprocs, rank;
MPI_Comm_rank(comm, &rank);
MPI_Comm_size(comm, &nprocs);
std::vector<int> allnumrows(nprocs);
// TODO: Not valid if blocks are of different size
const int blockNumRows = BlkAc(0,0)->Height();
MPI_Allgather(&blockNumRows, 1, MPI_INT, allnumrows.data(), 1, MPI_INT, comm);
for (int b=0; b<numBlocks; ++b)
{
blockProcOffsets[b].resize(nprocs);
all_block_num_loc_rows[b].resize(nprocs);
}
blockProcOffsets[0][0] = 0;
for (int i=0; i<nprocs-1; ++i)
{
blockProcOffsets[0][i+1] = blockProcOffsets[0][i] + allnumrows[i];
}
for (int i=0; i<nprocs; ++i)
{
for (int b=0; b<numBlocks; ++b)
{
all_block_num_loc_rows[b][i] = allnumrows[i];
}
for (int b=1; b<numBlocks; ++b)
{
blockProcOffsets[b][i] = blockProcOffsets[0][i];
}
}
}
Ac = CreateHypreParMatrixFromBlocks2(comm, Coffsets, BlkAc, Asp,
Acoef, blockProcOffsets, all_block_num_loc_rows);
#ifdef BLOCK_DIAG
{
Array2D<HypreParMatrix*> BlkAcDiag(numBlocks, numBlocks);
Array2D<double> DiagCoef(numBlocks, numBlocks);
DiagCoef = 0.0;
for (int i=0; i<numBlocks; ++i)
{
DiagCoef(i,i) = Acoef(i,i);
for (int j=0; j<numBlocks; ++j)
{
BlkAcDiag(i,j) = NULL;
}
BlkAcDiag(i,i) = BlkAc(i,i);
}
AcDiag = CreateHypreParMatrixFromBlocks2(comm, Coffsets, BlkAcDiag, Asp,
DiagCoef, blockProcOffsets, all_block_num_loc_rows);
AcDiag->GetDiag(AcDiagSp);
//delete AcDiag;
}
#endif
#ifdef MFEM_USE_STRUMPACK
invAc = CreateStrumpackSolver(new STRUMPACKRowLocMatrix(*Ac), comm);
delete Ac;
#else
Ac->GetDiag(AcSp); // AcSp does not own the data
delete Ac;
#ifdef ITERATIVE_COARSE_SOLVE
//CGSolver *cg_solver = new CGSolver(comm);
CGSolver *cg_solver = new CGSolver();
cg_solver->SetAbsTol(1.0e-6);
cg_solver->SetRelTol(1.0e-6);
cg_solver->SetMaxIter(1000);
cg_solver->SetOperator(AcSp);
cg_solver->SetPrintLevel(0);
cg_solver->iterative_mode = false;
#ifdef SPARSE_JACOBI
AcSp.GetDiag(diagAc);
JacobiAc = new OperatorJacobiSmoother(diagAc, emptyEssDof);
cg_solver->SetPreconditioner(*JacobiAc);
#endif
#ifdef SPARSE_ICHOLESKY
{
Vector tmpX(AcSp.Height());
Vector tmpY(AcSp.Height());
tmpX = 1.0;
tmpY = 0.0;
#ifdef BLOCK_DIAG
AcDiagSp.Finalize();
AcDiagSp.SortColumnIndices();
AcDiagSp.Mult(tmpX, tmpY);
#else
AcSp.Finalize();
AcSp.SortColumnIndices();
AcSp.Mult(tmpX, tmpY);
#endif
}
#ifdef BLOCK_DIAG
iCholAc = new IncompleteCholesky(AcDiagSp);
#else
iCholAc = new IncompleteCholesky(AcSp);
#endif
cg_solver->SetPreconditioner(*iCholAc);
cg_solver->SetPrintLevel(-1);
#endif // SPARSE_ICHOLESKY
#ifdef SPARSE_ILU
{
Vector tmpX(AcSp.Height());
Vector tmpY(AcSp.Height());
tmpX = 1.0;
tmpY = 0.0;
#ifdef BLOCK_DIAG
AcDiagSp.Finalize();
AcDiagSp.SortColumnIndices();
AcDiagSp.Mult(tmpX, tmpY);
#else
AcSp.Finalize();
AcSp.SortColumnIndices();
AcSp.Mult(tmpX, tmpY);
#endif
}
#ifdef BLOCK_DIAG
iluAc = new ILUcusparse(AcDiagSp);
#else
iluAc = new ILUcusparse(AcSp);
#endif
cg_solver->SetPreconditioner(*iluAc);
cg_solver->SetPrintLevel(0);
#endif // SPARSE_ILU
#ifdef COARSE_AMS
#ifndef COARSE_PA
MFEM_VERIFY(numBlocks == 4, "");
// TODO: just set 2 AMS solvers for E and H.
if (coarseFespace != NULL)
{
BlockDiagonalPreconditioner *blkAMS = new BlockDiagonalPreconditioner(Coffsets);
for (int i=0; i<numBlocks; ++i)
{
HypreAMS *ams = new HypreAMS(*BlkAc(i,i), coarseFespace);
blkAMS->SetDiagonalBlock(i, ams);
}
cg_solver->SetPreconditioner(*blkAMS);
}
#endif
#endif
invAc = cg_solver;
#else
UMFPackSolver *umf_solver = new UMFPackSolver();
umf_solver->Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver->SetOperator(AcSp);
invAc = umf_solver;
#endif
#endif
#endif // COARSE_PA
// Residual vectors
rv.resize(numGrids + 1);
// correction vectors
zv.resize(numGrids + 1);
// allocation
for (int i = 0; i <= numGrids ; i++)
{
int n = (i==0) ? invAc->Height(): BlkA[i]->Width();
rv[i].SetSize(n);
zv[i].SetSize(n);
rv[i].UseDevice(true);
zv[i].UseDevice(true);
}
}
virtual void SetOperator(const Operator &op) {}
virtual void SetTheta(const double a) { theta = a; }
virtual void Mult(const Vector &r, Vector &z) const
{
#ifdef SWTIMING
StopWatch sw;
sw.Clear();
sw.Start();
#endif
// Initial residual
rv[numGrids] = r;
// smooth and update residuals down to the coarsest level
for (int i = numGrids; i > 0 ; i--)
{
// Pre smooth
#ifdef SWTIMING
StopWatch sws;
sws.Clear();
sws.Start();
#endif
S[i - 1]->Mult(rv[i], zv[i]); zv[i] *= theta;
#ifdef SWTIMING
sws.Stop();
timeMultPresmooth += sws.RealTime();
#endif
// compute residual
int n = BlkA[i]->Width();
w.SetSize(n);
w.UseDevice(true);
#ifdef SWTIMING
StopWatch swop;
swop.Clear();
swop.Start();
#endif
BlkA[i]->Mult(zv[i], w);
rv[i] -= w;
#ifdef SWTIMING
swop.Stop();
timeMultResidual += swop.RealTime();
#endif
// Restrict
#ifdef SWTIMING
StopWatch swr;
swr.Clear();
swr.Start();
#endif
BlkP[i - 1]->MultTranspose(rv[i], rv[i - 1]);
#ifdef SWTIMING
swr.Stop();
timeMultRestrict += swr.RealTime();
#endif
}
#ifdef SWTIMING
StopWatch swAc;
swAc.Clear();
swAc.Start();
#endif
// Coarse grid Solve
invAc->Mult(rv[0], zv[0]);
//
#ifdef SWTIMING
swAc.Stop();
timeMultAc += swAc.RealTime();
#endif
for (int i = 1; i <= numGrids ; i++)
{
// Prolong correction
u.SetSize(BlkP[i - 1]->Height());
u.UseDevice(true);
BlkP[i - 1]->Mult(zv[i - 1], u);
// Update correction
zv[i] += u;
// Update residual
v.SetSize(BlkA[i]->Height());
v.UseDevice(true);
BlkA[i]->Mult(u, v); rv[i] -= v;
// Post smooth
S[i - 1]->Mult(rv[i], v); v *= theta;
// Update correction
zv[i] += v;
}
z = zv[numGrids];
#ifdef SWTIMING
sw.Stop();
timeMult += sw.RealTime();
#endif
}
virtual ~BlockMGPASolver()
{
for (int i = numGrids - 1; i >= 0 ; i--)
{
delete S[i];
delete BlkP[i];
delete BlkA[i];
for (int j=0; j<numBlocks; j++)
{
for (int k=0; k<numBlocks; k++)
{
delete A[i](j,k);
}
}
A[i].DeleteAll();
}
delete BlkA[numGrids];
delete invAc;
A.clear();
#ifdef SPARSE_ICHOLESKY
delete iCholAc;
#endif
#ifdef SPARSE_ILU
delete iluAc;
#endif
}
#ifdef MFEM_USE_STRUMPACK
STRUMPACKSolver* CreateStrumpackSolver(Operator *Arow, MPI_Comm comm)
{
//STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, comm);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(0, NULL, comm);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
return strumpack;
}
#endif
mutable double timeMult, timeMultAc, timeMultPresmooth, timeMultResidual,
timeMultRestrict;
};
}
//} // namespace mfem
#endif // BGMULTIGRID
+1 -1
View File
@@ -5,7 +5,7 @@
| | | | | || _|| __/| | | | | |
|_| |_| |_||_| \___||_| |_| |_|
https://mfem.org
http://mfem.org
This directory contains modifications of the example codes that illustrate the
use of MFEM features based on the PETSc suite.
+9 -4
View File
@@ -410,16 +410,21 @@ int main(int argc, char *argv[])
}
// 12. Free the used memory.
delete lobpcg;
delete slepc;
if (!use_slepc)
{
delete lobpcg;
}
else
{
delete slepc;
}
delete precond;
delete M;
delete A;
delete pA;
delete pM;
#if defined(MFEM_USE_SUPERLU) || defined(MFEM_USE_STRUMPACK)
delete Arow;
#endif
delete fespace;
if (order > 0)
{
+1 -1
View File
@@ -5,7 +5,7 @@
| | | | | || _|| __/| | | | | |
|_| |_| |_||_| \___||_| |_| |_|
https://mfem.org
http://mfem.org
This directory contains modifications of the example codes that illustrate the
use of MFEM features based on the Parallel Unstructured Mesh Infrastructure,
+363
View File
@@ -0,0 +1,363 @@
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstdlib>
#include <cmath>
using namespace std;
class HypreMat
{
private:
std::vector<int> I, J;
std::vector<double> d;
int size;
public:
HypreMat() : size(0)
{
}
int Size() const { return size; }
void ReadMatrixParallel(std::string filename, const int np)
{
int lastRow = 0;
int count = 0;
std::vector<int> R;
for (int p=0; p<np; ++p)
{
std::ifstream f(filename.c_str() + std::to_string(p), std::ifstream::in);
int rowFirst, rowLast, colFirst, colLast, row, col;
double v;
f >> rowFirst >> rowLast >> colFirst >> colLast;
I.resize(rowLast+2);
for (row=rowFirst+1; row<=rowLast+1; ++row)
{
I[row] = 0;
}
while (f.good())
{
f >> row >> col >> v;
if (row >= 2485)
{
abort();
}
//if (row < lastRow) // verify ascending order of rows
// abort();
lastRow = row;
I[row+1]++;
R.push_back(row);
J.push_back(col);
d.push_back(v);
count++;
}
f.close();
}
// Partial sum of I
size = I.size() - 1;
I[0] = 0;
for (int j=1; j<size; ++j)
{
I[j+1] += I[j];
}
//int *Idata = I.data();
if (I[size] != J.size() || I[size] != d.size())
{
abort();
}
{
// Reorder J and d
const int nnz = J.size();
if (nnz != d.size() || nnz != R.size())
{
abort();
}
std::vector<int> JJ(nnz);
std::vector<double> dd(nnz);
std::vector<int> cnt;
JJ = J;
dd = d;
cnt.assign(I.size(), 0); // slightly larger than necessary
for (int j=0; j<nnz; ++j)
{
J[I[R[j]] + cnt[R[j]]] = JJ[j];
d[I[R[j]] + cnt[R[j]]] = dd[j];
cnt[R[j]]++;
}
}
}
void CopyReorder(HypreMat const& A, std::vector<int> const& permRow,
std::vector<int> const& permCol)
{
if (permRow.size() != A.Size() || permCol.size() != A.Size())
{
abort();
}
size = A.Size();
I.resize(size+1);
I[0] = 0;
for (int j=0; j<size; ++j)
{
const std::size_t nnz_j = A.I[j+1] - A.I[j];
I[permRow[j]+1] = nnz_j;
}
for (int j=1; j<size; ++j)
{
I[j+1] += I[j];
}
const std::size_t nnz = I[size];
J.resize(nnz);
d.resize(nnz);
std::vector<std::size_t> cnt;
cnt.assign(size, 0);
for (int j=0; j<size; ++j)
{
const int pj = permRow[j];
const std::size_t nnz_j = A.I[j+1] - A.I[j];
for (int k=0; k<nnz_j; ++k, cnt[pj]++)
{
J[I[pj] + cnt[pj]] = permCol[A.J[A.I[j] + k]];
d[I[pj] + cnt[pj]] = A.d[A.I[j] + k];
}
}
}
void Print(std::string filename)
{
std::ofstream f(filename.c_str());
for (int j=0; j<size; ++j)
{
for (int k=I[j]; k<I[j+1]; ++k)
{
f << j << " " << J[k] << " " << d[k] << endl;
}
}
f.close();
}
void Compare(HypreMat const& A)
{
if (size != A.Size())
{
abort();
}
for (int j=0; j<size; ++j)
{
for (int k=I[j]; k<I[j+1]; ++k)
{
int m = -1;
for (int l=A.I[j]; l<A.I[j+1]; ++l)
{
if (A.J[l] == J[k])
{
m = l;
}
}
if (m < 0)
{
cout << "row " << j << " column " << J[k] << " not found" << endl;
}
//else if (fabs(A.d[m] - d[k]) > 1.0e-8)
else if (fabs(fabs(A.d[m]) - fabs(d[k])) > 0.0)
{
cout << "entry (" << j << ", " << J[k] << ") " << d[k] << " != " << A.d[m] <<
endl;
}
}
}
}
};
void ReadCrd(std::string filename, std::vector<double>& crd)
{
std::ifstream f(filename.c_str(), std::ifstream::in);
double c;
while (f.good())
{
f >> c;
crd.push_back(c);
}
// for some reason, the last value gets pushed twice
crd.pop_back();
f.close();
}
void ReadCrdParallel(std::string filenamebase, const int np,
std::vector<double>& crd)
{
for (int p=0; p<np; ++p)
{
ReadCrd(filenamebase + std::to_string(p),
crd); // results get concatenated in crd
}
}
// crd is the DOF coordinates in serial, crdp in parallel.
void SerialToParallelDOFMap(std::vector<double> const& crd,
std::vector<double> const& crdp,
std::vector<int>& sp)
{
if (crdp.size() != crd.size())
{
abort();
}
const int dim = 3;
const int n = crd.size() / dim; // number of DOF's
sp.resize(n);
const double tol = 1.0e-8;
// Simple O(n^2) search
for (int i=0; i<n; ++i)
{
sp[i] = -1;
for (int j=0; j<n; ++j)
{
bool eq = true;
for (int l=0; l<dim; ++l)
{
if (fabs(crd[(dim*i)+l] - crdp[(dim*j)+l]) > tol)
{
eq = false;
}
}
if (eq)
{
if (sp[i] != -1)
{
abort();
}
sp[i] = j;
}
}
if (sp[i] < 0)
{
abort();
}
}
}
int main()
{
const int np = 2;
std::vector<double> crd0, crdp0, crd1, crdp1;
ReadCrd("dofcrd0Ser", crd0);
ReadCrd("dofcrd1Ser", crd1);
const int numCrd = crd0.size() / 3;
if (3*numCrd != crd0.size())
{
abort();
}
//for (int i=7440; i<crd.size(); ++i)
//cout << "crd[" << i << "] " << crd[i] << endl;
ReadCrdParallel("dofcrd0Par", np, crdp0);
ReadCrdParallel("dofcrd1Par", np, crdp1);
if (3*numCrd != crdp0.size())
{
abort();
}
std::vector<int> sp0, sp1; // serial to parallel DOF map for a subdomain
SerialToParallelDOFMap(crd0, crdp0, sp0);
SerialToParallelDOFMap(crd1, crdp1, sp1);
/*
for (int i=0; i<sp.size(); ++i)
cout << i << " " << sp[i] << endl;
*/
cout << "sp0" << endl;
for (int i=0; i<sp0.size(); ++i)
{
cout << i << " " << sp0[i] << endl;
}
cout << "sp1" << endl;
for (int i=0; i<sp1.size(); ++i)
{
cout << i << " " << sp1[i] << endl;
}
HypreMat Aser, Apar, AserToPar;
/*
Aser.ReadMatrixParallel("HypreAsdComplexIm1_Serial.0000", 1);
Apar.ReadMatrixParallel("HypreAsdComplexIm1_Par5.0000", np);
*/
Aser.ReadMatrixParallel("ifopSer.0000", 1);
Apar.ReadMatrixParallel("ifopPar.0000", np);
//AserToPar.CopyReorder(Aser, sp, sp);
AserToPar.CopyReorder(Aser, sp0, sp1);
/*
Aser.Print("rbSer");
AserToPar.Print("rbSerToPar");
Apar.Print("rbPar");
*/
cout << "Comparing " << endl;
AserToPar.Compare(Apar);
return 0;
}
+1 -1
View File
@@ -5,7 +5,7 @@
| | | | | || _|| __/| | | | | |
|_| |_| |_||_| \___||_| |_| |_|
https://mfem.org
http://mfem.org
This directory contains modifications of the example codes that illustrate the
use of MFEM features based on the SUNDIALS suite of time integration and
+2 -4
View File
@@ -721,14 +721,12 @@ HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
if (nls_type == KINSOL)
{
KINSolver *kinsolver = new KINSolver(f.GetComm(), KIN_LINESEARCH, true);
kinsolver->SetJFNK(true);
kinsolver->SetLSMaxIter(100);
KINSolver *kinsolver = new KINSolver(f.GetComm(), KIN_NONE, true);
newton_solver = kinsolver;
newton_solver->SetOperator(*reduced_oper);
newton_solver->SetMaxIter(200);
newton_solver->SetRelTol(rel_tol);
newton_solver->SetPrintLevel(1);
newton_solver->SetPrintLevel(0);
kinsolver->SetMaxSetupCalls(4);
}
else
+23 -171
View File
@@ -13,27 +13,17 @@
// ex9 -m ../../data/disc-nurbs.mesh -p 1 -r 3 -s 7 -dt 0.005 -tf 9
// ex9 -m ../../data/periodic-cube.mesh -p 0 -r 2 -s 8 -dt 0.02 -tf 8 -o 2
//
// Device sample runs:
// ex9 -pa
// ex9 -ea
// ex9 -fa
// ex9 -pa -m ../../data/periodic-cube.mesh
// ex9 -pa -m ../../data/periodic-cube.mesh -d cuda
// ex9 -ea -m ../../data/periodic-cube.mesh -d cuda
// ex9 -fa -m ../../data/periodic-cube.mesh -d cuda
//
// Description: This example code solves the time-dependent advection equation
// du/dt + v.grad(u) = 0, where v is a given fluid velocity, and
// u0(x)=u(0,x) is a given initial condition.
//
// The example demonstrates the use of Discontinuous Galerkin (DG)
// bilinear forms in MFEM (face integrators), the use of implicit
// and explicit ODE time integrators, the definition of periodic
// boundary conditions through periodic meshes, as well as the use
// of GLVis for persistent visualization of a time-evolving
// solution. The saving of time-dependent data files for external
// visualization with VisIt (visit.llnl.gov) and ParaView
// (paraview.org) is also illustrated.
// bilinear forms in MFEM (face integrators), the use of explicit
// ODE time integrators, the definition of periodic boundary
// conditions through periodic meshes, as well as the use of GLVis
// for persistent visualization of a time-evolving solution. The
// saving of time-dependent data files for external visualization
// with VisIt (visit.llnl.gov) is also illustrated.
#include "mfem.hpp"
#include <fstream>
@@ -63,54 +53,6 @@ double inflow_function(const Vector &x);
// Mesh bounding box
Vector bb_min, bb_max;
class DG_Solver : public Solver
{
private:
SparseMatrix &M, &K, A;
GMRESSolver linear_solver;
BlockILU prec;
double dt;
public:
DG_Solver(SparseMatrix &M_, SparseMatrix &K_, const FiniteElementSpace &fes)
: M(M_),
K(K_),
prec(fes.GetFE(0)->GetDof(),
BlockILU::Reordering::MINIMUM_DISCARDED_FILL),
dt(-1.0)
{
linear_solver.iterative_mode = false;
linear_solver.SetRelTol(1e-9);
linear_solver.SetAbsTol(0.0);
linear_solver.SetMaxIter(100);
linear_solver.SetPrintLevel(0);
linear_solver.SetPreconditioner(prec);
}
void SetTimeStep(double dt_)
{
if (dt_ != dt)
{
dt = dt_;
// Form operator A = M - dt*K
A = K;
A *= -dt;
A += M;
// this will also call SetOperator on the preconditioner
linear_solver.SetOperator(A);
}
}
void SetOperator(const Operator &op)
{
linear_solver.SetOperator(op);
}
virtual void Mult(const Vector &x, Vector &y) const
{
linear_solver.Mult(x, y);
}
};
/** A time-dependent operator for the right-hand side of the ODE. The DG weak
form of du/dt = -v.grad(u) is M du/dt = K u + b, where M and K are the mass
@@ -120,21 +62,19 @@ public:
class FE_Evolution : public TimeDependentOperator
{
private:
BilinearForm &M, &K;
SparseMatrix &M, &K;
const Vector &b;
Solver *M_prec;
DSmoother M_prec;
CGSolver M_solver;
DG_Solver *dg_solver;
mutable Vector z;
public:
FE_Evolution(BilinearForm &_M, BilinearForm &_K, const Vector &_b);
FE_Evolution(SparseMatrix &_M, SparseMatrix &_K, const Vector &_b);
virtual void Mult(const Vector &x, Vector &y) const;
virtual void ImplicitSolve(const double dt, const Vector &x, Vector &k);
virtual ~FE_Evolution();
virtual ~FE_Evolution() { }
};
@@ -145,16 +85,11 @@ int main(int argc, char *argv[])
const char *mesh_file = "../../data/periodic-hexagon.mesh";
int ref_levels = 2;
int order = 3;
bool pa = false;
bool ea = false;
bool fa = false;
const char *device_config = "cpu";
int ode_solver_type = 7;
int ode_solver_type = 4;
double t_final = 10.0;
double dt = 0.01;
bool visualization = false;
bool visualization = true;
bool visit = false;
bool paraview = false;
bool binary = false;
int vis_steps = 5;
@@ -173,14 +108,6 @@ int main(int argc, char *argv[])
"Number of times to refine the mesh uniformly.");
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
"--no-element-assembly", "Enable Element Assembly.");
args.AddOption(&fa, "-fa", "--full-assembly", "-no-fa",
"--no-full-assembly", "Enable Full Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver:\n\t"
"1 - Forward Euler,\n\t"
@@ -201,9 +128,6 @@ int main(int argc, char *argv[])
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
"--no-visit-datafiles",
"Save data files for VisIt (visit.llnl.gov) visualization.");
args.AddOption(&paraview, "-paraview", "--paraview-datafiles", "-no-paraview",
"--no-paraview-datafiles",
"Save data files for ParaView (paraview.org) visualization.");
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
"--ascii-datafiles",
"Use binary (Sidre) or ascii format for VisIt data files.");
@@ -223,9 +147,6 @@ int main(int argc, char *argv[])
}
args.PrintOptions(cout);
Device device(device_config);
device.Print();
// 2. Read the mesh from the given mesh file. We can handle geometrically
// periodic meshes in this code.
Mesh mesh(mesh_file, 1, 1);
@@ -247,7 +168,7 @@ int main(int argc, char *argv[])
// 4. Define the discontinuous DG finite element space of the given
// polynomial order on the refined mesh.
DG_FECollection fec(order, dim, BasisType::GaussLobatto);
DG_FECollection fec(order, dim);
FiniteElementSpace fes(&mesh, &fec);
cout << "Number of unknowns: " << fes.GetVSize() << endl;
@@ -260,23 +181,8 @@ int main(int argc, char *argv[])
FunctionCoefficient u0(u0_function);
BilinearForm m(&fes);
BilinearForm k(&fes);
if (pa)
{
m.SetAssemblyLevel(AssemblyLevel::PARTIAL);
k.SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
else if (ea)
{
m.SetAssemblyLevel(AssemblyLevel::ELEMENT);
k.SetAssemblyLevel(AssemblyLevel::ELEMENT);
}
else if (fa)
{
m.SetAssemblyLevel(AssemblyLevel::FULL);
k.SetAssemblyLevel(AssemblyLevel::FULL);
}
m.AddDomainIntegrator(new MassIntegrator);
BilinearForm k(&fes);
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
k.AddInteriorFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
@@ -288,11 +194,11 @@ int main(int argc, char *argv[])
new BoundaryFlowIntegrator(inflow, velocity, -1.0, -0.5));
m.Assemble();
m.Finalize();
int skip_zeros = 0;
k.Assemble(skip_zeros);
b.Assemble();
m.Finalize();
k.Finalize(skip_zeros);
b.Assemble();
// 6. Define the initial conditions, save the corresponding grid function to
// a file and (optionally) save data in the VisIt format and initialize
@@ -333,20 +239,6 @@ int main(int argc, char *argv[])
dc->Save();
}
ParaViewDataCollection *pd = NULL;
if (paraview)
{
pd = new ParaViewDataCollection("Example9", &mesh);
pd->SetPrefixPath("ParaView");
pd->RegisterField("solution", &u);
pd->SetLevelsOfDetail(order);
pd->SetDataFormat(VTKFormat::BINARY);
pd->SetHighOrderOutput(true);
pd->SetCycle(0);
pd->SetTime(0.0);
pd->Save();
}
socketstream sout;
if (visualization)
{
@@ -373,7 +265,7 @@ int main(int argc, char *argv[])
// 7. Define the time-dependent evolution operator describing the ODE
// right-hand side, and define the ODE solver used for time integration.
FE_Evolution adv(m, k, b);
FE_Evolution adv(m.SpMat(), k.SpMat(), b);
double t = 0.0;
adv.SetTime(t);
@@ -397,18 +289,12 @@ int main(int argc, char *argv[])
cvode->UseSundialsLinearSolver();
ode_solver = cvode; break;
case 8:
arkode = new ARKStepSolver(ARKStepSolver::EXPLICIT);
arkode->Init(adv);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
arkode->SetOrder(4);
ode_solver = arkode; break;
case 9:
arkode = new ARKStepSolver(ARKStepSolver::EXPLICIT);
arkode->Init(adv);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
arkode->SetERKTableNum(FEHLBERG_13_7_8);
if (ode_solver_type == 9) { arkode->SetERKTableNum(FEHLBERG_13_7_8); }
ode_solver = arkode; break;
}
@@ -443,13 +329,6 @@ int main(int argc, char *argv[])
dc->SetTime(t);
dc->Save();
}
if (paraview)
{
pd->SetCycle(ti);
pd->SetTime(t);
pd->Save();
}
}
}
@@ -463,7 +342,6 @@ int main(int argc, char *argv[])
// 10. Free the used memory.
delete ode_solver;
delete pd;
delete dc;
return 0;
@@ -471,23 +349,12 @@ int main(int argc, char *argv[])
// Implementation of class FE_Evolution
FE_Evolution::FE_Evolution(BilinearForm &_M, BilinearForm &_K, const Vector &_b)
: TimeDependentOperator(_M.Height()), M(_M), K(_K), b(_b), z(_M.Height())
FE_Evolution::FE_Evolution(SparseMatrix &_M, SparseMatrix &_K, const Vector &_b)
: TimeDependentOperator(_M.Size()), M(_M), K(_K), b(_b), z(_M.Size())
{
Array<int> ess_tdof_list;
if (M.GetAssemblyLevel() == AssemblyLevel::LEGACYFULL)
{
M_prec = new DSmoother(M.SpMat());
M_solver.SetOperator(M.SpMat());
dg_solver = new DG_Solver(M.SpMat(), K.SpMat(), *M.FESpace());
}
else
{
M_prec = new OperatorJacobiSmoother(M, ess_tdof_list);
M_solver.SetOperator(M);
dg_solver = NULL;
}
M_solver.SetPreconditioner(*M_prec);
M_solver.SetPreconditioner(M_prec);
M_solver.SetOperator(M);
M_solver.iterative_mode = false;
M_solver.SetRelTol(1e-9);
M_solver.SetAbsTol(0.0);
@@ -503,21 +370,6 @@ void FE_Evolution::Mult(const Vector &x, Vector &y) const
M_solver.Mult(z, y);
}
void FE_Evolution::ImplicitSolve(const double dt, const Vector &x, Vector &k)
{
MFEM_VERIFY(dg_solver != NULL,
"Implicit time integration is not supported with partial assembly");
K.Mult(x, z);
z += b;
dg_solver->SetTimeStep(dt);
dg_solver->Mult(z, k);
}
FE_Evolution::~FE_Evolution()
{
delete M_prec;
delete dg_solver;
}
// Velocity coefficient
void velocity_function(const Vector &x, Vector &v)
+28 -240
View File
@@ -13,28 +13,17 @@
// mpirun -np 4 ex9p -m ../../data/disc-nurbs.mesh -p 1 -rp 2 -s 7 -dt 0.0025 -tf 9
// mpirun -np 4 ex9p -m ../../data/periodic-cube.mesh -p 0 -rp 1 -s 8 -dt 0.01 -tf 8 -o 2
//
// Device sample runs:
// mpirun -np 4 ex9p -pa
// mpirun -np 4 ex9p -ea
// mpirun -np 4 ex9p -fa
// mpirun -np 4 ex9p -pa -m ../../data/periodic-cube.mesh
// mpirun -np 4 ex9p -pa -m ../../data/periodic-cube.mesh -d cuda
// mpirun -np 4 ex9p -ea -m ../../data/periodic-cube.mesh -d cuda
// mpirun -np 4 ex9p -fa -m ../../data/periodic-cube.mesh -d cuda
//
// Description: This example code solves the time-dependent advection equation
// du/dt + v.grad(u) = 0, where v is a given fluid velocity, and
// u0(x)=u(0,x) is a given initial condition.
//
// The example demonstrates the use of Discontinuous Galerkin (DG)
// bilinear forms in MFEM (face integrators), the use of implicit
// and explicit ODE time integrators, the definition of periodic
// boundary conditions through periodic meshes, as well as the use
// of GLVis for persistent visualization of a time-evolving
// solution. Saving of time-dependent data files for visualization
// with VisIt (visit.llnl.gov) and ParaView (paraview.org), as
// well as the optional saving with ADIOS2 (adios2.readthedocs.io)
// are also illustrated.
// bilinear forms in MFEM (face integrators), the use of explicit
// ODE time integrators, the definition of periodic boundary
// conditions through periodic meshes, as well as the use of GLVis
// for persistent visualization of a time-evolving solution. The
// saving of time-dependent data files for external visualization
// with VisIt (visit.llnl.gov) is also illustrated.
#include "mfem.hpp"
#include <fstream>
@@ -63,66 +52,6 @@ double inflow_function(const Vector &x);
// Mesh bounding box
Vector bb_min, bb_max;
class DG_Solver : public Solver
{
private:
HypreParMatrix &M, &K;
SparseMatrix M_diag;
HypreParMatrix *A;
GMRESSolver linear_solver;
BlockILU prec;
double dt;
public:
DG_Solver(HypreParMatrix &M_, HypreParMatrix &K_, const FiniteElementSpace &fes)
: M(M_),
K(K_),
A(NULL),
linear_solver(M.GetComm()),
prec(fes.GetFE(0)->GetDof(),
BlockILU::Reordering::MINIMUM_DISCARDED_FILL),
dt(-1.0)
{
linear_solver.iterative_mode = false;
linear_solver.SetRelTol(1e-9);
linear_solver.SetAbsTol(0.0);
linear_solver.SetMaxIter(100);
linear_solver.SetPrintLevel(0);
linear_solver.SetPreconditioner(prec);
M.GetDiag(M_diag);
}
void SetTimeStep(double dt_)
{
if (dt_ != dt)
{
dt = dt_;
// Form operator A = M - dt*K
delete A;
A = Add(-dt, K, 0.0, K);
SparseMatrix A_diag;
A->GetDiag(A_diag);
A_diag.Add(1.0, M_diag);
// this will also call SetOperator on the preconditioner
linear_solver.SetOperator(*A);
}
}
void SetOperator(const Operator &op)
{
linear_solver.SetOperator(op);
}
virtual void Mult(const Vector &x, Vector &y) const
{
linear_solver.Mult(x, y);
}
~DG_Solver()
{
delete A;
}
};
/** A time-dependent operator for the right-hand side of the ODE. The DG weak
form of du/dt = -v.grad(u) is M du/dt = K u + b, where M and K are the mass
@@ -132,21 +61,19 @@ public:
class FE_Evolution : public TimeDependentOperator
{
private:
OperatorHandle M, K;
HypreParMatrix &M, &K;
const Vector &b;
Solver *M_prec;
HypreSmoother M_prec;
CGSolver M_solver;
DG_Solver *dg_solver;
mutable Vector z;
public:
FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K, const Vector &_b);
FE_Evolution(HypreParMatrix &_M, HypreParMatrix &_K, const Vector &_b);
virtual void Mult(const Vector &x, Vector &y) const;
virtual void ImplicitSolve(const double dt, const Vector &x, Vector &k);
virtual ~FE_Evolution();
virtual ~FE_Evolution() { }
};
@@ -164,17 +91,11 @@ int main(int argc, char *argv[])
int ser_ref_levels = 2;
int par_ref_levels = 0;
int order = 3;
bool pa = false;
bool ea = false;
bool fa = false;
const char *device_config = "cpu";
int ode_solver_type = 7;
int ode_solver_type = 4;
double t_final = 10.0;
double dt = 0.01;
bool visualization = false;
bool visualization = true;
bool visit = false;
bool paraview = false;
bool adios2 = false;
bool binary = false;
int vis_steps = 5;
@@ -195,14 +116,6 @@ int main(int argc, char *argv[])
"Number of times to refine the mesh uniformly in parallel.");
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
"--no-element-assembly", "Enable Element Assembly.");
args.AddOption(&fa, "-fa", "--full-assembly", "-no-fa",
"--no-full-assembly", "Enable Full Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
"ODE solver:\n\t"
"1 - Forward Euler,\n\t"
@@ -223,12 +136,6 @@ int main(int argc, char *argv[])
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
"--no-visit-datafiles",
"Save data files for VisIt (visit.llnl.gov) visualization.");
args.AddOption(&paraview, "-paraview", "--paraview-datafiles", "-no-paraview",
"--no-paraview-datafiles",
"Save data files for ParaView (paraview.org) visualization.");
args.AddOption(&adios2, "-adios2", "--adios2-streams", "-no-adios2",
"--no-adios2-streams",
"Save data using adios2 streams.");
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
"--ascii-datafiles",
"Use binary (Sidre) or ascii format for VisIt data files.");
@@ -248,7 +155,6 @@ int main(int argc, char *argv[])
{
args.PrintOptions(cout);
}
// check for vaild ODE solver option
if (ode_solver_type < 1 || ode_solver_type > 9)
{
@@ -260,9 +166,6 @@ int main(int argc, char *argv[])
return 3;
}
Device device(device_config);
if (myid == 0) { device.Print(); }
// 3. Read the serial mesh from the given mesh file on all processors. We can
// handle geometrically periodic meshes in this code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
@@ -294,7 +197,7 @@ int main(int argc, char *argv[])
// 6. Define the parallel discontinuous DG finite element space on the
// parallel refined mesh of the given polynomial order.
DG_FECollection fec(order, dim, BasisType::GaussLobatto);
DG_FECollection fec(order, dim);
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, &fec);
HYPRE_Int global_vSize = fes->GlobalTrueVSize();
@@ -311,24 +214,8 @@ int main(int argc, char *argv[])
FunctionCoefficient u0(u0_function);
ParBilinearForm *m = new ParBilinearForm(fes);
ParBilinearForm *k = new ParBilinearForm(fes);
if (pa)
{
m->SetAssemblyLevel(AssemblyLevel::PARTIAL);
k->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
else if (ea)
{
m->SetAssemblyLevel(AssemblyLevel::ELEMENT);
k->SetAssemblyLevel(AssemblyLevel::ELEMENT);
}
else if (fa)
{
m->SetAssemblyLevel(AssemblyLevel::FULL);
k->SetAssemblyLevel(AssemblyLevel::FULL);
}
m->AddDomainIntegrator(new MassIntegrator);
ParBilinearForm *k = new ParBilinearForm(fes);
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
k->AddInteriorFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
@@ -339,13 +226,15 @@ int main(int argc, char *argv[])
b->AddBdrFaceIntegrator(
new BoundaryFlowIntegrator(inflow, velocity, -1.0, -0.5));
int skip_zeros = 0;
m->Assemble();
k->Assemble(skip_zeros);
b->Assemble();
m->Finalize();
int skip_zeros = 0;
k->Assemble(skip_zeros);
k->Finalize(skip_zeros);
b->Assemble();
HypreParMatrix *M = m->ParallelAssemble();
HypreParMatrix *K = k->ParallelAssemble();
HypreParVector *B = b->ParallelAssemble();
// 8. Define the initial conditions, save the corresponding grid function to
@@ -384,8 +273,6 @@ int main(int argc, char *argv[])
{
dc = new VisItDataCollection("Example9-Parallel", pmesh);
dc->SetPrecision(precision);
// To save the mesh using MFEM's parallel mesh format:
// dc->SetFormat(DataCollection::PARALLEL_FORMAT);
}
dc->RegisterField("solution", u);
dc->SetCycle(0);
@@ -393,41 +280,6 @@ int main(int argc, char *argv[])
dc->Save();
}
ParaViewDataCollection *pd = NULL;
if (paraview)
{
pd = new ParaViewDataCollection("Example9P", pmesh);
pd->SetPrefixPath("ParaView");
pd->RegisterField("solution", u);
pd->SetLevelsOfDetail(order);
pd->SetDataFormat(VTKFormat::BINARY);
pd->SetHighOrderOutput(true);
pd->SetCycle(0);
pd->SetTime(0.0);
pd->Save();
}
// Optionally output a BP (binary pack) file using ADIOS2. This can be
// visualized with the ParaView VTX reader.
#ifdef MFEM_USE_ADIOS2
ADIOS2DataCollection *adios2_dc = NULL;
if (adios2)
{
std::string postfix(mesh_file);
postfix.erase(0, std::string("../data/").size() );
postfix += "_o" + std::to_string(order);
const std::string collection_name = "ex9-p-" + postfix + ".bp";
adios2_dc = new ADIOS2DataCollection(MPI_COMM_WORLD, collection_name, pmesh);
// output data substreams are half the number of mpi processes
adios2_dc->SetParameter("SubStreams", std::to_string(num_procs/2) );
adios2_dc->RegisterField("solution", u);
adios2_dc->SetCycle(0);
adios2_dc->SetTime(0.0);
adios2_dc->Save();
}
#endif
socketstream sout;
if (visualization)
{
@@ -460,7 +312,7 @@ int main(int argc, char *argv[])
// 9. Define the time-dependent evolution operator describing the ODE
// right-hand side, and define the ODE solver used for time integration.
FE_Evolution adv(*m, *k, *B);
FE_Evolution adv(*M, *K, *B);
double t = 0.0;
adv.SetTime(t);
@@ -532,23 +384,6 @@ int main(int argc, char *argv[])
dc->SetTime(t);
dc->Save();
}
if (paraview)
{
pd->SetCycle(ti);
pd->SetTime(t);
pd->Save();
}
#ifdef MFEM_USE_ADIOS2
// transient solutions can be visualized with ParaView
if (adios2)
{
adios2_dc->SetCycle(ti);
adios2_dc->SetTime(t);
adios2_dc->Save();
}
#endif
}
}
@@ -568,18 +403,13 @@ int main(int argc, char *argv[])
delete u;
delete B;
delete b;
delete K;
delete k;
delete M;
delete m;
delete fes;
delete pmesh;
delete ode_solver;
delete pd;
#ifdef MFEM_USE_ADIOS2
if (adios2)
{
delete adios2_dc;
}
#endif
delete dc;
MPI_Finalize();
@@ -588,43 +418,15 @@ int main(int argc, char *argv[])
// Implementation of class FE_Evolution
FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
FE_Evolution::FE_Evolution(HypreParMatrix &_M, HypreParMatrix &_K,
const Vector &_b)
: TimeDependentOperator(_M.Height()),
b(_b),
M_solver(_M.ParFESpace()->GetComm()),
z(_M.Height())
M(_M), K(_K), b(_b), M_solver(M.GetComm()), z(_M.Height())
{
if (_M.GetAssemblyLevel()==AssemblyLevel::LEGACYFULL)
{
M.Reset(_M.ParallelAssemble(), true);
K.Reset(_K.ParallelAssemble(), true);
}
else
{
M.Reset(&_M, false);
K.Reset(&_K, false);
}
M_prec.SetType(HypreSmoother::Jacobi);
M_solver.SetPreconditioner(M_prec);
M_solver.SetOperator(M);
M_solver.SetOperator(*M);
Array<int> ess_tdof_list;
if (_M.GetAssemblyLevel()==AssemblyLevel::LEGACYFULL)
{
HypreParMatrix &M_mat = *M.As<HypreParMatrix>();
HypreParMatrix &K_mat = *K.As<HypreParMatrix>();
HypreSmoother *hypre_prec = new HypreSmoother(M_mat, HypreSmoother::Jacobi);
M_prec = hypre_prec;
dg_solver = new DG_Solver(M_mat, K_mat, *_M.FESpace());
}
else
{
M_prec = new OperatorJacobiSmoother(_M, ess_tdof_list);
dg_solver = NULL;
}
M_solver.SetPreconditioner(*M_prec);
M_solver.iterative_mode = false;
M_solver.SetRelTol(1e-9);
M_solver.SetAbsTol(0.0);
@@ -632,28 +434,14 @@ FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
M_solver.SetPrintLevel(0);
}
void FE_Evolution::ImplicitSolve(const double dt, const Vector &x, Vector &k)
{
K->Mult(x, z);
z += b;
dg_solver->SetTimeStep(dt);
dg_solver->Mult(z, k);
}
void FE_Evolution::Mult(const Vector &x, Vector &y) const
{
// y = M^{-1} (K x + b)
K->Mult(x, z);
K.Mult(x, z);
z += b;
M_solver.Mult(z, y);
}
FE_Evolution::~FE_Evolution()
{
delete M_prec;
delete dg_solver;
}
// Velocity coefficient
void velocity_function(const Vector &x, Vector &v)
-62
View File
@@ -1,62 +0,0 @@
# Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
set(SUPERLU_EXAMPLES_SRCS)
if (MFEM_USE_MPI)
list(APPEND SUPERLU_EXAMPLES_SRCS
ex1p.cpp
)
endif()
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
include_directories(BEFORE ${PROJECT_BINARY_DIR})
# Add "test_superlu" target, see below.
add_custom_target(test_superlu
${CMAKE_CTEST_COMMAND} -R superlu USES_TERMINAL)
# Add one executable per cpp file, adding "superlu_" as prefix. Sets
# "test_superlu" as a target that depends on the given examples.
set(PFX superlu_)
add_mfem_examples(SUPERLU_EXAMPLES_SRCS ${PFX} "" test_superlu)
# Testing.
# The SuperLU tests can be run separately using the target "test_superlu"
# which builds the examples and runs:
# ctest -R superlu
# Command line options for the tests.
# Example 1: Test SuperLU on the simple Poisson problem
set(EX1_COMMON_OPTS -m ../../data/star.mesh -p 2)
set(EX1P_TEST_OPTS ${EX1_COMMON_OPTS})
# Add the tests: one test per source file.
foreach(SRC_FILE ${SUPERLU_EXAMPLES_SRCS})
get_filename_component(SRC_FILENAME ${SRC_FILE} NAME)
string(REPLACE ".cpp" "" TEST_NAME ${SRC_FILENAME})
string(TOUPPER ${TEST_NAME} UP_TEST_NAME)
set(TEST_NAME ${PFX}${TEST_NAME})
set(THIS_TEST_OPTIONS "-no-vis")
list(APPEND THIS_TEST_OPTIONS ${${UP_TEST_NAME}_TEST_OPTS})
# message(STATUS "Test ${TEST_NAME} options: ${THIS_TEST_OPTIONS}")
if (NOT (${TEST_NAME} MATCHES ".*p$"))
add_test(NAME ${TEST_NAME}_ser
COMMAND ${TEST_NAME} ${THIS_TEST_OPTIONS})
else()
add_test(NAME ${TEST_NAME}_np=4
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:${TEST_NAME}> ${THIS_TEST_OPTIONS}
${MPIEXEC_POSTFLAGS})
endif()
endforeach()
-321
View File
@@ -1,321 +0,0 @@
// MFEM Example 1 - Parallel Version
// SuperLU Modification
//
// Compile with: make ex1p
//
// Sample runs: mpirun -np 4 ex1p -m ../../data/square-disc.mesh
// mpirun -np 4 ex1p -m ../../data/star.mesh
// mpirun -np 4 ex1p -m ../../data/star-mixed.mesh
// mpirun -np 4 ex1p -m ../../data/escher.mesh
// mpirun -np 4 ex1p -m ../../data/fichera.mesh
// mpirun -np 4 ex1p -m ../../data/fichera-mixed.mesh
// mpirun -np 4 ex1p -m ../../data/toroid-wedge.mesh
// mpirun -np 4 ex1p -m ../../data/periodic-annulus-sector.msh
// mpirun -np 4 ex1p -m ../../data/periodic-torus-sector.msh
// mpirun -np 4 ex1p -m ../../data/square-disc-p2.vtk -o 2
// mpirun -np 4 ex1p -m ../../data/square-disc-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../../data/star-mixed-p2.mesh -o 2
// mpirun -np 4 ex1p -m ../../data/disc-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../../data/pipe-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../../data/ball-nurbs.mesh -o 2
// mpirun -np 4 ex1p -m ../../data/star-surf.mesh
// mpirun -np 4 ex1p -m ../../data/square-disc-surf.mesh
// mpirun -np 4 ex1p -m ../../data/inline-segment.mesh
// mpirun -np 4 ex1p -m ../../data/amr-quad.mesh
// mpirun -np 4 ex1p -m ../../data/amr-hex.mesh
// mpirun -np 4 ex1p -m ../../data/mobius-strip.mesh
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
// NURBS mesh, etc.)
//
// The example highlights the use of mesh refinement, finite
// element grid functions, as well as linear and bilinear forms
// corresponding to the left-hand side and right-hand side of the
// discrete linear system. We also cover the explicit elimination
// of essential boundary conditions, static condensation, and the
// optional connection to the GLVis tool for visualization.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
const char *mesh_file = "../../data/star.mesh";
int order = 1;
const char *device_config = "cpu";
bool visualization = true;
int slu_colperm = 4;
int slu_rowperm = 1;
int slu_iterref = 2;
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(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&slu_colperm, "-cp", "--colperm",
"SuperLU Column Permutation Method: 0-NATURAL, 1-MMD-ATA "
"2-MMD_AT_PLUS_A, 3-COLAMD, 4-METIS_AT_PLUS_A, 5-PARMETIS "
"6-ZOLTAN");
args.AddOption(&slu_rowperm, "-rp", "--rowperm",
"SuperLU Row Permutation Method: 0-NOROWPERM, 1-LargeDiag");
args.AddOption(&slu_iterref, "-rp", "--rowperm",
"SuperLU Iterative Refinement: 0-NOREFINE, 1-Single, "
"2-Double, 3-Extra");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// 3. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 4. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
// 5. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 1,000 elements.
{
int ref_levels =
(int)floor(log(1000./mesh.GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh.UniformRefinement();
}
}
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh.UniformRefinement();
}
}
// 7. Define a parallel finite element space on the parallel mesh. Here we
// use continuous Lagrange finite elements of the specified order. If
// order < 1, we instead use an isoparametric/isogeometric space.
FiniteElementCollection *fec;
bool delete_fec;
if (order > 0)
{
fec = new H1_FECollection(order, dim);
delete_fec = true;
}
else if (pmesh.GetNodes())
{
fec = pmesh.GetNodes()->OwnFEC();
delete_fec = false;
if (myid == 0)
{
cout << "Using isoparametric FEs: " << fec->Name() << endl;
}
}
else
{
fec = new H1_FECollection(order = 1, dim);
delete_fec = true;
}
ParFiniteElementSpace fespace(&pmesh, fec);
HYPRE_Int size = fespace.GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 8. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
if (pmesh.bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
ess_bdr = 1;
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 9. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (1,phi_i) where phi_i are the basis functions in fespace.
ParLinearForm b(&fespace);
ConstantCoefficient one(1.0);
b.AddDomainIntegrator(new DomainLFIntegrator(one));
b.Assemble();
// 10. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
ParGridFunction x(&fespace);
x = 0.0;
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
// domain integrator.
ParBilinearForm a(&fespace);
a.AddDomainIntegrator(new DiffusionIntegrator(one));
// 12. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
a.Assemble();
OperatorPtr A;
Vector B, X;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
// 13. Solve the linear system A X = B utilizing SuperLU.
SuperLUSolver *superlu = new SuperLUSolver(MPI_COMM_WORLD);
Operator *SLU_A = new SuperLURowLocMatrix(*A.As<HypreParMatrix>());
superlu->SetPrintStatistics(true);
superlu->SetSymmetricPattern(false);
if (slu_colperm == 0)
{
superlu->SetColumnPermutation(superlu::NATURAL);
}
else if (slu_colperm == 1)
{
superlu->SetColumnPermutation(superlu::MMD_ATA);
}
else if (slu_colperm == 2)
{
superlu->SetColumnPermutation(superlu::MMD_AT_PLUS_A);
}
else if (slu_colperm == 3)
{
superlu->SetColumnPermutation(superlu::COLAMD);
}
else if (slu_colperm == 4)
{
superlu->SetColumnPermutation(superlu::METIS_AT_PLUS_A);
}
else if (slu_colperm == 5)
{
superlu->SetColumnPermutation(superlu::PARMETIS);
}
else if (slu_colperm == 6)
{
superlu->SetColumnPermutation(superlu::ZOLTAN);
}
if (slu_rowperm == 0)
{
superlu->SetRowPermutation(superlu::NOROWPERM);
}
else if (slu_rowperm == 1)
{
#ifdef MFEM_USE_SUPERLU5
superlu->SetRowPermutation(superlu::LargeDiag);
#else
superlu->SetRowPermutation(superlu::LargeDiag_MC64);
#endif
}
if (slu_iterref == 0)
{
superlu->SetIterativeRefine(superlu::NOREFINE);
}
else if (slu_iterref == 1)
{
superlu->SetIterativeRefine(superlu::SLU_SINGLE);
}
else if (slu_iterref == 2)
{
superlu->SetIterativeRefine(superlu::SLU_DOUBLE);
}
else if (slu_iterref == 3)
{
superlu->SetIterativeRefine(superlu::SLU_EXTRA);
}
superlu->SetOperator(*SLU_A);
superlu->SetPrintStatistics(true);
superlu->Mult(B, X);
// 14. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a.RecoverFEMSolution(X, b, x);
// 15. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh.Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
x.Save(sol_ofs);
}
// 16. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << pmesh << x << flush;
}
// 17. Free the used memory.
if (delete_fec)
{
delete fec;
}
MPI_Finalize();
return 0;
}
-79
View File
@@ -1,79 +0,0 @@
# Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/superlu/,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
# Use the MFEM install directory
# MFEM_INSTALL_DIR = ../../mfem
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES =
PAR_EXAMPLES = ex1p
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)
ifeq ($(MFEM_USE_SUPERLU),NO)
$(EXAMPLES):
$(error MFEM is not configured with SuperLU)
endif
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
# Testing: Parallel runs
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
PARALLEL_NAME := Parallel SuperLU example
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI), $(PARALLEL_NAME))
# Testing: Specific execution options:
# Example 1: Test SuperLU on the simple poisson problem
EX1_COMMON_ARGS := -m ../../data/star.mesh
EX1P_ARGS := $(EX1_COMMON_ARGS)
ex1p-test-par: ex1p
@$(call mfem-test,$<, $(RUN_MPI), $(PARALLEL_NAME),$(EX1P_ARGS))
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -f mesh.* sol.*
+105
View File
@@ -0,0 +1,105 @@
#ifndef TESTSTRUMPACK_HPP
#define TESTSTRUMPACK_HPP
#include "mfem.hpp"
using namespace mfem;
using namespace std;
void TestStrumpackConstructor()
{
int num_procs, rank;
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
const int num_loc_rows = 100;
const int first_loc_row = num_loc_rows * rank;
const int glob_nrows = num_loc_rows * num_procs;
const int glob_ncols = glob_nrows;
int *opI = new int[num_loc_rows+1];
for (int i=0; i<num_loc_rows+1; ++i)
{
opI[i] = 0;
}
for (int i=0; i<num_loc_rows; ++i)
{
int nnz_i = 3;
if ((first_loc_row + i) == 0 ||
(first_loc_row + i) == glob_nrows-1) // if first or last row
{
nnz_i = 2;
}
opI[i+1] = opI[i] + nnz_i;
}
const int nnz = opI[num_loc_rows];
int *opJ = new int[nnz];
double *data = new double[nnz];
int cnt = 0;
for (int i=0; i<num_loc_rows; ++i)
{
const int globalRow = first_loc_row + i;
// Diagonal entry
opJ[cnt] = first_loc_row + i;
if (globalRow == 0 || globalRow == glob_nrows-1)
{
data[cnt] = 2.0;
}
else
{
data[cnt] = 3.0;
}
cnt++;
// Upper diagonal
if (globalRow < glob_nrows-1)
{
opJ[cnt] = first_loc_row + i + 1;
data[cnt] = -1.0;
cnt++;
}
// Lower diagonal
if (globalRow > 0)
{
opJ[cnt] = first_loc_row + i - 1;
data[cnt] = -1.0;
cnt++;
}
}
Operator *op = new STRUMPACKRowLocMatrix(MPI_COMM_WORLD, num_loc_rows,
first_loc_row, glob_nrows, glob_ncols, opI, opJ, data);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(0, NULL, MPI_COMM_WORLD);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strumpack->SetOperator(*op);
strumpack->SetFromCommandLine();
Vector x(num_loc_rows);
Vector y(num_loc_rows);
x = 1.0;
strumpack->Mult(x, y);
delete opI;
delete opJ;
delete data;
}
#endif // TESTSTRUMPACK_HPP
+11 -10
View File
@@ -17,7 +17,6 @@ set(SRCS
bilininteg_convection_ea.cpp
bilininteg_dgtrace_pa.cpp
bilininteg_dgtrace_ea.cpp
bilininteg_diffusion_mf.cpp
bilininteg_diffusion_pa.cpp
bilininteg_diffusion_ea.cpp
bilininteg_divergence.cpp
@@ -25,14 +24,11 @@ set(SRCS
bilininteg_hdiv.cpp
bilininteg_vectorfe.cpp
bilininteg_gradient.cpp
bilininteg_mass_mf.cpp
bilininteg_mass_pa.cpp
bilininteg_mass_ea.cpp
bilininteg_transpose_ea.cpp
bilininteg_vecdiffusion.cpp
bilininteg_vecdiffusion_mf.cpp
bilininteg_vecmass.cpp
bilininteg_vecmass_mf.cpp
coefficient.cpp
complex_fem.cpp
convergence.cpp
@@ -46,9 +42,6 @@ set(SRCS
gridfunc.cpp
hybridization.cpp
intrules.cpp
libceed/ceed.cpp
libceed/diffusion.cpp
libceed/mass.cpp
linearform.cpp
lininteg.cpp
multigrid.cpp
@@ -85,9 +78,6 @@ set(HDRS
gridfunc.hpp
hybridization.hpp
intrules.hpp
libceed/ceed.hpp
libceed/diffusion.hpp
libceed/mass.hpp
linearform.hpp
lininteg.hpp
multigrid.hpp
@@ -147,6 +137,17 @@ if (MFEM_USE_MPI)
prestriction.hpp)
endif()
if (MFEM_USE_CEED)
list(APPEND SRCS
libceed/ceed.cpp
libceed/diffusion.cpp
libceed/mass.cpp)
list(APPEND HDRS
libceed/ceed.hpp
libceed/diffusion.hpp
libceed/mass.hpp)
endif()
convert_filenames_to_full_paths(SRCS)
convert_filenames_to_full_paths(HDRS)
+33 -1
View File
@@ -133,7 +133,8 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
ext = new PABilinearFormExtension(this);
break;
case AssemblyLevel::NONE:
ext = new MFBilinearFormExtension(this);
mfem_error("Matrix-free action not supported yet... stay tuned!");
// ext = new MFBilinearFormExtension(this);
break;
default:
mfem_error("Unknown assembly level");
@@ -1770,9 +1771,40 @@ MixedBilinearForm::~MixedBilinearForm()
delete ext;
}
void DiscreteLinearOperator::SetAssemblyLevel(AssemblyLevel assembly_level)
{
if (ext)
{
MFEM_ABORT("the assembly level has already been set!");
}
assembly = assembly_level;
switch (assembly)
{
case AssemblyLevel::FULL:
// Use the original BilinearForm implementation for now
break;
case AssemblyLevel::ELEMENT:
mfem_error("Element assembly not supported yet... stay tuned!");
break;
case AssemblyLevel::PARTIAL:
ext = new PADiscreteLinearOperatorExtension(this);
break;
case AssemblyLevel::NONE:
mfem_error("Matrix-free action not supported yet... stay tuned!");
break;
default:
mfem_error("Unknown assembly level");
}
}
void DiscreteLinearOperator::Assemble(int skip_zeros)
{
if (ext)
{
ext->Assemble();
return;
}
Array<int> dom_vdofs, ran_vdofs;
ElementTransformation *T;
const FiniteElement *dom_fe, *ran_fe;
+37 -7
View File
@@ -157,8 +157,7 @@ public:
/// Set the desired assembly level.
/** Valid choices are:
- AssemblyLevel::LEGACYFULL (default)
- AssemblyLevel::FULL
- AssemblyLevel::FULL (default)
- AssemblyLevel::PARTIAL
- AssemblyLevel::ELEMENT
- AssemblyLevel::NONE
@@ -376,6 +375,9 @@ public:
/// Get the output finite element space prolongation matrix
virtual const Operator *GetOutputProlongation() const
{ return GetProlongation(); }
/// Get the output finite element space prolongation matrix (local diagonal)
virtual const Operator *GetLocalOutputProlongation() const
{ return GetOutputProlongation(); }
/// Get the output finite element space restriction matrix
virtual const Operator *GetOutputRestriction() const
{ return GetRestriction(); }
@@ -416,7 +418,9 @@ public:
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. */
destructor is called.
Currently, this method can be used only with AssemblyLevel::FULL. */
template <typename OpType>
void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x, Vector &b,
OpType &A, Vector &X, Vector &B,
@@ -438,7 +442,9 @@ public:
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. */
destructor is called.
Currently, this method can be used only with AssemblyLevel::FULL. */
template <typename OpType>
void FormSystemMatrix(const Array<int> &ess_tdof_list, OpType &A)
{
@@ -588,6 +594,18 @@ public:
/// Indicate that integrators are not owned by the BilinearForm
void UseExternalIntegrators() { extern_bfs = 1; };
void GetTimings(double& t1, double& t2)
{
t1 = 0.0;
t2 = 0.0;
PABilinearFormExtension *paext = dynamic_cast<PABilinearFormExtension*>(ext);
if (paext != NULL)
{
t1 = paext->timingDomain;
t2 = paext->timingBoundary;
}
}
/// Destroys bilinear form.
virtual ~BilinearForm();
};
@@ -756,7 +774,7 @@ public:
/// Sets all sparse values of \f$ M \f$ to @a a.
void operator=(const double a) { *mat = a; }
/// Set the desired assembly level. The default is AssemblyLevel::LEGACYFULL.
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
/** This method must be called before assembly. */
void SetAssemblyLevel(AssemblyLevel assembly_level);
@@ -858,7 +876,9 @@ public:
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. */
destructor is called.
Currently, this method can be used only with AssemblyLevel::FULL. */
template <typename OpType>
void FormRectangularSystemMatrix(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list, OpType &A)
@@ -888,7 +908,9 @@ public:
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. */
destructor is called.
Currently, this method can be used only with AssemblyLevel::FULL. */
template <typename OpType>
void FormRectangularLinearSystem(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
@@ -977,9 +999,17 @@ public:
/// Access all interpolators added with AddDomainInterpolator().
Array<BilinearFormIntegrator*> *GetDI() { return &dbfi; }
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
/** This method must be called before assembly. */
void SetAssemblyLevel(AssemblyLevel assembly_level);
/** @brief Construct the internal matrix representation of the discrete
linear operator. */
virtual void Assemble(int skip_zeros = 1);
/// Get the output finite element space prolongation matrix (local diagonal)
virtual const Operator *GetLocalOutputProlongation() const
{ return test_fes->GetLocalProlongationMatrix(); }
};
}
+329 -203
View File
@@ -17,6 +17,8 @@
#include "libceed/ceed.hpp"
#include "pgridfunc.hpp"
#include "../general/tic_toc.hpp"
namespace mfem
{
@@ -36,206 +38,6 @@ const Operator *BilinearFormExtension::GetRestriction() const
return a->GetRestriction();
}
// Data and methods for partially-assembled bilinear forms
MFBilinearFormExtension::MFBilinearFormExtension(BilinearForm *form)
: BilinearFormExtension(form),
trialFes(a->FESpace()),
testFes(a->FESpace())
{
elem_restrict = NULL;
int_face_restrict_lex = NULL;
bdr_face_restrict_lex = NULL;
}
void MFBilinearFormExtension::Assemble()
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int integratorCount = integrators.Size();
for (int i = 0; i < integratorCount; ++i)
{
integrators[i]->AssembleMF(*a->FESpace());
}
}
void MFBilinearFormExtension::AssembleDiagonal(Vector &y) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
if (elem_restrict && !DeviceCanUseCeed())
{
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AssembleDiagonalMF(localY);
}
const ElementRestriction* H1elem_restrict =
dynamic_cast<const ElementRestriction*>(elem_restrict);
if (H1elem_restrict)
{
H1elem_restrict->MultTransposeUnsigned(localY, y);
}
else
{
elem_restrict->MultTranspose(localY, y);
}
}
else
{
y.UseDevice(true); // typically this is a large vector, so store on device
y = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AssembleDiagonalMF(y);
}
}
}
void MFBilinearFormExtension::Update()
{
FiniteElementSpace *fes = a->FESpace();
height = width = fes->GetVSize();
trialFes = fes;
testFes = fes;
elem_restrict = nullptr;
int_face_restrict_lex = nullptr;
bdr_face_restrict_lex = nullptr;
}
void MFBilinearFormExtension::FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A)
{
Operator *oper;
Operator::FormSystemOperator(ess_tdof_list, oper);
A.Reset(oper); // A will own oper
}
void MFBilinearFormExtension::FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A,
Vector &X, Vector &B,
int copy_interior)
{
Operator *oper;
Operator::FormLinearSystem(ess_tdof_list, x, b, oper, X, B, copy_interior);
A.Reset(oper); // A will own oper
}
void MFBilinearFormExtension::Mult(const Vector &x, Vector &y) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
if (DeviceCanUseCeed() || !elem_restrict)
{
y.UseDevice(true); // typically this is a large vector, so store on device
y = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultMF(x, y);
}
}
else
{
elem_restrict->Mult(x, localX);
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultMF(localX, localY);
}
elem_restrict->MultTranspose(localY, y);
}
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int iFISz = intFaceIntegrators.Size();
if (int_face_restrict_lex && iFISz>0)
{
int_face_restrict_lex->Mult(x, faceIntX);
if (faceIntX.Size()>0)
{
faceIntY = 0.0;
for (int i = 0; i < iFISz; ++i)
{
intFaceIntegrators[i]->AddMultMF(faceIntX, faceIntY);
}
int_face_restrict_lex->MultTranspose(faceIntY, y);
}
}
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
const int bFISz = bdrFaceIntegrators.Size();
if (bdr_face_restrict_lex && bFISz>0)
{
bdr_face_restrict_lex->Mult(x, faceBdrX);
if (faceBdrX.Size()>0)
{
faceBdrY = 0.0;
for (int i = 0; i < bFISz; ++i)
{
bdrFaceIntegrators[i]->AddMultMF(faceBdrX, faceBdrY);
}
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
}
}
}
void MFBilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
if (elem_restrict)
{
elem_restrict->Mult(x, localX);
localY = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultTransposeMF(localX, localY);
}
elem_restrict->MultTranspose(localY, y);
}
else
{
y.UseDevice(true);
y = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultTransposeMF(x, y);
}
}
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int iFISz = intFaceIntegrators.Size();
if (int_face_restrict_lex && iFISz>0)
{
int_face_restrict_lex->Mult(x, faceIntX);
if (faceIntX.Size()>0)
{
faceIntY = 0.0;
for (int i = 0; i < iFISz; ++i)
{
intFaceIntegrators[i]->AddMultTransposeMF(faceIntX, faceIntY);
}
int_face_restrict_lex->MultTranspose(faceIntY, y);
}
}
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
const int bFISz = bdrFaceIntegrators.Size();
if (bdr_face_restrict_lex && bFISz>0)
{
bdr_face_restrict_lex->Mult(x, faceBdrX);
if (faceBdrX.Size()>0)
{
faceBdrY = 0.0;
for (int i = 0; i < bFISz; ++i)
{
bdrFaceIntegrators[i]->AddMultTransposeMF(faceBdrX, faceBdrY);
}
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
}
}
}
// Data and methods for partially-assembled bilinear forms
PABilinearFormExtension::PABilinearFormExtension(BilinearForm *form)
@@ -246,6 +48,9 @@ PABilinearFormExtension::PABilinearFormExtension(BilinearForm *form)
elem_restrict = NULL;
int_face_restrict_lex = NULL;
bdr_face_restrict_lex = NULL;
timingDomain = 0.0;
timingBoundary = 0.0;
}
void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
@@ -296,8 +101,12 @@ void PABilinearFormExtension::Assemble()
integrators[i]->AssemblePA(*a->FESpace());
}
MFEM_VERIFY(a->GetBBFI()->Size() == 0,
"Partial assembly does not support AddBoundaryIntegrator yet.");
Array<BilinearFormIntegrator*> &bdryIntegrators = *a->GetBBFI();
const int bdryIntegratorCount = bdryIntegrators.Size();
for (int i = 0; i < bdryIntegratorCount; ++i)
{
bdryIntegrators[i]->AssemblePA(*a->FESpace());
}
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int intFaceIntegratorCount = intFaceIntegrators.Size();
@@ -312,6 +121,96 @@ void PABilinearFormExtension::Assemble()
{
bdrFaceIntegrators[i]->AssemblePABoundaryFaces(*a->FESpace());
}
timingDomain = 0.0;
timingBoundary = 0.0;
if (bdryIntegratorCount > 0)
{
FiniteElementSpace *fes = a->GetFES();
const int nbe = fes->GetNBE();
const int bedofs = nbe > 0 ? nbe * fes->GetVDim() * fes->GetBE(0)->GetDof() : 0;
gatherMap.SetSize(bedofs);
indices.SetSize(bedofs);
ndofs = fes->GetNDofs();
offsets.SetSize(ndofs+1);
for (int i = 0; i <= ndofs; ++i)
{
offsets[i] = 0;
}
for (int i = 0; i < nbe; i++)
{
const FiniteElement &be = *fes->GetBE(i);
Array<int> vdofs;
fes -> GetBdrElementVDofs (i, vdofs);
const TensorBasisElement* el =
dynamic_cast<const TensorBasisElement*>(&be);
MFEM_VERIFY(el != NULL, "");
const Array<int> &fe_dof_map = el->GetDofMap();
MFEM_VERIFY(fe_dof_map.Size() == fes->GetBE(i)->GetDof(), "");
MFEM_VERIFY(vdofs.Size() == fes->GetBE(i)->GetDof(), "");
for (int j=0; j<vdofs.Size(); ++j)
{
const int sidj = fe_dof_map[j];
const int idj = sidj >= 0 ? sidj : -1 - sidj;
const int dof_j = vdofs[idj];
const int d = dof_j >= 0 ? dof_j : -1-dof_j;
offsets[d+1]++;
}
}
for (int i = 1; i <= ndofs; ++i)
{
// Partial sum
offsets[i] += offsets[i - 1];
}
int os = 0;
Array<int> cnt(ndofs);
cnt = 0;
indices = 0;
for (int i = 0; i < nbe; i++)
{
const FiniteElement &be = *fes->GetBE(i);
Array<int> vdofs;
fes -> GetBdrElementVDofs (i, vdofs);
const TensorBasisElement* el =
dynamic_cast<const TensorBasisElement*>(&be);
MFEM_VERIFY(el != NULL, "");
const Array<int> &fe_dof_map = el->GetDofMap();
MFEM_VERIFY(fe_dof_map.Size() == fes->GetBE(i)->GetDof(), "");
MFEM_VERIFY(vdofs.Size() == fes->GetBE(i)->GetDof(), "");
for (int j=0; j<vdofs.Size(); ++j)
{
const int sidj = fe_dof_map[j];
const int idj = sidj >= 0 ? sidj : -1 - sidj;
const int dof_j = vdofs[idj];
const bool plus = (sidj >= 0 && dof_j >= 0) || (sidj < 0 && dof_j < 0);
const int d = dof_j >= 0 ? dof_j : -1-dof_j;
const int lid = os + j;
gatherMap[lid] = plus ? d : -1-d;
indices[offsets[d] + cnt[d]] = plus ? lid : -1-lid;
cnt[d]++;
}
os += vdofs.Size();
}
MFEM_VERIFY(os == bedofs, "");
}
}
void PABilinearFormExtension::AssembleDiagonal(Vector &y) const
@@ -379,10 +278,18 @@ void PABilinearFormExtension::FormLinearSystem(const Array<int> &ess_tdof_list,
A.Reset(oper); // A will own oper
}
//#define SWTIMING
void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
#ifdef SWTIMING
StopWatch swd;
swd.Clear();
swd.Start();
#endif
const int iSz = integrators.Size();
if (DeviceCanUseCeed() || !elem_restrict)
{
@@ -404,6 +311,93 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
elem_restrict->MultTranspose(localY, y);
}
#ifdef SWTIMING
swd.Stop();
timingDomain += swd.RealTime();
StopWatch swb;
swb.Clear();
swb.Start();
#endif
Array<BilinearFormIntegrator*> &bdryIntegrators = *a->GetBBFI();
const int biSz = bdryIntegrators.Size();
if (DeviceCanUseCeed() || !elem_restrict)
{
MFEM_ABORT("Not implemented");
}
else if (biSz > 0)
{
FiniteElementSpace *fes = a->GetFES();
const int nbe = fes->GetNBE();
const int bedofs = nbe > 0 ? nbe * fes->GetVDim() * fes->GetBE(0)->GetDof() : 0;
bdryX.SetSize(bedofs);
bdryY.SetSize(bedofs);
bdryX.UseDevice(true);
bdryY.UseDevice(true);
bdryX = 0.0;
bdryY = 0.0;
{
MFEM_VERIFY(gatherMap.Size() == bedofs, "");
auto d_gatherMap = gatherMap.Read();
auto d_x = x.Read();
auto d_bdryX = bdryX.Write();
MFEM_FORALL(i, bedofs,
{
const int gid = d_gatherMap[i];
const bool plus = gid >= 0;
const int j = plus ? gid : -1-gid;
d_bdryX[i] = plus ? d_x[j] : -d_x[j];
});
}
for (int i = 0; i < biSz; ++i)
{
bdryIntegrators[i]->AddMultPA(bdryX, bdryY);
}
//elem_restrict->MultTranspose(bdryY, y);
// bdryY contains quantities on all boundary elements. Now add them to y.
{
const int xsize = x.Size();
MFEM_VERIFY(y.Size() == xsize, "");
MFEM_VERIFY(gatherMap.Size() == bedofs, "");
auto d_offsets = offsets.Read();
auto d_indices = indices.Read();
auto d_y = y.ReadWrite();
auto d_bdryY = bdryY.Read();
MFEM_FORALL(i, ndofs,
{
const int offset = d_offsets[i];
const int nextOffset = d_offsets[i + 1];
double val = 0.0;
for (int j = offset; j < nextOffset; ++j)
{
const int id = d_indices[j];
const bool plus = id >= 0;
const int idj = plus ? id : -1-id;
const double yval = d_bdryY[idj];
val += plus ? yval : -yval;
}
d_y[i] += val;
});
}
}
#ifdef SWTIMING
swb.Stop();
timingBoundary += swb.RealTime();
#endif
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int iFISz = intFaceIntegrators.Size();
if (int_face_restrict_lex && iFISz>0)
@@ -461,6 +455,10 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
}
}
Array<BilinearFormIntegrator*> &bdryIntegrators = *a->GetBBFI();
const int biSz = bdryIntegrators.Size();
MFEM_VERIFY(biSz == 0, "TODO");
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int iFISz = intFaceIntegrators.Size();
if (int_face_restrict_lex && iFISz>0)
@@ -1021,7 +1019,6 @@ void PAMixedBilinearFormExtension::Update()
localTrial.UseDevice(true);
localTrial.SetSize(elem_restrict_trial->Height(),
Device::GetMemoryType());
}
if (elem_restrict_test)
{
@@ -1221,4 +1218,133 @@ void PAMixedBilinearFormExtension::AssembleDiagonal_ADAt(const Vector &D,
}
}
PADiscreteLinearOperatorExtension::PADiscreteLinearOperatorExtension(
DiscreteLinearOperator *linop) :
PAMixedBilinearFormExtension(linop)
{
}
const Operator *PADiscreteLinearOperatorExtension::GetLocalOutputProlongation()
const
{
return a->GetLocalOutputProlongation();
}
void PADiscreteLinearOperatorExtension::Assemble()
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int integratorCount = integrators.Size();
for (int i = 0; i < integratorCount; ++i)
{
integrators[i]->AssemblePA(*trialFes, *testFes);
}
test_multiplicity.UseDevice(true);
test_multiplicity.SetSize(elem_restrict_test->Width()); // l-vector
Vector ones(elem_restrict_test->Height()); // e-vector
ones = 1.0;
const ElementRestriction* elem_restrict =
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
if (elem_restrict)
{
elem_restrict->MultTransposeUnsigned(ones, test_multiplicity);
}
else
{
mfem_error("A real ElementRestriction is required in this setting!");
}
auto tm = test_multiplicity.ReadWrite();
MFEM_FORALL(i, test_multiplicity.Size(),
{
tm[i] = 1.0 / tm[i];
});
}
void PADiscreteLinearOperatorExtension::AddMult(
const Vector &x, Vector &y, const double c) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
// * G operation
SetupMultInputs(elem_restrict_trial, x, localTrial,
elem_restrict_test, y, localTest, c);
// * B^TDB operation
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultPA(localTrial, localTest);
}
// do a kind of "set" rather than "add" in the below
// operation as compared to the BilinearForm case
// * G^T operation (kind of...)
const ElementRestriction* elem_restrict =
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
if (elem_restrict)
{
tempY.SetSize(y.Size());
elem_restrict->MultLeftInverse(localTest, tempY);
y += tempY;
}
else
{
mfem_error("In this setting you need a real ElementRestriction!");
}
}
void PADiscreteLinearOperatorExtension::AddMultTranspose(
const Vector &x, Vector &y, const double c) const
{
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
// do a kind of "set" rather than "add" in the below
// operation as compared to the BilinearForm case
// * G operation (kinda)
Vector xscaled(x);
MFEM_VERIFY(x.Size() == test_multiplicity.Size(), "Input vector of wrong size");
auto xs = xscaled.ReadWrite();
auto tm = test_multiplicity.Read();
MFEM_FORALL(i, x.Size(),
{
xs[i] *= tm[i];
});
SetupMultInputs(elem_restrict_test, xscaled, localTest,
elem_restrict_trial, y, localTrial, c);
// * B^TD^TB operation
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultTransposePA(localTest, localTrial);
}
// * G^T operation
if (elem_restrict_trial)
{
tempY.SetSize(y.Size());
elem_restrict_trial->MultTranspose(localTrial, tempY);
y += tempY;
}
else
{
mfem_error("Trial ElementRestriction not defined");
}
}
void PADiscreteLinearOperatorExtension::FormRectangularSystemOperator(
const Array<int>& ess1, const Array<int>& ess2, OperatorHandle &A)
{
const Operator *Pi = this->GetProlongation();
const Operator *Po = this->GetLocalOutputProlongation();
Operator *rap = SetupRAP(Pi, Po);
RectangularConstrainedOperator *Arco
= new RectangularConstrainedOperator(rap, ess1, ess2, rap != this);
A.Reset(Arco);
}
} // namespace mfem
+47 -19
View File
@@ -21,6 +21,7 @@ namespace mfem
class BilinearForm;
class MixedBilinearForm;
class DiscreteLinearOperator;
/// Class extending the BilinearForm class to support different AssemblyLevels.
/** FA - Full Assembly
@@ -68,11 +69,16 @@ class PABilinearFormExtension : public BilinearFormExtension
protected:
const FiniteElementSpace *trialFes, *testFes; // Not owned
mutable Vector localX, localY;
mutable Vector bdryX, bdryY;
mutable Vector faceIntX, faceIntY;
mutable Vector faceBdrX, faceBdrY;
const Operator *elem_restrict; // Not owned
const Operator *int_face_restrict_lex; // Not owned
const Operator *bdr_face_restrict_lex; // Not owned
Array<int> gatherMap;
Array<int> indices;
Array<int> offsets;
int ndofs;
public:
PABilinearFormExtension(BilinearForm*);
@@ -88,6 +94,8 @@ public:
void MultTranspose(const Vector &x, Vector &y) const;
void Update();
mutable double timingDomain, timingBoundary;
protected:
void SetupRestrictionOperators(const L2FaceValues m);
};
@@ -130,31 +138,24 @@ public:
void MultTranspose(const Vector &x, Vector &y) const;
};
/// Data and methods for matrix-free bilinear forms
/// Data and methods for matrix-free bilinear forms NOT YET IMPLEMENTED.
class MFBilinearFormExtension : public BilinearFormExtension
{
protected:
const FiniteElementSpace *trialFes, *testFes; // Not owned
mutable Vector localX, localY;
mutable Vector faceIntX, faceIntY;
mutable Vector faceBdrX, faceBdrY;
const Operator *elem_restrict; // Not owned
const Operator *int_face_restrict_lex; // Not owned
const Operator *bdr_face_restrict_lex; // Not owned
public:
MFBilinearFormExtension(BilinearForm *form);
MFBilinearFormExtension(BilinearForm *form)
: BilinearFormExtension(form) { }
void Assemble();
void AssembleDiagonal(Vector &diag) const;
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A);
/// TODO
void Assemble() {}
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A) {}
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0);
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
void Update();
int copy_interior = 0) {}
void Mult(const Vector &x, Vector &y) const {}
void MultTranspose(const Vector &x, Vector &y) const {}
void Update() {}
~MFBilinearFormExtension() {}
};
/// Class extending the MixedBilinearForm class to support different AssemblyLevels.
@@ -212,7 +213,7 @@ protected:
mutable Vector localTrial, localTest, tempY;
const Operator *elem_restrict_trial; // Not owned
const Operator *elem_restrict_test; // Not owned
private:
/// Helper function to set up inputs/outputs for Mult or MultTranspose
void SetupMultInputs(const Operator *elem_restrict_x,
const Vector &x, Vector &localX,
@@ -258,6 +259,33 @@ public:
void Update();
};
/**
This acts very much like PAMixedBilinearFormExtension, but its
FormRectangularSystemOperator implementation emulates 'Set' rather than
'Add' in the assembly case.
*/
class PADiscreteLinearOperatorExtension : public PAMixedBilinearFormExtension
{
public:
PADiscreteLinearOperatorExtension(DiscreteLinearOperator *linop);
/// Partial assembly of all internal integrators
void Assemble();
void AddMult(const Vector &x, Vector &y, const double c) const;
void AddMultTranspose(const Vector &x, Vector &y, const double c=1.0) const;
void FormRectangularSystemOperator(const Array<int>&, const Array<int>&,
OperatorHandle& A);
const Operator * GetLocalOutputProlongation() const;
private:
Vector test_multiplicity;
};
}
#endif
+78 -98
View File
@@ -22,14 +22,14 @@ namespace mfem
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&)
{
mfem_error ("BilinearFormIntegrator::AssemblePA(...)\n"
mfem_error ("BilinearFormIntegrator::AssemblePA(fes)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&,
const FiniteElementSpace&)
{
mfem_error ("BilinearFormIntegrator::AssemblePA(...)\n"
mfem_error ("BilinearFormIntegrator::AssemblePA(fes, fes)\n"
" is not implemented for this class.");
}
@@ -92,31 +92,7 @@ void BilinearFormIntegrator::AddMultPA(const Vector &, Vector &) const
void BilinearFormIntegrator::AddMultTransposePA(const Vector &, Vector &) const
{
mfem_error ("BilinearFormIntegrator::MultAssembledTranspose(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleMF(const FiniteElementSpace &fes)
{
mfem_error ("BilinearFormIntegrator::AssembleMF(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AddMultMF(const Vector &, Vector &) const
{
mfem_error ("BilinearFormIntegrator::AddMultMF(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AddMultTransposeMF(const Vector &, Vector &) const
{
mfem_error ("BilinearFormIntegrator::AddMultTransposeMF(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleDiagonalMF(Vector &)
{
mfem_error ("BilinearFormIntegrator::AssembleDiagonalMF(...)\n"
mfem_error ("BilinearFormIntegrator::AddMultTransposePA(...)\n"
" is not implemented for this class.");
}
@@ -157,22 +133,8 @@ void BilinearFormIntegrator::AssembleElementVector(
const FiniteElement &el, ElementTransformation &Tr, const Vector &elfun,
Vector &elvect)
{
// Note: This default implementation is general but not efficient
DenseMatrix elmat;
AssembleElementMatrix(el, Tr, elmat);
elvect.SetSize(elmat.Height());
elmat.Mult(elfun, elvect);
}
void BilinearFormIntegrator::AssembleFaceVector(
const FiniteElement &el1, const FiniteElement &el2,
FaceElementTransformations &Tr, const Vector &elfun, Vector &elvect)
{
// Note: This default implementation is general but not efficient
DenseMatrix elmat;
AssembleFaceMatrix(el1, el2, Tr, elmat);
elvect.SetSize(elmat.Height());
elmat.Mult(elfun, elvect);
mfem_error("BilinearFormIntegrator::AssembleElementVector\n"
" is not implemented for this class.");
}
@@ -554,12 +516,10 @@ void DiffusionIntegrator::AssembleElementMatrix
#ifdef MFEM_THREAD_SAFE
DenseMatrix dshape(nd,dim), dshapedxt(nd,spaceDim), invdfdx(dim,spaceDim);
Vector D(VQ ? VQ->GetVDim() : 0);
#else
dshape.SetSize(nd,dim);
dshapedxt.SetSize(nd,spaceDim);
invdfdx.SetSize(dim,spaceDim);
D.SetSize(VQ ? VQ->GetVDim() : 0);
#endif
elmat.SetSize(nd);
@@ -577,20 +537,7 @@ void DiffusionIntegrator::AssembleElementMatrix
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
Mult(dshape, Trans.AdjugateJacobian(), dshapedxt);
if (MQ)
{
MQ->Eval(invdfdx, Trans, ip);
invdfdx *= w;
Mult(dshapedxt, invdfdx, dshape);
AddMultABt(dshape, dshapedxt, elmat);
}
else if (VQ)
{
VQ->Eval(D, Trans, ip);
D *= w;
AddMultADAt(dshapedxt, D, elmat);
}
else
if (!MQ)
{
if (Q)
{
@@ -598,6 +545,13 @@ void DiffusionIntegrator::AssembleElementMatrix
}
AddMult_a_AAt(w, dshapedxt, elmat);
}
else
{
MQ->Eval(invdfdx, Trans, ip);
invdfdx *= w;
Mult(dshapedxt, invdfdx, dshape);
AddMultABt(dshape, dshapedxt, elmat);
}
}
}
@@ -616,14 +570,12 @@ void DiffusionIntegrator::AssembleElementMatrix2(
DenseMatrix dshape(tr_nd, dim), dshapedxt(tr_nd, spaceDim);
DenseMatrix te_dshape(te_nd, dim), te_dshapedxt(te_nd, spaceDim);
DenseMatrix invdfdx(dim, spaceDim);
Vector D(VQ ? VQ->GetVDim() : 0);
#else
dshape.SetSize(tr_nd, dim);
dshapedxt.SetSize(tr_nd, spaceDim);
te_dshape.SetSize(te_nd, dim);
te_dshapedxt.SetSize(te_nd, spaceDim);
invdfdx.SetSize(dim, spaceDim);
D.SetSize(VQ ? VQ->GetVDim() : 0);
#endif
elmat.SetSize(te_nd, tr_nd);
@@ -643,20 +595,7 @@ void DiffusionIntegrator::AssembleElementMatrix2(
Mult(dshape, invdfdx, dshapedxt);
Mult(te_dshape, invdfdx, te_dshapedxt);
// invdfdx, dshape, and te_dshape no longer needed
if (MQ)
{
MQ->Eval(invdfdx, Trans, ip);
invdfdx *= w;
Mult(te_dshapedxt, invdfdx, te_dshape);
AddMultABt(te_dshape, dshapedxt, elmat);
}
else if (VQ)
{
VQ->Eval(D, Trans, ip);
D *= w;
AddMultADAt(dshapedxt, D, elmat);
}
else
if (!MQ)
{
if (Q)
{
@@ -665,6 +604,13 @@ void DiffusionIntegrator::AssembleElementMatrix2(
dshapedxt *= w;
AddMultABt(te_dshapedxt, dshapedxt, elmat);
}
else
{
MQ->Eval(invdfdx, Trans, ip);
invdfdx *= w;
Mult(te_dshapedxt, invdfdx, te_dshape);
AddMultABt(te_dshape, dshapedxt, elmat);
}
}
}
@@ -676,19 +622,12 @@ void DiffusionIntegrator::AssembleElementVector(
int dim = el.GetDim();
double w;
if (VQ)
{
MFEM_VERIFY(VQ->GetVDim() == dim, "Unexpected dimension for VectorCoefficient");
}
#ifdef MFEM_THREAD_SAFE
DenseMatrix dshape(nd,dim), invdfdx(dim), mq(dim);
Vector D(VQ ? VQ->GetVDim() : 0);
#else
dshape.SetSize(nd,dim);
invdfdx.SetSize(dim);
mq.SetSize(dim);
D.SetSize(VQ ? VQ->GetVDim() : 0);
#endif
vec.SetSize(dim);
pointflux.SetSize(dim);
@@ -707,7 +646,7 @@ void DiffusionIntegrator::AssembleElementVector(
CalcAdjugate(Tr.Jacobian(), invdfdx); // invdfdx = adj(J)
w = ip.weight / Tr.Weight();
if (!MQ && !VQ)
if (!MQ)
{
dshape.MultTranspose(elfun, vec);
invdfdx.MultTranspose(vec, pointflux);
@@ -718,21 +657,11 @@ void DiffusionIntegrator::AssembleElementVector(
}
else
{
dshape.MultTranspose(elfun, pointflux);
invdfdx.MultTranspose(pointflux, vec);
if (MQ)
{
MQ->Eval(mq, Tr, ip);
mq.Mult(vec, pointflux);
}
else
{
VQ->Eval(D, Tr, ip);
for (int j=0; j<dim; ++j)
{
pointflux[j] *= D[j];
}
}
MQ->Eval(mq, Tr, ip);
mq.Mult(vec, pointflux);
}
pointflux *= w;
invdfdx.Mult(pointflux, vec);
@@ -971,6 +900,44 @@ const IntegrationRule &MassIntegrator::GetRule(const FiniteElement &trial_fe,
return IntRules.Get(trial_fe.GetGeomType(), order);
}
/// alpha (n x u, v)
void VectorFEBoundaryTangentIntegrator::AssembleElementMatrix
(const FiniteElement &el, ElementTransformation &Trans,
DenseMatrix &elmat)
{
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, Trans);
const int nd1 = el.GetDof();
DenseMatrix vshape(nd1, 3);
DenseMatrix vshapeRotated(nd1, 3);
elmat.SetSize(nd1);
elmat = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Trans.SetIntPoint (&ip);
Vector n(3);
CalcOrtho(Trans.Jacobian(), n);
el.CalcVShape(Trans, vshape);
for (int j=0; j<nd1; ++j)
{
// Set vshapeRotated(j) = n x vshape
vshapeRotated(j, 0) = (n[1] * vshape(j, 2)) - (n[2] * vshape(j, 1));
vshapeRotated(j, 1) = (n[2] * vshape(j, 0)) - (n[0] * vshape(j, 2));
vshapeRotated(j, 2) = (n[0] * vshape(j, 1)) - (n[1] * vshape(j, 0));
}
const double w = alpha * ip.weight; // Trans.Weight() is included in n
AddMult_a_ABt(w, vshape, vshapeRotated, elmat);
}
}
void BoundaryMassIntegrator::AssembleFaceMatrix(
const FiniteElement &el1, const FiniteElement &el2,
@@ -2265,7 +2232,16 @@ void VectorDiffusionIntegrator::AssembleElementMatrix(
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
ir = &DiffusionIntegrator::GetRule(el,el);
// integrand is rational function if det(J) is not constant
int order = 2 * Trans.OrderGrad(&el); // order of the numerator
if (el.Space() == FunctionSpace::rQk)
{
ir = &RefinedIntRules.Get(el.GetGeomType(), order);
}
else
{
ir = &IntRules.Get(el.GetGeomType(), order);
}
}
elmat = 0.0;
@@ -2316,7 +2292,11 @@ void VectorDiffusionIntegrator::AssembleElementVector(
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
ir = &DiffusionIntegrator::GetRule(el,el);
// integrand is rational function if det(J) is not constant
int order = 2 * Tr.OrderGrad(&el); // order of the numerator
ir = (el.Space() == FunctionSpace::rQk) ?
&RefinedIntRules.Get(el.GetGeomType(), order) :
&IntRules.Get(el.GetGeomType(), order);
}
elvect = 0.0;
+128 -110
View File
@@ -17,6 +17,8 @@
#include "fespace.hpp"
#include "libceed/ceed.hpp"
//#define SETUPONHOST
namespace mfem
{
@@ -95,30 +97,6 @@ public:
// const FiniteElementSpace &test_fes,
// Vector &emat);
/// Method defining matrix-free assembly.
/** The result of fully matrix-free assembly is stored internally so that it
can be used later in the methods AddMultMF() and AddMultTransposeMF(). */
virtual void AssembleMF(const FiniteElementSpace &fes);
/** Perform the action of integrator on the input @a x and add the result to
the output @a y. Both @a x and @a y are E-vectors, i.e. they represent
the element-wise discontinuous version of the FE space.
This method can be called only after the method AssembleMF() has been
called. */
virtual void AddMultMF(const Vector &x, Vector &y) const;
/** Perform the transpose action of integrator on the input @a x and add the
result to the output @a y. Both @a x and @a y are E-vectors, i.e. they
represent the element-wise discontinuous version of the FE space.
This method can be called only after the method AssemblePA() has been
called. */
virtual void AddMultTransposeMF(const Vector &x, Vector &y) const;
/// Assemble diagonal and add it to Vector @a diag.
virtual void AssembleDiagonalMF(Vector &diag);
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
Vector &ea_data_int,
Vector &ea_data_ext,
@@ -155,22 +133,11 @@ public:
FaceElementTransformations &Trans,
DenseMatrix &elmat);
/// @brief Perform the local action of the BilinearFormIntegrator.
/// Note that the default implementation in the base class is general but not
/// efficient.
/// Perform the local action of the BilinearFormIntegrator
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &Tr,
const Vector &elfun, Vector &elvect);
/// @brief Perform the local action of the BilinearFormIntegrator resulting
/// from a face integral term.
/// Note that the default implementation in the base class is general but not
/// efficient.
virtual void AssembleFaceVector(const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &Tr,
const Vector &elfun, Vector &elvect);
virtual void AssembleElementGrad(const FiniteElement &el,
ElementTransformation &Tr,
const Vector &elfun, DenseMatrix &elmat)
@@ -242,6 +209,9 @@ public:
{ return 0.0; }
virtual ~BilinearFormIntegrator() { }
bool isBdryInteg = false;
Array<int> *el_marker = NULL;
};
/** Wraps a given @a BilinearFormIntegrator and transposes the resulting element
@@ -1844,8 +1814,10 @@ protected:
};
/** Class for integrating the bilinear form a(u,v) := (Q grad u, v) where Q is a
scalar coefficient, and v is a vector with components v_i in the same space
as u. */
scalar coefficient, and v is a vector with components v_i in the same (H1) space
as u.
See also MixedVectorGradientIntegrator when v is in H(curl). */
class GradientIntegrator : public BilinearFormIntegrator
{
protected:
@@ -1898,7 +1870,6 @@ class DiffusionIntegrator: public BilinearFormIntegrator
{
protected:
Coefficient *Q;
VectorCoefficient *VQ;
MatrixCoefficient *MQ;
private:
@@ -1906,7 +1877,6 @@ private:
#ifndef MFEM_THREAD_SAFE
DenseMatrix dshape, dshapedxt, invdfdx, mq;
DenseMatrix te_dshape, te_dshapedxt;
Vector D;
#endif
// PA extension
@@ -1915,30 +1885,54 @@ private:
const GeometricFactors *geom; ///< Not owned
int dim, ne, dofs1D, quad1D;
Vector pa_data;
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
#ifdef MFEM_USE_CEED
// CEED extension
CeedData* ceedDataPtr;
#endif
public:
/// Construct a diffusion integrator with coefficient Q = 1
DiffusionIntegrator()
: Q(NULL), VQ(NULL), MQ(NULL), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
{
Q = NULL;
MQ = NULL;
maps = NULL;
geom = NULL;
#ifdef MFEM_USE_CEED
ceedDataPtr = NULL;
#endif
}
/// Construct a diffusion integrator with a scalar coefficient q
DiffusionIntegrator(Coefficient &q)
: Q(&q), VQ(NULL), MQ(NULL), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
/// Construct a diffusion integrator with a vector coefficient q
DiffusionIntegrator(VectorCoefficient &q)
: Q(NULL), VQ(&q), MQ(NULL), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
: Q(&q)
{
MQ = NULL;
maps = NULL;
geom = NULL;
#ifdef MFEM_USE_CEED
ceedDataPtr = NULL;
#endif
}
/// Construct a diffusion integrator with a matrix coefficient q
DiffusionIntegrator(MatrixCoefficient &q)
: Q(NULL), VQ(NULL), MQ(&q), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
: MQ(&q)
{
Q = NULL;
maps = NULL;
geom = NULL;
#ifdef MFEM_USE_CEED
ceedDataPtr = NULL;
#endif
}
virtual ~DiffusionIntegrator()
{
#ifdef MFEM_USE_CEED
delete ceedDataPtr;
#endif
}
/** Given a particular Finite Element computes the element stiffness matrix
@@ -1969,8 +1963,6 @@ public:
using BilinearFormIntegrator::AssemblePA;
virtual void AssembleMF(const FiniteElementSpace &fes);
virtual void AssemblePA(const FiniteElementSpace &fes);
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
@@ -1978,14 +1970,12 @@ public:
virtual void AssembleDiagonalPA(Vector &diag);
virtual void AssembleDiagonalMF(Vector &diag);
virtual void AddMultMF(const Vector&, Vector&) const;
virtual void AddMultPA(const Vector&, Vector&) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe);
void SetupPA(const FiniteElementSpace &fes);
};
/** Class for local mass matrix assembling a(u,v) := (Q u, v) */
@@ -2003,22 +1993,39 @@ protected:
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq, dofs1D, quad1D;
#ifdef MFEM_USE_CEED
// CEED extension
CeedData* ceedDataPtr;
#endif
public:
MassIntegrator(const IntegrationRule *ir = NULL)
: BilinearFormIntegrator(ir), Q(NULL), maps(NULL), geom(NULL),
ceedDataPtr(NULL) { }
: BilinearFormIntegrator(ir)
{
Q = NULL;
maps = NULL;
geom = NULL;
#ifdef MFEM_USE_CEED
ceedDataPtr = NULL;
#endif
}
/// Construct a mass integrator with coefficient q
MassIntegrator(Coefficient &q, const IntegrationRule *ir = NULL)
: BilinearFormIntegrator(ir), Q(&q), maps(NULL), geom(NULL),
ceedDataPtr(NULL) { }
: BilinearFormIntegrator(ir), Q(&q)
{
maps = NULL;
geom = NULL;
#ifdef MFEM_USE_CEED
ceedDataPtr = NULL;
#endif
}
virtual ~MassIntegrator()
{
#ifdef MFEM_USE_CEED
delete ceedDataPtr;
#endif
}
/** Given a particular Finite Element computes the element mass matrix
elmat. */
@@ -2032,8 +2039,6 @@ public:
using BilinearFormIntegrator::AssemblePA;
virtual void AssembleMF(const FiniteElementSpace &fes);
virtual void AssemblePA(const FiniteElementSpace &fes);
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
@@ -2041,10 +2046,6 @@ public:
virtual void AssembleDiagonalPA(Vector &diag);
virtual void AssembleDiagonalMF(Vector &diag);
virtual void AddMultMF(const Vector&, Vector&) const;
virtual void AddMultPA(const Vector&, Vector&) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
@@ -2068,6 +2069,20 @@ public:
DenseMatrix &elmat);
};
/// alpha (n x u, v)
class VectorFEBoundaryTangentIntegrator : public MassIntegrator
{
private:
double alpha;
public:
VectorFEBoundaryTangentIntegrator(double a = 1.0) : alpha(a)
{ }
void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans, DenseMatrix &elmat);
};
/// alpha (q . grad u, v)
class ConvectionIntegrator : public BilinearFormIntegrator
{
@@ -2151,34 +2166,24 @@ protected:
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq, dofs1D, quad1D;
// CEED extension
CeedData* ceedDataPtr;
public:
/// Construct an integrator with coefficient 1.0
VectorMassIntegrator()
: vdim(-1), Q_order(0), Q(NULL), VQ(NULL), MQ(NULL), ceedDataPtr(NULL) { }
: vdim(-1), Q_order(0), Q(NULL), VQ(NULL), MQ(NULL) { }
/** Construct an integrator with scalar coefficient q. If possible, save
memory by using a scalar integrator since the resulting matrix is block
diagonal with the same diagonal block repeated. */
VectorMassIntegrator(Coefficient &q, int qo = 0)
: vdim(-1), Q_order(qo), Q(&q), VQ(NULL), MQ(NULL), ceedDataPtr(NULL) { }
: vdim(-1), Q(&q) { VQ = NULL; MQ = NULL; Q_order = qo; }
VectorMassIntegrator(Coefficient &q, const IntegrationRule *ir)
: BilinearFormIntegrator(ir), vdim(-1), Q_order(0), Q(&q), VQ(NULL),
MQ(NULL), ceedDataPtr(NULL) { }
: BilinearFormIntegrator(ir), vdim(-1), Q(&q)
{ VQ = NULL; MQ = NULL; Q_order = 0; }
/// Construct an integrator with diagonal coefficient q
VectorMassIntegrator(VectorCoefficient &q, int qo = 0)
: vdim(q.GetVDim()), Q_order(qo), Q(NULL), VQ(&q), MQ(NULL),
ceedDataPtr(NULL) { }
: vdim(q.GetVDim()), VQ(&q) { Q = NULL; MQ = NULL; Q_order = qo; }
/// Construct an integrator with matrix coefficient q
VectorMassIntegrator(MatrixCoefficient &q, int qo = 0)
: vdim(q.GetVDim()), Q_order(qo), Q(NULL), VQ(NULL), MQ(&q),
ceedDataPtr(NULL) { }
virtual ~VectorMassIntegrator()
{
delete ceedDataPtr;
}
: vdim(q.GetVDim()), MQ(&q) { Q = NULL; VQ = NULL; Q_order = qo; }
int GetVDim() const { return vdim; }
void SetVDim(int vdim) { this->vdim = vdim; }
@@ -2192,11 +2197,8 @@ public:
DenseMatrix &elmat);
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace &fes);
virtual void AssembleMF(const FiniteElementSpace &fes);
virtual void AssembleDiagonalPA(Vector &diag);
virtual void AssembleDiagonalMF(Vector &diag);
virtual void AddMultPA(const Vector &x, Vector &y) const;
virtual void AddMultMF(const Vector &x, Vector &y) const;
};
@@ -2560,23 +2562,13 @@ protected:
int dim, sdim, ne, dofs1D, quad1D;
Vector pa_data;
// CEED extension
CeedData* ceedDataPtr;
private:
DenseMatrix dshape, dshapedxt, pelmat;
DenseMatrix Jinv, gshape;
public:
VectorDiffusionIntegrator()
: Q(NULL), ceedDataPtr(NULL) { }
VectorDiffusionIntegrator(Coefficient &q)
: Q(&q), ceedDataPtr(NULL) { }
virtual ~VectorDiffusionIntegrator()
{
delete ceedDataPtr;
}
VectorDiffusionIntegrator() { Q = NULL; }
VectorDiffusionIntegrator(Coefficient &q) { Q = &q; }
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
@@ -2586,11 +2578,8 @@ public:
const Vector &elfun, Vector &elvect);
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace &fes);
virtual void AssembleMF(const FiniteElementSpace &fes);
virtual void AssembleDiagonalPA(Vector &diag);
virtual void AssembleDiagonalMF(Vector &diag);
virtual void AddMultPA(const Vector &x, Vector &y) const;
virtual void AddMultMF(const Vector &x, Vector &y) const;
};
/** Integrator for the linear elasticity form:
@@ -2910,11 +2899,33 @@ class DiscreteInterpolator : public BilinearFormIntegrator { };
class GradientInterpolator : public DiscreteInterpolator
{
public:
GradientInterpolator() : fake_fe(NULL) { }
virtual ~GradientInterpolator() { delete fake_fe; }
virtual void AssembleElementMatrix2(const FiniteElement &h1_fe,
const FiniteElement &nd_fe,
ElementTransformation &Trans,
DenseMatrix &elmat)
{ nd_fe.ProjectGrad(h1_fe, Trans, elmat); }
using BilinearFormIntegrator::AssemblePA;
/**
trial_fes should be H1 Lagrange
test_fes should be Nedelec
*/
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes);
virtual void AddMultPA(const Vector &x, Vector &y) const;
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
private:
/// 1D finit element that generates and owns the 1D DofToQuad maps below
FiniteElement * fake_fe;
const DofToQuad *maps_C_C; // one-d map with Lobatto rows, Lobatto columns
const DofToQuad *maps_O_C; // one-d map with Legendre rows, Lobatto columns
int dim, ne, o_dofs1D, c_dofs1D;
};
@@ -2929,6 +2940,24 @@ public:
ElementTransformation &Trans,
DenseMatrix &elmat)
{ ran_fe.Project(dom_fe, Trans, elmat); }
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes);
virtual void AddMultPA(const Vector &x, Vector &y) const;
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
private:
/// 1D finit element that generates and owns the 1D DofToQuad maps below
FiniteElement * fake_fe;
const DofToQuad *maps_C_C; // one-d map with Lobatto rows, Lobatto columns
const DofToQuad *maps_O_C; // one-d map with Legendre rows, Lobatto columns
int dim, ne, o_dofs1D, c_dofs1D;
Vector pa_data;
};
@@ -3061,17 +3090,6 @@ protected:
VectorCoefficient *VQ;
};
// PA Diffusion Assemble 2D kernel
template<const int T_SDIM>
void PADiffusionSetup2D(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d);
}
#endif
-70
View File
@@ -1,70 +0,0 @@
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "libceed/diffusion.hpp"
using namespace std;
namespace mfem
{
void DiffusionIntegrator::AssembleMF(const FiniteElementSpace &fes)
{
#ifdef MFEM_USE_CEED
// Assuming the same element type
fespace = &fes;
Mesh *mesh = fes.GetMesh();
if (mesh->GetNE() == 0) { return; }
const FiniteElement &el = *fes.GetFE(0);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
if (DeviceCanUseCeed())
{
delete ceedDataPtr;
ceedDataPtr = new CeedData;
InitCeedCoeff(Q, *mesh, *ir, ceedDataPtr);
return CeedMFDiffusionAssemble(fes, *ir, *ceedDataPtr);
}
#endif
mfem_error("Error: DiffusionIntegrator::AssembleMF only implemented with libCEED");
}
void DiffusionIntegrator::AssembleDiagonalMF(Vector &diag)
{
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
CeedAssembleDiagonal(ceedDataPtr, diag);
}
else
#endif
{
mfem_error("Error: DiffusionIntegrator::AssembleDiagonalMF only implemented with libCEED");
}
}
void DiffusionIntegrator::AddMultMF(const Vector &x, Vector &y) const
{
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
CeedAddMult(ceedDataPtr, x, y);
}
else
#endif
{
mfem_error("Error: DiffusionIntegrator::AddMultMF only implemented with libCEED");
}
}
}
+145 -312
View File
@@ -80,24 +80,28 @@ static void OccaPADiffusionSetup3D(const int D1D,
}
#endif // MFEM_USE_OCCA
// PA Diffusion Assemble 2D kernel
template<const int T_SDIM>
static void PADiffusionSetup2D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d);
template<>
void PADiffusionSetup2D<2>(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
{
const bool symmetric = (coeffDim != 4);
const bool const_c = c.Size() == 1;
MFEM_VERIFY(coeffDim < 3 ||
!const_c, "Constant matrix coefficient not supported");
const auto W = Reshape(w.Read(), Q1D,Q1D);
const auto J = Reshape(j.Read(), Q1D,Q1D,2,2,NE);
const auto C = const_c ? Reshape(c.Read(), 1,1,1,1) :
Reshape(c.Read(), coeffDim,Q1D,Q1D,NE);
auto D = Reshape(d.Write(), Q1D,Q1D, symmetric ? 3 : 4, NE);
const auto C = const_c ? Reshape(c.Read(), 1,1,1) :
Reshape(c.Read(), Q1D,Q1D,NE);
auto D = Reshape(d.Write(), Q1D,Q1D, 3, NE);
MFEM_FORALL_2D(e, NE, Q1D,Q1D,1,
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
@@ -108,39 +112,11 @@ void PADiffusionSetup2D<2>(const int Q1D,
const double J21 = J(qx,qy,1,0,e);
const double J12 = J(qx,qy,0,1,e);
const double J22 = J(qx,qy,1,1,e);
const double w_detJ = W(qx,qy) / ((J11*J22)-(J21*J12));
if (coeffDim == 3 || coeffDim == 4) // Matrix coefficient
{
// First compute entries of R = MJ^{-T}, without det J factor.
const double M11 = C(0,qx,qy,e);
const double M12 = C(1,qx,qy,e);
const double M21 = symmetric ? M12 : C(2,qx,qy,e);
const double M22 = symmetric ? C(2,qx,qy,e) : C(3,qx,qy,e);
const double R11 = M11*J22 - M12*J12;
const double R21 = M21*J22 - M22*J12;
const double R12 = -M11*J21 + M12*J11;
const double R22 = -M21*J21 + M22*J11;
// Now set y to J^{-1}R.
D(qx,qy,0,e) = w_detJ * ( J22*R11 - J12*R21); // 1,1
D(qx,qy,1,e) = w_detJ * (-J21*R11 + J11*R21); // 2,1
D(qx,qy,2,e) = w_detJ * (symmetric ? (-J21*R12 + J11*R22) :
(J22*R12 - J12*R22)); // 2,2 or 1,2
if (!symmetric)
{
D(qx,qy,3,e) = w_detJ * (-J21*R12 + J11*R22); // 2,2
}
}
else // Vector or scalar coefficient
{
const double C1 = const_c ? C(0,0,0,0) : C(0,qx,qy,e);
const double C2 = const_c ? C(0,0,0,0) :
(coeffDim == 2 ? C(1,qx,qy,e) : C(0,qx,qy,e));
D(qx,qy,0,e) = w_detJ * (C2*J12*J12 + C1*J22*J22); // 1,1
D(qx,qy,1,e) = -w_detJ * (C2*J12*J11 + C1*J22*J21); // 1,2
D(qx,qy,2,e) = w_detJ * (C2*J11*J11 + C1*J21*J21); // 2,2
}
const double coeff = const_c ? C(0,0,0) : C(qx,qy,e);
const double c_detJ = W(qx,qy) * coeff / ((J11*J22)-(J21*J12));
D(qx,qy,0,e) = c_detJ * (J12*J12 + J22*J22); // 1,1
D(qx,qy,1,e) = -c_detJ * (J12*J11 + J22*J21); // 1,2
D(qx,qy,2,e) = c_detJ * (J11*J11 + J21*J21); // 2,2
}
}
});
@@ -149,14 +125,12 @@ void PADiffusionSetup2D<2>(const int Q1D,
// PA Diffusion Assemble 2D kernel with 3D node coords
template<>
void PADiffusionSetup2D<3>(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
{
MFEM_VERIFY(coeffDim == 1, "Matrix and vector coefficients not supported");
constexpr int DIM = 2;
constexpr int SDIM = 3;
const bool const_c = c.Size() == 1;
@@ -193,23 +167,19 @@ void PADiffusionSetup2D<3>(const int Q1D,
}
// PA Diffusion Assemble 3D kernel
void PADiffusionSetup3D(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
static void PADiffusionSetup3D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &c,
Vector &d)
{
const bool symmetric = (coeffDim != 9);
const bool const_c = c.Size() == 1;
MFEM_VERIFY(coeffDim < 6 ||
!const_c, "Constant matrix coefficient not supported");
const auto W = Reshape(w.Read(), Q1D,Q1D,Q1D);
const auto J = Reshape(j.Read(), Q1D,Q1D,Q1D,3,3,NE);
const auto C = const_c ? Reshape(c.Read(), 1,1,1,1,1) :
Reshape(c.Read(), coeffDim,Q1D,Q1D,Q1D,NE);
auto D = Reshape(d.Write(), Q1D,Q1D,Q1D, symmetric ? 6 : 9, NE);
const auto C = const_c ? Reshape(c.Read(), 1,1,1,1) :
Reshape(c.Read(), Q1D,Q1D,Q1D,NE);
auto D = Reshape(d.Write(), Q1D,Q1D,Q1D, 6, NE);
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
@@ -230,7 +200,8 @@ void PADiffusionSetup3D(const int Q1D,
const double detJ = J11 * (J22 * J33 - J32 * J23) -
/* */ J21 * (J12 * J33 - J32 * J13) +
/* */ J31 * (J12 * J23 - J22 * J13);
const double w_detJ = W(qx,qy,qz) / detJ;
const double coeff = const_c ? C(0,0,0,0) : C(qx,qy,qz,e);
const double c_detJ = W(qx,qy,qz) * coeff / detJ;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
const double A12 = (J32 * J13) - (J12 * J33);
@@ -241,69 +212,13 @@ void PADiffusionSetup3D(const int Q1D,
const double A31 = (J21 * J32) - (J31 * J22);
const double A32 = (J31 * J12) - (J11 * J32);
const double A33 = (J11 * J22) - (J12 * J21);
if (coeffDim == 6 || coeffDim == 9) // Matrix coefficient version
{
// Compute entries of R = MJ^{-T} = M adj(J)^T, without det J.
const double M11 = C(0, qx,qy,qz, e);
const double M12 = C(1, qx,qy,qz, e);
const double M13 = C(2, qx,qy,qz, e);
const double M21 = (!symmetric) ? C(3, qx,qy,qz, e) : M12;
const double M22 = (!symmetric) ? C(4, qx,qy,qz, e) : C(3, qx,qy,qz, e);
const double M23 = (!symmetric) ? C(5, qx,qy,qz, e) : C(4, qx,qy,qz, e);
const double M31 = (!symmetric) ? C(6, qx,qy,qz, e) : M13;
const double M32 = (!symmetric) ? C(7, qx,qy,qz, e) : M23;
const double M33 = (!symmetric) ? C(8, qx,qy,qz, e) : C(5, qx,qy,qz, e);
const double R11 = M11*A11 + M12*A12 + M13*A13;
const double R12 = M11*A21 + M12*A22 + M13*A23;
const double R13 = M11*A31 + M12*A32 + M13*A33;
const double R21 = M21*A11 + M22*A12 + M23*A13;
const double R22 = M21*A21 + M22*A22 + M23*A23;
const double R23 = M21*A31 + M22*A32 + M23*A33;
const double R31 = M31*A11 + M32*A12 + M33*A13;
const double R32 = M31*A21 + M32*A22 + M33*A23;
const double R33 = M31*A31 + M32*A32 + M33*A33;
// Now set D to J^{-1} R = adj(J) R
D(qx,qy,qz,0,e) = w_detJ * (A11*R11 + A12*R21 + A13*R31); // 1,1
const double D12 = w_detJ * (A11*R12 + A12*R22 + A13*R32);
D(qx,qy,qz,1,e) = D12; // 1,2
D(qx,qy,qz,2,e) = w_detJ * (A11*R13 + A12*R23 + A13*R33); // 1,3
const double D21 = w_detJ * (A21*R11 + A22*R21 + A23*R31);
const double D22 = w_detJ * (A21*R12 + A22*R22 + A23*R32);
const double D23 = w_detJ * (A21*R13 + A22*R23 + A23*R33);
const double D33 = w_detJ * (A31*R13 + A32*R23 + A33*R33);
D(qx,qy,qz,3,e) = symmetric ? D22 : D21; // 2,2 or 2,1
D(qx,qy,qz,4,e) = symmetric ? D23 : D22; // 2,3 or 2,2
D(qx,qy,qz,5,e) = symmetric ? D33 : D23; // 3,3 or 2,3
if (!symmetric)
{
D(qx,qy,qz,6,e) = w_detJ * (A31*R11 + A32*R21 + A33*R31); // 3,1
D(qx,qy,qz,7,e) = w_detJ * (A31*R12 + A32*R22 + A33*R32); // 3,2
D(qx,qy,qz,8,e) = D33; // 3,3
}
}
else // Vector or scalar coefficient version
{
const double C1 = const_c ? C(0,0,0,0,0) : C(0,qx,qy,qz,e);
const double C2 = const_c ? C(0,0,0,0,0) :
(coeffDim == 3 ? C(1,qx,qy,qz,e) : C(0,qx,qy,qz,e));
const double C3 = const_c ? C(0,0,0,0,0) :
(coeffDim == 3 ? C(2,qx,qy,qz,e) : C(0,qx,qy,qz,e));
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
D(qx,qy,qz,0,e) = w_detJ * (C1*A11*A11 + C2*A12*A12 + C3*A13*A13); // 1,1
D(qx,qy,qz,1,e) = w_detJ * (C1*A11*A21 + C2*A12*A22 + C3*A13*A23); // 2,1
D(qx,qy,qz,2,e) = w_detJ * (C1*A11*A31 + C2*A12*A32 + C3*A13*A33); // 3,1
D(qx,qy,qz,3,e) = w_detJ * (C1*A21*A21 + C2*A22*A22 + C3*A23*A23); // 2,2
D(qx,qy,qz,4,e) = w_detJ * (C1*A21*A31 + C2*A22*A32 + C3*A23*A33); // 3,2
D(qx,qy,qz,5,e) = w_detJ * (C1*A31*A31 + C2*A32*A32 + C3*A33*A33); // 3,3
}
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
D(qx,qy,qz,0,e) = c_detJ * (A11*A11 + A12*A12 + A13*A13); // 1,1
D(qx,qy,qz,1,e) = c_detJ * (A11*A21 + A12*A22 + A13*A23); // 2,1
D(qx,qy,qz,2,e) = c_detJ * (A11*A31 + A12*A32 + A13*A33); // 3,1
D(qx,qy,qz,3,e) = c_detJ * (A21*A21 + A22*A22 + A23*A23); // 2,2
D(qx,qy,qz,4,e) = c_detJ * (A21*A31 + A22*A32 + A23*A33); // 3,2
D(qx,qy,qz,5,e) = c_detJ * (A31*A31 + A32*A32 + A33*A33); // 3,3
}
}
}
@@ -314,7 +229,6 @@ static void PADiffusionSetup(const int dim,
const int sdim,
const int D1D,
const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &W,
const Vector &J,
@@ -333,8 +247,8 @@ static void PADiffusionSetup(const int dim,
#else
MFEM_CONTRACT_VAR(D1D);
#endif // MFEM_USE_OCCA
if (sdim == 2) { PADiffusionSetup2D<2>(Q1D, coeffDim, NE, W, J, C, D); }
if (sdim == 3) { PADiffusionSetup2D<3>(Q1D, coeffDim, NE, W, J, C, D); }
if (sdim == 2) { PADiffusionSetup2D<2>(Q1D, NE, W, J, C, D); }
if (sdim == 3) { PADiffusionSetup2D<3>(Q1D, NE, W, J, C, D); }
}
if (dim == 3)
{
@@ -345,11 +259,11 @@ static void PADiffusionSetup(const int dim,
return;
}
#endif // MFEM_USE_OCCA
PADiffusionSetup3D(Q1D, coeffDim, NE, W, J, C, D);
PADiffusionSetup3D(Q1D, NE, W, J, C, D);
}
}
void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
void DiffusionIntegrator::SetupPA(const FiniteElementSpace &fes)
{
// Assuming the same element type
fespace = &fes;
@@ -357,13 +271,16 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
if (mesh->GetNE() == 0) { return; }
const FiniteElement &el = *fes.GetFE(0);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
delete ceedDataPtr;
ceedDataPtr = new CeedData;
InitCeedCoeff(Q, *mesh, *ir, ceedDataPtr);
return CeedPADiffusionAssemble(fes, *ir, *ceedDataPtr);
if (ceedDataPtr) { delete ceedDataPtr; }
CeedData* ptr = new CeedData();
ceedDataPtr = ptr;
InitCeedCoeff(Q, ptr);
return CeedPADiffusionAssemble(fes, *ir, *ptr);
}
#endif
const int dims = el.GetDim();
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
@@ -374,80 +291,9 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
int coeffDim = 1;
pa_data.SetSize(symmDims * nq * ne, Device::GetDeviceMemoryType());
Vector coeff;
const int MQfullDim = MQ ? MQ->GetHeight() * MQ->GetWidth() : 0;
if (MQ)
{
MFEM_VERIFY(MQ->GetHeight() == dim && MQ->GetWidth() == dim, "");
const int MQsymmDim = MQ->GetWidth() * (MQ->GetWidth() + 1) / 2;
const int MQdim = MQ->IsSymmetric() ? MQsymmDim : MQfullDim;
coeffDim = MQdim;
coeff.SetSize(MQdim * nq * ne);
symmetric = MQ ? MQ->IsSymmetric() : true;
DenseMatrix M;
Vector Msymm;
if (symmetric)
{
Msymm.SetSize(MQsymmDim);
}
else
{
M.SetSize(dim);
}
auto C = Reshape(coeff.HostWrite(), MQdim, nq, ne);
for (int e=0; e<ne; ++e)
{
ElementTransformation *tr = mesh->GetElementTransformation(e);
for (int p=0; p<nq; ++p)
{
if (MQ->IsSymmetric())
{
MQ->EvalSymmetric(Msymm, *tr, ir->IntPoint(p));
for (int i=0; i<MQsymmDim; ++i)
{
C(i, p, e) = Msymm[i];
}
}
else
{
MQ->Eval(M, *tr, ir->IntPoint(p));
for (int i=0; i<dim; ++i)
for (int j=0; j<dim; ++j)
{
C(j+(i*dim), p, e) = M(i,j);
}
}
}
}
}
else if (VQ)
{
MFEM_VERIFY(VQ->GetVDim() == dim, "");
coeffDim = VQ->GetVDim();
coeff.SetSize(coeffDim * nq * ne);
auto C = Reshape(coeff.HostWrite(), coeffDim, nq, ne);
Vector D(coeffDim);
for (int e=0; e<ne; ++e)
{
ElementTransformation *tr = mesh->GetElementTransformation(e);
for (int p=0; p<nq; ++p)
{
VQ->Eval(D, *tr, ir->IntPoint(p));
for (int i=0; i<coeffDim; ++i)
{
C(i, p, e) = D[i];
}
}
}
}
else if (Q == nullptr)
if (Q == nullptr)
{
coeff.SetSize(1);
coeff(0) = 1.0;
@@ -483,15 +329,18 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
}
}
}
pa_data.SetSize((symmetric ? symmDims : MQfullDim) * nq * ne,
Device::GetDeviceMemoryType());
PADiffusionSetup(dim, sdim, dofs1D, quad1D, coeffDim, ne, ir->GetWeights(),
geom->J, coeff, pa_data);
PADiffusionSetup(dim, sdim, dofs1D, quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
SetupPA(fes);
}
template<int T_D1D = 0, int T_Q1D = 0>
static void PADiffusionDiagonal2D(const int NE,
const bool symmetric,
const Array<double> &b,
const Array<double> &g,
const Vector &d,
@@ -505,9 +354,9 @@ static void PADiffusionDiagonal2D(const int NE,
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
// note the different shape for D, if this is a symmetric matrix we only
// note the different shape for D, this is a (symmetric) matrix so we only
// store necessary entries
auto D = Reshape(d.Read(), Q1D*Q1D, symmetric ? 3 : 4, NE);
auto D = Reshape(d.Read(), Q1D*Q1D, 3, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, NE);
MFEM_FORALL(e, NE,
{
@@ -529,13 +378,12 @@ static void PADiffusionDiagonal2D(const int NE,
for (int qy = 0; qy < Q1D; ++qy)
{
const int q = qx + qy * Q1D;
const double D00 = D(q,0,e);
const double D10 = D(q,1,e);
const double D01 = symmetric ? D10 : D(q,2,e);
const double D11 = symmetric ? D(q,2,e) : D(q,3,e);
QD0[qx][dy] += B(qy, dy) * B(qy, dy) * D00;
QD1[qx][dy] += B(qy, dy) * G(qy, dy) * (D01 + D10);
QD2[qx][dy] += G(qy, dy) * G(qy, dy) * D11;
const double D0 = D(q,0,e);
const double D1 = D(q,1,e);
const double D2 = D(q,2,e);
QD0[qx][dy] += B(qy, dy) * B(qy, dy) * D0;
QD1[qx][dy] += B(qy, dy) * G(qy, dy) * D1;
QD2[qx][dy] += G(qy, dy) * G(qy, dy) * D2;
}
}
}
@@ -547,6 +395,7 @@ static void PADiffusionDiagonal2D(const int NE,
{
Y(dx,dy,e) += G(qx, dx) * G(qx, dx) * QD0[qx][dy];
Y(dx,dy,e) += G(qx, dx) * B(qx, dx) * QD1[qx][dy];
Y(dx,dy,e) += B(qx, dx) * G(qx, dx) * QD1[qx][dy];
Y(dx,dy,e) += B(qx, dx) * B(qx, dx) * QD2[qx][dy];
}
}
@@ -557,7 +406,6 @@ static void PADiffusionDiagonal2D(const int NE,
// Shared memory PA Diffusion Diagonal 2D kernel
template<int T_D1D = 0, int T_Q1D = 0, int T_NBZ = 0>
static void SmemPADiffusionDiagonal2D(const int NE,
const bool symmetric,
const Array<double> &b_,
const Array<double> &g_,
const Vector &d_,
@@ -574,7 +422,7 @@ static void SmemPADiffusionDiagonal2D(const int NE,
MFEM_VERIFY(Q1D <= MQ1, "");
auto b = Reshape(b_.Read(), Q1D, D1D);
auto g = Reshape(g_.Read(), Q1D, D1D);
auto D = Reshape(d_.Read(), Q1D*Q1D, symmetric ? 3 : 4, NE);
auto D = Reshape(d_.Read(), Q1D*Q1D, 3, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
MFEM_FORALL_2D(e, NE, Q1D, Q1D, NBZ,
{
@@ -587,10 +435,10 @@ static void SmemPADiffusionDiagonal2D(const int NE,
MFEM_SHARED double BG[2][MQ1*MD1];
double (*B)[MD1] = (double (*)[MD1]) (BG+0);
double (*G)[MD1] = (double (*)[MD1]) (BG+1);
MFEM_SHARED double QD[3][NBZ][MD1][MQ1];
MFEM_SHARED double QD[4][NBZ][MD1][MQ1];
double (*QD0)[MD1] = (double (*)[MD1])(QD[0] + tidz);
double (*QD1)[MD1] = (double (*)[MD1])(QD[1] + tidz);
double (*QD2)[MD1] = (double (*)[MD1])(QD[2] + tidz);
double (*QD2)[MD1] = (double (*)[MD1])(QD[3] + tidz);
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d,y,D1D)
@@ -613,18 +461,17 @@ static void SmemPADiffusionDiagonal2D(const int NE,
for (int qy = 0; qy < Q1D; ++qy)
{
const int q = qx + qy * Q1D;
const double D00 = D(q,0,e);
const double D10 = D(q,1,e);
const double D01 = symmetric ? D10 : D(q,2,e);
const double D11 = symmetric ? D(q,2,e) : D(q,3,e);
const double D0 = D(q,0,e);
const double D1 = D(q,1,e);
const double D2 = D(q,2,e);
const double By = B[qy][dy];
const double Gy = G[qy][dy];
const double BB = By * By;
const double BG = By * Gy;
const double GG = Gy * Gy;
QD0[qx][dy] += BB * D00;
QD1[qx][dy] += BG * (D01 + D10);
QD2[qx][dy] += GG * D11;
QD0[qx][dy] += BB * D0;
QD1[qx][dy] += BG * D1;
QD2[qx][dy] += GG * D2;
}
}
}
@@ -642,6 +489,7 @@ static void SmemPADiffusionDiagonal2D(const int NE,
const double GG = Gx * Gx;
Y(dx,dy,e) += GG * QD0[qx][dy];
Y(dx,dy,e) += BG * QD1[qx][dy];
Y(dx,dy,e) += BG * QD1[qx][dy];
Y(dx,dy,e) += BB * QD2[qx][dy];
}
}
@@ -651,7 +499,6 @@ static void SmemPADiffusionDiagonal2D(const int NE,
template<int T_D1D = 0, int T_Q1D = 0>
static void PADiffusionDiagonal3D(const int NE,
const bool symmetric,
const Array<double> &b,
const Array<double> &g,
const Vector &d,
@@ -668,7 +515,7 @@ static void PADiffusionDiagonal3D(const int NE,
MFEM_VERIFY(Q1D <= MQ1, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto Q = Reshape(d.Read(), Q1D*Q1D*Q1D, symmetric ? 6 : 9, NE);
auto Q = Reshape(d.Read(), Q1D*Q1D*Q1D, 6, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, NE);
MFEM_FORALL(e, NE,
{
@@ -693,10 +540,9 @@ static void PADiffusionDiagonal3D(const int NE,
for (int qz = 0; qz < Q1D; ++qz)
{
const int q = qx + (qy + qz * Q1D) * Q1D;
const int ksym = j >= i ?
const int k = j >= i ?
3 - (3-i)*(2-i)/2 + j:
3 - (3-j)*(2-j)/2 + i;
const int k = symmetric ? ksym : (i*DIM) + j;
const double O = Q(q,k,e);
const double Bz = B(qz,dz);
const double Gz = G(qz,dz);
@@ -752,7 +598,6 @@ static void PADiffusionDiagonal3D(const int NE,
// Shared memory PA Diffusion Diagonal 3D kernel
template<int T_D1D = 0, int T_Q1D = 0>
static void SmemPADiffusionDiagonal3D(const int NE,
const bool symmetric,
const Array<double> &b_,
const Array<double> &g_,
const Vector &d_,
@@ -769,7 +614,7 @@ static void SmemPADiffusionDiagonal3D(const int NE,
MFEM_VERIFY(Q1D <= MQ1, "");
auto b = Reshape(b_.Read(), Q1D, D1D);
auto g = Reshape(g_.Read(), Q1D, D1D);
auto D = Reshape(d_.Read(), Q1D*Q1D*Q1D, symmetric ? 6 : 9, NE);
auto D = Reshape(d_.Read(), Q1D*Q1D*Q1D, 6, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
@@ -810,10 +655,9 @@ static void SmemPADiffusionDiagonal3D(const int NE,
for (int qz = 0; qz < Q1D; ++qz)
{
const int q = qx + (qy + qz * Q1D) * Q1D;
const int ksym = j >= i ?
3 - (3-i)*(2-i)/2 + j:
3 - (3-j)*(2-j)/2 + i;
const int k = symmetric ? ksym : (i*DIM) + j;
const int k = j >= i ?
3 - (3-i)*(2-i)/2 + j:
3 - (3-j)*(2-j)/2 + i;
const double O = D(q,k,e);
const double Bz = B[qz][dz];
const double Gz = G[qz][dz];
@@ -872,7 +716,6 @@ static void PADiffusionAssembleDiagonal(const int dim,
const int D1D,
const int Q1D,
const int NE,
const bool symm,
const Array<double> &B,
const Array<double> &G,
const Vector &D,
@@ -882,30 +725,30 @@ static void PADiffusionAssembleDiagonal(const int dim,
{
switch ((D1D << 4 ) | Q1D)
{
case 0x22: return SmemPADiffusionDiagonal2D<2,2,8>(NE,symm,B,G,D,Y);
case 0x33: return SmemPADiffusionDiagonal2D<3,3,8>(NE,symm,B,G,D,Y);
case 0x44: return SmemPADiffusionDiagonal2D<4,4,4>(NE,symm,B,G,D,Y);
case 0x55: return SmemPADiffusionDiagonal2D<5,5,4>(NE,symm,B,G,D,Y);
case 0x66: return SmemPADiffusionDiagonal2D<6,6,2>(NE,symm,B,G,D,Y);
case 0x77: return SmemPADiffusionDiagonal2D<7,7,2>(NE,symm,B,G,D,Y);
case 0x88: return SmemPADiffusionDiagonal2D<8,8,1>(NE,symm,B,G,D,Y);
case 0x99: return SmemPADiffusionDiagonal2D<9,9,1>(NE,symm,B,G,D,Y);
default: return PADiffusionDiagonal2D(NE,symm,B,G,D,Y,D1D,Q1D);
case 0x22: return SmemPADiffusionDiagonal2D<2,2,8>(NE,B,G,D,Y);
case 0x33: return SmemPADiffusionDiagonal2D<3,3,8>(NE,B,G,D,Y);
case 0x44: return SmemPADiffusionDiagonal2D<4,4,4>(NE,B,G,D,Y);
case 0x55: return SmemPADiffusionDiagonal2D<5,5,4>(NE,B,G,D,Y);
case 0x66: return SmemPADiffusionDiagonal2D<6,6,2>(NE,B,G,D,Y);
case 0x77: return SmemPADiffusionDiagonal2D<7,7,2>(NE,B,G,D,Y);
case 0x88: return SmemPADiffusionDiagonal2D<8,8,1>(NE,B,G,D,Y);
case 0x99: return SmemPADiffusionDiagonal2D<9,9,1>(NE,B,G,D,Y);
default: return PADiffusionDiagonal2D(NE,B,G,D,Y,D1D,Q1D);
}
}
else if (dim == 3)
{
switch ((D1D << 4 ) | Q1D)
{
case 0x23: return SmemPADiffusionDiagonal3D<2,3>(NE,symm,B,G,D,Y);
case 0x34: return SmemPADiffusionDiagonal3D<3,4>(NE,symm,B,G,D,Y);
case 0x45: return SmemPADiffusionDiagonal3D<4,5>(NE,symm,B,G,D,Y);
case 0x56: return SmemPADiffusionDiagonal3D<5,6>(NE,symm,B,G,D,Y);
case 0x67: return SmemPADiffusionDiagonal3D<6,7>(NE,symm,B,G,D,Y);
case 0x78: return SmemPADiffusionDiagonal3D<7,8>(NE,symm,B,G,D,Y);
case 0x89: return SmemPADiffusionDiagonal3D<8,9>(NE,symm,B,G,D,Y);
case 0x9A: return SmemPADiffusionDiagonal3D<9,10>(NE,symm,B,G,D,Y);
default: return PADiffusionDiagonal3D(NE,symm,B,G,D,Y,D1D,Q1D);
case 0x23: return SmemPADiffusionDiagonal3D<2,3>(NE,B,G,D,Y);
case 0x34: return SmemPADiffusionDiagonal3D<3,4>(NE,B,G,D,Y);
case 0x45: return SmemPADiffusionDiagonal3D<4,5>(NE,B,G,D,Y);
case 0x56: return SmemPADiffusionDiagonal3D<5,6>(NE,B,G,D,Y);
case 0x67: return SmemPADiffusionDiagonal3D<6,7>(NE,B,G,D,Y);
case 0x78: return SmemPADiffusionDiagonal3D<7,8>(NE,B,G,D,Y);
case 0x89: return SmemPADiffusionDiagonal3D<8,9>(NE,B,G,D,Y);
case 0x9A: return SmemPADiffusionDiagonal3D<9,10>(NE,B,G,D,Y);
default: return PADiffusionDiagonal3D(NE,B,G,D,Y,D1D,Q1D);
}
}
MFEM_ABORT("Unknown kernel.");
@@ -913,14 +756,15 @@ static void PADiffusionAssembleDiagonal(const int dim,
void DiffusionIntegrator::AssembleDiagonalPA(Vector &diag)
{
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
CeedAssembleDiagonal(ceedDataPtr, diag);
CeedAssembleDiagonalPA(ceedDataPtr, diag);
}
else
#endif
{
if (pa_data.Size()==0) { AssemblePA(*fespace); }
PADiffusionAssembleDiagonal(dim, dofs1D, quad1D, ne, symmetric,
PADiffusionAssembleDiagonal(dim, dofs1D, quad1D, ne,
maps->B, maps->G, pa_data, diag);
}
}
@@ -1029,7 +873,6 @@ static void OccaPADiffusionApply3D(const int D1D,
// PA Diffusion Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0>
static void PADiffusionApply2D(const int NE,
const bool symmetric,
const Array<double> &b_,
const Array<double> &g_,
const Array<double> &bt_,
@@ -1048,7 +891,7 @@ static void PADiffusionApply2D(const int NE,
auto G = Reshape(g_.Read(), Q1D, D1D);
auto Bt = Reshape(bt_.Read(), D1D, Q1D);
auto Gt = Reshape(gt_.Read(), D1D, Q1D);
auto D = Reshape(d_.Read(), Q1D*Q1D, symmetric ? 3 : 4, NE);
auto D = Reshape(d_.Read(), Q1D*Q1D, 3, NE);
auto X = Reshape(x_.Read(), D1D, D1D, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
MFEM_FORALL(e, NE,
@@ -1104,15 +947,14 @@ static void PADiffusionApply2D(const int NE,
const int q = qx + qy * Q1D;
const double O11 = D(q,0,e);
const double O21 = D(q,1,e);
const double O12 = symmetric ? O21 : D(q,2,e);
const double O22 = symmetric ? D(q,2,e) : D(q,3,e);
const double O12 = D(q,1,e);
const double O22 = D(q,2,e);
const double gradX = grad[qy][qx][0];
const double gradY = grad[qy][qx][1];
grad[qy][qx][0] = (O11 * gradX) + (O12 * gradY);
grad[qy][qx][1] = (O21 * gradX) + (O22 * gradY);
grad[qy][qx][1] = (O12 * gradX) + (O22 * gradY);
}
}
for (int qy = 0; qy < Q1D; ++qy)
@@ -1151,7 +993,6 @@ static void PADiffusionApply2D(const int NE,
// Shared memory PA Diffusion Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0, int T_NBZ = 0>
static void SmemPADiffusionApply2D(const int NE,
const bool symmetric,
const Array<double> &b_,
const Array<double> &g_,
const Vector &d_,
@@ -1169,7 +1010,7 @@ static void SmemPADiffusionApply2D(const int NE,
MFEM_VERIFY(Q1D <= MQ1, "");
auto b = Reshape(b_.Read(), Q1D, D1D);
auto g = Reshape(g_.Read(), Q1D, D1D);
auto D = Reshape(d_.Read(), Q1D*Q1D, symmetric ? 3 : 4, NE);
auto D = Reshape(d_.Read(), Q1D*Q1D, 3, NE);
auto x = Reshape(x_.Read(), D1D, D1D, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, NE);
MFEM_FORALL_2D(e, NE, Q1D, Q1D, NBZ,
@@ -1251,13 +1092,12 @@ static void SmemPADiffusionApply2D(const int NE,
{
const int q = (qx + ((qy) * Q1D));
const double O11 = D(q,0,e);
const double O21 = D(q,1,e);
const double O12 = symmetric ? O21 : D(q,2,e);
const double O22 = symmetric ? D(q,2,e) : D(q,3,e);
const double O12 = D(q,1,e);
const double O22 = D(q,2,e);
const double gX = QQ0[qy][qx];
const double gY = QQ1[qy][qx];
QQ0[qy][qx] = (O11 * gX) + (O12 * gY);
QQ1[qy][qx] = (O21 * gX) + (O22 * gY);
QQ1[qy][qx] = (O12 * gX) + (O22 * gY);
}
}
MFEM_SYNC_THREAD;
@@ -1309,7 +1149,6 @@ static void SmemPADiffusionApply2D(const int NE,
// PA Diffusion Apply 3D kernel
template<int T_D1D = 0, int T_Q1D = 0>
static void PADiffusionApply3D(const int NE,
const bool symmetric,
const Array<double> &b,
const Array<double> &g,
const Array<double> &bt,
@@ -1327,7 +1166,7 @@ static void PADiffusionApply3D(const int NE,
auto G = Reshape(g.Read(), Q1D, D1D);
auto Bt = Reshape(bt.Read(), D1D, Q1D);
auto Gt = Reshape(gt.Read(), D1D, Q1D);
auto D = Reshape(d_.Read(), Q1D*Q1D*Q1D, symmetric ? 6 : 9, NE);
auto D = Reshape(d_.Read(), Q1D*Q1D*Q1D, 6, NE);
auto X = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
MFEM_FORALL(e, NE,
@@ -1418,18 +1257,15 @@ static void PADiffusionApply3D(const int NE,
const double O11 = D(q,0,e);
const double O12 = D(q,1,e);
const double O13 = D(q,2,e);
const double O21 = symmetric ? O12 : D(q,3,e);
const double O22 = symmetric ? D(q,3,e) : D(q,4,e);
const double O23 = symmetric ? D(q,4,e) : D(q,5,e);
const double O31 = symmetric ? O13 : D(q,6,e);
const double O32 = symmetric ? O23 : D(q,7,e);
const double O33 = symmetric ? D(q,5,e) : D(q,8,e);
const double O22 = D(q,3,e);
const double O23 = D(q,4,e);
const double O33 = D(q,5,e);
const double gradX = grad[qz][qy][qx][0];
const double gradY = grad[qz][qy][qx][1];
const double gradZ = grad[qz][qy][qx][2];
grad[qz][qy][qx][0] = (O11*gradX)+(O12*gradY)+(O13*gradZ);
grad[qz][qy][qx][1] = (O21*gradX)+(O22*gradY)+(O23*gradZ);
grad[qz][qy][qx][2] = (O31*gradX)+(O32*gradY)+(O33*gradZ);
grad[qz][qy][qx][1] = (O12*gradX)+(O22*gradY)+(O23*gradZ);
grad[qz][qy][qx][2] = (O13*gradX)+(O23*gradY)+(O33*gradZ);
}
}
}
@@ -1528,7 +1364,6 @@ static MFEM_HOST_DEVICE inline double sign(const int q, const int d)
template<int T_D1D = 0, int T_Q1D = 0>
static void SmemPADiffusionApply3D(const int NE,
const bool symmetric,
const Array<double> &b_,
const Array<double> &g_,
const Vector &d_,
@@ -1545,7 +1380,7 @@ static void SmemPADiffusionApply3D(const int NE,
MFEM_VERIFY(Q1D <= M1Q, "");
auto b = Reshape(b_.Read(), Q1D, D1D);
auto g = Reshape(g_.Read(), Q1D, D1D);
auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, 6, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, Q1D, Q1D, 1,
@@ -1692,18 +1527,15 @@ static void SmemPADiffusionApply3D(const int NE,
const double O11 = d(qx,qy,qz,0,e);
const double O12 = d(qx,qy,qz,1,e);
const double O13 = d(qx,qy,qz,2,e);
const double O21 = symmetric ? O12 : d(qx,qy,qz,3,e);
const double O22 = symmetric ? d(qx,qy,qz,3,e) : d(qx,qy,qz,4,e);
const double O23 = symmetric ? d(qx,qy,qz,4,e) : d(qx,qy,qz,5,e);
const double O31 = symmetric ? O13 : d(qx,qy,qz,6,e);
const double O32 = symmetric ? O23 : d(qx,qy,qz,7,e);
const double O33 = symmetric ? d(qx,qy,qz,5,e) : d(qx,qy,qz,8,e);
const double O22 = d(qx,qy,qz,3,e);
const double O23 = d(qx,qy,qz,4,e);
const double O33 = d(qx,qy,qz,5,e);
const double gX = u[qz];
const double gY = v[qz];
const double gZ = w[qz];
QQQ0[qz][qy][qx] = (O11*gX) + (O12*gY) + (O13*gZ);
QQQ1[qz][qy][qx] = (O21*gX) + (O22*gY) + (O23*gZ);
QQQ2[qz][qy][qx] = (O31*gX) + (O32*gY) + (O33*gZ);
QQQ1[qz][qy][qx] = (O12*gX) + (O22*gY) + (O23*gZ);
QQQ2[qz][qy][qx] = (O13*gX) + (O23*gY) + (O33*gZ);
}
}
}
@@ -1824,7 +1656,6 @@ static void PADiffusionApply(const int dim,
const int D1D,
const int Q1D,
const int NE,
const bool symm,
const Array<double> &B,
const Array<double> &G,
const Array<double> &Bt,
@@ -1855,15 +1686,15 @@ static void PADiffusionApply(const int dim,
{
switch (ID)
{
case 0x22: return SmemPADiffusionApply2D<2,2,16>(NE,symm,B,G,D,X,Y);
case 0x33: return SmemPADiffusionApply2D<3,3,16>(NE,symm,B,G,D,X,Y);
case 0x44: return SmemPADiffusionApply2D<4,4,8>(NE,symm,B,G,D,X,Y);
case 0x55: return SmemPADiffusionApply2D<5,5,8>(NE,symm,B,G,D,X,Y);
case 0x66: return SmemPADiffusionApply2D<6,6,4>(NE,symm,B,G,D,X,Y);
case 0x77: return SmemPADiffusionApply2D<7,7,4>(NE,symm,B,G,D,X,Y);
case 0x88: return SmemPADiffusionApply2D<8,8,2>(NE,symm,B,G,D,X,Y);
case 0x99: return SmemPADiffusionApply2D<9,9,2>(NE,symm,B,G,D,X,Y);
default: return PADiffusionApply2D(NE,symm,B,G,Bt,Gt,D,X,Y,D1D,Q1D);
case 0x22: return SmemPADiffusionApply2D<2,2,16>(NE,B,G,D,X,Y);
case 0x33: return SmemPADiffusionApply2D<3,3,16>(NE,B,G,D,X,Y);
case 0x44: return SmemPADiffusionApply2D<4,4,8>(NE,B,G,D,X,Y);
case 0x55: return SmemPADiffusionApply2D<5,5,8>(NE,B,G,D,X,Y);
case 0x66: return SmemPADiffusionApply2D<6,6,4>(NE,B,G,D,X,Y);
case 0x77: return SmemPADiffusionApply2D<7,7,4>(NE,B,G,D,X,Y);
case 0x88: return SmemPADiffusionApply2D<8,8,2>(NE,B,G,D,X,Y);
case 0x99: return SmemPADiffusionApply2D<9,9,2>(NE,B,G,D,X,Y);
default: return PADiffusionApply2D(NE,B,G,Bt,Gt,D,X,Y,D1D,Q1D);
}
}
@@ -1871,16 +1702,16 @@ static void PADiffusionApply(const int dim,
{
switch (ID)
{
case 0x23: return SmemPADiffusionApply3D<2,3>(NE,symm,B,G,D,X,Y);
case 0x34: return SmemPADiffusionApply3D<3,4>(NE,symm,B,G,D,X,Y);
case 0x45: return SmemPADiffusionApply3D<4,5>(NE,symm,B,G,D,X,Y);
case 0x46: return SmemPADiffusionApply3D<4,6>(NE,symm,B,G,D,X,Y);
case 0x56: return SmemPADiffusionApply3D<5,6>(NE,symm,B,G,D,X,Y);
case 0x58: return SmemPADiffusionApply3D<5,8>(NE,symm,B,G,D,X,Y);
case 0x67: return SmemPADiffusionApply3D<6,7>(NE,symm,B,G,D,X,Y);
case 0x78: return SmemPADiffusionApply3D<7,8>(NE,symm,B,G,D,X,Y);
case 0x89: return SmemPADiffusionApply3D<8,9>(NE,symm,B,G,D,X,Y);
default: return PADiffusionApply3D(NE,symm,B,G,Bt,Gt,D,X,Y,D1D,Q1D);
case 0x23: return SmemPADiffusionApply3D<2,3>(NE,B,G,D,X,Y);
case 0x34: return SmemPADiffusionApply3D<3,4>(NE,B,G,D,X,Y);
case 0x45: return SmemPADiffusionApply3D<4,5>(NE,B,G,D,X,Y);
case 0x46: return SmemPADiffusionApply3D<4,6>(NE,B,G,D,X,Y);
case 0x56: return SmemPADiffusionApply3D<5,6>(NE,B,G,D,X,Y);
case 0x58: return SmemPADiffusionApply3D<5,8>(NE,B,G,D,X,Y);
case 0x67: return SmemPADiffusionApply3D<6,7>(NE,B,G,D,X,Y);
case 0x78: return SmemPADiffusionApply3D<7,8>(NE,B,G,D,X,Y);
case 0x89: return SmemPADiffusionApply3D<8,9>(NE,B,G,D,X,Y);
default: return PADiffusionApply3D(NE,B,G,Bt,Gt,D,X,Y,D1D,Q1D);
}
}
MFEM_ABORT("Unknown kernel.");
@@ -1889,13 +1720,15 @@ static void PADiffusionApply(const int dim,
// PA Diffusion Apply kernel
void DiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
CeedAddMult(ceedDataPtr, x, y);
CeedAddMultPA(ceedDataPtr, x, y);
}
else
#endif
{
PADiffusionApply(dim, dofs1D, quad1D, ne, symmetric,
PADiffusionApply(dim, dofs1D, quad1D, ne,
maps->B, maps->G, maps->Bt, maps->Gt,
pa_data, x, y);
}
+1 -1
View File
@@ -334,7 +334,7 @@ static void PAGradientApplyTranspose2D(const int NE,
const int q1d = 0)
{
// TODO
MFEM_ASSERT(false, "GradientPAApplyTranspose 3D not implemented.");
MFEM_ASSERT(false, "PAGradientApplyTranspose2D not implemented.");
}
// PA Gradient Apply 3D kernel
+2613 -13
View File
File diff suppressed because it is too large Load Diff
-71
View File
@@ -1,71 +0,0 @@
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "libceed/mass.hpp"
using namespace std;
namespace mfem
{
void MassIntegrator::AssembleMF(const FiniteElementSpace &fes)
{
#ifdef MFEM_USE_CEED
// Assuming the same element type
fespace = &fes;
Mesh *mesh = fes.GetMesh();
if (mesh->GetNE() == 0) { return; }
const FiniteElement &el = *fes.GetFE(0);
ElementTransformation *T = mesh->GetElementTransformation(0);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, *T);
if (DeviceCanUseCeed())
{
delete ceedDataPtr;
ceedDataPtr = new CeedData;
InitCeedCoeff(Q, *mesh, *ir, ceedDataPtr);
return CeedMFMassAssemble(fes, *ir, *ceedDataPtr);
}
#endif
mfem_error("Error: MassIntegrator::AssembleMF only implemented with libCEED");
}
void MassIntegrator::AddMultMF(const Vector &x, Vector &y) const
{
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
CeedAddMult(ceedDataPtr, x, y);
}
else
#endif
{
mfem_error("Error: MassIntegrator::AddMultMF only implemented with libCEED");
}
}
void MassIntegrator::AssembleDiagonalMF(Vector &diag)
{
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
CeedAssembleDiagonal(ceedDataPtr, diag);
}
else
#endif
{
mfem_error("Error: MassIntegrator::AssembleDiagonalMF only implemented with libCEED");
}
}
} // namespace mfem
+107 -164
View File
@@ -1,13 +1,13 @@
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
// availability see http://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.
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#include "../general/forall.hpp"
#include "bilininteg.hpp"
@@ -23,7 +23,7 @@ namespace mfem
// PA Mass Assemble kernel
void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
void MassIntegrator::SetupPA(const FiniteElementSpace &fes)
{
// Assuming the same element type
fespace = &fes;
@@ -32,13 +32,16 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
const FiniteElement &el = *fes.GetFE(0);
ElementTransformation *T = mesh->GetElementTransformation(0);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, *T);
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
delete ceedDataPtr;
ceedDataPtr = new CeedData;
InitCeedCoeff(Q, *mesh, *ir, ceedDataPtr);
return CeedPAMassAssemble(fes, *ir, *ceedDataPtr);
if (ceedDataPtr) { delete ceedDataPtr; }
CeedData* ptr = new CeedData();
ceedDataPtr = ptr;
InitCeedCoeff(Q, ptr);
return CeedPAMassAssemble(fes, *ir, *ptr);
}
#endif
dim = mesh->Dimension();
ne = fes.GetMesh()->GetNE();
nq = ir->GetNPoints();
@@ -47,7 +50,7 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(ne*nq, Device::GetDeviceMemoryType());
pa_data.SetSize(ne*nq, Device::GetMemoryType());
Vector coeff;
if (Q == nullptr)
{
@@ -152,6 +155,12 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
}
}
void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
SetupPA(fes);
}
template<int T_D1D = 0, int T_Q1D = 0>
static void PAMassAssembleDiagonal2D(const int NE,
const Array<double> &b,
@@ -459,11 +468,13 @@ static void PAMassAssembleDiagonal(const int dim, const int D1D,
void MassIntegrator::AssembleDiagonalPA(Vector &diag)
{
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
CeedAssembleDiagonal(ceedDataPtr, diag);
CeedAssembleDiagonalPA(ceedDataPtr, diag);
}
else
#endif
{
PAMassAssembleDiagonal(dim, dofs1D, quad1D, ne, maps->B, pa_data, diag);
}
@@ -939,8 +950,9 @@ static void SmemPAMassApply3D(const int NE,
auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
MFEM_FORALL_3D(e, NE, Q1D, Q1D, 1,
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
@@ -957,188 +969,130 @@ static void SmemPAMassApply3D(const int NE,
double (*QQQ)[MQ1][MQ1] = (double (*)[MQ1][MQ1]) sm1;
double (*QQD)[MQ1][MD1] = (double (*)[MQ1][MD1]) sm0;
double (*QDD)[MD1][MD1] = (double (*)[MD1][MD1]) sm1;
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
MFEM_FOREACH_THREAD(dx,x,D1D)
{
X[dz][dy][dx] = x(dx,dy,dz,e);
}
}
MFEM_FOREACH_THREAD(dx,x,Q1D)
}
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d,y,D1D)
{
B[dx][dy] = b(dx,dy);
MFEM_FOREACH_THREAD(q,x,Q1D)
{
B[q][d] = b(q,d);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
double u[D1D];
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; dz++)
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
u[dz] = 0;
double u = 0.0;
for (int dx = 0; dx < D1D; ++dx)
{
u += X[dz][dy][dx] * B[qx][dx];
}
DDQ[dz][dy][qx] = u;
}
MFEM_UNROLL(MD1)
for (int dx = 0; dx < D1D; ++dx)
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
MFEM_UNROLL(MD1)
double u = 0.0;
for (int dy = 0; dy < D1D; ++dy)
{
u += DDQ[dz][dy][qx] * B[qy][dy];
}
DQQ[dz][qy][qx] = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
double u = 0.0;
for (int dz = 0; dz < D1D; ++dz)
{
u[dz] += X[dz][dy][dx] * B[qx][dx];
u += DQQ[dz][qy][qx] * B[qz][dz];
}
}
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
{
DDQ[dz][dy][qx] = u[dz];
QQQ[qz][qy][qx] = u * d(qx,qy,qz,e);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
if (tidz == 0)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD(d,y,D1D)
{
double u[D1D];
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; dz++)
MFEM_FOREACH_THREAD(q,x,Q1D)
{
u[dz] = 0;
Bt[d][q] = b(q,d);
}
MFEM_UNROLL(MD1)
for (int dy = 0; dy < D1D; ++dy)
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; dz++)
double u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u[dz] += DDQ[dz][dy][qx] * B[qy][dy];
u += QQQ[qz][qy][qx] * Bt[dx][qx];
}
}
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; dz++)
{
DQQ[dz][qy][qx] = u[dz];
QQD[qz][qy][dx] = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
double u[Q1D];
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; qz++)
MFEM_FOREACH_THREAD(dx,x,D1D)
{
u[qz] = 0;
}
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
{
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; qz++)
double u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u[qz] += DQQ[dz][qy][qx] * B[qz][dz];
u += QQD[qz][qy][dx] * Bt[dy][qy];
}
}
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; qz++)
{
QQQ[qz][qy][qx] = u[qz] * d(qx,qy,qz,e);
QDD[qz][dy][dx] = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(d,y,D1D)
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
Bt[d][q] = b(q,d);
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double u[Q1D];
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz)
MFEM_FOREACH_THREAD(dx,x,D1D)
{
u[qz] = 0;
}
MFEM_UNROLL(MQ1)
for (int qx = 0; qx < Q1D; ++qx)
{
MFEM_UNROLL(MQ1)
double u = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
u[qz] += QQQ[qz][qy][qx] * Bt[dx][qx];
u += QDD[qz][dy][dx] * Bt[dz][qz];
}
}
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz)
{
QQD[qz][qy][dx] = u[qz];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double u[Q1D];
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz)
{
u[qz] = 0;
}
MFEM_UNROLL(MQ1)
for (int qy = 0; qy < Q1D; ++qy)
{
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz)
{
u[qz] += QQD[qz][qy][dx] * Bt[dy][qy];
}
}
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz)
{
QDD[qz][dy][dx] = u[qz];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
double u[D1D];
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
{
u[dz] = 0;
}
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; ++qz)
{
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
{
u[dz] += QDD[qz][dy][dx] * Bt[dz][qz];
}
}
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
{
y(dx,dy,dz,e) += u[dz];
y(dx,dy,dz,e) += u;
}
}
}
@@ -1169,20 +1123,14 @@ static void PAMassApply(const int dim,
MFEM_ABORT("OCCA PA Mass Apply unknown kernel!");
}
#endif // MFEM_USE_OCCA
const int id = (D1D << 4) | Q1D;
if (dim == 2)
{
switch (id)
switch ((D1D << 4) | Q1D)
{
case 0x22: return SmemPAMassApply2D<2,2,16>(NE,B,Bt,D,X,Y);
case 0x24: return SmemPAMassApply2D<2,4,16>(NE,B,Bt,D,X,Y);
case 0x33: return SmemPAMassApply2D<3,3,16>(NE,B,Bt,D,X,Y);
case 0x34: return SmemPAMassApply2D<3,4,16>(NE,B,Bt,D,X,Y);
case 0x36: return SmemPAMassApply2D<3,6,16>(NE,B,Bt,D,X,Y);
case 0x44: return SmemPAMassApply2D<4,4,8>(NE,B,Bt,D,X,Y);
case 0x48: return SmemPAMassApply2D<4,8,4>(NE,B,Bt,D,X,Y);
case 0x55: return SmemPAMassApply2D<5,5,8>(NE,B,Bt,D,X,Y);
case 0x58: return SmemPAMassApply2D<5,8,2>(NE,B,Bt,D,X,Y);
case 0x66: return SmemPAMassApply2D<6,6,4>(NE,B,Bt,D,X,Y);
case 0x77: return SmemPAMassApply2D<7,7,4>(NE,B,Bt,D,X,Y);
case 0x88: return SmemPAMassApply2D<8,8,2>(NE,B,Bt,D,X,Y);
@@ -1192,35 +1140,30 @@ static void PAMassApply(const int dim,
}
else if (dim == 3)
{
switch (id)
switch ((D1D << 4) | Q1D)
{
case 0x23: return SmemPAMassApply3D<2,3>(NE,B,Bt,D,X,Y);
case 0x24: return SmemPAMassApply3D<2,4>(NE,B,Bt,D,X,Y);
case 0x34: return SmemPAMassApply3D<3,4>(NE,B,Bt,D,X,Y);
case 0x36: return SmemPAMassApply3D<3,6>(NE,B,Bt,D,X,Y);
case 0x45: return SmemPAMassApply3D<4,5>(NE,B,Bt,D,X,Y);
case 0x46: return SmemPAMassApply3D<4,6>(NE,B,Bt,D,X,Y);
case 0x48: return SmemPAMassApply3D<4,8>(NE,B,Bt,D,X,Y);
case 0x56: return SmemPAMassApply3D<5,6>(NE,B,Bt,D,X,Y);
case 0x58: return SmemPAMassApply3D<5,8>(NE,B,Bt,D,X,Y);
case 0x67: return SmemPAMassApply3D<6,7>(NE,B,Bt,D,X,Y);
case 0x78: return SmemPAMassApply3D<7,8>(NE,B,Bt,D,X,Y);
case 0x89: return SmemPAMassApply3D<8,9>(NE,B,Bt,D,X,Y);
case 0x9A: return SmemPAMassApply3D<9,10>(NE,B,Bt,D,X,Y);
default: return PAMassApply3D(NE,B,Bt,D,X,Y,D1D,Q1D);
}
}
mfem::out << "Unknown kernel 0x" << std::hex << id << std::endl;
MFEM_ABORT("Unknown kernel.");
}
void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
CeedAddMult(ceedDataPtr, x, y);
CeedAddMultPA(ceedDataPtr, x, y);
}
else
#endif
{
PAMassApply(dim, dofs1D, quad1D, ne, maps->B, maps->Bt, pa_data, x, y);
}
+31 -53
View File
@@ -12,7 +12,6 @@
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "libceed/diffusion.hpp"
using namespace std;
@@ -130,13 +129,6 @@ void VectorDiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
const FiniteElement &el = *fes.GetFE(0);
const IntegrationRule *ir
= IntRule ? IntRule : &DiffusionIntegrator::GetRule(el, el);
if (DeviceCanUseCeed())
{
delete ceedDataPtr;
ceedDataPtr = new CeedData;
InitCeedCoeff(Q, *mesh, *ir, ceedDataPtr);
return CeedPADiffusionAssemble(fes, *ir, * ceedDataPtr);
}
const int dims = el.GetDim();
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
@@ -514,40 +506,33 @@ void PAVectorDiffusionApply3D(const int NE,
// PA Diffusion Apply kernel
void VectorDiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
if (DeviceCanUseCeed())
{
CeedAddMult(ceedDataPtr, x, y);
}
else
{
const int D1D = dofs1D;
const int Q1D = quad1D;
const Array<double> &B = maps->B;
const Array<double> &G = maps->G;
const Array<double> &Bt = maps->Bt;
const Array<double> &Gt = maps->Gt;
const Vector &D = pa_data;
const int D1D = dofs1D;
const int Q1D = quad1D;
const Array<double> &B = maps->B;
const Array<double> &G = maps->G;
const Array<double> &Bt = maps->Bt;
const Array<double> &Gt = maps->Gt;
const Vector &D = pa_data;
if (dim == 2 && sdim == 3)
if (dim == 2 && sdim == 3)
{
switch ((dofs1D << 4 ) | quad1D)
{
switch ((dofs1D << 4 ) | quad1D)
{
case 0x22: return PAVectorDiffusionApply2D<2,2,3>(ne,B,G,Bt,Gt,D,x,y);
case 0x33: return PAVectorDiffusionApply2D<3,3,3>(ne,B,G,Bt,Gt,D,x,y);
case 0x44: return PAVectorDiffusionApply2D<4,4,3>(ne,B,G,Bt,Gt,D,x,y);
case 0x55: return PAVectorDiffusionApply2D<5,5,3>(ne,B,G,Bt,Gt,D,x,y);
default:
return PAVectorDiffusionApply2D(ne,B,G,Bt,Gt,D,x,y,D1D,Q1D,sdim);
}
case 0x22: return PAVectorDiffusionApply2D<2,2,3>(ne,B,G,Bt,Gt,D,x,y);
case 0x33: return PAVectorDiffusionApply2D<3,3,3>(ne,B,G,Bt,Gt,D,x,y);
case 0x44: return PAVectorDiffusionApply2D<4,4,3>(ne,B,G,Bt,Gt,D,x,y);
case 0x55: return PAVectorDiffusionApply2D<5,5,3>(ne,B,G,Bt,Gt,D,x,y);
default:
return PAVectorDiffusionApply2D(ne,B,G,Bt,Gt,D,x,y,D1D,Q1D,sdim);
}
if (dim == 2 && sdim == 2)
{ return PAVectorDiffusionApply2D(ne,B,G,Bt,Gt,D,x,y,D1D,Q1D,sdim); }
if (dim == 3 && sdim == 3)
{ return PAVectorDiffusionApply3D(ne,B,G,Bt,Gt,D,x,y,D1D,Q1D); }
MFEM_ABORT("Unknown kernel.");
}
if (dim == 2 && sdim == 2)
{ return PAVectorDiffusionApply2D(ne,B,G,Bt,Gt,D,x,y,D1D,Q1D,sdim); }
if (dim == 3 && sdim == 3)
{ return PAVectorDiffusionApply3D(ne,B,G,Bt,Gt,D,x,y,D1D,Q1D); }
MFEM_ABORT("Unknown kernel.");
}
template<int T_D1D = 0, int T_Q1D = 0>
@@ -741,21 +726,14 @@ static void PAVectorDiffusionAssembleDiagonal(const int dim,
void VectorDiffusionIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
{
CeedAssembleDiagonal(ceedDataPtr, diag);
}
else
{
PAVectorDiffusionAssembleDiagonal(dim,
dofs1D,
quad1D,
ne,
maps->B,
maps->G,
pa_data,
diag);
}
PAVectorDiffusionAssembleDiagonal(dim,
dofs1D,
quad1D,
ne,
maps->B,
maps->G,
pa_data,
diag);
}
} // namespace mfem
-69
View File
@@ -1,69 +0,0 @@
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "libceed/diffusion.hpp"
using namespace std;
namespace mfem
{
void VectorDiffusionIntegrator::AssembleMF(const FiniteElementSpace &fes)
{
#ifdef MFEM_USE_CEED
// Assumes tensor-product elements
Mesh *mesh = fes.GetMesh();
const FiniteElement &el = *fes.GetFE(0);
const IntegrationRule *ir
= IntRule ? IntRule : &DiffusionIntegrator::GetRule(el, el);
if (DeviceCanUseCeed())
{
delete ceedDataPtr;
ceedDataPtr = new CeedData;
InitCeedCoeff(Q, *mesh, *ir, ceedDataPtr);
return CeedMFDiffusionAssemble(fes, *ir, * ceedDataPtr);
}
#endif
mfem_error("Error: VectorDiffusionIntegrator::AssembleMF only implemented with libCEED");
}
void VectorDiffusionIntegrator::AddMultMF(const Vector &x, Vector &y) const
{
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
CeedAddMult(ceedDataPtr, x, y);
}
else
#endif
{
mfem_error("Error: VectorDiffusionIntegrator::AssembleDiagonalMF only implemented with libCEED");
}
}
void VectorDiffusionIntegrator::AssembleDiagonalMF(Vector &diag)
{
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
CeedAssembleDiagonal(ceedDataPtr, diag);
}
else
#endif
{
mfem_error("Error: VectorDiffusionIntegrator::AddMultMF only implemented with libCEED");
}
}
} // namespace mfem
+9 -31
View File
@@ -12,7 +12,6 @@
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "libceed/mass.hpp"
using namespace std;
@@ -31,13 +30,6 @@ void VectorMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
ElementTransformation *T = mesh->GetElementTransformation(0);
const IntegrationRule *ir
= IntRule ? IntRule : &MassIntegrator::GetRule(el, el, *T);
if (DeviceCanUseCeed())
{
delete ceedDataPtr;
ceedDataPtr = new CeedData;
InitCeedCoeff(Q, *mesh, *ir, ceedDataPtr);
return CeedPAMassAssemble(fes, *ir, *ceedDataPtr);
}
dim = mesh->Dimension();
ne = fes.GetMesh()->GetNE();
nq = ir->GetNPoints();
@@ -369,14 +361,7 @@ static void PAVectorMassApply(const int dim,
void VectorMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
if (DeviceCanUseCeed())
{
CeedAddMult(ceedDataPtr, x, y);
}
else
{
PAVectorMassApply(dim, dofs1D, quad1D, ne, maps->B, maps->Bt, pa_data, x, y);
}
PAVectorMassApply(dim, dofs1D, quad1D, ne, maps->B, maps->Bt, pa_data, x, y);
}
template<const int T_D1D = 0, const int T_Q1D = 0>
@@ -529,21 +514,14 @@ static void PAVectorMassAssembleDiagonal(const int dim,
void VectorMassIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
{
CeedAssembleDiagonal(ceedDataPtr, diag);
}
else
{
PAVectorMassAssembleDiagonal(dim,
dofs1D,
quad1D,
ne,
maps->B,
maps->Bt,
pa_data,
diag);
}
PAVectorMassAssembleDiagonal(dim,
dofs1D,
quad1D,
ne,
maps->B,
maps->Bt,
pa_data,
diag);
}
} // namespace mfem
-74
View File
@@ -1,74 +0,0 @@
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../general/forall.hpp"
#include "bilininteg.hpp"
#include "gridfunc.hpp"
#include "libceed/mass.hpp"
using namespace std;
namespace mfem
{
// MF Mass Integrator
// MF Mass Assemble kernel
void VectorMassIntegrator::AssembleMF(const FiniteElementSpace &fes)
{
#ifdef MFEM_USE_CEED
// Assuming the same element type
Mesh *mesh = fes.GetMesh();
if (mesh->GetNE() == 0) { return; }
const FiniteElement &el = *fes.GetFE(0);
ElementTransformation *T = mesh->GetElementTransformation(0);
const IntegrationRule *ir
= IntRule ? IntRule : &MassIntegrator::GetRule(el, el, *T);
if (DeviceCanUseCeed())
{
delete ceedDataPtr;
ceedDataPtr = new CeedData;
InitCeedCoeff(Q, *mesh, *ir, ceedDataPtr);
return CeedMFMassAssemble(fes, *ir, *ceedDataPtr);
}
#endif
mfem_error("Error: VectorMassIntegrator::AssembleMF only implemented with libCEED");
}
void VectorMassIntegrator::AddMultMF(const Vector &x, Vector &y) const
{
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
CeedAddMult(ceedDataPtr, x, y);
}
else
#endif
{
mfem_error("Error: VectorMassIntegrator::AssembleDiagonalMF only implemented with libCEED");
}
}
void VectorMassIntegrator::AssembleDiagonalMF(Vector &diag)
{
#ifdef MFEM_USE_CEED
if (DeviceCanUseCeed())
{
CeedAssembleDiagonal(ceedDataPtr, diag);
}
else
#endif
{
mfem_error("Error: VectorMassIntegrator::AddMultMF only implemented with libCEED");
}
}
} // namespace mfem
+251 -144
View File
@@ -15,14 +15,29 @@
namespace mfem
{
void PADiffusionSetup3D(const int Q1D,
const int coeffDim,
void PAHcurlSetup2D(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
Vector &coeff,
Vector &op);
void PAHcurlSetup2Din3D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const Vector &_coeff,
Vector &_coeff,
Vector &op);
void PAHcurlSetup3D(const int Q1D,
const int coeffDim,
const int NE,
const Array<double> &w,
const Vector &j,
Vector &coeff,
Vector &op);
void PAHcurlMassAssembleDiagonal2D(const int D1D,
const int Q1D,
const int NE,
@@ -41,6 +56,14 @@ void PAHcurlMassAssembleDiagonal3D(const int D1D,
const Vector &pa_data,
Vector &diag);
void PAHcurlMassAssembleDiagonal3DHost(const int D1D,
const int Q1D,
const int NE,
const Array<double> &_Bo,
const Array<double> &_Bc,
const Vector &_op,
Vector &_diag);
template<int T_D1D = 0, int T_Q1D = 0>
void SmemPAHcurlMassAssembleDiagonal3D(const int D1D,
const int Q1D,
@@ -181,11 +204,12 @@ void PAHcurlHdivSetup3D(const int Q1D,
Vector &_coeff,
Vector &op)
{
const int NQ = Q1D*Q1D*Q1D;
const bool symmetric = (coeffDim != 9);
auto W = Reshape(_w.Read(), Q1D, Q1D, Q1D);
auto J = Reshape(j.Read(), Q1D, Q1D, Q1D, 3, 3, NE);
auto coeff = Reshape(_coeff.Read(), coeffDim, Q1D, Q1D, Q1D, NE);
auto y = Reshape(op.Write(), 9, Q1D, Q1D, Q1D, NE);
auto W = _w.Read();
auto J = Reshape(j.Read(), NQ, 3, 3, NE);
auto coeff = Reshape(_coeff.Read(), coeffDim, NQ, NE);
auto y = Reshape(op.Write(), 9, NQ, NE);
const int i11 = 0;
const int i12 = transpose ? 3 : 1;
@@ -197,89 +221,83 @@ void PAHcurlHdivSetup3D(const int Q1D,
const int i32 = transpose ? 5 : 7;
const int i33 = 8;
MFEM_FORALL_3D(e, NE, Q1D, Q1D, Q1D,
MFEM_FORALL(e, NE,
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
for (int q = 0; q < NQ; ++q)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
const double J11 = J(q,0,0,e);
const double J21 = J(q,1,0,e);
const double J31 = J(q,2,0,e);
const double J12 = J(q,0,1,e);
const double J22 = J(q,1,1,e);
const double J32 = J(q,2,1,e);
const double J13 = J(q,0,2,e);
const double J23 = J(q,1,2,e);
const double J33 = J(q,2,2,e);
const double detJ = J11 * (J22 * J33 - J32 * J23) -
/* */ J21 * (J12 * J33 - J32 * J13) +
/* */ J31 * (J12 * J23 - J22 * J13);
const double w_detJ = W[q] / detJ;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
const double A12 = (J32 * J13) - (J12 * J33);
const double A13 = (J12 * J23) - (J22 * J13);
const double A21 = (J31 * J23) - (J21 * J33);
const double A22 = (J11 * J33) - (J13 * J31);
const double A23 = (J21 * J13) - (J11 * J23);
const double A31 = (J21 * J32) - (J31 * J22);
const double A32 = (J31 * J12) - (J11 * J32);
const double A33 = (J11 * J22) - (J12 * J21);
if (coeffDim == 6 || coeffDim == 9) // Matrix coefficient version
{
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
const double J11 = J(qx,qy,qz,0,0,e);
const double J21 = J(qx,qy,qz,1,0,e);
const double J31 = J(qx,qy,qz,2,0,e);
const double J12 = J(qx,qy,qz,0,1,e);
const double J22 = J(qx,qy,qz,1,1,e);
const double J32 = J(qx,qy,qz,2,1,e);
const double J13 = J(qx,qy,qz,0,2,e);
const double J23 = J(qx,qy,qz,1,2,e);
const double J33 = J(qx,qy,qz,2,2,e);
const double detJ = J11 * (J22 * J33 - J32 * J23) -
/* */ J21 * (J12 * J33 - J32 * J13) +
/* */ J31 * (J12 * J23 - J22 * J13);
const double w_detJ = W(qx,qy,qz) / detJ;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
const double A12 = (J32 * J13) - (J12 * J33);
const double A13 = (J12 * J23) - (J22 * J13);
const double A21 = (J31 * J23) - (J21 * J33);
const double A22 = (J11 * J33) - (J13 * J31);
const double A23 = (J21 * J13) - (J11 * J23);
const double A31 = (J21 * J32) - (J31 * J22);
const double A32 = (J31 * J12) - (J11 * J32);
const double A33 = (J11 * J22) - (J12 * J21);
// First compute entries of R = MJ
const double M11 = (!symmetric) ? coeff(i11, q, e) : coeff(0, q, e);
const double M12 = (!symmetric) ? coeff(i12, q, e) : coeff(1, q, e);
const double M13 = (!symmetric) ? coeff(i13, q, e) : coeff(2, q, e);
const double M21 = (!symmetric) ? coeff(i21, q, e) : M12;
const double M22 = (!symmetric) ? coeff(i22, q, e) : coeff(3, q, e);
const double M23 = (!symmetric) ? coeff(i23, q, e) : coeff(4, q, e);
const double M31 = (!symmetric) ? coeff(i31, q, e) : M13;
const double M32 = (!symmetric) ? coeff(i32, q, e) : M23;
const double M33 = (!symmetric) ? coeff(i33, q, e) : coeff(5, q, e);
if (coeffDim == 6 || coeffDim == 9) // Matrix coefficient version
{
// First compute entries of R = MJ
const double M11 = (!symmetric) ? coeff(i11,qx,qy,qz,e) : coeff(0,qx,qy,qz,e);
const double M12 = (!symmetric) ? coeff(i12,qx,qy,qz,e) : coeff(1,qx,qy,qz,e);
const double M13 = (!symmetric) ? coeff(i13,qx,qy,qz,e) : coeff(2,qx,qy,qz,e);
const double M21 = (!symmetric) ? coeff(i21,qx,qy,qz,e) : M12;
const double M22 = (!symmetric) ? coeff(i22,qx,qy,qz,e) : coeff(3,qx,qy,qz,e);
const double M23 = (!symmetric) ? coeff(i23,qx,qy,qz,e) : coeff(4,qx,qy,qz,e);
const double M31 = (!symmetric) ? coeff(i31,qx,qy,qz,e) : M13;
const double M32 = (!symmetric) ? coeff(i32,qx,qy,qz,e) : M23;
const double M33 = (!symmetric) ? coeff(i33,qx,qy,qz,e) : coeff(5,qx,qy,qz,e);
const double R11 = M11*J11 + M12*J12 + M13*J13;
const double R12 = M11*J21 + M12*J22 + M13*J23;
const double R13 = M11*J31 + M12*J32 + M13*J33;
const double R21 = M21*J11 + M22*J12 + M23*J13;
const double R22 = M21*J21 + M22*J22 + M23*J23;
const double R23 = M21*J31 + M22*J32 + M23*J33;
const double R31 = M31*J11 + M32*J12 + M33*J13;
const double R32 = M31*J21 + M32*J22 + M33*J23;
const double R33 = M31*J31 + M32*J32 + M33*J33;
const double R11 = M11*J11 + M12*J12 + M13*J13;
const double R12 = M11*J21 + M12*J22 + M13*J23;
const double R13 = M11*J31 + M12*J32 + M13*J33;
const double R21 = M21*J11 + M22*J12 + M23*J13;
const double R22 = M21*J21 + M22*J22 + M23*J23;
const double R23 = M21*J31 + M22*J32 + M23*J33;
const double R31 = M31*J11 + M32*J12 + M33*J13;
const double R32 = M31*J21 + M32*J22 + M33*J23;
const double R33 = M31*J31 + M32*J32 + M33*J33;
// Now set y to detJ J^{-1} R = adj(J) R
y(i11,qx,qy,qz,e) = w_detJ * (A11*R11 + A12*R21 + A13*R31); // 1,1
y(i12,qx,qy,qz,e) = w_detJ * (A11*R12 + A12*R22 + A13*R32); // 1,2
y(i13,qx,qy,qz,e) = w_detJ * (A11*R13 + A12*R23 + A13*R33); // 1,3
y(i21,qx,qy,qz,e) = w_detJ * (A21*R11 + A22*R21 + A23*R31); // 2,1
y(i22,qx,qy,qz,e) = w_detJ * (A21*R12 + A22*R22 + A23*R32); // 2,2
y(i23,qx,qy,qz,e) = w_detJ * (A21*R13 + A22*R23 + A23*R33); // 2,3
y(i31,qx,qy,qz,e) = w_detJ * (A31*R11 + A32*R21 + A33*R31); // 3,1
y(i32,qx,qy,qz,e) = w_detJ * (A31*R12 + A32*R22 + A33*R32); // 3,2
y(i33,qx,qy,qz,e) = w_detJ * (A31*R13 + A32*R23 + A33*R33); // 3,3
}
else if (coeffDim == 3) // Vector coefficient version
{
const double D1 = coeff(0,qx,qy,qz,e);
const double D2 = coeff(1,qx,qy,qz,e);
const double D3 = coeff(2,qx,qy,qz,e);
// detJ J^{-1} DJ = adj(J) DJ
y(i11,qx,qy,qz,e) = w_detJ * (D1*A11*J11 + D2*A12*J21 + D3*A13*J31); // 1,1
y(i12,qx,qy,qz,e) = w_detJ * (D1*A11*J12 + D2*A12*J22 + D3*A13*J32); // 1,2
y(i13,qx,qy,qz,e) = w_detJ * (D1*A11*J13 + D2*A12*J23 + D3*A13*J33); // 1,3
y(i21,qx,qy,qz,e) = w_detJ * (D1*A21*J11 + D2*A22*J21 + D3*A23*J31); // 2,1
y(i22,qx,qy,qz,e) = w_detJ * (D1*A21*J12 + D2*A22*J22 + D3*A23*J32); // 2,2
y(i23,qx,qy,qz,e) = w_detJ * (D1*A21*J13 + D2*A22*J23 + D3*A23*J33); // 2,3
y(i31,qx,qy,qz,e) = w_detJ * (D1*A31*J11 + D2*A32*J21 + D3*A33*J31); // 3,1
y(i32,qx,qy,qz,e) = w_detJ * (D1*A31*J12 + D2*A32*J22 + D3*A33*J32); // 3,2
y(i33,qx,qy,qz,e) = w_detJ * (D1*A31*J13 + D2*A32*J23 + D3*A33*J33); // 3,3
}
}
// Now set y to detJ J^{-1} R = adj(J) R
y(i11,q,e) = w_detJ * (A11*R11 + A12*R21 + A13*R31); // 1,1
y(i12,q,e) = w_detJ * (A11*R12 + A12*R22 + A13*R32); // 1,2
y(i13,q,e) = w_detJ * (A11*R13 + A12*R23 + A13*R33); // 1,3
y(i21,q,e) = w_detJ * (A21*R11 + A22*R21 + A23*R31); // 2,1
y(i22,q,e) = w_detJ * (A21*R12 + A22*R22 + A23*R32); // 2,2
y(i23,q,e) = w_detJ * (A21*R13 + A22*R23 + A23*R33); // 2,3
y(i31,q,e) = w_detJ * (A31*R11 + A32*R21 + A33*R31); // 3,1
y(i32,q,e) = w_detJ * (A31*R12 + A32*R22 + A33*R32); // 3,2
y(i33,q,e) = w_detJ * (A31*R13 + A32*R23 + A33*R33); // 3,3
}
else if (coeffDim == 3) // Vector coefficient version
{
const double D1 = coeff(0, q, e);
const double D2 = coeff(1, q, e);
const double D3 = coeff(2, q, e);
// detJ J^{-1} DJ = adj(J) DJ
y(i11,q,e) = w_detJ * (D1*A11*J11 + D2*A12*J21 + D3*A13*J31); // 1,1
y(i12,q,e) = w_detJ * (D1*A11*J12 + D2*A12*J22 + D3*A13*J32); // 1,2
y(i13,q,e) = w_detJ * (D1*A11*J13 + D2*A12*J23 + D3*A13*J33); // 1,3
y(i21,q,e) = w_detJ * (D1*A21*J11 + D2*A22*J21 + D3*A23*J31); // 2,1
y(i22,q,e) = w_detJ * (D1*A21*J12 + D2*A22*J22 + D3*A23*J32); // 2,2
y(i23,q,e) = w_detJ * (D1*A21*J13 + D2*A22*J23 + D3*A23*J33); // 2,3
y(i31,q,e) = w_detJ * (D1*A31*J11 + D2*A32*J21 + D3*A33*J31); // 3,1
y(i32,q,e) = w_detJ * (D1*A31*J12 + D2*A32*J22 + D3*A33*J32); // 3,2
y(i33,q,e) = w_detJ * (D1*A31*J13 + D2*A32*J23 + D3*A33*J33); // 3,3
}
}
});
@@ -297,61 +315,59 @@ void PAHcurlHdivSetup2D(const int Q1D,
Vector &_coeff,
Vector &op)
{
const int NQ = Q1D*Q1D;
const bool symmetric = (coeffDim != 4);
auto W = Reshape(_w.Read(), Q1D, Q1D);
auto J = Reshape(j.Read(), Q1D, Q1D, 2, 2, NE);
auto coeff = Reshape(_coeff.Read(), coeffDim, Q1D, Q1D, NE);
auto y = Reshape(op.Write(), 4, Q1D, Q1D, NE);
auto W = _w.Read();
auto J = Reshape(j.Read(), NQ, 2, 2, NE);
auto coeff = Reshape(_coeff.Read(), coeffDim, NQ, NE);
auto y = Reshape(op.Write(), 4, NQ, NE);
const int i11 = 0;
const int i12 = transpose ? 2 : 1;
const int i21 = transpose ? 1 : 2;
const int i22 = 3;
MFEM_FORALL_2D(e, NE, Q1D, Q1D, 1,
MFEM_FORALL(e, NE,
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
for (int q = 0; q < NQ; ++q)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
const double J11 = J(q,0,0,e);
const double J21 = J(q,1,0,e);
const double J12 = J(q,0,1,e);
const double J22 = J(q,1,1,e);
const double w_detJ = W[q] / (J11*J22) - (J21*J12);
if (coeffDim == 3 || coeffDim == 4) // Matrix coefficient version
{
const double J11 = J(qx,qy,0,0,e);
const double J21 = J(qx,qy,1,0,e);
const double J12 = J(qx,qy,0,1,e);
const double J22 = J(qx,qy,1,1,e);
const double w_detJ = W(qx,qy) / (J11*J22) - (J21*J12);
// First compute entries of R = MJ
const double M11 = coeff(i11, q, e);
const double M12 = (!symmetric) ? coeff(i12, q, e) : coeff(1, q, e);
const double M21 = (!symmetric) ? coeff(i21, q, e) : M12;
const double M22 = (!symmetric) ? coeff(i22, q, e) : coeff(2, q, e);
if (coeffDim == 3 || coeffDim == 4) // Matrix coefficient version
{
// First compute entries of R = MJ
const double M11 = coeff(i11,qx,qy,e);
const double M12 = (!symmetric) ? coeff(i12,qx,qy,e) : coeff(1,qx,qy,e);
const double M21 = (!symmetric) ? coeff(i21,qx,qy,e) : M12;
const double M22 = (!symmetric) ? coeff(i22,qx,qy,e) : coeff(2,qx,qy,e);
const double R11 = M11*J11 + M12*J21;
const double R12 = M11*J12 + M12*J22;
const double R21 = M21*J11 + M22*J21;
const double R22 = M21*J12 + M22*J22;
const double R11 = M11*J11 + M12*J21;
const double R12 = M11*J12 + M12*J22;
const double R21 = M21*J11 + M22*J21;
const double R22 = M21*J12 + M22*J22;
// Now set y to J^{-1} R
y(i11,qx,qy,e) = w_detJ * ( J22*R11 - J12*R21); // 1,1
y(i12,qx,qy,e) = w_detJ * ( J22*R12 - J12*R22); // 1,2
y(i21,qx,qy,e) = w_detJ * (-J21*R11 + J11*R21); // 2,1
y(i22,qx,qy,e) = w_detJ * (-J21*R12 + J11*R22); // 2,2
}
else if (coeffDim == 2) // Vector coefficient version
{
const double D1 = coeff(0,qx,qy,e);
const double D2 = coeff(1,qx,qy,e);
const double R11 = D1*J11;
const double R12 = D1*J12;
const double R21 = D2*J21;
const double R22 = D2*J22;
y(i11,qx,qy,e) = w_detJ * ( J22*R11 - J12*R21); // 1,1
y(i12,qx,qy,e) = w_detJ * ( J22*R12 - J12*R22); // 1,2
y(i21,qx,qy,e) = w_detJ * (-J21*R11 + J11*R21); // 2,1
y(i22,qx,qy,e) = w_detJ * (-J21*R12 + J11*R22); // 2,2
}
// Now set y to J^{-1} R
y(i11,q,e) = w_detJ * ( J22*R11 - J12*R21); // 1,1
y(i12,q,e) = w_detJ * ( J22*R12 - J12*R22); // 1,2
y(i21,q,e) = w_detJ * (-J21*R11 + J11*R21); // 2,1
y(i22,q,e) = w_detJ * (-J21*R12 + J11*R22); // 2,2
}
else if (coeffDim == 2) // Vector coefficient version
{
const double D1 = coeff(0, q, e);
const double D2 = coeff(1, q, e);
const double R11 = D1*J11;
const double R12 = D1*J12;
const double R21 = D2*J21;
const double R22 = D2*J22;
y(i11,q,e) = w_detJ * ( J22*R11 - J12*R21); // 1,1
y(i12,q,e) = w_detJ * ( J22*R12 - J12*R22); // 1,2
y(i21,q,e) = w_detJ * (-J21*R11 + J11*R21); // 2,1
y(i22,q,e) = w_detJ * (-J21*R12 + J11*R22); // 2,2
}
}
});
@@ -721,8 +737,14 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
{
// Assumes tensor-product elements
Mesh *mesh = trial_fes.GetMesh();
dim = mesh->Dimension();
const bool bdryInteg = isBdryInteg;
// TODO: this implementation applies boundary markers just by setting coeff to zero on boundary elements. It would be better to skip those elements in AddMultPA.
const FiniteElement *trial_fel = bdryInteg ? trial_fes.GetBE(
0) : trial_fes.GetFE(0);
const FiniteElement *trial_fel = trial_fes.GetFE(0);
const VectorTensorFiniteElement *trial_el =
dynamic_cast<const VectorTensorFiniteElement*>(trial_fel);
MFEM_VERIFY(trial_el != NULL, "Only VectorTensorFiniteElement is supported!");
@@ -732,21 +754,38 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
dynamic_cast<const VectorTensorFiniteElement*>(test_fel);
MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
if (bdryInteg)
{
MFEM_VERIFY(trial_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 3 &&
mesh->SpaceDimension() == 3 && trial_el->GetDim() == 2, "");
}
const IntegrationRule *ir
= IntRule ? IntRule : &MassIntegrator::GetRule(*trial_el, *trial_el,
bdryInteg ? *mesh->GetBdrElementTransformation(0) :
*mesh->GetElementTransformation(0));
const int dims = trial_el->GetDim();
MFEM_VERIFY(dims == 2 || dims == 3, "");
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
MFEM_VERIFY(dim == 2 || dim == 3, "");
ne = trial_fes.GetNE();
MFEM_VERIFY(ne == test_fes.GetNE(),
ne = bdryInteg ? trial_fes.GetNBE() : trial_fes.GetNE();
Vector bdryJac;
if (bdryInteg)
{
bdryJac.SetSize(ne*nq*6);
}
else
{
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
}
MFEM_VERIFY(ne == (bdryInteg ? test_fes.GetNBE() : test_fes.GetNE()),
"Different meshes for test and trial spaces");
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
mapsC = &trial_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
mapsO = &trial_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
dofs1D = mapsC->ndof;
@@ -780,7 +819,12 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
pa_data.SetSize((symmetric ? symmDims : MQfullDim) * nq * ne,
Device::GetMemoryType());
Vector coeff(coeffDim * ne * nq);
if (bdryInteg)
{
MFEM_VERIFY(coeffDim == 1 && dims == 2, "");
}
Vector coeff(coeffDim * nq * ne);
coeff = 1.0;
auto coeffh = Reshape(coeff.HostWrite(), coeffDim, nq, ne);
if (Q || VQ || MQ)
@@ -812,7 +856,8 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
for (int e=0; e<ne; ++e)
{
ElementTransformation *tr = mesh->GetElementTransformation(e);
ElementTransformation *tr = bdryInteg ? mesh->GetBdrElementTransformation(
e) : mesh->GetElementTransformation(e);
for (int p=0; p<nq; ++p)
{
if (MQ)
@@ -853,15 +898,77 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
}
}
if (trial_curl && test_curl && dim == 3)
if (el_marker)
{
PADiffusionSetup3D(quad1D, coeffDim, ne, ir->GetWeights(), geom->J,
MFEM_VERIFY(bdryInteg, "");
MFEM_VERIFY(el_marker->Size() == mesh->bdr_attributes.Max(), "");
for (int e=0; e<ne; ++e)
{
const int el_attr = (bdryInteg) ? mesh->GetBdrAttribute(e) : -1;
MFEM_VERIFY(el_attr > 0, "");
if ((*el_marker)[el_attr-1] == 0)
{
for (int p=0; p<nq; ++p)
{
for (int i=0; i<coeffDim; ++i)
{
coeffh(i, p, e) = 0.0;
}
}
}
}
}
if (isBdryInteg || (trial_el->GetDerivType() == mfem::FiniteElement::CURL &&
dim == 2 && mesh->SpaceDimension() == 3))
{
MFEM_VERIFY(coeffDim == 1,
""); // Vector coefficient not implemented in this case
if (isBdryInteg)
{
for (int e=0; e<ne; ++e)
{
ElementTransformation *tr = mesh->GetBdrElementTransformation(e);
for (int q = 0; q < nq; ++q)
{
tr->SetIntPoint(&(ir->IntPoint(q)));
const DenseMatrix& jac = tr->Jacobian();
for (int i=0; i<3; ++i)
for (int j=0; j<2; ++j)
{
bdryJac[(6*nq*e) + (3*nq*j) + (i*nq) + q] = jac(i,j);
}
}
}
}
else
{
// J is allocated as 3x3, but entries (i,j) are set only for 0 <= i < 3, 0 <= j < 2.
// That is, on each element J is 3x2 with 6 entries, not 9, so the last third of geom->J is unused.
MFEM_VERIFY(geom->J.Size() == 6 * ne * nq, "");
}
PAHcurlSetup2Din3D(quad1D, ne, ir->GetWeights(),
isBdryInteg ? bdryJac : geom->J,
coeff, pa_data);
}
else if (trial_curl && test_curl && dim == 3)
{
#ifdef SETUPONHOST
PAHcurlSetup3DHost(quad1D, coeffDim, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
#else
PAHcurlSetup3D(quad1D, coeffDim, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
#endif
}
else if (trial_curl && test_curl && dim == 2)
{
PADiffusionSetup2D<2>(quad1D, coeffDim, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
PAHcurlSetup2D(quad1D, coeffDim, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else if (trial_div && test_div && dim == 3)
{
@@ -966,7 +1073,7 @@ void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
const bool test_curl = (test_fetype == mfem::FiniteElement::CURL);
const bool test_div = (test_fetype == mfem::FiniteElement::DIV);
if (dim == 3)
if (dim == 3 && !isBdryInteg)
{
if (trial_curl && test_curl)
{
@@ -1108,13 +1215,13 @@ void MixedVectorGradientIntegrator::AssemblePA(const FiniteElementSpace
// Use the same setup functions as VectorFEMassIntegrator.
if (test_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 3)
{
PADiffusionSetup3D(quad1D, 1, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
PAHcurlSetup3D(quad1D, 1, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else if (test_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 2)
{
PADiffusionSetup2D<2>(quad1D, 1, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
PAHcurlSetup2D(quad1D, 1, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
}
else
{
+13 -40
View File
@@ -38,11 +38,11 @@ double FunctionCoefficient::Eval(ElementTransformation & T,
if (Function)
{
return Function(transip);
return ((*Function)(transip));
}
else
{
return TDFunction(transip, GetTime());
return (*TDFunction)(transip, GetTime());
}
}
@@ -112,11 +112,11 @@ void VectorFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
V.SetSize(vdim);
if (Function)
{
Function(transip, V);
(*Function)(transip, V);
}
else
{
TDFunction(transip, GetTime(), V);
(*TDFunction)(transip, GetTime(), V);
}
if (Q)
{
@@ -301,45 +301,18 @@ void MatrixFunctionCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
K.SetSize(height, width);
if (symmetric) // Use SymmFunction
if (Function)
{
MFEM_VERIFY(height == width && SymmFunction,
"MatrixFunctionCoefficient is not symmetric");
Vector Ksym((width * (width + 1)) / 2); // 1x1: 1, 2x2: 3, 3x3: 6
SymmFunction(transip, Ksym);
// Copy upper triangular values from Ksym to the full matrix K
int os = 0;
for (int i=0; i<height; ++i)
{
for (int j=i; j<width; ++j)
{
const double Kij = Ksym[j - i + os];
K(i,j) = Kij;
if (j != i) { K(j,i) = Kij; }
}
os += width - i;
}
(*Function)(transip, K);
}
else if (TDFunction)
{
(*TDFunction)(transip, GetTime(), K);
}
else
{
if (Function)
{
Function(transip, K);
}
else if (TDFunction)
{
TDFunction(transip, GetTime(), K);
}
else
{
K = mat;
}
K = mat;
}
if (Q)
{
K *= Q->Eval(T, ip, GetTime());
@@ -350,7 +323,7 @@ void MatrixFunctionCoefficient::EvalSymmetric(Vector &K,
ElementTransformation &T,
const IntegrationPoint &ip)
{
MFEM_VERIFY(symmetric && height == width && SymmFunction,
MFEM_VERIFY(symmetric && height == width && height < 4 && SymmFunction,
"MatrixFunctionCoefficient is not symmetric");
double x[3];
@@ -362,7 +335,7 @@ void MatrixFunctionCoefficient::EvalSymmetric(Vector &K,
if (SymmFunction)
{
SymmFunction(transip, K);
(*SymmFunction)(transip, K);
}
if (Q)
+88 -82
View File
@@ -12,8 +12,6 @@
#ifndef MFEM_COEFFICIENT
#define MFEM_COEFFICIENT
#include <functional>
#include "../config/config.hpp"
#include "../linalg/linalg.hpp"
#include "intrules.hpp"
@@ -125,25 +123,28 @@ public:
const IntegrationPoint &ip);
};
/// A general function coefficient
/// A general C-function coefficient
class FunctionCoefficient : public Coefficient
{
protected:
std::function<double(const Vector &)> Function;
std::function<double(const Vector &, double)> TDFunction;
double (*Function)(const Vector &);
double (*TDFunction)(const Vector &, double);
public:
/// Define a time-independent coefficient from a std function
/** \param F time-independent std::function */
FunctionCoefficient(std::function<double(const Vector &)> F)
: Function(std::move(F))
{ }
/// Define a time-independent coefficient from a pointer to a C-function
FunctionCoefficient(double (*f)(const Vector &))
{
Function = f;
TDFunction = NULL;
}
/// Define a time-dependent coefficient from a std function
/** \param TDF time-dependent function */
FunctionCoefficient(std::function<double(const Vector &, double)> TDF)
: TDFunction(std::move(TDF))
{ }
/// Define a time-dependent coefficient from a pointer to a C-function
FunctionCoefficient(double (*tdf)(const Vector &, double))
{
Function = NULL;
TDFunction = tdf;
}
/// (DEPRECATED) Define a time-independent coefficient from a C-function
/** @deprecated Use the method where the C-function, @a f, uses a const
@@ -404,34 +405,33 @@ public:
const Vector& GetVec() { return vec; }
};
/// A general vector function coefficient
/// A general C-function vector coefficient
class VectorFunctionCoefficient : public VectorCoefficient
{
private:
std::function<void(const Vector &, Vector &)> Function;
std::function<void(const Vector &, double, Vector &)> TDFunction;
void (*Function)(const Vector &, Vector &);
void (*TDFunction)(const Vector &, double, Vector &);
Coefficient *Q;
public:
/// Define a time-independent vector coefficient from a std function
/** \param dim - the size of the vector
\param F - time-independent function
\param q - optional scalar Coefficient to scale the vector coefficient */
VectorFunctionCoefficient(int dim,
std::function<void(const Vector &, Vector &)> F,
Coefficient *q = nullptr)
: VectorCoefficient(dim), Function(std::move(F)), Q(q)
{ }
/// Construct a time-independent vector coefficient from a C-function
VectorFunctionCoefficient(int dim, void (*F)(const Vector &, Vector &),
Coefficient *q = NULL)
: VectorCoefficient(dim), Q(q)
{
Function = F;
TDFunction = NULL;
}
/// Define a time-dependent vector coefficient from a std function
/** \param dim - the size of the vector
\param TDF - time-dependent function
\param q - optional scalar Coefficient to scale the vector coefficient */
/// Construct a time-dependent vector coefficient from a C-function
VectorFunctionCoefficient(int dim,
std::function<void(const Vector &, double, Vector &)> TDF,
Coefficient *q = nullptr)
: VectorCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
{ }
void (*TDF)(const Vector &, double, Vector &),
Coefficient *q = NULL)
: VectorCoefficient(dim), Q(q)
{
Function = NULL;
TDFunction = TDF;
}
using VectorCoefficient::Eval;
/// Evaluate the vector coefficient at @a ip.
@@ -721,6 +721,7 @@ public:
/// For backward compatibility get the width of the matrix.
int GetVDim() const { return width; }
void SetSymmetric(bool s) { symmetric = s; }
bool IsSymmetric() const { return symmetric; }
/** @brief Evaluate the matrix coefficient in the element described by @a T
@@ -761,56 +762,61 @@ public:
/** @brief A matrix coefficient with an optional scalar coefficient multiplier
\a q. The matrix function can either be represented by a std function or
a constant matrix provided when constructing this object. */
\a q. The matrix function can either be represented by a C-function or a
constant matrix provided when constructing this object. */
class MatrixFunctionCoefficient : public MatrixCoefficient
{
private:
std::function<void(const Vector &, DenseMatrix &)> Function;
std::function<void(const Vector &, Vector &)> SymmFunction;
std::function<void(const Vector &, double, DenseMatrix &)> TDFunction;
void (*Function)(const Vector &, DenseMatrix &);
void (*SymmFunction)(const Vector &, Vector &);
void (*TDFunction)(const Vector &, double, DenseMatrix &);
Coefficient *Q;
DenseMatrix mat;
public:
/// Define a time-independent square matrix coefficient from a std function
/** \param dim - the size of the matrix
\param F - time-independent function
\param q - optional scalar Coefficient to scale the matrix coefficient */
MatrixFunctionCoefficient(int dim,
std::function<void(const Vector &, DenseMatrix &)> F,
Coefficient *q = nullptr)
: MatrixCoefficient(dim), Function(std::move(F)), Q(q), mat(0)
{ }
/// Define a constant matrix coefficient times a scalar Coefficient
/** \param m - constant matrix
\param q - optional scalar Coefficient to scale the matrix coefficient */
MatrixFunctionCoefficient(const DenseMatrix &m, Coefficient &q)
: MatrixCoefficient(m.Height(), m.Width()), Q(&q), mat(m)
{ }
/// Define a time-dependent square matrix coefficient from a std function
/** \param dim - the size of the matrix
\param TDF - time-dependent function
\param q - optional scalar Coefficient to scale the matrix coefficient */
MatrixFunctionCoefficient(int dim,
std::function<void(const Vector &, double, DenseMatrix &)> TDF,
Coefficient *q = nullptr)
: MatrixCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
{ }
/** @brief Define a time-independent symmetric square matrix coefficient from
a std function */
/** \param dim - the size of the matrix
\param SymmF - function used in EvalSymmetric
\param q - optional scalar Coefficient to scale the matrix coefficient */
MatrixFunctionCoefficient(int dim,
std::function<void(const Vector &, Vector &)> SymmF,
/// Construct a square matrix coefficient from a C-function without time
/// dependence.
MatrixFunctionCoefficient(int dim, void (*F)(const Vector &, DenseMatrix &),
Coefficient *q = NULL)
: MatrixCoefficient(dim, true), SymmFunction(std::move(SymmF)), Q(q), mat(0)
{ }
: MatrixCoefficient(dim), Q(q)
{
Function = F;
TDFunction = NULL;
mat.SetSize(0);
}
/// Construct a constant matrix coefficient times a scalar Coefficient
MatrixFunctionCoefficient(const DenseMatrix &m, Coefficient &q)
: MatrixCoefficient(m.Height(), m.Width()), Q(&q)
{
Function = NULL;
TDFunction = NULL;
mat = m;
}
/// Construct a square matrix coefficient from a C-function with
/// time-dependence.
MatrixFunctionCoefficient(int dim,
void (*TDF)(const Vector &, double, DenseMatrix &),
Coefficient *q = NULL)
: MatrixCoefficient(dim), Q(q)
{
Function = NULL;
TDFunction = TDF;
mat.SetSize(0);
}
/// Construct a symmetric square matrix coefficient from a C-function
/// defining a vector function used by EvalSymmetric
MatrixFunctionCoefficient(int dim, void (*F)(const Vector &, Vector &),
Coefficient *q = NULL)
: MatrixCoefficient(dim, true), Q(q)
{
SymmFunction = F;
Function = NULL;
TDFunction = NULL;
mat.SetSize(0);
}
/// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
@@ -883,7 +889,7 @@ public:
/// Coefficients based on sums, products, or other functions of coefficients.
///@{
/** @brief Scalar coefficient defined as the linear combination of two scalar
/** Scalar coefficient defined as the linear combination of two scalar
coefficients or a scalar and a scalar coefficient */
class SumCoefficient : public Coefficient
{
@@ -940,8 +946,8 @@ public:
}
};
/** @brief Scalar coefficient defined as the product of two scalar coefficients
or a scalar and a scalar coefficient. */
/** Scalar coefficient defined as the product of two scalar coefficients or
a scalar and a scalar coefficient. */
class ProductCoefficient : public Coefficient
{
private:
@@ -979,8 +985,8 @@ public:
{ return ((a == NULL ) ? aConst : a->Eval(T, ip) ) * b->Eval(T, ip); }
};
/** @brief Scalar coefficient defined as the ratio of two scalars where one or
both scalars are scalar coefficients. */
/** Scalar coefficient defined as the ratio of two scalars where one or both
scalars are scalar coefficients. */
class RatioCoefficient : public Coefficient
{
private:
+1 -1
View File
@@ -968,7 +968,7 @@ ParComplexLinearForm::ParallelAssemble()
2*(pfes->GlobalTrueVSize()),
tdof_offsets);
tv->Write();
((Vector*) tv)->Write(true);
Vector tvr; tvr.MakeRef(*tv, 0, tvsize);
Vector tvi; tvi.MakeRef(*tv, tvsize, tvsize);
+2 -4
View File
@@ -238,8 +238,7 @@ public:
/// Set the desired assembly level.
/** Valid choices are:
- AssemblyLevel::LEGACYFULL (default)
- AssemblyLevel::FULL
- AssemblyLevel::FULL (default)
- AssemblyLevel::PARTIAL
- AssemblyLevel::ELEMENT
- AssemblyLevel::NONE
@@ -576,8 +575,7 @@ public:
/// Set the desired assembly level.
/** Valid choices are:
- AssemblyLevel::LEGACYFULL (default)
- AssemblyLevel::FULL
- AssemblyLevel::FULL (default)
- AssemblyLevel::PARTIAL
- AssemblyLevel::ELEMENT
- AssemblyLevel::NONE
+1 -1
View File
@@ -908,7 +908,7 @@ void ParaViewDataCollection::Save()
// CELL DATA
out << "<PCellData>\n";
out << "\t<PDataArray type=\"Int32\" Name=\"" << "attribute"
out << "\t<PDataArray type=\"Int32\" Name=\"" << "material"
<< "\" NumberOfComponents=\"1\""
<< " format=\"" << GetDataFormatString() << "\"/>\n";
out << "</PCellData>\n";
-4
View File
@@ -382,10 +382,6 @@ public:
int Error() const { return error; }
/// Reset the error state
void ResetError(int err = NO_ERROR) { error = err; }
#ifdef MFEM_USE_MPI
friend class ParMesh;
#endif
};
+3 -5
View File
@@ -14,7 +14,6 @@
#include "../general/text.hpp"
#include "../general/forall.hpp"
#include "../mesh/mesh_headers.hpp"
#include "../fem/libceed/ceed.hpp"
#include "fem.hpp"
#include <cmath>
@@ -699,7 +698,7 @@ void FiniteElementSpace::BuildConformingInterpolation() const
for (int entity = 1; entity <= 2; entity++)
{
const NCMesh::NCList &list = mesh->ncmesh->GetNCList(entity);
if (!list.masters.Size()) { continue; }
if (!list.masters.size()) { continue; }
Array<int> master_dofs, slave_dofs;
@@ -707,7 +706,7 @@ void FiniteElementSpace::BuildConformingInterpolation() const
DenseMatrix I;
// loop through all master edges/faces, constrain their slave edges/faces
for (int mi = 0; mi < list.masters.Size(); mi++)
for (unsigned mi = 0; mi < list.masters.size(); mi++)
{
const NCMesh::Master &master = list.masters[mi];
@@ -731,7 +730,7 @@ void FiniteElementSpace::BuildConformingInterpolation() const
GetEntityDofs(entity, slave.index, slave_dofs, master.Geom());
if (!slave_dofs.Size()) { continue; }
list.OrientedPointMatrix(slave, T.GetPointMat());
slave.OrientedPointMatrix(T.GetPointMat());
fe->GetLocalInterpolation(T, I);
// make each slave DOF dependent on all master DOFs
@@ -2161,7 +2160,6 @@ void FiniteElementSpace::Destroy()
delete [] bdofs;
delete [] fdofs;
}
RemoveCeedBasisAndRestriction(this);
}
void FiniteElementSpace::GetTransferOperator(
+5 -1
View File
@@ -330,6 +330,11 @@ public:
virtual const Operator *GetProlongationMatrix() const
{ return GetConformingProlongation(); }
/// Return a prolongation operator that only acts on local dofs
/** The returned operator is owned by the FiniteElementSpace. */
virtual const Operator *GetLocalProlongationMatrix() const
{ return GetConformingProlongation(); }
/// The returned SparseMatrix is owned by the FiniteElementSpace.
virtual const SparseMatrix *GetRestrictionMatrix() const
{ return GetConformingRestriction(); }
@@ -984,7 +989,6 @@ inline bool UsesTensorBasis(const FiniteElementSpace& fes)
{
return dynamic_cast<const mfem::TensorBasisElement *>(fes.GetFE(0))!=nullptr;
}
}
#endif
+1 -1
View File
@@ -68,7 +68,7 @@ void Hybridization::ConstructC()
{
const int dim = pmesh->Dimension();
const NCMesh::NCList &shared = pmesh->pncmesh->GetSharedList(dim-1);
num_shared_slave_faces = (HYPRE_Int) shared.slaves.Size();
num_shared_slave_faces = (HYPRE_Int)shared.slaves.size();
MPI_Allreduce(&num_shared_slave_faces, &glob_num_shared_slave_faces, 1,
HYPRE_MPI_INT, MPI_SUM, pmesh->GetComm());
MFEM_ASSERT(glob_num_shared_slave_faces%2 == 0, "");
+375 -515
View File
File diff suppressed because it is too large Load Diff
+92 -156
View File
@@ -14,29 +14,68 @@
#include "../../config/config.hpp"
#ifdef MFEM_USE_CEED
#include "../../general/device.hpp"
#include "../../linalg/vector.hpp"
#ifdef MFEM_USE_CEED
#include <ceed.h>
#include <ceed-hash.h>
#endif
#include <tuple>
#include <unordered_map>
namespace mfem
{
class Mesh;
class FiniteElementSpace;
class GridFunction;
class IntegrationRule;
class Coefficient;
#ifdef MFEM_USE_CEED
/// A structure used to pass additional data to f_build_diff and f_apply_diff
struct BuildContext { CeedInt dim, space_dim, vdim; CeedScalar coeff; };
// Hash table for CeedBasis
using CeedBasisKey =
std::tuple<const FiniteElementSpace*, const IntegrationRule*, int, int, int>;
struct CeedBasisHash
{
std::size_t operator()(const CeedBasisKey& k) const
{
return CeedHashCombine(CeedHashCombine(CeedHashInt(
reinterpret_cast<CeedHash64_t>(std::get<0>(k))),
CeedHashInt(
reinterpret_cast<CeedHash64_t>(std::get<1>(k)))),
CeedHashCombine(CeedHashCombine(CeedHashInt(std::get<2>(k)),
CeedHashInt(std::get<3>(k))),
CeedHashInt(std::get<4>(k))));
}
};
using CeedBasisMap =
std::unordered_map<const CeedBasisKey, CeedBasis, CeedBasisHash>;
enum class CeedCoeff { Const, Grid, Quad };
// Hash table for CeedElemRestriction
using CeedRestrKey = std::tuple<const FiniteElementSpace*, int, int, int>;
struct CeedRestrHash
{
std::size_t operator()(const CeedRestrKey& k) const
{
return CeedHashCombine(CeedHashCombine(CeedHashInt(
reinterpret_cast<CeedHash64_t>(std::get<0>(k))),
CeedHashInt(std::get<1>(k))),
CeedHashCombine(CeedHashInt(std::get<2>(k)),
CeedHashInt(std::get<3>(k))));
}
};
using CeedRestrMap =
std::unordered_map<const CeedRestrKey, CeedElemRestriction, CeedRestrHash>;
namespace internal
{
extern Ceed ceed; // defined in device.cpp
extern CeedBasisMap basis_map;
extern CeedRestrMap restr_map;
}
/// A structure used to pass additional data to f_build_diff and f_apply_diff
struct BuildContext { CeedInt dim, space_dim; CeedScalar coeff; };
enum class CeedCoeff { Const, Grid };
struct CeedConstCoeff
{
@@ -51,17 +90,8 @@ struct CeedGridCoeff
CeedVector coeffVector;
};
struct CeedQuadCoeff
{
Vector coeff;
CeedElemRestriction restr;
CeedVector coeffVector;
};
#endif
struct CeedData
{
#ifdef MFEM_USE_CEED
CeedOperator build_oper, oper;
CeedBasis basis, mesh_basis;
CeedElemRestriction restr, mesh_restr, restr_i, mesh_restr_i;
@@ -74,51 +104,39 @@ struct CeedData
CeedVector u, v;
CeedData()
: build_oper(nullptr), oper(nullptr), basis(nullptr), mesh_basis(nullptr),
restr(nullptr), mesh_restr(nullptr), restr_i(nullptr), mesh_restr_i(nullptr),
apply_qfunc(nullptr), build_qfunc(nullptr), node_coords(nullptr),
rho(nullptr), coeff(nullptr), build_ctx(nullptr) { }
~CeedData()
{
CeedOperatorDestroy(&build_oper);
CeedOperatorDestroy(&oper);
CeedElemRestrictionDestroy(&restr_i);
CeedElemRestrictionDestroy(&mesh_restr_i);
CeedQFunctionDestroy(&apply_qfunc);
CeedQFunctionDestroy(&build_qfunc);
CeedQFunctionContextDestroy(&build_ctx);
CeedVectorDestroy(&node_coords);
CeedVectorDestroy(&rho);
if (coeff_type==CeedCoeff::Const)
if (coeff_type==CeedCoeff::Grid)
{
delete static_cast<CeedConstCoeff*>(coeff);
}
else if (coeff_type==CeedCoeff::Grid)
{
CeedGridCoeff* c = static_cast<CeedGridCoeff*>(coeff);
CeedGridCoeff* c = (CeedGridCoeff*)coeff;
CeedVectorDestroy(&c->coeffVector);
delete c;
}
else if (coeff_type==CeedCoeff::Quad)
else
{
CeedQuadCoeff* c = static_cast<CeedQuadCoeff*>(coeff);
CeedVectorDestroy(&c->coeffVector);
delete c;
delete (CeedConstCoeff*)coeff;
}
CeedVectorDestroy(&u);
CeedVectorDestroy(&v);
}
#endif
};
/** This structure contains the data to assemble a PA operator with libCEED.
See libceed/mass.cpp or libceed/diffusion.cpp for examples. */
struct CeedPAOperator
{
#ifdef MFEM_USE_CEED
/** The finite element space for the trial and test functions. */
const FiniteElementSpace &fes;
/** The Integration Rule to use to compute the operator. */
/** The Integration Rule to use to compote the operator. */
const IntegrationRule &ir;
/** The number of quadrature data at each quadrature point. */
int qdatasize;
@@ -129,11 +147,11 @@ struct CeedPAOperator
std::string const_func;
/** The Qfunction to build the quadrature data with constant coefficient. */
CeedQFunctionUser const_qf;
/** The name of the Qfunction to build the quadrature data with a coefficient
evaluated at quadrature points. */
std::string quad_func;
/** The Qfunction to build the quad. data with a coefficient. */
CeedQFunctionUser quad_qf;
/** The name of the Qfunction to build the quadrature data with grid function
coefficient. */
std::string grid_func;
/** The Qfunction to build the quad. data with grid function coefficient. */
CeedQFunctionUser grid_qf;
/** The name of the Qfunction to apply the operator. */
std::string apply_func;
/** The Qfunction to apply the operator. */
@@ -144,84 +162,11 @@ struct CeedPAOperator
/** The evaluation mode to apply to the test function ( CEED_EVAL_INTERP,
CEED_EVAL_GRAD, etc.)*/
CeedEvalMode test_op;
#endif
};
struct CeedMFOperator
{
#ifdef MFEM_USE_CEED
/** The finite element space for the trial and test functions. */
const FiniteElementSpace &fes;
/** The Integration Rule to use to compote the operator. */
const IntegrationRule &ir;
/** The path to the header containing the functions for libCEED. */
std::string header;
/** The name of the Qfunction to apply the quadrature data with a constant
coefficient.*/
std::string const_func;
/** The Qfunction to apply the quadrature data with constant coefficient. */
CeedQFunctionUser const_qf;
/** The name of the Qfunction to apply the quadrature data with a coefficient
evaluated at quadrature point. */
std::string quad_func;
/** The Qfunction to apply the quad. data with a coefficient evaluated at
quadrature point. */
CeedQFunctionUser quad_qf;
/** The evaluation mode to apply to the trial function (CEED_EVAL_INTERP,
CEED_EVAL_GRAD, etc.) */
CeedEvalMode trial_op;
/** The evaluation mode to apply to the test function ( CEED_EVAL_INTERP,
CEED_EVAL_GRAD, etc.)*/
CeedEvalMode test_op;
#endif
};
/** @brief Identifies the type of coefficient of the Integrator to initialize
accordingly the CeedData. */
void InitCeedCoeff(Coefficient* Q, Mesh &mesh, const IntegrationRule &ir,
CeedData* ptr);
/** This function initializes an arbitrary linear operator using the partial
assembly decomposition in libCEED. The operator details are described by the
struct CEEDPAOperator input. */
void CeedPAAssemble(const CeedPAOperator& op,
CeedData& ceedData);
/** This function initializes an arbitrary linear operator using a fully matrix
free decomposition. The operator details are described by the
struct CEEDPAOperator input. */
void CeedMFAssemble(const CeedMFOperator& op,
CeedData& ceedData);
/** @brief Function that applies a libCEED operator. */
void CeedAddMult(const CeedData *ceedDataPtr,
const Vector &x,
Vector &y);
/** @brief Function that assembles a libCEED operator diagonal. */
void CeedAssembleDiagonal(const CeedData *ceedDataPtr,
Vector &diag);
/** @brief Function that determines if a CEED kernel should be used, based on
the current mfem::Device configuration. */
inline bool DeviceCanUseCeed()
{
return Device::Allows(Backend::CEED_MASK);
}
/** @brief Remove from ceed_basis_map and ceed_restr_map the entries associated
with the given @a fes. */
void RemoveCeedBasisAndRestriction(const FiniteElementSpace *fes);
#ifdef MFEM_USE_CEED
/// Initialize a CeedVector from a Vector
void InitCeedVector(const Vector &v, CeedVector &cv);
/// Initialize a strided CeedElemRestriction
void InitCeedStridedRestriction(const FiniteElementSpace &fes,
CeedInt nelem, CeedInt nqpts, CeedInt qdatasize,
const CeedInt *strides,
CeedElemRestriction *restr);
void InitCeedCoeff(Coefficient* Q, CeedData* ptr);
/// Initialize a CeedBasis and a CeedElemRestriction
void InitCeedBasisAndRestriction(const FiniteElementSpace &fes,
@@ -232,52 +177,43 @@ void InitCeedBasisAndRestriction(const FiniteElementSpace &fes,
/// Return the path to the libCEED q-function headers.
const std::string &GetCeedPath();
// Hash table for CeedBasis
using CeedBasisKey =
std::tuple<const FiniteElementSpace*, const IntegrationRule*, int, int, int>;
struct CeedBasisHash
{
std::size_t operator()(const CeedBasisKey& k) const
{
return CeedHashCombine(
CeedHashCombine(
CeedHashInt(reinterpret_cast<CeedHash64_t>(std::get<0>(k))),
CeedHashInt(reinterpret_cast<CeedHash64_t>(std::get<1>(k)))),
CeedHashCombine(
CeedHashCombine(CeedHashInt(std::get<2>(k)),
CeedHashInt(std::get<3>(k))),
CeedHashInt(std::get<4>(k))));
}
};
using CeedBasisMap =
std::unordered_map<const CeedBasisKey, CeedBasis, CeedBasisHash>;
/** This function initializes an arbitrary linear operator using the partial
assembly decomposition in libCEED. The operator details are described by the
struct CEEDPAOperator input. */
void CeedPAAssemble(const CeedPAOperator& op,
CeedData& ceedData);
// Hash table for CeedElemRestriction
using CeedRestrKey = std::tuple<const FiniteElementSpace*, int, int, int>;
struct CeedRestrHash
{
std::size_t operator()(const CeedRestrKey& k) const
{
return CeedHashCombine(
CeedHashCombine(
CeedHashInt(reinterpret_cast<CeedHash64_t>(std::get<0>(k))),
CeedHashInt(std::get<1>(k))),
CeedHashCombine(CeedHashInt(std::get<2>(k)),
CeedHashInt(std::get<3>(k))));
}
};
using CeedRestrMap =
std::unordered_map<const CeedRestrKey, CeedElemRestriction, CeedRestrHash>;
/** @brief Function that applies a libCEED PA operator. */
void CeedAddMultPA(const CeedData *ceedDataPtr,
const Vector &x,
Vector &y);
namespace internal
/** @brief Function that assembles a libCEED PA operator diagonal. */
void CeedAssembleDiagonalPA(const CeedData *ceedDataPtr,
Vector &diag);
/** @brief Function that determines if a CEED kernel should be used, based on
the current mfem::Device configuration. */
inline bool DeviceCanUseCeed()
{
extern Ceed ceed; // defined in device.cpp
extern CeedBasisMap basis_map;
extern CeedRestrMap restr_map;
return Device::Allows(Backend::CEED_CUDA) ||
(Device::Allows(Backend::CEED_CPU) &&
!Device::Allows(Backend::DEVICE_MASK|Backend::OMP_MASK));
}
#endif // MFEM_USE_CEED
} // namespace mfem
#else // MFEM_USE_CEED
namespace mfem
{
inline bool DeviceCanUseCeed()
{
return false;
}
} // namespace mfem
#endif // MFEM_USE_CEED
#endif // MFEM_LIBCEED_HPP
+5 -30
View File
@@ -11,10 +11,9 @@
#include "diffusion.hpp"
#include "ceed.hpp"
#ifdef MFEM_USE_CEED
#include "ceed.hpp"
#include "diffusion.h"
#endif
namespace mfem
{
@@ -22,42 +21,18 @@ namespace mfem
void CeedPADiffusionAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &irm, CeedData& ceedData)
{
#ifdef MFEM_USE_CEED
Mesh &mesh = *fes.GetMesh();
// Perform checks for some assumptions made in the Q-functions.
MFEM_VERIFY(mesh.Dimension() == mesh.SpaceDimension(), "case not supported");
MFEM_VERIFY(fes.GetVDim() == 1 || fes.GetVDim() == mesh.Dimension(),
"case not supported");
int dim = mesh.Dimension();
CeedInt dim = fes.GetMesh()->SpaceDimension();
CeedPAOperator diffOp = {fes, irm,
dim * (dim + 1) / 2, "/diffusion.h",
":f_build_diff_const", f_build_diff_const,
":f_build_diff_quad", f_build_diff_quad,
":f_build_diff_grid", f_build_diff_grid,
":f_apply_diff", f_apply_diff,
CEED_EVAL_GRAD,
CEED_EVAL_GRAD
};
CeedPAAssemble(diffOp, ceedData);
#else
mfem_error("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
#endif
}
void CeedMFDiffusionAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &irm, CeedData& ceedData)
{
#ifdef MFEM_USE_CEED
CeedMFOperator diffOp = {fes, irm,
"/diffusion.h",
":f_apply_diff_mf_const", f_apply_diff_mf_const,
":f_apply_diff_mf_quad", f_apply_diff_mf_quad,
CEED_EVAL_GRAD,
CEED_EVAL_GRAD
};
CeedMFAssemble(diffOp, ceedData);
#else
mfem_error("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
#endif
}
} // namespace mfem
#endif // MFEM_USE_CEED
+7 -354
View File
@@ -10,7 +10,7 @@
// CONTRIBUTING.md for details.
/// A structure used to pass additional data to f_build_diff and f_apply_diff
struct BuildContext { CeedInt dim, space_dim, vdim; CeedScalar coeff; };
struct BuildContext { CeedInt dim, space_dim; CeedScalar coeff; };
/// libCEED Q-function for building quadrature data for a diffusion operator
/// with a constant coefficient
@@ -88,8 +88,8 @@ CEED_QFUNCTION(f_build_diff_const)(void *ctx, CeedInt Q,
}
/// libCEED Q-function for building quadrature data for a diffusion operator
/// coefficient evaluated at quadrature points.
CEED_QFUNCTION(f_build_diff_quad)(void *ctx, CeedInt Q,
/// with a grid function coefficient
CEED_QFUNCTION(f_build_diff_grid)(void *ctx, CeedInt Q,
const CeedScalar *const *in,
CeedScalar *const *out)
{
@@ -172,15 +172,15 @@ CEED_QFUNCTION(f_apply_diff)(void *ctx, CeedInt Q,
// in[0], out[0] have shape [dim, nc=1, Q]
const CeedScalar *ug = in[0], *qd = in[1];
CeedScalar *vg = out[0];
switch (10*bc->dim + bc->vdim)
switch (bc->dim)
{
case 11:
case 1:
for (CeedInt i = 0; i < Q; i++)
{
vg[i] = ug[i] * qd[i];
}
break;
case 21:
case 2:
for (CeedInt i = 0; i < Q; i++)
{
const CeedScalar ug0 = ug[i + Q * 0];
@@ -189,23 +189,7 @@ CEED_QFUNCTION(f_apply_diff)(void *ctx, CeedInt Q,
vg[i + Q * 1] = qd[i + Q * 1] * ug0 + qd[i + Q * 2] * ug1;
}
break;
case 22:
for (CeedInt i = 0; i < Q; i++)
{
const CeedScalar qd00 = qd[i + Q * 0];
const CeedScalar qd01 = qd[i + Q * 1];
const CeedScalar qd10 = qd01;
const CeedScalar qd11 = qd[i + Q * 2];
for (CeedInt c = 0; c < 2; c++)
{
const CeedScalar ug0 = ug[i + Q * (c+2*0)];
const CeedScalar ug1 = ug[i + Q * (c+2*1)];
vg[i + Q * (c+2*0)] = qd00 * ug0 + qd01 * ug1;
vg[i + Q * (c+2*1)] = qd10 * ug0 + qd11 * ug1;
}
}
break;
case 31:
case 3:
for (CeedInt i = 0; i < Q; i++)
{
const CeedScalar ug0 = ug[i + Q * 0];
@@ -216,337 +200,6 @@ CEED_QFUNCTION(f_apply_diff)(void *ctx, CeedInt Q,
vg[i + Q * 2] = qd[i + Q * 2] * ug0 + qd[i + Q * 4] * ug1 + qd[i + Q * 5] * ug2;
}
break;
case 33:
for (CeedInt i = 0; i < Q; i++)
{
const CeedScalar qd00 = qd[i + Q * 0];
const CeedScalar qd01 = qd[i + Q * 1];
const CeedScalar qd02 = qd[i + Q * 2];
const CeedScalar qd10 = qd01;
const CeedScalar qd11 = qd[i + Q * 3];
const CeedScalar qd12 = qd[i + Q * 4];
const CeedScalar qd20 = qd02;
const CeedScalar qd21 = qd12;
const CeedScalar qd22 = qd[i + Q * 5];
for (CeedInt c = 0; c < 3; c++)
{
const CeedScalar ug0 = ug[i + Q * (c+3*0)];
const CeedScalar ug1 = ug[i + Q * (c+3*1)];
const CeedScalar ug2 = ug[i + Q * (c+3*2)];
vg[i + Q * (c+3*0)] = qd00 * ug0 + qd01 * ug1 + qd02 * ug2;
vg[i + Q * (c+3*1)] = qd10 * ug0 + qd11 * ug1 + qd12 * ug2;
vg[i + Q * (c+3*2)] = qd20 * ug0 + qd21 * ug1 + qd22 * ug2;
}
}
break;
}
return 0;
}
/// libCEED Q-function for applying a diff operator
CEED_QFUNCTION(f_apply_diff_mf_const)(void *ctx, CeedInt Q,
const CeedScalar *const *in,
CeedScalar *const *out)
{
BuildContext *bc = (BuildContext*)ctx;
// in[0], out[0] have shape [dim, nc=1, Q]
// in[1] is Jacobians with shape [dim, nc=dim, Q]
// in[2] is quadrature weights, size (Q)
//
// At every quadrature point, compute qw/det(J).adj(J).adj(J)^T
const CeedScalar coeff = bc->coeff;
const CeedScalar *ug = in[0], *J = in[1], *qw = in[2];
CeedScalar *vg = out[0];
switch (10 * bc->dim + bc->vdim)
{
case 11:
for (CeedInt i = 0; i < Q; i++)
{
const CeedScalar qd = coeff * qw[i] / J[i];
vg[i] = ug[i] * qd;
}
break;
case 21:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 2 qd: 0 1 adj(J): J22 -J12
// 1 3 1 2 -J21 J11
const CeedScalar J11 = J[i + Q * 0];
const CeedScalar J21 = J[i + Q * 1];
const CeedScalar J12 = J[i + Q * 2];
const CeedScalar J22 = J[i + Q * 3];
const CeedScalar w = qw[i] / (J11 * J22 - J21 * J12);
CeedScalar qd[3];
qd[0] = coeff * w * (J12 * J12 + J22 * J22);
qd[1] = - coeff * w * (J11 * J12 + J21 * J22);
qd[2] = coeff * w * (J11 * J11 + J21 * J21);
const CeedScalar ug0 = ug[i + Q * 0];
const CeedScalar ug1 = ug[i + Q * 1];
vg[i + Q * 0] = qd[0] * ug0 + qd[1] * ug1;
vg[i + Q * 1] = qd[1] * ug0 + qd[2] * ug1;
}
break;
case 22:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 2 qd: 0 1 adj(J): J22 -J12
// 1 3 1 2 -J21 J11
const CeedScalar J11 = J[i + Q * 0];
const CeedScalar J21 = J[i + Q * 1];
const CeedScalar J12 = J[i + Q * 2];
const CeedScalar J22 = J[i + Q * 3];
const CeedScalar w = qw[i] / (J11 * J22 - J21 * J12);
CeedScalar qd[3];
qd[0] = coeff * w * (J12 * J12 + J22 * J22);
qd[1] = - coeff * w * (J11 * J12 + J21 * J22);
qd[2] = coeff * w * (J11 * J11 + J21 * J21);
for (CeedInt c = 0; c < 2; c++)
{
const CeedScalar ug0 = ug[i + Q * (c+2*0)];
const CeedScalar ug1 = ug[i + Q * (c+2*1)];
vg[i + Q * (c+2*0)] = qd[0] * ug0 + qd[1] * ug1;
vg[i + Q * (c+2*1)] = qd[1] * ug0 + qd[2] * ug1;
}
}
break;
case 31:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 3 6 qd: 0 1 2
// 1 4 7 1 3 4
// 2 5 8 2 4 5
const CeedScalar J11 = J[i + Q * 0];
const CeedScalar J21 = J[i + Q * 1];
const CeedScalar J31 = J[i + Q * 2];
const CeedScalar J12 = J[i + Q * 3];
const CeedScalar J22 = J[i + Q * 4];
const CeedScalar J32 = J[i + Q * 5];
const CeedScalar J13 = J[i + Q * 6];
const CeedScalar J23 = J[i + Q * 7];
const CeedScalar J33 = J[i + Q * 8];
const CeedScalar A11 = J22 * J33 - J23 * J32;
const CeedScalar A12 = J13 * J32 - J12 * J33;
const CeedScalar A13 = J12 * J23 - J13 * J22;
const CeedScalar A21 = J23 * J31 - J21 * J33;
const CeedScalar A22 = J11 * J33 - J13 * J31;
const CeedScalar A23 = J13 * J21 - J11 * J23;
const CeedScalar A31 = J21 * J32 - J22 * J31;
const CeedScalar A32 = J12 * J31 - J11 * J32;
const CeedScalar A33 = J11 * J22 - J12 * J21;
const CeedScalar w = qw[i] / (J11 * A11 + J21 * A12 + J31 * A13);
CeedScalar qd[6];
qd[0] = coeff * w * (A11 * A11 + A12 * A12 + A13 * A13);
qd[1] = coeff * w * (A11 * A21 + A12 * A22 + A13 * A23);
qd[2] = coeff * w * (A11 * A31 + A12 * A32 + A13 * A33);
qd[3] = coeff * w * (A21 * A21 + A22 * A22 + A23 * A23);
qd[4] = coeff * w * (A21 * A31 + A22 * A32 + A23 * A33);
qd[5] = coeff * w * (A31 * A31 + A32 * A32 + A33 * A33);
const CeedScalar ug0 = ug[i + Q * 0];
const CeedScalar ug1 = ug[i + Q * 1];
const CeedScalar ug2 = ug[i + Q * 2];
vg[i + Q * 0] = qd[0] * ug0 + qd[1] * ug1 + qd[2] * ug2;
vg[i + Q * 1] = qd[1] * ug0 + qd[3] * ug1 + qd[4] * ug2;
vg[i + Q * 2] = qd[2] * ug0 + qd[4] * ug1 + qd[5] * ug2;
}
break;
case 33:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 3 6 qd: 0 1 2
// 1 4 7 1 3 4
// 2 5 8 2 4 5
const CeedScalar J11 = J[i + Q * 0];
const CeedScalar J21 = J[i + Q * 1];
const CeedScalar J31 = J[i + Q * 2];
const CeedScalar J12 = J[i + Q * 3];
const CeedScalar J22 = J[i + Q * 4];
const CeedScalar J32 = J[i + Q * 5];
const CeedScalar J13 = J[i + Q * 6];
const CeedScalar J23 = J[i + Q * 7];
const CeedScalar J33 = J[i + Q * 8];
const CeedScalar A11 = J22 * J33 - J23 * J32;
const CeedScalar A12 = J13 * J32 - J12 * J33;
const CeedScalar A13 = J12 * J23 - J13 * J22;
const CeedScalar A21 = J23 * J31 - J21 * J33;
const CeedScalar A22 = J11 * J33 - J13 * J31;
const CeedScalar A23 = J13 * J21 - J11 * J23;
const CeedScalar A31 = J21 * J32 - J22 * J31;
const CeedScalar A32 = J12 * J31 - J11 * J32;
const CeedScalar A33 = J11 * J22 - J12 * J21;
const CeedScalar w = qw[i] / (J11 * A11 + J21 * A12 + J31 * A13);
CeedScalar qd[6];
qd[0] = coeff * w * (A11 * A11 + A12 * A12 + A13 * A13);
qd[1] = coeff * w * (A11 * A21 + A12 * A22 + A13 * A23);
qd[2] = coeff * w * (A11 * A31 + A12 * A32 + A13 * A33);
qd[3] = coeff * w * (A21 * A21 + A22 * A22 + A23 * A23);
qd[4] = coeff * w * (A21 * A31 + A22 * A32 + A23 * A33);
qd[5] = coeff * w * (A31 * A31 + A32 * A32 + A33 * A33);
for (CeedInt c = 0; c < 3; c++)
{
const CeedScalar ug0 = ug[i + Q * (c+3*0)];
const CeedScalar ug1 = ug[i + Q * (c+3*1)];
const CeedScalar ug2 = ug[i + Q * (c+3*2)];
vg[i + Q * (c+3*0)] = qd[0] * ug0 + qd[1] * ug1 + qd[2] * ug2;
vg[i + Q * (c+3*1)] = qd[1] * ug0 + qd[3] * ug1 + qd[4] * ug2;
vg[i + Q * (c+3*2)] = qd[2] * ug0 + qd[4] * ug1 + qd[5] * ug2;
}
}
break;
}
return 0;
}
CEED_QFUNCTION(f_apply_diff_mf_quad)(void *ctx, CeedInt Q,
const CeedScalar *const *in,
CeedScalar *const *out)
{
BuildContext *bc = (BuildContext*)ctx;
// in[0], out[0] have shape [dim, nc=1, Q]
// in[1] is Jacobians with shape [dim, nc=dim, Q]
// in[2] is quadrature weights, size (Q)
//
// At every quadrature point, compute qw/det(J).adj(J).adj(J)^T
const CeedScalar *c = in[0], *ug = in[1], *J = in[2], *qw = in[3];
CeedScalar *vg = out[0];
switch (10 * bc->dim + bc->vdim)
{
case 11:
for (CeedInt i = 0; i < Q; i++)
{
const CeedScalar qd = c[i] * qw[i] / J[i];
vg[i] = ug[i] * qd;
}
break;
case 21:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 2 qd: 0 1 adj(J): J22 -J12
// 1 3 1 2 -J21 J11
const CeedScalar J11 = J[i + Q * 0];
const CeedScalar J21 = J[i + Q * 1];
const CeedScalar J12 = J[i + Q * 2];
const CeedScalar J22 = J[i + Q * 3];
const CeedScalar w = qw[i] / (J11 * J22 - J21 * J12);
CeedScalar qd[3];
const CeedScalar coeff = c[i];
qd[0] = coeff * w * (J12 * J12 + J22 * J22);
qd[1] = - coeff * w * (J11 * J12 + J21 * J22);
qd[2] = coeff * w * (J11 * J11 + J21 * J21);
const CeedScalar ug0 = ug[i + Q * 0];
const CeedScalar ug1 = ug[i + Q * 1];
vg[i + Q * 0] = qd[0] * ug0 + qd[1] * ug1;
vg[i + Q * 1] = qd[1] * ug0 + qd[2] * ug1;
}
break;
case 22:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 2 qd: 0 1 adj(J): J22 -J12
// 1 3 1 2 -J21 J11
const CeedScalar J11 = J[i + Q * 0];
const CeedScalar J21 = J[i + Q * 1];
const CeedScalar J12 = J[i + Q * 2];
const CeedScalar J22 = J[i + Q * 3];
const CeedScalar w = qw[i] / (J11 * J22 - J21 * J12);
CeedScalar qd[3];
const CeedScalar coeff = c[i];
qd[0] = coeff * w * (J12 * J12 + J22 * J22);
qd[1] = - coeff * w * (J11 * J12 + J21 * J22);
qd[2] = coeff * w * (J11 * J11 + J21 * J21);
for (CeedInt c = 0; c < 2; c++)
{
const CeedScalar ug0 = ug[i + Q * (c+2*0)];
const CeedScalar ug1 = ug[i + Q * (c+2*1)];
vg[i + Q * (c+2*0)] = qd[0] * ug0 + qd[1] * ug1;
vg[i + Q * (c+2*1)] = qd[1] * ug0 + qd[2] * ug1;
}
}
break;
case 31:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 3 6 qd: 0 1 2
// 1 4 7 1 3 4
// 2 5 8 2 4 5
const CeedScalar J11 = J[i + Q * 0];
const CeedScalar J21 = J[i + Q * 1];
const CeedScalar J31 = J[i + Q * 2];
const CeedScalar J12 = J[i + Q * 3];
const CeedScalar J22 = J[i + Q * 4];
const CeedScalar J32 = J[i + Q * 5];
const CeedScalar J13 = J[i + Q * 6];
const CeedScalar J23 = J[i + Q * 7];
const CeedScalar J33 = J[i + Q * 8];
const CeedScalar A11 = J22 * J33 - J23 * J32;
const CeedScalar A12 = J13 * J32 - J12 * J33;
const CeedScalar A13 = J12 * J23 - J13 * J22;
const CeedScalar A21 = J23 * J31 - J21 * J33;
const CeedScalar A22 = J11 * J33 - J13 * J31;
const CeedScalar A23 = J13 * J21 - J11 * J23;
const CeedScalar A31 = J21 * J32 - J22 * J31;
const CeedScalar A32 = J12 * J31 - J11 * J32;
const CeedScalar A33 = J11 * J22 - J12 * J21;
const CeedScalar w = qw[i] / (J11 * A11 + J21 * A12 + J31 * A13);
CeedScalar qd[6];
const CeedScalar coeff = c[i];
qd[0] = coeff * w * (A11 * A11 + A12 * A12 + A13 * A13);
qd[1] = coeff * w * (A11 * A21 + A12 * A22 + A13 * A23);
qd[2] = coeff * w * (A11 * A31 + A12 * A32 + A13 * A33);
qd[3] = coeff * w * (A21 * A21 + A22 * A22 + A23 * A23);
qd[4] = coeff * w * (A21 * A31 + A22 * A32 + A23 * A33);
qd[5] = coeff * w * (A31 * A31 + A32 * A32 + A33 * A33);
const CeedScalar ug0 = ug[i + Q * 0];
const CeedScalar ug1 = ug[i + Q * 1];
const CeedScalar ug2 = ug[i + Q * 2];
vg[i + Q * 0] = qd[0] * ug0 + qd[1] * ug1 + qd[2] * ug2;
vg[i + Q * 1] = qd[1] * ug0 + qd[3] * ug1 + qd[4] * ug2;
vg[i + Q * 2] = qd[2] * ug0 + qd[4] * ug1 + qd[5] * ug2;
}
break;
case 33:
for (CeedInt i = 0; i < Q; i++)
{
// J: 0 3 6 qd: 0 1 2
// 1 4 7 1 3 4
// 2 5 8 2 4 5
const CeedScalar J11 = J[i + Q * 0];
const CeedScalar J21 = J[i + Q * 1];
const CeedScalar J31 = J[i + Q * 2];
const CeedScalar J12 = J[i + Q * 3];
const CeedScalar J22 = J[i + Q * 4];
const CeedScalar J32 = J[i + Q * 5];
const CeedScalar J13 = J[i + Q * 6];
const CeedScalar J23 = J[i + Q * 7];
const CeedScalar J33 = J[i + Q * 8];
const CeedScalar A11 = J22 * J33 - J23 * J32;
const CeedScalar A12 = J13 * J32 - J12 * J33;
const CeedScalar A13 = J12 * J23 - J13 * J22;
const CeedScalar A21 = J23 * J31 - J21 * J33;
const CeedScalar A22 = J11 * J33 - J13 * J31;
const CeedScalar A23 = J13 * J21 - J11 * J23;
const CeedScalar A31 = J21 * J32 - J22 * J31;
const CeedScalar A32 = J12 * J31 - J11 * J32;
const CeedScalar A33 = J11 * J22 - J12 * J21;
const CeedScalar w = qw[i] / (J11 * A11 + J21 * A12 + J31 * A13);
CeedScalar qd[6];
const CeedScalar coeff = c[i];
qd[0] = coeff * w * (A11 * A11 + A12 * A12 + A13 * A13);
qd[1] = coeff * w * (A11 * A21 + A12 * A22 + A13 * A23);
qd[2] = coeff * w * (A11 * A31 + A12 * A32 + A13 * A33);
qd[3] = coeff * w * (A21 * A21 + A22 * A22 + A23 * A23);
qd[4] = coeff * w * (A21 * A31 + A22 * A32 + A23 * A33);
qd[5] = coeff * w * (A31 * A31 + A32 * A32 + A33 * A33);
for (CeedInt c = 0; c < 3; c++)
{
const CeedScalar ug0 = ug[i + Q * (c+3*0)];
const CeedScalar ug1 = ug[i + Q * (c+3*1)];
const CeedScalar ug2 = ug[i + Q * (c+3*2)];
vg[i + Q * (c+3*0)] = qd[0] * ug0 + qd[1] * ug1 + qd[2] * ug2;
vg[i + Q * (c+3*1)] = qd[1] * ug0 + qd[3] * ug1 + qd[4] * ug2;
vg[i + Q * (c+3*2)] = qd[2] * ug0 + qd[4] * ug1 + qd[5] * ug2;
}
}
break;
}
return 0;
}
+3 -4
View File
@@ -14,6 +14,7 @@
#include "ceed.hpp"
#ifdef MFEM_USE_CEED
#include "../fespace.hpp"
namespace mfem
@@ -24,10 +25,8 @@ void CeedPADiffusionAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &ir,
CeedData& ceedData);
void CeedMFDiffusionAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &ir,
CeedData& ceedData);
}
#endif // MFEM_USE_CEED
#endif // MFEM_LIBCEED_DIFF_HPP
+3 -28
View File
@@ -11,10 +11,9 @@
#include "mass.hpp"
#include "ceed.hpp"
#ifdef MFEM_USE_CEED
#include "ceed.hpp"
#include "mass.h"
#endif
namespace mfem
{
@@ -22,41 +21,17 @@ namespace mfem
void CeedPAMassAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &irm, CeedData& ceedData)
{
#ifdef MFEM_USE_CEED
Mesh &mesh = *fes.GetMesh();
// Perform checks for some assumptions made in the Q-functions.
MFEM_VERIFY(mesh.Dimension() == mesh.SpaceDimension(), "case not supported");
MFEM_VERIFY(1 <= fes.GetVDim() && fes.GetVDim() <= 3, "case not supported");
CeedPAOperator massOp = {fes, irm,
1, "/mass.h",
":f_build_mass_const", f_build_mass_const,
":f_build_mass_quad", f_build_mass_quad,
":f_build_mass_grid", f_build_mass_grid,
":f_apply_mass", f_apply_mass,
CEED_EVAL_INTERP,
CEED_EVAL_INTERP
};
CeedPAAssemble(massOp, ceedData);
#else
mfem_error("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
#endif
}
void CeedMFMassAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &irm, CeedData& ceedData)
{
#ifdef MFEM_USE_CEED
CeedMFOperator massOp = {fes, irm,
"/mass.h",
":f_apply_mass_mf_const", f_apply_mass_mf_const,
":f_apply_mass_mf_quad", f_apply_mass_mf_quad,
CEED_EVAL_INTERP,
CEED_EVAL_INTERP
};
CeedMFAssemble(massOp, ceedData);
#else
mfem_error("MFEM must be built with MFEM_USE_CEED=YES to use libCEED.");
#endif
}
} // namespace mfem
#endif // MFEM_USE_CEED
+5 -162
View File
@@ -10,7 +10,7 @@
// CONTRIBUTING.md for details.
/// A structure used to pass additional data to f_build_diff and f_apply_diff
struct BuildContext { CeedInt dim, space_dim, vdim; CeedScalar coeff; };
struct BuildContext { CeedInt dim, space_dim; CeedScalar coeff; };
/// libCEED Q-function for building quadrature data for a mass operator with a
/// constant coefficient
@@ -56,8 +56,8 @@ CEED_QFUNCTION(f_build_mass_const)(void *ctx, CeedInt Q,
}
/// libCEED Q-function for building quadrature data for a mass operator with a
/// coefficient evaluated at quadrature points.
CEED_QFUNCTION(f_build_mass_quad)(void *ctx, CeedInt Q,
/// grid function coefficient
CEED_QFUNCTION(f_build_mass_grid)(void *ctx, CeedInt Q,
const CeedScalar *const *in,
CeedScalar *const *out)
{
@@ -102,168 +102,11 @@ CEED_QFUNCTION(f_apply_mass)(void *ctx, CeedInt Q,
const CeedScalar *const *in,
CeedScalar *const *out)
{
BuildContext *bc = (BuildContext *)ctx;
const CeedScalar *u = in[0], *w = in[1];
CeedScalar *v = out[0];
switch (bc->vdim)
for (CeedInt i=0; i<Q; i++)
{
case 1:
for (CeedInt i=0; i<Q; i++)
{
v[i] = w[i] * u[i];
}
break;
case 2:
for (CeedInt i=0; i<Q; i++)
{
const CeedScalar W = w[i];
for (CeedInt c = 0; c < 2; c++)
{
v[i+c*Q] = W * u[i+c*Q];
}
}
break;
case 3:
for (CeedInt i=0; i<Q; i++)
{
const CeedScalar W = w[i];
for (CeedInt c = 0; c < 3; c++)
{
v[i+c*Q] = W * u[i+c*Q];
}
}
break;
}
return 0;
}
/// libCEED Q-function for applying a diff operator
CEED_QFUNCTION(f_apply_mass_mf_const)(void *ctx, CeedInt Q,
const CeedScalar *const *in, CeedScalar *const *out)
{
BuildContext *bc = (BuildContext*)ctx;
const CeedScalar coeff = bc->coeff;
const CeedScalar *u = in[0], *J = in[1], *qw = in[2];
CeedScalar *v = out[0];
switch (10 * bc->dim + bc->vdim)
{
case 11:
for (CeedInt i = 0; i < Q; i++)
{
const CeedScalar rho = coeff * qw[i] / J[i];
v[i] = rho * u[i];
}
break;
case 21:
for (CeedInt i = 0; i < Q; i++)
{
const CeedScalar rho = coeff * (J[i+Q*0]*J[i+Q*3] - J[i+Q*1]*J[i+Q*2]) * qw[i];
v[i] = rho * u[i];
}
break;
case 22:
for (CeedInt i=0; i<Q; i++)
{
// 0 2
// 1 3
const CeedScalar rho = coeff * (J[i+Q*0]*J[i+Q*3] - J[i+Q*1]*J[i+Q*2]) * qw[i];
for (CeedInt c = 0; c < 2; c++)
{
v[i+c*Q] = rho * u[i+c*Q];
}
}
break;
case 31:
for (CeedInt i = 0; i < Q; i++)
{
const CeedScalar rho = (J[i+Q*0]*(J[i+Q*4]*J[i+Q*8] - J[i+Q*5]*J[i+Q*7]) -
J[i+Q*1]*(J[i+Q*3]*J[i+Q*8] - J[i+Q*5]*J[i+Q*6]) +
J[i+Q*2]*(J[i+Q*3]*J[i+Q*7] - J[i+Q*4]*J[i+Q*6])) * coeff * qw[i];
v[i] = rho * u[i];
}
break;
case 33:
for (CeedInt i=0; i<Q; i++)
{
// 0 3 6
// 1 4 7
// 2 5 8
const CeedScalar rho = (J[i+Q*0]*(J[i+Q*4]*J[i+Q*8] - J[i+Q*5]*J[i+Q*7]) -
J[i+Q*1]*(J[i+Q*3]*J[i+Q*8] - J[i+Q*5]*J[i+Q*6]) +
J[i+Q*2]*(J[i+Q*3]*J[i+Q*7] - J[i+Q*4]*J[i+Q*6])) * coeff * qw[i];
for (CeedInt c = 0; c < 3; c++)
{
v[i+c*Q] = rho * u[i+c*Q];
}
}
break;
}
return 0;
}
CEED_QFUNCTION(f_apply_mass_mf_quad)(void *ctx, CeedInt Q,
const CeedScalar *const *in, CeedScalar *const *out)
{
BuildContext *bc = (BuildContext*)ctx;
const CeedScalar *c = in[0], *u = in[1], *J = in[2], *qw = in[3];
CeedScalar *v = out[0];
switch (10 * bc->dim + bc->vdim)
{
case 11:
for (CeedInt i=0; i<Q; i++)
{
const CeedScalar rho = c[i] * J[i] * qw[i];
v[i] = rho * u[i];
}
break;
case 21:
for (CeedInt i=0; i<Q; i++)
{
// 0 2
// 1 3
const CeedScalar rho = c[i] * (J[i+Q*0]*J[i+Q*3] - J[i+Q*1]*J[i+Q*2]) * qw[i];
v[i] = rho * u[i];
}
break;
case 22:
for (CeedInt i=0; i<Q; i++)
{
// 0 2
// 1 3
const CeedScalar rho = c[i] * (J[i+Q*0]*J[i+Q*3] - J[i+Q*1]*J[i+Q*2]) * qw[i];
for (CeedInt c = 0; c < 2; c++)
{
v[i+c*Q] = rho * u[i+c*Q];
}
}
break;
case 31:
for (CeedInt i=0; i<Q; i++)
{
// 0 3 6
// 1 4 7
// 2 5 8
const CeedScalar rho = (J[i+Q*0]*(J[i+Q*4]*J[i+Q*8] - J[i+Q*5]*J[i+Q*7]) -
J[i+Q*1]*(J[i+Q*3]*J[i+Q*8] - J[i+Q*5]*J[i+Q*6]) +
J[i+Q*2]*(J[i+Q*3]*J[i+Q*7] - J[i+Q*4]*J[i+Q*6])) * c[i] * qw[i];
v[i] = rho * u[i];
}
break;
case 33:
for (CeedInt i=0; i<Q; i++)
{
// 0 3 6
// 1 4 7
// 2 5 8
const CeedScalar rho = (J[i+Q*0]*(J[i+Q*4]*J[i+Q*8] - J[i+Q*5]*J[i+Q*7]) -
J[i+Q*1]*(J[i+Q*3]*J[i+Q*8] - J[i+Q*5]*J[i+Q*6]) +
J[i+Q*2]*(J[i+Q*3]*J[i+Q*7] - J[i+Q*4]*J[i+Q*6])) * c[i] * qw[i];
for (CeedInt c = 0; c < 3; c++)
{
v[i+c*Q] = rho * u[i+c*Q];
}
}
break;
v[i] = w[i] * u[i];
}
return 0;
}
+3 -4
View File
@@ -14,6 +14,7 @@
#include "ceed.hpp"
#ifdef MFEM_USE_CEED
#include "../fespace.hpp"
namespace mfem
@@ -24,10 +25,8 @@ void CeedPAMassAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &ir,
CeedData& ceedData);
void CeedMFMassAssemble(const FiniteElementSpace &fes,
const mfem::IntegrationRule &irm,
CeedData& ceedData);
}
#endif // MFEM_USE_CEED
#endif // MFEM_LIBCEED_MASS_HPP
+76 -127
View File
@@ -131,8 +131,8 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
const Vector &px = Prolongate(x);
if (P) { aux2.SetSize(P->Height()); }
// If we are in parallel, ParNonLinearForm::Mult uses the aux2 vector. In
// serial, place the result directly in y.
// If we are in parallel, ParNonLinearForm::Mult uses the aux2 vector.
// In serial, place the result directly in y.
Vector &py = P ? aux2 : y;
if (ext)
@@ -456,12 +456,11 @@ void BlockNonlinearForm::SetSpaces(Array<FiniteElementSpace *> &f)
for (int j=0; j<Grads.NumCols(); ++j)
{
delete Grads(i,j);
delete cGrads(i,j);
}
}
for (int i = 0; i < ess_tdofs.Size(); ++i)
for (int i = 0; i < ess_vdofs.Size(); ++i)
{
delete ess_tdofs[i];
delete ess_vdofs[i];
}
height = 0;
@@ -487,33 +486,10 @@ void BlockNonlinearForm::SetSpaces(Array<FiniteElementSpace *> &f)
Grads.SetSize(fes.Size(), fes.Size());
Grads = NULL;
cGrads.SetSize(fes.Size(), fes.Size());
cGrads = NULL;
P.SetSize(fes.Size());
cP.SetSize(fes.Size());
ess_tdofs.SetSize(fes.Size());
ess_vdofs.SetSize(fes.Size());
for (int s = 0; s < fes.Size(); ++s)
{
// Retrieve prolongation matrix for each FE space
P[s] = fes[s]->GetProlongationMatrix();
cP[s] = dynamic_cast<const SparseMatrix *>(P[s]);
// If the P Operator exists and its type is not SparseMatrix, this
// indicates the Operator is part of parallel run.
if (P[s] && !cP[s])
{
is_serial = false;
}
// If the P Operator exists and its type is SparseMatrix, this indicates
// the Operator is serial but needs prolongation on assembly.
if (cP[s])
{
needs_prolongation = true;
}
ess_tdofs[s] = new Array<int>;
ess_vdofs[s] = new Array<int>;
}
}
@@ -530,18 +506,45 @@ void BlockNonlinearForm::AddBdrFaceIntegrator(BlockNonlinearFormIntegrator *nfi,
bfnfi_marker.Append(&bdr_attr_marker);
}
void BlockNonlinearForm::SetEssentialBC(
const Array<Array<int> *> &bdr_attr_is_ess, Array<Vector *> &rhs)
void BlockNonlinearForm::SetEssentialBC(const
Array<Array<int> *>&bdr_attr_is_ess,
Array<Vector *> &rhs)
{
for (int s = 0; s < fes.Size(); ++s)
{
ess_tdofs[s]->SetSize(ess_tdofs.Size());
int i, j, vsize, nv;
fes[s]->GetEssentialTrueDofs(*bdr_attr_is_ess[s], *ess_tdofs[s]);
for (int s=0; s<fes.Size(); ++s)
{
// First, set u variables
vsize = fes[s]->GetVSize();
Array<int> vdof_marker(vsize);
// virtual call, works in parallel too
fes[s]->GetEssentialVDofs(*(bdr_attr_is_ess[s]), vdof_marker);
nv = 0;
for (i = 0; i < vsize; ++i)
{
if (vdof_marker[i])
{
nv++;
}
}
ess_vdofs[s]->SetSize(nv);
for (i = j = 0; i < vsize; ++i)
{
if (vdof_marker[i])
{
(*ess_vdofs[s])[j++] = i;
}
}
if (rhs[s])
{
rhs[s]->SetSubVector(*ess_tdofs[s], 0.0);
for (i = 0; i < nv; ++i)
{
(*rhs[s])[(*ess_vdofs[s])[i]] = 0.0;
}
}
}
}
@@ -754,52 +757,18 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
delete vdofs[s];
delete el_y[s];
delete el_x[s];
by.GetBlock(s).SetSubVector(*ess_vdofs[s], 0.0);
}
}
const BlockVector &BlockNonlinearForm::Prolongate(const BlockVector &bx) const
{
MFEM_VERIFY(bx.Size() == Width(), "invalid input BlockVector size");
if (needs_prolongation)
{
aux1.Update(block_offsets);
for (int s = 0; s < fes.Size(); s++)
{
P[s]->Mult(bx.GetBlock(s), aux1.GetBlock(s));
}
return aux1;
}
return bx;
}
void BlockNonlinearForm::Mult(const Vector &x, Vector &y) const
{
BlockVector bx(x.GetData(), block_trueOffsets);
BlockVector by(y.GetData(), block_trueOffsets);
const BlockVector &pbx = Prolongate(bx);
if (needs_prolongation)
{
aux2.Update(block_offsets);
}
BlockVector &pby = needs_prolongation ? aux2 : by;
xs.Update(pbx.GetData(), block_offsets);
ys.Update(pby.GetData(), block_offsets);
xs.Update(x.GetData(), block_offsets);
ys.Update(y.GetData(), block_offsets);
MultBlocked(xs, ys);
for (int s = 0; s < fes.Size(); s++)
{
if (cP[s])
{
cP[s]->MultTranspose(pby.GetBlock(s), by.GetBlock(s));
}
by.GetBlock(s).SetSubVector(*ess_tdofs[s], 0.0);
}
}
void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
Operator &BlockNonlinearForm::GetGradientBlocked(const BlockVector &bx) const
{
const int skip_zeros = 0;
Array<Array<int> *> vdofs(fes.Size());
@@ -811,6 +780,13 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
Array<const FiniteElement *>fe2(fes.Size());
ElementTransformation * T;
if (BlockGrad != NULL)
{
delete BlockGrad;
}
BlockGrad = new BlockOperator(block_offsets);
for (int i=0; i<fes.Size(); ++i)
{
el_x_const[i] = el_x[i] = new Vector();
@@ -975,70 +951,44 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
}
}
for (int s=0; s<fes.Size(); ++s)
{
for (int i = 0; i < ess_vdofs[s]->Size(); ++i)
{
for (int j=0; j<fes.Size(); ++j)
{
if (s==j)
{
Grads(s,s)->EliminateRowCol((*ess_vdofs[s])[i], Matrix::DIAG_ONE);
}
else
{
Grads(s,j)->EliminateRow((*ess_vdofs[s])[i]);
Grads(j,s)->EliminateCol((*ess_vdofs[s])[i]);
}
}
}
}
for (int i=0; i<fes.Size(); ++i)
{
for (int j=0; j<fes.Size(); ++j)
{
BlockGrad->SetBlock(i,j,Grads(i,j));
delete elmats(i,j);
}
delete vdofs2[i];
delete vdofs[i];
delete el_x[i];
}
return *BlockGrad;
}
Operator &BlockNonlinearForm::GetGradient(const Vector &x) const
{
BlockVector bx(x.GetData(), block_trueOffsets);
const BlockVector &pbx = Prolongate(bx);
ComputeGradientBlocked(pbx);
Array2D<SparseMatrix *> mGrads(fes.Size(), fes.Size());
mGrads = Grads;
if (needs_prolongation)
{
for (int s1 = 0; s1 < fes.Size(); ++s1)
{
for (int s2 = 0; s2 < fes.Size(); ++s2)
{
delete cGrads(s1, s2);
cGrads(s1, s2) = RAP(*cP[s1], *Grads(s1, s2), *cP[s2]);
mGrads(s1, s2) = cGrads(s1, s2);
}
}
}
for (int s = 0; s < fes.Size(); ++s)
{
for (int i = 0; i < ess_tdofs[s]->Size(); ++i)
{
for (int j = 0; j < fes.Size(); ++j)
{
if (s == j)
{
mGrads(s, s)->EliminateRowCol((*ess_tdofs[s])[i],
Matrix::DIAG_ONE);
}
else
{
mGrads(s, j)->EliminateRow((*ess_tdofs[s])[i]);
mGrads(j, s)->EliminateCol((*ess_tdofs[s])[i]);
}
}
}
}
delete BlockGrad;
BlockGrad = new BlockOperator(block_trueOffsets);
for (int i = 0; i < fes.Size(); ++i)
{
for (int j = 0; j < fes.Size(); ++j)
{
BlockGrad->SetBlock(i, j, mGrads(i, j));
}
}
return *BlockGrad;
xs.Update(x.GetData(), block_offsets);
return GetGradientBlocked(xs);
}
BlockNonlinearForm::~BlockNonlinearForm()
@@ -1049,9 +999,8 @@ BlockNonlinearForm::~BlockNonlinearForm()
for (int j=0; j<fes.Size(); ++j)
{
delete Grads(i,j);
delete cGrads(i,j);
}
delete ess_tdofs[i];
delete ess_vdofs[i];
}
for (int i = 0; i < dnfi.Size(); ++i)
+4 -25
View File
@@ -199,41 +199,24 @@ protected:
GridFunction-like block-vector data (e.g. in parallel). */
mutable BlockVector xs, ys;
mutable Array2D<SparseMatrix*> Grads, cGrads;
mutable Array2D<SparseMatrix*> Grads;
mutable BlockOperator *BlockGrad;
// A list of the offsets
Array<int> block_offsets;
Array<int> block_trueOffsets;
// Array of Arrays of tdofs for each space in 'fes'
Array<Array<int> *> ess_tdofs;
/// Array of pointers to the prolongation matrix of fes, may be NULL
Array<const Operator *> P;
/// Array of results of dynamic-casting P to SparseMatrix pointer
Array<const SparseMatrix *> cP;
/// Indicator if the Operator is part of a parallel run
bool is_serial = true;
/// Indicator if the Operator needs prolongation on assembly
bool needs_prolongation = false;
mutable BlockVector aux1, aux2;
const BlockVector &Prolongate(const BlockVector &bx) const;
// Essential vdofs: one list of vdofs for each space in 'fes'
Array<Array<int> *> ess_vdofs;
/// Specialized version of GetEnergy() for BlockVectors
double GetEnergyBlocked(const BlockVector &bx) const;
/// Specialized version of Mult() for BlockVector%s
/// Block L-Vector to Block L-Vector
void MultBlocked(const BlockVector &bx, BlockVector &by) const;
/// Specialized version of GetGradient() for BlockVector
void ComputeGradientBlocked(const BlockVector &bx) const;
Operator &GetGradientBlocked(const BlockVector &bx) const;
public:
/// Construct an empty BlockNonlinearForm. Initialize with SetSpaces().
@@ -278,12 +261,8 @@ public:
virtual double GetEnergy(const Vector &x) const;
/// Method is only called in serial, the parallel version calls MultBlocked
/// directly.
virtual void Mult(const Vector &x, Vector &y) const;
/// Method is only called in serial, the parallel version calls
/// GetGradientBlocked directly.
virtual Operator &GetGradient(const Vector &x) const;
/// Destructor.
+47
View File
@@ -565,6 +565,53 @@ HypreParMatrix* ParDiscreteLinearOperator::ParallelAssemble() const
return RAP;
}
void ParDiscreteLinearOperator::ParallelAssemble(OperatorHandle &A)
{
// construct the rectangular block-diagonal matrix dA
OperatorHandle dA(A.Type());
dA.MakeRectangularBlockDiag(domain_fes->GetComm(),
range_fes->GlobalVSize(),
domain_fes->GlobalVSize(),
range_fes->GetDofOffsets(),
domain_fes->GetDofOffsets(),
mat);
OperatorHandle P_test(A.Type()), P_trial(A.Type());
// TODO - construct the Dof_TrueDof_Matrix directly in the required format.
P_test.ConvertFrom(range_fes->Dof_TrueDof_Matrix());
P_trial.ConvertFrom(domain_fes->Dof_TrueDof_Matrix());
A.MakeRAP(P_test, dA, P_trial);
}
//// @todo copied from ParMixedBilinearForm, should be some inheritance?
void ParDiscreteLinearOperator::FormRectangularSystemMatrix(OperatorHandle &A)
{
if (ext)
{
Array<int> empty;
ext->FormRectangularSystemOperator(empty, empty, A);
return;
}
/*
if (mat)
{
Finalize();
ParallelAssemble(p_mat);
delete mat;
mat = NULL;
delete mat_e;
mat_e = NULL;
p_mat_e = NULL;
}
A = p_mat;
*/
mfem_error("not implemented!");
}
void ParDiscreteLinearOperator::GetParBlocks(Array2D<HypreParMatrix *> &blocks)
const
{
+12 -1
View File
@@ -76,7 +76,7 @@ public:
void KeepNbrBlock(bool knb = true) { keep_nbr_block = knb; }
/** @brief Set the operator type id for the parallel matrix/operator when
using AssemblyLevel::LEGACYFULL. */
using AssemblyLevel::FULL. */
/** If using static condensation or hybridization, call this method *after*
enabling it. */
void SetOperatorType(Operator::Type tid)
@@ -160,6 +160,9 @@ public:
/// Get the parallel finite element space prolongation matrix
virtual const Operator *GetProlongation() const
{ return pfes->GetProlongationMatrix(); }
/// Get the diagonal part of the parallel finite element space prolongation matrix
virtual const Operator *GetLocalProlongation() const
{ return pfes->GetLocalProlongationMatrix(); }
/// Get the parallel finite element space restriction matrix
virtual const Operator *GetRestriction() const
{ return pfes->GetRestrictionMatrix(); }
@@ -301,10 +304,18 @@ public:
/// Returns the matrix "assembled" on the true dofs
HypreParMatrix *ParallelAssemble() const;
/** @brief Returns the matrix assembled on the true dofs, i.e.
@a A = P_test^t A_local P_trial, in the format (type id) specified by
@a A. */
void ParallelAssemble(OperatorHandle &A);
/** Extract the parallel blocks corresponding to the vector dimensions of the
domain and range parallel finite element spaces */
void GetParBlocks(Array2D<HypreParMatrix *> &blocks) const;
/** @brief Return in @a A a parallel (on truedofs) version of this operator. */
virtual void FormRectangularSystemMatrix(OperatorHandle &A);
virtual ~ParDiscreteLinearOperator() { }
};
+64 -13
View File
@@ -101,6 +101,7 @@ void ParFiniteElementSpace::ParInit(ParMesh *pm)
P = NULL;
Pconf = NULL;
Pconf_local = NULL;
R = NULL;
num_face_nbr_dofs = -1;
@@ -921,6 +922,39 @@ const Operator *ParFiniteElementSpace::GetProlongationMatrix() const
}
}
const Operator *ParFiniteElementSpace::GetLocalProlongationMatrix() const
{
if (Conforming())
{
if (Pconf_local) { return Pconf_local; }
if (NRanks == 1)
{
Pconf_local = new IdentityOperator(GetTrueVSize());
}
else
{
if (!Device::Allows(Backend::DEVICE_MASK))
{
Pconf_local = new ConformingProlongationOperator(*this, true);
}
else
{
// Pconf = new DeviceConformingProlongationOperator(*this);
mfem_error("Not implemented!");
}
}
return Pconf_local;
}
else
{
// return Dof_TrueDof_Matrix();
// just need diagonal portion, not too hard
mfem_error("Not implemented!");
return NULL;
}
}
void ParFiniteElementSpace::ExchangeFaceNbrData()
{
if (num_face_nbr_dofs >= 0) { return; }
@@ -1999,13 +2033,13 @@ int ParFiniteElementSpace
for (int entity = 0; entity <= 2; entity++)
{
const NCMesh::NCList &list = pncmesh->GetNCList(entity);
if (!list.masters.Size()) { continue; }
if (!list.masters.size()) { continue; }
IsoparametricTransformation T;
DenseMatrix I;
// process masters that we own or that affect our edges/faces
for (int mi = 0; mi < list.masters.Size(); mi++)
for (unsigned mi = 0; mi < list.masters.size(); mi++)
{
const NCMesh::Master &mf = list.masters[mi];
@@ -2036,7 +2070,7 @@ int ParFiniteElementSpace
GetEntityDofs(entity, sf.index, slave_dofs, mf.Geom());
if (!slave_dofs.Size()) { continue; }
list.OrientedPointMatrix(sf, T.GetPointMat());
sf.OrientedPointMatrix(T.GetPointMat());
fe->GetLocalInterpolation(T, I);
// make each slave DOF dependent on all master DOFs
@@ -2064,12 +2098,12 @@ int ParFiniteElementSpace
{
const NCMesh::NCList &list = pncmesh->GetNCList(entity);
int lsize[3] =
{ list.conforming.Size(), list.masters.Size(), list.slaves.Size() };
std::size_t lsize[3] =
{ list.conforming.size(), list.masters.size(), list.slaves.size() };
for (int l = 0; l < 3; l++)
{
for (int i = 0; i < lsize[l]; i++)
for (std::size_t i = 0; i < lsize[l]; i++)
{
const MeshId &id =
(l == 0) ? list.conforming[i] :
@@ -2834,6 +2868,7 @@ void ParFiniteElementSpace::Destroy()
delete P; P = NULL;
delete Pconf; Pconf = NULL;
delete Pconf_local; Pconf_local = NULL;
delete R; R = NULL;
delete gcomm; gcomm = NULL;
@@ -2959,12 +2994,12 @@ void ParFiniteElementSpace::Update(bool want_transform)
}
}
ConformingProlongationOperator::ConformingProlongationOperator(
const ParFiniteElementSpace &pfes)
const ParFiniteElementSpace &pfes, bool local_)
: Operator(pfes.GetVSize(), pfes.GetTrueVSize()),
external_ldofs(),
gc(pfes.GroupComm())
gc(pfes.GroupComm()),
local(local_)
{
MFEM_VERIFY(pfes.Conforming(), "");
const Table &group_ldof = gc.GroupLDofTable();
@@ -3013,7 +3048,14 @@ void ConformingProlongationOperator::Mult(const Vector &x, Vector &y) const
const int m = external_ldofs.Size();
const int in_layout = 2; // 2 - input is ltdofs array
gc.BcastBegin(const_cast<double*>(xdata), in_layout);
if (local)
{
y = 0.0;
}
else
{
gc.BcastBegin(const_cast<double*>(xdata), in_layout);
}
int j = 0;
for (int i = 0; i < m; i++)
@@ -3025,7 +3067,10 @@ void ConformingProlongationOperator::Mult(const Vector &x, Vector &y) const
std::copy(xdata+j-m, xdata+Width(), ydata+j);
const int out_layout = 0; // 0 - output is ldofs array
gc.BcastEnd(ydata, out_layout);
if (!local)
{
gc.BcastEnd(ydata, out_layout);
}
}
void ConformingProlongationOperator::MultTranspose(
@@ -3038,7 +3083,10 @@ void ConformingProlongationOperator::MultTranspose(
double *ydata = y.HostWrite();
const int m = external_ldofs.Size();
gc.ReduceBegin(xdata);
if (!local)
{
gc.ReduceBegin(xdata);
}
int j = 0;
for (int i = 0; i < m; i++)
@@ -3050,7 +3098,10 @@ void ConformingProlongationOperator::MultTranspose(
std::copy(xdata+j, xdata+Height(), ydata+j-m);
const int out_layout = 2; // 2 - output is an array on all ltdofs
gc.ReduceEnd<double>(ydata, out_layout, GroupCommunicator::Sum);
if (!local)
{
gc.ReduceEnd<double>(ydata, out_layout, GroupCommunicator::Sum);
}
}
DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
+7 -1
View File
@@ -72,6 +72,9 @@ private:
mutable HypreParMatrix *P;
/// Optimized action-only prolongation operator for conforming meshes. Owned.
mutable Operator *Pconf;
/// Optimized action-only diagonal prolongation operator on ldofs for
/// conforming meshes. Owned.
mutable Operator *Pconf_local;
/// The (block-diagonal) matrix R (restriction of dof to true dof). Owned.
mutable SparseMatrix *R;
@@ -335,6 +338,7 @@ public:
HYPRE_Int GetMyTDofOffset() const;
virtual const Operator *GetProlongationMatrix() const;
virtual const Operator *GetLocalProlongationMatrix() const;
/// Get the R matrix which restricts a local dof vector to true dof vector.
virtual const SparseMatrix *GetRestrictionMatrix() const
{ Dof_TrueDof_Matrix(); return R; }
@@ -389,9 +393,11 @@ class ConformingProlongationOperator : public Operator
protected:
Array<int> external_ldofs;
const GroupCommunicator &gc;
bool local;
public:
ConformingProlongationOperator(const ParFiniteElementSpace &pfes);
ConformingProlongationOperator(const ParFiniteElementSpace &pfes,
bool local_=false);
virtual void Mult(const Vector &x, Vector &y) const;
-3
View File
@@ -1093,9 +1093,6 @@ double L2ZZErrorEstimator(BilinearFormIntegrator &flux_integrator,
if (xfes->GetNE())
{
MFEM_VERIFY(smooth_flux_fes.GetFE(0) != NULL,
"Could not obtain FE of smooth flux space.");
if (smooth_flux_fes.GetFE(0)->GetRangeType() == FiniteElement::SCALAR)
{
VectorMassIntegrator *vmass = new VectorMassIntegrator;

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