Compare commits
116
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b13e46bc5 | ||
|
|
de114112b5 | ||
|
|
ed1c21f656 | ||
|
|
811254989f | ||
|
|
1f639a826f | ||
|
|
41e6f97bc7 | ||
|
|
7045b49973 | ||
|
|
91a121e427 | ||
|
|
1f6d6ba82a | ||
|
|
379ec4a393 | ||
|
|
34895e545e | ||
|
|
e0659eecd4 | ||
|
|
20b774792a | ||
|
|
227bbe0283 | ||
|
|
88e8055d54 | ||
|
|
8e968658b5 | ||
|
|
978e2bbd99 | ||
|
|
6465d872b5 | ||
|
|
0439e51f6b | ||
|
|
e2917c9e15 | ||
|
|
f0ea358c36 | ||
|
|
c26460c0ce | ||
|
|
9975f4c9db | ||
|
|
b29d5856c2 | ||
|
|
ce54fdf7b4 | ||
|
|
7ca58c106a | ||
|
|
63056ab08b | ||
|
|
cd9456db2a | ||
|
|
7a8fb3ca79 | ||
|
|
dc251d9518 | ||
|
|
2c118aaf85 | ||
|
|
e0e32976ec | ||
|
|
ac71a377e1 | ||
|
|
ac9b4d3d38 | ||
|
|
6be47674d6 | ||
|
|
75b6f2ac19 | ||
|
|
76c805cbad | ||
|
|
592f87591d | ||
|
|
5decf944cb | ||
|
|
6eccede39e | ||
|
|
f8fd930618 | ||
|
|
c450622e6e | ||
|
|
416536eb9d | ||
|
|
55e42eeefe | ||
|
|
a96319e0be | ||
|
|
fa89c5e98c | ||
|
|
0980bda63b | ||
|
|
a1758e51e5 | ||
|
|
ccf84aab7c | ||
|
|
82abd48bba | ||
|
|
287cb24d0a | ||
|
|
d4374a9d5f | ||
|
|
dcd3a25730 | ||
|
|
0f99528c62 | ||
|
|
ddfd74e899 | ||
|
|
0248720eeb | ||
|
|
feded39641 | ||
|
|
09128b9a5d | ||
|
|
68383b462b | ||
|
|
24d5609585 | ||
|
|
670a3f9a45 | ||
|
|
7baae02d65 | ||
|
|
728a0f313b | ||
|
|
1bb624e2a8 | ||
|
|
ee7ccd6464 | ||
|
|
a3ae5a6f01 | ||
|
|
9243d00549 | ||
|
|
4fe3db5a5f | ||
|
|
55bb710cba | ||
|
|
7ad6939454 | ||
|
|
60cc94e5a1 | ||
|
|
9122ac1839 | ||
|
|
864186117d | ||
|
|
35de169fd0 | ||
|
|
0a3184ab31 | ||
|
|
a438e09caf | ||
|
|
ea03a86df2 | ||
|
|
6ef7a9e6fb | ||
|
|
db7dd30d32 | ||
|
|
a1fe3a19b1 | ||
|
|
213ccd7a4e | ||
|
|
8e78471fdf | ||
|
|
c0f8501950 | ||
|
|
c31510289f | ||
|
|
2b14134496 | ||
|
|
794a5fbfc2 | ||
|
|
746a62f017 | ||
|
|
26e9057f02 | ||
|
|
16dfa11f27 | ||
|
|
c7774e3c1c | ||
|
|
194f3d8140 | ||
|
|
3f9b44a9cd | ||
|
|
caa973d6a0 | ||
|
|
ad40704e20 | ||
|
|
d3470c07c9 | ||
|
|
06a15cb7a9 | ||
|
|
d19ff6c676 | ||
|
|
d85fbc6504 | ||
|
|
29346a87b6 | ||
|
|
3464f7a004 | ||
|
|
7de48e47ad | ||
|
|
70814c640b | ||
|
|
e9d3ae80f7 | ||
|
|
c8efc23c12 | ||
|
|
f26eb33252 | ||
|
|
05e622f837 | ||
|
|
e9f84b033f | ||
|
|
542467fd6a | ||
|
|
5986542e3d | ||
|
|
5163313285 | ||
|
|
2201f3354a | ||
|
|
83fd119b95 | ||
|
|
4aecb86d71 | ||
|
|
776a4c1815 | ||
|
|
c870d7dc1c | ||
|
|
8519889074 |
+5
-1
@@ -652,6 +652,8 @@ foreach(TPL IN LISTS MFEM_TPLS)
|
||||
endif()
|
||||
endforeach(TPL)
|
||||
|
||||
# reverse to remove the first instance of entries in TPL_LIBRARIES
|
||||
# so later duplicates are kept (for dependency ordering)
|
||||
list(REVERSE TPL_LIBRARIES)
|
||||
list(REMOVE_DUPLICATES TPL_LIBRARIES)
|
||||
list(REVERSE TPL_LIBRARIES)
|
||||
@@ -1015,5 +1017,7 @@ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data
|
||||
# Create 'config.mk' from 'config.mk.in' for the build and install locations and
|
||||
# define install rules for 'config.mk' and 'test.mk'
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
|
||||
option(MFEM_EXPORT_GPU_CONFIG "Export config.mk for GPU-enabled downstream packages" ON)
|
||||
endif()
|
||||
mfem_export_mk_files()
|
||||
|
||||
@@ -701,7 +701,6 @@ endfunction(mfem_find_library)
|
||||
# Extract compile and link options needed by the given target.
|
||||
#
|
||||
function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
|
||||
|
||||
if (NOT TARGET ${Target})
|
||||
return()
|
||||
endif()
|
||||
@@ -799,7 +798,12 @@ function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
|
||||
# message(STATUS "Lib = ${Lib}")
|
||||
# Filter-out generator expressions
|
||||
if (NOT ("${Lib}" MATCHES "^\\$"))
|
||||
list(APPEND LinkOpts "${Lib}")
|
||||
if(NOT ("${Lib}" STREQUAL "dl"))
|
||||
list(APPEND LinkOpts "${Lib}")
|
||||
else()
|
||||
# for some reason libdl doesn't include the "-l"
|
||||
list(APPEND LinkOpts "-ldl")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
mfem_get_target_options(${Lib} COpts LOpts)
|
||||
@@ -888,9 +892,18 @@ function(mfem_export_mk_files)
|
||||
set(${var} NO)
|
||||
endif()
|
||||
endforeach()
|
||||
# TODO: Add support for MFEM_USE_CUDA=YES
|
||||
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
|
||||
set(MFEM_HOST_CXX ${MFEM_CXX})
|
||||
if (MFEM_USE_CUDA AND MFEM_EXPORT_GPU_CONFIG)
|
||||
set(MFEM_CXX ${CMAKE_CUDA_COMPILER})
|
||||
if(MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
set(MFEM_HOST_CXX ${CMAKE_CUDA_HOST_COMPILER})
|
||||
else()
|
||||
set(MFEM_HOST_CXX ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
else()
|
||||
# mfem doesn't use enable_language(HIP)
|
||||
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
|
||||
set(MFEM_HOST_CXX ${CMAKE_CXX_COMPILER})
|
||||
endif()
|
||||
set(MFEM_CPPFLAGS "")
|
||||
get_target_property(cxx_std mfem CXX_STANDARD)
|
||||
# For now, we ignore the setting of the CXX_EXTENSIONS property. If this
|
||||
@@ -900,6 +913,50 @@ function(mfem_export_mk_files)
|
||||
string(STRIP
|
||||
"${cxx_std_flag} ${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
|
||||
MFEM_CXXFLAGS)
|
||||
if(MFEM_EXPORT_GPU_CONFIG)
|
||||
if (MFEM_USE_CUDA)
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} ${CMAKE_CUDA_FLAGS}")
|
||||
if (MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
set(MFEM_CXXFLAGS "-x=cu ${MFEM_CXXFLAGS} -ccbin ${CMAKE_CXX_COMPILER} --forward-unknown-to-host-compiler")
|
||||
# The following intentionally hides CUDA deprecation warnings
|
||||
foreach(ENTRY IN LISTS CUDAToolkit_INCLUDE_DIRS)
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -isystem ${ENTRY}")
|
||||
endforeach()
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0)
|
||||
# architecture flags not part of CMAKE_CUDA_FLAGS
|
||||
if ("all" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "native" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "all-major" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}")
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -arch=${CMAKE_CUDA_ARCHITECTURES}")
|
||||
else()
|
||||
foreach (ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
|
||||
set(MFEM_CXXFLAGS
|
||||
"${MFEM_CXXFLAGS} -gencode arch=compute_${ENTRY},code=sm_${ENTRY}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -xcuda --cuda-path=${CUDAToolkit_LIBRARY_ROOT}")
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0)
|
||||
# architecture flags not part of CMAKE_CUDA_FLAGS
|
||||
if ("all" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "native" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
|
||||
OR "all-major" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}")
|
||||
# TODO: not supported
|
||||
else()
|
||||
foreach(ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
|
||||
set(MFEM_CXXFLAGS "-cuda-gpu-arch=sm_${ENTRY} ${MFEM_CXXFLAGS}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
elseif (MFEM_USE_HIP)
|
||||
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -xhip")
|
||||
foreach(ENTRY IN LISTS CMAKE_HIP_ARCHITECTURES)
|
||||
set(MFEM_CXXFLAGS "--offload-arch=${ENTRY} ${MFEM_CXXFLAGS}")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
set(MFEM_TPLFLAGS "")
|
||||
foreach(dir ${TPL_INCLUDE_DIRS})
|
||||
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} -I${dir}")
|
||||
@@ -930,6 +987,9 @@ function(mfem_export_mk_files)
|
||||
set(MFEM_SHARED NO)
|
||||
set(MFEM_STATIC YES)
|
||||
endif()
|
||||
if (MFEM_USE_CUDA)
|
||||
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} -lcudart")
|
||||
endif()
|
||||
set(MFEM_BUILD_TAG "${CMAKE_SYSTEM}")
|
||||
set(MFEM_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||
# For the next 4 variables, these are the values for the build-tree version of
|
||||
@@ -938,8 +998,15 @@ function(mfem_export_mk_files)
|
||||
set(MFEM_LIB_DIR "${PROJECT_BINARY_DIR}")
|
||||
set(MFEM_TEST_MK "${PROJECT_SOURCE_DIR}/config/test.mk")
|
||||
set(MFEM_CONFIG_EXTRA "MFEM_BUILD_DIR ?= ${PROJECT_BINARY_DIR}")
|
||||
# TODO: CUDA/HIP support:
|
||||
set(MFEM_XLINKER "${CMAKE_CXX_LINKER_WRAPPER_FLAG}")
|
||||
if (MFEM_USE_CUDA AND MFEM_EXPORT_GPU_CONFIG)
|
||||
if (MFEM_CUDA_COMPILER_IS_NVCC)
|
||||
set(MFEM_XLINKER "-Xlinker=")
|
||||
else()
|
||||
set(MFEM_XLINKER "${CMAKE_CUDA_LINKER_WRAPPER_FLAG}")
|
||||
endif()
|
||||
else()
|
||||
set(MFEM_XLINKER "${CMAKE_CXX_LINKER_WRAPPER_FLAG}")
|
||||
endif()
|
||||
set(MFEM_MPIEXEC ${MPIEXEC})
|
||||
if (NOT MFEM_MPIEXEC)
|
||||
set(MFEM_MPIEXEC "mpirun")
|
||||
@@ -987,16 +1054,21 @@ function(mfem_export_mk_files)
|
||||
# handle interfaces (e.g., SCOREC::apf)
|
||||
if ("${lib}" MATCHES "SCOREC::.*" OR "${lib}" MATCHES "Ginkgo::.*" OR "${lib}" MATCHES "ParMoonolith::.*")
|
||||
elseif (TARGET "${lib}")
|
||||
mfem_get_target_options(${lib} CompileOpts LinkOpts)
|
||||
mfem_get_target_options(${lib} CompileOpts2 LinkOpts2)
|
||||
# remove generator expressions
|
||||
string(GENEX_STRIP "${CompileOpts2}" CompileOpts)
|
||||
string(GENEX_STRIP "${LinkOpts2}" LinkOpts)
|
||||
# Removing duplicates may lead to issues:
|
||||
# list(REMOVE_DUPLICATES CompileOpts)
|
||||
# list(REMOVE_DUPLICATES LinkOpts)
|
||||
string(REPLACE ";" " " COpts "${CompileOpts}")
|
||||
string(REPLACE ";" " " LOpts "${LinkOpts}")
|
||||
# message(STATUS "${lib}[COpts]: '${COpts}'")
|
||||
# message(STATUS "${lib}[LOpts]: '${LOpts}'")
|
||||
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} ${COpts}")
|
||||
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${LOpts}")
|
||||
# message(WARNING "${lib}[LinkOpts]: ${LinkOpts}")
|
||||
# message(WARNING "${lib}[CompileOpts]: ${CompileOpts}")
|
||||
foreach(LOpt IN LISTS LinkOpts)
|
||||
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${LOpt}")
|
||||
endforeach()
|
||||
foreach(COpt IN LISTS CompileOpts)
|
||||
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} ${COpt}")
|
||||
endforeach()
|
||||
# message(FATAL_ERROR "***** interface lib found ... exiting *****")
|
||||
# handle static and shared libs
|
||||
elseif ("${suffix}" STREQUAL "${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||
@@ -1004,7 +1076,7 @@ function(mfem_export_mk_files)
|
||||
get_filename_component(fullLibName ${lib} NAME_WE)
|
||||
string(REGEX REPLACE "^lib" "" libname ${fullLibName})
|
||||
set(MFEM_EXT_LIBS
|
||||
"${MFEM_EXT_LIBS} ${shared_link_flag}${dir} -L${dir} -l${libname}")
|
||||
"${MFEM_EXT_LIBS} ${shared_link_flag}${dir} -L${dir} -l${libname}")
|
||||
else()
|
||||
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${lib}")
|
||||
endif()
|
||||
@@ -1013,7 +1085,7 @@ function(mfem_export_mk_files)
|
||||
# Create the build-tree version of 'config.mk'
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/config/config.mk.in"
|
||||
"${PROJECT_BINARY_DIR}/config/config.mk")
|
||||
"${PROJECT_BINARY_DIR}/config/config.mk" @ONLY)
|
||||
# Copy 'test.mk' from the source-tree to the build-tree
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/config/test.mk"
|
||||
@@ -1031,7 +1103,7 @@ function(mfem_export_mk_files)
|
||||
# Create the install-tree version of 'config.mk'
|
||||
configure_file(
|
||||
"${PROJECT_SOURCE_DIR}/config/config.mk.in"
|
||||
"${PROJECT_BINARY_DIR}/config/config-install.mk")
|
||||
"${PROJECT_BINARY_DIR}/config/config-install.mk" @ONLY)
|
||||
|
||||
# Install rules for 'config.mk' and 'test.mk'
|
||||
install(FILES ${PROJECT_SOURCE_DIR}/config/test.mk
|
||||
|
||||
@@ -213,6 +213,7 @@ set(HDRS
|
||||
dfem/qfunction_apply.hpp
|
||||
dfem/qfunction_transform.hpp
|
||||
dfem/tuple.hpp
|
||||
dfem/univarsolvers.hpp
|
||||
dfem/util.hpp
|
||||
eltrans.hpp
|
||||
estimators.hpp
|
||||
|
||||
@@ -82,6 +82,25 @@ public:
|
||||
/// underlying #fes
|
||||
int VectorDim() const;
|
||||
|
||||
/// Copy assignment. Only the data of the base class Vector is copied.
|
||||
/** It is assumed that this object and @a rhs use FiniteElementSpace%s that
|
||||
have the same size.
|
||||
|
||||
@note Defining this method overwrites the implicitly defined copy
|
||||
assignment operator. */
|
||||
ComplexGridFunction &operator=(const ComplexGridFunction &rhs)
|
||||
{ return operator=((const Vector &)rhs); }
|
||||
|
||||
/// Copy the data from @a v.
|
||||
/** The size of @a v must be equal to double of the size of the associated
|
||||
FiniteElementSpace #fes. */
|
||||
ComplexGridFunction &operator=(const Vector &v)
|
||||
{
|
||||
MFEM_ASSERT(fes && v.Size() == 2*fes->GetVSize(), "");
|
||||
Vector::operator=(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Assign constant values to the ComplexGridFunction data.
|
||||
ComplexGridFunction &operator=(const std::complex<real_t> & value)
|
||||
{ *gfr = value.real(); *gfi = value.imag(); return *this; }
|
||||
|
||||
+11
-8
@@ -90,8 +90,8 @@ void map_quadrature_data_to_fields_impl(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor");
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
" for this field descriptor");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,8 +169,9 @@ void map_quadrature_data_to_fields_tensor_impl_1d(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
"for this field descriptor with sum factorization on"
|
||||
" tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,8 +307,9 @@ void map_quadrature_data_to_fields_tensor_impl_2d(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
" for this field descriptor with sum factorization on"
|
||||
" tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,8 +494,9 @@ void map_quadrature_data_to_fields_tensor_impl_3d(
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("quadrature data mapping to field is not implemented for"
|
||||
" this field descriptor with sum factorization on tensor product elements");
|
||||
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
|
||||
" for this field descriptor with sum factorization on"
|
||||
" tensor product elements");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @file univarsolvers.hpp
|
||||
*
|
||||
* @brief Solvers of functions of a single variable suitable for use in ∂FEM q-functions.
|
||||
*/
|
||||
|
||||
#ifndef MFEM_UNIVARSOLVERS
|
||||
#define MFEM_UNIVARSOLVERS
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include "../../config/config.hpp"
|
||||
#include "../../general/enzyme.hpp"
|
||||
#include "../../general/error.hpp"
|
||||
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
|
||||
// Currently needed to work around a bug in LLVM
|
||||
extern void __enzyme_double(void*, size_t);
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace future
|
||||
{
|
||||
/// Representation of root search bounds
|
||||
struct Bounds
|
||||
{
|
||||
real_t lower, upper;
|
||||
};
|
||||
|
||||
|
||||
/// Settings for univariate solver
|
||||
struct SolverSettings
|
||||
{
|
||||
real_t residual_abs_tol; ///< Tolerance for convergence check on absolute value of residual
|
||||
real_t residual_rel_tol; ///< Tolerance for convergence check on absolute value of current residual relative to absolute value of residual at initial guess
|
||||
Bounds bounds; ///< Bounds on root
|
||||
int max_iters = 50;
|
||||
};
|
||||
} // namespace future
|
||||
|
||||
namespace internal
|
||||
{
|
||||
/// @cond DO_NOT_DOCUMENT
|
||||
|
||||
using future::SolverSettings;
|
||||
|
||||
// The noinline attribute is neccessary for Enzyme. If this function were to be
|
||||
// inlined in the calling function, The custom derivative rules would not be
|
||||
// found (since the function they refer to would no longer exist).
|
||||
template <auto f, typename T>
|
||||
__attribute__((noinline))
|
||||
MFEM_HOST_DEVICE void SolveNewtonBisection_impl(const real_t* x0_ptr,
|
||||
const T* p_ptr, const SolverSettings* settings_ptr, real_t* x_ptr)
|
||||
{
|
||||
int max_iters = settings_ptr->max_iters;
|
||||
|
||||
const real_t& x0 = *x0_ptr;
|
||||
const T& p = *p_ptr;
|
||||
const SolverSettings& settings = *settings_ptr;
|
||||
const real_t& left_bracket = settings.bounds.lower;
|
||||
const real_t& right_bracket = settings.bounds.upper;
|
||||
real_t& x = *x_ptr;
|
||||
using std::abs;
|
||||
|
||||
auto fprime = [&p](real_t x)
|
||||
{
|
||||
real_t x_dot = 1.0;
|
||||
return __enzyme_fwddiff<real_t>((void*)+f, enzyme_dup, x, x_dot, enzyme_const,
|
||||
p);
|
||||
};
|
||||
|
||||
real_t fl = f(left_bracket, p);
|
||||
real_t fh = f(right_bracket, p);
|
||||
|
||||
// handle corner cases where one of the brackets is the root
|
||||
if (abs(fl) < settings.residual_abs_tol)
|
||||
{
|
||||
x = left_bracket;
|
||||
return;
|
||||
}
|
||||
else if (abs(fh) < settings.residual_abs_tol)
|
||||
{
|
||||
x = right_bracket;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fl * fh > 0)
|
||||
{
|
||||
MFEM_WARNING("Root is not bracketed, solver may diverge.");
|
||||
}
|
||||
|
||||
// clamp initial guess within root brackets
|
||||
x = x0 > right_bracket? right_bracket : x0 < left_bracket? left_bracket : x0;
|
||||
|
||||
// Orient search so that f(xl) < 0
|
||||
real_t xl = left_bracket;
|
||||
real_t xh = right_bracket;
|
||||
if (fl > 0.0)
|
||||
{
|
||||
xl = right_bracket;
|
||||
xh = left_bracket;
|
||||
real_t tmp = fl;
|
||||
fl = fh;
|
||||
fh = tmp;
|
||||
}
|
||||
|
||||
real_t dx_old = abs(right_bracket - left_bracket);
|
||||
real_t dx = dx_old;
|
||||
x = x0;
|
||||
real_t r = f(x, p);
|
||||
real_t dr_dx = fprime(x);
|
||||
real_t r0 = r;
|
||||
for (int i = 0; i < max_iters; i++)
|
||||
{
|
||||
if ((((x - xh) * dr_dx - r)*((x - xl)*dr_dx - r) >= 0.0) ||
|
||||
// Newton out of range
|
||||
(std::abs(2.0*r) > std::abs(
|
||||
dx_old*dr_dx))) // Newton decreasing bracket slower than bisection
|
||||
{
|
||||
// Take bisection step
|
||||
dx_old = dx;
|
||||
dx = 0.5*(xh - xl);
|
||||
real_t x_old = x;
|
||||
x = xl + dx;
|
||||
if (x == x_old) { return; }
|
||||
}
|
||||
else
|
||||
{
|
||||
// Take Newton step
|
||||
dx_old = dx;
|
||||
dx = -r/dr_dx;
|
||||
real_t x_old = x;
|
||||
x += dx;
|
||||
if (x == x_old) { return; }
|
||||
}
|
||||
|
||||
// update residual and jacobian
|
||||
r = f(x, p);
|
||||
dr_dx = fprime(x);
|
||||
|
||||
// Check convergence
|
||||
if (abs(r) < settings.residual_rel_tol*r0 ||
|
||||
abs(r) < settings.residual_abs_tol)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update bracket
|
||||
if (r < 0.0)
|
||||
{
|
||||
xl = x;
|
||||
fl = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
xh = x;
|
||||
fh = r;
|
||||
}
|
||||
}
|
||||
MFEM_ABORT("Univariate solve did not converge.");
|
||||
}
|
||||
|
||||
template <auto f, typename T>
|
||||
void SolveNewtonBisection_impl_fwddiff(const real_t* x0,
|
||||
const real_t* /* unused shadow */,
|
||||
const T* p, const T* dp,
|
||||
const SolverSettings* settings, const SolverSettings* /* unused shadow */,
|
||||
real_t* x, real_t* dx)
|
||||
{
|
||||
SolveNewtonBisection_impl<f>(x0, p, settings, x);
|
||||
real_t dfdx = __enzyme_fwddiff<real_t>((void*)+f, enzyme_dup, *x, 1.0,
|
||||
enzyme_const, *p);
|
||||
real_t dfdp = __enzyme_fwddiff<real_t>((void*)+f, enzyme_const, *x, enzyme_dup,
|
||||
*p, *dp);
|
||||
*dx = -dfdp/dfdx;
|
||||
}
|
||||
|
||||
|
||||
template<auto f, typename T>
|
||||
void SolveNewtonBisection_impl_aug(const real_t* x0, real_t* x0_bar,
|
||||
const T* p, T* p_bar,
|
||||
const SolverSettings* settings, SolverSettings* settings_bar,
|
||||
real_t* x, real_t* x_bar)
|
||||
{
|
||||
SolveNewtonBisection_impl<f>(x0, p, settings, x);
|
||||
}
|
||||
|
||||
// Change the residual function to return-by-reference so that there is a
|
||||
// slot to provide the downstream cotangent (ie the shadow for y)
|
||||
// in the reverse mode call.
|
||||
template<auto f, typename T>
|
||||
void rbr_wrapper(real_t x, T& p, real_t& y)
|
||||
{
|
||||
y = f(x, p);
|
||||
}
|
||||
|
||||
template<auto f, typename T>
|
||||
void SolveNewtonBisection_impl_rev(const real_t* x0, real_t* x0_bar,
|
||||
const T* p, T* p_bar,
|
||||
const SolverSettings* settings, SolverSettings* settings_bar,
|
||||
real_t* x, real_t* x_bar)
|
||||
{
|
||||
real_t drdx = __enzyme_fwddiff<real_t>((void*)+f, enzyme_dup, *x, 1.0,
|
||||
enzyme_const, *p);
|
||||
real_t lambda = -(*x_bar / drdx);
|
||||
real_t r;
|
||||
__enzyme_autodiff<void>((void*)rbr_wrapper<f, T>, enzyme_const, *x, enzyme_dup,
|
||||
p, p_bar, enzyme_dupnoneed, &r, &lambda);
|
||||
|
||||
// These are logically constants, the root has no sensitivity to these
|
||||
*x0_bar = 0.0;
|
||||
*settings_bar = SolverSettings{};
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
} // namespace internal
|
||||
|
||||
|
||||
namespace future
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Find the root of a univariate funtion
|
||||
*/
|
||||
template<auto f, typename T>
|
||||
MFEM_HOST_DEVICE __attribute__((always_inline)) real_t SolveNewtonBisection(
|
||||
real_t x0, T p, SolverSettings settings)
|
||||
{
|
||||
// We need to tell Enzyme how much memory in the SolverSettings object is
|
||||
// used by active variables (in the sense of Enzyme activity analysis).
|
||||
// Without this, it seems that a bug in LLVM causes this information to
|
||||
// be lost during some optimization pass, and the Enzyme pass fails in
|
||||
// Release builds.
|
||||
// There are 4 real_t members in settings, which is what Enzyme will
|
||||
// consider active.
|
||||
// TODO: File an issue on Enzyme to remind Bill to fix this in LLVM.
|
||||
__enzyme_double((void*)&settings, sizeof(real_t)*4);
|
||||
|
||||
real_t x;
|
||||
internal::SolveNewtonBisection_impl<f>(&x0, &p, &settings, &x);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
} // namespace future
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_ENZYME
|
||||
#endif // MFEM_UNIVARSOLVERS
|
||||
+82
-5
@@ -1044,9 +1044,50 @@ void VectorFiniteElement::SetDerivMembers()
|
||||
switch (map_type)
|
||||
{
|
||||
case H_DIV:
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
switch (dim)
|
||||
{
|
||||
case 3: // div: 3D H_DIV -> 3D INTEGRAL
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
break;
|
||||
case 2: // div: 2D H_DIV -> 2D INTEGRAL
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid dimension, Dim = " << dim);
|
||||
}
|
||||
break;
|
||||
case H_DIV_R2D:
|
||||
switch (dim)
|
||||
{
|
||||
case 2: // div: 2D H_DIV_R2D -> 2D INTEGRAL
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
break;
|
||||
case 1: // div: 1D H_DIV_R2D -> 1D INTEGRAL
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid dimension, Dim = " << dim);
|
||||
}
|
||||
break;
|
||||
case H_DIV_R1D:
|
||||
switch (dim)
|
||||
{
|
||||
case 1: // div: 1D H_DIV_R1D -> 1D INTEGRAL
|
||||
deriv_type = DIV;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid dimension, Dim = " << dim);
|
||||
}
|
||||
break;
|
||||
case H_CURL:
|
||||
switch (dim)
|
||||
@@ -1064,13 +1105,49 @@ void VectorFiniteElement::SetDerivMembers()
|
||||
break;
|
||||
case 1:
|
||||
deriv_type = NONE;
|
||||
deriv_range_type = SCALAR;
|
||||
deriv_map_type = INTEGRAL;
|
||||
deriv_range_type = UNKNOWN_RANGE_TYPE;
|
||||
deriv_map_type = UNKNOWN_MAP_TYPE;
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid dimension, Dim = " << dim);
|
||||
}
|
||||
break;
|
||||
case H_CURL_R2D:
|
||||
switch (dim)
|
||||
{
|
||||
case 2:
|
||||
// curl: 2D H_CURL_R2D -> H_DIV_R2D
|
||||
deriv_type = CURL;
|
||||
deriv_range_type = VECTOR;
|
||||
deriv_map_type = H_DIV_R2D;
|
||||
break;
|
||||
case 1:
|
||||
// curl: 1D H_CURL_R2D -> H_DIV_R2D
|
||||
deriv_type = CURL;
|
||||
deriv_range_type = VECTOR;
|
||||
deriv_map_type = H_DIV_R2D;
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid dimension, Dim = " << dim);
|
||||
}
|
||||
break;
|
||||
case H_CURL_R1D:
|
||||
switch (dim)
|
||||
{
|
||||
case 1:
|
||||
// curl: 1D H_CURL_R1D -> H_DIV_R1D
|
||||
deriv_type = CURL;
|
||||
deriv_range_type = VECTOR;
|
||||
deriv_map_type = H_DIV_R1D;
|
||||
break;
|
||||
case 0:
|
||||
deriv_type = NONE;
|
||||
deriv_range_type = UNKNOWN_RANGE_TYPE;
|
||||
deriv_map_type = UNKNOWN_MAP_TYPE;
|
||||
default:
|
||||
MFEM_ABORT("Invalid dimension, Dim = " << dim);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid MapType = " << map_type);
|
||||
}
|
||||
|
||||
+31
-3
@@ -295,10 +295,20 @@ public:
|
||||
$ u(x) = (1/w) \hat u(\hat x) $ */
|
||||
H_DIV, /**< For vector fields; preserves surface integrals of the
|
||||
normal component $ u(x) = (J/w) \hat u(\hat x) $ */
|
||||
H_CURL /**< For vector fields; preserves line integrals of the
|
||||
H_CURL, /**< For vector fields; preserves line integrals of the
|
||||
tangential component
|
||||
$ u(x) = J^{-t} \hat u(\hat x) $ (square J),
|
||||
$ u(x) = J(J^t J)^{-1} \hat u(\hat x) $ (general J) */
|
||||
H_DIV_R2D, /**< For 3-component vector fields in 2D; equivalent to a
|
||||
direct sum of an H_DIV basis and an INTEGRAL basis */
|
||||
H_CURL_R2D,/**< For 3-component vector fields in 2D; equivalent to a
|
||||
direct sum of an H_CURL basis and a VALUE basis */
|
||||
H_DIV_R1D, /**< For 3-component vector fields in 1D; equivalent to a
|
||||
direct sum of a VALUE basis and a pair of INTEGRAL
|
||||
bases */
|
||||
H_CURL_R1D /**< For 3-component vector fields in 1D; equivalent to a
|
||||
direct sum of an INTEGRAL basis and a pair of VALUE
|
||||
bases */
|
||||
};
|
||||
|
||||
/** @brief Enumeration for DerivType: defines which derivative method
|
||||
@@ -330,12 +340,28 @@ public:
|
||||
int GetDim() const { return dim; }
|
||||
|
||||
/** @brief Returns the vector dimension for vector-valued finite elements,
|
||||
which is also the dimension of the interpolation operation. */
|
||||
which is also the dimension of the interpolation operation and the
|
||||
width of the DenseMatrix argument in
|
||||
CalcVShape(const IntegrationPoint &ip, DenseMatrix &shape). */
|
||||
int GetRangeDim() const { return vdim; }
|
||||
|
||||
/// Returns the dimension of the curl for vector-valued finite elements.
|
||||
/** @brief Returns the vector dimension, in physical space, for
|
||||
vector-valued finite elements, which is also the width of the
|
||||
DenseMatrix argument in
|
||||
CalcPhysVShape(ElementTransformation &Trans, DenseMatrix &shape). */
|
||||
int GetPhysRangeDim(int /* space_dim */) const { return vdim; }
|
||||
|
||||
/** Returns the dimension of the curl for vector-valued finite elements,
|
||||
which is also the width of the DenseMatrix argument in
|
||||
CalcCurlShape(const IntegrationPoint &ip, DenseMatrix &curl_shape). */
|
||||
int GetCurlDim() const { return cdim; }
|
||||
|
||||
/** Returns the dimension, in physical space, of the curl for vector-valued
|
||||
finite elements, which is also the width of the DenseMatrix argument in
|
||||
CalcPhysCurlShape(ElementTransformation &Trans, DenseMatrix &curl_shape).
|
||||
*/
|
||||
int GetPhysCurlDim(int /* space_dim */) const { return cdim; }
|
||||
|
||||
/// Returns the Geometry::Type of the reference element.
|
||||
Geometry::Type GetGeomType() const { return geom_type; }
|
||||
|
||||
@@ -990,6 +1016,8 @@ protected:
|
||||
public:
|
||||
VectorFiniteElement(int D, Geometry::Type G, int Do, int O, int M,
|
||||
int F = FunctionSpace::Pk);
|
||||
|
||||
int GetPhysRangeDim(int space_dim) const { return space_dim; }
|
||||
};
|
||||
|
||||
/// @brief Class for computing 1D special polynomials and their associated basis
|
||||
|
||||
+4
-4
@@ -2531,7 +2531,7 @@ void ND_FuentesPyramidElement::calcCurlBasis(const int p,
|
||||
|
||||
ND_R1D_PointElement::ND_R1D_PointElement(int p)
|
||||
: VectorFiniteElement(1, Geometry::POINT, 2, p,
|
||||
H_CURL, FunctionSpace::Pk)
|
||||
H_CURL_R1D, FunctionSpace::Pk)
|
||||
{
|
||||
// VectorFiniteElement::SetDerivMembers doesn't support 0D H_CURL elements
|
||||
// so we mimic a 1D element and then correct the dimension here.
|
||||
@@ -2562,7 +2562,7 @@ ND_R1D_SegmentElement::ND_R1D_SegmentElement(const int p,
|
||||
const int cb_type,
|
||||
const int ob_type)
|
||||
: VectorFiniteElement(1, Geometry::SEGMENT, 3 * p + 2, p,
|
||||
H_CURL, FunctionSpace::Pk),
|
||||
H_CURL_R1D, FunctionSpace::Pk),
|
||||
dof2tk(dof),
|
||||
cbasis1d(poly1d.GetBasis(p, VerifyClosed(cb_type))),
|
||||
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(ob_type)))
|
||||
@@ -2839,7 +2839,7 @@ ND_R2D_SegmentElement::ND_R2D_SegmentElement(const int p,
|
||||
const int cb_type,
|
||||
const int ob_type)
|
||||
: VectorFiniteElement(1, Geometry::SEGMENT, 2 * p + 1, p,
|
||||
H_CURL, FunctionSpace::Pk),
|
||||
H_CURL_R2D, FunctionSpace::Pk),
|
||||
dof2tk(dof),
|
||||
cbasis1d(poly1d.GetBasis(p, VerifyClosed(cb_type))),
|
||||
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(ob_type)))
|
||||
@@ -3023,7 +3023,7 @@ void ND_R2D_SegmentElement::Project(VectorCoefficient &vc,
|
||||
ND_R2D_FiniteElement::ND_R2D_FiniteElement(int p, Geometry::Type G, int Do,
|
||||
const real_t *tk_fe)
|
||||
: VectorFiniteElement(2, G, Do, p,
|
||||
H_CURL, FunctionSpace::Pk),
|
||||
H_CURL_R2D, FunctionSpace::Pk),
|
||||
tk(tk_fe),
|
||||
dof_map(dof),
|
||||
dof2tk(dof)
|
||||
|
||||
@@ -663,6 +663,9 @@ public:
|
||||
const int cb_type = BasisType::GaussLobatto,
|
||||
const int ob_type = BasisType::GaussLegendre);
|
||||
|
||||
int GetPhysRangeDim(int space_dim) const { return 2; }
|
||||
int GetPhysCurlDim(int space_dim) const { return 1; }
|
||||
|
||||
void CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const override;
|
||||
|
||||
@@ -705,6 +708,9 @@ private:
|
||||
DenseMatrix &I) const;
|
||||
|
||||
public:
|
||||
int GetPhysRangeDim(int space_dim) const { return 3; }
|
||||
int GetPhysCurlDim(int space_dim) const { return 3; }
|
||||
|
||||
using FiniteElement::CalcVShape;
|
||||
using FiniteElement::CalcPhysCurlShape;
|
||||
|
||||
|
||||
+3
-3
@@ -2006,7 +2006,7 @@ RT_R1D_SegmentElement::RT_R1D_SegmentElement(const int p,
|
||||
const int cb_type,
|
||||
const int ob_type)
|
||||
: VectorFiniteElement(1, Geometry::SEGMENT, 3 * p + 4, p + 1,
|
||||
H_DIV, FunctionSpace::Pk),
|
||||
H_DIV_R1D, FunctionSpace::Pk),
|
||||
dof2nk(dof),
|
||||
cbasis1d(poly1d.GetBasis(p + 1, VerifyClosed(cb_type))),
|
||||
obasis1d(poly1d.GetBasis(p, VerifyOpen(ob_type)))
|
||||
@@ -2281,7 +2281,7 @@ const real_t RT_R2D_SegmentElement::nk[2] = { 0.,1.};
|
||||
RT_R2D_SegmentElement::RT_R2D_SegmentElement(const int p,
|
||||
const int ob_type)
|
||||
: VectorFiniteElement(1, Geometry::SEGMENT, p + 1, p + 1,
|
||||
H_DIV, FunctionSpace::Pk),
|
||||
H_DIV_R2D, FunctionSpace::Pk),
|
||||
dof2nk(dof),
|
||||
obasis1d(poly1d.GetBasis(p, VerifyOpen(ob_type)))
|
||||
{
|
||||
@@ -2392,7 +2392,7 @@ void RT_R2D_SegmentElement::LocalInterpolation(const VectorFiniteElement &cfe,
|
||||
RT_R2D_FiniteElement::RT_R2D_FiniteElement(int p, Geometry::Type G, int Do,
|
||||
const real_t *nk_fe)
|
||||
: VectorFiniteElement(2, G, Do, p + 1,
|
||||
H_DIV, FunctionSpace::Pk),
|
||||
H_DIV_R2D, FunctionSpace::Pk),
|
||||
nk(nk_fe),
|
||||
dof_map(dof),
|
||||
dof2nk(dof)
|
||||
|
||||
@@ -510,6 +510,9 @@ public:
|
||||
RT_R2D_SegmentElement(const int p,
|
||||
const int ob_type = BasisType::GaussLegendre);
|
||||
|
||||
int GetPhysRangeDim(int space_dim) const { return 2; }
|
||||
int GetPhysCurlDim(int space_dim) const { return 0; }
|
||||
|
||||
void CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const override;
|
||||
|
||||
@@ -547,6 +550,9 @@ private:
|
||||
DenseMatrix &I) const;
|
||||
|
||||
public:
|
||||
int GetPhysRangeDim(int space_dim) const { return 3; }
|
||||
int GetPhysCurlDim(int space_dim) const { return 0; }
|
||||
|
||||
using FiniteElement::CalcVShape;
|
||||
|
||||
void CalcVShape(ElementTransformation &Trans,
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "particleset.hpp"
|
||||
|
||||
#include "dfem/doperator.hpp"
|
||||
#include "dfem/univarsolvers.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "pfespace.hpp"
|
||||
|
||||
+11
-5
@@ -321,12 +321,17 @@ void ParL2FaceRestriction::DoubleValuedConformingMult(
|
||||
const int vd = vdim;
|
||||
const bool t = byvdim;
|
||||
const int threshold = ndofs;
|
||||
const int nsdofs = pfes.GetFaceNbrVSize();
|
||||
const int nsdofs = pfes.GetFaceNbrVSize() / vd;
|
||||
auto d_indices1 = scatter_indices1.Read();
|
||||
auto d_indices2 = scatter_indices2.Read();
|
||||
auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
|
||||
auto d_x_shared = Reshape(face_nbr_data.Read(),
|
||||
t?vd:nsdofs, t?nsdofs:vd);
|
||||
const int ne_shared = nsdofs / elem_dofs;
|
||||
const int nedof = elem_dofs;
|
||||
// Note: the shape of face_nbr_data, as determined by
|
||||
// ParFiniteElementSpace::ExchangeFaceNbrData, is (elem_dofs, vdim,
|
||||
// ne_shared), independent of the ordering (byNODES or byVDIM) of the finite
|
||||
// element space.
|
||||
auto d_x_shared = Reshape(face_nbr_data.Read(), elem_dofs, vd, ne_shared);
|
||||
auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
|
||||
mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
@@ -346,8 +351,9 @@ void ParL2FaceRestriction::DoubleValuedConformingMult(
|
||||
}
|
||||
else if (idx2>=threshold) // shared boundary
|
||||
{
|
||||
d_y(dof, c, 1, face) = d_x_shared(t?c:(idx2-threshold),
|
||||
t?(idx2-threshold):c);
|
||||
const int e_shared = (idx2 - threshold) / nedof;
|
||||
const int i_shared = (idx2 - threshold) % nedof;
|
||||
d_y(dof, c, 1, face) = d_x_shared(i_shared,c,e_shared);
|
||||
}
|
||||
else // true boundary
|
||||
{
|
||||
|
||||
+3
-6
@@ -1398,20 +1398,17 @@ void L2FaceRestriction::PermuteAndSetSharedFaceDofsScatterIndices2(
|
||||
const int dim = fes.GetMesh()->Dimension();
|
||||
const int dof1d = fes.GetTypicalFE()->GetOrder()+1;
|
||||
fes.GetTypicalFE()->GetFaceMap(face_id2, face_map);
|
||||
Array<int> face_nbr_dofs;
|
||||
const ParFiniteElementSpace &pfes =
|
||||
static_cast<const ParFiniteElementSpace&>(this->fes);
|
||||
pfes.GetFaceNbrElementVDofs(elem_index, face_nbr_dofs);
|
||||
|
||||
for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
|
||||
{
|
||||
const int face_dof_elem2 = PermuteFaceL2(dim, face_id1, face_id2,
|
||||
orientation, dof1d, face_dof_elem1);
|
||||
const int volume_dof_elem2 = face_map[face_dof_elem2];
|
||||
const int global_dof_elem2 = face_nbr_dofs[volume_dof_elem2];
|
||||
// Encode the volume DOF index and element index
|
||||
const int global_dof_elem2 = elem_index*elem_dofs + volume_dof_elem2;
|
||||
const int restriction_dof_elem2 = face_dofs*face_index + face_dof_elem1;
|
||||
// Trick to differentiate dof location inter/shared
|
||||
scatter_indices2[restriction_dof_elem2] = ndofs+global_dof_elem2;
|
||||
scatter_indices2[restriction_dof_elem2] = ndofs + global_dof_elem2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -317,6 +317,9 @@ void HypreParVector::WrapHypreParVector(hypre_ParVector *y, bool owner)
|
||||
|
||||
Vector * HypreParVector::GlobalVector() const
|
||||
{
|
||||
MFEM_VERIFY(size > 0,
|
||||
"GlobalVector method can only be called on vectors wherein each "
|
||||
"process owns one or more entries");
|
||||
hypre_Vector *hv = hypre_ParVectorToVectorAll(*this);
|
||||
Vector *v = new Vector(hv->data, internal::to_int(hv->size));
|
||||
v->MakeDataOwner();
|
||||
|
||||
+44
-78
@@ -38,6 +38,13 @@
|
||||
#if PETSC_VERSION_LT(3,19,0)
|
||||
#define PETSC_SUCCESS 0
|
||||
#endif
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
#define PetscContainerSetCtxDestroy(A,B) PetscContainerSetUserDestroy(A,B)
|
||||
typedef PetscErrorCode (PetscCtxDestroyFn)(void**);
|
||||
#endif
|
||||
#if PETSC_VERSION_LT(3,24,0)
|
||||
typedef PetscErrorCode KSPMonitorFn(KSP,PetscInt,PetscReal,void*);
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
@@ -77,13 +84,17 @@ static PetscErrorCode __mfem_mat_shell_apply_transpose(Mat,Vec,Vec);
|
||||
static PetscErrorCode __mfem_mat_shell_destroy(Mat);
|
||||
static PetscErrorCode __mfem_mat_shell_copy(Mat,Mat,MatStructure);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
static PetscErrorCode __mfem_array_container_destroy(void*);
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(void *);
|
||||
#else
|
||||
static PetscErrorCode __mfem_array_container_destroy(void**);
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(void**);
|
||||
typedef void *PetscCtxRt;
|
||||
#elif PETSC_VERSION_LT(3,25,0)
|
||||
typedef void **PetscCtxRt;
|
||||
#endif
|
||||
static PetscErrorCode __mfem_array_container_destroy(PetscCtxRt);
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(PetscCtxRt);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(void**);
|
||||
#else
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(PetscCtxRt);
|
||||
#endif
|
||||
|
||||
// auxiliary functions
|
||||
static PetscErrorCode Convert_Array_IS(MPI_Comm,bool,const mfem::Array<int>*,
|
||||
@@ -1317,11 +1328,7 @@ BlockDiagonalConstructor(MPI_Comm comm,
|
||||
|
||||
ierr = PetscContainerCreate(comm,&c); CCHKERRQ(comm,ierr);
|
||||
ierr = PetscContainerSetPointer(c,ptrs[i]); CCHKERRQ(comm,ierr);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
|
||||
#else
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
|
||||
#endif
|
||||
CCHKERRQ(comm,ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)A,names[i],(PetscObject)c);
|
||||
CCHKERRQ(comm,ierr);
|
||||
@@ -1648,11 +1655,7 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
|
||||
PetscContainer c;
|
||||
ierr = PetscContainerCreate(comm,&c); CCHKERRQ(comm,ierr);
|
||||
ierr = PetscContainerSetPointer(c,vmatsl2l); PCHKERRQ(c,ierr);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscContainerSetUserDestroy(c,__mfem_matarray_container_destroy);
|
||||
#else
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_matarray_container_destroy);
|
||||
#endif
|
||||
PCHKERRQ(c,ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)(*A),"_MatIS_PtAP_l2l",(PetscObject)c);
|
||||
PCHKERRQ((*A),ierr);
|
||||
@@ -1748,11 +1751,7 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
|
||||
|
||||
ierr = PetscContainerCreate(PETSC_COMM_SELF,&c); PCHKERRQ(B,ierr);
|
||||
ierr = PetscContainerSetPointer(c,ptrs[i]); PCHKERRQ(B,ierr);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
|
||||
#else
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
|
||||
#endif
|
||||
PCHKERRQ(B,ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)(B),names[i],(PetscObject)c);
|
||||
PCHKERRQ(B,ierr);
|
||||
@@ -2198,11 +2197,7 @@ PetscParMatrix * RAP(PetscParMatrix *Rt, PetscParMatrix *A, PetscParMatrix *P)
|
||||
ierr = PetscContainerCreate(PetscObjectComm((PetscObject)B),&c);
|
||||
PCHKERRQ(B,ierr);
|
||||
ierr = PetscContainerSetPointer(c,vmatsl2l); PCHKERRQ(c,ierr);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscContainerSetUserDestroy(c,__mfem_matarray_container_destroy);
|
||||
#else
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_matarray_container_destroy);
|
||||
#endif
|
||||
PCHKERRQ(c,ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)B,"_MatIS_PtAP_l2l",(PetscObject)c);
|
||||
PCHKERRQ(B,ierr);
|
||||
@@ -2485,7 +2480,6 @@ void PetscSolver::SetMaxIter(int max_iter)
|
||||
|
||||
void PetscSolver::SetPrintLevel(int plev)
|
||||
{
|
||||
typedef PetscErrorCode (*myPetscFunc)(void**);
|
||||
PetscViewerAndFormat *vf = NULL;
|
||||
PetscViewer viewer = PETSC_VIEWER_STDOUT_(PetscObjectComm(obj));
|
||||
|
||||
@@ -2498,7 +2492,6 @@ void PetscSolver::SetPrintLevel(int plev)
|
||||
{
|
||||
// there are many other options, see the function KSPSetFromOptions() in
|
||||
// src/ksp/ksp/interface/itcl.c
|
||||
typedef PetscErrorCode (*myMonitor)(KSP,PetscInt,PetscReal,void*);
|
||||
KSP ksp = (KSP)obj;
|
||||
if (plev >= 0)
|
||||
{
|
||||
@@ -2507,29 +2500,29 @@ void PetscSolver::SetPrintLevel(int plev)
|
||||
if (plev == 1)
|
||||
{
|
||||
#if PETSC_VERSION_LT(3,15,0)
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorDefault,vf,
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorDefault,vf,
|
||||
#else
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorResidual,vf,
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorResidual,vf,
|
||||
#endif
|
||||
(myPetscFunc)PetscViewerAndFormatDestroy);
|
||||
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
|
||||
PCHKERRQ(ksp,ierr);
|
||||
}
|
||||
else if (plev > 1)
|
||||
{
|
||||
ierr = KSPSetComputeSingularValues(ksp,PETSC_TRUE); PCHKERRQ(ksp,ierr);
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorSingularValue,vf,
|
||||
(myPetscFunc)PetscViewerAndFormatDestroy);
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorSingularValue,vf,
|
||||
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
|
||||
PCHKERRQ(ksp,ierr);
|
||||
if (plev > 2)
|
||||
{
|
||||
ierr = PetscViewerAndFormatCreate(viewer,PETSC_VIEWER_DEFAULT,&vf);
|
||||
PCHKERRQ(viewer,ierr);
|
||||
#if PETSC_VERSION_LT(3,15,0)
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorTrueResidualNorm,vf,
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorTrueResidualNorm,vf,
|
||||
#else
|
||||
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorTrueResidual,vf,
|
||||
ierr = KSPMonitorSet(ksp,(KSPMonitorFn *)KSPMonitorTrueResidual,vf,
|
||||
#endif
|
||||
(myPetscFunc)PetscViewerAndFormatDestroy);
|
||||
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
|
||||
PCHKERRQ(ksp,ierr);
|
||||
}
|
||||
}
|
||||
@@ -2545,7 +2538,7 @@ void PetscSolver::SetPrintLevel(int plev)
|
||||
if (plev > 0)
|
||||
{
|
||||
ierr = SNESMonitorSet(snes,(myMonitor)SNESMonitorDefault,vf,
|
||||
(myPetscFunc)PetscViewerAndFormatDestroy);
|
||||
(PetscCtxDestroyFn *)PetscViewerAndFormatDestroy);
|
||||
PCHKERRQ(snes,ierr);
|
||||
}
|
||||
}
|
||||
@@ -5329,21 +5322,27 @@ static PetscErrorCode __mfem_pc_shell_destroy(PC pc)
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
static PetscErrorCode __mfem_array_container_destroy(PetscCtxRt ptr)
|
||||
{
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
|
||||
static PetscErrorCode __mfem_array_container_destroy(void *ptr)
|
||||
{
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
ierr = PetscFree(ptr); CHKERRQ(ierr);
|
||||
#else
|
||||
ierr = PetscFree(*(void**)ptr); CHKERRQ(ierr);
|
||||
#endif
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(void *ptr)
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(PetscCtxRt ptr)
|
||||
{
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
mfem::Array<Mat> *a = (mfem::Array<Mat>*)ptr;
|
||||
PetscErrorCode ierr;
|
||||
#else
|
||||
mfem::Array<Mat> *a = *(mfem::Array<Mat>**)ptr;
|
||||
#endif
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
for (int i=0; i<a->Size(); i++)
|
||||
@@ -5356,41 +5355,16 @@ static PetscErrorCode __mfem_matarray_container_destroy(void *ptr)
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(void **ctx)
|
||||
#else
|
||||
|
||||
static PetscErrorCode __mfem_array_container_destroy(void **ptr)
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(PetscCtxRt ctx)
|
||||
#endif
|
||||
{
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
ierr = PetscFree(*ptr); CHKERRQ(ierr);
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
static PetscErrorCode __mfem_matarray_container_destroy(void **ptr)
|
||||
{
|
||||
mfem::Array<Mat> *a = (mfem::Array<Mat>*)*ptr;
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
for (int i=0; i<a->Size(); i++)
|
||||
{
|
||||
Mat M = (*a)[i];
|
||||
MPI_Comm comm = PetscObjectComm((PetscObject)M);
|
||||
ierr = MatDestroy(&M); CCHKERRQ(comm,ierr);
|
||||
}
|
||||
delete a;
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static PetscErrorCode __mfem_monitor_ctx_destroy(void **ctx)
|
||||
{
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
ierr = PetscFree(*ctx); CHKERRQ(ierr);
|
||||
ierr = PetscFree(*(void**)ctx); CHKERRQ(ierr);
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -5635,11 +5609,7 @@ static PetscErrorCode MatConvert_hypreParCSR_AIJ(hypre_ParCSRMatrix* hA,Mat* pA)
|
||||
|
||||
ierr = PetscContainerCreate(comm,&c); CHKERRQ(ierr);
|
||||
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
|
||||
#else
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
|
||||
#endif
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)(*pA),names[i],(PetscObject)c);
|
||||
CHKERRQ(ierr);
|
||||
@@ -5733,11 +5703,7 @@ static PetscErrorCode MatConvert_hypreParCSR_IS(hypre_ParCSRMatrix* hA,Mat* pA)
|
||||
|
||||
ierr = PetscContainerCreate(PETSC_COMM_SELF,&c); CHKERRQ(ierr);
|
||||
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
|
||||
#if PETSC_VERSION_LT(3,23,0)
|
||||
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
|
||||
#else
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
|
||||
#endif
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)lA,names[i],(PetscObject)c);
|
||||
CHKERRQ(ierr);
|
||||
|
||||
+299
-174
File diff suppressed because it is too large
Load Diff
@@ -126,11 +126,11 @@ EXAMPLE_TEST_DIRS := examples
|
||||
MINIAPP_SUBDIRS = common electromagnetics meshing performance tools \
|
||||
toys nurbs gslib adjoint solvers shifted mtop parelag tribol autodiff dfem \
|
||||
hooke multidomain dpg hdiv-linear-solver spde diag-smoothers contact \
|
||||
fluids/navier fluids/schrodinger-flow
|
||||
fluids/navier fluids/schrodinger-flow plasma
|
||||
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
|
||||
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
|
||||
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics meshing tools \
|
||||
toys shifted dpg diag-smoothers fluids/navier)
|
||||
toys shifted dpg diag-smoothers fluids/navier plasma)
|
||||
|
||||
EM_DIRS = $(EXAMPLE_DIRS) $(MINIAPP_DIRS)
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ add_subdirectory(multidomain)
|
||||
add_subdirectory(nurbs)
|
||||
add_subdirectory(parelag)
|
||||
add_subdirectory(performance)
|
||||
add_subdirectory(plasma)
|
||||
add_subdirectory(shifted)
|
||||
add_subdirectory(solvers)
|
||||
add_subdirectory(spde)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# 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.
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
list(APPEND PLASMA_COMMON_SOURCES)
|
||||
|
||||
list(APPEND PLASMA_COMMON_HEADERS
|
||||
plasma.hpp)
|
||||
|
||||
convert_filenames_to_full_paths(PLASMA_COMMON_SOURCES)
|
||||
convert_filenames_to_full_paths(PLASMA_COMMON_HEADERS)
|
||||
|
||||
set(PLASMA_COMMON_FILES
|
||||
EXTRA_SOURCES ${PLASMA_COMMON_SOURCES}
|
||||
EXTRA_HEADERS ${PLASMA_COMMON_HEADERS})
|
||||
|
||||
endif()
|
||||
@@ -0,0 +1,85 @@
|
||||
# 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.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/plasma/,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
# Use the MFEM install directory
|
||||
# MFEM_INSTALL_DIR = ../../mfem
|
||||
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_MINIAPPS =
|
||||
PAR_MINIAPPS =
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
MINIAPPS = $(SEQ_MINIAPPS)
|
||||
else
|
||||
MINIAPPS = $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all lib-common clean clean-build clean-exec
|
||||
.PRECIOUS: %.o
|
||||
|
||||
COMMON_LIB = -L$(MFEM_BUILD_DIR)/miniapps/common -lmfem-common
|
||||
|
||||
# If MFEM_SHARED is set, add the ../common rpath
|
||||
COMMON_LIB += $(if $(MFEM_SHARED:YES=),,\
|
||||
$(if $(MFEM_USE_CUDA:YES=),$(CXX_XLINKER),$(CUDA_XLINKER))-rpath,$(abspath\
|
||||
$(MFEM_BUILD_DIR)/miniapps/common))
|
||||
|
||||
COMMON_O=
|
||||
|
||||
# Remove built-in rules
|
||||
%: %.cpp
|
||||
%.o: %.cpp
|
||||
|
||||
all: $(MINIAPPS)
|
||||
|
||||
# Rules for building the miniapps
|
||||
%: $(SRC)%.cpp $(COMMON_O) $(MFEM_LIB_FILE) $(CONFIG_MK) | lib-common
|
||||
$(MFEM_CXX) $(MFEM_LINK_FLAGS) $< -o $@ $(COMMON_O) $(COMMON_LIB) \
|
||||
$(MFEM_LIBS)
|
||||
|
||||
# Rules for compiling miniapp dependencies
|
||||
$(COMMON_O) $(addsuffix _solver.o,$(MINIAPPS)): \
|
||||
%.o: $(SRC)%.cpp $(SRC)%.hpp $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) -c $(<) -o $(@)
|
||||
|
||||
# Rule for building lib-common
|
||||
lib-common:
|
||||
$(MAKE) -C $(MFEM_BUILD_DIR)/miniapps/common
|
||||
|
||||
MFEM_TESTS = MINIAPPS
|
||||
include $(MFEM_TEST_MK)
|
||||
|
||||
# Testing: Specific execution options
|
||||
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
|
||||
|
||||
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_MINIAPPS) $(PAR_MINIAPPS)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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_PLASMA_HPP
|
||||
#define MFEM_PLASMA_HPP
|
||||
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace plasma
|
||||
{
|
||||
|
||||
// Physical Constants
|
||||
|
||||
// Permittivity of Free Space (units F/m)
|
||||
static const real_t epsilon0_ = 8.8541878176e-12;
|
||||
|
||||
// Permeability of Free Space (units H/m)
|
||||
static const real_t mu0_ = 4.0e-7 * M_PI;
|
||||
|
||||
// Speed of light in Free Space (units m/s)
|
||||
static const real_t c0_ = 1.0 / sqrt(epsilon0_ * mu0_);
|
||||
|
||||
// Impedance of Free Space (units Ohm)
|
||||
static const real_t Z0_ = sqrt(mu0_ / epsilon0_);
|
||||
|
||||
static const real_t q_ = 1.602176634e-19; // Elementary charge in coulombs
|
||||
static const real_t eV_ = 1.602176634e-19; // 1 eV in Joules
|
||||
static const real_t amu_ = 1.660539040e-27; // Atomic mass unit in kilograms
|
||||
static const real_t me_kg_ = 9.10938356e-31; // Mass of electron in kilograms
|
||||
static const real_t me_u_ = 5.4857990907e-4; // Mass of electron in a.m.u
|
||||
|
||||
/**
|
||||
Returns the cyclotron frequency in radians/second
|
||||
m is the mass in a.m.u
|
||||
q is the charge in units of elementary electric charge
|
||||
B is the magnetic field magnitude in tesla
|
||||
*/
|
||||
inline real_t cyclotronFrequency(real_t B, real_t m, real_t q)
|
||||
{
|
||||
return fabs(q * q_ * B / (m * amu_));
|
||||
}
|
||||
|
||||
typedef std::complex<real_t> complex_t;
|
||||
|
||||
} // namespace plasma
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_PLASMA_HPP
|
||||
|
||||
@@ -39,6 +39,7 @@ set(UNIT_TESTS_SRCS
|
||||
dfem/test_divergence.cpp
|
||||
dfem/test_lvector_interface.cpp
|
||||
dfem/test_mass.cpp
|
||||
dfem/test_univarsolver.cpp
|
||||
general/test_array.cpp
|
||||
general/test_scan.cpp
|
||||
general/test_arrays_by_name.cpp
|
||||
|
||||
@@ -295,8 +295,13 @@ namespace Catch {
|
||||
// Otherwise all supported compilers support COUNTER macro,
|
||||
// but user still might want to turn it off
|
||||
#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
|
||||
#if ( !(defined(__clang__) && __clang_major__ >= 22 ) )
|
||||
// don't use __COUNTER__ if compiling with clang 22+ to avoid compiler warning
|
||||
// https://github.com/llvm/llvm-project/pull/162662
|
||||
// TODO: can enable if building with C2y
|
||||
#define CATCH_INTERNAL_CONFIG_COUNTER
|
||||
#endif
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
// 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 <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "unit_tests.hpp"
|
||||
|
||||
#ifdef MFEM_USE_ENZYME
|
||||
|
||||
using mfem::real_t;
|
||||
using namespace mfem::future;
|
||||
|
||||
MFEM_HOST_DEVICE inline real_t FlowResistance(real_t eqps, real_t sigma_y, real_t n, real_t ep_0)
|
||||
{
|
||||
return sigma_y*(1.0 + std::pow((eqps)/ep_0, n));
|
||||
}
|
||||
|
||||
using J2PlasticityParameters = tuple<real_t, real_t, real_t, real_t, real_t, real_t>;
|
||||
|
||||
// Residual function that is solved in the plasticity model.
|
||||
// Made a free function to facilitate Enzyme differentiation.
|
||||
real_t J2PlasticityResidual(real_t delta_eqps, J2PlasticityParameters p)
|
||||
{
|
||||
auto [eqps, q, G, sigma_y, n, ep_0] = p;
|
||||
return q - 3.0*G*delta_eqps - FlowResistance(eqps + delta_eqps, sigma_y, n, ep_0);
|
||||
}
|
||||
|
||||
struct J2Plasticity {
|
||||
static constexpr int dim = 3; ///< spatial dimension
|
||||
static constexpr int N_INTERNAL_STATES = 10;
|
||||
static constexpr real_t tol = 1e-10; ///< relative tolerance on residual mag to judge convergence of return map
|
||||
|
||||
real_t E; ///< Young's modulus
|
||||
real_t nu; ///< Poisson's ratio
|
||||
real_t sigma_y; ///< Yield strength
|
||||
real_t n; ///< Hardening index
|
||||
real_t ep_0; ///< Reference plastic strain
|
||||
|
||||
/// @brief variables required to characterize the hysteresis response
|
||||
struct InternalState {
|
||||
tensor<real_t, dim, dim> plastic_strain;
|
||||
real_t accumulated_plastic_strain;
|
||||
};
|
||||
|
||||
/// Internal state variables in a flattened array for storing in a global field
|
||||
using PackedInternalState = mfem::future::tensor<real_t, N_INTERNAL_STATES>;
|
||||
|
||||
// Unflatten internal state variables
|
||||
MFEM_HOST_DEVICE static inline InternalState unpack_internal_state(
|
||||
const mfem::future::tensor<real_t, N_INTERNAL_STATES>& packed_state)
|
||||
{
|
||||
auto plastic_strain =
|
||||
mfem::future::make_tensor<dim, dim>([&packed_state](int i, int j) { return packed_state[dim * i + j]; });
|
||||
real_t accumulated_plastic_strain = packed_state[N_INTERNAL_STATES - 1];
|
||||
return {plastic_strain, accumulated_plastic_strain};
|
||||
}
|
||||
|
||||
// Flatten internal state variables (for repacking into global field)
|
||||
MFEM_HOST_DEVICE static inline PackedInternalState pack_internal_state(
|
||||
const mfem::future::tensor<real_t, dim, dim>& plastic_strain, real_t accumulated_plastic_strain)
|
||||
{
|
||||
PackedInternalState packed_state{};
|
||||
for (int i = 0, ij = 0; i < dim; i++) {
|
||||
for (int j = 0; j < dim; j++, ij++) {
|
||||
packed_state[ij] = plastic_strain[i][j];
|
||||
}
|
||||
}
|
||||
packed_state[N_INTERNAL_STATES - 1] = accumulated_plastic_strain;
|
||||
return packed_state;
|
||||
}
|
||||
|
||||
// Compute the new stress and the internal state variables
|
||||
MFEM_HOST_DEVICE inline tuple<tensor<real_t, dim, dim>, PackedInternalState>
|
||||
update(tensor<real_t, dim, dim> dudxi,
|
||||
PackedInternalState internal_state,
|
||||
tensor<real_t, dim, dim> J,
|
||||
real_t w) const
|
||||
{
|
||||
auto invJ = inv(J);
|
||||
const auto dudX = dudxi * invJ;
|
||||
auto I = IdentityMatrix<dim>();
|
||||
const real_t K = E / (3.0 * (1.0 - 2.0 * nu));
|
||||
const real_t G = 0.5 * E / (1.0 + nu);
|
||||
|
||||
auto [plastic_strain, accumulated_plastic_strain] = unpack_internal_state(internal_state);
|
||||
|
||||
auto el_strain = sym(dudX) - plastic_strain;
|
||||
auto p = K * tr(el_strain);
|
||||
auto s = 2.0 * G * dev(el_strain);
|
||||
auto q = std::sqrt(1.5) * norm(s);
|
||||
real_t denom = q > 0.0? q : 1.0;
|
||||
auto Np = 1.5 * s / denom;
|
||||
|
||||
if (q > FlowResistance(accumulated_plastic_strain, sigma_y, n, ep_0)) {
|
||||
real_t lb = 0.0;
|
||||
real_t ub = (q - FlowResistance(accumulated_plastic_strain, sigma_y, n, ep_0))/(3*G);
|
||||
SolverSettings settings{1e-10*sigma_y, 1e-10, {lb, ub}};
|
||||
// Use the differentiable univariate root finder.
|
||||
// This has custom derivatives, so it's ok to differentiate this enclosing function.
|
||||
real_t delta_eqps = SolveNewtonBisection<J2PlasticityResidual>(
|
||||
0.5*(lb + ub), make_tuple(accumulated_plastic_strain, q, G, sigma_y, n, ep_0), settings);
|
||||
accumulated_plastic_strain += delta_eqps;
|
||||
plastic_strain += delta_eqps * Np;
|
||||
s -= 2.0 * G * delta_eqps * Np;
|
||||
}
|
||||
auto Q_new = pack_internal_state(plastic_strain, accumulated_plastic_strain);
|
||||
auto stress = s + p * I;
|
||||
const real_t dV = det(J)*w;
|
||||
return {stress*transpose(invJ)*dV, Q_new};
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline tensor<real_t, dim, dim>
|
||||
stress(tensor<real_t, dim, dim> dudxi,
|
||||
PackedInternalState internal_state,
|
||||
tensor<real_t, dim, dim> J,
|
||||
real_t w) const
|
||||
{
|
||||
auto [stress, internal_state_new] = update(dudxi, internal_state, J, w);
|
||||
return stress;
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline PackedInternalState
|
||||
internal_state_new(tensor<real_t, dim, dim> dudxi,
|
||||
PackedInternalState internal_state,
|
||||
tensor<real_t, dim, dim> J,
|
||||
real_t w) const
|
||||
{
|
||||
auto [stress, internal_state_new] = update(dudxi, internal_state, J, w);
|
||||
return internal_state_new;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Register the custom derivatives for the solver.
|
||||
// This needs to be done for every residual function that the solver is applied on,
|
||||
// since the SolveNewtonBisection_impl is a function template, and we need a real
|
||||
// function with an address to specify the custom derivative.
|
||||
|
||||
// Forward mode
|
||||
__attribute__((used))
|
||||
void * __enzyme_register_derivative_newton_bisection_on_j2[2] = {
|
||||
(void*) mfem::internal::SolveNewtonBisection_impl<J2PlasticityResidual, J2PlasticityParameters>,
|
||||
(void*) mfem::internal::SolveNewtonBisection_impl_fwddiff<J2PlasticityResidual, J2PlasticityParameters>
|
||||
};
|
||||
|
||||
// Reverse mode
|
||||
__attribute__((used))
|
||||
void* __enzyme_register_gradient_SolveNewtonBisectionJ2[3] = {
|
||||
(void*)mfem::internal::SolveNewtonBisection_impl<J2PlasticityResidual, J2PlasticityParameters>,
|
||||
(void*)mfem::internal::SolveNewtonBisection_impl_aug<J2PlasticityResidual, J2PlasticityParameters>,
|
||||
(void*)mfem::internal::SolveNewtonBisection_impl_rev<J2PlasticityResidual, J2PlasticityParameters>
|
||||
};
|
||||
|
||||
// Create free functions for Enzyme to differentiate in the tests
|
||||
// Return by value version
|
||||
tensor<real_t, 3, 3> ComputeStress(
|
||||
J2Plasticity* material, tensor<real_t, 3, 3> dudxi,
|
||||
J2Plasticity::PackedInternalState Q, tensor<real_t, 3, 3> J, real_t w)
|
||||
{
|
||||
return material->stress(dudxi, Q, J, w);
|
||||
}
|
||||
|
||||
// Return by reference version
|
||||
void ComputeStressRef(const J2Plasticity* material, const tensor<real_t, 3, 3>& dudxi,
|
||||
const J2Plasticity::PackedInternalState& Q,
|
||||
const tensor<real_t, 3, 3>& J, real_t w,
|
||||
tensor<real_t, 3, 3>& sigma)
|
||||
{
|
||||
sigma = material->stress(dudxi, Q, J, w);
|
||||
}
|
||||
|
||||
template <int dim>
|
||||
real_t elementwise_max_norm(tensor<real_t, dim, dim> A) {
|
||||
real_t maxval = 0;
|
||||
for (int i = 0; i < dim; i++) {
|
||||
for (int j = 0; j < dim; j++) {
|
||||
maxval = std::max(std::abs(A[i][j]), maxval);
|
||||
}
|
||||
}
|
||||
return maxval;
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Univariate function solver in a qfunction", "[univar]")
|
||||
{
|
||||
J2Plasticity material{70.0e3, 0.34, 240.0, 0.15, 1e-3};
|
||||
tensor<real_t, 3, 3> H{{{0.947667 , 0.9785799 , 0.33229148},
|
||||
{0.46866846, 0.5698887 , 0.16550303},
|
||||
{0.3101946 , 0.68948054, 0.74676657}}};
|
||||
J2Plasticity::PackedInternalState Q{};
|
||||
const tensor<real_t, 3, 3> J = IdentityMatrix<3>();
|
||||
const real_t w = 1.0;
|
||||
|
||||
SECTION("Correctness")
|
||||
{
|
||||
// Checks that stress after update is on the yield surface.
|
||||
auto [stress, Q_new] = material.update(H, Q, IdentityMatrix<3>(), 1.0);
|
||||
real_t mises = std::sqrt(1.5)*norm(dev(stress));
|
||||
real_t eqps = Q_new[9];
|
||||
// This test only makes sense if the displacement gradient is big enough to
|
||||
// cuase yielding.
|
||||
REQUIRE(eqps > 1e-9);
|
||||
real_t Y = FlowResistance(eqps, material.sigma_y, material.n, material.ep_0);
|
||||
CHECK(mises == MFEM_Approx(Y, 0.0, 1e-8));
|
||||
}
|
||||
|
||||
SECTION("JVP")
|
||||
{
|
||||
// Compare forward mode derivative to finite difference approximation
|
||||
|
||||
tensor<real_t, 3, 3> H_dot{{{1.0, 0.0 , 0.0},
|
||||
{0.0, 0.0 , 0.0},
|
||||
{0.0, 0.0 , 0.0}}};
|
||||
|
||||
// Enzyme directional derivative (uses custom derivative of solver)
|
||||
auto sigma_dot = __enzyme_fwddiff<tensor<real_t, 3, 3>>((void*)ComputeStress,
|
||||
enzyme_const, &material,
|
||||
enzyme_dup, H, H_dot,
|
||||
enzyme_const, Q,
|
||||
enzyme_const, J,
|
||||
enzyme_const, w);
|
||||
// sigma_dot = ∂sigma / ∂H[0, 0]
|
||||
REQUIRE(sigma_dot[0][0] > 0.0);
|
||||
|
||||
// Finite difference derivative approximation
|
||||
constexpr int dim = 3;
|
||||
real_t eps = 1e-5;
|
||||
tensor<real_t, 3, 3> sigma = ComputeStress(&material, H, Q, J, w);
|
||||
tensor<real_t, 3, 3> sigma_p = ComputeStress(&material, H + eps*H_dot, Q, J, w);
|
||||
tensor<real_t, 3, 3> sigma_dot_h = (1.0/eps)*(sigma_p - sigma);
|
||||
|
||||
tensor<real_t, 3, 3> rel_error = sigma_dot - sigma_dot_h;
|
||||
for (int i = 0; i < dim; i++) {
|
||||
for (int j = 0; j < dim; j++) {
|
||||
real_t denom = sigma[i][j] != 0? sigma[i][j] : 1.0;
|
||||
rel_error[i][j] /= denom;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(elementwise_max_norm(rel_error) < 1e-5);
|
||||
}
|
||||
|
||||
SECTION("VJP")
|
||||
{
|
||||
// compare reverse mode derivative to finite differences
|
||||
|
||||
tensor<real_t, 3, 3> sigma;
|
||||
ComputeStressRef(&material, H, Q, J, w, sigma);
|
||||
double epsilon = 1e-6;
|
||||
tensor<real_t, 3, 3> dH{{{1.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}};
|
||||
auto H_p = H + epsilon*dH;
|
||||
tensor<real_t, 3, 3> sigma_p;
|
||||
ComputeStressRef(&material, H_p, Q, J, w, sigma_p);
|
||||
auto sigma_dot_h = (sigma_p - sigma)/epsilon;
|
||||
// Note: sigma_dot_h[i,j] = ∂sigma[i,j]/∂H[0,0]
|
||||
|
||||
tensor<real_t, 3, 3> sigma_bar{{{1.0, 0.0, 0.0},
|
||||
{0.0, 0.0, 0.0},
|
||||
{0.0, 0.0, 0.0}}};
|
||||
|
||||
tensor<real_t, 3, 3> H_bar{};
|
||||
J2Plasticity::PackedInternalState Q_bar{};
|
||||
tensor<real_t, 3, 3> J_bar{};
|
||||
__enzyme_autodiff<void>(
|
||||
(void*)ComputeStressRef, enzyme_const, &material, enzyme_dup, &H, &H_bar,
|
||||
enzyme_dup, &Q, &Q_bar, enzyme_dup, &J, &J_bar, enzyme_const, w,
|
||||
enzyme_dup, &sigma, &sigma_bar);
|
||||
|
||||
// H_bar[ij] = ∂sigma[0,0]/∂H[i,j]
|
||||
// For this model, we expect the major symmetries in the tangent operator.
|
||||
// Hence H_bar \approx sigma_dot_h
|
||||
|
||||
const double abs_tol = 1e-12;
|
||||
const double rel_tol = 5e-6;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
CHECK(H_bar[i][j] == MFEM_Approx(sigma_dot_h[i][j], abs_tol, rel_tol));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
real_t nthroot_res(real_t x, tuple<real_t, real_t> p)
|
||||
{
|
||||
auto [index, radicand] = p;
|
||||
return std::pow(x, index) - radicand;
|
||||
}
|
||||
|
||||
__attribute__((used))
|
||||
void* __enzyme_register_gradient_solver[3] = {
|
||||
(void*)mfem::internal::SolveNewtonBisection_impl<nthroot_res, tuple<real_t, real_t>>,
|
||||
(void*)mfem::internal::SolveNewtonBisection_impl_aug<nthroot_res, tuple<real_t, real_t>>,
|
||||
(void*)mfem::internal::SolveNewtonBisection_impl_rev<nthroot_res, tuple<real_t, real_t>>
|
||||
};
|
||||
|
||||
TEST_CASE("Univariate solver reverse mode derivative", "[univar]")
|
||||
{
|
||||
auto mysqrt = [](real_t x) -> real_t
|
||||
{
|
||||
real_t x0 = x;
|
||||
real_t index = 2.0;
|
||||
real_t ub = std::max(1.0, x);
|
||||
SolverSettings settings{1e-12, 1e-12, {0, ub}};
|
||||
return SolveNewtonBisection<nthroot_res>(x0, make_tuple(index, x), settings);
|
||||
};
|
||||
|
||||
real_t x = 2.0;
|
||||
real_t dydx = __enzyme_autodiff<real_t>((void*)+mysqrt, enzyme_out, x);
|
||||
CHECK(dydx == MFEM_Approx(0.5/std::sqrt(2.0)));
|
||||
}
|
||||
|
||||
TEST_CASE("Univariate function solver robustness", "[univar]")
|
||||
{
|
||||
SolverSettings settings{1e-12, 1e-12};
|
||||
|
||||
SECTION("Simple case")
|
||||
{
|
||||
auto Nthroot = [&settings](real_t x, real_t n) {
|
||||
real_t x0 = std::max(x, 1.0);
|
||||
settings.bounds = {0.0, x0};
|
||||
return SolveNewtonBisection<nthroot_res>(x0, make_tuple(n, x), settings);
|
||||
};
|
||||
real_t x = 8.0;
|
||||
real_t y = Nthroot(x, 3.0);
|
||||
CHECK(y == MFEM_Approx(2.0));
|
||||
}
|
||||
|
||||
SECTION("Stiff problem")
|
||||
{
|
||||
auto f = [](real_t x, real_t p) { return std::pow(x, p) - 1.0; };
|
||||
real_t x0 = 0.1;
|
||||
real_t p = 50;
|
||||
settings.bounds = {0.0, 5.1};
|
||||
real_t x = SolveNewtonBisection<+f>(x0, p, settings);
|
||||
CHECK(x == MFEM_Approx(1.0));
|
||||
}
|
||||
|
||||
SECTION("Works where Newton diverges")
|
||||
{
|
||||
auto f = [](double x, int) { return std::atan(x); };
|
||||
real_t x0 = 1.5;
|
||||
settings.bounds = {0.0, 2.0};
|
||||
real_t x = SolveNewtonBisection<+f>(x0, int{}, settings);
|
||||
CHECK(std::abs(x) == MFEM_Approx(0.0));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_ENZYME
|
||||
@@ -117,3 +117,52 @@ TEST_CASE("Vector FE Face Restriction", "[FaceRestriction]")
|
||||
gf2 -= gf;
|
||||
REQUIRE(gf2.Normlinf() == MFEM_Approx(0.0));
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
TEST_CASE("L2 Face Restriction", "[FaceRestriction][Parallel]")
|
||||
{
|
||||
const int dim = GENERATE(2, 3);
|
||||
constexpr int nx = 3;
|
||||
constexpr int order = 2;
|
||||
constexpr int vdim = 2;
|
||||
const Ordering::Type ordering = GENERATE(Ordering::byNODES, Ordering::byVDIM);
|
||||
|
||||
Mesh serial_mesh = MakeCartesianMesh(nx, dim);
|
||||
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
|
||||
|
||||
L2_FECollection fec(order, dim, BasisType::GaussLobatto);
|
||||
ParFiniteElementSpace fes(&mesh, &fec, vdim, ordering);
|
||||
|
||||
auto *R = fes.GetFaceRestriction(ElementDofOrdering::LEXICOGRAPHIC,
|
||||
FaceType::Interior);
|
||||
|
||||
Vector vals({1.0, 2.0});
|
||||
VectorConstantCoefficient coeff(vals);
|
||||
|
||||
ParGridFunction gf(&fes);
|
||||
gf.ProjectCoefficient(coeff);
|
||||
|
||||
Vector face_vec(R->Height());
|
||||
R->Mult(gf, face_vec);
|
||||
|
||||
const int nf = mesh.GetNFbyType(FaceType::Interior);
|
||||
const int face_dofs = fes.GetTypicalTraceElement()->GetDof();
|
||||
auto h_face_vec = Reshape(face_vec.HostRead(), face_dofs, vdim, 2, nf);
|
||||
|
||||
for (int f = 0; f < nf; ++f)
|
||||
{
|
||||
for (int m = 0; m < 2; ++m)
|
||||
{
|
||||
for (int c = 0; c < vdim; ++c)
|
||||
{
|
||||
for (int i = 0; i < face_dofs; ++i)
|
||||
{
|
||||
REQUIRE(h_face_vec(i, c, m, f) == vals[c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -281,8 +281,10 @@ TEST_CASE("Nedelec Segment Finite Element",
|
||||
REQUIRE( fe.GetRangeType() == (int) FiniteElement::VECTOR );
|
||||
REQUIRE( fe.GetMapType() == (int) FiniteElement::H_CURL );
|
||||
REQUIRE( fe.GetDerivType() == (int) FiniteElement::NONE );
|
||||
REQUIRE( fe.GetDerivRangeType() == (int) FiniteElement::SCALAR );
|
||||
REQUIRE( fe.GetDerivMapType() == (int) FiniteElement::INTEGRAL);
|
||||
REQUIRE( fe.GetDerivRangeType() ==
|
||||
(int) FiniteElement::UNKNOWN_RANGE_TYPE);
|
||||
REQUIRE( fe.GetDerivMapType() ==
|
||||
(int) FiniteElement::UNKNOWN_MAP_TYPE);
|
||||
}
|
||||
}
|
||||
SECTION("Sizes for p = " + std::to_string(p))
|
||||
|
||||
Reference in New Issue
Block a user