Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18e636fa54 |
+14
-36
@@ -92,10 +92,6 @@ examples/ex9.mesh
|
||||
examples/ex9-mesh.*
|
||||
examples/ex9-init.*
|
||||
examples/ex9-final.*
|
||||
examples/ex41.mesh
|
||||
examples/ex41-mesh.*
|
||||
examples/ex41-init.*
|
||||
examples/ex41-final.*
|
||||
examples/deformed.*
|
||||
examples/velocity.*
|
||||
examples/elastic_energy.*
|
||||
@@ -227,9 +223,6 @@ miniapps/electromagnetics/Joule_[0-9]*
|
||||
miniapps/electromagnetics/Lorentz_[0-9]*
|
||||
miniapps/electromagnetics/Lorentz.dat
|
||||
|
||||
miniapps/fluids/schrodinger-flow/schrodinger_flow
|
||||
miniapps/fluids/schrodinger-flow/pschrodinger_flow
|
||||
|
||||
miniapps/gslib/field-diff
|
||||
miniapps/gslib/field-interp
|
||||
miniapps/gslib/findpts
|
||||
@@ -237,7 +230,6 @@ miniapps/gslib/pfindpts
|
||||
miniapps/gslib/schwarz_ex1
|
||||
miniapps/gslib/schwarz_ex1p
|
||||
miniapps/gslib/interpolated.gf
|
||||
miniapps/gslib/particles_redist
|
||||
|
||||
miniapps/meshing/mobius-strip
|
||||
miniapps/meshing/klein-bottle
|
||||
@@ -284,8 +276,10 @@ miniapps/meshing/refined.mesh
|
||||
miniapps/meshing/bounding-box*
|
||||
miniapps/meshing/jacobian-determinant*
|
||||
|
||||
miniapps/mtop/ParaView/
|
||||
miniapps/mtop/mtop_test_iso_elasticity
|
||||
miniapps/mtop/parheat
|
||||
miniapps/mtop/ParHeat/*
|
||||
miniapps/mtop/seqheat
|
||||
miniapps/mtop/SeqHeat/*
|
||||
|
||||
miniapps/autodiff/paradiff
|
||||
miniapps/autodiff/seqadiff
|
||||
@@ -295,26 +289,21 @@ miniapps/autodiff/seq_example
|
||||
miniapps/autodiff/seq_test
|
||||
miniapps/autodiff/Example/*
|
||||
|
||||
miniapps/fluids/navier/navier_mms
|
||||
miniapps/fluids/navier/navier_kovasznay
|
||||
miniapps/fluids/navier/navier_kovasznay_vs
|
||||
miniapps/fluids/navier/navier_tgv
|
||||
miniapps/fluids/navier/navier_shear
|
||||
miniapps/fluids/navier/navier_3dfoc
|
||||
miniapps/fluids/navier/navier_turbchan
|
||||
miniapps/fluids/navier/navier_cht
|
||||
miniapps/fluids/navier/navier_bifurcation
|
||||
miniapps/fluids/navier/Navier_Bifurcation_[0-9]*
|
||||
miniapps/fluids/navier/ParaView
|
||||
miniapps/fluids/navier/tgv_out*.txt
|
||||
miniapps/fluids/navier/*_output
|
||||
miniapps/navier/navier_mms
|
||||
miniapps/navier/navier_kovasznay
|
||||
miniapps/navier/navier_kovasznay_vs
|
||||
miniapps/navier/navier_tgv
|
||||
miniapps/navier/navier_shear
|
||||
miniapps/navier/navier_3dfoc
|
||||
miniapps/navier/navier_turbchan
|
||||
miniapps/navier/navier_cht
|
||||
miniapps/navier/tgv_out*.txt
|
||||
miniapps/navier/*_output
|
||||
|
||||
miniapps/nurbs/nurbs_ex1
|
||||
miniapps/nurbs/nurbs_ex1p
|
||||
miniapps/nurbs/nurbs_ex3
|
||||
miniapps/nurbs/nurbs_ex5
|
||||
miniapps/nurbs/nurbs_ex10
|
||||
miniapps/nurbs/nurbs_ex10p
|
||||
miniapps/nurbs/nurbs_ex11p
|
||||
miniapps/nurbs/nurbs_ex24
|
||||
miniapps/nurbs/nurbs_solenoidal
|
||||
@@ -340,14 +329,7 @@ miniapps/nurbs/nurbs_naca_cmesh
|
||||
miniapps/nurbs/naca-cmesh.mesh
|
||||
miniapps/nurbs/glvis_naca-cmesh.mesh
|
||||
miniapps/nurbs/Naca_cmesh
|
||||
miniapps/nurbs/nurbs_mesh_info
|
||||
miniapps/nurbs/k*_*.dat
|
||||
miniapps/nurbs/*-Surface.mesh
|
||||
miniapps/nurbs/*.mesh
|
||||
miniapps/nurbs/*.sol
|
||||
miniapps/nurbs/deformed.*
|
||||
miniapps/nurbs/elastic_energy.*
|
||||
miniapps/nurbs/velocity.*
|
||||
|
||||
miniapps/performance/ex1
|
||||
miniapps/performance/ex1p
|
||||
@@ -369,7 +351,6 @@ miniapps/shifted/lsf_integral
|
||||
miniapps/tools/display-basis
|
||||
miniapps/tools/load-dc
|
||||
miniapps/tools/convert-dc
|
||||
miniapps/tools/compare-dc
|
||||
miniapps/tools/gridfunction-bounds
|
||||
miniapps/tools/lor-transfer
|
||||
miniapps/tools/plor-transfer
|
||||
@@ -440,9 +421,6 @@ miniapps/tribol/contact-patch-test
|
||||
miniapps/diag-smoothers/abs-l1-jacobi
|
||||
miniapps/diag-smoothers/mg-abs-l1-jacobi
|
||||
|
||||
miniapps/contact/contact
|
||||
miniapps/contact/ParaView
|
||||
|
||||
# Unit test binary and outputs
|
||||
tests/unit/output_meshes
|
||||
tests/unit/unit_tests
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
https://mfem.org
|
||||
|
||||
|
||||
FIXME: this file needs to be updated
|
||||
|
||||
This directory contains most of the GitLab CI configuration. MFEM runs both PR
|
||||
and nightly testing on GitLab.
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
|
||||
|
||||
# run
|
||||
if [[ "${MACHINE_NAME}" == "dane" ]]; then
|
||||
salloc --nodes=1 -t 60 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
salloc --nodes=1 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
elif [[ ${MACHINE_NAME} == "corona" ]]; then
|
||||
salloc --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
else
|
||||
|
||||
@@ -8,29 +8,9 @@
|
||||
https://mfem.org
|
||||
|
||||
|
||||
Version 4.9.1 (development)
|
||||
Version 4.8.1 (development)
|
||||
===========================
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Improved the gridfunction projection routines. Projections work for Scalar,
|
||||
Vector and VectorFE, also NURBS versions. Optionally different types of
|
||||
projections can be selected, default behaviour has not changed.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Improved support for 1D NURBS meshes with variable order, including using
|
||||
the patches construct for 1D NURBS meshes.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Electromagnetics/lorentz miniapp has been updated to leverage the ParticleSet
|
||||
capability.
|
||||
|
||||
|
||||
Version 4.9, released on Dec 11, 2025
|
||||
=====================================
|
||||
|
||||
Starting with this version, MFEM requires a C++17 compiler.
|
||||
|
||||
Discretization improvements
|
||||
@@ -39,149 +19,86 @@ Discretization improvements
|
||||
nonlinear finite element operators, based on Enzyme or dual numbers AD at
|
||||
quadrature points. These features are part of the new mfem::future namespace
|
||||
and some of the API can change in the future. See the new dFEM minimal surface
|
||||
miniapp in the miniapps/dfem/ directory for illustration of dFEM's use. Using
|
||||
Enzyme for AD in MFEM is tested with clang v19 and requires clang/LLVM built
|
||||
with plugin support. See INSTALL for more details.
|
||||
miniapp in the miniapps/dfem/ directory for illustration of dFEM's use.
|
||||
|
||||
- Introduced initial support for particle methods in MFEM with new classes
|
||||
Particle, ParticleSet and ParticleVector.
|
||||
* Particle is a convenient interface for individual particle data.
|
||||
* ParticleSet manages and stores particle data in a struct-of-arrays form,
|
||||
carrying particle coordinates and IDs along with an arbitrary number of
|
||||
Vector and integer data for each particle.
|
||||
* ParticleVector is a Vector-derived container that stores vector data for an
|
||||
arbitrary number of particles contiguously based on specified vdim/ordering.
|
||||
See the new particle miniapps in miniapps/gslib/ and miniapps/fluids/navier/.
|
||||
|
||||
- Added a new miniapp and specialized AMG solver (AMGF) for optimization-based
|
||||
contact mechanics. The miniapp solves large-scale frictionless contact using a
|
||||
self-contained Interior Point (IP) solver, mortar-based contact constraints
|
||||
provided by Tribol. The resulting linear systems are solved with the new AMGF
|
||||
solver (see below). Benchmark examples include the two-block, ironing, and
|
||||
beam-sphere problems. See the miniapps/contact/ directory.
|
||||
|
||||
- Added support for boundary integration to the hyperbolic framework. Two new
|
||||
classes BdrHyperbolicDirichletIntegrator and BoundaryHyperbolicFlowIntegrator
|
||||
have been introduced for implementation of weak Dirichlet boundary conditions
|
||||
with a general flux or for the linear case respectively.
|
||||
|
||||
- Added a method to compute piecewise linear bounds on high-order functions on
|
||||
tensor-product elements.
|
||||
|
||||
- Added support for interior face integration enabling DG methods in
|
||||
ParMixedBilinearForm, ParNonlinearForm and ParBlockNonlinearForm.
|
||||
- Using Enzyme for AD in MFEM is tested with clang v19 and requires clang/LLVM
|
||||
built with plugin support. See INSTALL for more details.
|
||||
|
||||
- In the ParMoonolith integration, added support for variational resampling of
|
||||
H1 vector fields.
|
||||
|
||||
- Introduced IMEX ODE solvers based on a split-operator framework. Added
|
||||
examples ex41 and ex41p demonstrating IMEX DG/CG discretizations of the
|
||||
convection–diffusion equation, with ex41p using DG LOR preconditioning.
|
||||
- Added support for boundary integration to the hyperbolic framework. In this
|
||||
regard, new classes `BdrHyperbolicDirichletIntegrator` and
|
||||
`BoundaryHyperbolicFlowIntegrator` have been introduced for implementation
|
||||
of weak Dirichlet boundary conditions with a general flux or for the linear
|
||||
case respectively.
|
||||
|
||||
- Added method to compute piecewise linear bounds on high-order functions on
|
||||
tensor-product elements.
|
||||
|
||||
- Parallel anisotropic refinement of hexahedral meshes is now supported,
|
||||
provided that neighboring hexahedra are not refined in conflicting directions.
|
||||
A new ParMesh method is added to check for such conflicts, before refinement.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
|
||||
- The TMOP kernel hierarchy has been restructured to reduce compilation time.
|
||||
Most large kernels have been split into smaller specific kernels for each
|
||||
metric. The directory structure has been updated with assemble, metrics, mult
|
||||
and tools subdirectories. New kernel dispatch and specialization system has
|
||||
also been integrated. Unit tests have been revised to ensure --all tests pass.
|
||||
Most large kernels have been split into smaller, specific ones, with kernels
|
||||
for each metric. The directory structure has been updated with assemble,
|
||||
metrics, mult and tools subdirectories. The new kernel dispatch and
|
||||
specialization system has also been integrated.
|
||||
Unit tests have been revised to ensure --all tests pass.
|
||||
|
||||
- Introduced NC-patch NURBS meshes, which are conforming element-wise but allow
|
||||
for nonconforming patch topology. This new mesh format supports element
|
||||
spacing formulas for refinement, as well as local refinement factors for a
|
||||
subset of knot vectors.
|
||||
|
||||
- Added support for higher order meshes in Mesh::MakeSimplicial and
|
||||
ParMesh::MakeSimplicial.
|
||||
|
||||
- Added a new miniapp for interpolating a surface grid of points in 3D using a
|
||||
smooth NURBS surface, that can then be sampled at arbitrary resolution while
|
||||
staying close to the original geometry. See miniapps/nurbs/nurbs_surface.
|
||||
|
||||
- Parallel anisotropic refinement of hexahedral meshes is now supported,
|
||||
provided that neighboring hexahedra are not refined in conflicting directions.
|
||||
A new ParMesh method is added to check for such conflicts, before refinement.
|
||||
|
||||
- Added support for higher order meshes in (Par)Mesh::MakeSimplicial.
|
||||
|
||||
Linear and nonlinear solvers
|
||||
----------------------------
|
||||
- Added FilteredSolver: a base class for solvers with filtering. It handles
|
||||
cases where a solver performs well except in small subspaces, by adding a
|
||||
filtering step formulated as a subspace correction.
|
||||
|
||||
- Added AMGFSolver: a derived class of FilteredSolver, specialized for AMG with
|
||||
Filtering (AMGF), providing robust preconditioning for linear systems arising
|
||||
in constrained optimization problems such as frictionless contact.
|
||||
|
||||
Added 'GetResiduals' and 'GetFinalAbsResidualNorm' to 'HyprePCG',
|
||||
'HypreGMRES', and 'HypreFGMRES' to get 'r' and '|r|_p'. Note that the latter
|
||||
computes '|r|_p' from 'r' instead of returning a cached value like the
|
||||
relative 'GetFinalResidualNorm'. These require Hypre >= 2.15.0.
|
||||
|
||||
Changed the default solver parameters for 'HyprePCG' to 'tol=1e-6' and
|
||||
'max_iter=1000'. This matches the default parameters in Hypre 3.0.
|
||||
|
||||
Added various helper functions for querying/modifying Hypre solvers:
|
||||
'HypreSmoother::GetType', 'HypreSmoother::GetSOROptions',
|
||||
'HypreSmoother::GetPolyOptions', 'HypreSmoother::GetWindowParameters',
|
||||
'HypreSmoother::IsOperatorSymmetric', 'HyprePCG::GetTol',
|
||||
'HyprePCG::GetAbsTol', 'HyprePCG::GetMaxIter', 'HyprePCG::SetUseTwoNorm',
|
||||
'HypreGMRES::GetTol', 'HypreGMRES::GetAbsTol', 'HypreGMRES::GetMaxIter',
|
||||
'HypreGMRES::GetKDim', 'HypreFGMRES::GetTol', 'HypreFGMRES::GetMaxIter',
|
||||
'HypreFGMRES::GetKDim', and 'HypreBoomerAMG::GetMaxIter'.
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- Added the 'gpu', 'raja-gpu', and 'ceed-gpu' backend aliases/shortcuts which
|
||||
automatically select between CUDA or HIP.
|
||||
|
||||
- Added the option to enable GPU-aware MPI in MFEM using the environment
|
||||
variable 'MFEM_GPU_AWARE_MPI' set to any value. Setting this environment
|
||||
variable is an alternative to calling 'Device::SetGPUAwareMPI(true)'.
|
||||
|
||||
- Implemented a GPU-accelerated matrix-free AMR derefinement GridFunction update
|
||||
operator. This supports mixed geometry meshes and variable order spaces, and
|
||||
is the default derefinement operator constructed by FiniteElementSpace::Update
|
||||
and ParFiniteElementSpace::Update. The operator requires the finite element
|
||||
space to be nonconforming.
|
||||
- The function Vector::SetSubVector(const Array<int> &, const real_t) now
|
||||
executes on device if either the vector or the array have the device flag
|
||||
set. This is most often used for setting constant essential boundary
|
||||
conditions. A new function Vector::SetSubVectorHost has been added in cases
|
||||
where host execution is always needed (e.g. when the DOFs array is small).
|
||||
|
||||
- Introduced MFEM_FOREACH_THREAD_DIRECT, which directly maps loop tasks to GPU
|
||||
threads, assigning one task per thread.
|
||||
|
||||
- The function Vector::SetSubVector(const Array<int> &, const real_t) now
|
||||
executes on device if either the vector or the array have the device flag
|
||||
set. This is most often used for setting constant essential BCs. A new method,
|
||||
SetSubVectorHost, has been added for cases where host execution is always
|
||||
needed (e.g. when the DOFs array is small).
|
||||
|
||||
- Added GPU support in GradientGridFunction and InnerProduct Coefficient classes
|
||||
by implementing their Project methods.
|
||||
|
||||
- Implemented a GPU-accelerated matrix-free AMR derefinement `GridFunction`
|
||||
update operator. This supports mixed geometry meshes and variable order
|
||||
spaces, and is the default derefinement operator constructed by
|
||||
`FiniteElementSpace::Update` and `ParFiniteElementSpace::Update`.
|
||||
The operator requires `FiniteElementSpace::Nonconforming() == true`.
|
||||
- Added new method: GridFunction::GetGradients, with GPU support, for computing
|
||||
the gradients of a GridFunction on all elements.
|
||||
- Added GPU support in GradientGridFunctionCoefficient and
|
||||
InnerProductCoefficient by implementing their Project methods.
|
||||
|
||||
- The CUDA-specific names used by some of the unit tests like 'cunit_tests' and
|
||||
'pcunit_tests' were replaced by names using 'gpu' instead of 'c' (short for
|
||||
CUDA) or 'cuda'. These tests automatically run the CUDA/HIP tests based on the
|
||||
MFEM build configuration.
|
||||
Linear and nonlinear solvers
|
||||
----------------------------
|
||||
- Added `FilteredSolver`: a base class for solvers with filtering. It handles cases
|
||||
where a solver performs well except in small subspaces, by adding a filtering step
|
||||
formulated as a subspace correction.
|
||||
- Added `AMGFSolver`: a derived class of `FilteredSolver`, specialized for
|
||||
AMG with Filtering (AMGF), providing robust preconditioning for linear systems
|
||||
arising in constrained optimization problems such as frictionless contact.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added the miniapps/fluids directory and moved the previous Navier and the new
|
||||
incompressible Schrödinger flow miniapps into it.
|
||||
|
||||
- Introduced the new Incompressible Schrödinger Flow (ISF) miniapp, which models
|
||||
inviscid fluid dynamics by solving the linear Schrödinger equation, leveraging
|
||||
the hydrodynamical analogy to quantum mechanics.
|
||||
|
||||
- New particle-related miniapps:
|
||||
* New transient Navier-Stokes fluid-particles solver NavierParticles in
|
||||
miniapps/fluids/navier/navier_particles, for modeling tracer particles in
|
||||
fluid flow, demonstrating use of the new ParticleSet class.
|
||||
* New Navier miniapp, miniapps/fluids/navier/navier_bifurcation, showing the
|
||||
use of NavierParticles in a 2D bifurcating channel flow.
|
||||
* New FindPointsGSLIB miniapp, miniapps/gslib/particles_redist, showing
|
||||
parallel-redistribution of particle data between MPI ranks.
|
||||
* Particle visualization features in common/particles_extras for viewing
|
||||
particle locations and trajectories (ParticleTrajectories) using GLVis.
|
||||
- Added miniapps to demonstrate an implementation of the absolute-value
|
||||
L(1)-Jacobi preconditioners in partially assembled operators. This includes
|
||||
Multigrid wrapper to demonstrate the effectiveness of these Jacobi-type
|
||||
operators as smoothers.
|
||||
These miniapps can be found in `miniapps/diag-smoothers`.
|
||||
|
||||
- Added a new miniapp (meshing/mesh-bounding-boxes) that computes the bounding
|
||||
boxes for each element of a given mesh, and the bounds on the determinant of
|
||||
@@ -194,44 +111,36 @@ New and updated examples and miniapps
|
||||
of a charged particle, subject to Lorentz forces, in electrostatic and/or
|
||||
magnetostatic fields as computed by the volta or tesla miniapps.
|
||||
|
||||
- Added miniapps to demonstrate an implementation of the absolute-value
|
||||
l1-Jacobi preconditioners in partially assembled operators. This includes
|
||||
Multigrid wrapper to demonstrate the effectiveness of these Jacobi-type
|
||||
operators as smoothers. See the miniapps/diag-smoothers/ directory.
|
||||
|
||||
- Updated the mtop miniapp with a GPU enabled forward and adjoint solver for
|
||||
isotropic linear elasticity.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Introduced MFEM_FETCH_TPLS CMake option to enable downloading, configuring,
|
||||
and building of TPLs alongside MFEM (currently supported TPLs are hypre,
|
||||
METIS, and GSLIB).
|
||||
|
||||
- Added quadrature function support to the VisIt and Conduit data collections.
|
||||
|
||||
- Added access to the internal parallel matrix in Par(Mixed)BilinearForm and
|
||||
related utility methods for elimination of BCs.
|
||||
|
||||
- FindPointsGSLIB has a new constructor that accepts the mesh object and
|
||||
internally calls the Setup() method so users do not have to. The FreeData()
|
||||
method has also been moved to the destructor so users do not need to manually
|
||||
free-up the memory if the destructor is called before MPI_Finalize().
|
||||
|
||||
- Added parallel Address Sanitizer, serial and parallel Undefined Behavior
|
||||
Sanitizer and serial Memory Sanitizer GitHub actions tests on Ubuntu.
|
||||
|
||||
API changes
|
||||
API changes:
|
||||
-----------
|
||||
- mfem::internal::tensor and mfem::internal::dual have been moved to
|
||||
mfem::future::tensor and mfem::future::dual.
|
||||
|
||||
- API addition: in class Operator, added virtual functions: AbsMult, and
|
||||
AbsMultTranspose; in class Vector, added Abs and Pow.
|
||||
- API addition: in class `Operator`, added virtual functions: `AbsMult`, and
|
||||
`AbsMultTranspose`; in class `Vector`, added `Abs` and `Pow`.
|
||||
|
||||
- ParBilinearForm::EliminateEssentialVDofsInRhs() has been deprecated in favor
|
||||
of ParallelEliminateEssentialTDofsInRhs().
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Added the "gpu", "raja-gpu", and "ceed-gpu" backend aliases/shortcuts which
|
||||
automatically select between CUDA or HIP.
|
||||
|
||||
- The CUDA-specific names used by some of the unit tests like 'cunit_tests' and
|
||||
'pcunit_tests' were replaced by names using 'gpu' instead of 'c' (short for
|
||||
CUDA) or 'cuda'. These tests automatically run the CUDA/HIP tests based on the
|
||||
MFEM build configuration.
|
||||
|
||||
- Added the option to enable GPU-aware MPI in MFEM using the environment
|
||||
variable 'MFEM_GPU_AWARE_MPI' set to any value. Setting this environment
|
||||
variable is an alternative to calling 'Device::SetGPUAwareMPI(true)'.
|
||||
|
||||
- Added parallel Address Sanitizer, serial and parallel Undefined Behavior
|
||||
Sanitizer and serial Memory Sanitizer GitHub actions tests on Ubuntu.
|
||||
|
||||
- FindPointsGSLIB has a new constructor that accepts the mesh object and
|
||||
internally calls the Setup() method so that the user does not have to.
|
||||
The FreeData() method has also been moved to the destructor so the user does
|
||||
not need to manually free-up the memory if the destructor is called before
|
||||
MPI_Finalize().
|
||||
|
||||
Version 4.8, released on Apr 9, 2025
|
||||
====================================
|
||||
|
||||
+6
-12
@@ -59,7 +59,7 @@ project(mfem NONE)
|
||||
# Current version of MFEM, see also `makefile`.
|
||||
# mfem_VERSION = (string)
|
||||
# MFEM_VERSION = (int) [automatically derived from mfem_VERSION]
|
||||
set(${PROJECT_NAME}_VERSION 4.9.1)
|
||||
set(${PROJECT_NAME}_VERSION 4.8.1)
|
||||
|
||||
# Prohibit in-source build
|
||||
if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR})
|
||||
@@ -723,7 +723,6 @@ set(MFEM_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
|
||||
# Declaring the library
|
||||
mfem_add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
|
||||
target_compile_features(mfem PUBLIC cxx_std_${CMAKE_CXX_STANDARD})
|
||||
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
|
||||
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES} ${TPL_TARGETS})
|
||||
if (TPL_TARGETS)
|
||||
@@ -870,12 +869,11 @@ add_dependencies(exec
|
||||
# - https://cmake.org/Bug/view.php?id=8438
|
||||
|
||||
# Add a target to copy the mfem data directory to the build directory
|
||||
# Implementable as a single copy_directory_if_different command w/ CMake >= 3.26
|
||||
file(GLOB DATA_FILES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/data/*)
|
||||
add_custom_target(copy_data
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory data
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_FILES} data
|
||||
COMMENT "Syncing the data directory ...")
|
||||
add_custom_command(OUTPUT data_is_copied
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/data data
|
||||
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
|
||||
COMMENT "Copying the data directory ...")
|
||||
add_custom_target(copy_data DEPENDS data_is_copied)
|
||||
# Add 'copy_data' as a prerequisite for all executables, if the source and the
|
||||
# build directories are not the same.
|
||||
if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
|
||||
@@ -1007,10 +1005,6 @@ install(FILES
|
||||
install(EXPORT ${PROJECT_NAME_UC}Targets
|
||||
DESTINATION ${INSTALL_CMAKE_DIR})
|
||||
|
||||
# Install the data directory if present, i.e. if the copy_data target is built
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data
|
||||
DESTINATION ${MFEM_INSTALL_DIR} OPTIONAL)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Create 'config.mk' from 'config.mk.in' for the build and install locations and
|
||||
# define install rules for 'config.mk' and 'test.mk'
|
||||
|
||||
+1
-8
@@ -129,10 +129,6 @@ The MFEM source code has the following structure:
|
||||
│ ├── moonolith
|
||||
│ ├── qinterp
|
||||
│ └── tmop
|
||||
│ | ├── assemble
|
||||
│ | ├── metrics
|
||||
│ | ├── mult
|
||||
│ | └── tools
|
||||
├── general
|
||||
├── linalg
|
||||
│ ├── batched
|
||||
@@ -143,19 +139,16 @@ The MFEM source code has the following structure:
|
||||
│ ├── adjoint
|
||||
│ ├── autodiff
|
||||
│ ├── common
|
||||
│ ├── contact
|
||||
│ ├── dfem
|
||||
│ ├── dpg
|
||||
│ ├── electromagnetics
|
||||
│ ├── fluids
|
||||
│ │ ├── navier
|
||||
│ │ └── schrodinger-flow
|
||||
│ ├── gslib
|
||||
│ ├── hdiv-linear-solver
|
||||
│ ├── hooke
|
||||
│ ├── meshing
|
||||
│ ├── mtop
|
||||
│ ├── multidomain
|
||||
│ ├── navier
|
||||
│ ├── nurbs
|
||||
│ ├── parelag
|
||||
│ ├── performance
|
||||
|
||||
@@ -725,9 +725,7 @@ The specific libraries and their options are:
|
||||
URL: https://ginkgo-project.github.io
|
||||
Options: GINKGO_OPT, GINKGO_LIB, GINKGO_DIR, GINKGO_BUILD_TYPE (Release or
|
||||
Debug).
|
||||
Versions: Ginkgo >= 1.9.0. When building Ginkgo with distributed support, a
|
||||
recent version of the "develop" branch is required (1.11 as defined
|
||||
in include/ginkgo/config.hpp).
|
||||
Versions: Ginkgo >= 1.9.0.
|
||||
|
||||
- AmgX (optional), used when MFEM_USE_AMGX = YES.
|
||||
URL: https://github.com/NVIDIA/AMGX
|
||||
|
||||
+1
-2
@@ -18,7 +18,6 @@
|
||||
# Some choices below are based on the OS type:
|
||||
NOTMAC := $(subst Darwin,,$(shell uname -s))
|
||||
|
||||
ASTYLE_BIN = astyle
|
||||
ETAGS_BIN = $(shell command -v etags 2> /dev/null)
|
||||
EGREP_BIN = $(shell command -v egrep 2> /dev/null)
|
||||
|
||||
@@ -408,7 +407,7 @@ AMGX_LIB = -L$(AMGX_DIR)/lib -lamgx -lcusparse -lcusolver -lcublas -lnvToolsExt
|
||||
# MAGMA library configuration
|
||||
MAGMA_DIR = @MFEM_DIR@/../magma
|
||||
MAGMA_OPT = -I$(MAGMA_DIR)/include
|
||||
MAGMA_LIB = -L$(MAGMA_DIR)/lib -l:libmagma.a $(LAPACK_LIB)
|
||||
MAGMA_LIB = -L$(MAGMA_DIR)/lib -l:libmagma.a -lcublas -lcusparse $(LAPACK_LIB)
|
||||
|
||||
# GnuTLS library configuration
|
||||
GNUTLS_OPT =
|
||||
|
||||
@@ -101,7 +101,7 @@ $ cd ../miniapps
|
||||
$ ls
|
||||
CMakeLists.txt common meshing nurbs shifted toys
|
||||
adjoint electromagnetics mtop parelag solvers
|
||||
autodiff gslib fluids performance tools
|
||||
autodiff gslib navier performance tools
|
||||
```
|
||||
|
||||
And an example in "toys"
|
||||
|
||||
+2
-14
@@ -85,10 +85,6 @@ groups_serial=(
|
||||
"DPG miniapps:"
|
||||
"miniapps/dpg"
|
||||
"{acoustics,convection-diffusion,diffusion,maxwell}.cpp"'
|
||||
'"isf"
|
||||
"Schrodinger flow miniapps:"
|
||||
"miniapps/fluids/schrodinger-flow"
|
||||
"schrodinger_flow.cpp"'
|
||||
'"gslib"
|
||||
"GSLIB miniapps:"
|
||||
"miniapps/gslib"
|
||||
@@ -170,10 +166,6 @@ groups_parallel=(
|
||||
"miniapps/electromagnetics"
|
||||
"joule.cpp"'
|
||||
# "{joule,maxwell,tesla,volta}.cpp"' # todo: multiline sample runs
|
||||
'"isf"
|
||||
"Schrodinger flow miniapps:"
|
||||
"miniapps/fluids/schrodinger-flow"
|
||||
"pschrodinger_flow.cpp"'
|
||||
'"adjoint"
|
||||
"Adjoint miniapps:"
|
||||
"miniapps/adjoint"
|
||||
@@ -199,7 +191,7 @@ groups_parallel=(
|
||||
# todo: miniapps/multidomain
|
||||
'"navier"
|
||||
"Navier miniapps:"
|
||||
"miniapps/fluids/navier"
|
||||
"miniapps/navier"
|
||||
"navier_cht.cpp"'
|
||||
# todo: add other navier miniapps
|
||||
'"nurbs"
|
||||
@@ -289,10 +281,6 @@ groups_all=(
|
||||
"miniapps/electromagnetics"
|
||||
"joule.cpp"'
|
||||
# "{joule,maxwell,tesla,volta}.cpp"' # todo: multiline sample runs
|
||||
'"isf"
|
||||
"Schrodinger flow miniapps:"
|
||||
"miniapps/fluids/schrodinger-flow"
|
||||
"{,p}schrodinger_flow.cpp"'
|
||||
'"adjoint"
|
||||
"Adjoint miniapps:"
|
||||
"miniapps/adjoint"
|
||||
@@ -320,7 +308,7 @@ groups_all=(
|
||||
# todo: miniapps/multidomain
|
||||
'"navier"
|
||||
"Navier miniapps:"
|
||||
"miniapps/fluids/navier"
|
||||
"miniapps/navier"
|
||||
"navier_cht.cpp"'
|
||||
# todo: add other navier miniapps
|
||||
'"nurbs"
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
# PYRAMID = 7
|
||||
#
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
elements
|
||||
25
|
||||
3 3 0 1 2 3
|
||||
3 3 1 4 5 2
|
||||
3 3 4 6 7 5
|
||||
3 3 6 8 9 7
|
||||
3 3 8 10 11 9
|
||||
3 3 10 12 13 11
|
||||
3 3 12 14 15 13
|
||||
3 3 14 16 17 15
|
||||
3 3 16 18 19 17
|
||||
3 3 18 20 21 19
|
||||
3 3 20 22 23 21
|
||||
3 3 22 24 25 23
|
||||
3 3 24 26 27 25
|
||||
3 3 26 28 29 27
|
||||
3 3 28 30 31 29
|
||||
3 3 30 32 33 31
|
||||
3 3 32 34 35 33
|
||||
3 3 17 19 36 37
|
||||
3 3 37 36 38 39
|
||||
3 3 39 38 40 41
|
||||
3 3 41 40 42 43
|
||||
3 3 43 42 44 45
|
||||
3 3 45 44 46 47
|
||||
3 3 47 46 48 49
|
||||
3 3 49 48 50 51
|
||||
|
||||
boundary
|
||||
52
|
||||
2 1 0 1
|
||||
2 1 2 3
|
||||
1 1 3 0
|
||||
2 1 1 4
|
||||
2 1 5 2
|
||||
2 1 4 6
|
||||
2 1 7 5
|
||||
2 1 6 8
|
||||
2 1 9 7
|
||||
2 1 8 10
|
||||
2 1 11 9
|
||||
2 1 10 12
|
||||
2 1 13 11
|
||||
2 1 12 14
|
||||
2 1 15 13
|
||||
2 1 14 16
|
||||
2 1 17 15
|
||||
2 1 16 18
|
||||
2 1 18 20
|
||||
2 1 21 19
|
||||
2 1 20 22
|
||||
2 1 23 21
|
||||
2 1 22 24
|
||||
2 1 25 23
|
||||
2 1 24 26
|
||||
2 1 27 25
|
||||
2 1 26 28
|
||||
2 1 29 27
|
||||
2 1 28 30
|
||||
2 1 31 29
|
||||
2 1 30 32
|
||||
2 1 33 31
|
||||
2 1 32 34
|
||||
3 1 34 35
|
||||
2 1 35 33
|
||||
2 1 19 36
|
||||
2 1 37 17
|
||||
2 1 36 38
|
||||
2 1 39 37
|
||||
2 1 38 40
|
||||
2 1 41 39
|
||||
2 1 40 42
|
||||
2 1 43 41
|
||||
2 1 42 44
|
||||
2 1 45 43
|
||||
2 1 44 46
|
||||
2 1 47 45
|
||||
2 1 46 48
|
||||
2 1 49 47
|
||||
2 1 48 50
|
||||
4 1 50 51
|
||||
2 1 51 49
|
||||
|
||||
vertices
|
||||
52
|
||||
2
|
||||
0 0
|
||||
1 0
|
||||
1 1
|
||||
0 1
|
||||
2 0
|
||||
2 1
|
||||
3 0
|
||||
3 1
|
||||
4 0
|
||||
4 1
|
||||
5 0
|
||||
5 1
|
||||
6 0
|
||||
6 1
|
||||
7 0
|
||||
7 1
|
||||
8 0
|
||||
8 1
|
||||
9 0
|
||||
9 1
|
||||
10 0
|
||||
10 1
|
||||
11 0
|
||||
11 1
|
||||
12 0
|
||||
12 1
|
||||
13 0
|
||||
13 1
|
||||
14 0
|
||||
14 1
|
||||
15 0
|
||||
15 1
|
||||
16 0
|
||||
16 1
|
||||
17 0
|
||||
17 1
|
||||
9 2
|
||||
8 2
|
||||
9 3
|
||||
8 3
|
||||
9 4
|
||||
8 4
|
||||
9 5
|
||||
8 5
|
||||
9 6
|
||||
8 6
|
||||
9 7
|
||||
8 7
|
||||
9 8
|
||||
8 8
|
||||
9 9
|
||||
8 9
|
||||
@@ -1,86 +0,0 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
# Four segments with different NURBS orders, described via patches.
|
||||
elements
|
||||
4
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
4 1 6 7
|
||||
|
||||
boundary
|
||||
0
|
||||
|
||||
edges
|
||||
4
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
3 6 7
|
||||
|
||||
vertices
|
||||
8
|
||||
|
||||
patches
|
||||
|
||||
# Patch 0: linear (order 1, 3 spans)
|
||||
knotvectors
|
||||
1
|
||||
1 4 0 0 .4 .6 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
0.0 0.0 1.0
|
||||
0.6 0.4 1.0
|
||||
0.4 0.6 1.0
|
||||
1.0 1.0 1.0
|
||||
|
||||
# Patch 1: quadratic (order 2, 2 spans)
|
||||
knotvectors
|
||||
1
|
||||
2 4 0 0 0 .5 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
1.0 0.0 1.0
|
||||
1.9 0.0 1.21
|
||||
2.0 0.9 1.22
|
||||
2.0 1.0 1.0
|
||||
|
||||
# Patch 2: cubic (order 3, 3 spans)
|
||||
knotvectors
|
||||
1
|
||||
3 6 0 0 0 0 .33 .66 1 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
2.0 0.0 1.0
|
||||
2.1 0.2 1.31
|
||||
3.5 0.4 1.32
|
||||
2.5 0.6 1.33
|
||||
2.9 1.0 1.34
|
||||
3.0 1.0 1.0
|
||||
|
||||
# Patch 3: quartic (order 4, 1 span)
|
||||
knotvectors
|
||||
1
|
||||
4 5 0 0 0 0 0 1 1 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
3.0 0.0 1.0
|
||||
3.45 0.5 1.41
|
||||
3.50 1.0 1.42
|
||||
3.75 0.8 1.43
|
||||
4.0 0.0 1.0
|
||||
@@ -1,79 +0,0 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
# Three segments with different NURBS orders, described via patches.
|
||||
elements
|
||||
3
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
|
||||
boundary
|
||||
6
|
||||
1 0 0
|
||||
1 0 1
|
||||
1 0 2
|
||||
1 0 3
|
||||
1 0 4
|
||||
1 0 5
|
||||
|
||||
edges
|
||||
3
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
|
||||
vertices
|
||||
6
|
||||
|
||||
patches
|
||||
|
||||
# Patch 0: linear (order 1, 2 control points)
|
||||
knotvectors
|
||||
1
|
||||
1 2 0 0 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
0.0 0.0 1.0
|
||||
1.0 1.0 1.0
|
||||
|
||||
# Patch 1: quadratic (order 2, 3 control points)
|
||||
knotvectors
|
||||
1
|
||||
2 3 0 0 0 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
1.0 0.0 1.0
|
||||
1.02 1.02 1.2
|
||||
2.0 1.0 1.0
|
||||
|
||||
# Patch 2: cubic (order 3, 4 control points)
|
||||
knotvectors
|
||||
1
|
||||
3 4 0 0 0 0 1 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
2.0 0.0 1.0
|
||||
2.03 0.83 1.31
|
||||
2.33 1.03 1.32
|
||||
3.0 1.0 1.0
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
elements
|
||||
3
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
|
||||
boundary
|
||||
6
|
||||
1 0 0
|
||||
1 0 1
|
||||
1 0 2
|
||||
1 0 3
|
||||
1 0 4
|
||||
1 0 5
|
||||
|
||||
edges
|
||||
3
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
|
||||
vertices
|
||||
6
|
||||
|
||||
# Edge 0: linear (order 1, 2 control points)
|
||||
# Edge 1: quadratic (order 2, 3 control points)
|
||||
# Edge 2: cubic (order 3, 4 control points)
|
||||
knotvectors
|
||||
3
|
||||
1 2 0 0 1 1
|
||||
2 3 0 0 0 1 1 1
|
||||
3 4 0 0 0 0 1 1 1 1
|
||||
|
||||
# One weight per control point, in the same order as the control points; (2 + 3 + 4) = 9 weights total
|
||||
weights
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1.2
|
||||
1.31
|
||||
1.32
|
||||
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: NURBS
|
||||
VDim: 2
|
||||
Ordering: 1
|
||||
|
||||
0.0 0.0
|
||||
1.0 1.0
|
||||
1.0 0.0
|
||||
2.0 1.0
|
||||
2.0 0.0
|
||||
3.0 1.0
|
||||
1.02 1.02
|
||||
2.03 0.83
|
||||
2.33 1.03
|
||||
@@ -1,79 +0,0 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
# Three segments with different NURBS orders, described via patches.
|
||||
elements
|
||||
3
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
|
||||
boundary
|
||||
6
|
||||
1 0 0
|
||||
1 0 1
|
||||
1 0 2
|
||||
1 0 3
|
||||
1 0 4
|
||||
1 0 5
|
||||
|
||||
edges
|
||||
3
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
|
||||
vertices
|
||||
6
|
||||
|
||||
patches
|
||||
|
||||
# Patch 0: linear (order 1, 2 control points)
|
||||
knotvectors
|
||||
1
|
||||
1 2 0 0 1 1
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
controlpoints
|
||||
0.0 0.0 0.01 1.0
|
||||
1.0 1.0 1.01 1.0
|
||||
|
||||
# Patch 1: quadratic (order 2, 3 control points)
|
||||
knotvectors
|
||||
1
|
||||
2 3 0 0 0 1 1 1
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
controlpoints
|
||||
1.0 0.0 0.02 1.0
|
||||
1.02 1.02 0.52 1.2
|
||||
2.0 1.0 1.02 1.0
|
||||
|
||||
# Patch 2: cubic (order 3, 4 control points)
|
||||
knotvectors
|
||||
1
|
||||
3 4 0 0 0 0 1 1 1 1
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
controlpoints
|
||||
2.0 0.0 0.03 1.0
|
||||
2.03 0.83 0.33 1.31
|
||||
2.33 1.03 0.63 1.32
|
||||
3.0 1.0 1.03 1.0
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
elements
|
||||
3
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
|
||||
boundary
|
||||
6
|
||||
1 0 0
|
||||
1 0 1
|
||||
1 0 2
|
||||
1 0 3
|
||||
1 0 4
|
||||
1 0 5
|
||||
|
||||
edges
|
||||
3
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
|
||||
vertices
|
||||
6
|
||||
|
||||
# Edge 0: linear (order 1, 2 control points)
|
||||
# Edge 1: quadratic (order 2, 3 control points)
|
||||
# Edge 2: cubic (order 3, 4 control points)
|
||||
knotvectors
|
||||
3
|
||||
1 2 0 0 1 1
|
||||
2 3 0 0 0 1 1 1
|
||||
3 4 0 0 0 0 1 1 1 1
|
||||
|
||||
# One weight per control point, in the same order as the control points; (2 + 3 + 4) = 9 weights total
|
||||
weights
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1.2
|
||||
1.31
|
||||
1.32
|
||||
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: NURBS
|
||||
VDim: 3
|
||||
Ordering: 1
|
||||
|
||||
0.0 0.0 0.01
|
||||
1.0 1.0 1.01
|
||||
1.0 0.0 0.02
|
||||
2.0 1.0 1.02
|
||||
2.0 0.0 0.03
|
||||
3.0 1.0 1.03
|
||||
1.02 1.02 0.52
|
||||
2.03 0.83 0.33
|
||||
2.33 1.03 0.63
|
||||
@@ -48,7 +48,7 @@ PROJECT_NAME = MFEM
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = v4.9.1
|
||||
PROJECT_NUMBER = v4.8.1
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
@@ -973,13 +973,10 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/miniapps/adjoint \
|
||||
@MFEM_SOURCE_DIR@/miniapps/autodiff \
|
||||
@MFEM_SOURCE_DIR@/miniapps/common \
|
||||
@MFEM_SOURCE_DIR@/miniapps/contact \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dfem \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dpg \
|
||||
@MFEM_SOURCE_DIR@/miniapps/dpg/util \
|
||||
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
|
||||
@MFEM_SOURCE_DIR@/miniapps/fluids/navier \
|
||||
@MFEM_SOURCE_DIR@/miniapps/fluids/schrodinger-flow \
|
||||
@MFEM_SOURCE_DIR@/miniapps/gslib \
|
||||
@MFEM_SOURCE_DIR@/miniapps/hdiv-linear-solver \
|
||||
@MFEM_SOURCE_DIR@/miniapps/hooke \
|
||||
@@ -990,6 +987,7 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
|
||||
@MFEM_SOURCE_DIR@/miniapps/meshing \
|
||||
@MFEM_SOURCE_DIR@/miniapps/mtop \
|
||||
@MFEM_SOURCE_DIR@/miniapps/multidomain \
|
||||
@MFEM_SOURCE_DIR@/miniapps/navier \
|
||||
@MFEM_SOURCE_DIR@/miniapps/nurbs \
|
||||
@MFEM_SOURCE_DIR@/miniapps/parelag \
|
||||
@MFEM_SOURCE_DIR@/miniapps/performance \
|
||||
|
||||
@@ -117,8 +117,6 @@ namespace mfem {
|
||||
* - <a class="el" href="ex39p_8cpp_source.html">Example 39p</a>: parallel named mesh attributes
|
||||
* - <a class="el" href="ex40_8cpp_source.html">Example 40</a>: eikonal equation
|
||||
* - <a class="el" href="ex40p_8cpp_source.html">Example 40p</a>: parallel eikonal equation
|
||||
* - <a class="el" href="ex41_8cpp_source.html">Example 41</a>: DG/CG IMEX time-dependent advection-diffusion
|
||||
* - <a class="el" href="ex41p_8cpp_source.html">Example 41p</a>: parallel DG/CG IMEX time-dependent advection-diffusion
|
||||
*
|
||||
* <H4>AmgX Examples</H4>
|
||||
* - Variants of Examples
|
||||
@@ -190,8 +188,6 @@ namespace mfem {
|
||||
* <a class="el" href="nurbs__ex1p_8cpp_source.html">1p</a>,
|
||||
* <a class="el" href="nurbs__ex3_8cpp_source.html">3</a>,
|
||||
* <a class="el" href="nurbs__ex5_8cpp_source.html">5</a>,
|
||||
* <a class="el" href="nurbs__ex10_8cpp_source.html">10</a>,
|
||||
* <a class="el" href="nurbs__ex10p_8cpp_source.html">10p</a>,
|
||||
* <a class="el" href="nurbs__ex11p_8cpp_source.html">11p</a>, and
|
||||
* <a class="el" href="nurbs__ex24_8cpp_source.html">24</a>,
|
||||
* demonstrating howto perform NURBS-based Isogeometric Analysis.
|
||||
@@ -200,7 +196,6 @@ namespace mfem {
|
||||
* - <a class="el" href="nurbs__curveint_8cpp_source.html">NURBS Interpolation</a>: NURBS interpolation of given geometry
|
||||
* - <a class="el" href="nurbs__naca__cmesh_8cpp_source.html">NURBS NACA Mesher</a>: generate NURBS based mesh around a NACA foil
|
||||
* - <a class="el" href="nurbs__printfunc_8cpp_source.html">NURBS Printer</a>: print the NURBS-basis
|
||||
* - <a class="el" href="nurbs__mesh_info_8cpp_source.html">NURBS Mesh info</a>: print the info of a NURBS mesh
|
||||
*
|
||||
* <H3>Miniapps</H3>
|
||||
* - <a class="el" href="volta_8cpp_source.html">Volta</a>: simple electrostatics simulation code
|
||||
@@ -239,8 +234,7 @@ namespace mfem {
|
||||
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Poisson problem
|
||||
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Poisson problem
|
||||
* - <a class="el" href="generate__random__field_8cpp_source.html">SPDE Solvers</a>: SPDE solver random field generation
|
||||
* - <a class="el" href="contact-patch-test_8cpp_source.html">Tribol</a>: mortar contact patch test for elasticity
|
||||
* - <a class="el" href="contact_8cpp_source.html">Contact</a>: Frictionless contact examples using <a class="el" href="classmfem_1_1IPSolver.html#details">IP optimization</a> and the <a class="el" href="classmfem_1_1AMGFSolver.html#details">AMGF solver</a>
|
||||
* - <a class="el" href="contact-patch-test_8cpp_source.html">Contact</a>: mortar contact patch test for elasticity
|
||||
* - <a class="el" href="multidomain_8cpp_source.html">Multidomain miniapp</a>: Multidomain and Submesh demonstration miniapp
|
||||
* - <a class="el" href="pdiffusion_8cpp_source.html">DPG Diffusion example</a>: DPG formulation for the diffusion problem
|
||||
* - <a class="el" href="pmaxwell_8cpp_source.html">DPG Maxwell example</a>: DPG formulation for the indefinite Maxwell problem
|
||||
|
||||
@@ -46,7 +46,6 @@ list(APPEND ALL_EXE_SRCS
|
||||
ex38.cpp
|
||||
ex39.cpp
|
||||
ex40.cpp
|
||||
ex41.cpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
@@ -90,7 +89,7 @@ if (MFEM_USE_MPI)
|
||||
ex37p.cpp
|
||||
ex39p.cpp
|
||||
ex40p.cpp
|
||||
ex41p.cpp
|
||||
ex999p.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -133,8 +132,6 @@ if (MFEM_ENABLE_TESTING)
|
||||
list(APPEND THIS_TEST_OPTIONS "-dg")
|
||||
elseif(${TEST_NAME} MATCHES "ex37p*")
|
||||
list(APPEND THIS_TEST_OPTIONS "-mi" "3")
|
||||
elseif(${TEST_NAME} MATCHES "ex41p*")
|
||||
list(APPEND THIS_TEST_OPTIONS "-tf" "1.0")
|
||||
endif()
|
||||
|
||||
if (NOT (${TEST_NAME} MATCHES ".*p$"))
|
||||
|
||||
+6
-29
@@ -105,7 +105,6 @@ int main(int argc, char *argv[])
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
int vis_steps = 5;
|
||||
bool solve_implicit_state = false;
|
||||
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
@@ -127,9 +126,6 @@ int main(int argc, char *argv[])
|
||||
"Alpha coefficient.");
|
||||
args.AddOption(&kappa, "-k", "--kappa",
|
||||
"Kappa coefficient offset.");
|
||||
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
|
||||
"-imp-slope", "--implicit-slope",
|
||||
"Implicitly solve for stage state or slope.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -183,11 +179,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 7. Initialize the conduction operator and the visualization.
|
||||
ConductionOperator oper(fespace, alpha, kappa, u);
|
||||
using ImplicitVariableType = ConductionOperator::ImplicitVariableType;
|
||||
ImplicitVariableType imp_var = solve_implicit_state ?
|
||||
ImplicitVariableType::STATE
|
||||
: ImplicitVariableType::SLOPE;
|
||||
oper.SetImplicitVariableType(imp_var);
|
||||
|
||||
u_gf.SetFromTrueDofs(u);
|
||||
{
|
||||
@@ -325,14 +316,11 @@ void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
|
||||
}
|
||||
|
||||
void ConductionOperator::ImplicitSolve(const real_t dt,
|
||||
const Vector &u, Vector &k)
|
||||
const Vector &u, Vector &du_dt)
|
||||
{
|
||||
// Solve the equation:
|
||||
// M*k = -K(u + dt*k) for k = du/dt, if solving for stage-slope
|
||||
// or
|
||||
// M*k = -dt*K(k) + M*u for k = u_s, if solving for stage-state
|
||||
// where K is linearized by using u from the previous timestep, and
|
||||
// the stage-state and slope relation: du/dt = (u_s - u)/dt.
|
||||
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
if (!T)
|
||||
{
|
||||
T = Add(1.0, Mmat, dt, Kmat);
|
||||
@@ -340,20 +328,9 @@ void ConductionOperator::ImplicitSolve(const real_t dt,
|
||||
T_solver.SetOperator(*T);
|
||||
}
|
||||
MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt
|
||||
|
||||
// Construct current right-hand side for stage state vs. slope solve
|
||||
if (ImplicitVarTypeIsState())
|
||||
{
|
||||
// k, on return, is the stage value u_s
|
||||
Mmat.Mult(u, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k, on return, is the stage slope du/dt
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg();
|
||||
}
|
||||
T_solver.Mult(z, k);
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg();
|
||||
T_solver.Mult(z, du_dt);
|
||||
}
|
||||
|
||||
void ConductionOperator::SetParameters(const Vector &u)
|
||||
|
||||
+6
-29
@@ -115,7 +115,6 @@ int main(int argc, char *argv[])
|
||||
bool visit = false;
|
||||
int vis_steps = 5;
|
||||
bool adios2 = false;
|
||||
bool solve_implicit_state = false;
|
||||
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
@@ -139,9 +138,6 @@ int main(int argc, char *argv[])
|
||||
"Alpha coefficient.");
|
||||
args.AddOption(&kappa, "-k", "--kappa",
|
||||
"Kappa coefficient offset.");
|
||||
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
|
||||
"-imp-slope", "--implicit-slope",
|
||||
"Implicitly solve for stage state or slope.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -216,11 +212,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 9. Initialize the conduction operator and the VisIt visualization.
|
||||
ConductionOperator oper(fespace, alpha, kappa, u);
|
||||
using ImplicitVariableType = ConductionOperator::ImplicitVariableType;
|
||||
ImplicitVariableType imp_var = solve_implicit_state ?
|
||||
ImplicitVariableType::STATE
|
||||
: ImplicitVariableType::SLOPE;
|
||||
oper.SetImplicitVariableType(imp_var);
|
||||
|
||||
u_gf.SetFromTrueDofs(u);
|
||||
{
|
||||
@@ -416,14 +407,11 @@ void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
|
||||
}
|
||||
|
||||
void ConductionOperator::ImplicitSolve(const real_t dt,
|
||||
const Vector &u, Vector &k)
|
||||
const Vector &u, Vector &du_dt)
|
||||
{
|
||||
// Solve the equation:
|
||||
// M*k = -K(u + dt*k) for k = du/dt, if solving for stage-slope
|
||||
// or
|
||||
// M*k = -dt*K(k) + M*u for k = u_s, if solving for stage-state
|
||||
// where K is linearized by using u from the previous timestep, and
|
||||
// the stage-state and slope relation: du/dt = (u_s - u)/dt.
|
||||
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
if (!T)
|
||||
{
|
||||
T = Add(1.0, Mmat, dt, Kmat);
|
||||
@@ -431,20 +419,9 @@ void ConductionOperator::ImplicitSolve(const real_t dt,
|
||||
T_solver.SetOperator(*T);
|
||||
}
|
||||
MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt
|
||||
|
||||
// Construct current right-hand side for stage state vs. slope solve
|
||||
if (ImplicitVarTypeIsState())
|
||||
{
|
||||
// k, on return, is the stage value u
|
||||
Mmat.Mult(u, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k, on return, is the stage slope du/dt
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg();
|
||||
}
|
||||
T_solver.Mult(z, k);
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg();
|
||||
T_solver.Mult(z, du_dt);
|
||||
}
|
||||
|
||||
void ConductionOperator::SetParameters(const Vector &u)
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
LinearForm b(&fespace);
|
||||
b.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
b.AddDomainIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
|
||||
// 6. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the linear elasticity integrator with piece-wise
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
ParLinearForm b(&fespace);
|
||||
b.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
b.AddDomainIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
|
||||
// 6. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the linear elasticity integrator with piece-wise
|
||||
|
||||
+1
-5
@@ -9,7 +9,6 @@
|
||||
// ex4 -m ../data/beam-hex.mesh -o 2 -pa
|
||||
// ex4 -m ../data/escher.mesh
|
||||
// ex4 -m ../data/fichera.mesh -o 2 -hb
|
||||
// ex4 -m ../data/fichera.mesh -o 2 -hb -ea
|
||||
// ex4 -m ../data/fichera-q2.vtk
|
||||
// ex4 -m ../data/fichera-q3.mesh -o 2 -sc
|
||||
// ex4 -m ../data/square-disc-nurbs.mesh
|
||||
@@ -19,7 +18,6 @@
|
||||
// ex4 -m ../data/amr-quad.mesh
|
||||
// ex4 -m ../data/amr-hex.mesh
|
||||
// ex4 -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// ex4 -m ../data/amr-hex.mesh -o 2 -hb -ea
|
||||
// ex4 -m ../data/fichera-amr.mesh -o 2 -sc
|
||||
// ex4 -m ../data/ref-prism.mesh -o 1
|
||||
// ex4 -m ../data/octahedron.mesh -o 1
|
||||
@@ -27,8 +25,6 @@
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex4 -m ../data/star.mesh -pa -d cuda
|
||||
// ex4 -m ../data/star.mesh -hb -ea -d cuda
|
||||
// ex4 -m ../data/amr-quad.mesh -hb -ea -d cuda
|
||||
// ex4 -m ../data/star.mesh -pa -d raja-cuda
|
||||
// ex4 -m ../data/star.mesh -pa -d raja-omp
|
||||
// ex4 -m ../data/beam-hex.mesh -pa -d cuda
|
||||
@@ -197,7 +193,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Size of linear system: " << A->Height() << endl;
|
||||
|
||||
// 11. Solve the linear system A X = B.
|
||||
if (!pa && (!ea || hybridization))
|
||||
if (!pa)
|
||||
{
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
|
||||
@@ -1,589 +0,0 @@
|
||||
// MFEM Example 41
|
||||
//
|
||||
// Compile with: make ex41
|
||||
//
|
||||
// Sample runs:
|
||||
// ex41
|
||||
// ex41 -cg
|
||||
// ex41 -m ../data/periodic-hexagon.mesh -p 0 -r 2 -dt 0.005 -tf 10
|
||||
// ex41 -m ../data/periodic-square.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex41 -m ../data/periodic-hexagon.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex41 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.002 -tf 9
|
||||
// ex41 -m ../data/star-q3.mesh -p 1 -r 2 -dt 0.001 -tf 9
|
||||
// ex41 -m ../data/star-mixed.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex41 -m ../data/disc-nurbs.mesh -p 1 -r 3 -dt 0.005 -tf 9
|
||||
// ex41 -m ../data/disc-nurbs.mesh -p 2 -r 3 -dt 0.005 -tf 9
|
||||
// ex41 -m ../data/periodic-square.mesh -p 3 -r 4 -dt 0.0025 -tf 9 -vs 20
|
||||
// ex41 -m ../data/periodic-cube.mesh -p 0 -r 2 -o 2 -dt 0.01 -tf 8
|
||||
//
|
||||
// Device sample runs:
|
||||
//
|
||||
// Description: This example code solves the time-dependent advection-diffusion
|
||||
// equation du/dt + v.grad(u) - a div(grad(u)) = 0, where v is a
|
||||
// given fluid velocity, a is the diffusion coefficient, and
|
||||
// u0(x)=u(0,x) is a given initial condition.
|
||||
//
|
||||
// The example demonstrates the use of Discontinuous Galerkin (DG)
|
||||
// bilinear forms in MFEM (face integrators), and the use of IMEX
|
||||
// ODE time integrators.
|
||||
//
|
||||
// The option to use continuous finite elements is available too.
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Mesh bounding box
|
||||
Vector bb_min, bb_max;
|
||||
|
||||
// Velocity coefficient
|
||||
template<int problem=0>
|
||||
void velocity_function(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
real_t center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Translations in 1D, 2D, and 3D
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
|
||||
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 2:
|
||||
{
|
||||
// Clockwise rotation in 2D around the origin
|
||||
const real_t w = M_PI/2;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
|
||||
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// Clockwise twisting rotation in 2D around the origin
|
||||
const real_t w = M_PI/2;
|
||||
real_t d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
|
||||
d = d*d;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
|
||||
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initial condition
|
||||
template<int problem=0>
|
||||
real_t u0_function(const Vector &x)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
real_t center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
return exp(-40.*pow(X(0)-0.5,2));
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
real_t rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
|
||||
if (dim == 3)
|
||||
{
|
||||
const real_t s = (1. + 0.25*cos(2*M_PI*X(2)));
|
||||
rx *= s;
|
||||
ry *= s;
|
||||
}
|
||||
return ( std::erfc(w*(X(0)-cx-rx))*std::erfc(-w*(X(0)-cx+rx)) *
|
||||
std::erfc(w*(X(1)-cy-ry))*std::erfc(-w*(X(1)-cy+ry)) )/16;
|
||||
}
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
real_t x_ = X(0), y_ = X(1), rho, phi;
|
||||
rho = std::hypot(x_, y_);
|
||||
phi = atan2(y_, x_);
|
||||
return pow(sin(M_PI*rho),2)*sin(3*phi);
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
const real_t f = M_PI;
|
||||
return sin(f*X(0))*sin(f*X(1));
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/// Solver for the implicit part of the ODE (the diffusion term).
|
||||
/// Solves systems of the form: (M + dt*S) k = rhs.
|
||||
class Implicit_Solver : public Solver
|
||||
{
|
||||
private:
|
||||
SparseMatrix &M, &S, A;
|
||||
CGSolver linear_solver;
|
||||
BlockILU prec;
|
||||
real_t dt;
|
||||
public:
|
||||
Implicit_Solver(SparseMatrix &M_, SparseMatrix &S_,
|
||||
const FiniteElementSpace &fes)
|
||||
: M(M_),
|
||||
S(S_),
|
||||
prec(fes.GetTypicalFE()->GetDof(),
|
||||
BlockILU::Reordering::MINIMUM_DISCARDED_FILL),
|
||||
dt(1.0)
|
||||
{
|
||||
linear_solver.iterative_mode = false;
|
||||
linear_solver.SetRelTol(1e-9);
|
||||
linear_solver.SetAbsTol(0.0);
|
||||
linear_solver.SetMaxIter(100);
|
||||
linear_solver.SetPrintLevel(0);
|
||||
linear_solver.SetPreconditioner(prec);
|
||||
}
|
||||
|
||||
void SetTimeStep(real_t dt_)
|
||||
{
|
||||
real_t ddt = dt-dt_;
|
||||
|
||||
real_t epsilon;
|
||||
epsilon = std::numeric_limits<real_t>::epsilon();
|
||||
epsilon*=10;
|
||||
|
||||
if (std::abs(ddt) > epsilon)
|
||||
{
|
||||
dt = dt_;
|
||||
// Form operator A = M + dt*S
|
||||
A = S;
|
||||
A *= dt;
|
||||
A += M;
|
||||
|
||||
// this will also call SetOperator on the preconditioner
|
||||
linear_solver.SetOperator(A);
|
||||
}
|
||||
}
|
||||
|
||||
void SetOperator(const Operator &op) override
|
||||
{
|
||||
linear_solver.SetOperator(op);
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
linear_solver.Mult(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE. The weak
|
||||
form of the advection-diffusion equation is M du/dt = K u - S u + b,
|
||||
where M is the mass matrix, K and S are the advection and diffusion
|
||||
matrices, and b describes the flow on the boundary. In the case of IMEX
|
||||
evolution, the diffusion term is treated implicitly, and the advection
|
||||
term is treated explicitly. */
|
||||
class IMEX_Evolution : public TimeDependentOperator
|
||||
{
|
||||
private:
|
||||
BilinearForm &M, &K, &S;
|
||||
const Vector &b;
|
||||
unique_ptr<Solver> M_prec;
|
||||
CGSolver M_solver;
|
||||
unique_ptr<Implicit_Solver> implicit_solver;
|
||||
|
||||
mutable Vector z;
|
||||
|
||||
public:
|
||||
IMEX_Evolution(BilinearForm &M_, BilinearForm &K_, BilinearForm &S_,
|
||||
const Vector &b_);
|
||||
|
||||
/// Evaluate k1=M^{-1}*G1(u,t); -> k1 = M^{-1}*(K*u + b)
|
||||
void Mult1(const Vector &x, Vector &y) const;
|
||||
|
||||
/// Evaluate k2: M*k2 = G2(u+k2*dt,t); -> (M+S*dt)*k2=-S*u
|
||||
void ImplicitSolve2(const real_t dt, const Vector &x, Vector &k);
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
if (TimeDependentOperator::EvalMode::ADDITIVE_TERM_1 == GetEvalMode())
|
||||
{
|
||||
Mult1(x,y);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("TimeDependentOperator::Mult() is not overridden!");
|
||||
}
|
||||
}
|
||||
|
||||
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override
|
||||
{
|
||||
if (TimeDependentOperator::EvalMode::ADDITIVE_TERM_2 == GetEvalMode())
|
||||
{
|
||||
ImplicitSolve2(dt,x,k);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("TimeDependentOperator::ImplicitSolve() is not overridden!");
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
int problem = 0;
|
||||
const char *mesh_file = "../data/periodic-square.mesh";
|
||||
int ref_levels = 2;
|
||||
int order = 3;
|
||||
int ode_solver_type = 64; //IMEXRK3(3,4,3)
|
||||
real_t t_final = 10.0;
|
||||
real_t dt = 0.01;
|
||||
bool paraview = false;
|
||||
bool cg = false;
|
||||
int vis_steps = 50;
|
||||
real_t diffusion_term = 0.01;
|
||||
real_t kappa = -1.0;
|
||||
real_t sigma = -1.0;
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
bool binary = false;
|
||||
int precision = 8;
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&problem, "-p", "--problem",
|
||||
"Problem setup to use. See options in velocity_function().");
|
||||
args.AddOption(&ref_levels, "-r", "--refine",
|
||||
"Number of times to refine the mesh uniformly.");
|
||||
args.AddOption(&order, "-o", "--order", "Order of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::IMEXTypes.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final", "Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step", "Time step.");
|
||||
args.AddOption(&diffusion_term, "-dc", "--diffusion-coeff",
|
||||
"Diffusion coefficient in the PDE.");
|
||||
args.AddOption(¶view, "-paraview", "--paraview-datafiles", "-no-paraview",
|
||||
"--no-paraview-datafiles",
|
||||
"Save data files for ParaView (paraview.org) visualization.");
|
||||
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
|
||||
"Visualize every n-th timestep.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
|
||||
"--ascii-datafiles",
|
||||
"Use binary (Sidre) or ascii format for VisIt data files.");
|
||||
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
|
||||
"--no-visit-datafiles",
|
||||
"Save data files for VisIt (visit.llnl.gov) visualization.");
|
||||
args.AddOption(&cg, "-cg", "--continuous-galerkin", "-dg",
|
||||
"--discontinuous-galerkin",
|
||||
"Use Continuous-Galerkin Finite elements (Default is DG)");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
if (kappa < 0)
|
||||
{
|
||||
kappa = (order+1)*(order+1);
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Read the mesh from the given mesh file. We can handle geometrically
|
||||
// periodic meshes in this code.
|
||||
Mesh mesh(mesh_file);
|
||||
const int dim = mesh.Dimension();
|
||||
|
||||
// 3. Define the IMEX (Split) ODE solver used for time integration. The IMEX
|
||||
// solvers currently available are: 61 - Forward Backward Euler,
|
||||
// 62 - IMEXRK2(2,2,2), 63 - IMEXRK2(2,3,2), and 64 - IMEX_DIRK_RK3.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::SelectIMEX(ode_solver_type);
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
// command-line parameter.
|
||||
for (int lev = 0; lev < ref_levels; lev++) {mesh.UniformRefinement();}
|
||||
if (mesh.NURBSext) {mesh.SetCurvature(max(order, 1));}
|
||||
mesh.GetBoundingBox(bb_min, bb_max, max(order, 1));
|
||||
|
||||
// 5. Define the discontinuous DG finite element space of the given
|
||||
// polynomial order on the refined mesh.
|
||||
FiniteElementCollection *fec = NULL;
|
||||
if (cg)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new DG_FECollection(order, dim, BasisType::GaussLobatto);
|
||||
}
|
||||
FiniteElementSpace fes(&mesh, fec);
|
||||
|
||||
cout << "Number of unknowns: " << fes.GetVSize() << endl;
|
||||
|
||||
// 6. Set up and assemble the bilinear and linear forms corresponding to the
|
||||
// DG discretization. The DGTraceIntegrator involves integrals over mesh
|
||||
// interior faces.
|
||||
std::unique_ptr<VectorFunctionCoefficient> velocity;
|
||||
if (0==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<0>));
|
||||
}
|
||||
else if (1==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<1>));
|
||||
}
|
||||
else if (2==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<2>));
|
||||
}
|
||||
else if (3==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<3>));
|
||||
}
|
||||
|
||||
ConstantCoefficient diff_coeff(diffusion_term);
|
||||
|
||||
BilinearForm m(&fes);
|
||||
BilinearForm k(&fes);
|
||||
BilinearForm s(&fes);
|
||||
|
||||
Vector b(fes.GetTrueVSize());
|
||||
b = 0.0; //The inflow on the boundaries is set to zero.
|
||||
|
||||
m.AddDomainIntegrator(new MassIntegrator);
|
||||
|
||||
constexpr real_t alpha = -1.0;
|
||||
k.AddDomainIntegrator(new ConvectionIntegrator(*velocity, alpha));
|
||||
|
||||
s.AddDomainIntegrator(new DiffusionIntegrator(diff_coeff));
|
||||
if (!cg)
|
||||
{
|
||||
k.AddInteriorFaceIntegrator(new NonconservativeDGTraceIntegrator(*velocity,
|
||||
alpha));
|
||||
k.AddBdrFaceIntegrator(new NonconservativeDGTraceIntegrator(*velocity, alpha));
|
||||
s.AddInteriorFaceIntegrator(new DGDiffusionIntegrator(diff_coeff, sigma,
|
||||
kappa));
|
||||
s.AddBdrFaceIntegrator(new DGDiffusionIntegrator(diff_coeff, sigma, kappa));
|
||||
}
|
||||
|
||||
|
||||
int skip_zeros = 0;
|
||||
m.Assemble(skip_zeros);
|
||||
k.Assemble(skip_zeros);
|
||||
s.Assemble(skip_zeros);
|
||||
|
||||
m.Finalize(skip_zeros);
|
||||
k.Finalize(skip_zeros);
|
||||
s.Finalize(skip_zeros);
|
||||
|
||||
// 7. Define the initial conditions.
|
||||
std::unique_ptr<FunctionCoefficient> u0;
|
||||
if (0==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<0>));
|
||||
}
|
||||
else if (1==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<1>));
|
||||
}
|
||||
else if (2==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<2>));
|
||||
}
|
||||
else if (3==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<3>));
|
||||
}
|
||||
|
||||
GridFunction u(&fes);
|
||||
u.ProjectCoefficient(*u0);
|
||||
|
||||
// Create data collection for solution output: either VisItDataCollection for
|
||||
// ascii data files, or SidreDataCollection for binary data files.
|
||||
DataCollection *dc = NULL;
|
||||
if (visit)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
#ifdef MFEM_USE_SIDRE
|
||||
dc = new SidreDataCollection("Example41", &mesh);
|
||||
#else
|
||||
MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output.");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
dc = new VisItDataCollection("Example41", &mesh);
|
||||
dc->SetPrecision(precision);
|
||||
}
|
||||
dc->RegisterField("solution", &u);
|
||||
dc->SetCycle(0);
|
||||
dc->SetTime(0.0);
|
||||
dc->Save();
|
||||
}
|
||||
|
||||
// 8. Set up paraview visualization, if desired.
|
||||
unique_ptr<ParaViewDataCollection> pv;
|
||||
if (paraview)
|
||||
{
|
||||
pv = make_unique<ParaViewDataCollection>("Example41", &mesh);
|
||||
pv->SetPrefixPath("ParaView");
|
||||
pv->RegisterField("solution", &u);
|
||||
pv->SetLevelsOfDetail(order);
|
||||
pv->SetDataFormat(VTKFormat::BINARY);
|
||||
pv->SetHighOrderOutput(true);
|
||||
pv->SetCycle(0);
|
||||
pv->SetTime(0.0);
|
||||
pv->Save();
|
||||
}
|
||||
|
||||
socketstream sout;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
sout.open(vishost, visport);
|
||||
if (!sout)
|
||||
{
|
||||
cout << "Unable to connect to GLVis server at "
|
||||
<< vishost << ':' << visport << endl;
|
||||
visualization = false;
|
||||
cout << "GLVis visualization disabled.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
sout.precision(precision);
|
||||
sout << "solution\n" << mesh << u;
|
||||
sout << "pause\n";
|
||||
sout << flush;
|
||||
cout << "GLVis visualization paused."
|
||||
<< " Press space (in the GLVis window) to resume it.\n";
|
||||
}
|
||||
}
|
||||
|
||||
// 9. Define the time-dependent evolution operator describing the ODE
|
||||
// right-hand side, and perform time-integration (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
IMEX_Evolution adv(m, k, s, b);
|
||||
|
||||
real_t t = 0.0;
|
||||
adv.SetTime(t);
|
||||
ode_solver->Init(adv);
|
||||
|
||||
bool done = false;
|
||||
for (int ti = 0; !done; )
|
||||
{
|
||||
real_t dt_real = min(dt, t_final - t);
|
||||
ode_solver->Step(u, t, dt_real);
|
||||
ti++;
|
||||
|
||||
done = (t >= t_final - 1e-8*dt);
|
||||
|
||||
if (done || ti % vis_steps == 0)
|
||||
{
|
||||
cout << "time step: " << ti << ", time: " << t << endl;
|
||||
if (paraview)
|
||||
{
|
||||
pv->SetCycle(ti);
|
||||
pv->SetTime(t);
|
||||
pv->Save();
|
||||
}
|
||||
if (visualization)
|
||||
{
|
||||
sout << "solution\n" << mesh << u << flush;
|
||||
}
|
||||
if (visit)
|
||||
{
|
||||
dc->SetCycle(ti);
|
||||
dc->SetTime(t);
|
||||
dc->Save();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
delete fec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Implementation of class IMEX_Evolution
|
||||
IMEX_Evolution::IMEX_Evolution(BilinearForm &M_, BilinearForm &K_,
|
||||
BilinearForm &S_, const Vector &b_)
|
||||
: TimeDependentOperator(M_.FESpace()->GetTrueVSize()),
|
||||
M(M_), K(K_), S(S_), b(b_), z(height)
|
||||
{
|
||||
Array<int> ess_tdof_list;
|
||||
if (M.GetAssemblyLevel() == AssemblyLevel::LEGACY)
|
||||
{
|
||||
M_prec = make_unique<DSmoother>(M.SpMat());
|
||||
M_solver.SetOperator(M.SpMat());
|
||||
implicit_solver = make_unique<Implicit_Solver>(M.SpMat(), S.SpMat(),
|
||||
*M.FESpace());
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Implicit time integration is not supported with partial assembly");
|
||||
}
|
||||
M_solver.SetPreconditioner(*M_prec);
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(100);
|
||||
M_solver.SetPrintLevel(0);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::Mult1(const Vector &x, Vector &y) const
|
||||
{
|
||||
// Perform the explicit step
|
||||
// y = M^{-1} (K x + b)
|
||||
K.Mult(x, z);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::ImplicitSolve2(const real_t dt, const Vector &x, Vector &k)
|
||||
{
|
||||
// Perform the implicit step
|
||||
// solve for k, k = -(M+dt S)^{-1} S x
|
||||
MFEM_VERIFY(implicit_solver != NULL,
|
||||
"Implicit time integration is not supported with partial assembly");
|
||||
S.Mult(x, z);
|
||||
z.Neg();
|
||||
implicit_solver->SetTimeStep(dt);
|
||||
implicit_solver->Mult(z, k);
|
||||
}
|
||||
@@ -1,737 +0,0 @@
|
||||
// MFEM Example 41 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex41p
|
||||
//
|
||||
// Sample runs:
|
||||
// mpirun -np 4 ex41p
|
||||
// mpirun -np 4 ex41p -cg
|
||||
// mpirun -np 4 ex41p -m ../data/periodic-hexagon.mesh -p 0 -dt 0.005 -tf 10
|
||||
// mpirun -np 4 ex41p -m ../data/periodic-square.mesh -p 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex41p -m ../data/periodic-hexagon.mesh -p 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex41p -m ../data/star-q3.mesh -p 1 -rp 1 -dt 0.001 -tf 9
|
||||
// mpirun -np 4 ex41p -m ../data/disc-nurbs.mesh -p 1 -rp 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex41p -m ../data/disc-nurbs.mesh -p 2 -rp 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex41p -m ../data/periodic-square.mesh -rp 2 -dt 0.0025 -tf 9 -vs 20
|
||||
// mpirun -np 4 ex41p -m ../data/periodic-cube.mesh -p 0 -rs 2 -o 2 -dt 0.01 -tf 8
|
||||
//
|
||||
// Device sample runs:
|
||||
//
|
||||
// Description: This example code solves the time-dependent advection-diffusion
|
||||
// equation du/dt + v.grad(u) - a div(grad(u)) = 0, where v is a
|
||||
// given fluid velocity, a is the diffusion coefficient, and
|
||||
// u0(x)=u(0,x) is a given initial condition.
|
||||
//
|
||||
// The example demonstrates the use of Discontinuous Galerkin (DG)
|
||||
// bilinear forms in MFEM (face integrators), DG-LOR Preconditioning
|
||||
// and the use of IMEX ODE time integrators.
|
||||
//
|
||||
// The Option to use Continuous Finite Elements is available too.
|
||||
|
||||
#include "mfem.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Mesh bounding box
|
||||
Vector bb_min, bb_max;
|
||||
|
||||
// Velocity coefficient
|
||||
template<int problem=0>
|
||||
void velocity_function(const Vector &x, Vector &v)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
real_t center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Translations in 1D, 2D, and 3D
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
|
||||
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 2:
|
||||
{
|
||||
// Clockwise rotation in 2D around the origin
|
||||
const real_t w = M_PI/2;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
|
||||
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// Clockwise twisting rotation in 2D around the origin
|
||||
const real_t w = M_PI/2;
|
||||
real_t d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
|
||||
d = d*d;
|
||||
switch (dim)
|
||||
{
|
||||
case 1: v(0) = 1.0; break;
|
||||
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
|
||||
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Initial condition
|
||||
template<int problem=0>
|
||||
real_t u0_function(const Vector &x)
|
||||
{
|
||||
int dim = x.Size();
|
||||
|
||||
// map to the reference [-1,1] domain
|
||||
Vector X(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
{
|
||||
real_t center = (bb_min[i] + bb_max[i]) * 0.5;
|
||||
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
|
||||
}
|
||||
|
||||
switch (problem)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
{
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
return exp(-40.*pow(X(0)-0.5,2));
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
real_t rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
|
||||
if (dim == 3)
|
||||
{
|
||||
const real_t s = (1. + 0.25*cos(2*M_PI*X(2)));
|
||||
rx *= s;
|
||||
ry *= s;
|
||||
}
|
||||
return ( std::erfc(w*(X(0)-cx-rx))*std::erfc(-w*(X(0)-cx+rx)) *
|
||||
std::erfc(w*(X(1)-cy-ry))*std::erfc(-w*(X(1)-cy+ry)) )/16;
|
||||
}
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
real_t x_ = X(0), y_ = X(1), rho, phi;
|
||||
rho = std::hypot(x_, y_);
|
||||
phi = atan2(y_, x_);
|
||||
return pow(sin(M_PI*rho),2)*sin(3*phi);
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
const real_t f = M_PI;
|
||||
return sin(f*X(0))*sin(f*X(1));
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
class Implicit_Solver : public Solver
|
||||
{
|
||||
private:
|
||||
HypreParMatrix &M, &S;
|
||||
HypreParMatrix *A;
|
||||
CGSolver linear_solver;
|
||||
real_t dt;
|
||||
SparseMatrix M_diag;
|
||||
public:
|
||||
Implicit_Solver(HypreParMatrix &M_, HypreParMatrix &S_,
|
||||
const FiniteElementSpace &fes)
|
||||
: M(M_),
|
||||
S(S_),
|
||||
A(nullptr),
|
||||
linear_solver(M.GetComm()),
|
||||
dt(1.0)
|
||||
{
|
||||
linear_solver.iterative_mode = false;
|
||||
linear_solver.SetRelTol(1e-9);
|
||||
linear_solver.SetAbsTol(0.0);
|
||||
linear_solver.SetMaxIter(100);
|
||||
linear_solver.SetPrintLevel(0);
|
||||
|
||||
M.GetDiag(M_diag);
|
||||
}
|
||||
|
||||
void SetTimeStep(real_t dt_)
|
||||
{
|
||||
real_t ddt = dt-dt_;
|
||||
|
||||
// syncronize ddt across all processes
|
||||
MPI_Comm comm = M.GetComm();
|
||||
int myrank;
|
||||
MPI_Comm_rank(comm, &myrank);
|
||||
MPI_Bcast(&ddt, 1, MPI_DOUBLE, 0, comm);
|
||||
|
||||
real_t epsilon;
|
||||
epsilon = std::numeric_limits<real_t>::epsilon();
|
||||
// allow for some tolerance in the time stepping process
|
||||
epsilon*=10;
|
||||
|
||||
if (fabs(ddt) > epsilon)
|
||||
{
|
||||
if (0==myrank)
|
||||
{
|
||||
cout << "Updating Implicit_Solver time step from " << dt
|
||||
<< " to " << dt_ << endl;
|
||||
}
|
||||
|
||||
delete A;
|
||||
dt = dt_;
|
||||
// Form operator A = M + dt*S
|
||||
A = Add(dt, S, 1.0, M);
|
||||
linear_solver.SetOperator(*A);
|
||||
}
|
||||
}
|
||||
|
||||
void SetOperator(const Operator &op) override
|
||||
{
|
||||
linear_solver.SetOperator(op);
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
linear_solver.Mult(x, y);
|
||||
}
|
||||
|
||||
void SetPreconditioner(Solver &precond)
|
||||
{
|
||||
linear_solver.SetPreconditioner(precond);
|
||||
}
|
||||
|
||||
~Implicit_Solver() override
|
||||
{
|
||||
delete A;
|
||||
}
|
||||
};
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE. The DG weak
|
||||
form of the advection-diffusion equation is (M + dt S) du/dt = Su - K u + b
|
||||
, where M and K are the mass and advection matrices, and b describes the
|
||||
flow on the boundary. In the case of IMEX evolution, the diffusion term is
|
||||
treated implicitly, and the advection term is treated explicitly. */
|
||||
class IMEX_Evolution : public TimeDependentOperator
|
||||
{
|
||||
private:
|
||||
OperatorHandle M, K, S, A;
|
||||
const Vector &b;
|
||||
Solver *M_prec;
|
||||
CGSolver M_solver;
|
||||
Implicit_Solver *implicit_solver;
|
||||
LORSolver<HypreBoomerAMG>* lor_solver;
|
||||
|
||||
mutable Vector z;
|
||||
mutable Vector w;
|
||||
|
||||
public:
|
||||
IMEX_Evolution(ParBilinearForm &M_, ParBilinearForm &K_, ParBilinearForm &S_,
|
||||
const Vector &b_, ParBilinearForm &A_);
|
||||
|
||||
virtual
|
||||
~IMEX_Evolution()
|
||||
{
|
||||
delete implicit_solver;
|
||||
delete lor_solver;
|
||||
delete M_prec;
|
||||
}
|
||||
|
||||
void Mult1(const Vector &x, Vector &y) const;
|
||||
|
||||
void ImplicitSolve2(const real_t dt, const Vector &x, Vector &k);
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
if (TimeDependentOperator::EvalMode::ADDITIVE_TERM_1 == GetEvalMode())
|
||||
{
|
||||
Mult1(x,y);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("TimeDependentOperator::Mult() is not overridden!");
|
||||
}
|
||||
}
|
||||
|
||||
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override
|
||||
{
|
||||
if (TimeDependentOperator::EvalMode::ADDITIVE_TERM_2 == GetEvalMode())
|
||||
{
|
||||
ImplicitSolve2(dt,x,k);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("TimeDependentOperator::ImplicitSolve() is not overridden!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI and HYPRE.
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
// 2. Parse command-line options.
|
||||
int problem = 0;
|
||||
const char *mesh_file = "../data/periodic-square.mesh";
|
||||
int ser_ref_levels = 2;
|
||||
int par_ref_levels = 0;
|
||||
int order = 3;
|
||||
int ode_solver_type = 64; // 61 - Forward Backward Euler
|
||||
// 62 - IMEXRK2(2,2,2)
|
||||
// 63 - IMEXRK2(2,3,2)
|
||||
// 64 - IMEXRK3(3,4,3)
|
||||
real_t t_final = 10.0;
|
||||
real_t dt = 0.01;
|
||||
bool paraview = false;
|
||||
bool cg = false;
|
||||
int vis_steps = 50;
|
||||
bool adios2 = false;
|
||||
bool binary = false;
|
||||
real_t diffusion_term = 0.01;
|
||||
real_t kappa = -1.0;
|
||||
real_t sigma = -1.0;
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
int precision = 16;
|
||||
cout.precision(precision);
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&problem, "-p", "--problem",
|
||||
"Problem setup to use. See options in velocity_function().");
|
||||
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial.");
|
||||
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
|
||||
"Number of times to refine the mesh uniformly in parallel.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::IMEXTypes.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&diffusion_term, "-dc", "--diffusion-coeff",
|
||||
"Diffusion coefficient in the PDE.");
|
||||
args.AddOption(¶view, "-paraview", "--paraview-datafiles", "-no-paraview",
|
||||
"--no-paraview-datafiles",
|
||||
"Save data files for ParaView (paraview.org) visualization.");
|
||||
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
|
||||
"--no-visit-datafiles",
|
||||
"Save data files for VisIt (visit.llnl.gov) visualization.");
|
||||
args.AddOption(&adios2, "-adios2", "--adios2-streams", "-no-adios2",
|
||||
"--no-adios2-streams",
|
||||
"Save data using adios2 streams.");
|
||||
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
|
||||
"--ascii-datafiles",
|
||||
"Use binary (Sidre) or ascii format for VisIt data files.");
|
||||
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
|
||||
"Visualize every n-th timestep.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&cg, "-cg", "--continuous-galerkin", "-dg",
|
||||
"--discontinuous-galerkin",
|
||||
"Use Continuous-Galerkin Finite elements (Default is DG)");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (Mpi::Root())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (Mpi::Root())
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
if (kappa < 0)
|
||||
{
|
||||
kappa = (order+1)*(order+1);
|
||||
}
|
||||
|
||||
// 3. Read the mesh from the given mesh file. We can handle geometrically
|
||||
// periodic meshes in this code.
|
||||
Mesh *mesh = new Mesh(mesh_file);
|
||||
const int dim = mesh->Dimension();
|
||||
|
||||
// 4. Define the IMEX (Split) ODE solver used for time integration. The IMEX
|
||||
// solvers currently available are: 55 - Forward Backward Euler,
|
||||
// 56 - IMEXRK2(2,2,2), 57 - IMEXRK2(2,3,2), and
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::SelectIMEX(ode_solver_type);
|
||||
|
||||
// 5. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
// command-line parameter.
|
||||
for (int lev = 0; lev < ser_ref_levels; lev++) { mesh->UniformRefinement(); }
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
mesh->SetCurvature(max(order, 1));
|
||||
}
|
||||
mesh->GetBoundingBox(bb_min, bb_max, max(order, 1));
|
||||
|
||||
|
||||
// 6. Define the parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
for (int lev = 0; lev < par_ref_levels; lev++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 7. Define the discontinuous DG finite element space of the given
|
||||
// polynomial order on the refined mesh.
|
||||
FiniteElementCollection *fec = NULL;
|
||||
if (cg)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new DG_FECollection(order, dim, BasisType::GaussLobatto);
|
||||
}
|
||||
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, fec);
|
||||
|
||||
HYPRE_BigInt global_vSize = fes->GlobalTrueVSize();
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "Number of unknowns: " << global_vSize << endl;
|
||||
}
|
||||
|
||||
// 8. Set up and assemble the bilinear and linear forms corresponding to the
|
||||
// DG discretization. The DGTraceIntegrator involves integrals over mesh
|
||||
// interior faces.
|
||||
std::unique_ptr<VectorFunctionCoefficient> velocity;
|
||||
if (0==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<0>));
|
||||
}
|
||||
else if (1==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<1>));
|
||||
}
|
||||
else if (2==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<2>));
|
||||
}
|
||||
else if (3==problem)
|
||||
{
|
||||
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<3>));
|
||||
}
|
||||
ConstantCoefficient diff_coeff(diffusion_term);
|
||||
ConstantCoefficient dt_diff_coeff(dt*diffusion_term);
|
||||
|
||||
ParBilinearForm *m = new ParBilinearForm(fes);
|
||||
ParBilinearForm *k = new ParBilinearForm(fes);
|
||||
ParBilinearForm *s = new ParBilinearForm(fes);
|
||||
|
||||
m->AddDomainIntegrator(new MassIntegrator());
|
||||
|
||||
constexpr real_t alpha = -1.0;
|
||||
k->AddDomainIntegrator(new ConvectionIntegrator(*velocity, alpha));
|
||||
|
||||
s->AddDomainIntegrator(new DiffusionIntegrator(diff_coeff));
|
||||
|
||||
// For the preconditioner - create billinear form corresponding to
|
||||
// operator (M + dt S)
|
||||
ParBilinearForm *a = new ParBilinearForm(fes);
|
||||
a->AddDomainIntegrator(new MassIntegrator);
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(dt_diff_coeff));
|
||||
if (!cg)
|
||||
{
|
||||
k->AddInteriorFaceIntegrator(new NonconservativeDGTraceIntegrator(*velocity,
|
||||
alpha));
|
||||
k->AddBdrFaceIntegrator(new NonconservativeDGTraceIntegrator(*velocity, alpha));
|
||||
s->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(diff_coeff, sigma,
|
||||
kappa));
|
||||
s->AddBdrFaceIntegrator(new DGDiffusionIntegrator(diff_coeff, sigma, kappa));
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(dt_diff_coeff, sigma,
|
||||
kappa));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(dt_diff_coeff, sigma, kappa));
|
||||
}
|
||||
|
||||
int skip_zeros = 0;
|
||||
m->Assemble(skip_zeros);
|
||||
k->Assemble(skip_zeros);
|
||||
s->Assemble(skip_zeros);
|
||||
a->Assemble();
|
||||
|
||||
m->Finalize(skip_zeros);
|
||||
k->Finalize(skip_zeros);
|
||||
s->Finalize(skip_zeros);
|
||||
a->Finalize(skip_zeros);
|
||||
HypreParVector b(fes);
|
||||
b = 0.0;
|
||||
|
||||
// 9. Define the initial conditions. Set up visualization (if desired).
|
||||
std::unique_ptr<FunctionCoefficient> u0;
|
||||
if (0==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<0>));
|
||||
}
|
||||
else if (1==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<1>));
|
||||
}
|
||||
else if (2==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<2>));
|
||||
}
|
||||
else if (3==problem)
|
||||
{
|
||||
u0.reset(new FunctionCoefficient(u0_function<3>));
|
||||
}
|
||||
ParGridFunction *u = new ParGridFunction(fes);
|
||||
u->ProjectCoefficient(*u0);
|
||||
HypreParVector *U = u->GetTrueDofs();
|
||||
|
||||
DataCollection *dc = NULL;
|
||||
if (visit)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
#ifdef MFEM_USE_SIDRE
|
||||
dc = new SidreDataCollection("Example41-Parallel", pmesh);
|
||||
#else
|
||||
MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output.");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
dc = new VisItDataCollection("Example41-Parallel", pmesh);
|
||||
dc->SetPrecision(precision);
|
||||
// To save the mesh using MFEM's parallel mesh format:
|
||||
// dc->SetFormat(DataCollection::PARALLEL_FORMAT);
|
||||
}
|
||||
dc->RegisterField("solution", u);
|
||||
dc->SetCycle(0);
|
||||
dc->SetTime(0.0);
|
||||
dc->Save();
|
||||
}
|
||||
ParaViewDataCollection *pd = NULL;
|
||||
if (paraview)
|
||||
{
|
||||
pd = new ParaViewDataCollection("Example41P", pmesh);
|
||||
pd->SetPrefixPath("ParaView");
|
||||
pd->RegisterField("solution", u);
|
||||
pd->SetLevelsOfDetail(order);
|
||||
pd->SetDataFormat(VTKFormat::BINARY);
|
||||
pd->SetHighOrderOutput(true);
|
||||
pd->SetCycle(0);
|
||||
pd->SetTime(0.0);
|
||||
pd->Save();
|
||||
}
|
||||
socketstream sout;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
sout.open(vishost, visport);
|
||||
if (!sout)
|
||||
{
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "Unable to connect to GLVis server at "
|
||||
<< vishost << ':' << visport << endl;
|
||||
}
|
||||
visualization = false;
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "GLVis visualization disabled.\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sout << "parallel " << num_procs << " " << myid << "\n";
|
||||
sout.precision(precision);
|
||||
sout << "solution\n" << *pmesh << *u;
|
||||
sout << "pause\n";
|
||||
sout << flush;
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "GLVis visualization paused."
|
||||
<< " Press space (in the GLVis window) to resume it.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
ADIOS2DataCollection *adios2_dc = NULL;
|
||||
if (adios2)
|
||||
{
|
||||
std::string postfix(mesh_file);
|
||||
postfix.erase(0, std::string("../data/").size() );
|
||||
postfix += "_o" + std::to_string(order);
|
||||
const std::string collection_name = "ex41-p-" + postfix + ".bp";
|
||||
|
||||
adios2_dc = new ADIOS2DataCollection(MPI_COMM_WORLD, collection_name, pmesh);
|
||||
// output data substreams are half the number of mpi processes
|
||||
adios2_dc->SetParameter("SubStreams", std::to_string(num_procs/2) );
|
||||
// adios2_dc->SetLevelsOfDetail(2);
|
||||
adios2_dc->RegisterField("solution", u);
|
||||
adios2_dc->SetCycle(0);
|
||||
adios2_dc->SetTime(0.0);
|
||||
adios2_dc->Save();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// 10. Define the time-dependent evolution operator describing the
|
||||
// ODE right-hand side, and perform time-integration (looping
|
||||
// over the time iterations, ti, with a time-step dt).
|
||||
IMEX_Evolution adv(*m, *k, *s, b, *a);
|
||||
|
||||
real_t t = 0.0;
|
||||
adv.SetTime(t);
|
||||
ode_solver->Init(adv);
|
||||
|
||||
|
||||
bool done = false;
|
||||
for (int ti = 0; !done; )
|
||||
{
|
||||
real_t dt_real = min(dt, t_final - t);
|
||||
ode_solver->Step(*U, t, dt_real);
|
||||
ti++;
|
||||
|
||||
done = (t >= t_final - 1e-8*dt);
|
||||
|
||||
if (done || ti % vis_steps == 0)
|
||||
{
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "time step: " << ti << ", time: " << t << endl;
|
||||
}
|
||||
*u = *U;
|
||||
if (visualization)
|
||||
{
|
||||
sout << "parallel " << num_procs << " " << myid << "\n";
|
||||
sout << "solution\n" << *pmesh << *u << flush;
|
||||
}
|
||||
if (paraview)
|
||||
{
|
||||
pd->SetCycle(ti);
|
||||
pd->SetTime(t);
|
||||
pd->Save();
|
||||
}
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
// transient solutions can be visualized with ParaView
|
||||
if (adios2)
|
||||
{
|
||||
adios2_dc->SetCycle(ti);
|
||||
adios2_dc->SetTime(t);
|
||||
adios2_dc->Save();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Free the used memory.
|
||||
delete pd;
|
||||
delete U;
|
||||
delete u;
|
||||
delete a;
|
||||
delete s;
|
||||
delete k;
|
||||
delete m;
|
||||
delete fes;
|
||||
delete pmesh;
|
||||
delete dc;
|
||||
delete fec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Implementation of class IMEX_Evolution
|
||||
IMEX_Evolution::IMEX_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
|
||||
ParBilinearForm &S_, const Vector &b_, ParBilinearForm &A_)
|
||||
: TimeDependentOperator(M_.ParFESpace()->GetTrueVSize()), b(b_),
|
||||
M_solver(M_.ParFESpace()->GetComm()), z(height), w(height)
|
||||
{
|
||||
if (M_.GetAssemblyLevel()==AssemblyLevel::LEGACY)
|
||||
{
|
||||
M.Reset(M_.ParallelAssemble(), true);
|
||||
K.Reset(K_.ParallelAssemble(), true);
|
||||
S.Reset(S_.ParallelAssemble(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
M.Reset(&M_, false);
|
||||
K.Reset(&K_, false);
|
||||
S.Reset(&S_, false);
|
||||
}
|
||||
|
||||
M_solver.SetOperator(*M);
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
if (M_.GetAssemblyLevel() == AssemblyLevel::LEGACY)
|
||||
{
|
||||
A.Reset(A_.ParallelAssemble(), true);
|
||||
HypreParMatrix &M_mat = *M.As<HypreParMatrix>();
|
||||
HypreParMatrix &S_mat = *S.As<HypreParMatrix>();
|
||||
HypreSmoother *hypre_prec = new HypreSmoother(M_mat, HypreSmoother::Jacobi);
|
||||
M_prec = hypre_prec;
|
||||
|
||||
implicit_solver = new Implicit_Solver(M_mat, S_mat, *M_.FESpace());
|
||||
lor_solver = new LORSolver<HypreBoomerAMG>(A_, ess_tdof_list);
|
||||
lor_solver->GetSolver().SetSystemsOptions(A_.ParFESpace()->GetVDim(), true);
|
||||
implicit_solver -> SetPreconditioner(*lor_solver);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Implicit time integration is not supported with partial assembly");
|
||||
}
|
||||
M_solver.SetPreconditioner(*M_prec);
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(1e-9);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(100);
|
||||
M_solver.SetPrintLevel(0);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::Mult1(const Vector &x, Vector &y) const
|
||||
{
|
||||
// Perform the explicit step
|
||||
// y = M^{-1} (K x + b)
|
||||
K->Mult(x, z);
|
||||
z += b;
|
||||
M_solver.Mult(z, y);
|
||||
}
|
||||
|
||||
void IMEX_Evolution::ImplicitSolve2(const real_t dt, const Vector &x, Vector &k)
|
||||
{
|
||||
// Perform the implicit step
|
||||
// solve for k, k = -(M+dt S)^{-1} S x
|
||||
MFEM_VERIFY(implicit_solver != NULL,
|
||||
"Implicit time integration is not supported with partial assembly");
|
||||
S->Mult(x, z);
|
||||
z*= -1.0;
|
||||
implicit_solver->SetTimeStep(dt);
|
||||
implicit_solver->Mult(z, k);
|
||||
}
|
||||
+1
-6
@@ -9,7 +9,6 @@
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh -o 2 -pa
|
||||
// mpirun -np 4 ex4p -m ../data/escher.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb -ea
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q2.vtk
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q3.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/square-disc-nurbs.mesh -o 3
|
||||
@@ -18,18 +17,14 @@
|
||||
// mpirun -np 4 ex4p -m ../data/periodic-cube.mesh -no-bc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb
|
||||
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb -ea
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb -ea
|
||||
// mpirun -np 4 ex4p -m ../data/ref-prism.mesh -o 1
|
||||
// mpirun -np 4 ex4p -m ../data/octahedron.mesh -o 1
|
||||
// mpirun -np 4 ex4p -m ../data/star-surf.mesh -o 3 -hb
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d cuda
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -ea -hb -d cuda
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -ea -hb -d cuda
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-cuda
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-omp
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh -pa -d cuda
|
||||
@@ -235,7 +230,7 @@ int main(int argc, char *argv[])
|
||||
pcg->SetMaxIter(2000);
|
||||
pcg->SetPrintLevel(1);
|
||||
if (hybridization) { prec = new HypreBoomerAMG(*A.As<HypreParMatrix>()); }
|
||||
else if (pa || ea) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
|
||||
else if (pa) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
|
||||
else
|
||||
{
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
|
||||
+1
-20
@@ -160,7 +160,6 @@ int main(int argc, char *argv[])
|
||||
bool paraview = false;
|
||||
bool binary = false;
|
||||
int vis_steps = 5;
|
||||
bool solve_implicit_state = false;
|
||||
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
@@ -188,9 +187,6 @@ int main(int argc, char *argv[])
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
|
||||
"-imp-slope", "--implicit-slope",
|
||||
"Implicitly solve for stage state or slope.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -370,11 +366,6 @@ int main(int argc, char *argv[])
|
||||
// right-hand side, and perform time-integration (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
FE_Evolution adv(m, k, b);
|
||||
using ImplicitVariableType = FE_Evolution::ImplicitVariableType;
|
||||
ImplicitVariableType imp_var = solve_implicit_state ?
|
||||
ImplicitVariableType::STATE
|
||||
: ImplicitVariableType::SLOPE;
|
||||
adv.SetImplicitVariableType(imp_var);
|
||||
|
||||
real_t t = 0.0;
|
||||
adv.SetTime(t);
|
||||
@@ -468,17 +459,7 @@ void FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
|
||||
{
|
||||
MFEM_VERIFY(dg_solver != NULL,
|
||||
"Implicit time integration is not supported with partial assembly");
|
||||
// Construct current right-hand side for stage state vs. slope solve
|
||||
if (ImplicitVarTypeIsState())
|
||||
{
|
||||
// k, on return, is the stage value u
|
||||
M.Mult(x, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k, on return, is the stage slope du/dt
|
||||
K.Mult(x, z);
|
||||
}
|
||||
K.Mult(x, z);
|
||||
z += b;
|
||||
dg_solver->SetTimeStep(dt);
|
||||
dg_solver->Mult(z, k);
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
#include <mfem.hpp>
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "minja.hpp"
|
||||
#include "myqfunction.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using namespace mfem::future;
|
||||
|
||||
template<class T>
|
||||
struct remove_cvref
|
||||
{
|
||||
using type = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
};
|
||||
|
||||
template <typename qf_t>
|
||||
auto process(qf_t qf)
|
||||
{
|
||||
using qfsig = typename create_function_signature<qf_t>::type;
|
||||
using qfpar_t = typename qfsig::parameter_ts;
|
||||
using qfout_t = typename qfsig::return_t;
|
||||
|
||||
auto qfparams = decay_tuple<qfpar_t> {};
|
||||
|
||||
auto in_str = apply([](auto&&... arg)
|
||||
{
|
||||
return std::vector<std::string>
|
||||
{
|
||||
std::string(get_type_name<typename remove_cvref<decltype(arg)>::type>())...
|
||||
};
|
||||
}, qfparams);
|
||||
|
||||
std::vector<std::string> out_str
|
||||
{
|
||||
std::string(get_type_name<typename remove_cvref<qfout_t>::type>())
|
||||
};
|
||||
|
||||
return std::tuple{in_str, out_str};
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// load the kernel template
|
||||
std::ifstream
|
||||
kernel_istream("/Users/andrej1/repos/mfem/examples/kernel_skeleton.jinja");
|
||||
if (!kernel_istream.is_open())
|
||||
{
|
||||
std::cerr << "error opening jinja template file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::stringstream buffer;
|
||||
buffer << kernel_istream.rdbuf();
|
||||
|
||||
std::string fileContent = buffer.str();
|
||||
auto kernel_tmpl = minja::Parser::parse(buffer.str(), /* options= */ {});
|
||||
|
||||
auto [in_str, out_str] = process(myqfunction0);
|
||||
|
||||
for (auto &v : in_str)
|
||||
{
|
||||
std::cout << v << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
const size_t DUMMY_STRIDE = 64*32*32;
|
||||
const size_t basis_p_1d = 2;
|
||||
|
||||
json context_json{};
|
||||
context_json["kernel_name"] = "demo";
|
||||
|
||||
context_json["spaces"].push_back(
|
||||
{
|
||||
{"P_1D", basis_p_1d},
|
||||
{"dim", 3},
|
||||
{"needs_value", true},
|
||||
{"needs_grad", true},
|
||||
});
|
||||
|
||||
context_json["spaces"].push_back(
|
||||
{
|
||||
{"P_1D", basis_p_1d},
|
||||
});
|
||||
|
||||
context_json["inputs"].push_back(
|
||||
{
|
||||
{"name", "potential"},
|
||||
{"space_idx", 0},
|
||||
{"num_comp", 1},
|
||||
{"comp_stride", DUMMY_STRIDE},
|
||||
{"eval_grad", true},
|
||||
});
|
||||
|
||||
context_json["inputs"].push_back(
|
||||
{
|
||||
{"name", "weights"},
|
||||
{"space_idx", 0},
|
||||
{"num_comp", 1},
|
||||
{"comp_stride", DUMMY_STRIDE},
|
||||
{"is_qdata", true},
|
||||
});
|
||||
|
||||
context_json["outputs"].push_back(
|
||||
{
|
||||
{"name", "solution"},
|
||||
{"space_idx", 0},
|
||||
{"num_comp", 1},
|
||||
{"comp_stride", DUMMY_STRIDE},
|
||||
{"eval_grad", true},
|
||||
});
|
||||
|
||||
const size_t nqf = 1;
|
||||
const std::vector<std::string> qfunc_names = {"myqfunction0"};
|
||||
const std::vector<std::vector<size_t>> qfunc_inputs = {{0, 1, 2}};
|
||||
|
||||
for (size_t i = 0; i < nqf; i++)
|
||||
{
|
||||
json inarr = json::array();
|
||||
for (size_t j = 0; j < qfunc_inputs[i].size(); j++)
|
||||
{
|
||||
inarr.push_back(
|
||||
{
|
||||
{"index", j},
|
||||
{"datatype", in_str[j]}
|
||||
});
|
||||
}
|
||||
|
||||
context_json["qfuncs"].push_back(
|
||||
{
|
||||
{"name", qfunc_names[i]},
|
||||
{"inputs", inarr}
|
||||
});
|
||||
}
|
||||
|
||||
std::cout << context_json.dump(2) << std::endl;
|
||||
|
||||
auto context = minja::Context::make(context_json);
|
||||
auto kernel_source = kernel_tmpl->render(context);
|
||||
|
||||
std::cout << ">>> generated kernel source\n"
|
||||
<< kernel_source
|
||||
<< "\n<<< generated kernel source\n"
|
||||
<< std::endl;
|
||||
|
||||
{
|
||||
// test casting
|
||||
std::vector<real_t> d(4);
|
||||
int i = 0;
|
||||
for (auto &v : d)
|
||||
{
|
||||
v = ++i;
|
||||
}
|
||||
|
||||
mfem::future::tensor<real_t, 2, 2> *dudxi =
|
||||
reinterpret_cast<mfem::future::tensor<real_t, 2, 2> *>(d.data());
|
||||
|
||||
std::cout << *dudxi << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+1
-20
@@ -257,7 +257,6 @@ int main(int argc, char *argv[])
|
||||
bool adios2 = false;
|
||||
bool binary = false;
|
||||
int vis_steps = 5;
|
||||
bool solve_implicit_state = false;
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
PrecType prec_type = PrecType::AIR;
|
||||
#else
|
||||
@@ -291,9 +290,6 @@ int main(int argc, char *argv[])
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
|
||||
"-imp-slope", "--implicit-slope",
|
||||
"Implicitly solve for stage state or slope.");
|
||||
args.AddOption((int *)&prec_type, "-pt", "--prec-type", "Preconditioner for "
|
||||
"implicit solves. 0 for ILU, 1 for pAIR-AMG.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
@@ -540,11 +536,6 @@ int main(int argc, char *argv[])
|
||||
// right-hand side, and perform time-integration (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
FE_Evolution adv(*m, *k, *B, prec_type);
|
||||
using ImplicitVariableType = FE_Evolution::ImplicitVariableType;
|
||||
ImplicitVariableType imp_var = solve_implicit_state ?
|
||||
ImplicitVariableType::STATE
|
||||
: ImplicitVariableType::SLOPE;
|
||||
adv.SetImplicitVariableType(imp_var);
|
||||
|
||||
real_t t = 0.0;
|
||||
adv.SetTime(t);
|
||||
@@ -685,17 +676,7 @@ FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
|
||||
// (M - dt*K) d = K*u + b
|
||||
void FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
|
||||
{
|
||||
// Construct current right-hand side for stage state vs. slope solve
|
||||
if (ImplicitVarTypeIsState())
|
||||
{
|
||||
// k, on return, is the stage value u
|
||||
M->Mult(x, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k, on return, is the stage slope du/dt
|
||||
K->Mult(x, z);
|
||||
}
|
||||
K->Mult(x, z);
|
||||
z += b;
|
||||
dg_solver->SetTimeStep(dt);
|
||||
dg_solver->Mult(z, k);
|
||||
|
||||
@@ -14,12 +14,6 @@ list(APPEND GINKGO_EXAMPLES_SRCS
|
||||
ex1.cpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_MPI AND GINKGO_BUILD_MPI)
|
||||
list(APPEND GINKGO_EXAMPLES_SRCS
|
||||
ex1p.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR})
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ int main(int argc, char *argv[])
|
||||
Ginkgo::IcPreconditioner ginkgo_precond(exec, "paric", 30);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, ginkgo_precond);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetRelTol(1e-12);
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
@@ -225,7 +225,7 @@ int main(int argc, char *argv[])
|
||||
Ginkgo::MFEMPreconditioner gko_M(exec, M);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetRelTol(1e-12);
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
@@ -283,7 +283,7 @@ int main(int argc, char *argv[])
|
||||
Ginkgo::MFEMPreconditioner gko_M(exec, M);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetRelTol(1e-12);
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
|
||||
@@ -1,436 +0,0 @@
|
||||
// MFEM Example 1 - Parallel Version
|
||||
// GINKGO Modification
|
||||
//
|
||||
// Compile with: make ex1p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex1p -m ../data/square-disc.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/star-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/octahedron.mesh -o 1
|
||||
// mpirun -np 4 ex1p -m ../data/periodic-annulus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../data/periodic-torus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/star-mixed-p2.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/pipe-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/ball-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed-p2.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/star-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/inline-segment.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex1p -pa -d cuda
|
||||
// mpirun -np 4 ex1p -fa -d cuda
|
||||
// mpirun -np 4 ex1p -pa -d occa-cuda
|
||||
// mpirun -np 4 ex1p -pa -d raja-omp
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -o 4 -a
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/square-mixed.mesh
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/fichera-mixed.mesh
|
||||
// * mpirun -np 4 ex1p -pa -d ceed-cuda
|
||||
// * mpirun -np 4 ex1p -pa -d ceed-hip
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/square-mixed.mesh
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
|
||||
// NURBS mesh, etc.)
|
||||
//
|
||||
// The example highlights the use of mesh refinement, finite
|
||||
// element grid functions, as well as linear and bilinear forms
|
||||
// corresponding to the left-hand side and right-hand side of the
|
||||
// discrete linear system. We also cover the explicit elimination
|
||||
// of essential boundary conditions, static condensation, and the
|
||||
// optional connection to the GLVis tool for visualization.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#ifndef MFEM_USE_GINKGO
|
||||
#error This example requires that MFEM is built with MFEM_USE_GINKGO=YES
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI and HYPRE.
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../../data/star.mesh";
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
bool fa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
int solver_config = 0;
|
||||
int print_lvl = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&fa, "-fa", "--full-assembly", "-no-fa",
|
||||
"--no-full-assembly", "Enable Full Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&solver_config, "-s", "--solver-config",
|
||||
"Solver and preconditioner combination: \n\t"
|
||||
" 0 - Ginkgo solver and Ginkgo preconditioner, \n\t"
|
||||
" 1 - Ginkgo solver and MFEM preconditioner, \n\t"
|
||||
" 2 - MFEM solver and Ginkgo preconditioner, \n\t"
|
||||
" 3 - MFEM solver and MFEM preconditioner.");
|
||||
args.AddOption(&print_lvl, "-pl", "--print-level",
|
||||
"Print level for iterative solver (1 prints every iteration).");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
device.SetGPUAwareMPI(true);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 4. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 10,000 elements.
|
||||
{
|
||||
int ref_levels =
|
||||
(int)floor(log(10000./mesh.GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
{
|
||||
int par_ref_levels = 2;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh.UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
bool delete_fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
else if (pmesh.GetNodes())
|
||||
{
|
||||
fec = pmesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
ParFiniteElementSpace fespace(&pmesh, fec);
|
||||
HYPRE_BigInt size = fespace.GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 8. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (pmesh.bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 9. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (1,phi_i) where phi_i are the basis functions in fespace.
|
||||
ParLinearForm b(&fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b.Assemble();
|
||||
|
||||
// 10. Define the solution vector x as a parallel finite element grid
|
||||
// function corresponding to fespace. Initialize x with initial guess of
|
||||
// zero, which satisfies the boundary conditions.
|
||||
ParGridFunction x(&fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the
|
||||
// Diffusion domain integrator.
|
||||
ParBilinearForm a(&fespace);
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
if (fa)
|
||||
{
|
||||
a.SetAssemblyLevel(AssemblyLevel::FULL);
|
||||
// Sort the matrix column indices when running on GPU or with OpenMP (i.e.
|
||||
// when Device::IsEnabled() returns true). This makes the results
|
||||
// bit-for-bit deterministic at the cost of somewhat longer run time.
|
||||
a.EnableSparseMatrixSorting(Device::IsEnabled());
|
||||
}
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
// 12. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a.EnableStaticCondensation(); }
|
||||
a.Assemble();
|
||||
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
|
||||
// 13. Solve the linear system A X = B.
|
||||
if (!pa)
|
||||
{
|
||||
switch (solver_config)
|
||||
{
|
||||
// Solve the linear system with CG + Schwarz (with IC) from Ginkgo
|
||||
case 0:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + preconditioner...\n"; }
|
||||
Ginkgo::GinkgoExecutor exec(device);
|
||||
Ginkgo::IcPreconditioner local_solver(exec, "exact");
|
||||
Ginkgo::SchwarzPreconditioner gko_M(exec, MPI_COMM_WORLD, local_solver);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, MPI_COMM_WORLD, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
ginkgo_solver.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
|
||||
// Solve the linear system with CG from Ginkgo + MFEM preconditioner
|
||||
case 1:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + MFEM preconditioner...\n"; }
|
||||
Ginkgo::GinkgoExecutor exec(device);
|
||||
//Create MFEM preconditioner and wrap it for Ginkgo's use.
|
||||
HypreBoomerAMG M((HypreParMatrix&)(*A));
|
||||
Ginkgo::MFEMPreconditioner gko_M(exec, M, MPI_COMM_WORLD);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, MPI_COMM_WORLD, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
ginkgo_solver.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
|
||||
// Ginkgo Schwarz preconditioner (local ParIC) + MFEM CG solver
|
||||
case 2:
|
||||
{
|
||||
if (myid == 0) { cout << "Using MFEM solver + Ginkgo preconditioner...\n"; }
|
||||
Ginkgo::GinkgoExecutor exec(device);
|
||||
Ginkgo::IcPreconditioner local_M(exec, "exact");
|
||||
Ginkgo::SchwarzPreconditioner M(exec, MPI_COMM_WORLD, local_M);
|
||||
M.SetOperator(*(A.Ptr())); // Generate the preconditioner for the matrix A.
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(sqrt(1e-12));
|
||||
cg.SetMaxIter(400);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
|
||||
// MFEM solver + MFEM preconditioner
|
||||
case 3:
|
||||
{
|
||||
if (myid == 0) { cout << "Using MFEM solver + MFEM preconditioner...\n"; }
|
||||
HypreBoomerAMG M((HypreParMatrix&)(*A));
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(sqrt(1e-12));
|
||||
cg.SetMaxIter(400);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
} // End switch on solver_config
|
||||
}
|
||||
// Partial assembly mode. Cannot use Ginkgo preconditioners, but can use Ginkgo
|
||||
// solvers.
|
||||
else
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
// Use Jacobi preconditioning in partial assembly mode.
|
||||
OperatorJacobiSmoother M(a, ess_tdof_list);
|
||||
switch (solver_config)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + preconditioner...\n"; }
|
||||
MFEM_ABORT("Cannot use Ginkgo preconditioner in partial assembly mode.\n"
|
||||
" Try -s 1 to test Ginkgo solver with an MFEM preconditioner.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Use Ginkgo solver with MFEM preconditioner
|
||||
case 1:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + MFEM preconditioner...\n"; }
|
||||
Ginkgo::GinkgoExecutor exec(device);
|
||||
// Wrap MFEM preconditioner for Ginkgo's use.
|
||||
Ginkgo::MFEMPreconditioner gko_M(exec, M, MPI_COMM_WORLD);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, MPI_COMM_WORLD, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
ginkgo_solver.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
|
||||
// No Ginkgo preconditioners work with matrix-free; error
|
||||
case 2:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + preconditioner...\n"; }
|
||||
MFEM_ABORT("Cannot use Ginkgo preconditioner in partial assembly mode.\n"
|
||||
" Try -s 1 to test Ginkgo solver with an MFEM preconditioner.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Use MFEM solver and preconditioner
|
||||
case 3:
|
||||
{
|
||||
if (myid == 0) { cout << "Using MFEM solver + MFEM preconditioner...\n"; }
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(sqrt(1e-12));
|
||||
cg.SetMaxIter(400);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
} // End switch on solver_config
|
||||
}
|
||||
else // CG with no preconditioning
|
||||
{
|
||||
if (myid == 0) { cout << "Using MFEM solver + no preconditioner...\n"; }
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(sqrt(1e-12));
|
||||
cg.SetMaxIter(400);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
}
|
||||
}
|
||||
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh.Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -20,8 +20,9 @@ CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
# Currently there are only serial Ginkgo examples
|
||||
SEQ_EXAMPLES = ex1
|
||||
PAR_EXAMPLES = ex1p
|
||||
PAR_EXAMPLES =
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "util.hpp"
|
||||
|
||||
#define NUM_SPACES {{ spaces | count }}
|
||||
#define NUM_INPUTS {{ inputs | count }}
|
||||
#define NUM_OUTPUTS {{ outputs | count }}
|
||||
|
||||
extern "C" __global__ void dfem_jit_{{kernel_name}}(int num_entities, const real_t *fields[NUM_INPUTS], real_t *outputs[NUM_OUTPUTS], const real_t *B[NUM_SPACES]) {
|
||||
// transform fields
|
||||
const real_t *inputs = ...;
|
||||
|
||||
// call qfunctions
|
||||
{% for qf in qfuncs -%}
|
||||
{
|
||||
{%- for qfinput in qf.inputs %}
|
||||
{{ qfinput.datatype }}* in{{ loop.index0 }} =
|
||||
reinterpret_cast<{{ qfinput.datatype }}>(inputs[{{ qfinput.index }}]);
|
||||
{% endfor %}
|
||||
{{ qf.name }}({% for qfinput in qf.inputs %}*in{{ loop.index0 }}{{ "," if not loop.last else "" }}{% endfor %});
|
||||
}
|
||||
{% endfor %}
|
||||
}
|
||||
+2
-6
@@ -22,11 +22,11 @@ MFEM_LIB_FILE = mfem_is_not_built
|
||||
|
||||
SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \
|
||||
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 ex30 \
|
||||
ex31 ex33 ex34 ex36 ex37 ex38 ex39 ex40 ex41
|
||||
ex31 ex33 ex34 ex36 ex37 ex38 ex39 ex40
|
||||
PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \
|
||||
ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \
|
||||
ex25p ex26p ex27p ex28p ex29p ex30p ex31p ex32p ex33p ex34p ex35p ex36p \
|
||||
ex37p ex39p ex40p ex41p
|
||||
ex37p ex39p ex40p
|
||||
SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex14 ex22 ex24 ex25 ex26 ex34
|
||||
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex14p \
|
||||
ex22p ex24p ex25p ex26p ex34p ex35p
|
||||
@@ -157,10 +157,6 @@ ex37-test-seq: ex37
|
||||
@$(call mfem-test,$<,, Serial example,-mi 3)
|
||||
ex37p-test-par: ex37p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example,-mi 3)
|
||||
ex41-test-seq: ex41
|
||||
@$(call mfem-test,$<,, Serial example,-tf 1.0)
|
||||
ex41p-test-par: ex41p
|
||||
@$(call mfem-test,$<, $(RUN_MPI), Parallel example,-tf 1.0)
|
||||
# Testing: optional tests
|
||||
ifeq ($(MFEM_USE_STRUMPACK),YES)
|
||||
ex11p-test-strumpack: ex11p
|
||||
|
||||
+4137
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
#include <mfem.hpp>
|
||||
|
||||
using namespace mfem;
|
||||
using mfem::future::tensor;
|
||||
|
||||
constexpr int dim = 2;
|
||||
|
||||
tensor<real_t, dim, dim> myqfunction0(
|
||||
const tensor<real_t, dim, dim> &dvdxi,
|
||||
const tensor<real_t, dim, dim> &J,
|
||||
const real_t &w)
|
||||
{
|
||||
const auto invJ = inv(J);
|
||||
const auto dvdx = dvdxi * invJ;
|
||||
const auto test_function_terms = inv(J);
|
||||
return dot(dvdx, J) * det(J) * w * test_function_terms;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,183 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
|
||||
#include <cstdint> // int64_t, uint64_t
|
||||
#include <map> // map
|
||||
#include <memory> // allocator
|
||||
#include <string> // string
|
||||
#include <vector> // vector
|
||||
|
||||
// #include <nlohmann/detail/abi_macros.hpp>
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// This file contains all macro definitions affecting or depending on the ABI
|
||||
|
||||
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
|
||||
#if defined(NLOHMANN_JSON_VERSION_MAJOR) && \
|
||||
defined(NLOHMANN_JSON_VERSION_MINOR) && \
|
||||
defined(NLOHMANN_JSON_VERSION_PATCH)
|
||||
#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || \
|
||||
NLOHMANN_JSON_VERSION_PATCH != 0
|
||||
#warning "Already included a different version of the library!"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
|
||||
#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum)
|
||||
#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum)
|
||||
|
||||
#ifndef JSON_DIAGNOSTICS
|
||||
#define JSON_DIAGNOSTICS 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_DIAGNOSTIC_POSITIONS
|
||||
#define JSON_DIAGNOSTIC_POSITIONS 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS
|
||||
#endif
|
||||
|
||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||
#endif
|
||||
|
||||
// Construct the namespace ABI tags component
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi##a##b##c
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)
|
||||
|
||||
#define NLOHMANN_JSON_ABI_TAGS \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
||||
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
|
||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS)
|
||||
|
||||
// Construct the namespace version component
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||
_v##major##_##minor##_##patch
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
|
||||
|
||||
#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION
|
||||
#else
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
|
||||
NLOHMANN_JSON_VERSION_MINOR, \
|
||||
NLOHMANN_JSON_VERSION_PATCH)
|
||||
#endif
|
||||
|
||||
// Combine namespace components
|
||||
#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a##b
|
||||
#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
|
||||
NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE
|
||||
#define NLOHMANN_JSON_NAMESPACE \
|
||||
nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT(NLOHMANN_JSON_ABI_TAGS, \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION)
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
#define NLOHMANN_JSON_NAMESPACE_BEGIN \
|
||||
namespace nlohmann { \
|
||||
inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAGS, NLOHMANN_JSON_NAMESPACE_VERSION) {
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_END
|
||||
#define NLOHMANN_JSON_NAMESPACE_END \
|
||||
} /* namespace (inline namespace) NOLINT(readability/namespace) */ \
|
||||
} // namespace nlohmann
|
||||
#endif
|
||||
|
||||
/*!
|
||||
@brief namespace for Niels Lohmann
|
||||
@see https://github.com/nlohmann
|
||||
@since version 1.0.0
|
||||
*/
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
/*!
|
||||
@brief default JSONSerializer template argument
|
||||
|
||||
This serializer ignores the template arguments and uses ADL
|
||||
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
|
||||
for serialization.
|
||||
*/
|
||||
template <typename T = void, typename SFINAE = void> struct adl_serializer;
|
||||
|
||||
/// a class to store JSON values
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/
|
||||
template <template <typename U, typename V, typename... Args> class ObjectType =
|
||||
std::map,
|
||||
template <typename U, typename... Args> class ArrayType = std::vector,
|
||||
class StringType = std::string, class BooleanType = bool,
|
||||
class NumberIntegerType = std::int64_t,
|
||||
class NumberUnsignedType = std::uint64_t,
|
||||
class NumberFloatType = double,
|
||||
template <typename U> class AllocatorType = std::allocator,
|
||||
template <typename T, typename SFINAE = void> class JSONSerializer =
|
||||
adl_serializer,
|
||||
class BinaryType =
|
||||
std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||
class CustomBaseClass = void>
|
||||
class basic_json;
|
||||
|
||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value
|
||||
/// within a JSON document
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||
template <typename RefStringType> class json_pointer;
|
||||
|
||||
/*!
|
||||
@brief default specialization
|
||||
@sa https://json.nlohmann.me/api/json/
|
||||
*/
|
||||
using json = basic_json<>;
|
||||
|
||||
/// @brief a minimal map-like container that preserves insertion order
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/
|
||||
template <class Key, class T, class IgnoredLess, class Allocator>
|
||||
struct ordered_map;
|
||||
|
||||
/// @brief specialization that maintains the insertion order of object keys
|
||||
/// @sa https://json.nlohmann.me/api/ordered_json/
|
||||
using ordered_json = basic_json<nlohmann::ordered_map>;
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
@@ -179,7 +179,6 @@ set(SRCS
|
||||
hyperbolic.cpp
|
||||
integrator.cpp
|
||||
bounds.cpp
|
||||
particleset.cpp
|
||||
)
|
||||
|
||||
set(HDRS
|
||||
@@ -309,7 +308,6 @@ set(HDRS
|
||||
hyperbolic.hpp
|
||||
integrator.hpp
|
||||
bounds.hpp
|
||||
particleset.hpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_SIDRE)
|
||||
|
||||
+6
-35
@@ -825,46 +825,14 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
Vector &b, OperatorHandle &A, Vector &X,
|
||||
Vector &B, int copy_interior)
|
||||
{
|
||||
const SparseMatrix *P = fes->GetConformingProlongation();
|
||||
const SparseMatrix *R = fes->GetConformingRestriction();
|
||||
if (ext)
|
||||
{
|
||||
if (hybridization)
|
||||
{
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
|
||||
std::unique_ptr<ConstrainedOperator> A_constrained([&]()
|
||||
{
|
||||
Operator *op;
|
||||
Operator::FormSystemOperator(ess_tdof_list, op);
|
||||
return dynamic_cast<ConstrainedOperator*>(op);
|
||||
}());
|
||||
MFEM_ASSERT(A_constrained != nullptr, "");
|
||||
|
||||
Vector conf_b, conf_x;
|
||||
if (P)
|
||||
{
|
||||
// Nonconforming
|
||||
conf_b.SetSize(P->Width());
|
||||
conf_x.SetSize(P->Width());
|
||||
P->MultTranspose(b, conf_b);
|
||||
R->Mult(x, conf_x);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Conforming
|
||||
conf_b.MakeRef(b, 0, b.Size());
|
||||
conf_x.MakeRef(x, 0, x.Size());
|
||||
}
|
||||
|
||||
A_constrained->EliminateRHS(conf_x, conf_b);
|
||||
|
||||
if (P)
|
||||
{
|
||||
R->MultTranspose(conf_b, b); // store eliminated rhs in b
|
||||
}
|
||||
|
||||
hybridization->ReduceRHS(conf_b, B);
|
||||
ConstrainedOperator A_constrained(this, ess_tdof_list);
|
||||
A_constrained.EliminateRHS(x, b);
|
||||
hybridization->ReduceRHS(b, B);
|
||||
X.SetSize(B.Size());
|
||||
X = 0.0;
|
||||
}
|
||||
@@ -874,6 +842,7 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
}
|
||||
return;
|
||||
}
|
||||
const SparseMatrix *P = fes->GetConformingProlongation();
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
|
||||
// Transform the system and perform the elimination in B, based on the
|
||||
@@ -909,6 +878,7 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
if (hybridization)
|
||||
{
|
||||
// Reduction to the Lagrange multipliers system
|
||||
const SparseMatrix *R = fes->GetConformingRestriction();
|
||||
Vector conf_b(P->Width()), conf_x(P->Width());
|
||||
P->MultTranspose(b, conf_b);
|
||||
R->Mult(x, conf_x);
|
||||
@@ -921,6 +891,7 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
else
|
||||
{
|
||||
// Variational restriction with P
|
||||
const SparseMatrix *R = fes->GetConformingRestriction();
|
||||
B.SetSize(P->Width());
|
||||
P->MultTranspose(b, B);
|
||||
X.SetSize(R->Height());
|
||||
|
||||
+5
-9
@@ -207,8 +207,7 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
|
||||
}
|
||||
}
|
||||
|
||||
PLBound::PLBound(const FiniteElementSpace *fes, const int ncp_i,
|
||||
const int cp_type_i)
|
||||
PLBound::PLBound(FiniteElementSpace *fes, int ncp_i, int cp_type_i)
|
||||
{
|
||||
MFEM_VERIFY(!fes->IsVariableOrder(),
|
||||
"Variable order meshes not yet supported.");
|
||||
@@ -265,8 +264,7 @@ PLBound::PLBound(const FiniteElementSpace *fes, const int ncp_i,
|
||||
Setup(nb, ncp, b_type, cp_type, tol);
|
||||
}
|
||||
|
||||
void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
Vector &intmax) const
|
||||
void PLBound::Get1DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
|
||||
{
|
||||
real_t x,w;
|
||||
intmin.SetSize(ncp);
|
||||
@@ -348,8 +346,7 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
|
||||
}
|
||||
}
|
||||
|
||||
void PLBound::Get2DBounds(const Vector &coeff, Vector &intmin,
|
||||
Vector &intmax) const
|
||||
void PLBound::Get2DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
|
||||
{
|
||||
intmin.SetSize(ncp*ncp);
|
||||
intmax.SetSize(ncp*ncp);
|
||||
@@ -485,8 +482,7 @@ void PLBound::Get2DBounds(const Vector &coeff, Vector &intmin,
|
||||
}
|
||||
}
|
||||
|
||||
void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
|
||||
Vector &intmax) const
|
||||
void PLBound::Get3DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
|
||||
{
|
||||
int nb2 = nb*nb,
|
||||
ncp2 = ncp*ncp,
|
||||
@@ -628,7 +624,7 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
|
||||
}
|
||||
}
|
||||
|
||||
void PLBound::GetNDBounds(const int rdim, const Vector &coeff,
|
||||
void PLBound::GetNDBounds(int rdim, Vector &coeff,
|
||||
Vector &intmin, Vector &intmax) const
|
||||
{
|
||||
if (rdim == 1)
|
||||
|
||||
+8
-9
@@ -9,8 +9,8 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_BOUNDS
|
||||
#define MFEM_BOUNDS
|
||||
#ifndef MFEM_BOUND
|
||||
#define MFEM_BOUND
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "fespace.hpp"
|
||||
@@ -89,8 +89,7 @@ public:
|
||||
}
|
||||
|
||||
// Constructor
|
||||
PLBound(const FiniteElementSpace *fes,
|
||||
const int ncp_i = -1, const int cp_type_i = 0);
|
||||
PLBound(FiniteElementSpace *fes, int ncp_i = -1, int cp_type_i = 0);
|
||||
|
||||
// Get minimum number of control points needed to bound the given bases
|
||||
int GetMinimumPointsForGivenBases(int nb_i, int b_type_i,
|
||||
@@ -106,7 +105,7 @@ public:
|
||||
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 1D/2D/3D.
|
||||
void GetNDBounds(const int rdim, const Vector &coeff,
|
||||
void GetNDBounds(int rdim, Vector &coeff,
|
||||
Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Get number of control points used to compute the bounds.
|
||||
@@ -114,15 +113,15 @@ public:
|
||||
private:
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 1D.
|
||||
void Get1DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
void Get1DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 2D.
|
||||
void Get2DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
void Get2DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Compute piecewise linear bounds for the lexicographically-ordered
|
||||
/// coefficients in @a coeff in 3D.
|
||||
void Get3DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
void Get3DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
|
||||
|
||||
/// Setup matrix used to compute values at given 1D locations in [0,1]
|
||||
/// for Bernstein bases.
|
||||
@@ -134,4 +133,4 @@ private:
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_BOUNDS
|
||||
#endif // MFEM_BOUND
|
||||
|
||||
+3
-71
@@ -1302,73 +1302,6 @@ real_t TraceCoefficient::Eval(ElementTransformation &T,
|
||||
return ma.Trace();
|
||||
}
|
||||
|
||||
VectorComponentCoefficient::VectorComponentCoefficient(VectorCoefficient &A,
|
||||
int c)
|
||||
: a(&A), va(A.GetVDim())
|
||||
{
|
||||
SetComponent(c);
|
||||
}
|
||||
|
||||
void VectorComponentCoefficient::SetComponent(int c)
|
||||
{
|
||||
MFEM_ASSERT(c < a->GetVDim() && c >= 0,
|
||||
"VectorComponentCoefficient: "
|
||||
"Index not in range.");
|
||||
|
||||
component = c;
|
||||
}
|
||||
|
||||
void VectorComponentCoefficient::SetTime(real_t t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
real_t VectorComponentCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
a->Eval(va, T, ip);
|
||||
return va[component];
|
||||
}
|
||||
|
||||
MatrixComponentCoefficient::MatrixComponentCoefficient(MatrixCoefficient &A,
|
||||
int ri, int ci)
|
||||
: a(&A), ma(A.GetHeight(), A.GetWidth())
|
||||
{
|
||||
SetRowIndex(ri);
|
||||
SetColumnIndex(ci);
|
||||
}
|
||||
|
||||
void MatrixComponentCoefficient::SetRowIndex(int ri)
|
||||
{
|
||||
MFEM_ASSERT(ri < a->GetHeight() && ri >= 0,
|
||||
"MatrixComponentCoefficient: "
|
||||
"Row index not in range.");
|
||||
|
||||
row_idx = ri;
|
||||
}
|
||||
|
||||
void MatrixComponentCoefficient::SetColumnIndex(int ci)
|
||||
{
|
||||
MFEM_ASSERT(ci < a->GetWidth() && ci >= 0,
|
||||
"MatrixComponentCoefficient: "
|
||||
"Column index not in range.");
|
||||
col_idx = ci;
|
||||
}
|
||||
|
||||
void MatrixComponentCoefficient::SetTime(real_t t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
real_t MatrixComponentCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
a->Eval(ma, T, ip);
|
||||
return ma(row_idx,col_idx);
|
||||
}
|
||||
|
||||
VectorSumCoefficient::VectorSumCoefficient(int dim)
|
||||
: VectorCoefficient(dim),
|
||||
ACoef(NULL), BCoef(NULL),
|
||||
@@ -2094,7 +2027,7 @@ void CoefficientVector::Project(MatrixCoefficient &coeff, bool transpose)
|
||||
{
|
||||
if (auto *const_coeff = dynamic_cast<MatrixConstantCoefficient*>(&coeff))
|
||||
{
|
||||
SetConstant(const_coeff->GetMatrix(), transpose);
|
||||
SetConstant(const_coeff->GetMatrix());
|
||||
}
|
||||
else if (auto *const_sym_coeff =
|
||||
dynamic_cast<SymmetricMatrixConstantCoefficient*>(&coeff))
|
||||
@@ -2155,7 +2088,7 @@ void CoefficientVector::SetConstant(const Vector &constant)
|
||||
}
|
||||
}
|
||||
|
||||
void CoefficientVector::SetConstant(const DenseMatrix &constant, bool transpose)
|
||||
void CoefficientVector::SetConstant(const DenseMatrix &constant)
|
||||
{
|
||||
const int nq = (storage & CoefficientStorage::CONSTANTS) ? 1 : qs.GetSize();
|
||||
const int width = constant.Width();
|
||||
@@ -2168,8 +2101,7 @@ void CoefficientVector::SetConstant(const DenseMatrix &constant, bool transpose)
|
||||
{
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
const real_t val = transpose ? constant(j,i) : constant(i,j);
|
||||
(*this)[i + j*height + iq*vdim] = val;
|
||||
(*this)[i + j*height + iq*vdim] = constant(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-84
@@ -114,10 +114,11 @@ public:
|
||||
/// Construct the constant coefficient using a vector of constants.
|
||||
/** @a c should be a vector defined by attributes, so for region with
|
||||
attribute @a i @a c[i-1] is the coefficient in that region */
|
||||
PWConstCoefficient(const Vector &c) { UpdateConstants(c); }
|
||||
PWConstCoefficient(Vector &c)
|
||||
{ constants.SetSize(c.Size()); constants=c; }
|
||||
|
||||
/// Update the constants with vector @a c.
|
||||
void UpdateConstants(const Vector &c) { constants = c; }
|
||||
void UpdateConstants(Vector &c) { constants.SetSize(c.Size()); constants=c; }
|
||||
|
||||
/// Return a reference to the i-th constant
|
||||
real_t &operator()(int i) { return constants(i-1); }
|
||||
@@ -1331,8 +1332,8 @@ public:
|
||||
/// Get the coefficient located at (i,j) in the matrix.
|
||||
Coefficient* GetCoeff (int i, int j) { return Coeff[i*width+j]; }
|
||||
|
||||
/** @brief Set the coefficient located at (i,j) in the matrix. By default
|
||||
this will take ownership of the Coefficient passed in, but this
|
||||
/** @brief Set the coefficient located at (i,j) in the matrix. By default by
|
||||
default this will take ownership of the Coefficient passed in, but this
|
||||
can be overridden with the @a own parameter. */
|
||||
void Set(int i, int j, Coefficient * c, bool own=true);
|
||||
|
||||
@@ -1872,85 +1873,6 @@ public:
|
||||
const IntegrationPoint &ip) override;
|
||||
};
|
||||
|
||||
/// Scalar coefficient defined as component of a vector coefficient
|
||||
class VectorComponentCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
VectorCoefficient *a = nullptr;
|
||||
|
||||
mutable Vector va;
|
||||
int component;
|
||||
|
||||
public:
|
||||
/// Construct with a vector coefficient.
|
||||
VectorComponentCoefficient(VectorCoefficient &A)
|
||||
: a(&A), va(A.GetVDim()), component(0) {};
|
||||
|
||||
VectorComponentCoefficient(VectorCoefficient &A, int c);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(real_t t) override;
|
||||
|
||||
/// Reset the vector coefficient
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
|
||||
/// Return the vector coefficient
|
||||
VectorCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Set the component
|
||||
void SetComponent(int c);
|
||||
|
||||
/// Return the component
|
||||
int GetComponent() const { return component; }
|
||||
|
||||
/// Evaluate the trace coefficient at @a ip.
|
||||
real_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
};
|
||||
|
||||
/// Scalar coefficient defined as component of a matrix coefficient
|
||||
class MatrixComponentCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
MatrixCoefficient *a = nullptr;
|
||||
|
||||
mutable DenseMatrix ma;
|
||||
int row_idx,col_idx;
|
||||
|
||||
public:
|
||||
MatrixComponentCoefficient(MatrixCoefficient &A)
|
||||
: a(&A), ma(A.GetHeight(), A.GetWidth()), row_idx(0), col_idx(0) {};
|
||||
|
||||
/// Construct with the matrix coefficient.
|
||||
MatrixComponentCoefficient(MatrixCoefficient &A, int ri, int ci);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(real_t t) override;
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
|
||||
/// Return the matrix coefficient
|
||||
MatrixCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the index
|
||||
void SetRowIndex(int ri);
|
||||
|
||||
/// Return the index
|
||||
int GetRowIndex() const { return row_idx; }
|
||||
|
||||
/// Reset the index
|
||||
void SetColumnIndex(int ci);
|
||||
|
||||
/// Return the index
|
||||
int GetColumnIndex() const { return col_idx; }
|
||||
|
||||
|
||||
/// Evaluate the trace coefficient at @a ip.
|
||||
real_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
};
|
||||
|
||||
/// Vector coefficient defined as the linear combination of two vectors
|
||||
class VectorSumCoefficient : public VectorCoefficient
|
||||
{
|
||||
@@ -2598,7 +2520,7 @@ public:
|
||||
void SetConstant(const Vector &constant);
|
||||
|
||||
/// Set this vector to the given constant matrix.
|
||||
void SetConstant(const DenseMatrix &constant, bool transpose=false);
|
||||
void SetConstant(const DenseMatrix &constant);
|
||||
|
||||
/// Set this vector to the given constant symmetric matrix.
|
||||
void SetConstant(const DenseSymmetricMatrix &constant);
|
||||
|
||||
+47
-269
@@ -70,8 +70,8 @@ ConduitDataCollection::~ConduitDataCollection()
|
||||
void ConduitDataCollection::Save()
|
||||
{
|
||||
std::string dir_name = MeshDirectoryName();
|
||||
int err_ = create_directory(dir_name, mesh, myid);
|
||||
if (err_)
|
||||
int err = create_directory(dir_name, mesh, myid);
|
||||
if (err)
|
||||
{
|
||||
MFEM_ABORT("Error creating directory: " << dir_name);
|
||||
}
|
||||
@@ -88,7 +88,6 @@ void ConduitDataCollection::Save()
|
||||
<< verify_info.to_json());
|
||||
}
|
||||
|
||||
// wrap all grid functions
|
||||
FieldMapConstIterator itr;
|
||||
for ( itr = field_map.begin(); itr != field_map.end(); itr++)
|
||||
{
|
||||
@@ -104,16 +103,6 @@ void ConduitDataCollection::Save()
|
||||
}
|
||||
}
|
||||
|
||||
// wrap all quadrature functions
|
||||
QFieldMapConstIterator qf_itr;
|
||||
for ( qf_itr = q_field_map.begin(); qf_itr != q_field_map.end(); qf_itr++)
|
||||
{
|
||||
std::string name = qf_itr->first;
|
||||
QuadratureFunction *qf = qf_itr->second;
|
||||
QuadratureFunctionToBlueprintField(qf,
|
||||
n_mesh["fields"][name]);
|
||||
}
|
||||
|
||||
// save mesh data
|
||||
SaveMeshAndFields(myid,
|
||||
n_mesh,
|
||||
@@ -168,16 +157,6 @@ ConduitDataCollection::SetProtocol(const std::string &protocol)
|
||||
relay_protocol = protocol;
|
||||
}
|
||||
|
||||
// Conduit data type id for the MFEM precision
|
||||
constexpr conduit::index_t mfem_precision_conduit_id =
|
||||
#if defined(MFEM_USE_DOUBLE)
|
||||
CONDUIT_NATIVE_DOUBLE_ID;
|
||||
#elif defined(MFEM_USE_SINGLE)
|
||||
CONDUIT_NATIVE_FLOAT_ID;
|
||||
#else
|
||||
#error Unknown MFEM precision
|
||||
#endif
|
||||
|
||||
//------------------------------
|
||||
// begin static public methods
|
||||
//------------------------------
|
||||
@@ -227,41 +206,42 @@ ConduitDataCollection::BlueprintMeshToMesh(const Node &n_mesh,
|
||||
// get the number of points
|
||||
int num_verts = n_coordset_vals[0].dtype().number_of_elements();
|
||||
// get vals for points
|
||||
const real_t *verts_ptr = NULL;
|
||||
const double *verts_ptr = NULL;
|
||||
|
||||
// the mfem mesh constructor needs coords with interleaved (aos) type
|
||||
// ordering, even for 1d + 2d we always need 3 real_t (double/float) b/c it
|
||||
// uses Array<Vertex> and Vertex is a pod of 3 real_t. we check for this
|
||||
// ordering, even for 1d + 2d we always need 3 doubles b/c it uses
|
||||
// Array<Vertex> and Vertex is a pod of 3 doubles. we check for this
|
||||
// case, if we don't have it we convert the data
|
||||
|
||||
if (ndims == 3 &&
|
||||
n_coordset_vals[0].dtype().id() == mfem_precision_conduit_id &&
|
||||
n_coordset_vals[0].dtype().is_double() &&
|
||||
blueprint::mcarray::is_interleaved(n_coordset_vals) )
|
||||
{
|
||||
// already interleaved mcarray of 3 real_t (double/float),
|
||||
// already interleaved mcarray of 3 doubles,
|
||||
// return ptr to beginning
|
||||
verts_ptr = n_coordset_vals[0].value();
|
||||
}
|
||||
else
|
||||
{
|
||||
Node n_tmp;
|
||||
// check all vals, if we don't have real_t (double/float) convert
|
||||
// to real_t
|
||||
// check all vals, if we don't have doubles convert
|
||||
// to doubles
|
||||
NodeConstIterator itr = n_coordset_vals.children();
|
||||
while (itr.has_next())
|
||||
{
|
||||
const Node &c_vals = itr.next();
|
||||
std::string c_name = itr.name();
|
||||
|
||||
if ( c_vals.dtype().id() == mfem_precision_conduit_id )
|
||||
if ( c_vals.dtype().is_double() )
|
||||
{
|
||||
// zero copy current coords
|
||||
n_tmp[c_name].set_external(c_vals);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert
|
||||
c_vals.to_data_type(mfem_precision_conduit_id, n_tmp[c_name]);
|
||||
c_vals.to_double_array(n_tmp[c_name]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,13 +250,13 @@ ConduitDataCollection::BlueprintMeshToMesh(const Node &n_mesh,
|
||||
if (ndims < 3)
|
||||
{
|
||||
// add dummy z
|
||||
n_tmp["z"].set(DataType(mfem_precision_conduit_id, num_verts));
|
||||
n_tmp["z"].set(DataType::c_double(num_verts));
|
||||
}
|
||||
|
||||
if (ndims < 2)
|
||||
{
|
||||
// add dummy y
|
||||
n_tmp["y"].set(DataType(mfem_precision_conduit_id, num_verts));
|
||||
n_tmp["y"].set(DataType::c_double(num_verts));
|
||||
}
|
||||
|
||||
Node &n_conv_coords_vals = n_conv["coordsets"][coords_name]["values"];
|
||||
@@ -472,7 +452,7 @@ ConduitDataCollection::BlueprintMeshToMesh(const Node &n_mesh,
|
||||
// if nodes gf is attached later, it resets the space dim based
|
||||
// on the gf's fes.
|
||||
Mesh *mesh = new Mesh(// from coordset
|
||||
const_cast<real_t*>(verts_ptr),
|
||||
const_cast<double*>(verts_ptr),
|
||||
num_verts,
|
||||
// from topology
|
||||
const_cast<int*>(elem_indices),
|
||||
@@ -539,7 +519,7 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
// can't return a gf that zero copies the conduit data
|
||||
Node n_conv;
|
||||
|
||||
const real_t *vals_ptr = NULL;
|
||||
const double *vals_ptr = NULL;
|
||||
|
||||
int vdim = 1;
|
||||
|
||||
@@ -549,10 +529,10 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
{
|
||||
vdim = n_field["values"].number_of_children();
|
||||
|
||||
// need to check that we have real_t (double/float) and
|
||||
// need to check that we have doubles and
|
||||
// cover supported layouts
|
||||
|
||||
if ( n_field["values"][0].dtype().id() == mfem_precision_conduit_id )
|
||||
if ( n_field["values"][0].dtype().is_double() )
|
||||
{
|
||||
// check for contig
|
||||
if (n_field["values"].is_contiguous())
|
||||
@@ -576,26 +556,27 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
vals_ptr = n_conv["values"].child(0).value();
|
||||
}
|
||||
}
|
||||
else // convert to real_t (double/float) and use contig
|
||||
else // convert to doubles and use contig
|
||||
{
|
||||
Node n_tmp;
|
||||
// check all vals, if we don't have real_t (double/float) convert
|
||||
// to real_t
|
||||
// check all vals, if we don't have doubles convert
|
||||
// to doubles
|
||||
NodeConstIterator itr = n_field["values"].children();
|
||||
while (itr.has_next())
|
||||
{
|
||||
const Node &c_vals = itr.next();
|
||||
std::string c_name = itr.name();
|
||||
|
||||
if ( c_vals.dtype().id() == mfem_precision_conduit_id )
|
||||
if ( c_vals.dtype().is_double() )
|
||||
{
|
||||
// zero copy current coords
|
||||
n_tmp[c_name].set_external(c_vals);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert
|
||||
c_vals.to_data_type(mfem_precision_conduit_id, n_tmp[c_name]);
|
||||
c_vals.to_double_array(n_tmp[c_name]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,15 +589,14 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n_field["values"].dtype().id() == mfem_precision_conduit_id &&
|
||||
if (n_field["values"].dtype().is_double() &&
|
||||
n_field["values"].is_compact())
|
||||
{
|
||||
vals_ptr = n_field["values"].value();
|
||||
}
|
||||
else
|
||||
{
|
||||
n_field["values"].to_data_type(mfem_precision_conduit_id,
|
||||
n_conv["values"]);
|
||||
n_field["values"].to_double_array(n_conv["values"]);
|
||||
vals_ptr = n_conv["values"].value();
|
||||
}
|
||||
}
|
||||
@@ -640,14 +620,14 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
|
||||
if (zero_copy)
|
||||
{
|
||||
res = new GridFunction(fes,const_cast<real_t*>(vals_ptr));
|
||||
res = new GridFunction(fes,const_cast<double*>(vals_ptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy case, this constructor will alloc the space for the GF data
|
||||
res = new GridFunction(fes);
|
||||
// create an mfem vector that wraps the conduit data
|
||||
Vector vals_vec(const_cast<real_t*>(vals_ptr),fes->GetVSize());
|
||||
Vector vals_vec(const_cast<double*>(vals_ptr),fes->GetVSize());
|
||||
// copy values into the result
|
||||
(*res) = vals_vec;
|
||||
}
|
||||
@@ -659,155 +639,6 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
return res;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
mfem::QuadratureFunction *
|
||||
ConduitDataCollection::BlueprintFieldToQuadratureFunction(Mesh *mesh,
|
||||
const Node &n_field,
|
||||
bool zero_copy)
|
||||
{
|
||||
// n_conv holds converted data (when necessary for mfem api)
|
||||
// if n_conv is used ( !n_conv.dtype().empty() ) we
|
||||
// know that some data allocation was necessary, so we
|
||||
// can't return a qf that zero copies the conduit data
|
||||
Node n_conv;
|
||||
|
||||
const real_t *vals_ptr = NULL;
|
||||
int vdim = 1;
|
||||
|
||||
if (n_field["values"].dtype().is_object())
|
||||
{
|
||||
vdim = n_field["values"].number_of_children();
|
||||
|
||||
// need to check that we have real_t (double/float) and
|
||||
// cover supported layouts
|
||||
if ( n_field["values"][0].dtype().id() == mfem_precision_conduit_id )
|
||||
{
|
||||
// quad funcs use what mfem calls byVDIM
|
||||
// and what conduit calls interleaved
|
||||
// check for interleaved
|
||||
if (blueprint::mcarray::is_interleaved(n_field["values"]))
|
||||
{
|
||||
// conduit mcarray interleaved == mfem byVDIM
|
||||
vals_ptr = n_field["values"].child(0).value();
|
||||
}
|
||||
else
|
||||
{
|
||||
// for mcarray generic case -- default to byVDIM
|
||||
// aka interleaved
|
||||
blueprint::mcarray::to_interleaved(n_field["values"],
|
||||
n_conv["values"]);
|
||||
vals_ptr = n_conv["values"].child(0).value();
|
||||
}
|
||||
}
|
||||
else // convert to real_t (double/float) and use interleaved
|
||||
{
|
||||
Node n_tmp;
|
||||
// check all vals, if we don't have real_t (double/float) convert
|
||||
// to real_t
|
||||
NodeConstIterator itr = n_field["values"].children();
|
||||
while (itr.has_next())
|
||||
{
|
||||
const Node &c_vals = itr.next();
|
||||
std::string c_name = itr.name();
|
||||
|
||||
if ( c_vals.dtype().id() == mfem_precision_conduit_id )
|
||||
{
|
||||
// zero copy current coords
|
||||
n_tmp[c_name].set_external(c_vals);
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert
|
||||
c_vals.to_data_type(mfem_precision_conduit_id, n_tmp[c_name]);
|
||||
}
|
||||
}
|
||||
|
||||
// for mcarray generic case -- default to byVDIM
|
||||
// aka interleaved
|
||||
blueprint::mcarray::to_interleaved(n_tmp,
|
||||
n_conv["values"]);
|
||||
vals_ptr = n_conv["values"].child(0).value();
|
||||
}
|
||||
}
|
||||
else // scalar case
|
||||
{
|
||||
if (n_field["values"].dtype().id() == mfem_precision_conduit_id &&
|
||||
n_field["values"].is_compact())
|
||||
{
|
||||
vals_ptr = n_field["values"].value();
|
||||
}
|
||||
else
|
||||
{
|
||||
n_field["values"].to_data_type(mfem_precision_conduit_id,
|
||||
n_conv["values"]);
|
||||
vals_ptr = n_conv["values"].value();
|
||||
}
|
||||
}
|
||||
|
||||
if (zero_copy && !n_conv.dtype().is_empty())
|
||||
{
|
||||
//Info: "Cannot zero-copy since data conversions were necessary"
|
||||
zero_copy = false;
|
||||
}
|
||||
|
||||
// we need basis name to create the proper mfem quad space and quad func
|
||||
// the pattern used to encode the quad space params is:
|
||||
// QF_{ORDER}_{VDIM}
|
||||
// ORDER is the degree of the polynomials for the quad rule
|
||||
// VDIM is the number of components at each quad point (scalar, vector, etc)
|
||||
|
||||
int qf_order = 0;
|
||||
int qf_vdim = 0;
|
||||
std::string qf_name = n_field["basis"].as_string();
|
||||
const char *qf_name_cstr = qf_name.c_str();
|
||||
if (!strncmp(qf_name_cstr, "QF_", 3))
|
||||
{
|
||||
// parse {ORDER}
|
||||
qf_order = atoi(qf_name_cstr + 3);
|
||||
// find second `_`
|
||||
const char *qf_vdim_cstr = strstr(qf_name_cstr+3,"_");
|
||||
if (qf_vdim_cstr == NULL)
|
||||
{
|
||||
MFEM_ABORT("Error parsing quadrature function description string: "
|
||||
<< qf_name << std::endl
|
||||
<< "Expected: QF_{ORDER}_{VDIM}");
|
||||
}
|
||||
// parse {VDIM}
|
||||
qf_vdim = atoi(qf_vdim_cstr+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Error parsing quadrature function description string: "
|
||||
<< qf_name << std::endl
|
||||
<< "Expected: QF_{ORDER}_{VDIM}");
|
||||
}
|
||||
MFEM_VERIFY(qf_vdim == vdim, "vector dimension mismatch: vdim = " << vdim
|
||||
<< ", qf_vdim = " << qf_vdim);
|
||||
|
||||
mfem::QuadratureSpace *quad_space = new mfem::QuadratureSpace(mesh, qf_order);
|
||||
mfem::QuadratureFunction *res = new mfem::QuadratureFunction();
|
||||
|
||||
if (zero_copy)
|
||||
{
|
||||
res->SetSpace(quad_space, const_cast<real_t*>(vals_ptr), vdim);
|
||||
res->SetOwnsSpace(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
res->SetSpace(quad_space, vdim);
|
||||
res->SetOwnsSpace(true);
|
||||
// copy case, this constructor will alloc the space for the quad data
|
||||
// create an mfem vector that wraps the conduit data
|
||||
Vector vals_vec(const_cast<real_t*>(vals_ptr),res->Size());
|
||||
// copy values into the result
|
||||
(*res) = vals_vec;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
void
|
||||
ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
|
||||
@@ -825,20 +656,20 @@ ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
|
||||
// Setup main coordset
|
||||
////////////////////////////////////////////
|
||||
|
||||
// Assumes mfem::Vertex has the layout of a real_t (double/float) array.
|
||||
// Assumes mfem::Vertex has the layout of a double array.
|
||||
|
||||
// this logic assumes an mfem vertex is always 3 real_t (double/float) wide
|
||||
// this logic assumes an mfem vertex is always 3 doubles wide
|
||||
int stride = sizeof(mfem::Vertex);
|
||||
int num_vertices = mesh->GetNV();
|
||||
|
||||
MFEM_ASSERT( ( stride == 3 * sizeof(real_t) ),
|
||||
MFEM_ASSERT( ( stride == 3 * sizeof(double) ),
|
||||
"Unexpected stride for Vertex");
|
||||
|
||||
Node &n_mesh_coords = n_mesh["coordsets"][coordset_name];
|
||||
n_mesh_coords["type"] = "explicit";
|
||||
|
||||
|
||||
real_t *coords_ptr = mesh->GetVertex(0);
|
||||
double *coords_ptr = mesh->GetVertex(0);
|
||||
|
||||
n_mesh_coords["values/x"].set_external(coords_ptr,
|
||||
num_vertices,
|
||||
@@ -849,14 +680,14 @@ ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
|
||||
{
|
||||
n_mesh_coords["values/y"].set_external(coords_ptr,
|
||||
num_vertices,
|
||||
sizeof(real_t),
|
||||
sizeof(double),
|
||||
stride);
|
||||
}
|
||||
if (dim >= 3)
|
||||
{
|
||||
n_mesh_coords["values/z"].set_external(coords_ptr,
|
||||
num_vertices,
|
||||
sizeof(real_t) * 2,
|
||||
sizeof(double) * 2,
|
||||
stride);
|
||||
}
|
||||
|
||||
@@ -1111,59 +942,6 @@ ConduitDataCollection::GridFunctionToBlueprintField(mfem::GridFunction *gf,
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
void
|
||||
ConduitDataCollection::QuadratureFunctionToBlueprintField(
|
||||
mfem::QuadratureFunction *qf,
|
||||
Node &n_field,
|
||||
const std::string &main_topology_name)
|
||||
{
|
||||
// For quadrature functions, use basis pattern:
|
||||
// QF_{ORDER}_{VDIM}
|
||||
|
||||
int qf_vdim = qf->GetVDim();
|
||||
int qf_order = qf->GetSpace()->GetOrder();
|
||||
int qf_size = qf->GetSpace()->GetSize();
|
||||
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "QF_" << qf_order << "_" << qf_vdim;
|
||||
|
||||
n_field["basis"] = oss.str();
|
||||
n_field["topology"] = main_topology_name;
|
||||
}
|
||||
|
||||
if (qf_vdim == 1) // scalar case
|
||||
{
|
||||
n_field["values"].set_external(const_cast<real_t *>(qf->HostRead()),
|
||||
qf_size);
|
||||
}
|
||||
else // vector case
|
||||
{
|
||||
// deal with striding of all components
|
||||
// quadrature functions are always byVDIM
|
||||
// or what conduit calls interleaved
|
||||
|
||||
index_t offset = 0;
|
||||
index_t stride = sizeof(real_t) * qf_vdim;
|
||||
|
||||
for (int d = 0; d < qf_vdim; d++)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "v" << d;
|
||||
std::string comp_name = oss.str();
|
||||
n_field["values"][comp_name].set_external(const_cast<real_t *>(qf->HostRead()),
|
||||
qf_size,
|
||||
offset,
|
||||
stride);
|
||||
offset += sizeof(real_t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------
|
||||
// end static public methods
|
||||
//------------------------------
|
||||
@@ -1189,7 +967,7 @@ ConduitDataCollection::RootFileName()
|
||||
//---------------------------------------------------------------------------//
|
||||
std::string
|
||||
ConduitDataCollection::MeshFileName(int domain_id,
|
||||
const std::string &relay_protocol_)
|
||||
const std::string &relay_protocol)
|
||||
{
|
||||
std::string res = prefix_path +
|
||||
name +
|
||||
@@ -1198,7 +976,7 @@ ConduitDataCollection::MeshFileName(int domain_id,
|
||||
"/domain_" +
|
||||
to_padded_string(domain_id, pad_digits_rank) +
|
||||
"." +
|
||||
relay_protocol_;
|
||||
relay_protocol;
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -1216,7 +994,7 @@ ConduitDataCollection::MeshDirectoryName()
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
std::string
|
||||
ConduitDataCollection::MeshFilePattern(const std::string &relay_protocol_)
|
||||
ConduitDataCollection::MeshFilePattern(const std::string &relay_protocol)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << name
|
||||
@@ -1225,7 +1003,7 @@ ConduitDataCollection::MeshFilePattern(const std::string &relay_protocol_)
|
||||
<< "/domain_%0"
|
||||
<< pad_digits_rank
|
||||
<< "d."
|
||||
<< relay_protocol_;
|
||||
<< relay_protocol;
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
@@ -1235,14 +1013,14 @@ ConduitDataCollection::MeshFilePattern(const std::string &relay_protocol_)
|
||||
void
|
||||
ConduitDataCollection::SaveRootFile(int num_domains,
|
||||
const Node &n_mesh,
|
||||
const std::string &relay_protocol_)
|
||||
const std::string &relay_protocol)
|
||||
{
|
||||
// default to json root file, except for hdf5 case
|
||||
std::string root_proto = "json";
|
||||
|
||||
if (relay_protocol_ == "hdf5")
|
||||
if (relay_protocol == "hdf5")
|
||||
{
|
||||
root_proto = relay_protocol_;
|
||||
root_proto = relay_protocol;
|
||||
}
|
||||
|
||||
Node n_root;
|
||||
@@ -1273,14 +1051,14 @@ ConduitDataCollection::SaveRootFile(int num_domains,
|
||||
}
|
||||
}
|
||||
// add extra header info
|
||||
n_root["protocol/name"] = relay_protocol_;
|
||||
n_root["protocol/name"] = relay_protocol;
|
||||
n_root["protocol/version"] = "0.3.1";
|
||||
|
||||
|
||||
// we will save one file per domain, so trees == files
|
||||
n_root["number_of_files"] = num_domains;
|
||||
n_root["number_of_trees"] = num_domains;
|
||||
n_root["file_pattern"] = MeshFilePattern(relay_protocol_);
|
||||
n_root["file_pattern"] = MeshFilePattern(relay_protocol);
|
||||
n_root["tree_pattern"] = "";
|
||||
|
||||
// Add the time, time step, and cycle
|
||||
@@ -1295,9 +1073,9 @@ ConduitDataCollection::SaveRootFile(int num_domains,
|
||||
void
|
||||
ConduitDataCollection::SaveMeshAndFields(int domain_id,
|
||||
const Node &n_mesh,
|
||||
const std::string &relay_protocol_)
|
||||
const std::string &relay_protocol)
|
||||
{
|
||||
relay::io::save(n_mesh, MeshFileName(domain_id, relay_protocol_));
|
||||
relay::io::save(n_mesh, MeshFileName(domain_id, relay_protocol));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------//
|
||||
@@ -1394,13 +1172,13 @@ ConduitDataCollection::LoadRootFile(Node &root_out)
|
||||
//---------------------------------------------------------------------------//
|
||||
void
|
||||
ConduitDataCollection::LoadMeshAndFields(int domain_id,
|
||||
const std::string &relay_protocol_)
|
||||
const std::string &relay_protocol)
|
||||
{
|
||||
// Note: This path doesn't use any info from the root file
|
||||
// it uses the implicit mfem ConduitDataCollection layout
|
||||
|
||||
Node n_mesh;
|
||||
relay::io::load( MeshFileName(domain_id, relay_protocol_), n_mesh);
|
||||
relay::io::load( MeshFileName(domain_id, relay_protocol), n_mesh);
|
||||
|
||||
|
||||
Node verify_info;
|
||||
|
||||
@@ -50,11 +50,11 @@ namespace mfem
|
||||
Those that construct MFEM objects from Conduit Nodes (Conduit Blueprint to
|
||||
MFEM) provide a zero-copy option. Zero-copy is only possible if the
|
||||
blueprint data matches the data types provided by the MFEM API, for example:
|
||||
ints for connectivity arrays, real_t (double/float) for field value arrays,
|
||||
allocations that match MFEM's striding options, etc. If these constraints
|
||||
are not met, MFEM objects that own the data are created and returned. In
|
||||
either case pointers to new MFEM object instances are returned, the
|
||||
zero-copy only applies to data backing the MFEM object instances.
|
||||
ints for connectivity arrays, doubles for field value arrays, allocations
|
||||
that match MFEM's striding options, etc. If these constraints are not met,
|
||||
MFEM objects that own the data are created and returned. In either case
|
||||
pointers to new MFEM object instances are returned, the zero-copy only
|
||||
applies to data backing the MFEM object instances.
|
||||
|
||||
@note QuadratureFunction%s (q-fields) are not supported.
|
||||
|
||||
@@ -183,21 +183,6 @@ public:
|
||||
conduit::Node &out,
|
||||
const std::string &main_topology_name = "main");
|
||||
|
||||
/// Describes a MFEM quadrature function using the mesh blueprint
|
||||
/** Sets up passed conduit::Node out to describe the given quadrature function
|
||||
using the mesh field blueprint.
|
||||
|
||||
Zero-copies as much data as possible.
|
||||
|
||||
@a main_toplogy_name is used to set the associated topology name.
|
||||
With the default setting, the resulting field is associated with the
|
||||
topology `main`.
|
||||
*/
|
||||
static void QuadratureFunctionToBlueprintField(QuadratureFunction *qf,
|
||||
conduit::Node &out,
|
||||
const std::string &main_topology_name = "main");
|
||||
|
||||
|
||||
/// Constructs and MFEM mesh from a Conduit Blueprint Description
|
||||
/** @a main_topology_name is used to select which topology to use, when
|
||||
empty ("") the first topology entry will be used.
|
||||
@@ -205,7 +190,7 @@ public:
|
||||
If zero_copy == true, tries to construct a mesh that points to the data
|
||||
described by the conduit node. This is only possible if the data in the
|
||||
node matches the data types needed for the MFEM API (ints for
|
||||
connectivity, real_t for field values, etc). If these constraints are
|
||||
connectivity, doubles for field values, etc). If these constraints are
|
||||
not met, a mesh that owns the data is created and returned.
|
||||
*/
|
||||
static Mesh *BlueprintMeshToMesh(const conduit::Node &n_mesh,
|
||||
@@ -215,7 +200,7 @@ public:
|
||||
/// Constructs and MFEM Grid Function from a Conduit Blueprint Description
|
||||
/** If zero_copy == true, tries to construct a grid function that points to
|
||||
the data described by the conduit node. This is only possible if the data
|
||||
in the node matches the data types needed for the MFEM API (real_t for
|
||||
in the node matches the data types needed for the MFEM API (doubles for
|
||||
field values, allocated in soa or aos ordering, etc). If these
|
||||
constraints are not met, a grid function that owns the data is created
|
||||
and returned.
|
||||
@@ -223,17 +208,6 @@ public:
|
||||
static GridFunction *BlueprintFieldToGridFunction(Mesh *mesh,
|
||||
const conduit::Node &n_field,
|
||||
bool zero_copy = false);
|
||||
/// Constructs and MFEM Quadrature Function from a Conduit Blueprint Description
|
||||
/** If zero_copy == true, tries to construct a quadrature function that points to
|
||||
the data described by the conduit node. This is only possible if the data
|
||||
in the node matches the data types needed for the MFEM API (real_t for
|
||||
field values, allocated in an interleavred/byVDIM order, etc). If these
|
||||
constraints are not met, a grid function that owns the data is created
|
||||
and returned.
|
||||
*/
|
||||
static QuadratureFunction *BlueprintFieldToQuadratureFunction(Mesh *mesh,
|
||||
const conduit::Node &n_field,
|
||||
bool zero_copy = false);
|
||||
|
||||
private:
|
||||
/// Converts from MFEM element type enum to mesh bp shape name
|
||||
|
||||
+5
-40
@@ -430,9 +430,7 @@ void VisItDataCollection::RegisterField(const std::string& name,
|
||||
}
|
||||
|
||||
DataCollection::RegisterField(name, gf);
|
||||
field_info_map[name] = VisItFieldInfo("nodes", gf->VectorDim(), LOD,
|
||||
gf->FESpace()->FEColl()->Name(),
|
||||
gf->FESpace()->FEColl()->GetOrder());
|
||||
field_info_map[name] = VisItFieldInfo("nodes", gf->VectorDim(), LOD);
|
||||
visit_levels_of_detail = std::max(visit_levels_of_detail, LOD);
|
||||
}
|
||||
|
||||
@@ -451,14 +449,7 @@ void VisItDataCollection::RegisterQField(const std::string& name,
|
||||
}
|
||||
|
||||
DataCollection::RegisterQField(name, qf);
|
||||
// For quadrature functions, use basis pattern:
|
||||
// QF_{ORDER}_{VDIM}
|
||||
int qf_vdim = qf->GetVDim();
|
||||
int qf_order = qf->GetSpace()->GetOrder();
|
||||
std::ostringstream oss;
|
||||
oss << "QF_" << qf_order << "_" << qf_vdim;
|
||||
field_info_map[name] = VisItFieldInfo("quadrature", qf->GetVDim(), LOD,
|
||||
oss.str(), qf_order);
|
||||
field_info_map[name] = VisItFieldInfo("elements", 1, LOD);
|
||||
visit_levels_of_detail = std::max(visit_levels_of_detail, LOD);
|
||||
}
|
||||
|
||||
@@ -632,8 +623,7 @@ void VisItDataCollection::LoadFields()
|
||||
{
|
||||
field_map.Register(it->first, new GridFunction(mesh, file), own_data);
|
||||
}
|
||||
else if ((it->second).association == "elements" || // old style
|
||||
(it->second).association == "quadrature") // new style
|
||||
else if ((it->second).association == "elements")
|
||||
{
|
||||
q_field_map.Register(it->first, new QuadratureFunction(mesh, file), own_data);
|
||||
}
|
||||
@@ -647,8 +637,7 @@ void VisItDataCollection::LoadFields()
|
||||
it->first,
|
||||
new ParGridFunction(dynamic_cast<ParMesh*>(mesh), file), own_data);
|
||||
}
|
||||
else if ((it->second).association == "elements" || // old style
|
||||
(it->second).association == "quadrature") // new style
|
||||
else if ((it->second).association == "elements")
|
||||
{
|
||||
q_field_map.Register(it->first, new QuadratureFunction(mesh, file), own_data);
|
||||
}
|
||||
@@ -687,8 +676,6 @@ std::string VisItDataCollection::GetVisItRootString()
|
||||
ftags["assoc"] = picojson::value((it->second).association);
|
||||
ftags["comps"] = picojson::value(to_string((it->second).num_components));
|
||||
ftags["lod"] = picojson::value(to_string((it->second).lod));
|
||||
ftags["basis"] = picojson::value((it->second).basis);
|
||||
ftags["order"] = picojson::value(to_string((it->second).order));
|
||||
field["path"] = picojson::value(path_str + it->first + file_ext_format);
|
||||
field["tags"] = picojson::value(ftags);
|
||||
fields[it->first] = picojson::value(field);
|
||||
@@ -765,31 +752,9 @@ void VisItDataCollection::ParseVisItRootString(const std::string& json)
|
||||
it != fields_obj.end(); ++it)
|
||||
{
|
||||
picojson::value tags = it->second.get("tags");
|
||||
|
||||
// defaults that allow us to parse older mfem_root files
|
||||
int lod = 1;
|
||||
std::string basis = "";
|
||||
int order = -1;
|
||||
|
||||
if (tags.contains("lod"))
|
||||
{
|
||||
lod = to_int(tags.get("lod").get<std::string>());
|
||||
}
|
||||
|
||||
if (tags.contains("basis"))
|
||||
{
|
||||
basis = tags.get("comps").get<std::string>();
|
||||
}
|
||||
|
||||
if (tags.contains("order"))
|
||||
{
|
||||
order = to_int(tags.get("comps").get<std::string>());
|
||||
}
|
||||
|
||||
field_info_map[it->first] =
|
||||
VisItFieldInfo(tags.get("assoc").get<std::string>(),
|
||||
to_int(tags.get("comps").get<std::string>()),
|
||||
lod, basis, order);
|
||||
to_int(tags.get("comps").get<std::string>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-12
@@ -408,18 +408,12 @@ public:
|
||||
class VisItFieldInfo
|
||||
{
|
||||
public:
|
||||
std::string association = "";
|
||||
int num_components = 0;
|
||||
int lod = 1;
|
||||
std::string basis = "";
|
||||
int order = -1;
|
||||
VisItFieldInfo() = default;
|
||||
VisItFieldInfo(std::string association_, int num_components_, int lod_ = 1,
|
||||
std::string basis_ = "", int order_ = -1)
|
||||
{
|
||||
association = association_; num_components = num_components_; lod =lod_;
|
||||
basis = basis_; order = order_;
|
||||
}
|
||||
std::string association;
|
||||
int num_components;
|
||||
int lod;
|
||||
VisItFieldInfo() { association = ""; num_components = 0; lod = 1;}
|
||||
VisItFieldInfo(std::string association_, int num_components_, int lod_ = 1)
|
||||
{ association = association_; num_components = num_components_; lod =lod_;}
|
||||
};
|
||||
|
||||
/// Data collection with VisIt I/O routines
|
||||
|
||||
@@ -1,403 +0,0 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
#pragma once
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
|
||||
/// @brief Assemble element matrix for three dimensional data.
|
||||
///
|
||||
/// Note: In the below layouts, total_trial_op_dim is > 1 if
|
||||
/// there are more than one inputs dependent on the derivative variable.
|
||||
///
|
||||
/// @param A Memory for one element matrix with layout
|
||||
/// [test_ndof, test_vdim, trial_ndof, trial_vdim].
|
||||
/// @param fhat Memory to hold the residual computation with layout
|
||||
/// [test_vdim, test_op_dim, nqp].
|
||||
/// @param qpdc The quadrature point data cache with data layout
|
||||
/// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, nqp].
|
||||
/// @param itod Input Trial Operator Dimension array. If the trial
|
||||
/// operator is not dependent, the dimension is 0 to indicate that.
|
||||
/// @param inputs The input field operator types.
|
||||
/// @param output The output field operator types.
|
||||
/// @param input_dtqmaps The input DofToQuad maps.
|
||||
/// @param output_dtqmap The output DofToQuad maps.
|
||||
/// @param scratch_shmem Scratch shared memory for computations.
|
||||
/// @param q1d The number of quadrature points in one dimension.
|
||||
/// @param td1d The number of trial dofs in one dimension.
|
||||
template <typename input_fop_ts, size_t num_inputs, typename output_fop_t>
|
||||
MFEM_HOST_DEVICE void assemble_element_mat_t3d(
|
||||
const DeviceTensor<4, real_t>& A,
|
||||
const DeviceTensor<3, real_t>& fhat,
|
||||
const DeviceTensor<5, const real_t>& qpdc,
|
||||
const DeviceTensor<1, const real_t>& itod,
|
||||
const input_fop_ts& inputs,
|
||||
const output_fop_t& output,
|
||||
const std::array<DofToQuadMap, num_inputs>& input_dtqmaps,
|
||||
const DofToQuadMap& output_dtqmap,
|
||||
std::array<DeviceTensor<1>, 6>& scratch_shmem,
|
||||
const int& q1d,
|
||||
const int& td1d)
|
||||
{
|
||||
constexpr int dimension = 3;
|
||||
|
||||
// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, num_qp]
|
||||
const int test_vdim = qpdc.GetShape()[0];
|
||||
const int test_op_dim = qpdc.GetShape()[1];
|
||||
const int trial_vdim = qpdc.GetShape()[2];
|
||||
|
||||
// [num_test_dof, ...]
|
||||
const auto num_test_dof = A.GetShape()[0];
|
||||
|
||||
for (int Jx = 0; Jx < td1d; Jx++)
|
||||
{
|
||||
for (int Jy = 0; Jy < td1d; Jy++)
|
||||
{
|
||||
for (int Jz = 0; Jz < td1d; Jz++)
|
||||
{
|
||||
const int J = Jx + td1d * (Jy + td1d * Jz);
|
||||
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
for (int tv = 0; tv < test_vdim; tv++)
|
||||
{
|
||||
for (int tod = 0; tod < test_op_dim; tod++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
fhat(tv, tod, q) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MSVC lambda capture workaround
|
||||
[[maybe_unused]] const auto& inputs_ref = inputs;
|
||||
|
||||
int m_offset = 0;
|
||||
for_constexpr<num_inputs>([&](auto s)
|
||||
{
|
||||
using fop_t = std::decay_t<decltype(get<s>(inputs_ref))>;
|
||||
|
||||
const int trial_op_dim = static_cast<int>(itod(static_cast<int>(s)));
|
||||
if (trial_op_dim == 0)
|
||||
{
|
||||
// This is inside a lambda so we have to return
|
||||
// instead of idiomatic 'continue'.
|
||||
return;
|
||||
}
|
||||
|
||||
auto& B = input_dtqmaps[s].B;
|
||||
auto& G = input_dtqmaps[s].G;
|
||||
|
||||
if constexpr (is_value_fop<fop_t>::value)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t f = qpdc(i, k, j, m + m_offset, q);
|
||||
fhat(i, k, q) += f * B(qx, 0, Jx) * B(qy, 0, Jy) * B(qz, 0, Jz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<fop_t>::value)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t f = qpdc(i, k, j, m + m_offset, q);
|
||||
if (m == 0)
|
||||
{
|
||||
fhat(i, k, q) += f * G(qx, 0, Jx) * B(qy, 0, Jy) * B(qz, 0, Jz);
|
||||
}
|
||||
else if (m == 1)
|
||||
{
|
||||
fhat(i, k, q) += f * B(qx, 0, Jx) * G(qy, 0, Jy) * B(qz, 0, Jz);
|
||||
}
|
||||
else if (m == 2)
|
||||
{
|
||||
fhat(i, k, q) += f * B(qx, 0, Jx) * B(qy, 0, Jy) * G(qz, 0, Jz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ABORT("sum factorized sparse matrix assemble routine "
|
||||
"not implemented for field operator");
|
||||
#endif
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
m_offset += trial_op_dim;
|
||||
});
|
||||
|
||||
auto bvtfhat = Reshape(&A(0, 0, J, j), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(bvtfhat, fhat, output, output_dtqmap,
|
||||
scratch_shmem, dimension, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Assemble element matrix for two dimensional data.
|
||||
///
|
||||
/// Note: In the below layouts, total_trial_op_dim is > 1 if
|
||||
/// there are more than one inputs dependent on the derivative variable.
|
||||
///
|
||||
/// @param A Memory for one element matrix with layout
|
||||
/// [test_ndof, test_vdim, trial_ndof, trial_vdim].
|
||||
/// @param fhat Memory to hold the residual computation with layout
|
||||
/// [test_vdim, test_op_dim, nqp].
|
||||
/// @param qpdc The quadrature point data cache with data layout
|
||||
/// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, nqp].
|
||||
/// @param itod Input Trial Operator Dimension array. If the trial
|
||||
/// operator is not dependent, the dimension is 0 to indicate that.
|
||||
/// @param inputs The input field operator types.
|
||||
/// @param output The output field operator types.
|
||||
/// @param input_dtqmaps The input DofToQuad maps.
|
||||
/// @param output_dtqmap The output DofToQuad maps.
|
||||
/// @param scratch_shmem Scratch shared memory for computations.
|
||||
/// @param q1d The number of quadrature points in one dimension.
|
||||
/// @param td1d The number of trial dofs in one dimension.
|
||||
template <typename input_fop_ts, size_t num_inputs, typename output_fop_t>
|
||||
MFEM_HOST_DEVICE void assemble_element_mat_t2d(
|
||||
const DeviceTensor<4, real_t>& A,
|
||||
const DeviceTensor<3, real_t>& fhat,
|
||||
const DeviceTensor<5, const real_t>& qpdc,
|
||||
const DeviceTensor<1, const real_t>& itod,
|
||||
const input_fop_ts& inputs,
|
||||
const output_fop_t& output,
|
||||
const std::array<DofToQuadMap, num_inputs>& input_dtqmaps,
|
||||
const DofToQuadMap& output_dtqmap,
|
||||
std::array<DeviceTensor<1>, 6>& scratch_shmem,
|
||||
const int& q1d,
|
||||
const int& td1d)
|
||||
{
|
||||
constexpr int dimension = 2;
|
||||
|
||||
// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, num_qp]
|
||||
const int test_vdim = qpdc.GetShape()[0];
|
||||
const int test_op_dim = qpdc.GetShape()[1];
|
||||
const int trial_vdim = qpdc.GetShape()[2];
|
||||
|
||||
// [num_test_dof, ...]
|
||||
const auto num_test_dof = A.GetShape()[0];
|
||||
|
||||
for (int Jx = 0; Jx < td1d; Jx++)
|
||||
{
|
||||
for (int Jy = 0; Jy < td1d; Jy++)
|
||||
{
|
||||
const int J = Jy + Jx * td1d;
|
||||
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
for (int tv = 0; tv < test_vdim; tv++)
|
||||
{
|
||||
for (int tod = 0; tod < test_op_dim; tod++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qy + qx * q1d;
|
||||
fhat(tv, tod, q) = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MSVC lambda capture workaround
|
||||
[[maybe_unused]] const auto& inputs_ref = inputs;
|
||||
|
||||
int m_offset = 0;
|
||||
for_constexpr<num_inputs>([&](auto s)
|
||||
{
|
||||
using fop_t = std::decay_t<decltype(get<s>(inputs_ref))>;
|
||||
|
||||
const int trial_op_dim = static_cast<int>(itod(static_cast<int>(s)));
|
||||
if (trial_op_dim == 0)
|
||||
{
|
||||
// This is inside a lambda so we have to return
|
||||
// instead of idiomatic 'continue'.
|
||||
return;
|
||||
}
|
||||
|
||||
auto& B = input_dtqmaps[s].B;
|
||||
auto& G = input_dtqmaps[s].G;
|
||||
|
||||
if constexpr (is_value_fop<fop_t>::value)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qy + qx * q1d;
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t f = qpdc(i, k, j, m + m_offset, q);
|
||||
fhat(i, k, q) += f * B(qx, 0, Jx) * B(qy, 0, Jy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if constexpr (is_gradient_fop<fop_t>::value)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qy + qx * q1d;
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
const real_t f = qpdc(i, k, j, m + m_offset, q);
|
||||
if (m == 0)
|
||||
{
|
||||
fhat(i, k, q) += f * B(qx, 0, Jx) * G(qy, 0, Jy);
|
||||
}
|
||||
else
|
||||
{
|
||||
fhat(i, k, q) += f * G(qx, 0, Jx) * B(qy, 0, Jy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ABORT("sum factorized sparse matrix assemble routine "
|
||||
"not implemented for field operator");
|
||||
#endif
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
m_offset += trial_op_dim;
|
||||
});
|
||||
|
||||
auto bvtfhat = Reshape(&A(0, 0, J, j), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(bvtfhat, fhat, output, output_dtqmap,
|
||||
scratch_shmem, dimension, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Assemble element matrix for two or three dimensional data.
|
||||
///
|
||||
/// Note: In the below layouts, total_trial_op_dim is > 1 if
|
||||
/// there are more than one inputs dependent on the derivative variable.
|
||||
///
|
||||
/// @param A Memory for one element matrix with layout
|
||||
/// [test_ndof, test_vdim, trial_ndof, trial_vdim].
|
||||
/// @param fhat Memory to hold the residual computation with layout
|
||||
/// [test_vdim, test_op_dim, nqp].
|
||||
/// @param qpdc The quadrature point data cache with data layout
|
||||
/// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, nqp].
|
||||
/// @param itod Input Trial Operator Dimension array. If the trial
|
||||
/// operator is not dependent, the dimension is 0 to indicate that.
|
||||
/// @param inputs The input field operator types.
|
||||
/// @param output The output field operator types.
|
||||
/// @param input_dtqmaps The input DofToQuad maps.
|
||||
/// @param output_dtqmap The output DofToQuad maps.
|
||||
/// @param scratch_shmem Scratch shared memory for computations.
|
||||
/// @param dimension The spatial dimension.
|
||||
/// @param q1d The number of quadrature points in one dimension.
|
||||
/// @param td1d The number of trial dofs in one dimension.
|
||||
/// @param use_sum_factorization Indicator if sum factorization is used.
|
||||
template <typename input_fop_ts, size_t num_inputs, typename output_fop_t>
|
||||
MFEM_HOST_DEVICE void assemble_element_mat_naive(
|
||||
const DeviceTensor<4, real_t>& A,
|
||||
const DeviceTensor<3, real_t>& fhat,
|
||||
const DeviceTensor<5, const real_t>& qpdc,
|
||||
const DeviceTensor<1, const real_t>& itod,
|
||||
const input_fop_ts& inputs,
|
||||
const output_fop_t& output,
|
||||
const std::array<DofToQuadMap, num_inputs>& input_dtqmaps,
|
||||
const DofToQuadMap& output_dtqmap,
|
||||
std::array<DeviceTensor<1>, 6>& scratch_shmem,
|
||||
const int& dimension,
|
||||
const int& q1d,
|
||||
const int& td1d,
|
||||
const bool& use_sum_factorization)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 2)
|
||||
{
|
||||
assemble_element_mat_t2d(A, fhat, qpdc, itod, inputs, output,
|
||||
input_dtqmaps, output_dtqmap, scratch_shmem, q1d, td1d);
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
assemble_element_mat_t3d(A, fhat, qpdc, itod, inputs, output,
|
||||
input_dtqmaps, output_dtqmap, scratch_shmem, q1d, td1d);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
|
||||
MFEM_ABORT("element matrix assemble not implemented for non tensor "
|
||||
"product basis");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem::future
|
||||
+21
-483
@@ -22,7 +22,6 @@
|
||||
#include "interpolate.hpp"
|
||||
#include "integrate.hpp"
|
||||
#include "qfunction_apply.hpp"
|
||||
#include "assemble.hpp"
|
||||
|
||||
namespace mfem::future
|
||||
{
|
||||
@@ -31,23 +30,14 @@ namespace mfem::future
|
||||
using action_t =
|
||||
std::function<void(std::vector<Vector> &, const std::vector<Vector> &, Vector &)>;
|
||||
|
||||
/// @brief Type alias for a function that computes the cache for the action of a derivative
|
||||
using derivative_setup_t =
|
||||
std::function<void(std::vector<Vector> &, const Vector &)>;
|
||||
|
||||
/// @brief Type alias for a function that computes the action of a derivative
|
||||
using derivative_action_t =
|
||||
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
|
||||
|
||||
/// @brief Type alias for a function that assembles the SparseMatrix of a
|
||||
/// derivative operator
|
||||
using assemble_derivative_sparsematrix_callback_t =
|
||||
std::function<void(std::vector<Vector> &, SparseMatrix *&)>;
|
||||
|
||||
/// @brief Type alias for a function that assembles the HypreParMatrix of a
|
||||
/// @brief Type alias for a function that assembles the sparse matrix of a
|
||||
/// derivative operator
|
||||
using assemble_derivative_hypreparmatrix_callback_t =
|
||||
std::function<void(std::vector<Vector> &, HypreParMatrix *&)>;
|
||||
std::function<void(std::vector<Vector> &, HypreParMatrix &)>;
|
||||
|
||||
/// @brief Type alias for a function that applies the appropriate restriction to
|
||||
/// the solution and parameters
|
||||
@@ -91,8 +81,6 @@ public:
|
||||
const std::vector<Vector *> ¶meters_l,
|
||||
const restriction_callback_t &restriction_callback,
|
||||
const std::function<void(Vector &, Vector &)> &prolongation_transpose,
|
||||
const std::vector<assemble_derivative_sparsematrix_callback_t>
|
||||
&assemble_derivative_sparsematrix_callbacks,
|
||||
const std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
&assemble_derivative_hypreparmatrix_callbacks) :
|
||||
Operator(height, width),
|
||||
@@ -103,8 +91,6 @@ public:
|
||||
derivative_actions_transpose(derivative_actions_transpose),
|
||||
transpose_direction(transpose_direction),
|
||||
prolongation_transpose(prolongation_transpose),
|
||||
assemble_derivative_sparsematrix_callbacks(
|
||||
assemble_derivative_sparsematrix_callbacks),
|
||||
assemble_derivative_hypreparmatrix_callbacks(
|
||||
assemble_derivative_hypreparmatrix_callbacks)
|
||||
{
|
||||
@@ -170,29 +156,14 @@ public:
|
||||
prolongation_transpose(daction_l, result_t);
|
||||
};
|
||||
|
||||
/// @brief Assemble the derivative operator into a SparseMatrix.
|
||||
///
|
||||
/// @param A The SparseMatrix to assemble the derivative operator into. Can
|
||||
/// be an uninitialized object.
|
||||
void Assemble(SparseMatrix *&A)
|
||||
{
|
||||
MFEM_ASSERT(!assemble_derivative_sparsematrix_callbacks.empty(),
|
||||
"derivative can't be assembled into a SparseMatrix");
|
||||
|
||||
for (const auto &f : assemble_derivative_sparsematrix_callbacks)
|
||||
{
|
||||
f(fields_e, A);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Assemble the derivative operator into a HypreParMatrix.
|
||||
///
|
||||
/// @param A The HypreParMatrix to assemble the derivative operator into. Can
|
||||
/// be an uninitialized object.
|
||||
void Assemble(HypreParMatrix *&A)
|
||||
void Assemble(HypreParMatrix &A)
|
||||
{
|
||||
MFEM_ASSERT(!assemble_derivative_hypreparmatrix_callbacks.empty(),
|
||||
"derivative can't be assembled into a HypreParMatrix");
|
||||
"derivative can't be assembled into a matrix");
|
||||
|
||||
for (const auto &f : assemble_derivative_hypreparmatrix_callbacks)
|
||||
{
|
||||
@@ -225,10 +196,6 @@ private:
|
||||
|
||||
std::function<void(Vector &, Vector &)> prolongation_transpose;
|
||||
|
||||
/// Callbacks that assemble derivatives into a SparseMatrix.
|
||||
std::vector<assemble_derivative_sparsematrix_callback_t>
|
||||
assemble_derivative_sparsematrix_callbacks;
|
||||
|
||||
/// Callbacks that assemble derivatives into a HypreParMatrix.
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
@@ -431,34 +398,6 @@ public:
|
||||
|
||||
const size_t derivative_idx = FindIdx(derivative_id, fields);
|
||||
|
||||
std::vector<Vector> s_l(solutions_l.size());
|
||||
for (size_t i = 0; i < s_l.size(); i++)
|
||||
{
|
||||
s_l[i] = *sol_l[i];
|
||||
}
|
||||
|
||||
std::vector<Vector> p_l(parameters_l.size());
|
||||
for (size_t i = 0; i < p_l.size(); i++)
|
||||
{
|
||||
p_l[i] = *par_l[i];
|
||||
}
|
||||
|
||||
fields_e.resize(solutions_l.size() + parameters_l.size());
|
||||
restriction_callback(s_l, p_l, fields_e);
|
||||
|
||||
// Dummy
|
||||
Vector dir_l;
|
||||
if (derivative_idx > s_l.size())
|
||||
{
|
||||
dir_l = p_l[derivative_idx - s_l.size()];
|
||||
}
|
||||
else
|
||||
{
|
||||
dir_l = s_l[derivative_idx];
|
||||
}
|
||||
|
||||
derivative_setup_callbacks[derivative_id][0](fields_e, dir_l);
|
||||
|
||||
return std::make_shared<DerivativeOperator>(
|
||||
height,
|
||||
GetTrueVSize(fields[derivative_idx]),
|
||||
@@ -472,7 +411,6 @@ public:
|
||||
par_l,
|
||||
restriction_callback,
|
||||
prolongation_transpose,
|
||||
assemble_derivative_sparsematrix_callbacks[derivative_id],
|
||||
assemble_derivative_hypreparmatrix_callbacks[derivative_id]);
|
||||
}
|
||||
|
||||
@@ -482,14 +420,10 @@ private:
|
||||
MultLevel mult_level = TVECTOR;
|
||||
|
||||
std::vector<action_t> action_callbacks;
|
||||
std::map<size_t, std::vector<derivative_setup_t>> derivative_setup_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> derivative_action_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<derivative_action_t>> daction_transpose_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<assemble_derivative_sparsematrix_callback_t>>
|
||||
assemble_derivative_sparsematrix_callbacks;
|
||||
std::map<size_t,
|
||||
std::vector<assemble_derivative_hypreparmatrix_callback_t>>
|
||||
assemble_derivative_hypreparmatrix_callbacks;
|
||||
@@ -510,8 +444,6 @@ private:
|
||||
std::function<void(Vector &, Vector &)> output_restriction_transpose;
|
||||
restriction_callback_t restriction_callback;
|
||||
|
||||
std::map<size_t, Vector> derivative_qp_caches;
|
||||
|
||||
std::map<size_t, size_t> assembled_vector_sizes;
|
||||
|
||||
bool use_tensor_product_structure = true;
|
||||
@@ -631,13 +563,6 @@ void DifferentiableOperator::AddIntegrator(
|
||||
auto output_to_field =
|
||||
create_descriptors_to_fields_map<entity_t>(fields, outputs);
|
||||
|
||||
// TODO: factor out
|
||||
std::vector<int> inputs_vdim(num_inputs);
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
inputs_vdim[i] = get<i>(inputs).vdim;
|
||||
});
|
||||
|
||||
const Array<int> *elem_attributes = nullptr;
|
||||
if constexpr (std::is_same_v<entity_t, Entity::Element>)
|
||||
{
|
||||
@@ -904,8 +829,7 @@ void DifferentiableOperator::AddIntegrator(
|
||||
|
||||
// print_shared_memory_info(shmem_info);
|
||||
|
||||
Vector direction_e(get_restriction<entity_t>(fields[d_field_idx],
|
||||
element_dof_ordering)->Height());
|
||||
Vector direction_e;
|
||||
Vector derivative_action_e(output_e_size);
|
||||
derivative_action_e = 0.0;
|
||||
|
||||
@@ -917,152 +841,6 @@ void DifferentiableOperator::AddIntegrator(
|
||||
}
|
||||
const auto input_is_dependent = it->second;
|
||||
|
||||
// Trial operator dimension for each input.
|
||||
// The trial operator dimension is set for each input that is
|
||||
// dependent and if it is independent the dimension is 0.
|
||||
Vector inputs_trial_op_dim(num_inputs);
|
||||
int total_trial_op_dim = 0;
|
||||
{
|
||||
auto itod = Reshape(inputs_trial_op_dim.HostReadWrite(), num_inputs);
|
||||
int idx = 0;
|
||||
for_constexpr<num_inputs>([&](auto s)
|
||||
{
|
||||
if (!input_is_dependent[s])
|
||||
{
|
||||
itod(idx) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: BUG! Make this a general function that works for all kinds of inputs.
|
||||
itod(idx) = input_size_on_qp[s] / get<s>(inputs).vdim;
|
||||
}
|
||||
total_trial_op_dim += static_cast<int>(itod(idx));
|
||||
idx++;
|
||||
});
|
||||
}
|
||||
|
||||
// First Input index of the derivative
|
||||
const size_t d_input_idx = [d_field_idx, &input_to_field]
|
||||
{
|
||||
for (size_t i = 0; i < input_to_field.size(); i++)
|
||||
{
|
||||
if (input_to_field[i] == d_field_idx)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return size_t(SIZE_MAX);
|
||||
}();
|
||||
|
||||
const int trial_vdim = GetVDim(fields[d_field_idx]);
|
||||
const int num_trial_dof =
|
||||
get_restriction<entity_t>(fields[d_field_idx], element_dof_ordering)->Height() /
|
||||
inputs_vdim[d_input_idx] / num_entities;
|
||||
const int num_trial_dof_1d =
|
||||
input_dtq_maps[d_input_idx].B.GetShape()[DofToQuadMap::Index::DOF];
|
||||
|
||||
Vector Ae_mem(num_test_dof * test_vdim * num_trial_dof * trial_vdim *
|
||||
num_entities);
|
||||
Ae_mem = 0.0;
|
||||
|
||||
// Quadrature point local derivative cache for each element, with data
|
||||
// layout:
|
||||
// [test_vdim, test_op_dim, trial_vdim, trial_op_dim, qp, num_entities].
|
||||
derivative_qp_caches[derivative_id] = Vector(test_vdim * test_op_dim *
|
||||
trial_vdim *
|
||||
total_trial_op_dim * num_qp * num_entities);
|
||||
// Create local references for MSVC lambda capture compatibility
|
||||
auto& fields_ref = this->fields;
|
||||
auto& derivative_qp_caches_ref = this->derivative_qp_caches[derivative_id];
|
||||
|
||||
// In each of the callbacks we're saving the derivatives in the quadrature point
|
||||
// caches. This trades memory with computational effort but also minimizes
|
||||
// data movement on each multiplication of the gradient with a directional
|
||||
// vector.
|
||||
derivative_setup_callbacks[derivative_id].push_back(
|
||||
[
|
||||
// capture by copy:
|
||||
dimension, // int
|
||||
num_entities, // int
|
||||
num_qp, // int
|
||||
q1d, // int
|
||||
test_vdim, // int (= output_fop.vdim)
|
||||
test_op_dim, // int (derived from output_fop)
|
||||
inputs, // mfem::future::tuple
|
||||
attributes, // Array<int>
|
||||
ir_weights, // DeviceTensor
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
input_to_field, // std::array<int, s>
|
||||
qfunc, // qfunc_t
|
||||
thread_blocks, // ThreadBlocks
|
||||
shmem_cache, // Vector (local)
|
||||
shmem_info, // SharedMemoryInfo
|
||||
// TODO: make this Array<int> a member of the DifferentiableOperator
|
||||
// and capture it by ref.
|
||||
elem_attributes, // Array<int>
|
||||
element_dof_ordering, // ElementDofOrdering
|
||||
|
||||
direction, // FieldDescriptor
|
||||
direction_e, // Vector
|
||||
da_size_on_qp, // int
|
||||
|
||||
total_trial_op_dim,
|
||||
trial_vdim,
|
||||
inputs_trial_op_dim,
|
||||
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref
|
||||
](std::vector<Vector> &f_e, const Vector &dir_l) mutable
|
||||
{
|
||||
restriction<entity_t>(direction, dir_l, direction_e,
|
||||
element_dof_ordering);
|
||||
auto wrapped_fields_e = wrap_fields(f_e, shmem_info.field_sizes,
|
||||
num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(),
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem.ReadWrite(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
|
||||
const auto d_elem_attr = elem_attributes->Read();
|
||||
const bool has_attr = attributes.Size() > 0;
|
||||
const auto d_domain_attr = attributes.Read();
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
|
||||
{
|
||||
if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) { return; }
|
||||
|
||||
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem,
|
||||
direction_shmem, input_shmem,
|
||||
shadow_shmem_, residual_shmem,
|
||||
scratch_shmem] =
|
||||
unpack_shmem(shmem, shmem_info, input_dtq_maps, output_dtq_maps,
|
||||
wrapped_fields_e, wrapped_direction_e, num_qp, e);
|
||||
auto &shadow_shmem = shadow_shmem_;
|
||||
|
||||
map_fields_to_quadrature_data(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_to_field,
|
||||
inputs, ir_weights, scratch_shmem, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
set_zero(shadow_shmem);
|
||||
|
||||
auto qpdc_e = Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp);
|
||||
call_qfunction_derivative<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc_e, itod, da_size_on_qp,
|
||||
q1d, dimension, use_sum_factorization);
|
||||
}, num_entities, thread_blocks, shmem_info.total_size,
|
||||
shmem_cache.ReadWrite());
|
||||
});
|
||||
|
||||
// The derivative action only uses the quadrature point caches and applies
|
||||
// them to an input vector before integrating with the desired trial operator.
|
||||
derivative_action_callbacks[derivative_id].push_back(
|
||||
[
|
||||
// capture by copy:
|
||||
@@ -1079,7 +857,9 @@ void DifferentiableOperator::AddIntegrator(
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
input_to_field, // std::array<int, s>
|
||||
output_fop, // class derived from FieldOperator
|
||||
qfunc, // qfunc_t
|
||||
thread_blocks, // ThreadBlocks
|
||||
shmem_cache, // Vector (local)
|
||||
shmem_info, // SharedMemoryInfo
|
||||
@@ -1092,11 +872,9 @@ void DifferentiableOperator::AddIntegrator(
|
||||
direction_e, // Vector
|
||||
derivative_action_e, // Vector
|
||||
element_dof_ordering, // ElementDofOrdering
|
||||
inputs_trial_op_dim,
|
||||
total_trial_op_dim,
|
||||
trial_vdim,
|
||||
da_size_on_qp, // int
|
||||
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref,
|
||||
&or_transpose
|
||||
](
|
||||
std::vector<Vector> &f_e, const Vector &dir_l,
|
||||
@@ -1112,11 +890,6 @@ void DifferentiableOperator::AddIntegrator(
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
|
||||
const bool has_attr = attributes.Size() > 0;
|
||||
const auto d_attr = attributes.Read();
|
||||
const auto d_elem_attr = elem_attributes->Read();
|
||||
@@ -1134,20 +907,25 @@ void DifferentiableOperator::AddIntegrator(
|
||||
wrapped_fields_e, wrapped_direction_e, num_qp, e);
|
||||
auto &shadow_shmem = shadow_shmem_;
|
||||
|
||||
map_fields_to_quadrature_data(
|
||||
input_shmem, fields_shmem, input_dtq_shmem, input_to_field,
|
||||
inputs, ir_weights, scratch_shmem, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
// TODO: Probably redundant
|
||||
set_zero(shadow_shmem);
|
||||
|
||||
map_direction_to_quadrature_data_conditional(
|
||||
shadow_shmem, direction_shmem, input_dtq_shmem, inputs,
|
||||
ir_weights, scratch_shmem, input_is_dependent, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
call_qfunction_derivative_action<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem,
|
||||
da_size_on_qp, num_qp, q1d, dimension, use_sum_factorization);
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim,
|
||||
test_op_dim, num_qp);
|
||||
|
||||
auto qpdce = Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp);
|
||||
|
||||
apply_qpdc(fhat, shadow_shmem, qpdce, itod, q1d, dimension,
|
||||
use_sum_factorization);
|
||||
|
||||
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
|
||||
map_quadrature_data_to_fields(
|
||||
y, fhat, output_fop, output_dtq_shmem[0],
|
||||
@@ -1156,246 +934,6 @@ void DifferentiableOperator::AddIntegrator(
|
||||
shmem_cache.ReadWrite());
|
||||
or_transpose(derivative_action_e, der_action_l);
|
||||
});
|
||||
|
||||
assemble_derivative_sparsematrix_callbacks[derivative_id].push_back(
|
||||
[
|
||||
// capture by copy:
|
||||
dimension, // int
|
||||
num_entities, // int
|
||||
num_test_dof, // int
|
||||
num_qp, // int
|
||||
q1d, // int
|
||||
test_vdim, // int (= output_fop.vdim)
|
||||
test_op_dim, // int (derived from output_fop)
|
||||
inputs, // mfem::future::tuple
|
||||
attributes, // Array<int>
|
||||
use_sum_factorization, // bool
|
||||
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
|
||||
input_to_field, // std::array<int, s>
|
||||
output_fop, // class derived from FieldOperator
|
||||
thread_blocks, // ThreadBlocks
|
||||
shmem_cache, // Vector (local)
|
||||
shmem_info, // SharedMemoryInfo
|
||||
// TODO: make this Array<int> a member of the DifferentiableOperator
|
||||
// and capture it by ref.
|
||||
elem_attributes, // Array<int>
|
||||
|
||||
input_is_dependent, // std::array<bool, num_inputs>
|
||||
direction_e, // Vector
|
||||
total_trial_op_dim,
|
||||
trial_vdim,
|
||||
num_trial_dof,
|
||||
num_trial_dof_1d,
|
||||
inputs_trial_op_dim,
|
||||
Ae_mem,
|
||||
output_to_field,
|
||||
|
||||
// capture by ref:
|
||||
&qpdc_mem = derivative_qp_caches_ref,
|
||||
&fields = fields_ref
|
||||
](std::vector<Vector> &f_e, SparseMatrix *&A) mutable
|
||||
{
|
||||
auto wrapped_fields_e = wrap_fields(f_e, shmem_info.field_sizes,
|
||||
num_entities);
|
||||
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(),
|
||||
shmem_info.direction_size,
|
||||
num_entities);
|
||||
|
||||
auto qpdc = Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp, num_entities);
|
||||
|
||||
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
|
||||
|
||||
auto Ae = Reshape(Ae_mem.ReadWrite(), num_test_dof, test_vdim, num_trial_dof,
|
||||
trial_vdim, num_entities);
|
||||
|
||||
const auto d_elem_attr = elem_attributes->Read();
|
||||
const bool has_attr = attributes.Size() > 0;
|
||||
const auto d_domain_attr = attributes.Read();
|
||||
|
||||
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
|
||||
{
|
||||
if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) { return; }
|
||||
|
||||
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem,
|
||||
direction_shmem, input_shmem,
|
||||
shadow_shmem_, residual_shmem,
|
||||
scratch_shmem] =
|
||||
unpack_shmem(shmem, shmem_info, input_dtq_maps, output_dtq_maps,
|
||||
wrapped_fields_e, wrapped_direction_e, num_qp, e);
|
||||
|
||||
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
|
||||
auto Aee = Reshape(&Ae(0, 0, 0, 0, e), num_test_dof, test_vdim, num_trial_dof,
|
||||
trial_vdim);
|
||||
auto qpdce = Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
|
||||
trial_vdim, total_trial_op_dim, num_qp);
|
||||
assemble_element_mat_naive(Aee, fhat, qpdce, itod, inputs, output_fop,
|
||||
input_dtq_shmem, output_dtq_shmem[0], scratch_shmem, dimension, q1d,
|
||||
num_trial_dof_1d, use_sum_factorization);
|
||||
}, num_entities, thread_blocks, shmem_info.total_size,
|
||||
shmem_cache.ReadWrite());
|
||||
|
||||
FieldDescriptor *trial_field = nullptr;
|
||||
for (size_t s = 0; s < num_inputs; s++)
|
||||
{
|
||||
if (input_is_dependent[s])
|
||||
{
|
||||
trial_field = &fields[input_to_field[s]];
|
||||
}
|
||||
}
|
||||
|
||||
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&trial_field->data);
|
||||
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&fields[output_to_field[0]].data);
|
||||
|
||||
A = new SparseMatrix(test_fes->GetVSize(), trial_fes->GetVSize());
|
||||
|
||||
auto tmp = Reshape(Ae_mem.HostReadWrite(), num_test_dof * test_vdim,
|
||||
num_trial_dof * trial_vdim, num_entities);
|
||||
for (int e = 0; e < num_entities; e++)
|
||||
{
|
||||
DenseMatrix Aee(&tmp(0, 0, e), num_test_dof * test_vdim,
|
||||
num_trial_dof * trial_vdim);
|
||||
|
||||
Array<int> test_vdofs, trial_vdofs;
|
||||
test_fes->GetElementVDofs(e, test_vdofs);
|
||||
trial_fes->GetElementVDofs(e, trial_vdofs);
|
||||
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
Array<int> test_vdofs_mapped(test_vdofs.Size());
|
||||
|
||||
const Array<int> &test_dofmap =
|
||||
dynamic_cast<const TensorBasisElement&>(*test_fes->GetFE(0)).GetDofMap();
|
||||
|
||||
if (test_dofmap.Size() == 0)
|
||||
{
|
||||
test_vdofs_mapped = test_vdofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ASSERT(test_dofmap.Size() == num_test_dof,
|
||||
"internal error: dof map of the test space does not "
|
||||
"match previously determined number of test space dofs");
|
||||
|
||||
for (int vd = 0; vd < test_vdim; vd++)
|
||||
{
|
||||
for (int i = 0; i < num_test_dof; i++)
|
||||
{
|
||||
test_vdofs_mapped[i + vd * num_test_dof] =
|
||||
test_vdofs[test_dofmap[i] + vd * num_test_dof];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Array<int> trial_vdofs_mapped(trial_vdofs.Size());
|
||||
const Array<int> &trial_dofmap =
|
||||
dynamic_cast<const TensorBasisElement&>(*trial_fes->GetFE(0)).GetDofMap();
|
||||
|
||||
if (trial_dofmap.Size() == 0)
|
||||
{
|
||||
trial_vdofs_mapped = trial_vdofs;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ASSERT(trial_dofmap.Size() == num_trial_dof,
|
||||
"internal error: dof map of the test space does not "
|
||||
"match previously determined number of test space dofs");
|
||||
|
||||
for (int vd = 0; vd < trial_vdim; vd++)
|
||||
{
|
||||
for (int i = 0; i < num_trial_dof; i++)
|
||||
{
|
||||
trial_vdofs_mapped[i + vd * num_trial_dof] =
|
||||
trial_vdofs[trial_dofmap[i] + vd * num_trial_dof];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
A->AddSubMatrix(test_vdofs_mapped, trial_vdofs_mapped, Aee, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
A->AddSubMatrix(test_vdofs, trial_vdofs, Aee, 1);
|
||||
}
|
||||
}
|
||||
A->Finalize();
|
||||
});
|
||||
|
||||
// Create local references for MSVC lambda capture compatibility
|
||||
auto& assemble_derivative_sparsematrix_callbacks_ref =
|
||||
this->assemble_derivative_sparsematrix_callbacks[derivative_id];
|
||||
|
||||
assemble_derivative_hypreparmatrix_callbacks[derivative_id].push_back(
|
||||
[
|
||||
input_is_dependent,
|
||||
input_to_field,
|
||||
output_to_field,
|
||||
&spmatcb = assemble_derivative_sparsematrix_callbacks_ref,
|
||||
&fields = fields_ref
|
||||
](std::vector<Vector> &f_e, HypreParMatrix *&A) mutable
|
||||
{
|
||||
SparseMatrix *spmat = nullptr;
|
||||
for (const auto &f : spmatcb)
|
||||
{
|
||||
f(f_e, spmat);
|
||||
}
|
||||
|
||||
if (spmat == nullptr)
|
||||
{
|
||||
MFEM_ABORT("internal error");
|
||||
}
|
||||
|
||||
bool same_test_and_trial = false;
|
||||
for (size_t s = 0; s < num_inputs; s++)
|
||||
{
|
||||
if (input_is_dependent[s])
|
||||
{
|
||||
if (output_to_field[0] == input_to_field[s])
|
||||
{
|
||||
same_test_and_trial = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FieldDescriptor *trial_field = nullptr;
|
||||
for (size_t s = 0; s < num_inputs; s++)
|
||||
{
|
||||
if (input_is_dependent[s])
|
||||
{
|
||||
trial_field = &fields[input_to_field[s]];
|
||||
}
|
||||
}
|
||||
|
||||
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&trial_field->data);
|
||||
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
|
||||
(&fields[output_to_field[0]].data);
|
||||
|
||||
if (same_test_and_trial)
|
||||
{
|
||||
HypreParMatrix tmp(test_fes->GetComm(),
|
||||
test_fes->GlobalVSize(),
|
||||
test_fes->GetDofOffsets(),
|
||||
spmat);
|
||||
A = RAP(&tmp, test_fes->Dof_TrueDof_Matrix());
|
||||
}
|
||||
else
|
||||
{
|
||||
HypreParMatrix tmp(test_fes->GetComm(),
|
||||
test_fes->GlobalVSize(),
|
||||
trial_fes->GlobalVSize(),
|
||||
test_fes->GetDofOffsets(),
|
||||
trial_fes->GetDofOffsets(),
|
||||
spmat);
|
||||
A = RAP(test_fes->Dof_TrueDof_Matrix(), &tmp,
|
||||
trial_fes->Dof_TrueDof_Matrix());
|
||||
}
|
||||
delete spmat;
|
||||
});
|
||||
}, derivative_ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,7 +511,7 @@ void map_fields_to_quadrature_data(
|
||||
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
|
||||
const std::array<DeviceTensor<1>, num_fields> &fields_e,
|
||||
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
|
||||
const std::array<size_t, num_inputs> &input_to_field,
|
||||
const std::array<int, num_inputs> &input_to_field,
|
||||
const field_operator_ts &fops,
|
||||
const DeviceTensor<1, const real_t> &integration_weights,
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
@@ -526,8 +526,7 @@ void map_fields_to_quadrature_data(
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
const DeviceTensor<1> &field_e =
|
||||
(input_to_field[i] == SIZE_MAX) ? dummy_field_weight :
|
||||
fields_e[input_to_field[i]];
|
||||
(input_to_field[i] == -1) ? dummy_field_weight : fields_e[input_to_field[i]];
|
||||
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
@@ -638,7 +637,7 @@ void map_direction_to_quadrature_data_conditional(
|
||||
const std::array<DeviceTensor<1>, 6> &scratch_mem,
|
||||
const std::array<bool, num_inputs> &conditions,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
const bool &use_sum_factorization = false)
|
||||
{
|
||||
for_constexpr<num_inputs>([&](auto i)
|
||||
{
|
||||
|
||||
+14
-308
@@ -46,7 +46,7 @@ void call_qfunction(
|
||||
{
|
||||
if (dimension == 1)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
|
||||
MFEM_FOREACH_THREAD(q, x, q1d)
|
||||
{
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), rs_qp);
|
||||
@@ -55,9 +55,9 @@ void call_qfunction(
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
@@ -68,11 +68,11 @@ void call_qfunction(
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
@@ -92,7 +92,7 @@ void call_qfunction(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
|
||||
MFEM_FOREACH_THREAD(q, x, num_qp)
|
||||
{
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
auto r = Reshape(&residual_shmem(0, q), rs_qp);
|
||||
@@ -134,7 +134,7 @@ void call_qfunction_derivative_action(
|
||||
{
|
||||
if (dimension == 1)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
|
||||
MFEM_FOREACH_THREAD(q, x, q1d)
|
||||
{
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
@@ -149,9 +149,9 @@ void call_qfunction_derivative_action(
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
@@ -168,11 +168,11 @@ void call_qfunction_derivative_action(
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
MFEM_FOREACH_THREAD(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
MFEM_FOREACH_THREAD(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
|
||||
MFEM_FOREACH_THREAD(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
@@ -195,7 +195,7 @@ void call_qfunction_derivative_action(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
|
||||
MFEM_FOREACH_THREAD(q, x, num_qp)
|
||||
{
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
@@ -211,300 +211,6 @@ void call_qfunction_derivative_action(
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <
|
||||
typename qf_param_ts,
|
||||
typename qfunc_t,
|
||||
std::size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void call_qfunction_derivative(
|
||||
qfunc_t &qfunc,
|
||||
const std::array<DeviceTensor<2>, num_fields> &input_shmem,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
DeviceTensor<2> &residual_shmem,
|
||||
DeviceTensor<5> &qpdc,
|
||||
const DeviceTensor<1, const real_t> &itod,
|
||||
const int &das_qp,
|
||||
const int &q)
|
||||
{
|
||||
const int test_vdim = qpdc.GetShape()[0];
|
||||
const int test_op_dim = qpdc.GetShape()[1];
|
||||
const int trial_vdim = qpdc.GetShape()[2];
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
const size_t num_inputs = itod.GetShape()[0];
|
||||
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
int m_offset = 0;
|
||||
for (size_t s = 0; s < num_inputs; s++)
|
||||
{
|
||||
const int trial_op_dim = static_cast<int>(itod(s));
|
||||
if (trial_op_dim == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto d_qp = Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
d_qp(j, m, q) = 1.0;
|
||||
|
||||
auto r = Reshape(&residual_shmem(0, q), das_qp);
|
||||
auto qf_args = decay_tuple<qf_param_ts> {};
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
|
||||
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
|
||||
shadow_shmem, q);
|
||||
#else
|
||||
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
|
||||
#endif
|
||||
d_qp(j, m, q) = 0.0;
|
||||
|
||||
auto f = Reshape(&r(0), test_vdim, test_op_dim);
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
qpdc(i, k, j, m + m_offset, q) = f(i, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Call a qfunction with the given parameters and
|
||||
/// compute it's derivative represented by the Jacobian on
|
||||
/// each quadrature point.
|
||||
///
|
||||
/// @param qfunc the qfunction to call.
|
||||
/// @param input_shmem the input shared memory.
|
||||
/// @param shadow_shmem the shadow shared memory.
|
||||
/// @param residual_shmem the residual shared memory.
|
||||
/// @param qpdc the quadrature point data cache holding the resulting
|
||||
/// Jacobians on each quadrature point.
|
||||
/// @param itod inputs trial operator dimension.
|
||||
/// If input is dependent the value corresponds to the spatial dimension, otherwise
|
||||
/// a zero indicates non-dependence on the variable.
|
||||
/// @param das_qp the size of the derivative action.
|
||||
/// @param q1d the number of quadrature points in 1D.
|
||||
/// @param dimension the spatial dimension.
|
||||
/// @param use_sum_factorization whether to use sum factorization.
|
||||
/// @tparam qf_param_ts the tuple type of the qfunction parameters.
|
||||
template <
|
||||
typename qf_param_ts,
|
||||
typename qfunc_t,
|
||||
std::size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void call_qfunction_derivative(
|
||||
qfunc_t &qfunc,
|
||||
const std::array<DeviceTensor<2>, num_fields> &input_shmem,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
DeviceTensor<2> &residual_shmem,
|
||||
DeviceTensor<5> &qpdc,
|
||||
const DeviceTensor<1, const real_t> &itod,
|
||||
const int &das_qp,
|
||||
const int &q1d,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 1)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
|
||||
{
|
||||
detail::call_qfunction_derivative<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc, itod, das_qp, q);
|
||||
}
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
detail::call_qfunction_derivative<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc, itod, das_qp, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
detail::call_qfunction_derivative<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc, itod, das_qp, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("unsupported dimension");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
|
||||
{
|
||||
detail::call_qfunction_derivative<qf_param_ts>(
|
||||
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc, itod, das_qp, q);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// @brief Apply the quadrature point data cache (qpdc) to a vector
|
||||
/// (usually a direction) on quadrature point q.
|
||||
///
|
||||
/// The qpdc consists of compatible data to be used for integration with a test
|
||||
/// operator, e.g. Jacobians of a linearization from a FE operation with a trial
|
||||
/// function including integration weights and necessesary transformations.
|
||||
///
|
||||
/// @param fhat the qpdc applied to a vector in shadow_memory.
|
||||
/// @param shadow_shmem the shadow shared memory.
|
||||
/// @param qpdc the quadrature point data cache holding the resulting
|
||||
/// Jacobians on each quadrature point.
|
||||
/// @param itod inputs trial operator dimension.
|
||||
/// If input is dependent the value corresponds to the spatial dimension, otherwise
|
||||
/// a zero indicates non-dependence on the variable.
|
||||
/// @param q the current quadrature point index.
|
||||
template <size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_qpdc(
|
||||
DeviceTensor<3> &fhat,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
const DeviceTensor<5, const real_t> &qpdc,
|
||||
const DeviceTensor<1, const real_t> &itod,
|
||||
const int &q)
|
||||
{
|
||||
const int test_vdim = qpdc.GetShape()[0];
|
||||
const int test_op_dim = qpdc.GetShape()[1];
|
||||
const int trial_vdim = qpdc.GetShape()[2];
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
const size_t num_inputs = itod.GetShape()[0];
|
||||
|
||||
for (int i = 0; i < test_vdim; i++)
|
||||
{
|
||||
for (int k = 0; k < test_op_dim; k++)
|
||||
{
|
||||
real_t sum = 0.0;
|
||||
int m_offset = 0;
|
||||
for (size_t s = 0; s < num_inputs; s++)
|
||||
{
|
||||
const int trial_op_dim = static_cast<int>(itod(s));
|
||||
if (trial_op_dim == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto d_qp =
|
||||
Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
|
||||
for (int j = 0; j < trial_vdim; j++)
|
||||
{
|
||||
for (int m = 0; m < trial_op_dim; m++)
|
||||
{
|
||||
sum += qpdc(i, k, j, m + m_offset, q) * d_qp(j, m, q);
|
||||
}
|
||||
}
|
||||
m_offset += trial_op_dim;
|
||||
}
|
||||
fhat(i, k, q) = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Apply the quadrature point data cache (qpdc) to a vector
|
||||
/// (usually a direction).
|
||||
///
|
||||
/// The qpdc consists of compatible data to be used for integration with a test
|
||||
/// operator, e.g. Jacobians of a linearization from a FE operation with a trial
|
||||
/// function including integration weights and necessesary transformations.
|
||||
///
|
||||
/// @param fhat the qpdc applied to a vector in shadow_memory.
|
||||
/// @param shadow_shmem the shadow shared memory.
|
||||
/// @param qpdc the quadrature point data cache holding the resulting
|
||||
/// Jacobians on each quadrature point.
|
||||
/// @param itod inputs trial operator dimension.
|
||||
/// If input is dependent the value corresponds to the spatial dimension, otherwise
|
||||
/// a zero indicates non-dependence on the variable.
|
||||
/// @param q1d number of quadrature points in 1D.
|
||||
/// @param dimension spatial dimension.
|
||||
/// @param use_sum_factorization whether to use sum factorization.
|
||||
template <size_t num_fields>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_qpdc(
|
||||
DeviceTensor<3> &fhat,
|
||||
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
|
||||
const DeviceTensor<5, const real_t> &qpdc,
|
||||
const DeviceTensor<1, const real_t> &itod,
|
||||
const int &q1d,
|
||||
const int &dimension,
|
||||
const bool &use_sum_factorization)
|
||||
{
|
||||
if (use_sum_factorization)
|
||||
{
|
||||
if (dimension == 1)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
|
||||
{
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
}
|
||||
}
|
||||
else if (dimension == 2)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
{
|
||||
const int q = qx + q1d * qy;
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dimension == 3)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
|
||||
{
|
||||
const int q = qx + q1d * (qy + q1d * qz);
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT_KERNEL("unsupported dimension");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int num_qp = qpdc.GetShape()[4];
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
|
||||
{
|
||||
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename qfunc_t, typename args_ts, size_t num_args>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void apply_kernel(
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
#pragma once
|
||||
|
||||
// This is smith's tuple implementation
|
||||
// This is serac's tuple implementation
|
||||
|
||||
#include <ostream>
|
||||
#include "../../config/config.hpp"
|
||||
|
||||
+32
-61
@@ -20,7 +20,6 @@
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <numeric>
|
||||
#include <iomanip>
|
||||
|
||||
#include "../../general/communication.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
@@ -108,33 +107,26 @@ constexpr void for_constexpr_with_arg(lambda&& f, arg_t&& arg)
|
||||
indices{});
|
||||
}
|
||||
|
||||
template <std::size_t I, typename Tuple, std::size_t... Is>
|
||||
std::array<bool, sizeof...(Is)>
|
||||
make_dependency_array(const Tuple& inputs, std::index_sequence<Is...>)
|
||||
{
|
||||
return { (get<I>(inputs).GetFieldId() == get<Is>(inputs).GetFieldId())... };
|
||||
}
|
||||
|
||||
template <typename... input_ts, std::size_t... Is>
|
||||
auto make_dependency_map_impl(tuple<input_ts...> inputs,
|
||||
std::index_sequence<Is...>)
|
||||
auto make_dependency_map_impl(
|
||||
tuple<input_ts...> inputs,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
constexpr std::size_t N = sizeof...(input_ts);
|
||||
|
||||
if constexpr (N == 0)
|
||||
return std::unordered_map<int, std::array<bool, 0>> {};
|
||||
|
||||
std::unordered_map<int, std::array<bool, N>> map;
|
||||
|
||||
(void)std::initializer_list<int>
|
||||
auto make_dependency_array = [&](auto i)
|
||||
{
|
||||
(
|
||||
map[get<Is>(inputs).GetFieldId()] =
|
||||
make_dependency_array<Is>(inputs, std::make_index_sequence<N>{}),
|
||||
0
|
||||
)...
|
||||
return std::array<bool, sizeof...(input_ts)>
|
||||
{
|
||||
(get<i>(inputs).GetFieldId() == get<Is>(inputs).GetFieldId())...
|
||||
};
|
||||
};
|
||||
|
||||
std::unordered_map<int, std::array<bool, sizeof...(input_ts)>> map;
|
||||
for_constexpr<sizeof...(input_ts)>([&](auto i)
|
||||
{
|
||||
map[get<i>(inputs).GetFieldId()] =
|
||||
make_dependency_array(std::integral_constant<std::size_t, i> {});
|
||||
});
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -208,45 +200,24 @@ void print_tuple(const std::tuple<Args...>& t)
|
||||
/// ..., vmn]]
|
||||
/// which is compatible with numpy syntax.
|
||||
///
|
||||
/// @param out ostream to print to
|
||||
/// @param A mfem::DenseMatrix to print
|
||||
/// @param m mfem::DenseMatrix to print
|
||||
inline
|
||||
void pretty_print(std::ostream &out, const mfem::DenseMatrix &A)
|
||||
void pretty_print(const mfem::DenseMatrix& m)
|
||||
{
|
||||
// Determine the max width of any entry in scientific notation
|
||||
int max_width = 0;
|
||||
for (int i = 0; i < A.NumRows(); ++i)
|
||||
out << "[";
|
||||
for (int i = 0; i < m.NumRows(); i++)
|
||||
{
|
||||
for (int j = 0; j < A.NumCols(); ++j)
|
||||
for (int j = 0; j < m.NumCols(); j++)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << std::scientific << std::setprecision(2) << A(i, j);
|
||||
max_width = std::max(max_width, static_cast<int>(oss.str().length()));
|
||||
}
|
||||
}
|
||||
|
||||
out << "[\n";
|
||||
for (int i = 0; i < A.NumRows(); ++i)
|
||||
{
|
||||
out << " [";
|
||||
for (int j = 0; j < A.NumCols(); ++j)
|
||||
{
|
||||
out << std::setw(max_width) << std::scientific << std::setprecision(2) <<
|
||||
A(i, j);
|
||||
|
||||
if (j < A.NumCols() - 1)
|
||||
out << m(i, j);
|
||||
if (j < m.NumCols() - 1)
|
||||
{
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << "]";
|
||||
if (i < A.NumRows() - 1)
|
||||
if (i < m.NumRows() - 1)
|
||||
{
|
||||
out << ",\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
out << "\n";
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << "]\n";
|
||||
@@ -385,7 +356,7 @@ void print_mpi_sync(const std::string& msg)
|
||||
else
|
||||
{
|
||||
// Other ranks: Send message to rank 0
|
||||
MPI_Send(const_cast<char*>(msg.c_str()), static_cast<int>(msg_len), MPI_CHAR,
|
||||
MPI_Send(msg.c_str(), static_cast<int>(msg_len), MPI_CHAR,
|
||||
0, 0, MPI_COMM_WORLD);
|
||||
}
|
||||
|
||||
@@ -1433,12 +1404,12 @@ int GetSizeOnQP(const field_operator_t &, const FieldDescriptor &f)
|
||||
/// @tparam entity_t the entity type (see Entity).
|
||||
/// @returns an array mapping field operator types to field descriptor indices.
|
||||
template <typename entity_t, typename field_operator_ts>
|
||||
std::array<size_t, tuple_size<field_operator_ts>::value>
|
||||
std::array<int, tuple_size<field_operator_ts>::value>
|
||||
create_descriptors_to_fields_map(
|
||||
const std::vector<FieldDescriptor> &fields,
|
||||
field_operator_ts &fops)
|
||||
{
|
||||
std::array<size_t, tuple_size<field_operator_ts>::value> map;
|
||||
std::array<int, tuple_size<field_operator_ts>::value> map;
|
||||
|
||||
auto find_id = [](const std::vector<FieldDescriptor> &fields, std::size_t i)
|
||||
{
|
||||
@@ -1450,9 +1421,9 @@ create_descriptors_to_fields_map(
|
||||
|
||||
if (it == fields.end())
|
||||
{
|
||||
return SIZE_MAX;
|
||||
return -1;
|
||||
}
|
||||
return static_cast<size_t>(it - fields.begin());
|
||||
return static_cast<int>(it - fields.begin());
|
||||
};
|
||||
|
||||
auto f = [&](auto &fop, auto &map)
|
||||
@@ -1463,7 +1434,7 @@ create_descriptors_to_fields_map(
|
||||
fop.dim = GetDimension<entity_t>(fields[0]);
|
||||
fop.vdim = 1;
|
||||
fop.size_on_qp = 1;
|
||||
map = SIZE_MAX;
|
||||
map = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2249,7 +2220,7 @@ template <
|
||||
std::array<DofToQuadMap, N> create_dtq_maps_impl(
|
||||
field_operator_ts &fops,
|
||||
std::vector<const DofToQuad*> &dtqs,
|
||||
const std::array<size_t, N> &field_map,
|
||||
const std::array<int, N> &field_map,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
auto f = [&](auto fop, std::size_t idx)
|
||||
@@ -2334,7 +2305,7 @@ template <
|
||||
std::array<DofToQuadMap, num_fields> create_dtq_maps(
|
||||
field_operator_ts &fops,
|
||||
std::vector<const DofToQuad*> &dtqmaps,
|
||||
const std::array<size_t, num_fields> &to_field_map)
|
||||
const std::array<int, num_fields> &to_field_map)
|
||||
{
|
||||
return create_dtq_maps_impl<entity_t>(
|
||||
fops, dtqmaps,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "dgmassinv.hpp"
|
||||
#include "bilinearform.hpp"
|
||||
#include "dgmassinv_kernels.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -118,6 +119,151 @@ void DGMassInverse::Update()
|
||||
|
||||
DGMassInverse::~DGMassInverse() = default;
|
||||
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
void DGMassInverse::DGMassCGIteration(const Vector &b_, Vector &u_) const
|
||||
{
|
||||
using namespace internal; // host/device kernel functions
|
||||
|
||||
const int NE = fes.GetNE();
|
||||
const int d1d = m->dofs1D;
|
||||
const int q1d = m->quad1D;
|
||||
|
||||
const int ND = static_cast<int>(pow(d1d, DIM));
|
||||
|
||||
const auto B = m->maps->B.Read();
|
||||
const auto Bt = m->maps->Bt.Read();
|
||||
const auto pa_data = m->pa_data.Read();
|
||||
const auto dinv = diag_inv.Read();
|
||||
auto r = r_.Write();
|
||||
auto d = d_.Write();
|
||||
auto z = z_.Write();
|
||||
auto u = u_.ReadWrite();
|
||||
|
||||
const real_t RELTOL = rel_tol;
|
||||
const real_t ABSTOL = abs_tol;
|
||||
const int MAXIT = max_iter;
|
||||
const bool IT_MODE = iterative_mode;
|
||||
const bool CHANGE_BASIS = (d2q != nullptr);
|
||||
|
||||
// b is the right-hand side (if no change of basis, this just points to the
|
||||
// incoming RHS vector, if we have to change basis, this points to the
|
||||
// internal b2 vector where we put the transformed RHS)
|
||||
const real_t *b;
|
||||
// the following are non-null if we have to change basis
|
||||
real_t *b2 = nullptr; // non-const access to b2
|
||||
const real_t *b_orig = nullptr; // RHS vector in "original" basis
|
||||
const real_t *d2q_B = nullptr; // matrix to transform initial guess
|
||||
const real_t *q2d_B = nullptr; // matrix to transform solution
|
||||
const real_t *q2d_Bt = nullptr; // matrix to transform RHS
|
||||
if (CHANGE_BASIS)
|
||||
{
|
||||
d2q_B = d2q->B.Read();
|
||||
q2d_B = B_.Read();
|
||||
q2d_Bt = Bt_.Read();
|
||||
|
||||
b2 = b2_.Write();
|
||||
b_orig = b_.Read();
|
||||
b = b2;
|
||||
}
|
||||
else
|
||||
{
|
||||
b = b_.Read();
|
||||
}
|
||||
|
||||
static constexpr int NB = Q1D ? Q1D : 1; // block size
|
||||
|
||||
mfem::forall_2D(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
// Perform change of basis if needed
|
||||
if (CHANGE_BASIS)
|
||||
{
|
||||
// Transform RHS
|
||||
DGMassBasis<DIM,D1D>(e, NE, q2d_Bt, b_orig, b2, d1d);
|
||||
if (IT_MODE)
|
||||
{
|
||||
// Transform initial guess
|
||||
DGMassBasis<DIM,D1D>(e, NE, d2q_B, u, u, d1d);
|
||||
}
|
||||
}
|
||||
|
||||
const int tid = MFEM_THREAD_ID(x) + NB*MFEM_THREAD_ID(y);
|
||||
|
||||
// Compute first residual
|
||||
if (IT_MODE)
|
||||
{
|
||||
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, u, r, d1d, q1d);
|
||||
DGMassAxpy(e, NE, ND, 1.0, b, -1.0, r, r); // r = b - r
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not in iterative mode, use zero initial guess
|
||||
const int BX = MFEM_THREAD_SIZE(x);
|
||||
const int BY = MFEM_THREAD_SIZE(y);
|
||||
const int bxy = BX*BY;
|
||||
const auto B = ConstDeviceMatrix(b, ND, NE);
|
||||
auto U = DeviceMatrix(u, ND, NE);
|
||||
auto R = DeviceMatrix(r, ND, NE);
|
||||
for (int i = tid; i < ND; i += bxy)
|
||||
{
|
||||
U(i, e) = 0.0;
|
||||
R(i, e) = B(i, e);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
DGMassPreconditioner(e, NE, ND, dinv, r, z);
|
||||
DGMassAxpy(e, NE, ND, 1.0, z, 0.0, z, d); // d = z
|
||||
|
||||
real_t nom = DGMassDot<NB>(e, NE, ND, d, r);
|
||||
if (nom < 0.0) { return; /* Not positive definite */ }
|
||||
real_t r0 = fmax(nom*RELTOL*RELTOL, ABSTOL*ABSTOL);
|
||||
if (nom <= r0) { return; /* Converged */ }
|
||||
|
||||
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d);
|
||||
real_t den = DGMassDot<NB>(e, NE, ND, z, d);
|
||||
if (den <= 0.0)
|
||||
{
|
||||
DGMassDot<NB>(e, NE, ND, d, d);
|
||||
// d2 > 0 => not positive definite
|
||||
if (den == 0.0) { return; }
|
||||
}
|
||||
|
||||
// start iteration
|
||||
int i = 1;
|
||||
while (true)
|
||||
{
|
||||
const real_t alpha = nom/den;
|
||||
DGMassAxpy(e, NE, ND, 1.0, u, alpha, d, u); // u = u + alpha*d
|
||||
DGMassAxpy(e, NE, ND, 1.0, r, -alpha, z, r); // r = r - alpha*A*d
|
||||
|
||||
DGMassPreconditioner(e, NE, ND, dinv, r, z);
|
||||
|
||||
real_t betanom = DGMassDot<NB>(e, NE, ND, r, z);
|
||||
if (betanom < 0.0) { return; /* Not positive definite */ }
|
||||
if (betanom <= r0) { break; /* Converged */ }
|
||||
|
||||
if (++i > MAXIT) { break; }
|
||||
|
||||
const real_t beta = betanom/nom;
|
||||
DGMassAxpy(e, NE, ND, 1.0, z, beta, d, d); // d = z + beta*d
|
||||
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d); // z = A d
|
||||
den = DGMassDot<NB>(e, NE, ND, d, z);
|
||||
if (den <= 0.0)
|
||||
{
|
||||
DGMassDot<NB>(e, NE, ND, d, d);
|
||||
// d2 > 0 => not positive definite
|
||||
if (den == 0.0) { break; }
|
||||
}
|
||||
nom = betanom;
|
||||
}
|
||||
|
||||
if (CHANGE_BASIS)
|
||||
{
|
||||
DGMassBasis<DIM,D1D>(e, NE, q2d_B, u, u, d1d);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DGMassInverse::Mult(const Vector &Mu, Vector &u) const
|
||||
{
|
||||
// Dispatch to templated version based on dim, d1d, and q1d.
|
||||
@@ -160,4 +306,23 @@ DGMassInvKernels::DGMassInvKernels()
|
||||
k::Specialization<3,6,7>::Add();
|
||||
}
|
||||
|
||||
/// @cond Suppress_Doxygen_warnings
|
||||
|
||||
template <int DIM, int D1D, int Q1D>
|
||||
DGMassInverse::CGKernelType DGMassInverse::CGKernels::Kernel()
|
||||
{
|
||||
return &DGMassInverse::DGMassCGIteration<DIM,D1D,Q1D>;
|
||||
}
|
||||
|
||||
DGMassInverse::CGKernelType DGMassInverse::CGKernels::Fallback(
|
||||
int dim, int, int)
|
||||
{
|
||||
if (dim == 1) { return &DGMassInverse::DGMassCGIteration<1>; }
|
||||
else if (dim == 2) { return &DGMassInverse::DGMassCGIteration<2>; }
|
||||
else if (dim == 3) { return &DGMassInverse::DGMassCGIteration<3>; }
|
||||
else { MFEM_ABORT("Unsupported dimension."); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "../linalg/kernels.hpp"
|
||||
#include "kernels.hpp"
|
||||
#include "integ/bilininteg_mass_kernels.hpp"
|
||||
#include "dgmassinv.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -334,170 +333,6 @@ void DGMassBasis(const int e,
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
void DGMassInverse::DGMassCGIteration(const Vector &b_, Vector &u_) const
|
||||
{
|
||||
using namespace internal; // host/device kernel functions
|
||||
|
||||
const int NE = fes.GetNE();
|
||||
const int d1d = m->dofs1D;
|
||||
const int q1d = m->quad1D;
|
||||
|
||||
const int ND = static_cast<int>(pow(d1d, DIM));
|
||||
|
||||
const auto B = m->maps->B.Read();
|
||||
const auto Bt = m->maps->Bt.Read();
|
||||
const auto pa_data = m->pa_data.Read();
|
||||
const auto dinv = diag_inv.Read();
|
||||
auto r = r_.Write();
|
||||
auto d = d_.Write();
|
||||
auto z = z_.Write();
|
||||
auto u = u_.ReadWrite();
|
||||
|
||||
const real_t RELTOL = rel_tol;
|
||||
const real_t ABSTOL = abs_tol;
|
||||
const int MAXIT = max_iter;
|
||||
const bool IT_MODE = iterative_mode;
|
||||
const bool CHANGE_BASIS = (d2q != nullptr);
|
||||
|
||||
// b is the right-hand side (if no change of basis, this just points to the
|
||||
// incoming RHS vector, if we have to change basis, this points to the
|
||||
// internal b2 vector where we put the transformed RHS)
|
||||
const real_t *b;
|
||||
// the following are non-null if we have to change basis
|
||||
real_t *b2 = nullptr; // non-const access to b2
|
||||
const real_t *b_orig = nullptr; // RHS vector in "original" basis
|
||||
const real_t *d2q_B = nullptr; // matrix to transform initial guess
|
||||
const real_t *q2d_B = nullptr; // matrix to transform solution
|
||||
const real_t *q2d_Bt = nullptr; // matrix to transform RHS
|
||||
if (CHANGE_BASIS)
|
||||
{
|
||||
d2q_B = d2q->B.Read();
|
||||
q2d_B = B_.Read();
|
||||
q2d_Bt = Bt_.Read();
|
||||
|
||||
b2 = b2_.Write();
|
||||
b_orig = b_.Read();
|
||||
b = b2;
|
||||
}
|
||||
else
|
||||
{
|
||||
b = b_.Read();
|
||||
}
|
||||
|
||||
static constexpr int NB = Q1D ? Q1D : 1; // block size
|
||||
|
||||
mfem::forall_2D<NB*NB>(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
// Perform change of basis if needed
|
||||
if (CHANGE_BASIS)
|
||||
{
|
||||
// Transform RHS
|
||||
DGMassBasis<DIM,D1D>(e, NE, q2d_Bt, b_orig, b2, d1d);
|
||||
if (IT_MODE)
|
||||
{
|
||||
// Transform initial guess
|
||||
DGMassBasis<DIM,D1D>(e, NE, d2q_B, u, u, d1d);
|
||||
}
|
||||
}
|
||||
|
||||
const int tid = MFEM_THREAD_ID(x) + NB*MFEM_THREAD_ID(y);
|
||||
|
||||
// Compute first residual
|
||||
if (IT_MODE)
|
||||
{
|
||||
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, u, r, d1d, q1d);
|
||||
DGMassAxpy(e, NE, ND, 1.0, b, -1.0, r, r); // r = b - r
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not in iterative mode, use zero initial guess
|
||||
const int BX = MFEM_THREAD_SIZE(x);
|
||||
const int BY = MFEM_THREAD_SIZE(y);
|
||||
const int bxy = BX*BY;
|
||||
const auto B = ConstDeviceMatrix(b, ND, NE);
|
||||
auto U = DeviceMatrix(u, ND, NE);
|
||||
auto R = DeviceMatrix(r, ND, NE);
|
||||
for (int i = tid; i < ND; i += bxy)
|
||||
{
|
||||
U(i, e) = 0.0;
|
||||
R(i, e) = B(i, e);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
DGMassPreconditioner(e, NE, ND, dinv, r, z);
|
||||
DGMassAxpy(e, NE, ND, 1.0, z, 0.0, z, d); // d = z
|
||||
|
||||
real_t nom = DGMassDot<NB>(e, NE, ND, d, r);
|
||||
if (nom < 0.0) { return; /* Not positive definite */ }
|
||||
real_t r0 = fmax(nom*RELTOL*RELTOL, ABSTOL*ABSTOL);
|
||||
if (nom <= r0) { return; /* Converged */ }
|
||||
|
||||
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d);
|
||||
real_t den = DGMassDot<NB>(e, NE, ND, z, d);
|
||||
if (den <= 0.0)
|
||||
{
|
||||
DGMassDot<NB>(e, NE, ND, d, d);
|
||||
// d2 > 0 => not positive definite
|
||||
if (den == 0.0) { return; }
|
||||
}
|
||||
|
||||
// start iteration
|
||||
int i = 1;
|
||||
while (true)
|
||||
{
|
||||
const real_t alpha = nom/den;
|
||||
DGMassAxpy(e, NE, ND, 1.0, u, alpha, d, u); // u = u + alpha*d
|
||||
DGMassAxpy(e, NE, ND, 1.0, r, -alpha, z, r); // r = r - alpha*A*d
|
||||
|
||||
DGMassPreconditioner(e, NE, ND, dinv, r, z);
|
||||
|
||||
real_t betanom = DGMassDot<NB>(e, NE, ND, r, z);
|
||||
if (betanom < 0.0) { return; /* Not positive definite */ }
|
||||
if (betanom <= r0) { break; /* Converged */ }
|
||||
|
||||
if (++i > MAXIT) { break; }
|
||||
|
||||
const real_t beta = betanom/nom;
|
||||
DGMassAxpy(e, NE, ND, 1.0, z, beta, d, d); // d = z + beta*d
|
||||
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d); // z = A d
|
||||
den = DGMassDot<NB>(e, NE, ND, d, z);
|
||||
if (den <= 0.0)
|
||||
{
|
||||
DGMassDot<NB>(e, NE, ND, d, d);
|
||||
// d2 > 0 => not positive definite
|
||||
if (den == 0.0) { break; }
|
||||
}
|
||||
nom = betanom;
|
||||
}
|
||||
|
||||
if (CHANGE_BASIS)
|
||||
{
|
||||
DGMassBasis<DIM,D1D>(e, NE, q2d_B, u, u, d1d);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// @cond Suppress_Doxygen_warnings
|
||||
|
||||
template <int DIM, int D1D, int Q1D>
|
||||
inline DGMassInverse::CGKernelType DGMassInverse::CGKernels::Kernel()
|
||||
{
|
||||
return &DGMassInverse::DGMassCGIteration<DIM,D1D,Q1D>;
|
||||
}
|
||||
|
||||
inline DGMassInverse::CGKernelType DGMassInverse::CGKernels::Fallback(
|
||||
int dim, int, int)
|
||||
{
|
||||
if (dim == 1) { return &DGMassInverse::DGMassCGIteration<1>; }
|
||||
else if (dim == 2) { return &DGMassInverse::DGMassCGIteration<2>; }
|
||||
else if (dim == 3) { return &DGMassInverse::DGMassCGIteration<3>; }
|
||||
else { MFEM_ABORT("Unsupported dimension."); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -69,9 +69,9 @@ inline int ToLexOrdering2D(const int face_id, const int size1d, const int i)
|
||||
}
|
||||
|
||||
/// @brief Given a face DOF index on a shared face, ordered lexicographically
|
||||
/// relative to the element (where the local face is face_id), return the
|
||||
/// corresponding face DOF index ordered lexicographically relative to the face
|
||||
/// itself.
|
||||
/// relative to element the element (where the local face is face_id), and
|
||||
/// return the corresponding face DOF index ordered lexicographically relative
|
||||
/// to the face itself.
|
||||
MFEM_HOST_DEVICE
|
||||
inline int PermuteFace2D(const int face_id, const int orientation,
|
||||
const int size1d, const int index)
|
||||
|
||||
+34
-22
@@ -231,7 +231,7 @@ void FiniteElement::CalcPhysLaplacian(ElementTransformation &Trans,
|
||||
{
|
||||
for (int nd = 0; nd < dof; nd++)
|
||||
{
|
||||
Laplacian[nd] = hess(nd,0) + hess(nd,3) + hess(nd,5);
|
||||
Laplacian[nd] = hess(nd,0) + hess(nd,4) + hess(nd,5);
|
||||
}
|
||||
}
|
||||
else if (dim == 2)
|
||||
@@ -268,9 +268,11 @@ void FiniteElement::CalcPhysLinLaplacian(ElementTransformation &Trans,
|
||||
scale[0] = Gij(0,0);
|
||||
scale[1] = 2*Gij(0,1);
|
||||
scale[2] = 2*Gij(0,2);
|
||||
scale[3] = Gij(1,1);
|
||||
scale[4] = 2*Gij(1,2);
|
||||
scale[5] = Gij(2,2);
|
||||
|
||||
scale[3] = 2*Gij(1,2);
|
||||
scale[4] = Gij(2,2);
|
||||
|
||||
scale[5] = Gij(1,1);
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
@@ -307,12 +309,12 @@ void FiniteElement::CalcPhysHessian(ElementTransformation &Trans,
|
||||
map[2] = 2;
|
||||
|
||||
map[3] = 1;
|
||||
map[4] = 3;
|
||||
map[5] = 4;
|
||||
map[4] = 5;
|
||||
map[5] = 3;
|
||||
|
||||
map[6] = 2;
|
||||
map[7] = 4;
|
||||
map[8] = 5;
|
||||
map[7] = 3;
|
||||
map[8] = 4;
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
@@ -380,7 +382,11 @@ const DofToQuad &FiniteElement::GetDofToQuad(const IntegrationRule &ir,
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule != &ir || d2q->mode != mode) { d2q = nullptr; }
|
||||
}
|
||||
if (!d2q)
|
||||
{
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
@@ -655,22 +661,14 @@ void ScalarFiniteElement::ScalarLocalL2Restriction(
|
||||
void NodalFiniteElement::CreateLexicographicFullMap(const IntegrationRule &ir)
|
||||
const
|
||||
{
|
||||
// Get the FULL version of the map. This call contains omp critical region,
|
||||
// so it is done before the critical region below.
|
||||
auto &d2q = GetDofToQuad(ir, DofToQuad::FULL);
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
// If the new Dof2Quad is already present, e.g. added in a previous call
|
||||
// or added by another omp thread, return.
|
||||
if (DofToQuad::SearchArray(dof2quad_array, ir,
|
||||
DofToQuad::LEXICOGRAPHIC_FULL))
|
||||
{ return; }
|
||||
|
||||
// Undo the native ordering which is what FiniteElement::GetDofToQuad
|
||||
// returns.
|
||||
// Get the FULL version of the map.
|
||||
auto &d2q = GetDofToQuad(ir, DofToQuad::FULL);
|
||||
//Undo the native ordering which is what FiniteElement::GetDofToQuad returns.
|
||||
auto *d2q_new = new DofToQuad(d2q);
|
||||
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
|
||||
const int nqpt = ir.GetNPoints();
|
||||
@@ -726,7 +724,13 @@ const DofToQuad &NodalFiniteElement::GetDofToQuad(const IntegrationRule &ir,
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
|
||||
//Should make this loop a function of FiniteElement
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule == &ir && d2q->mode == mode) { break; }
|
||||
d2q = nullptr;
|
||||
}
|
||||
}
|
||||
if (d2q) { return *d2q; }
|
||||
if (mode != DofToQuad::LEXICOGRAPHIC_FULL)
|
||||
@@ -2627,7 +2631,15 @@ const DofToQuad &TensorBasisElement::GetTensorDofToQuad(
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
auto* d2q_ = dof2quad_array[i];
|
||||
if (d2q_->IntRule == &ir && d2q_->mode == mode)
|
||||
{
|
||||
d2q = d2q_;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!d2q)
|
||||
{
|
||||
d2q = new DofToQuad;
|
||||
|
||||
+3
-25
@@ -44,7 +44,7 @@ public:
|
||||
NumBasisTypes = 9 /**< Keep track of maximum types to prevent
|
||||
hard-coding */
|
||||
};
|
||||
/** @brief If the input does not represent a valid BasisType, abort with an
|
||||
/** @brief If the input does not represents a valid BasisType, abort with an
|
||||
error; otherwise return the input. */
|
||||
static int Check(int b_type)
|
||||
{
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
"unknown BasisType: " << b_type);
|
||||
return b_type;
|
||||
}
|
||||
/** @brief If the input does not represent a valid nodal BasisType, abort
|
||||
/** @brief If the input does not represents a valid nodal BasisType, abort
|
||||
with an error; otherwise return the input. */
|
||||
static int CheckNodal(int b_type)
|
||||
{
|
||||
@@ -222,12 +222,6 @@ public:
|
||||
|
||||
/// Returns absolute value of the maps
|
||||
DofToQuad Abs() const;
|
||||
|
||||
/// Auxiliary function for searching DofToQuad arrays.
|
||||
static inline DofToQuad *SearchArray(
|
||||
const Array<DofToQuad*> &dof2quad_array,
|
||||
const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode);
|
||||
};
|
||||
|
||||
/// Describes the function space on each element
|
||||
@@ -413,7 +407,6 @@ public:
|
||||
/** Each row of the result DenseMatrix @a Hessian contains upper triangular
|
||||
part of the Hessian of one shape function.
|
||||
The order in 2D is {u_xx, u_xy, u_yy}.
|
||||
The order in 3D is {u_xx, u_xy, u_xz, u_yy, u_yz, u_zz}.
|
||||
The size (#dof x (#dim (#dim+1)/2) of @a Hessian must be set in advance.*/
|
||||
virtual void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &Hessian) const;
|
||||
@@ -1127,7 +1120,7 @@ public:
|
||||
return GetPoints(p, btype, on_device);
|
||||
}
|
||||
|
||||
/// Get coordinates of a closed (GaussLobatto) set of points if degree @a p
|
||||
/// Get coordinates of a closed (GaussLegendre) set of points if degree @a p
|
||||
const real_t *ClosedPoints(const int p,
|
||||
const int btype = BasisType::GaussLobatto,
|
||||
bool on_device = false)
|
||||
@@ -1383,21 +1376,6 @@ public:
|
||||
void InvertLinearTrans(ElementTransformation &trans,
|
||||
const IntegrationPoint &pt, Vector &x);
|
||||
|
||||
|
||||
// static inline method
|
||||
inline DofToQuad *DofToQuad::SearchArray(
|
||||
const Array<DofToQuad*> &dof2quad_array,
|
||||
const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode)
|
||||
{
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
DofToQuad *d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule == &ir && d2q->mode == mode) { return d2q; }
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -60,12 +60,6 @@ void Linear1DFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
dshape(1,0) = 1.;
|
||||
}
|
||||
|
||||
void Linear1DFiniteElement::CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const
|
||||
{
|
||||
h = 0.0;
|
||||
}
|
||||
|
||||
Linear2DFiniteElement::Linear2DFiniteElement()
|
||||
: NodalFiniteElement(2, Geometry::TRIANGLE, 3, 1)
|
||||
{
|
||||
@@ -93,11 +87,6 @@ void Linear2DFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
dshape(2,0) = 0.; dshape(2,1) = 1.;
|
||||
}
|
||||
|
||||
void Linear2DFiniteElement::CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const
|
||||
{
|
||||
h = 0.0;
|
||||
}
|
||||
|
||||
BiLinear2DFiniteElement::BiLinear2DFiniteElement()
|
||||
: NodalFiniteElement(2, Geometry::SQUARE, 4, 1, FunctionSpace::Qk)
|
||||
@@ -1267,12 +1256,6 @@ void Linear3DFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
void Linear3DFiniteElement::CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const
|
||||
{
|
||||
h = 0.0;
|
||||
}
|
||||
|
||||
void Linear3DFiniteElement::GetFaceDofs (int face, int **dofs, int *ndofs)
|
||||
const
|
||||
{
|
||||
@@ -1649,37 +1632,6 @@ void TriLinear3DFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
dshape(7,2) = ox * y;
|
||||
}
|
||||
|
||||
void TriLinear3DFiniteElement::CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const
|
||||
{
|
||||
real_t x = ip.x, y = ip.y, z = ip.z;
|
||||
real_t ox = 1.-x, oy = 1.-y, oz = 1.-z;
|
||||
|
||||
h(0,0) = 0.; h(0,1) = oz; h(0,2) = oy;
|
||||
h(0,3) = 0.; h(0,4) = ox; h(0,5) = 0.;
|
||||
|
||||
h(1,0) = 0.; h(1,1) = -oz; h(1,2) = -oy;
|
||||
h(1,3) = 0.; h(1,4) = x; h(1,5) = 0.;
|
||||
|
||||
h(2,0) = 0.; h(2,1) = oz; h(2,2) = -y;
|
||||
h(2,3) = 0.; h(2,4) = -x; h(2,5) = 0.;
|
||||
|
||||
h(3,0) = 0.; h(3,1) = -oz; h(3,2) = y;
|
||||
h(3,3) = 0.; h(3,4) = -ox; h(3,5) = 0.;
|
||||
|
||||
h(4,0) = 0.; h(4,1) = z; h(4,2) = -oy;
|
||||
h(4,3) = 0.; h(4,4) = -ox; h(4,5) = 0.;
|
||||
|
||||
h(5,0) = 0.; h(5,1) = -z; h(5,2) = oy;
|
||||
h(5,3) = 0.; h(5,4) = -x; h(5,5) = 0.;
|
||||
|
||||
h(6,0) = 0.; h(6,1) = z; h(6,2) = y;
|
||||
h(6,3) = 0.; h(6,4) = x; h(6,5) = 0.;
|
||||
|
||||
h(7,0) = 0.; h(7,1) = -z; h(7,2) = -y;
|
||||
h(7,3) = 0.; h(7,4) = ox; h(7,5) = 0.;
|
||||
}
|
||||
|
||||
|
||||
P0SegmentFiniteElement::P0SegmentFiniteElement(int Ord)
|
||||
: NodalFiniteElement(1, Geometry::SEGMENT, 1, Ord) // default Ord = 0
|
||||
|
||||
@@ -50,8 +50,6 @@ public:
|
||||
contains the derivative of one shape function */
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const override;
|
||||
};
|
||||
|
||||
/// A 2D linear element on triangle with nodes at the vertices of the triangle
|
||||
@@ -72,8 +70,6 @@ public:
|
||||
so that each row contains the derivatives of one shape function */
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const override;
|
||||
void ProjectDelta(int vertex, Vector &dofs) const override
|
||||
{ dofs = 0.0; dofs(vertex) = 1.0; }
|
||||
};
|
||||
@@ -408,9 +404,6 @@ public:
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
|
||||
void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const override;
|
||||
|
||||
void ProjectDelta(int vertex, Vector &dofs) const override
|
||||
{ dofs = 0.0; dofs(vertex) = 1.0; }
|
||||
|
||||
@@ -452,8 +445,7 @@ public:
|
||||
so that each row contains the derivatives of one shape function */
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const override;
|
||||
|
||||
void ProjectDelta(int vertex, Vector &dofs) const override
|
||||
{ dofs = 0.0; dofs(vertex) = 1.0; }
|
||||
};
|
||||
|
||||
+5
-519
@@ -84,46 +84,6 @@ void NURBS1DFiniteElement::CalcHessian (const IntegrationPoint &ip,
|
||||
add(1.0, hess, (-d2sum + 2*dsum*dsum*sum)*sum*sum, shape_x, hess);
|
||||
}
|
||||
|
||||
void NURBS1DFiniteElement::Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
IntegrationPoint ip;
|
||||
|
||||
for (int i = 0; i <= order; i++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+order)) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+order);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
dofs(i) = coeff.Eval(Trans, ip);
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS1DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == vc.GetVDim()*dof, "");
|
||||
Vector x(vc.GetVDim());
|
||||
IntegrationPoint ip;
|
||||
|
||||
for (int i = 0; i <= order; i++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+order)) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+order);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
for (int j = 0; j < x.Size(); j++)
|
||||
{
|
||||
dofs(dof*j+i) = x(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NURBS2DFiniteElement::SetOrder() const
|
||||
{
|
||||
@@ -255,63 +215,6 @@ void NURBS2DFiniteElement::CalcHessian (const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS2DFiniteElement::Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
IntegrationPoint ip;
|
||||
for (int o = 0, j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
dofs(o) = coeff.Eval(Trans, ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS2DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == vc.GetVDim()*dof, "");
|
||||
Vector x(vc.GetVDim());
|
||||
IntegrationPoint ip;
|
||||
for (int o = 0, j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
for (int v = 0; v < x.Size(); v++)
|
||||
{
|
||||
dofs(dof*v+o) = x(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS3DFiniteElement::SetOrder() const
|
||||
{
|
||||
@@ -445,10 +348,11 @@ void NURBS3DFiniteElement::CalcHessian (const IntegrationPoint &ip,
|
||||
d2sum[0] += ( hessian(o,0) = d2sx*sy*sz*weights(o) );
|
||||
d2sum[1] += ( hessian(o,1) = dsx*dsy*sz*weights(o) );
|
||||
d2sum[2] += ( hessian(o,2) = dsx*sy*dsz*weights(o) );
|
||||
d2sum[3] += ( hessian(o,3) = sx*d2sy*sz*weights(o) );
|
||||
d2sum[4] += ( hessian(o,4) = sx*dsy*dsz*weights(o) );
|
||||
d2sum[5] += ( hessian(o,5) = sx*sy*d2sz*weights(o) );
|
||||
|
||||
d2sum[3] += ( hessian(o,3) = sx*dsy*dsz*weights(o) );
|
||||
|
||||
d2sum[4] += ( hessian(o,4) = sx*sy*d2sz*weights(o) );
|
||||
d2sum[5] += ( hessian(o,5) = sx*d2sy*sz*weights(o) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -497,85 +401,6 @@ void NURBS3DFiniteElement::CalcHessian (const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS3DFiniteElement::Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
IntegrationPoint ip;
|
||||
|
||||
for (int o = 0, k = 0; k <= orders[2]; k++)
|
||||
{
|
||||
real_t kz = kv[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
|
||||
{
|
||||
o += (orders[0] + 1)*(orders[1] + 1);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
|
||||
for (int j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
dofs(o) = coeff.Eval(Trans, ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS3DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == vc.GetVDim()*dof, "");
|
||||
Vector x(vc.GetVDim());
|
||||
IntegrationPoint ip;
|
||||
|
||||
for (int o = 0, k = 0; k <= orders[2]; k++)
|
||||
{
|
||||
real_t kz = kv[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
|
||||
{
|
||||
o += (orders[0] + 1)*(orders[1] + 1);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
|
||||
for (int j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
for (int v = 0; v < x.Size(); v++)
|
||||
{
|
||||
dofs(dof*v+o) = x(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS_HDiv2DFiniteElement::SetOrder() const
|
||||
{
|
||||
@@ -692,63 +517,6 @@ void NURBS_HDiv2DFiniteElement::CalcDivShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS_HDiv2DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == dof, "");
|
||||
MFEM_ASSERT(vc.GetVDim() == 2, "");
|
||||
Vector x(2), mx(2);
|
||||
IntegrationPoint ip;
|
||||
int o = 0;
|
||||
|
||||
for (int j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 2;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]+1; i++, o++)
|
||||
{
|
||||
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
|
||||
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.AdjugateJacobian().Mult(x,mx);
|
||||
dofs(o) = mx(0);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j <= orders[1]+1; j++)
|
||||
{
|
||||
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.AdjugateJacobian().Mult(x,mx);
|
||||
dofs(o) = mx(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NURBS_HDiv2DFiniteElement::~NURBS_HDiv2DFiniteElement()
|
||||
{
|
||||
if (kv1[0]) { delete kv1[0]; }
|
||||
@@ -928,120 +696,6 @@ void NURBS_HDiv3DFiniteElement::CalcDivShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NURBS_HDiv3DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == dof, "");
|
||||
MFEM_ASSERT(vc.GetVDim() == 3, "");
|
||||
Vector x(2), mx(3);
|
||||
IntegrationPoint ip;
|
||||
|
||||
int o = 0;
|
||||
|
||||
for (int k = 0; k <= orders[2]; k++)
|
||||
{
|
||||
real_t kz = kv[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
|
||||
{
|
||||
o += (orders[0] + 2)*(orders[1] + 1);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
|
||||
for (int j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 2;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]+1; i++, o++)
|
||||
{
|
||||
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
|
||||
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.AdjugateJacobian().Mult(x,mx);
|
||||
dofs(o) = mx(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k <= orders[2]; k++)
|
||||
{
|
||||
real_t kz = kv[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
|
||||
{
|
||||
o += (orders[0] + 1)*(orders[1] + 2);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
|
||||
for (int j = 0; j <= orders[1]+1; j++)
|
||||
{
|
||||
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.AdjugateJacobian().Mult(x,mx);
|
||||
dofs(o) = mx(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k <= orders[2]+1; k++)
|
||||
{
|
||||
real_t kz = kv1[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv1[2]->inSpan(kz, ijk[2]+orders[2]+1))
|
||||
{
|
||||
o += (orders[0] + 1)*(orders[1] + 1);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv1[2]->GetRefPoint(kz, ijk[2]+orders[2]+1);
|
||||
for (int j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.AdjugateJacobian().Mult(x,mx);
|
||||
dofs(o) = mx(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
NURBS_HDiv3DFiniteElement::~NURBS_HDiv3DFiniteElement()
|
||||
{
|
||||
if (kv1[0]) { delete kv1[0]; }
|
||||
@@ -1163,68 +817,13 @@ void NURBS_HCurl2DFiniteElement::CalcCurlShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS_HCurl2DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == dof, "");
|
||||
MFEM_ASSERT(vc.GetVDim() == 2, "");
|
||||
Vector x(2), xm(2);
|
||||
IntegrationPoint ip;
|
||||
int i, j, o;
|
||||
for (o = 0, j = 0; j <= orders[1]+1; j++)
|
||||
{
|
||||
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
|
||||
for (i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.Jacobian().MultTranspose(x,xm);
|
||||
dofs(o) = xm(0);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 2;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (i = 0; i <= orders[0]+1; i++, o++)
|
||||
{
|
||||
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
|
||||
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.Jacobian().MultTranspose(x,xm);
|
||||
dofs(o) = xm(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NURBS_HCurl2DFiniteElement::~NURBS_HCurl2DFiniteElement()
|
||||
{
|
||||
if (kv1[0]) { delete kv1[0]; }
|
||||
if (kv1[1]) { delete kv1[1]; }
|
||||
}
|
||||
|
||||
|
||||
void NURBS_HCurl3DFiniteElement::SetOrder() const
|
||||
{
|
||||
orders[0] = kv[0]->GetOrder();
|
||||
@@ -1404,124 +1003,11 @@ void NURBS_HCurl3DFiniteElement::CalcCurlShape(const IntegrationPoint &ip,
|
||||
curl_shape(o,0) = shape1_x(i)*dsy1_sz;
|
||||
curl_shape(o,1) = -dshape1_x(i)*sy1_sz;
|
||||
curl_shape(o,2) = 0.0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS_HCurl3DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == dof, "");
|
||||
MFEM_ASSERT(vc.GetVDim() == 3, "");
|
||||
Vector x(3), xm(3);
|
||||
IntegrationPoint ip;
|
||||
|
||||
int o = 0;
|
||||
for (int k = 0; k <= orders[2]+1; k++)
|
||||
{
|
||||
real_t kz = kv1[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv1[2]->inSpan(kz, ijk[2]+orders[2]+1))
|
||||
{
|
||||
o += (orders[0] + 1)*(orders[1] + 2);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv1[2]->GetRefPoint(kz, ijk[2]+orders[2]+1);
|
||||
for (int j = 0; j <= orders[1]+1; j++)
|
||||
{
|
||||
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.Jacobian().MultTranspose(x,xm);
|
||||
dofs(o) = xm(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k <= orders[2]+1; k++)
|
||||
{
|
||||
real_t kz = kv1[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv1[2]->inSpan(kz, ijk[2]+orders[2]+1))
|
||||
{
|
||||
o += (orders[0] + 2)*(orders[1] + 1);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv1[2]->GetRefPoint(kz, ijk[2]+orders[2]+1);
|
||||
for (int j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 2;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]+1; i++, o++)
|
||||
{
|
||||
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
|
||||
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.Jacobian().MultTranspose(x,xm);
|
||||
dofs(o) = xm(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k <= orders[2]; k++)
|
||||
{
|
||||
real_t kz = kv[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
|
||||
{
|
||||
o += (orders[0] + 2)*(orders[1] + 2);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
|
||||
for (int j = 0; j <= orders[1]+1; j++)
|
||||
{
|
||||
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
|
||||
{
|
||||
o += orders[0] + 2;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
|
||||
for (int i = 0; i <= orders[0]+1; i++, o++)
|
||||
{
|
||||
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
|
||||
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.Jacobian().MultTranspose(x,xm);
|
||||
dofs(o) = xm(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
NURBS_HCurl3DFiniteElement::~NURBS_HCurl3DFiniteElement()
|
||||
{
|
||||
if (kv1[0]) { delete kv1[0]; }
|
||||
|
||||
@@ -86,18 +86,6 @@ public:
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian (const IntegrationPoint &ip,
|
||||
DenseMatrix &hessian) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
};
|
||||
|
||||
/// An arbitrary order 2D NURBS element on a square
|
||||
@@ -133,18 +121,6 @@ public:
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian (const IntegrationPoint &ip,
|
||||
DenseMatrix &hessian) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
};
|
||||
|
||||
/// An arbitrary order 3D NURBS element on a cube
|
||||
@@ -185,18 +161,6 @@ public:
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian (const IntegrationPoint &ip,
|
||||
DenseMatrix &hessian) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
};
|
||||
|
||||
|
||||
@@ -278,13 +242,6 @@ public:
|
||||
void CalcDivShape(const IntegrationPoint &ip,
|
||||
Vector &divshape) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
~NURBS_HDiv2DFiniteElement();
|
||||
};
|
||||
|
||||
@@ -379,13 +336,6 @@ public:
|
||||
void CalcDivShape(const IntegrationPoint &ip,
|
||||
Vector &divshape) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
~NURBS_HDiv3DFiniteElement();
|
||||
};
|
||||
|
||||
@@ -465,13 +415,6 @@ public:
|
||||
void CalcCurlShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &curl_shape) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
~NURBS_HCurl2DFiniteElement();
|
||||
};
|
||||
|
||||
@@ -563,13 +506,6 @@ public:
|
||||
void CalcCurlShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &curl_shape) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
~NURBS_HCurl3DFiniteElement();
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -509,7 +509,7 @@ GetFace(int &nv, v_t &v, int &ne, e_t &e, eo_t &eo,
|
||||
int v0 = v[f_consts::Edges[i][0]];
|
||||
int v1 = v[f_consts::Edges[i][1]];
|
||||
int eor = 0;
|
||||
if (v0 > v1) { std::swap(v0, v1); eor = 1; }
|
||||
if (v0 > v1) { swap(v0, v1); eor = 1; }
|
||||
for (int j = g_consts::VertToVert::I[v0]; true; j++)
|
||||
{
|
||||
MFEM_ASSERT(j < g_consts::VertToVert::I[v0+1],
|
||||
|
||||
+13
-13
@@ -111,36 +111,36 @@ public:
|
||||
| :------: | :---: | :---: | :-------: | :-----: | :---: |
|
||||
| H1_[DIM]_[ORDER] | H1 | * | 1 | VALUE | H1 nodal elements |
|
||||
| H1@[BTYPE]_[DIM]_[ORDER] | H1 | * | * | VALUE | H1 nodal elements |
|
||||
| H1Pos_[DIM]_[ORDER] | H1 | * | 2 | VALUE | H1 nodal elements |
|
||||
| H1Pos_[DIM]_[ORDER] | H1 | * | 1 | VALUE | H1 nodal elements |
|
||||
| H1Pos_Trace_[DIM]_[ORDER] | H^{1/2} | * | 2 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
|
||||
| H1_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
|
||||
| H1_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | * | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
|
||||
| H1_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
|
||||
| ND_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | Nedelec vector elements |
|
||||
| ND@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | Nedelec vector elements |
|
||||
| ND_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces,edges) |
|
||||
| ND_Trace@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H^{1/2} | * | * / * | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces,edges) |
|
||||
| ND_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
|
||||
| ND_Trace@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
|
||||
| ND_R1D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 1D. |
|
||||
| ND_R1D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 1D. |
|
||||
| ND_R2D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 2D. |
|
||||
| ND_R2D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 2D. |
|
||||
| RT_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | Raviart-Thomas vector elements |
|
||||
| RT@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | Raviart-Thomas vector elements |
|
||||
| RT_Trace_[DIM]_[ORDER] | H^{1/2} | * | 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_ValTrace_[DIM]_[ORDER] | H^{1/2} | * | 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | * | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_ValTrace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | * | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_ValTrace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_ValTrace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_R1D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 1D. |
|
||||
| RT_R1D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 1D. |
|
||||
| RT_R2D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 2D. |
|
||||
| RT_R2D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 2D. |
|
||||
| L2_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
|
||||
| L2_T[BTYPE]_[DIM]_[ORDER] | L2 | * | * | VALUE | Discontinuous L2 elements |
|
||||
| L2_T[BTYPE]_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
|
||||
| L2Int_[DIM]_[ORDER] | L2 | * | 0 | INTEGRAL | Discontinuous L2 elements |
|
||||
| L2Int_T[BTYPE]_[DIM]_[ORDER] | L2 | * | * | INTEGRAL | Discontinuous L2 elements |
|
||||
| L2Int_T[BTYPE]_[DIM]_[ORDER] | L2 | * | 0 | INTEGRAL | Discontinuous L2 elements |
|
||||
| DG_Iface_[DIM]_[ORDER] | - | * | 0 | VALUE | Discontinuous elements on the interface between mesh elements (faces) |
|
||||
| DG_Iface@[BTYPE]_[DIM]_[ORDER] | - | * | * | VALUE | Discontinuous elements on the interface between mesh elements (faces) |
|
||||
| DG_Iface@[BTYPE]_[DIM]_[ORDER] | - | * | 0 | VALUE | Discontinuous elements on the interface between mesh elements (faces) |
|
||||
| DG_IntIface_[DIM]_[ORDER] | - | * | 0 | INTEGRAL | Discontinuous elements on the interface between mesh elements (faces) |
|
||||
| DG_IntIface@[BTYPE]_[DIM]_[ORDER] | - | * | * | INTEGRAL | Discontinuous elements on the interface between mesh elements (faces) |
|
||||
| DG_IntIface@[BTYPE]_[DIM]_[ORDER] | - | * | 0 | INTEGRAL | Discontinuous elements on the interface between mesh elements (faces) |
|
||||
| NURBS[ORDER] | - | * | - | VALUE | Non-Uniform Rational B-Splines (NURBS) elements |
|
||||
| LinearNonConf3D | - | 1 | 1 | VALUE | Piecewise-linear nonconforming finite elements in 3D |
|
||||
| CrouzeixRaviart | - | - | - | - | Crouzeix-Raviart nonconforming elements in 2D |
|
||||
@@ -172,7 +172,7 @@ public:
|
||||
| :------: | :--------: |
|
||||
| [DIM] | Dimension of the elements (1D, 2D, 3D) |
|
||||
| [ORDER] | Approximation order of the elements (P0, P1, P2, ...) |
|
||||
| [BTYPE] | BasisType of the element (0-GaussLegendre, 1-GaussLobatto, 2-Bernstein, 3-OpenUniform, 4-CloseUniform, 5-OpenHalfUniform 6-Serendipity 7-ClosedGL 8-IntegratedGLL) |
|
||||
| [BTYPE] | BasisType of the element (0-GaussLegendre, 1 - GaussLobatto, 2-Bernstein, 3-OpenUniform, 4-CloseUniform, 5-OpenHalfUniform) |
|
||||
| [OBTYPE] | Open BasisType of the element for elements which have both types |
|
||||
| [CBTYPE] | Closed BasisType of the element for elements which have both types |
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
#include "dgmassinv.hpp"
|
||||
#include "hyperbolic.hpp"
|
||||
#include "bounds.hpp"
|
||||
#include "particleset.hpp"
|
||||
|
||||
#include "dfem/doperator.hpp"
|
||||
|
||||
|
||||
+44
-65
@@ -27,6 +27,37 @@ using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
template <>
|
||||
void Ordering::DofsToVDofs<Ordering::byNODES>(int ndofs, int vdim,
|
||||
Array<int> &dofs)
|
||||
{
|
||||
// static method
|
||||
int size = dofs.Size();
|
||||
dofs.SetSize(size*vdim);
|
||||
for (int vd = 1; vd < vdim; vd++)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
dofs[i+size*vd] = Map<byNODES>(ndofs, vdim, dofs[i], vd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void Ordering::DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim,
|
||||
Array<int> &dofs)
|
||||
{
|
||||
// static method
|
||||
int size = dofs.Size();
|
||||
dofs.SetSize(size*vdim);
|
||||
for (int vd = vdim-1; vd >= 0; vd--)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
dofs[i+size*vd] = Map<byVDIM>(ndofs, vdim, dofs[i], vd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FiniteElementSpace::FiniteElementSpace()
|
||||
: mesh(NULL), fec(NULL), vdim(0), ordering(Ordering::byNODES),
|
||||
@@ -1516,87 +1547,42 @@ const FaceRestriction *FiniteElementSpace::GetFaceRestriction(
|
||||
const bool is_dg_space = IsDGSpace();
|
||||
const L2FaceValues m = (is_dg_space && mul==L2FaceValues::DoubleValued) ?
|
||||
L2FaceValues::DoubleValued : L2FaceValues::SingleValued;
|
||||
auto key = std::make_tuple(is_dg_space, f_ordering, type, m);
|
||||
key_face key = std::make_tuple(is_dg_space, f_ordering, type, m);
|
||||
auto itr = L2F.find(key);
|
||||
if (itr != L2F.end())
|
||||
{
|
||||
return itr->second.get();
|
||||
return itr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_ptr<FaceRestriction> res;
|
||||
FaceRestriction *res;
|
||||
if (is_dg_space)
|
||||
{
|
||||
if (Conforming())
|
||||
{
|
||||
res.reset(new L2FaceRestriction(*this, f_ordering, type, m));
|
||||
res = new L2FaceRestriction(*this, f_ordering, type, m);
|
||||
}
|
||||
else
|
||||
{
|
||||
res.reset(new NCL2FaceRestriction(*this, f_ordering, type, m));
|
||||
res = new NCL2FaceRestriction(*this, f_ordering, type, m);
|
||||
}
|
||||
}
|
||||
else if (dynamic_cast<const DG_Interface_FECollection*>(fec))
|
||||
{
|
||||
res.reset(new L2InterfaceFaceRestriction(*this, f_ordering, type));
|
||||
res = new L2InterfaceFaceRestriction(*this, f_ordering, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
res.reset(new ConformingFaceRestriction(*this, f_ordering, type));
|
||||
res = new ConformingFaceRestriction(*this, f_ordering, type);
|
||||
}
|
||||
return L2F.emplace(key, std::move(res)).first->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
const InterpolationManager &FiniteElementSpace::GetInterpolationManager(
|
||||
ElementDofOrdering f_ordering, FaceType type) const
|
||||
{
|
||||
const auto key = make_tuple(f_ordering, type);
|
||||
|
||||
auto it = interpolations.find(key);
|
||||
if (it != interpolations.end())
|
||||
{
|
||||
return *it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto interp = make_unique<InterpolationManager>(*this, f_ordering, type);
|
||||
|
||||
int face_idx = 0;
|
||||
for (int f = 0; f < mesh->GetNumFacesWithGhost(); ++f)
|
||||
{
|
||||
Mesh::FaceInformation face = mesh->GetFaceInformation(f);
|
||||
if (!face.IsOfFaceType(type) || face.IsNonconformingCoarse())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (face.IsConforming() || face.IsBoundary())
|
||||
{
|
||||
interp->RegisterFaceConformingInterpolation(face, face_idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
interp->RegisterFaceCoarseToFineInterpolation(face, face_idx);
|
||||
}
|
||||
++face_idx;
|
||||
}
|
||||
|
||||
// Transform the interpolation matrix map into contiguous memory.
|
||||
interp->LinearizeInterpolatorMapIntoVector();
|
||||
interp->InitializeNCInterpConfig();
|
||||
|
||||
return *interpolations.emplace(key, std::move(interp)).first->second;
|
||||
L2F[key] = res;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
const QuadratureInterpolator *FiniteElementSpace::GetQuadratureInterpolator(
|
||||
const IntegrationRule &ir) const
|
||||
{
|
||||
if (!QuadratureInterpolator::SupportsFESpace(*this))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (int i = 0; i < E2Q_array.Size(); i++)
|
||||
{
|
||||
const QuadratureInterpolator *qi = E2Q_array[i];
|
||||
@@ -1611,11 +1597,6 @@ const QuadratureInterpolator *FiniteElementSpace::GetQuadratureInterpolator(
|
||||
const QuadratureInterpolator *FiniteElementSpace::GetQuadratureInterpolator(
|
||||
const QuadratureSpace &qs) const
|
||||
{
|
||||
if (!QuadratureInterpolator::SupportsFESpace(*this))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (int i = 0; i < E2Q_array.Size(); i++)
|
||||
{
|
||||
const QuadratureInterpolator *qi = E2Q_array[i];
|
||||
@@ -1631,11 +1612,6 @@ const FaceQuadratureInterpolator
|
||||
*FiniteElementSpace::GetFaceQuadratureInterpolator(
|
||||
const IntegrationRule &ir, FaceType type) const
|
||||
{
|
||||
if (!FaceQuadratureInterpolator::SupportsFESpace(*this))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type==FaceType::Interior)
|
||||
{
|
||||
for (int i = 0; i < E2IFQ_array.Size(); i++)
|
||||
@@ -4009,8 +3985,11 @@ void FiniteElementSpace::Destroy()
|
||||
delete E2Q_array[i];
|
||||
}
|
||||
E2Q_array.SetSize(0);
|
||||
for (auto &x : L2F)
|
||||
{
|
||||
delete x.second;
|
||||
}
|
||||
L2F.clear();
|
||||
interpolations.clear();
|
||||
for (int i = 0; i < E2IFQ_array.Size(); i++)
|
||||
{
|
||||
delete E2IFQ_array[i];
|
||||
|
||||
+52
-22
@@ -13,8 +13,6 @@
|
||||
#define MFEM_FESPACE
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../general/hash_util.hpp"
|
||||
#include "../linalg/ordering.hpp"
|
||||
#include "../linalg/sparsemat.hpp"
|
||||
#include "../mesh/mesh.hpp"
|
||||
#include "fe_coll.hpp"
|
||||
@@ -26,6 +24,29 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** @brief The ordering method used when the number of unknowns per mesh node
|
||||
(vector dimension) is bigger than 1. */
|
||||
class Ordering
|
||||
{
|
||||
public:
|
||||
/// %Ordering methods:
|
||||
enum Type
|
||||
{
|
||||
byNODES, /**< loop first over the nodes (inner loop) then over the vector
|
||||
dimension (outer loop); symbolically it can be represented
|
||||
as: XXX...,YYY...,ZZZ... */
|
||||
byVDIM /**< loop first over the vector dimension (inner loop) then over
|
||||
the nodes (outer loop); symbolically it can be represented
|
||||
as: XYZ,XYZ,XYZ,... */
|
||||
};
|
||||
|
||||
template <Type Ord>
|
||||
static inline int Map(int ndofs, int vdim, int dof, int vd);
|
||||
|
||||
template <Type Ord>
|
||||
static void DofsToVDofs(int ndofs, int vdim, Array<int> &dofs);
|
||||
};
|
||||
|
||||
/// @brief Type describing possible layouts for Q-vectors.
|
||||
/// @sa QuadratureInterpolator and FaceQuadratureInterpolator.
|
||||
enum class QVectorLayout
|
||||
@@ -43,6 +64,20 @@ enum class QVectorLayout
|
||||
byVDIM
|
||||
};
|
||||
|
||||
template <> inline int
|
||||
Ordering::Map<Ordering::byNODES>(int ndofs, int vdim, int dof, int vd)
|
||||
{
|
||||
MFEM_ASSERT(dof < ndofs && -1-dof < ndofs && 0 <= vd && vd < vdim, "");
|
||||
return (dof >= 0) ? dof+ndofs*vd : dof-ndofs*vd;
|
||||
}
|
||||
|
||||
template <> inline int
|
||||
Ordering::Map<Ordering::byVDIM>(int ndofs, int vdim, int dof, int vd)
|
||||
{
|
||||
MFEM_ASSERT(dof < ndofs && -1-dof < ndofs && 0 <= vd && vd < vdim, "");
|
||||
return (dof >= 0) ? vd+vdim*dof : -1-(vd+vdim*(-1-dof));
|
||||
}
|
||||
|
||||
/// Constants describing the possible orderings of the DOFs in one element.
|
||||
enum class ElementDofOrdering
|
||||
{
|
||||
@@ -321,11 +356,18 @@ protected:
|
||||
mutable OperatorHandle L2E_nat, L2E_lex;
|
||||
/// The face restriction operators, see GetFaceRestriction().
|
||||
using key_face = std::tuple<bool, ElementDofOrdering, FaceType, L2FaceValues>;
|
||||
mutable std::unordered_map<key_face,std::unique_ptr<FaceRestriction>,
|
||||
TupleHasher> L2F;
|
||||
|
||||
mutable std::unordered_map<std::tuple<ElementDofOrdering,FaceType>,
|
||||
std::unique_ptr<InterpolationManager>, TupleHasher> interpolations;
|
||||
struct key_hash
|
||||
{
|
||||
std::size_t operator()(const key_face& k) const
|
||||
{
|
||||
return std::get<0>(k)
|
||||
+ 2 * (int)std::get<1>(k)
|
||||
+ 4 * (int)std::get<2>(k)
|
||||
+ 8 * (int)std::get<3>(k);
|
||||
}
|
||||
};
|
||||
using map_L2F = std::unordered_map<const key_face,FaceRestriction*,key_hash>;
|
||||
mutable map_L2F L2F;
|
||||
|
||||
mutable Array<QuadratureInterpolator*> E2Q_array;
|
||||
mutable Array<FaceQuadratureInterpolator*> E2IFQ_array;
|
||||
@@ -745,9 +787,6 @@ public:
|
||||
ElementDofOrdering f_ordering, FaceType,
|
||||
L2FaceValues mul = L2FaceValues::DoubleValued) const;
|
||||
|
||||
const InterpolationManager &GetInterpolationManager(
|
||||
ElementDofOrdering f_ordering, FaceType type) const;
|
||||
|
||||
/** @brief Return a QuadratureInterpolator that interpolates E-vectors to
|
||||
quadrature point values and/or derivatives (Q-vectors). */
|
||||
/** An E-vector represents the element-wise discontinuous version of the FE
|
||||
@@ -760,10 +799,7 @@ public:
|
||||
@note The returned pointer is shared. A good practice, before using it,
|
||||
is to set all its properties to their expected values, as other parts of
|
||||
the code may also change them. That is, it's good to call
|
||||
SetOutputLayout() and DisableTensorProducts() before interpolating.
|
||||
|
||||
@note If the space is not supported by QuadratureInterpolator, nullptr is
|
||||
returned. */
|
||||
SetOutputLayout() and DisableTensorProducts() before interpolating. */
|
||||
const QuadratureInterpolator *GetQuadratureInterpolator(
|
||||
const IntegrationRule &ir) const;
|
||||
|
||||
@@ -779,10 +815,7 @@ public:
|
||||
@note The returned pointer is shared. A good practice, before using it,
|
||||
is to set all its properties to their expected values, as other parts of
|
||||
the code may also change them. That is, it's good to call
|
||||
SetOutputLayout() and DisableTensorProducts() before interpolating.
|
||||
|
||||
@note If the space is not supported by QuadratureInterpolator, nullptr is
|
||||
returned. */
|
||||
SetOutputLayout() and DisableTensorProducts() before interpolating. */
|
||||
const QuadratureInterpolator *GetQuadratureInterpolator(
|
||||
const QuadratureSpace &qs) const;
|
||||
|
||||
@@ -792,10 +825,7 @@ public:
|
||||
@note The returned pointer is shared. A good practice, before using it,
|
||||
is to set all its properties to their expected values, as other parts of
|
||||
the code may also change them. That is, it's good to call
|
||||
SetOutputLayout() and DisableTensorProducts() before interpolating.
|
||||
|
||||
@note If the space is not supported by FaceQuadratureInterpolator,
|
||||
nullptr is returned. */
|
||||
SetOutputLayout() and DisableTensorProducts() before interpolating. */
|
||||
const FaceQuadratureInterpolator *GetFaceQuadratureInterpolator(
|
||||
const IntegrationRule &ir, FaceType type) const;
|
||||
|
||||
|
||||
+67
-528
@@ -2352,83 +2352,52 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
|
||||
void GridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
{
|
||||
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
|
||||
DofTransformation doftrans;
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
if (delta_c == NULL)
|
||||
{
|
||||
if (fes->GetNURBSext() == NULL)
|
||||
{
|
||||
switch (type)
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
case ProjectType::ELEMENT_L2:
|
||||
ProjectCoefficientElementL2(coeff);
|
||||
return;
|
||||
case ProjectType::GLOBAL_L2:
|
||||
ProjectCoefficientGlobalL2(coeff);
|
||||
return;
|
||||
default:
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(coeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(coeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ProjectType::DEFAULT:
|
||||
case ProjectType::ELEMENT_L2:
|
||||
ProjectCoefficientElementL2(coeff);
|
||||
return;
|
||||
case ProjectType::GLOBAL_L2:
|
||||
ProjectCoefficientGlobalL2(coeff);
|
||||
return;
|
||||
case ProjectType::ELEMENT:
|
||||
constexpr real_t signal = std::numeric_limits<real_t>::min();
|
||||
// Define and assemble linear form
|
||||
LinearForm b(fes);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(coeff));
|
||||
b.Assemble();
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
vals = signal;
|
||||
// Define and assemble bilinear form
|
||||
BilinearForm a(fes);
|
||||
a.AddDomainIntegrator(new MassIntegrator());
|
||||
a.Assemble();
|
||||
|
||||
fes->GetFE(i)->Project(coeff,
|
||||
*fes->GetElementTransformation(i),
|
||||
vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
// Set solver and preconditioner
|
||||
SparseMatrix A(a.SpMat());
|
||||
GSSmoother prec(A);
|
||||
CGSolver cg;
|
||||
cg.SetOperator(A);
|
||||
cg.SetPreconditioner(prec);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(1000);
|
||||
cg.SetPrintLevel(0);
|
||||
|
||||
// Remove undefined dofs
|
||||
// The knot location (either Botella, Demko or Greville point)
|
||||
// where the NURBS dof are evaluated might fall outside of the
|
||||
// domain of the element. In that case the value is not set, and
|
||||
// the value remains the signal value.
|
||||
int s = 0;
|
||||
for (int ii = 0; ii < vals.Size(); ii++)
|
||||
{
|
||||
if (vals[ii] != signal)
|
||||
{
|
||||
vdofs[s] = vdofs[ii];
|
||||
vals(s) = vals(ii);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
vdofs.SetSize(s);
|
||||
vals.SetSize(s);
|
||||
|
||||
// Add reduced dofs to global vector
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
// Solve and get solution
|
||||
*this = 0.0;
|
||||
cg.Mult(b,*this);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2441,167 +2410,6 @@ void GridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientGlobalL2(Coefficient &coeff, real_t rtol,
|
||||
int iter)
|
||||
{
|
||||
// Define and assemble linear form
|
||||
LinearForm b(fes);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(coeff));
|
||||
b.Assemble();
|
||||
|
||||
// Define and assemble bilinear form
|
||||
BilinearForm a(fes);
|
||||
a.AddDomainIntegrator(new MassIntegrator());
|
||||
a.Assemble();
|
||||
|
||||
// Set solver and preconditioner
|
||||
SparseMatrix A(a.SpMat());
|
||||
GSSmoother prec(A);
|
||||
CGSolver cg;
|
||||
cg.SetOperator(A);
|
||||
cg.SetPreconditioner(prec);
|
||||
cg.SetRelTol(rtol);
|
||||
cg.SetMaxIter(iter);
|
||||
cg.SetPrintLevel(0);
|
||||
|
||||
// Solve and get solution
|
||||
*this = 0.0;
|
||||
cg.Mult(b,*this);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientElementL2(Coefficient &coeff)
|
||||
{
|
||||
Vector Va;
|
||||
ProjectCoefficientElementL2_(coeff, *this, Va);
|
||||
(*this) /= Va;
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientElementL2_(Coefficient &coeff,
|
||||
Vector &x, Vector &Va)
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
Array<int> vdofs;
|
||||
Vector shape,shape2, elvect, elwght;
|
||||
DenseMatrix elmat;
|
||||
Va.SetSize(fes->GetNDofs() );
|
||||
x.SetSize(fes->GetNDofs() );
|
||||
Va = 0.0;
|
||||
x = 0.0;
|
||||
|
||||
if (fes->GetNURBSext() == NULL)
|
||||
{
|
||||
for (int e = 0; e < fes->GetNE(); e++)
|
||||
{
|
||||
fes->GetElementDofs (e, vdofs, doftrans);
|
||||
ElementTransformation &tr = *fes -> GetElementTransformation (e);
|
||||
const FiniteElement &el = *fes->GetFE(e);
|
||||
int dof = el.GetDof();
|
||||
shape.SetSize(dof);
|
||||
elvect.SetSize(dof);
|
||||
elwght.SetSize(dof);
|
||||
elmat.SetSize(dof,dof);
|
||||
elvect = 0.0;
|
||||
elwght = 0.0;
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule &ir = IntRules.Get(el.GetGeomType(),
|
||||
2 * el.GetOrder() + 1);
|
||||
|
||||
// Element vector & weight
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
|
||||
tr.SetIntPoint (&ip);
|
||||
real_t wght = ip.weight*tr.Weight();
|
||||
real_t val = coeff.Eval(tr, ip);
|
||||
|
||||
el.CalcPhysShape(tr, shape);
|
||||
|
||||
elvect.Add(wght * val, shape);
|
||||
elwght.Add(wght, shape);
|
||||
AddMult_a_VVt(wght, shape, elmat);
|
||||
}
|
||||
|
||||
// Solve
|
||||
if (!LinearSolve(elmat, elvect.GetData(),1e-12))
|
||||
{
|
||||
MFEM_WARNING("Error in inverting element local matrix");
|
||||
}
|
||||
|
||||
// Scale
|
||||
elvect *= elwght;
|
||||
|
||||
// Add reduced dofs to global vector
|
||||
x.AddElementVector(vdofs, elvect);
|
||||
Va.AddElementVector(vdofs, elwght);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int e = 0; e < fes->GetNE(); e++)
|
||||
{
|
||||
fes->GetElementDofs (e, vdofs, doftrans);
|
||||
ElementTransformation &tr = *fes -> GetElementTransformation (e);
|
||||
const FiniteElement &el = *fes->GetFE(e);
|
||||
int dof = el.GetDof();
|
||||
int dim = el.GetDim();
|
||||
int p = el.GetOrder();
|
||||
L2_FECollection fe_coll(p, dim);
|
||||
//H1_FECollection fe_coll(p, dim, BasisType::Positive);
|
||||
const FiniteElement &el2 = *fe_coll.FiniteElementForGeometry(el.GetGeomType());
|
||||
MFEM_ASSERT(el2.GetDof() == dof, "Element dofs do not match.");
|
||||
|
||||
shape.SetSize(dof);
|
||||
shape2.SetSize(dof);
|
||||
elvect.SetSize(dof);
|
||||
elwght.SetSize(dof);
|
||||
elmat.SetSize(dof,dof);
|
||||
elvect = 0.0;
|
||||
elwght = 0.0;
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule &ir = IntRules.Get(el.GetGeomType(),
|
||||
2 * el.GetOrder() + 1);
|
||||
|
||||
// Element vector & weight
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
|
||||
tr.SetIntPoint (&ip);
|
||||
real_t wght = ip.weight*tr.Weight();
|
||||
real_t val = coeff.Eval(tr, ip);
|
||||
el.CalcPhysShape(tr, shape);
|
||||
el2.CalcPhysShape(tr, shape2);
|
||||
|
||||
elvect.Add(wght * val, shape2);
|
||||
elwght.Add(wght, shape);
|
||||
AddMult_a_VVt(wght, shape2, elmat);
|
||||
}
|
||||
// Solve
|
||||
if (!LinearSolve(elmat, elvect.GetData(),1e-12))
|
||||
{
|
||||
MFEM_WARNING("Error in inverting element local matrix 2");
|
||||
}
|
||||
// Map to NURBS
|
||||
DenseMatrix I;
|
||||
el2.Project(el,tr,I);
|
||||
if (!LinearSolve(I, elvect.GetData(),1e-32))
|
||||
{
|
||||
MFEM_WARNING("Error in inverting element local matrix 3");
|
||||
}
|
||||
|
||||
// Scale
|
||||
elvect *= elwght;
|
||||
|
||||
// Add reduced dofs to global vector
|
||||
x.AddElementVector(vdofs, elvect);
|
||||
Va.AddElementVector(vdofs, elwght);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficient(
|
||||
Coefficient &coeff, Array<int> &dofs, int vd)
|
||||
{
|
||||
@@ -2626,318 +2434,49 @@ void GridFunction::ProjectCoefficient(
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
|
||||
ProjectType type)
|
||||
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
DofTransformation doftrans;
|
||||
|
||||
if (fes->GetNURBSext() == NULL)
|
||||
{
|
||||
switch (type)
|
||||
int i;
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
for (i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
case ProjectType::ELEMENT_L2:
|
||||
ProjectCoefficientElementL2(vcoeff);
|
||||
return;
|
||||
case ProjectType::GLOBAL_L2:
|
||||
ProjectCoefficientGlobalL2(vcoeff);
|
||||
return;
|
||||
default:
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ProjectType::DEFAULT:
|
||||
case ProjectType::ELEMENT_L2:
|
||||
ProjectCoefficientElementL2(vcoeff);
|
||||
return;
|
||||
case ProjectType::GLOBAL_L2:
|
||||
ProjectCoefficientGlobalL2(vcoeff);
|
||||
return;
|
||||
case ProjectType::ELEMENT:
|
||||
constexpr real_t signal = std::numeric_limits<real_t>::min();
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
vals = signal;
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
// Remove undefined dofs
|
||||
// The knot location (either Botella, Demko or Greville point)
|
||||
// where the NURBS dof are evaluated might fall outside of the
|
||||
// domain of the element. In that case the value is not set, and
|
||||
// the value remains the signal value.
|
||||
int s = 0;
|
||||
for (int ii = 0; ii < vals.Size(); ii++)
|
||||
{
|
||||
if (vals[ii] != signal)
|
||||
{
|
||||
vdofs[s] = vdofs[ii];
|
||||
vals(s) = vals(ii);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
vdofs.SetSize(s);
|
||||
vals.SetSize(s);
|
||||
|
||||
// Add reduced dofs to global vector
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
|
||||
real_t rtol, int iter)
|
||||
{
|
||||
// Define and assemble linear form
|
||||
LinearForm b(fes);
|
||||
BilinearForm a(fes);
|
||||
|
||||
if (fes->GetTypicalFE()->GetRangeType() == mfem::FiniteElement::VECTOR)
|
||||
{
|
||||
// Define and assemble linear form
|
||||
LinearForm b(fes);
|
||||
b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(vcoeff));
|
||||
b.Assemble();
|
||||
|
||||
// Define and assemble bilinear form
|
||||
BilinearForm a(fes);
|
||||
a.AddDomainIntegrator(new VectorFEMassIntegrator());
|
||||
}
|
||||
else
|
||||
{
|
||||
b.AddDomainIntegrator(new VectorDomainLFIntegrator(vcoeff));
|
||||
a.AddDomainIntegrator(new VectorMassIntegrator());
|
||||
}
|
||||
a.Assemble();
|
||||
b.Assemble();
|
||||
a.Assemble();
|
||||
|
||||
// Set solver and preconditioner
|
||||
SparseMatrix A(a.SpMat());
|
||||
GSSmoother prec(A);
|
||||
CGSolver cg;
|
||||
cg.SetOperator(A);
|
||||
cg.SetPreconditioner(prec);
|
||||
cg.SetRelTol(rtol);
|
||||
cg.SetMaxIter(iter);
|
||||
cg.SetPrintLevel(0);
|
||||
// Set solver and preconditioner
|
||||
SparseMatrix A(a.SpMat());
|
||||
GSSmoother prec(A);
|
||||
CGSolver cg;
|
||||
cg.SetOperator(A);
|
||||
cg.SetPreconditioner(prec);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(1000);
|
||||
cg.SetPrintLevel(0);
|
||||
|
||||
// Solve and get solution
|
||||
*this = 0.0;
|
||||
cg.Mult(b,*this);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientElementL2_(VectorCoefficient &vcoeff,
|
||||
Vector &x, Vector &Va)
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
Array<int> vdofs;
|
||||
Vector shapel2, elvect, elwght, val;
|
||||
DenseMatrix shape, elmat;
|
||||
Va.SetSize(Size());
|
||||
x.SetSize(Size());
|
||||
Va = 0.0;
|
||||
x = 0.0;
|
||||
|
||||
if (fes->GetNURBSext() == NULL)
|
||||
{
|
||||
for (int e = 0; e < fes->GetNE(); e++)
|
||||
{
|
||||
fes->GetElementVDofs (e, vdofs, doftrans);
|
||||
ElementTransformation &tr = *fes -> GetElementTransformation (e);
|
||||
const FiniteElement &el = *fes->GetFE(e);
|
||||
int dof = el.GetDof();
|
||||
int dim = el.GetRangeDim();
|
||||
shape.SetSize(dof,dim);
|
||||
shapel2.SetSize(dof);
|
||||
elvect.SetSize(dof);
|
||||
elwght.SetSize(dof);
|
||||
elmat.SetSize(dof,dof);
|
||||
elvect = 0.0;
|
||||
elwght = 0.0;
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule &ir = IntRules.Get(el.GetGeomType(),
|
||||
2 * el.GetOrder() + 1);
|
||||
|
||||
// Element vector & weight
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
|
||||
tr.SetIntPoint (&ip);
|
||||
real_t wght = ip.weight*tr.Weight();
|
||||
vcoeff.Eval(val, tr, ip);
|
||||
val *= wght;
|
||||
|
||||
el.CalcPhysVShape(tr, shape);
|
||||
|
||||
shape.AddMult (val, elvect);
|
||||
AddMult_a_AAt(wght, shape, elmat);
|
||||
|
||||
shape.GetRowl2(shapel2);
|
||||
elwght.Add(wght, shapel2);
|
||||
}
|
||||
|
||||
// Solve
|
||||
if (!LinearSolve(elmat, elvect.GetData(),1e-12))
|
||||
{
|
||||
MFEM_WARNING("Error in inverting element local matrix");
|
||||
}
|
||||
|
||||
// Scale
|
||||
elvect *= elwght;
|
||||
|
||||
// Add to global vector
|
||||
x.AddElementVector(vdofs, elvect);
|
||||
|
||||
// Add to weight vector -- no need for an orientation
|
||||
for (int i = 0; i < vdofs.Size(); i++)
|
||||
{
|
||||
vdofs[i] = FiniteElementSpace::DecodeDof(vdofs[i]);
|
||||
}
|
||||
Va.AddElementVector(vdofs, elwght);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DenseMatrix partelmat;
|
||||
Vector shape2;
|
||||
|
||||
if (fes->GetTypicalFE()->GetOrder() >= 6 )
|
||||
{
|
||||
MFEM_WARNING("This project is not stable for"
|
||||
"NURBS VectorFE with order >= 5");
|
||||
}
|
||||
for (int e = 0; e < fes->GetNE(); e++)
|
||||
{
|
||||
fes->GetElementVDofs (e, vdofs, doftrans);
|
||||
ElementTransformation &tr = *fes -> GetElementTransformation (e);
|
||||
const FiniteElement &el = *fes->GetFE(e);
|
||||
int dof = el.GetDof();
|
||||
int dim = el.GetRangeDim();
|
||||
int p = el.GetOrder();
|
||||
L2_FECollection fe_coll(p, dim);
|
||||
const FiniteElement &el2 = *fe_coll.FiniteElementForGeometry(el.GetGeomType());
|
||||
int dof2 = el2.GetDof();
|
||||
MFEM_ASSERT(dof2*dim >= dof, "Element dofs do not match.");
|
||||
shape2.SetSize(dof2);
|
||||
shape.SetSize(dof,dim);
|
||||
shapel2.SetSize(dof);
|
||||
elvect.SetSize(dof2*dim);
|
||||
elwght.SetSize(dof);
|
||||
elmat.SetSize(dof2*dim,dof2*dim);
|
||||
partelmat.SetSize(dof2,dof2);
|
||||
elvect = 0.0;
|
||||
elwght = 0.0;
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule &ir = IntRules.Get(el.GetGeomType(),
|
||||
2 * el.GetOrder() + 1);
|
||||
|
||||
// Element vector & weight
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
|
||||
tr.SetIntPoint (&ip);
|
||||
real_t wght = ip.weight*tr.Weight();
|
||||
vcoeff.Eval(val, tr, ip);
|
||||
val *= wght;
|
||||
|
||||
el2.CalcPhysShape(tr, shape2);
|
||||
el.CalcPhysVShape(tr, shape);
|
||||
|
||||
for (int k = 0; k < dim; k++)
|
||||
{
|
||||
for (int s = 0; s < dof2; s++)
|
||||
{
|
||||
elvect(dof2*k+s) += val(k) * shape2(s);
|
||||
}
|
||||
}
|
||||
|
||||
MultVVt(shape2, partelmat);
|
||||
partelmat *= wght;
|
||||
for (int k = 0; k < dim; k++)
|
||||
{
|
||||
elmat.AddMatrix(partelmat, dof2*k, dof2*k);
|
||||
}
|
||||
|
||||
shape.GetRowl2(shapel2);
|
||||
elwght.Add(wght, shapel2);
|
||||
}
|
||||
|
||||
// Solve
|
||||
if (!LinearSolve(elmat, elvect.GetData()))
|
||||
{
|
||||
MFEM_WARNING("Error in inverting element local matrix");
|
||||
}
|
||||
|
||||
// Map to NURBS
|
||||
DenseMatrix I;
|
||||
el2.Project(el,tr,I);
|
||||
|
||||
// LSQ solve
|
||||
// For higher order NURBS solving this non-square matrix causes issues.
|
||||
// For Order <=4 the routine seems to work fine.
|
||||
Vector vec(dof);
|
||||
DenseMatrix mat(dof, dof);
|
||||
I.Transpose();
|
||||
I.Mult(elvect, vec);
|
||||
MultAAt(I, mat);
|
||||
if (!LinearSolve(mat, vec.GetData(), 1e-24))
|
||||
{
|
||||
mat.TestInversion();
|
||||
MFEM_WARNING("Error in inverting element local matrix");
|
||||
}
|
||||
elvect = vec;
|
||||
|
||||
// Scale
|
||||
elvect *= elwght;
|
||||
|
||||
// Add to global vector
|
||||
x.AddElementVector(vdofs, elvect);
|
||||
|
||||
// Add to weight vector -- no need for an orientation
|
||||
for (int i = 0; i < vdofs.Size(); i++)
|
||||
{
|
||||
vdofs[i] = FiniteElementSpace::DecodeDof(vdofs[i]);
|
||||
}
|
||||
Va.AddElementVector(vdofs, elwght);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientElementL2(VectorCoefficient &vcoeff)
|
||||
{
|
||||
if (fes->GetTypicalFE()->GetRangeType() == mfem::FiniteElement::VECTOR)
|
||||
{
|
||||
Vector Va;
|
||||
ProjectCoefficientElementL2_(vcoeff, *this, Va);
|
||||
(*this) /= Va;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array<int> vdofs(fes->GetNDofs());
|
||||
Vector x, Va;
|
||||
VectorComponentCoefficient coeff(vcoeff,
|
||||
0); // 0 to ensure we have a valid object
|
||||
|
||||
for (int v = 0; v < VectorDim(); v++)
|
||||
{
|
||||
coeff.SetComponent(v);
|
||||
ProjectCoefficientElementL2_(coeff, x, Va);
|
||||
x /= Va;
|
||||
fes->GetVDofs(v, vdofs);
|
||||
SetSubVector(vdofs, x);
|
||||
}
|
||||
// Solve and get solution
|
||||
*this = 0.0;
|
||||
cg.Mult(b,*this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5071,7 +4610,7 @@ GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
|
||||
void GridFunction::GetElementBoundsAtControlPoints(const int elem,
|
||||
const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim) const
|
||||
const int vdim)
|
||||
{
|
||||
const FiniteElement *fe = fes->GetFE(elem);
|
||||
int fes_dim = fes->GetVDim();
|
||||
@@ -5087,7 +4626,7 @@ void GridFunction::GetElementBoundsAtControlPoints(const int elem,
|
||||
fes->GetElementDofs(elem, dof_idx);
|
||||
int ndofs = dof_idx.Size();
|
||||
|
||||
int n_c_pts = static_cast<int>(std::pow(plb.GetNControlPoints(), rdim));
|
||||
int n_c_pts = std::pow(plb.GetNControlPoints(), rdim);
|
||||
lower.SetSize(n_c_pts*(vdim > 0 ? 1 : fes_dim));
|
||||
upper.SetSize(n_c_pts*(vdim > 0 ? 1 : fes_dim));
|
||||
|
||||
@@ -5119,13 +4658,13 @@ void GridFunction::GetElementBoundsAtControlPoints(const int elem,
|
||||
|
||||
void GridFunction::GetElementBounds(const int elem, const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim) const
|
||||
const int vdim)
|
||||
{
|
||||
Vector lowerC, upperC;
|
||||
GetElementBoundsAtControlPoints(elem, plb, lowerC, upperC, vdim);
|
||||
const FiniteElement *fe = fes->GetFE(elem);
|
||||
int rdim = fe->GetDim();
|
||||
int n_c_pts = static_cast<int>(std::pow(plb.GetNControlPoints(), rdim));
|
||||
int n_c_pts = std::pow(plb.GetNControlPoints(), rdim);
|
||||
int fes_dim = fes->GetVDim();
|
||||
lower.SetSize((vdim > 0 ? 1 :fes_dim));
|
||||
upper.SetSize((vdim > 0 ? 1 :fes_dim));
|
||||
@@ -5142,7 +4681,7 @@ void GridFunction::GetElementBounds(const int elem, const PLBound &plb,
|
||||
|
||||
void GridFunction::GetElementBounds(const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim) const
|
||||
const int vdim)
|
||||
{
|
||||
int nel = fes->GetNE();
|
||||
int fes_dim = fes->GetVDim();
|
||||
@@ -5165,7 +4704,7 @@ void GridFunction::GetElementBounds(const PLBound &plb,
|
||||
PLBound GridFunction::GetElementBounds(Vector &lower,
|
||||
Vector &upper,
|
||||
const int ref_factor,
|
||||
const int vdim) const
|
||||
const int vdim)
|
||||
{
|
||||
int max_order = fes->GetMaxElementOrder();
|
||||
PLBound plb(fes, ref_factor*(max_order+1));
|
||||
@@ -5174,7 +4713,7 @@ PLBound GridFunction::GetElementBounds(Vector &lower,
|
||||
}
|
||||
|
||||
PLBound GridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor, const int vdim) const
|
||||
const int ref_factor, const int vdim)
|
||||
{
|
||||
int max_order = fes->GetMaxElementOrder();
|
||||
PLBound plb(fes, ref_factor*(max_order+1));
|
||||
|
||||
+12
-76
@@ -27,24 +27,6 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** This enumerated type describes the three main projection types:
|
||||
- ELEMENT, assigns the degree of freedom per element, as specified in the
|
||||
specific element
|
||||
- GLOBAL_L2, solves a global L2 projection
|
||||
- ELEMENT_L2, solves a element level L2 projection. Inter element
|
||||
connectivity is dealt with similar as in:
|
||||
Bezier-Projection : A unified approach for local projection and
|
||||
quadrature-free refinement and coarsening of NURBS and T-splines with
|
||||
particular application to isogeometric design and analysis
|
||||
[CMAME (284) 2015 pg 55-105]
|
||||
- DEFAULT, for NURBS spaces this is ELEMENT_L2, while for all other spaces
|
||||
this ELEMENT.
|
||||
Note 1: ELEMENT_L2 also works for non NURBS elements
|
||||
Note 2: For NURBS elements the ELEMENT projection gives results without
|
||||
over and undershoots. However, the gradient near the boundary does not
|
||||
converge.*/
|
||||
enum class ProjectType { DEFAULT, ELEMENT, GLOBAL_L2, ELEMENT_L2 };
|
||||
|
||||
/// Class for grid function - Vector with associated FE space.
|
||||
class GridFunction : public Vector
|
||||
{
|
||||
@@ -84,17 +66,13 @@ protected:
|
||||
degree of freedom. */
|
||||
void ProjectDiscCoefficient(VectorCoefficient &coeff, Array<int> &dof_attr);
|
||||
|
||||
/** Helper function for ProjectCoefficientElementL2 */
|
||||
void ProjectCoefficientElementL2_(Coefficient &coeff, Vector &sol, Vector &Va);
|
||||
void ProjectCoefficientElementL2_(VectorCoefficient &vcoeff, Vector &sol,
|
||||
Vector &Va);
|
||||
|
||||
/// Loading helper.
|
||||
void LegacyNCReorder();
|
||||
|
||||
void Destroy();
|
||||
|
||||
public:
|
||||
|
||||
GridFunction() { fes = NULL; fec_owned = NULL; fes_sequence = 0; UseDevice(true); }
|
||||
|
||||
/// Copy constructor. The internal true-dof vector #t_vec is not copied.
|
||||
@@ -106,10 +84,6 @@ public:
|
||||
GridFunction(FiniteElementSpace *f) : Vector(f->GetVSize())
|
||||
{ fes = f; fec_owned = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
|
||||
|
||||
/// Same as above but specify the memory type
|
||||
GridFunction(FiniteElementSpace *f, MemoryType mt) : Vector(f->GetVSize(), mt)
|
||||
{ fes = f; fec_owned = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
|
||||
|
||||
/// Construct a GridFunction using previously allocated array @a data.
|
||||
/** The GridFunction does not assume ownership of @a data which is assumed to
|
||||
be of size at least `f->GetVSize()`. Similar to the Vector constructor
|
||||
@@ -446,30 +420,9 @@ public:
|
||||
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
|
||||
projection computation depends on the choice of the FiniteElementSpace
|
||||
#fes. Note that this is usually interpolation at the degrees of freedom
|
||||
in each element (not L2 projection). For elements without a projection
|
||||
member function one could use ProjectCoefficientGlobalL2 instead.
|
||||
NOTE: For parallel simulations with NURBS elements some dofs might
|
||||
not be defined, if the evaluation point does not reside on this rank.
|
||||
If that is the case it is defined on another rank, and the issue is
|
||||
rectified with the appropriate communication, see in ParGridFunction.
|
||||
*/
|
||||
virtual void ProjectCoefficient(Coefficient &coeff,
|
||||
ProjectType type = ProjectType::DEFAULT);
|
||||
|
||||
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
|
||||
projection is a global L2 projection. This routine can be used a
|
||||
fallback for elements without a projection member function.*/
|
||||
virtual void ProjectCoefficientGlobalL2(Coefficient &coeff,
|
||||
real_t rtol = 1e-12,
|
||||
int iter = 1000);
|
||||
|
||||
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
|
||||
projection is an element local L2 projection, with an appropriate
|
||||
weighting for Dofs that are shared between elements. Inspired on
|
||||
Bezier-Projection [CMAME (284) 2015 pg 55-105]
|
||||
This routine can be used a fallback for elements without a projection
|
||||
member function.*/
|
||||
virtual void ProjectCoefficientElementL2(Coefficient &coeff);
|
||||
in each element (not L2 projection). For NURBS spaces these degrees of
|
||||
freedom are not available and L2 projection is resorted to as fallback. */
|
||||
virtual void ProjectCoefficient(Coefficient &coeff);
|
||||
|
||||
/** @brief Project @a coeff Coefficient to @a this GridFunction, using one
|
||||
element for each degree of freedom in @a dofs and nodal interpolation on
|
||||
@@ -479,26 +432,9 @@ public:
|
||||
/** @brief Project @a vcoeff VectorCoefficient to @a this GridFunction. The
|
||||
projection computation depends on the choice of the FiniteElementSpace
|
||||
#fes. Note that this is usually interpolation at the degrees of freedom
|
||||
in each element (not L2 projection). For elements without a projection
|
||||
member function one could use ProjectCoefficientGlobalL2 instead.
|
||||
NOTE: For parallel simulations with NURBS elements some dofs might
|
||||
not be defined, if the evaluation point does not reside on this rank.
|
||||
If that is the case it is defined on another rank, and the issue is
|
||||
rectified with the appropriate communication, see in ParGridFunction.*/
|
||||
virtual void ProjectCoefficient(VectorCoefficient &vcoeff,
|
||||
ProjectType type = ProjectType::DEFAULT);
|
||||
|
||||
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
|
||||
projection is a global L2 projection. This routine can be used a
|
||||
fallback for elements without a projection member function.*/
|
||||
virtual void ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
|
||||
real_t rtol = 1e-12,
|
||||
int iter = 1000);
|
||||
|
||||
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
|
||||
projection is a global L2 projection. This routine can be used a
|
||||
fallback for elements without a projection member function.*/
|
||||
virtual void ProjectCoefficientElementL2(VectorCoefficient &vcoeff);
|
||||
in each element (not L2 projection). For NURBS spaces these degrees of
|
||||
freedom are not available and L2 projection is resorted to as fallback. */
|
||||
void ProjectCoefficient(VectorCoefficient &vcoeff);
|
||||
|
||||
/** @brief Project @a vcoeff VectorCoefficient to @a this GridFunction, using
|
||||
one element for each degree of freedom in @a dofs and nodal interpolation
|
||||
@@ -1668,7 +1604,7 @@ public:
|
||||
/// We compute the bounds for each vdim if @a vdim < 1.
|
||||
/// Note: For most cases, this method/interface will be sufficient.
|
||||
virtual PLBound GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) const;
|
||||
const int ref_factor=1, const int vdim=-1);
|
||||
|
||||
/// Computes the \ref PLBound for the gridfunction with number of control
|
||||
/// points based on @a ref_factor, and returns the bounds for each element
|
||||
@@ -1678,27 +1614,27 @@ public:
|
||||
/// PLBound object used to compute the bounds.
|
||||
/// We compute the bounds for each vdim if @a vdim < 1.
|
||||
PLBound GetElementBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) const;
|
||||
const int ref_factor=1, const int vdim=-1);
|
||||
|
||||
/// Compute piecewise linear bounds on the given element at the grid of
|
||||
/// [plb.ncp x plb.ncp x plb.ncp] control points for each of the vdim
|
||||
/// components of the gridfunction.
|
||||
void GetElementBoundsAtControlPoints(const int elem, const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim = -1) const;
|
||||
const int vdim = -1);
|
||||
|
||||
/// Compute bounds on the grid function for the given element.
|
||||
/// The bounds are stored in @b lower and @b upper.
|
||||
void GetElementBounds(const int elem, const PLBound &plb,
|
||||
Vector &lower, Vector &upper,
|
||||
const int vdim = -1) const;
|
||||
const int vdim = -1);
|
||||
|
||||
/// Compute bounds on the grid function for all the elements. The bounds
|
||||
/// are returned in @b lower and @b upper, ordered byVDim:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}
|
||||
void GetElementBounds(const PLBound &plb, Vector &lower, Vector &upper,
|
||||
const int vdim=-1) const;
|
||||
const int vdim=-1);
|
||||
///@}
|
||||
|
||||
/// Destroys grid function.
|
||||
|
||||
+32
-60
@@ -234,7 +234,7 @@ void FindPointsGSLIB::Setup(Mesh &m, const double bb_t, const double newt_tol,
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
const int point_pos_ordering)
|
||||
int point_pos_ordering)
|
||||
{
|
||||
MFEM_VERIFY(setupflag, "Use FindPointsGSLIB::Setup before finding points.");
|
||||
bool dev_mode = (point_pos.UseDevice() && Device::IsEnabled());
|
||||
@@ -324,7 +324,6 @@ void FindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
gsl_elem[i] = 0;
|
||||
for (int d = 0; d < dim; d++) { gsl_ref(i*dim + d) = -1.; }
|
||||
gsl_code[i] = 2;
|
||||
gsl_proc[i] = gsl_comm->id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,7 +481,7 @@ void FindPointsGSLIB::SetupDevice()
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
|
||||
const int point_pos_ordering)
|
||||
int point_pos_ordering)
|
||||
{
|
||||
if (!DEV.setup_device)
|
||||
{
|
||||
@@ -505,13 +504,13 @@ void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
FindPointsLocal2(point_pos, point_pos_ordering, gsl_code, gsl_elem,
|
||||
gsl_ref, gsl_dist, points_cnt);
|
||||
FindPointsLocal2(point_pos, point_pos_ordering, gsl_code, gsl_elem, gsl_ref,
|
||||
gsl_dist, points_cnt);
|
||||
}
|
||||
else
|
||||
{
|
||||
FindPointsLocal3(point_pos, point_pos_ordering, gsl_code, gsl_elem,
|
||||
gsl_ref, gsl_dist, points_cnt);
|
||||
FindPointsLocal3(point_pos, point_pos_ordering, gsl_code, gsl_elem, gsl_ref,
|
||||
gsl_dist, points_cnt);
|
||||
}
|
||||
|
||||
// Sync from device to host
|
||||
@@ -1085,7 +1084,7 @@ void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
|
||||
#else
|
||||
void FindPointsGSLIB::SetupDevice() {};
|
||||
void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
|
||||
const int point_pos_ordering) {};
|
||||
int point_pos_ordering) {};
|
||||
void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
|
||||
Vector &field_out,
|
||||
const int nel, const int ncomp,
|
||||
@@ -1094,8 +1093,7 @@ void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
|
||||
#endif
|
||||
|
||||
void FindPointsGSLIB::FindPoints(Mesh &m, const Vector &point_pos,
|
||||
const int point_pos_ordering,
|
||||
const double bb_t,
|
||||
int point_pos_ordering, const double bb_t,
|
||||
const double newt_tol, const int npt_max)
|
||||
{
|
||||
if (!setupflag || (mesh != &m) )
|
||||
@@ -1106,28 +1104,16 @@ void FindPointsGSLIB::FindPoints(Mesh &m, const Vector &point_pos,
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::Interpolate(const Vector &point_pos,
|
||||
const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int point_pos_ordering)
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
int point_pos_ordering)
|
||||
{
|
||||
FindPoints(point_pos, point_pos_ordering);
|
||||
Interpolate(field_in, field_out);
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::Interpolate(const Vector &point_pos,
|
||||
const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int point_pos_ordering,
|
||||
const int field_out_ordering)
|
||||
{
|
||||
FindPoints(point_pos, point_pos_ordering);
|
||||
Interpolate(field_in, field_out, field_out_ordering);
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::Interpolate(Mesh &m, const Vector &point_pos,
|
||||
const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int point_pos_ordering)
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
int point_pos_ordering)
|
||||
{
|
||||
FindPoints(m, point_pos, point_pos_ordering);
|
||||
Interpolate(field_in, field_out);
|
||||
@@ -1483,7 +1469,7 @@ void FindPointsGSLIB::SetupSplitMeshesAndIntegrationRules(const int order)
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::GetNodalValues(const GridFunction *gf_in,
|
||||
Vector &node_vals) const
|
||||
Vector &node_vals)
|
||||
{
|
||||
const GridFunction *nodes = gf_in;
|
||||
const FiniteElementSpace *fes = nodes->FESpace();
|
||||
@@ -1771,13 +1757,6 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
|
||||
|
||||
void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
Vector &field_out)
|
||||
{
|
||||
Interpolate(field_in, field_out, field_in.FESpace()->GetOrdering());
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int field_out_ordering)
|
||||
{
|
||||
const int gf_order = field_in.FESpace()->GetMaxElementOrder(),
|
||||
mesh_order = mesh->GetNodalFESpace()->GetMaxElementOrder();
|
||||
@@ -1820,7 +1799,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
const int maxOrder = field_in.FESpace()->GetMaxElementOrder();
|
||||
|
||||
InterpolateOnDevice(node_vals, field_out, NE_split_total, ncomp,
|
||||
maxOrder+1, field_out_ordering);
|
||||
maxOrder+1, field_in.FESpace()->GetOrdering());
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
@@ -1832,13 +1811,12 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
field_in.FESpace()->IsVariableOrder() ==
|
||||
mesh->GetNodalFESpace()->IsVariableOrder())
|
||||
{
|
||||
InterpolateH1(field_in, field_out, field_out_ordering);
|
||||
InterpolateH1(field_in, field_out);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
InterpolateGeneral(field_in, field_out,
|
||||
field_out_ordering);
|
||||
InterpolateGeneral(field_in, field_out);
|
||||
if (!fec_l2 || avgtype == AvgType::NONE) { return; }
|
||||
}
|
||||
|
||||
@@ -1882,11 +1860,11 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
|
||||
if (gf_order_h1 == mesh_order) // basis is GaussLobatto by default
|
||||
{
|
||||
InterpolateH1(field_in_h1, field_out_l2, field_out_ordering);
|
||||
InterpolateH1(field_in_h1, field_out_l2);
|
||||
}
|
||||
else
|
||||
{
|
||||
InterpolateGeneral(field_in_h1, field_out_l2, field_out_ordering);
|
||||
InterpolateGeneral(field_in_h1, field_out_l2);
|
||||
}
|
||||
|
||||
// Copy interpolated values for the points on element border
|
||||
@@ -1894,7 +1872,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
{
|
||||
for (int i = 0; i < indl2.Size(); i++)
|
||||
{
|
||||
int idx = field_out_ordering == Ordering::byNODES?
|
||||
int idx = field_in_h1.FESpace()->GetOrdering() == Ordering::byNODES?
|
||||
indl2[i] + j*points_cnt:
|
||||
indl2[i]*ncomp + j;
|
||||
field_out(idx) = field_out_l2(idx);
|
||||
@@ -1904,8 +1882,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int field_out_ordering)
|
||||
Vector &field_out)
|
||||
{
|
||||
FiniteElementSpace ind_fes(mesh, field_in.FESpace()->FEColl());
|
||||
if (field_in.FESpace()->IsVariableOrder())
|
||||
@@ -1935,8 +1912,7 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
|
||||
dataptrout = i*points_cnt;
|
||||
if (field_in.FESpace()->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
field_in_scalar.NewDataAndSize(field_in.GetData()+dataptrin,
|
||||
points_fld);
|
||||
field_in_scalar.NewDataAndSize(field_in.GetData()+dataptrin, points_fld);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1968,7 +1944,7 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
|
||||
(gslib::findpts_data_3 *)this->fdataD);
|
||||
}
|
||||
}
|
||||
if (field_out_ordering == Ordering::byVDIM)
|
||||
if (field_in.FESpace()->GetOrdering() == Ordering::byVDIM)
|
||||
{
|
||||
Vector field_out_temp = field_out;
|
||||
for (int i = 0; i < ncomp; i++)
|
||||
@@ -1982,8 +1958,7 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int field_out_ordering)
|
||||
Vector &field_out)
|
||||
{
|
||||
int ncomp = field_in.VectorDim(),
|
||||
nptorig = points_cnt,
|
||||
@@ -2003,7 +1978,7 @@ void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
|
||||
if (dim == 3) { ip.z = gsl_mfem_ref(index*dim + 2); }
|
||||
Vector localval(ncomp);
|
||||
field_in.GetVectorValue(gsl_mfem_elem[index], ip, localval);
|
||||
if (field_out_ordering == Ordering::byNODES)
|
||||
if (field_in.FESpace()->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
for (int i = 0; i < ncomp; i++)
|
||||
{
|
||||
@@ -2038,10 +2013,7 @@ void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
|
||||
for (int index = 0; index < npt; index++)
|
||||
{
|
||||
if (gsl_code[index] == 2) { continue; }
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
pt->r[d]= gsl_mfem_ref(index*dim + d);
|
||||
}
|
||||
for (int d = 0; d < dim; ++d) { pt->r[d]= gsl_mfem_ref(index*dim + d); }
|
||||
pt->index = index;
|
||||
pt->proc = gsl_proc[index];
|
||||
pt->el = gsl_mfem_elem[index];
|
||||
@@ -2131,7 +2103,7 @@ void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
|
||||
sdpt = (struct send_pt *)sendpt->ptr;
|
||||
for (int index = 0; index < static_cast<int>(sendpt->n); index++)
|
||||
{
|
||||
int idx = field_out_ordering == Ordering::byNODES ?
|
||||
int idx = field_in.FESpace()->GetOrdering() == Ordering::byNODES ?
|
||||
sdpt->index + j*nptorig :
|
||||
sdpt->index*ncomp + j;
|
||||
field_out(idx) = sdpt->ival;
|
||||
@@ -2273,7 +2245,7 @@ void FindPointsGSLIB::DistributeInterpolatedValues(const Vector &int_vals,
|
||||
}
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::GetAxisAlignedBoundingBoxes(Vector &aabb) const
|
||||
void FindPointsGSLIB::GetAxisAlignedBoundingBoxes(Vector &aabb)
|
||||
{
|
||||
MFEM_VERIFY(setupflag, "Call FindPointsGSLIB::Setup method first");
|
||||
auto *findptsData3 = (gslib::findpts_data_3 *)this->fdataD;
|
||||
@@ -2344,7 +2316,7 @@ void FindPointsGSLIB::GetAxisAlignedBoundingBoxes(Vector &aabb) const
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC,
|
||||
Vector &obbV) const
|
||||
Vector &obbV)
|
||||
{
|
||||
MFEM_VERIFY(setupflag, "Call FindPointsGSLIB::Setup method first");
|
||||
auto *findptsData3 = (gslib::findpts_data_3 *)this->fdataD;
|
||||
@@ -2529,8 +2501,8 @@ void OversetFindPointsGSLIB::Setup(Mesh &m, const int meshid,
|
||||
}
|
||||
|
||||
void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
const Array<unsigned int> &point_id,
|
||||
const int point_pos_ordering)
|
||||
Array<unsigned int> &point_id,
|
||||
int point_pos_ordering)
|
||||
{
|
||||
MFEM_VERIFY(setupflag, "Use OversetFindPointsGSLIB::Setup before "
|
||||
"finding points.");
|
||||
@@ -2609,10 +2581,10 @@ void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
}
|
||||
|
||||
void OversetFindPointsGSLIB::Interpolate(const Vector &point_pos,
|
||||
const Array<unsigned int> &point_id,
|
||||
Array<unsigned int> &point_id,
|
||||
const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int point_pos_ordering)
|
||||
int point_pos_ordering)
|
||||
{
|
||||
FindPoints(point_pos, point_id, point_pos_ordering);
|
||||
Interpolate(field_in, field_out);
|
||||
|
||||
+15
-32
@@ -119,13 +119,11 @@ protected:
|
||||
} DEV;
|
||||
|
||||
/// Use GSLIB for communication and interpolation
|
||||
virtual void InterpolateH1(const GridFunction &field_in, Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
virtual void InterpolateH1(const GridFunction &field_in, Vector &field_out);
|
||||
/// Uses GSLIB Crystal Router for communication followed by MFEM's
|
||||
/// interpolation functions
|
||||
virtual void InterpolateGeneral(const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
Vector &field_out);
|
||||
|
||||
/// Since GSLIB is designed to work with quads/hexes, we split every
|
||||
/// triangle/tet/prism/pyramid element into quads/hexes.
|
||||
@@ -142,7 +140,7 @@ protected:
|
||||
virtual void SetupSplitMeshesAndIntegrationRules(const int order);
|
||||
|
||||
/// Get GridFunction value at the points expected by GSLIB.
|
||||
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals) const;
|
||||
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals);
|
||||
|
||||
/// Map {r,s,t} coordinates from [-1,1] to [0,1] for MFEM. For simplices,
|
||||
/// find the original element number (that was split into micro quads/hexes)
|
||||
@@ -184,7 +182,7 @@ protected:
|
||||
These positions can be ordered byNodes: (XXX...,YYY...,ZZZ) or
|
||||
byVDim: (XYZ,XYZ,....XYZ) specified by @a point_pos_ordering. */
|
||||
void FindPointsOnDevice(const Vector &point_pos,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/** Interpolation of field values at prescribed reference space positions.
|
||||
@param[in] field_in_evec E-vector of grid function to be interpolated.
|
||||
@@ -255,15 +253,10 @@ public:
|
||||
#gsl_dist Distance between the sought and the found point
|
||||
in physical space. */
|
||||
void FindPoints(const Vector &point_pos,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
/// Convenience function when point positions are in a ParticleVector
|
||||
void FindPoints(const ParticleVector &point_pos)
|
||||
{
|
||||
FindPoints(point_pos, point_pos.GetOrdering());
|
||||
}
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
/// Setup FindPoints and search positions
|
||||
void FindPoints(Mesh &m, const Vector &point_pos,
|
||||
const int point_pos_ordering = Ordering::byNODES,
|
||||
int point_pos_ordering = Ordering::byNODES,
|
||||
const double bb_t = 0.1, const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
@@ -273,28 +266,20 @@ public:
|
||||
\p field_in is in H1 and in the same space as the
|
||||
mesh that was given to Setup().
|
||||
@param[out] field_out Interpolated values. For points that are not found
|
||||
the value is set to #default_interp_value.
|
||||
The output ordering is determined from field_in.*/
|
||||
the value is set to #default_interp_value. */
|
||||
virtual void Interpolate(const GridFunction &field_in, Vector &field_out);
|
||||
/// Interpolation of field values, with output ordering specification.
|
||||
virtual void Interpolate(const GridFunction &field_in, Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
/** Search positions and interpolate. The ordering (byNODES or byVDIM) of
|
||||
the output values in \p field_out corresponds to the ordering used
|
||||
in the input GridFunction \p field_in. */
|
||||
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
/// Search positions and interpolate with given point and output ordering.
|
||||
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
|
||||
Vector &field_out, const int point_pos_ordering,
|
||||
const int field_out_ordering);
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
/** Setup FindPoints, search positions and interpolate. The ordering (byNODES
|
||||
or byVDIM) of the output values in \p field_out corresponds to the
|
||||
ordering used in the input GridFunction \p field_in. */
|
||||
void Interpolate(Mesh &m, const Vector &point_pos,
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/// Average type to be used for L2 functions in-case a point is located at
|
||||
/// an element boundary where the function might be multi-valued.
|
||||
@@ -391,7 +376,7 @@ public:
|
||||
/// The size of the returned vector is (nel x nverts x dim), where nel is the
|
||||
/// number of elements (after splitting for simplcies), nverts is number of
|
||||
/// vertices (4 in 2D, 8 in 3D), and dim is the spatial dimension.
|
||||
void GetAxisAlignedBoundingBoxes(Vector &aabb) const;
|
||||
void GetAxisAlignedBoundingBoxes(Vector &aabb);
|
||||
|
||||
/// Return the oriented bounding boxes (OBB) computed during \ref Setup.
|
||||
/// Each OBB is represented using the inverse transformation (A^{-1}) and
|
||||
@@ -401,8 +386,7 @@ public:
|
||||
/// size (dim x dim x nel), and the OBB centers are returned in \p obbC,
|
||||
/// a vector of size (nel x dim). The vertices of the OBBs are returned in
|
||||
/// \p obbV, a vector of size (nel x nverts x dim) .
|
||||
void GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC,
|
||||
Vector &obbV) const;
|
||||
void GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC, Vector &obbV);
|
||||
};
|
||||
|
||||
/** \brief OversetFindPointsGSLIB enables use of findpts for arbitrary number of
|
||||
@@ -462,14 +446,13 @@ public:
|
||||
byNodes: (XXX...,YYY...,ZZZ) or
|
||||
byVDim: (XYZ,XYZ,....XYZ) */
|
||||
void FindPoints(const Vector &point_pos,
|
||||
const Array<unsigned int> &point_id,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
Array<unsigned int> &point_id,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/** Search positions and interpolate */
|
||||
void Interpolate(const Vector &point_pos,
|
||||
const Array<unsigned int> &point_id,
|
||||
void Interpolate(const Vector &point_pos, Array<unsigned int> &point_id,
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
using FindPointsGSLIB::Interpolate;
|
||||
};
|
||||
|
||||
|
||||
@@ -789,6 +789,7 @@ void Hybridization::ComputeH()
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: add ones on the diagonal of zero rows
|
||||
V->Finalize();
|
||||
Array<HYPRE_BigInt> V_J(V->NumNonZeroElems());
|
||||
MFEM_ASSERT(c_pfes, "");
|
||||
@@ -822,13 +823,6 @@ void Hybridization::ComputeH()
|
||||
MFEM_VERIFY(pH.Type() != Operator::PETSC_MATIS, "To be implemented");
|
||||
pH.MakePtAP(plpH, pP);
|
||||
delete lpH;
|
||||
|
||||
HypreParMatrix *hH = pH.As<HypreParMatrix>();
|
||||
MFEM_ASSERT(hH, "");
|
||||
|
||||
SparseMatrix H_diag;
|
||||
hH->GetDiag(H_diag);
|
||||
H_diag.SetDiagIdentity();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+273
-453
File diff suppressed because it is too large
Load Diff
@@ -14,11 +14,8 @@
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../general/array.hpp"
|
||||
#include "../linalg/operator.hpp"
|
||||
#include "../linalg/vector.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -48,30 +45,15 @@ protected:
|
||||
Array<int> hat_dof_gather_map;
|
||||
Array<DofType> hat_dof_marker;
|
||||
|
||||
Array<int> el_to_face; ///< Element to face connectivity.
|
||||
Array<int> el_face_offsets; ///< Per-element offsets into @a el_to_face.
|
||||
Array<int> face_to_el; ///< Face-to-element connectivity.
|
||||
Array<int> face_face_offsets; ///< Face-to-face offsets.
|
||||
|
||||
int n_el_face; ///< Total number of element-to-face connections.
|
||||
int n_face_face; ///< Total number of face-to-face connections.
|
||||
|
||||
Array<int> el_to_face;
|
||||
Array<int> face_to_el;
|
||||
Vector Ct_mat; ///< Constraint matrix (transposed) stored element-wise.
|
||||
|
||||
/// @name For parallel non-conforming meshes
|
||||
///@{
|
||||
std::unique_ptr<Operator> P_pc; ///< Partially conforming prolongation.
|
||||
std::unique_ptr<Operator> P_nbr; ///< Face-neighbor prolongation.
|
||||
///@}
|
||||
|
||||
Array<int> idofs, bdofs;
|
||||
|
||||
Vector Ahat, Ahat_ii, Ahat_ib, Ahat_bi, Ahat_bb;
|
||||
Array<int> Ahat_ii_piv, Ahat_bb_piv;
|
||||
|
||||
/// Return the (partially) conforming prolongation on the constraint space.
|
||||
const Operator &GetProlongation() const;
|
||||
|
||||
public:
|
||||
/// Construct the constraint matrix.
|
||||
void ConstructC();
|
||||
|
||||
@@ -25,9 +25,9 @@ static void PADGDiffusionSetup2D(const int Q1D, const int NE, const int NF,
|
||||
const GeometricFactors &el_geom,
|
||||
const FaceGeometricFactors &face_geom,
|
||||
const FaceNeighborGeometricFactors *nbr_geom,
|
||||
const Vector &q, const int coeff_dim,
|
||||
const real_t sigma, const real_t kappa,
|
||||
Vector &pa_data, const Array<int> &face_info_)
|
||||
const Vector &q, const real_t sigma,
|
||||
const real_t kappa, Vector &pa_data,
|
||||
const Array<int> &face_info_)
|
||||
{
|
||||
const auto J_loc = Reshape(el_geom.J.Read(), Q1D, Q1D, 2, 2, NE);
|
||||
const auto detJe_loc = Reshape(el_geom.detJ.Read(), Q1D, Q1D, NE);
|
||||
@@ -41,9 +41,9 @@ static void PADGDiffusionSetup2D(const int Q1D, const int NE, const int NF,
|
||||
const auto detJf = Reshape(face_geom.detJ.Read(), Q1D, NF);
|
||||
const auto n = Reshape(face_geom.normal.Read(), Q1D, 2, NF);
|
||||
|
||||
const bool const_q = (q.Size() == coeff_dim);
|
||||
const auto Q = const_q ? Reshape(q.Read(), coeff_dim, 1, 1)
|
||||
: Reshape(q.Read(), coeff_dim, Q1D, NF);
|
||||
const bool const_q = (q.Size() == 1);
|
||||
const auto Q =
|
||||
const_q ? Reshape(q.Read(), 1, 1) : Reshape(q.Read(), Q1D, NF);
|
||||
|
||||
const auto W = w.Read();
|
||||
|
||||
@@ -53,12 +53,6 @@ static void PADGDiffusionSetup2D(const int Q1D, const int NE, const int NF,
|
||||
// (q, 1/h, J0_0, J0_1, J1_0, J1_1)
|
||||
auto pa = Reshape(pa_data.Write(), 6, Q1D, NF);
|
||||
|
||||
auto get_coeff = [const_q] MFEM_HOST_DEVICE (const decltype(Q) &Q, int i,
|
||||
int qx, int e)
|
||||
{
|
||||
return const_q ? Q(i,0,0) : Q(i,qx,e);
|
||||
};
|
||||
|
||||
mfem::forall(NF, [=] MFEM_HOST_DEVICE(int f) -> void
|
||||
{
|
||||
const int normal_dir[] = {face_info(0, f), face_info(1, f)};
|
||||
@@ -77,26 +71,10 @@ static void PADGDiffusionSetup2D(const int Q1D, const int NE, const int NF,
|
||||
|
||||
for (int p = 0; p < Q1D; ++p)
|
||||
{
|
||||
real_t qh = 0.0;
|
||||
const real_t Qp = const_q ? Q(0, 0) : Q(p, f);
|
||||
pa(0, p, f) = kappa * Qp * W[p] * detJf(p, f);
|
||||
|
||||
real_t hi = 0.0;
|
||||
|
||||
real_t Qtn[2];
|
||||
if (coeff_dim > 1)
|
||||
{
|
||||
// matrix coefficient
|
||||
Qtn[0] = get_coeff(Q,0,p,f)*n(p,0,f) + get_coeff(Q,1,p,f)*n(p,1,f);
|
||||
Qtn[1] = get_coeff(Q,2,p,f)*n(p,0,f) + get_coeff(Q,3,p,f)*n(p,1,f);
|
||||
qh = Qtn[0]*n(p,0,f) + Qtn[1]*n(p,1,f);
|
||||
}
|
||||
else
|
||||
{
|
||||
qh = get_coeff(Q, 0, p, f);
|
||||
Qtn[0] = qh*n(p,0,f);
|
||||
Qtn[1] = qh*n(p,1,f);
|
||||
}
|
||||
|
||||
pa(0, p, f) = kappa * qh * W[p] * detJf(p, f);
|
||||
|
||||
for (int side = 0; side < nsides; ++side)
|
||||
{
|
||||
int i, j;
|
||||
@@ -111,13 +89,15 @@ static void PADGDiffusionSetup2D(const int Q1D, const int NE, const int NF,
|
||||
const auto &detJ = (side == 1 && shared) ? detJ_shared : detJe_loc;
|
||||
|
||||
real_t nJi[2];
|
||||
nJi[0] = Qtn[0]*J(i, j, 1, 1, e) - Qtn[1]*J(i, j, 0, 1, e);
|
||||
nJi[1] = -Qtn[0]*J(i, j, 1, 0, e) + Qtn[1]*J(i, j, 0, 0, e);
|
||||
nJi[0] =
|
||||
n(p, 0, f) * J(i, j, 1, 1, e) - n(p, 1, f) * J(i, j, 0, 1, e);
|
||||
nJi[1] =
|
||||
-n(p, 0, f) * J(i, j, 1, 0, e) + n(p, 1, f) * J(i, j, 0, 0, e);
|
||||
|
||||
const real_t dJe = detJ(i, j, e);
|
||||
const real_t dJf = detJf(p, f);
|
||||
|
||||
const real_t w = factor * W[p] * dJf / dJe;
|
||||
const real_t w = factor * Qp * W[p] * dJf / dJe;
|
||||
|
||||
const int ni = normal_dir[side];
|
||||
const int ti = 1 - ni;
|
||||
@@ -146,9 +126,9 @@ static void PADGDiffusionSetup3D(const int Q1D, const int NE, const int NF,
|
||||
const GeometricFactors &el_geom,
|
||||
const FaceGeometricFactors &face_geom,
|
||||
const FaceNeighborGeometricFactors *nbr_geom,
|
||||
const Vector &q, const int coeff_dim,
|
||||
const real_t sigma, const real_t kappa,
|
||||
Vector &pa_data, const Array<int> &face_info_)
|
||||
const Vector &q, const real_t sigma,
|
||||
const real_t kappa, Vector &pa_data,
|
||||
const Array<int> &face_info_)
|
||||
{
|
||||
const auto J_loc = Reshape(el_geom.J.Read(), Q1D, Q1D, Q1D, 3, 3, NE);
|
||||
const auto detJe_loc = Reshape(el_geom.detJ.Read(), Q1D, Q1D, Q1D, NE);
|
||||
@@ -162,9 +142,9 @@ static void PADGDiffusionSetup3D(const int Q1D, const int NE, const int NF,
|
||||
const auto detJf = Reshape(face_geom.detJ.Read(), Q1D, Q1D, NF);
|
||||
const auto n = Reshape(face_geom.normal.Read(), Q1D, Q1D, 3, NF);
|
||||
|
||||
const bool const_q = (q.Size() == coeff_dim);
|
||||
const auto Q = const_q ? Reshape(q.Read(), coeff_dim, 1, 1, 1)
|
||||
: Reshape(q.Read(), coeff_dim, Q1D, Q1D, NF);
|
||||
const bool const_q = (q.Size() == 1);
|
||||
const auto Q =
|
||||
const_q ? Reshape(q.Read(), 1, 1, 1) : Reshape(q.Read(), Q1D, Q1D, NF);
|
||||
|
||||
const auto W = Reshape(w.Read(), Q1D, Q1D);
|
||||
|
||||
@@ -177,12 +157,6 @@ static void PADGDiffusionSetup3D(const int Q1D, const int NE, const int NF,
|
||||
// (J00, J01, J02, J10, J11, J12, q/h)
|
||||
const auto pa = Reshape(pa_data.Write(), 7, Q1D, Q1D, NF);
|
||||
|
||||
auto get_coeff = [const_q] MFEM_HOST_DEVICE (const decltype(Q) &Q, int i,
|
||||
int qx, int qy, int e)
|
||||
{
|
||||
return const_q ? Q(i,0,0,0) : Q(i,qx,qy,e);
|
||||
};
|
||||
|
||||
mfem::forall_2D(NF, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int f) -> void
|
||||
{
|
||||
MFEM_SHARED int perm[2][3];
|
||||
@@ -218,32 +192,11 @@ static void PADGDiffusionSetup3D(const int Q1D, const int NE, const int NF,
|
||||
{
|
||||
MFEM_FOREACH_THREAD(p2, y, Q1D)
|
||||
{
|
||||
const real_t Qp = const_q ? Q(0, 0, 0) : Q(p1, p2, f);
|
||||
const real_t dJf = detJf(p1, p2, f);
|
||||
|
||||
real_t hi = 0.0;
|
||||
|
||||
real_t Qtn[3];
|
||||
real_t qh = 0.0;
|
||||
|
||||
if (coeff_dim > 1)
|
||||
{
|
||||
// matrix coefficient
|
||||
for (int d = 0; d < 3; ++d)
|
||||
{
|
||||
Qtn[d] = get_coeff(Q,0+3*d,p1,p2,f)*n(p1,p2,0,f)
|
||||
+ get_coeff(Q,1+3*d,p1,p2,f)*n(p1,p2,1,f)
|
||||
+ get_coeff(Q,2+3*d,p1,p2,f)*n(p1,p2,2,f);
|
||||
qh += Qtn[d] * n(p1,p2,d,f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qh = get_coeff(Q,0,p1,p2,f);
|
||||
Qtn[0] = qh * n(p1,p2,0,f);
|
||||
Qtn[1] = qh * n(p1,p2,1,f);
|
||||
Qtn[2] = qh * n(p1,p2,2,f);
|
||||
}
|
||||
|
||||
for (int side = 0; side < nsides; ++side)
|
||||
{
|
||||
int i, j, k;
|
||||
@@ -257,29 +210,38 @@ static void PADGDiffusionSetup3D(const int Q1D, const int NE, const int NF,
|
||||
// *INDENT-OFF*
|
||||
real_t nJi[3];
|
||||
nJi[0] = (-J(i, j, k, 1, 2, e) * J(i, j, k, 2, 1, e) +
|
||||
J(i, j, k, 1, 1, e) * J(i, j, k, 2, 2, e)) * Qtn[0] +
|
||||
(J(i, j, k, 0, 2, e) * J(i, j, k, 2, 1, e) -
|
||||
J(i, j, k, 0, 1, e) * J(i, j, k, 2, 2, e)) * Qtn[1] +
|
||||
(-J(i, j, k, 0, 2, e) * J(i, j, k, 1, 1, e) +
|
||||
J(i, j, k, 0, 1, e) * J(i, j, k, 1, 2, e)) * Qtn[2];
|
||||
J(i, j, k, 1, 1, e) * J(i, j, k, 2, 2, e)) *
|
||||
n(p1, p2, 0, f) +
|
||||
(J(i, j, k, 0, 2, e) * J(i, j, k, 2, 1, e) -
|
||||
J(i, j, k, 0, 1, e) * J(i, j, k, 2, 2, e)) *
|
||||
n(p1, p2, 1, f) +
|
||||
(-J(i, j, k, 0, 2, e) * J(i, j, k, 1, 1, e) +
|
||||
J(i, j, k, 0, 1, e) * J(i, j, k, 1, 2, e)) *
|
||||
n(p1, p2, 2, f);
|
||||
|
||||
nJi[1] = (J(i, j, k, 1, 2, e) * J(i, j, k, 2, 0, e) -
|
||||
J(i, j, k, 1, 0, e) * J(i, j, k, 2, 2, e)) * Qtn[0] +
|
||||
(-J(i, j, k, 0, 2, e) * J(i, j, k, 2, 0, e) +
|
||||
J(i, j, k, 0, 0, e) * J(i, j, k, 2, 2, e)) * Qtn[1] +
|
||||
(J(i, j, k, 0, 2, e) * J(i, j, k, 1, 0, e) -
|
||||
J(i, j, k, 0, 0, e) * J(i, j, k, 1, 2, e)) * Qtn[2];
|
||||
J(i, j, k, 1, 0, e) * J(i, j, k, 2, 2, e)) *
|
||||
n(p1, p2, 0, f) +
|
||||
(-J(i, j, k, 0, 2, e) * J(i, j, k, 2, 0, e) +
|
||||
J(i, j, k, 0, 0, e) * J(i, j, k, 2, 2, e)) *
|
||||
n(p1, p2, 1, f) +
|
||||
(J(i, j, k, 0, 2, e) * J(i, j, k, 1, 0, e) -
|
||||
J(i, j, k, 0, 0, e) * J(i, j, k, 1, 2, e)) *
|
||||
n(p1, p2, 2, f);
|
||||
|
||||
nJi[2] = (-J(i, j, k, 1, 1, e) * J(i, j, k, 2, 0, e) +
|
||||
J(i, j, k, 1, 0, e) * J(i, j, k, 2, 1, e)) * Qtn[0] +
|
||||
(J(i, j, k, 0, 1, e) * J(i, j, k, 2, 0, e) -
|
||||
J(i, j, k, 0, 0, e) * J(i, j, k, 2, 1, e)) * Qtn[1] +
|
||||
(-J(i, j, k, 0, 1, e) * J(i, j, k, 1, 0, e) +
|
||||
J(i, j, k, 0, 0, e) * J(i, j, k, 1, 1, e)) * Qtn[2];
|
||||
J(i, j, k, 1, 0, e) * J(i, j, k, 2, 1, e)) *
|
||||
n(p1, p2, 0, f) +
|
||||
(J(i, j, k, 0, 1, e) * J(i, j, k, 2, 0, e) -
|
||||
J(i, j, k, 0, 0, e) * J(i, j, k, 2, 1, e)) *
|
||||
n(p1, p2, 1, f) +
|
||||
(-J(i, j, k, 0, 1, e) * J(i, j, k, 1, 0, e) +
|
||||
J(i, j, k, 0, 0, e) * J(i, j, k, 1, 1, e)) *
|
||||
n(p1, p2, 2, f);
|
||||
// *INDENT-ON*
|
||||
|
||||
const real_t dJe = detJe(i, j, k, e);
|
||||
const real_t val = factor * W(p1, p2) * dJf / dJe;
|
||||
const real_t val = factor * Qp * W(p1, p2) * dJf / dJe;
|
||||
|
||||
for (int d = 0; d < 3; ++d)
|
||||
{
|
||||
@@ -298,7 +260,7 @@ static void PADGDiffusionSetup3D(const int Q1D, const int NE, const int NF,
|
||||
pa(5, p1, p2, f) = 0.0;
|
||||
}
|
||||
|
||||
pa(6, p1, p2, f) = kappa * hi * qh * W(p1, p2) * dJf;
|
||||
pa(6, p1, p2, f) = kappa * hi * Qp * W(p1, p2) * dJf;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -540,12 +502,19 @@ void DGDiffusionIntegrator::SetupPA(const FiniteElementSpace &fes,
|
||||
|
||||
// Evaluate the coefficient at the face quadrature points.
|
||||
FaceQuadratureSpace fqs(mesh, ir, type);
|
||||
CoefficientVector q(fqs, CoefficientStorage::CONSTANTS);
|
||||
if (Q) { q.Project(*Q); }
|
||||
else if (MQ) { q.Project(*MQ); }
|
||||
else { q.SetConstant(1.0); }
|
||||
|
||||
const int coeff_dim = q.GetVDim();
|
||||
CoefficientVector q(fqs, CoefficientStorage::COMPRESSED);
|
||||
if (Q)
|
||||
{
|
||||
q.Project(*Q);
|
||||
}
|
||||
else if (MQ)
|
||||
{
|
||||
MFEM_ABORT("Not yet implemented"); /* q.Project(*MQ); */
|
||||
}
|
||||
else
|
||||
{
|
||||
q.SetConstant(1.0);
|
||||
}
|
||||
|
||||
Array<int> face_info;
|
||||
if (dim == 1)
|
||||
@@ -556,15 +525,15 @@ void DGDiffusionIntegrator::SetupPA(const FiniteElementSpace &fes,
|
||||
{
|
||||
PADGDiffusionSetupFaceInfo2D(nf, mesh, type, face_info);
|
||||
PADGDiffusionSetup2D(quad1D, ne, nf, ir.GetWeights(), *el_geom,
|
||||
*face_geom, nbr_geom.get(), q, coeff_dim, sigma,
|
||||
kappa, pa_data, face_info);
|
||||
*face_geom, nbr_geom.get(), q, sigma, kappa, pa_data,
|
||||
face_info);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
PADGDiffusionSetupFaceInfo3D(nf, mesh, type, face_info);
|
||||
PADGDiffusionSetup3D(quad1D, ne, nf, ir.GetWeights(), *el_geom,
|
||||
*face_geom, nbr_geom.get(), q, coeff_dim, sigma,
|
||||
kappa, pa_data, face_info);
|
||||
*face_geom, nbr_geom.get(), q, sigma, kappa, pa_data,
|
||||
face_info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -134,19 +134,14 @@ void PADiffusionSetup2D<2>(const int Q1D,
|
||||
Vector &d)
|
||||
{
|
||||
const bool symmetric = (coeffDim != 4);
|
||||
const bool const_c = c.Size() == coeffDim;
|
||||
const bool const_c = c.Size() == 1;
|
||||
MFEM_VERIFY(coeffDim < 3 ||
|
||||
!const_c, "Constant matrix coefficient not supported");
|
||||
const auto W = Reshape(w.Read(), Q1D,Q1D);
|
||||
const auto J = Reshape(j.Read(), Q1D,Q1D,2,2,NE);
|
||||
const auto C = const_c ? Reshape(c.Read(), coeffDim,1,1,1) :
|
||||
const auto C = const_c ? Reshape(c.Read(), 1,1,1,1) :
|
||||
Reshape(c.Read(), coeffDim,Q1D,Q1D,NE);
|
||||
auto D = Reshape(d.Write(), Q1D,Q1D, symmetric ? 3 : 4, NE);
|
||||
|
||||
auto get_coeff = [const_c] MFEM_HOST_DEVICE
|
||||
(const decltype(C) &C, int i, int qx, int qy, int e)
|
||||
{
|
||||
return const_c ? C(i,0,0,0) : C(i,qx,qy,e);
|
||||
};
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
@@ -161,11 +156,10 @@ void PADiffusionSetup2D<2>(const int Q1D,
|
||||
if (coeffDim == 3 || coeffDim == 4) // Matrix coefficient
|
||||
{
|
||||
// First compute entries of R = MJ^{-T}, without det J factor.
|
||||
const real_t M11 = get_coeff(C,0,qx,qy,e);
|
||||
const real_t M12 = get_coeff(C,1,qx,qy,e);
|
||||
const real_t M21 = symmetric ? M12 : get_coeff(C,2,qx,qy,e);
|
||||
const real_t M22 = symmetric ? get_coeff(C,2,qx,qy,e)
|
||||
: get_coeff(C,3,qx,qy,e);
|
||||
const real_t M11 = C(0,qx,qy,e);
|
||||
const real_t M12 = C(1,qx,qy,e);
|
||||
const real_t M21 = symmetric ? M12 : C(2,qx,qy,e);
|
||||
const real_t M22 = symmetric ? C(2,qx,qy,e) : C(3,qx,qy,e);
|
||||
const real_t R11 = M11*J22 - M12*J12;
|
||||
const real_t R21 = M21*J22 - M22*J12;
|
||||
const real_t R12 = -M11*J21 + M12*J11;
|
||||
@@ -183,8 +177,9 @@ void PADiffusionSetup2D<2>(const int Q1D,
|
||||
}
|
||||
else // Vector or scalar coefficient
|
||||
{
|
||||
const real_t C1 = get_coeff(C,0,qx,qy,e);
|
||||
const real_t C2 = get_coeff(C,coeffDim==2?1:0,qx,qy,e);
|
||||
const real_t C1 = const_c ? C(0,0,0,0) : C(0,qx,qy,e);
|
||||
const real_t C2 = const_c ? C(0,0,0,0) :
|
||||
(coeffDim == 2 ? C(1,qx,qy,e) : C(0,qx,qy,e));
|
||||
|
||||
D(qx,qy,0,e) = w_detJ * (C2*J12*J12 + C1*J22*J22); // 1,1
|
||||
D(qx,qy,1,e) = -w_detJ * (C2*J12*J11 + C1*J22*J21); // 1,2
|
||||
@@ -249,19 +244,14 @@ void PADiffusionSetup3D(const int Q1D,
|
||||
Vector &d)
|
||||
{
|
||||
const bool symmetric = (coeffDim != 9);
|
||||
const bool const_c = c.Size() == coeffDim;
|
||||
const bool const_c = c.Size() == 1;
|
||||
MFEM_VERIFY(coeffDim < 6 ||
|
||||
!const_c, "Constant matrix coefficient not supported");
|
||||
const auto W = Reshape(w.Read(), Q1D,Q1D,Q1D);
|
||||
const auto J = Reshape(j.Read(), Q1D,Q1D,Q1D,3,3,NE);
|
||||
const auto C = const_c ? Reshape(c.Read(), coeffDim,1,1,1,1) :
|
||||
const auto C = const_c ? Reshape(c.Read(), 1,1,1,1,1) :
|
||||
Reshape(c.Read(), coeffDim,Q1D,Q1D,Q1D,NE);
|
||||
auto D = Reshape(d.Write(), Q1D,Q1D,Q1D, symmetric ? 6 : 9, NE);
|
||||
|
||||
auto get_coeff = [const_c] MFEM_HOST_DEVICE
|
||||
(const decltype(C) &C, int i, int qx, int qy, int qz, int e)
|
||||
{
|
||||
return const_c ? C(i,0,0,0,0) : C(i,qx,qy,qz,e);
|
||||
};
|
||||
|
||||
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
@@ -297,18 +287,15 @@ void PADiffusionSetup3D(const int Q1D,
|
||||
if (coeffDim == 6 || coeffDim == 9) // Matrix coefficient version
|
||||
{
|
||||
// Compute entries of R = MJ^{-T} = M adj(J)^T, without det J.
|
||||
const real_t M11 = get_coeff(C, 0, qx,qy,qz, e);
|
||||
const real_t M12 = get_coeff(C, 1, qx,qy,qz, e);
|
||||
const real_t M13 = get_coeff(C, 2, qx,qy,qz, e);
|
||||
const real_t M21 = (!symmetric) ? get_coeff(C, 3, qx,qy,qz, e) : M12;
|
||||
const real_t M22 = (!symmetric) ? get_coeff(C, 4, qx,qy,qz, e)
|
||||
: get_coeff(C, 3, qx,qy,qz, e);
|
||||
const real_t M23 = (!symmetric) ? get_coeff(C, 5, qx,qy,qz, e)
|
||||
: get_coeff(C, 4, qx,qy,qz, e);
|
||||
const real_t M31 = (!symmetric) ? get_coeff(C, 6, qx,qy,qz, e) : M13;
|
||||
const real_t M32 = (!symmetric) ? get_coeff(C, 7, qx,qy,qz, e) : M23;
|
||||
const real_t M33 = (!symmetric) ? get_coeff(C, 8, qx,qy,qz, e)
|
||||
: get_coeff(C, 5, qx,qy,qz, e);
|
||||
const real_t M11 = C(0, qx,qy,qz, e);
|
||||
const real_t M12 = C(1, qx,qy,qz, e);
|
||||
const real_t M13 = C(2, qx,qy,qz, e);
|
||||
const real_t M21 = (!symmetric) ? C(3, qx,qy,qz, e) : M12;
|
||||
const real_t M22 = (!symmetric) ? C(4, qx,qy,qz, e) : C(3, qx,qy,qz, e);
|
||||
const real_t M23 = (!symmetric) ? C(5, qx,qy,qz, e) : C(4, qx,qy,qz, e);
|
||||
const real_t M31 = (!symmetric) ? C(6, qx,qy,qz, e) : M13;
|
||||
const real_t M32 = (!symmetric) ? C(7, qx,qy,qz, e) : M23;
|
||||
const real_t M33 = (!symmetric) ? C(8, qx,qy,qz, e) : C(5, qx,qy,qz, e);
|
||||
|
||||
const real_t R11 = M11*A11 + M12*A12 + M13*A13;
|
||||
const real_t R12 = M11*A21 + M12*A22 + M13*A23;
|
||||
@@ -348,9 +335,11 @@ void PADiffusionSetup3D(const int Q1D,
|
||||
}
|
||||
else // Vector or scalar coefficient version
|
||||
{
|
||||
const real_t C1 = get_coeff(C,0,qx,qy,qz,e);
|
||||
const real_t C2 = get_coeff(C,coeffDim==3?1:0,qx,qy,qz,e);
|
||||
const real_t C3 = get_coeff(C,coeffDim==3?2:0,qx,qy,qz,e);
|
||||
const real_t C1 = const_c ? C(0,0,0,0,0) : C(0,qx,qy,qz,e);
|
||||
const real_t C2 = const_c ? C(0,0,0,0,0) :
|
||||
(coeffDim == 3 ? C(1,qx,qy,qz,e) : C(0,qx,qy,qz,e));
|
||||
const real_t C3 = const_c ? C(0,0,0,0,0) :
|
||||
(coeffDim == 3 ? C(2,qx,qy,qz,e) : C(0,qx,qy,qz,e));
|
||||
|
||||
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
|
||||
D(qx,qy,qz,0,e) = w_detJ * (C1*A11*A11 + C2*A12*A12 + C3*A13*A13); // 1,1
|
||||
|
||||
@@ -201,7 +201,7 @@ inline void SmemPADiffusionDiagonal2D(const int NE,
|
||||
MFEM_SHARED real_t BG[2][MQ1*MD1];
|
||||
real_t (*B)[MD1] = (real_t (*)[MD1]) (BG+0);
|
||||
real_t (*G)[MD1] = (real_t (*)[MD1]) (BG+1);
|
||||
MFEM_SHARED real_t QD[3][NBZ][MQ1][MD1];
|
||||
MFEM_SHARED real_t QD[3][NBZ][MD1][MQ1];
|
||||
real_t (*QD0)[MD1] = (real_t (*)[MD1])(QD[0] + tidz);
|
||||
real_t (*QD1)[MD1] = (real_t (*)[MD1])(QD[1] + tidz);
|
||||
real_t (*QD2)[MD1] = (real_t (*)[MD1])(QD[2] + tidz);
|
||||
@@ -1004,16 +1004,13 @@ inline void SmemPADiffusionApply3D(const int NE,
|
||||
const int max_d1d = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D;
|
||||
MFEM_VERIFY(D1D <= max_d1d, "");
|
||||
MFEM_VERIFY(Q1D <= max_q1d, "");
|
||||
const auto b = Reshape(b_.Read(), Q1D, D1D);
|
||||
const auto g = Reshape(g_.Read(), Q1D, D1D);
|
||||
const auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
|
||||
const auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
|
||||
auto b = Reshape(b_.Read(), Q1D, D1D);
|
||||
auto g = Reshape(g_.Read(), Q1D, D1D);
|
||||
auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
|
||||
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
|
||||
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
|
||||
MFEM_VERIFY(D1D <= Q1D, "THREAD_DIRECT requires D1D <= Q1D");
|
||||
|
||||
mfem::forall_3D<T_Q1D*T_Q1D*T_Q1D>(NE,
|
||||
Q1D, Q1D, Q1D,
|
||||
[=] MFEM_HOST_DEVICE (int e)
|
||||
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
@@ -1133,11 +1133,11 @@ inline void SmemPAMassApply3D(const int NE,
|
||||
const int max_d1d = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D;
|
||||
MFEM_VERIFY(D1D <= max_d1d, "");
|
||||
MFEM_VERIFY(Q1D <= max_q1d, "");
|
||||
const auto b = b_.Read();
|
||||
const auto d = d_.Read();
|
||||
const auto x = x_.Read();
|
||||
auto b = b_.Read();
|
||||
auto d = d_.Read();
|
||||
auto x = x_.Read();
|
||||
auto y = y_.ReadWrite();
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
internal::SmemPAMassApply3D_Element<T_D1D,T_Q1D>(e, NE, b, d, x, y, d1d, q1d);
|
||||
});
|
||||
@@ -1156,8 +1156,8 @@ inline void EAMassAssemble1D(const int NE,
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
const auto B = Reshape(basis.Read(), Q1D, D1D);
|
||||
const auto D = Reshape(padata.Read(), Q1D, NE);
|
||||
auto B = Reshape(basis.Read(), Q1D, D1D);
|
||||
auto D = Reshape(padata.Read(), Q1D, NE);
|
||||
auto M = Reshape(add ? eadata.ReadWrite() : eadata.Write(), D1D, D1D, NE);
|
||||
mfem::forall_2D(NE, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
const FaceType ftype = FaceType::Interior;
|
||||
const int nf = mesh.GetNFbyType(ftype);
|
||||
|
||||
const Geometry::Type geom = mesh.GetTypicalFaceGeometry();
|
||||
const Geometry::Type geom = mesh.GetFaceGeometry(0);
|
||||
const int trial_order = trial_fes.GetMaxElementOrder();
|
||||
const int test_order = test_fes.GetMaxElementOrder();
|
||||
const int qorder = test_order + trial_order - 1;
|
||||
@@ -47,7 +47,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
});
|
||||
}
|
||||
|
||||
const FiniteElement &trial_face_el = *trial_fes.GetTypicalTraceElement();
|
||||
const FiniteElement &trial_face_el = *trial_fes.GetFaceElement(0);
|
||||
const auto maps = &trial_face_el.GetDofToQuad(ir, DofToQuad::TENSOR);
|
||||
const int ndof_face = trial_face_el.GetDof();
|
||||
|
||||
@@ -72,7 +72,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
MFEM_ABORT("Unknown kernel.");
|
||||
}
|
||||
|
||||
const FiniteElement &test_el = *test_fes.GetTypicalFE();
|
||||
const FiniteElement &test_el = *test_fes.GetFE(0);
|
||||
const int n_faces_per_el = 2*dim; // assuming tensor product
|
||||
// Get all the local face maps (mapping from lexicographic face index to
|
||||
// lexicographic volume index, depending on the local face index).
|
||||
@@ -90,10 +90,10 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
Array<int> face_info(nf * 4);
|
||||
{
|
||||
int fidx = 0;
|
||||
for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
|
||||
for (int f = 0; f < mesh.GetNumFaces(); ++f)
|
||||
{
|
||||
Mesh::FaceInformation finfo = mesh.GetFaceInformation(f);
|
||||
if (!finfo.IsInterior() || finfo.IsNonconformingCoarse()) { continue; }
|
||||
if (!finfo.IsInterior()) { continue; }
|
||||
face_info[0 + fidx*4] = finfo.element[0].local_face_id;
|
||||
face_info[1 + fidx*4] = finfo.element[0].orientation;
|
||||
face_info[2 + fidx*4] = finfo.element[1].local_face_id;
|
||||
@@ -114,7 +114,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
else
|
||||
{
|
||||
d_emat = emat.Write();
|
||||
emat = 0.0; // Will execute on device, since Write() sets the device flag
|
||||
mfem::forall(emat.Size(), [=] MFEM_HOST_DEVICE (int i) { d_emat[i] = 0.0; });
|
||||
}
|
||||
|
||||
const auto face_mats = Reshape(mass_emat.Read(), ndof_face, ndof_face, nf);
|
||||
@@ -133,104 +133,26 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
}
|
||||
};
|
||||
|
||||
auto permute_face_2 = [=] MFEM_HOST_DEVICE(int local_face_1, int local_face_2,
|
||||
int orient, int size1d, int index)
|
||||
mfem::forall_3D(nf, ndof_face, ndof_face, 2, [=] MFEM_HOST_DEVICE (int f)
|
||||
{
|
||||
if (dim == 2)
|
||||
MFEM_FOREACH_THREAD(el_i, z, 2)
|
||||
{
|
||||
return internal::PermuteFace2D(local_face_1, local_face_2, orient,
|
||||
size1d, index);
|
||||
}
|
||||
else // dim == 3
|
||||
{
|
||||
return internal::PermuteFace3D(local_face_1, local_face_2, orient,
|
||||
size1d, index);
|
||||
}
|
||||
};
|
||||
|
||||
if (mesh.Conforming())
|
||||
{
|
||||
mfem::forall_3D(nf, ndof_face, ndof_face, 2, [=] MFEM_HOST_DEVICE (int f)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(el_i, z, 2)
|
||||
const int lf_i = d_face_info(0, el_i, f);
|
||||
const int orient = d_face_info(1, el_i, f);
|
||||
// Loop over face indices in "native ordering"
|
||||
MFEM_FOREACH_THREAD(i_lex, x, ndof_face)
|
||||
{
|
||||
const int lf_i = d_face_info(0, el_i, f);
|
||||
const int orient = d_face_info(1, el_i, f);
|
||||
// Loop over face indices in "native ordering"
|
||||
MFEM_FOREACH_THREAD(i_lex, x, ndof_face)
|
||||
// Convert to lexicographic relative to the face itself
|
||||
const int i_face = permute_face(lf_i, orient, d1d, i_lex);
|
||||
// Convert from lexicographic face DOF to volume DOF
|
||||
const int i = d_face_maps(i_lex, lf_i);
|
||||
MFEM_FOREACH_THREAD(j, y, ndof_face)
|
||||
{
|
||||
// Convert to lexicographic relative to the face itself
|
||||
const int i_face = permute_face(lf_i, orient, d1d, i_lex);
|
||||
// Convert from lexicographic face DOF to volume DOF
|
||||
const int i = d_face_maps(i_lex, lf_i);
|
||||
MFEM_FOREACH_THREAD(j, y, ndof_face)
|
||||
{
|
||||
el_mats(i, j, el_i, f) += face_mats(i_face, j, f);
|
||||
}
|
||||
el_mats(i, j, el_i, f) += face_mats(i_face, j, f);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
const InterpolationManager &interp =
|
||||
test_fes.GetInterpolationManager(ElementDofOrdering::LEXICOGRAPHIC, ftype);
|
||||
|
||||
auto interp_configs = interp.GetFaceInterpConfig().Read();
|
||||
const int nc_size = interp.GetNumInterpolators();
|
||||
auto d_interp = Reshape(interp.GetInterpolators().Read(),
|
||||
ndof_face, ndof_face, nc_size);
|
||||
|
||||
mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
|
||||
{
|
||||
const InterpConfig conf = interp_configs[f];
|
||||
const int master_side = conf.master_side;
|
||||
const int interp_index = conf.index;
|
||||
|
||||
const int lf_0 = d_face_info(0, 0, f);
|
||||
|
||||
for (int el_i = 0; el_i < 2; ++el_i)
|
||||
{
|
||||
const int lf_i = d_face_info(0, el_i, f);
|
||||
const int orient = d_face_info(1, el_i, f);
|
||||
|
||||
for (int j = 0; j < ndof_face; j++)
|
||||
{
|
||||
for (int i_lex = 0; i_lex < ndof_face; i_lex++)
|
||||
{
|
||||
real_t val = 0.0;
|
||||
if (conf.is_non_conforming && el_i == master_side)
|
||||
{
|
||||
// Interpolate from el_i (coarse element) to the fine face.
|
||||
// The mapping is given by d_interp, which uses indices
|
||||
// relative to element 0.
|
||||
|
||||
// i0 is lexicographic relative to element 0
|
||||
const int i0 = permute_face_2(lf_i, lf_0, orient, d1d, i_lex);
|
||||
|
||||
// k0 is lexicographic relative to element 0
|
||||
for (int k0 = 0; k0 < ndof_face; k0++)
|
||||
{
|
||||
// k is relative to the face itself
|
||||
const int k = permute_face(lf_0, orient, d1d, k0);
|
||||
val += d_interp(k0, i0, interp_index)
|
||||
* face_mats(k, j, f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert to lexicographic relative to the face itself
|
||||
const int i_face = permute_face(lf_i, orient, d1d, i_lex);
|
||||
val = face_mats(i_face, j, f);
|
||||
}
|
||||
// Convert from lexicographic face DOF to volume DOF
|
||||
const int i = d_face_maps(i_lex, lf_i);
|
||||
el_mats(i, j, el_i, f) += val;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ void SmemPAVectorDiffusionApply2D(const int NE,
|
||||
const auto XE = Reshape(x.Read(), D1D, D1D, SDIM, NE);
|
||||
auto YE = Reshape(y.ReadWrite(), D1D, D1D, SDIM, NE);
|
||||
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
|
||||
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
|
||||
@@ -120,7 +120,7 @@ void SmemPAVectorDiffusionApply3D(const int NE,
|
||||
const auto XE = Reshape(x.Read(), D1D, D1D, D1D, SDIM, NE);
|
||||
auto YE = Reshape(y.ReadWrite(), D1D, D1D, D1D, SDIM, NE);
|
||||
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
|
||||
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
|
||||
|
||||
@@ -51,7 +51,7 @@ void SmemPAVectorMassApply2D(const int NE,
|
||||
const auto X = Reshape(x.Read(), D1D, D1D, VDIM, NE);
|
||||
auto Y = Reshape(y.ReadWrite(), D1D, D1D, VDIM, NE);
|
||||
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
|
||||
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
|
||||
@@ -119,7 +119,7 @@ void SmemPAVectorMassApply3D(const int NE,
|
||||
const auto X = Reshape(x.Read(), D1D, D1D, D1D, VDIM, NE);
|
||||
auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
|
||||
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
|
||||
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
|
||||
|
||||
+31
-3
@@ -14,7 +14,6 @@
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "kernel_reporter.hpp"
|
||||
#include "../general/hash_util.hpp"
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -87,6 +86,35 @@ namespace mfem
|
||||
} \
|
||||
}
|
||||
|
||||
/// @brief Hashes variadic packs for which each type contained in the variadic
|
||||
/// pack has a specialization of `std::hash` available.
|
||||
///
|
||||
/// For example, packs containing int, bool, enum values, etc.
|
||||
template<typename ...KernelParameters>
|
||||
struct KernelDispatchKeyHash
|
||||
{
|
||||
private:
|
||||
template<int N>
|
||||
size_t operator()(std::tuple<KernelParameters...> value) const { return 0; }
|
||||
|
||||
// The hashing formula here is taken directly from the Boost library, with
|
||||
// the magic number 0x9e3779b9 chosen to minimize hashing collisions.
|
||||
template<std::size_t N, typename THead, typename... TTail>
|
||||
size_t operator()(std::tuple<KernelParameters...> value) const
|
||||
{
|
||||
constexpr int Index = N - sizeof...(TTail) - 1;
|
||||
auto lhs_hash = std::hash<THead>()(std::get<Index>(value));
|
||||
auto rhs_hash = operator()<N, TTail...>(value);
|
||||
return lhs_hash^(rhs_hash + 0x9e3779b9 + (lhs_hash<<6) + (lhs_hash>>2));
|
||||
}
|
||||
public:
|
||||
/// Returns the hash of the given @a value.
|
||||
size_t operator()(std::tuple<KernelParameters...> value) const
|
||||
{
|
||||
return operator()<sizeof...(KernelParameters),KernelParameters...>(value);
|
||||
}
|
||||
};
|
||||
|
||||
namespace internal { template<typename... Types> struct KernelTypeList { }; }
|
||||
|
||||
template<typename... T> class KernelDispatchTable { };
|
||||
@@ -100,8 +128,8 @@ class KernelDispatchTable<Kernels,
|
||||
internal::KernelTypeList<Params...>,
|
||||
internal::KernelTypeList<OptParams...>>
|
||||
{
|
||||
using TableType =
|
||||
std::unordered_map<std::tuple<Params...>, Signature, TupleHasher>;
|
||||
using TableType = std::unordered_map<std::tuple<Params...>,
|
||||
Signature, KernelDispatchKeyHash<Params...>>;
|
||||
TableType table;
|
||||
|
||||
/// @brief Call function @a f with arguments @a args (perfect forwaring).
|
||||
|
||||
+2
-16
@@ -23,8 +23,6 @@ class BatchedLOR_DG : BatchedLORKernel
|
||||
{
|
||||
IntegrationRule ir_face; ///< Collocated Gauss-Lobatto face quadrature rule.
|
||||
real_t kappa; ///< DG penalty parameter.
|
||||
bool has_bdr_integ; ///< Is there a boundary integrator?
|
||||
const Array<int> *bdr_markers; ///< Boundary integrator markers.
|
||||
public:
|
||||
template <int ORDER, int SDIM> void Assemble2D();
|
||||
template <int ORDER> void Assemble3D();
|
||||
@@ -40,7 +38,8 @@ public:
|
||||
ProjectLORCoefficient<MassIntegrator>(a, c1);
|
||||
ProjectLORCoefficient<DiffusionIntegrator>(a, c2);
|
||||
|
||||
if (auto *integ = GetInteriorFaceIntegrator<DGDiffusionIntegrator>(a))
|
||||
auto *integ = GetInteriorFaceIntegrator<DGDiffusionIntegrator>(a);
|
||||
if (integ)
|
||||
{
|
||||
kappa = integ->GetPenaltyParameter();
|
||||
}
|
||||
@@ -48,19 +47,6 @@ public:
|
||||
{
|
||||
kappa = 0.0;
|
||||
}
|
||||
|
||||
has_bdr_integ = false;
|
||||
auto *bdr_face_integs = a.GetBFBFI();
|
||||
for (int i = 0; i < bdr_face_integs->Size(); ++i)
|
||||
{
|
||||
if (auto *integ = dynamic_cast<DGDiffusionIntegrator*>((*bdr_face_integs)[i]))
|
||||
{
|
||||
kappa = integ->GetPenaltyParameter();
|
||||
bdr_markers = (*a.GetBFBFI_Marker())[i];
|
||||
has_bdr_integ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Compute and return the face info array.
|
||||
|
||||
@@ -22,13 +22,9 @@ namespace mfem
|
||||
Array<int> BatchedLOR_DG::GetFaceInfo() const
|
||||
{
|
||||
Mesh &mesh = *fes_ho.GetMesh();
|
||||
const Array<int> &bdr_face_attrs = mesh.GetBdrFaceAttributes();
|
||||
const int nf = mesh.GetNumFaces();
|
||||
Array<int> face_info(nf * 6); // (e0, f0, o0, e1, f1, o1)
|
||||
auto h_face_info = Reshape(face_info.HostWrite(), 6, nf);
|
||||
|
||||
int bdr_face_counter = 0;
|
||||
|
||||
for (int f = 0; f < nf; ++f)
|
||||
{
|
||||
auto finfo = mesh.GetFaceInformation(f);
|
||||
@@ -47,19 +43,6 @@ Array<int> BatchedLOR_DG::GetFaceInfo() const
|
||||
h_face_info(4, f) = -1;
|
||||
h_face_info(5, f) = -1;
|
||||
}
|
||||
|
||||
if (finfo.IsBoundary())
|
||||
{
|
||||
// Check if Neumann boundary; skip these when adding boundary penalties
|
||||
const int bdr_attr = bdr_face_attrs[bdr_face_counter];
|
||||
if (!has_bdr_integ || (bdr_markers && !(*bdr_markers)[bdr_attr - 1]))
|
||||
{
|
||||
h_face_info(0, f) = -1;
|
||||
h_face_info(1, f) = -1;
|
||||
h_face_info(2, f) = -1;
|
||||
}
|
||||
bdr_face_counter += 1;
|
||||
}
|
||||
}
|
||||
return face_info;
|
||||
}
|
||||
@@ -161,7 +144,6 @@ void BatchedLOR_DG::AssembleFaceTerms()
|
||||
{
|
||||
const int f_0 = d_face_info(1, f);
|
||||
const int f_1 = d_face_info(4, f);
|
||||
if (f_0 < 0) { return; } // Skip Neumann boundary faces
|
||||
const int nsides = (f_1 >= 0) ? 2 : 1;
|
||||
for (int el_i = 0; el_i < nsides; ++el_i)
|
||||
{
|
||||
|
||||
@@ -78,7 +78,10 @@ template <int Dim>
|
||||
void BuildBoxes(const Mesh &mesh,
|
||||
std::vector<::moonolith::AABB<Dim, double>> &element_boxes)
|
||||
{
|
||||
MFEM_ASSERT(mesh.Dimension() == Dim, "Mesh and box dimensions mismatched");
|
||||
#ifndef NDEBUG
|
||||
const int dim = mesh.Dimension();
|
||||
assert(dim == Dim);
|
||||
#endif
|
||||
element_boxes.resize(mesh.GetNE());
|
||||
|
||||
DenseMatrix pts;
|
||||
|
||||
@@ -1,950 +0,0 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "particleset.hpp"
|
||||
|
||||
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
|
||||
|
||||
// Ignore warnings from the gslib header (GCC version)
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
|
||||
namespace gslib
|
||||
{
|
||||
extern "C"
|
||||
{
|
||||
#include <gslib.h>
|
||||
} // extern C
|
||||
} // namespace gslib
|
||||
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // MFEM_USE_MPI && MFEM_USE_GSLIB
|
||||
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
Particle::Particle(int dim, const Array<int> &field_vdims, int num_tags)
|
||||
: coords(dim), fields(), tags()
|
||||
{
|
||||
coords = 0.0;
|
||||
|
||||
fields.reserve(field_vdims.Size());
|
||||
for (int f = 0; f < field_vdims.Size(); f++)
|
||||
{
|
||||
fields.emplace_back(field_vdims[f]);
|
||||
fields.back() = 0.0;
|
||||
}
|
||||
|
||||
tags.reserve(num_tags);
|
||||
for (int t = 0; t < num_tags; t++)
|
||||
{
|
||||
tags.emplace_back(1);
|
||||
tags.back()[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Particle::SetTagRef(int t, int *tag_data)
|
||||
{
|
||||
MFEM_ASSERT(t >= 0 &&
|
||||
static_cast<size_t>(t) < tags.size(), "Invalid tag index");
|
||||
tags[t].MakeRef(tag_data, 1);
|
||||
}
|
||||
|
||||
void Particle::SetFieldRef(int f, real_t *field_data)
|
||||
{
|
||||
MFEM_ASSERT(f >= 0 &&
|
||||
static_cast<size_t>(f) < fields.size(), "Invalid field "
|
||||
"index");
|
||||
Vector temp(field_data, fields[f].Size());
|
||||
fields[f].MakeRef(temp, 0, fields[f].Size());
|
||||
}
|
||||
|
||||
bool Particle::operator==(const Particle &rhs) const
|
||||
{
|
||||
// Compare coordinate size and values
|
||||
if (coords.Size() != rhs.coords.Size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int d = 0; d < coords.Size(); d++)
|
||||
{
|
||||
if (coords[d] != rhs.coords[d])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Compare fields vdim and values
|
||||
if (fields.size() != rhs.fields.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (size_t f = 0; f < fields.size(); f++)
|
||||
{
|
||||
if (fields[f].Size() != rhs.fields[f].Size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int c = 0; c < fields[f].Size(); c++)
|
||||
{
|
||||
if (fields[f][c] != rhs.fields[f][c])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Compare tags size and values
|
||||
if (tags.size() != rhs.tags.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (size_t t = 0; t < tags.size(); t++)
|
||||
{
|
||||
if (tags[t][0] != rhs.tags[t][0])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Particle::Print(std::ostream &os) const
|
||||
{
|
||||
os << "Coords: (";
|
||||
for (int d = 0; d < coords.Size(); d++)
|
||||
{
|
||||
os << coords[d] << ( (d+1 < coords.Size()) ? "," : ")\n");
|
||||
}
|
||||
for (size_t f = 0; f < fields.size(); f++)
|
||||
{
|
||||
os << "Field " << f << ": (";
|
||||
for (int c = 0; c < fields[f].Size(); c++)
|
||||
{
|
||||
os << fields[f][c] << ( (c+1 < fields[f].Size()) ? "," : ")\n");
|
||||
}
|
||||
}
|
||||
for (size_t t = 0; t < tags.size(); t++)
|
||||
{
|
||||
os << "Tag " << t << ": " << tags[t][0] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
Array<Ordering::Type> ParticleSet::GetOrderingArray(Ordering::Type o, int N)
|
||||
{
|
||||
Array<Ordering::Type> ordering_arr(N);
|
||||
ordering_arr = o;
|
||||
return ordering_arr;
|
||||
}
|
||||
std::string ParticleSet::GetDefaultFieldName(int i)
|
||||
{
|
||||
return "Field_" + std::to_string(i);
|
||||
}
|
||||
|
||||
std::string ParticleSet::GetDefaultTagName(int i)
|
||||
{
|
||||
return "Tag_" + std::to_string(i);
|
||||
}
|
||||
|
||||
Array<const char*> ParticleSet::GetEmptyNameArray(int N)
|
||||
{
|
||||
Array<const char*> names(N);
|
||||
names = nullptr;
|
||||
return names;
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
int ParticleSet::GetRank(MPI_Comm comm_)
|
||||
{
|
||||
int r; MPI_Comm_rank(comm_, &r);
|
||||
return r;
|
||||
}
|
||||
int ParticleSet::GetSize(MPI_Comm comm_)
|
||||
{
|
||||
int s; MPI_Comm_size(comm_, &s);
|
||||
return s;
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
void ParticleSet::Reserve(int res)
|
||||
{
|
||||
ids.Reserve(res);
|
||||
|
||||
// Reserve fields
|
||||
for (int f = -1; f < GetNFields(); f++)
|
||||
{
|
||||
ParticleVector &pv = (f == -1 ? coords : *fields[f]);
|
||||
pv.Reserve(res*pv.GetVDim());
|
||||
}
|
||||
|
||||
// Reserve tags
|
||||
for (int t = 0; t < GetNTags(); t++)
|
||||
{
|
||||
tags[t]->Reserve(res);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const Array<int> ParticleSet::GetFieldVDims() const
|
||||
{
|
||||
Array<int> field_vdims(GetNFields());
|
||||
for (int f = 0; f < GetNFields(); f++)
|
||||
{
|
||||
field_vdims[f] = Field(f).GetVDim();
|
||||
}
|
||||
return field_vdims;
|
||||
}
|
||||
|
||||
void ParticleSet::AddParticles(const Array<IDType> &new_ids,
|
||||
Array<int> *new_indices)
|
||||
{
|
||||
int num_add = new_ids.Size();
|
||||
int old_np = GetNParticles();
|
||||
int new_np = old_np + num_add;
|
||||
|
||||
// Set indices of new particles
|
||||
if (new_indices)
|
||||
{
|
||||
new_indices->SetSize(num_add);
|
||||
for (int i = 0; i < num_add; i++)
|
||||
{
|
||||
(*new_indices)[i] = ids.Size() + i;
|
||||
}
|
||||
}
|
||||
// Add new ids
|
||||
ids.Append(new_ids);
|
||||
|
||||
// Update data
|
||||
for (int f = -1; f < GetNFields(); f++)
|
||||
{
|
||||
ParticleVector &pv = (f == -1 ? coords : *fields[f]);
|
||||
pv.SetNumParticles(new_np); // does not delete existing data
|
||||
}
|
||||
|
||||
// Update tags
|
||||
for (int t = 0; t < GetNTags(); t++)
|
||||
{
|
||||
tags[t]->SetSize(new_np);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
|
||||
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
template<size_t NBytes>
|
||||
void ParticleSet::TransferParticlesImpl(ParticleSet &pset,
|
||||
const Array<int> &send_idxs,
|
||||
const Array<unsigned int> &send_ranks)
|
||||
{
|
||||
struct pdata_t
|
||||
{
|
||||
alignas(real_t) std::array<std::byte, NBytes> data;
|
||||
IDType id;
|
||||
};
|
||||
|
||||
int nreals = pset.GetFieldVDims().Sum() + pset.Coords().GetVDim();
|
||||
int ntags = pset.GetNTags();
|
||||
size_t nbytes = nreals*sizeof(real_t) + ntags*sizeof(int);
|
||||
MFEM_VERIFY(nbytes <= NBytes, "More data than can be packed.");
|
||||
|
||||
using parr_t = pdata_t;
|
||||
gslib::array gsl_arr;
|
||||
parr_t *pdata_arr;
|
||||
array_init(parr_t, &gsl_arr, send_idxs.Size());
|
||||
pdata_arr = (parr_t*) gsl_arr.ptr;
|
||||
|
||||
gsl_arr.n = send_idxs.Size();
|
||||
for (int i = 0; i < send_idxs.Size(); i++)
|
||||
{
|
||||
parr_t &pdata = pdata_arr[i];
|
||||
pdata.id = pset.GetIDs()[send_idxs[i]];
|
||||
|
||||
// Copy particle data directly into pdata
|
||||
size_t counter = 0;
|
||||
for (int f = -1; f < pset.GetNFields(); f++)
|
||||
{
|
||||
ParticleVector &pv = (f == -1 ? pset.Coords() : pset.Field(f));
|
||||
for (int c = 0; c < pv.GetVDim(); c++)
|
||||
{
|
||||
std::memcpy(pdata.data.data() + counter, &pv(send_idxs[i], c),
|
||||
sizeof(real_t));
|
||||
counter += sizeof(real_t);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy tags
|
||||
for (int t = 0; t < pset.GetNTags(); t++)
|
||||
{
|
||||
Array<int> &tag_arr = pset.Tag(t);
|
||||
std::memcpy(pdata.data.data() + counter, &tag_arr[send_idxs[i]],
|
||||
sizeof(int));
|
||||
counter += sizeof(int);
|
||||
}
|
||||
}
|
||||
|
||||
int nparticles = pset.GetNParticles();
|
||||
int nsend = send_idxs.Size();
|
||||
|
||||
// Transfer particles
|
||||
sarray_transfer_ext(parr_t, &gsl_arr, send_ranks.GetData(),
|
||||
sizeof(unsigned int), pset.cr);
|
||||
|
||||
// Make sure we have enough space for received particles
|
||||
int nrecv = (int) gsl_arr.n;
|
||||
int ndelete = nsend - nrecv;
|
||||
if (ndelete > 0)
|
||||
{
|
||||
// Remove unneeded particles
|
||||
auto datap = const_cast<int*>(send_idxs.GetData());
|
||||
Array<int> delete_idxs(datap + nrecv, ndelete);
|
||||
pset.RemoveParticles(delete_idxs);
|
||||
}
|
||||
else
|
||||
{
|
||||
pset.Reserve(nparticles-ndelete);
|
||||
}
|
||||
|
||||
pdata_arr = (parr_t*) gsl_arr.ptr;
|
||||
|
||||
// Add newly-recvd data directly to active state
|
||||
for (int i = 0; i < nrecv; i++)
|
||||
{
|
||||
parr_t &pdata = pdata_arr[i];
|
||||
IDType id = pdata.id;
|
||||
|
||||
int new_loc_idx;
|
||||
if (i < nsend) // update existing particle
|
||||
{
|
||||
new_loc_idx = send_idxs[i];
|
||||
pset.UpdateID(new_loc_idx, id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// add new particle
|
||||
Array<int> idx_temp;
|
||||
pset.AddParticles(Array<IDType>({id}), &idx_temp);
|
||||
new_loc_idx = idx_temp[0]; // Get index of newly-added particle
|
||||
}
|
||||
|
||||
size_t counter = 0;
|
||||
for (int f = -1; f < pset.GetNFields(); f++)
|
||||
{
|
||||
ParticleVector &pv = (f == -1 ? pset.Coords() : pset.Field(f));
|
||||
for (int c = 0; c < pv.GetVDim(); c++)
|
||||
{
|
||||
real_t& val = pv(new_loc_idx, c);
|
||||
std::memcpy(&val, pdata.data.data() + counter, sizeof(real_t));
|
||||
counter += sizeof(real_t);
|
||||
}
|
||||
}
|
||||
|
||||
for (int t = 0; t < pset.GetNTags(); t++)
|
||||
{
|
||||
Array<int> &tag_arr = pset.Tag(t);
|
||||
std::memcpy(&tag_arr[new_loc_idx],
|
||||
pdata.data.data() + counter, sizeof(int));
|
||||
counter += sizeof(int);
|
||||
}
|
||||
}
|
||||
array_free(&gsl_arr);
|
||||
}
|
||||
|
||||
template<size_t NBytes>
|
||||
ParticleSet::TransferParticlesType ParticleSet::TransferParticles::Kernel()
|
||||
{
|
||||
return &ParticleSet::TransferParticlesImpl<NBytes>;
|
||||
}
|
||||
|
||||
ParticleSet::Kernels::Kernels()
|
||||
{
|
||||
constexpr size_t sizd = sizeof(real_t);
|
||||
TransferParticles::Specialization<2*sizd>::Add();
|
||||
TransferParticles::Specialization<3*sizd>::Add();
|
||||
TransferParticles::Specialization<4*sizd>::Add();
|
||||
TransferParticles::Specialization<8*sizd>::Add();
|
||||
TransferParticles::Specialization<12*sizd>::Add();
|
||||
TransferParticles::Specialization<16*sizd>::Add();
|
||||
TransferParticles::Specialization<20*sizd>::Add();
|
||||
TransferParticles::Specialization<24*sizd>::Add();
|
||||
TransferParticles::Specialization<28*sizd>::Add();
|
||||
TransferParticles::Specialization<32*sizd>::Add();
|
||||
TransferParticles::Specialization<36*sizd>::Add();
|
||||
TransferParticles::Specialization<40*sizd>::Add();
|
||||
}
|
||||
|
||||
auto ParticleSet::TransferParticles::Fallback(size_t bufsize)
|
||||
-> ParticleSet::TransferParticlesType
|
||||
{
|
||||
constexpr size_t sizd = sizeof(real_t);
|
||||
if (bufsize < 4*sizd)
|
||||
{
|
||||
return &ParticleSet::TransferParticlesImpl<4*sizd>;
|
||||
}
|
||||
else if (bufsize < 8*sizd)
|
||||
{
|
||||
return &ParticleSet::TransferParticlesImpl<8*sizd>;
|
||||
}
|
||||
else if (bufsize < 12*sizd)
|
||||
{
|
||||
return &ParticleSet::TransferParticlesImpl<12*sizd>;
|
||||
}
|
||||
else if (bufsize < 16*sizd)
|
||||
{
|
||||
return &ParticleSet::TransferParticlesImpl<16*sizd>;
|
||||
}
|
||||
else if (bufsize < 20*sizd)
|
||||
{
|
||||
return &ParticleSet::TransferParticlesImpl<20*sizd>;
|
||||
}
|
||||
else if (bufsize < 24*sizd)
|
||||
{
|
||||
return &ParticleSet::TransferParticlesImpl<24*sizd>;
|
||||
}
|
||||
else if (bufsize < 28*sizd)
|
||||
{
|
||||
return &ParticleSet::TransferParticlesImpl<28*sizd>;
|
||||
}
|
||||
else if (bufsize < 32*sizd)
|
||||
{
|
||||
return &ParticleSet::TransferParticlesImpl<32*sizd>;
|
||||
}
|
||||
else if (bufsize < 36*sizd)
|
||||
{
|
||||
return &ParticleSet::TransferParticlesImpl<36*sizd>;
|
||||
}
|
||||
else if (bufsize < 40*sizd)
|
||||
{
|
||||
return &ParticleSet::TransferParticlesImpl<40*sizd>;
|
||||
}
|
||||
return &ParticleSet::TransferParticlesImpl<60*sizd>;
|
||||
}
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
void ParticleSet::Redistribute(const Array<unsigned int> &rank_list)
|
||||
{
|
||||
MFEM_ASSERT(rank_list.Size() == GetNParticles(),
|
||||
"rank_list must be of size GetNParticles().");
|
||||
|
||||
int rank = GetRank(comm);
|
||||
|
||||
// Get particles to be transferred
|
||||
// (Avoid unnecessary copies of particle data into and out of buffers)
|
||||
Array<int> send_idxs;
|
||||
Array<unsigned int> send_ranks;
|
||||
send_idxs.Reserve(rank_list.Size());
|
||||
send_ranks.Reserve(rank_list.Size());
|
||||
for (int i = 0; i < rank_list.Size(); i++)
|
||||
{
|
||||
if (rank != static_cast<int>(rank_list[i]))
|
||||
{
|
||||
send_idxs.Append(i);
|
||||
send_ranks.Append(rank_list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute number of bytes of a single particle
|
||||
int nreals = GetFieldVDims().Sum() + coords.GetVDim();
|
||||
int ntags = GetNTags();
|
||||
size_t nbytes = nreals*sizeof(real_t) + ntags*sizeof(int);
|
||||
|
||||
// Dispatch to appropriate redistribution function for this size
|
||||
TransferParticles::Run(nbytes, *this, send_idxs, send_ranks);
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_MPI && MFEM_USE_GSLIB
|
||||
|
||||
Particle ParticleSet::CreateParticle() const
|
||||
{
|
||||
return Particle(GetDim(), GetFieldVDims(), GetNTags());
|
||||
}
|
||||
|
||||
void ParticleSet::WriteToFile(const char *fname,
|
||||
const std::stringstream &ss_header, const std::stringstream &ss_data)
|
||||
{
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
// Parallel:
|
||||
int rank = GetRank(comm);
|
||||
|
||||
MPI_File_delete(fname, MPI_INFO_NULL); // delete old file if it exists
|
||||
MPI_File file;
|
||||
int mpi_err = MPI_File_open(comm, fname, MPI_MODE_CREATE | MPI_MODE_WRONLY,
|
||||
MPI_INFO_NULL, &file);
|
||||
MFEM_VERIFY(mpi_err == MPI_SUCCESS, "MPI_File_open failed.");
|
||||
|
||||
// Print header
|
||||
if (rank == 0)
|
||||
{
|
||||
MPI_File_write_at(file, 0, ss_header.str().data(), ss_header.str().size(),
|
||||
MPI_CHAR, MPI_STATUS_IGNORE);
|
||||
}
|
||||
|
||||
// Compute the data size in bytes
|
||||
MPI_Offset data_size = ss_data.str().size();
|
||||
MPI_Offset offset;
|
||||
|
||||
// Compute the offsets using an exclusive scan
|
||||
MPI_Exscan(&data_size, &offset, 1, MPI_OFFSET, MPI_SUM, comm);
|
||||
if (rank == 0)
|
||||
{
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
// Add offset from the header
|
||||
offset += ss_header.str().size();
|
||||
|
||||
// Write data collectively
|
||||
MPI_File_write_at_all(file, offset, ss_data.str().data(),
|
||||
data_size, MPI_BYTE, MPI_STATUS_IGNORE);
|
||||
|
||||
// Close file
|
||||
MPI_File_close(&file);
|
||||
#else
|
||||
// Serial:
|
||||
std::ofstream ofs(fname);
|
||||
MFEM_VERIFY(ofs.is_open() && !ofs.fail(),
|
||||
"Error: Could not open file " << fname << " for writing.");
|
||||
ofs << ss_header.str() << ss_data.str();
|
||||
ofs.close();
|
||||
#endif // MFEM_USE_MPI
|
||||
}
|
||||
|
||||
ParticleSet::ParticleSet(int id_stride_, IDType id_counter_, int num_particles,
|
||||
int dim, Ordering::Type coords_ordering, const Array<int> &field_vdims,
|
||||
const Array<Ordering::Type> &field_orderings,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_)
|
||||
: id_stride(id_stride_),
|
||||
id_counter(id_counter_),
|
||||
coords(dim, coords_ordering)
|
||||
{
|
||||
// Initialize fields
|
||||
for (int f = 0; f < field_vdims.Size(); f++)
|
||||
{
|
||||
AddField(field_vdims[f], field_orderings[f], field_names_[f]);
|
||||
}
|
||||
|
||||
// Initialize tags
|
||||
for (int t = 0; t < num_tags; t++)
|
||||
{
|
||||
AddTag(tag_names_[t]);
|
||||
}
|
||||
|
||||
// Add num_particles
|
||||
Array<IDType> init_ids(num_particles);
|
||||
for (int i = 0; i < num_particles; i++)
|
||||
{
|
||||
init_ids[i] = id_counter;
|
||||
id_counter += id_stride;
|
||||
}
|
||||
AddParticles(init_ids);
|
||||
}
|
||||
|
||||
bool ParticleSet::IsValidParticle(const Particle &p) const
|
||||
{
|
||||
if (p.GetDim() != GetDim())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (p.GetNFields() != GetNFields())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int f = 0; f < GetNFields(); f++)
|
||||
{
|
||||
if (p.GetFieldVDim(f) != Field(f).GetVDim())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (p.GetNTags() != GetNTags())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
ParticleSet::ParticleSet(int num_particles, int dim,
|
||||
Ordering::Type coords_ordering)
|
||||
: ParticleSet(1, 0, num_particles, dim, coords_ordering, Array<int>(),
|
||||
Array<Ordering::Type>(), Array<const char*>(), 0,
|
||||
Array<const char*>())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ParticleSet::ParticleSet(int num_particles, int dim,
|
||||
const Array<int> &field_vdims, int num_tags,
|
||||
Ordering::Type all_ordering)
|
||||
: ParticleSet(1, 0, num_particles, dim, all_ordering, field_vdims,
|
||||
GetOrderingArray(all_ordering, field_vdims.Size()),
|
||||
GetEmptyNameArray(field_vdims.Size()), num_tags,
|
||||
GetEmptyNameArray(num_tags))
|
||||
{
|
||||
}
|
||||
|
||||
ParticleSet::ParticleSet(int num_particles, int dim,
|
||||
const Array<int> &field_vdims, const Array<const
|
||||
char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_,
|
||||
Ordering::Type all_ordering)
|
||||
: ParticleSet(1, 0, num_particles, dim, all_ordering, field_vdims,
|
||||
GetOrderingArray(all_ordering, field_vdims.Size()),
|
||||
field_names_, num_tags,
|
||||
tag_names_)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ParticleSet::ParticleSet(int num_particles, int dim,
|
||||
Ordering::Type coords_ordering,
|
||||
const Array<int> &field_vdims,
|
||||
const Array<Ordering::Type> &field_orderings,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_)
|
||||
: ParticleSet(1, 0, num_particles, dim, coords_ordering, field_vdims,
|
||||
field_orderings, field_names_, num_tags, tag_names_)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
Ordering::Type coords_ordering)
|
||||
: ParticleSet(comm_, rank_num_particles, dim, coords_ordering, Array<int>(),
|
||||
Array<Ordering::Type>(), Array<const char*>(), 0,
|
||||
Array<const char*>())
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
const Array<int> &field_vdims, int num_tags,
|
||||
Ordering::Type all_ordering)
|
||||
: ParticleSet(comm_, rank_num_particles, dim, all_ordering, field_vdims,
|
||||
GetOrderingArray(all_ordering, field_vdims.Size()),
|
||||
GetEmptyNameArray(field_vdims.Size()), num_tags,
|
||||
GetEmptyNameArray(num_tags))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
const Array<int> &field_vdims, const Array<const
|
||||
char*> &field_names_,
|
||||
int num_tags, const Array<const char*> &tag_names_,
|
||||
Ordering::Type all_ordering)
|
||||
: ParticleSet(comm_, rank_num_particles, dim, all_ordering, field_vdims,
|
||||
GetOrderingArray(all_ordering, field_vdims.Size()),
|
||||
field_names_, num_tags,
|
||||
tag_names_)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
Ordering::Type coords_ordering,
|
||||
const Array<int> &field_vdims,
|
||||
const Array<Ordering::Type> &field_orderings,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_)
|
||||
: ParticleSet(GetSize(comm_), (IDType)GetRank(comm_),
|
||||
rank_num_particles,
|
||||
dim,
|
||||
coords_ordering,
|
||||
field_vdims,
|
||||
field_orderings,
|
||||
field_names_,
|
||||
num_tags,
|
||||
tag_names_)
|
||||
{
|
||||
comm = comm_;
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
gsl_comm = new gslib::comm;
|
||||
cr = new gslib::crystal;
|
||||
comm_init(gsl_comm, comm);
|
||||
crystal_init(cr, gsl_comm);
|
||||
#endif // MFEM_USE_GSLIB
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
ParticleSet::IDType ParticleSet::GetGlobalNParticles() const
|
||||
{
|
||||
IDType total = (IDType)GetNParticles();
|
||||
#ifdef MFEM_USE_MPI
|
||||
MPI_Allreduce(MPI_IN_PLACE, &total, 1, MPI_UNSIGNED_LONG_LONG,
|
||||
MPI_SUM, comm);
|
||||
#endif // MFEM_USE_MPI
|
||||
return total;
|
||||
}
|
||||
|
||||
int ParticleSet::AddField(int vdim, Ordering::Type field_ordering,
|
||||
const char* field_name)
|
||||
{
|
||||
std::string field_name_str(field_name ? field_name : "");
|
||||
if (!field_name)
|
||||
{
|
||||
field_name_str = GetDefaultFieldName(field_names.size());
|
||||
}
|
||||
fields.emplace_back(std::make_unique<ParticleVector>(vdim, field_ordering,
|
||||
GetNParticles()));
|
||||
field_names.emplace_back(field_name_str);
|
||||
|
||||
return GetNFields() - 1;
|
||||
}
|
||||
|
||||
int ParticleSet::AddTag(const char* tag_name)
|
||||
{
|
||||
std::string tag_name_str(tag_name ? tag_name : "");
|
||||
if (!tag_name)
|
||||
{
|
||||
tag_name_str = GetDefaultTagName(tag_names.size());
|
||||
}
|
||||
tags.emplace_back(std::make_unique<Array<int>>(GetNParticles()));
|
||||
tag_names.emplace_back(tag_name_str);
|
||||
|
||||
return GetNTags() - 1;
|
||||
}
|
||||
|
||||
void ParticleSet::AddParticle(const Particle &p)
|
||||
{
|
||||
MFEM_ASSERT(IsValidParticle(p),
|
||||
"Particle is incompatible with ParticleSet.");
|
||||
|
||||
// Add the particle
|
||||
Array<int> idxs;
|
||||
AddParticles(Array<IDType>({id_counter}), &idxs);
|
||||
id_counter += id_stride;
|
||||
|
||||
// Set the new particle data
|
||||
int idx = idxs[0];
|
||||
SetParticle(idx, p);
|
||||
}
|
||||
|
||||
void ParticleSet::AddParticles(int num_particles, Array<int> *new_indices)
|
||||
{
|
||||
Array<IDType> add_ids(num_particles);
|
||||
for (int i = 0; i < num_particles; i++)
|
||||
{
|
||||
add_ids[i] = id_counter;
|
||||
id_counter += id_stride;
|
||||
}
|
||||
|
||||
AddParticles(add_ids, new_indices);
|
||||
}
|
||||
|
||||
void ParticleSet::RemoveParticles(const Array<int> &list)
|
||||
{
|
||||
// Delete IDs
|
||||
ids.DeleteAt(list);
|
||||
|
||||
// Delete data
|
||||
for (int f = -1; f < GetNFields(); f++)
|
||||
{
|
||||
ParticleVector &pv = (f == -1 ? coords : *fields[f]);
|
||||
pv.DeleteParticles(list);
|
||||
}
|
||||
|
||||
// Delete tags
|
||||
for (int t = 0; t < GetNTags(); t++)
|
||||
{
|
||||
tags[t]->DeleteAt(list);
|
||||
}
|
||||
}
|
||||
|
||||
Particle ParticleSet::GetParticle(int i) const
|
||||
{
|
||||
Particle p = CreateParticle();
|
||||
|
||||
Coords().GetValues(i, p.Coords());
|
||||
|
||||
for (int f = 0; f < GetNFields(); f++)
|
||||
{
|
||||
Field(f).GetValues(i, p.Field(f));
|
||||
}
|
||||
|
||||
for (int t = 0; t < GetNTags(); t++)
|
||||
{
|
||||
p.Tag(t) = Tag(t)[i];
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
bool ParticleSet::IsParticleRefValid() const
|
||||
{
|
||||
if (coords.GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int f = 0; f < GetNFields(); f++)
|
||||
{
|
||||
if (fields[f]->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Particle ParticleSet::GetParticleRef(int i)
|
||||
{
|
||||
Particle p = CreateParticle();
|
||||
|
||||
Coords().GetValuesRef(i, p.Coords());
|
||||
|
||||
for (int f = 0; f < GetNFields(); f++)
|
||||
{
|
||||
MFEM_ASSERT(Field(f).GetOrdering() == Ordering::byVDIM,
|
||||
"GetParticleRef only valid when all fields ordered byVDIM.");
|
||||
p.SetFieldRef(f, Field(f).GetData() + i*Field(f).GetVDim());
|
||||
}
|
||||
|
||||
for (int t = 0; t < GetNTags(); t++)
|
||||
{
|
||||
p.SetTagRef(t, &(*tags[t])[i]);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void ParticleSet::SetParticle(int i, const Particle &p)
|
||||
{
|
||||
MFEM_ASSERT(IsValidParticle(p),
|
||||
"Particle is incompatible with ParticleSet.");
|
||||
|
||||
Coords().SetValues(i, p.Coords());
|
||||
|
||||
for (int f = 0; f < GetNFields(); f++)
|
||||
{
|
||||
Field(f).SetValues(i, p.Field(f));
|
||||
}
|
||||
|
||||
for (int t = 0; t < GetNTags(); t++)
|
||||
{
|
||||
Tag(t)[i] = p.Tag(t);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleSet::PrintCSV(const char *fname, int precision)
|
||||
{
|
||||
Array<int> all_field_idxs(GetNFields()), all_tag_idxs(GetNTags());
|
||||
|
||||
for (int f = 0; f < GetNFields(); f++)
|
||||
{
|
||||
all_field_idxs[f] = f;
|
||||
}
|
||||
|
||||
for (int t = 0; t < GetNTags(); t++)
|
||||
{
|
||||
all_tag_idxs[t] = t;
|
||||
}
|
||||
|
||||
PrintCSV(fname, all_field_idxs, all_tag_idxs, precision);
|
||||
}
|
||||
|
||||
void ParticleSet::PrintCSV(const char *fname, const Array<int> &field_idxs,
|
||||
const Array<int> &tag_idxs, int precision)
|
||||
{
|
||||
std::stringstream ss_header;
|
||||
|
||||
// Configure header:
|
||||
ss_header << "id";
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
ss_header << ",rank";
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
std::array<char, 3> ax = {'X', 'Y', 'Z'};
|
||||
for (int c = 0; c < coords.GetVDim(); c++)
|
||||
{
|
||||
ss_header << "," << ax[c];
|
||||
}
|
||||
|
||||
for (int f = 0; f < field_idxs.Size(); f++)
|
||||
{
|
||||
ParticleVector &pv = *fields[field_idxs[f]];
|
||||
for (int c = 0; c < pv.GetVDim(); c++)
|
||||
{
|
||||
ss_header << "," << field_names[field_idxs[f]] <<
|
||||
(pv.GetVDim() > 1 ? "_" + std::to_string(c) : "");
|
||||
}
|
||||
}
|
||||
|
||||
for (int t = 0; t < tag_idxs.Size(); t++)
|
||||
{
|
||||
ss_header << "," << tag_names[tag_idxs[t]];
|
||||
}
|
||||
ss_header << "\n";
|
||||
|
||||
// Configure data
|
||||
std::stringstream ss_data;
|
||||
ss_data.precision(precision);
|
||||
#ifdef MFEM_USE_MPI
|
||||
int rank = GetRank(comm);
|
||||
#endif // MFEM_USE_MPI
|
||||
for (int i = 0; i < GetNParticles(); i++)
|
||||
{
|
||||
ss_data << ids[i];
|
||||
#ifdef MFEM_USE_MPI
|
||||
ss_data << "," << rank;
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
for (int c = 0; c < coords.GetVDim(); c++)
|
||||
{
|
||||
ss_data << "," << coords(i, c);
|
||||
}
|
||||
for (int f = 0; f < field_idxs.Size(); f++)
|
||||
{
|
||||
ParticleVector &pv = *fields[field_idxs[f]];
|
||||
for (int c = 0; c < pv.GetVDim(); c++)
|
||||
{
|
||||
ss_data << "," << pv(i, c);
|
||||
}
|
||||
}
|
||||
for (int t = 0; t < tag_idxs.Size(); t++)
|
||||
{
|
||||
ss_data << "," << (*tags[tag_idxs[t]])[i];
|
||||
}
|
||||
ss_data << "\n";
|
||||
}
|
||||
|
||||
// Write
|
||||
WriteToFile(fname, ss_header, ss_data);
|
||||
}
|
||||
|
||||
ParticleSet::~ParticleSet()
|
||||
{
|
||||
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
|
||||
if (gsl_comm)
|
||||
{
|
||||
if (!Mpi::IsFinalized()) // currently segfaults inside gslib otherwise
|
||||
{
|
||||
crystal_free(cr);
|
||||
comm_free(gsl_comm);
|
||||
delete gsl_comm;
|
||||
delete cr;
|
||||
}
|
||||
}
|
||||
#endif // MFEM_USE_MPI && MFEM_USE_GSLIB
|
||||
}
|
||||
|
||||
|
||||
} // namespace mfem
|
||||
@@ -1,685 +0,0 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_PARTICLESET
|
||||
#define MFEM_PARTICLESET
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../linalg/linalg.hpp"
|
||||
#include "gslib.hpp"
|
||||
#include "kernel_dispatch.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** @brief Container for data associated with a single particle.
|
||||
*
|
||||
* @note This class mainly serves as a convenience interface to individual
|
||||
* particle data from ParticleSet. We recommend seeing ParticleSet first.
|
||||
*
|
||||
* @details As described in ParticleSet documentation, each particle has a
|
||||
* position (\ref coords), arbitrary number of scalar or vector \ref real_t
|
||||
* data (\ref fields), and arbitrary number of integers (\ref tags)
|
||||
* associated with it.
|
||||
*
|
||||
* \ref fields can thus hold data such as mass, momentum, and velocity, while
|
||||
* \ref tags can hold integer data such as particle type, color, etc.
|
||||
*
|
||||
* Each particle also has a unique global ID, but that is managed by the
|
||||
* ParticleSet class and not stored in this Particle class. Simiarly, the names
|
||||
* of the fields and tags, typically useful for output purposes, are managed by
|
||||
* the ParticleSet class.
|
||||
*
|
||||
*
|
||||
* For clarity, we will use the particles below to illustrate the data layout
|
||||
* for \ref coords, \ref fields, and \ref tags
|
||||
*
|
||||
* @anchor sample_particle_data
|
||||
* @code
|
||||
* Particle_0: coords = (x0, y0),
|
||||
* fields = {'mass'=m0, 'vel' = (vx0, vy0)},
|
||||
* tags = {'type'=t0, 'color'=color0}
|
||||
* Particle_1: coords = (x1, y1),
|
||||
* fields = {'mass'=m1, 'vel' = (vx1, vy1)},
|
||||
* tags = {'type'=t1, 'color'=color1}
|
||||
* Particle_2: coords = (x2, y2),
|
||||
* fields = {'mass'=m2, 'vel' = (vx2, vy2)},
|
||||
* tags = {'type'=t2, 'color'=color2}
|
||||
* @endcode
|
||||
*
|
||||
*/
|
||||
class Particle
|
||||
{
|
||||
protected:
|
||||
/** @brief Spatial coordinates
|
||||
*
|
||||
* @details For the \ref sample_particle_data, \ref coords would hold
|
||||
* (x_i, y_i) for each particle i.
|
||||
*/
|
||||
Vector coords;
|
||||
|
||||
/** @brief A std::vector of Vector where each Vector holds data for a given
|
||||
* field (e.g., mass, momentum or velocity) associated with the particle.
|
||||
*
|
||||
* @details For the \ref sample_particle_data, \ref fields would be
|
||||
* fields[0]=(m_i), fields[1]=(vx_i,vy_i) for each particle i.
|
||||
*/
|
||||
std::vector<Vector> fields;
|
||||
|
||||
/** @brief A std::vector of Array<int> where each Array<int> holds data
|
||||
* for a given tag.
|
||||
*
|
||||
* @details For the \ref sample_particle_data, \ref tags would be
|
||||
* tags[0]=(type_i), tags[1]=(color_i) for each particle i. \n
|
||||
*
|
||||
* @note An Array of length 1 is used for EACH tag, strictly for
|
||||
* its owning/non-owning semantics (see Array<T>::MakeRef).
|
||||
*/
|
||||
std::vector<Array<int>> tags;
|
||||
public:
|
||||
/** @brief Construct a Particle instance.
|
||||
* @param[in] dim Spatial dimension (size of #coords).
|
||||
* @param[in] field_vdims Vector dimensions of particle fields.
|
||||
* @param[in] num_tags Number of integer tags.
|
||||
*/
|
||||
Particle(int dim, const Array<int> &field_vdims, int num_tags);
|
||||
|
||||
// Force default constructors and destructor
|
||||
Particle(const Particle&) = default;
|
||||
Particle& operator=(const Particle&) = default;
|
||||
Particle(Particle&&) = default;
|
||||
Particle& operator=(Particle&&) = default;
|
||||
~Particle() = default;
|
||||
|
||||
/// Get the spatial dimension of this particle.
|
||||
int GetDim() const { return coords.Size(); }
|
||||
|
||||
/// Get the number of fields associated with this particle.
|
||||
int GetNFields() const { return fields.size(); }
|
||||
|
||||
/// Get the vector dimension of field \p f .
|
||||
int GetFieldVDim(int f) const { return fields[f].Size(); }
|
||||
|
||||
/// Get the number of tags associated with this particle.
|
||||
int GetNTags() const { return tags.size(); }
|
||||
|
||||
/// Get reference to particle coordinates Vector.
|
||||
Vector& Coords() { return coords; }
|
||||
|
||||
/// Get const reference to particle coordinates Vector.
|
||||
const Vector& Coords() const { return coords; }
|
||||
|
||||
/// Get reference to field \p f , component \p c value.
|
||||
real_t& FieldValue(int f, int c=0)
|
||||
{
|
||||
MFEM_ASSERT(f >= 0 && static_cast<std::size_t>(f) < fields.size(),
|
||||
"Invalid field index");
|
||||
MFEM_ASSERT(c >= 0 && c < fields[f].Size(),
|
||||
"Invalid component index");
|
||||
return fields[f][c];
|
||||
}
|
||||
|
||||
/// Get const reference to field \p f , component \p c value.
|
||||
const real_t& FieldValue(int f, int c=0) const
|
||||
{
|
||||
MFEM_ASSERT(f >= 0 && static_cast<std::size_t>(f) < fields.size(),
|
||||
"invalid field index");
|
||||
MFEM_ASSERT(c >= 0 && c < fields[f].Size(),
|
||||
"invalid component index");
|
||||
return fields[f][c];
|
||||
}
|
||||
|
||||
/// Get reference to field \p f Vector.
|
||||
Vector& Field(int f)
|
||||
{
|
||||
MFEM_ASSERT(f >= 0 && static_cast<std::size_t>(f) < fields.size(),
|
||||
"invalid field index");
|
||||
return fields[f];
|
||||
}
|
||||
|
||||
/// Get const reference to field \p f Vector.
|
||||
const Vector& Field(int f) const
|
||||
{
|
||||
MFEM_ASSERT(f >= 0 && static_cast<std::size_t>(f) < fields.size(),
|
||||
"invalid field index");
|
||||
return fields[f];
|
||||
}
|
||||
|
||||
/// Get reference to tag \p t .
|
||||
int& Tag(int t)
|
||||
{
|
||||
MFEM_ASSERT(t >= 0 && static_cast<std::size_t>(t) < tags.size(),
|
||||
"invalid tag index");
|
||||
return tags[t][0];
|
||||
}
|
||||
|
||||
/// Get const reference to tag \p t .
|
||||
const int& Tag(int t) const
|
||||
{
|
||||
MFEM_ASSERT(t >= 0 && static_cast<std::size_t>(t) < tags.size(),
|
||||
"invalid tag index");
|
||||
return tags[t][0];
|
||||
}
|
||||
|
||||
/// Set tag \p t to reference external data.
|
||||
void SetTagRef(int t, int *tag_data);
|
||||
|
||||
/// Set field \p f to reference external data.
|
||||
void SetFieldRef(int f, real_t *field_data);
|
||||
|
||||
/// Particle equality operator.
|
||||
bool operator==(const Particle &rhs) const;
|
||||
|
||||
/// Particle inequality operator.
|
||||
bool operator!=(const Particle &rhs) const { return !operator==(rhs); }
|
||||
|
||||
/// Print all particle data to \p os.
|
||||
void Print(std::ostream &os=mfem::out) const;
|
||||
};
|
||||
|
||||
/** @brief ParticleSet initializes and manages data associated with particles.
|
||||
*
|
||||
* @details Particles are inherently initialized to have a position and an ID,
|
||||
* and optionally can have any number of Vector (of arbitrary vdim) and scalar
|
||||
* integer data in the form of @b fields and @b tags respectively. All particle
|
||||
* data are internally stored in a Struct-of-Arrays fashion, as elaborated on
|
||||
* below.
|
||||
*
|
||||
* @par Coordinates:
|
||||
* All particle coordinates are stored in a ParticleVector with vector
|
||||
* dimension equal to the spatial dimension, ordered either byNODES or byVDIM.
|
||||
* The ParticleVector \ref coords contains the coordinates of all particles.
|
||||
*
|
||||
* @par IDs:
|
||||
* Each particle is assigned a unique global ID of type IDType. In parallel,
|
||||
* IDs are initialized starting with @b rank and striding by @b size. The IDs
|
||||
* of all particles owned by this rank are stored in \ref ids.
|
||||
*
|
||||
* @par Fields:
|
||||
* Fields represent scalar or vector \ref real_t data to be associated with
|
||||
* each particles, such as mass, momentum, or moment. For a given field, all
|
||||
* particle data is stored in a single ParticleVector with a given
|
||||
* vector dimension (1 for scalar data) and Ordering::Type (byNODES or
|
||||
* byVDIM). The unique_ptrs to all the ParticleVectors are stored in the
|
||||
* std::vector \ref fields.
|
||||
*
|
||||
* @par Tags:
|
||||
* Tags represent integers associated with each particle. For a given tag,
|
||||
* all particle data are stored in a single Array<int>. The unique_ptrs to all
|
||||
* the Array<int> is stored in the std::vector \ref tags.
|
||||
*
|
||||
* @par Names:
|
||||
* Each field and tag can optionally be given a name (string) to be used when
|
||||
* printing particle data in CSV format using PrintCSV(). The names of all
|
||||
* fields and tags are stored in the std::vectors \ref field_names and
|
||||
* \ref tag_names, respectively.
|
||||
*
|
||||
* @note We assume that all particles in a ParticleSet have the same number
|
||||
* of fields and tags.
|
||||
*
|
||||
* Following the example in the Particle class, we will use the
|
||||
* particles below to illustrate the data layout for \ref coords, \ref ids,
|
||||
* \ref fields, \ref tags, \ref field_names, and \ref tag_names.
|
||||
* In each case, the name of the field and tag is enclosed in '...' for
|
||||
* clarity. Additionally, we assume for this example that the particle
|
||||
* coordinates and the 'vel' field are ordered byVDIM in their respective
|
||||
* ParticleVector.
|
||||
* @anchor sample_particleset_data
|
||||
* @code
|
||||
* Particle_0: id = id0, coords = (x0, y0),
|
||||
* fields = {'mass'=m0, 'vel' = (vx0, vy0)},
|
||||
* tags = {'type'=t0, 'color'=c0}
|
||||
* Particle_1: id = id1, coords = (x1, y1),
|
||||
* fields = {'mass'=m1, 'vel' = (vx1, vy1)},
|
||||
* tags = {'type'=t1, 'color'=c1}
|
||||
* Particle_2: id = id2, coords = (x2, y2),
|
||||
* fields = {'mass'=m2, 'vel' = (vx2, vy2)},
|
||||
* tags = {'type'=t2, 'color'=c2}
|
||||
* @endcode
|
||||
*/
|
||||
class ParticleSet
|
||||
{
|
||||
public:
|
||||
using IDType = unsigned long long;
|
||||
private:
|
||||
/// Constructs an Array of size N filled with Ordering::Type o.
|
||||
static Array<Ordering::Type> GetOrderingArray(Ordering::Type o, int N);
|
||||
|
||||
/// Returns default field name for field index i. "Field_{i}"
|
||||
static std::string GetDefaultFieldName(int i);
|
||||
|
||||
/// Returns default tag name for tag index i. "Tag_{i}"
|
||||
static std::string GetDefaultTagName(int i);
|
||||
|
||||
/// Constructs an Array of size N filled with nullptr.
|
||||
static Array<const char*> GetEmptyNameArray(int N);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
static int GetRank(MPI_Comm comm_);
|
||||
static int GetSize(MPI_Comm comm_);
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
protected:
|
||||
/// Stride for IDs (used internally when new particles are added).
|
||||
/** In parallel, this defaults to the number of MPI ranks. */
|
||||
const int id_stride;
|
||||
|
||||
/// Current globally unique ID to be assigned to the next particle added.
|
||||
/** In parallel, this starts locally as the rank and increments with
|
||||
* id_stride, ensuring a global unique identifier whenever a particle is
|
||||
* added.
|
||||
*/
|
||||
IDType id_counter;
|
||||
|
||||
/** @brief Global unique IDs of particles owned by this rank.
|
||||
*
|
||||
* @details For the \ref sample_particleset_data, \ref ids would be
|
||||
* ids[0]=id0, ids[1]=id1, ids[2]=id2.
|
||||
*/
|
||||
Array<IDType> ids;
|
||||
|
||||
/** @brief Spatial coordinates of particles owned by this rank.
|
||||
*
|
||||
* @details For the \ref sample_particleset_data, \ref coords would be
|
||||
* coords=(x0,y0,x1,y1,x2,y2) assuming coords ordering is byVDIM.
|
||||
*/
|
||||
ParticleVector coords;
|
||||
|
||||
/** @brief All particle fields for particles owned by this rank.
|
||||
*
|
||||
* @details For the \ref sample_particleset_data, \ref fields would be
|
||||
* *fields[0]=(m0,m1,m2), *fields[1]=(vx0,vy0,vx1,vy1,vx2,vy2)
|
||||
* assuming fields[1] ordering is byVDIM.
|
||||
*/
|
||||
std::vector<std::unique_ptr<ParticleVector>> fields;
|
||||
|
||||
/** @brief All particle tags for particles owned by this rank.
|
||||
*
|
||||
* @details For the \ref sample_particleset_data, \ref tags would be
|
||||
* *tags[0]=(t0,t1,t2), *tags[1]=(c0,c1,c2).
|
||||
*/
|
||||
std::vector<std::unique_ptr<Array<int>>> tags;
|
||||
|
||||
/** @brief Field names, to be written when PrintCSV() is called.
|
||||
*
|
||||
* @details For the \ref sample_particleset_data, \ref field_names would be
|
||||
* field_names[0]='mass', field_names[1]='vel'.
|
||||
*/
|
||||
std::vector<std::string> field_names;
|
||||
|
||||
/** @brief Tag names, to be written when PrintCSV() is called.
|
||||
*
|
||||
* @details For the \ref sample_particleset_data, \ref tag_names would be
|
||||
* tag_names[0]='type', tag_names[1]='color'.
|
||||
*/
|
||||
std::vector<std::string> tag_names;
|
||||
|
||||
/** @brief Add particles with global identifiers \p new_ids and
|
||||
* optionally get the local indices of new particles in \p new_indices .
|
||||
*
|
||||
* @details Note the data of new particles is uninitialized and must be
|
||||
* set.
|
||||
*/
|
||||
void AddParticles(const Array<IDType> &new_ids,
|
||||
Array<int> *new_indices=nullptr);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
MPI_Comm comm;
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
|
||||
struct gslib::crystal *cr = nullptr; // gslib's internal data
|
||||
struct gslib::comm *gsl_comm = nullptr; // gslib's internal data
|
||||
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
template<std::size_t NBytes>
|
||||
static void TransferParticlesImpl(ParticleSet &pset,
|
||||
const Array<int> &send_idxs,
|
||||
const Array<unsigned int> &send_ranks);
|
||||
|
||||
using TransferParticlesType = void (*)(ParticleSet &pset,
|
||||
const Array<int> &send_idxs,
|
||||
const Array<unsigned int> &send_ranks);
|
||||
|
||||
// Specialization parameter: NBytes
|
||||
MFEM_REGISTER_KERNELS(TransferParticles, TransferParticlesType, (size_t));
|
||||
friend TransferParticles;
|
||||
struct Kernels
|
||||
{
|
||||
Kernels();
|
||||
};
|
||||
/// \endcond
|
||||
|
||||
#endif // MFEM_USE_MPI && MFEM_USE_GSLIB
|
||||
|
||||
/** @brief Update global ID of a particle.
|
||||
*
|
||||
* @details This method updates the global ID of the particle at given
|
||||
* local index after Redistribute().
|
||||
*
|
||||
* @note This method must be used very carefully as it updates global
|
||||
* ID of a particle.
|
||||
*/
|
||||
void UpdateID(int local_idx, IDType new_global_id)
|
||||
{ ids[local_idx] = new_global_id; }
|
||||
|
||||
/** @brief Create a Particle object with the same spatial dimension,
|
||||
* number of fields and field vdims, and number of tags as this ParticleSet.
|
||||
*/
|
||||
Particle CreateParticle() const;
|
||||
|
||||
/** @brief Write string in \p ss_header , followed by \p ss_data , to a
|
||||
* single file; compatible in parallel.
|
||||
*/
|
||||
void WriteToFile(const char *fname, const std::stringstream &ss_header,
|
||||
const std::stringstream &ss_data);
|
||||
|
||||
/** @brief Check if a particle could belong in this ParticleSet by
|
||||
* comparing field and tag dimension.
|
||||
*/
|
||||
bool IsValidParticle(const Particle &p) const;
|
||||
|
||||
/** @brief Hidden main constructor of ParticleSet
|
||||
*
|
||||
* @param[in] id_stride_ ID stride.
|
||||
* @param[in] id_counter_ Starting ID counter.
|
||||
* @param[in] num_particles Number of particles to initialize.
|
||||
* @param[in] dim Particle spatial dimension.
|
||||
* @param[in] coords_ordering Ordering of coordinates
|
||||
* @param[in] field_vdims Array of field vector dimensions
|
||||
* @param[in] field_orderings Array of field ordering types.
|
||||
* @param[in] field_names_ Array of field names.
|
||||
* @param[in] num_tags Number of tags to register.
|
||||
* @param[in] tag_names_ Array of tag names.
|
||||
*/
|
||||
ParticleSet(int id_stride_, IDType id_counter_, int num_particles, int dim,
|
||||
Ordering::Type coords_ordering, const Array<int> &field_vdims,
|
||||
const Array<Ordering::Type> &field_orderings,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_);
|
||||
|
||||
public:
|
||||
|
||||
/** @brief Construct a serial ParticleSet.
|
||||
*
|
||||
* @param[in] num_particles Number of particles to initialize.
|
||||
* @param[in] dim Particle spatial dimension.
|
||||
* @param[in] coords_ordering Ordering of coordinates.
|
||||
*/
|
||||
ParticleSet(int num_particles, int dim,
|
||||
Ordering::Type coords_ordering=Ordering::byVDIM);
|
||||
|
||||
/** @brief Construct a serial ParticleSet with specified fields and tags at
|
||||
* construction.
|
||||
*
|
||||
* @param[in] num_particles Number of particles to initialize.
|
||||
* @param[in] dim Particle spatial dimension.
|
||||
* @param[in] field_vdims Array of field vector dimensions.
|
||||
* @param[in] num_tags Number of tags to register.
|
||||
* @param[in] all_ordering (Optional) Ordering of coordinates and
|
||||
* field ParticleVector.
|
||||
*/
|
||||
ParticleSet(int num_particles, int dim, const Array<int> &field_vdims,
|
||||
int num_tags, Ordering::Type all_ordering=Ordering::byVDIM);
|
||||
|
||||
/** @brief Construct a serial ParticleSet with specified fields and tags at
|
||||
* construction, with names.
|
||||
*
|
||||
* @param[in] num_particles Number of particles to initialize.
|
||||
* @param[in] dim Particle spatial dimension.
|
||||
* @param[in] field_vdims Array of field vector dimensions.
|
||||
* @param[in] field_names_ Array of field names.
|
||||
* @param[in] num_tags Number of tags to register.
|
||||
* @param[in] tag_names_ Array of tag names.
|
||||
* @param[in] all_ordering (Optional) Ordering of coordinates and
|
||||
* field ParticleVector.
|
||||
*/
|
||||
ParticleSet(int num_particles, int dim, const Array<int> &field_vdims,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_,
|
||||
Ordering::Type all_ordering=Ordering::byVDIM);
|
||||
|
||||
/** @brief Comprehensive serial constructor of ParticleSet.
|
||||
*
|
||||
* @param[in] num_particles Number of particles to initialize.
|
||||
* @param[in] dim Particle spatial dimension.
|
||||
* @param[in] coords_ordering Ordering of coordinates.
|
||||
* @param[in] field_vdims Array of field vector dimensions.
|
||||
* @param[in] field_orderings Array of field ordering types.
|
||||
* @param[in] field_names_ Array of field names.
|
||||
* @param[in] num_tags Number of tags to register.
|
||||
* @param[in] tag_names_ Array of tag names.
|
||||
*/
|
||||
ParticleSet(int num_particles, int dim, Ordering::Type coords_ordering,
|
||||
const Array<int> &field_vdims,
|
||||
const Array<Ordering::Type> &field_orderings,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/** @brief Construct a parallel ParticleSet.
|
||||
*
|
||||
* @param[in] comm_ MPI communicator.
|
||||
* @param[in] rank_num_particles Number of particles to initialize.
|
||||
* @param[in] dim Particle spatial dimension.
|
||||
* @param[in] coords_ordering (Optional) Ordering of coordinates.
|
||||
*/
|
||||
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
Ordering::Type coords_ordering=Ordering::byVDIM);
|
||||
|
||||
/** @brief Construct a parallel ParticleSet with specified fields and tags
|
||||
* at construction.
|
||||
*
|
||||
* @param[in] comm_ MPI communicator.
|
||||
* @param[in] rank_num_particles # of particles to initialize on this rank.
|
||||
* @param[in] dim Particle spatial dimension.
|
||||
* @param[in] field_vdims Array of field vector dimensions.
|
||||
* @param[in] num_tags Number of tags to register.
|
||||
* @param[in] all_ordering (Optional) Ordering of coordinates and
|
||||
* field ParticleVector.
|
||||
*/
|
||||
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
const Array<int> &field_vdims, int num_tags,
|
||||
Ordering::Type all_ordering=Ordering::byVDIM);
|
||||
|
||||
/** @brief Construct a parallel ParticleSet with specified fields and tags
|
||||
* at construction, with names (for PrintCSV()).
|
||||
*
|
||||
* @param[in] comm_ MPI communicator.
|
||||
* @param[in] rank_num_particles # of particles to initialize on this rank.
|
||||
* @param[in] dim Particle spatial dimension.
|
||||
* @param[in] field_vdims Array of field vector dimension.
|
||||
* @param[in] field_names_ Array of field names.
|
||||
* @param[in] num_tags Number of tags to register.
|
||||
* @param[in] tag_names_ Array of tag names.
|
||||
* @param[in] all_ordering (Optional) Ordering of coordinates and
|
||||
* field ParticleVector.
|
||||
*/
|
||||
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
const Array<int> &field_vdims,
|
||||
const Array<const char*> &field_names_,
|
||||
int num_tags, const Array<const char*> &tag_names_,
|
||||
Ordering::Type all_ordering=Ordering::byVDIM);
|
||||
|
||||
/** @brief Comprehensive parallel constructor of ParticleSet.
|
||||
*
|
||||
* @param[in] comm_ MPI communicator.
|
||||
* @param[in] rank_num_particles # of particles to initialize on this rank.
|
||||
* @param[in] dim Particle spatial dimension.
|
||||
* @param[in] coords_ordering Ordering of coordinates.
|
||||
* @param[in] field_vdims Array of field vector dimensions.
|
||||
* @param[in] field_orderings Array of field ordering types.
|
||||
* @param[in] field_names_ Array of field names.
|
||||
* @param[in] num_tags Number of tags to register.
|
||||
* @param[in] tag_names_ Array of tag names.
|
||||
*/
|
||||
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
|
||||
Ordering::Type coords_ordering, const Array<int> &field_vdims,
|
||||
const Array<Ordering::Type> &field_orderings,
|
||||
const Array<const char*> &field_names_, int num_tags,
|
||||
const Array<const char*> &tag_names_);
|
||||
|
||||
/// Get the MPI communicator for this ParticleSet.
|
||||
MPI_Comm GetComm() const { return comm; };
|
||||
#endif // MFEM_USE_MPI
|
||||
/// Get the global number of active particles across all ranks.
|
||||
IDType GetGlobalNParticles() const;
|
||||
|
||||
/// Get the spatial dimension.
|
||||
int GetDim() const { return coords.GetVDim(); }
|
||||
|
||||
/// Get the global IDs of the active particles owned by this ParticleSet.
|
||||
const Array<IDType>& GetIDs() const { return ids; }
|
||||
|
||||
/** @brief Add a field to the ParticleSet.
|
||||
*
|
||||
* @param[in] vdim Vector dimension of the field.
|
||||
* @param[in] field_ordering (Optional) Ordering::Type of the field.
|
||||
* @param[in] field_name (Optional) Name of the field.
|
||||
*
|
||||
* @return Index of the newly-added field.
|
||||
*/
|
||||
int AddField(int vdim, Ordering::Type field_ordering=Ordering::byVDIM,
|
||||
const char* field_name=nullptr);
|
||||
|
||||
/** @brief Add a field to the ParticleSet.
|
||||
*
|
||||
* @details Same as AddField() but with different parameter order
|
||||
* for convenience
|
||||
*/
|
||||
int AddNamedField(int vdim, const char* field_name,
|
||||
Ordering::Type field_ordering=Ordering::byVDIM)
|
||||
{
|
||||
return AddField(vdim, field_ordering, field_name);
|
||||
}
|
||||
|
||||
/** @brief Add a tag to the ParticleSet.
|
||||
*
|
||||
* @param[in] tag_name (Optional) Name of the tag.
|
||||
*
|
||||
* @return Index of the newly-added tag.
|
||||
*/
|
||||
int AddTag(const char* tag_name=nullptr);
|
||||
|
||||
/// Reserve memory for \p res particles.
|
||||
/** Can help to avoid re-allocation for adding + removing particles. */
|
||||
void Reserve(int res);
|
||||
|
||||
/// Get the number of active particles currently held by this ParticleSet.
|
||||
int GetNParticles() const { return ids.Size(); }
|
||||
|
||||
/// Get the number of fields registered to particles.
|
||||
int GetNFields() const { return fields.size(); }
|
||||
|
||||
/// Get an Array<int> of the field vector-dimensions registered to particles.
|
||||
const Array<int> GetFieldVDims() const;
|
||||
|
||||
/// Get Field vector-dimension
|
||||
int FieldVDim(int f) const { return fields[f]->GetVDim(); }
|
||||
|
||||
/// Get the number of tags registered to particles.
|
||||
int GetNTags() const { return tags.size(); }
|
||||
|
||||
/// Add a particle using Particle .
|
||||
void AddParticle(const Particle &p);
|
||||
|
||||
/** @brief Add \p num_particles particles, and optionally get the local
|
||||
* indices of new particles in \p new_indices .
|
||||
*
|
||||
* @details The data of new particles is uninitialized and must be
|
||||
* set.
|
||||
*/
|
||||
void AddParticles(int num_particles, Array<int> *new_indices=nullptr);
|
||||
|
||||
/// Remove particle data specified by \p list of particle indices.
|
||||
void RemoveParticles(const Array<int> &list);
|
||||
|
||||
/// Get a reference to the coordinates ParticleVector.
|
||||
ParticleVector& Coords() { return coords; }
|
||||
|
||||
/// Get a const reference to the coordinates ParticleVector.
|
||||
const ParticleVector& Coords() const { return coords; }
|
||||
|
||||
/// Get a reference to field \p f 's ParticleVector.
|
||||
ParticleVector& Field(int f) { return *fields[f]; }
|
||||
|
||||
/// Get a const reference to field \p f 's ParticleVector.
|
||||
const ParticleVector& Field(int f) const { return *fields[f]; }
|
||||
|
||||
/// Get a reference to tag \p t 's Array<int>.
|
||||
Array<int>& Tag(int t) { return *tags[t]; }
|
||||
|
||||
/// Get a const reference to tag \p t 's Array<int>.
|
||||
const Array<int>& Tag(int t) const { return *tags[t]; }
|
||||
|
||||
/** @brief Get new Particle object with copy of data associated with
|
||||
particle \p i . */
|
||||
Particle GetParticle(int i) const;
|
||||
|
||||
/** @brief Get Particle object whose members reference the actual data
|
||||
* associated with particle \p i in this ParticleSet.
|
||||
*
|
||||
* @see IsParticleRefValid for when this method can be used.
|
||||
*
|
||||
* @warning If particles are added, removed, or redistributed after
|
||||
* invoking this, the returned Particle member references may be
|
||||
* invalidated.
|
||||
*/
|
||||
Particle GetParticleRef(int i);
|
||||
|
||||
/** @brief Determine if GetParticleRef is valid.
|
||||
*
|
||||
* If coordinates and all fields are ordered byVDIM, then returns true.
|
||||
* Otherwise, false.
|
||||
*/
|
||||
bool IsParticleRefValid() const;
|
||||
|
||||
/// Set data for particle at index \p i with data from provided particle \p p
|
||||
void SetParticle(int i, const Particle &p);
|
||||
|
||||
/** @brief Print all particle data to a comma-delimited CSV file.
|
||||
*
|
||||
* The first row contains the header. We include the particle ID,
|
||||
* owning rank (in parallel), coordinates, followed by all fields and
|
||||
* tags.
|
||||
*
|
||||
* The output can be visualized in Paraview by loading the csv files, and
|
||||
* applying the "Table To Points" filter.
|
||||
*/
|
||||
void PrintCSV(const char *fname, int precision=16);
|
||||
|
||||
/** @brief Print only particle field and tags given by \p field_idxs and
|
||||
\p tag_idxs respectively to a CSV file. */
|
||||
void PrintCSV(const char *fname, const Array<int> &field_idxs,
|
||||
const Array<int> &tag_idxs, int precision=16);
|
||||
|
||||
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
|
||||
|
||||
/** @brief Redistribute particle data to \p rank_list
|
||||
|
||||
@param[in] rank_list Array of size GetNParticles() denoting ultimate
|
||||
destination of particle data. Index = this rank
|
||||
means no data is moved.
|
||||
*/
|
||||
void Redistribute(const Array<unsigned int> &rank_list);
|
||||
|
||||
#endif // MFEM_USE_MPI && MFEM_USE_GSLIB
|
||||
|
||||
/// Destructor
|
||||
~ParticleSet();
|
||||
ParticleSet(const ParticleSet&) = delete;
|
||||
ParticleSet& operator=(const ParticleSet&) = delete;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
#endif // MFEM_PARTICLESET
|
||||
@@ -488,16 +488,10 @@ void ParBilinearForm::FormLinearSystem(
|
||||
R.Mult(x, true_X);
|
||||
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
|
||||
std::unique_ptr<ConstrainedOperator> A_constrained([&]()
|
||||
{
|
||||
Operator *op;
|
||||
Operator::FormSystemOperator(ess_tdof_list, op);
|
||||
return dynamic_cast<ConstrainedOperator*>(op);
|
||||
}());
|
||||
MFEM_ASSERT(A_constrained != nullptr, "");
|
||||
|
||||
ConstrainedOperator *A_constrained;
|
||||
Operator::FormConstrainedSystemOperator(ess_tdof_list, A_constrained);
|
||||
A_constrained->EliminateRHS(true_X, true_B);
|
||||
delete A_constrained;
|
||||
R.MultTranspose(true_B, b);
|
||||
hybridization->ReduceRHS(true_B, B);
|
||||
X.SetSize(B.Size());
|
||||
|
||||
+13
-17
@@ -646,38 +646,39 @@ const FaceRestriction *ParFiniteElementSpace::GetFaceRestriction(
|
||||
auto itr = L2F.find(key);
|
||||
if (itr != L2F.end())
|
||||
{
|
||||
return itr->second.get();
|
||||
return itr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_ptr<FaceRestriction> res;
|
||||
FaceRestriction *res;
|
||||
if (is_dg_space)
|
||||
{
|
||||
if (Conforming())
|
||||
{
|
||||
res.reset(new ParL2FaceRestriction(*this, f_ordering, type, m));
|
||||
res = new ParL2FaceRestriction(*this, f_ordering, type, m);
|
||||
}
|
||||
else
|
||||
{
|
||||
res.reset(new ParNCL2FaceRestriction(*this, f_ordering, type, m));
|
||||
res = new ParNCL2FaceRestriction(*this, f_ordering, type, m);
|
||||
}
|
||||
}
|
||||
else if (dynamic_cast<const DG_Interface_FECollection*>(fec))
|
||||
{
|
||||
res.reset(new L2InterfaceFaceRestriction(*this, f_ordering, type));
|
||||
res = new L2InterfaceFaceRestriction(*this, f_ordering, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Conforming())
|
||||
{
|
||||
res.reset(new ConformingFaceRestriction(*this, f_ordering, type));
|
||||
res = new ConformingFaceRestriction(*this, f_ordering, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
res.reset(new ParNCH1FaceRestriction(*this, f_ordering, type));
|
||||
res = new ParNCH1FaceRestriction(*this, f_ordering, type);
|
||||
}
|
||||
}
|
||||
return L2F.emplace(key, std::move(res)).first->second.get();
|
||||
L2F[key] = res;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5270,8 +5271,7 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
|
||||
MFEM_ASSERT(R->Finalized(), "");
|
||||
const int tdofs = R->Height();
|
||||
MFEM_ASSERT(tdofs == R->HostReadI()[tdofs], "");
|
||||
ltdof_ldof.SetSize(tdofs);
|
||||
ltdof_ldof.CopyFrom(R->HostReadJ());
|
||||
ltdof_ldof = Array<int>(const_cast<int*>(R->HostReadJ()), tdofs);
|
||||
{
|
||||
Table nbr_ltdof;
|
||||
gc.GetNeighborLTDofTable(nbr_ltdof);
|
||||
@@ -5294,13 +5294,9 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
|
||||
}
|
||||
Table unique_shr;
|
||||
Transpose(shared_ltdof, unique_shr, unique_ltdof.Size());
|
||||
unq_ltdof = unique_ltdof;
|
||||
// Steal I and J arrays from the unique_shr table.
|
||||
unq_shr_i.GetMemory() = unique_shr.GetIMemory();
|
||||
unq_shr_i.SetSize(unique_shr.Size()+1);
|
||||
unq_shr_j.GetMemory() = unique_shr.GetJMemory();
|
||||
unq_shr_j.SetSize(unique_shr.Size_of_connections());
|
||||
unique_shr.LoseData();
|
||||
unq_ltdof = Array<int>(unique_ltdof, unique_ltdof.Size());
|
||||
unq_shr_i = Array<int>(unique_shr.GetI(), unique_shr.Size()+1);
|
||||
unq_shr_j = Array<int>(unique_shr.GetJ(), unique_shr.Size_of_connections());
|
||||
}
|
||||
nbr_ltdof.GetJMemory().Delete();
|
||||
nbr_ltdof.LoseData();
|
||||
|
||||
@@ -483,8 +483,6 @@ public:
|
||||
const FiniteElement *GetFaceNbrFaceFE(int i) const;
|
||||
const Array<HYPRE_BigInt> &GetFaceNbrGlobalDofMapArray() { return face_nbr_glob_dof_map; }
|
||||
const HYPRE_BigInt *GetFaceNbrGlobalDofMap() { return face_nbr_glob_dof_map; }
|
||||
const Array<HYPRE_BigInt> &GetFaceNbrGlobalDofMapArray() const
|
||||
{ return face_nbr_glob_dof_map; }
|
||||
ElementTransformation *GetFaceNbrElementTransformation(int i) const
|
||||
{ return pmesh->GetFaceNbrElementTransformation(i); }
|
||||
|
||||
|
||||
+3
-153
@@ -543,22 +543,13 @@ void ParGridFunction::GetElementDofValues(int el, Vector &dof_vals) const
|
||||
}
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
|
||||
void ParGridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
{
|
||||
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
|
||||
|
||||
if (delta_c == NULL)
|
||||
{
|
||||
(*this) = std::numeric_limits<real_t>::min();
|
||||
GridFunction::ProjectCoefficient(coeff,type);
|
||||
|
||||
// Accumulate for all vdofs.
|
||||
if (pfes->GetNURBSext())
|
||||
{
|
||||
GroupCommunicator &gcomm = pfes->GroupComm();
|
||||
gcomm.Reduce<real_t>(data, GroupCommunicator::Max);
|
||||
gcomm.Bcast<real_t>(data);
|
||||
}
|
||||
GridFunction::ProjectCoefficient(coeff);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -574,147 +565,6 @@ void ParGridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
|
||||
}
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
|
||||
ProjectType type)
|
||||
{
|
||||
GridFunction::ProjectCoefficient(vcoeff, type);
|
||||
|
||||
// Accumulate for all vdofs.
|
||||
if (pfes->GetNURBSext())
|
||||
{
|
||||
GroupCommunicator &gcomm = pfes->GroupComm();
|
||||
gcomm.Reduce<real_t>(data, GroupCommunicator::Max);
|
||||
gcomm.Bcast<real_t>(data);
|
||||
}
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficientGlobalL2(Coefficient &coeff,
|
||||
real_t rtol,
|
||||
int iter)
|
||||
{
|
||||
// Define and assemble linear form
|
||||
ParLinearForm b(pfes);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(coeff));
|
||||
b.Assemble();
|
||||
|
||||
// Define and assemble bilinear form
|
||||
ParBilinearForm a(pfes);
|
||||
a.AddDomainIntegrator(new MassIntegrator());
|
||||
a.Assemble();
|
||||
|
||||
// Configure solver
|
||||
OperatorPtr A;
|
||||
Vector B, X, x(*this);
|
||||
Array<int> ess_tdof_list;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
Solver *prec = new HypreBoomerAMG;
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(rtol);
|
||||
cg.SetMaxIter(iter);
|
||||
cg.SetPrintLevel(0);
|
||||
cg.SetPreconditioner(*prec);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
delete prec;
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficientElementL2(Coefficient &coeff)
|
||||
{
|
||||
Vector Va;
|
||||
ProjectCoefficientElementL2_(coeff, *this, Va);
|
||||
|
||||
GroupCommunicator &gcomm = pfes->GroupComm();
|
||||
gcomm.Reduce<real_t>(GetData(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<real_t>(GetData());
|
||||
|
||||
gcomm.Reduce<real_t>(Va.GetData(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<real_t>(Va.GetData());
|
||||
(*this)/=Va;
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
|
||||
real_t rtol, int iter)
|
||||
{
|
||||
// Define and assemble linear form
|
||||
ParLinearForm b(pfes);
|
||||
ParBilinearForm a(pfes);
|
||||
|
||||
// Dimension argument to GetRangeType is arbitrary to be 3, could also be 2.
|
||||
if (fes->FEColl()->GetRangeType(3) == mfem::FiniteElement::VECTOR)
|
||||
{
|
||||
b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(vcoeff));
|
||||
a.AddDomainIntegrator(new VectorFEMassIntegrator());
|
||||
}
|
||||
else
|
||||
{
|
||||
b.AddDomainIntegrator(new VectorDomainLFIntegrator(vcoeff));
|
||||
a.AddDomainIntegrator(new VectorMassIntegrator());
|
||||
}
|
||||
b.Assemble();
|
||||
a.Assemble();
|
||||
|
||||
// Configure solver
|
||||
OperatorPtr A;
|
||||
Vector B, X, x(*this);
|
||||
x = 0.0;
|
||||
Array<int> ess_tdof_list;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
Solver *prec = new HypreBoomerAMG;
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(rtol);
|
||||
cg.SetMaxIter(iter);
|
||||
cg.SetPrintLevel(0);
|
||||
cg.SetPreconditioner(*prec);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
x.Print();
|
||||
delete prec;
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficientElementL2(VectorCoefficient &vcoeff)
|
||||
{
|
||||
if (fes->GetTypicalFE()->GetRangeType() == mfem::FiniteElement::VECTOR)
|
||||
{
|
||||
Vector Va;
|
||||
ProjectCoefficientElementL2_(vcoeff, *this, Va);
|
||||
|
||||
GroupCommunicator &gcomm = pfes->GroupComm();
|
||||
gcomm.Reduce<real_t>(GetData(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<real_t>(GetData());
|
||||
|
||||
gcomm.Reduce<real_t>(Va.GetData(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<real_t>(Va.GetData());
|
||||
(*this)/=Va;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array<int> vdofs(fes->GetNDofs());
|
||||
Vector x, Va, gVa(Size());
|
||||
VectorComponentCoefficient coeff(vcoeff,0);
|
||||
*this = 0.0;
|
||||
gVa = 0.0;
|
||||
for (int v = 0; v < VectorDim(); v++)
|
||||
{
|
||||
coeff.SetComponent(v);
|
||||
ProjectCoefficientElementL2_(coeff, x, Va);
|
||||
fes->GetVDofs(v, vdofs);
|
||||
SetSubVector(vdofs, x);
|
||||
gVa.SetSubVector(vdofs, Va);
|
||||
}
|
||||
|
||||
GroupCommunicator &gcomm = pfes->GroupComm();
|
||||
gcomm.Reduce<real_t>(GetData(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<real_t>(GetData());
|
||||
|
||||
gcomm.Reduce<real_t>(gVa.GetData(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<real_t>(gVa.GetData());
|
||||
*this /= gVa;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParGridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff)
|
||||
{
|
||||
// local maximal element attribute for each dof
|
||||
@@ -1557,7 +1407,7 @@ real_t L2ZZErrorEstimator(BilinearFormIntegrator &flux_integrator,
|
||||
}
|
||||
|
||||
PLBound ParGridFunction::GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor, const int vdim) const
|
||||
const int ref_factor, const int vdim)
|
||||
{
|
||||
PLBound plb = GridFunction::GetBounds(lower, upper, ref_factor, vdim);
|
||||
int siz = vdim > 0 ? 1 : fes->GetVDim();
|
||||
|
||||
+2
-22
@@ -72,10 +72,6 @@ public:
|
||||
|
||||
ParGridFunction(ParFiniteElementSpace *pf) : GridFunction(pf), pfes(pf) { }
|
||||
|
||||
/// Same as above but specify the device memory type
|
||||
ParGridFunction(ParFiniteElementSpace *pf, MemoryType mt) :
|
||||
GridFunction(pf, mt), pfes(pf) { }
|
||||
|
||||
/// Construct a ParGridFunction using previously allocated array @a data.
|
||||
/** The ParGridFunction does not assume ownership of @a data which is assumed
|
||||
to be of size at least `pf->GetVSize()`. Similar to the GridFunction and
|
||||
@@ -261,11 +257,7 @@ public:
|
||||
void GetElementDofValues(int el, Vector &dof_vals) const override;
|
||||
|
||||
using GridFunction::ProjectCoefficient;
|
||||
void ProjectCoefficient(Coefficient &coeff,
|
||||
ProjectType type = ProjectType::DEFAULT) override;
|
||||
|
||||
void ProjectCoefficient(VectorCoefficient &vcoeff,
|
||||
ProjectType type = ProjectType::DEFAULT) override;
|
||||
void ProjectCoefficient(Coefficient &coeff) override;
|
||||
|
||||
using GridFunction::ProjectDiscCoefficient;
|
||||
/** @brief Project a discontinuous vector coefficient as a grid function on
|
||||
@@ -290,18 +282,6 @@ public:
|
||||
void ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
|
||||
const Array<int> &bdr_attr) override;
|
||||
|
||||
void ProjectCoefficientGlobalL2(Coefficient &coeff,
|
||||
real_t rtol = 1e-12,
|
||||
int iter = 1000) override;
|
||||
|
||||
void ProjectCoefficientElementL2(Coefficient &coeff) override;
|
||||
|
||||
void ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
|
||||
real_t rtol = 1e-12,
|
||||
int iter = 1000) override;
|
||||
|
||||
void ProjectCoefficientElementL2(VectorCoefficient &vcoeff) override;
|
||||
|
||||
/// @brief Returns ||u_ex - u_h||_L1 in parallel for H1 or L2 elements
|
||||
///
|
||||
/// @see GridFunction::ComputeL1Error(Coefficient *exsol[],
|
||||
@@ -607,7 +587,7 @@ public:
|
||||
/// PLBound object used to compute the bounds. Note: if vdim < 1, we compute
|
||||
/// the bounds for each vector dimension.
|
||||
PLBound GetBounds(Vector &lower, Vector &upper,
|
||||
const int ref_factor=1, const int vdim=-1) const override;
|
||||
const int ref_factor=1, const int vdim=-1) override;
|
||||
|
||||
/** Save the local portion of the ParGridFunction. This differs from the
|
||||
serial GridFunction::Save in that it takes into account the signs of
|
||||
|
||||
@@ -994,6 +994,7 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
{
|
||||
if ( face.IsConforming() )
|
||||
{
|
||||
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
|
||||
SetFaceDofsScatterIndices1(face,f_ind);
|
||||
if ( m==L2FaceValues::DoubleValued )
|
||||
{
|
||||
@@ -1009,6 +1010,7 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
}
|
||||
else // Non-conforming face
|
||||
{
|
||||
interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
|
||||
SetFaceDofsScatterIndices1(face,f_ind);
|
||||
if ( m==L2FaceValues::DoubleValued )
|
||||
{
|
||||
@@ -1026,6 +1028,7 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
}
|
||||
else if (type==FaceType::Boundary && face.IsBoundary())
|
||||
{
|
||||
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
|
||||
SetFaceDofsScatterIndices1(face,f_ind);
|
||||
if ( m==L2FaceValues::DoubleValued )
|
||||
{
|
||||
@@ -1043,6 +1046,10 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
{
|
||||
gather_offsets[i] += gather_offsets[i - 1];
|
||||
}
|
||||
|
||||
// Transform the interpolation matrix map into a contiguous memory structure.
|
||||
interpolations.LinearizeInterpolatorMapIntoVector();
|
||||
interpolations.InitializeNCInterpConfig();
|
||||
}
|
||||
|
||||
void ParNCL2FaceRestriction::ComputeGatherIndices()
|
||||
|
||||
@@ -326,7 +326,9 @@ public:
|
||||
@param[in] keep_nbr_block When set to true the SparseMatrix will
|
||||
include the rows (in addition to the columns)
|
||||
corresponding to face-neighbor dofs. The
|
||||
default behavior is to disregard those rows. */
|
||||
default behavior is to disregard those rows.
|
||||
|
||||
@warning This method is not implemented yet. */
|
||||
void FillI(SparseMatrix &mat,
|
||||
const bool keep_nbr_block = false) const override;
|
||||
|
||||
@@ -362,7 +364,9 @@ public:
|
||||
@param[in] keep_nbr_block When set to true the SparseMatrix will
|
||||
include the rows (in addition to the columns)
|
||||
corresponding to face-neighbor dofs. The
|
||||
default behavior is to disregard those rows. */
|
||||
default behavior is to disregard those rows.
|
||||
|
||||
@warning This method is not implemented yet. */
|
||||
void FillJAndData(const Vector &fea_data,
|
||||
SparseMatrix &mat,
|
||||
const bool keep_nbr_block = false) const override;
|
||||
|
||||
+7
-40
@@ -56,20 +56,6 @@ void QuadratureFunction::Save(std::ostream &os) const
|
||||
os.flush();
|
||||
}
|
||||
|
||||
void QuadratureFunction::ProjectGridFunctionFallback(const GridFunction &gf)
|
||||
{
|
||||
if (gf.VectorDim() == 1)
|
||||
{
|
||||
GridFunctionCoefficient coeff(&gf);
|
||||
coeff.Coefficient::Project(*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorGridFunctionCoefficient coeff(&gf);
|
||||
coeff.VectorCoefficient::Project(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void QuadratureFunction::ProjectGridFunction(const GridFunction &gf)
|
||||
{
|
||||
SetVDim(gf.VectorDim());
|
||||
@@ -82,23 +68,14 @@ void QuadratureFunction::ProjectGridFunction(const GridFunction &gf)
|
||||
ElementDofOrdering::LEXICOGRAPHIC :
|
||||
ElementDofOrdering::NATIVE;
|
||||
|
||||
// Use quadrature interpolator to go from E-vector to Q-vector
|
||||
const QuadratureInterpolator *qi =
|
||||
gf_fes.GetQuadratureInterpolator(*qs_elem);
|
||||
|
||||
// If quadrature interpolator doesn't support this space, then fallback
|
||||
// on slower (non-device) version, and return early.
|
||||
if (!qi)
|
||||
{
|
||||
ProjectGridFunctionFallback(gf);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use element restriction to go from L-vector to E-vector
|
||||
const Operator *R = gf_fes.GetElementRestriction(ordering);
|
||||
Vector e_vec(R->Height());
|
||||
R->Mult(gf, e_vec);
|
||||
|
||||
// Use quadrature interpolator to go from E-vector to Q-vector
|
||||
const QuadratureInterpolator *qi =
|
||||
gf_fes.GetQuadratureInterpolator(*qs_elem);
|
||||
qi->SetOutputLayout(QVectorLayout::byVDIM);
|
||||
qi->DisableTensorProducts(!use_tensor_products);
|
||||
qi->PhysValues(e_vec, *this);
|
||||
@@ -106,25 +83,12 @@ void QuadratureFunction::ProjectGridFunction(const GridFunction &gf)
|
||||
else if (auto *qs_face = dynamic_cast<FaceQuadratureSpace*>(qspace))
|
||||
{
|
||||
const FiniteElementSpace &gf_fes = *gf.FESpace();
|
||||
const FaceType face_type = qs_face->GetFaceType();
|
||||
const bool use_tensor_products = UsesTensorBasis(gf_fes);
|
||||
const ElementDofOrdering ordering = use_tensor_products ?
|
||||
ElementDofOrdering::LEXICOGRAPHIC :
|
||||
ElementDofOrdering::NATIVE;
|
||||
|
||||
// Use quadrature interpolator to go from E-vector to Q-vector
|
||||
const FaceQuadratureInterpolator *qi =
|
||||
gf_fes.GetFaceQuadratureInterpolator(qspace->GetIntRule(0), face_type);
|
||||
|
||||
// If quadrature interpolator doesn't support this space, then fallback
|
||||
// on slower (non-device) version, and return early. Also, currently,
|
||||
// ElementDofOrdering::NATIVE in FaceRestriction, so fall back in that
|
||||
// case too.
|
||||
if (qi == nullptr || ordering == ElementDofOrdering::NATIVE)
|
||||
{
|
||||
ProjectGridFunctionFallback(gf);
|
||||
return;
|
||||
}
|
||||
const FaceType face_type = qs_face->GetFaceType();
|
||||
|
||||
// Use element restriction to go from L-vector to E-vector
|
||||
const Operator *R = gf_fes.GetFaceRestriction(
|
||||
@@ -132,6 +96,9 @@ void QuadratureFunction::ProjectGridFunction(const GridFunction &gf)
|
||||
Vector e_vec(R->Height());
|
||||
R->Mult(gf, e_vec);
|
||||
|
||||
// Use quadrature interpolator to go from E-vector to Q-vector
|
||||
const FaceQuadratureInterpolator *qi =
|
||||
gf_fes.GetFaceQuadratureInterpolator(qspace->GetIntRule(0), face_type);
|
||||
qi->SetOutputLayout(QVectorLayout::byVDIM);
|
||||
qi->DisableTensorProducts(!use_tensor_products);
|
||||
qi->Values(e_vec, *this);
|
||||
|
||||
@@ -27,8 +27,6 @@ protected:
|
||||
bool own_qspace; ///< Does this own the associated QuadratureSpaceBase?
|
||||
int vdim; ///< Vector dimension.
|
||||
|
||||
void ProjectGridFunctionFallback(const GridFunction &gf);
|
||||
|
||||
public:
|
||||
/// Default constructor, results in an empty vector.
|
||||
QuadratureFunction() : qspace(nullptr), own_qspace(false), vdim(0)
|
||||
|
||||
+1
-7
@@ -50,13 +50,7 @@ QuadratureInterpolator::DetKernelType
|
||||
QuadratureInterpolator::DetKernels::Fallback(
|
||||
int DIM, int SDIM, int D1D, int Q1D)
|
||||
{
|
||||
if (DIM == 1)
|
||||
{
|
||||
if (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<0,0,2>; }
|
||||
else if (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<0,0,3>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D; }
|
||||
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface; }
|
||||
else if (DIM == 3)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user