Compare commits

...
Author SHA1 Message Date
blaz cd6eca6ada modifications 2021-02-24 13:54:33 -08:00
blaz e823646017 Merge remote-tracking branch 'origin/master' into PrmBlockNonlinearForm 2021-01-26 15:35:17 -08:00
Tzanio Kolev 7ae181ba6a Merge pull request #975 from mfem/feature/vtk_high_order
Mesh reader support for VTK high order elements [feature/vtk_high_order]
2021-01-24 12:46:47 -08:00
Tzanio 8e1c02db18 typo 2021-01-24 12:43:58 -08:00
Tzanio cbbef06624 minor 2021-01-24 12:43:17 -08:00
Tzanio Kolev 0f641e344f Merge pull request #1940 from mfem/wrapper-bform-dev
Add wrapper for FormRectangular functions [wrapper-bform-dev]
2021-01-24 12:27:49 -08:00
0d9146d364 [SQUASH-MERGE] AIR solver in Hypre (#1053)
* AIR solver

* AIR: block-diag scaling, set AIR options

* new example

* SetAIROptions

* fix bugs

* more bug fix

* initialize solution vector in ode

* added blocksize

* Added command0line flags for AIR parameters.

* implemented ParCSRMatrix add in hypre and added MFEM interface.
no assumptions on "the same col_map_offd" as in the old "Add".
Should replace Add (no longer needed) with the new one

* Added absolute tolerance to HypreGMRES.

* Updated advection driver, added options to use BoomerAMG w/o gmres.

* Missing &

* AIR options: type of dropping entries for filtering A

* boomeramg print options.

* add a different \theta for R

* minor change

* for hypre 2.14

* test

* give hypre dof's coordinates

* nodal coarsening support

* changes according to the new changes in hypre/AIR [Sabs, BdiagInv]

* Added function to get CSR for on-processor rows of hyper matrix.

* Merged Ruipeng's edits with get processor function.

* Added hypre preconditioner for on-processor diret solves of triangular matrices.

* Added function to set max levels of BoomerAMG

* Added function to set max levels of BoomerAMG

* Merging with master

* Added extra functions to set specific BoomerAMG options such as interpolation, coarsening, etc.

* Added cycle option to boomeramg.

* Added funtionality to hypre boomeramg class

* Updated to support d1.5 AIR

* Added nAIR vs. lAIR options:

* Updated advection example

* Accidentally added build folder

* Small fixes to hypre files

* Add MFEM_adv steady-state-non-periodic test cases.

Add visualization.

Change config/defaults.mk to point to hypre dir.

* Corrected typo in MFEM_adv.cpp.

* Add multiple SN directions (azimuthal) as ImplicitSolve unknowns.

* Add higher azimuthal resolution test.

* Reduced number of AMG_solvers to one (not SN).

Clean up.

* First step towards the electron transport.

Hypre-mass-matrices-sparsity works.

* Code clean and tested.

Plasma profiles and all coeficients defined by functions.

* Transport example extended to run on ALE meshes (within the hydro code).

* Add include in exTRguts example.

* Source of particles inside Mult/ImplicitSolve.

* Scalar flux evolved within the TimeIntegrator.

* Integration over velocity range + a nice fig.

* AWBS/v3 model on, decelaretion integration on.

dfMdv source needs to be checked to give ne.

* AWBS working. Number of particles test ok.

* Both explicit and implicit integrators num of particles test ok.

* Add timer.

* Add AIR parameters parsing for AWBS runs.

* Add ne_ref and test nonlocal/local runs.

* Improved timer

Better matrix in memory treatment.

* Add velocity range input params.

* Add E field v-limiting

Lorentz Efield calculation.

* Add working scattering.

Low density -> positivity problems.

* A working setting (dv = - dt).

mpirun -n 4 exTRguts -m '../data/inline-quad.mesh' -rs 2 -vis -s 24 -ne 1e21 -sna 8 -vini 1.0 -vfin 0.0 -Ngr 100 -scit 1

Scattering is off.

* Scattering converges, yet unchecked.

mpirun -n 4 exTRguts -m '../data/inline-quad.mesh' -rs 1 -vis -s 24 -ne 1e21 -sna 4 -vini 1.0 -vfin 0.075 -Ngr 100 -scit 30

* Scaled -> nicer plot.

* Add exETRp example.

* Scattering is on.

* rm temp files, add user cmake to gitignore

* Added some more hypre options

* Updated example 9 to support implicit time stepping and AIR

* Added ex23 DG adv-diff with AIR for implicit solves

* example updates

* Fixing merge issue

* added two air options

* Updated AIR flags to be consistent with hypre master branch

* Removed include folder from repo

* removed build dir accidentally added..

* Removed TRT deve examples

* Removed TRT dev file

* Removed 23, MFEM_adv, updated ex9p to have AIR support

* Fixing travis stuff

* More travis

* Update hypre version in yml file for Travis

* Update hypre version in appveyor yml

* Update .appveyor.yml

* \ for windows?

* Added general DG blocksize, added extrat submatrix routine from hypre

* Fixed pointer

* Merge w/ master, fix one space in style

* Missed hypre dirs in appveyor and travis

* Reset appveyor/travis, added flag for hypre >= 21800.

* missed some hypre version #s

* Ex9 was modified in master, auto merge didn't work

* Ex9 running on quartz

* Affressing most of Dylan's comments

* Cleaning up hypre/AIR interface, addressing comments

* Some more comments

* Simplify AIR with ex9p

* Minor changes to hypre/AIR interface

* Use MPI_Session in ex9p

* Use Array instead of pointer

* Use HYPRE_Int or HYPRE_BigInt instead of int

* make style

* Minor

* Fix signed/unsigned int warning

* Removed boomeramg mult, added doxygen for wraphypreparvector

* Use MFEM's hypre memory wrappers

* Remove HypreBoomerAMG::Mult from header

* Added AIR comments, #if for hypre version

* Make input parameter const

* Use 3 procs for sample run

* Re-add comment

* Address review comments

* Address issues in WrapHypreParCSRMatrix

* Fix unintended change

* Fix memory leak in HypreParMatrix::MergeDiagAndOffd

* Small doxygen updates.

* Always call HypreParMatrix::Destroy when wrapping new vector

* Update CHANGELOG

Co-authored-by: Ruipeng Li <li50@llnl.gov>
Co-authored-by: Ruipeng Li <li50@quartz2498.llnl.gov>
Co-authored-by: holec1 <holec1@llnl.gov>
Co-authored-by: milan_holec <homijan@gmail.com>
Co-authored-by: Tzanio <tzanio@llnl.gov>
Co-authored-by: Will Pazner <will.e.p@gmail.com>
Co-authored-by: Veselin Dobrev <dobrev@llnl.gov>
2021-01-19 12:46:34 -08:00
Tzanio Kolev 9d236c575c Merge pull request #1965 from mfem/sum-integ-pa
PA and DG for SumIntegrator and add NonconservativeDGTraceIntegrator [sum-integ-pa]
2021-01-19 12:24:09 -08:00
Tzanio 998f4b5578 Fix compiler warning 2021-01-19 08:39:45 -08:00
Tzanio ebf7d3e1ef Merge branch 'master' into wrapper-bform-dev 2021-01-19 08:30:04 -08:00
Will Pazner 06054386c0 Merge remote-tracking branch 'origin/master' into sum-integ-pa
# Conflicts:
#	examples/ex9.cpp
#	examples/ex9p.cpp
#	fem/bilininteg.hpp
2021-01-18 15:47:44 -08:00
Tzanio b09086725d Listing MixedBilinearForm::FormRectangularLinearSystem as virtual 2021-01-16 23:12:19 -08:00
Tzanio Kolev 1155c003a8 Merge pull request #1761 from mfem/pa-id-interp
PA for GradientInterpolator and IdentityInterpolator
2021-01-16 23:01:04 -08:00
Tzanio Kolev 20f92199d2 Merge pull request #1879 from mfem/chak/hypresmoother-transpose
Action of HypreSmoother::MultTranspose
2021-01-14 16:30:49 -08:00
Tzanio Kolev 841769637b Merge pull request #1901 from mfem/wcdawn/documentation-DGTraceIntegrator
Add documentation for outflow boundary condition in DGTraceIntegrator
2021-01-14 16:05:06 -08:00
wcdawnandVeselin Dobrev ecef55a712 Apply suggestions from code review
Co-authored-by: Veselin Dobrev <v-dobrev@users.noreply.github.com>
2021-01-14 13:40:30 -07:00
Will Pazner 5d8cf0d37e Fix issue setting spaceDim with vertices 2021-01-13 10:37:30 -08:00
Tzanio Kolev 759987ef31 Merge pull request #1931 from mfem/test-lin-interp-dev
Broadening support for linear interpolators [test-lin-interp-dev]
2021-01-12 15:58:25 -08:00
Tzanio 316bb189c9 minor 2021-01-12 15:57:50 -08:00
Stowell, Mark L b5682f808f Updating CHANGELOG 2021-01-12 14:12:38 -08:00
Stowell, Mark L a77fb59626 Implementing/testing ProjectCurl_2D for L2 basis with FiniteElement::VALUE 2021-01-12 13:43:13 -08:00
Tzanio 2550df844d Merge branch 'master' into feature/vtk_high_order
Conflicts:
	CHANGELOG
2021-01-11 10:47:34 -08:00
Will Pazner e46d6bec93 Update CHANGELOG 2021-01-11 10:39:54 -08:00
Will Pazner 1e21230bc9 Fix bug reading quadratic VTK mesh 2021-01-11 10:32:04 -08:00
Andrew T. Barker 36796e6d55 Mention GradientInterpolator and IdentityInterpolator in CHANGELOG. 2021-01-11 08:24:45 -08:00
Andrew T. Barker 83669def7d Merge remote-tracking branch 'origin/master' into pa-id-interp 2021-01-11 08:20:08 -08:00
Tzanio 2399f47207 minor 2021-01-10 15:36:32 -08:00
Andrew T. Barker 35882669b9 Add (Par)FiniteElementSpace::GetRestrictionOperator() for consistency 2021-01-08 15:14:11 -08:00
Tzanio Kolev 3f9443d497 Merge pull request #1818 from mfem/ew-newton
Adaptive linear rtol for NewtonSolver
2021-01-08 12:57:22 -08:00
Julian Andrej 9794db95c5 added changelog entry 2021-01-08 12:44:41 -08:00
Tzanio Kolev 928d66caba Merge pull request #1950 from mfem/fix-make-shared-install
Fix the shared installation with CUDA and TPL link flags with CUDA
2021-01-07 08:23:24 -08:00
Andrew T. Barker d09826e403 Rename LocalProlongation as RestrictionTranspose for matrix-free interpolators. 2021-01-06 16:13:30 -08:00
Tzanio bbe4b002f1 Comments 2021-01-06 15:54:43 -08:00
Andrew T. Barker 0ae9e69567 ParDiscreteLinearOperator::ParallelAssemble: rename local variable for clarity 2021-01-06 15:17:52 -08:00
chakshinglee 718e9b2ce3 better setup 2021-01-05 20:29:00 -08:00
chakshinglee 17a8e525a4 use symmetry of the linear system A to determine the action of HypreSmoother::Transpose 2021-01-05 19:06:54 -08:00
Will Pazner dbd5cfcb4c Remove Doxygen comments to try to fix CI 2021-01-05 18:56:44 -08:00
Alejandro Campos 23074f0e35 test passed with ex5p, reverting it back to original 2021-01-05 17:50:45 -08:00
Alejandro Campos 423ca596d8 Merge branch 'master' into wrapper-bform-dev 2021-01-05 17:46:49 -08:00
Will Pazner 57975eb915 Use default beta value in ex9 and ex9p. 2021-01-05 17:26:52 -08:00
Will Pazner 46899fcbb5 Add default beta values for BoundaryFlowIntegrator, ...
... DGTraceIntegrator, and NonconservativeDGTraceIntegrator.

If no beta is provided, 0.5*alpha will be used, which gives an upwind
discretization. This is usually what the user wants, and seems to be
a good "sane default".
2021-01-05 17:26:18 -08:00
Will Pazner 41c5dafc2a Simplify NonconservativeDGTraceIntegrator using consistent alpha 2021-01-05 17:11:53 -08:00
Will Pazner 0fa2698cc7 Add comment about central flux 2021-01-05 17:11:18 -08:00
Will Pazner fb2e25d068 Make elem_mat mutable 2021-01-05 17:09:54 -08:00
Dylan Copeland 2faf0ae640 Reverting some changes to SetSubVector. 2021-01-05 14:15:28 -08:00
Will Pazner eae4e1128c Add comment explaining lexicographic ordering 2021-01-04 14:26:20 -08:00
Will Pazner b31f086d3c Merge remote-tracking branch 'origin/master' into sum-integ-pa 2021-01-04 11:49:00 -08:00
Will Pazner 01e8b1da07 Formatting 2021-01-04 11:48:37 -08:00
Will Pazner db53ce67bb Fix typo in comment 2021-01-04 11:48:30 -08:00
Will Pazner 7dd8e562d5 Add constants for alpha and beta in ex9 and ex9p 2021-01-04 11:48:25 -08:00
chakshinglee 92a87e1253 use Boolean flag to indicate if a HypreSmoother is symmetric 2021-01-04 10:59:45 -08:00
Tzanio Kolev f4c2758e82 Merge pull request #1977 from mfem/bugfix/matvec-coef-dev
Setting the size of the output vector in MatrixVectorProductCoefficient [bugfix/matvec-coef-dev]
2021-01-04 08:55:16 -08:00
Tzanio Kolev 9535299c2a Merge pull request #1955 from mfem/docu-proj
Document GridFunction::ProjectCoefficient and FiniteElement::Project.
2021-01-02 14:55:20 -08:00
Stowell, Mark L 9d87c4efcd Expanding product interpolator tests to include 1D and 2D tests 2020-12-31 12:48:10 -08:00
Stowell, Mark L c5ceee3ef5 Collapsing derivative tests into a loop over element types 2020-12-31 12:47:24 -08:00
Veselin Dobrev 3d946569a1 Merge pull request #1829 from mfem/face-nbr-fix
Fix GridFunction::GetGradient for face neighbor elements
2020-12-30 15:28:17 -08:00
Veselin Dobrev a8ebd745a8 Merge pull request #1956 from mfem/feature/qspace_get_order
Update QuadratureSpace with a GetOrder
2020-12-30 15:23:54 -08:00
Stowell, Mark L 3191cfa1c3 Setting the size of the output vector in MatrixVectorProductCoefficient 2020-12-30 10:25:38 -08:00
blaz c0b3dc0097 fixing bugs 2020-12-29 19:35:48 -08:00
Stowell, Mark L 388e07a373 Collapsing new 1D, 2D, and 3D unit tests into a loop over element types 2020-12-29 15:32:56 -08:00
Stowell, Mark L 320deb43ec Implementing vector versions of lower dimensional grad and curl test functions 2020-12-29 15:32:14 -08:00
Stowell, Mark L 04444b15d3 Adding comments to the VectorFiniteElement projection methods 2020-12-29 14:38:43 -08:00
Tzanio Kolev caf5d7b471 Merge pull request #1904 from mfem/amgx/artv3/configure
AmgX configuration method
2020-12-28 12:28:52 -08:00
Tzanio 736b2c79b2 minor 2020-12-28 12:27:25 -08:00
Julian Andrej 92748c1c70 remove leftovers 2020-12-28 11:45:08 -08:00
Julian Andrej 7c27b32e90 fix doxygen comment 2020-12-28 11:27:16 -08:00
Julian Andrej f442f6cee1 Merge branch 'master' into ew-newton 2020-12-28 11:07:58 -08:00
Julian Andrej 1342a59943 remove jacobian vector product 2020-12-28 11:04:51 -08:00
Julian Andrej fe23fcd39b remove jacobian vector product 2020-12-28 10:59:55 -08:00
Tzanio Kolev de9fb83efe Merge pull request #1971 from mfem/hip-typo-fix
Fix a minor typo in hip.cpp
2020-12-26 13:36:27 -08:00
Tzanio Kolev 60b1997858 Merge pull request #1921 from mfem/d2q-1d
Simple implementation for D2QValues1D
2020-12-26 13:31:09 -08:00
Veselin Dobrev de990a5fdc In hip.cpp, fix typo: MFEM_TRACK_HPI_MEM -> MFEM_TRACK_HIP_MEM 2020-12-24 14:00:41 -08:00
Tzanio Kolev 3ee90e4c0e Merge pull request #1890 from mfem/umf-mumps-slu-device-fix
Direct Solvers device fix
2020-12-24 12:20:41 -08:00
Tzanio Kolev b4c53cff15 Merge pull request #1845 from mfem/chak/multigird-without-fe
Remove FE spaces from Multigrid
2020-12-24 12:17:56 -08:00
Tzanio 69981e1996 Updated CHANGELOG and comments 2020-12-24 12:13:47 -08:00
Tzanio f84a8d80e0 Merge branch 'master' into chak/multigird-without-fe 2020-12-24 11:42:16 -08:00
Tzanio 50197feee0 Minor wording 2020-12-23 12:56:38 -08:00
blaz 108956ea49 serial example 2020-12-23 12:37:18 -08:00
blaz b581a194e2 bug cleaning 2020-12-23 12:36:16 -08:00
Dylan Copeland 116ae7bbed Editing some comments based on reviewer suggestions. 2020-12-23 12:35:44 -08:00
Will Pazner ba69eadeac Reverting addition of new integrators
Those will be added in a more comprehensive way in PR #1965.
2020-12-23 10:01:57 -08:00
Will Pazner 7e93c72948 make style 2020-12-23 09:53:46 -08:00
Will Pazner 876fafd76b Merge branch 'wcdawn/documentation-DGTraceIntegrator' into sum-integ-pa 2020-12-22 17:56:48 -08:00
Will Pazner aa1da8bee2 Merge remote-tracking branch 'origin/master' into wcdawn/documentation-DGTraceIntegrator 2020-12-22 17:45:24 -08:00
Will Pazner d738070ee2 Unify coefficients for DGTraceIntegrator and NonconservativeDGTraceIntegrator 2020-12-22 17:37:29 -08:00
Will Pazner c775a8a1a4 Merge branch 'wcdawn/documentation-DGTraceIntegrator' into dg-trace-sum 2020-12-22 17:04:09 -08:00
Will Pazner 621ad50df6 Test PA and DG for SumIntegrator 2020-12-22 17:03:08 -08:00
Will Pazner 152b02df4f Add PA and DG support for SumIntegrator 2020-12-22 17:02:50 -08:00
Will Pazner b6ea922b35 Add AddMultTransposePA for (symmetric) mass and diffusion 2020-12-22 17:02:31 -08:00
Veselin Dobrev 476e95305d Merge branch 'master' into fix-make-shared-install 2020-12-22 16:40:01 -08:00
Tzanio Kolev d5e291e989 Update fespace.hpp 2020-12-22 12:57:29 -08:00
Robert CarsonandTzanio Kolev 6d0f5971ad Update fem/fespace.hpp
Co-authored-by: Tzanio Kolev <tzanio@llnl.gov>
2020-12-22 12:33:43 -08:00
Robert 3a21476b5c Update wording of function comment 2020-12-22 09:37:29 -08:00
blaz dc044d8e93 new features 2020-12-21 14:49:58 -08:00
Tzanio Kolev 9974c0c6a9 Merge pull request #1752 from mfem/raja-teams
Add RAJA HIP and RAJA Teams
2020-12-21 12:15:43 -08:00
psocratis 55e117bb82 Enforce metis ordering in SuperLU solver test 2020-12-21 12:04:56 -08:00
Veselin Dobrev 0eaa5824ee In Mesh::GeneratePartitioning, fix a bug in the case when the
number of elements is less than the number of processors.
2020-12-20 13:41:56 -08:00
Tzanio Kolev ae5a84f2ef Merge pull request #1920 from mfem/overload-function-warning-fix
Fixing overloaded function warning [overload-function-warning-fix]
2020-12-20 13:07:53 -08:00
Vladimir TomovandTzanio a1a0aeb6af [SQUASH] TMOP improvements for untangling (#1884)
* Untangling example.

* Improvements in the untangling logic.

* Bug.

* Minor.

* More untangling corrections.

* Slight improvement.

* More untangling improvements.

* Minor.

* No energy / residual checks in the line search for untangling.

* Minor improvement in 313 and 022.

* Renamed variables.

* Disabled the size-only untangling metrics in the miniapp.

* Corresponding changes in the mesh-optimizer.

* Removed mesh files.

* make style

* Minor.

* Removed the use of sign(detT) from invariants.hpp.

* Changelog.

* minor

* Added '-vl 1' for the untangling sample runs.

* Removed all of '-rs 0 -ls 2 -li 100' from the sample command lines as
these are the default parameters in the miniapp.

Co-authored-by: Tzanio <tzanio@llnl.gov>
2020-12-20 12:57:25 -08:00
Arturo Vargas 241a5a2544 pr comments 2020-12-18 14:13:24 -08:00
Arturo VargasandAndrew T. Barker b9e26ee081 Update linalg/amgxsolver.hpp docs
Co-authored-by: Andrew T. Barker <barker29@llnl.gov>
2020-12-18 14:00:24 -08:00
Veselin Dobrev 54111e4fd5 Merge pull request #1722 from mfem/kelly-error-indicator-dev
Add Kelly error indicator
2020-12-18 12:19:56 -08:00
Veselin Dobrev da7c353df6 Merge pull request #1065 from najlkin/pr11
Fixed indices for boundary trace face integrators in MixedBilinearForm::Assemble()
2020-12-18 12:18:52 -08:00
Veselin Dobrev 9b25209337 Merge pull request #910 from hillyuan/master
METIS_PartGraphKway in FindMETIS.cmake may not working due to type error [hillyuan:master]
2020-12-18 12:17:11 -08:00
Robert 58e6db4118 make style 2020-12-17 10:52:18 -08:00
Robert Carson d226010d4e Update QuadratureSpace with a GetOrder 2020-12-17 09:53:31 -08:00
Dylan Copeland 805d51a2eb Documenting GridFunction::ProjectCoefficient and FiniteElement::Project. 2020-12-16 17:44:19 -08:00
Vladimir Z Tomov 6e7e1092db Avoids writing in the global vector every time. 2020-12-16 14:34:13 -08:00
blaz a614cac02c Initial implementation of parametric block nonlinear form 2020-12-16 09:45:19 -08:00
Tzanio Kolev 95e7dd3df8 Merge pull request #1934 from mfem/cmake-export-cxx-std-flag
CMake update
2020-12-15 13:27:24 -08:00
psocratis ecd61941d6 removing test_umf 2020-12-15 10:29:04 -08:00
Veselin Dobrev 8f010f3c0c In makefile, fix the shared install with CUDA.
In config/defaults.mk, use "$(XLINKER)" instead of "-Wl," in all
library link options.
2020-12-14 18:33:54 -08:00
Arturo Vargas bdf0e9b1d0 make style 2020-12-14 16:19:05 -08:00
Arturo Vargas 84dcb16949 ConfigureAs->SetConvergenceCheck 2020-12-14 16:17:23 -08:00
psocratis 01df630912 adding pcunit_tests to CMakeLists.txt 2020-12-14 13:26:19 -08:00
Vladimir Z Tomov 80ff56cfca Commented out the approximate Jac-vec-product. 2020-12-13 17:31:53 -08:00
Vladimir Z Tomov 58bf8f54e3 Exposed both types in the miniapps. 2020-12-13 15:13:41 -08:00
Vladimir Z Tomov 2552255ffe Updated some sample runs to use the adaptive rtol.
Updated the serial mesh-optimizer version.
2020-12-13 14:44:34 -08:00
Vladimir Z Tomov 8a130243fb Merge branch 'master' into ew-newton 2020-12-13 13:29:34 -08:00
Vladimir Z Tomov 3acc834f1d Minor. 2020-12-13 13:28:52 -08:00
Alejandro Campos 63a40bf1e9 fixes performed by make style 2020-12-11 16:35:57 -08:00
psocratis e06097b9af removing some tests 2020-12-11 16:16:54 -08:00
psocratis 09372aea2d adding Parallel and CUDA unit tests for mumps and slu 2020-12-11 13:43:08 -08:00
Dennis Ogiermann 502b95fb86 Merge branch 'master' into kelly-error-indicator-dev 2020-12-11 18:21:53 +01:00
termi-official 777d222577 Update changelog. 2020-12-11 18:05:47 +01:00
psocratis be1b800b61 Adding cuda unit test for umf 2020-12-10 18:45:59 -08:00
blaz b01b4956cf Initial interface definition for the Parametric Block integrators 2020-12-10 18:44:46 -08:00
psocratis 4f5619c88a minor edits 2020-12-10 16:07:25 -08:00
psocratis 5231ed302c Merge branch 'master' into umf-mumps-slu-device-fix 2020-12-10 15:56:33 -08:00
Julian Andrej b796dae62c restore ex10p 2020-12-10 07:36:26 -08:00
Julian Andrej 7bcba91890 address comments and correct dq eps 2020-12-10 07:36:18 -08:00
Veselin Dobrev 33dfee978b Merge pull request #1865 from mfem/coef-api
Matrix coefficient interface improvements
2020-12-09 22:48:46 -08:00
Veselin Dobrev 73ff58db37 Merge branch 'master' into kelly-error-indicator-dev 2020-12-09 22:12:51 -08:00
Veselin Dobrev 7d1bf5a748 Replace 'abs()' with 'std::abs()' to fix an issue with some
compilers (e.g. Intel) using the integer version of `abs()`
instead of the 'double' version causing failures in the unit
tests in tests/unit/fem/test_estimator.cpp.

Use 'auto &' instead of just 'auto' in various places to
avoid copying arrays and structures.

Pass 'Array' parameters by reference instead of by value.

Define ErrorEstimator::GetTotalError() in the base class to
return zero, making it optional to override in derived classes.
This should allow any user defined derived classes to continue
to work without having to define this new method.

Some small formatting edits.
2020-12-09 21:56:14 -08:00
Will Pazner 921f4aaab6 Small simplification in ParGridFunction::GetElementDofValues 2020-12-09 10:31:12 -08:00
Will Pazner a5806ea9eb Rename GridFunction::GetElementDofs to GridFunction::GetElementDofValues 2020-12-09 10:22:55 -08:00
Veselin Dobrev ca721dd1ae Make sure CMake exports the C++ language flag to config.mk
Bump the minimum CMake version requirement to 3.1.
2020-12-08 23:21:12 -08:00
Stowell, Mark L 04f44f2a50 Expanding linear interpolator unit tests 2020-12-08 15:11:34 -08:00
Stowell, Mark L de36574fa7 Adding Vector FE support in Project_RT and Project_ND 2020-12-08 15:11:09 -08:00
Stowell, Mark L 0e2735128c Adding support for mixed FiniteElement::INTEGRAL/VALUE in NodalFiniteElement::Project 2020-12-08 15:10:26 -08:00
Stowell, Mark L 83c9d7289a Adding FiniteElement::INTEGRAL support to GridFunction::GetVectorValues 2020-12-08 15:08:44 -08:00
Stowell, Mark L 0bbbc43ebf Adding FiniteElement::INTEGRAL support to GridFunction::GetValues 2020-12-08 15:07:45 -08:00
Socratis PetridesandWill Pazner 216d50b0ad Update fem/gridfunc.hpp
Co-authored-by: Will Pazner <11493037+pazner@users.noreply.github.com>
2020-12-07 17:41:29 -08:00
Vladimir Z Tomov 428b515668 style. 2020-12-06 22:03:21 -08:00
Vladimir Z Tomov 72924b927b D2QValues1D 2020-12-06 21:36:06 -08:00
Alejandro Campos bac26a7422 added wrapper for FormRectangularSystemMatrix() 2020-12-06 16:38:15 -08:00
psocratis 165b7910ec removing virtual from deprecated function 2020-12-04 12:06:06 -08:00
psocratis 83181083e9 fixing overloaded function warning 2020-12-04 11:57:16 -08:00
wcdawn aa1127fdf0 Merge pull request #1914 from mfem/convection-documentation
Documentation/convenience changes for upwind advection
2020-12-04 11:59:16 -05:00
termi-official aba640498e Fix duplicate in example 15 docs. 2020-12-04 17:26:54 +01:00
Dylan Copeland c059fe7f41 Merge branch 'master' of https://github.com/mfem/mfem into coef-api 2020-12-02 15:20:20 -08:00
Dylan Copeland 8c55d3aa1f CHANGELOG 2020-12-02 15:18:39 -08:00
Julian Andrej b420256c2f another try to fix doxygen comments 2020-12-02 13:36:29 -08:00
Julian Andrej 9b12b445d5 fix doxygen comment 2020-12-02 13:07:10 -08:00
Julian Andrej b8922a8988 non-messed up merge 2020-12-02 10:52:38 -08:00
Julian Andrej d0fd04c7dd Merge branch 'master' into ew-newton 2020-12-02 10:49:06 -08:00
Will Pazner 08de600931 Modify ex9 and ex9p to use NonconservativeDGTraceIntegrator 2020-12-02 08:33:45 -08:00
Will Pazner 3ac5c3a4b7 Add ConservativeConvectionIntegrator, NonconservativeDGTraceIntegrator, and some comments 2020-12-01 13:18:14 -08:00
Will Pazner 906e82f099 Merge remote-tracking branch 'origin/master' into face-nbr-fix 2020-11-30 13:12:47 -08:00
Arturo Vargas 4b4cde0da5 clean up, add to examples 2020-11-24 00:30:17 -08:00
Arturo Vargas ecace58420 Merge branch 'master' into amgx/artv3/configure 2020-11-23 22:59:49 -08:00
William Dawn a74010deb5 add documentation for outflow boundary conditions with DGTraceIntegrator 2020-11-23 11:31:27 -05:00
Will Pazner 90301913e7 Merge pull request #1894 from mfem/xml_vtk
XML VTK Support
2020-11-20 14:42:34 -08:00
Will Pazner 2851ca57b7 Add TinyXML2 to NOTICE file 2020-11-20 14:41:38 -08:00
Will Pazner 12363d84a1 Fix Doxygen syntax error 2020-11-20 14:25:12 -08:00
Will Pazner da1df498cb Merge remote-tracking branch 'origin/feature/vtk_high_order' into xml_vtk 2020-11-20 11:20:07 -08:00
Will Pazner b73a913dd2 Merge remote-tracking branch 'origin/master' into feature/vtk_high_order
# Conflicts:
#	mesh/vtk.cpp
2020-11-20 11:19:46 -08:00
Will Pazner f0f3dccb79 Fix warning 2020-11-19 20:57:01 -08:00
Will Pazner 75c206f360 Fix errors with sscanf format string in TinyXML 2020-11-19 20:46:26 -08:00
Will Pazner c10c41e170 Minor 2020-11-19 17:04:56 -08:00
Will Pazner 02aee8bbbf Exclude TinyXML from code style and Doxygen docs 2020-11-19 14:47:25 -08:00
Will Pazner b35a29a82c Minor 2020-11-19 13:03:15 -08:00
Will Pazner 224ea084d8 Read XML VTK meshes (including high-order) 2020-11-19 12:48:37 -08:00
Will Pazner 9cda70d880 Add TinyXML2
zlib licensed, repo at https://github.com/leethomason/tinyxml2
2020-11-19 12:48:07 -08:00
Will Pazner 2c5a2a6045 Initialize spaceDim in NURBS mesh reader 2020-11-18 21:43:29 -08:00
Will Pazner acb253ca96 Fix small memory leak in VTK mesh reader 2020-11-18 18:30:11 -08:00
Will Pazner 8300ab8fa1 Test lexicographic ordering of L2 elements 2020-11-18 18:26:13 -08:00
Will Pazner 883011bc85 Update high-order VTK mesh reader 2020-11-18 18:16:41 -08:00
Will Pazner ab28d29fea Minor 2020-11-18 18:14:38 -08:00
Will Pazner 7deaae57b3 Make VTK tet output consistent with new refined vertex ordering 2020-11-18 17:14:47 -08:00
Will Pazner 4a1ae4af84 Add lexicographic ordering for all H1 element types
Mappings from canonical "MFEM" ordering (vertices, edges, faces,
interior) to lexicographic/Cartesian geometry on triangles,
tetrahedra, and prisms.
This map already exists for tensor-product geometries.
2020-11-18 15:55:08 -08:00
Will Pazner 7ca85ffcef Remove unnecessary special case for refined prism 2020-11-18 14:24:08 -08:00
Will Pazner b1ae8d6807 Use lexicographic vertex ordering for refined tetrahedron 2020-11-18 14:24:08 -08:00
termi-official 4a0142a1c6 Update descrition of ex15p to include the different error estimators. 2020-11-18 21:22:39 +01:00
psocratis 1cbecbc0cf ComplexUMFPackSolver fix 2020-11-18 11:30:41 -08:00
psocratis ff1c2deb4f fixing device runs for slu, mumps and umfpack 2020-11-17 19:16:02 -08:00
Will Pazner 9dc9c9bcf5 Simplify Mesh::PrintVTU and Mesh::PrintVTK 2020-11-17 17:50:02 -08:00
Will Pazner 9250600e11 Add VTK geometry helpers 2020-11-17 17:38:55 -08:00
Will Pazner 38d30f034a Simplify spaceDim detection code 2020-11-17 12:43:43 -08:00
Will Pazner 30e4f991ad Merge remote-tracking branch 'origin/master' into feature/vtk_high_order 2020-11-17 12:34:11 -08:00
Dylan Copeland 4051ee21f8 Restoring EvalSymmetric function as deprecated. 2020-11-17 10:30:58 -08:00
Veselin Dobrev d28d6e4641 Remove the use of the deprecated field 'Face' in the class
FaceElementTransformations.
2020-11-16 17:41:36 -08:00
Veselin Dobrev 647fda9c97 Merge branch 'master' into najlkin-pr11 2020-11-16 17:33:56 -08:00
Dylan CopelandandWill Pazner 5594557196 Add LEGACYFULL case.
Co-authored-by: Will Pazner <11493037+pazner@users.noreply.github.com>
2020-11-16 11:50:10 -08:00
Dylan Copeland b2a61eb9dc Moving some underscores. 2020-11-16 09:39:21 -08:00
chakshinglee df6cea3833 add a flag to give flexibility for HypreSmoother::MultTranspose to be defined when the smoother is symmetric 2020-11-15 23:24:26 -08:00
Andrew T. Barker 474cfafb95 make style 2020-11-13 13:19:32 -08:00
Andrew T. Barker 4d23cd820b Specialize PAGradient kernels for (common) case where B operator is the identity. 2020-11-13 12:37:48 -08:00
termi-official 5d085f9710 Remove unused member in Kelly. 2020-11-13 01:09:09 +01:00
termi-official 7428c2053f Address remaining ownership issues and add the ZZ error estimator to ex15p. 2020-11-13 00:19:05 +01:00
termi-official b7b6d8142f Fix global error computation for the Lp error estimator in parallel and add a mechanism to take ownership of the used FE space in the Kelly error indicator. 2020-11-13 00:15:03 +01:00
termi-official 8bd7b14714 Fix global error computation for the ZZ error estimator in parallel. 2020-11-13 00:12:02 +01:00
termi-official cb78aa6c4a Fix test style. 2020-11-12 01:52:57 +01:00
termi-official 0e47d1297f Add error estimator tests on embedded meshes. 2020-11-12 01:40:34 +01:00
termi-official a722d2b704 Remove errnous sign. 2020-11-12 01:34:52 +01:00
termi-official 10cecd5134 Docs again. 2020-11-11 23:49:29 +01:00
termi-official 34070ab952 Polishing docs and types. 2020-11-11 23:47:13 +01:00
termi-official f5830b612a Add types for the coefficient functions. 2020-11-11 23:36:47 +01:00
termi-official 78a6152900 Finalize ex15p. 2020-11-11 23:29:14 +01:00
termi-official 28667f27f6 Move GetTotalError up one abstraction layer. 2020-11-11 23:17:55 +01:00
termi-official ee7b17f18a Fixed formatting. 2020-11-10 22:40:21 +01:00
termi-official 0c315483e7 Speeding up attribute identification. 2020-11-10 22:29:02 +01:00
termi-official 3f7f0e9933 Improve the documentation. 2020-11-10 22:15:10 +01:00
termi-official 43ce4436d8 Exchange constructed integration rule with the global one. 2020-11-10 22:04:28 +01:00
Dylan Copeland 1b52f0fb50 Merge branch 'master' of github.com:mfem/mfem into coef-api 2020-11-10 09:41:53 -08:00
Dylan Copeland 44d95614f5 Bug fix 2020-11-10 09:41:04 -08:00
Dylan Copeland 05c8ee6f80 Minor changes 2020-11-10 09:18:33 -08:00
Dylan Copeland c60500466b Adding DiagonalMatrixCoefficient typedef to replace VectorCoefficient in some integrator interfaces. 2020-11-10 09:08:12 -08:00
termi-official 092b808022 Add error message. 2020-11-08 20:56:08 +01:00
termi-official 455b92546e Fix flux field. 2020-11-08 20:18:20 +01:00
termi-official e1bea63afb Actually include fluxes. 2020-11-08 18:30:31 +01:00
termi-official 162ac70998 Fix signs and increase order of integration. 2020-11-08 18:27:10 +01:00
Dylan Copeland 40ebc18e97 Addressing various review comments. 2020-11-06 14:01:56 -08:00
termi-official a4ced201d0 Fix error estimation for embedded meshes. 2020-11-05 21:42:56 +01:00
Arturo Vargas e3d6a0e867 logic fix when using raja 2020-11-05 11:24:32 -08:00
termi-official 42b3b64c8e Change the error coefficients to the approach described by Wolfgang Bangerth. 2020-11-05 17:13:04 +01:00
Dylan Copeland 9a238d002f Fixing a compiler warning. 2020-11-03 18:06:30 -08:00
Dylan Copeland a5d5c1ba32 Eliminating EvalSymmetric from MatrixCoefficient and replacing with usage of SymmetricMatrixCoefficient. 2020-11-03 17:02:28 -08:00
Dylan Copeland 0626694f33 Adding new classes for symmetric matrices and corresponding coefficients. 2020-11-03 08:11:52 -08:00
termi-official a3b8ebf2db Fixing typos, clarifying docs and adding an additional coefficient to the formulation. 2020-11-03 04:48:56 +01:00
termi-official d5310e7ac3 First try to fix face integrals. Note: P-refinement is still broken. 2020-11-03 03:45:03 +01:00
Arturo Vargas 42658ec9f8 minor clean up 2020-11-02 18:08:26 -08:00
Arturo Vargas 0680fd802b Merge branch 'master' into raja-teams 2020-11-02 18:00:35 -08:00
Arturo Vargas eb3bb90760 split cuda and hip backends 2020-11-02 17:59:59 -08:00
Arturo Vargas 76f7c4e698 bump DEBUG_DEVICE 2020-11-02 14:59:21 -08:00
Arturo Vargas dfc50efa35 add * to raja-hip in example 1 2020-11-02 14:48:21 -08:00
chakshinglee 924e321351 fix destructor of Multigrid 2020-11-02 13:03:35 -08:00
chakshinglee a2f5eb54cb fix destructor of Multigrid 2020-11-02 12:50:49 -08:00
Arturo Vargas ccbdf2b1eb add configure as method comments 2020-11-01 19:47:21 -08:00
Arturo Vargas 39ff9c78c2 add method to configure as preconditioner 2020-11-01 16:44:34 -08:00
Arturo Vargas 520c63b56b update install 2020-10-30 17:50:46 -07:00
Arturo Vargas 4bc672906c revert changes in defaults 2020-10-30 17:44:43 -07:00
Arturo Vargas b11168cfe0 fix merge conflicts 2020-10-30 16:46:52 -07:00
Andrew T. Barker d732a479aa Improve documentation for FiniteElementSpace::GetLocalProlongationOperator() 2020-10-28 09:47:37 -07:00
Andrew T. Barker 4235a22838 Remove commented out code. 2020-10-28 09:45:45 -07:00
Andrew T. Barker ad363ada13 GetLocalProlongationMatrix() -> GetLocalProlongationOperator() 2020-10-28 09:35:10 -07:00
Dylan Copeland e069d9b034 Fixing parallel device bug. 2020-10-27 16:42:34 -07:00
chakshinglee 71c947d966 fix style 2020-10-27 13:46:45 -07:00
chakshinglee 5db9988a41 adjust documentation 2020-10-27 01:24:56 -07:00
chakshinglee a19b34b403 fix wrong input for Solver size and initialize vectors with 0.0 2020-10-27 00:30:06 -07:00
chakshinglee e55fbc7af5 add a constructor of Multigrid that all the components (operators, smoothers, prolongations) and given from input 2020-10-26 15:12:32 -07:00
chakshinglee 8f5d0ef084 define GeometricMultigrid as a derived class of Multigrid, move the components in Multigrid that depend on finite element spaces to GeometricMultigrid 2020-10-26 15:00:55 -07:00
Julian Andrej 0cdbcd647e add matrix free jacobian vector product 2020-10-26 10:55:52 -07:00
Andrew T. BarkerandTzanio Kolev 35159031ee Apply suggestions from code review
Co-authored-by: Tzanio Kolev <tzanio@llnl.gov>
2020-10-23 14:43:08 -07:00
Will Pazner 2237a20063 Add Doxygen comments 2020-10-22 12:06:44 -07:00
Vladimir Z Tomov 45b2f40d98 Added gamma scaling to Option 1. 2020-10-22 11:47:15 -07:00
Vladimir Z Tomov 6994291f15 Added as option in pmesh-optimizer. 2020-10-21 15:00:23 -07:00
Will Pazner 9a84630556 Add check for ExchangeFaceNbrData 2020-10-20 18:17:34 -07:00
Will Pazner ba6d89b4cc Fix GridFunction::GetGradient for face neighbor elements
Try to make some GridFunction and FiniteElementSpace functionality a
bit more general/automatic when working with face neighbors.
2020-10-20 18:08:13 -07:00
Dylan Copeland 9d07fea8f4 More unit test improvements. 2020-10-20 11:08:22 -07:00
Dylan Copeland 2886dcc849 Minor improvements to unit test. 2020-10-20 10:28:17 -07:00
Julian Andrej 749f3da7cc make style 2020-10-20 09:28:43 -07:00
Julian Andrej 1f1b545ec0 rework interface 2020-10-19 11:21:20 -07:00
Julian Andrej b3b63da0c2 eisenstat-walker adaptive linear rtol 2020-10-16 16:15:18 -07:00
Andrew T. Barker fe1e1a4128 Begin implementation of a local version of DeviceConformingProlongationOperator
This is for doing Interpolators (rather than Integrators) on the device,
and it almost works but appears to have some sort of bug in 3D.
2020-09-28 13:02:10 -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
Arturo Vargas b213d80b12 revert KERNEL_ABORT_METHOD function 2020-09-18 10:33:33 -07:00
Arturo Vargas fdf86706ea PR comments, styling 2020-09-18 10:31:26 -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
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
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
Arturo Vargas cb637556c3 makefile fixes 2020-09-08 15:30:01 -07:00
Arturo Vargas bc5a051d8c Add RAJA-hip 2020-09-08 15:07:37 -07:00
Arturo Vargas 0b4861a28f RAJA::Kernel->RAJA::Teams 2020-09-08 11:28:33 -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
termi-official 9a8258e65e Removed errnous const qualifier. 2020-09-01 01:13:27 +02:00
termi-official 8e02a5f475 Style again. 2020-09-01 01:07:17 +02:00
termi-official 9d8490a4bf Finalize documentation. 2020-09-01 00:52:23 +02:00
termi-official 010a2e2246 Make doxygen happy. 2020-09-01 00:26:38 +02:00
termi-official af958ec46c Updated style. 2020-09-01 00:24:50 +02:00
termi-official 3365b91583 Add a basic unit test for Kelly error indicator. 2020-08-31 23:46:35 +02:00
termi-official 46666d6c40 Merge branch 'master' into kelly-error-indicator-dev 2020-08-31 22:22:33 +02:00
termi-official d21c52603f Since there is no general correct way to compute the scaling coefficient for each element, an option to let the user provide it has been added. 2020-08-31 21:55:56 +02:00
termi-official bdf5a2bdc5 Modified example 15 to showcase the error estimator. 2020-08-24 23:09:09 +02:00
termi-official b319fbb1a6 Base implementation of the Kelly error estimator for scalar finite element problems. 2020-08-24 23:08:40 +02: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
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
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
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
Andrew T. Barker 1f85594b4f WIP: thinking and exploring, this compiles, much to do. 2020-04-27 16:02:58 -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
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
Jan Nikl 287d8ec540 Fixed face element indices for boundary trace face integrators in MixedBilinearForm::Assemble(). 2019-09-10 08:44:51 +02:00
Holger Jones e2ded13c1e add support for vtk high order elements 2019-06-23 18:53:21 -07:00
hillyuan 51cf866693 Merge branch 'master' of https://github.com/hillyuan/mfem 2019-06-02 18:36:52 +09:00
hillyuan 5097dd2e3d Merge branch 'master' of https://github.com/mfem/mfem 2019-06-02 18:26:54 +09:00
hillyuan 2bb926094a Merge branch 'master' of https://github.com/mfem/mfem 2019-05-23 10:03:52 +09:00
hillyuan 05c74af854 return type of METIS_PartGraphKway wrong 2019-05-15 11:36:28 +09:00
hillyuan 13f39b38a5 METIS_PartGraphKway in FindMETIS.cmake may not working due to type error 2019-05-14 23:19:39 +09:00
111 changed files with 17790 additions and 1298 deletions
+36 -2
View File
@@ -11,6 +11,9 @@
Version 4.2.1 (development)
===========================
- Added matrix-free GPU-enabled implementations of GradientInterpolator and
IdentityInterpolator.
- Added interface to MUMPS direct solver. Its usage is demonstrated in ex25p.
See http://mumps.enseeiht.fr/ for more details. Supported versions >= 5.1.1.
@@ -23,17 +26,48 @@ Version 4.2.1 (development)
- Added support for the "BR2" discontinuous Galerkin discretization for
diffusion via DGDiffusionBR2Integrator (see Example 14/14p).
- Generalized the Multigrid class to support non-geometric multigrid. The
previous functionality, based on FiniteElementSpaceHierarchy, is now available
in the derived class GeometricMultigrid.
- Upgraded the Catch unit test framework from version 2.13.0 to version 2.13.2.
- Implemented a filter method for the Navier miniapp to stabilize highly
turbulent flows in direct numerical simulation.
- Added partial assembly and device support to Example 25/25p, with diagonal
- Added support for reading high-order Lagrange meshes in VTK format. Arbitrary-
orders and all element types are supported. See the VTK blog for more info:
https://blog.kitware.com/wp-content/uploads/2018/09/Source_Issue_43.pdf
- Added support for reading VTK meshes in XML format.
- Added partial assembly and device support to Example 25/25p, with diagonal
preconditioning.
- Implemented a variable step-size IMEX (VSSIMEX) method for the Navier miniapp.
- Added new mesh quality metrics for TMOP-based mesh optimization.
- Added new mesh quality metrics and improved the untangling capabilities of the
TMOP-based mesh optimization algorithms.
- Changed the interface for the error estimator.
- Implemented the parallel Kelly error indicator for scalar-valued problems.
- Added new classes DenseSymmetricMatrix and SymmetricMatrixCoefficient for
efficient evaluation of symmetric matrix coefficients. This replaces the now
deprecated EvalSymmetric in MatrixCoefficient. Added DiagonalMatrixCoefficient
for clarity, which is a typedef of VectorCoefficient.
- Added support for AMG preconditioners for non-symmetric systems (e.g.
advection-dominated problems) using hypre's approximate ideal restriction
(AIR) AMG. Requires hypre version 2.14.0 or newer. Usage is illustrated in
example 9/9p.
- Implemented an adaptive linear solver tolerance option for NewtonSolver based
on the algorithm of Eisenstat and Walker.
- Extending support for L2 basis functions using MapTypes VALUE and INTEGRAL in
linear interpolators and GridFunction "GetValue" methods.
Version 4.2, released on October 30, 2020
+2 -1
View File
@@ -9,7 +9,8 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
cmake_minimum_required(VERSION 2.8.11)
# The variable CMAKE_CXX_STANDARD and related were introduced in CMake v3.1
cmake_minimum_required(VERSION 3.1)
set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/config/user.cmake" CACHE PATH
"Path to optional user configuration file.")
+2 -2
View File
@@ -710,10 +710,10 @@ The specific libraries and their options are:
Versions: libCEED >= 0.7.
- RAJA (optional), used when MFEM_USE_RAJA = YES.
Beginning with MFEM v4.1, only RAJA v0.10.0+ is supported.
Beginning with MFEM v4.3, only RAJA v0.13.0+ is supported.
URL: https://github.com/LLNL/RAJA
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
Versions: RAJA >= 0.10.0.
Versions: RAJA >= 0.13.0.
- Umpire, used when MFEM_USE_UMPIRE = YES.
URL: https://github.com/LLNL/Umpire
+1
View File
@@ -29,4 +29,5 @@ license files. These software products and their licenses are as follows:
* 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
* TinyXML2 (general/tinyxml2.{cpp,h}) -- zlib license
* Zstr (general/zstr.hpp) -- MIT license
+7 -7
View File
@@ -25,16 +25,16 @@ mfem_find_package(METIS METIS METIS_DIR "include;Lib" "metis.h"
int main()
{
int n = 10;
int nparts = 5;
int edgecut;
int* partitioning = new int[10];
int* I = partitioning,
idx_t n = 10;
idx_t nparts = 5;
idx_t edgecut;
idx_t* partitioning = new idx_t[10];
idx_t* I = partitioning,
* J = partitioning;
int ncon = 1;
idx_t ncon = 1;
int err;
int options[40];
idx_t options[40];
METIS_SetDefaultOptions(options);
options[10] = 1; // set METIS_OPTION_CONTIG
@@ -754,7 +754,13 @@ function(mfem_export_mk_files)
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
set(MFEM_HOST_CXX ${MFEM_CXX})
set(MFEM_CPPFLAGS "")
string(STRIP "${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
get_target_property(cxx_std mfem CXX_STANDARD)
# For now, we ignore the setting of the CXX_EXTENSIONS property. If this
# property is set, then we need to use a variable like:
# CMAKE_CXX11_EXTENSION_COMPILE_OPTION
set(cxx_std_flag ${CMAKE_CXX${cxx_std}_STANDARD_COMPILE_OPTION})
string(STRIP
"${cxx_std_flag} ${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
MFEM_CXXFLAGS)
set(MFEM_TPLFLAGS "")
foreach(dir ${MFEM_TPL_INCLUDE_DIRS})
+27 -23
View File
@@ -161,7 +161,7 @@ endif
ZLIB_DIR =
ZLIB_OPT = $(if $(ZLIB_DIR),-I$(ZLIB_DIR)/include)
ZLIB_LIB = $(if $(ZLIB_DIR),$(ZLIB_RPATH) -L$(ZLIB_DIR)/lib ,)-lz
ZLIB_RPATH = -Wl,-rpath,$(ZLIB_DIR)/lib
ZLIB_RPATH = $(XLINKER)-rpath,$(ZLIB_DIR)/lib
LIBUNWIND_OPT = -g
LIBUNWIND_LIB = $(if $(NOTMAC),-lunwind -ldl,)
@@ -231,19 +231,21 @@ MESQUITE_LIB = -L$(MESQUITE_DIR)/lib -lmesquite
LIB_RT = $(if $(NOTMAC),-lrt,)
SUITESPARSE_DIR = @MFEM_DIR@/../SuiteSparse
SUITESPARSE_OPT = -I$(SUITESPARSE_DIR)/include
SUITESPARSE_LIB = -Wl,-rpath,$(SUITESPARSE_DIR)/lib -L$(SUITESPARSE_DIR)/lib\
-lklu -lbtf -lumfpack -lcholmod -lcolamd -lamd -lcamd -lccolamd\
-lsuitesparseconfig $(LIB_RT) $(METIS_LIB) $(LAPACK_LIB)
SUITESPARSE_LIB = $(XLINKER)-rpath,$(SUITESPARSE_DIR)/lib\
-L$(SUITESPARSE_DIR)/lib -lklu -lbtf -lumfpack -lcholmod -lcolamd -lamd -lcamd\
-lccolamd -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
SUPERLU_LIB = $(XLINKER)-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
SUPERLU_LIB = $(XLINKER)-rpath,$(SUPERLU_DIR)/lib64 -L$(SUPERLU_DIR)/lib64\
-lsuperlu_dist -lblas
endif
# SCOTCH library configuration (required by STRUMPACK <= v2.1.0, optional in
@@ -269,7 +271,7 @@ MPI_FORTRAN_LIB = -lmpifort
# MUMPS library configuration
MUMPS_DIR = @MFEM_DIR@/../MUMPS_5.2.0
MUMPS_OPT = -I$(MUMPS_DIR)/include
MUMPS_LIB = -Wl,-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib -ldmumps\
MUMPS_LIB = $(XLINKER)-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib -ldmumps\
-lmumps_common -lpord $(SCALAPACK_LIB) $(LAPACK_LIB) $(MPI_FORTRAN_LIB)
# STRUMPACK library configuration
@@ -299,8 +301,8 @@ GNUTLS_LIB = -lgnutls
NETCDF_DIR = $(HOME)/local
HDF5_DIR = $(HOME)/local
NETCDF_OPT = -I$(NETCDF_DIR)/include -I$(HDF5_DIR)/include $(ZLIB_OPT)
NETCDF_LIB = -Wl,-rpath,$(NETCDF_DIR)/lib -L$(NETCDF_DIR)/lib\
-Wl,-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib\
NETCDF_LIB = $(XLINKER)-rpath,$(NETCDF_DIR)/lib -L$(NETCDF_DIR)/lib\
$(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib\
-lnetcdf -lhdf5_hl -lhdf5 $(ZLIB_LIB)
# PETSc library configuration (version greater or equal to 3.8 or the dev branch)
@@ -312,9 +314,10 @@ PETSC_INC_VAR = PETSC_CC_INCLUDES
PETSC_LIB_VAR = PETSC_EXTERNAL_LIB_BASIC
ifeq ($(PETSC_FOUND),YES)
PETSC_OPT := $(shell sed -n "s/$(PETSC_INC_VAR) = *//p" $(PETSC_VARS))
PETSC_LIB := $(shell sed -n "s/$(PETSC_LIB_VAR) = *//p" $(PETSC_VARS))
PETSC_LIB := -Wl,-rpath,$(abspath $(PETSC_DIR))/lib\
-L$(abspath $(PETSC_DIR))/lib -lpetsc $(PETSC_LIB)
PETSC_DEP := $(shell sed -n "s/$(PETSC_LIB_VAR) = *//p" $(PETSC_VARS))
PETSC_LIB = $(XLINKER)-rpath,$(abspath $(PETSC_DIR))/lib\
-L$(abspath $(PETSC_DIR))/lib -lpetsc\
$(subst $(CXX_XLINKER),$(XLINKER),$(PETSC_DEP))
endif
SLEPC_DIR := $(MFEM_DIR)/../slepc
@@ -326,9 +329,10 @@ ifeq ($(SLEPC_FOUND),YES)
SLEPC_OPT := $(shell sed -n "s/$(SLEPC_INC_VAR) *= *//p" $(SLEPC_VARS))
# Some additional external libraries might be defined in this file
-include ${SLEPC_DIR}/${PETSC_ARCH}/lib/slepc/conf/slepcvariables
SLEPC_LIB := $(shell sed -n "s/$(SLEPC_LIB_VAR) *= *//p" $(SLEPC_VARS))
SLEPC_LIB := -Wl,-rpath,$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib\
-L$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib -lslepc $(SLEPC_LIB)
SLEPC_DEP := $(shell sed -n "s/$(SLEPC_LIB_VAR) *= *//p" $(SLEPC_VARS))
SLEPC_LIB = $(XLINKER)-rpath,$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib\
-L$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib -lslepc\
$(subst $(CXX_XLINKER),$(XLINKER),$(SLEPC_DEP))
endif
# MPFR library configuration
@@ -339,7 +343,7 @@ MPFR_LIB = -lmpfr
CONDUIT_DIR = @MFEM_DIR@/../conduit
CONDUIT_OPT = -I$(CONDUIT_DIR)/include/conduit
CONDUIT_LIB = \
-Wl,-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
$(XLINKER)-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
-lconduit -lconduit_relay -lconduit_blueprint -ldl
# Check if Conduit was built with hdf5 support, by looking
@@ -347,7 +351,7 @@ CONDUIT_LIB = \
CONDUIT_HDF5_HEADER=$(CONDUIT_DIR)/include/conduit/conduit_relay_hdf5.hpp
ifneq (,$(wildcard $(CONDUIT_HDF5_HEADER)))
CONDUIT_OPT += -I$(HDF5_DIR)/include
CONDUIT_LIB += -Wl,-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
CONDUIT_LIB += $(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
-lhdf5 $(ZLIB_LIB)
endif
@@ -357,9 +361,9 @@ SIDRE_DIR = @MFEM_DIR@/../axom
SIDRE_OPT = -I$(SIDRE_DIR)/include -I$(CONDUIT_DIR)/include/conduit\
-I$(HDF5_DIR)/include
SIDRE_LIB = \
-Wl,-rpath,$(SIDRE_DIR)/lib -L$(SIDRE_DIR)/lib \
-Wl,-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
-Wl,-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
$(XLINKER)-rpath,$(SIDRE_DIR)/lib -L$(SIDRE_DIR)/lib \
$(XLINKER)-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
$(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
-laxom -lconduit -lconduit_relay -lconduit_blueprint -lhdf5 $(ZLIB_LIB) -ldl
# PUMI
@@ -415,9 +419,9 @@ 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
MKL_CPARDISO_LIB = $(XLINKER)-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
# If YES, enable some informational messages
VERBOSE = NO
+3 -1
View File
@@ -819,7 +819,9 @@ RECURSIVE = NO
# run.
EXCLUDE = @MFEM_SOURCE_DIR@/config/_config.hpp \
@MFEM_SOURCE_DIR@/config/get_hypre_version.cpp
@MFEM_SOURCE_DIR@/config/get_hypre_version.cpp \
@MFEM_SOURCE_DIR@/general/tinyxml2.h \
@MFEM_SOURCE_DIR@/general/tinyxml2.cpp
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
+1
View File
@@ -34,6 +34,7 @@ list(APPEND ALL_EXE_SRCS
ex25.cpp
ex26.cpp
ex27.cpp
ex91.cpp
)
if (MFEM_USE_MPI)
+4
View File
@@ -205,10 +205,14 @@ int main(int argc, char *argv[])
if (amgx_solver)
{
amgx.SetConvergenceCheck(true);
amgx.Mult(B,X);
}
else
{
// Omit convergence check at the AmgX level when using as a
// preconditioner.
amgx.SetConvergenceCheck(false);
PCG(*A.As<SparseMatrix>(), amgx, B, X, 3, 40, 1e-12, 0.0);
}
}
+1
View File
@@ -264,6 +264,7 @@ int main(int argc, char *argv[])
}
amgx.SetOperator(*A.As<HypreParMatrix>());
amgx.SetConvergenceCheck(true);
amgx.Mult(B, X);
// Release MPI communicators and resources created by AmgX
+1
View File
@@ -30,6 +30,7 @@
// Device sample runs:
// ex1 -pa -d cuda
// ex1 -pa -d raja-cuda
// * ex1 -pa -d raja-hip
// ex1 -pa -d occa-cuda
// ex1 -pa -d raja-omp
// ex1 -pa -d occa-omp
+5
View File
@@ -178,6 +178,7 @@ int main(int argc, char *argv[])
double visc = 1e-2;
double mu = 0.25;
double K = 5.0;
bool adaptive_lin_rtol = true;
bool visualization = true;
int vis_steps = 1;
@@ -206,6 +207,9 @@ int main(int argc, char *argv[])
"Shear modulus in the Neo-Hookean hyperelastic model.");
args.AddOption(&K, "-K", "--bulk-modulus",
"Bulk modulus in the Neo-Hookean hyperelastic model.");
args.AddOption(&adaptive_lin_rtol, "-alrtol", "--adaptive-lin-rtol",
"-no-alrtol", "--no-adaptive-lin-rtol",
"Enable or disable adaptive linear solver rtol.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -574,6 +578,7 @@ HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
newton_solver.SetPrintLevel(1); // print Newton iterations
newton_solver.SetRelTol(rel_tol);
newton_solver.SetAbsTol(0.0);
newton_solver.SetAdaptiveLinRtol(2, 0.5, 0.9);
newton_solver.SetMaxIter(10);
}
+50 -8
View File
@@ -19,6 +19,13 @@
// mpirun -np 4 ex15p -m ../data/square-disc.mesh
// mpirun -np 4 ex15p -m ../data/escher.mesh -r 2 -tf 0.3
//
// Different estimators:
//
// mpirun -np 4 ex15p -est 0 -e 1e-4
// mpirun -np 4 ex15p -est 1 -e 1e-6
// mpirun -np 4 ex15p -est 1 -o 3 -tf 0.3
// mpirun -np 4 ex15p -est 2 -o 2
//
// Description: Building on Example 6, this example demonstrates dynamic AMR.
// The mesh is adapted to a time-dependent solution by refinement
// as well as by derefinement. For simplicity, the solution is
@@ -28,8 +35,11 @@
// At each outer iteration the right hand side function is changed
// to mimic a time dependent problem. Within each inner iteration
// the problem is solved on a sequence of meshes which are locally
// refined according to a simple ZZ error estimator. At the end
// of the inner iteration the error estimates are also used to
// refined according to a chosen error estimator. Currently there
// are three error estimators supported: A L2 formulation of the
// Zienkiewicz-Zhu error estimator (0), a Kelly error indicator (1)
// and a traditional Zienkiewicz-Zhu error estimator (2). At the
// end of the inner iteration the error estimates are also used to
// identify any elements which may be over-refined and a single
// derefinement step is performed. After each refinement or
// derefinement step a rebalance operation is performed to keep
@@ -87,6 +97,7 @@ int main(int argc, char *argv[])
int nc_limit = 3; // maximum level of hanging nodes
bool visualization = true;
bool visit = false;
int which_estimator = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -107,6 +118,9 @@ int main(int argc, char *argv[])
"Maximum level of hanging nodes.");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&which_estimator, "-est", "--estimator",
"Which estimator to use: "
"0 = L2ZZ, 1 = Kelly, 2 = ZZ. Defaults to L2ZZ.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -214,17 +228,43 @@ int main(int argc, char *argv[])
// provide the method ComputeElementFlux. We supply an L2 space for the
// discontinuous flux and an H(div) space for the smoothed flux.
L2_FECollection flux_fec(order, dim);
ParFiniteElementSpace flux_fes(&pmesh, &flux_fec, sdim);
RT_FECollection smooth_flux_fec(order-1, dim);
ParFiniteElementSpace smooth_flux_fes(&pmesh, &smooth_flux_fec);
L2ZienkiewiczZhuEstimator estimator(*integ, x, flux_fes, smooth_flux_fes);
ErrorEstimator* estimator;
switch (which_estimator)
{
case 1:
{
auto flux_fes = new ParFiniteElementSpace(&pmesh, &flux_fec, sdim);
estimator = new KellyErrorEstimator(*integ, x, flux_fes);
break;
}
case 2:
{
auto flux_fes = new ParFiniteElementSpace(&pmesh, &fec, sdim);
estimator = new ZienkiewiczZhuEstimator(*integ, x, flux_fes);
break;
}
default:
if (myid == 0)
{
std::cout << "Unkown estimator. Falling back to L2ZZ." << std::endl;
}
case 0:
{
auto flux_fes = new ParFiniteElementSpace(&pmesh, &flux_fec, sdim);
auto smooth_flux_fes = new ParFiniteElementSpace(&pmesh, &smooth_flux_fec);
estimator = new L2ZienkiewiczZhuEstimator(*integ, x, flux_fes, smooth_flux_fes);
break;
}
}
// 11. As in Example 6p, we also need a refiner. This time the refinement
// strategy is based on a fixed threshold that is applied locally to each
// element. The global threshold is turned off by setting the total error
// fraction to zero. We also enforce a maximum refinement ratio between
// adjacent elements.
ThresholdRefiner refiner(estimator);
ThresholdRefiner refiner(*estimator);
refiner.SetTotalErrorFraction(0.0); // use purely local threshold
refiner.SetLocalErrorGoal(max_elem_error);
refiner.PreferConformingRefinement();
@@ -233,7 +273,7 @@ int main(int argc, char *argv[])
// 12. A derefiner selects groups of elements that can be coarsened to form
// a larger element. A conservative enough threshold needs to be set to
// prevent derefining elements that would immediately be refined again.
ThresholdDerefiner derefiner(estimator);
ThresholdDerefiner derefiner(*estimator);
derefiner.SetThreshold(hysteresis * max_elem_error);
derefiner.SetNCLimit(nc_limit);
@@ -316,7 +356,7 @@ int main(int argc, char *argv[])
refiner.Apply(pmesh);
if (myid == 0)
{
cout << ", total error: " << estimator.GetTotalError() << endl;
cout << ", total error: " << estimator->GetTotalError() << endl;
}
// 21. Quit the AMR loop if the termination criterion has been met
@@ -346,6 +386,8 @@ int main(int argc, char *argv[])
}
}
delete estimator;
// 25. Exit
MPI_Finalize();
return 0;
+2 -2
View File
@@ -40,7 +40,7 @@ using namespace mfem;
// in the FiniteElementSpaceHierarchy. The preconditioner uses a CG solver on
// the coarsest level and second order Chebyshev accelerated smoothers on the
// other levels.
class DiffusionMultigrid : public Multigrid
class DiffusionMultigrid : public GeometricMultigrid
{
private:
ConstantCoefficient one;
@@ -49,7 +49,7 @@ public:
// Constructs a diffusion multigrid for the given FiniteElementSpaceHierarchy
// and the array of essential boundaries
DiffusionMultigrid(FiniteElementSpaceHierarchy& fespaces, Array<int>& ess_bdr)
: Multigrid(fespaces), one(1.0)
: GeometricMultigrid(fespaces), one(1.0)
{
ConstructCoarseOperatorAndSolver(fespaces.GetFESpaceAtLevel(0), ess_bdr);
+2 -2
View File
@@ -37,7 +37,7 @@ using namespace mfem;
// all spaces except the coarsest one in the ParFiniteElementSpaceHierarchy.
// The multigrid uses a PCG solver preconditioned with AMG on the coarsest level
// and second order Chebyshev accelerated smoothers on the other levels.
class DiffusionMultigrid : public Multigrid
class DiffusionMultigrid : public GeometricMultigrid
{
private:
ConstantCoefficient one;
@@ -48,7 +48,7 @@ public:
// and the array of essential boundaries
DiffusionMultigrid(ParFiniteElementSpaceHierarchy& fespaces,
Array<int>& ess_bdr)
: Multigrid(fespaces), one(1.0)
: GeometricMultigrid(fespaces), one(1.0)
{
ConstructCoarseOperatorAndSolver(fespaces.GetFESpaceAtLevel(0), ess_bdr);
+5 -4
View File
@@ -290,15 +290,16 @@ int main(int argc, char *argv[])
k.SetAssemblyLevel(AssemblyLevel::FULL);
}
m.AddDomainIntegrator(new MassIntegrator);
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
constexpr double alpha = -1.0;
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, alpha));
k.AddInteriorFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
new NonconservativeDGTraceIntegrator(velocity, alpha));
k.AddBdrFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
new NonconservativeDGTraceIntegrator(velocity, alpha));
LinearForm b(&fes);
b.AddBdrFaceIntegrator(
new BoundaryFlowIntegrator(inflow, velocity, -1.0, -0.5));
new BoundaryFlowIntegrator(inflow, velocity, alpha));
m.Assemble();
int skip_zeros = 0;
+672
View File
@@ -0,0 +1,672 @@
#include"mfem.hpp"
#include<memory>
#include<iostream>
#include<fstream>
namespace mfem {
class LinDiffQFunc
{
public:
LinDiffQFunc(mfem::Coefficient& dd, mfem::Coefficient& ll, double gg_,
double pp0_, double pp1_):diff(dd),load(ll),gg(gg_),pp0(pp0_),pp1(pp1_)
{
}
double QEnergy(ElementTransformation &T,
const IntegrationPoint &ip,
mfem::Vector& param, mfem::Vector& uu)
{
double dd=diff.Eval(T,ip);
double ll=load.Eval(T,ip);
double rho0=param[0];
double rho1=param[1];
double fd=dd*std::pow(rho0,pp0)*std::pow(rho1,pp1);
double rez = 0.5*(uu[0]*uu[0]+uu[1]*uu[1]+uu[2]*uu[2])*fd
+ 0.5*gg*uu[3]*uu[3] -uu[3]*ll;
return rez;
}
void QResidual(ElementTransformation &T,
const IntegrationPoint &ip,
mfem::Vector& param, mfem::Vector& uu, mfem::Vector& rr)
{
rr.SetSize(4);
double dd=diff.Eval(T,ip);
double ll=load.Eval(T,ip);
double rho0=param[0];
double rho1=param[1];
double fd=dd*std::pow(rho0,pp0)*std::pow(rho1,pp1);
rr[0]=uu[0]*fd;
rr[1]=uu[1]*fd;
rr[2]=uu[2]*fd;
rr[3]=gg*uu[3]-ll;
}
void AQResidual(ElementTransformation &T,
const IntegrationPoint &ip,
mfem::Vector& param,
mfem::Vector& uu, mfem::Vector& aa, mfem::Vector& rr)
{
rr.SetSize(2);
double dd=diff.Eval(T,ip);
double ll=load.Eval(T,ip);
double rho0=param[0];
double rho1=param[1];
double fd0=dd*pp0*std::pow(rho0,pp0-1.0)*std::pow(rho1,pp1);
double fd1=dd*std::pow(rho0,pp0)*pp1*std::pow(rho1,pp1-1.0);
rr[0] = (aa[0]*uu[0]+aa[1]*uu[1]+aa[2]*uu[2])*fd0;
rr[1] = (aa[0]*uu[0]+aa[1]*uu[1]+aa[2]*uu[2])*fd1;
}
void QGradResidual(ElementTransformation &T,
const IntegrationPoint &ip,
mfem::Vector& param, mfem::Vector& uu, mfem::DenseMatrix& hh)
{
hh.SetSize(4);
double dd=diff.Eval(T,ip);
//double ll=load.Eval(T,ip);
double rho0=param[0];
double rho1=param[1];
double fd=dd*std::pow(rho0,pp0)*std::pow(rho1,pp1);
hh=0.0;
hh(0,0)=fd;
hh(1,1)=fd;
hh(2,2)=fd;
hh(3,3)=gg;
}
private:
mfem::Coefficient& diff;
mfem::Coefficient& load;
double gg;
double pp0;
double pp1;
};
class PrmBlockLSFEMDiffusion: public PrmBlockNonlinearFormIntegrator
{
public:
PrmBlockLSFEMDiffusion(LinDiffQFunc& qfun_)
{
qfunc=&qfun_;
}
/// Compute the local energy
virtual double GetElementEnergy(const Array<const FiniteElement *>&el,
const Array<const FiniteElement *>&pel,
ElementTransformation &Tr,
const Array<const Vector *>&elfun,
const Array<const Vector *>&pelfun)
{
int dof_u0 = el[0]->GetDof();
int dof_r0 = pel[0]->GetDof();
int dof_r1 = pel[1]->GetDof();
int dim = el[0]->GetDim();
int spaceDim = Tr.GetSpaceDim();
if (dim != spaceDim)
{
mfem::mfem_error(" PrmBlockLSFEMDiffusion::GetElementEnergy"
" is not defined on manifold meshes");
}
//shape functions
Vector shu0(dof_u0);
Vector shr0(dof_r0);
Vector shr1(dof_r1);
DenseMatrix dsu0(dof_u0,dim);
DenseMatrix B(dof_u0, 4);
B=0.0;
double w;
Vector param(2); param=0.0;
Vector uu(4); uu=0.0;
double energy =0.0;
const IntegrationRule *ir = nullptr;
if(ir==nullptr){
int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0])
+pel[0]->GetOrder()+pel[1]->GetOrder();
ir=&IntRules.Get(Tr.GetGeometryType(),order);
}
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint(&ip);
w=Tr.Weight();
w = ip.weight * w;
el[0]->CalcPhysDShape(Tr,dsu0);
el[0]->CalcPhysShape(Tr,shu0);
pel[0]->CalcPhysShape(Tr,shr0);
pel[1]->CalcPhysShape(Tr,shr1);
param[0]=shr0*(*pelfun[0]);
param[1]=shr1*(*pelfun[1]);
//set the matrix B
for(int jj=0;jj<dim;jj++)
{
B.SetCol(jj,dsu0.GetColumn(jj));
}
B.SetCol(3,shu0);
B.MultTranspose(*elfun[0],uu);
energy=energy+w * qfunc->QEnergy(Tr,ip,param,uu);
}
return energy;
}
/// Perform the local action of the BlockNonlinearFormIntegrator
virtual void AssembleElementVector(const Array<const FiniteElement *> &el,
const Array<const FiniteElement *>&pel,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *>&pelfun,
const Array<Vector *> &elvec)
{
int dof_u0 = el[0]->GetDof();
int dof_r0 = pel[0]->GetDof();
int dof_r1 = pel[1]->GetDof();
int dim = el[0]->GetDim();
elvec[0]->SetSize(dof_u0);
*elvec[0]=0.0;
int spaceDim = Tr.GetSpaceDim();
if (dim != spaceDim)
{
mfem::mfem_error(" PrmBlockLSFEMDiffusion::AssembleElementVector"
" is not defined on manifold meshes");
}
//shape functions
Vector shu0(dof_u0);
Vector shr0(dof_r0);
Vector shr1(dof_r1);
DenseMatrix dsu0(dof_u0,dim);
DenseMatrix B(dof_u0, 4);
B=0.0;
double w;
Vector param(2); param=0.0;
Vector uu(4); uu=0.0;
Vector rr;
Vector lvec; lvec.SetSize(dof_u0);
const IntegrationRule *ir = nullptr;
if(ir==nullptr){
int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0])
+pel[0]->GetOrder()+pel[1]->GetOrder();
ir=&IntRules.Get(Tr.GetGeometryType(),order);
}
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint(&ip);
w=Tr.Weight();
w = ip.weight * w;
el[0]->CalcPhysDShape(Tr,dsu0);
el[0]->CalcPhysShape(Tr,shu0);
pel[0]->CalcPhysShape(Tr,shr0);
pel[1]->CalcPhysShape(Tr,shr1);
param[0]=shr0*(*pelfun[0]);
param[1]=shr1*(*pelfun[1]);
//set the matrix B
for(int jj=0;jj<dim;jj++)
{
B.SetCol(jj,dsu0.GetColumn(jj));
}
B.SetCol(3,shu0);
B.MultTranspose(*elfun[0],uu);
qfunc->QResidual(Tr,ip,param, uu, rr);
B.Mult(rr,lvec);
elvec[0]->Add(w,lvec);
}
}
virtual void AssembleFaceVector(const Array<const FiniteElement *> &el1,
const Array<const FiniteElement *> &el2,
const Array<const FiniteElement *> &pel1,
const Array<const FiniteElement *> &pel2,
FaceElementTransformations &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *>&pelfun,
const Array<Vector *> &elvect)
{
}
/// Assemble the local gradient matrix
virtual void AssembleElementGrad(const Array<const FiniteElement*> &el,
const Array<const FiniteElement *>&pel,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *>&pelfun,
const Array2D<DenseMatrix *> &elmats)
{
int dof_u0 = el[0]->GetDof();
int dof_r0 = pel[0]->GetDof();
int dof_r1 = pel[1]->GetDof();
int dim = el[0]->GetDim();
//elmats[0]->Size(dof_u0, dof_u0);
//*elmats[0]=0.0;
DenseMatrix* K=elmats(0,0);
K->SetSize(dof_u0,dof_u0);
(*K)=0.0;
int spaceDim = Tr.GetSpaceDim();
if (dim != spaceDim)
{
mfem::mfem_error(" PrmBlockLSFEMDiffusion::AssembleElementVector"
" is not defined on manifold meshes");
}
//shape functions
Vector shu0(dof_u0);
Vector shr0(dof_r0);
Vector shr1(dof_r1);
DenseMatrix dsu0(dof_u0,dim);
DenseMatrix B(dof_u0, 4);
DenseMatrix A(dof_u0, 4);
B=0.0;
double w;
Vector param(2); param=0.0;
Vector uu(4); uu=0.0;
DenseMatrix hh;
Vector lvec; lvec.SetSize(dof_u0);
const IntegrationRule *ir = nullptr;
if(ir==nullptr){
int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0])
+pel[0]->GetOrder()+pel[1]->GetOrder();
ir=&IntRules.Get(Tr.GetGeometryType(),order);
}
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint(&ip);
w = Tr.Weight();
w = ip.weight * w;
el[0]->CalcPhysDShape(Tr,dsu0);
el[0]->CalcPhysShape(Tr,shu0);
pel[0]->CalcPhysShape(Tr,shr0);
pel[1]->CalcPhysShape(Tr,shr1);
param[0]=shr0*(*pelfun[0]);
param[1]=shr1*(*pelfun[1]);
//set the matrix B
for(int jj=0;jj<dim;jj++)
{
B.SetCol(jj,dsu0.GetColumn(jj));
}
B.SetCol(3,shu0);
B.MultTranspose(*elfun[0],uu);
qfunc->QGradResidual(Tr,ip,param,uu,hh);
Mult(B,hh,A);
AddMult_a_ABt(w,A,B,*K);
}
}
virtual void AssembleFaceGrad(const Array<const FiniteElement *>&el1,
const Array<const FiniteElement *>&el2,
const Array<const FiniteElement *> &pel1,
const Array<const FiniteElement *> &pel2,
FaceElementTransformations &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *>&pelfun,
const Array2D<DenseMatrix *> &elmats)
{
}
virtual void AssemblePrmElementVector(const Array<const FiniteElement *> &el,
const Array<const FiniteElement *> &pel,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *> &alfun,
const Array<const Vector *> &pelfun,
const Array<Vector *> &elvec)
{
int dof_u0 = el[0]->GetDof();
int dof_r0 = pel[0]->GetDof();
int dof_r1 = pel[1]->GetDof();
int dim = el[0]->GetDim();
Vector& e0 = *(elvec[0]);
Vector& e1 = *(elvec[1]);
e0.SetSize(dof_r0);
e0=0.0;
e1.SetSize(dof_r1);
e1=0.0;
int spaceDim = Tr.GetSpaceDim();
if (dim != spaceDim)
{
mfem::mfem_error(" PrmBlockLSFEMDiffusion::AssembleElementVector"
" is not defined on manifold meshes");
}
//shape functions
Vector shu0(dof_u0);
Vector shr0(dof_r0);
Vector shr1(dof_r1);
DenseMatrix dsu0(dof_u0,dim);
DenseMatrix B(dof_u0, 4);
B=0.0;
double w;
Vector param(2); param=0.0;
Vector uu(4); uu=0.0;
Vector aa(4); aa=0.0;
Vector rr;
Vector lvec0; lvec0.SetSize(dof_r0);
Vector lvec1; lvec1.SetSize(dof_r1);
const IntegrationRule *ir = nullptr;
if(ir==nullptr){
int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0])
+pel[0]->GetOrder()+pel[1]->GetOrder();
ir=&IntRules.Get(Tr.GetGeometryType(),order);
}
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint(&ip);
w=Tr.Weight();
w = ip.weight * w;
el[0]->CalcPhysDShape(Tr,dsu0);
el[0]->CalcPhysShape(Tr,shu0);
pel[0]->CalcPhysShape(Tr,shr0);
pel[1]->CalcPhysShape(Tr,shr1);
param[0]=shr0*(*pelfun[0]);
param[1]=shr1*(*pelfun[1]);
//set the matrix B
for(int jj=0;jj<dim;jj++)
{
B.SetCol(jj,dsu0.GetColumn(jj));
}
B.SetCol(3,shu0);
B.MultTranspose(*elfun[0],uu);
B.MultTranspose(*alfun[0],aa);
qfunc->AQResidual(Tr, ip, param, uu, aa, rr);
lvec0=shr0;
lvec0*=rr[0];
lvec1=shr1;
lvec1*=rr[1];
e0.Add(w,lvec0);
e1.Add(w,lvec1);
}
}
virtual void AssemblePrmFaceVector(const Array<const FiniteElement *> &el1,
const Array<const FiniteElement *> &el2,
const Array<const FiniteElement *> &pel1,
const Array<const FiniteElement *> &pel2,
FaceElementTransformations &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *> &alfun,
const Array<const Vector *> &pelfun,
const Array<Vector *> &elvect)
{
}
private:
LinDiffQFunc* qfunc;
};
}
int main(int argc, char *argv[])
{
const char *mesh_file = "../../data/beam-tet.mesh";
int ser_ref_levels = 1;
int order = 2;
bool visualization = true;
double newton_rel_tol = 1e-4;
double newton_abs_tol = 1e-6;
int newton_iter = 10;
int print_level = 0;
mfem::OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&ser_ref_levels,
"-rs",
"--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&order,
"-o",
"--order",
"Order (degree) of the finite elements.");
args.AddOption(&visualization,
"-vis",
"--visualization",
"-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&newton_rel_tol,
"-rel",
"--relative-tolerance",
"Relative tolerance for the Newton solve.");
args.AddOption(&newton_abs_tol,
"-abs",
"--absolute-tolerance",
"Absolute tolerance for the Newton solve.");
args.AddOption(&newton_iter,
"-it",
"--newton-iterations",
"Maximum iterations for the Newton solve.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(std::cout);
return 1;
}
args.PrintOptions(std::cout);
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral and hexahedral meshes
// with the same code.
mfem::Mesh *mesh = new mfem::Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Refine the mesh in serial to increase the resolution. In this example
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
// a command-line parameter.
for (int lev = 0; lev < ser_ref_levels; lev++)
{
mesh->UniformRefinement();
}
/// Define the q-function
mfem::ConstantCoefficient* dc=new mfem::ConstantCoefficient(1.0);
mfem::ConstantCoefficient* lc=new mfem::ConstantCoefficient(1.0);
mfem::LinDiffQFunc* qfun=new mfem::LinDiffQFunc(*dc,*lc,1.0,1.0,1.0);
mfem::H1_FECollection fec00(order, dim);
mfem::L2_FECollection fec01(order, dim);
mfem::FiniteElementSpace* bfes00=new mfem::FiniteElementSpace(mesh,&fec00,1,mfem::Ordering::byVDIM);
mfem::FiniteElementSpace* pfes00=new mfem::FiniteElementSpace(mesh,&fec00,1,mfem::Ordering::byVDIM);
mfem::FiniteElementSpace* pfes01=new mfem::FiniteElementSpace(mesh,&fec01,1,mfem::Ordering::byVDIM);
/// Define parametric nonlinear form
mfem::Array<mfem::FiniteElementSpace*> bfes;
mfem::Array<mfem::FiniteElementSpace*> pfes;
bfes.Append(bfes00);
pfes.Append(pfes00);
pfes.Append(pfes01);
mfem::PrmBlockNonlinearForm* nf=new mfem::PrmBlockNonlinearForm(bfes,pfes);
nf->AddDomainIntegrator(new mfem::PrmBlockLSFEMDiffusion(*qfun));
/// Define the grid functions
mfem::GridFunction* bgf00=new mfem::GridFunction(bfes00);
mfem::GridFunction* pgf00=new mfem::GridFunction(pfes00);
mfem::GridFunction* pgf01=new mfem::GridFunction(pfes01);
mfem::GridFunction* ggf00=new mfem::GridFunction(pfes00);
mfem::GridFunction* ggf01=new mfem::GridFunction(pfes01);
*bgf00=0.0;
*pgf00=1.0;
*pgf01=1.0;
mfem::BlockVector solbv; solbv.Update(nf->GetBlockTrueOffsets()); solbv=0.0;
mfem::BlockVector resbv; resbv.Update(nf->GetBlockTrueOffsets()); resbv=0.0;
mfem::BlockVector adjbv; adjbv.Update(nf->GetBlockTrueOffsets()); adjbv=0.0;
mfem::BlockVector prmbv; prmbv.Update(nf->PrmGetBlockTrueOffsets()); prmbv=1.0;
mfem::BlockVector grdbv; grdbv.Update(nf->PrmGetBlockTrueOffsets()); grdbv=0.0;
bgf00->SetFromTrueDofs(solbv.GetBlock(0));
pgf00->SetFromTrueDofs(prmbv.GetBlock(0));
pgf01->SetFromTrueDofs(prmbv.GetBlock(1));
nf->SetPrmFields(prmbv);
double energy = nf->GetEnergy(solbv);
nf->Mult(solbv,resbv);
std::cout<<"Norm res="<<resbv.Norml2()<<std::endl;
//mfem::Operator& K=nf->GetGradient(solbv);
std::cout<<"energy ="<< energy<<std::endl;
nf->SetStateFields(solbv);
nf->SetAdjointFields(adjbv);
nf->PrmMult(prmbv,grdbv);
//set the BC for the physics
mfem::Array<mfem::Array<int> *> ess_bdr;
mfem::Array<mfem::Vector*> ess_rhs;
ess_bdr.Append(new mfem::Array<int>(mesh->bdr_attributes.Max()));
ess_rhs.Append(nullptr);
(*ess_bdr[0]) = 1;
nf->SetEssentialBC(ess_bdr,ess_rhs);
//define the solvers
mfem::UMFPackSolver* umfsolv=new mfem::UMFPackSolver();
mfem::GMRESSolver *gmres;
gmres = new mfem::GMRESSolver();
gmres->SetAbsTol(newton_abs_tol/10);
gmres->SetRelTol(newton_rel_tol/10);
gmres->SetMaxIter(100);
gmres->SetPrintLevel(print_level);
//gmres->SetPreconditioner(*prec);
mfem::NewtonSolver *ns;
ns = new mfem::NewtonSolver();
ns->iterative_mode = true;
ns->SetSolver(*gmres);
ns->SetOperator(*nf);
ns->SetPrintLevel(print_level);
ns->SetRelTol(newton_rel_tol);
ns->SetAbsTol(newton_abs_tol);
ns->SetMaxIter(newton_iter);
mfem::Vector b; //RHS is zero
solbv=0.0;
ns->Mult(b, solbv);
nf->SetStateFields(solbv);
nf->SetAdjointFields(solbv);
nf->PrmMult(prmbv,grdbv);
mfem::ParaViewDataCollection *dacol = new mfem::ParaViewDataCollection("Example91",
mesh);
ggf00->SetFromTrueDofs(grdbv.GetBlock(0));
ggf01->SetFromTrueDofs(grdbv.GetBlock(1));
pgf00->SetFromTrueDofs(solbv.GetBlock(0));
dacol->SetLevelsOfDetail(order);
dacol->RegisterField("sol", pgf00);
dacol->RegisterField("grad00", ggf00);
dacol->RegisterField("grad01", ggf01);
dacol->SetTime(1.0);
dacol->SetCycle(1);
dacol->Save();
delete dacol;
delete ns;
delete umfsolv;
delete gmres;
delete ess_bdr[0];
delete bgf00;
delete pgf00;
delete pgf01;
delete ggf00;
delete ggf01;
delete nf;
delete pfes01;
delete pfes00;
delete bfes00;
delete qfun;
delete lc;
delete dc;
delete mesh;
}
+114 -32
View File
@@ -64,6 +64,66 @@ double inflow_function(const Vector &x);
// Mesh bounding box
Vector bb_min, bb_max;
// Type of preconditioner for implicit time integrator
enum class PrecType : int
{
ILU = 0,
AIR = 1
};
#if MFEM_HYPRE_VERSION >= 21800
// Algebraic multigrid preconditioner for advective problems based on
// approximate ideal restriction (AIR). Most effective when matrix is
// first scaled by DG block inverse, and AIR applied to scaled matrix.
// See https://doi.org/10.1137/17M1144350.
class AIR_prec : public Solver
{
private:
const HypreParMatrix *A;
// Copy of A scaled by block-diagonal inverse
HypreParMatrix A_s;
HypreBoomerAMG *AIR_solver;
int blocksize;
public:
AIR_prec(int blocksize_) : AIR_solver(NULL), blocksize(blocksize_) { }
void SetOperator(const Operator &op)
{
width = op.Width();
height = op.Height();
A = dynamic_cast<const HypreParMatrix *>(&op);
MFEM_VERIFY(A != NULL, "AIR_prec requires a HypreParMatrix.")
// Scale A by block-diagonal inverse
BlockInverseScale(A, &A_s, NULL, NULL, blocksize,
BlockInverseScaleJob::MATRIX_ONLY);
delete AIR_solver;
AIR_solver = new HypreBoomerAMG(A_s);
AIR_solver->SetAdvectiveOptions(1, "", "FA");
AIR_solver->SetPrintLevel(0);
AIR_solver->SetMaxLevels(50);
}
virtual void Mult(const Vector &x, Vector &y) const
{
// Scale the rhs by block inverse and solve system
HypreParVector z_s;
BlockInverseScale(A, NULL, &x, &z_s, blocksize,
BlockInverseScaleJob::RHS_ONLY);
AIR_solver->Mult(z_s, y);
}
~AIR_prec()
{
delete AIR_solver;
}
};
#endif
class DG_Solver : public Solver
{
private:
@@ -71,24 +131,37 @@ private:
SparseMatrix M_diag;
HypreParMatrix *A;
GMRESSolver linear_solver;
BlockILU prec;
Solver *prec;
double dt;
public:
DG_Solver(HypreParMatrix &M_, HypreParMatrix &K_, const FiniteElementSpace &fes)
DG_Solver(HypreParMatrix &M_, HypreParMatrix &K_, const FiniteElementSpace &fes,
PrecType prec_type)
: M(M_),
K(K_),
A(NULL),
linear_solver(M.GetComm()),
prec(fes.GetFE(0)->GetDof(),
BlockILU::Reordering::MINIMUM_DISCARDED_FILL),
dt(-1.0)
{
int block_size = fes.GetFE(0)->GetDof();
if (prec_type == PrecType::ILU)
{
prec = new BlockILU(block_size,
BlockILU::Reordering::MINIMUM_DISCARDED_FILL);
}
else if (prec_type == PrecType::AIR)
{
#if MFEM_HYPRE_VERSION >= 21800
prec = new AIR_prec(block_size);
#else
MFEM_ABORT("Must have MFEM_HYPRE_VERSION >= 21800 to use AIR.\n");
#endif
}
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);
linear_solver.SetPreconditioner(*prec);
M.GetDiag(M_diag);
}
@@ -121,10 +194,12 @@ public:
~DG_Solver()
{
delete prec;
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
and advection matrices, and b describes the flow on the boundary. This can
@@ -142,7 +217,8 @@ private:
mutable Vector z;
public:
FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K, const Vector &_b);
FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K, const Vector &_b,
PrecType prec_type);
virtual void Mult(const Vector &x, Vector &y) const;
virtual void ImplicitSolve(const double dt, const Vector &x, Vector &k);
@@ -154,10 +230,9 @@ public:
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);
MPI_Session mpi;
int num_procs = mpi.WorldSize();
int myid = mpi.WorldRank();
// 2. Parse command-line options.
problem = 0;
@@ -178,7 +253,11 @@ int main(int argc, char *argv[])
bool adios2 = false;
bool binary = false;
int vis_steps = 5;
#if MFEM_HYPRE_VERSION >= 21800
PrecType prec_type = PrecType::AIR;
#else
PrecType prec_type = PrecType::ILU;
#endif
int precision = 8;
cout.precision(precision);
@@ -212,6 +291,8 @@ int main(int argc, char *argv[])
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
"Time step.");
args.AddOption((int *)&prec_type, "-pt", "--prec-type", "Preconditioner for "
"implicit solves. 0 for ILU, 1 for pAIR-AMG.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -232,20 +313,19 @@ int main(int argc, char *argv[])
args.Parse();
if (!args.Good())
{
if (myid == 0)
if (mpi.Root())
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
if (mpi.Root())
{
args.PrintOptions(cout);
}
Device device(device_config);
if (myid == 0) { device.Print(); }
if (mpi.Root()) { device.Print(); }
// 3. Read the serial mesh from the given mesh file on all processors. We can
// handle geometrically periodic meshes in this code.
@@ -272,12 +352,11 @@ int main(int argc, char *argv[])
case 23: ode_solver = new SDIRK23Solver; break;
case 24: ode_solver = new SDIRK34Solver; break;
default:
if (myid == 0)
if (mpi.Root())
{
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
}
delete mesh;
MPI_Finalize();
return 3;
}
@@ -311,7 +390,7 @@ int main(int argc, char *argv[])
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, &fec);
HYPRE_Int global_vSize = fes->GlobalTrueVSize();
if (myid == 0)
if (mpi.Root())
{
cout << "Number of unknowns: " << global_vSize << endl;
}
@@ -342,15 +421,16 @@ int main(int argc, char *argv[])
}
m->AddDomainIntegrator(new MassIntegrator);
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
constexpr double alpha = -1.0;
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, alpha));
k->AddInteriorFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
new NonconservativeDGTraceIntegrator(velocity, alpha));
k->AddBdrFaceIntegrator(
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
new NonconservativeDGTraceIntegrator(velocity, alpha));
ParLinearForm *b = new ParLinearForm(fes);
b->AddBdrFaceIntegrator(
new BoundaryFlowIntegrator(inflow, velocity, -1.0, -0.5));
new BoundaryFlowIntegrator(inflow, velocity, alpha));
int skip_zeros = 0;
m->Assemble();
@@ -451,11 +531,11 @@ int main(int argc, char *argv[])
sout.open(vishost, visport);
if (!sout)
{
if (myid == 0)
if (mpi.Root())
cout << "Unable to connect to GLVis server at "
<< vishost << ':' << visport << endl;
visualization = false;
if (myid == 0)
if (mpi.Root())
{
cout << "GLVis visualization disabled.\n";
}
@@ -467,7 +547,7 @@ int main(int argc, char *argv[])
sout << "solution\n" << *pmesh << *u;
sout << "pause\n";
sout << flush;
if (myid == 0)
if (mpi.Root())
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
}
@@ -476,7 +556,7 @@ int main(int argc, char *argv[])
// 10. Define the time-dependent evolution operator describing the ODE
// right-hand side, and perform time-integration (looping over the time
// iterations, ti, with a time-step dt).
FE_Evolution adv(*m, *k, *B);
FE_Evolution adv(*m, *k, *B, prec_type);
double t = 0.0;
adv.SetTime(t);
@@ -493,7 +573,7 @@ int main(int argc, char *argv[])
if (done || ti % vis_steps == 0)
{
if (myid == 0)
if (mpi.Root())
{
cout << "time step: " << ti << ", time: " << t << endl;
}
@@ -564,16 +644,14 @@ int main(int argc, char *argv[])
#endif
delete dc;
MPI_Finalize();
return 0;
}
// Implementation of class FE_Evolution
FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
const Vector &_b)
: TimeDependentOperator(_M.Height()),
b(_b),
const Vector &_b, PrecType prec_type)
: TimeDependentOperator(_M.Height()), b(_b),
M_solver(_M.ParFESpace()->GetComm()),
z(_M.Height())
{
@@ -598,7 +676,7 @@ FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
HypreSmoother *hypre_prec = new HypreSmoother(M_mat, HypreSmoother::Jacobi);
M_prec = hypre_prec;
dg_solver = new DG_Solver(M_mat, K_mat, *_M.FESpace());
dg_solver = new DG_Solver(M_mat, K_mat, *_M.FESpace(), prec_type);
}
else
{
@@ -614,6 +692,10 @@ FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
M_solver.SetPrintLevel(0);
}
// Solve the equation:
// u_t = M^{-1}(Ku + b),
// by solving associated linear system
// (M - dt*K) d = K*u + b
void FE_Evolution::ImplicitSolve(const double dt, const Vector &x, Vector &k)
{
K->Mult(x, z);
+6 -2
View File
@@ -66,6 +66,7 @@ set(SRCS
tmop_tools.cpp
gslib.cpp
transfer.cpp
prmnonlinearform.cpp
)
set(HDRS
@@ -112,6 +113,7 @@ set(HDRS
tmop_tools.hpp
gslib.hpp
transfer.hpp
prmnonlinearform.hpp
)
if (MFEM_USE_SIDRE)
@@ -136,7 +138,8 @@ if (MFEM_USE_MPI)
pgridfunc.cpp
plinearform.cpp
pnonlinearform.cpp
prestriction.cpp)
prestriction.cpp
pprmnonlinearform.cpp)
# If this list (HDRS -> HEADERS) is used for install, we probably want the
# headers added all the time.
list(APPEND HDRS
@@ -145,7 +148,8 @@ if (MFEM_USE_MPI)
pgridfunc.hpp
plinearform.hpp
pnonlinearform.hpp
prestriction.hpp)
prestriction.hpp
pprmnonlinearform.hpp)
endif()
convert_filenames_to_full_paths(SRCS)
+34 -2
View File
@@ -1437,9 +1437,9 @@ void MixedBilinearForm::Assemble (int skip_zeros)
ftr = mesh->GetBdrFaceTransformations(i);
if (ftr)
{
trial_fes->GetFaceVDofs(i, tr_vdofs);
trial_fes->GetFaceVDofs(ftr->ElementNo, tr_vdofs);
test_fes->GetElementVDofs(ftr->Elem1No, te_vdofs);
trial_face_fe = trial_fes->GetFaceElement(i);
trial_face_fe = trial_fes->GetFaceElement(ftr->ElementNo);
test_fe1 = test_fes->GetFE(ftr->Elem1No);
// The test_fe2 object is really a dummy and not used on the
// boundaries, but we can't dereference a NULL pointer, and we don't
@@ -1770,9 +1770,41 @@ 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::LEGACYFULL:
case AssemblyLevel::FULL:
// Use the original 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;
+24 -7
View File
@@ -376,6 +376,13 @@ public:
/// Get the output finite element space prolongation matrix
virtual const Operator *GetOutputProlongation() const
{ return GetProlongation(); }
/** @brief Returns the output fe space restriction matrix, transposed
Logically, this is the transpose of GetOutputRestriction, but in
practice it is convenient to have it in transposed form for
construction of RAP operators in matrix-free methods. */
virtual const Operator *GetOutputRestrictionTranspose() const
{ return GetOutputProlongation(); }
/// Get the output finite element space restriction matrix
virtual const Operator *GetOutputRestriction() const
{ return GetRestriction(); }
@@ -847,9 +854,9 @@ public:
This returns the same operator as FormRectangularLinearSystem(), but does
without the transformations of the right-hand side. */
void FormRectangularSystemMatrix(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
OperatorHandle &A);
virtual void FormRectangularSystemMatrix(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
OperatorHandle &A);
/** @brief Form the column-constrained linear system matrix A.
See FormRectangularSystemMatrix() for details.
@@ -876,10 +883,11 @@ public:
Return in @a A a *reference* to the system matrix that is column-constrained.
The reference will be invalidated when SetOperatorType(), Update(), or the
destructor is called. */
void FormRectangularLinearSystem(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B);
virtual void FormRectangularLinearSystem(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X,
Vector &B);
/** @brief Form the linear system A X = B, corresponding to this bilinear
form and the linear form @a b(.).
@@ -977,9 +985,18 @@ 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);
/** @brief Get the output finite element space restriction matrix in
transposed form. */
virtual const Operator *GetOutputRestrictionTranspose() const
{ return test_fes->GetRestrictionTransposeOperator(); }
};
}
+130 -1
View File
@@ -1021,7 +1021,6 @@ void PAMixedBilinearFormExtension::Update()
localTrial.UseDevice(true);
localTrial.SetSize(elem_restrict_trial->Height(),
Device::GetMemoryType());
}
if (elem_restrict_test)
{
@@ -1221,4 +1220,134 @@ void PAMixedBilinearFormExtension::AssembleDiagonal_ADAt(const Vector &D,
}
}
PADiscreteLinearOperatorExtension::PADiscreteLinearOperatorExtension(
DiscreteLinearOperator *linop) :
PAMixedBilinearFormExtension(linop)
{
}
const
Operator *PADiscreteLinearOperatorExtension::GetOutputRestrictionTranspose()
const
{
return a->GetOutputRestrictionTranspose();
}
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 *RoT = this->GetOutputRestrictionTranspose();
Operator *rap = SetupRAP(Pi, RoT);
RectangularConstrainedOperator *Arco
= new RectangularConstrainedOperator(rap, ess1, ess2, rap != this);
A.Reset(Arco);
}
} // namespace mfem
+31 -1
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
@@ -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,35 @@ public:
void Update();
};
/**
@brief Partial assembly extension for DiscreteLinearOperator
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 * GetOutputRestrictionTranspose() const;
private:
Vector test_multiplicity;
};
}
#endif
+209 -15
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,7 +92,7 @@ void BilinearFormIntegrator::AddMultPA(const Vector &, Vector &) const
void BilinearFormIntegrator::AddMultTransposePA(const Vector &, Vector &) const
{
mfem_error ("BilinearFormIntegrator::MultAssembledTranspose(...)\n"
mfem_error ("BilinearFormIntegrator::AddMultTransposePA(...)\n"
" is not implemented for this class.");
}
@@ -229,6 +229,159 @@ void SumIntegrator::AssembleElementMatrix(
}
}
void SumIntegrator::AssembleElementMatrix2(
const FiniteElement &el1, const FiniteElement &el2,
ElementTransformation &Trans, DenseMatrix &elmat)
{
MFEM_ASSERT(integrators.Size() > 0, "empty SumIntegrator.");
integrators[0]->AssembleElementMatrix2(el1, el2, Trans, elmat);
for (int i = 1; i < integrators.Size(); i++)
{
integrators[i]->AssembleElementMatrix2(el1, el2, Trans, elem_mat);
elmat += elem_mat;
}
}
void SumIntegrator::AssembleFaceMatrix(
const FiniteElement &el1, const FiniteElement &el2,
FaceElementTransformations &Trans, DenseMatrix &elmat)
{
MFEM_ASSERT(integrators.Size() > 0, "empty SumIntegrator.");
integrators[0]->AssembleFaceMatrix(el1, el2, Trans, elmat);
for (int i = 1; i < integrators.Size(); i++)
{
integrators[i]->AssembleFaceMatrix(el1, el2, Trans, elem_mat);
elmat += elem_mat;
}
}
void SumIntegrator::AssembleFaceMatrix(
const FiniteElement &tr_fe,
const FiniteElement &te_fe1, const FiniteElement &te_fe2,
FaceElementTransformations &Trans, DenseMatrix &elmat)
{
MFEM_ASSERT(integrators.Size() > 0, "empty SumIntegrator.");
integrators[0]->AssembleFaceMatrix(tr_fe, te_fe1, te_fe2, Trans, elmat);
for (int i = 1; i < integrators.Size(); i++)
{
integrators[i]->AssembleFaceMatrix(tr_fe, te_fe1, te_fe2, Trans, elem_mat);
elmat += elem_mat;
}
}
void SumIntegrator::AssemblePA(const FiniteElementSpace& fes)
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AssemblePA(fes);
}
}
void SumIntegrator::AssembleDiagonalPA(Vector &diag)
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AssembleDiagonalPA(diag);
}
}
void SumIntegrator::AssemblePAInteriorFaces(const FiniteElementSpace &fes)
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AssemblePAInteriorFaces(fes);
}
}
void SumIntegrator::AssemblePABoundaryFaces(const FiniteElementSpace &fes)
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AssemblePABoundaryFaces(fes);
}
}
void SumIntegrator::AddMultPA(const Vector& x, Vector& y) const
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AddMultPA(x, y);
}
}
void SumIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AddMultTransposePA(x, y);
}
}
void SumIntegrator::AssembleMF(const FiniteElementSpace &fes)
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AssembleMF(fes);
}
}
void SumIntegrator::AddMultMF(const Vector& x, Vector& y) const
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AddMultTransposeMF(x, y);
}
}
void SumIntegrator::AddMultTransposeMF(const Vector &x, Vector &y) const
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AddMultMF(x, y);
}
}
void SumIntegrator::AssembleDiagonalMF(Vector &diag)
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AssembleDiagonalMF(diag);
}
}
void SumIntegrator::AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add)
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AssembleEA(fes, emat, add);
}
}
void SumIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace &fes,
Vector &ea_data_int,
Vector &ea_data_ext,
const bool add)
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AssembleEAInteriorFaces(fes,ea_data_int,ea_data_ext,add);
}
}
void SumIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace &fes,
Vector &ea_data_bdr,
const bool add)
{
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->AssembleEABoundaryFaces(fes, ea_data_bdr, add);
}
}
SumIntegrator::~SumIntegrator()
{
if (own_integrators)
@@ -1913,12 +2066,12 @@ void VectorFEMassIntegrator::AssembleElementMatrix(
double w;
#ifdef MFEM_THREAD_SAFE
Vector D(VQ ? VQ->GetVDim() : 0);
Vector D(DQ ? DQ->GetVDim() : 0);
DenseMatrix trial_vshape(dof, spaceDim);
DenseMatrix K(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#else
trial_vshape.SetSize(dof, spaceDim);
D.SetSize(VQ ? VQ->GetVDim() : 0);
D.SetSize(DQ ? DQ->GetVDim() : 0);
K.SetSize(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#endif
DenseMatrix tmp(trial_vshape.Height(), K.Width());
@@ -1950,9 +2103,9 @@ void VectorFEMassIntegrator::AssembleElementMatrix(
Mult(trial_vshape,K,tmp);
AddMultABt(tmp,trial_vshape,elmat);
}
else if (VQ)
else if (DQ)
{
VQ->Eval(D, Trans, ip);
DQ->Eval(D, Trans, ip);
D *= w;
AddMultADAt(trial_vshape, D, elmat);
}
@@ -1984,12 +2137,12 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
#ifdef MFEM_THREAD_SAFE
DenseMatrix trial_vshape(trial_dof, spaceDim);
Vector shape(test_dof);
Vector D(VQ ? VQ->GetVDim() : 0);
Vector D(DQ ? DQ->GetVDim() : 0);
DenseMatrix K(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#else
trial_vshape.SetSize(trial_dof, spaceDim);
shape.SetSize(test_dof);
D.SetSize(VQ ? VQ->GetVDim() : 0);
D.SetSize(DQ ? DQ->GetVDim() : 0);
K.SetSize(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#endif
@@ -2013,9 +2166,9 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
test_fe.CalcShape(ip, shape);
w = ip.weight * Trans.Weight();
if (VQ)
if (DQ)
{
VQ->Eval(D, Trans, ip);
DQ->Eval(D, Trans, ip);
D *= w;
for (int d = 0; d < vdim; d++)
{
@@ -2081,12 +2234,12 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
#ifdef MFEM_THREAD_SAFE
DenseMatrix trial_vshape(trial_dof,spaceDim);
DenseMatrix test_vshape(test_dof,spaceDim);
Vector D(VQ ? VQ->GetVDim() : 0);
Vector D(DQ ? DQ->GetVDim() : 0);
DenseMatrix K(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#else
trial_vshape.SetSize(trial_dof,spaceDim);
test_vshape.SetSize(test_dof,spaceDim);
D.SetSize(VQ ? VQ->GetVDim() : 0);
D.SetSize(DQ ? DQ->GetVDim() : 0);
K.SetSize(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#endif
DenseMatrix tmp(test_vshape.Height(), K.Width());
@@ -2118,9 +2271,9 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
Mult(test_vshape,K,tmp);
AddMultABt(tmp,trial_vshape,elmat);
}
else if (VQ)
else if (DQ)
{
VQ->Eval(D, Trans, ip);
DQ->Eval(D, Trans, ip);
D *= w;
AddMultADBt(test_vshape,D,trial_vshape,elmat);
}
@@ -3515,6 +3668,47 @@ VectorScalarProductInterpolator::AssembleElementMatrix2(
}
void
ScalarCrossProductInterpolator::AssembleElementMatrix2(
const FiniteElement &dom_fe,
const FiniteElement &ran_fe,
ElementTransformation &Trans,
DenseMatrix &elmat)
{
// Vector coefficient product with vector shape functions
struct VCrossVShapeCoefficient : public VectorCoefficient
{
VectorCoefficient &VQ;
const FiniteElement &fe;
DenseMatrix vshape;
Vector vc;
VCrossVShapeCoefficient(VectorCoefficient &vq, const FiniteElement &fe_)
: VectorCoefficient(fe_.GetDof()), VQ(vq), fe(fe_),
vshape(vdim, vq.GetVDim()), vc(vq.GetVDim()) { }
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
V.SetSize(vdim);
VQ.Eval(vc, T, ip);
fe.CalcPhysVShape(T, vshape);
for (int k = 0; k < vdim; k++)
{
V(k) = vc(0) * vshape(k,1) - vc(1) * vshape(k,0);
}
}
};
VCrossVShapeCoefficient dom_shape_coeff(*VQ, dom_fe);
elmat.SetSize(ran_fe.GetDof(),dom_fe.GetDof());
Vector elmat_as_vec(elmat.Data(), elmat.Height()*elmat.Width());
ran_fe.Project(dom_shape_coeff, Trans, elmat_as_vec);
}
void
VectorCrossProductInterpolator::AssembleElementMatrix2(
const FiniteElement &dom_fe,
+232 -43
View File
@@ -355,7 +355,7 @@ class SumIntegrator : public BilinearFormIntegrator
{
private:
int own_integrators;
DenseMatrix elem_mat;
mutable DenseMatrix elem_mat;
Array<BilinearFormIntegrator*> integrators;
public:
@@ -367,6 +367,55 @@ public:
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
DenseMatrix &elmat);
virtual void AssembleElementMatrix2(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
using BilinearFormIntegrator::AssembleFaceMatrix;
virtual void AssembleFaceMatrix(const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &Trans,
DenseMatrix &elmat);
virtual void AssembleFaceMatrix(const FiniteElement &trial_face_fe,
const FiniteElement &test_fe1,
const FiniteElement &test_fe2,
FaceElementTransformations &Trans,
DenseMatrix &elmat);
using BilinearFormIntegrator::AssemblePA;
virtual void AssemblePA(const FiniteElementSpace& fes);
virtual void AssembleDiagonalPA(Vector &diag);
virtual void AssemblePAInteriorFaces(const FiniteElementSpace &fes);
virtual void AssemblePABoundaryFaces(const FiniteElementSpace &fes);
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
virtual void AddMultPA(const Vector& x, Vector& y) const;
virtual void AssembleMF(const FiniteElementSpace &fes);
virtual void AddMultMF(const Vector &x, Vector &y) const;
virtual void AddMultTransposeMF(const Vector &x, Vector &y) const;
virtual void AssembleDiagonalMF(Vector &diag);
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add);
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
Vector &ea_data_int,
Vector &ea_data_ext,
const bool add);
virtual void AssembleEABoundaryFaces(const FiniteElementSpace &fes,
Vector &ea_data_bdr,
const bool add);
virtual ~SumIntegrator();
};
@@ -465,8 +514,8 @@ protected:
: same_calc_shape(false), Q(NULL), VQ(NULL), DQ(NULL), MQ(NULL) {}
MixedVectorIntegrator(Coefficient &q)
: same_calc_shape(false), Q(&q), VQ(NULL), DQ(NULL), MQ(NULL) {}
MixedVectorIntegrator(VectorCoefficient &dq, bool diag = true)
: same_calc_shape(false), Q(NULL), VQ(diag?NULL:&dq), DQ(diag?&dq:NULL),
MixedVectorIntegrator(VectorCoefficient &vq, bool diag = true)
: same_calc_shape(false), Q(NULL), VQ(diag?NULL:&vq), DQ(diag?&vq:NULL),
MQ(NULL) {}
MixedVectorIntegrator(MatrixCoefficient &mq)
: same_calc_shape(false), Q(NULL), VQ(NULL), DQ(NULL), MQ(&mq) {}
@@ -503,7 +552,7 @@ protected:
Coefficient *Q;
VectorCoefficient *VQ;
VectorCoefficient *DQ;
DiagonalMatrixCoefficient *DQ;
MatrixCoefficient *MQ;
private:
@@ -901,7 +950,7 @@ public:
MixedVectorMassIntegrator() { same_calc_shape = true; }
MixedVectorMassIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) { same_calc_shape = true; }
MixedVectorMassIntegrator(VectorCoefficient &dq)
MixedVectorMassIntegrator(DiagonalMatrixCoefficient &dq)
: MixedVectorIntegrator(dq, true) { same_calc_shape = true; }
MixedVectorMassIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) { same_calc_shape = true; }
@@ -1019,7 +1068,7 @@ public:
MixedGradGradIntegrator() { same_calc_shape = true; }
MixedGradGradIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) { same_calc_shape = true; }
MixedGradGradIntegrator(VectorCoefficient &dq)
MixedGradGradIntegrator(DiagonalMatrixCoefficient &dq)
: MixedVectorIntegrator(dq, true) { same_calc_shape = true; }
MixedGradGradIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) { same_calc_shape = true; }
@@ -1107,7 +1156,7 @@ public:
MixedCurlCurlIntegrator() { same_calc_shape = true; }
MixedCurlCurlIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) { same_calc_shape = true; }
MixedCurlCurlIntegrator(VectorCoefficient &dq)
MixedCurlCurlIntegrator(DiagonalMatrixCoefficient &dq)
: MixedVectorIntegrator(dq, true) { same_calc_shape = true; }
MixedCurlCurlIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) { same_calc_shape = true; }
@@ -1651,7 +1700,7 @@ public:
MixedVectorGradientIntegrator() {}
MixedVectorGradientIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) {}
MixedVectorGradientIntegrator(VectorCoefficient &dq)
MixedVectorGradientIntegrator(DiagonalMatrixCoefficient &dq)
: MixedVectorIntegrator(dq, true) {}
MixedVectorGradientIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) {}
@@ -1705,7 +1754,7 @@ public:
MixedVectorCurlIntegrator() {}
MixedVectorCurlIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) {}
MixedVectorCurlIntegrator(VectorCoefficient &dq)
MixedVectorCurlIntegrator(DiagonalMatrixCoefficient &dq)
: MixedVectorIntegrator(dq, true) {}
MixedVectorCurlIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) {}
@@ -1760,7 +1809,7 @@ public:
MixedVectorWeakCurlIntegrator() {}
MixedVectorWeakCurlIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) {}
MixedVectorWeakCurlIntegrator(VectorCoefficient &dq)
MixedVectorWeakCurlIntegrator(DiagonalMatrixCoefficient &dq)
: MixedVectorIntegrator(dq, true) {}
MixedVectorWeakCurlIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) {}
@@ -1813,7 +1862,7 @@ public:
MixedVectorWeakDivergenceIntegrator() {}
MixedVectorWeakDivergenceIntegrator(Coefficient &q)
: MixedVectorIntegrator(q) {}
MixedVectorWeakDivergenceIntegrator(VectorCoefficient &dq)
MixedVectorWeakDivergenceIntegrator(DiagonalMatrixCoefficient &dq)
: MixedVectorIntegrator(dq, true) {}
MixedVectorWeakDivergenceIntegrator(MatrixCoefficient &mq)
: MixedVectorIntegrator(mq) {}
@@ -1844,8 +1893,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:
@@ -1900,6 +1951,7 @@ protected:
Coefficient *Q;
VectorCoefficient *VQ;
MatrixCoefficient *MQ;
SymmetricMatrixCoefficient *SMQ;
private:
Vector vec, pointflux, shape;
@@ -1922,19 +1974,28 @@ private:
public:
/// Construct a diffusion integrator with coefficient Q = 1
DiffusionIntegrator()
: Q(NULL), VQ(NULL), MQ(NULL), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
: Q(NULL), VQ(NULL), MQ(NULL), SMQ(NULL), maps(NULL), geom(NULL),
ceedDataPtr(NULL) { }
/// Construct a diffusion integrator with a scalar coefficient q
DiffusionIntegrator(Coefficient &q)
: Q(&q), VQ(NULL), MQ(NULL), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
: Q(&q), VQ(NULL), MQ(NULL), SMQ(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(NULL), VQ(&q), MQ(NULL), SMQ(NULL), maps(NULL), geom(NULL),
ceedDataPtr(NULL) { }
/// Construct a diffusion integrator with a matrix coefficient q
DiffusionIntegrator(MatrixCoefficient &q)
: Q(NULL), VQ(NULL), MQ(&q), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
: Q(NULL), VQ(NULL), MQ(&q), SMQ(NULL), maps(NULL), geom(NULL),
ceedDataPtr(NULL) { }
/// Construct a diffusion integrator with a symmetric matrix coefficient q
DiffusionIntegrator(SymmetricMatrixCoefficient &q)
: Q(NULL), VQ(NULL), MQ(NULL), SMQ(&q), maps(NULL), geom(NULL),
ceedDataPtr(NULL) { }
virtual ~DiffusionIntegrator()
{
@@ -1984,6 +2045,8 @@ public:
virtual void AddMultPA(const Vector&, Vector&) const;
virtual void AddMultTransposePA(const Vector&, Vector&) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe);
};
@@ -2047,6 +2110,8 @@ public:
virtual void AddMultPA(const Vector&, Vector&) const;
virtual void AddMultTransposePA(const Vector&, Vector&) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
ElementTransformation &Trans);
@@ -2108,6 +2173,17 @@ public:
ElementTransformation &Trans);
};
// Alias for @ConvectionIntegrator.
using NonconservativeConvectionIntegrator = ConvectionIntegrator;
/// -alpha (u, q . grad v), negative transpose of ConvectionIntegrator
class ConservativeConvectionIntegrator : public TransposeIntegrator
{
public:
ConservativeConvectionIntegrator(VectorCoefficient &q, double a = 1.0)
: TransposeIntegrator(new ConvectionIntegrator(q, -a)) { }
};
/// alpha (q . grad u, v) using the "group" FE discretization
class GroupConvectionIntegrator : public BilinearFormIntegrator
{
@@ -2333,8 +2409,9 @@ private:
protected:
Coefficient *Q;
VectorCoefficient *DQ;
DiagonalMatrixCoefficient *DQ;
MatrixCoefficient *MQ;
SymmetricMatrixCoefficient *SMQ;
// PA extension
Vector pa_data;
@@ -2345,14 +2422,18 @@ protected:
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
public:
CurlCurlIntegrator() { Q = NULL; DQ = NULL; MQ = NULL; }
CurlCurlIntegrator() { Q = NULL; DQ = NULL; MQ = NULL; SMQ = NULL; }
/// Construct a bilinear form integrator for Nedelec elements
CurlCurlIntegrator(Coefficient &q, const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(&q) { DQ = NULL; MQ = NULL; }
CurlCurlIntegrator(VectorCoefficient &dq, const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), DQ(&dq) { Q = NULL; MQ = NULL; }
BilinearFormIntegrator(ir), Q(&q), DQ(NULL), MQ(NULL), SMQ(NULL) { }
CurlCurlIntegrator(DiagonalMatrixCoefficient &dq,
const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(NULL), DQ(&dq), MQ(NULL), SMQ(NULL) { }
CurlCurlIntegrator(MatrixCoefficient &mq, const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), MQ(&mq) { Q = NULL; DQ = NULL; }
BilinearFormIntegrator(ir), Q(NULL), DQ(NULL), MQ(&mq), SMQ(NULL) { }
CurlCurlIntegrator(SymmetricMatrixCoefficient &smq,
const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(NULL), DQ(NULL), MQ(NULL), SMQ(&smq) { }
/* Given a particular Finite Element, compute the
element curl-curl matrix elmat */
@@ -2409,8 +2490,9 @@ public:
class VectorFEMassIntegrator: public BilinearFormIntegrator
{
private:
void Init(Coefficient *q, VectorCoefficient *vq, MatrixCoefficient *mq)
{ Q = q; VQ = vq; MQ = mq; }
void Init(Coefficient *q, DiagonalMatrixCoefficient *dq, MatrixCoefficient *mq,
SymmetricMatrixCoefficient *smq)
{ Q = q; DQ = dq; MQ = mq; SMQ = smq; }
#ifndef MFEM_THREAD_SAFE
Vector shape;
@@ -2423,8 +2505,9 @@ private:
protected:
Coefficient *Q;
VectorCoefficient *VQ;
DiagonalMatrixCoefficient *DQ;
MatrixCoefficient *MQ;
SymmetricMatrixCoefficient *SMQ;
// PA extension
Vector pa_data;
@@ -2437,13 +2520,15 @@ protected:
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
public:
VectorFEMassIntegrator() { Init(NULL, NULL, NULL); }
VectorFEMassIntegrator(Coefficient *_q) { Init(_q, NULL, NULL); }
VectorFEMassIntegrator(Coefficient &q) { Init(&q, NULL, NULL); }
VectorFEMassIntegrator(VectorCoefficient *_vq) { Init(NULL, _vq, NULL); }
VectorFEMassIntegrator(VectorCoefficient &vq) { Init(NULL, &vq, NULL); }
VectorFEMassIntegrator(MatrixCoefficient *_mq) { Init(NULL, NULL, _mq); }
VectorFEMassIntegrator(MatrixCoefficient &mq) { Init(NULL, NULL, &mq); }
VectorFEMassIntegrator() { Init(NULL, NULL, NULL, NULL); }
VectorFEMassIntegrator(Coefficient *_q) { Init(_q, NULL, NULL, NULL); }
VectorFEMassIntegrator(Coefficient &q) { Init(&q, NULL, NULL, NULL); }
VectorFEMassIntegrator(DiagonalMatrixCoefficient *_dq) { Init(NULL, _dq, NULL, NULL); }
VectorFEMassIntegrator(DiagonalMatrixCoefficient &dq) { Init(NULL, &dq, NULL, NULL); }
VectorFEMassIntegrator(MatrixCoefficient *_mq) { Init(NULL, NULL, _mq, NULL); }
VectorFEMassIntegrator(MatrixCoefficient &mq) { Init(NULL, NULL, &mq, NULL); }
VectorFEMassIntegrator(SymmetricMatrixCoefficient &smq) { Init(NULL, NULL, NULL, &smq); }
VectorFEMassIntegrator(SymmetricMatrixCoefficient *smq) { Init(NULL, NULL, NULL, smq); }
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
@@ -2652,11 +2737,28 @@ public:
/** Integrator for the DG form:
alpha < rho_u (u.n) {v},[w] > + beta < rho_u |u.n| [v],[w] >,
where v and w are the trial and test variables, respectively, and rho/u are
given scalar/vector coefficients. The vector coefficient, u, is assumed to
be continuous across the faces and when given the scalar coefficient, rho,
is assumed to be discontinuous. The integrator uses the upwind value of rho,
rho_u, which is value from the side into which the vector coefficient, u,
points. */
given scalar/vector coefficients. {v} represents the average value of v on
the face and [v] is the jump such that {v}=(v1+v2)/2 and [v]=(v1-v2) for the
face between elements 1 and 2. For boundary elements, v2=0. The vector
coefficient, u, is assumed to be continuous across the faces and when given
the scalar coefficient, rho, is assumed to be discontinuous. The integrator
uses the upwind value of rho, rho_u, which is value from the side into which
the vector coefficient, u, points.
One use case for this integrator is to discretize the operator -u.grad(v)
with a DG formulation. The resulting formulation uses the
ConvectionIntegrator (with coefficient u, and parameter alpha = -1) and the
transpose of the DGTraceIntegrator (with coefficient u, and parameters alpha
= 1, beta = -1/2 to use the upwind face flux, see also
NonconservativeDGTraceIntegrator). This discretization and the handling of
the inflow and outflow boundaries is illustrated in Example 9/9p.
Another use case for this integrator is to discretize the operator -div(u v)
with a DG formulation. The resulting formulation is conservative and
consists of the ConservativeConvectionIntegrator (with coefficient u, and
parameter alpha = -1) plus the DGTraceIntegrator (with coefficient u, and
parameters alpha = -1, beta = -1/2 to use the upwind face flux).
*/
class DGTraceIntegrator : public BilinearFormIntegrator
{
protected:
@@ -2673,13 +2775,17 @@ private:
Vector shape1, shape2;
public:
/// Construct integrator with rho = 1.
DGTraceIntegrator(VectorCoefficient &_u, double a, double b)
{ rho = NULL; u = &_u; alpha = a; beta = b; }
/// Construct integrator with rho = 1, b = 0.5*a.
DGTraceIntegrator(VectorCoefficient &u_, double a)
{ rho = NULL; u = &u_; alpha = a; beta = 0.5*a; }
DGTraceIntegrator(Coefficient &_rho, VectorCoefficient &_u,
/// Construct integrator with rho = 1.
DGTraceIntegrator(VectorCoefficient &u_, double a, double b)
{ rho = NULL; u = &u_; alpha = a; beta = b; }
DGTraceIntegrator(Coefficient &_rho, VectorCoefficient &u_,
double a, double b)
{ rho = &_rho; u = &_u; alpha = a; beta = b; }
{ rho = &_rho; u = &u_; alpha = a; beta = b; }
using BilinearFormIntegrator::AssembleFaceMatrix;
virtual void AssembleFaceMatrix(const FiniteElement &el1,
@@ -2713,6 +2819,30 @@ private:
void SetupPA(const FiniteElementSpace &fes, FaceType type);
};
// Alias for @a DGTraceIntegrator.
using ConservativeDGTraceIntegrator = DGTraceIntegrator;
/** Integrator that represents the face terms used for the non-conservative
DG discretization of the convection equation:
-alpha < rho_u (u.n) {v},[w] > + beta < rho_u |u.n| [v],[w] >.
This integrator can be used with together with ConvectionIntegrator to
implement an upwind DG discretization in non-conservative form, see ex9 and
ex9p. */
class NonconservativeDGTraceIntegrator : public TransposeIntegrator
{
public:
NonconservativeDGTraceIntegrator(VectorCoefficient &u, double a)
: TransposeIntegrator(new DGTraceIntegrator(u, -a, 0.5*a)) { }
NonconservativeDGTraceIntegrator(VectorCoefficient &u, double a, double b)
: TransposeIntegrator(new DGTraceIntegrator(u, -a, b)) { }
NonconservativeDGTraceIntegrator(Coefficient &rho, VectorCoefficient &u,
double a, double b)
: TransposeIntegrator(new DGTraceIntegrator(rho, u, -a, b)) { }
};
/** Integrator for the DG form:
- < {(Q grad(u)).n}, [v] > + sigma < [u], {(Q grad(v)).n} >
@@ -2953,11 +3083,36 @@ class DiscreteInterpolator : public BilinearFormIntegrator { };
class GradientInterpolator : public DiscreteInterpolator
{
public:
GradientInterpolator() : dofquad_fe(NULL) { }
virtual ~GradientInterpolator() { delete dofquad_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;
/** @brief Setup method for PA data.
@param[in] trial_fes H1 Lagrange space
@param[in] test_fes H(curl) Nedelec space
*/
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 finite element that generates and owns the 1D DofToQuad maps below
FiniteElement * dofquad_fe;
bool B_id; // is the B basis operator (maps_C_C) the identity?
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;
};
@@ -2972,6 +3127,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 finite element that generates and owns the 1D DofToQuad maps below
FiniteElement * dofquad_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;
};
@@ -3071,6 +3244,22 @@ protected:
VectorCoefficient *VQ;
};
/** Interpolator of the 2D cross product between a vector coefficient and an
H(curl)-conforming field onto an L2-conforming field. */
class ScalarCrossProductInterpolator : public DiscreteInterpolator
{
public:
ScalarCrossProductInterpolator(VectorCoefficient & vc)
: VQ(&vc) { }
virtual void AssembleElementMatrix2(const FiniteElement &nd_fe,
const FiniteElement &l2_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
protected:
VectorCoefficient *VQ;
};
/** Interpolator of the cross product between a vector coefficient and an
H(curl)-conforming field onto an H(div)-conforming field. The range space
can also be vector L2. */
+47 -31
View File
@@ -379,51 +379,54 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
const int MQfullDim = MQ ? MQ->GetHeight() * MQ->GetWidth() : 0;
if (MQ)
{
symmetric = false;
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;
coeffDim = MQfullDim;
coeff.SetSize(MQdim * nq * ne);
symmetric = MQ ? MQ->IsSymmetric() : true;
coeff.SetSize(MQfullDim * nq * ne);
DenseMatrix M;
Vector Msymm;
if (symmetric)
{
Msymm.SetSize(MQsymmDim);
}
else
{
M.SetSize(dim);
}
M.SetSize(dim);
auto C = Reshape(coeff.HostWrite(), MQdim, nq, ne);
auto C = Reshape(coeff.HostWrite(), MQfullDim, 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)
MQ->Eval(M, *tr, ir->IntPoint(p));
for (int i=0; i<dim; ++i)
for (int j=0; j<dim; ++j)
{
C(i, p, e) = Msymm[i];
C(j+(i*dim), p, e) = M(i,j);
}
}
else
{
MQ->Eval(M, *tr, ir->IntPoint(p));
}
}
}
else if (SMQ)
{
MFEM_VERIFY(SMQ->GetSize() == dim, "");
coeffDim = symmDims;
coeff.SetSize(symmDims * nq * ne);
for (int i=0; i<dim; ++i)
for (int j=0; j<dim; ++j)
{
C(j+(i*dim), p, e) = M(i,j);
}
}
DenseSymmetricMatrix M;
M.SetSize(dim);
auto C = Reshape(coeff.HostWrite(), symmDims, nq, ne);
for (int e=0; e<ne; ++e)
{
ElementTransformation *tr = mesh->GetElementTransformation(e);
for (int p=0; p<nq; ++p)
{
SMQ->Eval(M, *tr, ir->IntPoint(p));
int cnt = 0;
for (int i=0; i<dim; ++i)
for (int j=i; j<dim; ++j, ++cnt)
{
C(cnt, p, e) = M(i,j);
}
}
}
}
@@ -1901,4 +1904,17 @@ void DiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
}
void DiffusionIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
{
if (symmetric)
{
AddMultPA(x, y);
}
else
{
MFEM_ABORT("DiffusionIntegrator::AddMultTransposePA only implemented in "
"the symmetric case.")
}
}
} // namespace mfem
+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
+1952 -37
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -1226,4 +1226,10 @@ void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
}
void MassIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
{
// Mass integrator is symmetric
AddMultPA(x, y);
}
} // namespace mfem
+33 -40
View File
@@ -761,12 +761,12 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
trial_fetype = trial_el->GetDerivType();
test_fetype = test_el->GetDerivType();
const int MQsymmDim = MQ ? (MQ->GetWidth() * (MQ->GetWidth() + 1)) / 2 : 0;
const int MQsymmDim = SMQ ? (SMQ->GetSize() * (SMQ->GetSize() + 1)) / 2 : 0;
const int MQfullDim = MQ ? (MQ->GetHeight() * MQ->GetWidth()) : 0;
const int MQdim = MQ ? (MQ->IsSymmetric() ? MQsymmDim : MQfullDim) : 0;
const int coeffDim = MQ ? MQdim : (VQ ? VQ->GetVDim() : 1);
const int MQdim = MQ ? MQfullDim : MQsymmDim;
const int coeffDim = (MQ || SMQ) ? MQdim : (DQ ? DQ->GetVDim() : 1);
symmetric = MQ ? MQ->IsSymmetric() : true;
symmetric = (MQ == NULL);
const bool trial_curl = (trial_fetype == mfem::FiniteElement::CURL);
const bool trial_div = (trial_fetype == mfem::FiniteElement::DIV);
@@ -783,24 +783,13 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
Vector coeff(coeffDim * ne * nq);
coeff = 1.0;
auto coeffh = Reshape(coeff.HostWrite(), coeffDim, nq, ne);
if (Q || VQ || MQ)
if (Q || DQ || MQ || SMQ)
{
Vector D(VQ ? coeffDim : 0);
Vector D(DQ ? coeffDim : 0);
DenseMatrix M;
Vector Msymm;
if (MQ)
{
if (symmetric)
{
Msymm.SetSize(MQsymmDim);
}
else
{
M.SetSize(dim);
}
}
DenseSymmetricMatrix SM;
if (VQ)
if (DQ)
{
MFEM_VERIFY(coeffDim == dim, "");
}
@@ -808,6 +797,12 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
{
MFEM_VERIFY(coeffDim == MQdim, "");
MFEM_VERIFY(MQ->GetHeight() == dim && MQ->GetWidth() == dim, "");
M.SetSize(dim);
}
if (SMQ)
{
MFEM_VERIFY(SMQ->GetSize() == dim, "");
SM.SetSize(dim);
}
for (int e=0; e<ne; ++e)
@@ -817,29 +812,27 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
{
if (MQ)
{
if (MQ->IsSymmetric())
{
MQ->EvalSymmetric(Msymm, *tr, ir->IntPoint(p));
MQ->Eval(M, *tr, ir->IntPoint(p));
for (int i=0; i<MQsymmDim; ++i)
for (int i=0; i<dim; ++i)
for (int j=0; j<dim; ++j)
{
coeffh(i, p, e) = Msymm[i];
coeffh(j+(i*dim), p, e) = M(i,j);
}
}
else
{
MQ->Eval(M, *tr, ir->IntPoint(p));
for (int i=0; i<dim; ++i)
for (int j=0; j<dim; ++j)
{
coeffh(j+(i*dim), p, e) = M(i,j);
}
}
}
else if (VQ)
else if (SMQ)
{
VQ->Eval(D, *tr, ir->IntPoint(p));
SMQ->Eval(SM, *tr, ir->IntPoint(p));
int cnt = 0;
for (int i=0; i<dim; ++i)
for (int j=i; j<dim; ++j, ++cnt)
{
coeffh(cnt, p, e) = SM(i,j);
}
}
else if (DQ)
{
DQ->Eval(D, *tr, ir->IntPoint(p));
for (int i=0; i<coeffDim; ++i)
{
coeffh(i, p, e) = D[i];
@@ -1007,14 +1000,14 @@ void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
else if (trial_curl && test_div)
{
const bool scalarCoeff = !(VQ || MQ);
const bool scalarCoeff = !(DQ || MQ || SMQ);
PAHcurlHdivMassApply3D(dofs1D, dofs1Dtest, quad1D, ne, scalarCoeff,
true, mapsO->B, mapsC->B, mapsOtest->Bt,
mapsCtest->Bt, pa_data, x, y);
}
else if (trial_div && test_curl)
{
const bool scalarCoeff = !(VQ || MQ);
const bool scalarCoeff = !(DQ || MQ || SMQ);
PAHcurlHdivMassApply3D(dofs1D, dofs1Dtest, quad1D, ne, scalarCoeff,
false, mapsO->B, mapsC->B, mapsOtest->Bt,
mapsCtest->Bt, pa_data, x, y);
@@ -1038,7 +1031,7 @@ void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
else if ((trial_curl && test_div) || (trial_div && test_curl))
{
const bool scalarCoeff = !(VQ || MQ);
const bool scalarCoeff = !(DQ || MQ || SMQ);
PAHcurlHdivMassApply2D(dofs1D, dofs1Dtest, quad1D, ne, scalarCoeff,
trial_curl, mapsO->B, mapsC->B, mapsOtest->Bt,
mapsCtest->Bt, pa_data, x, y);
+32 -1
View File
@@ -301,7 +301,7 @@ void MatrixFunctionCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
K.SetSize(height, width);
if (symmetric) // Use SymmFunction
if (symmetric) // Use SymmFunction (deprecated version)
{
MFEM_VERIFY(height == width && SymmFunction,
"MatrixFunctionCoefficient is not symmetric");
@@ -371,6 +371,36 @@ void MatrixFunctionCoefficient::EvalSymmetric(Vector &K,
}
}
void SymmetricMatrixFunctionCoefficient::Eval(DenseSymmetricMatrix &K,
ElementTransformation &T,
const IntegrationPoint &ip)
{
double x[3];
Vector transip(x, 3);
T.Transform(ip, transip);
K.SetSize(dim);
if (Function)
{
Function(transip, K);
}
else if (TDFunction)
{
TDFunction(transip, GetTime(), K);
}
else
{
K = mat;
}
if (Q)
{
K *= Q->Eval(T, ip, GetTime());
}
}
MatrixArrayCoefficient::MatrixArrayCoefficient (int dim)
: MatrixCoefficient (dim)
{
@@ -595,6 +625,7 @@ void MatrixVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T,
{
a->Eval(ma, T, ip);
b->Eval(vb, T, ip);
V.SetSize(vdim);
ma.Mult(vb, V);
}
+121 -16
View File
@@ -688,6 +688,7 @@ public:
const IntegrationRule &ir);
};
typedef VectorCoefficient DiagonalMatrixCoefficient;
/// Base class for Matrix Coefficients that optionally depend on time and space.
class MatrixCoefficient
@@ -695,7 +696,7 @@ class MatrixCoefficient
protected:
int height, width;
double time;
bool symmetric;
bool symmetric; // deprecated
public:
/// Construct a dim x dim matrix coefficient.
@@ -721,6 +722,7 @@ public:
/// For backward compatibility get the width of the matrix.
int GetVDim() const { return width; }
/** @deprecated Use SymmetricMatrixCoefficient instead */
bool IsSymmetric() const { return symmetric; }
/** @brief Evaluate the matrix coefficient in the element described by @a T
@@ -731,11 +733,13 @@ public:
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) = 0;
/// (DEPRECATED) Evaluate a symmetric matrix coefficient.
/** @brief Evaluate the upper triangular entries of the matrix coefficient
in the symmetric case, similarly to Eval. Matrix entry (i,j) is stored
in K[j - i + os_i] for 0 <= i <= j < width, os_0 = 0,
os_{i+1} = os_i + width - i. That is, K = {M(0,0), ..., M(0,w-1),
M(1,1), ..., M(1,w-1), ..., M(w-1,w-1) with w = width. */
M(1,1), ..., M(1,w-1), ..., M(w-1,w-1) with w = width.
@deprecated Use Eval() instead. */
virtual void EvalSymmetric(Vector &K, ElementTransformation &T,
const IntegrationPoint &ip)
{ mfem_error("MatrixCoefficient::EvalSymmetric"); }
@@ -767,7 +771,7 @@ class MatrixFunctionCoefficient : public MatrixCoefficient
{
private:
std::function<void(const Vector &, DenseMatrix &)> Function;
std::function<void(const Vector &, Vector &)> SymmFunction;
std::function<void(const Vector &, Vector &)> SymmFunction; // deprecated
std::function<void(const Vector &, double, DenseMatrix &)> TDFunction;
Coefficient *Q;
@@ -791,6 +795,18 @@ public:
: MatrixCoefficient(m.Height(), m.Width()), Q(&q), mat(m)
{ }
/** @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
@deprecated Use another constructor without setting SymmFunction. */
MatrixFunctionCoefficient(int dim,
std::function<void(const Vector &, Vector &)> SymmF,
Coefficient *q = NULL)
: MatrixCoefficient(dim, true), SymmFunction(std::move(SymmF)), Q(q), mat(0)
{ }
/// Define a time-dependent square matrix coefficient from a std function
/** \param dim - the size of the matrix
\param TDF - time-dependent function
@@ -801,22 +817,12 @@ public:
: 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,
Coefficient *q = NULL)
: MatrixCoefficient(dim, true), SymmFunction(std::move(SymmF)), Q(q), mat(0)
{ }
/// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
/// Evaluate the symmetric matrix coefficient at @a ip.
/// (DEPRECATED) Evaluate the symmetric matrix coefficient at @a ip.
/** @deprecated Use Eval() instead. */
virtual void EvalSymmetric(Vector &K, ElementTransformation &T,
const IntegrationPoint &ip);
@@ -824,7 +830,6 @@ public:
};
/** @brief Matrix coefficient defined by a matrix of scalar coefficients.
Coefficients that are not set will evaluate to zero in the vector. The
coefficient is stored as a flat Array with indexing (i,j) -> i*width+j. */
@@ -940,6 +945,106 @@ public:
}
};
/// Base class for symmetric matrix coefficients that optionally depend on time and space.
class SymmetricMatrixCoefficient
{
protected:
int dim;
double time;
public:
/// Construct a dim x dim matrix coefficient.
explicit SymmetricMatrixCoefficient(int dimension)
{ dim = dimension; time = 0.; }
/// Set the time for time dependent coefficients
void SetTime(double t) { time = t; }
/// Get the time for time dependent coefficients
double GetTime() { return time; }
/// Get the size of the matrix.
int GetSize() const { return dim; }
/** @brief Evaluate the matrix coefficient in the element described by @a T
at the point @a ip, storing the result in @a K. */
/** @note When this method is called, the caller must make sure that the
IntegrationPoint associated with @a T is the same as @a ip. This can be
achieved by calling T.SetIntPoint(&ip). */
virtual void Eval(DenseSymmetricMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) = 0;
virtual ~SymmetricMatrixCoefficient() { }
};
/// A matrix coefficient that is constant in space and time.
class SymmetricMatrixConstantCoefficient : public SymmetricMatrixCoefficient
{
private:
DenseSymmetricMatrix mat;
public:
///Construct using matrix @a m for the constant.
SymmetricMatrixConstantCoefficient(const DenseSymmetricMatrix &m)
: SymmetricMatrixCoefficient(m.Height()), mat(m) { }
using SymmetricMatrixCoefficient::Eval;
/// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseSymmetricMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) { M = mat; }
};
/** @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. */
class SymmetricMatrixFunctionCoefficient : public SymmetricMatrixCoefficient
{
private:
std::function<void(const Vector &, DenseSymmetricMatrix &)> Function;
std::function<void(const Vector &, double, DenseSymmetricMatrix &)> TDFunction;
Coefficient *Q;
DenseSymmetricMatrix mat;
public:
/// Define a time-independent symmetric 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 */
SymmetricMatrixFunctionCoefficient(int dim,
std::function<void(const Vector &, DenseSymmetricMatrix &)> F,
Coefficient *q = nullptr)
: SymmetricMatrixCoefficient(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 */
SymmetricMatrixFunctionCoefficient(const DenseSymmetricMatrix &m,
Coefficient &q)
: SymmetricMatrixCoefficient(m.Height()), 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 */
SymmetricMatrixFunctionCoefficient(int dim,
std::function<void(const Vector &, double, DenseSymmetricMatrix &)> TDF,
Coefficient *q = nullptr)
: SymmetricMatrixCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
{ }
/// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseSymmetricMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~SymmetricMatrixFunctionCoefficient() { }
};
/** @brief Scalar coefficient defined as the product of two scalar coefficients
or a scalar and a scalar coefficient. */
class ProductCoefficient : public Coefficient
+1
View File
@@ -1204,6 +1204,7 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
});
// Modify offdiagonal blocks (imaginary parts of the matrix) to conform
// with standard essential BC treatment
ess_tdof_list.HostRead();
if (A_i.Type() == Operator::Hypre_ParCSR)
{
HypreParMatrix * Ah;
+376
View File
@@ -48,8 +48,373 @@ void L2ZienkiewiczZhuEstimator::ComputeEstimates()
current_sequence = solution->FESpace()->GetMesh()->GetSequence();
}
KellyErrorEstimator::KellyErrorEstimator(BilinearFormIntegrator& di_,
ParGridFunction& sol_,
ParFiniteElementSpace& flux_fespace_,
const Array<int> &attributes_)
: attributes(attributes_)
, flux_integrator(&di_)
, solution(&sol_)
, flux_space(&flux_fespace_)
, own_flux_fespace(false)
{
ResetCoefficientFunctions();
}
KellyErrorEstimator::KellyErrorEstimator(BilinearFormIntegrator& di_,
ParGridFunction& sol_,
ParFiniteElementSpace* flux_fespace_,
const Array<int> &attributes_)
: attributes(attributes_)
, flux_integrator(&di_)
, solution(&sol_)
, flux_space(flux_fespace_)
, own_flux_fespace(true)
{
ResetCoefficientFunctions();
}
KellyErrorEstimator::~KellyErrorEstimator()
{
if (own_flux_fespace)
{
delete flux_space;
}
}
void KellyErrorEstimator::ResetCoefficientFunctions()
{
compute_element_coefficient = [](ParMesh* pmesh, const int e)
{
return 1.0;
};
compute_face_coefficient = [](ParMesh* pmesh, const int f,
const bool shared_face)
{
auto FT = [&]()
{
if (shared_face)
{
return pmesh->GetSharedFaceTransformations(f);
}
return pmesh->GetFaceElementTransformations(f);
}();
const auto order = FT->GetFE()->GetOrder();
// Poor man's face diameter.
double diameter = 0.0;
Vector p1(pmesh->SpaceDimension());
Vector p2(pmesh->SpaceDimension());
// NOTE: We have no direct access to vertices for shared faces,
// so we fall back to compute the positions from the element.
// This can also be modified to compute the diameter for non-linear
// geometries by sampling along geometry-specific lines.
auto vtx_intrule = Geometries.GetVertices(FT->GetGeometryType());
const auto nip = vtx_intrule->GetNPoints();
for (int i = 0; i < nip; i++)
{
// Evaluate flux vector at integration point
auto fip1 = vtx_intrule->IntPoint(i);
FT->Transform(fip1, p1);
for (int j = 0; j < nip; j++)
{
auto fip2 = vtx_intrule->IntPoint(j);
FT->Transform(fip2, p2);
diameter = std::max<double>(diameter, p2.DistanceTo(p1));
}
}
return diameter/(2.0*order);
};
}
void KellyErrorEstimator::ComputeEstimates()
{
// Remarks:
// For some context you may have to consult the documentation of
// the FaceInfo class [1]. Also, the FaceElementTransformations
// documentation [2] may be helpful to grasp what is going on. Note
// that the FaceElementTransformations also works in the non-
// conforming case to transfer the gauss points from the slave to
// the master element.
// [1]
// https://github.com/mfem/mfem/blob/02d0bfe9c18ce049c3c93a6a4208080fcfc96991/mesh/mesh.hpp#L94
// [2]
// https://github.com/mfem/mfem/blob/02d0bfe9c18ce049c3c93a6a4208080fcfc96991/fem/eltrans.hpp#L435
flux_space->Update(false);
auto xfes = solution->ParFESpace();
MFEM_ASSERT(xfes->GetVDim() == 1,
"Estimation for vector-valued problems not implemented yet.");
auto pmesh = xfes->GetParMesh();
this->error_estimates.SetSize(xfes->GetNE());
this->error_estimates = 0.0;
// 1. Compute fluxes in discontinuous space
ParGridFunction flux(flux_space);
flux = 0.0;
// We pre-sort the array to speed up the search in the following loops.
if (attributes.Size())
{
attributes.Sort();
}
Array<int> xdofs, fdofs;
Vector el_x, el_f;
for (int e = 0; e < xfes->GetNE(); e++)
{
auto attr = xfes->GetAttribute(e);
if (attributes.Size() && attributes.FindSorted(attr) == -1)
{
continue;
}
xfes->GetElementVDofs(e, xdofs);
solution->GetSubVector(xdofs, el_x);
ElementTransformation* Transf = xfes->GetElementTransformation(e);
flux_integrator->ComputeElementFlux(*xfes->GetFE(e), *Transf, el_x,
*flux_space->GetFE(e), el_f, true);
flux_space->GetElementVDofs(e, fdofs);
flux.AddElementVector(fdofs, el_f);
}
// 2. Add error contribution from local interior faces
for (int f = 0; f < pmesh->GetNumFaces(); f++)
{
auto FT = pmesh->GetFaceElementTransformations(f);
auto &int_rule = IntRules.Get(FT->FaceGeom, 2 * xfes->GetFaceOrder(f));
const auto nip = int_rule.GetNPoints();
if (pmesh->FaceIsInterior(f))
{
int Inf1, Inf2, NCFace;
pmesh->GetFaceInfos(f, &Inf1, &Inf2, &NCFace);
// Convention
// * Conforming face: Face side with smaller element id handles
// the integration
// * Non-conforming face: The slave handles the integration.
// See FaceInfo documentation for details.
bool isNCSlave = FT->Elem2No >= 0 && NCFace >= 0;
bool isConforming = FT->Elem2No >= 0 && NCFace == -1;
if ((FT->Elem1No < FT->Elem2No && isConforming) || isNCSlave)
{
if (attributes.Size() &&
(attributes.FindSorted(FT->Elem1->Attribute) == -1
|| attributes.FindSorted(FT->Elem2->Attribute) == -1))
{
continue;
}
IntegrationRule eir;
Vector jumps(nip);
// Integral over local half face on the side of e₁
// i.e. the numerical integration of ∫ flux ⋅ n dS₁
for (int i = 0; i < nip; i++)
{
// Evaluate flux at IP
auto &fip = int_rule.IntPoint(i);
IntegrationPoint ip;
FT->Loc1.Transform(fip, ip);
Vector val(flux_space->GetVDim());
flux.GetVectorValue(FT->Elem1No, ip, val);
// And build scalar product with normal
Vector normal(pmesh->SpaceDimension());
FT->Face->SetIntPoint(&fip);
if (pmesh->Dimension() == pmesh->SpaceDimension())
{
CalcOrtho(FT->Face->Jacobian(), normal);
}
else
{
Vector ref_normal(pmesh->Dimension());
FT->Loc1.Transf.SetIntPoint(&fip);
CalcOrtho(FT->Loc1.Transf.Jacobian(), ref_normal);
auto &e1 = FT->GetElement1Transformation();
e1.AdjugateJacobian().MultTranspose(ref_normal, normal);
normal /= e1.Weight();
}
jumps(i) = val * normal * fip.weight * FT->Face->Weight();
}
// Subtract integral over half face of e₂
// i.e. the numerical integration of ∫ flux ⋅ n dS₂
for (int i = 0; i < nip; i++)
{
// Evaluate flux vector at IP
auto &fip = int_rule.IntPoint(i);
IntegrationPoint ip;
FT->Loc2.Transform(fip, ip);
Vector val(flux_space->GetVDim());
flux.GetVectorValue(FT->Elem2No, ip, val);
// And build scalar product with normal
Vector normal(pmesh->SpaceDimension());
FT->Face->SetIntPoint(&fip);
if (pmesh->Dimension() == pmesh->SpaceDimension())
{
CalcOrtho(FT->Face->Jacobian(), normal);
}
else
{
Vector ref_normal(pmesh->Dimension());
FT->Loc1.Transf.SetIntPoint(&fip);
CalcOrtho(FT->Loc1.Transf.Jacobian(), ref_normal);
auto &e1 = FT->GetElement1Transformation();
e1.AdjugateJacobian().MultTranspose(ref_normal, normal);
normal /= e1.Weight();
}
jumps(i) -= val * normal * fip.weight * FT->Face->Weight();
}
// Finalize "local" L₂ contribution
for (int i = 0; i < nip; i++)
{
jumps(i) *= jumps(i);
}
auto h_k_face = compute_face_coefficient(pmesh, f, false);
double jump_integral = h_k_face*jumps.Sum();
// A local face is shared between two local elements, so we
// can get away with integrating the jump only once and add
// it to both elements. To minimize communication, the jump
// of shared faces is computed locally by each process.
error_estimates(FT->Elem1No) += jump_integral;
error_estimates(FT->Elem2No) += jump_integral;
}
}
}
// 3. Add error contribution from shared interior faces
// Synchronize face data.
flux.ExchangeFaceNbrData();
for (int sf = 0; sf < pmesh->GetNSharedFaces(); sf++)
{
auto FT = pmesh->GetSharedFaceTransformations(sf, true);
if (attributes.Size() &&
(attributes.FindSorted(FT->Elem1->Attribute) == -1
|| attributes.FindSorted(FT->Elem2->Attribute) == -1))
{
continue;
}
auto &int_rule = IntRules.Get(FT->FaceGeom, 2 * xfes->GetFaceOrder(0));
const auto nip = int_rule.GetNPoints();
IntegrationRule eir;
Vector jumps(nip);
// Integral over local half face on the side of e₁
// i.e. the numerical integration of ∫ flux ⋅ n dS₁
for (int i = 0; i < nip; i++)
{
// Evaluate flux vector at integration point
auto &fip = int_rule.IntPoint(i);
IntegrationPoint ip;
FT->Loc1.Transform(fip, ip);
Vector val(flux_space->GetVDim());
flux.GetVectorValue(FT->Elem1No, ip, val);
Vector normal(pmesh->SpaceDimension());
FT->Face->SetIntPoint(&fip);
if (pmesh->Dimension() == pmesh->SpaceDimension())
{
CalcOrtho(FT->Face->Jacobian(), normal);
}
else
{
Vector ref_normal(pmesh->Dimension());
FT->Loc1.Transf.SetIntPoint(&fip);
CalcOrtho(FT->Loc1.Transf.Jacobian(), ref_normal);
auto &e1 = FT->GetElement1Transformation();
e1.AdjugateJacobian().MultTranspose(ref_normal, normal);
normal /= e1.Weight();
}
jumps(i) = val * normal * fip.weight * FT->Face->Weight();
}
// Subtract integral over non-local half face of e₂
// i.e. the numerical integration of ∫ flux ⋅ n dS₂
for (int i = 0; i < nip; i++)
{
// Evaluate flux vector at integration point
auto &fip = int_rule.IntPoint(i);
IntegrationPoint ip;
FT->Loc2.Transform(fip, ip);
Vector val(flux_space->GetVDim());
flux.GetVectorValue(FT->Elem2No, ip, val);
// Evaluate gauss point
Vector normal(pmesh->SpaceDimension());
FT->Face->SetIntPoint(&fip);
if (pmesh->Dimension() == pmesh->SpaceDimension())
{
CalcOrtho(FT->Face->Jacobian(), normal);
}
else
{
Vector ref_normal(pmesh->Dimension());
CalcOrtho(FT->Loc1.Transf.Jacobian(), ref_normal);
auto &e1 = FT->GetElement1Transformation();
e1.AdjugateJacobian().MultTranspose(ref_normal, normal);
normal /= e1.Weight();
}
jumps(i) -= val * normal * fip.weight * FT->Face->Weight();
}
// Finalize "local" L₂ contribution
for (int i = 0; i < nip; i++)
{
jumps(i) *= jumps(i);
}
auto h_k_face = compute_face_coefficient(pmesh, sf, true);
double jump_integral = h_k_face*jumps.Sum();
error_estimates(FT->Elem1No) += jump_integral;
// We skip "error_estimates(FT->Elem2No) += jump_integral"
// because the error is stored on the remote process and
// recomputed there.
}
// Finalize element errors
for (int e = 0; e < xfes->GetNE(); e++)
{
auto factor = compute_element_coefficient(pmesh, e);
// The sqrt belongs to the norm and hₑ to the indicator.
error_estimates(e) = sqrt(factor * error_estimates(e));
}
current_sequence = solution->FESpace()->GetMesh()->GetSequence();
// Finish by computing the global error.
double process_local_error = error_estimates.Sum();
MPI_Allreduce(&process_local_error, &total_error, 1, MPI_DOUBLE,
MPI_SUM, xfes->GetComm());
}
#endif // MFEM_USE_MPI
void LpErrorEstimator::ComputeEstimates()
{
MFEM_VERIFY(coef != NULL || vcoef != NULL,
@@ -64,6 +429,17 @@ void LpErrorEstimator::ComputeEstimates()
{
sol->ComputeElementLpErrors(local_norm_p, *vcoef, error_estimates);
}
#ifdef MFEM_USE_MPI
total_error = error_estimates.Sum();
auto pfes = dynamic_cast<ParFiniteElementSpace*>(sol->FESpace());
if (pfes)
{
auto process_local_error = total_error;
MPI_Allreduce(&process_local_error, &total_error, 1, MPI_DOUBLE,
MPI_SUM, pfes->GetComm());
}
#endif // MFEM_USE_MPI
total_error = pow(total_error, 1.0/local_norm_p);
current_sequence = sol->FESpace()->GetMesh()->GetSequence();
}
+189 -11
View File
@@ -12,6 +12,8 @@
#ifndef MFEM_ERROR_ESTIMATORS
#define MFEM_ERROR_ESTIMATORS
#include <functional>
#include "../config/config.hpp"
#include "../linalg/vector.hpp"
#include "bilinearform.hpp"
@@ -39,6 +41,11 @@ public:
class ErrorEstimator : public AbstractErrorEstimator
{
public:
/// Return the total error from the last error estimate.
/** @note This method is optional for derived classes to override and the
base class implementation simply returns 0. */
virtual double GetTotalError() const { return 0.0; }
/// Get a Vector with all element errors.
virtual const Vector &GetLocalErrors() = 0;
@@ -148,8 +155,8 @@ public:
own_flux_fes(false)
{ }
/** @brief Consider the coefficient in BilinearFormIntegrator to calculate the
fluxes for the error estimator.*/
/** @brief Consider the coefficient in BilinearFormIntegrator to calculate
the fluxes for the error estimator.*/
void SetWithCoeff(bool w_coeff = true) { with_coeff = w_coeff; }
/** @brief Enable/disable anisotropic estimates. To enable this option, the
@@ -166,10 +173,10 @@ public:
void SetFluxAveraging(int fa) { flux_averaging = fa; }
/// Return the total error from the last error estimate.
double GetTotalError() const { return total_error; }
virtual double GetTotalError() const override { return total_error; }
/// Get a Vector with all element errors.
virtual const Vector &GetLocalErrors()
virtual const Vector &GetLocalErrors() override
{
if (MeshIsModified()) { ComputeEstimates(); }
return error_estimates;
@@ -178,14 +185,14 @@ public:
/** @brief Get an Array<int> with anisotropic flags for all mesh elements.
Return an empty array when anisotropic estimates are not available or
enabled. */
virtual const Array<int> &GetAnisotropicFlags()
virtual const Array<int> &GetAnisotropicFlags() override
{
if (MeshIsModified()) { ComputeEstimates(); }
return aniso_flags;
}
/// Reset the error estimator.
virtual void Reset() { current_sequence = -1; }
virtual void Reset() override { current_sequence = -1; }
/** @brief Destroy a ZienkiewiczZhuEstimator object. Destroys, if owned, the
FiniteElementSpace, flux_space. */
@@ -292,17 +299,17 @@ public:
void SetLocalErrorNormP(int p) { local_norm_p = p; }
/// Return the total error from the last error estimate.
double GetTotalError() const { return total_error; }
virtual double GetTotalError() const override { return total_error; }
/// Get a Vector with all element errors.
virtual const Vector &GetLocalErrors()
virtual const Vector &GetLocalErrors() override
{
if (MeshIsModified()) { ComputeEstimates(); }
return error_estimates;
}
/// Reset the error estimator.
virtual void Reset() { current_sequence = -1; }
virtual void Reset() override { current_sequence = -1; }
/** @brief Destroy a L2ZienkiewiczZhuEstimator object. Destroys, if owned,
the FiniteElementSpace, flux_space. */
@@ -314,6 +321,7 @@ public:
#endif // MFEM_USE_MPI
/** @brief The LpErrorEstimator class compares the solution to a known
coefficient.
@@ -332,6 +340,8 @@ protected:
int local_norm_p;
Vector error_estimates;
double total_error = 0.0;
Coefficient * coef;
VectorCoefficient * vcoef;
GridFunction * sol;
@@ -383,10 +393,10 @@ public:
void SetCoef(VectorCoefficient &A) { vcoef = &A; }
/// Reset the error estimator.
virtual void Reset() { current_sequence = -1; }
virtual void Reset() override { current_sequence = -1; }
/// Get a Vector with all element errors.
virtual const Vector &GetLocalErrors()
virtual const Vector &GetLocalErrors() override
{
if (MeshIsModified()) { ComputeEstimates(); }
return error_estimates;
@@ -396,6 +406,174 @@ public:
virtual ~LpErrorEstimator() {}
};
#ifdef MFEM_USE_MPI
/** @brief The KellyErrorEstimator class provides a fast error indication
strategy for smooth scalar parallel problems.
The Kelly error indicator is based on the following papers:
Kelly, D. W., et al. "A posteriori error analysis and adaptive processes in
the finite element method: Part IError analysis." International journal for
numerical methods in engineering 19.11 (1983): 1593-1619.
De SR Gago, J. P., et al. "A posteriori error analysis and adaptive
processes in the finite element method: Part IIAdaptive mesh refinement."
International journal for numerical methods in engineering 19.11 (1983):
1621-1656.
It can be roughly described by:
||(u-uₕ)|| ( C hₑ (hₖ |J[uₕ]|²) dS )
where "e" denotes an element, |||| the corresponding local norm and k the
corresponding faces. u is the analytic solution and uₕ the discretized
solution. hₖ and hₑ are factors dependend on the face and element geometry.
J is the jump function, i.e. the difference between the limits at each point
for each side of the face. A custom method to compute hₖ can be provided. It
is also possible to estimate the error only on a subspace by feeding this
class an attribute array describing the subspace.
@note This algorithm is only for Poisson problems a proper error esimator.
The current implementation does not reflect this, because the "C" factor is
not included.
It further assumes that the approximation error at the boundary is small
enough, as the implementation ignores boundary faces.
*/
class KellyErrorEstimator final : public ErrorEstimator
{
public:
/// Function type to compute the local coefficient hₑ of an element.
using ElementCoefficientFunction =
std::function<double(ParMesh*, const int)>;
/** @brief Function type to compute the local coefficient hₖ of a face. The
third argument is true for shared faces and false for local faces. */
using FaceCoefficientFunction =
std::function<double(ParMesh*, const int, const bool)>;
private:
int current_sequence = -1;
Vector error_estimates;
double total_error = 0.0;
Array<int> attributes;
/** @brief A method to compute hₑ on per-element basis.
This method weights the error approximation on the element level.
Defaults to hₑ=1.0.
*/
ElementCoefficientFunction compute_element_coefficient;
/** @brief A method to compute hₖ on per-face basis.
This method weights the error approximation on the face level. The
background here is that classical Kelly error estimator implementations
approximate the geometrical characteristic hₖ with the face diameter,
which should be also be a possibility in this implementation.
Defaults to hₖ=diameter/2p.
*/
FaceCoefficientFunction compute_face_coefficient;
BilinearFormIntegrator* flux_integrator; ///< Not owned.
ParGridFunction* solution; ///< Not owned.
ParFiniteElementSpace*
flux_space; /**< @brief Ownership based on own_flux_fes. */
bool own_flux_fespace; ///< Ownership flag for flux_space.
/// Check if the mesh of the solution was modified.
bool MeshIsModified()
{
long mesh_sequence = solution->FESpace()->GetMesh()->GetSequence();
MFEM_ASSERT(mesh_sequence >= current_sequence,
"improper mesh update sequence");
return (mesh_sequence > current_sequence);
}
/** @brief Compute the element error estimates.
Algorithm outline:
1. Compute flux field for each element
2. Add error contribution from local interior faces
3. Add error contribution from shared interior faces
4. Finalize by computing hₖ and scale errors.
*/
void ComputeEstimates();
public:
/** @brief Construct a new KellyErrorEstimator object for a scalar field.
@param di_ The bilinearform to compute the interface flux.
@param sol_ The solution field whose error is to be estimated.
@param flux_fes_ The finite element space for the interface flux.
@param attributes_ The attributes of the subdomain(s) for which the
error should be estimated. An empty array results in
estimating the error over the complete domain.
*/
KellyErrorEstimator(BilinearFormIntegrator& di_, ParGridFunction& sol_,
ParFiniteElementSpace& flux_fes_,
const Array<int> &attributes_ = Array<int>());
/** @brief Construct a new KellyErrorEstimator object for a scalar field.
@param di_ The bilinearform to compute the interface flux.
@param sol_ The solution field whose error is to be estimated.
@param flux_fes_ The finite element space for the interface flux.
@param attributes_ The attributes of the subdomain(s) for which the
error should be estimated. An empty array results in
estimating the error over the complete domain.
*/
KellyErrorEstimator(BilinearFormIntegrator& di_, ParGridFunction& sol_,
ParFiniteElementSpace* flux_fes_,
const Array<int> &attributes_ = Array<int>());
~KellyErrorEstimator();
/// Get a Vector with all element errors.
const Vector& GetLocalErrors() override
{
if (MeshIsModified())
{
ComputeEstimates();
}
return error_estimates;
}
/// Reset the error estimator.
void Reset() override { current_sequence = -1; };
virtual double GetTotalError() const override { return total_error; }
/** @brief Change the method to compute hₑ on a per-element basis.
@param compute_element_coefficient_
A function taking a mesh and an element index to
compute the local hₑ for the element.
*/
void SetElementCoefficientFunction(ElementCoefficientFunction
compute_element_coefficient_)
{
compute_element_coefficient = compute_element_coefficient_;
}
/** @brief Change the method to compute hₖ on a per-element basis.
@param compute_face_coefficient_
A function taking a mesh and a face index to
compute the local hₖ for the face.
*/
void SetFaceCoefficientFunction(
FaceCoefficientFunction
compute_face_coefficient_)
{
compute_face_coefficient = compute_face_coefficient_;
}
/// Change the coefficients back to default as described above.
void ResetCoefficientFunctions();
};
#endif // MFEM_USE_MPI
} // namespace mfem
#endif // MFEM_ERROR_ESTIMATORS
+165 -14
View File
@@ -596,17 +596,22 @@ void NodalFiniteElement::ProjectCurl_2D(
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &curl) const
{
MFEM_ASSERT(GetMapType() == FiniteElement::INTEGRAL, "");
DenseMatrix curl_shape(fe.GetDof(), 1);
curl.SetSize(dof, fe.GetDof());
for (int i = 0; i < dof; i++)
{
fe.CalcCurlShape(Nodes.IntPoint(i), curl_shape);
double w = 1.0;
if (GetMapType() == FiniteElement::VALUE)
{
Trans.SetIntPoint(&Nodes.IntPoint(i));
w /= Trans.Weight();
}
for (int j = 0; j < fe.GetDof(); j++)
{
curl(i,j) = curl_shape(j,0);
curl(i,j) = w * curl_shape(j,0);
}
}
}
@@ -722,17 +727,34 @@ void NodalFiniteElement::Project(
{
if (fe.GetRangeType() == SCALAR)
{
MFEM_ASSERT(map_type == fe.GetMapType(), "");
Vector shape(fe.GetDof());
I.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
if (map_type == fe.GetMapType())
{
fe.CalcShape(Nodes.IntPoint(k), shape);
for (int j = 0; j < shape.Size(); j++)
for (int k = 0; k < dof; k++)
{
I(k,j) = (fabs(shape(j)) < 1e-12) ? 0.0 : shape(j);
fe.CalcShape(Nodes.IntPoint(k), shape);
for (int j = 0; j < shape.Size(); j++)
{
I(k,j) = (fabs(shape(j)) < 1e-12) ? 0.0 : shape(j);
}
}
}
else
{
for (int k = 0; k < dof; k++)
{
Trans.SetIntPoint(&Nodes.IntPoint(k));
fe.CalcPhysShape(Trans, shape);
if (map_type == INTEGRAL)
{
shape *= Trans.Weight();
}
for (int j = 0; j < shape.Size(); j++)
{
I(k,j) = (fabs(shape(j)) < 1e-12) ? 0.0 : shape(j);
}
}
}
}
@@ -1031,6 +1053,8 @@ void VectorFiniteElement::Project_RT(
fe.CalcShape(ip, shape);
Trans.SetIntPoint(&ip);
// Transform RT face normals from reference to physical space
// vk = adj(J)^T nk
Trans.AdjugateJacobian().MultTranspose(nk + d2n[k]*dim, vk);
if (fe.GetMapType() == INTEGRAL)
{
@@ -1048,6 +1072,8 @@ void VectorFiniteElement::Project_RT(
{
s = 0.0;
}
// Project scalar basis function multiplied by each coordinate
// direction onto the transformed face normals
for (int d = 0; d < sdim; d++)
{
I(k,j+d*shape.Size()) = s*vk[d];
@@ -1057,7 +1083,31 @@ void VectorFiniteElement::Project_RT(
}
else
{
mfem_error("VectorFiniteElement::Project_RT (fe version)");
int sdim = Trans.GetSpaceDim();
double vk[Geometry::MaxDim];
DenseMatrix vshape(fe.GetDof(), sdim);
Vector vshapenk(fe.GetDof());
const bool square_J = (dim == sdim);
I.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
Trans.SetIntPoint(&ip);
// Transform RT face normals from reference to physical space
// vk = adj(J)^T nk
Trans.AdjugateJacobian().MultTranspose(nk + d2n[k]*dim, vk);
// Compute fe basis functions in physical space
fe.CalcVShape(Trans, vshape);
// Project fe basis functions onto transformed face normals
vshape.Mult(vk, vshapenk);
if (!square_J) { vshapenk /= Trans.Weight(); }
for (int j=0; j<vshapenk.Size(); j++)
{
I(k,j) = vshapenk(j);
}
}
}
}
@@ -1218,6 +1268,8 @@ void VectorFiniteElement::Project_ND(
fe.CalcShape(ip, shape);
Trans.SetIntPoint(&ip);
// Transform ND edge tengents from reference to physical space
// vk = J tk
Trans.Jacobian().Mult(tk + d2t[k]*dim, vk);
if (fe.GetMapType() == INTEGRAL)
{
@@ -1235,6 +1287,8 @@ void VectorFiniteElement::Project_ND(
{
s = 0.0;
}
// Project scalar basis function multiplied by each coordinate
// direction onto the transformed edge tangents
for (int d = 0; d < sdim; d++)
{
I(k, j + d*shape.Size()) = s*vk[d];
@@ -1244,7 +1298,29 @@ void VectorFiniteElement::Project_ND(
}
else
{
mfem_error("VectorFiniteElement::Project_ND (fe version)");
int sdim = Trans.GetSpaceDim();
double vk[Geometry::MaxDim];
DenseMatrix vshape(fe.GetDof(), sdim);
Vector vshapetk(fe.GetDof());
I.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
Trans.SetIntPoint(&ip);
// Transform ND edge tangents from reference to physical space
// vk = J tk
Trans.Jacobian().Mult(tk + d2t[k]*dim, vk);
// Compute fe basis functions in physical space
fe.CalcVShape(Trans, vshape);
// Project fe basis functions onto transformed edge tangents
vshape.Mult(vk, vshapetk);
for (int j=0; j<vshapetk.Size(); j++)
{
I(k, j) = vshapetk(j);
}
}
}
}
@@ -7721,7 +7797,10 @@ NodalTensorFiniteElement::NodalTensorFiniteElement(const int dims,
const DofMapType dmtype)
: NodalFiniteElement(dims, GetTensorProductGeometry(dims), Pow(p + 1, dims),
p, dims > 1 ? FunctionSpace::Qk : FunctionSpace::Pk),
TensorBasisElement(dims, p, VerifyNodal(btype), dmtype) { }
TensorBasisElement(dims, p, VerifyNodal(btype), dmtype)
{
lex_ordering = dof_map;
}
PositiveTensorFiniteElement::PositiveTensorFiniteElement(
@@ -8405,23 +8484,33 @@ H1_TriangleElement::H1_TriangleElement(const int p, const int btype)
Vector shape_x(p + 1), shape_y(p + 1), shape_l(p + 1);
#endif
int p2p3 = 2*p + 3;
auto idx = [p2p3](int i, int j) { return ((p2p3-j)*j)/2+i; };
lex_ordering.SetSize(dof);
// vertices
lex_ordering[idx(0,0)] = 0;
Nodes.IntPoint(0).Set2(cp[0], cp[0]);
lex_ordering[idx(p,0)] = 1;
Nodes.IntPoint(1).Set2(cp[p], cp[0]);
lex_ordering[idx(0,p)] = 2;
Nodes.IntPoint(2).Set2(cp[0], cp[p]);
// edges
int o = 3;
for (int i = 1; i < p; i++)
{
lex_ordering[idx(i,0)] = o;
Nodes.IntPoint(o++).Set2(cp[i], cp[0]);
}
for (int i = 1; i < p; i++)
{
lex_ordering[idx(p-i,i)] = o;
Nodes.IntPoint(o++).Set2(cp[p-i], cp[i]);
}
for (int i = 1; i < p; i++)
{
lex_ordering[idx(0,p-i)] = o;
Nodes.IntPoint(o++).Set2(cp[0], cp[p-i]);
}
@@ -8430,6 +8519,7 @@ H1_TriangleElement::H1_TriangleElement(const int p, const int btype)
for (int i = 1; i + j < p; i++)
{
const double w = cp[i] + cp[j] + cp[p-i-j];
lex_ordering[idx(i,j)] = o;
Nodes.IntPoint(o++).Set2(cp[i]/w, cp[j]/w);
}
@@ -8563,36 +8653,56 @@ H1_TetrahedronElement::H1_TetrahedronElement(const int p, const int btype)
Vector shape_x(p + 1), shape_y(p + 1), shape_z(p + 1), shape_l(p + 1);
#endif
auto tri = [](int k) { return (k*(k + 1))/2; };
auto tet = [](int k) { return (k*(k + 1)*(k + 2))/6; };
int ndof = tet(p+1);
auto idx = [tri, tet, p, ndof](int i, int j, int k)
{
return ndof - tet(p - k) - tri(p + 1 - k - j) + i;
};
lex_ordering.SetSize(dof);
// vertices
lex_ordering[idx(0,0,0)] = 0;
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
lex_ordering[idx(p,0,0)] = 1;
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
lex_ordering[idx(0,p,0)] = 2;
Nodes.IntPoint(2).Set3(cp[0], cp[p], cp[0]);
lex_ordering[idx(0,0,p)] = 3;
Nodes.IntPoint(3).Set3(cp[0], cp[0], cp[p]);
// edges (see Tetrahedron::edges in mesh/tetrahedron.cpp)
int o = 4;
for (int i = 1; i < p; i++) // (0,1)
{
lex_ordering[idx(i,0,0)] = o;
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]);
}
for (int i = 1; i < p; i++) // (0,2)
{
lex_ordering[idx(0,i,0)] = o;
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (0,3)
{
lex_ordering[idx(0,0,i)] = o;
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (1,2)
{
lex_ordering[idx(p-i,i,0)] = o;
Nodes.IntPoint(o++).Set3(cp[p-i], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (1,3)
{
lex_ordering[idx(p-i,0,i)] = o;
Nodes.IntPoint(o++).Set3(cp[p-i], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (2,3)
{
lex_ordering[idx(0,p-i,i)] = o;
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[i]);
}
@@ -8600,24 +8710,28 @@ H1_TetrahedronElement::H1_TetrahedronElement(const int p, const int btype)
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (1,2,3)
{
lex_ordering[idx(p-i-j,i,j)] = o;
double w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[p-i-j]/w, cp[i]/w, cp[j]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (0,3,2)
{
lex_ordering[idx(0,j,i)] = o;
double w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[0], cp[j]/w, cp[i]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (0,1,3)
{
lex_ordering[idx(i,0,j)] = o;
double w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[0], cp[j]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (0,2,1)
{
lex_ordering[idx(j,i,0)] = o;
double w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[j]/w, cp[i]/w, cp[0]);
}
@@ -8627,6 +8741,7 @@ H1_TetrahedronElement::H1_TetrahedronElement(const int p, const int btype)
for (int j = 1; j + k < p; j++)
for (int i = 1; i + j + k < p; i++)
{
lex_ordering[idx(i,j,k)] = o;
double w = cp[i] + cp[j] + cp[k] + cp[p-i-j-k];
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[j]/w, cp[k]/w);
}
@@ -9171,7 +9286,22 @@ H1_WedgeElement::H1_WedgeElement(const int p,
t_dof.SetSize(dof);
s_dof.SetSize(dof);
int p2p3 = 2*p + 3, ntri = ((p + 1)*(p + 2))/2;
auto idx = [p2p3,ntri](int i, int j, int k)
{
return k*ntri + ((p2p3-j)*j)/2+i;
};
lex_ordering.SetSize(dof);
int o = 0;
// Nodal DoFs
lex_ordering[idx(0,0,0)] = o++;
lex_ordering[idx(p,0,0)] = o++;
lex_ordering[idx(0,p,0)] = o++;
lex_ordering[idx(0,0,p)] = o++;
lex_ordering[idx(p,0,p)] = o++;
lex_ordering[idx(0,p,p)] = o++;
t_dof[0] = 0; s_dof[0] = 0;
t_dof[1] = 1; s_dof[1] = 0;
t_dof[2] = 2; s_dof[2] = 0;
@@ -9180,9 +9310,19 @@ H1_WedgeElement::H1_WedgeElement(const int p,
t_dof[5] = 2; s_dof[5] = 1;
// Edge DoFs
int k = 0;
int ne = p-1;
for (int i=1; i<p; i++)
{
lex_ordering[idx(i,0,0)] = o + 0*ne + k;
lex_ordering[idx(p-i,i,0)] = o + 1*ne + k;
lex_ordering[idx(0,p-i,0)] = o + 2*ne + k;
lex_ordering[idx(i,0,p)] = o + 3*ne + k;
lex_ordering[idx(p-i,i,p)] = o + 4*ne + k;
lex_ordering[idx(0,p-i,p)] = o + 5*ne + k;
lex_ordering[idx(0,0,i)] = o + 6*ne + k;
lex_ordering[idx(p,0,i)] = o + 7*ne + k;
lex_ordering[idx(0,p,i)] = o + 8*ne + k;
t_dof[5 + 0 * ne + i] = 2 + 0 * ne + i; s_dof[5 + 0 * ne + i] = 0;
t_dof[5 + 1 * ne + i] = 2 + 1 * ne + i; s_dof[5 + 1 * ne + i] = 0;
t_dof[5 + 2 * ne + i] = 2 + 2 * ne + i; s_dof[5 + 2 * ne + i] = 0;
@@ -9192,21 +9332,26 @@ H1_WedgeElement::H1_WedgeElement(const int p,
t_dof[5 + 6 * ne + i] = 0; s_dof[5 + 6 * ne + i] = i + 1;
t_dof[5 + 7 * ne + i] = 1; s_dof[5 + 7 * ne + i] = i + 1;
t_dof[5 + 8 * ne + i] = 2; s_dof[5 + 8 * ne + i] = i + 1;
++k;
}
o += 9*ne;
// Triangular Face DoFs
int k=0;
k=0;
int nt = (p-1)*(p-2)/2;
for (int j=1; j<p; j++)
{
for (int i=1; i<p-j; i++)
{
int l = j - p + (((2 * p - 1) - i) * i) / 2;
lex_ordering[idx(i,j,0)] = o+l;
lex_ordering[idx(i,j,p)] = o+nt+k;
t_dof[6 + 9 * ne + k] = 3 * p + l; s_dof[6 + 9 * ne + k] = 0;
t_dof[6 + 9 * ne + nt + k] = 3 * p + k; s_dof[6 + 9 * ne + nt + k] = 1;
k++;
}
}
o += 2*nt;
// Quadrilateral Face DoFs
k=0;
@@ -9215,6 +9360,10 @@ H1_WedgeElement::H1_WedgeElement(const int p,
{
for (int i=1; i<p; i++)
{
lex_ordering[idx(i,0,j)] = o+k;
lex_ordering[idx(p-i,i,j)] = o+nq+k;
lex_ordering[idx(0,p-i,j)] = o+2*nq+k;
t_dof[6 + 9 * ne + 2 * nt + 0 * nq + k] = 2 + 0 * ne + i;
t_dof[6 + 9 * ne + 2 * nt + 1 * nq + k] = 2 + 1 * ne + i;
t_dof[6 + 9 * ne + 2 * nt + 2 * nq + k] = 2 + 2 * ne + i;
@@ -9226,6 +9375,7 @@ H1_WedgeElement::H1_WedgeElement(const int p,
k++;
}
}
o += 3*nq;
// Interior DoFs
int m=0;
@@ -9234,8 +9384,9 @@ H1_WedgeElement::H1_WedgeElement(const int p,
int l=0;
for (int j=1; j<p; j++)
{
for (int i=1; i<j; i++)
for (int i=1; i+j<p; i++)
{
lex_ordering[idx(i,j,k)] = o++;
t_dof[6 + 9 * ne + 2 * nt + 3 * nq + m] = 3 * p + l;
s_dof[6 + 9 * ne + 2 * nt + 3 * nq + m] = 1 + k;
l++; m++;
+84
View File
@@ -504,12 +504,18 @@ public:
/** @brief Given a coefficient and a transformation, compute its projection
(approximation) in the local finite dimensional space in terms
of the degrees of freedom. */
/** The approximation used to project is usually local interpolation of
degrees of freedom. The derived class could use other methods not
implemented yet, e.g. local L2 projection. */
virtual void Project(Coefficient &coeff,
ElementTransformation &Trans, Vector &dofs) const;
/** @brief Given a vector coefficient and a transformation, compute its
projection (approximation) in the local finite dimensional space
in terms of the degrees of freedom. (VectorFiniteElements) */
/** The approximation used to project is usually local interpolation of
degrees of freedom. The derived class could use other methods not
implemented yet, e.g. local L2 projection. */
virtual void Project(VectorCoefficient &vc,
ElementTransformation &Trans, Vector &dofs) const;
@@ -692,6 +698,7 @@ public:
class NodalFiniteElement : public ScalarFiniteElement
{
protected:
Array<int> lex_ordering;
void ProjectCurl_2D(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &curl) const;
@@ -740,6 +747,29 @@ public:
virtual void ProjectDiv(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &div) const;
/** @brief Get an Array<int> that maps lexicographically ordered indices to
the indices of the respective nodes/dofs/basis functions. Lexicographic
ordering of nodes is defined in terms of reference-space coordinates
(x,y,z). Lexicographically ordered nodes are listed first in order of
increasing x-coordinate, and then in order of increasing y-coordinate,
and finally in order of increasing z-coordinate.
For example, the six nodes of a quadratic triangle are lexicographically
ordered as follows:
5
|\
3 4
| \
0-1-2
The resulting array may be empty if the DOFs are already ordered
lexicographically, or if the finite element does not support creating
this permutation. The array returned is the same as the array given by
TensorBasisElement::GetDofMap, but it is also available for non-tensor
elements. */
const Array<int> &GetLexicographicOrdering() const { return lex_ordering; }
};
/** @brief Class for finite elements utilizing the
@@ -813,11 +843,25 @@ protected:
void CalcVShape_ND(ElementTransformation &Trans,
DenseMatrix &shape) const;
/** @brief Project a vector coefficient onto the RT basis functions
@param nk Face normal vectors for this element type
@param d2n Offset into nk for each degree of freedom
@param vc Vector coefficient to be projected
@param Trans Transformation from reference to physical coordinates
@param dofs Expansion coefficients for the approximation of vc
*/
void Project_RT(const double *nk, const Array<int> &d2n,
VectorCoefficient &vc, ElementTransformation &Trans,
Vector &dofs) const;
/// Projects the vector of values given at FE nodes to RT space
/** Project vector values onto the RT basis functions
@param nk Face normal vectors for this element type
@param d2n Offset into nk for each degree of freedom
@param vc Vector values at each interpolation point
@param Trans Transformation from reference to physical coordinates
@param dofs Expansion coefficients for the approximation of vc
*/
void Project_RT(const double *nk, const Array<int> &d2n,
Vector &vc, ElementTransformation &Trans,
Vector &dofs) const;
@@ -827,6 +871,19 @@ protected:
const double *nk, const Array<int> &d2n,
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const;
/** @brief Project vector-valued basis functions onto the RT basis functions
@param nk Face normal vectors for this element type
@param d2n Offset into nk for each degree of freedom
@param fe Vector-valued finite element basis
@param Trans Transformation from reference to physical coordinates
@param I Expansion coefficients for the approximation of each basis
function
Note: If the FiniteElement, fe, is scalar-valued the projection will
assume that a FiniteElementSpace is being used to define a vector
field using the scalar basis functions for each component of the
vector field.
*/
void Project_RT(const double *nk, const Array<int> &d2n,
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &I) const;
@@ -846,11 +903,25 @@ protected:
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &curl) const;
/** @brief Project a vector coefficient onto the ND basis functions
@param tk Edge tangent vectors for this element type
@param d2t Offset into tk for each degree of freedom
@param vc Vector coefficient to be projected
@param Trans Transformation from reference to physical coordinates
@param dofs Expansion coefficients for the approximation of vc
*/
void Project_ND(const double *tk, const Array<int> &d2t,
VectorCoefficient &vc, ElementTransformation &Trans,
Vector &dofs) const;
/// Projects the vector of values given at FE nodes to ND space
/** Project vector values onto the ND basis functions
@param tk Edge tangent vectors for this element type
@param d2t Offset into tk for each degree of freedom
@param vc Vector values at each interpolation point
@param Trans Transformation from reference to physical coordinates
@param dofs Expansion coefficients for the approximation of vc
*/
void Project_ND(const double *tk, const Array<int> &d2t,
Vector &vc, ElementTransformation &Trans,
Vector &dofs) const;
@@ -860,6 +931,19 @@ protected:
const double *tk, const Array<int> &d2t,
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const;
/** @brief Project vector-valued basis functions onto the ND basis functions
@param tk Edge tangent vectors for this element type
@param d2t Offset into tk for each degree of freedom
@param fe Vector-valued finite element basis
@param Trans Transformation from reference to physical coordinates
@param I Expansion coefficients for the approximation of each basis
function
Note: If the FiniteElement, fe, is scalar-valued the projection will
assume that a FiniteElementSpace is being used to define a vector
field using the scalar basis functions for each component of the
vector field.
*/
void Project_ND(const double *tk, const Array<int> &d2t,
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &I) const;
+9 -12
View File
@@ -1854,19 +1854,16 @@ const int *H1_FECollection::GetDofMap(Geometry::Type GeomType) const
{
const int *dof_map = NULL;
const FiniteElement *fe = H1_Elements[GeomType];
switch (GeomType)
const NodalFiniteElement *nodal_fe =
dynamic_cast<const NodalFiniteElement*>(fe);
if (nodal_fe)
{
case Geometry::SEGMENT:
case Geometry::SQUARE:
case Geometry::CUBE:
dof_map = dynamic_cast<const TensorBasisElement *>(fe)
->GetDofMap().GetData();
break;
default:
MFEM_ABORT("Geometry type " << Geometry::Name[GeomType] << " is not "
"implemented");
// The "Cartesian" ordering for other geometries is defined by the
// class GeometryRefiner.
dof_map = nodal_fe->GetLexicographicOrdering().GetData();
}
else
{
MFEM_ABORT("Geometry type " << Geometry::Name[GeomType] << " is not "
"implemented");
}
return dof_map;
}
+2
View File
@@ -41,6 +41,7 @@
#include "transfer.hpp"
#include "fespacehierarchy.hpp"
#include "multigrid.hpp"
#include "prmnonlinearform.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
@@ -48,6 +49,7 @@
#include "plinearform.hpp"
#include "pbilinearform.hpp"
#include "pnonlinearform.hpp"
#include "pprmnonlinearform.hpp"
#endif
#ifdef MFEM_USE_SIDRE
+2 -1
View File
@@ -2608,9 +2608,9 @@ const Operator &GridTransfer::MakeTrueOperator(
else // Parallel() == true
{
#ifdef MFEM_USE_MPI
const SparseMatrix *out_R = fes_out.GetRestrictionMatrix();
if (oper_type == Operator::Hypre_ParCSR)
{
const SparseMatrix *out_R = fes_out.GetRestrictionMatrix();
const ParFiniteElementSpace *pfes_in =
dynamic_cast<const ParFiniteElementSpace *>(&fes_in);
const ParFiniteElementSpace *pfes_out =
@@ -2638,6 +2638,7 @@ const Operator &GridTransfer::MakeTrueOperator(
}
else if (oper_type == Operator::ANY_TYPE)
{
const Operator *out_R = fes_out.GetRestrictionOperator();
t_oper.Reset(new TripleProductOperator(
out_R, &oper, fes_in.GetProlongationMatrix(),
false, false, false));
+16 -1
View File
@@ -330,6 +330,18 @@ public:
virtual const Operator *GetProlongationMatrix() const
{ return GetConformingProlongation(); }
/// Return an operator that performs the transpose of GetRestrictionOperator
/** The returned operator is owned by the FiniteElementSpace. In serial this
is the same as GetProlongationMatrix() */
virtual const Operator *GetRestrictionTransposeOperator() const
{ return GetConformingProlongation(); }
/// An abstract operator that performs the same action as GetRestrictionMatrix
/** In some cases this is an optimized matrix-free implementation. The
returned operator is owned by the FiniteElementSpace. */
virtual const Operator *GetRestrictionOperator() const
{ return GetConformingRestriction(); }
/// The returned SparseMatrix is owned by the FiniteElementSpace.
virtual const SparseMatrix *GetRestrictionMatrix() const
{ return GetConformingRestriction(); }
@@ -571,7 +583,7 @@ public:
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th element in the mesh object. */
const FiniteElement *GetFE(int i) const;
virtual const FiniteElement *GetFE(int i) const;
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th boundary face in the mesh object. */
@@ -756,6 +768,9 @@ public:
/// Return the total number of quadrature points.
int GetSize() const { return size; }
/// Return the order of the quadrature rule(s) used by all elements.
int GetOrder() const { return order; }
/// Returns the mesh
inline Mesh *GetMesh() const { return mesh; }
+22 -24
View File
@@ -1165,24 +1165,31 @@ RefinedGeometry * GeometryRefiner::Refine(Geometry::Type Geom,
Array<int> vi((n+1)*(n+1)*(n+1));
vi = -1;
m = 0;
for (k = 0; k <= n; k++)
for (j = 0; j <= k; j++)
for (i = 0; i <= j; i++)
// vertices are given in lexicographic ordering on the reference
// element
for (int kk = 0; kk <= n; kk++)
for (int jj = 0; jj <= n-kk; jj++)
for (int ii = 0; ii <= n-jj-kk; ii++)
{
IntegrationPoint &ip = RG->RefPts.IntPoint(m);
// map the coordinates to the reference tetrahedron
// (0,0,0) -> (0,0,0)
// (0,0,1) -> (1,0,0)
// (1,1,1) -> (0,1,0)
// (0,1,1) -> (0,0,1)
double w = cp[k-j] + cp[i] + cp[j-i] + cp[Times-k];
ip.x = cp[k-j]/w;
ip.y = cp[i]/w;
ip.z = cp[j-i]/w;
double w = cp[ii] + cp[jj] + cp[kk] + cp[Times-ii-jj-kk];
ip.x = cp[ii]/w;
ip.y = cp[jj]/w;
ip.z = cp[kk]/w;
// (ii,jj,kk) are coordinates in the reference tetrahedron,
// transform to coordinates (i,j,k) in the auxiliary
// tetrahedron defined by (0,0,0), (0,0,1), (1,1,1), (0,1,1)
int i = jj;
int j = jj+kk;
int k = ii+jj+kk;
l = i + (j + k * (n+1)) * (n+1);
// map from linear Cartesian hex index in the auxiliary tet
// to lexicographic in the reference tet
vi[l] = m;
m++;
}
if (m != (n+3)*(n+2)*(n+1)/6)
{
mfem_error("GeometryRefiner::Refine() for TETRAHEDRON #1");
@@ -1269,18 +1276,9 @@ RefinedGeometry * GeometryRefiner::Refine(Geometry::Type Geom,
for (i = 0; i <= n-j; i++, l++)
{
IntegrationPoint &ip = RG->RefPts.IntPoint(l);
if (type == 0)
{
ip.x = double(i) / n;
ip.y = double(j) / n;
ip.z = double(k) / n;
}
else
{
ip.x = cp[i]/(cp[i] + cp[j] + cp[n-i-j]);
ip.y = cp[j]/(cp[i] + cp[j] + cp[n-i-j]);
ip.z = cp[k];
}
ip.x = cp[i]/(cp[i] + cp[j] + cp[n-i-j]);
ip.y = cp[j]/(cp[i] + cp[j] + cp[n-i-j]);
ip.z = cp[k];
m++;
}
if (m != (n+1)*(n+1)*(n+2)/2)
+41 -13
View File
@@ -15,6 +15,10 @@
#include "../mesh/nurbs.hpp"
#include "../general/text.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
#endif
#include <limits>
#include <cstring>
#include <string>
@@ -22,6 +26,7 @@
#include <iostream>
#include <algorithm>
namespace mfem
{
@@ -462,15 +467,26 @@ const
fes->GetElementDofs(i, dofs);
fes->DofsToVDofs(vdim-1, dofs);
const FiniteElement *FElem = fes->GetFE(i);
MFEM_ASSERT(FElem->GetMapType() == FiniteElement::VALUE,
"invalid FE map type");
int dof = FElem->GetDof();
Vector DofVal(dof), loc_data(dof);
GetSubVector(dofs, loc_data);
for (int k = 0; k < n; k++)
if (FElem->GetMapType() == FiniteElement::VALUE)
{
FElem->CalcShape(ir.IntPoint(k), DofVal);
vals(k) = DofVal * loc_data;
for (int k = 0; k < n; k++)
{
FElem->CalcShape(ir.IntPoint(k), DofVal);
vals(k) = DofVal * loc_data;
}
}
else
{
ElementTransformation *Tr = fes->GetElementTransformation(i);
for (int k = 0; k < n; k++)
{
Tr->SetIntPoint(&ir.IntPoint(k));
FElem->CalcPhysShape(*Tr, DofVal);
vals(k) = DofVal * loc_data;
}
}
}
@@ -984,15 +1000,14 @@ void GridFunction::GetVectorValues(ElementTransformation &T,
if (FElem->GetRangeType() == FiniteElement::SCALAR)
{
MFEM_ASSERT(FElem->GetMapType() == FiniteElement::VALUE,
"invalid FE map type");
Vector shape(dof);
int vdim = fes->GetVDim();
vals.SetSize(vdim, nip);
for (int j = 0; j < nip; j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
FElem->CalcShape(ip, shape);
T.SetIntPoint(&ip);
FElem->CalcPhysShape(T, shape);
for (int k = 0; k < vdim; k++)
{
@@ -1550,18 +1565,16 @@ void GridFunction::GetGradient(ElementTransformation &T, Vector &grad) const
{
case ElementTransformation::ELEMENT:
{
const FiniteElement * fe = fes->GetFE(T.ElementNo);
const FiniteElement *fe = fes->GetFE(T.ElementNo);
MFEM_ASSERT(fe->GetMapType() == FiniteElement::VALUE,
"invalid FE map type");
int spaceDim = fes->GetMesh()->SpaceDimension();
int dim = fe->GetDim(), dof = fe->GetDof();
DenseMatrix dshape(dof, dim);
Vector lval, gh(dim);
Array<int> dofs;
grad.SetSize(spaceDim);
fes->GetElementDofs(T.ElementNo, dofs);
GetSubVector(dofs, lval);
GetElementDofValues(T.ElementNo, lval);
fe->CalcDShape(T.GetIntPoint(), dshape);
dshape.MultTranspose(lval, gh);
T.InverseJacobian().MultTranspose(gh, grad);
@@ -1731,6 +1744,13 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
}
}
void GridFunction::GetElementDofValues(int el, Vector &dof_vals) const
{
Array<int> dof_idx;
fes->GetElementVDofs(el, dof_idx);
GetSubVector(dof_idx, dof_vals);
}
void GridFunction::ProjectGridFunction(const GridFunction &src)
{
Mesh *mesh = fes->GetMesh();
@@ -3793,7 +3813,15 @@ double ZZErrorEstimator(BilinearFormIntegrator &blfi,
}
}
}
#ifdef MFEM_USE_MPI
auto pfes = dynamic_cast<ParFiniteElementSpace*>(ufes);
if (pfes)
{
auto process_local_error = total_error;
MPI_Allreduce(&process_local_error, &total_error, 1, MPI_DOUBLE,
MPI_SUM, pfes->GetComm());
}
#endif // MFEM_USE_MPI
return std::sqrt(total_error);
}
+24 -5
View File
@@ -325,6 +325,10 @@ public:
Both FE spaces should be scalar and on the same mesh. */
void GetElementAverages(GridFunction &avgs) const;
/** Sets the output vector @a dof_vals to the values of the degrees of
freedom of element @a el. */
virtual void GetElementDofValues(int el, Vector &dof_vals) const;
/** Impose the given bounds on the function's DOFs while preserving its local
* integral (described in terms of the given weights) on the i'th element
* through SLBPQ optimization.
@@ -345,14 +349,30 @@ public:
projection matrix. */
void ProjectGridFunction(const GridFunction &src);
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
projection computation depends on the choice of the FiniteElementSpace
#fes. Note that this is usually interpolation at the degrees of freedom
in each element (not L2 projection). */
virtual void ProjectCoefficient(Coefficient &coeff);
/** @brief Project @a coeff Coefficient to @a this GridFunction, using one
element for each degree of freedom in @a dofs and nodal interpolation on
that element. */
void ProjectCoefficient(Coefficient &coeff, Array<int> &dofs, int vd = 0);
/** @brief Project @a vcoeff VectorCoefficient to @a this GridFunction. The
projection computation depends on the choice of the FiniteElementSpace
#fes. Note that this is usually interpolation at the degrees of freedom
in each element (not L2 projection).*/
void ProjectCoefficient(VectorCoefficient &vcoeff);
/** @brief Project @a vcoeff VectorCoefficient to @a this GridFunction, using
one element for each degree of freedom in @a dofs and nodal interpolation
on that element. */
void ProjectCoefficient(VectorCoefficient &vcoeff, Array<int> &dofs);
/** @brief Analogous to the version with argument @a vcoeff VectorCoefficient
but using an array of scalar coefficients for each component. */
void ProjectCoefficient(Coefficient *coeff[]);
/** @brief Project a discontinuous vector coefficient as a grid function on
@@ -462,11 +482,10 @@ public:
/// Returns the Face Jumps error for L2 elements, with 1/h scaling.
MFEM_DEPRECATED
virtual double ComputeDGFaceJumpError(Coefficient *exsol,
Coefficient *ell_coeff,
double Nu,
const IntegrationRule *irs[] = NULL)
const;
double ComputeDGFaceJumpError(Coefficient *exsol,
Coefficient *ell_coeff,
double Nu,
const IntegrationRule *irs[] = NULL) const;
/** This method is kept for backward compatibility.
+4
View File
@@ -406,6 +406,10 @@ private:
Vector shape;
public:
BoundaryFlowIntegrator(Coefficient &_f, VectorCoefficient &_u,
double a)
{ f = &_f; u = &_u; alpha = a; beta = 0.5*a; }
BoundaryFlowIntegrator(Coefficient &_f, VectorCoefficient &_u,
double a, double b)
{ f = &_f; u = &_u; alpha = a; beta = b; }
+75 -24
View File
@@ -14,11 +14,40 @@
namespace mfem
{
Multigrid::Multigrid(const FiniteElementSpaceHierarchy& fespaces_)
: fespaces(fespaces_), cycleType(CycleType::VCYCLE), preSmoothingSteps(1),
postSmoothingSteps(1)
Multigrid::Multigrid()
: cycleType(CycleType::VCYCLE), preSmoothingSteps(1), postSmoothingSteps(1)
{}
Multigrid::Multigrid(const Array<Operator*>& operators_,
const Array<Solver*>& smoothers_,
const Array<Operator*>& prolongations_,
const Array<bool>& ownedOperators_,
const Array<bool>& ownedSmoothers_,
const Array<bool>& ownedProlongations_)
: Solver(operators_.Last()->NumRows()), cycleType(CycleType::VCYCLE),
preSmoothingSteps(1), postSmoothingSteps(1),
X(operators_.Size()), Y(X.Size()), R(X.Size()), Z(X.Size())
{
operators_.Copy(operators);
smoothers_.Copy(smoothers);
prolongations_.Copy(prolongations);
ownedOperators_.Copy(ownedOperators);
ownedSmoothers_.Copy(ownedSmoothers);
ownedProlongations_.Copy(ownedProlongations);
for (int level = 0; level < operators.Size(); ++level)
{
X[level] = new Vector(operators[level]->NumRows());
*X[level] = 0.0;
Y[level] = new Vector(operators[level]->NumRows());
*Y[level] = 0.0;
R[level] = new Vector(operators[level]->NumRows());
*R[level] = 0.0;
Z[level] = new Vector(operators[level]->NumRows());
*Z[level] = 0.0;
}
}
Multigrid::~Multigrid()
{
for (int i = 0; i < operators.Size(); ++i)
@@ -37,26 +66,21 @@ Multigrid::~Multigrid()
delete Z[i];
}
for (int i = 0; i < prolongations.Size(); ++i)
{
if (ownedProlongations[i])
{
delete prolongations[i];
}
}
operators.DeleteAll();
smoothers.DeleteAll();
prolongations.DeleteAll();
X.DeleteAll();
Y.DeleteAll();
R.DeleteAll();
Z.DeleteAll();
for (int i = 0; i < bfs.Size(); ++i)
{
delete bfs[i];
}
bfs.DeleteAll();
for (int i = 0; i < essentialTrueDofs.Size(); ++i)
{
delete essentialTrueDofs[i];
}
essentialTrueDofs.DeleteAll();
}
void Multigrid::AddLevel(Operator* opr, Solver* smoother, bool ownOperator,
@@ -168,8 +192,7 @@ void Multigrid::Cycle(int level) const
subtract(*X[level], *R[level], *R[level]);
// Restrict residual
fespaces.GetProlongationAtLevel(level - 1)->MultTranspose(*R[level],
*X[level - 1]);
GetProlongationAtLevel(level - 1)->MultTranspose(*R[level], *X[level - 1]);
// Init zeros
*Y[level - 1] = 0.0;
@@ -186,7 +209,7 @@ void Multigrid::Cycle(int level) const
}
// Prolongate
fespaces.GetProlongationAtLevel(level - 1)->Mult(*Y[level - 1], *R[level]);
GetProlongationAtLevel(level - 1)->Mult(*Y[level - 1], *R[level]);
// Add update
*Y[level] += *R[level];
@@ -198,16 +221,44 @@ void Multigrid::Cycle(int level) const
}
}
void Multigrid::FormFineLinearSystem(Vector& x, Vector& b, OperatorHandle& A,
Vector& X, Vector& B)
const Operator* Multigrid::GetProlongationAtLevel(int level) const
{
return prolongations[level];
}
GeometricMultigrid::~GeometricMultigrid()
{
for (int i = 0; i < bfs.Size(); ++i)
{
delete bfs[i];
}
bfs.DeleteAll();
for (int i = 0; i < essentialTrueDofs.Size(); ++i)
{
delete essentialTrueDofs[i];
}
essentialTrueDofs.DeleteAll();
}
void GeometricMultigrid::FormFineLinearSystem(Vector& x, Vector& b,
OperatorHandle& A,
Vector& X, Vector& B)
{
bfs.Last()->FormLinearSystem(*essentialTrueDofs.Last(), x, b, A, X, B);
}
void Multigrid::RecoverFineFEMSolution(const Vector& X, const Vector& b,
Vector& x)
void GeometricMultigrid::RecoverFineFEMSolution(const Vector& X,
const Vector& b, Vector& x)
{
bfs.Last()->RecoverFEMSolution(X, b, x);
}
const Operator* GeometricMultigrid::GetProlongationAtLevel(int level) const
{
return fespaces.GetProlongationAtLevel(level);
}
} // namespace mfem
+45 -14
View File
@@ -32,16 +32,13 @@ public:
};
protected:
const FiniteElementSpaceHierarchy& fespaces;
Array<Array<int>*> essentialTrueDofs;
Array<BilinearForm*> bfs;
private:
Array<Operator*> operators;
Array<Solver*> smoothers;
Array<Operator*> prolongations;
Array<bool> ownedOperators;
Array<bool> ownedSmoothers;
Array<bool> ownedProlongations;
CycleType cycleType;
int preSmoothingSteps;
@@ -53,8 +50,16 @@ private:
mutable Array<Vector*> Z;
public:
/// Constructs an empty multigrid for the given FiniteElementSpaceHierarchy
Multigrid(const FiniteElementSpaceHierarchy& fespaces_);
/// Constructs an empty multigrid hierarchy.
Multigrid();
/// Constructs a multigrid hierarchy from the given inputs.
/** Inputs include operators and smoothers on all levels, prolongation
operators that go from coarser to finer levels, and ownership of the
given operators, smoothers, and prolongations. */
Multigrid(const Array<Operator*>& operators_, const Array<Solver*>& smoothers_,
const Array<Operator*>& prolongations_, const Array<bool>& ownedOperators_,
const Array<bool>& ownedSmoothers_, const Array<bool>& ownedProlongations_);
/// Destructor
virtual ~Multigrid();
@@ -89,7 +94,7 @@ public:
/// Returns smoother at given level
Solver* GetSmootherAtLevel(int level);
/// Set the cycle type and number of pre- and post-smoothing steps used by Mult
/// Set cycle type and number of pre- and post-smoothing steps used by Mult
void SetCycleType(CycleType cycleType_, int preSmoothingSteps_,
int postSmoothingSteps_);
@@ -99,7 +104,36 @@ public:
/// Not supported for multigrid
virtual void SetOperator(const Operator& op) override;
/// Form the linear system A X = B, corresponding to the operator on the finest level
private:
/// Application of a smoothing step at particular level
void SmoothingStep(int level, bool transpose) const;
/// Application of a multigrid cycle at particular level
void Cycle(int level) const;
/// Returns prolongation operator at given level
virtual const Operator* GetProlongationAtLevel(int level) const;
};
/// Geometric multigrid associated with a hierarchy of finite element spaces
class GeometricMultigrid : public Multigrid
{
protected:
const FiniteElementSpaceHierarchy& fespaces;
Array<Array<int>*> essentialTrueDofs;
Array<BilinearForm*> bfs;
public:
/** Construct an empty multigrid object for the given finite element space
hierarchy @a fespaces_ */
GeometricMultigrid(const FiniteElementSpaceHierarchy& fespaces_)
: Multigrid(), fespaces(fespaces_) { }
/// Destructor
virtual ~GeometricMultigrid();
/** Form the linear system A X = B, corresponding to the operator on the
finest level of the geometric multigrid hierarchy */
void FormFineLinearSystem(Vector& x, Vector& b, OperatorHandle& A, Vector& X,
Vector& B);
@@ -107,11 +141,8 @@ public:
void RecoverFineFEMSolution(const Vector& X, const Vector& b, Vector& x);
private:
/// Application of a smoothing step at particular level
void SmoothingStep(int level, bool transpose) const;
/// Application of a cycle at particular level
void Cycle(int level) const;
/// Returns prolongation operator at given level
virtual const Operator* GetProlongationAtLevel(int level) const override;
};
} // namespace mfem
+86
View File
@@ -128,6 +128,92 @@ double BlockNonlinearFormIntegrator::GetElementEnergy(
return 0.0;
}
double PrmBlockNonlinearFormIntegrator::GetElementEnergy(const Array<const FiniteElement *> &el,
const Array<const FiniteElement *> &pel,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *> &pelfun)
{
mfem_error("PrmBlockNonlinearFormIntegrator::GetElementEnergy"
" is not overloaded!");
return 0.0;
}
void PrmBlockNonlinearFormIntegrator::AssembleFaceGrad(const Array<const FiniteElement *> &el1,
const Array<const FiniteElement *> &el2,
const Array<const FiniteElement *> &pel1,
const Array<const FiniteElement *> &pel2,
FaceElementTransformations &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *> &pelfun,
const Array2D<DenseMatrix *> &elmats)
{
mfem_error("PrmBlockNonlinearFormIntegrator::AssembleFaceGrad"
" is not overloaded!");
}
void PrmBlockNonlinearFormIntegrator::AssembleElementGrad(const Array<const FiniteElement *> &el,
const Array<const FiniteElement *> &pel,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *> &pelfun,
const Array2D<DenseMatrix *> &elmats)
{
mfem_error("PrmBlockNonlinearFormIntegrator::AssembleElementGrad"
" is not overloaded!");
}
void PrmBlockNonlinearFormIntegrator::AssembleElementVector(const Array<const FiniteElement *> &el,
const Array<const FiniteElement *> &pel,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *> &pelfun,
const Array<Vector *> &elvec)
{
mfem_error("PrmBlockNonlinearFormIntegrator::AssembleElementVector"
" is not overloaded!");
}
void PrmBlockNonlinearFormIntegrator::AssembleFaceVector(const Array<const FiniteElement *> &el1,
const Array<const FiniteElement *> &el2,
const Array<const FiniteElement *> &pel1,
const Array<const FiniteElement *> &pel2,
FaceElementTransformations &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *> &pelfun,
const Array<Vector *> &elvect)
{
mfem_error("PrmBlockNonlinearFormIntegrator::AssembleFaceVector"
" is not overloaded!");
}
void PrmBlockNonlinearFormIntegrator::AssemblePrmElementVector(const Array<const FiniteElement *> &el,
const Array<const FiniteElement *> &pel,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *> &alfun,
const Array<const Vector *> &pelfun,
const Array<Vector *> &elvec)
{
mfem_error("PrmBlockNonlinearFormIntegrator::AssemblePrmElementVector"
" is not overloaded!");
}
void PrmBlockNonlinearFormIntegrator::AssemblePrmFaceVector(const Array<const FiniteElement *> &el1,
const Array<const FiniteElement *> &el2,
const Array<const FiniteElement *> &pel1,
const Array<const FiniteElement *> &pel2,
FaceElementTransformations &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *> &alfun,
const Array<const Vector *> &pelfun,
const Array<Vector *> &elvect)
{
mfem_error("PrmBlockNonlinearFormIntegrator::AssemblePrmFaceVector"
" is not overloaded!");
}
double InverseHarmonicModel::EvalW(const DenseMatrix &J) const
{
+74
View File
@@ -130,6 +130,80 @@ public:
};
/** The abstract base class PrmBlockNonlinearFormIntegrator is
a generalization of the BlockNonlinearFormIntegrator class suitable
for block state and parameter vectors. */
class PrmBlockNonlinearFormIntegrator
{
public:
/// Compute the local energy
virtual double GetElementEnergy(const Array<const FiniteElement *>&el,
const Array<const FiniteElement *>&pel,
ElementTransformation &Tr,
const Array<const Vector *>&elfun,
const Array<const Vector *>&pelfun);
/// Perform the local action of the BlockNonlinearFormIntegrator
virtual void AssembleElementVector(const Array<const FiniteElement *> &el,
const Array<const FiniteElement *>&pel,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *>&pelfun,
const Array<Vector *> &elvec);
virtual void AssembleFaceVector(const Array<const FiniteElement *> &el1,
const Array<const FiniteElement *> &el2,
const Array<const FiniteElement *> &pel1,
const Array<const FiniteElement *> &pel2,
FaceElementTransformations &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *>&pelfun,
const Array<Vector *> &elvect);
/// Perform the local action on the parameters of the BlockNonlinearFormIntegrator
virtual void AssemblePrmElementVector(const Array<const FiniteElement *> &el,
const Array<const FiniteElement *>&pel,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *> &alfun,
const Array<const Vector *>&pelfun,
const Array<Vector *> &elvec);
virtual void AssemblePrmFaceVector(const Array<const FiniteElement *> &el1,
const Array<const FiniteElement *> &el2,
const Array<const FiniteElement *> &pel1,
const Array<const FiniteElement *> &pel2,
FaceElementTransformations &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *> &alfun,
const Array<const Vector *>&pelfun,
const Array<Vector *> &elvect);
/// Assemble the local gradient matrix
virtual void AssembleElementGrad(const Array<const FiniteElement*> &el,
const Array<const FiniteElement *>&pel,
ElementTransformation &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *>&pelfun,
const Array2D<DenseMatrix *> &elmats);
virtual void AssembleFaceGrad(const Array<const FiniteElement *>&el1,
const Array<const FiniteElement *>&el2,
const Array<const FiniteElement *> &pel1,
const Array<const FiniteElement *> &pel2,
FaceElementTransformations &Tr,
const Array<const Vector *> &elfun,
const Array<const Vector *>&pelfun,
const Array2D<DenseMatrix *> &elmats);
virtual ~PrmBlockNonlinearFormIntegrator() { }
};
/// Abstract class for hyperelastic models
class HyperelasticModel
{
+32
View File
@@ -565,6 +565,38 @@ 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 R_test_transpose(A.Type()), P_trial(A.Type());
// TODO - construct the Dof_TrueDof_Matrix directly in the required format.
R_test_transpose.ConvertFrom(range_fes->Dof_TrueDof_Matrix());
P_trial.ConvertFrom(domain_fes->Dof_TrueDof_Matrix());
A.MakeRAP(R_test_transpose, dA, P_trial);
}
void ParDiscreteLinearOperator::FormRectangularSystemMatrix(OperatorHandle &A)
{
if (ext)
{
Array<int> empty;
ext->FormRectangularSystemOperator(empty, empty, A);
return;
}
mfem_error("not implemented!");
}
void ParDiscreteLinearOperator::GetParBlocks(Array2D<HypreParMatrix *> &blocks)
const
{
+16
View File
@@ -160,6 +160,9 @@ public:
/// Get the parallel finite element space prolongation matrix
virtual const Operator *GetProlongation() const
{ return pfes->GetProlongationMatrix(); }
/// Get the transpose of GetRestriction, useful for matrix-free RAP
virtual const Operator *GetRestrictionTranspose() const
{ return pfes->GetRestrictionTransposeOperator(); }
/// Get the parallel finite element space restriction matrix
virtual const Operator *GetRestriction() const
{ return pfes->GetRestrictionMatrix(); }
@@ -246,6 +249,9 @@ public:
@a A. */
void ParallelAssemble(OperatorHandle &A);
using MixedBilinearForm::FormRectangularSystemMatrix;
using MixedBilinearForm::FormRectangularLinearSystem;
/** @brief Return in @a A a parallel (on truedofs) version of this operator.
This returns the same operator as FormRectangularLinearSystem(), but does
@@ -301,10 +307,20 @@ 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 = R_test 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;
using MixedBilinearForm::FormRectangularSystemMatrix;
/** @brief Return in @a A a parallel (on truedofs) version of this operator. */
virtual void FormRectangularSystemMatrix(OperatorHandle &A);
virtual ~ParDiscreteLinearOperator() { }
};
+132 -49
View File
@@ -101,6 +101,8 @@ void ParFiniteElementSpace::ParInit(ParMesh *pm)
P = NULL;
Pconf = NULL;
Rconf = NULL;
R_transpose = NULL;
R = NULL;
num_face_nbr_dofs = -1;
@@ -499,6 +501,12 @@ void ParFiniteElementSpace::GetFaceDofs(int i, Array<int> &dofs) const
}
}
const FiniteElement *ParFiniteElementSpace::GetFE(int i) const
{
int ne = mesh->GetNE();
if (i >= ne) { return GetFaceNbrFE(i - ne); }
else { return FiniteElementSpace::GetFE(i); }
}
const Operator *ParFiniteElementSpace::GetFaceRestriction(
ElementDofOrdering e_ordering, FaceType type, L2FaceValues mul) const
@@ -921,6 +929,45 @@ const Operator *ParFiniteElementSpace::GetProlongationMatrix() const
}
}
const Operator *ParFiniteElementSpace::GetRestrictionOperator() const
{
if (Conforming())
{
if (Rconf) { return Rconf; }
if (NRanks == 1)
{
R_transpose = new IdentityOperator(GetTrueVSize());
}
else
{
if (!Device::Allows(Backend::DEVICE_MASK))
{
R_transpose = new ConformingProlongationOperator(*this, true);
}
else
{
R_transpose =
new DeviceConformingProlongationOperator(*this, true);
}
}
Rconf = new TransposeOperator(R_transpose);
return Rconf;
}
else
{
Dof_TrueDof_Matrix();
R_transpose = new TransposeOperator(R);
return R;
}
}
const Operator *ParFiniteElementSpace::GetRestrictionTransposeOperator() const
{
GetRestrictionOperator();
return R_transpose;
}
void ParFiniteElementSpace::ExchangeFaceNbrData()
{
if (num_face_nbr_dofs >= 0) { return; }
@@ -2834,6 +2881,8 @@ void ParFiniteElementSpace::Destroy()
delete P; P = NULL;
delete Pconf; Pconf = NULL;
delete Rconf; Rconf = NULL;
delete R_transpose; R_transpose = NULL;
delete R; R = NULL;
delete gcomm; gcomm = NULL;
@@ -2959,12 +3008,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 +3062,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 +3081,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 +3097,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,13 +3112,18 @@ 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(
const ParFiniteElementSpace &pfes) :
const ParFiniteElementSpace &pfes,
bool local_) :
ConformingProlongationOperator(pfes),
mpi_gpu_aware(Device::GetGPUAwareMPI())
mpi_gpu_aware(Device::GetGPUAwareMPI()),
local(local_)
{
MFEM_ASSERT(pfes.Conforming(), "internal error");
const SparseMatrix *R = pfes.GetRestrictionMatrix();
@@ -3173,32 +3240,42 @@ void DeviceConformingProlongationOperator::Mult(const Vector &x,
Vector &y) const
{
const GroupTopology &gtopo = gc.GetGroupTopology();
BcastBeginCopy(x); // copy to 'shr_buf'
int req_counter = 0;
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
if (local)
{
const int send_offset = shr_buf_offsets[nbr];
const int send_size = shr_buf_offsets[nbr+1] - send_offset;
if (send_size > 0)
y = 0.0;
}
else
{
BcastBeginCopy(x); // copy to 'shr_buf'
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
{
auto send_buf = mpi_gpu_aware ? shr_buf.Read() : shr_buf.HostRead();
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
}
const int recv_offset = ext_buf_offsets[nbr];
const int recv_size = ext_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
auto recv_buf = mpi_gpu_aware ? ext_buf.Write() : ext_buf.HostWrite();
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
const int send_offset = shr_buf_offsets[nbr];
const int send_size = shr_buf_offsets[nbr+1] - send_offset;
if (send_size > 0)
{
auto send_buf = mpi_gpu_aware ? shr_buf.Read() : shr_buf.HostRead();
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
}
const int recv_offset = ext_buf_offsets[nbr];
const int recv_size = ext_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
auto recv_buf = mpi_gpu_aware ? ext_buf.Write() : ext_buf.HostWrite();
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
}
}
}
BcastLocalCopy(x, y);
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
BcastEndCopy(y); // copy from 'ext_buf'
if (!local)
{
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
BcastEndCopy(y); // copy from 'ext_buf'
}
}
DeviceConformingProlongationOperator::~DeviceConformingProlongationOperator()
@@ -3261,32 +3338,38 @@ void DeviceConformingProlongationOperator::MultTranspose(const Vector &x,
Vector &y) const
{
const GroupTopology &gtopo = gc.GetGroupTopology();
ReduceBeginCopy(x); // copy to 'ext_buf'
int req_counter = 0;
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
if (!local)
{
const int send_offset = ext_buf_offsets[nbr];
const int send_size = ext_buf_offsets[nbr+1] - send_offset;
if (send_size > 0)
ReduceBeginCopy(x); // copy to 'ext_buf'
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
{
auto send_buf = mpi_gpu_aware ? ext_buf.Read() : ext_buf.HostRead();
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
}
const int recv_offset = shr_buf_offsets[nbr];
const int recv_size = shr_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
auto recv_buf = mpi_gpu_aware ? shr_buf.Write() : shr_buf.HostWrite();
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
const int send_offset = ext_buf_offsets[nbr];
const int send_size = ext_buf_offsets[nbr+1] - send_offset;
if (send_size > 0)
{
auto send_buf = mpi_gpu_aware ? ext_buf.Read() : ext_buf.HostRead();
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
}
const int recv_offset = shr_buf_offsets[nbr];
const int recv_size = shr_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
auto recv_buf = mpi_gpu_aware ? shr_buf.Write() : shr_buf.HostWrite();
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
}
}
}
ReduceLocalCopy(x, y);
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
ReduceEndAssemble(y); // assemble from 'shr_buf'
if (!local)
{
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
ReduceEndAssemble(y); // assemble from 'shr_buf'
}
}
} // namespace mfem
+29 -2
View File
@@ -75,6 +75,12 @@ private:
/// The (block-diagonal) matrix R (restriction of dof to true dof). Owned.
mutable SparseMatrix *R;
/// Optimized action-only restriction operator for conforming meshes. Owned.
mutable Operator *Rconf;
/** Transpose of R or Rconf. For conforming mesh, this is a matrix-free
(Device)ConformingProlongationOperator, for a non-conforming mesh
this is a TransposeOperator wrapping R. */
mutable Operator *R_transpose;
ParNURBSExtension *pNURBSext() const
{ return dynamic_cast<ParNURBSExtension *>(NURBSext); }
@@ -264,6 +270,12 @@ public:
including the dofs for the edges and the vertices of the face. */
virtual void GetFaceDofs(int i, Array<int> &dofs) const;
/** Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th element in the mesh object. If @a i is greater than
or equal to the number of local mesh elements, @a i will be interpreted
as a shifted index of a face neigbor element. */
virtual const FiniteElement *GetFE(int i) const;
/** Returns an Operator that converts L-vectors to E-vectors on each face.
The parallel version is different from the serial one because of the
presence of shared faces. Shared faces are treated as interior faces,
@@ -335,6 +347,16 @@ public:
HYPRE_Int GetMyTDofOffset() const;
virtual const Operator *GetProlongationMatrix() const;
/** @brief Return logical transpose of restriction matrix, but in
non-assembled optimized matrix-free form.
The implementation is like GetProlongationMatrix, but it sets local
DOFs to the true DOF values if owned locally, otherwise zero. */
virtual const Operator *GetRestrictionTransposeOperator() const;
/** Get an Operator that performs the action of GetRestrictionMatrix(),
but potentially with a non-assembled optimized matrix-free
implementation. */
virtual const Operator *GetRestrictionOperator() 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 +411,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;
@@ -410,6 +434,8 @@ protected:
Array<int> ltdof_ldof, unq_ltdof;
Array<int> unq_shr_i, unq_shr_j;
MPI_Request *requests;
bool local;
// Kernel: copy ltdofs from 'src' to 'shr_buf' - prepare for send.
// shr_buf[i] = src[shr_ltdof[i]]
void BcastBeginCopy(const Vector &src) const;
@@ -435,7 +461,8 @@ protected:
void ReduceEndAssemble(Vector &dst) const;
public:
DeviceConformingProlongationOperator(const ParFiniteElementSpace &pfes);
DeviceConformingProlongationOperator(const ParFiniteElementSpace &pfes,
bool local_=false);
virtual ~DeviceConformingProlongationOperator();
+19
View File
@@ -471,6 +471,25 @@ void ParGridFunction::GetVectorValue(ElementTransformation &T,
}
}
void ParGridFunction::GetElementDofValues(int el, Vector &dof_vals) const
{
int ne = fes->GetNE();
if (el >= ne)
{
MFEM_ASSERT(face_nbr_data.Size() > 0,
"ParGridFunction::GetElementDofValues: ExchangeFaceNbrData "
"must be called before accessing face neighbor elements.");
// Face neighbor element
Array<int> dof_idx;
pfes->GetFaceNbrElementVDofs(el - ne, dof_idx);
face_nbr_data.GetSubVector(dof_idx, dof_vals);
}
else
{
GridFunction::GetElementDofValues(el, dof_vals);
}
}
void ParGridFunction::ProjectCoefficient(Coefficient &coeff)
{
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
+6
View File
@@ -221,6 +221,12 @@ public:
const IntegrationPoint &ip,
Vector &val, Vector *tr = NULL) const;
/** Sets the output vector @a dof_vals to the values of the degrees of
freedom of element @a el. If @a el is greater than or equal to the number
of local elements, it will be interpreted as a shifted index of a face
neighbor element. */
virtual void GetElementDofValues(int el, Vector &dof_vals) const;
using GridFunction::ProjectCoefficient;
virtual void ProjectCoefficient(Coefficient &coeff);
+363
View File
@@ -0,0 +1,363 @@
// 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 "../config/config.hpp"
#ifdef MFEM_USE_MPI
#include "fem.hpp"
namespace mfem
{
ParPrmBlockNonlinearForm::ParPrmBlockNonlinearForm(Array<ParFiniteElementSpace *> &pf,
Array<ParFiniteElementSpace *> &ppf)
:PrmBlockNonlinearForm()
{
pBlockGrad = nullptr;
SetParSpaces(pf,ppf);
}
void ParPrmBlockNonlinearForm::SetParSpaces(Array<ParFiniteElementSpace *> &pf,
Array<ParFiniteElementSpace *> &pprmf)
{
delete pBlockGrad;
pBlockGrad = nullptr;
for (int s1=0; s1<fes.Size(); ++s1)
{
for (int s2=0; s2<fes.Size(); ++s2)
{
delete phBlockGrad(s1,s2);
}
}
Array<FiniteElementSpace *> serialSpaces(pf.Size());
Array<FiniteElementSpace *> prmserialSpaces(pprmf.Size());
for (int s=0; s<pf.Size(); s++)
{
serialSpaces[s] = (FiniteElementSpace *) pf[s];
}
for (int s=0; s<pprmf.Size(); s++)
{
prmserialSpaces[s] = (FiniteElementSpace *) pprmf[s];
}
SetSpaces(serialSpaces,prmserialSpaces);
phBlockGrad.SetSize(fes.Size(), fes.Size());
for (int s1=0; s1<fes.Size(); ++s1)
{
for (int s2=0; s2<fes.Size(); ++s2)
{
phBlockGrad(s1,s2) = new OperatorHandle(Operator::Hypre_ParCSR);
}
}
}
ParFiniteElementSpace * ParPrmBlockNonlinearForm::ParFESpace(int k)
{
return (ParFiniteElementSpace *)fes[k];
}
const ParFiniteElementSpace *ParPrmBlockNonlinearForm::ParFESpace(int k) const
{
return (const ParFiniteElementSpace *)fes[k];
}
ParFiniteElementSpace * ParPrmBlockNonlinearForm::ParPrmFESpace(int k)
{
return (ParFiniteElementSpace *)prmfes[k];
}
const ParFiniteElementSpace *ParPrmBlockNonlinearForm::ParPrmFESpace(int k) const
{
return (const ParFiniteElementSpace *)prmfes[k];
}
// Here, rhs is a true dof vector
void ParPrmBlockNonlinearForm::SetEssentialBC(const
Array<Array<int> *>&bdr_attr_is_ess,
Array<Vector *> &rhs)
{
Array<Vector *> nullarray(fes.Size());
nullarray = NULL;
PrmBlockNonlinearForm::SetEssentialBC(bdr_attr_is_ess, nullarray);
for (int s = 0; s < fes.Size(); ++s)
{
if (rhs[s])
{
rhs[s]->SetSubVector(*ess_tdofs[s], 0.0);
}
}
}
void ParPrmBlockNonlinearForm::SetPrmEssentialBC(const
Array<Array<int> *>&bdr_attr_is_ess,
Array<Vector *> &rhs)
{
Array<Vector *> nullarray(fes.Size());
nullarray = NULL;
PrmBlockNonlinearForm::SetPrmEssentialBC(bdr_attr_is_ess, nullarray);
for (int s = 0; s < prmfes.Size(); ++s)
{
if (rhs[s])
{
rhs[s]->SetSubVector(*prmess_tdofs[s], 0.0);
}
}
}
double ParPrmBlockNonlinearForm::GetEnergy(const Vector &x) const
{
xs_true.Update(x.GetData(), block_trueOffsets);
xs.Update(block_offsets);
for (int s = 0; s < fes.Size(); ++s)
{
fes[s]->GetProlongationMatrix()->Mult(xs_true.GetBlock(s), xs.GetBlock(s));
}
double enloc = PrmBlockNonlinearForm::GetEnergyBlocked(xs,xdv);
double englo = 0.0;
MPI_Allreduce(&enloc, &englo, 1, MPI_DOUBLE, MPI_SUM,
ParFESpace(0)->GetComm());
return englo;
}
void ParPrmBlockNonlinearForm::Mult(const Vector &x, Vector &y) const
{
xs_true.Update(x.GetData(), block_trueOffsets);
ys_true.Update(y.GetData(), block_trueOffsets);
xs.Update(block_offsets);
ys.Update(block_offsets);
for (int s=0; s<fes.Size(); ++s)
{
fes[s]->GetProlongationMatrix()->Mult(
xs_true.GetBlock(s), xs.GetBlock(s));
}
PrmBlockNonlinearForm::MultBlocked(xs, xdv, ys);
if (fnfi.Size() > 0)
{
MFEM_ABORT("TODO: assemble contributions from shared face terms");
}
for (int s=0; s<fes.Size(); ++s)
{
fes[s]->GetProlongationMatrix()->MultTranspose(
ys.GetBlock(s), ys_true.GetBlock(s));
ys_true.GetBlock(s).SetSubVector(*ess_tdofs[s], 0.0);
}
}
/// Block T-Vector to Block T-Vector
void ParPrmBlockNonlinearForm::PrmMult(const Vector &x, Vector &y) const
{
xs_true.Update(x.GetData(), prmblock_trueOffsets);
ys_true.Update(y.GetData(), prmblock_trueOffsets);
prmxs.Update(prmblock_offsets);
prmys.Update(prmblock_offsets);
for (int s=0; s<prmfes.Size(); ++s)
{
prmfes[s]->GetProlongationMatrix()->Mult(
xs_true.GetBlock(s), prmxs.GetBlock(s));
}
PrmBlockNonlinearForm::MultPrmBlocked(xsv,adv,xdv,prmys);
if (fnfi.Size() > 0)
{
MFEM_ABORT("TODO: assemble contributions from shared face terms");
}
for (int s=0; s<prmfes.Size(); ++s)
{
prmfes[s]->GetProlongationMatrix()->MultTranspose(
prmys.GetBlock(s), ys_true.GetBlock(s));
ys_true.GetBlock(s).SetSubVector(*prmess_tdofs[s], 0.0);
}
}
/// Return the local gradient matrix for the given true-dof vector x
const BlockOperator & ParPrmBlockNonlinearForm::GetLocalGradient(
const Vector &x) const
{
xs_true.Update(x.GetData(), block_trueOffsets);
xs.Update(block_offsets);
for (int s=0; s<fes.Size(); ++s)
{
fes[s]->GetProlongationMatrix()->Mult(
xs_true.GetBlock(s), xs.GetBlock(s));
}
PrmBlockNonlinearForm::ComputeGradientBlocked(xs,xdv); // (re)assemble Grad with b.c.
delete BlockGrad;
BlockGrad = new BlockOperator(block_offsets);
for (int i = 0; i < fes.Size(); ++i)
{
for (int j = 0; j < fes.Size(); ++j)
{
BlockGrad->SetBlock(i, j, Grads(i, j));
}
}
return *BlockGrad;
}
// Set the operator type id for the parallel gradient matrix/operator.
void ParPrmBlockNonlinearForm::SetGradientType(Operator::Type tid)
{
for (int s1=0; s1<fes.Size(); ++s1)
{
for (int s2=0; s2<fes.Size(); ++s2)
{
phBlockGrad(s1,s2)->SetType(tid);
}
}
}
BlockOperator & ParPrmBlockNonlinearForm::GetGradient(const Vector &x) const
{
if (pBlockGrad == NULL)
{
pBlockGrad = new BlockOperator(block_trueOffsets);
}
Array<const ParFiniteElementSpace *> pfes(fes.Size());
for (int s1=0; s1<fes.Size(); ++s1)
{
pfes[s1] = ParFESpace(s1);
for (int s2=0; s2<fes.Size(); ++s2)
{
phBlockGrad(s1,s2)->Clear();
}
}
GetLocalGradient(x); // gradients are stored in 'Grads'
if (fnfi.Size() > 0)
{
MFEM_ABORT("TODO: assemble contributions from shared face terms");
}
for (int s1=0; s1<fes.Size(); ++s1)
{
for (int s2=0; s2<fes.Size(); ++s2)
{
OperatorHandle dA(phBlockGrad(s1,s2)->Type()),
Ph(phBlockGrad(s1,s2)->Type()),
Rh(phBlockGrad(s1,s2)->Type());
if (s1 == s2)
{
dA.MakeSquareBlockDiag(pfes[s1]->GetComm(), pfes[s1]->GlobalVSize(),
pfes[s1]->GetDofOffsets(), Grads(s1,s1));
Ph.ConvertFrom(pfes[s1]->Dof_TrueDof_Matrix());
phBlockGrad(s1,s1)->MakePtAP(dA, Ph);
OperatorHandle Ae;
Ae.EliminateRowsCols(*phBlockGrad(s1,s1), *ess_tdofs[s1]);
}
else
{
dA.MakeRectangularBlockDiag(pfes[s1]->GetComm(),
pfes[s1]->GlobalVSize(),
pfes[s2]->GlobalVSize(),
pfes[s1]->GetDofOffsets(),
pfes[s2]->GetDofOffsets(),
Grads(s1,s2));
Rh.ConvertFrom(pfes[s1]->Dof_TrueDof_Matrix());
Ph.ConvertFrom(pfes[s2]->Dof_TrueDof_Matrix());
phBlockGrad(s1,s2)->MakeRAP(Rh, dA, Ph);
phBlockGrad(s1,s2)->EliminateRows(*ess_tdofs[s1]);
phBlockGrad(s1,s2)->EliminateCols(*ess_tdofs[s2]);
}
pBlockGrad->SetBlock(s1, s2, phBlockGrad(s1,s2)->Ptr());
}
}
return *pBlockGrad;
}
ParPrmBlockNonlinearForm::~ParPrmBlockNonlinearForm()
{
delete pBlockGrad;
for (int s1=0; s1<fes.Size(); ++s1)
{
for (int s2=0; s2<fes.Size(); ++s2)
{
delete phBlockGrad(s1,s2);
}
}
}
void ParPrmBlockNonlinearForm::SetStateFields(const Vector &xv) const
{
xs_true.Update(xv.GetData(), block_trueOffsets);
xsv.Update(block_offsets);
for (int s=0; s<fes.Size(); ++s)
{
fes[s]->GetProlongationMatrix()->Mult(
xs_true.GetBlock(s), xsv.GetBlock(s));
}
}
void ParPrmBlockNonlinearForm::SetAdjointFields(const Vector &av) const
{
xs_true.Update(av.GetData(), block_trueOffsets);
adv.Update(block_offsets);
for (int s=0; s<fes.Size(); ++s)
{
fes[s]->GetProlongationMatrix()->Mult(
xs_true.GetBlock(s), adv.GetBlock(s));
}
}
void ParPrmBlockNonlinearForm::SetPrmFields(const Vector &dv) const
{
xs_true.Update(dv.GetData(),prmblock_trueOffsets);
xdv.Update(prmblock_offsets);
for (int s=0; s<prmfes.Size(); ++s)
{
prmfes[s]->GetProlongationMatrix()->Mult(
xs_true.GetBlock(s), xdv.GetBlock(s));
}
}
}
#endif
+105
View File
@@ -0,0 +1,105 @@
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_PPRMNONLINEARFORM
#define MFEM_PPRMNONLINEARFORM
#include "../config/config.hpp"
#ifdef MFEM_USE_MPI
#include "pgridfunc.hpp"
#include "prmnonlinearform.hpp"
namespace mfem
{
/** @brief A class representing a general parametric parallel block nonlinear operator
defined on the Cartesian product of multiple ParFiniteElementSpace%s. */
/** The ParPrmBlockNonlinearForm takes as input, and returns as output, vectors on
the true dofs. */
class ParPrmBlockNonlinearForm : public PrmBlockNonlinearForm
{
protected:
mutable BlockVector xs_true, ys_true;
mutable Array2D<OperatorHandle *> phBlockGrad;
mutable BlockOperator *pBlockGrad;
public:
/// Computes the energy of the system
virtual double GetEnergy(const Vector &x) const;
/// Construct an empty ParPrmBlockNonlinearForm. Initialize with SetParSpaces().
ParPrmBlockNonlinearForm() : pBlockGrad(nullptr) { }
/** @brief Construct a ParPrmBlockNonlinearForm on the given set of
parametric and state ParFiniteElementSpace%s. */
ParPrmBlockNonlinearForm(Array<ParFiniteElementSpace *> &pf, Array<ParFiniteElementSpace *> &ppf );
/// Return the @a k-th parallel FE state space of the ParPrmBlockNonlinearForm.
ParFiniteElementSpace *ParFESpace(int k);
/** @brief Return the @a k-th parallel FE state space of the ParPrmBlockNonlinearForm
(const version). */
const ParFiniteElementSpace *ParFESpace(int k) const;
/// Return the @a k-th parallel FE parameters space of the ParPrmBlockNonlinearForm.
ParFiniteElementSpace *ParPrmFESpace(int k);
/** @brief Return the @a k-th parallel FE parameters space of the ParPrmBlockNonlinearForm
(const version). */
const ParFiniteElementSpace *ParPrmFESpace(int k) const;
/** @brief After a call to SetParSpaces(), the essential b.c. and the
gradient-type (if different from the default) must be set again. */
void SetParSpaces(Array<ParFiniteElementSpace *> &pf, Array<ParFiniteElementSpace *> &pprmf);
// Here, rhs is a true dof vector
virtual void SetEssentialBC(const Array<Array<int> *>&bdr_attr_is_ess,
Array<Vector *> &rhs);
// Here, rhs is a true dof vector
virtual void SetPrmEssentialBC(const Array<Array<int> *>&bdr_attr_is_ess,
Array<Vector *> &rhs);
/// Block T-Vector to Block T-Vector
virtual void Mult(const Vector &x, Vector &y) const;
/// Block T-Vector to Block T-Vector
virtual void PrmMult(const Vector &x, Vector &y) const;
/// Return the local block gradient matrix for the given true-dof vector x
const BlockOperator &GetLocalGradient(const Vector &x) const;
virtual BlockOperator &GetGradient(const Vector &x) const;
/** @brief Set the operator type id for the blocks of the parallel gradient
matrix/operator. The default type is Operator::Hypre_ParCSR. */
void SetGradientType(Operator::Type tid);
/// Destructor.
virtual ~ParPrmBlockNonlinearForm();
/// Set the state fields
virtual void SetStateFields(const Vector &xv) const;
/// Set the adjoint fields
virtual void SetAdjointFields(const Vector &av) const;
/// Set the parameters/design fields
virtual void SetPrmFields(const Vector &dv) const;
};
}
#endif
#endif
File diff suppressed because it is too large Load Diff
+231
View File
@@ -0,0 +1,231 @@
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_PRMNONLINEARFORM
#define MFEM_PRMNONLINEARFORM
#include "../config/config.hpp"
#include "nonlininteg.hpp"
#include "nonlinearform_ext.hpp"
#include "bilinearform.hpp"
#include "gridfunc.hpp"
namespace mfem
{
/** @brief A class representing a general parametric block nonlinear operator
defined on the Cartesian product of multiple FiniteElementSpace%s. */
class PrmBlockNonlinearForm : public Operator
{
protected:
/// FE spaces on which the form lives.
Array<FiniteElementSpace*> fes;
/// FE spaces for the parametric fields
Array<FiniteElementSpace*> prmfes;
int prmheight;
int prmwidth;
/// Set of Domain Integrators to be assembled (added).
Array<PrmBlockNonlinearFormIntegrator*> dnfi;
/// Set of interior face Integrators to be assembled (added).
Array<PrmBlockNonlinearFormIntegrator*> fnfi;
/// Set of Boundary Face Integrators to be assembled (added).
Array<PrmBlockNonlinearFormIntegrator*> bfnfi;
Array<Array<int>*> bfnfi_marker;
/** Auxiliary block-vectors for wrapping input and output vectors or holding
GridFunction-like block-vector data (e.g. in parallel). */
mutable BlockVector xs, ys;
mutable BlockVector prmxs, prmys;
/** Auxiliary block-vectors for holding
GridFunction-like block-vector data (e.g. in parallel). */
mutable BlockVector xsv;
/** Auxiliary block-vectors for holding
GridFunction-like block-vector data for the parameter fields
(e.g. in parallel). */
mutable BlockVector xdv;
/** Auxiliary block-vectors for holding
GridFunction-like block-vector data for the adjoint fields
(e.g. in parallel). */
mutable BlockVector adv;
mutable Array2D<SparseMatrix*> Grads, cGrads;
mutable BlockOperator *BlockGrad;
// A list of the offsets
Array<int> block_offsets;
Array<int> block_trueOffsets;
// A list with the offsets for the parametric fields
Array<int> prmblock_offsets;
Array<int> prmblock_trueOffsets;
// Array of Arrays of tdofs for each space in 'fes'
Array<Array<int> *> ess_tdofs;
// Array of Arrays of tdofs for each space in 'prmfes'
Array<Array<int> *> prmess_tdofs;
/// Array of pointers to the prolongation matrix of fes, may be NULL
Array<const Operator *> P;
/// Array of pointers to the prolongation matrix of prmfes, may be NULL
Array<const Operator *> Pprm;
/// Array of results of dynamic-casting P to SparseMatrix pointer
Array<const SparseMatrix *> cP;
/// Array of results of dynamic-casting Pprm to SparseMatrix pointer
Array<const SparseMatrix *> cPprm;
/// 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;
/// Indicator if the Operator needs prolongation on assembly
bool prmneeds_prolongation = false;
mutable BlockVector aux1, aux2;
mutable BlockVector prmaux1, prmaux2;
const BlockVector &Prolongate(const BlockVector &bx) const;
const BlockVector &PrmProlongate(const BlockVector &bx) const;
/// Specialized version of GetEnergy() for BlockVectors
//double GetEnergyBlocked(const BlockVector &bx) const;
double GetEnergyBlocked(const BlockVector &bx, const BlockVector &dx) const;
/// Specialized version of Mult() for BlockVector%s
/// Block L-Vector to Block L-Vector
void MultBlocked(const BlockVector &bx, const BlockVector &dx, BlockVector &by) const;
/// Specialized version of Mult() for BlockVector%s
/// Block L-Vector to Block L-Vector
/// bx - state vector, ax - adjoint vector, dx - parametric fields
/// dy = ax' d(residual(bx))/d(dx)
void MultPrmBlocked(const BlockVector &bx, const BlockVector & ax, const BlockVector &dx, BlockVector &dy) const;
/// Specialized version of GetGradient() for BlockVector
//void ComputeGradientBlocked(const BlockVector &bx) const;
void ComputeGradientBlocked(const BlockVector &bx, const BlockVector &dx) const;
public:
/// Construct an empty BlockNonlinearForm. Initialize with SetSpaces().
PrmBlockNonlinearForm();
/// Construct a BlockNonlinearForm on the given set of FiniteElementSpace%s.
PrmBlockNonlinearForm(Array<FiniteElementSpace *> &f, Array<FiniteElementSpace *> &pf );
/// Return the @a k-th FE space of the PrmBlockNonlinearForm.
FiniteElementSpace *FESpace(int k) { return fes[k]; }
/// Return the @a k-th parametric FE space of the PrmBlockNonlinearForm.
FiniteElementSpace *PrmFESpace(int k) { return prmfes[k]; }
/// Return the @a k-th FE space of the BlockNonlinearForm (const version).
const FiniteElementSpace *FESpace(int k) const { return fes[k]; }
/// Return the @a k-th parametric FE space of the BlockNonlinearForm (const version).
const FiniteElementSpace *PrmFESpace(int k) const { return prmfes[k]; }
Array<PrmBlockNonlinearFormIntegrator*>& GetDNFI(){ return dnfi;}
/// (Re)initialize the PrmBlockNonlinearForm.
/** After a call to SetSpaces(), the essential b.c. must be set again. */
void SetSpaces(Array<FiniteElementSpace *> &f, Array<FiniteElementSpace *> &prmf);
/// Return the regular dof offsets.
const Array<int> &GetBlockOffsets() const { return block_offsets; }
/// Return the true-dof offsets.
const Array<int> &GetBlockTrueOffsets() const { return block_trueOffsets; }
/// Return the regular dof offsets for the parameters.
const Array<int> &PrmGetBlockOffsets() const { return prmblock_offsets; }
/// Return the true-dof offsets for the parameters.
const Array<int> &PrmGetBlockTrueOffsets() const { return prmblock_trueOffsets; }
/// Adds new Domain Integrator.
void AddDomainIntegrator(PrmBlockNonlinearFormIntegrator *nlfi)
{ dnfi.Append(nlfi); }
/// Adds new Interior Face Integrator.
void AddInteriorFaceIntegrator(PrmBlockNonlinearFormIntegrator *nlfi)
{ fnfi.Append(nlfi); }
/// Adds new Boundary Face Integrator.
void AddBdrFaceIntegrator(PrmBlockNonlinearFormIntegrator *nlfi)
{ bfnfi.Append(nlfi); bfnfi_marker.Append(NULL); }
/** @brief Adds new Boundary Face Integrator, restricted to specific boundary
attributes. */
void AddBdrFaceIntegrator(PrmBlockNonlinearFormIntegrator *nlfi,
Array<int> &bdr_marker);
virtual void SetEssentialBC(const Array<Array<int> *>&bdr_attr_is_ess,
Array<Vector *> &rhs);
virtual void SetPrmEssentialBC(const Array<Array<int> *>&bdr_attr_is_ess,
Array<Vector *> &rhs);
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 MultBlocked
/// directly.
virtual void PrmMult(const Vector &x, Vector &t) const;
/// Method is only called in serial, the parallel version calls
/// GetGradientBlocked directly.
virtual Operator &GetGradient(const Vector &x) const;
/// Set the state fields
virtual void SetStateFields(const Vector &xv) const;
/// Set the adjoint fields
virtual void SetAdjointFields(const Vector &av) const;
/// Set the parameters/design fields
virtual void SetPrmFields(const Vector &dv) const;
/// Destructor.
virtual ~PrmBlockNonlinearForm();
};
}
#endif
+38
View File
@@ -420,6 +420,35 @@ void QuadratureInterpolator::MultTranspose(
MFEM_ABORT("this method is not implemented yet");
}
static void D2QValues1D(const int NE,
const Array<double> &b_,
const Vector &x_,
Vector &y_,
const int vdim = 1,
const int d1d = 0,
const int q1d = 0)
{
auto b = Reshape(b_.Read(), q1d, d1d);
auto x = Reshape(x_.Read(), d1d, vdim, NE);
auto y = Reshape(y_.Write(), vdim, q1d, NE);
MFEM_FORALL(e, NE,
{
for (int c = 0; c < vdim; c++)
{
for (int q = 0; q < q1d; ++q)
{
double val = 0.0;
for (int d = 0; d < d1d; ++d)
{
val += b(q, d) * x(d, c, e);
}
y(c, q, e) = val;
}
}
});
}
template<int T_VDIM = 0, int T_D1D = 0, int T_Q1D = 0, int T_NBZ = 0>
static void D2QValues2D(const int NE,
@@ -631,6 +660,15 @@ static void D2QValues(const FiniteElementSpace &fes,
const int Q1D = maps->nqpt;
const int id = (vdim<<8) | (D1D<<4) | Q1D;
if (dim == 1)
{
MFEM_VERIFY(D1D <= MAX_D1D, "Orders higher than " << MAX_D1D-1
<< " are not supported!");
MFEM_VERIFY(Q1D <= MAX_Q1D, "Quadrature rules with more than "
<< MAX_Q1D << " 1D points are not supported!");
D2QValues1D(NE, maps->B, e_vec, q_val, vdim, D1D, Q1D);
return;
}
if (dim == 2)
{
switch (id)
+25
View File
@@ -195,6 +195,31 @@ void ElementRestriction::MultTransposeUnsigned(const Vector& x, Vector& y) const
});
}
void ElementRestriction::MultLeftInverse(const Vector& x, Vector& y) const
{
// Assumes all elements have the same number of dofs
const int nd = dof;
const int vd = vdim;
const bool t = byvdim;
auto d_offsets = offsets.Read();
auto d_indices = indices.Read();
auto d_x = Reshape(x.Read(), nd, vd, ne);
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
MFEM_FORALL(i, ndofs,
{
const int nextOffset = d_offsets[i + 1];
for (int c = 0; c < vd; ++c)
{
double dofValue = 0;
const int j = nextOffset - 1;
const int idx_j = (d_indices[j] >= 0) ? d_indices[j] : -1 - d_indices[j];
dofValue = (d_indices[j] >= 0) ? d_x(idx_j % nd, c, idx_j / nd) :
-d_x(idx_j % nd, c, idx_j / nd);
d_y(t?c:i,t?i:c) = dofValue;
}
});
}
void ElementRestriction::BooleanMask(Vector& y) const
{
// Assumes all elements have the same number of dofs
+4
View File
@@ -57,6 +57,10 @@ public:
/// Compute MultTranspose without applying signs based on DOF orientations.
void MultTransposeUnsigned(const Vector &x, Vector &y) const;
/// Compute MultTranspose by setting (rather than adding) element
/// contributions; this is a left inverse of the Mult() operation
void MultLeftInverse(const Vector &x, Vector &y) const;
/// @brief Fills the E-vector y with `boolean` values 0.0 and 1.0 such that each
/// each entry of the L-vector is uniquely represented in `y`.
/** This means, the sum of the E-vector `y` is equal to the sum of the
+81 -5
View File
@@ -314,7 +314,18 @@ double TMOP_Metric_022::EvalW(const DenseMatrix &Jpt) const
// = (0.5*I1 - I2b) / (I2b - tau0)
ie.SetJacobian(Jpt.GetData());
const double I2b = ie.Get_I2b();
return (0.5*ie.Get_I1() - I2b) / (I2b - tau0);
double d = I2b - min_detT;
if (d < 0.0 && min_detT == 0.0)
{
// The mesh has been untangled, but it's still possible to get negative
// detJ in FD calculations, as they move the nodes around with some small
// increments and can produce negative determinants. Thus we put a small
// value in the denominator. Note that here I2b < 0.
d = - I2b * 0.1;
}
return (0.5*ie.Get_I1() - I2b) / d;
}
void TMOP_Metric_022::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
@@ -323,8 +334,8 @@ void TMOP_Metric_022::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
// P = 1/(I2b - tau0)*(0.5*dI1 - dI2b) - (0.5*I1 - I2b)/(I2b - tau0)^2*dI2b
// = 0.5/(I2b - tau0)*dI1 + (tau0 - 0.5*I1)/(I2b - tau0)^2*dI2b
ie.SetJacobian(Jpt.GetData());
const double c1 = 1.0/(ie.Get_I2b() - tau0);
Add(c1/2, ie.Get_dI1(), (tau0 - ie.Get_I1()/2)*c1*c1, ie.Get_dI2b(), P);
const double c1 = 1.0/(ie.Get_I2b() - min_detT);
Add(c1/2, ie.Get_dI1(), (min_detT - ie.Get_I1()/2)*c1*c1, ie.Get_dI2b(), P);
}
void TMOP_Metric_022::AssembleH(const DenseMatrix &Jpt,
@@ -344,10 +355,10 @@ void TMOP_Metric_022::AssembleH(const DenseMatrix &Jpt,
// +0.5/(I2b - tau0)*ddI1 + z*ddI2b
ie.SetJacobian(Jpt.GetData());
ie.SetDerivativeMatrix(DS.Height(), DS.GetData());
const double c1 = 1.0/(ie.Get_I2b() - tau0);
const double c1 = 1.0/(ie.Get_I2b() - min_detT);
const double c2 = weight*c1/2;
const double c3 = c1*c2;
const double c4 = (2*tau0 - ie.Get_I1())*c3; // weight*z
const double c4 = (2*min_detT - ie.Get_I1())*c3; // weight*z
ie.Assemble_TProd(-c3, ie.Get_dI1(), ie.Get_dI2b(), A.GetData());
ie.Assemble_TProd(-2*c1*c4, ie.Get_dI2b(), A.GetData());
ie.Assemble_ddI1(c2, A.GetData());
@@ -703,6 +714,71 @@ void TMOP_Metric_303::AssembleH(const DenseMatrix &Jpt,
ie.Assemble_ddI1b(weight/3., A.GetData());
}
double TMOP_Metric_311::EvalW(const DenseMatrix &Jpt) const
{
// mu_311 = (det(J) - 1)^2 - det(J) + (det(J)^2 + eps)^{1/2}
// = (I3b - 1)^2 - I3b + sqrt(I3b^2 + eps)
ie.SetJacobian(Jpt.GetData());
const double I3b = ie.Get_I3b();
return (I3b - 1.0)*(I3b - 1.0) - I3b + std::sqrt(I3b*I3b + eps);
}
void TMOP_Metric_311::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{
ie.SetJacobian(Jpt.GetData());
const double I3b = ie.Get_I3b();
const double c = 2*I3b-3+(I3b)/(std::pow((I3b*I3b+eps),0.5));
P.Set(c, ie.Get_dI3b());
}
void TMOP_Metric_311::AssembleH(const DenseMatrix &Jpt,
const DenseMatrix &DS,
const double weight,
DenseMatrix &A) const
{
ie.SetJacobian(Jpt.GetData());
ie.SetDerivativeMatrix(DS.Height(), DS.GetData());
const double I3b = ie.Get_I3b();
const double c0 = I3b*I3b+eps;
const double c1 = 2 + 1/(pow(c0,0.5)) - I3b*I3b/(pow(c0,1.5));
const double c2 = 2*I3b - 3 + I3b/(pow(c0,0.5));
ie.Assemble_TProd(weight*c1, ie.Get_dI3b(), A.GetData());
ie.Assemble_ddI3b(c2*weight, A.GetData());
}
double TMOP_Metric_313::EvalW(const DenseMatrix &Jpt) const
{
ie.SetJacobian(Jpt.GetData());
const double I3b = ie.Get_I3b();
double d = I3b - min_detT;
if (d < 0.0 && min_detT == 0.0)
{
// The mesh has been untangled, but it's still possible to get negative
// detJ in FD calculations, as they move the nodes around with some small
// increments and can produce negative determinants. Thus we put a small
// value in the denominator. Note that here I3b < 0.
d = - I3b * 0.1;
}
const double c = std::pow(d, -2.0/3.0);
return ie.Get_I1() * c / 3.0;
}
void TMOP_Metric_313::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{
MFEM_ABORT("Metric not implemented yet.");
}
void TMOP_Metric_313::AssembleH(const DenseMatrix &Jpt,
const DenseMatrix &DS,
const double weight,
DenseMatrix &A) const
{
MFEM_ABORT("Metric not implemented yet.");
}
double TMOP_Metric_315::EvalW(const DenseMatrix &Jpt) const
{
// mu_315 = mu_15_3D = (det(J) - 1)^2
+41 -3
View File
@@ -243,11 +243,11 @@ public:
class TMOP_Metric_022 : public TMOP_QualityMetric
{
protected:
double &tau0;
double &min_detT;
mutable InvariantsEvaluator2D<double> ie;
public:
TMOP_Metric_022(double &t0): tau0(t0) {}
TMOP_Metric_022(double &t0): min_detT(t0) {}
// W = 0.5(|J|^2 - 2det(J)) / (det(J) - tau0).
virtual double EvalW(const DenseMatrix &Jpt) const;
@@ -474,7 +474,45 @@ protected:
mutable InvariantsEvaluator3D<double> ie;
public:
// W = |J|^2 / 3 * det(J)^(2/3) - 1.
// W = |J|^2 / 3 * det(J)^(-2/3) - 1.
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const;
};
/// 3D Size (V) untangling metric.
class TMOP_Metric_311 : public TMOP_QualityMetric
{
protected:
const double eps;
mutable InvariantsEvaluator3D<double> ie;
public:
TMOP_Metric_311(double epsilon = 1e-4) : eps(epsilon) { }
// W = (det(J) - 1)^2 - det(J) + (det(J)^2 + eps)^(1/2).
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const;
};
/// 3D Shape (S) metric, untangling version of 303.
class TMOP_Metric_313 : public TMOP_QualityMetric
{
protected:
double &min_detT;
mutable InvariantsEvaluator3D<double> ie;
public:
TMOP_Metric_313(double &mindet) : min_detT(mindet) { }
// W = 1/3 |J|^2 / [det(J)-tau0]^(-2/3).
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
+115 -103
View File
@@ -331,10 +331,6 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
energy_in = nlf->GetEnergy(x);
}
const int NE = fes->GetMesh()->GetNE(), dim = fes->GetMesh()->Dimension();
Array<int> xdofs;
DenseMatrix Jpr(dim);
// Get the local prolongation of the solution vector.
Vector x_out_loc(fes->GetVSize());
if (serial)
@@ -350,51 +346,32 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
}
#endif
// Check if the starting mesh (given by x) is inverted.
// Note that x hasn't been modified by the Newton update yet.
double min_detJ = infinity();
for (int i = 0; i < NE; i++)
// Check if the starting mesh (given by x) is inverted. Note that x hasn't
// been modified by the Newton update yet.
const double min_detT_in = ComputeMinDet(x_out_loc, *fes);
const bool untangling = (min_detT_in <= 0.0) ? true : false;
const double untangle_factor = 1.5;
if (untangling)
{
const int dof = fes->GetFE(i)->GetDof();
DenseMatrix dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
fes->GetElementVDofs(i, xdofs);
x_out_loc.GetSubVector(xdofs, posV);
const IntegrationRule &irule = GetIntegrationRule(*fes->GetFE(i));
const int nsp = irule.GetNPoints();
for (int j = 0; j < nsp; j++)
{
fes->GetFE(i)->CalcDShape(irule.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
min_detJ = std::min(min_detJ, Jpr.Det());
}
// Needed for the line search below. The untangling metrics see this
// reference to detect deteriorations.
*min_det_ptr = untangle_factor * min_detT_in;
}
double min_detJ_all = min_detJ;
#ifdef MFEM_USE_MPI
if (parallel)
{
MPI_Allreduce(&min_detJ, &min_detJ_all, 1, MPI_DOUBLE, MPI_MIN,
p_nlf->ParFESpace()->GetComm());
}
#endif
const bool untangling = (min_detJ_all <= 0) ? true : false;
const bool have_b = (b.Size() == Height());
Vector x_out(x.Size());
bool x_out_ok = false;
double scale = 1.0, energy_out = 0.0;
const double norm0 = Norm(r);
double scale = 1.0, energy_out = 0.0, min_detT_out;
const double norm_in = Norm(r);
const double detJ_factor = (solver_type == 1) ? 0.25 : 0.5;
// Perform the line search.
for (int i = 0; i < 12; i++)
{
// Update the mesh and get the L-vector in x_out_loc.
add(x, -scale, c, x_out);
if (serial)
{
const SparseMatrix *cP = fes->GetConformingProlongation();
@@ -408,47 +385,29 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
}
#endif
// Check det(Jpr) > 0.
if (!untangling)
// Check the changes in detJ.
min_detT_out = ComputeMinDet(x_out_loc, *fes);
if (untangling == false && min_detT_out < 0.0)
{
int jac_ok = 1;
for (int i = 0; i < NE; i++)
{
const int dof = fes->GetFE(i)->GetDof();
DenseMatrix dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
// No untangling, and detJ got negative -- no good.
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Neg det(J) found.\n"; }
scale *= detJ_factor; continue;
}
if (untangling == true && min_detT_out < *min_det_ptr)
{
// Untangling, and detJ got even more negative -- no good.
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Neg det(J) decreased.\n"; }
scale *= detJ_factor; continue;
}
fes->GetElementVDofs(i, xdofs);
x_out_loc.GetSubVector(xdofs, posV);
const IntegrationRule &irule = GetIntegrationRule(*fes->GetFE(i));
const int nsp = irule.GetNPoints();
for (int j = 0; j < nsp; j++)
{
fes->GetFE(i)->CalcDShape(irule.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
if (Jpr.Det() <= 0.0) { jac_ok = 0; goto break2; }
}
}
break2:
int jac_ok_all = jac_ok;
#ifdef MFEM_USE_MPI
if (parallel)
{
MPI_Allreduce(&jac_ok, &jac_ok_all, 1, MPI_INT, MPI_LAND,
p_nlf->ParFESpace()->GetComm());
}
#endif
if (jac_ok_all == 0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Neg det(J) found.\n"; }
scale *= detJ_factor; continue;
}
} // endif(!untangling)
// Skip the energy and residual checks when we're untangling. The
// untangling metrics change their denominators, which can affect the
// energy and residual, so their increase/decrease is not relevant.
if (untangling) { x_out_ok = true; break; }
// Check the changes in total energy.
ProcessNewState(x_out);
if (serial)
{
@@ -460,43 +419,55 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
energy_out = p_nlf->GetParGridFunctionEnergy(x_out_loc);
}
#endif
if (untangling)
if (energy_out > 1.2*energy_in || std::isnan(energy_out) != 0)
{
if (energy_out > energy_in || std::isnan(energy_out) != 0)
if (print_level >= 0)
{
scale *= 0.5;
mfem::out << "Scale = " << scale << " Increasing energy.\n";
}
else { x_out_ok = true; break; }
scale *= 0.5; continue;
}
else
// Check the changes in the Newton residual.
oper->Mult(x_out, r);
if (have_b) { r -= b; }
double norm_out = Norm(r);
if (norm_out > 1.2*norm_in)
{
if (energy_out > 1.2*energy_in || std::isnan(energy_out) != 0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Increasing energy.\n"; }
scale *= 0.5; continue;
}
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Norm increased.\n"; }
scale *= 0.5; continue;
}
else { x_out_ok = true; break; }
} // end line search
oper->Mult(x_out, r);
if (have_b) { r -= b; }
double norm = Norm(r);
if (norm > 1.2*norm0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Norm increased.\n"; }
scale *= 0.5; continue;
}
else { x_out_ok = true; break; }
} // endif (untangling)
} // enddo (i)
if (untangling)
{
// Update the global min detJ. Untangling metrics see this min_det_ptr.
if (min_detT_out > 0.0)
{
*min_det_ptr = 0.0;
if (print_level >= 0)
{ mfem::out << "The mesh has been untangled at the used points!\n"; }
}
else { *min_det_ptr = untangle_factor * min_detT_out; }
}
if (print_level >= 0)
{
mfem::out << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling.\n";
if (untangling)
{
mfem::out << "Min det(T) change: "
<< min_detT_in << " -> " << min_detT_out
<< " with " << scale << " scaling.\n";
}
else
{
mfem::out << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling.\n";
}
}
if (x_out_ok == false) { scale = 0.0; }
@@ -508,8 +479,8 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
// Reset the update flags of all TargetConstructors.
// This is done to avoid repeated updates of shared TargetConstructors.
// Reset the update flags of all TargetConstructors. This is done to avoid
// repeated updates of shared TargetConstructors.
TMOP_Integrator *ti = NULL;
TMOPComboIntegrator *co = NULL;
DiscreteAdaptTC *dtc = NULL;
@@ -617,6 +588,47 @@ void TMOPNewtonSolver::UpdateDiscreteTC(const TMOP_Integrator &ti,
}
}
double TMOPNewtonSolver::ComputeMinDet(const Vector &x_loc,
const FiniteElementSpace &fes) const
{
double min_detJ = infinity();
const int NE = fes.GetNE(), dim = fes.GetMesh()->Dimension();
Array<int> xdofs;
DenseMatrix Jpr(dim);
for (int i = 0; i < NE; i++)
{
const int dof = fes.GetFE(i)->GetDof();
DenseMatrix dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
fes.GetElementVDofs(i, xdofs);
x_loc.GetSubVector(xdofs, posV);
const IntegrationRule &irule = GetIntegrationRule(*fes.GetFE(i));
const int nsp = irule.GetNPoints();
for (int j = 0; j < nsp; j++)
{
fes.GetFE(i)->CalcDShape(irule.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
min_detJ = std::min(min_detJ, Jpr.Det());
}
}
double min_detT_all = min_detJ;
#ifdef MFEM_USE_MPI
if (parallel)
{
auto p_nlf = dynamic_cast<const ParNonlinearForm *>(oper);
MPI_Allreduce(&min_detJ, &min_detT_all, 1, MPI_DOUBLE, MPI_MIN,
p_nlf->ParFESpace()->GetComm());
}
#endif
const DenseMatrix &Wideal =
Geometries.GetGeomToPerfGeomJac(fes.GetFE(0)->GetGeomType());
min_detT_all /= Wideal.Det();
return min_detT_all;
}
#ifdef MFEM_USE_MPI
// Metric values are visualized by creating an L2 finite element functions and
// computing the metric values at the nodes.
+8
View File
@@ -114,6 +114,9 @@ protected:
int solver_type;
bool parallel;
// Minimum determinant over the whole mesh. Used for mesh untangling.
double *min_det_ptr = nullptr;
// Quadrature points that are checked for negative Jacobians etc.
const IntegrationRule &ir;
// These fields are relevant for mixed meshes.
@@ -131,6 +134,9 @@ protected:
void UpdateDiscreteTC(const TMOP_Integrator &ti, const Vector &x_new) const;
double ComputeMinDet(const Vector &x_loc,
const FiniteElementSpace &fes) const;
public:
#ifdef MFEM_USE_MPI
TMOPNewtonSolver(MPI_Comm comm, const IntegrationRule &irule, int type = 0)
@@ -150,6 +156,8 @@ public:
integ_order = order;
}
void SetMinDetPtr(double *md_ptr) { min_det_ptr = md_ptr; }
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const;
virtual void ProcessNewState(const Vector &x) const;
+2
View File
@@ -27,6 +27,7 @@ list(APPEND SRCS
stable3d.cpp
table.cpp
tic_toc.cpp
tinyxml2.cpp
version.cpp
)
@@ -55,6 +56,7 @@ list(APPEND HDRS
table.hpp
tassign.hpp
tic_toc.hpp
tinyxml2.h
text.hpp
version.hpp
)
+6 -3
View File
@@ -43,7 +43,7 @@ CeedRestrMap ceed_restr_map;
static const Backend::Id backend_list[Backend::NUM_BACKENDS] =
{
Backend::CEED_CUDA, Backend::OCCA_CUDA, Backend::RAJA_CUDA, Backend::CUDA,
Backend::CEED_HIP, Backend::HIP, Backend::DEBUG_DEVICE,
Backend::CEED_HIP, Backend::RAJA_HIP, Backend::HIP, Backend::DEBUG_DEVICE,
Backend::OCCA_OMP, Backend::RAJA_OMP, Backend::OMP,
Backend::CEED_CPU, Backend::OCCA_CPU, Backend::RAJA_CPU, Backend::CPU
};
@@ -52,7 +52,7 @@ static const Backend::Id backend_list[Backend::NUM_BACKENDS] =
static const char *backend_name[Backend::NUM_BACKENDS] =
{
"ceed-cuda", "occa-cuda", "raja-cuda", "cuda",
"ceed-hip", "hip", "debug",
"ceed-hip", "raja-hip", "hip", "debug",
"occa-omp", "raja-omp", "omp",
"ceed-cpu", "occa-cpu", "raja-cpu", "cpu"
};
@@ -394,6 +394,8 @@ static void RajaDeviceSetup(const int dev, int &ngpu)
{
#ifdef MFEM_USE_CUDA
if (ngpu <= 0) { DeviceSetup(dev, ngpu); }
#elif defined(MFEM_USE_HIP)
HipDeviceSetup(dev, ngpu);
#else
MFEM_CONTRACT_VAR(dev);
MFEM_CONTRACT_VAR(ngpu);
@@ -507,7 +509,8 @@ void Device::Setup(const int device)
#endif
if (Allows(Backend::CUDA)) { CudaDeviceSetup(dev, ngpu); }
if (Allows(Backend::HIP)) { HipDeviceSetup(dev, ngpu); }
if (Allows(Backend::RAJA_CUDA)) { RajaDeviceSetup(dev, ngpu); }
if (Allows(Backend::RAJA_CUDA) || Allows(Backend::RAJA_HIP))
{ RajaDeviceSetup(dev, ngpu); }
// The check for MFEM_USE_OCCA is in the function OccaDeviceSetup().
if (Allows(Backend::OCCA_MASK)) { OccaDeviceSetup(dev); }
if (Allows(Backend::CEED_CPU))
+13 -10
View File
@@ -46,30 +46,33 @@ struct Backend
/** @brief [device] RAJA CUDA backend. Enabled when MFEM_USE_RAJA = YES
and MFEM_USE_CUDA = YES. */
RAJA_CUDA = 1 << 6,
/** @brief [device] RAJA HIP backend. Enabled when MFEM_USE_RAJA = YES
and MFEM_USE_HIP = YES. */
RAJA_HIP = 1 << 7,
/** @brief [host] OCCA CPU backend: sequential execution on each MPI rank.
Enabled when MFEM_USE_OCCA = YES. */
OCCA_CPU = 1 << 7,
OCCA_CPU = 1 << 8,
/// [host] OCCA OpenMP backend. Enabled when MFEM_USE_OCCA = YES.
OCCA_OMP = 1 << 8,
OCCA_OMP = 1 << 9,
/** @brief [device] OCCA CUDA backend. Enabled when MFEM_USE_OCCA = YES
and MFEM_USE_CUDA = YES. */
OCCA_CUDA = 1 << 9,
OCCA_CUDA = 1 << 10,
/** @brief [host] CEED CPU backend. GPU backends can still be used, but
with expensive memory transfers. Enabled when MFEM_USE_CEED = YES. */
CEED_CPU = 1 << 10,
CEED_CPU = 1 << 11,
/** @brief [device] CEED CUDA backend working together with the CUDA
backend. Enabled when MFEM_USE_CEED = YES and MFEM_USE_CUDA = YES.
NOTE: The current default libCEED CUDA backend is non-deterministic! */
CEED_CUDA = 1 << 11,
CEED_CUDA = 1 << 12,
/** @brief [device] CEED HIP backend working together with the HIP
backend. Enabled when MFEM_USE_CEED = YES and MFEM_USE_HIP = YES. */
CEED_HIP = 1 << 12,
CEED_HIP = 1 << 13,
/** @brief [device] Debug backend: host memory is READ/WRITE protected
while a device is in use. It allows to test the "device" code-path
(using separate host/device memory pools and host <-> device
transfers) without any GPU hardware. As 'DEBUG' is sometimes used
as a macro, `_DEVICE` has been added to avoid conflicts. */
DEBUG_DEVICE = 1 << 13
DEBUG_DEVICE = 1 << 14
};
/** @brief Additional useful constants. For example, the *_MASK constants can
@@ -77,14 +80,14 @@ struct Backend
enum
{
/// Number of backends: from (1 << 0) to (1 << (NUM_BACKENDS-1)).
NUM_BACKENDS = 14,
NUM_BACKENDS = 15,
/// Biwise-OR of all CPU backends
CPU_MASK = CPU | RAJA_CPU | OCCA_CPU | CEED_CPU,
/// Biwise-OR of all CUDA backends
CUDA_MASK = CUDA | RAJA_CUDA | OCCA_CUDA | CEED_CUDA,
/// Biwise-OR of all HIP backends
HIP_MASK = HIP | CEED_HIP,
HIP_MASK = HIP | RAJA_HIP | CEED_HIP,
/// Biwise-OR of all OpenMP backends
OMP_MASK = OMP | RAJA_OMP | OCCA_OMP,
/// Bitwise-OR of all CEED backends
@@ -93,7 +96,7 @@ struct Backend
DEVICE_MASK = CUDA_MASK | HIP_MASK | DEBUG_DEVICE,
/// Biwise-OR of all RAJA backends
RAJA_MASK = RAJA_CPU | RAJA_OMP | RAJA_CUDA,
RAJA_MASK = RAJA_CPU | RAJA_OMP | RAJA_CUDA | RAJA_HIP,
/// Biwise-OR of all OCCA backends
OCCA_MASK = OCCA_CPU | OCCA_OMP | OCCA_CUDA
};
+134 -44
View File
@@ -86,77 +86,157 @@ void OmpWrap(const int N, HBODY &&h_body)
}
/// RAJA Cuda backend
/// RAJA Cuda and Hip backends
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA)
#if RAJA_VERSION_MAJOR == 0 && RAJA_VERSION_MINOR < 12
using RAJA::statement::Segs;
#else
using RAJA::Segs;
using cuda_launch_policy =
RAJA::expt::LaunchPolicy<RAJA::expt::null_launch_t, RAJA::expt::cuda_launch_t<false>>;
using cuda_teams_x =
RAJA::expt::LoopPolicy<RAJA::loop_exec,RAJA::cuda_block_x_direct>;
using cuda_threads_z =
RAJA::expt::LoopPolicy<RAJA::loop_exec,RAJA::cuda_thread_z_direct>;
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP)
using hip_launch_policy =
RAJA::expt::LaunchPolicy<RAJA::expt::null_launch_t, RAJA::expt::hip_launch_t<false>>;
using hip_teams_x =
RAJA::expt::LoopPolicy<RAJA::loop_exec,RAJA::hip_block_x_direct>;
using hip_threads_z =
RAJA::expt::LoopPolicy<RAJA::loop_exec,RAJA::hip_thread_z_direct>;
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA)
template <const int BLOCKS = MFEM_CUDA_BLOCKS, typename DBODY>
void RajaCudaWrap1D(const int N, DBODY &&d_body)
void RajaCuWrap1D(const int N, DBODY &&d_body)
{
// true denotes asynchronous kernel
//true denotes asynchronous kernel
RAJA::forall<RAJA::cuda_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
}
template <typename DBODY>
void RajaCudaWrap2D(const int N, DBODY &&d_body,
const int X, const int Y, const int BZ)
void RajaCuWrap2D(const int N, DBODY &&d_body,
const int X, const int Y, const int BZ)
{
MFEM_VERIFY(N>0, "");
MFEM_VERIFY(BZ>0, "");
const int G = (N+BZ-1)/BZ;
RAJA::kernel<RAJA::KernelPolicy<
RAJA::statement::CudaKernelAsync<
RAJA::statement::For<0, RAJA::cuda_block_x_direct,
RAJA::statement::For<1, RAJA::cuda_thread_x_direct,
RAJA::statement::For<2, RAJA::cuda_thread_y_direct,
RAJA::statement::For<3, RAJA::cuda_thread_z_direct,
RAJA::statement::Lambda<0, Segs<0>>>>>>>>>
(RAJA::make_tuple(RAJA::RangeSegment(0,G), RAJA::RangeSegment(0,X),
RAJA::RangeSegment(0,Y), RAJA::RangeSegment(0,BZ)),
[=] RAJA_DEVICE (const int n)
using namespace RAJA::expt;
using RAJA::RangeSegment;
launch<cuda_launch_policy>
(DEVICE, Resources(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE (LaunchContext ctx)
{
const int k = n*BZ + threadIdx.z;
if (k >= N) { return; }
d_body(k);
loop<cuda_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
{
loop<cuda_threads_z>(ctx, RangeSegment(0, BZ), [&] (const int tz)
{
const int k = n*BZ + tz;
if (k >= N) { return; }
d_body(k);
});
});
});
MFEM_GPU_CHECK(cudaGetLastError());
}
template <typename DBODY>
void RajaCudaWrap3D(const int N, DBODY &&d_body,
const int X, const int Y, const int Z)
void RajaCuWrap3D(const int N, DBODY &&d_body,
const int X, const int Y, const int Z)
{
MFEM_VERIFY(N>0, "");
RAJA::kernel<RAJA::KernelPolicy<
RAJA::statement::CudaKernelAsync<
RAJA::statement::For<0, RAJA::cuda_block_x_direct,
RAJA::statement::For<1, RAJA::cuda_thread_x_direct,
RAJA::statement::For<2, RAJA::cuda_thread_y_direct,
RAJA::statement::For<3, RAJA::cuda_thread_z_direct,
RAJA::statement::Lambda<0, Segs<0>>>>>>>>>
(RAJA::make_tuple(RAJA::RangeSegment(0,N), RAJA::RangeSegment(0,X),
RAJA::RangeSegment(0,Y), RAJA::RangeSegment(0,Z)),
[=] RAJA_DEVICE (const int k) { d_body(k); });
using namespace RAJA::expt;
using RAJA::RangeSegment;
launch<cuda_launch_policy>
(DEVICE, Resources(Teams(N), Threads(X, Y, Z)),
[=] RAJA_DEVICE (LaunchContext ctx)
{
loop<cuda_teams_x>(ctx, RangeSegment(0, N), d_body);
});
MFEM_GPU_CHECK(cudaGetLastError());
}
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP)
template <const int BLOCKS = MFEM_HIP_BLOCKS, typename DBODY>
void RajaHipWrap1D(const int N, DBODY &&d_body)
{
//true denotes asynchronous kernel
RAJA::forall<RAJA::hip_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
}
template <typename DBODY>
void RajaHipWrap2D(const int N, DBODY &&d_body,
const int X, const int Y, const int BZ)
{
MFEM_VERIFY(N>0, "");
MFEM_VERIFY(BZ>0, "");
const int G = (N+BZ-1)/BZ;
using namespace RAJA::expt;
using RAJA::RangeSegment;
launch<hip_launch_policy>
(DEVICE, Resources(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE (LaunchContext ctx)
{
loop<hip_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
{
loop<hip_threads_z>(ctx, RangeSegment(0, BZ), [&] (const int tz)
{
const int k = n*BZ + tz;
if (k >= N) { return; }
d_body(k);
});
});
});
MFEM_GPU_CHECK(hipGetLastError());
}
template <typename DBODY>
void RajaHipWrap3D(const int N, DBODY &&d_body,
const int X, const int Y, const int Z)
{
MFEM_VERIFY(N>0, "");
using namespace RAJA::expt;
using RAJA::RangeSegment;
launch<hip_launch_policy>
(DEVICE, Resources(Teams(N), Threads(X, Y, Z)),
[=] RAJA_DEVICE (LaunchContext ctx)
{
loop<hip_teams_x>(ctx, RangeSegment(0, N), d_body);
});
MFEM_GPU_CHECK(hipGetLastError());
}
#endif
/// RAJA OpenMP backend
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_OPENMP)
#if RAJA_VERSION_MAJOR == 0 && RAJA_VERSION_MINOR < 12
using RAJA::statement::Segs;
#else
using RAJA::Segs;
#endif
template <typename HBODY>
void RajaOmpWrap(const int N, HBODY &&h_body)
{
@@ -319,9 +399,19 @@ inline void ForallWrap(const bool use_dev, const int N,
// If Backend::RAJA_CUDA is allowed, use it
if (Device::Allows(Backend::RAJA_CUDA))
{
if (DIM == 1) { return RajaCudaWrap1D(N, d_body); }
if (DIM == 2) { return RajaCudaWrap2D(N, d_body, X, Y, Z); }
if (DIM == 3) { return RajaCudaWrap3D(N, d_body, X, Y, Z); }
if (DIM == 1) { return RajaCuWrap1D(N, d_body); }
if (DIM == 2) { return RajaCuWrap2D(N, d_body, X, Y, Z); }
if (DIM == 3) { return RajaCuWrap3D(N, d_body, X, Y, Z); }
}
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP)
// If Backend::RAJA_HIP is allowed, use it
if (Device::Allows(Backend::RAJA_HIP))
{
if (DIM == 1) { return RajaHipWrap1D(N, d_body); }
if (DIM == 2) { return RajaHipWrap2D(N, d_body, X, Y, Z); }
if (DIM == 3) { return RajaHipWrap3D(N, d_body, X, Y, Z); }
}
#endif
+1 -1
View File
@@ -125,7 +125,7 @@ void* HipMemcpyDtoDAsync(void* dst, const void *src, size_t bytes)
void* HipMemcpyDtoH(void *dst, const void *src, size_t bytes)
{
#ifdef MFEM_USE_HIP
#ifdef MFEM_TRACK_HPI_MEM
#ifdef MFEM_TRACK_HIP_MEM
mfem::out << "HipMemcpyDtoH(): copying " << bytes << " bytes from "
<< src << " to " << dst << " ... " << std::flush;
#endif
+2969
View File
File diff suppressed because it is too large Load Diff
+2380
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -15,6 +15,7 @@ list(APPEND SRCS
blockvector.cpp
complex_operator.cpp
densemat.cpp
symmat.cpp
handle.cpp
matrix.cpp
ode.cpp
@@ -31,6 +32,7 @@ list(APPEND HDRS
blockvector.hpp
complex_operator.hpp
densemat.hpp
symmat.hpp
dtensor.hpp
handle.hpp
invariants.hpp
+20 -3
View File
@@ -30,10 +30,16 @@ int AmgXSolver::count = 0;
AMGX_resources_handle AmgXSolver::rsrc = nullptr;
AmgXSolver::AmgXSolver()
: ConvergenceCheck(false) {};
AmgXSolver::AmgXSolver(const AMGX_MODE amgxMode_, const bool verbose)
{
amgxMode = amgxMode_;
if (amgxMode == AmgXSolver::SOLVER) { ConvergenceCheck = true;}
else { ConvergenceCheck = false;}
DefaultParameters(amgxMode, verbose);
InitSerial();
@@ -47,6 +53,9 @@ AmgXSolver::AmgXSolver(const MPI_Comm &comm,
std::string config;
amgxMode = amgxMode_;
if (amgxMode == AmgXSolver::SOLVER) { ConvergenceCheck = true;}
else { ConvergenceCheck = false;}
DefaultParameters(amgxMode, verbose);
InitExclusiveGPU(comm);
@@ -58,6 +67,9 @@ AmgXSolver::AmgXSolver(const MPI_Comm &comm, const int nDevs,
std::string config;
amgxMode = amgxMode_;
if (amgxMode == AmgXSolver::SOLVER) { ConvergenceCheck = true;}
else { ConvergenceCheck = false;}
DefaultParameters(amgxMode_, verbose);
InitMPITeams(comm, nDevs);
@@ -178,6 +190,11 @@ void AmgXSolver::ReadParameters(const std::string config,
configSrc = source;
}
void AmgXSolver::SetConvergenceCheck(bool setConvergenceCheck_)
{
ConvergenceCheck = setConvergenceCheck_;
}
void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
const bool verbose)
{
@@ -201,8 +218,8 @@ void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
{
amgx_config = amgx_config + ",\n"
" \"obtain_timings\": 1, \n"
" \"monitor_residual\": 1, \n"
" \"print_grid_stats\": 1, \n"
" \"monitor_residual\": 1, \n"
" \"print_solve_stats\": 1 \n";
}
else
@@ -238,12 +255,12 @@ void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
" \"convergence\": \"RELATIVE_MAX\", \n"
" \"scope\": \"main\", \n"
" \"tolerance\": 1e-12, \n"
" \"monitor_residual\": 1, \n"
" \"norm\": \"L2\" ";
if (verbose)
{
amgx_config = amgx_config + ", \n"
" \"obtain_timings\": 1, \n"
" \"monitor_residual\": 1, \n"
" \"print_grid_stats\": 1, \n"
" \"print_solve_stats\": 1 \n";
}
@@ -884,7 +901,7 @@ void AmgXSolver::Mult(const Vector& B, Vector& X) const
AMGX_SOLVE_STATUS status;
AMGX_solver_get_status(solver, &status);
if (status != AMGX_SOLVE_SUCCESS && amgxMode == SOLVER)
if (status != AMGX_SOLVE_SUCCESS && ConvergenceCheck)
{
if (status == AMGX_SOLVE_DIVERGED)
{
+7 -1
View File
@@ -73,13 +73,16 @@ public:
/// Flags to configure AmgXSolver as a solver or preconditioner
enum AMGX_MODE {SOLVER, PRECONDITIONER};
/// Flag to check for convergence
bool ConvergenceCheck;
/**
Flags to determine whether user solver settings are defined internally in
the source code or will be read through an external JSON file.
*/
enum CONFIG_SRC {INTERNAL, EXTERNAL, UNDEFINED};
AmgXSolver() = default;
AmgXSolver();
/**
Configures AmgX with a default configuration based on the AmgX mode, and
@@ -162,6 +165,9 @@ public:
*/
void DefaultParameters(const AMGX_MODE amgxMode_, const bool verbose);
/// Add a check for convergence after applying Mult.
void SetConvergenceCheck(bool setConvergenceCheck_=true);
~AmgXSolver();
void Finalize();
+19 -15
View File
@@ -287,10 +287,7 @@ void ComplexUMFPackSolver::Init()
void ComplexUMFPackSolver::SetOperator(const Operator &op)
{
int *Ap, *Ai;
void *Symbolic;
double *Ax;
double *Az;
if (Numeric)
{
@@ -322,10 +319,11 @@ void ComplexUMFPackSolver::SetOperator(const Operator &op)
width = mat->real().Width();
MFEM_VERIFY(width == height, "not a square matrix");
Ap = mat->real().GetI(); // assuming real and imag have the same sparsity
Ai = mat->real().GetJ();
Ax = mat->real().GetData();
Az = mat->imag().GetData();
const int * Ap =
mat->real().HostReadI(); // assuming real and imag have the same sparsity
const int * Ai = mat->real().HostReadJ();
const double * Ax = mat->real().HostReadData();
const double * Az = mat->imag().HostReadData();
if (!use_long_ints)
{
@@ -395,6 +393,10 @@ void ComplexUMFPackSolver::Mult(const Vector &b, Vector &x) const
if (mat == NULL)
mfem_error("ComplexUMFPackSolver::Mult : matrix is not set!"
" Call SetOperator first!");
b.HostRead();
x.HostReadWrite();
int n = b.Size()/2;
double * datax = x.GetData();
double * datab = b.GetData();
@@ -413,8 +415,8 @@ void ComplexUMFPackSolver::Mult(const Vector &b, Vector &x) const
if (!use_long_ints)
{
int status =
umfpack_zi_solve(UMFPACK_Aat, mat->real().GetI(), mat->real().GetJ(),
mat->real().GetData(), mat->imag().GetData(),
umfpack_zi_solve(UMFPACK_Aat, mat->real().HostReadI(), mat->real().HostReadJ(),
mat->real().HostReadData(), mat->imag().HostReadData(),
datax, &datax[n], datab, &datab[n], Numeric, Control, Info);
umfpack_zi_report_info(Control, Info);
if (status < 0)
@@ -426,8 +428,8 @@ void ComplexUMFPackSolver::Mult(const Vector &b, Vector &x) const
else
{
SuiteSparse_long status =
umfpack_zl_solve(UMFPACK_Aat,AI,AJ,mat->real().GetData(),
mat->imag().GetData(),
umfpack_zl_solve(UMFPACK_Aat,AI,AJ,mat->real().HostReadData(),
mat->imag().HostReadData(),
datax,&datax[n],datab,&datab[n],Numeric,Control,Info);
umfpack_zl_report_info(Control, Info);
@@ -448,6 +450,8 @@ void ComplexUMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
if (mat == NULL)
mfem_error("ComplexUMFPackSolver::Mult : matrix is not set!"
" Call SetOperator first!");
b.HostRead();
x.HostReadWrite();
int n = b.Size()/2;
double * datax = x.GetData();
double * datab = b.GetData();
@@ -467,8 +471,8 @@ void ComplexUMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
if (!use_long_ints)
{
int status =
umfpack_zi_solve(UMFPACK_A, mat->real().GetI(), mat->real().GetJ(),
mat->real().GetData(), mat->imag().GetData(),
umfpack_zi_solve(UMFPACK_A, mat->real().HostReadI(), mat->real().HostReadJ(),
mat->real().HostReadData(), mat->imag().HostReadData(),
datax, &datax[n], datab, &datab[n], Numeric, Control, Info);
umfpack_zi_report_info(Control, Info);
if (status < 0)
@@ -480,8 +484,8 @@ void ComplexUMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
else
{
SuiteSparse_long status =
umfpack_zl_solve(UMFPACK_A,AI,AJ,mat->real().GetData(),
mat->imag().GetData(),
umfpack_zl_solve(UMFPACK_A,AI,AJ,mat->real().HostReadData(),
mat->imag().HostReadData(),
datax,&datax[n],datab,&datab[n],Numeric,Control,Info);
umfpack_zl_report_info(Control, Info);
+220 -4
View File
@@ -128,6 +128,15 @@ HypreParVector::HypreParVector(ParFiniteElementSpace *pfes)
own_ParVector = 1;
}
void HypreParVector::WrapHypreParVector(hypre_ParVector *y, bool owner)
{
if (own_ParVector) { hypre_ParVectorDestroy(x); }
Destroy();
x = y;
_SetDataAndSize_();
own_ParVector = owner;
}
Vector * HypreParVector::GlobalVector() const
{
hypre_Vector *hv = hypre_ParVectorToVectorAll(*this);
@@ -926,6 +935,15 @@ void HypreParMatrix::GetOffd(SparseMatrix &offd, HYPRE_Int* &cmap) const
cmap = A->col_map_offd;
}
void HypreParMatrix::MergeDiagAndOffd(SparseMatrix &merged)
{
SparseMatrix tmp_wrapper;
hypre_CSRMatrix *hypre_merged = hypre_MergeDiagAndOffd(A);
MakeWrapper(hypre_merged, tmp_wrapper);
merged = tmp_wrapper;
hypre_CSRMatrixDestroy(hypre_merged);
}
void HypreParMatrix::GetBlocks(Array2D<HypreParMatrix*> &blocks,
bool interleaved_rows,
bool interleaved_cols) const
@@ -966,6 +984,46 @@ HypreParMatrix * HypreParMatrix::Transpose() const
return new HypreParMatrix(At);
}
#if MFEM_HYPRE_VERSION >= 21800
HypreParMatrix *HypreParMatrix::ExtractSubmatrix(const Array<int> &indices,
double threshhold) const
{
if (!(A->comm))
{
hypre_MatvecCommPkgCreate(A);
}
hypre_ParCSRMatrix *submat;
// Get number of rows stored on this processor
int local_num_vars = hypre_CSRMatrixNumRows(hypre_ParCSRMatrixDiag(A));
// Form hypre CF-splitting array designating submatrix as F-points (-1)
Array<int> CF_marker(local_num_vars);
CF_marker = 1;
for (int j=0; j<indices.Size(); j++)
{
if (indices[j] > local_num_vars)
{
MFEM_WARNING("WARNING : " << indices[j] << " > " << local_num_vars);
}
CF_marker[indices[j]] = -1;
}
// Construct cpts_global array on hypre matrix structure
HYPRE_BigInt *cpts_global;
hypre_BoomerAMGCoarseParms(MPI_COMM_WORLD, local_num_vars, 1, NULL,
CF_marker, NULL, &cpts_global);
// Extract submatrix into *submat
hypre_ParCSRMatrixExtractSubmatrixFC(A, CF_marker, cpts_global,
"FF", &submat, threshhold);
mfem_hypre_TFree(cpts_global);
return new HypreParMatrix(submat);
}
#endif
HYPRE_Int HypreParMatrix::Mult(HypreParVector &x, HypreParVector &y,
double a, double b)
{
@@ -1279,6 +1337,7 @@ void HypreParMatrix::operator*=(double s)
static void get_sorted_rows_cols(const Array<int> &rows_cols,
Array<HYPRE_Int> &hypre_sorted)
{
rows_cols.HostRead();
hypre_sorted.SetSize(rows_cols.Size());
bool sorted = true;
for (int i = 0; i < rows_cols.Size(); i++)
@@ -1568,6 +1627,36 @@ void HypreParMatrix::Destroy()
}
}
#if MFEM_HYPRE_VERSION >= 21800
void BlockInverseScale(const HypreParMatrix *A, HypreParMatrix *C,
const Vector *b, HypreParVector *d,
int blocksize, BlockInverseScaleJob job)
{
if (job == BlockInverseScaleJob::MATRIX_ONLY ||
job == BlockInverseScaleJob::MATRIX_AND_RHS)
{
hypre_ParCSRMatrix *C_hypre;
hypre_ParcsrBdiagInvScal(*A, blocksize, &C_hypre);
hypre_ParCSRMatrixDropSmallEntries(C_hypre, 1e-15, 1);
C->WrapHypreParCSRMatrix(C_hypre);
}
if (job == BlockInverseScaleJob::RHS_ONLY ||
job == BlockInverseScaleJob::MATRIX_AND_RHS)
{
HypreParVector b_Hypre(A->GetComm(),
A->GetGlobalNumRows(),
b->GetData(), A->GetRowStarts());
hypre_ParVector *d_hypre;
hypre_ParvecBdiagInvScal(b_Hypre, blocksize, &d_hypre, *A);
d->WrapHypreParVector(d_hypre, true);
}
}
#endif
#if MFEM_HYPRE_VERSION < 21400
HypreParMatrix *Add(double alpha, const HypreParMatrix &A,
@@ -1995,6 +2084,8 @@ void EliminateBC(HypreParMatrix &A, HypreParMatrix &Ae,
double *data_offd = hypre_CSRMatrixData(A_offd);
#endif
ess_dof_list.HostRead();
for (int i = 0; i < ess_dof_list.Size(); i++)
{
int r = ess_dof_list[i];
@@ -2161,6 +2252,7 @@ HypreSmoother::HypreSmoother() : Solver()
B = X = V = Z = NULL;
X0 = X1 = NULL;
fir_coeffs = NULL;
A_is_symmetric = false;
}
HypreSmoother::HypreSmoother(HypreParMatrix &_A, int _type,
@@ -2180,6 +2272,7 @@ HypreSmoother::HypreSmoother(HypreParMatrix &_A, int _type,
B = X = V = Z = NULL;
X0 = X1 = NULL;
fir_coeffs = NULL;
A_is_symmetric = false;
SetOperator(_A);
}
@@ -2467,6 +2560,16 @@ void HypreSmoother::Mult(const Vector &b, Vector &x) const
Mult(*B, *X);
}
void HypreSmoother::MultTranspose(const Vector &b, Vector &x) const
{
if (A_is_symmetric || type == 0 || type == 1 || type == 5)
{
Mult(b, x);
return;
}
mfem_error("HypreSmoother::MultTranspose (...) : undefined!\n");
}
HypreSmoother::~HypreSmoother()
{
if (B) { delete B; }
@@ -2517,10 +2620,10 @@ void HypreSolver::Mult(const HypreParVector &b, HypreParVector &x) const
x = 0.0;
}
b.HostRead();
x.HostReadWrite();
if (!setup_called)
{
b.HostRead();
x.HostReadWrite();
err = SetupFcn()(*this, *A, b, x);
if (error_mode == WARN_HYPRE_ERRORS)
{
@@ -2534,8 +2637,6 @@ void HypreSolver::Mult(const HypreParVector &b, HypreParVector &x) const
setup_called = 1;
}
b.HostRead();
x.HostReadWrite();
err = SolveFcn()(*this, *A, b, x);
if (error_mode == WARN_HYPRE_ERRORS)
{
@@ -2804,6 +2905,11 @@ void HypreGMRES::SetTol(double tol)
HYPRE_GMRESSetTol(gmres_solver, tol);
}
void HypreGMRES::SetAbsTol(double tol)
{
HYPRE_GMRESSetAbsoluteTol(gmres_solver, tol);
}
void HypreGMRES::SetMaxIter(int max_iter)
{
HYPRE_GMRESSetMaxIter(gmres_solver, max_iter);
@@ -3607,6 +3713,116 @@ void HypreBoomerAMG::SetElasticityOptions(ParFiniteElementSpace *fespace)
error_mode = IGNORE_HYPRE_ERRORS;
}
#if MFEM_HYPRE_VERSION >= 21800
void HypreBoomerAMG::SetAdvectiveOptions(int distanceR,
const std::string &prerelax,
const std::string &postrelax)
{
// Hypre parameters
int Sabs = 0;
int interp_type = 100;
int relax_type = 10;
int coarsen_type = 6;
double strength_tolC = 0.1;
double strength_tolR = 0.01;
double filter_tolR = 0.0;
double filterA_tol = 0.0;
// Set relaxation on specified grid points
int ns_down, ns_up, ns_coarse;
if (distanceR > 0)
{
ns_down = prerelax.length();
ns_up = postrelax.length();
ns_coarse = 1;
// Array to store relaxation scheme and pass to Hypre
HYPRE_Int **grid_relax_points = mfem_hypre_TAlloc(HYPRE_Int*, 4);
grid_relax_points[0] = NULL;
grid_relax_points[1] = mfem_hypre_TAlloc(HYPRE_Int, ns_down);
grid_relax_points[2] = mfem_hypre_TAlloc(HYPRE_Int, ns_up);
grid_relax_points[3] = mfem_hypre_TAlloc(HYPRE_Int, 1);
grid_relax_points[3][0] = 0;
// set down relax scheme
for (int i = 0; i<ns_down; i++)
{
if (prerelax[i] == 'F')
{
grid_relax_points[1][i] = -1;
}
else if (prerelax[i] == 'C')
{
grid_relax_points[1][i] = 1;
}
else if (prerelax[i] == 'A')
{
grid_relax_points[1][i] = 0;
}
}
// set up relax scheme
for (int i = 0; i<ns_up; i++)
{
if (postrelax[i] == 'F')
{
grid_relax_points[2][i] = -1;
}
else if (postrelax[i] == 'C')
{
grid_relax_points[2][i] = 1;
}
else if (postrelax[i] == 'A')
{
grid_relax_points[2][i] = 0;
}
}
HYPRE_BoomerAMGSetRestriction(amg_precond, distanceR);
HYPRE_BoomerAMGSetGridRelaxPoints(amg_precond, grid_relax_points);
HYPRE_BoomerAMGSetInterpType(amg_precond, interp_type);
}
if (Sabs)
{
HYPRE_BoomerAMGSetSabs(amg_precond, Sabs);
}
HYPRE_BoomerAMGSetCoarsenType(amg_precond, coarsen_type);
// does not support aggressive coarsening
HYPRE_BoomerAMGSetAggNumLevels(amg_precond, 0);
HYPRE_BoomerAMGSetStrongThreshold(amg_precond, strength_tolC);
if (distanceR > 0)
{
HYPRE_BoomerAMGSetStrongThresholdR(amg_precond, strength_tolR);
HYPRE_BoomerAMGSetFilterThresholdR(amg_precond, filter_tolR);
}
if (relax_type > -1)
{
HYPRE_BoomerAMGSetRelaxType(amg_precond, relax_type);
}
if (distanceR > 0)
{
HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, ns_coarse, 3);
HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, ns_down, 1);
HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, ns_up, 2);
HYPRE_BoomerAMGSetADropTol(amg_precond, filterA_tol);
// type = -1: drop based on row inf-norm
HYPRE_BoomerAMGSetADropType(amg_precond, -1);
}
}
#endif
HypreBoomerAMG::~HypreBoomerAMG()
{
for (int i = 0; i < rbms.Size(); i++)
+179 -5
View File
@@ -81,6 +81,14 @@ private:
inline void _SetDataAndSize_();
public:
/// Default constructor, no underlying @a hypre_ParVector is created.
HypreParVector()
{
own_ParVector = false;
x = NULL;
}
/** @brief Creates vector with given global size and parallel partitioning of
the rows/columns given by @a col. */
/** @anchor hypre_partitioning_descr
@@ -113,6 +121,9 @@ public:
/// MPI communicator
MPI_Comm GetComm() { return x->comm; }
/// Converts hypre's format to HypreParVector
void WrapHypreParVector(hypre_ParVector *y, bool owner=true);
/// Returns the parallel row/column partitioning
/** See @ref hypre_partitioning_descr "here" for a description of the
partitioning array. */
@@ -228,15 +239,24 @@ public:
/// An empty matrix to be used as a reference to an existing matrix
HypreParMatrix();
/// Converts hypre's format to HypreParMatrix
/** If @a owner is false, ownership of @a a is not transferred */
void WrapHypreParCSRMatrix(hypre_ParCSRMatrix *a, bool owner = true)
{
Destroy();
Init();
A = a;
ParCSROwner = owner;
height = GetNumRows();
width = GetNumCols();
}
/// Converts hypre's format to HypreParMatrix
/** If @a owner is false, ownership of @a a is not transferred */
explicit HypreParMatrix(hypre_ParCSRMatrix *a, bool owner = true)
{
Init();
A = a;
if (!owner) { ParCSROwner = 0; }
height = GetNumRows();
width = GetNumCols();
WrapHypreParCSRMatrix(a, owner);
}
/// Creates block-diagonal square parallel matrix.
@@ -387,6 +407,13 @@ public:
void GetDiag(SparseMatrix &diag) const;
/// Get the local off-diagonal block. NOTE: 'offd' will not own any data.
void GetOffd(SparseMatrix &offd, HYPRE_Int* &cmap) const;
/** @brief Get a single SparseMatrix containing all rows from this processor,
merged from the diagonal and off-diagonal blocks stored by the
HypreParMatrix. */
/** @note The number of columns in the SparseMatrix will be the global number
of columns in the parallel matrix, so using this method may result in an
integer overflow in the column indices. */
void MergeDiagAndOffd(SparseMatrix &merged);
/** Split the matrix into M x N equally sized blocks of parallel matrices.
The size of 'blocks' must already be set to M x N. */
@@ -397,6 +424,13 @@ public:
/// Returns the transpose of *this
HypreParMatrix * Transpose() const;
/** Returns principle submatrix given by array of indices of connections
with relative size > @a threshold in *this. */
#if MFEM_HYPRE_VERSION >= 21800
HypreParMatrix *ExtractSubmatrix(const Array<int> &indices,
double threshhold=0.0) const;
#endif
/// Returns the number of rows in the diagonal block of the ParCSRMatrix
int GetNumRows() const
{
@@ -549,6 +583,23 @@ public:
Type GetType() const { return Hypre_ParCSR; }
};
#if MFEM_HYPRE_VERSION >= 21800
enum class BlockInverseScaleJob
{
MATRIX_ONLY,
RHS_ONLY,
MATRIX_AND_RHS
};
/** Constructs and applies block diagonal inverse of HypreParMatrix.
The enum @a job specifies whether the matrix or the RHS should be
scaled (or both). */
void BlockInverseScale(const HypreParMatrix *A, HypreParMatrix *C,
const Vector *b, HypreParVector *d,
int blocksize, BlockInverseScaleJob job);
#endif
/** @brief Return a new matrix `C = alpha*A + beta*B`, assuming that both `A`
and `B` use the same row and column partitions and the same `col_map_offd`
arrays. */
@@ -638,6 +689,9 @@ protected:
/// Combined coefficients for windowing and Chebyshev polynomials.
double* fir_coeffs;
/// A flag that indicates whether the linear system matrix A is symmetric
bool A_is_symmetric;
public:
/** Hypre smoother types:
0 = Jacobi
@@ -646,11 +700,12 @@ public:
4 = truncated l1-scaled block Gauss-Seidel/SSOR
5 = lumped Jacobi
6 = Gauss-Seidel
10 = On-processor forward solve for matrix w/ triangular structure
16 = Chebyshev
1001 = Taubin polynomial smoother
1002 = FIR polynomial smoother. */
enum Type { Jacobi = 0, l1Jacobi = 1, l1GS = 2, l1GStr = 4, lumpedJacobi = 5,
GS = 6, Chebyshev = 16, Taubin = 1001, FIR = 1002
GS = 6, OPFS = 10, Chebyshev = 16, Taubin = 1001, FIR = 1002
};
HypreSmoother();
@@ -684,6 +739,12 @@ public:
entries in the associated matrix. */
void SetPositiveDiagonal(bool pos = true) { pos_l1_norms = pos; }
/** Explicitly indicate whether the linear system matrix A is symmetric. If A
is symmetric, the smoother will also be symmetric. In this case, calling
MultTranspose will be redirected to Mult. (This is also done if the
smoother is diagonal.) By default, A is assumed to be nonsymmetric. */
void SetOperatorSymmetry(bool is_sym) { A_is_symmetric = is_sym; }
/** Set/update the associated operator. Must be called after setting the
HypreSmoother type and options. */
virtual void SetOperator(const Operator &op);
@@ -692,6 +753,9 @@ public:
virtual void Mult(const HypreParVector &b, HypreParVector &x) const;
virtual void Mult(const Vector &b, Vector &x) const;
/// Apply transpose of the smoother to relax the linear system Ax=b
virtual void MultTranspose(const Vector &b, Vector &x) const;
virtual ~HypreSmoother();
};
@@ -755,6 +819,28 @@ public:
virtual ~HypreSolver();
};
#if MFEM_HYPRE_VERSION >= 21800
/** Preconditioner for HypreParMatrices that are triangular in some ordering.
Finds correct ordering and performs forward substitution on processor
as approximate inverse. Exact on one processor. */
class HypreTriSolve : public HypreSolver
{
public:
HypreTriSolve() : HypreSolver() { }
explicit HypreTriSolve(HypreParMatrix &A) : HypreSolver(&A) { }
virtual operator HYPRE_Solver() const { return NULL; }
virtual HYPRE_PtrToParSolverFcn SetupFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParCSROnProcTriSetup; }
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParCSROnProcTriSolve; }
HypreParMatrix* GetData() { return A; }
virtual ~HypreTriSolve() { }
};
#endif
/// PCG solver in hypre
class HyprePCG : public HypreSolver
{
@@ -832,6 +918,7 @@ public:
virtual void SetOperator(const Operator &op);
void SetTol(double tol);
void SetAbsTol(double tol);
void SetMaxIter(int max_iter);
void SetKDim(int dim);
void SetLogging(int logging);
@@ -1116,9 +1203,94 @@ public:
construct A. */
void SetElasticityOptions(ParFiniteElementSpace *fespace);
#if MFEM_HYPRE_VERSION >= 21800
/** Hypre parameters to use AIR AMG solve for advection-dominated problems.
See "Nonsymmetric Algebraic Multigrid Based on Local Approximate Ideal
Restriction (AIR)," Manteuffel, Ruge, Southworth, SISC (2018),
DOI:/10.1137/17M1144350. Options: "distanceR" -> distance of neighbor
DOFs to buld restriction operator; options include 1, 2, and 15 (1.5).
Strings "prerelax" and "postrelax" indicate points to relax on:
F = F-points, C = C-points, A = all points. E.g., FFC -> relax on
F-points, relax again on F-points, then relax on C-points. */
void SetAdvectiveOptions(int distance=15, const std::string &prerelax="",
const std::string &postrelax="FFC");
/// Expert option - consult hypre documentation/team
void SetStrongThresholdR(double strengthR)
{ HYPRE_BoomerAMGSetStrongThresholdR(amg_precond, strengthR); }
/// Expert option - consult hypre documentation/team
void SetFilterThresholdR(double filterR)
{ HYPRE_BoomerAMGSetFilterThresholdR(amg_precond, filterR); }
/// Expert option - consult hypre documentation/team
void SetRestriction(int restrict_type)
{ HYPRE_BoomerAMGSetRestriction(amg_precond, restrict_type); }
/// Expert option - consult hypre documentation/team
void SetIsTriangular()
{ HYPRE_BoomerAMGSetIsTriangular(amg_precond, 1); }
/// Expert option - consult hypre documentation/team
void SetGMRESSwitchR(int gmres_switch)
{ HYPRE_BoomerAMGSetGMRESSwitchR(amg_precond, gmres_switch); }
/// Expert option - consult hypre documentation/team
void SetCycleNumSweeps(int prerelax, int postrelax)
{
HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, prerelax, 1);
HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, postrelax, 2);
}
#endif
void SetPrintLevel(int print_level)
{ HYPRE_BoomerAMGSetPrintLevel(amg_precond, print_level); }
void SetMaxIter(int max_iter)
{ HYPRE_BoomerAMGSetMaxIter(amg_precond, max_iter); }
/// Expert option - consult hypre documentation/team
void SetMaxLevels(int max_levels)
{ HYPRE_BoomerAMGSetMaxLevels(amg_precond, max_levels); }
/// Expert option - consult hypre documentation/team
void SetTol(double tol)
{ HYPRE_BoomerAMGSetTol(amg_precond, tol); }
/// Expert option - consult hypre documentation/team
void SetStrengthThresh(double strength)
{ HYPRE_BoomerAMGSetStrongThreshold(amg_precond, strength); }
/// Expert option - consult hypre documentation/team
void SetInterpolation(int interp_type)
{ HYPRE_BoomerAMGSetInterpType(amg_precond, interp_type); }
/// Expert option - consult hypre documentation/team
void SetCoarsening(int coarsen_type)
{ HYPRE_BoomerAMGSetCoarsenType(amg_precond, coarsen_type); }
/// Expert option - consult hypre documentation/team
void SetRelaxType(int relax_type)
{ HYPRE_BoomerAMGSetRelaxType(amg_precond, relax_type); }
/// Expert option - consult hypre documentation/team
void SetCycleType(int cycle_type)
{ HYPRE_BoomerAMGSetCycleType(amg_precond, cycle_type); }
void GetNumIterations(int &num_it)
{ HYPRE_BoomerAMGGetNumIterations(amg_precond, &num_it); }
/// Expert option - consult hypre documentation/team
void SetNodal(int blocksize)
{
HYPRE_BoomerAMGSetNumFunctions(amg_precond, blocksize);
HYPRE_BoomerAMGSetNodal(amg_precond, 1);
}
/// Expert option - consult hypre documentation/team
void SetAggressiveCoarsening(int num_levels)
{ HYPRE_BoomerAMGSetAggNumLevels(amg_precond, num_levels); }
/// The typecast to HYPRE_Solver returns the internal amg_precond
virtual operator HYPRE_Solver() const { return amg_precond; }
@@ -1127,6 +1299,8 @@ public:
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_BoomerAMGSolve; }
using HypreSolver::Mult;
virtual ~HypreBoomerAMG();
};
+15 -22
View File
@@ -62,8 +62,6 @@ protected:
const scalar_t *D; // Always points to external data or is empty
scalar_t *DaJ, *DJt, *DXt, *DYt;
scalar_t sign_detJ;
enum EvalMasks
{
HAVE_I1 = 1,
@@ -96,8 +94,7 @@ protected:
{
eval_state |= HAVE_I2b;
const scalar_t det = J[0]*J[3] - J[1]*J[2];
sign_detJ = scalar_ops::sign(det);
I2b = sign_detJ*det;
I2b = det;
}
void Eval_dI1()
{
@@ -136,10 +133,10 @@ protected:
// I2b = det(J)
// dI2b = adj(J)^T
Get_I2b();
dI2b[0] = sign_detJ*J[3];
dI2b[1] = -sign_detJ*J[2];
dI2b[2] = -sign_detJ*J[1];
dI2b[3] = sign_detJ*J[0];
dI2b[0] = J[3];
dI2b[1] = -J[2];
dI2b[2] = -J[1];
dI2b[3] = J[0];
}
void Eval_DaJ() // D adj(J) = D dI2b^t
{
@@ -516,8 +513,6 @@ protected:
const scalar_t *D; // Always points to external data or is empty
scalar_t *DaJ, *DJt, *DdI2t, *DXt, *DYt;
scalar_t sign_detJ;
enum EvalMasks
{
HAVE_I1 = 1,
@@ -585,8 +580,6 @@ protected:
eval_state |= HAVE_I3b;
I3b = J[0]*(J[4]*J[8] - J[7]*J[5]) - J[1]*(J[3]*J[8] - J[5]*J[6]) +
J[2]*(J[3]*J[7] - J[4]*J[6]);
sign_detJ = scalar_ops::sign(I3b);
I3b = sign_detJ*I3b;
}
scalar_t Get_I3b_p() // I3b^{-2/3}
{
@@ -594,7 +587,7 @@ protected:
{
eval_state |= HAVE_I3b_p;
const scalar_t i3b = Get_I3b();
I3b_p = sign_detJ*scalar_ops::pow(i3b, -2, 3);
I3b_p = scalar_ops::pow(i3b, -2, 3);
}
return I3b_p;
}
@@ -680,15 +673,15 @@ protected:
eval_state |= HAVE_dI3b;
// I3b = det(J)
// dI3b = adj(J)^T
dI3b[0] = sign_detJ*(J[4]*J[8] - J[5]*J[7]); // 0 3 6
dI3b[1] = sign_detJ*(J[5]*J[6] - J[3]*J[8]); // 1 4 7
dI3b[2] = sign_detJ*(J[3]*J[7] - J[4]*J[6]); // 2 5 8
dI3b[3] = sign_detJ*(J[2]*J[7] - J[1]*J[8]);
dI3b[4] = sign_detJ*(J[0]*J[8] - J[2]*J[6]);
dI3b[5] = sign_detJ*(J[1]*J[6] - J[0]*J[7]);
dI3b[6] = sign_detJ*(J[1]*J[5] - J[2]*J[4]);
dI3b[7] = sign_detJ*(J[2]*J[3] - J[0]*J[5]);
dI3b[8] = sign_detJ*(J[0]*J[4] - J[1]*J[3]);
dI3b[0] = J[4]*J[8] - J[5]*J[7]; // 0 3 6
dI3b[1] = J[5]*J[6] - J[3]*J[8]; // 1 4 7
dI3b[2] = J[3]*J[7] - J[4]*J[6]; // 2 5 8
dI3b[3] = J[2]*J[7] - J[1]*J[8];
dI3b[4] = J[0]*J[8] - J[2]*J[6];
dI3b[5] = J[1]*J[6] - J[0]*J[7];
dI3b[6] = J[1]*J[5] - J[2]*J[4];
dI3b[7] = J[2]*J[3] - J[0]*J[5];
dI3b[8] = J[0]*J[4] - J[1]*J[3];
}
void Eval_DZt(const scalar_t *Z, scalar_t **DZt_ptr)
{
+1
View File
@@ -24,6 +24,7 @@
#include "blockoperator.hpp"
#include "sparsesmoothers.hpp"
#include "densemat.hpp"
#include "symmat.hpp"
#include "ode.hpp"
#include "solvers.hpp"
#include "handle.hpp"
+2
View File
@@ -196,6 +196,8 @@ void MUMPSSolver::SetOperator(const Operator &op)
void MUMPSSolver::Mult(const Vector &x, Vector &y) const
{
x.HostRead();
y.HostReadWrite();
#if MFEM_MUMPS_VERSION >= 530
id->nloc_rhs = x.Size();
+7 -1
View File
@@ -37,7 +37,8 @@ protected:
const Array<int> &test_tdof_list,
RectangularConstrainedOperator* &Aout);
/// Returns RAP Operator of this, taking in input/output Prolongation matrices
/** @brief Returns RAP Operator of this, using input/output Prolongation matrices
@a Pi corresponds to "P", @a Po corresponds to "Rt" */
Operator *SetupRAP(const Operator *Pi, const Operator *Po);
public:
@@ -112,6 +113,11 @@ public:
{
return GetProlongation(); // Assume square unless specialized
}
/** @brief Transpose of GetOutputRestriction, directly available in this
form to facilitate matrix-free RAP-type operators.
`NULL` means identity. */
virtual const Operator *GetOutputRestrictionTranspose() const { return NULL; }
/** @brief Restriction operator from output vectors for the operator to linear
algebra (linear system) vectors. `NULL` means identity. */
virtual const Operator *GetOutputRestriction() const
+107 -15
View File
@@ -1556,6 +1556,7 @@ void NewtonSolver::SetOperator(const Operator &op)
width = op.Width();
MFEM_ASSERT(height == width, "square Operator is required.");
xcur.SetSize(width);
r.SetSize(width);
c.SetSize(width);
}
@@ -1615,9 +1616,20 @@ void NewtonSolver::Mult(const Vector &b, Vector &x) const
break;
}
prec->SetOperator(oper->GetGradient(x));
grad = &oper->GetGradient(x);
prec->SetOperator(*grad);
prec->Mult(r, c); // c = [DF(x_i)]^{-1} [F(x_i)-b]
if (lin_rtol_type)
{
AdaptiveLinRtolPreSolve(x, it, norm);
}
prec->Mult(r, c); // c = [DF(x_i)]^{-1} [F(x_i)-b]
if (lin_rtol_type)
{
AdaptiveLinRtolPostSolve(c, r, it, norm);
}
const double c_scale = ComputeScalingFactor(x, b);
if (c_scale == 0.0)
@@ -1641,6 +1653,86 @@ void NewtonSolver::Mult(const Vector &b, Vector &x) const
final_norm = norm;
}
void NewtonSolver::SetAdaptiveLinRtol(const int type,
const double rtol0,
const double rtol_max,
const double alpha,
const double gamma)
{
lin_rtol_type = type;
lin_rtol0 = rtol0;
lin_rtol_max = rtol_max;
this->alpha = alpha;
this->gamma = gamma;
}
void NewtonSolver::AdaptiveLinRtolPreSolve(const Vector &x,
const int it,
const double fnorm) const
{
// Assume that when adaptive linear solver relative tolerance is activated,
// we are working with an iterative solver.
auto iterative_solver = static_cast<IterativeSolver *>(prec);
// Adaptive linear solver relative tolerance
double eta;
// Safeguard threshold
double sg_threshold = 0.1;
if (it == 0)
{
eta = lin_rtol0;
}
else
{
if (lin_rtol_type == 1)
{
// eta = gamma * abs(||F(x1)|| - ||F(x0) + DF(x0) s0||) / ||F(x0)||
eta = gamma * abs(fnorm - lnorm_last) / fnorm_last;
}
else if (lin_rtol_type == 2)
{
// eta = gamma * (||F(x1)|| / ||F(x0)||)^alpha
eta = gamma * pow(fnorm / fnorm_last, alpha);
}
else
{
MFEM_ABORT("Unknown adaptive linear solver rtol version");
}
// Safeguard rtol from "oversolving" ?!
const double sg_eta = gamma * pow(eta_last, alpha);
if (sg_eta > sg_threshold) { eta = std::max(eta, sg_eta); }
}
eta = std::min(eta, lin_rtol_max);
iterative_solver->SetRelTol(eta);
eta_last = eta;
if (print_level >= 0)
{
mfem::out << "Eisenstat-Walker rtol = " << eta << "\n";
}
}
void NewtonSolver::AdaptiveLinRtolPostSolve(const Vector &x,
const Vector &b,
const int it,
const double fnorm) const
{
fnorm_last = fnorm;
// If version 1 is chosen, the true linear residual norm has to be computed
// and in most cases we can only retrieve the preconditioned linear residual
// norm.
if (lin_rtol_type == 1)
{
// lnorm_last = ||F(x0) + DF(x0) s0||
Vector linres(x.Size());
grad->Mult(x, linres);
linres -= b;
lnorm_last = Norm(linres);
}
}
void LBFGSSolver::Mult(const Vector &b, Vector &x) const
{
MFEM_VERIFY(oper != NULL, "the Operator is not set (use SetOperator).");
@@ -2720,9 +2812,7 @@ void UMFPackSolver::Init()
void UMFPackSolver::SetOperator(const Operator &op)
{
int *Ap, *Ai;
void *Symbolic;
double *Ax;
if (Numeric)
{
@@ -2748,9 +2838,9 @@ void UMFPackSolver::SetOperator(const Operator &op)
width = mat->Width();
MFEM_VERIFY(width == height, "not a square matrix");
Ap = mat->GetI();
Ai = mat->GetJ();
Ax = mat->GetData();
const int * Ap = mat->HostReadI();
const int * Ai = mat->HostReadJ();
const double * Ax = mat->HostReadData();
if (!use_long_ints)
{
@@ -2820,12 +2910,13 @@ void UMFPackSolver::Mult(const Vector &b, Vector &x) const
if (mat == NULL)
mfem_error("UMFPackSolver::Mult : matrix is not set!"
" Call SetOperator first!");
b.HostRead();
x.HostReadWrite();
if (!use_long_ints)
{
int status =
umfpack_di_solve(UMFPACK_At, mat->GetI(), mat->GetJ(),
mat->GetData(), x, b, Numeric, Control, Info);
umfpack_di_solve(UMFPACK_At, mat->HostReadI(), mat->HostReadJ(),
mat->HostReadData(), x, b, Numeric, Control, Info);
umfpack_di_report_info(Control, Info);
if (status < 0)
{
@@ -2836,7 +2927,7 @@ void UMFPackSolver::Mult(const Vector &b, Vector &x) const
else
{
SuiteSparse_long status =
umfpack_dl_solve(UMFPACK_At, AI, AJ, mat->GetData(), x, b,
umfpack_dl_solve(UMFPACK_At, AI, AJ, mat->HostReadData(), x, b,
Numeric, Control, Info);
umfpack_dl_report_info(Control, Info);
if (status < 0)
@@ -2852,12 +2943,13 @@ void UMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
if (mat == NULL)
mfem_error("UMFPackSolver::MultTranspose : matrix is not set!"
" Call SetOperator first!");
b.HostRead();
x.HostReadWrite();
if (!use_long_ints)
{
int status =
umfpack_di_solve(UMFPACK_A, mat->GetI(), mat->GetJ(),
mat->GetData(), x, b, Numeric, Control, Info);
umfpack_di_solve(UMFPACK_A, mat->HostReadI(), mat->HostReadJ(),
mat->HostReadData(), x, b, Numeric, Control, Info);
umfpack_di_report_info(Control, Info);
if (status < 0)
{
@@ -2869,7 +2961,7 @@ void UMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
else
{
SuiteSparse_long status =
umfpack_dl_solve(UMFPACK_A, AI, AJ, mat->GetData(), x, b,
umfpack_dl_solve(UMFPACK_A, AI, AJ, mat->HostReadData(), x, b,
Numeric, Control, Info);
umfpack_dl_report_info(Control, Info);
if (status < 0)
+54 -1
View File
@@ -406,7 +406,40 @@ void MINRES(const Operator &A, Solver &B, const Vector &b, Vector &x,
class NewtonSolver : public IterativeSolver
{
protected:
mutable Vector r, c;
mutable Vector xcur, r, c;
mutable Operator *grad;
// Adaptive linear solver rtol variables
// Method to determine rtol, 0 means the adaptive algorithm is deactivated.
int lin_rtol_type = 0;
// rtol to use in first iteration
double lin_rtol0;
// Maximum rtol
double lin_rtol_max;
// Function norm ||F(x)|| of the previous iterate
mutable double fnorm_last = 0.0;
// Linear residual norm of the previous iterate
mutable double lnorm_last = 0.0;
// Forcing term (linear residual rtol) from the previous iterate
mutable double eta_last = 0.0;
// Eisenstat-Walker factor gamma
double gamma;
// Eisenstat-Walker factor alpha
double alpha;
/** @brief Method for the adaptive linear solver rtol invoked before the
linear solve. */
void AdaptiveLinRtolPreSolve(const Vector &x,
const int it,
const double fnorm) const;
/** @brief Method for the adaptive linear solver rtol invoked after the
linear solve. */
void AdaptiveLinRtolPostSolve(const Vector &x,
const Vector &b,
const int it,
const double fnorm) const;
public:
NewtonSolver() { }
@@ -434,6 +467,26 @@ public:
/** @brief This method can be overloaded in derived classes to perform
computations that need knowledge of the newest Newton state. */
virtual void ProcessNewState(const Vector &x) const { }
const Vector &GetCurrentResidual() const { return r; }
const Vector &GetCurrentIterate() const { return xcur; }
/// Enable adaptive linear solver relative tolerance algorithm.
/** Compute a relative tolerance for the Krylov method after each nonlinear
iteration, based on the algorithm presented in [1].
The maximum linear solver relative tolerance @a rtol_max should be < 1. For
@a type 1 the parameters @a alpha and @a gamma are ignored. For @a type 2
@a alpha has to be between 0 and 1 and @a gamma between 1 and 2.
[1] Eisenstat, Stanley C., and Homer F. Walker. "Choosing the forcing terms
in an inexact Newton method."
*/
void SetAdaptiveLinRtol(const int type = 2,
const double rtol0 = 0.5,
const double rtol_max = 0.9,
const double alpha = 0.5 * (1.0 + sqrt(5.0)),
const double gamma = 1.0);
};
/** L-BFGS method for solving F(x)=b for a given operator F, by minimizing
+5 -1
View File
@@ -415,10 +415,14 @@ void SparseMatrix::SortColumnIndices()
return;
}
const int * Ip=HostReadI();
HostReadWriteJ();
HostReadWriteData();
Array<Pair<int,double> > row;
for (int j = 0, i = 0; i < height; i++)
{
int end = I[i+1];
int end = Ip[i+1];
row.SetSize(end - j);
for (int k = 0; k < row.Size(); k++)
{
+3 -2
View File
@@ -523,9 +523,10 @@ void SuperLUSolver::Mult( const Vector & x, Vector & y ) const
// SuperLU overwrites x with y, so copy x to y and pass that to the solve
// routine.
y = x;
const double *xPtr = x.HostRead();
y = xPtr;
double * yPtr = y.HostReadWrite();
double* yPtr = (double*)y;
int info = -1, locSize = y.Size();
// Solve the system
+109
View File
@@ -0,0 +1,109 @@
// 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.
// Implementation of data type DenseSymmetricMatrix
#include "symmat.hpp"
namespace mfem
{
DenseSymmetricMatrix::DenseSymmetricMatrix() : Matrix(0)
{
data.Reset();
}
DenseSymmetricMatrix::DenseSymmetricMatrix(int s) : Matrix(s)
{
MFEM_ASSERT(s >= 0, "invalid DenseSymmetricMatrix size: " << s);
if (s > 0)
{
data.New((s*(s+1))/2);
*this = 0.0; // init with zeroes
}
else
{
data.Reset();
}
}
void DenseSymmetricMatrix::SetSize(int s)
{
MFEM_ASSERT(s >= 0,
"invalid DenseSymmetricMatrix size: " << s);
if (Height() == s)
{
return;
}
height = s;
width = s;
const int s2 = (s*(s+1))/2;
if (s2 > data.Capacity())
{
data.Delete();
data.New(s2);
*this = 0.0; // init with zeroes
}
}
DenseSymmetricMatrix &DenseSymmetricMatrix::operator=(double c)
{
const int s = (Height()*(Height()+1))/2;
for (int i = 0; i < s; i++)
{
data[i] = c;
}
return *this;
}
double &DenseSymmetricMatrix::Elem(int i, int j)
{
return (*this)(i,j);
}
const double &DenseSymmetricMatrix::Elem(int i, int j) const
{
return (*this)(i,j);
}
DenseSymmetricMatrix &DenseSymmetricMatrix::operator*=(double c)
{
int s = Height()*(Height()+1)/2;
for (int i = 0; i < s; i++)
{
data[i] *= c;
}
return *this;
}
void DenseSymmetricMatrix::Mult(const Vector &x, Vector &y) const
{
mfem_error("DenseSymmetricMatrix::Mult() not implemented!");
}
MatrixInverse *DenseSymmetricMatrix::Inverse() const
{
mfem_error("DenseSymmetricMatrix::Inverse() not implemented!");
return nullptr;
}
void DenseSymmetricMatrix::Print (std::ostream & out, int width_) const
{
mfem_error("DenseSymmetricMatrix::Print() not implemented!");
}
DenseSymmetricMatrix::~DenseSymmetricMatrix()
{
data.Delete();
}
}
+175
View File
@@ -0,0 +1,175 @@
// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_SYMMETRICMAT
#define MFEM_SYMMETRICMAT
#include "../config/config.hpp"
#include "../general/globals.hpp"
#include "matrix.hpp"
namespace mfem
{
/// Dense symmetric matrix storing the upper triangular part. This class so far
/// has little functionality beyond storage.
class DenseSymmetricMatrix : public Matrix
{
private:
Memory<double> data;
public:
/** Default constructor for DenseSymmetricMatrix.
Sets data = NULL and height = width = 0. */
DenseSymmetricMatrix();
/// Creates square matrix of size s.
explicit DenseSymmetricMatrix(int s);
/// Construct a DenseSymmetricMatrix using an existing data array.
/** The DenseSymmetricMatrix does not assume ownership of the data array, i.e. it will
not delete the array. */
DenseSymmetricMatrix(double *d, int s)
: Matrix(s, s) { UseExternalData(d, s); }
/// Change the data array and the size of the DenseSymmetricMatrix.
/** The DenseSymmetricMatrix does not assume ownership of the data array, i.e. it will
not delete the data array @a d. This method should not be used with
DenseSymmetricMatrix that owns its current data array. */
void UseExternalData(double *d, int s)
{
data.Wrap(d, (s*(s+1))/2, false);
height = s; width = s;
}
/// Change the data array and the size of the DenseSymmetricMatrix.
/** The DenseSymmetricMatrix does not assume ownership of the data array, i.e. it will
not delete the new array @a d. This method will delete the current data
array, if owned. */
void Reset(double *d, int s)
{ if (OwnsData()) { data.Delete(); } UseExternalData(d, s); }
/** Clear the data array and the dimensions of the DenseSymmetricMatrix. This method
should not be used with DenseSymmetricMatrix that owns its current data array. */
void ClearExternalData() { data.Reset(); height = width = 0; }
/// Delete the matrix data array (if owned) and reset the matrix state.
void Clear()
{ if (OwnsData()) { data.Delete(); } ClearExternalData(); }
/// Change the size of the DenseSymmetricMatrix to s x s.
void SetSize(int s);
/// Returns the matrix data array.
inline double *Data() const
{ return const_cast<double*>((const double*)data);}
/// Returns the matrix data array.
inline double *GetData() const { return Data(); }
Memory<double> &GetMemory() { return data; }
const Memory<double> &GetMemory() const { return data; }
/// Return the DenseSymmetricMatrix data (host pointer) ownership flag.
inline bool OwnsData() const { return data.OwnsHostPtr(); }
/// Returns reference to a_{ij}.
inline double &operator()(int i, int j);
/// Returns constant reference to a_{ij}.
inline const double &operator()(int i, int j) const;
/// Returns reference to a_{ij}.
virtual double &Elem(int i, int j);
/// Returns constant reference to a_{ij}.
virtual const double &Elem(int i, int j) const;
/// Sets the matrix elements equal to constant c
DenseSymmetricMatrix &operator=(double c);
DenseSymmetricMatrix &operator*=(double c);
long MemoryUsage() const { return data.Capacity() * sizeof(double); }
/// Shortcut for mfem::Read( GetMemory(), TotalSize(), on_dev).
const double *Read(bool on_dev = true) const
{ return mfem::Read(data, Height()*Width(), on_dev); }
/// Shortcut for mfem::Read(GetMemory(), TotalSize(), false).
const double *HostRead() const
{ return mfem::Read(data, Height()*Width(), false); }
/// Shortcut for mfem::Write(GetMemory(), TotalSize(), on_dev).
double *Write(bool on_dev = true)
{ return mfem::Write(data, Height()*Width(), on_dev); }
/// Shortcut for mfem::Write(GetMemory(), TotalSize(), false).
double *HostWrite()
{ return mfem::Write(data, Height()*Width(), false); }
/// Shortcut for mfem::ReadWrite(GetMemory(), TotalSize(), on_dev).
double *ReadWrite(bool on_dev = true)
{ return mfem::ReadWrite(data, Height()*Width(), on_dev); }
/// Shortcut for mfem::ReadWrite(GetMemory(), TotalSize(), false).
double *HostReadWrite()
{ return mfem::ReadWrite(data, Height()*Width(), false); }
/// Matrix vector multiplication.
virtual void Mult(const Vector &x, Vector &y) const;
/// Returns a pointer to (an approximation) of the matrix inverse.
virtual MatrixInverse *Inverse() const;
/// Prints matrix to stream out.
virtual void Print (std::ostream & out = mfem::out, int width_ = 4) const;
/// Destroys the symmetric matrix.
virtual ~DenseSymmetricMatrix();
};
// Inline methods
// The number of entries stored in rows 1,...,k is
// n + n-1 + n-2 + ... + n-k+1, where there are k terms. This equals
// kn - sum_{i=1}^{k-1} i = kn - (k-1)k/2
// This formula is used for the offset for each row.
inline double &DenseSymmetricMatrix::operator()(int i, int j)
{
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
if (i > j) // reverse i and j
{
return data[(j*height) - (((j-1)*j)/2) + i - j];
}
else
{
return data[(i*height) - (((i-1)*i)/2) + j - i];
}
}
inline const double &DenseSymmetricMatrix::operator()(int i, int j) const
{
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
if (i > j) // reverse i and j
{
return data[(j*height) - (((j-1)*j)/2) + i - j];
}
else
{
return data[(i*height) - (((i-1)*i)/2) + j - i];
}
}
} // namespace mfem
#endif
+5 -4
View File
@@ -536,7 +536,7 @@ clean: $(addsuffix /clean,$(EM_DIRS) $(TEST_DIRS))
distclean: clean config/clean doc/clean
rm -rf mfem/
INSTALL_SHARED_LIB = $(MFEM_CXX) $(MFEM_BUILD_FLAGS) $(INSTALL_SOFLAGS)\
INSTALL_SHARED_LIB = $(MFEM_CXX) $(MFEM_LINK_FLAGS) $(INSTALL_SOFLAGS)\
$(OBJECT_FILES) $(EXT_LIBS) -o $(PREFIX_LIB)/libmfem.$(SO_VER) && \
cd $(PREFIX_LIB) && ln -sf libmfem.$(SO_VER) libmfem.$(SO_EXT)
@@ -684,9 +684,10 @@ status info:
ASTYLE_BIN = astyle
ASTYLE = $(ASTYLE_BIN) --options=$(SRC)config/mfem.astylerc
ASTYLE_VER = "Artistic Style Version 2.05.1"
FORMAT_FILES = $(foreach dir,$(DIRS) $(EM_DIRS) config,"$(dir)/*.?pp")
FORMAT_FILES += "tests/unit/*.cpp"
FORMAT_FILES += $(foreach dir,general linalg mesh fem,"tests/unit/$(dir)/*.?pp")
FORMAT_FILES := $(foreach dir,$(DIRS) $(EM_DIRS) config,$(dir)/*.?pp)
FORMAT_FILES += tests/unit/*.cpp
FORMAT_FILES += $(foreach dir,general linalg mesh fem,tests/unit/$(dir)/*.?pp)
FORMAT_FILES := $(filter-out general/tinyxml2.cpp,$(wildcard $(FORMAT_FILES)))
COUT_CERR_FILES = $(foreach dir,$(DIRS),$(dir)/*.[ch]pp)
COUT_CERR_EXCLUDE = '^general/error\.cpp' '^general/globals\.[ch]pp'
+29 -76
View File
@@ -1049,6 +1049,13 @@ void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2) const
*Inf2 = faces_info[Face].Elem2Inf;
}
void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2, int *NCFace) const
{
*Inf1 = faces_info[Face].Elem1Inf;
*Inf2 = faces_info[Face].Elem2Inf;
*NCFace = faces_info[Face].NCFace;
}
Geometry::Type Mesh::GetFaceGeometryType(int Face) const
{
switch (Dim)
@@ -3441,6 +3448,7 @@ void Mesh::Loader(std::istream &input, int generate_edges,
Clear();
istream::pos_type beginning_pos = input.tellg();
string mesh_type;
input >> ws;
getline(input, mesh_type);
@@ -3482,11 +3490,20 @@ void Mesh::Loader(std::istream &input, int generate_edges,
{
ReadTrueGridMesh(input);
}
else if (mesh_type == "# vtk DataFile Version 3.0" ||
mesh_type == "# vtk DataFile Version 2.0") // VTK
else if (mesh_type.rfind("# vtk DataFile Version") == 0)
{
int major_vtk_version = mesh_type[mesh_type.length()-3] - '0';
// int minor_vtk_version = mesh_type[mesh_type.length()-1] - '0';
MFEM_VERIFY(major_vtk_version >= 2 && major_vtk_version <= 4,
"Unsupported VTK format");
ReadVTKMesh(input, curved, read_gf, finalize_topo);
}
else if (mesh_type.rfind("<VTKFile ") == 0)
{
// Go back to beginning of stream
input.seekg(beginning_pos);
ReadXML_VTKMesh(input, curved, read_gf, finalize_topo);
}
else if (mesh_type == "MFEM NURBS mesh v1.0")
{
ReadNURBSMesh(input, curved, read_gf);
@@ -5983,6 +6000,7 @@ int *Mesh::GeneratePartitioning(int nparts, int part_method)
el_to_el = NULL;
// Check for empty partitionings (a "feature" in METIS)
if (nparts > 1 && NumOfElements > nparts)
{
Array< Pair<int,int> > psize(nparts);
int empty_parts;
@@ -8930,7 +8948,7 @@ void Mesh::PrintVTK(std::ostream &out)
const int nv = elements[i]->GetNVertices();
out << nv;
Geometry::Type geom = elements[i]->GetGeometryType();
const int *perm = (geom == Geometry::PRISM) ? vtk_prism_perm : NULL;
const int *perm = VTKGeometry::VertexPermutation[geom];
for (int j = 0; j < nv; j++)
{
out << ' ' << v[perm ? perm[j] : j];
@@ -9015,35 +9033,9 @@ void Mesh::PrintVTK(std::ostream &out)
for (int i = 0; i < NumOfElements; i++)
{
int vtk_cell_type = 5;
Geometry::Type geom_type = GetElement(i)->GetGeometryType();
if (order == 1)
{
switch (geom_type)
{
case Geometry::POINT: vtk_cell_type = 1; break;
case Geometry::SEGMENT: vtk_cell_type = 3; break;
case Geometry::TRIANGLE: vtk_cell_type = 5; break;
case Geometry::SQUARE: vtk_cell_type = 9; break;
case Geometry::TETRAHEDRON: vtk_cell_type = 10; break;
case Geometry::CUBE: vtk_cell_type = 12; break;
case Geometry::PRISM: vtk_cell_type = 13; break;
default: break;
}
}
else if (order == 2)
{
switch (geom_type)
{
case Geometry::SEGMENT: vtk_cell_type = 21; break;
case Geometry::TRIANGLE: vtk_cell_type = 22; break;
case Geometry::SQUARE: vtk_cell_type = 28; break;
case Geometry::TETRAHEDRON: vtk_cell_type = 24; break;
case Geometry::CUBE: vtk_cell_type = 29; break;
case Geometry::PRISM: vtk_cell_type = 32; break;
default: break;
}
}
Geometry::Type geom = GetElement(i)->GetGeometryType();
if (order == 1) { vtk_cell_type = VTKGeometry::Map[geom]; }
else if (order == 2) { vtk_cell_type = VTKGeometry::QuadraticMap[geom]; }
out << vtk_cell_type << '\n';
}
@@ -9261,7 +9253,7 @@ void Mesh::PrintVTU(std::ostream &out, int ref, VTKFormat format,
{
coff = coff+nv;
offset.push_back(coff);
const int *p = (geom == Geometry::PRISM) ? vtk_prism_perm : NULL;
const int *p = VTKGeometry::VertexPermutation[geom];
for (int k = 0; k < nv; k++, j++)
{
WriteBinaryOrASCII(out, buf, np + RG[p ? p[j] : j], " ", format);
@@ -9292,39 +9284,14 @@ void Mesh::PrintVTU(std::ostream &out, int ref, VTKFormat format,
out << "<DataArray type=\"UInt8\" Name=\"types\" format=\""
<< fmt_str << "\">" << std::endl;
// cell types
const int *vtk_geom_map =
high_order_output ? VTKGeometry::HighOrderMap : VTKGeometry::Map;
for (int i = 0; i < ne; i++)
{
Geometry::Type geom = get_geom(i);
uint8_t vtk_cell_type = 5;
// VTK element types defined at: https://git.io/JvZLm
switch (geom)
{
case Geometry::POINT:
vtk_cell_type = 1;
break;
case Geometry::SEGMENT:
vtk_cell_type = high_order_output ? 68 : 3;
break;
case Geometry::TRIANGLE:
vtk_cell_type = high_order_output ? 69 : 5;
break;
case Geometry::SQUARE:
vtk_cell_type = high_order_output ? 70 : 9;
break;
case Geometry::TETRAHEDRON:
vtk_cell_type = high_order_output ? 71 : 10;
break;
case Geometry::CUBE:
vtk_cell_type = high_order_output ? 72 : 12;
break;
case Geometry::PRISM:
vtk_cell_type = high_order_output ? 73 : 13;
break;
default:
MFEM_ABORT("Unrecognized VTK element type \"" << geom << "\"");
break;
}
vtk_cell_type = vtk_geom_map[geom];
if (high_order_output)
{
@@ -9473,21 +9440,7 @@ void Mesh::PrintVTK(std::ostream &out, int ref, int field_data)
int nv = Geometries.GetVertices(geom)->GetNPoints();
RefG = GlobGeometryRefiner.Refine(geom, ref, 1);
Array<int> &RG = RefG->RefGeoms;
int vtk_cell_type = 5;
switch (geom)
{
case Geometry::POINT: vtk_cell_type = 1; break;
case Geometry::SEGMENT: vtk_cell_type = 3; break;
case Geometry::TRIANGLE: vtk_cell_type = 5; break;
case Geometry::SQUARE: vtk_cell_type = 9; break;
case Geometry::TETRAHEDRON: vtk_cell_type = 10; break;
case Geometry::CUBE: vtk_cell_type = 12; break;
case Geometry::PRISM: vtk_cell_type = 13; break;
default:
MFEM_ABORT("Unrecognized VTK element type \"" << geom << "\"");
break;
}
int vtk_cell_type = VTKGeometry::Map[geom];
for (int j = 0; j < RG.Size(); j += nv)
{
+8
View File
@@ -239,8 +239,15 @@ protected:
void ReadNetgen2DMesh(std::istream &input, int &curved);
void ReadNetgen3DMesh(std::istream &input);
void ReadTrueGridMesh(std::istream &input);
void CreateVTKMesh(const Vector &points, const Array<int> &cell_data,
const Array<int> &cell_offsets,
const Array<int> &cell_types,
const Array<int> &cell_attributes,
int &curved, int &read_gf, bool &finalize_topo);
void ReadVTKMesh(std::istream &input, int &curved, int &read_gf,
bool &finalize_topo);
void ReadXML_VTKMesh(std::istream &input, int &curved, int &read_gf,
bool &finalize_topo);
void ReadNURBSMesh(std::istream &input, int &curved, int &read_gf);
void ReadInlineMesh(std::istream &input, bool generate_edges = false);
void ReadGmshMesh(std::istream &input, int &curved, int &read_gf);
@@ -1036,6 +1043,7 @@ public:
}
void GetFaceElements (int Face, int *Elem1, int *Elem2) const;
void GetFaceInfos (int Face, int *Inf1, int *Inf2) const;
void GetFaceInfos (int Face, int *Inf1, int *Inf2, int *NCFace) const;
Geometry::Type GetFaceGeometryType(int Face) const;
Element::Type GetFaceElementType(int Face) const;
+443 -242
View File
@@ -12,10 +12,13 @@
#include "mesh_headers.hpp"
#include "../fem/fem.hpp"
#include "../general/text.hpp"
#include "../general/tinyxml2.h"
#include "gmsh.hpp"
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#ifdef MFEM_USE_NETCDF
#include "netcdf.h"
@@ -373,6 +376,398 @@ const int Mesh::vtk_quadratic_hex[27] =
24, 22, 21, 23, 20, 25, 26
};
void Mesh::CreateVTKMesh(const Vector &points, const Array<int> &cell_data,
const Array<int> &cell_offsets,
const Array<int> &cell_types,
const Array<int> &cell_attributes,
int &curved, int &read_gf, bool &finalize_topo)
{
int np = points.Size()/3;
Dim = -1;
NumOfElements = cell_types.Size();
elements.SetSize(NumOfElements);
int order = -1;
bool legacy_elem = false, lagrange_elem = false;
int j = 0;
for (int i = 0; i < NumOfElements; i++)
{
int ct = cell_types[i];
Geometry::Type geom = VTKGeometry::GetMFEMGeometry(ct);
elements[i] = NewElement(geom);
if (cell_attributes.Size() > 0)
{
elements[i]->SetAttribute(cell_attributes[i]);
}
// VTK ordering of vertices is the same as MFEM ordering of vertices
// for all element types *except* prisms, which require a permutation
if (geom == Geometry::PRISM && ct != VTKGeometry::LAGRANGE_PRISM)
{
int prism_vertices[6];
for (int k=0; k<6; ++k)
{
prism_vertices[k] = cell_data[j+VTKGeometry::PrismMap[k]];
}
elements[i]->SetVertices(prism_vertices);
}
else
{
elements[i]->SetVertices(&cell_data[j]);
}
int elem_dim = Geometry::Dimension[geom];
int elem_order = VTKGeometry::GetOrder(ct, cell_offsets[i] - j);
if (VTKGeometry::IsLagrange(ct)) { lagrange_elem = true; }
else { legacy_elem = true; }
MFEM_VERIFY(Dim == -1 || Dim == elem_dim,
"Elements with different dimensions are not supported");
MFEM_VERIFY(order == -1 || order == elem_order,
"Elements with different orders are not supported");
MFEM_VERIFY(legacy_elem != lagrange_elem,
"Mixing of legacy and Lagrange cell types is not supported");
Dim = elem_dim;
order = elem_order;
j = cell_offsets[i];
}
if (order == 1 && !lagrange_elem)
{
NumOfVertices = np;
vertices.SetSize(np);
for (int i = 0; i < np; i++)
{
vertices[i](0) = points(3*i+0);
vertices[i](1) = points(3*i+1);
vertices[i](2) = points(3*i+2);
}
// No boundary is defined in a VTK mesh
NumOfBdrElements = 0;
FinalizeTopology();
CheckElementOrientation(true);
}
else
{
// The following section of code is shared for legacy quadratic and the
// Lagrange high order elements
curved = 1;
// generate new enumeration for the vertices
Array<int> pts_dof(np);
pts_dof = -1;
// mark vertex points
for (int i = 0; i < NumOfElements; i++)
{
int *v = elements[i]->GetVertices();
int nv = elements[i]->GetNVertices();
for (int j = 0; j < nv; j++)
{
if (pts_dof[v[j]] == -1) { pts_dof[v[j]] = 0; }
}
}
// The following loop reorders pts_dofs so vertices are visited in
// canonical order
// Keep the original ordering of the vertices
int i, n;
for (n = i = 0; i < np; i++)
{
if (pts_dof[i] != -1)
{
pts_dof[i] = n++;
}
}
// update the element vertices
for (int i = 0; i < NumOfElements; i++)
{
int *v = elements[i]->GetVertices();
int nv = elements[i]->GetNVertices();
for (int j = 0; j < nv; j++)
{
v[j] = pts_dof[v[j]];
}
}
// Define the 'vertices' from the 'points' through the 'pts_dof' map
NumOfVertices = n;
vertices.SetSize(n);
for (int i = 0; i < np; i++)
{
int j = pts_dof[i];
if (j != -1)
{
vertices[j](0) = points(3*i+0);
vertices[j](1) = points(3*i+1);
vertices[j](2) = points(3*i+2);
}
}
// No boundary is defined in a VTK mesh
NumOfBdrElements = 0;
// determine spaceDim based on min/max differences detected each dimension
if (vertices.Size() > 0)
{
double min_value, max_value;
for (int d=0; d<3; ++d)
{
min_value = max_value = vertices[0](d);
for (int i = 1; i < vertices.Size(); i++)
{
min_value = std::min(min_value,vertices[i](d));
max_value = std::max(max_value,vertices[i](d));
if (min_value != max_value)
{
spaceDim++;
break;
}
}
}
}
// Generate faces and edges so that we can define
// FE space on the mesh
FinalizeTopology();
FiniteElementCollection *fec;
FiniteElementSpace *fes;
if (legacy_elem)
{
// Define quadratic FE space
fec = new QuadraticFECollection;
fes = new FiniteElementSpace(this, fec, spaceDim);
Nodes = new GridFunction(fes);
Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
own_nodes = 1;
// Map vtk points to edge/face/element dofs
Array<int> dofs;
for (int i = 0; i < NumOfElements; i++)
{
fes->GetElementDofs(i, dofs);
const int *vtk_mfem;
switch (elements[i]->GetGeometryType())
{
case Geometry::TRIANGLE:
case Geometry::SQUARE:
vtk_mfem = vtk_quadratic_hex; break; // identity map
case Geometry::TETRAHEDRON:
vtk_mfem = vtk_quadratic_tet; break;
case Geometry::CUBE:
vtk_mfem = vtk_quadratic_hex; break;
case Geometry::PRISM:
vtk_mfem = vtk_quadratic_wedge; break;
default:
vtk_mfem = NULL; // suppress a warning
break;
}
int offset = (i == 0) ? 0 : cell_offsets[i-1];
for (int j = 0; j < dofs.Size(); j++)
{
if (pts_dof[cell_data[offset+j]] == -1)
{
pts_dof[cell_data[offset+j]] = dofs[vtk_mfem[j]];
}
else
{
if (pts_dof[cell_data[offset+j]] != dofs[vtk_mfem[j]])
{
MFEM_ABORT("VTK mesh: inconsistent quadratic mesh!");
}
}
}
}
}
else
{
// Define H1 FE space
fec = new H1_FECollection(order,Dim,BasisType::ClosedUniform);
fes = new FiniteElementSpace(this, fec, spaceDim);
Nodes = new GridFunction(fes);
Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
own_nodes = 1;
Array<int> dofs;
std::map<Geometry::Type,Array<int>> vtk_inv_maps;
std::map<Geometry::Type,const Array<int>*> lex_orderings;
int i, n;
for (n = i = 0; i < NumOfElements; i++)
{
Geometry::Type geom = GetElementBaseGeometry(i);
fes->GetElementDofs(i, dofs);
Array<int> &vtk_inv_map = vtk_inv_maps[geom];
if (vtk_inv_map.Size() == 0)
{
Array<int> vtk_map;
CreateVTKElementConnectivity(vtk_map, geom, order);
vtk_inv_map.SetSize(vtk_map.Size());
for (int j=0; j<vtk_map.Size(); ++j)
{
vtk_inv_map[vtk_map[j]] = j;
}
}
const Array<int> *&lex_ordering = lex_orderings[geom];
if (!lex_ordering)
{
const FiniteElement *fe = fes->GetFE(i);
const NodalFiniteElement *nodal_fe =
dynamic_cast<const NodalFiniteElement*>(fe);
MFEM_ASSERT(nodal_fe != NULL, "Unsupported element type");
lex_ordering = &nodal_fe->GetLexicographicOrdering();
}
for (int lex_idx = 0; lex_idx < dofs.Size(); lex_idx++)
{
int mfem_idx = (*lex_ordering)[lex_idx];
int vtk_idx = vtk_inv_map[lex_idx];
int pt_idx = cell_data[n + vtk_idx];
if (pts_dof[pt_idx] == -1)
{
pts_dof[pt_idx] = dofs[mfem_idx];
}
else
{
if (pts_dof[pt_idx] != dofs[mfem_idx])
{
MFEM_ABORT("VTK mesh: inconsistent Lagrange mesh!");
}
}
}
n += dofs.Size();
}
}
// Define the 'Nodes' from the 'points' through the 'pts_dof' map
Array<int> dofs;
for (int i = 0; i < np; i++)
{
dofs.SetSize(1);
if (pts_dof[i] != -1)
{
dofs[0] = pts_dof[i];
fes->DofsToVDofs(dofs);
for (int d = 0; d < dofs.Size(); d++)
{
(*Nodes)(dofs[d]) = points(3*i+d);
}
}
}
read_gf = 0;
}
}
void Mesh::ReadXML_VTKMesh(std::istream &input, int &curved, int &read_gf,
bool &finalize_topo)
{
using namespace tinyxml2;
const char *erstr = "XML parsing error";
// Read entire stream into buffer
std::istreambuf_iterator<char> eos;
std::vector<char> buf(std::istreambuf_iterator<char>(input), eos);
buf.push_back('\0'); // null-terminate buffer
XMLDocument xml;
xml.Parse(buf.data());
MFEM_VERIFY(xml.ErrorID() == XML_SUCCESS, erstr);
const XMLElement *vtkfile = xml.FirstChildElement();
MFEM_VERIFY(vtkfile, erstr);
MFEM_VERIFY(std::string(vtkfile->Name()) == "VTKFile", erstr);
const XMLElement *vtu = vtkfile->FirstChildElement();
MFEM_VERIFY(vtu, erstr);
MFEM_VERIFY(std::string(vtu->Name()) == "UnstructuredGrid", erstr);
// Count the number of points and cells
const XMLElement *piece = vtu->FirstChildElement();
MFEM_VERIFY(std::string(piece->Name()) == "Piece", erstr);
MFEM_VERIFY(piece->NextSiblingElement() == NULL,
"XML VTK meshes with more than one Piece are not supported");
int npts = piece->IntAttribute("NumberOfPoints");
int ncells = piece->IntAttribute("NumberOfCells");
// Read the points
Vector points(3*npts);
const XMLElement *pts_xml;
for (pts_xml = piece->FirstChildElement();
pts_xml != NULL;
pts_xml = pts_xml->NextSiblingElement())
{
if (std::string(pts_xml->Name()) == "Points")
{
const XMLElement *pts_data = pts_xml->FirstChildElement();
MFEM_VERIFY(std::string(pts_data->Name()) == "DataArray", erstr);
MFEM_VERIFY(std::string(pts_data->Attribute("Name")) == "Points",
erstr);
MFEM_VERIFY(pts_data->IntAttribute("NumberOfComponents") == 3,
"XML VTK Points DataArray must have 3 components");
const char *pts_txt = pts_data->GetText();
MFEM_VERIFY(pts_txt != NULL, erstr);
std::istringstream pts_stream(pts_txt);
points.Load(pts_stream, 3*npts);
break;
}
}
if (pts_xml == NULL) { MFEM_ABORT(erstr); }
// Read the cells
Array<int> cell_data, cell_offsets, cell_types;
const XMLElement *cells_xml;
for (cells_xml = piece->FirstChildElement();
cells_xml != NULL;
cells_xml = cells_xml->NextSiblingElement())
{
if (std::string(cells_xml->Name()) == "Cells")
{
const char *cell_data_txt = NULL;
for (const XMLElement *data_xml = cells_xml->FirstChildElement();
data_xml != NULL;
data_xml = data_xml->NextSiblingElement())
{
MFEM_VERIFY(std::string(data_xml->Name()) == "DataArray", erstr);
std::string data_name(data_xml->Attribute("Name"));
const char *data_txt = data_xml->GetText();
MFEM_VERIFY(data_txt != NULL, erstr);
if (data_name == "offsets")
{
std::istringstream data_stream(data_txt);
cell_offsets.Load(ncells, data_stream);
}
else if (data_name == "types")
{
std::istringstream data_stream(data_txt);
cell_types.Load(ncells, data_stream);
}
else if (data_name == "connectivity")
{
// Have to read the connectivity after the offsets, because we
// don't know how many points to read until we have the offsets
// (size of connectivity array is equal to the last offset), so
// store the data pointer and read this array later.
cell_data_txt = data_txt;
}
}
MFEM_VERIFY(cell_offsets.Size() == ncells, erstr);
MFEM_VERIFY(cell_types.Size() == ncells, erstr);
MFEM_VERIFY(cell_data_txt != NULL, erstr);
int cell_data_size = cell_offsets.Last();
std::istringstream cell_data_stream(cell_data_txt);
cell_data.Load(cell_data_size, cell_data_stream);
break;
}
}
if (cells_xml == NULL) { MFEM_ABORT(erstr); }
// Currently don't support reading cell attributes from VTK mesh
Array<int> cell_attributes;
CreateVTKMesh(points, cell_data, cell_offsets, cell_types, cell_attributes,
curved, read_gf, finalize_topo);
}
void Mesh::ReadVTKMesh(std::istream &input, int &curved, int &read_gf,
bool &finalize_topo)
{
@@ -382,8 +777,6 @@ void Mesh::ReadVTKMesh(std::istream &input, int &curved, int &read_gf,
// * https://lorensen.github.io/VTKExamples/site/VTKFileFormats
// * https://www.kitware.com/products/books/VTKUsersGuide.pdf
int i, j, n, attr;
string buff;
getline(input, buff); // comment line
getline(input, buff);
@@ -412,141 +805,78 @@ void Mesh::ReadVTKMesh(std::istream &input, int &curved, int &read_gf,
}
}
while (buff != "POINTS");
int np = 0;
Vector points;
int np;
input >> np >> ws;
getline(input, buff); // "double"
points.Load(input, 3*np);
//skip metadata
// Looks like:
// METADATA
//INFORMATION 2
//NAME L2_NORM_RANGE LOCATION vtkDataArray
//DATA 2 0 5.19615
//NAME L2_NORM_FINITE_RANGE LOCATION vtkDataArray
//DATA 2 0 5.19615
do
{
input >> np >> ws;
points.SetSize(3*np);
getline(input, buff); // "double"
for (i = 0; i < points.Size(); i++)
input >> buff;
if (!input.good())
{
input >> points(i);
MFEM_ABORT("VTK mesh does not have CELLS data!");
}
}
while (buff != "CELLS");
// Read the cells
NumOfElements = n = 0;
Array<int> cells_data;
input >> ws >> buff;
Array<int> cell_data, cell_offsets;
if (buff == "CELLS")
{
input >> NumOfElements >> n >> ws;
cells_data.SetSize(n);
for (i = 0; i < n; i++)
int ncells, n;
input >> ncells >> n >> ws;
cell_offsets.SetSize(ncells);
cell_data.SetSize(n - ncells);
int offset = 0;
for (int i=0; i<ncells; ++i)
{
input >> cells_data[i];
int nv;
input >> nv;
cell_offsets[i] = offset + nv;
for (int j=0; j<nv; ++j)
{
input >> cell_data[offset + j];
}
offset += nv;
}
}
// Read the cell types
Dim = -1;
int order = -1;
input >> ws >> buff;
Array<int> cell_types;
int ncells;
if (buff == "CELL_TYPES")
{
input >> NumOfElements;
elements.SetSize(NumOfElements);
for (j = i = 0; i < NumOfElements; i++)
{
int ct, elem_dim, elem_order = 1;
input >> ct;
switch (ct)
{
case 5: // triangle
elem_dim = 2;
elements[i] = new Triangle(&cells_data[j+1]);
break;
case 9: // quadrilateral
elem_dim = 2;
elements[i] = new Quadrilateral(&cells_data[j+1]);
break;
case 10: // tetrahedron
elem_dim = 3;
#ifdef MFEM_USE_MEMALLOC
elements[i] = TetMemory.Alloc();
elements[i]->SetVertices(&cells_data[j+1]);
#else
elements[i] = new Tetrahedron(&cells_data[j+1]);
#endif
break;
case 12: // hexahedron
elem_dim = 3;
elements[i] = new Hexahedron(&cells_data[j+1]);
break;
case 13: // wedge
elem_dim = 3;
// switch between vtk vertex ordering and mfem vertex ordering:
// swap vertices (1,2) and (4,5)
elements[i] =
new Wedge(cells_data[j+1], cells_data[j+3], cells_data[j+2],
cells_data[j+4], cells_data[j+6], cells_data[j+5]);
break;
case 22: // quadratic triangle
elem_dim = 2;
elem_order = 2;
elements[i] = new Triangle(&cells_data[j+1]);
break;
case 28: // biquadratic quadrilateral
elem_dim = 2;
elem_order = 2;
elements[i] = new Quadrilateral(&cells_data[j+1]);
break;
case 24: // quadratic tetrahedron
elem_dim = 3;
elem_order = 2;
#ifdef MFEM_USE_MEMALLOC
elements[i] = TetMemory.Alloc();
elements[i]->SetVertices(&cells_data[j+1]);
#else
elements[i] = new Tetrahedron(&cells_data[j+1]);
#endif
break;
case 32: // biquadratic-quadratic wedge
elem_dim = 3;
elem_order = 2;
// switch between vtk vertex ordering and mfem vertex ordering:
// swap vertices (1,2) and (4,5)
elements[i] =
new Wedge(cells_data[j+1], cells_data[j+3], cells_data[j+2],
cells_data[j+4], cells_data[j+6], cells_data[j+5]);
break;
case 29: // triquadratic hexahedron
elem_dim = 3;
elem_order = 2;
elements[i] = new Hexahedron(&cells_data[j+1]);
break;
default:
MFEM_ABORT("VTK mesh : cell type " << ct << " is not supported!");
return;
}
MFEM_VERIFY(Dim == -1 || Dim == elem_dim,
"elements with different dimensions are not supported");
MFEM_VERIFY(order == -1 || order == elem_order,
"elements with different orders are not supported");
Dim = elem_dim;
order = elem_order;
j += cells_data[j] + 1;
}
input >> ncells;
cell_types.Load(ncells, input);
}
// Read attributes
// Read cell attributes
streampos sp = input.tellg();
input >> ws >> buff;
Array<int> cell_attributes;
if (buff == "CELL_DATA")
{
int n;
input >> n >> ws;
getline(input, buff);
filter_dos(buff);
// "SCALARS material dataType numComp"
if (!strncmp(buff.c_str(), "SCALARS material", 16))
if (buff.rfind("SCALARS material") == 0)
{
getline(input, buff); // "LOOKUP_TABLE default"
for (i = 0; i < NumOfElements; i++)
{
input >> attr;
elements[i]->SetAttribute(attr);
}
cell_attributes.Load(ncells, input);
}
else
{
@@ -558,138 +888,9 @@ void Mesh::ReadVTKMesh(std::istream &input, int &curved, int &read_gf,
input.seekg(sp);
}
if (order == 1)
{
cells_data.DeleteAll();
NumOfVertices = np;
vertices.SetSize(np);
for (i = 0; i < np; i++)
{
vertices[i](0) = points(3*i+0);
vertices[i](1) = points(3*i+1);
vertices[i](2) = points(3*i+2);
}
points.Destroy();
// No boundary is defined in a VTK mesh
NumOfBdrElements = 0;
}
else if (order == 2)
{
curved = 1;
// generate new enumeration for the vertices
Array<int> pts_dof(np);
pts_dof = -1;
for (n = i = 0; i < NumOfElements; i++)
{
int *v = elements[i]->GetVertices();
int nv = elements[i]->GetNVertices();
for (j = 0; j < nv; j++)
if (pts_dof[v[j]] == -1)
{
pts_dof[v[j]] = n++;
}
}
// keep the original ordering of the vertices
for (n = i = 0; i < np; i++)
if (pts_dof[i] != -1)
{
pts_dof[i] = n++;
}
// update the element vertices
for (i = 0; i < NumOfElements; i++)
{
int *v = elements[i]->GetVertices();
int nv = elements[i]->GetNVertices();
for (j = 0; j < nv; j++)
{
v[j] = pts_dof[v[j]];
}
}
// Define the 'vertices' from the 'points' through the 'pts_dof' map
NumOfVertices = n;
vertices.SetSize(n);
for (i = 0; i < np; i++)
{
if ((j = pts_dof[i]) != -1)
{
vertices[j](0) = points(3*i+0);
vertices[j](1) = points(3*i+1);
vertices[j](2) = points(3*i+2);
}
}
// No boundary is defined in a VTK mesh
NumOfBdrElements = 0;
// Generate faces and edges so that we can define quadratic
// FE space on the mesh
FinalizeTopology();
finalize_topo = false;
// Define quadratic FE space
FiniteElementCollection *fec = new QuadraticFECollection;
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim);
Nodes = new GridFunction(fes);
Nodes->MakeOwner(fec); // Nodes will destroy 'fec' and 'fes'
own_nodes = 1;
// Map vtk points to edge/face/element dofs
Array<int> dofs;
for (n = i = 0; i < NumOfElements; i++)
{
fes->GetElementDofs(i, dofs);
const int *vtk_mfem;
switch (elements[i]->GetGeometryType())
{
case Geometry::TRIANGLE:
case Geometry::SQUARE:
vtk_mfem = vtk_quadratic_hex; break; // identity map
case Geometry::TETRAHEDRON:
vtk_mfem = vtk_quadratic_tet; break;
case Geometry::CUBE:
vtk_mfem = vtk_quadratic_hex; break;
case Geometry::PRISM:
vtk_mfem = vtk_quadratic_wedge; break;
default:
vtk_mfem = NULL; // suppress a warning
break;
}
for (n++, j = 0; j < dofs.Size(); j++, n++)
{
if (pts_dof[cells_data[n]] == -1)
{
pts_dof[cells_data[n]] = dofs[vtk_mfem[j]];
}
else
{
if (pts_dof[cells_data[n]] != dofs[vtk_mfem[j]])
{
MFEM_ABORT("VTK mesh : inconsistent quadratic mesh!");
}
}
}
}
// Define the 'Nodes' from the 'points' through the 'pts_dof' map
for (i = 0; i < np; i++)
{
dofs.SetSize(1);
if ((dofs[0] = pts_dof[i]) != -1)
{
fes->DofsToVDofs(dofs);
for (j = 0; j < dofs.Size(); j++)
{
(*Nodes)(dofs[j]) = points(3*i+j);
}
}
}
read_gf = 0;
}
}
CreateVTKMesh(points, cell_data, cell_offsets, cell_types, cell_attributes,
curved, read_gf, finalize_topo);
} // end ReadVTKMesh
void Mesh::ReadNURBSMesh(std::istream &input, int &curved, int &read_gf)
{
@@ -715,8 +916,8 @@ void Mesh::ReadNURBSMesh(std::istream &input, int &curved, int &read_gf)
NURBSext->SetCoordsFromPatches(*Nodes);
own_nodes = 1;
read_gf = 0;
int vd = Nodes->VectorDim();
for (int i = 0; i < vd; i++)
spaceDim = Nodes->VectorDim();
for (int i = 0; i < spaceDim; i++)
{
Vector vert_val;
Nodes->GetNodalValues(vert_val, i+1);
+134 -7
View File
@@ -18,7 +18,137 @@
namespace mfem
{
const int vtk_prism_perm[6] = {0, 2, 1, 3, 5, 4};
const int VTKGeometry::Map[Geometry::NUM_GEOMETRIES] =
{
POINT, SEGMENT, TRIANGLE, SQUARE, TETRAHEDRON, CUBE, PRISM
};
const int VTKGeometry::QuadraticMap[Geometry::NUM_GEOMETRIES] =
{
POINT, QUADRATIC_SEGMENT, QUADRATIC_TRIANGLE, BIQUADRATIC_SQUARE,
QUADRATIC_TETRAHEDRON, TRIQUADRATIC_CUBE, BIQUADRATIC_QUADRATIC_PRISM
};
const int VTKGeometry::HighOrderMap[Geometry::NUM_GEOMETRIES] =
{
POINT, LAGRANGE_SEGMENT, LAGRANGE_TRIANGLE, LAGRANGE_SQUARE,
LAGRANGE_TETRAHEDRON, LAGRANGE_CUBE, LAGRANGE_PRISM
};
const int VTKGeometry::PrismMap[6] = {0, 2, 1, 3, 5, 4};
const int *VTKGeometry::VertexPermutation[Geometry::NUM_GEOMETRIES] =
{
NULL, NULL, NULL, NULL, NULL, NULL, VTKGeometry::PrismMap
};
Geometry::Type VTKGeometry::GetMFEMGeometry(int vtk_geom)
{
switch (vtk_geom)
{
case POINT:
return Geometry::POINT;
case SEGMENT:
case QUADRATIC_SEGMENT:
case LAGRANGE_SEGMENT:
return Geometry::SEGMENT;
case TRIANGLE:
case QUADRATIC_TRIANGLE:
case LAGRANGE_TRIANGLE:
return Geometry::TRIANGLE;
case SQUARE:
case BIQUADRATIC_SQUARE:
case LAGRANGE_SQUARE:
return Geometry::SQUARE;
case TETRAHEDRON:
case QUADRATIC_TETRAHEDRON:
case LAGRANGE_TETRAHEDRON:
return Geometry::TETRAHEDRON;
case CUBE:
case TRIQUADRATIC_CUBE:
case LAGRANGE_CUBE:
return Geometry::CUBE;
case PRISM:
case BIQUADRATIC_QUADRATIC_PRISM:
case LAGRANGE_PRISM:
return Geometry::PRISM;
default:
return Geometry::INVALID;
}
}
bool VTKGeometry::IsLagrange(int vtk_geom)
{
return vtk_geom >= LAGRANGE_SEGMENT && vtk_geom <= LAGRANGE_PRISM;
}
bool VTKGeometry::IsQuadratic(int vtk_geom)
{
return vtk_geom >= QUADRATIC_SEGMENT
&& vtk_geom <= BIQUADRATIC_QUADRATIC_PRISM;
}
int VTKGeometry::GetOrder(int vtk_geom, int npoints)
{
if (IsQuadratic(vtk_geom))
{
return 2;
}
else if (IsLagrange(vtk_geom))
{
switch (vtk_geom)
{
case LAGRANGE_SEGMENT:
return npoints - 1;
case LAGRANGE_TRIANGLE:
return (std::sqrt(8*npoints + 1) - 3)/2;
case LAGRANGE_SQUARE:
return std::round(std::sqrt(npoints)) - 1;
case LAGRANGE_TETRAHEDRON:
switch (npoints)
{
// Note that for given order, npoints is given by
// npoints_order = (order + 1)*(order + 2)*(order + 3)/6,
case 4: return 1;
case 10: return 2;
case 20: return 3;
case 35: return 4;
case 56: return 5;
case 84: return 6;
case 120: return 7;
case 165: return 8;
case 220: return 9;
case 286: return 10;
default:
{
constexpr int max_order = 20;
int order = 11, npoints_order;
for (; order<max_order; ++order)
{
npoints_order = (order + 1)*(order + 2)*(order + 3)/6;
if (npoints_order == npoints) { break; }
}
MFEM_VERIFY(npoints == npoints_order, "");
return order;
}
}
case LAGRANGE_CUBE:
return std::round(std::cbrt(npoints)) - 1;
case LAGRANGE_PRISM:
{
const double n = npoints;
static const double third = 1.0/3.0;
static const double ninth = 1.0/9.0;
static const double twentyseventh = 1.0/27.0;
const double term =
std::cbrt(third*sqrt(third)*sqrt((27.0*n - 2.0)*n) + n
- twentyseventh);
return std::round(term + ninth / term - 4*third);
}
}
}
return 1;
}
int BarycentricToVTKTriangle(int *b, int ref)
{
@@ -379,15 +509,12 @@ void CreateVTKElementConnectivity(Array<int> &con, Geometry::Type geom, int ref)
{
int idx = 0;
int b[4];
for (int k=0; k<=ref; k++)
for (b[2]=0; b[2]<=ref; b[2]++)
{
for (int j=0; j<=k; j++)
for (b[1]=0; b[1]<=ref-b[2]; b[1]++)
{
for (int i=0; i<=j; i++)
for (b[0]=0; b[0]<=ref-b[1]-b[2]; b[0]++)
{
b[0] = k-j;
b[1] = i;
b[2] = j-i;
b[3] = ref-b[0]-b[1]-b[2];
con[BarycentricToVTKTetra(b, ref)] = idx++;
}
+40 -3
View File
@@ -17,7 +17,46 @@
namespace mfem
{
// Helpers for writing to the VTK format
// Helpers for reading and writing VTK format
// VTK element types defined at: https://git.io/JvZLm
struct VTKGeometry
{
static const int POINT = 1;
static const int SEGMENT = 3;
static const int TRIANGLE = 5;
static const int SQUARE = 9;
static const int TETRAHEDRON = 10;
static const int CUBE = 12;
static const int PRISM = 13;
static const int QUADRATIC_SEGMENT = 21;
static const int QUADRATIC_TRIANGLE = 22;
static const int BIQUADRATIC_SQUARE = 28;
static const int QUADRATIC_TETRAHEDRON = 24;
static const int TRIQUADRATIC_CUBE = 29;
static const int QUADRATIC_PRISM = 26;
static const int BIQUADRATIC_QUADRATIC_PRISM = 32;
static const int LAGRANGE_SEGMENT = 68;
static const int LAGRANGE_TRIANGLE = 69;
static const int LAGRANGE_SQUARE = 70;
static const int LAGRANGE_TETRAHEDRON = 71;
static const int LAGRANGE_CUBE = 72;
static const int LAGRANGE_PRISM = 73;
static const int PrismMap[6];
static const int *VertexPermutation[Geometry::NUM_GEOMETRIES];
static const int Map[Geometry::NUM_GEOMETRIES];
static const int QuadraticMap[Geometry::NUM_GEOMETRIES];
static const int HighOrderMap[Geometry::NUM_GEOMETRIES];
static Geometry::Type GetMFEMGeometry(int vtk_geom);
static bool IsLagrange(int vtk_geom);
static bool IsQuadratic(int vtk_geom);
static int GetOrder(int vtk_geom, int npoints);
};
enum class VTKFormat
{
@@ -26,8 +65,6 @@ enum class VTKFormat
BINARY32
};
extern const int vtk_prism_perm[6];
/// Create the VTK element connectivity array for a given element geometry and
/// refinement level. Converts node numbers from MFEM to VTK ordering.
void CreateVTKElementConnectivity(Array<int> &con, Geometry::Type geom,
+717
View File
@@ -0,0 +1,717 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
#
dimension
2
elements
18
1 3 0 1 8 7
1 3 1 2 9 8
1 3 2 3 10 9
1 3 3 4 11 10
1 3 4 5 12 11
1 3 5 6 13 12
1 3 7 8 15 14
1 3 8 9 16 15
1 3 9 10 17 16
1 3 10 11 18 17
1 3 11 12 19 18
1 3 12 13 20 19
1 3 14 15 22 21
1 3 15 16 23 22
1 3 16 17 24 23
1 3 17 18 25 24
1 3 18 19 26 25
1 3 19 20 27 26
boundary
18
4 1 0 1
4 1 1 2
4 1 2 3
4 1 3 4
4 1 4 5
4 1 5 6
2 1 22 21
2 1 23 22
2 1 24 23
2 1 25 24
2 1 26 25
2 1 27 26
4 1 7 0
4 1 14 7
4 1 21 14
4 1 6 13
4 1 13 20
4 1 20 27
vertices
28
nodes
FiniteElementSpace
FiniteElementCollection: H1_2D_P4
VDim: 2
Ordering: 0
0
1
2
3
3.1666667
3.3333333
3.5
-0.33333333
0.61111111
1.5555556
2.5
2.8888889
3.2777778
3.6666667
-0.66666667
0.22222222
1.1111111
2
2.6111111
3.2222222
3.8333333
-1
-0.16666667
0.66666667
1.5
2.3333333
3.1666667
4
0.17267316
0.5
0.82732684
0.93284932
0.80555556
0.67826179
-0.17025312
0.13888889
0.4480309
-0.057557722
-0.16666667
-0.27577561
1.1726732
1.5
1.8273268
1.9232564
1.7777778
1.6322992
0.77419132
1.0833333
1.3924753
2.1726732
2.5
2.8273268
2.9136634
2.75
2.5863366
1.7186358
2.0277778
2.3369198
3.0287789
3.0833333
3.1378878
3.1187019
3.0277778
2.9368537
2.5671507
2.6944444
2.8217382
3.1954455
3.25
3.3045545
3.3237404
3.3055556
3.2873707
2.9560396
3.0833333
3.2106271
3.3621122
3.4166667
3.4712211
3.5287789
3.5833333
3.6378878
3.3449285
3.4722222
3.599516
0.54396044
0.41666667
0.2893729
-0.51317941
-0.22222222
0.068734965
-0.39089105
-0.5
-0.60910895
1.4788119
1.3333333
1.1878547
0.37570948
0.66666667
0.95762385
2.4136634
2.25
2.0863366
1.2645984
1.5555556
1.8465127
2.8409241
2.75
2.6590759
2.1055225
2.3055556
2.5055886
3.2681848
3.25
3.2318152
2.7166336
2.9166667
3.1166997
3.6954455
3.75
3.8045545
3.3277447
3.5277778
3.7278108
0.15507155
0.027777778
-0.099515992
-0.8561057
-0.58333333
-0.31056097
-0.72422439
-0.83333333
-0.94244228
1.0343675
0.88888889
0.7434103
-0.022772363
0.25
0.52277236
1.9136634
1.75
1.5863366
0.81056097
1.0833333
1.3561057
2.5631463
2.4722222
2.3812981
1.6438943
1.9166667
2.189439
3.2126293
3.1944444
3.1762596
2.4772276
2.75
3.0227724
3.8621122
3.9166667
3.9712211
3.310561
3.5833333
3.8561057
0.113459
0.4376458
0.76183261
0.0012100212
0.31944444
0.63767887
-0.11103896
0.20124309
0.51352513
1.103866
1.4280528
1.7522397
0.97343224
1.2916667
1.6099011
0.84299844
1.1552805
1.4675625
2.0942731
2.4184599
2.7426467
1.9456545
2.2638889
2.5821233
1.7970358
2.1093179
2.4215999
2.9490681
3.0161827
3.0832973
2.7979648
2.8888889
2.979813
2.6468615
2.7615951
2.8763288
3.1541065
3.2212211
3.2883357
3.0757425
3.1666667
3.2575908
2.9973785
3.1121122
3.2268458
3.359145
3.4262596
3.4933742
3.3535203
3.4444444
3.5353686
3.3478956
3.4626293
3.5773629
-0.22946729
0.076534691
0.38253667
-0.34171627
-0.041666667
0.25838293
-0.45396524
-0.15986802
0.13422919
0.7053842
1.0113862
1.3173882
0.5749504
0.875
1.1750496
0.4445166
0.73861382
1.032711
1.6402357
1.9462377
2.2522397
1.4916171
1.7916667
2.0917163
1.3429984
1.6370957
1.9311929
2.4874399
2.6272938
2.7671477
2.3363366
2.5
2.6636634
2.1852333
2.3727062
2.5601792
2.9147006
3.0545545
3.1944084
2.8363366
3
3.1636634
2.7579726
2.9454455
3.1329185
3.3419613
3.4818152
3.6216691
3.3363366
3.5
3.6636634
3.3307119
3.5181848
3.7056578
-0.57239358
-0.28457642
0.0032407353
-0.68464255
-0.40277778
-0.120913
-0.79689153
-0.52097913
-0.24506674
0.30690236
0.59471951
0.88253667
0.17646856
0.45833333
0.74019811
0.046034758
0.32194715
0.59785955
1.1861983
1.4740155
1.7618326
1.0375797
1.3194444
1.6013092
0.88896104
1.1648734
1.4407858
2.0258117
2.2384049
2.4509981
1.8747084
2.1111111
2.3475138
1.7236051
1.9838173
2.2440296
2.6752946
2.8878878
3.100481
2.5969306
2.8333333
3.069736
2.5185666
2.7787789
3.0389911
3.3247775
3.5373707
3.7499639
3.3191528
3.5555556
3.7919583
3.3135281
3.5737404
3.8339526
0
0.5
1
1.5
1
0.5
0
0.66666667
1
1.3333333
1.6666667
1.3333333
1
0.66666667
1.3333333
1.5
1.6666667
1.8333333
1.6666667
1.5
1.3333333
2
2
2
2
2
2
2
0.086336582
0.25
0.41366342
0.58633658
0.75
0.91366342
0.72422439
0.83333333
0.94244228
0.11511544
0.33333333
0.55155122
0.58633658
0.75
0.91366342
1.0575577
1.1666667
1.2757756
1.0575577
1.1666667
1.2757756
1.0863366
1.25
1.4136634
1.5287789
1.5833333
1.6378878
1.3908911
1.5
1.6091089
1.4136634
1.25
1.0863366
1.0575577
1.1666667
1.2757756
1.6091089
1.5
1.3908911
0.91366342
0.75
0.58633658
0.58633658
0.75
0.91366342
1.2757756
1.1666667
1.0575577
0.41366342
0.25
0.086336582
0.11511544
0.33333333
0.55155122
0.94244228
0.83333333
0.72422439
1.0863366
1.25
1.4136634
1.3621122
1.4166667
1.4712211
0.78178211
1
1.2182179
1.3908911
1.5
1.6091089
1.5287789
1.5833333
1.6378878
1.6954455
1.75
1.8045545
1.6954455
1.75
1.8045545
1.3908911
1.5
1.6091089
1.8045545
1.75
1.6954455
1.0863366
1.25
1.4136634
1.6378878
1.5833333
1.5287789
0.78178211
1
1.2182179
1.4712211
1.4166667
1.3621122
1.5863366
1.75
1.9136634
2
2
2
1.4484488
1.6666667
1.8848846
1.7242244
1.8333333
1.9424423
2
2
2
1.8621122
1.9166667
1.9712211
2
2
2
1.7242244
1.8333333
1.9424423
2
2
2
1.5863366
1.75
1.9136634
2
2
2
1.4484488
1.6666667
1.8848846
2
2
2
0.19648269
0.35072601
0.50496934
0.40528049
0.54166667
0.67805285
0.61407828
0.73260732
0.85113636
0.66770383
0.82194715
0.97619048
0.82194715
0.95833333
1.0947195
0.97619048
1.0947195
1.2132486
1.138925
1.2931683
1.4474116
1.2386138
1.375
1.5113862
1.3383027
1.4568317
1.5753607
1.4474116
1.2931683
1.138925
1.5113862
1.375
1.2386138
1.5753607
1.4568317
1.3383027
0.97619048
0.82194715
0.66770383
1.0947195
0.95833333
0.82194715
1.2132486
1.0947195
0.97619048
0.50496934
0.35072601
0.19648269
0.67805285
0.54166667
0.40528049
0.85113636
0.73260732
0.61407828
0.83437049
0.93405935
1.0337482
1.0431683
1.125
1.2068317
1.2519661
1.3159407
1.3799152
1.138925
1.2386138
1.3383027
1.2931683
1.375
1.4568317
1.4474116
1.5113862
1.5753607
1.4434794
1.5431683
1.6428571
1.5431683
1.625
1.7068317
1.6428571
1.7068317
1.7708063
1.6428571
1.5431683
1.4434794
1.7068317
1.625
1.5431683
1.7708063
1.7068317
1.6428571
1.3383027
1.2386138
1.138925
1.4568317
1.375
1.2931683
1.5753607
1.5113862
1.4474116
1.0337482
0.93405935
0.83437049
1.2068317
1.125
1.0431683
1.3799152
1.3159407
1.2519661
1.4722583
1.5173927
1.5625271
1.6810561
1.7083333
1.7356106
1.8898539
1.899274
1.9086941
1.6101461
1.6552805
1.7004149
1.7643894
1.7916667
1.8189439
1.9186328
1.9280528
1.9374729
1.7480339
1.7931683
1.8383027
1.8477228
1.875
1.9022772
1.9474116
1.9568317
1.9662518
1.8383027
1.7931683
1.7480339
1.9022772
1.875
1.8477228
1.9662518
1.9568317
1.9474116
1.7004149
1.6552805
1.6101461
1.8189439
1.7916667
1.7643894
1.9374729
1.9280528
1.9186328
1.5625271
1.5173927
1.4722583
1.7356106
1.7083333
1.6810561
1.9086941
1.899274
1.8898539
+83 -45
View File
@@ -33,11 +33,11 @@
//
// Sample runs:
// Adapted analytic shape:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -bnd -qt 1 -qo 8
// Adapted analytic size+orientation:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
// Adapted analytic shape+orientation:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
//
// Adapted discrete size:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 80 -tid 5 -ni 50 -qo 4 -nor
@@ -49,9 +49,9 @@
// Adapted discrete size+orientation (requires GSLIB):
// * mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 36 -tid 8 -qo 4 -fd -ae 1 -nor
// Adapted discrete aspect-ratio+orientation (requires GSLIB):
// * mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 8 -ni 10 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd -ae 1
// * mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 8 -ni 10 -bnd -qt 1 -qo 8 -fd -ae 1
// Adapted discrete aspect ratio (3D):
// mesh-optimizer -m cube.mesh -o 2 -rs 2 -mid 302 -tid 7 -ni 20 -ls 2 -li 100 -bnd -qt 1 -qo 8
// mesh-optimizer -m cube.mesh -o 2 -rs 2 -mid 302 -tid 7 -ni 20 -bnd -qt 1 -qo 8
//
// Adaptive limiting:
// mesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5
@@ -61,28 +61,33 @@
// * mesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5 -fd -ae 1
//
// Blade shape:
// mesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// mesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 3 -art 1 -bnd -qt 1 -qo 8
// Blade shape with FD-based solver:
// mesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd
// mesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 4 -bnd -qt 1 -qo 8 -fd
// Blade limited shape:
// mesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8 -lc 5000
// mesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -bnd -qt 1 -qo 8 -lc 5000
// ICF shape and equal size:
// mesh-optimizer -o 3 -rs 0 -mid 9 -tid 2 -ni 25 -ls 3 -qo 5
// mesh-optimizer -o 3 -mid 9 -tid 2 -ni 25 -ls 3 -art 2 -qo 5
// ICF shape and initial size:
// mesh-optimizer -o 3 -rs 0 -mid 9 -tid 3 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8
// mesh-optimizer -o 3 -mid 9 -tid 3 -ni 30 -ls 3 -bnd -qt 1 -qo 8
// ICF shape:
// mesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8
// mesh-optimizer -o 3 -mid 1 -tid 1 -ni 100 -bnd -qt 1 -qo 8
// ICF limited shape:
// mesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8 -lc 10
// mesh-optimizer -o 3 -mid 1 -tid 1 -ni 100 -bnd -qt 1 -qo 8 -lc 10
// ICF combo shape + size (rings, slow convergence):
// mesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 1000 -ls 2 -li 100 -bnd -qt 1 -qo 8 -cmb 1
// mesh-optimizer -o 3 -mid 1 -tid 1 -ni 1000 -bnd -qt 1 -qo 8 -cmb 1
// Mixed tet / cube / hex mesh with limiting:
// mesh-optimizer -m ../../data/fichera-mixed-p2.mesh -o 4 -rs 1 -mid 301 -tid 1 -fix-bnd -qo 6 -nor -lc 0.25
// 3D pinched sphere shape (the mesh is in the mfem/data GitHub repository):
// * mesh-optimizer -m ../../../mfem_data/ball-pert.mesh -o 4 -rs 0 -mid 303 -tid 1 -ni 20 -ls 2 -li 500 -fix-bnd
// * mesh-optimizer -m ../../../mfem_data/ball-pert.mesh -o 4 -mid 303 -tid 1 -ni 20 -li 500 -fix-bnd
// 2D non-conforming shape and equal size:
// mesh-optimizer -m ./amr-quad-q2.mesh -o 2 -rs 1 -mid 9 -tid 2 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// mesh-optimizer -m ./amr-quad-q2.mesh -o 2 -rs 1 -mid 9 -tid 2 -ni 200 -bnd -qt 1 -qo 8
//
// 2D untangling:
// mesh-optimizer -m jagged.mesh -o 2 -mid 22 -tid 1 -ni 50 -li 50 -qo 4 -fd -vl 1
// 3D untangling (the mesh is in the mfem/data GitHub repository):
// * mesh-optimizer -m ../../../mfem_data/cube-holes-inv.mesh -o 3 -mid 313 -tid 1 -rtol 1e-5 -li 50 -qo 4 -fd -vl 1
//
#include "mfem.hpp"
#include "../common/mfem-common.hpp"
@@ -109,6 +114,7 @@ int main(int argc, char *argv[])
int solver_type = 0;
int solver_iter = 20;
double solver_rtol = 1e-10;
int solver_art_type = 0;
int lin_solver = 2;
int max_lin_iter = 100;
bool move_bnd = true;
@@ -147,15 +153,17 @@ int main(int argc, char *argv[])
"80 : (1-gamma)mu_2 + gamma mu_77 -- 2D shape+size\n\t"
"85 : |T-|T|/sqrt(2)I|^2 -- 2D shape+orientation\n\t"
"98 : (1/tau)|T-I|^2 -- 2D shape+size+orientation\n\t"
"211: (tau-1)^2-tau+sqrt(tau^2) -- 2D untangling\n\t"
"252: 0.5(tau-1)^2/(tau-tau_0) -- 2D untangling\n\t"
// "211: (tau-1)^2-tau+sqrt(tau^2) -- 2D untangling\n\t"
// "252: 0.5(tau-1)^2/(tau-tau_0) -- 2D untangling\n\t"
"301: (|T||T^-1|)/3-1 -- 3D shape\n\t"
"302: (|T|^2|T^-1|^2)/9-1 -- 3D shape\n\t"
"303: (|T|^2)/3*tau^(2/3)-1 -- 3D shape\n\t"
//"311: (tau-1)^2-tau+sqrt(tau^2+eps)-- 3D untangling\n\t"
"313: (|T|^2)(tau-tau0)^(-2/3)/3 -- 3D untangling\n\t"
"315: (tau-1)^2 -- 3D size\n\t"
"316: 0.5(sqrt(tau)-1/sqrt(tau))^2 -- 3D size\n\t"
"321: |T-T^-t|^2 -- 3D shape+size\n\t"
"352: 0.5(tau-1)^2/(tau-tau_0) -- 3D untangling\n\t"
// "352: 0.5(tau-1)^2/(tau-tau_0) -- 3D untangling\n\t"
"A-metrics\n\t"
"11 : (1/4*alpha)|A-(adjA)^T(W^TW)/omega|^2 -- 2D shape\n\t"
"36 : (1/alpha)|A-W|^2 -- 2D shape+size+orientation\n\t"
@@ -185,12 +193,17 @@ int main(int argc, char *argv[])
"Maximum number of Newton iterations.");
args.AddOption(&solver_rtol, "-rtol", "--newton-rel-tolerance",
"Relative tolerance for the Newton solver.");
args.AddOption(&solver_art_type, "-art", "--adaptive-rel-tol",
"Type of adaptive relative linear solver tolerance:\n\t"
"0: None (default)\n\t"
"1: Eisenstat-Walker type 1\n\t"
"2: Eisenstat-Walker type 2");
args.AddOption(&lin_solver, "-ls", "--lin-solver",
"Linear solver:\n\t"
"0: l1-Jacobi\n\t"
"1: CG\n\t"
"2: MINRES\n\t"
"3: MINRES + Jacobi preconditioner"
"3: MINRES + Jacobi preconditioner\n\t"
"4: MINRES + l1-Jacobi preconditioner");
args.AddOption(&max_lin_iter, "-li", "--lin-iter",
"Maximum number of iterations in the linear solve.");
@@ -198,7 +211,7 @@ int main(int argc, char *argv[])
"--fix-boundary",
"Enable motion along horizontal and vertical boundaries.");
args.AddOption(&combomet, "-cmb", "--combo-type",
"Combination of metrics options:"
"Combination of metrics options:\n\t"
"0: Use single metric\n\t"
"1: Shape + space-dependent size given analytically\n\t"
"2: Shape + adapted size given discretely; shared target");
@@ -345,15 +358,17 @@ int main(int argc, char *argv[])
case 80: metric = new TMOP_Metric_080(0.5); break;
case 85: metric = new TMOP_Metric_085; break;
case 98: metric = new TMOP_Metric_098; break;
case 211: metric = new TMOP_Metric_211; break;
case 252: metric = new TMOP_Metric_252(tauval); break;
// case 211: metric = new TMOP_Metric_211; break;
// case 252: metric = new TMOP_Metric_252(tauval); break;
case 301: metric = new TMOP_Metric_301; break;
case 302: metric = new TMOP_Metric_302; break;
case 303: metric = new TMOP_Metric_303; break;
// case 311: metric = new TMOP_Metric_311; break;
case 313: metric = new TMOP_Metric_313(tauval); break;
case 315: metric = new TMOP_Metric_315; break;
case 316: metric = new TMOP_Metric_316; break;
case 321: metric = new TMOP_Metric_321; break;
case 352: metric = new TMOP_Metric_352(tauval); break;
// case 352: metric = new TMOP_Metric_352(tauval); break;
// A-metrics
case 11: metric = new TMOP_AMetric_011; break;
case 36: metric = new TMOP_AMetric_036; break;
@@ -370,7 +385,7 @@ int main(int argc, char *argv[])
FiniteElementSpace ind_fes(mesh, &ind_fec);
FiniteElementSpace ind_fesv(mesh, &ind_fec, dim);
GridFunction size(&ind_fes), aspr(&ind_fes), disc(&ind_fes), ori(&ind_fes);
GridFunction aspr3d(&ind_fesv), size3d(&ind_fesv);
GridFunction aspr3d(&ind_fesv);
switch (target_id)
{
case 1: target_t = TargetConstructor::IDEAL_SHAPE_UNIT_SIZE; break;
@@ -428,14 +443,14 @@ int main(int argc, char *argv[])
#endif
}
//Diffuse the interface
// Diffuse the interface
DiffuseField(disc,2);
//Get partials with respect to x and y of the grid function
// Get partials with respect to x and y of the grid function
disc.GetDerivative(1,0,d_x);
disc.GetDerivative(1,1,d_y);
//Compute the squared magnitude of the gradient
// Compute the squared magnitude of the gradient
for (int i = 0; i < size.Size(); i++)
{
size(i) = std::pow(d_x(i),2)+std::pow(d_y(i),2);
@@ -687,6 +702,40 @@ int main(int argc, char *argv[])
}
else { a.AddDomainIntegrator(he_nlf_integ); }
// Compute the minimum det(J) of the starting mesh.
tauval = infinity();
const int NE = mesh->GetNE();
for (int i = 0; i < NE; i++)
{
const IntegrationRule &ir =
irules->Get(fespace->GetFE(i)->GetGeomType(), quad_order);
ElementTransformation *transf = mesh->GetElementTransformation(i);
for (int j = 0; j < ir.GetNPoints(); j++)
{
transf->SetIntPoint(&ir.IntPoint(j));
tauval = min(tauval, transf->Jacobian().Det());
}
}
cout << "Minimum det(J) of the original mesh is " << tauval << endl;
if (tauval < 0.0 && metric_id != 22 && metric_id != 211 && metric_id != 252
&& metric_id != 311 && metric_id != 313 && metric_id != 352)
{
MFEM_ABORT("The input mesh is inverted! Try an untangling metric.");
}
if (tauval < 0.0)
{
MFEM_VERIFY(target_t == TargetConstructor::IDEAL_SHAPE_UNIT_SIZE,
"Untangling is supported only for ideal targets.");
const DenseMatrix &Wideal =
Geometries.GetGeomToPerfGeomJac(fespace->GetFE(0)->GetGeomType());
tauval /= Wideal.Det();
// Slightly below minJ0 to avoid div by 0.
tauval -= 0.01 * h0.Min();
}
const double init_energy = a.GetGridFunctionEnergy(x);
// Visualize the starting mesh and metric values.
@@ -786,23 +835,6 @@ int main(int argc, char *argv[])
S = minres;
}
// Compute the minimum det(J) of the starting mesh.
tauval = infinity();
const int NE = mesh->GetNE();
for (int i = 0; i < NE; i++)
{
const IntegrationRule &ir =
irules->Get(fespace->GetFE(i)->GetGeomType(), quad_order);
ElementTransformation *transf = mesh->GetElementTransformation(i);
for (int j = 0; j < ir.GetNPoints(); j++)
{
transf->SetIntPoint(&ir.IntPoint(j));
tauval = min(tauval, transf->Jacobian().Det());
}
}
cout << "Minimum det(J) of the original mesh is " << tauval << endl;
tauval -= 0.01 * h0.Min(); // Slightly below minJ0 to avoid div by 0.
// Perform the nonlinear optimization.
const IntegrationRule &ir =
irules->Get(fespace->GetFE(0)->GetGeomType(), quad_order);
@@ -814,9 +846,15 @@ int main(int argc, char *argv[])
// Specify linear solver when we use a Newton-based solver.
solver.SetPreconditioner(*S);
}
// For untangling, the solver will update the min det(T) values.
if (tauval < 0.0) { solver.SetMinDetPtr(&tauval); }
solver.SetMaxIter(solver_iter);
solver.SetRelTol(solver_rtol);
solver.SetAbsTol(0.0);
if (solver_art_type > 0)
{
solver.SetAdaptiveLinRtol(solver_art_type, 0.5, 0.9);
}
solver.SetPrintLevel(verbosity_level >= 1 ? 1 : -1);
solver.SetOperator(a);
solver.Mult(b, x.GetTrueVector());
+90 -51
View File
@@ -33,11 +33,11 @@
//
// Sample runs:
// Adapted analytic shape:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -bnd -qt 1 -qo 8
// Adapted analytic size+orientation:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 200 -bnd -qt 1 -qo 8 -fd
// Adapted analytic shape+orientation:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
//
// Adapted discrete size:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 80 -tid 5 -ni 50 -qo 4 -nor
@@ -49,9 +49,9 @@
// Adapted discrete size+orientation (requires GSLIB):
// * mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 36 -tid 8 -qo 4 -fd -ae 1 -nor
// Adapted discrete aspect_ratio+orientation (requires GSLIB):
// * mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 8 -ni 10 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd -ae 1
// * mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 8 -ni 10 -bnd -qt 1 -qo 8 -fd -ae 1
// Adapted discrete aspect ratio (3D):
// mpirun -np 4 pmesh-optimizer -m cube.mesh -o 2 -rs 2 -mid 302 -tid 7 -ni 20 -ls 2 -li 100 -bnd -qt 1 -qo 8
// mpirun -np 4 pmesh-optimizer -m cube.mesh -o 2 -rs 2 -mid 302 -tid 7 -ni 20 -bnd -qt 1 -qo 8
//
// Adaptive limiting:
// mpirun -np 4 pmesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5
@@ -61,27 +61,33 @@
// * mpirun -np 4 pmesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5 -fd -ae 1
//
// Blade shape:
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 3 -art 1 -bnd -qt 1 -qo 8
// Blade shape with FD-based solver:
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8 -fd
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 4 -bnd -qt 1 -qo 8 -fd
// Blade limited shape:
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8 -lc 5000
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -bnd -qt 1 -qo 8 -lc 5000
// ICF shape and equal size:
// mpirun -np 4 pmesh-optimizer -o 3 -rs 0 -mid 9 -tid 2 -ni 25 -ls 3 -qo 5
// mpirun -np 4 pmesh-optimizer -o 3 -mid 9 -tid 2 -ni 25 -ls 3 -art 2 -qo 5
// ICF shape and initial size:
// mpirun -np 4 pmesh-optimizer -o 3 -rs 0 -mid 9 -tid 3 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8
// mpirun -np 4 pmesh-optimizer -o 3 -mid 9 -tid 3 -ni 30 -ls 3 -bnd -qt 1 -qo 8
// ICF shape:
// mpirun -np 4 pmesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8
// mpirun -np 4 pmesh-optimizer -o 3 -mid 1 -tid 1 -ni 100 -bnd -qt 1 -qo 8
// ICF limited shape:
// mpirun -np 4 pmesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 100 -ls 2 -li 100 -bnd -qt 1 -qo 8 -lc 10
// mpirun -np 4 pmesh-optimizer -o 3 -mid 1 -tid 1 -ni 100 -bnd -qt 1 -qo 8 -lc 10
// ICF combo shape + size (rings, slow convergence):
// mpirun -np 4 pmesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 1000 -ls 2 -li 100 -bnd -qt 1 -qo 8 -cmb 1
// mpirun -np 4 pmesh-optimizer -o 3 -mid 1 -tid 1 -ni 1000 -bnd -qt 1 -qo 8 -cmb 1
// Mixed tet / cube / hex mesh with limiting:
// mpirun -np 4 pmesh-optimizer -m ../../data/fichera-mixed-p2.mesh -o 4 -rs 1 -mid 301 -tid 1 -fix-bnd -qo 6 -nor -lc 0.25
// 3D pinched sphere shape (the mesh is in the mfem/data GitHub repository):
// * mpirun -np 4 pmesh-optimizer -m ../../../mfem_data/ball-pert.mesh -o 4 -rs 0 -mid 303 -tid 1 -ni 20 -ls 2 -li 500 -fix-bnd
// * mpirun -np 4 pmesh-optimizer -m ../../../mfem_data/ball-pert.mesh -o 4 -mid 303 -tid 1 -ni 20 -li 500 -fix-bnd
// 2D non-conforming shape and equal size:
// mpirun -np 4 pmesh-optimizer -m ./amr-quad-q2.mesh -o 2 -rs 1 -mid 9 -tid 2 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// mpirun -np 4 pmesh-optimizer -m ./amr-quad-q2.mesh -o 2 -rs 1 -mid 9 -tid 2 -ni 200 -bnd -qt 1 -qo 8
//
// 2D untangling:
// mpirun -np 4 pmesh-optimizer -m jagged.mesh -o 2 -mid 22 -tid 1 -ni 50 -li 50 -qo 4 -fd -vl 1
// 3D untangling (the mesh is in the mfem/data GitHub repository):
// * mpirun -np 4 pmesh-optimizer -m ../../../mfem_data/cube-holes-inv.mesh -o 3 -mid 313 -tid 1 -rtol 1e-5 -li 50 -qo 4 -fd -vl 1
//
#include "mfem.hpp"
#include "../common/mfem-common.hpp"
@@ -115,6 +121,7 @@ int main (int argc, char *argv[])
int solver_type = 0;
int solver_iter = 20;
double solver_rtol = 1e-10;
int solver_art_type = 0;
int lin_solver = 2;
int max_lin_iter = 100;
bool move_bnd = true;
@@ -155,15 +162,17 @@ int main (int argc, char *argv[])
"80 : (1-gamma)mu_2 + gamma mu_77 -- 2D shape+size\n\t"
"85 : |T-|T|/sqrt(2)I|^2 -- 2D shape+orientation\n\t"
"98 : (1/tau)|T-I|^2 -- 2D shape+size+orientation\n\t"
"211: (tau-1)^2-tau+sqrt(tau^2) -- 2D untangling\n\t"
"252: 0.5(tau-1)^2/(tau-tau_0) -- 2D untangling\n\t"
// "211: (tau-1)^2-tau+sqrt(tau^2+eps) -- 2D untangling\n\t"
// "252: 0.5(tau-1)^2/(tau-tau_0) -- 2D untangling\n\t"
"301: (|T||T^-1|)/3-1 -- 3D shape\n\t"
"302: (|T|^2|T^-1|^2)/9-1 -- 3D shape\n\t"
"303: (|T|^2)/3*tau^(2/3)-1 -- 3D shape\n\t"
// "311: (tau-1)^2-tau+sqrt(tau^2+eps)-- 3D untangling\n\t"
"313: (|T|^2)(tau-tau0)^(-2/3)/3 -- 3D untangling\n\t"
"315: (tau-1)^2 -- 3D size\n\t"
"316: 0.5(sqrt(tau)-1/sqrt(tau))^2 -- 3D size\n\t"
"321: |T-T^-t|^2 -- 3D shape+size\n\t"
"352: 0.5(tau-1)^2/(tau-tau_0) -- 3D untangling\n\t"
// "352: 0.5(tau-1)^2/(tau-tau_0) -- 3D untangling\n\t"
"A-metrics\n\t"
"11 : (1/4*alpha)|A-(adjA)^T(W^TW)/omega|^2 -- 2D shape\n\t"
"36 : (1/alpha)|A-W|^2 -- 2D shape+size+orientation\n\t"
@@ -193,12 +202,17 @@ int main (int argc, char *argv[])
"Maximum number of Newton iterations.");
args.AddOption(&solver_rtol, "-rtol", "--newton-rel-tolerance",
"Relative tolerance for the Newton solver.");
args.AddOption(&solver_art_type, "-art", "--adaptive-rel-tol",
"Type of adaptive relative linear solver tolerance:\n\t"
"0: None (default)\n\t"
"1: Eisenstat-Walker type 1\n\t"
"2: Eisenstat-Walker type 2");
args.AddOption(&lin_solver, "-ls", "--lin-solver",
"Linear solver:\n\t"
"0: l1-Jacobi\n\t"
"1: CG\n\t"
"2: MINRES\n\t"
"3: MINRES + Jacobi preconditioner"
"3: MINRES + Jacobi preconditioner\n\t"
"4: MINRES + l1-Jacobi preconditioner");
args.AddOption(&max_lin_iter, "-li", "--lin-iter",
"Maximum number of iterations in the linear solve.");
@@ -206,7 +220,7 @@ int main (int argc, char *argv[])
"--fix-boundary",
"Enable motion along horizontal and vertical boundaries.");
args.AddOption(&combomet, "-cmb", "--combo-type",
"Combination of metrics options:"
"Combination of metrics options:\n\t"
"0: Use single metric\n\t"
"1: Shape + space-dependent size given analytically\n\t"
"2: Shape + adapted size given discretely; shared target");
@@ -374,15 +388,17 @@ int main (int argc, char *argv[])
case 80: metric = new TMOP_Metric_080(0.5); break;
case 85: metric = new TMOP_Metric_085; break;
case 98: metric = new TMOP_Metric_098; break;
case 211: metric = new TMOP_Metric_211; break;
case 252: metric = new TMOP_Metric_252(tauval); break;
// case 211: metric = new TMOP_Metric_211; break;
// case 252: metric = new TMOP_Metric_252(tauval); break;
case 301: metric = new TMOP_Metric_301; break;
case 302: metric = new TMOP_Metric_302; break;
case 303: metric = new TMOP_Metric_303; break;
// case 311: metric = new TMOP_Metric_311; break;
case 313: metric = new TMOP_Metric_313(tauval); break;
case 315: metric = new TMOP_Metric_315; break;
case 316: metric = new TMOP_Metric_316; break;
case 321: metric = new TMOP_Metric_321; break;
case 352: metric = new TMOP_Metric_352(tauval); break;
// case 352: metric = new TMOP_Metric_352(tauval); break;
// A-metrics
case 11: metric = new TMOP_AMetric_011; break;
case 36: metric = new TMOP_AMetric_036; break;
@@ -399,7 +415,7 @@ int main (int argc, char *argv[])
ParFiniteElementSpace ind_fes(pmesh, &ind_fec);
ParFiniteElementSpace ind_fesv(pmesh, &ind_fec, dim);
ParGridFunction size(&ind_fes), aspr(&ind_fes), disc(&ind_fes), ori(&ind_fes);
ParGridFunction aspr3d(&ind_fesv), size3d(&ind_fesv);
ParGridFunction aspr3d(&ind_fesv);
switch (target_id)
{
@@ -437,7 +453,7 @@ int main (int argc, char *argv[])
target_c = tc;
break;
}
case 6: //material indicator 2D
case 6: // material indicator 2D
{
ParGridFunction d_x(&ind_fes), d_y(&ind_fes);
@@ -457,14 +473,14 @@ int main (int argc, char *argv[])
MFEM_ABORT("MFEM is not built with GSLIB.");
#endif
}
//Diffuse the interface
// Diffuse the interface
DiffuseField(disc,2);
//Get partials with respect to x and y of the grid function
// Get partials with respect to x and y of the grid function
disc.GetDerivative(1,0,d_x);
disc.GetDerivative(1,1,d_y);
//Compute the squared magnitude of the gradient
// Compute the squared magnitude of the gradient
for (int i = 0; i < size.Size(); i++)
{
size(i) = std::pow(d_x(i),2)+std::pow(d_y(i),2);
@@ -727,6 +743,46 @@ int main (int argc, char *argv[])
}
else { a.AddDomainIntegrator(he_nlf_integ); }
// Compute the minimum det(J) of the starting mesh.
tauval = infinity();
const int NE = pmesh->GetNE();
for (int i = 0; i < NE; i++)
{
const IntegrationRule &ir =
irules->Get(pfespace->GetFE(i)->GetGeomType(), quad_order);
ElementTransformation *transf = pmesh->GetElementTransformation(i);
for (int j = 0; j < ir.GetNPoints(); j++)
{
transf->SetIntPoint(&ir.IntPoint(j));
tauval = min(tauval, transf->Jacobian().Det());
}
}
double minJ0;
MPI_Allreduce(&tauval, &minJ0, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
tauval = minJ0;
if (myid == 0)
{ cout << "Minimum det(J) of the original mesh is " << tauval << endl; }
if (tauval < 0.0 && metric_id != 22 && metric_id != 211 && metric_id != 252
&& metric_id != 311 && metric_id != 313 && metric_id != 352)
{
MFEM_ABORT("The input mesh is inverted! Try an untangling metric.");
}
if (tauval < 0.0)
{
MFEM_VERIFY(target_t == TargetConstructor::IDEAL_SHAPE_UNIT_SIZE,
"Untangling is supported only for ideal targets.");
const DenseMatrix &Wideal =
Geometries.GetGeomToPerfGeomJac(pfespace->GetFE(0)->GetGeomType());
tauval /= Wideal.Det();
double h0min = h0.Min(), h0min_all;
MPI_Allreduce(&h0min, &h0min_all, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
// Slightly below minJ0 to avoid div by 0.
tauval -= 0.01 * h0min_all;
}
const double init_energy = a.GetParGridFunctionEnergy(x);
// Visualize the starting mesh and metric values.
@@ -829,29 +885,6 @@ int main (int argc, char *argv[])
S = minres;
}
// Compute the minimum det(J) of the starting mesh.
tauval = infinity();
const int NE = pmesh->GetNE();
for (int i = 0; i < NE; i++)
{
const IntegrationRule &ir =
irules->Get(pfespace->GetFE(i)->GetGeomType(), quad_order);
ElementTransformation *transf = pmesh->GetElementTransformation(i);
for (int j = 0; j < ir.GetNPoints(); j++)
{
transf->SetIntPoint(&ir.IntPoint(j));
tauval = min(tauval, transf->Jacobian().Det());
}
}
double minJ0;
MPI_Allreduce(&tauval, &minJ0, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
tauval = minJ0;
if (myid == 0)
{ cout << "Minimum det(J) of the original mesh is " << tauval << endl; }
double h0min = h0.Min(), h0min_all;
MPI_Allreduce(&h0min, &h0min_all, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
tauval -= 0.01 * h0min_all; // Slightly below minJ0 to avoid div by 0.
// Perform the nonlinear optimization.
const IntegrationRule &ir =
irules->Get(pfespace->GetFE(0)->GetGeomType(), quad_order);
@@ -863,9 +896,15 @@ int main (int argc, char *argv[])
// Specify linear solver when we use a Newton-based solver.
solver.SetPreconditioner(*S);
}
// For untangling, the solver will update the min det(T) values.
if (tauval < 0.0) { solver.SetMinDetPtr(&tauval); }
solver.SetMaxIter(solver_iter);
solver.SetRelTol(solver_rtol);
solver.SetAbsTol(0.0);
if (solver_art_type > 0)
{
solver.SetAdaptiveLinRtol(solver_art_type, 0.5, 0.9);
}
solver.SetPrintLevel(verbosity_level >= 1 ? 1 : -1);
solver.SetOperator(a);
solver.Mult(b, x.GetTrueVector());
+14 -1
View File
@@ -22,6 +22,7 @@ set(UNIT_TESTS_SRCS
general/test_text.cpp
general/test_zlib.cpp
linalg/test_complex_operator.cpp
linalg/test_direct_solvers.cpp
linalg/test_hypre_ilu.cpp
linalg/test_ilu.cpp
linalg/test_matrix_block.cpp
@@ -41,21 +42,26 @@ set(UNIT_TESTS_SRCS
fem/test_2d_bilininteg.cpp
fem/test_3d_bilininteg.cpp
fem/test_assemblediagonalpa.cpp
fem/test_blocknonlinearform.cpp
fem/test_calcshape.cpp
fem/test_datacollection.cpp
fem/test_estimator.cpp
fem/test_face_permutation.cpp
fem/test_fe.cpp
fem/test_intrules.cpp
fem/test_intruletypes.cpp
fem/test_inversetransform.cpp
fem/test_lexicographic_ordering.cpp
fem/test_lin_interp.cpp
fem/test_linear_fes.cpp
fem/test_operatorjacobismoother.cpp
fem/test_pa_coeff.cpp
fem/test_pa_kernels.cpp
fem/test_pa_grad.cpp
fem/test_pa_idinterp.cpp
fem/test_quadf_coef.cpp
fem/test_quadraturefunc.cpp
fem/test_blocknonlinearform.cpp
fem/test_sum_bilin.cpp
miniapps/test_sedov.cpp
)
@@ -202,6 +208,13 @@ if (MFEM_USE_MPI)
target_compile_definitions(psedov_tests_cuda_uvm PUBLIC MFEM_SEDOV_TESTS=1)
target_compile_definitions(psedov_tests_cuda_uvm PUBLIC MFEM_SEDOV_DEVICE="cuda:uvm")
target_link_libraries(psedov_tests_cuda_uvm mfem)
set(PCUNIT_TESTS_SRCS pcunit_test_main.cpp)
set_property(SOURCE ${PCUNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
add_executable(pcunit_tests ${PCUNIT_TESTS_SRCS})
add_dependencies(pcunit_tests copy_data)
target_link_libraries(pcunit_tests mfem)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} pcunit_tests)
endif()
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} punit_tests psedov_tests_cpu psedov_tests_debug)

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