Compare commits

..
Author SHA1 Message Date
dohyun b8d0ba6715 Finalize attributes and correct ess_bdr size 2023-11-16 17:50:49 -05:00
dohyun 0328a5c117 add bilienarform too 2023-11-16 15:44:01 -05:00
dohyun 5333e1fc7e minimal example 2023-11-16 15:34:47 -05:00
Tzanio Kolev 64c8725594 Merge pull request #3831 from mfem/sjg/cuda-flags-cmake-fix
Fix `CMAKE_CUDA_FLAGS` flags for CMake build
2023-09-05 08:58:27 -07:00
Tzanio Kolev b57486e027 Merge pull request #3682 from mfem/tmop-lim-fit
TMOP - option to specify target positions for some nodes
2023-09-03 14:26:19 -07:00
Tzanio Kolev 5e0140e877 Merge pull request #3613 from mfem/getfe-fix
Remove NULL return in GetFE
2023-09-03 14:25:58 -07:00
Tzanio Kolev 1ccb31fde6 Merge pull request #3833 from mfem/actions-add-debug-tests
In GitHub actions, enable testing of debug builds under Ubuntu
2023-08-27 11:29:27 -07:00
Mittal, Ketan a279b4592b reviewer comment 2023-08-24 20:11:01 -07:00
Mittal, Ketan 40b61f789e Merge branch 'master' of https://github.com/mfem/mfem into getfe-fix 2023-08-24 20:10:42 -07:00
Veselin DobrevandWill Pazner 0933014721 Apply suggestions from code review
Co-authored-by: Will Pazner <11493037+pazner@users.noreply.github.com>
2023-08-23 21:14:31 -07:00
Tzanio Kolev c9e63c6292 Merge pull request #3557 from mfem/mpi_h1lor
Parallel H1 HO <-> LOR Transfer Operator
2023-08-22 15:53:34 -07:00
Tzanio Kolev 05442e17dc Merge pull request #3088 from mfem/nurbsquad
NURBS patch quadrature
2023-08-22 15:53:13 -07:00
Tzanio Kolev 27235c38ef Merge pull request #3404 from mfem/nurbs-auto-knot2edge-dev
Nurbs auto knot2edge and free patch orientation dev [nurbs-auto-knot2edge-dev]
2023-08-22 15:51:28 -07:00
Tzanio Kolev 5fc6ae6201 Merge pull request #3822 from mfem/qf-save-vtu
QuadratureFunction::SaveVTU fixes
2023-08-22 15:50:45 -07:00
Will Pazner 315065b727 Merge pull request #3789 from mfem/hughcars/gslib-shadow-variable
Fix shadow variable warning
2023-08-23 03:27:05 +09:00
Will Pazner 51adfe2785 Merge pull request #3829 from mfem/submesh-transfer-fix
Update `(Par)TransferMap` to support data on GPU by copying it to CPU
2023-08-23 03:26:53 +09:00
Veselin Dobrev 7c6af8cab8 More changes to suppress gcc warnings when building with -Og and
older gcc versions.
2023-08-19 16:08:16 -07:00
Veselin Dobrev 397abc4190 Changes to suppress gcc warnings when building with -Og 2023-08-18 02:03:18 -07:00
Veselin Dobrev 680ccf0f2d In GitHub actions, add flag -Og for the debug builds that are tested
(under Ubuntu) to speed up the tests.
2023-08-17 20:30:49 -07:00
Veselin Dobrev e42f2cc32e Small CMake tweaks to fix faulty merge into 'next' (after re-merge) 2023-08-17 19:52:54 -07:00
Veselin Dobrev e4c0b956e8 In GitHub actions, enable testing of debug builds under Ubuntu 2023-08-17 19:26:04 -07:00
Sebastian Grimberg 1844c93b14 Fix CMAKE_CUDA_FLAGS flags for CMake build 2023-08-17 10:28:30 -07:00
Vladimir Z Tomov 1d1443cb1a glvis option to fix autotest. 2023-08-16 09:31:08 -07:00
Mark L. Stowell a15866e212 Merge pull request #3709 from mfem/hughcars/prism-ncmesh-bug
Bug fixes for NC prism meshes
2023-08-15 20:10:57 -07:00
Vladimir Z Tomov 9baadbe00e gitignore 2023-08-15 14:04:15 -07:00
Mark L. Stowell 4222287b02 Merge branch 'master' into hughcars/prism-ncmesh-bug 2023-08-15 13:39:40 -07:00
Mark L. Stowell d46c2cd5a7 Merge pull request #3676 from mfem/hughcars/variable-length-arrays-of-length-zero-bug
Fix heap overflow from variable length arrays
2023-08-15 13:36:30 -07:00
Mark L. Stowell 5a2d286e0c Merge branch 'master' into hughcars/prism-ncmesh-bug 2023-08-15 11:30:57 -07:00
Mark L. Stowell d15f9136c5 Merge branch 'master' into hughcars/variable-length-arrays-of-length-zero-bug 2023-08-15 11:30:27 -07:00
Veselin Dobrev 5d50d96a7b Update (Par)TransferMap to support data on GPU by copying it to CPU.
This fixes issues when running ex34, ex34p, ex35p with device backends.
2023-08-14 18:39:07 -07:00
Vladimir Z Tomov edf209a78c better doxygen 2023-08-14 17:27:22 -07:00
Vladimir Z Tomov eb1a95acfd changelog. 2023-08-14 16:45:00 -07:00
Vladimir Z Tomov bee28f59f5 rename to fit-node-position.
added to makefile, CMakeLists.
2023-08-14 16:38:59 -07:00
Tzanio Kolev cbad53d9f9 Merge branch 'master' into nurbsquad 2023-08-12 09:43:54 -07:00
Tzanio Kolev bebdead740 Merge branch 'master' into nurbs-auto-knot2edge-dev 2023-08-12 09:40:19 -07:00
Tzanio Kolev 1a5207f856 Merge pull request #3659 from mfem/nurbs-interp-fix-make
fix make file of nurbs-interp-dev [nurbs-interp-fix-make]
2023-08-12 06:53:42 -07:00
Tzanio Kolev be82bb8e6b Merge pull request #3798 from mfem/svd
SVD modified to solve linear systems
2023-08-12 06:52:48 -07:00
Tzanio Kolev 0c7fee782f Merge pull request #3817 from mfem/face-bdr-map-2d
Generalize Mesh::GetFaceToBdrElMap
2023-08-12 06:52:32 -07:00
Tzanio Kolev 0ba4fdc591 Merge pull request #3810 from mfem/opt-std-string
Support std::string in OptionsParser
2023-08-12 06:52:12 -07:00
Tzanio Kolev 300af9c231 Merge pull request #3769 from researchapps/update/docker-build-instructions
fix config/docker image instructions and dockerfiles
2023-08-12 06:51:52 -07:00
Vladimir Z Tomov 4a4b0062f8 verify compaatible ordering. 2023-08-11 18:42:48 -07:00
Vladimir Z Tomov aee4575404 Support of byVDIM ordering of the spaces + bug fix in CalcSurfaceFitErrors. 2023-08-11 18:22:57 -07:00
Vladimir Z Tomov 834205ac04 Improved the tmop-lim-fit: allowed slipping on the non-marked boundaries. 2023-08-11 14:42:52 -07:00
Will Pazner df8b9928f8 Save QFields with correct name in VTU format 2023-08-09 22:04:47 -07:00
Will Pazner 3ad69e8e65 Support FaceQuadratureSpace in QuadratureFunction::SaveVTU 2023-08-09 22:04:17 -07:00
Vladimir Z Tomov 057e25657e Fixed errors that Ketan pointed out. 2023-08-09 13:44:49 -07:00
Will Pazner 94fb0f94c5 Fix shadow warning 2023-08-09 12:34:22 -07:00
Will Pazner f986b2022b Minor Doxygen formatting 2023-08-09 12:09:18 -07:00
Eric B. Chin 8806eec449 add doxygen comments 2023-08-09 11:55:31 -07:00
Will Pazner c1779a49cb Fix shadow warning 2023-08-09 11:37:17 -07:00
Will Pazner 472f2b83d2 Small changes to dual vector transfer in lor-transfer miniapps 2023-08-09 11:22:45 -07:00
Will Pazner d4d1eadb1a Avoid GetRestrictionTransposeOperator in L2ProjectionH1Space
See the bug fixed in PR #3821
2023-08-09 11:21:21 -07:00
Will Pazner 1a10217ba4 Move L2ProjectionH1Space::AllocR from private to protected 2023-08-09 11:20:21 -07:00
Will Pazner 65921a4dda make style 2023-08-09 11:20:01 -07:00
Will Pazner 1cf0ffbe03 Remove virtual destructor from L2ProjectionH1Space 2023-08-09 11:17:55 -07:00
Will Pazner 32432c2c19 Revert "add amr option to lor-transfer"
This reverts commit 3e1a24c35c.
2023-08-09 11:16:43 -07:00
Vladimir Z Tomov 2fc92344bc Merge branch 'master' into tmop-lim-fit 2023-08-08 17:50:20 -07:00
Eric B. Chin 3e1a24c35c add amr option to lor-transfer 2023-08-08 14:49:26 -07:00
EB Chin aeacd50a40 fix dual field parallel transfers 2023-08-08 12:40:43 -07:00
Will Pazner edc138ebd5 Handle cases RA and RA in branch-history 2023-08-08 11:31:28 -07:00
Eric B. Chin 134af6f08f Merge pull request #3815 from mfem/mpi_h1lor_edits
Proposed changes to #3557
2023-08-08 09:48:13 -07:00
Will Pazner c2ed725739 Generalize Mesh::GetFaceToBdrElMap 2023-08-08 09:13:31 -07:00
JacobLotz 971eb7bb5f update changelog 2023-08-08 08:53:18 +02:00
Will Pazner c4dc57ffd2 Always perform copy in L2ProjectionH1Space GetTDofs and SetTDofs 2023-08-07 13:34:08 -07:00
Will Pazner 89f7d276ef Remove unused variables 2023-08-07 13:26:32 -07:00
Will Pazner cddbd24df3 Simplify mass computation in lor-transfer miniapps 2023-08-07 13:26:24 -07:00
Will Pazner 39714f039a Merge ParL2ProjectionH1Space with L2ProjectionH1Space 2023-08-07 13:26:06 -07:00
Will Pazner e42147f3a7 Simplify ParL2ProjectionH1Space 2023-08-07 11:14:40 -07:00
Will Pazner 62dc04c5ca Use std::string in ex0 and ex0p 2023-08-04 14:53:19 -07:00
Will Pazner fa101bcb05 Use std::string& instead of const char* in mesh API 2023-08-04 14:53:19 -07:00
Jacob Lotz 1eb679b9e1 Merge branch 'master' into nurbs-interp-fix-make 2023-08-04 09:37:03 +02:00
Will Pazner c770c80bf7 Allow std::string in OptionsParser 2023-08-03 19:42:44 -07:00
Eric B. Chin 100de86cfa add new miniapp and new feature 2023-08-01 22:14:22 -07:00
Eric B. Chin 7d2e402a08 fix style 2023-08-01 22:06:07 -07:00
Eric B. Chin c0dab30375 rename variables 2023-08-01 22:00:26 -07:00
EB Chin fdd1c6c8b4 simplify tdof method 2023-08-01 13:20:31 -07:00
JacobLotz d70cb175e7 Small style improvement 2023-08-01 08:39:54 +02:00
Dylan Copeland 7c62434ef3 Minor 2023-07-31 20:50:34 -07:00
Dylan Copeland e18279782d Merge branch 'master' of github.com:mfem/mfem into nurbsquad 2023-07-31 20:17:42 -07:00
Dylan Copeland 90c13f0634 Fixing tests. 2023-07-31 18:44:53 -07:00
Backer 21889e22e4 Merge branch 'svd' of https://github.com/mfem/mfem into svd 2023-07-31 16:59:22 -07:00
Backer b9a3d1fffe Reverted the chage for the astyle binary in makefile 2023-07-31 16:58:43 -07:00
Backer b77c106d72 Style changes by make style 2023-07-31 16:58:09 -07:00
Dylan Copeland 994cdacff4 Reverting bug introduced in last commit. 2023-07-31 16:02:17 -07:00
Dylan Copeland c52da3dd57 Improved documentation. Changed NNLS verbosity to 0 in diffusion integrator. Added testing to miniapp makefile. Fixed some memory leaks. Decreased miniapp solver tolerance. 2023-07-31 15:12:34 -07:00
J-Pi 7c9f9b262d Merge branch 'master' into svd 2023-07-31 15:01:59 -07:00
Backer a759f32692 Made the style better 2023-07-31 14:51:53 -07:00
Backer 79b2967464 Tried to fix the style. 2023-07-31 14:27:40 -07:00
Backer d6aab14ec1 Corrected some more typos resulting in failed checks 2023-07-31 14:24:21 -07:00
Backer 69302c3ce7 Corrected typos resulting in failed checks 2023-07-31 14:16:44 -07:00
Backer 75e5bb2a7f Corrected typos resulting in failed checks 2023-07-31 14:04:34 -07:00
Backer 56eef31eb8 Merge branch 'svd' of https://github.com/mfem/mfem into svd 2023-07-31 12:42:14 -07:00
Backer abe702f5b8 Added documentation for the DenseMatrixSVD class. MArked the old constructors as deprecated as discussed with Vladimir. 2023-07-31 12:36:32 -07:00
JacobLotz 8a5b30ff71 fix issues after merge/solving conflicts 2023-07-31 17:41:51 +02:00
Jacob Lotz a9eb6ecc1a Merge branch 'master' into nurbs-auto-knot2edge-dev 2023-07-31 16:56:24 +02:00
Eric B. Chin 98d96e5c99 Merge branch 'mpi_h1lor' of github.com:mfem/mfem into mpi_h1lor 2023-07-29 15:30:59 -07:00
Eric B. Chin f121373467 bugfixes 2023-07-29 15:30:51 -07:00
Tzanio Kolev 428f051e1c Merge branch 'master' into svd 2023-07-28 08:45:55 -07:00
Tzanio Kolev bc441dab14 Merge branch 'master' into mpi_h1lor 2023-07-28 08:41:03 -07:00
J-Pi d919149e5f Merge branch 'master' into svd 2023-07-27 16:36:29 -07:00
Backer 1ee6e88934 Added two constructers to the DenseMatrixSVD and to if to the DenseMatrixSVD::Eval so that it canbeused to solve a linear system. 2023-07-27 14:40:16 -07:00
Hugh Carson 76905b7496 Fix shadow variable warning 2023-07-21 14:50:41 -04:00
Stowell, Mark L 1b76a2ee5e Adding Hex test to test_ncmesh 2023-07-14 12:24:06 -07:00
Hugh Carson 5d2f112d1e Remove unneeded meshes 2023-07-14 11:50:38 -04:00
Hugh Carson a35c335bd7 Merge remote-tracking branch 'origin/master' into hughcars/prism-ncmesh-bug 2023-07-14 11:20:40 -04:00
Hugh Carson 5848987cf7 Add in variant switch for alternative wedge constructions 2023-07-14 11:20:24 -04:00
Eric B. Chin 2aa15a7ef0 address review comments 2023-07-13 17:54:33 -07:00
Stowell, Mark L 7417766c5e Normalizing error, automating mesh construction, and adding tetrahedron tests 2023-07-13 13:21:30 -07:00
Mark L. Stowell 9ef9f3c271 Merge branch 'master' into hughcars/variable-length-arrays-of-length-zero-bug 2023-07-11 16:38:23 -07:00
Mittal, Ketan 037031bae6 minor 2023-07-11 13:15:53 -07:00
Mittal, Ketan fc13314a10 update CHANGELOG, fespace.hpp, and abort message to indicate change in behavior of GetFE 2023-07-11 13:05:48 -07:00
Mittal, Ketan f0a5e74bab Merge branch 'master' of https://github.com/mfem/mfem into getfe-fix 2023-07-11 12:23:08 -07:00
Vladimir Z Tomov 91199ccb6c Merge branch 'master' into tmop-lim-fit 2023-07-11 11:18:30 -07:00
Hugh Carson 72bfdbc906 Merge remote-tracking branch 'origin/master' into hughcars/prism-ncmesh-bug 2023-07-10 15:22:52 -04:00
Hugh Carson a93c20c5ad Merge remote-tracking branch 'origin/master' into hughcars/variable-length-arrays-of-length-zero-bug 2023-07-10 15:22:26 -04:00
Eric B. Chin 2103b7ea8c Merge branch 'master' into mpi_h1lor 2023-07-08 17:43:15 -07:00
Jacob Lotz c2deaeed46 Merge branch 'master' into nurbs-interp-fix-make 2023-07-07 11:37:06 +02:00
JacobLotz 3883d47caa Revert "Remove CurveInt"
This reverts commit 40fefd264e.
2023-07-07 09:41:45 +02:00
hughcars fb41dc55a8 Merge branch 'master' into hughcars/variable-length-arrays-of-length-zero-bug 2023-07-05 16:05:26 -04:00
hughcars e07747c246 Merge branch 'master' into hughcars/prism-ncmesh-bug 2023-07-05 16:04:49 -04:00
Jacob Lotz 75e6cfc574 Merge branch 'master' into nurbs-interp-fix-make 2023-07-04 13:02:44 +02:00
Jacob Lotz 575c63a564 Merge branch 'master' into nurbs-auto-knot2edge-dev 2023-07-04 12:49:29 +02:00
JacobLotz 40fefd264e Remove CurveInt 2023-07-04 12:48:46 +02:00
JacobLotz 340f0b8d51 Use meshes from source 2023-07-04 11:26:21 +02:00
JacobLotz c2d4f28f87 Fix testing 2 2023-07-04 09:41:11 +02:00
JacobLotz 3418bd94d3 Fix testing 1 2023-07-04 09:01:28 +02:00
JacobLotz e92d16f165 Move two-* from data to miniapps/nurbs 2023-07-03 18:10:58 +02:00
vsoch 3e2de8dfbe fix config/docker image instructions and dockerfiles
Problem: the current base image does not finish installing mfem,
and the instructions do not clearly lay out the use cases for using
the two containers.
Solution: update the builds and better delineate the different use
cases and usage instructions in the README.

Signed-off-by: vsoch <vsoch@users.noreply.github.com>
2023-07-01 15:48:21 -06:00
Vladimir Z Tomov 84a2de8dec Merge branch 'master' into tmop-lim-fit 2023-06-27 12:16:26 -07:00
hughcars b5e65b187d Merge branch 'master' into hughcars/prism-ncmesh-bug 2023-06-26 09:27:57 -04:00
hughcars 3b57334da0 Merge branch 'master' into hughcars/variable-length-arrays-of-length-zero-bug 2023-06-26 09:27:34 -04:00
Tzanio Kolev acf49d4035 Merge branch 'master' into mpi_h1lor 2023-06-24 14:20:56 -07:00
Dylan Copeland 864677cdcb Documentation and minor interface changes. 2023-06-23 19:16:02 -07:00
Dylan Copeland ba91fb564b Reduced the number of dimensions in the array reducedIDs. Eliminated NNLSSolver constructor arguments and made set functions for the parameters. Changed interface for AssemblePatchMatrix to take a FiniteElementSpace. 2023-06-23 18:29:00 -07:00
hughcars d9d674b82e Merge branch 'master' into hughcars/prism-ncmesh-bug 2023-06-23 17:53:57 -04:00
hughcars c17092c5c7 Merge branch 'master' into hughcars/variable-length-arrays-of-length-zero-bug 2023-06-23 17:53:03 -04:00
Dylan Copeland e7fb674fa3 Renamed NURBSPatchRule as NURBSMeshRules. Removed options for NURBSMeshRules, so that it only supports patch-wise tensor-product rules. Changed bool integrator options to an enum. Reduced the number of dimensions in the reducedWeights array. 2023-06-23 12:02:00 -07:00
Dylan Copeland adcc8a53b8 Merge branch 'master' of https://github.com/mfem/mfem into nurbsquad 2023-06-22 12:47:47 -07:00
Dylan Copeland f9167a6752 Initializing variable. 2023-06-22 12:46:51 -07:00
Tzanio Kolev b3d4f575b1 Merge branch 'master' into nurbs-auto-knot2edge-dev 2023-06-22 11:34:09 -07:00
Dylan Copeland fa81db96ba Removing static condensation option in nurbs_patch_ex1. Removing inefficient integrators. 2023-06-21 17:28:07 -07:00
hughcars 3e1bb914c5 Merge branch 'master' into hughcars/variable-length-arrays-of-length-zero-bug 2023-06-19 11:50:07 -04:00
Hugh Carson 0ca88cb295 Remove some empty lines 2023-06-19 11:31:12 -04:00
Hugh Carson 2419e6211e Merge remote-tracking branch 'origin/master' into hughcars/prism-ncmesh-bug 2023-06-19 11:30:58 -04:00
hughcars 7f4cce4a0a Merge branch 'master' into hughcars/prism-ncmesh-bug 2023-06-14 11:43:07 -04:00
hughcars 4016cceb6a Merge branch 'master' into hughcars/variable-length-arrays-of-length-zero-bug 2023-06-14 11:41:34 -04:00
Mittal, Ketan c4336e8c6c refactor abort to specify if invalid element index or empty partition 2023-06-13 08:53:51 -07:00
Mittal, Ketan 228e4e5564 Merge branch 'master' of https://github.com/mfem/mfem into getfe-fix 2023-06-13 08:48:16 -07:00
Tzanio Kolev 1a6a3ac5d7 Merge branch 'master' into nurbs-auto-knot2edge-dev 2023-06-11 16:45:21 -07:00
Tzanio Kolev d22c68dbd8 Merge branch 'master' into nurbsquad
Conflicts:
	CHANGELOG
2023-06-11 16:37:24 -07:00
Tzanio Kolev c068471192 Merge branch 'master' into nurbs-interp-fix-make 2023-06-11 15:34:35 -07:00
Tzanio Kolev 502ad09e67 Small adjustments 2023-06-11 15:33:43 -07:00
Tzanio Kolev e2963ad817 Merge branch 'master' into nurbs-interp-fix-make 2023-06-11 15:32:59 -07:00
Hugh Carson a389cd5257 Fix warning 2023-06-05 12:36:58 -04:00
Hugh Carson 6d0b9d81c2 Change relative paths to match those from the build directory rather than the source directory 2023-06-05 09:50:00 -04:00
Hugh Carson 06daad4e03 Remove unneeded refined mesh 2023-06-05 09:37:23 -04:00
Hugh Carson 8009bcb192 Style fixes 2023-06-05 09:36:25 -04:00
Hugh Carson c82b9b33fc Provisional tests 2023-06-05 09:26:19 -04:00
Hugh Carson 886b04a5f3 Fix set unused warnings, and remove unneeded new 2023-06-05 09:26:19 -04:00
Hugh Carson 07b5787f5b Adding some overrides, range loops, and some comments. Also small refactor of group and owner array building. 2023-06-05 09:26:19 -04:00
Hugh Carson c4cd39a785 Move the simplest prism meshes to the data folder. 2023-06-05 09:26:19 -04:00
Hugh Carson a7be74494c Adding pair of quadratic prism meshes causing issues 2023-06-05 09:26:19 -04:00
Hugh Carson 525a083a78 Fix for handling dof -> entity map containing entities with zero dofs (P2 tris, no dof, and P2 quads, 1 dof) 2023-06-05 09:26:19 -04:00
Hugh Carson 5a4190c392 Fix for handling dof sent with negative index 2023-06-05 09:26:19 -04:00
Hugh Carson e4ba44bcb7 Fixing bug where variable length arrays of length zero are being deaddressed, resulting in heap overflow and/or not failing nullptr checks 2023-06-05 09:26:19 -04:00
Tzanio Kolev 5a494f1bbf Merge branch 'master' into tmop-lim-fit 2023-06-04 13:49:17 -07:00
Hugh Carson 93dcb69132 Fix for handling dof sent with negative index 2023-05-24 15:10:26 -04:00
Hugh Carson b9de68ea8c Fixing bug where variable length arrays of length zero are being deaddressed, resulting in heap overflow and/or not failing nullptr checks 2023-05-24 15:10:26 -04:00
Mittal, Ketan 311accf5eb Merge branch 'master' of https://github.com/mfem/mfem into getfe-fix 2023-05-23 19:09:30 -07:00
Mittal, Ketan ad61527aff remove null return from GetFE 2023-05-23 19:09:03 -07:00
Vladimir Z Tomov e75f24ff76 minor 2023-05-19 14:24:54 -07:00
Vladimir Z Tomov 72a1d09920 minor 2023-05-18 23:16:34 -07:00
Vladimir Z Tomov 03be75de97 minor 2023-05-18 19:32:16 -07:00
Vladimir Z Tomov ec77e3988a missing computation 2023-05-18 11:52:13 -07:00
Vladimir Z Tomov ccadddc9f5 Merge branch 'master' into tmop-lim-fit 2023-05-17 23:03:48 -07:00
Jacob Lotz 8adb531554 fix .gitignore and memleak 2023-05-17 15:02:25 +02:00
Jacob Lotz 11f3d34963 Merge branch 'nurbs-auto-knot2edge-dev' of github.com:mfem/mfem into nurbs-auto-knot2edge-dev 2023-05-16 17:34:14 +02:00
Jacob Lotz f561f1d069 Fix memleak after 1D impl 2023-05-16 17:33:45 +02:00
Jacob Lotz 6376dc1bb9 Merge branch 'master' into nurbs-auto-knot2edge-dev 2023-05-16 11:34:13 +02:00
Jacob Lotz 1bcd411837 Merge branch 'master' into nurbs-interp-fix-make 2023-05-15 10:59:29 +02:00
JacobLotz 31ed77c025 make style 2023-05-10 16:51:55 +02:00
JacobLotz a124640f43 Make style and some documentation 2023-05-10 12:30:48 +02:00
JacobLotz 9ca0124b99 Working 1D 2023-05-10 12:18:36 +02:00
JacobLotz 29d17b7174 fix 1 issue 2023-05-10 09:23:11 +02:00
JacobLotz ceab44d9fd improved naming of check 2023-05-09 18:13:50 +02:00
JacobLotz d8d5adb62b looking for bug 1 2023-05-09 18:02:45 +02:00
JacobLotz aad2c99e48 more 1D and prettified 2023-05-09 17:24:24 +02:00
JacobLotz 5630c8aefc fixes for 1d nurbs 2023-05-09 16:01:08 +02:00
Jacob Lotz 80cf146882 Merge branch 'master' into nurbs-auto-knot2edge-dev 2023-05-09 15:06:00 +02:00
JacobLotz c6c2381171 Updated testing in makefile as well. 2023-05-09 14:57:41 +02:00
JacobLotz c7e8d7f99a Updated changelog 2023-05-09 14:28:11 +02:00
JacobLotz 1b8d520eb9 added visit option 2023-05-09 13:50:53 +02:00
JacobLotz d2af3ac8f4 fix make file 2023-05-09 13:25:51 +02:00
Dylan Copeland 8a5a0d038c Fixing another memory leak. 2023-04-24 21:11:39 -07:00
Dylan Copeland 3331d6d9b6 Fixing memory leaks. 2023-04-24 11:07:53 -07:00
Dylan Copeland 47241bd101 Merge branch 'master' of github.com:mfem/mfem into nurbsquad 2023-04-24 09:13:11 -07:00
Jacob Lotz 42feb99ddc Merge branch 'master' into nurbs-auto-knot2edge-dev 2023-04-12 17:39:55 +02:00
Dylan Copeland 261c252e28 Copyright year. 2023-04-10 19:45:40 -07:00
Dylan Copeland 6a09060d1a Merge branch 'master' of https://github.com/mfem/mfem into nurbsquad 2023-04-10 19:41:08 -07:00
Tzanio Kolev fbf9839a3e Merge branch 'master' into mpi_h1lor 2023-04-09 13:07:38 -07:00
EB Chin e21949b16b update copyright date 2023-03-24 20:58:00 -07:00
EB Chin ddc27a60df Merge branch 'master' into mpi_h1lor 2023-03-24 20:55:18 -07:00
Eric B. Chin 36e3062378 make style 2023-03-24 13:47:06 -07:00
EB Chin 102d4ad3f8 fix output 2023-03-24 13:44:44 -07:00
EB Chin 3ef4611c1f Merge branch 'mpi_h1lor' of github.com:mfem/mfem into mpi_h1lor 2023-03-24 13:39:58 -07:00
EB Chin b81b023ea5 simplify parallel get/set dofs 2023-03-24 13:35:57 -07:00
Eric B. Chin 35938b691a astyle changes 2023-03-24 12:58:02 -07:00
EB Chin ada5c8f4e9 update docs 2023-03-24 12:55:34 -07:00
EB Chin 16fede3622 reorganize and consolidate parallel vs serial 2023-03-24 12:34:15 -07:00
Eric B. Chin ebdfac0dd7 fix dual example 2023-03-24 08:39:52 -07:00
EB Chin a89d24843f separate methods for parallel; example refinement 2023-03-23 22:53:18 -07:00
EB Chin 05f18737a0 initial example 2023-03-21 09:18:43 -07:00
EB Chin b06b1c0006 initial support for parallel h1 fields 2023-03-20 13:12:53 -07:00
Ido Akkerman 6b8865705e Merge branch 'nurbsquad' of https://github.com/mfem/mfem into nurbsquad 2023-03-07 09:07:51 +01:00
Ido Akkerman ebcb6cc603 Streamlining interface with NURBSExtension 2023-03-07 09:04:27 +01:00
Dylan Copeland dee6d806dd Adding MFEM_VERIFY with error message to catch the case of a patchwise integrator on a non-NURBS mesh. 2023-03-06 10:12:36 -08:00
Dylan Copeland 0b5be406aa Merge branch 'master' of github.com:mfem/mfem into nurbsquad 2023-03-06 09:39:13 -08:00
Dylan Copeland 10e5ac4403 NNLS system normalization is now optional. 2023-02-27 11:47:05 -08:00
Dylan Copeland f645886ada Putting NNLS instance in ifdef MFEM_USE_LAPACK. 2023-02-22 16:06:58 -08:00
Dylan Copeland 2a974948a6 Merge branch 'master' of github.com:mfem/mfem into nurbsquad 2023-02-22 14:55:17 -08:00
Dylan Copeland e8e6b1b159 Adding unit test for NNLS and an error message when insufficient integration points are used in NNLS. 2023-02-22 14:54:41 -08:00
Dylan Copeland d52d1fea0a Refactored NNLS as a class derived from Solver. 2023-02-22 12:28:50 -08:00
Tzanio Kolev c2e028c916 Merge branch 'master' into nurbs-auto-knot2edge-dev 2023-02-14 10:05:01 -08:00
JacobLotz c18c204279 Fix typo and added extra line 2023-02-10 10:25:15 +01:00
JacobLotz 80a4407f74 Updated refinement documentation in nurbs_ex1.cpp 2023-02-09 13:24:33 +01:00
JacobLotz a4a5f784ab Moved RefineNURBSFromFile to mesh.cpp 2023-02-09 11:49:45 +01:00
JacobLotz 8d3c5ee11c Updated documentation 2023-02-08 21:45:56 +01:00
JacobLotz b8da9d0448 missed a j 2023-02-08 21:32:36 +01:00
JacobLotz 431d3ea432 Improved counter for while loop, MFEM_ASSERT -> MFEM_VERIFY 2023-02-08 21:28:59 +01:00
Dylan Copeland 5ddc41f07e Putting casting back to avoid compiler warning. 2023-02-08 12:13:12 -08:00
JacobLotz 8d4b3657aa fix typos 2023-02-08 21:12:05 +01:00
Dylan Copeland ebbd6dee0c More documentation. 2023-02-08 12:08:36 -08:00
JacobLotz d585f60011 Forced specific refinement for 3D cases 2023-02-08 10:08:54 +01:00
JacobLotz 39c661388e typo 2023-02-07 14:25:11 +01:00
JacobLotz f1eb3267bb make style 2023-02-07 14:18:45 +01:00
JacobLotz 0cde0fd1bb Added documentation, while loop and mfem_error 2023-02-07 14:18:06 +01:00
Dylan Copeland 57483a1921 Adding documentation. 2023-02-06 15:28:54 -08:00
Dylan Copeland 60b82f2fe4 Removed unused variables. 2023-02-03 20:42:25 -08:00
Dylan Copeland 1eab3a2193 Merge branch 'master' of github.com:mfem/mfem into nurbsquad 2023-02-03 19:53:25 -08:00
JacobLotz 8becbfbb7d Added 3D examples and tests 2023-02-03 17:19:01 +01:00
JacobLotz 2ae4b1914a Fix spelling 2023-02-03 15:53:17 +01:00
JacobLotz 813e99a41b Changed name extended -> comprehensive 2023-01-31 13:42:09 +01:00
JacobLotz 15af16215a fix typos in mesh descriptions 2023-01-31 11:34:20 +01:00
JacobLotz bc76844900 Added sample runs to nurbs miniapp and added test including auto edge 2 2023-01-31 11:26:49 +01:00
JacobLotz 8f0d944b26 Added sample runs to nurbs miniapp and added test including auto edge 2023-01-31 11:25:16 +01:00
JacobLotz ea283d2ac7 Merge branch 'nurbs-auto-knot2edge-dev' of github.com:mfem/mfem into nurbs-auto-knot2edge-dev 2023-01-31 11:23:32 +01:00
JacobLotz 242b2c4f91 Added extra test including auto edge to knot. Also sample runs to miniapps/nurbs/nurbs_ex1.cpp. 2023-01-31 11:18:43 +01:00
Tzanio Kolev 15deef3206 Merge branch 'master' into nurbs-auto-knot2edge-dev 2023-01-21 14:57:45 -08:00
Jacob Lotz abc7074b3a Merge branch 'master' into nurbs-auto-knot2edge-dev 2023-01-11 10:32:22 +01:00
Jacob Lotz 18c0719ba2 Merge branch 'master' into nurbs-auto-knot2edge-dev 2023-01-10 12:01:45 +01:00
jelotz cc8cebaa99 fix reinit of variable 2023-01-09 15:14:41 +01:00
jelotz de262c4134 fix reinit of variable 2023-01-09 15:11:46 +01:00
jelotz c0c3618627 make style of just improved documentation 2023-01-09 15:02:24 +01:00
jelotz e1c2bf47d5 fix documentation 2023-01-09 15:00:22 +01:00
jelotz 5403f28eca More reinit fix 2023-01-09 14:48:14 +01:00
jelotz f5cd5b4117 fix reinitialisation of variables 2023-01-09 14:44:03 +01:00
Tzanio Kolev 9cc92e72c2 Merge branch 'master' into nurbsquad 2023-01-08 14:57:17 -08:00
Dylan Copeland 820ececea0 Run tests only if built with lapack. 2022-12-30 15:01:46 -08:00
Dylan Copeland b113b19cd6 Minor 2022-12-30 14:37:50 -08:00
Dylan Copeland 7885381cd8 Adding sample runs. Updating CHANGELOG. Cleaning up NNLS. 2022-12-30 14:21:36 -08:00
Dylan Copeland 6286897c16 Fixing more compiler warnings. 2022-12-29 18:06:25 -08:00
Dylan Copeland c779c2ec8d Fixing mac warnings. 2022-12-29 13:03:56 -08:00
Dylan Copeland a5391d125f Fixing pedantic compiler warnings. 2022-12-29 12:38:06 -08:00
Dylan Copeland 550904ab55 Fixing an issue with dgeqrf in NNLS. Fixing a bug for element-wise integration. Eliminating reduced rule computation in the PA case. 2022-12-28 21:47:17 -08:00
Tzanio Kolev d1bd5c6d95 Merge branch 'master' into nurbsquad 2022-12-28 11:06:41 -08:00
Dylan Copeland 7f63fba18c Moved NURBS patch integration to its own file. 2022-12-27 20:00:41 -08:00
Dylan Copeland 5ac9aa4a47 Improved efficiency of DiffusionIntegrator::AssemblePatchMatrix_fullQuadrature. Improved the interface. 2022-12-27 19:29:11 -08:00
Dylan Copeland 11b8330ae3 Documentation. Assignment operator for Array3D. 2022-12-27 14:11:33 -08:00
Dylan Copeland 58ca949331 Reverting ex1. 2022-12-23 11:49:46 -08:00
Dylan Copeland 4c27a6c252 Moved ex1 to miniapps/nurbs and cleaned it up. 2022-12-23 11:39:46 -08:00
Dylan Copeland 25b23be5e8 Merge branch 'master' of github.com:mfem/mfem into nurbsquad 2022-12-22 10:51:37 -08:00
Dylan Copeland 05151e52eb Added input parameters to choose between full and reduced quadrature. Removed patch DenseMatrix option. 2022-12-22 10:50:25 -08:00
Dylan Copeland d27e88109d Fixing some build issues. 2022-12-05 21:42:34 -08:00
Dylan Copeland e9a5bf3c8f Merge branch 'master' of github.com:mfem/mfem into nurbsquad 2022-12-05 20:29:03 -08:00
Dylan Copeland b58e1e09d3 Implemented operator PA for NURBS patches in AddMultPatchPA, for DiffusionIntegrator. Refactored some setup into functions. 2022-12-05 20:06:31 -08:00
Dylan Copeland 40e3d14449 More optimizations. 2022-10-18 12:25:18 -07:00
Dylan Copeland f5469f73b7 Added NNLS solver for DenseMatrix. Implemented reduced 1D patch rules computed by NNLS. Modified ex1 to compare timing of patch- and element-assembly. 2022-10-07 13:45:11 -07:00
Jacob Lotz 8b1df3016d Removed patch orientation part of CheckPatches completely. Patch orientation is not important anymore 2022-10-05 16:12:25 +02:00
Jacob Lotz 1bfce790ae Uncommented part of CheckPatches 2022-10-05 15:42:31 +02:00
Jacob Lotz e39b7498fc fix Generate knot 2 edge mapping 2022-10-05 15:38:23 +02:00
Jacob Lotz 838cc607b6 Test cases added and clean up 2022-10-03 16:01:10 +02:00
Jacob Lotz fddc4fb9e1 More naming updates 2022-10-03 14:31:04 +02:00
Jacob Lotz 06a49e3e42 Improved naming and prettify KnotInsert 2022-10-03 14:24:50 +02:00
Jacob Lotz 5577701da0 Small bug fixes due to testing. 2D KnotInsert and DegreeElevate works 2022-10-03 10:42:18 +02:00
Jacob Lotz 10b486fe47 Copied edge2knot mapping. Implementation should now be complete. Also make style.
To do:
- Testing
- Remove/adapt CheckPatches: maybe knotvector check is useful?
2022-09-30 16:12:06 +02:00
Jacob Lotz a0d1e57794 Commented out check in CheckPatches. When the implementation is succesfull the orientation/combination/glueing of the patches is much more flexible and this check is not required anymore. This check can then be removed entirely. 2022-09-30 15:49:00 +02:00
Jacob Lotz d370ba7ff3 removed debug statements 2022-09-30 15:39:53 +02:00
Jacob Lotz 99338fd4de Fix 2 bugs, one in constructor where the wrong function is called. One in splitting the order comparison as kv->difference cannot compare knotvectors of different orders. All nurbs miniapps test now succesful 2022-09-30 15:37:37 +02:00
Jacob Lotz d7573ec13b Start implementation extended array of knotvectors. A lot of tests failing... 2022-09-30 15:17:06 +02:00
Jacob Lotz 555dec9d2c Add debug statements 2022-09-30 13:08:22 +02:00
Jacob Lotz cee9e87fde Added KVExt2KV which updates the unique set of knotvectors. Also make style 2022-09-30 13:04:15 +02:00
Jacob Lotz 2380adc193 Fixed naming ussue and added function to verify that both sets of knotvectors are equal 2022-09-30 10:51:39 +02:00
Jacob Lotz 57397e0373 Adds a function which creates an extended set of KnotVectors 2022-09-30 10:26:42 +02:00
Jacob Lotz 5f0b9d52ff Adding a function which checks the direction of the knotvectors 2022-09-30 09:52:03 +02:00
Jacob Lotz cd36c676eb Adding testcase 2022-09-29 17:10:36 +02:00
Dylan Copeland 901a2143b7 Replacing DenseMatrix assembly of patch matrices with SparseMatrix row assembly. 2022-09-06 11:35:24 -07:00
Dylan Copeland 780f383762 Implemented SparseMatrix patch assembly for the transpose (assuming symmetric matrix). 2022-09-02 21:22:54 -07:00
Dylan Copeland 77a58cd1ab Minor improvements. 2022-09-02 15:00:51 -07:00
Dylan Copeland debdaff7b2 Optimized the sum factorization loops in AssemblePatchMatrix. 2022-09-02 14:41:49 -07:00
Dylan Copeland 161163a160 Generalizing for non-unit knot spans. 2022-08-24 15:08:37 -07:00
Dylan Copeland 913c80e05f Debugged the beam-hex-nurbs.mesh example (after DegreeElevate). 2022-08-24 13:58:56 -07:00
Dylan Copeland 1afe29794a Fixing some bugs. 2022-08-17 21:05:58 -07:00
Dylan Copeland b03e51fdac Adding the option for patch-wise matrix assembly on NURBS meshes, with a DiffusionIntegrator example using sum factorization similar to PA. Currently it runs with incorrect results. 2022-08-17 12:11:33 -07:00
Dylan Copeland 12c9aa6207 Added the capability to set element rules that are used instead of the patch rules. 2022-07-13 13:00:45 -07:00
Dylan Copeland 08353d2c4f Some refactoring and documentation. 2022-07-08 10:43:39 -07:00
Dylan Copeland a96b9f873b Implemented element-wise IntegrationRules based on patch 1D rules, with an example in ex1. 2022-06-28 18:07:42 -07:00
Dylan Copeland 6723095508 Adding the capability, with an example, to set a different IntegrationRule on each patch. 2022-06-27 15:23:01 -07:00
Dylan Copeland 6dd313d466 Beginning to implement NURBS patch quadrature rules. 2022-06-22 11:05:10 -07:00
Vladimir Z Tomov f09e08bb8f tri/tet/hex sample runs. 2022-03-31 20:21:45 -07:00
Vladimir Z Tomov 4b1709a418 More complicated test. 2022-03-29 15:21:44 -07:00
Vladimir Z Tomov 5429f7af4a Fixed the fitting error computation. 2022-03-28 16:24:36 -07:00
Vladimir Z Tomov c9accc9abe AssembleElementGrad. 2022-03-25 14:27:02 -07:00
Vladimir Z Tomov 331111c3d3 GetElementVector for limited fitting. 2022-03-24 15:42:05 -07:00
Vladimir Z Tomov d93891f6c1 Data structured and elementEnergy for new fitting. 2022-03-24 13:46:41 -07:00
122 changed files with 9698 additions and 12358 deletions
+5 -2
View File
@@ -72,6 +72,9 @@ jobs:
codecov: NO
- target: opt
codecov: YES
- os: ubuntu-latest
target: dbg
config-opts: 'CPPFLAGS+=-Og'
- os: windows-latest
codecov: NO
- os: windows-latest
@@ -230,7 +233,7 @@ jobs:
metis-dir: ${{ env.METIS_TOP_DIR }}
mfem-dir: ${{ env.MFEM_TOP_DIR }}
config-options: ${{ matrix.config-opts }}
library-only: ${{ matrix.target == 'dbg' }}
library-only: ${{ matrix.target == 'dbg' && matrix.os != 'ubuntu-latest' }}
# Run checks (and only checks) on debug targets
- name: checks
@@ -240,7 +243,7 @@ jobs:
# Note: 'tests' include the unit tests
- name: tests
if: matrix.build-system == 'make' && matrix.target == 'opt'
if: matrix.build-system == 'make' && (matrix.target == 'opt' || matrix.os == 'ubuntu-latest')
run: |
cd ${{ env.MFEM_TOP_DIR }} && make test
+6
View File
@@ -213,6 +213,7 @@ miniapps/meshing/twist
miniapps/meshing/mesh-explorer
miniapps/meshing/shaper
miniapps/meshing/extruder
miniapps/meshing/fit-node-position
miniapps/meshing/trimmer
miniapps/meshing/reflector
miniapps/meshing/mesh-optimizer
@@ -265,11 +266,15 @@ miniapps/navier/*_output
miniapps/nurbs/nurbs_ex1
miniapps/nurbs/nurbs_ex1p
miniapps/nurbs/nurbs_ex11p
miniapps/nurbs/nurbs_patch_ex1
miniapps/nurbs/nurbs_curveint
miniapps/nurbs/refined.mesh
miniapps/nurbs/mesh.*
miniapps/nurbs/sol.*
miniapps/nurbs/mode_*
miniapps/nurbs/Example1*
miniapps/nurbs/sin-fit.mesh
miniapps/nurbs/CurveInt
miniapps/performance/ex1
miniapps/performance/ex1p
@@ -292,6 +297,7 @@ miniapps/tools/display-basis
miniapps/tools/load-dc
miniapps/tools/convert-dc
miniapps/tools/lor-transfer
miniapps/tools/plor-transfer
miniapps/tools/get-values
miniapps/tools/check-tmop-metric
miniapps/tools/tmop-metric-magnitude
+29 -1
View File
@@ -10,6 +10,7 @@
Version 4.5.3 (development)
===========================
- Added curve interpolation method for NURBS.
New and updated examples and miniapps
-------------------------------------
@@ -20,7 +21,10 @@ New and updated examples and miniapps
skewness, and aspect-ratio computed from the Jacobian of the transformation.
- Added a new miniapp for interface and boundary fitting to implicit domains
defined using level-set functions. See miniapps/meshing/pmesh-fitting.cpp
defined using level-set functions. See miniapps/meshing/pmesh-fitting.cpp.
- Added a new miniapp for fitting of selected mesh nodes to specified positions,
while maintaining mesh quality. See miniapps/meshing/fit-node-position.cpp.
- Added new Discontinuous Petrov-Galerkin (DPG) miniapp which includes serial
and parallel examples for diffusion, convection-diffusion, acoustics and
@@ -32,6 +36,8 @@ New and updated examples and miniapps
- Added new SubMesh examples demonstrating source terms and boundary conditions
transferred from SubMesh objects.
- Added a miniapp for interpolation of NURBS.
- Added a new H(div) solvers miniapp in miniapps/hdiv-linear-solver,
demonstrating the use of a matrix-free saddle-point solver methodology,
suitable for high-order discretizations and for GPU acceleration. Examples
@@ -42,8 +48,20 @@ New and updated examples and miniapps
- Moved the distance solver methods from miniapps/shifted to miniapps/common.
- Added a new parallel LOR transfer miniapp, miniapps/tools/plor-transfer, which
mirrors the functionality of the serial LOR transfer miniapp,
miniapps/tools/lor-transfer
Meshing improvements
--------------------
- Added support for free connectivity of NURBS patches allowing for more complex
patch configurations such as C-meshes. This is demonstrated in a new NURBS
miniapp.
- The edge to knot map for NURBS meshes can be determined automatically. It is no
longer needed to specify this in the NURBS mesh. A mesh in the NURBS miniapp
demonstrates this.
- Added new methods in the Mesh class to set and get attributes on NURBS patches
and patch boundaries.
@@ -63,8 +81,16 @@ Discretization improvements
- VectorFEBoundaryFluxLFIntegrator is now supported on device/GPU.
- Added support for partial assembly on NURBS patches and NURBS patch sparse
matrix assembly. Patch matrix assembly includes the option to use reduced
approximate integration rules, computed by the newly implemented non-negative
least-squares (NNLS) solver.
- Added support for p-refined meshes in FindPointsGSLIB.
- Support for parallel transfer of H1 fields using the low-order refined (LOR)
transfer operators in L2ProjectionGridTransfer
Linear and nonlinear solvers
----------------------------
- Updated interface to MUMPS direct solver to support multiple right-hand
@@ -90,6 +116,8 @@ Miscellaneous
- Reorganized files for bilinear form, linear form, and nonlinear form integrators
in the fem/integ/ subdirectory.
- FiniteElementSpace::GetFE has been updated to abort instead of returning NULL for
an empty partition.
Version 4.5.2, released on March 23, 2023
=========================================
+1 -1
View File
@@ -138,7 +138,7 @@ if (MFEM_USE_CUDA)
set(CUDA_FLAGS "-ccbin=${CMAKE_CXX_COMPILER} ${CUDA_FLAGS}")
set(CMAKE_CUDA_HOST_LINK_LAUNCHER ${CMAKE_CXX_COMPILER})
endif()
set(CMAKE_CUDA_FLAGS ${CMAKE_CUDA_FLAGS} ${CUDA_FLAGS})
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${CUDA_FLAGS}")
set(CUSPARSE_FOUND TRUE)
set(CUSPARSE_LIBRARIES "cusparse")
set(CUBLAS_FOUND TRUE)
-2
View File
@@ -19,9 +19,7 @@ RUN apt-get update && \
apt-get install -y libcurl4-openssl-dev libssl-dev
ENV PATH=$PATH:/opt/mfem-view/bin
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/mfem-view/lib:/opt/mfem-view/lib64
ENV DEBIAN_FRONTEND=noninteractive
# The user will see the view on shell into the container
WORKDIR /opt/mfem-view
ENTRYPOINT ["/bin/bash"]
+6 -6
View File
@@ -34,14 +34,14 @@ RUN cd /opt/mfem-env && \
. /opt/spack/share/spack/setup-env.sh && \
spack env activate . && \
spack develop --path /code mfem@master+examples+miniapps && \
spack add mfem@master+examples+miniapps # && \
# spack install
spack add mfem@master+examples+miniapps && \
spack install
# ensure mfem always on various paths
#RUN cd /opt/mfem-env && \
# spack env activate --sh -d . >> /etc/profile.d/z10_spack_environment.sh
RUN cd /opt/mfem-env && \
spack env activate --sh -d . >> /etc/profile.d/z10_spack_environment.sh
# Present the software install when we shell in
# The view is at /opt/mfem-env/.spack-env/view
#WORKDIR /opt/software
#ENTRYPOINT ["/bin/bash", "--rcfile", "/etc/profile", "-l", "-c"]
WORKDIR /opt/software
ENTRYPOINT ["/bin/bash", "--rcfile", "/etc/profile", "-l", "-c"]
+108 -46
View File
@@ -7,21 +7,31 @@ You can use this image for a demo of using mfem! 🎉️
Updated containers are built and deployed on merges to the main branch and releases.
If you want to request a build on demand, you can [manually run the workflow](https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow) thanks to the workflow dispatch event.
### Usage
## Usage
Here is how to build the container. Note that we build so it belongs to the same
namespace as the repository here. "ghcr.io" means "GitHub Container Registry" and
We provide two containers, which you can either build or use directly from
[GitHub packages](https://github.com/orgs/mfem/packages?repo_name=mfem).
- `ghcr.io/mfem/mfem-ubuntu-base`: a "build from scratch" for mfem
- `ghcr.io/mfem/mfem-ubuntu`: a quick build that uses the base container
In the above, "ghcr.io" means "GitHub Container Registry" and
is the [GitHub packages](https://github.com/features/packages) registry that supports
Docker images and other OCI artifacts. From the root of the repository:
Docker images and other OCI artifacts.
### Ubuntu
> Use or build this container for a multi-stage, slimmer base to develop on top of mfem
Note that this container is provided on GitHub packages [here](https://github.com/mfem/mfem/pkgs/container/mfem-ubuntu)
so you don't need to build it. However, if you want to, you can do the following:
```bash
$ docker build -f config/docker/Dockerfile -t ghcr.io/mfem/mfem-ubuntu .
$ docker build -f config/docker/Dockerfile.base -t ghcr.io/mfem/mfem-ubuntu-base .
```
### Shell Ubuntu
To shell into the container:
Note that this will pull the base image. If you want to rebuild it, see [ubuntu base](#ubuntu-base)
below. Once you have built (or prefer to pull) you can shell into the container as follows:
```bash
$ docker run -it ghcr.io/mfem/mfem-ubuntu
@@ -37,39 +47,13 @@ bin etc include lib libexec sbin share var
- Examples are in share/mfem/examples
- Examples are in share/mfem/miniapps
You can read more about interaction with these examples and miniapps below.
### Shell Ubuntu Base
To shell into the container:
Using this container, if you want to develop a tool that _uses_ mfem, you can find the libraries / includes in:
```bash
$ docker run -it ghcr.io/mfem/mfem-ubuntu-base bash
```
Off the bat, you can see mfem libraries are in your path so you can jump into development:
```bash
env | grep mfem
```
```bash
PKG_CONFIG_PATH=/opt/mfem-env/.spack-env/view/lib/pkgconfig:/opt/mfem-env/.spack-env/view/share/pkgconfig:/opt/mfem-env/.spack-env/view/lib64/pkgconfig
PWD=/opt/mfem-env
MANPATH=/opt/mfem-env/.spack-env/view/share/man:/opt/mfem-env/.spack-env/view/man:
CMAKE_PREFIX_PATH=/opt/mfem-env/.spack-env/view
SPACK_ENV=/opt/mfem-env
ACLOCAL_PATH=/opt/mfem-env/.spack-env/view/share/aclocal
LD_LIBRARY_PATH=/opt/mfem-env/.spack-env/view/lib:/opt/mfem-env/.spack-env/view/lib64
PATH=/opt/mfem-env/.spack-env/view/bin:/opt/view/bin:/opt/spack/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
```
#### Examples and MiniApps
If you want to develop a tool that _uses_ mfem, you can find the built libraries in:
```
$ ls /opt/mfem-env/.spack-env/view/
bin etc include lib libexec sbin share var
$ ls include/ | grep mfem
mfem
mfem-performance.hpp
mfem.hpp
```
And yes, this is the working directory when you shell into the container!
@@ -79,6 +63,16 @@ You can find the examples here:
```bash
cd share/mfem/examples
```
Try quickly setting the `LD_LIBRARY_PATH` so we can see the shared libraries
we need:
```bash
export LD_LIBRARY_PATH=/opt/mfem-view/lib:$LD_LIBRARY_PATH
```
And then run:
```bash
$ ./ex0
Options used:
@@ -97,7 +91,6 @@ Number of unknowns: 101
Average reduction factor = 0.140201
```
Try running a few, and look at the associated .cpp file for the source code!
You can also explore the "mini apps," also in share/mfem, but under miniapps.
```bash
@@ -130,18 +123,87 @@ Rule:
Applying rule...done.
```
Have fun!
Have fun! As a reminder, this container is ideal for developing your own
applications that might use mfem, or having a nice environment to test out
examples.
#### Your own App
If you want to develop with your own code base
(and mfem as is in the container) you can bind to somewhere else in the container (e.g., src)
### Ubuntu Base
> Use this build for a development environment with spack and mfem
This container is also [provided on GitHub packages](https://github.com/mfem/mfem/pkgs/container/mfem-ubuntu-base),
however you can build it locally too:
```bash
$ docker run -it ghcr.io/mfem/mfem-ubuntu-base -v $PWD:/src bash
$ docker build -f config/docker/Dockerfile.base -t ghcr.io/mfem/mfem-ubuntu-base .
```
To shell into the container:
```bash
$ docker run -it ghcr.io/mfem/mfem-ubuntu-base bash
```
Change directory to the mfem environment, setup spack, and activate the environment:
```bash
source /opt/spack/share/spack/setup-env.sh
cd /opt/mfem-env/
spack env activate .
```
Note that this environment is installing to the view at `/opt/view`. Since the environment
knows to install mfem from `/code` this means that you could make changes in the container (or bind
`/code` to your container) and then update spack:
```bash
# Note that concretization takes a hot minute!
$ spack install
```
And if you want to load mfem:
```bash
$ spack load mfem
$ env | grep mfem
```
In this development container, you can find the examples and miniapps alongside
mfem under `/code`:
```bash
cd /code/examples
```
```bash
$ ./ex0
```
```console
Options used:
--mesh ../data/star.mesh
--order 1
Number of unknowns: 101
Iteration : 0 (B r, r) = 0.184259
Iteration : 1 (B r, r) = 0.102754
Iteration : 2 (B r, r) = 0.00558141
Iteration : 3 (B r, r) = 1.5247e-05
Iteration : 4 (B r, r) = 1.13807e-07
Iteration : 5 (B r, r) = 6.27231e-09
Iteration : 6 (B r, r) = 3.76268e-11
Iteration : 7 (B r, r) = 6.07423e-13
Iteration : 8 (B r, r) = 4.10615e-15
Average reduction factor = 0.140201
```
This container is likely ideal for someone that wants to develop mfem itself.
For other use cases, we recommend using the slimmer image. As an example,
if you want to develop with your own code base (and mfem as is in the container)
you can bind to somewhere else in the container (e.g., src)
```bash
$ docker run -it ghcr.io/mfem/mfem-ubuntu-base -v $PWD:/code bash
```
In the above, we can pretend your project is in the present working directory (PWD) and we are
binding to source. You can then use the mfem in the container for development, and if you
want to distribute your library or app in a container, you can use the mfem container as the base.
-907
View File
@@ -1,907 +0,0 @@
#include "mfem.hpp"
#include "IPsolver.hpp"
#include "problems.hpp"
#include <fstream>
#include <iostream>
#include <cstdlib>
using namespace std;
using namespace mfem;
InteriorPointSolver::InteriorPointSolver(OptProblem * Problem, ParFiniteElementSpace *Vhin)
: problem(Problem), block_offsetsumlz(5), block_offsetsuml(4), block_offsetsx(3),
saveLogBarrierIterates(false), Vh(Vhin)
{
tol = 1.e-2;
max_iter = 20;
mu_k = 1.0;
sMax = 1.e2;
kSig = 1.e10; // control deviation from primal Hessian
tauMin = 0.8; // control rate at which iterates can approach the boundary
eta = 1.e-4; // backtracking constant
thetaMin = 1.e-4; // allowed violation of the equality constraints
// constants in line-step A-5.4
delta = 1.0;
sTheta = 1.1;
sPhi = 2.3;
// control the rate at which the penalty parameter is decreased
kMu = 0.2;
thetaMu = 1.5;
thetaMax = 1.e6; // maximum constraint violation
// data for the second order correction
kSoc = 0.99;
// equation (18)
gTheta = 1.e-5;
gPhi = 1.e-5;
kEps = 1.e1;
dimU = problem->GetDimU();
dimM = problem->GetDimM();
dimC = problem->GetDimC();
ckSoc.SetSize(dimC);
block_offsetsumlz[0] = 0;
block_offsetsumlz[1] = dimU; // u
block_offsetsumlz[2] = dimM; // m
block_offsetsumlz[3] = dimC; // lambda
block_offsetsumlz[4] = dimM; // zl
block_offsetsumlz.PartialSum();
for(int i = 0; i < block_offsetsuml.Size(); i++) { block_offsetsuml[i] = block_offsetsumlz[i]; }
for(int i = 0; i < block_offsetsx.Size(); i++) { block_offsetsx[i] = block_offsetsuml[i] ; }
// lower-bound for the inequality constraint m >= ml
ml = problem->Getml();
lk.SetSize(dimC); lk = 0.0;
zlk.SetSize(dimM); zlk = 0.0;
mf.SetSize(dimM); mf = 0.0;
linSolver = 0;
MyRank = 0;
iAmRoot = MyRank == 0 ? true : false;
}
double InteriorPointSolver::MaxStepSize(Vector &x, Vector &xl, Vector &xhat, double tau)
{
double alphaMaxloc = 1.0;
double alphaTmp;
for(int i = 0; i < x.Size(); i++)
{
if( xhat(i) < 0. )
{
alphaTmp = -1. * tau * (x(i) - xl(i)) / xhat(i);
alphaMaxloc = min(alphaMaxloc, alphaTmp);
}
}
// alphaMaxloc is the local maximum step size which is
// distinct on each MPI process. Need to compute
// the global maximum step size
double alphaMaxglb;
alphaMaxglb = alphaMaxloc;
return alphaMaxglb;
}
double InteriorPointSolver::MaxStepSize(Vector &x, Vector &xhat, double tau)
{
Vector zero(x.Size()); zero = 0.0;
return MaxStepSize(x, zero, xhat, tau);
}
void InteriorPointSolver::Mult(const Vector &x0, Vector &xf)
{
BlockVector x0block(block_offsetsx); x0block = 0.0;
x0block.GetBlock(0).Set(1.0, x0);
// hard coded initialization :(
x0block.GetBlock(1) = 1.0;
x0block.GetBlock(1).Add(1.0, ml);
BlockVector xfblock(block_offsetsx); xfblock = 0.0;
Mult(x0block, xfblock);
xf.Set(1.0, xfblock.GetBlock(0));
mf.Set(1.0, xfblock.GetBlock(1));
}
void InteriorPointSolver::Mult(const BlockVector &x0, BlockVector &xf)
{
converged = false;
IPNewtonKrylovIters.open("IPNewtonKrylovIters.dat", ios::out | ios::trunc);
BlockVector xk(block_offsetsx), xhat(block_offsetsx); xk = 0; xhat = 0.0;
BlockVector Xk(block_offsetsumlz), Xhat(block_offsetsumlz); Xk = 0.0; Xhat = 0.0;
BlockVector Xhatuml(block_offsetsuml); Xhatuml = 0.0;
Vector zlhat(dimM); zlhat = 0.0;
xk.GetBlock(0).Set(1.0, x0.GetBlock(0));
xk.GetBlock(1).Set(1.0, x0.GetBlock(1));
// running estimate of the final values of the Lagrange multipliers
lk = 0.0;
zlk = 0.0;
for(int i = 0; i < dimM; i++)
{
zlk(i) = 1.e1 * mu_k / (xk(i+dimU) - ml(i));
}
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
Xk.GetBlock(2).Set(1.0, lk);
Xk.GetBlock(3).Set(1.0, zlk);
/* set theta0 = theta(x0)
* thetaMin
* thetaMax
* when theta(xk) < thetaMin and the switching condition holds
* then we ask for the Armijo sufficient decrease of the barrier
* objective to be satisfied, in order to accept the trial step length alphakl
*
* thetaMax controls how the filter is initialized for each log-barrier subproblem
* F0 = {(th, phi) s.t. th > thetaMax}
* that is the filter does not allow for iterates where the constraint violation
* is larger than that of thetaMax
*/
double theta0 = theta(xk);
thetaMin = 1.e-4 * max(1.0, theta0);
thetaMax = 1.e8 * thetaMin;
double Eeval, maxBarrierSolves, Eevalmu0;
bool printOptimalityError; // control optimality error print to console for log-barrier subproblems
maxBarrierSolves = 10;
for(jOpt = 0; jOpt < max_iter; jOpt++)
{
if(iAmRoot)
{
cout << "interior-point solve step " << jOpt << endl;
}
// A-2. Check convergence of overall optimization problem
printOptimalityError = false;
Eevalmu0 = E(xk, lk, zlk, printOptimalityError);
if(Eevalmu0 < tol)
{
converged = true;
if(iAmRoot)
{
IPNewtonKrylovIters.close();
cout << "solved optimization problem :)\n";
}
break;
}
if(jOpt > 0) { maxBarrierSolves = 1; }
for(int i = 0; i < maxBarrierSolves; i++)
{
// A-3. Check convergence of the barrier subproblem
printOptimalityError = true;
Eeval = E(xk, lk, zlk, mu_k, printOptimalityError);
if(Eeval < kEps * mu_k)
{
if(iAmRoot)
{
cout << "solved barrier subproblem :), for mu = " << mu_k << endl;
}
// A-3.1. Recompute the barrier parameter
mu_k = max(tol / 10., min(kMu * mu_k, pow(mu_k, thetaMu)));
// A-3.2. Re-initialize the filter
F1.DeleteAll();
F2.DeleteAll();
}
else
{
break;
}
}
// A-4. Compute the search direction
// solve for (uhat, mhat, lhat)
if(iAmRoot)
{
cout << "\n** A-4. IP-Newton solve **\n";
}
zlhat = 0.0; Xhatuml = 0.0;
// why do we have Xhatuml ....???
// TO DO: remove Xhatuml in favor of passing Xhat
IPNewtonSolve(xk, lk, zlk, zlhat, Xhatuml, mu_k, false);
// assign data stack, X = (u, m, l, zl)
Xk = 0.0;
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
Xk.GetBlock(2).Set(1.0, lk);
Xk.GetBlock(3).Set(1.0, zlk);
// assign data stack, Xhat = (uhat, mhat, lhat, zlhat)
Xhat = 0.0;
for(int i = 0; i < 3; i++)
{
Xhat.GetBlock(i).Set(1.0, Xhatuml.GetBlock(i));
}
Xhat.GetBlock(3).Set(1.0, zlhat);
// A-5. Backtracking line search.
if(iAmRoot)
{
cout << "\n** A-5. Linesearch **\n";
cout << "mu = " << mu_k << endl;
}
lineSearch(Xk, Xhat, mu_k);
if(lineSearchSuccess)
{
if(iAmRoot)
{
cout << "lineSearch successful :)\n";
}
if(!switchCondition || !sufficientDecrease)
{
F1.Append( (1. - gTheta) * thx0);
F2.Append( phx0 - gPhi * thx0);
}
// ----- A-6: Accept the trial point
// print info regarding zl...
xk.GetBlock(0).Add(alpha, Xhat.GetBlock(0));
xk.GetBlock(1).Add(alpha, Xhat.GetBlock(1));
lk.Add(alpha, Xhat.GetBlock(2));
zlk.Add(alphaz, Xhat.GetBlock(3));
projectZ(xk, zlk, mu_k);
}
else
{
if(iAmRoot)
{
cout << "lineSearch not successful :(\n";
cout << "attempting feasibility restoration with theta = " << thx0 << endl;
cout << "no feasibility restoration implemented, exiting now \n";
}
break;
//cout << "feasibility restoration!!! :( :( :(\n";
//problem->feasibilityRestoration(x, 1.e-12);
// break;
}
//
if(jOpt + 1 == max_iter && iAmRoot)
{
cout << "maximum optimization iterations :(\n";
IPNewtonKrylovIters.close();
}
}
// done with optimization routine, just reassign data to xf reference so
// that the application code has access to the optimal point
xf = 0.0;
xf.GetBlock(0).Set(1.0, xk.GetBlock(0));
xf.GetBlock(1).Set(1.0, xk.GetBlock(1));
}
void InteriorPointSolver::FormIPNewtonMat(BlockVector & x, Vector & l, Vector &zl, BlockOperator &Ak)
{
// WARNING: Huu, Hum, Hmu, Hmm should all be Hessian terms of the Lagrangian, currently we
// them by Hessian terms of the objective function and neglect the Hessian of l^T c
Huu = problem->Duuf(x); Hum = problem->Dumf(x);
Hmu = problem->Dmuf(x); Hmm = problem->Dmmf(x);
Vector DiagLogBar(dimM); DiagLogBar = 0.0;
for(int ii = 0; ii < dimM; ii++)
{
DiagLogBar(ii) = zl(ii) / (x(ii+dimU) - ml(ii));
}
if(saveLogBarrierIterates)
{
std::ofstream diagStream;
char diagString[100];
snprintf(diagString, 100, "logBarrierHessiandata/D%d.dat", jOpt);
diagStream.open(diagString, ios::out | ios::trunc);
for(int ii = 0; ii < dimM; ii++)
{
diagStream << setprecision(30) << DiagLogBar(ii) << endl;
}
diagStream.close();
}
delete Wmm;
if(Hmm != nullptr)
{
SparseMatrix * D = new SparseMatrix(DiagLogBar);
Wmm = Add(*Hmm, *D);
delete D;
}
else
{
Wmm = new SparseMatrix(DiagLogBar);
}
delete JuT;
delete JmT;
Ju = problem->Duc(x); JuT = Transpose(*Ju);
Jm = problem->Dmc(x); JmT = Transpose(*Jm);
// IP-Newton system matrix
// Ak = [[H_(u,u) H_(u,m) J_u^T]
// [H_(m,u) W_(m,m) J_m^T]
// [ J_u J_m 0 ]]
Ak.SetBlock(0, 0, Huu); Ak.SetBlock(0, 2, JuT);
Ak.SetBlock(1, 1, Wmm); Ak.SetBlock(1, 2, JmT);
Ak.SetBlock(2, 0, Ju); Ak.SetBlock(2, 1, Jm);
if(Hum != nullptr) { Ak.SetBlock(0, 1, Hum); Ak.SetBlock(1, 0, Hmu); }
}
// perturbed KKT system solve
// determine the search direction
void InteriorPointSolver::IPNewtonSolve(BlockVector &x, Vector &l, Vector &zl, Vector &zlhat, BlockVector &Xhat, double mu, bool socSolve)
{
// solve A x = b, where A is the IP-Newton matrix
BlockOperator A(block_offsetsuml, block_offsetsuml); BlockVector b(block_offsetsuml); b = 0.0;
FormIPNewtonMat(x, l, zl, A);
// [grad_u phi + Ju^T l]
// b = - [grad_m phi + Jm^T l]
// [ c ]
BlockVector gradphi(block_offsetsx); gradphi = 0.0;
BlockVector JTl(block_offsetsx); JTl = 0.0;
Dxphi(x, mu, gradphi);
(A.GetBlock(0,2)).Mult(l, JTl.GetBlock(0));
(A.GetBlock(1,2)).Mult(l, JTl.GetBlock(1));
for(int ii = 0; ii < 2; ii++)
{
b.GetBlock(ii).Set(1.0, gradphi.GetBlock(ii));
b.GetBlock(ii).Add(1.0, JTl.GetBlock(ii));
}
if(!socSolve)
{
problem->c(x, b.GetBlock(2));
}
else
{
b.GetBlock(2).Set(1.0, ckSoc);
}
b *= -1.0;
Xhat = 0.0;
#ifdef MFEM_USE_SUITESPARSE
// Direct solve for IP-Newton saddle-point system
// A = [ [ Huu 0 Ju^T]
// [ 0 D -I ]
// [ Ju -I 0 ]]
if(linSolver == 0)
{
BlockMatrix ABlockMatrix(block_offsetsuml, block_offsetsuml);
for(int ii = 0; ii < 3; ii++)
{
for(int jj = 0; jj < 3; jj++)
{
if(!A.IsZeroBlock(ii, jj))
{
ABlockMatrix.SetBlock(ii, jj, dynamic_cast<SparseMatrix *>(&(A.GetBlock(ii, jj))));
}
}
}
/* direct solve of the 3x3 IP-Newton linear system */
UMFPackSolver ASolver;
SparseMatrix *ASparse = ABlockMatrix.CreateMonolithic();
ASolver.SetOperator(*ASparse);
ASolver.Mult(b, Xhat);
Vector residual(Xhat.Size());
ASparse->Mult(Xhat, residual);
residual.Add(-1.0, b);
delete ASparse;
}
else if(linSolver == 1)
{
// Direct solve for 0,0 Schur complement of IP-Newton system, Huu + Ju^T Wmm Ju,
// where Wmm = D for contact problems
SparseMatrix * Huuloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 0))));
SparseMatrix * Wmmloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(1, 1))));
SparseMatrix * Juloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(2, 0))));
SparseMatrix * JuTloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 2))));
Vector Dvec(dimM); Dvec = 0.0;
Vector one(dimM); one = 1.0;
Wmmloc->Mult(one, Dvec);
SparseMatrix *JuTDJu = Mult_AtDA(*Juloc, Dvec); // Ju^T D Ju
SparseMatrix *Areduced = Add(*Huuloc, *JuTDJu); // Huu + Ju^T D Ju
/* prepare the reduced rhs */
// breduced = bu + Ju^T (bm + Wmm bl)
Vector breduced(dimU); breduced = 0.0;
Vector tempVec(dimM); tempVec = 0.0;
Wmmloc->Mult(b.GetBlock(2), tempVec);
tempVec.Add(1.0, b.GetBlock(1));
JuTloc->Mult(tempVec, breduced);
breduced.Add(1.0, b.GetBlock(0));
// solve the reduced linear system
UMFPackSolver AreducedSolver;
AreducedSolver.SetOperator(*Areduced);
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
// now propagate solved uhat to obtain mhat and lhat
// xm = Ju xu - bl
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
// xl = Wmm xm - bm
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
delete Wmmloc;
delete Huuloc;
delete JuTDJu;
delete Juloc;
delete Areduced;
}
#else
MFEM_VERIFY(linSolver > 1, "linSolver = 0, 1 require MFEM_USE_SUITESPARSE=YES");
#endif
if (linSolver == 2 || linSolver == 3)
{
// Iterative solve for 0,0 Schur complement of IP-Newton system, Huu + Ju^T Wmm Ju,
// where Wmm = D for contact problems
// here the iterative solver is a Jacobi-preconditioned CG-solve
SparseMatrix * Huuloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 0))));
SparseMatrix * Wmmloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(1, 1))));
SparseMatrix * Juloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(2, 0))));
SparseMatrix * JuTloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 2))));
// Vector Dvec(dimM); Dvec = 0.0;
// Vector one(dimM); one = 1.0;
// Wmmloc->Mult(one, Dvec);
// SparseMatrix *JuTDJu = Mult_AtDA(*Juloc, Dvec); // Ju^T D Ju
SparseMatrix *JuTDJu = RAP(*Juloc,*Wmmloc,*Juloc); // Ju^T D Ju
SparseMatrix *Areduced = Add(*Huuloc, *JuTDJu); // Huu + Ju^T D Ju
/* prepare the reduced rhs */
// breduced = bu + Ju^T (bm + Wmm bl)
Vector breduced(dimU); breduced = 0.0;
Vector tempVec(dimM); tempVec = 0.0;
Wmmloc->Mult(b.GetBlock(2), tempVec);
tempVec.Add(1.0, b.GetBlock(1));
JuTloc->Mult(tempVec, breduced);
breduced.Add(1.0, b.GetBlock(0));
/* set up an iterative solver */
int globalNumRows = dimU;
HYPRE_BigInt rowStarts[2];
rowStarts[0] = 0;
rowStarts[1] = dimU;
HypreParMatrix * Ahypre = new HypreParMatrix(MPI_COMM_WORLD, globalNumRows, rowStarts, Areduced);
// CGSolver Asolver(MPI_COMM_WORLD);
HyprePCG Asolver(MPI_COMM_WORLD);
HypreBoomerAMG * Aprec = new HypreBoomerAMG(*Ahypre);
Aprec->SetPrintLevel(0);
if(linSolver == 3)
{
Aprec->SetElasticityOptions(Vh);
}
Aprec->SetSystemsOptions(3,false);
Asolver.SetOperator(*Ahypre);
Asolver.SetPrintLevel(2);
Asolver.SetMaxIter(1000);
// Asolver.SetResidualConvergenceOptions();
Asolver.SetTol(1.e-6);
Asolver.SetPreconditioner(*Aprec);
// Asolver.SetResidualConvergenceOptions();
Asolver.Mult(breduced, Xhat.GetBlock(0));
int num_iterations;
Asolver.GetNumIterations(num_iterations);
cgnum_iterations.Append(num_iterations);
// int numNewtonKrylovIters = -1;
// numNewtonKrylovIters = Asolver.GetNumIterations();
// IPNewtonKrylovIters << numNewtonKrylovIters << endl;
delete Aprec;
delete Ahypre;
// now propagate solved uhat to obtain mhat and lhat
// xm = Ju xu - bl
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
// // xl = Wmm xm - bm
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
delete Wmmloc;
delete Huuloc;
delete JuTDJu;
delete Juloc;
delete Areduced;
}
else if(linSolver > 2)
{
// Iterative solve for 0,0 Schur complement of IP-Newton system, Huu + Ju^T Wmm Ju,
// where Wmm = D for contact problems
// here the iterative solver is a Jacobi-preconditioned CG-solve
SparseMatrix * Huuloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 0))));
SparseMatrix * Wmmloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(1, 1))));
SparseMatrix * Juloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(2, 0))));
SparseMatrix * JuTloc = new SparseMatrix(*dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 2))));
Vector Dvec(dimM); Dvec = 0.0;
Vector one(dimM); one = 1.0;
Wmmloc->Mult(one, Dvec);
SparseMatrix *JuTDJu = Mult_AtDA(*Juloc, Dvec); // Ju^T D Ju
SparseMatrix *Areduced = Add(*Huuloc, *JuTDJu); // Huu + Ju^T D Ju
/* prepare the reduced rhs */
// breduced = bu + Ju^T (bm + Wmm bl)
Vector breduced(dimU); breduced = 0.0;
Vector tempVec(dimM); tempVec = 0.0;
Wmmloc->Mult(b.GetBlock(2), tempVec);
tempVec.Add(1.0, b.GetBlock(1));
JuTloc->Mult(tempVec, breduced);
breduced.Add(1.0, b.GetBlock(0));
/* set up an iterative solver */
GSSmoother AreducedPrec((SparseMatrix &)(*Areduced));
GMRESSolver AreducedSolver;
AreducedSolver.SetOperator(*Areduced);
AreducedSolver.SetAbsTol(1.e-12);
AreducedSolver.SetRelTol(1.e-8);
AreducedSolver.SetMaxIter(500);
AreducedSolver.SetPreconditioner(AreducedPrec);
AreducedSolver.SetPrintLevel(1);
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
// now propagate solved uhat to obtain mhat and lhat
// xm = Ju xu - bl
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
// xl = Wmm xm - bm
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
delete Wmmloc;
delete Huuloc;
delete JuTDJu;
delete Juloc;
delete Areduced;
}
/* backsolve to determine zlhat */
for(int ii = 0; ii < dimM; ii++)
{
zlhat(ii) = -1.*(zl(ii) + (zl(ii) * Xhat(ii + dimU) - mu) / (x(ii + dimU) - ml(ii)) );
}
}
// here Xhat, X will be BlockVectors w.r.t. the 4 partitioning X = (u, m, l, zl)
void InteriorPointSolver::lineSearch(BlockVector& X0, BlockVector& Xhat, double mu)
{
double tau = max(tauMin, 1.0 - mu);
Vector u0 = X0.GetBlock(0);
Vector m0 = X0.GetBlock(1);
Vector l0 = X0.GetBlock(2);
Vector z0 = X0.GetBlock(3);
Vector uhat = Xhat.GetBlock(0);
Vector mhat = Xhat.GetBlock(1);
Vector lhat = Xhat.GetBlock(2);
Vector zhat = Xhat.GetBlock(3);
double alphaMax = MaxStepSize(m0, ml, mhat, tau);
double alphaMaxz = MaxStepSize(z0, zhat, tau);
alphaz = alphaMaxz;
BlockVector x0(block_offsetsx); x0 = 0.0;
x0.GetBlock(0).Set(1.0, u0);
x0.GetBlock(1).Set(1.0, m0);
BlockVector xhat(block_offsetsx); xhat = 0.0;
xhat.GetBlock(0).Set(1.0, uhat);
xhat.GetBlock(1).Set(1.0, mhat);
BlockVector xtrial(block_offsetsx); xtrial = 0.0;
BlockVector Dxphi0(block_offsetsx); Dxphi0 = 0.0;
int maxBacktrack = 20;
alpha = alphaMax;
Vector ck0(dimC); ck0 = 0.0;
Vector zhatsoc(dimM); zhatsoc = 0.0;
BlockVector Xhatumlsoc(block_offsetsuml); Xhatumlsoc = 0.0;
BlockVector xhatsoc(block_offsetsx); xhatsoc = 0.0;
Vector uhatsoc(dimU); uhatsoc = 0.0;
Vector mhatsoc(dimM); mhatsoc = 0.0;
Dxphi(x0, mu, Dxphi0);
Dxphi0_xhat = InnerProduct(Dxphi0, xhat);
double xhat_L2norm = sqrt(InnerProduct(xhat, xhat));
double Dxphi_L2norm = sqrt(InnerProduct(Dxphi0, Dxphi0));
descentDirection = Dxphi0_xhat < 0. ? true : false;
if(descentDirection)
{
cout << "is a descent direction for the log-barrier objective\n";
}
else
{
cout << "is not a descent direction for the log-barrier objective\n";
}
cout << "Dxphi^T xhat / (|| Dxphi ||_2 * || xhat ||_2) = " << Dxphi0_xhat / (xhat_L2norm * Dxphi_L2norm) << endl;
thx0 = theta(x0);
phx0 = phi(x0, mu);
lineSearchSuccess = false;
for(int i = 0; i < maxBacktrack; i++)
{
cout << "\n--------- alpha = " << alpha << " ---------\n";
// ----- A-5.2. Compute trial point: xtrial = x0 + alpha_i xhat
xtrial.Set(1.0, x0);
xtrial.Add(alpha, xhat);
// ------ A-5.3. if not in filter region go to A.5.4 otherwise go to A-5.5.
thxtrial = theta(xtrial);
phxtrial = phi(xtrial, mu);
filterCheck(thxtrial, phxtrial);
if(!inFilterRegion)
{
cout << "not in filter region :)\n";
// ------ A.5.4: Check sufficient decrease
if(!descentDirection)
{
switchCondition = false;
}
else
{
switchCondition = (alpha * pow(abs(Dxphi0_xhat), sPhi) > delta * pow(thx0, sTheta)) ? true : false;
}
cout << "alpha |Dxphi(x0)^T xhat|^sPhi = " << alpha * pow(abs(Dxphi0_xhat), sPhi) << endl;
cout << "delta * theta(x0)^sTheta = " << delta * pow(thx0, sTheta) << endl;
cout << "theta(x0) = " << thx0 << ", thetaMin = " << thetaMin << endl;
cout << "theta(xtrial) = " << thxtrial << ", (1-gTheta) *theta(x0) = " << (1. - gTheta) * thx0 << endl;
cout << "phi(xtrial) = " << phxtrial << ", phi(x0) - gPhi *theta(x0) = " << phx0 - gPhi * thx0 << endl;
// Case I
if(thx0 <= thetaMin && switchCondition)
{
sufficientDecrease = phxtrial <= phx0 + eta * alpha * Dxphi0_xhat ? true : false;
if(sufficientDecrease)
{
if(iAmRoot) { cout << "A-5.4. Case I -- accepted step length.\n"; }
// accept the trial step
lineSearchSuccess = true;
break;
}
}
else
{
if(thxtrial <= (1. - gTheta) * thx0 || phxtrial <= phx0 - gPhi * thx0)
{
if(iAmRoot) { cout << "A-5.4. Case II -- accepted step length.\n"; }
// accept the trial step
lineSearchSuccess = true;
break;
}
}
// A-5.5: Initialize the second-order correction
if((!(thx0 < thxtrial)) && i == 0)
{
cout << "second order correction\n";
problem->c(xtrial, ckSoc);
problem->c(x0, ck0);
ckSoc.Add(alphaMax, ck0);
// A-5.6 Compute the second-order correction.
IPNewtonSolve(x0, l0, z0, zhatsoc, Xhatumlsoc, mu, true);
mhatsoc.Set(1.0, Xhatumlsoc.GetBlock(1));
// alphasoc = MaxStepSize(m0, ml, mhatsoc, tau);
//WARNING: not complete but currently solver isn't entering this region
}
}
else
{
cout << "in filter region :(\n";
}
// include more if needed
alpha *= 0.5;
}
}
void InteriorPointSolver::projectZ(const Vector &x, Vector &z, double mu)
{
double zi;
double mudivmml;
for(int i = 0; i < dimM; i++)
{
zi = z(i);
mudivmml = mu / (x(i + dimU) - ml(i));
z(i) = max(min(zi, kSig * mudivmml), mudivmml / kSig);
}
}
void InteriorPointSolver::filterCheck(double th, double ph)
{
inFilterRegion = false;
if(th > thetaMax)
{
inFilterRegion = true;
}
else
{
for(int i = 0; i < F1.Size(); i++)
{
if(th >= F1[i] && ph >= F2[i])
{
inFilterRegion = true;
break;
}
}
}
}
double InteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, double mu, bool print)
{
double E1, E2, E3;
double sc, sd;
BlockVector gradL(block_offsetsx); gradL = 0.0; // stationarity grad L = grad f + J^T l - z
Vector cx(dimC); cx = 0.0; // feasibility c = c(x)
Vector comp(dimM); comp = 0.0; // complementarity M Z - mu 1
DxL(x, l, zl, gradL);
E1 = gradL.Normlinf();
problem->c(x, cx);
E2 = cx.Normlinf();
for(int ii = 0; ii < dimM; ii++)
{
comp(ii) = x(dimU + ii) * zl(ii) - mu;
}
E3 = comp.Normlinf();
double ll1, zl1;
zl1 = zl.Norml1() / double(dimC + dimM);
ll1 = l.Norml1();
sc = max(sMax, zl1 / (double(dimM)) ) / sMax;
sd = max(sMax, (ll1 + zl1) / (double(dimC + dimM))) / sMax;
if(iAmRoot && print)
{
cout << "evaluating optimality error for mu = " << mu << endl;
cout << "stationarity measure = " << E1 / sd << endl;
cout << "feasibility measure = " << E2 << endl;
cout << "complimentarity measure = " << E3 / sc << endl;
}
return max(max(E1 / sd, E2), E3 / sc);
}
double InteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, bool print)
{
return E(x, l, zl, 0.0, print);
}
double InteriorPointSolver::theta(const BlockVector &x)
{
Vector cx(dimC); cx = 0.0;
problem->c(x, cx);
return sqrt(InnerProduct(cx, cx));
}
// log-barrier objective
double InteriorPointSolver::phi(const BlockVector &x, double mu)
{
double fx = problem->CalcObjective(x);
double logBarrierLoc = 0.0;
for(int i = 0; i < dimM; i++)
{
logBarrierLoc += log(x(dimU+i)-ml(i));
}
double logBarrierGlb = 0.0;
logBarrierGlb = logBarrierLoc;
return fx - mu * logBarrierGlb;
}
// gradient of log-barrier objective with respect to x = (u, m)
void InteriorPointSolver::Dxphi(const BlockVector &x, double mu, BlockVector &y)
{
problem->CalcObjectiveGrad(x, y);
for(int i = 0; i < dimM; i++)
{
y(dimU + i) -= mu / (x(dimU + i) - ml(i));
}
}
// Lagrangian function evaluation
// L(x, l, zl) = f(x) + l^T c(x) - zl^T m
double InteriorPointSolver::L(const BlockVector &x, const Vector &l, const Vector &zl)
{
double fx = problem->CalcObjective(x);
Vector cx(dimC); problem->c(x, cx);
return (fx + InnerProduct(cx, l) - InnerProduct(x.GetBlock(1), zl));
}
void InteriorPointSolver::DxL(const BlockVector &x, const Vector &l, const Vector &zl, BlockVector &y)
{
// evaluate the gradient of the objective with respect to the primal variables x = (u, m)
BlockVector gradxf(block_offsetsx); gradxf = 0.0;
problem->CalcObjectiveGrad(x, gradxf);
SparseMatrix *Jacu, *Jacm, *JacuT, *JacmT;
Jacu = problem->Duc(x); Jacm = problem->Dmc(x);
JacuT = Transpose(*Jacu);
JacmT = Transpose(*Jacm);
JacuT->Mult(l, y.GetBlock(0));
JacmT->Mult(l, y.GetBlock(1));
delete Jacu; delete JacuT;
delete Jacm; delete JacmT;
y.Add(1.0, gradxf);
(y.GetBlock(1)).Add(-1.0, zl);
}
bool InteriorPointSolver::GetConverged() const
{
return converged;
}
void InteriorPointSolver::SetTol(double Tol)
{
tol = Tol;
}
void InteriorPointSolver::SetMaxIter(int max_it)
{
max_iter = max_it;
}
void InteriorPointSolver::SetBarrierParameter(double mu_0)
{
mu_k = mu_0;
}
void InteriorPointSolver::SaveLogBarrierHessianIterates(bool save)
{
MFEM_ASSERT(MyRank == 0 || save == false, "currently can only save logbarrier hessian in serial codes");
saveLogBarrierIterates = save;
}
void InteriorPointSolver::SetLinearSolver(int LinSolver)
{
linSolver = LinSolver;
}
InteriorPointSolver::~InteriorPointSolver()
{
delete Wmm;
delete Huu;
delete Hum;
delete Hmu;
delete Hmm;
delete Hum;
delete Ju;
delete Jm;
delete JuT;
delete JmT;
F1.DeleteAll();
F2.DeleteAll();
block_offsetsx.DeleteAll();
block_offsetsumlz.DeleteAll();
block_offsetsuml.DeleteAll();
ml.SetSize(0);
}
-103
View File
@@ -1,103 +0,0 @@
#include "mfem.hpp"
#include "problems.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
#ifndef IPSOLVER
#define IPSOLVER
class InteriorPointSolver
{
protected:
OptProblem* problem;
double tol;
int max_iter;
double mu_k; // \mu_k
Vector lk, zlk, mf;
double sMax, kSig, tauMin, eta, thetaMin, delta, sTheta, sPhi, kMu, thetaMu;
double thetaMax, kSoc, gTheta, gPhi, kEps;
// filter
Array<double> F1, F2;
// quantities computed in lineSearch
double alpha, alphaz;
double thx0, thxtrial;
double phx0, phxtrial;
bool descentDirection, switchCondition, sufficientDecrease, lineSearchSuccess, inFilterRegion;
double Dxphi0_xhat;
int dimU, dimM, dimC;
Array<int> block_offsetsumlz, block_offsetsuml, block_offsetsx;
Vector ml;
Vector ckSoc;
SparseMatrix * Huu = nullptr;
SparseMatrix * Hum = nullptr;
SparseMatrix * Hmu = nullptr;
SparseMatrix * Hmm = nullptr;
SparseMatrix * Wmm = nullptr;
SparseMatrix * Ju = nullptr;
SparseMatrix * Jm = nullptr;
SparseMatrix * JuT = nullptr;
SparseMatrix * JmT = nullptr;;
int jOpt;
bool converged;
int MyRank;
bool iAmRoot;
bool saveLogBarrierIterates;
int linSolver;
std::ofstream IPNewtonKrylovIters;
ParFiniteElementSpace *Vh;
Array<int> cgnum_iterations;
// not sure if this data is needed or if it can
// all be accounted for in the problem class
// which variables have equality constraints
//Array<int> eqConstrainedVariables;
//Array<double> eqConstrainedValues;
public:
InteriorPointSolver(OptProblem*, ParFiniteElementSpace *);
void Mult(const BlockVector& , BlockVector&); // used when the user wants to be aware of bound-constrained variable m >= ml
void Mult(const Vector&, Vector &); // useful when the user doesn't need to know about bound-constrained variable m >= ml
double MaxStepSize(Vector& , Vector& , Vector& , double);
double MaxStepSize(Vector& , Vector& , double);
void FormIPNewtonMat(BlockVector& , Vector& , Vector& , BlockOperator &);
void IPNewtonSolve(BlockVector& , Vector& , Vector& , Vector&, BlockVector& , double, bool);
void lineSearch(BlockVector& , BlockVector& , double);
void projectZ(const Vector & , Vector &, double);
void filterCheck(double, double);
double E(const BlockVector &, const Vector &, const Vector &, double, bool);
double E(const BlockVector &, const Vector &, const Vector &, bool);
bool GetConverged() const;
// TO DO: include Hessian of Lagrangian
double theta(const BlockVector &);
double phi(const BlockVector &, double);
void Dxphi(const BlockVector &, double, BlockVector &);
double L(const BlockVector &, const Vector &, const Vector &);
void DxL(const BlockVector &, const Vector &, const Vector &, BlockVector &);
void SetTol(double);
void SetMaxIter(int);
void SetBarrierParameter(double);
void SaveLogBarrierHessianIterates(bool);
void SetLinearSolver(int);
Vector GetBoundConstrainedVariable() {return mf;}
Array<int> & GetCGIterNumbers() {return cgnum_iterations;}
virtual ~InteriorPointSolver();
};
#endif
-17
View File
@@ -1,17 +0,0 @@
# OneProcessAMGContact
Be sure to edit the makefile so that it points to a parallel MFEM build
specifically the MFEM_BUILD_DIR
after building exQPContactBlockTL one can
1. run the bash script scalingJobArray.bat via `source scalingJobArray.bat' which will populate the CG iterations required to solve
various linear systems into the data/ subdirectory
2. run the python script data/process.py in order to put the scaling information into the single files algorithmicScaling_Elasticity.dat and algorithmicScaling_noElasticity.dat
in order to see the number of average AMG-CG iterations per optimization solve.
-274
View File
@@ -1,274 +0,0 @@
// Contact example
//
// Compile with: make contact
//
// Sample runs: ./contact -m1 block1.mesh -m2 block2.mesh -at "5 6 7 8"
// Sample runs: ./contact -m1 block1_d.mesh -m2 block2_d.mesh -at "5 6 7 8"
#include <fstream>
#include <iostream>
#include <array>
#include "mfem.hpp"
#include "problems.hpp"
#include "IPsolver.hpp"
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
Mpi::Init(argc, argv);
Hypre::Init();
int linSolver = 2;
int maxIPMiters = 30;
bool iAmRoot = true;
int ref_levels = 0;
OptionsParser args(argc, argv);
args.AddOption(&linSolver, "-linSolver", "--linearSolver", \
"IP-Newton linear system solution strategy.");
args.AddOption(&maxIPMiters, "-IPMiters", "--IPMiters",\
"Maximum number of IPM iterations");
args.AddOption(&ref_levels, "-r", "--mesh_refinement", \
"Mesh Refinement");
args.Parse();
if(!args.Good())
{
args.PrintUsage(cout);
return 1;
}
else
{
if( iAmRoot )
{
args.PrintOptions(cout);
}
}
// Create an instance of the nlp
ExContactBlockTL * contact = new ExContactBlockTL(ref_levels);
int ndofs = contact->GetDimD();
int nconstraints = contact->GetDimS();
std::ofstream problemDimStream;
problemDimStream.open("problemDim.dat", ios::out | ios::trunc);
problemDimStream << ndofs << endl;
problemDimStream.close();
std::ofstream problemDimConstraintsStream;
problemDimConstraintsStream.open("problemDimConstraints.dat", ios::out | ios::trunc);
problemDimConstraintsStream << nconstraints << endl;
problemDimConstraintsStream.close();
// set up a QP-problem
// E(d) = 1 / 2 d^T K d + f^T d
// g(d) = J d + g0
// where K, J, f and g0 are evaluated at d0 (a valid configuration)
// to do: seems more appropriate to evaluate at a valid configuration...
// that is one where the Dirichlet conditions hold... need to pull
// this data from contactBlockTL...
Vector d0(ndofs); d0 = 0.0;
Array<int> DirichletDofs = contact->GetDirichletDofs();
Array<double> DirichletVals = contact->GetDirichletVals();
SparseMatrix *K;
Vector f(ndofs); f = 0.0;
contact->DdE(d0, f); K = contact->DddE(d0);
for(int i = 0; i < DirichletDofs.Size(); i++)
{
d0(DirichletDofs[i]) = DirichletVals[i];
}
SparseMatrix *J;
Vector g0(nconstraints); g0 = 0.0;
J = contact->Ddg(d0); contact->g(d0, g0);
Vector temp(nconstraints);
J->Mult(d0, temp);
g0.Add(-1.0, temp);
// check which rows of the Jacobian are zero!
Vector ei(nconstraints); ei = 0.0;
Vector JTei(ndofs); JTei = 0.0;
double normJTei;
int reduced_nconstraints = 0; // find actual number of constraints
Array<int> nonZeroRows;
for(int i = 0; i < nconstraints; i++)
{
ei(i) = 1.0;
J->MultTranspose(ei, JTei);
// nullify contributions from Dirichlet constrined dofs
for(int j = 0; j < DirichletDofs.Size(); j++)
{
JTei(DirichletDofs[j]) = 0.0;
}
normJTei = sqrt(InnerProduct(JTei, JTei));
if (normJTei > 1.e-12)
{
reduced_nconstraints += 1;
nonZeroRows.Append(i);
}
ei(i) = 0.0;
}
cout << "number of linearized constraints = " << reduced_nconstraints << endl; // 9 constraints
// remove zero rows of the gap function Jacobian and corresponding gap function entries
SparseMatrix * Jreduced = new SparseMatrix(reduced_nconstraints, ndofs);
Vector g0reduced(reduced_nconstraints); g0reduced = 0.0;
for(int i = 0; i < reduced_nconstraints; i++)
{
Array<int> col_tmp;
Vector v_tmp; v_tmp = 0.0;
J->GetRow(nonZeroRows[i], col_tmp, v_tmp);
/* obtain subset of columns of the given nonZero Jacobian row that are not Dirichlet constrained */
bool freeDof;
Array<int> loc_indicies;
for(int j = 0; j < col_tmp.Size(); j++)
{
freeDof = true;
for(int k = 0; k < DirichletDofs.Size(); k++)
{
if(col_tmp[j] == DirichletDofs[k])
{
freeDof = false;
}
}
if(freeDof)
{
loc_indicies.Append(j);
}
}
Array<int> col_tmp_reduced(loc_indicies.Size());
Vector v_tmp_reduced(loc_indicies.Size());
for(int j = 0; j < loc_indicies.Size(); j++)
{
col_tmp_reduced[j] = col_tmp[loc_indicies[j]];
v_tmp_reduced(j) = v_tmp(loc_indicies[j]);
}
Jreduced->SetRow(i, col_tmp_reduced, v_tmp_reduced);
g0reduced(i) = g0(nonZeroRows[i]);
}
QPContactProblem *QPContact = new QPContactProblem(*K, *Jreduced, f, g0reduced);
Mesh * mesh1 = new Mesh("meshes/block1.mesh", 1, 1);
Mesh * mesh2 = new Mesh("meshes/rotatedblock2.mesh", 1, 1);
for(int i = 0; i < ref_levels; i++)
{
mesh1->UniformRefinement();
mesh2->UniformRefinement();
}
int numMeshes = 2;
Mesh *meshArray[numMeshes];
meshArray[0] = mesh1;
meshArray[1] = mesh2;
Mesh mesh(meshArray, numMeshes);
ParMesh pmesh(MPI_COMM_WORLD, mesh);
H1_FECollection fec(1, mesh.Dimension());
ParFiniteElementSpace fespace(&pmesh, &fec, mesh.Dimension(), Ordering::byVDIM);
InteriorPointSolver * QPContactOptimizer = new InteriorPointSolver(QPContact, &fespace);
QPContactOptimizer->SetTol(1.e-6);
QPContactOptimizer->SetLinearSolver(linSolver);
QPContactOptimizer->SetMaxIter(50);
Vector x0(ndofs); x0 = 0.0;
for(int i = 0; i < DirichletDofs.Size(); i++)
{
x0(DirichletDofs[i]) = DirichletVals[i];
}
Vector xf(ndofs); xf = 0.0;
QPContactOptimizer->Mult(x0, xf);
double Einitial = QPContact->E(x0);
double Efinal = QPContact->E(xf);
cout << "Energy objective at initial point = " << Einitial << endl;
cout << "Energy objective at QP optimizer = " << Efinal << endl;
QPContactOptimizer->GetCGIterNumbers().Print(mfem::out, 20);
MFEM_VERIFY(QPContactOptimizer->GetConverged(), "Interior point solver did not converge.");
//Mesh * mesh1 = new Mesh("meshes/block1.mesh", 1, 1);
//Mesh * mesh2 = new Mesh("meshes/rotatedblock2.mesh", 1, 1);
//for(int i = 0; i < ref_levels; i++)
//{
// mesh1->UniformRefinement();
// mesh2->UniformRefinement();
//}
//int gdim = mesh1->Dimension();
//FiniteElementCollection * fec = new H1_FECollection(1, gdim);
//FiniteElementSpace * fespace1 = new FiniteElementSpace(mesh1, fec, gdim, Ordering::byVDIM);
//FiniteElementSpace * fespace2 = new FiniteElementSpace(mesh2, fec, gdim, Ordering::byVDIM);
//
//GridFunction x1_gf(fespace1);
//GridFunction x2_gf(fespace2);
//int ndof1 = fespace1->GetTrueVSize();
//int ndof2 = fespace2->GetTrueVSize();
//int ndof = ndof1 + ndof2;
//for(int i = 0; i < ndof1; i++)
//{
// x1_gf(i) = xf(i);
//}
//for(int i = ndof1; i < ndof; i++)
//{
// x2_gf(i - ndof1) = xf(i);
//}
//mesh1->SetNodalFESpace(fespace1);
//mesh2->SetNodalFESpace(fespace2);
//GridFunction *nodes1 = mesh1->GetNodes();
//GridFunction *nodes2 = mesh2->GetNodes();
//{
// *nodes1 += x1_gf;
// *nodes2 += x2_gf;
//}
//
//ParaViewDataCollection paraview_dc1("QPContactBody1", mesh1);
//paraview_dc1.SetPrefixPath("ParaView");
//paraview_dc1.SetLevelsOfDetail(1);
//paraview_dc1.SetDataFormat(VTKFormat::BINARY);
//paraview_dc1.SetHighOrderOutput(true);
//paraview_dc1.SetCycle(0);
//paraview_dc1.SetTime(0.0);
//paraview_dc1.RegisterField("Body1", &x1_gf);
//paraview_dc1.Save();
//
//ParaViewDataCollection paraview_dc2("QPContactBody2", mesh2);
//paraview_dc2.SetPrefixPath("ParaView");
//paraview_dc2.SetLevelsOfDetail(1);
//paraview_dc2.SetDataFormat(VTKFormat::BINARY);
//paraview_dc2.SetHighOrderOutput(true);
//paraview_dc2.SetCycle(0);
//paraview_dc2.SetTime(0.0);
//paraview_dc2.RegisterField("Body2", &x2_gf);
//paraview_dc2.Save();
//delete fespace1;
//delete fespace2;
//delete fec;
//delete mesh1;
//delete mesh2;
delete QPContact;
delete QPContactOptimizer;
delete K;
delete J;
delete Jreduced;
delete contact;
return 0;
}
-36
View File
@@ -1,36 +0,0 @@
# 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)
# Remove built-in rule
#%: %.cpp
exQPContactBlockTL: exQPContactBlockTL.o problems.o IPsolver.o $(MFEM_LIB_FILE)
$(MFEM_CXX) $(MFEM_FLAGS) exQPContactBlockTL.o problems.o IPsolver.o -o $@ $(MFEM_LIBS)
exQPContactBlockTL.o: exQPContactBlockTL.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
problems.o: problems.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
IPsolver.o: IPsolver.cpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
.PHONY: clean
clean:
rm -f *.o exQPContactBlockTL
-103
View File
@@ -1,103 +0,0 @@
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
3
elements
9
1 5 0 1 3 2 8 9 11 10
1 5 2 3 5 4 10 11 13 12
1 5 4 5 7 6 12 13 15 14
1 5 8 9 11 10 16 17 19 18
1 5 10 11 13 12 18 19 21 20
1 5 12 13 15 14 20 21 23 22
1 5 16 17 19 18 24 25 27 26
1 5 18 19 21 20 26 27 29 28
1 5 20 21 23 22 28 29 31 30
# 0 nothing
# 1 dirichlet bc
# 2 contact
boundary
30
1 3 1 0 2 3
1 3 3 2 4 5
1 3 5 4 6 7
1 3 24 25 27 26
1 3 26 27 29 28
1 3 28 29 31 30
2 3 2 0 8 10
2 3 4 2 10 12
2 3 6 4 12 14
2 3 10 8 16 18
2 3 12 10 18 20
2 3 14 12 20 22
2 3 18 16 24 26
2 3 20 18 26 28
2 3 22 20 28 30
3 3 1 3 11 9
3 3 3 5 13 11
3 3 5 7 15 13
3 3 9 11 19 17
3 3 11 13 21 19
3 3 13 15 23 21
3 3 17 19 27 25
3 3 19 21 29 27
3 3 21 23 31 29
1 3 8 0 1 9
1 3 16 8 9 17
1 3 24 16 17 25
1 3 6 14 15 7
1 3 14 22 23 15
1 3 22 30 31 23
vertices
32
3
-1.0000 0 0
0 0 0
-1.0000 0.3000 0
0 0.3000 0
-1.0000 0.6500 0
0 0.6500 0
-1.0000 1.0000 0
0 1.0000 0
-1.0000 0 0.3000
0 0 0.3000
-1.0000 0.3000 0.3500
0 0.3000 0.3500
-1.0000 0.6500 0.3000
0 0.6500 0.3000
-1.0000 1.0000 0.3000
0 1.0000 0.3000
-1.0000 0 0.6500
0 0 0.6500
-1.0000 0.3000 0.6500
0 0.3000 0.6500
-1.0000 0.6500 0.6500
0 0.6500 0.6500
-1.0000 1.0000 0.6500
0 1.0000 0.6500
-1.0000 0 1.0000
0 0 1.0000
-1.0000 0.3000 1.0000
0 0.3000 1.0000
-1.0000 0.6500 1.0000
0 0.6500 1.0000
-1.0000 1.0000 1.0000
0 1.0000 1.0000
@@ -1,70 +0,0 @@
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
3
# 1 nothing
elements
4
1 5 0 1 3 2 6 7 9 8
1 5 2 3 5 4 8 9 11 10
1 5 6 7 9 8 12 13 15 14
1 5 8 9 11 10 14 15 17 16
# 0 nothing
# 1 dirichlet bc
# 2 contact
boundary
16
1 3 1 0 2 3
1 3 3 2 4 5
1 3 12 13 15 14
1 3 14 15 17 16
3 3 2 0 6 8
3 3 4 2 8 10
3 3 8 6 12 14
3 3 10 8 14 16
2 3 1 3 9 7
2 3 3 5 11 9
2 3 7 9 15 13
2 3 9 11 17 15
1 3 6 0 1 7
1 3 12 6 7 13
1 3 4 10 11 5
1 3 10 16 17 11
vertices
18
3
0.000000000000 0.145770950245 0.443895630208
0.507100000000 0.145770950245 0.443895630208
0.000000000000 0.350937660019 0.294833290227
0.507100000000 0.350937660019 0.294833290227
0.000000000000 0.556104369792 0.145770950245
0.507100000000 0.556104369792 0.145770950245
0.000000000000 0.294833290227 0.649062339981
0.507100000000 0.294833290227 0.649062339981
0.000000000000 0.500000000000 0.500000000000
0.507100000000 0.500000000000 0.500000000000
0.000000000000 0.705166709773 0.350937660019
0.507100000000 0.705166709773 0.350937660019
0.000000000000 0.443895630208 0.854229049755
0.507100000000 0.443895630208 0.854229049755
0.000000000000 0.649062339981 0.705166709773
0.507100000000 0.649062339981 0.705166709773
0.000000000000 0.854229049755 0.556104369792
0.507100000000 0.854229049755 0.556104369792
-897
View File
@@ -1,897 +0,0 @@
using namespace std;
using namespace mfem;
void BasisEval(const Vector xi, Vector &N, DenseMatrix &dNdxi) // dNdxi is 2*4
{
N[0] = 0.25*(1-xi[0])*(1-xi[1]);
N[1] = 0.25*(1+xi[0])*(1-xi[1]);
N[2] = 0.25*(1+xi[0])*(1+xi[1]);
N[3] = 0.25*(1-xi[0])*(1+xi[1]);
dNdxi(0,0) = 0.25*(-1+xi[1]);
dNdxi(0,1) = 0.25*(1-xi[1]);
dNdxi(0,2) = 0.25*(1+xi[1]);
dNdxi(0,3) = 0.25*(-1-xi[1]);
dNdxi(1,0) = 0.25*(-1+xi[0]);
dNdxi(1,1) = 0.25*(-1-xi[0]);
dNdxi(1,2) = 0.25*(1+xi[0]);
dNdxi(1,3) = 0.25*(1-xi[0]);
}
void BasisEvalDerivs(const Vector xi, Vector& N, DenseMatrix& dNdxi,
DenseMatrix& dN2dxi)
{
N[0] = 0.25*(1-xi[0])*(1-xi[1]);
N[1] = 0.25*(1+xi[0])*(1-xi[1]);
N[2] = 0.25*(1+xi[0])*(1+xi[1]);
N[3] = 0.25*(1-xi[0])*(1+xi[1]);
dNdxi.SetSize(2,4); dNdxi = 0.0;
dN2dxi.SetSize(3,4);
dN2dxi = 0.0; // first row dxi2, second detadxi, third deta2
dNdxi(0,0) = 0.25*(-1+xi[1]); dNdxi(0,1) = 0.25*(1-xi[1]);
dNdxi(0,2) = 0.25*(1+xi[1]); dNdxi(0,3) = 0.25*(-1-xi[1]);
dNdxi(1,0) = 0.25*(-1+xi[0]); dNdxi(1,1) = 0.25*(-1-xi[0]);
dNdxi(1,2) = 0.25*(1+xi[0]); dNdxi(1,3) = 0.25*(1-xi[0]);
dN2dxi(1,0) = 0.25; dN2dxi(1,1) = -0.25; dN2dxi(1,2) = 0.25;
dN2dxi(1,3) = -0.25;
}
// returns the vector and matrix form of the shape functions and its derivative
void BasisVectorDerivs(const Vector xi, DenseMatrix& N, DenseMatrix& dNdxi,
DenseMatrix& ddNdxi)
{
N.SetSize(3,12); N = 0.0;
N(0,0) = 0.25*(1-xi[0])*(1-xi[1]); N(0,3) = 0.25*(1+xi[0])*(1-xi[1]);
N(0,6) = 0.25*(1+xi[0])*(1+xi[1]); N(0,9) = 0.25*(1-xi[0])*(1+xi[1]);
N(1,1) = 0.25*(1-xi[0])*(1-xi[1]); N(1,4) = 0.25*(1+xi[0])*(1-xi[1]);
N(1,7) = 0.25*(1+xi[0])*(1+xi[1]); N(1,10) = 0.25*(1-xi[0])*(1+xi[1]);
N(2,2) = 0.25*(1-xi[0])*(1-xi[1]); N(2,5) = 0.25*(1+xi[0])*(1-xi[1]);
N(2,8) = 0.25*(1+xi[0])*(1+xi[1]); N(2,11) = 0.25*(1-xi[0])*(1+xi[1]);
dNdxi.SetSize(3*2, 3*4); dNdxi = 0.0;
dNdxi(0,0) = 0.25*(-1+xi[1]); dNdxi(0,3) = 0.25*(1-xi[1]);
dNdxi(0,6) = 0.25*(1+xi[1]); dNdxi(0,9) = 0.25*(-1-xi[1]);
dNdxi(1,1) = 0.25*(-1+xi[1]); dNdxi(1,4) = 0.25*(1-xi[1]);
dNdxi(1,7) = 0.25*(1+xi[1]); dNdxi(1,10) = 0.25*(-1-xi[1]);
dNdxi(2,2) = 0.25*(-1+xi[1]); dNdxi(2,5) = 0.25*(1-xi[1]);
dNdxi(2,8) = 0.25*(1+xi[1]); dNdxi(2,11) = 0.25*(-1-xi[1]);
dNdxi(3,0) = 0.25*(-1+xi[0]); dNdxi(3,3) = 0.25*(-1-xi[0]);
dNdxi(3,6) = 0.25*(1+xi[0]); dNdxi(3,9) = 0.25*(1-xi[0]);
dNdxi(4,1) = 0.25*(-1+xi[0]); dNdxi(4,4) = 0.25*(-1-xi[0]);
dNdxi(4,7) = 0.25*(1+xi[0]); dNdxi(4,10) = 0.25*(1-xi[0]);
dNdxi(5,2) = 0.25*(-1+xi[0]); dNdxi(5,5) = 0.25*(-1-xi[0]);
dNdxi(5,8) = 0.25*(1+xi[0]); dNdxi(5,11) = 0.25*(1-xi[0]);
ddNdxi.SetSize(3*4, 3*4); ddNdxi = 0.0;
ddNdxi(3,0) = 0.25; ddNdxi(3,3) = -0.25;
ddNdxi(3,6) = 0.25; ddNdxi(3,9) = -0.25;
ddNdxi(4,1) = 0.25; ddNdxi(4,4) = -0.25;
ddNdxi(4,7) = 0.25; ddNdxi(4,10) = -0.25;
ddNdxi(5,2) = 0.25; ddNdxi(5,5) = -0.25;
ddNdxi(5,8) = 0.25; ddNdxi(5,11) = -0.25;
ddNdxi(6,0) = 0.25; ddNdxi(6,3) = -0.25;
ddNdxi(6,6) = 0.25; ddNdxi(6,9) = -0.25;
ddNdxi(7,1) = 0.25; ddNdxi(7,4) = -0.25;
ddNdxi(7,7) = 0.25; ddNdxi(7,10) = -0.25;
ddNdxi(8,2) = 0.25; ddNdxi(8,5) = -0.25;
ddNdxi(8,8) = 0.25; ddNdxi(8,11) = -0.25;
}
void cross(const Vector a, const Vector b, Vector& c)
{
assert(a.Size()==3);
c.SetSize(3);
c[0] = a[1]*b[2] - a[2]*b[1];
c[1] = -a[0]*b[2] + b[0]*a[2];
c[2] = a[0]*b[1] - a[1]*b[0];
}
// a outer b
void outer(const Vector a, const Vector b, DenseMatrix& c)
{
int m = a.Size();
int n = b.Size();
assert(c.Height()==m);
assert(c.Width() ==n);
for (int i=0; i<m; i++)
{
for (int j=0; j<n; j++)
{
c(i,j) = a[i]*b[j];
}
}
}
// dphidxi 2*4
// coords 4*3
void ComputeNormal(const DenseMatrix& dphidxi, const DenseMatrix& coords,
Vector& normal, double& nnorm)
{
DenseMatrix dxdxi(2,3);
Mult(dphidxi, coords, dxdxi);
Vector dxdxi1(3);
Vector dxdxi2(3);
dxdxi.GetRow(0,dxdxi1);
dxdxi.GetRow(1,dxdxi2);
cross(dxdxi1, dxdxi2, normal); // is there a cross product? no
// VectorCrossProductCoefficient::Eval has hard-coded cross product
nnorm = normal.Norml2( );
normal /= nnorm;
}
void SlaveToMaster(const DenseMatrix& m_coords, const Vector& s_x, Vector& xi)
{
bool converged = false;
bool pt_on_elem = false;
int dim = 3;
xi.SetSize(dim-1);
xi = 0.0;
int max_iter = 15;
double off_el_xi = 1e-2;
double proj_newton_tol = 1e-13;
double proj_max_gap = 0.5;
Vector gap_v(dim);
// warm start from linear solution
for (int it=0; it<max_iter; it++)
{
//cout<<it<<endl;
Vector m_N(4);
m_N = 0.;
DenseMatrix m_dN(2,4);
m_dN = 0.;
DenseMatrix m_dN2(3,4);
m_dN2 = 0.;
BasisEvalDerivs(xi, m_N, m_dN, m_dN2);
Vector x_c(dim);
m_coords.MultTranspose(m_N, x_c);
gap_v = s_x;
gap_v -= x_c;
DenseMatrix m_dx(2,3);
m_dx = 0.;
Mult(m_dN, m_coords, m_dx);
Vector r(dim-1);
r = 0.0;
m_dx.Mult(gap_v, r);
if (r.Normlinf() < proj_newton_tol)
{
converged = true;
break;
}
DenseMatrix drdxi(dim-1,dim-1);
drdxi = 0.;
MultABt(m_dx, m_dx, drdxi); // m_dx * m_dx.T
drdxi *= -1.0;
DenseMatrix m_dx2(3,3); m_dx2 = 0.0;
Mult(m_dN2,m_coords, m_dx2);
//m_d2x = m_dN(:,:,2) * m_elem_coords(1:4,:); //m_dN(:,:,2) is 3*4
for (int d=0; d<3; d++)
{
DenseMatrix Mtemp(2,2); Mtemp = 0.0;
Mtemp(0,0) = m_dx2(0,d); Mtemp(0,1) = m_dx2(1,d);
Mtemp(1,0) = m_dx2(1,d); Mtemp(1,1) = m_dx2(2,d);
drdxi.Add(gap_v[d], Mtemp);
}
//cond_num = rcond(drdxi); condition number?
//drdxi.TestInversion();
DenseMatrixInverse drdxi_inv(drdxi);
Vector xi_tmp(dim-1);
drdxi_inv.Mult(r,xi_tmp);
xi -= xi_tmp;
}
if (!converged)
{
xi = 0.0;
}
off_el_xi += 1 ; // tolerance of offset of xi outside [-1,1]
//cout<<gap_v.Norml2()<<" " <<xi.Normlinf()<<endl;
//
// Discuss with Frank... what is happening here
if (gap_v.Norml2() < proj_max_gap && xi.Normlinf() <= off_el_xi)
{
pt_on_elem = true;
}
if (pt_on_elem)
{
//cout << "convergence of node to segment projection? " << converged << endl;
//for(int i = 0; i < 2; i++)
//{
// cout << "xi_" << i << " = " << xi(i) << endl;
//}
}
MFEM_VERIFY(pt_on_elem == true, "xi went out of bounds");
MFEM_VERIFY(converged == true, "projection didn't converge");
}
// m_coords is expected to be 4 * 3
void ComputeGapJacobian(const Vector x_s, const Vector xi,
const DenseMatrix m_coords,
double& gap, Vector& normal, Vector& dgdxm, Vector& dgdxs)
{
Vector m_N(4);
DenseMatrix m_dN(2,4);
DenseMatrix m_dN2(3,4);
BasisEvalDerivs(xi, m_N, m_dN, m_dN2);
Vector x_c(3);
m_coords.MultTranspose(m_N, x_c);
Vector gap_v(3); gap_v = 0.0;
gap_v = x_s;
gap_v -= x_c;
DenseMatrix m_dx(2,3);
Mult(m_dN, m_coords, m_dx);
double nnorm = 0;
ComputeNormal(m_dN, m_coords, normal, nnorm);
gap = gap_v * normal; // gap function value, dot product between vectors
//dr_dx = zeros(2,4,3); % nsegment, nodes in quad, ndim
DenseMatrix dr_dx_res1(4,3); dr_dx_res1 = 0.;
DenseMatrix dr_dx_res2(4,3); dr_dx_res2 = 0.;
Vector m_dxrow1(3);
m_dx.GetRow(0, m_dxrow1);
outer(m_N, m_dxrow1, dr_dx_res1);// 4*1 times 1*3
dr_dx_res1 *= -1.0;
Vector m_dxrow2(3);
m_dx.GetRow(1, m_dxrow2);
outer(m_N, m_dxrow2, dr_dx_res2);// 4*1 times 1*3
dr_dx_res2 *= -1.0;
Vector m_dNrow1(4); m_dN.GetRow(0, m_dNrow1);
Vector m_dNrow2(4); m_dN.GetRow(1, m_dNrow2);
DenseMatrix dr_dx_res1_tmp(4,3); dr_dx_res1_tmp = 0.;
DenseMatrix dr_dx_res2_tmp(4,3); dr_dx_res2_tmp = 0.;
outer(m_dNrow1, gap_v, dr_dx_res1_tmp);// 4*1 times 1*3
outer(m_dNrow2, gap_v, dr_dx_res2_tmp);// 4*1 times 1*3
dr_dx_res1 += dr_dx_res1_tmp; // outer product in vector?
dr_dx_res2 += dr_dx_res2_tmp;
DenseMatrix K_dxidx1(2,2); // 2*2
K_dxidx1 = 0.;
MultABt(m_dx, m_dx, K_dxidx1); // m_dx * m_dx.T
Vector v_dxidx2(4);
m_coords.Mult(gap_v, v_dxidx2); // m_coords * gap_v; // 4*3 * 3 = 4
DenseMatrix K_dxidx2(2,2); K_dxidx2 = 0.0;
Vector m_dN2row1(4); m_dN2.GetRow(0, m_dN2row1);
Vector m_dN2row2(4); m_dN2.GetRow(1, m_dN2row2);
Vector m_dN2row3(4); m_dN2.GetRow(2, m_dN2row3);
// how to get 2nd order? multidimensional matrix?
K_dxidx2(0,0) = m_dN2row1 * v_dxidx2; // how would 4*1 * 1*4 be computed?
K_dxidx2(0,1) = m_dN2row2 * v_dxidx2;
K_dxidx2(1,0) = m_dN2row2 * v_dxidx2;
K_dxidx2(1,1) = m_dN2row3 * v_dxidx2;
DenseMatrix K_dxidx(2,2);
K_dxidx -= K_dxidx1;
K_dxidx += K_dxidx2;
// resize the vectors and matrices
Vector dxidx(24); dxidx = 0.0;
Vector drdx_r(24); drdx_r = 0.0;
for (int i=0; i<4; i++)
{
for (int j=0; j<3; j++)
{
drdx_r[4*j+i] = dr_dx_res1(i,j);
drdx_r[4*j+i+12] = dr_dx_res2(i,j);
}
}
//drdx_r(1:4*3,1) = reshape(dr_dx_res(:,:,1),4*3,1);
//drdx_r(4*3+1:2*4*3,1) = reshape(dr_dx_res(:,:,2),4*3,1);
DenseMatrix drdx_K(24,24); drdx_K = 0.;
for (int i =0; i<12; i++)
{
drdx_K(i,i) = K_dxidx(0,0);
drdx_K(i,12+i) = K_dxidx(0,1);
drdx_K(12+i,i) = K_dxidx(1,0);
drdx_K(12+i,12+i) = K_dxidx(1,1);
}
DenseMatrixInverse drdxK_inv(drdx_K);
drdxK_inv.Mult(drdx_r,dxidx);
// LinearSolve (drdx_K,drdx_r, dxidx) ; //???
dxidx *= -1.0;
Vector drdxs_r(6);
drdxs_r[0] = m_dx(0,0); drdxs_r[1] = m_dx(0,1); drdxs_r[2] = m_dx(0,2);
drdxs_r[3] = m_dx(1,0); drdxs_r[4] = m_dx(1,1); drdxs_r[5] = m_dx(1,2);
DenseMatrix drdxs_K(6,6); drdxs_K = 0.;
for (int i=0; i<3; i++)
{
drdxs_K(i,i) = K_dxidx(0,0);
drdxs_K(i,3+i) = K_dxidx(0,1);
drdxs_K(i+3,i) = K_dxidx(1,0);
drdxs_K(i+3,i+3) = K_dxidx(1,1);
}
Vector dxidxs(6); dxidxs = 0.0;
DenseMatrixInverse drdxsK_inv(drdxs_K);
drdxsK_inv.Mult(drdxs_r,dxidxs);
dxidxs *= -1.0;
//dxidxs = -drdxs_K\drdxs_r;
//dxidx = reshape(dxidx, 4,3,2); dxidxs = reshape(dxidxs, 1,3,2);
dgdxm.SetSize(12); dgdxm = 0.;
DenseMatrix dgdxm_tmp(4,3);
outer(m_N, normal,dgdxm_tmp);
for (int i=0; i<4; i++)
{
for (int j=0; j<3; j++)
{
dgdxm[3*i+j] = -dgdxm_tmp(i,j);
}
}
//dxidx_M = -m_dN(1:2,:,1) * (m_coords(1:4,:)*normal'); % this turns out to be 0
dgdxs.SetSize(3);
dgdxs += normal;
//dgdxs = dgdxs + dxidx_M(1) * dxidxs(:,:,1) + dxidx_M(2) * dxidxs(:,:,2);
};
void ComputeGapHessian(const Vector x_s, const Vector xi,
const DenseMatrix m_coords,
DenseMatrix& dg2dx)
{
Vector m_N(4);
DenseMatrix m_dN(2,4);
DenseMatrix m_dN2(3,4);
BasisEvalDerivs(xi, m_N, m_dN, m_dN2);
int dim = 3;
int num_dofs1 = dim;
int num_dofs2 = 4*dim;
int num_dofs = num_dofs1 + num_dofs2;
dg2dx.SetSize(num_dofs,num_dofs); dg2dx = 0.0;
Vector x_c(3);
m_coords.MultTranspose(m_N,x_c);
Vector gap_v(3); gap_v = 0.0;
gap_v = x_s;
gap_v -= x_c;
DenseMatrix m_dx(2,3);
Mult(m_dN, m_coords, m_dx);
DenseMatrix m_dx2(3,3); m_dx2 = 0.0;
Mult(m_dN2,m_coords, m_dx2);
double nnorm = 0.0;
Vector normal(3); normal = 0.0;
ComputeNormal(m_dN, m_coords, normal, nnorm);
double gap = gap_v * normal; // gap function value, dot product between vectors
DenseMatrix M(2,2); M = 0.0;
MultABt(m_dx, m_dx, M);
DenseMatrix f(2, num_dofs2); f = 0.0;
for (int d=0; d<3; d++)
{
DenseMatrix Mtemp(2,2); Mtemp = 0.0;
Mtemp(0,0) = m_dx2(0,d); Mtemp(0,1) = m_dx2(1,d);
Mtemp(1,0) = m_dx2(1,d); Mtemp(1,1) = m_dx2(2,d);
M.Add(-gap_v[d], Mtemp);
Vector m_dxcol(2); m_dx.GetColumn(d, m_dxcol);
DenseMatrix ftmp(2,4);
outer(m_dxcol, m_N, ftmp);
ftmp *= -1;
ftmp.Add( gap_v[d], m_dN); // 2*4
for (int j=0; j<4; j++)
{
assert(d+3*j<num_dofs2);
f(0,d+j*3) = ftmp(0,j);
f(1,d+j*3) = ftmp(1,j);
}
}
//fprintf('hess dxidxm\n');
DenseMatrixInverse Minv(M);
DenseMatrix dxidxm(2,num_dofs2); dxidxm = 0.0;
Minv.Mult(f, dxidxm);
//LinearSolve??
//dxidxm = M\f;
DenseMatrix nde2(2,2); nde2 = 0.0;
DenseMatrix Nndx2(2,num_dofs2); Nndx2 = 0.0;
for (int d=0; d<3; d++)
{
DenseMatrix ndetmp(2,2); ndetmp = 0.0;
ndetmp(0,0) = normal(d)*m_dx2(0,d); ndetmp(0,1) = normal(d)*m_dx2(1,d);
ndetmp(1,0) = normal(d)*m_dx2(1,d); ndetmp(1,1) = normal(d)*m_dx2(2,d);
nde2 += ndetmp;
for (int j=0; j<4; j++)
{
assert(d+3*j<num_dofs2);
Nndx2(0,d+j*3) = normal[d]*m_dN(0,j);
Nndx2(1,d+j*3) = normal[d]*m_dN(1,j);
}
}
DenseMatrix Ndn(2,num_dofs2); Ndn = 0.0;
Ndn += Nndx2;
AddMult(nde2, dxidxm, Ndn);
DenseMatrix M2(2,2); M2 = 0.0;
MultABt(m_dx, m_dx, M2);
DenseMatrixInverse M2inv(M2);
DenseMatrix diag2(2,2); diag2(0,0) = 1.0; diag2(1,1) = 1.0;
DenseMatrix m_con(2,2); m_con = 0.0;
M2inv.Mult(diag2, m_con);
DenseMatrix dg2dxm(num_dofs2, num_dofs2); dg2dxm = 0.0;
DenseMatrix dg2dxm_tmp(num_dofs2,2); dg2dxm_tmp = 0.0;
MultAtB(Ndn, m_con, dg2dxm_tmp);
Mult(dg2dxm_tmp, Ndn, dg2dxm);
dg2dxm *= gap;
DenseMatrix dg2dxm_tmp2(num_dofs2,num_dofs2); dg2dxm_tmp2 = 0.0;
MultAtB(Nndx2, dxidxm, dg2dxm_tmp2);
dg2dxm.Add(-1.0, dg2dxm_tmp2);
dg2dxm_tmp = 0.0;
MultAtB(dxidxm, nde2, dg2dxm_tmp);
AddMult_a(-1.0, dg2dxm_tmp, dxidxm, dg2dxm);
dg2dxm_tmp2 = 0.0;
MultAtB(dxidxm, Nndx2, dg2dxm_tmp2);
dg2dxm.Add(-1.0, dg2dxm_tmp2);
Vector v_dxidx2(4);
m_coords.Mult(gap_v, v_dxidx2); // m_coords * gap_v; // 4*3 * 3 = 4
DenseMatrix K_dxidx2(2,2); K_dxidx2 = 0.0;
Vector m_dN2row1(4); m_dN2.GetRow(0, m_dN2row1);
Vector m_dN2row2(4); m_dN2.GetRow(1, m_dN2row2);
Vector m_dN2row3(4); m_dN2.GetRow(2, m_dN2row3);
K_dxidx2(0,0) = m_dN2row1 * v_dxidx2; // how would 4*1 * 1*4 be computed?
K_dxidx2(0,1) = m_dN2row2 * v_dxidx2;
K_dxidx2(1,0) = m_dN2row2 * v_dxidx2;
K_dxidx2(1,1) = m_dN2row3 * v_dxidx2;
DenseMatrix K_dxidx(2,2);
K_dxidx -= M2;
K_dxidx += K_dxidx2;
Vector drdxs_r(6);
drdxs_r[0] = m_dx(0,0); drdxs_r[1] = m_dx(0,1); drdxs_r[2] = m_dx(0,2);
drdxs_r[3] = m_dx(1,0); drdxs_r[4] = m_dx(1,1); drdxs_r[5] = m_dx(1,2);
DenseMatrix drdxs_K(6,6); drdxs_K = 0.;
for (int i=0; i<3; i++)
{
drdxs_K(i,i) = K_dxidx(0,0);
drdxs_K(i,3+i) = K_dxidx(0,1);
drdxs_K(i+3,i) = K_dxidx(1,0);
drdxs_K(i+3,i+3) = K_dxidx(1,1);
}
Vector dxidxs(6);
DenseMatrixInverse drdxsK_inv(drdxs_K);
drdxsK_inv.Mult(drdxs_r,dxidxs);
dxidxs *= -1.0;
//dxidxs = -drdxs_K\drdxs_r;
DenseMatrix dxidxs_m(2,3); dxidxs_m = 0.0;
dxidxs_m(0,0) = dxidxs[0]; dxidxs_m(0,1) = dxidxs[1]; dxidxs_m(0,2) = dxidxs[2];
dxidxs_m(1,0) = dxidxs[3]; dxidxs_m(1,1) = dxidxs[4]; dxidxs_m(1,2) = dxidxs[5];
DenseMatrix dtao1dxs(3,3); dtao1dxs = 0.0;
DenseMatrix dtao2dxs(3,3); dtao2dxs = 0.0;
Vector dxidxs_row1(3); dxidxs_row1 = 0.0; Vector dxidxs_row2(3);
dxidxs_row2 = 0.0;
Vector mdx2_row1(3); mdx2_row1 = 0.0; Vector mdx2_row2(3); mdx2_row2 = 0.0;
Vector mdx2_row3(3); mdx2_row3 = 0.0;
dxidxs_m.GetRow(0,dxidxs_row1);
dxidxs_m.GetRow(1,dxidxs_row2);
m_dx2.GetRow(0,mdx2_row1);
m_dx2.GetRow(1,mdx2_row2);
m_dx2.GetRow(2,mdx2_row3);
DenseMatrix dtaotmp(3,3); dtaotmp = 0.0;
outer(mdx2_row1, dxidxs_row1,dtaotmp);
dtao1dxs += dtaotmp; dtaotmp = 0.0;
outer(mdx2_row2, dxidxs_row1,dtaotmp);
dtao1dxs += dtaotmp; dtaotmp = 0.0;
outer(mdx2_row2, dxidxs_row2, dtaotmp);
dtao2dxs += dtaotmp; dtaotmp = 0.0;
outer(mdx2_row3, dxidxs_row2, dtaotmp);
dtao2dxs += dtaotmp; dtaotmp = 0.0;
DenseMatrix dtaodxs(3,3); dtaodxs = 0.0; //tao = tao1 cross tao2
for (int d=0; d<3; d++)
{
Vector dtao1dxs_tmp(3); dtao1dxs_tmp = 0.0;
dtao1dxs.GetColumn(d,dtao1dxs_tmp);
Vector m_dxrow(3); m_dx.GetRow(1, m_dxrow);
Vector dtaodxs_tmp(3); dtaodxs_tmp = 0.0;
cross(dtao1dxs_tmp, m_dxrow, dtaodxs_tmp);
Vector dtaodxs_tmp2(3); dtaodxs_tmp2 = 0.0;
m_dx.GetRow(0, m_dxrow);
dtao1dxs_tmp = 0.0; // reuse the same vector for dtao2
dtao2dxs.GetColumn(d,dtao1dxs_tmp);
cross(m_dxrow, dtao1dxs_tmp, dtaodxs_tmp2);
dtaodxs_tmp2 += dtaodxs_tmp;
dtaodxs.SetCol(d, dtaodxs_tmp2);
}
DenseMatrix dndxs(3,3); dndxs = 0.0; dndxs += dtaodxs; dndxs *= 1.0/nnorm;
DenseMatrix dndxs_tmp(3,3); dndxs_tmp = 0.0;
outer(normal, normal, dndxs_tmp);
AddMult_a(-1/nnorm, dndxs_tmp, dtaodxs, dndxs);
DenseMatrix dgvdxs(3,3); dgvdxs = 0.0;
MultAtB(m_dx, dxidxs_m, dgvdxs);
dgvdxs *= -1;
for (int d=0; d<3; d++)
{
dgvdxs(d,d) += 1.0;
}
//dxidxs: 2*3
DenseMatrix dg2dxs(3,3); dg2dxs = 0.0;
DenseMatrix dg2dxs_tmp(3,2); dg2dxs_tmp = 0.0;
MultAtB(dxidxs_m, nde2, dg2dxs_tmp);
AddMult_a(-1.0, dg2dxs_tmp, dxidxs_m, dg2dxs);
DenseMatrix dg2dxs_tmp2(3,3); dg2dxs_tmp2 = 0.0;
MultAtB(dgvdxs, dndxs, dg2dxs_tmp2);
dg2dxs += dg2dxs_tmp2;
dg2dxs_tmp2 = 0.0;
MultAtB(dndxs, dndxs_tmp, dg2dxs_tmp2);
AddMult(dg2dxs_tmp2, dgvdxs, dg2dxs);
DenseMatrix Ne(3,12), Be(6,12), dBe(12,12);
BasisVectorDerivs(xi, Ne, Be, dBe);
DenseMatrix dtao1dxm(3,12); dtao1dxm.CopyRows(Be, 0, 2);
DenseMatrix dtao2dxm(3,12); dtao2dxm.CopyRows(Be, 3, 5);
Vector m_coords_v(12);
for (int i=0; i<4; i++)
{
for (int j=0; j<3; j++)
{
m_coords_v[i*3+j] = m_coords(i,j);
}
}
for (int i=0; i<2; i++)
{
Vector dxidxm_tmp(num_dofs2); dxidxm_tmp = 0.0;
dxidxm.GetRow(i,dxidxm_tmp);
DenseMatrix dBe_tmp(3,12);
dBe_tmp.CopyRows(dBe,i*3,(i+1)*3-1);
DenseMatrix dtaodxm_tmp(12,12); dtaodxm_tmp = 0.0;
outer(m_coords_v, dxidxm_tmp, dtaodxm_tmp);
AddMult(dBe_tmp, dtaodxm_tmp, dtao1dxm);
//dtao1dxm += dBe(:,:,i)*reshape(m_coords(1:4,:)',12,1)*reshape(dxidxm(i,:),1,12); % 3*12
dBe_tmp = 0.0;
dBe_tmp.CopyRows(dBe,(i+2)*3,(i+3)*3-1);
AddMult(dBe_tmp, dtaodxm_tmp, dtao2dxm);
}
DenseMatrix dtaodxm(3,12); dtaodxm = 0.0;//tao = tao1 cross tao2
for (int d=0; d<12; d++)
{
Vector dtaodxm_tmp(3); dtaodxm_tmp = 0.0;
Vector dtaodxm_tmp2(3); dtaodxm_tmp2 = 0.0;
Vector tmp1(3); tmp1 = 0.0; dtao1dxm.GetColumn(d,tmp1);
Vector m_dxrow2(3); m_dx.GetRow(1, m_dxrow2);
Vector m_dxrow1(3); m_dx.GetRow(0, m_dxrow1);
Vector tmp2(3); tmp2 = 0.0; dtao2dxm.GetColumn(d,tmp2);
cross(tmp1, m_dxrow2, dtaodxm_tmp);
cross(m_dxrow1,tmp2, dtaodxm_tmp2);
dtaodxm_tmp += dtaodxm_tmp2;
dtaodxm.SetCol(d, dtaodxm_tmp);
}
DenseMatrix dndxm(3,12); dndxm = 0.0;
dndxm += dtaodxm;
dndxm *= 1.0/nnorm;
AddMult_a(-1/nnorm, dndxs_tmp, dtaodxm, dndxm); //dndxs_tmp = normal'*normal
DenseMatrix dgvdxm(3,12); dgvdxm = 0.0;
dgvdxm -= Ne;
for (int i=0; i<2; i++)
{
Vector dxidxm_tmp(num_dofs2); dxidxm_tmp = 0.0;
dxidxm.GetRow(i,dxidxm_tmp);
DenseMatrix Be_tmp(3,12);
Be_tmp.CopyRows(Be,i*3,(i+1)*3-1);
DenseMatrix dgvdxm_tmp(12,12); dgvdxm_tmp = 0.0;
outer(m_coords_v, dxidxm_tmp, dgvdxm_tmp);
AddMult_a(-1.0, Be_tmp, dgvdxm_tmp, dgvdxm);
}
DenseMatrix dg2dxsxm(3,12); dg2dxsxm = 0.0;
DenseMatrix dg2dxsxm_tmp(3,3); dg2dxsxm_tmp = 0.0;
MultAtB(dgvdxs, dndxm, dg2dxsxm);
MultAtB(dndxs, dndxs_tmp, dg2dxsxm_tmp);
AddMult(dg2dxsxm_tmp, dgvdxm, dg2dxsxm); // += dndxs'*normal'*normal*dgvdxm;
DenseMatrix dgvdxsxmn(3,12); dgvdxsxmn = 0.0;
DenseMatrix dgvdxsxmn_tmp(3,2); dgvdxsxmn_tmp = 0.0;
MultAtB(dxidxs_m, nde2, dgvdxsxmn_tmp); //dxidxs_m: 2*3
AddMult_a(-1.0, dgvdxsxmn_tmp, dxidxm, dgvdxsxmn);
for (int i =0; i<2; i++)
{
DenseMatrix Be_tmp(3,12);
Be_tmp.CopyRows(Be,i*3,(i+1)*3-1);
Vector dxidxs_row(3); dxidxs_row = 0.0; dxidxs_m.GetRow(i,dxidxs_row);
DenseMatrix dgvdxsxmn_tmp2(3,3); dgvdxsxmn_tmp2 = 0.0;
outer(dxidxs_row, normal, dgvdxsxmn_tmp2);
AddMult_a(-1.0, dgvdxsxmn_tmp2, Be_tmp, dgvdxsxmn);
}
dg2dxsxm += dgvdxsxmn;
DenseMatrix dg2dxmxs(12,3); dg2dxmxs = 0.0;
DenseMatrix dg2dxmxs_tmp(12,3); dg2dxmxs_tmp = 0.0;
MultAtB(dgvdxm, dndxs, dg2dxmxs);
MultAtB(dndxm, dndxs_tmp, dg2dxmxs_tmp);
AddMult(dg2dxmxs_tmp, dgvdxs, dg2dxmxs);
DenseMatrix dgvdxmxsn(12,3); dgvdxmxsn = 0.0;
DenseMatrix dgvdxmxsn_tmp(12,2); dgvdxmxsn_tmp = 0.0;
MultAtB(dxidxm, nde2, dgvdxmxsn_tmp);
dgvdxmxsn_tmp *= -1.0;
AddMult(dgvdxmxsn_tmp, dxidxs_m, dgvdxmxsn);
for (int i =0; i<2; i++)
{
DenseMatrix Be_tmp(3,12);
Be_tmp.CopyRows(Be,i*3,(i+1)*3-1);
Be_tmp.Transpose(); // Be is now 12*3
Vector dxidxs_row(3); dxidxs_row = 0.0; dxidxs_m.GetRow(i,dxidxs_row);
DenseMatrix dgvdxmxsn_tmp2(3,3); dgvdxmxsn_tmp2 = 0.0;
outer(normal, dxidxs_row, dgvdxmxsn_tmp2);
AddMult_a(-1.0, Be_tmp, dgvdxmxsn_tmp2, dgvdxmxsn);
}
dg2dxmxs += dgvdxmxsn;
dg2dx.CopyMN(dg2dxs, 0, 0);
dg2dx.CopyMN(dg2dxm, 3, 3);
dg2dx.CopyMN(dg2dxsxm, 0, 3);
dg2dx.CopyMN(dg2dxmxs, 3, 0);
};
void NodeSegConPairs(const Vector x1, const Vector xi2,
const DenseMatrix coords2,
double& node_g, Vector& node_dg, DenseMatrix& node_dg2)
{
double gap = 0.0;
Vector normal(3); normal = 0.0;
Vector dgdxm(12); dgdxm = 0.0;
Vector dgdxs(3); dgdxs = 0.0;
ComputeGapJacobian(x1, xi2, coords2, gap, normal, dgdxm, dgdxs);
node_g = gap;
node_dg.SetSize(12+3);
for (int i=0; i<3; i++) { node_dg[i] = dgdxs[i]; }
for (int i=0; i<12; i++) { node_dg[i+3] = dgdxm[i]; }
DenseMatrix dg2dx(15,15); dg2dx = 0.0;
DenseMatrix dgvdxmxsn(12,3); dgvdxmxsn = 0.0;
ComputeGapHessian(x1, xi2, coords2, dg2dx);
node_dg2.SetSize(15,15);
node_dg2 = dg2dx;
/*
if(obj.space1.conns{e1}(i)==150) % for debugging purpose
v1 = 1:3;
v2 = 1:12;
%v1 = ones(1,3)
%v2 = ones(1,12)
v2 = reshape(v2,4,3);
x1n1 = x1 + 0.01*v1;
coords2n1 = coords2 + 0.001*v2;
[xi2n1, gapv1, ~, ~] = SlaveToMaster(obj, coords2n1, x1n1);
[gapn1, n1,dgdxmn1, dgdxsn1] = ComputeGapJacobian(obj, x1n1, xi2n1, coords2n1);
x1n2 = x1 - 0.01*v1;
coords2n2 = coords2 - 0.001*v2;
[xi2n2, gapv2, ~, ~] = SlaveToMaster(obj, coords2n2, x1n2);
[gapn2, n2,dgdxmn2, dgdxsn2] = ComputeGapJacobian(obj, x1n2, xi2n2, coords2n2);
fprintf('fd\n');
%gapv1-gapv2
[dgdxsn1(:)',dgdxmn1(:)'] - [dgdxsn2(:)',dgdxmn2(:)']
%dgdxsn1-dgdxsn2
fprintf('code\n');
v2n = v2';
%dg2dx(1:3,1:3)*0.04*ones(3,1)
temp = zeros(12,3);
for i = 1:4
temp1 = dg2dx(3+(i-1)*3+1:3+i*3,1:3);
temp((i-1)*3+1:i*3,:) = temp1';
end
temp2 = zeros(3,12);
for i = 1:4
temp3 = dg2dx(1:3,3+(i-1)*3+1:3+i*3);
temp2(:,(i-1)*3+1:i*3) = temp3';
end
%dg2dx
%dg2dx(4:end,1:3) = temp;
%dg2dx(1:3,4:end) = temp2;
%dgvdxm * 0.002*v2n(:)
(dg2dx*[0.02*v1(:)',0.002*v2n(:)']')'
%dg2dx(4:end,1:3)
end*/
};
// coordsm : (npoints*4, 3) use what class?
// m_conn: (npoints*4)
void Assemble_Contact(const int m, const int npoints, const int ndofs,
const Vector x_s,
const Vector xi, const DenseMatrix coordsm, const Array<int> s_conn,
const Array<int> m_conn, Vector& g, SparseMatrix& M,
std::vector<SparseMatrix>& dM)
{
int ndim = 3;
g.SetSize(m);
g = 0.0;
//SparseMatrix M(m, n); // M needs to be the correct size
//dM.resize(m); // needs to clear?
double g_tmp = 0.;
Vector dg(4*ndim+ndim);
dg = 0.;
DenseMatrix dg2(4*ndim+ndim,4*ndim+ndim);
dg2 = 0.;
for (int i=0; i<npoints; i++)
{
Vector x1(ndim);
x1[0] = x_s[i*ndim];
x1[1] = x_s[i*ndim+1];
x1[2] = x_s[i*ndim+2];
Vector xi2(ndim-1);
xi2[0] = xi[i*(ndim-1)];
xi2[1] = xi[i*(ndim-1)+1];
DenseMatrix coords2(4,3);
coords2.CopyRows(coordsm, i*4,(i+1)*4-1);
//how to get coords2?
dg = 0.0;
dg2 = 0.;
NodeSegConPairs(x1, xi2, coords2, g_tmp, dg, dg2);
g[s_conn[i]] = g_tmp; // should be unique
Array<int> m_conn_i(4);
m_conn.GetSubArray(4*i, 4, m_conn_i);
Array<int> node_conn(5);
node_conn[0] = s_conn[i];
for (int j=0; j<4; j++)
{
node_conn[j+1] = m_conn_i[j];
}
Array<int> M_i_tmp(1);
M_i_tmp[0] = s_conn[i];
//j_idx = (node_conn-1)*obj.disp_field.num_components +repmat((1:obj.disp_field.num_components)', 1, length(node_conn{i}));
Array<int> j_idx(5*ndim); j_idx = 0;
for (int j=0; j< 5; j++)
{
for (int k=0; k<ndim; k++)
{
j_idx[j*ndim+k] = node_conn[j]*ndim+k;
}
}
DenseMatrix M_v_tmp(1, ndim*(4+1)); // SetData now?
M_v_tmp.SetRow(0, dg);
M.AddSubMatrix(M_i_tmp, j_idx, M_v_tmp);
Array<int> dM_i(ndim*(4+1));
Array<int> dM_j(ndim*(4+1));
for (int j=0; j< ndim*(4+1); j++)
{
dM_i[j] = j_idx[j];
dM_j[j] = j_idx[j];
}
dM[s_conn[i]].AddSubMatrix(dM_i,dM_j, dg2);
dM[s_conn[i]].Finalize();
dM[s_conn[i]].Threshold(0.0);
dM[s_conn[i]].SortColumnIndices();
}
M.Finalize();
M.Threshold(0.0);
M.SortColumnIndices();
};
File diff suppressed because it is too large Load Diff
-396
View File
@@ -1,396 +0,0 @@
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include <set>
using namespace std;
using namespace mfem;
#ifndef PROBLEM_DEFS
#define PROBLEM_DEFS
// abstract OptProblem class
// of the form
// min_(u,m) f(u,m) s.t. c(u,m)=0 and m>=ml
// the primal variable (u, m) is represented as a BlockVector
class OptProblem
{
protected:
int dimU, dimM, dimC;
Array<int> block_offsetsx;
Vector ml;
public:
OptProblem();
virtual double CalcObjective(const BlockVector &) const = 0;
virtual void Duf(const BlockVector &, Vector &) const = 0;
virtual void Dmf(const BlockVector &, Vector &) const = 0;
void CalcObjectiveGrad(const BlockVector &, BlockVector &) const;
virtual SparseMatrix* Duuf(const BlockVector &) = 0;
virtual SparseMatrix* Dumf(const BlockVector &) = 0;
virtual SparseMatrix* Dmuf(const BlockVector &) = 0;
virtual SparseMatrix* Dmmf(const BlockVector &) = 0;
virtual void c(const BlockVector &, Vector &) const = 0;
virtual SparseMatrix* Duc(const BlockVector &) = 0;
virtual SparseMatrix* Dmc(const BlockVector &) = 0;
// TO DO: include Hessian terms of constraint c
// TO DO: include log-barrier lumped-mass and pass that
// to the optimizer
//virtual SparseMatrix* GetLogBarrierLumpedMass() = 0;
int GetDimU() const { return dimU; };
int GetDimM() const { return dimM; };
int GetDimC() const { return dimC; };
Vector Getml() const { return ml; };
~OptProblem();
};
// abstract ContactProblem class
// of the form
// min_d e(d) s.t. g(d) >= 0
// TO DO: add functionality for gap function Hessian apply
class ContactProblem : public OptProblem
{
protected:
int dimD;
int dimS;
Array<int> block_offsetsx;
public:
//ContactProblem(int, int); // constructor
ContactProblem();
void InitializeParentData(int, int);
double CalcObjective(const BlockVector &) const; // objective e
void Duf(const BlockVector &, Vector &) const;
void Dmf(const BlockVector &, Vector &) const;
SparseMatrix* Duuf(const BlockVector &);
SparseMatrix* Dumf(const BlockVector &);
SparseMatrix* Dmuf(const BlockVector &);
SparseMatrix* Dmmf(const BlockVector &);
void c(const BlockVector &, Vector &) const;
SparseMatrix* Duc(const BlockVector &);
SparseMatrix* Dmc(const BlockVector &);
virtual double E(const Vector &) const = 0; // objective e(d) (energy function)
virtual void DdE(const Vector &, Vector &) const = 0; // gradient of objective De / Dd
virtual SparseMatrix* DddE(const Vector &) = 0; // Hessian of objective D^2 e / D d^2
virtual void g(const Vector &, Vector &) const = 0; // inequality constraint g(d) >= 0 (gap function)
virtual SparseMatrix* Ddg(const Vector &) = 0; // Jacobian of inequality constraint Dg / Dd
int GetDimD() const { return dimD; };
int GetDimS() const { return dimS; };
virtual ~ContactProblem();
};
class ObstacleProblem : public ContactProblem
{
protected:
// data to define energy objective function e(d) = 0.5 d^T K d - f^T d, g(d) = d >= 0
// stiffness matrix used to define objective
BilinearForm *Kform;
LinearForm *fform;
Array<int> empty_tdof_list; // needed for calls to FormSystemMatrix
SparseMatrix K;
SparseMatrix *J;
FiniteElementSpace *Vh;
Vector f;
public :
ObstacleProblem(FiniteElementSpace* , double (*fSource)(const Vector &));
double E(const Vector &) const;
void DdE(const Vector &, Vector &) const;
SparseMatrix* DddE(const Vector &);
void g(const Vector &, Vector &) const;
SparseMatrix* Ddg(const Vector &);
// TO DO: include lumped-mass for the log-barrier term
//SparseMatrix* GetLogBarrierLumpedMass();
virtual ~ObstacleProblem();
};
class DirichletObstacleProblem : public ContactProblem
{
protected:
// data to define energy objective function e(d) = 0.5 d^T K d - f^T d, g(d) = d + \psi >= 0
// stiffness matrix used to define objective
BilinearForm *Kform;
LinearForm *fform;
Array<int> ess_tdof_list; // needed for calls to FormSystemMatrix
SparseMatrix *K;
SparseMatrix *J;
FiniteElementSpace *Vh;
Vector f;
Vector psi;
Vector xDC;
public :
DirichletObstacleProblem(FiniteElementSpace*, Vector&, double (*fSource)(const Vector &), double (*obstacleSource)(const Vector &), Array<int> tdof_list, bool);
double E(const Vector &) const;
void DdE(const Vector &, Vector &) const;
SparseMatrix* DddE(const Vector &);
void g(const Vector &, Vector &) const;
SparseMatrix* Ddg(const Vector &);
virtual ~DirichletObstacleProblem();
};
// abstract out technology for removing null rows of the Jacobian from an existing contact problem
class ReducedContactProblem : public ContactProblem
{
protected:
Array<int> activeConstraints;
Array<int> fixedDofs;
ContactProblem * contact;
int dimSin;
public:
ReducedContactProblem(ContactProblem * contact, Array<int> activeConstraints, Array<int> fixedDofs);
double E(const Vector &) const;
void DdE(const Vector &, Vector &) const;
SparseMatrix* DddE(const Vector &);
void g(const Vector &, Vector &) const;
SparseMatrix* Ddg(const Vector &);
virtual ~ReducedContactProblem();
};
class QPContactProblem : public ContactProblem
{
protected:
SparseMatrix *K;
SparseMatrix *J;
Vector f;
Vector g0;
public:
QPContactProblem(const SparseMatrix, const SparseMatrix, const Vector, const Vector);
double E(const Vector &) const;
void DdE(const Vector &, Vector &) const;
SparseMatrix* DddE(const Vector &);
void g(const Vector &, Vector &) const;
SparseMatrix* Ddg(const Vector &);
virtual ~QPContactProblem();
};
typedef int Index;
typedef double Number;
class ExContactBlockTL : public ContactProblem
{
public:
double E(const Vector &) const;
void DdE(const Vector &, Vector &) const;
SparseMatrix* DddE(const Vector &);
void g(const Vector &, Vector &) const;
SparseMatrix* Ddg(const Vector &);
FiniteElementSpace GetVh1();
FiniteElementSpace GetVh2();
public:
/** default constructor */
ExContactBlockTL(int );
/** default destructor */
virtual ~ExContactBlockTL();
///**@name Overloaded from TNLP */
///** Method to return some info about the nlp */
//virtual bool get_nlp_info(
// Index& n,
// Index& m,
// Index& nnz_jac_g,
// Index& nnz_h_lag,
// IndexStyleEnum& index_style
//);
///** Method to return the bounds for my problem */
//virtual bool get_bounds_info(
// Index n,
// Number* x_l,
// Number* x_u,
// Index m,
// Number* g_l,
// Number* g_u
//);
///** Method to return the starting point for the algorithm */
//virtual bool get_starting_point(
// Index n,
// bool init_x,
// Number* x,
// bool init_z,
// Number* z_L,
// Number* z_U,
// Index m,
// bool init_lambda,
// Number* lambda
//);
/* Method to return the objective value */
virtual bool eval_f(
Index n,
const Number* x,
bool new_x,
Number& obj_value
) const;
/* Method to return the gradient of the objective */
virtual bool eval_grad_f(
Index n,
const Number* x,
bool new_x,
Number* grad_f
) const;
/* Method to return the constraint residuals */
virtual bool eval_g(
Index n,
const Number* x,
bool new_x,
Index m,
Number* cons
) const;
/* Method to return:
1) The structure of the Jacobian (if "values" is NULL)
2) The values of the Jacobian (if "values" is not NULL)
*/
virtual bool eval_jac_g(
Index n,
const Number* x,
bool new_x,
Index m,
Index nele_jac,
Index* iRow,
Index* jCol,
Number* values
) const;
/* Method to return:
* 1) The structure of the Hessian of the Lagrangian (if "values" is NULL)
* 2) The values of the Hessian of the Lagrangian (if "values" is not NULL)
*/
virtual bool eval_h(
Index n,
const Number* x,
bool new_x,
Number obj_factor,
Index m,
const Number* lambda,
bool new_lambda,
Index nele_hess,
Index* iRow,
Index* jCol,
Number* values
);
///** This method is called when the algorithm is complete so the TNLP can store/write the solution */
//virtual void finalize_solution(
// SolverReturn status,
// Index n,
// const Number* x,
// const Number* z_L,
// const Number* z_U,
// Index m,
// const Number* g,
// const Number* lambda,
// Number obj_value,
// const IpoptData* ip_data,
// IpoptCalculatedQuantities* ip_cq
//);
private:
void update_g() const;
void update_jac();
void update_hess();
private:
/**@name Methods to block default compiler methods.
*
* The compiler automatically generates the following three methods.
* Since the default compiler implementation is generally not what
* you want (for all but the most simple classes), we usually
* put the declarations of these methods in the private section
* and never implement them. This prevents the compiler from
* implementing an incorrect "default" behavior without us
* knowing. (See Scott Meyers book, "Effective C++")
*/
ExContactBlockTL(
const ExContactBlockTL&
);
ExContactBlockTL& operator=(
const ExContactBlockTL&
);
Array<int> attr;
Array<int> m_attr;
Array<int> s_conn; // connectivity of the second/slave mesh
std::string mesh_file1;
std::string mesh_file2;
Mesh* mesh1;
Mesh* mesh2;
FiniteElementCollection* fec1;
FiniteElementCollection* fec2;
FiniteElementSpace* fespace1;
FiniteElementSpace* fespace2;
Array<int> ess_tdof_list1;
Array<int> ess_tdof_list2;
GridFunction nodes0;
GridFunction* nodes1;
GridFunction* nodes2;
mutable GridFunction* x1;
mutable GridFunction* x2;
LinearForm* b1;
LinearForm* b2;
PWConstCoefficient* lambda1_func;
PWConstCoefficient* lambda2_func;
PWConstCoefficient* mu1_func;
PWConstCoefficient* mu2_func;
BilinearForm* a1;
BilinearForm* a2;
mfem::Vector lambda1;
mfem::Vector lambda2;
mfem::Vector mu1;
mfem::Vector mu2;
mutable mfem::Vector xyz;
std::set<int> bdryVerts2;
int dim;
// degrees of freedom of both meshes
int ndof_1;
int ndof_2;
int ndofs;
// number of nodes for each mesh
int nnd_1;
int nnd_2;
int nnd;
int npoints;
SparseMatrix A1;
mfem::Vector B1, X1;
SparseMatrix A2;
mfem::Vector B2, X2;
SparseMatrix* K;
mutable mfem::Vector gapv;
mutable mfem::Vector m_xi;
mutable mfem::Vector xs;
mutable Array<int> m_conn; // only works for linear elements that have 4 vertices!
mutable DenseMatrix* coordsm;
mutable SparseMatrix* M;
mutable std::vector<SparseMatrix>* dM;
Array<int> Dirichlet_dof;
Array<double> Dirichlet_val;
public:
Mesh * GetMesh1() {return mesh1;}
Mesh * GetMesh2() {return mesh2;}
Array<int> GetDirichletDofs() {return Dirichlet_dof;}
Array<double> GetDirichletVals() {return Dirichlet_val;}
};
#endif
+1 -1
View File
@@ -22,7 +22,7 @@ using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Parse command line options.
const char *mesh_file = "../data/star.mesh";
string mesh_file = "../data/star.mesh";
int order = 1;
OptionsParser args(argc, argv);
+1 -1
View File
@@ -26,7 +26,7 @@ int main(int argc, char *argv[])
Hypre::Init();
// 2. Parse command line options.
const char *mesh_file = "../data/star.mesh";
string mesh_file = "../data/star.mesh";
int order = 1;
OptionsParser args(argc, argv);
+349
View File
@@ -0,0 +1,349 @@
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
/** Mass integrator (u⋅d, v⋅d) restricted to the boundary of a domain */
class VectorBoundaryDirectionalMassIntegrator: public BilinearFormIntegrator
{
private:
VectorCoefficient &direction;
int vdim;
int oa, ob;
const double k;
public:
/// Construct an integrator with coefficient 1.0
VectorBoundaryDirectionalMassIntegrator(const double k,
VectorCoefficient &direction,
const int oa=1, const int ob=1)
: k(k), vdim(direction.GetVDim()), direction(direction),
oa(oa), ob(ob) { }
using BilinearFormIntegrator::AssembleElementMatrix;
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Tr,
DenseMatrix &elmat)
{
int dof = el.GetDof();
Vector shape(dof), vec(vdim);
out << Tr.Attribute - 1 << " " << dof << " LHSElement" << std::endl;
elmat.SetSize(dof*vdim);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int intorder = oa * el.GetOrder() + ob; // <------ user control
ir = &IntRules.Get(Tr.GetGeometryType(), intorder); // of integration order
}
DenseMatrix elmat_scalar(dof);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
// Set the integration point in the face and the neighboring element
Tr.SetIntPoint(&ip);
// Access the neighboring element's integration point
direction.Eval(vec, Tr, ip);
double val = k*Tr.Weight() * ip.weight;
el.CalcShape(ip, shape);
MultVVt(shape, elmat_scalar);
for (int row = 0; row < vdim; row++)
{
for (int col = 0; col < vdim; col++)
{
elmat.AddMatrix(val*vec(row)*vec(col), elmat_scalar, dof*row, dof*col);
}
}
}
}
using BilinearFormIntegrator::AssembleFaceMatrix;
virtual void AssembleFaceMatrix(const FiniteElement &el,
const FiniteElement &dummy,
FaceElementTransformations &Tr,
DenseMatrix &elmat)
{
int dof = el.GetDof();
Vector shape(dof), vec(vdim);
out << Tr.Attribute - 1 << " " << dof << " LHSFace" << std::endl;
elmat.SetSize(dof*vdim);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int intorder = oa * el.GetOrder() + ob; // <------ user control
ir = &IntRules.Get(Tr.FaceGeom, intorder); // of integration order
}
DenseMatrix elmat_scalar(dof);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
// Set the integration point in the face and the neighboring element
Tr.SetAllIntPoints(&ip);
// Access the neighboring element's integration point
const IntegrationPoint &eip = Tr.GetElement1IntPoint();
direction.Eval(vec, *Tr.Face, ip);
double val = k*Tr.Face->Weight() * ip.weight;
el.CalcShape(eip, shape);
for (int row = 0; row < vdim; row++)
{
for (int col = 0; col < vdim; col++)
{
elmat.AddMatrix(val*vec(row)*vec(col), elmat_scalar, dof*row, dof*col);
}
}
}
}
};
/** Mass integrator (u⋅n, v⋅n) restricted to the boundary of a domain */
class VectorBoundaryDirectionalLFIntegrator : public LinearFormIntegrator
{
VectorCoefficient &direction, &force;
int oa, ob, vdim;
public:
/** @brief Constructs a boundary integrator with a given Coefficient @a QG.
Integration order will be @a a * basis_order + @a b. */
VectorBoundaryDirectionalLFIntegrator(VectorCoefficient &direction,
VectorCoefficient &force,
int a = 1, int b = 1)
: direction(direction), force(force), oa(a), ob(b), vdim(direction.GetVDim()) { }
/** Given a particular boundary Finite Element and a transformation (Tr)
computes the element boundary vector, elvect. */
using LinearFormIntegrator::AssembleRHSElementVect;
virtual void AssembleRHSElementVect(
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
{
int dof = el.GetDof();
out << Tr.Attribute - 1 << " " << dof << " RHSElement" << std::endl;
Vector shape(dof), vec(vdim), vecF(vdim);
elvect.SetSize(dof*vdim);
elvect = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int intorder = oa * el.GetOrder() + ob; // <------ user control
ir = &IntRules.Get(Tr.GetGeometryType(), intorder); // of integration order
}
double * data = elvect.GetData();
Vector elvect_loc(data, dof);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
direction.Eval(vec, Tr, ip);
force.Eval(vecF, Tr, ip);
double val = Tr.Weight() * ip.weight * (vec * vecF);
el.CalcShape(ip, shape);
for (int row = 0; row < vdim; row++)
{
elvect_loc.SetData(data + dof*row);
elvect_loc.Add(val*vec(row), shape);
}
}
}
virtual void AssembleRHSElementVect(
const FiniteElement &el, FaceElementTransformations &Tr, Vector &elvect)
{
int dof = el.GetDof();
out << Tr.Attribute - 1 << " " << dof << " RHSFace" << std::endl;
Vector shape(dof), vec(vdim), vecF(vdim);
elvect.SetSize(dof*vdim);
elvect = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int intorder = oa * el.GetOrder() + ob; // <------ user control
ir = &IntRules.Get(Tr.FaceGeom, intorder); // of integration order
}
double * data = elvect.GetData();
Vector elvect_loc(data, dof);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
// Set the integration point in the face and the neighboring element
Tr.SetAllIntPoints(&ip);
// Access the neighboring element's integration point
const IntegrationPoint &eip = Tr.GetElement1IntPoint();
direction.Eval(vec, Tr, ip);
force.Eval(vecF, Tr, ip);
double val = Tr.Face->Weight() * ip.weight * (vec * vecF);
el.CalcShape(eip, shape);
for (int row = 0; row < vdim; row++)
{
elvect_loc.SetData(data + dof*row);
elvect_loc.Add(val*vec(row), shape);
}
}
}
};
enum BdrType
{
Fixed,
XRoller,
YRoller,
ZRoller,
Input,
Output,
Free,
NumBdr
};
int main(int argc, char *argv[])
{
int p=1;
int nel = 40;
int numelx = nel*2;
int numely = nel;
const double len = nel*0.025; // fixed, input, output boundary length
// Setup spring
double input_spring = 1;
double output_spring = 0.0001;
Vector input_direction(2), output_direction(2);
input_direction = 0.0; output_direction = 0.0;
input_direction[0] = 1.0;
output_direction[0] = -1.0;
// Mesh
Mesh mesh = mesh.MakeCartesian2D(numelx, numely,
mfem::Element::Type::QUADRILATERAL,
true,
(double)numelx, (double)numely);
// Setup boundary
//
// ooooooooooooooooooooooo <- x roller (Y fixed)
// Input -> II II <- Output
// | |
// | |
// Fixed -> II--------------------|
//
// Otherwise, free.
Array2D<int> ess_bdr(mesh.SpaceDimension() + 1, BdrType::NumBdr); // [X-fixed; Y-fixed; All-fixed]
ess_bdr = 0;
ess_bdr(0, BdrType::YRoller) = 1; // y-roller - x direction fixed
ess_bdr(1, BdrType::XRoller) = 1; // x-roller - y direction fixed
ess_bdr(2, BdrType::Fixed) = 1; // all direction fixed
Array<int> input_bdr(BdrType::NumBdr), output_bdr(BdrType::NumBdr);
input_bdr = 0; output_bdr = 0;
input_bdr[BdrType::Input] = 1; output_bdr[BdrType::Output] = 1;
// To ensure that there are input/output boundaries
int nrInputBdrFace = 0;
int nrOutputBdrFace = 0;
// Set boundary attributes
for (int i = 0; i<mesh.GetNBE(); i++)
{
Element * be = mesh.GetBdrElement(i);
Array<int> vertices;
be->GetVertices(vertices);
double * coords1 = mesh.GetVertex(vertices[0]);
double * coords2 = mesh.GetVertex(vertices[1]);
Vector fc(2);
fc(0) = 0.5*(coords1[0] + coords2[0]);
fc(1) = 0.5*(coords1[1] + coords2[1]);
switch (be->GetAttribute())
{
case 1: // bottom
be->SetAttribute(BdrType::Free + 1);
break;
case 2: // right
if (fc(1) > numely - len)
{
be->SetAttribute(BdrType::Output + 1);
nrOutputBdrFace++;
break;
}
be->SetAttribute(BdrType::Free + 1);
break;
case 3: // top
be->SetAttribute(BdrType::XRoller + 1);
break;
case 4: // left
if (fc(1) > numely - len)
{
be->SetAttribute(BdrType::Input + 1);
nrInputBdrFace++;
break;
}
else if (fc(1) < len)
{
be->SetAttribute(BdrType::Fixed + 1);
break;
}
be->SetAttribute(BdrType::Free + 1);
break;
default:
mfem_error("Something went wrong");
}
}
mesh.SetAttributes();
out << "(# Input, # Output) = (" << nrInputBdrFace << ", " << nrOutputBdrFace << ")" << std::endl;
H1_FECollection fec(p);
FiniteElementSpace fes(&mesh, &fec, mesh.SpaceDimension(), Ordering::byNODES);
VectorConstantCoefficient output_d_cf(output_direction), input_d_cf(input_direction);
for(int i=0; i<10; i++)
{
// Expected output for each iteration:
// BdrType::Input (p+1)*dim LHSFace
// BdrType::Output (p+1)*dim LHSFace
// BdrType::Input (p+1)*dim RHSFace
// BdrType::Output (p+1)*dim RHSFace
//
// When p = 1 and dim = 2,
// 4 4 LHSFace
// 5 4 LHSFace
// 4 4 RHSFace
// 5 4 RHSFace
//
out << i << std::endl;
LinearForm b(&fes);
b.AddBdrFaceIntegrator(new VectorBoundaryDirectionalLFIntegrator(input_d_cf, input_d_cf), input_bdr);
b.AddBdrFaceIntegrator(new VectorBoundaryDirectionalLFIntegrator(output_d_cf, output_d_cf), output_bdr);
b.Assemble();
BilinearForm a(&fes);
a.AddBdrFaceIntegrator(new VectorBoundaryDirectionalMassIntegrator(input_spring, input_d_cf), input_bdr);
a.AddBdrFaceIntegrator(new VectorBoundaryDirectionalMassIntegrator(output_spring, output_d_cf), output_bdr);
a.Assemble();
out << std::endl;
}
}
+1
View File
@@ -23,6 +23,7 @@ set(SRCS
integ/bilininteg_diffusion_mf.cpp
integ/bilininteg_diffusion_pa.cpp
integ/bilininteg_diffusion_ea.cpp
integ/bilininteg_diffusion_patch.cpp
integ/bilininteg_divdiv_pa.cpp
integ/bilininteg_gradient_pa.cpp
integ/bilininteg_interp_pa.cpp
+49 -3
View File
@@ -13,6 +13,7 @@
#include "fem.hpp"
#include "../general/device.hpp"
#include "../mesh/nurbs.hpp"
#include <cmath>
namespace mfem
@@ -421,23 +422,30 @@ void BilinearForm::Assemble(int skip_zeros)
"invalid element marker for domain integrator #"
<< k << ", counting from zero");
}
if (domain_integs[k]->Patchwise())
{
MFEM_VERIFY(fes->GetNURBSext(), "Patchwise integration requires a "
<< "NURBS FE space");
}
}
// Element-wise integration
for (int i = 0; i < fes -> GetNE(); i++)
{
int elem_attr = fes->GetMesh()->GetAttribute(i);
doftrans = fes->GetElementVDofs(i, vdofs);
if (element_matrices)
{
elmat_p = &(*element_matrices)(i);
}
else
{
const int elem_attr = fes->GetMesh()->GetAttribute(i);
elmat.SetSize(0);
for (int k = 0; k < domain_integs.Size(); k++)
{
if ( domain_integs_marker[k] == NULL ||
if ((domain_integs_marker[k] == NULL ||
(*(domain_integs_marker[k]))[elem_attr-1] == 1)
&& !domain_integs[k]->Patchwise())
{
const FiniteElement &fe = *fes->GetFE(i);
eltrans = fes->GetElementTransformation(i);
@@ -460,6 +468,7 @@ void BilinearForm::Assemble(int skip_zeros)
{
elmat_p = &elmat;
}
doftrans = fes->GetElementVDofs(i, vdofs);
if (doftrans)
{
doftrans->TransformDual(elmat);
@@ -479,6 +488,43 @@ void BilinearForm::Assemble(int skip_zeros)
}
}
}
// Patch-wise integration
if (fes->GetNURBSext())
{
for (int p=0; p<mesh->NURBSext->GetNP(); ++p)
{
bool vdofsSet = false;
for (int k = 0; k < domain_integs.Size(); k++)
{
if (domain_integs[k]->Patchwise())
{
if (!vdofsSet)
{
fes->GetPatchVDofs(p, vdofs);
vdofsSet = true;
}
SparseMatrix* spmat = nullptr;
domain_integs[k]->AssemblePatchMatrix(p, *fes, spmat);
Array<int> cols;
Vector srow;
for (int r=0; r<spmat->Height(); ++r)
{
spmat->GetRow(r, cols, srow);
for (int i=0; i<cols.Size(); ++i)
{
cols[i] = vdofs[cols[i]];
}
mat->AddRow(vdofs[r], cols, srow);
}
delete spmat;
}
}
}
}
}
if (boundary_integs.Size())
+38 -3
View File
@@ -299,7 +299,16 @@ void PABilinearFormExtension::Assemble()
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
for (BilinearFormIntegrator *integ : integrators)
{
integ->AssemblePA(*a->FESpace());
if (integ->Patchwise())
{
MFEM_VERIFY(a->FESpace()->GetNURBSext(),
"Patchwise integration requires a NURBS FE space");
integ->AssembleNURBSPA(*a->FESpace());
}
else
{
integ->AssemblePA(*a->FESpace());
}
}
Array<BilinearFormIntegrator*> &bdr_integrators = *a->GetBBFI();
@@ -410,13 +419,39 @@ void PABilinearFormExtension::Mult(const Vector &x, Vector &y) const
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int iSz = integrators.Size();
if (DeviceCanUseCeed() || !elem_restrict)
bool allPatchwise = true;
bool somePatchwise = false;
for (int i = 0; i < iSz; ++i)
{
if (integrators[i]->Patchwise())
{
somePatchwise = true;
}
else
{
allPatchwise = false;
}
}
MFEM_VERIFY(!(somePatchwise && !allPatchwise),
"All or none of the integrators should be patchwise");
if (DeviceCanUseCeed() || !elem_restrict || allPatchwise)
{
y.UseDevice(true); // typically this is a large vector, so store on device
y = 0.0;
for (int i = 0; i < iSz; ++i)
{
integrators[i]->AddMultPA(x, y);
if (integrators[i]->Patchwise())
{
integrators[i]->AddMultNURBSPA(x, y);
}
else
{
integrators[i]->AddMultPA(x, y);
}
}
}
else
+43 -6
View File
@@ -26,6 +26,12 @@ void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&)
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleNURBSPA(const FiniteElementSpace&)
{
mfem_error ("BilinearFormIntegrator::AssembleNURBSPA(fes)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&,
const FiniteElementSpace&)
{
@@ -92,7 +98,13 @@ void BilinearFormIntegrator::AssembleDiagonalPA_ADAt(const Vector &, Vector &)
void BilinearFormIntegrator::AddMultPA(const Vector &, Vector &) const
{
MFEM_ABORT("BilinearFormIntegrator::MultAssembled(...)\n"
MFEM_ABORT("BilinearFormIntegrator:AddMultPA:(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AddMultNURBSPA(const Vector &, Vector &) const
{
MFEM_ABORT("BilinearFormIntegrator::AddMultNURBSPA(...)\n"
" is not implemented for this class.");
}
@@ -126,23 +138,30 @@ void BilinearFormIntegrator::AssembleDiagonalMF(Vector &)
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleElementMatrix (
void BilinearFormIntegrator::AssembleElementMatrix(
const FiniteElement &el, ElementTransformation &Trans,
DenseMatrix &elmat )
DenseMatrix &elmat)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleElementMatrix(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleElementMatrix2 (
void BilinearFormIntegrator::AssembleElementMatrix2(
const FiniteElement &el1, const FiniteElement &el2,
ElementTransformation &Trans, DenseMatrix &elmat )
ElementTransformation &Trans, DenseMatrix &elmat)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleElementMatrix2(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleFaceMatrix (
void BilinearFormIntegrator::AssemblePatchMatrix(
const int patch, const FiniteElementSpace &fes, SparseMatrix*& smat)
{
mfem_error ("BilinearFormIntegrator::AssemblePatchMatrix(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleFaceMatrix(
const FiniteElement &el1, const FiniteElement &el2,
FaceElementTransformations &Trans, DenseMatrix &elmat)
{
@@ -848,6 +867,19 @@ void DiffusionIntegrator::AssembleElementMatrix
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
const NURBSFiniteElement *NURBSFE =
dynamic_cast<const NURBSFiniteElement *>(&el);
bool deleteRule = false;
if (NURBSFE && patchRules)
{
const int patch = NURBSFE->GetPatch();
const int* ijk = NURBSFE->GetIJK();
Array<const KnotVector*>& kv = NURBSFE->KnotVectors();
ir = &patchRules->GetElementRule(NURBSFE->GetElement(), patch, ijk, kv,
deleteRule);
}
elmat = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
{
@@ -882,6 +914,11 @@ void DiffusionIntegrator::AssembleElementMatrix
AddMult_a_AAt(w, dshapedxt, elmat);
}
}
if (deleteRule)
{
delete ir;
}
}
void DiffusionIntegrator::AssembleElementMatrix2(
+80
View File
@@ -61,6 +61,11 @@ public:
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes);
/// Method defining partial assembly on NURBS patches.
/** The result of the partial assembly is stored internally so that it can be
used later in the method AddMultNURBSPA(). */
virtual void AssembleNURBSPA(const FiniteElementSpace &fes);
virtual void AssemblePABoundary(const FiniteElementSpace &fes);
virtual void AssemblePAInteriorFaces(const FiniteElementSpace &fes);
@@ -82,6 +87,9 @@ public:
called. */
virtual void AddMultPA(const Vector &x, Vector &y) const;
/// Method for partially assembled action on NURBS patches.
virtual void AddMultNURBSPA(const Vector&x, Vector&y) const;
/// Method for partially assembled transposed action.
/** Perform the transpose action of integrator on the input @a x and add the
result to the output @a y. Both @a x and @a y are E-vectors, i.e. they
@@ -148,6 +156,13 @@ public:
ElementTransformation &Trans,
DenseMatrix &elmat);
/** Given a particular NURBS patch, computes the patch matrix as a
SparseMatrix @a smat.
*/
virtual void AssemblePatchMatrix(const int patch,
const FiniteElementSpace &fes,
SparseMatrix*& smat);
virtual void AssembleFaceMatrix(const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &Trans,
@@ -2111,6 +2126,59 @@ private:
Vector pa_data;
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
// Data for NURBS patch PA
// Type for a variable-row-length 2D array, used for data related to 1D
// quadrature rules in each dimension.
typedef std::vector<std::vector<int>> IntArrayVar2D;
int numPatches = 0;
static constexpr int numTypes = 2; // Number of rule types
// In the case integrationMode == Mode::PATCHWISE_REDUCED, an approximate
// integration rule with sparse nonzero weights is computed by NNLSSolver,
// for each 1D basis function on each patch, in each spatial dimension. For a
// fixed 1D basis function b_i with DOF index i, in the tensor product basis
// of patch p, the prescribed exact 1D rule is of the form
// \sum_k a_{i,j,k} w_k for some integration points indexed by k, with
// weights w_k and coefficients a_{i,j,k} depending on Q(x), an element
// transformation, b_i, and b_j, for all 1D basis functions b_j whose support
// overlaps that of b_i. Define the constraint matrix G = [g_{j,k}] with
// g_{j,k} = a_{i,j,k} and the vector of exact weights w = [w_k]. A reduced
// rule should have different weights w_r, many of them zero, and should
// approximately satisfy Gw_r = Gw. A sparse approximate solution to this
// underdetermined system is computed by NNLSSolver, and its data is stored
// in the following members.
// For each patch p, spatial dimension d (total dim), and rule type t (total
// numTypes), an std::vector<Vector> of reduced quadrature weights for all
// basis functions is stored in reducedWeights[t + numTypes * (d + dim * p)],
// reshaped as rw(t,d,p). Note that nd may vary with respect to the patch and
// spatial dimension. Array reducedIDs is treated similarly.
std::vector<std::vector<Vector>> reducedWeights;
std::vector<IntArrayVar2D> reducedIDs;
std::vector<Array<int>> pQ1D, pD1D;
std::vector<std::vector<Array2D<double>>> pB, pG;
std::vector<IntArrayVar2D> pminD, pmaxD, pminQ, pmaxQ, pminDD, pmaxDD;
std::vector<Array<const IntegrationRule*>> pir1d;
void SetupPatchPA(const int patch, Mesh *mesh, bool unitWeights=false);
void SetupPatchBasisData(Mesh *mesh, unsigned int patch);
/** Called by AssemblePatchMatrix for sparse matrix assembly on a NURBS patch
with full 1D quadrature rules. */
void AssemblePatchMatrix_fullQuadrature(const int patch,
const FiniteElementSpace &fes,
SparseMatrix*& smat);
/** Called by AssemblePatchMatrix for sparse matrix assembly on a NURBS patch
with reduced 1D quadrature rules. */
void AssemblePatchMatrix_reducedQuadrature(const int patch,
const FiniteElementSpace &fes,
SparseMatrix*& smat);
public:
/// Construct a diffusion integrator with coefficient Q = 1
DiffusionIntegrator(const IntegrationRule *ir = nullptr)
@@ -2146,6 +2214,14 @@ public:
ElementTransformation &Trans,
DenseMatrix &elmat);
virtual void AssemblePatchMatrix(const int patch,
const FiniteElementSpace &fes,
SparseMatrix*& smat);
virtual void AssembleNURBSPA(const FiniteElementSpace &fes);
void AssemblePatchPA(const int patch, const FiniteElementSpace &fes);
/// Perform the local action of the BilinearFormIntegrator
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &Tr,
@@ -2180,6 +2256,10 @@ public:
virtual void AddMultTransposePA(const Vector&, Vector&) const;
virtual void AddMultNURBSPA(const Vector&, Vector&) const;
void AddMultPatchPA(const int patch, const Vector &x, Vector &y) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe);
+1 -1
View File
@@ -922,7 +922,7 @@ void ParaViewDataCollection::Save()
{
const std::string &field_name = qfield.first;
std::ofstream os(vtu_prefix + GenerateVTUFileName(field_name, myid));
qfield.second->SaveVTU(os, pv_data_format, GetCompressionLevel());
qfield.second->SaveVTU(os, pv_data_format, GetCompressionLevel(), field_name);
}
// MPI rank 0 also creates a "PVTU" file that points to all of the separately
+4
View File
@@ -56,6 +56,10 @@ public:
Vector &Weights () const { return weights; }
/// Update the NURBSFiniteElement according to the currently set knot vectors
virtual void SetOrder () const { }
/// Returns the indices (i,j) in 2D or (i,j,k) in 3D of this element in the
/// tensor product ordering of the patch.
const int* GetIJK() const { return ijk; }
};
+17 -17
View File
@@ -1713,7 +1713,7 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
H1_Elements[Geometry::SEGMENT] = new H1_SegmentElement(p, btype);
}
SegDofOrd[0] = new int[2*pm1];
SegDofOrd[0] = (pm1 > 0) ? new int[2*pm1] : nullptr;
SegDofOrd[1] = SegDofOrd[0] + pm1;
for (int i = 0; i < pm1; i++)
{
@@ -1751,7 +1751,7 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
const int &TriDof = H1_dof[Geometry::TRIANGLE];
const int &QuadDof = H1_dof[Geometry::SQUARE];
TriDofOrd[0] = new int[6*TriDof];
TriDofOrd[0] = (TriDof > 0) ? new int[6*TriDof] : nullptr;
for (int i = 1; i < 6; i++)
{
TriDofOrd[i] = TriDofOrd[i-1] + TriDof;
@@ -1772,7 +1772,7 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
}
}
QuadDofOrd[0] = new int[8*QuadDof];
QuadDofOrd[0] = (QuadDof > 0) ? new int[8*QuadDof] : nullptr;
for (int i = 1; i < 8; i++)
{
QuadDofOrd[i] = QuadDofOrd[i-1] + QuadDof;
@@ -1855,7 +1855,7 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
H1_Elements[Geometry::PYRAMID] = new LinearPyramidFiniteElement;
const int &TetDof = H1_dof[Geometry::TETRAHEDRON];
TetDofOrd[0] = new int[24*TetDof];
TetDofOrd[0] = (TetDof > 0) ? new int[24*TetDof] : nullptr;
for (int i = 1; i < 24; i++)
{
TetDofOrd[i] = TetDofOrd[i-1] + TetDof;
@@ -2127,7 +2127,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
// No need to set the map_type for Tr_Elements.
const int pp1 = p + 1;
SegDofOrd[0] = new int[2*pp1];
SegDofOrd[0] = (pp1 > 0) ? new int[2*pp1] : nullptr;
SegDofOrd[1] = SegDofOrd[0] + pp1;
for (int i = 0; i <= p; i++)
{
@@ -2160,7 +2160,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
}
const int TriDof = L2_Elements[Geometry::TRIANGLE]->GetDof();
TriDofOrd[0] = new int[6*TriDof];
TriDofOrd[0] = (TriDof > 0) ? new int[6*TriDof] : nullptr;
for (int i = 1; i < 6; i++)
{
TriDofOrd[i] = TriDofOrd[i-1] + TriDof;
@@ -2181,7 +2181,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
}
}
const int QuadDof = L2_Elements[Geometry::SQUARE]->GetDof();
OtherDofOrd = new int[QuadDof];
OtherDofOrd = (QuadDof > 0) ? new int[QuadDof] : nullptr;
for (int j = 0; j < QuadDof; j++)
{
OtherDofOrd[j] = j; // for Or == 0
@@ -2225,7 +2225,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
const int PriDof = L2_Elements[Geometry::PRISM]->GetDof();
const int MaxDof = std::max(TetDof, std::max(PriDof, HexDof));
TetDofOrd[0] = new int[24*TetDof];
TetDofOrd[0] = (TetDof > 0) ? new int[24*TetDof] : nullptr;
for (int i = 1; i < 24; i++)
{
TetDofOrd[i] = TetDofOrd[i-1] + TetDof;
@@ -2314,7 +2314,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
}
}
}
OtherDofOrd = new int[MaxDof];
OtherDofOrd = (MaxDof > 0) ? new int[MaxDof] : nullptr;
for (int j = 0; j < MaxDof; j++)
{
OtherDofOrd[j] = j; // for Or == 0
@@ -2502,7 +2502,7 @@ void RT_FECollection::InitFaces(const int p, const int dim_,
RT_Elements[Geometry::SEGMENT] = l2_seg;
RT_dof[Geometry::SEGMENT] = pp1;
SegDofOrd[0] = new int[2*pp1];
SegDofOrd[0] = (pp1 > 0) ? new int[2*pp1] : nullptr;
SegDofOrd[1] = SegDofOrd[0] + pp1;
for (int i = 0; i <= p; i++)
{
@@ -2523,7 +2523,7 @@ void RT_FECollection::InitFaces(const int p, const int dim_,
RT_dof[Geometry::SQUARE] = pp1*pp1;
int TriDof = RT_dof[Geometry::TRIANGLE];
TriDofOrd[0] = new int[6*TriDof];
TriDofOrd[0] = (TriDof > 0) ? new int[6*TriDof] : nullptr;
for (int i = 1; i < 6; i++)
{
TriDofOrd[i] = TriDofOrd[i-1] + TriDof;
@@ -2553,7 +2553,7 @@ void RT_FECollection::InitFaces(const int p, const int dim_,
}
int QuadDof = RT_dof[Geometry::SQUARE];
QuadDofOrd[0] = new int[8*QuadDof];
QuadDofOrd[0] = (QuadDof > 0) ? new int[8*QuadDof] : nullptr;
for (int i = 1; i < 8; i++)
{
QuadDofOrd[i] = QuadDofOrd[i-1] + QuadDof;
@@ -2749,7 +2749,7 @@ ND_FECollection::ND_FECollection(const int p, const int dim,
ND_Elements[Geometry::SEGMENT] = new ND_SegmentElement(p, ob_type);
ND_dof[Geometry::SEGMENT] = p;
SegDofOrd[0] = new int[2*p];
SegDofOrd[0] = (p > 0) ? new int[2*p] : nullptr;
SegDofOrd[1] = SegDofOrd[0] + p;
for (int i = 0; i < p; i++)
{
@@ -2769,7 +2769,7 @@ ND_FECollection::ND_FECollection(const int p, const int dim,
ND_dof[Geometry::TRIANGLE] = p*pm1;
int QuadDof = ND_dof[Geometry::SQUARE];
QuadDofOrd[0] = new int[8*QuadDof];
QuadDofOrd[0] = (QuadDof > 0) ? new int[8*QuadDof] : nullptr;
for (int i = 1; i < 8; i++)
{
QuadDofOrd[i] = QuadDofOrd[i-1] + QuadDof;
@@ -2813,7 +2813,7 @@ ND_FECollection::ND_FECollection(const int p, const int dim,
}
int TriDof = ND_dof[Geometry::TRIANGLE];
TriDofOrd[0] = new int[6*TriDof];
TriDofOrd[0] = (TriDof > 0) ? new int[6*TriDof] : nullptr;
for (int i = 1; i < 6; i++)
{
TriDofOrd[i] = TriDofOrd[i-1] + TriDof;
@@ -3163,7 +3163,7 @@ ND_R2D_FECollection::ND_R2D_FECollection(const int p, const int dim,
ob_type);
ND_dof[Geometry::SEGMENT] = 2 * p - 1;
SegDofOrd[0] = new int[4 * p - 2];
SegDofOrd[0] = (4*p > 2) ? new int[4 * p - 2] : nullptr;
SegDofOrd[1] = SegDofOrd[0] + 2 * p - 1;
for (int i = 0; i < p; i++)
{
@@ -3347,7 +3347,7 @@ void RT_R2D_FECollection::InitFaces(const int p, const int dim,
RT_Elements[Geometry::SEGMENT] = l2_seg;
RT_dof[Geometry::SEGMENT] = pp1;
SegDofOrd[0] = new int[2*pp1];
SegDofOrd[0] = (pp1 > 0) ? new int[2*pp1] : nullptr;
SegDofOrd[1] = SegDofOrd[0] + pp1;
for (int i = 0; i <= p; i++)
{
+22 -3
View File
@@ -309,6 +309,12 @@ FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs) const
}
}
void FiniteElementSpace::GetPatchVDofs(int i, Array<int> &vdofs) const
{
GetPatchDofs(i, vdofs);
DofsToVDofs(vdofs);
}
void FiniteElementSpace::GetFaceVDofs(int i, Array<int> &vdofs) const
{
GetFaceDofs(i, vdofs);
@@ -2801,11 +2807,24 @@ FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
return DoFTrans[mesh->GetElementBaseGeometry(elem)];
}
void FiniteElementSpace::GetPatchDofs(int patch, Array<int> &dofs) const
{
MFEM_ASSERT(NURBSext,
"FiniteElementSpace::GetPatchDofs needs a NURBSExtension");
NURBSext->GetPatchDofs(patch, dofs);
}
const FiniteElement *FiniteElementSpace::GetFE(int i) const
{
if (i < 0 || !mesh->GetNE()) { return NULL; }
MFEM_VERIFY(i < mesh->GetNE(),
"Invalid element id " << i << ", maximum allowed " << mesh->GetNE()-1);
if (i < 0 || i >= mesh->GetNE())
{
if (mesh->GetNE() == 0)
{
MFEM_ABORT("Empty MPI partitions are not permitted!");
}
MFEM_ABORT("Invalid element id:" << i << "; minimum allowed:" << 0 <<
", maximum allowed:" << mesh->GetNE()-1);
}
const FiniteElement *FE =
fec->GetFE(mesh->GetElementGeometry(i), GetElementOrderImpl(i));
+12 -2
View File
@@ -811,6 +811,11 @@ public:
virtual DofTransformation *GetBdrElementDofs(int bel,
Array<int> &dofs) const;
/** @brief Returns indices of degrees of freedom for NURBS patch index
@a patch. Cartesian ordering is used, for the tensor-product degrees of
freedom. */
void GetPatchDofs(int patch, Array<int> &dofs) const;
/// @brief Returns the indices of the degrees of freedom for the specified
/// face, including the DOFs for the edges and the vertices of the face.
///
@@ -995,7 +1000,7 @@ public:
/// @brief Returns indices of degrees of freedom for the @a i'th element.
/// The returned indices are offsets into an @ref ldof vector with @b vdim
/// not necessarily equal to 1. The returned indexes are always ordered
/// not necessarily equal to 1. The returned indices are always ordered
/// byNODES, irrespective of whether the space is byNODES or byVDIM.
/// See also GetElementDofs().
///
@@ -1024,6 +1029,9 @@ public:
/// @note The returned object should NOT be deleted by the caller.
DofTransformation *GetBdrElementVDofs(int i, Array<int> &vdofs) const;
/// Returns indices of degrees of freedom in @a vdofs for NURBS patch @a i.
void GetPatchVDofs(int i, Array<int> &vdofs) const;
/// @brief Returns the indices of the degrees of freedom for the specified
/// face, including the DOFs for the edges and the vertices of the face.
///
@@ -1107,7 +1115,9 @@ public:
int GetLocalDofForDof(int i) const { return dof_ldof_array[i]; }
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th element in the mesh object. */
associated with i'th element in the mesh object.
Note: The method has been updated to abort instead of returning NULL for
an empty partition. */
virtual const FiniteElement *GetFE(int i) const;
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
+3 -295
View File
@@ -1236,7 +1236,7 @@ void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
gsl_ref.SetSize(points_cnt * dim);
gsl_dist.SetSize(points_cnt);
auto xvFill = [&](const double *xv_base[], unsigned xv_stride[], int dim)
auto xvFill = [&](const double *xv_base[], unsigned xv_stride[])
{
for (int d = 0; d < dim; d++)
{
@@ -1256,7 +1256,7 @@ void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
{
const double *xv_base[2];
unsigned xv_stride[2];
xvFill(xv_base, xv_stride, dim);
xvFill(xv_base, xv_stride);
findptsms_2(gsl_code.GetData(), sizeof(unsigned int),
gsl_proc.GetData(), sizeof(unsigned int),
gsl_elem.GetData(), sizeof(unsigned int),
@@ -1270,7 +1270,7 @@ void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
{
const double *xv_base[3];
unsigned xv_stride[3];
xvFill(xv_base, xv_stride, dim);
xvFill(xv_base, xv_stride);
findptsms_3(gsl_code.GetData(), sizeof(unsigned int),
gsl_proc.GetData(), sizeof(unsigned int),
gsl_elem.GetData(), sizeof(unsigned int),
@@ -1308,298 +1308,6 @@ void OversetFindPointsGSLIB::Interpolate(const Vector &point_pos,
Interpolate(field_in, field_out);
}
#ifdef MFEM_USE_MPI
GSLIBCommunicator::GSLIBCommunicator(MPI_Comm comm_)
: cr(NULL), gsl_comm(NULL)
{
gsl_comm = new gslib::comm;
cr = new gslib::crystal;
comm_init(gsl_comm, comm_);
crystal_init(cr, gsl_comm);
}
void GSLIBCommunicator::SendData(int dim, const Array<unsigned int> & gsl_proc,
const Array<unsigned int> & elem_send,
const Vector &ref_send,
const Vector &coords_send,
const Array<int> &s_conn_send,
Array<unsigned int> & proc_recv,
Array<unsigned int> & index_recv,
Array<unsigned int> & elem_recv,
Vector &ref_recv,
Vector &coords_recv,
Array<int> &s_conn_recv)
{
int nptsend = gsl_proc.Size();
int nptElem = elem_send.Size();
int nptRST = ref_send.Size();
MFEM_VERIFY(nptElem == nptsend,
"Incompatible Elem size.");
MFEM_VERIFY(nptsend*dim == nptRST,
"Incompatible nptRST size.");
MFEM_VERIFY(dim <= 3,
"Incompatible dimension.");
// Pack data to send via crystal router
struct gslib::array *outpt = new gslib::array;
struct out_pt { double rst[3], coords[3]; int s_conn; uint index, elem, proc; };
struct out_pt *pt;
array_init(struct out_pt, outpt, nptsend);
outpt->n=nptsend;
pt = (struct out_pt *)outpt->ptr;
for (int index = 0; index < nptsend; index++)
{
pt->index = index;
pt->elem = elem_send[index];
pt->proc = gsl_proc[index];
pt->s_conn = s_conn_send[index];
for (int d = 0; d < dim; ++d)
{
pt->rst[d]= ref_send(index*dim + d);
pt->coords[d]= coords_send(index + d*nptsend);
}
++pt;
}
// Transfer data to target MPI ranks
sarray_transfer(struct out_pt, outpt, proc, 1, cr);
// unpack
int npt = outpt->n;
proc_recv.SetSize(npt);
elem_recv.SetSize(npt);
index_recv.SetSize(npt);
ref_recv.SetSize(npt*dim);
coords_recv.SetSize(npt*dim);
s_conn_recv.SetSize(npt);
pt = (struct out_pt *)outpt->ptr;
for (int index = 0; index < npt; index++)
{
index_recv[index] = pt->index;
elem_recv[index] = pt->elem;
proc_recv[index] = pt->proc;
s_conn_recv[index] = pt->s_conn;
for (int d = 0; d < dim; ++d)
{
ref_recv(index*dim + d)= pt->rst[d]; // by VDIM
coords_recv(index + d*npt)= pt->coords[d]; // by NODES
}
++pt;
}
array_free(outpt);
delete outpt;
}
void GSLIBCommunicator::SendData2(int dim,
const Array<unsigned int> & gsl_proc,
const Vector &xyz_send,
const Vector &xi_send,
const Array<int> &s_conn_send,
const Array<int> &conn_send,
const DenseMatrix &coords_send,
Vector &xyz_recv,
Vector &xi_recv,
Array<int> &s_conn_recv,
Array<int> &conn_recv,
DenseMatrix &coords_recv)
{
int nptsend = gsl_proc.Size();
struct gslib::array *outpt = new gslib::array;
struct out_pt {double xyz[3], xi[2], coords[12]; int s_conn; int conn[4]; uint proc;};
struct out_pt *pt;
array_init(struct out_pt, outpt, nptsend);
outpt->n=nptsend;
pt = (struct out_pt *)outpt->ptr;
for (int index = 0; index < nptsend; index++)
{
pt->proc = gsl_proc[index];
pt->s_conn = s_conn_send[index];
for (int d = 0; d < dim-1; ++d)
{
pt->xi[d]= xi_send(index*(dim-1) + d);
}
for (int d = 0; d < dim; ++d)
{
pt->xyz[d]= xyz_send(index + d*nptsend);
}
for (int j = 0; j<4; j++)
{
pt->conn[j] = conn_send[index*4+j];
for (int d = 0; d < dim; ++d)
{
pt->coords[j*dim+d]= coords_send(index*4+j,d);
}
}
++pt;
}
// Transfer data to target MPI ranks
sarray_transfer(struct out_pt, outpt, proc, 1, cr);
// unpack
int npt = outpt->n;
xi_recv.SetSize(npt*(dim-1));
xyz_recv.SetSize(npt*dim);
s_conn_recv.SetSize(npt);
conn_recv.SetSize(npt*4);
coords_recv.SetSize(npt*4,dim);
pt = (struct out_pt *)outpt->ptr;
for (int index = 0; index < npt; index++)
{
s_conn_recv[index] = pt->s_conn;
for (int d = 0; d < dim-1; ++d)
{
xi_recv(index*(dim-1) + d) = pt->xi[d];
}
for (int d = 0; d < dim; ++d)
{
xyz_recv(index + d*npt)= pt->xyz[d]; // by NODES
}
for (int j = 0; j<4; j++)
{
conn_recv[index*4+j] = pt->conn[j];
for (int d = 0; d < dim; ++d)
{
coords_recv(index*4+j,d) = pt->coords[j*dim+d];
}
}
++pt;
}
array_free(outpt);
delete outpt;
}
void GSLIBCommunicator::ExchangeNormal(Mesh & mesh,
const Array<unsigned int> &gsl_proc,
const Array<unsigned int> &gsl_mfem_elem,
const Vector &gsl_mfem_ref,
Vector &recv_normals)
{
int dim = mesh.Dimension();
int nptsend = gsl_proc.Size();
int nptElem = gsl_mfem_elem.Size();
int nptRST = gsl_mfem_ref.Size();
recv_normals.SetSize(nptRST);
int nptNormal = recv_normals.Size();
MFEM_VERIFY(nptElem == nptsend,
"Incompatible Elem size.");
MFEM_VERIFY(nptsend*dim == nptRST,
"Incompatible nptRST size.");
MFEM_VERIFY(dim <= 3,
"Incompatible dimension.");
// Pack data to send via crystal router
struct gslib::array *outpt = new gslib::array;
struct out_pt { double rst[3]; uint index, elem, proc; };
struct out_pt *pt;
array_init(struct out_pt, outpt, nptsend);
outpt->n=nptsend;
pt = (struct out_pt *)outpt->ptr;
for (int index = 0; index < nptsend; index++)
{
pt->index = index;
pt->elem = gsl_mfem_elem[index];
pt->proc = gsl_proc[index];
for (int d = 0; d < dim; ++d)
{
pt->rst[d]= gsl_mfem_ref(index*dim + d);
}
++pt;
}
// Transfer data to target MPI ranks
sarray_transfer(struct out_pt, outpt, proc, 1, cr);
// Get normal vector
int npt = outpt->n;
pt = (struct out_pt *)outpt->ptr;
Vector normal(npt*dim);
for (int index = 0; index < npt; index++)
{
IntegrationPoint ip;
ip.Set3(&pt->rst[0]);
Vector localval(normal.GetData()+index*dim, dim);
// get the normal at this integration point here
// for now I just put back this proc's rank + the input rst coordinates
for (int d = 0; d < dim; d++)
{
localval(d) = gsl_comm->id + pt->rst[d];
}
++pt;
}
// Save index and proc data in a struct
struct gslib::array *savpt = new gslib::array;
struct sav_pt { uint index, proc; };
struct sav_pt *spt;
array_init(struct sav_pt, savpt, npt);
savpt->n=npt;
spt = (struct sav_pt *)savpt->ptr;
pt = (struct out_pt *)outpt->ptr;
for (int index = 0; index < npt; index++)
{
spt->index = pt->index;
spt->proc = pt->proc;
++pt; ++spt;
}
array_free(outpt);
delete outpt;
// Copy data from save struct to send struct and send component wise
struct gslib::array *sendpt = new gslib::array;
struct send_pt { double ival; uint index, proc; };
struct send_pt *sdpt;
for (int j = 0; j < dim; j++)
{
array_init(struct send_pt, sendpt, npt);
sendpt->n=npt;
spt = (struct sav_pt *)savpt->ptr;
sdpt = (struct send_pt *)sendpt->ptr;
for (int index = 0; index < npt; index++)
{
sdpt->index = spt->index;
sdpt->proc = spt->proc;
sdpt->ival = normal(j + index*dim);
++sdpt; ++spt;
}
sarray_transfer(struct send_pt, sendpt, proc, 1, cr);
sdpt = (struct send_pt *)sendpt->ptr;
for (int index = 0; index < static_cast<int>(sendpt->n); index++)
{
int idx = sdpt->index*dim + j;
recv_normals(idx) = sdpt->ival;
++sdpt;
}
array_free(sendpt);
}
array_free(savpt);
delete sendpt;
delete savpt;
}
void GSLIBCommunicator::FreeData()
{
crystal_free(cr);
}
GSLIBCommunicator::~GSLIBCommunicator()
{
delete gsl_comm;
delete cr;
}
#endif
} // namespace mfem
-49
View File
@@ -290,55 +290,6 @@ public:
using FindPointsGSLIB::Interpolate;
};
#ifdef MFEM_USE_MPI
// Use to send info to certain processes
class GSLIBCommunicator
{
protected:
struct gslib::crystal *cr; // gslib's internal data
struct gslib::comm *gsl_comm; // gslib's internal data
public:
GSLIBCommunicator(MPI_Comm comm_);
virtual ~GSLIBCommunicator();
void ExchangeNormal(Mesh& mesh,
const Array<unsigned int> &gsl_proc,
const Array<unsigned int> &gsl_mfem_elem,
const Vector &gsl_mfem_ref,
Vector &recv_normals); //npt*dim
void SendData(int dim,
const Array<unsigned int> & gsl_proc,
const Array<unsigned int> & elem_send,
const Vector &ref_send,
const Vector &coords_send,
const Array<int> &s_conn_send,
Array<unsigned int> & proc_recv,
Array<unsigned int> & index_recv,
Array<unsigned int> & elem_recv,
Vector &ref_recv,
Vector &coords_recv,
Array<int> & s_conn_recv);
void SendData2(int dim,
const Array<unsigned int> & gsl_proc,
const Vector &xyz_send,
const Vector &xi_send,
const Array<int> &s_conn_send,
const Array<int> &conn_send,
const DenseMatrix &coords_send,
Vector &xyz_recv,
Vector &ref_recv,
Array<int> &s_conn_recv,
Array<int> &conn_recv,
DenseMatrix &coords_recv);
virtual void FreeData();
};
#endif
} // namespace mfem
#endif // MFEM_USE_GSLIB
+241
View File
@@ -12,6 +12,7 @@
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
#include "../../mesh/nurbs.hpp"
#include "../ceed/integrators/diffusion/diffusion.hpp"
#include "bilininteg_diffusion_kernels.hpp"
@@ -74,6 +75,29 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
ir->GetWeights(), geom->J, coeff, pa_data);
}
void DiffusionIntegrator::AssembleNURBSPA(const FiniteElementSpace &fes)
{
fespace = &fes;
Mesh *mesh = fes.GetMesh();
dim = mesh->Dimension();
MFEM_VERIFY(3 == dim, "Only 3D so far");
numPatches = mesh->NURBSext->GetNP();
for (int p=0; p<numPatches; ++p)
{
AssemblePatchPA(p, fes);
}
}
void DiffusionIntegrator::AssemblePatchPA(const int patch,
const FiniteElementSpace &fes)
{
Mesh *mesh = fes.GetMesh();
SetupPatchBasisData(mesh, patch);
SetupPatchPA(patch, mesh); // For full quadrature, unitWeights = false
}
void DiffusionIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
@@ -115,4 +139,221 @@ void DiffusionIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
}
}
// This version uses full 1D quadrature rules, taking into account the
// minimum interaction between basis functions and integration points.
void DiffusionIntegrator::AddMultPatchPA(const int patch, const Vector &x,
Vector &y) const
{
MFEM_VERIFY(3 == dim, "Only 3D so far");
const Array<int>& Q1D = pQ1D[patch];
const Array<int>& D1D = pD1D[patch];
const std::vector<Array2D<double>>& B = pB[patch];
const std::vector<Array2D<double>>& G = pG[patch];
const IntArrayVar2D& minD = pminD[patch];
const IntArrayVar2D& maxD = pmaxD[patch];
const IntArrayVar2D& minQ = pminQ[patch];
const IntArrayVar2D& maxQ = pmaxQ[patch];
auto X = Reshape(x.Read(), D1D[0], D1D[1], D1D[2]);
auto Y = Reshape(y.ReadWrite(), D1D[0], D1D[1], D1D[2]);
const auto qd = Reshape(pa_data.Read(), Q1D[0]*Q1D[1]*Q1D[2],
(symmetric ? 6 : 9));
// NOTE: the following is adapted from AssemblePatchMatrix_fullQuadrature
std::vector<Array3D<double>> grad(dim);
// TODO: Can an optimal order of dimensions be determined, for each patch?
Array3D<double> gradXY(3, std::max(Q1D[0], D1D[0]), std::max(Q1D[1], D1D[1]));
Array2D<double> gradX(3, std::max(Q1D[0], D1D[0]));
for (int d=0; d<dim; ++d)
{
grad[d].SetSize(Q1D[0], Q1D[1], Q1D[2]);
for (int qz = 0; qz < Q1D[2]; ++qz)
{
for (int qy = 0; qy < Q1D[1]; ++qy)
{
for (int qx = 0; qx < Q1D[0]; ++qx)
{
grad[d](qx,qy,qz) = 0.0;
}
}
}
}
for (int dz = 0; dz < D1D[2]; ++dz)
{
for (int qy = 0; qy < Q1D[1]; ++qy)
{
for (int qx = 0; qx < Q1D[0]; ++qx)
{
for (int d=0; d<dim; ++d)
{
gradXY(d,qx,qy) = 0.0;
}
}
}
for (int dy = 0; dy < D1D[1]; ++dy)
{
for (int qx = 0; qx < Q1D[0]; ++qx)
{
gradX(0,qx) = 0.0;
gradX(1,qx) = 0.0;
}
for (int dx = 0; dx < D1D[0]; ++dx)
{
const double s = X(dx,dy,dz);
for (int qx = minD[0][dx]; qx <= maxD[0][dx]; ++qx)
{
gradX(0,qx) += s * B[0](qx,dx);
gradX(1,qx) += s * G[0](qx,dx);
}
}
for (int qy = minD[1][dy]; qy <= maxD[1][dy]; ++qy)
{
const double wy = B[1](qy,dy);
const double wDy = G[1](qy,dy);
// This full range of qx values is generally necessary.
for (int qx = 0; qx < Q1D[0]; ++qx)
{
const double wx = gradX(0,qx);
const double wDx = gradX(1,qx);
gradXY(0,qx,qy) += wDx * wy;
gradXY(1,qx,qy) += wx * wDy;
gradXY(2,qx,qy) += wx * wy;
}
}
}
for (int qz = minD[2][dz]; qz <= maxD[2][dz]; ++qz)
{
const double wz = B[2](qz,dz);
const double wDz = G[2](qz,dz);
for (int qy = 0; qy < Q1D[1]; ++qy)
{
for (int qx = 0; qx < Q1D[0]; ++qx)
{
grad[0](qx,qy,qz) += gradXY(0,qx,qy) * wz;
grad[1](qx,qy,qz) += gradXY(1,qx,qy) * wz;
grad[2](qx,qy,qz) += gradXY(2,qx,qy) * wDz;
}
}
}
}
for (int qz = 0; qz < Q1D[2]; ++qz)
{
for (int qy = 0; qy < Q1D[1]; ++qy)
{
for (int qx = 0; qx < Q1D[0]; ++qx)
{
const int q = qx + ((qy + (qz * Q1D[1])) * Q1D[0]);
const double O00 = qd(q,0);
const double O01 = qd(q,1);
const double O02 = qd(q,2);
const double O10 = symmetric ? O01 : qd(q,3);
const double O11 = symmetric ? qd(q,3) : qd(q,4);
const double O12 = symmetric ? qd(q,4) : qd(q,5);
const double O20 = symmetric ? O02 : qd(q,6);
const double O21 = symmetric ? O12 : qd(q,7);
const double O22 = symmetric ? qd(q,5) : qd(q,8);
const double grad0 = grad[0](qx,qy,qz);
const double grad1 = grad[1](qx,qy,qz);
const double grad2 = grad[2](qx,qy,qz);
grad[0](qx,qy,qz) = (O00*grad0)+(O01*grad1)+(O02*grad2);
grad[1](qx,qy,qz) = (O10*grad0)+(O11*grad1)+(O12*grad2);
grad[2](qx,qy,qz) = (O20*grad0)+(O21*grad1)+(O22*grad2);
} // qx
} // qy
} // qz
for (int qz = 0; qz < Q1D[2]; ++qz)
{
for (int dy = 0; dy < D1D[1]; ++dy)
{
for (int dx = 0; dx < D1D[0]; ++dx)
{
for (int d=0; d<3; ++d)
{
gradXY(d,dx,dy) = 0.0;
}
}
}
for (int qy = 0; qy < Q1D[1]; ++qy)
{
for (int dx = 0; dx < D1D[0]; ++dx)
{
for (int d=0; d<3; ++d)
{
gradX(d,dx) = 0.0;
}
}
for (int qx = 0; qx < Q1D[0]; ++qx)
{
const double gX = grad[0](qx,qy,qz);
const double gY = grad[1](qx,qy,qz);
const double gZ = grad[2](qx,qy,qz);
for (int dx = minQ[0][qx]; dx <= maxQ[0][qx]; ++dx)
{
const double wx = B[0](qx,dx);
const double wDx = G[0](qx,dx);
gradX(0,dx) += gX * wDx;
gradX(1,dx) += gY * wx;
gradX(2,dx) += gZ * wx;
}
}
for (int dy = minQ[1][qy]; dy <= maxQ[1][qy]; ++dy)
{
const double wy = B[1](qy,dy);
const double wDy = G[1](qy,dy);
for (int dx = 0; dx < D1D[0]; ++dx)
{
gradXY(0,dx,dy) += gradX(0,dx) * wy;
gradXY(1,dx,dy) += gradX(1,dx) * wDy;
gradXY(2,dx,dy) += gradX(2,dx) * wy;
}
}
}
for (int dz = minQ[2][qz]; dz <= maxQ[2][qz]; ++dz)
{
const double wz = B[2](qz,dz);
const double wDz = G[2](qz,dz);
for (int dy = 0; dy < D1D[1]; ++dy)
{
for (int dx = 0; dx < D1D[0]; ++dx)
{
Y(dx,dy,dz) +=
((gradXY(0,dx,dy) * wz) +
(gradXY(1,dx,dy) * wz) +
(gradXY(2,dx,dy) * wDz));
}
}
} // dz
} // qz
}
void DiffusionIntegrator::AddMultNURBSPA(const Vector &x, Vector &y) const
{
Vector xp, yp;
for (int p=0; p<numPatches; ++p)
{
Array<int> vdofs;
fespace->GetPatchVDofs(p, vdofs);
x.GetSubVector(vdofs, xp);
yp.SetSize(vdofs.Size());
yp = 0.0;
AddMultPatchPA(p, xp, yp);
y.AddElementVector(vdofs, yp);
}
}
} // namespace mfem
File diff suppressed because it is too large Load Diff
+277
View File
@@ -16,6 +16,7 @@
// Formulas at http://nines.cs.kuleuven.be/research/ecf/ecf.html
#include "fem.hpp"
#include "../mesh/nurbs.hpp"
#include <cmath>
#ifdef MFEM_USE_MPFR
@@ -173,6 +174,51 @@ void IntegrationRule::GrundmannMollerSimplexRule(int s, int n)
}
}
IntegrationRule*
IntegrationRule::ApplyToKnotIntervals(KnotVector const& kv) const
{
const int np = this->GetNPoints();
const int ne = kv.GetNE();
IntegrationRule *kvir = new IntegrationRule(ne * np);
double x0 = kv[0];
double x1 = x0;
int id = 0;
for (int e=0; e<ne; ++e)
{
x0 = x1;
if (e == ne-1)
{
x1 = kv[kv.Size() - 1];
}
else
{
// Find the next unique knot
while (id < kv.Size() - 1)
{
id++;
if (kv[id] != x0)
{
x1 = kv[id];
break;
}
}
}
const double s = x1 - x0;
for (int j=0; j<this->GetNPoints(); ++j)
{
const double x = x0 + (s * (*this)[j].x);
(*kvir)[(e * np) + j].Set1w(x, (*this)[j].weight);
}
}
return kvir;
}
#ifdef MFEM_USE_MPFR
@@ -1725,4 +1771,235 @@ IntegrationRule *IntegrationRules::CubeIntegrationRule(int Order)
return CubeIntRules[Order];
}
IntegrationRule& NURBSMeshRules::GetElementRule(const int elem,
const int patch, const int *ijk,
Array<const KnotVector*> const& kv,
bool & deleteRule) const
{
deleteRule = false;
// First check whether a rule has been assigned to element index elem.
auto search = elementToRule.find(elem);
if (search != elementToRule.end())
{
return *elementRule[search->second];
}
MFEM_VERIFY(patchRules1D.NumRows(),
"Undefined rule in NURBSMeshRules::GetElementRule");
// Use a tensor product of rules on the patch.
MFEM_VERIFY(kv.Size() == dim, "");
int np = 1;
std::vector<std::vector<double>> el(dim);
std::vector<int> npd;
npd.assign(3, 0);
for (int d=0; d<dim; ++d)
{
const int order = kv[d]->GetOrder();
const double kv0 = (*kv[d])[order + ijk[d]];
const double kv1 = (*kv[d])[order + ijk[d] + 1];
const bool rightEnd = (order + ijk[d] + 1) == (kv[d]->Size() - 1);
for (int i=0; i<patchRules1D(patch,d)->Size(); ++i)
{
const IntegrationPoint& ip = (*patchRules1D(patch,d))[i];
if (kv0 <= ip.x && (ip.x < kv1 || rightEnd))
{
const double x = (ip.x - kv0) / (kv1 - kv0);
el[d].push_back(x);
el[d].push_back(ip.weight);
}
}
npd[d] = el[d].size() / 2;
np *= npd[d];
}
IntegrationRule *irp = new IntegrationRule(np);
deleteRule = true;
// Set (*irp)[i + j*npd[0] + k*npd[0]*npd[1]] =
// (el[0][2*i], el[1][2*j], el[2][2*k])
MFEM_VERIFY(npd[0] > 0 && npd[1] > 0, "Assuming 2D or 3D");
for (int i = 0; i < npd[0]; ++i)
{
for (int j = 0; j < npd[1]; ++j)
{
for (int k = 0; k < std::max(npd[2], 1); ++k)
{
const int id = i + j*npd[0] + k*npd[0]*npd[1];
(*irp)[id].x = el[0][2*i];
(*irp)[id].y = el[1][2*j];
(*irp)[id].weight = el[0][(2*i)+1];
(*irp)[id].weight *= el[1][(2*j)+1];
if (npd[2] > 0)
{
(*irp)[id].z = el[2][2*k];
(*irp)[id].weight *= el[2][(2*k)+1];
}
}
}
}
return *irp;
}
void NURBSMeshRules::GetIntegrationPointFrom1D(const int patch, int i, int j,
int k, IntegrationPoint & ip)
{
MFEM_VERIFY(patchRules1D.NumRows() > 0,
"Assuming patchRules1D is set.");
ip.weight = (*patchRules1D(patch,0))[i].weight;
ip.x = (*patchRules1D(patch,0))[i].x;
if (dim > 1)
{
ip.weight *= (*patchRules1D(patch,1))[j].weight;
ip.y = (*patchRules1D(patch,1))[j].x; // 1D rule only has x
}
if (dim > 2)
{
ip.weight *= (*patchRules1D(patch,2))[k].weight;
ip.z = (*patchRules1D(patch,2))[k].x; // 1D rule only has x
}
}
void NURBSMeshRules::Finalize(Mesh const& mesh)
{
if ((int) pointToElem.size() == npatches) { return; } // Already set
MFEM_VERIFY(elementToRule.empty() && patchRules1D.NumRows() > 0
&& npatches > 0, "Assuming patchRules1D is set.");
MFEM_VERIFY(mesh.NURBSext, "");
MFEM_VERIFY(mesh.Dimension() == dim, "");
pointToElem.resize(npatches);
patchRules1D_KnotSpan.resize(npatches);
// First, find all the elements in each patch.
std::vector<std::vector<int>> patchElements(npatches);
for (int e=0; e<mesh.GetNE(); ++e)
{
patchElements[mesh.NURBSext->GetElementPatch(e)].push_back(e);
}
Array<int> ijk(3);
Array<int> maxijk(3);
Array<int> np(3); // Number of points in each dimension
ijk = 0;
Array<const KnotVector*> pkv;
for (int p=0; p<npatches; ++p)
{
patchRules1D_KnotSpan[p].resize(dim);
// For each patch, get the range of ijk.
mesh.NURBSext->GetPatchKnotVectors(p, pkv);
MFEM_VERIFY((int) pkv.Size() == dim, "");
maxijk = 1;
np = 1;
for (int d=0; d<dim; ++d)
{
maxijk[d] = pkv[d]->GetNKS();
np[d] = patchRules1D(p,d)->Size();
}
// For each patch, set a map from ijk to element index.
Array3D<int> ijk2elem(maxijk[0], maxijk[1], maxijk[2]);
ijk2elem = -1;
for (auto elem : patchElements[p])
{
mesh.NURBSext->GetElementIJK(elem, ijk);
MFEM_VERIFY(ijk2elem(ijk[0], ijk[1], ijk[2]) == -1, "");
ijk2elem(ijk[0], ijk[1], ijk[2]) = elem;
}
// For each point, find its ijk and from that its element index.
// It is assumed here that the NURBSFiniteElement kv the same as the
// patch kv.
for (int d=0; d<dim; ++d)
{
patchRules1D_KnotSpan[p][d].SetSize(patchRules1D(p,d)->Size());
for (int r=0; r<patchRules1D(p,d)->Size(); ++r)
{
const IntegrationPoint& ip = (*patchRules1D(p,d))[r];
const int order = pkv[d]->GetOrder();
// Find ijk_d such that ip.x is in the corresponding knot-span.
int ijk_d = 0;
bool found = false;
while (!found)
{
const double kv0 = (*pkv[d])[order + ijk_d];
const double kv1 = (*pkv[d])[order + ijk_d + 1];
const bool rightEnd = (order + ijk_d + 1) == (pkv[d]->Size() - 1);
if (kv0 <= ip.x && (ip.x < kv1 || rightEnd))
{
found = true;
}
else
{
ijk_d++;
}
}
patchRules1D_KnotSpan[p][d][r] = ijk_d;
}
}
pointToElem[p].SetSize(np[0], np[1], np[2]);
for (int i=0; i<np[0]; ++i)
for (int j=0; j<np[1]; ++j)
for (int k=0; k<np[2]; ++k)
{
const int elem = ijk2elem(patchRules1D_KnotSpan[p][0][i],
patchRules1D_KnotSpan[p][1][j],
patchRules1D_KnotSpan[p][2][k]);
MFEM_VERIFY(elem >= 0, "");
pointToElem[p](i,j,k) = elem;
}
} // Loop (p) over patches
}
void NURBSMeshRules::SetPatchRules1D(const int patch,
std::vector<const IntegrationRule*> & ir1D)
{
MFEM_VERIFY((int) ir1D.size() == dim, "Wrong dimension");
for (int i=0; i<dim; ++i)
{
patchRules1D(patch,i) = ir1D[i];
}
}
NURBSMeshRules::~NURBSMeshRules()
{
for (int i=0; i<patchRules1D.NumRows(); ++i)
for (int j=0; j<patchRules1D.NumCols(); ++j)
{
delete patchRules1D(i, j);
}
}
}
+101
View File
@@ -15,9 +15,15 @@
#include "../config/config.hpp"
#include "../general/array.hpp"
#include <vector>
#include <map>
namespace mfem
{
class KnotVector;
class Mesh;
/* Classes for IntegrationPoint, IntegrationRule, and container class
IntegrationRules. Declares the global variable IntRules */
@@ -257,10 +263,105 @@ public:
a call like this: `IntPoint(i).weight`. */
const Array<double> &GetWeights() const;
/// @brief Return an integration rule for KnotVector @a kv, defined by
/// applying this rule on each knot interval.
IntegrationRule* ApplyToKnotIntervals(KnotVector const& kv) const;
/// Destroys an IntegrationRule object
~IntegrationRule() { }
};
/// Class for defining different integration rules on each NURBS patch.
class NURBSMeshRules
{
public:
/// Construct a rule for each patch, using SetPatchRules1D.
NURBSMeshRules(const int numPatches, const int dim_) :
patchRules1D(numPatches, dim_),
npatches(numPatches), dim(dim_) { }
/// Returns a rule for the element.
IntegrationRule &GetElementRule(const int elem, const int patch,
const int *ijk,
Array<const KnotVector*> const& kv,
bool & deleteRule) const;
/// Add a rule to be used for individual elements. Returns the rule index.
std::size_t AddElementRule(IntegrationRule *ir_element)
{
elementRule.push_back(ir_element);
return elementRule.size() - 1;
}
/// @brief Set the integration rule for the element of the given index. This
/// rule is used instead of the rule for the patch containing the element.
void SetElementRule(const std::size_t element,
const std::size_t elementRuleIndex)
{
elementToRule[element] = elementRuleIndex;
}
/// @brief Set 1D integration rules to be used as a tensor product rule on
/// the patch with index @a patch. This class takes ownership of these rules.
void SetPatchRules1D(const int patch,
std::vector<const IntegrationRule*> & ir1D);
/// @brief For tensor product rules defined on each patch by
/// SetPatchRules1D(), return a pointer to the 1D rule in the specified
/// @a dimension.
const IntegrationRule* GetPatchRule1D(const int patch,
const int dimension) const
{
return patchRules1D(patch, dimension);
}
/// @brief For tensor product rules defined on each patch by
/// SetPatchRules1D(), return the integration point with index (i,j,k).
void GetIntegrationPointFrom1D(const int patch, int i, int j, int k,
IntegrationPoint & ip);
/// @brief Finalize() must be called before this class can be used for
/// assembly. In particular, it defines data used by GetPointElement().
void Finalize(Mesh const& mesh);
/// @brief For tensor product rules defined on each patch by
/// SetPatchRules1D(), returns the index of the element containing
/// integration point (i,j,k) for patch index @a patch. Finalize() must be
/// called first.
int GetPointElement(int patch, int i, int j, int k) const
{
return pointToElem[patch](i,j,k);
}
int GetDim() const { return dim; }
/// @brief For tensor product rules defined on each patch by
/// SetPatchRules1D(), returns an array of knot span indices for each
/// integration point in the specified @a dimension.
const Array<int>& GetPatchRule1D_KnotSpan(const int patch,
const int dimension) const
{
return patchRules1D_KnotSpan[patch][dimension];
}
~NURBSMeshRules();
private:
/// Tensor-product rules defined on all patches independently.
Array2D<const IntegrationRule*> patchRules1D;
/// Integration rules defined on elements.
std::vector<IntegrationRule*> elementRule;
std::map<std::size_t, std::size_t> elementToRule;
std::vector<Array3D<int>> pointToElem;
std::vector<std::vector<Array<int>>> patchRules1D_KnotSpan;
const int npatches;
const int dim;
};
/// A Class that defines 1-D numerical quadrature rules on [0,1].
class QuadratureFunctions1D
{
+22
View File
@@ -26,9 +26,23 @@ namespace mfem
assemble the local gradient operator and to compute the local energy. */
class NonlinearFormIntegrator
{
public:
enum Mode
{
ELEMENTWISE = 0, /**< Element-wise integration (default) */
PATCHWISE = 1, /**< Patch-wise integration (NURBS meshes) */
PATCHWISE_REDUCED = 2, /**< Patch-wise integration (NURBS meshes) with
reduced integration rules. */
};
protected:
const IntegrationRule *IntRule;
Mode integrationMode = Mode::ELEMENTWISE;
// Prescribed integration rules (not reduced approximate rules).
NURBSMeshRules *patchRules = nullptr;
// CEED extension
ceed::Operator* ceedOp;
@@ -42,6 +56,14 @@ public:
let the integrator choose (when @a ir == NULL). */
virtual void SetIntRule(const IntegrationRule *ir) { IntRule = ir; }
void SetIntegrationMode(Mode m) { integrationMode = m; }
/// For patchwise integration, SetNURBSPatchIntRule must be called.
void SetNURBSPatchIntRule(NURBSMeshRules *pr) { patchRules = pr; }
bool HasNURBSPatchIntRule() const { return patchRules != nullptr; }
bool Patchwise() const { return integrationMode != Mode::ELEMENTWISE; }
/// Prescribe a fixed IntegrationRule to use.
void SetIntegrationRule(const IntegrationRule &ir) { SetIntRule(&ir); }
+121 -80
View File
@@ -163,18 +163,18 @@ void ParFiniteElementSpace::Construct()
// calculate number of ghost DOFs
ngvdofs = pncmesh->GetNGhostVertices()
* fec->DofForGeometry(Geometry::POINT);
* fec->DofForGeometry(Geometry::Type::POINT);
if (pmesh->Dimension() > 1)
{
ngedofs = pncmesh->GetNGhostEdges()
* fec->DofForGeometry(Geometry::SEGMENT);
* fec->DofForGeometry(Geometry::Type::SEGMENT);
}
if (pmesh->Dimension() > 2)
{
int stride = fec->DofForGeometry(Geometry::SQUARE);
ngfdofs = pncmesh->GetNGhostFaces() * stride;
ngfdofs = pncmesh->GetNGhostFaces()
* fec->DofForGeometry(Geometry::Type::SQUARE);
}
// total number of ghost DOFs. Ghost DOFs start at index 'ndofs', i.e.,
@@ -1842,9 +1842,13 @@ int ParFiniteElementSpace::PackDof(int entity, int index, int edof) const
static int bisect(const int* array, int size, int value)
{
const int* end = array + size;
const int* pos = std::lower_bound(array, end, value);
MFEM_VERIFY(pos != end, "value not found");
return pos - array;
const int* pos = std::upper_bound(array, end, value);
MFEM_VERIFY(pos != array, "value not found");
if (pos == end)
{
MFEM_VERIFY(*(array+size - 1) == value, "Last entry must be exact")
}
return pos - array - 1;
}
/** Dissect a DOF number to obtain the entity type (0=vertex, 1=edge, 2=face),
@@ -1880,7 +1884,8 @@ void ParFiniteElementSpace::UnpackDof(int dof,
else // mixed faces or var-order space
{
const Table &table = var_face_dofs;
MFEM_ASSERT(table.Size(), "");
MFEM_ASSERT(table.Size() > 0, "");
int jpos = bisect(table.GetJ(), table.Size_of_connections(), dof);
index = bisect(table.GetI(), table.Size(), jpos);
edof = dof - table.GetRow(index)[0];
@@ -2010,7 +2015,6 @@ class NeighborRowMessage : public VarMessage<314>
public:
typedef NCMesh::MeshId MeshId;
typedef ParNCMesh::GroupId GroupId;
struct RowInfo
{
int entity, index, edof;
@@ -2022,8 +2026,6 @@ public:
RowInfo(int ent, int idx, int edof, GroupId grp)
: entity(ent), index(idx), edof(edof), group(grp) {}
typedef std::vector<RowInfo> List;
};
NeighborRowMessage() : pncmesh(NULL) {}
@@ -2034,7 +2036,7 @@ public:
rows.push_back(RowInfo(entity, index, edof, group, row));
}
const RowInfo::List& GetRows() const { return rows; }
const std::vector<RowInfo>& GetRows() const { return rows; }
void SetNCMesh(ParNCMesh* pnc) { pncmesh = pnc; }
void SetFEC(const FiniteElementCollection* fec_) { this->fec = fec_; }
@@ -2042,7 +2044,7 @@ public:
typedef std::map<int, NeighborRowMessage> Map;
protected:
RowInfo::List rows;
std::vector<RowInfo> rows;
ParNCMesh *pncmesh;
const FiniteElementCollection* fec;
@@ -2051,7 +2053,6 @@ protected:
virtual void Decode(int);
};
void NeighborRowMessage::Encode(int rank)
{
std::ostringstream stream;
@@ -2161,11 +2162,21 @@ void NeighborRowMessage::Decode(int rank)
ind = fec->DofOrderForOrientation(geom, fo);
}
double s = 1.0;
#ifdef MFEM_DEBUG_PMATRIX
mfem::out << "Rank " << pncmesh->MyRank << " receiving from " << rank
<< ": ent " << ent << ", index " << id.index
<< ", edof " << edof << " (id " << id.element << "/"
<< int(id.local) << ")" << std::endl;
#endif
// If edof arrived with a negative index, flip it, and the scaling.
double s = (edof < 0) ? -1.0 : 1.0;
edof = (edof < 0) ? -1 - edof : edof;
if (ind && (edof = ind[edof]) < 0)
{
edof = -1 - edof;
s = -1.0;
s *= -1.0;
}
rows.push_back(RowInfo(ent, id.index, edof, group_ids[gi++]));
@@ -2189,10 +2200,8 @@ ParFiniteElementSpace::ScheduleSendRow(const PMatrixRow &row, int dof,
int ent, idx, edof;
UnpackDof(dof, ent, idx, edof);
const ParNCMesh::CommGroup &group = pncmesh->GetGroup(group_id);
for (unsigned i = 0; i < group.size(); i++)
for (const auto &rank : pncmesh->GetGroup(group_id))
{
int rank = group[i];
if (rank != MyRank)
{
NeighborRowMessage &msg = send_msg[rank];
@@ -2312,7 +2321,7 @@ int ParFiniteElementSpace
&& fec->GetContType() == FiniteElementCollection::TANGENTIAL),
"Nedelec NC tets of order >= 2 are not supported yet.");
bool dg = (nvdofs == 0 && nedofs == 0 && nfdofs == 0);
const bool dg = (nvdofs == 0 && nedofs == 0 && nfdofs == 0);
#ifdef MFEM_PMATRIX_STATS
n_msgs_sent = n_msgs_recv = 0;
@@ -2321,7 +2330,7 @@ int ParFiniteElementSpace
// *** STEP 1: build master-slave dependency lists ***
int total_dofs = ndofs + ngdofs;
const int total_dofs = ndofs + ngdofs;
SparseMatrix deps(ndofs, total_dofs);
if (!dg && !partial)
@@ -2332,16 +2341,14 @@ int ParFiniteElementSpace
for (int entity = 0; entity <= 2; entity++)
{
const NCMesh::NCList &list = pncmesh->GetNCList(entity);
if (!list.masters.Size()) { continue; }
if (list.masters.Size() == 0) { continue; }
IsoparametricTransformation T;
DenseMatrix I;
// process masters that we own or that affect our edges/faces
for (int mi = 0; mi < list.masters.Size(); mi++)
for (const auto &mf : list.masters)
{
const NCMesh::Master &mf = list.masters[mi];
// get master DOFs
if (pncmesh->IsGhost(entity, mf.index))
{
@@ -2352,10 +2359,10 @@ int ParFiniteElementSpace
GetEntityDofs(entity, mf.index, master_dofs, mf.Geom());
}
if (!master_dofs.Size()) { continue; }
if (master_dofs.Size() == 0) { continue; }
const FiniteElement* fe = fec->FiniteElementForGeometry(mf.Geom());
if (!fe) { continue; }
if (fe == nullptr) { continue; }
switch (mf.Geom())
{
@@ -2371,7 +2378,7 @@ int ParFiniteElementSpace
const NCMesh::Slave &sf = list.slaves[si];
if (pncmesh->IsGhost(entity, sf.index)) { continue; }
const int variant = 0; // TODO parallel var-order
constexpr int variant = 0; // TODO parallel var-order
GetEntityDofs(entity, sf.index, slave_dofs, mf.Geom(), variant);
if (!slave_dofs.Size()) { continue; }
@@ -2398,37 +2405,37 @@ int ParFiniteElementSpace
{
Array<int> dofs;
// initialize dof_group[], dof_owner[]
for (int entity = 0; entity <= 2; entity++)
auto initialize_group_and_owner = [&dof_group, &dof_owner, &dofs,
this](int entity, const MeshId &id)
{
const NCMesh::NCList &list = pncmesh->GetNCList(entity);
if (id.index < 0) { return; }
int lsize[3] =
{ list.conforming.Size(), list.masters.Size(), list.slaves.Size() };
GroupId owner = pncmesh->GetEntityOwnerId(entity, id.index);
GroupId group = pncmesh->GetEntityGroupId(entity, id.index);
for (int l = 0; l < 3; l++)
GetBareDofs(entity, id.index, dofs);
for (auto dof : dofs)
{
for (int i = 0; i < lsize[l]; i++)
{
const MeshId &id =
(l == 0) ? list.conforming[i] :
(l == 1) ? (const MeshId&) list.masters[i]
/* */ : (const MeshId&) list.slaves[i];
dof_owner[dof] = owner;
dof_group[dof] = group;
}
};
if (id.index < 0) { continue; }
GroupId owner = pncmesh->GetEntityOwnerId(entity, id.index);
GroupId group = pncmesh->GetEntityGroupId(entity, id.index);
GetBareDofs(entity, id.index, dofs);
for (int j = 0; j < dofs.Size(); j++)
{
int dof = dofs[j];
dof_owner[dof] = owner;
dof_group[dof] = group;
}
}
// initialize dof_group[], dof_owner[] in sequence
for (int entity : {0,1,2})
{
for (const auto &id : pncmesh->GetNCList(entity).conforming)
{
initialize_group_and_owner(entity, id);
}
for (const auto &id : pncmesh->GetNCList(entity).masters)
{
initialize_group_and_owner(entity, id);
}
for (const auto &id : pncmesh->GetNCList(entity).slaves)
{
initialize_group_and_owner(entity, id);
}
}
}
@@ -2440,15 +2447,50 @@ int ParFiniteElementSpace
// DOFs that stayed independent and are ours are true DOFs
int num_true_dofs = 0;
for (int i = 0; i < ndofs; i++)
for (int i = 0; i < ndofs; ++i)
{
if (dof_owner[i] == 0 && deps.RowSize(i) == 0)
{
num_true_dofs++;
++num_true_dofs;
finalized[i] = true;
}
}
#ifdef MFEM_DEBUG_PMATRIX
// Helper for dumping diagnostics on one dof
auto dof_diagnostics = [&](int dof, bool print_diagnostic)
{
const auto &comm_group = pncmesh->GetGroup(dof_group[dof]);
std::stringstream msg;
msg << std::boolalpha;
msg << "R" << Mpi::WorldRank() << " dof " << dof
<< " owner_rank " << pncmesh->GetGroup(dof_owner[dof])[0] << " CommGroup {";
for (const auto &x : comm_group)
{
msg << x << ' ';
}
msg << "} finalized " << finalized[dof];
Array<int> cols;
if (dof < ndofs)
{
Vector row;
deps.GetRow(dof, cols, row);
msg << " deps cols {";
for (const auto &x : cols)
{
msg << x << ' ';
}
msg << '}';
}
int entity, index, edof;
UnpackDof(dof, entity, index, edof);
msg << " entity " << entity << " index " << index << " edof " << edof;
return msg.str();
};
#endif
// calculate global offsets
HYPRE_BigInt loc_sizes[2] = { ndofs*vdim, num_true_dofs*vdim };
Array<HYPRE_BigInt>* offsets[2] = { &dof_offs, &tdof_offs };
@@ -2470,10 +2512,10 @@ int ParFiniteElementSpace
std::vector<PMatrixRow> pmatrix(total_dofs);
bool bynodes = (ordering == Ordering::byNODES);
int vdim_factor = bynodes ? 1 : vdim;
int dof_stride = bynodes ? ndofs : 1;
int tdof_stride = bynodes ? num_true_dofs : 1;
const bool bynodes = (ordering == Ordering::byNODES);
const int vdim_factor = bynodes ? 1 : vdim;
const int dof_stride = bynodes ? ndofs : 1;
const int tdof_stride = bynodes ? num_true_dofs : 1;
// big container for all messages we send (the list is for iterations)
std::list<NeighborRowMessage::Map> send_msg;
@@ -2495,13 +2537,13 @@ int ParFiniteElementSpace
for (int vd = 0; vd < vdim; vd++)
{
int vdof = dof*vdim_factor + vd*dof_stride;
int vtdof = tdof*vdim_factor + vd*tdof_stride;
const int vdof = dof*vdim_factor + vd*dof_stride;
const int vtdof = tdof*vdim_factor + vd*tdof_stride;
if (R_) { (*R_)->Add(vtdof, vdof, 1.0); }
if (dof_tdof) { (*dof_tdof)[vdof] = vtdof; }
}
tdof++;
++tdof;
}
}
@@ -2542,14 +2584,12 @@ int ParFiniteElementSpace
n_rows_recv += recv_msg.GetRows().size();
#endif
const NeighborRowMessage::RowInfo::List &rows = recv_msg.GetRows();
for (unsigned i = 0; i < rows.size(); i++)
for (const auto &ri : recv_msg.GetRows())
{
const NeighborRowMessage::RowInfo &ri = rows[i];
int dof = PackDof(ri.entity, ri.index, ri.edof);
const int dof = PackDof(ri.entity, ri.index, ri.edof);
pmatrix[dof] = ri.row;
if (dof < ndofs && !finalized[dof]) { num_finalized++; }
if (dof < ndofs && !finalized[dof]) { ++num_finalized; }
finalized[dof] = true;
if (ri.group >= 0 && dof_group[dof] != ri.group)
@@ -2567,13 +2607,14 @@ int ParFiniteElementSpace
done = true;
for (int dof = 0; dof < ndofs; dof++)
{
if (finalized[dof]) { continue; }
bool owned = (dof_owner[dof] == 0);
bool shared = (dof_group[dof] != 0);
if (owned && DofFinalizable(dof, finalized, deps))
const bool owned = (dof_owner[dof] == 0);
if (!finalized[dof]
&& owned
&& DofFinalizable(dof, finalized, deps))
{
int ent, idx, edof;
UnpackDof(dof, ent, idx, edof);
const int* dep_col = deps.GetRowColumns(dof);
const double* dep_coef = deps.GetRowEntries(dof);
int num_dep = deps.RowSize(dof);
@@ -2588,10 +2629,11 @@ int ParFiniteElementSpace
pmatrix[dof] = buffer;
finalized[dof] = true;
num_finalized++;
++num_finalized;
done = false;
// send row to neighbors who need it
const bool shared = (dof_group[dof] != 0);
if (shared)
{
ScheduleSendRow(pmatrix[dof], dof, dof_group[dof],
@@ -2602,7 +2644,7 @@ int ParFiniteElementSpace
}
#ifdef MFEM_DEBUG_PMATRIX
/*static int dump = 0;
static int dump = 0;
if (dump < 10)
{
char fname[100];
@@ -2610,7 +2652,7 @@ int ParFiniteElementSpace
std::ofstream f(fname);
DebugDumpDOFs(f, deps, dof_group, dof_owner, finalized);
dump++;
}*/
}
#endif
// send current batch of messages
@@ -2635,10 +2677,9 @@ int ParFiniteElementSpace
}
// make sure we can discard all send buffers
for (std::list<NeighborRowMessage::Map>::iterator
it = send_msg.begin(); it != send_msg.end(); ++it)
for (auto &msg : send_msg)
{
NeighborRowMessage::WaitAllSent(*it);
NeighborRowMessage::WaitAllSent(msg);
}
#ifdef MFEM_PMATRIX_STATS
+12 -11
View File
@@ -115,7 +115,8 @@ std::ostream &operator<<(std::ostream &os, const QuadratureFunction &qf)
}
void QuadratureFunction::SaveVTU(std::ostream &os, VTKFormat format,
int compression_level) const
int compression_level,
const std::string &field_name) const
{
os << R"(<VTKFile type="UnstructuredGrid" version="0.1")";
if (compression_level != 0)
@@ -129,11 +130,9 @@ void QuadratureFunction::SaveVTU(std::ostream &os, VTKFormat format,
const char *type_str = (format != VTKFormat::BINARY32) ? "Float64" : "Float32";
std::vector<char> buf;
Mesh &mesh = *qspace->GetMesh();
int np = qspace->GetSize();
int ne = mesh.GetNE();
int sdim = mesh.SpaceDimension();
const int np = qspace->GetSize();
const int ne = qspace->GetNE();
const int sdim = qspace->GetMesh()->SpaceDimension();
// For quadrature functions, each point is a vertex cell, so number of cells
// is equal to number of points
@@ -148,7 +147,7 @@ void QuadratureFunction::SaveVTU(std::ostream &os, VTKFormat format,
Vector pt(sdim);
for (int i = 0; i < ne; i++)
{
ElementTransformation &T = *mesh.GetElementTransformation(i);
ElementTransformation &T = *qspace->GetTransformation(i);
const IntegrationRule &ir = GetIntRule(i);
for (int j = 0; j < ir.Size(); j++)
{
@@ -205,8 +204,9 @@ void QuadratureFunction::SaveVTU(std::ostream &os, VTKFormat format,
os << "</Cells>\n";
os << "<PointData>\n";
os << "<DataArray type=\"" << type_str << "\" Name=\"u\" format=\""
<< fmt_str << "\" NumberOfComponents=\"" << vdim << "\">\n";
os << "<DataArray type=\"" << type_str << "\" Name=\"" << field_name
<< "\" format=\"" << fmt_str << "\" NumberOfComponents=\"" << vdim
<< "\">\n";
for (int i = 0; i < ne; i++)
{
DenseMatrix vals;
@@ -233,10 +233,11 @@ void QuadratureFunction::SaveVTU(std::ostream &os, VTKFormat format,
}
void QuadratureFunction::SaveVTU(const std::string &filename, VTKFormat format,
int compression_level) const
int compression_level,
const std::string &field_name) const
{
std::ofstream f(filename + ".vtu");
SaveVTU(f, format, compression_level);
SaveVTU(f, format, compression_level, field_name);
}
}
+2 -2
View File
@@ -185,7 +185,7 @@ public:
/// format is VTKFormat::ASCII. Otherwise, zlib compression will be used for
/// binary data.
void SaveVTU(std::ostream &out, VTKFormat format=VTKFormat::ASCII,
int compression_level=0) const;
int compression_level=0, const std::string &field_name="u") const;
/// @brief Save the QuadratureFunction to a VTU (ParaView) file.
///
@@ -193,7 +193,7 @@ public:
/// @sa SaveVTU(std::ostream &out, VTKFormat format=VTKFormat::ASCII,
/// int compression_level=0)
void SaveVTU(const std::string &filename, VTKFormat format=VTKFormat::ASCII,
int compression_level=0) const;
int compression_level=0, const std::string &field_name="u") const;
virtual ~QuadratureFunction()
{
+237 -107
View File
@@ -2838,6 +2838,7 @@ TMOP_Integrator::~TMOP_Integrator()
delete lim_func;
delete adapt_lim_gf;
delete surf_fit_gf;
delete surf_fit_limiter;
delete surf_fit_grad;
delete surf_fit_hess;
for (int i = 0; i < ElemDer.Size(); i++)
@@ -2912,6 +2913,10 @@ void TMOP_Integrator::EnableSurfaceFitting(const GridFunction &s0,
Coefficient &coeff,
AdaptivityEvaluator &ae)
{
// To have both we must duplicate the markers.
MFEM_VERIFY(surf_fit_pos == NULL,
"Using both fitting approaches is not supported.");
delete surf_fit_gf;
surf_fit_gf = new GridFunction(s0);
surf_fit_gf->CountElementsPerVDof(surf_fit_dof_count);
@@ -2925,12 +2930,37 @@ void TMOP_Integrator::EnableSurfaceFitting(const GridFunction &s0,
(*surf_fit_gf->FESpace()->GetMesh()->GetNodes(), *surf_fit_gf);
}
void TMOP_Integrator::EnableSurfaceFitting(const GridFunction &pos,
const Array<bool> &smarker,
Coefficient &coeff)
{
// To have both we must duplicate the markers.
MFEM_VERIFY(surf_fit_gf == NULL,
"Using both fitting approaches is not supported.");
MFEM_VERIFY(pos.FESpace()->GetMesh()->GetNodes(),
"Positions on a mesh without Nodes is not supported.");
MFEM_VERIFY(pos.FESpace()->GetOrdering() ==
pos.FESpace()->GetMesh()->GetNodes()->FESpace()->GetOrdering(),
"Incompatible ordering of spaces!");
surf_fit_pos = &pos;
pos.CountElementsPerVDof(surf_fit_dof_count);
surf_fit_marker = &smarker;
surf_fit_coeff = &coeff;
delete surf_fit_limiter;
surf_fit_limiter = new TMOP_QuadraticLimiter;
}
#ifdef MFEM_USE_MPI
void TMOP_Integrator::EnableSurfaceFitting(const ParGridFunction &s0,
const Array<bool> &smarker,
Coefficient &coeff,
AdaptivityEvaluator &ae)
{
// To have both we must duplicate the markers.
MFEM_VERIFY(surf_fit_pos == NULL,
"Using both fitting approaches is not supported.");
delete surf_fit_gf;
surf_fit_gf = new GridFunction(s0);
s0.CountElementsPerVDof(surf_fit_dof_count);
@@ -3014,31 +3044,53 @@ void TMOP_Integrator::EnableSurfaceFittingFromSource(
}
#endif
void TMOP_Integrator::GetSurfaceFittingErrors(double &err_avg, double &err_max)
void TMOP_Integrator::GetSurfaceFittingErrors(const Vector &pos,
double &err_avg, double &err_max)
{
MFEM_VERIFY(surf_fit_gf, "Surface fitting has not been enabled.");
MFEM_VERIFY(surf_fit_marker, "Surface fitting has not been enabled.");
const FiniteElementSpace *fes =
(surf_fit_gf) ? surf_fit_gf->FESpace() : surf_fit_pos->FESpace();
#ifdef MFEM_USE_MPI
auto pfes =
dynamic_cast<const ParFiniteElementSpace *>(surf_fit_gf->FESpace());
auto pfes = dynamic_cast<const ParFiniteElementSpace *>(fes);
bool parallel = (pfes) ? true : false;
#endif
int dim = fes->GetMesh()->Dimension();
const int node_cnt = surf_fit_marker->Size();
err_max = 0.0;
int dof_cnt = 0;
double err_sum = 0.0;
for (int i = 0; i < surf_fit_marker->Size(); i++)
for (int i = 0; i < node_cnt; i++)
{
if ((*surf_fit_marker)[i] == true)
{
if ((*surf_fit_marker)[i] == false) { continue; }
#ifdef MFEM_USE_MPI
// Don't count the overlapping DOFs in parallel.
if (parallel && pfes->GetLocalTDofNumber(i) < 0) { continue; }
// Don't count the overlapping DOFs in parallel.
// The pfes might be ordered byVDIM, while the loop goes consecutively.
const int dof_i = pfes->DofToVDof(i, 0);
if (parallel && pfes->GetLocalTDofNumber(dof_i) < 0) { continue; }
#endif
dof_cnt++;
err_max = fmax(err_max, fabs((*surf_fit_gf)(i)));
err_sum += fabs((*surf_fit_gf)(i));
dof_cnt++;
double sigma_s = 0.0;
if (surf_fit_gf) { sigma_s = fabs((*surf_fit_gf)(i)); }
if (surf_fit_pos)
{
Vector pos_s(dim), pos_s_target(dim);
for (int d = 0; d < dim; d++)
{
pos_s(d) = (fes->GetOrdering() == Ordering::byNODES) ?
pos(d*node_cnt + i) : pos(i*dim + d);
pos_s_target(d) = (fes->GetOrdering() == Ordering::byNODES)
? (*surf_fit_pos)(d*node_cnt + i)
: (*surf_fit_pos)(i*dim + d);
}
sigma_s = pos_s.DistanceTo(pos_s_target);
}
err_max = fmax(err_max, sigma_s);
err_sum += sigma_s;
}
#ifdef MFEM_USE_MPI
@@ -3092,7 +3144,7 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
// as part of a FD derivative computation (because we include the exact
// derivatives of these terms in FD computations).
const bool adaptive_limiting = (adapt_lim_gf && fd_call_flag == false);
const bool surface_fit = (surf_fit_gf && fd_call_flag == false);
const bool surface_fit = (surf_fit_marker && fd_call_flag == false);
DSh.SetSize(dof, dim);
Jrt.SetSize(dim);
@@ -3195,21 +3247,42 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
// Contribution from the surface fitting term.
if (surface_fit)
{
const IntegrationRule &ir_s =
surf_fit_gf->FESpace()->GetFE(el_id)->GetNodes();
Array<int> dofs;
Vector sigma_e;
surf_fit_gf->FESpace()->GetElementDofs(el_id, dofs);
surf_fit_gf->GetSubVector(dofs, sigma_e);
for (int s = 0; s < dofs.Size(); s++)
// Scalar for surf_fit_gf, vector for surf_fit_pos, but that's ok.
const FiniteElementSpace *fes_fit =
(surf_fit_gf) ? surf_fit_gf->FESpace() : surf_fit_pos->FESpace();
const IntegrationRule *ir_s = &fes_fit->GetFE(el_id)->GetNodes();
Array<int> vdofs;
fes_fit->GetElementVDofs(el_id, vdofs);
Vector sigma_e(dof);
if (surf_fit_gf) { surf_fit_gf->GetSubVector(vdofs, sigma_e); }
for (int s = 0; s < dof; s++)
{
if ((*surf_fit_marker)[dofs[s]] == true)
// Because surf_fit_pos.fes might be ordered byVDIM.
const int scalar_dof_id = fes_fit->VDofToDof(vdofs[s]);
if ((*surf_fit_marker)[scalar_dof_id] == false) { continue; }
const IntegrationPoint &ip_s = ir_s->IntPoint(s);
Tpr->SetIntPoint(&ip_s);
if (surf_fit_gf)
{
const IntegrationPoint &ip_s = ir_s.IntPoint(s);
Tpr->SetIntPoint(&ip_s);
energy += surf_fit_coeff->Eval(*Tpr, ip_s) * surf_fit_normal *
sigma_e(s) * sigma_e(s);
}
if (surf_fit_pos)
{
// Fitting to exact positions.
Vector pos(dim), pos_target(dim);
for (int d = 0; d < dim; d++)
{
pos(d) = PMatI(s, d);
pos_target(d) = (*surf_fit_pos)(vdofs[d*dof + s]);
}
energy += surf_fit_coeff->Eval(*Tpr, ip_s) * surf_fit_normal *
surf_fit_limiter->Eval(pos, pos_target, 1.0);
}
}
}
@@ -3437,7 +3510,8 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
// Define ref->physical transformation, when a Coefficient is specified.
IsoparametricTransformation *Tpr = NULL;
if (metric_coeff || lim_coeff || adapt_lim_gf || surf_fit_gf || exact_action)
if (metric_coeff || lim_coeff || adapt_lim_gf ||
surf_fit_gf || surf_fit_pos || exact_action)
{
Tpr = new IsoparametricTransformation;
Tpr->SetFE(&el);
@@ -3519,7 +3593,7 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
}
if (adapt_lim_gf) { AssembleElemVecAdaptLim(el, *Tpr, ir, weights, PMatO); }
if (surf_fit_gf) { AssembleElemVecSurfFit(el, *Tpr, PMatO); }
if (surf_fit_gf || surf_fit_pos) { AssembleElemVecSurfFit(el, *Tpr, PMatO); }
delete Tpr;
}
@@ -3571,7 +3645,7 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
// Define ref->physical transformation, when a Coefficient is specified.
IsoparametricTransformation *Tpr = NULL;
if (metric_coeff || lim_coeff || adapt_lim_gf || surf_fit_gf)
if (metric_coeff || lim_coeff || adapt_lim_gf || surf_fit_gf || surf_fit_pos)
{
Tpr = new IsoparametricTransformation;
Tpr->SetFE(&el);
@@ -3627,7 +3701,7 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
}
if (adapt_lim_gf) { AssembleElemGradAdaptLim(el, *Tpr, ir, weights, elmat); }
if (surf_fit_gf) { AssembleElemGradSurfFit(el, *Tpr, elmat); }
if (surf_fit_gf || surf_fit_pos) { AssembleElemGradSurfFit(el, *Tpr, elmat);}
delete Tpr;
}
@@ -3734,49 +3808,75 @@ void TMOP_Integrator::AssembleElemVecSurfFit(const FiniteElement &el_x,
DenseMatrix &mat)
{
const int el_id = Tpr.ElementNo;
// Scalar for surf_fit_gf, vector for surf_fit_pos, but that's ok.
const FiniteElementSpace *fes_fit =
(surf_fit_gf) ? surf_fit_gf->FESpace() : surf_fit_pos->FESpace();
const FiniteElement &el_s = *fes_fit->GetFE(el_id);
const int dof_s = el_s.GetDof(), dim = el_x.GetDim();
// Check if the element has any DOFs marked for surface fitting.
Array<int> sdofs, dofs;
surf_fit_gf->FESpace()->GetElementDofs(el_id, sdofs);
Array<int> dofs, vdofs;
fes_fit->GetElementVDofs(el_id, vdofs);
int count = 0;
for (int s = 0; s < sdofs.Size(); s++)
for (int s = 0; s < dof_s; s++)
{
count += ((*surf_fit_marker)[sdofs[s]]) ? 1 : 0;
// Because surf_fit_pos.fes might be ordered byVDIM.
const int scalar_dof_id = fes_fit->VDofToDof(vdofs[s]);
count += ((*surf_fit_marker)[scalar_dof_id]) ? 1 : 0;
}
if (count == 0) { return; }
const FiniteElement &el_s = *surf_fit_gf->FESpace()->GetFE(el_id);
const int dof_s = el_s.GetDof(), dim = el_x.GetDim();
Vector sigma_e;
surf_fit_gf->GetSubVector(sdofs, sigma_e);
// Project the gradient of sigma in the same space.
// The FE coefficients of the gradient go in surf_fit_grad_e.
Vector sigma_e(dof_s);
DenseMatrix surf_fit_grad_e(dof_s, dim);
Vector grad_ptr(surf_fit_grad_e.GetData(), dof_s * dim);
DenseMatrix grad_phys; // This will be (dof x dim, dof).
if (surf_fit_gf_bg)
if (surf_fit_gf || surf_fit_gf_bg)
{
surf_fit_grad->FESpace()->GetElementVDofs(el_id, dofs);
surf_fit_grad->GetSubVector(dofs, grad_ptr);
}
else
{
el_s.ProjectGrad(el_s, Tpr, grad_phys);
grad_phys.Mult(sigma_e, grad_ptr);
surf_fit_gf->GetSubVector(vdofs, sigma_e);
// Project the gradient of sigma in the same space.
// The FE coefficients of the gradient go in surf_fit_grad_e.
Vector grad_ptr(surf_fit_grad_e.GetData(), dof_s * dim);
DenseMatrix grad_phys; // This will be (dof x dim, dof).
if (surf_fit_gf_bg)
{
surf_fit_grad->FESpace()->GetElementVDofs(el_id, dofs);
surf_fit_grad->GetSubVector(dofs, grad_ptr);
}
else
{
el_s.ProjectGrad(el_s, Tpr, grad_phys);
grad_phys.Mult(sigma_e, grad_ptr);
}
}
else { Tpr.GetPointMat().Transpose(PMatI); }
const IntegrationRule &ir = el_s.GetNodes();
for (int s = 0; s < dof_s; s++)
{
if ((*surf_fit_marker)[sdofs[s]] == false) { continue; }
// Because surf_fit_pos.fes might be ordered byVDIM.
const int scalar_dof_id = fes_fit->VDofToDof(vdofs[s]);
if ((*surf_fit_marker)[scalar_dof_id] == false) { continue; }
const IntegrationPoint &ip = ir.IntPoint(s);
Tpr.SetIntPoint(&ip);
const double w = 2.0 * surf_fit_normal *
surf_fit_coeff->Eval(Tpr, ip) * sigma_e(s) *
1.0/surf_fit_dof_count[sdofs[s]];
double w = surf_fit_normal * surf_fit_coeff->Eval(Tpr, ip) *
1.0 / surf_fit_dof_count[vdofs[s]];
if (surf_fit_gf) { w *= 2.0 * sigma_e(s); }
if (surf_fit_pos)
{
Vector pos(dim), pos_target(dim);
for (int d = 0; d < dim; d++)
{
pos(d) = PMatI(s, d);
pos_target(d) = (*surf_fit_pos)(vdofs[d*dof_s + s]);
}
Vector grad_s(dim);
surf_fit_limiter->Eval_d1(pos, pos_target, 1.0, grad_s);
for (int d = 0; d < dim; d++) { surf_fit_grad_e(s, d) = grad_s(d); }
}
for (int d = 0; d < dim; d++)
{
mat(s, d) += w * surf_fit_grad_e(s, d);
@@ -3789,79 +3889,109 @@ void TMOP_Integrator::AssembleElemGradSurfFit(const FiniteElement &el_x,
DenseMatrix &mat)
{
const int el_id = Tpr.ElementNo;
// Scalar for surf_fit_gf, vector for surf_fit_pos, but that's ok.
const FiniteElementSpace *fes_fit =
(surf_fit_gf) ? surf_fit_gf->FESpace() : surf_fit_pos->FESpace();
const FiniteElement &el_s = *fes_fit->GetFE(el_id);
const int dof_s = el_s.GetDof(), dim = el_x.GetDim();
// Check if the element has any DOFs marked for surface fitting.
Array<int> dofs, sdofs;
surf_fit_gf->FESpace()->GetElementDofs(el_id, sdofs);
int ndofs = sdofs.Size();
Array<int> dofs, vdofs;
fes_fit->GetElementVDofs(el_id, vdofs);
int count = 0;
for (int s = 0; s < ndofs; s++)
for (int s = 0; s < dof_s; s++)
{
count += ((*surf_fit_marker)[sdofs[s]]) ? 1 : 0;
// Because surf_fit_pos.fes might be ordered byVDIM.
const int scalar_dof_id = fes_fit->VDofToDof(vdofs[s]);
count += ((*surf_fit_marker)[scalar_dof_id]) ? 1 : 0;
}
if (count == 0) { return; }
const FiniteElement &el_s = *surf_fit_gf->FESpace()->GetFE(el_id);
const int dof_s = el_s.GetDof(), dim = el_x.GetDim();
Vector sigma_e;
surf_fit_gf->GetSubVector(sdofs, sigma_e);
Vector sigma_e(dof_s);
DenseMatrix surf_fit_grad_e(dof_s, dim);
Vector grad_ptr(surf_fit_grad_e.GetData(), dof_s * dim);
DenseMatrix grad_phys;
if (surf_fit_gf_bg)
{
surf_fit_grad->FESpace()->GetElementVDofs(el_id, dofs);
surf_fit_grad->GetSubVector(dofs, grad_ptr);
}
else
{
el_s.ProjectGrad(el_s, Tpr, grad_phys);
grad_phys.Mult(sigma_e, grad_ptr);
}
DenseMatrix surf_fit_hess_e(dof_s, dim*dim);
Vector hess_ptr(surf_fit_hess_e.GetData(), dof_s*dim*dim);
if (surf_fit_gf_bg)
if (surf_fit_gf || surf_fit_gf_bg)
{
surf_fit_hess->FESpace()->GetElementVDofs(el_id, dofs);
surf_fit_hess->GetSubVector(dofs, hess_ptr);
}
else
{
surf_fit_hess_e.SetSize(dof_s*dim, dim);
Mult(grad_phys, surf_fit_grad_e, surf_fit_hess_e);
surf_fit_hess_e.SetSize(dof_s, dim * dim);
surf_fit_gf->GetSubVector(vdofs, sigma_e);
// Project the gradient of sigma in the same space.
// The FE coefficients of the gradient go in surf_fit_grad_e.
Vector grad_ptr(surf_fit_grad_e.GetData(), dof_s * dim);
DenseMatrix grad_phys; // This will be (dof x dim, dof).
if (surf_fit_gf_bg)
{
surf_fit_grad->FESpace()->GetElementVDofs(el_id, dofs);
surf_fit_grad->GetSubVector(dofs, grad_ptr);
}
else
{
el_s.ProjectGrad(el_s, Tpr, grad_phys);
grad_phys.Mult(sigma_e, grad_ptr);
}
// Project the Hessian of sigma in the same space.
// The FE coefficients of the Hessian go in surf_fit_hess_e.
Vector hess_ptr(surf_fit_hess_e.GetData(), dof_s*dim*dim);
if (surf_fit_gf_bg)
{
surf_fit_hess->FESpace()->GetElementVDofs(el_id, dofs);
surf_fit_hess->GetSubVector(dofs, hess_ptr);
}
else
{
surf_fit_hess_e.SetSize(dof_s*dim, dim);
Mult(grad_phys, surf_fit_grad_e, surf_fit_hess_e);
surf_fit_hess_e.SetSize(dof_s, dim * dim);
}
}
else { Tpr.GetPointMat().Transpose(PMatI); }
const IntegrationRule &ir = el_s.GetNodes();
Vector surf_fit_grad_s(dim);
DenseMatrix surf_fit_hess_s(dim, dim);
for (int s = 0; s < dof_s; s++)
{
if ((*surf_fit_marker)[sdofs[s]] == false) { continue; }
// Because surf_fit_pos.fes might be ordered byVDIM.
const int scalar_dof_id = fes_fit->VDofToDof(vdofs[s]);
if ((*surf_fit_marker)[scalar_dof_id] == false) { continue; }
const IntegrationPoint &ip = ir.IntPoint(s);
Tpr.SetIntPoint(&ip);
double w = surf_fit_normal * surf_fit_coeff->Eval(Tpr, ip);
Vector gg_ptr(surf_fit_hess_s.GetData(), dim * dim);
surf_fit_hess_e.GetRow(s, gg_ptr);
if (surf_fit_gf || surf_fit_gf_bg)
{
Vector gg_ptr(surf_fit_hess_s.GetData(), dim * dim);
surf_fit_hess_e.GetRow(s, gg_ptr);
w *= 2.0;
}
if (surf_fit_pos)
{
Vector pos(dim), pos_target(dim);
for (int d = 0; d < dim; d++)
{
pos(d) = PMatI(s, d);
pos_target(d) = (*surf_fit_pos)(vdofs[d*dof_s + s]);
}
// Eval_d2 returns the full Hessian, but we still use the general
// computation that's in the dim x dim loop below.
sigma_e(s) = 1.0;
for (int d = 0; d < dim; d++) { surf_fit_grad_e(s, d) = 0.0; }
surf_fit_limiter->Eval_d2(pos, pos_target, 1.0, surf_fit_hess_s);
}
// Loops over the local matrix.
const double w = surf_fit_normal * surf_fit_coeff->Eval(Tpr, ip);
for (int idim = 0; idim < dim; idim++)
{
for (int jdim = 0; jdim <= idim; jdim++)
{
double entry = w * ( 2.0 * surf_fit_grad_e(s, idim) *
/* */ surf_fit_grad_e(s, jdim) +
2.0 * sigma_e(s) * surf_fit_hess_s(idim, jdim));
entry *= 1.0/surf_fit_dof_count[sdofs[s]];
int idx = s + idim*ndofs;
int jdx = s + jdim*ndofs;
double entry = w * ( surf_fit_grad_e(s, idim) *
surf_fit_grad_e(s, jdim) +
sigma_e(s) * surf_fit_hess_s(idim, jdim));
entry *= 1.0 / surf_fit_dof_count[vdofs[s]];
int idx = s + idim*dof_s;
int jdx = s + jdim*dof_s;
mat(idx, jdx) += entry;
if (idx != jdx) { mat(jdx, idx) += entry; }
}
@@ -3930,7 +4060,7 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el,
fd_call_flag = false;
// Contributions from adaptive limiting, surface fitting (exact derivatives).
if (adapt_lim_gf || surf_fit_gf)
if (adapt_lim_gf || surf_fit_gf || surf_fit_pos)
{
const IntegrationRule &ir = ActionIntegrationRule(el);
const int nqp = ir.GetNPoints();
@@ -3955,7 +4085,7 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el,
PMatO.UseExternalData(elvect.GetData(), dof, dim);
if (adapt_lim_gf) { AssembleElemVecAdaptLim(el, Tpr, ir, weights, PMatO); }
if (surf_fit_gf) { AssembleElemVecSurfFit(el, Tpr, PMatO); }
if (surf_fit_gf || surf_fit_pos) { AssembleElemVecSurfFit(el, Tpr, PMatO); }
}
}
@@ -4030,7 +4160,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el,
fd_call_flag = false;
// Contributions from adaptive limiting.
if (adapt_lim_gf || surf_fit_gf)
if (adapt_lim_gf || surf_fit_gf || surf_fit_pos)
{
const IntegrationRule &ir = GradientIntegrationRule(el);
const int nqp = ir.GetNPoints();
@@ -4054,7 +4184,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el,
}
if (adapt_lim_gf) { AssembleElemGradAdaptLim(el, Tpr, ir, weights, elmat); }
if (surf_fit_gf) { AssembleElemGradSurfFit(el, Tpr, elmat); }
if (surf_fit_gf || surf_fit_pos) { AssembleElemGradSurfFit(el, Tpr, elmat); }
}
}
@@ -4087,7 +4217,7 @@ void TMOP_Integrator::EnableNormalization(const GridFunction &x)
metric_normal = 1.0 / metric_normal;
lim_normal = 1.0 / lim_normal;
//if (surf_fit_gf) { surf_fit_normal = 1.0 / surf_fit_normal; }
if (surf_fit_gf) { surf_fit_normal = lim_normal; }
if (surf_fit_gf || surf_fit_pos) { surf_fit_normal = lim_normal; }
}
#ifdef MFEM_USE_MPI
@@ -4100,7 +4230,7 @@ void TMOP_Integrator::ParEnableNormalization(const ParGridFunction &x)
metric_normal = 1.0 / rdc[0];
lim_normal = 1.0 / rdc[1];
// if (surf_fit_gf) { surf_fit_normal = 1.0 / rdc[2]; }
if (surf_fit_gf) { surf_fit_normal = lim_normal; }
if (surf_fit_gf || surf_fit_pos) { surf_fit_normal = lim_normal; }
}
#endif
+34 -10
View File
@@ -1772,10 +1772,14 @@ protected:
AdaptivityEvaluator *adapt_lim_eval; // Not owned.
// Surface fitting.
GridFunction *surf_fit_gf; // Owned, Updated by surf_fit_eval.
const Array<bool> *surf_fit_marker; // Not owned.
Coefficient *surf_fit_coeff; // Not owned.
AdaptivityEvaluator *surf_fit_eval; // Not owned.
const Array<bool> *surf_fit_marker; // Not owned. Nodes to fit.
Coefficient *surf_fit_coeff; // Not owned. Fitting term scaling.
// Fitting to a discrete level set.
GridFunction *surf_fit_gf; // Owned. Updated by surf_fit_eval.
AdaptivityEvaluator *surf_fit_eval; // Not owned.
// Fitting to given physical positions.
TMOP_QuadraticLimiter *surf_fit_limiter; // Owned. Created internally.
const GridFunction *surf_fit_pos; // Not owned. Positions to fit.
double surf_fit_normal;
bool surf_fit_gf_bg;
GridFunction *surf_fit_grad, *surf_fit_hess;
@@ -1976,9 +1980,10 @@ public:
lim_dist(NULL), lim_func(NULL), lim_normal(1.0),
adapt_lim_gf0(NULL), adapt_lim_gf(NULL), adapt_lim_coeff(NULL),
adapt_lim_eval(NULL),
surf_fit_gf(NULL), surf_fit_marker(NULL),
surf_fit_coeff(NULL),
surf_fit_eval(NULL), surf_fit_normal(1.0),
surf_fit_marker(NULL), surf_fit_coeff(NULL),
surf_fit_gf(NULL), surf_fit_eval(NULL),
surf_fit_limiter(NULL), surf_fit_pos(NULL),
surf_fit_normal(1.0),
surf_fit_gf_bg(false), surf_fit_grad(NULL), surf_fit_hess(NULL),
surf_fit_eval_bg_grad(NULL), surf_fit_eval_bg_hess(NULL),
discr_tc(dynamic_cast<DiscreteAdaptTC *>(tc)),
@@ -2080,7 +2085,7 @@ public:
AdaptivityEvaluator &ae);
#ifdef MFEM_USE_MPI
/// Parallel support for surface fitting.
/// Parallel support for surface fitting to the zero level set of a function.
void EnableSurfaceFitting(const ParGridFunction &s0,
const Array<bool> &smarker, Coefficient &coeff,
AdaptivityEvaluator &ae);
@@ -2118,8 +2123,27 @@ public:
ParGridFunction &s0_hess,
AdaptivityEvaluator &ahe);
#endif
void GetSurfaceFittingErrors(double &err_avg, double &err_max);
bool IsSurfaceFittingEnabled() { return (surf_fit_gf != NULL); }
/** @brief Fitting of certain DOFs to given positions in physical space.
Having a set S of marked nodes (or DOFs) and their target positions in
physical space x_t, we move these nodes to the target positions during
the optimization process.
This function adds to the TMOP functional the term
@f$ \sum_{i \in S} c \frac{1}{2} (x_i - x_{t,i})^2 @f$,
where @f$c@f$ corresponds to @a coeff below and is evaluated at the
DOF locations.
@param[in] pos The desired positions for the mesh nodes.
@param[in] smarker Indicates which DOFs will be aligned.
@param[in] coeff Coefficient c for the above integral. */
void EnableSurfaceFitting(const GridFunction &pos,
const Array<bool> &smarker, Coefficient &coeff);
void GetSurfaceFittingErrors(const Vector &pos,
double &err_avg, double &err_max);
bool IsSurfaceFittingEnabled()
{
return surf_fit_gf != NULL || surf_fit_pos != NULL;
}
/// Update the original/reference nodes used for limiting.
void SetLimitingNodes(const GridFunction &n0) { lim_nodes0 = &n0; }
+9 -7
View File
@@ -419,7 +419,7 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
double avg_surf_fit_err, max_surf_fit_err = 0.0;
if (surf_fit_max_threshold > 0.0)
{
GetSurfaceFittingError(avg_surf_fit_err, max_surf_fit_err);
GetSurfaceFittingError(x_out_loc, avg_surf_fit_err, max_surf_fit_err);
if (max_surf_fit_err < surf_fit_max_threshold)
{
if (print_options.iterations)
@@ -519,7 +519,7 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
double avg_fit_err, max_fit_err = 0.0;
if (surf_fit_max_threshold > 0.0)
{
GetSurfaceFittingError(avg_fit_err, max_fit_err);
GetSurfaceFittingError(x_out_loc, avg_fit_err, max_fit_err);
}
if (surf_fit_max_threshold > 0.0 && max_fit_err >= 1.2*max_surf_fit_err)
{
@@ -662,7 +662,8 @@ void TMOPNewtonSolver::GetSurfaceFittingWeight(Array<double> &weights) const
}
}
void TMOPNewtonSolver::GetSurfaceFittingError(double &err_avg,
void TMOPNewtonSolver::GetSurfaceFittingError(const Vector &x_loc,
double &err_avg,
double &err_max) const
{
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
@@ -680,7 +681,7 @@ void TMOPNewtonSolver::GetSurfaceFittingError(double &err_avg,
{
if (ti->IsSurfaceFittingEnabled())
{
ti->GetSurfaceFittingErrors(err_avg_loc, err_max_loc);
ti->GetSurfaceFittingErrors(x_loc, err_avg_loc, err_max_loc);
err_avg = std::fmax(err_avg_loc, err_avg);
err_max = std::fmax(err_max_loc, err_max);
}
@@ -693,7 +694,7 @@ void TMOPNewtonSolver::GetSurfaceFittingError(double &err_avg,
{
if (ati[j]->IsSurfaceFittingEnabled())
{
ati[j]->GetSurfaceFittingErrors(err_avg_loc, err_max_loc);
ati[j]->GetSurfaceFittingErrors(x_loc, err_avg_loc, err_max_loc);
err_avg = std::fmax(err_avg_loc, err_avg);
err_max = std::fmax(err_max_loc, err_max);
}
@@ -733,12 +734,13 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
}
Vector x_loc;
const FiniteElementSpace *x_fes;
const FiniteElementSpace *x_fes = nullptr;
if (parallel)
{
#ifdef MFEM_USE_MPI
const ParNonlinearForm *pnlf =
dynamic_cast<const ParNonlinearForm *>(oper);
x_fes = pnlf->ParFESpace();
x_loc.SetSize(x_fes->GetVSize());
x_fes->GetProlongationMatrix()->Mult(x, x_loc);
@@ -789,7 +791,7 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
if (update_surf_fit_coeff)
{
// Get surface fitting errors.
GetSurfaceFittingError(surf_fit_err_avg, surf_fit_err_max);
GetSurfaceFittingError(x_loc, surf_fit_err_avg, surf_fit_err_max);
// Get array with surface fitting weights.
Array<double> weights;
GetSurfaceFittingWeight(weights);
+2 -1
View File
@@ -181,7 +181,8 @@ protected:
/// Get the average and maximum surface fitting error at the marked nodes.
/// If there is more than 1 TMOP integrator, we get the maximum of the
/// average and maximum error over all integrators.
virtual void GetSurfaceFittingError(double &err_avg, double &err_max) const;
virtual void GetSurfaceFittingError(const Vector &x_loc,
double &err_avg, double &err_max) const;
/// Update surface fitting weight as surf_fit_weight *= factor.
void UpdateSurfaceFittingWeight(double factor) const;
+344 -106
View File
@@ -546,6 +546,234 @@ L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
const FiniteElementSpace& fes_ho_, const FiniteElementSpace& fes_lor_)
: L2Projection(fes_ho_, fes_lor_)
{
std::unique_ptr<SparseMatrix> R_mat, M_LH_mat;
std::tie(R_mat, M_LH_mat) = ComputeSparseRAndM_LH();
FiniteElementSpace fes_ho_scalar(fes_ho.GetMesh(), fes_ho.FEColl(), 1);
FiniteElementSpace fes_lor_scalar(fes_lor.GetMesh(), fes_lor.FEColl(), 1);
const SparseMatrix *P_ho = fes_ho_scalar.GetConformingProlongation();
const SparseMatrix *P_lor = fes_lor_scalar.GetConformingProlongation();
if (P_ho || P_lor)
{
if (P_ho && P_lor)
{
R_mat.reset(RAP(*P_lor, *R_mat, *P_ho));
M_LH_mat.reset(RAP(*P_lor, *M_LH_mat, *P_ho));
}
else if (P_ho)
{
R_mat.reset(mfem::Mult(*R_mat, *P_ho));
M_LH_mat.reset(mfem::Mult(*M_LH_mat, *P_ho));
}
else // P_lor != nullptr
{
R_mat.reset(mfem::Mult(*P_lor, *R_mat));
M_LH_mat.reset(mfem::Mult(*P_lor, *M_LH_mat));
}
}
SparseMatrix *RTxM_LH_mat = TransposeMult(*R_mat, *M_LH_mat);
precon.reset(new DSmoother(*RTxM_LH_mat));
// Set ownership
RTxM_LH.reset(RTxM_LH_mat);
R = std::move(R_mat);
M_LH = std::move(M_LH_mat);
SetupPCG();
}
#ifdef MFEM_USE_MPI
L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
const ParFiniteElementSpace& pfes_ho, const ParFiniteElementSpace& pfes_lor)
: L2Projection(pfes_ho, pfes_lor),
pcg(pfes_ho.GetComm())
{
std::tie(R, M_LH) = ComputeSparseRAndM_LH();
ParFiniteElementSpace pfes_ho_scalar(pfes_ho.GetParMesh(),
pfes_ho.FEColl(), 1);
ParFiniteElementSpace pfes_lor_scalar(pfes_lor.GetParMesh(),
pfes_lor.FEColl(), 1);
HypreParMatrix R_local = HypreParMatrix(pfes_ho.GetComm(),
pfes_lor_scalar.GlobalVSize(),
pfes_ho_scalar.GlobalVSize(),
pfes_lor_scalar.GetDofOffsets(),
pfes_ho_scalar.GetDofOffsets(),
static_cast<SparseMatrix*>(R.get()));
HypreParMatrix M_LH_local = HypreParMatrix(pfes_ho.GetComm(),
pfes_lor_scalar.GlobalVSize(),
pfes_ho_scalar.GlobalVSize(),
pfes_lor_scalar.GetDofOffsets(),
pfes_ho_scalar.GetDofOffsets(),
static_cast<SparseMatrix*>(M_LH.get()));
HypreParMatrix *R_mat = RAP(pfes_lor_scalar.Dof_TrueDof_Matrix(),
&R_local, pfes_ho_scalar.Dof_TrueDof_Matrix());
HypreParMatrix *M_LH_mat = RAP(pfes_lor_scalar.Dof_TrueDof_Matrix(),
&M_LH_local, pfes_ho_scalar.Dof_TrueDof_Matrix());
std::unique_ptr<HypreParMatrix> R_T(R_mat->Transpose());
HypreParMatrix *RTxM_LH_mat = ParMult(R_T.get(), M_LH_mat, true);
HypreBoomerAMG *amg = new HypreBoomerAMG(*RTxM_LH_mat);
amg->SetPrintLevel(0);
R.reset(R_mat);
M_LH.reset(M_LH_mat);
RTxM_LH.reset(RTxM_LH_mat);
precon.reset(amg);
SetupPCG();
pcg.SetPreconditioner(*precon);
pcg.SetOperator(*RTxM_LH);
}
#endif
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetupPCG()
{
// Basic PCG solver setup
pcg.SetPrintLevel(0);
// pcg.SetPrintLevel(IterativeSolver::PrintLevel().Summary());
pcg.SetMaxIter(1000);
// initial values for relative and absolute tolerance
pcg.SetRelTol(1e-13);
pcg.SetAbsTol(1e-13);
pcg.SetPreconditioner(*precon);
pcg.SetOperator(*RTxM_LH);
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::Mult(
const Vector& x, Vector& y) const
{
Vector X(fes_ho.GetTrueVSize());
Vector X_dim(R->Width());
Vector Y_dim(R->Height());
Vector Y(fes_lor.GetTrueVSize());
Array<int> vdofs_list;
GetTDofs(fes_ho, x, X);
for (int d = 0; d < fes_ho.GetVDim(); ++d)
{
TDofsListByVDim(fes_ho, d, vdofs_list);
X.GetSubVector(vdofs_list, X_dim);
R->Mult(X_dim, Y_dim);
TDofsListByVDim(fes_lor, d, vdofs_list);
Y.SetSubVector(vdofs_list, Y_dim);
}
SetFromTDofs(fes_lor, Y, y);
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::MultTranspose(
const Vector& x, Vector& y) const
{
Vector X(fes_lor.GetTrueVSize());
Vector X_dim(R->Height());
Vector Y_dim(R->Width());
Vector Y(fes_ho.GetTrueVSize());
Array<int> vdofs_list;
GetTDofsTranspose(fes_lor, x, X);
for (int d = 0; d < fes_ho.GetVDim(); ++d)
{
TDofsListByVDim(fes_lor, d, vdofs_list);
X.GetSubVector(vdofs_list, X_dim);
R->MultTranspose(X_dim, Y_dim);
TDofsListByVDim(fes_ho, d, vdofs_list);
Y.SetSubVector(vdofs_list, Y_dim);
}
SetFromTDofsTranspose(fes_ho, Y, y);
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::Prolongate(
const Vector& x, Vector& y) const
{
Vector X(fes_lor.GetTrueVSize());
Vector X_dim(M_LH->Height());
Vector Xbar(pcg.Width());
Vector Y_dim(pcg.Height());
Vector Y(fes_ho.GetTrueVSize());
Array<int> vdofs_list;
GetTDofs(fes_lor, x, X);
for (int d = 0; d < fes_ho.GetVDim(); ++d)
{
TDofsListByVDim(fes_lor, d, vdofs_list);
X.GetSubVector(vdofs_list, X_dim);
// Compute y = P x = (R^T M_LH)^(-1) M_LH^T X = (R^T M_LH)^(-1) Xbar
M_LH->MultTranspose(X_dim, Xbar);
Y_dim = 0.0;
pcg.Mult(Xbar, Y_dim);
TDofsListByVDim(fes_ho, d, vdofs_list);
Y.SetSubVector(vdofs_list, Y_dim);
}
SetFromTDofs(fes_ho, Y, y);
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::ProlongateTranspose(
const Vector& x, Vector& y) const
{
Vector X(fes_ho.GetTrueVSize());
Vector X_dim(pcg.Width());
Vector Xbar(pcg.Height());
Vector Y_dim(M_LH->Height());
Vector Y(fes_lor.GetTrueVSize());
Array<int> vdofs_list;
GetTDofsTranspose(fes_ho, x, X);
for (int d = 0; d < fes_ho.GetVDim(); ++d)
{
TDofsListByVDim(fes_ho, d, vdofs_list);
X.GetSubVector(vdofs_list, X_dim);
// Compute y = P^T x = M_LH (R^T M_LH)^(-1) X = M_LH Xbar
Xbar = 0.0;
pcg.Mult(X_dim, Xbar);
M_LH->Mult(Xbar, Y_dim);
TDofsListByVDim(fes_lor, d, vdofs_list);
Y.SetSubVector(vdofs_list, Y_dim);
}
SetFromTDofsTranspose(fes_lor, Y, y);
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetRelTol(double p_rtol_)
{
pcg.SetRelTol(p_rtol_);
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetAbsTol(double p_atol_)
{
pcg.SetAbsTol(p_atol_);
}
std::pair<
std::unique_ptr<SparseMatrix>,
std::unique_ptr<SparseMatrix>>
L2ProjectionGridTransfer::L2ProjectionH1Space::ComputeSparseRAndM_LH()
{
std::pair<std::unique_ptr<SparseMatrix>,
std::unique_ptr<SparseMatrix>> r_and_mlh;
Mesh* mesh_ho = fes_ho.GetMesh();
Mesh* mesh_lor = fes_lor.GetMesh();
int nel_ho = mesh_ho->GetNE();
@@ -553,7 +781,7 @@ L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
int ndof_lor = fes_lor.GetNDofs();
// If the local mesh is empty, skip all computations
if (nel_ho == 0) { return; }
if (nel_ho == 0) { return {nullptr, nullptr}; }
const CoarseFineTransformations& cf_tr = mesh_lor->GetRefinementTransforms();
@@ -611,18 +839,26 @@ L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
}
}
// DOF by DOF inverse of non-zero entries
for (int i = 0; i < ndof_lor; ++i)
{
ML_inv[i] = 1.0 / ML_inv[i];
}
LumpedMassInverse(ML_inv);
// Compute sparsity pattern for R = M_L^(-1) M_LH and allocate
AllocR();
r_and_mlh.first = AllocR();
// Allocate M_LH (same sparsity pattern as R)
// L refers to the low-order refined mesh (DOFs correspond to rows)
// H refers to the higher-order mesh (DOFs correspond to columns)
M_LH = SparseMatrix(R.GetI(), R.GetJ(), NULL,
R.Height(), R.Width(), false, true, true);
Memory<int> I(r_and_mlh.first->Height() + 1);
for (int icol = 0; icol < r_and_mlh.first->Height() + 1; ++icol)
{
I[icol] = r_and_mlh.first->GetI()[icol];
}
Memory<int> J(r_and_mlh.first->NumNonZeroElems());
for (int jcol = 0; jcol < r_and_mlh.first->NumNonZeroElems(); ++jcol)
{
J[jcol] = r_and_mlh.first->GetJ()[jcol];
}
r_and_mlh.second = std::unique_ptr<SparseMatrix>(new SparseMatrix(
I, J, NULL,
r_and_mlh.first->Height(), r_and_mlh.first->Width(), true, true, true));
IntegrationPointTransformation ip_tr;
IsoparametricTransformation& emb_tr = ip_tr.Transf;
@@ -667,131 +903,118 @@ L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
}
Array<int> dofs_ho(nedof_ho);
fes_ho.GetElementDofs(iho, dofs_ho);
M_LH.AddSubMatrix(dofs_lor, dofs_ho, M_LH_el);
R.AddSubMatrix(dofs_lor, dofs_ho, R_el);
r_and_mlh.second->AddSubMatrix(dofs_lor, dofs_ho, M_LH_el);
r_and_mlh.first->AddSubMatrix(dofs_lor, dofs_ho, R_el);
}
}
// Create PCG solver
RTxM_LH = TransposeMult(R, M_LH);
pcg.SetPrintLevel(0);
pcg.SetMaxIter(1000);
// initial values for relative and absolute tolerance
SetRelTol(1e-13);
SetAbsTol(1e-13);
Ds = DSmoother(*RTxM_LH);
pcg.SetPreconditioner(Ds);
pcg.SetOperator(*RTxM_LH);
return r_and_mlh;
}
L2ProjectionGridTransfer::L2ProjectionH1Space::~L2ProjectionH1Space()
void L2ProjectionGridTransfer::L2ProjectionH1Space::GetTDofs(
const FiniteElementSpace& fes, const Vector& x, Vector& X) const
{
delete RTxM_LH;
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::Mult(
const Vector& x, Vector& y) const
{
int vdim = fes_ho.GetVDim();
const int ndof_ho = fes_ho.GetNDofs();
const int ndof_lor = fes_lor.GetNDofs();
Array<int> dofs_ho(ndof_ho);
Array<int> dofs_lor(ndof_lor);
Vector x_dim(ndof_ho);
Vector y_dim(ndof_lor);
for (int d = 0; d < vdim; ++d)
const Operator* res = fes.GetRestrictionOperator();
if (res)
{
fes_ho.GetVDofs(d, dofs_ho);
fes_lor.GetVDofs(d, dofs_lor);
x.GetSubVector(dofs_ho, x_dim);
R.Mult(x_dim, y_dim);
y.SetSubVector(dofs_lor, y_dim);
res->Mult(x, X);
}
else
{
X = x;
}
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::MultTranspose(
const Vector& x, Vector& y) const
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetFromTDofs(
const FiniteElementSpace& fes, const Vector &X, Vector& x) const
{
int vdim = fes_ho.GetVDim();
const int ndof_ho = fes_ho.GetNDofs();
const int ndof_lor = fes_lor.GetNDofs();
Array<int> dofs_ho(ndof_ho);
Array<int> dofs_lor(ndof_lor);
Vector x_dim(ndof_lor);
Vector y_dim(ndof_ho);
for (int d = 0; d < vdim; ++d)
const Operator* P = fes.GetProlongationMatrix();
if (P)
{
fes_ho.GetVDofs(d, dofs_ho);
fes_lor.GetVDofs(d, dofs_lor);
x.GetSubVector(dofs_lor, x_dim);
R.MultTranspose(x_dim, y_dim);
y.SetSubVector(dofs_ho, y_dim);
P->Mult(X, x);
}
else
{
x = X;
}
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::Prolongate(
const Vector& x, Vector& y) const
void L2ProjectionGridTransfer::L2ProjectionH1Space::GetTDofsTranspose(
const FiniteElementSpace& fes, const Vector& x, Vector& X) const
{
int vdim = fes_ho.GetVDim();
const int ndof_ho = fes_ho.GetNDofs();
const int ndof_lor = fes_lor.GetNDofs();
Array<int> dofs_ho(ndof_ho);
Array<int> dofs_lor(ndof_lor);
Vector x_dim(ndof_lor);
Vector y_dim(ndof_ho);
Vector xbar(ndof_ho);
for (int d = 0; d < vdim; ++d)
const Operator* P = fes.GetProlongationMatrix();
if (P)
{
fes_lor.GetVDofs(d, dofs_lor);
x.GetSubVector(dofs_lor, x_dim);
// Compute y = P x = (R^T M_LH)^(-1) M_LH^T x = (R^T M_LH)^(-1) xbar
M_LH.MultTranspose(x_dim, xbar);
y_dim = 0.0;
pcg.Mult(xbar, y_dim);
fes_ho.GetVDofs(d, dofs_ho);
y.SetSubVector(dofs_ho, y_dim);
P->MultTranspose(x, X);
}
else
{
X = x;
}
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::ProlongateTranspose(
const Vector& x, Vector& y) const
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetFromTDofsTranspose(
const FiniteElementSpace& fes, const Vector &X, Vector& x) const
{
int vdim = fes_ho.GetVDim();
const int ndof_ho = fes_ho.GetNDofs();
const int ndof_lor = fes_lor.GetNDofs();
Array<int> dofs_ho(ndof_ho);
Array<int> dofs_lor(ndof_lor);
Vector x_dim(ndof_ho);
Vector y_dim(ndof_lor);
Vector xbar(ndof_ho);
for (int d = 0; d < vdim; ++d)
const Operator *R_op = fes.GetRestrictionOperator();
if (R_op)
{
fes_ho.GetVDofs(d, dofs_ho);
x.GetSubVector(dofs_ho, x_dim);
// Compute y = P^T x = M_LH (R^T M_LH)^(-1) x = M_LH xbar
xbar = 0.0;
pcg.Mult(x_dim, xbar);
M_LH.Mult(xbar, y_dim);
fes_lor.GetVDofs(d, dofs_lor);
y.SetSubVector(dofs_lor, y_dim);
R_op->MultTranspose(X, x);
}
else
{
x = X;
}
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetRelTol(double p_rtol_)
void L2ProjectionGridTransfer::L2ProjectionH1Space::TDofsListByVDim(
const FiniteElementSpace& fes, int vdim, Array<int>& vdofs_list) const
{
pcg.SetRelTol(p_rtol_);
const SparseMatrix *R_mat = fes.GetRestrictionMatrix();
if (R_mat)
{
Array<int> x_vdofs_list(fes.GetNDofs());
Array<int> x_vdofs_marker(fes.GetVSize());
Array<int> X_vdofs_marker(fes.GetTrueVSize());
fes.GetVDofs(vdim, x_vdofs_list);
FiniteElementSpace::ListToMarker(x_vdofs_list, fes.GetVSize(), x_vdofs_marker);
R_mat->BooleanMult(x_vdofs_marker, X_vdofs_marker);
FiniteElementSpace::MarkerToList(X_vdofs_marker, vdofs_list);
}
else
{
vdofs_list.SetSize(fes.GetNDofs());
fes.GetVDofs(vdim, vdofs_list);
}
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetAbsTol(double p_atol_)
void L2ProjectionGridTransfer::L2ProjectionH1Space::LumpedMassInverse(
Vector& ML_inv) const
{
pcg.SetAbsTol(p_atol_);
Vector ML_inv_full(fes_lor.GetVSize());
// set ML_inv on dofs for vdim = 0
Array<int> vdofs_list(fes_lor.GetNDofs());
fes_lor.GetVDofs(0, vdofs_list);
ML_inv_full.SetSubVector(vdofs_list, ML_inv);
Vector ML_inv_true(fes_lor.GetTrueVSize());
const Operator *P = fes_lor.GetProlongationMatrix();
if (P) { P->MultTranspose(ML_inv_full, ML_inv_true); }
else { ML_inv_true = ML_inv_full; }
for (int i = 0; i < ML_inv_true.Size(); ++i)
{
ML_inv_true[i] = 1.0 / ML_inv_true[i];
}
if (P) { P->Mult(ML_inv_true, ML_inv_full); }
else { ML_inv_full = ML_inv_true; }
ML_inv_full.GetSubVector(vdofs_list, ML_inv);
}
void L2ProjectionGridTransfer::L2ProjectionH1Space::AllocR()
std::unique_ptr<SparseMatrix>
L2ProjectionGridTransfer::L2ProjectionH1Space::AllocR()
{
const Table& elem_dof_ho = fes_ho.GetElementToDofTable();
const Table& elem_dof_lor = fes_lor.GetElementToDofTable();
@@ -871,11 +1094,13 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::AllocR()
dof_lor_dof_ho.SortRows();
double* data = Memory<double>(dof_dofI[ndof_lor]);
R = SparseMatrix(dof_dofI, dof_dofJ, data, ndof_lor, ndof_ho,
true, true, true);
R = 0.0;
std::unique_ptr<SparseMatrix> R_local(new SparseMatrix(
dof_dofI, dof_dofJ, data, ndof_lor, ndof_ho, true, true, true));
(*R_local) = 0.0;
dof_lor_dof_ho.LoseData();
return R_local;
}
L2ProjectionGridTransfer::~L2ProjectionGridTransfer()
@@ -905,7 +1130,20 @@ void L2ProjectionGridTransfer::BuildF()
if (!force_l2_space &&
dom_fes.FEColl()->GetContType() == FiniteElementCollection::CONTINUOUS)
{
F = new L2ProjectionH1Space(dom_fes, ran_fes);
if (!Parallel())
{
F = new L2ProjectionH1Space(dom_fes, ran_fes);
}
else
{
#ifdef MFEM_USE_MPI
const mfem::ParFiniteElementSpace& dom_pfes =
static_cast<mfem::ParFiniteElementSpace&>(dom_fes);
const mfem::ParFiniteElementSpace& ran_pfes =
static_cast<mfem::ParFiniteElementSpace&>(ran_fes);
F = new L2ProjectionH1Space(dom_pfes, ran_pfes);
#endif
}
}
else
{
+64 -25
View File
@@ -180,9 +180,15 @@ protected:
public:
virtual void Prolongate(const Vector& x, Vector& y) const = 0;
virtual void ProlongateTranspose(const Vector& x, Vector& y) const = 0;
/// Sets relative tolerance and absolute tolerance in preconditioned
/// conjugate gradient solver. Only used for H1 spaces.
/// @brief Sets relative tolerance in preconditioned conjugate gradient
/// solver.
///
/// Only used for H1 spaces.
virtual void SetRelTol(double p_rtol_) = 0;
/// @brief Sets absolute tolerance in preconditioned conjugate gradient
/// solver.
///
/// Only used for H1 spaces.
virtual void SetAbsTol(double p_atol_) = 0;
protected:
const FiniteElementSpace& fes_ho;
@@ -249,29 +255,22 @@ protected:
/// conservative left-inverse prolongation operation. This functionality
/// is also provided as an Operator by L2Prolongation.
virtual void ProlongateTranspose(const Vector& x, Vector& y) const;
virtual void SetRelTol(double p_rtol_) {}
virtual void SetAbsTol(double p_atol_) {}
virtual void SetRelTol(double p_rtol_) { } ///< No-op.
virtual void SetAbsTol(double p_atol_) { } ///< No-op.
};
/** Class for projection operator between a H1 high-order finite element
space on a coarse mesh, and a H1 low-order finite element space on a
refined mesh (LOR). */
/** Projection operator between a H1 high-order finite element space on a
coarse mesh, and a H1 low-order finite element space on a refined mesh
(LOR). */
class L2ProjectionH1Space : public L2Projection
{
// The restriction operator is represented as a SparseMatrix R. The
// prolongation operator is a dense matrix computed as the inverse of (R^T
// M_L R), and hence, is not stored.
SparseMatrix R;
// Used to compute P = (RTxM_LH)^(-1) M_LH^T
SparseMatrix M_LH;
SparseMatrix* RTxM_LH;
CGSolver pcg;
DSmoother Ds;
public:
L2ProjectionH1Space(const FiniteElementSpace& fes_ho_,
const FiniteElementSpace& fes_lor_);
virtual ~L2ProjectionH1Space();
L2ProjectionH1Space(const FiniteElementSpace &fes_ho_,
const FiniteElementSpace &fes_lor_);
#ifdef MFEM_USE_MPI
L2ProjectionH1Space(const ParFiniteElementSpace &pfes_ho_,
const ParFiniteElementSpace &pfes_lor_);
#endif
/// Maps <tt>x</tt>, primal field coefficients defined on a coarse mesh
/// with a higher order H1 finite element space, to <tt>y</tt>, primal
/// field coefficients defined on a refined mesh with a low order H1
@@ -305,11 +304,51 @@ protected:
virtual void ProlongateTranspose(const Vector& x, Vector& y) const;
virtual void SetRelTol(double p_rtol_);
virtual void SetAbsTol(double p_atol_);
private:
/// Computes sparsity pattern and initializes R matrix. Based on
/// BilinearForm::AllocMat() except maps between HO elements and LOR
/// elements.
void AllocR();
protected:
/// Sets up the PCG solver (sets parameters, operator, and preconditioner)
void SetupPCG();
/// Computes on-rank R and M_LH matrices.
std::pair<std::unique_ptr<SparseMatrix>,
std::unique_ptr<SparseMatrix>> ComputeSparseRAndM_LH();
/// @brief Recovers vector of tdofs given a vector of dofs and a finite
/// element space
void GetTDofs(const FiniteElementSpace& fes, const Vector& x, Vector& X) const;
/// Sets dof values given a vector of tdofs and a finite element space
void SetFromTDofs(const FiniteElementSpace& fes,
const Vector& X,
Vector& x) const;
/// @brief Recovers a vector of dual field coefficients on the tdofs given
/// a vector of dual coefficients and a finite element space
void GetTDofsTranspose(const FiniteElementSpace& fes,
const Vector& x,
Vector& X) const;
/// @brief Sets dual field coefficients given a vector of dual field
/// coefficients on the tdofs and a finite element space
void SetFromTDofsTranspose(const FiniteElementSpace& fes,
const Vector& X,
Vector& x) const;
/// @brief Fills the vdofs_list array with a list of vdofs for a given
/// vdim and a given finite element space
void TDofsListByVDim(const FiniteElementSpace& fes,
int vdim,
Array<int>& vdofs_list) const;
/// Returns the inverse of an on-rank lumped mass matrix
void LumpedMassInverse(Vector& ML_inv) const;
/// @brief Computes sparsity pattern and initializes R matrix.
///
/// Based on BilinearForm::AllocMat(), except maps between coarse HO
/// elements and refined LOR elements.
std::unique_ptr<SparseMatrix> AllocR();
CGSolver pcg;
std::unique_ptr<Solver> precon;
// The restriction operator is represented as an Operator R. The
// prolongation operator is a dense matrix computed as the inverse of (R^T
// M_L R), and hence, is not stored.
std::unique_ptr<Operator> R;
// Used to compute P = (RT*M_LH)^(-1) M_LH^T
std::unique_ptr<Operator> M_LH;
std::unique_ptr<Operator> RTxM_LH;
};
/** Mass-conservative prolongation operator going in the opposite direction
+3
View File
@@ -465,6 +465,9 @@ public:
inline const T &operator()(int i, int j, int k) const;
inline T &operator()(int i, int j, int k);
inline void operator=(const T &a)
{ array1d = a; }
};
+10 -2
View File
@@ -207,6 +207,9 @@ void OptionsParser::Parse()
case STRING:
*(const char **)(options[j].var_ptr) = argv[i++];
break;
case STD_STRING:
*(std::string *)(options[j].var_ptr) = argv[i++];
break;
case ENABLE:
*(bool *)(options[j].var_ptr) = true;
option_check[j+1] = 1; // Do not allow the DISABLE Option
@@ -284,6 +287,10 @@ void OptionsParser::WriteValue(const Option &opt, std::ostream &os)
os << *(const char **)(opt.var_ptr);
break;
case STD_STRING:
out << *(std::string *)(opt.var_ptr);
break;
case ARRAY:
{
Array<int> &list = *(Array<int>*)(opt.var_ptr);
@@ -401,8 +408,9 @@ void OptionsParser::PrintHelp(ostream &os) const
static const char *seprtr = ", ";
static const char *descr_sep = "\n\t";
static const char *line_sep = "";
static const char *types[] = { " <int>", " <double>", " <string>", "", "",
" '<int>...'", " '<double>...'"
static const char *types[] = { " <int>", " <double>", " <string>",
" <string>", "", "", " '<int>...'",
" '<double>...'"
};
os << indent << "-h" << seprtr << "--help" << descr_sep
+10 -1
View File
@@ -31,7 +31,7 @@ class Vector;
class OptionsParser
{
public:
enum OptionType { INT, DOUBLE, STRING, ENABLE, DISABLE, ARRAY, VECTOR };
enum OptionType { INT, DOUBLE, STRING, STD_STRING, ENABLE, DISABLE, ARRAY, VECTOR };
private:
struct Option
@@ -115,6 +115,15 @@ public:
required));
}
/// Add a string (std::string) option and set 'var' to receive the value.
void AddOption(std::string *var, const char *short_name,
const char *long_name, const char *description,
bool required = false)
{
options.Append(Option(STD_STRING, var, short_name, long_name, description,
required));
}
/** Add an integer array (separated by spaces) option and set 'var' to
receive the values. */
void AddOption(Array<int> * var, const char *short_name,
+34 -2
View File
@@ -4173,6 +4173,28 @@ DenseMatrixSVD::DenseMatrixSVD(int h, int w,
Init();
}
DenseMatrixSVD::DenseMatrixSVD(DenseMatrix &M,
char left_singular_vectors,
char right_singular_vectors)
{
m = M.Height();
n = M.Width();
jobu = left_singular_vectors;
jobvt = right_singular_vectors;
Init();
}
DenseMatrixSVD::DenseMatrixSVD(int h, int w,
char left_singular_vectors,
char right_singular_vectors)
{
m = h;
n = w;
jobu = left_singular_vectors;
jobvt = right_singular_vectors;
Init();
}
void DenseMatrixSVD::Init()
{
sv.SetSize(min(m, n));
@@ -4195,12 +4217,22 @@ void DenseMatrixSVD::Eval(DenseMatrix &M)
#endif
double * datau = nullptr;
double * datavt = nullptr;
if (jobu == 'S')
if (jobu == 'A')
{
U.SetSize(m,m);
datau = U.Data();
}
else if (jobu == 'S')
{
U.SetSize(m,min(m,n));
datau = U.Data();
}
if (jobvt == 'S')
if (jobvt == 'A')
{
Vt.SetSize(n,n);
datavt = Vt.Data();
}
else if (jobvt == 'S')
{
Vt.SetSize(min(m,n),n);
datavt = Vt.Data();
+122 -4
View File
@@ -939,7 +939,12 @@ public:
~DenseMatrixGeneralizedEigensystem();
};
/**
@brief Class for Singular Value Decomposition of a DenseMatrix
Singular Value Decomposition (SVD) of a DenseMatrix with the use of the DGESVD
driver from LAPACK.
*/
class DenseMatrixSVD
{
DenseMatrix Mc;
@@ -955,16 +960,129 @@ class DenseMatrixSVD
void Init();
public:
/**
@brief Constructor for the DenseMatrixSVD
Constructor for the DenseMatrixSVD with LAPACK. The parameters for the left
and right singular vectors can be choosen according to the parameters for
the LAPACK DGESVD.
@param [in] M matrix to set the size to n=M.Height(), m=M.Width()
@param [in] left_singular_vectors optional parameter to define if first
left singular vectors should be computed
@param [in] right_singular_vectors optional parameter to define if first
right singular vectors should be computed
*/
MFEM_DEPRECATED DenseMatrixSVD(DenseMatrix &M,
bool left_singular_vectors=false,
bool right_singular_vectors=false);
/**
@brief Constructor for the DenseMatrixSVD
Constructor for the DenseMatrixSVD with LAPACK. The parameters for the left
and right singular
vectors can be choosen according to the parameters for the LAPACK DGESVD.
@param [in] h height of the matrix
@param [in] w width of the matrix
@param [in] left_singular_vectors optional parameter to define if first
left singular vectors should be computed
@param [in] right_singular_vectors optional parameter to define if first
right singular vectors should be computed
*/
MFEM_DEPRECATED DenseMatrixSVD(int h, int w,
bool left_singular_vectors=false,
bool right_singular_vectors=false);
/**
@brief Constructor for the DenseMatrixSVD
Constructor for the DenseMatrixSVD with LAPACK. The parameters for the left
and right singular vectors can be choosen according to the parameters for
the LAPACK DGESVD.
@param [in] M matrix to set the size to n=M.Height(), m=M.Width()
@param [in] left_singular_vectors optional parameter to define which left
singular vectors should be computed
@param [in] right_singular_vectors optional parameter to define which right
singular vectors should be computed
Options for computation of singular vectors:
'A': All singular vectors are computed (default)
'S': The first min(n,m) singular vectors are computed
'N': No singular vectors are computed
*/
DenseMatrixSVD(DenseMatrix &M,
bool left_singular_vectors=false,
bool right_singlular_vectors=false);
char left_singular_vectors='A',
char right_singular_vectors='A');
/**
@brief Constructor for the DenseMatrixSVD
Constructor for the DenseMatrixSVD with LAPACK. The parameters for the left
and right singular vectors can be choosen according to the
parameters for the LAPACK DGESVD.
@param [in] h height of the matrix
@param [in] w width of the matrix
@param [in] left_singular_vectors optional parameter to define which left
singular vectors should be computed
@param [in] right_singular_vectors optional parameter to define which right
singular vectors should be computed
Options for computation of singular vectors:
'A': All singular vectors are computed (default)
'S': The first min(n,m) singular vectors are computed
'N': No singular vectors are computed
*/
DenseMatrixSVD(int h, int w,
bool left_singular_vectors=false,
bool right_singlular_vectors=false);
char left_singular_vectors='A',
char right_singular_vectors='A');
/**
@brief Evaluate the SVD
Call of the DGESVD driver from LAPACK for the DenseMatrix M. The singular
vectors are computed according to the setup in the call of the constructor.
@param [in] M DenseMatrix the SVD should be evaluated for
*/
void Eval(DenseMatrix &M);
/**
@brief Return singular values
@return sv Vector containing all singular values
*/
Vector &Singularvalues() { return sv; }
/**
@brief Return specific singular value
@return sv(i) i-th singular value
*/
double Singularvalue(int i) { return sv(i); }
/**
@brief Return left singular vectors
@return U DenseMatrix containing left singular vectors
*/
DenseMatrix &LeftSingularvectors() { return U; }
/**
@brief Return right singular vectors
@return Vt DenseMatrix containing right singular vectors
*/
DenseMatrix &RightSingularvectors() { return Vt; }
~DenseMatrixSVD();
};
+1 -1
View File
@@ -5138,7 +5138,7 @@ void HypreBoomerAMG::SetAdvectiveOptions(int distanceR,
double filterA_tol = 0.0;
// Set relaxation on specified grid points
int ns_down, ns_up, ns_coarse;
int ns_down = 0, ns_up = 0, ns_coarse; // init to suppress gcc warnings
if (distanceR > 0)
{
ns_down = prerelax.length();
+829 -10
View File
@@ -989,7 +989,6 @@ void GMRESSolver::Mult(const Vector &b, Vector &x) const
Vector r(n), w(n);
Array<Vector *> v;
double resid;
int i, j, k;
if (iterative_mode)
@@ -1035,7 +1034,6 @@ void GMRESSolver::Mult(const Vector &b, Vector &x) const
final_iter = 0;
converged = true;
j = 0;
resid = beta;
goto finish;
}
@@ -1089,7 +1087,7 @@ void GMRESSolver::Mult(const Vector &b, Vector &x) const
ApplyPlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i));
ApplyPlaneRotation(s(i), s(i+1), cs(i), sn(i));
resid = fabs(s(i+1));
const double resid = fabs(s(i+1));
MFEM_ASSERT(IsFinite(resid), "resid = " << resid);
if (resid <= final_norm)
@@ -1148,7 +1146,7 @@ finish:
{
mfem::out << " Pass : " << setw(2) << (j-1)/m+1
<< " Iteration : " << setw(3) << final_iter
<< " ||B r|| = " << resid << '\n';
<< " ||B r|| = " << final_norm << '\n';
}
if (print_options.summary || (print_options.warnings && !converged))
{
@@ -1175,7 +1173,6 @@ void FGMRESSolver::Mult(const Vector &b, Vector &x) const
int i, j, k;
if (iterative_mode)
{
oper->Mult(x, r);
@@ -1187,9 +1184,6 @@ void FGMRESSolver::Mult(const Vector &b, Vector &x) const
r = b;
}
double beta = initial_norm = Norm(r); // beta = ||r||
// We need to preallocate this to report the correct result in the case of
// no convergence.
double resid;
MFEM_ASSERT(IsFinite(beta), "beta = " << beta);
final_norm = std::max(rel_tol*beta, abs_tol);
@@ -1264,7 +1258,7 @@ void FGMRESSolver::Mult(const Vector &b, Vector &x) const
ApplyPlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i));
ApplyPlaneRotation(s(i), s(i+1), cs(i), sn(i));
resid = fabs(s(i+1));
const double resid = fabs(s(i+1));
MFEM_ASSERT(IsFinite(resid), "resid = " << resid);
if (print_options.iterations || (print_options.first_and_last &&
resid <= final_norm))
@@ -1330,7 +1324,7 @@ void FGMRESSolver::Mult(const Vector &b, Vector &x) const
{
mfem::out << " Pass : " << setw(2) << (j-1)/m+1
<< " Iteration : " << setw(3) << j-1
<< " || r || = " << resid << endl;
<< " || r || = " << final_norm << endl;
}
if (print_options.summary || (print_options.warnings && !converged))
{
@@ -3544,4 +3538,829 @@ void AuxSpaceSmoother::Mult(const Vector &x, Vector &y, bool transpose) const
}
#endif // MFEM_USE_MPI
#ifdef MFEM_USE_LAPACK
// LAPACK routines for NNLSSolver
extern "C" void
dormqr_(char *, char *, int *, int *, int *, double *, int*, double *,
double *, int *, double *, int*, int*);
extern "C" void
dgeqrf_(int *, int *, double *, int *, double *, double *, int *, int *);
extern "C" void
dgemv_(char *, int *, int *, double *, double *, int *, double *, int *,
double *, double *, int *);
extern "C" void
dtrsm_(char *side, char *uplo, char *transa, char *diag, int *m, int *n,
double *alpha, double *a, int *lda, double *b, int *ldb);
NNLSSolver::NNLSSolver()
: Solver(0), mat(nullptr), const_tol_(1.0e-14), min_nnz_(0),
max_nnz_(0), verbosity_(0), res_change_termination_tol_(1.0e-4),
zero_tol_(1.0e-14), rhs_delta_(1.0e-11), n_outer_(100000),
n_inner_(100000), nStallCheck_(100), normalize_(true),
NNLS_qrres_on_(false), qr_residual_mode_(QRresidualMode::hybrid)
{}
void NNLSSolver::SetOperator(const Operator &op)
{
mat = dynamic_cast<const DenseMatrix*>(&op);
MFEM_VERIFY(mat, "NNLSSolver operator must be of type DenseMatrix");
// The size of this operator is that of the transpose of op.
height = op.Width();
width = op.Height();
row_scaling_.SetSize(mat->NumRows());
row_scaling_ = 1.0;
}
void NNLSSolver::SetQRResidualMode(const QRresidualMode qr_residual_mode)
{
qr_residual_mode_ = qr_residual_mode;
if (qr_residual_mode_ == QRresidualMode::on)
{
NNLS_qrres_on_ = true;
}
}
void NNLSSolver::NormalizeConstraints(Vector& rhs_lb, Vector& rhs_ub) const
{
// Scale everything so that rescaled half gap is the same for all constraints
const int m = mat->NumRows();
MFEM_VERIFY(rhs_lb.Size() == m && rhs_ub.Size() == m, "");
Vector rhs_avg = rhs_ub;
rhs_avg += rhs_lb;
rhs_avg *= 0.5;
Vector rhs_halfgap = rhs_ub;
rhs_halfgap -= rhs_lb;
rhs_halfgap *= 0.5;
Vector rhs_avg_glob = rhs_avg;
Vector rhs_halfgap_glob = rhs_halfgap;
Vector halfgap_target(m);
halfgap_target = 1.0e3 * const_tol_;
row_scaling_.SetSize(m);
for (int i=0; i<m; ++i)
{
const double s = halfgap_target(i) / rhs_halfgap_glob(i);
row_scaling_[i] = s;
rhs_lb(i) = (rhs_avg(i) * s) - halfgap_target(i);
rhs_ub(i) = (rhs_avg(i) * s) + halfgap_target(i);
}
}
void NNLSSolver::Mult(const Vector &w, Vector &sol) const
{
MFEM_VERIFY(mat, "NNLSSolver operator must be of type DenseMatrix");
Vector rhs_ub(mat->NumRows());
mat->Mult(w, rhs_ub);
rhs_ub *= row_scaling_;
Vector rhs_lb(rhs_ub);
Vector rhs_Gw(rhs_ub);
for (int i=0; i<rhs_ub.Size(); ++i)
{
rhs_lb(i) -= rhs_delta_;
rhs_ub(i) += rhs_delta_;
}
if (normalize_) { NormalizeConstraints(rhs_lb, rhs_ub); }
Solve(rhs_lb, rhs_ub, sol);
if (verbosity_ > 1)
{
int nnz = 0;
for (int i=0; i<sol.Size(); ++i)
{
if (sol(i) != 0.0)
{
nnz++;
}
}
mfem::out << "Number of nonzeros in NNLSSolver solution: " << nnz
<< ", out of " << sol.Size() << endl;
// Check residual of NNLS solution
Vector res(mat->NumRows());
mat->Mult(sol, res);
res *= row_scaling_;
const double normGsol = res.Norml2();
const double normRHS = rhs_Gw.Norml2();
res -= rhs_Gw;
const double relNorm = res.Norml2() / std::max(normGsol, normRHS);
mfem::out << "Relative residual norm for NNLSSolver solution of Gs = Gw: "
<< relNorm << endl;
}
}
void NNLSSolver::Solve(const Vector& rhs_lb, const Vector& rhs_ub,
Vector& soln) const
{
int m = mat->NumRows();
int n = mat->NumCols();
MFEM_VERIFY(rhs_lb.Size() == m && rhs_lb.Size() == m && soln.Size() == n, "");
MFEM_VERIFY(n >= m, "NNLSSolver system cannot be over-determined.");
if (max_nnz_ == 0)
{
max_nnz_ = mat->NumCols();
}
// Prepare right hand side
Vector rhs_avg(rhs_ub);
rhs_avg += rhs_lb;
rhs_avg *= 0.5;
Vector rhs_halfgap(rhs_ub);
rhs_halfgap -= rhs_lb;
rhs_halfgap *= 0.5;
Vector rhs_avg_glob(rhs_avg);
Vector rhs_halfgap_glob(rhs_halfgap);
int ione = 1;
double fone = 1.0;
char lside = 'L';
char trans = 'T';
char notrans = 'N';
std::vector<unsigned int> nz_ind(m);
Vector res_glob(m);
Vector mu(n);
Vector mu2(n);
int n_nz_ind = 0;
int n_glob = 0;
int m_update;
int min_nnz_cap = std::min(static_cast<int>(min_nnz_), std::min(m,n));
int info;
std::vector<double> l2_res_hist;
std::vector<unsigned int> stalled_indices;
int stalledFlag = 0;
int num_stalled = 0;
int nz_ind_zero = 0;
Vector soln_nz_glob(m);
Vector soln_nz_glob_up(m);
// The following matrices are stored in column-major format as Vectors
Vector mat_0_data(m * n);
Vector mat_qr_data(m * n);
Vector submat_data(m * n);
Vector tau(n);
Vector sub_tau = tau;
Vector vec1(m);
// Temporary work arrays
int lwork;
std::vector<double> work;
int n_outer_iter = 0;
int n_total_inner_iter = 0;
int i_qr_start;
int n_update;
// 0 = converged; 1 = maximum iterations reached;
// 2 = NNLS stalled (no change in residual for many iterations)
int exit_flag = 1;
res_glob = rhs_avg_glob;
Vector qt_rhs_glob = rhs_avg_glob;
Vector qqt_rhs_glob = qt_rhs_glob;
Vector sub_qt = rhs_avg_glob;
// Compute threshold tolerance for the Lagrange multiplier mu
double mu_tol = 0.0;
{
Vector rhs_scaled(rhs_halfgap_glob);
Vector tmp(n);
rhs_scaled *= row_scaling_;
mat->MultTranspose(rhs_scaled, tmp);
mu_tol = 1.0e-15 * tmp.Max();
}
double rmax = 0.0;
double mumax = 0.0;
for (int oiter = 0; oiter < n_outer_; ++oiter)
{
stalledFlag = 0;
rmax = fabs(res_glob(0)) - rhs_halfgap_glob(0);
for (int i=1; i<m; ++i)
{
rmax = std::max(rmax, fabs(res_glob(i)) - rhs_halfgap_glob(i));
}
l2_res_hist.push_back(res_glob.Norml2());
if (verbosity_ > 1)
{
mfem::out << "NNLS " << oiter << " " << n_total_inner_iter << " " << m
<< " " << n << " " << n_glob << " " << rmax << " "
<< l2_res_hist[oiter] << endl;
}
if (rmax <= const_tol_ && n_glob >= min_nnz_cap)
{
if (verbosity_ > 1)
{
mfem::out << "NNLS target tolerance met" << endl;
}
exit_flag = 0;
break;
}
if (n_glob >= max_nnz_)
{
if (verbosity_ > 1)
{
mfem::out << "NNLS target nnz met" << endl;
}
exit_flag = 0;
break;
}
if (n_glob >= m)
{
if (verbosity_ > 1)
{
mfem::out << "NNLS system is square... exiting" << endl;
}
exit_flag = 3;
break;
}
// Check for stall after the first nStallCheck iterations
if (oiter > nStallCheck_)
{
double mean0 = 0.0;
double mean1 = 0.0;
for (int i=0; i<nStallCheck_/2; ++i)
{
mean0 += l2_res_hist[oiter - i];
mean1 += l2_res_hist[oiter - (nStallCheck_) - i];
}
double mean_res_change = (mean1 / mean0) - 1.0;
if (std::abs(mean_res_change) < res_change_termination_tol_)
{
if (verbosity_ > 1)
{
mfem::out << "NNLSSolver stall detected... exiting" << endl;
}
exit_flag = 2;
break;
}
}
// Find the next index
res_glob *= row_scaling_;
mat->MultTranspose(res_glob, mu);
for (int i = 0; i < n_nz_ind; ++i)
{
mu(nz_ind[i]) = 0.0;
}
for (unsigned int i = 0; i < stalled_indices.size(); ++i)
{
mu(stalled_indices[i]) = 0.0;
}
mumax = mu.Max();
if (mumax < mu_tol)
{
num_stalled = stalled_indices.size();
if (num_stalled > 0)
{
if (verbosity_ > 0)
{
mfem::out << "NNLS Lagrange multiplier is below the minimum "
<< "threshold: mumax = " << mumax << ", mutol = "
<< mu_tol << "\n" << " Resetting stalled indices "
<< "vector of size " << num_stalled << "\n";
}
stalled_indices.resize(0);
mat->MultTranspose(res_glob, mu);
for (int i = 0; i < n_nz_ind; ++i)
{
mu(nz_ind[i]) = 0.0;
}
mumax = mu.Max();
}
}
int imax = 0;
{
double tmax = mu(0);
for (int i=1; i<n; ++i)
{
if (mu(i) > tmax)
{
tmax = mu(i);
imax = i;
}
}
}
// Record the local value of the next index
nz_ind[n_nz_ind] = imax;
++n_nz_ind;
if (verbosity_ > 2)
{
mfem::out << "Found next index: " << imax << " " << mumax << endl;
}
for (int i=0; i<m; ++i)
{
mat_0_data(i + (n_glob*m)) = (*mat)(i,imax) * row_scaling_[i];
mat_qr_data(i + (n_glob*m)) = mat_0_data(i + (n_glob*m));
}
i_qr_start = n_glob;
++n_glob; // Increment the size of the global matrix
if (verbosity_ > 2)
{
mfem::out << "Updated matrix with new index" << endl;
}
for (int iiter = 0; iiter < n_inner_; ++iiter)
{
++n_total_inner_iter;
// Initialize
const bool incremental_update = true;
n_update = n_glob - i_qr_start;
m_update = m - i_qr_start;
if (incremental_update)
{
// Apply Householder reflectors to compute Q^T new_cols
lwork = -1;
work.resize(10);
dormqr_(&lside, &trans, &m, &n_update, &i_qr_start,
mat_qr_data.GetData(), &m, tau.GetData(),
mat_qr_data.GetData() + (i_qr_start * m), &m,
work.data(), &lwork, &info);
MFEM_VERIFY(info == 0, ""); // Q^T A update work calculation failed
lwork = static_cast<int>(work[0]);
work.resize(lwork);
dormqr_(&lside, &trans, &m, &n_update, &i_qr_start,
mat_qr_data.GetData(), &m, tau.GetData(),
mat_qr_data.GetData() + (i_qr_start * m), &m,
work.data(), &lwork, &info);
MFEM_VERIFY(info == 0, ""); // Q^T A update failed
// Compute QR factorization of the submatrix
lwork = -1;
work.resize(10);
// Copy m_update-by-n_update submatrix of mat_qr_data,
// starting at (i_qr_start, i_qr_start)
for (int i=0; i<m_update; ++i)
for (int j=0; j<n_update; ++j)
{
submat_data[i + (j * m_update)] =
mat_qr_data[i + i_qr_start + ((j + i_qr_start) * m)];
}
// Copy tau subvector of length n_update, starting at i_qr_start
for (int j=0; j<n_update; ++j)
{
sub_tau[j] = tau[i_qr_start + j];
}
dgeqrf_(&m_update, &n_update,
submat_data.GetData(), &m_update, sub_tau.GetData(),
work.data(), &lwork, &info);
MFEM_VERIFY(info == 0, ""); // QR update factorization work calc
lwork = static_cast<int>(work[0]);
if (lwork == 0) { lwork = 1; }
work.resize(lwork);
dgeqrf_(&m_update, &n_update,
submat_data.GetData(), &m_update, sub_tau.GetData(),
work.data(), &lwork, &info);
MFEM_VERIFY(info == 0, ""); // QR update factorization failed
// Copy result back
for (int i=0; i<m_update; ++i)
for (int j=0; j<n_update; ++j)
{
mat_qr_data[i + i_qr_start + ((j + i_qr_start)* m)] =
submat_data[i + (j * m_update)];
}
for (int j=0; j<n_update; ++j)
{
tau[i_qr_start + j] = sub_tau[j];
}
}
else
{
// Copy everything to mat_qr then do full QR
for (int i=0; i<m; ++i)
for (int j=0; j<n_glob; ++j)
{
mat_qr_data(i + (j*m)) = mat_0_data(i + (j*m));
}
// Compute qr factorization (first find the size of work and then
// perform qr)
lwork = -1;
work.resize(10);
dgeqrf_(&m, &n_glob,
mat_qr_data.GetData(), &m, tau.GetData(),
work.data(), &lwork, &info);
MFEM_VERIFY(info == 0, ""); // QR factorization work calculation
lwork = static_cast<int>(work[0]);
work.resize(lwork);
dgeqrf_(&m, &n_glob,
mat_qr_data.GetData(), &m, tau.GetData(),
work.data(), &lwork, &info);
MFEM_VERIFY(info == 0, ""); // QR factorization failed
}
if (verbosity_ > 2)
{
mfem::out << "Updated QR " << iiter << endl;
}
// Apply Householder reflectors to compute Q^T b
if (incremental_update && iiter == 0)
{
lwork = -1;
work.resize(10);
// Copy submatrix of mat_qr_data starting at
// (i_qr_start, i_qr_start), of size m_update-by-1
// Copy submatrix of qt_rhs_glob starting at (i_qr_start, 0),
// of size m_update-by-1
for (int i=0; i<m_update; ++i)
{
submat_data[i] = mat_qr_data[i + i_qr_start + (i_qr_start * m)];
sub_qt[i] = qt_rhs_glob[i + i_qr_start];
}
sub_tau[0] = tau[i_qr_start];
dormqr_(&lside, &trans, &m_update, &ione, &ione,
submat_data.GetData(), &m_update, sub_tau.GetData(),
sub_qt.GetData(), &m_update,
work.data(), &lwork, &info);
MFEM_VERIFY(info == 0, ""); // H_last y work calculation failed
lwork = static_cast<int>(work[0]);
work.resize(lwork);
dormqr_(&lside, &trans, &m_update, &ione, &ione,
submat_data.GetData(), &m_update, sub_tau.GetData(),
sub_qt.GetData(), &m_update,
work.data(), &lwork, &info);
MFEM_VERIFY(info == 0, ""); // H_last y failed
// Copy result back
for (int i=0; i<m_update; ++i)
{
qt_rhs_glob[i + i_qr_start] = sub_qt[i];
}
}
else
{
// Compute Q^T b from scratch
qt_rhs_glob = rhs_avg_glob;
lwork = -1;
work.resize(10);
dormqr_(&lside, &trans, &m, &ione, &n_glob,
mat_qr_data.GetData(), &m, tau.GetData(),
qt_rhs_glob.GetData(), &m,
work.data(), &lwork, &info);
MFEM_VERIFY(info == 0, ""); // Q^T b work calculation failed
lwork = static_cast<int>(work[0]);
work.resize(lwork);
dormqr_(&lside, &trans, &m, &ione, &n_glob,
mat_qr_data.GetData(), &m, tau.GetData(),
qt_rhs_glob.GetData(), &m,
work.data(), &lwork, &info);
MFEM_VERIFY(info == 0, ""); // Q^T b failed
}
if (verbosity_ > 2)
{
mfem::out << "Updated rhs " << iiter << endl;
}
// Apply R^{-1}; first n_glob entries of vec1 are overwritten
char upper = 'U';
char nounit = 'N';
vec1 = qt_rhs_glob;
dtrsm_(&lside, &upper, &notrans, &nounit,
&n_glob, &ione, &fone,
mat_qr_data.GetData(), &m,
vec1.GetData(), &n_glob);
if (verbosity_ > 2)
{
mfem::out << "Solved triangular system " << iiter << endl;
}
// Check if all entries are positive
int pos_ibool = 0;
double smin = n_glob > 0 ? vec1(0) : 0.0;
for (int i=0; i<n_glob; ++i)
{
soln_nz_glob_up(i) = vec1(i);
smin = std::min(smin, soln_nz_glob_up(i));
}
if (smin > zero_tol_)
{
pos_ibool = 1;
for (int i=0; i<n_glob; ++i)
{
soln_nz_glob(i) = soln_nz_glob_up(i);
}
}
if (pos_ibool == 1)
{
break;
}
if (verbosity_ > 2)
{
mfem::out << "Start pruning " << iiter << endl;
for (int i = 0; i < n_glob; ++i)
{
if (soln_nz_glob_up(i) <= zero_tol_)
{
mfem::out << i << " " << n_glob << " " << soln_nz_glob_up(i) << endl;
}
}
}
if (soln_nz_glob_up(n_glob - 1) <= zero_tol_)
{
stalledFlag = 1;
if (verbosity_ > 2)
{
if (qr_residual_mode_ == QRresidualMode::hybrid)
{
mfem::out << "Detected stall due to adding and removing same "
<< "column. Switching to QR residual calculation "
<< "method." << endl;
}
else
{
mfem::out << "Detected stall due to adding and removing same"
<< " column. Exiting now." << endl;
}
}
}
if (stalledFlag == 1 && qr_residual_mode_ == QRresidualMode::hybrid)
{
NNLS_qrres_on_ = true;
break;
}
double alpha = 1.0e300;
// Find maximum permissible step
for (int i = 0; i < n_glob; ++i)
{
if (soln_nz_glob_up(i) <= zero_tol_)
{
alpha = std::min(alpha, soln_nz_glob(i)/(soln_nz_glob(i) - soln_nz_glob_up(i)));
}
}
// Update solution
smin = 0.0;
for (int i = 0; i < n_glob; ++i)
{
soln_nz_glob(i) += alpha*(soln_nz_glob_up(i) - soln_nz_glob(i));
if (i == 0 || soln_nz_glob(i) < smin)
{
smin = soln_nz_glob(i);
}
}
while (smin > zero_tol_)
{
// This means there was a rounding error, as we should have
// a zero element by definition. Recalculate alpha based on
// the index that corresponds to the element that should be
// zero.
int index_min = 0;
smin = soln_nz_glob(0);
for (int i = 1; i < n_glob; ++i)
{
if (soln_nz_glob(i) < smin)
{
smin = soln_nz_glob(i);
index_min = i;
}
}
alpha = soln_nz_glob(index_min)/(soln_nz_glob(index_min)
- soln_nz_glob_up(index_min));
// Reupdate solution
for (int i = 0; i < n_glob; ++i)
{
soln_nz_glob(i) += alpha*(soln_nz_glob_up(i) - soln_nz_glob(i));
}
}
// Clean up zeroed entry
i_qr_start = n_glob+1;
while (true)
{
// Check if there is a zero entry
int zero_ibool;
smin = n_glob > 0 ? soln_nz_glob(0) : 0.0;
for (int i=1; i<n_glob; ++i)
{
smin = std::min(smin, soln_nz_glob(i));
}
if (smin < zero_tol_)
{
zero_ibool = 1;
}
else
{
zero_ibool = 0;
}
if (zero_ibool == 0) // Break if there is no more zero entry
{
break;
}
int ind_zero = -1; // Index where the first zero is encountered
nz_ind_zero = 0;
// Identify global index of the zeroed element
for (int i = 0; i < n_glob; ++i)
{
if (soln_nz_glob(i) < zero_tol_)
{
ind_zero = i;
break;
}
}
MFEM_VERIFY(ind_zero != -1, "");
// Identify the local index for nz_ind to which the zeroed entry
// belongs
for (int i = 0; i < ind_zero; ++i)
{
++nz_ind_zero;
}
{
// Copy mat_0.cols[ind_zero+1,n_glob) to mat_qr.cols[ind_zero,n_glob-1)
for (int i=0; i<m; ++i)
for (int j=ind_zero; j<n_glob-1; ++j)
{
mat_qr_data(i + (j*m)) = mat_0_data(i + ((j+1)*m));
}
// Copy mat_qr.cols[ind_zero,n_glob-1) to
// mat_0.cols[ind_zero,n_glob-1)
for (int i=0; i<m; ++i)
for (int j=ind_zero; j<n_glob-1; ++j)
{
mat_0_data(i + (j*m)) = mat_qr_data(i + (j*m));
}
}
// Remove the zeroed entry from the local matrix index
for (int i = nz_ind_zero; i < n_nz_ind-1; ++i)
{
nz_ind[i] = nz_ind[i+1];
}
--n_nz_ind;
// Shift soln_nz_glob and proc_index
for (int i = ind_zero; i < n_glob-1; ++i)
{
soln_nz_glob(i) = soln_nz_glob(i+1);
}
i_qr_start = std::min(i_qr_start, ind_zero);
--n_glob;
} // End of pruning loop
if (verbosity_ > 2)
{
mfem::out << "Finished pruning " << iiter << endl;
}
} // End of inner loop
// Check if we have stalled
if (stalledFlag == 1)
{
--n_glob;
--n_nz_ind;
num_stalled = stalled_indices.size();
stalled_indices.resize(num_stalled + 1);
stalled_indices[num_stalled] = imax;
if (verbosity_ > 2)
{
mfem::out << "Adding index " << imax << " to stalled index list "
<< "of size " << num_stalled << endl;
}
}
// Compute residual
if (!NNLS_qrres_on_)
{
res_glob = rhs_avg_glob;
double fmone = -1.0;
dgemv_(&notrans, &m, &n_glob, &fmone,
mat_0_data.GetData(), &m,
soln_nz_glob.GetData(), &ione, &fone,
res_glob.GetData(), &ione);
}
else
{
// Compute residual using res = b - Q*Q^T*b, where Q is from an
// economical QR decomposition
lwork = -1;
work.resize(10);
qqt_rhs_glob = 0.0;
for (int i=0; i<n_glob; ++i)
{
qqt_rhs_glob(i) = qt_rhs_glob(i);
}
dormqr_(&lside, &notrans, &m, &ione, &n_glob, mat_qr_data.GetData(), &m,
tau.GetData(), qqt_rhs_glob.GetData(), &m,
work.data(), &lwork, &info);
MFEM_VERIFY(info == 0, ""); // Q Q^T b work calculation failed.
lwork = static_cast<int>(work[0]);
work.resize(lwork);
dormqr_(&lside, &notrans, &m, &ione, &n_glob, mat_qr_data.GetData(), &m,
tau.GetData(), qqt_rhs_glob.GetData(), &m,
work.data(), &lwork, &info);
MFEM_VERIFY(info == 0, ""); // Q Q^T b calculation failed.
res_glob = rhs_avg_glob;
res_glob -= qqt_rhs_glob;
}
if (verbosity_ > 2)
{
mfem::out << "Computed residual" << endl;
}
++n_outer_iter;
} // End of outer loop
// Insert the solutions
MFEM_VERIFY(n_glob == n_nz_ind, "");
soln = 0.0;
for (int i = 0; i < n_glob; ++i)
{
soln(nz_ind[i]) = soln_nz_glob(i);
}
if (verbosity_ > 0)
{
mfem::out << "NNLS solver: m = " << m << ", n = " << n
<< ", outer_iter = " << n_outer_iter << ", inner_iter = "
<< n_total_inner_iter;
if (exit_flag == 0)
{
mfem::out << ": converged" << endl;
}
else
{
mfem::out << endl << "Warning, NNLS convergence stalled: "
<< (exit_flag == 2) << endl;
mfem::out << "resErr = " << rmax << " vs tol = " << const_tol_
<< "; mumax = " << mumax << " vs tol = " << mu_tol << endl;
}
}
}
#endif // MFEM_USE_LAPACK
}
+121 -1
View File
@@ -432,7 +432,7 @@ public:
~OperatorChebyshevSmoother() {}
void Mult(const Vector&x, Vector &y) const;
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const { Mult(x, y); }
@@ -1245,6 +1245,126 @@ public:
};
#endif // MFEM_USE_MPI
#ifdef MFEM_USE_LAPACK
/** Non-negative least squares (NNLS) solver class, for computing a vector
with non-negative entries approximately satisfying an under-determined
linear system. */
class NNLSSolver : public Solver
{
public:
NNLSSolver();
~NNLSSolver() { }
/// The operator must be a DenseMatrix.
void SetOperator(const Operator &op) override;
void Mult(const Vector &w, Vector &sol) const override;
/**
* Set verbosity. If set to 0: print nothing; if 1: just print results;
* if 2: print short update on every iteration; if 3: print longer update
* each iteration.
*/
void SetVerbosity(int v) { verbosity_ = v; }
void SetTolerance(double tol) { const_tol_ = tol; }
/// Set the minimum number of nonzeros required for the solution.
void SetMinNNZ(int min_nnz) { min_nnz_ = min_nnz; }
/// Set the maximum number of nonzeros required for the solution, as an early
/// termination condition.
void SetMaxNNZ(int max_nnz) { max_nnz_ = max_nnz; }
/// Set threshold on relative change in residual over nStallCheck_ iterations.
void SetResidualChangeTolerance(double tol)
{ res_change_termination_tol_ = tol; }
void SetZeroTolerance(double tol) { zero_tol_ = tol; }
/// Set RHS vector constant shift, defining rhs_lb and rhs_ub in Solve().
void SetRHSDelta(double d) { rhs_delta_ = d; }
/// Set the maximum number of outer iterations in Solve().
void SetOuterIterations(int n) { n_outer_ = n; }
/// Set the maximum number of inner iterations in Solve().
void SetInnerIterations(int n) { n_inner_ = n; }
/// Set the number of iterations to use for stall checking.
void SetStallCheck(int n) { nStallCheck_ = n; }
/// Set a flag to determine whether to call NormalizeConstraints().
void SetNormalize(bool n) { normalize_ = n; }
/**
* Enumerated types of QRresidual mode. Options are 'off': the residual is
* calculated normally, 'on': the residual is calculated using the QR
* method, 'hybrid': the residual is calculated normally until we experience
* rounding errors, then the QR method is used. The default is 'hybrid',
* which should see the best performance. Recommend using 'hybrid' or 'off'
* only, since 'on' is computationally expensive.
*/
enum class QRresidualMode {off, on, hybrid};
/**
* Set the residual calculation mode for the NNLS solver. See QRresidualMode
* enum above for details.
*/
void SetQRResidualMode(const QRresidualMode qr_residual_mode);
/**
* @brief Solve the NNLS problem. Specifically, we find a vector @a soln,
* such that rhs_lb < mat*soln < rhs_ub is satisfied, where mat is the
* DenseMatrix input to SetOperator().
*
* The method by which we find the solution is the active-set method
* developed by Lawson and Hanson (1974) using lapack. To decrease rounding
* errors in the case of very tight tolerances, we have the option to compute
* the residual using the QR factorization of A, by res = b - Q*Q^T*b. This
* residual calculation results in less rounding error, but is more
* computationally expensive. To select whether to use the QR residual method
* or not, see set_qrresidual_mode above.
*/
void Solve(const Vector& rhs_lb, const Vector& rhs_ub, Vector& soln) const;
/**
* Normalize the constraints such that the tolerances for each constraint
* (i.e. (UB - LB)/2) are equal. This seems to help the performance in most
* cases.
*/
void NormalizeConstraints(Vector& rhs_lb, Vector& rhs_ub) const;
private:
const DenseMatrix *mat;
double const_tol_;
int min_nnz_; // minimum number of nonzero entries
mutable int max_nnz_; // maximum number of nonzero entries
int verbosity_;
/**
* @brief Threshold on relative change in residual over nStallCheck_
* iterations, for stall sensing.
*/
double res_change_termination_tol_;
double zero_tol_;
double rhs_delta_;
int n_outer_;
int n_inner_;
int nStallCheck_;
bool normalize_;
mutable bool NNLS_qrres_on_;
QRresidualMode qr_residual_mode_;
mutable Vector row_scaling_;
};
#endif // MFEM_USE_LAPACK
}
#endif // MFEM_SOLVERS
+2 -2
View File
@@ -1345,7 +1345,7 @@ void SparseMatrix::Finalize(int skip_zeros, bool fix_empty_rows)
if ((i-1) != aux->Column) { continue; }
bool found = false;
double found_val;
double found_val = 0.0; // init to suppress gcc warning
for (RowNode *other = Rows[aux->Column]; other != NULL; other = other->Prev)
{
if (other->Column == (i-1))
@@ -1381,7 +1381,7 @@ void SparseMatrix::Finalize(int skip_zeros, bool fix_empty_rows)
if (i != aux->Column) { continue; }
bool found = false;
double found_val;
double found_val = 0.0; // init to suppress gcc warning
for (RowNode *other = Rows[aux->Column]; other != NULL; other = other->Prev)
{
if (other->Column == i)
+1 -1
View File
@@ -125,7 +125,7 @@ EXAMPLE_TEST_DIRS := examples
MINIAPP_SUBDIRS = common electromagnetics meshing navier performance tools \
toys nurbs gslib adjoint solvers shifted mtop parelag autodiff hooke \
multidomain dpg hdiv-linear-solver spde contact
multidomain dpg hdiv-linear-solver spde
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics meshing tools \
+248 -12
View File
@@ -1444,7 +1444,7 @@ Element::Type Mesh::GetFaceElementType(int Face) const
Array<int> Mesh::GetFaceToBdrElMap() const
{
Array<int> face_to_be(NumOfFaces);
Array<int> face_to_be(GetNumFaces());
face_to_be = -1;
for (int i = 0; i < NumOfBdrElements; i++)
{
@@ -3752,8 +3752,8 @@ Mesh& Mesh::operator=(Mesh &&mesh)
return *this;
}
Mesh Mesh::LoadFromFile(const char *filename, int generate_edges, int refine,
bool fix_orientation)
Mesh Mesh::LoadFromFile(const std::string &filename, int generate_edges,
int refine, bool fix_orientation)
{
Mesh mesh;
named_ifgzstream imesh(filename);
@@ -3807,7 +3807,7 @@ Mesh Mesh::MakeRefined(Mesh &orig_mesh, const Array<int> &ref_factors,
return mesh;
}
Mesh::Mesh(const char *filename, int generate_edges, int refine,
Mesh::Mesh(const std::string &filename, int generate_edges, int refine,
bool fix_orientation)
{
// Initialization as in the default constructor
@@ -5107,6 +5107,43 @@ std::vector<int> Mesh::CreatePeriodicVertexMapping(
return v2v;
}
void Mesh::RefineNURBSFromFile(std::string ref_file)
{
MFEM_VERIFY(NURBSext,"Mesh::RefineNURBSFromFile: Not a NURBS mesh!");
mfem::out<<"Refining NURBS from refinement file: "<<ref_file<<endl;
int nkv;
ifstream input(ref_file);
input >> nkv;
// Check if the number of knot vectors in the refinement file and mesh match
if ( nkv != NURBSext->GetNKV())
{
mfem::out<<endl;
mfem::out<<"Knot vectors in ref_file: "<<nkv<<endl;
mfem::out<<"Knot vectors in NURBSExt: "<<NURBSext->GetNKV()<<endl;
MFEM_ABORT("Refine file does not have the correct number of knot vectors");
}
// Read knot vectors from file
Array<Vector *> knotVec(nkv);
for (int kv = 0; kv < nkv; kv++)
{
knotVec[kv] = new Vector();
knotVec[kv]-> Load(input);
}
input.close();
// Insert knots
KnotInsert(knotVec);
// Delete knots
for (int kv = 0; kv < nkv; kv++)
{
delete knotVec[kv];
}
}
void Mesh::KnotInsert(Array<KnotVector *> &kv)
{
if (NURBSext == NULL)
@@ -5279,17 +5316,24 @@ void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_knot)
input >> ident; // 'edges'
input >> NumOfEdges;
edge_vertex = new Table(NumOfEdges, 2);
edge_to_knot.SetSize(NumOfEdges);
for (int j = 0; j < NumOfEdges; j++)
if (NumOfEdges > 0)
{
int *v = edge_vertex->GetRow(j);
input >> edge_to_knot[j] >> v[0] >> v[1];
if (v[0] > v[1])
edge_vertex = new Table(NumOfEdges, 2);
edge_to_knot.SetSize(NumOfEdges);
for (int j = 0; j < NumOfEdges; j++)
{
edge_to_knot[j] = -1 - edge_to_knot[j];
int *v = edge_vertex->GetRow(j);
input >> edge_to_knot[j] >> v[0] >> v[1];
if (v[0] > v[1])
{
edge_to_knot[j] = -1 - edge_to_knot[j];
}
}
}
else
{
edge_to_knot.SetSize(0);
}
skip_comment_lines(input, '#');
@@ -5299,6 +5343,198 @@ void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_knot)
FinalizeTopology();
CheckBdrElementOrientation(); // check and fix boundary element orientation
/* Generate knot 2 edge mapping -- if edges are not specified in the mesh file
See data/two-squares-nurbs-autoedge.mesh for an example */
if (edge_to_knot.Size() == 0)
{
edge_vertex = new Table(NumOfEdges, 2);
edge_to_knot.SetSize(NumOfEdges);
constexpr int notset = -9999999;
edge_to_knot = notset;
Array<int> edges;
Array<int> oedge;
int knot = 0;
Array<int> edge0, edge1;
int flip = 1;
if (Dimension() == 2 )
{
edge0.SetSize(2);
edge1.SetSize(2);
edge0[0] = 0; edge1[0] = 2;
edge0[1] = 1; edge1[1] = 3;
flip = 1;
}
else if (Dimension() == 3 )
{
edge0.SetSize(9);
edge1.SetSize(9);
edge0[0] = 0; edge1[0] = 2;
edge0[1] = 0; edge1[1] = 4;
edge0[2] = 0; edge1[2] = 6;
edge0[3] = 1; edge1[3] = 3;
edge0[4] = 1; edge1[4] = 5;
edge0[5] = 1; edge1[5] = 7;
edge0[6] = 8; edge1[6] = 9;
edge0[7] = 8; edge1[7] = 10;
edge0[8] = 8; edge1[8] = 11;
flip = -1;
}
/* Initial assignment of knots to edges. This is an algorithm that loops over the
patches and assigns knot vectors to edges. It starts with assigning knot vector 0
and 1 to the edges of the first patch. Then it uses: 1) patches can share edges
2) knot vectors on opposing edges in a patch are equal, to create edge_to_knot */
int e0, e1, v0, v1, df;
int p,j,k;
for (p = 0; p < GetNE(); p++)
{
GetElementEdges(p, edges, oedge);
const int *v = elements[p]->GetVertices();
for (j = 0; j < edges.Size(); j++)
{
int *vv = edge_vertex->GetRow(edges[j]);
const int *e = elements[p]->GetEdgeVertices(j);
if (oedge[j] == 1)
{
vv[0] = v[e[0]];
vv[1] = v[e[1]];
}
else
{
vv[0] = v[e[1]];
vv[1] = v[e[0]];
}
}
for (j = 0; j < edge1.Size(); j++)
{
e0 = edges[edge0[j]];
e1 = edges[edge1[j]];
v0 = edge_to_knot[e0];
v1 = edge_to_knot[e1];
df = flip*oedge[edge0[j]]*oedge[edge1[j]];
// Case 1: knot vector is not set
if ((v0 == notset) && (v1 == notset))
{
edge_to_knot[e0] = knot;
edge_to_knot[e1] = knot;
knot++;
}
// Case 2 & 3: knot vector on one of the two edges
// is set earlier (in another patch). We just have
// to copy it for the opposing edge.
else if ((v0 != notset) && (v1 == notset))
{
edge_to_knot[e1] = (df >= 0 ? -v0-1 : v0);
}
else if ((v0 == notset) && (v1 != notset))
{
edge_to_knot[e0] = (df >= 0 ? -v1-1 : v1);
}
}
}
/* Verify correct assignment, make sure that corresponding edges
within patch point to same knot vector. If not assign the lowest number.
We bound the while by GetNE() + 1 as this is probably the most unlucky
case. +1 to finish without corrections. Note that this is a check and
in general the initial assignment is correct. Then the while is performed
only once. Only on very tricky meshes it might need corrections.*/
int corrections;
int passes = 0;
do
{
corrections = 0;
for (p = 0; p < GetNE(); p++)
{
GetElementEdges(p, edges, oedge);
for (j = 0; j < edge1.Size(); j++)
{
e0 = edges[edge0[j]];
e1 = edges[edge1[j]];
v0 = edge_to_knot[e0];
v1 = edge_to_knot[e1];
v0 = ( v0 >= 0 ? v0 : -v0-1);
v1 = ( v1 >= 0 ? v1 : -v1-1);
if (v0 != v1)
{
corrections++;
if (v0 < v1)
{
edge_to_knot[e1] = (oedge[edge1[j]] >= 0 ? v0 : -v0-1);
}
else if (v1 < v0)
{
edge_to_knot[e0] = (oedge[edge0[j]] >= 0 ? v1 : -v1-1);
}
}
}
}
passes++;
}
while (corrections > 0 && passes < GetNE() + 1);
// Check the validity of corrections applied
if (corrections > 0 )
{
mfem::err<<"Edge_to_knot mapping potentially incorrect"<<endl;
mfem::err<<" passes = "<<passes<<endl;
mfem::err<<" corrections = "<<corrections<<endl;
}
/* Renumber knotvectors, such that:
-- numbering is consecutive
-- starts at zero */
Array<int> cnt(NumOfEdges);
cnt = 0;
for (j = 0; j < NumOfEdges; j++)
{
k = edge_to_knot[j];
cnt[(k >= 0 ? k : -k-1)]++;
}
k = 0;
for (j = 0; j < cnt.Size(); j++)
{
cnt[j] = (cnt[j] > 0 ? k++ : -1);
}
for (j = 0; j < NumOfEdges; j++)
{
k = edge_to_knot[j];
edge_to_knot[j] = (k >= 0 ? cnt[k]:-cnt[-k-1]-1);
}
// Print knot to edge mapping
mfem::out<<"Generated edge to knot mapping:"<<endl;
for (j = 0; j < NumOfEdges; j++)
{
int *v = edge_vertex->GetRow(j);
k = edge_to_knot[j];
v0 = v[0];
v1 = v[1];
if (k < 0)
{
v[0] = v1;
v[1] = v0;
}
mfem::out<<(k >= 0 ? k:-k-1)<<" "<< v[0] <<" "<<v[1]<<endl;
}
// Terminate here upon failure after printing to have an idea of edge_to_knot.
if (corrections > 0 ) {mfem_error("Mesh::LoadPatchTopo");}
}
}
void XYZ_VectorFunction(const Vector &p, Vector &v)
@@ -10466,7 +10702,7 @@ void Mesh::PrintTopo(std::ostream &os,const Array<int> &e_to_k) const
os << "\nvertices\n" << NumOfVertices << '\n';
}
void Mesh::Save(const char *fname, int precision) const
void Mesh::Save(const std::string &fname, int precision) const
{
ofstream ofs(fname);
ofs.precision(precision);
+13 -4
View File
@@ -643,8 +643,8 @@ public:
/** Creates mesh by reading a file in MFEM, Netgen, or VTK format. If
generate_edges = 0 (default) edges are not generated, if 1 edges are
generated. See also @a Mesh::LoadFromFile. */
explicit Mesh(const char *filename, int generate_edges = 0, int refine = 1,
bool fix_orientation = true);
explicit Mesh(const std::string &filename, int generate_edges = 0,
int refine = 1, bool fix_orientation = true);
/** Creates mesh by reading data stream in MFEM, Netgen, or VTK format. If
generate_edges = 0 (default) edges are not generated, if 1 edges are
@@ -697,7 +697,7 @@ public:
@note @a filename is not cached by the Mesh object and can be
safely deleted following this function call.
*/
static Mesh LoadFromFile(const char *filename,
static Mesh LoadFromFile(const std::string &filename,
int generate_edges = 0, int refine = 1,
bool fix_orientation = true);
@@ -1984,6 +1984,15 @@ public:
/// @}
///@{ @name NURBS mesh refinement methods
/** Refine a NURBS mesh with the knots specified in the file named @a ref_file.
The file has the number of knot vectors on the first line. It is the same
number of knot vectors specified in the NURBS mesh in the section edges. Then
for each knot vector specified in the section edges (with the same ordering),
a line describes (in this order): 1) an integer giving the number of knots
inserted, 2) the knots inserted as a double. The advantage of this method
is that it is possible to specifically refine a coarse NURBS mesh without
changing the mesh file itself. Examples in miniapps/nurbs/meshes. */
void RefineNURBSFromFile(std::string ref_file);
void KnotInsert(Array<KnotVector *> &kv);
void KnotInsert(Array<Vector *> &kv);
/* For each knot vector:
@@ -2003,7 +2012,7 @@ public:
/// Save the mesh to a file using Mesh::Print. The given @a precision will be
/// used for ASCII output.
virtual void Save(const char *fname, int precision=16) const;
virtual void Save(const std::string &fname, int precision=16) const;
/// Print the mesh to the given stream using the adios2 bp format
#ifdef MFEM_USE_ADIOS2
+23 -23
View File
@@ -2176,9 +2176,9 @@ void NCMesh::UpdateVertices()
// - ghost (non-local) vertices (code -3)
// - vertices beyond the ghost layer (code -4)
for (auto node = nodes.begin(); node != nodes.end(); ++node)
for (auto & node : nodes)
{
node->vert_index = -4; // assume beyond ghost layer
node.vert_index = -4; // assume beyond ghost layer
}
for (int i = 0; i < leaf_elements.Size(); i++)
@@ -2208,11 +2208,11 @@ void NCMesh::UpdateVertices()
// STEP 2: assign indices of top-level local vertices, in original order
NVertices = 0;
for (auto node = nodes.begin(); node != nodes.end(); ++node)
for (auto &node : nodes)
{
if (node->vert_index == -1)
if (node.vert_index == -1)
{
node->vert_index = NVertices++;
node.vert_index = NVertices++;
}
}
@@ -2308,20 +2308,20 @@ void NCMesh::UpdateVertices()
}
vertex_nodeId.SetSize(NVertices);
for (auto node = nodes.begin(); node != nodes.end(); ++node)
for (auto &node : nodes)
{
if (node->HasVertex() && node->vert_index >= 0)
if (node.HasVertex() && node.vert_index >= 0)
{
vertex_nodeId[node->vert_index] = node.index();
vertex_nodeId[node.vert_index] = node.index();
}
}
NGhostVertices = 0;
for (auto node = nodes.begin(); node != nodes.end(); ++node)
for (auto &node : nodes)
{
if (node->HasVertex() && node->vert_index < 0)
if (node.HasVertex() && node.vert_index < 0)
{
node->vert_index = NVertices + (NGhostVertices++);
node.vert_index = NVertices + (NGhostVertices++);
}
}
}
@@ -2545,13 +2545,13 @@ void NCMesh::OnMeshUpdated(Mesh *mesh)
NFaces = mesh->GetNumFaces();
if (Dim < 2) { NFaces = 0; }
// clear Node::edge_index and Face::index
for (auto node = nodes.begin(); node != nodes.end(); ++node)
for (auto &node : nodes)
{
if (node->HasEdge()) { node->edge_index = -1; }
if (node.HasEdge()) { node.edge_index = -1; }
}
for (auto face = faces.begin(); face != faces.end(); ++face)
for (auto &face : faces)
{
face->index = -1;
face.index = -1;
}
// get edge enumeration from the Mesh
@@ -2615,19 +2615,19 @@ void NCMesh::OnMeshUpdated(Mesh *mesh)
// count ghost edges and assign their indices
NGhostEdges = 0;
for (auto node = nodes.begin(); node != nodes.end(); ++node)
for (auto &node : nodes)
{
if (node->HasEdge() && node->edge_index < 0)
if (node.HasEdge() && node.edge_index < 0)
{
node->edge_index = NEdges + (NGhostEdges++);
node.edge_index = NEdges + (NGhostEdges++);
}
}
// count ghost faces
NGhostFaces = 0;
for (auto face = faces.begin(); face != faces.end(); ++face)
for (auto &face : faces)
{
if (face->index < 0) { NGhostFaces++; }
if (face.index < 0) { NGhostFaces++; }
}
if (Dim == 2)
@@ -2671,9 +2671,9 @@ void NCMesh::OnMeshUpdated(Mesh *mesh)
}
// assign valid indices also to faces beyond the ghost layer
for (auto face = faces.begin(); face != faces.end(); ++face)
for (auto &face : faces)
{
if (face->index < 0) { face->index = NFaces + (nghosts++); }
if (face.index < 0) { face.index = NFaces + (nghosts++); }
}
MFEM_ASSERT(nghosts == NGhostFaces, "");
}
@@ -3452,7 +3452,7 @@ const NCMesh::MeshId& NCMesh::NCList::LookUp(int index, int *type) const
if (!type)
{
MFEM_VERIFY(key >= 0, "entity not found.");
MFEM_VERIFY(key >= 0, "index " << index << " not found.");
}
else // return entity type if requested, don't abort when not found
{
+408 -36
View File
@@ -1522,6 +1522,7 @@ NURBSExtension::NURBSExtension(const NURBSExtension &orig)
own_topo(true),
edge_to_knot(orig.edge_to_knot),
knotVectors(orig.knotVectors.Size()), // knotVectors are copied in the body
knotVectorsCompr(orig.knotVectorsCompr.Size()),
weights(orig.weights),
d_to_d(orig.d_to_d),
master(orig.master),
@@ -1547,6 +1548,7 @@ NURBSExtension::NURBSExtension(const NURBSExtension &orig)
{
knotVectors[i] = new KnotVector(*orig.knotVectors[i]);
}
CreateComprehensiveKV();
// Copy the patches:
for (int p = 0; p < patches.Size(); p++)
@@ -1649,6 +1651,8 @@ NURBSExtension::NURBSExtension(std::istream &input)
MFEM_ABORT("invalid section: " << ident);
}
CreateComprehensiveKV();
SetOrdersFromKnotVectors();
GenerateOffsets();
@@ -1728,6 +1732,7 @@ NURBSExtension::NURBSExtension(NURBSExtension *parent, int newOrder)
NumOfKnotVectors = parent->GetNKV();
knotVectors.SetSize(NumOfKnotVectors);
knotVectorsCompr.SetSize(parent->GetNP()*parent->Dimension());
const Array<int> &pOrders = parent->GetOrders();
for (int i = 0; i < NumOfKnotVectors; i++)
{
@@ -1741,6 +1746,7 @@ NURBSExtension::NURBSExtension(NURBSExtension *parent, int newOrder)
knotVectors[i] = new KnotVector(*parent->GetKnotVector(i));
}
}
CreateComprehensiveKV();
// copy some data from parent
NumOfElements = parent->NumOfElements;
@@ -1798,6 +1804,7 @@ NURBSExtension::NURBSExtension(NURBSExtension *parent,
knotVectors[i] = new KnotVector(*parent->GetKnotVector(i));
}
}
CreateComprehensiveKV();
// copy some data from parent
NumOfElements = parent->NumOfElements;
@@ -1848,6 +1855,7 @@ NURBSExtension::NURBSExtension(Mesh *mesh_array[], int num_pieces)
{
knotVectors[i] = new KnotVector(*parent->GetKnotVector(i));
}
CreateComprehensiveKV();
GenerateOffsets();
CountElements();
@@ -1881,6 +1889,11 @@ NURBSExtension::~NURBSExtension()
delete knotVectors[i];
}
for (int i = 0; i < knotVectorsCompr.Size(); i++)
{
delete knotVectorsCompr[i];
}
for (int i = 0; i < patches.Size(); i++)
{
delete patches[i];
@@ -2360,17 +2373,6 @@ void NURBSExtension::CheckPatches()
<< ")\n Inconsistent edge-to-knot mapping!\n";
mfem_error();
}
if ((Dimension() == 2 &&
(edges[0] < 0 || edges[1] < 0)) ||
(Dimension() == 3 &&
(edges[0] < 0 || edges[3] < 0 || edges[8] < 0)))
{
mfem::err << "NURBSExtension::CheckPatch (patch = " << p
<< ") : Bad orientation!\n";
mfem_error();
}
}
}
@@ -2402,6 +2404,269 @@ void NURBSExtension::CheckBdrPatches()
}
}
void NURBSExtension::CheckKVDirection(int p, Array <int> &kvdir)
{
// patchTopo->GetElementEdges is not yet implemented for 1D
MFEM_VERIFY(Dimension()>1, "1D not yet implemented.");
kvdir.SetSize(Dimension());
kvdir = 0;
Array<int> patchvert, edges, orient, edgevert;
patchTopo->GetElementVertices(p, patchvert);
patchTopo->GetElementEdges(p, edges, orient);
// Compare the vertices of the patches with the vertices of the knotvectors of knot2dge
// Based on the match the orientation will be a 1 or a -1
// -1: direction is flipped
// 1: direction is not flipped
for (int i = 0; i < edges.Size(); i++)
{
// First side
patchTopo->GetEdgeVertices(edges[i], edgevert);
if (edgevert[0] == patchvert[0] && edgevert[1] == patchvert[1])
{
kvdir[0] = 1;
}
if (edgevert[0] == patchvert[1] && edgevert[1] == patchvert[0])
{
kvdir[0] = -1;
}
// Second side
if (edgevert[0] == patchvert[1] && edgevert[1] == patchvert[2])
{
kvdir[1] = 1;
}
if (edgevert[0] == patchvert[2] && edgevert[1] == patchvert[1])
{
kvdir[1] = -1;
}
}
if (Dimension() == 3)
{
// Third side
for (int i = 0; i < edges.Size(); i++)
{
patchTopo->GetEdgeVertices(edges[i], edgevert);
if (edgevert[0] == patchvert[0] && edgevert[1] == patchvert[4])
{
kvdir[2] = 1;
}
if (edgevert[0] == patchvert[4] && edgevert[1] == patchvert[0])
{
kvdir[2] = -1;
}
}
}
MFEM_VERIFY(kvdir.Find(0) == -1, "Could not find direction of knotvector.");
}
void NURBSExtension::CreateComprehensiveKV()
{
Array<int> edges, orient, kvdir;
Array<int> e(Dimension());
// 1D: comprehensive and unique KV are the same
if (Dimension() == 1)
{
knotVectorsCompr.SetSize(GetNKV());
for (int i = 0; i < GetNKV(); i++)
{
knotVectorsCompr[i] = new KnotVector(*(KnotVec(i)));
}
return;
}
else if (Dimension() == 2)
{
knotVectorsCompr.SetSize(GetNP()*Dimension());
e[0] = 0;
e[1] = 1;
}
else if (Dimension() == 3)
{
knotVectorsCompr.SetSize(GetNP()*Dimension());
e[0] = 0;
e[1] = 3;
e[2] = 8;
}
for (int p = 0; p < GetNP(); p++)
{
CheckKVDirection(p, kvdir);
patchTopo->GetElementEdges(p, edges, orient);
for (int d = 0; d < Dimension(); d++)
{
// Indices in unique and comprehensive sets of the KnotVector
int iun = edges[e[d]];
int icomp = Dimension()*p+d;
knotVectorsCompr[icomp] = new KnotVector(*(KnotVec(iun)));
if (kvdir[d] == -1) {knotVectorsCompr[icomp]->Flip();}
}
}
MFEM_VERIFY(ConsistentKVSets(), "Mismatch in KnotVectors");
}
void NURBSExtension::UpdateUniqueKV()
{
Array<int> e(Dimension());
// 1D: comprehensive and unique KV are the same
if (Dimension() == 1)
{
for (int i = 0; i < GetNKV(); i++)
{
*(KnotVec(i)) = *(knotVectorsCompr[i]);
}
return;
}
else if (Dimension() == 2)
{
e[0] = 0;
e[1] = 1;
}
else if (Dimension() == 3)
{
e[0] = 0;
e[1] = 3;
e[2] = 8;
}
for (int p = 0; p < GetNP(); p++)
{
Array<int> edges, orient, kvdir;
patchTopo->GetElementEdges(p, edges, orient);
CheckKVDirection(p, kvdir);
for ( int d = 0; d < Dimension(); d++)
{
bool flip = false;
if (kvdir[d] == -1) {flip = true;}
// Indices in unique and comprehensive sets of the KnotVector
int iun = edges[e[d]];
int icomp = Dimension()*p+d;
// Check if difference in order
int o1 = KnotVec(iun)->GetOrder();
int o2 = knotVectorsCompr[icomp]->GetOrder();
int diffo = abs(o1 - o2);
if (diffo)
{
// Update reduced set of knotvectors
*(KnotVec(iun)) = *(knotVectorsCompr[icomp]);
// Give correct direction to unique knotvector.
if (flip) { KnotVec(iun)->Flip(); }
}
// Check if difference between knots
Vector diffknot;
if (flip) { knotVectorsCompr[icomp]->Flip(); }
KnotVec(iun)->Difference(*(knotVectorsCompr[icomp]), diffknot);
if (flip) { knotVectorsCompr[icomp]->Flip(); }
if (diffknot.Size() > 0)
{
// Update reduced set of knotvectors
*(KnotVec(iun)) = *(knotVectorsCompr[icomp]);
// Give correct direction to unique knotvector.
if (flip) {KnotVec(iun)->Flip();}
}
}
}
MFEM_VERIFY(ConsistentKVSets(), "Mismatch in KnotVectors");
}
bool NURBSExtension::ConsistentKVSets()
{
// patchTopo->GetElementEdges is not yet implemented for 1D
MFEM_VERIFY(Dimension()>1, "1D not yet implemented.");
Array<int> edges, orient, kvdir;
Vector diff;
Array<int>e(Dimension());
e[0] = 0;
if (Dimension() == 2)
{
e[1] = 1;
}
else if (Dimension() == 3)
{
e[1] = 3;
e[2] = 8;
}
for (int p = 0; p < GetNP(); p++)
{
patchTopo->GetElementEdges(p, edges, orient);
CheckKVDirection(p, kvdir);
for (int d = 0; d < Dimension(); d++)
{
bool flip = false;
if (kvdir[d] == -1) {flip = true;}
// Indices in unique and comprehensive sets of the KnotVector
int iun = edges[e[d]];
int icomp = Dimension()*p+d;
// Check if KnotVectors are of equal order
int o1 = KnotVec(iun)->GetOrder();
int o2 = knotVectorsCompr[icomp]->GetOrder();
int diffo = abs(o1 - o2);
if (diffo)
{
mfem::out << "\norder of knotVectorsCompr " << d << " of patch " << p;
mfem::out << " does not agree with knotVectors " << KnotInd(iun) << "\n";
return false;
}
// Check if Knotvectors have the same knots
if (flip) {knotVectorsCompr[icomp]->Flip();}
KnotVec(iun)->Difference(*(knotVectorsCompr[icomp]), diff);
if (flip) {knotVectorsCompr[icomp]->Flip();}
if (diff.Size() > 0)
{
mfem::out << "\nknotVectorsCompr " << d << " of patch " << p;
mfem::out << " does not agree with knotVectors " << KnotInd(iun) << "\n";
return false;
}
}
}
return true;
}
void NURBSExtension::GetPatchKnotVectors(int p, Array<KnotVector *> &kv)
{
Array<int> edges, orient;
@@ -2410,20 +2675,18 @@ void NURBSExtension::GetPatchKnotVectors(int p, Array<KnotVector *> &kv)
if (Dimension() == 1)
{
kv[0] = KnotVec(p);
kv[0] = knotVectorsCompr[Dimension()*p];
}
else if (Dimension() == 2)
{
patchTopo->GetElementEdges(p, edges, orient);
kv[0] = KnotVec(edges[0]);
kv[1] = KnotVec(edges[1]);
kv[0] = knotVectorsCompr[Dimension()*p];
kv[1] = knotVectorsCompr[Dimension()*p + 1];
}
else
{
patchTopo->GetElementEdges(p, edges, orient);
kv[0] = KnotVec(edges[0]);
kv[1] = KnotVec(edges[3]);
kv[2] = KnotVec(edges[8]);
kv[0] = knotVectorsCompr[Dimension()*p];
kv[1] = knotVectorsCompr[Dimension()*p + 1];
kv[2] = knotVectorsCompr[Dimension()*p + 2];
}
}
@@ -2433,22 +2696,21 @@ const
Array<int> edges, orient;
kv.SetSize(Dimension());
if (Dimension() == 1)
{
kv[0] = KnotVec(p);
kv[0] = knotVectorsCompr[Dimension()*p];
}
else if (Dimension() == 2)
{
patchTopo->GetElementEdges(p, edges, orient);
kv[0] = KnotVec(edges[0]);
kv[1] = KnotVec(edges[1]);
kv[0] = knotVectorsCompr[Dimension()*p];
kv[1] = knotVectorsCompr[Dimension()*p + 1];
}
else
{
patchTopo->GetElementEdges(p, edges, orient);
kv[0] = KnotVec(edges[0]);
kv[1] = KnotVec(edges[3]);
kv[2] = KnotVec(edges[8]);
kv[0] = knotVectorsCompr[Dimension()*p];
kv[1] = knotVectorsCompr[Dimension()*p + 1];
kv[2] = knotVectorsCompr[Dimension()*p + 2];
}
}
@@ -3089,6 +3351,55 @@ void NURBSExtension::Generate3DElementDofTable()
el_dof = new Table(NumOfActiveElems, el_dof_list);
}
void NURBSExtension::GetPatchDofs(const int patch, Array<int> &dofs)
{
const KnotVector *kv[3];
NURBSPatchMap p2g(this);
p2g.SetPatchDofMap(patch, kv);
if (Dimension() == 1)
{
const int nx = kv[0]->GetNCP();
dofs.SetSize(nx);
for (int i=0; i<nx; ++i)
{
dofs[i] = DofMap(p2g(i));
}
}
else if (Dimension() == 2)
{
const int nx = kv[0]->GetNCP();
const int ny = kv[1]->GetNCP();
dofs.SetSize(nx * ny);
for (int j=0; j<ny; ++j)
for (int i=0; i<nx; ++i)
{
dofs[i + (nx * j)] = DofMap(p2g(i, j));
}
}
else if (Dimension() == 3)
{
const int nx = kv[0]->GetNCP();
const int ny = kv[1]->GetNCP();
const int nz = kv[2]->GetNCP();
dofs.SetSize(nx * ny * nz);
for (int k=0; k<nz; ++k)
for (int j=0; j<ny; ++j)
for (int i=0; i<nx; ++i)
{
dofs[i + (nx * (j + (k * ny)))] = DofMap(p2g(i, j, k));
}
}
else
{
MFEM_ABORT("Only 1D/2D/3D supported currently in NURBSExtension::GetPatchDofs");
}
}
void NURBSExtension::GenerateBdrElementDofTable()
{
if (Dimension() == 1)
@@ -3348,6 +3659,7 @@ void NURBSExtension::SetKnotsFromPatches()
}
}
UpdateUniqueKV();
SetOrdersFromKnotVectors();
GenerateOffsets();
@@ -3471,6 +3783,7 @@ void NURBSExtension::KnotInsert(Array<KnotVector *> &kv)
{
Array<int> edges;
Array<int> orient;
Array<int> kvdir;
Array<KnotVector *> pkv(Dimension());
@@ -3494,7 +3807,26 @@ void NURBSExtension::KnotInsert(Array<KnotVector *> &kv)
pkv[2] = kv[KnotInd(edges[8])];
}
patches[p]->KnotInsert(pkv);
// Check whether inserted knots should be flipped before inserting.
// Knotvectors are stored in a different array pkvc such that the original
// knots which are inserted are not changed.
// We need those knots for multiple patches so they have to remain original
CheckKVDirection(p, kvdir);
Array<KnotVector *> pkvc(Dimension());
for (int d = 0; d < Dimension(); d++)
{
pkvc[d] = new KnotVector(*(pkv[d]));
if (kvdir[d] == -1)
{
pkvc[d]->Flip();
}
}
patches[p]->KnotInsert(pkvc);
for (int d = 0; d < Dimension(); d++) { delete pkvc[d]; }
}
}
@@ -3502,6 +3834,7 @@ void NURBSExtension::KnotInsert(Array<Vector *> &kv)
{
Array<int> edges;
Array<int> orient;
Array<int> kvdir;
Array<Vector *> pkv(Dimension());
@@ -3525,11 +3858,41 @@ void NURBSExtension::KnotInsert(Array<Vector *> &kv)
pkv[2] = kv[KnotInd(edges[8])];
}
patches[p]->KnotInsert(pkv);
// Check whether inserted knots should be flipped before inserting.
// Knotvectors are stored in a different array pkvc such that the original
// knots which are inserted are not changed.
CheckKVDirection(p, kvdir);
Array<Vector *> pkvc(Dimension());
for (int d = 0; d < Dimension(); d++)
{
pkvc[d] = new Vector(*(pkv[d]));
if (kvdir[d] == -1)
{
// Find flip point, for knotvectors that do not have the domain [0:1]
KnotVector *kva = knotVectorsCompr[Dimension()*p+d];
double apb = (*kva)[0] + (*kva)[kva->Size()-1];
// Flip vector
int size = pkvc[d]->Size();
int ns = ceil(size/2.0);
for (int j = 0; j < ns; j++)
{
double tmp = apb - pkvc[d]->Elem(j);
pkvc[d]->Elem(j) = apb - pkvc[d]->Elem(size-1-j);
pkvc[d]->Elem(size-1-j) = tmp;
}
}
}
patches[p]->KnotInsert(pkvc);
for (int i = 0; i < Dimension(); i++) { delete pkvc[i]; }
}
}
void NURBSExtension::GetPatchNets(const Vector &coords, int vdim)
{
if (Dimension() == 1)
@@ -3730,6 +4093,12 @@ void NURBSExtension::Set3DSolutionVector(Vector &coords, int vdim)
}
}
void NURBSExtension::GetElementIJK(int elem, Array<int> & ijk)
{
MFEM_VERIFY(ijk.Size() == el_to_IJK.NumCols(), "");
el_to_IJK.GetRow(elem, ijk);
}
void NURBSExtension::SetPatchToElements()
{
const int np = GetNP();
@@ -3813,6 +4182,7 @@ ParNURBSExtension::ParNURBSExtension(MPI_Comm comm, NURBSExtension *parent,
{
knotVectors[i] = new KnotVector(*parent->GetKnotVector(i));
}
CreateComprehensiveKV();
GenerateOffsets();
CountElements();
@@ -3868,6 +4238,7 @@ ParNURBSExtension::ParNURBSExtension(NURBSExtension *parent,
NumOfKnotVectors = parent->NumOfKnotVectors;
Swap(knotVectors, parent->knotVectors);
Swap(knotVectorsCompr, parent->knotVectorsCompr);
NumOfVertices = parent->NumOfVertices;
NumOfElements = parent->NumOfElements;
@@ -4171,22 +4542,23 @@ void NURBSPatchMap::GetPatchKnotVectors(int p, const KnotVector *kv[])
if (Ext->Dimension() == 1)
{
kv[0] = Ext->KnotVec(p);
kv[0] = Ext->knotVectorsCompr[Ext->Dimension()*p];
}
else if (Ext->Dimension() == 2)
{
Ext->patchTopo->GetElementEdges(p, edges, oedge);
kv[0] = Ext->KnotVec(edges[0]);
kv[1] = Ext->KnotVec(edges[1]);
kv[0] = Ext->knotVectorsCompr[Ext->Dimension()*p];
kv[1] = Ext->knotVectorsCompr[Ext->Dimension()*p + 1];
}
else if (Ext->Dimension() == 3)
{
Ext->patchTopo->GetElementEdges(p, edges, oedge);
Ext->patchTopo->GetElementFaces(p, faces, oface);
kv[0] = Ext->KnotVec(edges[0]);
kv[1] = Ext->KnotVec(edges[3]);
kv[2] = Ext->KnotVec(edges[8]);
kv[0] = Ext->knotVectorsCompr[Ext->Dimension()*p];
kv[1] = Ext->knotVectorsCompr[Ext->Dimension()*p + 1];
kv[2] = Ext->knotVectorsCompr[Ext->Dimension()*p + 2];
}
opatch = 0;
}
+34 -2
View File
@@ -21,6 +21,7 @@
#include "../general/communication.hpp"
#endif
#include <iostream>
#include <set>
namespace mfem
{
@@ -218,7 +219,11 @@ protected:
Mesh *patchTopo;
int own_topo;
Array<int> edge_to_knot;
/** Set of knotvectors containing unique KnotVectors only */
Array<KnotVector *> knotVectors;
/** Comprehensive set of knotvectors. This set contains a KnotVector for
every edge.*/
Array<KnotVector *> knotVectorsCompr;
Vector weights;
// periodic BC info:
@@ -261,10 +266,22 @@ protected:
void CheckPatches();
void CheckBdrPatches();
/** Checks the direction of the knotvectors in the patch based on
the patch orientation for patch @a p returns the direction of
the Knotvectors in @a kvdir.*/
void CheckKVDirection(int p, Array <int> &kvdir);
/** Creates the comprehensive set of KnotVectors. They are the same for 1D. */
void CreateComprehensiveKV();
/** Updates the unique set of KnotVectors */
void UpdateUniqueKV();
/** Checks if the comprehensive array of KnotVectors agrees with
the reduced set of KnotVectors. Returns false if it finds
a difference. */
bool ConsistentKVSets();
void GetPatchKnotVectors (int p, Array<KnotVector *> &kv);
void GetPatchKnotVectors (int p, Array<const KnotVector *> &kv) const;
void GetBdrPatchKnotVectors(int p, Array<KnotVector *> &kv);
void GetBdrPatchKnotVectors(int p, Array<const KnotVector *> &kv) const;
void SetOrderFromOrders();
void SetOrdersFromKnotVectors();
@@ -407,6 +424,11 @@ public:
int GetNTotalDof() const { return NumOfDofs; }
int GetNDof() const { return NumOfActiveDofs; }
/// Returns knotvectors in each dimension for patch @a p.
void GetPatchKnotVectors(int p, Array<const KnotVector *> &kv) const;
void GetBdrPatchKnotVectors(int p, Array<const KnotVector *> &kv) const;
// Knotvector read-only access function
const KnotVector *GetKnotVector(int i) const { return knotVectors[i]; }
@@ -466,6 +488,16 @@ public:
void KnotInsert(Array<KnotVector *> &kv);
void KnotInsert(Array<Vector *> &kv);
/// Returns the index of the patch containing element @a elem.
int GetElementPatch(int elem) const { return el_to_patch[elem]; }
/** Returns the Cartesian indices (i,j) in 2D or (i,j,k) in 3D of element
@a elem, in the knot-span tensor product ordering for its patch. */
void GetElementIJK(int elem, Array<int> & ijk);
// Returns the degrees of freedom on the patch, in Cartesian order.
void GetPatchDofs(const int patch, Array<int> &dofs);
const Array<int>& GetPatchElements(int patch);
const Array<int>& GetPatchBdrElements(int patch);
};
+3 -3
View File
@@ -4952,7 +4952,7 @@ void ParMesh::Print(std::ostream &os) const
}
}
void ParMesh::Save(const char *fname, int precision) const
void ParMesh::Save(const std::string &fname, int precision) const
{
ostringstream fname_with_suffix;
fname_with_suffix << fname << "." << setfill('0') << setw(6) << MyRank;
@@ -5615,7 +5615,7 @@ Mesh ParMesh::GetSerialMesh(int save_rank) const
return serialmesh;
}
void ParMesh::SaveAsOne(const char *fname, int precision) const
void ParMesh::SaveAsOne(const std::string &fname, int precision) const
{
ofstream ofs;
if (MyRank == 0)
@@ -6505,7 +6505,7 @@ static void PrintVertex(const Vertex &v, int space_dim, ostream &os)
}
}
void ParMesh::PrintSharedEntities(const char *fname_prefix) const
void ParMesh::PrintSharedEntities(const std::string &fname_prefix) const
{
stringstream out_name;
out_name << fname_prefix << '_' << setw(5) << setfill('0') << MyRank
+3 -3
View File
@@ -596,7 +596,7 @@ public:
/// given suffixes according to the MPI rank. The mesh will be written to the
/// files using ParMesh::Print. The given @a precision will be used for ASCII
/// output.
void Save(const char *fname, int precision=16) const override;
void Save(const std::string &fname, int precision=16) const override;
#ifdef MFEM_USE_ADIOS2
/** Print the part of the mesh in the calling processor using adios2 bp
@@ -625,7 +625,7 @@ public:
/// Save the mesh as a single file (using ParMesh::PrintAsOne). The given
/// @a precision is used for ASCII output.
void SaveAsOne(const char *fname, int precision=16) const;
void SaveAsOne(const std::string &fname, int precision=16) const;
/// Old mesh format (Netgen/Truegrid) version of 'PrintAsOne'
void PrintAsOneXG(std::ostream &out = mfem::out);
@@ -662,7 +662,7 @@ public:
InverseElementTransformation *inv_trans = NULL) override;
/// Debugging method
void PrintSharedEntities(const char *fname_prefix) const;
void PrintSharedEntities(const std::string &fname_prefix) const;
virtual ~ParMesh();
+17 -23
View File
@@ -435,25 +435,21 @@ void ParNCMesh::CreateGroups(int nentities, Array<Connection> &index_rank,
entity_group = 0;
CommGroup group;
group.reserve(128);
int begin = 0, end = 0;
while (begin < index_rank.Size())
for (auto begin = index_rank.begin(); begin != index_rank.end(); /* nothing */)
{
int index = index_rank[begin].from;
if (index >= nentities)
{
break; // probably creating entity_conf_group (no ghosts)
}
while (end < index_rank.Size() && index_rank[end].from == index)
{
end++;
}
group.resize(end - begin);
for (int i = begin; i < end; i++)
{
group[i - begin] = index_rank[i].to;
}
const auto &index = begin->from;
if (index >= nentities) { break; }
// Locate the next connection that is not from this index
const auto end = std::find_if(begin, index_rank.end(),
[&index](const mfem::Connection &c) { return c.from != index;});
// For each connection from this index, collect the ranks connected.
group.resize(std::distance(begin, end));
std::transform(begin, end, group.begin(), [](const mfem::Connection &c) { return c.to; });
// assign this entity's group and advance the search start
entity_group[index] = GetGroupId(group);
begin = end;
}
@@ -461,9 +457,9 @@ void ParNCMesh::CreateGroups(int nentities, Array<Connection> &index_rank,
void ParNCMesh::AddConnections(int entity, int index, const Array<int> &ranks)
{
for (int i = 0; i < ranks.Size(); i++)
for (auto rank : ranks)
{
entity_index_rank[entity].Append(Connection(index, ranks[i]));
entity_index_rank[entity].Append(Connection(index, rank));
}
}
@@ -480,9 +476,8 @@ void ParNCMesh::CalculatePMatrixGroups()
ranks.Reserve(256);
// connect slave edges to master edges and their vertices
for (int i = 0; i < shared_edges.masters.Size(); i++)
for (const auto &master_edge : shared_edges.masters)
{
const Master &master_edge = shared_edges.masters[i];
ranks.SetSize(0);
for (int j = master_edge.slaves_begin; j < master_edge.slaves_end; j++)
{
@@ -502,9 +497,8 @@ void ParNCMesh::CalculatePMatrixGroups()
}
// connect slave faces to master faces and their edges and vertices
for (int i = 0; i < shared_faces.masters.Size(); i++)
for (const auto &master_face : shared_faces.masters)
{
const Master &master_face = shared_faces.masters[i];
ranks.SetSize(0);
for (int j = master_face.slaves_begin; j < master_face.slaves_end; j++)
{
+25 -25
View File
@@ -82,23 +82,23 @@ public:
/** An override of NCMesh::Refine, which is called eventually, after making
sure that refinements that occur on the processor boundary are sent to
the neighbor processors so they can keep their ghost layers up to date.*/
virtual void Refine(const Array<Refinement> &refinements);
void Refine(const Array<Refinement> &refinements) override;
/// Parallel version of NCMesh::LimitNCLevel.
virtual void LimitNCLevel(int max_nc_level);
void LimitNCLevel(int max_nc_level) override;
/** Parallel version of NCMesh::CheckDerefinementNCLevel. */
virtual void CheckDerefinementNCLevel(const Table &deref_table,
Array<int> &level_ok, int max_nc_level);
void CheckDerefinementNCLevel(const Table &deref_table,
Array<int> &level_ok, int max_nc_level) override;
/** Parallel reimplementation of NCMesh::Derefine, keeps ghost layers
in sync. The interface is identical. */
virtual void Derefine(const Array<int> &derefs);
void Derefine(const Array<int> &derefs) override;
/** Gets partitioning for the coarse mesh if the current fine mesh were to
be derefined. */
virtual void GetFineToCoarsePartitioning(const Array<int> &derefs,
Array<int> &new_ranks) const;
void GetFineToCoarsePartitioning(const Array<int> &derefs,
Array<int> &new_ranks) const;
/** Migrate leaf elements of the global refinement hierarchy (including ghost
elements) so that each processor owns the same number of leaves (+-1).
@@ -116,7 +116,7 @@ public:
int GetNGhostVertices() const { return NGhostVertices; }
int GetNGhostEdges() const { return NGhostEdges; }
int GetNGhostFaces() const { return NGhostFaces; }
int GetNGhostElements() const { return NGhostElements; }
int GetNGhostElements() const override { return NGhostElements; }
// Return a list of vertices/edges/faces shared by this processor and at
// least one other processor. These are subsets of NCMesh::<entity>_list. */
@@ -232,12 +232,12 @@ public:
/** Extension of NCMesh::GetBoundaryClosure. Filters out ghost vertices and
ghost edges from 'bdr_vertices' and 'bdr_edges'. */
virtual void GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
Array<int> &bdr_vertices,
Array<int> &bdr_edges);
void GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
Array<int> &bdr_vertices,
Array<int> &bdr_edges) override;
/// Save memory by releasing all non-essential and cached data.
virtual void Trim();
void Trim() override;
/// Return total number of bytes allocated.
std::size_t MemoryUsage(bool with_base = true) const;
@@ -267,8 +267,8 @@ protected: // implementation
MPI_Comm MyComm;
int NRanks;
typedef std::vector<CommGroup> GroupList;
typedef std::map<CommGroup, GroupId> GroupMap;
using GroupList = std::vector<CommGroup>;
using GroupMap = std::map<CommGroup, GroupId>;
GroupList groups; // comm group list; NOTE: groups[0] = { MyRank }
GroupMap group_id; // search index over groups
@@ -299,7 +299,7 @@ protected: // implementation
Array<int> ghost_layer; ///< list of elements whose 'element_type' == 2.
Array<int> boundary_layer; ///< list of type 3 elements
virtual void Update();
void Update() override;
/// Return the processor number for a global element number.
int Partition(long index, long total_elements) const
@@ -313,13 +313,13 @@ protected: // implementation
long PartitionFirstIndex(int rank, long total_elements) const
{ return (rank * total_elements + NRanks-1) / NRanks; }
virtual void BuildFaceList();
virtual void BuildEdgeList();
virtual void BuildVertexList();
void BuildFaceList() override;
void BuildEdgeList() override;
void BuildVertexList() override;
virtual void ElementSharesFace(int elem, int local, int face);
virtual void ElementSharesEdge(int elem, int local, int enode);
virtual void ElementSharesVertex(int elem, int local, int vnode);
void ElementSharesFace(int elem, int local, int face) override;
void ElementSharesEdge(int elem, int local, int enode) override;
void ElementSharesVertex(int elem, int local, int vnode) override;
GroupId GetGroupId(const CommGroup &group);
GroupId GetSingletonGroup(int rank);
@@ -451,8 +451,8 @@ protected: // implementation
protected:
ParNCMesh* pncmesh;
virtual void Encode(int);
virtual void Decode(int);
void Encode(int) override;
void Decode(int) override;
};
/** Used by ParNCMesh::Refine() to inform neighbors about refinements at
@@ -513,8 +513,8 @@ protected: // implementation
protected:
ElementSet eset;
virtual void Encode(int);
virtual void Decode(int);
void Encode(int) override;
void Decode(int) override;
};
/** Assign new Element::rank to leaf elements and send them to their new
+7
View File
@@ -162,6 +162,8 @@ void ParTransferMap::Transfer(const ParGridFunction &src,
if (category_ == TransferCategory::ParentToSubMesh)
{
// dst = S1^T src
src.HostRead();
dst.HostWrite(); // dst is fully overwritten
for (int i = 0; i < sub1_to_parent_map_.Size(); i++)
{
double s = 1.0;
@@ -178,6 +180,8 @@ void ParTransferMap::Transfer(const ParGridFunction &src,
//
// G is identity if the partitioning matches
src.HostRead();
dst.HostReadWrite(); // dst is only partially overwritten
for (int i = 0; i < sub1_to_parent_map_.Size(); i++)
{
double s = 1.0;
@@ -196,6 +200,9 @@ void ParTransferMap::Transfer(const ParGridFunction &src,
//
// G is identity if the partitioning matches
src.HostRead();
dst.HostReadWrite();
z_ = 0.0;
for (int i = 0; i < sub2_to_parent_map_.Size(); i++)
+7
View File
@@ -152,6 +152,8 @@ void TransferMap::Transfer(const GridFunction &src,
if (category_ == TransferCategory::ParentToSubMesh)
{
// dst = S1^T src
src.HostRead();
dst.HostWrite(); // dst is fully overwritten
for (int i = 0; i < sub1_to_parent_map_.Size(); i++)
{
double s = 1.0;
@@ -168,6 +170,8 @@ void TransferMap::Transfer(const GridFunction &src,
//
// G is identity if the partitioning matches
src.HostRead();
dst.HostReadWrite(); // dst is only partially overwritten
for (int i = 0; i < sub1_to_parent_map_.Size(); i++)
{
double s = 1.0;
@@ -184,6 +188,9 @@ void TransferMap::Transfer(const GridFunction &src,
//
// G is identity if the partitioning matches
src.HostRead();
dst.HostReadWrite();
z_ = 0.0;
for (int i = 0; i < sub2_to_parent_map_.Size(); i++)
-151
View File
@@ -1,151 +0,0 @@
// Contact example
//
// Compile with: make contact
//
// Sample runs: ./contact -m1 block1.mesh -m2 block2.mesh -at "5 6 7 8"
// Sample runs: ./contact -m1 block1_d.mesh -m2 block2_d.mesh -at "5 6 7 8"
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "ipsolver/IPsolver.hpp"
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file1 = "meshes/block1.mesh";
const char *mesh_file2 = "meshes/rotatedblock2.mesh";
int order = 1;
int ref = 0;
Array<int> attr;
Array<int> m_attr;
int linSolver = 2;
bool paraview = false;
bool visualization = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file1, "-m1", "--mesh1",
"First mesh file to use.");
args.AddOption(&mesh_file2, "-m2", "--mesh2",
"Second mesh file to use.");
args.AddOption(&attr, "-at", "--attributes-surf",
"Attributes of boundary faces on contact surface for mesh 2.");
args.AddOption(&ref, "-r", "--refinements",
"Number of uniform refinements.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
ElasticityProblem prob1(mesh_file1,ref,order);
ElasticityProblem prob2(mesh_file2,ref,order);
ContactProblem contact(&prob1, &prob2);
QPOptContactProblem qpopt(&contact);
int numconstr = contact.GetNumConstraints();
InteriorPointSolver optimizer(&qpopt);
optimizer.SetTol(1e-6);
optimizer.SetMaxIter(50);
optimizer.SetLinearSolver(linSolver);
optimizer.SetLinearSolveTol(1e-10);
GridFunction x1 = prob1.GetDisplacementGridFunction();
GridFunction x2 = prob2.GetDisplacementGridFunction();
int ndofs1 = prob1.GetNumDofs();
int ndofs2 = prob2.GetNumDofs();
int ndofs = ndofs1 + ndofs2;
Vector x0(ndofs); x0 = 0.0;
x0.SetVector(x1,0);
x0.SetVector(x2,x1.Size());
Vector xf(ndofs); xf = 0.0;
optimizer.Mult(x0, xf);
Array<int> & CGiterations = optimizer.GetCGIterNumbers();
double Einitial = contact.E(x0);
double Efinal = contact.E(xf);
mfem::out << endl;
mfem::out << " Initial Energy objective = " << Einitial << endl;
mfem::out << " Final Energy objective = " << Efinal << endl;
mfem::out << " Global number of dofs = " << ndofs1 + ndofs2 << endl;
mfem::out << " Global number of constraints = " << numconstr << endl;
mfem::out << " CG iteration numbers = " ;
CGiterations.Print(mfem::out, CGiterations.Size());
MFEM_VERIFY(optimizer.GetConverged(),
"Interior point solver did not converge.");
if (visualization || paraview)
{
FiniteElementSpace * fes1 = prob1.GetFESpace();
FiniteElementSpace * fes2 = prob2.GetFESpace();
Mesh * mesh1 = fes1->GetMesh();
Mesh * mesh2 = fes2->GetMesh();
GridFunction x1_gf(fes1,xf.GetData());
GridFunction x2_gf(fes2,&xf.GetData()[fes1->GetTrueVSize()]);
mesh1->MoveNodes(x1_gf);
mesh2->MoveNodes(x2_gf);
if (paraview)
{
ParaViewDataCollection paraview_dc1("QPContactBody1", mesh1);
paraview_dc1.SetPrefixPath("ParaView");
paraview_dc1.SetLevelsOfDetail(1);
paraview_dc1.SetDataFormat(VTKFormat::BINARY);
paraview_dc1.SetHighOrderOutput(true);
paraview_dc1.SetCycle(0);
paraview_dc1.SetTime(0.0);
paraview_dc1.RegisterField("Body1", &x1_gf);
paraview_dc1.Save();
ParaViewDataCollection paraview_dc2("QPContactBody2", mesh2);
paraview_dc2.SetPrefixPath("ParaView");
paraview_dc2.SetLevelsOfDetail(1);
paraview_dc2.SetDataFormat(VTKFormat::BINARY);
paraview_dc2.SetHighOrderOutput(true);
paraview_dc2.SetCycle(0);
paraview_dc2.SetTime(0.0);
paraview_dc2.RegisterField("Body2", &x2_gf);
paraview_dc2.Save();
}
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
{
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "parallel " << 2 << " " << 0 << "\n"
<< "solution\n" << *mesh1 << x1_gf << flush;
}
{
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "parallel " << 2 << " " << 1 << "\n"
<< "solution\n" << *mesh2 << x2_gf << flush;
}
}
}
return 0;
}
-818
View File
@@ -1,818 +0,0 @@
#include "mfem.hpp"
#include "IPsolver.hpp"
#include <fstream>
#include <iostream>
#include <cstdlib>
using namespace std;
using namespace mfem;
InteriorPointSolver::InteriorPointSolver(QPOptContactProblem * Problem)
: optProblem(Problem), block_offsetsumlz(5), block_offsetsuml(4), block_offsetsx(3),
saveLogBarrierIterates(false)
{
rel_tol = 1.e-2;
max_iter = 20;
mu_k = 1.0;
sMax = 1.e2;
kSig = 1.e10; // control deviation from primal Hessian
tauMin = 0.8; // control rate at which iterates can approach the boundary
eta = 1.e-4; // backtracking constant
thetaMin = 1.e-4; // allowed violation of the equality constraints
// constants in line-step A-5.4
delta = 1.0;
sTheta = 1.1;
sPhi = 2.3;
// control the rate at which the penalty parameter is decreased
kMu = 0.2;
thetaMu = 1.5;
// TO DO -- include the filter
thetaMax = 1.e6; // maximum constraint violation
// data for the second order correction
kSoc = 0.99;
// equation (18)
gTheta = 1.e-5;
gPhi = 1.e-5;
kEps = 1.e1;
dimU = optProblem->GetDimU();
dimM = optProblem->GetDimM();
dimC = optProblem->GetDimC();
ckSoc.SetSize(dimC);
block_offsetsumlz[0] = 0;
block_offsetsumlz[1] = dimU; // u
block_offsetsumlz[2] = dimM; // m
block_offsetsumlz[3] = dimC; // lambda
block_offsetsumlz[4] = dimM; // zl
block_offsetsumlz.PartialSum();
for(int i = 0; i < block_offsetsuml.Size(); i++) { block_offsetsuml[i] = block_offsetsumlz[i]; }
for(int i = 0; i < block_offsetsx.Size(); i++) { block_offsetsx[i] = block_offsetsuml[i] ; }
// lower-bound for the inequality constraint m >= ml
ml = optProblem->Getml();
lk.SetSize(dimC); lk = 0.0;
zlk.SetSize(dimM); zlk = 0.0;
linSolver = 0;
MyRank = 0;
iAmRoot = MyRank == 0 ? true : false;
}
double InteriorPointSolver::MaxStepSize(Vector &x, Vector &xl, Vector &xhat, double tau)
{
double alphaMaxloc = 1.0;
double alphaTmp;
for(int i = 0; i < x.Size(); i++)
{
if( xhat(i) < 0. )
{
alphaTmp = -1. * tau * (x(i) - xl(i)) / xhat(i);
alphaMaxloc = min(alphaMaxloc, alphaTmp);
}
}
// alphaMaxloc is the local maximum step size which is
// distinct on each MPI process. Need to compute
// the global maximum step size
double alphaMaxglb;
alphaMaxglb = alphaMaxloc;
return alphaMaxglb;
}
double InteriorPointSolver::MaxStepSize(Vector &x, Vector &xhat, double tau)
{
Vector zero(x.Size()); zero = 0.0;
return MaxStepSize(x, zero, xhat, tau);
}
void InteriorPointSolver::Mult(const Vector &x0, Vector &xf)
{
BlockVector x0block(block_offsetsx); x0block = 0.0;
x0block.GetBlock(0).Set(1.0, x0);
// To do: give options for user specificiation of initialization m0
x0block.GetBlock(1) = 1.0;
x0block.GetBlock(1).Add(1.0, ml);
BlockVector xfblock(block_offsetsx); xfblock = 0.0;
Mult(x0block, xfblock);
xf.Set(1.0, xfblock.GetBlock(0));
}
void InteriorPointSolver::Mult(const BlockVector &x0, BlockVector &xf)
{
converged = false;
BlockVector xk(block_offsetsx), xhat(block_offsetsx); xk = 0; xhat = 0.0;
BlockVector Xk(block_offsetsumlz), Xhat(block_offsetsumlz); Xk = 0.0; Xhat = 0.0;
BlockVector Xhatuml(block_offsetsuml); Xhatuml = 0.0;
Vector zlhat(dimM); zlhat = 0.0;
xk.GetBlock(0).Set(1.0, x0.GetBlock(0));
xk.GetBlock(1).Set(1.0, x0.GetBlock(1));
// running estimate of the final values of the Lagrange multipliers
lk = 0.0;
zlk = 0.0;
for(int i = 0; i < dimM; i++)
{
zlk(i) = 1.e1 * mu_k / (xk(i+dimU) - ml(i));
}
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
Xk.GetBlock(2).Set(1.0, lk);
Xk.GetBlock(3).Set(1.0, zlk);
/* set theta0 = theta(x0)
* thetaMin
* thetaMax
* when theta(xk) < thetaMin and the switching condition holds
* then we ask for the Armijo sufficient decrease of the barrier
* objective to be satisfied, in order to accept the trial step length alphakl
*
* thetaMax controls how the filter is initialized for each log-barrier subproblem
* F0 = {(th, phi) s.t. th > thetaMax}
* that is the filter does not allow for iterates where the constraint violation
* is larger than that of thetaMax
*/
double theta0 = theta(xk);
thetaMin = 1.e-4 * max(1.0, theta0);
thetaMax = 1.e8 * thetaMin;
double Eeval, maxBarrierSolves, Eevalmu0;
bool printOptimalityError; // control optimality error print to console for log-barrier subproblems
maxBarrierSolves = 10;
for(jOpt = 0; jOpt < max_iter; jOpt++)
{
mfem::out << "interior-point solve step " << jOpt << endl;
// A-2. Check convergence of overall optimization problem
printOptimalityError = false;
Eevalmu0 = E(xk, lk, zlk, printOptimalityError);
if(Eevalmu0 < rel_tol)
{
converged = true;
mfem::out << "solved optimization problem :)\n";
break;
}
if(jOpt > 0) { maxBarrierSolves = 1; }
for(int i = 0; i < maxBarrierSolves; i++)
{
// A-3. Check convergence of the barrier subproblem
printOptimalityError = true;
Eeval = E(xk, lk, zlk, mu_k, printOptimalityError);
if(Eeval < kEps * mu_k)
{
mfem::out << "solved barrier subproblem, for mu = " << mu_k << endl;
// A-3.1. Recompute the barrier parameter
mu_k = max(rel_tol / 10., min(kMu * mu_k, pow(mu_k, thetaMu)));
// A-3.2. Re-initialize the filter
F1.DeleteAll();
F2.DeleteAll();
}
else
{
break;
}
}
// A-4. Compute the search direction
// solve for (uhat, mhat, lhat)
mfem::out << "\n** A-4. IP-Newton solve **\n";
zlhat = 0.0; Xhatuml = 0.0;
// why do we have Xhatuml ....???
// TO DO: remove Xhatuml in favor of passing Xhat
IPNewtonSolve(xk, lk, zlk, zlhat, Xhatuml, mu_k, false);
// assign data stack, X = (u, m, l, zl)
Xk = 0.0;
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
Xk.GetBlock(2).Set(1.0, lk);
Xk.GetBlock(3).Set(1.0, zlk);
// assign data stack, Xhat = (uhat, mhat, lhat, zlhat)
Xhat = 0.0;
for(int i = 0; i < 3; i++)
{
Xhat.GetBlock(i).Set(1.0, Xhatuml.GetBlock(i));
}
Xhat.GetBlock(3).Set(1.0, zlhat);
// A-5. Backtracking line search.
mfem::out << "\n** A-5. Linesearch **\n";
mfem::out << "mu = " << mu_k << endl;
lineSearch(Xk, Xhat, mu_k);
if(lineSearchSuccess)
{
if(!switchCondition || !sufficientDecrease)
{
F1.Append( (1. - gTheta) * thx0);
F2.Append( phx0 - gPhi * thx0);
}
// ----- A-6: Accept the trial point
// print info regarding zl...
xk.GetBlock(0).Add(alpha, Xhat.GetBlock(0));
xk.GetBlock(1).Add(alpha, Xhat.GetBlock(1));
lk.Add(alpha, Xhat.GetBlock(2));
zlk.Add(alphaz, Xhat.GetBlock(3));
projectZ(xk, zlk, mu_k);
}
else
{
mfem::out << "lineSearch not successful :(\n";
mfem::out << "attempting feasibility restoration with theta = " << thx0 << endl;
mfem::out << "no feasibility restoration implemented, exiting now \n";
break;
}
//
if(jOpt + 1 == max_iter)
{
mfem::out << "maximum optimization iterations :(\n";
}
}
// done with optimization routine, just reassign data to xf reference so
// that the application code has access to the optimal point
xf = 0.0;
xf.GetBlock(0).Set(1.0, xk.GetBlock(0));
xf.GetBlock(1).Set(1.0, xk.GetBlock(1));
}
void InteriorPointSolver::FormIPNewtonMat(BlockVector & x, Vector & l, Vector &zl, BlockOperator &Ak)
{
// WARNING: Huu, Hum, Hmu, Hmm should all be Hessian terms of the Lagrangian, currently we
// them by Hessian terms of the objective function and neglect the Hessian of l^T c
Huu = optProblem->Duuf(x);
Hum = optProblem->Dumf(x);
Hmu = optProblem->Dmuf(x);
Hmm = optProblem->Dmmf(x);
Vector DiagLogBar(dimM); DiagLogBar = 0.0;
for(int ii = 0; ii < dimM; ii++)
{
DiagLogBar(ii) = zl(ii) / (x(ii+dimU) - ml(ii));
}
if(saveLogBarrierIterates)
{
std::ofstream diagStream;
char diagString[100];
snprintf(diagString, 100, "logBarrierHessiandata/D%d.dat", jOpt);
diagStream.open(diagString, ios::out | ios::trunc);
for(int ii = 0; ii < dimM; ii++)
{
diagStream << setprecision(30) << DiagLogBar(ii) << endl;
}
diagStream.close();
}
delete Wmm;
if(Hmm != nullptr)
{
SparseMatrix * D = new SparseMatrix(DiagLogBar);
Wmm = Add(*Hmm, *D);
delete D;
}
else
{
Wmm = new SparseMatrix(DiagLogBar);
}
delete JuT;
delete JmT;
Ju = optProblem->Duc(x); JuT = Transpose(*Ju);
Jm = optProblem->Dmc(x); JmT = Transpose(*Jm);
Huucl = optProblem->lDuuc(x, l);
if(Huucl != nullptr)
{
delete HLuucl;
HLuucl = Add(*Huucl, *Huu);
Ak.SetBlock(0, 0, HLuucl);
}
else
{
Ak.SetBlock(0, 0, Huu);
}
// IP-Newton system matrix
// Ak = [[H_(u,u) H_(u,m) J_u^T]
// [H_(m,u) W_(m,m) J_m^T]
// [ J_u J_m 0 ]]
Ak.SetBlock(0, 0, Huu);
Ak.SetBlock(0, 2, JuT);
Ak.SetBlock(1, 1, Wmm);
Ak.SetBlock(1, 2, JmT);
Ak.SetBlock(2, 0, Ju);
Ak.SetBlock(2, 1, Jm);
if(Hum != nullptr) { Ak.SetBlock(0, 1, Hum); Ak.SetBlock(1, 0, Hmu); }
}
// perturbed KKT system solve
// determine the search direction
void InteriorPointSolver::IPNewtonSolve(BlockVector &x, Vector &l, Vector &zl, Vector &zlhat, BlockVector &Xhat, double mu, bool socSolve)
{
// solve A x = b, where A is the IP-Newton matrix
BlockOperator A(block_offsetsuml, block_offsetsuml); BlockVector b(block_offsetsuml); b = 0.0;
FormIPNewtonMat(x, l, zl, A);
// [grad_u phi + Ju^T l]
// b = - [grad_m phi + Jm^T l]
// [ c ]
BlockVector gradphi(block_offsetsx); gradphi = 0.0;
BlockVector JTl(block_offsetsx); JTl = 0.0;
Dxphi(x, mu, gradphi);
(A.GetBlock(0,2)).Mult(l, JTl.GetBlock(0));
(A.GetBlock(1,2)).Mult(l, JTl.GetBlock(1));
for(int ii = 0; ii < 2; ii++)
{
b.GetBlock(ii).Set(1.0, gradphi.GetBlock(ii));
b.GetBlock(ii).Add(1.0, JTl.GetBlock(ii));
}
if(!socSolve)
{
optProblem->c(x, b.GetBlock(2));
}
else
{
b.GetBlock(2).Set(1.0, ckSoc);
}
b *= -1.0;
Xhat = 0.0;
#ifdef MFEM_USE_SUITESPARSE
// Direct solve for IP-Newton saddle-point system
// A = [ [ Huu 0 Ju^T]
// [ 0 D -I ]
// [ Ju -I 0 ]]
// if(linSolver == 0)
// {
// BlockMatrix ABlockMatrix(block_offsetsuml, block_offsetsuml);
// for(int ii = 0; ii < 3; ii++)
// {
// for(int jj = 0; jj < 3; jj++)
// {
// if(!A.IsZeroBlock(ii, jj))
// {
// ABlockMatrix.SetBlock(ii, jj, dynamic_cast<SparseMatrix *>(&(A.GetBlock(ii, jj))));
// }
// }
// }
// /* direct solve of the 3x3 IP-Newton linear system */
// UMFPackSolver ASolver;
// SparseMatrix *ASparse = ABlockMatrix.CreateMonolithic();
// ASolver.SetOperator(*ASparse);
// ASolver.Mult(b, Xhat);
// Vector residual(Xhat.Size());
// ASparse->Mult(Xhat, residual);
// residual.Add(-1.0, b);
// delete ASparse;
// }
// else if(linSolver == 1)
// {
// // Direct solve for 0,0 Schur complement of IP-Newton system, Huu + Ju^T Wmm Ju,
// // where Wmm = D for contact problems
// SparseMatrix * Huuloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 0)));
// SparseMatrix * Wmmloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(1, 1)));
// SparseMatrix * Juloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(2, 0)));
// SparseMatrix * JuTloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 2)));
// Vector DVec(dimM); DVec = 0.0;
// Vector one(dimM); one = 1.0;
// D->Mult(one, DVec);
// SparseMatrix *JuTDJu = Mult_AtDA(*Juloc, DVec); // Ju^T D Ju
// SparseMatrix *Areduced = Add(*Huuloc, *JuTDJu); // Huu + Ju^T D Ju
// /* prepare the reduced rhs */
// // breduced = bu + Ju^T (bm + Wmm bl)
// Vector breduced(dimU); breduced = 0.0;
// Vector tempVec(dimM); tempVec = 0.0;
// Wmmloc->Mult(b.GetBlock(2), tempVec);
// tempVec.Add(1.0, b.GetBlock(1));
// JuTloc->Mult(tempVec, breduced);
// breduced.Add(1.0, b.GetBlock(0));
// // solve the reduced linear system
// UMFPackSolver AreducedSolver;
// AreducedSolver.SetOperator(*Areduced);
// AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
// // now propagate solved uhat to obtain mhat and lhat
// // xm = Ju xu - bl
// Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
// Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
// // xl = Wmm xm - bm
// Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
// Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
// delete JuTDJu;
// delete Areduced;
// }
#else
MFEM_VERIFY(linSolver > 1, "linSolver = 0, 1 require MFEM_USE_SUITESPARSE=YES");
#endif
// if(linSolver ==2)
{
// Iterative solve for 0,0 Schur complement of IP-Newton system, Huu + Ju^T Wmm Ju,
// where Wmm = D for contact problems
// here the iterative solver is a Jacobi-preconditioned CG-solve
SparseMatrix * Huuloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 0)));
SparseMatrix * Wmmloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(1, 1)));
SparseMatrix * Juloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(2, 0)));
SparseMatrix * JuTloc = dynamic_cast<SparseMatrix *>(&(A.GetBlock(0, 2)));
SparseMatrix *JuTDJu = RAP(*Juloc,*Wmmloc,*Juloc); // Ju^T D Ju
SparseMatrix *Areduced = Add(*Huuloc, *JuTDJu); // Huu + Ju^T D Ju
/* prepare the reduced rhs */
// breduced = bu + Ju^T (bm + Wmm bl)
Vector breduced(dimU); breduced = 0.0;
Vector tempVec(dimM); tempVec = 0.0;
Wmmloc->SortColumnIndices();
Wmmloc->Mult(b.GetBlock(2), tempVec);
tempVec.Add(1.0, b.GetBlock(1));
JuTloc->Mult(tempVec, breduced);
breduced.Add(1.0, b.GetBlock(0));
int globalNumRows = dimU;
HYPRE_BigInt rowStarts[2];
rowStarts[0] = 0;
rowStarts[1] = dimU;
HypreParMatrix Ahypre(MPI_COMM_WORLD, globalNumRows, rowStarts, Areduced);
HypreBoomerAMG Aprec(Ahypre);
Aprec.SetPrintLevel(0);
Aprec.SetSystemsOptions(3,false);
HyprePCG AreducedSolver(MPI_COMM_WORLD);
AreducedSolver.SetOperator(Ahypre);
// AreducedSolver.SetRelTol(linSolveTol);
// AreducedSolver.SetRelTol(1e-6);
AreducedSolver.SetTol(1e-6);
AreducedSolver.SetMaxIter(1000);
AreducedSolver.SetPreconditioner(Aprec);
// AreducedSolver.SetResidualConvergenceOptions();
AreducedSolver.SetPrintLevel(2);
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
int num_iterations;
AreducedSolver.GetNumIterations(num_iterations);
cgnum_iterations.Append(num_iterations);
// now propagate solved uhat to obtain mhat and lhat
// xm = Ju xu - bl
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
// xl = Wmm xm - bm
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
delete JuTDJu;
delete Areduced;
}
/* backsolve to determine zlhat */
for(int ii = 0; ii < dimM; ii++)
{
zlhat(ii) = -1.*(zl(ii) + (zl(ii) * Xhat(ii + dimU) - mu) / (x(ii + dimU) - ml(ii)) );
}
}
// here Xhat, X will be BlockVectors w.r.t. the 4 partitioning X = (u, m, l, zl)
void InteriorPointSolver::lineSearch(BlockVector& X0, BlockVector& Xhat, double mu)
{
double tau = max(tauMin, 1.0 - mu);
Vector u0 = X0.GetBlock(0);
Vector m0 = X0.GetBlock(1);
Vector l0 = X0.GetBlock(2);
Vector z0 = X0.GetBlock(3);
Vector uhat = Xhat.GetBlock(0);
Vector mhat = Xhat.GetBlock(1);
Vector lhat = Xhat.GetBlock(2);
Vector zhat = Xhat.GetBlock(3);
double alphaMax = MaxStepSize(m0, ml, mhat, tau);
double alphaMaxz = MaxStepSize(z0, zhat, tau);
alphaz = alphaMaxz;
BlockVector x0(block_offsetsx); x0 = 0.0;
x0.GetBlock(0).Set(1.0, u0);
x0.GetBlock(1).Set(1.0, m0);
BlockVector xhat(block_offsetsx); xhat = 0.0;
xhat.GetBlock(0).Set(1.0, uhat);
xhat.GetBlock(1).Set(1.0, mhat);
BlockVector xtrial(block_offsetsx); xtrial = 0.0;
BlockVector Dxphi0(block_offsetsx); Dxphi0 = 0.0;
int maxBacktrack = 20;
alpha = alphaMax;
Vector ck0(dimC); ck0 = 0.0;
Vector zhatsoc(dimM); zhatsoc = 0.0;
BlockVector Xhatumlsoc(block_offsetsuml); Xhatumlsoc = 0.0;
BlockVector xhatsoc(block_offsetsx); xhatsoc = 0.0;
Vector uhatsoc(dimU); uhatsoc = 0.0;
Vector mhatsoc(dimM); mhatsoc = 0.0;
Dxphi(x0, mu, Dxphi0);
Dxphi0_xhat = InnerProduct(Dxphi0, xhat);
descentDirection = Dxphi0_xhat < 0. ? true : false;
if(descentDirection)
{
mfem::out << "is a descent direction for the log-barrier objective\n";
}
else
{
mfem::out << "is not a descent direction for the log-barrier objective\n";
}
mfem::out << "Dxphi^T xhat / (|| Dxphi||_2 * || xhat ||_2) = " << Dxphi0_xhat / (xhat.Norml2() * Dxphi0.Norml2()) << endl;
thx0 = theta(x0);
phx0 = phi(x0, mu);
lineSearchSuccess = false;
for(int i = 0; i < maxBacktrack; i++)
{
mfem::out << "\n--------- alpha = " << alpha << " ---------\n";
// ----- A-5.2. Compute trial point: xtrial = x0 + alpha_i xhat
xtrial.Set(1.0, x0);
xtrial.Add(alpha, xhat);
// ------ A-5.3. if not in filter region go to A.5.4 otherwise go to A-5.5.
thxtrial = theta(xtrial);
phxtrial = phi(xtrial, mu);
filterCheck(thxtrial, phxtrial);
if(!inFilterRegion)
{
mfem::out << "not in filter region :)\n";
// ------ A.5.4: Check sufficient decrease
if(!descentDirection)
{
switchCondition = false;
}
else
{
switchCondition = (alpha * pow(abs(Dxphi0_xhat), sPhi) > delta * pow(thx0, sTheta)) ? true : false;
}
mfem::out << "theta(x0) = " << thx0 << ", thetaMin = " << thetaMin << endl;
mfem::out << "theta(xtrial) = " << thxtrial << ", (1-gTheta) *theta(x0) = " << (1. - gTheta) * thx0 << endl;
mfem::out << "phi(xtrial) = " << phxtrial << ", phi(x0) - gPhi *theta(x0) = " << phx0 - gPhi * thx0 << endl;
// Case I
if(thx0 <= thetaMin && switchCondition)
{
sufficientDecrease = phxtrial <= phx0 + eta * alpha * Dxphi0_xhat ? true : false;
if(sufficientDecrease)
{
mfem::out << "Accepted step length -- sufficient decrease in log-barrier objective.\n";
// accept the trial step
lineSearchSuccess = true;
break;
}
}
else
{
if(thxtrial <= (1. - gTheta) * thx0 || phxtrial <= phx0 - gPhi * thx0)
{
mfem::out << "Accepted step length -- decrease in either constraint violation or log-barrier objective.\n";
// accept the trial step
lineSearchSuccess = true;
break;
}
}
// A-5.5: Initialize the second-order correction
if((!(thx0 < thxtrial)) && i == 0)
{
mfem::out << "second order correction\n";
optProblem->c(xtrial, ckSoc);
optProblem->c(x0, ck0);
ckSoc.Add(alphaMax, ck0);
// A-5.6 Compute the second-order correction.
IPNewtonSolve(x0, l0, z0, zhatsoc, Xhatumlsoc, mu, true);
mhatsoc.Set(1.0, Xhatumlsoc.GetBlock(1));
// alphasoc = MaxStepSize(m0, ml, mhatsoc, tau);
//WARNING: not complete but currently solver isn't entering this region
}
}
else
{
mfem::out << "in filter region\n";
}
// include more if needed
alpha *= 0.5;
}
}
void InteriorPointSolver::projectZ(const Vector &x, Vector &z, double mu)
{
double zi;
double mudivmml;
for(int i = 0; i < dimM; i++)
{
zi = z(i);
mudivmml = mu / (x(i + dimU) - ml(i));
z(i) = max(min(zi, kSig * mudivmml), mudivmml / kSig);
}
}
void InteriorPointSolver::filterCheck(double th, double ph)
{
inFilterRegion = false;
if(th > thetaMax)
{
inFilterRegion = true;
}
else
{
for(int i = 0; i < F1.Size(); i++)
{
if(th >= F1[i] && ph >= F2[i])
{
inFilterRegion = true;
break;
}
}
}
}
double InteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, double mu, bool print)
{
double E1, E2, E3;
double sc, sd;
BlockVector gradL(block_offsetsx); gradL = 0.0; // stationarity grad L = grad f + J^T l - z
Vector cx(dimC); cx = 0.0; // feasibility c = c(x)
Vector comp(dimM); comp = 0.0; // complementarity M Z - mu 1
DxL(x, l, zl, gradL);
E1 = gradL.Normlinf();
optProblem->c(x, cx);
E2 = cx.Normlinf();
for(int ii = 0; ii < dimM; ii++)
{
comp(ii) = x(dimU + ii) * zl(ii) - mu;
}
E3 = comp.Normlinf();
double ll1, zl1;
zl1 = zl.Norml1() / double(dimC + dimM);
ll1 = l.Norml1();
sc = max(sMax, zl1 / (double(dimM)) ) / sMax;
sd = max(sMax, (ll1 + zl1) / (double(dimC + dimM))) / sMax;
if(print)
{
mfem::out << "evaluating optimality error for mu = " << mu << endl;
mfem::out << "stationarity measure = " << E1 / sd << endl;
mfem::out << "feasibility measure = " << E2 << endl;
mfem::out << "complimentarity measure = " << E3 / sc << endl;
}
return max(max(E1 / sd, E2), E3 / sc);
}
double InteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, bool print)
{
return E(x, l, zl, 0.0, print);
}
double InteriorPointSolver::theta(const BlockVector &x)
{
Vector cx(dimC); cx = 0.0;
optProblem->c(x, cx);
return cx.Norml2();
}
// log-barrier objective
double InteriorPointSolver::phi(const BlockVector &x, double mu)
{
double fx = optProblem->CalcObjective(x);
double logBarrierLoc = 0.0;
for(int i = 0; i < dimM; i++)
{
logBarrierLoc += log(x(dimU+i)-ml(i));
}
double logBarrierGlb = 0.0;
logBarrierGlb = logBarrierLoc;
return fx - mu * logBarrierGlb;
}
// gradient of log-barrier objective with respect to x = (u, m)
void InteriorPointSolver::Dxphi(const BlockVector &x, double mu, BlockVector &y)
{
optProblem->CalcObjectiveGrad(x, y);
for(int i = 0; i < dimM; i++)
{
y(dimU + i) -= mu / (x(dimU + i));
}
}
// Lagrangian function evaluation
// L(x, l, zl) = f(x) + l^T c(x) - zl^T m
double InteriorPointSolver::L(const BlockVector &x, const Vector &l, const Vector &zl)
{
double fx = optProblem->CalcObjective(x);
Vector cx(dimC); optProblem->c(x, cx);
return (fx + InnerProduct(cx, l) - InnerProduct(x.GetBlock(1), zl));
}
void InteriorPointSolver::DxL(const BlockVector &x, const Vector &l, const Vector &zl, BlockVector &y)
{
// evaluate the gradient of the objective with respect to the primal variables x = (u, m)
BlockVector gradxf(block_offsetsx); gradxf = 0.0;
optProblem->CalcObjectiveGrad(x, gradxf);
SparseMatrix *Jacu, *Jacm, *JacuT, *JacmT;
Jacu = optProblem->Duc(x); Jacm = optProblem->Dmc(x);
JacuT = Transpose(*Jacu);
JacmT = Transpose(*Jacm);
JacuT->Mult(l, y.GetBlock(0));
JacmT->Mult(l, y.GetBlock(1));
delete JacuT;
delete JacmT;
y.Add(1.0, gradxf);
(y.GetBlock(1)).Add(-1.0, zl);
}
bool InteriorPointSolver::GetConverged() const
{
return converged;
}
void InteriorPointSolver::SetTol(double Tol)
{
rel_tol = Tol;
}
void InteriorPointSolver::SetMaxIter(int max_it)
{
max_iter = max_it;
}
void InteriorPointSolver::SetBarrierParameter(double mu_0)
{
mu_k = mu_0;
}
void InteriorPointSolver::SaveLogBarrierHessianIterates(bool save)
{
MFEM_ASSERT(MyRank == 0 || save == false, "currently can only save logbarrier hessian in serial codes");
saveLogBarrierIterates = save;
}
void InteriorPointSolver::SetLinearSolver(int LinSolver)
{
linSolver = LinSolver;
}
void InteriorPointSolver::SetLinearSolveTol(double Tol)
{
linSolveTol = Tol;
}
InteriorPointSolver::~InteriorPointSolver()
{
delete HLuucl;
delete JuT;
delete JmT;
delete Wmm;
F1.DeleteAll();
F2.DeleteAll();
block_offsetsx.DeleteAll();
block_offsetsumlz.DeleteAll();
block_offsetsuml.DeleteAll();
ml.SetSize(0);
}
-94
View File
@@ -1,94 +0,0 @@
#include "mfem.hpp"
#include "../problems/problems.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
#ifndef IPSOLVER
#define IPSOLVER
class InteriorPointSolver
{
protected:
QPOptContactProblem * optProblem;
double rel_tol;
int max_iter;
double mu_k; // \mu_k
Vector lk, zlk;
double sMax, kSig, tauMin, eta, thetaMin, delta, sTheta, sPhi, kMu, thetaMu;
double thetaMax, kSoc, gTheta, gPhi, kEps;
// filter
Array<double> F1, F2;
// quantities computed in lineSearch
double alpha, alphaz;
double thx0, thxtrial;
double phx0, phxtrial;
bool descentDirection, switchCondition, sufficientDecrease, lineSearchSuccess, inFilterRegion;
double Dxphi0_xhat;
int dimU, dimM, dimC;
Array<int> block_offsetsumlz, block_offsetsuml, block_offsetsx;
Vector ml;
Vector ckSoc;
SparseMatrix * Huu = nullptr;
SparseMatrix * Hum = nullptr;
SparseMatrix * Hmu = nullptr;
SparseMatrix * Hmm = nullptr;
SparseMatrix * Wmm = nullptr;
SparseMatrix * Ju = nullptr;
SparseMatrix * Jm = nullptr;
SparseMatrix * JmT = nullptr;
SparseMatrix * JuT = nullptr;
SparseMatrix * Huucl = nullptr;
SparseMatrix * HLuucl = nullptr;
int jOpt;
bool converged;
int MyRank;
bool iAmRoot;
bool saveLogBarrierIterates;
int linSolver;
double linSolveTol;
Array<int> cgnum_iterations;
public:
InteriorPointSolver(QPOptContactProblem*);
void Mult(const BlockVector& , BlockVector&); // used when the user wants to be aware of bound-constrained variable m >= ml
void Mult(const Vector&, Vector &); // useful when the user doesn't need to know about bound-constrained variable m >= ml, e.g., when m is a slack variable
double MaxStepSize(Vector& , Vector& , Vector& , double);
double MaxStepSize(Vector& , Vector& , double);
void FormIPNewtonMat(BlockVector& , Vector& , Vector& , BlockOperator &);
void IPNewtonSolve(BlockVector& , Vector& , Vector& , Vector&, BlockVector& , double, bool);
void lineSearch(BlockVector& , BlockVector& , double);
void projectZ(const Vector & , Vector &, double);
void filterCheck(double, double);
double E(const BlockVector &, const Vector &, const Vector &, double, bool);
double E(const BlockVector &, const Vector &, const Vector &, bool);
Array<int> & GetCGIterNumbers() {return cgnum_iterations;}
bool GetConverged() const;
// TO DO: include Hessian of Lagrangian
double theta(const BlockVector &);
double phi(const BlockVector &, double);
void Dxphi(const BlockVector &, double, BlockVector &);
double L(const BlockVector &, const Vector &, const Vector &);
void DxL(const BlockVector &, const Vector &, const Vector &, BlockVector &);
void SetTol(double);
void SetMaxIter(int);
void SetBarrierParameter(double);
void SaveLogBarrierHessianIterates(bool);
void SetLinearSolver(int);
void SetLinearSolveTol(double);
virtual ~InteriorPointSolver();
};
#endif
-864
View File
@@ -1,864 +0,0 @@
#include "mfem.hpp"
#include "ParIPsolver.hpp"
#include <fstream>
#include <iostream>
#include <cstdlib>
using namespace std;
using namespace mfem;
ParInteriorPointSolver::ParInteriorPointSolver(QPOptParContactProblem * problem_)
: problem(problem_)
{
OptTol = 1.e-2;
max_iter = 20;
mu_k = 1.0;
sMax = 1.e2;
kSig = 1.e10; // control deviation from primal Hessian
tauMin = 0.8; // control rate at which iterates can approach the boundary
eta = 1.e-4; // backtracking constant
thetaMin = 1.e-4; // allowed violation of the equality constraints
// constants in line-step A-5.4
delta = 1.0;
sTheta = 1.1;
sPhi = 2.3;
// control the rate at which the penalty parameter is decreased
kMu = 0.2;
thetaMu = 1.5;
thetaMax = 1.e6; // maximum constraint violation
// data for the second order correction
kSoc = 0.99;
// equation (18)
gTheta = 1.e-5;
gPhi = 1.e-5;
kEps = 1.e1;
dimU = problem->GetDimU();
dimM = problem->GetDimM();
dimC = problem->GetDimC();
MPI_Allreduce(&dimU,&gdimU,1,MPI_INT,MPI_SUM,problem->GetComm());
MPI_Allreduce(&dimM,&gdimM,1,MPI_INT,MPI_SUM,problem->GetComm());
MPI_Allreduce(&dimC,&gdimC,1,MPI_INT,MPI_SUM,problem->GetComm());
ckSoc.SetSize(dimC);
block_offsetsumlz.SetSize(5);
block_offsetsuml.SetSize(4);
block_offsetsx.SetSize(3);
block_offsetsumlz[0] = 0;
block_offsetsumlz[1] = dimU; // u
block_offsetsumlz[2] = dimM; // m
block_offsetsumlz[3] = dimC; // lambda
block_offsetsumlz[4] = dimM; // zl
block_offsetsumlz.PartialSum();
for(int i = 0; i < block_offsetsuml.Size(); i++)
{
block_offsetsuml[i] = block_offsetsumlz[i];
}
for(int i = 0; i < block_offsetsx.Size(); i++)
{
block_offsetsx[i] = block_offsetsuml[i] ;
}
ml = problem->Getml();
lk.SetSize(dimC); lk = 0.0;
zlk.SetSize(dimM); zlk = 0.0;
linSolver = 0;
linSolveTol = 1.e-8;
MyRank = Mpi::WorldRank();
iAmRoot = MyRank == 0 ? true : false;
}
double ParInteriorPointSolver::MaxStepSize(Vector &x, Vector &xl, Vector &xhat, double tau)
{
double alphaMaxloc = 1.0;
double alphaTmp;
for(int i = 0; i < x.Size(); i++)
{
if( xhat(i) < 0. )
{
alphaTmp = -1. * tau * (x(i) - xl(i)) / xhat(i);
alphaMaxloc = min(alphaMaxloc, alphaTmp);
}
}
// alphaMaxloc is the local maximum step size which is
// distinct on each MPI process. Need to compute
// the global maximum step size
double alphaMaxglb;
MPI_Allreduce(&alphaMaxloc, &alphaMaxglb, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
return alphaMaxglb;
}
double ParInteriorPointSolver::MaxStepSize(Vector &x, Vector &xhat, double tau)
{
Vector zero(x.Size()); zero = 0.0;
return MaxStepSize(x, zero, xhat, tau);
}
void ParInteriorPointSolver::Mult(const Vector &x0, Vector &xf)
{
BlockVector x0block(block_offsetsx); x0block = 0.0;
x0block.GetBlock(0).Set(1.0, x0);
x0block.GetBlock(1) = 1.0;
x0block.GetBlock(1).Add(1.0, ml);
BlockVector xfblock(block_offsetsx); xfblock = 0.0;
Mult(x0block, xfblock);
xf.Set(1.0, xfblock.GetBlock(0));
}
void ParInteriorPointSolver::Mult(const BlockVector &x0, BlockVector &xf)
{
converged = false;
BlockVector xk(block_offsetsx), xhat(block_offsetsx); xk = 0; xhat = 0.0;
BlockVector Xk(block_offsetsumlz), Xhat(block_offsetsumlz); Xk = 0.0; Xhat = 0.0;
BlockVector Xhatuml(block_offsetsuml); Xhatuml = 0.0;
Vector zlhat(dimM); zlhat = 0.0;
xk.GetBlock(0).Set(1.0, x0.GetBlock(0));
xk.GetBlock(1).Set(1.0, x0.GetBlock(1));
// running estimate of the final values of the Lagrange multipliers
lk = 0.0;
zlk = 0.0;
for(int i = 0; i < dimM; i++)
{
zlk(i) = 1.e1 * mu_k / (xk(i+dimU) - ml(i));
}
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
Xk.GetBlock(2).Set(1.0, lk);
Xk.GetBlock(3).Set(1.0, zlk);
/* set theta0 = theta(x0)
* thetaMin
* thetaMax
* when theta(xk) < thetaMin and the switching condition holds
* then we ask for the Armijo sufficient decrease of the barrier
* objective to be satisfied, in order to accept the trial step length alphakl
*
* thetaMax controls how the filter is initialized for each log-barrier subproblem
* F0 = {(th, phi) s.t. th > thetaMax}
* that is the filter does not allow for iterates where the constraint violation
* is larger than that of thetaMax
*/
double theta0 = theta(xk);
thetaMin = 1.e-4 * max(1.0, theta0);
thetaMax = 1.e8 * thetaMin; // 1.e4 * max(1.0, theta0)
double Eeval, maxBarrierSolves, Eevalmu0;
bool printOptimalityError; // control optimality error print to console for log-barrier subproblems
maxBarrierSolves = 10;
for(jOpt = 0; jOpt < max_iter; jOpt++)
{
if(iAmRoot)
{
cout << "interior-point solve step " << jOpt << endl;
}
// A-2. Check convergence of overall optimization problem
printOptimalityError = false;
Eevalmu0 = E(xk, lk, zlk, printOptimalityError);
if(Eevalmu0 < OptTol)
{
converged = true;
if(iAmRoot)
{
cout << "solved optimization problem :)\n";
}
break;
}
if(jOpt > 0) { maxBarrierSolves = 1; }
for(int i = 0; i < maxBarrierSolves; i++)
{
// A-3. Check convergence of the barrier subproblem
printOptimalityError = true;
Eeval = E(xk, lk, zlk, mu_k, printOptimalityError);
if(iAmRoot)
{
cout << "E = " << Eeval << endl;
}
if(Eeval < kEps * mu_k)
{
if(iAmRoot)
{
cout << "solved barrier subproblem :), for mu = " << mu_k << endl;
}
// A-3.1. Recompute the barrier parameter
mu_k = max(OptTol / 10., min(kMu * mu_k, pow(mu_k, thetaMu)));
// A-3.2. Re-initialize the filter
F1.DeleteAll();
F2.DeleteAll();
}
else
{
break;
}
}
// A-4. Compute the search direction
// solve for (uhat, mhat, lhat)
if(iAmRoot)
{
cout << "\n** A-4. IP-Newton solve **\n";
}
zlhat = 0.0; Xhatuml = 0.0;
// why do we have Xhatuml ....???
// TO DO: remove Xhatuml in favor of passing Xhat
IPNewtonSolve(xk, lk, zlk, zlhat, Xhatuml, mu_k, false);
// assign data stack, X = (u, m, l, zl)
Xk = 0.0;
Xk.GetBlock(0).Set(1.0, xk.GetBlock(0));
Xk.GetBlock(1).Set(1.0, xk.GetBlock(1));
Xk.GetBlock(2).Set(1.0, lk);
Xk.GetBlock(3).Set(1.0, zlk);
// assign data stack, Xhat = (uhat, mhat, lhat, zlhat)
Xhat = 0.0;
for(int i = 0; i < 3; i++)
{
Xhat.GetBlock(i).Set(1.0, Xhatuml.GetBlock(i));
}
Xhat.GetBlock(3).Set(1.0, zlhat);
// A-5. Backtracking line search.
if(iAmRoot)
{
cout << "\n** A-5. Linesearch **\n";
cout << "mu = " << mu_k << endl;
}
lineSearch(Xk, Xhat, mu_k);
if(lineSearchSuccess)
{
if(iAmRoot)
{
cout << "lineSearch successful :)\n";
}
if(!switchCondition || !sufficientDecrease)
{
F1.Append( (1. - gTheta) * thx0);
F2.Append( phx0 - gPhi * thx0);
}
// ----- A-6: Accept the trial point
// print info regarding zl...
xk.GetBlock(0).Add(alpha, Xhat.GetBlock(0));
xk.GetBlock(1).Add(alpha, Xhat.GetBlock(1));
lk.Add(alpha, Xhat.GetBlock(2));
zlk.Add(alphaz, Xhat.GetBlock(3));
projectZ(xk, zlk, mu_k);
}
else
{
if(iAmRoot)
{
cout << "lineSearch not successful :(\n";
cout << "attempting feasibility restoration with theta = " << thx0 << endl;
cout << "no feasibility restoration implemented, exiting now \n";
}
break;
}
if(jOpt + 1 == max_iter && iAmRoot)
{
cout << "maximum optimization iterations :(\n";
}
}
// done with optimization routine, just reassign data to xf reference so
// that the application code has access to the optimal point
xf = 0.0;
xf.GetBlock(0).Set(1.0, xk.GetBlock(0));
xf.GetBlock(1).Set(1.0, xk.GetBlock(1));
}
void ParInteriorPointSolver::FormIPNewtonMat(BlockVector & x, Vector & l, Vector &zl,
BlockOperator &Ak)
{
// WARNING: Huu, Hum, Hmu, Hmm should all be Hessian terms of the Lagrangian, currently we
// them by Hessian terms of the objective function and neglect the Hessian of l^T c
Huu = problem->Duuf(x);
Hum = problem->Dumf(x);
Hmu = problem->Dmuf(x);
Hmm = problem->Dmmf(x);
Vector DiagLogBar(dimM); DiagLogBar = 0.0;
for(int ii = 0; ii < dimM; ii++)
{
DiagLogBar(ii) = zl(ii) / (x(ii+dimU) - ml(ii));
}
if(saveLogBarrierIterates)
{
std::ofstream diagStream;
char diagString[100];
snprintf(diagString, 100, "logBarrierHessiandata/D%d.dat", jOpt);
diagStream.open(diagString, ios::out | ios::trunc);
for(int ii = 0; ii < dimM; ii++)
{
diagStream << setprecision(30) << DiagLogBar(ii) << endl;
// mfem::out << DiagLogBar(ii) << endl;
}
diagStream.close();
}
int gsize = problem->GetGlobalNumConstraints();
int * rows = problem->GetConstraintsStarts();
delete Wmm;
if(Hmm != nullptr)
{
SparseMatrix * Ds = new SparseMatrix(DiagLogBar);
HypreParMatrix * D = new HypreParMatrix(problem->GetComm(), gsize, rows, Ds);
HypreStealOwnership(*D,*Ds);
delete Ds;
Wmm = ParAdd(Hmm,D);
delete D;
}
else
{
SparseMatrix * Ds = new SparseMatrix(DiagLogBar);
Wmm = new HypreParMatrix(problem->GetComm(), gsize, rows, Ds);
HypreStealOwnership(*Wmm,*Ds);
delete Ds;
}
delete JuT;
delete JmT;
Ju = problem->Duc(x); JuT = Ju->Transpose();
Jm = problem->Dmc(x); JmT = Jm->Transpose();
// IP-Newton system matrix
// Ak = [[H_(u,u) H_(u,m) J_u^T]
// [H_(m,u) W_(m,m) J_m^T]
// [ J_u J_m 0 ]]
Ak.SetBlock(0, 0, Huu); Ak.SetBlock(0, 2, JuT);
Ak.SetBlock(1, 1, Wmm); Ak.SetBlock(1, 2, JmT);
Ak.SetBlock(2, 0, Ju); Ak.SetBlock(2, 1, Jm);
if(Hum != nullptr) { Ak.SetBlock(0, 1, Hum); Ak.SetBlock(1, 0, Hmu); }
}
// perturbed KKT system solve
// determine the search direction
void ParInteriorPointSolver::IPNewtonSolve(BlockVector &x, Vector &l, Vector &zl, Vector &zlhat, BlockVector &Xhat, double mu, bool socSolve)
{
// solve A x = b, where A is the IP-Newton matrix
BlockOperator A(block_offsetsuml, block_offsetsuml);
BlockVector b(block_offsetsuml); b = 0.0;
FormIPNewtonMat(x, l, zl, A);
// [grad_u phi + Ju^T l]
// b = - [grad_m phi + Jm^T l]
// [ c ]
BlockVector gradphi(block_offsetsx); gradphi = 0.0;
BlockVector JTl(block_offsetsx); JTl = 0.0;
Dxphi(x, mu, gradphi);
(A.GetBlock(0,2)).Mult(l, JTl.GetBlock(0));
(A.GetBlock(1,2)).Mult(l, JTl.GetBlock(1));
for(int ii = 0; ii < 2; ii++)
{
b.GetBlock(ii).Set(1.0, gradphi.GetBlock(ii));
b.GetBlock(ii).Add(1.0, JTl.GetBlock(ii));
}
if(!socSolve)
{
problem->c(x, b.GetBlock(2));
}
else
{
b.GetBlock(2).Set(1.0, ckSoc);
}
b *= -1.0;
Xhat = 0.0;
// Direct solver (default)
if(linSolver == 0)
{
Array2D<HypreParMatrix *> ABlockMatrix(3,3);
for(int ii = 0; ii < 3; ii++)
{
for(int jj = 0; jj < 3; jj++)
{
if(!A.IsZeroBlock(ii, jj))
{
ABlockMatrix(ii, jj) = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(ii, jj)));
}
else
{
ABlockMatrix(ii, jj) = nullptr;
}
}
}
HypreParMatrix * Ah = HypreParMatrixFromBlocks(ABlockMatrix);
/* direct solve of the 3x3 IP-Newton linear system */
#ifdef MFEM_USE_MUMPS
MUMPSSolver ASolver(*Ah);;
ASolver.SetPrintLevel(0);
ASolver.SetMatrixSymType(MUMPSSolver::MatType::UNSYMMETRIC);
ASolver.Mult(b, Xhat);
#else
#ifdef MFEM_USE_MKL_CPARDISO
CPardisoSolver ASolver(MPI_COMM_WORLD);
ASolver.SetOperator(*Ah);
ASolver.Mult(b, Xhat);
#else
MFEM_VERIFY(false, "linSolver 0 will not work unless compiled with MUMPS or MKL");
#endif
#endif
delete Ah;
}
else if(linSolver == 1 || linSolver == 2)
{
// form A = Huu + Ju^T D Ju, Wmm = D for contact
HypreParMatrix * Wmmloc = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(1, 1)));
HypreParMatrix * Huuloc = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(0, 0)));
HypreParMatrix * Juloc = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(2, 0)));
HypreParMatrix * JuTloc = dynamic_cast<HypreParMatrix *>(&(A.GetBlock(0, 2)));
HypreParMatrix *JuTDJu = RAP(Wmmloc, Juloc); // Ju^T D Ju
HypreParMatrix *Areduced = ParAdd(Huuloc, JuTDJu); // Huu + Ju^T D Ju
Areduced->DropSmallEntries(1e-16);
/* prepare the reduced rhs */
// breduced = bu + Ju^T (bm + Wmm bl)
Vector breduced(dimU); breduced = 0.0;
Vector tempVec(dimM); tempVec = 0.0;
Wmmloc->Mult(b.GetBlock(2), tempVec);
tempVec.Add(1.0, b.GetBlock(1));
JuTloc->Mult(tempVec, breduced);
breduced.Add(1.0, b.GetBlock(0));
if(linSolver == 1)
{
// setup the solver for the reduced linear system
#ifdef MFEM_USE_MUMPS
MUMPSSolver AreducedSolver(*Areduced);
AreducedSolver.SetPrintLevel(0);
AreducedSolver.SetMatrixSymType(MUMPSSolver::MatType::SYMMETRIC_INDEFINITE);
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
#else
#ifdef MFEM_USE_MKL_CPARDISO
CPardisoSolver AreducedSolver(MPI_COMM_WORLD);
AreducedSolver.SetOperator(*Areduced);
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
#else
MFEM_VERIFY(false, "linSolver 1 will not work unless compiled with MUMPS or MKL");
#endif
#endif
}
else
{
HypreBoomerAMG amg(*Areduced);
amg.SetPrintLevel(0);
if (pfes)
{
amg.SetElasticityOptions(pfes);
}
else
{
amg.SetSystemsOptions(3,false);
}
amg.SetRelaxType(relax_type);
int n;
// CGSolver AreducedSolver(MPI_COMM_WORLD);
// AreducedSolver.SetOperator(*Areduced);
// AreducedSolver.SetRelTol(linSolveTol);
// AreducedSolver.SetMaxIter(1000);
// AreducedSolver.SetPreconditioner(amg);
// AreducedSolver.SetPrintLevel(3);
// AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
// n = AreducedSolver.GetNumIterations();
HyprePCG AreducedSolver(*Areduced);
AreducedSolver.SetTol(linSolveTol);
AreducedSolver.SetMaxIter(1000);
AreducedSolver.SetPreconditioner(amg);
AreducedSolver.SetPrintLevel(2);
// AreducedSolver.SetResidualConvergenceOptions();
AreducedSolver.Mult(breduced, Xhat.GetBlock(0));
AreducedSolver.GetNumIterations(n);
cgnum_iterations.Append(n);
}
// now propagate solved uhat to obtain mhat and lhat
// xm = Ju xu - bl
Juloc->Mult(Xhat.GetBlock(0), Xhat.GetBlock(1));
Xhat.GetBlock(1).Add(-1.0, b.GetBlock(2));
// xl = Wmm xm - bm
Wmmloc->Mult(Xhat.GetBlock(1), Xhat.GetBlock(2));
Xhat.GetBlock(2).Add(-1.0, b.GetBlock(1));
delete JuTDJu;
delete Areduced;
}
/* backsolve to determine zlhat */
for(int ii = 0; ii < dimM; ii++)
{
zlhat(ii) = -1.*(zl(ii) + (zl(ii) * Xhat(ii + dimU) - mu) / (x(ii + dimU) - ml(ii)) );
}
}
// here Xhat, X will be BlockVectors w.r.t. the 4 partitioning X = (u, m, l, zl)
void ParInteriorPointSolver::lineSearch(BlockVector& X0, BlockVector& Xhat, double mu)
{
double tau = max(tauMin, 1.0 - mu);
Vector u0 = X0.GetBlock(0);
Vector m0 = X0.GetBlock(1);
Vector l0 = X0.GetBlock(2);
Vector z0 = X0.GetBlock(3);
Vector uhat = Xhat.GetBlock(0);
Vector mhat = Xhat.GetBlock(1);
Vector lhat = Xhat.GetBlock(2);
Vector zhat = Xhat.GetBlock(3);
double alphaMax = MaxStepSize(m0, ml, mhat, tau);
double alphaMaxz = MaxStepSize(z0, zhat, tau);
alphaz = alphaMaxz;
BlockVector x0(block_offsetsx); x0 = 0.0;
x0.GetBlock(0).Set(1.0, u0);
x0.GetBlock(1).Set(1.0, m0);
BlockVector xhat(block_offsetsx); xhat = 0.0;
xhat.GetBlock(0).Set(1.0, uhat);
xhat.GetBlock(1).Set(1.0, mhat);
BlockVector xtrial(block_offsetsx); xtrial = 0.0;
BlockVector Dxphi0(block_offsetsx); Dxphi0 = 0.0;
int maxBacktrack = 20;
alpha = alphaMax;
Vector ck0(dimC); ck0 = 0.0;
Vector zhatsoc(dimM); zhatsoc = 0.0;
BlockVector Xhatumlsoc(block_offsetsuml); Xhatumlsoc = 0.0;
BlockVector xhatsoc(block_offsetsx); xhatsoc = 0.0;
Vector uhatsoc(dimU); uhatsoc = 0.0;
Vector mhatsoc(dimM); mhatsoc = 0.0;
Dxphi(x0, mu, Dxphi0);
Dxphi0_xhat = InnerProduct(MPI_COMM_WORLD, Dxphi0, xhat);
descentDirection = Dxphi0_xhat < 0. ? true : false;
if (iAmRoot)
{
if(descentDirection)
{
cout << "is a descent direction for the log-barrier objective\n";
}
else
{
cout << "is not a descent direction for the log-barrier objective\n";
}
}
thx0 = theta(x0);
phx0 = phi(x0, mu);
lineSearchSuccess = false;
for(int i = 0; i < maxBacktrack; i++)
{
if (iAmRoot)
{
cout << "\n--------- alpha = " << alpha << " ---------\n";
}
// ----- A-5.2. Compute trial point: xtrial = x0 + alpha_i xhat
xtrial.Set(1.0, x0);
xtrial.Add(alpha, xhat);
// ------ A-5.3. if not in filter region go to A.5.4 otherwise go to A-5.5.
thxtrial = theta(xtrial);
phxtrial = phi(xtrial, mu);
filterCheck(thxtrial, phxtrial);
if(!inFilterRegion)
{
if (iAmRoot)
{
cout << "not in filter region :)\n";
}
// ------ A.5.4: Check sufficient decrease
if(!descentDirection)
{
switchCondition = false;
}
else
{
switchCondition = (alpha * pow(abs(Dxphi0_xhat), sPhi) > delta * pow(thx0, sTheta)) ? true : false;
}
if (iAmRoot)
{
cout << "theta(x0) = " << thx0 << ", thetaMin = " << thetaMin << endl;
cout << "theta(xtrial) = " << thxtrial << ", (1-gTheta) *theta(x0) = " << (1. - gTheta) * thx0 << endl;
cout << "phi(xtrial) = " << phxtrial << ", phi(x0) - gPhi *theta(x0) = " << phx0 - gPhi * thx0 << endl;
}
// Case I
if(thx0 <= thetaMin && switchCondition)
{
sufficientDecrease = (phxtrial <= phx0 + eta * alpha * Dxphi0_xhat) ? true : false;
if(sufficientDecrease)
{
if(iAmRoot) { cout << "Line search successful: sufficient decrease in log-barrier objective.\n"; }
// accept the trial step
lineSearchSuccess = true;
break;
}
}
else
{
if(thxtrial <= (1. - gTheta) * thx0 || phxtrial <= phx0 - gPhi * thx0)
{
if(iAmRoot) { cout << "Line search successful: infeasibility or log-barrier objective decreased.\n"; }
// accept the trial step
lineSearchSuccess = true;
break;
}
}
// A-5.5: Initialize the second-order correction
if((!(thx0 < thxtrial)) && i == 0)
{
if (iAmRoot)
{
cout << "second order correction\n";
}
problem->c(xtrial, ckSoc);
problem->c(x0, ck0);
ckSoc.Add(alphaMax, ck0);
// A-5.6 Compute the second-order correction.
IPNewtonSolve(x0, l0, z0, zhatsoc, Xhatumlsoc, mu, true);
mhatsoc.Set(1.0, Xhatumlsoc.GetBlock(1));
//WARNING: not complete but currently solver isn't entering this region
}
}
else
{
if (iAmRoot)
{
cout << "in filter region :(\n";
}
}
// include more if needed
alpha *= 0.5;
}
}
void ParInteriorPointSolver::projectZ(const Vector &x, Vector &z, double mu)
{
double zi;
double mudivmml;
for(int i = 0; i < dimM; i++)
{
zi = z(i);
mudivmml = mu / (x(i + dimU) - ml(i));
z(i) = max(min(zi, kSig * mudivmml), mudivmml / kSig);
}
}
void ParInteriorPointSolver::filterCheck(double th, double ph)
{
inFilterRegion = false;
if(th > thetaMax)
{
inFilterRegion = true;
}
else
{
for(int i = 0; i < F1.Size(); i++)
{
if(th >= F1[i] && ph >= F2[i])
{
inFilterRegion = true;
break;
}
}
}
}
double ParInteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, double mu, bool printEeval)
{
double E1, E2, E3;
double sc, sd;
BlockVector gradL(block_offsetsx); gradL = 0.0; // stationarity grad L = grad f + J^T l - z
Vector cx(dimC); cx = 0.0; // feasibility c = c(x)
Vector comp(dimM); comp = 0.0; // complementarity M Z - mu 1
DxL(x, l, zl, gradL);
E1 = GlobalLpNorm(infinity(), gradL.Normlinf(), MPI_COMM_WORLD);
problem->c(x, cx);
E2 = GlobalLpNorm(infinity(), cx.Normlinf(), MPI_COMM_WORLD);
for(int ii = 0; ii < dimM; ii++)
{
comp(ii) = x(dimU + ii) * zl(ii) - mu;
}
E3 = GlobalLpNorm(infinity(), comp.Normlinf(), MPI_COMM_WORLD);
double ll1, zl1;
zl1 = GlobalLpNorm(1, zl.Norml1(), MPI_COMM_WORLD)/ double(gdimC + gdimM);;
ll1 = GlobalLpNorm(1, l.Norml1(), MPI_COMM_WORLD);
sc = max(sMax, zl1 / (double(gdimM)) ) / sMax;
sd = max(sMax, (ll1 + zl1) / (double(gdimC + gdimM))) / sMax;
if(iAmRoot && printEeval)
{
cout << "evaluating optimality error for mu = " << mu << endl;
cout << "stationarity measure = " << E1 / sd << endl;
cout << "feasibility measure = " << E2 << endl;
cout << "complimentarity measure = " << E3 / sc << endl;
}
return max(max(E1 / sd, E2), E3 / sc);
}
double ParInteriorPointSolver::E(const BlockVector &x, const Vector &l, const Vector &zl, bool printEeval)
{
return E(x, l, zl, 0.0, printEeval);
}
double ParInteriorPointSolver::theta(const BlockVector &x)
{
Vector cx(dimC);
problem->c(x, cx);
return sqrt(InnerProduct(MPI_COMM_WORLD,cx, cx));
}
// log-barrier objective
double ParInteriorPointSolver::phi(const BlockVector &x, double mu)
{
double fx = problem->CalcObjective(x);
double logBarrierLoc = 0.0;
for(int i = 0; i < dimM; i++)
{
logBarrierLoc += log(x(dimU+i)-ml(i));
}
double logBarrierGlb;
MPI_Allreduce(&logBarrierLoc, &logBarrierGlb, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
return fx - mu * logBarrierGlb;
}
// gradient of log-barrier objective with respect to x = (u, m)
void ParInteriorPointSolver::Dxphi(const BlockVector &x, double mu, BlockVector &y)
{
problem->CalcObjectiveGrad(x, y);
for(int i = 0; i < dimM; i++)
{
y(dimU + i) -= mu / (x(dimU + i));
}
}
// Lagrangian function evaluation
// L(x, l, zl) = f(x) + l^T c(x) - zl^T m
double ParInteriorPointSolver::L(const BlockVector &x, const Vector &l, const Vector &zl)
{
double fx = problem->CalcObjective(x);
Vector cx(dimC); problem->c(x, cx);
return (fx + InnerProduct(MPI_COMM_WORLD,cx, l) - InnerProduct(MPI_COMM_WORLD, x.GetBlock(1), zl));
}
void ParInteriorPointSolver::DxL(const BlockVector &x, const Vector &l, const Vector &zl, BlockVector &y)
{
// evaluate the gradient of the objective with respect to the primal variables x = (u, m)
BlockVector gradxf(block_offsetsx); gradxf = 0.0;
problem->CalcObjectiveGrad(x, gradxf);
HypreParMatrix *Jacu, *Jacm, *JacuT, *JacmT;
Jacu = problem->Duc(x);
Jacm = problem->Dmc(x);
JacuT = Jacu->Transpose();
JacmT = Jacm->Transpose();
JacuT->Mult(l, y.GetBlock(0));
JacmT->Mult(l, y.GetBlock(1));
delete JacuT;
delete JacmT;
y.Add(1.0, gradxf);
(y.GetBlock(1)).Add(-1.0, zl);
}
bool ParInteriorPointSolver::GetConverged() const
{
return converged;
}
void ParInteriorPointSolver::SetTol(double Tol)
{
OptTol = Tol;
}
void ParInteriorPointSolver::SetMaxIter(int max_it)
{
max_iter = max_it;
}
void ParInteriorPointSolver::SetBarrierParameter(double mu_0)
{
mu_k = mu_0;
}
void ParInteriorPointSolver::SaveLogBarrierHessianIterates(bool save)
{
MFEM_ASSERT(MyRank == 0 || save == false, "currently can only save logbarrier hessian in serial codes");
saveLogBarrierIterates = save;
}
void ParInteriorPointSolver::SetLinearSolver(int LinSolver)
{
linSolver = LinSolver;
}
void ParInteriorPointSolver::SetLinearSolveTol(double Tol)
{
linSolveTol = Tol;
}
void ParInteriorPointSolver::SetLinearSolveRelaxType(int relax_type_)
{
relax_type = relax_type_;
}
ParInteriorPointSolver::~ParInteriorPointSolver()
{
delete JuT;
delete JmT;
delete Wmm;
}
-100
View File
@@ -1,100 +0,0 @@
#include "mfem.hpp"
#include "../problems/parproblems.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
#ifndef PARIPSOLVER
#define PARIPSOLVER
class ParInteriorPointSolver
{
protected:
QPOptParContactProblem* problem;
double OptTol;
int max_iter;
double mu_k; // \mu_k
Vector lk, zlk;
double sMax, kSig, tauMin, eta, thetaMin, delta, sTheta, sPhi, kMu, thetaMu;
double thetaMax, kSoc, gTheta, gPhi, kEps;
// filter
Array<double> F1, F2;
// quantities computed in lineSearch
double alpha, alphaz;
double thx0, thxtrial;
double phx0, phxtrial;
bool descentDirection, switchCondition, sufficientDecrease, lineSearchSuccess, inFilterRegion;
double Dxphi0_xhat;
int dimU, dimM, dimC;
int gdimU, gdimM, gdimC;
Array<int> block_offsetsumlz, block_offsetsuml, block_offsetsx;
Vector ml;
Vector ckSoc;
HypreParMatrix * Huu = nullptr;
HypreParMatrix * Hum = nullptr;
HypreParMatrix * Hmu = nullptr;
HypreParMatrix * Hmm = nullptr;
HypreParMatrix * Wmm = nullptr;
HypreParMatrix * Ju = nullptr;
HypreParMatrix * Jm = nullptr;
HypreParMatrix * JuT = nullptr;
HypreParMatrix * JmT = nullptr;
Array<int> cgnum_iterations;
ParFiniteElementSpace *pfes = nullptr;
int jOpt;
bool converged;
int MyRank;
bool iAmRoot;
bool saveLogBarrierIterates = false;
int linSolver;
double linSolveTol;
int relax_type = 8;
public:
ParInteriorPointSolver(QPOptParContactProblem*);
double MaxStepSize(Vector& , Vector& , Vector& , double);
double MaxStepSize(Vector& , Vector& , double);
void Mult(const BlockVector& , BlockVector&);
void Mult(const Vector&, Vector &);
void FormIPNewtonMat(BlockVector& , Vector& , Vector& , BlockOperator &);
void IPNewtonSolve(BlockVector& , Vector& , Vector& , Vector&, BlockVector& , double, bool);
void lineSearch(BlockVector& , BlockVector& , double);
void projectZ(const Vector & , Vector &, double);
void filterCheck(double, double);
double E(const BlockVector &, const Vector &, const Vector &, double, bool);
double E(const BlockVector &, const Vector &, const Vector &, bool);
bool GetConverged() const;
Array<int> & GetCGIterNumbers() {return cgnum_iterations;}
// TO DO: include Hessian of Lagrangian
double theta(const BlockVector &);
double phi(const BlockVector &, double);
void Dxphi(const BlockVector &, double, BlockVector &);
double L(const BlockVector &, const Vector &, const Vector &);
void DxL(const BlockVector &, const Vector &, const Vector &, BlockVector &);
void SetTol(double);
void SetMaxIter(int);
void SetBarrierParameter(double);
void SaveLogBarrierHessianIterates(bool);
void SetLinearSolver(int);
void SetLinearSolveTol(double);
void SetLinearSolveRelaxType(int);
void SetFiniteElementSpace(ParFiniteElementSpace * pfes_)
{
pfes = pfes_;
}
virtual ~ParInteriorPointSolver();
};
#endif
-110
View File
@@ -1,110 +0,0 @@
# Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/contact/,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
# Include defaults.mk to get XLINKER
DEFAULTS_MK = $(MFEM_DIR)/config/defaults.mk
include $(DEFAULTS_MK)
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
CONTACT_SEQ_SRC = problems/problems.cpp problems/problems_util.cpp util/util.cpp ipsolver/IPsolver.cpp
CONTACT_SEC_OBJ = $(CONTACT_PAR_SRC:.cpp=.o)
CONTACT_PAR_SRC = $(CONTACT_SEQ_SRC) ipsolver/ParIPsolver.cpp problems/parproblems.cpp problems/parproblems_util.cpp util/mpicomm.cpp
CONTACT_PAR_OBJ = $(CONTACT_PAR_SRC:.cpp=.o)
CONTACT_SRC = contact_driver.cpp $(CONTACT_SEQ_SRC)
CONTACT_OBJ = $(CONTACT_SRC:.cpp=.o)
PCONTACT_SRC = pcontact_driver.cpp $(CONTACT_PAR_SRC)
PCONTACT_OBJ = $(PCONTACT_SRC:.cpp=.o)
SEQ_MINIAPPS = contact_driver
PAR_MINIAPPS = pcontact_driver
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
else
MINIAPPS = $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
endif
COMMON_LIB = -L$(MFEM_BUILD_DIR)/miniapps/common -lmfem-common
# If MFEM_SHARED is set, add the ../common rpath
COMMON_LIB += $(if $(MFEM_SHARED:YES=),,\
$(if $(MFEM_USE_CUDA:YES=),$(CXX_XLINKER),$(CUDA_XLINKER))-rpath,$(abspath\
$(MFEM_BUILD_DIR)/miniapps/common))
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all lib-common clean clean-build clean-exec
# Remove built-in rule
%: %.cpp
%.o: %.cpp
%.o: $(SRC)%.cpp $(wildcard $(SRC)%.hpp) $(MFEM_LIB_FILE)\
$(CONFIG_MK) | lib-common
$(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@
util/%.o: $(SRC)util/%.cpp $(wildcard $(SRC)util/%.hpp) $(MFEM_LIB_FILE)\
$(CONFIG_MK) | lib-common
mkdir -p $(@D)
$(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@
problems/%.o: $(SRC)problems/%.cpp $(wildcard $(SRC)problems/%.hpp) $(MFEM_LIB_FILE)\
$(CONFIG_MK) | lib-common
mkdir -p $(@D)
$(MFEM_CXX) $(MFEM_FLAGS) -c $< -o $@
all: $(MINIAPPS)
contact_driver: $(CONTACT_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(CONTACT_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
pcontact_driver: $(PCONTACT_OBJ)
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $(PCONTACT_OBJ) $(COMMON_LIB) $(MFEM_LIBS)
# Rule for building lib-common
lib-common:
$(MAKE) -C $(MFEM_BUILD_DIR)/miniapps/common
MFEM_TESTS = MINIAPPS
include $(MFEM_TEST_MK)
# Testing: Specific execution options
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
contact-test-seq: diffusion
@$(call mfem-test,$<,, contact miniapp,)
pcontact-test-par: pcontact
@$(call mfem-test,$<, $(RUN_MPI), pcontact miniapp,)
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
rm -f $(CONTACT_OBJ) $(PCONTACT_OBJ)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -rf ParaView
-103
View File
@@ -1,103 +0,0 @@
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
3
elements
9
1 5 0 1 3 2 8 9 11 10
1 5 2 3 5 4 10 11 13 12
1 5 4 5 7 6 12 13 15 14
1 5 8 9 11 10 16 17 19 18
1 5 10 11 13 12 18 19 21 20
1 5 12 13 15 14 20 21 23 22
1 5 16 17 19 18 24 25 27 26
1 5 18 19 21 20 26 27 29 28
1 5 20 21 23 22 28 29 31 30
# 0 nothing
# 1 dirichlet bc
# 2 contact
boundary
30
1 3 1 0 2 3
1 3 3 2 4 5
1 3 5 4 6 7
1 3 24 25 27 26
1 3 26 27 29 28
1 3 28 29 31 30
2 3 2 0 8 10
2 3 4 2 10 12
2 3 6 4 12 14
2 3 10 8 16 18
2 3 12 10 18 20
2 3 14 12 20 22
2 3 18 16 24 26
2 3 20 18 26 28
2 3 22 20 28 30
3 3 1 3 11 9
3 3 3 5 13 11
3 3 5 7 15 13
3 3 9 11 19 17
3 3 11 13 21 19
3 3 13 15 23 21
3 3 17 19 27 25
3 3 19 21 29 27
3 3 21 23 31 29
1 3 8 0 1 9
1 3 16 8 9 17
1 3 24 16 17 25
1 3 6 14 15 7
1 3 14 22 23 15
1 3 22 30 31 23
vertices
32
3
-1.0000 0 0
0 0 0
-1.0000 0.3000 0
0 0.3000 0
-1.0000 0.6500 0
0 0.6500 0
-1.0000 1.0000 0
0 1.0000 0
-1.0000 0 0.3000
0 0 0.3000
-1.0000 0.3000 0.3500
0 0.3000 0.3500
-1.0000 0.6500 0.3000
0 0.6500 0.3000
-1.0000 1.0000 0.3000
0 1.0000 0.3000
-1.0000 0 0.6500
0 0 0.6500
-1.0000 0.3000 0.6500
0 0.3000 0.6500
-1.0000 0.6500 0.6500
0 0.6500 0.6500
-1.0000 1.0000 0.6500
0 1.0000 0.6500
-1.0000 0 1.0000
0 0 1.0000
-1.0000 0.3000 1.0000
0 0.3000 1.0000
-1.0000 0.6500 1.0000
0 0.6500 1.0000
-1.0000 1.0000 1.0000
0 1.0000 1.0000
-68
View File
@@ -1,68 +0,0 @@
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
3
# 1 nothing
elements
4
1 5 0 1 3 2 6 7 9 8
1 5 2 3 5 4 8 9 11 10
1 5 6 7 9 8 12 13 15 14
1 5 8 9 11 10 14 15 17 16
# 0 nothing
# 1 dirichlet bc
# 2 contact
boundary
16
1 3 1 0 2 3
1 3 3 2 4 5
1 3 12 13 15 14
1 3 14 15 17 16
3 3 2 0 6 8
3 3 4 2 8 10
3 3 8 6 12 14
3 3 10 8 14 16
2 3 1 3 9 7
2 3 3 5 11 9
2 3 7 9 15 13
2 3 9 11 17 15
1 3 6 0 1 7
1 3 12 6 7 13
1 3 4 10 11 5
1 3 10 16 17 11
vertices
18
3
0 0.2464 0.2464
0.5071 0.2464 0.2464
0 0.5000 0.2464
0.5071 0.5000 0.2464
0 0.7536 0.2464
0.5071 0.7536 0.2464
0 0.2464 0.5000
0.5071 0.2464 0.5000
0 0.5000 0.5000
0.5071 0.5000 0.5000
0 0.7536 0.5000
0.5071 0.7536 0.5000
0 0.2464 0.7536
0.5071 0.2464 0.7536
0 0.5000 0.7536
0.5071 0.5000 0.7536
0 0.7536 0.7536
0.5071 0.7536 0.7536
@@ -1,70 +0,0 @@
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
3
# 1 nothing
elements
4
1 5 0 1 3 2 6 7 9 8
1 5 2 3 5 4 8 9 11 10
1 5 6 7 9 8 12 13 15 14
1 5 8 9 11 10 14 15 17 16
# 0 nothing
# 1 dirichlet bc
# 2 contact
boundary
16
1 3 1 0 2 3
1 3 3 2 4 5
1 3 12 13 15 14
1 3 14 15 17 16
3 3 2 0 6 8
3 3 4 2 8 10
3 3 8 6 12 14
3 3 10 8 14 16
2 3 1 3 9 7
2 3 3 5 11 9
2 3 7 9 15 13
2 3 9 11 17 15
1 3 6 0 1 7
1 3 12 6 7 13
1 3 4 10 11 5
1 3 10 16 17 11
vertices
18
3
0.000000000000 0.145770950245 0.443895630208
0.507100000000 0.145770950245 0.443895630208
0.000000000000 0.350937660019 0.294833290227
0.507100000000 0.350937660019 0.294833290227
0.000000000000 0.556104369792 0.145770950245
0.507100000000 0.556104369792 0.145770950245
0.000000000000 0.294833290227 0.649062339981
0.507100000000 0.294833290227 0.649062339981
0.000000000000 0.500000000000 0.500000000000
0.507100000000 0.500000000000 0.500000000000
0.000000000000 0.705166709773 0.350937660019
0.507100000000 0.705166709773 0.350937660019
0.000000000000 0.443895630208 0.854229049755
0.507100000000 0.443895630208 0.854229049755
0.000000000000 0.649062339981 0.705166709773
0.507100000000 0.649062339981 0.705166709773
0.000000000000 0.854229049755 0.556104369792
0.507100000000 0.854229049755 0.556104369792
-254
View File
@@ -1,254 +0,0 @@
// Parallel contact example
//
// Compile with: make pcontact_driver
// sample run
// mpirun -np 6 ./pcontact_driver -sr 2 -pr 2
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "ipsolver/ParIPsolver.hpp"
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
Mpi::Init();
int myid = Mpi::WorldRank();
int num_procs = Mpi::WorldSize();
Hypre::Init();
// 1. Parse command-line options.
const char *mesh_file = "meshes/merged.mesh";
int order = 1;
int sref = 0;
int pref = 0;
Array<int> attr;
Array<int> m_attr;
bool visualization = true;
bool paraview = false;
double linsolvertol = 1e-6;
int relax_type = 8;
double optimizer_tol = 1e-6;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&attr, "-at", "--attributes-surf",
"Attributes of boundary faces on contact surface for mesh 2.");
args.AddOption(&sref, "-sr", "--serial-refinements",
"Number of uniform refinements.");
args.AddOption(&pref, "-pr", "--parallel-refinements",
"Number of uniform refinements.");
args.AddOption(&linsolvertol, "-stol", "--solver-tol",
"Linear Solver Tolerance.");
args.AddOption(&optimizer_tol, "-otol", "--optimizer-tol",
"Interior Point Solver Tolerance.");
args.AddOption(&relax_type, "-rt", "--relax-type",
"Selection of Smoother for AMG");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&paraview, "-paraview", "--paraview", "-no-paraview",
"--no-paraview",
"Enable or disable ParaView visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
Mesh * merged_mesh = new Mesh(mesh_file,1);
Array<int> attr1; attr1.Append(1);
Array<int> attr2; attr2.Append(2);
Mesh * mesh1 = new Mesh(SubMesh::CreateFromDomain(*merged_mesh,attr1));
Mesh * mesh2 = new Mesh(SubMesh::CreateFromDomain(*merged_mesh,attr2));
for (int i = 0; i<sref; i++)
{
mesh1->UniformRefinement();
mesh2->UniformRefinement();
}
for (int i = 0; i<mesh1->GetNE(); i++)
{
mesh1->SetAttribute(i,1);
}
mesh1->SetAttributes();
for (int i = 0; i<mesh2->GetNE(); i++)
{
mesh2->SetAttribute(i,2);
}
mesh2->SetAttributes();
ParMesh * pmesh1 = new ParMesh(MPI_COMM_WORLD,*mesh1);
ParMesh * pmesh2 = new ParMesh(MPI_COMM_WORLD,*mesh2);
for (int i = 0; i<pref; i++)
{
pmesh1->UniformRefinement();
pmesh2->UniformRefinement();
}
MFEM_VERIFY(pmesh1->GetNE(), "Empty partition mesh1");
MFEM_VERIFY(pmesh2->GetNE(), "Empty partition mesh2");
ParElasticityProblem * prob1 = new ParElasticityProblem(pmesh1,order);
ParElasticityProblem * prob2 = new ParElasticityProblem(pmesh2,order);
Vector lambda1(prob1->GetMesh()->attributes.Max()); lambda1 = 57.6923076923;
Vector mu1(prob1->GetMesh()->attributes.Max()); mu1 = 38.4615384615;
Vector lambda2(prob2->GetMesh()->attributes.Max()); lambda2 = 57.6923076923;
Vector mu2(prob2->GetMesh()->attributes.Max()); mu2 = 38.4615384615;
prob1->SetLambda(lambda1); prob1->SetMu(mu1);
prob2->SetLambda(lambda2); prob2->SetMu(mu2);
ParContactProblem contact(prob1,prob2);
QPOptParContactProblem qpopt(&contact);
int numconstr = contact.GetGlobalNumConstraints();
ParInteriorPointSolver optimizer(&qpopt);
optimizer.SetTol(optimizer_tol);
optimizer.SetMaxIter(50);
int linsolver = 2;
optimizer.SetLinearSolver(linsolver);
optimizer.SetLinearSolveTol(linsolvertol);
optimizer.SetLinearSolveRelaxType(relax_type);
ParGridFunction x1 = prob1->GetDisplacementGridFunction();
ParGridFunction x2 = prob2->GetDisplacementGridFunction();
int ndofs1 = prob1->GetNumTDofs();
int ndofs2 = prob2->GetNumTDofs();
int gndofs1 = prob1->GetGlobalNumDofs();
int gndofs2 = prob2->GetGlobalNumDofs();
int ndofs = ndofs1 + ndofs2;
Vector X1 = x1.GetTrueVector();
Vector X2 = x2.GetTrueVector();
Vector x0(ndofs); x0 = 0.0;
x0.SetVector(X1,0);
x0.SetVector(X2,X1.Size());
Vector xf(ndofs); xf = 0.0;
optimizer.Mult(x0, xf);
double Einitial = contact.E(x0);
double Efinal = contact.E(xf);
Array<int> & CGiterations = optimizer.GetCGIterNumbers();
if (Mpi::Root())
{
mfem::out << endl;
mfem::out << " Initial Energy objective = " << Einitial << endl;
mfem::out << " Final Energy objective = " << Efinal << endl;
mfem::out << " Global number of dofs = " << gndofs1 + gndofs2 << endl;
mfem::out << " Global number of constraints = " << numconstr << endl;
mfem::out << " CG iteration numbers = " ;
CGiterations.Print(mfem::out, CGiterations.Size());
}
MFEM_VERIFY(optimizer.GetConverged(),
"Interior point solver did not converge.");
if (visualization || paraview)
{
ParFiniteElementSpace * fes1 = prob1->GetFESpace();
ParFiniteElementSpace * fes2 = prob2->GetFESpace();
ParMesh * pmesh_1 = fes1->GetParMesh();
ParMesh * pmesh_2 = fes2->GetParMesh();
Vector X1_new(xf.GetData(),fes1->GetTrueVSize());
Vector X2_new(&xf.GetData()[fes1->GetTrueVSize()],fes2->GetTrueVSize());
ParGridFunction x1_gf(fes1);
ParGridFunction x2_gf(fes2);
x1_gf.SetFromTrueDofs(X1_new);
x2_gf.SetFromTrueDofs(X2_new);
pmesh_1->MoveNodes(x1_gf);
pmesh_2->MoveNodes(x2_gf);
if (paraview)
{
ParaViewDataCollection paraview_dc1("QPContactBody1", pmesh_1);
paraview_dc1.SetPrefixPath("ParaView");
paraview_dc1.SetLevelsOfDetail(1);
paraview_dc1.SetDataFormat(VTKFormat::BINARY);
paraview_dc1.SetHighOrderOutput(true);
paraview_dc1.SetCycle(0);
paraview_dc1.SetTime(0.0);
paraview_dc1.RegisterField("Body1", &x1_gf);
paraview_dc1.Save();
ParaViewDataCollection paraview_dc2("QPContactBody2", pmesh_2);
paraview_dc2.SetPrefixPath("ParaView");
paraview_dc2.SetLevelsOfDetail(1);
paraview_dc2.SetDataFormat(VTKFormat::BINARY);
paraview_dc2.SetHighOrderOutput(true);
paraview_dc2.SetCycle(0);
paraview_dc2.SetTime(0.0);
paraview_dc2.RegisterField("Body2", &x2_gf);
paraview_dc2.Save();
}
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
{
socketstream sol_sock1(vishost, visport);
sol_sock1.precision(8);
sol_sock1 << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pmesh_1 << x1_gf << flush;
}
{
socketstream sol_sock2(vishost, visport);
sol_sock2.precision(8);
sol_sock2 << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pmesh_2 << x2_gf << flush;
}
// {
// socketstream sol_sock(vishost, visport);
// sol_sock.precision(8);
// sol_sock << "parallel " << 2*num_procs << " " << myid << "\n"
// << "solution\n" << *pmesh_1 << x1_gf << flush;
// }
// {
// socketstream sol_sock(vishost, visport);
// sol_sock.precision(8);
// sol_sock << "parallel " << 2*num_procs << " " << myid+num_procs << "\n"
// << "solution\n" << *pmesh_2 << x2_gf << flush;
// }
}
}
delete prob2;
delete prob1;
delete pmesh2;
delete pmesh1;
// delete mesh1;
// delete mesh2;
return 0;
}
-516
View File
@@ -1,516 +0,0 @@
#include "parproblems.hpp"
void ParElasticityProblem::Init()
{
int dim = pmesh->Dimension();
fec = new H1_FECollection(order,dim);
fes = new ParFiniteElementSpace(pmesh,fec,dim,Ordering::byVDIM);
ndofs = fes->GetVSize();
ntdofs = fes->GetTrueVSize();
gndofs = fes->GlobalTrueVSize();
pmesh->SetNodalFESpace(fes);
if (pmesh->bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
}
ess_bdr = 0; ess_bdr[1] = 1;
fes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list);
// Solution GridFunction
x.SetSpace(fes); x = 0.0;
// RHS
b.Update(fes);
// Elasticity operator
lambda.SetSize(pmesh->attributes.Max()); lambda = 57.6923076923;
mu.SetSize(pmesh->attributes.Max()); mu = 38.4615384615;
lambda_cf.UpdateConstants(lambda);
mu_cf.UpdateConstants(mu);
a = new ParBilinearForm(fes);
a->AddDomainIntegrator(new ElasticityIntegrator(lambda_cf,mu_cf));
}
void ParElasticityProblem::FormLinearSystem()
{
if (!formsystem)
{
formsystem = true;
b.Assemble();
a->Assemble();
a->FormLinearSystem(ess_tdof_list, x, b, A, X, B);
}
}
void ParElasticityProblem::UpdateLinearSystem()
{
if (formsystem)
{
b.Update();
a->Update();
formsystem = false;
}
FormLinearSystem();
}
ParContactProblem::ParContactProblem(ParElasticityProblem * prob1_, ParElasticityProblem * prob2_)
: prob1(prob1_), prob2(prob2_)
{
ParMesh* pmesh1 = prob1->GetMesh();
comm = pmesh1->GetComm();
MPI_Comm_rank(comm, &myid);
MPI_Comm_size(comm, &numprocs);
dim = pmesh1->Dimension();
nodes0.SetSpace(pmesh1->GetNodes()->FESpace());
nodes0 = *pmesh1->GetNodes();
nodes1 = pmesh1->GetNodes();
Vector delta1(dim);
delta1 = 0.0; delta1[0] = 0.1;
prob1->SetDisplacementDirichletData(delta1);
prob1->FormLinearSystem();
Vector delta2(dim);
delta2 = 0.0;
prob2->SetDisplacementDirichletData(delta2);
prob2->FormLinearSystem();
int ndof1 = prob1->GetNumTDofs();
int ndof2 = prob2->GetNumTDofs();
tdof_offsets.SetSize(3);
tdof_offsets[0] = 0;
tdof_offsets[1] = ndof1;
tdof_offsets[2] = ndof2;
tdof_offsets.PartialSum();
Array2D<HypreParMatrix*> A(2,2);
A(0,0) = &prob1->GetOperator();
A(1,1) = &prob2->GetOperator();
A(1,0) = nullptr;
A(0,1) = nullptr;
K = HypreParMatrixFromBlocks(A);
B = new BlockVector(tdof_offsets);
B->GetBlock(0).Set(1.0, prob1->GetRHS());
B->GetBlock(1).Set(1.0, prob2->GetRHS());
ComputeContactVertices();
}
void ParContactProblem::ComputeContactVertices()
{
if (gnpoints>0) return;
ParMesh * pmesh1 = prob1->GetMesh();
ParMesh * pmesh2 = prob2->GetMesh();
dim = pmesh1->Dimension();
vfes1 = new ParFiniteElementSpace(pmesh1, prob1->GetFECol());
vfes2 = new ParFiniteElementSpace(pmesh2, prob2->GetFECol());
int gnv1 = vfes1->GlobalTrueVSize();
int gnv2 = vfes2->GlobalTrueVSize();
gnv = gnv1+gnv2;
int nv1 = vfes1->GetTrueVSize();
int nv2 = vfes2->GetTrueVSize();
nv = nv1+nv2;
vertices1.SetSize(pmesh1->GetNV());
vertices2.SetSize(pmesh2->GetNV());
for (int i = 0; i<pmesh1->GetNV(); i++)
{
vertices1[i] = i;
}
pmesh1->GetGlobalVertexIndices(vertices1);
for (int i = 0; i<pmesh2->GetNV(); i++)
{
vertices2[i] = i;
}
pmesh2->GetGlobalVertexIndices(vertices2);
int voffset2 = vfes2->GetMyTDofOffset();
std::vector<int> vertex2_offsets;
ComputeTdofOffsets(comm,voffset2, vertex2_offsets);
Array<int> vert;
for (int b=0; b<pmesh2->GetNBE(); b++)
{
if (pmesh2->GetBdrAttribute(b) == 3)
{
pmesh2->GetBdrElementVertices(b, vert);
for (auto v : vert)
{
if (myid != get_rank(vertices2[v],vertex2_offsets)) { continue; }
contact_vertices.insert(v);
}
}
}
npoints = contact_vertices.size();
MPI_Allreduce(&npoints, &gnpoints,1,MPI_INT,MPI_SUM,pmesh1->GetComm());
int constrains_offset;
MPI_Scan(&npoints,&constrains_offset,1,MPI_INT,MPI_SUM,pmesh1->GetComm());
constrains_offset-=npoints;
constraints_starts.SetSize(2);
constraints_starts[0] = constrains_offset;
constraints_starts[1] = constrains_offset+npoints;
ComputeTdofOffsets(comm,constrains_offset, constraints_offsets);
}
void ParContactProblem::ComputeGapFunctionAndDerivatives(const Vector & displ1, const Vector &displ2)
{
ComputeContactVertices();
ParMesh * pmesh1 = prob1->GetMesh();
ParMesh * pmesh2 = prob2->GetMesh();
ParGridFunction displ1_gf(prob1->GetFESpace());
ParGridFunction displ2_gf(prob2->GetFESpace());
displ1_gf.SetFromTrueDofs(displ1);
displ2_gf.SetFromTrueDofs(displ2);
Array<int> conn2(npoints);
Vector xyz(dim * npoints);
int cnt = 0;
for (auto v : contact_vertices)
{
for (int d = 0; d<dim; d++)
{
xyz(cnt*dim + d) = pmesh2->GetVertex(v)[d]+displ2_gf[v*dim+d];
}
conn2[cnt] = vertices2[v];
cnt++;
}
MFEM_VERIFY(cnt == npoints, "");
gapv.SetSize(npoints*dim); gapv = 0.0;
// segment reference coordinates of the closest point
Vector xi1(npoints*(dim-1));
Array<int> conn1(npoints*4);
DenseMatrix coordsm(npoints*4, dim);
// add(nodes0, displ1_gf, *nodes1);
FindPointsInMesh(*pmesh1, vertices1, conn2, displ1_gf, xyz, conn1, xi1, coordsm);
if (M)
{
delete M;
for (int i = 0; i<dM.Size(); i++)
{
delete dM[i];
}
dM.SetSize(0);
}
int ndofs1 = prob1->GetFESpace()->GetTrueVSize();
int ndofs2 = prob2->GetFESpace()->GetTrueVSize();
int gndofs1 = prob1->GetFESpace()->GlobalTrueVSize();
int gndofs2 = prob2->GetFESpace()->GlobalTrueVSize();
Array<int> npts(numprocs);
MPI_Allgather(&npoints,1,MPI_INT,&npts[0],1,MPI_INT,comm);
npts.PartialSum(); npts.Prepend(0);
SparseMatrix S1(gnpoints,gndofs1);
SparseMatrix S2(gnpoints,gndofs2);
Array<SparseMatrix *> dS11;
Array<SparseMatrix *> dS12;
Array<SparseMatrix *> dS21;
Array<SparseMatrix *> dS22;
// local to global map for constraints
Array<int> points_map(npoints);
cnt = 0;
for (int i = 0; i<gnpoints; i++)
{
if (i >= npts[myid] && i< npts[myid+1])
{
points_map[cnt++] = i;
}
}
if (compute_hessians)
{
dS11.SetSize(gnpoints);
dS12.SetSize(gnpoints);
dS21.SetSize(gnpoints);
dS22.SetSize(gnpoints);
for (int i = 0; i<gnpoints; i++)
{
if (i >= npts[myid] && i< npts[myid+1])
{
dS11[i] = new SparseMatrix(gndofs1,gndofs1);
dS12[i] = new SparseMatrix(gndofs1,gndofs2);
dS21[i] = new SparseMatrix(gndofs2,gndofs1);
dS22[i] = new SparseMatrix(gndofs2,gndofs2);
}
else
{
dS11[i] = nullptr;
dS12[i] = nullptr;
dS21[i] = nullptr;
dS22[i] = nullptr;
}
}
Assemble_Contact(xyz, xi1, coordsm, conn2, conn1, gapv, S1,S2,
dS11,dS12,dS21,dS22);
}
else
{
Assemble_Contact(xyz, xi1, coordsm, conn2, conn1, gapv, S1,S2, points_map);
}
// --------------------------------------------------------------------
// Redistribute the M block matrix [M1 M2]
// --------------------------------------------------------------------
int offset = constraints_offsets[myid];
MPICommunicator Mcomm1(comm,offset,gnpoints);
SparseMatrix localS1(npoints,gndofs1);
Mcomm1.Communicate(S1,localS1);
MPICommunicator Mcomm2(comm,offset,gnpoints);
SparseMatrix localS2(npoints,gndofs2);
Mcomm2.Communicate(S2,localS2);
MFEM_VERIFY(HYPRE_AssumedPartitionCheck(), "Hypre_AssumedPartitionCheck is False");
// Construct M row and col starts to construct HypreParMatrix
int M1rows[2], M2rows[2];
int M1cols[2], M2cols[2];
M1rows[0] = constraints_starts[0];
M1rows[1] = constraints_starts[1];
M2rows[0] = constraints_starts[0];
M2rows[1] = constraints_starts[1];
M1cols[0] = prob1->GetFESpace()->GetTrueDofOffsets()[0];
M1cols[1] = prob1->GetFESpace()->GetTrueDofOffsets()[1];
M2cols[0] = prob2->GetFESpace()->GetTrueDofOffsets()[0];
M2cols[1] = prob2->GetFESpace()->GetTrueDofOffsets()[1];
Array2D<HypreParMatrix*> blockM(1,2);
blockM(0,0) = new HypreParMatrix(comm,npoints,gnpoints,gndofs1,
localS1.GetI(), localS1.GetJ(),localS1.GetData(),
M1rows,M1cols);
blockM(0,1) = new HypreParMatrix(comm,npoints,gnpoints,gndofs2,
localS2.GetI(), localS2.GetJ(),localS2.GetData(),
M2rows,M2cols);
M = HypreParMatrixFromBlocks(blockM);
delete blockM(0,0);
delete blockM(0,1);
blockM.DeleteAll();
if (compute_hessians)
{
Array<SparseMatrix*> localdS11(gnpoints);
Array<SparseMatrix*> localdS12(gnpoints);
Array<SparseMatrix*> localdS21(gnpoints);
Array<SparseMatrix*> localdS22(gnpoints);
for (int k = 0; k<gnpoints; k++)
{
localdS11[k] = new SparseMatrix(ndofs1,gndofs1);
localdS12[k] = new SparseMatrix(ndofs1,gndofs2);
localdS21[k] = new SparseMatrix(ndofs2,gndofs1);
localdS22[k] = new SparseMatrix(ndofs2,gndofs2);
}
int offset1 = prob1->GetFESpace()->GetMyTDofOffset();
int offset2 = prob2->GetFESpace()->GetMyTDofOffset();
MPICommunicator dmcomm11(comm, offset1, gndofs1);
dmcomm11.Communicate(dS11,localdS11);
for (int k = 0; k<gnpoints; k++) { delete dS11[k]; }
MPICommunicator dmcomm12(comm, offset1, gndofs1);
dmcomm12.Communicate(dS12,localdS12);
for (int k = 0; k<gnpoints; k++) { delete dS12[k]; }
MPICommunicator dmcomm21(comm, offset2, gndofs2);
dmcomm21.Communicate(dS21,localdS21);
for (int k = 0; k<gnpoints; k++) { delete dS21[k]; }
MPICommunicator dmcomm22(comm, offset2, gndofs2);
dmcomm22.Communicate(dS22,localdS22);
for (int k = 0; k<gnpoints; k++) { delete dS22[k]; }
// --------------------------------------------------------------------
// Redistribute the block dM matrices [dM11 dM12; dM21 dM22]
// --------------------------------------------------------------------
// Construct dMi HypreParMatrix
Array2D<HypreParMatrix *> dMs(2,2);
dM.SetSize(gnpoints);
int * offs1 = prob1->GetFESpace()->GetTrueDofOffsets();
int * offs2 = prob2->GetFESpace()->GetTrueDofOffsets();
for (int i = 0; i<gnpoints; i++)
{
dMs(0,0) = new HypreParMatrix(comm, ndofs1, gndofs1, gndofs1,
localdS11[i]->GetI(), localdS11[i]->GetJ(),
localdS11[i]->GetData(),
offs1,offs1);
delete localdS11[i];
dMs(0,1) = new HypreParMatrix(comm, ndofs1, gndofs1, gndofs2,
localdS12[i]->GetI(), localdS12[i]->GetJ(),
localdS12[i]->GetData(),
offs1,offs2);
delete localdS12[i];
dMs(1,0) = new HypreParMatrix(comm, ndofs2, gndofs2, gndofs1,
localdS21[i]->GetI(), localdS21[i]->GetJ(),
localdS21[i]->GetData(),
offs2,offs1);
delete localdS21[i];
dMs(1,1) = new HypreParMatrix(comm, ndofs2, gndofs2, gndofs2,
localdS22[i]->GetI(), localdS22[i]->GetJ(),
localdS22[i]->GetData(),
offs2,offs2);
delete localdS22[i];
dM[i] = HypreParMatrixFromBlocks(dMs);
delete dMs(0,0);
delete dMs(0,1);
delete dMs(1,0);
delete dMs(1,1);
}
dMs.DeleteAll();
}
}
double ParContactProblem::E(const Vector & d)
{
Vector kd(K->Height());
K->Mult(d,kd);
return 0.5 * InnerProduct(comm,d, kd) - InnerProduct(comm,d, *B);
}
void ParContactProblem::DdE(const Vector &d, Vector &gradE)
{
gradE.SetSize(K->Height());
K->Mult(d, gradE);
gradE.Add(-1.0, *B);
}
HypreParMatrix* ParContactProblem::DddE(const Vector &d)
{
return K;
}
void ParContactProblem::g(const Vector &d, Vector &gd, bool compute_hessians_)
{
compute_hessians = compute_hessians_;
int ndof1 = prob1->GetNumTDofs();
int ndof2 = prob2->GetNumTDofs();
double * data = d.GetData();
Vector displ1(data,ndof1);
Vector displ2(&data[ndof1],ndof2);
if (recompute)
{
ComputeGapFunctionAndDerivatives(displ1, displ2);
recompute = false;
}
gd = GetGapFunction();
}
HypreParMatrix* ParContactProblem::Ddg(const Vector &d)
{
return GetJacobian();
}
HypreParMatrix* ParContactProblem::lDddg(const Vector &d, const Vector &l)
{
return nullptr; // for now
}
QPOptParContactProblem::QPOptParContactProblem(ParContactProblem * problem_)
: problem(problem_)
{
dimU = problem->GetNumDofs();
dimM = problem->GetNumContraints();
dimC = problem->GetNumContraints();
ml.SetSize(dimM); ml = 0.0;
Vector negone(dimM); negone = -1.0;
SparseMatrix diag(negone);
int gsize = problem->GetGlobalNumConstraints();
int * rows = problem->GetConstraintsStarts().GetData();
NegId = new HypreParMatrix(problem->GetComm(),gsize, rows,&diag);
HypreStealOwnership(*NegId, diag);
}
int QPOptParContactProblem::GetDimU() { return dimU; }
int QPOptParContactProblem::GetDimM() { return dimM; }
int QPOptParContactProblem::GetDimC() { return dimC; }
Vector & QPOptParContactProblem::Getml() { return ml; }
HypreParMatrix * QPOptParContactProblem::Duuf(const BlockVector & x)
{
return problem->DddE(x.GetBlock(0));
}
HypreParMatrix * QPOptParContactProblem::Dumf(const BlockVector & x)
{
return nullptr;
}
HypreParMatrix * QPOptParContactProblem::Dmuf(const BlockVector & x)
{
return nullptr;
}
HypreParMatrix * QPOptParContactProblem::Dmmf(const BlockVector & x)
{
return nullptr;
}
HypreParMatrix * QPOptParContactProblem::Duc(const BlockVector & x)
{
return problem->Ddg(x.GetBlock(0));
}
HypreParMatrix * QPOptParContactProblem::Dmc(const BlockVector & x)
{
return NegId;
}
HypreParMatrix * QPOptParContactProblem::lDuuc(const BlockVector & x, const Vector & l)
{
return nullptr;
}
void QPOptParContactProblem::c(const BlockVector &x, Vector & y)
{
Vector g0;
problem->g(x.GetBlock(0),g0, false); // gap function
g0.Add(-1.0, x.GetBlock(1));
problem->GetJacobian()->Mult(x.GetBlock(0),y);
y.Add(1.0, g0);
}
double QPOptParContactProblem::CalcObjective(const BlockVector & x)
{
return problem->E(x.GetBlock(0));
}
void QPOptParContactProblem::CalcObjectiveGrad(const BlockVector & x, BlockVector & y)
{
problem->DdE(x.GetBlock(0), y.GetBlock(0));
y.GetBlock(1) = 0.0;
}
QPOptParContactProblem::~QPOptParContactProblem()
{
delete NegId;
}
-218
View File
@@ -1,218 +0,0 @@
#include "parproblems_util.hpp"
class ParElasticityProblem
{
private:
MPI_Comm comm;
bool formsystem = false;
ParMesh * pmesh = nullptr;
int order;
int ndofs;
int ntdofs;
int gndofs;
FiniteElementCollection * fec = nullptr;
ParFiniteElementSpace * fes = nullptr;
Vector lambda, mu;
PWConstCoefficient lambda_cf, mu_cf;
Array<int> ess_bdr, ess_tdof_list;
ParBilinearForm *a=nullptr;
ParLinearForm b;
ParGridFunction x;
HypreParMatrix A;
Vector B,X;
void Init();
bool own_mesh;
public:
ParElasticityProblem(MPI_Comm comm_, const char *mesh_file , int sref, int pref, int order_ = 1) : comm(comm_), order(order_)
{
own_mesh = true;
Mesh * mesh = new Mesh(mesh_file,1,1);
for (int i = 0; i<sref; i++)
{
mesh->UniformRefinement();
}
pmesh = new ParMesh(comm,*mesh);
MFEM_VERIFY(pmesh->GetNE(), "ParElasticityProblem::Empty partition");
delete mesh;
for (int i = 0; i<pref; i++)
{
pmesh->UniformRefinement();
}
Init();
}
ParElasticityProblem(ParMesh * pmesh_, int order_ = 1) : pmesh(pmesh_), order(order_)
{
own_mesh = false;
comm = pmesh->GetComm();
Init();
}
ParMesh * GetMesh() { return pmesh; }
ParFiniteElementSpace * GetFESpace() { return fes; }
FiniteElementCollection * GetFECol() { return fec; }
int GetNumDofs() { return ndofs; }
int GetNumTDofs() { return ntdofs; }
int GetGlobalNumDofs() { return gndofs; }
HypreParMatrix & GetOperator()
{
MFEM_VERIFY(formsystem, "System not formed yet. Call FormLinearSystem()");
return A;
}
Vector & GetRHS()
{
MFEM_VERIFY(formsystem, "System not formed yet. Call FormLinearSystem()");
return B;
}
void SetLambda(const Vector & lambda_)
{
lambda = lambda_;
lambda_cf.UpdateConstants(lambda);
}
void SetMu(const Vector & mu_)
{
mu = mu_;
mu_cf.UpdateConstants(mu);
}
void FormLinearSystem();
void UpdateLinearSystem();
void SetDisplacementDirichletData(const Vector & delta)
{
VectorConstantCoefficient delta_cf(delta);
x.ProjectBdrCoefficient(delta_cf,ess_bdr);
};
ParGridFunction & GetDisplacementGridFunction() {return x;};
Array<int> & GetEssentialDofs() {return ess_tdof_list;};
~ParElasticityProblem()
{
delete a;
delete fes;
delete fec;
if (own_mesh)
{
delete pmesh;
}
}
};
class ParContactProblem
{
private:
MPI_Comm comm;
int numprocs;
int myid;
ParElasticityProblem * prob1 = nullptr;
ParElasticityProblem * prob2 = nullptr;
ParFiniteElementSpace * vfes1 = nullptr;
ParFiniteElementSpace * vfes2 = nullptr;
int dim;
GridFunction nodes0;
GridFunction *nodes1 = nullptr;
std::set<int> contact_vertices;
bool recompute = true;
bool compute_hessians = true;
std::vector<int> dof_offsets;
std::vector<int> vertex_offsets;
std::vector<int> constraints_offsets;
Array<int> tdof_offsets;
Array<int> constraints_starts;
Array<int> globalvertices1;
Array<int> globalvertices2;
Array<int> vertices2;
Array<int> vertices1;
protected:
int npoints=0;
int gnpoints=0;
int nv, gnv;
HypreParMatrix * K = nullptr;
BlockVector *B = nullptr;
Vector gapv;
HypreParMatrix * M=nullptr;
Array<HypreParMatrix*> dM;
void ComputeContactVertices();
public:
ParContactProblem(ParElasticityProblem * prob1_, ParElasticityProblem * prob2_);
ParElasticityProblem * GetElasticityProblem1() {return prob1;}
ParElasticityProblem * GetElasticityProblem2() {return prob2;}
MPI_Comm GetComm() {return comm;}
int GetNumDofs() {return K->Height();}
int GetGlobalNumDofs() {return K->GetGlobalNumRows();}
int GetNumContraints() {return npoints;}
int GetGlobalNumConstraints() {return gnpoints;}
std::vector<int> & GetDofOffets() { return dof_offsets; }
std::vector<int> & GetVertexOffsets() { return vertex_offsets; }
std::vector<int> & GetConstraintsOffsets() { return constraints_offsets; }
Array<int> & GetConstraintsStarts() { return constraints_starts; }
Vector & GetGapFunction() {return gapv;}
HypreParMatrix * GetJacobian() {return M;}
Array<HypreParMatrix*> & GetHessian() {return dM;}
void ComputeGapFunctionAndDerivatives(const Vector & displ1, const Vector &displ2);
double E(const Vector & d);
void DdE(const Vector &d, Vector &gradE);
HypreParMatrix* DddE(const Vector &d);
void g(const Vector &d, Vector &gd, bool compute_hessians_ = true);
HypreParMatrix* Ddg(const Vector &d);
HypreParMatrix* lDddg(const Vector &d, const Vector &l);
~ParContactProblem()
{
delete B;
delete K;
delete M;
for (int i = 0; i<dM.Size(); i++)
{
delete dM[i];
}
delete vfes1;
delete vfes2;
}
};
class QPOptParContactProblem
{
private:
ParContactProblem * problem = nullptr;
int dimU, dimM, dimC;
// Array<int> block_offsets;
Vector ml;
HypreParMatrix * NegId = nullptr;
public:
QPOptParContactProblem(ParContactProblem * problem_);
int GetDimU();
int GetDimM();
int GetDimC();
Vector & Getml();
MPI_Comm GetComm() {return problem->GetComm();}
int * GetConstraintsStarts() {return problem->GetConstraintsStarts().GetData();}
int GetGlobalNumConstraints() {return problem->GetGlobalNumConstraints();}
ParElasticityProblem * GetElasticityProblem1() {return problem->GetElasticityProblem1();}
ParElasticityProblem * GetElasticityProblem2() {return problem->GetElasticityProblem2();}
HypreParMatrix * Duuf(const BlockVector &);
HypreParMatrix * Dumf(const BlockVector &);
HypreParMatrix * Dmuf(const BlockVector &);
HypreParMatrix * Dmmf(const BlockVector &);
HypreParMatrix * Duc(const BlockVector &);
HypreParMatrix * Dmc(const BlockVector &);
HypreParMatrix * lDuuc(const BlockVector &, const Vector &);
void c(const BlockVector &, Vector &);
double CalcObjective(const BlockVector &);
void CalcObjectiveGrad(const BlockVector &, BlockVector &);
~QPOptParContactProblem();
};
@@ -1,554 +0,0 @@
#include "parproblems_util.hpp"
void FindPointsInMesh(Mesh & mesh, const Array<int> & gvert, const Vector & xyz, const Array<int> & s_conn, Array<int>& conn,
Vector & xyz2, Array<int> & s_conn2, Vector& xi, DenseMatrix & coords)
{
const int dim = mesh.Dimension();
const int np = xyz.Size() / dim;
MFEM_VERIFY(np * dim == xyz.Size(), "");
mesh.EnsureNodes();
FindPointsGSLIB finder(MPI_COMM_WORLD);
finder.SetDistanceToleranceForPointsFoundOnBoundary(0.5);
const double bb_t = 0.5;
finder.Setup(mesh, bb_t);
finder.FindPoints(xyz,mfem::Ordering::byVDIM);
Array<unsigned int> procs = finder.GetProc();
/// Return code for each point searched by FindPoints: inside element (0), on
/// element boundary (1), or not found (2).
Array<unsigned int> codes = finder.GetCode();
/// Return element number for each point found by FindPoints.
Array<unsigned int> elems = finder.GetElem();
/// Return reference coordinates for each point found by FindPoints.
Vector refcrd = finder.GetReferencePosition();
/// Return distance between the sought and the found point in physical space,
/// for each point found by FindPoints.
Vector dist = finder.GetDist();
finder.FreeData();
MFEM_VERIFY(dist.Size() == np, "");
MFEM_VERIFY(refcrd.Size() == np * dim, "");
MFEM_VERIFY(elems.Size() == np, "");
MFEM_VERIFY(codes.Size() == np, "");
bool allfound = true;
for (auto code : codes)
if (code == 2) { allfound = false; }
MFEM_VERIFY(allfound, "A point was not found");
// cout << "Maximum distance of projected points: " << dist.Max() << endl;
Array<unsigned int> elems_recv, proc_recv;
Vector ref_recv;
Vector xyz_recv;
Array<int> s_conn_recv;
MPICommunicator mycomm(MPI_COMM_WORLD, procs);
mycomm.Communicate(xyz,xyz_recv,3,mfem::Ordering::byNODES);
mycomm.Communicate(elems,elems_recv,1,mfem::Ordering::byVDIM);
mycomm.Communicate(refcrd,ref_recv,3,mfem::Ordering::byVDIM);
mycomm.Communicate(s_conn,s_conn_recv,1,mfem::Ordering::byVDIM);
proc_recv = mycomm.GetOriginProcs();
int np_loc = elems_recv.Size();
Array<int> conn_loc(np_loc*4);
Vector xi_send(np_loc*(dim-1));
for (int i=0; i<np_loc; ++i)
{
int refFace, refNormal;
// int refNormalSide;
bool is_interior = -1;
Vector normal = GetNormalVector(mesh, elems_recv[i],
ref_recv.GetData() + (i*dim),
refFace, refNormal, is_interior);
// continue;
int phyFace;
if (is_interior)
{
phyFace = -1; // the id of the face that has the closest point
FindSurfaceToProject(mesh, elems_recv[i], phyFace); // seems that this works
Array<int> cbdrVert;
mesh.GetFaceVertices(phyFace, cbdrVert);
Vector xs(dim);
xs[0] = xyz_recv[i + 0*np_loc];
xs[1] = xyz_recv[i + 1*np_loc];
xs[2] = xyz_recv[i + 2*np_loc];
Vector xi_tmp(dim-1);
// get nodes!
GridFunction *nodes = mesh.GetNodes();
DenseMatrix coord(4,3);
for (int j=0; j<4; j++)
{
for (int k=0; k<3; k++)
{
coord(j,k) = (*nodes)[cbdrVert[j]*3+k];
}
}
SlaveToMaster(coord, xs, xi_tmp);
for (int j=0; j<dim-1; ++j)
{
xi_send[i*(dim-1)+j] = xi_tmp[j];
}
// now get get the projection to the surface
}
else
{
Vector faceRefCrd(dim-1);
{
int fd = 0;
for (int j=0; j<dim; ++j)
{
if (j == refNormal)
{
// refNormalSide = (ref_recv[(i*dim) + j] > 0.5); // not used
}
else
{
faceRefCrd[fd] = ref_recv[(i*dim) + j];
fd++;
}
}
MFEM_VERIFY(fd == dim-1, "");
}
for (int j=0; j<dim-1; ++j)
{
xi_send[i*(dim-1)+j] = faceRefCrd[j]*2.0 - 1.0;
}
}
// Get the element face
Array<int> faces;
Array<int> ori;
int face;
if (is_interior)
{
face = phyFace;
}
else
{
mesh.GetElementFaces(elems_recv[i], faces, ori);
face = faces[refFace];
}
Array<int> faceVert;
mesh.GetFaceVertices(face, faceVert);
for (int p=0; p<4; p++)
{
conn_loc[4*i+p] = faceVert[p];
}
}
if (0) // for debugging
{
int sz = xi_send.Size()/2;
for (int i = 0; i<sz; i++)
{
mfem::out << "("<<xi_send[i*(dim-1)]<<","<<xi_send[i*(dim-1)+1]<<"): -> ";
for (int j = 0; j<4; j++)
{
double * vc = mesh.GetVertex(conn_loc[4*i+j]);
if (j<3)
{
mfem::out << "("<<vc[0]<<","<<vc[1]<<","<<vc[2]<<"), ";
}
else
{
mfem::out << "("<<vc[0]<<","<<vc[1]<<","<<vc[2]<<") \n " << endl;
}
}
}
}
int sz = xi_send.Size()/2;
DenseMatrix coordsm(sz*4, dim);
for (int i = 0; i<sz; i++)
{
for (int j = 0; j<4; j++)
{
for (int k=0; k<dim; k++)
{
coordsm(i*4+j,k) = mesh.GetVertex(conn_loc[i*4+j])[k];
}
}
}
// pass global indices for conn_loc
for (int i = 0; i<conn_loc.Size(); i++)
{
conn_loc[i] = gvert[conn_loc[i]];
}
mycomm.UpdateDestinationProcs();
mycomm.Communicate(xyz_recv,xyz2,3,mfem::Ordering::byNODES);
mycomm.Communicate(xi_send,xi,2,mfem::Ordering::byVDIM);
mycomm.Communicate(s_conn_recv,s_conn2,1,mfem::Ordering::byVDIM);
mycomm.Communicate(conn_loc,conn,4,mfem::Ordering::byVDIM);
mycomm.Communicate(coordsm,coords,4,mfem::Ordering::byVDIM);
}
void FindPointsInMesh(Mesh & mesh, const Array<int> & gvert, Array<int> & s_conn, const Vector &x1, Vector & xyz, Array<int>& conn,
Vector& xi, DenseMatrix & coords)
{
const int dim = mesh.Dimension();
const int np = xyz.Size() / dim;
MFEM_VERIFY(np * dim == xyz.Size(), "");
mesh.EnsureNodes();
FindPointsGSLIB finder(MPI_COMM_WORLD);
finder.SetDistanceToleranceForPointsFoundOnBoundary(0.5);
const double bb_t = 0.5;
finder.Setup(mesh, bb_t);
finder.FindPoints(xyz,mfem::Ordering::byVDIM);
Array<unsigned int> procs = finder.GetProc();
/// Return code for each point searched by FindPoints: inside element (0), on
/// element boundary (1), or not found (2).
Array<unsigned int> codes = finder.GetCode();
/// Return element number for each point found by FindPoints.
Array<unsigned int> elems = finder.GetElem();
/// Return reference coordinates for each point found by FindPoints.
Vector refcrd = finder.GetReferencePosition();
/// Return distance between the sought and the found point in physical space,
/// for each point found by FindPoints.
Vector dist = finder.GetDist();
finder.FreeData();
MFEM_VERIFY(dist.Size() == np, "");
MFEM_VERIFY(refcrd.Size() == np * dim, "");
MFEM_VERIFY(elems.Size() == np, "");
MFEM_VERIFY(codes.Size() == np, "");
bool allfound = true;
for (auto code : codes)
if (code == 2) { allfound = false; }
MFEM_VERIFY(allfound, "A point was not found");
// reorder data so that the procs are in ascending order
// sort procs and save the permutation
std::vector<unsigned int> procs_index(np);
std::iota(procs_index.begin(),procs_index.end(),0); //Initializing
sort( procs_index.begin(),procs_index.end(), [&](int i,int j){return procs[i]<procs[j];} );
// map to sorted
Array<unsigned int> procs_sorted(np);
Array<unsigned int> elems_sorted(np);
Vector xyz_sorted(np*dim);
Vector refcrd_sorted(np*dim);
Array<int> s_conn_sorted(np);
for (int i = 0; i<np; i++)
{
int j = procs_index[i];
procs_sorted[i] = procs[j];
elems_sorted[i] = elems[j];
s_conn_sorted[i] = s_conn[j];
for (int d = 0; d<dim; d++)
{
xyz_sorted(i*dim+d) = xyz(j*dim+d);
refcrd_sorted(i*dim+d) = refcrd(j*dim+d);
}
}
Array<unsigned int> elems_recv, proc_recv;
xyz = xyz_sorted;
s_conn = s_conn_sorted;
Vector ref_recv;
Vector xyz_recv;
MPICommunicator mycomm(MPI_COMM_WORLD, procs_sorted);
mycomm.Communicate(xyz_sorted,xyz_recv,3,mfem::Ordering::byVDIM);
mycomm.Communicate(elems_sorted,elems_recv,1,mfem::Ordering::byVDIM);
mycomm.Communicate(refcrd_sorted,ref_recv,3,mfem::Ordering::byVDIM);
proc_recv = mycomm.GetOriginProcs();
int np_loc = elems_recv.Size();
Array<int> conn_loc(np_loc*4);
Vector xi_send(np_loc*(dim-1));
for (int i=0; i<np_loc; ++i)
{
int refFace, refNormal;
// int refNormalSide;
bool is_interior = -1;
Vector normal = GetNormalVector(mesh, elems_recv[i],
ref_recv.GetData() + (i*dim),
refFace, refNormal, is_interior);
// continue;
int phyFace;
if (is_interior)
{
phyFace = -1; // the id of the face that has the closest point
FindSurfaceToProject(mesh, elems_recv[i], phyFace); // seems that this works
Array<int> cbdrVert;
mesh.GetFaceVertices(phyFace, cbdrVert);
Vector xs(dim);
xs[0] = xyz_recv[i*dim + 0];
xs[1] = xyz_recv[i*dim + 1];
xs[2] = xyz_recv[i*dim + 2];
Vector xi_tmp(dim-1);
// get nodes!
GridFunction *nodes = mesh.GetNodes();
DenseMatrix coord(4,3);
for (int j=0; j<4; j++)
{
for (int k=0; k<3; k++)
{
coord(j,k) = (*nodes)[cbdrVert[j]*3+k];
}
}
SlaveToMaster(coord, xs, xi_tmp);
for (int j=0; j<dim-1; ++j)
{
xi_send[i*(dim-1)+j] = xi_tmp[j];
}
// now get the projection to the surface
}
else
{
Vector faceRefCrd(dim-1);
{
int fd = 0;
for (int j=0; j<dim; ++j)
{
if (j == refNormal)
{
// refNormalSide = (ref_recv[(i*dim) + j] > 0.5); // not used
}
else
{
faceRefCrd[fd] = ref_recv[(i*dim) + j];
fd++;
}
}
MFEM_VERIFY(fd == dim-1, "");
}
for (int j=0; j<dim-1; ++j)
{
xi_send[i*(dim-1)+j] = faceRefCrd[j]*2.0 - 1.0;
}
}
// Get the element face
Array<int> faces;
Array<int> ori;
int face;
if (is_interior)
{
face = phyFace;
}
else
{
mesh.GetElementFaces(elems_recv[i], faces, ori);
face = faces[refFace];
}
Array<int> faceVert;
mesh.GetFaceVertices(face, faceVert);
for (int p=0; p<4; p++)
{
conn_loc[4*i+p] = faceVert[p];
}
}
if (0) // for debugging
{
int sz = xi_send.Size()/2;
for (int i = 0; i<sz; i++)
{
mfem::out << "("<<xi_send[i*(dim-1)]<<","<<xi_send[i*(dim-1)+1]<<"): -> ";
for (int j = 0; j<4; j++)
{
double * vc = mesh.GetVertex(conn_loc[4*i+j]);
if (j<3)
{
mfem::out << "("<<vc[0]<<","<<vc[1]<<","<<vc[2]<<"), ";
}
else
{
mfem::out << "("<<vc[0]<<","<<vc[1]<<","<<vc[2]<<") \n " << endl;
}
}
}
}
int sz = xi_send.Size()/2;
DenseMatrix coordsm(sz*4, dim);
for (int i = 0; i<sz; i++)
{
for (int j = 0; j<4; j++)
{
for (int k=0; k<dim; k++)
{
coordsm(i*4+j,k) = mesh.GetVertex(conn_loc[i*4+j])[k]+x1[dim*conn_loc[i*4+j]+k];
}
}
}
// pass global indices for conn_loc
for (int i = 0; i<conn_loc.Size(); i++)
{
conn_loc[i] = gvert[conn_loc[i]];
}
mycomm.UpdateDestinationProcs();
mycomm.Communicate(xi_send,xi,2,mfem::Ordering::byVDIM);
mycomm.Communicate(conn_loc,conn,4,mfem::Ordering::byVDIM);
mycomm.Communicate(coordsm,coords,4,mfem::Ordering::byVDIM);
}
int get_rank(int tdof, std::vector<int> & tdof_offsets)
{
int size = tdof_offsets.size();
if (size == 1) { return 0; }
std::vector<int>::iterator up;
up=std::upper_bound(tdof_offsets.begin(), tdof_offsets.end(),tdof); //
return std::distance(tdof_offsets.begin(),up)-1;
}
void ComputeTdofOffsets(const ParFiniteElementSpace * pfes,
std::vector<int> & tdof_offsets)
{
MPI_Comm comm = pfes->GetComm();
int num_procs;
MPI_Comm_size(comm, &num_procs);
tdof_offsets.resize(num_procs);
int mytoffset = pfes->GetMyTDofOffset();
MPI_Allgather(&mytoffset,1,MPI_INT,&tdof_offsets[0],1,MPI_INT,comm);
}
void ComputeTdofOffsets(MPI_Comm comm, int mytoffset, std::vector<int> & tdof_offsets)
{
int num_procs;
MPI_Comm_size(comm,&num_procs);
tdof_offsets.resize(num_procs);
MPI_Allgather(&mytoffset,1,MPI_INT,&tdof_offsets[0],1,MPI_INT,comm);
}
void ComputeTdofs(MPI_Comm comm, int mytoffs, std::vector<int> & tdofs)
{
int num_procs;
MPI_Comm_size(comm,&num_procs);
tdofs.resize(num_procs);
MPI_Allgather(&mytoffs,1,MPI_INT,&tdofs,1,MPI_INT,comm);
}
// Performs Pᵀ * A * P for BlockOperator P (with blocks as HypreParMatrices)
// and A a HypreParMatrix, i.e., this handles the special case
// where P = [P₁ P₂ ⋅⋅⋅ Pₙ]
// C = Pᵀ * A * P
void RAP(const HypreParMatrix & A, const BlockOperator & P,
BlockOperator & C)
{
int nblocks = P.NumColBlocks();
const HypreParMatrix * Pi = nullptr;
const HypreParMatrix * Pj = nullptr;
HypreParMatrix * PitAPj = nullptr;
for (int i = 0; i< nblocks; i++)
{
if (P.IsZeroBlock(0,i)) continue;
Pi = dynamic_cast<const HypreParMatrix*>(&P.GetBlock(0,i));
for (int j = 0; j<nblocks; j++)
{
if (P.IsZeroBlock(0,j)) continue;
Pj = dynamic_cast<const HypreParMatrix*>(&P.GetBlock(0,j));
if (i == j)
{
PitAPj = RAP(&A, Pj);
}
else
{
PitAPj = RAP(Pi, &A, Pj);
}
C.SetBlock(i,j,PitAPj);
}
}
}
void ParAdd(const BlockOperator & A, const BlockOperator & B, BlockOperator & C)
{
int n = A.NumRowBlocks();
int m = A.NumColBlocks();
MFEM_VERIFY(B.NumRowBlocks() == n, "Inconsistent number of row blocks");
MFEM_VERIFY(B.NumColBlocks() == m, "Inconsistent number of column blocks");
const HypreParMatrix * a;
const HypreParMatrix * b;
for (int i = 0; i<n; i++)
{
for (int j = 0; j<m; j++)
{
a = nullptr;
b = nullptr;
if (!A.IsZeroBlock(i,j))
{
a = dynamic_cast<const HypreParMatrix*>(&A.GetBlock(i,j));
}
if (!B.IsZeroBlock(i,j))
{
b = dynamic_cast<const HypreParMatrix*>(&B.GetBlock(i,j));
}
if (a && b)
{
C.SetBlock(i,j,ParAdd(a,b));
}
else if (a)
{
C.SetBlock(i,j,new HypreParMatrix(*a));
}
else if (b)
{
C.SetBlock(i,j,new HypreParMatrix(*b));
}
else
{
// do nothing
}
}
}
}
@@ -1,28 +0,0 @@
#include "mfem.hpp"
#include "problems_util.hpp"
#include "../util/mpicomm.hpp"
// Coordinates in xyz are assumed to be ordered as [X, Y, Z]
// where X is the list of x-coordinates for all points and so on.
// conn: connectivity of the target surface elements
// xi: surface reference cooridnates for the cloest point, involves a linear transformation from [0,1] to [-1,1]
void FindPointsInMesh(Mesh & mesh, const Array<int> & gvert, const Vector & xyz, const Array<int> & s_conn, Array<int>& conn,
Vector & xyz2, Array<int> & s_conn2, Vector& xi, DenseMatrix & coords);
// somewhat simplified version of the above
void FindPointsInMesh(Mesh & mesh, const Array<int> & gvert, Array<int> & s_conn, const Vector &x1, Vector & xyz, Array<int>& conn,
Vector& xi, DenseMatrix & coords);
int get_rank(int tdof, std::vector<int> & tdof_offsets);
void ComputeTdofOffsets(const ParFiniteElementSpace * pfes,
std::vector<int> & tdof_offsets);
void ComputeTdofOffsets(MPI_Comm comm, int mytoffset, std::vector<int> & tdof_offsets);
void ComputeTdofs(MPI_Comm comm, int mytoffs, std::vector<int> & tdofs);
// Performs Pᵀ * A * P for BlockOperator P (with blocks as HypreParMatrices)
// and A a HypreParMatrix, i.e., this handles the special case
// where P = [P₁ P₂ ⋅⋅⋅ Pₙ]
void RAP(const HypreParMatrix & A, const BlockOperator & P, BlockOperator & C);
void ParAdd(const BlockOperator & A, const BlockOperator & B, BlockOperator & C);
-367
View File
@@ -1,367 +0,0 @@
#include "problems.hpp"
void ElasticityProblem::Init()
{
int dim = mesh->Dimension();
fec = new H1_FECollection(order,dim);
fes = new FiniteElementSpace(mesh,fec,dim,Ordering::byVDIM);
ndofs = fes->GetTrueVSize();
mesh->SetNodalFESpace(fes);
if (mesh->bdr_attributes.Size())
{
ess_bdr.SetSize(mesh->bdr_attributes.Max());
}
ess_bdr = 0; ess_bdr[1] = 1;
fes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list);
// Solution GridFunction
x.SetSpace(fes); x = 0.0;
// RHS
b.Update(fes);
// Elasticity operator
lambda.SetSize(mesh->attributes.Max()); lambda = 57.6923076923;
mu.SetSize(mesh->attributes.Max()); mu = 38.4615384615;
lambda_cf.UpdateConstants(lambda);
mu_cf.UpdateConstants(mu);
a = new BilinearForm(fes);
a->SetDiagonalPolicy(mfem::Operator::DIAG_ONE);
a->AddDomainIntegrator(new ElasticityIntegrator(lambda_cf,mu_cf));
}
void ElasticityProblem::FormLinearSystem()
{
if (!formsystem)
{
formsystem = true;
b.Assemble();
a->Assemble();
a->FormLinearSystem(ess_tdof_list, x, b, A, X, B);
}
}
void ElasticityProblem::UpdateLinearSystem()
{
if (formsystem)
{
b.Update();
a->Update();
formsystem = false;
}
FormLinearSystem();
}
ContactProblem::ContactProblem(ElasticityProblem * prob1_, ElasticityProblem * prob2_)
: prob1(prob1_), prob2(prob2_)
{
// 1. Set up block system
Mesh* mesh1 = prob1->GetMesh();
int dim = mesh1->Dimension();
nodes0.SetSpace(mesh1->GetNodes()->FESpace());
nodes0 = *mesh1->GetNodes();
nodes1 = mesh1->GetNodes();
Vector delta1(dim);
delta1 = 0.0; delta1[0] = 0.1;
prob1->SetDisplacementDirichletData(delta1);
prob1->FormLinearSystem();
Vector delta2(dim);
delta2 = 0.0;
prob2->SetDisplacementDirichletData(delta2);
prob2->FormLinearSystem();
int ndof1 = prob1->GetNumDofs();
int ndof2 = prob2->GetNumDofs();
Array<int> offsets(3);
offsets[0] = 0;
offsets[1] = ndof1;
offsets[2] = ndof2;
offsets.PartialSum();
BlockMatrix Kb(offsets);
SparseMatrix A1 = prob1->GetOperator();
SparseMatrix A2 = prob2->GetOperator();
Kb.SetBlock(0,0,&A1);
Kb.SetBlock(1,1,&A2);
K = Kb.CreateMonolithic();
K->Threshold(0.0);
K->SortColumnIndices();
B = new BlockVector(offsets);
B->GetBlock(0).Set(1.0, prob1->GetRHS());
B->GetBlock(1).Set(1.0, prob2->GetRHS());
ComputeContactVertrices();
}
void ContactProblem::ComputeContactVertrices()
{
if (npoints>0) return;
Mesh * mesh2 = prob2->GetMesh();
Array<int> vert;
for (int b=0; b<mesh2->GetNBE(); b++)
{
if (mesh2->GetBdrAttribute(b) == 3)
{
mesh2->GetBdrElementVertices(b, vert);
for (auto v : vert)
{
contact_vertices.insert(v);
}
}
}
npoints = contact_vertices.size();
}
void ContactProblem::ComputeGapFunctionAndDerivatives(const Vector &displ1,
const Vector & displ2)
{
ComputeContactVertrices();
Mesh * mesh1 = prob1->GetMesh();
int dim = mesh1->Dimension();
Mesh * mesh2 = prob2->GetMesh();
int ndof1 = prob1->GetNumDofs();
int ndof2 = prob2->GetNumDofs();
int ndofs = ndof1 + ndof2;
int nv1 = mesh1->GetNV();
// connectivity of the second mesh
Array<int> conn2(npoints);
// mesh2->MoveNodes(displ2);
Vector xyz(dim * npoints);
int cnt = 0;
for (auto v : contact_vertices)
{
for (int d = 0; d<dim; d++)
{
xyz(cnt*dim + d) = mesh2->GetVertex(v)[d]+displ2[v*dim+d];
}
conn2[cnt] = v + nv1;
cnt++;
}
MFEM_VERIFY(cnt == npoints, "");
gapv.SetSize(npoints*dim);
// segment reference coordinates of the closest point
Vector xi1(npoints*(dim-1));
Array<int> conn1(npoints*4);
// add(nodes0, displ1, *nodes1);
FindPointsInMesh(*mesh1, xyz, conn1, xi1);
DenseMatrix coordsm(npoints*4, dim);
for (int i=0; i<npoints; i++)
{
for (int j=0; j<4; j++)
{
for (int k=0; k<dim; k++)
{
coordsm(i*4+j,k) = mesh1->GetVertex(conn1[i*4+j])[k]+displ1[dim*conn1[i*4+j]+k];
}
}
}
if (M)
{
delete M;
for (int i = 0; i<dM.Size(); i++)
{
delete dM[i];
}
dM.SetSize(0);
}
int h = npoints;
M = new SparseMatrix(h,ndofs);
dM.SetSize(npoints);
for (int i = 0; i<npoints; i++)
{
dM[i] = new SparseMatrix(ndofs,ndofs);
}
Assemble_Contact(xyz, xi1, coordsm, conn2, conn1, gapv, *M, dM);
}
double ContactProblem::E(const Vector & d)
{
return 0.5 * K->InnerProduct(d, d) - InnerProduct(d, *B);
}
void ContactProblem::DdE(const Vector &d, Vector &gradE)
{
gradE.SetSize(K->Height());
K->Mult(d, gradE);
gradE.Add(-1.0, *B);
}
SparseMatrix* ContactProblem::DddE(const Vector &d)
{
return K;
}
void ContactProblem::g(const Vector &d, Vector &gd)
{
int ndof1 = prob1->GetNumDofs();
int ndof2 = prob2->GetNumDofs();
double * data = d.GetData();
Vector displ1(data,ndof1);
Vector displ2(&data[ndof1],ndof2);
if (recompute)
{
ComputeGapFunctionAndDerivatives(displ1, displ2);
recompute = false;
}
gd = GetGapFunction();
}
SparseMatrix* ContactProblem::Ddg(const Vector &d)
{
return GetJacobian();
}
SparseMatrix* ContactProblem::lDddg(const Vector &d, const Vector &l)
{
return nullptr; // for now
}
QPContactProblem::QPContactProblem(ElasticityProblem * prob1_, ElasticityProblem * prob2_)
: ContactProblem(prob1_,prob2_)
{
ContactProblem::ComputeContactVertrices();
dimS = npoints;
dimD = K->Height();
}
// E(d) = 1 / 2 d^T K d + f^T d
double QPContactProblem::E(const Vector &d)
{
return ContactProblem::E(d);
}
// gradient(E) = K d + f
void QPContactProblem::DdE(const Vector &d, Vector &gradE)
{
ContactProblem::DdE(d,gradE);
}
// Hessian(E) = K
SparseMatrix* QPContactProblem::DddE(const Vector &d)
{
return ContactProblem::DddE(d);
}
// g(d) = J * d + g0 >= 0
void QPContactProblem::g(const Vector &d, Vector &gd)
{
Vector g0;
ContactProblem::g(d,g0);
M->Mult(d, gd);
gd.Add(1.0, g0);
}
// Jacobian(g) = J
SparseMatrix* QPContactProblem::Ddg(const Vector &d)
{
return M;
}
SparseMatrix* QPContactProblem::lDddg(const Vector &d, const Vector &l)
{
return ContactProblem::lDddg(d,l);
}
QPOptContactProblem::QPOptContactProblem(ContactProblem * problem_)
: problem(problem_)
{
dimU = problem->GetNumDofs();
dimM = problem->GetNumConstraints();
dimC = problem->GetNumConstraints();
block_offsets.SetSize(3);
block_offsets[0] = 0;
block_offsets[1] = dimU;
block_offsets[2] = dimM;
block_offsets.PartialSum();
ml.SetSize(dimM); ml = 0.0;
Vector negone(dimM); negone = -1.0;
NegId = new SparseMatrix(negone);
}
int QPOptContactProblem::GetDimU() { return dimU; }
int QPOptContactProblem::GetDimM() { return dimM; }
int QPOptContactProblem::GetDimC() { return dimC; }
Vector & QPOptContactProblem::Getml() { return ml; }
SparseMatrix * QPOptContactProblem::Duuf(const BlockVector & x)
{
return problem->DddE(x.GetBlock(0));
}
SparseMatrix * QPOptContactProblem::Dumf(const BlockVector & x)
{
return nullptr;
}
SparseMatrix * QPOptContactProblem::Dmuf(const BlockVector & x)
{
return nullptr;
}
SparseMatrix * QPOptContactProblem::Dmmf(const BlockVector & x)
{
return nullptr;
}
SparseMatrix * QPOptContactProblem::Duc(const BlockVector & x)
{
return problem->Ddg(x.GetBlock(0));
}
SparseMatrix * QPOptContactProblem::Dmc(const BlockVector & x)
{
return NegId;
}
SparseMatrix * QPOptContactProblem::lDuuc(const BlockVector & x, const Vector & l)
{
return nullptr;
}
void QPOptContactProblem::c(const BlockVector &x, Vector & y)
{
Vector g0;
problem->g(x.GetBlock(0),g0); // gap function
g0.Add(-1.0, x.GetBlock(1));
problem->GetJacobian()->Mult(x.GetBlock(0),y);
y.Add(1.0, g0);
}
double QPOptContactProblem::CalcObjective(const BlockVector & x)
{
return problem->E(x.GetBlock(0));
}
void QPOptContactProblem::CalcObjectiveGrad(const BlockVector & x, BlockVector & y)
{
problem->DdE(x.GetBlock(0), y.GetBlock(0));
y.GetBlock(1) = 0.0;
}
QPOptContactProblem::~QPOptContactProblem()
{
delete NegId;
}
-169
View File
@@ -1,169 +0,0 @@
#include "problems_util.hpp"
class ElasticityProblem
{
private:
bool formsystem = false;
Mesh * mesh = nullptr;
int order;
int ndofs;
FiniteElementCollection * fec = nullptr;
FiniteElementSpace * fes = nullptr;
Vector lambda, mu;
PWConstCoefficient lambda_cf, mu_cf;
Array<int> ess_bdr, ess_tdof_list;
BilinearForm *a=nullptr;
LinearForm b;
GridFunction x;
SparseMatrix A;
Vector B,X;
void Init();
public:
ElasticityProblem(const char *mesh_file , int ref, int order_ = 1) : order(order_)
{
mesh = new Mesh(mesh_file,1,1);
for (int i = 0; i<ref; i++)
{
mesh->UniformRefinement();
}
Init();
}
Mesh * GetMesh() { return mesh; }
FiniteElementSpace * GetFESpace() { return fes; }
int GetNumDofs() { return ndofs; }
SparseMatrix & GetOperator()
{
MFEM_VERIFY(formsystem, "System not formed yet. Call FormLinearSystem()");
return A;
}
Vector & GetRHS()
{
MFEM_VERIFY(formsystem, "System not formed yet. Call FormLinearSystem()");
return B;
}
void FormLinearSystem();
void UpdateLinearSystem();
void SetDisplacementDirichletData(const Vector & delta)
{
VectorConstantCoefficient delta_cf(delta);
x.ProjectBdrCoefficient(delta_cf,ess_bdr);
};
void UpdateDisplacement(const Vector & x_)
{
// x = x_;
// mesh->MoveVertices(x);
// mesh->NodesUpdated();
};
GridFunction & GetDisplacementGridFunction() {return x;};
Array<int> & GetEssentialDofs() {return ess_tdof_list;};
~ElasticityProblem()
{
delete a;
delete fes;
delete fec;
delete mesh;
}
};
class ContactProblem
{
private:
ElasticityProblem * prob1 = nullptr;
ElasticityProblem * prob2 = nullptr;
GridFunction nodes0;
GridFunction *nodes1 = nullptr;
std::set<int> contact_vertices;
bool recompute = true;
protected:
int npoints=0;
SparseMatrix *K =nullptr;
BlockVector *B = nullptr;
Vector gapv;
Array<SparseMatrix*> dM;
SparseMatrix * M=nullptr;
void ComputeContactVertrices();
public:
ContactProblem(ElasticityProblem * prob1_, ElasticityProblem * prob2_);
ElasticityProblem * GetElasticityProblem1() {return prob1;}
ElasticityProblem * GetElasticityProblem2() {return prob2;}
int GetNumDofs() {return K->Height();}
int GetNumConstraints() {return npoints;}
Vector & GetGapFunction() {return gapv;}
SparseMatrix * GetJacobian() {return M;}
Array<SparseMatrix*> & GetHessian() {return dM;}
void ComputeGapFunctionAndDerivatives(const Vector & displ1, const Vector &displ2);
virtual double E(const Vector & d);
virtual void DdE(const Vector &d, Vector &gradE);
virtual SparseMatrix* DddE(const Vector &d);
void g(const Vector &d, Vector &gd);
virtual SparseMatrix* Ddg(const Vector &d);
virtual SparseMatrix* lDddg(const Vector &d, const Vector &l);
~ContactProblem()
{
delete B;
delete K;
delete M;
for (int i = 0; i<dM.Size(); i++)
{
delete dM[i];
}
}
};
class QPContactProblem : public ContactProblem
{
private:
int dimD, dimS;
public:
QPContactProblem(ElasticityProblem * prob1_, ElasticityProblem * prob2_);
double E(const Vector & d);
void DdE(const Vector &d, Vector &gradE);
SparseMatrix* DddE(const Vector &d);
void g(const Vector &d, Vector &gd);
SparseMatrix* Ddg(const Vector &d);
SparseMatrix* lDddg(const Vector &d, const Vector &l);
};
class QPOptContactProblem
{
private:
ContactProblem * problem = nullptr;
int dimU, dimM, dimC;
Array<int> block_offsets;
Vector ml;
SparseMatrix * NegId = nullptr;
public:
QPOptContactProblem(ContactProblem * problem_);
int GetDimU();
int GetDimM();
int GetDimC();
Vector & Getml();
SparseMatrix * Duuf(const BlockVector &);
SparseMatrix * Dumf(const BlockVector &);
SparseMatrix * Dmuf(const BlockVector &);
SparseMatrix * Dmmf(const BlockVector &);
SparseMatrix * Duc(const BlockVector &);
SparseMatrix * Dmc(const BlockVector &);
SparseMatrix * lDuuc(const BlockVector &, const Vector &);
void c(const BlockVector &, Vector &);
double CalcObjective(const BlockVector &);
void CalcObjectiveGrad(const BlockVector &, BlockVector &);
~QPOptContactProblem();
};
File diff suppressed because it is too large Load Diff
@@ -1,61 +0,0 @@
#include "mfem.hpp"
using namespace std;
using namespace mfem;
void BasisEval(const Vector xi, Vector &N, DenseMatrix &dNdxi); // dNdxi is 2*4
void BasisEvalDerivs(const Vector xi, Vector& N, DenseMatrix& dNdxi,
DenseMatrix& dN2dxi);
// returns the vector and matrix form of the shape functions and its derivative
void BasisVectorDerivs(const Vector xi, DenseMatrix& N, DenseMatrix& dNdxi,
DenseMatrix& ddNdxi);
void cross(const Vector a, const Vector b, Vector& c);
// a outer b
void outer(const Vector a, const Vector b, DenseMatrix& c);
// dphidxi 2*4
// coords 4*3
void ComputeNormal(const DenseMatrix& dphidxi, const DenseMatrix& coords,
Vector& normal, double& nnorm);
void SlaveToMaster(const DenseMatrix& m_coords, const Vector& s_x, Vector& xi);
// m_coords is expected to be 4 * 3
void ComputeGapJacobian(const Vector x_s, const Vector xi,
const DenseMatrix m_coords,
double& gap, Vector& normal, Vector& dgdxm, Vector& dgdxs);
void ComputeGapHessian(const Vector x_s, const Vector xi,
const DenseMatrix m_coords,
DenseMatrix& dg2dx);
void NodeSegConPairs(const Vector x1, const Vector xi2,
const DenseMatrix coords2,
double& node_g, Vector& node_dg, DenseMatrix& node_dg2);
// coordsm : (npoints*4, 3) use what class?
// m_conn: (npoints*4)
void Assemble_Contact(const Vector x_s,
const Vector xi, const DenseMatrix coordsm, const Array<int> s_conn,
const Array<int> m_conn, Vector& g, SparseMatrix& M,
Array<SparseMatrix *> & dM);
void Assemble_Contact(const Vector x_s,
const Vector xi, const DenseMatrix coordsm, const Array<int> s_conn,
const Array<int> m_conn, Vector & g, SparseMatrix & M1, SparseMatrix & M2,
Array<SparseMatrix *> & dM11,
Array<SparseMatrix *> & dM12,
Array<SparseMatrix *> & dM21,
Array<SparseMatrix *> & dM22);
void Assemble_Contact(const Vector x_s,
const Vector xi, const DenseMatrix coordsm, const Array<int> s_conn,
const Array<int> m_conn, Vector & g, SparseMatrix & M1, SparseMatrix & M2,const Array<int> & points_map);
void FindSurfaceToProject(Mesh& mesh, const int elem, int& cbdrface);
Vector GetNormalVector(Mesh & mesh, const int elem, const double *ref,
int & refFace, int & refNormal, bool & interior);
int GetHexVertex(int cdim, int c, int fa, int fb, Vector & refCrd);
// Coordinates in xyz are assumed to be ordered as [X, Y, Z]
// where X is the list of x-coordinates for all points and so on.
// conn: connectivity of the target surface elements
// xi: surface reference cooridnates for the cloest point, involves a linear transformation from [0,1] to [-1,1]
void FindPointsInMesh(Mesh & mesh, Vector const& xyz, Array<int>& conn, Vector& xi);
-530
View File
@@ -1,530 +0,0 @@
#include "mpicomm.hpp"
#include "util.hpp"
MPICommunicator::MPICommunicator(MPI_Comm comm_, int offset_, int gsize)
: comm(comm_), offset(offset_)
{
MPI_Comm_size(comm,&num_procs);
MPI_Comm_rank(comm,&myid);
offsets.resize(num_procs);
MPI_Allgather(&offset,1,MPI_INT,&offsets[0],1,MPI_INT,comm);
lsize = (myid == num_procs-1) ? gsize - offsets[myid]
: offsets[myid+1]-offsets[myid];
send_count.SetSize(num_procs); send_count = 0;
send_displ.SetSize(num_procs); send_displ = 0;
recv_count.SetSize(num_procs); recv_count = 0;
recv_displ.SetSize(num_procs); recv_displ = 0;
}
MPICommunicator::MPICommunicator(MPI_Comm comm_, Array<unsigned int> & destination_procs_)
: comm(comm_), destination_procs(destination_procs_)
{
MPI_Comm_size(comm,&num_procs);
MPI_Comm_rank(comm,&myid);
send_count.SetSize(num_procs);
send_displ.SetSize(num_procs);
recv_count.SetSize(num_procs);
recv_displ.SetSize(num_procs);
resetcounts();
}
int MPICommunicator::get_rank(int dof)
{
if (num_procs == 1) { return 0; }
std::vector<int>::iterator up;
up=std::upper_bound(offsets.begin(), offsets.end(),dof);
return std::distance(offsets.begin(),up)-1;
}
void MPICommunicator::Communicate(const Vector & x_s, Vector & x_r, int vdim, int ordering)
{
int npts = x_s.Size()/vdim;
MFEM_VERIFY(npts == destination_procs.Size(), "Inconsistent number of points to be send");
// construct send count
for (int i = 0; i<npts; i++)
{
int rank = destination_procs[i];
send_count[rank] += vdim + 1; // including the sending processor id
}
// 2. Compute recv_count
MPI_Alltoall(&send_count[0],1,MPI_INT,&recv_count[0],1,MPI_INT,comm);
// 3. Compute displacements
for (int k=0; k<num_procs-1; k++)
{
send_displ[k+1] = send_displ[k] + send_count[k];
recv_displ[k+1] = recv_displ[k] + recv_count[k];
}
int sbuff_size = send_count.Sum();
int rbuff_size = recv_count.Sum();
// 4. Allocate memory and fill in send buffers
Array<double> sendvals(sbuff_size); sendvals = 0.0;
Array<int> sendoffs(num_procs); sendoffs = 0;
for (int i = 0; i<npts; i++)
{
int rank = destination_procs[i];
int j = send_displ[rank] + sendoffs[rank];
sendoffs[rank] += vdim+1;
sendvals[j] = (double)myid;
for (int k = 0; k<vdim; k++)
{
int kk = (ordering == mfem::Ordering::byNODES) ? k*npts+i : i*vdim + k;
sendvals[j+k+1] = x_s(kk);
}
}
// 5. Communication
Array<double> recvvals(rbuff_size);
double * sendvals_ptr = nullptr;
double * recvvals_ptr = nullptr;
if (sbuff_size !=0 ) { sendvals_ptr = &sendvals[0]; }
if (rbuff_size !=0 ) { recvvals_ptr = &recvvals[0]; }
MPI_Alltoallv(sendvals_ptr, send_count, send_displ, MPI_DOUBLE, recvvals_ptr,
recv_count, recv_displ, MPI_DOUBLE, comm);
// 6. Unpack
int n = rbuff_size/(vdim+1);
origin_procs.SetSize(n);
x_r.SetSize(vdim*n);
for (int i = 0; i<n; i++)
{
origin_procs[i] = (unsigned int)recvvals[(vdim+1)*i];
for (int j=0; j<vdim; j++)
{
int kk = (ordering == mfem::Ordering::byNODES) ? j*n+i : i*vdim + j;
x_r(kk) = recvvals[(vdim+1)*i + j+1];
}
}
resetcounts();
}
void MPICommunicator::Communicate(const Array<unsigned int> & x_s, Array<unsigned int> & x_r, int vdim, int ordering)
{
int npts = x_s.Size()/vdim;
MFEM_VERIFY(npts == destination_procs.Size(), "Inconsistent number of points to be send");
// construct send count
for (int i = 0; i<npts; i++)
{
int rank = destination_procs[i];
send_count[rank] += vdim + 1; // including the sending processor id
}
// 2. Compute recv_count
MPI_Alltoall(&send_count[0],1,MPI_INT,&recv_count[0],1,MPI_INT,comm);
// 3. Compute displacements
for (int k=0; k<num_procs-1; k++)
{
send_displ[k+1] = send_displ[k] + send_count[k];
recv_displ[k+1] = recv_displ[k] + recv_count[k];
}
int sbuff_size = send_count.Sum();
int rbuff_size = recv_count.Sum();
// 4. Allocate memory and fill in send buffers
Array<unsigned int> sendvals(sbuff_size); sendvals = 0.0;
Array<int> sendoffs(num_procs); sendoffs = 0;
for (int i = 0; i<npts; i++)
{
int rank = destination_procs[i];
int j = send_displ[rank] + sendoffs[rank];
sendoffs[rank] += vdim+1;
sendvals[j] = myid;
for (int k = 0; k<vdim; k++)
{
int kk = (ordering == mfem::Ordering::byNODES) ? k*npts+i : i*vdim + k;
sendvals[j+k+1] = x_s[kk];
}
}
// 5. Communication
Array<unsigned int> recvvals(rbuff_size);
unsigned int * sendvals_ptr = nullptr;
unsigned int * recvvals_ptr = nullptr;
if (sbuff_size !=0 ) { sendvals_ptr = &sendvals[0]; }
if (rbuff_size !=0 ) { recvvals_ptr = &recvvals[0]; }
MPI_Alltoallv(sendvals_ptr, send_count, send_displ, MPI_UNSIGNED, recvvals_ptr,
recv_count, recv_displ, MPI_UNSIGNED, comm);
// 6. Unpack
int n = rbuff_size/(vdim+1);
origin_procs.SetSize(n);
x_r.SetSize(vdim*n);
for (int i = 0; i<n; i++)
{
origin_procs[i] = recvvals[(vdim+1)*i];
for (int j=0; j<vdim; j++)
{
int kk = (ordering == mfem::Ordering::byNODES) ? j*n+i : i*vdim + j;
x_r[kk] = recvvals[(vdim+1)*i + j+1];
}
}
resetcounts();
}
void MPICommunicator::Communicate(const Array<int> & x_s, Array<int> & x_r, int vdim, int ordering)
{
int npts = x_s.Size()/vdim;
MFEM_VERIFY(npts == destination_procs.Size(), "Inconsistent number of points to be send");
// construct send count
for (int i = 0; i<npts; i++)
{
int rank = destination_procs[i];
send_count[rank] += vdim + 1; // including the sending processor id
}
// 2. Compute recv_count
MPI_Alltoall(&send_count[0],1,MPI_INT,&recv_count[0],1,MPI_INT,comm);
// 3. Compute displacements
for (int k=0; k<num_procs-1; k++)
{
send_displ[k+1] = send_displ[k] + send_count[k];
recv_displ[k+1] = recv_displ[k] + recv_count[k];
}
int sbuff_size = send_count.Sum();
int rbuff_size = recv_count.Sum();
// 4. Allocate memory and fill in send buffers
Array<int> sendvals(sbuff_size); sendvals = 0.0;
Array<int> sendoffs(num_procs); sendoffs = 0;
for (int i = 0; i<npts; i++)
{
int rank = destination_procs[i];
int j = send_displ[rank] + sendoffs[rank];
sendoffs[rank] += vdim+1;
sendvals[j] = myid;
for (int k = 0; k<vdim; k++)
{
int kk = (ordering == mfem::Ordering::byNODES) ? k*npts+i : i*vdim + k;
sendvals[j+k+1] = x_s[kk];
}
}
// 5. Communication
Array<int> recvvals(rbuff_size);
int * sendvals_ptr = nullptr;
int * recvvals_ptr = nullptr;
if (sbuff_size !=0 ) { sendvals_ptr = &sendvals[0]; }
if (rbuff_size !=0 ) { recvvals_ptr = &recvvals[0]; }
MPI_Alltoallv(sendvals_ptr, send_count, send_displ, MPI_INT, recvvals_ptr,
recv_count, recv_displ, MPI_INT, comm);
// 6. Unpack
int n = rbuff_size/(vdim+1);
origin_procs.SetSize(n);
x_r.SetSize(vdim*n);
for (int i = 0; i<n; i++)
{
origin_procs[i] = (unsigned int)recvvals[(vdim+1)*i];
for (int j=0; j<vdim; j++)
{
int kk = (ordering == mfem::Ordering::byNODES) ? j*n+i : i*vdim + j;
x_r[kk] = recvvals[(vdim+1)*i + j+1];
}
}
resetcounts();
}
void MPICommunicator::Communicate(const DenseMatrix & A_s, DenseMatrix & A_r, int vdim, int ordering)
{
// matrix width corresponds to dim coordinates
// matrix rows might include vdim copies
int npts = A_s.Height()/vdim;
int dim = A_s.Width();
MFEM_VERIFY(npts == destination_procs.Size(), "Inconsistent number of points to be send");
// construct send count
for (int i = 0; i<npts; i++)
{
int rank = destination_procs[i];
send_count[rank] += dim*vdim + 1; // including the sending processor id
}
// 2. Compute recv_count
MPI_Alltoall(&send_count[0],1,MPI_INT,&recv_count[0],1,MPI_INT,comm);
// 3. Compute displacements
for (int k=0; k<num_procs-1; k++)
{
send_displ[k+1] = send_displ[k] + send_count[k];
recv_displ[k+1] = recv_displ[k] + recv_count[k];
}
int sbuff_size = send_count.Sum();
int rbuff_size = recv_count.Sum();
// 4. Allocate memory and fill in send buffers
Array<double> sendvals(sbuff_size); sendvals = 0.0;
Array<int> sendoffs(num_procs); sendoffs = 0;
for (int i = 0; i<npts; i++)
{
int rank = destination_procs[i];
int j = send_displ[rank] + sendoffs[rank];
sendoffs[rank] += dim*vdim+1;
sendvals[j] = myid;
for (int k = 0; k<vdim; k++)
{
int kk = (ordering == mfem::Ordering::byNODES) ? k*npts+i : i*vdim + k;
for (int d=0; d<dim; d++)
{
sendvals[j+k*dim+d+1] = A_s(kk,d);
}
}
}
// 5. Communication
Array<double> recvvals(rbuff_size);
double * sendvals_ptr = nullptr;
double * recvvals_ptr = nullptr;
if (sbuff_size !=0 ) { sendvals_ptr = &sendvals[0]; }
if (rbuff_size !=0 ) { recvvals_ptr = &recvvals[0]; }
MPI_Alltoallv(sendvals_ptr, send_count, send_displ, MPI_DOUBLE, recvvals_ptr,
recv_count, recv_displ, MPI_DOUBLE, comm);
// 6. Unpack
int n = rbuff_size/(dim*vdim+1);
origin_procs.SetSize(n);
A_r.SetSize(vdim*n,dim);
for (int i = 0; i<n; i++)
{
origin_procs[i] = (unsigned int)recvvals[(dim*vdim+1)*i];
for (int j=0; j<vdim; j++)
{
int kk = (ordering == mfem::Ordering::byNODES) ? j*n+i : i*vdim + j;
for (int d=0; d<dim; d++)
{
A_r(kk,d) = recvvals[(dim*vdim+1)*i + j*dim + d+1];
}
}
}
resetcounts();
}
void MPICommunicator::Communicate(const SparseMatrix & mat_s , SparseMatrix & mat_r)
{
// 1. Compute send_count
int n = mat_s.NumRows();
for (int i = 0; i<n; i++)
{
int rsize = mat_s.RowSize(i);
if (rsize == 0) continue;
int rank = get_rank(i);
send_count[rank] += rsize+2;
}
// 2. Compute recv_count
MPI_Alltoall(&send_count[0],1,MPI_INT,&recv_count[0],1,MPI_INT,comm);
// 3. Compute displacements
for (int k=0; k<num_procs-1; k++)
{
send_displ[k+1] = send_displ[k] + send_count[k];
recv_displ[k+1] = recv_displ[k] + recv_count[k];
}
int sbuff_size = send_count.Sum();
int rbuff_size = recv_count.Sum();
// 4. Allocate memory and fill in send buffers
Array<double> sendvals(sbuff_size); sendvals = 0.0;
Array<int> sendcols(sbuff_size); sendcols = 0;
Array<int> sendoffs(num_procs); sendoffs = 0;
Array<int> cols;
Vector vals;
for (int i = 0; i<n; i++)
{
int rsize = mat_s.RowSize(i);
if (rsize == 0) continue;
int rank = get_rank(i);
int j = send_displ[rank] + sendoffs[rank];
mat_s.GetRow(i,cols,vals);
sendoffs[rank] += rsize+2;
sendvals[j] = (double)i;
sendvals[j+1] = (double)rsize;
sendcols[j] = i;
sendcols[j+1] = rsize;
for (int l=0; l<rsize ; l++)
{
sendvals[j+l+2] = vals[l];
sendcols[j+l+2] = cols[l];
}
}
// 5. Communication
Array<double> recvvals(rbuff_size);
Array<int> recvcols(rbuff_size);
double * sendvals_ptr = nullptr;
double * recvvals_ptr = nullptr;
int * sendcols_ptr = nullptr;
int * recvcols_ptr = nullptr;
if (sbuff_size !=0 )
{
sendvals_ptr = &sendvals[0];
sendcols_ptr = &sendcols[0];
}
if (rbuff_size !=0 )
{
recvvals_ptr = &recvvals[0];
recvcols_ptr = &recvcols[0];
}
MPI_Alltoallv(sendvals_ptr, send_count, send_displ, MPI_DOUBLE, recvvals_ptr,
recv_count, recv_displ, MPI_DOUBLE, comm);
MPI_Alltoallv(sendcols_ptr, send_count, send_displ, MPI_INT, recvcols_ptr,
recv_count, recv_displ, MPI_INT, comm);
// 6. Unpack and store to the output SparseMatrix
MFEM_VERIFY(mat_r.Height() == lsize, "Inconsistent row size of output SparseMatrix");
MFEM_VERIFY(mat_r.Width() == mat_s.Width(), "Inconsistent column size of output SparseMatrix");
int counter = 0;
while (counter < rbuff_size)
{
int row = recvcols[counter] - offset;
int size = recvcols[counter+1];
vals.SetSize(size);
cols.SetSize(size);
for (int i = 0; i<size; i++)
{
vals[i] = recvvals[counter+2 + i];
cols[i] = recvcols[counter+2 + i];
}
mat_r.AddRow(row,cols,vals);
counter += size+2;
}
MFEM_VERIFY(counter == rbuff_size, "inconsistent rbuff size");
mat_r.Finalize();
mat_r.SortColumnIndices();
resetcounts();
}
void MPICommunicator::Communicate(const Array<SparseMatrix*> & vmat_s, Array<SparseMatrix*> & vmat_r)
{
// 1. Compute send_count
for (int k = 0; k<vmat_s.Size(); k++)
{
if (!vmat_s[k]) continue;
if (vmat_s[k]->NumNonZeroElems() == 0) continue;
int nrows = vmat_s[k]->NumRows();
for (int i = 0; i<nrows; i++)
{
int rsize = vmat_s[k]->RowSize(i);
if (rsize == 0) continue;
int rank = get_rank(i);
send_count[rank] += rsize+3;
}
}
// 2. Compute recv_count
MPI_Alltoall(&send_count[0],1,MPI_INT,&recv_count[0],1,MPI_INT,comm);
// 3. Compute displacements
for (int k=0; k<num_procs-1; k++)
{
send_displ[k+1] = send_displ[k] + send_count[k];
recv_displ[k+1] = recv_displ[k] + recv_count[k];
}
int sbuff_size = send_count.Sum();
int rbuff_size = recv_count.Sum();
// 4. Allocate memory and fill in send buffers
Array<double> sendvals(sbuff_size); sendvals = 0.0;
Array<int> sendcols(sbuff_size); sendcols = 0;
Array<int> sendoffs(num_procs); sendoffs = 0;
for (int k = 0; k<vmat_s.Size(); k++)
{
if (!vmat_s[k]) continue;
if (vmat_s[k]->NumNonZeroElems() == 0) continue;
int nrows = vmat_s[k]->NumRows();
for (int i = 0; i<nrows; i++)
{
int rsize = vmat_s[k]->RowSize(i);
if (rsize == 0) continue;
int rank = get_rank(i);
int j = send_displ[rank] + sendoffs[rank];
Array<int> cols;
Vector vals;
vmat_s[k]->GetRow(i,cols,vals);
sendoffs[rank] += rsize+3;
sendvals[j] = (double)k;
sendvals[j+1] = (double)i;
sendvals[j+2] = (double)rsize;
sendcols[j] = k;
sendcols[j+1] = i;
sendcols[j+2] = rsize;
for (int l=0; l<rsize ; l++)
{
sendvals[j+l+3] = vals[l];
sendcols[j+l+3] = cols[l];
}
}
}
// 5. Communication
Array<double> recvvals(rbuff_size);
Array<int> recvcols(rbuff_size);
double * sendvals_ptr = nullptr;
double * recvvals_ptr = nullptr;
int * sendcols_ptr = nullptr;
int * recvcols_ptr = nullptr;
if (sbuff_size !=0 )
{
sendvals_ptr = &sendvals[0];
sendcols_ptr = &sendcols[0];
}
if (rbuff_size !=0 )
{
recvvals_ptr = &recvvals[0];
recvcols_ptr = &recvcols[0];
}
MPI_Alltoallv(sendvals_ptr, send_count, send_displ, MPI_DOUBLE, recvvals_ptr,
recv_count, recv_displ, MPI_DOUBLE,comm);
MPI_Alltoallv(sendcols_ptr, send_count, send_displ, MPI_INT, recvcols_ptr,
recv_count, recv_displ, MPI_INT,comm);
// 6. Unpack and store to the output SparseMatrix
int counter = 0;
while (counter < rbuff_size)
{
int npt = recvcols[counter];
int row = recvcols[counter+1] - offset;
int size = recvcols[counter+2];
Vector vals(size);
Array<int> cols(size);
for (int i = 0; i<size; i++)
{
vals[i] = recvvals[counter+3 + i];
cols[i] = recvcols[counter+3 + i];
}
vmat_r[npt]->AddRow(row,cols,vals);
counter += size+3;
}
MFEM_VERIFY(counter == rbuff_size, "inconsistent size");
for (int i = 0; i<vmat_r.Size(); i++)
{
vmat_r[i]->Finalize();
vmat_r[i]->SortColumnIndices();
}
resetcounts();
}
-47
View File
@@ -1,47 +0,0 @@
#include "mfem.hpp"
using namespace std;
using namespace mfem;
class MPICommunicator
{
private:
MPI_Comm comm;
int myid, num_procs;
Array<unsigned int > origin_procs;
Array<unsigned int > destination_procs;
int offset, lsize;
std::vector<int> offsets;
Array<int> send_count;
Array<int> send_displ;
Array<int> recv_count;
Array<int> recv_displ;
void resetcounts()
{
send_count = 0;
send_displ = 0;
recv_count = 0;
recv_displ = 0;
}
public:
MPICommunicator(MPI_Comm comm_, int offset_, int gsize);
MPICommunicator(MPI_Comm comm_, Array<unsigned int> & destination_procs_);
int get_rank(int dof);
Array<unsigned int> & GetOriginProcs() {return origin_procs;}
void UpdateDestinationProcs()
{
destination_procs.SetSize(origin_procs.Size());
destination_procs = origin_procs;
resetcounts();
}
void Communicate(const Vector & x_s, Vector & x_r, int vdim, int ordering);
void Communicate(const Array<int> & x_s, Array<int> & x_r, int vdim, int ordering);
void Communicate(const DenseMatrix & A_s, DenseMatrix & A_r, int vdim, int ordering);
void Communicate(const Array<unsigned int> & x_s, Array<unsigned int> & x_r, int vdim, int ordering);
void Communicate(const SparseMatrix & mat_s , SparseMatrix & mat_r);
void Communicate(const Array<SparseMatrix*> & vmat_s, Array<SparseMatrix*> & vmat_r);
};
-171
View File
@@ -1,171 +0,0 @@
#include "util.hpp"
void PrintVertex(Mesh * mesh, int vertex)
{
Array<int> vertices;
mfem::out << "vertex: " << vertex << ": ";
double *coords = mesh->GetVertex(vertex);
mfem::out << "(" << coords[0] << ", " << coords[1] << ", " << coords[2] << ") \n";
}
void PrintElementVertices(Mesh * mesh, int elem)
{
Array<int> vertices;
mfem::out << "elem: " << elem << ". Vertices = \n" ;
mesh->GetElementVertices(elem,vertices);
for (int i = 0; i<vertices.Size(); i++)
{
PrintVertex(mesh,vertices[i]);
}
mfem::out << endl;
}
void PrintFaceVertices(Mesh * mesh, int face)
{
Array<int> vertices;
mfem::out << "face: " << face << ". Vertices = \n" ;
mesh->GetFaceVertices(face,vertices);
for (int i = 0; i<vertices.Size(); i++)
{
PrintVertex(mesh,vertices[i]);
}
mfem::out << endl;
}
void PrintSet(const std::set<int> & a, const char *aname)
{
mfem::out << aname << " = " ;
for (std::set<int>::iterator it = a.begin(); it!= a.end(); it++)
{
mfem::out << *it << " ";
}
mfem::out << endl;
}
void PrintVector(const Vector & a, const char *aname)
{
int sz = a.Size();
mfem::out << aname << " = " ;
for (int i = 0; i<sz; i++)
{
mfem::out << a[i] << " ";
}
mfem::out << endl;
}
void PrintVertex(Mesh * mesh, int vertex, int printid)
{
int myid = Mpi::WorldRank();
if (myid == printid)
{
mfem::out << "myid = " << myid <<": " << "vertex: " << vertex << ": ";
double *coords = mesh->GetVertex(vertex);
mfem::out << "(" << coords[0] << ", " << coords[1] << ", " << coords[2] << ")\n";
}
}
void PrintElementVertices(Mesh * mesh, int elem, int printid)
{
int myid = Mpi::WorldRank();
Array<int> vertices;
if (myid == printid)
{
mfem::out << "myid = " << myid <<": " << "elem: " << elem <<
". Vertices = \n" ;
mesh->GetElementVertices(elem,vertices);
for (int i = 0; i<vertices.Size(); i++)
{
PrintVertex(mesh,vertices[i],printid);
}
mfem::out << endl;
}
}
void PrintFaceVertices(Mesh * mesh, int face, int printid)
{
int myid = Mpi::WorldRank();
Array<int> vertices;
if (myid == printid)
{
mfem::out << "myid = " << myid <<": " << "face: " << face <<
". Vertices = \n" ;
mesh->GetFaceVertices(face,vertices);
for (int i = 0; i<vertices.Size(); i++)
{
PrintVertex(mesh,vertices[i],printid);
}
mfem::out << endl;
}
}
void PrintSet(const std::set<int> & a, const char *aname, int printid)
{
int myid = Mpi::WorldRank();
if (myid == printid)
{
mfem::out << "myid = " << myid <<": " << aname << " = " ;
for (std::set<int>::iterator it = a.begin(); it!= a.end(); it++)
{
mfem::out << *it << " ";
}
mfem::out << endl;
}
}
void PrintVector(const Vector & a, const char *aname, int printid)
{
int myid = Mpi::WorldRank();
if (myid == printid)
{
int sz = a.Size();
mfem::out << "myid = " << myid <<": " << aname << " = " ;
for (int i = 0; i<sz; i++)
{
mfem::out << a[i] << " ";
}
mfem::out << endl;
}
}
void PrintVector(const std::vector<int> & a, const char *aname, int printid)
{
int myid = Mpi::WorldRank();
if (myid == printid)
{
int sz = a.size();
mfem::out << "myid = " << myid <<": " << aname << " = " ;
for (int i = 0; i<sz; i++)
{
mfem::out << a[i] << " ";
}
mfem::out << endl;
}
}
void PrintVector(const std::vector<unsigned int> & a, const char *aname, int printid)
{
int myid = Mpi::WorldRank();
if (myid == printid)
{
int sz = a.size();
mfem::out << "myid = " << myid <<": " << aname << " = " ;
for (int i = 0; i<sz; i++)
{
mfem::out << a[i] << " ";
}
mfem::out << endl;
}
}
void PrintSparseMatrix(const SparseMatrix & a, const char *aname, int printid)
{
int myid = Mpi::WorldRank();
if (myid == printid)
{
mfem::out << "myid = " << myid <<": " << aname << " = " ;
a.PrintMatlab(mfem::out);
}
mfem::out << endl;
}
-46
View File
@@ -1,46 +0,0 @@
#include "mfem.hpp"
using namespace std;
using namespace mfem;
void PrintVertex(Mesh * mesh, int vertex);
void PrintElementVertices(Mesh * mesh, int elem);
void PrintFaceVertices(Mesh * mesh, int face);
template <class T>
void PrintArray(const Array<T> & a, const char *aname)
{
int sz = a.Size();
mfem::out << aname << " = " ;
for (int i = 0; i<sz; i++)
{
mfem::out << a[i] << " ";
}
mfem::out << endl;
}
void PrintSet(const std::set<int> & a, const char *aname);
void PrintVector(const Vector & a, const char *aname);
// for parallel
void PrintVertex(Mesh * mesh, int vertex, int printid);
void PrintElementVertices(Mesh * mesh, int elem, int printid);
void PrintFaceVertices(Mesh * mesh, int face, int printid);
template <class T>
void PrintArray(const Array<T> & a, const char *aname, int printid)
{
int myid = Mpi::WorldRank();
if (myid == printid)
{
int sz = a.Size();
mfem::out << "myid = " << myid <<": " << aname << " = " ;
for (int i = 0; i<sz; i++)
{
mfem::out << a[i] << " ";
}
mfem::out << endl;
}
}
void PrintSet(const std::set<int> & a, const char *aname, int printid);
void PrintVector(const Vector & a, const char *aname, int printid);
void PrintVector(const std::vector<int> & a, const char *aname, int printid);
void PrintVector(const std::vector<unsigned int> & a, const char *aname, int printid);
void PrintSparseMatrix(const SparseMatrix & a, const char *aname, int printid);
+1 -1
View File
@@ -271,7 +271,7 @@ int main(int argc, char *argv[])
socketstream p_out_i;
double err0 = 0.;
int dof0;
int dof0 = 0; // init to suppress gcc warning
std::cout << "\n Ref |"
<< " Dofs |"
+1 -1
View File
@@ -273,7 +273,7 @@ int main(int argc, char *argv[])
double res0 = 0.;
double err0 = 0.;
int dof0;
int dof0 = 0; // init to suppress gcc warning
std::cout << "\n Ref |"
<< " Dofs |"
<< " L2 Error |"
+1 -1
View File
@@ -345,7 +345,7 @@ int main(int argc, char *argv[])
socketstream E_out_i;
double err0 = 0.;
int dof0;
int dof0 = 0; // init to suppress gcc warning
std::cout << "\n Ref |"
<< " Dofs |"
+1 -1
View File
@@ -333,7 +333,7 @@ int main(int argc, char *argv[])
double res0 = 0.;
double err0 = 0.;
int dof0;
int dof0 = 0; // init to suppress gcc warning
if (myid == 0)
{
std::cout << " Ref |"
+1 -1
View File
@@ -728,7 +728,7 @@ int main(int argc, char *argv[])
double res0 = 0.;
double err0 = 0.;
int dof0;
int dof0 = 0; // init to suppress gcc warning
Array<int> elements_to_refine;
+1 -1
View File
@@ -234,7 +234,7 @@ int main(int argc, char *argv[])
// We can use the same material maps for both problems.
std::map<int, double> sigmaMap, InvTcondMap, TcapMap, InvTcapMap;
double sigmaAir;
double sigmaAir = 0.0; // init to suppress gcc warning
double TcondAir;
double TcapAir;
if (strcmp(problem,"rod")==0 || strcmp(problem,"coil")==0)
+8
View File
@@ -13,9 +13,11 @@ set(MESH_FILES
amr-quad-q2.mesh
blade.mesh
cube.mesh
cube-tet.mesh
icf.mesh
jagged.mesh
square01.mesh
square01-tri.mesh
stretched2D.mesh
)
@@ -109,6 +111,12 @@ if (MFEM_USE_MPI)
LIBRARIES mfem mfem-common)
add_dependencies(pmesh-fitting copy_miniapps_meshing_data)
add_mfem_miniapp(fit-node-position
MAIN fit-node-position.cpp
${MFEM_MINIAPPS_COMMON_HEADERS}
LIBRARIES mfem mfem-common)
add_dependencies(fit-node-position copy_miniapps_meshing_data)
add_mfem_miniapp(pminimal-surface
MAIN pminimal-surface.cpp
LIBRARIES mfem)
File diff suppressed because it is too large Load Diff
+227
View File
@@ -0,0 +1,227 @@
// Copyright (c) 2010-2023, 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.
//
// ------------------------------------------------------------------
// Fitting of Selected Mesh Nodes to Specified Physical Positions
// ------------------------------------------------------------------
//
// This example fits a selected set of the mesh nodes to given physical
// positions while maintaining a valid mesh with good quality.
//
// Sample runs:
// mpirun -np 4 fit-node-position
// mpirun -np 4 fit-node-position -m square01-tri.mesh
// mpirun -np 4 fit-node-position -m ./cube.mesh
// mpirun -np 4 fit-node-position -m ./cube-tet.mesh -rs 0
#include "mfem.hpp"
#include "../common/mfem-common.hpp"
using namespace mfem;
using namespace std;
char vishost[] = "localhost";
int visport = 19916;
int wsize = 350;
int main (int argc, char *argv[])
{
// Initialize MPI.
Mpi::Init();
int myid = Mpi::WorldRank();
const char *mesh_file = "square01.mesh";
int rs_levels = 2;
int mesh_poly_deg = 2;
int quad_order = 5;
bool glvis = true;
// Parse command-line options.
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&rs_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&mesh_poly_deg, "-o", "--order",
"Polynomial degree of mesh finite element space.");
args.AddOption(&quad_order, "-qo", "--quad_order",
"Order of the quadrature rule.");
args.AddOption(&glvis, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0) { args.PrintUsage(cout); }
return 1;
}
if (myid == 0) { args.PrintOptions(cout); }
// Read and refine the mesh.
Mesh *mesh = new Mesh(mesh_file, 1, 1, false);
for (int lev = 0; lev < rs_levels; lev++) { mesh->UniformRefinement(); }
ParMesh pmesh(MPI_COMM_WORLD, *mesh);
delete mesh;
const int dim = pmesh.Dimension();
// Setup mesh curvature and GridFunction that stores the coordinates.
FiniteElementCollection *fec_mesh;
if (mesh_poly_deg <= 0)
{
fec_mesh = new QuadraticPosFECollection;
mesh_poly_deg = 2;
}
else { fec_mesh = new H1_FECollection(mesh_poly_deg, dim); }
ParFiniteElementSpace pfes_mesh(&pmesh, fec_mesh, dim);
pmesh.SetNodalFESpace(&pfes_mesh);
ParGridFunction coord(&pfes_mesh);
pmesh.SetNodalGridFunction(&coord);
ParGridFunction x0(coord);
// Pick which nodes to fit and select the target positions.
// (attribute 2 would have a prescribed deformation in y-direction, same x).
Array<bool> fit_marker(pfes_mesh.GetNDofs());
ParGridFunction fit_marker_vis_gf(&pfes_mesh);
ParGridFunction coord_target(&pfes_mesh);
Array<int> vdofs;
fit_marker = false;
coord_target = coord;
fit_marker_vis_gf = 0.0;
for (int e = 0; e < pmesh.GetNBE(); e++)
{
const int nd = pfes_mesh.GetBE(e)->GetDof();
const int attr = pmesh.GetBdrElement(e)->GetAttribute();
if (attr != 2) { continue; }
pfes_mesh.GetBdrElementVDofs(e, vdofs);
for (int j = 0; j < nd; j++)
{
int j_x = vdofs[j], j_y = vdofs[nd+j];
const double x = coord(j_x),
z = (dim == 2) ? 0.0 : coord(vdofs[2*nd + j]);
fit_marker[pfes_mesh.VDofToDof(j_x)] = true;
fit_marker_vis_gf(j_x) = 1.0;
if (coord(j_y) < 0.5)
{
coord_target(j_y) = 0.1 * sin(4 * M_PI * x) * cos(M_PI * z);
}
else
{
if (coord(j_x) < 0.5)
{
coord_target(j_y) = 1.0 + 0.1 * sin(2 * M_PI * x);
}
else
{
coord_target(j_y) = 1.0 + 0.1 * sin(2 * M_PI * (x + 0.5));
}
}
}
}
// Visualize the selected nodes and their target positions.
if (glvis)
{
socketstream vis1;
coord = coord_target;
common::VisualizeField(vis1, "localhost", 19916, fit_marker_vis_gf,
"Target positions (DOFS with value 1)",
0, 0, 400, 400, (dim == 2) ? "Rjm" : "");
coord = x0;
}
// Allow slipping along the remaining boundaries.
// (attributes 1 and 3 would slip, while 4 is completely fixed).
int n = 0;
for (int i = 0; i < pmesh.GetNBE(); i++)
{
const int nd = pfes_mesh.GetBE(i)->GetDof();
const int attr = pmesh.GetBdrElement(i)->GetAttribute();
MFEM_VERIFY(!(dim == 2 && attr == 3),
"Boundary attribute 3 must be used only for 3D meshes. "
"Adjust the attributes (1/2/3/4 for fixed x/y/z/all "
"components, rest for free nodes), or use -fix-bnd.");
if (attr == 1 || attr == 3) { n += nd; }
if (attr == 4) { n += nd * dim; }
}
Array<int> ess_vdofs(n);
n = 0;
for (int i = 0; i < pmesh.GetNBE(); i++)
{
const int nd = pfes_mesh.GetBE(i)->GetDof();
const int attr = pmesh.GetBdrElement(i)->GetAttribute();
pfes_mesh.GetBdrElementVDofs(i, vdofs);
if (attr == 1) // Fix x components.
{
for (int j = 0; j < nd; j++)
{ ess_vdofs[n++] = vdofs[j]; }
}
else if (attr == 3) // Fix z components.
{
for (int j = 0; j < nd; j++)
{ ess_vdofs[n++] = vdofs[j+2*nd]; }
}
else if (attr == 4) // Fix all components.
{
for (int j = 0; j < vdofs.Size(); j++)
{ ess_vdofs[n++] = vdofs[j]; }
}
}
// TMOP setup.
TMOP_QualityMetric *metric;
if (dim == 2) { metric = new TMOP_Metric_002; }
else { metric = new TMOP_Metric_302; }
TargetConstructor target(TargetConstructor::IDEAL_SHAPE_UNIT_SIZE,
pfes_mesh.GetComm());
ConstantCoefficient fit_weight(100.0);
auto integ = new TMOP_Integrator(metric, &target, nullptr);
integ->EnableSurfaceFitting(coord_target, fit_marker, fit_weight);
// Linear solver.
MINRESSolver minres(pfes_mesh.GetComm());
minres.SetMaxIter(100);
minres.SetRelTol(1e-12);
minres.SetAbsTol(0.0);
// Nonlinear solver.
ParNonlinearForm a(&pfes_mesh);
a.SetEssentialVDofs(ess_vdofs);
a.AddDomainIntegrator(integ);
const IntegrationRule &ir =
IntRules.Get(pfes_mesh.GetFE(0)->GetGeomType(), quad_order);
TMOPNewtonSolver solver(pfes_mesh.GetComm(), ir, 0);
solver.SetOperator(a);
solver.SetPreconditioner(minres);
solver.SetPrintLevel(1);
solver.SetMaxIter(200);
solver.SetRelTol(1e-10);
solver.SetAbsTol(0.0);
solver.EnableAdaptiveSurfaceFitting();
solver.SetTerminationWithMaxSurfaceFittingError(1e-3);
// Solve.
Vector b(0);
coord.SetTrueVector();
solver.Mult(b, coord.GetTrueVector());
coord.SetFromTrueVector();
if (glvis)
{
socketstream vis2;
common::VisualizeMesh(vis2, "localhost", 19916, pmesh, "Final mesh",
400, 0, 400, 400);
}
delete metric;
return 0;
}
+7 -5
View File
@@ -28,7 +28,7 @@ MFEM_LIB_FILE = mfem_is_not_built
SEQ_MINIAPPS = mobius-strip klein-bottle toroid trimmer twist mesh-explorer\
shaper extruder mesh-optimizer minimal-surface polar-nc reflector\
mesh-quality
PAR_MINIAPPS = pmesh-optimizer pminimal-surface pmesh-fitting
PAR_MINIAPPS = pmesh-optimizer pminimal-surface pmesh-fitting fit-node-position
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
else
@@ -62,11 +62,11 @@ lib-common:
# Rules to copy the *.mesh files - needed for running the sample runs when
# building out-of-source:
ifneq ($(SRC),)
MESH_FILES = amr-quad-q2.mesh blade.mesh cube.mesh icf.mesh jagged.mesh\
square01.mesh stretched2D.mesh
MESH_FILES = amr-quad-q2.mesh blade.mesh cube.mesh cube-tet.mesh icf.mesh\
jagged.mesh square01.mesh square01-tri.mesh stretched2D.mesh
$(MESH_FILES): %: $(SRC)%
ln -sf $(<) .
mesh-optimizer pmesh-optimizer pmesh-fitting: | $(MESH_FILES)
mesh-optimizer pmesh-optimizer pmesh-fitting fit-node-position: | $(MESH_FILES)
.PHONY: copy-data
copy-data: | $(MESH_FILES)
endif
@@ -92,6 +92,8 @@ mesh-quality-test-seq: mesh-quality
@$(call mfem-test,$<,, Mesh quality miniapp)
pmesh-fitting-test-par: pmesh-fitting
@$(call mfem-test,$<, $(RUN_MPI), Parallel mesh fitting miniapp)
fit-node-position-test-par: fit-node-position
@$(call mfem-test,$<, $(RUN_MPI), Parallel position fitting miniapp)
minimal-surface-test-seq: minimal-surface
@$(call mfem-test,$<,, Meshing miniapp)
pminimal-surface-test-par: pminimal-surface
@@ -117,7 +119,7 @@ clean-build:
rm -f *.o *~ mobius-strip klein-bottle toroid twist
rm -f mesh-explorer shaper extruder trimmer reflector
rm -f mesh-optimizer pmesh-optimizer pmesh-fitting polar-nc
rm -f minimal-surface pminimal-surface mesh-quality
rm -f minimal-surface pminimal-surface mesh-quality fit-node-position
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
+10 -4
View File
@@ -259,10 +259,16 @@ public:
double small = 0.001, big = 0.01;
if (dim == 3) { small = 0.005, big = 0.1; }
const double xc = pos(0) - 0.5, yc = pos(1) - 0.5;
double zc;
if (dim == 3) { zc = pos(2) - 0.5; }
double r = sqrt(xc*xc + yc*yc);
if (dim == 3) { r = sqrt(xc*xc + yc*yc + zc*zc); }
double r;
if (dim == 2)
{
r = sqrt(xc*xc + yc*yc);
}
else
{
const double zc = pos(2) - 0.5;
r = sqrt(xc*xc + yc*yc + zc*zc);
}
double r1 = 0.15; double r2 = 0.35; double sf=30.0;
const double tan1 = std::tanh(sf*(r-r1)),

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