Compare commits

..
Author SHA1 Message Date
Victor DeCaria d31dfa15fa fix missing std::array and std::ignore 2024-11-12 06:36:03 -07:00
Julian Andrej e94b8b6c89 cleanup 2024-11-11 09:53:37 -08:00
Julian Andrej 3f8348d04b update 2024-11-08 10:03:21 -08:00
Julian Andrej e30f7e5aa9 the big refactor 2024-11-07 14:31:30 -08:00
Julian Andrej 047943cfda missing file 2024-10-31 14:50:32 -07:00
Julian Andrej 4e6c38cf6d add qfunction_dual 2024-10-31 14:47:45 -07:00
Julian Andrej c981633846 move integration rule to element operator 2024-10-29 09:34:01 -07:00
Julian Andrej ac2a5107da update nonlinear test 2024-10-28 15:00:30 -07:00
Julian Andrej e6d733aa95 native ad 2024-10-28 14:53:26 -07:00
Julian Andrej c6ee709eef directly pass through quadrature point data 2024-10-28 09:57:49 -07:00
Julian Andrej 0274b67ff6 demo updates 2024-10-28 08:30:45 -07:00
Julian Andrej 38da503958 refactors 2024-10-18 15:14:46 -07:00
Julian Andrej 6c4f9c69a9 refactor 2024-10-18 11:07:07 -07:00
Julian Andrej 0702eb2fb2 refactor 2024-10-18 10:01:41 -07:00
Julian Andrej 7a62a7fd4a add more tests 2024-10-15 16:13:04 -07:00
Julian Andrej 0f94d484a4 more updates 2024-10-15 12:44:49 -07:00
Julian Andrej 4b5798f905 updateees 2024-10-15 12:44:20 -07:00
Julian Andrej 87240f5619 reorder loops 2024-10-11 11:16:50 -07:00
Julian Andrej 3ccaa48cd4 fixes 2024-10-10 11:09:04 -07:00
Julian Andrej a08928dd97 benchmark 2024-10-09 13:11:03 -07:00
Julian Andrej a0694d5825 tweaks 2024-10-09 12:39:57 -07:00
Julian Andrej a3ecbef0ec partial assembly test for 3d diffusion 2024-10-09 09:16:05 -07:00
Julian Andrej db5bc1725e three dee 2024-10-08 07:19:57 -07:00
Julian Andrej 6d50ebc9c3 derpderp 2024-09-18 15:53:22 -07:00
Julian Andrej e713913177 derp 2024-09-18 15:41:36 -07:00
Julian Andrej dd9898af3b SYNC ALL THE SYNCS 2024-09-18 13:10:44 -07:00
Julian Andrej 0435ef5dac laghos progress 2024-09-17 15:02:56 -07:00
Julian Andrej 79e5020a18 device 2024-09-13 20:54:37 -07:00
Julian Andrej adc81c7f5d hd annot 2024-09-13 20:42:20 -07:00
Julian Andrej 18676c61b7 HD annotation 2024-09-13 20:39:29 -07:00
Julian Andrej 8f8deab121 add missing examples 2024-09-13 20:33:45 -07:00
Julian Andrej 8b0c779320 get laghos example to work 2024-09-13 19:32:18 -07:00
Julian Andrej a012769434 reintroduce derivatives 2024-09-11 16:29:34 -07:00
Julian Andrej ce5517b9af remove old dfem header 2024-09-11 16:25:43 -07:00
Julian Andrej 70ae37d5f0 sync 2024-08-28 16:23:59 -07:00
Julian Andrej feac718e95 Merge branch 'master' into dfem-coefficient
# Conflicts:
#	CMakeLists.txt
2024-08-26 15:19:10 -07:00
Julian Andrej 9be8c15cf8 performance updates 2024-08-22 07:43:21 -07:00
Julian Andrej 08ba45fca3 more shmemenigans 2024-08-19 13:15:31 -07:00
Julian Andrej 3aacfbfab0 threaded loops 2024-08-19 11:13:38 -07:00
Julian Andrej 2a60b998c7 more shmem shenan 2024-08-19 10:59:56 -07:00
Julian Andrej 91a168929f maybe 2024-08-16 08:12:48 -07:00
Julian Andrej 1369f5e189 still bugs 2024-08-16 07:33:29 -07:00
Julian Andrej 993e4fbbe1 buuugs 2024-08-15 10:58:37 -07:00
Julian Andrej 07d8a17abe simplification 2024-08-15 09:38:23 -07:00
Julian Andrej 5531b82dbc buugs 2024-08-15 07:46:21 -07:00
Julian Andrej d9f60f401b shmem info doc 2024-08-15 07:41:58 -07:00
Julian Andrej fb876ba3a1 shared memory bug 2024-08-15 07:41:37 -07:00
Julian Andrej 32a94b438f more shared memory 2024-08-15 07:37:02 -07:00
Julian Andrej 917978d310 refactor for tensor product elements 2024-08-14 13:10:35 -07:00
Julian Andrej a62302b4cb make input qp memory thread safe 2024-07-29 12:28:00 -07:00
Julian Andrej f5d2b82839 add device config to tests 2024-07-26 15:13:23 -07:00
Julian Andrej 1382f8aa1f more gpu compat 2024-07-26 14:58:08 -07:00
Julian Andrej f76a884d15 more device sanitizing 2024-07-26 14:00:52 -07:00
Julian Andrej 5c56659e46 add tuple impl 2024-07-26 12:59:09 -07:00
Julian Andrej 4bcd4586ba add serac::tuple 2024-07-26 12:51:00 -07:00
Julian Andrej 083d42c6ce try other initializer 2024-07-26 11:37:33 -07:00
Julian Andrej dbc3458db0 MFEM_HOST_DEVICE 2024-07-26 11:33:43 -07:00
Julian Andrej 4f694287ae host device annotations 2024-07-26 11:32:33 -07:00
Julian Andrej 5d8fbfee93 clean up use of Vector for device prep 2024-07-26 11:21:57 -07:00
Julian Andrej 7799753053 forall capture 2024-07-24 12:48:51 -07:00
Julian Andrej cb6db58ad3 simplify conversion 2024-07-22 10:12:34 -07:00
Julian Andrej 5da2bfc23d cruft 2024-07-22 10:12:16 -07:00
Julian Andrej 466a771ab0 bugfix 2024-07-22 10:11:44 -07:00
Julian Andrej a317e1a17d more options 2024-07-15 09:01:47 -07:00
Julian Andrej fcbde98cb6 working laghos example 2024-07-11 08:04:14 -07:00
Julian Andrej 80cb02328c add normal test 2024-06-10 10:27:39 -07:00
Julian Andrej 1847e460cf working boundary operators 2024-06-10 08:07:37 -07:00
Julian Andrej 1cd46aa768 add qoi derivatives, dual type option and qoi derivative assembly 2024-06-05 08:31:01 -07:00
Julian Andrej 11edee7aca remove custom enzyme cmake module 2024-06-05 08:29:28 -07:00
Julian Andrej 28115e5de2 temporarily add cmake targets 2024-06-05 08:29:09 -07:00
Julian Andrej 20954328c3 add enzyme to cmake 2024-06-05 08:28:58 -07:00
Julian Andrej 41e92219ee reorganize files and navier stokes example 2024-05-21 09:01:10 -07:00
Julian Andrej a0c3620618 relocate restrictions to individual operators 2024-05-06 08:19:05 -07:00
Julian Andrej 9c791bed5a starting boundary and L2 2024-05-02 15:39:17 -07:00
Julian Andrej 42d0fc17a1 updates 2024-04-29 08:53:33 -07:00
Julian Andrej 2d2da417bb bugfixes 2024-04-25 15:05:21 -07:00
Julian Andrej 2e86ccb948 working assembly 2024-04-22 08:36:39 -07:00
Julian Andrej c7fe1ff1f4 working most recent interface iteration 2024-03-25 09:38:56 -07:00
306 changed files with 21169 additions and 8050 deletions
-31
View File
@@ -1,31 +0,0 @@
# Copyright (c) 2010-2024, 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.
name: "Trigger PyMFEM CI"
on:
push:
branches:
- master
jobs:
trigger-pymfem:
runs-on: ubuntu-latest
steps:
- name: Send POST request to trigger PyMFEM CI
run: |
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.PYMFEM_CI_TOKEN }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/mfem/pymfem/actions/workflows/build-and-test-dispatch.yml/dispatches \
-d '{"ref":"master", "inputs":{"test_options":"fast"}}'
-3
View File
@@ -15,9 +15,6 @@
CMakeCache.txt
CMakeFiles/
# Clangd server cache
*.cache*
# Backup files
*~
-15
View File
@@ -30,26 +30,11 @@ New and updated examples and miniapps
- Added an MFEM example for the eikonal equation. This new solver is based on
the proximal Galerkin method introduced by Keith and Surowiec.
- Added a command line option to all miniapps (`-p` or `--send-port`) for
specifying the GLVis server socket port (19916 by default).
GPU computing
-------------
- Added support for GPU-accelerated batched linear algebra (using cuBLAS,
hipBLAS, MAGMA, or native MFEM functionality) through the BatchedLinAlg class.
- A new GPU kernel dispatch mechanism was introduced. Users can instantiate
specialized kernels for specific combinations of (for example) polynomial
degree and number of quadrature points using
`DiffusionIntegrator::AddSpecialization` and
`MassIntegrator::AddSpecialization` (this functionality may be added to more
integrators in the future).
- Calls to slower fallback kernels can be reported to `mfem::err` by setting
the environment variable `MFEM_REPORT_KERNELS` to any value other than `NO`
or by explicitly calling `KernelReporter::Enable`. Users can then add
specializations for these kernels to achieve higher performance.
Miscellaneous
-------------
- Refactored the `ARKStepSolver` class (ARKODE interface) to use
+9 -1
View File
@@ -522,7 +522,10 @@ endif()
# Enzyme
if (MFEM_USE_ENZYME)
find_package(ENZYME REQUIRED)
find_package(Enzyme REQUIRED HINTS ${ENZYME_DIR})
message(STATUS "Enzyme found in ${ENZYME_DIR}.")
set(ENZYME_INCLUDE_DIRS ${ENZYME_DIR}/include)
set(ENZYME_FOUND 1)
endif()
# MFEM_TIMER_TYPE
@@ -629,6 +632,11 @@ set(MFEM_INSTALL_DIR ${CMAKE_INSTALL_PREFIX} CACHE PATH
mfem_add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES})
if (MFEM_USE_ENZYME)
target_link_libraries(mfem PUBLIC ClangEnzymeFlags)
endif()
if (MINGW)
target_link_libraries(mfem PRIVATE ws2_32)
endif()
-27
View File
@@ -1,27 +0,0 @@
# Copyright (c) 2010-2024, 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.
message(STATUS "Looking for ENZYME ...")
message(STATUS " in ENZYME_DIR = ${ENZYME_DIR}")
# Make sure the directory and version combination works. Do nothing otherwise.
if(EXISTS "${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so")
message(STATUS "Found ENZYME: ${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so")
# Set ENZYME_FOUND
set(ENZYME_FOUND TRUE CACHE BOOL "ENZYME was found." FORCE)
# Set CXX flags to accommodate the Enzyme Clang plugin
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -load -Xclang ${ENZYME_DIR}/ClangEnzyme-${ENZYME_VERSION}.so -mllvm -enzyme-loose-types=1")
set(MFEM_USE_ENZYME YES)
else()
endif()
+35
View File
@@ -0,0 +1,35 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
#
dimension
2
elements
1
1 3 0 1 2 3
boundary
4
1 1 0 1
2 1 1 2
3 1 2 3
4 1 3 0
vertices
4
2
0 0
1 0.3
1.4 1.2
0.25 1.34
+33
View File
@@ -50,6 +50,28 @@ list(APPEND ALL_EXE_SRCS
if (MFEM_USE_MPI)
list(APPEND ALL_EXE_SRCS
dfem_poisson.cpp
dfem_stokes.cpp
enzyme_interface_smoketest.cpp
test_dfem_dual.cpp
test_dfem.cpp
dfem_laghos.cpp
dfem_minimal_example.cpp
dfem_test_diffusion_2d.cpp
dfem_test_diffusion_3d.cpp
dfem_test_diffusion_3d_refactor.cpp
dfem_test_ordering.cpp
dfem_test_vector_diffusion.cpp
dfem_test_elasticity.cpp
dfem_test_nonlinear_elasticity_3d.cpp
dfem_test_nonlinear_diffusion_3d.cpp
dfem_test_interpolate_linear_scalar.cpp
dfem_test_interpolate_linear_scalar_3d.cpp
dfem_test_interpolate_gradient_linear_scalar_3d.cpp
dfem_test_mass_scalar_3d.cpp
dfem_test_mass_scalar_2d.cpp
dfem_test_interpolate_linear_vector.cpp
dfem_test_interpolate_linear_vector_3d.cpp
ex0p.cpp
ex1p.cpp
ex2p.cpp
@@ -110,6 +132,17 @@ include_directories(BEFORE ${PROJECT_BINARY_DIR})
# Add one executable per cpp file
add_mfem_examples(ALL_EXE_SRCS)
target_link_libraries(dfem_poisson ClangEnzymeFlags)
target_link_libraries(dfem_stokes ClangEnzymeFlags)
target_link_libraries(enzyme_interface_smoketest ClangEnzymeFlags)
target_link_libraries(test_dfem ClangEnzymeFlags)
target_link_libraries(dfem_laghos ClangEnzymeFlags)
target_link_libraries(dfem_minimal_example ClangEnzymeFlags)
target_link_libraries(dfem_test_diffusion_3d ClangEnzymeFlags)
target_link_libraries(dfem_test_diffusion_3d_refactor ClangEnzymeFlags)
target_link_libraries(dfem_test_nonlinear_diffusion_3d ClangEnzymeFlags)
target_link_libraries(dfem_test_nonlinear_elasticity_3d ClangEnzymeFlags)
# Add a test for each example
if (MFEM_ENABLE_TESTING)
foreach(SRC_FILE ${ALL_EXE_SRCS})
+184
View File
@@ -0,0 +1,184 @@
/*
MIT License
Copyright (c) 2017 André L. Maravilha
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef CXX_TIMER_HPP
#define CXX_TIMER_HPP
#include <chrono>
namespace cxxtimer {
/**
* This class works as a stopwatch.
*/
class Timer {
public:
/**
* Constructor.
*
* @param start
* If true, the timer is started just after construction.
* Otherwise, it will not be automatically started.
*/
Timer(bool start = false);
/**
* Copy constructor.
*
* @param other
* The object to be copied.
*/
Timer(const Timer& other) = default;
/**
* Transfer constructor.
*
* @param other
* The object to be transferred.
*/
Timer(Timer&& other) = default;
/**
* Destructor.
*/
virtual ~Timer() = default;
/**
* Assignment operator by copy.
*
* @param other
* The object to be copied.
*
* @return A reference to this object.
*/
Timer& operator=(const Timer& other) = default;
/**
* Assignment operator by transfer.
*
* @param other
* The object to be transferred.
*
* @return A reference to this object.
*/
Timer& operator=(Timer&& other) = default;
/**
* Start/resume the timer.
*/
void start();
/**
* Stop/pause the timer.
*/
void stop();
/**
* Reset the timer.
*/
void reset();
/**
* Return the elapsed time.
*
* @param duration_t
* The duration type used to return the time elapsed. If not
* specified, it returns the time as represented by
* std::chrono::milliseconds.
*
* @return The elapsed time.
*/
template <class duration_t = std::chrono::milliseconds>
typename duration_t::rep count() const;
private:
bool started_;
bool paused_;
std::chrono::steady_clock::time_point reference_;
std::chrono::duration<long double> accumulated_;
};
}
inline cxxtimer::Timer::Timer(bool start) :
started_(false), paused_(false),
reference_(std::chrono::steady_clock::now()),
accumulated_(std::chrono::duration<long double>(0)) {
if (start) {
this->start();
}
}
inline void cxxtimer::Timer::start() {
if (!started_) {
started_ = true;
paused_ = false;
accumulated_ = std::chrono::duration<long double>(0);
reference_ = std::chrono::steady_clock::now();
} else if (paused_) {
reference_ = std::chrono::steady_clock::now();
paused_ = false;
}
}
inline void cxxtimer::Timer::stop() {
if (started_ && !paused_) {
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
accumulated_ = accumulated_ + std::chrono::duration_cast< std::chrono::duration<long double> >(now - reference_);
paused_ = true;
}
}
inline void cxxtimer::Timer::reset() {
if (started_) {
started_ = false;
paused_ = false;
reference_ = std::chrono::steady_clock::now();
accumulated_ = std::chrono::duration<long double>(0);
}
}
template <class duration_t>
typename duration_t::rep cxxtimer::Timer::count() const {
if (started_) {
if (paused_) {
return std::chrono::duration_cast<duration_t>(accumulated_).count();
} else {
return std::chrono::duration_cast<duration_t>(
accumulated_ + (std::chrono::steady_clock::now() - reference_)).count();
}
} else {
return duration_t(0).count();
}
}
#endif
+4
View File
@@ -0,0 +1,4 @@
#pragma once
#include "dfem_differentiable_operator.hpp"
#include "dfem_element_operator.hpp"
+232
View File
@@ -0,0 +1,232 @@
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields,
size_t num_kernels
>
template <
typename kernel_t
>
void DifferentiableOperator<kernels_tuple,
num_solutions,
num_parameters,
num_fields,
num_kernels>::Action::create_action_callback(
kernel_t kernel,
mult_func_t &func)
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
const int num_entities = GetNumEntities<entity_t>(op.mesh);
const int num_qp = op.integration_rule.GetNPoints();
// All solutions T-vector sizes make up the width of the operator, since
// they are explicitly provided in Mult() for example.
op.width = GetTrueVSize(op.fields[test_space_field_idx]);
op.residual_lsize = GetVSize(op.fields[test_space_field_idx]);
if constexpr (std::is_same_v<decltype(output_fop), One>)
{
op.height = 1;
}
else
{
op.height = op.residual_lsize;
}
residual_l.SetSize(op.residual_lsize);
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : op.fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
doftoquad_mode));
}
const int q1d = (int)floor(pow(num_qp, 1.0/op.mesh.Dimension()) + 0.5);
residual_e.SetSize(R->Height());
const int residual_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(kernel.outputs),
op.fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
koutput_to_field);
auto input_fops = create_bare_fops(kernel.inputs);
auto output_fops = create_bare_fops(kernel.outputs);
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(output_fops).vdim /
num_entities;
auto ir_weights = Reshape(this->op.integration_rule.GetWeights().Read(),
num_qp);
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
output_dtq_maps,
op.fields,
num_entities,
kernel.inputs,
num_qp,
input_size_on_qp,
residual_size_on_qp);
Vector shmem_cache(shmem_info.total_size);
print_shared_memory_info(shmem_info);
func = [=](Vector &ye_mem) mutable
{
restriction<entity_t>(op.solutions, solutions_l, this->fields_e,
op.element_dof_ordering);
restriction<entity_t>(op.parameters, parameters_l, this->fields_e,
op.element_dof_ordering,
op.solutions.size());
auto ye = Reshape(ye_mem.ReadWrite(), test_vdim, num_test_dof, num_entities);
auto wrapped_fields_e = wrap_fields(this->fields_e, shmem_info.field_sizes, num_entities);
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
{
// printf("\ne: %d\n", e);
// tic();
auto input_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
shmem_info.input_dtq_sizes,
input_dtq_maps);
auto output_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
shmem_info.output_dtq_sizes,
output_dtq_maps);
auto fields_shmem = load_field_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::FIELD],
shmem_info.field_sizes,
kinput_to_field,
wrapped_fields_e,
e);
// These methods don't copy, they simply create a `DeviceTensor` object
// that points to correct chunks of the shared memory pool.
auto input_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT],
shmem_info.input_sizes,
num_qp);
auto residual_shmem = load_residual_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT],
shmem_info.residual_size,
num_qp);
auto scratch_mem = load_scratch_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::TEMP],
shmem_info.temp_sizes);
MFEM_SYNC_THREAD;
// printf("shmem load elapsed: %.1fus\n", toc() * 1e6);
// tic();
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, input_fops, ir_weights, scratch_mem,
std::make_index_sequence<kernel.num_kinputs> {});
// printf("interpolate elapsed: %.1fus\n", toc() * 1e6);
// tic();
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);
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
auto r = Reshape(&residual_shmem(0, q), residual_size_on_qp);
apply_kernel(r, kernel.func, kernel_args, input_shmem, q);
}
}
}
MFEM_SYNC_THREAD;
// printf("qf elapsed: %.1fus\n", toc() * 1e6);
// tic();
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(output_fops),
output_dtq_shmem[hardcoded_output_idx],
scratch_mem);
// printf("integrate elapsed: %.1fus\n", toc() * 1e6);
}, num_entities, q1d, q1d, q1d, shmem_info.total_size, shmem_cache.ReadWrite());
if constexpr (std::is_same_v<decltype(output_fop), None>)
{
residual_l = ye_mem;
}
else
{
R->MultTranspose(ye_mem, residual_l);
}
};
if constexpr (std::is_same_v<decltype(output_fop), None>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
y = r_local;
};
}
else if constexpr (std::is_same_v<decltype(output_fop), One>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
double local_sum = r_local.Sum();
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
op.mesh.GetComm());
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
};
}
else
{
auto P = get_prolongation(op.fields[test_space_field_idx]);
prolongation_transpose = [P](const Vector &r_local, Vector &y)
{
P->MultTranspose(r_local, y);
};
}
}
@@ -0,0 +1,308 @@
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields,
size_t num_kernels
>
template <
size_t derivative_idx
>
template <
typename kernel_t
>
void DifferentiableOperator<kernels_tuple,
num_solutions,
num_parameters,
num_fields,
num_kernels>::Derivative<derivative_idx>::assemble_hypreparmatrix_impl(
kernel_t kernel, HypreParMatrix &A)
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs,
std::make_index_sequence<kernel.num_koutputs> {});
auto output_fop = std::get<0>(kernel.outputs);
constexpr int hardcoded_output_idx = 0;
int num_qp = op.integration_rule.GetNPoints();;
int num_el = 0;
int dimension = 0;
if constexpr (std::is_same_v<entity_t, Entity::Element>)
{
num_el = op.mesh.GetNE();
dimension = op.dim;
}
else if (std::is_same_v<entity_t, Entity::Face>)
{
num_el = op.mesh.GetNumFacesWithGhost();
dimension = op.dim - 1;
}
else
{
static_assert(always_false<entity_t>, "not implemented");
}
std::vector<const DofToQuad*> dtqmaps;
for (const auto &field : op.fields)
{
dtqmaps.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
doftoquad_mode));
}
// Allocate memory for fields on quadrature points
auto input_qp_mem = create_input_qp_memory(num_qp, kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto directions_qp_mem = create_input_qp_memory(num_qp, kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
for (auto &d_qp_mem : directions_qp_mem)
{
d_qp_mem = 0.0;
}
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
bool no_kinput_is_dependent = true;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_to_field[i] == derivative_idx)
{
no_kinput_is_dependent = false;
kinput_is_dependent[i] = true;
// out << "function input " << i << " is dependent on "
// << op.fields[kinput_to_field[i]].field_label << "\n";
}
else
{
kinput_is_dependent[i] = false;
}
}
if (no_kinput_is_dependent)
{
return;
}
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
DeviceTensor<1, const double> integration_weights(
this->op.integration_rule.GetWeights().Read(), num_qp);
Vector zero;
GeometricFactorMaps geometric_factors
{
DeviceTensor<3, const double>(zero.Read(), 0, 0, 0)
};
// fields interpolated to the quadrature points in the order of
// kernel function arguments
auto input_qp = map_inputs_to_memory(input_qp_mem, num_qp,
kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto directions_qp = map_inputs_to_memory(directions_qp_mem, num_qp,
kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto input_dtq_ops = create_dtq_operators<entity_t>(kernel.inputs, dtqmaps,
kinput_to_field);
auto dependent_input_dtq_ops = create_dtq_operators_conditional<entity_t>(
kernel.inputs,
dtqmaps,
kinput_to_field,
kinput_is_dependent, std::make_index_sequence<kernel.num_kinputs> {});
auto output_dtq_ops = create_dtq_operators<entity_t>(kernel.outputs, dtqmaps,
koutput_to_field);
constexpr int fixed_output_idx = 0;
auto Bv = output_dtq_ops[fixed_output_idx];
auto [num_test_qp, test_op_dim, num_test_dof] = Bv.GetShape();
const int test_vdim = std::get<0>(kernel.outputs).vdim;
const int num_trial_dof = dependent_input_dtq_ops[0].GetShape()[2];
int trial_vdim = 0;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_is_dependent[i])
{
trial_vdim = GetVDim(op.fields[kinput_to_field[i]]);
break;
}
}
// All trial operators dimensions accumulated
int total_trial_op_dim = 0;
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
{
total_trial_op_dim += dependent_input_dtq_ops[s].GetShape()[1];
}
Vector a_qp_mem(test_vdim * test_op_dim * trial_vdim * total_trial_op_dim *
num_qp *
num_el);
const auto a_qp = Reshape(a_qp_mem.ReadWrite(), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp,
num_el);
Vector Ae_mem(num_test_dof * test_vdim * num_trial_dof * trial_vdim * num_el);
Ae_mem = 0.0;
auto A_e = Reshape(Ae_mem.ReadWrite(), num_test_dof, test_vdim, num_trial_dof,
trial_vdim, num_el);
for (int e = 0; e < num_el; e++)
{
map_fields_to_quadrature_data(
input_qp, e, this->fields_e,
kinput_to_field, input_dtq_ops,
integration_weights, geometric_factors, kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
for (int q = 0; q < num_qp; q++)
{
for (int j = 0; j < trial_vdim; j++)
{
size_t m_offset = 0;
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
{
auto Bu = dependent_input_dtq_ops[s];
auto [unused1, trial_op_dim, unused2] = Bu.GetShape();
auto d_qp = Reshape(&(directions_qp[Bu.which_input])[0], trial_vdim,
trial_op_dim, num_qp);
for (int m = 0; m < trial_op_dim; m++)
{
d_qp(j, m, q) = 1.0;
Vector f_qp = apply_kernel_fwddiff_enzyme(
kernel.func,
kernel_args,
input_qp,
kernel_shadow_args,
directions_qp,
q);
// Vector f_qp = apply_kernel_fwddiff_dual(
// kernel.func,
// kernel_args,
// input_qp,
// directions_qp,
// q);
d_qp(j, m, q) = 0.0;
auto f = Reshape(f_qp.Read(), test_vdim, test_op_dim);
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
a_qp(i, k, j, m + m_offset, q, e) = f(i, k);
}
}
}
m_offset += trial_op_dim;
}
}
}
Vector fhat_mem(test_op_dim * num_qp * dimension);
auto fhat = Reshape(fhat_mem.ReadWrite(), test_vdim, test_op_dim, num_qp);
for (int J = 0; J < num_trial_dof; J++)
{
for (int j = 0; j < trial_vdim; j++)
{
fhat_mem = 0.0;
size_t m_offset = 0;
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
{
auto Bu = dependent_input_dtq_ops[s];
int trial_op_dim = dependent_input_dtq_ops[s].GetShape()[1];
for (int q = 0; q < num_qp; q++)
{
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
for (int m = 0; m < trial_op_dim; m++)
{
fhat(i, k, q) += a_qp(i, k, j, m + m_offset, q, e) * Bu(q, m, J);
}
}
}
}
m_offset += trial_op_dim;
}
auto bvtfhat = Reshape(&A_e(0, 0, J, j, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields(bvtfhat, fhat, output_fop,
output_dtq_ops[hardcoded_output_idx]);
}
}
}
bool same_test_and_trial = false;
if (koutput_to_field[0] ==
kinput_to_field[dependent_input_dtq_ops[0].which_input])
{
same_test_and_trial = true;
}
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
(&op.fields[kinput_to_field[dependent_input_dtq_ops[0].which_input]].data);
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
(&op.fields[koutput_to_field[0]].data);
SparseMatrix mat(test_fes->GlobalVSize(), trial_fes->GlobalVSize());
if (test_fes == nullptr)
{
MFEM_ABORT("error");
}
for (int e = 0; e < num_el; e++)
{
auto tmp = Reshape(Ae_mem.ReadWrite(), num_test_dof * test_vdim,
num_trial_dof * trial_vdim,
num_el);
DenseMatrix A_e(&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);
GetElementVDofs(
op.fields[kinput_to_field[dependent_input_dtq_ops[0].which_input]], e,
trial_vdofs);
mat.AddSubMatrix(test_vdofs, trial_vdofs, A_e, 1);
}
mat.Finalize();
if (same_test_and_trial)
{
HypreParMatrix tmp(test_fes->GetComm(),
test_fes->GlobalVSize(),
test_fes->GetDofOffsets(),
&mat);
A = *RAP(&tmp, test_fes->Dof_TrueDof_Matrix());
A.EliminateBC(op.ess_tdof_list, DiagonalPolicy::DIAG_ONE);
}
else
{
HypreParMatrix tmp(test_fes->GetComm(),
test_fes->GlobalVSize(),
trial_fes->GlobalVSize(),
test_fes->GetDofOffsets(),
trial_fes->GetDofOffsets(),
&mat);
A = *RAP(test_fes->Dof_TrueDof_Matrix(), &tmp, trial_fes->Dof_TrueDof_Matrix());
// A.EliminateBC(op.ess_tdof_list, DiagonalPolicy::DIAG_ONE);
}
}
+233
View File
@@ -0,0 +1,233 @@
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields,
size_t num_kernels
>
template <
size_t derivative_idx
>
template <
typename kernel_t
>
void DifferentiableOperator<kernels_tuple,
num_solutions,
num_parameters,
num_fields,
num_kernels>::Derivative<derivative_idx>::assemble_vector_impl(
kernel_t kernel, Vector &v)
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs,
std::make_index_sequence<kernel.num_koutputs> {});
auto output_fop = std::get<0>(kernel.outputs);
constexpr int hardcoded_output_idx = 0;
int num_qp = op.integration_rule.GetNPoints();;
int num_el = 0;
int dimension = 0;
if constexpr (std::is_same_v<entity_t, Entity::Element>)
{
num_el = op.mesh.GetNE();
dimension = op.dim;
}
else if (std::is_same_v<entity_t, Entity::Face>)
{
num_el = op.mesh.GetNumFacesWithGhost();
dimension = op.dim - 1;
}
else
{
static_assert(always_false<entity_t>, "not implemented");
}
std::vector<const DofToQuad*> dtqmaps;
for (const auto &field : op.fields)
{
dtqmaps.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
doftoquad_mode));
}
// Allocate memory for fields on quadrature points
auto input_qp_mem = create_input_qp_memory(num_qp, kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto directions_qp_mem = create_input_qp_memory(num_qp, kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
for (auto &d_qp_mem : directions_qp_mem)
{
d_qp_mem = 0.0;
}
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
bool no_kinput_is_dependent = true;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_to_field[i] == derivative_idx)
{
no_kinput_is_dependent = false;
kinput_is_dependent[i] = true;
// out << "function input " << i << " is dependent on "
// << op.fields[kinput_to_field[i]].field_label << "\n";
}
else
{
kinput_is_dependent[i] = false;
}
}
if (no_kinput_is_dependent)
{
return;
}
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
DeviceTensor<1, const double> integration_weights(
this->op.integration_rule.GetWeights().Read(), num_qp);
Vector zero;
GeometricFactorMaps geometric_factors
{
DeviceTensor<3, const double>(zero.Read(), 0, 0, 0)
};
// fields interpolated to the quadrature points in the order of
// kernel function arguments
auto input_qp = map_inputs_to_memory(input_qp_mem, num_qp,
kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto directions_qp = map_inputs_to_memory(directions_qp_mem, num_qp,
kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto input_dtq_ops = create_dtq_operators<entity_t>(kernel.inputs, dtqmaps,
kinput_to_field);
auto dependent_input_dtq_ops = create_dtq_operators_conditional<entity_t>(
kernel.inputs,
dtqmaps,
kinput_to_field,
kinput_is_dependent, std::make_index_sequence<kernel.num_kinputs> {});
auto output_dtq_ops = create_dtq_operators<entity_t>(kernel.outputs, dtqmaps,
koutput_to_field);
constexpr int fixed_output_idx = 0;
auto Bv = output_dtq_ops[fixed_output_idx];
auto [num_test_qp, test_op_dim, num_test_dof] = Bv.GetShape();
const int test_vdim = std::get<0>(kernel.outputs).vdim;
const int num_trial_dof = dependent_input_dtq_ops[0].GetShape()[2];
int trial_vdim = 0;
int dependent_field_idx = -1;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_is_dependent[i])
{
dependent_field_idx = kinput_to_field[i];
break;
}
}
trial_vdim = GetVDim(op.fields[dependent_field_idx]);
// All trial operators dimensions accumulated
int total_trial_op_dim = 0;
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
{
total_trial_op_dim += dependent_input_dtq_ops[s].GetShape()[1];
}
Vector a_qp_mem(trial_vdim * total_trial_op_dim * num_qp * num_el);
const auto a_qp = Reshape(a_qp_mem.ReadWrite(), trial_vdim,
total_trial_op_dim, num_qp, num_el);
Vector ve_mem(num_trial_dof * trial_vdim * num_el);
ve_mem = 0.0;
for (int e = 0; e < num_el; e++)
{
map_fields_to_quadrature_data(
input_qp, e, this->fields_e,
kinput_to_field, input_dtq_ops,
integration_weights, geometric_factors, kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
for (int q = 0; q < num_qp; q++)
{
for (int j = 0; j < trial_vdim; j++)
{
size_t m_offset = 0;
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
{
auto Bu = dependent_input_dtq_ops[s];
auto [unused1, trial_op_dim, unused2] = Bu.GetShape();
auto d_qp = Reshape(&(directions_qp[Bu.which_input])[0], trial_vdim,
trial_op_dim, num_qp);
for (int m = 0; m < trial_op_dim; m++)
{
d_qp(j, m, q) = 1.0;
// Vector f_qp = apply_kernel_fwddiff_dual(
// kernel.func,
// kernel_args,
// input_qp,
// directions_qp,
// q);
Vector f_qp = apply_kernel_fwddiff_enzyme(
kernel.func,
kernel_args,
input_qp,
kernel_shadow_args,
directions_qp,
q);
d_qp(j, m, q) = 0.0;
auto f = Reshape(f_qp.Read(), test_vdim);
a_qp(j, m + m_offset, q, e) = f(0);
}
m_offset += trial_op_dim;
}
}
}
auto shat = Reshape(ve_mem.ReadWrite(), num_trial_dof, trial_vdim, num_el);
for (int J = 0; J < num_trial_dof; J++)
{
for (int j = 0; j < trial_vdim; j++)
{
size_t m_offset = 0;
for (int s = 0; s < dependent_input_dtq_ops.size(); s++)
{
auto Bu = dependent_input_dtq_ops[s];
int trial_op_dim = dependent_input_dtq_ops[s].GetShape()[1];
for (int q = 0; q < num_qp; q++)
{
for (int m = 0; m < trial_op_dim; m++)
{
shat(J, j, e) += a_qp(j, m + m_offset, q, e) * Bu(q, m, J);
}
}
m_offset += trial_op_dim;
}
}
}
}
auto R = get_element_restriction(op.fields[dependent_field_idx],
element_dof_ordering);
Vector ve(R->Width());
R->MultTranspose(ve_mem, ve);
get_prolongation(op.fields[dependent_field_idx])->MultTranspose(ve, v);
}
+244
View File
@@ -0,0 +1,244 @@
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields,
size_t num_kernels
>
template <
size_t derivative_idx
>
template <
typename kernel_t
>
void DifferentiableOperator<kernels_tuple,
num_solutions,
num_parameters,
num_fields,
num_kernels>::Derivative<derivative_idx>::create_callback(kernel_t kernel,
mult_func_t &func)
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
const int num_entities = GetNumEntities<entity_t>(op.mesh);
const int num_qp = op.integration_rule.GetNPoints();
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : op.fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
doftoquad_mode));
}
const int q1d = dtq[0]->nqpt;
derivative_action_e.SetSize(R->Height());
const int da_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(kernel.outputs),
op.fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
koutput_to_field);
auto input_fops = create_bare_fops(kernel.inputs);
auto output_fops = create_bare_fops(kernel.outputs);
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(output_fops).vdim /
num_entities;
auto ir_weights = Reshape(this->op.integration_rule.GetWeights().Read(),
num_qp);
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
// Check which qf inputs are dependent on the dependent variable
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
bool no_kinput_is_dependent = true;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_to_field[i] == derivative_idx)
{
no_kinput_is_dependent = false;
kinput_is_dependent[i] = true;
// out << "function input " << i << " is dependent on "
// << op.fields[kinput_to_field[i]].field_label << "\n";
}
else
{
kinput_is_dependent[i] = false;
}
}
bool with_derivatives = true;
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
output_dtq_maps,
op.fields,
num_entities,
kernel.inputs,
num_qp,
input_size_on_qp,
da_size_on_qp,
derivative_idx);
Vector shmem_cache(shmem_info.total_size);
print_shared_memory_info(shmem_info);
func = [=](Vector &ye_mem) mutable
{
if (no_kinput_is_dependent)
{
return;
}
restriction<entity_t>(direction, direction_l, direction_e,
op.element_dof_ordering);
auto ye = Reshape(ye_mem.ReadWrite(), num_test_dof, test_vdim, num_entities);
auto wrapped_fields_e = wrap_fields(this->fields_e, shmem_info.field_sizes, num_entities);
auto wrapped_direction_e = Reshape(direction_e.Read(), shmem_info.direction_size, num_entities);
forall([=] MFEM_HOST_DEVICE (int e, double *shmem)
{
auto input_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
shmem_info.input_dtq_sizes,
input_dtq_maps);
auto output_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
shmem_info.output_dtq_sizes,
output_dtq_maps);
auto fields_shmem = load_field_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::FIELD],
shmem_info.field_sizes,
kinput_to_field,
wrapped_fields_e,
e);
auto direction_shmem = load_direction_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::DIRECTION],
shmem_info.direction_size,
wrapped_direction_e,
e);
// These methods don't copy, they simply create a `DeviceTensor` object
// that points to correct chunks of the shared memory pool.
auto input_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT],
shmem_info.input_sizes,
num_qp);
auto shadow_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::SHADOW],
shmem_info.input_sizes,
num_qp);
auto residual_shmem = load_residual_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT],
shmem_info.residual_size,
num_qp);
auto scratch_mem = load_scratch_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::TEMP],
shmem_info.temp_sizes);
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, input_fops, ir_weights, scratch_mem,
std::make_index_sequence<kernel.num_kinputs> {});
zero_all(shadow_shmem);
map_direction_to_quadrature_data_conditional<TensorProduct>(
shadow_shmem, direction_shmem, input_dtq_shmem, input_fops, ir_weights,
scratch_mem, kinput_is_dependent,
std::make_index_sequence<kernel.num_kinputs> {});
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);
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
auto r = Reshape(&residual_shmem(0, q), da_size_on_qp);
apply_kernel_fwddiff_enzyme(
r,
kernel.func,
kernel_args,
input_shmem,
kernel_shadow_args,
shadow_shmem,
q);
// printf(">>>>> WARNING: AD DISABLED\n");
}
}
}
MFEM_SYNC_THREAD;
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(output_fops),
output_dtq_shmem[hardcoded_output_idx],
scratch_mem);
}, num_entities, q1d, q1d, 1, shmem_info.total_size, shmem_cache.ReadWrite());
R->MultTranspose(ye_mem, derivative_action_l);
};
if constexpr (std::is_same_v<decltype(output_fop), One>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
double local_sum = r_local.Sum();
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
op.mesh.GetComm());
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
};
}
else
{
auto P = get_prolongation(op.fields[test_space_field_idx]);
prolongation_transpose = [P](const Vector &r_local, Vector &y)
{
P->MultTranspose(r_local, y);
};
}
}
@@ -0,0 +1,820 @@
#pragma once
#include <algorithm>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <utility>
#include <variant>
#include <vector>
#include <type_traits>
#include <mfem.hpp>
#include <type_traits>
#include "dfem_fieldoperator.hpp"
#include "dfem_parametricspace.hpp"
#include "general/tic_toc.hpp"
#include "tuple.hpp"
#include <linalg/tensor.hpp>
#include <enzyme/utils>
#include <enzyme/enzyme>
#include "dfem_util.hpp"
#include "dfem_interpolate.hpp"
#include "dfem_qfunction.hpp"
#include "dfem_qfunction_dual.hpp"
#include "dfem_integrate.hpp"
namespace mfem
{
using mult_func_t = std::function<void(Vector &)>;
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields = num_solutions + num_parameters,
size_t num_kernels = mfem::tuple_size<kernels_tuple>::value,
typename autodiff_t = AutoDiff::NativeDualNumber
>
class DifferentiableOperator : public Operator
{
public:
DifferentiableOperator(DifferentiableOperator&) = delete;
DifferentiableOperator(DifferentiableOperator&&) = delete;
class Action : public Operator
{
public:
template <typename kernel_t>
void create_action_callback(kernel_t kernel, mult_func_t &func);
template<std::size_t... idx>
void materialize_callbacks(kernels_tuple &ks,
std::array<mult_func_t, num_kernels>,
std::index_sequence<idx...> const&)
{
(create_action_callback(mfem::get<idx>(ks), funcs[idx]), ...);
}
Action(DifferentiableOperator &op, kernels_tuple &ks) : op(op)
{
materialize_callbacks(ks, funcs,
std::make_index_sequence<mfem::tuple_size<kernels_tuple>::value>());
}
void Mult(const Vector &x, Vector &y) const
{
prolongation(op.solutions, x, solutions_l);
residual_e = 0.0;
for (const auto &f : funcs)
{
f(residual_e);
}
prolongation_transpose(residual_l, y);
y.SetSubVector(op.ess_tdof_list, 0.0);
}
void SetParameters(std::vector<Vector *> p) const
{
MFEM_ASSERT(num_parameters == p.size(),
"number of parameters doesn't match descriptors");
for (int i = 0; i < num_parameters; i++)
{
p[i]->Read();
parameters_l[i] = *p[i];
// parameters_l[i].MakeRef(p[i], 0, p[i]->Size());
}
}
protected:
DifferentiableOperator &op;
std::array<mult_func_t, num_kernels> funcs;
std::function<void(Vector &, Vector &)> prolongation_transpose;
mutable std::array<Vector, num_solutions> solutions_l;
mutable std::array<Vector, num_parameters> parameters_l;
mutable Vector residual_l;
mutable std::array<Vector, num_fields> fields_e;
mutable Vector residual_e;
};
template <size_t derivative_idx>
class Derivative : public Operator
{
public:
template <typename kernel_t>
void create_callback(kernel_t kernel, mult_func_t &func);
template<std::size_t... idx>
void materialize_callbacks(kernels_tuple &ks,
std::array<mult_func_t, num_kernels>,
std::index_sequence<idx...> const&)
{
(create_callback(mfem::get<idx>(ks), funcs[idx]), ...);
}
Derivative(
DifferentiableOperator &op,
std::array<Vector *, num_solutions> &solutions,
std::array<Vector *, num_parameters> &parameters,
kernels_tuple &ks) : op(op), ks(ks)
{
for (int i = 0; i < num_solutions; i++)
{
solutions_l[i] = *solutions[i];
}
for (int i = 0; i < num_parameters; i++)
{
parameters_l[i] = *parameters[i];
}
// G
// if constexpr (std::is_same_v<OperatesOn, OperatesOnElement>)
// {
element_restriction(op.solutions, solutions_l, fields_e,
op.element_dof_ordering);
element_restriction(op.parameters, parameters_l, fields_e,
op.element_dof_ordering,
op.solutions.size());
// }
// else
// {
// MFEM_ABORT("restriction not implemented for OperatesOn");
// }
direction = op.fields[derivative_idx];
size_t derivative_action_l_size = 0;
for (auto &s : op.solutions)
{
derivative_action_l_size += GetVSize(s);
this->width += GetTrueVSize(s);
}
this->height = derivative_action_l_size;
derivative_action_l.SetSize(derivative_action_l_size);
materialize_callbacks(ks, funcs,
std::make_index_sequence<num_kernels>());
}
void Mult(const Vector &x, Vector &y) const override
{
current_direction_t = x;
current_direction_t.SetSubVector(op.ess_tdof_list, 0.0);
prolongation(direction, current_direction_t, direction_l);
derivative_action_e = 0.0;
for (const auto &f : funcs)
{
f(derivative_action_e);
}
prolongation_transpose(derivative_action_l, y);
y.SetSubVector(op.ess_tdof_list, 0.0);
}
template <typename kernel_t>
void assemble_vector_impl(kernel_t kernel, Vector &v);
template<std::size_t... idx>
void assemble_vector(
kernels_tuple &ks,
Vector &v,
std::index_sequence<idx...> const&)
{
(assemble_vector_impl(mfem::get<idx>(ks), v), ...);
}
void Assemble(Vector &v)
{
assemble_vector(ks, v, std::make_index_sequence<num_kernels>());
}
template <typename kernel_t>
void assemble_hypreparmatrix_impl(kernel_t kernel, HypreParMatrix &A);
template<std::size_t... idx>
void assemble_hypreparmatrix(
kernels_tuple &ks,
HypreParMatrix &A,
std::index_sequence<idx...> const&)
{
(assemble_hypreparmatrix_impl(mfem::get<idx>(ks), A), ...);
}
void Assemble(HypreParMatrix &A)
{
assemble_hypreparmatrix(ks, A, std::make_index_sequence<num_kernels>());
}
void AssembleDiagonal(Vector &d) const override {}
protected:
DifferentiableOperator &op;
kernels_tuple &ks;
std::array<mult_func_t, num_kernels> funcs;
std::function<void(Vector &, Vector &)> prolongation_transpose;
FieldDescriptor direction;
std::array<Vector, num_solutions> solutions_l;
std::array<Vector, num_parameters> parameters_l;
mutable Vector direction_l;
mutable Vector derivative_action_l;
mutable std::array<Vector, num_fields> fields_e;
mutable Vector direction_e;
mutable Vector derivative_action_e;
mutable Vector current_direction_t;
};
DifferentiableOperator(std::array<FieldDescriptor, num_solutions> s,
std::array<FieldDescriptor, num_parameters> p,
kernels_tuple ks,
ParMesh &m,
autodiff_t ad = AutoDiff::NativeDualNumber{}) :
kernels(ks),
mesh(m),
dim(mesh.Dimension()),
solutions(s),
parameters(p)
{
for (int i = 0; i < num_solutions; i++)
{
fields[i] = solutions[i];
}
for (int i = 0; i < num_parameters; i++)
{
fields[i + num_solutions] = parameters[i];
}
residual.reset(new Action(*this, kernels));
}
void SetParameters(std::vector<Vector *> p) const
{
residual->SetParameters(p);
}
void Mult(const Vector &x, Vector &y) const override
{
residual->Mult(x, y);
}
template <int derivative_idx>
std::shared_ptr<Derivative<derivative_idx>>
GetDerivativeWrt(std::array<Vector *, num_solutions> solutions,
std::array<Vector *, num_parameters> parameters)
{
return std::shared_ptr<Derivative<derivative_idx>>(
new Derivative<derivative_idx>(*this, solutions, parameters, kernels));
}
void SetEssentialTrueDofs(const Array<int> &l)
{
l.Copy(ess_tdof_list);
}
kernels_tuple kernels;
ParMesh &mesh;
const int dim;
std::array<FieldDescriptor, num_solutions> solutions;
std::array<FieldDescriptor, num_parameters> parameters;
// solutions and parameters
std::array<FieldDescriptor, num_fields> fields;
int residual_lsize = 0;
mutable std::array<Vector, num_solutions> current_state_l;
mutable Vector direction_l;
mutable Vector current_direction_t;
Array<int> ess_tdof_list;
static constexpr ElementDofOrdering element_dof_ordering =
ElementDofOrdering::LEXICOGRAPHIC;
static constexpr DofToQuad::Mode doftoquad_mode =
DofToQuad::Mode::TENSOR;
// static constexpr ElementDofOrdering element_dof_ordering =
// ElementDofOrdering::NATIVE;
// static constexpr DofToQuad::Mode doftoquad_mode =
// DofToQuad::Mode::FULL;
std::shared_ptr<Action> residual;
};
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields,
size_t num_kernels,
typename autodiff_t
>
template <
typename kernel_t
>
void DifferentiableOperator<kernels_tuple,
num_solutions,
num_parameters,
num_fields,
num_kernels,
autodiff_t>::Action::create_action_callback(
kernel_t kernel,
mult_func_t &func)
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
const int num_entities = GetNumEntities<entity_t>(op.mesh);
const int num_qp = kernel.integration_rule.GetNPoints();
// All solutions T-vector sizes make up the width of the operator, since
// they are explicitly provided in Mult() for example.
op.width = GetTrueVSize(op.fields[test_space_field_idx]);
op.residual_lsize = GetVSize(op.fields[test_space_field_idx]);
if constexpr (std::is_same_v<decltype(output_fop), One>)
{
op.height = 1;
}
else
{
op.height = op.residual_lsize;
}
residual_l.SetSize(op.residual_lsize);
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : op.fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(field, kernel.integration_rule,
doftoquad_mode));
}
const int q1d = (int)floor(pow(num_qp, 1.0/op.mesh.Dimension()) + 0.5);
residual_e.SetSize(R->Height());
const int residual_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(kernel.outputs),
op.fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
koutput_to_field);
auto input_fops = create_bare_fops(kernel.inputs);
auto output_fops = create_bare_fops(kernel.outputs);
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(output_fops).vdim /
num_entities;
auto ir_weights = Reshape(kernel.integration_rule.GetWeights().Read(), num_qp);
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
output_dtq_maps,
op.fields,
num_entities,
kernel.inputs,
num_qp,
input_size_on_qp,
residual_size_on_qp);
Vector shmem_cache(shmem_info.total_size);
// print_shared_memory_info(shmem_info);
func = [=](Vector &ye_mem) mutable
{
restriction<entity_t>(op.solutions, solutions_l, this->fields_e,
op.element_dof_ordering);
restriction<entity_t>(op.parameters, parameters_l, this->fields_e,
op.element_dof_ordering,
op.solutions.size());
auto ye = Reshape(ye_mem.ReadWrite(), test_vdim, num_test_dof, num_entities);
auto wrapped_fields_e = wrap_fields(this->fields_e, shmem_info.field_sizes, num_entities);
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
{
// printf("\ne: %d\n", e);
// tic();
auto input_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
shmem_info.input_dtq_sizes,
input_dtq_maps);
auto output_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
shmem_info.output_dtq_sizes,
output_dtq_maps);
auto fields_shmem = load_field_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::FIELD],
shmem_info.field_sizes,
kinput_to_field,
input_fops,
wrapped_fields_e,
e,
std::make_index_sequence<kernel.num_kinputs> {});
// These functions don't copy, they simply create a `DeviceTensor` object
// that points to correct chunks of the shared memory pool.
auto input_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT],
shmem_info.input_sizes,
num_qp);
auto residual_shmem = load_residual_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT],
shmem_info.residual_size,
num_qp);
auto scratch_mem = load_scratch_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::TEMP],
shmem_info.temp_sizes);
MFEM_SYNC_THREAD;
// printf("shmem load elapsed: %.1fus\n", toc() * 1e6);
// tic();
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, input_fops, ir_weights, scratch_mem,
std::make_index_sequence<kernel.num_kinputs> {});
// printf("interpolate elapsed: %.1fus\n", toc() * 1e6);
// tic();
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);
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
auto r = Reshape(&residual_shmem(0, q), residual_size_on_qp);
apply_kernel(r, kernel.func, kernel_args, input_shmem, q);
}
}
}
MFEM_SYNC_THREAD;
// printf("qf elapsed: %.1fus\n", toc() * 1e6);
// tic();
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(output_fops),
output_dtq_shmem[hardcoded_output_idx],
scratch_mem);
// printf("integrate elapsed: %.1fus\n", toc() * 1e6);
}, num_entities, q1d, q1d, q1d, shmem_info.total_size, shmem_cache.ReadWrite());
if constexpr (std::is_same_v<decltype(output_fop), None>)
{
residual_l = ye_mem;
}
else
{
R->MultTranspose(ye_mem, residual_l);
}
};
if constexpr (std::is_same_v<decltype(output_fop), None>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
y = r_local;
};
}
else if constexpr (std::is_same_v<decltype(output_fop), One>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
double local_sum = r_local.Sum();
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
op.mesh.GetComm());
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
};
}
else
{
auto P = get_prolongation(op.fields[test_space_field_idx]);
prolongation_transpose = [P](const Vector &r_local, Vector &y)
{
P->MultTranspose(r_local, y);
};
}
}
template <
typename kernels_tuple,
size_t num_solutions,
size_t num_parameters,
size_t num_fields,
size_t num_kernels,
typename autodiff_t
>
template <
size_t derivative_idx
>
template <
typename kernel_t
>
void DifferentiableOperator<kernels_tuple,
num_solutions,
num_parameters,
num_fields,
num_kernels,
autodiff_t>::Derivative<derivative_idx>::create_callback(kernel_t kernel,
mult_func_t &func)
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
const int num_entities = GetNumEntities<entity_t>(op.mesh);
const int num_qp = kernel.integration_rule.GetNPoints();
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : op.fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(field, kernel.integration_rule,
doftoquad_mode));
}
const int q1d = dtq[0]->nqpt;
derivative_action_e.SetSize(R->Height());
const int da_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(kernel.outputs),
op.fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
koutput_to_field);
auto input_fops = create_bare_fops(kernel.inputs);
auto output_fops = create_bare_fops(kernel.outputs);
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(output_fops).vdim /
num_entities;
auto ir_weights = Reshape(kernel.integration_rule.GetWeights().Read(), num_qp);
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
// Check which qf inputs are dependent on the dependent variable
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
bool no_kinput_is_dependent = true;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_to_field[i] == derivative_idx)
{
no_kinput_is_dependent = false;
kinput_is_dependent[i] = true;
// out << "function input " << i << " is dependent on "
// << op.fields[kinput_to_field[i]].field_label << "\n";
}
else
{
kinput_is_dependent[i] = false;
}
}
bool with_derivatives = true;
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
output_dtq_maps,
op.fields,
num_entities,
kernel.inputs,
num_qp,
input_size_on_qp,
da_size_on_qp,
derivative_idx);
Vector shmem_cache(shmem_info.total_size);
// print_shared_memory_info(shmem_info);
func = [=](Vector &ye_mem) mutable
{
if (no_kinput_is_dependent)
{
return;
}
restriction<entity_t>(direction, direction_l, direction_e,
op.element_dof_ordering);
auto ye = Reshape(ye_mem.ReadWrite(), num_test_dof, test_vdim, num_entities);
auto wrapped_fields_e = wrap_fields(this->fields_e, shmem_info.field_sizes, num_entities);
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(), shmem_info.direction_size, num_entities);
forall([=] MFEM_HOST_DEVICE (int e, double *shmem)
{
auto input_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
shmem_info.input_dtq_sizes,
input_dtq_maps);
auto output_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
shmem_info.output_dtq_sizes,
output_dtq_maps);
auto fields_shmem = load_field_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::FIELD],
shmem_info.field_sizes,
kinput_to_field,
input_fops,
wrapped_fields_e,
e,
std::make_index_sequence<kernel.num_kinputs> {});
auto direction_shmem = load_direction_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::DIRECTION],
shmem_info.direction_size,
wrapped_direction_e,
e);
// These methods don't copy, they simply create a `DeviceTensor` object
// that points to correct chunks of the shared memory pool.
auto input_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT],
shmem_info.input_sizes,
num_qp);
auto shadow_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::SHADOW],
shmem_info.input_sizes,
num_qp);
auto residual_shmem = load_residual_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT],
shmem_info.residual_size,
num_qp);
auto scratch_mem = load_scratch_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::TEMP],
shmem_info.temp_sizes);
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, input_fops, ir_weights, scratch_mem,
std::make_index_sequence<kernel.num_kinputs> {});
zero_all(shadow_shmem);
map_direction_to_quadrature_data_conditional<TensorProduct>(
shadow_shmem, direction_shmem, input_dtq_shmem, input_fops, ir_weights,
scratch_mem, kinput_is_dependent,
std::make_index_sequence<kernel.num_kinputs> {});
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);
auto r = Reshape(&residual_shmem(0, q), da_size_on_qp);
auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
if constexpr (std::is_same_v<autodiff_t, AutoDiff::EnzymeForward>)
{
auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
apply_kernel_fwddiff_enzyme(
r,
kernel.func,
kernel_args,
kernel_shadow_args,
input_shmem,
shadow_shmem,
q);
}
else if constexpr (std::is_same_v<autodiff_t, AutoDiff::NativeDualNumber>)
{
apply_kernel_native_dual(
r,
kernel.func,
kernel_args,
input_shmem,
shadow_shmem,
q);
}
else
{
static_assert(always_false<autodiff_t>, "unknown autodiff type");
}
}
}
}
MFEM_SYNC_THREAD;
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(output_fops),
output_dtq_shmem[hardcoded_output_idx],
scratch_mem);
}, num_entities, q1d, q1d, 1, shmem_info.total_size, shmem_cache.ReadWrite());
R->MultTranspose(ye_mem, derivative_action_l);
};
if constexpr (std::is_same_v<decltype(output_fop), One>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
double local_sum = r_local.Sum();
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
op.mesh.GetComm());
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
};
}
else
{
auto P = get_prolongation(op.fields[test_space_field_idx]);
prolongation_transpose = [P](const Vector &r_local, Vector &y)
{
P->MultTranspose(r_local, y);
};
}
}
} // namespace mfem
+79
View File
@@ -0,0 +1,79 @@
#include "dfem_util.hpp"
namespace mfem
{
template <typename func_t, typename input_t, typename output_t, typename dependency_map_t>
struct ElementOperator;
template <typename func_t, typename... input_ts, typename... output_ts, typename dependency_map_t>
struct ElementOperator<func_t, mfem::tuple<input_ts...>, mfem::tuple<output_ts...>, dependency_map_t>
{
using entity_t = Entity::Element;
func_t qfunc;
mfem::tuple<input_ts...> inputs;
mfem::tuple<output_ts...> outputs;
dependency_map_t dependency_map;
using qf_param_ts = typename create_function_signature<
decltype(&func_t::operator())>::type::parameter_ts;
using qf_output_t = typename create_function_signature<
decltype(&func_t::operator())>::type::return_t;
static constexpr size_t num_inputs =
mfem::tuple_size<decltype(inputs)>::value;
static constexpr size_t num_outputs =
mfem::tuple_size<decltype(outputs)>::value;
ElementOperator(func_t qfunc,
mfem::tuple<input_ts...> inputs,
mfem::tuple<output_ts...> outputs)
: qfunc(qfunc), inputs(inputs), outputs(outputs),
dependency_map(make_dependency_map(inputs))
{
// Consistency checks
if constexpr (num_outputs > 1)
{
static_assert(always_false<func_t>,
"more than one output per kernel is not supported right now");
}
constexpr size_t num_qfinputs = mfem::tuple_size<qf_param_ts>::value;
static_assert(num_qfinputs == num_inputs,
"kernel function inputs and descriptor inputs have to match");
constexpr size_t num_qf_outputs = mfem::tuple_size<qf_output_t>::value;
static_assert(num_qf_outputs == num_qf_outputs,
"kernel function outputs and descriptor outputs have to match");
}
};
template <typename func_t, typename... input_ts, typename... output_ts>
ElementOperator(func_t, mfem::tuple<input_ts...>, mfem::tuple<output_ts...>)
-> ElementOperator<func_t, mfem::tuple<input_ts...>, mfem::tuple<output_ts...>,
decltype(make_dependency_map(std::declval<mfem::tuple<input_ts...>>()))>;
// template <typename func_t, typename input_t, typename output_t>
// struct BoundaryElementOperator : public
// ElementOperator<func_t, input_t, output_t>
// {
// public:
// using entity_t = Entity::BoundaryElement;
// BoundaryElementOperator(func_t func, input_t inputs, output_t outputs)
// : ElementOperator<func_t, input_t, output_t>(func, inputs, outputs) {}
// };
// template <typename func_t, typename input_t, typename output_t>
// struct FaceOperator : public
// ElementOperator<func_t, input_t, output_t>
// {
// public:
// using entity_t = Entity::Face;
// FaceOperator(func_t func, input_t inputs, output_t outputs)
// : ElementOperator<func_t, input_t, output_t>(func, inputs, outputs) {}
// };
} // namespace mfem
+227
View File
@@ -0,0 +1,227 @@
#pragma once
#include <string>
namespace mfem
{
template <int FIELD_ID = -1>
class FieldOperator
{
public:
constexpr FieldOperator(int size_on_qp = 0) :
size_on_qp(size_on_qp) {};
static constexpr int GetFieldId() { return FIELD_ID; }
int size_on_qp = -1;
int dim = -1;
int vdim = -1;
};
template <int FIELD_ID = -1>
class None : public FieldOperator<FIELD_ID>
{
public:
constexpr None() : FieldOperator<FIELD_ID>() {}
};
template< typename T >
struct is_none_fop
{
static const bool value = false;
};
template <int FIELD_ID>
struct is_none_fop<None<FIELD_ID>>
{
static const bool value = true;
};
template <typename T>
struct DisableAD
{
T& operator()() const { return fop; }
T fop;
};
class Weight : public FieldOperator<-1>
{
public:
constexpr Weight() : FieldOperator<-1>() {};
};
template< typename T >
struct is_weight_fop
{
static const bool value = false;
};
template <>
struct is_weight_fop<Weight>
{
static const bool value = true;
};
template <int FIELD_ID = -1>
class Value : public FieldOperator<FIELD_ID>
{
public:
constexpr Value() : FieldOperator<FIELD_ID>() {};
};
template< typename T >
struct is_value_fop
{
static const bool value = false;
};
template <int FIELD_ID>
struct is_value_fop<Value<FIELD_ID>>
{
static const bool value = true;
};
template <typename T>
struct is_value_fop<DisableAD<T>>
{
static const bool value = is_value_fop<T>::value;
};
template <int FIELD_ID = -1>
class Gradient : public FieldOperator<FIELD_ID>
{
public:
constexpr Gradient() : FieldOperator<FIELD_ID>() {};
};
template< typename T >
struct is_gradient_fop
{
static const bool value = false;
};
template <int FIELD_ID>
struct is_gradient_fop<Gradient<FIELD_ID>>
{
static const bool value = true;
};
// class FieldOperator
// {
// public:
// FieldOperator(std::string field_label = "", int size_on_qp = 0) :
// field_label(field_label),
// size_on_qp(size_on_qp) {};
// std::string field_label;
// int size_on_qp = -1;
// int dim = -1;
// int vdim = -1;
// };
// class None : public FieldOperator
// {
// public:
// None(std::string field_label) :
// FieldOperator(field_label) {}
// };
// class Weight : public FieldOperator
// {
// public:
// Weight() : FieldOperator("quadrature_weights") {};
// };
// class Value : public FieldOperator
// {
// public:
// Value(std::string field_label) : FieldOperator(field_label) {};
// };
// class Gradient : public FieldOperator
// {
// public:
// Gradient(std::string field_label) : FieldOperator(field_label) {};
// };
// class Curl : public FieldOperator
// {
// public:
// Curl(std::string field_label) : FieldOperator(field_label) {};
// };
// class Div : public FieldOperator
// {
// public:
// Div(std::string field_label) : FieldOperator(field_label) {};
// };
// class FaceValueLeft : public FieldOperator
// {
// public:
// FaceValueLeft(std::string field_label) : FieldOperator(field_label) {};
// };
// class FaceValueRight : public FieldOperator
// {
// public:
// FaceValueRight(std::string field_label) : FieldOperator(field_label) {};
// };
// class FaceNormal : public FieldOperator
// {
// public:
// FaceNormal(std::string field_label) : FieldOperator(field_label) {};
// };
// class One : public FieldOperator
// {
// public:
// One(std::string field_label) : FieldOperator(field_label) {};
// };
// namespace BareFieldOperator
// {
// struct Base
// {
// Base(FieldOperator &o)
// {
// size_on_qp = o.size_on_qp;
// dim = o.dim;
// vdim = o.vdim;
// };
// int size_on_qp = -1;
// int dim = -1;
// int vdim = -1;
// };
// struct None : Base
// {
// None(FieldOperator &o) : Base(o) {}
// };
// struct Weight : Base
// {
// Weight(FieldOperator &o) : Base(o) {}
// };
// struct Value : Base
// {
// Value(FieldOperator &o) : Base(o) {}
// };
// struct Gradient : Base
// {
// Gradient(FieldOperator &o) : Base(o) {}
// };
// }
} // namespace mfem
+290
View File
@@ -0,0 +1,290 @@
#pragma once
#include "dfem_util.hpp"
#include <type_traits>
namespace mfem
{
template <typename output_t>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields_impl(DeviceTensor<2, double> &y,
const DeviceTensor<3, double> &f,
const output_t &output,
const DofToQuadMap &dtq)
{
auto B = dtq.B;
auto G = dtq.G;
// assuming the quadrature point residual has to "play nice with
// the test function"
if constexpr (std::is_same_v<std::decay_t<output_t>, Value<>>)
{
const auto [num_qp, cdim, num_dof] = B.GetShape();
const int vdim = output.vdim > 0 ? output.vdim : cdim ;
for (int dof = 0; dof < num_dof; dof++)
{
for (int vd = 0; vd < vdim; vd++)
{
double acc = 0.0;
for (int qp = 0; qp < num_qp; qp++)
{
acc += B(qp, 0, dof) * f(vd, 0, qp);
}
y(dof, vd) += acc;
}
}
}
else if constexpr (
std::is_same_v<std::decay_t<output_t>, Gradient<>>)
{
const auto [num_qp, dim, num_dof] = G.GetShape();
const int vdim = output.vdim;
for (int dof = 0; dof < num_dof; dof++)
{
for (int vd = 0; vd < vdim; vd++)
{
double acc = 0.0;
for (int d = 0; d < dim; d++)
{
for (int qp = 0; qp < num_qp; qp++)
{
acc += G(qp, d, dof) * f(vd, d, qp);
}
}
y(dof, vd) += acc;
}
}
}
// else if constexpr (std::is_same_v<std::decay_t<output_t>, One>)
// {
// // This is the "integral over all quadrature points type" applying
// // B = 1 s.t. B^T * C \in R^1.
// const auto [a, b, num_qp] = B.GetShape();
// auto cc = Reshape(&c(0, 0, 0), num_qp);
// for (int i = 0; i < num_qp; i++)
// {
// y(0, 0) += cc(i);
// }
// }
else if constexpr (
std::is_same_v<std::decay_t<output_t>, None<>>)
{
const auto [vdim, dim, num_qp] = G.GetShape();
auto cc = Reshape(&f(0, 0, 0), num_qp * vdim);
auto yy = Reshape(&y(0, 0), num_qp * vdim);
for (int i = 0; i < num_qp * vdim; i++)
{
yy(i) = cc(i);
}
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor");
}
}
template <typename output_t>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields_tensor_impl(DeviceTensor<2, double> &y,
const DeviceTensor<3, double> &f,
const output_t &output,
const DofToQuadMap &dtq,
std::array<DeviceTensor<1>, 6> &scratch_mem)
{
auto B = dtq.B;
auto G = dtq.G;
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
const int vdim = output.vdim;
const int test_dim = output.size_on_qp / vdim;
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d, q1d);
auto yd = Reshape(&y(0, 0), d1d, d1d, d1d, vdim);
auto s0 = Reshape(&scratch_mem[0](0), q1d, q1d, d1d);
auto s1 = Reshape(&scratch_mem[1](0), q1d, d1d, d1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
double acc = 0.0;
for (int qx = 0; qx < q1d; qx++)
{
acc += fqp(vd, 0, qx, qy, qz) * B(qx, 0, dx);
}
s0(qz, qy, dx) = acc;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
double acc = 0.0;
for (int qy = 0; qy < q1d; qy++)
{
acc += s0(qz, qy, dx) * B(qy, 0, dy);
}
s1(qz, dy, dx) = acc;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
MFEM_FOREACH_THREAD(dz, z, d1d)
{
double acc = 0.0;
for (int qz = 0; qz < q1d; qz++)
{
acc += s1(qz, dy, dx) * B(qz, 0, dz);
}
yd(dx, dy, dz, vd) += acc;
}
}
}
MFEM_SYNC_THREAD;
}
}
else if constexpr (is_gradient_fop<std::decay_t<output_t>>::value)
{
const auto [q1d, unused, d1d] = G.GetShape();
const int vdim = output.vdim;
const int test_dim = output.size_on_qp / vdim;
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d, q1d, q1d);
auto yd = Reshape(&y(0, 0), d1d, d1d, d1d, vdim);
auto s0 = Reshape(&scratch_mem[0](0), q1d, q1d, d1d);
auto s1 = Reshape(&scratch_mem[1](0), q1d, q1d, d1d);
auto s2 = Reshape(&scratch_mem[2](0), q1d, q1d, d1d);
auto s3 = Reshape(&scratch_mem[3](0), q1d, d1d, d1d);
auto s4 = Reshape(&scratch_mem[4](0), q1d, d1d, d1d);
auto s5 = Reshape(&scratch_mem[5](0), q1d, d1d, d1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int qx = 0; qx < q1d; qx++)
{
uvw[0] += fqp(vd, 0, qx, qy, qz) * G(qx, 0, dx);
uvw[1] += fqp(vd, 1, qx, qy, qz) * B(qx, 0, dx);
uvw[2] += fqp(vd, 2, qx, qy, qz) * B(qx, 0, dx);
}
s0(qz, qy, dx) = uvw[0];
s1(qz, qy, dx) = uvw[1];
s2(qz, qy, dx) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int qy = 0; qy < q1d; qy++)
{
uvw[0] += s0(qz, qy, dx) * B(qy, 0, dy);
uvw[1] += s1(qz, qy, dx) * G(qy, 0, dy);
uvw[2] += s2(qz, qy, dx) * B(qy, 0, dy);
}
s3(qz, dy, dx) = uvw[0];
s4(qz, dy, dx) = uvw[1];
s5(qz, dy, dx) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int qz = 0; qz < q1d; qz++)
{
uvw[0] += s3(qz, dy, dx) * B(qz, 0, dz);
uvw[1] += s4(qz, dy, dx) * B(qz, 0, dz);
uvw[2] += s5(qz, dy, dx) * G(qz, 0, dz);
}
yd(dx, dy, dz, vd) += uvw[0] + uvw[1] + uvw[2];
}
}
}
MFEM_SYNC_THREAD;
}
}
else if constexpr (is_none_fop<std::decay_t<output_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
auto fqp = Reshape(&f(0, 0, 0), output.size_on_qp, q1d, q1d, q1d);
auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d, q1d, q1d);
for (int sq = 0; sq < output.size_on_qp; sq++)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
yqp(sq, qx, qy, qz) = fqp(sq, qx, qy, qz);
}
}
}
MFEM_SYNC_THREAD;
}
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor with sum factorization on tensor product elements");
}
}
template <typename T = NonTensorProduct, typename output_t>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields(DeviceTensor<2, double> &y,
const DeviceTensor<3, double> &f,
const output_t &output,
const DofToQuadMap &dtq,
std::array<DeviceTensor<1>, 6> &scratch_mem)
{
if constexpr (std::is_same_v<T, NonTensorProduct>)
{
map_quadrature_data_to_fields_impl(y, f, output, dtq);
}
else if constexpr (std::is_same_v<T, TensorProduct>)
{
map_quadrature_data_to_fields_tensor_impl(y, f, output, dtq, scratch_mem);
}
}
}
+403
View File
@@ -0,0 +1,403 @@
#pragma once
#include "dfem_util.hpp"
namespace mfem
{
template <typename field_operator_t>
MFEM_HOST_DEVICE inline
void map_field_to_quadrature_data_tensor_product(
DeviceTensor<2> &field_qp,
const DofToQuadMap &dtq,
const DeviceTensor<1> &field_e,
const field_operator_t &input,
const DeviceTensor<1, const double> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem)
{
auto B = dtq.B;
auto G = dtq.G;
if constexpr (is_value_fop<std::decay_t<field_operator_t>>::value)
{
auto [q1d, unused, d1d] = B.GetShape();
const int vdim = input.vdim;
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
auto fqp = Reshape(&field_qp[0], vdim, q1d, q1d, q1d);
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
auto s1 = Reshape(&scratch_mem[1](0), d1d, q1d, q1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
double acc = 0.0;
for (int dx = 0; dx < d1d; dx++)
{
acc += B(qx, 0, dx) * field(dx, dy, dz, vd);
}
s0(dz, dy, qx) = acc;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
double acc = 0.0;
for (int dy = 0; dy < d1d; dy++)
{
acc += s0(dz, dy, qx) * B(qy, 0, dy);
}
s1(dz, qy, qx) = acc;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
double acc = 0.0;
for (int dz = 0; dz < d1d; dz++)
{
acc += s1(dz, qy, qx) * B(qz, 0, dz);
}
fqp(vd, qx, qy, qz) = acc;
}
}
}
MFEM_SYNC_THREAD;
}
}
else if constexpr (
is_gradient_fop<std::decay_t<field_operator_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
const int vdim = input.vdim;
const int dim = input.dim;
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
auto fqp = Reshape(&field_qp[0], vdim, dim, q1d, q1d, q1d);
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
auto s1 = Reshape(&scratch_mem[1](0), d1d, d1d, q1d);
auto s2 = Reshape(&scratch_mem[2](0), d1d, q1d, q1d);
auto s3 = Reshape(&scratch_mem[3](0), d1d, q1d, q1d);
auto s4 = Reshape(&scratch_mem[4](0), d1d, q1d, q1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(dy, y, d1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t uv[2] = {0.0, 0.0};
for (int dx = 0; dx < d1d; dx++)
{
const real_t f = field(dx, dy, dz, vd);
uv[0] += f * B(qx, 0, dx);
uv[1] += f * G(qx, 0, dx);
}
s0(dz, dy, qx) = uv[0];
s1(dz, dy, qx) = uv[1];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz, z, d1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int dy = 0; dy < d1d; dy++)
{
const real_t s0i = s0(dz, dy, qx);
uvw[0] += s1(dz, dy, qx) * B(qy, 0, dy);
uvw[1] += s0i * G(qy, 0, dy);
uvw[2] += s0i * B(qy, 0, dy);
}
s2(dz, qy, qx) = uvw[0];
s3(dz, qy, qx) = uvw[1];
s4(dz, qy, qx) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t uvw[3] = {0.0, 0.0, 0.0};
for (int dz = 0; dz < d1d; dz++)
{
uvw[0] += s2(dz, qy, qx) * B(qz, 0, dz);
uvw[1] += s3(dz, qy, qx) * B(qz, 0, dz);
uvw[2] += s4(dz, qy, qx) * G(qz, 0, dz);
}
fqp(vd, 0, qx, qy, qz) = uvw[0];
fqp(vd, 1, qx, qy, qz) = uvw[1];
fqp(vd, 2, qx, qy, qz) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
}
}
// TODO: Create separate function for clarity
else if constexpr (
std::is_same_v<std::decay_t<field_operator_t>, Weight>)
{
const int num_qp = integration_weights.GetShape()[0];
// TODO: eeek
const int q1d = (int)floor(pow(num_qp, 1.0/input.dim) + 0.5);
auto w = Reshape(&integration_weights[0], q1d, q1d, q1d);
auto f = Reshape(&field_qp[0], q1d, q1d, q1d);
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
f(qx, qy, qz) = w(qx, qy, qz);
}
}
}
MFEM_SYNC_THREAD;
}
else if constexpr (is_none_fop<std::decay_t<field_operator_t>>::value)
{
const int q1d = B.GetShape()[0];
auto field = Reshape(&field_e[0], input.size_on_qp, q1d * q1d * q1d);
field_qp = field;
}
else
{
static_assert(always_false<std::decay_t<field_operator_t>>,
"can't map field to quadrature data");
}
}
template <typename field_operator_t>
MFEM_HOST_DEVICE
void map_field_to_quadrature_data(
DeviceTensor<2> field_qp,
const DofToQuadMap &dtq,
const DeviceTensor<1, const double> &field_e,
field_operator_t &input,
DeviceTensor<1, const double> integration_weights)
{
auto B = dtq.B;
auto G = dtq.G;
if constexpr (is_value_fop<field_operator_t>::value)
{
auto [num_qp, dim, num_dof] = B.GetShape();
const int vdim = input.vdim;
const auto field = Reshape(&field_e(0), num_dof, vdim);
for (int vd = 0; vd < vdim; vd++)
{
for (int qp = 0; qp < num_qp; qp++)
{
double acc = 0.0;
for (int dof = 0; dof < num_dof; dof++)
{
acc += B(qp, 0, dof) * field(dof, vd);
}
field_qp(vd, qp) = acc;
}
}
}
else if constexpr (is_gradient_fop<field_operator_t>::value)
{
const auto [num_qp, dim, num_dof] = G.GetShape();
const int vdim = input.vdim;
const auto field = Reshape(&field_e(0), num_dof, vdim);
auto f = Reshape(&field_qp[0], vdim, dim, num_qp);
for (int qp = 0; qp < num_qp; qp++)
{
for (int vd = 0; vd < vdim; vd++)
{
for (int d = 0; d < dim; d++)
{
double acc = 0.0;
for (int dof = 0; dof < num_dof; dof++)
{
acc += G(qp, d, dof) * field(dof, vd);
}
f(vd, d, qp) = acc;
}
}
}
}
// else if constexpr (std::is_same_v<field_operator_t, FaceNormal>)
// {
// auto normal = geometric_factors.normal;
// auto [num_qp, dim, num_entities] = normal.GetShape();
// auto f = Reshape(&field_qp[0], dim, num_qp);
// for (int qp = 0; qp < num_qp; qp++)
// {
// for (int d = 0; d < dim; d++)
// {
// f(d, qp) = normal(qp, d, entity_idx);
// }
// }
// }
// TODO: Create separate function for clarity
else if constexpr (std::is_same_v<field_operator_t, Weight>)
{
const int num_qp = integration_weights.GetShape()[0];
auto f = Reshape(&field_qp[0], num_qp);
for (int qp = 0; qp < num_qp; qp++)
{
f(qp) = integration_weights(qp);
}
}
else if constexpr (is_none_fop<field_operator_t>::value)
{
auto [num_qp, unused, num_dof] = B.GetShape();
const int size_on_qp = input.size_on_qp;
const auto field = Reshape(&field_e[0], size_on_qp * num_qp);
auto f = Reshape(&field_qp[0], size_on_qp * num_qp);
for (int i = 0; i < size_on_qp * num_qp; i++)
{
f(i) = field(i);
}
}
else
{
static_assert(always_false<field_operator_t>,
"can't map field to quadrature data");
}
}
template <typename T = NonTensorProduct, typename field_operator_ts, size_t num_inputs, size_t num_fields>
MFEM_HOST_DEVICE inline
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<int, num_inputs> &input_to_field,
const field_operator_ts &fops,
const DeviceTensor<1, const double> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem)
{
for_constexpr<num_inputs>([&](auto i)
{
if constexpr (std::is_same_v<T, TensorProduct>)
{
map_field_to_quadrature_data_tensor_product(
fields_qp[i],
dtqmaps[i],
fields_e[input_to_field[i]],
mfem::get<i>(fops),
integration_weights,
scratch_mem);
}
else
{
map_field_to_quadrature_data(
fields_qp[i],
dtqmaps[i],
fields_e[i],
mfem::get<i>(fops),
integration_weights);
}
});
}
template <typename T, typename field_operator_t>
MFEM_HOST_DEVICE
void map_field_to_quadrature_data_conditional(
DeviceTensor<2> &field_qp,
const DeviceTensor<1> &field_e,
const DofToQuadMap &dtqmap,
field_operator_t &fop,
const DeviceTensor<1, const double> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
const bool &condition)
{
if (condition)
{
if constexpr (std::is_same_v<T, TensorProduct>)
{
map_field_to_quadrature_data_tensor_product(field_qp, dtqmap,
field_e, fop,
integration_weights,
scratch_mem);
}
else
{
map_field_to_quadrature_data(field_qp, dtqmap, field_e, fop,
integration_weights);
}
}
}
template <typename T = NonTensorProduct, size_t num_fields, size_t num_kinputs, typename field_operator_ts, std::size_t... i>
MFEM_HOST_DEVICE
void map_fields_to_quadrature_data_conditional(
std::array<DeviceTensor<2>, num_kinputs> &fields_qp,
const std::array<DeviceTensor<1, const double>, num_fields> &fields_e,
const std::array<DofToQuadMap, num_kinputs> &dtqmaps,
field_operator_ts fops,
const DeviceTensor<1, const double> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
const std::array<bool, num_kinputs> &conditions,
std::index_sequence<i...>)
{
(map_field_to_quadrature_data_conditional<T>(fields_qp[i],
fields_e[i],
dtqmaps[i],
mfem::get<i>(fops),
integration_weights,
scratch_mem,
conditions[i]),
...);
}
template <typename T = NonTensorProduct, size_t num_inputs, typename field_operator_ts>
MFEM_HOST_DEVICE
void map_direction_to_quadrature_data_conditional(
std::array<DeviceTensor<2>, num_inputs> &directions_qp,
const DeviceTensor<1> &direction_e,
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
field_operator_ts fops,
const DeviceTensor<1, const double> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
const std::array<bool, num_inputs> &conditions)
{
for_constexpr<num_inputs>([&](auto i)
{
map_field_to_quadrature_data_conditional<T>(directions_qp[i],
direction_e,
dtqmaps[i],
mfem::get<i>(fops),
integration_weights,
scratch_mem,
conditions[i]);
});
}
}
+99
View File
@@ -0,0 +1,99 @@
#pragma once
#include <mfem.hpp>
namespace mfem
{
class ParametricSpace
{
public:
ParametricSpace(int spatial_dim, int local_size, int element_size,
int total_size) :
spatial_dim(spatial_dim),
local_size(local_size),
element_size(element_size),
total_size(total_size),
identity(total_size)
{
dtq.ndof = (int)floor(pow(element_size, 1.0/spatial_dim) + 0.5);
dtq.nqpt = dtq.ndof;
}
ParametricSpace(int local_size) :
local_size(local_size),
element_size(local_size),
total_size(local_size),
identity(local_size)
{
dtq.ndof = (int)floor(pow(element_size, 1.0/spatial_dim) + 0.5);
dtq.nqpt = dtq.ndof;
}
int Dimension() const
{
return spatial_dim;
}
int GetLocalSize() const
{
return local_size;
}
int GetElementSize() const
{
return element_size;
}
int GetTotalSize() const
{
return total_size;
}
const DofToQuad &GetDofToQuad() const
{
return dtq;
}
const Operator *GetProlongation() const
{
return &identity;
}
const Operator *GetRestriction() const
{
return &identity;
}
private:
int spatial_dim;
// Hint for the local dimension. E.g. the size on the quadrature point or vdim.
int local_size;
// Size of the data on an element
int element_size;
int total_size;
IdentityOperator identity;
DofToQuad dtq;
};
class ParametricFunction : public Vector
{
public:
ParametricFunction(ParametricSpace &space) :
Vector(space.GetTotalSize()),
space(space)
{}
ParametricSpace &space;
using Vector::operator=;
};
}
+253
View File
@@ -0,0 +1,253 @@
#pragma once
#include "dfem_util.hpp"
#ifdef MFEM_USE_ENZYME
#include <enzyme/utils>
#include <enzyme/enzyme>
#endif
namespace mfem
{
template <typename T0, typename T1>
MFEM_HOST_DEVICE
void process_kf_arg(const T0 &, T1 &)
{
static_assert(always_false<T0, T1>,
"process_kf_arg not implemented for arg type");
}
template <typename T>
MFEM_HOST_DEVICE
void process_kf_arg(
const DeviceTensor<1, T> &u,
T &arg)
{
arg = u(0);
}
template <typename T>
MFEM_HOST_DEVICE
void process_kf_arg(
const DeviceTensor<1, T> &u,
internal::tensor<T> &arg)
{
arg(0) = u(0);
}
template <typename T, int n>
MFEM_HOST_DEVICE
void process_kf_arg(
const DeviceTensor<1> &u,
internal::tensor<T, n> &arg)
{
for (int i = 0; i < n; i++)
{
arg(i) = u(i);
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE
void process_kf_arg(
const DeviceTensor<1> &u,
internal::tensor<T, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i) = u((i * m) + j);
}
}
}
template <typename arg_type>
MFEM_HOST_DEVICE
void process_kf_arg(const DeviceTensor<2> &u, arg_type &arg, int qp)
{
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
process_kf_arg(u_qp, arg);
}
template <size_t num_fields, typename kf_args, std::size_t... i>
MFEM_HOST_DEVICE
void process_kf_args(
const std::array<DeviceTensor<2>, num_fields> &u,
kf_args &args,
const int &qp,
std::index_sequence<i...>)
{
(process_kf_arg(u[i], mfem::get<i>(args), qp), ...);
}
template <typename T0, typename T1> inline
Vector process_kf_result(T0, T1)
{
static_assert(always_false<T0, T1>,
"process_kf_result not implemented for result type");
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_kf_result(
DeviceTensor<1, T> &r,
const double &x)
{
r(0) = x;
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_kf_result(
DeviceTensor<1, T> &r,
const internal::tensor<T> &x)
{
r(0) = x(0);
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_kf_result(
DeviceTensor<1, T> &r,
const internal::tensor<T, n> &x)
{
for (size_t i = 0; i < n; i++)
{
r(i) = x(i);
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_kf_result(
DeviceTensor<1, T> &r,
const internal::tensor<T, n, m> &x)
{
for (size_t i = 0; i < n; i++)
{
for (size_t j = 0; j < m; j++)
{
r(i + n * j) = x(i, j);
}
}
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
double &arg)
{
arg = u(0);
}
template <int n, int m>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
internal::tensor<double, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i) = u((i * m) + j);
}
}
}
template <typename kernel_func_t, typename kernel_args_ts, size_t num_args>
MFEM_HOST_DEVICE inline
void apply_kernel(
DeviceTensor<1, double> &f_qp,
const kernel_func_t &kf,
kernel_args_ts &args,
const std::array<DeviceTensor<2>, num_args> &u,
int qp)
{
process_kf_args(u, args, qp,
std::make_index_sequence<mfem::tuple_size<kernel_args_ts>::value> {});
process_kf_result(f_qp, mfem::get<0>(mfem::apply(kf, args)));
}
#ifdef MFEM_USE_ENZYME
// Version for active function arguments only
//
// This is an Enzyme regression and can be removed in later versions.
template <typename kernel_t, typename arg_ts, std::size_t... Is,
typename inactive_arg_ts>
MFEM_HOST_DEVICE inline
auto fwddiff_apply_enzyme_indexed(kernel_t kernel, arg_ts &&args,
arg_ts &&shadow_args,
std::index_sequence<Is...>,
inactive_arg_ts &&inactive_args,
std::index_sequence<>)
{
using kf_return_t = typename create_function_signature<
decltype(&kernel_t::operator())>::type::return_t;
return __enzyme_fwddiff<kf_return_t>(
+kernel, enzyme_dup, &mfem::get<Is>(args)..., enzyme_interleave,
&mfem::get<Is>(shadow_args)...);
}
// Interleave function arguments for enzyme
template <typename kernel_t, typename arg_ts, std::size_t... Is,
typename inactive_arg_ts, std::size_t... Js>
MFEM_HOST_DEVICE inline
auto fwddiff_apply_enzyme_indexed(kernel_t kernel, arg_ts &&args,
arg_ts &&shadow_args,
std::index_sequence<Is...>,
inactive_arg_ts &&inactive_args,
std::index_sequence<Js...>)
{
using kf_return_t = typename create_function_signature<
decltype(&kernel_t::operator())>::type::return_t;
return __enzyme_fwddiff<kf_return_t>(
+kernel, enzyme_dup, &std::get<Is>(args)..., enzyme_const,
&mfem::get<Js>(inactive_args)..., enzyme_interleave,
&mfem::get<Is>(shadow_args)...);
}
template <typename kernel_t, typename arg_ts, typename inactive_arg_ts>
MFEM_HOST_DEVICE inline
auto fwddiff_apply_enzyme(kernel_t kernel, arg_ts &&args,
arg_ts &&shadow_args,
inactive_arg_ts &&inactive_args)
{
auto arg_indices = std::make_index_sequence<
mfem::tuple_size<std::remove_reference_t<arg_ts>>::value> {};
auto inactive_arg_indices = std::make_index_sequence<
mfem::tuple_size<std::remove_reference_t<inactive_arg_ts>>::value> {};
return fwddiff_apply_enzyme_indexed(kernel, args, shadow_args, arg_indices,
inactive_args, inactive_arg_indices);
}
template <typename kf_t, typename kernel_arg_ts, size_t num_args>
MFEM_HOST_DEVICE inline
void apply_kernel_fwddiff_enzyme(
DeviceTensor<1, double> &f_qp,
const kf_t &kf,
kernel_arg_ts &args,
kernel_arg_ts &shadow_args,
const std::array<DeviceTensor<2>, num_args> &u,
const std::array<DeviceTensor<2>, num_args> &v,
int qp_idx)
{
process_kf_args(u, args, qp_idx,
std::make_index_sequence<mfem::tuple_size<kernel_arg_ts>::value> {});
process_kf_args(v, shadow_args, qp_idx,
std::make_index_sequence<mfem::tuple_size<kernel_arg_ts>::value> {});
process_kf_result(f_qp,
mfem::get<0>(fwddiff_apply_enzyme(kf, args, shadow_args, mfem::tuple<> {})));
}
#endif // MFEM_USE_ENZYME
} // namespace mfem
+187
View File
@@ -0,0 +1,187 @@
#pragma once
#include "dfem_util.hpp"
#include "dfem_qfunction.hpp"
namespace mfem
{
MFEM_HOST_DEVICE
template <typename T0, typename T1, typename T2>
void process_kf_arg(const T0 &, const T1 &, T2 &)
{
static_assert(always_false<T0, T1, T2>,
"process_kf_arg not implemented for arg type");
}
template <typename T>
MFEM_HOST_DEVICE
void process_kf_arg(
const DeviceTensor<1, T> &u,
const DeviceTensor<1, T> &v,
T &arg)
{
arg = u(0);
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
internal::tensor<internal::dual<T, T>, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i).value = u((i * m) + j);
}
}
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
internal::dual<T, T> &arg)
{
arg.value = u(0);
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
internal::dual<T, T> &arg)
{
arg.value = u(0);
arg.gradient = v(0);
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
internal::tensor<internal::dual<T, T>, n> &arg)
{
for (int i = 0; i < n; i++)
{
arg(i).value = u(i);
arg(i).gradient = v(i);
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<1> &u,
const DeviceTensor<1> &v,
internal::tensor<internal::dual<T, T>, n, m> &arg)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
arg(j, i).value = u((i * m) + j);
arg(j, i).gradient = v((i * m) + j);
}
}
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_kf_result(
DeviceTensor<1, T> &r,
const internal::tensor<internal::dual<T, T>, n> &x)
{
for (size_t i = 0; i < n; i++)
{
r(i) = x(i).value;
}
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_kf_result(
DeviceTensor<1, T> &r,
const internal::tensor<internal::dual<T, T>, n, m> &x)
{
for (size_t i = 0; i < n; i++)
{
for (size_t j = 0; j < m; j++)
{
r(i + n * j) = x(i, j).value;
}
}
}
template <typename arg_type>
MFEM_HOST_DEVICE inline
void process_kf_arg(
const DeviceTensor<2> &u,
const DeviceTensor<2> &v,
arg_type &arg,
const int &qp)
{
const auto u_qp = Reshape(&u(0, qp), u.GetShape()[0]);
const auto v_qp = Reshape(&v(0, qp), v.GetShape()[0]);
process_kf_arg(u_qp, v_qp, arg);
}
template <size_t num_args, typename kf_args, std::size_t... Is>
MFEM_HOST_DEVICE inline
void process_kf_args(
const std::array<DeviceTensor<2>, num_args> &u,
const std::array<DeviceTensor<2>, num_args> &v,
kf_args &args,
const int &qp,
std::index_sequence<Is...>)
{
(process_kf_arg(u[Is], v[Is], mfem::get<Is>(args), qp), ...);
}
template <typename T, int n, int m>
MFEM_HOST_DEVICE inline
void process_derivative_from_native_dual(
DeviceTensor<1, T> &r,
const internal::tensor<internal::dual<T, T>, n, m> &x)
{
for (size_t i = 0; i < n; i++)
{
for (size_t j = 0; j < m; j++)
{
r(i + n * j) = x(i, j).gradient;
}
}
}
template <typename T, int n>
MFEM_HOST_DEVICE inline
void process_derivative_from_native_dual(
DeviceTensor<1, T> &r,
const internal::tensor<internal::dual<T, T>, n> &x)
{
for (size_t i = 0; i < n; i++)
{
r(i) = x(i).gradient;
}
}
template <typename kf_t, typename kernel_arg_ts, size_t num_args>
MFEM_HOST_DEVICE inline
void apply_kernel_native_dual(
DeviceTensor<1, double> &f_qp,
const kf_t &kf,
kernel_arg_ts &args,
const std::array<DeviceTensor<2>, num_args> &u,
const std::array<DeviceTensor<2>, num_args> &v,
const int &qp_idx)
{
process_kf_args(u, v, args, qp_idx,
std::make_index_sequence<mfem::tuple_size<kernel_arg_ts>::value> {});
auto r = mfem::get<0>(mfem::apply(kf, args));
process_derivative_from_native_dual(f_qp, r);
}
} // namespace mfem
+530
View File
@@ -0,0 +1,530 @@
#pragma once
#include <mfem.hpp>
#include <utility>
#include "dfem_interpolate.hpp"
#include "dfem_integrate.hpp"
#include "dfem_qfunction.hpp"
#include "dfem_qfunction_dual.hpp"
#include "examples/dfem/dfem_util.hpp"
namespace mfem
{
class DerivativeOperator : public Operator
{
using derivative_action_t =
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
using restriction_callback_t =
std::function<void(std::vector<Vector> &,
const std::vector<Vector> &,
std::vector<Vector> &)>;
public:
DerivativeOperator(
const std::vector<derivative_action_t> &derivative_actions,
const FieldDescriptor &direction,
const std::vector<Vector *> &solutions_l,
const std::vector<Vector *> &parameters_l,
const std::vector<restriction_callback_t> &restriction_callbacks,
const std::function<void(Vector &, Vector &)> prolongation_transpose) :
derivative_actions(derivative_actions),
direction(direction),
restriction_callbacks(restriction_callbacks),
derivative_action_l(GetVSize(direction)),
prolongation_transpose(prolongation_transpose)
{
MFEM_ASSERT(derivative_actions.size() == restriction_callbacks.size(),
"internal error");
derivative_action_l = 0.0;
this->solutions_l.resize(solutions_l.size());
this->parameters_l.resize(parameters_l.size());
for (int i = 0; i < solutions_l.size(); i++)
{
this->solutions_l[i] = *solutions_l[i];
}
for (int i = 0; i < parameters_l.size(); i++)
{
this->parameters_l[i] = *parameters_l[i];
}
fields_e.resize(solutions_l.size() + parameters_l.size());
}
void Mult(const Vector &x, Vector &y) const override
{
direction_t = x;
direction_t.SetSubVector(ess_tdof_list, 0.0);
prolongation(direction, direction_t, direction_l);
for (int i = 0; i < derivative_actions.size(); i++)
{
restriction_callbacks[i](solutions_l, parameters_l, fields_e);
derivative_actions[i](fields_e, direction_l, derivative_action_l);
}
prolongation_transpose(derivative_action_l, y);
y.SetSubVector(ess_tdof_list, 0.0);
};
private:
std::vector<derivative_action_t> derivative_actions;
mutable std::vector<Vector> solutions_l;
std::vector<Vector> parameters_l;
FieldDescriptor direction;
mutable Vector direction_t;
mutable Vector direction_e;
mutable Vector direction_l;
mutable Vector derivative_action_e;
mutable Vector derivative_action_l;
mutable std::vector<Vector> fields_e;
Array<int> ess_tdof_list;
std::vector<restriction_callback_t> restriction_callbacks;
std::function<void(Vector &, Vector &)> prolongation_transpose;
};
class DifferentiableOperator : public Operator
{
using action_t =
std::function<void(std::vector<Vector> &, const std::vector<Vector> &, Vector &)>;
using derivative_action_t =
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
using restriction_callback_t =
std::function<void(std::vector<Vector> &,
const std::vector<Vector> &,
std::vector<Vector> &)>;
public:
DifferentiableOperator(
const std::vector<FieldDescriptor> &solutions,
const std::vector<FieldDescriptor> &parameters,
const ParMesh &mesh);
void Mult(const Vector &x, Vector &y) const override
{
MFEM_ASSERT(!action_callbacks.empty(), "no integrators have been set");
prolongation(solutions, x, solutions_l);
for (auto &action : action_callbacks)
{
action(solutions_l, parameters_l, residual_l);
}
prolongation_transpose(residual_l, y);
y.SetSubVector(ess_tdof_list, 0.0);
}
template <
typename func_t,
typename... input_ts,
typename... output_ts,
typename derivative_indices_t>
void AddDomainIntegrator(
func_t qfunc,
mfem::tuple<input_ts...> inputs,
mfem::tuple<output_ts...> outputs,
const IntegrationRule &integration_rule,
const derivative_indices_t derivative_indices = {});
void SetParameters(std::vector<Vector *> p) const;
std::shared_ptr<DerivativeOperator> GetDerivative(
size_t derivative_idx,
std::vector<Vector *> solutions_l,
std::vector<Vector *> parameters_l)
{
MFEM_ASSERT(derivative_action_callbacks.find(derivative_idx) !=
derivative_action_callbacks.end(),
"no derivative action has been found for index " << derivative_idx);
return std::make_shared<DerivativeOperator>(
derivative_action_callbacks[derivative_idx],
fields[derivative_idx],
solutions_l,
parameters_l,
restriction_callbacks,
prolongation_transpose);
}
private:
const ParMesh &mesh;
std::vector<action_t> action_callbacks;
std::map<size_t, std::vector<derivative_action_t>> derivative_action_callbacks;
std::vector<FieldDescriptor> solutions;
std::vector<FieldDescriptor> parameters;
// solutions and parameters
std::vector<FieldDescriptor> fields;
Array<int> ess_tdof_list;
mutable std::vector<Vector> solutions_l;
mutable std::vector<Vector> parameters_l;
mutable Vector residual_l;
mutable std::vector<Vector> fields_e;
mutable Vector residual_e;
std::function<void(Vector &, Vector &)> prolongation_transpose;
std::vector<restriction_callback_t> restriction_callbacks;
};
void DifferentiableOperator::SetParameters(std::vector<Vector *> p) const
{
MFEM_ASSERT(parameters.size() == p.size(),
"number of parameters doesn't match descriptors");
for (int i = 0; i < parameters.size(); i++)
{
p[i]->Read();
parameters_l[i] = *p[i];
}
}
DifferentiableOperator::DifferentiableOperator(
const std::vector<FieldDescriptor> &solutions,
const std::vector<FieldDescriptor> &parameters,
const ParMesh &mesh) :
mesh(mesh),
solutions(solutions),
parameters(parameters)
{
fields.resize(solutions.size() + parameters.size());
fields_e.resize(fields.size());
solutions_l.resize(solutions.size());
parameters_l.resize(parameters.size());
for (int i = 0; i < solutions.size(); i++)
{
fields[i] = solutions[i];
}
for (int i = 0; i < parameters.size(); i++)
{
fields[i + solutions.size()] = parameters[i];
}
}
template <
typename func_t,
typename... input_ts,
typename... output_ts,
typename derivative_indices_t = std::make_index_sequence<0>>
void DifferentiableOperator::AddDomainIntegrator(
func_t qfunc,
mfem::tuple<input_ts...> inputs,
mfem::tuple<output_ts...> outputs,
const IntegrationRule &integration_rule,
const derivative_indices_t derivative_indices)
{
using entity_t = Entity::Element;
static constexpr size_t num_inputs =
mfem::tuple_size<decltype(inputs)>::value;
static constexpr size_t num_outputs =
mfem::tuple_size<decltype(outputs)>::value;
using qf_param_ts = typename create_function_signature<
decltype(&func_t::operator())>::type::parameter_ts;
using qf_output_t = typename create_function_signature<
decltype(&func_t::operator())>::type::return_t;
// Consistency checks
if constexpr (num_outputs > 1)
{
static_assert(always_false<func_t>,
"more than one output per kernel is not supported right now");
}
constexpr size_t num_qfinputs = mfem::tuple_size<qf_param_ts>::value;
static_assert(num_qfinputs == num_inputs,
"kernel function inputs and descriptor inputs have to match");
constexpr size_t num_qf_outputs = mfem::tuple_size<qf_output_t>::value;
static_assert(num_qf_outputs == num_qf_outputs,
"kernel function outputs and descriptor outputs have to match");
constexpr auto field_tuple = std::tuple_cat(std::tuple<input_ts...> {},
std::tuple<output_ts...> {});
constexpr auto filtered_field_tuple = filter_fields(field_tuple);
constexpr size_t num_fields = count_unique_field_ids(filtered_field_tuple);
constexpr auto dependency_map = make_dependency_map(mfem::tuple<input_ts...> {});
// Create the action callback
auto input_to_field = create_descriptors_to_fields_map<entity_t>(
fields,
inputs,
std::make_index_sequence<num_inputs> {});
auto output_to_field = create_descriptors_to_fields_map<entity_t>(
fields,
outputs,
std::make_index_sequence<num_outputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = output_to_field[hardcoded_output_idx];
ElementDofOrdering element_dof_ordering = ElementDofOrdering::LEXICOGRAPHIC;
DofToQuad::Mode doftoquad_mode = DofToQuad::Mode::TENSOR;
const Operator *R = get_restriction<entity_t>(fields[test_space_field_idx],
element_dof_ordering);
// The explicit captures are necessary to avoid dependency on
// the specific instance of this class (this pointer).
auto restriction_callback =
[=, solutions = this->solutions, parameters = this->parameters]
(std::vector<Vector> &solutions_l,
const std::vector<Vector> &parameters_l,
std::vector<Vector> &fields_e)
{
restriction<entity_t>(solutions, solutions_l, fields_e,
element_dof_ordering);
restriction<entity_t>(parameters, parameters_l, fields_e,
element_dof_ordering,
solutions.size());
};
restriction_callbacks.push_back(restriction_callback);
auto output_fop = mfem::get<hardcoded_output_idx>(outputs);
if constexpr (is_none_fop<decltype(output_fop)>::value)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
y = r_local;
};
}
// else if constexpr (std::is_same_v<decltype(output_fop), One>)
// {
// prolongation_transpose = [&](Vector &r_local, Vector &y)
// {
// double local_sum = r_local.Sum();
// MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
// op.mesh.GetComm());
// MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
// };
// }
else
{
auto P = get_prolongation(fields[test_space_field_idx]);
prolongation_transpose = [P](const Vector &r_local, Vector &y)
{
P->MultTranspose(r_local, y);
};
}
const int num_elements = GetNumEntities<Entity::Element>(mesh);
const int num_entities = GetNumEntities<entity_t>(mesh);
const int num_qp = integration_rule.GetNPoints();
size_t residual_lsize = GetVSize(fields[test_space_field_idx]);
// if constexpr (std::is_same_v<decltype(output_fop), One>)
// {
// this->width = 1;
// }
// else
{
width = residual_lsize;
}
residual_l.SetSize(residual_lsize);
std::vector<const DofToQuad*> dtq;
for (const auto &field : fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(
field,
integration_rule,
doftoquad_mode));
}
const int q1d = (int)floor(pow(num_qp, 1.0/mesh.Dimension()) + 0.5);
residual_e.SetSize(R->Height());
const int residual_size_on_qp =
GetSizeOnQP<entity_t>(mfem::get<hardcoded_output_idx>(outputs),
fields[test_space_field_idx]);
auto input_dtq_maps =
create_dtq_maps<entity_t>(inputs, dtq, input_to_field);
auto output_dtq_maps =
create_dtq_maps<entity_t>(outputs, dtq, output_to_field);
const int test_vdim = mfem::get<hardcoded_output_idx>(outputs).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(inputs).size_on_qp /
mfem::get<hardcoded_output_idx>(outputs).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(outputs).vdim /
num_entities;
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
auto input_size_on_qp =
get_input_size_on_qp(inputs, std::make_index_sequence<num_inputs> {});
auto action_shmem_info =
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
input_size_on_qp, residual_size_on_qp);
Vector shmem_cache(action_shmem_info.total_size);
// print_shared_memory_info(action_shmem_info);
action_callbacks.push_back(
[=](std::vector<Vector> &solutions_l,
const std::vector<Vector> &parameters_l,
Vector &residual_l) mutable
{
restriction_callback(solutions_l, parameters_l, fields_e);
residual_e = 0.0;
auto ye = Reshape(residual_e.ReadWrite(), test_vdim, num_test_dof, num_entities);
auto wrapped_fields_e = wrap_fields(fields_e,
action_shmem_info.field_sizes,
num_entities);
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
{
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, input_shmem,
residual_shmem, scratch_shmem] =
unpack_shmem(shmem, action_shmem_info, input_dtq_maps, output_dtq_maps,
wrapped_fields_e, num_qp, e);
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, input_to_field, inputs, ir_weights,
scratch_shmem);
call_qfunction<TensorProduct, qf_param_ts>(
qfunc, input_shmem, residual_shmem,
residual_size_on_qp, num_qp, q1d);
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(outputs),
output_dtq_shmem[hardcoded_output_idx],
scratch_shmem);
}, num_entities, q1d, q1d, q1d, action_shmem_info.total_size, shmem_cache.ReadWrite());
if constexpr (is_none_fop<decltype(output_fop)>::value)
{
residual_l = residual_e;
}
else
{
R->MultTranspose(residual_e, residual_l);
}
});
for_constexpr([&](auto derivative_idx)
{
// bool is_dependent = false;
// for_constexpr<num_inputs>([&](auto input_idx)
// {
// constexpr auto input_is_dependent_on_field_idx =
// std::get<derivative_idx>(std::get<input_idx>(dependency_map));
// if constexpr (input_is_dependent_on_field_idx == 1)
// {
// is_dependent = true;
// }
// });
// if (!is_dependent)
// {
// derivative_action_callbacks[derivative_idx].push_back(
// [=](const Vector &direction_l, Vector &y) mutable
// {
// y += 0.0;
// });
// return;
// }
auto direction = fields[derivative_idx];
size_t derivative_action_l_size = GetVSize(direction);
const int da_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(outputs),
fields[test_space_field_idx]);
auto shmem_info =
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
input_size_on_qp, residual_size_on_qp, derivative_idx);
Vector shmem_cache(shmem_info.total_size);
// print_shared_memory_info(shmem_info);
Vector direction_e;
Vector derivative_action_e(R->Height());
derivative_action_e = 0.0;
auto input_is_dependent = get_array_from_tuple(std::get<derivative_idx>
(dependency_map));
derivative_action_callbacks[derivative_idx].push_back(
[=](std::vector<Vector> &fields_e, const Vector &direction_l,
Vector &derivative_action_l) mutable
{
restriction<entity_t>(direction, direction_l, direction_e, element_dof_ordering);
auto ye = Reshape(derivative_action_e.ReadWrite(), num_test_dof, test_vdim, num_entities);
auto wrapped_fields_e = wrap_fields(fields_e, shmem_info.field_sizes, num_entities);
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(), shmem_info.direction_size, num_entities);
forall([=] MFEM_HOST_DEVICE (int e, double *shmem)
{
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);
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, input_to_field, inputs, ir_weights,
scratch_shmem);
zero_all(shadow_shmem);
map_direction_to_quadrature_data_conditional<TensorProduct>(
shadow_shmem, direction_shmem, input_dtq_shmem, inputs, ir_weights,
scratch_shmem, input_is_dependent);
call_qfunction_derivative_action<TensorProduct, qf_param_ts>(
qfunc, input_shmem, shadow_shmem, residual_shmem,
da_size_on_qp, num_qp, q1d);
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(outputs),
output_dtq_shmem[hardcoded_output_idx],
scratch_shmem);
}, num_entities, q1d, q1d, q1d, shmem_info.total_size, shmem_cache.ReadWrite());
R->MultTranspose(derivative_action_e, derivative_action_l);
});
}, derivative_indices);
}
} // namespace mfem
// #include "dfem_refactor_action.hpp"
// #include "dfem_refactor_derivatives.hpp"
+232
View File
@@ -0,0 +1,232 @@
#pragma once
#include "dfem_refactor.hpp"
namespace mfem
{
template <typename element_operator_t, size_t num_fields>
void DifferentiableOperator::instantiate_action(
element_operator_t element_operator, action_t &action)
{
using entity_t = typename element_operator_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(
fields,
element_operator.inputs,
std::make_index_sequence<element_operator.num_inputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(
fields,
element_operator.outputs,
std::make_index_sequence<element_operator.num_outputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(element_operator.outputs);
const int num_elements = GetNumEntities<Entity::Element>(mesh);
const int num_entities = GetNumEntities<entity_t>(mesh);
const int num_qp = integration_rule.GetNPoints();
this->width = GetTrueVSize(fields[test_space_field_idx]);
size_t residual_lsize = GetVSize(fields[test_space_field_idx]);
// if constexpr (std::is_same_v<decltype(output_fop), One>)
// {
// this->width = 1;
// }
// else
{
this->width = residual_lsize;
}
residual_l.SetSize(residual_lsize);
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(
field,
integration_rule,
doftoquad_mode));
}
const int q1d = (int)floor(pow(num_qp, 1.0/mesh.Dimension()) + 0.5);
residual_e.SetSize(R->Height());
const int residual_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(element_operator.outputs),
fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(element_operator.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(element_operator.outputs, dtq,
koutput_to_field);
// auto input_fops = create_bare_fops(element_operator.inputs);
// auto output_fops = create_bare_fops(element_operator.outputs);
const int test_vdim = mfem::get<hardcoded_output_idx>
(element_operator.outputs).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(element_operator.inputs).size_on_qp /
mfem::get<hardcoded_output_idx>(element_operator.outputs).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(element_operator.outputs).vdim /
num_entities;
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
auto input_size_on_qp = get_input_size_on_qp(
element_operator.inputs,
std::make_index_sequence<element_operator.num_inputs> {});
auto shmem_info =
get_shmem_info<entity_t, num_fields, element_operator.num_inputs, element_operator.num_outputs>
(input_dtq_maps,
output_dtq_maps,
fields,
num_entities,
element_operator.inputs,
num_qp,
input_size_on_qp,
residual_size_on_qp);
Vector shmem_cache(shmem_info.total_size);
print_shared_memory_info(shmem_info);
action = [=](const Vector &x, Vector &y) mutable
{
prolongation(solutions, x, solutions_l);
restriction<entity_t>(solutions, solutions_l, this->fields_e,
element_dof_ordering);
restriction<entity_t>(parameters, parameters_l, this->fields_e,
element_dof_ordering,
solutions.size());
residual_e = 0.0;
auto ye = Reshape(residual_e.ReadWrite(), test_vdim, num_test_dof,
num_entities);
auto wrapped_fields_e = wrap_fields(this->fields_e,
shmem_info.field_sizes,
num_entities);
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
{
// printf("\ne: %d\n", e);
// tic();
auto input_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT_DTQ],
shmem_info.input_dtq_sizes,
input_dtq_maps);
auto output_dtq_shmem = load_dtq_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT_DTQ],
shmem_info.output_dtq_sizes,
output_dtq_maps);
auto fields_shmem = load_field_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::FIELD],
shmem_info.field_sizes,
kinput_to_field,
element_operator.inputs,
wrapped_fields_e,
e,
std::make_index_sequence<element_operator.num_inputs> {});
// These functions don't copy, they simply create a `DeviceTensor` object
// that points to correct chunks of the shared memory pool.
auto input_shmem = load_input_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::INPUT],
shmem_info.input_sizes,
num_qp);
auto residual_shmem = load_residual_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::OUTPUT],
shmem_info.residual_size,
num_qp);
auto scratch_mem = load_scratch_mem(
shmem,
shmem_info.offsets[SharedMemory::Index::TEMP],
shmem_info.temp_sizes);
MFEM_SYNC_THREAD;
// // printf("shmem load elapsed: %.1fus\n", toc() * 1e6);
// // tic();
map_fields_to_quadrature_data<TensorProduct>(
input_shmem, fields_shmem, input_dtq_shmem, element_operator.inputs, ir_weights,
scratch_mem,
std::make_index_sequence<element_operator.num_inputs> {});
// printf("interpolate elapsed: %.1fus\n", toc() * 1e6);
// // tic();
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);
auto qf_args = decay_tuple<typename element_operator_t::qf_param_ts> {};
auto r = Reshape(&residual_shmem(0, q), residual_size_on_qp);
apply_kernel(r, element_operator.qfunc, qf_args, input_shmem, q);
}
}
}
MFEM_SYNC_THREAD;
// // printf("qf elapsed: %.1fus\n", toc() * 1e6);
// // tic();
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields<TensorProduct>(y, fhat,
mfem::get<0>(element_operator.outputs),
output_dtq_shmem[hardcoded_output_idx],
scratch_mem);
// printf("integrate elapsed: %.1fus\n", toc() * 1e6);
}, num_entities, q1d, q1d, q1d, shmem_info.total_size, shmem_cache.ReadWrite());
if constexpr (std::is_same_v<decltype(output_fop), None<>>)
{
residual_l = y;
}
else
{
R->MultTranspose(residual_e, residual_l);
}
if constexpr (std::is_same_v<decltype(output_fop), None<>>)
{
y = residual_l;
}
// else if constexpr (std::is_same_v<decltype(output_fop), One>)
// {
// double local_sum = residual_l.Sum();
// MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM, mesh.GetComm());
// MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
// }
else
{
get_prolongation(fields[test_space_field_idx])->MultTranspose(residual_l, y);
}
};
}
}
+132
View File
@@ -0,0 +1,132 @@
#pragma once
#include "dfem_refactor.hpp"
template<typename T, T... Ints>
void print_sequence(std::integer_sequence<T, Ints...>)
{
((std::cout << Ints << " "), ...);
std::cout << std::endl;
}
namespace mfem
{
template <
typename element_operator_t,
size_t num_solutions,
size_t num_parameters,
size_t derivative_idx>
DerivativeOperator::DerivativeOperator(
element_operator_t element_operator,
const std::array<FieldDescriptor, num_solutions> &solutions,
const std::array<FieldDescriptor, num_parameters> &parameters,
const std::vector<FieldDescriptor> &fields,
ParMesh &mesh,
const IntegrationRule &integration_rule,
const ElementDofOrdering &element_dof_ordering,
const DofToQuad::Mode &doftoquad_mode,
std::integral_constant<size_t, derivative_idx>)
{
direction = fields[derivative_idx];
size_t derivative_action_l_size = 0;
for (auto &s : solutions)
{
derivative_action_l_size += GetVSize(s);
this->width += GetTrueVSize(s);
}
this->height = derivative_action_l_size;
derivative_action_l.SetSize(derivative_action_l_size);
constexpr size_t num_fields = num_solutions + num_parameters;
using entity_t = typename element_operator_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(
fields,
element_operator.inputs,
std::make_index_sequence<element_operator.num_inputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(
fields,
element_operator.outputs,
std::make_index_sequence<element_operator.num_outputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(element_operator.outputs);
const int num_elements = GetNumEntities<Entity::Element>(mesh);
const int num_entities = GetNumEntities<entity_t>(mesh);
const int num_qp = integration_rule.GetNPoints();
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(
field,
integration_rule,
doftoquad_mode));
}
const int q1d = (int)floor(pow(num_qp, 1.0/mesh.Dimension()) + 0.5);
derivative_action_e.SetSize(R->Height());
const int da_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(element_operator.outputs),
fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(element_operator.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(element_operator.outputs, dtq,
koutput_to_field);
const int test_vdim = mfem::get<hardcoded_output_idx>
(element_operator.outputs).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(element_operator.inputs).size_on_qp /
mfem::get<hardcoded_output_idx>(element_operator.outputs).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(element_operator.outputs).vdim /
num_entities;
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
auto input_size_on_qp = get_input_size_on_qp(
element_operator.inputs,
std::make_index_sequence<element_operator.num_inputs> {});
auto input_is_dependent = std::get<derivative_idx>
(element_operator.dependency_map);
constexpr bool with_derivatives = true;
auto shmem_info =
get_shmem_info<entity_t, num_fields, element_operator.num_inputs, element_operator.num_outputs>
(input_dtq_maps,
output_dtq_maps,
fields,
num_entities,
element_operator.inputs,
num_qp,
input_size_on_qp,
da_size_on_qp,
derivative_idx);
Vector shmem_cache(shmem_info.total_size);
print_shared_memory_info(shmem_info);
action_callback = [=](const Vector &x, Vector &y) mutable
{
restriction<entity_t>(direction, direction_l, direction_e,
element_dof_ordering);
};
}
} // namespace mfem
+116
View File
@@ -0,0 +1,116 @@
#pragma once
#include <mfem.hpp>
class SharedMemoryManager
{
private:
struct MemoryBlock
{
char* ptr;
int size;
bool used;
};
MFEM_HOST_DEVICE static const int MAX_BLOCKS = 16;
MFEM_HOST_DEVICE static MemoryBlock blocks[MAX_BLOCKS];
MFEM_HOST_DEVICE static int num_blocks;
MFEM_HOST_DEVICE static char* base_ptr;
public:
MFEM_HOST_DEVICE static void init(void* shmem, int total_size)
{
base_ptr = static_cast<char*>(shmem);
num_blocks = 1;
blocks[0] = {base_ptr, total_size, false};
}
template<typename T>
MFEM_HOST_DEVICE static T* reserve(int n)
{
int size_bytes = n * sizeof(T);
for (int i = 0; i < num_blocks; ++i)
{
if (!blocks[i].used && blocks[i].size >= size_bytes)
{
blocks[i].used = true;
if (blocks[i].size > size_bytes)
{
// Split block
if (num_blocks < MAX_BLOCKS)
{
blocks[num_blocks] = {blocks[i].ptr + size_bytes, blocks[i].size - size_bytes, false};
++num_blocks;
blocks[i].size = size_bytes;
}
}
return reinterpret_cast<T*>(blocks[i].ptr);
}
}
return nullptr; // Allocation failed
}
MFEM_HOST_DEVICE static void release(void* ptr)
{
for (int i = 0; i < num_blocks; ++i)
{
if (blocks[i].ptr == ptr)
{
blocks[i].used = false;
return;
}
}
}
MFEM_HOST_DEVICE static void release_and_try_merge(void* ptr)
{
for (int i = 0; i < num_blocks; ++i)
{
if (blocks[i].ptr == ptr)
{
blocks[i].used = false;
merge_adjacent_free_blocks();
return;
}
}
}
private:
MFEM_HOST_DEVICE static void merge_adjacent_free_blocks()
{
// Simple bubble sort for simplicity (can be optimized)
for (int i = 0; i < num_blocks - 1; ++i)
{
for (int j = 0; j < num_blocks - i - 1; ++j)
{
if (blocks[j].ptr > blocks[j + 1].ptr)
{
MemoryBlock temp = blocks[j];
blocks[j] = blocks[j + 1];
blocks[j + 1] = temp;
}
}
}
for (int i = 0; i < num_blocks - 1; ++i)
{
if (!blocks[i].used && !blocks[i + 1].used)
{
blocks[i].size += blocks[i + 1].size;
for (int j = i + 1; j < num_blocks - 1; ++j)
{
blocks[j] = blocks[j + 1];
}
--num_blocks;
--i;
}
}
}
};
MFEM_HOST_DEVICE SharedMemoryManager::MemoryBlock
SharedMemoryManager::blocks[SharedMemoryManager::MAX_BLOCKS];
MFEM_HOST_DEVICE int SharedMemoryManager::num_blocks;
MFEM_HOST_DEVICE char* SharedMemoryManager::base_ptr;
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include "dfem_refactor.hpp"
#define DFEM_TEST_MAIN(function) \
int main(int argc, char* argv[]) \
{ \
Mpi::Init(); \
\
const char* device_config = "cpu"; \
const char* mesh_file = "../data/ref-square.mesh"; \
int polynomial_order = 1; \
int ir_order = 2; \
int refinements = 0; \
\
OptionsParser args(argc, argv); \
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use."); \
args.AddOption(&polynomial_order, "-o", "--order", ""); \
args.AddOption(&refinements, "-r", "--r", ""); \
args.AddOption(&ir_order, "-iro", "--iro", ""); \
args.AddOption(&device_config, "-d", "--device", \
"Device configuration string, see Device::Configure()."); \
args.ParseCheck(); \
\
Device device(device_config); \
if (Mpi::Root() == 0) \
{ \
device.Print(); \
} \
\
out << std::setprecision(12); \
\
int ret; \
\
ret = function(mesh_file, refinements, polynomial_order); \
out << #function; \
ret ? out << " FAILURE\n" : out << " OK\n"; \
\
return ret; \
}\
File diff suppressed because it is too large Load Diff
+130
View File
@@ -0,0 +1,130 @@
// SPDX-ArtifactOfProjectName: noisy
// SPDX-ArtifactOfProjectHomePage: https://github.com/VincentZalzal/noisy
// SPDX-FileCopyrightText: Copyright 2024 Vincent Zalzal
// SPDX-License-Identifier: MIT
#pragma once
#include <iomanip>
#include <iostream>
namespace vz {
struct Counters {
unsigned m_def_ctor = 0;
unsigned m_copy_ctor = 0;
unsigned m_move_ctor = 0;
unsigned m_copy_assign = 0;
unsigned m_move_assign = 0;
unsigned m_dtor = 0;
void reset() {
*this = {};
}
bool leaks() const {
return m_def_ctor + m_copy_ctor + m_move_ctor != m_dtor;
}
friend std::ostream& operator<<(std::ostream& os, const Counters& c) {
stream_counter(os, "Default constructor count: ", c.m_def_ctor );
stream_counter(os, "Copy constructor count: ", c.m_copy_ctor );
stream_counter(os, "Move constructor count: ", c.m_move_ctor );
stream_counter(os, "Copy assignment count: ", c.m_copy_assign);
stream_counter(os, "Move assignment count: ", c.m_move_assign);
stream_counter(os, "Destructor count: ", c.m_dtor );
return os;
}
friend bool operator==(const Counters& lhs, const Counters& rhs) {
return
lhs.m_def_ctor == rhs.m_def_ctor &&
lhs.m_copy_ctor == rhs.m_copy_ctor &&
lhs.m_move_ctor == rhs.m_move_ctor &&
lhs.m_copy_assign == rhs.m_copy_assign &&
lhs.m_move_assign == rhs.m_move_assign &&
lhs.m_dtor == rhs.m_dtor ;
}
friend bool operator!=(const Counters& lhs, const Counters& rhs) { return !(lhs == rhs); }
private:
static void stream_counter(std::ostream& os, const char* msg, unsigned value) {
if (value != 0)
os << msg << std::setw(2) << value << '\n';
}
};
namespace detail {
struct Globals {
~Globals() {
if (m_verbose)
std::cout << "\n===== Noisy counters =====\n" << m_counters;
}
Counters m_counters;
unsigned m_next_id = 0;
bool m_verbose = true;
};
}
class Noisy {
private:
static detail::Globals& globals() {
static detail::Globals s_globals;
return s_globals;
}
public:
static Counters& counters() { return globals().m_counters; }
static void set_verbose(bool verbose) { globals().m_verbose = verbose; }
Noisy() {
if (globals().m_verbose)
std::cout << *this << ": default constructor\n";
globals().m_counters.m_def_ctor++;
}
Noisy(const Noisy& other) {
if (globals().m_verbose)
std::cout << *this << ": copy constructor from " << other << '\n';
globals().m_counters.m_copy_ctor++;
}
Noisy(Noisy&& other) noexcept {
if (globals().m_verbose)
std::cout << *this << ": move constructor from " << other << '\n';
globals().m_counters.m_move_ctor++;
}
~Noisy() {
if (globals().m_verbose)
std::cout << *this << ": destructor\n";
globals().m_counters.m_dtor++;
}
Noisy& operator=(const Noisy& other) {
if (globals().m_verbose)
std::cout << *this << ": copy assignment from " << other << '\n';
globals().m_counters.m_copy_assign++;
return *this;
}
Noisy& operator=(Noisy&& other) noexcept {
if (globals().m_verbose)
std::cout << *this << ": move assignment from " << other << '\n';
globals().m_counters.m_move_assign++;
return *this;
}
unsigned id() const { return m_id; }
friend std::ostream& operator<<(std::ostream& os, const Noisy& noisy) { return os << "Noisy(" << std::setw(2) << noisy.m_id << ')'; }
private:
unsigned m_id = globals().m_next_id++;
};
}
@@ -0,0 +1,188 @@
{
using entity_t = typename kernel_t::entity_t;
auto kinput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.inputs, std::make_index_sequence<kernel.num_kinputs> {});
auto koutput_to_field = create_descriptors_to_fields_map<entity_t>(op.fields,
kernel.outputs, std::make_index_sequence<kernel.num_koutputs> {});
constexpr int hardcoded_output_idx = 0;
const int test_space_field_idx = koutput_to_field[hardcoded_output_idx];
const Operator *R = get_restriction<entity_t>(op.fields[test_space_field_idx],
element_dof_ordering);
auto output_fop = mfem::get<hardcoded_output_idx>(kernel.outputs);
const int num_elements = GetNumEntities<Entity::Element>(op.mesh);
const int num_entities = GetNumEntities<entity_t>(op.mesh);
const int num_qp = op.integration_rule.GetNPoints();
// assume only a single element type for now
std::vector<const DofToQuad*> dtq;
for (const auto &field : op.fields)
{
dtq.emplace_back(GetDofToQuad<entity_t>(field, op.integration_rule,
doftoquad_mode));
}
const int q1d = dtq[0]->nqpt;
derivative_action_e.SetSize(R->Height());
const int da_size_on_qp = GetSizeOnQP<entity_t>(
mfem::get<hardcoded_output_idx>(kernel.outputs),
op.fields[test_space_field_idx]);
auto input_dtq_maps = create_dtq_maps<entity_t>(kernel.inputs, dtq,
kinput_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(kernel.outputs, dtq,
koutput_to_field);
auto input_fops = create_bare_fops(kernel.inputs);
auto output_fops = create_bare_fops(kernel.outputs);
const int test_vdim = mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int test_op_dim =
mfem::get<hardcoded_output_idx>(output_fops).size_on_qp /
mfem::get<hardcoded_output_idx>(output_fops).vdim;
const int num_test_dof = R->Height() /
mfem::get<hardcoded_output_idx>(output_fops).vdim /
num_entities;
auto ir_weights = Reshape(this->op.integration_rule.GetWeights().Read(),
num_qp);
auto input_size_on_qp = get_input_size_on_qp(kernel.inputs,
std::make_index_sequence<kernel.num_kinputs> {});
auto shmem_info = get_shmem_info<entity_t>(input_dtq_maps,
output_dtq_maps,
op.fields,
num_entities,
kernel.inputs,
num_qp,
input_size_on_qp,
da_size_on_qp);
Vector shmem_cache(shmem_info.total_size);
func = [=](Vector &ye_mem) mutable
{
restriction<entity_t>(direction, direction_l, direction_e,
op.element_dof_ordering, derivative_idx);
// Check which qf inputs are dependent on the dependent variable
std::array<bool, kernel.num_kinputs> kinput_is_dependent;
bool no_qfinput_is_dependent = true;
for (int i = 0; i < kinput_is_dependent.size(); i++)
{
if (kinput_to_field[i] == derivative_idx)
{
no_qfinput_is_dependent = false;
kinput_is_dependent[i] = true;
// out << "function input " << i << " is dependent on "
// << op.fields[kinput_to_field[i]].field_label << "\n";
}
else
{
kinput_is_dependent[i] = false;
}
}
if (no_qfinput_is_dependent)
{
return;
}
// auto kernel_args = decay_tuple<typename kernel_t::kf_param_ts> {};
// auto kernel_shadow_args = decay_tuple<typename kernel_t::kf_param_ts> {};
// DeviceTensor<1, const double> integration_weights(
// this->op.integration_rule.GetWeights().Read(), num_qp);
// Vector zero;
// GeometricFactorMaps geometric_factors
// {
// DeviceTensor<3, const double>(zero.Read(), 0, 0, 0)
// };
// // Fields interpolated to the quadrature points in the order of
// // kernel function arguments
// auto input_qp = map_inputs_to_memory(input_qp_mem, num_qp,
// kernel.inputs,
// std::make_index_sequence<kernel.num_kinputs> {});
// auto directions_qp = map_inputs_to_memory(directions_qp_mem, num_qp,
// kernel.inputs,
// std::make_index_sequence<kernel.num_kinputs> {});
// constexpr int fixed_output_idx = 0;
// auto Bv = output_dtq_maps[fixed_output_idx];
// auto [num_test_qp, test_op_dim, num_test_dof] = Bv.GetShape();
// const int test_vdim = mfem::get<0>(kernel.outputs).vdim;
// DeviceTensor<3> ye = Reshape(ye_mem.ReadWrite(), num_test_dof, test_vdim, num_entities);
forall([=] MFEM_HOST_DEVICE (int e, double *shmem)
{
// map_fields_to_quadrature_data(
// input_qp, e, this->fields_e,
// kinput_to_field, input_dtq_maps,
// integration_weights, geometric_factors, kernel.inputs,
// std::make_index_sequence<kernel.num_kinputs> {});
// map_fields_to_quadrature_data_conditional(
// directions_qp, e,
// directions_e, kinput_to_field,
// input_dtq_maps,
// integration_weights,
// geometric_factors,
// kinput_is_dependent,
// kernel.inputs,
// std::make_index_sequence<kernel.num_kinputs> {});
// for (int qp = 0; qp < num_qp; qp++)
// {
// auto f_qp = apply_kernel_fwddiff_enzyme(
// kernel.func,
// kernel_args,
// input_qp,
// kernel_shadow_args,
// directions_qp,
// qp);
// auto r_qp = Reshape(&da_qp(0, qp, e), da_size_on_qp);
// for (int i = 0; i < da_size_on_qp; i++)
// {
// r_qp(i) = f_qp(i);
// }
// }
// DeviceTensor<3> fhat = Reshape(&da_qp(0, 0, e), test_vdim, test_op_dim, num_qp);
// DeviceTensor<2> y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
// map_quadrature_data_to_fields(y, fhat,
// output_fop,
// output_dtq_maps[hardcoded_output_idx]);
}, num_entities, q1d, q1d, 1, shmem_info.total_size, shmem_cache.GetData());
R->MultTranspose(ye_mem, derivative_action_l);
};
if constexpr (std::is_same_v<decltype(output_fop), One>)
{
prolongation_transpose = [&](Vector &r_local, Vector &y)
{
double local_sum = r_local.Sum();
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM,
op.mesh.GetComm());
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
};
}
else
{
auto P = get_prolongation(op.fields[test_space_field_idx]);
prolongation_transpose = [P](Vector &r_l, Vector &y)
{
P->MultTranspose(r_l, y);
};
}
+49
View File
@@ -0,0 +1,49 @@
* Calculate shared memory requirements
* Interpolation and integration
---
* If grad involved, need B and G
* Fit largest field, depends on polynomial order (#dofs)
-> vdim is irrelevant
* Temporaries for each sum
- DDQ (d1d x d1d x q1d) x 2 -> DDQ0, DDQ1
- DQQ (d1d x q1d x q1d) x 3 -> DQQ0, DQQ1, DQQ2
- QQQ (q1d x q1d x q1d) x 3 -> QQQ0, QQQ1, QQQ2
We need the following combinations at the same time
(1) DDQ0 + DDQ1 + DQQ0 + DQQ1 + DQQ2
(2) DQQ0 + DQQ1 + DQQ2 + QQQ0 + QQQ1 + QQQ2
(3) QQQ0 + QQQ1 + QQQ2 + QQD0 + QQD1 + QQD2
(4) QQD0 + QQD1 + QQD2 + QDD0 + QDD1 + QDD2
Allocate largest memory footprint from 2, 3 or 4 and
add memory footprint of fields and B/G.
Annotations with NR and R mean "not reusable" and
"reusable", respectively. This means the memory location is
reused for _all_ e.g. interpolation of a value etc.
----
For the action of nonlinear diffusion in 2D we have
(rho * |u|^2 \nabla u, \nabla v)
* Load
RHO (D x D) | R (after interpolation)
U (D x D x VDIM) | R (after interpolation)
B (Q x D) | NR
G (Q x D) | NR
* Interpolate Value
Temporary (Q x D) | R
R (Q x Q) | NR
U (Q x Q x VDIM) | NR
* Interpolate Grad
Temporaries (Q x D) + (Q x D) | R
U (Q x Q x DIM x VDIM) | NR
Quadrature point function
-> purely thread local
* Integrate Grad
R | temp from Interpolation
R | U from Load
+845
View File
@@ -0,0 +1,845 @@
// This is serac's tuple implementation
#pragma once
#include "general/backends.hpp"
#include <utility>
#include <mfem.hpp>
#include <tuple>
namespace mfem
{
/**
* @tparam T the types stored in the tuple
* @brief This is a class that mimics most of std::tuple's interface,
* except that it is usable in CUDA kernels and admits some arithmetic operator overloads.
*
* see https://en.cppreference.com/w/cpp/utility/tuple for more information about std::tuple
*/
template <typename... T>
struct tuple
{
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
*/
template <typename T0>
struct tuple<T0>
{
T0 v0; ///< The first member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
*/
template <typename T0, typename T1>
struct tuple<T0, T1>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
*/
template <typename T0, typename T1, typename T2>
struct tuple<T0, T1, T2>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3>
struct tuple<T0, T1, T2, T3>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
* @tparam T4 The fifth type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3, typename T4>
struct tuple<T0, T1, T2, T3, T4>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
* @tparam T4 The fifth type stored in the tuple
* @tparam T5 The sixth type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
struct tuple<T0, T1, T2, T3, T4, T5>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
T5 v5; ///< The sixth member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
* @tparam T4 The fifth type stored in the tuple
* @tparam T5 The sixth type stored in the tuple
* @tparam T6 The seventh type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
struct tuple<T0, T1, T2, T3, T4, T5, T6>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
T5 v5; ///< The sixth member of the tuple
T6 v6; ///< The seventh member of the tuple
};
/**
* @brief Type that mimics std::tuple
*
* @tparam T0 The first type stored in the tuple
* @tparam T1 The second type stored in the tuple
* @tparam T2 The third type stored in the tuple
* @tparam T3 The fourth type stored in the tuple
* @tparam T4 The fifth type stored in the tuple
* @tparam T5 The sixth type stored in the tuple
* @tparam T6 The seventh type stored in the tuple
* @tparam T7 The eighth type stored in the tuple
*/
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
struct tuple<T0, T1, T2, T3, T4, T5, T6, T7>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
T5 v5; ///< The sixth member of the tuple
T6 v6; ///< The seventh member of the tuple
T7 v7; ///< The eighth member of the tuple
};
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
struct tuple<T0, T1, T2, T3, T4, T5, T6, T7, T8>
{
T0 v0; ///< The first member of the tuple
T1 v1; ///< The second member of the tuple
T2 v2; ///< The third member of the tuple
T3 v3; ///< The fourth member of the tuple
T4 v4; ///< The fifth member of the tuple
T5 v5; ///< The sixth member of the tuple
T6 v6; ///< The seventh member of the tuple
T7 v7; ///< The eighth member of the tuple
T8 v8;
};
/**
* @brief Class template argument deduction rule for tuples
* @tparam T The variadic template parameter for tuple types
*/
template <typename... T>
MFEM_HOST_DEVICE
tuple(T...) -> tuple<T...>;
/**
* @brief helper function for combining a list of values into a tuple
* @tparam T types of the values to be tuple-d
* @param args the actual values to be put into a tuple
*/
template <typename... T>
MFEM_HOST_DEVICE tuple<T...> make_tuple(const T&... args)
{
return tuple<T...> {args...};
}
template <class... Types>
struct tuple_size
{
};
template <class... Types>
struct tuple_size<mfem::tuple<Types...>> :
std::integral_constant<std::size_t, sizeof...(Types)>
{
};
/**
* @tparam i the tuple index to access
* @tparam T the types stored in the tuple
* @brief return a reference to the ith tuple entry
*/
template <int i, typename... T>
MFEM_HOST_DEVICE constexpr auto& get(tuple<T...>& values)
{
static_assert(i < sizeof...(T), "");
if constexpr (i == 0)
{
return values.v0;
}
if constexpr (i == 1)
{
return values.v1;
}
if constexpr (i == 2)
{
return values.v2;
}
if constexpr (i == 3)
{
return values.v3;
}
if constexpr (i == 4)
{
return values.v4;
}
if constexpr (i == 5)
{
return values.v5;
}
if constexpr (i == 6)
{
return values.v6;
}
if constexpr (i == 7)
{
return values.v7;
}
if constexpr (i == 8)
{
return values.v8;
}
}
/**
* @tparam i the tuple index to access
* @tparam T the types stored in the tuple
* @brief return a copy of the ith tuple entry
*/
template <int i, typename... T>
MFEM_HOST_DEVICE constexpr const auto& get(const tuple<T...>& values)
{
static_assert(i < sizeof...(T), "");
if constexpr (i == 0)
{
return values.v0;
}
if constexpr (i == 1)
{
return values.v1;
}
if constexpr (i == 2)
{
return values.v2;
}
if constexpr (i == 3)
{
return values.v3;
}
if constexpr (i == 4)
{
return values.v4;
}
if constexpr (i == 5)
{
return values.v5;
}
if constexpr (i == 6)
{
return values.v6;
}
if constexpr (i == 7)
{
return values.v7;
}
if constexpr (i == 8)
{
return values.v8;
}
}
/**
* @brief a function intended to be used for extracting the ith type from a tuple.
*
* @note type<i>(my_tuple) returns a value, whereas get<i>(my_tuple) returns a reference
*
* @tparam i the index of the tuple to query
* @tparam T the types stored in the tuple
* @param values the tuple of values
* @return a copy of the ith entry of the input
*/
template <int i, typename... T>
MFEM_HOST_DEVICE constexpr auto type(const tuple<T...>& values)
{
static_assert(i < sizeof...(T), "");
if constexpr (i == 0)
{
return values.v0;
}
if constexpr (i == 1)
{
return values.v1;
}
if constexpr (i == 2)
{
return values.v2;
}
if constexpr (i == 3)
{
return values.v3;
}
if constexpr (i == 4)
{
return values.v4;
}
if constexpr (i == 5)
{
return values.v5;
}
if constexpr (i == 6)
{
return values.v6;
}
if constexpr (i == 7)
{
return values.v7;
}
if constexpr (i == 8)
{
return values.v8;
}
}
/**
* @brief A helper function for the + operator of tuples
*
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param y tuple of values
* @return the returned tuple sum
*/
template <typename... S, typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto plus_helper(const tuple<S...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) + get<i>(y)...};
}
/**
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @param y a tuple of values
* @brief return a tuple of values defined by elementwise sum of x and y
*/
template <typename... S, typename... T>
MFEM_HOST_DEVICE constexpr auto operator+(const tuple<S...>& x,
const tuple<T...>& y)
{
static_assert(sizeof...(S) == sizeof...(T));
return plus_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
}
/**
* @brief A helper function for the += operator of tuples
*
* @tparam T the types stored in the tuples x and y
* @tparam i integer sequence used to index the tuples
* @param x tuple of values to be incremented
* @param y tuple of increment values
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr void plus_equals_helper(tuple<T...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
((get<i>(x) += get<i>(y)), ...);
}
/**
* @tparam T the types stored in the tuples x and y
* @param x a tuple of values
* @param y a tuple of values
* @brief add values contained in y, to the tuple x
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator+=(tuple<T...>& x,
const tuple<T...>& y)
{
return plus_equals_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief A helper function for the -= operator of tuples
*
* @tparam T the types stored in the tuples x and y
* @tparam i integer sequence used to index the tuples
* @param x tuple of values to be subracted from
* @param y tuple of values to subtract from x
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr void minus_equals_helper(tuple<T...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
((get<i>(x) -= get<i>(y)), ...);
}
/**
* @tparam T the types stored in the tuples x and y
* @param x a tuple of values
* @param y a tuple of values
* @brief add values contained in y, to the tuple x
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator-=(tuple<T...>& x,
const tuple<T...>& y)
{
return minus_equals_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief A helper function for the - operator of tuples
*
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param y tuple of values
* @return the returned tuple difference
*/
template <typename... S, typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto minus_helper(const tuple<S...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) - get<i>(y)...};
}
/**
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @param y a tuple of values
* @brief return a tuple of values defined by elementwise difference of x and y
*/
template <typename... S, typename... T>
MFEM_HOST_DEVICE constexpr auto operator-(const tuple<S...>& x,
const tuple<T...>& y)
{
static_assert(sizeof...(S) == sizeof...(T));
return minus_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
}
/**
* @brief A helper function for the - operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @return the returned tuple difference
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto unary_minus_helper(const tuple<T...>& x,
std::integer_sequence<int, i...>)
{
return tuple{-get<i>(x)...};
}
/**
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @brief return a tuple of values defined by applying the unary minus operator to each element of x
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator-(const tuple<T...>& x)
{
return unary_minus_helper(x,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief A helper function for the / operator of tuples
*
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param y tuple of values
* @return the returned tuple ratio
*/
template <typename... S, typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<S...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) / get<i>(y)...};
}
/**
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @param y a tuple of values
* @brief return a tuple of values defined by elementwise division of x by y
*/
template <typename... S, typename... T>
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<S...>& x,
const tuple<T...>& y)
{
static_assert(sizeof...(S) == sizeof...(T));
return div_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
}
/**
* @brief A helper function for the / operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a the constant numerator
* @return the returned tuple ratio
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto div_helper(const double a,
const tuple<T...>& x, std::integer_sequence<int, i...>)
{
return tuple{a / get<i>(x)...};
}
/**
* @brief A helper function for the / operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a the constant denomenator
* @return the returned tuple ratio
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto div_helper(const tuple<T...>& x,
const double a, std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) / a...};
}
/**
* @tparam T the types stored in the tuple x
* @param a the numerator
* @param x a tuple of denominator values
* @brief return a tuple of values defined by division of a by the elements of x
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator/(const double a, const tuple<T...>& x)
{
return div_helper(a, x,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @tparam T the types stored in the tuple y
* @param x a tuple of numerator values
* @param a a denominator
* @brief return a tuple of values defined by elementwise division of x by a
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator/(const tuple<T...>& x, const double a)
{
return div_helper(x, a,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief A helper function for the * operator of tuples
*
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param y tuple of values
* @return the returned tuple product
*/
template <typename... S, typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<S...>& x,
const tuple<T...>& y,
std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) * get<i>(y)...};
}
/**
* @tparam S the types stored in the tuple x
* @tparam T the types stored in the tuple y
* @param x a tuple of values
* @param y a tuple of values
* @brief return a tuple of values defined by elementwise multiplication of x and y
*/
template <typename... S, typename... T>
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<S...>& x,
const tuple<T...>& y)
{
static_assert(sizeof...(S) == sizeof...(T));
return mult_helper(x, y,
std::make_integer_sequence<int, static_cast<int>(sizeof...(S))>());
}
/**
* @brief A helper function for the * operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a a constant multiplier
* @return the returned tuple product
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto mult_helper(const double a,
const tuple<T...>& x, std::integer_sequence<int, i...>)
{
return tuple{a * get<i>(x)...};
}
/**
* @brief A helper function for the * operator of tuples
*
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a a constant multiplier
* @return the returned tuple product
*/
template <typename... T, int... i>
MFEM_HOST_DEVICE constexpr auto mult_helper(const tuple<T...>& x,
const double a, std::integer_sequence<int, i...>)
{
return tuple{get<i>(x) * a...};
}
/**
* @tparam T the types stored in the tuple
* @param a a scaling factor
* @param x the tuple object
* @brief multiply each component of x by the value a on the left
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator*(const double a, const tuple<T...>& x)
{
return mult_helper(a, x,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @tparam T the types stored in the tuple
* @param x the tuple object
* @param a a scaling factor
* @brief multiply each component of x by the value a on the right
*/
template <typename... T>
MFEM_HOST_DEVICE constexpr auto operator*(const tuple<T...>& x, const double a)
{
return mult_helper(x, a,
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @tparam T the types stored in the tuple
* @tparam i a list of indices used to acces each element of the tuple
* @param out the ostream to write the output to
* @param A the tuple of values
* @brief helper used to implement printing a tuple of values
*/
template <typename... T, std::size_t... i>
auto& print_helper(std::ostream& out, const mfem::tuple<T...>& A,
std::integer_sequence<size_t, i...>)
{
out << "tuple{";
(..., (out << (i == 0 ? "" : ", ") << mfem::get<i>(A)));
out << "}";
return out;
}
/**
* @tparam T the types stored in the tuple
* @param out the ostream to write the output to
* @param A the tuple of values
* @brief print a tuple of values
*/
template <typename... T>
auto& operator<<(std::ostream& out, const mfem::tuple<T...>& A)
{
return print_helper(out, A, std::make_integer_sequence<size_t, sizeof...(T)>());
}
/**
* @brief A helper to apply a lambda to a tuple
*
* @tparam lambda The functor type
* @tparam T The tuple types
* @tparam i The integer sequence to i
* @param f The functor to apply to the tuple
* @param args The input tuple
* @return The functor output
*/
template <typename lambda, typename... T, int... i>
MFEM_HOST_DEVICE auto apply_helper(lambda f, tuple<T...>& args,
std::integer_sequence<int, i...>)
{
return f(get<i>(args)...);
}
/**
* @tparam lambda a callable type
* @tparam T the types of arguments to be passed in to f
* @param f the callable object
* @param args a tuple of arguments
* @brief a way of passing an n-tuple to a function that expects n separate arguments
*
* e.g. foo(bar, baz) is equivalent to apply(foo, mfem::tuple(bar,baz));
*/
template <typename lambda, typename... T>
MFEM_HOST_DEVICE auto apply(lambda f, tuple<T...>& args)
{
return apply_helper(f, std::move(args),
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @overload
*/
template <typename lambda, typename... T, int... i>
MFEM_HOST_DEVICE auto apply_helper(lambda f, const tuple<T...>& args,
std::integer_sequence<int, i...>)
{
return f(get<i>(args)...);
}
/**
* @tparam lambda a callable type
* @tparam T the types of arguments to be passed in to f
* @param f the callable object
* @param args a tuple of arguments
* @brief a way of passing an n-tuple to a function that expects n separate arguments
*
* e.g. foo(bar, baz) is equivalent to apply(foo, mfem::tuple(bar,baz));
*/
template <typename lambda, typename... T>
MFEM_HOST_DEVICE auto apply(lambda f, const tuple<T...>& args)
{
return apply_helper(f, std::move(args),
std::make_integer_sequence<int, static_cast<int>(sizeof...(T))>());
}
/**
* @brief a struct used to determine the type at index I of a tuple
*
* @note see: https://en.cppreference.com/w/cpp/utility/tuple/tuple_element
*
* @tparam I the index of the desired type
* @tparam T a tuple of different types
*/
template <size_t I, class T>
struct tuple_element;
// recursive case
/// @overload
template <size_t I, class Head, class... Tail>
struct tuple_element<I, tuple<Head, Tail...>> : tuple_element<I - 1,
tuple<Tail...>>
{
};
// base case
/// @overload
template <class Head, class... Tail>
struct tuple_element<0, tuple<Head, Tail...>>
{
using type = Head; ///< the type at the specified index
};
/**
* @brief Trait for checking if a type is a @p mfem::tuple
*/
template <typename T>
struct is_tuple : std::false_type
{
};
/// @overload
template <typename... T>
struct is_tuple<mfem::tuple<T...>> : std::true_type
{
};
/**
* @brief Trait for checking if a type if a @p mfem::tuple containing only @p mfem::tuple
*/
template <typename T>
struct is_tuple_of_tuples : std::false_type
{
};
/**
* @brief Trait for checking if a type if a @p mfem::tuple containing only @p mfem::tuple
*/
template <typename... T>
struct is_tuple_of_tuples<mfem::tuple<T...>>
{
static constexpr bool value = (is_tuple<T>::value &&
...); ///< true/false result of type check
};
} // namespace mfem
+123
View File
@@ -0,0 +1,123 @@
#include "dfem/dfem_refactor.hpp"
#include "fem/bilininteg.hpp"
#include "fem/coefficient.hpp"
#include "linalg/auxiliary.hpp"
#include "linalg/hypre.hpp"
using namespace mfem;
using mfem::internal::tensor;
int main(int argc, char *argv[])
{
Mpi::Init();
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "../data/ref-square.mesh";
int polynomial_order = 1;
int ir_order = 2;
int refinements = 1;
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
L2_FECollection fec(polynomial_order, dim, BasisType::GaussLobatto);
ParFiniteElementSpace fes(&mesh, &fec);
const IntegrationRule &ir = IntRules.Get(fes.GetFE(0)->GetGeomType(),
ir_order * fec.GetOrder());
const IntegrationRule &ir_face = IntRules.Get(
fes.GetTraceElement(0, fes.GetMesh()->GetFaceGeometry(0))->GetGeomType(),
ir_order * fec.GetOrder());
ParGridFunction u(&fes);
// // -\nabla \cdot (\nabla u + p * I) -> (\nabla u + p * I, \nabla v)
// auto advection_kernel = [](const tensor<double, 2> &dudxi,
// const tensor<double, 2, 2> &J,
// const double &w)
// {
// constexpr tensor<double, 2> b{1.0, 1.0};
// return std::tuple{dot(b, dudxi * inv(J)) * det(J) * w};
// };
// std::tuple argument_operators_0{Gradient{"quantity"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
// std::tuple output_operator_0{Value{"quantity"}};
// ElementOperator op_0{advection_kernel, argument_operators_0, output_operator_0};
// std::array solutions{FieldDescriptor{&fes, "quantity"}};
// std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
// DifferentiableOperator advection_op{solutions, parameters, std::tuple{op_0}, mesh, ir};
// auto adv_du = advection_op.template GetDerivativeWrt<0>({&u}, {mesh_nodes});
// HypreParMatrix A;
// adv_du->Assemble(A);
// std::ofstream mmatofs("dfem_mat.dat");
// A.PrintMatlab(mmatofs);
// mmatofs.close();
auto trace_kernel = [](const double &uL, const double &uR, const double &J,
const double &w)
{
return std::tuple{1.0 / J * w};
};
std::tuple argument_operators_0
{
FaceValueLeft{"quantity"},
FaceValueRight{"quantity"},
Gradient{"coordinates"},
Weight{"integration_weights"}
};
std::tuple output_operator_0{Value{"quantity"}};
FaceElementOperator op_0{trace_kernel, argument_operators_0, output_operator_0};
std::array solutions{FieldDescriptor{&fes, "quantity"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator trace_op{solutions, parameters, std::tuple{op_0}, mesh, ir_face};
auto vector_func = [](const Vector &, Vector &u)
{
u = 1.0;
};
VectorFunctionCoefficient vel_coeff(dim, vector_func);
ParBilinearForm adv_form(&fes);
constexpr double alpha = 1.0;
auto integ = new ConvectionIntegrator(vel_coeff, alpha);
integ->SetIntRule(&ir);
adv_form.AddInteriorFaceIntegrator(
new NonconservativeDGTraceIntegrator(vel_coeff, alpha));
// adv_form.AddDomainIntegrator(integ);
adv_form.Assemble();
adv_form.Finalize();
auto K = adv_form.ParallelAssemble();
std::ofstream kmatofs("mfem_mat.dat");
K->PrintMatlab(kmatofs);
kmatofs.close();
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" << mesh << u << std::flush;
return 0;
}
+150
View File
@@ -0,0 +1,150 @@
#include "dfem.hpp"
int main(int argc, char *argv[])
{
Mpi::Init();
std::cout << std::setprecision(9);
const char *mesh_file = "../data/star.mesh";
int polynomial_order = 1;
int refinements = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&polynomial_order, "-o", "--order", "");
args.AddOption(&refinements, "-r", "--r", "");
args.ParseCheck();
Mesh mesh_serial(mesh_file, 1, 1);
mesh_serial.SetCurvature(1);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
const int dim = mesh_serial.Dimension();
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh_serial.Clear();
constexpr int vdim = 2;
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_tdof_list;
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
std::cout << "nqpts = " << ir.GetNPoints() << std::endl;
std::cout << "ndofs = " << h1fes.GlobalTrueVSize() << std::endl;
ParGridFunction u(&h1fes);
auto exact_solution = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = x*x + y;
u(1) = x + 0.5*y*y;
};
VectorFunctionCoefficient exact_solution_coeff(dim, exact_solution);
auto elasticity_kernel = [](tensor<double, 2, 2> &dudxi,
tensor<double, 2, 2> &J,
double &w)
{
using mfem::internal::tensor;
using mfem::internal::IsotropicIdentity;
double lambda, mu;
{
lambda = 1.0;
mu = 1.0;
}
static constexpr auto I = IsotropicIdentity<2>();
auto eps = sym(dudxi * inv(J));
auto JxW = transpose(inv(J)) * det(J) * w;
auto r = (lambda * tr(eps) * I + 2.0 * mu * eps) * JxW;
return r;
};
tensor<double, 2, 2> dudxi, s_dudxi, J;
double w = 1.0;
enzyme::get<0>
(enzyme::autodiff<enzyme::Forward,
enzyme::DuplicatedNoNeed<tensor<double, 2, 2>>>
(+elasticity_kernel,
enzyme::Duplicated<tensor<double, 2, 2> *>(&dudxi, &s_dudxi),
enzyme::Const<tensor<double, 2, 2>*>(&J),
enzyme::Const<double*>(&w)));
// std::tuple input_descriptors = {Gradient{"displacement"}, Gradient{"coordinates"}, Weight{"integration_weight"}};
// std::tuple output_descriptors = {Gradient{"displacement"}};
// ElementOperator qf {elasticity_kernel, input_descriptors, output_descriptors};
// ElementOperator forcing_qf
// {
// [](tensor<double, 2> x, tensor<double, 2, 2> J, double w)
// {
// double lambda, mu;
// {
// lambda = 1.0;
// mu = 1.0;
// }
// auto f = x;
// f(0) = 4.0*mu + 2.0*lambda;
// f(1) = 2.0*mu + lambda;
// return f * det(J) * w;
// },
// // inputs
// std::tuple{
// Value{"coordinates"},
// Gradient{"coordinates"},
// Weight{"integration_weight"}},
// // outputs
// std::tuple{
// Value{"displacement"}}
// };
// std::vector<Field> solutions{{&u, "displacement"}};
// std::vector<Field> parameters{{mesh.GetNodes(), "coordinates"}};
// std::vector<Field> dependent_fields{{&u, "displacement"}};
// DifferentiableForm dop(solutions, parameters, dependent_fields, mesh);
// dop.AddElementOperator<AD::Enzyme>(qf, ir);
// dop.AddElementOperator<AD::None>(forcing_qf, ir);
// dop.SetEssentialTrueDofs(ess_tdof_list);
// GMRESSolver gmres(MPI_COMM_WORLD);
// gmres.SetRelTol(1e-12);
// gmres.SetMaxIter(5000);
// gmres.SetPrintLevel(IterativeSolver::PrintLevel().Summary());
// NewtonSolver newton(MPI_COMM_WORLD);
// newton.SetSolver(gmres);
// newton.SetOperator(dop);
// newton.SetRelTol(1e-12);
// newton.SetMaxIter(100);
// newton.SetPrintLevel(1);
// u = 1e-6;
// u.ProjectBdrCoefficient(exact_solution_coeff, ess_bdr);
// Vector x;
// u.GetTrueDofs(x);
// Vector zero;
// newton.Mult(zero, x);
// u.Distribute(x);
// std::cout << "|u-u_ex|_L2 = " << u.ComputeL2Error(exact_solution_coeff) << "\n";
return 0;
}
File diff suppressed because it is too large Load Diff
+115
View File
@@ -0,0 +1,115 @@
#include <tuple>
#include <type_traits>
#include <iostream>
#include <enzyme/enzyme>
template <typename T>
constexpr auto get_type_name() -> std::string_view
{
#if defined(__clang__)
constexpr auto prefix = std::string_view {"[T = "};
constexpr auto suffix = "]";
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(__GNUC__)
constexpr auto prefix = std::string_view {"with T = "};
constexpr auto suffix = "; ";
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(_MSC_VER)
constexpr auto prefix = std::string_view {"get_type_name<"};
constexpr auto suffix = ">(void)";
constexpr auto function = std::string_view{__FUNCSIG__};
#else
#error Unsupported compiler
#endif
const auto start = function.find(prefix) + prefix.size();
const auto end = function.find(suffix);
const auto size = end - start;
return function.substr(start, size);
}
template <typename ... Ts>
constexpr auto decay_types(std::tuple<Ts...> const &)
-> std::tuple<std::remove_cv_t<std::remove_reference_t<Ts>>...>;
template <typename T>
using decay_tuple = decltype(decay_types(std::declval<T>()));
template <class F> struct FunctionSignature;
template <typename output_t, typename... input_ts>
struct FunctionSignature<output_t(input_ts...)>
{
using return_t = output_t;
using parameter_ts = std::tuple<input_ts...>;
};
template <class T> struct create_function_signature;
template <typename output_t, typename T, typename... input_ts>
struct create_function_signature<output_t (T::*)(input_ts...) const>
{
using type = FunctionSignature<output_t(input_ts...)>;
};
template <typename arg_ts, std::size_t... Is>
auto create_enzyme_args(arg_ts &args,
arg_ts &shadow_args,
std::index_sequence<Is...>)
{
((std::cout << std::get<Is>(shadow_args) << "\n"), ...);
return std::tuple<enzyme::Duplicated<decltype(std::get<Is>(args))>...>
{
{ std::get<Is>(args), std::get<Is>(shadow_args) }...
};
}
template <typename kernel_t, typename arg_ts>
auto fwddiff_apply_enzyme(kernel_t kernel, arg_ts &&args, arg_ts &&shadow_args)
{
auto arg_indices =
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<arg_ts>>> {};
auto enzyme_args = create_enzyme_args(args, shadow_args, arg_indices);
using kf_return_t = typename create_function_signature<
decltype(&kernel_t::operator())>::type::return_t;
std::cout << "args is " << get_type_name<decltype(args)>() << "\n\n";
std::cout << "enzyme_args type is " << get_type_name<decltype(enzyme_args)>() <<
"\n\n";
std::cout << "return type is " << get_type_name<decltype(kf_return_t{})>() <<
"\n\n";
return std::apply([&](auto &&...args)
{
return enzyme::get<0>(
enzyme::autodiff<enzyme::Forward>
(+kernel, args...));
},
enzyme_args);
}
int main()
{
auto func = [](const double &x)
{
return x*x;
};
using kf_param_ts = typename create_function_signature<
decltype(&decltype(func)::operator())>::type::parameter_ts;
using kf_output_t = typename create_function_signature<
decltype(&decltype(func)::operator())>::type::return_t;
auto kernel_args = decay_tuple<kf_param_ts> {};
auto kernel_shadow_args = decay_tuple<kf_param_ts> {};
std::get<0>(kernel_args) = 3;
std::get<0>(kernel_shadow_args) = 1;
const auto res = fwddiff_apply_enzyme(func, kernel_args, kernel_shadow_args);
std::cout << res << " == 6\n";
return 0;
}
+114
View File
@@ -0,0 +1,114 @@
#include "dfem.hpp"
int main(int argc, char *argv[])
{
Mpi::Init();
std::cout << std::setprecision(9);
const char *mesh_file = "../data/star.mesh";
int polynomial_order = 1;
int refinements = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&polynomial_order, "-o", "--order", "");
args.AddOption(&refinements, "-r", "--r", "");
args.ParseCheck();
Mesh mesh_serial(mesh_file, 1, 1);
mesh_serial.SetCurvature(1);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
const int dim = mesh_serial.Dimension();
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh_serial.Clear();
constexpr int vdim = 2;
// test_partial_assembly_setup_qf(mesh, 1, polynomial_order);
// exit(0);
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_tdof_list;
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
std::cout << "nqpts = " << ir.GetNPoints() << std::endl;
std::cout << "ndofs = " << h1fes.GlobalTrueVSize() << std::endl;
ParGridFunction u(&h1fes);
ParGridFunction g(&h1fes);
ParGridFunction rho(&h1fes);
auto exact_solution = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = x*x + y;
u(1) = x + 0.5*y*y;
};
VectorFunctionCoefficient exact_solution_coeff(dim, exact_solution);
auto objective = [](tensor<double, 2> u, double rho,
tensor<double, 2, 2> J,
double w)
{
return sqnorm(u) * det(J) * w;
};
std::tuple inputs{Value{"displacement"}, Value{"density"}, Gradient{"coordinates"}, Weight{"integration_weight"}};
std::tuple outputs{ One{"integral"} };
ElementOperator objective_eop { objective, inputs, outputs };
std::vector<Field> solution_fields{{&u, "displacement"}};
std::vector<Field> parameter_fields{{mesh.GetNodes(), "coordinates"}, {&rho, "density"}};
std::vector<Field> dependent_variables{{&u, "displacement"}};
DifferentiableForm dop(solution_fields, parameter_fields, dependent_variables,
mesh);
dop.AddElementOperator(objective_eop, ir);
u.ProjectCoefficient(exact_solution_coeff);
Vector zero;
Vector y(1);
Vector utdof;
u.GetTrueDofs(utdof);
dop.Mult(utdof, y);
// finite difference test
Vector dgdu(u.Size());
Vector fx(y);
out << "g: ";
print_vector(fx);
out << "\n";
for (int i = 0; i < u.Size(); i++)
{
double h = 1e-6;
u(i) += h;
dop.Mult(u, y);
u(i) -= h;
y -= fx;
y /= h;
dgdu(i) = y(0);
}
out << "dgdu: ";
print_vector(dgdu);
// Vector dgdu = dop.GetGradientWrt({&u, "displacement"});
return 0;
}
+138
View File
@@ -0,0 +1,138 @@
#include "dfem.hpp"
int main(int argc, char *argv[])
{
Mpi::Init();
std::cout << std::setprecision(9);
const char *mesh_file = "../data/star.mesh";
int polynomial_order = 1;
int refinements = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&polynomial_order, "-o", "--order", "");
args.AddOption(&refinements, "-r", "--r", "");
args.ParseCheck();
Mesh mesh_serial(mesh_file, 1, 1);
mesh_serial.SetCurvature(1);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
const int dim = mesh_serial.Dimension();
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh_serial.Clear();
constexpr int vdim = 1;
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_tdof_list;
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
std::cout << "nqpts = " << ir.GetNPoints() << std::endl;
std::cout << "ndofs = " << h1fes.GlobalTrueVSize() << std::endl;
ParGridFunction u(&h1fes);
auto exact_solution = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
// PRESENT
return pow(x,2) + 0.5*x*pow(y,2);
};
FunctionCoefficient exact_solution_coeff(exact_solution);
auto plaplacian = [](double u,
tensor<double, 2> dudxi,
tensor<double, 2, 2> J,
double w)
{
using mfem::internal::tensor;
auto dudx = dudxi * inv(J);
auto JxW = transpose(inv(J)) * det(J) * w;
// PRESENT: Implement (1+u^2) * ∇u
return (1.0 + u*u) * dudx * JxW;
};
// PRESENT: Implement descriptors
std::tuple input_descriptors = {Value{"potential"}, Gradient{"potential"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
// PRESENT: Implement descriptors
std::tuple output_descriptors = {Gradient{"potential"}};
ElementOperator qf {plaplacian, input_descriptors, output_descriptors};
ElementOperator forcing_qf
{
[](tensor<double, 2> coords, tensor<double, 2, 2> J, double w)
{
int p = 2;
double x = coords(0);
double y = coords(1);
// *INDENT-OFF*
double mathematica_please_help_me = 2.*pow(x,2)*pow(y,2)*(pow(x,2) + 0.5*x*pow(y,2)) + 2*pow(2*x + 0.5*pow(y,2),2)*(pow(x,2) + 0.5*x*pow(y,2)) + 2*(1 + pow(pow(x,2) + 0.5*x*pow(y,2),2)) + 1.*x*(1 + pow(pow(x,2) + 0.5*x*pow(y,2),2));
return mathematica_please_help_me * det(J) * w;
// *INDENT-ON*
},
// inputs
std::tuple{
Value{"coordinates"},
Gradient{"coordinates"},
Weight{"integration_weight"}},
// outputs
std::tuple{
Value{"potential"}}
};
std::tuple list_of_qfs{qf_1, qf_2, qf_n};
std::vector<Field> solutions{{&u, "potential"}};
std::vector<Field> parameters{{mesh.GetNodes(), "coordinates"}};
DifferentiableForm dop(solutions, parameters, mesh);
dop.SetEssentialTrueDofs(ess_tdof_list);
auto R = dop.GetResidual(list_of_qfs, ir);
auto Jacobian_aka_dRdu = dop.GetDerivative<0>(list_of_qfs, ir);
// R(u) = (\grad u, \grad v) + (f, v)
// dop.AddElementOperator<AD::Enzyme>(qf, ir);
// dop.AddElementOperator<AD::None>(forcing_qf, ir);
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetRelTol(1e-12);
gmres.SetMaxIter(5000);
gmres.SetPrintLevel(IterativeSolver::PrintLevel().Summary());
NewtonSolver newton(MPI_COMM_WORLD);
newton.SetSolver(gmres);
newton.SetOperator(dop);
newton.SetRelTol(1e-12);
newton.SetMaxIter(100);
newton.SetPrintLevel(1);
u = 1e-6;
u.ProjectBdrCoefficient(exact_solution_coeff, ess_bdr);
Vector x;
u.GetTrueDofs(x);
Vector zero;
newton.Mult(zero, x);
u.Distribute(x);
std::cout << "|u-u_ex|_L2 = " << u.ComputeL2Error(exact_solution_coeff) << "\n";
return 0;
}
+192
View File
@@ -0,0 +1,192 @@
#include "dfem/dfem_refactor.hpp"
#include "linalg/hypre.hpp"
using namespace mfem;
using mfem::internal::tensor;
template <typename diffusion_t, typename force_t>
class DiffusionOperator : public Operator
{
template <typename diffusion_du_t>
class DiffusionJacobianOperator : public Operator
{
public:
DiffusionJacobianOperator(const DiffusionOperator *diffusion,
std::shared_ptr<diffusion_du_t> diff_du) :
Operator(diffusion->Height()), s(diffusion)
{
diff_du->Assemble(A);
A.EliminateBC(s->ess_tdofs, Operator::DiagonalPolicy::DIAG_ONE);
}
void Mult(const Vector &x, Vector &y) const override
{
A.Mult(x, y);
}
const DiffusionOperator *s;
HypreParMatrix A;
};
public:
DiffusionOperator(diffusion_t &diffusion, force_t &force,
Array<int> &ess_tdofs) :
Operator(diffusion.Height()), diffusion(diffusion),
force(force), ess_tdofs(ess_tdofs), f(force.Height()) {}
void SetParameters(ParGridFunction &mesh_nodes)
{
diffusion.SetParameters({&mesh_nodes});
force.SetParameters({&mesh_nodes});
Vector zero;
this->mesh_nodes.SetSpace(mesh_nodes.ParFESpace());
this->mesh_nodes = mesh_nodes;
}
void Mult(const Vector &x, Vector &r) const override
{
diffusion.Mult(x, r);
force.Mult(x, f);
r -= f;
r.SetSubVector(ess_tdofs, 0.0);
}
Operator &GetGradient(const Vector &x) const override
{
ParGridFunction u(const_cast<ParFiniteElementSpace *>
(*std::get_if<const ParFiniteElementSpace *>
(&diffusion.solutions[0].data)));
u.SetFromTrueDofs(x);
auto dfdu = diffusion.template GetDerivativeWrt<0>({&u}, {&mesh_nodes});
dfdu->Assemble(A);
A.EliminateBC(ess_tdofs, DiagonalPolicy::DIAG_ONE);
return A;
// delete jacobian_operator;
// jacobian_operator = new
// DiffusionJacobianOperator<typename std::remove_pointer<decltype(dfdu.get())>::type>
// (this, dfdu);
// return *jacobian_operator;
}
diffusion_t &diffusion;
force_t &force;
const Array<int> ess_tdofs;
mutable Vector f;
mutable ParGridFunction mesh_nodes;
mutable Operator *jacobian_operator = nullptr;
mutable HypreParMatrix A;
};
int main(int argc, char *argv[])
{
Mpi::Init();
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "../data/ref-square.mesh";
int polynomial_order = 2;
int ir_order = 2;
int refinements = 4;
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection potential_fec(polynomial_order, dim);
ParFiniteElementSpace potential_fes(&mesh, &potential_fec);
const IntegrationRule &potential_ir =
IntRules.Get(potential_fes.GetFE(0)->GetGeomType(),
ir_order * potential_fec.GetOrder());
Array<int> bdr_attr_is_ess(mesh.bdr_attributes.Max());
bdr_attr_is_ess = 1;
Array<int> ess_tdofs;
potential_fes.GetEssentialTrueDofs(bdr_attr_is_ess, ess_tdofs);
ParGridFunction u(&potential_fes);
u = 0.0;
auto diffusion_kernel = [](const internal::dual<double, double> &u,
const tensor<internal::dual<double, double>, 2> &dudxi,
const tensor<double, 2, 2> &J,
const double &w)
{
auto invJ = inv(J);
auto dudx = dudxi * invJ;
return std::tuple{(1.0 + u * u) * dudx * det(J) * w * transpose(invJ)};
};
std::tuple argument_operators_0{Value{"potential"}, Gradient{"potential"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
std::tuple output_operator_0{Gradient{"potential"}};
ElementOperator op_0{diffusion_kernel, argument_operators_0, output_operator_0};
auto force_kernel = [](const tensor<double, 2, 2> &J,
const double &w)
{
return std::tuple{1.0 * det(J) * w};
};
std::tuple argument_operators_1{Gradient{"coordinates"}, Weight{"integration_weights"}};
std::tuple output_operator_1{Value{"potential"}};
ElementOperator op_1{force_kernel, argument_operators_1, output_operator_1};
std::array solutions{FieldDescriptor{&potential_fes, "potential"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator diffusion_op{solutions, parameters, std::tuple{op_0}, mesh, potential_ir};
DifferentiableOperator force_op{solutions, parameters, std::tuple{op_1}, mesh, potential_ir};
DiffusionOperator diffusion(diffusion_op, force_op, ess_tdofs);
diffusion.SetParameters({*mesh_nodes});
HypreBoomerAMG amg;
amg.SetPrintLevel(0);
CGSolver solver(MPI_COMM_WORLD);
solver.SetAbsTol(1e-12);
solver.SetRelTol(1e-12);
solver.SetMaxIter(500);
solver.SetPrintLevel(2);
solver.SetPreconditioner(amg);
NewtonSolver newton(MPI_COMM_WORLD);
newton.SetOperator(diffusion);
newton.SetSolver(solver);
newton.SetRelTol(1e-8);
newton.SetMaxIter(10);
newton.SetPrintLevel(1);
Vector zero;
Vector x(potential_fes.GetTrueVSize());
u.ParallelProject(x);
newton.Mult(zero, x);
u.SetFromTrueDofs(x);
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" << mesh << u << std::flush;
return 0;
}
+102
View File
@@ -0,0 +1,102 @@
#include "mfem.hpp"
#include "dfem/dfem_refactor.hpp"
using namespace mfem;
auto main(int argc, char *argv[]) -> int
{
Mpi::Init();
std::cout << std::setprecision(9);
const char *mesh_file = "../data/star.mesh";
int polynomial_order = 1;
int refinements = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&polynomial_order, "-o", "--order", "");
args.AddOption(&refinements, "-r", "--r", "");
args.ParseCheck();
Mesh mesh_serial(mesh_file, 1, 1);
mesh_serial.SetCurvature(1);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
const int dim = mesh_serial.Dimension();
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
constexpr int vdim = 1;
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_tdof_list;
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
std::cout << "nqpts = " << ir.GetNPoints() << std::endl;
std::cout << "ndofs = " << h1fes.GlobalTrueVSize() << std::endl;
ParGridFunction u(&h1fes);
auto exact_solution = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
return 2.345 + x + y;
};
FunctionCoefficient exact_solution_coeff(exact_solution);
u.ProjectCoefficient(exact_solution_coeff);
auto domain_qf = [](const double &u,
const tensor<double, 2, 2> &J,
const double &w)
{
out << u << "\n" << J << "\n" << w << "\n\n";
return std::tuple{u * det(J) * w};
};
std::tuple input_descriptors = {Value{"potential"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
std::tuple output_descriptors = {Value{"potential"}};
ElementOperator eop{domain_qf, input_descriptors, output_descriptors};
auto ops = std::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop{solutions, parameters, ops, mesh, ir};
Vector x(h1fes.GetTrueVSize()), y(h1fes.GetTrueVSize());
u.GetTrueDofs(x);
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
// Derivative wrt "potential", indicated by the index 0 of the set {solutions} \cup {parameters}
auto dFd0 = dop.GetDerivativeWrt<0>({&u}, {mesh_nodes});
dFd0->Mult(x, y);
Vector dFd0_vec;
dFd0->Assemble(dFd0_vec);
// Derivative wrt "coordinates", indicated by the index 1 of the set {solutions} \cup {parameters}
auto dFd1 = dop.GetDerivativeWrt<1>({&u}, {mesh_nodes});
dFd1->Mult(x, y);
return 0;
}
+302
View File
@@ -0,0 +1,302 @@
#include "dfem/dfem.hpp"
using namespace mfem;
using mfem::internal::tensor;
template <typename momentum_t, typename mass_conservation_t>
class NavierStokesOperator : public Operator
{
template <typename momentum_du_t, typename momentum_dp_t>
class NavierStokesJacobianOperator : public Operator
{
public:
NavierStokesJacobianOperator(const NavierStokesOperator *ns,
std::shared_ptr<momentum_du_t> mom_du,
std::shared_ptr<momentum_dp_t> mom_dp) :
Operator(ns->Height()), ns(ns), block_op(ns->block_offsets)
{
mom_du->Assemble(A);
A.EliminateBC(ns->vel_ess_tdofs, Operator::DiagonalPolicy::DIAG_ONE);
mom_dp->Assemble(D);
D.EliminateRows(ns->vel_ess_tdofs);
Dt = new TransposeOperator(D);
block_op.SetBlock(0, 0, &A);
block_op.SetBlock(0, 1, &D);
block_op.SetBlock(1, 0, Dt);
// std::ofstream amatofs("dfem_mat.dat");
// block_op.PrintMatlab(amatofs);
// amatofs.close();
}
void Mult(const Vector &x, Vector &y) const override
{
block_op.Mult(x, y);
}
~NavierStokesJacobianOperator()
{
delete Dt;
}
const NavierStokesOperator *ns = nullptr;
HypreParMatrix A, D;
TransposeOperator *Dt = nullptr;
BlockOperator block_op;
};
public:
NavierStokesOperator(momentum_t &momentum,
mass_conservation_t &mass_conservation,
Array<int> &offsets, Array<int> &vel_ess_tdofs) :
Operator(offsets.Last()), momentum(momentum),
mass_conservation(mass_conservation),
block_offsets(offsets), vel_ess_tdofs(vel_ess_tdofs) {}
void SetParameters(ParGridFunction &mesh_nodes)
{
momentum.SetParameters({&mesh_nodes});
mass_conservation.SetParameters({&mesh_nodes});
this->mesh_nodes.SetSpace(mesh_nodes.ParFESpace());
this->mesh_nodes = mesh_nodes;
}
void Mult(const Vector &x, Vector &r) const override
{
Vector ru(r.ReadWrite() + block_offsets[0],
block_offsets[1] - block_offsets[0]);
Vector rp(r.ReadWrite() + block_offsets[1],
block_offsets[2] - block_offsets[1]);
momentum.Mult(x, ru);
mass_conservation.Mult(x, rp);
ru.SetSubVector(vel_ess_tdofs, 0.0);
}
Operator &GetGradient(const Vector &x) const override
{
xtmp = x;
BlockVector xb(xtmp.ReadWrite(), block_offsets);
ParGridFunction u(const_cast<ParFiniteElementSpace *>
(*std::get_if<const ParFiniteElementSpace *>
(&momentum.solutions[0].data)));
ParGridFunction p(const_cast<ParFiniteElementSpace *>
(*std::get_if<const ParFiniteElementSpace *>
(&momentum.solutions[1].data)));
u.SetFromTrueDofs(xb.GetBlock(0));
p.SetFromTrueDofs(xb.GetBlock(1));
auto mom_du = momentum.template GetDerivativeWrt<0>({&u, &p}, {&mesh_nodes});
auto mom_dp = momentum.template GetDerivativeWrt<1>({&u, &p}, {&mesh_nodes});
delete jacobian_operator;
jacobian_operator = new NavierStokesJacobianOperator<
typename std::remove_pointer<decltype(mom_du.get())>::type,
typename std::remove_pointer<decltype(mom_dp.get())>::type>(this, mom_du,
mom_dp);
return *jacobian_operator;
}
momentum_t &momentum;
mass_conservation_t &mass_conservation;
const Array<int> block_offsets;
const Array<int> vel_ess_tdofs;
mutable Vector xtmp;
mutable ParGridFunction mesh_nodes;
mutable Operator *jacobian_operator = nullptr;
};
double reynolds = 10.0;
int main(int argc, char *argv[])
{
constexpr int dim = 3;
constexpr int vdim = dim;
Mpi::Init();
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "../data/ref-cube.mesh";
int polynomial_order = 2;
int ir_order = 2;
int refinements = 2;
OptionsParser args(argc, argv);
args.AddOption(&refinements, "-r", "--refinements", "");
args.AddOption(&reynolds, "-rey", "--reynolds", "");
args.ParseCheck();
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection velocity_fec(polynomial_order, dim);
ParFiniteElementSpace velocity_fes(&mesh, &velocity_fec, dim);
H1_FECollection pressure_fec(polynomial_order - 1, dim);
ParFiniteElementSpace pressure_fes(&mesh, &pressure_fec);
const IntegrationRule &velocity_ir =
IntRules.Get(velocity_fes.GetFE(0)->GetGeomType(),
ir_order * velocity_fec.GetOrder());
const IntegrationRule &pressure_ir =
IntRules.Get(pressure_fes.GetFE(0)->GetGeomType(),
ir_order * pressure_fec.GetOrder());
Array<int> bdr_attr_is_ess(mesh.bdr_attributes.Max());
bdr_attr_is_ess = 1;
Array<int> vel_ess_tdofs;
velocity_fes.GetEssentialTrueDofs(bdr_attr_is_ess, vel_ess_tdofs);
ParGridFunction u(&velocity_fes);
ParGridFunction p(&pressure_fes);
auto u_f = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double z = coords(2);
if (z >= 1.0)
{
u(0) = 1.0;
}
else
{
u(0) = 0.0;
}
u(1) = 0.0;
u(2) = 0.0;
};
auto u_coef = VectorFunctionCoefficient(dim, u_f);
u.ProjectCoefficient(u_coef);
p = 0.0;
// -\nabla \cdot (\nabla u + p * I) -> (\nabla u + p * I, \nabla v)
auto momentum_kernel = [](const tensor<double, dim> &u,
const tensor<double, dim, dim> &dudxi,
const double &p,
const tensor<double, dim, dim> &J,
const double &w)
{
static constexpr auto I = mfem::internal::IsotropicIdentity<dim>();
auto invJ = inv(J);
auto dudx = dudxi * invJ;
double Re = reynolds;
return mfem::tuple{(outer(u, u) - 1.0 / Re * dudx + p * I) * det(J) * w * transpose(invJ)};
};
mfem::tuple argument_operators_0{Value{"velocity"}, Gradient{"velocity"}, Value{"pressure"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator_0{Gradient{"velocity"}};
ElementOperator op_0{momentum_kernel, argument_operators_0, output_operator_0};
// (\nabla \cdot u, q)
auto mass_conservation_kernel = [](const tensor<double, dim, dim> &dudxi,
const tensor<double, dim, dim> &J,
const double &w)
{
return mfem::tuple{tr(dudxi * inv(J)) * det(J) * w};
};
mfem::tuple argument_operators_1{Gradient{"velocity"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator_1{Value{"pressure"}};
ElementOperator op_1{mass_conservation_kernel, argument_operators_1, output_operator_1};
std::array solutions{FieldDescriptor{&velocity_fes, "velocity"}, FieldDescriptor{&pressure_fes, "pressure"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator momentum_op{solutions, parameters, mfem::tuple{op_0}, mesh, velocity_ir};
DifferentiableOperator mass_conservation_op{solutions, parameters, mfem::tuple{op_1}, mesh, pressure_ir};
// Preconditioner form
auto pressure_mass_kernel = [](const double &p,
const tensor<double, dim, dim> &J,
const double &w)
{
return mfem::tuple{p * det(J) * w};
};
mfem::tuple pms_args{Value{"pressure"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple pms_outs{Value{"pressure"}};
ElementOperator pressure_mass{pressure_mass_kernel, pms_args, pms_outs};
std::array pms_sols{FieldDescriptor{&pressure_fes, "pressure"}};
std::array pms_params{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator pressure_mass_op{pms_sols, pms_params, mfem::tuple{pressure_mass}, mesh, pressure_ir};
Array<int> block_offsets(3);
block_offsets[0] = 0;
block_offsets[1] = velocity_fes.GetTrueVSize();
block_offsets[2] = pressure_fes.GetTrueVSize();
block_offsets.PartialSum();
NavierStokesOperator navierstokes(momentum_op, mass_conservation_op,
block_offsets,
vel_ess_tdofs);
BlockVector x(block_offsets), y(block_offsets);
u.ParallelProject(x.GetBlock(0));
// p.ParallelProject(x.GetBlock(1));
navierstokes.SetParameters(*mesh_nodes);
HypreParMatrix A;
momentum_op.template GetDerivativeWrt<0>({&u, &p}, {mesh_nodes})->Assemble(A);
A.EliminateBC(vel_ess_tdofs, Operator::DiagonalPolicy::DIAG_ONE);
HypreBoomerAMG amg(A);
amg.SetMaxLevels(50);
amg.SetPrintLevel(0);
HypreParMatrix Mp;
pressure_mass_op.template GetDerivativeWrt<0>({&p}, {mesh_nodes})->Assemble(Mp);
HypreDiagScale Mp_inv(Mp);
BlockDiagonalPreconditioner prec(block_offsets);
prec.SetDiagonalBlock(0, &amg);
prec.SetDiagonalBlock(1, &Mp_inv);
GMRESSolver solver(MPI_COMM_WORLD);
solver.SetAbsTol(0.0);
solver.SetRelTol(1e-8);
solver.SetKDim(100);
solver.SetMaxIter(500);
solver.SetPrintLevel(2);
solver.SetPreconditioner(prec);
NewtonSolver newton(MPI_COMM_WORLD);
newton.SetOperator(navierstokes);
newton.SetSolver(solver);
newton.SetRelTol(1e-8);
newton.SetMaxIter(50);
newton.SetPrintLevel(1);
Vector zero;
newton.Mult(zero, x);
u.SetFromTrueDofs(x.GetBlock(0));
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" << mesh << u << std::flush;
return 0;
}
+174
View File
@@ -0,0 +1,174 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_diffusion(
std::string mesh_file, int refinements, int polynomial_order)
{
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == 2, "incorrect mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
mesh_serial.Clear();
out << "#el: " << mesh.GetNE() << "\n";
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder());
out << "#qp: " << ir.GetNPoints() << "\n";
ParGridFunction f1_g(&h1fes);
ParGridFunction rho_g(&h1fes);
auto kernel = [] MFEM_HOST_DEVICE(const tensor<double, 2, 2>& J,
const double& w, const tensor<double, 2>& dudxi)
{
auto invJ = inv(J);
return mfem::tuple{dudxi * invJ * transpose(invJ) * det(J) * w};
};
mfem::tuple argument_operators =
{
Gradient{"coordinates"}, Weight{}, Gradient{"potential"}
};
mfem::tuple output_operator = {Gradient{"potential"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector& coords)
{
const double x = coords(0);
const double y = coords(1);
return 2.345 + 0.25 * x * x * y + y * y * x;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
y.HostRead();
ParBilinearForm a(&h1fes);
a.AddDomainIntegrator(new DiffusionIntegrator);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a.Assemble();
a.Finalize();
Vector y2(h1fes.TrueVSize());
a.Mult(x, y2);
y2.HostRead();
Vector diff(y2);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(y2);
print_vector(y);
return 1;
}
// // Test linearization here as well
// auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
// if (dFdu->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdu unexpected height of " << dFdu->Height() << "\n";
// return 1;
// }
// dFdu->Mult(x, y);
// y.HostRead();
// a.Mult(x, y2);
// y2.HostRead();
// diff = y2;
// diff -= y;
// if (diff.Norml2() > 1e-10)
// {
// print_vector(diff);
// print_vector(y2);
// print_vector(y);
// return 1;
// }
// // fd jacobian test
// {
// double eps = 1.0e-6;
// Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
// v *= eps;
// xpv += v;
// xmv -= v;
// dop.Mult(xpv, fxpv);
// dop.Mult(xmv, fxmv);
// fxpv -= fxmv;
// fxpv /= (2.0*eps);
// fxpv -= y;
// if (fxpv.Norml2() > eps)
// {
// out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
// return 1;
// }
// }
// f1_g.ProjectCoefficient(f1_c);
// rho_g.ProjectCoefficient(rho_c);
// auto dFdrho = dop.GetDerivativeWrt<1>({&f1_g}, {&rho_g, mesh_nodes});
// if (dFdrho->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdrho unexpected height of " << dFdrho->Height() << "\n";
// return 1;
// }
// dFdrho->Mult(rho_g, y);
// // fd test
// {
// double eps = 1.0e-6;
// Vector v(rho_g), rhopv(rho_g), rhomv(rho_g), frhopv(x.Size()),
// frhomv(x.Size()); v *= eps; rhopv += v; rhomv -= v;
// dop.SetParameters({&rhopv, mesh_nodes});
// dop.Mult(x, frhopv);
// dop.SetParameters({&rhomv, mesh_nodes});
// dop.Mult(x, frhomv);
// frhopv -= frhomv;
// frhopv /= (2.0*eps);
// frhopv -= y;
// if (frhopv.Norml2() > eps)
// {
// out << "||dFdu_FD u^* - ex||_l2 = " << frhopv.Norml2() << "\n";
// return 1;
// }
// }
return 0;
}
DFEM_TEST_MAIN(test_diffusion);
+296
View File
@@ -0,0 +1,296 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
#include "examples/dfem/dfem_parametricspace.hpp"
#include "fem/bilininteg.hpp"
#include "general/tic_toc.hpp"
using namespace mfem;
using mfem::internal::tensor;
using mfem::internal::dual;
int test_diffusion_3d(
std::string mesh_file, int refinements, int polynomial_order)
{
constexpr int num_samples = 10;
constexpr int dim = 3;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "incorrect mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(polynomial_order);
mesh_serial.Clear();
out << "#el: " << mesh.GetNE() << "\n";
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
0)->GetDim() - 1);
printf("#ndof per el = %d\n", h1fes.GetFE(0)->GetDof());
printf("#nqp = %d\n", ir.GetNPoints());
printf("#q1d = %d\n", (int)floor(pow(ir.GetNPoints(), 1.0/dim) + 0.5));
ParametricSpace qdata_space(dim, dim * dim, ir.GetNPoints(),
dim * dim * ir.GetNPoints() * mesh.GetNE());
ParametricFunction qdata(qdata_space);
ParGridFunction f1_g(&h1fes);
ParGridFunction rho_g(&h1fes);
auto f1 = [](const Vector& coords)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
return 2.345 + x + x*y + 1.25 * z*x;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(h1fes.GetTrueVSize());
{
auto diffusion_mf_kernel =
[] MFEM_HOST_DEVICE (
const tensor<dual<real_t, real_t>, dim>& dudxi,
const tensor<double, dim, dim>& J,
const double& w)
{
auto invJ = inv(J);
return mfem::tuple{dudxi * invJ * transpose(invJ) * det(J) * w};
};
mfem::tuple argument_operators = {Gradient{"potential"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator = {Gradient{"potential"}};
ElementOperator eop = {diffusion_mf_kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
dop.SetParameters({mesh_nodes});
StopWatch sw;
sw.Start();
for (int i = 0; i < num_samples; i++)
{
dop.Mult(x, y);
}
sw.Stop();
printf("dfem mf: %fs\n", sw.RealTime() / num_samples);
y.HostRead();
}
{
auto diffusion_setup_kernel =
[] MFEM_HOST_DEVICE (
const tensor<double, dim, dim>& J,
const double& w)
{
auto invJ = inv(J);
return mfem::tuple{invJ * transpose(invJ) * det(J) * w};
};
mfem::tuple argument_operators = {Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator = {None{"qdata"}};
ElementOperator eop = {diffusion_setup_kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array
{
FieldDescriptor{&mesh_fes, "coordinates"},
FieldDescriptor{&qdata_space, "qdata"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
dop.SetParameters({mesh_nodes, &qdata});
StopWatch sw;
sw.Start();
for (int i = 0; i < num_samples; i++)
{
dop.Mult(x, qdata);
}
sw.Stop();
printf("dfem pa setup: %fs\n", sw.RealTime() / num_samples);
qdata.HostRead();
}
// printf("qdata: ");
// print_vector(qdata);
{
auto diffusion_apply_kernel =
[] MFEM_HOST_DEVICE (
const tensor<dual<real_t, real_t>, dim>& dudxi,
const tensor<double, dim, dim>& qdata)
{
return mfem::tuple{dudxi * qdata};
};
mfem::tuple argument_operators = {Gradient{"potential"}, None{"qdata"}};
mfem::tuple output_operator = {Gradient{"potential"}};
ElementOperator eop = {diffusion_apply_kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&qdata_space, "qdata"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
dop.SetParameters({&qdata});
StopWatch sw;
sw.Start();
for (int i = 0; i < num_samples; i++)
{
dop.Mult(x, y);
}
sw.Stop();
printf("dfem pa apply: %fs\n", sw.RealTime() / num_samples);
y.HostRead();
}
// printf("y: ");
// print_vector(y);
Vector y2(h1fes.TrueVSize());
{
ParBilinearForm a(&h1fes);
auto diff_integ = new DiffusionIntegrator;
diff_integ->SetIntRule(&ir);
a.AddDomainIntegrator(diff_integ);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
OperatorPtr A;
StopWatch sw;
sw.Start();
a.Assemble();
a.Finalize();
Array<int> empty;
a.FormSystemMatrix(empty, A);
sw.Stop();
printf("mfem pa setup: %fs\n", sw.RealTime());
sw.Clear();
sw.Start();
for (int i = 0; i < num_samples; i++)
{
A->Mult(x, y2);
}
sw.Stop();
printf("mfem pa apply: %fs\n", sw.RealTime() / num_samples);
y2.HostRead();
}
// printf("y2: ");
// print_vector(y2);
Vector diff(y2);
diff -= y;
if (diff.Norml2() > 1e-15)
{
// printf("y ");
// print_vector(y);
// printf("y2: ");
// print_vector(y2);
// printf("diff: ");
// print_vector(diff);
return 1;
}
// Test linearization here as well
// auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
// if (dFdu->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdu unexpected height of " << dFdu->Height() << "\n";
// return 1;
// }
// dFdu->Mult(x, y);
// y.HostRead();
// a.Mult(x, y2);
// y2.HostRead();
// diff = y2;
// diff -= y;
// if (diff.Norml2() > 1e-10)
// {
// print_vector(diff);
// print_vector(y2);
// print_vector(y);
// return 1;
// }
// // fd jacobian test
// {
// double eps = 1.0e-6;
// Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
// v *= eps;
// xpv += v;
// xmv -= v;
// dop.Mult(xpv, fxpv);
// dop.Mult(xmv, fxmv);
// fxpv -= fxmv;
// fxpv /= (2.0*eps);
// fxpv -= y;
// if (fxpv.Norml2() > eps)
// {
// out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
// return 1;
// }
// }
// f1_g.ProjectCoefficient(f1_c);
// rho_g.ProjectCoefficient(rho_c);
// auto dFdrho = dop.GetDerivativeWrt<1>({&f1_g}, {&rho_g, mesh_nodes});
// if (dFdrho->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdrho unexpected height of " << dFdrho->Height() << "\n";
// return 1;
// }
// dFdrho->Mult(rho_g, y);
// // fd test
// {
// double eps = 1.0e-6;
// Vector v(rho_g), rhopv(rho_g), rhomv(rho_g), frhopv(x.Size()),
// frhomv(x.Size()); v *= eps; rhopv += v; rhomv -= v;
// dop.SetParameters({&rhopv, mesh_nodes});
// dop.Mult(x, frhopv);
// dop.SetParameters({&rhomv, mesh_nodes});
// dop.Mult(x, frhomv);
// frhopv -= frhomv;
// frhopv /= (2.0*eps);
// frhopv -= y;
// if (frhopv.Norml2() > eps)
// {
// out << "||dFdu_FD u^* - ex||_l2 = " << frhopv.Norml2() << "\n";
// return 1;
// }
// }
return 0;
}
DFEM_TEST_MAIN(test_diffusion_3d);
@@ -0,0 +1,309 @@
#include "dfem/dfem_test_macro.hpp"
#include "examples/dfem/dfem_fieldoperator.hpp"
#include "examples/dfem/dfem_refactor.hpp"
#include "fem/bilininteg.hpp"
#include "general/tic_toc.hpp"
#include <utility>
using namespace mfem;
using mfem::internal::tensor;
using mfem::internal::dual;
int test_diffusion_3d(
std::string mesh_file, int refinements, int polynomial_order)
{
constexpr int num_samples = 100;
constexpr int dim = 3;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "incorrect mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(polynomial_order);
mesh_serial.Clear();
out << "#el: " << mesh.GetNE() << "\n";
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
0)->GetDim() - 1);
printf("#ndof per el = %d\n", h1fes.GetFE(0)->GetDof());
printf("#nqp = %d\n", ir.GetNPoints());
printf("#q1d = %d\n", (int)floor(pow(ir.GetNPoints(), 1.0/dim) + 0.5));
ParametricSpace qdata_space(dim, dim * dim, ir.GetNPoints(),
dim * dim * ir.GetNPoints() * mesh.GetNE());
ParametricFunction qdata(qdata_space);
ParGridFunction f1_g(&h1fes);
ParGridFunction rho_g(&h1fes);
auto f1 = [](const Vector& coords)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
return 2.345 + x + x*y + 1.25 * z*x;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(h1fes.GetTrueVSize());
{
std::shared_ptr<DerivativeOperator> dpotential;
{
auto diffusion_mf_kernel =
[] MFEM_HOST_DEVICE (
const tensor<real_t, dim>& dudxi,
const tensor<real_t, dim, dim>& J,
const real_t& w)
{
auto invJ = inv(J);
return mfem::tuple{dudxi * invJ * transpose(invJ) * det(J) * w};
};
constexpr int Potential = 0;
constexpr int Coordinates = 1;
auto input_operators = mfem::tuple{Gradient<Potential>{}, Gradient<Coordinates>{}, Weight{}};
auto output_operator = mfem::tuple{Gradient<Potential>{}};
auto solutions = std::vector{FieldDescriptor{Potential, &h1fes}};
auto parameters = std::vector{FieldDescriptor{Coordinates, &mesh_fes}};
DifferentiableOperator dop(solutions, parameters, mesh);
auto derivatives = std::integer_sequence<size_t, Potential> {};
dop.AddDomainIntegrator(
diffusion_mf_kernel, input_operators, output_operator, ir, derivatives);
dop.SetParameters({mesh_nodes});
StopWatch sw;
sw.Start();
for (int i = 0; i < num_samples; i++)
{
dop.Mult(x, y);
}
sw.Stop();
printf("dfem mf: %fs\n", sw.RealTime() / num_samples);
y.HostRead();
dpotential = dop.GetDerivative(Potential, {&f1_g}, {mesh_nodes});
}
dpotential->Mult(x, y);
}
{
auto diffusion_setup_kernel =
[] MFEM_HOST_DEVICE (
const tensor<double, dim, dim>& J,
const double& w)
{
auto invJ = inv(J);
return mfem::tuple{invJ * transpose(invJ) * det(J) * w};
};
constexpr int Potential = 0;
constexpr int Coordinates = 1;
constexpr int QData = 2;
auto input_operators = mfem::tuple{Gradient<Coordinates>{}, Weight{}};
auto output_operator = mfem::tuple{None<QData>{}};
auto solutions = std::vector{FieldDescriptor{Potential, &h1fes}};
auto parameters = std::vector{FieldDescriptor{Coordinates, &mesh_fes},
FieldDescriptor{QData, &qdata_space}};
DifferentiableOperator dop(solutions, parameters, mesh);
dop.AddDomainIntegrator(
diffusion_setup_kernel, input_operators, output_operator, ir);
dop.SetParameters({mesh_nodes, &qdata});
StopWatch sw;
sw.Start();
for (int i = 0; i < num_samples; i++)
{
dop.Mult(x, qdata);
}
sw.Stop();
printf("dfem pa setup: %fs\n", sw.RealTime() / num_samples);
qdata.HostRead();
}
// printf("qdata: ");
// print_vector(qdata);
{
auto diffusion_apply_kernel =
[] MFEM_HOST_DEVICE (
const tensor<real_t, dim>& dudxi,
const tensor<double, dim, dim>& qdata)
{
return mfem::tuple{dudxi * qdata};
};
constexpr int Potential = 0;
constexpr int QData = 1;
auto input_operators = mfem::tuple{Gradient<Potential>{}, None<QData>{}};
auto output_operator = mfem::tuple{Gradient<Potential>{}};
auto solutions = std::vector{FieldDescriptor{Potential, &h1fes}};
auto parameters = std::vector{FieldDescriptor{QData, &qdata_space}};
DifferentiableOperator dop(solutions, parameters, mesh);
dop.AddDomainIntegrator(
diffusion_apply_kernel, input_operators, output_operator, ir);
dop.SetParameters({&qdata});
StopWatch sw;
sw.Start();
for (int i = 0; i < num_samples; i++)
{
dop.Mult(x, y);
}
sw.Stop();
printf("dfem pa apply: %fs\n", sw.RealTime() / num_samples);
y.HostRead();
}
// printf("y: ");
// print_vector(y);
Vector y2(h1fes.TrueVSize());
{
ParBilinearForm a(&h1fes);
auto diff_integ = new DiffusionIntegrator;
diff_integ->SetIntRule(&ir);
a.AddDomainIntegrator(diff_integ);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
OperatorPtr A;
StopWatch sw;
sw.Start();
a.Assemble();
a.Finalize();
Array<int> empty;
a.FormSystemMatrix(empty, A);
sw.Stop();
printf("mfem pa setup: %fs\n", sw.RealTime());
sw.Clear();
sw.Start();
y2 = 0.0;
for (int i = 0; i < num_samples; i++)
{
A->Mult(x, y2);
}
sw.Stop();
printf("mfem pa apply: %fs\n", sw.RealTime() / num_samples);
y2.HostRead();
}
// printf("y2: ");
// print_vector(y2);
Vector diff(y2);
diff -= y;
if (diff.Norml2() > 1e-15)
{
printf("y: ");
print_vector(y);
printf("y2: ");
print_vector(y2);
printf("diff: ");
print_vector(diff);
return 1;
}
// Test linearization here as well
// auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
// if (dFdu->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdu unexpected height of " << dFdu->Height() << "\n";
// return 1;
// }
// dFdu->Mult(x, y);
// y.HostRead();
// a.Mult(x, y2);
// y2.HostRead();
// diff = y2;
// diff -= y;
// if (diff.Norml2() > 1e-10)
// {
// print_vector(diff);
// print_vector(y2);
// print_vector(y);
// return 1;
// }
// // fd jacobian test
// {
// double eps = 1.0e-6;
// Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
// v *= eps;
// xpv += v;
// xmv -= v;
// dop.Mult(xpv, fxpv);
// dop.Mult(xmv, fxmv);
// fxpv -= fxmv;
// fxpv /= (2.0*eps);
// fxpv -= y;
// if (fxpv.Norml2() > eps)
// {
// out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
// return 1;
// }
// }
// f1_g.ProjectCoefficient(f1_c);
// rho_g.ProjectCoefficient(rho_c);
// auto dFdrho = dop.GetDerivativeWrt<1>({&f1_g}, {&rho_g, mesh_nodes});
// if (dFdrho->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdrho unexpected height of " << dFdrho->Height() << "\n";
// return 1;
// }
// dFdrho->Mult(rho_g, y);
// // fd test
// {
// double eps = 1.0e-6;
// Vector v(rho_g), rhopv(rho_g), rhomv(rho_g), frhopv(x.Size()),
// frhomv(x.Size()); v *= eps; rhopv += v; rhomv -= v;
// dop.SetParameters({&rhopv, mesh_nodes});
// dop.Mult(x, frhopv);
// dop.SetParameters({&rhomv, mesh_nodes});
// dop.Mult(x, frhomv);
// frhopv -= frhomv;
// frhopv /= (2.0*eps);
// frhopv -= y;
// if (frhopv.Norml2() > eps)
// {
// out << "||dFdu_FD u^* - ex||_l2 = " << frhopv.Norml2() << "\n";
// return 1;
// }
// }
return 0;
}
DFEM_TEST_MAIN(test_diffusion_3d);
+109
View File
@@ -0,0 +1,109 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_elasticity(std::string mesh_file,
int refinements,
int polynomial_order)
{
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
const int vdim = dim;
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_bdr(mesh.bdr_attributes.Max());
Array<int> ess_tdof;
ess_bdr = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 6 * h1fec.GetOrder());
out << "#qp: " << ir.GetNPoints() << "\n";
out << "#dof_el: " << h1fes.GetRestrictionMatrix()->Height() / mesh.GetNE() <<
"\n";
ParGridFunction u(&h1fes);
auto f1 = [](const Vector& coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = 2.345 + 0.25 * x * x * y + y * y * x;
u(1) = 2.345 - 0.25 * x * y * y + y * x * x;
};
VectorFunctionCoefficient u_c(dim, f1);
u.ProjectCoefficient(u_c);
ConstantCoefficient l_coeff(0.5), m_coeff(0.25);
ParBilinearForm A_form(&h1fes);
auto A_integ = new ElasticityIntegrator(l_coeff, m_coeff);
A_integ->SetIntegrationRule(ir);
A_form.AddDomainIntegrator(A_integ);
A_form.Assemble();
A_form.Finalize();
auto elasticity_kernel = [](const tensor<double, 2, 2> &dudxi,
const tensor<double, 2, 2> &J,
const double &w)
{
constexpr double lambda = 0.5;
constexpr double mu = 0.25;
static constexpr auto I = mfem::internal::IsotropicIdentity<2>();
auto invJ = inv(J);
auto eps = sym(dudxi * invJ);
return mfem::tuple{transpose(lambda * tr(eps) * I + 2.0 * mu * eps) * det(J) * w * transpose(invJ)};
};
mfem::tuple argument_operators{Gradient{"displacement"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator{Gradient{"displacement"}};
ElementOperator op{elasticity_kernel, argument_operators, output_operator};
std::array solutions{FieldDescriptor{&h1fes, "displacement"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop{solutions, parameters, mfem::tuple{op}, mesh, ir};
Vector x(u), y1(h1fes.GetTrueVSize()),
y2(h1fes.GetTrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y1);
y1.HostRead();
A_form.Mult(x, y2);
y2.HostRead();
Vector diff(y2);
diff -= y1;
if (diff.Norml2() > 1e-10)
{
out << "||F(u) - ex||_l2 = " << diff.Norml2() << "\n";
print_vector(diff);
print_vector(y1);
print_vector(y2);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_elasticity);
@@ -0,0 +1,115 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
#include "examples/dfem/dfem_parametricspace.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_interpolate_gradient_linear_scalar_3d(std::string mesh_file,
int refinements,
int polynomial_order)
{
constexpr int dim = 3;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
mesh_serial.Clear();
ParGridFunction *mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
// const IntegrationRule &ir =
// IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
IntegrationRules gll_rules(0, Quadrature1D::GaussLobatto);
const IntegrationRule &ir = gll_rules.Get(h1fes.GetFE(0)->GetGeomType(),
2 * polynomial_order - 1);
ParGridFunction f1_g(&h1fes);
ParametricSpace pspace(dim, dim, ir.GetNPoints(),
dim * ir.GetNPoints() * mesh.GetNE());
ParametricFunction qdata(pspace);
auto kernel = [](const tensor<double, dim> &dudxi,
const tensor<double, dim, dim> &J)
{
return mfem::tuple{dudxi * inv(J)};
};
mfem::tuple argument_operators = {Gradient{"potential"}, Gradient{"coordinates"}};
mfem::tuple output_operator = {None{"qdata"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array
{
FieldDescriptor{&mesh_fes, "coordinates"},
FieldDescriptor{&pspace, "qdata"}
};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
return 2.345 + x * y * z + y * z;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize() * dim);
dop.SetParameters({mesh_nodes, &qdata});
dop.Mult(x, y);
Vector f_test(h1fes.GetElementRestriction(
ElementDofOrdering::LEXICOGRAPHIC)->Height() * dim);
for (int e = 0; e < mesh.GetNE(); e++)
{
ElementTransformation *T = mesh.GetElementTransformation(e);
for (int qp = 0; qp < ir.GetNPoints(); qp++)
{
const IntegrationPoint &ip = ir.IntPoint(qp);
T->SetIntPoint(&ip);
Vector g(dim);
f1_g.GetGradient(*T, g);
// printf("(%f, %f, %f): (%f, %f, %f)\n", ip.x, ip.y, ip.z, g(0), g(1), g(2));
for (int d = 0; d < dim; d++)
{
int qpo = qp * dim;
int eo = e * (ir.GetNPoints() * dim);
f_test(d + qpo + eo) = g(d);
}
}
}
Vector diff(f_test);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(f_test);
print_vector(y);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_interpolate_gradient_linear_scalar_3d);
@@ -0,0 +1,91 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_interpolate_linear_scalar(std::string mesh_file,
int refinements,
int polynomial_order)
{
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
ParGridFunction f1_g(&h1fes);
auto kernel = [](const double &u, const tensor<double, 2, 2> &J,
const double &w)
{
return mfem::tuple{u};
};
mfem::tuple argument_operators = {Value{"potential"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator = {None{"potential"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
return 2.345 + x + y;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
Vector f_test(h1fes.GetElementRestriction(
ElementDofOrdering::LEXICOGRAPHIC)->Height());
for (int e = 0; e < mesh.GetNE(); e++)
{
ElementTransformation *T = mesh.GetElementTransformation(e);
for (int qp = 0; qp < ir.GetNPoints(); qp++)
{
const IntegrationPoint &ip = ir.IntPoint(qp);
T->SetIntPoint(&ip);
f_test((e * ir.GetNPoints()) + qp) = f1_c.Eval(*T, ip);
}
}
Vector diff(f_test);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(f_test);
print_vector(y);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_interpolate_linear_scalar);
@@ -0,0 +1,93 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_interpolate_linear_scalar_3d(std::string mesh_file,
int refinements,
int polynomial_order)
{
constexpr int dim = 3;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
mesh_serial.Clear();
ParGridFunction *mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
ParGridFunction f1_g(&h1fes);
auto kernel = [](const double &u)
{
return mfem::tuple{u};
};
mfem::tuple argument_operators = {Value{"potential"}};
mfem::tuple output_operator = {None{"potential"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
return 2.345 + x + y + 1.25 * z;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
Vector f_test(h1fes.GetElementRestriction(
ElementDofOrdering::LEXICOGRAPHIC)->Height());
for (int e = 0; e < mesh.GetNE(); e++)
{
ElementTransformation *T = mesh.GetElementTransformation(e);
for (int qp = 0; qp < ir.GetNPoints(); qp++)
{
const IntegrationPoint &ip = ir.IntPoint(qp);
T->SetIntPoint(&ip);
f_test((e * ir.GetNPoints()) + qp) = f1_c.Eval(*T, ip);
}
}
Vector diff(f_test);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(f_test);
print_vector(y);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_interpolate_linear_scalar_3d);
@@ -0,0 +1,100 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_interpolate_linear_vector(std::string mesh_file, int refinements,
int polynomial_order)
{
constexpr int vdim = 2;
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
QuadratureSpace qspace(mesh, ir);
QuadratureFunction qf(&qspace, vdim);
ParGridFunction f1_g(&h1fes);
auto kernel = [](const tensor<double, 2> &u)
{
return mfem::tuple{u};
};
mfem::tuple argument_operators = {Value{"potential"}};
mfem::tuple output_operator = {None{"potential"}};
ElementOperator eop{kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = 2.345 + x + y;
u(1) = 12.345 + x + y;
};
VectorFunctionCoefficient f1_c(vdim, f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(f1_g.Size());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
Vector f_test(qf.Size());
for (int e = 0; e < mesh.GetNE(); e++)
{
ElementTransformation *T = mesh.GetElementTransformation(e);
for (int qp = 0; qp < ir.GetNPoints(); qp++)
{
const IntegrationPoint &ip = ir.IntPoint(qp);
T->SetIntPoint(&ip);
Vector f(vdim);
f1_g.GetVectorValue(*T, ip, f);
for (int d = 0; d < vdim; d++)
{
int qpo = qp * vdim;
int eo = e * (ir.GetNPoints() * vdim);
f_test(d + qpo + eo) = f(d);
}
}
}
Vector diff(f_test);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(f_test);
print_vector(y);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_interpolate_linear_vector);
@@ -0,0 +1,105 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_interpolate_linear_vector_3d(std::string mesh_file, int refinements,
int polynomial_order)
{
constexpr int dim = 3;
constexpr int vdim = 3;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
QuadratureSpace qspace(mesh, ir);
QuadratureFunction qf(&qspace, vdim);
ParGridFunction f1_g(&h1fes);
auto kernel = [](const tensor<double, vdim> &u)
{
return mfem::tuple{u};
};
mfem::tuple argument_operators = {Value{"potential"}};
mfem::tuple output_operator = {None{"potential"}};
ElementOperator eop{kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
u(0) = 2.345 + x + y + 3.0 * z;
u(1) = 12.345 + x + y + 2.0 * z;
u(2) = 5.345 + x + y + 1.0 * z;
};
VectorFunctionCoefficient f1_c(vdim, f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(f1_g.Size());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
Vector f_test(qf.Size());
for (int e = 0; e < mesh.GetNE(); e++)
{
ElementTransformation *T = mesh.GetElementTransformation(e);
for (int qp = 0; qp < ir.GetNPoints(); qp++)
{
const IntegrationPoint &ip = ir.IntPoint(qp);
T->SetIntPoint(&ip);
Vector f(vdim);
f1_g.GetVectorValue(*T, ip, f);
for (int d = 0; d < vdim; d++)
{
int qpo = qp * vdim;
int eo = e * (ir.GetNPoints() * vdim);
f_test(d + qpo + eo) = f(d);
}
}
}
Vector diff(f_test);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(f_test);
print_vector(y);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_interpolate_linear_vector_3d);
+113
View File
@@ -0,0 +1,113 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
#include "fem/bilininteg.hpp"
#include "fem/normal_deriv_restriction.hpp"
#include <fstream>
using namespace mfem;
using mfem::internal::tensor;
int dfem_test_mass_scalar_2d(std::string mesh_file,
int refinements,
int polynomial_order)
{
constexpr int dim = 2;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
mesh_serial.Clear();
ParGridFunction *mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() + 1);
// IntegrationRules gll_rules(0, Quadrature1D::GaussLobatto);
// const IntegrationRule &ir = gll_rules.Get(h1fes.GetFE(0)->GetGeomType(),
// 2 * polynomial_order - 1);
printf("#nqp = %d\n", ir.GetNPoints());
printf("#q1d = %d\n", (int)floor(pow(ir.GetNPoints(), 1.0/dim) + 0.5));
ParGridFunction f1_g(&h1fes);
auto kernel = [](const double& u,
const tensor<double, dim> x,
const tensor<double, dim, dim> J,
const double& w)
{
out << x << ": " << u << "\n";
return mfem::tuple{u * w * det(J)};
};
mfem::tuple argument_operators = {Value{"potential"}, Value{"coordinates"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator = {Value{"potential"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
return 2.345 + x + x*y + 1.25 * x;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector f1_g_e(f1_g.Size());
auto R = h1fes.GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
// R->Mult(f1_g, f1_g_e);
auto r_out = std::ofstream("r_mat.mtx");
R->PrintMatlab(r_out);
r_out.close();
print_vector(f1_g);
// print_vector(f1_g_e);
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
ParBilinearForm a(&h1fes);
auto mass_integ = new MassIntegrator;
mass_integ->SetIntRule(&ir);
a.AddDomainIntegrator(mass_integ);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a.Assemble();
a.Finalize();
Vector y2(h1fes.TrueVSize());
a.Mult(x, y2);
y2.HostRead();
Vector diff(y2);
diff -= y;
if (diff.Norml2() > 1e-10)
{
print_vector(diff);
print_vector(y2);
print_vector(y);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(dfem_test_mass_scalar_2d);
+147
View File
@@ -0,0 +1,147 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
#include "fem/bilininteg.hpp"
#include "fem/fe/fe_base.hpp"
using namespace mfem;
using mfem::internal::tensor;
int dfem_test_mass_scalar_3d(std::string mesh_file,
int refinements,
int polynomial_order)
{
constexpr int dim = 3;
Mesh mesh_serial = Mesh(mesh_file);
MFEM_ASSERT(mesh_serial.Dimension() == dim, "wrong mesh dimension");
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(polynomial_order);
mesh_serial.Clear();
ParGridFunction *mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
0)->GetDim() - 1);
// IntegrationRules gll_rules(0, Quadrature1D::GaussLobatto);
// const IntegrationRule &ir = gll_rules.Get(h1fes.GetFE(0)->GetGeomType(),
// 2 * polynomial_order - 1);
auto dtq = h1fes.GetFE(0)->GetDofToQuad(ir, DofToQuad::TENSOR);
// printf("\n B: ");
// dtq.B.Print(out, dtq.B.Size());
// printf("\n G: ");
// dtq.G.Print(out, dtq.G.Size());
// printf("\n w: ");
// ir.GetWeights().Print(out, ir.GetWeights().Size());
// printf("#ndof per el = %d\n", h1fes.GetFE(0)->GetDof());
// printf("#nqp = %d\n", ir.GetNPoints());
// printf("#q1d = %d\n", (int)floor(pow(ir.GetNPoints(), 1.0/dim) + 0.5));
// printf("nodes: ");
// print_vector(*mesh_nodes);
ParGridFunction f1_g(&h1fes);
auto kernel = [](const double &u,
const tensor<double, dim, dim> &J,
const double &w)
{
return mfem::tuple{u * det(J) * w};
};
mfem::tuple argument_operators = {Value{"potential"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator = {Value{"potential"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
return 2.345 + x + x*y + 1.25 * z*x;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
// printf("\nf1_g: ");
// print_vector(f1_g);
auto R = h1fes.GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
// Vector f1_g_e(R->Height());
// R->Mult(f1_g, f1_g_e);
// printf("\nf1_g_e: ");
// print_vector(f1_g_e);
// auto r_out = std::ofstream("r_mat.mtx");
// R->PrintMatlab(r_out);
// r_out.close();
Vector x(*f1_g.GetTrueDofs()), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
ParBilinearForm a(&h1fes);
auto mass_integ = new MassIntegrator;
mass_integ->SetIntRule(&ir);
a.AddDomainIntegrator(mass_integ);
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a.Assemble();
a.Finalize();
Vector y2(h1fes.TrueVSize());
a.Mult(x, y2);
y2.HostRead();
Vector diff(y2);
diff -= y;
if (diff.Norml2() > 1e-15)
{
printf("y ");
print_vector(y);
printf("y2: ");
print_vector(y2);
printf("diff: ");
print_vector(diff);
return 1;
}
Vector y3(h1fes.TrueVSize());
auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
dFdu->Mult(x, y3);
diff = y2;
diff -= y;
if (diff.Norml2() > 1e-15)
{
printf("y2 ");
print_vector(y2);
printf("y3: ");
print_vector(y3);
printf("diff: ");
print_vector(diff);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(dfem_test_mass_scalar_3d);
@@ -0,0 +1,114 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_neo_hookean_elasticity_2d(
std::string mesh_file, int refinements, int polynomial_order)
{
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
MFEM_ASSERT(dim == 2, "This test is for 2D meshes only");
mesh_serial.Clear();
out << "#el: " << mesh.GetNE() << "\n";
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, dim);
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder());
out << "#qp: " << ir.GetNPoints() << "\n";
ParGridFunction u_g(&h1fes);
auto kernel = [] MFEM_HOST_DEVICE(const tensor<double, 2, 2>& J,
const double& w,
const tensor<double, 2, 2>& dudxi)
{
// Neo-Hookean parameters
const double lambda = 1.0;
const double mu = 0.5;
static constexpr auto I = mfem::internal::IsotropicIdentity<2>();
auto F = I + (dudxi * inv(J));
auto E = 0.5 * (transpose(F) * F - I);
auto invF = inv(F);
// 2D plane strain formulation
auto P = mu * (F - transpose(invF)) + lambda * log(det(F)) * transpose(invF);
return mfem::tuple{P * det(J) * w};
};
mfem::tuple argument_operators = {Gradient{"coordinates"}, Weight{},
Gradient{"displacement"}
};
mfem::tuple output_operator = {Gradient{"displacement"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "displacement"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto displacement = [](const Vector& coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = 0.1 * x * y;
u(1) = 0.1 * y * x;
};
VectorFunctionCoefficient disp_coeff(2, displacement);
u_g.ProjectCoefficient(disp_coeff);
Vector x(u_g), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
y.HostRead();
// Test linearization
auto dFdu = dop.GetDerivativeWrt<0>({&u_g}, {mesh_nodes});
dFdu->Mult(x, y);
// Finite difference Jacobian test
{
double eps = 1.0e-6;
Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
v *= eps;
xpv += v;
xmv -= v;
dop.Mult(xpv, fxpv);
dop.Mult(xmv, fxmv);
fxpv -= fxmv;
fxpv /= (2.0*eps);
fxpv -= y;
if (fxpv.Norml2() > eps)
{
out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
return 1;
}
}
return 0;
}
DFEM_TEST_MAIN(test_neo_hookean_elasticity_2d);
@@ -0,0 +1,169 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_nonlinear_diffusion(
std::string mesh_file, int refinements, int polynomial_order)
{
constexpr int dim = 3;
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
mesh_serial.Clear();
out << "#el: " << mesh.GetNE() << "\n";
ParGridFunction* mesh_nodes = static_cast<ParGridFunction*>(mesh.GetNodes());
ParFiniteElementSpace& mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
out << "#dofs " << h1fes.GetTrueVSize() << "\n";
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
0)->GetDim() - 1);
out << "#qp: " << ir.GetNPoints() << "\n";
ParGridFunction f1_g(&h1fes);
bool inactive_derivative = false;
auto kernel = [] MFEM_HOST_DEVICE(
const tensor<double, dim, dim>& J,
const double& w,
const tensor<double, dim>& dudxi,
const double& u)
{
auto invJ = inv(J);
return mfem::tuple{(u * u) * dudxi * invJ * transpose(invJ) * det(J) * w};
};
mfem::tuple argument_operators =
{
Gradient{"coordinates"},
Weight{},
Gradient{"potential"},
Value{"potential"}
};
mfem::tuple output_operator =
{
Gradient{"potential"}
};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = mfem::tuple{eop};
auto solutions = std::array
{
FieldDescriptor{&h1fes, "potential"}
};
auto parameters = std::array
{
FieldDescriptor{&mesh_fes, "coordinates"}
};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector& coords)
{
const double x = coords(0);
const double y = coords(1);
const double z = coords(2);
return 2.345 + 0.25 * x * x * y + y * y * x + z;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(h1fes.TrueVSize());
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
y.HostRead();
ParBilinearForm a(&h1fes);
GridFunctionCoefficient f1gc(&f1_g);
TransformedCoefficient tf_c(&f1gc, [](double f) { return f * f; });
a.AddDomainIntegrator(new DiffusionIntegrator(tf_c));
a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
a.Assemble();
a.Finalize();
Vector y2(h1fes.TrueVSize()), diff(h1fes.TrueVSize());
a.Mult(x, y2);
y2.HostRead();
diff = y2;
diff -= y;
if (diff.Norml2() > 1e-10)
{
out << "||F(u) - ex||_l2 = " << diff.Norml2() << "\n";
print_vector(diff);
print_vector(y);
print_vector(y2);
return 1;
}
// Test linearization here as well
auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {mesh_nodes});
dFdu->Mult(x, y);
// fd jacobian test
{
double eps = 1.0e-6;
Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
v *= eps;
xpv += v;
xmv -= v;
dop.Mult(xpv, fxpv);
dop.Mult(xmv, fxmv);
fxpv -= fxmv;
fxpv /= (2.0*eps);
fxpv -= y;
if (fxpv.Norml2() > eps)
{
out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
return 1;
}
}
// ParBilinearForm da(&h1fes);
// TransformedCoefficient dtf_c(&f1gc, [](double f) { return 2.0 * f; });
// da.AddDomainIntegrator(new DiffusionIntegrator(dtf_c));
// da.SetAssemblyLevel(AssemblyLevel::PARTIAL);
// da.Assemble();
// da.Finalize();
// if (dFdu->Height() != h1fes.GetTrueVSize())
// {
// out << "dFdu unexpected height of " << dFdu->Height() << "\n";
// return 1;
// }
// dFdu->Mult(x, y);
// print_vector(y);
// da.Mult(x, y2);
// print_vector(y2);
// y2 -= y;
// out << "||dFdu x - A x||_l2 = " << y2.Norml2() << "\n";
// if (y2.Norml2() > 1e-10)
// {
// out << "||dFdu u^* - ex||_l2 = " << y2.Norml2() << "\n";
// }
return 0;
}
DFEM_TEST_MAIN(test_nonlinear_diffusion);
@@ -0,0 +1,267 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
#include <fstream>
using namespace mfem;
using mfem::internal::tensor;
using mfem::internal::dual;
class FDJacobian : public Operator
{
public:
FDJacobian(const Operator &op, const Vector &x) :
Operator(op.Height()),
op(op),
x(x)
{
f.SetSize(Height());
xpev.SetSize(Height());
op.Mult(x, f);
xnorm = x.Norml2();
}
void Mult(const Vector &v, Vector &y) const override
{
x.HostRead();
// See [1] for choice of eps.
//
// [1] Woodward, C.S., Gardner, D.J. and Evans, K.J., 2015. On the use of
// finite difference matrix-vector products in Newton-Krylov solvers for
// implicit climate dynamics with spectral elements. Procedia Computer
// Science, 51, pp.2036-2045.
real_t eps = lambda * (lambda + xnorm / v.Norml2());
for (int i = 0; i < x.Size(); i++)
{
xpev(i) = x(i) + eps * v(i);
}
// y = f(x + eps * v)
op.Mult(xpev, y);
// y = (f(x + eps * v) - f(x)) / eps
for (int i = 0; i < x.Size(); i++)
{
y(i) = (y(i) - f(i)) / eps;
}
}
virtual MemoryClass GetMemoryClass() const override
{
return Device::GetDeviceMemoryClass();
}
private:
const Operator &op;
Vector x, f;
mutable Vector xpev;
real_t lambda = 1.0e-6;
real_t xnorm;
};
template <typename elasticity_t>
class ElasticityOperator : public Operator
{
template <typename elasticity_du_t>
class ElasticityJacobianOperator : public Operator
{
public:
ElasticityJacobianOperator(const ElasticityOperator *elasticity,
std::shared_ptr<elasticity_du_t> dRdu) :
Operator(elasticity->Height()),
elasticity(elasticity),
dRdu(dRdu),
x_ess(dRdu->Height())
{
}
void Mult(const Vector &x, Vector &y) const override
{
x_ess = x;
x_ess.SetSubVector(elasticity->ess_tdofs, 0.0);
dRdu->Mult(x_ess, y);
for (int i = 0; i < elasticity->ess_tdofs.Size(); i++)
{
y[elasticity->ess_tdofs[i]] = x[elasticity->ess_tdofs[i]];
}
}
const ElasticityOperator *elasticity = nullptr;
std::shared_ptr<elasticity_du_t> dRdu;
mutable Vector x_ess;
};
public:
ElasticityOperator(ParFiniteElementSpace &fes, elasticity_t &elasticity,
Array<int> &ess_tdofs) :
Operator(fes.GetTrueVSize()),
fes(fes),
elasticity(elasticity),
ess_tdofs(ess_tdofs) {}
void Mult(const Vector &x, Vector &r) const override
{
elasticity.Mult(x, r);
r.SetSubVector(ess_tdofs, 0.0);
}
Operator &GetGradient(const Vector &x) const override
{
ParGridFunction u(const_cast<ParFiniteElementSpace *>
(*std::get_if<const ParFiniteElementSpace *>
(&elasticity.solutions[0].data)));
u.SetFromTrueDofs(x);
auto dRdu = elasticity.template GetDerivativeWrt<0>({&u}, {mesh_nodes});
jacobian.reset(
new ElasticityJacobianOperator<
typename std::remove_pointer<decltype(dRdu.get())>::type> (this, dRdu));
// jacobian.reset(new FDJacobian(*this, x));
return *jacobian;
}
void SetParameters(ParGridFunction &mesh_nodes)
{
elasticity.SetParameters({&mesh_nodes});
this->mesh_nodes = &mesh_nodes;
}
ParFiniteElementSpace &fes;
elasticity_t &elasticity;
Array<int> ess_tdofs;
mutable ParGridFunction *mesh_nodes = nullptr;
mutable std::shared_ptr<Operator> jacobian;
};
int test_nonlinear_elasticity_3d(std::string mesh_file,
int refinements,
int polynomial_order)
{
constexpr int dim = 3;
constexpr int vdim = dim;
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(polynomial_order);
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_tdof_list, ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 0;
ess_bdr[0] = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
const IntegrationRule& ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(),
h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(0)->GetOrder() + h1fes.GetFE(
0)->GetDim() - 1);
out << "#qp: " << ir.GetNPoints() << "\n";
out << "#dof: " << h1fes.GetNDofs() << "\n";
ParGridFunction u(&h1fes);
auto elasticity_kernel = [] MFEM_HOST_DEVICE
(const tensor<dual<real_t, real_t>, dim, dim> &dudxi,
const tensor<real_t, dim, dim> &J,
const real_t &w)
{
// shear modulus
real_t D1{0.1e6};
// bulk modulus
real_t C1{1.0e6};
constexpr auto I = mfem::internal::IsotropicIdentity<dim>();
auto invJ = inv(J);
auto dudx = dudxi * invJ;
auto F = det(I + dudx);
auto p = -2.0 * D1 * F * (F - 1);
auto devB = dev(dudx + transpose(dudx) + dot(dudx, transpose(dudx)));
auto sigma = -(p / F) * I + 2.0 * (C1 / pow(F, 5.0 / 3.0)) * devB;
return mfem::tuple{sigma * det(J) * w * transpose(invJ)};
};
mfem::tuple argument_operators{Gradient{"displacement"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator{Gradient{"displacement"}};
// B^T D(B0*dudxi, B1*J, B2*w)
ElementOperator op(elasticity_kernel, argument_operators, output_operator, ir);
std::array solutions{FieldDescriptor{&h1fes, "displacement"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, mfem::tuple{op}, mesh,
AutoDiff::NativeDualNumber{});
ElasticityOperator elasticity(h1fes, dop, ess_tdof_list);
VectorArrayCoefficient f(dim);
for (int i = 0; i < dim-1; i++)
{
f.Set(i, new ConstantCoefficient(0.0));
}
{
Vector pull_force(mesh.bdr_attributes.Max());
pull_force = 0.0;
pull_force(1) = -1.0e-2;
f.Set(dim-1, new PWConstCoefficient(pull_force));
}
ParLinearForm b(&h1fes);
b.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
b.UseFastAssembly(true);
b.Assemble();
auto B = b.ParallelAssemble();
Vector X = u.GetTrueVector();
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-8);
cg.SetMaxIter(1000);
cg.SetPrintLevel(IterativeSolver::PrintLevel().Summary());
NewtonSolver newton(MPI_COMM_WORLD);
newton.SetSolver(cg);
newton.SetOperator(elasticity);
newton.SetRelTol(1e-6);
newton.SetMaxIter(100);
// newton.SetAdaptiveLinRtol();
newton.SetPrintLevel(IterativeSolver::PrintLevel().Iterations());
elasticity.SetParameters(*mesh_nodes);
// Vector zero;
newton.Mult(*B, X);
u.SetFromTrueDofs(X);
ParaViewDataCollection paraview_dc("dfem", &mesh);
paraview_dc.SetPrefixPath("ParaView");
paraview_dc.SetLevelsOfDetail(polynomial_order);
paraview_dc.SetDataFormat(VTKFormat::BINARY);
paraview_dc.SetHighOrderOutput(true);
paraview_dc.SetCycle(0);
paraview_dc.SetTime(0.0);
paraview_dc.RegisterField("displacement", &u);
paraview_dc.Save();
return 0;
}
DFEM_TEST_MAIN(test_nonlinear_elasticity_3d);
+82
View File
@@ -0,0 +1,82 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
#include "fem/coefficient.hpp"
#include "fem/pgridfunc.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_ordering(std::string mesh_file,
int refinements,
int polynomial_order)
{
constexpr int dim = 2;
constexpr int vdim = dim;
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(polynomial_order);
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
const IntegrationRule &ir =
IntRules.Get(mesh_fes.GetFE(0)->GetGeomType(),
2 * mesh_fes.FEColl()->GetOrder() - 1);
for (int q = 0; q < ir.GetNPoints(); q++)
{
out << "(" << ir.IntPoint(q).x << ", " << ir.IntPoint(q).y << ")\n";
}
ParGridFunction u(&mesh_fes);
auto f = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = x*x*y + 1.0;
u(1) = y*y*x*x + 2.0;
};
VectorFunctionCoefficient uc(dim, f);
u.ProjectCoefficient(uc);
auto kernel = [](const tensor<double, dim> &xi,
const tensor<double, vdim, dim> &J,
const tensor<double, dim> &u,
const tensor<double, vdim, dim> &dudxi)
{
out << "xi: " << xi << "\n";
out << "J: " << J << "\n";
out << "u: " << u << "\n";
out << "dudxi: " << dudxi << "\n\n";
return mfem::tuple{J};
};
mfem::tuple argument_operators{Value{"coordinates"}, Gradient{"coordinates"}, Value{"potential"}, Gradient{"potential"}};
mfem::tuple output_operator{Gradient{"potential"}};
ElementOperator op{kernel, argument_operators, output_operator};
std::array solutions{FieldDescriptor{&mesh_fes, "potential"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop{solutions, parameters, mfem::tuple{op}, mesh, ir};
Vector y(u);
dop.SetParameters({mesh_nodes});
dop.Mult(u, y);
print_vector(y);
return 0;
}
DFEM_TEST_MAIN(test_ordering);
+102
View File
@@ -0,0 +1,102 @@
#include "dfem/dfem.hpp"
#include "dfem/dfem_test_macro.hpp"
using namespace mfem;
using mfem::internal::tensor;
int test_vector_diffusion(std::string mesh_file,
int refinements,
int polynomial_order)
{
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
const int vdim = dim;
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, vdim);
Array<int> ess_bdr(mesh.bdr_attributes.Max());
Array<int> ess_tdof;
ess_bdr = 1;
h1fes.GetEssentialTrueDofs(ess_bdr, ess_tdof);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder() - 1);
ParGridFunction u(&h1fes);
auto f1 = [](const Vector& coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = 2.345 + 0.25 * x * x * y + y * y * x;
u(1) = 2.345 - 0.25 * x * y * y + y * x * x;
};
VectorFunctionCoefficient u_c(dim, f1);
u.ProjectCoefficient(u_c);
auto vector_diffusion_kernel = [](const tensor<double, 2> &xi,
const tensor<double, 2, 2> &dudxi,
const tensor<double, 2, 2> &J,
const double &w)
{
out << "xi: " << xi << "\n";
out << "dudxi: " << dudxi << "\n";
return mfem::tuple{dudxi * inv(J) * det(J) * w * transpose(inv(J))};
// return mfem::tuple{dudxi};
};
mfem::tuple argument_operators{Value{"coordinates"}, Gradient{"potential"}, Gradient{"coordinates"}, Weight{}};
mfem::tuple output_operator{Gradient{"potential"}};
ElementOperator op{vector_diffusion_kernel, argument_operators, output_operator};
std::array solutions{FieldDescriptor{&h1fes, "potential"}};
std::array parameters{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop{solutions, parameters, mfem::tuple{op}, mesh, ir};
Vector x(u), y1(h1fes.GetTrueVSize()),
y2(h1fes.GetTrueVSize());
ParBilinearForm A_form(&h1fes);
auto A_integ = new VectorDiffusionIntegrator(vdim);
A_integ->SetIntegrationRule(ir);
A_form.AddDomainIntegrator(A_integ);
A_form.Assemble();
A_form.Finalize();
dop.SetParameters({mesh_nodes});
dop.Mult(x, y1);
y1.HostRead();
A_form.Mult(x, y2);
y2.HostRead();
Vector diff(y2);
diff -= y1;
if (diff.Norml2() > 1e-10)
{
out << "||F(u) - ex||_l2 = " << diff.Norml2() << "\n";
print_vector(diff);
print_vector(y1);
print_vector(y2);
return 1;
}
return 0;
}
DFEM_TEST_MAIN(test_vector_diffusion);
+122
View File
@@ -0,0 +1,122 @@
#include <tuple>
#include <type_traits>
#include <iostream>
#include <enzyme/enzyme>
template <typename T>
constexpr auto get_type_name() -> std::string_view
{
#if defined(__clang__)
constexpr auto prefix = std::string_view {"[T = "};
constexpr auto suffix = "]";
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(__GNUC__)
constexpr auto prefix = std::string_view {"with T = "};
constexpr auto suffix = "; ";
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(_MSC_VER)
constexpr auto prefix = std::string_view {"get_type_name<"};
constexpr auto suffix = ">(void)";
constexpr auto function = std::string_view{__FUNCSIG__};
#else
#error Unsupported compiler
#endif
const auto start = function.find(prefix) + prefix.size();
const auto end = function.find(suffix);
const auto size = end - start;
return function.substr(start, size);
}
template <typename ... Ts>
constexpr auto decay_types(std::tuple<Ts...> const &)
-> std::tuple<std::remove_cv_t<std::remove_reference_t<Ts>>...>;
template <typename T>
using decay_tuple = decltype(decay_types(std::declval<T>()));
template <class F> struct FunctionSignature;
template <typename output_t, typename... input_ts>
struct FunctionSignature<output_t(input_ts...)>
{
using return_t = output_t;
using parameter_ts = std::tuple<input_ts...>;
};
template <class T> struct create_function_signature;
template <typename output_t, typename T, typename... input_ts>
struct create_function_signature<output_t (T::*)(input_ts...) const>
{
using type = FunctionSignature<output_t(input_ts...)>;
};
template <typename arg_ts, std::size_t... Is>
auto create_enzyme_args(arg_ts &args,
arg_ts &shadow_args,
std::index_sequence<Is...>)
{
// (std::cout << ... << std::get<Is>(shadow_args));
return std::tuple<enzyme::Duplicated<decltype(std::get<Is>(args))>...>
{
{ std::get<Is>(args), std::get<Is>(shadow_args) }...
};
}
template <typename kernel_t, typename arg_ts>
auto fwddiff_apply_enzyme(kernel_t kernel, arg_ts &&args, arg_ts &&shadow_args)
{
auto arg_indices =
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<arg_ts>>> {};
auto enzyme_args = create_enzyme_args(args, shadow_args, arg_indices);
// using kf_return_t = typename create_function_signature<
// decltype(&kernel_t::operator())>::type::return_t;
std::cout << "\n";
std::cout << "args is " << get_type_name<decltype(args)>() << "\n\n";
std::cout << "enzyme_args type is " << get_type_name<decltype(enzyme_args)>() <<
"\n\n";
// std::cout << "return type is " << get_type_name<decltype(kf_return_t{})>() <<
// "\n\n";
std::cout << "args " << std::get<0>(args) << "\n";
std::cout << "shadow args " << std::get<0>(shadow_args) << "\n";
return std::apply([&](auto &&...args)
{
// std::cout << enzyme::autodiff<enzyme::Forward>(+kernel, args...) << "\n";
return enzyme::get<0>
(enzyme::autodiff<enzyme::Forward>(+kernel, args...));
},
enzyme_args);
}
int main()
{
auto func = [](const double &x, double &y)
{
std::cout << "func( x = " << x << " )\n";
return x*x;
};
using kf_param_ts = typename create_function_signature<
decltype(&decltype(func)::operator())>::type::parameter_ts;
using kf_output_t = typename create_function_signature<
decltype(&decltype(func)::operator())>::type::return_t;
auto kernel_args = decay_tuple<kf_param_ts> {};
auto kernel_shadow_args = decay_tuple<kf_param_ts> {};
std::get<0>(kernel_args) = 3;
std::get<0>(kernel_shadow_args) = 1;
auto dx = fwddiff_apply_enzyme(func, kernel_args, kernel_shadow_args);
std::cout << "dfdx = " << dx << "\n";
return 0;
}
+8 -8
View File
@@ -87,16 +87,16 @@ public:
real_t visc, real_t mu, real_t K);
/// Compute the right-hand side of the ODE system.
void Mult(const Vector &vx, Vector &dvx_dt) const override;
virtual void Mult(const Vector &vx, Vector &dvx_dt) const;
/** Solve the Backward-Euler equation: k = f(x + dt*k, t), for the unknown k.
This is the only requirement for high-order SDIRK implicit integration.*/
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override;
virtual void ImplicitSolve(const real_t dt, const Vector &x, Vector &k);
real_t ElasticEnergy(const Vector &x) const;
real_t KineticEnergy(const Vector &v) const;
void GetElasticEnergyDensity(const GridFunction &x, GridFunction &w) const;
~HyperelasticOperator() override;
virtual ~HyperelasticOperator();
};
/** Nonlinear operator of the form:
@@ -120,12 +120,12 @@ public:
void SetParameters(real_t dt_, const Vector *v_, const Vector *x_);
/// Compute y = H(x + dt (v + dt k)) + M k + S (v + dt k).
void Mult(const Vector &k, Vector &y) const override;
virtual void Mult(const Vector &k, Vector &y) const;
/// Compute J = M + dt S + dt^2 grad_H(x + dt (v + dt k)).
Operator &GetGradient(const Vector &k) const override;
virtual Operator &GetGradient(const Vector &k) const;
~ReducedSystemOperator() override;
virtual ~ReducedSystemOperator();
};
@@ -141,8 +141,8 @@ private:
public:
ElasticEnergyCoefficient(HyperelasticModel &m, const GridFunction &x_)
: model(m), x(x_) { }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
~ElasticEnergyCoefficient() override { }
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip);
virtual ~ElasticEnergyCoefficient() { }
};
void InitialDeformation(const Vector &x, Vector &y);
+8 -8
View File
@@ -89,17 +89,17 @@ public:
real_t visc, real_t mu, real_t K);
/// Compute the right-hand side of the ODE system.
void Mult(const Vector &vx, Vector &dvx_dt) const override;
virtual void Mult(const Vector &vx, Vector &dvx_dt) const;
/** Solve the Backward-Euler equation: k = f(x + dt*k, t), for the unknown k.
This is the only requirement for high-order SDIRK implicit integration.*/
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override;
virtual void ImplicitSolve(const real_t dt, const Vector &x, Vector &k);
real_t ElasticEnergy(const ParGridFunction &x) const;
real_t KineticEnergy(const ParGridFunction &v) const;
void GetElasticEnergyDensity(const ParGridFunction &x,
ParGridFunction &w) const;
~HyperelasticOperator() override;
virtual ~HyperelasticOperator();
};
/** Nonlinear operator of the form:
@@ -125,12 +125,12 @@ public:
void SetParameters(real_t dt_, const Vector *v_, const Vector *x_);
/// Compute y = H(x + dt (v + dt k)) + M k + S (v + dt k).
void Mult(const Vector &k, Vector &y) const override;
virtual void Mult(const Vector &k, Vector &y) const;
/// Compute J = M + dt S + dt^2 grad_H(x + dt (v + dt k)).
Operator &GetGradient(const Vector &k) const override;
virtual Operator &GetGradient(const Vector &k) const;
~ReducedSystemOperator() override;
virtual ~ReducedSystemOperator();
};
@@ -146,8 +146,8 @@ private:
public:
ElasticEnergyCoefficient(HyperelasticModel &m, const ParGridFunction &x_)
: model(m), x(x_) { }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
~ElasticEnergyCoefficient() override { }
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip);
virtual ~ElasticEnergyCoefficient() { }
};
void InitialDeformation(const Vector &x, Vector &y);
+1 -1
View File
@@ -53,7 +53,7 @@ public:
pmesh(pmesh_),
pgf(pgf_) {}
void MonitorSolution(int i, real_t norm, const Vector &x, bool final) override
void MonitorSolution(int i, real_t norm, const Vector &x, bool final)
{
char vishost[] = "localhost";
int visport = 19916;
+3 -3
View File
@@ -76,15 +76,15 @@ public:
ConductionOperator(FiniteElementSpace &f, real_t alpha, real_t kappa,
const Vector &u);
void Mult(const Vector &u, Vector &du_dt) const override;
virtual void Mult(const Vector &u, Vector &du_dt) const;
/** Solve the Backward-Euler equation: k = f(u + dt*k, t), for the unknown k.
This is the only requirement for high-order SDIRK implicit integration.*/
void ImplicitSolve(const real_t dt, const Vector &u, Vector &k) override;
virtual void ImplicitSolve(const real_t dt, const Vector &u, Vector &k);
/// Update the diffusion BilinearForm K using the given true-dof vector `u`.
void SetParameters(const Vector &u);
~ConductionOperator() override;
virtual ~ConductionOperator();
};
real_t InitialTemperature(const Vector &x);
+3 -3
View File
@@ -78,15 +78,15 @@ public:
ConductionOperator(ParFiniteElementSpace &f, real_t alpha, real_t kappa,
const Vector &u);
void Mult(const Vector &u, Vector &du_dt) const override;
virtual void Mult(const Vector &u, Vector &du_dt) const;
/** Solve the Backward-Euler equation: k = f(u + dt*k, t), for the unknown k.
This is the only requirement for high-order SDIRK implicit integration.*/
void ImplicitSolve(const real_t dt, const Vector &u, Vector &k) override;
virtual void ImplicitSolve(const real_t dt, const Vector &u, Vector &k);
/// Update the diffusion BilinearForm K using the given true-dof vector `u`.
void SetParameters(const Vector &u);
~ConductionOperator() override;
virtual ~ConductionOperator();
};
real_t InitialTemperature(const Vector &x);
+2 -2
View File
@@ -69,7 +69,7 @@ public:
void SetDisplacement(GridFunction &u_) { u = &u_; }
void SetComponent(int i, int j) { si = i; sj = j; }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip);
};
// Simple GLVis visualization manager.
@@ -89,7 +89,7 @@ public:
void NewWindow();
void CloseConnection();
void PositionWindow();
~VisMan() override;
virtual ~VisMan();
};
// Manipulators for the GLVis visualization manager.
+2 -2
View File
@@ -69,7 +69,7 @@ public:
void SetDisplacement(GridFunction &u_) { u = &u_; }
void SetComponent(int i, int j) { si = i; sj = j; }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip);
};
// Simple GLVis visualization manager.
@@ -89,7 +89,7 @@ public:
void NewWindow();
void CloseConnection();
void PositionWindow();
~VisMan() override;
virtual ~VisMan();
};
// Manipulators for the GLVis visualization manager.
+7 -7
View File
@@ -48,7 +48,7 @@ public:
print_level = print_lvl;
}
void MonitorResidual(int it, real_t norm, const Vector &r, bool final) override;
virtual void MonitorResidual(int it, real_t norm, const Vector &r, bool final);
private:
const std::string prefix;
@@ -116,10 +116,10 @@ public:
JacobianPreconditioner(Array<FiniteElementSpace *> &fes,
SparseMatrix &mass, Array<int> &offsets);
void Mult(const Vector &k, Vector &y) const override;
void SetOperator(const Operator &op) override;
virtual void Mult(const Vector &k, Vector &y) const;
virtual void SetOperator(const Operator &op);
~JacobianPreconditioner() override;
virtual ~JacobianPreconditioner();
};
// After spatial discretization, the rubber model can be written as:
@@ -161,13 +161,13 @@ public:
int iter, Coefficient &mu);
// Required to use the native newton solver
Operator &GetGradient(const Vector &xp) const override;
void Mult(const Vector &k, Vector &y) const override;
virtual Operator &GetGradient(const Vector &xp) const;
virtual void Mult(const Vector &k, Vector &y) const;
// Driver for the newton solver
void Solve(Vector &xp) const;
~RubberOperator() override;
virtual ~RubberOperator();
};
// Visualization driver
+7 -7
View File
@@ -62,7 +62,7 @@ public:
#endif
}
void MonitorResidual(int it, real_t norm, const Vector &r, bool final) override;
virtual void MonitorResidual(int it, real_t norm, const Vector &r, bool final);
private:
const std::string prefix;
@@ -130,10 +130,10 @@ public:
JacobianPreconditioner(Array<ParFiniteElementSpace *> &fes,
Operator &mass, Array<int> &offsets);
void Mult(const Vector &k, Vector &y) const override;
void SetOperator(const Operator &op) override;
virtual void Mult(const Vector &k, Vector &y) const;
virtual void SetOperator(const Operator &op);
~JacobianPreconditioner() override;
virtual ~JacobianPreconditioner();
};
// After spatial discretization, the rubber model can be written as:
@@ -175,13 +175,13 @@ public:
int iter, Coefficient &mu);
// Required to use the native newton solver
Operator &GetGradient(const Vector &xp) const override;
void Mult(const Vector &k, Vector &y) const override;
virtual Operator &GetGradient(const Vector &xp) const;
virtual void Mult(const Vector &k, Vector &y) const;
// Driver for the newton solver
void Solve(Vector &xp) const;
~RubberOperator() override;
virtual ~RubberOperator();
};
// Visualization driver
+2 -2
View File
@@ -79,14 +79,14 @@ class GradT : public Operator
{
public:
GradT() : Operator(1) {}
void Mult(const Vector &x, Vector &y) const override { y.Set(1.0/m_, x); }
void Mult(const Vector &x, Vector &y) const { y.Set(1.0/m_, x); }
};
class NegGradV : public TimeDependentOperator
{
public:
NegGradV() : TimeDependentOperator(1) {}
void Mult(const Vector &x, Vector &y) const override;
void Mult(const Vector &x, Vector &y) const;
};
int main(int argc, char *argv[])
+2 -2
View File
@@ -84,14 +84,14 @@ class GradT : public Operator
{
public:
GradT() : Operator(1) {}
void Mult(const Vector &x, Vector &y) const override { y.Set(1.0/m_, x); }
void Mult(const Vector &x, Vector &y) const { y.Set(1.0/m_, x); }
};
class NegGradV : public TimeDependentOperator
{
public:
NegGradV() : TimeDependentOperator(1) {}
void Mult(const Vector &x, Vector &y) const override;
void Mult(const Vector &x, Vector &y) const;
};
int main(int argc, char *argv[])
+5 -5
View File
@@ -61,20 +61,20 @@ public:
WaveOperator(FiniteElementSpace &f, Array<int> &ess_bdr, real_t speed);
using SecondOrderTimeDependentOperator::Mult;
void Mult(const Vector &u, const Vector &du_dt,
Vector &d2udt2) const override;
virtual void Mult(const Vector &u, const Vector &du_dt,
Vector &d2udt2) const;
/** Solve the Backward-Euler equation:
d2udt2 = f(u + fac0*d2udt2,dudt + fac1*d2udt2, t),
for the unknown d2udt2. */
using SecondOrderTimeDependentOperator::ImplicitSolve;
void ImplicitSolve(const real_t fac0, const real_t fac1,
const Vector &u, const Vector &dudt, Vector &d2udt2) override;
virtual void ImplicitSolve(const real_t fac0, const real_t fac1,
const Vector &u, const Vector &dudt, Vector &d2udt2);
///
void SetParameters(const Vector &u);
~WaveOperator() override;
virtual ~WaveOperator();
};
+2 -2
View File
@@ -103,8 +103,8 @@ public:
using VectorCoefficient::Eval;
void Eval(Vector &K, ElementTransformation &T,
const IntegrationPoint &ip) override
virtual void Eval(Vector &K, ElementTransformation &T,
const IntegrationPoint &ip)
{
real_t x[3];
Vector transip(x, 3);
+2 -2
View File
@@ -102,8 +102,8 @@ public:
using VectorCoefficient::Eval;
void Eval(Vector &K, ElementTransformation &T,
const IntegrationPoint &ip) override
virtual void Eval(Vector &K, ElementTransformation &T,
const IntegrationPoint &ip)
{
real_t x[3];
Vector transip(x, 3);
+1 -1
View File
@@ -58,7 +58,7 @@ public:
}
}
~DiffusionMultigrid() override
virtual ~DiffusionMultigrid()
{
delete amg;
}
+3 -3
View File
@@ -53,7 +53,7 @@ public:
real_t min_val_=-36)
: u(&u_), obstacle(&obst_), min_val(min_val_) { }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip);
};
class ExponentialGridFunctionCoefficient : public Coefficient
@@ -69,7 +69,7 @@ public:
real_t min_val_=0.0, real_t max_val_=1e6)
: u(&u_), obstacle(&obst_), min_val(min_val_), max_val(max_val_) { }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip);
};
int main(int argc, char *argv[])
@@ -258,7 +258,7 @@ int main(int argc, char *argv[])
MixedBilinearForm a10(&H1fes,&L2fes);
a10.AddDomainIntegrator(new MixedScalarMassIntegrator());
a10.Assemble();
a10.EliminateTrialEssentialBC(ess_bdr, x.GetBlock(0), rhs.GetBlock(1));
a10.EliminateTrialDofs(ess_bdr, x.GetBlock(0), rhs.GetBlock(1));
a10.Finalize();
SparseMatrix &A10 = a10.SpMat();
+2 -2
View File
@@ -53,7 +53,7 @@ public:
real_t min_val_=-36)
: u(&u_), obstacle(&obst_), min_val(min_val_) { }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip);
};
class ExponentialGridFunctionCoefficient : public Coefficient
@@ -69,7 +69,7 @@ public:
real_t min_val_=0.0, real_t max_val_=1e6)
: u(&u_), obstacle(&obst_), min_val(min_val_), max_val(max_val_) { }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip);
};
int main(int argc, char *argv[])
+8 -8
View File
@@ -52,8 +52,8 @@ public:
fun(fun_) {}
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
return fun(GridFunctionCoefficient::Eval(T, ip));
}
@@ -83,8 +83,8 @@ public:
OtherGridF_cf(OtherGridF),
fun(fun_) {}
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
const real_t value1 = fun(GridFunctionCoefficient::Eval(T, ip));
const real_t value2 = fun(OtherGridF_cf.Eval(T, ip));
@@ -108,7 +108,7 @@ public:
: rho_filter(rho_filter_), min_val(min_val_), max_val(max_val_),
exponent(exponent_) { }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip)
{
real_t val = rho_filter->GetValue(T, ip);
real_t coeff = min_val + pow(val,exponent)*(max_val-min_val);
@@ -142,7 +142,7 @@ public:
MFEM_ASSERT(rho_filter, "density field is not set");
}
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip)
{
real_t L = lambda->Eval(T, ip);
real_t M = mu->Eval(T, ip);
@@ -176,8 +176,8 @@ public:
using VectorCoefficient::Eval;
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
Vector xx; xx.SetSize(T.GetDimension());
T.Transform(ip,xx);
+6 -6
View File
@@ -408,9 +408,9 @@ public:
@param [in] Tr transformation of finite element
@param [out] elvect vector containing the
*/
void AssembleRHSElementVect(const FiniteElement &el,
ElementTransformation &Tr,
Vector &elvect) override
virtual void AssembleRHSElementVect(const FiniteElement &el,
ElementTransformation &Tr,
Vector &elvect) override
{
int dof = el.GetDof();
shape.SetSize(dof);
@@ -476,9 +476,9 @@ public:
@param [in] Tr transformation of finite element
@param [out] elvect vector containing the
*/
void AssembleRHSElementVect(const FiniteElement &el,
ElementTransformation &Tr,
Vector &elvect) override
virtual void AssembleRHSElementVect(const FiniteElement &el,
ElementTransformation &Tr,
Vector &elvect) override
{
int dof = el.GetDof();
shape.SetSize(dof);
+4 -4
View File
@@ -69,8 +69,8 @@ public:
using VectorCoefficient::Eval;
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
void SetAlpha(real_t alpha_) { alpha = alpha_; }
};
@@ -84,8 +84,8 @@ public:
DZCoefficient(int height, GridFunction &psi_, real_t alpha_ = 1.0)
: MatrixCoefficient(height), psi(&psi_), alpha(alpha_) { }
void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
void SetAlpha(real_t alpha_) { alpha = alpha_; }
};
+4 -4
View File
@@ -69,8 +69,8 @@ public:
using VectorCoefficient::Eval;
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
void SetAlpha(real_t alpha_) { alpha = alpha_; }
};
@@ -84,8 +84,8 @@ public:
DZCoefficient(int height, ParGridFunction &psi_, real_t alpha_ = 1.0)
: MatrixCoefficient(height), psi(&psi_), alpha(alpha_) { }
void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
void SetAlpha(real_t alpha_) { alpha = alpha_; }
};
+1 -1
View File
@@ -157,7 +157,7 @@ int main(int argc, char *argv[])
MixedBilinearForm *B0 = new MixedBilinearForm(x0_space,test_space);
B0->AddDomainIntegrator(new DiffusionIntegrator(one));
B0->Assemble();
B0->EliminateTrialEssentialBC(ess_bdr, x.GetBlock(x0_var), F);
B0->EliminateTrialDofs(ess_bdr, x.GetBlock(x0_var), F);
B0->Finalize();
MixedBilinearForm *Bhat = new MixedBilinearForm(xhat_space,test_space);
+5 -5
View File
@@ -104,12 +104,12 @@ public:
}
}
void SetOperator(const Operator &op) override
void SetOperator(const Operator &op)
{
linear_solver.SetOperator(op);
}
void Mult(const Vector &x, Vector &y) const override
virtual void Mult(const Vector &x, Vector &y) const
{
linear_solver.Mult(x, y);
}
@@ -134,10 +134,10 @@ private:
public:
FE_Evolution(BilinearForm &M_, BilinearForm &K_, const Vector &b_);
void Mult(const Vector &x, Vector &y) const override;
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override;
virtual void Mult(const Vector &x, Vector &y) const;
virtual void ImplicitSolve(const real_t dt, const Vector &x, Vector &k);
~FE_Evolution() override;
virtual ~FE_Evolution();
};
+9 -9
View File
@@ -92,7 +92,7 @@ private:
public:
AIR_prec(int blocksize_) : AIR_solver(NULL), blocksize(blocksize_) { }
void SetOperator(const Operator &op) override
void SetOperator(const Operator &op)
{
width = op.Width();
height = op.Height();
@@ -110,7 +110,7 @@ public:
AIR_solver->SetMaxLevels(50);
}
void Mult(const Vector &x, Vector &y) const override
virtual void Mult(const Vector &x, Vector &y) const
{
// Scale the rhs by block inverse and solve system
HypreParVector z_s;
@@ -119,7 +119,7 @@ public:
AIR_solver->Mult(z_s, y);
}
~AIR_prec() override
~AIR_prec()
{
delete AIR_solver;
}
@@ -185,17 +185,17 @@ public:
}
}
void SetOperator(const Operator &op) override
void SetOperator(const Operator &op)
{
linear_solver.SetOperator(op);
}
void Mult(const Vector &x, Vector &y) const override
virtual void Mult(const Vector &x, Vector &y) const
{
linear_solver.Mult(x, y);
}
~DG_Solver() override
~DG_Solver()
{
delete prec;
delete A;
@@ -223,10 +223,10 @@ public:
FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_, const Vector &b_,
PrecType prec_type);
void Mult(const Vector &x, Vector &y) const override;
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override;
virtual void Mult(const Vector &x, Vector &y) const;
virtual void ImplicitSolve(const real_t dt, const Vector &x, Vector &k);
~FE_Evolution() override;
virtual ~FE_Evolution();
};
+1 -9
View File
@@ -206,7 +206,6 @@ int main(int argc, char *argv[])
bool use_petsc = true;
const char *petscrc_file = "";
bool petsc_use_jfnk = false;
const char *device_config = "cpu";
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -244,8 +243,6 @@ int main(int argc, char *argv[])
args.AddOption(&petsc_use_jfnk, "-jfnk", "--jfnk", "-no-jfnk",
"--no-jfnk",
"Use JFNK with user-defined preconditioner factory.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.Parse();
if (!args.Good())
{
@@ -260,12 +257,7 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
}
// 2b. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 2c. We initialize PETSc
// 2b. We initialize PETSc
if (use_petsc)
{
MFEMInitializePetsc(NULL,NULL,petscrc_file,NULL);
+1 -9
View File
@@ -67,7 +67,6 @@ int main(int argc, char *argv[])
bool use_petsc = true;
const char *petscrc_file = "";
bool use_nonoverlapping = false;
const char *device_config = "cpu";
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -96,8 +95,6 @@ int main(int argc, char *argv[])
"-no-nonoverlapping", "--no-nonoverlapping",
"Use or not the block diagonal PETSc's matrix format "
"for non-overlapping domain decomposition.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.Parse();
if (!args.Good())
{
@@ -112,12 +109,7 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
}
// 2b. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 2c. We initialize PETSc
// 2b. We initialize PETSc
if (use_petsc) { MFEMInitializePetsc(NULL,NULL,petscrc_file,NULL); }
// 3. Read the (serial) mesh from the given mesh file on all processors. We
+2 -11
View File
@@ -61,7 +61,6 @@ int main(int argc, char *argv[])
bool use_petsc = true;
const char *petscrc_file = "";
bool use_nonoverlapping = false;
const char *device_config = "cpu";
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -88,8 +87,6 @@ int main(int argc, char *argv[])
"-no-nonoverlapping", "--no-nonoverlapping",
"Use or not the block diagonal PETSc's matrix format "
"for non-overlapping domain decomposition.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.Parse();
if (!args.Good())
{
@@ -103,15 +100,9 @@ int main(int argc, char *argv[])
{
args.PrintOptions(cout);
}
kappa = freq * M_PI;
// 2b. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 2c. We initialize PETSc
// 2b. We initialize PETSc
if (use_petsc) { MFEMInitializePetsc(NULL,NULL,petscrc_file,NULL); }
kappa = freq * M_PI;
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
+2 -11
View File
@@ -58,7 +58,6 @@ int main(int argc, char *argv[])
bool use_petsc = true;
const char *petscrc_file = "";
bool use_nonoverlapping = false;
const char *device_config = "cpu";
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -89,8 +88,6 @@ int main(int argc, char *argv[])
"-no-nonoverlapping", "--no-nonoverlapping",
"Use or not the block diagonal PETSc's matrix format "
"for non-overlapping domain decomposition.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.Parse();
if (!args.Good())
{
@@ -104,15 +101,9 @@ int main(int argc, char *argv[])
{
args.PrintOptions(cout);
}
kappa = freq * M_PI;
// 2b. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 2c. We initialize PETSc
// 2b. We initialize PETSc
if (use_petsc) { MFEMInitializePetsc(NULL,NULL,petscrc_file,NULL); }
kappa = freq * M_PI;
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
+7 -28
View File
@@ -59,8 +59,6 @@ int main(int argc, char *argv[])
// 2. Parse command-line options.
const char *mesh_file = "../../data/star.mesh";
int ser_ref_levels = -1;
int par_ref_levels = 2;
int order = 1;
bool par_format = false;
bool visualization = 1;
@@ -68,22 +66,15 @@ int main(int argc, char *argv[])
bool use_nonoverlapping = false;
bool local_bdr_spec = false;
const char *petscrc_file = "";
const char *device_config = "cpu";
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
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",
"Finite element order (polynomial degree).");
args.AddOption(&par_format, "-pf", "--parallel-format", "-sf",
"--serial-format",
"Format to use when saving the results for VisIt.");
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.");
@@ -112,13 +103,7 @@ int main(int argc, char *argv[])
{
args.PrintOptions(cout);
}
// 2b. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 2c. We initialize PETSc
// 2b. We initialize PETSc
if (use_petsc) { MFEMInitializePetsc(NULL,NULL,petscrc_file,NULL); }
// 3. Read the (serial) mesh from the given mesh file on all processors. We
@@ -132,11 +117,9 @@ int main(int argc, char *argv[])
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 10,000 elements.
{
if (ser_ref_levels < 0)
{
ser_ref_levels = (int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
}
for (int l = 0; l < ser_ref_levels; l++)
int ref_levels =
(int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
@@ -148,6 +131,7 @@ int main(int argc, char *argv[])
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
@@ -203,26 +187,21 @@ int main(int argc, char *argv[])
// 9. Define the parallel grid function and parallel linear forms, solution
// vector and rhs.
MemoryType mt = device.GetMemoryType();
BlockVector x(block_offsets, mt), rhs(block_offsets, mt);
BlockVector trueX(block_trueOffsets, mt), trueRhs(block_trueOffsets, mt);
BlockVector x(block_offsets), rhs(block_offsets);
BlockVector trueX(block_trueOffsets), trueRhs(block_trueOffsets);
ParLinearForm *fform(new ParLinearForm);
fform->Update(R_space, rhs.GetBlock(0), 0);
fform->AddDomainIntegrator(new VectorFEDomainLFIntegrator(fcoeff));
fform->AddBoundaryIntegrator(new VectorFEBoundaryFluxLFIntegrator(fnatcoeff));
fform->Assemble();
fform->SyncAliasMemory(rhs);
fform->ParallelAssemble(trueRhs.GetBlock(0));
trueRhs.GetBlock(0).SyncAliasMemory(trueRhs);
ParLinearForm *gform(new ParLinearForm);
gform->Update(W_space, rhs.GetBlock(1), 0);
gform->AddDomainIntegrator(new DomainLFIntegrator(gcoeff));
gform->Assemble();
gform->SyncAliasMemory(rhs);
gform->ParallelAssemble(trueRhs.GetBlock(1));
trueRhs.GetBlock(1).SyncAliasMemory(trueRhs);
// 10. Assemble the finite element matrices for the Darcy operator
//
+1 -10
View File
@@ -53,7 +53,6 @@ int main(int argc, char *argv[])
bool use_petsc = true;
const char *petscrc_file = "";
bool use_nonoverlapping = false;
const char *device_config = "cpu";
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -74,8 +73,6 @@ int main(int argc, char *argv[])
"-no-nonoverlapping", "--no-nonoverlapping",
"Use or not the block diagonal PETSc's matrix format "
"for non-overlapping domain decomposition.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.Parse();
if (!args.Good())
{
@@ -89,13 +86,7 @@ int main(int argc, char *argv[])
{
args.PrintOptions(cout);
}
// 2b. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
if (myid == 0) { device.Print(); }
// 2c. We initialize PETSc
// 2b. We initialize PETSc
if (use_petsc) { MFEMInitializePetsc(NULL,NULL,petscrc_file,NULL); }
// 3. Read the (serial) mesh from the given mesh file on all processors. We
+49 -105
View File
@@ -9,9 +9,9 @@
// ex10 -m ../../data/beam-quad.mesh -r 2 -o 2 -s 12 -dt 0.15 -vs 10
// ex10 -m ../../data/beam-tri.mesh -r 2 -o 2 -s 16 -dt 0.3 -vs 5
// ex10 -m ../../data/beam-hex.mesh -r 1 -o 2 -s 12 -dt 0.2 -vs 5
// ex10 -m ../../data/beam-tri.mesh -r 2 -o 2 -s 2 -dt 3 -nls 1
// ex10 -m ../../data/beam-quad.mesh -r 2 -o 2 -s 2 -dt 3 -nls 2
// ex10 -m ../../data/beam-hex.mesh -r 1 -o 2 -s 2 -dt 3 -nls 4
// ex10 -m ../../data/beam-tri.mesh -r 2 -o 2 -s 2 -dt 3 -nls kinsol
// ex10 -m ../../data/beam-quad.mesh -r 2 -o 2 -s 2 -dt 3 -nls kinsol
// ex10 -m ../../data/beam-hex.mesh -r 1 -o 2 -s 2 -dt 3 -nls kinsol
// ex10 -m ../../data/beam-quad.mesh -r 2 -o 2 -s 14 -dt 0.15 -vs 10
// ex10 -m ../../data/beam-tri.mesh -r 2 -o 2 -s 17 -dt 0.01 -vs 30
// ex10 -m ../../data/beam-hex.mesh -r 1 -o 2 -s 14 -dt 0.15 -vs 10
@@ -99,11 +99,16 @@ protected:
double saved_gamma; // saved gamma value from implicit setup
public:
/// Solver type to use in the ImplicitSolve() method, used by SDIRK methods.
enum NonlinearSolverType
{
NEWTON = 0, ///< Use MFEM's plain NewtonSolver
KINSOL = 1 ///< Use SUNDIALS' KINSOL (through MFEM's class KINSolver)
};
HyperelasticOperator(FiniteElementSpace &f, Array<int> &ess_bdr,
double visc, double mu, double K,
int kinsol_nls_type = -1, double kinsol_damping = 0.0,
int kinsol_aa_n = 0);
NonlinearSolverType nls_type);
/// Compute the right-hand side of the ODE system.
virtual void Mult(const Vector &vx, Vector &dvx_dt) const;
@@ -221,10 +226,8 @@ int main(int argc, char *argv[])
double mu = 0.25;
double K = 5.0;
bool visualization = true;
int nonlinear_solver_type = 0;
const char *nls = "newton";
int vis_steps = 1;
double kinsol_damping = 0.0;
int kinsol_aa_n = -1;
// Relative and absolute tolerances for CVODE and ARKODE.
const double reltol = 1e-1, abstol = 1e-1;
@@ -261,18 +264,9 @@ int main(int argc, char *argv[])
"15 - ARKODE implicit, approximate Jacobian,\n\t"
"16 - ARKODE implicit, specified Jacobian,\n\t"
"17 - ARKODE explicit, 4th order.");
args.AddOption(&nonlinear_solver_type, "-nls", "--nonlinear-solver",
"Nonlinear system solver:\n\t"
"0 - MFEM Newton method,\n\t"
"1 - KINSOL Newton method,\n\t"
"2 - KINSOL Newton method with globalization,\n\t"
"3 - KINSOL fixed-point method (with or without AA),\n\t"
"4 - KINSOL Picard method (with or without AA).");
args.AddOption(&kinsol_damping, "-damp", "--kinsol-damping",
"Picard or Fixed-Point damping parameter (only valid with KINSOL): "
"0 < d <= 1.0");
args.AddOption(&kinsol_aa_n, "-aan", "--anderson-subspace",
"Anderson Acceleration subspace size (only valid with KINSOL)");
args.AddOption(&nls, "-nls", "--nonlinear-solver",
"Nonlinear systems solver: "
"\"newton\" (plain Newton) or \"kinsol\" (KINSOL).");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
@@ -303,32 +297,22 @@ int main(int argc, char *argv[])
return 1;
}
// check for valid nonlinear solver options
if (nonlinear_solver_type < 0 || nonlinear_solver_type > 4)
{
cout << "Unknown nonlinear solver type: " << nonlinear_solver_type << "\n";
return 1;
}
if (kinsol_damping > 0.0 &&
!(nonlinear_solver_type == 3 || nonlinear_solver_type == 4))
{
cout << "Only KINSOL fixed-point and Picard methods can use damping\n";
return 1;
}
if (kinsol_aa_n > 0 &&
!(nonlinear_solver_type == 3 || nonlinear_solver_type == 4))
{
cout << "Only KINSOL fixed-point and Picard methods can use AA\n";
return 1;
}
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral and hexahedral meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 3. Refine the mesh to increase the resolution. In this example we do
// 3. Setup the nonlinear solver
map<string,HyperelasticOperator::NonlinearSolverType> nls_map;
nls_map["newton"] = HyperelasticOperator::NEWTON;
nls_map["kinsol"] = HyperelasticOperator::KINSOL;
if (nls_map.find(nls) == nls_map.end())
{
cout << "Unknown type of nonlinear solver: " << nls << endl;
return 4;
}
// 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++)
@@ -336,7 +320,7 @@ int main(int argc, char *argv[])
mesh->UniformRefinement();
}
// 4. Define the vector finite element spaces representing the mesh
// 5. Define the vector finite element spaces representing the mesh
// deformation x, the velocity v, and the initial configuration, x_ref.
// Define also the elastic energy density, w, which is in a discontinuous
// higher-order space. Since x and v are integrated in time as a system,
@@ -364,7 +348,7 @@ int main(int argc, char *argv[])
FiniteElementSpace w_fespace(mesh, &w_fec);
GridFunction w(&w_fespace);
// 5. Set the initial conditions for v and x, and the boundary conditions on
// 6. Set the initial conditions for v and x, and the boundary conditions on
// a beam-like mesh (see description above).
VectorFunctionCoefficient velo(dim, InitialVelocity);
v.ProjectCoefficient(velo);
@@ -377,34 +361,9 @@ int main(int argc, char *argv[])
ess_bdr = 0;
ess_bdr[0] = 1; // boundary attribute 1 (index 0) is fixed
// 6. Initialize the hyperelastic operator, the GLVis visualization and print
// 7. Initialize the hyperelastic operator, the GLVis visualization and print
// the initial energies.
std::unique_ptr<HyperelasticOperator> oper;
if (nonlinear_solver_type == 0)
oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr, visc, mu,
K);
else
{
switch (nonlinear_solver_type)
{
case 1:
oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr,
visc, mu, K, KIN_NONE);
break;
case 2:
oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr,
visc, mu, K, KIN_LINESEARCH);
break;
case 3:
oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr,
visc, mu, K, KIN_FP, kinsol_damping, kinsol_aa_n);
break;
case 4:
oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr,
visc, mu, K, KIN_PICARD, kinsol_damping, kinsol_aa_n);
break;
}
}
HyperelasticOperator oper(fespace, ess_bdr, visc, mu, K, nls_map[nls]);
socketstream vis_v, vis_w;
if (visualization)
@@ -418,23 +377,23 @@ int main(int argc, char *argv[])
vis_w.open(vishost, visport);
if (vis_w)
{
oper->GetElasticEnergyDensity(x, w);
oper.GetElasticEnergyDensity(x, w);
vis_w.precision(8);
visualize(vis_w, mesh, &x, &w, "Elastic energy density", true);
}
}
double ee0 = oper->ElasticEnergy(x.GetTrueVector());
double ke0 = oper->KineticEnergy(v.GetTrueVector());
double ee0 = oper.ElasticEnergy(x.GetTrueVector());
double ke0 = oper.KineticEnergy(v.GetTrueVector());
cout << "initial elastic energy (EE) = " << ee0 << endl;
cout << "initial kinetic energy (KE) = " << ke0 << endl;
cout << "initial total energy (TE) = " << (ee0 + ke0) << endl;
// 7. Define the ODE solver used for time integration. Several implicit
// 8. Define the ODE solver used for time integration. Several implicit
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
// explicit Runge-Kutta methods are available.
double t = 0.0;
oper->SetTime(t);
oper.SetTime(t);
ODESolver *ode_solver = NULL;
CVODESolver *cvode = NULL;
@@ -458,7 +417,7 @@ int main(int argc, char *argv[])
case 11:
case 12:
cvode = new CVODESolver(CV_BDF);
cvode->Init(*oper);
cvode->Init(oper);
cvode->SetSStolerances(reltol, abstol);
CVodeSetEpsLin(cvode->GetMem(), cvode_eps_lin);
cvode->SetMaxStep(dt);
@@ -471,7 +430,7 @@ int main(int argc, char *argv[])
case 13:
case 14:
cvode = new CVODESolver(CV_ADAMS);
cvode->Init(*oper);
cvode->Init(oper);
cvode->SetSStolerances(reltol, abstol);
CVodeSetEpsLin(cvode->GetMem(), cvode_eps_lin);
cvode->SetMaxStep(dt);
@@ -484,7 +443,7 @@ int main(int argc, char *argv[])
case 15:
case 16:
arkode = new ARKStepSolver(ARKStepSolver::IMPLICIT);
arkode->Init(*oper);
arkode->Init(oper);
arkode->SetSStolerances(reltol, abstol);
ARKStepSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
arkode->SetMaxStep(dt);
@@ -496,16 +455,16 @@ int main(int argc, char *argv[])
// ARKStep Explicit methods
case 17:
arkode = new ARKStepSolver(ARKStepSolver::EXPLICIT);
arkode->Init(*oper);
arkode->Init(oper);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
ode_solver = arkode; break;
}
// Initialize MFEM integrators, SUNDIALS integrators are initialized above
if (ode_solver_type < 11) { ode_solver->Init(*oper); }
if (ode_solver_type < 11) { ode_solver->Init(oper); }
// 8. Perform time-integration (looping over the time iterations, ti, with a
// 9. Perform time-integration (looping over the time iterations, ti, with a
// time-step dt).
bool last_step = false;
for (int ti = 1; !last_step; ti++)
@@ -518,8 +477,8 @@ int main(int argc, char *argv[])
if (last_step || (ti % vis_steps) == 0)
{
double ee = oper->ElasticEnergy(x.GetTrueVector());
double ke = oper->KineticEnergy(v.GetTrueVector());
double ee = oper.ElasticEnergy(x.GetTrueVector());
double ke = oper.KineticEnergy(v.GetTrueVector());
cout << "step " << ti << ", t = " << t << ", EE = " << ee << ", KE = "
<< ke << ", ΔTE = " << (ee+ke)-(ee0+ke0) << endl;
@@ -533,14 +492,14 @@ int main(int argc, char *argv[])
visualize(vis_v, mesh, &x, &v);
if (vis_w)
{
oper->GetElasticEnergyDensity(x, w);
oper.GetElasticEnergyDensity(x, w);
visualize(vis_w, mesh, &x, &w);
}
}
}
}
// 9. Save the displaced mesh, the velocity and elastic energy.
// 10. Save the displaced mesh, the velocity and elastic energy.
{
v.SetFromTrueVector(); x.SetFromTrueVector();
GridFunction *nodes = &x;
@@ -555,11 +514,11 @@ int main(int argc, char *argv[])
v.Save(velo_ofs);
ofstream ee_ofs("elastic_energy.sol");
ee_ofs.precision(8);
oper->GetElasticEnergyDensity(x, w);
oper.GetElasticEnergyDensity(x, w);
w.Save(ee_ofs);
}
// 10. Free the used memory.
// 11. Free the used memory.
delete ode_solver;
delete mesh;
@@ -643,9 +602,7 @@ ReducedSystemOperator::~ReducedSystemOperator()
HyperelasticOperator::HyperelasticOperator(FiniteElementSpace &f,
Array<int> &ess_bdr, double visc,
double mu, double K,
int kinsol_nls_type,
double kinsol_damping,
int kinsol_aa_n)
NonlinearSolverType nls_type)
: TimeDependentOperator(2*f.GetTrueVSize(), 0.0), fespace(f),
M(&fespace), S(&fespace), H(&fespace),
viscosity(visc), z(height/2),
@@ -696,28 +653,15 @@ HyperelasticOperator::HyperelasticOperator(FiniteElementSpace &f,
J_prec = NULL;
#endif
if (kinsol_nls_type > 0)
if (nls_type == KINSOL)
{
KINSolver *kinsolver = new KINSolver(kinsol_nls_type, true);
if (kinsol_nls_type != KIN_PICARD)
{
kinsolver->SetJFNK(true);
kinsolver->SetLSMaxIter(100);
}
if (kinsol_aa_n > 0)
{
kinsolver->EnableAndersonAcc(kinsol_aa_n);
}
KINSolver *kinsolver = new KINSolver(KIN_NONE, true);
newton_solver = kinsolver;
newton_solver->SetOperator(*reduced_oper);
newton_solver->SetMaxIter(200);
newton_solver->SetRelTol(rel_tol);
newton_solver->SetPrintLevel(0);
kinsolver->SetMaxSetupCalls(4);
if (kinsol_damping > 0.0)
{
kinsolver->SetDamping(kinsol_damping);
}
}
else
{
+59 -123
View File
@@ -9,9 +9,9 @@
// mpirun -np 4 ex10p -m ../../data/beam-quad.mesh -rp 1 -o 2 -s 12 -dt 0.15 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-tri.mesh -rp 1 -o 2 -s 16 -dt 0.25 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-hex.mesh -rp 0 -o 2 -s 12 -dt 0.15 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-tri.mesh -rp 1 -o 2 -s 2 -dt 3 -nls 1
// mpirun -np 4 ex10p -m ../../data/beam-quad.mesh -rp 1 -o 2 -s 2 -dt 3 -nls 2
// mpirun -np 4 ex10p -m ../../data/beam-hex.mesh -rs 1 -o 2 -s 2 -dt 3 -nls 4
// mpirun -np 4 ex10p -m ../../data/beam-tri.mesh -rp 1 -o 2 -s 2 -dt 3 -nls kinsol
// mpirun -np 4 ex10p -m ../../data/beam-quad.mesh -rp 1 -o 2 -s 2 -dt 3 -nls kinsol
// mpirun -np 4 ex10p -m ../../data/beam-hex.mesh -rs 1 -o 2 -s 2 -dt 3 -nls kinsol
// mpirun -np 4 ex10p -m ../../data/beam-quad.mesh -rp 1 -o 2 -s 14 -dt 0.15 -vs 10
// mpirun -np 4 ex10p -m ../../data/beam-tri.mesh -rp 1 -o 2 -s 17 -dt 5e-3 -vs 60
// mpirun -np 4 ex10p -m ../../data/beam-hex.mesh -rp 0 -o 2 -s 14 -dt 0.15 -vs 10
@@ -101,11 +101,16 @@ protected:
double saved_gamma; // saved gamma value from implicit setup
public:
/// Solver type to use in the ImplicitSolve() method, used by SDIRK methods.
enum NonlinearSolverType
{
NEWTON = 0, ///< Use MFEM's plain NewtonSolver
KINSOL = 1 ///< Use SUNDIALS' KINSOL (through MFEM's class KINSolver)
};
HyperelasticOperator(ParFiniteElementSpace &f, Array<int> &ess_bdr,
double visc, double mu, double K,
int kinsol_nls_type = -1, double kinsol_damping = 0.0,
int kinsol_aa_n = 0);
NonlinearSolverType nls_type);
/// Compute the right-hand side of the ODE system.
virtual void Mult(const Vector &vx, Vector &dvx_dt) const;
@@ -230,10 +235,8 @@ int main(int argc, char *argv[])
double mu = 0.25;
double K = 5.0;
bool visualization = true;
int nonlinear_solver_type = 0;
const char *nls = "newton";
int vis_steps = 1;
double kinsol_damping = 0.0;
int kinsol_aa_n = -1;
// Relative and absolute tolerances for CVODE and ARKODE.
const double reltol = 1e-1, abstol = 1e-1;
@@ -272,18 +275,9 @@ int main(int argc, char *argv[])
"15 - ARKODE implicit, approximate Jacobian,\n\t"
"16 - ARKODE implicit, specified Jacobian,\n\t"
"17 - ARKODE explicit, 4th order.");
args.AddOption(&nonlinear_solver_type, "-nls", "--nonlinear-solver",
"Nonlinear system solver:\n\t"
"0 - MFEM Newton method,\n\t"
"1 - KINSOL Newton method,\n\t"
"2 - KINSOL Newton method with globalization,\n\t"
"3 - KINSOL fixed-point method (with or without AA),\n\t"
"4 - KINSOL Picard method (with or without AA).");
args.AddOption(&kinsol_damping, "-damp", "--kinsol-damping",
"Picard or Fixed-Point damping parameter (only valid with KINSOL): "
"0 < d <= 1.0");
args.AddOption(&kinsol_aa_n, "-aan", "--anderson-subspace",
"Anderson Acceleration subspace size (only valid with KINSOL)");
args.AddOption(&nls, "-nls", "--nonlinear-solver",
"Nonlinear systems solver: "
"\"newton\" (plain Newton) or \"kinsol\" (KINSOL).");
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
@@ -323,42 +317,27 @@ int main(int argc, char *argv[])
return 1;
}
// check for valid nonlinear solver options
if (nonlinear_solver_type < 0 || nonlinear_solver_type > 4)
{
if (myid == 0)
{
cout << "Unknown nonlinear solver type: " << nonlinear_solver_type
<< "\n";
}
return 1;
}
if (kinsol_damping > 0.0 &&
!(nonlinear_solver_type == 3 || nonlinear_solver_type == 4))
{
if (myid == 0)
{
cout << "Only KINSOL fixed-point and Picard methods can use damping\n";
}
return 1;
}
if (kinsol_aa_n > 0 &&
!(nonlinear_solver_type == 3 || nonlinear_solver_type == 4))
{
if (myid == 0)
{
cout << "Only KINSOL fixed-point and Picard methods can use AA\n";
}
return 1;
}
// 3. Read the serial mesh from the given mesh file on all processors. We can
// handle triangular, quadrilateral, tetrahedral and hexahedral meshes
// with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Refine the mesh in serial to increase the resolution. In this example
// 4. Nonlinear solver
map<string,HyperelasticOperator::NonlinearSolverType> nls_map;
nls_map["newton"] = HyperelasticOperator::NEWTON;
nls_map["kinsol"] = HyperelasticOperator::KINSOL;
if (nls_map.find(nls) == nls_map.end())
{
if (myid == 0)
{
cout << "Unknown type of nonlinear solver: " << nls << endl;
}
delete mesh;
return 4;
}
// 5. Refine the mesh in serial to increase the resolution. In this example
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
// a command-line parameter.
for (int lev = 0; lev < ser_ref_levels; lev++)
@@ -366,7 +345,7 @@ int main(int argc, char *argv[])
mesh->UniformRefinement();
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// 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 = new ParMesh(MPI_COMM_WORLD, *mesh);
@@ -376,7 +355,7 @@ int main(int argc, char *argv[])
pmesh->UniformRefinement();
}
// 6. Define the parallel vector finite element spaces representing the mesh
// 7. Define the parallel vector finite element spaces representing the mesh
// deformation x_gf, the velocity v_gf, and the initial configuration,
// x_ref. Define also the elastic energy density, w_gf, which is in a
// discontinuous higher-order space. Since x and v are integrated in time
@@ -408,7 +387,7 @@ int main(int argc, char *argv[])
ParFiniteElementSpace w_fespace(pmesh, &w_fec);
ParGridFunction w_gf(&w_fespace);
// 7. Set the initial conditions for v_gf, x_gf and vx, and define the
// 8. Set the initial conditions for v_gf, x_gf and vx, and define the
// boundary conditions on a beam-like mesh (see description above).
VectorFunctionCoefficient velo(dim, InitialVelocity);
v_gf.ProjectCoefficient(velo);
@@ -423,38 +402,9 @@ int main(int argc, char *argv[])
ess_bdr = 0;
ess_bdr[0] = 1; // boundary attribute 1 (index 0) is fixed
// 8. Initialize the hyperelastic operator, the GLVis visualization and print
// 9. Initialize the hyperelastic operator, the GLVis visualization and print
// the initial energies.
std::unique_ptr<HyperelasticOperator> oper;
if (nonlinear_solver_type == 0)
oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr, visc, mu,
K);
else
{
switch (nonlinear_solver_type)
{
case 1:
oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr,
visc, mu, K, KIN_NONE);
break;
case 2:
oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr,
visc, mu, K, KIN_LINESEARCH);
break;
case 3:
oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr,
visc, mu, K, KIN_FP, kinsol_damping, kinsol_aa_n);
break;
case 4:
oper = std::make_unique<HyperelasticOperator>(fespace, ess_bdr,
visc, mu, K, KIN_PICARD, kinsol_damping, kinsol_aa_n);
break;
default:
cout << "Unknown type of nonlinear solver: "
<< nonlinear_solver_type << endl;
return 4;
}
}
HyperelasticOperator oper(fespace, ess_bdr, visc, mu, K, nls_map[nls]);
socketstream vis_v, vis_w;
if (visualization)
@@ -470,14 +420,14 @@ int main(int argc, char *argv[])
vis_w.open(vishost, visport);
if (vis_w)
{
oper->GetElasticEnergyDensity(x_gf, w_gf);
oper.GetElasticEnergyDensity(x_gf, w_gf);
vis_w.precision(8);
visualize(vis_w, pmesh, &x_gf, &w_gf, "Elastic energy density", true);
}
}
double ee0 = oper->ElasticEnergy(x_gf);
double ke0 = oper->KineticEnergy(v_gf);
double ee0 = oper.ElasticEnergy(x_gf);
double ke0 = oper.KineticEnergy(v_gf);
if (myid == 0)
{
cout << "initial elastic energy (EE) = " << ee0 << endl;
@@ -485,11 +435,11 @@ int main(int argc, char *argv[])
cout << "initial total energy (TE) = " << (ee0 + ke0) << endl;
}
// 9. Define the ODE solver used for time integration. Several implicit
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
// explicit Runge-Kutta methods are available.
// 10. Define the ODE solver used for time integration. Several implicit
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
// explicit Runge-Kutta methods are available.
double t = 0.0;
oper->SetTime(t);
oper.SetTime(t);
ODESolver *ode_solver = NULL;
CVODESolver *cvode = NULL;
@@ -513,7 +463,7 @@ int main(int argc, char *argv[])
case 11:
case 12:
cvode = new CVODESolver(MPI_COMM_WORLD, CV_BDF);
cvode->Init(*oper);
cvode->Init(oper);
cvode->SetSStolerances(reltol, abstol);
CVodeSetEpsLin(cvode->GetMem(), cvode_eps_lin);
cvode->SetMaxStep(dt);
@@ -526,7 +476,7 @@ int main(int argc, char *argv[])
case 13:
case 14:
cvode = new CVODESolver(MPI_COMM_WORLD, CV_ADAMS);
cvode->Init(*oper);
cvode->Init(oper);
cvode->SetSStolerances(reltol, abstol);
CVodeSetEpsLin(cvode->GetMem(), cvode_eps_lin);
cvode->SetMaxStep(dt);
@@ -539,7 +489,7 @@ int main(int argc, char *argv[])
case 15:
case 16:
arkode = new ARKStepSolver(MPI_COMM_WORLD, ARKStepSolver::IMPLICIT);
arkode->Init(*oper);
arkode->Init(oper);
arkode->SetSStolerances(reltol, abstol);
ARKStepSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
arkode->SetMaxStep(dt);
@@ -551,16 +501,16 @@ int main(int argc, char *argv[])
// ARKStep Explicit methods
case 17:
arkode = new ARKStepSolver(MPI_COMM_WORLD, ARKStepSolver::EXPLICIT);
arkode->Init(*oper);
arkode->Init(oper);
arkode->SetSStolerances(reltol, abstol);
arkode->SetMaxStep(dt);
ode_solver = arkode; break;
}
// Initialize MFEM integrators, SUNDIALS integrators are initialized above
if (ode_solver_type < 11) { ode_solver->Init(*oper); }
if (ode_solver_type < 11) { ode_solver->Init(oper); }
// 10. Perform time-integration
// 11. Perform time-integration
// (looping over the time iterations, ti, with a time-step dt).
bool last_step = false;
for (int ti = 1; !last_step; ti++)
@@ -575,8 +525,8 @@ int main(int argc, char *argv[])
{
v_gf.SetFromTrueVector(); x_gf.SetFromTrueVector();
double ee = oper->ElasticEnergy(x_gf);
double ke = oper->KineticEnergy(v_gf);
double ee = oper.ElasticEnergy(x_gf);
double ke = oper.KineticEnergy(v_gf);
if (myid == 0)
{
@@ -592,14 +542,14 @@ int main(int argc, char *argv[])
visualize(vis_v, pmesh, &x_gf, &v_gf);
if (vis_w)
{
oper->GetElasticEnergyDensity(x_gf, w_gf);
oper.GetElasticEnergyDensity(x_gf, w_gf);
visualize(vis_w, pmesh, &x_gf, &w_gf);
}
}
}
}
// 11. Save the displaced mesh, the velocity and elastic energy.
// 12. Save the displaced mesh, the velocity and elastic energy.
{
v_gf.SetFromTrueVector(); x_gf.SetFromTrueVector();
GridFunction *nodes = &x_gf;
@@ -620,11 +570,11 @@ int main(int argc, char *argv[])
v_gf.Save(velo_ofs);
ofstream ee_ofs(ee_name.str().c_str());
ee_ofs.precision(8);
oper->GetElasticEnergyDensity(x_gf, w_gf);
oper.GetElasticEnergyDensity(x_gf, w_gf);
w_gf.Save(ee_ofs);
}
// 12. Free the used memory.
// 13. Free the used memory.
delete ode_solver;
delete pmesh;
@@ -714,10 +664,7 @@ ReducedSystemOperator::~ReducedSystemOperator()
HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
Array<int> &ess_bdr, double visc,
double mu, double K,
int kinsol_nls_type,
double kinsol_damping,
int kinsol_aa_n)
NonlinearSolverType nls_type)
: TimeDependentOperator(2*f.TrueVSize(), 0.0), fespace(f),
M(&fespace), S(&fespace), H(&fespace),
viscosity(visc), M_solver(f.GetComm()), z(height/2),
@@ -769,28 +716,17 @@ HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
J_minres->SetPreconditioner(*J_prec);
J_solver = J_minres;
if (kinsol_nls_type > 0)
if (nls_type == KINSOL)
{
KINSolver *kinsolver = new KINSolver(f.GetComm(), kinsol_nls_type, true);
if (kinsol_nls_type != KIN_PICARD)
{
kinsolver->SetJFNK(true);
kinsolver->SetLSMaxIter(100);
}
if (kinsol_aa_n > 0)
{
kinsolver->EnableAndersonAcc(kinsol_aa_n);
}
KINSolver *kinsolver = new KINSolver(f.GetComm(), KIN_LINESEARCH, true);
kinsolver->SetJFNK(true);
kinsolver->SetLSMaxIter(100);
newton_solver = kinsolver;
newton_solver->SetOperator(*reduced_oper);
newton_solver->SetMaxIter(200);
newton_solver->SetRelTol(rel_tol);
newton_solver->SetPrintLevel(0);
newton_solver->SetPrintLevel(1);
kinsolver->SetMaxSetupCalls(4);
if (kinsol_damping > 0.0)
{
kinsolver->SetDamping(kinsol_damping);
}
}
else
{
File diff suppressed because it is too large Load Diff
+305
View File
@@ -0,0 +1,305 @@
#include "dfem/dfem_refactor.hpp"
#include "linalg/hypre.hpp"
using namespace mfem;
using mfem::internal::tensor;
using mfem::internal::dual;
int test_diffusion_integrator(std::string mesh_file,
int refinements,
int polynomial_order)
{
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder());
ParGridFunction f1_g(&h1fes);
ParGridFunction rho_g(&h1fes);
auto rho_f = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
return x + y;
};
FunctionCoefficient rho_c(rho_f);
rho_g.ProjectCoefficient(rho_c);
auto kernel = [](const tensor<dual<double, double>, 2> &grad_u,
const dual<double, double> &rho,
const tensor<double, 2, 2> &J,
const double &w)
{
auto invJ = inv(J);
return std::tuple{rho*rho * grad_u * invJ * transpose(invJ) * det(J) * w};
};
std::tuple argument_operators = {Gradient{"potential"}, Value{"density"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
std::tuple output_operator = {Gradient{"potential"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = std::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "potential"}};
auto parameters = std::array
{
FieldDescriptor{&h1fes, "density"},
FieldDescriptor{&mesh_fes, "coordinates"}
};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
auto f1 = [](const Vector &coords)
{
const double x = coords(0);
const double y = coords(1);
return 2.345 + 0.25 * x*x*y + y*y*x;
};
FunctionCoefficient f1_c(f1);
f1_g.ProjectCoefficient(f1_c);
Vector x(f1_g), y(h1fes.TrueVSize());
dop.SetParameters({&rho_g, mesh_nodes});
dop.Mult(x, y);
ParBilinearForm a(&h1fes);
TransformedCoefficient rho_c2(&rho_c, [](double c) {return c*c;});
a.AddDomainIntegrator(new DiffusionIntegrator(rho_c2));
a.Assemble();
a.Finalize();
Vector y2(h1fes.TrueVSize());
a.Mult(x, y2);
y2 -= y;
if (y2.Norml2() > 1e-10)
{
out << "||F(u) - ex||_l2 = " << y2.Norml2() << "\n";
return 1;
}
// Test linearization here as well
auto dFdu = dop.GetDerivativeWrt<0>({&f1_g}, {&rho_g, mesh_nodes});
// HypreParMatrix A;
// dFdu->Assemble(A);
if (dFdu->Height() != h1fes.GetTrueVSize())
{
out << "dFdu unexpected height of " << dFdu->Height() << "\n";
return 1;
}
dFdu->Mult(x, y);
a.Mult(x, y2);
y2 -= y;
if (y2.Norml2() > 1e-10)
{
out << "||dFdu u^* - ex||_l2 = " << y2.Norml2() << "\n";
return 1;
}
// fd jacobian test
{
double eps = 1.0e-6;
Vector v(x), xpv(x), xmv(x), fxpv(x.Size()), fxmv(x.Size());
v *= eps;
xpv += v;
xmv -= v;
dop.Mult(xpv, fxpv);
dop.Mult(xmv, fxmv);
fxpv -= fxmv;
fxpv /= (2.0*eps);
fxpv -= y;
if (fxpv.Norml2() > eps)
{
out << "||dFdu_FD u^* - ex||_l2 = " << fxpv.Norml2() << "\n";
return 1;
}
}
f1_g.ProjectCoefficient(f1_c);
rho_g.ProjectCoefficient(rho_c);
auto dFdrho = dop.GetDerivativeWrt<1>({&f1_g}, {&rho_g, mesh_nodes});
if (dFdrho->Height() != h1fes.GetTrueVSize())
{
out << "dFdrho unexpected height of " << dFdrho->Height() << "\n";
return 1;
}
dFdrho->Mult(rho_g, y);
// fd test
{
double eps = 1.0e-6;
Vector v(rho_g), rhopv(rho_g), rhomv(rho_g), frhopv(x.Size()), frhomv(x.Size());
v *= eps;
rhopv += v;
rhomv -= v;
dop.SetParameters({&rhopv, mesh_nodes});
dop.Mult(x, frhopv);
dop.SetParameters({&rhomv, mesh_nodes});
dop.Mult(x, frhomv);
frhopv -= frhomv;
frhopv /= (2.0*eps);
frhopv -= y;
if (frhopv.Norml2() > eps)
{
out << "||dFdu_FD u^* - ex||_l2 = " << frhopv.Norml2() << "\n";
return 1;
}
}
return 0;
}
int test_qoi(std::string mesh_file,
int refinements,
int polynomial_order)
{
Mesh mesh_serial = Mesh(mesh_file);
for (int i = 0; i < refinements; i++)
{
mesh_serial.UniformRefinement();
}
ParMesh mesh(MPI_COMM_WORLD, mesh_serial);
mesh.SetCurvature(1);
const int dim = mesh.Dimension();
mesh_serial.Clear();
ParGridFunction* mesh_nodes = static_cast<ParGridFunction *>(mesh.GetNodes());
ParFiniteElementSpace &mesh_fes = *mesh_nodes->ParFESpace();
H1_FECollection h1fec(polynomial_order, dim);
ParFiniteElementSpace h1fes(&mesh, &h1fec, dim);
const IntegrationRule &ir =
IntRules.Get(h1fes.GetFE(0)->GetGeomType(), 2 * h1fec.GetOrder());
ParGridFunction rho_g(&h1fes);
auto rho_f = [](const Vector &coords, Vector &u)
{
const double x = coords(0);
const double y = coords(1);
u(0) = x + y;
u(1) = x + y;
};
VectorFunctionCoefficient rho_c(dim, rho_f);
rho_g.ProjectCoefficient(rho_c);
auto kernel = [](const tensor<dual<double, double>, 2> &rho,
const tensor<dual<double, double>, 2, 2> &drhodxi,
const tensor<double, 2, 2> &J,
const double &w)
{
const double eps = 1.2345;
const auto drhodx = drhodxi * inv(J);
return std::tuple{(0.5 * eps * dot(rho, rho) + ddot(drhodx, drhodx)) * det(J) * w};
};
std::tuple argument_operators = {Value{"density"}, Gradient{"density"}, Gradient{"coordinates"}, Weight{"integration_weights"}};
std::tuple output_operator = {One{"density"}};
ElementOperator eop = {kernel, argument_operators, output_operator};
auto ops = std::tuple{eop};
auto solutions = std::array{FieldDescriptor{&h1fes, "density"}};
auto parameters = std::array{FieldDescriptor{&mesh_fes, "coordinates"}};
DifferentiableOperator dop(solutions, parameters, ops, mesh, ir);
Vector x(rho_g), y(1);
dop.SetParameters({mesh_nodes});
dop.Mult(x, y);
// print_vector(y);
auto dFdrho = dop.GetDerivativeWrt<0>({&rho_g}, {mesh_nodes});
// Vector dFdrho_vec;
// dFdrho->Assemble(dFdrho_vec);
// print_vector(dFdrho_vec);
// fd jacobian test
{
double eps = 1.0e-8;
Vector v(x), fxpv(1), fxmv(1), dfdx(x.Size());
for (int i = 0; i < x.Size(); i++)
{
v(i) += eps;
dop.Mult(v, fxpv);
v(i) -= 2.0 * eps;
dop.Mult(v, fxmv);
fxpv -= fxmv;
fxpv /= (2.0*eps);
dfdx(i) = fxpv(0);
}
// print_vector(dfdx);
dfdx -= dFdrho_vec;
if (dfdx.Norml2() > 1e-6)
{
out << "||dFdu_FD u^* - ex||_l2 = " << dfdx.Norml2() << "\n";
return 1;
}
}
return 0;
}
int main(int argc, char *argv[])
{
Mpi::Init();
std::cout << std::setprecision(9);
const char *mesh_file = "../data/star.mesh";
int polynomial_order = 1;
int ir_order = 2;
int refinements = 0;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&polynomial_order, "-o", "--order", "");
args.AddOption(&refinements, "-r", "--r", "");
args.AddOption(&ir_order, "-iro", "--iro", "");
args.ParseCheck();
out << std::setprecision(12);
int ret;
ret = test_diffusion_integrator(mesh_file,
refinements,
polynomial_order);
out << "test_diffusion_integrator";
ret ? out << " FAILURE\n" : out << " OK\n";
ret = test_qoi(mesh_file, refinements, polynomial_order);
out << "test_qoi";
ret ? out << " FAILURE\n" : out << " OK\n";
return 0;
}
+3 -3
View File
@@ -112,6 +112,8 @@ set(SRCS
qinterp/eval_by_vdim.cpp
qinterp/grad_by_nodes.cpp
qinterp/grad_by_vdim.cpp
qinterp/grad_phys_by_nodes.cpp
qinterp/grad_phys_by_vdim.cpp
qspace.cpp
quadinterpolator.cpp
quadinterpolator_face.cpp
@@ -190,9 +192,6 @@ set(HDRS
hybridization.hpp
intrules.hpp
intrules_cut.hpp
kernel_dispatch.hpp
kernel_reporter.hpp
kernels.hpp
ceed/interface/basis.hpp
ceed/interface/integrator.hpp
ceed/interface/interface.hpp
@@ -224,6 +223,7 @@ set(HDRS
nonlinearform_ext.hpp
nonlininteg.hpp
qfunction.hpp
qinterp/dispatch.hpp
qinterp/eval.hpp
qinterp/grad.hpp
qspace.hpp
+62 -305
View File
@@ -289,10 +289,9 @@ void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat) const
return;
}
const FiniteElement &fe = *fes->GetFE(i);
if (domain_integs.Size())
{
const FiniteElement &fe = *fes->GetFE(i);
ElementTransformation *eltrans = fes->GetElementTransformation(i);
domain_integs[0]->AssembleElementMatrix(fe, *eltrans, elmat);
for (int k = 1; k < domain_integs.Size(); k++)
@@ -303,18 +302,17 @@ void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat) const
}
else
{
const int ndof = fe.GetDof() * fes->GetVDim();
elmat.SetSize(ndof);
fes->GetElementVDofs(i, vdofs);
elmat.SetSize(vdofs.Size());
elmat = 0.0;
}
}
void BilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat) const
{
const FiniteElement &be = *fes->GetBE(i);
if (boundary_integs.Size())
{
const FiniteElement &be = *fes->GetBE(i);
ElementTransformation *eltrans = fes->GetBdrElementTransformation(i);
boundary_integs[0]->AssembleElementMatrix(be, *eltrans, elmat);
for (int k = 1; k < boundary_integs.Size(); k++)
@@ -325,8 +323,8 @@ void BilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat) const
}
else
{
const int ndof = be.GetDof() * fes->GetVDim();
elmat.SetSize(ndof);
fes->GetBdrElementVDofs(i, vdofs);
elmat.SetSize(vdofs.Size());
elmat = 0.0;
}
}
@@ -1431,50 +1429,32 @@ void MixedBilinearForm::GetBlocks(Array2D<SparseMatrix *> &blocks) const
mat->GetBlocks(blocks);
}
void MixedBilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi)
void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi)
{
domain_integs.Append(bfi);
domain_integs.Append (bfi);
domain_integs_marker.Append(NULL); // NULL marker means apply everywhere
}
void MixedBilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi,
Array<int> &elem_marker)
void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi,
Array<int> &elem_marker)
{
domain_integs.Append(bfi);
domain_integs.Append (bfi);
domain_integs_marker.Append(&elem_marker);
}
void MixedBilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi)
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi)
{
boundary_integs.Append(bfi);
boundary_integs.Append (bfi);
boundary_integs_marker.Append(NULL); // NULL marker means apply everywhere
}
void MixedBilinearForm::AddBoundaryIntegrator(BilinearFormIntegrator *bfi,
Array<int> &bdr_marker)
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi,
Array<int> &bdr_marker)
{
boundary_integs.Append(bfi);
boundary_integs.Append (bfi);
boundary_integs_marker.Append(&bdr_marker);
}
void MixedBilinearForm::AddInteriorFaceIntegrator(BilinearFormIntegrator *bfi)
{
interior_face_integs.Append(bfi);
}
void MixedBilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi)
{
boundary_face_integs.Append(bfi);
boundary_face_integs_marker.Append(NULL); // NULL marker means apply everywhere
}
void MixedBilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi,
Array<int> &bdr_marker)
{
boundary_face_integs.Append(bfi);
boundary_face_integs_marker.Append(&bdr_marker);
}
void MixedBilinearForm::AddTraceFaceIntegrator (BilinearFormIntegrator * bfi)
{
trace_face_integs.Append (bfi);
@@ -1607,108 +1587,6 @@ void MixedBilinearForm::Assemble(int skip_zeros)
}
}
if (interior_face_integs.Size())
{
FaceElementTransformations *ftr;
Array<int> trial_vdofs2, test_vdofs2;
const FiniteElement *trial_fe1, *trial_fe2, *test_fe1, *test_fe2;
int nfaces = mesh->GetNumFaces();
for (int i = 0; i < nfaces; i++)
{
ftr = mesh->GetInteriorFaceTransformations(i);
if (ftr != NULL)
{
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs);
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs);
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
test_fe1 = test_fes->GetFE(ftr->Elem1No);
if (ftr->Elem2No >= 0)
{
trial_fes->GetElementVDofs(ftr->Elem2No, trial_vdofs2);
test_fes->GetElementVDofs(ftr->Elem2No, test_vdofs2);
trial_vdofs.Append(trial_vdofs2);
test_vdofs.Append(test_vdofs2);
trial_fe2 = trial_fes->GetFE(ftr->Elem2No);
test_fe2 = test_fes->GetFE(ftr->Elem2No);
}
else
{
// The test_fe2 object is really a dummy and not used on the
// boundaries, but we can't dereference a NULL pointer, and we don't
// want to actually make a fake element.
trial_fe2 = trial_fe1;
test_fe2 = test_fe1;
}
for (int k = 0; k < interior_face_integs.Size(); k++)
{
interior_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
*test_fe2,
*ftr, elemmat);
mat->AddSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
}
}
}
}
if (boundary_face_integs.Size())
{
FaceElementTransformations *ftr;
Array<int> tr_vdofs2, te_vdofs2;
const FiniteElement *trial_fe1, *trial_fe2, *test_fe1, *test_fe2;
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < boundary_face_integs.Size(); k++)
{
if (boundary_face_integs_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *boundary_face_integs_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary face integrator #"
<< k << ", counting from zero");
for (int i = 0; i < bdr_attr_marker.Size(); i++)
{
bdr_attr_marker[i] |= bdr_marker[i];
}
}
for (int i = 0; i < trial_fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
ftr = mesh -> GetBdrFaceTransformations (i);
if (ftr != NULL)
{
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs);
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs);
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
test_fe1 = test_fes->GetFE(ftr->Elem1No);
// The test_fe2 object is really a dummy and not used on the
// boundaries, but we can't dereference a NULL pointer, and we don't
// want to actually make a fake element.
trial_fe2 = trial_fe1;
test_fe2 = test_fe1;
for (int k = 0; k < boundary_face_integs.Size(); k++)
{
if (boundary_face_integs_marker[k] &&
(*boundary_face_integs_marker[k])[bdr_attr-1] == 0) { continue; }
boundary_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
*test_fe2,
*ftr, elemmat);
mat->AddSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
}
}
}
}
if (trace_face_integs.Size())
{
FaceElementTransformations *ftr;
@@ -1889,11 +1767,10 @@ void MixedBilinearForm::ConformingAssemble()
void MixedBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat) const
{
const FiniteElement &trial_fe = *trial_fes->GetFE(i);
const FiniteElement &test_fe = *test_fes->GetFE(i);
if (domain_integs.Size())
{
const FiniteElement &trial_fe = *trial_fes->GetFE(i);
const FiniteElement &test_fe = *test_fes->GetFE(i);
ElementTransformation *eltrans = test_fes->GetElementTransformation(i);
domain_integs[0]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans,
elmat);
@@ -1906,21 +1783,19 @@ void MixedBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat) const
}
else
{
const int tr_dofs = trial_fe.GetDof() * trial_fes->GetVDim();
const int te_dofs = test_fe.GetDof() * test_fes->GetVDim();
elmat.SetSize(te_dofs, tr_dofs);
trial_fes->GetElementVDofs(i, trial_vdofs);
test_fes->GetElementVDofs(i, test_vdofs);
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
elmat = 0.0;
}
}
void MixedBilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat) const
{
const FiniteElement &trial_be = *trial_fes->GetBE(i);
const FiniteElement &test_be = *test_fes->GetBE(i);
if (boundary_integs.Size())
{
const FiniteElement &trial_be = *trial_fes->GetBE(i);
const FiniteElement &test_be = *test_fes->GetBE(i);
ElementTransformation *eltrans = test_fes->GetBdrElementTransformation(i);
boundary_integs[0]->AssembleElementMatrix2(trial_be, test_be, *eltrans,
elmat);
@@ -1933,103 +1808,9 @@ void MixedBilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat) const
}
else
{
const int tr_dofs = trial_be.GetDof() * trial_fes->GetVDim();
const int te_dofs = test_be.GetDof() * test_fes->GetVDim();
elmat.SetSize(te_dofs, tr_dofs);
elmat = 0.0;
}
}
void MixedBilinearForm::ComputeFaceMatrix(int i, DenseMatrix &elmat) const
{
FaceElementTransformations *ftr;
Mesh *mesh = test_fes -> GetMesh();
ftr = mesh->GetFaceElementTransformations(i);
MFEM_ASSERT(ftr, "No associated face transformations.");
const FiniteElement *trial_fe1, *trial_fe2, *test_fe1, *test_fe2;
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
test_fe1 = test_fes->GetFE(ftr->Elem1No);
if (ftr->Elem2No >= 0)
{
trial_fe2 = trial_fes->GetFE(ftr->Elem2No);
test_fe2 = test_fes->GetFE(ftr->Elem2No);
}
else
{
// The test_fe2 object is really a dummy and not used on the
// boundaries, but we can't dereference a NULL pointer, and we don't
// want to actually make a fake element.
trial_fe2 = trial_fe1;
test_fe2 = test_fe1;
}
if (interior_face_integs.Size())
{
interior_face_integs[0]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
*test_fe2,
*ftr, elmat);
for (int k = 1; k < interior_face_integs.Size(); k++)
{
interior_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
*test_fe2,
*ftr, elemmat);
elmat += elemmat;
}
}
else
{
int tr_dofs = trial_fe1->GetDof() * trial_fes->GetVDim();
int te_dofs = test_fe1->GetDof() * test_fes->GetVDim();
if (ftr->Elem2No >= 0)
{
tr_dofs += trial_fe2->GetDof() * trial_fes->GetVDim();
te_dofs += test_fe2->GetDof() * test_fes->GetVDim();
}
elmat.SetSize(te_dofs, tr_dofs);
elmat = 0.0;
}
}
void MixedBilinearForm::ComputeBdrFaceMatrix(int i, DenseMatrix &elmat) const
{
FaceElementTransformations *ftr;
Mesh *mesh = test_fes -> GetMesh();
ftr = mesh->GetBdrFaceTransformations(i);
MFEM_ASSERT(ftr, "No associated boundary face.");
const FiniteElement *trial_fe1, *trial_fe2, *test_fe1, *test_fe2;
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
test_fe1 = test_fes->GetFE(ftr->Elem1No);
// The test_fe2 object is really a dummy and not used on the
// boundaries, but we can't dereference a NULL pointer, and we don't
// want to actually make a fake element.
trial_fe2 = trial_fe1;
test_fe2 = test_fe1;
if (boundary_face_integs.Size())
{
boundary_face_integs[0]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
*test_fe2,
*ftr, elmat);
for (int k = 1; k < boundary_face_integs.Size(); k++)
{
boundary_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
*test_fe2,
*ftr, elemmat);
elmat += elemmat;
}
}
else
{
const int tr_dofs = trial_fe1->GetDof() * trial_fes->GetVDim();
const int te_dofs = test_fe1->GetDof() * test_fes->GetVDim();
elmat.SetSize(te_dofs, tr_dofs);
trial_fes->GetBdrElementVDofs(i, trial_vdofs);
test_fes->GetBdrElementVDofs(i, test_vdofs);
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
elmat = 0.0;
}
}
@@ -2160,59 +1941,36 @@ void MixedBilinearForm::AssembleBdrElementMatrix(
mat->AddSubMatrix(test_vdofs_, trial_vdofs_, elmat, skip_zeros);
}
void MixedBilinearForm::EliminateTrialEssentialBC(
void MixedBilinearForm::EliminateTrialDofs (
const Array<int> &bdr_attr_is_ess, const Vector &sol, Vector &rhs )
{
Array<int> trial_ess_dofs;
trial_fes->GetEssentialVDofs(bdr_attr_is_ess, trial_ess_dofs);
mat->EliminateCols(trial_ess_dofs, &sol, &rhs);
int i, j, k;
Array<int> tr_vdofs, cols_marker (trial_fes -> GetVSize());
cols_marker = 0;
for (i = 0; i < trial_fes -> GetNBE(); i++)
if (bdr_attr_is_ess[trial_fes -> GetBdrAttribute (i)-1])
{
trial_fes -> GetBdrElementVDofs (i, tr_vdofs);
for (j = 0; j < tr_vdofs.Size(); j++)
{
if ( (k = tr_vdofs[j]) < 0 )
{
k = -1-k;
}
cols_marker[k] = 1;
}
}
mat -> EliminateCols (cols_marker, &sol, &rhs);
}
void MixedBilinearForm::EliminateTrialEssentialBC(const Array<int>
&bdr_attr_is_ess)
{
Array<int> trial_ess_dofs;
trial_fes->GetEssentialVDofs(bdr_attr_is_ess, trial_ess_dofs);
mat->EliminateCols(trial_ess_dofs);
}
void MixedBilinearForm::EliminateTrialVDofs(const Array<int> &trial_vdofs_,
const Vector &sol, Vector &rhs)
{
Array<int> trial_vdofs_marker;
FiniteElementSpace::ListToMarker(trial_vdofs_, mat->Width(),
trial_vdofs_marker);
mat->EliminateCols(trial_vdofs_marker, &sol, &rhs);
}
void MixedBilinearForm::EliminateTrialVDofs(const Array<int> &trial_vdofs_)
{
if (mat_e == NULL)
{
mat_e = new SparseMatrix(mat->Height(), mat->Width());
}
Array<int> trial_vdofs_marker;
FiniteElementSpace::ListToMarker(trial_vdofs_, mat->Width(),
trial_vdofs_marker);
mat->EliminateCols(trial_vdofs_marker, *mat_e);
mat_e->Finalize();
}
void MixedBilinearForm::EliminateTrialVDofsInRHS(const Array<int> &trial_vdofs_,
const Vector &x, Vector &b)
{
mat_e->AddMult(x, b, -1.);
}
void MixedBilinearForm::EliminateEssentialBCFromTrialDofs(
void MixedBilinearForm::EliminateEssentialBCFromTrialDofs (
const Array<int> &marked_vdofs, const Vector &sol, Vector &rhs)
{
mat->EliminateCols(marked_vdofs, &sol, &rhs);
mat -> EliminateCols (marked_vdofs, &sol, &rhs);
}
void MixedBilinearForm::EliminateTestEssentialBC(const Array<int>
&bdr_attr_is_ess)
void MixedBilinearForm::EliminateTestDofs (const Array<int> &bdr_attr_is_ess)
{
int i, j, k;
Array<int> te_vdofs;
@@ -2232,14 +1990,6 @@ void MixedBilinearForm::EliminateTestEssentialBC(const Array<int>
}
}
void MixedBilinearForm::EliminateTestVDofs(const Array<int> &test_vdofs_)
{
for (int i=0; i<test_vdofs_.Size(); ++i)
{
mat->EliminateRow(test_vdofs_[i]);
}
}
void MixedBilinearForm::FormRectangularSystemMatrix(
const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
@@ -2276,9 +2026,20 @@ void MixedBilinearForm::FormRectangularSystemMatrix(
mat = m;
}
EliminateTrialVDofs(trial_tdof_list);
EliminateTestVDofs(test_tdof_list);
Array<int> ess_trial_tdof_marker, ess_test_tdof_marker;
FiniteElementSpace::ListToMarker(trial_tdof_list, trial_fes->GetTrueVSize(),
ess_trial_tdof_marker);
FiniteElementSpace::ListToMarker(test_tdof_list, test_fes->GetTrueVSize(),
ess_test_tdof_marker);
mat_e = new SparseMatrix(mat->Height(), mat->Width());
mat->EliminateCols(ess_trial_tdof_marker, *mat_e);
for (int i=0; i<test_tdof_list.Size(); ++i)
{
mat->EliminateRow(test_tdof_list[i]);
}
mat_e->Finalize();
A.Reset(mat, false);
}
@@ -2307,7 +2068,7 @@ void MixedBilinearForm::FormRectangularLinearSystem(
A); // Set A = mat_e
}
// Eliminate essential BCs with B -= Ab xb
EliminateTrialVDofsInRHS(trial_tdof_list, X, B);
mat_e->AddMult(X, B, -1.0);
B.SetSubVector(test_tdof_list, 0.0);
}
@@ -2333,10 +2094,6 @@ MixedBilinearForm::~MixedBilinearForm()
for (i = 0; i < domain_integs.Size(); i++) { delete domain_integs[i]; }
for (i = 0; i < boundary_integs.Size(); i++)
{ delete boundary_integs[i]; }
for (i = 0; i < interior_face_integs.Size(); i++)
{ delete interior_face_integs[i]; }
for (i = 0; i < boundary_face_integs.Size(); i++)
{ delete boundary_face_integs[i]; }
for (i = 0; i < trace_face_integs.Size(); i++)
{ delete trace_face_integs[i]; }
for (i = 0; i < boundary_trace_face_integs.Size(); i++)
+45 -140
View File
@@ -294,13 +294,13 @@ public:
const real_t &operator()(int i, int j) { return (*mat)(i,j); }
/// Returns a reference to: $ M_{ij} $
real_t &Elem(int i, int j) override;
virtual real_t &Elem(int i, int j);
/// Returns constant reference to: $ M_{ij} $
const real_t &Elem(int i, int j) const override;
virtual const real_t &Elem(int i, int j) const;
/// Matrix vector multiplication: $ y = M x $
void Mult(const Vector &x, Vector &y) const override;
virtual void Mult(const Vector &x, Vector &y) const;
/** @brief Matrix vector multiplication with the original uneliminated
matrix. The original matrix is $ M + M_e $ so we have:
@@ -309,7 +309,7 @@ public:
{ mat->Mult(x, y); mat_e->AddMult(x, y); }
/// Add the matrix vector multiple to a vector: $ y += a M x $
void AddMult(const Vector &x, Vector &y, const real_t a = 1.0) const override
virtual void AddMult(const Vector &x, Vector &y, const real_t a = 1.0) const
{ mat -> AddMult (x, y, a); }
/** @brief Add the original uneliminated matrix vector multiple to a vector.
@@ -319,8 +319,8 @@ public:
{ mat->AddMult(x, y); mat_e->AddMult(x, y); }
/// Add the matrix transpose vector multiplication: $ y += a M^T x $
void AddMultTranspose(const Vector & x, Vector & y,
const real_t a = 1.0) const override
virtual void AddMultTranspose(const Vector & x, Vector & y,
const real_t a = 1.0) const
{ mat->AddMultTranspose(x, y, a); }
/** @brief Add the original uneliminated matrix transpose vector
@@ -330,7 +330,7 @@ public:
{ mat->AddMultTranspose(x, y); mat_e->AddMultTranspose(x, y); }
/// Matrix transpose vector multiplication: $ y = M^T x $
void MultTranspose(const Vector & x, Vector & y) const override;
virtual void MultTranspose(const Vector & x, Vector & y) const;
/// Compute $ y^T M x $
real_t InnerProduct(const Vector &x, const Vector &y) const
@@ -338,13 +338,13 @@ public:
/** @brief Returns a pointer to (approximation) of the matrix inverse:
$ M^{-1} $ (currently returns NULL) */
MatrixInverse *Inverse() const override;
virtual MatrixInverse *Inverse() const;
/** @brief Finalizes the matrix initialization if the ::AssemblyLevel is
AssemblyLevel::LEGACY.
The matrix that gets finalized is different if you are using static
condensation or hybridization.*/
void Finalize(int skip_zeros = 1) override;
virtual void Finalize(int skip_zeros = 1);
/** @brief Returns a const reference to the sparse matrix: $ M $
*
@@ -458,18 +458,18 @@ public:
conforming prolongation, and |.| denotes the entry-wise absolute value.
In general, this is just an approximation of the exact diagonal for this
case. */
void AssembleDiagonal(Vector &diag) const override;
virtual void AssembleDiagonal(Vector &diag) const;
/// Get the finite element space prolongation operator.
const Operator *GetProlongation() const override
virtual const Operator *GetProlongation() const
{ return fes->GetConformingProlongation(); }
/// Get the finite element space restriction operator
const Operator *GetRestriction() const override
virtual const Operator *GetRestriction() const
{ return fes->GetConformingRestriction(); }
/// Get the output finite element space prolongation matrix
const Operator *GetOutputProlongation() const override
virtual const Operator *GetOutputProlongation() const
{ return GetProlongation(); }
/** @brief Returns the output fe space restriction matrix, transposed
@@ -477,11 +477,11 @@ public:
Logically, this is the transpose of GetOutputRestriction, but in
practice it is convenient to have it in transposed form for
construction of RAP operators in matrix-free methods. */
const Operator *GetOutputRestrictionTranspose() const override
virtual const Operator *GetOutputRestrictionTranspose() const
{ return fes->GetRestrictionTransposeOperator(); }
/// Get the output finite element space restriction matrix
const Operator *GetOutputRestriction() const override
virtual const Operator *GetOutputRestriction() const
{ return GetRestriction(); }
/// Compute serial RAP operator and store it in @a A as a SparseMatrix.
@@ -566,8 +566,7 @@ public:
FormLinearSystem() method to recover the solution as a GridFunction-size
vector in @a x. Use the same arguments as in the FormLinearSystem() call.
*/
void RecoverFEMSolution(const Vector &X, const Vector &b,
Vector &x) override;
virtual void RecoverFEMSolution(const Vector &X, const Vector &b, Vector &x);
/// Compute and store internally all element matrices.
void ComputeElementMatrices();
@@ -772,14 +771,6 @@ protected:
/// Entries are not owned.
Array<Array<int>*> boundary_integs_marker;
/// Interior face integrators.
Array<BilinearFormIntegrator*> interior_face_integs;
/// Boundary face integrators.
Array<BilinearFormIntegrator*> boundary_face_integs;
/// Entries are not owned.
Array<Array<int>*> boundary_face_integs_marker;
/// Trace face (skeleton) integrators.
Array<BilinearFormIntegrator*> trace_face_integs;
@@ -820,32 +811,32 @@ public:
MixedBilinearForm *mbf);
/// Returns a reference to: $ M_{ij} $
real_t &Elem(int i, int j) override;
virtual real_t &Elem(int i, int j);
/// Returns a reference to: $ M_{ij} $
const real_t &Elem(int i, int j) const override;
virtual const real_t &Elem(int i, int j) const;
/// Matrix multiplication: $ y = M x $
void Mult(const Vector & x, Vector & y) const override;
virtual void Mult(const Vector & x, Vector & y) const;
/// Add the matrix vector multiple to a vector: $ y += a M x $
void AddMult(const Vector & x, Vector & y,
const real_t a = 1.0) const override;
virtual void AddMult(const Vector & x, Vector & y,
const real_t a = 1.0) const;
/// Matrix transpose vector multiplication: $ y = M^T x $
void MultTranspose(const Vector & x, Vector & y) const override;
virtual void MultTranspose(const Vector & x, Vector & y) const;
/// Add the matrix transpose vector multiplication: $ y += a M^T x $
void AddMultTranspose(const Vector & x, Vector & y,
const real_t a = 1.0) const override;
virtual void AddMultTranspose(const Vector & x, Vector & y,
const real_t a = 1.0) const;
/** @brief Returns a pointer to (approximation) of the matrix inverse:
$ M^{-1} $ (currently unimplemented and returns NULL)*/
MatrixInverse *Inverse() const override;
virtual MatrixInverse *Inverse() const;
/** @brief Finalizes the matrix initialization if the ::AssemblyLevel is
AssemblyLevel::LEGACY.*/
void Finalize(int skip_zeros = 1) override;
virtual void Finalize(int skip_zeros = 1);
/** @brief Extract the associated matrix as SparseMatrix blocks. The number
of block rows and columns is given by the vector dimensions (vdim) of the
@@ -856,37 +847,15 @@ public:
/** This will segfault if the usual sparse mat is not defined
like when static condensation is being used or AllocMat() has
not yet been called. */
const SparseMatrix &SpMat() const
{
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
return *mat;
}
const SparseMatrix &SpMat() const { return *mat; }
/// Returns a reference to the sparse matrix: $ M $
SparseMatrix &SpMat()
{
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
return *mat;
}
SparseMatrix &SpMat() { return *mat; }
/** @brief Nullifies the internal matrix $ M $ and returns a pointer
to it. Used for transferring ownership. */
SparseMatrix *LoseMat() { SparseMatrix *tmp = mat; mat = NULL; return tmp; }
/// Returns a const reference to the sparse matrix of eliminated b.c.: $ M_e $
const SparseMatrix &SpMatElim() const
{
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
return *mat_e;
}
/// Returns a reference to the sparse matrix of eliminated b.c.: $ M_e $
SparseMatrix &SpMatElim()
{
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
return *mat_e;
}
/// Adds a domain integrator. Assumes ownership of @a bfi.
void AddDomainIntegrator(BilinearFormIntegrator *bfi);
@@ -901,16 +870,6 @@ public:
void AddBoundaryIntegrator(BilinearFormIntegrator * bfi,
Array<int> &bdr_marker);
/// Adds an interior face integrator. Assumes ownership of @a bfi.
void AddInteriorFaceIntegrator(BilinearFormIntegrator *bfi);
/// Adds a boundary face integrator. Assumes ownership of @a bfi.
void AddBdrFaceIntegrator(BilinearFormIntegrator *bfi);
/// Adds a boundary face integrator. Assumes ownership of @a bfi.
void AddBdrFaceIntegrator(BilinearFormIntegrator *bfi,
Array<int> &bdr_marker);
/** @brief Add a trace face integrator. Assumes ownership of @a bfi.
This type of integrator assembles terms over all faces of the mesh using
@@ -941,16 +900,6 @@ public:
corresponding pointer (to Array<int>) will be NULL. */
Array<Array<int>*> *GetBBFI_Marker() { return &boundary_integs_marker; }
/// Access all integrators added with AddInteriorFaceIntegrator().
Array<BilinearFormIntegrator*> *GetFBFI() { return &interior_face_integs; }
/// Access all integrators added with AddBdrFaceIntegrator().
Array<BilinearFormIntegrator*> *GetBFBFI() { return &boundary_face_integs; }
/** @brief Access all boundary markers added with AddBdrFaceIntegrator().
If no marker was specified when the integrator was added, the
corresponding pointer (to Array<int>) will be NULL. */
Array<Array<int>*> *GetBFBFI_Marker() { return &boundary_face_integs_marker; }
/// Access all integrators added with AddTraceFaceIntegrator().
Array<BilinearFormIntegrator*> *GetTFBFI() { return &trace_face_integs; }
@@ -979,19 +928,19 @@ public:
void AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const;
/// Get the input finite element space prolongation matrix
const Operator *GetProlongation() const override
virtual const Operator *GetProlongation() const
{ return trial_fes->GetProlongationMatrix(); }
/// Get the input finite element space restriction matrix
const Operator *GetRestriction() const override
virtual const Operator *GetRestriction() const
{ return trial_fes->GetRestrictionMatrix(); }
/// Get the test finite element space prolongation matrix
const Operator *GetOutputProlongation() const override
virtual const Operator *GetOutputProlongation() const
{ return test_fes->GetProlongationMatrix(); }
/// Get the test finite element space restriction matrix
const Operator *GetOutputRestriction() const override
virtual const Operator *GetOutputRestriction() const
{ return test_fes->GetRestrictionMatrix(); }
/** @brief For partially conforming trial and/or test FE spaces, complete the
@@ -1016,13 +965,6 @@ public:
/** @note The boundary attribute markers of the integrators are ignored. */
void ComputeBdrTraceFaceMatrix(int i, DenseMatrix &elmat) const;
/// Compute the face matrix of the given face element
void ComputeFaceMatrix(int i, DenseMatrix &elmat) const;
/// Compute the boundary face matrix of the given boundary element
/** @note The boundary attribute markers of the integrators are ignored. */
void ComputeBdrFaceMatrix(int i, DenseMatrix &elmat) const;
/// Assemble the given element matrix
/** The element matrix @a elmat is assembled for the element @a i, i.e.
added to the system matrix. The flag @a skip_zeros skips the zero
@@ -1063,61 +1005,24 @@ public:
Array<int> &test_vdofs,
int skip_zeros = 1);
/// Eliminate essential boundary trial DOFs from the system.
/// Eliminate essential boundary DOFs from the columns of the system.
/** The array @a bdr_attr_is_ess marks boundary attributes that constitute
the essential part of the boundary. */
void EliminateTrialEssentialBC(const Array<int> &bdr_attr_is_ess,
const Vector &sol, Vector &rhs);
the essential part of the boundary. All entries in the columns will be
set to 0.0 through elimination.*/
void EliminateTrialDofs(const Array<int> &bdr_attr_is_ess,
const Vector &sol, Vector &rhs);
/// Eliminate essential boundary trial DOFs from the system matrix.
/** The array @a bdr_attr_is_ess marks boundary attributes that constitute
the essential part of the boundary. */
void EliminateTrialEssentialBC(const Array<int> &bdr_attr_is_ess);
/// (DEPRECATED) Eliminate essential boundary trial DOFs from the system.
/** @see EliminateTrialEssentialBC() */
MFEM_DEPRECATED void EliminateTrialDofs(const Array<int> &bdr_attr_is_ess,
const Vector &sol, Vector &rhs)
{ EliminateTrialEssentialBC(bdr_attr_is_ess, sol, rhs); }
/// Eliminate the given trial @a vdofs. NOTE: here, @a vdofs is a list of DOFs.
/** In this case the eliminations are applied to the internal $ M $
and @a rhs without storing the elimination matrix $ M_e $. */
void EliminateTrialVDofs(const Array<int> &vdofs, const Vector &sol,
Vector &rhs);
/// Eliminate the given trial @a vdofs, storing the eliminated part internally in $ M_e $.
/** This method works in conjunction with EliminateTrialVDofsInRHS() and allows
elimination of boundary conditions in multiple right-hand sides. In this
method, @a vdofs is a list of DOFs. */
void EliminateTrialVDofs(const Array<int> &vdofs);
/** @brief Use the stored eliminated part of the matrix (see
EliminateTrialVDofs(const Array<int> &)) to modify the r.h.s.
@a b; @a vdofs is a list of DOFs (non-directional, i.e. >= 0). */
void EliminateTrialVDofsInRHS(const Array<int> &vdofs, const Vector &x,
Vector &b);
/** @brief Similar to
EliminateTrialVDofs(const Array<int> &, const Vector &, Vector &)
but here @a ess_dofs is a marker (boolean) array on all vector-dofs
(@a ess_dofs[i] < 0 is true). */
/// Eliminate the list of DOFs from the columns of the system.
/** @a marked_vdofs is the of colunm numbers that will be eliminated. All
entries in the columns will be set to 0.0 through elimination.*/
void EliminateEssentialBCFromTrialDofs(const Array<int> &marked_vdofs,
const Vector &sol, Vector &rhs);
/// Eliminate essential boundary test DOFs from the system matrix.
/// Eliminate essential boundary DOFs from the rows of the system.
/** The array @a bdr_attr_is_ess marks boundary attributes that constitute
the essential part of the boundary. */
void EliminateTestEssentialBC(const Array<int> &bdr_attr_is_ess);
/// (DEPRECATED) Eliminate essential boundary test DOFs from the system.
/** @see EliminateTestEssentialBC() */
MFEM_DEPRECATED virtual void EliminateTestDofs(const Array<int>
&bdr_attr_is_ess)
{ EliminateTestEssentialBC(bdr_attr_is_ess); }
/// Eliminate the given test @a vdofs. NOTE: here, @a vdofs is a list of DOFs.
void EliminateTestVDofs(const Array<int> &vdofs);
the essential part of the boundary. All entries in the rows will be
set to 0.0 through elimination.*/
virtual void EliminateTestDofs(const Array<int> &bdr_attr_is_ess);
/** @brief Return in @a A that is column-constrained.
@@ -1273,7 +1178,7 @@ public:
/** @brief Get the output finite element space restriction matrix in
transposed form. */
const Operator *GetOutputRestrictionTranspose() const override
virtual const Operator *GetOutputRestrictionTranspose() const
{ return test_fes->GetRestrictionTransposeOperator(); }
};
+45 -50
View File
@@ -37,19 +37,19 @@ protected:
public:
BilinearFormExtension(BilinearForm *form);
MemoryClass GetMemoryClass() const override
virtual MemoryClass GetMemoryClass() const
{ return Device::GetDeviceMemoryClass(); }
/// Get the finite element space prolongation matrix
const Operator *GetProlongation() const override;
virtual const Operator *GetProlongation() const;
/// Get the finite element space restriction matrix
const Operator *GetRestriction() const override;
virtual const Operator *GetRestriction() const;
/// Assemble at the level given for the BilinearFormExtension subclass
virtual void Assemble() = 0;
void AssembleDiagonal(Vector &diag) const override
virtual void AssembleDiagonal(Vector &diag) const
{
MFEM_ABORT("AssembleDiagonal not implemented for this assembly level!");
}
@@ -83,17 +83,16 @@ protected:
public:
PABilinearFormExtension(BilinearForm*);
void Assemble() override;
void AssembleDiagonal(Vector &diag) const override;
void FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A) override;
void Assemble();
void AssembleDiagonal(Vector &diag) const;
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A);
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0) override;
void Mult(const Vector &x, Vector &y) const override;
void MultTranspose(const Vector &x, Vector &y) const override;
void Update() override;
int copy_interior = 0);
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
void Update();
protected:
void SetupRestrictionOperators(const L2FaceValues m);
@@ -151,9 +150,9 @@ protected:
public:
EABilinearFormExtension(BilinearForm *form);
void Assemble() override;
void Mult(const Vector &x, Vector &y) const override;
void MultTranspose(const Vector &x, Vector &y) const override;
void Assemble();
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
};
/// Data and methods for fully-assembled bilinear forms
@@ -166,19 +165,18 @@ private:
public:
FABilinearFormExtension(BilinearForm *form);
void Assemble() override;
void Assemble();
void RAP(OperatorHandle &A);
/** @note Always does `DIAG_ONE` policy to be consistent with
`Operator::FormConstrainedSystemOperator`. */
void EliminateBC(const Array<int> &ess_dofs, OperatorHandle &A);
void FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A) override;
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A);
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0) override;
void Mult(const Vector &x, Vector &y) const override;
void MultTranspose(const Vector &x, Vector &y) const override;
int copy_interior = 0);
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
/** DGMult and DGMultTranspose use the extended L-vector to perform the
computation. */
@@ -201,17 +199,16 @@ protected:
public:
MFBilinearFormExtension(BilinearForm *form);
void Assemble() override;
void AssembleDiagonal(Vector &diag) const override;
void FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A) override;
void Assemble();
void AssembleDiagonal(Vector &diag) const;
void FormSystemMatrix(const Array<int> &ess_tdof_list, OperatorHandle &A);
void FormLinearSystem(const Array<int> &ess_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0) override;
void Mult(const Vector &x, Vector &y) const override;
void MultTranspose(const Vector &x, Vector &y) const override;
void Update() override;
int copy_interior = 0);
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
void Update();
};
/// Class extending the MixedBilinearForm class to support different AssemblyLevels.
@@ -228,20 +225,20 @@ protected:
public:
MixedBilinearFormExtension(MixedBilinearForm *form);
MemoryClass GetMemoryClass() const override
virtual MemoryClass GetMemoryClass() const
{ return Device::GetMemoryClass(); }
/// Get the finite element space prolongation matrix
const Operator *GetProlongation() const override;
virtual const Operator *GetProlongation() const;
/// Get the finite element space restriction matrix
const Operator *GetRestriction() const override;
virtual const Operator *GetRestriction() const;
/// Get the output finite element space restriction matrix
const Operator *GetOutputProlongation() const override;
virtual const Operator *GetOutputProlongation() const;
/// Get the output finite element space restriction matrix
const Operator *GetOutputRestriction() const override;
virtual const Operator *GetOutputRestriction() const;
virtual void Assemble() = 0;
virtual void FormRectangularSystemOperator(const Array<int> &trial_tdof_list,
@@ -276,7 +273,7 @@ public:
PAMixedBilinearFormExtension(MixedBilinearForm *form);
/// Partial assembly of all internal integrators
void Assemble() override;
void Assemble();
/**
@brief Setup OperatorHandle A to contain constrained linear operator
@@ -286,7 +283,7 @@ public:
*/
void FormRectangularSystemOperator(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
OperatorHandle &A) override;
OperatorHandle &A);
/**
Setup OperatorHandle A to contain constrained linear operator and
eliminate columns corresponding to essential dofs from system,
@@ -295,21 +292,20 @@ public:
void FormRectangularLinearSystem(const Array<int> &trial_tdof_list,
const Array<int> &test_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B) override;
OperatorHandle &A, Vector &X, Vector &B);
/// y = A*x
void Mult(const Vector &x, Vector &y) const override;
void Mult(const Vector &x, Vector &y) const;
/// y += c*A*x
void AddMult(const Vector &x, Vector &y, const real_t c=1.0) const override;
void AddMult(const Vector &x, Vector &y, const real_t c=1.0) const;
/// y = A^T*x
void MultTranspose(const Vector &x, Vector &y) const override;
void MultTranspose(const Vector &x, Vector &y) const;
/// y += c*A^T*x
void AddMultTranspose(const Vector &x, Vector &y,
const real_t c=1.0) const override;
void AddMultTranspose(const Vector &x, Vector &y, const real_t c=1.0) const;
/// Assemble the diagonal of ADA^T for a diagonal vector D.
void AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const override;
void AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const;
/// Update internals for when a new MixedBilinearForm is given to this class
void Update() override;
void Update();
};
@@ -326,17 +322,16 @@ public:
PADiscreteLinearOperatorExtension(DiscreteLinearOperator *linop);
/// Partial assembly of all internal integrators
void Assemble() override;
void Assemble();
void AddMult(const Vector &x, Vector &y, const real_t c=1.0) const override;
void AddMult(const Vector &x, Vector &y, const real_t c=1.0) const;
void AddMultTranspose(const Vector &x, Vector &y,
const real_t c=1.0) const override;
void AddMultTranspose(const Vector &x, Vector &y, const real_t c=1.0) const;
void FormRectangularSystemOperator(const Array<int>&, const Array<int>&,
OperatorHandle& A) override;
OperatorHandle& A);
const Operator * GetOutputRestrictionTranspose() const override;
const Operator * GetOutputRestrictionTranspose() const;
private:
Vector test_multiplicity;
+18 -182
View File
@@ -170,16 +170,6 @@ void BilinearFormIntegrator::AssembleFaceMatrix(
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleFaceMatrix(
const FiniteElement &trial_fe1, const FiniteElement &test_fe1,
const FiniteElement &trial_fe2, const FiniteElement &test_fe2,
FaceElementTransformations &Trans,
DenseMatrix &elmat)
{
MFEM_ABORT("AssembleFaceMatrix (mixed form) is not implemented for this"
" Integrator class.");
}
void BilinearFormIntegrator::AssembleFaceMatrix(
const FiniteElement &trial_face_fe, const FiniteElement &test_fe1,
const FiniteElement &test_fe2, FaceElementTransformations &Trans,
@@ -233,38 +223,28 @@ void TransposeIntegrator::SetIntRule(const IntegrationRule *ir)
bfi->SetIntRule(ir);
}
void TransposeIntegrator::AssembleElementMatrix(
void TransposeIntegrator::AssembleElementMatrix (
const FiniteElement &el, ElementTransformation &Trans, DenseMatrix &elmat)
{
bfi->AssembleElementMatrix(el, Trans, bfi_elmat);
bfi -> AssembleElementMatrix (el, Trans, bfi_elmat);
// elmat = bfi_elmat^t
elmat.Transpose (bfi_elmat);
}
void TransposeIntegrator::AssembleElementMatrix2(
void TransposeIntegrator::AssembleElementMatrix2 (
const FiniteElement &trial_fe, const FiniteElement &test_fe,
ElementTransformation &Trans, DenseMatrix &elmat)
{
bfi->AssembleElementMatrix2(test_fe, trial_fe, Trans, bfi_elmat);
bfi -> AssembleElementMatrix2 (test_fe, trial_fe, Trans, bfi_elmat);
// elmat = bfi_elmat^t
elmat.Transpose (bfi_elmat);
}
void TransposeIntegrator::AssembleFaceMatrix(
void TransposeIntegrator::AssembleFaceMatrix (
const FiniteElement &el1, const FiniteElement &el2,
FaceElementTransformations &Trans, DenseMatrix &elmat)
{
bfi->AssembleFaceMatrix(el1, el2, Trans, bfi_elmat);
// elmat = bfi_elmat^t
elmat.Transpose (bfi_elmat);
}
void TransposeIntegrator::AssembleFaceMatrix(
const FiniteElement &tr_el1, const FiniteElement &te_el1,
const FiniteElement &tr_el2, const FiniteElement &te_el2,
FaceElementTransformations &Trans, DenseMatrix &elmat)
{
bfi->AssembleFaceMatrix(te_el1, tr_el1, te_el2, tr_el2, Trans, bfi_elmat);
bfi -> AssembleFaceMatrix (el1, el2, Trans, bfi_elmat);
// elmat = bfi_elmat^t
elmat.Transpose (bfi_elmat);
}
@@ -3518,150 +3498,6 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
}
}
void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &trial_fe1,
const FiniteElement &test_fe1,
const FiniteElement &trial_fe2,
const FiniteElement &test_fe2,
FaceElementTransformations &Trans,
DenseMatrix &elmat)
{
int tr_ndof1, te_ndof1, tr_ndof2, te_ndof2;
real_t un, a, b, w;
dim = test_fe1.GetDim();
tr_ndof1 = trial_fe1.GetDof();
te_ndof1 = test_fe1.GetDof();
Vector vu(dim), nor(dim);
if (Trans.Elem2No >= 0)
{
tr_ndof2 = trial_fe2.GetDof();
te_ndof2 = test_fe2.GetDof();
}
else
{
tr_ndof2 = 0;
te_ndof2 = 0;
}
tr_shape1.SetSize(tr_ndof1);
te_shape1.SetSize(te_ndof1);
tr_shape2.SetSize(tr_ndof2);
te_shape2.SetSize(te_ndof2);
elmat.SetSize(te_ndof1 + te_ndof2, tr_ndof1 + tr_ndof2);
elmat = 0.0;
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
int order;
// Assuming order(u)==order(mesh)
if (Trans.Elem2No >= 0)
order = (min(Trans.Elem1->OrderW(), Trans.Elem2->OrderW()) +
max(trial_fe1.GetOrder(), trial_fe2.GetOrder()) +
max(test_fe1.GetOrder(), test_fe2.GetOrder()));
else
{
order = Trans.Elem1->OrderW() + trial_fe1.GetOrder() + test_fe1.GetOrder();
}
if (trial_fe1.Space() == FunctionSpace::Pk)
{
order++;
}
ir = &IntRules.Get(Trans.FaceGeom, order);
}
for (int p = 0; p < ir->GetNPoints(); p++)
{
const IntegrationPoint &ip = ir->IntPoint(p);
IntegrationPoint eip1, eip2;
Trans.Loc1.Transform(ip, eip1);
Trans.Elem1->SetIntPoint(&eip1);
if (tr_ndof2 && te_ndof2)
{
Trans.Loc2.Transform(ip, eip2);
Trans.Elem2->SetIntPoint(&eip2);
}
trial_fe1.CalcPhysShape(*Trans.Elem1, tr_shape1);
test_fe1.CalcPhysShape(*Trans.Elem1, te_shape1);
Trans.Face->SetIntPoint(&ip);
u->Eval(vu, *Trans.Elem1, eip1);
if (dim == 1)
{
nor(0) = 2*eip1.x - 1.0;
}
else
{
CalcOrtho(Trans.Face->Jacobian(), nor);
}
un = vu * nor;
a = 0.5 * alpha * un;
b = beta * fabs(un);
// note: if |alpha/2|==|beta| then |a|==|b|, i.e. (a==b) or (a==-b)
// and therefore two blocks in the element matrix contribution
// (from the current quadrature point) are 0
if (rho)
{
real_t rho_p;
if (un >= 0.0 && tr_ndof2 && te_ndof2)
{
Trans.Elem2->SetIntPoint(&eip2);
rho_p = rho->Eval(*Trans.Elem2, eip2);
}
else
{
rho_p = rho->Eval(*Trans.Elem1, eip1);
}
a *= rho_p;
b *= rho_p;
}
w = ip.weight * (a+b);
if (w != 0.0)
{
for (int i = 0; i < te_ndof1; i++)
for (int j = 0; j < tr_ndof1; j++)
{
elmat(i, j) += w * te_shape1(i) * tr_shape1(j);
}
}
if (tr_ndof2 && te_ndof2)
{
trial_fe2.CalcPhysShape(*Trans.Elem2, tr_shape2);
test_fe2.CalcPhysShape(*Trans.Elem2, te_shape2);
if (w != 0.0)
for (int i = 0; i < te_ndof2; i++)
for (int j = 0; j < tr_ndof1; j++)
{
elmat(te_ndof1+i, j) -= w * te_shape2(i) * tr_shape1(j);
}
w = ip.weight * (b-a);
if (w != 0.0)
{
for (int i = 0; i < te_ndof2; i++)
for (int j = 0; j < tr_ndof2; j++)
{
elmat(te_ndof1+i, tr_ndof1+j) += w * te_shape2(i) * tr_shape2(j);
}
for (int i = 0; i < te_ndof1; i++)
for (int j = 0; j < tr_ndof2; j++)
{
elmat(i, tr_ndof1+j) -= w * te_shape1(i) * tr_shape2(j);
}
}
}
}
}
const IntegrationRule &DGTraceIntegrator::GetRule(
Geometry::Type geom, int order, FaceElementTransformations &T)
@@ -4554,8 +4390,8 @@ struct ShapeCoefficient : public VectorCoefficient
: VectorCoefficient(fe_.GetDof()), Q(q), fe(fe_) { }
using VectorCoefficient::Eval;
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
V.SetSize(vdim);
fe.CalcPhysShape(T, V);
@@ -4597,8 +4433,8 @@ ScalarVectorProductInterpolator::AssembleElementMatrix2(
VShapeCoefficient(Coefficient &q, const FiniteElement &fe_, int sdim)
: MatrixCoefficient(fe_.GetDof(), sdim), Q(q), fe(fe_) { }
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip)
{
M.SetSize(height, width);
fe.CalcPhysVShape(T, M);
@@ -4634,8 +4470,8 @@ VectorScalarProductInterpolator::AssembleElementMatrix2(
: MatrixCoefficient(fe_.GetDof(), vq.GetVDim()), VQ(vq), fe(fe_),
vc(width), shape(height) { }
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip)
{
M.SetSize(height, width);
VQ.Eval(vc, T, ip);
@@ -4674,8 +4510,8 @@ ScalarCrossProductInterpolator::AssembleElementMatrix2(
vshape(vdim, vq.GetVDim()), vc(vq.GetVDim()) { }
using VectorCoefficient::Eval;
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
V.SetSize(vdim);
VQ.Eval(vc, T, ip);
@@ -4718,8 +4554,8 @@ VectorCrossProductInterpolator::AssembleElementMatrix2(
MFEM_ASSERT(width == 3, "");
}
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip)
{
M.SetSize(height, width);
VQ.Eval(vc, T, ip);
@@ -4767,8 +4603,8 @@ struct VDotVShapeCoefficient : public VectorCoefficient
vshape(vdim, vq.GetVDim()), vc(vq.GetVDim()) { }
using VectorCoefficient::Eval;
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
V.SetSize(vdim);
VQ.Eval(vc, T, ip);
+469 -536
View File
File diff suppressed because it is too large Load Diff
+156 -156
View File
@@ -90,12 +90,12 @@ public:
explicit ConstantCoefficient(real_t c = 1.0) { constant=c; }
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{ return (constant); }
/// Fill the QuadratureFunction @a qf with the constant value.
void Project(QuadratureFunction &qf) override;
void Project(QuadratureFunction &qf);
};
/** @brief A piecewise constant coefficient with the constants keyed
@@ -130,8 +130,8 @@ public:
int GetNConst() { return constants.Size(); }
/// Evaluate the coefficient.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/** @brief A piecewise coefficient with the pieces keyed off the element
@@ -195,7 +195,7 @@ public:
{ InitMap(attr, coefs); }
/// Set the time for time dependent coefficients
void SetTime(real_t t) override;
virtual void SetTime(real_t t);
/// Replace a set of coefficients
void UpdateCoefficients(const Array<int> & attr,
@@ -211,8 +211,8 @@ public:
{ pieces.erase(attr); }
/// Evaluate the coefficient.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// A general function coefficient
@@ -254,8 +254,8 @@ public:
}
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// A common base class for returning individual components of the domain's
@@ -271,8 +271,8 @@ protected:
public:
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient which returns the x-component of the evaluation point
@@ -307,8 +307,8 @@ public:
CylindricalRadialCoefficient() : transip(3) {}
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient which returns the angular position or azimuth (often
@@ -323,8 +323,8 @@ public:
CylindricalAzimuthalCoefficient() : transip(3) {}
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient which returns the height or altitude of
@@ -342,8 +342,8 @@ public:
SphericalRadialCoefficient() : transip(3) {}
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient which returns the azimuthal angle (often denoted by phi)
@@ -357,8 +357,8 @@ public:
SphericalAzimuthalCoefficient() : transip(3) {}
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient which returns the polar angle (often denoted by theta)
@@ -372,8 +372,8 @@ public:
SphericalPolarCoefficient() : transip(3) {}
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
class GridFunction;
@@ -399,15 +399,15 @@ public:
const GridFunction * GetGridFunction() const { return GridF; }
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
/// @brief Fill the QuadratureFunction @a qf by evaluating the coefficient at
/// the quadrature points.
///
/// This function uses the efficient QuadratureFunction::ProjectGridFunction
/// to fill the QuadratureFunction.
void Project(QuadratureFunction &qf) override;
virtual void Project(QuadratureFunction &qf);
};
@@ -433,10 +433,10 @@ public:
: Q1(q1), Q2(q2), Transform2(std::move(F)) { Transform1 = 0; }
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip);
};
/** @brief Delta function coefficient optionally multiplied by a weight
@@ -488,7 +488,7 @@ public:
}
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Set the center location of the delta function.
void SetDeltaCenter(const Vector& center);
@@ -534,7 +534,7 @@ public:
virtual real_t EvalDelta(ElementTransformation &T, const IntegrationPoint &ip);
/** @brief A DeltaFunction cannot be evaluated. Calling this method will
cause an MFEM error, terminating the application. */
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip)
{ mfem_error("DeltaCoefficient::Eval"); return 0.; }
virtual ~DeltaCoefficient() { delete weight; }
};
@@ -555,10 +555,10 @@ public:
{ c = &c_; attr.Copy(active_attr); }
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip)
{ return active_attr[T.Attribute-1] ? c->Eval(T, ip, GetTime()) : 0.0; }
};
@@ -628,8 +628,8 @@ public:
using VectorCoefficient::Eval;
/// Evaluate the vector coefficient at @a ip.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override { V = vec; }
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) { V = vec; }
/// Return a reference to the constant vector in this class.
const Vector& GetVec() const { return vec; }
@@ -698,7 +698,7 @@ public:
: VectorCoefficient(vd) { InitMap(attr, coefs); }
/// Set the time for time dependent coefficients
void SetTime(real_t t) override;
virtual void SetTime(real_t t);
/// Replace a set of coefficients
void UpdateCoefficients(const Array<int> & attr,
@@ -713,8 +713,8 @@ public:
{ pieces.erase(attr); }
/// Evaluate the coefficient.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
@@ -728,8 +728,8 @@ public:
using VectorCoefficient::Eval;
/// Evaluate the vector coefficient at @a ip.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~PositionVectorCoefficient() { }
};
@@ -765,8 +765,8 @@ public:
using VectorCoefficient::Eval;
/// Evaluate the vector coefficient at @a ip.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~VectorFunctionCoefficient() { }
};
@@ -787,7 +787,7 @@ public:
explicit VectorArrayCoefficient(int dim);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Returns i'th coefficient.
Coefficient* GetCoeff(int i) { return Coeff[i]; }
@@ -806,8 +806,8 @@ public:
using VectorCoefficient::Eval;
/** @brief Evaluate the coefficient. Each element of vector V comes from the
associated array of scalar coefficients. */
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
/// Destroys vector coefficient.
virtual ~VectorArrayCoefficient();
@@ -836,21 +836,21 @@ public:
const GridFunction * GetGridFunction() const { return GridFunc; }
/// Evaluate the vector coefficient at @a ip.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
/** @brief Evaluate the vector coefficients at all of the locations in the
integration rule and write the vectors into the columns of matrix @a
M. */
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationRule &ir) override;
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationRule &ir);
/// @brief Fill the QuadratureFunction @a qf by evaluating the coefficient at
/// the quadrature points.
///
/// This function uses the efficient QuadratureFunction::ProjectGridFunction
/// to fill the QuadratureFunction.
void Project(QuadratureFunction &qf) override;
virtual void Project(QuadratureFunction &qf);
virtual ~VectorGridFunctionCoefficient() { }
};
@@ -874,14 +874,14 @@ public:
const GridFunction * GetGridFunction() const { return GridFunc; }
/// Evaluate the gradient vector coefficient at @a ip.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
/** @brief Evaluate the gradient vector coefficient at all of the locations
in the integration rule and write the vectors into columns of matrix @a
M. */
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationRule &ir) override;
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationRule &ir);
virtual ~GradientGridFunctionCoefficient() { }
};
@@ -905,8 +905,8 @@ public:
using VectorCoefficient::Eval;
/// Evaluate the vector curl coefficient at @a ip.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~CurlGridFunctionCoefficient() { }
};
@@ -929,8 +929,8 @@ public:
const GridFunction * GetGridFunction() const { return GridFunc; }
/// Evaluate the scalar divergence coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~DivergenceGridFunctionCoefficient() { }
};
@@ -973,7 +973,7 @@ public:
: VectorCoefficient(dir_.Size()), dir(dir_), d(x,y,z,s) { }
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Replace the associated DeltaCoefficient with a new DeltaCoefficient.
/** The new DeltaCoefficient cannot have a specified weight Coefficient, i.e.
@@ -998,8 +998,8 @@ public:
using VectorCoefficient::Eval;
/** @brief A VectorDeltaFunction cannot be evaluated. Calling this method
will cause an MFEM error, terminating the application. */
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{ mfem_error("VectorDeltaCoefficient::Eval"); }
virtual ~VectorDeltaCoefficient() { }
};
@@ -1021,17 +1021,17 @@ public:
{ c = &vc; attr.Copy(active_attr); }
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Evaluate the vector coefficient at @a ip.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
/** @brief Evaluate the vector coefficient at all of the locations in the
integration rule and write the vectors into the columns of matrix @a
M. */
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationRule &ir) override;
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationRule &ir);
};
typedef VectorCoefficient DiagonalMatrixCoefficient;
@@ -1113,8 +1113,8 @@ public:
: MatrixCoefficient(m.Height(), m.Width()), mat(m) { }
using MatrixCoefficient::Eval;
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override { M = mat; }
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) { M = mat; }
/// Return a reference to the constant matrix.
const DenseMatrix& GetMatrix() { return mat; }
};
@@ -1207,7 +1207,7 @@ public:
: MatrixCoefficient(h, w, symm) { InitMap(attr, coefs); }
/// Set the time for time dependent coefficients
void SetTime(real_t t) override;
virtual void SetTime(real_t t);
/// Replace a set of coefficients
void UpdateCoefficients(const Array<int> & attr,
@@ -1222,8 +1222,8 @@ public:
{ pieces.erase(attr); }
/// Evaluate the coefficient.
void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
};
/** @brief A matrix coefficient with an optional scalar coefficient multiplier
@@ -1280,16 +1280,16 @@ public:
{ }
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
/// (DEPRECATED) Evaluate the symmetric matrix coefficient at @a ip.
/** @deprecated Use Eval() instead. */
void EvalSymmetric(Vector &K, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void EvalSymmetric(Vector &K, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~MatrixFunctionCoefficient() { }
};
@@ -1310,7 +1310,7 @@ public:
explicit MatrixArrayCoefficient (int dim);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Get the coefficient located at (i,j) in the matrix.
Coefficient* GetCoeff (int i, int j) { return Coeff[i*width+j]; }
@@ -1328,8 +1328,8 @@ public:
{ return Coeff[i*width+j] ? Coeff[i*width+j] -> Eval(T, ip, GetTime()) : 0.0; }
/// Evaluate the matrix coefficient @a ip.
void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~MatrixArrayCoefficient();
};
@@ -1392,11 +1392,11 @@ public:
{ c = &mc; attr.Copy(active_attr); }
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Coefficients based on sums, products, or other functions of coefficients.
@@ -1425,7 +1425,7 @@ public:
: aConst(0.0), a(&A), b(&B), alpha(alpha_), beta(beta_) { }
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the first term in the linear combination as a constant
void SetAConst(real_t A) { a = NULL; aConst = A; }
@@ -1453,8 +1453,8 @@ public:
real_t GetBeta() const { return beta; }
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
return alpha * ((a == NULL ) ? aConst : a->Eval(T, ip) )
+ beta * b->Eval(T, ip);
@@ -1502,8 +1502,8 @@ public:
@note When this method is called, the caller must make sure that the
IntegrationPoint associated with @a T is the same as @a ip. This can be
achieved by calling T.SetIntPoint(&ip). */
void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
/// @deprecated Return a reference to the internal matrix used when evaluating this coefficient as a DenseMatrix.
@@ -1525,8 +1525,8 @@ public:
: SymmetricMatrixCoefficient(m.Height()), mat(m) { }
using SymmetricMatrixCoefficient::Eval;
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseSymmetricMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override { M = mat; }
virtual void Eval(DenseSymmetricMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) { M = mat; }
/// Return a reference to the constant matrix.
const DenseSymmetricMatrix& GetMatrix() { return mat; }
@@ -1576,12 +1576,12 @@ public:
{ }
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
using SymmetricMatrixCoefficient::Eval;
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseSymmetricMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseSymmetricMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~SymmetricMatrixFunctionCoefficient() { }
};
@@ -1606,7 +1606,7 @@ public:
: aConst(0.0), a(&A), b(&B) { }
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the first term in the product as a constant
void SetAConst(real_t A) { a = NULL; aConst = A; }
@@ -1624,8 +1624,8 @@ public:
Coefficient * GetBCoef() const { return b; }
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{ return ((a == NULL ) ? aConst : a->Eval(T, ip) ) * b->Eval(T, ip); }
};
@@ -1654,7 +1654,7 @@ public:
: aConst(0.0), bConst(B), a(&A), b(NULL) { }
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the numerator in the ratio as a constant
void SetAConst(real_t A) { a = NULL; aConst = A; }
@@ -1677,8 +1677,8 @@ public:
Coefficient * GetBCoef() const { return b; }
/// Evaluate the coefficient
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
real_t den = (b == NULL ) ? bConst : b->Eval(T, ip);
MFEM_ASSERT(den != 0.0, "Division by zero in RatioCoefficient");
@@ -1700,7 +1700,7 @@ public:
: a(&A), p(p_) { }
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the base coefficient
void SetACoef(Coefficient &A) { a = &A; }
@@ -1713,8 +1713,8 @@ public:
real_t GetExponent() const { return p; }
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{ return pow(a->Eval(T, ip), p); }
};
@@ -1733,7 +1733,7 @@ public:
InnerProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the first vector in the inner product
void SetACoef(VectorCoefficient &A) { a = &A; }
@@ -1746,8 +1746,8 @@ public:
VectorCoefficient * GetBCoef() const { return b; }
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient defined as a cross product of two vectors in the xy-plane.
@@ -1765,7 +1765,7 @@ public:
VectorRotProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the first vector in the product
void SetACoef(VectorCoefficient &A) { a = &A; }
@@ -1778,8 +1778,8 @@ public:
VectorCoefficient * GetBCoef() const { return b; }
/// Evaluate the coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient defined as the determinant of a matrix coefficient
@@ -1795,7 +1795,7 @@ public:
DeterminantCoefficient(MatrixCoefficient &A);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
@@ -1803,8 +1803,8 @@ public:
MatrixCoefficient * GetACoef() const { return a; }
/// Evaluate the determinant coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Scalar coefficient defined as the trace of a matrix coefficient
@@ -1820,7 +1820,7 @@ public:
TraceCoefficient(MatrixCoefficient &A);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
@@ -1828,8 +1828,8 @@ public:
MatrixCoefficient * GetACoef() const { return a; }
/// Evaluate the trace coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Vector coefficient defined as the linear combination of two vectors
@@ -1866,7 +1866,7 @@ public:
Coefficient &alpha_, Coefficient &beta_);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the first vector coefficient
void SetACoef(VectorCoefficient &A_) { ACoef = &A_; }
@@ -1909,8 +1909,8 @@ public:
real_t GetBeta() const { return beta; }
/// Evaluate the coefficient at @a ip.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
@@ -1930,7 +1930,7 @@ public:
ScalarVectorProductCoefficient(Coefficient &A, VectorCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the scalar factor as a constant
void SetAConst(real_t A) { a = NULL; aConst = A; }
@@ -1948,8 +1948,8 @@ public:
VectorCoefficient * GetBCoef() const { return b; }
/// Evaluate the coefficient at @a ip.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
@@ -1971,7 +1971,7 @@ public:
NormalizedVectorCoefficient(VectorCoefficient &A, real_t tol = 1e-6);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the vector coefficient
void SetACoef(VectorCoefficient &A) { a = &A; }
@@ -1979,8 +1979,8 @@ public:
VectorCoefficient * GetACoef() const { return a; }
/// Evaluate the coefficient at @a ip.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
@@ -1999,7 +1999,7 @@ public:
VectorCrossProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the first term in the product
void SetACoef(VectorCoefficient &A) { a = &A; }
@@ -2012,8 +2012,8 @@ public:
VectorCoefficient * GetBCoef() const { return b; }
/// Evaluate the coefficient at @a ip.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
@@ -2033,7 +2033,7 @@ public:
MatrixVectorProductCoefficient(MatrixCoefficient &A, VectorCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
@@ -2046,8 +2046,8 @@ public:
VectorCoefficient * GetBCoef() const { return b; }
/// Evaluate the vector coefficient at @a ip.
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
@@ -2066,8 +2066,8 @@ public:
: MatrixCoefficient(d, d), dim(d) { }
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Matrix coefficient defined as the linear combination of two matrices
@@ -2088,7 +2088,7 @@ public:
real_t alpha_ = 1.0, real_t beta_ = 1.0);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the first matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
@@ -2111,8 +2111,8 @@ public:
real_t GetBeta() const { return beta; }
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Matrix coefficient defined as the product of two matrices
@@ -2140,8 +2140,8 @@ public:
MatrixCoefficient * GetBCoef() const { return b; }
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
/** @brief Matrix coefficient defined as a product of a scalar coefficient and a
@@ -2161,7 +2161,7 @@ public:
ScalarMatrixProductCoefficient(Coefficient &A, MatrixCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the scalar factor as a constant
void SetAConst(real_t A) { a = NULL; aConst = A; }
@@ -2179,8 +2179,8 @@ public:
MatrixCoefficient * GetBCoef() const { return b; }
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Matrix coefficient defined as the transpose of a matrix coefficient
@@ -2194,7 +2194,7 @@ public:
TransposeMatrixCoefficient(MatrixCoefficient &A);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
@@ -2202,8 +2202,8 @@ public:
MatrixCoefficient * GetACoef() const { return a; }
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Matrix coefficient defined as the inverse of a matrix coefficient.
@@ -2217,7 +2217,7 @@ public:
InverseMatrixCoefficient(MatrixCoefficient &A);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
@@ -2225,8 +2225,8 @@ public:
MatrixCoefficient * GetACoef() const { return a; }
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Matrix coefficient defined as the exponential of a matrix coefficient.
@@ -2240,7 +2240,7 @@ public:
ExponentialMatrixCoefficient(MatrixCoefficient &A);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
@@ -2248,8 +2248,8 @@ public:
MatrixCoefficient * GetACoef() const { return a; }
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Matrix coefficient defined as the outer product of two vector coefficients.
@@ -2267,7 +2267,7 @@ public:
OuterProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the first vector in the outer product
void SetACoef(VectorCoefficient &A) { a = &A; }
@@ -2280,8 +2280,8 @@ public:
VectorCoefficient * GetBCoef() const { return b; }
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
/** @brief Matrix coefficient defined as -a k x k x, for a vector k and scalar a
@@ -2305,7 +2305,7 @@ public:
CrossCrossCoefficient(Coefficient &A, VectorCoefficient &K);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
void SetTime(real_t t);
/// Reset the scalar factor as a constant
void SetAConst(real_t A) { a = NULL; aConst = A; }
@@ -2323,8 +2323,8 @@ public:
VectorCoefficient * GetKCoef() const { return k; }
/// Evaluate the matrix coefficient at @a ip.
void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
///@}
@@ -2349,10 +2349,10 @@ public:
const QuadratureFunction& GetQuadFunction() const { return QuadF; }
using VectorCoefficient::Eval;
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override;
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
void Project(QuadratureFunction &qf) override;
virtual void Project(QuadratureFunction &qf);
virtual ~VectorQuadratureFunctionCoefficient() { }
};
@@ -2371,9 +2371,9 @@ public:
const QuadratureFunction& GetQuadFunction() const { return QuadF; }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
virtual real_t Eval(ElementTransformation &T, const IntegrationPoint &ip);
void Project(QuadratureFunction &qf) override;
virtual void Project(QuadratureFunction &qf);
virtual ~QuadratureFunctionCoefficient() { }
};
+11 -11
View File
@@ -454,28 +454,28 @@ public:
#endif
/// Set/change the mesh associated with the collection
void SetMesh(Mesh *new_mesh) override;
virtual void SetMesh(Mesh *new_mesh) override;
#ifdef MFEM_USE_MPI
/// Set/change the mesh associated with the collection.
void SetMesh(MPI_Comm comm, Mesh *new_mesh) override;
virtual void SetMesh(MPI_Comm comm, Mesh *new_mesh) override;
#endif
/// Add a grid function to the collection and update the root file
void RegisterField(const std::string& field_name,
GridFunction *gf) override;
virtual void RegisterField(const std::string& field_name,
GridFunction *gf) override;
/// Add a quadrature function to the collection and update the root file.
/** Visualization of quadrature function is not supported in VisIt(3.12).
A patch has been sent to VisIt developers in June 2020. */
void RegisterQField(const std::string& q_field_name,
QuadratureFunction *qf) override;
virtual void RegisterQField(const std::string& q_field_name,
QuadratureFunction *qf) override;
/// Set the number of digits used for both the cycle and the MPI rank
/// @note VisIt seems to require 6 pad digits for the MPI rank. Therefore,
/// this function uses this default value. This behavior can be overridden
/// by calling SetPadDigitsCycle() and SetPadDigitsRank() instead.
void SetPadDigits(int digits) override
virtual void SetPadDigits(int digits) override
{ pad_digits_cycle=digits; pad_digits_rank=6; }
/// Set VisIt parameter: default levels of detail for the MultiresControl
@@ -489,13 +489,13 @@ public:
void DeleteAll();
/// Save the collection and a VisIt root file
void Save() override;
virtual void Save() override;
/// Save a VisIt root file for the collection
void SaveRootFile();
/// Load the collection based on its VisIt data (described in its root file)
void Load(int cycle_ = 0) override;
virtual void Load(int cycle_ = 0) override;
/// We will delete the mesh and fields if we own them
virtual ~VisItDataCollection() {}
@@ -546,7 +546,7 @@ public:
/// Save the collection - the directory name is constructed based on the
/// cycle value
void Save() override;
virtual void Save() override;
/// Set the data format for the ParaView output files. Possible options are
/// VTKFormat::ASCII, VTKFormat::BINARY, and VTKFormat::BINARY32.
@@ -590,7 +590,7 @@ public:
void UseRestartMode(bool restart_mode_);
/// Load the collection - not implemented in the ParaView writer
void Load(int cycle_ = 0) override;
virtual void Load(int cycle_ = 0) override;
};
}
+15 -15
View File
@@ -385,10 +385,10 @@ private:
/** @brief Evaluate the Jacobian of the transformation at the IntPoint and
store it in dFdx. */
const DenseMatrix &EvalJacobian() override;
virtual const DenseMatrix &EvalJacobian();
// Evaluate the Hessian of the transformation at the IntPoint and store it
// in d2Fdx2.
const DenseMatrix &EvalHessian() override;
virtual const DenseMatrix &EvalHessian();
public:
IsoparametricTransformation() : FElem(NULL) {}
@@ -430,32 +430,32 @@ public:
/** @brief Transform integration point from reference coordinates to
physical coordinates and store them in the vector. */
void Transform(const IntegrationPoint &, Vector &) override;
virtual void Transform(const IntegrationPoint &, Vector &);
/** @brief Transform all the integration points from the integration rule
from reference coordinates to physical
coordinates and store them as column vectors in the matrix. */
void Transform(const IntegrationRule &, DenseMatrix &) override;
virtual void Transform(const IntegrationRule &, DenseMatrix &);
/** @brief Transform all the integration points from the column vectors
of @a matrix from reference coordinates to physical
coordinates and store them as column vectors in @a result. */
void Transform(const DenseMatrix &matrix, DenseMatrix &result) override;
virtual void Transform(const DenseMatrix &matrix, DenseMatrix &result);
/// Return the order of the current element we are using for the transformation.
int Order() const override { return FElem->GetOrder(); }
virtual int Order() const { return FElem->GetOrder(); }
/// Return the order of the elements of the Jacobian of the transformation.
int OrderJ() const override;
virtual int OrderJ() const;
/** @brief Return the order of the determinant of the Jacobian (weight)
of the transformation. */
int OrderW() const override;
virtual int OrderW() const;
/// Return the order of $ adj(J)^T \nabla fi $
int OrderGrad(const FiniteElement *fe) const override;
virtual int OrderGrad(const FiniteElement *fe) const;
int GetSpaceDim() const override { return PointMat.Height(); }
virtual int GetSpaceDim() const { return PointMat.Height(); }
/** @brief Transform a point @a pt from physical space to a point @a ip in
reference space and optionally can set a solver tolerance using @a phys_tol. */
@@ -463,8 +463,8 @@ public:
point in physical space. If the inversion fails a non-zero value is
returned. This method is not 100 percent reliable for non-linear
transformations. */
int TransformBack (const Vector & v, IntegrationPoint & ip,
const real_t phys_rel_tol = tol_0) override
virtual int TransformBack(const Vector & v, IntegrationPoint & ip,
const real_t phys_rel_tol = tol_0)
{
InverseElementTransformation inv_tr(this);
inv_tr.SetPhysicalRelTol(phys_rel_tol);
@@ -604,9 +604,9 @@ public:
has been configured. */
const IntegrationPoint &GetElement2IntPoint() { return eip2; }
void Transform(const IntegrationPoint &, Vector &) override;
void Transform(const IntegrationRule &, DenseMatrix &) override;
void Transform(const DenseMatrix &matrix, DenseMatrix &result) override;
virtual void Transform(const IntegrationPoint &, Vector &);
virtual void Transform(const IntegrationRule &, DenseMatrix &);
virtual void Transform(const DenseMatrix &matrix, DenseMatrix &result);
ElementTransformation & GetElement1Transformation();
ElementTransformation & GetElement2Transformation();

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