Compare commits
78
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d31dfa15fa | ||
|
|
e94b8b6c89 | ||
|
|
3f8348d04b | ||
|
|
e30f7e5aa9 | ||
|
|
047943cfda | ||
|
|
4e6c38cf6d | ||
|
|
c981633846 | ||
|
|
ac2a5107da | ||
|
|
e6d733aa95 | ||
|
|
c6ee709eef | ||
|
|
0274b67ff6 | ||
|
|
38da503958 | ||
|
|
6c4f9c69a9 | ||
|
|
0702eb2fb2 | ||
|
|
7a62a7fd4a | ||
|
|
0f94d484a4 | ||
|
|
4b5798f905 | ||
|
|
87240f5619 | ||
|
|
3ccaa48cd4 | ||
|
|
a08928dd97 | ||
|
|
a0694d5825 | ||
|
|
a3ecbef0ec | ||
|
|
db5bc1725e | ||
|
|
6d50ebc9c3 | ||
|
|
e713913177 | ||
|
|
dd9898af3b | ||
|
|
0435ef5dac | ||
|
|
79e5020a18 | ||
|
|
adc81c7f5d | ||
|
|
18676c61b7 | ||
|
|
8f8deab121 | ||
|
|
8b0c779320 | ||
|
|
a012769434 | ||
|
|
ce5517b9af | ||
|
|
70ae37d5f0 | ||
|
|
feac718e95 | ||
|
|
9be8c15cf8 | ||
|
|
08ba45fca3 | ||
|
|
3aacfbfab0 | ||
|
|
2a60b998c7 | ||
|
|
91a168929f | ||
|
|
1369f5e189 | ||
|
|
993e4fbbe1 | ||
|
|
07d8a17abe | ||
|
|
5531b82dbc | ||
|
|
d9f60f401b | ||
|
|
fb876ba3a1 | ||
|
|
32a94b438f | ||
|
|
917978d310 | ||
|
|
a62302b4cb | ||
|
|
f5d2b82839 | ||
|
|
1382f8aa1f | ||
|
|
f76a884d15 | ||
|
|
5c56659e46 | ||
|
|
4bcd4586ba | ||
|
|
083d42c6ce | ||
|
|
dbc3458db0 | ||
|
|
4f694287ae | ||
|
|
5d8fbfee93 | ||
|
|
7799753053 | ||
|
|
cb6db58ad3 | ||
|
|
5da2bfc23d | ||
|
|
466a771ab0 | ||
|
|
a317e1a17d | ||
|
|
fcbde98cb6 | ||
|
|
80cb02328c | ||
|
|
1847e460cf | ||
|
|
1cd46aa768 | ||
|
|
11edee7aca | ||
|
|
28115e5de2 | ||
|
|
20954328c3 | ||
|
|
41e92219ee | ||
|
|
a0c3620618 | ||
|
|
9c791bed5a | ||
|
|
42d0fc17a1 | ||
|
|
2d2da417bb | ||
|
|
2e86ccb948 | ||
|
|
c7fe1ff1f4 |
@@ -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"}}'
|
||||
@@ -15,9 +15,6 @@
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
|
||||
# Clangd server cache
|
||||
*.cache*
|
||||
|
||||
# Backup files
|
||||
*~
|
||||
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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})
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include "dfem_differentiable_operator.hpp"
|
||||
#include "dfem_element_operator.hpp"
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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> ¶meters,
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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]);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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=;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 *> ¶meters_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> ¶meters,
|
||||
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> ¶meters,
|
||||
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> ¶meters_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> ¶meters_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"
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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> ¶meters,
|
||||
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
|
||||
@@ -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;
|
||||
@@ -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
@@ -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);
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -58,7 +58,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
~DiffusionMultigrid() override
|
||||
virtual ~DiffusionMultigrid()
|
||||
{
|
||||
delete amg;
|
||||
}
|
||||
|
||||
+3
-3
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+156
-156
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user