Compare commits

...
Author SHA1 Message Date
psocratis 174b930544 testing amr results for l-shape domain problem 2021-08-17 19:52:22 -07:00
psocratis afc10818a0 minor 2021-08-04 17:45:20 -07:00
psocratis e9088d9b84 adding AMR example for element-smoother 2021-07-23 17:08:19 -07:00
psocratis d62c094e97 adding diffusionMG class with element-smoother 2021-07-22 16:40:48 -07:00
psocratis 9541aa6026 diffusion par example testing the smoothers 2021-07-22 12:53:29 -07:00
psocratis bf83b55578 adding new constructor to Chebychev smoother for a generic preconditioner (not just the diagonal) 2021-07-22 12:53:02 -07:00
psocratis 8a4d53098b porting par-el-smoother 1st commit 2021-07-21 18:30:07 -07:00
psocratis 026d4834d2 Merge branch 'master' into fdsolver 2021-07-20 13:59:28 -07:00
Tzanio Kolev d0caa1c432 Merge pull request #2069 from mfem/testing/bernede1/hypre-bigint
Add a CI test for Hypre Big Int [testing/bernede1/hypre-bigint]
2021-07-20 08:48:38 -07:00
Adrien M. Bernede 603e545060 Name coverage report with job name 2021-07-20 07:52:38 -07:00
Adrien M. Bernede 8db0c68dd2 Merge remote-tracking branch 'origin/master' into testing/bernede1/hypre-bigint 2021-07-20 06:11:47 -07:00
Adrien Bernede 6e6a3d9751 Merge pull request #2415 from mfem/testing/bernede1/compact-names
Compact names for job parameters in GitHub Actions
2021-07-20 03:39:58 -07:00
Adrien M. Bernede 5f1b8614c6 Point to mfem action "release" versions 2021-07-20 01:19:17 -07:00
Adrien M. Bernede a05a93c187 Fix 2021-07-20 00:34:05 -07:00
Adrien M. Bernede 7864972fc1 Compact names for job parameters in GitHub Actions 2021-07-20 00:18:08 -07:00
Tzanio Kolev ab73eea018 Merge pull request #2414 from mfem/doc/bernede1/workflow-badge
Add Github Actions Badges to contributing. [doc/bernede1/workflow-badge]
2021-07-19 15:33:07 -07:00
Adrien M. Bernede 81421858f3 Fix file name 2021-07-19 14:55:31 -07:00
Adrien M. Bernede 9f7baced30 Add Github Actions Bagdes to contributing. 2021-07-19 14:40:55 -07:00
Adrien M. Bernede a45d86ff2a Remove redundant job 2021-07-19 14:06:29 -07:00
Tzanio Kolev a17c6530ff Merge pull request #2397 from mfem/testing/bernede1/disable-travis
Remove Travis [testing/bernede1/disable-travis]
2021-07-18 18:54:16 -07:00
Tzanio Kolev 00b1ae1850 Merge pull request #2392 from mfem/mfem-data-unit-tests
GitLab CI using mfem/data [mfem-data-unit-tests]
2021-07-18 18:49:21 -07:00
Tzanio Kolev 3c25eeab3d Merge pull request #2403 from mfem/DerivativeIntegrator_fix
DerivativeIntegrator for 1D embedded geometry
2021-07-15 08:15:27 -07:00
Adrien M. Bernede ac91aef8f9 Match cache key suffix with version of action used 2021-07-15 06:35:18 -07:00
Adrien M. Bernede c86860362a New version for mfem actions:
v1.0 -> v2.0 (breaking change in the interface).
2021-07-15 06:32:13 -07:00
Adrien M. Bernede f5991995c3 Fix missing use of HYPRE_Int for build to pass 2021-07-15 04:10:26 -07:00
Adrien M. Bernede b8264a5fb2 Remove useless hypre-target specification for MFEM (auto-detected) 2021-07-14 10:19:39 -07:00
Veselin Dobrev cf90fad7fb Merge pull request #2375 from mfem/bugfix-lininteg
Bug fix in ParLinearForm and changing integrator names
2021-07-13 11:50:38 -07:00
Syun'ichi Shiraiwa f8dae2249f changef size of array 2021-07-12 16:46:08 -04:00
Tzanio Kolev fd7b54c41a Merge pull request #2394 from mfem/fix-sbmprint
Fix print in the shifted diffusion miniapp
2021-07-12 08:29:02 -07:00
Tzanio Kolev 4d8cd51cca Merge pull request #2396 from mfem/bnl_bug
Bug in the assembly  for BlockNonlinearForm
2021-07-12 08:28:35 -07:00
Veselin Dobrev d25c9ffff0 Formatting adjustments 2021-07-09 19:04:19 -07:00
Tzanio Kolev 72204b7f01 Merge pull request #2370 from mfem/op-by-ref-dev
Changing OperatorChebyshevSmoother to pass-by-reference [op-by-ref-dev]
2021-07-09 16:02:12 -07:00
Adrien M. Bernede 4d037ca87a Remove Travis 2021-07-08 10:03:13 -07:00
blaz 49a47922f2 make style 2021-07-07 21:26:44 -07:00
blaz f3b094d8d8 bug in the assembly for faces 2021-07-07 21:14:41 -07:00
Ketan Mittal 46ed6ee7b6 print from myid = 0 only 2021-07-07 10:28:39 -07:00
Will Pazner 22849928ac Fix symlinking issue 2021-07-07 09:59:02 -07:00
Will Pazner ea0dfd78db Refactor unit test main files 2021-07-06 22:37:45 -07:00
Will Pazner 27d6b2be89 Add quartz to setup job tags 2021-07-06 22:25:46 -07:00
Will Pazner 7be7168769 Unit tests that depend on mfem/data repository
Only run unit tests if data directory is present
Clone mfem/data in GitLab CI
2021-07-06 17:21:08 -07:00
Will Pazner 6f637f57b0 Fix signed-unsigned comparisons 2021-07-06 17:11:56 -07:00
Tzanio Kolev bf542247e8 Merge pull request #2074 from mfem/bugfix-coarse_to_fine_map_derefine
Bugfix - CoarseToFineMap currently fails for derefinement transform
2021-07-06 13:10:32 -07:00
Will Pazner 0cd6d21e8f Reduce code duplication in OperatorChebyshevSmoother constructors 2021-07-06 10:47:26 -07:00
Tzanio Kolev 4557ab17b4 Merge pull request #2372 from mfem/nd-segment-tensor
Make ND_SegmentElement inherit from VectorTensorFiniteElement [nd-segment-tensor]
2021-07-04 10:25:58 -07:00
Ketan Mittal 844644957d Merge branch 'master' of https://github.com/mfem/mfem into bugfix-coarse_to_fine_map_derefine 2021-07-02 09:24:58 -07:00
Ketan Mittal a0dfc29c71 move implementation to cpp 2021-07-02 09:24:41 -07:00
Will Pazner 0446a8fa32 Improve comments 2021-07-01 11:30:49 -07:00
Will Pazner 4256a61cd7 Doxygen comments in binaryio 2021-07-01 10:42:53 -07:00
Will Pazner 8f647fa4ef Add convenience function NumBase64Chars
This function returns the number of characters required to encode a given number
of bytes in base-64.
2021-07-01 10:40:04 -07:00
Will Pazner 5da228a103 Support reading VTK XML binary compressed data with multiple blocks
Both raw binary data and base-64 encoded are supported.

We previously only supported reading a single "block" of compressed data. VTK
format supports multiple blocks for semi-random access. Each block is
compressed separately, and the blocks are concatenated.

The header format is as follows (where header_t is either uint32_t or uint64_t
according to the VTK file metadata):

    header_t number_of blocks;
    header_t uncompressed_block_size;
    header_t uncompressed_last_block_size;
    header_t[] compressed_size;

The total uncompressed_block_size is bounded by (number_of_blocks -
1)*uncompressed_block_size + uncompressed_last_block_size.

The main complication is that the header contains a variable number of integers
(depending on the number of blocks), rather than a fixed number of 4 integers
as is the case when we are restricted to one block only.
2021-07-01 10:29:02 -07:00
Ketan Mittal a20ec1f29f minor 2021-07-01 09:15:02 -07:00
Will Pazner 9066476c67 Add check for wrong VectorTensorFiniteElement constructor in 1D 2021-06-30 18:25:17 -07:00
Ketan Mittal 0c639efcd5 formatting 2021-06-30 16:24:54 -07:00
Ketan Mittal 16cca1a724 fix line wrap 2021-06-30 15:55:51 -07:00
Ketan Mittal 157e3dc151 renaming the integrators and fixing indexing of interiorfaceintegs in parlinearform 2021-06-30 15:52:23 -07:00
Tzanio Kolev 598a5994bc Merge pull request #2292 from mfem/bernede1/githooks
Githooks
2021-06-30 13:52:55 -07:00
Tzanio Kolev 6a088021e7 Merge pull request #2345 from mfem/feature/artv3/amgx-defaults
Adjust AmgX to use JACOBI_L1
2021-06-30 13:52:31 -07:00
Will Pazner 703083a43c Make ND_SegmentElement inherit from VectorTensorFiniteElement 2021-06-30 11:01:33 -07:00
Tzanio f45fbc53da minor 2021-06-29 13:59:56 -07:00
Adrien BernedeandTzanio Kolev 345abd1a5a Update CHANGELOG
Co-authored-by: Tzanio Kolev <tzanio@llnl.gov>
2021-06-29 13:36:38 -07:00
Adrien M. Bernede 39e7705da1 Add a make target to setup hooks 2021-06-29 13:35:29 -07:00
Tzanio KolevandAdrien Bernede 154e100214 Update CONTRIBUTING.md
Co-authored-by: Adrien Bernede <51493078+adrienbernede@users.noreply.github.com>
2021-06-29 10:04:11 -07:00
Tzanio KolevandAdrien Bernede 04bdfc9409 Update CONTRIBUTING.md
Co-authored-by: Adrien Bernede <51493078+adrienbernede@users.noreply.github.com>
2021-06-29 10:03:50 -07:00
Tzanio KolevandAdrien Bernede 34cf1432e9 Update CONTRIBUTING.md
Co-authored-by: Adrien Bernede <51493078+adrienbernede@users.noreply.github.com>
2021-06-29 10:03:37 -07:00
Stowell, Mark L b723fd39cc Updating applications to use the new OperatorChebyshevSmoother constructors 2021-06-29 09:50:01 -07:00
Stowell, Mark L eb863cff5f Changing operator argument in OperatorChebyshevSmoother to pass by reference 2021-06-29 09:49:11 -07:00
Tzanio e9b8ecdff7 Final updates 2021-06-29 09:45:06 -07:00
Tzanio 27c55c2280 Several updates in CONTRIBUTING.md 2021-06-29 09:19:20 -07:00
Tzanio 21f37f99d2 Condense testing bullets in CHANGELOG 2021-06-29 09:03:58 -07:00
Tzanio 63cfc40b41 Merge branch 'master' into bernede1/githooks 2021-06-29 08:53:17 -07:00
Tzanio Kolev 73286c6f4d Merge pull request #2361 from mfem/git-ignore-user-mk
Add user.mk to .gitignore [git-ignore-user-mk]
2021-06-29 08:51:09 -07:00
Tzanio Kolev b3c2f05887 Merge pull request #2339 from koomie/pview_timeseries
Update ParaViewDataCollection::Save() method to preserve metadata for previous timesteps
2021-06-29 08:44:16 -07:00
Tzanio Kolev fd846ee3e1 Merge pull request #2360 from mfem/solver-const-correctness
Solver const correctness [solver-const-correctness]
2021-06-29 08:41:23 -07:00
Adrien M. Bernede eee909351d Force rebuild cache deps 2021-06-28 20:21:56 -07:00
Adrien M. Bernede f79415ed65 Set a value for default hypre target... will be improve by merging with faster actions 2021-06-28 20:20:31 -07:00
Adrien M. Bernede 462b21fe54 Point to updated action 2021-06-28 20:06:54 -07:00
Adrien Bernede d3941dfb60 Fix typos 2021-06-28 17:23:03 -07:00
Adrien M. Bernede 2dbc9ca892 Merge branch 'master' into testing/bernede1/hypre-bigint 2021-06-28 17:09:57 -07:00
Tzanio Kolev a631e6909f Merge pull request #2351 from mfem/tmop-80-333
TMOP kernels for metrics 80 and 332.
2021-06-28 14:38:22 -07:00
Will Pazner 4b1d423210 Add user.mk to .gitignore 2021-06-25 18:23:14 -07:00
Will Pazner 37af117694 Solver const correctness 2021-06-25 18:11:42 -07:00
Veselin Dobrev c1485a49ed Merge pull request #2348 from mfem/wcdawn/hypre_v22000
HYPRE v2.22 API update
2021-06-25 12:34:50 -07:00
camierjs dcbf4f1850 Increase linear iterations for all metric 80 tests to pass 2021-06-25 12:15:06 -07:00
Tzanio Kolev 98e9c4cc37 Merge pull request #2338 from mfem/int-rule-set-point-idx
Make sure to call SetPointIndices when constructing IntegrationRules [int-rule-set-point-idx]
2021-06-25 08:01:22 -07:00
Tzanio Kolev cdb92cefd0 Merge pull request #2329 from mfem/yohann/face-restriction-base
Add an abstract interface FaceRestriction
2021-06-25 08:00:41 -07:00
Tzanio Kolev 6c1f52d805 Merge pull request #2334 from mfem/transpose-integration-rule
Allow changing the integration rule of TransposeIntegrator
2021-06-25 07:58:47 -07:00
Veselin Dobrev eb1493dd80 A few small tweaks:
Make the function (defined for hypre >= 2.22.0)
   mfem::internal::hypre_CSRMatrixAdd
inline.

Do not modify the implementation of
   mfem::internal::hypre_ParCSRMatrixAdd
for hypre >= 2.22.0.

Do not remove the functions
   mfem::internal::hypre_CSRMatrixSetConstantValues
   mfem::internal::hypre_ParCSRMatrixSetConstantValues
for hypre >= 2.22.0.
2021-06-24 15:42:03 -07:00
Ketan Mittal 0696c3d662 minor 2021-06-24 13:22:34 -07:00
Arturo Vargas a16f6a2450 fix alignment. 2021-06-24 11:46:11 -07:00
William Dawn f01988c0cc fixing version number to v 2.22 2021-06-23 13:24:11 -06:00
Vladimir Z Tomov 604cfaa65a Added kernels and unit tests for metric 332. 2021-06-23 11:39:19 -07:00
Arturo Vargas 0c1db68601 remove extra , in solver config mode. 2021-06-23 09:46:28 -07:00
William Dawn e1733d5b29 updating hypre functions to be compatible with version 2.20 2021-06-23 06:42:41 -06:00
Vladimir Z Tomov 7dfaaa6806 Added kernels and unit test for metric 80. 2021-06-22 18:16:38 -07:00
Arturo Vargas 6d3d44bca6 adjust amgx defaults based on lessons learned 2021-06-22 13:51:14 -07:00
Will Pazner 4099390b15 Fix ParaView restart mode on Windows
We were reading from an ifstream in text mode, which automatically
converts CRLF to just LF. However, seekg reports a file offset in bytes
rather than in characters, so in Windows we were overshooting.

Changing the ifstream mode from text to binary fixes this issue.
2021-06-22 13:12:40 -07:00
Will Pazner 69ac786a0c Add unit test for ParaView restart mode 2021-06-22 09:19:28 -07:00
Will Pazner bf7a36735e Modify ParaViewDataCollection restart mode
* Make more portable: don't depend on truncate from unistd
* Verify that the cycle we're trying to write won't overwrite any previous cycle
we are retaining
2021-06-22 09:19:15 -07:00
Will Pazner 72764aef89 Change SetRestartMode to UseRestartMode 2021-06-22 09:16:15 -07:00
Adrien M. Bernede 13ed072d48 Update CONTRIBUTING.md and CHANGELOG 2021-06-21 16:20:48 -07:00
Tzanio Kolev a472f4a1a3 Merge pull request #2326 from mfem/bernede1/githooks-add-on
Githooks: share checks with github actions
2021-06-21 16:00:06 -07:00
Adrien M. Bernede c9a0b7195b Fix 2021-06-21 12:27:42 -07:00
Adrien M. Bernede 5162cfb89f Typos 2021-06-21 12:22:45 -07:00
Adrien M. Bernede 64494e2b24 Fix ordering 2021-06-21 12:19:30 -07:00
Karl W. Schulz 989866a7aa switch to MFEM_ABORT for truncate check
Signed-off-by: Karl W. Schulz <karl@oden.utexas.edu>
2021-06-21 09:00:07 -05:00
Karl W. Schulz c4a744154e update ParaViewDataCollection::Save() with logic to truncate timesteps from
existing pvd file if they are greater than the currently defined time.
Presently implemented for linux only, so disabled SetRestartMode() for windows.

Signed-off-by: Karl W. Schulz <karl@oden.utexas.edu>
2021-06-21 07:56:20 -05:00
Karl W. Schulz 21d7b39e0f reformat for astyle check
Signed-off-by: Karl W. Schulz <karl@oden.utexas.edu>
2021-06-20 16:08:38 -05:00
Karl W. Schulz 9a98a2359e fix assert syntax
Signed-off-by: Karl W. Schulz <karl@oden.utexas.edu>
2021-06-20 15:28:50 -05:00
Karl W. Schulz 1f6158a726 Add a restart_mode for ParaViewDataCollection class (off by default). If
enabled, any timestep values present in a previously created pvd file while be
preserved as long as they are less than the currently defined time.

Signed-off-by: Karl W. Schulz <karl@oden.utexas.edu>
2021-06-20 15:04:46 -05:00
Will Pazner 0969273d99 Make sure SetPointIndices is called in the right places 2021-06-18 09:25:23 -07:00
Karl W. Schulz 37586c597c Update ParaViewDataCollection::Save() method to preserve metadata for
previously dumped timesteps if existing .pvd file exists in chosen output
directory.

Signed-off-by: Karl W. Schulz <karl@oden.utexas.edu>
2021-06-17 17:28:49 -05:00
Will Pazner 1486c07dad Make sure to call SetPointIndices when constructing IntegrationRules
Also makes SetPointIndices public. Every call to IntegrationRule::SetSize should
be paired with a corresponding call to SetPointIndices.
2021-06-17 15:23:51 -07:00
Yohann Dudouit d090a456cf Add API changes section 2021-06-15 12:10:52 -07:00
Will Pazner 1017da8f51 Add SetIntRule for SumIntegrator, LumpedIntegrator and InverseIntegrator 2021-06-15 11:47:43 -07:00
Will Pazner 7789aa9387 Merge remote-tracking branch 'origin/master' into transpose-integration-rule 2021-06-15 10:24:16 -07:00
Will Pazner dde5c2a230 Allow changing the integration rule of TransposeIntegrator
Changed NonlinearFormIntegrator::SetIntRule to be virtual, so derived
classes (like TransposeIntegrator) can implement custom behavior.

Changed NonlinearFormIntegrator::SetIntegrationRule to call SetIntRule.

Added TransposeIntegrator::SetIntRule which in turn calls SetIntRule on
the wrapped BilinearFormIntegrator.
2021-06-15 10:07:19 -07:00
Yohann Dudouit ec615d590b CHANGELOG 2021-06-14 15:53:03 -07:00
Yohann Dudouit 5d1b2d9261 make style. 2021-06-14 11:26:07 -07:00
Yohann Dudouit 038ee646ee Add some documentation. 2021-06-14 11:23:43 -07:00
Yohann Dudouit 5338aa8bb7 Add virtual destructor. 2021-06-11 16:18:14 -07:00
Yohann Dudouit b6eac61043 Modify test_sum_bilin 2021-06-11 16:12:45 -07:00
Yohann Dudouit c869398f60 Add a FaceRestriction interface. 2021-06-11 15:49:49 -07:00
Adrien M. Bernede ab6f3a546f Share code between git hook and github actions 2021-06-11 11:13:34 -07:00
psocratis beeabc53c6 fixing failed test caused by auto merging with master 2021-06-11 10:56:12 -07:00
psocratis f0124d2fc5 merge master 2021-06-11 10:48:55 -07:00
Adrien M. Bernede 54dc1405dc Add a comment to explain why code-style is commented out 2021-06-11 09:30:05 -07:00
Adrien M. Bernede 677aa95e48 Wrap to 80 col 2021-06-11 09:02:15 -07:00
Ketan Mittal d0eaf9760a minor 2021-06-09 14:08:40 -07:00
Ketan Mittal f2c9f8e400 removing GetNumGhost method 2021-06-09 14:06:06 -07:00
Ketan Mittal a8a00821d1 update GetCoarseToFineMap 2021-06-09 14:00:33 -07:00
Ketan Mittal 67bd515ed7 Merge branch 'master' of https://github.com/mfem/mfem into bugfix-coarse_to_fine_map_derefine 2021-06-09 13:43:04 -07:00
Adrien M. Bernede ac8784e122 move readme to markdown extension 2021-06-07 17:45:58 -07:00
Adrien M. Bernede 0d98a14863 Add README 2021-06-07 17:45:22 -07:00
Adrien M. Bernede a27ab37d62 Make hooks executables 2021-06-03 12:25:42 -07:00
Adrien M. Bernede b553390c8b Add 2 useful hooks 2021-06-03 12:25:42 -07:00
psocratis f8a7405fb8 addressing some of the review comments 2021-05-13 20:23:36 -07:00
psocratis 76d384d6be fixing unused variable warning 2021-04-28 20:22:52 -07:00
psocratis 76382289d0 Adding checks if MFEM_USE_LAPACK 2021-04-28 20:10:37 -07:00
psocratis e0d52ab9f7 make style 2021-04-28 19:29:57 -07:00
psocratis 2214130993 fixing valgrind complaint in KronMult 2021-04-28 19:22:30 -07:00
psocratis 2b760e8a0a Additional unit test for eigensystems of indefinite matrices 2021-04-28 18:48:02 -07:00
psocratis ecfd6ff848 reordering the matrices in fdsolver 2021-04-28 18:46:12 -07:00
psocratis f96929cc9e Fixing issue where the given matrix given to the eigen system is overwritten 2021-04-28 18:42:57 -07:00
psocratis dd4eb1a7b9 Corrected comment for the fdsolver description 2021-04-27 20:52:48 -07:00
psocratis b2dce094f7 Adding unit tests for fdsolver 2021-04-27 20:50:27 -07:00
psocratis e96797c7db fixed small bug in lapack eig solver 2021-04-27 20:49:38 -07:00
psocratis 157f3e1a43 Cleaning up fdsolver 2021-04-27 19:08:57 -07:00
psocratis c9c181d25b adding entry-wise mult operator *= 2021-04-27 19:08:17 -07:00
psocratis 9b5b9ddcbb adding KronMult for collection of DenseMatrices 2021-04-27 19:06:25 -07:00
psocratis b328746f4a make style 2021-04-26 20:24:54 -07:00
psocratis da6f519346 Started fdsolver 2021-04-26 20:24:21 -07:00
psocratis 806595ccc2 Adding lapack EigenSystem for general dense matrides 2021-04-26 20:19:22 -07:00
psocratis 48183748ba KronMult for DenseMatrixInverse and unit tests 2021-04-23 17:22:03 -07:00
psocratis 06ccc3cc29 adding KronMult and unit tests 2021-04-23 15:51:12 -07:00
Adrien M. Bernede 07be8038a2 Minor 2021-03-08 09:22:19 -08:00
Adrien M. Bernede 11801d19a8 Merge branch 'testing/bernede1/github-actions' into testing/bernede1/hypre-bigint 2021-03-02 15:23:37 -08:00
Adrien M. Bernede a7f4a57370 Fix action input name 2021-03-02 15:14:24 -08:00
Adrien M. Bernede 323000faee Allow empty values in conditionals 2021-03-02 13:48:56 -08:00
Adrien M. Bernede bb78d8514f Define HYPRE_BIGINT in MFEM when building hypre with big int 2021-03-02 11:12:18 -08:00
Adrien M. Bernede fb596116ab Merge branch 'testing/bernede1/github-actions' into testing/bernede1/hypre-bigint 2021-03-02 10:59:02 -08:00
Ketan Mittal 1cf11888b2 getnumghostelements in ncmesh 2021-03-01 12:10:33 -08:00
Ketan Mittal 7a2bae81d9 make style 2021-03-01 11:59:56 -08:00
Ketan Mittal 63889ef70f fix embedding size for refinement transform 2021-03-01 11:54:16 -08:00
Ketan Mittal 2ba5aeaa93 coarse to fine map bug fix for derefinement transform 2021-03-01 11:35:00 -08:00
Adrien M. Bernede f581eec582 Fix: incomplete matrix definitions 2021-02-26 09:28:00 -08:00
Adrien M. Bernede defc0b741a Fix: merge jobs for shared environment variables 2021-02-25 23:57:48 -08:00
Adrien M. Bernede 330764a2e7 Merge branch 'testing/bernede1/github-actions' into testing/bernede1/hypre-bigint 2021-02-25 23:14:44 -08:00
Adrien M. Bernede 3e4ae315e7 Fix bash conditionnal 2021-02-25 15:59:49 -08:00
Adrien M. Bernede 769e9eeba5 Add missing target in install-hypre action invocation 2021-02-25 15:57:52 -08:00
Adrien M. Bernede b0d7d3e909 Coherency and typo 2021-02-25 15:43:01 -08:00
Adrien M. Bernede 3e6bcc91f8 Fix missing shell type in composite action 2021-02-25 15:21:49 -08:00
Adrien M. Bernede 3450a067d3 Add a test config with hypre bigint 2021-02-25 15:15:20 -08:00
99 changed files with 4872 additions and 1204 deletions
+42 -31
View File
@@ -47,26 +47,36 @@ jobs:
strategy:
matrix:
os: [ubuntu-18.04, macos-10.15]
target: [debug, optim]
mpi: [sequential, parallel]
target: [dbg, opt]
mpi: [seq, par]
build-system: [make]
# 'include' allows us to
# - add a variable without creating a new matrix dimension.
# - add a new combination ('build-system: cmake' case here)
hypre-target: [int32]
# 'include' allows us to:
# - Add a variable to all jobs without creating a new matrix dimension.
# Codecov is defined that way.
# - Add a new combination.
# 'build-system: cmake' and 'hypre-target: int64'
#
# note: we will gather coverage info for any non-debug run except the
# CMake build.
include:
- target: debug
- target: dbg
codecov: NO
- target: optim
- target: opt
codecov: YES
- os: ubuntu-18.04
target: optim
target: opt
codecov: NO
mpi: parallel
mpi: par
build-system: cmake
name: ${{ matrix.os }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.build-system }}
hypre-target: int32
- os: ubuntu-18.04
target: opt
codecov: NO
mpi: par
build-system: make
hypre-target: int64
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
runs-on: ${{ matrix.os }}
@@ -92,7 +102,7 @@ jobs:
# TODO: It would be nice to have only one step, e.g. with a dedicated
# action, but I (@adrienbernede) don't see how at the moment.
- name: get MPI (Linux)
if: matrix.mpi == 'parallel' && matrix.os == 'ubuntu-18.04'
if: matrix.mpi == 'par' && matrix.os == 'ubuntu-18.04'
run: |
sudo apt-get install mpich libmpich-dev
export MAKE_CXX_FLAG="MPICXX=mpic++"
@@ -103,11 +113,11 @@ jobs:
sudo apt-get install lcov
- name: Set up Homebrew
if: ( matrix.mpi == 'parallel' || matrix.codecov == 'YES' ) && matrix.os == 'macos-10.15'
if: ( matrix.mpi == 'par' || matrix.codecov == 'YES' ) && matrix.os == 'macos-10.15'
uses: Homebrew/actions/setup-homebrew@c4aafe8c4620bf08883dd4679c374f11e73329d3
- name: get MPI (MacOS)
if: matrix.mpi == 'parallel' && matrix.os == 'macos-10.15'
if: matrix.mpi == 'par' && matrix.os == 'macos-10.15'
run: |
export HOMEBREW_NO_INSTALL_CLEANUP=1
brew install openmpi
@@ -123,39 +133,40 @@ jobs:
# Install will only run on cache miss.
- name: cache hypre
id: hypre-cache
if: matrix.mpi == 'parallel'
if: matrix.mpi == 'par'
uses: actions/cache@v2
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-v2.0
- name: get hypre
if: matrix.mpi == 'parallel' && steps.hypre-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@v1.0
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@v2.0
with:
hypre-archive: ${{ env.HYPRE_ARCHIVE }}
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
target: ${{ matrix.hypre-target }}
# Get Metis through cache, or build it.
# Install will only run on cache miss.
- name: cache metis
id: metis-cache
if: matrix.mpi == 'parallel'
if: matrix.mpi == 'par'
uses: actions/cache@v2
with:
path: ${{ env.METIS_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.0
- name: install metis
if: matrix.mpi == 'parallel' && steps.metis-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v1.0
if: matrix.mpi == 'par' && steps.metis-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.0
with:
metis-archive: ${{ env.METIS_ARCHIVE }}
metis-dir: ${{ env.METIS_TOP_DIR }}
archive: ${{ env.METIS_ARCHIVE }}
dir: ${{ env.METIS_TOP_DIR }}
# MFEM build and test
- name: build
uses: mfem/github-actions/build-mfem@v1.0
uses: mfem/github-actions/build-mfem@v2.0
with:
os: ${{ matrix.os }}
target: ${{ matrix.target }}
@@ -168,17 +179,17 @@ jobs:
# Run checks (and only checks) on debug targets
- name: checks
if: matrix.build-system == 'make' && matrix.target == 'debug'
if: matrix.build-system == 'make' && matrix.target == 'dbg'
run: |
cd ${{ env.MFEM_TOP_DIR }} && make check
- name: unit tests
if: matrix.build-system == 'make' && matrix.target == 'optim'
if: matrix.build-system == 'make' && matrix.target == 'opt'
run: |
cd ${{ env.MFEM_TOP_DIR }} && make unittest
- name: tests
if: matrix.build-system == 'make' && matrix.target == 'optim'
if: matrix.build-system == 'make' && matrix.target == 'opt'
run: |
cd ${{ env.MFEM_TOP_DIR }} && make test
@@ -190,8 +201,8 @@ jobs:
# Code coverage (process and upload reports)
- name: codecov
if: matrix.codecov == 'YES'
uses: mfem/github-actions/upload-coverage@v1.0
uses: mfem/github-actions/upload-coverage@v2.0
with:
name: ${{ matrix.os }}-${{ matrix.mpi }}
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
project_dir: ${{ env.MFEM_TOP_DIR }}
directories: "fem general linalg mesh"
+10 -9
View File
@@ -53,32 +53,33 @@ jobs:
uses: actions/cache@v2
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2.0
- name: Get Hypre
if: steps.hypre-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@master
uses: mfem/github-actions/build-hypre@v2.0
with:
hypre-archive: ${{ env.HYPRE_ARCHIVE }}
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
target: int32
- name: Cache Metis Install
id: metis-cache
uses: actions/cache@v2
with:
path: ${{ env.METIS_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.0
- name: Install Metis
if: steps.metis-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@master
uses: mfem/github-actions/build-metis@v2.0
with:
metis-archive: ${{ env.METIS_ARCHIVE }}
metis-dir: ${{ env.METIS_TOP_DIR }}
archive: ${{ env.METIS_ARCHIVE }}
dir: ${{ env.METIS_TOP_DIR }}
# MFEM build and test
- name: build-mfem
uses: mfem/github-actions/build-mfem@master
uses: mfem/github-actions/build-mfem@v2.0
with:
os: ${{ runner.os }}
target: optim
+6 -33
View File
@@ -28,49 +28,24 @@ jobs:
access_token: ${{ github.token }}
- name: checkout mfem
uses: actions/checkout@v2
with:
path: mfem
- name: copyright check
id: copyright
run: |
cd mfem
if git grep -l "^\(#\|//\).*\(\-2020\|\ 2010,\)" > matches.txt
then
echo "Please update the following files to Copyright (c) 2010-2021:"
cat matches.txt
exit 1
else
echo "No outdated copyright found."
fi
./config/githooks/pre-push --copyright
continue-on-error: true
- name: license check
id: license
run: |
cd mfem
if git grep -li "^\(#\|//\).*GNU\ Lesser\ General\ Public\ License" > matches.txt
then
echo "Please update the following files to the BSD-3 license:"
cat matches.txt
exit 1
else
echo "No GNU GPL license found."
fi
./config/githooks/pre-push --license
continue-on-error: true
- name: release check
id: release
run: |
cd mfem
if git grep -l "^\(#\|//\).*LLNL\-CODE\-443211" > matches.txt
then
echo "Please update the following files to LLNL-CODE-806117:"
cat matches.txt
exit 1
else
echo "No outdated release number found."
fi
./config/githooks/pre-push --release
continue-on-error: true
- name: wrap-up
@@ -100,8 +75,7 @@ jobs:
- name: style check
run: |
cd tests/scripts
./runtest code-style
./config/githooks/pre-push --style
documentation:
runs-on: ubuntu-18.04
@@ -133,5 +107,4 @@ jobs:
run: |
git fetch origin master:master
git checkout -b gh-actions-branch-history
cd tests/scripts
./runtest branch-history
./config/githooks/pre-push --history
+1
View File
@@ -26,6 +26,7 @@ CMakeFiles/
config/_config.hpp
config/config.mk
config/sample-runs-build.log
config/user.mk
doc/CodeDocumentation.conf
doc/CodeDocumentation.html
doc/CodeDocumentation
+27 -8
View File
@@ -48,39 +48,58 @@ variables:
TPLS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tpls.git
TESTS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tests.git
AUTOTEST_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/autotest.git
MFEM_DATA_REPO: https://github.com/mfem/data.git
ARTIFACTS_DIR: artifacts
# The pipeline is divided into stages. Usually, these are also synchronization
# points, however, we use "needs" keyword to express the DAG of jobs for more
# efficiency.
# - We use setup phase to download content outside of mfem directory.
# - We use setup and setup_baseline phases to download content outside of mfem
# directory.
# - Allocate/Release is where quartz resources are allocated/released once for all.
# - Build and Test is where we build and MFEM for multiple toolchains.
# - Baseline_checks gathers baseline-type test suites execution
# - Baseline_publish, only available on master, allows to update baseline
# results
stages:
- setup
- q_allocate_resources
- q_build_and_test
- q_release_resources
- l_build_and_test
- c_build_and_test
- setup
- setup_baseline
- baseline_check
- baseline_to_autotest
- baseline_publish
# The setup job in setup stage don't rely on MFEM git repo. It prepares a
# pipeline-wide working directory downloading/updating external repos.
# TODO: updating tests and tpls is not necessary anymore since pipelines are
# now using unique directories so repo are never shared with another pipeline.
# This is not memory efficient (we keep a lot of data), hence this reminder.
# Setup
# setup clones the mfem/data repo in ${BUILD_ROOT}. The build_and_test script
# then symlinks the repo to the parent directory of the MFEM source directory.
# Unit tests that depend on the mfem/data repo will then detect that this
# directory is present and be enabled.
setup:
tags:
- shell
- quartz
stage: setup
variables:
GIT_STRATEGY: none
script:
- mkdir -p ${BUILD_ROOT} && cd ${BUILD_ROOT}
- if [ ! -d data ]; then git clone ${MFEM_DATA_REPO}; fi
needs: []
# The setup_baseline job in setup stage_baseline doesn't rely on MFEM git repo.
# It prepares a pipeline-wide working directory downloading/updating external
# repos. TODO: updating tests and tpls is not necessary anymore since pipelines
# are now using unique directories so repo are never shared with another
# pipeline. This is not memory efficient (we keep a lot of data), hence this
# reminder.
setup_baseline:
tags:
- shell
- quartz
stage: setup_baseline
variables:
GIT_STRATEGY: none
script:
+1 -1
View File
@@ -25,7 +25,7 @@
.build_and_test_on_lassen:
extends: [.build_blueos_3_ppc64le_ib_script, .on_lassen]
stage: l_build_and_test
needs: []
needs: [setup]
opt_mpi_cuda_xl_16_1_1_8:
variables:
+2 -1
View File
@@ -94,6 +94,7 @@ q_report_failure:
.build_and_test_on_quartz:
extends: [.build_toss_3_x86_64_ib_script, .on_quartz]
stage: q_build_and_test
needs: [setup]
# Build MFEM
debug_ser_gcc_4_9_3:
@@ -139,7 +140,7 @@ opt_par_gcc_6_1_0_pumi:
# Baseline
baselinecheck_mfem_intel_quartz:
extends: [.baselinecheck_mfem, .on_quartz]
needs: [setup]
needs: [setup_baseline]
update_autotest:
extends: [.on_quartz]
-469
View File
@@ -1,469 +0,0 @@
# Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
language: cpp
os: linux
dist: bionic
stages:
- checks
- tests
- optional
env:
global:
- HYPRE_ARCHIVE=v2.19.0.tar.gz
HYPRE_URL=https://github.com/hypre-space/hypre/archive/$HYPRE_ARCHIVE
HYPRE_TOP_DIR=hypre-2.19.0
jobs:
include:
# ========================
# Checks
# ========================
# - code-style
# - documentation
# - gitignore
- stage: checks
os: linux
dist: xenial
name: "code-style"
addons:
apt:
packages:
- astyle=2.05.1-0ubuntu1
script:
- cd ${TRAVIS_BUILD_DIR}
- cd tests/scripts
- ./runtest code-style
- stage: checks
os: linux
name: "documentation"
addons:
apt:
packages:
- doxygen
- graphviz
script:
- cd ${TRAVIS_BUILD_DIR}
- cd tests/scripts
- ./runtest documentation
- stage: checks
os: linux
name: "gitignore"
addons:
apt:
packages:
- mpich
- libmpich-dev
env: MPI=YES
before_script:
- cd ${TRAVIS_BUILD_DIR}
- mpicxx -v
- make config MFEM_USE_MPI=YES MFEM_MPI_NP=2
- make all -j3
- make test-noclean
script:
- cd tests/scripts
- ./runtest gitignore
cache:
ccache: true
directories:
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
- $TRAVIS_BUILD_DIR/../metis-4.0
before_cache:
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
mv libmetis.a Lib ..; rm -rf * ; mv ../libmetis.a ../Lib .;
rm -f Lib/*.{c,o}
# ========================
# Optional Checks/Tests
# ========================
# - branch-history
- stage: optional
name: "branch-history"
if: branch != next
# need full git history for the binary/big files check
git:
depth: false
script:
- cd ${TRAVIS_BUILD_DIR}
# update master
- git fetch origin master:master
# checkout a branch (otherwise Travis works in detached head)
- git checkout -b travis_tests
- cd tests/scripts
- ./runtest branch-history
# ========================
# Linux tests
# ========================
# - serial + debug
# - serial
# - parallel + debug
# - parallel
- stage: tests
os: linux
compiler: gcc
name: "Linux: Serial + Debug"
env: DEBUG=YES
MPI=NO
CODECOV=NO
MFEM_TEST_TARGET=check
cache:
ccache: true
- os: linux
compiler: gcc
name: "Linux: Serial"
env: DEBUG=NO
MPI=NO
CODECOV=NO
MFEM_TEST_TARGET=test
cache:
ccache: true
- os: linux
compiler: gcc
name: "Linux: Parallel + Debug"
addons:
apt:
# sources:
# - ubuntu-toolchain-r-test
packages:
# GCC 4.9
# - g++-4.9
# MPICH
- mpich
- libmpich-dev
# OpenMPI
# - openmpi-bin
# - libopenmpi-dev
env: DEBUG=YES
MPI=YES
CODECOV=NO
MFEM_TEST_TARGET=check
NPROCS=2
cache:
ccache: true
directories:
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
- $TRAVIS_BUILD_DIR/../metis-4.0
before_cache:
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
mv libmetis.a Lib ..; rm -rf * ; mv ../libmetis.a ../Lib .;
rm -f Lib/*.{c,o}
- os: linux
compiler: gcc
name: "Linux: Parallel"
addons:
apt:
# sources:
# - ubuntu-toolchain-r-test
packages:
# GCC 4.9
# - g++-4.9
# MPICH
- mpich
- libmpich-dev
# OpenMPI
# - openmpi-bin
# - libopenmpi-dev
env: DEBUG=NO
MPI=YES
CODECOV=YES
MFEM_TEST_TARGET=test
NPROCS=2
cache:
ccache: true
directories:
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
- $TRAVIS_BUILD_DIR/../metis-4.0
before_cache:
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
mv libmetis.a Lib ..; rm -rf * ; mv ../libmetis.a ../Lib .;
rm -f Lib/*.{c,o}
- os: linux
compiler: gcc
name: "Linux: Parallel (cmake)"
addons:
apt:
packages:
- mpich
- libmpich-dev
env: MPI=YES
NPROCS=2
script:
- cd ${TRAVIS_BUILD_DIR}
- mkdir ${TRAVIS_BUILD_DIR}/build
- cd ${TRAVIS_BUILD_DIR}/build
- cmake ..
-DMFEM_USE_MPI=ON
-DHYPRE_DIR=${TRAVIS_BUILD_DIR}/../$HYPRE_TOP_DIR/src/hypre
-DMFEM_MPI_NP=$NPROCS
- make -j3 mfem examples
- cd ${TRAVIS_BUILD_DIR}/build/tests/unit
- make -j3
- ctest --output-on-failure
cache:
ccache: true
directories:
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
- $TRAVIS_BUILD_DIR/../metis-4.0
before_cache:
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
mv libmetis.a Lib ..; rm -rf * ; mv ../libmetis.a ../Lib .;
rm -f Lib/*.{c,o}
# ========================
# Mac OS X tests
# ========================
# - serial + debug
# - serial
# - parallel + debug
# - parallel
- os: osx
osx_image: xcode11.2
compiler: clang
name: "Mac: Serial + Debug"
addons:
homebrew:
packages:
- ccache
env: DEBUG=YES
MPI=NO
CODECOV=NO
MFEM_TEST_TARGET=check
cache:
ccache: true
- os: osx
osx_image: xcode11.2
compiler: clang
name: "Mac: Serial"
addons:
homebrew:
packages:
- ccache
env: DEBUG=NO
MPI=NO
CODECOV=NO
MFEM_TEST_TARGET=test
cache:
ccache: true
- os: osx
osx_image: xcode11.2
compiler: clang
name: "Mac: Parallel + Debug"
addons:
homebrew:
packages:
- ccache
env: DEBUG=YES
MPI=YES
CODECOV=NO
MFEM_TEST_TARGET=check
NPROCS=4
TMPDIR=/tmp
cache:
ccache: true
directories:
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
- $TRAVIS_BUILD_DIR/../metis-4.0
- $HOME/local-cached
before_cache:
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
mv libmetis.a Lib ..; rm -rf * ; mv ../libmetis.a ../Lib .;
rm -f Lib/*.{c,o}
- os: osx
osx_image: xcode11.2
compiler: clang
name: "Mac: Parallel"
addons:
homebrew:
packages:
- ccache
env: DEBUG=NO
MPI=YES
CODECOV=YES
MFEM_TEST_TARGET=test
NPROCS=4
TMPDIR=/tmp
cache:
ccache: true
directories:
- $TRAVIS_BUILD_DIR/../$HYPRE_TOP_DIR/src/hypre
- $TRAVIS_BUILD_DIR/../metis-4.0
- $HOME/local-cached
before_cache:
- cd $TRAVIS_BUILD_DIR/../metis-4.0;
mv libmetis.a Lib ..; rm -rf * ; mv ../libmetis.a ../Lib .;
rm -f Lib/*.{c,o}
before_install:
# No addon for brew yet, have to install OSX packages this way.
# - if [ $TRAVIS_OS_NAME == "osx" ] && [ $MPI == "YES" ]; then
# brew install open-mpi;
# fi
# Disable ccache while building dependencies that are cached:
- echo "before \$PATH = $PATH";
export PATH=${PATH//\/usr\/lib\/ccache:/};
echo "after \$PATH = $PATH"
# On Mac OS X, build and cache OpenMPI 2.1.6:
- if [ $TRAVIS_OS_NAME == "osx" ] && [ $MPI == "YES" ]; then
if [ ! -e $HOME/local-cached/bin/mpicc ]; then
mkdir -p $HOME/builds && cd $HOME/builds &&
wget https://download.open-mpi.org/release/open-mpi/v2.1/openmpi-2.1.6.tar.bz2 &&
tar jxf openmpi-2.1.6.tar.bz2 &&
mkdir openmpi-build && cd openmpi-build &&
../openmpi-2.1.6/configure --prefix=$HOME/local-cached &&
make -j3 all && make install;
fi;
PATH=$HOME/local-cached/bin:$PATH;
cd $TRAVIS_BUILD_DIR;
fi
# Update environment to find g++ 4.9 installation first.
# - if [ $TRAVIS_OS_NAME == "linux" ]; then
# mkdir -p latest-gcc-symlinks;
# ln -s /usr/bin/g++-4.9 latest-gcc-symlinks/g++;
# ln -s /usr/bin/gcc-4.9 latest-gcc-symlinks/gcc;
# ln -s /usr/bin/gcov-4.9 latest-gcc-symlinks/gcov;
# export PATH=$PWD/latest-gcc-symlinks:$PATH;
# fi
# Install tool to upload code coverage reports to coveralls.io
- if [ "$CODECOV" == "YES" ]; then
export PYTHONUSERBASE=$HOME/local;
pip install --user cpp-coveralls;
pip install --user pyyaml;
PATH=$HOME/local/bin:$PATH;
fi
install:
# Set MPI compilers, print compiler version
- if [ $MPI == "YES" ]; then
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
export MPICH_CC="$CC";
export MPICH_CXX="$CXX";
else
export OMPI_CC="$CC";
export OMPI_CXX="$CXX";
mpic++ --showme:version;
fi;
mpic++ -v;
else
$CXX -v;
fi
# Back out of the mfem directory to install the libraries
- cd ..
# hypre
- if [ $MPI == "YES" ]; then
if [ ! -e $HYPRE_TOP_DIR/src/hypre/lib/libHYPRE.a ]; then
wget $HYPRE_URL;
rm -rf $HYPRE_TOP_DIR;
tar xvzf $HYPRE_ARCHIVE;
cd $HYPRE_TOP_DIR/src;
./configure --disable-fortran CC=mpicc CXX=mpic++;
make -j3;
cd ../..;
else
echo "Reusing cached $HYPRE_TOP_DIR/";
fi;
ln -s $HYPRE_TOP_DIR hypre;
else
echo "Serial build, not using hypre";
fi
# METIS, use a mirror because the original source server is not always up.
# Original url:
# http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/OLD/metis-4.0.3.tar.gz
- if [ $MPI == "YES" ]; then
if [ ! -e metis-4.0/libmetis.a ]; then
wget https://mfem.github.io/tpls/metis-4.0.3.tar.gz;
tar xvzf metis-4.0.3.tar.gz;
make -j3 -C metis-4.0.3/Lib CC="$CC" OPTFLAGS="-O2";
rm -rf metis-4.0;
mv metis-4.0.3 metis-4.0;
else
echo "Reusing cached metis-4.0/";
fi;
fi
# Re-enable ccache on linux; enable ccache on mac os:
- if [ $TRAVIS_OS_NAME == "linux" ]; then
export PATH="/usr/lib/ccache:$PATH";
else
if [ $TRAVIS_OS_NAME == "osx" ]; then
export PATH="/usr/local/opt/ccache/libexec:$PATH";
fi;
fi
- printf "which \$CC = "; which $CC;
printf "which \$CXX = "; which $CXX
script:
# Compiler
- if [ $MPI == "YES" ]; then
export MYCXX=mpic++;
export MAKE_CXX_FLAG=MPICXX=$MYCXX;
else
export MYCXX="$CXX";
export MAKE_CXX_FLAG=CXX=$MYCXX;
fi
# Print the compiler version
- $MYCXX -v
# Set some variables
- cd $TRAVIS_BUILD_DIR;
CPPFLAGS="";
SKIP_TEST_DIRS="";
if [ "$CODECOV" == "YES" ]; then
CPPFLAGS="--coverage -g";
fi;
if [ "$TRAVIS_OS_NAME" != "linux" ] || [ "$DEBUG" == "YES" ]; then
CPPFLAGS+=" -pedantic -Wall -Werror";
fi
# Configure the library
- make config MFEM_USE_MPI=$MPI MFEM_DEBUG=$DEBUG $MAKE_CXX_FLAG
MFEM_MPI_NP=$NPROCS CPPFLAGS="$CPPFLAGS"
# Show the configuration
- make info
# Build the library
- make -j3
# Build the examples and the miniapps
- make -j3 all
# Run tests
- make $MFEM_TEST_TARGET SKIP_TEST_DIRS="$SKIP_TEST_DIRS"
after_success:
- if [ "$CODECOV" == "YES" ]; then
coveralls --include fem --include general --include linalg --include
mesh --exclude /usr --gcov-options '\-lp' --root $TRAVIS_BUILD_DIR;
fi
+14 -1
View File
@@ -173,7 +173,11 @@ Version 4.2.1 (development)
mixed meshes. The LOR Transfer miniapp (miniapps/tools/lor-transfer.cpp) now
supports meshes with any element geometry.
- Gitlab CI: use Spack (and Uberenv) to automate the build of TPLs.
- Testing improvements:
* Transitioned from Travis to GitHub Action for testing/CI on GitHub.
* Effectively remove Travis from CI.
* Use Spack (and Uberenv) to automate TPL building in LLNL GitLab tests.
* Added a set of suggested git hooks for developers in config/githooks.
- Added new miniapps demonstrating: 1) the use of GSLIB for overlapping grids,
see gslib/schwarz_ex1, and 2) coupling different physics in different domains,
@@ -231,6 +235,15 @@ Version 4.2.1 (development)
- Added makefile rule to generate TAGS table for vi or Emacs users.
API changes
-----------
- Added an abstract interface `mfem::FaceRestriction` for `H1FaceRestriction`
and `L2FaceRestriction`.
In order to conform with the semantic of `MultTranspose` in `mfem::Operator`,
`mfem::FaceRestriction::MultTranspose` now sets instead of adding values, and
`mfem::FaceRestriction::AddMultTranspose` should replace previous calls to
`mfem::FaceRestriction::MultTranspose`.
libCEED integration improvements
--------------------------------
- Refactor the libCEED integration
+34 -17
View File
@@ -4,7 +4,9 @@
<p align="center">
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-brightgreen.svg"></a>
<a href="https://travis-ci.org/mfem/mfem"><img alt="Build Status" src="https://travis-ci.org/mfem/mfem.svg?branch=master"></a>
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Arepo-check+branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuild-analysis+branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuilds-and-tests+branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
<a href="https://ci.appveyor.com/project/mfem/mfem"><img alt="Build Status" src="https://ci.appveyor.com/api/projects/status/19non9sqm6msi2wy?svg=true"></a>
<a href="https://mfem.github.io/doxygen/html/index.html"><img alt="Doxygen" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
</p>
@@ -63,6 +65,8 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
development branches off `mfem:master`.
- Please follow the [developer guidelines](#developer-guidelines), in particular
with regards to documentation and code styling.
- Please do not commit large/binary files to the central repository (use a fork
instead).
- Pull requests should be issued toward `mfem:master`. Make sure
to check the items off the [Pull Request Checklist](#pull-request-checklist).
- When your contribution is fully working and ready to be reviewed, add
@@ -71,6 +75,7 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
reviewers to evaluate the changes.
- The reviewers have 3 weeks to evaluate the PR and work with the author to
fix issues and implement improvements.
- During review there should be no force pushes/rewriting history in the branch.
- After approval, MFEM developers merge the PR manually in the [mfem:next branch](#masternext-workflow).
- After a week of testing in `mfem:next`, the original PR is merged in `mfem:master`.
- We use [milestones](https://github.com/mfem/mfem/milestones) to coordinate the
@@ -91,8 +96,9 @@ The MFEM source code has the following structure:
```
.
├── config
── cmake
│ └── ...
── cmake
└── ...
│ └── githooks
├── data
├── doc
├── examples
@@ -363,6 +369,10 @@ Before you can start, you need a GitHub account, here are a few suggestions:
two reviewers to evaluate the changes. The reviewers have 3 weeks to evaluate
the PR and work with the author to implement improvements and fix issues.
- Once the `ready-for-review` label has been applied and reviewers have been
assigned, the PR is considered under review. To help with the review process
there should be no force pushes/rewriting history in the branch.
- After approval, the PR is [tested](#masternext-workflow) for a week with
other approved PRs in the `mfem:next` branch.
@@ -370,16 +380,20 @@ Before you can start, you need a GitHub account, here are a few suggestions:
`mfem:next`, see the [README](tests/scripts/README) file in that directory
for more details.
- Track the Travis CI, Github Actions and Appveyor [continuous integration](#automated-testing)
- Track the GitHub Actions and Appveyor [continuous integration](#automated-testing)
builds at the end of the PR. These should generally run clean, so address any
errors as soon as possible. Please ask if you are unsure how to do that.
- Note that some tests, such as the `branch-history` check in Travis and Github
Actions are safeguards that are allowed to fail in certain cases.
- Note that some tests, such as the `branch-history` check in GitHub Actions
are safeguards that are allowed to fail in certain cases.
- Other tests, such as the `code-style`, `documentation` and `gitignore`
checks in Travis and Github Actions enforce MFEM-specific rules which are
explained in the error messages and the `tests/scripts` directory.
checks in GitHub Actions enforce MFEM-specific rules which are explained in
the error messages and the `tests/scripts` directory.
- Also note that the tests `branch-history` and `repos-checks` found in GitHub
Actions can be triggered automatically before each push using git hooks. See
the [git hooks README](config/githooks/README.md) for a detailed explanation.
- If triggered, track the status of the LLNL GitLab tests. If failing, ask
one of the _LLNL developers_ for details.
@@ -399,7 +413,7 @@ Before a PR can be merged, it should satisfy the following:
- [ ] Does `make` or `cmake` have a new target?
- [ ] Did the requirements or the installation process change? *(rare)*
- [ ] Update continuous integration server configurations if necessary (e.g. with new version requirements for each of MFEM's dependencies)
- [ ] `.travis.yml`
- [ ] `.github`
- [ ] `.appveyor.yml`
- [ ] Update `.gitignore`:
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
@@ -516,7 +530,7 @@ MFEM uses a `master`/`next`-branch workflow as described below:
- [ ] `doc/CodeDocumentation.conf.in`
- [ ] Check that version requirements for each of MFEM's dependencies are documented in `INSTALL` and up-to-date
- [ ] Check that continuous integration server configurations reflect the dependency version requirements of the new release
- [ ] `.travis.yml`
- [ ] `.github`
- [ ] `.appveyor.yml`
- [ ] Update the `CHANGELOG` to organize all release contributions
- [ ] Review the whole source code once over
@@ -578,14 +592,17 @@ MFEM uses a `master`/`next`-branch workflow as described below:
MFEM has several levels of automated testing running on GitHub, as well as on
local Mac and Linux workstations, and Livermore Computing clusters at LLNL.
### Linux and Mac smoke tests
We use Travis CI and Github Actions to drive the default tests on the `master`
and `next` branches. See the `.travis` file and the logs at
[https://travis-ci.org/mfem/mfem](https://travis-ci.org/mfem/mfem).
In addition, developers can set local git hooks to run some quick checks on
commit or push, see the [README](config/githooks/README.md) in the `config/githooks`
directory.
Testing using Travis CI and Github Actions should be kept lightweight, as there
is a time constraint on jobs. Two virtual machines are configured - Mac (OS X)
and Linux.
### Linux and Mac smoke tests
We use GitHub Actions to drive the default tests on the `master` and `next`
branches. See the `.github/workflows` files and the logs at
[https://github.com/mfem/mfem/actions](https://github.com/mfem/mfem/actions).
Testing using GitHub Actions should be kept lightweight, as there is a time
constraint on jobs. Two virtual machines are configured - Mac (OS X) and Linux.
- Tests on the `master` branch are triggered whenever a PR is issued on this branch.
- Tests on the `next` branch are currently scheduled to run each night.
+41
View File
@@ -0,0 +1,41 @@
Finite Element Discretization Library
__
_ __ ___ / _| ___ _ __ ___
| '_ ` _ \ | |_ / _ \| '_ ` _ \
| | | | | || _|| __/| | | | | |
|_| |_| |_||_| \___||_| |_| |_|
https://mfem.org
This directory contains recommended git hooks, which are scripts that can be
used to improve your development experience with MFEM:
### The hooks
* `pre-commit` is a hook that will be applied before each commit and run
`astyle` on the code. This will ensure that your changes comply with the MFEM
code styling guidelines.
* `pre-push` is a hook that will be applied before each push to run a quick set
of tests that verify that your files headers are in compliance, and that you did
not add any large files to the repo.
### Setup
To setup the git hooks, run `make hooks`, which creates symlinks to the hooks in
the `.git/hooks` directory. Individual hooks can be enabled by manually creating
symlinks.
(You may also copy the scripts directly and customize them further, but this way
you may miss additional updates in the future.)
### Failures
The `branch-history` check can fail in some cases when the history is OK. For
example, when a large number of files were modified for a legitimate reason, or
when a picture was added for documentation.
If that is the case, make sure the failure is indeed justified, and rerun the
push command with the `--no-verify` option. This will skip the hooks, allowing
you to push those changes.
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
# Apply automated code formatting
make -C $(git rev-parse --show-toplevel) style
+107
View File
@@ -0,0 +1,107 @@
#!/bin/bash
# Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
option=${1:-""}
if [[ "${option}" == "--help" ]]; then
echo "This script runs checks on the repository."
echo "It has 2 modes: with and without an option."
echo ""
echo "Options are used in GitHub Actions and can be:"
echo " --copyright"
echo " --license"
echo " --release"
echo " --style"
echo " --history"
echo ""
echo "As a githook, the script is used without options."
echo "In that case, it will run all the checks except style."
echo ""
echo "Use --help to print this help message."
fi
cd $(git rev-parse --show-toplevel)
# copyright check
copyright=true
if [[ "${option}" == "--copyright" || "${option}" == "" ]]; then
if git grep -l "^\(#\|//\).*\(\-2020\|\ 2010,\)" > matches.txt; then
echo "Please update the following files to Copyright (c) 2010-2021:"
cat matches.txt
copyright=false
fi
fi
# license check
license=true
if [[ "${option}" == "--license" || "${option}" == "" ]]; then
if git grep -li "^\(#\|//\).*GNU\ Lesser\ General\ Public\ License" > matches.txt; then
echo "Please update the following files to the BSD-3 license:"
cat matches.txt
license=false
fi
fi
# release check
release=true
if [[ "${option}" == "--release" || "${option}" == "" ]]; then
if git grep -l "^\(#\|//\).*LLNL\-CODE\-443211" > matches.txt
then
echo "Please update the following files to LLNL-CODE-806117:"
cat matches.txt
release=false
fi
fi
# wrap-up
code=0
if ! $copyright ; then
echo "copyright check failed, unroll log for details"
code=1
fi
if ! $license ; then
echo "license check failed, unroll log for details"
code=1
fi
if ! $release ; then
echo "release check failed, unroll log for details"
code=1
fi
# `code-style` is not just a check, it will actually reformat the code if
# necessary. This means that if one pushes while the repo is in dirty state
# (changes not staged), those changes may be mixed with format changes.
# To activate this, you will need to hard-copy this hook script in the hook
# directory and uncomment only then. (See README.md)
#
## style check
#if [[ "${option}" == "--style" || "${option}" == "" ]]; then
if [[ "${option}" == "--style" ]]; then
if which astyle && [[ "$(astyle --version)" == "Artistic Style Version 2.05.1" ]]; then
cd tests/scripts
if ! ./runtest code-style; then code=1; fi
cd -
else
echo "Warning: astyle not found or version is not 2.05.1"
fi
fi
# branch-history
if [[ "${option}" == "--history" || "${option}" == "" ]]; then
git fetch origin master:master
cd tests/scripts
if ! ./runtest branch-history; then code=1; fi
cd -
fi
exit $code
+1 -1
View File
@@ -105,7 +105,7 @@ private:
Vector diag(fespace.GetTrueVSize());
bfs.Last()->AssembleDiagonal(diag);
Solver* smoother = new OperatorChebyshevSmoother(opr.Ptr(), diag,
Solver* smoother = new OperatorChebyshevSmoother(*opr, diag,
*essentialTrueDofs.Last(), 2);
AddLevel(opr.Ptr(), smoother, true, true);
}
+1 -1
View File
@@ -115,7 +115,7 @@ private:
Vector diag(fespace.GetTrueVSize());
bfs.Last()->AssembleDiagonal(diag);
Solver* smoother = new OperatorChebyshevSmoother(opr.Ptr(), diag,
Solver* smoother = new OperatorChebyshevSmoother(*opr, diag,
*essentialTrueDofs.Last(), 2, fespace.GetParMesh()->GetComm());
AddLevel(opr.Ptr(), smoother, true, true);
+353
View File
@@ -0,0 +1,353 @@
#include "element-smoother.hpp"
using namespace std;
using namespace mfem;
ElementSmoother::ElementSmoother(ParFiniteElementSpace * fes_,
Array<int> ess_bdr, Coefficient * cf_ )
: Solver(fes_->GetTrueVSize()), fes(fes_), cf(cf_)
{
comm = fes->GetComm();
MPI_Comm_size(comm, &num_procs);
MPI_Comm_rank(comm, &myid);
Pr = fes->GetProlongationMatrix();
ParMesh * pmesh = fes->GetParMesh();
dim = pmesh->Dimension();
eidx.SetSize(dim);
eidx[0] = 0;
eidx[1] = 1;
if (dim == 3) eidx[2] = 8;
nrelems = pmesh->GetNE();
tpcf.SetSize(nrelems);
int vsize = fes->GetVSize();
ovlp_count.SetSize(vsize);
ovlp_count = 0.0;
// Construct overlap count for each dof &
// count x y z edges sharing a vertex
for (int i=0; i<nrelems; i++)
{
Array<int> elem_dofs;
fes->GetElementDofs(i,elem_dofs);
for (int j = 0; j<elem_dofs.Size(); j++)
{
ovlp_count(elem_dofs[j]) +=1.0;
}
}
Vector tovlp_count(fes->TrueVSize());
if (Pr)
{
Pr->MultTranspose(ovlp_count,tovlp_count);
Pr->Mult(tovlp_count, ovlp_count);
}
DenseMatrix edge_counts;
// GetVertexToEdgeCount(pmesh,edge_counts);
double * data = edge_counts.GetData();
// helper H1 fespace for communication of vertex info
H1_FECollection fec(1, dim);
ParFiniteElementSpace aux_fes(const_cast<ParMesh *>(pmesh), &fec);
Vector tedge_counts(aux_fes.TrueVSize());
Vector temp(aux_fes.GetVSize());
// const Operator * Ph = aux_fes.GetProlongationMatrix();
// if (Ph)
// {
// for (int d=0; d<dim; d++)
// {
// temp.SetData(&data[d*aux_fes.GetVSize()]);
// Ph->MultTranspose(temp,tedge_counts);
// Ph->Mult(tedge_counts, temp);
// }
// }
for (int i = 0; i < vsize; i++)
{
ovlp_count(i) = 1.0/sqrt(ovlp_count(i));
}
fes->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
TPElementTransformation TPTrans(*fes);
int nredges = pmesh->GetNEdges();
edge_orient.SetSize(nredges); // orientation of edges wrt the element
Array<DenseMatrix *> EdgeGrad(nredges);
Array<DenseMatrix *> EdgeMass(nredges);
Array<DenseMatrix *> AssembledEdgeGrad(nredges);
Array<DenseMatrix *> AssembledEdgeMass(nredges);
// Initialize
for (int i = 0; i<nredges; i++)
{
AssembledEdgeGrad[i] = nullptr;
AssembledEdgeMass[i] = nullptr;
EdgeGrad[i] = nullptr;
EdgeMass[i] = nullptr;
}
// loop through element to calculate Edge matrices
Array<int> emarker(nredges); emarker = 0;
for (int iel = 0; iel<nrelems; iel++)
{
tpcf[iel] = new ElementTPFunctionCoefficient(*fes,iel,*cf);
Array<int> edges, cor;
pmesh->GetElementEdges(iel,edges,cor);
for (int ii = 0; ii<dim; ii++)
{
int i = eidx[ii];
int edge = edges[i];
if (emarker[edge]) continue;
edge_orient[edge] = cor[i];
const FiniteElement * fe = fes->GetEdgeElement(edge);
tpcf[iel]->SetCoord(ii);
tpcf[iel]->SetOrient(edge_orient[edge]);
tpcf[iel]->ResetCounter(ii);
IntegrationRule *irs = TensorIntegrationRule(1,fe->GetOrder());
EdgeGrad[edge] = new DenseMatrix(fe->GetDof());
EdgeMass[edge] = new DenseMatrix(fe->GetDof());
int j;
Vector * Q;
switch (ii)
{
case 0:
j = 1;
Q = tpcf[iel]->GetVecX();
break;
case 1:
j = 0;
Q = tpcf[iel]->GetVecY();
break;
default:
j=2;
Q = tpcf[iel]->GetVecZ();
break;
}
Vector * vecG = TPTrans.GetTPTransformation(iel,ii,ii);
Vector * vecM = nullptr;
Vector *vecM1 = nullptr;
Vector *vecM2 = nullptr;
if (dim == 2)
{
vecM = TPTrans.GetTPTransformation(iel,ii,j);
}
else
{
switch (ii)
{
case 0:
{
vecM1 = TPTrans.GetTPTransformation(iel,ii,1);
vecM2 = TPTrans.GetTPTransformation(iel,ii,2);
}
break;
case 1:
{
vecM1 = TPTrans.GetTPTransformation(iel,ii,0);
vecM2 = TPTrans.GetTPTransformation(iel,ii,2);
}
default:
{
vecM1 = TPTrans.GetTPTransformation(iel,ii,0);
vecM2 = TPTrans.GetTPTransformation(iel,ii,1);
}
break;
}
vecM = new Vector(vecM1->Size());
for (int i=0; i<vecM->Size(); i++)
{
// (*vecM)(i) = ((*vecM1)(i)+(*vecM2)(i))/2.0;
// (*vecM)(i) = (*vecM1)(i);
(*vecM)(i) = 1.0/((1./(*vecM1)(i)+1./(*vecM2)(i))/2.0);
}
}
// GetDiffusionEdgeMatrix(edge,fes,*vecG,*Q,irs,*EdgeGrad[edge],edge_orient[edge]);
// GetMassEdgeMatrix(edge,fes,*vecM,*Q,irs,*EdgeMass[edge],edge_orient[edge]);
Get1DMatrices(fes,edge, edge_orient[edge],
*vecG, *vecM,*Q,irs,*EdgeGrad[edge],*EdgeMass[edge]);
emarker[edge] = 1;
}
}
for (int iel=0; iel<nrelems; iel++)
{
Array<int> edges, cor;
pmesh->GetElementEdges(iel,edges,cor);
for (int ii = 0; ii<dim; ii++)
{
int i = eidx[ii];
int k = edges[i];
const FiniteElement *fe = fes->GetEdgeElement(k);
int ndof = fe->GetDof();
DenseMatrix Grad(ndof); Grad = *EdgeGrad[k];
DenseMatrix Mass(ndof); Mass = *EdgeMass[k];
Array<int> vert;
pmesh->GetEdgeVertices(k,vert);
for (int i = 0; i<2; i++)
{
int vertex = vert[i];
Array<int> vertdofs;
fes->GetVertexDofs(vertex,vertdofs);
// double count = edge_counts(vertdofs[0],ii);
// Grad(i,i) *= count;
// Mass(i,i) *= count;
Grad(i,i) *= 2.;
Mass(i,i) *= 2.;
}
const Array<int> &dmap =
dynamic_cast<const TensorBasisElement&>(*fe).GetDofMap();
SparseMatrix * P = new SparseMatrix(dmap.Size());
for (int j = 0; j<dmap.Size(); j++)
{
P->Set(dmap[j],j, 1.0);
}
P->Finalize();
// Map from MFEM ordering to TensorProduct Ordering
AssembledEdgeGrad[k] = RAP(Grad,*P);
AssembledEdgeMass[k] = RAP(Mass,*P);
delete P;
}
}
Array<Array<int> * > tmap;
TensorProductEssentialDofsMaps(ess_tdof_list, fes, tmap, dofmap);
Array<DenseMatrix * > G(nredges);
Array<DenseMatrix * > M(nredges);
for (int ie = 0; ie<nredges; ie++)
{
if (!emarker[ie]) continue;
if (!AssembledEdgeMass[ie])
{
cout << "ie = " << ie << endl;
MFEM_ABORT("Memory allocation incosistency 2");
}
const FiniteElement * fe = fes->GetEdgeElement(ie);
int n = fe->GetDof() - tmap[ie]->Size();
G[ie] = new DenseMatrix(n);
M[ie] = new DenseMatrix(n);
const Array<int> &dmap =
dynamic_cast<const TensorBasisElement&>(*fe).GetDofMap();
// modify tmap to use tensor product index;
Array<int> dmapt(dmap.Size());
for (int i = 0; i<dmap.Size(); i++)
{
dmapt[dmap[i]] = i;
}
// Eliminate indices corresponding to tmap from matrices Grad1D_A and Mass1D_A
// construct Map;
Array<int> tmap_marker(fe->GetDof());
tmap_marker = 0;
for (int i = 0; i<tmap[ie]->Size(); i++)
{
int j = (*tmap[ie])[i];
tmap_marker[j] = 1;
}
Array<int> dof_list;
for (int i =0; i<fe->GetDof(); i++)
{
if (tmap_marker[i]) continue;
dof_list.Append(dmapt[i]);
}
dof_list.Sort();
for (int i=0; i<dof_list.Size(); i++)
{
int iii = (edge_orient[ie] == 1) ? i : n - i - 1;
int ii = dof_list[i];
for (int j=0; j<dof_list.Size(); j++)
{
int jjj = (edge_orient[ie] == 1) ? j : n - j - 1;
int jj = dof_list[j];
(*G[ie])(iii,jjj) = (*AssembledEdgeGrad[ie])(ii,jj);
(*M[ie])(iii,jjj) = (*AssembledEdgeMass[ie])(ii,jj);
}
}
}
elem_inv.SetSize(nrelems);
for (int iel = 0; iel<nrelems; iel++)
{
elem_inv[iel] = nullptr;
Array<int> edges, cor;
pmesh->GetElementEdges(iel,edges,cor);
Array<DenseMatrix *> Gv(dim);
Array<DenseMatrix *> Mv(dim);
bool msize = true;
for (int d = 0; d<dim; d++)
{
Gv[dim - d - 1] = G[edges[eidx[d]]];
Mv[dim - d - 1] = M[edges[eidx[d]]];
if (Mv[dim-d-1]->Size() == 0) msize = false;
}
if (msize) elem_inv[iel] = new FDSolver(Gv,Mv);
}
}
void ElementSmoother::Mult(const Vector &r, Vector &z) const
{
z = r;
z.SetSubVectorComplement(ess_tdof_list,0.0);
Vector rnew(fes->GetVSize());
Vector znew(fes->GetVSize());
Vector ztemp(fes->GetTrueVSize());
ztemp = 0.0;
znew = 0.0;
// const SparseMatrix * R = fes->GetRestrictionMatrix();
if (Pr)
{
Pr->Mult(r,rnew);
}
else
{
rnew = r;
}
for (int iel=0; iel<nrelems; iel++)
{
if (!elem_inv[iel]) continue;
int n = dofmap[iel]->Size();
Vector rloc(n);
rnew.GetSubVector(*dofmap[iel],rloc);
// pre-scale
for (int i = 0; i<n; i++)
{
int j = (*dofmap[iel])[i];
rloc[i] *= ovlp_count[j];
}
Vector zloc(n);
elem_inv[iel]->Mult(rloc,zloc);
// post-scale
for (int i = 0; i<n; i++)
{
int j = (*dofmap[iel])[i];
zloc[i] *= ovlp_count[j];
}
znew.AddElementVector(*dofmap[iel],zloc);
}
if (Pr)
{
Pr->MultTranspose(znew,ztemp);
}
else
{
ztemp = znew;
}
// R->Mult(znew,ztemp);
z += ztemp;
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "smoother-util.hpp"
using namespace std;
using namespace mfem;
class ElementSmoother: public Solver
{
private:
int num_procs, myid;
MPI_Comm comm;
int nrelems;
int dim;
ParFiniteElementSpace * fes = nullptr;
const Operator * Pr = nullptr;
Coefficient * cf = nullptr;
Array<int> eidx; // edge local index
Array<int> edge_orient; // orientation of edges wrt the element
Array<FDSolver *> elem_inv;
Array<int> ess_tdof_list;
Vector ovlp_count;
Array<Array<int> * > dofmap;
Array<ElementTPFunctionCoefficient *> tpcf;
public:
ElementSmoother(ParFiniteElementSpace * fes_, Array<int> ess_bdr, Coefficient * cf_=nullptr);
virtual void SetOperator(const Operator &op) { }
virtual void Mult(const Vector &r, Vector &z) const;
virtual void MultTranspose(const Vector &r, Vector &z) const { Mult(r,z); }
virtual ~ElementSmoother(){};
};
+289
View File
@@ -0,0 +1,289 @@
// MFEM Example 26
//
#include "mfem.hpp"
#include "exact_sol.hpp"
#include <fstream>
#include <iostream>
#include <random>
#include "element-smoother.hpp"
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 0. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 1. Parse command-line options.
const char *mesh_file = "l-shape-benchmark.mesh";
int init_geometric_refinements = 0;
int pinit_geometric_refinements = 0;
int geometric_refinements = 0;
int order_refinements = 2;
const char *device_config = "cpu";
bool visualization = true;
int order = 1;
int solver = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order", "Finite element order.");
args.AddOption(&solver, "-solver", "--solver", "Solver: 0:MG-Cheb-Jac, 1: MG-Cheb-ElemSmoother");
args.AddOption(&init_geometric_refinements, "-ref", "--initial-geometric-refinements",
"Number of serial geometric refinements defining the coarse mesh.");
args.AddOption(&pinit_geometric_refinements, "-pref", "--initial-geometric-refinements",
"Number of parallel geometric refinements defining the coarse mesh.");
args.AddOption(&geometric_refinements, "-gr", "--geometric-refinements",
"Number of geometric refinements done prior to order refinements.");
args.AddOption(&order_refinements, "-or", "--order-refinements",
"Number of order refinements. Finest level in the hierarchy has order 2^{or}.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
for (int l = 0; l < init_geometric_refinements; l++)
{
mesh->UniformRefinement();
}
mesh->EnsureNCMesh();
ParMesh * pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
mesh->Clear();
{
for (int l = 0; l < pinit_geometric_refinements; l++)
{
pmesh->UniformRefinement();
}
}
FiniteElementCollection *fec = new H1_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
Array<int> ess_bdr;
if(pmesh->bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
ess_bdr = 1;
}
ParGridFunction x(fespace);
FunctionCoefficient ex_coeff(lshape_exact);
x.ProjectCoefficient(ex_coeff);
// -------------------------------------------------
// Bilinear and linear forms
// -------------------------------------------------
ConstantCoefficient cf(1.0);
ParBilinearForm a(fespace);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
DiffusionIntegrator * aa = new DiffusionIntegrator(cf);
// int order1 = fespace->GetElementOrder(0);
IntegrationRule *irs = TensorIntegrationRule(*fespace,order);
aa->SetIntegrationRule(*irs);
a.AddDomainIntegrator(aa);
ParLinearForm b(fespace);
FunctionCoefficient rhscf(lshape_rhs);
b.AddDomainIntegrator(new DomainLFIntegrator(rhscf));
// -------------------------------------------------
char vishost[] = "localhost";
int visport = 19916;
socketstream sout;
if (visualization)
{
sout.open(vishost, visport);
if (!sout)
{
if (myid == 0)
{
cout << "Unable to connect to GLVis server at "
<< vishost << ':' << visport << endl;
cout << "GLVis visualization disabled.\n";
}
visualization = false;
}
sout.precision(8);
sout << "parallel " << num_procs << " " << myid << "\n";
sout << "solution\n" << *pmesh << x << flush;
}
L2_FECollection flux_fec(order, dim);
ParFiniteElementSpace flux_fes(pmesh, &flux_fec, dim);
FiniteElementCollection *smooth_flux_fec = NULL;
ParFiniteElementSpace *smooth_flux_fes = NULL;
smooth_flux_fec = new RT_FECollection(order-1, dim);
smooth_flux_fes = new ParFiniteElementSpace(pmesh, smooth_flux_fec, 1);
L2ZienkiewiczZhuEstimator estimator(*aa, x, flux_fes, *smooth_flux_fes);
ThresholdRefiner refiner(estimator);
refiner.SetTotalErrorFraction(0.7);
refiner.SetNCLimit(1);
StopWatch chrono;
Array<double> ts0, ts1, tsol;
int ref_amr = 20;
Array<int> iter;
Array<int> dofs;
ostringstream file_name;
file_name << "lshape-amr_" << order << ".csv";
ofstream conv(file_name.str().c_str());
conv << "DOFs " << ", " << "it-Cheb-Jac" << ", " << "it-ChebElemSmoother" << endl;
for (int it = 0; it < ref_amr ; it++)
{
HYPRE_BigInt global_dofs = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "\nAMR iteration " << it << endl;
cout << "Number of unknowns: " << global_dofs << endl;
}
Array<int> ess_tdof_list;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
a.Assemble();
b.Assemble();
OperatorPtr A;
Vector B, X;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
chrono.Clear();
chrono.Start();
chrono.Stop();
ts0.Append(chrono.RealTime());
chrono.Clear();
chrono.Start();
Solver * prec1 = nullptr;
Solver * prec2 = nullptr;
Solver * S = nullptr;
// if (solver)
// {
S = new ElementSmoother(fespace,ess_bdr, &cf);
prec1 = new OperatorChebyshevSmoother(*A, *S, 4, MPI_COMM_WORLD,7);
// }
// else
// {
Vector diag(fespace->GetTrueVSize());
a.AssembleDiagonal(diag);
prec2 = new OperatorChebyshevSmoother(*A, diag,ess_tdof_list, 4, MPI_COMM_WORLD,10);
// }
chrono.Stop();
ts1.Append(chrono.RealTime());
int print_level = 3;
int max_iter = 2000;
double rtol = 1e-8;
CGSolver pcg(MPI_COMM_WORLD);
pcg.SetPrintLevel(print_level);
pcg.SetMaxIter(max_iter);
pcg.SetRelTol(rtol);
pcg.SetOperator(*A);
pcg.SetPreconditioner(*prec1);
// chrono.Clear();
// chrono.Start();
Vector Y = X;
pcg.Mult(B,Y);
int iter1 = pcg.GetNumIterations();
pcg.SetPreconditioner(*prec2);
pcg.Mult(B,X);
int iter2 = pcg.GetNumIterations();
// chrono.Stop();
// tsol.Append(chrono.RealTime());
// iter.Append(pcg.GetNumIterations());
// dofs.Append(global_dofs);
delete S;
delete prec1;
delete prec2;
conv << global_dofs << ", " << iter1 << ", " << iter2 << endl;
a.RecoverFEMSolution(X,b,x);
if (visualization)
{
sout << "parallel " << num_procs << " " << myid << "\n";
sout << "solution\n" << *pmesh << x << flush;
}
refiner.Apply(*pmesh);
if (refiner.Stop())
{
if (myid == 0)
{
cout << "Stopping criterion satisfied. Stop." << endl;
}
break;
}
fespace->Update();
x.Update();
x.ProjectCoefficient(ex_coeff);
a.Update();
b.Update();
}
if (myid==0)
{
cout << "ts0 total = " << ts0.Sum() << endl;
cout << "ts1 total = " << ts1.Sum() << endl;
cout << "tsol total = " << tsol.Sum() << endl;
}
if (myid == 0)
{
cout << "num iterations = "; iter.Print(cout, iter.Size());
cout << "dofs = "; dofs.Print(cout, dofs.Size());
}
delete smooth_flux_fes;
delete smooth_flux_fec;
delete pmesh;
// 13. Free the used memory.
MPI_Finalize();
return 0;
}
View File
+670
View File
@@ -0,0 +1,670 @@
// MFEM Example 26
//
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include <random>
#include "element-smoother.hpp"
using namespace std;
using namespace mfem;
class DiffusionMultigrid : public GeometricMultigrid
{
private:
Coefficient * cf = nullptr;
int smoother_kind = 0;
// 0: Jacobi, 1:Chebychev, 2: element-smoother(matrix-free)
HypreBoomerAMG* amg;
public:
// Constructs a diffusion multigrid for the ParFiniteElementSpaceHierarchy
// and the array of essential boundaries
DiffusionMultigrid(ParFiniteElementSpaceHierarchy& fespaces,
Array<int>& ess_bdr, Coefficient * cf_,int smoother_ = 0)
: GeometricMultigrid(fespaces), cf(cf_), smoother_kind(smoother_)
{
ConstructCoarseOperatorAndSolver(fespaces.GetFESpaceAtLevel(0), ess_bdr,cf);
for (int level = 1; level < fespaces.GetNumLevels(); ++level)
{
ConstructOperatorAndSmoother(fespaces.GetFESpaceAtLevel(level), ess_bdr,cf);
}
}
virtual ~DiffusionMultigrid()
{
delete amg;
}
private:
void ConstructBilinearForm(ParFiniteElementSpace& fespace, Array<int>& ess_bdr,
bool partial_assembly, Coefficient * cf)
{
ParBilinearForm* form = new ParBilinearForm(&fespace);
if (partial_assembly)
{
form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
form->AddDomainIntegrator(new DiffusionIntegrator(*cf));
form->Assemble();
bfs.Append(form);
essentialTrueDofs.Append(new Array<int>());
fespace.GetEssentialTrueDofs(ess_bdr, *essentialTrueDofs.Last());
}
void ConstructCoarseOperatorAndSolver(ParFiniteElementSpace& coarse_fespace,
Array<int>& ess_bdr,
Coefficient * cf)
{
ConstructBilinearForm(coarse_fespace, ess_bdr, false, cf);
HypreParMatrix* hypreCoarseMat = new HypreParMatrix();
bfs.Last()->FormSystemMatrix(*essentialTrueDofs.Last(), *hypreCoarseMat);
amg = new HypreBoomerAMG(*hypreCoarseMat);
amg->SetPrintLevel(-1);
CGSolver* pcg = new CGSolver(MPI_COMM_WORLD);
pcg->SetPrintLevel(-1);
pcg->SetMaxIter(10);
pcg->SetRelTol(sqrt(1e-8));
pcg->SetAbsTol(0.0);
pcg->SetOperator(*hypreCoarseMat);
pcg->SetPreconditioner(*amg);
AddLevel(hypreCoarseMat, pcg, true, true);
}
void ConstructOperatorAndSmoother(ParFiniteElementSpace& fespace,
Array<int>& ess_bdr, Coefficient *cf)
{
ConstructBilinearForm(fespace, ess_bdr, true, cf);
OperatorPtr opr;
opr.SetType(Operator::ANY_TYPE);
bfs.Last()->FormSystemMatrix(*essentialTrueDofs.Last(), opr);
opr.SetOperatorOwner(false);
Solver * smoother = nullptr;
Vector diag;
if (smoother_kind < 2 )
{
diag.SetSize(fespace.GetTrueVSize());
bfs.Last()->AssembleDiagonal(diag);
}
switch (smoother_kind)
{
case 0:
smoother = new OperatorJacobiSmoother(diag,*essentialTrueDofs.Last(),0.6667);
break;
case 1:
smoother = new OperatorChebyshevSmoother(opr.Ptr(), diag,
*essentialTrueDofs.Last(), 5);
break;
case 2:
smoother = new ElementSmoother(&fespace,ess_bdr,cf);
break;
case 3:
{
ElementSmoother * sm = new ElementSmoother(&fespace,ess_bdr,cf);
smoother = new OperatorChebyshevSmoother(*opr,*sm,5,fespace.GetComm());
break;
}
default:
MFEM_ABORT("Wrong smoother choice");
break;
}
AddLevel(opr.Ptr(), smoother, true, true);
}
};
int dim;
int exact = 0;
bool tpcoeff = true;
double f_exact(const Vector & x);
double u_exact(const Vector & x);
void usol(const Vector & x, double &u, Vector & Grad, double & d2u);
double DiffusionCoeff(const Vector & x);
double TPDiffusionCoeff(const Vector & x, int coord);
void DiffusionCoeffGrad(const Vector & x, Vector & Grad);
int main(int argc, char *argv[])
{
// 0. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 1. Parse command-line options.
const char *mesh_file = "../../data/inline-quad.mesh";
int init_geometric_refinements = 0;
int pinit_geometric_refinements = 0;
int geometric_refinements = 0;
int order_refinements = 2;
const char *device_config = "cpu";
bool visualization = true;
int order = 1;
double skew_factor = 0.0;
double scale_factor = 1.0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order", "Finite element order.");
args.AddOption(&init_geometric_refinements, "-ref", "--initial-geometric-refinements",
"Number of serial geometric refinements defining the coarse mesh.");
args.AddOption(&pinit_geometric_refinements, "-pref", "--initial-geometric-refinements",
"Number of parallel geometric refinements defining the coarse mesh.");
args.AddOption(&geometric_refinements, "-gr", "--geometric-refinements",
"Number of geometric refinements done prior to order refinements.");
args.AddOption(&order_refinements, "-or", "--order-refinements",
"Number of order refinements. Finest level in the hierarchy has order 2^{or}.");
args.AddOption(&tpcoeff, "-tpcoeff", "--tp-coefficient", "-no-tpcoeff",
"--no-tp-coefficient", "Tensor product diffusion coefficient or not");
args.AddOption(&exact, "-exact", "--exact", "Exact Solution flag: 0: unknown");
args.AddOption(&skew_factor, "-c", "--skew_factor", "Skew_factor");
args.AddOption(&scale_factor, "-s", "--scale_factor", "Scale_factor");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// 2. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 3. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
// the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
// int nx = pow(2,init_geometric_refinements);
// int ny = pow(2,init_geometric_refinements);
// Mesh *mesh = new Mesh(1,1,1,mfem::Element::HEXAHEDRON,true,1.0,2.0,3.0,false);
// Mesh *mesh = new Mesh(1,1,mfem::Element::QUADRILATERAL,true,1.0,1.0,false);
// Mesh *mesh = new Mesh(1,4.0);
// move nodes
dim = mesh->Dimension();
mesh->EnsureNodes();
mesh->SetCurvature(3);
GridFunction * nodes = mesh->GetNodes();
// *nodes +=1.0;
// *nodes *=0.5;
double c = skew_factor;
double s = scale_factor;
if (dim == 2)
{
for (int i=0; i<nodes->Size()/2; i++)
{
// double temp = (*nodes)(2*i);
// (*nodes)(2*i) += (*nodes)(2*i)*(*nodes)(2*i) + c*pow((*nodes)(2*i+1),2);
// (*nodes)(2*i) += c*pow((*nodes)(2*i+1),2);
(*nodes)(2*i) += c*pow((*nodes)(2*i+1),2);
// (*nodes)(2*i+1) = (*nodes)(2*i+1)*(*nodes)(2*i+1) + c*pow(temp,2);
}
}
else
{
for (int i=0; i<nodes->Size()/3; i++)
{
// (*nodes)(3*i) += c*pow((*nodes)(3*i+1),2);
// (*nodes)(3*i+1) += c*pow((*nodes)(3*i+2),3);
(*nodes)(3*i+2) += c*pow((*nodes)(3*i),2);
}
}
for (int i=0; i<nodes->Size(); i++)
{
(*nodes)(i) *= s;
}
dim = mesh->Dimension();
// mesh->EnsureNCMesh();
// 4. Refine the mesh to increase the resolution and order
{
for (int l = 0; l < init_geometric_refinements; l++)
{
mesh->UniformRefinement();
}
}
ParMesh * pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
mesh->Clear();
{
for (int l = 0; l < pinit_geometric_refinements; l++)
{
pmesh->UniformRefinement();
}
}
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream mesh_sock(vishost, visport);
mesh_sock << "parallel " << num_procs << " " << myid << "\n";
mesh_sock.precision(8);
mesh_sock << "mesh\n" << *pmesh << flush;
}
FiniteElementCollection *fec = new H1_FECollection(order, dim);
ParFiniteElementSpace *coarse_fespace = new ParFiniteElementSpace(pmesh, fec);
ParFiniteElementSpaceHierarchy fespaces(pmesh, coarse_fespace, true, true);
Coefficient * cf = nullptr;
if (exact)
{
cf = new FunctionCoefficient(DiffusionCoeff);
}
else
{
cf = new ConstantCoefficient(1.0);
}
Array<FiniteElementCollection*> collections;
collections.Append(fec);
for (int level = 0; level < geometric_refinements; ++level)
{
fespaces.AddUniformlyRefinedLevel();
}
for (int level = 0; level < order_refinements; ++level)
{
// order++;
order *=2;
// collections.Append(new H1_FECollection(std::pow(2, level+1), dim));
collections.Append(new H1_FECollection(order, dim));
fespaces.AddOrderRefinedLevel(collections.Last());
}
HYPRE_Int size = fespaces.GetFinestFESpace().GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
cout << "Order = " << order << endl;
}
// 6. Set up the linear form b(.) which corresponds to the right-hand side of
// the FEM linear system, which in this case is (1,phi_i) where phi_i are
// the basis functions in the finite element fespace.
FunctionCoefficient f(f_exact);
ConstantCoefficient one(1.0);
Array<int> ess_bdr;
if(pmesh->bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
ess_bdr = 1;
}
ParGridFunction x(&fespaces.GetFinestFESpace());
// GridFunction gf_coeff(&fespaces.GetFinestFESpace());
ParMesh * ref_mesh = fespaces.GetFinestFESpace().GetParMesh();
L2_FECollection * l2fec = new L2_FECollection(order,dim);
ParFiniteElementSpace * l2fes = new ParFiniteElementSpace(ref_mesh,l2fec);
ParGridFunction gf_coeff(l2fes);
// gf_coeff.ProjectCoefficient(*cf);
gf_coeff.ProjectDiscCoefficient(*cf,mfem::GridFunction::AvgType::ARITHMETIC);
int print_level = 3;
int max_iter = 2000;
double rtol = 1e-8;
StopWatch chrono;
// for (int i = 0; i<=6; i++)
for (int i = 0; i<=6; i++)
{
OperatorPtr A;
Vector B, X;
Solver * prec = nullptr;
CGSolver pcg(MPI_COMM_WORLD);
pcg.SetPrintLevel(print_level);
pcg.SetMaxIter(max_iter);
pcg.SetRelTol(rtol);
// i=1; Chebychev-Jacobi-MG
// i=2; Chebychev-Element-MG
// i=3; Chebychev-Jacobi-Smoother
// i=4; Element-Smoother
// i=5; Chebychev-Element-Smoother
ParLinearForm *b = new ParLinearForm(&fespaces.GetFinestFESpace());
if (exact)
{
b->AddDomainIntegrator(new DomainLFIntegrator(f));
}
else
{
b->AddDomainIntegrator(new DomainLFIntegrator(one));
}
b->Assemble();
FunctionCoefficient u_ex(u_exact);
x = 0.0;
if (exact) x.ProjectCoefficient(u_ex);
if (i<4)
{
prec = new DiffusionMultigrid(fespaces, ess_bdr, cf,i);
dynamic_cast<DiffusionMultigrid *>(prec)->
SetCycleType(Multigrid::CycleType::VCYCLE, 1, 1);
dynamic_cast<DiffusionMultigrid *>(prec)->
FormFineLinearSystem(x, *b, A, X, B);
if (i == 0)
{
if (myid == 0)
cout << "\nJacobi-MG " << endl;
}
else if (i == 1)
{
if (myid == 0)
cout << "\nJacobi-Chebychev-MG " << endl;
}
else if (i == 2)
{
if (myid == 0)
cout << "\nElement-Smoother-MG " << endl;
}
else
{
if (myid == 0)
cout << "\nElement-Chebychev-MG " << endl;
}
pcg.SetOperator(*A);
if (prec) { pcg.SetPreconditioner(*prec); }
chrono.Clear();
chrono.Start();
pcg.Mult(B,X);
chrono.Stop();
if (myid == 0)
cout<< "PCG::mult time = " << chrono.RealTime() << endl;
// Recover the solution as a finite element grid function.
dynamic_cast<DiffusionMultigrid *>(prec)->RecoverFineFEMSolution(X, *b, x);
delete prec;
}
else
{
ParBilinearForm a(&fespaces.GetFinestFESpace());
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
DiffusionIntegrator * aa = new DiffusionIntegrator(*cf);
int order1 = fespaces.GetFinestFESpace().GetOrder(0);
IntegrationRule *irs = TensorIntegrationRule(fespaces.GetFinestFESpace(),order1);
aa->SetIntegrationRule(*irs);
a.AddDomainIntegrator(aa);
a.Assemble();
Array<int> ess_tdof_list;
fespaces.GetFinestFESpace().GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
a.FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
if (i==4)
{
Vector diag(fespaces.GetFinestFESpace().GetTrueVSize());
a.AssembleDiagonal(diag);
prec = new OperatorChebyshevSmoother(A.Ptr(), diag,ess_tdof_list, 1, MPI_COMM_WORLD);
if (myid == 0)
cout << "\nJacobi-Chebychev " << endl;
}
else if (i==5)
{
prec = new ElementSmoother(&fespaces.GetFinestFESpace(),ess_bdr, cf);
if (myid == 0)
cout << "\nElementSmoother " << endl;
}
else
{
ElementSmoother *S = new ElementSmoother(&fespaces.GetFinestFESpace(),ess_bdr, cf);
prec = new OperatorChebyshevSmoother(*A, *S, 1, MPI_COMM_WORLD);
if (myid == 0)
cout << "\nElement-Chebychev " << endl;
}
pcg.SetOperator(*A);
if (prec) { pcg.SetPreconditioner(*prec); }
chrono.Clear();
chrono.Start();
pcg.Mult(B,X);
chrono.Stop();
if (myid == 0)
cout<< "PCG::mult time = " << chrono.RealTime() << endl;
delete prec;
a.RecoverFEMSolution(X,*b,x);
}
delete b;
}
// 12. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << *fespaces.GetFinestFESpace().GetMesh() << x <<
flush;
socketstream coeff_sock(vishost, visport);
coeff_sock << "parallel " << num_procs << " " << myid << "\n";
coeff_sock.precision(8);
coeff_sock << "solution\n" << *fespaces.GetFinestFESpace().GetMesh() << gf_coeff <<
flush;
}
// 13. Free the used memory.
for (int level = 0; level < collections.Size(); ++level)
{
delete collections[level];
}
MPI_Finalize();
return 0;
}
double f_exact(const Vector & x)
{
// -div (f * grad(u)) = (f * gradu(0))_x + (f*gradu(1))_y + + (f*gradu(2))_z
// = f_x*gradu(0) + f * gradu(0)_x + f_y * gradu(1) + f* gradu(1)_y + f_z * gradu(2) + f* gradu(2)_z
// = f_x*gradu(0) + f_y * gradu(1) + f_z * gradu(2) + f*d2u
double u;
double d2u;
Vector gradu;
usol(x,u,gradu,d2u);
Vector gradf;
DiffusionCoeffGrad(x,gradf);
double f = DiffusionCoeff(x);
double val = gradf * gradu + f*d2u;
return -val;
}
double u_exact(const Vector & x)
{
double u;
Vector gradu;
double d2u;
usol(x,u,gradu,d2u);
return u;
}
void usol(const Vector & x, double &u, Vector & Grad, double & d2u)
{
Grad.SetSize(dim);
if (exact == 1)
{
Vector alpha(dim); alpha = 5.0;
// Vector alpha(dim); alpha = 0.5;
double s = alpha * x; // dot product
u = sin(M_PI*s);
d2u = 0.0;
for (int i = 0; i<dim; i++)
{
Grad[i] = alpha(i) * M_PI * cos(M_PI*s);
d2u += alpha(i)*alpha(i);
}
d2u = - M_PI*M_PI * d2u * u;
}
else if (exact == 2)
{
double c_0 = 1.2;
double k_0 = 3.0;
double c_1 = 2.3;
double k_1 = 5.0;
double c_2 = 1.3;
double k_2 = 1.0;
double alpha = c_0 + k_0 * x(0);
double beta = c_1 + k_1 * x(1);
double gamma = 1.0;
if (dim == 2)
{
u = sin(M_PI * alpha) * sin(M_PI * beta);
Grad[0] = M_PI*k_0 * cos(alpha) * sin(M_PI*beta);
Grad[1] = M_PI*k_1 * cos(beta) * sin(M_PI*alpha);
}
else if (dim == 3)
{
gamma = c_2 + k_2 * x(2);
u = sin(M_PI * alpha) * sin(M_PI * beta) * sin(M_PI*gamma);
Grad[0] = M_PI*k_0 * cos(alpha) * sin(M_PI*beta) * sin(M_PI*gamma);
Grad[1] = M_PI*k_1 * cos(beta) * sin(M_PI*alpha) * sin(M_PI*gamma);
Grad[2] = M_PI*k_2 * cos(gamma) * sin(M_PI*alpha) * sin(M_PI*beta);
}
double u_xx = - M_PI * M_PI * k_0 * k_0 * u;
double u_yy = - M_PI * M_PI * k_1 * k_1 * u;
double u_zz = - M_PI * M_PI * k_2 * k_2 * u;
d2u = u_xx + u_yy;
if (dim == 3 ) d2u += u_zz;
}
}
double TPDiffusionCoeff(const Vector & x, int coord)
{
double val;
switch (coord)
{
case 0: val = 4.+3.*x(0); break;
case 1: val = 0.5+7.*x(1)*x(1); break;
case 2: val = (0.1+2.*x(2)); break;
default:
val = (4.+3.*x(0))*(0.5+7.*x(1)*x(1));
if (dim == 3 ) val *= (0.1+2.*x(2));
break;
// case 0: val = x(0); break;
// case 1: val = 1.0; break;
// case 2: val = 1.0; break;
// default: val = x(0); break;
// case 0: val = 3.0; break;
// case 1: val = 2.0; break;
// case 2: val = 1.0; break;
// default: val = 6.0; break;
}
return val;
// return 2.0;
}
double DiffusionCoeff(const Vector & x)
{
double val;
if (tpcoeff)
{
val = (4.+3.*x(0))*(0.5+7.*x(1)*x(1));
if (dim == 3) val *= (0.1+2.*x(2));
}
else
{
// val = 2.0+cos(x.Sum());
Vector cf(dim);
// cf(0) = 0.1; cf(1) = 3.;
cf(0) = 1.0; cf(1) = 2.0;
// if (dim == 3) cf(2) = -7.8;
if (dim == 3) cf(2) = +1.8;
// double dd = x * cf + 1.5* x(1)*x(1);
double dd = x * cf;
// // double dd = x * cf;
// // val = exp(cos(dd));
val = exp(dd);
// Vector alpha(dim); alpha = 5.0;
// double s = alpha * x; // dot product
// val = 2.0+sin(M_PI*s);
}
return val;
}
void DiffusionCoeffGrad(const Vector & x, Vector & Grad)
{
Grad.SetSize(dim);
if (tpcoeff)
{
if (dim == 2)
{
Grad[0] = 3.* (0.5+7.*x(1)*x(1));
Grad[1] = 14.* x(1) * (4.+3.*x(0));
}
else
{
Grad[0] = 3.* (0.5+7.*x(1)*x(1))*(0.1+2.*x(2));
Grad[1] = 14.* x(1) * (4.+3.*x(0))*(0.1+2.*x(2));
Grad[2] = 2.*(4.+3.*x(0))*(0.5+7.*x(1)*x(1));
}
}
else
{
Vector cf(dim);
// cf(0) = 0.1; cf(1) = 3.;
cf(0) = 1.0; cf(1) = 2.0;
// if (dim == 3) cf(2) = -7.8;
if (dim == 3) cf(2) = 1.8;
// double dd = x * cf + 1.5* x(1)*x(1);
double dd = x * cf;
Vector alpha(dim); alpha = 5.0;
// for (int d = 0; d<dim; d++)
// {
// // // Grad[d] = -sin(x.Sum());
// // // Grad[d] = -cf(d) * exp(cos(dd))*sin(dd);
// // Grad[d] = cf(d) * exp(dd);
// Grad[d] = alpha(d) * M_PI * cos(M_PI*s);
// }
if (dim == 2)
{
Grad[0] = (cf(0) )*exp(dd);
Grad[1] = (cf(1) + 3.0*x(1))*exp(dd);
}
else
{
// Grad[0] = (cf(0) + 1.5 * x(1))*exp(dd);
Grad[0] = cf(0)*exp(dd);
Grad[1] = cf(1)*exp(dd);
Grad[2] = cf(2)*exp(dd);
}
}
}
+39
View File
@@ -0,0 +1,39 @@
#include "exact_sol.hpp"
double lshape_exact(const Vector & pt)
{
double x = pt[0];
double y = pt[1];
double r = sqrt(x*x + y*y);
double alpha = 2. / 3.;
double theta = atan2(y, x);
if (y < 0) { theta += 2 * M_PI; }
return pow(r,alpha) * sin(alpha * theta);
}
void lshape_grad(const Vector & x, Vector & grad)
{
}
double lshape_rhs(const Vector & x)
{
return 0.0;
}
double wavefront_exact(const Vector & x)
{
return 0.;
}
void wavefront_grad(const Vector & x, Vector & grad)
{
}
double wavefront_rhs(const Vector & x)
{
return 0.;
}
+15
View File
@@ -0,0 +1,15 @@
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
double lshape_exact(const Vector & x);
void lshape_grad(const Vector & x, Vector & grad);
double lshape_rhs(const Vector & x);
double wavefront_exact(const Vector & x);
void wavefront_grad(const Vector & x, Vector & grad);
double wavefront_rhs(const Vector & x);
+44
View File
@@ -0,0 +1,44 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
#
dimension
2
elements
3
1 3 0 3 4 1
1 3 3 6 7 4
1 3 4 5 2 1
boundary
8
1 1 0 1
1 1 1 2
1 1 2 5
2 1 5 4
2 1 4 7
1 1 7 6
1 1 6 3
1 1 3 0
vertices
8
2
-1 1
0 1
1 1
-1 0
0 0
1 0
-1 -1
0 -1
+59
View File
@@ -0,0 +1,59 @@
# Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
SRC = $(.,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES = ex_diffusion
PAR_EXAMPLES = ex_diffusionp ex_amr_diffusionp
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean
.PRECIOUS: %.o
COMMON_O= element-smoother.o smoother-util.o exact_sol.o
# Remove built-in rules
%: %.cpp
%.o: %.cpp
all: $(EXAMPLES)
# Rules for building the EXAMPLES
%: $(SRC)%.cpp $(COMMON_O) $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(COMMON_O) $(MFEM_LIBS)
# Rules for compiling miniapp dependencies
$(COMMON_O) $($(EXAMPLES)): \
%.o: $(SRC)%.cpp $(SRC)%.hpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $(<) -o $(@)
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
+746
View File
@@ -0,0 +1,746 @@
#include "smoother-util.hpp"
IntegrationRule * TensorIntegrationRule(const FiniteElementSpace & fes, int order)
{
Mesh *mesh = fes.GetMesh();
IntegrationRule * ir;
int ir_order = 2*order+2;
IntegrationRules IntRule(0, Quadrature1D::GaussLobatto);
// IntegrationRules IntRule(0, Quadrature1D::GaussLegendre);
int dim = mesh->Dimension();
switch (dim)
{
case 1:
ir = new IntegrationRule(IntRules.Get(mfem::Geometry::SEGMENT, ir_order));
break;
case 2:
ir = new IntegrationRule(IntRules.Get(mfem::Geometry::SQUARE, ir_order));
break;
default:
ir = new IntegrationRule(IntRules.Get(mfem::Geometry::CUBE, ir_order));
break;
}
return ir;
}
IntegrationRule * TensorIntegrationRule(int dim, int order)
{
IntegrationRule * ir;
int ir_order = 2*order+2;
// IntegrationRules IntRule(0, Quadrature1D::GaussLegendre);
IntegrationRules IntRule(0, Quadrature1D::GaussLobatto);
switch (dim)
{
case 1:
ir = new IntegrationRule(IntRules.Get(mfem::Geometry::SEGMENT, ir_order));
break;
case 2:
ir = new IntegrationRule(IntRules.Get(mfem::Geometry::SQUARE, ir_order));
break;
default:
ir = new IntegrationRule(IntRules.Get(mfem::Geometry::CUBE, ir_order));
break;
}
return ir;
}
void KronMult(const Vector & x, const Vector & y, Vector & z)
{
int n = x.Size();
int m = y.Size();
z.SetSize(n*m);
for (int i=0; i<n; i++)
{
for (int j = 0; j<m; j++)
{
z(i*m+j) = x(i)*y(j);
}
}
}
void KronMult(const Vector & x, const Vector & y, const Vector & z, Vector & w)
{
Vector xy;
KronMult(x,y,xy);
KronMult(xy,z,w);
}
void AlterLS(DenseMatrix & T, Vector & vecA, Vector & vecB)
{
int n = T.Height();
int m = T.Width();
Vector x(n);
Vector temp(m);
double s;
Vector y(m); y.Randomize(1); y /= y.Norml2();
int maxit = 3;
for (int i=0; i<maxit; i++)
{
T.Mult(y,x); x /= x.Norml2();
T.MultTranspose(x,temp); y=temp; y/= y.Norml2();
s = InnerProduct(y,temp);
}
vecA = x; vecA *= sqrt(s);
vecB = y; vecB *= sqrt(s);
}
void AlterLS(DenseTensor & T, Vector & vecA, Vector & vecB, Vector & vecC)
{
int n = T.SizeI();
int m = T.SizeJ();
int l = T.SizeK();
DenseMatrix A0(n,l*m);
DenseMatrix A1(m,l*n);
DenseMatrix A2(l,n*m);
for (int i=0; i<n; ++i)
{
for (int j=0; j<m; ++j)
{
for (int k=0; k<l; ++k)
{
A0(i,j+k*m) = T(i,j,k);
A1(j,i+k*n) = T(i,j,k);
A2(k,i+j*n) = T(i,j,k);
}
}
}
double s;
Vector x(n);
Vector temp;
// Vector temp;
Vector y(m); y.Randomize(1); y/=y.Norml2();
Vector z(l); z.Randomize(2); z/=z.Norml2();
int maxit = 3;
for (int i = 0; i<maxit; i++)
{
KronMult(z,y,temp); A0.Mult(temp,x); s=x.Norml2(); x/=s;
KronMult(z,x,temp); A1.Mult(temp,y); s=y.Norml2(); y/=s;
KronMult(y,x,temp); A2.Mult(temp,z); s=z.Norml2(); z/=s;
}
vecA = x; vecA *= cbrt(s);
vecB = y; vecB *= cbrt(s);
vecC = z; vecC *= cbrt(s);
}
ElementTPFunctionCoefficient::ElementTPFunctionCoefficient(FiniteElementSpace &fes, int iel, Coefficient &cf)
{
coeff_avg = 0.0;
dim = fes.GetMesh()->Dimension();
ElementTransformation * Tr;
const IntegrationRule * ir;
const FiniteElement * fe = fes.GetFE(iel);
ir = TensorIntegrationRule(fes,fe->GetOrder());
int nint1D = (dim == 2) ? sqrt(ir->GetNPoints()) : cbrt(ir->GetNPoints());
int nrintx = nint1D;
int nrinty = nint1D;
int nrintz = (dim ==2) ? 0 : nint1D;
if (dim == 2)
{
A.SetSize(nrintx,nrinty);
Tr = fes.GetElementTransformation(iel);
nint = ir->GetNPoints();
for (int i = 0; i < nint; i++)
{
int nint1D = sqrt(ir->GetNPoints());
int iy = i/nint1D;
int ix = i%nint1D;
const IntegrationPoint &ip = ir->IntPoint(i);
Vector x(dim);
Tr->Transform(ip,x);
double val = cf.Eval(*Tr, ip);
A(ix,iy) = val;
coeff_avg += val;
}
AlterLS(A,VecX,VecY);
coeff_avg /= (double)nint;
}
else
{
T.SetSize(nrintx,nrinty,nrintz);
Tr = fes.GetElementTransformation(iel);
for (int i = 0; i < ir->GetNPoints(); i++)
{
int nint1D = cbrt(ir->GetNPoints());
int iz = i/(nint1D*nint1D);
int iy = (i - iz*nint1D*nint1D)/nint1D;
int ix = (i - iz*nint1D*nint1D)%nint1D;
const IntegrationPoint &ip = ir->IntPoint(i);
Tr->SetIntPoint (&ip);
double val = cf.Eval(*Tr, ip);
T(ix,iy,iz) = val;
}
AlterLS(T,VecX,VecY,VecZ);
}
delete ir;
}
double ElementTPFunctionCoefficient::Eval(ElementTransformation &T, const IntegrationPoint &ip)
{
double val = 0.0;
switch (coord)
{
case 0:
{
if (orient == 1)
{
val = VecX(nintx++);
}
else
{
int nend = VecX.Size();
val = VecX(nend-1-nintx++);
}
}
break;
case 1:
{
if (orient == 1)
{
val = VecY(ninty++);
}
else
{
int nend = VecY.Size();
val = VecY(nend-1-ninty++);
}
}
break;
case 2:
{
if (orient == 1)
{
val = VecZ(nintz++);
}
else
{
int nend = VecZ.Size();
val = VecZ(nend-1-nintz++);
}
}
break;
case -1:
{
int nint1D = sqrt(nint);
int iy = mint/nint1D;
int ix = mint%nint1D;
val = VecX(ix) * VecY(iy);
mint++;
}
break;
case -2:
{
int nint1D = cbrt(nint);
int iz = mint/(nint1D*nint1D);
int iy = (mint - iz*nint1D*nint1D)/nint1D;
int ix = (mint - iz*nint1D*nint1D)%nint1D;
val = VecX(ix) * VecY(iy) * VecZ(iz);
mint++;
}
break;
default: MFEM_ABORT("ElementTPFunctionCoefficient::Eval: Wrong coord choice");
break;
}
return val;
}
void TPElementTransformation::Setup2D()
{
Mesh * mesh = fes->GetMesh();
MFEM_VERIFY(dim == 2, "Wrong dimension");
int nel = mesh->GetNE();
TransA1D.SetSize(nel,dim);
TransB1D.SetSize(nel,dim);
// Get ElementTransformations for the 2D elements
for (int iel = 0; iel <nel; iel++)
{
// allocate memory for Trans1D
for (int d = 0; d<dim; d++)
{
TransA1D[iel][d] = new Vector;
TransB1D[iel][d] = new Vector;
}
ElementTransformation * T = mesh->GetElementTransformation(iel);
// Populate integration points and get the K = adj(J)/sqrt(detJ);
// Store K_11^2 + K_12^2
// K_21^2 + K_22^2
const FiniteElement * fe = fes->GetFE(iel);
const IntegrationRule * ir = TensorIntegrationRule(*fes,fe->GetOrder());
int nint = ir->GetNPoints();
int nint1D = sqrt(nint);
DenseMatrix A, B;
A.SetSize(nint1D,nint1D);
B.SetSize(nint1D,nint1D);
for (int i = 0; i < nint; i++)
{
int iy = i/nint1D;
int ix = i%nint1D;
const IntegrationPoint &ip = ir->IntPoint(i);
T->SetIntPoint(&ip);
double detJ = T->Weight();
const DenseMatrix & adjJ = T->AdjugateJacobian();
DenseMatrix JtJ(adjJ.Height());
MultAtB(adjJ,adjJ,JtJ);
JtJ *= 1.0/abs(detJ);
// DenseMatrix adjtt(adjJ.Height());
// MultAtB(adjJ, adjJ, adjtt);
double valA = pow(abs(adjJ(0,0))+abs(adjJ(0,1)),2)/abs(detJ);
// double valA = abs(JtJ(0,0))+abs(JtJ(0,1));
double valB = pow(abs(adjJ(1,1))+abs(adjJ(1,0)),2)/abs(detJ);
// double valB = abs(JtJ(1,1))+abs(JtJ(1,0));
A(ix,iy) = valA;
B(ix,iy) = valB;
}
AlterLS(A,*TransA1D[iel][0],*TransA1D[iel][1]);
AlterLS(B,*TransB1D[iel][0],*TransB1D[iel][1]);
}
}
void TPElementTransformation::Setup3D()
{
Mesh * mesh = fes->GetMesh();
MFEM_VERIFY(dim == 3, "Wrong dimension");
int nel = mesh->GetNE();
TransA1D.SetSize(nel,dim);
TransB1D.SetSize(nel,dim);
TransC1D.SetSize(nel,dim);
// Get ElementTransformations for the 3D elements
for (int iel = 0; iel <nel; iel++)
{
// allocate memory for Trans1D
for (int d = 0; d<dim; d++)
{
TransA1D[iel][d] = new Vector;
TransB1D[iel][d] = new Vector;
TransC1D[iel][d] = new Vector;
}
ElementTransformation * T = mesh->GetElementTransformation(iel);
// Populate integrations points and get the K = adj(J)/sqrt(detJ);
// Store K_11^2 + K_12^2 + K_13^2
// K_21^2 + K_22^2 + K_23^2
// K_31^2 + K_32^2 + K_33^2
const FiniteElement * fe = fes->GetFE(iel);
const IntegrationRule * ir = TensorIntegrationRule(*fes,fe->GetOrder());
int nint = ir->GetNPoints();
int nint1D = cbrt(nint);
DenseTensor A, B, C;
A.SetSize(nint1D,nint1D,nint1D);
B.SetSize(nint1D,nint1D,nint1D);
C.SetSize(nint1D,nint1D,nint1D);
for (int i = 0; i < nint; i++)
{
int iz = i/(nint1D*nint1D);
int iy = (i - iz*nint1D*nint1D)/nint1D;
int ix = (i - iz*nint1D*nint1D)%nint1D;
const IntegrationPoint &ip = ir->IntPoint(i);
T->SetIntPoint(&ip);
double detJ = T->Weight();
const DenseMatrix & adjJ = T->AdjugateJacobian();
// DenseMatrix JtJ(adjJ.Height());
// MultAtB(adjJ,adjJ,JtJ);
// JtJ *= 1.0/abs(detJ);
// double valA = abs(JtJ(0,0))+abs(JtJ(0,1))+abs(JtJ(0,2));
double valA = pow(abs(adjJ(0,0))+abs(adjJ(0,1))+abs(adjJ(0,2)),2)/abs(detJ);
// double valB = abs(JtJ(1,0))+abs(JtJ(1,1))+abs(JtJ(1,2));
double valB = pow(abs(adjJ(1,0))+abs(adjJ(1,1))+abs(adjJ(1,2)),2)/abs(detJ);
// double valC = abs(JtJ(2,0))+abs(JtJ(2,1))+abs(JtJ(2,2));
double valC = pow(abs(adjJ(2,0))+abs(adjJ(2,1))+abs(adjJ(2,2)),2)/abs(detJ);
A(ix,iy,iz) = valA;
B(ix,iy,iz) = valB;
C(ix,iy,iz) = valC;
}
AlterLS(A,*TransA1D[iel][0],*TransA1D[iel][1],*TransA1D[iel][2]);
AlterLS(B,*TransB1D[iel][0],*TransB1D[iel][1],*TransB1D[iel][2]);
AlterLS(C,*TransC1D[iel][0],*TransC1D[iel][1],*TransC1D[iel][2]);
// for (int i = 0; i<dim; i++)
// {
// cout << "TransA["<<i<<"] = "; TransA1D[iel][i]->Print(cout, TransA1D[iel][i]->Size());
// cout << "TransB["<<i<<"] = "; TransB1D[iel][i]->Print(cout, TransB1D[iel][i]->Size());
// cout << "TransC["<<i<<"] = "; TransC1D[iel][i]->Print(cout, TransC1D[iel][i]->Size());
// }
// cin.get();
}
}
TPElementTransformation::TPElementTransformation(FiniteElementSpace &fes_)
: fes(&fes_)
{
dim = fes->GetMesh()->Dimension();
if (dim == 2)
{
Setup2D();
}
else
{
Setup3D();
}
}
void GetVertexToEdgeCount(const Mesh * mesh, DenseMatrix & edge_counts)
{
// serial
int dim = mesh->Dimension();
int nv = mesh->GetNV();
int ne = mesh->GetNEdges();
int nel = mesh->GetNE();
Array<int> ibeg(dim), iend(dim), inc(dim);
// loop through axis
for (int axis = 0; axis<dim; axis++)
{
switch (axis)
{
case 0: ibeg[axis] = 0; inc[axis] = 2; iend[axis] = (dim == 2) ? 4 : 8 ; break; // "x" edges
case 1: ibeg[axis] = 1; inc[axis] = 2; iend[axis] = (dim == 2) ? 4 : 8 ; break; // "y" edges
case 2: ibeg[axis] = 8; inc[axis] = 1; iend[axis] = 12; break; // "z edges"
default: MFEM_ABORT("This should be unreachable"); break;
}
}
Array<bool> edge_marker(ne);
edge_counts.SetSize(nv,dim);
edge_counts = 0.0;
Array<int> edge_owned;
bool par = false;
#ifdef MFEM_USE_MPI
const ParMesh * pmesh = dynamic_cast<const ParMesh *>(mesh);
if (pmesh) par = true;
#endif
edge_owned.SetSize(ne); edge_owned = 0;
ND_FECollection fec(1, dim);
if (par)
{
ParFiniteElementSpace aux_fes(const_cast<ParMesh *>(pmesh), &fec);
int mytoffset = aux_fes.GetMyTDofOffset();
int tsize = aux_fes.GetTrueVSize();
Array<int> dofs;
for (int i=0; i<ne; ++i)
{
aux_fes.GetEdgeDofs(i, dofs);
const int ldof = (dofs[0] >= 0) ? dofs[0] : -1 - dofs[0];
int sign = aux_fes.GetLocalTDofNumber(ldof);
if (sign == -1) continue; // just a hack for now to work with AMR, need to rethink this.
int gdof = aux_fes.GetGlobalTDofNumber(ldof);
if (gdof >= mytoffset && gdof < mytoffset+tsize)
{
edge_owned[i] = 1;
}
}
}
else
{
edge_owned = 1;
if (mesh->Nonconforming())
{
NCMesh * ncmesh = mesh->ncmesh;
const mfem::NCMesh::NCList &nclist = ncmesh->GetEdgeList();
Array<mfem::NCMesh::Slave> ncslaves = nclist.slaves;
int ns = ncslaves.Size();
for (int i=0; i<ns; ++i)
{
int j = ncslaves[i].index;
edge_owned[j] = 0;
}
}
}
for (int d=0; d<dim; d++)
{
edge_marker = false;
for (int iel = 0; iel<nel; iel++)
{
Array<int> elem_edges, cor;
mesh->GetElementEdges(iel,elem_edges,cor);
for (int i = ibeg[d]; i<iend[d]; i += inc[d])
{
int edge = elem_edges[i];
if (edge_owned.Size())
{
// skip contributions from edge not owned by the proc
if (!edge_owned[edge]) continue;
}
if (edge_marker[edge]) continue;
Array<int>edge_vert;
mesh->GetEdgeVertices(edge,edge_vert);
for (int j = 0; j < 2; j++)
{
int vertex = edge_vert[j];
edge_counts(vertex,d) += 1.0;
}
edge_marker[edge] = true;
}
}
}
}
void GetDiffusionEdgeMatrix(int iedge, FiniteElementSpace * fes,
Vector & Jac1D, Vector & Coeff1D,
const IntegrationRule *ir,
DenseMatrix &elmat, int orient)
{
const FiniteElement * el = fes->GetEdgeElement(iedge);
int nd = el->GetDof();
int dim = el->GetDim();
DenseMatrix dshape(nd,dim);
elmat.SetSize(nd);
elmat = 0.0;
int nint = ir->GetNPoints();
for (int i = 0; i < nint; i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
double w = ip.weight;
el->CalcDShape(ip, dshape);
int j = orient == -1 ? nint-i-1 : i;
double val = Coeff1D(j) * Jac1D(i);
w *= val;
AddMult_a_AAt(w, dshape, elmat);
}
}
void GetMassEdgeMatrix(int iedge, FiniteElementSpace * fes,
Vector & Jac1D, Vector & Coeff1D,
const IntegrationRule *ir,
DenseMatrix &elmat, int orient)
{
const FiniteElement * el = fes->GetEdgeElement(iedge);
int nd = el->GetDof();
Vector shape(nd);
elmat.SetSize(nd);
elmat = 0.0;
int nint = ir->GetNPoints();
for (int i = 0; i < nint; i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
double w = ip.weight;
el->CalcShape(ip, shape);
int j = orient == -1 ? nint-i-1 : i;
double val = Coeff1D(j) * Jac1D(i);
w *= val;
AddMult_a_VVt(w, shape, elmat);
}
}
void Get1DMatrices(FiniteElementSpace * fes, int iedge, int orient,
Vector & JacL, Vector & JacM, Vector & Coeff1D,
const IntegrationRule *ir,
DenseMatrix &L, DenseMatrix & M)
{
const FiniteElement * el = fes->GetEdgeElement(iedge);
int dim = el->GetDim();
int nd = el->GetDof();
DenseMatrix dshape(nd,dim);
Vector shape(nd);
L.SetSize(nd); L = 0.0;
M.SetSize(nd); M = 0.0;
int nint = ir->GetNPoints();
for (int i = 0; i < nint; i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
double w = ip.weight;
el->CalcDShape(ip, dshape);
el->CalcShape(ip, shape);
int j = orient == -1 ? nint-i-1 : i;
double wL = w*Coeff1D(j) * JacL(j);
double wM = w*Coeff1D(j) * JacM(j);
AddMult_a_AAt(wL, dshape, L);
AddMult_a_VVt(wM, shape, M);
}
}
void TensorProductEssentialDofsMaps(const Array<int> & ess_tdof_list,
const ParFiniteElementSpace * fes,
Array<Array<int> *> & tmap, // local edge map
Array<Array<int>* > & non_ess_dofs) // element map
{
MPI_Comm comm = fes->GetComm();
int num_procs,myid;
MPI_Comm_size(comm, &num_procs);
MPI_Comm_rank(comm, &myid);
// 1. Find the element local dofs that are essential and then identify
// edge vertices (in local numbering) that are produced from
// Gather the vertex dofs to be eliminated for each edge in tmap.
// 2. Reconstruct the essential dofs for each element (this list might be different
// for each element e.g. l-shape/fichera mesh, where to keep the kronecker product
// structure an essential dof is not eliminated, see vertex (v) below)
// . . .
// . . .
// . . v . .
// . . . . .
// . . . . .
// ----------------------------------------------------------------------
//
ParMesh * pmesh = fes->GetParMesh();
int dim = pmesh->Dimension();
int nredges = pmesh->GetNEdges();
int tsize = fes->GetTrueVSize();
int vsize = fes->GetVSize();
Vector tess_tdof_marker(tsize); tess_tdof_marker = 0.0;
Vector ess_tdof_marker(vsize);
for (int i = 0; i<ess_tdof_list.Size(); i++)
{
int tdof = ess_tdof_list[i];
tess_tdof_marker[tdof] = 1.0;
}
fes->GetProlongationMatrix()->Mult(tess_tdof_marker, ess_tdof_marker);
tmap.SetSize(nredges);
for (int i = 0; i<nredges; i++) { tmap[i] = new Array<int>(0); }
int nel = pmesh->GetNE();
non_ess_dofs.SetSize(nel);
for (int iel = 0; iel<nel; iel++)
{
non_ess_dofs[iel] = new Array<int>(0);
Array<int> local_dofs;
Array<int> local_tdofs;
const FiniteElement &fe = *fes->GetFE(iel);
// mfem to Tensor basis map
const Array<int> &dmap =
dynamic_cast<const TensorBasisElement&>(fe).GetDofMap();
Array<int> dmapt(dmap.Size());
for (int i = 0; i<dmapt.Size(); i++)
{
dmapt[dmap[i]] = i;
}
Array<int> elem_dofs;
fes->GetElementDofs(iel,elem_dofs);
// get local index of ess_dofs
int n = elem_dofs.Size();
// loop through the vertices
for (int i = 0; i< elem_dofs.Size(); i++)
{
int ldof = elem_dofs[i];
if (!ess_tdof_marker[ldof]) // if not essential dof
{
local_dofs.Append(dmapt[i]); // append in local element dofs
local_tdofs.Append(ldof); // append in local element dofs
}
}
// Find the possible vertex local dofs on the edges to be removed
int n1D = (dim == 2) ? sqrt(n) : cbrt(n);
Array<int> edges, cor;
pmesh->GetElementEdges(iel,edges,cor);
Array<int> eidx(dim);
eidx[0] = 0;
eidx[1] = 1;
if (dim == 3) eidx[2] = 8;
// mark edge local dofs
Array<Array<int> *> markers(dim);
for (int d = 0; d<dim; d++)
{
Array<int> marker(n1D); marker = 0;
for (int i = 0; i<local_dofs.Size(); i++)
{
int j = local_dofs[i];
int c = j/(n1D*n1D);
int l = c*n1D*n1D;
int k = (d == 0) ? (j-l)%n1D : d == 1 ? (j-l)/n1D : c;
marker[k] = 1;
}
markers[d] = new Array<int>(marker);
// pick up the edge and orientation
int edge = edges[eidx[d]];
int orient = cor[eidx[d]];
const FiniteElement &fe = *fes->GetEdgeElement(edge);
// edge tensor product map
const Array<int> &emap =
dynamic_cast<const TensorBasisElement&>(fe).GetDofMap();
Array<int> edge_ldofs;
for (int i = 0; i<marker.Size(); i++)
{
if (!marker[i])
{
if (orient == 1)
{
edge_ldofs.Append(emap[i]);
}
else
{
int k = (emap[i] == 1) ? 0 : 1;
edge_ldofs.Append(k);
}
}
}
edge_ldofs.Sort(); edge_ldofs.Unique();
tmap[edge]->Append(edge_ldofs);
tmap[edge]->Sort();
tmap[edge]->Unique();
}
if (dim == 2)
{
for (int j = 0; j<n1D; j++)
{
if ((*markers[1])[j])
{
for (int i = 0; i<n1D; i++)
{
if ((*markers[0])[i])
{
int ldof = n1D*j+i;
non_ess_dofs[iel]->Append(ldof);
}
}
}
}
}
else
{
for (int k = 0; k<n1D; k++)
{
if ((*markers[2])[k])
{
for (int j = 0; j<n1D; j++)
{
if ((*markers[1])[j])
{
for (int i = 0; i<n1D; i++)
{
if ((*markers[0])[i])
{
int ldof = n1D*n1D*k + n1D*j+i;
non_ess_dofs[iel]->Append(ldof);
}
}
}
}
}
}
}
non_ess_dofs[iel]->Sort();
non_ess_dofs[iel]->Unique();
for (int i = 0; i<non_ess_dofs[iel]->Size(); i++)
{
int ldof = (*non_ess_dofs[iel])[i];
int tdof = elem_dofs[dmap[ldof]];
(*non_ess_dofs[iel])[i] = tdof;
}
for (int d=0; d<dim; d++)
{
delete markers[d];
}
}
};
+112
View File
@@ -0,0 +1,112 @@
#pragma once
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
IntegrationRule * TensorIntegrationRule(const FiniteElementSpace & fes, int order);
IntegrationRule * TensorIntegrationRule(int dim, int order);
void KronMult(const Vector & x, const Vector & y, Vector & z);
void KronMult(const Vector & x, const Vector & y, const Vector & z, Vector & w);
void AlterLS(DenseMatrix & T, Vector & vecA, Vector & vecB);
void AlterLS(DenseTensor & T, Vector & vecA, Vector & vecB, Vector & vecC);
class ElementTPFunctionCoefficient : public Coefficient//
{
private:
int dim;
DenseMatrix A;
DenseTensor T;
double coeff_avg;
Vector VecX;
Vector VecY;
Vector VecZ;
int orient;
int nint; // total num of integrations points
int mint=0; // counter for all the integrations points
int nintx = 0; // counter for the x integrations points
int ninty = 0; // counter for the y integration points
int nintz = 0; // counter for the z integration points
int coord = 0; // (indication flag for x,y or z coordinate)
public:
ElementTPFunctionCoefficient(FiniteElementSpace &fes, int iel, Coefficient &cf);
double GetCoeffAvg() {return coeff_avg;}
void ResetCounters() { mint = nintx = ninty = nintz = 0; }
void ResetCounter(int c)
{
switch (c)
{
case 0: nintx = 0; break;
case 1: ninty = 0; break;
case 2: nintz = 0; break;
default: mint = 0; break;
}
}
void SetCoord(int coord_) { coord = coord_; }
void SetOrient(int orient_) { orient = orient_; }
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip);
Vector * GetVecX(){return &VecX;}
Vector * GetVecY(){return &VecY;}
Vector * GetVecZ(){return &VecZ;}
virtual ~ElementTPFunctionCoefficient() { }
};
class TPElementTransformation
{
private:
int dim;
FiniteElementSpace * fes = nullptr;
Array2D<Vector *> TransA1D;
Array2D<Vector *> TransB1D;
Array2D<Vector *> TransC1D;
void Setup2D();
void Setup3D();
public:
TPElementTransformation(FiniteElementSpace &fes_);
Vector * GetTPTransformation(int iel, int coord, int which_coeff)
{
switch(which_coeff)
{
case 0: return TransA1D[iel][coord]; break;
case 1: return TransB1D[iel][coord]; break;
case 2:
{
MFEM_VERIFY(dim == 3, "Wrong coeff for this dimension");
return TransC1D[iel][coord];
break;
}
default: MFEM_ABORT("Wrong coeff selection"); return 0; break;
}
}
~TPElementTransformation() { }
};
void GetVertexToEdgeCount(const Mesh * mesh, DenseMatrix & edge_counts);
void Get1DMatrices(FiniteElementSpace * fes, int iedge, int orient,
Vector & JacL, Vector & JacM, Vector & Coeff1D,
const IntegrationRule *ir,
DenseMatrix &L, DenseMatrix & M);
void GetDiffusionEdgeMatrix(int iedge, FiniteElementSpace * fes,
Vector & Jac1D, Vector & Coeff1D,
const IntegrationRule *ir,
DenseMatrix &elmat, int orient);
void GetMassEdgeMatrix(int iedge, FiniteElementSpace * fes,
Vector & Jac1D, Vector & Coeff1D,
const IntegrationRule *ir,
DenseMatrix &elmat, int orient);
void TensorProductEssentialDofsMaps(const Array<int> & ess_tdof_list,
const ParFiniteElementSpace * fes,
Array<Array<int> *> & tmap, // local edge map
Array<Array<int>* > & non_ess_dofs); // element map
+171 -148
View File
@@ -31,7 +31,7 @@ void BilinearForm::AllocMat()
const Table &elem_dof = fes->GetElementToDofTable();
Table dof_dof;
if (fbfi.Size() > 0)
if (interior_face_integs.Size() > 0)
{
// the sparsity pattern is defined from the map: face->element->dof
Table face_dof, dof_face;
@@ -99,15 +99,15 @@ BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
ext = NULL;
// Copy the pointers to the integrators
dbfi = bf->dbfi;
domain_integs = bf->domain_integs;
bbfi = bf->bbfi;
bbfi_marker = bf->bbfi_marker;
boundary_integs = bf->boundary_integs;
boundary_integs_marker = bf->boundary_integs_marker;
fbfi = bf->fbfi;
interior_face_integs = bf->interior_face_integs;
bfbfi = bf->bfbfi;
bfbfi_marker = bf->bfbfi_marker;
boundary_face_integs = bf->boundary_face_integs;
boundary_face_integs_marker = bf->boundary_face_integs_marker;
AllocMat();
}
@@ -234,46 +234,47 @@ void BilinearForm::Finalize (int skip_zeros)
void BilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi)
{
dbfi.Append(bfi);
dbfi_marker.Append(NULL); // NULL marker means apply everywhere
domain_integs.Append(bfi);
domain_integs_marker.Append(NULL); // NULL marker means apply everywhere
}
void BilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi,
Array<int> &elem_marker)
{
dbfi.Append(bfi);
dbfi_marker.Append(&elem_marker);
domain_integs.Append(bfi);
domain_integs_marker.Append(&elem_marker);
}
void BilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi)
{
bbfi.Append (bfi);
bbfi_marker.Append(NULL); // NULL marker means apply everywhere
boundary_integs.Append (bfi);
boundary_integs_marker.Append(NULL); // NULL marker means apply everywhere
}
void BilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi,
Array<int> &bdr_marker)
{
bbfi.Append (bfi);
bbfi_marker.Append(&bdr_marker);
boundary_integs.Append (bfi);
boundary_integs_marker.Append(&bdr_marker);
}
void BilinearForm::AddInteriorFaceIntegrator(BilinearFormIntegrator * bfi)
{
fbfi.Append (bfi);
interior_face_integs.Append (bfi);
}
void BilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi)
{
bfbfi.Append(bfi);
bfbfi_marker.Append(NULL); // NULL marker means apply everywhere
boundary_face_integs.Append(bfi);
// NULL marker means apply everywhere
boundary_face_integs_marker.Append(NULL);
}
void BilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi,
Array<int> &bdr_marker)
{
bfbfi.Append(bfi);
bfbfi_marker.Append(&bdr_marker);
boundary_face_integs.Append(bfi);
boundary_face_integs_marker.Append(&bdr_marker);
}
void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
@@ -285,14 +286,14 @@ void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
return;
}
if (dbfi.Size())
if (domain_integs.Size())
{
const FiniteElement &fe = *fes->GetFE(i);
ElementTransformation *eltrans = fes->GetElementTransformation(i);
dbfi[0]->AssembleElementMatrix(fe, *eltrans, elmat);
for (int k = 1; k < dbfi.Size(); k++)
domain_integs[0]->AssembleElementMatrix(fe, *eltrans, elmat);
for (int k = 1; k < domain_integs.Size(); k++)
{
dbfi[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
domain_integs[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
elmat += elemmat;
}
}
@@ -306,14 +307,14 @@ void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
void BilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat)
{
if (bbfi.Size())
if (boundary_integs.Size())
{
const FiniteElement &be = *fes->GetBE(i);
ElementTransformation *eltrans = fes->GetBdrElementTransformation(i);
bbfi[0]->AssembleElementMatrix(be, *eltrans, elmat);
for (int k = 1; k < bbfi.Size(); k++)
boundary_integs[0]->AssembleElementMatrix(be, *eltrans, elmat);
for (int k = 1; k < boundary_integs.Size(); k++)
{
bbfi[k]->AssembleElementMatrix(be, *eltrans, elemmat);
boundary_integs[k]->AssembleElementMatrix(be, *eltrans, elemmat);
elmat += elemmat;
}
}
@@ -407,13 +408,14 @@ void BilinearForm::Assemble(int skip_zeros)
}
#endif
if (dbfi.Size())
if (domain_integs.Size())
{
for (int k = 0; k < dbfi.Size(); k++)
for (int k = 0; k < domain_integs.Size(); k++)
{
if (dbfi_marker[k] != NULL)
if (domain_integs_marker[k] != NULL)
{
MFEM_VERIFY(mesh->attributes.Size() == dbfi_marker[k]->Size(),
MFEM_VERIFY(mesh->attributes.Size() ==
domain_integs_marker[k]->Size(),
"invalid element marker for domain integrator #"
<< k << ", counting from zero");
}
@@ -430,14 +432,14 @@ void BilinearForm::Assemble(int skip_zeros)
else
{
elmat.SetSize(0);
for (int k = 0; k < dbfi.Size(); k++)
for (int k = 0; k < domain_integs.Size(); k++)
{
if ( dbfi_marker[k] == NULL ||
(*(dbfi_marker[k]))[elem_attr-1] == 1)
if ( domain_integs_marker[k] == NULL ||
(*(domain_integs_marker[k]))[elem_attr-1] == 1)
{
const FiniteElement &fe = *fes->GetFE(i);
eltrans = fes->GetElementTransformation(i);
dbfi[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
domain_integs[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
if (elmat.Size() == 0)
{
elmat = elemmat;
@@ -472,20 +474,20 @@ void BilinearForm::Assemble(int skip_zeros)
}
}
if (bbfi.Size())
if (boundary_integs.Size())
{
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < bbfi.Size(); k++)
for (int k = 0; k < boundary_integs.Size(); k++)
{
if (bbfi_marker[k] == NULL)
if (boundary_integs_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *bbfi_marker[k];
Array<int> &bdr_marker = *boundary_integs_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary integrator #"
<< k << ", counting from zero");
@@ -504,21 +506,21 @@ void BilinearForm::Assemble(int skip_zeros)
fes -> GetBdrElementVDofs (i, vdofs);
eltrans = fes -> GetBdrElementTransformation (i);
int k = 0;
for (; k < bbfi.Size(); k++)
for (; k < boundary_integs.Size(); k++)
{
if (bbfi_marker[k] &&
(*bbfi_marker[k])[bdr_attr-1] == 0) { continue; }
if (boundary_integs_marker[k] &&
(*boundary_integs_marker[k])[bdr_attr-1] == 0) { continue; }
bbfi[k]->AssembleElementMatrix(be, *eltrans, elmat);
boundary_integs[k]->AssembleElementMatrix(be, *eltrans, elmat);
k++;
break;
}
for (; k < bbfi.Size(); k++)
for (; k < boundary_integs.Size(); k++)
{
if (bbfi_marker[k] &&
(*bbfi_marker[k])[bdr_attr-1] == 0) { continue; }
if (boundary_integs_marker[k] &&
(*boundary_integs_marker[k])[bdr_attr-1] == 0) { continue; }
bbfi[k]->AssembleElementMatrix(be, *eltrans, elemmat);
boundary_integs[k]->AssembleElementMatrix(be, *eltrans, elemmat);
elmat += elemmat;
}
if (!static_cond)
@@ -536,7 +538,7 @@ void BilinearForm::Assemble(int skip_zeros)
}
}
if (fbfi.Size())
if (interior_face_integs.Size())
{
FaceElementTransformations *tr;
Array<int> vdofs2;
@@ -550,18 +552,19 @@ void BilinearForm::Assemble(int skip_zeros)
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
fes -> GetElementVDofs (tr -> Elem2No, vdofs2);
vdofs.Append (vdofs2);
for (int k = 0; k < fbfi.Size(); k++)
for (int k = 0; k < interior_face_integs.Size(); k++)
{
fbfi[k] -> AssembleFaceMatrix (*fes -> GetFE (tr -> Elem1No),
*fes -> GetFE (tr -> Elem2No),
*tr, elemmat);
interior_face_integs[k]->
AssembleFaceMatrix(*fes->GetFE(tr->Elem1No),
*fes->GetFE(tr->Elem2No),
*tr, elemmat);
mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros);
}
}
}
}
if (bfbfi.Size())
if (boundary_face_integs.Size())
{
FaceElementTransformations *tr;
const FiniteElement *fe1, *fe2;
@@ -570,14 +573,14 @@ void BilinearForm::Assemble(int skip_zeros)
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < bfbfi.Size(); k++)
for (int k = 0; k < boundary_face_integs.Size(); k++)
{
if (bfbfi_marker[k] == NULL)
if (boundary_face_integs_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *bfbfi_marker[k];
Array<int> &bdr_marker = *boundary_face_integs_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary face integrator #"
<< k << ", counting from zero");
@@ -601,12 +604,14 @@ void BilinearForm::Assemble(int skip_zeros)
// but we can't dereference a NULL pointer, and we don't want to
// actually make a fake element.
fe2 = fe1;
for (int k = 0; k < bfbfi.Size(); k++)
for (int k = 0; k < boundary_face_integs.Size(); k++)
{
if (bfbfi_marker[k] &&
(*bfbfi_marker[k])[bdr_attr-1] == 0) { continue; }
if (boundary_face_integs_marker[k] &&
(*boundary_face_integs_marker[k])[bdr_attr-1] == 0)
{ continue; }
bfbfi[k] -> AssembleFaceMatrix (*fe1, *fe2, *tr, elemmat);
boundary_face_integs[k] -> AssembleFaceMatrix (*fe1, *fe2, *tr,
elemmat);
mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros);
}
}
@@ -857,7 +862,7 @@ void BilinearForm::RecoverFEMSolution(const Vector &X,
void BilinearForm::ComputeElementMatrices()
{
if (element_matrices || dbfi.Size() == 0 || fes->GetNE() == 0)
if (element_matrices || domain_integs.Size() == 0 || fes->GetNE() == 0)
{
return;
}
@@ -886,11 +891,11 @@ void BilinearForm::ComputeElementMatrices()
#endif
fes->GetElementTransformation(i, &eltrans);
dbfi[0]->AssembleElementMatrix(fe, eltrans, elmat);
for (int k = 1; k < dbfi.Size(); k++)
domain_integs[0]->AssembleElementMatrix(fe, eltrans, elmat);
for (int k = 1; k < domain_integs.Size(); k++)
{
// note: some integrators may not be thread-safe
dbfi[k]->AssembleElementMatrix(fe, eltrans, tmp);
domain_integs[k]->AssembleElementMatrix(fe, eltrans, tmp);
elmat += tmp;
}
elmat.ClearExternalData();
@@ -1105,10 +1110,12 @@ BilinearForm::~BilinearForm()
if (!extern_bfs)
{
int k;
for (k=0; k < dbfi.Size(); k++) { delete dbfi[k]; }
for (k=0; k < bbfi.Size(); k++) { delete bbfi[k]; }
for (k=0; k < fbfi.Size(); k++) { delete fbfi[k]; }
for (k=0; k < bfbfi.Size(); k++) { delete bfbfi[k]; }
for (k=0; k < domain_integs.Size(); k++) { delete domain_integs[k]; }
for (k=0; k < boundary_integs.Size(); k++) { delete boundary_integs[k]; }
for (k=0; k < interior_face_integs.Size(); k++)
{ delete interior_face_integs[k]; }
for (k=0; k < boundary_face_integs.Size(); k++)
{ delete boundary_face_integs[k]; }
}
delete ext;
@@ -1141,13 +1148,13 @@ MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
ext = NULL;
// Copy the pointers to the integrators
dbfi = mbf->dbfi;
bbfi = mbf->bbfi;
tfbfi = mbf->tfbfi;
btfbfi = mbf->btfbfi;
domain_integs = mbf->domain_integs;
boundary_integs = mbf->boundary_integs;
trace_face_integs = mbf->trace_face_integs;
boundary_trace_face_integs = mbf->boundary_trace_face_integs;
bbfi_marker = mbf->bbfi_marker;
btfbfi_marker = mbf->btfbfi_marker;
boundary_integs_marker = mbf->boundary_integs_marker;
boundary_trace_face_integs_marker = mbf->boundary_trace_face_integs_marker;
assembly = AssemblyLevel::LEGACY;
ext = NULL;
@@ -1236,7 +1243,8 @@ MatrixInverse * MixedBilinearForm::Inverse() const
{
if (assembly != AssemblyLevel::LEGACY)
{
MFEM_WARNING("MixedBilinearForm::Inverse not possible with this assembly level!");
MFEM_WARNING("MixedBilinearForm::Inverse not possible with this "
"assembly level!");
return NULL;
}
else
@@ -1267,38 +1275,39 @@ void MixedBilinearForm::GetBlocks(Array2D<SparseMatrix *> &blocks) const
void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi)
{
dbfi.Append (bfi);
domain_integs.Append (bfi);
}
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi)
{
bbfi.Append (bfi);
bbfi_marker.Append(NULL); // NULL marker means apply everywhere
boundary_integs.Append (bfi);
boundary_integs_marker.Append(NULL); // NULL marker means apply everywhere
}
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi,
Array<int> &bdr_marker)
{
bbfi.Append (bfi);
bbfi_marker.Append(&bdr_marker);
boundary_integs.Append (bfi);
boundary_integs_marker.Append(&bdr_marker);
}
void MixedBilinearForm::AddTraceFaceIntegrator (BilinearFormIntegrator * bfi)
{
tfbfi.Append (bfi);
trace_face_integs.Append (bfi);
}
void MixedBilinearForm::AddBdrTraceFaceIntegrator(BilinearFormIntegrator *bfi)
{
btfbfi.Append(bfi);
btfbfi_marker.Append(NULL); // NULL marker means apply everywhere
boundary_trace_face_integs.Append(bfi);
// NULL marker means apply everywhere
boundary_trace_face_integs_marker.Append(NULL);
}
void MixedBilinearForm::AddBdrTraceFaceIntegrator(BilinearFormIntegrator *bfi,
Array<int> &bdr_marker)
{
btfbfi.Append(bfi);
btfbfi_marker.Append(&bdr_marker);
boundary_trace_face_integs.Append(bfi);
boundary_trace_face_integs_marker.Append(&bdr_marker);
}
void MixedBilinearForm::Assemble (int skip_zeros)
@@ -1320,37 +1329,37 @@ void MixedBilinearForm::Assemble (int skip_zeros)
mat = new SparseMatrix(height, width);
}
if (dbfi.Size())
if (domain_integs.Size())
{
for (int i = 0; i < test_fes -> GetNE(); i++)
{
trial_fes -> GetElementVDofs (i, tr_vdofs);
test_fes -> GetElementVDofs (i, te_vdofs);
eltrans = test_fes -> GetElementTransformation (i);
for (int k = 0; k < dbfi.Size(); k++)
for (int k = 0; k < domain_integs.Size(); k++)
{
dbfi[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i),
*test_fes -> GetFE(i),
*eltrans, elemmat);
domain_integs[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i),
*test_fes -> GetFE(i),
*eltrans, elemmat);
mat -> AddSubMatrix (te_vdofs, tr_vdofs, elemmat, skip_zeros);
}
}
}
if (bbfi.Size())
if (boundary_integs.Size())
{
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < bbfi.Size(); k++)
for (int k = 0; k < boundary_integs.Size(); k++)
{
if (bbfi_marker[k] == NULL)
if (boundary_integs_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *bbfi_marker[k];
Array<int> &bdr_marker = *boundary_integs_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary integrator #"
<< k << ", counting from zero");
@@ -1368,20 +1377,20 @@ void MixedBilinearForm::Assemble (int skip_zeros)
trial_fes -> GetBdrElementVDofs (i, tr_vdofs);
test_fes -> GetBdrElementVDofs (i, te_vdofs);
eltrans = test_fes -> GetBdrElementTransformation (i);
for (int k = 0; k < bbfi.Size(); k++)
for (int k = 0; k < boundary_integs.Size(); k++)
{
if (bbfi_marker[k] &&
(*bbfi_marker[k])[bdr_attr-1] == 0) { continue; }
if (boundary_integs_marker[k] &&
(*boundary_integs_marker[k])[bdr_attr-1] == 0) { continue; }
bbfi[k] -> AssembleElementMatrix2 (*trial_fes -> GetBE(i),
*test_fes -> GetBE(i),
*eltrans, elemmat);
boundary_integs[k]->AssembleElementMatrix2 (*trial_fes -> GetBE(i),
*test_fes -> GetBE(i),
*eltrans, elemmat);
mat -> AddSubMatrix (te_vdofs, tr_vdofs, elemmat, skip_zeros);
}
}
}
if (tfbfi.Size())
if (trace_face_integs.Size())
{
FaceElementTransformations *ftr;
Array<int> te_vdofs2;
@@ -1408,16 +1417,16 @@ void MixedBilinearForm::Assemble (int skip_zeros)
// want to actually make a fake element.
test_fe2 = test_fe1;
}
for (int k = 0; k < tfbfi.Size(); k++)
for (int k = 0; k < trace_face_integs.Size(); k++)
{
tfbfi[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1, *test_fe2,
*ftr, elemmat);
trace_face_integs[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1,
*test_fe2, *ftr, elemmat);
mat->AddSubMatrix(te_vdofs, tr_vdofs, elemmat, skip_zeros);
}
}
}
if (btfbfi.Size())
if (boundary_trace_face_integs.Size())
{
FaceElementTransformations *ftr;
Array<int> te_vdofs2;
@@ -1427,17 +1436,17 @@ void MixedBilinearForm::Assemble (int skip_zeros)
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < btfbfi.Size(); k++)
for (int k = 0; k < boundary_trace_face_integs.Size(); k++)
{
if (btfbfi_marker[k] == NULL)
if (boundary_trace_face_integs_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *btfbfi_marker[k];
Array<int> &bdr_marker = *boundary_trace_face_integs_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary trace face integrator #"
<< k << ", counting from zero");
"invalid boundary marker for boundary trace face"
"integrator #" << k << ", counting from zero");
for (int i = 0; i < bdr_attr_marker.Size(); i++)
{
bdr_attr_marker[i] |= bdr_marker[i];
@@ -1460,13 +1469,16 @@ void MixedBilinearForm::Assemble (int skip_zeros)
// boundaries, but we can't dereference a NULL pointer, and we don't
// want to actually make a fake element.
test_fe2 = test_fe1;
for (int k = 0; k < btfbfi.Size(); k++)
for (int k = 0; k < boundary_trace_face_integs.Size(); k++)
{
if (btfbfi_marker[k] &&
(*btfbfi_marker[k])[bdr_attr-1] == 0) { continue; }
if (boundary_trace_face_integs_marker[k] &&
(*boundary_trace_face_integs_marker[k])[bdr_attr-1] == 0)
{ continue; }
btfbfi[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1, *test_fe2,
*ftr, elemmat);
boundary_trace_face_integs[k]->AssembleFaceMatrix(*trial_face_fe,
*test_fe1,
*test_fe2,
*ftr, elemmat);
mat->AddSubMatrix(te_vdofs, tr_vdofs, elemmat, skip_zeros);
}
}
@@ -1557,15 +1569,17 @@ void MixedBilinearForm::ConformingAssemble()
void MixedBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
{
if (dbfi.Size())
if (domain_integs.Size())
{
const FiniteElement &trial_fe = *trial_fes->GetFE(i);
const FiniteElement &test_fe = *test_fes->GetFE(i);
ElementTransformation *eltrans = test_fes->GetElementTransformation(i);
dbfi[0]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans, elmat);
for (int k = 1; k < dbfi.Size(); k++)
domain_integs[0]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans,
elmat);
for (int k = 1; k < domain_integs.Size(); k++)
{
dbfi[k]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans, elemmat);
domain_integs[k]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans,
elemmat);
elmat += elemmat;
}
}
@@ -1580,15 +1594,17 @@ void MixedBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
void MixedBilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat)
{
if (bbfi.Size())
if (boundary_integs.Size())
{
const FiniteElement &trial_be = *trial_fes->GetBE(i);
const FiniteElement &test_be = *test_fes->GetBE(i);
ElementTransformation *eltrans = test_fes->GetBdrElementTransformation(i);
bbfi[0]->AssembleElementMatrix2(trial_be, test_be, *eltrans, elmat);
for (int k = 1; k < bbfi.Size(); k++)
boundary_integs[0]->AssembleElementMatrix2(trial_be, test_be, *eltrans,
elmat);
for (int k = 1; k < boundary_integs.Size(); k++)
{
bbfi[k]->AssembleElementMatrix2(trial_be, test_be, *eltrans, elemmat);
boundary_integs[k]->AssembleElementMatrix2(trial_be, test_be, *eltrans,
elemmat);
elmat += elemmat;
}
}
@@ -1688,10 +1704,10 @@ void MixedBilinearForm::EliminateTestDofs (const Array<int> &bdr_attr_is_ess)
}
}
void MixedBilinearForm::FormRectangularSystemMatrix(const Array<int>
&trial_tdof_list,
const Array<int> &test_tdof_list,
OperatorHandle &A)
void MixedBilinearForm::FormRectangularSystemMatrix(
const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
OperatorHandle &A)
{
if (ext)
@@ -1729,17 +1745,17 @@ void MixedBilinearForm::FormRectangularSystemMatrix(const Array<int>
A.Reset(mat, false);
}
void MixedBilinearForm::FormRectangularLinearSystem(const Array<int>
&trial_tdof_list,
const Array<int> &test_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A,
Vector &X, Vector &B)
void MixedBilinearForm::FormRectangularLinearSystem(
const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A,
Vector &X, Vector &B)
{
if (ext)
{
ext->FormRectangularLinearSystem(trial_tdof_list, test_tdof_list, x, b, A, X,
B);
ext->FormRectangularLinearSystem(trial_tdof_list, test_tdof_list,
x, b, A, X, B);
return;
}
@@ -1777,10 +1793,13 @@ MixedBilinearForm::~MixedBilinearForm()
if (!extern_bfs)
{
int i;
for (i = 0; i < dbfi.Size(); i++) { delete dbfi[i]; }
for (i = 0; i < bbfi.Size(); i++) { delete bbfi[i]; }
for (i = 0; i < tfbfi.Size(); i++) { delete tfbfi[i]; }
for (i = 0; i < btfbfi.Size(); i++) { delete btfbfi[i]; }
for (i = 0; i < domain_integs.Size(); i++) { delete domain_integs[i]; }
for (i = 0; i < boundary_integs.Size(); i++)
{ delete boundary_integs[i]; }
for (i = 0; i < trace_face_integs.Size(); i++)
{ delete trace_face_integs[i]; }
for (i = 0; i < boundary_trace_face_integs.Size(); i++)
{ delete boundary_trace_face_integs[i]; }
}
delete ext;
}
@@ -1830,7 +1849,7 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
mat = new SparseMatrix(height, width);
}
if (dbfi.Size() > 0)
if (domain_integs.Size() > 0)
{
for (int i = 0; i < test_fes->GetNE(); i++)
{
@@ -1840,17 +1859,19 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
dom_fe = trial_fes->GetFE(i);
ran_fe = test_fes->GetFE(i);
dbfi[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, totelmat);
for (int j = 1; j < dbfi.Size(); j++)
domain_integs[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
totelmat);
for (int j = 1; j < domain_integs.Size(); j++)
{
dbfi[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, elmat);
domain_integs[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
elmat);
totelmat += elmat;
}
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
}
}
if (tfbfi.Size())
if (trace_face_integs.Size())
{
const int nfaces = test_fes->GetMesh()->GetNumFaces();
for (int i = 0; i < nfaces; i++)
@@ -1861,10 +1882,12 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
dom_fe = trial_fes->GetFaceElement(i);
ran_fe = test_fes->GetFaceElement(i);
tfbfi[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, totelmat);
for (int j = 1; j < tfbfi.Size(); j++)
trace_face_integs[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
totelmat);
for (int j = 1; j < trace_face_integs.Size(); j++)
{
tfbfi[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, elmat);
trace_face_integs[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
elmat);
totelmat += elmat;
}
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
+36 -30
View File
@@ -84,28 +84,29 @@ protected:
the BilinearForm. */
long sequence;
/** @brief Indicates the BilinearFormIntegrator%s stored in #dbfi, #bbfi,
#fbfi, and #bfbfi are owned by another BilinearForm. */
/** @brief Indicates the BilinearFormIntegrator%s stored in #domain_integs,
#boundary_integs, #interior_face_integs, and #boundary_face_integs are
owned by another BilinearForm. */
int extern_bfs;
/// Set of Domain Integrators to be applied.
Array<BilinearFormIntegrator*> dbfi;
Array<BilinearFormIntegrator*> domain_integs;
/// Element attribute marker (should be of length mesh->attributes)
/// Includes all by default.
/// 0 - ignore attribute
/// 1 - include attribute
Array<Array<int>*> dbfi_marker;
Array<Array<int>*> domain_integs_marker;
/// Set of Boundary Integrators to be applied.
Array<BilinearFormIntegrator*> bbfi;
Array<Array<int>*> bbfi_marker; ///< Entries are not owned.
Array<BilinearFormIntegrator*> boundary_integs;
Array<Array<int>*> boundary_integs_marker; ///< Entries are not owned.
/// Set of interior face Integrators to be applied.
Array<BilinearFormIntegrator*> fbfi;
Array<BilinearFormIntegrator*> interior_face_integs;
/// Set of boundary face Integrators to be applied.
Array<BilinearFormIntegrator*> bfbfi;
Array<Array<int>*> bfbfi_marker; ///< Entries are not owned.
Array<BilinearFormIntegrator*> boundary_face_integs;
Array<Array<int>*> boundary_face_integs_marker; ///< Entries are not owned.
DenseMatrix elemmat;
Array<int> vdofs;
@@ -231,24 +232,25 @@ public:
void AllocateMatrix() { if (mat == NULL) { AllocMat(); } }
/// Access all the integrators added with AddDomainIntegrator().
Array<BilinearFormIntegrator*> *GetDBFI() { return &dbfi; }
Array<BilinearFormIntegrator*> *GetDBFI() { return &domain_integs; }
/// Access all the integrators added with AddBoundaryIntegrator().
Array<BilinearFormIntegrator*> *GetBBFI() { return &bbfi; }
Array<BilinearFormIntegrator*> *GetBBFI() { return &boundary_integs; }
/** @brief Access all boundary markers added with AddBoundaryIntegrator().
If no marker was specified when the integrator was added, the
corresponding pointer (to Array<int>) will be NULL. */
Array<Array<int>*> *GetBBFI_Marker() { return &bbfi_marker; }
Array<Array<int>*> *GetBBFI_Marker() { return &boundary_integs_marker; }
/// Access all integrators added with AddInteriorFaceIntegrator().
Array<BilinearFormIntegrator*> *GetFBFI() { return &fbfi; }
Array<BilinearFormIntegrator*> *GetFBFI() { return &interior_face_integs; }
/// Access all integrators added with AddBdrFaceIntegrator().
Array<BilinearFormIntegrator*> *GetBFBFI() { return &bfbfi; }
Array<BilinearFormIntegrator*> *GetBFBFI() { return &boundary_face_integs; }
/** @brief Access all boundary markers added with AddBdrFaceIntegrator().
If no marker was specified when the integrator was added, the
corresponding pointer (to Array<int>) will be NULL. */
Array<Array<int>*> *GetBFBFI_Marker() { return &bfbfi_marker; }
Array<Array<int>*> *GetBFBFI_Marker()
{ return &boundary_face_integs_marker; }
/// Returns a reference to: \f$ M_{ij} \f$
const double &operator()(int i, int j) { return (*mat)(i,j); }
@@ -652,23 +654,25 @@ protected:
Partial Assembly (PA), or Matrix Free assembly (MF). */
MixedBilinearFormExtension *ext;
/** @brief Indicates the BilinearFormIntegrator%s stored in #dbfi, #bbfi,
#tfbfi and #btfbfi are owned by another MixedBilinearForm. */
/** @brief Indicates the BilinearFormIntegrator%s stored in #domain_integs,
#boundary_integs, #trace_face_integs and #boundary_trace_face_integs
are owned by another MixedBilinearForm. */
int extern_bfs;
/// Domain integrators.
Array<BilinearFormIntegrator*> dbfi;
Array<BilinearFormIntegrator*> domain_integs;
/// Boundary integrators.
Array<BilinearFormIntegrator*> bbfi;
Array<Array<int>*> bbfi_marker;///< Entries are not owned.
Array<BilinearFormIntegrator*> boundary_integs;
Array<Array<int>*> boundary_integs_marker; ///< Entries are not owned.
/// Trace face (skeleton) integrators.
Array<BilinearFormIntegrator*> tfbfi;
Array<BilinearFormIntegrator*> trace_face_integs;
/// Boundary trace face (skeleton) integrators.
Array<BilinearFormIntegrator*> btfbfi;
Array<Array<int>*> btfbfi_marker;///< Entries are not owned.
Array<BilinearFormIntegrator*> boundary_trace_face_integs;
/// Entries are not owned.
Array<Array<int>*> boundary_trace_face_integs_marker;
DenseMatrix elemmat;
Array<int> trial_vdofs, test_vdofs;
@@ -762,24 +766,26 @@ public:
Array<int> &bdr_marker);
/// Access all integrators added with AddDomainIntegrator().
Array<BilinearFormIntegrator*> *GetDBFI() { return &dbfi; }
Array<BilinearFormIntegrator*> *GetDBFI() { return &domain_integs; }
/// Access all integrators added with AddBoundaryIntegrator().
Array<BilinearFormIntegrator*> *GetBBFI() { return &bbfi; }
Array<BilinearFormIntegrator*> *GetBBFI() { return &boundary_integs; }
/** @brief Access all boundary markers added with AddBoundaryIntegrator().
If no marker was specified when the integrator was added, the
corresponding pointer (to Array<int>) will be NULL. */
Array<Array<int>*> *GetBBFI_Marker() { return &bbfi_marker; }
Array<Array<int>*> *GetBBFI_Marker() { return &boundary_integs_marker; }
/// Access all integrators added with AddTraceFaceIntegrator().
Array<BilinearFormIntegrator*> *GetTFBFI() { return &tfbfi; }
Array<BilinearFormIntegrator*> *GetTFBFI() { return &trace_face_integs; }
/// Access all integrators added with AddBdrTraceFaceIntegrator().
Array<BilinearFormIntegrator*> *GetBTFBFI() { return &btfbfi; }
Array<BilinearFormIntegrator*> *GetBTFBFI()
{ return &boundary_trace_face_integs; }
/** @brief Access all boundary markers added with AddBdrTraceFaceIntegrator().
If no marker was specified when the integrator was added, the
corresponding pointer (to Array<int>) will be NULL. */
Array<Array<int>*> *GetBTFBFI_Marker() { return &btfbfi_marker; }
Array<Array<int>*> *GetBTFBFI_Marker()
{ return &boundary_trace_face_integs_marker; }
/// Sets all sparse values of \f$ M \f$ to @a a.
void operator=(const double a) { *mat = a; }
@@ -1004,7 +1010,7 @@ public:
{ AddTraceFaceIntegrator(di); }
/// Access all interpolators added with AddDomainInterpolator().
Array<BilinearFormIntegrator*> *GetDI() { return &dbfi; }
Array<BilinearFormIntegrator*> *GetDI() { return &domain_integs; }
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
/** This method must be called before assembly. */
+12 -12
View File
@@ -160,7 +160,7 @@ void MFBilinearFormExtension::Mult(const Vector &x, Vector &y) const
{
intFaceIntegrators[i]->AddMultMF(faceIntX, faceIntY);
}
int_face_restrict_lex->MultTranspose(faceIntY, y);
int_face_restrict_lex->AddMultTranspose(faceIntY, y);
}
}
@@ -176,7 +176,7 @@ void MFBilinearFormExtension::Mult(const Vector &x, Vector &y) const
{
bdrFaceIntegrators[i]->AddMultMF(faceBdrX, faceBdrY);
}
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
bdr_face_restrict_lex->AddMultTranspose(faceBdrY, y);
}
}
}
@@ -217,7 +217,7 @@ void MFBilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
{
intFaceIntegrators[i]->AddMultTransposeMF(faceIntX, faceIntY);
}
int_face_restrict_lex->MultTranspose(faceIntY, y);
int_face_restrict_lex->AddMultTranspose(faceIntY, y);
}
}
@@ -233,7 +233,7 @@ void MFBilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
{
bdrFaceIntegrators[i]->AddMultTransposeMF(faceBdrX, faceBdrY);
}
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
bdr_face_restrict_lex->AddMultTranspose(faceBdrY, y);
}
}
}
@@ -417,7 +417,7 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
{
intFaceIntegrators[i]->AddMultPA(faceIntX, faceIntY);
}
int_face_restrict_lex->MultTranspose(faceIntY, y);
int_face_restrict_lex->AddMultTranspose(faceIntY, y);
}
}
@@ -433,7 +433,7 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
{
bdrFaceIntegrators[i]->AddMultPA(faceBdrX, faceBdrY);
}
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
bdr_face_restrict_lex->AddMultTranspose(faceBdrY, y);
}
}
}
@@ -474,7 +474,7 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
{
intFaceIntegrators[i]->AddMultTransposePA(faceIntX, faceIntY);
}
int_face_restrict_lex->MultTranspose(faceIntY, y);
int_face_restrict_lex->AddMultTranspose(faceIntY, y);
}
}
@@ -490,7 +490,7 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
{
bdrFaceIntegrators[i]->AddMultTransposePA(faceBdrX, faceBdrY);
}
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
bdr_face_restrict_lex->AddMultTranspose(faceBdrY, y);
}
}
}
@@ -657,7 +657,7 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
Y(j, 0, f) += res;
});
// Apply the Interior Face Restriction transposed
int_face_restrict_lex->MultTranspose(faceIntY, y);
int_face_restrict_lex->AddMultTranspose(faceIntY, y);
}
}
@@ -688,7 +688,7 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
bdr_face_restrict_lex->AddMultTranspose(faceBdrY, y);
}
}
}
@@ -783,7 +783,7 @@ void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
Y(j, 0, f) += res;
});
// Apply the Interior Face Restriction transposed
int_face_restrict_lex->MultTranspose(faceIntY, y);
int_face_restrict_lex->AddMultTranspose(faceIntY, y);
}
}
@@ -814,7 +814,7 @@ void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
bdr_face_restrict_lex->AddMultTranspose(faceBdrY, y);
}
}
}
+4 -4
View File
@@ -72,8 +72,8 @@ protected:
mutable Vector faceIntX, faceIntY;
mutable Vector faceBdrX, faceBdrY;
const Operator *elem_restrict; // Not owned
const Operator *int_face_restrict_lex; // Not owned
const Operator *bdr_face_restrict_lex; // Not owned
const FaceRestriction *int_face_restrict_lex; // Not owned
const FaceRestriction *bdr_face_restrict_lex; // Not owned
public:
PABilinearFormExtension(BilinearForm*);
@@ -143,8 +143,8 @@ protected:
mutable Vector faceIntX, faceIntY;
mutable Vector faceBdrX, faceBdrY;
const Operator *elem_restrict; // Not owned
const Operator *int_face_restrict_lex; // Not owned
const Operator *bdr_face_restrict_lex; // Not owned
const FaceRestriction *int_face_restrict_lex; // Not owned
const FaceRestriction *bdr_face_restrict_lex; // Not owned
public:
MFBilinearFormExtension(BilinearForm *form);
+29 -2
View File
@@ -175,6 +175,11 @@ void BilinearFormIntegrator::AssembleFaceVector(
elmat.Mult(elfun, elvect);
}
void TransposeIntegrator::SetIntRule(const IntegrationRule *ir)
{
IntRule = ir;
bfi->SetIntRule(ir);
}
void TransposeIntegrator::AssembleElementMatrix (
const FiniteElement &el, ElementTransformation &Trans, DenseMatrix &elmat)
@@ -202,6 +207,12 @@ void TransposeIntegrator::AssembleFaceMatrix (
elmat.Transpose (bfi_elmat);
}
void LumpedIntegrator::SetIntRule(const IntegrationRule *ir)
{
IntRule = ir;
bfi->SetIntRule(ir);
}
void LumpedIntegrator::AssembleElementMatrix (
const FiniteElement &el, ElementTransformation &Trans, DenseMatrix &elmat)
{
@@ -209,6 +220,12 @@ void LumpedIntegrator::AssembleElementMatrix (
elmat.Lump();
}
void InverseIntegrator::SetIntRule(const IntegrationRule *ir)
{
IntRule = ir;
integrator->SetIntRule(ir);
}
void InverseIntegrator::AssembleElementMatrix(
const FiniteElement &el, ElementTransformation &Trans, DenseMatrix &elmat)
{
@@ -216,6 +233,15 @@ void InverseIntegrator::AssembleElementMatrix(
elmat.Invert();
}
void SumIntegrator::SetIntRule(const IntegrationRule *ir)
{
IntRule = ir;
for (int i = 0; i < integrators.Size(); i++)
{
integrators[i]->SetIntRule(ir);
}
}
void SumIntegrator::AssembleElementMatrix(
const FiniteElement &el, ElementTransformation &Trans, DenseMatrix &elmat)
{
@@ -1751,15 +1777,16 @@ void DerivativeIntegrator::AssembleElementMatrix2 (
int dim = trial_fe.GetDim();
int trial_nd = trial_fe.GetDof();
int test_nd = test_fe.GetDof();
int spaceDim = Trans.GetSpaceDim();
int i, l;
double det;
elmat.SetSize (test_nd,trial_nd);
dshape.SetSize (trial_nd,dim);
dshapedxt.SetSize(trial_nd,dim);
dshapedxt.SetSize(trial_nd, spaceDim);
dshapedxi.SetSize(trial_nd);
invdfdx.SetSize(dim);
invdfdx.SetSize(dim, spaceDim);
shape.SetSize (test_nd);
const IntegrationRule *ir = IntRule;
+8
View File
@@ -261,6 +261,8 @@ public:
TransposeIntegrator (BilinearFormIntegrator *bfi_, int own_bfi_ = 1)
{ bfi = bfi_; own_bfi = own_bfi_; }
virtual void SetIntRule(const IntegrationRule *ir);
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
DenseMatrix &elmat);
@@ -328,6 +330,8 @@ public:
LumpedIntegrator (BilinearFormIntegrator *bfi_, int own_bfi_ = 1)
{ bfi = bfi_; own_bfi = own_bfi_; }
virtual void SetIntRule(const IntegrationRule *ir);
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
DenseMatrix &elmat);
@@ -346,6 +350,8 @@ public:
InverseIntegrator(BilinearFormIntegrator *integ, int own_integ = 1)
{ integrator = integ; own_integrator = own_integ; }
virtual void SetIntRule(const IntegrationRule *ir);
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
DenseMatrix &elmat);
@@ -364,6 +370,8 @@ private:
public:
SumIntegrator(int own_integs = 1) { own_integrators = own_integs; }
virtual void SetIntRule(const IntegrationRule *ir);
void AddIntegrator(BilinearFormIntegrator *integ)
{ integrators.Append(integ); }
+1 -1
View File
@@ -143,7 +143,7 @@ Solver *BuildSmootherFromCeed(ConstrainedOperator &op, bool chebyshev)
if (chebyshev)
{
const int cheb_order = 3;
out = new OperatorChebyshevSmoother(&op, t_diag, ess_tdofs, cheb_order);
out = new OperatorChebyshevSmoother(op, t_diag, ess_tdofs, cheb_order);
}
else
{
+58 -8
View File
@@ -17,6 +17,7 @@
#include <cerrno> // errno
#include <sstream>
#include <regex>
#ifndef _WIN32
#include <sys/stat.h> // mkdir
@@ -764,7 +765,8 @@ ParaViewDataCollection::ParaViewDataCollection(const std::string&
: DataCollection(collection_name, mesh_),
levels_of_detail(1),
pv_data_format(VTKFormat::BINARY),
high_order_output(false)
high_order_output(false),
restart_mode(false)
{
#ifdef MFEM_USE_ZLIB
compression = -1; // default zlib compression level, equivalent to 6
@@ -842,17 +844,60 @@ void ParaViewDataCollection::Save()
}
// the directory is created
// create pvd file if needed
// create pvd file if needed. If we are not in restart mode, a new pvd file
// is always created. In restart mode, we keep any previously defined
// timestep values as long as they are less than the currently defined time.
if (myid == 0 && !pvd_stream.is_open())
{
std::string dpath=GenerateCollectionPath();
std::string pvdname=dpath+"/"+GeneratePVDFileName();
pvd_stream.open(pvdname.c_str(),std::ios::out);
// initialize the file
pvd_stream << "<?xml version=\"1.0\"?>\n";
pvd_stream << "<VTKFile type=\"Collection\" version=\"0.1\"";
pvd_stream << " byte_order=\"" << VTKByteOrder() << "\">\n";
pvd_stream << "<Collection>" << std::endl;
std::ifstream pvd_in;
if (restart_mode && (pvd_in.open(pvdname,std::ios::binary),pvd_in.good()))
{
// PVD file exists and restart mode enabled: preserve existing time
// steps less than the current time.
std::fstream::pos_type pos_begin = pvd_in.tellg();
std::fstream::pos_type pos_end = pos_begin;
std::regex regexp("timestep=\"([^[:space:]]+)\".*file=\"Cycle(\\d+)");
std::smatch match;
std::string line;
while (getline(pvd_in,line))
{
if (regex_search(line,match,regexp))
{
MFEM_ASSERT(match.size() == 3, "Unable to parse DataSet");
double tvalue = std::stod(match[1]);
if (tvalue >= GetTime()) { break; }
int cvalue = std::stoi(match[2]);
MFEM_VERIFY(cvalue < GetCycle(), "Cycle " << GetCycle() <<
" is too small for restart mode: trying to overwrite"
" existing data.");
pos_end = pvd_in.tellg();
}
}
size_t count = pos_end - pos_begin;
std::vector<char> buf(count);
pvd_in.clear();
pvd_in.seekg(pos_begin);
pvd_in.read(buf.data(), count);
pvd_in.close();
pvd_stream.open(pvdname.c_str(),std::ios::out);
pvd_stream.write(buf.data(), count);
}
else
{
// initialize new pvd file
pvd_stream.open(pvdname.c_str(),std::ios::out);
// initialize the file
pvd_stream << "<?xml version=\"1.0\"?>\n";
pvd_stream << "<VTKFile type=\"Collection\" version=\"0.1\"";
pvd_stream << " byte_order=\"" << VTKByteOrder() << "\">\n";
pvd_stream << "<Collection>" << std::endl;
}
}
// define the vtu file
@@ -1091,6 +1136,11 @@ void ParaViewDataCollection::SetCompression(bool compression_)
}
}
void ParaViewDataCollection::UseRestartMode(bool restart_mode_)
{
restart_mode = restart_mode_;
}
const char *ParaViewDataCollection::GetDataFormatString() const
{
if (pv_data_format == VTKFormat::ASCII)
+6
View File
@@ -488,6 +488,7 @@ private:
std::fstream pvd_stream;
VTKFormat pv_data_format;
bool high_order_output;
bool restart_mode;
protected:
void SaveDataVTU(std::ostream &out, int ref);
@@ -545,6 +546,11 @@ public:
/// by default). Reading high-order data requires ParaView 5.5 or later.
void SetHighOrderOutput(bool high_order_output_);
/// Enable or disable restart mode. If restart is enabled, new writes will
/// preserve timestep metadata for any solutions prior to the currently
/// defined time.
void UseRestartMode(bool restart_mode_);
/// Load the collection - not implemented in the ParaView writer
virtual void Load(int cycle_ = 0) override;
};
+23 -4
View File
@@ -7948,7 +7948,27 @@ VectorTensorFiniteElement::VectorTensorFiniteElement(const int dims,
p, M, FunctionSpace::Qk),
TensorBasisElement(dims, p, VerifyNodal(cbtype), dmtype),
cbasis1d(poly1d.GetBasis(p, VerifyClosed(cbtype))),
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(obtype))) { }
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(obtype)))
{
MFEM_VERIFY(dims > 1, "Constructor for VectorTensorFiniteElement with both "
"open and closed bases is not valid for 1D elements.");
}
VectorTensorFiniteElement::VectorTensorFiniteElement(const int dims,
const int d,
const int p,
const int obtype,
const int M,
const DofMapType dmtype)
: VectorFiniteElement(dims, GetTensorProductGeometry(dims), d,
p, M, FunctionSpace::Pk),
TensorBasisElement(dims, p, obtype, dmtype),
cbasis1d(poly1d.GetBasis(p, VerifyOpen(obtype))),
obasis1d(poly1d.GetBasis(p, VerifyOpen(obtype)))
{
MFEM_VERIFY(dims == 1, "Constructor for VectorTensorFiniteElement without "
"closed basis is only valid for 1D elements.");
}
H1_SegmentElement::H1_SegmentElement(const int p, const int btype)
: NodalTensorFiniteElement(1, p, VerifyClosed(btype), H1_DOF_MAP)
@@ -13055,9 +13075,8 @@ void ND_TriangleElement::CalcCurlShape(const IntegrationPoint &ip,
const double ND_SegmentElement::tk[1] = { 1. };
ND_SegmentElement::ND_SegmentElement(const int p, const int ob_type)
: VectorFiniteElement(1, Geometry::SEGMENT, p, p - 1,
H_CURL, FunctionSpace::Pk),
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(ob_type))),
: VectorTensorFiniteElement(1, p, p - 1, ob_type, H_CURL,
DofMapType::L2_DOF_MAP),
dof2tk(dof)
{
if (obasis1d.IsIntegratedType()) { is_nodal = false; }
+6 -3
View File
@@ -2239,6 +2239,11 @@ public:
const int cbtype, const int obtype,
const int M, const DofMapType dmtype);
// For 1D elements: there is only an "open basis", no "closed basis"
VectorTensorFiniteElement(const int dims, const int d, const int p,
const int obtype, const int M,
const DofMapType dmtype);
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const;
@@ -3311,11 +3316,9 @@ public:
/// Arbitrary order Nedelec elements in 1D on a segment
class ND_SegmentElement : public VectorFiniteElement
class ND_SegmentElement : public VectorTensorFiniteElement
{
static const double tk[1];
Poly_1D::Basis &obasis1d;
Array<int> dof2tk;
public:
+2 -2
View File
@@ -1225,7 +1225,7 @@ const Operator *FiniteElementSpace::GetElementRestriction(
return L2E_nat.Ptr();
}
const Operator *FiniteElementSpace::GetFaceRestriction(
const FaceRestriction *FiniteElementSpace::GetFaceRestriction(
ElementDofOrdering e_ordering, FaceType type, L2FaceValues mul) const
{
const bool is_dg_space = IsDGSpace();
@@ -1239,7 +1239,7 @@ const Operator *FiniteElementSpace::GetFaceRestriction(
}
else
{
Operator* res;
FaceRestriction *res;
if (is_dg_space)
{
res = new L2FaceRestriction(*this, e_ordering, type, m);
+2 -2
View File
@@ -164,7 +164,7 @@ protected:
+ 8 * (int)std::get<3>(k);
}
};
using map_L2F = std::unordered_map<const key_face,Operator*,key_hash>;
using map_L2F = std::unordered_map<const key_face,FaceRestriction*,key_hash>;
mutable map_L2F L2F;
mutable Array<QuadratureInterpolator*> E2Q_array;
@@ -488,7 +488,7 @@ public:
const Operator *GetElementRestriction(ElementDofOrdering e_ordering) const;
/// Return an Operator that converts L-vectors to E-vectors on each face.
virtual const Operator *GetFaceRestriction(
virtual const FaceRestriction *GetFaceRestriction(
ElementDofOrdering e_ordering, FaceType,
L2FaceValues mul = L2FaceValues::DoubleValued) const;
+9 -4
View File
@@ -34,6 +34,7 @@ IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry)
nx = irx.GetNPoints();
ny = iry.GetNPoints();
SetSize(nx * ny);
SetPointIndices();
for (j = 0; j < ny; j++)
{
@@ -48,8 +49,6 @@ IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry)
ip.weight = ipx.weight * ipy.weight;
}
}
SetPointIndices();
}
IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry,
@@ -59,6 +58,7 @@ IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry,
const int ny = iry.GetNPoints();
const int nz = irz.GetNPoints();
SetSize(nx*ny*nz);
SetPointIndices();
for (int iz = 0; iz < nz; ++iz)
{
@@ -78,8 +78,6 @@ IntegrationRule::IntegrationRule(IntegrationRule &irx, IntegrationRule &iry,
}
}
}
SetPointIndices();
}
const Array<double> &IntegrationRule::GetWeights() const
@@ -125,6 +123,7 @@ void IntegrationRule::GrundmannMollerSimplexRule(int s, int n)
}
np /= f;
SetSize(np);
SetPointIndices();
int pt = 0;
for (int i = 0; i <= s; i++)
@@ -375,6 +374,7 @@ public:
void QuadratureFunctions1D::GaussLegendre(const int np, IntegrationRule* ir)
{
ir->SetSize(np);
ir->SetPointIndices();
switch (np)
{
@@ -477,6 +477,7 @@ void QuadratureFunctions1D::GaussLobatto(const int np, IntegrationRule* ir)
*/
ir->SetSize(np);
ir->SetPointIndices();
if ( np == 1 )
{
ir->IntPoint(0).Set1w(0.5, 1.0);
@@ -576,6 +577,7 @@ void QuadratureFunctions1D::GaussLobatto(const int np, IntegrationRule* ir)
void QuadratureFunctions1D::OpenUniform(const int np, IntegrationRule* ir)
{
ir->SetSize(np);
ir->SetPointIndices();
// The Newton-Cotes quadrature is based on weights that integrate exactly the
// interpolatory polynomial through the equally spaced quadrature points.
@@ -591,6 +593,7 @@ void QuadratureFunctions1D::ClosedUniform(const int np,
IntegrationRule* ir)
{
ir->SetSize(np);
ir->SetPointIndices();
if ( np == 1 ) // allow this case as "closed"
{
ir->IntPoint(0).Set1w(0.5, 1.0);
@@ -608,6 +611,7 @@ void QuadratureFunctions1D::ClosedUniform(const int np,
void QuadratureFunctions1D::OpenHalfUniform(const int np, IntegrationRule* ir)
{
ir->SetSize(np);
ir->SetPointIndices();
// Open half points: the centers of np uniform intervals
for (int i = 0; i < np ; ++i)
@@ -621,6 +625,7 @@ void QuadratureFunctions1D::OpenHalfUniform(const int np, IntegrationRule* ir)
void QuadratureFunctions1D::ClosedGL(const int np, IntegrationRule* ir)
{
ir->SetSize(np);
ir->SetPointIndices();
ir->IntPoint(0).x = 0.0;
ir->IntPoint(np-1).x = 1.0;
+5 -3
View File
@@ -96,9 +96,6 @@ private:
by request with the method GetWeights(). */
mutable Array<double> weights;
/// Sets the indices of each quadrature point on initialization.
void SetPointIndices();
/// Define n-simplex rule (triangle/tetrahedron for n=2/3) of order (2s+1)
void GrundmannMollerSimplexRule(int s, int n = 3);
@@ -227,6 +224,11 @@ public:
}
}
/// Sets the indices of each quadrature point on initialization.
/** Note that most calls to IntegrationRule::SetSize should be paired with a
call to SetPointIndices in order for the indices to be set correctly. */
void SetPointIndices();
/// Tensor product of two 1D integration rules
IntegrationRule(IntegrationRule &irx, IntegrationRule &iry);
+74 -64
View File
@@ -26,14 +26,14 @@ LinearForm::LinearForm(FiniteElementSpace *f, LinearForm *lf)
extern_lfs = 1;
// Copy the pointers to the integrators
dlfi = lf->dlfi;
domain_integs = lf->domain_integs;
dlfi_delta = lf->dlfi_delta;
domain_delta_integs = lf->domain_delta_integs;
blfi = lf->blfi;
boundary_integs = lf->boundary_integs;
flfi = lf->flfi;
flfi_marker = lf->flfi_marker;
boundary_face_integs = lf->boundary_face_integs;
boundary_face_integs_marker = lf->boundary_face_integs_marker;
}
void LinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi)
@@ -42,13 +42,13 @@ void LinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi)
dynamic_cast<DeltaLFIntegrator *>(lfi);
if (!maybe_delta || !maybe_delta->IsDelta())
{
dlfi.Append(lfi);
domain_integs.Append(lfi);
}
else
{
dlfi_delta.Append(maybe_delta);
domain_delta_integs.Append(maybe_delta);
}
dlfi_marker.Append(NULL);
domain_integs_marker.Append(NULL);
}
void LinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi,
@@ -58,44 +58,45 @@ void LinearForm::AddDomainIntegrator(LinearFormIntegrator *lfi,
dynamic_cast<DeltaLFIntegrator *>(lfi);
if (!maybe_delta || !maybe_delta->IsDelta())
{
dlfi.Append(lfi);
domain_integs.Append(lfi);
}
else
{
dlfi_delta.Append(maybe_delta);
domain_delta_integs.Append(maybe_delta);
}
dlfi_marker.Append(&elem_marker);
domain_integs_marker.Append(&elem_marker);
}
void LinearForm::AddBoundaryIntegrator (LinearFormIntegrator * lfi)
{
blfi.Append (lfi);
blfi_marker.Append(NULL); // NULL -> all attributes are active
boundary_integs.Append (lfi);
boundary_integs_marker.Append(NULL); // NULL -> all attributes are active
}
void LinearForm::AddBoundaryIntegrator (LinearFormIntegrator * lfi,
Array<int> &bdr_attr_marker)
{
blfi.Append (lfi);
blfi_marker.Append(&bdr_attr_marker);
boundary_integs.Append (lfi);
boundary_integs_marker.Append(&bdr_attr_marker);
}
void LinearForm::AddBdrFaceIntegrator (LinearFormIntegrator * lfi)
{
flfi.Append(lfi);
flfi_marker.Append(NULL); // NULL -> all attributes are active
boundary_face_integs.Append(lfi);
// NULL -> all attributes are active
boundary_face_integs_marker.Append(NULL);
}
void LinearForm::AddBdrFaceIntegrator(LinearFormIntegrator *lfi,
Array<int> &bdr_attr_marker)
{
flfi.Append(lfi);
flfi_marker.Append(&bdr_attr_marker);
boundary_face_integs.Append(lfi);
boundary_face_integs_marker.Append(&bdr_attr_marker);
}
void LinearForm::AddInteriorFaceIntegrator(LinearFormIntegrator *lfi)
{
iflfi.Append(lfi);
interior_face_integs.Append(lfi);
}
void LinearForm::Assemble()
@@ -112,14 +113,14 @@ void LinearForm::Assemble()
// The first use of AddElementVector() below will move it back to host
// because both 'vdofs' and 'elemvect' are on host.
if (dlfi.Size())
if (domain_integs.Size())
{
for (int k = 0; k < dlfi.Size(); k++)
for (int k = 0; k < domain_integs.Size(); k++)
{
if (dlfi_marker[k] != NULL)
if (domain_integs_marker[k] != NULL)
{
MFEM_VERIFY(fes->GetMesh()->attributes.Size() ==
dlfi_marker[k]->Size(),
domain_integs_marker[k]->Size(),
"invalid element marker for domain linear form "
"integrator #" << k << ", counting from zero");
}
@@ -128,14 +129,15 @@ void LinearForm::Assemble()
for (i = 0; i < fes -> GetNE(); i++)
{
int elem_attr = fes->GetMesh()->GetAttribute(i);
for (int k = 0; k < dlfi.Size(); k++)
for (int k = 0; k < domain_integs.Size(); k++)
{
if ( dlfi_marker[k] == NULL ||
(*(dlfi_marker[k]))[elem_attr-1] == 1 )
if ( domain_integs_marker[k] == NULL ||
(*(domain_integs_marker[k]))[elem_attr-1] == 1 )
{
fes -> GetElementVDofs (i, vdofs);
eltrans = fes -> GetElementTransformation (i);
dlfi[k]->AssembleRHSElementVect(*fes->GetFE(i), *eltrans, elemvect);
domain_integs[k]->AssembleRHSElementVect(*fes->GetFE(i),
*eltrans, elemvect);
AddElementVector (vdofs, elemvect);
}
}
@@ -143,7 +145,7 @@ void LinearForm::Assemble()
}
AssembleDelta();
if (blfi.Size())
if (boundary_integs.Size())
{
Mesh *mesh = fes->GetMesh();
@@ -151,14 +153,14 @@ void LinearForm::Assemble()
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < blfi.Size(); k++)
for (int k = 0; k < boundary_integs.Size(); k++)
{
if (blfi_marker[k] == NULL)
if (boundary_integs_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *blfi_marker[k];
Array<int> &bdr_marker = *boundary_integs_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary integrator #"
<< k << ", counting from zero");
@@ -174,18 +176,19 @@ void LinearForm::Assemble()
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
fes -> GetBdrElementVDofs (i, vdofs);
eltrans = fes -> GetBdrElementTransformation (i);
for (int k=0; k < blfi.Size(); k++)
for (int k=0; k < boundary_integs.Size(); k++)
{
if (blfi_marker[k] &&
(*blfi_marker[k])[bdr_attr-1] == 0) { continue; }
if (boundary_integs_marker[k] &&
(*boundary_integs_marker[k])[bdr_attr-1] == 0) { continue; }
blfi[k]->AssembleRHSElementVect(*fes->GetBE(i), *eltrans, elemvect);
boundary_integs[k]->AssembleRHSElementVect(*fes->GetBE(i),
*eltrans, elemvect);
AddElementVector (vdofs, elemvect);
}
}
}
if (flfi.Size())
if (boundary_face_integs.Size())
{
FaceElementTransformations *tr;
Mesh *mesh = fes->GetMesh();
@@ -194,14 +197,14 @@ void LinearForm::Assemble()
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < flfi.Size(); k++)
for (int k = 0; k < boundary_face_integs.Size(); k++)
{
if (flfi_marker[k] == NULL)
if (boundary_face_integs_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *flfi_marker[k];
Array<int> &bdr_marker = *boundary_face_integs_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary face integrator #"
<< k << ", counting from zero");
@@ -220,24 +223,26 @@ void LinearForm::Assemble()
if (tr != NULL)
{
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
for (int k = 0; k < flfi.Size(); k++)
for (int k = 0; k < boundary_face_integs.Size(); k++)
{
if (flfi_marker[k] &&
(*flfi_marker[k])[bdr_attr-1] == 0) { continue; }
if (boundary_face_integs_marker[k] &&
(*boundary_face_integs_marker[k])[bdr_attr-1] == 0)
{ continue; }
flfi[k] -> AssembleRHSElementVect (*fes->GetFE(tr -> Elem1No),
*tr, elemvect);
boundary_face_integs[k]->
AssembleRHSElementVect(*fes->GetFE(tr->Elem1No),
*tr, elemvect);
AddElementVector (vdofs, elemvect);
}
}
}
}
if (iflfi.Size())
if (interior_face_integs.Size())
{
Mesh *mesh = fes->GetMesh();
for (int k = 0; k < iflfi.Size(); k++)
for (int k = 0; k < interior_face_integs.Size(); k++)
{
for (i = 0; i < mesh->GetNumFaces(); i++)
{
@@ -249,9 +254,10 @@ void LinearForm::Assemble()
Array<int> vdofs2;
fes -> GetElementVDofs (tr -> Elem2No, vdofs2);
vdofs.Append(vdofs2);
iflfi[k] -> AssembleRHSElementVect (*fes->GetFE(tr -> Elem1No),
*fes->GetFE(tr -> Elem2No),
*tr, elemvect);
interior_face_integs[k]->
AssembleRHSElementVect(*fes->GetFE(tr->Elem1No),
*fes->GetFE(tr->Elem2No),
*tr, elemvect);
AddElementVector (vdofs, elemvect);
}
}
@@ -277,40 +283,41 @@ void LinearForm::MakeRef(FiniteElementSpace *f, Vector &v, int v_offset)
void LinearForm::AssembleDelta()
{
if (dlfi_delta.Size() == 0) { return; }
if (domain_delta_integs.Size() == 0) { return; }
if (!HaveDeltaLocations())
{
int sdim = fes->GetMesh()->SpaceDimension();
Vector center;
DenseMatrix centers(sdim, dlfi_delta.Size());
DenseMatrix centers(sdim, domain_delta_integs.Size());
for (int i = 0; i < centers.Width(); i++)
{
centers.GetColumnReference(i, center);
dlfi_delta[i]->GetDeltaCenter(center);
domain_delta_integs[i]->GetDeltaCenter(center);
MFEM_VERIFY(center.Size() == sdim,
"Point dim " << center.Size() <<
" does not match space dim " << sdim);
}
fes->GetMesh()->FindPoints(centers, dlfi_delta_elem_id, dlfi_delta_ip);
fes->GetMesh()->FindPoints(centers, domain_delta_integs_elem_id,
domain_delta_integs_ip);
}
Array<int> vdofs;
Vector elemvect;
for (int i = 0; i < dlfi_delta.Size(); i++)
for (int i = 0; i < domain_delta_integs.Size(); i++)
{
int elem_id = dlfi_delta_elem_id[i];
int elem_id = domain_delta_integs_elem_id[i];
// The delta center may be outside of this sub-domain, or
// (Par)Mesh::FindPoints() failed to find this point:
if (elem_id < 0) { continue; }
const IntegrationPoint &ip = dlfi_delta_ip[i];
const IntegrationPoint &ip = domain_delta_integs_ip[i];
ElementTransformation &Trans = *fes->GetElementTransformation(elem_id);
Trans.SetIntPoint(&ip);
fes->GetElementVDofs(elem_id, vdofs);
dlfi_delta[i]->AssembleDeltaElementVect(*fes->GetFE(elem_id), Trans,
elemvect);
domain_delta_integs[i]->AssembleDeltaElementVect(*fes->GetFE(elem_id),
Trans, elemvect);
AddElementVector(vdofs, elemvect);
}
}
@@ -333,11 +340,14 @@ LinearForm::~LinearForm()
if (!extern_lfs)
{
int k;
for (k=0; k < dlfi_delta.Size(); k++) { delete dlfi_delta[k]; }
for (k=0; k < dlfi.Size(); k++) { delete dlfi[k]; }
for (k=0; k < blfi.Size(); k++) { delete blfi[k]; }
for (k=0; k < flfi.Size(); k++) { delete flfi[k]; }
for (k=0; k < iflfi.Size(); k++) { delete iflfi[k]; }
for (k=0; k < domain_delta_integs.Size(); k++)
{ delete domain_delta_integs[k]; }
for (k=0; k < domain_integs.Size(); k++) { delete domain_integs[k]; }
for (k=0; k < boundary_integs.Size(); k++) { delete boundary_integs[k]; }
for (k=0; k < boundary_face_integs.Size(); k++)
{ delete boundary_face_integs[k]; }
for (k=0; k < interior_face_integs.Size(); k++)
{ delete interior_face_integs[k]; }
}
}
+25 -19
View File
@@ -26,43 +26,46 @@ protected:
/// FE space on which the LinearForm lives. Not owned.
FiniteElementSpace *fes;
/** @brief Indicates the LinearFormIntegrator%s stored in #dlfi, #dlfi_delta,
#blfi, and #flfi are owned by another LinearForm. */
/** @brief Indicates the LinearFormIntegrator%s stored in #domain_integs,
#domain_delta_integs, #boundary_integs, and #boundary_face_integs are
owned by another LinearForm. */
int extern_lfs;
/// Set of Domain Integrators to be applied.
Array<LinearFormIntegrator*> dlfi;
Array<LinearFormIntegrator*> domain_integs;
/// Element attribute marker (should be of length mesh->attributes)
/// Includes all by default.
/// 0 - ignore attribute
/// 1 - include attribute
Array<Array<int>*> dlfi_marker;
Array<Array<int>*> domain_integs_marker;
/// Separate array for integrators with delta function coefficients.
Array<DeltaLFIntegrator*> dlfi_delta;
Array<DeltaLFIntegrator*> domain_delta_integs;
/// Set of Boundary Integrators to be applied.
Array<LinearFormIntegrator*> blfi;
Array<Array<int>*> blfi_marker; ///< Entries are not owned.
Array<LinearFormIntegrator*> boundary_integs;
/// Entries are not owned.
Array<Array<int>*> boundary_integs_marker;
/// Set of Boundary Face Integrators to be applied.
Array<LinearFormIntegrator*> flfi;
Array<Array<int>*> flfi_marker; ///< Entries are not owned.
Array<LinearFormIntegrator*> boundary_face_integs;
Array<Array<int>*> boundary_face_integs_marker; ///< Entries not owned.
/// Set of Internal Face Integrators to be applied.
Array<LinearFormIntegrator*> iflfi;
Array<LinearFormIntegrator*> interior_face_integs;
/// The element ids where the centers of the delta functions lie
Array<int> dlfi_delta_elem_id;
Array<int> domain_delta_integs_elem_id;
/// The reference coordinates where the centers of the delta functions lie
Array<IntegrationPoint> dlfi_delta_ip;
Array<IntegrationPoint> domain_delta_integs_ip;
/// If true, the delta locations are not (re)computed during assembly.
bool HaveDeltaLocations() { return (dlfi_delta_elem_id.Size() != 0); }
bool HaveDeltaLocations()
{ return (domain_delta_integs_elem_id.Size() != 0); }
/// Force (re)computation of delta locations.
void ResetDeltaLocations() { dlfi_delta_elem_id.SetSize(0); }
void ResetDeltaLocations() { domain_delta_integs_elem_id.SetSize(0); }
private:
/// Copy construction is not supported; body is undefined.
@@ -150,22 +153,25 @@ public:
/** @brief Access all integrators added with AddDomainIntegrator() which are
not DeltaLFIntegrator%s or they are DeltaLFIntegrator%s with non-delta
coefficients. */
Array<LinearFormIntegrator*> *GetDLFI() { return &dlfi; }
Array<LinearFormIntegrator*> *GetDLFI() { return &domain_integs; }
/** @brief Access all integrators added with AddDomainIntegrator() which are
DeltaLFIntegrator%s with delta coefficients. */
Array<DeltaLFIntegrator*> *GetDLFI_Delta() { return &dlfi_delta; }
Array<DeltaLFIntegrator*> *GetDLFI_Delta() { return &domain_delta_integs; }
/// Access all integrators added with AddBoundaryIntegrator().
Array<LinearFormIntegrator*> *GetBLFI() { return &blfi; }
Array<LinearFormIntegrator*> *GetBLFI() { return &boundary_integs; }
/// Access all integrators added with AddBdrFaceIntegrator().
Array<LinearFormIntegrator*> *GetFLFI() { return &flfi; }
Array<LinearFormIntegrator*> *GetFLFI() { return &boundary_face_integs; }
/// Access all integrators added with AddInteriorFaceIntegrator().
Array<LinearFormIntegrator*> *GetIFLFI() { return &interior_face_integs; }
/** @brief Access all boundary markers added with AddBdrFaceIntegrator().
If no marker was specified when the integrator was added, the
corresponding pointer (to Array<int>) will be NULL. */
Array<Array<int>*> *GetFLFI_Marker() { return &flfi_marker; }
Array<Array<int>*> *GetFLFI_Marker() { return &boundary_face_integs_marker; }
/// Assembles the linear form i.e. sums over all domain/bdr integrators.
void Assemble();
+2
View File
@@ -979,6 +979,8 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
for (int k = 0; k < bfnfi.Size(); ++k)
{
if (bfnfi_marker[k] &&
(*bfnfi_marker[k])[bdr_attr-1] == 0) { continue; }
bfnfi[k]->AssembleFaceGrad(fe, fe2, *tr, el_x_const, elmats);
for (int l=0; l<fes.Size(); ++l)
{
+2 -2
View File
@@ -40,10 +40,10 @@ protected:
public:
/** @brief Prescribe a fixed IntegrationRule to use (when @a ir != NULL) or
let the integrator choose (when @a ir == NULL). */
void SetIntRule(const IntegrationRule *ir) { IntRule = ir; }
virtual void SetIntRule(const IntegrationRule *ir) { IntRule = ir; }
/// Prescribe a fixed IntegrationRule to use.
void SetIntegrationRule(const IntegrationRule &irule) { IntRule = &irule; }
void SetIntegrationRule(const IntegrationRule &ir) { SetIntRule(&ir); }
/// Set the memory type used for GeometricFactors and other large allocations
/// in PA extensions.
+10 -8
View File
@@ -130,7 +130,7 @@ void ParBilinearForm::ParallelAssemble(OperatorHandle &A, SparseMatrix *A_local)
OperatorHandle dA(A.Type()), Ph(A.Type()), hdA;
if (fbfi.Size() == 0)
if (interior_face_integs.Size() == 0)
{
// construct a parallel block-diagonal matrix 'A' based on 'a'
dA.MakeSquareBlockDiag(pfes->GetComm(), pfes->GlobalVSize(),
@@ -214,11 +214,12 @@ void ParBilinearForm::AssembleSharedFaces(int skip_zeros)
}
}
vdofs_all.Append(vdofs2);
for (int k = 0; k < fbfi.Size(); k++)
for (int k = 0; k < interior_face_integs.Size(); k++)
{
fbfi[k]->AssembleFaceMatrix(*pfes->GetFE(T->Elem1No),
*pfes->GetFaceNbrFE(Elem2NbrNo),
*T, elemmat);
interior_face_integs[k]->
AssembleFaceMatrix(*pfes->GetFE(T->Elem1No),
*pfes->GetFaceNbrFE(Elem2NbrNo),
*T, elemmat);
if (keep_nbr_block)
{
mat->AddSubMatrix(vdofs_all, vdofs_all, elemmat, skip_zeros);
@@ -233,7 +234,7 @@ void ParBilinearForm::AssembleSharedFaces(int skip_zeros)
void ParBilinearForm::Assemble(int skip_zeros)
{
if (fbfi.Size())
if (interior_face_integs.Size())
{
pfes->ExchangeFaceNbrData();
if (!ext && mat == NULL)
@@ -244,7 +245,7 @@ void ParBilinearForm::Assemble(int skip_zeros)
BilinearForm::Assemble(skip_zeros);
if (!ext && fbfi.Size() > 0)
if (!ext && interior_face_integs.Size() > 0)
{
AssembleSharedFaces(skip_zeros);
}
@@ -316,7 +317,8 @@ ParallelEliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
void ParBilinearForm::TrueAddMult(const Vector &x, Vector &y, const double a)
const
{
MFEM_VERIFY(fbfi.Size() == 0, "the case of interior face integrators is not"
MFEM_VERIFY(interior_face_integs.Size() == 0,
"the case of interior face integrators is not"
" implemented");
if (X.ParFESpace() != pfes)
+2 -2
View File
@@ -515,7 +515,7 @@ const FiniteElement *ParFiniteElementSpace::GetFE(int i) const
else { return FiniteElementSpace::GetFE(i); }
}
const Operator *ParFiniteElementSpace::GetFaceRestriction(
const FaceRestriction *ParFiniteElementSpace::GetFaceRestriction(
ElementDofOrdering e_ordering, FaceType type, L2FaceValues mul) const
{
const bool is_dg_space = IsDGSpace();
@@ -529,7 +529,7 @@ const Operator *ParFiniteElementSpace::GetFaceRestriction(
}
else
{
Operator* res;
FaceRestriction *res;
if (is_dg_space)
{
res = new ParL2FaceRestriction(*this, e_ordering, type, m);
+1 -1
View File
@@ -299,7 +299,7 @@ public:
presence of shared faces. Shared faces are treated as interior faces,
the returned operator handles the communication needed to get the
shared face values from other MPI ranks */
virtual const Operator *GetFaceRestriction(
virtual const FaceRestriction *GetFaceRestriction(
ElementDofOrdering e_ordering, FaceType type,
L2FaceValues mul = L2FaceValues::DoubleValued) const;
+7 -6
View File
@@ -47,7 +47,7 @@ void ParLinearForm::Assemble()
{
LinearForm::Assemble();
if (iflfi.Size())
if (interior_face_integs.Size())
{
pfes->ExchangeFaceNbrData();
AssembleSharedFaces();
@@ -59,10 +59,10 @@ void ParLinearForm::AssembleSharedFaces()
Array<int> vdofs;
Vector elemvect;
if (iflfi.Size())
if (interior_face_integs.Size())
{
ParMesh *pmesh = pfes->GetParMesh();
for (int k = 0; k < iflfi.Size(); k++)
for (int k = 0; k < interior_face_integs.Size(); k++)
{
for (int i = 0; i < pmesh->GetNSharedFaces(); i++)
{
@@ -73,9 +73,10 @@ void ParLinearForm::AssembleSharedFaces()
{
int Elem2Nbr = tr->Elem2No - pmesh->GetNE();
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
iflfi[0] -> AssembleRHSElementVect (*fes->GetFE(tr -> Elem1No),
*pfes->GetFaceNbrFE(Elem2Nbr),
*tr, elemvect);
interior_face_integs[k]->
AssembleRHSElementVect(*fes->GetFE(tr->Elem1No),
*pfes->GetFaceNbrFE(Elem2Nbr),
*tr, elemvect);
AddElementVector (vdofs, elemvect);
}
}
+5 -5
View File
@@ -847,7 +847,7 @@ void H1FaceRestriction::Mult(const Vector& x, Vector& y) const
});
}
void H1FaceRestriction::MultTranspose(const Vector& x, Vector& y) const
void H1FaceRestriction::AddMultTranspose(const Vector& x, Vector& y) const
{
// Assumes all elements have the same number of dofs
const int nd = dof;
@@ -856,7 +856,7 @@ void H1FaceRestriction::MultTranspose(const Vector& x, Vector& y) const
auto d_offsets = offsets.Read();
auto d_indices = gather_indices.Read();
auto d_x = Reshape(x.Read(), nd, vd, nf);
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
MFEM_FORALL(i, ndofs,
{
const int offset = d_offsets[i];
@@ -1267,7 +1267,7 @@ void L2FaceRestriction::Mult(const Vector& x, Vector& y) const
}
}
void L2FaceRestriction::MultTranspose(const Vector& x, Vector& y) const
void L2FaceRestriction::AddMultTranspose(const Vector& x, Vector& y) const
{
// Assumes all elements have the same number of dofs
const int nd = dof;
@@ -1280,7 +1280,7 @@ void L2FaceRestriction::MultTranspose(const Vector& x, Vector& y) const
if (m == L2FaceValues::DoubleValued)
{
auto d_x = Reshape(x.Read(), nd, vd, 2, nf);
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
MFEM_FORALL(i, ndofs,
{
const int offset = d_offsets[i];
@@ -1304,7 +1304,7 @@ void L2FaceRestriction::MultTranspose(const Vector& x, Vector& y) const
else
{
auto d_x = Reshape(x.Read(), nd, vd, nf);
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
MFEM_FORALL(i, ndofs,
{
const int offset = d_offsets[i];
+121 -15
View File
@@ -21,10 +21,6 @@ namespace mfem
class FiniteElementSpace;
enum class ElementDofOrdering;
/** An enum type to specify if only e1 value is requested (SingleValued) or both
e1 and e2 (DoubleValued). */
enum class L2FaceValues : bool {SingleValued, DoubleValued};
/// Operator that converts FiniteElementSpace L-vectors to E-vectors.
/** Objects of this type are typically created and owned by FiniteElementSpace
objects, see FiniteElementSpace::GetElementRestriction(). */
@@ -104,10 +100,75 @@ public:
void FillJAndData(const Vector &ea_data, SparseMatrix &mat) const;
};
/// Operator that extracts Face degrees of freedom.
/** An enum type to specify if only e1 value is requested (SingleValued) or both
e1 and e2 (DoubleValued). */
enum class L2FaceValues : bool {SingleValued, DoubleValued};
/** @brief Base class for operators that extracts Face degrees of freedom.
In order to compute quantities on the faces of a mesh, it is often useful to
extract the degrees of freedom on the faces of the elements. This class
provides an interface for such operations.
If the FiniteElementSpace is ordered by Ordering::byVDIM, then the expected
format for the L-vector is (vdim x ndofs), otherwise if Ordering::byNODES
the expected format is (ndofs x vdim), where ndofs is the total number of
degrees of freedom.
Since FiniteElementSpace can either be continuous or discontinuous, the
degrees of freedom on a face can either be single valued or double valued,
this is what we refer to as the multiplicity and is represented by the
L2FaceValues enum type.
The format of the output face E-vector of degrees of freedom is
(face_dofs x vdim x multiplicity x nfaces), where face_dofs is the number of
degrees of freedom on each face, and nfaces the number of faces of the
requested FaceType (see FiniteElementSpace::GetNFbyType).
@note Objects of this type are typically created and owned by
FiniteElementSpace objects, see FiniteElementSpace::GetFaceRestriction(). */
class FaceRestriction : public Operator
{
public:
FaceRestriction(): Operator() { }
FaceRestriction(int h, int w): Operator(h, w) { }
virtual ~FaceRestriction() { }
/** @brief Extract the face degrees of freedom from @a x into @a y.
@param[in] x The L-vector of degrees of freedom.
@param[out] y The degrees of freedom on the face, corresponding to a face
E-vector.
*/
void Mult(const Vector &x, Vector &y) const override = 0;
/** @brief Add the face degrees of freedom @a x to the element degrees of
freedom @a y.
@param[in] x The face degrees of freedom on the face.
@param[in,out] y The L-vector of degrees of freedom to which we add the
face degrees of freedom.
*/
virtual void AddMultTranspose(const Vector &x, Vector &y) const = 0;
/** @brief Set the face degrees of freedom in the element degrees of freedom
@a y to the values given in @a x.
@param[in] x The face degrees of freedom on the face.
@param[in,out] y The L-vector of degrees of freedom to which we add the
face degrees of freedom.
*/
void MultTranspose(const Vector &x, Vector &y) const override
{
y = 0.0;
AddMultTranspose(x, y);
}
};
/// Operator that extracts Face degrees of freedom for H1 FiniteElementSpaces.
/** Objects of this type are typically created and owned by FiniteElementSpace
objects, see FiniteElementSpace::GetFaceRestriction(). */
class H1FaceRestriction : public Operator
class H1FaceRestriction : public FaceRestriction
{
protected:
const FiniteElementSpace &fes;
@@ -122,16 +183,42 @@ protected:
Array<int> gather_indices;
public:
H1FaceRestriction(const FiniteElementSpace&, const ElementDofOrdering,
const FaceType);
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
/** @brief Constructor for a H1FaceRestriction.
@param[in] fes The FiniteElementSpace on which this H1FaceRestriction
operates.
@param[in] ordering The requested output ordering of the
H1FaceRestriction, either Native or Lexicographic.
@param[in] type The requested type of faces on which this operator
extracts the degrees of freedom, either Interior or
Boundary.
*/
H1FaceRestriction(const FiniteElementSpace& fes,
const ElementDofOrdering ordering,
const FaceType type);
/** @brief Extract the face degrees of freedom from @a x into @a y.
@param[in] x The L-vector of degrees of freedom.
@param[out] y The degrees of freedom on the face, corresponding to a face
E-vector.
*/
void Mult(const Vector &x, Vector &y) const override;
/** @brief Add the face degrees of freedom @a x to the element degrees of
freedom @a y.
@param[in] x The face degrees of freedom on the face.
@param[in,out] y The L-vector of degrees of freedom to which we add the
face degrees of freedom.
*/
void AddMultTranspose(const Vector &x, Vector &y) const override;
};
/// Operator that extracts Face degrees of freedom.
/// Operator that extracts Face degrees of freedom on L2 FiniteElementSpaces.
/** Objects of this type are typically created and owned by FiniteElementSpace
objects, see FiniteElementSpace::GetFaceRestriction(). */
class L2FaceRestriction : public Operator
class L2FaceRestriction : public FaceRestriction
{
protected:
const FiniteElementSpace &fes;
@@ -154,19 +241,38 @@ protected:
const L2FaceValues m = L2FaceValues::DoubleValued);
public:
L2FaceRestriction(const FiniteElementSpace&, const ElementDofOrdering,
L2FaceRestriction(const FiniteElementSpace&,
const ElementDofOrdering,
const FaceType,
const L2FaceValues m = L2FaceValues::DoubleValued);
virtual void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
/** @brief Extract the face degrees of freedom from @a x into @a y.
@param[in] x The L-vector of degrees of freedom.
@param[out] y The degrees of freedom on the face, corresponding to a face
E-vector.
*/
void Mult(const Vector &x, Vector &y) const override;
/** @brief Add the face degrees of freedom @a x to the element degrees of
freedom @a y.
@param[in] x The face degrees of freedom on the face.
@param[in,out] y The L-vector of degrees of freedom to which we add the
face degrees of freedom.
*/
void AddMultTranspose(const Vector &x, Vector &y) const override;
/** Fill the I array of SparseMatrix corresponding to the sparsity pattern
given by this L2FaceRestriction. */
virtual void FillI(SparseMatrix &mat, const bool keep_nbr_block = false) const;
/** Fill the J and Data arrays of SparseMatrix corresponding to the sparsity
pattern given by this L2FaceRestriction, and the values of ea_data. */
virtual void FillJAndData(const Vector &ea_data,
SparseMatrix &mat,
const bool keep_nbr_block = false) const;
/// This methods adds the DG face matrices to the element matrices.
void AddFaceMatricesToElementMatrices(Vector &fea_data,
Vector &ea_data) const;
+2
View File
@@ -34,6 +34,7 @@ void TMOP_Combo_QualityMetric::EvalP(const DenseMatrix &Jpt,
DenseMatrix &P) const
{
DenseMatrix Pt(P.Size());
P = 0.0;
for (int i = 0; i < tmop_q_arr.Size(); i++)
{
tmop_q_arr[i]->EvalP(Jpt, Pt);
@@ -50,6 +51,7 @@ void TMOP_Combo_QualityMetric::AssembleH(const DenseMatrix &Jpt,
DenseMatrix At(A.Size());
for (int i = 0; i < tmop_q_arr.Size(); i++)
{
At = 0.0;
tmop_q_arr[i]->AssembleH(Jpt, DS, weight, At);
At *= wt_arr[i];
A += At;
+48
View File
@@ -371,6 +371,8 @@ public:
AddQualityMetric(sh_metric, 1.-gamma_);
AddQualityMetric(sz_metric, gamma_);
}
virtual int Id() const { return 80; }
double GetGamma() const { return gamma; }
virtual ~TMOP_Metric_080() { delete sh_metric; delete sz_metric; }
};
@@ -590,6 +592,52 @@ public:
virtual int Id() const { return 321; }
};
/// 3D barrier Shape+Size (VS) metric (polyconvex).
class TMOP_Metric_332 : public TMOP_Combo_QualityMetric
{
protected:
double gamma;
TMOP_QualityMetric *sh_metric, *sz_metric;
public:
TMOP_Metric_332(double gamma_) : gamma(gamma_),
sh_metric(new TMOP_Metric_302),
sz_metric(new TMOP_Metric_315)
{
// (1-gamma) mu_302 + gamma mu_315
AddQualityMetric(sh_metric, 1.-gamma_);
AddQualityMetric(sz_metric, gamma_);
}
virtual int Id() const { return 332; }
double GetGamma() const { return gamma; }
virtual ~TMOP_Metric_332() { delete sh_metric; delete sz_metric; }
};
/// 3D barrier Shape+Size (VS) metric (polyconvex).
class TMOP_Metric_333 : public TMOP_Combo_QualityMetric
{
protected:
double gamma;
TMOP_QualityMetric *sh_metric, *sz_metric;
public:
TMOP_Metric_333(double gamma_) : gamma(gamma_),
sh_metric(new TMOP_Metric_302),
sz_metric(new TMOP_Metric_316)
{
// (1-gamma) mu_302 + gamma mu_316
AddQualityMetric(sh_metric, 1.-gamma_);
AddQualityMetric(sz_metric, gamma_);
}
virtual int Id() const { return 333; }
double GetGamma() const { return gamma; }
virtual ~TMOP_Metric_333() { delete sh_metric; delete sz_metric; }
};
/// Shifted barrier form of 3D metric 16 (volume, ideal barrier metric), 3D
class TMOP_Metric_352 : public TMOP_QualityMetric
{
+46 -2
View File
@@ -150,9 +150,49 @@ void EvalH_077(const int e, const int qx, const int qy,
}
}
static MFEM_HOST_DEVICE inline
void EvalH_080(const int e, const int qx, const int qy,
const double weight, const double gamma, const double *Jpt,
DeviceTensor<7,double> H)
{
// h_80 = (1-gamma) h_2 + gamma h_77.
constexpr int DIM = 2;
double ddI1[4], ddI1b[4], dI2[4], dI2b[4], ddI2[4];
kernels::InvariantsEvaluator2D ie(Args()
.J(Jpt)
.dI2(dI2)
.ddI1(ddI1)
.ddI1b(ddI1b)
.dI2b(dI2b)
.ddI2(ddI2));
const double I2 = ie.Get_I2(), I2inv_sq = 1.0 / (I2 * I2);
ConstDeviceMatrix di2(ie.Get_dI2(),DIM,DIM);
for (int i = 0; i < DIM; i++)
{
for (int j = 0; j < DIM; j++)
{
ConstDeviceMatrix ddi1b(ie.Get_ddI1b(i,j),DIM,DIM);
ConstDeviceMatrix ddi2(ie.Get_ddI2(i,j),DIM,DIM);
for (int r = 0; r < DIM; r++)
{
for (int c = 0; c < DIM; c++)
{
H(r,c,i,j,qx,qy,e) =
(1.0 - gamma) * 0.5 * weight * ddi1b(r,c) +
gamma * ( weight * 0.5 * (1.0 - I2inv_sq) * ddi2(r,c) +
weight * (I2inv_sq / I2) * di2(r,c) * di2(i,j) );
}
}
}
}
}
MFEM_REGISTER_TMOP_KERNELS(void, SetupGradPA_2D,
const Vector &x_,
const double metric_normal,
const double metric_param,
const int mid,
const int NE,
const Array<double> &w_,
@@ -163,7 +203,7 @@ MFEM_REGISTER_TMOP_KERNELS(void, SetupGradPA_2D,
const int d1d,
const int q1d)
{
MFEM_VERIFY(mid == 1 || mid == 2 || mid == 7 || mid == 77,
MFEM_VERIFY(mid == 1 || mid == 2 || mid == 7 || mid == 77 || mid == 80,
"Metric not yet implemented!");
constexpr int DIM = 2;
@@ -222,6 +262,7 @@ MFEM_REGISTER_TMOP_KERNELS(void, SetupGradPA_2D,
if (mid == 2) { EvalH_002(e,qx,qy,weight,Jpt,H); }
if (mid == 7) { EvalH_007(e,qx,qy,weight,Jpt,H); }
if (mid == 77) { EvalH_077(e,qx,qy,weight,Jpt,H); }
if (mid == 80) { EvalH_080(e,qx,qy,weight,metric_param,Jpt,H); }
} // qx
} // qy
});
@@ -241,7 +282,10 @@ void TMOP_Integrator::AssembleGradPA_2D(const Vector &X) const
const Array<double> &G = PA.maps->G;
Vector &H = PA.H;
MFEM_LAUNCH_TMOP_KERNEL(SetupGradPA_2D,id,X,mn,M,N,W,B,G,J,H);
double mp = 0.0;
if (auto m = dynamic_cast<TMOP_Metric_080 *>(metric)) { mp = m->GetGamma(); }
MFEM_LAUNCH_TMOP_KERNEL(SetupGradPA_2D,id,X,mn,mp,M,N,W,B,G,J,H);
}
} // namespace mfem
+57 -3
View File
@@ -181,8 +181,58 @@ void EvalH_321(const int e, const int qx, const int qy, const int qz,
}
}
// H_332 = (1-gamma) H_302 + gamma H_315
static MFEM_HOST_DEVICE inline
void EvalH_332(const int e, const int qx, const int qy, const int qz,
const double weight, const double gamma,
const double *J, DeviceTensor<8,double> dP)
{
double B[9];
double dI1b[9], ddI1b[9];
double dI2[9], dI2b[9], ddI2[9], ddI2b[9];
double dI3b[9], ddI3b[9];
constexpr int DIM = 3;
kernels::InvariantsEvaluator3D ie(Args()
.J(J).B(B)
.dI1b(dI1b).ddI1b(ddI1b)
.dI2(dI2).dI2b(dI2b).ddI2(ddI2).ddI2b(ddI2b)
.dI3b(dI3b).ddI3b(ddI3b));
double sign_detJ;
const double c1 = weight/9.;
const double I1b = ie.Get_I1b();
const double I2b = ie.Get_I2b();
const double I3b = ie.Get_I3b(sign_detJ);
ConstDeviceMatrix di1b(ie.Get_dI1b(),DIM,DIM);
ConstDeviceMatrix di2b(ie.Get_dI2b(),DIM,DIM);
ConstDeviceMatrix di3b(ie.Get_dI3b(sign_detJ),DIM,DIM);
for (int i = 0; i < DIM; i++)
{
for (int j = 0; j < DIM; j++)
{
ConstDeviceMatrix ddi1b(ie.Get_ddI1b(i,j),DIM,DIM);
ConstDeviceMatrix ddi2b(ie.Get_ddI2b(i,j),DIM,DIM);
ConstDeviceMatrix ddi3b(ie.Get_ddI3b(i,j),DIM,DIM);
for (int r = 0; r < DIM; r++)
{
for (int c = 0; c < DIM; c++)
{
const double dp_302 =
(di2b(r,c)*di1b(i,j) + di1b(r,c)*di2b(i,j))
+ ddi2b(r,c)*I1b
+ ddi1b(r,c)*I2b;
const double dp_315 = 2.0 * weight * (I3b - 1.0) * ddi3b(r,c) +
2.0 * weight * di3b(r,c) * di3b(i,j);
dP(r,c,i,j,qx,qy,qz,e) = (1.0 - gamma) * c1 * dp_302 +
gamma * dp_315;
}
}
}
}
}
MFEM_REGISTER_TMOP_KERNELS(void, SetupGradPA_3D,
const double metric_normal,
const double metric_param,
const int mid,
const Vector &x_,
const int NE,
@@ -194,8 +244,8 @@ MFEM_REGISTER_TMOP_KERNELS(void, SetupGradPA_3D,
const int d1d,
const int q1d)
{
MFEM_VERIFY(mid == 302 || mid == 303 || mid == 315 || mid == 321 ,
"3D metric not yet implemented!");
MFEM_VERIFY(mid == 302 || mid == 303 || mid == 315 ||
mid == 321 || mid == 332, "3D metric not yet implemented!");
constexpr int DIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
@@ -255,6 +305,7 @@ MFEM_REGISTER_TMOP_KERNELS(void, SetupGradPA_3D,
if (mid == 303) { EvalH_303(e,qx,qy,qz,weight,Jpt,H); }
if (mid == 315) { EvalH_315(e,qx,qy,qz,weight,Jpt,H); }
if (mid == 321) { EvalH_321(e,qx,qy,qz,weight,Jpt,H); }
if (mid == 332) { EvalH_332(e,qx,qy,qz,weight,metric_param,Jpt,H); }
} // qx
} // qy
} // qz
@@ -275,7 +326,10 @@ void TMOP_Integrator::AssembleGradPA_3D(const Vector &X) const
const Array<double> &G = PA.maps->G;
Vector &H = PA.H;
MFEM_LAUNCH_TMOP_KERNEL(SetupGradPA_3D,id,mn,M,X,N,W,B,G,J,H);
double mp = 0.0;
if (auto m = dynamic_cast<TMOP_Metric_332 *>(metric)) { mp = m->GetGamma(); }
MFEM_LAUNCH_TMOP_KERNEL(SetupGradPA_3D,id,mn,mp,M,X,N,W,B,G,J,H);
}
} // namespace mfem
+22 -2
View File
@@ -58,8 +58,24 @@ void EvalP_077(const double *Jpt, double *P)
kernels::Set(2,2, 0.5 * (1.0 - 1.0 / (I2 * I2)), ie.Get_dI2(), P);
}
static MFEM_HOST_DEVICE inline
void EvalP_080(const double *Jpt, double gamma, double *P)
{
// p_80 = (1-gamma) p_2 + gamma p_77.
double dI1b[4], dI2[4], dI2b[4];
kernels::InvariantsEvaluator2D ie(Args().J(Jpt).
dI1b(dI1b).dI2(dI2).dI2b(dI2b));
kernels::Set(2,2, (1.0 - gamma) * 1./2., ie.Get_dI1b(), P);
const double I2 = ie.Get_I2();
kernels::Add(2,2, gamma * 0.5 * (1.0 - 1.0 / (I2 * I2)), ie.Get_dI2(), P);
}
MFEM_REGISTER_TMOP_KERNELS(void, AddMultPA_Kernel_2D,
const double metric_normal,
const double metric_param,
const int mid,
const int NE,
const DenseTensor &j_,
@@ -71,7 +87,7 @@ MFEM_REGISTER_TMOP_KERNELS(void, AddMultPA_Kernel_2D,
const int d1d,
const int q1d)
{
MFEM_VERIFY(mid == 1 || mid == 2 || mid == 7 || mid == 77,
MFEM_VERIFY(mid == 1 || mid == 2 || mid == 7 || mid == 77 || mid == 80,
"Metric not yet implemented!");
constexpr int DIM = 2;
@@ -132,6 +148,7 @@ MFEM_REGISTER_TMOP_KERNELS(void, AddMultPA_Kernel_2D,
if (mid == 2) { EvalP_002(Jpt, P); }
if (mid == 7) { EvalP_007(Jpt, P); }
if (mid == 77) { EvalP_077(Jpt, P); }
if (mid == 80) { EvalP_080(Jpt, metric_param, P); }
for (int i = 0; i < 4; i++) { P[i] *= weight; }
// PMatO += DS . P^t += DSh . (Jrt . P^t)
@@ -160,7 +177,10 @@ void TMOP_Integrator::AddMultPA_2D(const Vector &X, Vector &Y) const
const Array<double> &G = PA.maps->G;
const double mn = metric_normal;
MFEM_LAUNCH_TMOP_KERNEL(AddMultPA_Kernel_2D,id,mn,M,N,J,W,B,G,X,Y);
double mp = 0.0;
if (auto m = dynamic_cast<TMOP_Metric_080 *>(metric)) { mp = m->GetGamma(); }
MFEM_LAUNCH_TMOP_KERNEL(AddMultPA_Kernel_2D,id,mn,mp,M,N,J,W,B,G,X,Y);
}
} // namespace mfem
+32 -7
View File
@@ -75,8 +75,29 @@ void EvalP_321(const double *J, double *P)
kernels::Add(3,3, ie.Get_dI1(), P);
}
// P_332 = (1-gamma) P_302 + gamma P_315.
static MFEM_HOST_DEVICE inline
void EvalP_332(const double *J, double gamma, double *P)
{
double B[9];
double dI1b[9], dI2[9], dI2b[9], dI3b[9];
kernels::InvariantsEvaluator3D ie(Args()
.J(J).B(B)
.dI1b(dI1b)
.dI2(dI2).dI2b(dI2b)
.dI3b(dI3b));
const double alpha = (1.0 - gamma) * ie.Get_I1b()/9.;
const double beta = (1.0 - gamma) * ie.Get_I2b()/9.;
kernels::Add(3,3, alpha, ie.Get_dI2b(), beta, ie.Get_dI1b(), P);
double sign_detJ;
const double I3b = ie.Get_I3b(sign_detJ);
kernels::Add(3,3, gamma * 2.0 * (I3b - 1.0), ie.Get_dI3b(sign_detJ), P);
}
MFEM_REGISTER_TMOP_KERNELS(void, AddMultPA_Kernel_3D,
const double metric_normal,
double metric_param,
const int mid,
const int NE,
const DenseTensor &j_,
@@ -88,8 +109,8 @@ MFEM_REGISTER_TMOP_KERNELS(void, AddMultPA_Kernel_3D,
const int d1d,
const int q1d)
{
MFEM_VERIFY(mid == 302 || mid == 303 || mid == 315 || mid == 321 ,
"3D metric not yet implemented!");
MFEM_VERIFY(mid == 302 || mid == 303 || mid == 315 ||
mid == 321 || mid == 332, "3D metric not yet implemented!");
constexpr int DIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
@@ -146,10 +167,11 @@ MFEM_REGISTER_TMOP_KERNELS(void, AddMultPA_Kernel_3D,
// metric->EvalP(Jpt, P);
double P[9];
if (mid == 302) { EvalP_302(Jpt,P); }
if (mid == 303) { EvalP_303(Jpt,P); }
if (mid == 315) { EvalP_315(Jpt,P); }
if (mid == 321) { EvalP_321(Jpt,P); }
if (mid == 302) { EvalP_302(Jpt, P); }
if (mid == 303) { EvalP_303(Jpt, P); }
if (mid == 315) { EvalP_315(Jpt, P); }
if (mid == 321) { EvalP_321(Jpt, P); }
if (mid == 332) { EvalP_332(Jpt, metric_param, P); }
for (int i = 0; i < 9; i++) { P[i] *= weight; }
// Y += DS . P^t += DSh . (Jrt . P^t)
@@ -180,7 +202,10 @@ void TMOP_Integrator::AddMultPA_3D(const Vector &X, Vector &Y) const
const Array<double> &G = PA.maps->G;
const double mn = metric_normal;
MFEM_LAUNCH_TMOP_KERNEL(AddMultPA_Kernel_3D,id,mn,M,N,J,W,B,G,X,Y);
double mp = 0.0;
if (auto m = dynamic_cast<TMOP_Metric_332 *>(metric)) { mp = m->GetGamma(); }
MFEM_LAUNCH_TMOP_KERNEL(AddMultPA_Kernel_3D,id,mn,mp,M,N,J,W,B,G,X,Y);
}
} // namespace mfem
+15 -4
View File
@@ -50,8 +50,15 @@ double EvalW_077(const double *Jpt)
return 0.5*(I2b*I2b + 1./(I2b*I2b) - 2.);
}
static MFEM_HOST_DEVICE inline
double EvalW_080(const double *Jpt, double gamma)
{
return (1.0 - gamma) * EvalW_002(Jpt) + gamma * EvalW_077(Jpt);
}
MFEM_REGISTER_TMOP_KERNELS(double, EnergyPA_2D,
const double metric_normal,
const double metric_param,
const int mid,
const int NE,
const DenseTensor &j_,
@@ -64,7 +71,7 @@ MFEM_REGISTER_TMOP_KERNELS(double, EnergyPA_2D,
const int d1d,
const int q1d)
{
MFEM_VERIFY(mid == 1 || mid == 2 || mid == 7 || mid == 77,
MFEM_VERIFY(mid == 1 || mid == 2 || mid == 7 || mid == 77 || mid == 80,
"2D metric not yet implemented!");
constexpr int DIM = 2;
@@ -125,7 +132,8 @@ MFEM_REGISTER_TMOP_KERNELS(double, EnergyPA_2D,
mid == 1 ? EvalW_001(Jpt) :
mid == 2 ? EvalW_002(Jpt) :
mid == 7 ? EvalW_007(Jpt) :
mid == 77 ? EvalW_077(Jpt) : 0.0;
mid == 77 ? EvalW_077(Jpt) :
mid == 80 ? EvalW_080(Jpt, metric_param) : 0.0;
E(qx,qy,e) = weight * EvalW;
}
@@ -141,7 +149,7 @@ double TMOP_Integrator::GetLocalStateEnergyPA_2D(const Vector &X) const
const int D1D = PA.maps->ndof;
const int Q1D = PA.maps->nqpt;
const int id = (D1D << 4 ) | Q1D;
const double m = metric_normal;
const double mn = metric_normal;
const DenseTensor &J = PA.Jtr;
const Array<double> &W = PA.ir->GetWeights();
const Array<double> &B = PA.maps->B;
@@ -149,7 +157,10 @@ double TMOP_Integrator::GetLocalStateEnergyPA_2D(const Vector &X) const
const Vector &O = PA.O;
Vector &E = PA.E;
MFEM_LAUNCH_TMOP_KERNEL(EnergyPA_2D,id,m,M,N,J,W,B,G,X,O,E);
double mp = 0.0;
if (auto m = dynamic_cast<TMOP_Metric_080 *>(metric)) { mp = m->GetGamma(); }
MFEM_LAUNCH_TMOP_KERNEL(EnergyPA_2D,id,mn,mp,M,N,J,W,B,G,X,O,E);
}
} // namespace mfem
+15 -4
View File
@@ -58,8 +58,15 @@ double EvalW_321(const double *J)
return ie.Get_I1() + ie.Get_I2()/ie.Get_I3() - 6.0;
}
static MFEM_HOST_DEVICE inline
double EvalW_332(const double *J, double gamma)
{
return (1.0 - gamma) * EvalW_302(J) + gamma * EvalW_315(J);
}
MFEM_REGISTER_TMOP_KERNELS(double, EnergyPA_3D,
const double metric_normal,
const double metric_param,
const int mid,
const int NE,
const DenseTensor &j_,
@@ -72,8 +79,8 @@ MFEM_REGISTER_TMOP_KERNELS(double, EnergyPA_3D,
const int d1d,
const int q1d)
{
MFEM_VERIFY(mid == 302 || mid == 303 || mid == 315 || mid == 321 ,
"3D metric not yet implemented!");
MFEM_VERIFY(mid == 302 || mid == 303 || mid == 315 ||
mid == 321 || mid == 332, "3D metric not yet implemented!");
constexpr int DIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
@@ -134,7 +141,8 @@ MFEM_REGISTER_TMOP_KERNELS(double, EnergyPA_3D,
mid == 302 ? EvalW_302(Jpt) :
mid == 303 ? EvalW_303(Jpt) :
mid == 315 ? EvalW_315(Jpt) :
mid == 321 ? EvalW_321(Jpt) : 0.0;
mid == 321 ? EvalW_321(Jpt) :
mid == 332 ? EvalW_332(Jpt, metric_param) : 0.0;
E(qx,qy,qz,e) = weight * EvalW;
}
@@ -159,7 +167,10 @@ double TMOP_Integrator::GetLocalStateEnergyPA_3D(const Vector &X) const
const Vector &O = PA.O;
Vector &E = PA.E;
MFEM_LAUNCH_TMOP_KERNEL(EnergyPA_3D,id,mn,M,N,J,W,B,G,O,X,E);
double mp = 0.0;
if (auto m = dynamic_cast<TMOP_Metric_332 *>(metric)) { mp = m->GetGamma(); }
MFEM_LAUNCH_TMOP_KERNEL(EnergyPA_3D,id,mn,mp,M,N,J,W,B,G,O,X,E);
}
} // namespace mfem
+5 -3
View File
@@ -71,7 +71,7 @@ static const unsigned char b64table[] =
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
};
void DecodeBase64(const char *src, size_t len, std::vector<unsigned char> &buf)
void DecodeBase64(const char *src, size_t len, std::vector<char> &buf)
{
const unsigned char *in = (const unsigned char *)src;
buf.clear();
@@ -79,7 +79,7 @@ void DecodeBase64(const char *src, size_t len, std::vector<unsigned char> &buf)
for (size_t i=0; i<len; ++i) { if (b64table[in[i]] != 255) { ++count; } }
if (count % 4 != 0) { return; }
buf.resize(3*len/4);
unsigned char *out = buf.data();
unsigned char *out = (unsigned char *)buf.data();
count = 0;
int pad = 0;
unsigned char c[4];
@@ -97,8 +97,10 @@ void DecodeBase64(const char *src, size_t len, std::vector<unsigned char> &buf)
count = pad = 0;
}
}
buf.resize(out - buf.data());
buf.resize(out - (unsigned char *)buf.data());
}
size_t NumBase64Chars(size_t nbytes) { return ((4*nbytes/3) + 3) & ~3; }
} // namespace mfem::bin_io
} // namespace mfem
+11 -2
View File
@@ -50,6 +50,7 @@ inline T read(const char *buf)
return value;
}
/// Append the binary representation of @a val to the byte buffer @a vec.
template <typename T>
void AppendBytes(std::vector<char> &vec, const T &val)
{
@@ -57,9 +58,17 @@ void AppendBytes(std::vector<char> &vec, const T &val)
vec.insert(vec.end(), ptr, ptr + sizeof(T));
}
void WriteBase64(std::ostream &out, const void *bytes, size_t length);
/// Given a buffer @a buf of length @a nbytes, encode the data in base-64
/// format, and write the encoded data to the output stream @a out.
void WriteBase64(std::ostream &out, const void *bytes, size_t nbytes);
void DecodeBase64(const char *src, size_t len, std::vector<unsigned char> &buf);
/// Decode @a len base-64 encoded characters in the buffer @a src, and store the
/// resulting decoded data in @a buf. @a buf will be resized as needed.
void DecodeBase64(const char *src, size_t len, std::vector<char> &buf);
/// Return the number of characters needed to encode @a nbytes in base-64. This
/// is equal to 4*nbytes/3, rounded up to the nearest multiple of 4.
size_t NumBase64Chars(size_t nbytes);
} // namespace mfem::bin_io
+2
View File
@@ -17,6 +17,7 @@ list(APPEND SRCS
complex_operator.cpp
constraints.cpp
densemat.cpp
fdsolver.cpp
symmat.cpp
handle.cpp
matrix.cpp
@@ -39,6 +40,7 @@ list(APPEND HDRS
dinvariants.hpp
symmat.hpp
dtensor.hpp
fdsolver.hpp
handle.hpp
invariants.hpp
kernels.hpp
+10 -8
View File
@@ -208,11 +208,14 @@ void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
" \"config_version\": 2, \n"
" \"solver\": { \n"
" \"solver\": \"AMG\", \n"
" \"scope\": \"main\", \n"
" \"smoother\": \"JACOBI_L1\", \n"
" \"presweeps\": 1, \n"
" \"postsweeps\": 1, \n"
" \"interpolator\": \"D2\", \n"
" \"max_iters\": 2, \n"
" \"convergence\": \"ABSOLUTE\", \n"
" \"max_row_sum\" : 0.9, \n"
" \"strength_threshold\" : 0.25, \n"
" \"postsweeps\": 1, \n"
" \"max_iters\": 1, \n"
" \"cycle\": \"V\"";
if (verbose)
{
@@ -239,22 +242,21 @@ void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
" \"solver\": \"AMG\", \n"
" \"smoother\": { \n"
" \"scope\": \"jacobi\", \n"
" \"solver\": \"BLOCK_JACOBI\", \n"
" \"relaxation_factor\": 0.7 \n"
" \"solver\": \"JACOBI_L1\" \n"
" }, \n"
" \"presweeps\": 1, \n"
" \"interpolator\": \"D2\", \n"
" \"max_row_sum\" : 0.9, \n"
" \"strength_threshold\" : 0.25, \n"
" \"max_iters\": 2, \n"
" \"max_iters\": 1, \n"
" \"scope\": \"amg\", \n"
" \"max_levels\": 100, \n"
" \"cycle\": \"V\", \n"
" \"postsweeps\": 1 \n"
" }, \n"
" \"solver\": \"PCG\", \n"
" \"max_iters\": 100, \n"
" \"convergence\": \"RELATIVE_MAX\", \n"
" \"max_iters\": 150, \n"
" \"convergence\": \"RELATIVE_INI_CORE\", \n"
" \"scope\": \"main\", \n"
" \"tolerance\": 1e-12, \n"
" \"monitor_residual\": 1, \n"
+203 -10
View File
@@ -50,6 +50,10 @@ dsyevr_(char *JOBZ, char *RANGE, char *UPLO, int *N, double *A, int *LDA,
double *W, double *Z, int *LDZ, int *ISUPPZ, double *WORK, int *LWORK,
int *IWORK, int *LIWORK, int *INFO);
extern "C" void
dgeev_(const char * jobvl, const char * jobvr, int *n, double * A, int * lda,
double * wr, double * wl, double * vl, int * ldvl, double * vr, int * ldvr,
double * work, int * lwork, int * info);
extern "C" void
dsyev_(char *JOBZ, char *UPLO, int *N, double *A, int *LDA, double *W,
double *WORK, int *LWORK, int *INFO);
extern "C" void
@@ -2859,6 +2863,101 @@ void AddMult_a_VVt(const double a, const Vector &v, DenseMatrix &VVt)
}
}
void KronProd(const DenseMatrix & A, const DenseMatrix & B, DenseMatrix & C)
{
const int ah = A.Height();
const int aw = A.Width();
const int bh = B.Height();
const int bw = B.Width();
C.SetSize(ah*bh,aw*bw);
const double * ad = A.Data();
const double * bd = B.Data();
double * cd = C.Data();
for (int ja = 0; ja<aw; ++ja)
for (int jb = 0; jb<bw; ++jb)
for (int ia = 0; ia<ah; ++ia)
for (int ib = 0; ib<bh; ++ib)
cd[bh*ia + ib + ah*bh*(bw*ja + jb)]
= ad[ia + ja * ah] * bd[ib + jb*bh];
}
void KronMult(const DenseMatrix &A, const DenseMatrix &B, const Vector &r,
Vector & z)
{
const int nA = A.Height();
const int mA = A.Width();
const int nB = B.Height();
const int mB = B.Width();
const int nr = r.Size();
MFEM_VERIFY(nr == mA*mB, "Wrong size of Vector r");
z.SetSize(nA*nB);
DenseMatrix R(r.GetData(),mB,mA);
DenseMatrix X(nB,mA);
DenseMatrix Y(z.GetData(),nB,nA);
Mult(B,R,X);
MultABt(X,A,Y);
}
void KronMult(const DenseMatrix &A, const DenseMatrix &B, const DenseMatrix &R,
DenseMatrix & Z)
{
const int nA = A.Height();
const int nB = B.Height();
const int nR = R.Height();
const int mR = R.Width();
Z.SetSize(nA*nB,mR);
Vector r,z;
double * dataR = R.Data();
for (int i = 0; i<mR; i++)
{
r.SetDataAndSize(&dataR[i*nR],nR);
KronMult(A,B,r,z);
Z.SetCol(i,z);
}
}
void KronMult(const DenseMatrix &A, const DenseMatrix &B, const DenseMatrix &C,
const Vector &r, Vector &z)
{
const int nA = A.Height();
const int mA = A.Width();
const int nB = B.Height();
const int mB = B.Width();
const int nC = C.Height();
const int mC = C.Width();
const int nr = r.Size();
MFEM_VERIFY(nr == mA*mB*mC, "Wrong size of Vector r");
z.SetSize(nA*nB*nC);
double * dataR = r.GetData();
DenseMatrix R(dataR,mC,mA*mB);
DenseMatrix X(nC,mA*mB);
Mult(C,R,X);
X.Transpose();
DenseMatrix Z(z.GetData(),mA*mB,nC);
KronMult(A,B,X,Z);
Z.Transpose();
}
void KronMult(const Array<DenseMatrix *> & A, const Vector & r, Vector & z)
{
int dim = A.Size();
if (dim == 2)
{
KronMult(*A[0],*A[1],r,z);
}
else if (dim == 3)
{
KronMult(*A[0],*A[1],*A[2], r,z);
}
else
{
MFEM_ABORT("KronMult::Wrong dimension");
}
}
bool LUFactors::Factor(int m, double TOL)
{
@@ -3310,23 +3409,105 @@ DenseMatrixInverse::~DenseMatrixInverse()
delete [] lu.ipiv;
}
void KronMult(const DenseMatrixInverse &A, const DenseMatrixInverse &B,
const Vector &r, Vector & z)
{
// A and B are square matrices
z.SetSize(r.Size());
int nA = A.Height();
int nB = B.Height();
DenseMatrix R(r.GetData(),nB,nA);
DenseMatrix X(nB,nA);
B.Mult(R,X);
X.Transpose();
DenseMatrix Y(z.GetData(),nA,nB);
A.Mult(X,Y);
Y.Transpose();
}
DenseMatrixEigensystem::DenseMatrixEigensystem(DenseMatrix &m)
void KronMult(const DenseMatrixInverse &A, const DenseMatrixInverse &B,
const DenseMatrix &R, DenseMatrix & Z)
{
// A and B are square matrices
int nR = R.Height();
int mR = R.Width();
Z.SetSize(nR,mR);
Vector r(nR);
Vector z(nR);
double * dataR = R.GetData();
double * dataZ = Z.GetData();
for (int i = 0; i<mR; i++)
{
r.SetData(&dataR[i*nR]);
z.SetData(&dataZ[i*nR]);
KronMult(A,B,r,z);
}
}
void KronMult(const DenseMatrixInverse &A, const DenseMatrixInverse &B,
const DenseMatrixInverse &C, const Vector &r, Vector & z)
{
// A, B and C are square matrices
int n = r.Size();
z.SetSize(n);
int nA = A.Height();
int nB = B.Height();
int nC = C.Height();
double * dataR = r.GetData();
DenseMatrix R(dataR,nC,nA*nB);
DenseMatrix X(nC,nA*nB);
C.Mult(R,X);
X.Transpose();
DenseMatrix Z(z.GetData(),nC,nA*nB);
KronMult(A,B,X,Z);
Z.Transpose();
}
void KronMult(const Array<DenseMatrixInverse *> & A, const Vector & r,
Vector & z)
{
int dim = A.Size();
if (dim == 2)
{
KronMult(*A[0],*A[1],r,z);
}
else if (dim == 3)
{
KronMult(*A[0],*A[1],*A[2], r,z);
}
else
{
MFEM_ABORT("KronMult::Wrong dimension");
}
}
DenseMatrixEigensystem::DenseMatrixEigensystem(DenseMatrix &m, bool sym_)
: mat(m)
{
n = mat.Width();
EVal.SetSize(n);
EVali.SetSize(n);
EVect.SetSize(n);
ev.SetDataAndSize(NULL, n);
#ifdef MFEM_USE_LAPACK
sym = sym_;
jobz = 'V';
uplo = 'U';
lwork = -1;
double qwork;
dsyev_(&jobz, &uplo, &n, EVect.Data(), &n, EVal.GetData(),
&qwork, &lwork, &info);
if (sym)
{
uplo = 'U';
dsyev_(&jobz, &uplo, &n, EVect.Data(), &n, EVal.GetData(),
&qwork, &lwork, &info);
}
else
{
char jobvl = 'N';
int ldvl = 1;
dgeev_(&jobvl,&jobz,&n, mat.GetData(), &n, EVal.GetData(), EVali.GetData(),
nullptr, &ldvl, EVect.GetData(), &n, &qwork, &lwork, &info);
}
lwork = (int) qwork;
work = new double[lwork];
#endif
@@ -3338,6 +3519,7 @@ DenseMatrixEigensystem::DenseMatrixEigensystem(
n(other.n)
{
#ifdef MFEM_USE_LAPACK
sym = other.sym;
jobz = other.jobz;
uplo = other.uplo;
lwork = other.lwork;
@@ -3356,13 +3538,24 @@ void DenseMatrixEigensystem::Eval()
#endif
#ifdef MFEM_USE_LAPACK
EVect = mat;
dsyev_(&jobz, &uplo, &n, EVect.Data(), &n, EVal.GetData(),
work, &lwork, &info);
if (sym)
{
EVect = mat;
dsyev_(&jobz, &uplo, &n, EVect.Data(), &n, EVal.GetData(),
work, &lwork, &info);
}
else
{
char jobvl = 'N';
int ldvl = 1;
DenseMatrix T = mat; // mat is overwritten by dgeev
dgeev_(&jobvl,&jobz,&n, T.GetData(), &n, EVal.GetData(), EVali.GetData(),
nullptr, &ldvl, EVect.GetData(), &n, work, &lwork, &info);
}
if (info != 0)
{
mfem::err << "DenseMatrixEigensystem::Eval(): DSYEV error code: "
string lpck = (sym) ? "DSYEV" : "DGEEV";
mfem::err << "DenseMatrixEigensystem::Eval(): " << lpck << "error code: "
<< info << endl;
mfem_error();
}
+35 -2
View File
@@ -523,6 +523,22 @@ void AddMult_a_VWt(const double a, const Vector &v, const Vector &w,
/// VVt += a * v v^t
void AddMult_a_VVt(const double a, const Vector &v, DenseMatrix &VVt);
/// C = A ⊗ B
void KronProd(const DenseMatrix & A, const DenseMatrix & B, DenseMatrix & C);
/// z = (A ⊗ B) r = vec(B R A^T), where R := vec^-1 (r)
void KronMult(const DenseMatrix &A, const DenseMatrix &B, const Vector &r,
Vector & z);
/// z = (A ⊗ B) R
void KronMult(const DenseMatrix &A, const DenseMatrix &B, const DenseMatrix &R,
DenseMatrix & Z);
/// z = ( A ⊗ B ⊗ C ) r
void KronMult(const DenseMatrix &A, const DenseMatrix &B, const DenseMatrix &C,
const Vector &r, Vector & z);
void KronMult(const Array<DenseMatrix *> & A, const Vector & r, Vector & z);
/** Class that can compute LU factorization of external data and perform various
operations with the factored data. */
@@ -685,16 +701,33 @@ public:
virtual ~DenseMatrixInverse();
};
/// z = (A^-1 ⊗ B^-1) r = vec(B^-1 R A^-T), where R := vec^-1 (r)
void KronMult(const DenseMatrixInverse &Ainv, const DenseMatrixInverse &Binv,
const Vector &r, Vector & z);
/// z = (A^-1 ⊗ B^-1) R
void KronMult(const DenseMatrixInverse &Ainv, const DenseMatrixInverse &Binv,
const DenseMatrix &R, DenseMatrix & Z);
/// z = ( A^-1 ⊗ B^-1 ⊗ C^-1 ) r
void KronMult(const DenseMatrixInverse &Ainv, const DenseMatrixInverse &Binv,
const DenseMatrixInverse &Cinv, const Vector &r, Vector & z);
void KronMult(const Array<DenseMatrixInverse *> & A, const Vector & r,
Vector & z);
class DenseMatrixEigensystem
{
DenseMatrix &mat;
Vector EVal;
// Possible non zero imaginary part of Eigenvalues
Vector EVali;
DenseMatrix EVect;
Vector ev;
int n;
#ifdef MFEM_USE_LAPACK
bool sym;
double *work;
char jobz, uplo;
int lwork, info;
@@ -702,10 +735,10 @@ class DenseMatrixEigensystem
public:
DenseMatrixEigensystem(DenseMatrix &m);
DenseMatrixEigensystem(DenseMatrix &m, bool sym_ = false);
DenseMatrixEigensystem(const DenseMatrixEigensystem &other);
void Eval();
Vector &Eigenvalues() { return EVal; }
Vector &Eigenvalues(bool imag = false) { return imag ? EVali : EVal; }
DenseMatrix &Eigenvectors() { return EVect; }
double Eigenvalue(int i) { return EVal(i); }
const Vector &Eigenvector(int i)
+143
View File
@@ -0,0 +1,143 @@
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "linalg.hpp"
namespace mfem
{
void KronProdInvDiag(const Vector & a, const Vector & b, Vector & dinv)
{
int n = a.Size(), m = b.Size();
dinv.SetSize(n*m);
for (int j = 0; j<m; j++)
for (int i = 0; i<n; i++)
{
dinv(i*m+j) = 1./(a(i) + b(j));
}
}
void KronProdInvDiag(const Vector & a, const Vector & b,
const Vector & c, Vector & dinv)
{
int n = a.Size(), m = b.Size(), l = c.Size();
dinv.SetSize(n*m*l);
for (int k = 0; k<l; k++)
for (int j = 0; j<m; j++)
for (int i = 0; i<n; i++)
{
dinv(i*m*l+j*l+k) = 1./(a(i) + b(j) + c(k));
}
}
void KronProdInvDiag(const Array<Vector *> & X, Vector & dinv)
{
int dim = X.Size();
if (dim == 1)
{
int n = X[0]->Size();
dinv.SetSize(n);
for (int i = 0; i<n; i++) { dinv(i) = 1./(*X[0])(i); }
}
else if (dim == 2)
{
KronProdInvDiag(*X[0], *X[1], dinv);
}
else if (dim == 3)
{
KronProdInvDiag(*X[0], *X[1], *X[2], dinv);
}
else
{
MFEM_ABORT("KronProdInvDiag::Wrong dimension");
}
}
#ifdef MFEM_USE_LAPACK
FDSolver::FDSolver(const Array<DenseMatrix *> & A,
const Array<DenseMatrix *> & B)
{
MFEM_ASSERT(A.Size() == B.Size(), "DenseFDSolver: Incompatible Dimensions");
dim = A.Size();
int solver_size = 1;
for (int i = 0; i<dim; i++)
{
MFEM_ASSERT(A[i]->Height() == A[i]->Width(),
"DenseFDSolver: Matrix is not square");
MFEM_ASSERT(B[i]->Height() == B[i]->Width(),
"DenseFDSolver: Matrix is not square");
MFEM_ASSERT(A[i]->Height() == B[i]->Height(),
"DenseFDSolver: Matrices A and B have incompatible size");
solver_size *= A[i]->Height();
}
this->height = solver_size;
this->width = solver_size;
if (solver_size) { Setup(A,B); }
}
void FDSolver::Setup(const Array<DenseMatrix *> & A,
const Array<DenseMatrix *> & B)
{
EigSystem.SetSize(dim);
eigv.SetSize(dim);
Array<Vector *> evalues(dim);
SQ.SetSize(dim);
DenseMatrix D;
for (int i = 0; i<dim; i++)
{
DenseMatrixInverse Minv(*B[i]);
Minv.Mult(*A[i],D);
EigSystem[i] = new DenseMatrixEigensystem(D);
EigSystem[i]->Eval();
evalues[i] = &EigSystem[i]->Eigenvalues();
eigv[i] = &EigSystem[i]->Eigenvectors();
DenseMatrixInverse Qinv(*eigv[i]);
DenseMatrix Sdinv;
Minv.GetInverseMatrix(Sdinv);
SQ[i] = new DenseMatrix;
Qinv.Mult(Sdinv,*SQ[i]);
}
KronProdInvDiag(evalues,dinv);
}
void FDSolver::Mult(const Vector & r,Vector & z) const
{
MFEM_ASSERT(height == r.Size(),
"DenseFDSolver::Mult: Inconsistent vector size");
if (r.Size() == 0) { return; }
Vector rtemp;
KronMult(SQ,r,rtemp);
// 2. Diagonal solve;
rtemp *= dinv;
// 3. Modify RHS; z <-- (Q1 x Q2) rtemp
KronMult(eigv,rtemp,z);
}
FDSolver::~FDSolver()
{
if (height)
{
for (int i=0; i<dim; i++)
{
delete SQ[i];
delete EigSystem[i];
}
}
}
#endif // MFEM_USE_LAPACK
} // namespace mfem
+60
View File
@@ -0,0 +1,60 @@
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_FDSOLVER
#define MFEM_FDSOLVER
#include "../config/config.hpp"
#include "densemat.hpp"
namespace mfem
{
/// Computes the inverse diagonal dinv = (a⊗I + I⊗b)^-1
/// where a, b are diagonal matrices and I is the identity of the
/// appropriate size
void KronProdInvDiag(const Vector & a, const Vector & b, Vector & dinv);
/// Computes the inverse diagonal dinv = (a⊗I⊗I + I⊗b⊗I + I⊗I⊗c)^-1
/// where a, b, c are diagonal matrices and I is the identity of the
/// appropriate size
void KronProdInvDiag(const Vector & a, const Vector & b,
const Vector & c, Vector & dinv);
void KronProdInvDiag(const Array<Vector *> & X, Vector & dinv);
#ifdef MFEM_USE_LAPACK
/// In 2D it solves the system (A_0 ⊗ B_1 + B_0 ⊗ A_1) z = r
/// In 3D it solves the system
/// (A_0 ⊗ B_1 ⊗ B_2 + B_0 ⊗ A_1 ⊗ B_2 + B_0 ⊗ B_1 ⊗ A_2) z = r
class FDSolver: public Solver
{
private:
int dim = 2;
Array<DenseMatrixEigensystem *> EigSystem;
Array<DenseMatrix *> eigv; // eigenvectors
Array<DenseMatrix *> SQ;
mutable Vector dinv;
void Setup(const Array<DenseMatrix *> & A, const Array<DenseMatrix *> & B);
public:
FDSolver(const Array<DenseMatrix *> & A, const Array<DenseMatrix *> & B);
virtual void SetOperator(const Operator &op) {}
virtual void Mult(const Vector &r, Vector &z) const;
virtual ~FDSolver();
};
#endif // MFEM_USE_LAPACK
} // mfem name space
#endif // MFEM_FDSOLVER
+8 -1
View File
@@ -516,7 +516,14 @@ public:
/// Initialize all entries with value.
HypreParMatrix &operator=(double value)
{ internal::hypre_ParCSRMatrixSetConstantValues(A, value); return *this; }
{
#if MFEM_HYPRE_VERSION < 22200
internal::hypre_ParCSRMatrixSetConstantValues(A, value);
#else
hypre_ParCSRMatrixSetConstantValues(A, value);
#endif
return *this;
}
/** Perform the operation `*this += B`, assuming that both matrices use the
same row and column partitions and the same col_map_offd arrays, or B has
+2 -2
View File
@@ -1942,8 +1942,8 @@ HYPRE_Int
hypre_ParCSRMatrixSetConstantValues(hypre_ParCSRMatrix *A,
HYPRE_Complex value)
{
hypre_CSRMatrixSetConstantValues(hypre_ParCSRMatrixDiag(A), value);
hypre_CSRMatrixSetConstantValues(hypre_ParCSRMatrixOffd(A), value);
internal::hypre_CSRMatrixSetConstantValues(hypre_ParCSRMatrixDiag(A), value);
internal::hypre_CSRMatrixSetConstantValues(hypre_ParCSRMatrixOffd(A), value);
return 0;
}
+10
View File
@@ -198,6 +198,16 @@ hypre_CSRMatrixSum(hypre_CSRMatrix *A,
HYPRE_Complex beta,
hypre_CSRMatrix *B);
#if MFEM_HYPRE_VERSION >= 22200
/** Provide an overloaded function for code consistency between HYPRE API
versions. */
inline hypre_CSRMatrix *hypre_CSRMatrixAdd(hypre_CSRMatrix *A,
hypre_CSRMatrix *B)
{
return ::hypre_CSRMatrixAdd(1.0, A, 1.0, B);
}
#endif
/** Return a new matrix containing the sum of A and B, assuming that both
matrices use the same row and column partitions. The col_map_offd do not
need to be the same, but a more efficient algorithm is used if that's the
+14 -1
View File
@@ -276,6 +276,20 @@ void Add(const int height, const int width, const TA *Adata, TB *Bdata)
}
}
/** @brief Compute B +=alpha*A, where the matrices A and B are of size
@a height x @a width with data @a Adata and @a Bdata. */
template<typename TA, typename TB>
MFEM_HOST_DEVICE inline
void Add(const int height, const int width,
const double alpha, const TA *Adata, TB *Bdata)
{
const int m = height * width;
for (int i = 0; i < m; i++)
{
Bdata[i] += alpha * Adata[i];
}
}
/** @brief Compute B = alpha*A, where the matrices A and B are of size
@a height x @a width with data @a Adata and @a Bdata. */
template<typename TA, typename TB>
@@ -290,7 +304,6 @@ void Set(const int height, const int width,
}
}
/** @brief Matrix-matrix multiplication: A = B * C, where the matrices A, B and
C are of sizes @a Aheight x @a Awidth, @a Aheight x @a Bwidth and @a Bwidth
x @a Awidth, respectively. */
+1
View File
@@ -31,6 +31,7 @@
#include "invariants.hpp"
#include "constraints.hpp"
#include "auxiliary.hpp"
#include "fdsolver.hpp"
#ifdef MFEM_USE_AMGX
#include "amgxsolver.hpp"
+90 -21
View File
@@ -232,7 +232,7 @@ void OperatorJacobiSmoother::Mult(const Vector &x, Vector &y) const
MFEM_FORALL(i, height, Y[i] += DI[i] * R[i]; );
}
OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
const Vector &d,
const Array<int>& ess_tdofs,
int order_, double max_eig_estimate_)
@@ -242,19 +242,19 @@ OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
max_eig_estimate(max_eig_estimate_),
N(d.Size()),
dinv(N),
diag(d),
diag(&d),
coeffs(order),
ess_tdof_list(ess_tdofs),
ess_tdof_list(&ess_tdofs),
residual(N),
oper(oper_) { Setup(); }
oper(&oper_) { Setup(); }
#ifdef MFEM_USE_MPI
OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
const Vector &d,
const Array<int>& ess_tdofs,
int order_, MPI_Comm comm, int power_iterations, double power_tolerance)
#else
OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
const Vector &d,
const Array<int>& ess_tdofs,
int order_, int power_iterations, double power_tolerance)
@@ -263,13 +263,13 @@ OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
order(order_),
N(d.Size()),
dinv(N),
diag(d),
diag(&d),
coeffs(order),
ess_tdof_list(ess_tdofs),
ess_tdof_list(&ess_tdofs),
residual(N),
oper(oper_)
oper(&oper_)
{
OperatorJacobiSmoother invDiagOperator(diag, ess_tdofs, 1.0);
OperatorJacobiSmoother invDiagOperator(*diag, ess_tdofs, 1.0);
ProductOperator diagPrecond(&invDiagOperator, oper, false, false);
#ifdef MFEM_USE_MPI
@@ -284,16 +284,74 @@ OperatorChebyshevSmoother::OperatorChebyshevSmoother(Operator* oper_,
Setup();
}
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator* oper_,
const Vector &d,
const Array<int>& ess_tdofs,
int order_, double max_eig_estimate_)
: OperatorChebyshevSmoother(*oper_, d, ess_tdofs, order_, max_eig_estimate_) { }
#ifdef MFEM_USE_MPI
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator* oper_,
const Vector &d,
const Array<int>& ess_tdofs,
int order_, MPI_Comm comm, int power_iterations, double power_tolerance)
: OperatorChebyshevSmoother(*oper_, d, ess_tdofs, order_, comm,
power_iterations, power_tolerance) { }
#else
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator* oper_,
const Vector &d,
const Array<int>& ess_tdofs,
int order_, int power_iterations, double power_tolerance)
: OperatorChebyshevSmoother(*oper_, d, ess_tdofs, order_, power_iterations,
power_tolerance) { }
#endif
#ifdef MFEM_USE_MPI
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
const Solver &prec_,
int order_, MPI_Comm comm,
int power_iterations, double power_tolerance)
#else
OperatorChebyshevSmoother::OperatorChebyshevSmoother(const Operator &oper_,
const Solver &prec_,
int order_, int power_iterations, double power_tolerance)
#endif
: Solver(oper_.Height()),
order(order_),
diag(nullptr),
N(oper_.Height()),
coeffs(order),
ess_tdof_list(nullptr),
residual(N),
oper(&oper_),
prec(&prec_)
{
ProductOperator Precond(prec, oper, false, false);
#ifdef MFEM_USE_MPI
PowerMethod powerMethod(comm);
#else
PowerMethod powerMethod;
#endif
Vector ev(oper->Width());
max_eig_estimate = powerMethod.EstimateLargestEigenvalue(Precond, ev,
power_iterations, power_tolerance);
Setup();
}
void OperatorChebyshevSmoother::Setup()
{
// Invert diagonal
residual.UseDevice(true);
auto D = diag.Read();
auto X = dinv.Write();
MFEM_FORALL(i, N, X[i] = 1.0 / D[i]; );
auto I = ess_tdof_list.Read();
MFEM_FORALL(i, ess_tdof_list.Size(), X[I[i]] = 1.0; );
// Invert diagonal
if (diag)
{
auto D = diag->Read();
auto X = dinv.Write();
auto I = ess_tdof_list->Read();
MFEM_FORALL(i, N, X[i] = 1.0 / D[i]; );
MFEM_FORALL(i, ess_tdof_list->Size(), X[I[i]] = 1.0; );
}
// Set up Chebyshev coefficients
// For reference, see e.g., Parallel multigrid smoothing: polynomial versus
// Gauss-Seidel by Adams et al.
@@ -388,11 +446,22 @@ void OperatorChebyshevSmoother::Mult(const Vector& x, Vector &y) const
residual = helperVector;
}
// Scale residual by inverse diagonal
// Scale residual by inverse diagonal or apply the given preconditioner
const int n = N;
auto Dinv = dinv.Read();
auto R = residual.ReadWrite();
MFEM_FORALL(i, n, R[i] *= Dinv[i]; );
if (prec)
{
// No device yet
Vector z(residual.Size()); z = 0.0;
prec->Mult(residual,z);
residual = z;
}
else
{
auto Dinv = dinv.Read();
MFEM_FORALL(i, n, R[i] *= Dinv[i]; );
}
// Add weighted contribution to y
auto Y = y.ReadWrite();
@@ -2533,7 +2602,7 @@ BlockILU::BlockILU(int block_size_,
reordering(reordering_)
{ }
BlockILU::BlockILU(Operator &op,
BlockILU::BlockILU(const Operator &op,
int block_size_,
Reordering reordering_,
int k_fill_)
+41 -6
View File
@@ -208,7 +208,13 @@ public:
the matrix-free setting. The estimated largest eigenvalue of the
diagonally preconditoned operator must be provided via
max_eig_estimate. */
OperatorChebyshevSmoother(Operator* oper_, const Vector &d,
OperatorChebyshevSmoother(const Operator &oper_, const Vector &d,
const Array<int>& ess_tdof_list,
int order, double max_eig_estimate);
/// Deprecated: see pass-by-reference version above
MFEM_DEPRECATED
OperatorChebyshevSmoother(const Operator* oper_, const Vector &d,
const Array<int>& ess_tdof_list,
int order, double max_eig_estimate);
@@ -220,13 +226,41 @@ public:
accuracy of the estimated eigenvalue may be controlled via
power_iterations and power_tolerance. */
#ifdef MFEM_USE_MPI
OperatorChebyshevSmoother(Operator* oper_, const Vector &d,
OperatorChebyshevSmoother(const Operator &oper_, const Vector &d,
const Array<int>& ess_tdof_list,
int order, MPI_Comm comm = MPI_COMM_NULL,
int power_iterations = 10,
double power_tolerance = 1e-8);
/// Deprecated: see pass-by-reference version above
MFEM_DEPRECATED
OperatorChebyshevSmoother(const Operator* oper_, const Vector &d,
const Array<int>& ess_tdof_list,
int order, MPI_Comm comm = MPI_COMM_NULL,
int power_iterations = 10,
double power_tolerance = 1e-8);
/** Chebyshev accelaration for the given preconditioner @a prec.
The largest eigenvalue of the preconditoned operator
is estimated internally via a power method. The
accuracy of the estimated eigenvalue may be controlled via
power_iterations and power_tolerance. */
OperatorChebyshevSmoother(const Operator &oper_, const Solver &prec,
int order, MPI_Comm comm = MPI_COMM_NULL,
int power_iterations = 10,
double power_tolerance = 1e-8);
#else
OperatorChebyshevSmoother(Operator* oper_, const Vector &d,
OperatorChebyshevSmoother(const Operator &oper_, const Vector &d,
const Array<int>& ess_tdof_list,
int order, int power_iterations = 10,
double power_tolerance = 1e-8);
OperatorChebyshevSmoother(const Operator &oper_, const Solver &prec,
int order, int power_iterations = 10,
double power_tolerance = 1e-8);
/// Deprecated: see pass-by-reference version above
MFEM_DEPRECATED
OperatorChebyshevSmoother(const Operator* oper_, const Vector &d,
const Array<int>& ess_tdof_list,
int order, int power_iterations = 10,
double power_tolerance = 1e-8);
@@ -250,12 +284,13 @@ private:
double max_eig_estimate;
const int N;
Vector dinv;
const Vector &diag;
const Vector * diag = nullptr;
Array<double> coeffs;
const Array<int>& ess_tdof_list;
const Array<int> * ess_tdof_list = nullptr;
mutable Vector residual;
mutable Vector helperVector;
const Operator* oper;
const Solver* prec=nullptr;
};
@@ -731,7 +766,7 @@ public:
* case that @a op is a HypreParMatrix, the ILU factorization is performed
* on the diagonal blocks of the parallel decomposition.
*/
BlockILU(Operator &op, int block_size_ = 1,
BlockILU(const Operator &op, int block_size_ = 1,
Reordering reordering_ = Reordering::MINIMUM_DISCARDED_FILL,
int k_fill_ = 0);
+12
View File
@@ -37,6 +37,7 @@ MFEM makefile targets:
make distclean
make style
make tags
make hooks
Examples:
@@ -96,6 +97,8 @@ make style
make tags
Generate a vi or Emacs compatible TAGS file in ${MFEM_DIR}/TAGS. Requires
functional "etags" and "egrep" in the user ${PATH}.
make hooks
Creates symlinks to the hooks in the `.git/hooks` directory.
endef
# Save the MAKEOVERRIDES for cases where we explicitly want to pass the command
@@ -759,6 +762,15 @@ endif
@cd $(MFEM_REAL_DIR) && $(ETAGS_BIN) --class-qualify \
--declarations -o $(MFEM_REAL_DIR)/TAGS $(MFEM_TRACKED_SOURCE)
# Creates symlinks to the hooks in the `.git/hooks` directory. Individual
# hooks can be enabled by manually creating symlinks. Hooks can be customized
# using hard copies (trading off with automated updates).
.PHONY: hooks
hooks:
@cd $(MFEM_DIR)/.git/hooks && \
ln -s ../../config/githooks/pre-commit pre-commit; \
ln -s ../../config/githooks/pre-push pre-push;
# Print the contents of a makefile variable, e.g.: 'make print-MFEM_LIBS'.
print-%:
$(info [ variable name]: $*)
+4 -4
View File
@@ -11478,10 +11478,10 @@ FaceGeometricFactors::FaceGeometricFactors(const Mesh *mesh,
const int NF = fespace->GetNFbyType(type);
const int NQ = ir.GetNPoints();
const Operator *face_restr = fespace->GetFaceRestriction(
ElementDofOrdering::LEXICOGRAPHIC,
type,
L2FaceValues::SingleValued );
const FaceRestriction *face_restr = fespace->GetFaceRestriction(
ElementDofOrdering::LEXICOGRAPHIC,
type,
L2FaceValues::SingleValued );
Vector Fnodes(face_restr->Height());
face_restr->Mult(*nodes, Fnodes);
+64 -35
View File
@@ -691,16 +691,31 @@ struct BufferReader : BufferReaderBase
BufferReader(bool compressed_, HeaderType header_type_)
: compressed(compressed_), header_type(header_type_) { }
/// Return the number of bytes in the header. The header consists of one
/// integer if the data is uncompressed, and four integers if the data is
/// compressed. The integers are either 32 or 64 bytes depending on the value
/// of @a header_type.
int NumHeaderBytes() const
/// Return the number of bytes of each header entry.
size_t HeaderEntrySize() const
{
int num_entries = compressed ? 4 : 1;
int entry_size = (header_type == UINT64_HEADER)
? sizeof(uint64_t) : sizeof(uint32_t);
return num_entries*entry_size;
return header_type == UINT64_HEADER ? sizeof(uint64_t) : sizeof(uint32_t);
}
/// Return the value of the header entry pointer to by @a header_buf. The
/// value is stored as either uint32_t or uint64_t, according to the @a
/// header_type, and is returned as uint64_t.
uint64_t ReadHeaderEntry(const char *header_buf) const
{
return (header_type == UINT64_HEADER) ? bin_io::read<uint64_t>(header_buf)
: bin_io::read<uint32_t>(header_buf);
}
/// Return the number of bytes in the header. The header consists of one
/// integer if the data is uncompressed, and @a N + 3 integers if the data is
/// compressed, where @a N is the number of blocks. The integers are either
/// 32 or 64 bytes depending on the value of @a header_type. The number of
/// blocks is determined by reading the first integer (of type @a
/// header_type) pointed to by @a header_buf.
int NumHeaderBytes(const char *header_buf) const
{
if (!compressed) { return HeaderEntrySize(); }
return (3 + ReadHeaderEntry(header_buf))*HeaderEntrySize();
}
/// Read @a n elements of type @a F from the source buffer @a buf into the
@@ -711,32 +726,42 @@ struct BufferReader : BufferReaderBase
void ReadBinaryWithHeader(const char *header_buf, const char *buf,
void *dest_void, int n) const
{
std::vector<unsigned char> uncompressed_data;
std::vector<char> uncompressed_data;
T *dest = static_cast<T*>(dest_void);
if (compressed)
{
#ifdef MFEM_USE_ZLIB
uint64_t header[4];
if (header_type == UINT32_HEADER)
// The header has format (where header_t is uint32_t or uint64_t):
// header_t number_of_blocks;
// header_t uncompressed_block_size;
// header_t uncompressed_last_block_size;
// header_t compressed_size[number_of_blocks];
int header_entry_size = HeaderEntrySize();
int nblocks = ReadHeaderEntry(header_buf);
header_buf += header_entry_size;
std::vector<int> header(nblocks + 2);
for (int i=0; i<nblocks+2; ++i)
{
uint32_t *header_32 = (uint32_t *)header_buf;
for (int i=0; i<4; ++i) { header[i] = header_32[i]; }
header[i] = ReadHeaderEntry(header_buf);
header_buf += header_entry_size;
}
else
uncompressed_data.resize((nblocks-1)*header[0] + header[1]);
Bytef *dest_ptr = (Bytef *)uncompressed_data.data();
Bytef *dest_start = dest_ptr;
const Bytef *source_ptr = (const Bytef *)buf;
for (int i=0; i<nblocks; ++i)
{
uint64_t *header_64 = (uint64_t *)header_buf;
for (int i=0; i<4; ++i) { header[i] = header_64[i]; }
uLongf source_len = header[i+2];
uLong dest_len = (i == nblocks-1) ? header[1] : header[0];
int res = uncompress(dest_ptr, &dest_len, source_ptr, source_len);
MFEM_VERIFY(res == Z_OK, "Error uncompressing");
dest_ptr += dest_len;
source_ptr += source_len;
}
MFEM_VERIFY(header[0] == 1, "Multiple compressed blocks not supported");
uLongf dest_len = header[1];
uncompressed_data.resize(dest_len);
int res = uncompress(uncompressed_data.data(), &dest_len,
(const Bytef *)buf, header[3]);
MFEM_VERIFY(res == Z_OK, "Error uncompressing");
MFEM_VERIFY(sizeof(F)*n == dest_len, "AppendedData: wrong data size");
buf = (const char *)uncompressed_data.data();
MFEM_VERIFY(int(sizeof(F)*n) == (dest_ptr - dest_start),
"AppendedData: wrong data size");
buf = uncompressed_data.data();
#else
MFEM_ABORT("MFEM must be compiled with zlib enabled to uncompress.")
#endif
@@ -779,7 +804,7 @@ struct BufferReader : BufferReaderBase
/// buffer @a dest. The input buffer contains both the header and the data.
void ReadBinary(const char *buf, void *dest, int n) const override
{
ReadBinaryWithHeader(buf, buf + NumHeaderBytes(), dest, n);
ReadBinaryWithHeader(buf, buf + NumHeaderBytes(buf), dest, n);
}
/// Read @a n elements of type @a F from base-64 encoded source buffer into
@@ -796,21 +821,25 @@ struct BufferReader : BufferReaderBase
}
if (compressed)
{
std::vector<unsigned char> data, header;
// Decode the first entry of the header, which we need to determine
// how long the rest of the header is.
std::vector<char> nblocks_buf;
int nblocks_b64 = bin_io::NumBase64Chars(HeaderEntrySize());
bin_io::DecodeBase64(txt, nblocks_b64, nblocks_buf);
std::vector<char> data, header;
// Compute number of characters needed to encode header in base 64,
// then round to nearest multiple of 4 to take padding into account.
int b64_header = ((4*NumHeaderBytes()/3) + 3) & ~3;
int header_b64 = bin_io::NumBase64Chars(NumHeaderBytes(nblocks_buf.data()));
// If data is compressed, header is encoded separately
bin_io::DecodeBase64(txt, b64_header, header);
bin_io::DecodeBase64(txt + b64_header, strlen(txt)-b64_header, data);
ReadBinaryWithHeader((const char *)header.data(),
(const char *)data.data(), dest, n);
bin_io::DecodeBase64(txt, header_b64, header);
bin_io::DecodeBase64(txt + header_b64, strlen(txt)-header_b64, data);
ReadBinaryWithHeader(header.data(), data.data(), dest, n);
}
else
{
std::vector<unsigned char> data;
std::vector<char> data;
bin_io::DecodeBase64(txt, strlen(txt), data);
ReadBinary((const char *)data.data(), dest, n);
ReadBinary(data.data(), dest, n);
}
}
};
+20 -2
View File
@@ -4321,7 +4321,7 @@ const CoarseFineTransformations& NCMesh::GetRefinementTransforms()
if (!transforms.embeddings.Size())
{
transforms.Clear();
transforms.embeddings.SetSize(leaf_elements.Size());
transforms.embeddings.SetSize(NElements);
std::string ref_path;
ref_path.reserve(100);
@@ -4455,7 +4455,8 @@ struct RefType
void CoarseFineTransformations::GetCoarseToFineMap(
const mfem::Mesh &fine_mesh, Table &coarse_to_fine,
Array<int> &coarse_to_ref_type, Table &ref_type_to_matrix,
Array<mfem::Geometry::Type> &ref_type_to_geom) const
Array<mfem::Geometry::Type> &ref_type_to_geom,
bool get_coarse_to_fine_only) const
{
const int fine_ne = embeddings.Size();
int coarse_ne = -1;
@@ -4495,6 +4496,11 @@ void CoarseFineTransformations::GetCoarseToFineMap(
coarse_to_fine.GetJ()[i] = cf_j[i].two;
}
if (get_coarse_to_fine_only) { return; }
MFEM_VERIFY(fine_mesh.GetLastOperation() != Mesh::Operation::DEREFINE,
"GetCoarseToFineMap is not fully supported for derefined meshes."
" Set 'get_coarse_to_fine_only=true'.")
using internal::RefType;
using std::map;
using std::pair;
@@ -4536,6 +4542,18 @@ void CoarseFineTransformations::GetCoarseToFineMap(
ref_type_to_matrix.ShiftUpI();
}
void CoarseFineTransformations::GetCoarseToFineMap(const Mesh &fine_mesh,
Table &coarse_to_fine) const
{
Array<int> coarse_to_ref_type;
Table ref_type_to_matrix;
Array<mfem::Geometry::Type> ref_type_to_geom;
bool get_coarse_to_fine_only = true;
GetCoarseToFineMap(fine_mesh, coarse_to_fine, coarse_to_ref_type,
ref_type_to_matrix, ref_type_to_geom,
get_coarse_to_fine_only);
}
void NCMesh::ClearTransforms()
{
coarse_elements.DeleteAll();
+5 -1
View File
@@ -68,7 +68,11 @@ struct CoarseFineTransformations
Table &coarse_to_fine,
Array<int> &coarse_to_ref_type,
Table &ref_type_to_matrix,
Array<Geometry::Type> &ref_type_to_geom) const;
Array<Geometry::Type> &ref_type_to_geom,
bool get_coarse_to_fine_only = false) const;
void GetCoarseToFineMap(const Mesh &fine_mesh,
Table &coarse_to_fine) const;
void Clear();
bool IsInitialized() const;
-3
View File
@@ -297,9 +297,6 @@ protected: // implementation
virtual void Update();
virtual int GetNumGhostElements() const { return NGhostElements; }
virtual int GetNumGhostVertices() const { return NGhostVertices; }
/// Return the processor number for a global element number.
int Partition(long index, long total_elements) const
{ return index * NRanks / total_elements; }
+7 -3
View File
@@ -109,15 +109,19 @@ int main(int argc, char *argv[])
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
args.PrintOptions(cout);
if (myid == 0) { args.PrintOptions(cout); }
// Enable hardware devices such as GPUs, and programming models such as CUDA,
// OCCA, RAJA and OpenMP based on command line options.
Device device("cpu");
device.Print();
if (myid == 0) { device.Print(); }
// Refine the mesh.
Mesh mesh(mesh_file, 1, 1);
+2 -2
View File
@@ -96,7 +96,7 @@ SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
const SparseMatrix& agg_elem,
const SparseMatrix& elem_dof,
const HypreParMatrix& dof_truedof,
Array<int>& agg_starts)
Array<HYPRE_Int>& agg_starts)
{
OperatorPtr agg_dof(Mult(agg_elem, elem_dof));
SparseMatrix& agg_dof_ref = *agg_dof.As<SparseMatrix>();
@@ -131,7 +131,7 @@ SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
void DFSSpaces::MakeDofRelationTables(int level)
{
Array<int> agg_starts(Array<int>(l2_0_fes_->GetDofOffsets(), 2));
Array<HYPRE_Int> agg_starts(Array<HYPRE_Int>(l2_0_fes_->GetDofOffsets(), 2));
auto& elem_agg = (const SparseMatrix&)*l2_0_fes_->GetUpdateOperator();
OperatorPtr agg_elem(Transpose(elem_agg));
SparseMatrix& agg_el = *agg_elem.As<SparseMatrix>();
+1
View File
@@ -110,6 +110,7 @@ then
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
cp ${hostconfig_path} ${project_dir}/config/
ln -sf ${build_root}/data ${project_dir}/../
make all -j ${threads}
fi
+1
View File
@@ -28,6 +28,7 @@ set(UNIT_TESTS_SRCS
linalg/test_complex_operator.cpp
linalg/test_constrainedsolver.cpp
linalg/test_direct_solvers.cpp
linalg/test_fdsolver.cpp
linalg/test_hypre_ilu.cpp
linalg/test_ilu.cpp
linalg/test_matrix_block.cpp
+4 -38
View File
@@ -11,48 +11,14 @@
#define CATCH_CONFIG_RUNNER
#include "mfem.hpp"
#include "unit_tests.hpp"
#include "run_unit_tests.hpp"
bool launch_all_non_regression_tests = false;
std::string mfem_data_dir;
int main(int argc, char *argv[])
{
mfem::Device device("cuda");
// There must be exactly one instance.
Catch::Session session;
// Build a new command line parser on top of Catch's
using namespace Catch::clara;
auto cli = session.cli() |
Opt(launch_all_non_regression_tests) ["--all"] ("all tests");
session.cli(cli);
// For floating point comparisons, print 8 digits for single precision
// values, and 16 digits for double precision values.
Catch::StringMaker<float>::precision = 8;
Catch::StringMaker<double>::precision = 16;
// Apply provided command line arguments.
int r = session.applyCommandLine(argc, argv);
if (r != 0) { return r; }
auto cfg = session.configData();
cfg.testsOrTags.push_back("[CUDA]");
#ifdef MFEM_USE_MPI
// Exclude tests marked as Parallel in a serial run, even when compiled with
// MPI. This is done because there is no MPI session initialized.
cfg.testsOrTags.push_back("~[Parallel]");
#endif
std::cout << "INFO: Test filter: [CUDA] ~[Parallel]" << std::endl;
device.Print();
session.useConfigData(cfg);
int result = session.run();
return result;
// Include only tests labeled with CUDA. Exclude parallel tests.
return RunCatchSession(argc, argv, {"[CUDA]", "~[Parallel]"});
}
+84
View File
@@ -11,6 +11,7 @@
#include "mfem.hpp"
#include "unit_tests.hpp"
#include "general/tinyxml2.h"
#include <stdio.h>
#ifndef _WIN32
@@ -235,3 +236,86 @@ TEST_CASE("Save and load from collections", "[DataCollection]")
}
}
void SaveDataCollection(DataCollection &dc, int cycle, double t)
{
dc.SetCycle(cycle);
dc.SetTime(t);
dc.Save();
}
TEST_CASE("ParaView restart mode", "[ParaView]")
{
Mesh mesh = Mesh::MakeCartesian2D(2, 3, Element::QUADRILATERAL);
H1_FECollection fec(1, mesh.Dimension());
FiniteElementSpace fes(&mesh, &fec);
GridFunction u(&fes);
u = 0.0;
// Write initial dataset with three timesteps: 0, 1, 2.
{
ParaViewDataCollection dc("ParaView", &mesh);
dc.RegisterField("u", &u);
SaveDataCollection(dc, 0, 0);
SaveDataCollection(dc, 1, 1);
SaveDataCollection(dc, 2, 2);
}
// Using restart mode, append to the existing dataset, overwriting timesteps
// 1 and 2 with 1 and 1.5.
{
ParaViewDataCollection dc("ParaView", &mesh);
dc.UseRestartMode(true);
dc.RegisterField("u", &u);
SaveDataCollection(dc, 1, 1.0);
SaveDataCollection(dc, 2, 1.5);
}
// Parse the resulting PVD file, and verify that the structure is correct,
// and that it contains three timesteps: 0, 1, and 1.5.
using namespace tinyxml2;
auto StringCompare = [](const char *s1, const char *s2)
{
if (s1 == NULL || s2 == NULL) { return false; }
return strcmp(s1, s2) == 0;
};
auto VerifyDataset = [StringCompare](const XMLElement *ds, double t_ref)
{
REQUIRE(ds);
REQUIRE(StringCompare(ds->Name(), "DataSet"));
const char *timestep = ds->Attribute("timestep");
REQUIRE(timestep);
double t = std::stod(timestep);
REQUIRE(t == MFEM_Approx(t_ref));
};
XMLDocument xml;
xml.LoadFile("ParaView/ParaView.pvd");
REQUIRE(xml.ErrorID() == XML_SUCCESS);
const XMLElement *vtkfile = xml.FirstChildElement();
REQUIRE(vtkfile);
REQUIRE(StringCompare(vtkfile->Name(), "VTKFile"));
const XMLElement *collection = vtkfile->FirstChildElement();
REQUIRE(collection);
REQUIRE(StringCompare(collection->Name(), "Collection"));
const XMLElement *dataset = collection->FirstChildElement();
VerifyDataset(dataset, 0.0);
dataset = dataset->NextSiblingElement();
VerifyDataset(dataset, 1.0);
dataset = dataset->NextSiblingElement();
VerifyDataset(dataset, 1.5);
REQUIRE(dataset->NextSiblingElement() == NULL);
// Clean up
for (int c=0; c<=2; ++c)
{
std::string prefix = "ParaView/Cycle00000" + std::to_string(c);
REQUIRE(remove((prefix + "/data.pvtu").c_str()) == 0);
REQUIRE(remove((prefix + "/proc000000.vtu").c_str()) == 0);
REQUIRE(rmdir(prefix.c_str()) == 0);
}
REQUIRE(remove("ParaView/ParaView.pvd") == 0);
REQUIRE(rmdir("ParaView") == 0);
}
+7 -7
View File
@@ -167,9 +167,9 @@ TEST_CASE("DG SumIntegrator", "[SumIntegrator][PartialAssembly]")
integ2.AssemblePAInteriorFaces(fes);
integ_sum.AssemblePAInteriorFaces(fes);
const Operator *R_int = fes.GetFaceRestriction(
ElementDofOrdering::LEXICOGRAPHIC,
FaceType::Interior);
const FaceRestriction *R_int = fes.GetFaceRestriction(
ElementDofOrdering::LEXICOGRAPHIC,
FaceType::Interior);
int n_int = R_int->Height();
Vector x(n_int), y1(n_int), y2(n_int);
@@ -198,10 +198,10 @@ TEST_CASE("DG SumIntegrator", "[SumIntegrator][PartialAssembly]")
integ2.AssemblePABoundaryFaces(fes);
integ_sum.AssemblePABoundaryFaces(fes);
const Operator *R_bdr = fes.GetFaceRestriction(
ElementDofOrdering::LEXICOGRAPHIC,
FaceType::Boundary,
L2FaceValues::DoubleValued);
const FaceRestriction *R_bdr = fes.GetFaceRestriction(
ElementDofOrdering::LEXICOGRAPHIC,
FaceType::Boundary,
L2FaceValues::DoubleValued);
int n_bdr = R_bdr->Height();
x.SetSize(n_bdr);
+1 -1
View File
@@ -38,7 +38,7 @@ TEST_CASE("OperatorChebyshevSmoother", "[Chebyshev symmetry]")
Vector diag(fespace.GetTrueVSize());
aform.AssembleDiagonal(diag);
Solver* smoother = new OperatorChebyshevSmoother(opr.Ptr(), diag, ess_tdof_list,
Solver* smoother = new OperatorChebyshevSmoother(*opr, diag, ess_tdof_list,
cheb_order);
int n = smoother->Width();
+126
View File
@@ -0,0 +1,126 @@
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "mfem.hpp"
#include "unit_tests.hpp"
using namespace mfem;
#ifdef MFEM_USE_LAPACK
TEST_CASE("FDSolver",
"[FDSolver]")
{
double tol = 1e-10;
// SPD matrices
DenseMatrix A0(
{
{
1.29919, 0.61256, 0.82545
},
{0.61256, 0.57891, 0.39662},
{0.82545, 0.39662, 0.57541}
});
DenseMatrix A1(
{
{0.748236, 0.701663, 0.607517, 0.236740},
{0.701663, 0.809316, 0.713186, 0.256070},
{0.607517, 0.713186, 0.794221, 0.233943},
{0.236740, 0.256070, 0.233943, 0.083129}
});
DenseMatrix B0(
{
{0.13483, 0.51389, 0.43052},
{0.51389, 2.26750, 1.86331},
{0.43052, 1.86331, 1.59869}
});
DenseMatrix B1(
{
{0.94177, 1.02400, 1.14743, 0.35723},
{1.02400, 1.79087, 1.78708, 0.78304},
{1.14743, 1.78708, 2.06259, 0.80837},
{0.35723, 0.78304, 0.80837, 1.01798}
});
SECTION("2D")
{
Array<DenseMatrix *> A(2), B(2);
A[0] = &A0; A[1] = &A1;
B[0] = &B0; B[1] = &B1;
Vector y(12); y.Randomize(1);
Vector x(12), diff(12);
FDSolver S(A,B);
S.Mult(y,x);
DenseMatrix C1, C;
KronProd(A0, B1, C1);
KronProd(B0, A1, C);
C.Add(1., C1);
DenseMatrixInverse Cinv(C);
Cinv.Mult(y,diff);
diff-=x;
REQUIRE(diff.Norml2() < tol);
}
SECTION("3D")
{
DenseMatrix A2(
{
{1.14593, 0.76119},
{0.76119, 0.78993}
});
DenseMatrix B2(
{
{0.88088, 0.37899},
{0.37899, 0.45096}
});
Array<DenseMatrix *> A(3), B(3);
A[0] = &A0; A[1] = &A1; A[2] = &A2;
B[0] = &B0; B[1] = &B1; B[2] = &B2;
Vector y(24); y.Randomize(1);
Vector x(24), diff(24);
FDSolver S(A,B);
S.Mult(y,x);
DenseMatrix Temp, C0, C1, C;
KronProd(A0, B1, Temp);
KronProd(Temp, B2, C0);
KronProd(B0, A1, Temp);
KronProd(Temp, B2, C1);
KronProd(B0, B1, Temp);
KronProd(Temp, A2, C);
C.Add(1.,C0);
C.Add(1.,C1);
DenseMatrixInverse Cinv(C);
Cinv.Mult(y,diff);
diff-=x;
REQUIRE(diff.Norml2() < tol);
}
}
#endif // if MFEM_USE_LAPACK
+264
View File
@@ -239,6 +239,188 @@ TEST_CASE("DenseMatrix A*B^T methods",
}
}
TEST_CASE("KronMult methods",
"[DenseMatrix]")
{
double tol = 1e-12;
int nA = 3, mA = 4;
int nB = 5, mB = 6;
DenseMatrix A(nA,mA);
DenseMatrix B(nB,mB);
for (int i = 0; i<nA; i++)
for (int j = 0; j<mA; j++)
{
A(i,j) = ((double)rand()/(double)RAND_MAX);
}
for (int i = 0; i<nB; i++)
for (int j = 0; j<mB; j++)
{
B(i,j) = ((double)rand()/(double)RAND_MAX);
}
DenseMatrix AB;
KronProd(A,B,AB);
// (A ⊗ B) r
SECTION("KronMultABr")
{
Vector r(mA*mB); r.Randomize();
MFEM_VERIFY(r.Size() == AB.Width(), "Check r size");
Vector z0(AB.Height());
AB.Mult(r,z0);
Vector z1;
KronMult(A,B,r,z1);
MFEM_VERIFY(z0.Size() == z1.Size(), "Check z1 size");
z0-=z1;
REQUIRE(z0.Norml2() < tol);
}
// (A ⊗ B) R
SECTION("KronMultABR")
{
int nR = mA*mB;
int mR = 7;
DenseMatrix R(nR, mR);
for (int i = 0; i<nR; i++)
for (int j = 0; j<mR; j++)
{
R(i,j) = ((double)rand()/(double)RAND_MAX);
}
DenseMatrix Z0(nA*nB,mR);
Mult(AB,R,Z0);
DenseMatrix Z1;
KronMult(A,B,R,Z1);
MFEM_VERIFY(Z0.Height() == Z1.Height() &&
Z0.Width() == Z1.Width(), "Check z1 size");
Z0-=Z1;
REQUIRE(Z0.MaxMaxNorm() < tol);
}
// (A ⊗ B ⊗ C) r
SECTION("KronMultABCr")
{
int nC = 7, mC = 2;
DenseMatrix C(nC, mC);
for (int i = 0; i<nC; i++)
for (int j = 0; j<mC; j++)
{
C(i,j) = ((double)rand()/(double)RAND_MAX);
}
DenseMatrix ABC;
KronProd(AB,C,ABC);
Vector r(mA*mB*mC); r.Randomize();
MFEM_VERIFY(r.Size() == ABC.Width(), "Check r size");
Vector z0(nA*nB*nC);
ABC.Mult(r,z0);
Vector z1;
KronMult(A,B,C,r,z1);
MFEM_VERIFY(z0.Size() == z1.Size(), "Check z1 size");
z0-=z1;
REQUIRE(z0.Norml2() < tol);
}
}
TEST_CASE("KronMultInv methods",
"[DenseMatrixInverse]")
{
double tol = 1e-12;
int nA = 3;
int nB = 2;
DenseMatrix A(
{
{ 1.0, 0.2, 3.4},
{-2.0, -1.0, 3.1},
{ 0.7, 1.4,-0.9}
});
DenseMatrix B(
{
{-10.1, 5.7},
{-3.0, 4.2}
});
DenseMatrixInverse Ainv(A);
DenseMatrixInverse Binv(B);
DenseMatrix AB;
KronProd(A,B,AB);
// (A^-1 ⊗ B^-1) r
SECTION("KronMultInvABr")
{
Vector r(nA*nB); r.Randomize();
MFEM_VERIFY(r.Size() == AB.Width(), "Check r size");
Vector z0(AB.Height());
DenseMatrixInverse ABinv(AB);
ABinv.Mult(r,z0);
Vector z1;
KronMult(Ainv,Binv,r,z1);
MFEM_VERIFY(z0.Size() == z1.Size(), "Check z1 size");
z0-=z1;
REQUIRE(z0.Norml2() < tol);
}
// (A^-1 ⊗ B^-1) R
SECTION("KronMultInvABR")
{
int nR = nA*nB;
int mR = 7;
DenseMatrix R(nR, mR);
for (int i = 0; i<nR; i++)
for (int j = 0; j<mR; j++)
{
R(i,j) = ((double)rand()/(double)RAND_MAX);
}
DenseMatrixInverse ABinv(AB);
DenseMatrix Z0(nA*nB,mR);
ABinv.Mult(R,Z0);
DenseMatrix Z1;
KronMult(Ainv,Binv,R,Z1);
MFEM_VERIFY(Z0.Height() == Z1.Height() &&
Z0.Width() == Z1.Width(), "Check z1 size");
Z0-=Z1;
REQUIRE(Z0.MaxMaxNorm() < tol);
}
// (A^-1 ⊗ B^-1 ⊗ C^-1) r
SECTION("KronMultInvABCr")
{
int nC = 4;
DenseMatrix C(
{
{-2.1, 1.6, -3.4, 17.5},
{-7.1, 1.3, -7.5, -12.5},
{ 0.5, 5.7, -6.0, -0.5},
{ 9.2, 0.3, -1.4, -14.9}
});
DenseMatrix ABC;
KronProd(AB,C,ABC);
DenseMatrixInverse ABCInv(ABC);
Vector r(nA*nB*nC); r.Randomize();
MFEM_VERIFY(r.Size() == ABC.Width(), "Check r size");
Vector z0(nA*nB*nC);
ABCInv.Mult(r,z0);
DenseMatrixInverse Cinv(C);
Vector z1;
KronMult(Ainv,Binv,Cinv,r,z1);
MFEM_VERIFY(z0.Size() == z1.Size(), "Check z1 size");
z0-=z1;
REQUIRE(z0.Norml2() < tol);
}
}
TEST_CASE("LUFactors RightSolve", "[DenseMatrix]")
{
@@ -314,6 +496,88 @@ TEST_CASE("DenseTensor LinearSolve methods",
}
}
#ifdef MFEM_USE_LAPACK
TEST_CASE("EigenSystem methods",
"[DenseMatrix]")
{
double tol = 1e-12;
SECTION("SPD Matrix")
{
DenseMatrix A({{0.56806, 0.29211, 0.48315, 0.70024},
{0.29211, 0.85147, 0.68123, 0.70689},
{0.48315, 0.68123, 1.07229, 1.02681},
{0.70024, 0.70689, 1.02681, 1.15468}
});
DenseMatrix V, AV(4);
Vector Lambda;
for (bool sym: { false, true })
{
DenseMatrixEigensystem EigA(A,sym);
EigA.Eval();
V = EigA.Eigenvectors();
Lambda = EigA.Eigenvalues();
Mult(A,V,AV);
V.RightScaling(Lambda);
AV -= V;
REQUIRE(AV.MaxMaxNorm() < tol);
}
}
SECTION("Indefinite Matrix")
{
DenseMatrix A({{0.486278, 0.041135, 0.480727, 0.616026},
{0.523599, 0.119827, 0.087808, 0.415241},
{0.214454, 0.661631, 0.909626, 0.744259},
{0.107007, 0.630604, 0.077862, 0.221006}
});
DenseMatrixEigensystem EigA(A);
EigA.Eval();
Vector Lambda_r, Lambda_i;
// Real part of eigenvalues
Lambda_r = EigA.Eigenvalues();
// Imag part of eigenvalues
Lambda_i = EigA.Eigenvalues(true);
DenseMatrix V;
V = EigA.Eigenvectors();
// Real part of eigenvectors
DenseMatrix Vr(4), Vi(4);
Vr.SetCol(0,V.GetColumn(0));
Vr.SetCol(1,V.GetColumn(1));
Vr.SetCol(2,V.GetColumn(1));
Vr.SetCol(3,V.GetColumn(3));
// Imag part of eigenvectors
Vector vi(4); V.GetColumn(2,vi);
Vi.SetCol(0,0.);
Vi.SetCol(1,vi); vi *= -1.;
Vi.SetCol(2,vi);
Vi.SetCol(3,0.);
// Check that A*V = V * Lambda
// or A * (V_r + i V_i ) = (V_r + i V_i)*(Lamda_r + i Lambda_i)
// or A * V_r = V_r * Lambda_r - V_i * Lambda_i
// and A * V_i = V_r ( Lambda_i + V_i * Lambda_r
DenseMatrix AVr(4), AVi(4);
Mult(A,Vr, AVr);
Mult(A,Vi, AVi);
DenseMatrix Vrlr = Vr; Vrlr.RightScaling(Lambda_r);
DenseMatrix Vrli = Vr; Vrli.RightScaling(Lambda_i);
DenseMatrix Vilr = Vi; Vilr.RightScaling(Lambda_r);
DenseMatrix Vili = Vi; Vili.RightScaling(Lambda_i);
AVr -= Vrlr; AVr+= Vili;
AVi -= Vrli; AVi-= Vilr;
REQUIRE(AVr.MaxMaxNorm() < tol);
REQUIRE(AVi.MaxMaxNorm() < tol);
}
}
#endif // if MFEM_USE_LAPACK
TEST_CASE("DenseTensor copy", "[DenseMatrix][DenseTensor]")
{
DenseTensor t1(2,3,4);
+13 -8
View File
@@ -214,21 +214,27 @@ $(DATA_DIR):
MFEM_TESTS = UNIT_TESTS
include $(MFEM_TEST_MK)
ifeq (,$(wildcard $(MFEM_DIR)/../data))
MFEM_DATA_FLAG =
else
MFEM_DATA_FLAG = --data $(MFEM_DIR)/../data
endif
%-test-seq: %
@$(call mfem-test,$<,, Unit tests,,SKIP-NO-VIS)
@$(call mfem-test,$<,, Unit tests,$(MFEM_DATA_FLAG),SKIP-NO-VIS)
ceed_tests-test-seq: ceed_tests
@$(call mfem-test,$<,, CEED Unit tests (cpu),--device ceed-cpu,SKIP-NO-VIS)
@$(call mfem-test,$<,, CEED Unit tests (cpu),--device ceed-cpu $(MFEM_DATA_FLAG),SKIP-NO-VIS)
ifeq ($(MFEM_USE_CUDA),YES)
@$(call mfem-test,$<,, CEED Unit tests (cuda-ref),--device ceed-cuda:/gpu/cuda/ref,SKIP-NO-VIS)
@$(call mfem-test,$<,, CEED Unit tests (cuda-shared),--device ceed-cuda:/gpu/cuda/shared,SKIP-NO-VIS)
@$(call mfem-test,$<,, CEED Unit tests (cuda-gen),--device ceed-cuda:/gpu/cuda/gen,SKIP-NO-VIS)
@$(call mfem-test,$<,, CEED Unit tests (cuda-ref),--device ceed-cuda:/gpu/cuda/ref $(MFEM_DATA_FLAG),SKIP-NO-VIS)
@$(call mfem-test,$<,, CEED Unit tests (cuda-shared),--device ceed-cuda:/gpu/cuda/shared $(MFEM_DATA_FLAG),SKIP-NO-VIS)
@$(call mfem-test,$<,, CEED Unit tests (cuda-gen),--device ceed-cuda:/gpu/cuda/gen $(MFEM_DATA_FLAG),SKIP-NO-VIS)
endif
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP)
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI) 1, Parallel unit tests,,SKIP-NO-VIS)
@$(call mfem-test,$<, $(RUN_MPI) $(MFEM_MPI_NP), Parallel unit tests,,SKIP-NO-VIS)
@$(call mfem-test,$<, $(RUN_MPI) 1, Parallel unit tests,$(MFEM_DATA_FLAG),SKIP-NO-VIS)
@$(call mfem-test,$<, $(RUN_MPI) $(MFEM_MPI_NP), Parallel unit tests,$(MFEM_DATA_FLAG),SKIP-NO-VIS)
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
@@ -237,4 +243,3 @@ $(MFEM_LIB_FILE):
clean:
rm -f $(SEQ_UNIT_TESTS) $(PAR_UNIT_TESTS) *.o */*.o */*~ *~
rm -rf *.dSYM output_meshes
+18
View File
@@ -41,3 +41,21 @@ TEST_CASE("VTU XML Reader", "[Mesh][VTU][XML]")
REQUIRE(mesh.GetNumGeometries(2) == 1);
}
}
TEST_CASE("VTU XML Compressed Blocks", "[VTU][XML][MFEMData]")
{
auto filename = GENERATE(
"bracket_appended_compressed.vtu",
"bracket_appended_encoded_compressed.vtu",
"bracket_inline_compressed.vtu"
);
std::string mesh_path = mfem_data_dir + "/vtk/" + filename;
Mesh mesh = Mesh::LoadFromFile(mesh_path.c_str());
REQUIRE(mesh.Dimension() == 3);
REQUIRE(mesh.GetNE() == 206208);
REQUIRE(mesh.GetNV() == 50000);
REQUIRE(mesh.HasGeometry(Geometry::TETRAHEDRON));
REQUIRE(mesh.GetNumGeometries(3) == 1);
}
+15
View File
@@ -186,10 +186,12 @@ int tmop(int id, Req &res, int argc, char *argv[])
case 2: metric = new TMOP_Metric_002; break;
case 7: metric = new TMOP_Metric_007; break;
case 77: metric = new TMOP_Metric_077; break;
case 80: metric = new TMOP_Metric_080(0.5); break;
case 302: metric = new TMOP_Metric_302; break;
case 303: metric = new TMOP_Metric_303; break;
case 315: metric = new TMOP_Metric_315; break;
case 321: metric = new TMOP_Metric_321; break;
case 332: metric = new TMOP_Metric_332(0.5); break;
default:
{
if (id == 0) { cout << "Unknown metric_id: " << metric_id << endl; }
@@ -740,6 +742,13 @@ static void tmop_tests(int id = 0, bool all = false)
POR({1,2}).QOR({2,4}).
TID({4}).MID({1,2})).Run(id,all);
Launch(Launch::Args("Square01 + Adapted discrete size").
MESH("../../miniapps/meshing/square01.mesh").REFINE(1).
NORMALIZATION(true).
POR({1,2}).QOR({4,6}).
LINEAR_ITERATIONS(150).
TID({5}).MID({80}).LS({3})).Run(id,all);
Launch(Launch::Args("Blade").
MESH("../../miniapps/meshing/blade.mesh").
POR({1,2}).QOR({2,4}).
@@ -768,6 +777,12 @@ static void tmop_tests(int id = 0, bool all = false)
POR({1,2}).QOR({4,2}).
TID({7}).MID({302,321})).Run(id,all);
Launch(Launch::Args("Cube + Discrete size + normalization").
MESH("../../miniapps/meshing/cube.mesh").
NORMALIZATION(true).
POR({1,2}).QOR({4,2}).
TID({5}).MID({332})).Run(id,all);
// Note: order 1 has no interior nodes, so all residuals are zero and the
// Newton iteration exits immediately.
Launch(Launch::Args("Toroid-Hex").
+7 -36
View File
@@ -11,9 +11,10 @@
#define CATCH_CONFIG_RUNNER
#include "mfem.hpp"
#include "unit_tests.hpp"
#include "run_unit_tests.hpp"
bool launch_all_non_regression_tests = false;
std::string mfem_data_dir;
#ifdef MFEM_USE_MPI
mfem::MPI_Session *GlobalMPISession;
@@ -24,44 +25,14 @@ mfem::MPI_Session *GlobalMPISession;
int main(int argc, char *argv[])
{
mfem::Device device("cuda");
// There must be exactly one instance.
Catch::Session session;
// Build a new command line parser on top of Catch's
using namespace Catch::clara;
auto cli = session.cli() |
Opt(launch_all_non_regression_tests) ["--all"] ("all tests");
session.cli(cli);
// For floating point comparisons, print 8 digits for single precision
// values, and 16 digits for double precision values.
Catch::StringMaker<float>::precision = 8;
Catch::StringMaker<double>::precision = 16;
// Apply provided command line arguments.
int r = session.applyCommandLine(argc, argv);
if (r != 0) { return r; }
#ifdef MFEM_USE_MPI
mfem::MPI_Session mpi;
GlobalMPISession = &mpi;
// Exclude all tests that are not labeled with Parallel and CUDA.
auto cfg = session.configData();
cfg.testsOrTags.push_back("[Parallel]");
cfg.testsOrTags.push_back("[CUDA]");
session.useConfigData(cfg);
if (mpi.Root())
{
std::cout << "INFO: Test filter: [Parallel] [CUDA]" << std::endl;
device.Print();
}
bool root = mpi.Root();
#else
bool root = true;
#endif
int result = session.run();
return result;
// Include only tests that are labeled with both CUDA and Parallel.
return RunCatchSession(argc, argv, {"[CUDA]","[Parallel]"}, root);
}
+7 -35
View File
@@ -11,9 +11,10 @@
#define CATCH_CONFIG_RUNNER
#include "mfem.hpp"
#include "unit_tests.hpp"
#include "run_unit_tests.hpp"
bool launch_all_non_regression_tests = false;
std::string mfem_data_dir;
#ifdef MFEM_USE_MPI
mfem::MPI_Session *GlobalMPISession;
@@ -23,43 +24,14 @@ mfem::MPI_Session *GlobalMPISession;
int main(int argc, char *argv[])
{
// There must be exactly one instance.
Catch::Session session;
// Build a new command line parser on top of Catch's
using namespace Catch::clara;
auto cli = session.cli() |
Opt(launch_all_non_regression_tests) ["--all"] ("all tests");
session.cli(cli);
// For floating point comparisons, print 8 digits for single precision
// values, and 16 digits for double precision values.
Catch::StringMaker<float>::precision = 8;
Catch::StringMaker<double>::precision = 16;
// Apply provided command line arguments.
int r = session.applyCommandLine(argc, argv);
if (r != 0) { return r; }
#ifdef MFEM_USE_MPI
mfem::MPI_Session mpi;
GlobalMPISession = &mpi;
// Exclude all tests that are not labeled with Parallel.
auto cfg = session.configData();
cfg.testsOrTags.push_back("[Parallel]");
session.useConfigData(cfg);
// NOTE: tests marked with "[CUDA]" (in addition to "[Parallel]") are still
// run with the default device.
if (mpi.Root())
{
std::cout << "INFO: Test filter: [Parallel]" << std::endl;
}
bool root = mpi.Root();
#else
bool root = true;
#endif
int result = session.run();
return result;
// Only run tests that are labeled with Parallel.
return RunCatchSession(argc, argv, {"[Parallel]"}, root);
}
+60
View File
@@ -0,0 +1,60 @@
// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_RUN_UNIT_TEST
#define MFEM_RUN_UNIT_TEST
#include "unit_tests.hpp"
static int RunCatchSession(int argc, char *argv[],
const std::vector<std::string> &testsOrTags,
bool root=true)
{
// There must be exactly one instance.
Catch::Session session;
// Build a new command line parser on top of Catch's
using namespace Catch::clara;
auto cli = session.cli()
| Opt(launch_all_non_regression_tests) ["--all"] ("all tests")
| Opt(mfem_data_dir, "") ["--data"] ("mfem/data repository");
session.cli(cli);
// For floating point comparisons, print 8 digits for single precision
// values, and 16 digits for double precision values.
Catch::StringMaker<float>::precision = 8;
Catch::StringMaker<double>::precision = 16;
// Apply provided command line arguments.
int r = session.applyCommandLine(argc, argv);
if (r != 0) { return r; }
auto cfg = session.configData();
cfg.testsOrTags.insert(cfg.testsOrTags.end(), testsOrTags.begin(), testsOrTags.end());
if (mfem_data_dir == "") { cfg.testsOrTags.push_back("~[MFEMData]"); }
session.useConfigData(cfg);
if (root)
{
std::cout << "INFO: Test filter: ";
for (std::string &filter : cfg.testsOrTags)
{
std::cout << filter << " ";
}
std::cout << std::endl;
}
int result = session.run();
return result;
}
#endif
+4 -34
View File
@@ -11,43 +11,13 @@
#define CATCH_CONFIG_RUNNER
#include "mfem.hpp"
#include "unit_tests.hpp"
#include "run_unit_tests.hpp"
bool launch_all_non_regression_tests = false;
std::string mfem_data_dir;
int main(int argc, char *argv[])
{
// There must be exactly one instance.
Catch::Session session;
// Build a new command line parser on top of Catch's
using namespace Catch::clara;
auto cli = session.cli() |
Opt(launch_all_non_regression_tests) ["--all"] ("all tests");
session.cli(cli);
// For floating point comparisons, print 8 digits for single precision
// values, and 16 digits for double precision values.
Catch::StringMaker<float>::precision = 8;
Catch::StringMaker<double>::precision = 16;
// Apply provided command line arguments.
int r = session.applyCommandLine(argc, argv);
if (r != 0) { return r; }
#ifdef MFEM_USE_MPI
// Exclude tests marked as Parallel in a serial run, even when compiled with
// MPI. This is done because there is no MPI session initialized.
auto cfg = session.configData();
cfg.testsOrTags.push_back("~[Parallel]");
session.useConfigData(cfg);
#endif
// NOTE: tests marked with "[CUDA]" are still run using the default device.
std::cout << "INFO: Test filter: ~[Parallel]" << std::endl;
int result = session.run();
return result;
// Exclude parallel tests.
return RunCatchSession(argc, argv, {"~[Parallel]"});
}
+5
View File
@@ -17,6 +17,11 @@
/// Command line '--all' option to launch all non-regression tests.
extern bool launch_all_non_regression_tests;
/// Command line '--data' argument for path to mfem/data repo.
/** If no --data path is provided, then mfem_data_dir will be the empty string,
and tests tagged with [MFEMData] will be skipped. */
extern std::string mfem_data_dir;
/** @brief MFEM_Approx can be used to compare floating point values within an
absolute tolerance of @a abs_tol (default value 1e-12) and relative
tolerance of @a rel_tol (default value 1e-12). */