Compare commits

..
Author SHA1 Message Date
Jason Hicken a67cf7bff6 fixed euclid issue 2020-02-26 14:15:38 -05:00
Jason Hicken 858e47628f Merge branch 'master' into functional-dev 2019-12-19 15:08:07 -05:00
Tzanio Kolev b38d59d008 Merge pull request #1186 from mfem/stefanozampini/fix-matnest-fieldsplit-issue
Bugfixes for PetscSolvers
2019-12-17 15:04:33 -08:00
Tzanio Kolev 3032b74016 Merge pull request #994 from mfem/volta-charge-dev
Adding charge calculation to Volta mini app [volta-charge-dev]
2019-12-17 15:02:43 -08:00
Tzanio Kolev 9411ef9012 Merge pull request #799 from mfem/tmop-adaptivity
Initial TMOP adaptivity interface [tmop-adaptivity]
2019-12-15 12:12:35 -08:00
Tzanio 075b5c63f9 Merge branch 'master' into tmop-adaptivity
Conflicts:
	CHANGELOG
2019-12-15 12:10:20 -08:00
Tzanio Kolev 1c629d61c6 Merge pull request #937 from mfem/gpu/appfixes
Application fixes [gpu/appfixes]
2019-12-15 12:04:30 -08:00
Tzanio c4ed6fa061 make style 2019-12-12 11:53:28 -08:00
Tzanio Kolev 69b4ed5399 Merge pull request #1107 from mfem/drzisga/pa_coeff
Support for any scalar coefficient in partial assembly operators [drzisga/pa_coeff]
2019-12-12 08:54:44 -08:00
Tzanio Kolev 9fc1933c60 Merge pull request #895 from mfem/tmop-amr-size
TMOP + AMR + equal size optimization [tmop-amr-size]
2019-12-12 08:52:01 -08:00
Stowell, Mark L 980a7fb7de Merge remote-tracking branch 'origin/master' into volta-charge-dev 2019-12-11 11:08:02 -08:00
Tzanio 6ee3586b12 make style 2019-12-10 15:32:39 -08:00
Ketan Mittal befca87b55 minor 2019-12-10 15:05:45 -08:00
Ketan Mittal 04e89ca424 changes to be compatible with AMR meshes 2019-12-10 15:03:24 -08:00
Tzanio Kolev 55dbce3d70 Merge pull request #988 from mfem/fix-superlu-hypre2160
Fix SuperLU interfacing bug with recent hypre version [fix-superlu-hypre2160]
2019-12-09 07:53:49 -08:00
Tzanio Kolev 8cee95de93 Merge pull request #1169 from mfem/fem-tetra-fix
L2_TetrahedronElement ProjectDelta fix [fem-tetra-fix]
2019-12-09 07:50:30 -08:00
Stefano Zampini 4fbe08637c PetscFieldSplitSolver: Remove futile MATNEST requirement
we can still detect saddle points out of an AIJ or MATIS matrix
Also, field detection may happen trough block size information
2019-12-09 16:59:44 +03:00
Stefano Zampini 0345edae2d PetscSolver: fix issue with blocked Jacobians and PCFIELDSPLIT 2019-12-09 16:59:44 +03:00
Stefano Zampini 9a1110cae5 PetscLinearSolver::SetOperator let the method behave as for other MFEM linear solvers 2019-12-09 16:00:25 +03:00
Tzanio Kolev 123321c886 Merge pull request #892 from mfem/print-as-one-1d-dev
Adding support for 1D meshes in ParMesh::PrintAsOne [print-as-one-1d-dev]
2019-12-08 11:18:33 -08:00
Tzanio ca56ea0258 Merge branch 'master' into tmop-adaptivity
Conflicts:
	CHANGELOG
2019-12-06 18:44:42 -08:00
Tzanio a2b05c411f make style 2019-12-05 12:41:15 -08:00
Stowell, Mark L 0a442f918e Adding a check of the number of local rows 2019-12-05 11:12:07 -08:00
Ketan Mittal b677a8bda5 minor 2019-12-02 15:10:25 -08:00
Stowell, Mark L d8c95fb8db Fixing an issue arising from inconsistent boundary attributes on different processors 2019-12-02 15:04:47 -08:00
Stowell, Mark L 4ce8ebbe87 Fixed the integration order 2019-12-01 20:43:17 -08:00
Stowell, Mark L c06496888f Adjusting integration order 2019-11-29 14:29:26 -08:00
Stowell, Mark L a9bfdb990b Making coefficient an optional argument in VectorFEBoundaryFluxLFIntegrator and using this in volta miniapp 2019-11-29 14:19:03 -08:00
Ketan Mittal 607e2df8f5 updating CHANGELOG 2019-11-26 15:37:50 -08:00
k10 b2c5e39fe6 Merge branch 'master' into tmop-amr-size 2019-11-26 14:36:44 -08:00
Ketan Mittal 41f60fc274 minor 2019-11-26 14:30:21 -08:00
Ketan Mittal b9678156b3 fixing size reduction function 2019-11-26 14:07:59 -08:00
camierjs be991043da L2_TetrahedronElement ProjectDelta 'break' fix 2019-11-22 09:40:11 -08:00
Veselin Dobrev a26d308c73 Add Conduit-blueprint to the list of libraries needed for
linking with Axom.

In INSTALL, mention the Axom/Sidre options in the CMake section.
2019-11-21 20:38:55 -08:00
Veselin Dobrev 6ab9884606 Remove some temporary debugging code. 2019-11-21 19:37:18 -08:00
Veselin Dobrev fc2e5fe938 In INSTALL, update Axom url and add version requirement. 2019-11-21 19:09:19 -08:00
Ketan Mittal 39c7d31135 removing some comments 2019-11-16 22:19:54 -08:00
Tzanio 5c29ec5738 Updated CHANGELOG 2019-11-16 16:52:51 -08:00
Ketan Mittal e9130a852f minor 2019-11-14 14:05:14 -08:00
Ketan Mittal 977ed68edf updating CHANGELOG 2019-11-12 11:57:42 -08:00
Ketan Mittal 0b41842129 minor fix to CMakeLists 2019-11-11 12:02:01 -08:00
Ketan Mittal c8c1c18c8c minor 2019-11-11 11:37:52 -08:00
Tzanio 5913fdc64c minor 2019-11-11 09:12:51 -08:00
Tzanio 0babf42d83 make style 2019-11-11 09:09:21 -08:00
Ketan Mittal 364aabee2f fix CMake build 2019-11-06 14:09:02 -08:00
Daniel Drzisga c595c0bb43 Optimized PA assembly for constant coefficient case 2019-11-05 13:35:42 -08:00
Ketan Mittal 449be76629 reverting changes from last commit 2019-11-05 12:27:27 -08:00
Ketan Mittal cab8ed64b1 minor 2019-11-05 12:09:36 -08:00
Vladimir Tomov e523024a94 Minor. 2019-11-01 16:16:37 -07:00
Ketan Mittal e66d620c13 minor fix to isnan 2019-11-01 10:51:08 -07:00
Daniel Drzisga 67f0dd8111 Added check for null pointer coefficients 2019-10-28 08:57:17 -07:00
Daniel Drzisga 0a2766adea Added support for any scalar coefficients in partial assembly of mass and
diffusion operators
2019-10-09 12:16:07 -07:00
Veselin Dobrev c22e3949d9 Merge pull request #972 from mfem/artv3/spMatfix
Methods for sparse matrix memory object [artv3/spMatfix]
2019-09-13 20:24:14 -07:00
Veselin Dobrev 5d31138a14 Merge pull request #1034 from mfem/artv3/appfixes
Additional app-fixes [artv3/appfixes]
2019-09-13 14:12:00 -07:00
Tzanio e6d01d9c4a minor reformat 2019-09-12 09:47:00 +02:00
Tzanio 0fd8f1cc72 make style 2019-09-12 09:39:57 +02:00
artv3 996f97689b fixed conflicts in mesh.cpp 2019-09-05 15:47:18 -07:00
Jason Hicken d9e78bae82 removed Functional alias to CalcEnergy, and removed comment about prolongate 2019-08-30 15:27:32 -04:00
Tzanio 8d798dd583 make style 2019-08-29 16:18:57 -07:00
Jason Hicken ecfe5a98c7 added face and boundary terms to GetEnergy 2019-08-22 15:46:01 -04:00
artv3 fa81e33913 reverted vector 2019-08-15 08:35:48 -07:00
artv3 11fb615076 reverted + mpi fix pgridfunc.cpp 2019-08-14 14:48:49 -07:00
artv3 874ff033d4 additional app-fixes 2019-08-14 08:44:13 -07:00
Stowell, Mark L d8f1960ac7 Removing unneeded code and using lower integration order (thanks v-dobrev) 2019-07-08 20:23:43 -07:00
Vladimir Tomov bc44bfdbe2 Merge branch 'master' into tmop-adaptivity
Conflicts:
	miniapps/meshing/mesh-optimizer.cpp
	miniapps/meshing/pmesh-optimizer.cpp
2019-07-05 13:43:14 -07:00
Stowell, Mark L f4b6e406e5 Adding charge calculation to Volta (includes linear integrator for surface integrals of RT fields) 2019-07-04 17:24:35 -07:00
Stowell, Mark L 1359271b79 Renaming objects to distinguish source terms 2019-07-04 13:13:47 -07:00
artv3 64ced33798 SpMat: I[Height()] -> Capacity() 2019-06-28 14:46:16 -07:00
Julian Andrej c36e2ca8f4 fixed SuperLU bug with recent hypre version 2019-06-27 17:59:08 -07:00
artv3 3e7cdc651e fixed spMat memory args 2019-06-25 08:59:05 -07:00
artv3 feb46302d9 methods for spmat memory class 2019-06-21 15:49:01 -07:00
Veselin Dobrev a36f51db5b Update the methods:
* Vector::Sum() and
 * ParGridFunction::ExchangeFaceNbrData()

to work when the data is not on the host when called.
2019-06-18 19:12:50 -07:00
Veselin Dobrev 6e457ec497 Fix a few issues related to using the classes SparseMatrix,
HypreParVector, HypreParMatrix, and HypreSmoother with GPU backends.

Add 2 versions of a new method: Vector::MakeRef -- one that takes
a Vector and an offset and another that takes a Vector, an offset
and a new size.

Add some simple debugging tools:
 * Add a new output "trace" stream: mfem::trc. By default, the output
   of mfem::trc goes to std::cout; the new function mfem::OpenTraceFile()
   can be used to redirect the output of mfem::trc to a file called
   'mfem-trace-<pid>' where <pid> is the process id. Note that this
   function can be called from a debugger.
 * In class Vector, add a new method Vector::PrintTrace() that prints a
   Vector to the mfem::trc stream -- this method can be called from a
   debugger, unlike Vector::Print with mfem::trc as argument (at least
   in gdb this did not work).
 * In class Memory<T>, add 2 new methods: PrintFlags() and
   CompareHostAndDevice() which are explicitly instantiated for T=int
   and T=double, so that they can be called inside a debugger for
   these two types.
2019-06-17 18:55:17 -07:00
Veselin Dobrev 994fd13162 Ensure the method HypreParMatrix::Mult(HypreParVector &,
HypreParVector &, double, double) can be used when the mfem::Device is
configured with a backend using a separate memory space, e.g. "cuda".
2019-06-05 12:32:11 -07:00
artv3 80f781df02 first pass at app fixes 2019-05-30 06:29:07 -07:00
artv3 20bc0cbfec Merge branch 'axom-dev' into mfem4+axom 2019-05-25 08:56:25 -07:00
Vladimir Tomov 5a5e0c5eea Fixed some issues that came up during testing. 2019-05-16 14:22:18 -07:00
Tzanio 5825f74cbf Renamed miniapps/meshing/amr-quad.mesh 2019-05-01 16:06:58 -07:00
Kenneth Weiss 20becdcab0 Updates Axom TPL setup 2019-04-30 21:45:30 -07:00
Kenneth Weiss 690eb80767 Updates axom library names in build system 2019-04-30 20:50:44 -07:00
Kenneth Weiss 7798771ccb Updates SidreDataCollection due to changes to Axom's include directory structure
axom::sidre::SidreLength was also renamed as axom::sidre::IndexType.
2019-04-30 20:50:32 -07:00
Vladimir Tomov 187174f417 Mesh and command lines for TMOP+AMR test. 2019-04-30 17:48:17 -07:00
Vladimir Tomov ee184019a8 Taking into account local refinements when computing
ideal_equal_size tmop targets.
2019-04-30 17:36:10 -07:00
Stowell, Mark L 5b005edd6b Adding support for 1D meshes in ParMesh::PrintAsOne 2019-04-29 15:33:38 -07:00
Vladimir Tomov cb4a628cb6 Minor. 2019-03-20 18:05:32 -07:00
Vladimir Tomov e5f4eb4ef9 Minor corrections. 2019-03-20 16:59:06 -07:00
Vladimir Tomov 0995a54a8d Update error.cpp 2019-03-19 14:51:34 -07:00
Vladimir Tomov 7d8cc3e756 Update error.cpp 2019-03-19 14:50:56 -07:00
Tomov 9905362fd5 Fixed small errors, improved interface. 2019-03-19 14:40:48 -07:00
Vladimir Tomov b7ff3e44c7 Completed sync of the serial mesh-optimizer, fixed some errors. 2019-03-18 13:50:45 -07:00
Vladimir Tomov dbadae5eff WIP on updating the serial miniapp. 2019-03-15 19:08:35 -07:00
Vladimir Tomov 9647b2b25c Handling of serial/parallel logic in the TMOP-specific
Newton solvers.
2019-03-13 19:01:33 -07:00
Vladimir Tomov 9a1a623f49 Handling of serial/parallel logic in the remap classes. 2019-03-12 19:01:27 -07:00
Vladimir Tomov f1b1b99a9a Minor edits. 2019-03-11 19:36:24 -07:00
Vladimir Tomov a7d41e3eb3 Minor fix. 2019-03-11 17:43:21 -07:00
Vladimir Tomov a863937520 Moved the custom parallel tmop solvers in tmop_tools.hpp. 2019-02-15 16:43:56 -08:00
Vladimir Tomov 6a46e3a119 Interface improvements for more straightforward serial/parallel
execution.
2019-02-14 17:53:20 -08:00
Vladimir Tomov 65f9bec5e5 Working parallel example for discrete adaptivity through
the new interface.
2019-02-11 15:30:00 -08:00
Tomov 4bf8206469 Code for discrete adaptivity example. 2019-02-10 21:45:32 -08:00
Tomov 2321518c9a CG remap logic in tmop_tools.hpp/cpp. 2019-02-10 21:02:06 -08:00
Vladimir Tomov 2da963308c Work towards the discrete adaptivity example. 2019-02-08 18:13:26 -08:00
Tomov 4dd18ecd88 Simplifications. 2019-02-01 18:26:40 -08:00
Tomov d95c948b72 Adaptivity interface work. Example for analytic adaptivity. 2019-01-30 21:53:06 -08:00
Tomov 05389942a2 Merge branch 'master' into tmop-adaptivity 2019-01-28 17:55:36 -08:00
Vladimir Tomov 448f9e5e25 Initial mesh adaptivity interface. 2019-01-10 18:17:56 -08:00
55 changed files with 2757 additions and 708 deletions
+24 -11
View File
@@ -18,13 +18,7 @@ Improved GPU support
list of backends is: "occa-cuda", "raja-cuda", "cuda", "hip", "occa-omp",
"raja-omp", "omp", "occa-cpu", "raja-cpu", and "cpu".
- Improved RAJA backend.
- Improved multi-GPU MPI communication.
Miscellaneous
-------------
- Improved element numbering after uniform mesh refinement.
- Improved RAJA backend and multi-GPU MPI communications.
Discretization improvements
---------------------------
@@ -32,6 +26,29 @@ Discretization improvements
load balancing. Anisotropic prism refinement is only available in the serial
version at the moment.
Meshing improvements
--------------------
- The TMOP mesh optimization algorithms were extended to support r-adaptivity.
Target matrices can now be constructed either via a given analytical function
(e.g. spatial dependence of size, aspect ratio, etc., for each element) or via
a (Par)GridFunction specified on the original mesh.
- The TMOP mesh optimization algorithms have been improved to support AMR meshes.
- Added support for creating refined versions of periodic meshes, making use of
the new L2ElementRestriction class. This class also allows for computing
geometric factors on periodic meshes using partial assembly.
- Improved element numbering after uniform mesh refinement.
New and updated examples and miniapps
-------------------------------------
- The mesh-optimizer and pmesh-optimizer miniapps have been updated to
demonstrate the new r-adaptivity capabilities of TMOP.
- The (p)mesh-optimizer miniapp has been updated to demonstrate mesh
optimization for an AMR mesh.
Miscellaneous
-------------
- Upgraded the SUNDIALS interface to utilize SUNDIALS version 5.0. This
@@ -39,10 +56,6 @@ Miscellaneous
the application level. Example usage of this new interface can be found
in the examples/sundials directory.
- Added support for creating refined versions of periodic meshes, making use of
the new L2ElementRestriction class. This class also allows for computing
geometric factors on periodic meshes using partial assembly.
Version 4.0, released on May 24, 2019
=====================================
+1 -1
View File
@@ -254,7 +254,7 @@ endif()
# Axom/Sidre
if (MFEM_USE_SIDRE)
find_package(Axom REQUIRED Sidre SLIC axom_utils)
find_package(Axom REQUIRED Axom)
endif()
# PUMI
+9 -6
View File
@@ -383,11 +383,11 @@ MFEM_USE_MPFR = YES/NO
see below.
MFEM_USE_SIDRE = YES/NO
Sidre is a component of LLNL's axom project, http://goo.gl/cZyJdn, that
provides an HDF5-based file format for visualization or restart capability
following the Conduit (https://github.com/LLNL/conduit) mesh blueprint
specification. When enabled, this option requires installation of HDF5 (see
also MFEM_USE_NETCDF), Conduit and LLNL's axom project.
Sidre is a component of LLNL's axom project, https://github.com/LLNL/axom,
that provides an HDF5-based file format for visualization or restart
capability following the Conduit (https://github.com/LLNL/conduit) mesh
blueprint specification. When enabled, this option requires installation of
HDF5 (see also MFEM_USE_NETCDF), Conduit and LLNL's axom project.
MFEM_USE_CONDUIT = YES/NO
Enables support for converting MFEM Mesh and Grid Function objects to and
@@ -543,7 +543,8 @@ The specific libraries and their options are:
Options: PETSC_OPT, PETSC_LIB.
- Sidre (optional), part of LLNL's axom project, used when MFEM_USE_SIDRE = YES.
URL: http://goo.gl/cZyJdn (axom, to be released)
Starting with MFEM v4.1, Axom version 0.3.1 or later is required.
URL: https://github.com/LLNL/axom
https://github.com/LLNL/conduit (Conduit)
https://support.hdfgroup.org/HDF5 (HDF5)
Options: SIDRE_OPT, SIDRE_LIB.
@@ -714,6 +715,7 @@ MFEM_USE_PUMI
MFEM_USE_CUDA
MFEM_USE_OCCA
MFEM_USE_RAJA
MFEM_USE_SIDRE
The following options are CMake specific:
@@ -762,6 +764,7 @@ The CMake build system adds auto-detection for the following packages/libraries:
- PUMI
- OCCA
- RAJA
- AXOM - Used when MFEM_USE_SIDRE is enabled
The following built-in CMake packages are also used:
+1 -3
View File
@@ -18,6 +18,4 @@ include(MfemCmakeUtilities)
# Note: components are enabled based on the find_package() parameters.
mfem_find_package(Axom AXOM AXOM_DIR "include" "" "lib" ""
"Paths to headers required by Axom." "Libraries required by Axom."
ADD_COMPONENT Sidre "include" sidre/sidre.hpp "lib" sidre
ADD_COMPONENT SLIC "include" slic/slic.hpp "lib" slic
ADD_COMPONENT axom_utils "include" axom_utils/Utilities.hpp "lib" axom_utils)
ADD_COMPONENT Axom "include" axom/config.hpp "lib" axom)
+1 -1
View File
@@ -154,7 +154,7 @@ set(CONDUIT_DIR "${MFEM_DIR}/../conduit" CACHE PATH
set(AXOM_DIR "${MFEM_DIR}/../axom" CACHE PATH "Path to the Axom library.")
# May need to add "Boost" as requirement.
set(Axom_REQUIRED_PACKAGES "Conduit/relay" CACHE STRING
set(Axom_REQUIRED_PACKAGES "Conduit/relay/blueprint" CACHE STRING
"Additional packages required by Axom.")
set(PUMI_DIR "${MFEM_DIR}/../pumi-2.1.0" CACHE STRING
+1 -1
View File
@@ -299,7 +299,7 @@ SIDRE_LIB = \
-Wl,-rpath,$(SIDRE_DIR)/lib -L$(SIDRE_DIR)/lib \
-Wl,-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
-Wl,-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
-lsidre -lslic -laxom_utils -lconduit -lconduit_relay -lhdf5 $(ZLIB_LIB) -ldl
-laxom -lconduit -lconduit_relay -lconduit_blueprint -lhdf5 $(ZLIB_LIB) -ldl
# PUMI
# Note that PUMI_DIR is needed -- it is used to check for gmi_sim.h
+2
View File
@@ -32,6 +32,7 @@ set(SRCS
nonlininteg.cpp
staticcond.cpp
tmop.cpp
tmop_tools.cpp
)
set(HDRS
@@ -64,6 +65,7 @@ set(HDRS
tfespace.hpp
tintrules.hpp
tmop.hpp
tmop_tools.hpp
)
if (MFEM_USE_SIDRE)
+63 -32
View File
@@ -27,7 +27,7 @@ static void OccaPADiffusionSetup2D(const int D1D,
const int NE,
const Array<double> &W,
const Vector &J,
const double COEFF,
const Vector &C,
Vector &op)
{
occa::properties props;
@@ -35,7 +35,9 @@ static void OccaPADiffusionSetup2D(const int D1D,
props["defines/Q1D"] = Q1D;
const occa::memory o_W = OccaMemoryRead(W.GetMemory(), W.Size());
const occa::memory o_J = OccaMemoryRead(J.GetMemory(), J.Size());
const occa::memory o_C = OccaMemoryRead(C.GetMemory(), C.Size());
occa::memory o_op = OccaMemoryWrite(op.GetMemory(), op.Size());
const bool const_c = C.Size() == 1;
const occa_id_t id = std::make_pair(D1D,Q1D);
static occa_kernel_t OccaDiffSetup2D_ker;
if (OccaDiffSetup2D_ker.find(id) == OccaDiffSetup2D_ker.end())
@@ -45,7 +47,7 @@ static void OccaPADiffusionSetup2D(const int D1D,
"DiffusionSetup2D", props);
OccaDiffSetup2D_ker.emplace(id, DiffusionSetup2D);
}
OccaDiffSetup2D_ker.at(id)(NE, o_W, o_J, COEFF, o_op);
OccaDiffSetup2D_ker.at(id)(NE, o_W, o_J, o_C, o_op, const_c);
}
static void OccaPADiffusionSetup3D(const int D1D,
@@ -53,7 +55,7 @@ static void OccaPADiffusionSetup3D(const int D1D,
const int NE,
const Array<double> &W,
const Vector &J,
const double COEFF,
const Vector &C,
Vector &op)
{
occa::properties props;
@@ -61,7 +63,9 @@ static void OccaPADiffusionSetup3D(const int D1D,
props["defines/Q1D"] = Q1D;
const occa::memory o_W = OccaMemoryRead(W.GetMemory(), W.Size());
const occa::memory o_J = OccaMemoryRead(J.GetMemory(), J.Size());
const occa::memory o_C = OccaMemoryRead(C.GetMemory(), C.Size());
occa::memory o_op = OccaMemoryWrite(op.GetMemory(), op.Size());
const bool const_c = C.Size() == 1;
const occa_id_t id = std::make_pair(D1D,Q1D);
static occa_kernel_t OccaDiffSetup3D_ker;
if (OccaDiffSetup3D_ker.find(id) == OccaDiffSetup3D_ker.end())
@@ -71,7 +75,7 @@ static void OccaPADiffusionSetup3D(const int D1D,
"DiffusionSetup3D", props);
OccaDiffSetup3D_ker.emplace(id, DiffusionSetup3D);
}
OccaDiffSetup3D_ker.at(id)(NE, o_W, o_J, COEFF, o_op);
OccaDiffSetup3D_ker.at(id)(NE, o_W, o_J, o_C, o_op, const_c);
}
#endif // MFEM_USE_OCCA
@@ -80,14 +84,16 @@ static void PADiffusionSetup2D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const double COEFF,
Vector &op)
const Vector &c,
Vector &d)
{
const int NQ = Q1D*Q1D;
const bool const_c = c.Size() == 1;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, 2, 2, NE);
auto y = Reshape(op.Write(), NQ, 3, NE);
auto C = const_c ? Reshape(c.Read(), 1, 1) : Reshape(c.Read(), NQ, NE);
auto D = Reshape(d.Write(), NQ, 3, NE);
MFEM_FORALL(e, NE,
{
@@ -97,10 +103,11 @@ static void PADiffusionSetup2D(const int Q1D,
const double J21 = J(q,1,0,e);
const double J12 = J(q,0,1,e);
const double J22 = J(q,1,1,e);
const double c_detJ = W[q] * COEFF / ((J11*J22)-(J21*J12));
y(q,0,e) = c_detJ * (J12*J12 + J22*J22); // 1,1
y(q,1,e) = -c_detJ * (J12*J11 + J22*J21); // 1,2
y(q,2,e) = c_detJ * (J11*J11 + J21*J21); // 2,2
const double coeff = const_c ? C(0,0) : C(q,e);
const double c_detJ = W[q] * coeff / ((J11*J22)-(J21*J12));
D(q,0,e) = c_detJ * (J12*J12 + J22*J22); // 1,1
D(q,1,e) = -c_detJ * (J12*J11 + J22*J21); // 1,2
D(q,2,e) = c_detJ * (J11*J11 + J21*J21); // 2,2
}
});
}
@@ -110,13 +117,15 @@ static void PADiffusionSetup3D(const int Q1D,
const int NE,
const Array<double> &w,
const Vector &j,
const double COEFF,
Vector &op)
const Vector &c,
Vector &d)
{
const int NQ = Q1D*Q1D*Q1D;
const bool const_c = c.Size() == 1;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, 3, 3, NE);
auto y = Reshape(op.Write(), NQ, 6, NE);
auto C = const_c ? Reshape(c.Read(), 1, 1) : Reshape(c.Read(), NQ, NE);
auto D = Reshape(d.Write(), NQ, 6, NE);
MFEM_FORALL(e, NE,
{
for (int q = 0; q < NQ; ++q)
@@ -133,7 +142,8 @@ static void PADiffusionSetup3D(const int Q1D,
const double detJ = J11 * (J22 * J33 - J32 * J23) -
/* */ J21 * (J12 * J33 - J32 * J13) +
/* */ J31 * (J12 * J23 - J22 * J13);
const double c_detJ = W[q] * COEFF / detJ;
const double coeff = const_c ? C(0,0) : C(q,e);
const double c_detJ = W[q] * coeff / detJ;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
const double A12 = (J32 * J13) - (J12 * J33);
@@ -145,12 +155,12 @@ static void PADiffusionSetup3D(const int Q1D,
const double A32 = (J31 * J12) - (J11 * J32);
const double A33 = (J11 * J22) - (J12 * J21);
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
y(q,0,e) = c_detJ * (A11*A11 + A12*A12 + A13*A13); // 1,1
y(q,1,e) = c_detJ * (A11*A21 + A12*A22 + A13*A23); // 2,1
y(q,2,e) = c_detJ * (A11*A31 + A12*A32 + A13*A33); // 3,1
y(q,3,e) = c_detJ * (A21*A21 + A22*A22 + A23*A23); // 2,2
y(q,4,e) = c_detJ * (A21*A31 + A22*A32 + A23*A33); // 3,2
y(q,5,e) = c_detJ * (A31*A31 + A32*A32 + A33*A33); // 3,3
D(q,0,e) = c_detJ * (A11*A11 + A12*A12 + A13*A13); // 1,1
D(q,1,e) = c_detJ * (A11*A21 + A12*A22 + A13*A23); // 2,1
D(q,2,e) = c_detJ * (A11*A31 + A12*A32 + A13*A33); // 3,1
D(q,3,e) = c_detJ * (A21*A21 + A22*A22 + A23*A23); // 2,2
D(q,4,e) = c_detJ * (A21*A31 + A22*A32 + A23*A33); // 3,2
D(q,5,e) = c_detJ * (A31*A31 + A32*A32 + A33*A33); // 3,3
}
});
}
@@ -161,8 +171,8 @@ static void PADiffusionSetup(const int dim,
const int NE,
const Array<double> &W,
const Vector &J,
const double COEFF,
Vector &op)
const Vector &C,
Vector &D)
{
if (dim == 1) { MFEM_ABORT("dim==1 not supported in PADiffusionSetup"); }
if (dim == 2)
@@ -170,22 +180,22 @@ static void PADiffusionSetup(const int dim,
#ifdef MFEM_USE_OCCA
if (DeviceCanUseOcca())
{
OccaPADiffusionSetup2D(D1D, Q1D, NE, W, J, COEFF, op);
OccaPADiffusionSetup2D(D1D, Q1D, NE, W, J, C, D);
return;
}
#endif // MFEM_USE_OCCA
PADiffusionSetup2D(Q1D, NE, W, J, COEFF, op);
PADiffusionSetup2D(Q1D, NE, W, J, C, D);
}
if (dim == 3)
{
#ifdef MFEM_USE_OCCA
if (DeviceCanUseOcca())
{
OccaPADiffusionSetup3D(D1D, Q1D, NE, W, J, COEFF, op);
OccaPADiffusionSetup3D(D1D, Q1D, NE, W, J, C, D);
return;
}
#endif // MFEM_USE_OCCA
PADiffusionSetup3D(Q1D, NE, W, J, COEFF, op);
PADiffusionSetup3D(Q1D, NE, W, J, C, D);
}
}
@@ -205,11 +215,32 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
ConstantCoefficient *cQ = dynamic_cast<ConstantCoefficient*>(Q);
MFEM_VERIFY(cQ != NULL, "only ConstantCoefficient is supported!");
const double coeff = cQ->constant;
PADiffusionSetup(dim, dofs1D, quad1D, ne, ir->GetWeights(), geom->J,
coeff, pa_data);
Vector coeff;
if (Q == nullptr)
{
coeff.SetSize(1);
coeff(0) = 1.0;
}
else if (ConstantCoefficient* cQ = dynamic_cast<ConstantCoefficient*>(Q))
{
coeff.SetSize(1);
coeff(0) = cQ->constant;
}
else
{
coeff.SetSize(nq * ne);
auto C = Reshape(coeff.Write(), nq, ne);
for (int e = 0; e < ne; ++e)
{
ElementTransformation& T = *fes.GetElementTransformation(e);
for (int q = 0; q < nq; ++q)
{
C(q,e) = Q->Eval(T, ir->IntPoint(q));
}
}
}
PADiffusionSetup(dim, dofs1D, quad1D, ne, ir->GetWeights(), geom->J, coeff,
pa_data);
}
#ifdef MFEM_USE_OCCA
+34 -22
View File
@@ -38,24 +38,40 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(ne*nq, Device::GetMemoryType());
ConstantCoefficient *const_coeff = dynamic_cast<ConstantCoefficient*>(Q);
// TODO: other types of coefficients ...
Vector coeff;
if (Q == nullptr)
{
coeff.SetSize(1);
coeff(0) = 1.0;
}
else if (ConstantCoefficient* cQ = dynamic_cast<ConstantCoefficient*>(Q))
{
coeff.SetSize(1);
coeff(0) = cQ->constant;
}
else
{
coeff.SetSize(nq * ne);
auto C = Reshape(coeff.Write(), nq, ne);
for (int e = 0; e < ne; ++e)
{
ElementTransformation& T = *fes.GetElementTransformation(e);
for (int q = 0; q < nq; ++q)
{
C(q,e) = Q->Eval(T, ir->IntPoint(q));
}
}
}
if (dim==1) { MFEM_ABORT("Not supported yet... stay tuned!"); }
if (dim==2)
{
double constant = 0.0;
if (const_coeff)
{
constant = const_coeff->constant;
}
else
{
MFEM_ABORT("Coefficient type not supported");
}
const int NE = ne;
const int NQ = nq;
const bool const_c = coeff.Size() == 1;
auto w = ir->GetWeights().Read();
auto J = Reshape(geom->J.Read(), NQ,2,2,NE);
auto C =
const_c ? Reshape(coeff.Read(), 1,1) : Reshape(coeff.Read(), NQ,NE);
auto v = Reshape(pa_data.Write(), NQ, NE);
MFEM_FORALL(e, NE,
{
@@ -66,25 +82,20 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
const double J21 = J(q,0,1,e);
const double J22 = J(q,1,1,e);
const double detJ = (J11*J22)-(J21*J12);
v(q,e) = w[q] * constant * detJ;
const double coeff = const_c ? C(0,0) : C(q,e);
v(q,e) = w[q] * coeff * detJ;
}
});
}
if (dim==3)
{
double constant = 0.0;
if (const_coeff)
{
constant = const_coeff->constant;
}
else
{
MFEM_ABORT("Coefficient type not supported");
}
const int NE = ne;
const int NQ = nq;
const bool const_c = coeff.Size() == 1;
auto W = ir->GetWeights().Read();
auto J = Reshape(geom->J.Read(), NQ,3,3,NE);
auto C =
const_c ? Reshape(coeff.Read(), 1,1) : Reshape(coeff.Read(), NQ,NE);
auto v = Reshape(pa_data.Write(), NQ,NE);
MFEM_FORALL(e, NE,
{
@@ -96,7 +107,8 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
const double detJ = J11 * (J22 * J33 - J32 * J23) -
/* */ J21 * (J12 * J33 - J32 * J13) +
/* */ J31 * (J12 * J23 - J22 * J13);
v(q,e) = W[q] * constant * detJ;
const double coeff = const_c ? C(0,0) : C(q,e);
v(q,e) = W[q] * coeff * detJ;
}
});
}
+1
View File
@@ -9635,6 +9635,7 @@ void L2_TetrahedronElement::ProjectDelta(int vertex, Vector &dofs) const
const IntegrationPoint &ip = Nodes.IntPoint(i);
dofs[i] = pow(ip.y, Order);
}
break;
case 3:
for (int i = 0; i < Dof; i++)
{
+1
View File
@@ -31,6 +31,7 @@
#include "estimators.hpp"
#include "staticcond.hpp"
#include "tmop.hpp"
#include "tmop_tools.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
+1
View File
@@ -1716,6 +1716,7 @@ void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff,
Array<int> vdofs;
Vector vals;
HostWrite();
// maximal element attribute for each dof
dof_attr.SetSize(fes->GetVSize());
dof_attr = -1;
+2
View File
@@ -434,6 +434,8 @@ public:
/** The GridFunction is resized using the SetSize() method. */
virtual void SetSpace(FiniteElementSpace *f);
using Vector::MakeRef;
/** @brief Make the GridFunction reference external data on a new
FiniteElementSpace. */
/** This method changes the FiniteElementSpace associated with the
+12 -6
View File
@@ -350,7 +350,6 @@ void VectorFEDomainLFIntegrator::AssembleDeltaElementVect(
vshape.Mult(vec, elvect);
}
void VectorBoundaryFluxLFIntegrator::AssembleRHSElementVect(
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
{
@@ -397,19 +396,26 @@ void VectorFEBoundaryFluxLFIntegrator::AssembleRHSElementVect(
if (ir == NULL)
{
int intorder = 2*el.GetOrder(); // <----------
if (F == NULL)
{
intorder -= el.GetOrder() + 1;
}
ir = &IntRules.Get(el.GetGeomType(), intorder);
}
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint (&ip);
double val = ip.weight*F.Eval(Tr, ip);
el.CalcShape(ip, shape);
add(elvect, val, shape, elvect);
double val = ip.weight;
if (F)
{
Tr.SetIntPoint (&ip);
val *= F->Eval(Tr, ip);
}
elvect.Add(val, shape);
}
}
+3 -2
View File
@@ -279,11 +279,12 @@ public:
class VectorFEBoundaryFluxLFIntegrator : public LinearFormIntegrator
{
private:
Coefficient &F;
Coefficient *F;
Vector shape;
public:
VectorFEBoundaryFluxLFIntegrator(Coefficient &f) : F(f) { }
VectorFEBoundaryFluxLFIntegrator() : F(NULL) { }
VectorFEBoundaryFluxLFIntegrator(Coefficient &f) : F(&f) { }
virtual void AssembleRHSElementVect(const FiniteElement &el,
ElementTransformation &Tr,
+72 -3
View File
@@ -65,6 +65,8 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
Vector el_x;
const FiniteElement *fe;
ElementTransformation *T;
Mesh *mesh = fes->GetMesh();
double energy = 0.0;
if (dnfi.Size())
@@ -84,14 +86,81 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
if (fnfi.Size())
{
MFEM_ABORT("TODO: add energy contribution from interior face terms");
FaceElementTransformations *tr;
const FiniteElement *fe1, *fe2;
Array<int> vdofs2;
for (int i = 0; i < mesh->GetNumFaces(); i++)
{
tr = mesh->GetInteriorFaceTransformations(i);
if (tr != NULL)
{
fes->GetElementVDofs(tr->Elem1No, vdofs);
fes->GetElementVDofs(tr->Elem2No, vdofs2);
vdofs.Append (vdofs2);
x.GetSubVector(vdofs, el_x);
fe1 = fes->GetFE(tr->Elem1No);
fe2 = fes->GetFE(tr->Elem2No);
for (int k = 0; k < fnfi.Size(); k++)
{
energy += fnfi[k]->GetFaceEnergy(*fe1, *fe2, *tr, el_x);
}
}
}
}
if (bfnfi.Size())
{
MFEM_ABORT("TODO: add energy contribution from boundary face terms");
}
FaceElementTransformations *tr;
const FiniteElement *fe1, *fe2;
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < bfnfi.Size(); k++)
{
if (bfnfi_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *bfnfi_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary face integrator #"
<< k << ", counting from zero");
for (int i = 0; i < bdr_attr_marker.Size(); i++)
{
bdr_attr_marker[i] |= bdr_marker[i];
}
}
for (int i = 0; i < fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
tr = mesh->GetBdrFaceTransformations (i);
if (tr != NULL)
{
fes->GetElementVDofs(tr->Elem1No, vdofs);
x.GetSubVector(vdofs, el_x);
fe1 = fes->GetFE(tr->Elem1No);
// The fe2 object is really a dummy and not used on the boundaries,
// but we can't dereference a NULL pointer, and we don't want to
// actually make a fake element.
fe2 = fe1;
for (int k = 0; k < bfnfi.Size(); k++)
{
if (bfnfi_marker[k] &&
(*bfnfi_marker[k])[bdr_attr-1] == 0) { continue; }
energy += bfnfi[k]->GetFaceEnergy(*fe1, *fe2, *tr, el_x);
}
}
}
}
return energy;
}
+1 -1
View File
@@ -111,7 +111,7 @@ public:
be fes->GetVSize(). */
double GetGridFunctionEnergy(const Vector &x) const;
/// Compute the enery corresponding to the state @a x.
/// Compute the energy corresponding to the state @a x.
/** In general, @a x may have non-homogeneous essential boundary values.
The state @a x must be a true-dof vector. */
+8
View File
@@ -55,6 +55,14 @@ double NonlinearFormIntegrator::GetElementEnergy(
return 0.0;
}
double NonlinearFormIntegrator::GetFaceEnergy(
const FiniteElement &el1, const FiniteElement &el2,
FaceElementTransformations &Tr, const Vector &elfun)
{
mfem_error("NonlinearFormIntegrator::GetFaceEnergy"
" is not overloaded!");
return 0.0;
}
void BlockNonlinearFormIntegrator::AssembleElementVector(
const Array<const FiniteElement *> &el,
+7 -1
View File
@@ -63,11 +63,17 @@ public:
FaceElementTransformations &Tr,
const Vector &elfun, DenseMatrix &elmat);
/// Compute the local energy
/// Compute the local energy/functional
virtual double GetElementEnergy(const FiniteElement &el,
ElementTransformation &Tr,
const Vector &elfun);
/// Compute the face(s) contribution to the energy/functional
virtual double GetFaceEnergy(const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &Tr,
const Vector &elfun);
virtual ~NonlinearFormIntegrator() { }
};
+13 -6
View File
@@ -38,19 +38,24 @@ typedef double* QLocal3D_t @dim(Q1D, Q1D, Q1D, NE);
typedef double* Jacobian2D_t @dim(Q2D, 2, 2, NE);
typedef double* Jacobian3D_t @dim(Q3D, 3, 3, NE);
typedef double* Coeff2D_t @dim(Q2D, NE);
typedef double* Coeff3D_t @dim(Q3D, NE);
typedef double* SymmOperator2D_t @dim(Q2D, 3, NE);
typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
@kernel void DiffusionSetup2D(const int NE,
@restrict const double *W,
@restrict const Jacobian2D_t J,
const double COEFF,
@restrict SymmOperator2D_t op) {
@restrict const Coeff2D_t C,
@restrict SymmOperator2D_t op,
const bool const_c) {
for (int e = 0; e < NE; ++e; @outer) {
for (int q = 0; q < Q2D; ++q; @inner) {
const double J11 = J(q, 0, 0, e), J12 = J(q, 1, 0, e);
const double J21 = J(q, 0, 1, e), J22 = J(q, 1, 1, e);
const double c_detJ = W[q] * COEFF / ((J11 * J22) - (J21 * J12));
const double coeff = const_c ? C(0,0) : C(q,e);
const double c_detJ = W[q] * coeff / ((J11 * J22) - (J21 * J12));
op(q, 0, e) = c_detJ * (J21*J21 + J22*J22); // (1,1)
op(q, 1, e) = -c_detJ * (J21*J11 + J22*J12); // (1,2), (2,1)
op(q, 2, e) = c_detJ * (J11*J11 + J12*J12); // (2,2)
@@ -61,8 +66,9 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
@kernel void DiffusionSetup3D(const int NE,
@restrict const double *W,
@restrict const Jacobian3D_t J,
const double COEFF,
@restrict SymmOperator3D_t op) {
@restrict const Coeff3D_t C,
@restrict SymmOperator3D_t op,
const bool const_c) {
for (int e = 0; e < NE; ++e; @outer) {
for (int q = 0; q < Q3D; ++q; @inner) {
const double J11 = J(q, 0, 0, e), J12 = J(q, 1, 0, e), J13 = J(q, 2, 0, e);
@@ -72,7 +78,8 @@ typedef double* SymmOperator3D_t @dim(Q3D, 6, NE);
const double detJ = ((J11 * J22 * J33) + (J12 * J23 * J31) + (J13 * J21 * J32) -
(J13 * J22 * J31) - (J12 * J21 * J33) - (J11 * J23 * J32));
const double c_detJ = W[q] * COEFF / detJ;
const double coeff = const_c ? C(0,0) : C(q,e);
const double c_detJ = W[q] * coeff / detJ;
// adj(J)
const double A11 = (J22 * J33) - (J23 * J32);
+4 -2
View File
@@ -225,11 +225,13 @@ void ParGridFunction::ExchangeFaceNbrData()
MPI_Request *recv_requests = requests + num_face_nbrs;
MPI_Status *statuses = new MPI_Status[num_face_nbrs];
const double *h_data = this->HostRead();
for (int i = 0; i < send_data.Size(); i++)
{
send_data[i] = data[send_ldof[i]];
send_data[i] = h_data[send_ldof[i]];
}
double *h_face_nbr_data = face_nbr_data.HostWrite();
for (int fn = 0; fn < num_face_nbrs; fn++)
{
int nbr_rank = pmesh->GetFaceNbrRank(fn);
@@ -239,7 +241,7 @@ void ParGridFunction::ExchangeFaceNbrData()
send_offset[fn+1] - send_offset[fn],
MPI_DOUBLE, nbr_rank, tag, MyComm, &send_requests[fn]);
MPI_Irecv(&face_nbr_data(recv_offset[fn]),
MPI_Irecv(&h_face_nbr_data[recv_offset[fn]],
recv_offset[fn+1] - recv_offset[fn],
MPI_DOUBLE, nbr_rank, tag, MyComm, &recv_requests[fn]);
}
+2
View File
@@ -112,6 +112,8 @@ public:
/// Associate a new parallel space with the ParGridFunction.
void SetSpace(ParFiniteElementSpace *f);
using GridFunction::MakeRef;
/** @brief Make the ParGridFunction reference external data on a new
FiniteElementSpace. */
/** This method changes the FiniteElementSpace associated with the
+2 -2
View File
@@ -46,7 +46,7 @@ double ParNonlinearForm::GetParGridFunctionEnergy(const Vector &x) const
void ParNonlinearForm::Mult(const Vector &x, Vector &y) const
{
NonlinearForm::Mult(x, y); // x --(P)--> aux1 --(A_local)--> aux2
Y.SetData(aux2.GetData()); // aux2 contains A_local.P.x
Y.MakeRef(aux2, 0); // aux2 contains A_local.P.x
if (fnfi.Size())
{
@@ -58,7 +58,7 @@ void ParNonlinearForm::Mult(const Vector &x, Vector &y) const
Array<int> vdofs1, vdofs2;
Vector el_x, el_y;
X.SetData(aux1.GetData()); // aux1 contains P.x
X.MakeRef(aux1, 0); // aux1 contains P.x
X.ExchangeFaceNbrData();
const int n_shared_faces = pmesh->GetNSharedFaces();
for (int i = 0; i < n_shared_faces; i++)
+6 -8
View File
@@ -16,9 +16,7 @@
#include "fem.hpp"
#ifdef MFEM_USE_MPI
#include <sidre/IOManager.hpp>
#endif
#include <axom/sidre.hpp>
#include <string>
#include <iomanip> // for setw, setfill
@@ -204,10 +202,10 @@ SidreDataCollection::get_file_path(const std::string &filename) const
axom::sidre::View *
SidreDataCollection::AllocNamedBuffer(const std::string& buffer_name,
axom::sidre::SidreLength sz,
axom::sidre::IndexType sz,
axom::sidre::TypeID type)
{
sz = std::max(sz, sidre::SidreLength(0));
sz = std::max(sz, sidre::IndexType(0));
sidre::Group *f = named_buffers_grp();
sidre::View *v = NULL;
@@ -825,7 +823,7 @@ void SidreDataCollection::Save(const std::string& filename,
void SidreDataCollection::
addScalarBasedGridFunction(const std::string &field_name, GridFunction *gf,
const std::string &buffer_name,
axom::sidre::SidreLength offset)
axom::sidre::IndexType offset)
{
sidre::Group* grp = m_bp_grp->getGroup("fields/" + field_name);
MFEM_ASSERT(grp != NULL, "field " << field_name << " does not exist");
@@ -888,7 +886,7 @@ addScalarBasedGridFunction(const std::string &field_name, GridFunction *gf,
void SidreDataCollection::
addVectorBasedGridFunction(const std::string& field_name, GridFunction *gf,
const std::string &buffer_name,
axom::sidre::SidreLength offset)
axom::sidre::IndexType offset)
{
sidre::Group* grp = m_bp_grp->getGroup("fields/" + field_name);
MFEM_ASSERT(grp != NULL, "field " << field_name << " does not exist");
@@ -1013,7 +1011,7 @@ DeregisterFieldInBPIndex(const std::string& field_name)
void SidreDataCollection::RegisterField(const std::string &field_name,
GridFunction *gf,
const std::string &buffer_name,
axom::sidre::SidreLength offset)
axom::sidre::IndexType offset)
{
if ( field_name.empty() || buffer_name.empty() ||
gf == NULL || gf->FESpace() == NULL )
+5 -5
View File
@@ -25,7 +25,7 @@
# pragma GCC diagnostic ignored "-Wpedantic"
# endif
#endif
#include <sidre/sidre.hpp>
#include <axom/sidre.hpp>
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
# pragma GCC diagnostic pop
#endif
@@ -246,7 +246,7 @@ public:
*/
void RegisterField(const std::string &field_name, GridFunction *gf,
const std::string &buffer_name,
axom::sidre::SidreLength offset);
axom::sidre::IndexType offset);
/// Registers an attribute field in the Sidre DataStore
/** The registration process is similar to that of RegisterField()
@@ -385,7 +385,7 @@ public:
*/
axom::sidre::View *
AllocNamedBuffer(const std::string& buffer_name,
axom::sidre::SidreLength sz,
axom::sidre::IndexType sz,
axom::sidre::TypeID type =
axom::sidre::DOUBLE_ID);
@@ -469,7 +469,7 @@ private:
void addScalarBasedGridFunction(const std::string& field_name,
GridFunction* gf,
const std::string &buffer_name,
axom::sidre::SidreLength offset);
axom::sidre::IndexType offset);
/**
* \brief A private helper function to set up the views associated with the
@@ -483,7 +483,7 @@ private:
void addVectorBasedGridFunction(const std::string& field_name,
GridFunction* gf,
const std::string &buffer_name,
axom::sidre::SidreLength offset);
axom::sidre::IndexType offset);
/** @brief A private helper function to set up the Views associated with
attribute field named @a field_name */
+190 -13
View File
@@ -12,6 +12,7 @@
#include "tmop.hpp"
#include "linearform.hpp"
#include "pgridfunc.hpp"
#include "tmop_tools.hpp"
namespace mfem
{
@@ -768,7 +769,7 @@ void TMOP_Metric_352::AssembleH(const DenseMatrix &Jpt,
void TargetConstructor::ComputeAvgVolume() const
{
MFEM_VERIFY(nodes, "Nodes are not given!");
MFEM_ASSERT(avg_volume == 0.0, "the average volume is already computed!");
MFEM_ASSERT(avg_volume == 0.0, "The average volume is already computed!");
Mesh *mesh = nodes->FESpace()->GetMesh();
const int NE = mesh->GetNE();
@@ -787,9 +788,13 @@ void TargetConstructor::ComputeAvgVolume() const
volume += ip.weight * Tr.Weight();
}
}
if (!Parallel())
NCMesh *ncmesh = mesh->ncmesh;
if (Parallel() == false)
{
avg_volume = volume / NE;
avg_volume = (ncmesh == NULL) ?
volume / NE : volume / ncmesh->GetNumRootElements();
}
#ifdef MFEM_USE_MPI
else
@@ -797,7 +802,8 @@ void TargetConstructor::ComputeAvgVolume() const
double area_NE[4];
area_NE[0] = volume; area_NE[1] = NE;
MPI_Allreduce(area_NE, area_NE + 2, 2, MPI_DOUBLE, MPI_SUM, comm);
avg_volume = area_NE[2] / area_NE[3];
avg_volume = (ncmesh == NULL) ?
area_NE[2] / area_NE[3] : area_NE[2] / ncmesh->GetNumRootElements();
}
#endif
}
@@ -805,6 +811,7 @@ void TargetConstructor::ComputeAvgVolume() const
// virtual method
void TargetConstructor::ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const
{
MFEM_ASSERT(target_type == IDEAL_SHAPE_UNIT_SIZE || nodes != NULL, "");
@@ -827,7 +834,15 @@ void TargetConstructor::ComputeElementTargets(int e_id, const FiniteElement &fe,
{
if (avg_volume == 0.0) { ComputeAvgVolume(); }
DenseMatrix W(Wideal.Height());
W.Set(std::pow(volume_scale * avg_volume / Wideal.Det(),
NCMesh *ncmesh = nodes->FESpace()->GetMesh()->ncmesh;
double el_volume = avg_volume;
if (ncmesh)
{
el_volume = avg_volume / ncmesh->GetElementSizeReduction(e_id);
}
W.Set(std::pow(volume_scale * el_volume / Wideal.Det(),
1./W.Height()), Wideal);
for (int i = 0; i < ir.GetNPoints(); i++) { Jtr(i) = W; }
break;
@@ -853,7 +868,7 @@ void TargetConstructor::ComputeElementTargets(int e_id, const FiniteElement &fe,
if (target_type == IDEAL_SHAPE_GIVEN_SIZE)
{
const double det = Jtr(i).Det();
MFEM_VERIFY(det > 0.0, "Initial mesh is inverted!");
MFEM_VERIFY(det > 0.0, "The given mesh is inverted!");
Jtr(i).Set(std::pow(det / detW, 1./dim), Wideal);
}
}
@@ -864,6 +879,162 @@ void TargetConstructor::ComputeElementTargets(int e_id, const FiniteElement &fe,
}
}
void AnalyticAdaptTC::SetAnalyticTargetSpec(Coefficient *sspec,
VectorCoefficient *vspec,
MatrixCoefficient *mspec)
{
scalar_tspec = sspec;
vector_tspec = vspec;
matrix_tspec = mspec;
}
void AnalyticAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const
{
DenseMatrix point_mat;
point_mat.UseExternalData(elfun.GetData(), fe.GetDof(), fe.GetDim());
switch (target_type)
{
case GIVEN_FULL:
{
MFEM_VERIFY(matrix_tspec != NULL,
"Target type GIVEN_FULL requires a MatrixCoefficient.");
IsoparametricTransformation Tpr;
Tpr.SetFE(&fe);
Tpr.ElementNo = e_id;
Tpr.GetPointMat().Transpose(point_mat);
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
Tpr.SetIntPoint(&ip);
matrix_tspec->Eval(Jtr(i), Tpr, ip);
}
break;
}
default:
MFEM_ABORT("Incompatible target type for analytic adaptation!");
}
}
#ifdef MFEM_USE_MPI
void DiscreteAdaptTC::SetParDiscreteTargetSpec(ParGridFunction &tspec)
{
target_spec.SetSize(tspec.Size());
target_spec = tspec;
tspec_fes = tspec.FESpace();
// Default evaluator is based on CG advection.
if (adapt_eval == NULL) { adapt_eval = new AdvectorCG; }
adapt_eval->SetParMetaInfo(*tspec.ParFESpace()->GetParMesh(),
*tspec.FESpace()->FEColl(),
tspec.FESpace()->GetVDim());
adapt_eval->SetInitialField
(*tspec.FESpace()->GetMesh()->GetNodes(), target_spec);
}
#endif
void DiscreteAdaptTC::SetSerialDiscreteTargetSpec(GridFunction &tspec)
{
target_spec.SetSize(tspec.Size());
target_spec = tspec;
tspec_fes = tspec.FESpace();
// Default evaluator is based on CG advection.
if (adapt_eval == NULL) { adapt_eval = new AdvectorCG; }
adapt_eval->SetSerialMetaInfo(*tspec.FESpace()->GetMesh(),
*tspec.FESpace()->FEColl(),
tspec.FESpace()->GetVDim());
adapt_eval->SetInitialField
(*tspec.FESpace()->GetMesh()->GetNodes(), target_spec);
}
void DiscreteAdaptTC::UpdateTargetSpecification(const Vector &new_x)
{
MFEM_VERIFY(target_spec.Size() > 0, "Target specification is not set!");
adapt_eval->ComputeAtNewPosition(new_x, target_spec);
}
void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const
{
MFEM_VERIFY(tspec_fes, "A call to SetDiscreteTargerSpec() is needed.");
switch (target_type)
{
case IDEAL_SHAPE_GIVEN_SIZE:
{
const DenseMatrix &Wideal =
Geometries.GetGeomToPerfGeomJac(fe.GetGeomType());
const int dim = Wideal.Height(),
ntspec_dofs = tspec_fes->GetFE(0)->GetDof();
Vector shape(ntspec_dofs), tspec_vals(ntspec_dofs);
Array<int> dofs;
tspec_fes->GetElementDofs(e_id, dofs);
target_spec.GetSubVector(dofs, tspec_vals);
const double min_size = tspec_vals.Min();
MFEM_ASSERT(min_size > 0.0,
"Non-positive size propagated in the target definition.");
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
const double size = std::max(shape * tspec_vals, min_size);
Jtr(i).Set(std::pow(size / Wideal.Det(), 1.0/dim), Wideal);
}
break;
}
default:
MFEM_ABORT("Incompatible target type for analytic adaptation!");
}
}
void AdaptivityEvaluator::SetSerialMetaInfo(const Mesh &m,
const FiniteElementCollection &fec,
int num_comp)
{
delete fes;
delete mesh;
mesh = new Mesh(m, true);
fes = new FiniteElementSpace(mesh, &fec, num_comp);
}
#ifdef MFEM_USE_MPI
void AdaptivityEvaluator::SetParMetaInfo(const ParMesh &m,
const FiniteElementCollection &fec,
int num_comp)
{
delete pfes;
delete pmesh;
pmesh = new ParMesh(m, true);
pfes = new ParFiniteElementSpace(pmesh, &fec, num_comp);
}
#endif
AdaptivityEvaluator::~AdaptivityEvaluator()
{
delete fes;
delete mesh;
#ifdef MFEM_USE_MPI
delete pfes;
delete pmesh;
#endif
}
void TMOP_Integrator::EnableLimiting(const GridFunction &n0,
const GridFunction &dist, Coefficient &w0,
TMOP_LimiterFunction *lfunc)
@@ -921,7 +1092,7 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
energy = 0.0;
DenseTensor Jtr(dim, dim, ir->GetNPoints());
targetC->ComputeElementTargets(T.ElementNo, el, *ir, Jtr);
targetC->ComputeElementTargets(T.ElementNo, el, *ir, elfun, Jtr);
// Limited case.
Vector shape, p, p0, d_vals;
@@ -990,6 +1161,7 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
energy += weight * val;
}
delete Tpr;
return energy;
}
@@ -1016,7 +1188,7 @@ void TMOP_Integrator::AssembleElementVector(const FiniteElement &el,
elvect = 0.0;
DenseTensor Jtr(dim, dim, ir->GetNPoints());
targetC->ComputeElementTargets(T.ElementNo, el, *ir, Jtr);
targetC->ComputeElementTargets(T.ElementNo, el, *ir, elfun, Jtr);
// Limited case.
DenseMatrix pos0;
@@ -1072,6 +1244,8 @@ void TMOP_Integrator::AssembleElementVector(const FiniteElement &el,
P *= weight_m;
AddMultABt(DS, P, PMatO);
// TODO: derivatives of adaptivity-based targets.
if (coeff0)
{
el.CalcShape(ip, shape);
@@ -1107,7 +1281,7 @@ void TMOP_Integrator::AssembleElementGrad(const FiniteElement &el,
elmat = 0.0;
DenseTensor Jtr(dim, dim, ir->GetNPoints());
targetC->ComputeElementTargets(T.ElementNo, el, *ir, Jtr);
targetC->ComputeElementTargets(T.ElementNo, el, *ir, elfun, Jtr);
// Limited case.
DenseMatrix pos0, grad_grad;
@@ -1160,6 +1334,8 @@ void TMOP_Integrator::AssembleElementGrad(const FiniteElement &el,
metric->AssembleH(Jpt, DS, weight_m, elmat);
// TODO: derivatives of adaptivity-based targets.
if (coeff0)
{
el.CalcShape(ip, shape);
@@ -1234,11 +1410,12 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
for (int i = 0; i < fes->GetNE(); i++)
{
fe = fes->GetFE(i);
targetC->ComputeElementTargets(i, *fe, *ir, Jtr);
fes->GetElementVDofs(i, vdofs);
x.GetSubVector(vdofs, x_vals);
PMatI.UseExternalData(x_vals.GetData(), dof, dim);
targetC->ComputeElementTargets(i, *fe, *ir, x_vals, Jtr);
for (int i = 0; i < ir->GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
@@ -1274,9 +1451,6 @@ void InterpolateTMOP_QualityMetric(TMOP_QualityMetric &metric,
const IntegrationRule &ir = metric_gf.FESpace()->GetFE(i)->GetNodes();
const int nsp = ir.GetNPoints(), dof = fe_pos.GetDof();
W.SetSize(dim, dim, nsp);
tc.ComputeElementTargets(i, fe_pos, ir, W);
dshape.SetSize(dof, dim);
pos.SetSize(dof, dim);
posV.SetDataAndSize(pos.Data(), dof * dim);
@@ -1285,6 +1459,9 @@ void InterpolateTMOP_QualityMetric(TMOP_QualityMetric &metric,
nodes.FESpace()->GetElementVDofs(i, pos_dofs);
nodes.GetSubVector(pos_dofs, posV);
W.SetSize(dim, dim, nsp);
tc.ComputeElementTargets(i, fe_pos, ir, posV, W);
for (int j = 0; j < nsp; j++)
{
const DenseMatrix &Wj = W(j);
+124 -3
View File
@@ -12,7 +12,6 @@
#ifndef MFEM_TMOP_HPP
#define MFEM_TMOP_HPP
#include "../config/config.hpp"
#include "../linalg/invariants.hpp"
#include "nonlininteg.hpp"
@@ -514,6 +513,51 @@ public:
virtual ~TMOP_QuadraticLimiter() { }
};
class FiniteElementCollection;
class FiniteElementSpace;
class ParFiniteElementSpace;
class AdaptivityEvaluator
{
protected:
// Owned.
Mesh *mesh;
FiniteElementSpace *fes;
#ifdef MFEM_USE_MPI
// Owned.
ParMesh *pmesh;
ParFiniteElementSpace *pfes;
#endif
public:
AdaptivityEvaluator() : mesh(NULL), fes(NULL)
{
#ifdef MFEM_USE_MPI
pmesh = NULL;
pfes = NULL;
#endif
}
virtual ~AdaptivityEvaluator();
/** Specifies the Mesh and FiniteElementCollection of the solution that will
be evaluated. The given mesh will be copied into the internal object. */
void SetSerialMetaInfo(const Mesh &m,
const FiniteElementCollection &fec, int num_comp);
#ifdef MFEM_USE_MPI
/// Parallel version of SetSerialMetaInfo.
void SetParMetaInfo(const ParMesh &m,
const FiniteElementCollection &fec, int num_comp);
#endif
// TODO use GridFunctions to make clear it's on the ldofs?
virtual void SetInitialField(const Vector &init_nodes,
const Vector &init_field) = 0;
virtual void ComputeAtNewPosition(const Vector &new_nodes,
Vector &new_field) = 0;
};
/** @brief Base class representing target-matrix construction algorithms for
mesh optimization via the target-matrix optimization paradigm (TMOP). */
@@ -538,9 +582,11 @@ public:
IDEAL_SHAPE_GIVEN_SIZE, /**<
Ideal shape, given size/volume; the given nodes define the target
volume at all quadrature points. */
GIVEN_SHAPE_AND_SIZE /**<
GIVEN_SHAPE_AND_SIZE, /**<
Given shape, given size/volume; the given nodes define the exact target
Jacobian matrix at all quadrature points. */
GIVEN_FULL /**<
Full target tensor is specified at every quadrature point. */
};
protected:
@@ -589,14 +635,89 @@ public:
void SetVolumeScale(double vol_scale) { volume_scale = vol_scale; }
/** @brief Given an element and quadrature rule, computes ref->target
transformation Jacobians for each quadrature point in the element. */
transformation Jacobians for each quadrature point in the element.
The physical positions of the element's nodes are given by @a elfun. */
virtual void ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const;
};
class AnalyticAdaptTC : public TargetConstructor
{
protected:
// Analytic target specification.
Coefficient *scalar_tspec;
VectorCoefficient *vector_tspec;
MatrixCoefficient *matrix_tspec;
public:
AnalyticAdaptTC(TargetType ttype)
: TargetConstructor(ttype),
scalar_tspec(NULL), vector_tspec(NULL), matrix_tspec(NULL) { }
virtual void SetAnalyticTargetSpec(Coefficient *sspec,
VectorCoefficient *vspec,
MatrixCoefficient *mspec);
/** @brief Given an element and quadrature rule, computes ref->target
transformation Jacobians for each quadrature point in the element.
The physical positions of the element's nodes are given by @a elfun. */
virtual void ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const;
};
class ParGridFunction;
class DiscreteAdaptTC : public TargetConstructor
{
protected:
// Discrete target specification.
// Data is owned, updated by UpdateTargetSpecification.
Vector target_spec;
// Note: do not use the Nodes of this space as they may not be on the
// positions corresponding to the values of tspec.
const FiniteElementSpace *tspec_fes;
// Evaluation of the discrete target specification on different meshes.
// Owned.
AdaptivityEvaluator *adapt_eval;
public:
DiscreteAdaptTC(TargetType ttype)
: TargetConstructor(ttype),
target_spec(), tspec_fes(NULL), adapt_eval(NULL) { }
virtual ~DiscreteAdaptTC() { delete adapt_eval; }
virtual void SetSerialDiscreteTargetSpec(GridFunction &tspec);
#ifdef MFEM_USE_MPI
virtual void SetParDiscreteTargetSpec(ParGridFunction &tspec);
#endif
/** Used to update the target specification after the mesh has changed. The
new mesh positions are given by new_x. */
void UpdateTargetSpecification(const Vector &new_x);
void SetAdaptivityEvaluator(AdaptivityEvaluator *ae)
{
if (adapt_eval) { delete adapt_eval; }
adapt_eval = ae;
}
/** @brief Given an element and quadrature rule, computes ref->target
transformation Jacobians for each quadrature point in the element.
The physical positions of the element's nodes are given by @a elfun.
Note that this function assumes that UpdateTargetSpecification() has
been called with the position vector corresponding to @a elfun. */
virtual void ComputeElementTargets(int e_id, const FiniteElement &fe,
const IntegrationRule &ir,
const Vector &elfun,
DenseTensor &Jtr) const;
};
/** @brief A TMOP integrator class based on any given TMOP_QualityMetric and
TargetConstructor.
+518
View File
@@ -0,0 +1,518 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#include "tmop_tools.hpp"
#include "nonlinearform.hpp"
#include "pnonlinearform.hpp"
#include "../general/osockstream.hpp"
namespace mfem
{
using namespace mfem;
void AdvectorCG::SetInitialField(const Vector &init_nodes,
const Vector &init_field)
{
nodes0 = init_nodes;
field0 = init_field;
}
void AdvectorCG::ComputeAtNewPosition(const Vector &new_nodes,
Vector &new_field)
{
int myid = 0;
Mesh *m = mesh;
#ifdef MFEM_USE_MPI
if (pfes) { MPI_Comm_rank(pfes->GetComm(), &myid); }
if (pmesh) { m = pmesh; }
#endif
MFEM_VERIFY(m != NULL, "No mesh has been given to the AdaptivityEvaluator.");
// This will be used to move the positions.
GridFunction *mesh_nodes = m->GetNodes();
*mesh_nodes = nodes0;
new_field = field0;
// Velocity of the positions.
GridFunction u(mesh_nodes->FESpace());
subtract(new_nodes, nodes0, u);
TimeDependentOperator *oper = NULL;
// This must be the fes of the ind, associated with the object's mesh.
if (fes) { oper = new SerialAdvectorCGOper(nodes0, u, *fes); }
#ifdef MFEM_USE_MPI
else if (pfes) { oper = new ParAdvectorCGOper(nodes0, u, *pfes); }
#endif
MFEM_VERIFY(oper != NULL,
"No FE space has been given to the AdaptivityEvaluator.");
ode_solver.Init(*oper);
// Compute some time step [mesh_size / speed].
double min_h = std::numeric_limits<double>::infinity();
for (int i = 0; i < m->GetNE(); i++)
{
min_h = std::min(min_h, m->GetElementSize(i));
}
double v_max = 0.0;
const int s = u.FESpace()->GetVSize() / 2;
for (int i = 0; i < s; i++)
{
const double vel = u(i) * u(i) + u(i+s) * u(i+s);
v_max = std::max(v_max, vel);
}
if (v_max == 0.0)
{
// No need to change the field.
return;
}
v_max = std::sqrt(v_max);
double dt = 0.5 * min_h / v_max;
double glob_dt = dt;
#ifdef MFEM_USE_MPI
if (pfes)
{
MPI_Allreduce(&dt, &glob_dt, 1, MPI_DOUBLE, MPI_MIN, pfes->GetComm());
}
#endif
double t = 0.0;
bool last_step = false;
for (int ti = 1; !last_step; ti++)
{
if (t + glob_dt >= 1.0)
{
#ifdef MFEM_DEBUG
if (myid == 0)
{
mfem::out << "Remap took " << ti << " steps." << std::endl;
}
#endif
glob_dt = 1.0 - t;
last_step = true;
}
ode_solver.Step(new_field, t, glob_dt);
}
// Trim the overshoots and undershoots.
const double minv = field0.Min(), maxv = field0.Max();
for (int i = 0; i < new_field.Size(); i++)
{
if (new_field(i) < minv) { new_field(i) = minv; }
if (new_field(i) > maxv) { new_field(i) = maxv; }
}
nodes0 = new_nodes;
field0 = new_field;
delete oper;
}
SerialAdvectorCGOper::SerialAdvectorCGOper(const Vector &x_start,
GridFunction &vel,
FiniteElementSpace &fes)
: TimeDependentOperator(fes.GetVSize()),
x0(x_start), x_now(*fes.GetMesh()->GetNodes()),
u(vel), u_coeff(&u), M(&fes), K(&fes)
{
ConvectionIntegrator *Kinteg = new ConvectionIntegrator(u_coeff);
K.AddDomainIntegrator(Kinteg);
K.Assemble(0);
K.Finalize(0);
MassIntegrator *Minteg = new MassIntegrator;
M.AddDomainIntegrator(Minteg);
M.Assemble();
M.Finalize();
}
void SerialAdvectorCGOper::Mult(const Vector &ind, Vector &di_dt) const
{
// Move the mesh.
const double t = GetTime();
add(x0, t, u, x_now);
// Assemble on the new mesh.
K.BilinearForm::operator=(0.0);
K.Assemble();
Vector rhs(K.Size());
K.Mult(ind, rhs);
M.BilinearForm::operator=(0.0);
M.Assemble();
di_dt = 0.0;
CGSolver lin_solver;
DSmoother prec;
lin_solver.SetPreconditioner(prec);
lin_solver.SetOperator(M.SpMat());
lin_solver.SetRelTol(1e-12); lin_solver.SetAbsTol(0.0);
lin_solver.SetMaxIter(100);
lin_solver.SetPrintLevel(0);
lin_solver.Mult(rhs, di_dt);
}
#ifdef MFEM_USE_MPI
ParAdvectorCGOper::ParAdvectorCGOper(const Vector &x_start,
GridFunction &vel,
ParFiniteElementSpace &pfes)
: TimeDependentOperator(pfes.GetVSize()),
x0(x_start), x_now(*pfes.GetMesh()->GetNodes()),
u(vel), u_coeff(&u), M(&pfes), K(&pfes)
{
ConvectionIntegrator *Kinteg = new ConvectionIntegrator(u_coeff);
K.AddDomainIntegrator(Kinteg);
K.Assemble(0);
K.Finalize(0);
MassIntegrator *Minteg = new MassIntegrator;
M.AddDomainIntegrator(Minteg);
M.Assemble();
M.Finalize();
}
void ParAdvectorCGOper::Mult(const Vector &ind, Vector &di_dt) const
{
// Move the mesh.
const double t = GetTime();
add(x0, t, u, x_now);
// Assemble on the new mesh.
K.BilinearForm::operator=(0.0);
K.Assemble();
ParGridFunction rhs(K.ParFESpace());
K.Mult(ind, rhs);
M.BilinearForm::operator=(0.0);
M.Assemble();
HypreParVector *RHS = rhs.ParallelAssemble();
HypreParVector X(K.ParFESpace());
X = 0.0;
HypreParMatrix *Mh = M.ParallelAssemble();
CGSolver lin_solver(M.ParFESpace()->GetParMesh()->GetComm());
HypreSmoother prec;
prec.SetType(HypreSmoother::Jacobi, 1);
lin_solver.SetPreconditioner(prec);
lin_solver.SetOperator(*Mh);
lin_solver.SetRelTol(1e-8);
lin_solver.SetAbsTol(0.0);
lin_solver.SetMaxIter(100);
lin_solver.SetPrintLevel(0);
lin_solver.Mult(*RHS, X);
K.ParFESpace()->GetProlongationMatrix()->Mult(X, di_dt);
delete Mh;
delete RHS;
}
#endif
double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
const Vector &b) const
{
const FiniteElementSpace *fes = NULL;
double energy_in = 0.0;
#ifdef MFEM_USE_MPI
const ParNonlinearForm *p_nlf = dynamic_cast<const ParNonlinearForm *>(oper);
MFEM_VERIFY(!(parallel && p_nlf == NULL), "Invalid Operator subclass.");
if (parallel)
{
fes = p_nlf->FESpace();
energy_in = p_nlf->GetEnergy(x);
}
#endif
const bool serial = !parallel;
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
MFEM_VERIFY(!(serial && nlf == NULL), "Invalid Operator subclass.");
if (serial)
{
fes = nlf->FESpace();
energy_in = nlf->GetEnergy(x);
}
const bool have_b = (b.Size() == Height());
const int NE = fes->GetMesh()->GetNE(), dim = fes->GetFE(0)->GetDim(),
dof = fes->GetFE(0)->GetDof(), nsp = ir.GetNPoints();
Array<int> xdofs(dof * dim);
DenseMatrix Jpr(dim), dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
Vector x_out(x.Size()), x_out_loc(fes->GetVSize());
bool x_out_ok = false;
double scale = 1.0, energy_out;
double norm0 = Norm(r);
// Decreases the scaling of the update until the new mesh is valid.
for (int i = 0; i < 12; i++)
{
add(x, -scale, c, x_out);
if (serial)
{
const SparseMatrix *cP = fes->GetConformingProlongation();
if (!cP) {x_out_loc.SetData(x_out.GetData());}
else {cP->Mult(x_out,x_out_loc);}
energy_out = nlf->GetGridFunctionEnergy(x_out_loc);
}
#ifdef MFEM_USE_MPI
else
{
fes->GetProlongationMatrix()->Mult(x_out, x_out_loc);
energy_out = p_nlf->GetParGridFunctionEnergy(x_out_loc);
}
#endif
if (energy_out > 1.2*energy_in || std::isnan(energy_out) != 0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Increasing energy.\n"; }
scale *= 0.5; continue;
}
int jac_ok = 1;
for (int i = 0; i < NE; i++)
{
fes->GetElementVDofs(i, xdofs);
x_out_loc.GetSubVector(xdofs, posV);
for (int j = 0; j < nsp; j++)
{
fes->GetFE(i)->CalcDShape(ir.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
if (Jpr.Det() <= 0.0) { jac_ok = 0; goto break2; }
}
}
break2:
int jac_ok_all = jac_ok;
#ifdef MFEM_USE_MPI
if (parallel)
{
MPI_Allreduce(&jac_ok, &jac_ok_all, 1, MPI_INT, MPI_LAND,
p_nlf->ParFESpace()->GetComm());
}
#endif
if (jac_ok_all == 0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Neg det(J) found.\n"; }
scale *= 0.5; continue;
}
oper->Mult(x_out, r);
if (have_b) { r -= b; }
double norm = Norm(r);
if (norm > 1.2*norm0)
{
if (print_level >= 0)
{ mfem::out << "Scale = " << scale << " Norm increased.\n"; }
scale *= 0.5; continue;
}
else { x_out_ok = true; break; }
}
if (print_level >= 0)
{
mfem::out << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling.\n";
}
if (x_out_ok == false) { scale = 0.0; }
return scale;
}
void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
{
if (discr_tc)
{
if (parallel)
{
#ifdef MFEM_USE_MPI
const ParNonlinearForm *nlf =
dynamic_cast<const ParNonlinearForm *>(oper);
Vector x_loc(nlf->ParFESpace()->GetVSize());
nlf->ParFESpace()->GetProlongationMatrix()->Mult(x, x_loc);
discr_tc->UpdateTargetSpecification(x_loc);
#endif
}
else { discr_tc->UpdateTargetSpecification(x); }
}
}
double TMOPDescentNewtonSolver::ComputeScalingFactor(const Vector &x,
const Vector &b) const
{
const FiniteElementSpace *fes = NULL;
double energy_in = 0.0;
#ifdef MFEM_USE_MPI
const ParNonlinearForm *p_nlf = dynamic_cast<const ParNonlinearForm *>(oper);
MFEM_VERIFY(!(parallel && p_nlf == NULL), "Invalid Operator subclass.");
if (parallel)
{
fes = p_nlf->FESpace();
energy_in = p_nlf->GetEnergy(x);
}
#endif
const bool serial = !parallel;
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
MFEM_VERIFY(!(serial && nlf == NULL), "Invalid Operator subclass.");
if (serial)
{
fes = nlf->FESpace();
energy_in = nlf->GetEnergy(x);
}
const int NE = fes->GetMesh()->GetNE(), dim = fes->GetFE(0)->GetDim(),
dof = fes->GetFE(0)->GetDof(), nsp = ir.GetNPoints();
Array<int> xdofs(dof * dim);
DenseMatrix Jpr(dim), dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
Vector x_loc(fes->GetVSize());
double min_detJ = infinity();
for (int i = 0; i < NE; i++)
{
fes->GetElementVDofs(i, xdofs);
x_loc.GetSubVector(xdofs, posV);
for (int j = 0; j < nsp; j++)
{
fes->GetFE(i)->CalcDShape(ir.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
min_detJ = std::min(min_detJ, Jpr.Det());
}
}
double min_detJ_all = min_detJ;
#ifdef MFEM_USE_MPI
if (parallel)
{
MPI_Allreduce(&min_detJ, &min_detJ_all, 1, MPI_DOUBLE, MPI_MIN,
p_nlf->ParFESpace()->GetComm());
}
#endif
if (print_level >= 0)
{
mfem::out << "Minimum det(J) = " << min_detJ_all << '\n';
}
Vector x_out(x.Size());
bool x_out_ok = false;
double scale = 1.0, energy_out;
for (int i = 0; i < 7; i++)
{
add(x, -scale, c, x_out);
if (serial)
{
const SparseMatrix *cP = fes->GetConformingProlongation();
if (!cP) {x_loc.SetData(x_out.GetData());}
else {cP->Mult(x_out,x_loc);}
energy_out = nlf->GetGridFunctionEnergy(x_loc);
}
#ifdef MFEM_USE_MPI
else
{
fes->GetProlongationMatrix()->Mult(x_out, x_loc);
energy_out = p_nlf->GetParGridFunctionEnergy(x_loc);
}
#endif
if (energy_out > energy_in || std::isnan(energy_out) != 0)
{
scale *= 0.5;
}
else { x_out_ok = true; break; }
}
if (print_level >= 0)
{
mfem::out << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling.\n";
}
if (x_out_ok == false) { return 0.0; }
return scale;
}
void TMOPDescentNewtonSolver::ProcessNewState(const Vector &x) const
{
if (discr_tc)
{
if (parallel)
{
#ifdef MFEM_USE_MPI
const ParNonlinearForm *nlf =
dynamic_cast<const ParNonlinearForm *>(oper);
Vector x_loc(nlf->ParFESpace()->GetVSize());
nlf->ParFESpace()->GetProlongationMatrix()->Mult(x, x_loc);
discr_tc->UpdateTargetSpecification(x_loc);
#endif
}
else { discr_tc->UpdateTargetSpecification(x); }
}
}
#ifdef MFEM_USE_MPI
// Metric values are visualized by creating an L2 finite element functions and
// computing the metric values at the nodes.
void vis_tmop_metric_p(int order, TMOP_QualityMetric &qm,
const TargetConstructor &tc, ParMesh &pmesh,
char *title, int position)
{
L2_FECollection fec(order, pmesh.Dimension(), BasisType::GaussLobatto);
ParFiniteElementSpace fes(&pmesh, &fec, 1);
ParGridFunction metric(&fes);
InterpolateTMOP_QualityMetric(qm, tc, pmesh, metric);
socketstream sock;
if (pmesh.GetMyRank() == 0)
{
sock.open("localhost", 19916);
sock << "solution\n";
}
pmesh.PrintAsOne(sock);
metric.SaveAsOne(sock);
if (pmesh.GetMyRank() == 0)
{
sock << "window_title '"<< title << "'\n"
<< "window_geometry "
<< position << " " << 0 << " " << 600 << " " << 600 << "\n"
<< "keys jRmclA\n";
}
}
#endif
// Metric values are visualized by creating an L2 finite element functions and
// computing the metric values at the nodes.
void vis_tmop_metric_s(int order, TMOP_QualityMetric &qm,
const TargetConstructor &tc, Mesh &mesh,
char *title, int position)
{
L2_FECollection fec(order, mesh.Dimension(), BasisType::GaussLobatto);
FiniteElementSpace fes(&mesh, &fec, 1);
GridFunction metric(&fes);
InterpolateTMOP_QualityMetric(qm, tc, mesh, metric);
osockstream sock(19916, "localhost");
sock << "solution\n";
mesh.Print(sock);
metric.Save(sock);
sock.send();
sock << "window_title '"<< title << "'\n"
<< "window_geometry "
<< position << " " << 0 << " " << 600 << " " << 600 << "\n"
<< "keys jRmclA\n";
}
}
+140
View File
@@ -0,0 +1,140 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#ifndef MFEM_TMOP_TOOLS_HPP
#define MFEM_TMOP_TOOLS_HPP
#include "bilinearform.hpp"
#include "pbilinearform.hpp"
#include "tmop.hpp"
namespace mfem
{
// Performs the full remap advection loop.
class AdvectorCG : public AdaptivityEvaluator
{
private:
RK4Solver ode_solver;
Vector nodes0;
Vector field0;
public:
AdvectorCG() : AdaptivityEvaluator(), ode_solver(), nodes0(), field0() { }
virtual void SetInitialField(const Vector &init_nodes,
const Vector &init_field);
virtual void ComputeAtNewPosition(const Vector &new_nodes,
Vector &new_field);
};
/// Performs a single remap advection step in serial.
class SerialAdvectorCGOper : public TimeDependentOperator
{
protected:
const Vector &x0;
Vector &x_now;
GridFunction &u;
VectorGridFunctionCoefficient u_coeff;
mutable BilinearForm M, K;
public:
/** Here @a fes is the FESpace of the function that will be moved. Note
that Mult() moves the nodes of the mesh corresponding to @a fes. */
SerialAdvectorCGOper(const Vector &x_start, GridFunction &vel,
FiniteElementSpace &fes);
virtual void Mult(const Vector &ind, Vector &di_dt) const;
};
#ifdef MFEM_USE_MPI
/// Performs a single remap advection step in parallel.
class ParAdvectorCGOper : public TimeDependentOperator
{
protected:
const Vector &x0;
Vector &x_now;
GridFunction &u;
VectorGridFunctionCoefficient u_coeff;
mutable ParBilinearForm M, K;
public:
/** Here @a pfes is the ParFESpace of the function that will be moved. Note
that Mult() moves the nodes of the mesh corresponding to @a pfes. */
ParAdvectorCGOper(const Vector &x_start, GridFunction &vel,
ParFiniteElementSpace &pfes);
virtual void Mult(const Vector &ind, Vector &di_dt) const;
};
#endif
class TMOPNewtonSolver : public NewtonSolver
{
private:
bool parallel;
// Quadrature points that are checked for negative Jacobians etc.
const IntegrationRule &ir;
mutable DiscreteAdaptTC *discr_tc;
public:
#ifdef MFEM_USE_MPI
TMOPNewtonSolver(MPI_Comm comm, const IntegrationRule &irule)
: NewtonSolver(comm), parallel(true), ir(irule), discr_tc(NULL) { }
#endif
TMOPNewtonSolver(const IntegrationRule &irule)
: NewtonSolver(), parallel(false), ir(irule), discr_tc(NULL) { }
void SetDiscreteAdaptTC(DiscreteAdaptTC *tc) { discr_tc = tc; }
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const;
virtual void ProcessNewState(const Vector &x) const;
};
/// Allows negative Jacobians. Used for untangling.
class TMOPDescentNewtonSolver : public NewtonSolver
{
private:
bool parallel;
// Quadrature points that are checked for negative Jacobians etc.
const IntegrationRule &ir;
mutable DiscreteAdaptTC *discr_tc;
public:
#ifdef MFEM_USE_MPI
TMOPDescentNewtonSolver(MPI_Comm comm, const IntegrationRule &irule)
: NewtonSolver(comm), parallel(true), ir(irule), discr_tc(NULL) { }
#endif
TMOPDescentNewtonSolver(const IntegrationRule &irule)
: NewtonSolver(), parallel(false), ir(irule), discr_tc(NULL) { }
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const;
virtual void ProcessNewState(const Vector &x) const;
};
void vis_tmop_metric_s(int order, TMOP_QualityMetric &qm,
const TargetConstructor &tc, Mesh &pmesh,
char *title, int position);
#ifdef MFEM_USE_MPI
void vis_tmop_metric_p(int order, TMOP_QualityMetric &qm,
const TargetConstructor &tc, ParMesh &pmesh,
char *title, int position);
#endif
}
#endif
+1 -1
View File
@@ -276,7 +276,7 @@ inline T *Write(Memory<T> &mem, int size, bool on_dev = true)
/** @brief Shortcut to Write(const Memory<T> &mem, int size, false) */
template <typename T>
inline const T *HostWrite(const Memory<T> &mem, int size)
inline T *HostWrite(Memory<T> &mem, int size)
{
return mfem::Write(mem, size, false);
}
+23 -1
View File
@@ -11,7 +11,7 @@
#include "../general/forall.hpp"
#include <cstring> // std::memcpy
#include <cstring> // std::memcpy, std::memcmp
#include <list>
#include <unordered_map>
@@ -57,6 +57,15 @@ MemoryClass operator*(MemoryClass mc1, MemoryClass mc2)
}
// Instantiate Memory<T>::PrintFlags for T = int and T = double.
template void Memory<int>::PrintFlags() const;
template void Memory<double>::PrintFlags() const;
// Instantiate Memory<T>::CompareHostAndDevice for T = int and T = double.
template int Memory<int>::CompareHostAndDevice(int size) const;
template int Memory<double>::CompareHostAndDevice(int size) const;
namespace internal
{
@@ -695,6 +704,19 @@ void MemoryManager::CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
~(dest_on_host ? Mem::VALID_DEVICE : Mem::VALID_HOST);
}
int MemoryManager::CompareHostAndDevice_(void *h_ptr, size_t size,
unsigned flags)
{
void *d_ptr = (flags & Mem::ALIAS) ?
mm.GetAliasDevicePtr(h_ptr, size, false) :
mm.GetDevicePtr(h_ptr, size, false);
char *h_buf = new char[size];
CuMemcpyDtoH(h_buf, d_ptr, size);
int res = std::memcmp(h_ptr, h_buf, size);
delete [] h_buf;
return res;
}
void MemoryPrintFlags(unsigned flags)
{
+30 -1
View File
@@ -377,6 +377,16 @@ public:
/// Copy @a size entries from @a *this to the host pointer @a dest.
/** The given @a size should not exceed the Capacity() of @a *this. */
inline void CopyToHost(T *dest, int size) const;
/// Print the internal flags.
/** This method can be useful for debugging. It is explicitly instantiated
for Memory<T> with T = int and T = double. */
inline void PrintFlags() const;
/// If both the host and the device data are valid, compare their contents.
/** This method can be useful for debugging. It is explicitly instantiated
for Memory<T> with T = int and T = double. */
inline int CompareHostAndDevice(int size) const;
};
@@ -447,6 +457,11 @@ private:
static void CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
std::size_t size, unsigned &dest_flags);
// Compare the contents of the host and the device memory - useful for
// debugging.
static int CompareHostAndDevice_(void *h_ptr, size_t size, unsigned flags);
/// Adds an address in the map
void *Insert(void *ptr, const std::size_t bytes);
@@ -727,10 +742,24 @@ inline void Memory<T>::CopyToHost(T *dest, int size) const
/** @brief Print the state of a Memory object based on its internal flags.
Useful in a debugger. */
Useful in a debugger. See also Memory<T>::PrintFlags(). */
extern void MemoryPrintFlags(unsigned flags);
template <typename T>
inline void Memory<T>::PrintFlags() const
{
MemoryPrintFlags(flags);
}
template <typename T>
inline int Memory<T>::CompareHostAndDevice(int size) const
{
if (!(flags & VALID_HOST) || !(flags & VALID_DEVICE)) { return 0; }
return MemoryManager::CompareHostAndDevice_(h_ptr, size*sizeof(T), flags);
}
/// The (single) global memory manager object
extern MemoryManager mm;
+2 -1
View File
@@ -4456,7 +4456,8 @@ const
int n = SizeI(), ne = SizeK();
const int *I = elem_dof.GetI(), *J = elem_dof.GetJ(), *dofs;
const double *d_col = tdata;
double *yp = y, x_col;
double *yp = y.HostReadWrite();
double x_col;
const double *xp = x;
// the '4' here can be tuned for given platform and compiler
if (n <= 4)
+18 -12
View File
@@ -185,7 +185,7 @@ Vector * HypreParVector::GlobalVector() const
HypreParVector& HypreParVector::operator=(double d)
{
hypre_ParVectorSetConstantValues(x,d);
Vector::operator=(d);
return *this;
}
@@ -198,10 +198,7 @@ HypreParVector& HypreParVector::operator=(const HypreParVector &y)
}
#endif
for (int i = 0; i < size; i++)
{
data[i] = y.data[i];
}
Vector::operator=(y);
return *this;
}
@@ -1024,6 +1021,8 @@ HypreParMatrix * HypreParMatrix::Transpose() const
HYPRE_Int HypreParMatrix::Mult(HypreParVector &x, HypreParVector &y,
double a, double b)
{
x.HostRead();
(b == 0.0) ? y.HostWrite() : y.HostReadWrite();
return hypre_ParCSRMatrixMatvec(a, A, x, b, y);
}
@@ -1035,7 +1034,7 @@ void HypreParMatrix::Mult(double a, const Vector &x, double b, Vector &y) const
<< ", expected size = " << Height());
auto x_data = x.HostRead();
auto y_data = y.HostWrite();
auto y_data = (b == 0.0) ? y.HostWrite() : y.HostReadWrite();
if (X == NULL)
{
X = new HypreParVector(A->comm,
@@ -1067,7 +1066,7 @@ void HypreParMatrix::MultTranspose(double a, const Vector &x,
// Note: x has the dimensions of Y (height), and
// y has the dimensions of X (width)
auto x_data = x.HostRead();
auto y_data = y.HostWrite();
auto y_data = (b == 0.0) ? y.HostWrite() : y.HostReadWrite();
if (X == NULL)
{
X = new HypreParVector(A->comm,
@@ -2009,10 +2008,12 @@ void HypreSmoother::Mult(const HypreParVector &b, HypreParVector &x) const
return;
}
b.HostRead();
if (!iterative_mode)
{
if (type == 0 && relax_times == 1)
{
x.HostWrite();
HYPRE_ParCSRDiagScale(NULL, *A, b, x);
if (relax_weight != 1.0)
{
@@ -2022,6 +2023,7 @@ void HypreSmoother::Mult(const HypreParVector &b, HypreParVector &x) const
}
x = 0.0;
}
x.HostReadWrite();
if (V == NULL)
{
@@ -2071,21 +2073,25 @@ void HypreSmoother::Mult(const Vector &b, Vector &x) const
mfem_error("HypreSmoother::Mult (...) : HypreParMatrix A is missing");
return;
}
auto b_data = b.HostRead();
auto x_data = iterative_mode ? x.HostReadWrite() : x.HostWrite();
if (B == NULL)
{
B = new HypreParVector(A->GetComm(),
A -> GetGlobalNumRows(),
b.GetData(),
const_cast<double*>(b_data),
A -> GetRowStarts());
X = new HypreParVector(A->GetComm(),
A -> GetGlobalNumCols(),
x.GetData(),
x_data,
A -> GetColStarts());
}
else
{
B -> SetData(b.GetData());
X -> SetData(x.GetData());
B -> SetData(const_cast<double*>(b_data));
X -> SetData(x_data);
}
Mult(*B, *X);
@@ -2687,7 +2693,7 @@ void HypreEuclid::SetOperator(const Operator &op)
if (A)
{
MPI_Comm comm;
HYPRE_ParCSRMatrixGetComm(*A, &comm);
HYPRE_ParCSRMatrixGetComm(*new_A, &comm);
ResetEuclidPrecond(comm);
}
+74 -30
View File
@@ -2309,17 +2309,6 @@ void PetscLinearSolver::SetOperator(const Operator &op)
(dynamic_cast<const PetscParMatrix *>(&op));
const Operator *oA = dynamic_cast<const Operator *>(&op);
// Preserve Pmat if already set
KSP ksp = (KSP)obj;
Mat P = NULL;
PetscBool pmat;
ierr = KSPGetOperatorsSet(ksp,NULL,&pmat); PCHKERRQ(ksp,ierr);
if (pmat)
{
ierr = KSPGetOperators(ksp,NULL,&P); PCHKERRQ(ksp,ierr);
ierr = PetscObjectReference((PetscObject)P); PCHKERRQ(ksp,ierr);
}
// update base classes: Operator, Solver, PetscLinearSolver
bool delete_pA = false;
if (!pA)
@@ -2342,6 +2331,7 @@ void PetscLinearSolver::SetOperator(const Operator &op)
MFEM_VERIFY(pA, "Unsupported operation!");
// Set operators into PETSc KSP
KSP ksp = (KSP)obj;
Mat A = pA->A;
if (operatorset)
{
@@ -2362,15 +2352,7 @@ void PetscLinearSolver::SetOperator(const Operator &op)
wrap = false;
}
}
if (P)
{
ierr = KSPSetOperators(ksp,A,P); PCHKERRQ(ksp,ierr);
ierr = MatDestroy(&P); PCHKERRQ(ksp,ierr);
}
else
{
ierr = KSPSetOperators(ksp,A,A); PCHKERRQ(ksp,ierr);
}
ierr = KSPSetOperators(ksp,A,A); PCHKERRQ(ksp,ierr);
// Update PetscSolver
operatorset = true;
@@ -3229,26 +3211,27 @@ PetscFieldSplitSolver::PetscFieldSplitSolver(MPI_Comm comm, Operator &op,
: PetscPreconditioner(comm,op,prefix)
{
PC pc = (PC)obj;
ierr = PCSetType(pc,PCFIELDSPLIT); PCHKERRQ(pc,ierr);
Mat pA;
ierr = PCGetOperators(pc,&pA,NULL); PCHKERRQ(pc,ierr);
// Check if pA is of type MATNEST
// (this requirement can be removed when we can pass fields).
PetscBool isnest;
ierr = PetscObjectTypeCompare((PetscObject)pA,MATNEST,&isnest);
PCHKERRQ(pA,ierr);
MFEM_VERIFY(isnest,
"PetscFieldSplitSolver needs the matrix in nested format.");
PetscInt nr;
IS *isrow;
ierr = PCSetType(pc,PCFIELDSPLIT); PCHKERRQ(pc,ierr);
ierr = MatNestGetSize(pA,&nr,NULL); PCHKERRQ(pc,ierr);
ierr = PetscCalloc1(nr,&isrow); CCHKERRQ(PETSC_COMM_SELF,ierr);
ierr = MatNestGetISs(pA,isrow,NULL); PCHKERRQ(pc,ierr);
PetscInt nr = 0;
IS *isrow = NULL;
if (isnest) // we now the fields
{
ierr = MatNestGetSize(pA,&nr,NULL); PCHKERRQ(pc,ierr);
ierr = PetscCalloc1(nr,&isrow); CCHKERRQ(PETSC_COMM_SELF,ierr);
ierr = MatNestGetISs(pA,isrow,NULL); PCHKERRQ(pc,ierr);
}
// We need to customize here, before setting the index sets.
// This is because PCFieldSplitSetType customizes the function
// pointers. SubSolver options will be processed during PCApply
Customize();
for (PetscInt i=0; i<nr; i++)
@@ -3616,6 +3599,7 @@ void PetscODESolver::Run(Vector &x, double &t, double &dt, double t_final)
} // namespace mfem
#include "petsc/private/petscimpl.h"
#include "petsc/private/matimpl.h"
// auxiliary functions
static PetscErrorCode __mfem_ts_monitor(TS ts, PetscInt it, PetscReal t, Vec x,
@@ -3771,17 +3755,37 @@ static PetscErrorCode __mfem_ts_ijacobian(TS ts, PetscReal t, Vec x,
pA->EliminateRowsCols(bchandler->GetTDofs(),dummy,dummy);
}
// Get nonzerostate
PetscObjectState nonzerostate;
ierr = MatGetNonzeroState(P,&nonzerostate); CHKERRQ(ierr);
// Avoid unneeded copy of the matrix by hacking
Mat B;
B = pA->ReleaseMat(false);
ierr = MatHeaderReplace(P,&B); CHKERRQ(ierr);
if (delete_pA) { delete pA; }
// Matrix-free case
if (A && A != P)
{
ierr = MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
ierr = MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
}
// When using MATNEST and PCFIELDSPLIT, the second setup of the
// preconditioner fails because MatCreateSubMatrix_Nest does not
// actually return a matrix. Instead, for efficiency reasons,
// it returns a reference to the submatrix. The second time it
// is called, MAT_REUSE_MATRIX is used and MatCreateSubMatrix_Nest
// aborts since the two submatrices are actually different.
// We circumvent this issue by incrementing the nonzero state
// (i.e. PETSc thinks the operator sparsity pattern has changed)
// This does not impact performances in the case of MATNEST
PetscBool isnest;
ierr = PetscObjectTypeCompare((PetscObject)P,MATNEST,&isnest);
CHKERRQ(ierr);
if (isnest) { P->nonzerostate = nonzerostate + 1; }
// Jacobian reusage
ierr = PetscObjectStateGet((PetscObject)P,&ts_ctx->cached_ijacstate);
CHKERRQ(ierr);
@@ -3938,6 +3942,7 @@ static PetscErrorCode __mfem_ts_computesplits(TS ts,PetscReal t,Vec x,Vec xp,
ierr = MatAXPY(*pJxp,-1.0,*pJx,SAME_NONZERO_PATTERN); PCHKERRQ(ts,ierr);
}
// Matrix-free cases
if (Ax && Ax != Jx)
{
ierr = MatAssemblyBegin(Ax,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
@@ -4024,12 +4029,31 @@ static PetscErrorCode __mfem_ts_rhsjacobian(TS ts, PetscReal t, Vec x,
pA->EliminateRowsCols(bchandler->GetTDofs(),dummy,dummy);
}
// Get nonzerostate
PetscObjectState nonzerostate;
ierr = MatGetNonzeroState(P,&nonzerostate); CHKERRQ(ierr);
// Avoid unneeded copy of the matrix by hacking
Mat B;
B = pA->ReleaseMat(false);
ierr = MatHeaderReplace(P,&B); CHKERRQ(ierr);
if (delete_pA) { delete pA; }
// When using MATNEST and PCFIELDSPLIT, the second setup of the
// preconditioner fails because MatCreateSubMatrix_Nest does not
// actually return a matrix. Instead, for efficiency reasons,
// it returns a reference to the submatrix. The second time it
// is called, MAT_REUSE_MATRIX is used and MatCreateSubMatrix_Nest
// aborts since the two submatrices are actually different.
// We circumvent this issue by incrementing the nonzero state
// (i.e. PETSc thinks the operator sparsity pattern has changed)
// This does not impact performances in the case of MATNEST
PetscBool isnest;
ierr = PetscObjectTypeCompare((PetscObject)P,MATNEST,&isnest);
CHKERRQ(ierr);
if (isnest) { P->nonzerostate = nonzerostate + 1; }
// Matrix-free case
if (A && A != P)
{
ierr = MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
@@ -4133,10 +4157,30 @@ static PetscErrorCode __mfem_snes_jacobian(SNES snes, Vec x, Mat A, Mat P,
pA->EliminateRowsCols(bchandler->GetTDofs(),dummy,dummy);
}
// Get nonzerostate
PetscObjectState nonzerostate;
ierr = MatGetNonzeroState(P,&nonzerostate); CHKERRQ(ierr);
// Avoid unneeded copy of the matrix by hacking
Mat B = pA->ReleaseMat(false);
ierr = MatHeaderReplace(P,&B); CHKERRQ(ierr);
if (delete_pA) { delete pA; }
// When using MATNEST and PCFIELDSPLIT, the second setup of the
// preconditioner fails because MatCreateSubMatrix_Nest does not
// actually return a matrix. Instead, for efficiency reasons,
// it returns a reference to the submatrix. The second time it
// is called, MAT_REUSE_MATRIX is used and MatCreateSubMatrix_Nest
// aborts since the two submatrices are actually different.
// We circumvent this issue by incrementing the nonzero state
// (i.e. PETSc thinks the operator sparsity pattern has changed)
// This does not impact performances in the case of MATNEST
PetscBool isnest;
ierr = PetscObjectTypeCompare((PetscObject)P,MATNEST,&isnest);
CHKERRQ(ierr);
if (isnest) { P->nonzerostate = nonzerostate + 1; }
// Matrix-free case
if (A && A != P)
{
ierr = MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
+3
View File
@@ -618,6 +618,8 @@ public:
const std::string &prefix = std::string());
virtual ~PetscLinearSolver();
/// Sets the operator to be used for mat-vec operations and
/// for the construction of the preconditioner
virtual void SetOperator(const Operator &op);
/// Allows to prescribe a different operator (@a pop) to construct
@@ -625,6 +627,7 @@ public:
void SetOperator(const Operator &op, const Operator &pop);
/// Sets the solver to perform preconditioning
/// preserves the linear operator for the mat-vec
void SetPreconditioner(Solver &precond);
/// Application of the solver.
+2
View File
@@ -1311,6 +1311,8 @@ void NewtonSolver::Mult(const Vector &b, Vector &x) const
}
add(x, -c_scale, c, x);
ProcessNewState(x);
oper->Mult(x, r);
if (have_b)
{
+4
View File
@@ -283,6 +283,10 @@ public:
value of 0 indicates a failure, interrupting the Newton iteration. */
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const
{ return 1.0; }
/** @brief This method can be overloaded in derived classes to perform
computations that need knowledge of the newest Newton state. */
virtual void ProcessNewState(const Vector &x) const { }
};
/** Adaptive restarted GMRES.
+4 -2
View File
@@ -2640,9 +2640,11 @@ SparseMatrix &SparseMatrix::operator=(double a)
{
if (Rows == NULL)
{
for (int i = 0, nnz = I[height]; i < nnz; i++)
const int nnz = J.Capacity();
double *h_A = HostWrite(A, nnz);
for (int i = 0; i < nnz; i++)
{
A[i] = a;
h_A[i] = a;
}
}
else
+48
View File
@@ -152,6 +152,54 @@ public:
/// Return the element data, i.e. the array #A, const version.
inline const double *GetData() const { return A; }
// Memory access methods for the #I array.
Memory<int> &GetMemoryI() { return I; }
const Memory<int> &GetMemoryI() const { return I; }
const int *ReadI(bool on_dev = true) const
{ return mfem::Read(I, Height()+1, on_dev); }
int *WriteI(bool on_dev = true)
{ return mfem::Write(I, Height()+1, on_dev); }
int *ReadWriteI(bool on_dev = true)
{ return mfem::ReadWrite(I, Height()+1, on_dev); }
const int *HostReadI() const
{ return mfem::Read(I, Height()+1, false); }
int *HostWriteI()
{ return mfem::Write(I, Height()+1, false); }
int *HostReadWriteI()
{ return mfem::ReadWrite(I, Height()+1, false); }
// Memory access methods for the #J array.
Memory<int> &GetMemoryJ() { return J; }
const Memory<int> &GetMemoryJ() const { return J; }
const int *ReadJ(bool on_dev = true) const
{ return mfem::Read(J, J.Capacity(), on_dev); }
int *WriteJ(bool on_dev = true)
{ return mfem::Write(J, J.Capacity(), on_dev); }
int *ReadWriteJ(bool on_dev = true)
{ return mfem::ReadWrite(J, J.Capacity(), on_dev); }
const int *HostReadJ() const
{ return mfem::Read(J, J.Capacity(), false); }
int *HostWriteJ()
{ return mfem::Write(J, J.Capacity(), false); }
int *HostReadWriteJ()
{ return mfem::ReadWrite(J, J.Capacity(), false); }
// Memory access methods for the #A array.
Memory<double> &GetMemoryData() { return A; }
const Memory<double> &GetMemoryData() const { return A; }
const double *ReadData(bool on_dev = true) const
{ return mfem::Read(A, A.Capacity(), on_dev); }
double *WriteData(bool on_dev = true)
{ return mfem::Write(A, A.Capacity(), on_dev); }
double *ReadWriteData(bool on_dev = true)
{ return mfem::ReadWrite(A, A.Capacity(), on_dev); }
const double *HostReadData() const
{ return mfem::Read(A, A.Capacity(), false); }
double *HostWriteData()
{ return mfem::Write(A, A.Capacity(), false); }
double *HostReadWriteData()
{ return mfem::ReadWrite(A, A.Capacity(), false); }
/// Returns the number of elements in row @a i.
int RowSize(const int i) const;
+5
View File
@@ -134,6 +134,11 @@ SuperLURowLocMatrix::SuperLURowLocMatrix( const HypreParMatrix & hypParMat )
// hypre_CSRMatrix.
hypre_CSRMatrix * csr_op = hypre_MergeDiagAndOffd(parcsr_op);
hypre_CSRMatrixSetDataOwner(csr_op,0);
#if MFEM_HYPRE_VERSION >= 21600
MFEM_VERIFY(csr_op->num_rows < INT_MAX,"SuperLU: number of local rows "
"is too large to store as an integer.");
hypre_CSRMatrixBigJtoJ(csr_op);
#endif
int m = parcsr_op->global_num_rows;
int n = parcsr_op->global_num_cols;
+2 -1
View File
@@ -834,9 +834,10 @@ double Vector::Sum() const
{
double sum = 0.0;
const double *h_data = this->HostRead();
for (int i = 0; i < size; i++)
{
sum += data[i];
sum += h_data[i];
}
return sum;
+20
View File
@@ -140,6 +140,13 @@ public:
@sa NewDataAndSize(). */
inline void NewMemoryAndSize(const Memory<double> &mem, int s, bool own_mem);
/// Reset the Vector to be a reference to a sub-vector of @a base.
inline void MakeRef(Vector &base, int offset, int size);
/** @brief Reset the Vector to be a reference to a sub-vector of @a base
without changing its current size. */
inline void MakeRef(Vector &base, int offset);
/// Set the Vector data (host pointer) ownership flag.
void MakeDataOwner() const { data.SetHostPtrOwner(true); }
@@ -455,6 +462,19 @@ inline void Vector::NewMemoryAndSize(const Memory<double> &mem, int s,
if (!own_mem) { data.ClearOwnerFlags(); }
}
inline void Vector::MakeRef(Vector &base, int offset, int s)
{
data.Delete();
size = s;
data.MakeAlias(base.GetMemory(), offset, s);
}
inline void Vector::MakeRef(Vector &base, int offset)
{
data.Delete();
data.MakeAlias(base.GetMemory(), offset, size);
}
inline void Vector::Destroy()
{
const bool use_dev = data.UseDevice();
+6 -56
View File
@@ -25,7 +25,6 @@
#include <cstring>
#include <ctime>
#include <functional>
#include <set>
// Include the METIS header, if using version 5. If using METIS 4, the needed
// declarations are inlined below, i.e. no header is needed.
@@ -431,6 +430,8 @@ void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
else
{
const FiniteElement *bdr_el = Nodes->FESpace()->GetBE(i);
Nodes->HostRead();
const GridFunction &nodes = *Nodes;
if (bdr_el)
{
Array<int> vdofs;
@@ -441,7 +442,7 @@ void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
{
for (int j = 0; j < n; j++)
{
pm(k,j) = (*Nodes)(vdofs[n*k+j]);
pm(k,j) = nodes(vdofs[n*k+j]);
}
}
ElTr->SetFE(bdr_el);
@@ -493,6 +494,8 @@ void Mesh::GetFaceTransformation(int FaceNo, IsoparametricTransformation *FTr)
else // curved mesh
{
const FiniteElement *face_el = Nodes->FESpace()->GetFaceElement(FaceNo);
Nodes->HostRead();
const GridFunction &nodes = *Nodes;
if (face_el)
{
Array<int> vdofs;
@@ -503,7 +506,7 @@ void Mesh::GetFaceTransformation(int FaceNo, IsoparametricTransformation *FTr)
{
for (int j = 0; j < n; j++)
{
pm(i, j) = (*Nodes)(vdofs[n*i+j]);
pm(i, j) = nodes(vdofs[n*i+j]);
}
}
FTr->SetFE(face_el);
@@ -7095,59 +7098,6 @@ bool Mesh::NonconformingDerefinement(Array<double> &elem_error,
return true;
}
bool Mesh::GeneralDerefinement(Array<int>& derefs)
{
MFEM_VERIFY(ncmesh, "Only supported for non-conforming meshes.");
MFEM_VERIFY(!NURBSext, "Derefinement of NURBS meshes is not supported. "
"Project the NURBS to Nodes first.");
DeleteLazyTables();
if (!derefs.Size()) { return false; }
const Table &dt = ncmesh->GetDerefinementTable();
// map elements to rows in table
Table dtT;
Transpose(dt, dtT);
// Find the set of rows in the refinement table which correspond to
// the specified elements to derefine
std::set<int> set_rows;
for (int i = 0; i < derefs.Size(); i++) {
int el = derefs[i];
const int *row = dtT.GetRow(el);
set_rows.insert(row[0]);
}
Array<int> deref_rows;
std::set<int>::iterator it;
for (it = set_rows.begin(); it != set_rows.end(); ++it) {
deref_rows.Append(*it);
}
ncmesh->Derefine(deref_rows);
Mesh* mesh2 = new Mesh(*ncmesh);
ncmesh->OnMeshUpdated(mesh2);
Swap(*mesh2, false);
delete mesh2;
GenerateNCFaceInfo();
last_operation = Mesh::DEREFINE;
sequence++;
if (Nodes) // update/interpolate mesh curvature
{
Nodes->FESpace()->Update();
Nodes->Update();
}
return true;
}
bool Mesh::DerefineByError(Array<double> &elem_error, double threshold,
int nc_limit, int op)
{
-3
View File
@@ -1103,9 +1103,6 @@ public:
bool RefineByError(const Vector &elem_error, double threshold,
int nonconforming = -1, int nc_limit = 0);
/** Derefine specified elements (and associated children as required) */
bool GeneralDerefinement(Array<int> &el);
/** Derefine the mesh based on an error measure associated with each
element. A derefinement is performed if the sum of errors of its fine
elements is smaller than 'threshold'. If 'nc_limit' > 0, derefinements
+14
View File
@@ -4307,6 +4307,20 @@ int NCMesh::GetElementDepth(int i) const
return depth;
}
int NCMesh::GetElementSizeReduction(int i) const
{
int elem = leaf_elements[i];
int parent, reduction = 1;
while ((parent = elements[elem].parent) != -1)
{
if (elements[parent].ref_type & 1) { reduction *= 2; }
if (elements[parent].ref_type & 2) { reduction *= 2; }
if (elements[parent].ref_type & 4) { reduction *= 2; }
elem = parent;
}
return reduction;
}
void NCMesh::GetElementFacesAttributes(int i,
Array<int> &faces,
Array<int> &fattr) const
+8
View File
@@ -319,13 +319,21 @@ public:
Geometry::Type GetFaceGeometry(int index) const
{ return Geometry::Type(face_geom[index]); }
/// Return the number of root elements.
int GetNumRootElements() { return root_state.Size(); }
/// Return the distance of leaf 'i' from the root.
int GetElementDepth(int i) const;
/** Return the size reduction compared to the root element (ignoring local
stretching and curvature). */
int GetElementSizeReduction(int i) const;
/// Return the faces and face attributes of leaf element 'i'.
void GetElementFacesAttributes(int i, Array<int> &faces,
Array<int> &fattr) const;
/// I/O: Print the "vertex_parents" section of the mesh file (ver. >= 1.1).
void PrintVertexParents(std::ostream &out) const;
+10
View File
@@ -4171,6 +4171,7 @@ void ParMesh::PrintAsOne(std::ostream &out)
"# SQUARE = 3\n"
"# TETRAHEDRON = 4\n"
"# CUBE = 5\n"
"# PRISM = 6\n"
"#\n";
out << "\ndimension\n" << Dim;
@@ -4269,6 +4270,15 @@ void ParMesh::PrintAsOne(std::ostream &out)
{
switch (Dim)
{
case 1:
for (i = 0; i < svert_lvert.Size(); i++)
{
ints.Append(Geometry::POINT);
ints.Append(svert_lvert[i]);
ne++;
}
break;
case 2:
for (i = 0; i < shared_edges.Size(); i++)
{
+131 -63
View File
@@ -41,6 +41,7 @@ VoltaSolver::VoltaSolver(ParMesh & pmesh, int order,
H1FESpace_(NULL),
HCurlFESpace_(NULL),
HDivFESpace_(NULL),
L2FESpace_(NULL),
divEpsGrad_(NULL),
h1Mass_(NULL),
h1SurfMass_(NULL),
@@ -49,20 +50,24 @@ VoltaSolver::VoltaSolver(ParMesh & pmesh, int order,
hCurlHDiv_(NULL),
weakDiv_(NULL),
rhod_(NULL),
l2_vol_int_(NULL),
rt_surf_int_(NULL),
grad_(NULL),
phi_(NULL),
rho_src_(NULL),
rho_(NULL),
sigma_(NULL),
sigma_src_(NULL),
e_(NULL),
d_(NULL),
p_(NULL),
p_src_(NULL),
oneCoef_(1.0),
epsCoef_(&epsCoef),
phiBCCoef_(NULL),
rhoCoef_(NULL),
pCoef_(NULL),
phi_bc_(phi_bc),
rho_src_(rho_src),
p_src_(p_src),
phi_bc_func_(phi_bc),
rho_src_func_(rho_src),
p_src_func_(p_src),
point_charge_params_(point_charges),
point_charges_(0)
{
@@ -76,34 +81,38 @@ VoltaSolver::VoltaSolver(ParMesh & pmesh, int order,
H1FESpace_ = new H1_ParFESpace(pmesh_,order,pmesh_->Dimension());
HCurlFESpace_ = new ND_ParFESpace(pmesh_,order,pmesh_->Dimension());
HDivFESpace_ = new RT_ParFESpace(pmesh_,order,pmesh_->Dimension());
L2FESpace_ = new L2_ParFESpace(pmesh_,order-1,pmesh_->Dimension());
// Select surface attributes for Dirichlet BCs
ess_bdr_.SetSize(pmesh.bdr_attributes.Max());
ess_bdr_ = 0; // Deselect all outer surfaces
for (int i=0; i<dbcs_->Size(); i++)
{
ess_bdr_[(*dbcs_)[i]-1] = 1;
if ((*dbcs_)[i] <= ess_bdr_.Size())
{
ess_bdr_[(*dbcs_)[i]-1] = 1;
}
}
// Setup various coefficients
// Potential on outer surface
if ( phi_bc_ != NULL )
if ( phi_bc_func_ != NULL )
{
phiBCCoef_ = new FunctionCoefficient(*phi_bc_);
phiBCCoef_ = new FunctionCoefficient(*phi_bc_func_);
}
// Volume Charge Density
if ( rho_src_ != NULL )
if ( rho_src_func_ != NULL )
{
rhoCoef_ = new FunctionCoefficient(rho_src_);
rhoCoef_ = new FunctionCoefficient(rho_src_func_);
}
// Polarization
if ( p_src_ != NULL )
if ( p_src_func_ != NULL )
{
pCoef_ = new VectorFunctionCoefficient(pmesh_->SpaceDimension(),
p_src_);
p_src_func_);
}
// Bilinear Forms
@@ -116,15 +125,23 @@ VoltaSolver::VoltaSolver(ParMesh & pmesh, int order,
hCurlHDivEps_ = new ParMixedBilinearForm(HCurlFESpace_,HDivFESpace_);
hCurlHDivEps_->AddDomainIntegrator(new VectorFEMassIntegrator(*epsCoef_));
rhod_ = new ParLinearForm(H1FESpace_);
rhod_ = new ParLinearForm(H1FESpace_);
// Discrete Grad operator
l2_vol_int_ = new ParLinearForm(L2FESpace_);
l2_vol_int_->AddDomainIntegrator(new DomainLFIntegrator(oneCoef_));
rt_surf_int_ = new ParLinearForm(HDivFESpace_);
rt_surf_int_->AddBoundaryIntegrator(new VectorFEBoundaryFluxLFIntegrator);
// Discrete derivative operator
grad_ = new ParDiscreteGradOperator(H1FESpace_, HCurlFESpace_);
div_ = new ParDiscreteDivOperator(HDivFESpace_, L2FESpace_);
// Build grid functions
phi_ = new ParGridFunction(H1FESpace_);
d_ = new ParGridFunction(HDivFESpace_);
e_ = new ParGridFunction(HCurlFESpace_);
rho_ = new ParGridFunction(L2FESpace_);
if ( point_charge_params_.Size() > 0 )
{
@@ -149,29 +166,28 @@ VoltaSolver::VoltaSolver(ParMesh & pmesh, int order,
}
}
if ( rho_src_ )
if ( rho_src_func_ )
{
rho_ = new ParGridFunction(H1FESpace_);
rho_src_ = new ParGridFunction(H1FESpace_);
h1Mass_ = new ParBilinearForm(H1FESpace_);
h1Mass_->AddDomainIntegrator(new MassIntegrator);
}
if ( p_src_ )
if ( p_src_func_ )
{
p_ = new ParGridFunction(HCurlFESpace_);
p_src_ = new ParGridFunction(HCurlFESpace_);
hCurlHDiv_ = new ParMixedBilinearForm(HCurlFESpace_, HDivFESpace_);
hCurlHDiv_->AddDomainIntegrator(new VectorFEMassIntegrator);
weakDiv_ = new ParMixedBilinearForm(HCurlFESpace_, H1FESpace_);
weakDiv_->AddDomainIntegrator(new VectorFEWeakDivergenceIntegrator);
}
if ( nbcs_->Size() > 0 )
{
sigma_ = new ParGridFunction(H1FESpace_);
sigma_src_ = new ParGridFunction(H1FESpace_);
h1SurfMass_ = new ParBilinearForm(H1FESpace_);
h1SurfMass_->AddBoundaryIntegrator(new MassIntegrator);
@@ -185,14 +201,18 @@ VoltaSolver::~VoltaSolver()
delete pCoef_;
delete phi_;
delete rho_src_;
delete rho_;
delete rhod_;
delete sigma_;
delete l2_vol_int_;
delete rt_surf_int_;
delete sigma_src_;
delete d_;
delete e_;
delete p_;
delete p_src_;
delete grad_;
delete div_;
delete divEpsGrad_;
delete h1Mass_;
@@ -205,6 +225,7 @@ VoltaSolver::~VoltaSolver()
delete H1FESpace_;
delete HCurlFESpace_;
delete HDivFESpace_;
delete L2FESpace_;
for (unsigned int i=0; i<point_charges_.size(); i++)
{
@@ -230,11 +251,13 @@ VoltaSolver::PrintSizes()
HYPRE_Int size_h1 = H1FESpace_->GlobalTrueVSize();
HYPRE_Int size_nd = HCurlFESpace_->GlobalTrueVSize();
HYPRE_Int size_rt = HDivFESpace_->GlobalTrueVSize();
HYPRE_Int size_l2 = L2FESpace_->GlobalTrueVSize();
if (myid_ == 0)
{
cout << "Number of H1 unknowns: " << size_h1 << endl;
cout << "Number of H(Curl) unknowns: " << size_nd << endl;
cout << "Number of H(Div) unknowns: " << size_rt << endl;
cout << "Number of L2 unknowns: " << size_l2 << endl;
}
}
@@ -254,9 +277,15 @@ void VoltaSolver::Assemble()
*rhod_ = 0.0;
rhod_->Assemble();
l2_vol_int_->Assemble();
rt_surf_int_->Assemble();
grad_->Assemble();
grad_->Finalize();
div_->Assemble();
div_->Finalize();
if ( h1Mass_ )
{
h1Mass_->Assemble();
@@ -292,15 +321,19 @@ VoltaSolver::Update()
H1FESpace_->Update(false);
HCurlFESpace_->Update(false);
HDivFESpace_->Update(false);
L2FESpace_->Update(false);
// Inform the grid functions that the space has changed.
phi_->Update();
rhod_->Update();
l2_vol_int_->Update();
rt_surf_int_->Update();
d_->Update();
e_->Update();
if ( rho_ ) { rho_->Update(); }
if ( sigma_ ) { sigma_->Update(); }
if ( p_ ) { p_->Update(); }
rho_->Update();
if ( rho_src_ ) { rho_src_->Update(); }
if ( sigma_src_ ) { sigma_src_->Update(); }
if ( p_src_ ) { p_src_->Update(); }
// Inform the bilinear forms that the space has changed.
divEpsGrad_->Update();
@@ -314,6 +347,7 @@ VoltaSolver::Update()
// Inform the other objects that the space has changed.
grad_->Update();
div_->Update();
}
void
@@ -339,40 +373,46 @@ VoltaSolver::Solve()
{
ConstantCoefficient voltage((*dbcv_)[i]);
dbc_bdr_attr = 0;
dbc_bdr_attr[(*dbcs_)[i]-1] = 1;
if ((*dbcs_)[i] <= dbc_bdr_attr.Size())
{
dbc_bdr_attr[(*dbcs_)[i]-1] = 1;
}
phi_->ProjectBdrCoefficient(voltage, dbc_bdr_attr);
}
}
}
// Initialize the volumetric charge density
if ( rho_ )
if ( rho_src_ )
{
rho_->ProjectCoefficient(*rhoCoef_);
h1Mass_->AddMult(*rho_, *rhod_);
rho_src_->ProjectCoefficient(*rhoCoef_);
h1Mass_->AddMult(*rho_src_, *rhod_);
}
// Initialize the Polarization
if ( p_ )
if ( p_src_ )
{
p_->ProjectCoefficient(*pCoef_);
weakDiv_->AddMult(*p_, *rhod_);
p_src_->ProjectCoefficient(*pCoef_);
weakDiv_->AddMult(*p_src_, *rhod_);
}
// Initialize the surface charge density
if ( sigma_ )
if ( sigma_src_ )
{
*sigma_ = 0.0;
*sigma_src_ = 0.0;
Array<int> nbc_bdr_attr(pmesh_->bdr_attributes.Max());
for (int i=0; i<nbcs_->Size(); i++)
{
ConstantCoefficient sigma_coef((*nbcv_)[i]);
nbc_bdr_attr = 0;
nbc_bdr_attr[(*nbcs_)[i]-1] = 1;
sigma_->ProjectBdrCoefficient(sigma_coef, nbc_bdr_attr);
if ((*nbcs_)[i] <= nbc_bdr_attr.Size())
{
nbc_bdr_attr[(*nbcs_)[i]-1] = 1;
}
sigma_src_->ProjectBdrCoefficient(sigma_coef, nbc_bdr_attr);
}
h1SurfMass_->AddMult(*sigma_, *rhod_);
h1SurfMass_->AddMult(*sigma_src_, *rhod_);
}
// Determine the essential BC degrees of freedom
@@ -424,9 +464,9 @@ VoltaSolver::Solve()
ParGridFunction ed(HDivFESpace_);
hCurlHDivEps_->Mult(*e_, ed);
if ( p_ )
if ( p_src_ )
{
hCurlHDiv_->AddMult(*p_, ed, -1.0);
hCurlHDiv_->AddMult(*p_src_, ed, -1.0);
}
HypreParMatrix MassHDiv;
@@ -445,8 +485,27 @@ VoltaSolver::Solve()
hDivMass_->RecoverFEMSolution(D, ed, *d_);
// Compute charge density from rho = Div(D)
div_->Mult(*d_, *rho_);
if (myid_ == 0) { cout << "done." << flush; }
{
// Compute total charge as volume integral of rho
double charge_rho = (*l2_vol_int_)(*rho_);
// Compute total charge as surface integral of D
double charge_D = (*rt_surf_int_)(*d_);
if (myid_ == 0)
{
cout << endl << "Total charge: \n"
<< " Volume integral of charge density: " << charge_rho
<< "\n Surface integral of dielectric flux: " << charge_D
<< endl << flush;
}
}
if (myid_ == 0) { cout << "Solver done. " << endl; }
}
@@ -480,9 +539,10 @@ VoltaSolver::RegisterVisItFields(VisItDataCollection & visit_dc)
visit_dc.RegisterField("Phi", phi_);
visit_dc.RegisterField("D", d_);
visit_dc.RegisterField("E", e_);
if ( rho_ ) { visit_dc.RegisterField("Rho", rho_); }
if ( p_ ) { visit_dc.RegisterField("P", p_); }
if ( sigma_ ) { visit_dc.RegisterField("Sigma", sigma_); }
visit_dc.RegisterField("Rho", rho_);
if ( rho_src_ ) { visit_dc.RegisterField("Rho Source", rho_src_); }
if ( p_src_ ) { visit_dc.RegisterField("P Source", p_src_); }
if ( sigma_src_ ) { visit_dc.RegisterField("Sigma Source", sigma_src_); }
}
void
@@ -515,20 +575,23 @@ VoltaSolver::InitializeGLVis()
socks_["E"] = new socketstream;
socks_["E"]->precision(8);
if ( rho_)
socks_["Rho"] = new socketstream;
socks_["Rho"]->precision(8);
if ( rho_src_ )
{
socks_["Rho"] = new socketstream;
socks_["Rho"]->precision(8);
socks_["RhoSrc"] = new socketstream;
socks_["RhoSrc"]->precision(8);
}
if ( p_)
if ( p_src_ )
{
socks_["P"] = new socketstream;
socks_["P"]->precision(8);
socks_["PSrc"] = new socketstream;
socks_["PSrc"]->precision(8);
}
if ( sigma_)
if ( sigma_src_ )
{
socks_["Sigma"] = new socketstream;
socks_["Sigma"]->precision(8);
socks_["SigmaSrc"] = new socketstream;
socks_["SigmaSrc"]->precision(8);
}
}
@@ -548,31 +611,36 @@ VoltaSolver::DisplayToGLVis()
*phi_, "Electric Potential (Phi)", Wx, Wy, Ww, Wh);
Wx += offx;
VisualizeField(*socks_["E"], vishost, visport,
*e_, "Electric Field (E)", Wx, Wy, Ww, Wh);
Wx += offx;
VisualizeField(*socks_["D"], vishost, visport,
*d_, "Electric Displacement (D)", Wx, Wy, Ww, Wh);
Wx += offx;
VisualizeField(*socks_["E"], vishost, visport,
*e_, "Electric Field (E)", Wx, Wy, Ww, Wh);
VisualizeField(*socks_["Rho"], vishost, visport,
*rho_, "Charge Density", Wx, Wy, Ww, Wh);
Wx = 0; Wy += offy; // next line
if ( rho_ )
if ( rho_src_ )
{
VisualizeField(*socks_["Rho"], vishost, visport,
*rho_, "Charge Density (Rho)", Wx, Wy, Ww, Wh);
VisualizeField(*socks_["RhoSrc"], vishost, visport,
*rho_src_, "Charge Density Source (Rho)", Wx, Wy, Ww, Wh);
Wx += offx;
}
if ( p_ )
if ( p_src_ )
{
VisualizeField(*socks_["P"], vishost, visport,
*p_, "Electric Polarization (P)", Wx, Wy, Ww, Wh);
VisualizeField(*socks_["PSrc"], vishost, visport,
*p_src_, "Electric Polarization Source (P)",
Wx, Wy, Ww, Wh);
Wx += offx;
}
if ( sigma_ )
if ( sigma_src_ )
{
VisualizeField(*socks_["Sigma"], vishost, visport,
*sigma_, "Surface Charge Density (Sigma)", Wx, Wy, Ww, Wh);
VisualizeField(*socks_["SigmaSrc"], vishost, visport,
*sigma_src_, "Surface Charge Density Source (Sigma)",
Wx, Wy, Ww, Wh);
// Wx += offx; // not used
}
if (myid_ == 0) { cout << " done." << endl; }
+19 -10
View File
@@ -26,7 +26,9 @@ namespace mfem
using miniapps::H1_ParFESpace;
using miniapps::ND_ParFESpace;
using miniapps::RT_ParFESpace;
using miniapps::L2_ParFESpace;
using miniapps::ParDiscreteGradOperator;
using miniapps::ParDiscreteDivOperator;
namespace electromagnetics
{
@@ -84,6 +86,7 @@ private:
H1_ParFESpace * H1FESpace_; // Continuous space for phi
ND_ParFESpace * HCurlFESpace_; // Tangentially continuous space for E
RT_ParFESpace * HDivFESpace_; // Normally continuous space for D
L2_ParFESpace * L2FESpace_; // Discontinuous space for rho
ParBilinearForm * divEpsGrad_; // Laplacian operator
ParBilinearForm * h1Mass_; // For Volumetric Charge Density Source
@@ -94,26 +97,32 @@ private:
ParMixedBilinearForm * hCurlHDiv_; // For computing D from E and P
ParMixedBilinearForm * weakDiv_; // For computing the source term from P
ParLinearForm * rhod_; // Dual of Volumetric Charge Density
ParLinearForm * rhod_; // Dual of Volumetric Charge Density Source
ParLinearForm * l2_vol_int_; // Integral of L2 field
ParLinearForm * rt_surf_int_; // Integral of H(Div) field over boundary
ParDiscreteGradOperator * grad_; // For Computing E from phi
ParDiscreteDivOperator * div_; // For Computing rho from D
ParGridFunction * phi_; // Electric Scalar Potential
ParGridFunction * rho_; // Volumetric Charge Density
ParGridFunction * sigma_; // Surface Charge Density
ParGridFunction * e_; // Electric Field
ParGridFunction * d_; // Electric Flux Density (aka Dielectric Flux)
ParGridFunction * p_; // Polarization Field
ParGridFunction * phi_; // Electric Scalar Potential
ParGridFunction * rho_src_; // Volumetric Charge Density Source
ParGridFunction * rho_; // Volumetric Charge Density (Div(D))
ParGridFunction * sigma_src_; // Surface Charge Density Source
ParGridFunction * e_; // Electric Field
ParGridFunction * d_; // Electric Flux Density (aka Dielectric Flux)
ParGridFunction * p_src_; // Polarization Field Source
ConstantCoefficient oneCoef_; // Coefficient equal to 1
Coefficient * epsCoef_; // Dielectric Permittivity Coefficient
Coefficient * phiBCCoef_; // Scalar Potential Boundary Condition
Coefficient * rhoCoef_; // Charge Density Coefficient
VectorCoefficient * pCoef_; // Polarization Vector Field Coefficient
// Source functions
double (*phi_bc_ )(const Vector&); // Scalar Potential BC
double (*rho_src_)(const Vector&); // Volumetric Charge Density
void (*p_src_ )(const Vector&, Vector&); // Polarization Field
double (*phi_bc_func_ )(const Vector&); // Scalar Potential BC
double (*rho_src_func_)(const Vector&); // Volumetric Charge Density
void (*p_src_func_ )(const Vector&, Vector&); // Polarization Field
const Vector & point_charge_params_;
+418
View File
@@ -0,0 +1,418 @@
MFEM mesh v1.1
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
#
dimension
2
elements
28
1 3 0 9 21 12
1 3 12 21 11 7
1 3 21 22 37 25
1 3 25 37 24 11
1 3 37 23 8 24
1 3 22 10 23 37
1 3 9 4 10 21
1 3 4 13 26 10
1 3 13 1 14 26
1 3 26 14 5 15
1 3 38 28 15 29
1 3 27 26 28 38
1 3 10 27 38 23
1 3 23 38 29 8
1 3 8 29 39 33
1 3 33 39 32 18
1 3 39 31 30 32
1 3 29 15 31 39
1 3 15 5 16 30
1 3 30 16 3 17
1 3 18 30 17 6
1 3 34 18 6 19
1 3 40 33 18 36
1 3 24 8 33 40
1 3 11 24 40 35
1 3 35 40 36 34
1 3 7 11 34 20
1 3 20 34 19 2
boundary
16
2 1 0 9
1 1 12 0
1 1 7 12
2 1 9 4
2 1 4 13
2 1 13 1
1 1 1 14
1 1 14 5
1 1 5 16
1 1 16 3
2 1 3 17
2 1 17 6
2 1 6 19
1 1 20 7
2 1 19 2
1 1 2 20
vertex_parents
37
4 0 1
5 1 3
6 2 3
7 0 2
8 4 6
9 0 4
10 4 8
11 7 8
12 0 7
13 1 4
14 1 5
15 5 8
16 3 5
17 3 6
18 6 8
19 2 6
20 2 7
21 9 11
22 10 21
23 8 10
24 8 11
25 11 21
26 13 15
27 10 26
28 15 26
29 8 15
30 15 17
31 15 30
32 18 30
33 8 18
34 11 19
35 11 34
36 18 34
37 22 24
38 27 29
39 29 32
40 24 36
coarse_elements
9
3 2 5 4 3
3 0 6 28 1
3 12 11 10 13
3 7 8 9 30
3 14 17 16 15
3 32 18 19 20
3 24 23 22 25
3 26 34 21 27
3 29 31 33 35
vertices
41
nodes
FiniteElementSpace
FiniteElementCollection: H1_2D_P2
VDim: 2
Ordering: 0
0
1
0
1
0.5
1
0.5
0
0.490917
0.25
0.481659
0.229025
0
0.75
1
0.75002
1
0.75
0.497731
0.25
0
0.230224
0.361824
0.498798
0.358947
0.256374
0.75627
0.609156
0.760931
0.614878
0.754468
0.729327
0.597212
0.479497
0.255854
0.252575
0.374701
0.365809
0.616315
0.620005
0.354453
0.125
0.241186
0.113998
0
0.256374
0.0996625
0
0.297495
0.381624
0.298118
0.249986
0.354661
0.29368
0.249387
0.422671
0.50313
0.416618
0.423212
0.50141
0.375
0.498432
0.361824
0.625
0.750551
0.609156
0.875
1
0.883899
1
0.859365
0.760931
0.66728
0.757422
0.680277
0.604085
0.680261
0.760547
0.611485
0.542955
0.563608
0.556166
0.630915
0.564831
0.496409
0.603763
0.54025
0.492351
0.666944
0.736168
0.668618
0.733944
1
0.878096
0.729327
1
0.875
0.731558
0.597212
0.625
0.509544
0.374701
0.375
0.262228
0.439501
0.435693
0.381762
0.375151
0.303302
0.243334
0.314755
0.256748
0.252575
0.100238
0
0.125
0
0.0884843
0.133844
0.301451
0.319617
0.421308
0.439579
0.366542
0.638837
0.84411
0.887525
0.673669
0.684397
0.547681
0.553932
0.556981
0.569094
0.682474
0.680908
0.877814
0.843209
0.610964
0.363741
0.433969
0.438104
0.317325
0.315253
0.114408
0.0955121
0
0
1
1
0
0.5
1
0.5
0.500169
0
0.230361
0.506286
0.25
0
0.25
0.501817
0.75
1
0.751223
1
0.75
0.245442
0.249774
0.359053
0.482625
0.339768
0.25622
0.246257
0.342759
0.493736
0.737081
0.598044
0.732223
0.628407
0.755429
0.618147
0.736346
0.380657
0.367339
0.619478
0.619469
0
0.112152
0.214709
0.125
0.339768
0.511468
0.375
0.250576
0.312269
0.354505
0.283581
0.425524
0.495609
0.414003
0.367212
0.438493
0.482955
0.243036
0.314443
0
0.130361
0.249774
0
0.1228
0.246257
0
0.125
0.243197
0.375
0.460998
0.342759
0.372076
0.413223
0.487797
0.431307
0.25198
0.290425
0.296938
0.239051
0.353925
0.485506
0.542001
0.613511
0.565178
0.672486
0.738741
0.683983
0.614503
0.662211
0.73167
0.544579
0.625
0.717877
0.598044
0.875
1
0.883366
0.732223
1
0.888992
0.736346
1
0.868768
0.606623
0.739539
0.671004
0.56527
0.611998
0.559039
0.741642
0.68361
0.618147
0.760055
0.625
1
0.875
0.110313
0.337719
0.306391
0.440854
0.431661
0.308523
0.0865872
0.124325
0.133215
0.332392
0.433604
0.313603
0.30387
0.419458
0.563411
0.693606
0.689503
0.553108
0.586083
0.880316
0.866348
0.884747
0.669969
0.569241
0.56415
0.679016
0.617255
0.87398
+210 -196
View File
@@ -32,6 +32,11 @@
// Compile with: make mesh-optimizer
//
// Sample runs:
// Adapted analytic Hessian:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// Adapted discrete size:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 7 -tid 5 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
//
// Blade shape:
// mesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// Blade limited shape:
@@ -48,6 +53,9 @@
// mesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 1000 -ls 2 -li 100 -bnd -qt 1 -qo 8 -cmb
// 3D pinched sphere shape (the mesh is in the mfem/data GitHub repository):
// * mesh-optimizer -m ../../../mfem_data/ball-pert.mesh -o 4 -rs 0 -mid 303 -tid 1 -ni 20 -ls 2 -li 500 -fix-bnd
// 2D non-conforming shape and equal size:
// mesh-optimizer -m ./amr-quad-q2.mesh -o 2 -rs 1 -mid 9 -tid 2 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
#include "mfem.hpp"
#include <fstream>
@@ -58,190 +66,160 @@ using namespace std;
double weight_fun(const Vector &x);
// Metric values are visualized by creating an L2 finite element functions and
// computing the metric values at the nodes.
void vis_metric(int order, TMOP_QualityMetric &qm, const TargetConstructor &tc,
Mesh &mesh, char *title, int position)
double ind_values(const Vector &x)
{
L2_FECollection fec(order, mesh.Dimension(), BasisType::GaussLobatto);
FiniteElementSpace fes(&mesh, &fec, 1);
GridFunction metric(&fes);
InterpolateTMOP_QualityMetric(qm, tc, mesh, metric);
osockstream sock(19916, "localhost");
sock << "solution\n";
mesh.Print(sock);
metric.Save(sock);
sock.send();
sock << "window_title '"<< title << "'\n"
<< "window_geometry "
<< position << " " << 0 << " " << 600 << " " << 600 << "\n"
<< "keys jRmclA" << endl;
const int opt = 6;
const double small = 0.001, big = 0.01;
// Sine wave.
if (opt==1)
{
const double X = x(0), Y = x(1);
const double ind = std::tanh((10*(Y-0.5) + std::sin(4.0*M_PI*X)) + 1) -
std::tanh((10*(Y-0.5) + std::sin(4.0*M_PI*X)) - 1);
return ind * small + (1.0 - ind) * big;
}
if (opt==2)
{
// Circle in the middle.
double val = 0.;
const double xc = x(0) - 0.5, yc = x(1) - 0.5;
const double r = sqrt(xc*xc + yc*yc);
double r1 = 0.15; double r2 = 0.35; double sf=30.0;
val = 0.5*(std::tanh(sf*(r-r1)) - std::tanh(sf*(r-r2)));
if (val > 1.) {val = 1;}
return val * small + (1.0 - val) * big;
}
if (opt == 3)
{
// cross
const double X = x(0), Y = x(1);
const double r1 = 0.45, r2 = 0.55;
const double sf = 40.0;
double val = 0.5 * ( std::tanh(sf*(X-r1)) - std::tanh(sf*(X-r2)) +
std::tanh(sf*(Y-r1)) - std::tanh(sf*(Y-r2)) );
if (val > 1.) { val = 1.0; }
return val * small + (1.0 - val) * big;
}
if (opt==4)
{
// Multiple circles
double r1,r2,val,rval;
double sf = 10;
val = 0.;
// circle 1
r1= 0.25; r2 = 0.25; rval = 0.1;
double xc = x(0) - r1, yc = x(1) - r2;
double r = sqrt(xc*xc+yc*yc);
val = 0.5*(1+std::tanh(sf*(r+rval))) - 0.5*(1+std::tanh(sf*
(r-rval)));// std::exp(val1);
// circle 2
r1= 0.75; r2 = 0.75;
xc = x(0) - r1, yc = x(1) - r2;
r = sqrt(xc*xc+yc*yc);
val += (0.5*(1+std::tanh(sf*(r+rval))) - 0.5*(1+std::tanh(sf*
(r-rval))));// std::exp(val1);
// circle 3
r1= 0.75; r2 = 0.25;
xc = x(0) - r1, yc = x(1) - r2;
r = sqrt(xc*xc+yc*yc);
val += 0.5*(1+std::tanh(sf*(r+rval))) - 0.5*(1+std::tanh(sf*
(r-rval)));// std::exp(val1);
// circle 4
r1= 0.25; r2 = 0.75;
xc = x(0) - r1, yc = x(1) - r2;
r = sqrt(xc*xc+yc*yc);
val += 0.5*(1+std::tanh(sf*(r+rval))) - 0.5*(1+std::tanh(sf*(r-rval)));
if (val > 1.0) {val = 1.;}
if (val < 0.0) {val = 0.;}
return val * small + (1.0 - val) * big;
}
if (opt==5)
{
// cross
double val = 0.;
double X = x(0)-0.5, Y = x(1)-0.5;
double rval = std::sqrt(X*X + Y*Y);
double thval = 60.*M_PI/180.;
double Xmod,Ymod;
Xmod = X*std::cos(thval) + Y*std::sin(thval);
Ymod= -X*std::sin(thval) + Y*std::cos(thval);
X = Xmod+0.5; Y = Ymod+0.5;
double r1 = 0.45; double r2 = 0.55; double sf=30.0;
val = ( 0.5*(1+std::tanh(sf*(X-r1))) - 0.5*(1+std::tanh(sf*(X-r2)))
+ 0.5*(1+std::tanh(sf*(Y-r1))) - 0.5*(1+std::tanh(sf*(Y-r2))) );
if (rval > 0.4) {val = 0.;}
if (val > 1.0) {val = 1.;}
if (val < 0.0) {val = 0.;}
return val * small + (1.0 - val) * big;
}
if (opt==6)
{
double val = 0.;
const double xc = x(0) - 0.0, yc = x(1) - 0.5;
const double r = sqrt(xc*xc + yc*yc);
double r1 = 0.45; double r2 = 0.55; double sf=30.0;
val = 0.5*(1+std::tanh(sf*(r-r1))) - 0.5*(1+std::tanh(sf*(r-r2)));
if (val > 1.) {val = 1;}
if (val < 0.) {val = 0;}
return val * small + (1.0 - val) * big;
}
return 0.0;
}
class RelaxedNewtonSolver : public NewtonSolver
class HessianCoefficient : public MatrixCoefficient
{
private:
// Quadrature points that are checked for negative Jacobians etc.
const IntegrationRule &ir;
FiniteElementSpace *fes;
mutable GridFunction x_gf;
int type;
public:
RelaxedNewtonSolver(const IntegrationRule &irule, FiniteElementSpace *f)
: ir(irule), fes(f) { }
HessianCoefficient(int dim, int type_)
: MatrixCoefficient(dim), type(type_) { }
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const;
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip)
{
Vector pos(3);
T.Transform(ip, pos);
if (type == 0)
{
K(0, 0) = 1.0 + 3.0 * std::sin(M_PI*pos(0));
K(0, 1) = 0.0;
K(1, 0) = 0.0;
K(1, 1) = 1.0;
}
else
{
const double xc = pos(0) - 0.5, yc = pos(1) - 0.5;
const double r = sqrt(xc*xc + yc*yc);
double r1 = 0.15; double r2 = 0.35; double sf=30.0;
const double eps = 0.5;
const double tan1 = std::tanh(sf*(r-r1)),
tan2 = std::tanh(sf*(r-r2));
K(0, 0) = eps + 1.0 * (tan1 - tan2);
K(0, 1) = 0.0;
K(1, 0) = 0.0;
K(1, 1) = 1.0;
}
}
};
double RelaxedNewtonSolver::ComputeScalingFactor(const Vector &x,
const Vector &b) const
{
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
MFEM_VERIFY(nlf != NULL, "invalid Operator subclass");
const bool have_b = (b.Size() == Height());
const int NE = fes->GetMesh()->GetNE(), dim = fes->GetFE(0)->GetDim(),
dof = fes->GetFE(0)->GetDof(), nsp = ir.GetNPoints();
Array<int> xdofs(dof * dim);
DenseMatrix Jpr(dim), dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
Vector x_out(x.Size());
bool x_out_ok = false;
const double energy_in = nlf->GetEnergy(x);
double scale = 1.0, energy_out;
double norm0 = Norm(r);
x_gf.MakeTRef(fes, x_out, 0);
// Decreases the scaling of the update until the new mesh is valid.
for (int i = 0; i < 12; i++)
{
add(x, -scale, c, x_out);
x_gf.SetFromTrueVector();
energy_out = nlf->GetGridFunctionEnergy(x_gf);
if (energy_out > 1.2*energy_in || std::isnan(energy_out) != 0)
{
if (print_level >= 0)
{ cout << "Scale = " << scale << " Increasing energy." << endl; }
scale *= 0.5; continue;
}
int jac_ok = 1;
for (int i = 0; i < NE; i++)
{
fes->GetElementVDofs(i, xdofs);
x_gf.GetSubVector(xdofs, posV);
for (int j = 0; j < nsp; j++)
{
fes->GetFE(i)->CalcDShape(ir.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
if (Jpr.Det() <= 0.0) { jac_ok = 0; goto break2; }
}
}
break2:
if (jac_ok == 0)
{
if (print_level >= 0)
{ cout << "Scale = " << scale << " Neg det(J) found." << endl; }
scale *= 0.5; continue;
}
oper->Mult(x_out, r);
if (have_b) { r -= b; }
double norm = Norm(r);
if (norm > 1.2*norm0)
{
if (print_level >= 0)
{ cout << "Scale = " << scale << " Norm increased." << endl; }
scale *= 0.5; continue;
}
else { x_out_ok = true; break; }
}
if (print_level >= 0)
{
cout << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling." << endl;
}
if (x_out_ok == false) { scale = 0.0; }
return scale;
}
// Allows negative Jacobians. Used in untangling metrics.
class DescentNewtonSolver : public NewtonSolver
{
private:
// Quadrature points that are checked for negative Jacobians etc.
const IntegrationRule &ir;
FiniteElementSpace *fes;
mutable GridFunction x_gf;
public:
DescentNewtonSolver(const IntegrationRule &irule, FiniteElementSpace *f)
: ir(irule), fes(f) { }
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const;
};
double DescentNewtonSolver::ComputeScalingFactor(const Vector &x,
const Vector &b) const
{
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
MFEM_VERIFY(nlf != NULL, "invalid Operator subclass");
const int NE = fes->GetMesh()->GetNE(), dim = fes->GetFE(0)->GetDim(),
dof = fes->GetFE(0)->GetDof(), nsp = ir.GetNPoints();
Array<int> xdofs(dof * dim);
DenseMatrix Jpr(dim), dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
x_gf.MakeTRef(fes, x.GetData());
x_gf.SetFromTrueVector();
double min_detJ = infinity();
for (int i = 0; i < NE; i++)
{
fes->GetElementVDofs(i, xdofs);
x_gf.GetSubVector(xdofs, posV);
for (int j = 0; j < nsp; j++)
{
fes->GetFE(i)->CalcDShape(ir.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
min_detJ = min(min_detJ, Jpr.Det());
}
}
cout << "Minimum det(J) = " << min_detJ << endl;
Vector x_out(x.Size());
bool x_out_ok = false;
const double energy_in = nlf->GetGridFunctionEnergy(x_gf);
double scale = 1.0, energy_out;
for (int i = 0; i < 7; i++)
{
add(x, -scale, c, x_out);
energy_out = nlf->GetEnergy(x_out);
if (energy_out > energy_in || std::isnan(energy_out) != 0)
{
scale *= 0.5;
}
else { x_out_ok = true; break; }
}
cout << "Energy decrease: " << (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling." << endl;
if (x_out_ok == false) { return 0.0; }
return scale;
}
// Additional IntegrationRules that can be used with the --quad-type option.
IntegrationRules IntRulesLo(0, Quadrature1D::GaussLobatto);
IntegrationRules IntRulesCU(0, Quadrature1D::ClosedUniform);
@@ -304,7 +282,9 @@ int main (int argc, char *argv[])
"Target (ideal element) type:\n\t"
"1: Ideal shape, unit size\n\t"
"2: Ideal shape, equal size\n\t"
"3: Ideal shape, initial size");
"3: Ideal shape, initial size\n\t"
"4: Given full analytic Jacobian (in physical space)\n\t"
"5: Ideal shape, given size (in physical space)");
args.AddOption(&lim_const, "-lc", "--limit-const", "Limiting constant.");
args.AddOption(&quad_type, "-qt", "--quad-type",
"Quadrature rule type:\n\t"
@@ -375,7 +355,8 @@ int main (int argc, char *argv[])
// 6. Get the mesh nodes (vertices and other degrees of freedom in the finite
// element space) as a finite element grid function in fespace. Note that
// changing x automatically changes the shapes of the mesh elements.
GridFunction *x = mesh->GetNodes();
GridFunction x(fespace);
mesh->SetNodalGridFunction(&x);
// 7. Define a vector representing the minimal local mesh size in the mesh
// nodes. We index the nodes using the scalar version of the degrees of
@@ -425,10 +406,9 @@ int main (int argc, char *argv[])
// Set the boundary values to zero.
for (int j = 0; j < vdofs.Size(); j++) { rdm(vdofs[j]) = 0.0; }
}
*x -= rdm;
// Set the perturbation of all nodes from the true nodes.
x->SetTrueVector();
x->SetFromTrueVector();
x -= rdm;
x.SetTrueVector();
x.SetFromTrueVector();
// 9. Save the starting (prior to the optimization) mesh to a file. This
// output can be viewed later using GLVis: "glvis -m perturbed.mesh".
@@ -439,7 +419,7 @@ int main (int argc, char *argv[])
// 10. Store the starting (prior to the optimization) positions.
GridFunction x0(fespace);
x0 = *x;
x0 = x;
// 11. Form the integrator that uses the chosen metric and target.
double tauval = -0.1;
@@ -468,15 +448,43 @@ int main (int argc, char *argv[])
default: cout << "Unknown metric_id: " << metric_id << endl; return 3;
}
TargetConstructor::TargetType target_t;
TargetConstructor *target_c = NULL;
HessianCoefficient *adapt_coeff = NULL;
H1_FECollection ind_fec(3, dim);
FiniteElementSpace ind_fes(mesh, &ind_fec);
GridFunction size;
switch (target_id)
{
case 1: target_t = TargetConstructor::IDEAL_SHAPE_UNIT_SIZE; break;
case 2: target_t = TargetConstructor::IDEAL_SHAPE_EQUAL_SIZE; break;
case 3: target_t = TargetConstructor::IDEAL_SHAPE_GIVEN_SIZE; break;
default: cout << "Unknown target_id: " << target_id << endl;
delete metric; return 3;
case 4:
{
target_t = TargetConstructor::GIVEN_FULL;
AnalyticAdaptTC *tc = new AnalyticAdaptTC(target_t);
adapt_coeff = new HessianCoefficient(dim, 1);
tc->SetAnalyticTargetSpec(NULL, NULL, adapt_coeff);
target_c = tc;
break;
}
case 5:
{
target_t = TargetConstructor::IDEAL_SHAPE_GIVEN_SIZE;
DiscreteAdaptTC *tc = new DiscreteAdaptTC(target_t);
size.SetSpace(&ind_fes);
FunctionCoefficient ind_coeff(ind_values);
size.ProjectCoefficient(ind_coeff);
tc->SetSerialDiscreteTargetSpec(size);
target_c = tc;
break;
}
default: cout << "Unknown target_id: " << target_id << endl; return 3;
}
if (target_c == NULL)
{
target_c = new TargetConstructor(target_t);
}
TargetConstructor *target_c = new TargetConstructor(target_t);
target_c->SetNodes(x0);
TMOP_Integrator *he_nlf_integ = new TMOP_Integrator(metric, target_c);
@@ -542,13 +550,13 @@ int main (int argc, char *argv[])
}
else { a.AddDomainIntegrator(he_nlf_integ); }
const double init_energy = a.GetGridFunctionEnergy(*x);
const double init_energy = a.GetGridFunctionEnergy(x);
// 15. Visualize the starting mesh and metric values.
if (visualization)
{
char title[] = "Initial metric values";
vis_metric(mesh_poly_deg, *metric, *target_c, *mesh, title, 0);
vis_tmop_metric_s(mesh_poly_deg, *metric, *target_c, *mesh, title, 0);
}
// 16. Fix all boundary nodes, or fix only a given component depending on the
@@ -652,8 +660,13 @@ int main (int argc, char *argv[])
if (tauval > 0.0)
{
tauval = 0.0;
newton = new RelaxedNewtonSolver(*ir, fespace);
cout << "The RelaxedNewtonSolver is used (as all det(J)>0)." << endl;
TMOPNewtonSolver *tns = new TMOPNewtonSolver(*ir);
if (target_id == 5)
{
tns->SetDiscreteAdaptTC(dynamic_cast<DiscreteAdaptTC *>(target_c));
}
newton = tns;
cout << "TMOPNewtonSolver is used (as all det(J) > 0).\n";
}
else
{
@@ -664,8 +677,8 @@ int main (int argc, char *argv[])
return 3;
}
tauval -= 0.01 * h0.Min(); // Slightly below minJ0 to avoid div by 0.
newton = new DescentNewtonSolver(*ir, fespace);
cout << "The DescentNewtonSolver is used (as some det(J)<0)." << endl;
newton = new TMOPDescentNewtonSolver(*ir);
cout << "The TMOPDescentNewtonSolver is used (as some det(J) < 0).\n";
}
newton->SetPreconditioner(*S);
newton->SetMaxIter(newton_iter);
@@ -673,8 +686,9 @@ int main (int argc, char *argv[])
newton->SetAbsTol(0.0);
newton->SetPrintLevel(verbosity_level >= 1 ? 1 : -1);
newton->SetOperator(a);
newton->Mult(b, x->GetTrueVector());
x->SetFromTrueVector();
newton->Mult(b, x.GetTrueVector());
x.SetFromTrueVector();
if (newton->GetConverged() == false)
{
cout << "NewtonIteration: rtol = " << newton_rtol << " not achieved."
@@ -691,12 +705,12 @@ int main (int argc, char *argv[])
}
// 21. Compute the amount of energy decrease.
const double fin_energy = a.GetGridFunctionEnergy(*x);
const double fin_energy = a.GetGridFunctionEnergy(x);
double metric_part = fin_energy;
if (lim_const != 0.0)
{
lim_coeff.constant = 0.0;
metric_part = a.GetGridFunctionEnergy(*x);
metric_part = a.GetGridFunctionEnergy(x);
lim_coeff.constant = lim_const;
}
cout << "Initial strain energy: " << init_energy
@@ -712,13 +726,13 @@ int main (int argc, char *argv[])
if (visualization)
{
char title[] = "Final metric values";
vis_metric(mesh_poly_deg, *metric, *target_c, *mesh, title, 600);
vis_tmop_metric_s(mesh_poly_deg, *metric, *target_c, *mesh, title, 600);
}
// 23. Visualize the mesh displacement.
if (visualization)
{
x0 -= *x;
x0 -= x;
osockstream sock(19916, "localhost");
sock << "solution\n";
mesh->Print(sock);
+196 -202
View File
@@ -32,6 +32,11 @@
// Compile with: make pmesh-optimizer
//
// Sample runs:
// Adapted analytic Hessian:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// Adapted discrete size:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 7 -tid 5 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
//
// Blade shape:
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -rs 0 -mid 2 -tid 1 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
// Blade limited shape:
@@ -48,223 +53,176 @@
// mpirun -np 4 pmesh-optimizer -o 3 -rs 0 -mid 1 -tid 1 -ni 1000 -ls 2 -li 100 -bnd -qt 1 -qo 8 -cmb
// 3D pinched sphere shape (the mesh is in the mfem/data GitHub repository):
// * mpirun -np 4 pmesh-optimizer -m ../../../mfem_data/ball-pert.mesh -o 4 -rs 0 -mid 303 -tid 1 -ni 20 -ls 2 -li 500 -fix-bnd
// 2D non-conforming shape and equal size:
// mpirun -np 4 pmesh-optimizer -m ./amr-quad-q2.mesh -o 2 -rs 1 -mid 9 -tid 2 -ni 200 -ls 2 -li 100 -bnd -qt 1 -qo 8
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include <fstream>
using namespace mfem;
using namespace std;
double weight_fun(const Vector &x);
// Metric values are visualized by creating an L2 finite element functions and
// computing the metric values at the nodes.
void vis_metric(int order, TMOP_QualityMetric &qm, const TargetConstructor &tc,
ParMesh &pmesh, char *title, int position)
double ind_values(const Vector &x)
{
L2_FECollection fec(order, pmesh.Dimension(), BasisType::GaussLobatto);
ParFiniteElementSpace fes(&pmesh, &fec, 1);
ParGridFunction metric(&fes);
InterpolateTMOP_QualityMetric(qm, tc, pmesh, metric);
socketstream sock;
if (pmesh.GetMyRank() == 0)
const int opt = 6;
const double small = 0.001, big = 0.01;
// Sine wave.
if (opt==1)
{
sock.open("localhost", 19916);
sock << "solution\n";
const double X = x(0), Y = x(1);
const double ind = std::tanh((10*(Y-0.5) + std::sin(4.0*M_PI*X)) + 1) -
std::tanh((10*(Y-0.5) + std::sin(4.0*M_PI*X)) - 1);
return ind * small + (1.0 - ind) * big;
}
pmesh.PrintAsOne(sock);
metric.SaveAsOne(sock);
if (pmesh.GetMyRank() == 0)
if (opt==2)
{
sock << "window_title '"<< title << "'\n"
<< "window_geometry "
<< position << " " << 0 << " " << 600 << " " << 600 << "\n"
<< "keys jRmclA" << endl;
// Circle in the middle.
double val = 0.;
const double xc = x(0) - 0.5, yc = x(1) - 0.5;
const double r = sqrt(xc*xc + yc*yc);
double r1 = 0.15; double r2 = 0.35; double sf=30.0;
val = 0.5*(std::tanh(sf*(r-r1)) - std::tanh(sf*(r-r2)));
if (val > 1.) {val = 1;}
return val * small + (1.0 - val) * big;
}
if (opt == 3)
{
// cross
const double X = x(0), Y = x(1);
const double r1 = 0.45, r2 = 0.55;
const double sf = 40.0;
double val = 0.5 * ( std::tanh(sf*(X-r1)) - std::tanh(sf*(X-r2)) +
std::tanh(sf*(Y-r1)) - std::tanh(sf*(Y-r2)) );
if (val > 1.) { val = 1.0; }
return val * small + (1.0 - val) * big;
}
if (opt==4)
{
// Multiple circles
double r1,r2,val,rval;
double sf = 10;
val = 0.;
// circle 1
r1= 0.25; r2 = 0.25; rval = 0.1;
double xc = x(0) - r1, yc = x(1) - r2;
double r = sqrt(xc*xc+yc*yc);
val = 0.5*(1+std::tanh(sf*(r+rval))) - 0.5*(1+std::tanh(sf*
(r-rval)));// std::exp(val1);
// circle 2
r1= 0.75; r2 = 0.75;
xc = x(0) - r1, yc = x(1) - r2;
r = sqrt(xc*xc+yc*yc);
val += (0.5*(1+std::tanh(sf*(r+rval))) - 0.5*(1+std::tanh(sf*
(r-rval))));// std::exp(val1);
// circle 3
r1= 0.75; r2 = 0.25;
xc = x(0) - r1, yc = x(1) - r2;
r = sqrt(xc*xc+yc*yc);
val += 0.5*(1+std::tanh(sf*(r+rval))) - 0.5*(1+std::tanh(sf*
(r-rval)));// std::exp(val1);
// circle 4
r1= 0.25; r2 = 0.75;
xc = x(0) - r1, yc = x(1) - r2;
r = sqrt(xc*xc+yc*yc);
val += 0.5*(1+std::tanh(sf*(r+rval))) - 0.5*(1+std::tanh(sf*(r-rval)));
if (val > 1.0) {val = 1.;}
if (val < 0.0) {val = 0.;}
return val * small + (1.0 - val) * big;
}
if (opt==5)
{
// cross
double val = 0.;
double X = x(0)-0.5, Y = x(1)-0.5;
double rval = std::sqrt(X*X + Y*Y);
double thval = 60.*M_PI/180.;
double Xmod,Ymod;
Xmod = X*std::cos(thval) + Y*std::sin(thval);
Ymod= -X*std::sin(thval) + Y*std::cos(thval);
X = Xmod+0.5; Y = Ymod+0.5;
double r1 = 0.45; double r2 = 0.55; double sf=30.0;
val = ( 0.5*(1+std::tanh(sf*(X-r1))) - 0.5*(1+std::tanh(sf*(X-r2)))
+ 0.5*(1+std::tanh(sf*(Y-r1))) - 0.5*(1+std::tanh(sf*(Y-r2))) );
if (rval > 0.4) {val = 0.;}
if (val > 1.0) {val = 1.;}
if (val < 0.0) {val = 0.;}
return val * small + (1.0 - val) * big;
}
if (opt==6)
{
double val = 0.;
const double xc = x(0) - 0.0, yc = x(1) - 0.5;
const double r = sqrt(xc*xc + yc*yc);
double r1 = 0.45; double r2 = 0.55; double sf=30.0;
val = 0.5*(1+std::tanh(sf*(r-r1))) - 0.5*(1+std::tanh(sf*(r-r2)));
if (val > 1.) {val = 1;}
if (val < 0.) {val = 0;}
return val * small + (1.0 - val) * big;
}
return 0.0;
}
class RelaxedNewtonSolver : public NewtonSolver
class HessianCoefficient : public MatrixCoefficient
{
private:
// Quadrature points that are checked for negative Jacobians etc.
const IntegrationRule &ir;
ParFiniteElementSpace *pfes;
mutable ParGridFunction x_gf;
int type;
public:
RelaxedNewtonSolver(const IntegrationRule &irule, ParFiniteElementSpace *pf)
: NewtonSolver(pf->GetComm()), ir(irule), pfes(pf) { }
HessianCoefficient(int dim, int type_)
: MatrixCoefficient(dim), type(type_) { }
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const;
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip)
{
Vector pos(3);
T.Transform(ip, pos);
if (type == 0)
{
K(0, 0) = 1.0 + 3.0 * std::sin(M_PI*pos(0));
K(0, 1) = 0.0;
K(1, 0) = 0.0;
K(1, 1) = 1.0;
}
else
{
const double xc = pos(0) - 0.5, yc = pos(1) - 0.5;
const double r = sqrt(xc*xc + yc*yc);
double r1 = 0.15; double r2 = 0.35; double sf=30.0;
const double eps = 0.5;
const double tan1 = std::tanh(sf*(r-r1)),
tan2 = std::tanh(sf*(r-r2));
K(0, 0) = eps + 1.0 * (tan1 - tan2);
K(0, 1) = 0.0;
K(1, 0) = 0.0;
K(1, 1) = 1.0;
}
}
};
double RelaxedNewtonSolver::ComputeScalingFactor(const Vector &x,
const Vector &b) const
{
const ParNonlinearForm *nlf = dynamic_cast<const ParNonlinearForm *>(oper);
MFEM_VERIFY(nlf != NULL, "invalid Operator subclass");
const bool have_b = (b.Size() == Height());
const int NE = pfes->GetParMesh()->GetNE(), dim = pfes->GetFE(0)->GetDim(),
dof = pfes->GetFE(0)->GetDof(), nsp = ir.GetNPoints();
Array<int> xdofs(dof * dim);
DenseMatrix Jpr(dim), dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
Vector x_out(x.Size());
bool x_out_ok = false;
const double energy_in = nlf->GetEnergy(x);
double scale = 1.0, energy_out;
double norm0 = Norm(r);
x_gf.MakeTRef(pfes, x_out, 0);
// Decreases the scaling of the update until the new mesh is valid.
for (int i = 0; i < 12; i++)
{
add(x, -scale, c, x_out);
x_gf.SetFromTrueVector();
energy_out = nlf->GetParGridFunctionEnergy(x_gf);
if (energy_out > 1.2*energy_in || std::isnan(energy_out) != 0)
{
if (print_level >= 0)
{ cout << "Scale = " << scale << " Increasing energy." << endl; }
scale *= 0.5; continue;
}
int jac_ok = 1;
for (int i = 0; i < NE; i++)
{
pfes->GetElementVDofs(i, xdofs);
x_gf.GetSubVector(xdofs, posV);
for (int j = 0; j < nsp; j++)
{
pfes->GetFE(i)->CalcDShape(ir.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
if (Jpr.Det() <= 0.0) { jac_ok = 0; goto break2; }
}
}
break2:
int jac_ok_all;
MPI_Allreduce(&jac_ok, &jac_ok_all, 1, MPI_INT, MPI_LAND,
pfes->GetComm());
if (jac_ok_all == 0)
{
if (print_level >= 0)
{ cout << "Scale = " << scale << " Neg det(J) found." << endl; }
scale *= 0.5; continue;
}
oper->Mult(x_out, r);
if (have_b) { r -= b; }
double norm = Norm(r);
if (norm > 1.2*norm0)
{
if (print_level >= 0)
{ cout << "Scale = " << scale << " Norm increased." << endl; }
scale *= 0.5; continue;
}
else { x_out_ok = true; break; }
}
if (print_level >= 0)
{
cout << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling." << endl;
}
if (x_out_ok == false) { scale = 0.0; }
return scale;
}
// Allows negative Jacobians. Used in untangling metrics.
class DescentNewtonSolver : public NewtonSolver
{
private:
// Quadrature points that are checked for negative Jacobians etc.
const IntegrationRule &ir;
ParFiniteElementSpace *pfes;
mutable ParGridFunction x_gf;
public:
DescentNewtonSolver(const IntegrationRule &irule, ParFiniteElementSpace *pf)
: NewtonSolver(pf->GetComm()), ir(irule), pfes(pf) { }
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const;
};
double DescentNewtonSolver::ComputeScalingFactor(const Vector &x,
const Vector &b) const
{
const ParNonlinearForm *nlf = dynamic_cast<const ParNonlinearForm *>(oper);
MFEM_VERIFY(nlf != NULL, "invalid Operator subclass");
const int NE = pfes->GetParMesh()->GetNE(), dim = pfes->GetFE(0)->GetDim(),
dof = pfes->GetFE(0)->GetDof(), nsp = ir.GetNPoints();
Array<int> xdofs(dof * dim);
DenseMatrix Jpr(dim), dshape(dof, dim), pos(dof, dim);
Vector posV(pos.Data(), dof * dim);
x_gf.MakeTRef(pfes, x.GetData());
x_gf.SetFromTrueVector();
double min_detJ = infinity();
for (int i = 0; i < NE; i++)
{
pfes->GetElementVDofs(i, xdofs);
x_gf.GetSubVector(xdofs, posV);
for (int j = 0; j < nsp; j++)
{
pfes->GetFE(i)->CalcDShape(ir.IntPoint(j), dshape);
MultAtB(pos, dshape, Jpr);
min_detJ = min(min_detJ, Jpr.Det());
}
}
double min_detJ_all;
MPI_Allreduce(&min_detJ, &min_detJ_all, 1, MPI_DOUBLE, MPI_MIN,
pfes->GetComm());
if (print_level >= 0)
{ cout << "Minimum det(J) = " << min_detJ_all << endl; }
Vector x_out(x.Size());
bool x_out_ok = false;
const double energy_in = nlf->GetParGridFunctionEnergy(x_gf);
double scale = 1.0, energy_out;
for (int i = 0; i < 7; i++)
{
add(x, -scale, c, x_out);
energy_out = nlf->GetEnergy(x_out);
if (energy_out > energy_in || std::isnan(energy_out) != 0)
{
scale *= 0.5;
}
else { x_out_ok = true; break; }
}
if (print_level >= 0)
{
cout << "Energy decrease: "
<< (energy_in - energy_out) / energy_in * 100.0
<< "% with " << scale << " scaling." << endl;
}
if (x_out_ok == false) { return 0.0; }
return scale;
}
// Additional IntegrationRules that can be used with the --quad-type option.
IntegrationRules IntRulesLo(0, Quadrature1D::GaussLobatto);
IntegrationRules IntRulesCU(0, Quadrature1D::ClosedUniform);
int main (int argc, char *argv[])
{
// 0. Initialize MPI.
@@ -331,7 +289,9 @@ int main (int argc, char *argv[])
"Target (ideal element) type:\n\t"
"1: Ideal shape, unit size\n\t"
"2: Ideal shape, equal size\n\t"
"3: Ideal shape, initial size");
"3: Ideal shape, initial size\n\t"
"4: Given full analytic Jacobian (in physical space)\n\t"
"5: Ideal shape, given size (in physical space)");
args.AddOption(&lim_const, "-lc", "--limit-const", "Limiting constant.");
args.AddOption(&quad_type, "-qt", "--quad-type",
"Quadrature rule type:\n\t"
@@ -381,6 +341,7 @@ int main (int argc, char *argv[])
cout << endl;
}
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
for (int lev = 0; lev < rp_levels; lev++) { pmesh->UniformRefinement(); }
@@ -510,20 +471,47 @@ int main (int argc, char *argv[])
return 3;
}
TargetConstructor::TargetType target_t;
TargetConstructor *target_c = NULL;
HessianCoefficient *adapt_coeff = NULL;
H1_FECollection ind_fec(3, dim);
ParFiniteElementSpace ind_fes(pmesh, &ind_fec);
ParGridFunction size;
switch (target_id)
{
case 1: target_t = TargetConstructor::IDEAL_SHAPE_UNIT_SIZE; break;
case 2: target_t = TargetConstructor::IDEAL_SHAPE_EQUAL_SIZE; break;
case 3: target_t = TargetConstructor::IDEAL_SHAPE_GIVEN_SIZE; break;
case 4:
{
target_t = TargetConstructor::GIVEN_FULL;
AnalyticAdaptTC *tc = new AnalyticAdaptTC(target_t);
adapt_coeff = new HessianCoefficient(dim, 1);
tc->SetAnalyticTargetSpec(NULL, NULL, adapt_coeff);
target_c = tc;
break;
}
case 5:
{
target_t = TargetConstructor::IDEAL_SHAPE_GIVEN_SIZE;
DiscreteAdaptTC *tc = new DiscreteAdaptTC(target_t);
size.SetSpace(&ind_fes);
FunctionCoefficient ind_coeff(ind_values);
size.ProjectCoefficient(ind_coeff);
tc->SetParDiscreteTargetSpec(size);
target_c = tc;
break;
}
default:
if (myid == 0) { cout << "Unknown target_id: " << target_id << endl; }
return 3;
}
TargetConstructor *target_c;
target_c = new TargetConstructor(target_t, MPI_COMM_WORLD);
if (target_c == NULL)
{
target_c = new TargetConstructor(target_t, MPI_COMM_WORLD);
}
target_c->SetNodes(x0);
TMOP_Integrator *he_nlf_integ;
he_nlf_integ = new TMOP_Integrator(metric, target_c);
TMOP_Integrator *he_nlf_integ= new TMOP_Integrator(metric, target_c);
// 13. Setup the quadrature rule for the non-linear form integrator.
const IntegrationRule *ir = NULL;
@@ -597,7 +585,7 @@ int main (int argc, char *argv[])
if (visualization)
{
char title[] = "Initial metric values";
vis_metric(mesh_poly_deg, *metric, *target_c, *pmesh, title, 0);
vis_tmop_metric_p(mesh_poly_deg, *metric, *target_c, *pmesh, title, 0);
}
// 17. Fix all boundary nodes, or fix only a given component depending on the
@@ -705,9 +693,14 @@ int main (int argc, char *argv[])
if (tauval > 0.0)
{
tauval = 0.0;
newton = new RelaxedNewtonSolver(*ir, pfespace);
TMOPNewtonSolver *tns = new TMOPNewtonSolver(pfespace->GetComm(), *ir);
if (target_id == 5)
{
tns->SetDiscreteAdaptTC(dynamic_cast<DiscreteAdaptTC *>(target_c));
}
newton = tns;
if (myid == 0)
{ cout << "RelaxedNewtonSolver is used (as all det(J) > 0)." << endl; }
{ cout << "TMOPNewtonSolver is used (as all det(J) > 0)." << endl; }
}
else
{
@@ -715,15 +708,15 @@ int main (int argc, char *argv[])
(dim == 3 && metric_id != 352) )
{
if (myid == 0)
{ cout << "The mesh is inverted. Use an untangling metric." << endl; }
{ cout << "The mesh is inverted. Use an untangling metric.\n"; }
return 3;
}
double h0min = h0.Min(), h0min_all;
MPI_Allreduce(&h0min, &h0min_all, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD);
tauval -= 0.01 * h0min_all; // Slightly below minJ0 to avoid div by 0.
newton = new DescentNewtonSolver(*ir, pfespace);
newton = new TMOPDescentNewtonSolver(pfespace->GetComm(), *ir);
if (myid == 0)
{ cout << "DescentNewtonSolver is used (as some det(J) < 0)." << endl; }
{ cout << "TMOPDescentNewtonSolver is used (as some det(J) < 0).\n"; }
}
newton->SetPreconditioner(*S);
newton->SetMaxIter(newton_iter);
@@ -775,7 +768,7 @@ int main (int argc, char *argv[])
if (visualization)
{
char title[] = "Final metric values";
vis_metric(mesh_poly_deg, *metric, *target_c, *pmesh, title, 600);
vis_tmop_metric_p(mesh_poly_deg, *metric, *target_c, *pmesh, title, 600);
}
// 23. Visualize the mesh displacement.
@@ -805,6 +798,7 @@ int main (int argc, char *argv[])
delete metric2;
delete coeff1;
delete target_c;
delete adapt_coeff;
delete metric;
delete pfespace;
delete fec;
+114
View File
@@ -0,0 +1,114 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
#
dimension
2
elements
16
1 3 0 1 6 5
1 3 1 2 7 6
1 3 2 3 8 7
1 3 3 4 9 8
1 3 5 6 11 10
1 3 6 7 12 11
1 3 7 8 13 12
1 3 8 9 14 13
1 3 10 11 16 15
1 3 11 12 17 16
1 3 12 13 18 17
1 3 13 14 19 18
1 3 15 16 21 20
1 3 16 17 22 21
1 3 17 18 23 22
1 3 18 19 24 23
boundary
16
2 1 0 1
2 1 1 2
2 1 2 3
2 1 3 4
2 1 21 20
2 1 22 21
2 1 23 22
2 1 24 23
1 1 5 0
1 1 10 5
1 1 15 10
1 1 20 15
1 1 4 9
1 1 9 14
1 1 14 19
1 1 19 24
vertices
25
nodes
FiniteElementSpace
FiniteElementCollection: H1_2D_P1
VDim: 2
Ordering: 0
0
0.25
0.5
0.75
1
0
0.25
0.5
0.75
1
0
0.25
0.5
0.75
1
0
0.25
0.5
0.75
1
0
0.25
0.5
0.75
1
0
0
0
0
0
0.25
0.25
0.25
0.25
0.25
0.5
0.5
0.5
0.5
0.5
0.75
0.75
0.75
0.75
0.75
1
1
1
1
1
+147
View File
@@ -0,0 +1,147 @@
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.
#include "catch.hpp"
#include "mfem.hpp"
using namespace mfem;
namespace pa_coeff
{
int dimension;
double coeffFunction(const Vector& x)
{
if (dimension == 2)
{
return sin(8.0 * M_PI * x[0]) * cos(6.0 * M_PI * x[1]) + 2.0;
}
else
{
return sin(8.0 * M_PI * x[0]) * cos(6.0 * M_PI * x[1]) *
sin(4.0 * M_PI * x[2]) +
2.0;
}
}
TEST_CASE("pa_coeff")
{
for (dimension = 2; dimension < 4; ++dimension)
{
for (int coeffType = 0; coeffType < 3; ++coeffType)
{
for (int integrator = 0; integrator < 2; ++integrator)
{
const int ne = 2;
std::cout << "Testing " << dimension << "D partial assembly with "
<< "coeffType " << coeffType << " and "
<< "integrator " << integrator << std::endl;
for (int order = 1; order < 5; ++order)
{
Mesh* mesh;
if (dimension == 2)
{
mesh = new Mesh(ne, ne, Element::QUADRILATERAL, 1, 1.0, 1.0);
}
else
{
mesh = new Mesh(ne, ne, ne, Element::HEXAHEDRON, 1, 1.0, 1.0,
1.0);
}
FiniteElementCollection* h1_fec =
new H1_FECollection(order, dimension);
FiniteElementSpace h1_fespace(mesh, h1_fec);
Array<int> ess_tdof_list;
BilinearForm paform(&h1_fespace);
GridFunction* coeffGridFunction = nullptr;
Coefficient* coeff = nullptr;
if (coeffType == 0)
{
coeff = new ConstantCoefficient(1.0);
}
else if (coeffType == 1)
{
coeff = new FunctionCoefficient(&coeffFunction);
}
else if (coeffType == 2)
{
FunctionCoefficient tmpCoeff(&coeffFunction);
coeffGridFunction = new GridFunction(&h1_fespace);
coeffGridFunction->ProjectCoefficient(tmpCoeff);
coeff = new GridFunctionCoefficient(coeffGridFunction);
}
paform.SetAssemblyLevel(AssemblyLevel::PARTIAL);
if (integrator < 2)
{
paform.AddDomainIntegrator(new DiffusionIntegrator(*coeff));
}
if (integrator > 0)
{
paform.AddDomainIntegrator(new MassIntegrator(*coeff));
}
paform.Assemble();
OperatorHandle paopr;
paform.FormSystemMatrix(ess_tdof_list, paopr);
BilinearForm assemblyform(&h1_fespace);
if (integrator < 2)
{
assemblyform.AddDomainIntegrator(
new DiffusionIntegrator(*coeff));
}
if (integrator > 0)
{
assemblyform.AddDomainIntegrator(new MassIntegrator(*coeff));
}
assemblyform.SetDiagonalPolicy(Matrix::DIAG_ONE);
assemblyform.Assemble();
assemblyform.Finalize();
const SparseMatrix& A_explicit = assemblyform.SpMat();
Vector xin(h1_fespace.GetTrueVSize());
xin.Randomize();
Vector y_mat(xin);
y_mat = 0.0;
Vector y_assembly(xin);
y_assembly = 0.0;
Vector y_pa(xin);
y_pa = 0.0;
paopr->Mult(xin, y_pa);
assemblyform.Mult(xin, y_assembly);
A_explicit.Mult(xin, y_mat);
y_pa -= y_mat;
double pa_error = y_pa.Norml2();
std::cout << " order: " << order
<< ", pa error norm: " << pa_error << std::endl;
REQUIRE(pa_error < 1.e-12);
y_assembly -= y_mat;
double assembly_error = y_assembly.Norml2();
std::cout << " order: " << order
<< ", assembly error norm: " << assembly_error
<< std::endl;
REQUIRE(assembly_error < 1.e-12);
delete coeff;
delete coeffGridFunction;
delete mesh;
delete h1_fec;
}
}
}
}
}
} // namespace pa_coeff